本文最先發布DeanWu的技術博客:pylixm.cc/posts/2019-…html
題圖來源網絡。python
隨着Docker的普及成熟,已經逐漸成爲部署項目的首選,今天來和你們分享下如何使用docker部署django技術棧項目。mysql
咱們這裏說的Django技術棧爲:python3.六、Django2.二、redis、mysql、celery、gunicorn和nginx。在實際的生產項目中,這些組件分佈在集羣的不一樣機器,如Nginx、redis和Mysql可能會有單獨的團隊或部門負責。涉及的部署架構和容器的編排會更爲複雜,本文暫不去深究。本文主要介紹,如何使用docker-compose
來編排這些組件,這種方式適用於測試環境的部署或者你的我的sideproject
的部署。nginx
本文默認你已經瞭解docker
和docker-compose
的一些基本知識,若你不瞭解,可閱讀下面這些資料:git
下面咱們來講下如何部署。github
首先,看下咱們的項目組織結構,結構以下:web
├── 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
配置文件。redis
compose
目錄主要存放各組件的dockerfile文件和啓動腳本。docker-compose.yml
是docker-compose的編排配置文件。在docker-compose中,容器的啓動有兩種方法,一種是直接使用公共的鏡像來啓動容器,另外一種是經過咱們本身編寫的Dockerfile。由於咱們要安裝額外的工具包和初始化相關配置,web和celery組件咱們使用自定義的Dockerfile方式。sql
web容器的compose/web/Dockerfile
:docker
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的啓動腳本。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.四、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在開發和部署上的使用心得。