[轉]Docker部署Django由淺入深系列(下): 八步部署Django+Uwsgi+Nginx+MySQL+Redis

在上篇教程中,我們手動構建了兩個容器,一個容器放Django + Uwsgi,另一個容器放Nginx,成功部署了一個簡單的Django項目。然而在實際的生產環境中,我們往往需要定義數量龐大的 docker 容器,並且容器之間具有錯綜複雜的依賴聯繫,一個一個去手動創建容器並記錄和配置這些複雜的容器關係,不僅效率低下而且容易出錯,所以迫切需要一種定義容器集羣編排和部署的工具,這就是docker-compose。本文是Docker部署Django項目的終章,我們將詳細介紹如何使用docker-compose工具八步部署Django + Uwsgi + Nginx + MySQL + Redis,並分享一個可以複用的項目佈局和各項服務的配置文件,強烈建議先收藏再閱讀。閱讀本篇前強烈建議先閱讀本系列文章的上篇和中篇。

format,png

什麼是docker-compose及docker-compose工具的安裝

Docker-compose是一個用來定義和運行復雜應用的 Docker 工具。使用 docker-compose 後不再需要使用 shell 腳本來逐一創建和啓動容器,還可以通過 docker-compose.yml 文件構建和管理複雜多容器組合。

Docker-compose的下載和安裝很簡單,網上有很多教程,我就不再詳述了。這裏只記錄下ubuntu系統下docker-compose的安裝過程。

 # Step 1: 以ubuntu爲例,下載docker-compose $ sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose # Step 2: 給予docker-compose可執行權限 $ sudo chmod +x /usr/local/bin/docker-compose # Step 3: 查看docker-compose版本 $ docker-compose --version

注意:安裝docker-compose前必需先安裝好docker。

Django + Uwsgi + Nginx + MySQL + Redis組合容器示意圖

本例中我們將使用docker-compose編排並啓動4個容器,這更接近於實際生成環境下的部署。

  1. Django + Uwsgi容器:核心應用程序,處理動態請求

  2. MySQL 容器:數據庫服務

  3. Redis 容器:緩存服務

  4. Nginx容器:反向代理服務並處理靜態資源請求

這四個容器的依賴關係是:Django+Uwsgi 容器依賴 Redis 容器和 MySQL 容器,Nginx 容器依賴Django+Uwsgi容器。爲了方便容器間的相互訪問和通信,我們使用docker-compose時可以給每個容器取個別名,這樣訪問容器時就可以直接使用別名訪問,而不使用Docker臨時給容器分配的IP了。

這四個容器的別名及通信端口如下圖所示:

format,png

Docker-compose部署Django項目佈局樹形圖

我們新建了一個compose文件夾,專門存放用於構建其它容器鏡像的Dockerfile及配置文件。compose文件夾與django項目的根目錄myproject同級。這樣做的好處是不同的django項目可以共享compose文件夾。

 myproject_docker # 項目根目錄 ├── compose # 存放各項容器服務的Dockerfile和配置文件 │   ├── mysql │   │   ├── conf │   │   │   └── my.cnf # MySQL配置文件 │   │   └── init │   │       └── init.sql # MySQL啓動腳本 │   ├── nginx │   │   ├── Dockerfile # 構建Nginx鏡像所的Dockerfile │   │   ├── log # 掛載保存nginx容器內nginx日誌 │   │   ├── nginx.conf # Nginx配置文件 │   │   └── ssl # 如果需要配置https需要用到 │   ├── redis │   │   └── redis.conf # redis配置文件 │   └── uwsgi # 掛載保存django+uwsgi容器內uwsgi日誌 ├── docker-compose.yml # 核心編排文件 └── myproject # 常規Django項目目錄     ├── Dockerfile # 構建Django+Uwsgi鏡像的Dockerfile     ├── apps # 存放Django項目的各個apps     ├── manage.py     ├── myproject # Django項目配置文件     │   ├── asgi.py     │   ├── __init__.py     │   ├── settings.py     │   ├── urls.py     │   └── wsgi.py     ├── pip.conf # 非必需。pypi源設置成國內,加速pip安裝     ├── requirements.txt # Django項目依賴文件     ├── start.sh # 啓動Django+Uwsgi容器後要執行的腳本     ├── media # 用戶上傳的媒體資源與靜態文件     ├── static # 項目所使用到的靜態文件     └── uwsgi.ini # uwsgi配置文件

