怎么用 Docker 把 KNow 部署到服务器?
前九篇完成了 KNow 产品的所有功能开发。本篇回答三个问题:
1. Docker 镜像怎么构建和优化?
2. docker-compose 怎么编排所有服务?
3. 上线前要做哪些安全检查?
Docker 镜像怎么构建和优化?
后端 Dockerfile
# backend/Dockerfile
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
# 在构建阶段安装依赖,利用 Docker 缓存
RUN pip install --no-cache-dir -r requirements.txt
# 正式镜像
FROM python:3.11-slim
WORKDIR /app
# 安装运行时依赖(不含构建工具,减小镜像体积)
RUN apt-get update && apt-get install -y --no-install-recommends \
libmagic1 && \
rm -rf /var/lib/apt/lists/*
# 从 builder 阶段复制已安装的包
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
为什么用多阶段构建? 构建阶段有编译器和构建工具,运行时不需要。多阶段构建可以把镜像体积从 1.2GB 降到 400MB。
前端 Dockerfile
# frontend/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
前端 nginx 配置
# frontend/nginx.conf
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8000;
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_read_timeout 120s;
}
}
docker-compose 怎么编排所有服务?
完整编排文件
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: know
POSTGRES_USER: know
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U know"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
qdrant:
image: qdrant/qdrant:latest
volumes:
- qdrant_data:/qdrant/storage
minio:
image: minio/minio:latest
environment:
MINIO_ROOT_USER: ${MINIO_USER}
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
volumes:
- minio_data:/data
command: server /data
backend:
build: ./backend
env_file: .env
depends_on:
postgres: { condition: service_healthy }
redis: { condition: service_healthy }
qdrant: { condition: service_started }
minio: { condition: service_started }
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
postgres_data:
redis_data:
qdrant_data:
minio_data:
环境变量模板
# .env(不要提交 Git)
DB_PASSWORD=your_secure_password
MINIO_USER=know_admin
MINIO_PASSWORD=your_minio_password
LLM_API_KEY=sk-your-key
LLM_BASE_URL=https://api.deepseek.com/v1
JWT_SECRET=your_random_secret_here
一键部署
# 克隆代码
git clone https://github.com/your/know.git
cd know
# 配置环境变量
cp .env.example .env
# 编辑 .env 填入真实密钥
# 启动
docker compose up -d --build
# 查看日志
docker compose logs -f
# 更新
git pull
docker compose up -d --build
上线前要做哪些安全检查?
1. 密钥检查
# 不要把这些硬编码在代码里
❌ JWT_SECRET = "123456"
❌ DB_PASSWORD = "password"
❌ API_KEY = "sk-test"
✅ 使用环境变量或密钥管理服务
2. CORS 配置
# 生产环境限制来源
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-domain.com"],
allow_methods=["GET", "POST"],
allow_headers=["Authorization", "Content-Type"],
)
3. 速率限制
# 防止 API 被滥用
from slowapi import Limiter
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@router.post("/api/chat/stream")
@limiter.limit("30/minute")
async def chat_stream():
...
4. 数据备份
# 定时备份数据库
0 3 * * * docker exec know-postgres pg_dump -U know know > /backup/know_$(date +\%Y\%m\%d).sql
# 保留最近 30 天
0 4 * * * find /backup -name "know_*.sql" -mtime +30 -delete
上线 checklist
□ 所有密码和密钥已修改(不追求认密码)
□ HTTPS 证书已配置
□ CORS 已限制域名
□ 速率限制已启用
□ 数据库备份已配置
□ 日志轮转已配置
□ Docker 镜像已优化(多阶段构建)
□ .env 文件未提交 Git
□ 容器以非 root 用户运行
□ 健康检查接口已配置
总结
| 步骤 | 做的事 |
|---|---|
| 构建镜像 | 多阶段构建,前端用 nginx 托管静态文件 |
| 编排服务 | docker-compose 管理 6 个服务 |
| 安全加固 | CORS、限流、密钥管理、备份 |
| 上线验证 | 按 checklist 逐项确认 |
本文是 《AI 全栈开发实战——做一个真正的产品》 系列的第 10 篇。
系列目录:1-9 已完成,10. Docker 部署 ← 你在这里
本文由 Zyentor(智元界) 原创发布