AI Agent 开发实战(12):Docker 部署 Agent
六段骨架:目标 → 环境准备 → 步骤 → 验证 → 设计权衡 → 踩坑记录 → 下一步
目标
把第 09 / 11 篇写好的 Agent 脚本,打包成一个 Docker 镜像:别人(或服务器)只需要有 Docker,一条 docker run 就能跑起来,不用关心他机器上有没有 Python、版本对不对、依赖装没装。
一句话价值:消灭"在我机器上能跑"这个永恒借口。
背景衔接
前面几篇你在本机跑通了三种 Agent:
- 09 篇:手写的 Tool Calling 循环(
agent.py) - 10 篇:把工具标准化成 MCP Server
- 11 篇:用 LangGraph 把流程画成图
它们现在都躺在你的项目目录里。本篇用 Docker 给这个项目"拍个快照",让它变成可复制、可分发、可上线的形态。
环境准备
- 第 01 篇已经装好 Docker(Windows 上的 Docker Desktop,或 Linux 上的 Docker Engine)
- 一个能跑的 Agent 项目(以第 11 篇的 LangGraph 项目为例,目录结构如下)
- 模型 Key 放在
.env里(第 09 篇就是这么做的)
假设你的项目长这样:
my-agent/
├── app.py # LangGraph 主程序(第 11 篇的 graph 代码)
├── requirements.txt # 依赖清单
├── .env # OPENAI_API_KEY / BASE_URL / MODEL(密钥,不进镜像)
├── Dockerfile # 本篇要写的
├── .dockerignore # 本篇要写的
└── docker-compose.yml# 可选,编排用
requirements.txt 内容(和本机开发时一致,不要偷懒写 *):
openai
langgraph
python-dotenv
步骤
① 写 Dockerfile
在 my-agent/ 下新建 Dockerfile(无扩展名):
# 用确定版本的基础镜像,别用 latest(latest 下次构建会变,破坏可复现)
FROM python:3.11-slim
# 统一工作目录,后面所有相对路径都基于它
WORKDIR /app
# 先只拷依赖清单并安装——这层会被 Docker 缓存,
# 只要你不动 requirements.txt,改业务代码就不用重装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再拷全部代码(包含 .env 之外的源码)
COPY . .
# 容器启动时执行的命令
CMD ["python", "app.py"]
app.py 里读 Key 的方式保持第 09 篇的写法即可(用 python-dotenv 读 .env):
from dotenv import load_dotenv
import os
load_dotenv() # 运行时从 .env 读,不写死在代码里
API_KEY = os.getenv("OPENAI_API_KEY")
② 写 .dockerignore(防密钥进镜像,关键)
作用像 .gitignore,告诉 Docker 构建时哪些文件别打进镜像。最重要的是排除 .env:
.env
venv
.venv
__pycache__
*.pyc
.git
.gitignore
⚠️ 这步是安全红线:如果忘了写
.dockerignore,COPY . .会把.env(含你的模型 Key)一起打进镜像层。任何人拿到镜像都能docker history看到,或反解出 Key。
③ 构建镜像
在项目目录里执行:
docker build -t my-agent:0.1 .
my-agent:0.1是镜像名和标签,0.1方便以后版本管理- 末尾的
.表示用当前目录的 Dockerfile
构建成功会看到 Successfully tagged my-agent:0.1。
④ 运行容器(密钥运行时注入,不打进镜像)
docker run --rm --env-file .env my-agent:0.1
--env-file .env:把本机的.env注入容器环境变量,Key 始终只在你本地,不进镜像--rm:容器退出后自动清理,不留垃圾
如果 app.py 需要持续运行(比如是个服务),加 -d 后台运行,用 docker logs 看输出:
docker run -d --name my-agent --env-file .env my-agent:0.1
docker logs -f my-agent
⑤ (可选)用 docker-compose 编排
当 Agent 要搭配其他服务(比如一个前端、一个数据库)时,写 docker-compose.yml 更省事:
services:
agent:
build: .
env_file: .env # 同样运行时注入,不进镜像
restart: unless-stopped # 意外退出自动拉起
启动:docker compose up -d --build
验证
三项全过 = 部署成立:
docker images能看到my-agent:0.1(说明构建成功)docker run --rm --env-file .env my-agent:0.1输出正确结果(问时间能调工具、问笑话能直接答,和第 11 篇本地跑一致)- 改一行
app.py(比如打印一句 "v2"),重新docker build再run,输出变化(说明镜像确实是最新代码,没缓存旧版)
第 2 项最关键:它证明"换一台只有 Docker 的机器,也能原样跑通"。

设计权衡:Docker vs venv vs 裸跑
| 维度 | 裸跑(直接 python app.py) | venv 虚拟环境 | Docker 容器 |
|---|---|---|---|
| 环境一致性 | 最差,依赖本机 | 较好,但 Python 版本仍依赖本机 | 最好,连系统层都隔离 |
| 上手成本 | 0 | 低 | 中(要学 Dockerfile) |
| 分发他人 | 难,"你装下依赖" | 较难 | 易,给镜像或 Dockerfile 即可 |
| 部署上线 | 几乎不可行 | 勉强 | 标准做法(云厂商原生支持) |
| 资源开销 | 0 | 极低 | 多一层隔离,略重 |
| 适用阶段 | 本地调试 | 个人开发 | 交付 / 部署 / 协作 |
诚实地说:单机自娱自乐,venv 足够;要给别人用、要上线,才值得上 Docker。这点和你"反过度工程"的判断一致——别在 demo 阶段就先写一整套 K8s。
踩坑记录
1. .env 被打进镜像层
忘了 .dockerignore,COPY . . 把密钥带进去了。修复:echo ".env" >> .dockerignore 后重新 build(已有的镜像层不会自动消失,必须重建,必要时 docker build --no-cache)。
2. COPY 顺序写反,依赖每次都重装
写成 COPY . . 再 COPY requirements.txt . 再 pip install,导致改任何代码都会让依赖层缓存失效、全量重装。正确顺序见步骤 ①:先 requirements.txt 后源码。
3. requirements 没锁版本
只写 langgraph 不写版本,三个月后别人 build 拿到新版 API,代码跑挂。演示项目可写 langgraph==0.x,生产项目用 pip freeze > requirements.txt 锁死。
4. Windows 路径挂载权限
WSL2 下把 Windows 目录挂进容器(-v /mnt/d/...:/app)可能遇到权限或换行符(CRLF)问题。尽量在 WSL 文件系统内构建,或确保 .dockerignore 排除无关文件。
5. 暴露了不必要的端口
Agent 是脚本不是服务,别乱写 EXPOSE 8080 和 -p 映射,反而增加攻击面。只有真正起服务时才开端口。
6. 镜像体积爆炸
用 python:latest(带编译器等,1G+)且 pip install 不带 --no-cache-dir,镜像臃肿、上传慢。改用 python:3.11-slim + --no-cache-dir,体积能砍到一两百兆。
下一步
镜像打好了,Agent 变成"可分发"的形态。下一步是给它找个家——发到 GitHub,让它能作为你的公开作品被看到、被复用:
→ AI Agent 开发实战(13):GitHub 开源
第 13 篇把项目推上 GitHub,补全 README / LICENSE / .gitignore,成为你简历里能点开的作品链接。
系列衔接:09(手写 Agent)→ 10(工具标准化)→ 11(流程编排)→ 12(容器化部署)→ 13(开源交付),工程化这条线走到"能交付给别人"。
更多推荐


所有评论(0)