Background

繼上一篇我們討論完如何建立一個Docker的image後,那接著下來我們要討論一個比較進階的功能 docker-compose,我們將會利用 docker-compose 來完成多個 services 的 container 實作。使用的範例程式一樣是 [flask-vue-crud] (https://github.com/testdrivenio/flask-vue-crud)。

Start

接著我們進一步來討論 Docker Compose,Docker Compose 是一個工具可以讓你可以透過指令來控制專案中所需要的 services。那在執行Docker Compose相關指令前,我們必須撰寫 docker-compose.yml 來告訴 Docker Compose 怎麼使用這些 services。詳細的部份我們稍後再用例子來說明。

Directory

先列一下整個專案的目錄架構,那這邊我們需要填寫的設定檔有四個(client/Dockerfile, server/Dockerfile, nginx/Dockerfile, docker-compose.yml)

├── client
│   ├── Dockerfile
│   ├── README.md
│   ├── babel.config.js
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   └── src
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── default.conf
└── server
    ├── Dockerfile
    ├── __pycache__
    ├── requirements.txt
    └── run.py

整個詳細的內容可以到我的github查看,建議下載下來。

Dockerfile & docker-compose.yml

client/Dockerfile

FROM node:lts-alpine 
# install simple http server for serving static content 
RUN npm install -g http-server 
# make the 'app' folder the current working directory 
WORKDIR /app
# copy both 'package.json' and 'package-lock.json' (if available) 
COPY package*.json ./ 
# install project dependencies 
RUN npm install 
# copy project files and folders to the current working directory (i.e. 'app' folder) 
COPY . . 
# build app for production with minification 
RUN npm run build 
EXPOSE 8080 
CMD [ "npm", "run", "serve"]

整個 client/Dockerfile 是根據 vue.js 官網的建議所寫的,沒啥太大的問題。

server/Dockerfile

FROM python:3.7.3-stretch

# set working directory
WORKDIR '/app'

COPY ./requirements.txt ./
RUN pip install -r requirements.txt

COPY . .

# Expose the port uWSGI will listen on
EXPOSE 8000

#CMD ["gunicorn", "-b", "0.0.0.0:8000", "--worker-tmp-dir", "/dev/shm", "--workers=2", "--threads=4", "--worker-class=gthread", "run:app"]
CMD ["gunicorn", "-b", "0.0.0.0:8000", "--workers=1", "--worker-class=gthread", "run:app"]

server/Dockerfile 這個跟我上一篇文章提到的一模一樣,有興趣的可以在回去看看。

nginx/Dockerfile

FROM nginx
COPY ./default.conf /etc/nginx/conf.d/default.conf

基本上就是把 config Copy過去而已,詳細的 conf 內容可到 github上去看。

docker-compose.yml

version: '3'
services:
  nginx:
    image: flask-vue-crud-nginx
    restart: always
    build:
      dockerfile: Dockerfile
      context: ./nginx
    ports:
      - '3000:3000'
      - '5000:5000'
  api:
    image: flask-vue-crud-api
    build:
      dockerfile: Dockerfile
      context: ./server
  client:
    image: flask-vue-crud-client
    build:
      dockerfile: Dockerfile
      context: ./client

這邊稍微簡單解釋一下:

  • version: 目前使用的版本, 可以參考官網
  • services: 關鍵字後面列出 web, nginx 兩項專案中的sevices
  • context: 指定 Dockerfile 的位址
  • dockerfile: 指定的 dockerfile for building image
  • port: 外部露出開放的 port 對應到 docker container 的 port
  • volumes: 用於host與container之間的對映掛載

Nginx config

upstream vue {
  server client:8080;
}

server {
    listen 3000;
        
    location / {

        proxy_pass http://vue;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    listen 5000;
    
    location / {

        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

        proxy_pass http://api:8000;
        proxy_redirect   off;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

在nginx config中首先要先了解的兩個container之間的network互通不是透過localhost. 所以nginx那邊才要改成web (proxy_pass http://api:8000;, 常見的為 proxy_pass http://localhost:8000;). 必須明確告知是要把 request 導到哪個 container 中.

Test & Start

完成 docker-compose.yml 後我們就可以利用 docker-compose 指令來喚起整個服務. (沒安裝指令的話請自行上官網查安裝文件.)

$ docker-compose up --build

等到完全啟動後 Navigate to http://localhost:3000 就可以操作 flask-vue-crud。

Reference