這也是很容易誤會的一點。
不是:
用了 Docker
就完全沒有 systemd
Docker Engine 本身在 Linux 上往往就是由 systemd 管理。
例如:
systemctl status docker
所以可能是:
systemd
│
▼
Docker Daemon
│
▼
Container
│
▼
FastAPI
差別是:
以前:
systemd
→ 直接管理 FastAPI Process
現在:
systemd
→ 管理 Docker
Docker
→ 管理 FastAPI Container
目前:
Container
映射:
Host :8000
所以原本 Nginx:
location / {
proxy_pass http://127.0.0.1:8000;
}
完全可以不改。
架構:
Internet
│
▼
Nginx
Host :80
│
▼
Host :8000
│
▼
Docker Port Mapping
│
▼
Container :8000
│
▼
FastAPI
因此這次從 systemd 改 Docker,對外部使用者甚至可以完全沒有感覺。
如果只有 Host 上的 Nginx 需要存取 FastAPI,可以不把:
8000
開給所有 Network Interface。
不要:
-p 8000:8000
而可以:
-p 127.0.0.1:8000:8000
表示:
Host 127.0.0.1:8000
↓
Container 8000
這樣外部網路不能直接:
server-ip:8000
存取 FastAPI。
只能:
Nginx
→ localhost:8000
架構跟前面的 systemd 部署保持一致:
Internet
│
▼
Nginx :80/:443
│
▼
127.0.0.1:8000
│
▼
Container
前面的 FastAPI 已經有:
/health
Dockerfile 也可以加入:
HEALTHCHECK \
--interval=30s \
--timeout=3s \
--start-period=5s \
--retries=3 \
CMD python -c \
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
完整 Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
-r requirements.txt
COPY app ./app
HEALTHCHECK \
--interval=30s \
--timeout=3s \
--start-period=5s \
--retries=3 \
CMD python -c \
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')"
CMD [
"uvicorn",
"app.main:app",
"--host",
"0.0.0.0",
"--port",
"8000"
]
查看:
docker ps
一段時間後可能顯示:
Up ... (healthy)
因此:
/health
開始真正被 Infrastructure 使用。
之後到了 Kubernetes,這個概念就會演進成:
Liveness Probe
Readiness Probe
Docker 很重要的一個思維就是:
Immutable Image。
可以理解成:
Image Build 完之後,不要再去修改裡面的 Application。
例如:
backend:v1
應該永遠代表同一份內容。
如果程式修改:
重新 Build
產生:
backend:v2
而不是:
進 Container
vim main.py
修改 Production Code。
因為一旦這樣做:
Running Container
跟
Docker Image
就會不一致。
Container 重建後,修改又全部消失。