ByteNoteByteNote
AI 工作流 06:Docker 容器化与 CI/CD
字

字节笔记本

2026年10月6日 · 约 21 分钟读完

AI 工作流 06:Docker 容器化与 CI/CD

API中转
¥120

本文是「AI 工作流」专栏的一篇,补上工程交付的关键一环:用 Docker 把应用容器化,再用 CI/CD 让测试与部署在每次 push 代码后自动完成。

本文你会看到:Docker 核心概念与上手命令、写 Dockerfile 的正确姿势、docker-compose 编排多服务、GitHub Actions 自动测试与部署,以及 AI 应用部署特有的三个坑。

一、"在我电脑上能跑",Docker 来终结

在自己电脑上写好的项目发给同事,对方回复:跑不起来,缺这个库、那个版本不对。把代码部署到服务器,又发现服务器是 Ubuntu、本地是 macOS,服务器 Python 是 3.8、本地是 3.12,还缺一堆系统依赖,折腾一天才能跑起来。

根本原因只有一个:代码依赖一整套环境,换了地方环境不一致就崩。

Docker 的解法,是把代码、依赖、操作系统环境一起打包成一个标准"集装箱",官方叫镜像(Image)。镜像搬到任何一台装了 Docker 的机器上,都能开箱即跑。

三个核心概念必须先分清:

概念类比一句话
镜像(Image)蛋糕配方只读模板,包含代码与环境
容器(Container)烤出的蛋糕镜像跑起来的实例,可启停
仓库(Registry)蛋糕店存放镜像,最出名的是 Docker Hub

关系是 镜像 --run--> 容器,类比面向对象里的 类 --new--> 对象。

和虚拟机对比,容器胜在轻:

维度虚拟机Docker 容器
启动时间分钟级秒级
体积GB 级MB 级
资源占用高(每个带完整操作系统)低(共享内核)
隔离性强中等

一句话记忆:虚拟机像租一整套独立公寓,Docker 像合租一个房间,便宜够用。

Docker 容器化流程与镜像分层:一次构建,到处运行

二、5 分钟跑起第一个容器

安装:macOS 和 Windows 直接下载 Docker Desktop;Linux 一行命令搞定:

bash
curl -fsSL https://get.docker.com | sh

装完打开终端验证:

bash
docker --version          # 看版本
docker run hello-world    # 跑官方测试镜像

看到 "Hello from Docker!" 就成功了。再玩一个现成镜像,跑一个 nginx:

bash
# 拉取 nginx 镜像
docker pull nginx

# 把容器的 80 端口映射到主机 8080
docker run -d -p 8080:80 --name my-nginx nginx

# 浏览器打开 http://localhost:8080 就能看到 nginx 欢迎页

下面这些常用命令值得形成肌肉记忆:

bash
docker ps                # 看正在跑的容器
docker ps -a             # 看所有容器(包括停止的)
docker stop my-nginx     # 停止
docker start my-nginx    # 启动
docker rm my-nginx       # 删除容器
docker logs my-nginx     # 看日志
docker exec -it my-nginx bash   # 进入容器(调试必备)
docker images            # 看本地镜像
docker rmi nginx         # 删除镜像

三、写 Dockerfile:把应用容器化

假设项目是一个 FastAPI 写的待办清单后端,结构如下:

text
my-server/
├── main.py
└── requirements.txt

在项目根目录新建一个无后缀的 Dockerfile:

dockerfile
# 基础镜像:用官方 Python 精简版
FROM python:3.12-slim

# 设置工作目录
WORKDIR /app

# 先复制依赖文件(利用 Docker 缓存层,加速构建)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 再复制源代码
COPY . .

# 暴露端口(文档作用,实际映射靠 -p)
EXPOSE 8000

# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

构建并运行:

bash
# 构建镜像(注意最后那个点,表示当前目录)
docker build -t my-todo-server:1.0 .

# 运行容器
docker run -d -p 8000:8000 --name todo-server my-todo-server:1.0

# 测试接口
curl http://localhost:8000/api/todos

