Byte Ebi's Logo

Byte Ebi 🍤

每天一小口,蝦米變鯨魚

[Express+Vue 搭建電商網站] 22 使用 Docker 將專案容器化

使用 Express + Vue 搭建一個電商網站 - 使用 Docker 將專案容器化

Ray

接著我們會把整個專案使用 Docker 來將服務容器化,也會在 MongoDB 設定身份驗證機制
如果對 Docker 不熟的話必須先去了解,才有辦法進行
或是直接跳過這個章節 交給熱心的攤主處理

在這邊我們會用到三個容器

  • nginx 伺服器:處理 Vue 框架實現的前端靜態頁面
  • api:運行我們使用 Express 建立的 API 服務
  • db:就是一個運行 MongoDB 資料庫的容器

流程會變成

資料進來由 nginx 分派,如果是前端或是靜態資源就直接從 nginx 回傳
若是 API 類型的請求則轉交給 API 容器處理後再回傳資料。

這種架構有以下優勢

  • 透過 nginx 過濾非法請求
  • 解決前後端跨域問題,因為兩者間都透過相同端點訪問
  • 可以輕鬆擴增伺服器大小,並且使用 Nginx 做負載平衡

前端容器化

首先容器化的是先前用 Vue 寫的前端專案,將專案打包成靜態頁面

npm run build

接著增加 nginx 設定檔 client/config/nginx.conf

server {
    listen 80;
    root /www;
    index index.html;
    sendfile on;
    sendfile_max_chunk 1M;
    tcp_nopush on;
    gzip_static on;

    location /api/v1 {
      proxy_pass http://api:3000;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

其中要特別注意的是 location 規則

  • 請求目標如果是 /api/v1,那就把請求傳到 api 容器裡
  • 請求目標 /,則直接回傳靜態頁面 index.html

然後要回頭稍微修改一下前端訪問後端的網址,打開 client/src/store/actions.js 修改 API_BASE

const API_BASE = '/api/v1';

這樣改了之後前端在對 API 發起請求時就是取決於當前頁面的網址,而不是固定的 http://localhost:3000/api/v1

接著就是要撰寫 Docker 相關設定
首先建立 client/src/Dockerfile

FROM nginx:1.13

# 刪除預設的 Nginc 設定
RUN rm /etc/nginx/conf.d/default.conf

# 加入自己定義的 Nginx 設定檔
COPY config/nginx.conf /etc/nginx/conf.d/

# 將前端靜態檔案映射到容器的 /www 目錄下
COPY dist /www

接著建立 client/.dockerignore,裡面只有短短一行

node_modules

代表不 mount 進 docker 的資料夾

後端容器化

前端容器化之後,接著準備進行後端的容器化。首先把寫死的 MongoDB 連線字串透過環境變數注入,修改 server/app.js 連線資料庫的部分成

mongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`);

接著一樣新建 server/Dockerfile

FROM node:10

# 指定工作目錄為 /usr/src/app,接下来的指令全部在這個路徑下操作
WORKDIR /usr/src/app

# 將 package.json 複製到根目錄
COPY package*.json ./

# 安裝 npm 依賴
RUN npm install

# 複製全部程式內容
COPY . .

# 設定環境變數
ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/test
ENV HOST=0.0.0.0
ENV PORT=3000

# 曝露出 3000 port
EXPOSE 3000

# 設定映像檔內執行的指令
CMD ["npm", "start"]

和前端一樣建立 server/.dockerignore 確定不會把 node_modules 給映射進容器裡
裡面也只有一行:

node_modules

Docker Compose

Docker Compose 可以讓我們很好的管理 docker 容器,只需要透過一個 YAML 檔就可以修改設定。

在專案的根目錄下建立(跟 client 和 server 兩個資料夾同層)docker-compose.yml

version: '3'

services: 
  db:
    image: mongo
    restart: always
  api:
    build: server
    restart: always
    ports:
      - 3000:3000
  nginx:
    build: client
    restart: always
    ports:
      - 8080:80

可以看到我們建立了三個 service,對應到前面說的 dbapinginx

  • db:指定使用 mongo 映像檔,然後當服務意外停止則總是重啟
  • api:映像檔透過 server 資料夾內來建立,對外 port 為 3000:3000
  • nginx:映像檔透過 client 資料夾內來建立,對外 port 為 8080:80,外部的 8080 port 會指向到內部 80 port

如果 docker-compose.yml 中的 service 使用 image 指定映像檔,則會去Docker Hub 上拉回指定的映像檔
若是使用 build 則會根據指定目錄下的 Dockerfile 來建立映像檔。

接著就是測試的時間!打開 Docker 之後,在終端機輸入

docker-compose up --build

第一次執行會多花一些時間,因為先前提過的要從Docker Hub 上拉回指定的映像檔,如下圖
docker_container.md

可以藉由

docker ps

來觀察目前所有的容器狀態,看起來沒問題的話就可以開啟 localhost:8080 來看看專案是不是跟原本一樣

MongoDB 身份驗證

在之前的設定中我們的 MongoDB 資料庫並沒定任何身份驗證
所以任何人只要能夠發送請求到資料庫都可以對資料庫做修改,這是一個可怕的資安問題!

接著我們要來搞定 MongoDB 的身份驗證,增加系統的安全性

修改 MongoDB 連線設定

打開 server/app.js,修改連線資料庫的部分成以下

ongoose.connect(process.env.MONGO_URI || `mongodb://localhost:27017/test`, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  user: process.env.MONGO_USER,
  pass: process.env.MONGO_PASSWORD,
});

這代表

  • useNewUrlParser:使用新的 MongoDB 驅動 URL 解析器
  • useUnifiedTopology:使用新的連線管理引擎,支持重新連線,這樣可以大大提升連線穩定性
  • user:連線的使用者名稱,通過環境變數注入
  • pass:連線使用的密碼,通過環境變數注入

Dockerfile 中注入環境變數

server/Dockerfile 中加入下面的環境變數:

ENV NODE_ENV=production
ENV MONGO_URI=mongodb://db:27017/admin
ENV MONGO_USER=mongoadmin
ENV MONGO_PASSWORD=secret
ENV HOST=0.0.0.0
ENV PORT=3000

可以發現一些不同的地方,調整了 MONGO_URI,把預設的 test 換成 admin
這是為了啟用驗證功能並且使用 admin 作為 Authentication Database 的使用者

設定 Docker Compose 預設密碼

接著在 docker-compose.yml 裡面幫 db 服務加入預設密碼的環境變數

db:
  image: mongo
  restart: always
  environment:
    MONGO_INITDB_ROOT_USERNAME: mongoadmin
    MONGO_INITDB_ROOT_PASSWORD: secret

測試

首先使用

docker-compose down --volumes

不只關閉了原本運行中的 docker 容器,還透過 --volumes 把原本的建立 MongoDB 的容器徹底刪除
若不這麼做,之後重啟容器會跳過初始化使用者的過程,這樣我們的驗證資料庫就沒辦法被建立,而會載入之前的資料

接著重新啟動容器

docker-compose up --build

應該會看到網頁一切正常,不過資料庫已經有了驗證,不是「裸奔」的狀態了


專案範例程式碼 GitHub 網址:ray247k/mini-E-commerce

最新文章

Category

Tag