1. 程式人生 > 程式設計 >使用docker部署django技術棧專案

使用docker部署django技術棧專案

本文最早釋出DeanWu的技術部落格:pylixm.cc/posts/2019-…

題圖來源網路。

隨著Docker的普及成熟,已經逐漸成為部署專案的首選,今天來和大家分享下如何使用docker部署django技術棧專案。

我們這裡說的Django技術棧為:python3.6、Django2.2、redis、mysql、celery、gunicorn和nginx。在實際的生產專案中,這些元件分佈在叢集的不同機器,如Nginx、redis和Mysql可能會有單獨的團隊或部門負責。涉及的部署架構和容器的編排會更為複雜,本文暫不去深究。本文主要介紹,如何使用docker-compose來編排這些元件,這種方式適用於測試環境的部署或者你的個人sideproject

的部署。

本文預設你已經瞭解dockerdocker-compose的一些基本知識,若你不瞭解,可閱讀下面這些資料:

下面我們來說下如何部署。

專案組織結構

首先,看下我們的專案組織結構,結構如下:

├── LICENSE
├── README.md
├── compose
│   ├── celery
│   │   ├── Dockerfile
│   │   ├── celery-beat.sh
│   │   └── celery.sh
│   ├── mysql
│   │   └── my.cnf
│   ├── nginx
│   │   └── nginx.conf
│   └── web
│       ├── Dockerfile
│       ├── entrypoint.sh
│       ├── gunicorn.conf
│       └── gunicorn.sh
├── docker-compose.yml
├── docker_django_demo
│   ├── __init__.py
│   ├── celery.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── env.tpl
├── manage.py
├── requirements.txt
複製程式碼

除了Django的專案檔案外,主要增加了compose 配置檔案目錄和docker-compose.yml 配置檔案。

  • compose目錄主要存放各元件的dockerfile檔案和啟動指令碼。
  • docker-compose.yml 是docker-compose的編排配置檔案。

編寫Dockerfile 及啟動初始化指令碼

在docker-compose中,容器的啟動有兩種方法,一種是直接使用公共的映象來啟動容器,另一種是通過我們自己編寫的Dockerfile。因為我們要安裝額外的工具包和初始化相關配置,web和celery元件我們使用自定義的Dockerfile方式。

web容器的compose/web/Dockerfile

:

FROM python:3.6
ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code

COPY ./requirements.txt /code/
RUN pip install --no-cache-dir -r requirements.txt \
    && rm -rf requirements.txt

