一条命令跑通整个后端:Docker Compose 入门

「从零到 AI 应用工程师」专栏 · 第 7 篇


到上一篇,你的对话服务已经有了:接口、分层、统一错误、PostgreSQL 历史、Redis 缓存。

但「在我电脑上能跑」和「别人(或未来的你)十分钟能跑」之间,还差一口气。本机装数据库、改端口、环境不一致......这些摩擦会吃掉大量热情。

今天把它们收成一句:

bash 复制代码
docker compose up -d --build

应用、Postgres、Redis 一起起来;再用 /healthz 证明依赖真的可用------不只是容器显示 Up。


一、先把三个词分清

概念 一句话
镜像 Image 只读的构建产物,像安装包
容器 Container 镜像跑起来的进程实例
Compose 用一份 YAML 声明「多容器怎么一起跑」

Compose 里,服务名就是容器网络里的主机名 。所以应用连数据库应写 db,连 Redis 应写 redis------不要写 127.0.0.1。

127.0.0.1 在容器里指的是这个容器自己,不是你的笔记本电脑,也不是旁边的数据库容器。这是新手踩得最多的坑。


二、最小 Dockerfile

dockerfile 复制代码
FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app

# 先装依赖,再拷代码 ------ 利用层缓存,改代码不必每次重装包
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

两个细节:

  • 监听 0.0.0.0,否则容器外访问不到;
  • requirements.txt 与源码分开 COPY,改一行业务代码不会让依赖层全部失效。

三、docker-compose.yml(应用 + 库 + 缓存)

yaml 复制代码
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: chat_db
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./sql/init.sql:/docker-entrypoint-initdb.d/01_init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app_user -d chat_db"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10

  web:
    build: .
    env_file: .env.docker
    ports:
      - "127.0.0.1:8000:8000"   # 只绑本机,降低误暴露风险
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "python -c \"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/healthz')\"",
        ]
      interval: 10s
      timeout: 5s
      retries: 10

volumes:
  pg_data:
  redis_data:

.env.docker 示例(不要提交真实密钥):

env 复制代码
DB_HOST=db
DB_PORT=5432
DB_USER=app_user
DB_PASSWORD=请换成强密码
DB_NAME=chat_db

REDIS_HOST=redis
REDIS_PORT=6379

API_TOKEN=请换成你的令牌
MODEL_API_KEY=sk-你的密钥
MODEL_API_URL=https://api.example.com/v1/chat/completions
MODEL_NAME=your-model-name

应用代码里读库、读缓存,主机名要用环境变量里的 db / redis。


四、健康检查:Up 不等于可用

depends_on: condition: service_healthy 比「只等容器启动」强一截,但应用自己仍应提供:

text 复制代码
GET /ping     → 进程活着
GET /healthz  → 进程 + 数据库 + Redis(必要时再加模型配置检查)

示例逻辑:

python 复制代码
@router.get("/healthz")
async def healthz():
    db_ok = check_db()       # SELECT 1
    redis_ok = check_redis() # PING
    ready = db_ok and redis_ok
    status_code = 200 if ready else 503
    return JSONResponse(
        status_code=status_code,
        content={
            "code": status_code,
            "message": "ok" if ready else "dependency not ready",
            "data": {"db": db_ok, "redis": redis_ok},
        },
    )

验收时:容器 Up 只是起点,/healthz 返回 200 才算栈就绪。


五、常用命令

bash 复制代码
# 构建并后台启动
docker compose up -d --build

# 看状态
docker compose ps

# 看日志(出问题先看这里)
docker compose logs --tail=100 web
docker compose logs --tail=100 db

# 停掉(默认保留数据卷)
docker compose down

# 停掉并删除数据卷 ------ 危险,本地数据会没
# docker compose down -v

改了 .env 或依赖:通常需要 重建容器 ,不是简单 restart:

bash 复制代码
docker compose up -d --build --force-recreate web

六、验收清单

bash 复制代码
docker compose up -d --build
docker compose ps

