在『伺服器部署 Vue 和 Django 項目的全記錄』一文中,介紹了在伺服器中使用 Nginx 部署前後端項目的過程。然而,當 Web 應用流量增多時,需要考慮負載均衡、流量分發、容災等情況,原生的部署方式通常難以滿足需求。此時,引入 Docker 部署多節點,能夠在單台高性能伺服器或伺服器集群中... ...
在『伺服器部署 Vue 和 Django 項目的全記錄』一文中,介紹了在伺服器中使用 Nginx 部署前後端項目的過程。然而,當 Web 應用流量增多時,需要考慮負載均衡、流量分發、容災等情況,原生的部署方式通常難以滿足需求。此時,引入 Docker 部署多節點,能夠在單台高性能伺服器或伺服器集群中搭建更完善的部署架構。
本文主要以 Vue 和 Django 項目為例介紹 Docker 部署的流程,稍帶 Docker 簡介和基礎的 Nginx 負載均衡配置。
Docker 簡介與安裝
簡單介紹 Docker 相關概念,具體需要讀者另外學一學,推薦『Docker-從入門到實踐』
Docker 是什麼
Docker 是一個開源的應用容器引擎,可以讓開發者打包應用和依賴到一個輕量級、可移植的容器中,併發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器使用沙箱機制,相互之間不存在介面,其與宿主機通過埠轉發進行通信,性能開銷低。
Docker 部署 Web 應用有以下優點:
- 容器適合持續集成和持續交付(CI/CD)流程
- 響應式部署和擴展,其可移植性和輕量級的特性支持實時擴展或拆除服務
- Docker 輕巧快速,支持開發者在同一機器上運行更多工作負載
Docker 工作流程
Docker 包括三個概念:
- 鏡像(Image):相當於一個 root 文件系統。
- 容器(Container):鏡像和容器的關係類似於對象程式設計中的類和實例,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等。
- 倉庫(Repository):倉庫可看成一個代碼控制中心,用來保存鏡像。
Docker 的工作流程通常為:
- 從倉庫中拉取(pull)官方或基準鏡像
- 在 Dockerfile 中描述應用和安裝依賴的指令,構建鏡像
- 由鏡像創建和運行容器
Docker 安裝
部署架構
在不考慮多節點負載均衡時,本文的部署架構如下:
前後端項目分離部署,分別部署在兩個 Nginx 節點,對應兩個功能變數名稱或兩個埠。
Nginx + Docker 部署前端
首先,Vue 項目打包為 dist 文件夾,同目錄下新建 Dockerfile
、vhosts.conf
文件和 logs
文件夾,作用見下列代碼塊中的註釋。
.
├── dist # Vue 項目打包用以部署的文件夾
├── Dockerfile # 用於建立 Docker 鏡像
├── vhosts.conf # 容器中啟動 Nginx 服務的配置文件
└── logs # 映射容器中的 Nginx 日誌目錄,以便在宿主機查看日誌
Dockerfile
文件內容:
# 設置基礎鏡像
FROM nginx:latest
#設置CTS時區
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone
# 將dist文件中的內容複製到 /usr/share/nginx/html/ 這個目錄下麵
COPY ./dist /usr/share/nginx/html/
#用本地的 vhosts.conf 配置來替換 nginx 鏡像里的預設配置
COPY vhosts.conf /etc/nginx/conf.d/vhosts.conf
vhosts.conf
文件內容:
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
構建 Docker 鏡像
在終端中輸入以下命令,根據 Dockerfile
構建鏡像。
docker build -f Dockerfile -t nginx/hsheng-mall:v1.0.0 .
# 鏡像名稱:nginx/hsheng-mall
# 版本號:v1.0.0
運行 Docker 容器
依據創建的鏡像 nginx/hsheng-mall:v1.0.0
運行容器。
docker run -d -p 8081:80 --name=hsheng-mall -v /home/hsheng/www/hsheng-mall/logs:/var/log/nginx nginx/hsheng-mall:v1.0.0
# 宿主機 8081 埠映射容器 80 埠
# 容器名稱:hsheng-mall
# 宿主機 /home/hsheng/www/hsheng-mall/logs 目錄映射容器 /var/log/nginx 目錄
# 鏡像名稱:nginx/hsheng-mall:v1.0.0
宿主機 Nginx 轉發
與原生 Nginx 部署類似,在 /etc/nginx/conf.d
目錄下創建配置文件 hsheng-mall.conf
,內容如下:
server {
listen 443 ssl; # 埠,若部署 https 功能變數名稱則為 443
server_name aaa.abc.com; # 功能變數名稱或 IP
location / {
proxy_pass http://127.0.0.1:8081; # 轉發本機(宿主機) 8081 埠,已與 Docker 埠建立映射
proxy_redirect default;
}
ssl_certificate /home/hsheng/www/hsheng-mall/ssl_certs/aaa.abc.com_bundle.crt; # ssl證書絕對路徑
ssl_certificate_key /home/hsheng/www/hsheng-mall/ssl_certs/aaa.abc.com.key; # ssl證書私鑰絕對路徑
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
}
server {
listen 80;
server_name aaa.abc.com;
# 把http的功能變數名稱請求轉成https
return 301 https://$host$request_uri;
}
而後重啟 Nginx 服務,即可通過 server_name
訪問 Docker 的應用服務。
sudo nginx -s reload
Nginx + Docker 部署 uWSGI 後端
項目部署目錄結構如下:
.
├── src # Django 項目源碼
│ ├── manage.py
│ ├── requirements.txt # Python 項目依賴包
│ ├── uwsgi.ini # uWSGI 配置文件
│ ├── start.sh # Django 服務啟動腳本
| └── ...
├── Dockerfile # 用於建立 Docker 鏡像
└── logs # 映射容器中的 uWSGI 日誌目錄,以便在宿主機查看日誌
Dockerfile
文件內容:
FROM python:3.8
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' > /etc/timezone
RUN mkdir -p /var/www/html/backend
COPY ./src /var/www/html/backend/
WORKDIR /var/www/html/backend
RUN pip install -i https://pypi.doubanio.com/simple uwsgi
RUN pip install -i https://pypi.doubanio.com/simple/ -r requirements.txt
# Windows環境下編寫的start.sh每行命令結尾有多餘的\r字元,需移除
RUN sed -i 's/\r//' ./start.sh
RUN chmod +x ./start.sh
uwsgi.ini
文件內容:
[uwsgi]
socket = 0.0.0.0:8000 # 容器內 uWSGI 服務須為 0.0.0.0,以便與宿主機建立正常連接
project = backend
base = /var/www/html
base-app = hshengmall
chdir = %(base)/%(project)
wsgi-file = %(base)/%(project)/%(base-app)/wsgi.py
master = true
processes = 8
threads = 4
enable-threads = true
buffer-size = 65536
post-buffering = 32768
vacuum = true
pidfile = %(base)/uwsgi/%(project)-master.pid
daemonize = %(base)/uwsgi/uwsgi.log
chmod-socket = 664
# 設置一個請求的超時時間(秒),如果一個請求超過了這個時間,則請求被丟棄
harakiri = 300
# 當一個請求被harakiri殺掉會,會輸出一條日誌
harakiri-verbose = true
start.sh
文件內容:
python manage.py makemigrations&&
python manage.py migrate&&
uwsgi --ini /var/www/html/backend/uwsgi.ini
構建 Docker 鏡像
在終端中輸入以下命令,根據 Dockerfile
構建鏡像。
docker build -f Dockerfile -t python/hsheng-mall-backend:v1.0.0 .
運行 Docker 容器
依據創建的鏡像 python/hsheng-mall-backend:v1.0.0
運行容器。
docker run -it -p 8001:8000 --name=hsheng-mall-backend -v /home/hsheng/www/hsheng-mall-backend/logs:/var/www/html/uwsgi -d python/hsheng-mall-backend:v1.0.0
啟動服務
進入容器:
docker exec -it <container_id> /bin/bash
運行啟動腳本:
./start.sh
這樣即成功啟動了一個後端服務容器,若想做多節點負載均衡,可以修改埠映射關係,按上述步驟多創建和啟動幾個容器。
宿主機 Nginx 轉發
在 /etc/nginx/conf.d
目錄下創建配置文件 hsheng-mall-backend.conf
,內容如下:
server {
listen 443 ssl;
server_name api-aaa.abc.com;
location / {
include /etc/nginx/uwsgi_params;
uwsgi_pass 127.0.0.1:8001; # 轉發本機(宿主機) 8001 埠,已與 Docker 埠建立映射
}
ssl_certificate /home/hsheng/www/hsheng-mall-backend/ssl_certs/api-aaa.abc.com_bundle.crt;
ssl_certificate_key /home/hsheng/www/hsheng-mall-backend/ssl_certs/api-aaa.abc.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
}
server {
listen 80;
server_name api-aaa.abc.com;
return 301 https://$host$request_uri;
}
而後重啟 Nginx 服務,即可通過 server_name
請求後端服務。
sudo nginx -s reload
*Nginx 負載均衡
簡略描述一個負載均衡的結構:Nginx + Docker 多節點部署架構。
前端節點為靜態節點,通常只需要單個節點即可,可使用 CDN 加速優化訪問。因此,當請求流量大時,主要通過增多後端 Docker+uWSGI 節點進行負載均衡。
為實現上圖架構,首先根據本文第四章節「Docker 部署 uWSGI 後端節點」創建和啟動 3 個 Docker 後端服務節點,分別映射至宿主機 8001 ~ 8003 埠。
而後,修改宿主機用於部署後端的 Nginx 配置文件,例如本文的 hsheng-mall-backend.conf
,添加 upstream
。修改後文件內容應為:
upstream uwsgicluster {
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
server {
listen 443 ssl;
server_name api-aaa.abc.com;
location / {
include /etc/nginx/uwsgi_params;
uwsgi_pass uwsgicluster # 轉發上游集群
}
ssl_certificate /home/hsheng/www/hsheng-mall-backend/ssl_certs/api-aaa.abc.com_bundle.crt;
ssl_certificate_key /home/hsheng/www/hsheng-mall-backend/ssl_certs/api-aaa.abc.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
}
server {
listen 80;
server_name api-aaa.abc.com;
return 301 https://$host$request_uri;
}
可以看到,與單節點不同之處在於,新增了 upstream
定義,並將 uwsgi_pass
修改為定義的 upstream
名稱。
在配置文件中,還能設置各個節點的權重分配等,此處不展開介紹,預設為輪詢方式,請求隨機派發到各節點。