COPY . /code/
COPY ./compose/web/*.sh /code/
RUN sed -i 's/\r//' gunicorn.sh \
    && chmod +x gunicorn.sh \
    && sed -i 's/\r//' entrypoint.sh \
    && chmod +x entrypoint.sh

ENTRYPOINT ["/bin/bash","entrypoint.sh"]
複製程式碼

web容器的其他檔案:

  • compose/web/entrypoint.sh web容器的啟動指令碼,執行一些初始化或檢測邏輯。
  • compose/web/gunicorn.conf gunicorn配置檔案。
  • compose/web/gunicorn.sh gunicorn的啟動指令碼。

celery的Dockerfile:

FROM python:3.6
ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code

COPY ./requirements.txt /code/
COPY ./compose/celery/*.sh /code/
RUN pip install --no-cache-dir -r requirements.txt \
    && rm -rf requirements.txt && sh init_env.sh

COPY . /code/
COPY ./compose/celery/*.sh /code/
RUN sed -i 's/\r//' celery.sh \
    && chmod +x celery.sh \
    && sed -i 's/\r//' celery-beat.sh \
    && chmod +x celery-beat.sh
複製程式碼

celery的其他檔案:

  • compose/celery/celery.sh celery的啟動指令碼。
  • compose/celery/celery-beat.sh celery-beat的啟動指令碼。

編寫Compose啟動配置檔案

docker-compose 配置如下:

version: '2'

services:
  redis:
    image: redis
    ports:
      - "6379:6379"

  db:
    restart: always
    image: mysql:5.7.19
    # command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./compose/mysql/:/etc/mysql/conf.d
      - ./db:/var/lib/mysql
    # for test
    ports:
      - "127.0.0.1:3307:3306"
      # (HOST:CONTAINER)
    env_file:
      - .env

  web:
    # restart: always
    build:
      context: .
      dockerfile: ./compose/web/Dockerfile
    command: sh gunicorn.sh # ["/bin/bash","gunicorn.sh"]
    ports:
      - "8080:8002"
      # (HOST:CONTAINER)
    volumes:
      - ./logs:/var/logs/
      - ./collect_static:/code/collect_static
      - ./static:/code/static
      - ./templates:/code/templates
      - ./uploads:/code/uploads
    env_file: .env
    depends_on:
      - redis
      - db

  nginx:
    restart: always
    image: nginx:1.13.0
    volumes:
      - ./compose/nginx:/etc/nginx/conf.d/
      - ./staticfiles:/code/staticfiles
      - ./logs:/var/log/nginx
    ports:
      - "80:80"
      # (HOST:CONTAINER)
    depends_on:
      - web

  celery:
    build:
      context: .
      dockerfile: ./compose/celery/Dockerfile
    command: sh celery.sh
    volumes:
      - ./logs:/var/logs/
      - ./uploads:/code/uploads
    depends_on:
      - redis
      - db
    env_file: .env

  celery-beat:
    build:
      context: .
      dockerfile: ./compose/celery/Dockerfile
    command: sh celery-beat.sh
    volumes:
      - ./logs:/var/logs/
    depends_on:
      - redis
      - db
    env_file: .env
複製程式碼

celery 的worker 和beat這裡我們使用同一個映象Dockerfile,按照一個映象一個程式的原則,啟動兩個容器來分別跑worker和beat程式。

編譯測試

編寫好配置檔案之後,編譯映象測試執行:

docker-compose build 
docker-compose up  # 前臺執行 
docker-compose up -d  # 無誤後可後臺執行
複製程式碼

docker-compose ps 可以看到啟動好的容器:

$ docker-compose ps    
             Name                           Command               State            Ports          
--------------------------------------------------------------------------------------------------
dockerdjangodemo_celery-beat_1   sh celery-beat.sh                Up                              
dockerdjangodemo_celery_1        sh celery.sh                     Up                              
dockerdjangodemo_db_1            docker-entrypoint.sh mysqld      Up      127.0.0.1:3307->3306/tcp
dockerdjangodemo_nginx_1         nginx -g daemon off;             Up      0.0.0.0:80->80/tcp      
dockerdjangodemo_redis_1         docker-entrypoint.sh redis ...   Up      0.0.0.0:6379->6379/tcp  
dockerdjangodemo_web_1           /bin/bash entrypoint.sh sh ...   Up      0.0.0.0:8080->8002/tcp  
複製程式碼

對映埠可根據自己的實際情況調整。

問題

下面說下在構建過程中的幾個需要注意的問題。

mysql 編碼問題

docker 提供的mysql映象,預設編碼為latin1,在儲存中文時會顯示亂碼。官方提供了一種修改編碼方式的方法,在啟動指令碼後指定編碼格式,檔案可見這裡。mysql容器5.7.19版本可直接在docker-compose.yml 中的command後跟上引數--character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci。這種方式,只是修改server端的編碼。可直接使用配置檔案覆蓋的方式,指定所有的編碼格式。

配置如下:

[mysqld]
default-storage-engine=INNODB
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'
init_connect='SET collation_connection = utf8mb4_general_ci'
skip-character-set-client-handshake  # 跳過客戶端的編碼配置,客戶端直接使用服務端的編碼配置
bind-address = 0.0.0.0
複製程式碼

注:mysql 5.7.19 配置檔案方式成功,5.7.4、5.7.17 均失敗,可做參考。

web等mysql啟動完成後再繼續

mysql 容器在啟動起來之前是無法接受資料庫連結的,在web啟動初始化時,若資料庫還沒有啟動好會導致web容器啟動失敗直接退出。我們可以增加在web容器啟動時增加檢測指令碼,資料庫連通之後,再繼續。

指令碼如下:

#!/usr/bin/env bash
set -o errexit
set -o pipefail

echo $MYSQL_PASSWORD
echo $MYSQL_DATABASE
echo $MYSQL_HOST
echo $MYSQL_USER
echo $MYSQL_PORT

function mysql_ready(){
python << END
import sys
import pymysql
try:
    conn = pymysql.connect(host="db",port=3306,user="root",passwd="$MYSQL_ROOT_PASSWORD",db='$MYSQL_DATABASE',charset='utf8')
except pymysql.err.OperationalError:
    sys.exit(-1)
sys.exit(0)
END
}

until mysql_ready; do
  >&2 echo "MySQL is unavailable - sleeping"
  sleep 1
done

>&2 echo "MySQL is up - continuing..."

複製程式碼

總結

到此,使用docker來部署django技術棧服務就完成了,完整的專案程式碼,大家可參閱 docker-django-demo

文章開始說了,該部署方式不適合大型網站的線上生產服務,耦合度太高,不好維護等存在著許多問題。但是,部署自己的sideproject或者測試環境,在硬體資源有限的情況的下還是非常不錯的。除了減少環境部署搭建的麻煩外,遷移起來也是很方便的。

demo專案中也有些開發環境下如何使用docker的案例,但是個人一直認為docker更適合部署,在開發環境方便不如直接搭建來的靈活方便。歡迎大家留言,共同討論docker在開發和部署上的使用心得。

參考