下面我們開始正式部署。

第一步:編寫docker-compose.yml文件

docker-compose.yml的核心內容如下。我們定義了3個數據卷,用於掛載各個容器內動態生成的數據,比如MySQL的存儲數據,redis生成的快照和django容器中用戶上傳的媒體資源與文件。這樣即使刪除容器,容器內產生的數據也不會丟失。我們還編排了4項容器服務,別名分別爲redis, db, nginx和web,接下來我們將依次看看各個容器的Dockerfile和配置文件。

 version: "3"  volumes: # 自定義數據卷,位於宿主機/var/lib/docker/volumes內   myproject_db_vol: # 定義數據卷同步容器內mysql數據   myproject_redis_vol: # 定義數據卷同步redis容器內數據   myproject_media_vol: # 定義數據卷同步media文件夾數據  services:   redis:     image: redis:5     command: redis-server /etc/redis/redis.conf # 容器啓動後啓動redis服務器     volumes:       - myproject_redis_vol:/data # 通過掛載給redis數據備份       - ./compose/redis/redis.conf:/etc/redis/redis.conf # 掛載redis配置文件     ports:       - "6379:6379"     restart: always # always表容器運行發生錯誤時一直重啓    db:     image: mysql:5.7     environment:       - MYSQL_ROOT_PASSWORD=123456 # 數據庫密碼       - MYSQL_DATABASE=myproject # 數據庫名稱       - MYSQL_USER=dbuser # 數據庫用戶名       - MYSQL_PASSWORD=password # 用戶密碼      volumes:       - myproject_db_vol:/var/lib/mysql:rw # 掛載數據庫數據, 可讀可寫       - ./compose/mysql/conf/my.cnf:/etc/mysql/my.cnf # 掛載配置文件       - ./compose/mysql/init:/docker-entrypoint-initdb.d/ # 掛載數據初始化sql腳本     ports:       - "3306:3306" # 與配置文件保持一致     restart: always    web:     build: ./myproject # 使用myproject目錄下的Dockerfile     expose:       - "8000"     volumes:       - ./myproject:/var/www/html/myproject # 掛載項目代碼       - myproject_media_vol:/var/www/html/myproject/media # 以數據卷掛載容器內用戶上傳媒體文件       - ./compose/uwsgi:/tmp # 掛載uwsgi日誌     links:       - db       - redis     depends_on: # 依賴關係       - db       - redis     environment:       - DEBUG=False     restart: always     tty: true     stdin_open: true    nginx:     build: ./compose/nginx     ports:       - "80:80"       - "443:443"     expose:       - "80"     volumes:       - ./myproject/static:/usr/share/nginx/html/static # 掛載靜態文件       - ./compose/nginx/ssl:/usr/share/nginx/ssl # 掛載ssl證書目錄       - ./compose/nginx/log:/var/log/nginx # 掛載日誌       - myproject_media_vol:/usr/share/nginx/html/media # 掛載用戶上傳媒體文件     links:       - web     depends_on:       - web     restart: always 

第二步:編寫Web (Django+Uwsgi)鏡像和容器所需文件

構建Web鏡像(Django+Uwsgi)的所使用的Dockerfile如下所示:

 # myproject/Dockerfile # 建立 python3.7 環境 FROM python:3.7  # 鏡像作者大江狗 MAINTAINER DJG  # 設置 python 環境變量 ENV PYTHONUNBUFFERED 1  COPY pip.conf /root/.pip/pip.conf  # 創建 myproject 文件夾 RUN mkdir -p /var/www/html/myproject  # 將 myproject 文件夾爲工作目錄 WORKDIR /var/www/html/myproject  # 將當前目錄加入到工作目錄中(. 表示當前目錄) ADD . /var/www/html/myproject  # 更新pip版本 RUN /usr/local/bin/python -m pip install --upgrade pip  # 利用 pip 安裝依賴 RUN pip install -r requirements.txt  # 去除windows系統編輯文件中多餘的\r回車空格 RUN sed -i 's/\r//' ./start.sh  # 給start.sh可執行權限 RUN chmod +x ./start.sh

本Django項目所依賴的requirements.txt內容如下所示:

 # django django==3.0.6 # uwsgi uwsgi==2.0.18 # mysql mysqlclient==1.4.6 # redis django-redis==4.11.0 redis==3.5.0 # for images Pillow==7.1.2

