目錄html
咱們來使用 docker-compose 並基於 gunicorn + mariadb + nginx 部署一個簡單的 django 博客應用;python
. ├── django-blog │ ├── blogproject │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── Dockerfile │ ├── manage.py │ ├── requirements.txt │ └── static ├── docker-compose.yaml ├── mysql │ ├── conf │ │ └── django-blog.cnf │ └── data └── nginx ├── conf │ └── mysite.template ├── log └── ssl
mysql/ ├── conf │ └── django-blog.cnf └── data
配置數據庫使用utf8mb4
編碼:mysql
# mysql/conf/django-blog.cnf [client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] character-set-client-handshake = FALSE character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci init_connect='SET NAMES utf8mb4'
docker-compose.yaml 中 mysql service 的配置:linux
db: image: mariadb:10.4 container_name: django-blog-db restart: always environment: MYSQL_ROOT_PASSWORD: <root 用戶的密碼> MYSQL_DATABASE: <容器啓動時,自動建立的數據庫名> MYSQL_USER: <容器啓動時,自動建立的數據庫用戶,其對上述數據庫擁有所有的權限> MYSQL_PASSWORD: <上述數據庫用戶的密碼> volumes: - ./mysql/conf:/etc/mysql/conf.d # 掛載自定義配置目錄,即上述的 mysql/conf/django-blog.cnf 中的配置 - ./mysql/data:/var/lib/mysql # 掛載數據目錄 - ./mysql/log:/var/log # 掛載日誌目錄
爲何不用 mysql,而是使用 mariadb ?nginx
mysql:latest 默認的認證方式變爲caching_sha2_password
,直接使用會報錯:Authentication plugin 'caching_sha2_password' cannot be loaded
;git
使用 mariadb:latest 或者使用 mysql:5.7 是沒有問題的;github
關於這個問題的討論能夠參考以下連接,裏面提到一些規避的方法:web
caching_sha2_password
介紹:sql
github 兩個相關 issue 的討論:docker
django-blog/ ├── blogproject │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── Dockerfile ├── manage.py ├── requirements.txt # 包依賴 └── static # python manage.py collectstatic 命令收集到的靜態文件的目錄
編寫 Dockerfile:
FROM python:3.7-alpine # 使用 alpine 版本,精簡 image 體積; RUN echo "https://mirrors.ustc.edu.cn/alpine/latest-stable/main" > /etc/apk/repositories \ && apk update \ # alpine 中 mariadb 代替了 mysql: <https://alpinelinux.org/posts/Alpine-3.2.0-released.html> && apk add mariadb-dev build-base RUN mkdir /code WORKDIR /code COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt
其中, requirements.txt 內容爲:
-i https://mirrors.aliyun.com/pypi/simple Django==2.2.5 django-haystack==2.8.1 gunicorn==19.9.0 jieba==0.39 Markdown==3.1.1 mysqlclient==1.4.4 Whoosh==2.7.4
docker-compose.yaml 中 web service 的配置:
web: build: django-blog/ image: django-blog:2.2.5 # 構建後,image 的名字,2.2.5是目前使用的 django 版本 container_name: django-blog-web restart: always depends_on: - db volumes: - ./django-blog:/code # 掛載項目代碼 # - /code/static # 能夠選擇不掛載項目代碼中的 static 文件夾;https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder# command: /bin/bash -c "python manage.py makemigrations && python manage.py migrate && gunicorn blogproject.wsgi -w 3 -k gthread -b 0.0.0.0:8000" ports: - "8000:8000" environment: # settings.py 中能夠使用到的環境變量 DB_NAME: <mysql 容器中配置的數據庫> DB_USER: <mysql 容器中配置的數據庫用戶> DB_PASS: <mysql 容器中配置的數據庫用戶的密碼> DB_PORT: 3306 DJANGO_SECRET_KEY: "<django 密鑰>"
修改 django-blog/blogproject/settings.py 文件相關項:
import os _env = os.environ # 使用上述定義的環境變量代替具體值 DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': _env['DB_NAME'], 'USER': _env['DB_USER'], 'PASSWORD': _env['DB_PASS'], 'HOST': 'db', # docker-compose.yaml 中數據庫的服務名 'PORT': _env['DB_PORT'], # 默認的服務端口號 'OPTIONS': { # 存儲引擎啓用嚴格模式,非法數據值被拒絕 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 'charset': 'utf8mb4', }, } } # 安全考慮,不要直接顯示密鑰,定義在容器的環境變量中 SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] # 生產環境不要將其置爲 True DEBUG = False
nginx/ ├── conf │ └── mysite.template # 用戶配置 ├── log └── ssl # 存放證書
docker-compose.yaml 中 nginx service 的配置:
nginx: image: nginx:stable # 使用穩定版 container_name: django-blog-nginx restart: always depends_on: - web ports: - 80:80 - 443:443 environment: # 定義環境變量 NGINX_HOST: luizyao.com NGINX_PORT: 80 NGINX_SSL_PORT: 443 WEB_PORT: 8000 volumes: # 用戶配置目錄 - ./nginx/conf/mysite.template:/etc/nginx/conf.d/mysite.template # ssl 證書目錄 - ./nginx/ssl:/etc/nginx/ssl # 靜態文件,即 python manage.py collectstatic 命令收集到的靜態文件的目錄 - ./django-blog/static:/data/apps/django-blog/static # 日誌 - ./nginx/log/:/var/log/nginx/ command: /bin/bash -c "envsubst '$$NGINX_HOST $$NGINX_PORT $$NGINX_SSL_PORT $$WEB_PORT' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'" # 只有這幾個變量須要轉義:'$$NGINX_HOST $$NGINX_PORT $$NGINX_SSL_PORT $$WEB_PORT',不然會報錯
編寫 mysite.template 文件:
upstream django-blog { server web:${WEB_PORT}; } server { charset utf-8; listen ${NGINX_PORT}; server_name ${NGINX_HOST}; rewrite ^(.*)$ https://${server_name}$1 permanent; } server { listen ${NGINX_SSL_PORT} ssl http2 default_server; server_name ${NGINX_HOST}; ssl_certificate /etc/nginx/ssl/1_www.luizyao.com_bundle.crt; ssl_certificate_key /etc/nginx/ssl/2_www.luizyao.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location /static { alias /data/apps/django-blog/static; } location / { proxy_pass http://django-blog; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; } error_page 404 /404.html; location = /40x.html {} error_page 500 502 503 504 /50x.html; location = /50x.html {} }
version: '3.7' # docker 版本:18.06.0+ services: db: image: mariadb:10.4 container_name: django-blog-db restart: always environment: MYSQL_ROOT_PASSWORD: <root 用戶的密碼> MYSQL_DATABASE: <容器啓動時,自動建立的數據庫名> MYSQL_USER: <容器啓動時,自動建立的數據庫用戶,其對上述數據庫擁有所有的權限> MYSQL_PASSWORD: <上述數據庫用戶的密碼> volumes: - ./mysql/conf:/etc/mysql/conf.d # 掛載自定義配置 - ./mysql/data:/var/lib/mysql # 掛載數據存儲目錄 - ./mysql/log:/var/log # 掛載日誌存儲目錄 web: build: django-blog/ image: django-blog:2.2.5 # 構建後,image 的名字,2.2.5是目前使用的 django 版本 container_name: django-blog-web restart: always depends_on: - db volumes: - ./django-blog:/code # 掛載項目代碼 # - /code/static # 能夠選擇不掛載項目代碼中的 static 文件夾;https://stackoverflow.com/questions/29181032/add-a-volume-to-docker-but-exclude-a-sub-folder# command: /bin/bash -c "python manage.py makemigrations && python manage.py migrate && gunicorn blogproject.wsgi -w 3 -k gthread -b 0.0.0.0:8000" ports: - "8000:8000" environment: # settings.py 中能夠使用到的環境變量 DB_NAME: <mysql 容器中配置的數據庫> DB_USER: <mysql 容器中配置的數據庫用戶> DB_PASS: <mysql 容器中配置的數據庫用戶的密碼> DB_PORT: 3306 DJANGO_SECRET_KEY: "<django 密鑰>" nginx: image: nginx:stable # 使用穩定版 container_name: django-blog-nginx restart: always depends_on: - web ports: - 80:80 - 443:443 environment: # 定義環境變量 NGINX_HOST: luizyao.com NGINX_PORT: 80 NGINX_SSL_PORT: 443 WEB_PORT: 8000 volumes: # 用戶配置目錄 - ./nginx/conf/mysite.template:/etc/nginx/conf.d/mysite.template # ssl 證書目錄 - ./nginx/ssl:/etc/nginx/ssl # 靜態文件,即 python manage.py collectstatic 命令收集到的靜態文件的目錄 - ./django-blog/static:/data/apps/django-blog/static # 日誌 - ./nginx/log/:/var/log/nginx/ command: /bin/bash -c "envsubst '$$NGINX_HOST $$NGINX_PORT $$NGINX_SSL_PORT $$WEB_PORT' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && exec nginx -g 'daemon off;'" # 只有這幾個變量須要轉義:'$$NGINX_HOST $$NGINX_PORT $$NGINX_SSL_PORT $$WEB_PORT',不然會報錯
[luizyao@centos_7_6_1810 blog]$ docker-compose up Creating network "blog_default" with the default driver Creating django-blog-db ... done Creating django-blog-web ... done Creating django-blog-nginx ... done Attaching to django-blog-db, django-blog-web, django-blog-nginx django-blog-db | 2019-09-24 8:45:41 0 [Note] mysqld (mysqld 10.4.8-MariaDB-1:10.4.8+maria~bionic) starting as process 1 ... django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Using Linux native AIO django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Uses event mutexes django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Compressed tables use zlib 1.2.11 django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Number of pools: 1 django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Using SSE2 crc32 instructions django-blog-db | 2019-09-24 8:45:41 0 [Note] mysqld: O_TMPFILE is not supported on /tmp (disabling future attempts) django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Initializing buffer pool, total size = 256M, instances = 1, chunk size = 128M django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: Completed initialization of buffer pool django-blog-db | 2019-09-24 8:45:41 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority(). django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: 128 out of 128 rollback segments are active. django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: Creating shared tablespace for temporary tables django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ... django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB. django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: Waiting for purge to start django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: 10.4.8 started; log sequence number 21042225; transaction id 15378 django-blog-db | 2019-09-24 8:45:42 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool django-blog-db | 2019-09-24 8:45:42 0 [Note] Plugin 'FEEDBACK' is disabled. django-blog-db | 2019-09-24 8:45:42 0 [Note] Server socket created on IP: '::'. django-blog-db | 2019-09-24 8:45:42 0 [Note] Reading of all Master_info entries succeeded django-blog-db | 2019-09-24 8:45:42 0 [Note] Added new Master_info '' to hash table django-blog-db | 2019-09-24 8:45:42 0 [Note] mysqld: ready for connections. django-blog-db | Version: '10.4.8-MariaDB-1:10.4.8+maria~bionic' socket: '/var/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution django-blog-web | No changes detected django-blog-db | 2019-09-24 8:45:43 0 [Note] InnoDB: Buffer pool(s) load completed at 190924 8:45:43 django-blog-web | Operations to perform: django-blog-web | Apply all migrations: admin, auth, blog, comment, contenttypes, sessions django-blog-web | Running migrations: django-blog-web | No migrations to apply. django-blog-web | [2019-09-24 08:45:44 +0000] [1] [INFO] Starting gunicorn 19.9.0 django-blog-web | [2019-09-24 08:45:44 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) django-blog-web | [2019-09-24 08:45:44 +0000] [1] [INFO] Using worker: gthread django-blog-web | [2019-09-24 08:45:44 +0000] [10] [INFO] Booting worker with pid: 10 django-blog-web | [2019-09-24 08:45:44 +0000] [11] [INFO] Booting worker with pid: 11 django-blog-web | [2019-09-24 08:45:44 +0000] [12] [INFO] Booting worker with pid: 12