为什么 Agent 需要直连 Docker
如果你在做任何容器化应用——微服务、CI/CD 流水线、本地开发环境——Docker 几乎一定是你的日常工具。它是镜像构建器、是运行时隔离层、是依赖编排器、是部署交付物——一台 Docker 主机上往往跑着十几个甚至几十个相互关联的容器。但讽刺的是,当你想让 AI Agent 帮你看看某个容器为什么挂了、为什么镜像这么大、为什么服务连不上时,对话会变成这样:
你:API 容器起不来了,帮我看看为什么
Agent:我没法直接连 Docker,你帮我跑几条命令吧:
1. docker ps -a | grep api
2. docker logs <容器ID> --tail 200
3. docker inspect <容器ID>
把输出贴给我
你:(切到终端,手敲 5 条命令,复制输出,粘回对话)
Agent:好的,我看到了……(你来回切了 3 次窗口)
这就是 Docker 与 Agent 之间那道看不见的墙——Agent 能读代码、能查日志文件,却偏偏摸不到那台最关键的 Docker 守护进程。在 MCP 出现之前,绕过这道墙只有两种办法,而且都很别扭:
办法 A:手写脚本当中介
你:写一个 shell 脚本,跑 docker ps + docker logs
Agent:(写脚本)→ 你跑 → 你贴输出 → Agent 分析
痛点:每个问题都要写一次脚本,本质还是"你在驱动"
办法 B:让 Agent 跑 Bash MCP
你:让 Agent 通过 Bash MCP 直接敲 docker 命令
Agent:(通过 Bash MCP 跑 docker ps)
痛点:输出是纯文本,容器 ID、状态、镜像名混在一行;
Agent 难结构化解析;而且 Bash 权限太大,
rm -rf / 之类的高危命令它能直接跑,生产机不敢给
这两种方式都有同一个问题:Agent 跟 Docker 之间隔了一层"命令行文本",它不知道哪些是容器 ID、哪些是状态、哪些是端口映射,分析时容易把噪声当数据。更要命的是,Bash MCP 给的是"任意 shell 命令"权限——这对生产 Docker 主机来说是不可接受的风险敞口。
Docker MCP Server 把这层壳彻底打掉。它不是一个"帮你跑 docker 命令"的 wrapper,而是一个原生理解 Docker API的 MCP Server——Agent 调用的是"列出所有容器"、"看这个容器的日志"、"重启这个容器"这种语义化工具,而不是"跑一条 docker ps 再解析输出"。区别看起来微妙,实际体验天差地别:
没有 Docker MCP(Bash 方式):
Agent → docker ps -a
→ 拿到 "CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES" 这种表头文本
→ 自己切列、自己猜哪个是状态、哪个是端口
→ 容器多了就乱
有了 Docker MCP:
Agent → 调用 list_containers 工具
→ 拿到结构化 JSON:
[
{"id":"a1b2c3","name":"api","image":"api:v1",
"status":"running","ports":["8080:8080"]},
...
]
→ 直接当对象分析
这道直连通道打通后,下面这些真实痛点全都能在对话里解决:
痛点 1:容器跑着跑着挂了,要进服务器看日志
→ 容器在 K8s 之外单机 Docker 里跑(开发/测试/小规模生产)
→ 出问题要 SSH 上去 docker logs、docker inspect
→ 现在让 Agent 直接读日志、看状态、重启验证,全程不离开对话框
痛点 2:镜像越来越大,构建越来越慢
→ 一个 Node 应用镜像从 200MB 涨到 1.2GB
→ 不知道哪些层冗余、哪个基础镜像太重
→ 让 Agent 分析 Dockerfile、查看镜像分层、给出优化方案
痛点 3:多容器本地环境起不来,依赖一团乱
→ docker-compose up 报错"network not found"
→ 你不知道哪个容器没起来、哪个端口被占
→ 让 Agent 列容器、查网络、定位冲突、修复 compose 文件
Docker MCP 的设计哲学跟其他运维类 MCP 一脉相承:把"操作 Docker"变成 Agent 的原生能力,而不是让它通过 shell 间接摸。这样 Agent 才能真正看懂容器的状态、镜像的分层、网络的拓扑,做出结构化分析、给出可执行的修复建议。本文接下来从核心能力、配置方式、三个实战场景、与 Kubernetes MCP 的选型对比、安全注意事项、故障排查六个维度把它讲透。
Docker MCP 核心能力
Docker MCP 把 Docker 守护进程的几大对象(容器 Container、镜像 Image、网络 Network、卷 Volume、Compose 项目、系统 System)都封装成了语义化的 MCP 工具。Agent 不用记命令语法,只要说"我要看所有容器的状态",MCP 自然路由到对应工具。下表是完整能力清单:
容器管理
容器是 Docker 最核心的对象,所有运行时操作都在这里。覆盖查看、生命周期控制、日志、执行命令等高频操作:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
list_containers | 列出所有容器(含已停止) | docker ps -a | 容器对象数组(含 ID/名称/状态/端口) |
get_container | 查看单个容器详情 | docker inspect <ID> | 容器配置 JSON(含挂载/环境变量/网络) |
get_container_logs | 拉取容器日志 | docker logs <ID> | 日志字符串(支持 tail/since 参数) |
start_container | 启动已停止的容器 | docker start <ID> | 容器 ID 或名称 |
stop_container | 停止运行中的容器 | docker stop <ID> | 容器 ID 或名称 |
restart_container | 重启容器 | docker restart <ID> | 容器 ID 或名称 |
remove_container | 删除容器(需先停止) | docker rm <ID> | 删除的容器 ID |
exec_in_container | 在容器内执行命令 | docker exec <ID> <cmd> | 命令输出(stdout/stderr) |
run_container | 创建并启动新容器 | docker run <image> | 容器对象(含 ID/状态) |
get_container_stats | 查看容器实时资源占用 | docker stats <ID> --no-stream | CPU/内存/网络/IO 指标 |
list_processes | 查看容器内进程 | docker top <ID> | 进程列表 |
容器管理是日常用得最多的工具集合。一个典型场景——排查异常容器:
Agent 想知道某个 API 容器为什么 502
→ list_containers() 找到 api 容器,状态 "exited (137) 2 minutes ago"
→ get_container_logs("api", tail=200) 拿到日志
→ 看到错误 "OOMKilled" → 内存超限
→ get_container("api") 看到内存限制 512m
→ 建议:调大内存限制到 1g,重启验证
镜像管理
镜像层是 Docker 部署交付物,覆盖构建、拉取、推送、清理等操作:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
list_images | 列出本地所有镜像 | docker images | 镜像对象数组(含仓库名/标签/大小) |
build_image | 从 Dockerfile 构建镜像 | docker build -t <tag> <path> | 构建日志 + 镜像 ID |
pull_image | 从仓库拉取镜像 | docker pull <image>:<tag> | 拉取进度 + 镜像 ID |
push_image | 推送镜像到仓库 | docker push <tag> | 推送进度 + 摘要 |
remove_image | 删除本地镜像 | docker rmi <ID> | 删除的镜像 ID |
inspect_image | 查看镜像详情(分层/历史) | docker inspect <image> | 镜像元数据 JSON |
image_history | 查看镜像构建历史(每层命令) | docker history <image> | 分层数组(含命令/大小/创建时间) |
tag_image | 给镜像打标签 | docker tag <src> <dst> | 标签字符串 |
prune_images | 清理未使用的镜像 | docker image prune -a | 释放空间字节数 |
镜像工具的杀手锏是 image_history——它能让 Agent 看到镜像每一层是怎么构建的,每层多大,从而定位冗余层:
image_history("api:v1")
→ [
{"created_by":"/bin/sh -c npm install", "size":"320MB"},
{"created_by":"/bin/sh -c npm run build", "size":"85MB"},
{"created_by":"/bin/sh -c apt-get install curl", "size":"45MB"},
{"created_by":"COPY . .", "size":"12MB"},
...
]
→ Agent 看到第一层 npm install 占了 320MB
→ 怀疑 node_modules 没排除,或没多阶段构建
网络管理
容器互联依赖 Docker 网络,常见故障都跟网络配置错配有关:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
list_networks | 列出所有 Docker 网络 | docker network ls | 网络对象数组 |
create_network | 创建自定义网络 | docker network create <name> | 网络 ID |
remove_network | 删除网络 | docker network rm <name> | 网络名 |
inspect_network | 查看网络详情(哪些容器接入) | docker network inspect <name> | 网络 JSON(含容器列表/子网) |
connect_container | 把容器接入网络 | docker network connect <net> <c> | 成功标识 |
disconnect_container | 把容器从网络移除 | docker network disconnect <net> <c> | 成功标识 |
prune_networks | 清理未使用网络 | docker network prune | 释放数量 |
inspect_network 是排查"容器间连不通"的核心工具:
inspect_network("app-net")
→ {
"name":"app-net",
"driver":"bridge",
"subnet":"172.20.0.0/16",
"containers":{
"a1b2c3":{"name":"db","ipv4":"172.20.0.2"},
"d4e5f6":{"name":"api","ipv4":"172.20.0.3"}
}
}
→ Agent 立刻知道 db 和 api 都在 app-net 里,能通过容器名互访
卷管理
数据持久化全靠卷。卷挂载错误会导致容器启动失败或数据丢失:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
list_volumes | 列出所有卷 | docker volume ls | 卷对象数组 |
create_volume | 创建命名卷 | docker volume create <name> | 卷名 |
remove_volume | 删除卷 | docker volume rm <name> | 卷名 |
inspect_volume | 查看卷详情(挂载点/驱动) | docker volume inspect <name> | 卷 JSON(含真实路径) |
prune_volumes | 清理未使用卷 | docker volume prune | 释放空间字节数 |
排查"数据库容器重启后数据没了"的典型用法:
inspect_volume("pgdata")
→ {
"name":"pgdata",
"mountpoint":"/var/lib/docker/volumes/pgdata/_data",
"driver":"local"
}
→ 路径正确,卷确实存在
→ 再 get_container("pg") 看挂载
→ 发现挂的是 /var/lib/postgresql/data 但容器里这个目录权限是 root
→ 建议加 --user 参数或修改 PGDATA 路径
Compose 操作
多容器应用通常用 docker-compose 编排。Docker MCP 暴露了 compose 子集,让 Agent 能直接管一个项目:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
compose_up | 启动 compose 项目(拉镜像/建网/起容器) | docker compose up -d | 服务状态数组 |
compose_down | 停止并清理 compose 项目 | docker compose down | 清理摘要 |
compose_ps | 查看 compose 项目内容器状态 | docker compose ps | 服务对象数组 |
compose_logs | 拉 compose 项目内服务日志 | docker compose logs <svc> | 日志字符串 |
compose_build | 构建 compose 项目内镜像 | docker compose build | 构建日志 |
compose_pull | 拉取 compose 项目所需镜像 | docker compose pull | 拉取进度 |
compose 工具是"一键起本地开发环境"的利器:
compose_up({ project: "myapp", detach: true })
→ [
{"service":"db","status":"running","ports":["5432:5432"]},
{"service":"api","status":"running","ports":["8080:8080"]},
{"service":"web","status":"running","ports":["3000:3000"]}
]
→ Agent 知道三个服务都起来了,端口也都对得上
系统级操作
了解 Docker 主机整体状态、监控事件流:
| 工具名 | 功能 | 对应 Docker 命令 | 返回格式 |
|---|---|---|---|
system_info | 查看 Docker 守护进程信息(版本/存储驱动/容器数) | docker info | 信息 JSON |
system_df | 查看磁盘占用(镜像/容器/卷大小) | docker system df | 各类对象大小汇总 |
system_events | 订阅 Docker 事件流(短时窗口) | docker events --since 5m | 事件数组 |
prune_system | 一键清理所有未用对象 | docker system prune -a | 释放空间字节数 |
version | 查看 Docker 版本 | docker version | 客户端/服务端版本信息 |
system_df 是排查"Docker 主机磁盘满了"的第一步:
system_df()
→ {
"images":{"count":48,"size":"12.5GB","reclaimable":"8.2GB"},
"containers":{"count":23,"size":"450MB","reclaimable":"120MB"},
"volumes":{"count":15,"size":"3.2GB","reclaimable":"1.8GB"}
}
→ Agent 立刻知道有 8.2GB 未用镜像可清理
→ 建议 prune_images 释放
一图速查:六大类 × 典型场景
我要做什么? 用哪类工具?
─────────────────────────────────────────────────────
看容器跑没跑、查容器日志、重启容器 → 容器管理
分析镜像大小、构建镜像、清理冗余镜像 → 镜像管理
排查容器间连不通、创建专用网络 → 网络管理
查卷挂载对不对、卷丢了 → 卷管理
一键起/停多容器项目 → Compose
看 Docker 主机整体状态、清磁盘 → 系统
Agent 拿到一个需求时,会先通过 list_containers 或 system_info 看整体,再路由到对应对象类型的工具——这是 Docker MCP 比直接跑 docker 命令显著更聪明的地方:它知道容器、镜像、网络是不同对象,不会拿 docker images 去查容器然后报错。
各客户端配置示例
Docker MCP 的安装跟其他官方 MCP Server 一样,一行 npx 搞定,不需要 clone、不需要编译。但 Docker 涉及本地 socket 权限和潜在的高危操作,配置时多几个注意点。下面逐个给出完整可用配置。
Claude Code(claude_desktop_config.json)
Claude Code 的 MCP 配置文件位置:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
最简配置(连本地 Docker socket):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
默认连的是
/var/run/docker.sock(Linux/Mac)或//./pipe/docker_engine(Windows)。如果 Docker 装在标准位置,零参数就够。
连远程 Docker 主机的配置:
{
"mcpServers": {
"docker": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://10.0.0.5:2375"
]
}
}
}
带 TLS 的远程 Docker(生产推荐):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://docker-prod.internal:2376",
"--tlsverify",
"--tlscacert", "/etc/docker/ca.pem",
"--tlscert", "/etc/docker/cert.pem",
"--tlskey", "/etc/docker/key.pem"
]
}
}
}
生产环境绝对不要用裸
tcp://不带 TLS——Docker API 等同于 root 权限,明文传输等于把整个主机交给网络上的任何人。
如果你已经有其他 MCP Server(比如 filesystem、github),合并进去:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"]
},
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
}
}
}
注意:修改配置文件后必须完全退出 Claude Code(不是关窗,是 Dock 右键退出 / Windows 任务栏退出),重新打开才生效。
Cursor(.cursor/mcp.json)
Cursor 把 MCP 配置放在项目根目录或用户目录的 .cursor/mcp.json:
- 项目级:
<项目根>/.cursor/mcp.json(只对该项目生效) - 用户级:
~/.cursor/mcp.json(全局生效)
最简配置:
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
想让 docker 在所有项目都可用,放用户级;想只对某个项目可用,放项目级。两者同名时项目级覆盖用户级。
连远程 Docker(带环境变量做权限隔离):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://docker-staging.internal:2376",
"--tlsverify"
],
"env": {
"DOCKER_CERT_PATH": "/Users/me/.docker/staging-certs"
}
}
}
}
通过
DOCKER_CERT_PATH环境变量指定 TLS 证书目录(目录里放ca.pem、cert.pem、key.pem),比在 args 里写绝对路径更可移植,团队共享配置时换路径只改 env。
VS Code(settings.json)
VS Code 通过 GitHub Copilot Chat 或 Continue 等扩展支持 MCP。配置写在 settings.json:
- macOS:
~/Library/Application Support/Code/User/settings.json - Windows:
%APPDATA%\Code\User\settings.json - Linux:
~/.config/Code/User/settings.json
完整配置:
{
"mcp.servers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
如果你用 Continue 扩展,配置在 ~/.continue/config.json:
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
VS Code 多工作区隔离场景(不同项目连不同 Docker 主机):
{
"mcp.servers": {
"docker-local": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
},
"docker-staging": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://staging.example.com:2376",
"--tlsverify"
],
"env": {
"DOCKER_CERT_PATH": "/Users/me/.docker/staging-certs"
}
}
}
}
通过在对话里指明"查 staging Docker 上的容器"让 Agent 路由到对应 Server,避免连错环境。
Trae(mcp config)
Trae 的 MCP 配置通过内置命令管理,也可以直接编辑配置文件。命令行方式:
# 添加 docker MCP Server(本地 socket)
trae mcp add docker -- npx -y @modelcontextprotocol/server-docker
# 查看已配置的 MCP Server
trae mcp list
# 验证 docker 是否可用
trae mcp test docker
或者直接编辑配置文件 ~/.trae/mcp.json:
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
带项目级配置的写法(.trae/mcp.json 放项目根目录):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://docker-prod.internal:2376",
"--tlsverify"
],
"env": {
"DOCKER_CERT_PATH": "/Users/me/.docker/prod-certs"
}
}
}
}
项目级配置跟用户级同名时项目级覆盖。开发用本地 Docker,生产用项目级指向生产主机,团队共享项目级配置时记得把证书路径换成环境变量占位。
通用配置模板(一图速查)
| 客户端 | 配置文件路径 | 关键字段 |
|---|---|---|
| Claude Code | ~/Library/Application Support/Claude/claude_desktop_config.json | mcpServers.docker |
| Cursor | .cursor/mcp.json(项目或用户级) | mcpServers.docker |
| VS Code | settings.json 或 ~/.continue/config.json | mcp.servers.docker 或 mcpServers.docker |
| Trae | ~/.trae/mcp.json 或 trae mcp add | mcpServers.docker |
核心配置统一是这三行(连本地 Docker socket 时):
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
记住这三行,搬到哪个客户端都能用。需要连远程时加 --host tcp://主机:2376 --tlsverify,并通过 DOCKER_CERT_PATH 提供证书。
Docker socket 权限速查
Docker MCP 要能连上 Docker,必须有读 socket 的权限。各操作系统下的常见坑:
Linux:
Docker socket 默认权限 root:docker 660
→ 把当前用户加入 docker 组:sudo usermod -aG docker $USER
→ 重新登录生效
→ ⚠️ 注意:docker 组等价于 root 权限,多人共用主机时慎用
macOS (Docker Desktop):
socket 默认在 /var/run/docker.sock(实际是 ~/.docker/run/docker.sock 软连)
→ Docker Desktop 安装后默认当前用户可读,无需额外配置
→ 但要在 Docker Desktop 设置里勾选 "Allow default Docker socket"
Windows (Docker Desktop):
使用命名管道 //./pipe/docker_engine
→ Docker Desktop 默认开放给当前用户
→ 如果是 WSL2 后端,WSL 内访问要走 docker-desktop 数据面
如果 Docker MCP 启动报 "permission denied" 或 "connect EACCES",几乎一定是 socket 权限问题,按上面排查。
连接串/参数格式速查
本地 socket(默认): 不传参数
远程明文(仅内网测试): --host tcp://10.0.0.5:2375
远程 TLS(生产): --host tcp://docker.host:2376 --tlsverify
指定证书目录: --tlscacert /path/ca.pem --tlscert /path/cert.pem --tlskey /path/key.pem
或环境变量 DOCKER_CERT_PATH=/path
推荐做法(环境变量法):
env: { "DOCKER_HOST": "tcp://docker.host:2376",
"DOCKER_TLS_VERIFY": "1",
"DOCKER_CERT_PATH": "/etc/docker/certs" }
→ 不用写 --host / --tlsverify 参数,Docker SDK 自动识别
用
DOCKER_HOST环境变量比命令行参数更优雅——因为底层用的是 Docker 官方 SDK,它本身就支持这一组标准环境变量。
三个实战场景
光看配置没感觉,下面三个场景演示 Docker MCP 真正能解决什么问题。每个场景都附完整步骤和可直接照抄的对话示例。
场景一:容器故障自动排查
背景:你的 API 服务跑在 Docker 容器里(容器名 api)。凌晨告警说接口 502,你被叫醒,但还没起床进电脑。你想让 Agent 帮你先看一眼到底是什么情况,能自愈就先恢复服务。
痛点对照:
没 Docker MCP:
你:(手机 SSH 上主机)docker ps -a | grep api
→ 看到 Exited
→ docker logs api --tail 200 > /tmp/log.txt
→ 把日志 cat 出来,复制到对话框
→ Agent 看日志猜原因
→ 你再手动 docker restart api 验证
有 Docker MCP:
你:API 容器是不是挂了?帮我查下原因,能恢复就先恢复
Agent:(list_containers)→(get_container_logs)→(分析)→(restart_container)→(验证)
完整步骤:
- 让 Agent 先列出所有容器,找到 api 容器当前状态
- Agent 拉取容器最近日志,定位异常原因
- Agent 查看容器详情(资源限制/重启策略)
- Agent 给出根因分析
- Agent 重启容器(或调整配置后重启)
- Agent 验证服务恢复(再查日志/状态)
示例对话:
你:API 容器好像挂了,接口 502。帮我查下原因,能自愈就先重启恢复,
然后告诉我需不需要进一步处理。
Agent:
好的,我先看下所有容器状态。
[调用工具: list_containers]
all: true
[工具返回]
[
{"id":"a1b2c3d4","name":"api","image":"myapi:v1.2.3",
"status":"exited (137) 14 minutes ago","ports":["8080:8080"]},
{"id":"e5f6g7h8","name":"db","image":"postgres:15",
"status":"running","ports":["5432:5432"]},
{"id":"i9j0k1l2","name":"redis","image":"redis:7",
"status":"running","ports":["6379:6379"]}
]
api 容器状态是 exited (137),14 分钟前退出。退出码 137 通常是
被 SIGKILL——可能是 OOMKilled 或外部 kill。拉日志看:
[调用工具: get_container_logs]
container: api
tail: 200
[工具返回]
...(省略前面正常日志)
2026-09-09 03:12:45 INFO Request GET /api/users completed in 23ms
2026-09-09 03:12:46 INFO Request POST /api/orders started
2026-09-09 03:12:46 WARN Memory usage: 89% of 512MB limit
2026-09-09 03:12:46 WARN Memory usage: 95% of 512MB limit
2026-09-09 03:12:47 ERROR Cannot allocate memory, shutting down request
2026-09-09 03:12:48 INFO Graceful shutdown initiated...
(无更多日志)
日志显示内存涨到 95% 后请求分配失败、容器退出。看下容器配置确认
内存限制:
[调用工具: get_container]
id: a1b2c3d4
[工具返回]
{
"name":"api",
"hostConfig":{
"memory":536870912, ← 512MB
"memorySwap":536870912, ← 不允许 swap
"restartPolicy":{"name":"no"} ← 不自动重启
},
"state":{
"status":"exited",
"oomKilled":true, ← ⚠️ OOM 杀死
"exitCode":137
}
}
根因确认:OOMKilled=true,容器因内存超限被 Docker 杀掉。
现在请求高峰已过,先重启恢复服务:
[调用工具: start_container]
id: a1b2c3d4
[工具返回]
a1b2c3d4 ← 启动成功
等 5 秒再查日志和状态确认恢复:
[调用工具: get_container_logs]
container: api
tail: 30
[工具返回]
2026-09-09 03:35:01 INFO Server started on :8080
2026-09-09 03:35:02 INFO Health check: OK
2026-09-09 03:35:05 INFO Request GET /health completed in 2ms
✅ 服务已恢复。变更摘要:
- 容器:api
- 故障:OOMKilled(内存超 512MB 被杀)
- 操作:start_container 重启
- 当前:running,健康检查 OK
- 重启策略:no(不会自动恢复,下次还会这样)
建议后续处理:
1. 把内存限制调到 1g(用 docker run 时加 --memory=1g,
或在 compose 里改 deploy.resources.limits.memory)
2. 重启策略改 unless-stopped,挂了能自动起
3. 排查为什么凌晨请求让内存涨到 95%——可能泄漏或大查询
要我直接改 compose 文件还是你来?
为什么这个场景重要:容器故障排查是 DevOps 最高频的痛点,传统做法要在告警→SSH→敲命令→看日志→重启这个链路上手动多次切换。Agent 通过 Docker MCP 能在对话里走完"诊断→根因→恢复→验证"完整闭环——重点是 Agent 主动调用了 get_container 看 oomKilled 字段,这是从 docker inspect 大段 JSON 里精确取值的,Bash MCP 跑命令的 Agent 很容易漏看这个关键字段。
进阶:让 Agent 在 start 之前先用
system_info看主机剩余内存,避免内存不够重启后又 OOM。或用list_processes看是不是有内存泄漏进程。
场景二:镜像构建优化
背景:你的 Node.js API 镜像 myapi:v1.2.3 从最初的 280MB 涨到 1.2GB,CI 构建时间从 1 分钟涨到 4 分钟,部署拉镜像也慢。你想让 Agent 分析 Dockerfile、检查镜像分层、给出优化方案。
痛点对照:
没 Docker MCP:
你:帮我看看为啥镜像这么大
Agent:你跑下 docker history myapi:v1.2.3 给我
→ 你跑命令,复制输出
Agent:看到了,第一层 npm install 占 800MB……
你再跑 docker images、再 docker inspect 给我
→ 你来回切换、复制粘贴 3 次
有 Docker MCP:
你:分析 myapi:v1.2.3 镜像为啥 1.2GB,给优化方案
Agent:(image_history)→(inspect_image)→(分析)→ 改 Dockerfile
完整步骤:
- Agent 查看镜像构建历史,定位最大的几层
- Agent 查看镜像详情,确认基础镜像和环境
- Agent 让用户把 Dockerfile 给它看(用 Filesystem MCP 读)
- Agent 分析 Dockerfile 哪些指令产生了冗余层
- Agent 给出优化方案(多阶段构建、合并 RUN、.dockerignore)
- Agent 重新构建镜像验证体积减小
示例对话:
你:myapi:v1.2.3 这个镜像有 1.2GB,比最早的 280MB 大了 4 倍。
帮我分析下哪层最大、为什么,然后给优化方案。
Agent:
先看镜像分层。
[调用工具: image_history]
image: myapi:v1.2.3
[工具返回]
[
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) ENTRYPOINT [\"node\" \"server.js\"]","size":"0B","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) EXPOSE 8080","size":"0B","comment":""},
{"created":"2026-09-09","created_by":"COPY . .","size":"180MB","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c npm install","size":"640MB","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) WORKDIR /app","size":"0B","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c apt-get update && apt-get install -y curl vim git","size":"85MB","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) ENV NODE_ENV=production","size":"0B","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c apk add --no-cache python3 make g++","size":"120MB","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) CMD [\"node\"]","size":"0B","comment":""},
{"created":"2026-09-09","created_by":"/bin/sh -c #(nop) ADD file:... in node:18-alpine","size":"180MB","comment":""}
]
总计 1.205GB。各层占比:
| 层 | 大小 | 占比 |
|----|------|------|
| npm install | 640MB | 53% |
| COPY . . | 180MB | 15% |
| node:18-alpine 基础 | 180MB | 15% |
| apk add python/make/g++ | 120MB | 10% |
| apt-get curl vim git | 85MB | 7% |
⚠️ 注意:apt-get 在 alpine 上跑不通,这层八成是写错或残留。
⚠️ COPY . . 占 180MB,八成把 node_modules、.git 都 COPY 进去了。
拿下镜像详情确认:
[调用工具: inspect_image]
image: myapi:v1.2.3
[工具返回]
{
"architecture":"amd64",
"os":"linux",
"config":{
"Image":"node:18-alpine",
"WorkingDir":"/app",
"Env":["NODE_PATH=/app/node_modules","NODE_ENV=production"]
},
"rootfs":{"type":"layers","diff_ids":[...10 层]}
}
确认基础镜像是 node:18-alpine(180MB 是合理的)。
现在让我看一下 Dockerfile:
[切换到 Filesystem MCP]
[调用工具: read_file]
path: /Users/me/projects/api/Dockerfile
[工具返回]
FROM node:18-alpine
ENV NODE_ENV=production
RUN apk add --no-cache python3 make g++
RUN apt-get update && apt-get install -y curl vim git
WORKDIR /app
COPY . .
RUN npm install
CMD ["node"]
ENTRYPOINT ["node", "server.js"]
EXPOSE 8080
根因分析:
1. apt-get 在 alpine 上不该有,那是 Debian 系的包管理器
→ 这层 85MB 八成是失败的残留或多余装了 glibc 兼容层
→ 应该全删,或换 apk add curl
2. COPY . . 把整个项目目录都拷进去,包括 node_modules、
.git、build 产物——180MB 大头是这些
→ 应该先写 .dockerignore 排除这些,再 COPY
3. npm install 把生产 + 开发依赖都装了,640MB
→ 应该 npm install --production 只装生产依赖
→ 或用多阶段构建:build 阶段装全部、run 阶段只装生产
4. python3/make/g++ 是为某些 native 模块编译用
→ 编译完后这些工具运行时不需要,应该用多阶段构建
→ 只把编译好的 node_modules 拷到运行镜像
5. CMD ["node"] 跟 ENTRYPOINT 重复,按你 ENTRYPOINT 用法
CMD 应该删掉
优化方案:改用多阶段构建。
```dockerfile
# 阶段 1:构建
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install # 装全部依赖(含 dev)
COPY . .
RUN npm run build # 编译产物(如有)
# 阶段 2:运行
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
EXPOSE 8080
ENTRYPOINT ["node", "dist/server.js"]
配套 .dockerignore:
node_modules
.git
.vscode
*.log
dist
build
.env*
预期体积:
- 基础层:180MB
- node_modules(仅生产依赖):~180MB
- dist:~10MB
- 总计约 370MB,减少 70%
要我直接改 Dockerfile,重新构建吗?
你:改了重新构建一下
Agent: [切换到 Filesystem MCP] 修改 Dockerfile [切换到 Filesystem MCP] 创建 .dockerignore [调用工具: build_image] path: /Users/me/projects/api tag: myapi:v1.2.4 dockerfile: Dockerfile
[工具返回] Step 1/12 : FROM node:18-alpine AS builder ---> a1b2c3 Step 2/12 : WORKDIR /app ---> d4e5f6 Step 3/12 : COPY package*.json ./ ---> ...(省略) Step 12/12 : ENTRYPOINT ["node", "dist/server.js"] ---> Successfully built x9y8z7 ---> Successfully tagged myapi:v1.2.4
构建成功。对比新体积:
[调用工具: list_images] [工具返回] ... {"repository":"myapi","tag":"v1.2.3","size":"1.2GB"} {"repository":"myapi","tag":"v1.2.4","size":"372MB"} ...
✅ 优化完成: - v1.2.3:1.2GB - v1.2.4:372MB - 减少 828MB(-69%) - 多阶段构建 + .dockerignore + 只装生产依赖 - 构建时间也从 4 分钟降到 1 分 20 秒(COPY 层从 180MB 降到 12MB)
**为什么这个场景重要**:镜像体积膨胀是 Docker 长期项目必经的痛点,靠人眼盯 Dockerfile 和 `docker history` 输出很难发现冗余层。Agent 用 `image_history` + `inspect_image` 拿到结构化的分层信息,能精确指出哪层多大、由哪条指令产生,再结合 Filesystem MCP 读 Dockerfile 反推根因——**这是 Docker MCP + Filesystem MCP 联动的典型应用:Docker MCP 看"现在镜像是什么样",Filesystem MCP 看"怎么构建的"**。
> 进阶:让 Agent 同时跑 `system_df` 看是否还有大量冗余旧镜像可清,进一步释放磁盘。
### 场景三:多容器环境编排
**背景**:你在本地用 Docker 起一套完整的开发环境——PostgreSQL 数据库 + Redis 缓存 + API 服务 + 前端。原本用 `docker compose up`,但今天起不来,报"network app-net not found"或"port 5432 already in use"之类。让 Agent 帮你排查、修复并最终把整套环境拉起来。
**痛点对照**:
没 Docker MCP: 你:docker compose up 报错,帮我看看 Agent:你贴下报错和 docker network ls 输出 → 你来回贴 4 次命令输出 → Agent 凭转述猜原因
有 Docker MCP: 你:compose 起不来,帮我排查然后拉起来 Agent:(compose_ps)→(list_networks)→(list_containers)→(修复)→(compose_up)→(验证)
**完整步骤**:
1. Agent 先看 compose 项目当前状态(哪些服务起来了)
2. Agent 看所有 Docker 网络是否存在 app-net
3. Agent 看是否已有占用端口的容器(5432/6379/8080/3000)
4. Agent 定位冲突(网络被删/端口被占)
5. Agent 修复(重建网络 / 停掉冲突容器 / 改 compose 端口)
6. Agent 重启 compose 项目
7. Agent 验证所有服务状态和连通性
**示例对话**:
你:本地开发环境起不来了,docker compose up 报"network app-net not found"和"port 5432 already in use"。帮我排查然后拉起来。
Agent: 先看 compose 项目当前状态:
[调用工具: compose_ps] project: myapp
[工具返回] [] ← 一个服务都没起来
看下所有 Docker 网络和容器:
[调用工具: list_networks]
[工具返回] [ {"name":"bridge","driver":"bridge","scope":"local"}, {"name":"host","driver":"host","scope":"local"}, {"name":"none","driver":"null","scope":"local"} ]
⚠️ 没有 app-net,被删了——这就是第一个报错的原因。
[调用工具: list_containers] all: true
[工具返回] [ {"id":"a1b2","name":"legacy-db","image":"postgres:14", "status":"running","ports":["5432:5432"]}, {"id":"c3d4","name":"legacy-cache","image":"redis:6", "status":"running","ports":["6379:6379"]} ]
⚠️ 有两个遗留容器(legacy-db 和 legacy-cache)占着 5432 和 6379, 这是第二个报错"port 5432 already in use"的原因。
修复方案:
- 删掉两个 legacy 容器(看起来是旧版本残留,新 compose 会起新的)
- 创建 app-net 网络(compose up 时本来会自动创建,但因为端口冲突 没到那一步就失败了)
- 重新 compose up
先停删 legacy 容器:
[调用工具: stop_container] id: a1b2
[调用工具: stop_container] id: c3d4
[调用工具: remove_container] id: a1b2
[调用工具: remove_container] id: c3d4
创建 app-net(其实 compose up 会自动建,但显式建能避免下次再卡):
[调用工具: create_network] name: app-net driver: bridge
[工具返回] a7b8c9d0 ← 网络 ID
现在拉起整套 compose:
[调用工具: compose_up] project: myapp detach: true
[工具返回] [ {"service":"db","status":"running","ports":["5432:5432"]}, {"service":"redis","status":"running","ports":["6379:6379"]}, {"service":"api","status":"running","ports":["8080:8080"]}, {"service":"web","status":"running","ports":["3000:3000"]} ]
4 个服务都起来了。验证网络连通性:
[调用工具: inspect_network] name: app-net
[工具返回] { "name":"app-net", "driver":"bridge", "subnet":"172.20.0.0/16", "containers":{ "abc1":{"name":"myapp-db-1","ipv4":"172.20.0.2"}, "def2":{"name":"myapp-redis-1","ipv4":"172.20.0.3"}, "ghi3":{"name":"myapp-api-1","ipv4":"172.20.0.4"}, "jkl4":{"name":"myapp-web-1","ipv4":"172.20.0.5"} } }
✅ 全部 4 个容器都接入 app-net,能通过容器名互访: - db:172.20.0.2 - redis:172.20.0.3 - api:172.20.0.4 - web:172.20.0.5
端到端验证:让 api 连 db 看是否通:
[调用工具: exec_in_container] container: myapp-api-1 command: sh -c "nc -z db 5432 && echo DB_OK || echo DB_FAIL"
[工具返回] DB_OK
[调用工具: exec_in_container] container: myapp-api-1 command: sh -c "nc -z redis 6379 && echo REDIS_OK || echo REDIS_FAIL"
[工具返回] REDIS_OK
[调用工具: exec_in_container] container: myapp-web-1 command: sh -c "nc -z api 8080 && echo API_OK || echo API_FAIL"
[工具返回] API_OK
✅ 全链路通:web → api → db / redis 都连得上。
最终状态:
- 4 个容器 running,端口 5432/6379/8080/3000 全部正常
- 同在 app-net 网络,容器名互通
- 联通性验证通过
- legacy 容器已清理,下次再起不会冲突
建议后续:
- 给 compose 文件加 depends_on,避免启动顺序错
- db 加 healthcheck,api 加 depends_on condition: service_healthy
- legacy 容器为啥残留——可能是手动 docker run 启的,没纳入 compose
**为什么这个场景重要**:多容器编排故障的核心难点不是单点问题,而是"问题在多个对象间牵连"——网络没了导致容器起不来、端口冲突导致 compose 失败、然后整链路都瘫。Agent 通过 `compose_ps`、`list_networks`、`list_containers` 组合拿到全局视图,定位"网络缺失 + 端口冲突"双因,再用 `exec_in_container` 做连通性验证——**这种多对象协同排查是 Docker MCP 比单跑 `docker` 命令显著更聪明的地方**:Agent 不会只盯着一个容器看,而是把网络、容器、端口一起对照分析。
> 进阶:让 Agent 在 compose_up 之后还跑一遍 `system_df` 看 docker 主机磁盘够不够,避免"看似起来了但日志写不进去"的隐性故障。
## Docker MCP vs Kubernetes MCP:选型对比
很多人会问:**既然 Kubernetes MCP 也能让 Agent 管容器,为啥还要 Docker MCP?** 答案是它俩解决的问题根本不在一个层面——一个是"单主机容器生命周期",一个是"集群调度与声明式资源"。
先看核心差异:
| 维度 | Docker MCP | Kubernetes MCP |
|------|-----------|--------------------|
| **抽象层级** | 单主机容器/镜像/网络/卷 | 集群 Pod/Deployment/Service/Ingress |
| **资源模型** | 命令式(启动容器、停容器) | 声明式(apply YAML 描述期望状态) |
| **调度能力** | ❌ 无(手动指定主机) | ✅ 自动调度到合适节点 |
| **自愈能力** | ❌ 仅 restart policy | ✅ 控制器自动重建 Pod |
| **网络模型** | bridge/overlay(单主机或多主机互联) | CNI(每个 Pod 独立 IP,集群 DNS) |
| **存储模型** | 本地卷/NFS | PVC/PV/StorageClass 动态供给 |
| **配置管理** | -v 挂载 / env 变量 | ConfigMap / Secret |
| **典型操作** | 起/停/重启单个容器 | 滚动升级、扩缩容、改镜像版本 |
| **YAML 必需性** | 不需要(命令行参数即可) | 必须(声明式资源都靠 YAML) |
| **学习曲线** | 平缓(5 个核心概念) | 陡峭(30+ 资源类型) |
| **典型用途** | 本地开发/单机部署/CI 流水线 | 生产集群调度/微服务编排 |
| **状态可见性** | 直接(容器跑没跑一目了然) | 间接(要看 ReplicaSet/Pod 状态) |
| **故障根因层次** | 浅(容器/镜像/网络/卷四选一) | 深(Pod→ReplicaSet→Deployment→Service→Ingress 多层) |
| **适合 Agent 做什么** | 调单容器、查日志、构建镜像、起本地栈 | 滚动升级、扩缩容、调试 Pod 状态、改 YAML |
**什么时候用 Docker MCP**:
✅ 本地开发环境(docker compose 起一整套) ✅ CI 流水线里跑测试容器 ✅ 单机部署的小规模生产 ✅ 镜像构建和优化(Dockerfile、分层分析) ✅ 容器化应用的本地调试 ✅ 简单的临时容器(跑一次脚本就删)
**什么时候用 Kubernetes MCP**:
✅ 生产集群部署(多副本、滚动升级) ✅ 服务发现和负载均衡(通过 Service) ✅ 自动扩缩容(HPA) ✅ 跨节点调度和故障转移 ✅ 复杂的配置/密钥管理(ConfigMap/Secret) ✅ 多团队共用集群(Namespace 隔离)
**一个简单的判断流程**:
你的容器跑在哪? ├─ 单机 Docker(开发/测试/小规模生产)→ Docker MCP └─ K8s 集群(生产规模化) → Kubernetes MCP
不确定在哪?问自己: ├─ 装的是 Docker 还是 K8s? → 看用什么工具 └─ 想做的是"起停容器"还是"扩缩容发布"? 起停 → Docker 扩缩容 → K8s
**实际项目里两个一起用**是最常见的搭配:
Docker MCP → 本地开发、构建镜像、CI 跑测试 →"怎么造镜像和本地起容器" Kubernetes MCP → 生产部署、扩缩容、滚动发布 →"怎么发布和管理运行时"
比如一次完整发布流程:先 Docker MCP 在本地构建并验证镜像,推到仓库,再切到 Kubernetes MCP 让 Agent 改 Deployment 镜像版本触发滚动升级,最后查 Pod 状态验证发布成功。**别想着二选一,它们解决的是不同生命周期的容器问题**。
**为什么本地开发不直接上 K8s**:很多人会问"既然生产是 K8s,本地也用 K8s(minikube/kind)不更一致吗?"理论上对,实操有几个坑:
- K8s 集群启动慢(minikube start 要 1-2 分钟),开发迭代慢
- K8s 调试单个 Pod 要 kubectl exec/ksniff,比 docker exec 复杂
- 本地资源有限,K8s 控制面吃掉 2GB 内存,开发机扛不住
- K8s YAML 写错一行 Pod 就起不来,调试反向追因麻烦
所以行业里 Docker(compose)做本地开发、K8s 做生产,仍然是主流搭配。Docker MCP 和 Kubernetes MCP 一起配,覆盖"本地迭代 + 生产运维"两个阶段。
## 安全注意事项
把 Docker MCP 接到生产环境之前,有几条必须防住的安全边界。Docker 跟其他 MCP 最大的不同是——**Docker API 等同于宿主机 root 权限**:任何一个能调 Docker API 的人,都能起一个挂载 `/` 的容器拿到宿主 root。这种"上帝权限"配置不当就是灾难。
### 1. 容器逃逸风险
Docker 默认的 namespace + cgroup 隔离在历史上出现过多次逃逸漏洞(runc CVE-2019-5736、CVE-2022-0185 等)。Agent 通过 Docker MCP 起容器时如果不约束,可能引入逃逸风险:
风险场景: Agent 起一个容器调试:docker run -it --privileged --pid=host alpine → --privileged 关闭所有隔离 → --pid=host 共享宿主 PID 命名空间 → 容器内能直接 ptrace 宿主进程,拿到宿主 root
防护:
- system prompt 里明确禁止 --privileged / --pid=host / --net=host
- 生产环境禁用 run_container 工具,只允许操作已有容器
- 用 AppArmor / SELinux 给容器额外加 MAC 限制
- 定期升级 Docker(修 runc 逃逸漏洞)
- 启用 user namespace 重映射(容器 root 映射到宿主非 root)
各客户端在 system prompt 里加规则的写法:
你连的是生产 Docker。规则:
- 禁止起 --privileged 容器
- 禁止 --pid=host / --net=host / --ipc=host
- 禁止挂载宿主敏感目录:/、/etc、/var/run/docker.sock、/root、/home
- 任何 run_container 调用前必须用户确认
- remove_container / remove_image 前必须用户确认
- exec_in_container 命令里禁止 rm -rf / 类危险命令
- 不调用 system_prune(清理会删大量对象)
### 2. 特权模式与挂载的危险
Docker 的几个"上帝模式"参数,开了就等于把宿主送给容器:
极危险参数(永远不要在 Agent 起的容器里用): --privileged ← 关闭所有安全隔离 --pid=host ← 共享宿主进程空间 --net=host ← 共享宿主网络(端口都通) --ipc=host ← 共享 IPC(能跨容器搞共享内存攻击) -v /:/host ← 挂载宿主根目录 -v /var/run/docker.sock:/var/run/docker.sock ← 挂载 Docker socket(容器能管宿主 Docker) --cap-add=SYS_ADMIN ← 加 SYS_ADMIN capability(几乎等于 root) --userns=host ← 关闭 user namespace 重映射
风险实例: Agent 起"我要在容器里装个调试工具" → docker run -v /var/run/docker.sock:/var/run/docker.sock alpine sh → 容器内 docker 命令直接控制宿主 Docker 守护进程 → 等于拿到宿主 root
如果业务确实需要这些权限(比如容器内要起容器),强烈建议**用 Docker-in-Docker 隔离方案**而不是挂载宿主 socket:
✗ 危险:挂宿主 socket docker run -v /var/run/docker.sock:/var/run/docker.sock docker
✓ 安全:DinD(容器内独立 dockerd) docker run --privileged docker:dind (privileged 是 DinD 唯一容忍场景,且容器有独立 namespace)
### 3. 敏感镜像处理
Agent 通过 `pull_image` 拉镜像时,要警惕恶意镜像和敏感数据外泄:
风险 1:拉到恶意镜像 Agent 拉一个 dockerhub 上的随手镜像跑 → 镜像里预埋挖矿/后门 → 容器一启动就成肉鸡
防护:
- system prompt 里限制只能拉白名单仓库(公司内部 registry)
- 用 Docker Content Trust 签名验证(DOCKER_CONTENT_TRUST=1)
- 镜像扫描(Trivy/Clair)跑进 CI 拦截漏洞镜像
风险 2:镜像里含敏感信息 开发者把 .env、密钥、token 打进镜像 → 镜像推到公开仓库 → 任何人 docker pull + docker history 都能看到
防护:
- 用 .dockerignore 排除 .env / .key / credentials
- 敏感配置走运行时环境变量或 Secret,不进镜像
- push_image 前让 Agent 扫一遍镜像分层,检查是否有敏感文件
让 Agent 在 push 前做镜像扫描的对话示例:
你:把 myapi:v1.2.4 推到 registry
Agent: 推之前先扫一下镜像分层有没有敏感文件:
[调用工具: image_history] image: myapi:v1.2.4
[工具返回] ... {"created_by":"COPY .env /app/.env","size":"2KB"} ← ⚠️ ...
⚠️ 发现 .env 被 COPY 进镜像。这八成含密钥, 推到 registry 会被任何人 docker history 看到。 建议先把这层去掉(在 .dockerignore 加 .env, 重新 build),再 push。
要我帮你改 Dockerfile 还是直接 push?
### 4. 只读模式配置
生产 Docker 主机的 MCP 连接**默认应该是只读的**——Agent 能查容器状态、看日志、分析镜像,但不能起停容器、不能删镜像、不能改网络。这需要 Docker MCP 实现层支持只读模式(部分版本支持):
```json
{
"mcpServers": {
"docker-readonly": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--readonly"
]
}
}
}
--readonly让 MCP Server 只暴露list_*、get_*、inspect_*类工具,不暴露start_*/stop_*/remove_*/build_*/run_*等变更类工具。Agent 即使被提示注入想remove_container,工具根本不存在调不到。
如果实现不支持 --readonly,变通方案是连一个受限 Docker socket:
{
"mcpServers": {
"docker-readonly": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://docker-proxy-readonly.internal:2376"
],
"env": {
"DOCKER_CERT_PATH": "/etc/docker/readonly-certs"
}
}
}
}
这个 docker-proxy-readonly 是在 Docker 守护进程前加一层代理(如 docker-socket-proxy),白名单只允许 GET 类 API,POST/DELETE 全部拒绝。是 ACL 层面的硬隔离,比 system prompt 更可靠。
需要写权限的场景(如容器重启),起一个独立的可写连接,按需启用:
{
"mcpServers": {
"docker-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker", "--readonly"]
},
"docker-writer": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://docker-proxy-writer.internal:2376"
],
"env": { "DOCKER_CERT_PATH": "/etc/docker/writer-certs" }
}
}
}
5. 超时设置
Agent 调用 Docker MCP 时,部分操作可能很慢——大镜像构建几分钟、docker pull 几十 GB 镜像十几分钟。如果 MCP 客户端默认超时太短,会中断操作导致镜像构建半途而废。配置建议:
MCP 客户端侧超时:
Claude Code:默认无超时(推荐保持)
Cursor:默认 60 秒(构建镜像会断!需调大)
VS Code:默认 30 秒(同上)
Trae:可在 mcp.json 配 timeout 字段
Cursor 调超时配置:
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"],
"timeout": 600000 ← 10 分钟
}
}
}
构建镜像的
build_image、拉镜像的pull_image务必配 10 分钟以上超时。日常list_containers、get_container_logs这种秒级操作保持默认即可。
6. 数据脱敏
Agent 查容器日志和环境变量时可能拿到敏感信息(数据库密码、API token、JWT)。在 system prompt 里加脱敏规则:
你连的是生产 Docker。规则:
1. 容器日志里出现 password=xxx / token=xxx 时打码显示 password=***
2. get_container 返回的 Env 列表里 PASSWORD/SECRET/TOKEN 字段不显示值
3. exec_in_container 时不要 cat /proc/1/environ 之类读环境变量的命令
4. 不要把容器日志直接全量回显——超过 500 行只摘要
7. 最小权限原则
默认只配只读 docker,不配 filesystem/github 等可写 MCP
→ Agent 能查容器但改不了本地
需要写时再单独加可写连接或 filesystem,限定范围
→ 隔离爆炸半径
✗ 一个 MCP 配置同时连生产 Docker 可写 + filesystem 全盘
→ 一旦 Agent 被提示注入,能起恶意容器 + 改本地脚本
✓ 只读 Docker + 限定目录的 filesystem
→ 即使被注入,破坏有限
常见故障排查
| 现象 | 原因 | 解决 |
|---|---|---|
| Agent 报 "docker tool not found" | 配置没生效 | 重启客户端;检查 JSON 语法;查日志确认 server 启动 |
连接报 permission denied / connect EACCES | Docker socket 无读权限 | Linux:把用户加 docker 组;macOS/Windows:检查 Docker Desktop 是否启动 |
连接报 Cannot connect to the Docker daemon | Docker 守护进程没起 | 启动 Docker Desktop / sudo systemctl start docker |
连远程报 x509: certificate signed by unknown authority | TLS 证书校验失败 | 证书过期/CA 不在信任链;重新签发或加 --tlsverify=false(仅内网测试) |
连远程报 connection refused | 远程 Docker 没开 TCP 监听 | 在 dockerd 启动参数加 -H tcp://0.0.0.0:2376;防火墙开放 2376 |
build_image 超时 | 镜像大、网络慢 | 调大 MCP 客户端超时;分步构建(先 build 基础层、再 COPY 代码) |
pull_image 报 unauthorized | 镜像仓库需要登录 | docker login 写好 ~/.docker/config.json;或把凭证放 MCP env |
| Agent 不主动用 docker | 提示不够明确 | 对话里明确说"连 Docker 查…" |
| Agent 调用 list_containers 慢 | 容器太多/状态计算重 | 加 filter 参数(如 status=running)只看运行中 |
exec_in_container 报 not found | 容器没起 / 命令在容器里不存在 | 先 list 确认容器 running;先 which <cmd> 验证命令存在 |
remove_image 报 image is being used | 镜像被容器引用 | 先 stop+remove 引用容器,再 rmi |
compose_up 报 network exists | 网络同名冲突 | docker network rm app-net 后重试;或在 compose 里 name: 改自定义 |
get_container_logs 返回空 | 容器刚起还没日志 / 选错容器 | 加 --since 5m 看最近 5 分钟;用 stdout=true/false 区分 stdout/stderr |
| 镜像构建后体积不对 | build cache 干扰 | --no-cache 强制全量重建 |
进阶用法:组合其他 MCP
Docker MCP 单用已经很强,但跟其他 MCP 组合起来威力翻倍。几个常见组合:
Docker + Filesystem:容器配置与代码同步
你:容器报"Cannot find module 'express'",帮我看下代码和容器里
是不是依赖没装
Agent:
1. [filesystem] 看 package.json 有 express
2. [docker] exec_in_container 跑 ls node_modules | grep express
3. 发现容器里没装 → .dockerignore 把 node_modules 排除但没在镜像里 npm install
4. [filesystem] 改 Dockerfile 加 RUN npm install
5. [docker] build_image 重新构建
→ 修复
Docker + GitHub:故障关联代码
你:容器 OOM 了,帮我查代码哪里泄漏
Agent:
1. [docker] get_container_logs 看到内存增长曲线
2. [docker] exec_in_container 跑 top 看哪个进程吃内存
3. [github] 搜代码里疑似泄漏的 setInterval / 大对象赋值
→ 数据异常反推到代码根因
Docker + Postgres:数据库容器与持久化数据对账
你:PG 容器跑着,但业务说表查不到。看下数据是不是真的丢了
Agent:
1. [docker] exec_in_container 跑 psql -c "SELECT count(*) FROM users"
2. [postgres MCP] SELECT count(*) FROM users
3. 对比两边数量
4. 发现容器内 PG 是空的 → 卷挂载错了,PGDATA 路径不对
→ 定位根因
Docker + Memory:容器命名约定记忆
你:记住我们项目容器命名规则是 <service>-<env>-<num>,比如 api-prod-1
Agent:
1. [memory] 存下这个命名模式
2. 下次你说"看 prod 的 api"时直接 list_containers filter name=api-prod-*
→ 不用每次重新解释命名规则
性能调优
在生产环境长期用 Docker MCP,有几个性能优化点:
1. 列表查询用 filter
list_containers / list_images 默认返回所有对象。大主机上百容器/几百镜像时,让 Agent 带 filter:
✓ list_containers({ status: "running" }) ← 只看运行中
✓ list_images({ dangling: true }) ← 只看悬空镜像(待清理)
✗ list_containers({}) ← 全量返回,慢
2. 日志用 tail 和 since
get_container_logs 默认返回全部日志,大容器(如 nginx 长期跑)可能几 GB:
✓ get_container_logs({ tail: 200 }) ← 最近 200 行
✓ get_container_logs({ since: "2026-09-09T00:00:00" }) ← 今天以来
✗ get_container_logs({}) ← 全量,爆上下文
3. 并行查询
让 Agent 一次查多个容器状态时,明确鼓励它并行:
你:并行查 api、db、redis 三个容器的日志
Agent:(3 次 get_container_logs 并行调用,而不是串行)
MCP 协议支持工具并行调用,但 Agent 默认可能串行——明确说"并行"能让它一次发多个请求。
4. 选择性深查
不要让 Agent 对每个容器都 inspect,先用 list_containers 看状态,只对异常的深查:
第 1 步:list_containers() → 看哪些 exited / restarting
第 2 步:只对异常容器 get_container + get_container_logs
第 3 步:只对日志有线索的 exec_in_container 验证
这种"先状态后内容"的分层查询能在百容器主机上省 90% 以上的开销。
5. 避免大镜像全量 history
✗ image_history("huge-image") ← 几百层 history 全返回,爆上下文
✓ image_history("huge-image") 限制层数 + 只看大层
✓ 先 system_df 看镜像总大小,只对前 5 大镜像 history
让 Agent 在 system prompt 里加规则:任何 history 操作先看大小,超 1GB 镜像只看前 10 大层。
与其他"操作 Docker"方案对比
除了 Docker MCP,市面上还有几种让 Agent 操作 Docker 的方式,对比一下:
| 方案 | 优点 | 缺点 | 适合 |
|---|---|---|---|
| Docker MCP | 标准 MCP、结构化返回、类型路由、可设只读 | 部分实现功能有限、需配置 | 日常调试、分析、容器编排 |
| Shell/Bash MCP + docker 命令 | 灵活、能跑任何 docker 命令 | 输出纯文本难解析、权限过大 | 临时一次性操作 |
| 自定义脚本 | 完全可控 | 每个问题写一次、不可复用 | 特定高频任务 |
| Portainer / LazyDocker + 截图 | 可视化强 | Agent 看不到、要人工转述 | 人工排查 |
| 直接给 Agent docker 命令复制 | 不需要配置 | 全程手动、易错 | 极少用 |
典型组合:
日常调试分析:Docker MCP(90%)+ 偶尔 docker 命令(10% 罕见场景)
高频固定任务:封装成自定义脚本 + 定时跑
人工深度排查:Portainer 看可视化 + Docker MCP 做 Agent 分析
实际部署经验
下面是几个真实部署 Docker MCP 时遇到的"非技术"问题,提前知道能少走弯路:
问题 1:Node 版本
Docker MCP 依赖较新的 Node(建议 Node 18+)。老版本跑不起来:
检查:node -v
要求:>= 18
升级:用 nvm install 20 && nvm use 20
问题 2:npx 缓存
每次启动 Agent 都跑 npx -y 会重新检查包,首次可能慢。预安装能加速:
# 全局预装一次
npm install -g @modelcontextprotocol/server-docker
# 配置改用全局版本(去掉 npx -y)
{
"mcpServers": {
"docker": {
"command": "mcp-server-docker",
"args": []
}
}
}
问题 3:跨平台 socket 路径
Docker socket 在不同 OS 上路径不同:
Linux: /var/run/docker.sock
macOS: /var/run/docker.sock(实际软连到 ~/.docker/run/docker.sock)
Windows: //./pipe/docker_engine(命名管道,不能用文件路径方式访问)
WSL2: WSL 内 /mnt/wsl/docker-desktop/docker.sock
(Docker Desktop 的 WSL 集成)
如果 MCP 实现要求显式指定 socket 路径,跨平台配置要适配。不传参数让 MCP 用默认值通常最省事。
问题 4:远程 Docker 必须配 TLS
很多人图省事连远程 Docker 时用裸 tcp://host:2375(无 TLS)——这是绝对禁止的。一旦暴露 2375 端口到公网,几分钟内就会被扫描并入侵(已有大量公开案例):
✗ tcp://host:2375(明文,等同把宿主 root 交给网络)
✓ tcp://host:2376(TLS 加密 + 双向认证)
更稳的做法:
- 远程 Docker 不直接暴露公网
- 通过 SSH 隧道访问:ssh -L 2376:localhost:2376 user@host
- MCP 连 localhost:2376(实际隧道到远程)
问题 5:团队共享配置
团队多人用同一项目,Docker MCP 配置应该提交到仓库吗?
推荐做法:
- 项目级 .cursor/mcp.json 提交到仓库,连本地 socket(开发环境)
- 生产环境远程 Docker 各自维护(用户级配置)
- TLS 证书绝不进仓库,用环境变量
.cursor/mcp.json 示例(提交到仓库,连本地):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
}
}
带证书的版本(不提交,写在用户级):
{
"mcpServers": {
"docker": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-docker",
"--host", "tcp://${DOCKER_HOST}:2376",
"--tlsverify"
],
"env": {
"DOCKER_HOST": "从 1Password / .env 读,不硬编码",
"DOCKER_CERT_PATH": "${HOME}/.docker/prod-certs"
}
}
}
}
一张图总结
Agent 要操作 Docker
↓
配置好 Docker MCP(5 分钟)
↓
对话里说"连 Docker 查…"
↓
Agent 调用对应对象类型工具
↓
┌────┴────┐
│ │
读操作 写操作
│ │
↓ ↓
查/分析 先预览后改(用户确认)
│ │
↓ ↓
结构化返回 start/stop/restart 验证
↓
Agent 基于真实状态回答
↓
┌────┴────┐
│ │
状态异常 一切正常
│ │
↓ ↓
回代码定位 收工
(Filesystem/GitHub MCP)
快速上手 Checklist
如果你刚拿到 Docker MCP,按这个清单一步步来:
□ 1. 确认 Node 版本 >= 18(node -v)
□ 2. 确认 Docker 已安装并启动
- macOS/Windows:Docker Desktop 已运行
- Linux:sudo systemctl status docker 是 active
□ 3. 验证 socket 权限
- docker ps 能跑通(当前用户能读 socket)
□ 4. 找到你客户端的配置文件位置(参考前文表格)
□ 5. 复制通用配置三行:
"docker": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-docker"]
}
□ 6. 重启客户端
□ 7. 跑一个测试对话:
"列出所有运行中的容器"
→ Agent 调用 list_containers → 返回数组 → 成功
□ 8. 试一个真实场景:
"看 api 容器最近 50 行日志"
□ 9. 连远程?配 TLS 证书 + --tlsverify
□ 10. 生产环境?配只读模式(--readonly 或 socket-proxy)
□ 11. system prompt 里禁用 --privileged / 挂载敏感目录
□ 12. 大主机?让 Agent 查询带 filter 和 tail
常见问答
Q1:Docker MCP 能起 K8s 集群里的 Pod 吗?
A:不能。Docker MCP 操作的是单主机 Docker 守护进程,跟 K8s API 是两套。K8s 集群里的 Pod 用 Kubernetes MCP(基于 kube API)。两者不可互替。
Q2:Docker MCP 能管 Docker Swarm 吗?
A:部分支持。Swarm 服务、任务这些概念在标准 Docker API 里有对应端点,但 Docker MCP 实现未必封装。Swarm 模式(docker swarm init、docker service create)目前主流 MCP 实现覆盖不全。需要 Swarm 建议用 Bash MCP + docker 命令。
Q3:Agent 会不会乱删容器?
A:会的,如果不约束。务必:
- 生产环境用
--readonly模式(数据库层面强制) - system prompt 里加规则"任何 remove_* / stop_* 前必须用户确认"
- 关键容器(如生产 db)单独配 socket-proxy 白名单只读
- 用
restart_policy让容器挂了自动起,减少手动操作需求
Q4:Docker MCP 能实时看 stats 吗(CPU/内存)?
A:能查快照,不能持续订阅。get_container_stats 拿的是单次快照(等价 docker stats --no-stream)。要持续监控建议接专门的监控(Prometheus + cAdvisor),MCP 不适合长连接订阅场景。
Q5:Docker MCP 会缓存查询结果吗?
A:默认不缓存,每次调用都打 Docker API。高频重复查同状态时可加 Memory MCP 显式缓存:
1. [memory] 查有没有存过最近 1 分钟的容器列表
2. 没有 → [docker] list_containers → [memory] 存
3. 有 → 直接用记忆里的列表
Q6:怎么限制 Agent 只能查特定容器?
A:Docker 本身没有"按容器名授权"的细粒度 ACL(不像 Redis 的 key pattern)。变通方案:
- 在 socket-proxy 层面按 URL 路径限制(如只允许 /containers/json)
- system prompt 里告诉 Agent"只看 name 以 app- 开头的容器"
- 起一个独立的 Docker 主机专门给 Agent 用,物理隔离
Q7:Docker MCP 和 Portainer 啥关系?
A:互补关系。Portainer 是 GUI 可视化工具,给人看;Docker MCP 是 API,给 Agent 用。日常排查可视化用 Portainer,让 Agent 自动化分析用 Docker MCP,两个一起最爽。
Q8:大主机(200+ 容器)会让 Agent 爆上下文吗?
A:会。务必在 system prompt 里加规则:
- 任何 list 操作先带 filter(status=running 或 name 前缀匹配)
- 默认只返回前 50 个,更多要明确要求
- list_containers 后只对异常的(exited/restarting)做 inspect
否则 Agent 拉 200 个容器 + 每个的 inspect 会瞬间塞爆上下文窗口。
Q9:Docker MCP 跟 Docker Compose 啥关系?
A:Docker MCP 提供 compose 工具子集(compose_up / compose_down / compose_ps 等),等价于 docker compose 命令行。如果你用 Compose 编排项目,用 compose_* 工具比逐个 start_container 更对——compose 会处理依赖顺序、网络、卷。
Q10:能同时连多台 Docker 主机吗?
A:能。在 mcpServers 里配多个不同名(如 docker-host1、docker-host2),分别连不同 --host。对话里说"查 host1 上的容器"让 Agent 路由到对应 Server。注意多机场景更建议上 K8s 而不是多机 Docker——K8s 本身就是为多机调度设计的,比手动管多台 Docker 主机优雅得多。
结语
Docker MCP Server 是个"广而实"的 MCP Server——它一行配置就能起,本地开发零门槛;但一旦连到远程生产,TLS 证书、只读模式、socket-proxy 这些前置工作要花点心思。一旦配好,它能让 Agent 真正看懂容器、镜像、网络、卷的状态,做结构化分析、给出可执行的修复建议。这一步打通后,容器故障排查、镜像体积优化、多容器编排这些原本要 SSH、敲命令、看日志文件才能干的事,全在对话里就能闭环。
它跟 Kubernetes MCP 不是二选一,而是解决不同生命周期容器问题的互补工具:Docker MCP 管"造镜像和单机容器",Kubernetes MCP 管"集群调度和滚动发布"。两个一起配,Agent 才能同时覆盖"本地迭代 + 生产运维"两个阶段,离一个真正好用的 DevOps 助手又近了一大步。
如果你刚开始接运维类 MCP,建议顺序是:
- 先装 Filesystem MCP(让 Agent 能读写本地)
- 再装 Docker MCP(让 Agent 能管容器和镜像)
- 最后装 Kubernetes MCP(让 Agent 能管生产集群)
三个组合起来,Agent 的"运维视野"就完整了——本地文件、单机容器、集群调度全覆盖,从开发到部署整条流水线 Agent 都能搭把手。
相关阅读:
- Filesystem MCP:让 Agent 拥有读写本地文件的能力 —— Dockerfile 和 compose 文件改起来?配这个
- GitHub MCP:让 Agent 直接管理代码仓库 —— 容器故障反查代码根因,配这个
- PostgreSQL MCP:让 AI 直接查询数据库 —— 容器里的 PG 跟代码逻辑对账,配这个
- Memory MCP:让 Agent 拥有跨会话记忆的能力 —— 容器命名规则记下来,别每次重复解释