start.sh腳本文件內容如下所示。最重要的是最後一句,使用uwsgi.ini配置文件啓動Django服務。

 #!/bin/bash # 從第一行到最後一行分別表示: # 1. 收集靜態文件到根目錄, # 2. 生成數據庫可執行文件, # 3. 根據數據庫可執行文件來修改數據庫 # 4. 用 uwsgi啓動 django 服務 python manage.py collectstatic --noinput&& python manage.py makemigrations&& python manage.py migrate&& uwsgi --ini /var/www/html/myproject/uwsgi.ini

uwsgi.ini配置文件如下所示:

 [uwsgi]  project=myproject uid=www-data gid=www-data base=/var/www/html  chdir=%(base)/%(project) module=%(project).wsgi:application master=True processes=2  socket=0.0.0.0:8000 chown-socket=%(uid):www-data chmod-socket=664  vacuum=True max-requests=5000  pidfile=/tmp/%(project)-master.pid daemonize=/tmp/%(project)-uwsgi.log  #設置一個請求的超時時間(秒),如果一個請求超過了這個時間,則請求被丟棄 harakiri = 60 post buffering = 8192 buffer-size= 65535 #當一個請求被harakiri殺掉會,會輸出一條日誌 harakiri-verbose = true  #開啓內存使用情況報告 memory-report = true  #設置平滑的重啓(直到處理完接收到的請求)的長等待時間(秒) reload-mercy = 10  #設置工作進程使用虛擬內存超過N MB就回收重啓 reload-on-as= 1024 python-autoreload=1

第三步:編寫Nginx鏡像和容器所需文件

構建Nginx鏡像所使用的Dockerfile如下所示:

 # nginx鏡像compose/nginx/Dockerfile  FROM nginx:latest  # 刪除原有配置文件,創建靜態資源文件夾和ssl證書保存文件夾 RUN rm /etc/nginx/conf.d/default.conf \ && mkdir -p /usr/share/nginx/html/static \ && mkdir -p /usr/share/nginx/html/media \ && mkdir -p /usr/share/nginx/ssl  # 設置Media文件夾用戶和用戶組爲Linux默認www-data, 並給予可讀和可執行權限, # 否則用戶上傳的圖片無法正確顯示。 RUN chown -R www-data:www-data /usr/share/nginx/html/media \ && chmod -R 775 /usr/share/nginx/html/media  # 添加配置文件 ADD ./nginx.conf /etc/nginx/conf.d/  # 關閉守護模式 CMD ["nginx", "-g", "daemon off;"]

Nginx的配置文件如下所示

 # nginx配置文件 # compose/nginx/nginx.conf  upstream django {     ip_hash;     server web:8000; # Docker-compose web服務端口 }  server {     listen 80; # 監聽80端口     server_name localhost; # 可以是nginx容器所在ip地址或127.0.0.1,不能寫宿主機外網ip地址      charset utf-8;     client_max_body_size 10M; # 限制用戶上傳文件大小      location /static {         alias /usr/share/nginx/html/static; # 靜態資源路徑     }      location /media {         alias /usr/share/nginx/html/media; # 媒體資源,用戶上傳文件路徑     }      location / {         include /etc/nginx/uwsgi_params;         uwsgi_pass django;         uwsgi_read_timeout 600;         uwsgi_connect_timeout 600;         uwsgi_send_timeout 600;          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header Host $http_host;         proxy_redirect off;         proxy_set_header X-Real-IP  $remote_addr;        # proxy_pass http://django;  # 使用uwsgi通信,而不是http,所以不使用proxy_pass。     } }      access_log /var/log/nginx/access.log main;     error_log /var/log/nginx/error.log warn;      server_tokens off;

第四步:編寫Db (MySQL)容器配置文件

啓動MySQL容器我們直接使用官方鏡像即可,不過我們需要給MySQL增加配置文件。

 # compose/mysql/conf/my.cnf [mysqld] user=mysql default-storage-engine=INNODB character-set-server=utf8  port            = 3306 # 端口與docker-compose裏映射端口保持一致 #bind-address= localhost #一定要註釋掉,mysql所在容器和django所在容器不同IP  basedir         = /usr datadir         = /var/lib/mysql tmpdir          = /tmp pid-file        = /var/run/mysqld/mysqld.pid socket          = /var/run/mysqld/mysqld.sock skip-name-resolve  # 這個參數是禁止域名解析的,遠程訪問推薦開啓skip_name_resolve。  [client] port = 3306 default-character-set=utf8  [mysql] no-auto-rehash default-character-set=utf8