curl -i http://127.0.0.1:8000/ping
curl -i http://127.0.0.1:8000/healthz

curl -X POST http://127.0.0.1:8000/chat \
  -H "Authorization: Bearer 你的令牌" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "docker_demo",
    "session_id": "session_docker_01",
    "message": "栈是否健康"
  }'

全部通过,阶段 1 的「能交付最小后端」就齐了。


七、常见坑(建议贴显示器旁)

  1. 容器里把数据库地址写成 localhost。 → 改成服务名 db。
  2. 密码、Token、模型 Key 写进镜像或打进 Git。 → 环境变量 / env_file,示例用 .env.example。
  3. 以为 depends_on 等于数据库已可查询。 → 加 healthcheck;应用再做 /healthz。
  4. 误用 docker compose down -v。 → 本地库被清空。
  5. 改了 init.sql 却不见生效。 → 初始化脚本只在数据卷首次创建时跑;已有卷需要迁移或删卷重建(删卷会丢数据)。
  6. 生产把 5432 / 6379 映射到公网。 → 数据库和缓存只走内部网络;本示例仅把 8000 绑在 127.0.0.1。
  7. 改了依赖不 --build。 → 容器里仍是旧包。

八、阶段 1 小结:你现在手里有什么

从第 2 篇到第 7 篇,一条对话后端的骨架已经立住:

篇 能力
02 FastAPI + 大模型,打通 /chat
03 路由 / 业务 / 客户端分层
04 统一响应、统一异常、request_id
05 PostgreSQL 历史 + 分页
06 Redis 缓存重复问题(可降级)
07 Docker Compose 一键拉起整栈

这不是玩具脚本了------这是后面做 RAG、做 Agent 时,可以继续往上长的底座。


九、带走这三条

  1. Compose 把应用、依赖、网络、卷、健康检查变成可重复执行的声明。
  2. 容器网络用服务名;持久数据用 Volume;密钥用环境注入。
  3. 验收看 /healthz 和核心接口,不只看容器 Up。

下一阶段进入专栏最硬核的一块:RAG------让大模型读懂你自己的资料,还不许瞎编。 第 8 篇先把概念和全貌讲清,再进入解析、分块、向量检索。

这是专栏第 7 篇,也是 chat-api 阶段的收官。两天一更,RAG 见。

相关推荐
冬奇Lab22 分钟前
LLM 驱动的自动化测试系列(06):移动端自动化(二)——DroidRun/Mobilerun 的角色级模型拆分
人工智能·测试
冬奇Lab35 分钟前
一天一个开源项目(第230篇):HandRaw-Style —— 把 327 种手绘风格、165 种排版、36 种配色编号化,让 AI 画图不再每次都飘
人工智能·开源·资讯
罗西的思考39 分钟前
从陶哲轩的访谈看:AI 数学研究方法论 & 给其它行业的启示
人工智能
学...1 小时前
软件学报 2023 论文《面向复杂约束优化问题的进化算法综述》阅读笔记
人工智能·ga·cmop
xhy_07071 小时前
AI 在同一步上反复打转怎么办?WES Code 循环检测怎么用
人工智能·大模型·ai编程·wes code
鱼宵2 小时前
Spring AI 提示词模板:{变量} 参数化 + few-shot,一条提示词反复用
java·人工智能·spring·few-shot·提示词工程·springai
蜗牛互联网2 小时前
Python消费Responses SSE事件:增量文本、超时与取消
java·开发语言·人工智能·后端·python
架构师那点事儿2 小时前
大模型如何私有化部署到生产环境
人工智能·架构·llm
HeyAI人工智能2 小时前
官网内容优化 vs 企业级 RAG:AI 搜索时代,企业到底该先做什么?
人工智能·aigc
黑妹天下第一乖2 小时前
第09讲 · 多媒体与音频 SDK:硬件编解码与端侧语音
人工智能·嵌入式硬件·深度学习·机器人·音视频·iot