ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker + AI Agent 部署实战:用 TaoToken 打通 MCP 让 Spring Boot 一条指令上线

Docker + AI Agent 部署实战:用 TaoToken 打通 MCP 让 Spring Boot 一条指令上线 1. 手工部署 Spring Boot 到 Docker 到底卡在哪从打包到上线的完整链路拆解Spring Boot 项目从本地打包到服务器上线这条链路看起来只有几步真正跑起来却处处是坑。我先把最典型的流程摆出来本地执行mvn clean package打出 jar 包用 scp 传到服务器改application-prod.yml里的数据库地址docker build构建镜像docker stop停掉旧容器docker run启动新容器最后docker logs -f盯着日志看有没有报错。这一套走下来顺利的话二十分钟中间任何一个环节出问题半小时到一小时都很正常。问题不在于命令本身难而在于这些命令是散的。环境变量忘了改、健康检查忘了加、旧镜像没清理导致磁盘满了、回滚的时候找不到上一个可用版本——这些都是人工操作里高频出现的失误。更麻烦的是当你想让 AI Agent 来接管这套流程时Agent 需要一个明确的工具接口去调用这些操作而不是让它去猜 shell 命令。这就是 MCPModel Context Protocol要解决的问题把 Docker 操作封装成 Agent 能理解、能调用的 Tool同时把鉴权和通道统一到一个可控的入口。这篇要讲的就是这条链路Spring Boot 3.3.0 Docker 26 MCP 协议服务器用 LinuxCentOS 7 或 Ubuntu 22.04 都行。核心思路是把部署动作拆成可复用的 Tool让 AI Agent 通过 MCP 调用而所有对外请求的 endpoint 和 API Key 统一走 TaoToken 的通道。这样做的直接好处是鉴权集中管理Agent 调用部署工具时不会把密钥散落在各个脚本里通道统一后模型对话、coding-plan、console 这些能力可以共用同一套凭证。适合谁看如果你是用 Docker 部署 Spring Boot 的开发者或者正在尝试把 AI Agent 接入实际运维流程这篇的配置片段可以直接复制。如果你只是想了解 MCP 是什么、能做什么前面的 Dockerfile 和 compose 部分也能帮你把基础链路跑通。下面从 Dockerfile 开始一步步把可复制的配置给出来。2. TaoToken 前置准备把 endpoint 与 API Key 统一到一条通道在把 Docker 操作封装成 MCP Tool 之前先要把鉴权和通道配置好。这一步的核心是所有需要调用模型或 Agent 能力的地方endpoint 和 API Key 都指向同一个入口而不是每个脚本里各写一份。TaoToken 在这里扮演的是统一通道的角色官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。具体要准备三样东西Base URL、API Key、Model ID。这三件套在后面的 MCP 服务端配置、Cline MCP 配置、以及 Codex 的 auth.json 里都会用到。Base URL 填https://taotoken.net/apiAPI Key 在 console 里生成Model ID 根据你实际用的模型填。如果你用的是 Claude Code 或者类似的编码 Agentcoding-plan 页面有对应的套餐说明如果只是验证模型连通性模型对话页面可以直接测试。这里要强调一点不要把 API Key 硬编码在 Dockerfile 或者 docker-compose.yml 里。正确的做法是通过环境变量注入或者在 MCP 服务端的配置文件里引用。下面给一个环境变量的示例你可以放在服务器的.env文件里或者通过 CI/CD 的 secret 管理# .env 文件不要提交到 git TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的实际key TAOTOKEN_MODEL_ID你的模型ID然后在 docker-compose.yml 里引用这些变量。这样做的另一个好处是当你要切换环境dev/test/prod时只需要改.env文件不用动 Dockerfile 和 compose 文件。MCP 服务端读取这些环境变量后Agent 调用部署工具时就会自动带上正确的鉴权信息。如果你用的是 Cline 或者类似的 MCP 客户端配置里需要写全三件套。下面是一个 MCP 服务端的配置片段路径和字段名按你实际的项目结构调整{ mcpServers: { docker-deploy: { command: java, args: [-jar, /app/mcp-server.jar], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的实际key, TAOTOKEN_MODEL_ID: 你的模型ID } } } }这段配置的关键是env里的三个变量。MCP 服务端启动后会读取它们后续 Agent 发起的请求都会走这个通道。如果你用的是 Codex对应的配置文件是auth.json字段名可能略有不同但 Base URL、Key、Model ID 这三样是必须的。配置完成后可以用模型对话页面发一条测试请求确认通道是通的。确认无误后再进入下一步的 Docker 配置。3. 可复制配置Dockerfile、docker-compose 与 MCP 服务端片段这一节给的是可以直接复制粘贴的配置。先看 Dockerfile基于 eclipse-temurin:21-jre-alpine设置了时区、非 root 用户、健康检查和 JVM 参数FROM eclipse-temurin:21-jre-alpine # 设置时区 RUN apk add --no-cache tzdata \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ echo Asia/Shanghai /etc/timezone # 创建应用目录 WORKDIR /app # 复制 jar 包 COPY target/*.jar app.jar # 创建非 root 用户运行 RUN addgroup -S appgroup adduser -S appuser -G appgroup USER appuser # 健康检查 HEALTHCHECK --interval30s --timeout5s --retries3 \ CMD wget --no-verbose --tries1 --spider http://localhost:8080/actuator/health || exit 1 EXPOSE 8080 # JVM 参数限制堆内存、开启 GC 日志 ENTRYPOINT [java, \ -Xms256m, -Xmx512m, \ -XX:UseG1GC, \ -XX:MaxGCPauseMillis200, \ -Djava.security.egdfile:/dev/./urandom, \ -jar, app.jar]这里有几个细节值得说。HEALTHCHECK用的是 wget 而不是 curl因为 alpine 镜像里默认没有 curl装 curl 会增加镜像体积。start_period在 Dockerfile 的 HEALTHCHECK 里不能直接写需要在 docker-compose 里覆盖。JVM 参数里-Xmx512m是上限实际用多少取决于你的应用别照搬。接下来是 docker-compose.yml这里把环境变量、健康检查的 start_period、以及网络配置都写全version: 3.8 services: app: build: . container_name: ${APP_NAME:-myapp} ports: - ${APP_PORT:-8080}:8080 environment: - SPRING_PROFILES_ACTIVE${SPRING_PROFILES_ACTIVE:-prod} - DB_URL${DB_URL} - DB_USERNAME${DB_USERNAME} - DB_PASSWORD${DB_PASSWORD} - REDIS_HOST${REDIS_HOST:-redis} - TAOTOKEN_BASE_URL${TAOTOKEN_BASE_URL} - TAOTOKEN_API_KEY${TAOTOKEN_API_KEY} - TAOTOKEN_MODEL_ID${TAOTOKEN_MODEL_ID} volumes: - ./logs:/app/logs restart: unless-stopped healthcheck: test: [CMD, wget, --no-verbose, --tries1, --spider, http://localhost:8080/actuator/health] interval: 30s timeout: 5s retries: 5 start_period: 60s networks: - app-network networks: app-network: driver: bridge注意start_period: 60s这一行。Spring Boot 启动到完全就绪可能需要 30 到 60 秒如果 start_period 设太短容器还没启动完就被判定为 unhealthy然后被重启进入死亡循环。这个坑后面还会细说。然后是 MCP 服务端的配置。这里给的是 Java 侧的 Tool 定义核心是把部署流程封装成一个 Agent 可调用的方法Component public class DockerDeployTool { Tool(description 执行完整的部署流程1.编译打包 2.构建Docker镜像 3.停止旧容器 4.启动新容器 5.健康检查。 任何一步失败都会自动回滚到上一个可用版本。 部署前确保.gitignore已配置避免提交敏感文件) public String deploy( ToolParam(description 项目路径如/home/app/myproject) String projectPath, ToolParam(description 环境dev/test/prod) String env, ToolParam(description 是否跳过测试紧急修复时可跳过) boolean skipTests) { StringBuilder log new StringBuilder(); String oldContainerId null; try { // 1. 编译打包 log.append(execCommand(projectPath, skipTests ? mvn clean package -DskipTests -q : mvn clean package -q)); // 2. 备份当前运行的容器ID oldContainerId execCommand(projectPath, docker ps -q --filter namemyapp).trim(); // 3. 构建新镜像带版本标签方便回滚 String version String.valueOf(System.currentTimeMillis()); log.append(execCommand(projectPath, docker build -t myapp: version -t myapp:latest .)); // 4. 停旧容器 if (!oldContainerId.isEmpty()) { log.append(execCommand(projectPath, docker stop oldContainerId)); } // 5. 启动新容器 log.append(execCommand(projectPath, docker compose up -d)); // 6. 等待健康检查 Thread.sleep(10000); String health execCommand(projectPath, docker inspect --format{{.State.Health.Status}} myapp).trim(); if (!healthy.equals(health)) { throw new RuntimeException(健康检查失败状态 health); } log.append(部署成功新版本 version); log.append(健康检查通过); // 7. 清理旧镜像保留最近3个版本 execCommand(projectPath, docker images myapp --format {{.Tag}} | sort -r | tail -n 4 | xargs -r docker rmi); return log.toString(); } catch (Exception e) { log.append(部署失败 e.getMessage()); // 自动回滚 if (oldContainerId ! null !oldContainerId.isEmpty()) { log.append(\n正在回滚到上一个版本...); execCommand(projectPath, docker start oldContainerId); log.append(已回滚); } return log.toString(); } } private String execCommand(String workingDir, String command) { try { ProcessBuilder pb new ProcessBuilder(/bin/bash, -c, command); if (workingDir ! null) { pb.directory(new java.io.File(workingDir)); } Process process pb.start(); String output new String(process.getInputStream().readAllBytes()); String error new String(process.getErrorStream().readAllBytes()); process.waitFor(5, java.util.concurrent.TimeUnit.MINUTES); return output (error.isEmpty() ? : \n error); } catch (Exception e) { return 命令执行失败 e.getMessage(); } } }这段代码的关键是Tool注解和ToolParam注解它们让 MCP 协议能识别这个方法是一个可调用的工具参数也能被 Agent 理解。execCommand方法封装了 ProcessBuilder注意waitFor设了 5 分钟超时避免构建卡死。回滚逻辑在 catch 块里用备份的 oldContainerId 重新启动旧容器。4. 验证请求与成功结果一条指令触发构建、推送、拉取、启动配置写完之后验证的步骤很直接。你只需要对 Agent 说一句话“帮我把项目部署到测试环境。” Agent 会通过 MCP 调用上面定义的deploy方法自动执行编译打包、构建镜像、停止旧容器、启动新容器、健康检查这一整套流程。实际跑起来的时候你会在日志里看到类似这样的输出[INFO] BUILD SUCCESS [INFO] Total time: 12.345 s Successfully built a1b2c3d4e5f6 Successfully tagged myapp:1712345678901 Successfully tagged myapp:latest myapp Container myapp Stopped Container myapp Started 部署成功新版本1712345678901 健康检查通过最后一步是访问健康检查接口确认上线结果。在服务器上执行curl -s http://localhost:8080/actuator/health返回{status:UP}就说明服务已经正常启动。如果你在 docker-compose 里映射了端口也可以从外部访问http://你的服务器IP:8080/actuator/health。这一步不要跳过因为健康检查通过只代表容器内部认为自己是健康的外部端口是否可达还需要单独验证。如果健康检查失败Agent 会自动回滚到上一个版本。你会在日志里看到“正在回滚到上一个版本...”和“已回滚”的提示。这时候需要去看docker logs myapp --tail 100的输出定位具体是哪个环节出了问题。常见的失败原因包括数据库连接不上、端口被占用、环境变量没注入。回滚机制保证了即使部署失败线上服务也不会中断。验证通过之后你可以把这条指令固化下来。比如在 CI/CD 流水线里把“帮我把项目部署到测试环境”替换成直接调用 MCP 工具的 API 请求。或者在 Cline 的对话里保存这个指令模板下次直接复用。整个流程从手工二十分钟压缩到一条指令而且出错有回滚这是这套方案最直接的价值。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth部署过程中最容易遇到的报错集中在鉴权和通道配置上。下面按真实报错逐个排查。401 Unauthorized这个报错通常出现在 MCP 服务端调用模型接口时。原因一般是 API Key 没配置、配置错了、或者环境变量没注入到容器里。排查步骤先在服务器上执行echo $TAOTOKEN_API_KEY确认变量存在然后在容器内执行docker exec -it myapp env | grep TAOTOKEN确认变量传进去了最后用模型对话页面单独测试这个 Key 是否有效。如果 Key 是对的但依然 401检查 Base URL 是不是写成了https://taotoken.net/api注意不要多加斜杠或者路径。local proxy failed这个报错一般出现在 MCP 客户端连接服务端的时候。常见原因是 MCP 服务端的启动命令路径不对或者 Java 进程没起来。排查步骤先手动执行java -jar /app/mcp-server.jar看能不能启动如果能启动但客户端连不上检查 MCP 配置里的command和args是否和实际路径一致。另外如果服务器上有防火墙或者安全组限制也需要确认端口是放行的。reading choices 报错这个通常出现在模型返回结果解析阶段。原因可能是 Model ID 填错了或者请求的模型和返回格式不匹配。排查步骤确认TAOTOKEN_MODEL_ID和你在模型对话页面测试时用的模型一致如果用的是 coding-plan 里的模型确认套餐是否覆盖了这个模型。另外检查 MCP 服务端的日志看请求发出去之后返回的原始内容是什么有时候是返回了错误信息但被当成正常结果解析了。OAuth 相关报错如果你用的是 Claude Code 或者类似的编码 Agent可能会遇到 OAuth 鉴权失败。这时候需要检查三件套是否写全Base URL、API Key、Model ID。在 Claude Code 的配置里这三个字段通常写在 settings 文件里。如果用的是 Codex检查auth.json里的字段名是否正确。一个常见的坑是Base URL 写成了官网地址而不是 API 地址导致 OAuth 流程走不通。记住 API 入口是https://taotoken.net/api不带 UTM 参数。除了鉴权问题还有几个部署本身的坑。第一个是mvn package太慢大项目一次打包两分钟如果每次全量打包CI/CD 流水线会被拖慢。解决办法是本地开发用 dev profile跳过不必要的插件。第二个是docker build的层层缓存Dockerfile 里 COPY 命令顺序错了每次改代码都会让所有层重建。正确顺序是先 COPY pom.xml → RUN mvn dependency → 再 COPY src → RUN mvn package。第三个是健康检查的 start_period 设太短前面已经说过Spring Boot 启动到完全就绪可能需要 30 到 60 秒start_period 至少设 60s。6. 语义一致 CTA把部署交给 Agent把精力留给代码这套方案跑通之后部署这件事就从“手工二十分钟易出错”变成了“一条指令自动完成还带回滚”。三个核心组件各司其职Docker Tool 封装操作命令健康检查确保新版本可用自动回滚保证不出生产事故。适合所有用 Docker 部署 Spring Boot 应用的团队个人开发者用这套也能省下大量运维时间。如果你在配置 MCP 服务端或者鉴权通道时遇到问题可以直接去 API Keys 页面生成新的 Key然后对照接入文档检查配置。文档里有完整的字段说明和示例。如果你只是想先验证模型连通性模型对话页面可以快速测试。如果你打算长期用 Agent 做编码和部署coding-plan 页面有对应的套餐说明适合需要持续调用模型能力的场景。下一篇会讲 AI Agent 结合 Elasticsearch 通过 MCP 协议实现智能全文检索思路和这篇类似把检索操作封装成 Tool让 Agent 来调用。部署这条链路打通之后后面的运维和检索场景都可以复用同一套鉴权和通道配置。
返回列表