我們還需設置MySQL服務啓動時需要執行的腳本命令, 注意這裏的用戶名和password必需和docker-compose.yml裏與MySQL相關的環境變量保持一致。

 # compose/mysql/init/init.sql GRANT ALL PRIVILEGES ON myproject.* TO dbuser@"%" IDENTIFIED BY "password"; FLUSH PRIVILEGES;

第五步:編寫Redis 容器配置文件

啓動redis容器我們直接使用官方鏡像即可,不過我們需要給redis增加配置文件。大部分情況下采用默認配置就好了,這裏我們只做出了如下幾條核心改動:

 # compose/redis/redis.conf # Redis 5配置文件下載地址 # https://raw.githubusercontent.com/antirez/redis/5.0/redis.conf  # 請註釋掉下面一行,變成#bind 127.0.0.1,這樣其它機器或容器也可訪問 bind 127.0.0.1  # 取消下行註釋,給redis設置登錄密碼。這個密碼django settings.py會用到。 requirepass yourpassword

第六步:修改Django項目settings.py

在你準備好docker-compose.yml並編排好各容器的Dockerfile及配置文件後,請先不要急於使用Docker-compose命令構建鏡像和啓動容器。這時還有一件非常重要的事情要做,那就是修改Django的settings.py, 提供mysql和redis服務的配置信息。最重要的幾項配置如下所示:

 # 生產環境設置 Debug = False Debug = False  # 設置ALLOWED HOSTS ALLOWED_HOSTS = ['your_server_IP', 'your_domain_name']  # 設置STATIC ROOT 和 STATIC URL STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_URL = "/static/"  # 設置MEDIA ROOT 和 MEDIA URL MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = "/media/"  # 設置數據庫。這裏用戶名和密碼必需和docker-compose.yml裏mysql環境變量保持一致 DATABASES = {     'default': {         'ENGINE': 'django.db.backends.mysql',         'NAME': 'myproject', # 數據庫名         'USER':'dbuser', # 你設置的用戶名 - 非root用戶         'PASSWORD':'password', # # 換成你自己密碼         'HOST': 'db', # 注意:這裏使用的是db別名,docker會自動解析成ip         'PORT':'3306', # 端口     } }  # 設置redis緩存。這裏密碼爲redis.conf裏設置的密碼 CACHES = {     "default": {         "BACKEND": "django_redis.cache.RedisCache",         "LOCATION": "redis://redis:6379/1", #這裏直接使用redis別名作爲host ip地址         "OPTIONS": {             "CLIENT_CLASS": "django_redis.client.DefaultClient",             "PASSWORD": "yourpassword", # 換成你自己密碼         },     } }

第七步:使用docker-compose 構建鏡像並啓動容器組服務

現在我們可以使用docker-compose命名構建鏡像並啓動容器組了。

 # 進入docker-compose.yml所在文件夾,輸入以下命令構建鏡像 sudo docker-compose build # 查看已生成的鏡像 sudo docker images # 啓動容器組服務 sudo docker-compose up # 查看運行中的容器 sudo docker ps

如果一切順利,此時你應該可以看到四個容器都已經成功運行了。

format,png

第八步:進入web容器內執行Django命令並啓動uwsgi服務器

雖然我們四個容器都已啓動運行,但我們還沒有執行Django相關命令並啓動uwsgi服務器。現在我們只需進入web容器執行我們先前編寫的腳本文件start.sh即可。

 sudo docker exec -it myprojectdocker_web_1 /bin/bash start.sh

此時打開你的瀏覽器,輸入你服務器的ip地址或域名指向地址,你就應該可以看到網站已經上線啦。

小結

本文詳細地介紹瞭如何使用docker-compose工具分八步在生成環境下部署Django + Uwsgi + Nginx + MySQL + Redis。過程看似很複雜,但很多Dockerfile,項目佈局及docker-compose.yml都是可以複用的。花時間學習並練習本章內容是非常值得的,一但你學會了,基本上可以10分鐘內完成一個正式Django項目的部署,而且可以保證在任何一臺Linux機器上順利地運行。

注意:整個部署過程如果遇到問題,可以給我們留言或發信息,可以節省你的時間。

喜歡我們的文章,獲取更多幹貨內容,歡迎關注【Python Web與Django開發】並加星標哦。感謝大家的支持!

format,png

format,png

大江狗

2020.5.25

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章