Docker Compose

Docker 是應用程式虛擬化技術,它改變了應用程式的使用方式,藉由 docker 環境的整合將變得不再是任何問題。Docker 官網上有不少的教學告訴我們怎麼去使用 container,用 container 來達到我們所需要的服務,但如果有一項 project 需要多個 container 時該怎麼辦呢?這些 container 之間該如何溝通?該如何去管理?這時後可以使用 Docker Compose 這個工具來解決上述的問題,我們可以事先定義好需要的 service,並定好 services 的相依性與網路等,最後只需要透過 docker-compose 的指令來操作,就可以很簡單的完成環境的管理。

Develop Django

這裡舉 Docker 網站上的 Compose QuickStart 的例子,假設我們想開發一個 Django 的網站:myweb,需要 Python3.5 的環境和 Django1.9 以及 Postgres 的 DB,如果不用 docker 的話,我們必須在開發的工作站上先安裝好這些軟體,往往會碰到套件相依和版本不同等問題,但如果是利用 docker 的話,我們只需要完成幾件事就可以解決這些困擾

  1. 將 python, django 包裝成一個 container: web,並將它 mount 在當前 project 的目錄下
  2. 然後 run 一個 postgres 的 container: db
  3. 將 django 的 db setting 設定連結到 container: db
  4. 透過 container: web 來執行 manage.py runserver

整個流程就像下圖這樣:

Untitled Diagram (14).png

那上述的這些事情該怎麼用 docker-compose 來做呢?我們要準備幾樣東西:docker-compose.yml、Dockerfile、requirements.txt、以及很短的 script 來簡化我們的指令,接下來則是就各部份來介紹:

requirements.txt

django==1.9
psycopg2

指定 pip install 要用的套件

Dockerfile

FROM python:3.5

MAINTAINER evan176.gui@gmail.com

WORKDIR /workspace

ADD requirements.txt

RUN pip install -r requirements.txt

CMD ["bash"]
  1. 指定一個工作目錄給 container: web,之後用來 mount 到目前的目錄
  2. 從目前目錄加入 requirements.txt,之後 build image 時用來 pip install

docker-compose.yml

version: '3'

services:
  web:
    build: .
    depends_on:
      - db
    volumes:
      - .:/workspace
    ports:
      - "8000:8000"
    tty: true

  db:
    image: postgres:9.6
    ports:
      - "8001:5432"

我們描述了兩個services:
1. web:
build: 用這個目錄下的 Dockerfile 來 build images
depends_on: 啟動這個 service 前,要先啟動 db
volumes: 將 /workspace mount 到當前目錄
ports: 將 docker host 的 8000 port 導到 container: web 的 8000 port
tty: 開啟虛擬終端機,之後可以用來 attach
2. db:
image: 使用 postgres 9.6 的 image
ports: 將 docker host 的 8001 port 導到 container db 裡頭的 5432 port

Networking in Compose

到這邊可能會有一點奇怪的地方,究竟 docker 的 port forwarding 是怎麼運作的,從 Networking in Compose 上的描述來看,docker-compose 會在開始時預設一個 network: myweb_default,並將 container: web, db 加入這個 network,這兩個 container 之間只要透過 service name 就可以溝通,例如從 web 去 access db 時,只需要用 postgres://db:5432。
那 host port 呢?在上述所寫的 host 其實是指 docker host 而不是目前的這台電腦,也就是說當我們從 host 去 access db 時,我們必須要透過 postgres://docker_host_ip:8001,像下圖這樣:
Untitled Diagram (12).png

myweb/settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}

基於上述所說的 networking 機制,我們需要將 django settings 中的 HOST 換成 db

manage.sh

#!/bin/bash
SERVICE="web"
DOCKER_COMPOSE="docker-compose"

main() {
  if [[ $# -ne 0 ]]; then
    case "$1" in
      start)
        ${DOCKER_COMPOSE} down
        ${DOCKER_COMPOSE} up -d --build
        ;;
      stop)
        ${DOCKER_COMPOSE} down
        ;;
      bash)
        ${DOCKER_COMPOSE} exec ${SERVICE} bash
        ;;
      migrate)
        ${DOCKER_COMPOSE} exec ${SERVICE} python myweb/manage.py migrate
        ;;
      runserver)
        ${DOCKER_COMPOSE} exec ${SERVICE} python myweb/manage.py runserver 0.0.0.0:8080
        ;;
      *)
        ;;
    esac
  fi
}

main "$@"

在這裡設定了幾個指令,start、stop、bash、migrate、runserver

./manage.sh start

開啟這些服務,docker-compose up 的時候我們加上 -d 代表是在後台執行,之後只要使用 docker-compose exec 就可以將指令送到 container 中

./manage.sh bash

開啟 container: web 的 shell

./manage.sh runserver

用 container: web 來 run django server

./manage.sh stop

關閉這些 services

References

https://docs.docker.com/compose/django/
https://yeasy.gitbooks.io/docker_practice/content/compose/
https://realpython.com/blog/python/django-development-with-docker-compose-and-machine/
https://docs.docker.com/compose/networking/

廣告