Docker+Flask 實戰:打造高并發微服務架構
今天我們要深入探討一個非常熱門且實用的主題:基于 Docker 部署 Python Flask 應用。Docker 作為當下最流行的容器化技術,已經廣泛應用于各種開發和部署場景,尤其是在微服務架構中。而 Flask 作為 Python 世界里輕量級的 Web 框架,同樣備受開發者青睞。將二者結合,能極大地提高我們應用的部署效率和可移植性。接下來,我們就一起通過一個完整的實例來學習如何實現這個過程。
測試虛擬機環境介紹
在開始之前,先給大家介紹一下我們使用的測試虛擬機環境。我們使用的是 VMware,配置為 4G 內存和 2 核心。這樣的配置對于我們今天的演示來說是足夠的,當然在實際生產環境中,你可能需要根據具體的應用需求來調整硬件資源。
Docker + Flask 部署實例
1. 項目代碼
首先,我們來看一下 Flask 應用的代碼,它在 app.py
文件中。
from flask import Flask
app = Flask(__name__)@app.route('/')
def hello():return "Hello Docker World!"if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)
這段代碼非常簡單,我們導入了 Flask 框架,創建了一個 Flask 應用實例,然后定義了一個路由 /
,當訪問這個路由時,會返回一個簡單的字符串。最后,我們通過 app.run
方法啟動了這個應用,并將其監聽在 0.0.0.0:5000
上,這樣容器內的服務就能監聽所有網絡接口,而不僅僅是本地環回地址。
2. Dockerfile 配置
接下來,我們要在項目根目錄創建一個 Dockerfile
。
# 使用輕量級 Python 基礎鏡像
FROM python:3.11-slim# 設置工作目錄
WORKDIR /app# 代碼到容器
COPY . .# 安裝依賴(使用國內鏡像加速)
RUN pip install --no-cache-dir -r requirements.txt# 暴露端口
EXPOSE 5000# 啟動命令
CMD ["python", "app.py"]
這里面有幾個關鍵的指令需要大家注意。首先是 FROM python:3.10-slim
,我們使用的是官方的輕量級 Python 鏡像,它的體積只有約 127MB,相比 centos
鏡像更加高效,這對于我們構建和部署鏡像來說可以節省很多時間和存儲空間。然后是 --no-cache-dir
這個參數,它的作用是避免緩存依賴,確保每次構建時都能安裝最新版本的依賴。EXPOSE 5000
聲明了容器暴露的端口,不過需要注意的是,這只是一個聲明,實際運行容器時還需要結合 -p
參數將容器端口映射到宿主機上。最后,CMD
指令定義了容器啟動時要執行的命令。
3. 依賴管理
我們還需要創建一個 requirements.txt
文件,用來列出所有的依賴。
Flask==3.0.0
明確指定依賴的版本號是非常重要的,這樣可以避免依賴沖突。建議大家在本地使用虛擬環境進行測試,確保所有依賴都能正常工作后,再進行打包。
4. 構建鏡像
在項目目錄下執行以下命令來構建鏡像:
docker build --no-cache -t my-flask-app .
這里的 --no-cache
參數是強制重新構建鏡像,避免緩存導致依賴未更新。
5. 運行容器
構建好鏡像后,我們就可以啟動容器并映射端口了。
docker run -d --name flask-container -p 8080:5000 my-flask-app
-d
參數表示后臺運行容器,-p 8080:5000
是將宿主機的 8080 端口映射到容器的 5000 端口,--name
則是為容器指定一個名稱,方便我們后續管理。
6. 測試訪問
我們可以通過瀏覽器或終端來訪問我們的應用:
curl http://localhost:8080
# 或
http://127.0.0.1:8080
如果一切正常,你應該能看到 Hello Docker World!
這個輸出。
7. 調試與維護
在實際開發過程中,調試和維護是非常重要的。我們可以通過以下命令來查看容器的日志:
docker logs flask-container
如果需要進入容器的終端進行一些操作,可以使用:
docker exec -it flask-container /bin/bash
當我們修改了 app.py
中的代碼后,需要重新執行 docker build
和 docker run
命令,或者使用 docker-compose
來簡化這個流程。
完整高級優化實例
文件結構
下面我們來看一個更高級的優化實例,首先看一下文件結構:
.
├── docker-compose.yml # 編排配置
├── Dockerfile # 多階段構建
├── app
│ ├── app.py # Flask 主程序
│ └── requirements.txt # 精確依賴
└── nginx└── nginx.conf # 反向代理配置
Dockerfile(含多階段構建)
# 構建階段
FROM python:3.11-slim-bullseye AS builder
WORKDIR /build
COPY app/requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt# 運行階段
FROM python:3.11-slim-bullseye
WORKDIR /app# 安全配置
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser:appuser /app
USER appuser# 依賴安裝
# 這里要注意的是 /root/.local 不要寫成 /home/appuser/.local,因為你的系統內沒有這個目錄
# 我是在 root 用戶下執行的操作,所以是/root/.local
COPY --from=builder /root/.local /home/appuser/.local
ENV PATH=/home/appuser/.local/bin:$PATH# 應用代碼
COPY app .# 健康檢查
HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:5000/health || exit 1
# 啟動命令
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "9", "--worker-class", "gevent", "app:app"]
這里使用了多階段構建的方式,在構建階段我們只安裝依賴,然后在運行階段將依賴復制過來,并使用非 root 用戶運行應用,提高了容器的安全性。同時,我們還添加了健康檢查,確保容器內的服務正常運行。啟動命令使用了 gunicorn
作為 WSGI 服務器,并使用 gevent
作為工作類,提高了應用的并發性能。
app/app.py(含監控端點)
from flask import Flask
from prometheus_client import generate_latest, Counterapp = Flask(__name__)
REQUEST_COUNTER = Counter('http_requests_total', 'Total HTTP Requests')@app.route('/')
def hello():REQUEST_COUNTER.inc()return "Hello Production Docker World!"@app.route('/health')
def health():return "OK", 200@app.route('/metrics')
def metrics():return generate_latest()
在這個 app.py
中,我們添加了一些監控端點。/health
端點用于健康檢查,/metrics
端點用于提供 Prometheus 監控數據,這樣我們就能更好地監控應用的運行狀態。
app/requirements.txt(精確版本控制)
Flask==3.0.0
gunicorn==21.2.0
gevent==23.9.1
prometheus-client==0.20.0
精確控制依賴的版本號可以避免因依賴版本不一致而導致的問題。
docker-compose.yml(生產編排)
version: '3.11'services:web:build: .ports:- "5000:5000"deploy:resources:limits:cpus: '2.0'memory: 1GBlogging:driver: "json-file"options:max-size: "100m"nginx:image: nginx:1.25ports:- "80:80"volumes:- ./nginx/nginx.conf:/etc/nginx/nginx.confdepends_on:- web
docker-compose.yml
是用于生產編排的,我們可以通過它來定義多個服務,并進行資源管控和日志配置。這里我們定義了 web
和 nginx
兩個服務,web
服務構建我們的 Flask 應用,nginx
服務作為反向代理,將請求轉發到 web
服務上。
nginx/nginx.conf(反向代理配置)
worker_processes auto;events {worker_connections 1024;
}http {upstream flask {server web:5000;}server {listen 80;location / {proxy_pass http://flask;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}}
}
這個 nginx.conf
文件配置了反向代理,將所有請求轉發到 web
服務的 5000 端口上。
部署流程
# 構建鏡像
docker-compose build# 啟動集群
docker-compose up -d# 驗證部署
curl http://localhost/health
通過這幾個簡單的命令,我們就可以完成整個應用的部署和驗證。
關鍵優化點說明
最后,我們來看一下這個高級優化實例的關鍵優化點:
優化方向 | 實現方案 |
---|---|
?鏡像體積? | 多階段構建(162MB 鏡像) |
?并發性能? | Gunicorn + Gevent(9 workers) |
?安全基線? | 非 root 用戶運行 + 文件權限控制 |
?可觀測性? | Prometheus 監控端點 + 健康檢查 |
?資源管控? | CPU/內存限制 + 日志滾動策略 |
我們還進行了壓力測試,所有參數均通過 wrk -t12 -c400 -d30s http://localhost/
壓力測試驗證,QPS 為1125,可能是虛擬機的原因吧。
下面的幾個版本的對比:
鏡像 | 組件 | 鏡像大小 | QPS |
---|---|---|---|
flask-app:v1 | Python:3.11 +Flask3.0 | 138MB | 1000.95 |
flask-app:v2 | Python:3.11-alpine +Flask3.0 | 58.6MB | 899.78 |
flask-nginx-web:v1 | Python:3.11-slim-bullseye +Flask3.0+gunicorn21.2.0+ gevent23.9.1+prometheus-client0.20.0 | 162MB | 1125.04 |
flask-nginx-web:v2 | Python:3.11-alpine +Flask3.0+gunicorn21.2.0+ gevent23.9.1+prometheus-client0.20.0 | 92.7MB | 1049.89 |
alpin鏡像確實小,從QPS的角度來看,并不是什么都用alpin鏡像就好的。
總結
今天我們學習了如何基于 Docker 部署 Python Flask 應用,從簡單的實例到高級優化實例,希望大家能對 Docker 的使用有更深入的理解。在實際開發中,大家可以根據自己的需求進行調整和優化,充分發揮 Docker 的優勢。如果大家有任何問題,歡迎隨時提問。謝謝大家!