分层缓存:构建快的关键

Dockerfile 的每条指令(FROM / RUN / COPY)都会生成一层(layer),层是只读的、可缓存的。这也是为什么把 COPY requirements.txt 和 COPY . . 分开写:依赖不常变,能命中缓存,构建明显加快。

三个实用优化:

  1. 指令按变化频率从低到高排列:基础镜像、系统依赖、应用依赖、代码。
  2. 用 .dockerignore 排除不需要进入镜像的文件,写法类似 .gitignore:
text
.git
node_modules
__pycache__
*.pyc
.env
.venv
  1. 用多阶段构建减小镜像体积(进阶技巧)。

四、docker-compose:一键启动整个系统

真实项目通常多个服务一起跑:前端、后端、数据库、Redis。一个个 docker run 太累,docker-compose 用一个 YAML 文件管理所有服务。新建 docker-compose.yml:

yaml
version: "3.9"

services:
  # 数据库
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: "123456"
      POSTGRES_DB: myapp
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data    # 数据持久化
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s

  # Redis
  redis:
    image: redis:7
    ports:
      - "6379:6379"

  # 后端(用上面写好的 Dockerfile 构建)
  server:
    build: ./server            # 指向 Dockerfile 所在目录
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgres://postgres:123456@db:5432/myapp
      REDIS_URL: redis://redis:6379/0
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  # 前端
  web:
    build: ./web
    ports:
      - "5173:5173"

volumes:
  pgdata:                      # 声明数据卷,防止容器删了数据没了

常用操作一行命令:

bash
# 一键启动所有服务(后台)
docker compose up -d

# 看日志
docker compose logs -f server

# 停止所有
docker compose down

# 改了代码后重新构建并启动
docker compose up -d --build

一个 up -d 把整个多服务架构跑起来。新同事入职、CI 环境、生产部署,用同一个 compose 文件,环境绝对一致。

五、CI/CD:让代码自动测试、自动部署

CI(Continuous Integration,持续集成):代码 push 后自动跑测试和构建,保证"代码一直是好的"。

CD(Continuous Deployment/Delivery,持续部署/交付):测试通过后自动部署到生产,保证"上线不靠人肉"。

为什么建议掌握:远程岗位的 JD 里经常明确写着"熟悉 Docker 容器化与 CI/CD 流程"。原因有三:没有 CI/CD 时,谁合并代码谁手动测试,效率低且易出错;AI 应用迭代快,Prompt 和模型经常调整,需要频繁上线,必须自动化;团队协作里,push 代码后自动测试、通过后自动部署,全团队受益。

GitHub Actions 是 GitHub 内置的 CI/CD 工具,个人项目免费额度够用,是最推荐的入门选择。在项目根目录新建 .github/workflows/ci.yml:

yaml
name: CI

# 什么时候触发:push 到 main 分支 或 提 PR 时
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest    # 跑在 GitHub 提供的 Linux 机器上

    steps:
      # 第 1 步:拉代码
      - uses: actions/checkout@v4

      # 第 2 步:装 Python
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      # 第 3 步:装依赖
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest

      # 第 4 步:跑测试
      - name: Run tests
        run: pytest tests/ -v

      # 第 5 步:构建 Docker 镜像(验证能构建成功)
      - name: Build Docker image
        run: docker build -t my-todo-server:ci .

保存后,每次 push 代码到 GitHub,GitHub 会自动开一台 Linux 机器,拉代码、装依赖、跑测试、构建 Docker 镜像。任何一步失败,PR 上显示红叉;全绿显示绿勾。

CI/CD 流水线全景:GitHub Actions 从 push 到自动部署

CI 跑通后可以加 CD:测试通过后自动部署。下面是 SSH 部署到自有服务器的例子:

yaml
name: CI/CD

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    # 这里简化处理,实际可用 needs 让部署依赖测试任务
    steps:
      - uses: actions/checkout@v4

      - name: Build and push image
        run: |
          echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker build -t myuser/todo-server:latest .
          docker push myuser/todo-server:latest

      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /opt/myapp
            docker compose pull
            docker compose up -d
            docker image prune -f

