
【部署实战】Docker AI Agent让AI一键部署Spring Boot到服务器从打包到上线只要一条指令文章目录设置时区创建应用目录复制jar包创建非root用户运行健康检查JVM参数限制堆内存、开启GC日志写在前面写过Spring Boot的都懂——开发一时爽部署火葬场。打完jar包忘了改配置文件的环境变量上去了连不上数据库。改了代码忘了更新Docker镜像上线后跑的还是旧版本。服务器磁盘满了没发现部署到一半直接卡死。这些破事每个开发者都遇到过。我们团队最近搞了一个方案把Docker操作包装成MCP Tool让AI Agent接手部署全流程——从打包到启动到健康检查全部自动化出错了还会自己回滚。这篇文章完整拆解这套方案。环境Spring Boot 3.3.0 Docker 26 MCP协议。服务器LinuxCentOS 7/Ubuntu 22.04均可。一、手工部署到底有多烦回忆一下你上次部署的流程。本地 mvn clean package 打jar包。把jar包scp到服务器。修改application-prod.yml里的数据库地址。docker build 构建镜像。docker stop 停掉旧容器。docker run 启动新容器。docker logs -f 盯着日志看有没有报错。发现数据库连不上查了半天发现是环境变量没设对。重新改了环境变量再跑一遍。整个过程20分钟起步如果中间出问题半小时一小时很正常。人还会漏步骤——忘了备份、忘了健康检查、忘了清理旧镜像导致磁盘满了。AI Agent都能帮你干。二、Dockerfile模板dockerfileFROM 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 appgroupUSER appuser健康检查HEALTHCHECK --interval30s --timeout5s --retries3CMD wget --no-verbose --tries1 --spider http://localhost:8080/actuator/health || exit 1EXPOSE 8080JVM参数限制堆内存、开启GC日志ENTRYPOINT [“java”,“-Xms256m”, “-Xmx512m”,“-XX:UseG1GC”,“-XX:MaxGCPauseMillis200”,“-Djava.security.egdfile:/dev/./urandom”,“-jar”, “app.jar”]docker-compose.ymlyamlversion: ‘3.8’services:app:build: .container_name:A P P N A M E : − m y a p p p o r t s : − {APP_NAME:-myapp} ports: - APPNAME:−myappports:−{APP_PORT:-8080}:8080environment:- SPRING_PROFILES_ACTIVES P R I N G P R O F I L E S A C T I V E : − p r o d − D B U R L {SPRING_PROFILES_ACTIVE:-prod} - DB_URLSPRINGPROFILESACTIVE:−prod−DBURL{DB_URL}- DB_USERNAMED B U S E R N A M E − D B P A S S W O R D {DB_USERNAME} - DB_PASSWORDDBUSERNAME−DBPASSWORD{DB_PASSWORD}- REDIS_HOST${REDIS_HOST:-redis}volumes:- ./logs:/app/logsrestart: unless-stoppedhealthcheck:test: [“CMD”, “curl”, “-f”, “http://localhost:8080/actuator/health”]interval: 30stimeout: 5sretries: 5start_period: 60snetworks:- app-networknetworks:app-network:driver: bridge三、部署操作Tool——给AI的操作接口javaComponentpublic 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(); } } Tool(description 查看服务器磁盘和内存使用情况。 Docker镜像和容器日志经常占满磁盘需要定期清理。 磁盘使用率超过80%时需要关注超过90%需要紧急清理) public String checkServerHealth() { StringBuilder report new StringBuilder(服务器健康报告\n\n); // 磁盘 report.append(【磁盘使用】\n); report.append(execCommand(null, df -h / /var/lib/docker | tail -n 2)); // 内存 report.append(\n【内存使用】\n); report.append(execCommand(null, free -h)); // Docker磁盘占用 report.append(\n【Docker磁盘占用】\n); report.append(execCommand(null, docker system df)); return report.toString(); } Tool(description 清理Docker垃圾未使用的镜像、停止的容器、悬挂的卷、构建缓存。 建议每周执行一次避免磁盘被占满。 清理前确认没有需要保留的旧版本) public String cleanDocker() { StringBuilder log new StringBuilder(Docker清理报告\n); log.append(清理前\n); log.append(execCommand(null, docker system df)); log.append(\n开始清理...\n); log.append(execCommand(null, docker system prune -f --volumes)); log.append(\n清理后\n); log.append(execCommand(null, docker system df)); return log.toString(); } Tool(description 查看应用实时日志。支持过滤关键词如ERROR、WARN。 tailLines控制返回最近多少行避免日志量过大) public String viewLogs( ToolParam(description 过滤关键词如ERROR/WARN/Exception留空查看全部) String keyword, ToolParam(description 显示最近N行建议20-100) int tailLines) { String cmd docker logs myapp --tail tailLines; if (keyword ! null !keyword.isEmpty()) { cmd 21 | grep -i keyword; } return execCommand(null, cmd); } Tool(description 回滚到指定版本。version为docker image的tag。) public String rollback( ToolParam(description 目标版本号image tag) String version) { StringBuilder log new StringBuilder(); log.append(execCommand(null, docker stop myapp)); log.append(execCommand(null, docker rm myapp)); log.append(execCommand(null, docker run -d --name myapp -p 8080:8080 myapp: version)); return 已回滚到版本 version \n 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(); } }}四、AI Agent的部署全流程你只需要说一句话“帮我把项目部署到测试环境。”Agent自动执行编译打包跳过测试或全量跑取决于环境、构建Docker镜像、备份当前运行版本、停止旧容器、启动新容器、等待健康检查、健康检查通过→清理旧镜像、健康检查失败→自动回滚。整个过程你喝着咖啡看日志就行出错了还能自动回滚。五、定时巡检javaComponentpublic class ServerCronJob {Autowired private DockerDeployTool deployTool; Scheduled(cron 0 0 9 * * ?) // 每天早上9点 public void dailyHealthCheck() { String health deployTool.checkServerHealth(); // 如果磁盘使用率超80%发告警 if (health.contains(80%) || health.contains(90%)) { sendAlert(服务器磁盘告警, health); } } Scheduled(cron 0 0 3 ? * SUN) // 每周日凌晨3点 public void weeklyCleanup() { deployTool.cleanDocker(); }}六、踩坑记录坑1mvn package慢得离谱。 小项目还好大项目一次打包两分钟。如果每次都全量打包CI/CD流水线会被拖慢。解决方法本地开发用dev profile跳过不必要的插件。坑2docker build的层层缓存。 Dockerfile里COPY命令顺序错了每次改代码都会让所有层重建。正确顺序先COPY pom.xml → RUN mvn dependency → 再COPY src → RUN mvn package。坑3健康检查的start_period设太短。 Spring Boot启动到完全就绪可能需要30-60秒。如果start_period只设10秒容器还没启动完就被判定为unhealthy然后又被重启进入死亡循环。yamlhealthcheck:test: [“CMD”, “curl”, “-f”, “http://localhost:8080/actuator/health”]interval: 30stimeout: 5sretries: 5start_period: 60s # ← 给够启动时间七、总结这套方案把部署从手动20分钟易出错变成了一句话指令自动完成还带回滚。三个核心组件Docker Tool包装操作命令、健康检查确保新版本可用、自动回滚保证不出生产事故。适合所有用Docker部署Spring Boot应用的团队。个人开发者用这套也能省下大量运维时间——把精力放在写代码上部署交给AI。有用的话点赞收藏。下一篇《AI Agent ElasticsearchMCP协议实现智能全文搜索》。