欢迎来到第十一期!
在第一阶段的收官之作 Episode 10 中,你从零搭建了一个名为 **FinanceFlow** 的个人财务 Dashboard 项目——有后端(FastAPI + SQLite),有前端(React + Chart.js),还有 AI 智能分析(LangChain)。
**但有一个问题:** 你的代码跑在你自己的电脑上,换一台机器就可能会报错——"Python 版本不对"、"依赖没装全"、"环境变量缺失"……这种 **「在我机器上是好的」** 的痛点,几乎是每个程序员成长路上必经的一课。
**本期解决方案:Docker。**
学了这一期,你将能够:把任何 Python 项目打包成一个**可以在任何地方一键运行**的容器镜像。
>**前置知识**:需要熟悉本系列前面 10 期的内容,特别是 Episode 10 的 FinanceFlow 项目。
---
## 11.1 为什么需要 Docker?
### 11.1.1 现实困境
在 Docker 出现之前,部署一个 Python 应用通常需要经历这些痛苦:
```
开发环境(你的电脑):
Python 3.12
pip install fastapi uvicorn pandas matplotlib
一切正常运行 ✅
生产服务器(别人的电脑):
Python 3.8 ← 版本不匹配 ❌
库没装全 ❌
系统缺少依赖库(比如 libpq-dev)❌
环境变量不对 ❌
运行失败 💥
```
这就是著名的 **"它在我机器上是好的"** 问题。
### 11.1.2 Docker 的思路
Docker 的做法是:**不只打包代码,还打包一切——操作系统、解释器、依赖库、环境变量、甚至运行时配置。**
```
Docker 镜像 = 你的代码 + Python 解释器 + 所有依赖 + 系统库 + 配置文件
↓
在任何地方 "运行" 这个镜像
↓
保证行为完全一致 ✅
```
### 11.1.3 核心概念
| 概念 | 说明 | 类比 |
|------|------|------|
| **镜像(Image)** | 只读的模板,包含了运行应用所需的一切 | 安装光盘 |
| **容器(Container)** | 镜像的运行实例,是可读写的 | 从光盘里运行的程序 |
| **Dockerfile** | 定义如何构建镜像的文件 | 食谱 |
| **Docker Compose** | 定义多容器应用的配置文件 | 餐厅菜单(多个菜的组合) |
| **Registry(仓库)** | 存储和分发镜像的地方(如 Docker Hub) | 应用商店 |
---
## 11.2 第一个 Docker 镜像
### 11.2.1 Hello World 示例
先从一个最简单的例子入手。创建一个 `hello.py`:
```python
# hello.py
print("你好,Docker!")
print(f"Python 版本: {__import__('sys').version}")
```
接下来写 Dockerfile:
```dockerfile
# Dockerfile(第一版:最简单)
FROM python:3.12-slim
COPY hello.py /app/hello.py
CMD ["python", "/app/hello.py"]
```
逐行解释:
-`FROM python:3.12-slim`:基础镜像。python:3.12 表示使用 Python 3.12,`slim` 是精简版(体积小)。
-`COPY hello.py /app/hello.py`:将本地的 hello.py 复制到镜像中的 /app/ 目录。
-`CMD`:容器启动时默认执行的命令。
构建和运行:
```bash
# 1. 构建镜像(-t 给镜像起个名字)
dockerbuild-tmy-hello.
# 2. 运行容器
dockerrunmy-hello
# 输出:
# 你好,Docker!
# Python 版本: 3.12.x (main, ...)
```
### 11.2.2 交互式容器
试试进入容器的 Shell:
```bash
# 启动容器并进入交互模式
dockerrun-it--rmpython:3.12-slim/bin/bash
# 在容器内部
root@abc123:/#python--version
Python3.12.x
root@abc123:/#ls
binbootdevetchomelibmediaoptprocrootrunsbinsrvsystmpusrvar
root@abc123:/#exit
```
`-it` 表示交互式终端,`--rm` 表示容器停止后自动删除。
---
## 11.3 为 FinanceFlow 项目编写 Dockerfile
现在回到 Episode 10 的 FinanceFlow 项目,这是我们要容器化的目标。
### 11.3.1 项目结构回顾
```
financeflow/
├── backend/
│ ├── main.py # FastAPI 入口
│ ├── models.py # Pydantic 模型
│ ├── database.py # 数据库连接
│ ├── crud.py # CRUD 操作
│ ├── ai.py # AI 分析
│ └── requirements.txt # Python 依赖
├── frontend/
│ └── financeflow-dashboard/ # React 项目
│ ├── package.json
│ └── src/
└── docker-compose.yml # 多容器编排文件
```
### 11.3.2 后端 Dockerfile
```dockerfile
# backend/Dockerfile
# ==================== 构建阶段 ====================
FROM python:3.12-slim AS builder
# 设置工作目录
WORKDIR /build
# 安装构建依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖文件
COPY requirements.txt .
# 创建虚拟环境并安装依赖
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --no-cache-dir -r requirements.txt
# ==================== 运行阶段 ====================
FROM python:3.12-slim AS runner
# 创建非 root 用户(安全最佳实践)
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 从 builder 阶段复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 设置工作目录
WORKDIR /app
# 复制应用代码
COPY --chown=appuser:appuser main.py models.py database.py crud.py ai.py ./
# 创建数据目录
RUN mkdir -p /app/data && chown appuser:appuser /app/data
# 切换到非 root 用户
USER appuser
# 暴露端口
EXPOSE 8000
# 健康检查(让 Docker Compose 知道服务是否就绪)
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')" || exit 1
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```
关键点解析:
1.**多阶段构建**:`AS builder` 和 `AS runner` 是两个阶段。构建阶段安装编译工具,运行阶段只保留必要的东西。这样可以做出**极小体积**的镜像。
2.**非 root 用户**:生产环境中不应该以 root 身份运行应用,这是基本的安全要求。
3.**健康检查**:`HEALTHCHECK` 让 Docker 自动监控服务状态。
4.**`--no-cache-dir`**:pip 安装时不缓存下载的包,减少镜像体积。
### 11.3.3 前端 Dockerfile
```dockerfile
# frontend/Dockerfile
# ==================== 构建阶段 ====================
FROM node:20-alpine AS builder
WORKDIR /build
# 复制依赖文件(先复制再复制源码,利用 Docker 缓存层)
COPY package.json package-lock.json* ./
RUN npm ci --only=production
# 复制源码
COPY . .
# 构建生产版本
RUN npm run build
# ==================== 运行阶段 ====================
FROM nginx:alpine AS runner
# 复制构建产物到 Nginx
COPY --from=builder /build/dist /usr/share/nginx/html
# 自定义 Nginx 配置(CORS + API 代理)
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
对应的 `nginx.conf`:
```nginx
# frontend/nginx.conf
server {
listen 80;
server_name localhost;
# 前端静态文件
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html; # SPA 路由支持
}
# API 代理到后端
location /api/ {
proxy_pass http://backend:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 优化静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
```
注意 `proxy_pass http://backend:8000/api/;` 中的 `backend` 是 Docker Compose 中后端服务的名字,Docker 内置 DNS 会自动解析。
### 11.3.4 依赖文件
```text
# backend/requirements.txt
fastapi==0.115.0
uvicorn[standard]==0.30.6
pydantic==2.9.2
sqlite3 # 内置,不需要
langchain==0.3.0
langchain-openai==0.2.0
openai==1.45.0
python-multipart==0.0.9
```
---
## 11.4 Docker Compose:多容器编排
一个完整的 FinanceFlow 项目至少需要两个服务:**后端 API** 和 **前端 Web 服务器**。Docker Compose 让你用一个命令管理它们。
### 11.4.1 docker-compose.yml
```yaml
# docker-compose.yml
version: "3.9"
services:
# ========== 后端服务 ==========
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: financeflow-backend
ports:
- "8000:8000"
volumes:
# 挂载数据目录(SQLite 数据库持久化)
- db-data:/app/data
# 开发时用 volume 挂载代码,实现热重载
- ./backend:/app
environment:
- PYTHONUNBUFFERED=1
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
# ========== 前端服务 ==========
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: financeflow-frontend
ports:
- "3000:80"
depends_on:
backend:
condition: service_healthy# 等后端健康后再启动
restart: unless-stopped
# ========== 持久化卷 ==========
volumes:
db-data:
driver: local
```
关键概念:
-**`build`**:指定 Dockerfile 的位置和构建上下文
-**`ports`**:端口映射,`宿主机端口:容器端口`
-**`volumes`**:数据持久化和代码热重载
-**`environment`**:环境变量,`${VAR:-default}` 语法表示有值就用,没值就用默认
-**`depends_on` + `condition`**:依赖管理和启动顺序
-**`restart: unless-stopped`**:除非手动停止,否则 Docker 重启时自动恢复
### 11.4.2 .env 环境变量文件
```bash
# .env
OPENAI_API_KEY=sk-your-actual-api-key-here
```
在 docker-compose.yml 中引用:`${OPENAI_API_KEY}` 会从 `.env` 文件中读取。这样**不会把密钥提交到 Git**。
记得在 `.gitignore` 中加入:
```
.env
__pycache__/
*.pyc
*.db
node_modules/
dist/
```
---
## 11.5 构建与运行
### 11.5.1 常用命令
```bash
# 1. 构建并启动所有服务
dockercomposeup--build
# 2. 后台运行(推荐)
dockercomposeup-d--build
# 3. 查看运行中的容器
dockerps
# 输出:
# CONTAINER ID NAME STATUS PORTS NAMES
# abc123 financeflow-backend Up 2 min (healthy) 0.0.0.0:8000->8000/tcp financeflow-backend
# def456 financeflow-frontend Up 1 min 0.0.0.0:3000->80/tcp financeflow-frontend
# 4. 查看日志
dockercomposelogs-fbackend# 实时查看后端日志
dockercomposelogs-ffrontend# 实时查看前端日志
# 5. 查看前端
# 浏览器打开 http://localhost:3000
# 6. 查看后端 API 文档
# 浏览器打开 http://localhost:8000/docs
# 7. 进入容器 Shell
dockerexec-itfinanceflow-backend/bin/bash
# 8. 停止服务(保留容器)
dockercomposestop
# 9. 删除所有容器(保留镜像和数据卷)
dockercomposedown
# 10. 彻底清理(删除容器、网络)
dockercomposedown--remove-orphans
# 11. 删除所有数据(包括数据库)
dockercomposedown-v
```
### 11.5.2 开发模式 vs 生产模式
```yaml
# docker-compose.dev.yml — 开发模式:代码热重载
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev# 开发版 Dockerfile
volumes:
- ./backend:/app# 挂载源码,改代码即刻生效
ports:
- "8000:8000"
environment:
- PYTHONUNBUFFERED=1
- DEBUG=true
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev# 开发版 Dockerfile
ports:
- "3000:5173"
volumes:
- ./frontend:/app# 挂载源码
command: npm run dev -- --host 0.0.0.0
```
开发版 Dockerfile(`backend/Dockerfile.dev`):
```dockerfile
# backend/Dockerfile.dev — 开发专用
FROM python:3.12-slim
WORKDIR /app
# 开发时需要完整依赖(包括 gcc 等编译工具)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 开发时以 root 运行(方便),用 --reload 实现热重载
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
```
```yaml
# docker-compose.prod.yml — 生产模式:精简、安全
# 使用前面 11.3 节写的生产 Dockerfile
version: "3.9"
services:
backend:
build:
context: ./backend
dockerfile: Dockerfile# 生产 Dockerfile
container_name: financeflow-backend
ports:
- "8000:8000"
volumes:
- db-data:/app/data
environment:
- PYTHONUNBUFFERED=1
- DEBUG=false
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile# 生产 Dockerfile
container_name: financeflow-frontend
ports:
- "3000:80"
depends_on:
backend:
condition: service_healthy
restart: unless-stopped
volumes:
db-data:
driver: local
```
启动生产环境:
```bash
dockercompose-fdocker-compose.prod.ymlup-d--build
```
---
## 11.6 镜像优化技巧
### 11.6.1 减小镜像体积
**未优化的镜像:**
```bash
dockerbuild-tmy-app:bad./backend
dockerimagesmy-app:bad
# 输出:my-app:bad 1.2GB
```
1.2 GB?太大了!让我们看看怎么优化。
**优化策略一:使用 slim/alpine 基础镜像**
```dockerfile
FROM python:3.12-slim # 而不是 python:3.12(完整版约 900MB,slim 约 150MB)
```
**优化策略二:多阶段构建**(见 11.3.2,我们已经用了 ✅)
**优化策略三:合并 RUN 指令,利用缓存**
```dockerfile
# ❌ 差的写法:每行 RUN 都产生一层新镜像
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y libpq-dev
RUN rm -rf /var/lib/apt/lists/*
# ✅ 好的写法:合并为一条 RUN,apt 的缓存只在第一条产生
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
```
**优化策略四:利用 Docker 缓存层**
```dockerfile
# ❌ 差的写法:代码变动会导致重新安装依赖
COPY . .
RUN pip install -r requirements.txt
# ✅ 好的写法:先复制依赖文件,pip install 利用缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . . # 代码变动不会影响上面那层的缓存
```
**优化策略五:使用 .dockerignore**
```text
# .dockerignore
__pycache__/
*.pyc
*.db
.git/
node_modules/
.env
*.md
dist/
build/
.venv/
```
**优化结果:**
```bash
# 优化前:1.2GB
dockerimagesmy-app:bad
# my-app:bad 1.2GB
# 优化后:约 200MB
dockerimagesmy-app:good
# my-app:good 198MB
# 体积缩小超过 80%!
```
### 11.6.2 镜像大小对比
| 基础镜像 | 大小(约) | 适用场景 |
|---------|-----------|---------|
| `python:3.12` | 900MB | 开发、教学 |
| `python:3.12-slim` | 150MB | **生产首选** |
| `python:3.12-alpine` | 70MB | 极致精简(可能有兼容性问题) |
| `node:20` | 1.1GB | 开发 |
| `node:20-alpine` | 170MB | **前端构建首选** |
---
## 11.7 推送到 Docker Hub 并部署
### 11.7.1 推送镜像
```bash
# 1. 登录 Docker Hub
dockerlogin
# 2. 给镜像打标签(Tag)
# 格式:docker tag 本地镜像名 用户名/仓库名:版本号
dockertagfinanceflow-backendyourname/financeflow-backend:v1.0.0
dockertagfinanceflow-frontendyourname/financeflow-frontend:v1.0.0
# 3. 推送
dockerpushyourname/financeflow-backend:v1.0.0
dockerpushyourname/financeflow-frontend:v1.0.0
```
### 11.7.2 在生产服务器上部署
假设你有一台云服务器(Ubuntu / CentOS):
```bash
# 1. SSH 连接到服务器
sshuser@your-server.com
# 2. 安装 Docker(如果还没有)
curl-fsSLhttps://get.docker.com | sh
# 3. 克隆项目
gitclonehttps://github.com/yourname/financeflow.git
cdfinanceflow
# 4. 创建 .env 文件
cat > .env << 'EOF'
OPENAI_API_KEY=sk-your-api-key
EOF
# 5. 启动生产服务
dockercompose-fdocker-compose.prod.ymlup-d--build
# 6. 验证
curlhttp://localhost:8000/api/health
# 输出:{"status":"running","version":"1.0.0"}
# 浏览器访问 http://your-server.com:3000
```
### 11.7.3 使用 CI/CD 自动构建和部署
```yaml
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# 检出代码
- uses: actions/checkout@v4
# 登录 Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# 构建并推送后端镜像
- name: Build and push backend
uses: docker/build-push-action@v5
with:
context: ./backend
push: true
tags: yourname/financeflow-backend:v${{ github.run_number }}
# 构建并推送前端镜像
- name: Build and push frontend
uses: docker/build-push-action@v5
with:
context: ./frontend
push: true
tags: yourname/financeflow-frontend:v${{ github.run_number }}
# 部署到服务器(通过 SSH)
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd ~/financeflow
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
```
每次推送到 main 分支,GitHub Actions 会自动构建镜像、推送到 Docker Hub、并部署到你的服务器。
---
## 11.8 Docker 常见问题排查
### 11.8.1 常见问题清单
```bash
# 问题 1:端口被占用
# 错误:Error response from daemon: Ports are not available: exposing port TCP :8000
解决:更换宿主机端口
dockercomposedown
# 修改 docker-compose.yml: ports: ["8001:8000"]
dockercomposeup-d
# 问题 2:容器启动后立即退出
# 错误:Exit 0 (container stopped)
解决:容器需要有持续运行的进程。检查CMD是否正确
dockercomposelogsbackend# 查看日志
dockercomposeexecbackendpsaux# 查看容器内进程
# 问题 3:无法连接数据库
# 错误:Connection refused
解决:检查volume挂载是否正确,数据是否持久化
dockervolumels# 查看卷
dockercomposedown-v# 删除卷(重置数据)
# 问题 4:权限被拒绝
# 错误:Permission denied
解决:确认USERappuser正确设置了文件所有权
dockercomposeexecbackendls-la/app
# 问题 5:镜像构建慢
解决:使用国内镜像加速器
# /etc/docker/daemon.json(Linux)
# 或通过 Docker Desktop 设置(Windows/Mac)
{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
sudosystemctlrestartdocker
```
### 11.8.2 调试技巧
```bash
# 技巧 1:进入正在运行的容器
dockerexec-itfinanceflow-backend/bin/bash
# 技巧 2:查看容器内部文件系统
dockercpfinanceflow-backend:/app/main.py./main.py.debug
# 技巧 3:临时覆盖启动命令
dockercomposerun--rmbackendbash
# 技巧 4:查看容器资源使用
dockerstats
# 输出:
# CONTAINER CPU % MEM USAGE MEM % NET I/O
# financeflow-backend 2.5% 120MiB 3.0% 1.2kB/2.4kB
# financeflow-frontend 0.8% 45MiB 1.1% 0.8kB/1.6kB
# 技巧 5:打印环境变量,排查配置问题
dockercomposeexecbackendenv | grepOPENAI
# 技巧 6:重建镜像(不使用缓存)
dockercomposebuild--no-cache
```
---
## 11.9 实操练习
### 练习 1:构建第一个镜像
**题目**:为一个简单的 Python Flask 应用创建 Dockerfile。
```python
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
defhello():
return"Hello from Docker!"
@app.route('/health')
defhealth():
return {"status": "ok"}
if__name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
```
```text
# requirements.txt
flask==3.0.3
gunicorn==22.0.0
```
**任务**:
1. 编写一个生产级 Dockerfile(多阶段构建 + 非 root 用户 + 健康检查)
2. 构建镜像并运行容器
3. 验证 `/` 和 `/health` 端点都能正常工作
<details>
<summary>参考答案</summary>
```dockerfile
# Dockerfile(参考答案)
FROM python:3.12-slim AS builder
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends gcc && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN python -m venv /opt/venv && /opt/venv/bin/pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim AS runner
RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
COPY --chown=appuser:appuser app.py .
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')" || exit 1
USER appuser
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]
```
构建和测试:
```bash
dockerbuild-tmy-flask.
dockerrun-d-p5000:5000--nameflask-testmy-flask
# 验证
curlhttp://localhost:5000/# → Hello from Docker!
curlhttp://localhost:5000/health# → {"status":"ok"}
dockerrm-fflask-test
```
</details>
---
### 练习 2:Docker Compose 多服务编排
**题目**:创建一个包含三个服务的 docker-compose.yml:
1. 后端 API(使用我们前面写的 Python 镜像)
2. 前端(使用 Nginx 镜像托管静态文件)
3. Redis 缓存服务
**任务**:
1. 编写 `docker-compose.yml`,让 Redis 作为一个独立的 service 运行
2. 让后端可以连接 Redis(通过服务名 `redis` 作为主机名)
3. 验证三个容器都能正常启动和通信
<details>
<summary>参考答案</summary>
```yaml
# docker-compose.yml
version: "3.9"
services:
backend:
image: python:3.12-slim
container_name: exercise-backend
working_dir: /app
command: bash -c "pip install redis flask > /dev/null && python -c \"import redis; r = redis.Redis(host='redis', port=6379); r.set('test', 'hello from docker!'); print(r.get('test').decode())\""
volumes:
- ./backend:/app
depends_on:
- redis
networks:
- app-network
frontend:
image: nginx:alpine
container_name: exercise-frontend
ports:
- "3000:80"
volumes:
- ./frontend:/usr/share/nginx/html
depends_on:
- backend
networks:
- app-network
redis:
image: redis:7-alpine
container_name: exercise-redis
ports:
- "6379:6379"
networks:
- app-network
networks:
app-network:
driver: bridge
```
运行:
```bash
dockercomposeup-d
dockercomposelogsbackend# 应看到 "hello from docker!"
dockercomposedown
```
</details>
---
### 练习 3:镜像体积优化
**题目**:比较不同基础镜像的大小,并尝试优化一个已有的 Dockerfile。
**任务**:
1. 分别拉取 `python:3.12`、`python:3.12-slim`、`python:3.12-alpine`,记录各自大小
2. 将一个使用 `python:3.12` 的 Dockerfile 改为多阶段构建 + `slim` 基础镜像
3. 计算优化前后的镜像大小对比
<details>
<summary>参考答案</summary>
```bash
# 1. 查看基础镜像大小
dockerimagespython
# 输出示例:
# python 3.12 c43fac3f6a29 3 weeks ago 923MB
# python 3.12-slim 8f4d3f6e8c21 3 weeks ago 148MB
# python 3.12-alpine 2a1b3c4d5e6f 3 weeks ago 68MB
# 优化前后对比:
# 优化前:python:3.12 + 单阶段 → 约 500MB(安装依赖后)
# 优化后:python:3.12-slim + 多阶段 → 约 150MB
# 体积缩小约 70%
```
</details>
---
### 练习 4:健康检查和自动重启
**题目**:为一个生产级 Dockerfile 添加健康检查和自动重启策略。
**任务**:
1. 编写一个包含 `HEALTHCHECK` 指令的 Dockerfile
2. 在 `docker-compose.yml` 中使用 `depends_on` 的 `condition: service_healthy`
3. 模拟后端启动慢的场景,验证前端是否能正确等待
<details>
<summary>参考答案</summary>
```dockerfile
# Dockerfile(含健康检查)
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
HEALTHCHECK --interval=15s --timeout=3s --start-period=30s --retries=5 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')" || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```
```yaml
# docker-compose.yml 片段
services:
frontend:
depends_on:
backend:
condition: service_healthy# 关键:只有后端健康后才启动
image: nginx:alpine
ports:
- "3000:80"
```
</details>
---
### 练习 5:部署到云服务器
**题目**:将你本地开发好的 FinanceFlow 项目部署到一台真实的云服务器上。
**任务**:
1. 购买一台便宜的云服务器(阿里云/腾讯云/AWS 都有学生优惠)
2. 在服务器上安装 Docker
3. 将项目代码和 `docker-compose.yml` 上传到服务器
4. 配置防火墙和安全组(开放 80 和 443 端口)
5. 启动服务并访问你的在线 Dashboard!
<details>
<summary>参考步骤</summary>
```bash
# 1. 在本地构建并测试
cdfinanceflow
dockercomposeup-d--build
# 2. 上传到服务器(使用 scp 或 git)
scp-rfinanceflowuser@your-server.com:~/
# 3. 在服务器上操作
sshuser@your-server.com
cd~/financeflow
# 4. 创建 .env 文件(手动输入密钥)
nano.env
# 写入 OPENAI_API_KEY=sk-your-key-here
# 5. 启动生产环境
dockercompose-fdocker-compose.prod.ymlup-d
# 6. 配置 Nginx 做反向代理(可选,让域名直接访问)
sudoaptinstallnginx
sudonano/etc/nginx/sites-available/default
# 配置 server_name 和 proxy_pass
# 7. 验证
curlhttp://localhost:3000
```
</details>
---
## 11.10 知识点小结
| 知识点 | 在实际项目中的价值 |
|--------|-------------------|
| Docker 镜像与容器 | 一次构建,到处运行,消除环境差异 |
| 多阶段构建 | 大幅减小镜像体积,提升安全性 |
| Docker Compose | 轻松管理多服务架构(API + DB + 前端) |
| Volume 挂载 | 开发时热重载,生产时数据持久化 |
| 健康检查 | 让服务感知彼此状态,避免启动顺序问题 |
| 镜像推送与部署 | 从本地到云端的完整发布流程 |
---
## 11.11 小结与展望
恭喜!你已经学会了**容器化部署**,这是从"写出能跑的代码"到"让代码在任何地方都能可靠运行"的关键一步。
回顾一下这一期的核心收获:
```
学习阶段 你得到了什么
─────────────────────────────────────────
Episode 01-04 Python 基础语法和能力
Episode 05 抓取外部数据的能力
Episode 06 分析和理解数据的能力
Episode 07 构建后端服务的能力
Episode 08 接入 AI 智能的能力
Episode 09 打造用户界面的能力
Episode 10 整合所有能力的综合能力
Episode 11 ⭐ 将项目部署到世界任何地方的能力
```
**有了这一章,你的代码不再是"只能在本地跑的玩具",而是"可以被任何人随时访问的产品"。**
---
## 11.12 下期预告
第二阶段已经蓄势待发!以下是后续方向的预告:
-**Episode 12:数据库进阶** —— 从 SQLite 到 PostgreSQL,SQLAlchemy ORM,Alembic 迁移工具,让数据层像专业软件一样健壮。
-**Episode 13:Celery 异步任务** —— 定时财务报表、邮件通知、批量数据处理,学会让后台任务替你打工。
-**Episode 14:单元测试与 CI/CD** —— pytest + coverage,GitHub Actions 自动测试和部署,拥抱工程化。
-**Episode 15:WebSocket 实时通信** —— 让 Dashboard 的数据实时更新,告别手动刷新。
-**Episode 16:微服务架构初探** —— 把一个大项目拆成几个独立服务,了解分布式系统的入门概念。
无论你选择哪条路走下去,**容器化**都是现代软件开发的基础设施。哪怕你以后不做 Python 了,Docker 的知识依然会在每一个项目中帮到你。
下一期,我们聊聊如何让数据库也"容器化"—— Episode 12:数据库进阶。敬请期待!🐳🚀✨