两个关键点:

  1. 密码和密钥绝不写在 yml 里,统一用 GitHub 的 Secrets(仓库 Settings 里的 Secrets and variables)配置。
  2. 整体流程:CI 构建镜像,推到 Docker Hub,SSH 到服务器拉新镜像并重启服务。

六、部署上云:最省钱的路径

个人项目的低成本组合:

服务推荐价格
服务器阿里云/腾讯云轻量应用服务器、Hetzner、DigitalOcean¥24/月起 / $4/月起
域名Cloudflare / 阿里云¥30/年起
HTTPSCloudflare 免费证书 / Caddy 自动证书免费
镜像仓库Docker Hub(公开免费)/ 阿里云 ACR免费

部署流程(简化版):

bash
# 1. 买台云服务器,装 Docker
ssh root@your-server
curl -fsSL https://get.docker.com | sh

# 2. 上传项目代码(或用 git clone)
git clone https://github.com/you/myapp.git
cd myapp

# 3. 配置生产环境变量
cp .env.example .env
vim .env    # 填生产环境的数据库密码、API Key

# 4. 启动
docker compose up -d

# 5. 配置反向代理 + HTTPS(用 Caddy 最简单)
# Caddyfile:
# yourdomain.com {
#     reverse_proxy localhost:8000
# }

走到这里,项目就从"本地能跑"变成了"全世界能访问"。

七、AI 应用部署的三个坑

7.1 API Key 安全

绝不能把 API Key 写进代码或打进 Docker 镜像。正确做法是用环境变量:

python
import os
api_key = os.getenv("OPENAI_API_KEY")

Dockerfile 里不要写 ENV OPENAI_API_KEY=xxx,要么运行时用 docker run -e 传入,要么在 docker-compose.yml 里从 .env 文件读取:

yaml
services:
  server:
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}

.env 文件必须加进 .gitignore,绝不能提交到 Git。

7.2 长时间任务

大模型生成可能要几十秒,HTTP 请求超时设置要给够:

python
import openai
client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"), timeout=120)

或者改成异步任务:请求扔进 Redis 队列慢慢处理,前端轮询结果。

7.3 流式输出的代理

如果应用用 SSE 流式输出,反向代理(nginx/Caddy)要关掉缓冲,否则流式输出会失效:

nginx
location /api/chat {
    proxy_pass http://localhost:8000;
    proxy_buffering off;
    proxy_cache off;
    chunked_transfer_encoding on;
}

八、要点回顾

  1. Docker 把"代码加环境"打包成镜像,做到一次构建、到处运行。
  2. 三个核心概念:镜像(模板)、容器(运行实例)、仓库(存镜像)。
  3. Dockerfile 用指令描述构建过程,分层缓存加速构建,变化频率低的写前面。
  4. docker-compose 用一个 yml 管理多个服务,docker compose up -d 一键启动整个系统。
  5. CI 自动测试构建,CD 自动部署,GitHub Actions 是最易上手的 CI/CD 工具。
  6. AI 应用部署额外注意:API Key 走环境变量、超时给够、流式输出关代理缓冲。

动手练习

  1. 给一个前后端分离的待办清单项目写 Dockerfile(前端一个、后端一个),再加 docker-compose.yml(含 PostgreSQL),docker compose up -d 一键跑起来。
  2. 把项目推到 GitHub,加 .github/workflows/ci.yml,让每次 push 自动跑测试;还没有测试就先加一个简单的导入测试。
  3. 买一台最便宜的云服务器,或用免费的 Render/Railway,把项目部署上线,让朋友能通过公网访问。
  4. 进阶:给项目加 .dockerignore,用多阶段构建把镜像体积从 1GB 压到 200MB 以内。

延伸阅读

容器与流水线打稳了工程地基,接下来就可以放心进入模型侧:用尽量少的数学,理解 Token、Embedding 和注意力机制到底在做什么。

相关文章

分享: