
一、为什么需要自动化构建与部署随着微服务架构在企业级项目中的大规模落地传统的「手工打包、手工上传服务器、手工启动进程」部署方式已经很难满足业务快速迭代的需要。一个中大型 Spring Cloud 项目通常由网关、认证、用户、订单、支付、消息等十几个甚至几十个微服务组成每次发版如果都依赖人工操作不仅耗时惊人而且极易因为环境差异、版本遗漏、命令执行顺序错误等原因引发线上故障。自动化构建与部署也就是通常所说的 CI/CD持续集成与持续交付/持续部署核心目标是把代码从提交到上线的整个链路标准化、流程化、可追踪。开发人员只需要把代码推送到 Git 仓库后续的编译、单元测试、镜像构建、镜像推送、容器部署、健康检查全部由流水线自动完成。这样既降低了人为失误也能把发版频率从过去的几周一次提升到一天多次真正支撑起快速迭代的业务需求。本文将以一个典型的 Java 微服务项目为背景完整讲解如何用 GitLab 管理源码、Maven 完成依赖管理与打包、Docker 完成容器化、Jenkins 完成流水线编排最终实现 Spring Cloud 微服务的自动化构建与部署。整篇文章会从环境准备开始逐步深入到项目改造、流水线设计、部署策略、监控回滚等生产级实践力求让读者跟着操作就能搭建起一套可用的 CI/CD 体系。阅读建议本文假定读者已经具备 Spring Boot、Spring Cloud、Docker 和 Linux 的基础知识。如果你对 Maven 或 Jenkins 还不太熟悉建议先通读基础章节再动手实践。全文代码示例均基于 Java 11、Spring Cloud Hoxton 系列和 Jenkins 2.x 进行编写其他版本的核心思路一致只有少量配置细节需要调整。二、整体架构与部署拓扑在设计自动化构建与部署方案之前我们首先要明确整套系统的整体架构。一个典型的微服务 CI/CD 链路可以抽象为以下几个核心角色代码仓库使用 GitLab 托管所有微服务的源码和部署脚本负责版本控制、代码评审、合并请求管理。构建工具使用 Maven 负责依赖下载、编译、单元测试和打包最终产物为可执行的 JAR 文件。镜像仓库使用私有 Docker Registry 或 Harbor 存储构建好的镜像作为镜像分发的中枢。持续集成平台使用 Jenkins 负责监听代码变更、触发流水线、调度构建任务、执行部署脚本。运行环境使用 Docker 容器承载微服务实例通过 Docker Compose 或 Kubernetes 进行编排和调度。从数据流的角度看整个过程可以拆成以下阶段开发人员将代码推送到 GitLab 的指定分支。GitLab 通过 Webhook 通知 Jenkins 有新的代码提交。Jenkins 拉取源码调用 Maven 执行编译和单元测试。Maven 打包成功后Jenkins 调用 Docker 构建镜像并给镜像打上版本标签。Jenkins 将镜像推送到私有镜像仓库。Jenkins 通过 SSH 或 Docker Remote API 在目标服务器上拉取新镜像并重启容器。部署完成后执行健康检查检查通过则整个发版流程结束。下图使用 Mermaid 流程图描述了这条链路下面给出一套可用于实际演示的最小部署拓扑。为了控制成本初学者可以把 GitLab、Jenkins、Harbor 和测试环境部署在同一台服务器上生产环境则建议按照角色进行拆分服务器角色主机名示例部署组件推荐配置代码仓库服务器gitlab.example.comGitLab4 核 8G 起建议 8G 以上内存持续集成服务器jenkins.example.comJenkins、Maven、Docker4 核 8G 起建议 8G 以上内存镜像仓库服务器harbor.example.comHarbor 或 Docker Registry4 核 8G磁盘 200G 以上测试环境服务器test-app.example.comDocker 运行微服务容器8 核 16G 起视服务数量调整生产环境服务器prod-app.example.comDocker、微服务容器建议多节点高可用理解了整体架构之后接下来的章节会按照「环境准备、代码仓库、项目开发、Maven 配置、Docker 容器化、Jenkins 流水线、部署与运维」的顺序逐步展开。三、环境准备与基础安装3.1 服务器基础环境本文所有操作均以 CentOS 7 或 CentOS 8 为例。开始之前建议先统一基础环境关闭 SELinux、关闭防火墙或放行所需端口、安装常用工具并配置好主机名和时间同步。以下是初始化服务器的常用命令bash# 查看系统版本 cat /etc/redhat-release # 关闭 SELinux临时 setenforce 0 # 永久关闭 SELinux编辑 /etc/selinux/config # 将 SELINUXenforcing 改为 SELINUXdisabled保存后重启 # 安装常用工具 yum install -y vim wget git net-tools telnet lsof ntpdate # 同步时间 ntpdate ntp.aliyun.com # 设置主机名 hostnamectl set-hostname jenkins.example.com端口规划是一个很容易被忽略的细节。正式部署时建议提前约定好各组件的端口避免安装到一半才发现端口冲突组件默认端口说明GitLab80 / 443 / 22Web 页面和 SSH 协议拉取代码Jenkins8080 / 50000Web 页面和 JNLP 代理端口Harbor80 / 443镜像仓库 Web 和管理 APIMySQL3306微服务数据库Redis6379微服务缓存微服务实例8100-8999按服务规划独立端口3.2 安装 JDKJava 环境是所有环节的基础。Jenkins 运行依赖 JDKMaven 编译依赖 JDKSpring Cloud 服务运行同样依赖 JDK。本文以 OpenJDK 11 为例使用 yum 安装bash# 安装 OpenJDK 11 yum install -y java-11-openjdk java-11-openjdk-devel # 查看 Java 版本 java -version # 查看 JAVA_HOME 路径 dirname $(dirname $(readlink -f $(which java)))安装完成后建议在/etc/profile中配置JAVA_HOME环境变量方便 Maven、Jenkins 以及其他脚本统一引用bash# 编辑 /etc/profile追加以下内容 export JAVA_HOME/usr/lib/jvm/java-11-openjdk-11.0.21.0.9-2.el7_9.x86_64 export PATH$JAVA_HOME/bin:$PATH # 使配置生效 source /etc/profile # 验证 echo $JAVA_HOME3.3 安装 MavenMaven 是 Java 项目最常用的构建工具。这里下载官方二进制包并解压到/usr/local目录bash# 下载 Maven 3.8.8 cd /usr/local wget https://archive.apache.org/dist/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz # 解压 tar -zxvf apache-maven-3.8.8-bin.tar.gz # 更名为 maven mv apache-maven-3.8.8 maven # 配置环境变量 echo export MAVEN_HOME/usr/local/maven /etc/profile echo export PATH$MAVEN_HOME/bin:$PATH /etc/profile source /etc/profile # 验证 mvn -versionMaven 默认从中央仓库下载依赖国内网络环境下下载速度往往比较慢建议配置阿里云镜像。编辑$MAVEN_HOME/conf/settings.xml在mirrors节点中添加xmlmirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror同时建议在settings.xml中配置本地仓库路径避免默认路径占用系统盘空间xmllocalRepository/data/maven-repo/localRepository3.4 安装 DockerDocker 是承载微服务运行环境的核心组件。这里使用 Docker 官方脚本安装社区版并配置镜像加速bash# 安装 Docker curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun # 启动 Docker 并设置开机自启 systemctl start docker systemctl enable docker # 验证安装 docker version docker info配置国内镜像加速器可以显著提升镜像拉取速度。创建或编辑/etc/docker/daemon.jsonjson{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.nju.edu.cn ], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } }配置完成后重启 Docker 服务bashsystemctl daemon-reload systemctl restart docker生产环境中还建议把 Docker 数据目录迁移到大容量磁盘避免容器和镜像占用系统盘导致磁盘写满。修改daemon.json增加data-root配置后重启 Docker 即可。3.5 安装 Docker Compose对于中小规模的服务编排场景Docker Compose 是一种简单直观的选择。安装命令如下bash# 下载 Docker Compose curl -L https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # 赋予执行权限 chmod x /usr/local/bin/docker-compose # 创建软链接 ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose # 验证 docker-compose version至此基础的运行环境已经准备完毕。后续章节中我们用到的所有组件包括 GitLab、Jenkins、Harbor都可以直接通过 Docker 安装也可以采用传统方式安装读者可以根据自己的服务器资源和熟悉程度选择。四、GitLab 私有代码仓库搭建与配置4.1 安装 GitLabGitLab 提供了官方 Docker 镜像使用 Docker 安装是最快捷的方式。假设我们计划把 GitLab 部署在 192.168.1.100 这台服务器上域名规划为gitlab.example.com安装命令如下bash# 创建数据目录 mkdir -p /data/gitlab/config /data/gitlab/logs /data/gitlab/data # 启动 GitLab 容器 docker run -d \ --name gitlab \ --restart always \ --hostname gitlab.example.com \ -p 8929:80 \ -p 8920:22 \ -v /data/gitlab/config:/etc/gitlab \ -v /data/gitlab/logs:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:16.8.0-ce.0这里把容器内的 80 和 22 端口分别映射到宿主机的 8929 和 8920 端口是因为宿主机上往往还运行着 Nginx 和 SSH 服务直接占用 80 和 22 会冲突。GitLab 首次启动比较慢需要耐心等待几分钟。可以通过以下命令查看启动日志bashdocker logs -f gitlab当看到提示服务已经启动后访问http://192.168.1.100:8929即可打开 GitLab 页面。初次访问需要设置root用户密码设置完成后用root登录。4.2 配置外部访问地址和 SSH 端口GitLab 容器启动后还需要调整配置以匹配我们自定义的端口和域名。编辑宿主机的/data/gitlab/config/gitlab.rb文件rubyexternal_url http://gitlab.example.com:8929 gitlab_rails[gitlab_shell_ssh_port] 8920 gitlab_rails[time_zone] Asia/Shanghai nginx[listen_port] 80 nginx[listen_https] false gitlab_rails[smtp_enable] false保存文件后进入容器重新加载配置bashdocker exec -it gitlab gitlab-ctl reconfigure docker exec -it gitlab gitlab-ctl restart重新加载完成后还需要确认 GitLab 页面展示的 SSH 克隆地址端口是否正确。登录 GitLab进入Admin Area → Settings → Network → Performance optimization可以查看相关配置在项目克隆页面也可以直观地看到 SSH 地址是否带上了 8920 端口。4.3 创建项目与用户为了让团队协作更加规范建议不要在个人命名空间下直接创建项目而是先创建一个 Group例如microservice再在 Group 下创建各个微服务项目。操作步骤如下点击顶部菜单Groups → New group创建名为microservice的群组。进入群组点击New project → Create blank project创建第一个服务项目user-service。进入Settings → Members为团队成员分配 Developer 或 Maintainer 权限。对于本文演示我们至少需要创建一个可供 Jenkins 使用的访问凭据。在 GitLab 中推荐使用 Access Token 替代密码进行 API 调用和代码拉取进入Profile → Access Tokens输入 Token 名称并勾选read_repository、write_repository、api权限。点击创建并复制 Token该 Token 只会显示一次请妥善保存。随后在 Jenkins 的凭据管理中配置该 Token并在项目 Webhook 中开启 Push Events 与 Merge Request Events。项目创建完成后建议先将本地代码仓库与 GitLab 关联尝试 push 一次确认 HTTP 和 SSH 两种方式均能正常访问再进入后续流水线配置。五、Spring Cloud 微服务项目准备5.1 项目模块规划为了演示完整流水线我们准备一个名为spring-cloud-cicd-demo的父工程下面挂载user-service和gateway-service两个子模块。这样的结构既能说明多模块继承依赖的方式也方便后续逐个服务独立构建镜像。textspring-cloud-cicd-demo ├── pom.xml ├── user-service │ ├── pom.xml │ └── src └── gateway-service ├── pom.xml └── src5.2 创建 user-service 示例父工程主要负责统一管理版本号不承担实际业务逻辑。下面是父工程的 pom.xmlxmlproject groupIdcom.example/groupId artifactIdspring-cloud-cicd-demo/artifactId version1.0.0/version packagingpom/packaging properties java.version11/java.version spring-cloud.versionHoxton.SR12/spring-cloud.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version${spring-cloud.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement /projectuser-service 子模块继承父工程只需要声明自己需要的依赖即可xmlproject parent groupIdcom.example/groupId artifactIdspring-cloud-cicd-demo/artifactId version1.0.0/version /parent artifactIduser-service/artifactId version1.0.0/version dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project接着编写启动类和简单的接口用于后续验证部署结果javapackage com.example.user; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }javapackage com.example.user.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/user) public class UserController { GetMapping(/{id}) public String getUser(Long id) { return user- id; } }5.3 配置文件与环境隔离微服务在不同环境中使用的数据库、注册中心、日志级别往往不同。推荐使用 Spring Profile 区分 dev、test 和 prod 环境。下面是 user-service 的 application.ymlyamlserver: port: 8101 spring: application: name: user-service profiles: active: test eureka: client: service-url: defaultZone: http://localhost:8761/eureka/把不同环境的差异写到 application-test.yml、application-prod.yml 中构建时通过 SPRING_PROFILES_ACTIVE 环境变量或启动参数指定即可无需修改代码。六、Maven 构建配置6.1 依赖版本统一管理父工程通过 dependencyManagement 锁定 Spring Cloud 的版本子模块直接继承便能获得一致的版本约束避免各服务版本不一致造成的冲突。团队在实际项目中还应把数据库驱动、消息组件、监控组件等公共依赖也统一放到父工程声明。6.2 打包插件配置Spring Boot 项目打包需要 spring-boot-maven-plugin打包后会得到一个可执行 JAR。也可以用 finalName 控制产物名称方便后续 Dockerfile 引用固定文件名xmlbuild finalNameuser-service/finalName plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration mainClasscom.example.user.UserServiceApplication/mainClass /configuration /plugin /plugins /build6.3 多环境构建参数流水线中的 Maven 命令通常带有-DskipTests因为单元测试可以在独立的 Stage 中执行构建镜像阶段不必重复跑测试。常见的构建命令如下bash# 跳过测试直接打包 mvn clean package -DskipTests # 指定 test 环境配置打包 mvn clean package -Ptest -DskipTests # 生成本地仓库依赖副本供离线打包使用 mvn dependency:go-offline -B七、Docker 容器化改造7.1 编写 Dockerfile采用多阶段构建可以在同一个 Dockerfile 中先完成编译再把 JAR 拷贝到轻量运行镜像中避免把 Maven 依赖层一起打进最终镜像。示例如下dockerfileFROM maven:3.8.8-eclipse-temurin-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests FROM eclipse-temurin:11-jre WORKDIR /app COPY --frombuilder /app/target/user-service.jar app.jar EXPOSE 8101 ENTRYPOINT [java, -jar, app.jar]7.2 本地构建与验证bash# 构建镜像 docker build -t harbor.example.com/microservice/user-service:latest . # 本地运行验证 docker run -d --name user-service -p 8101:8101 \ harbor.example.com/microservice/user-service:latest # 查看日志 docker logs -f user-service # 调用接口 curl http://localhost:8101/api/user/10017.3 Docker Compose 编排测试环境在开发和测试阶段可以使用 Docker Compose 同时拉起多个服务快速验证服务间调用是否正常yamlversion: 3.8 services: user-service: image: harbor.example.com/microservice/user-service:latest container_name: user-service ports: - 8101:8101 environment: - SPRING_PROFILES_ACTIVEtest restart: always gateway-service: image: harbor.example.com/microservice/gateway-service:latest container_name: gateway-service ports: - 8080:8080 depends_on: - user-service restart: always八、Jenkins 流水线搭建8.1 安装 Jenkins这里同样使用 Docker 启动 Jenkins并将宿主机 Docker 套接字挂载进去使 Jenkins 能够直接在本机构建镜像bashmkdir -p /data/jenkins/home docker run -d \ --name jenkins \ --restart always \ -p 8080:8080 \ -p 50000:50000 \ -v /data/jenkins/home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ jenkins/jenkins:2.440.3-lts第一次访问http://jenkins.example.com:8080时需要输入初始密码密码可通过docker logs jenkins查看或读取容器内/var/jenkins_home/secrets/initialAdminPassword文件。8.2 插件与凭据配置安装完成后建议先安装 GitLab、Docker Pipeline、Pipeline、SSH Agent 等插件再完成以下配置在Manage Jenkins → Tools中配置 Maven 安装路径和 JDK 11。在Manage Jenkins → Credentials中新增 GitLab Access Token 凭据。新增 Harbor 账号凭据用于推送镜像。新增目标服务器的 SSH 私钥凭据用于执行部署命令。8.3 Jenkinsfile 流水线示例把流水线以 Jenkinsfile 的形式放入代码仓库可以让构建规则和代码一起版本化。下面是一个从编译到部署的完整示例groovypipeline { agent any environment { SERVICE_NAME user-service IMAGE_NAME harbor.example.com/microservice/user-service IMAGE_TAG ${env.BUILD_NUMBER} DEPLOY_HOST test-app.example.com } stages { stage(Checkout) { steps { checkout scm } } stage(Unit Test) { steps { sh mvn clean test } } stage(Package) { steps { sh mvn clean package -DskipTests } } stage(Build Image) { steps { sh docker build -t ${IMAGE_NAME}:${IMAGE_TAG} . } } stage(Push Image) { steps { sh docker push ${IMAGE_NAME}:${IMAGE_TAG} } } stage(Deploy) { steps { sh ssh ${DEPLOY_HOST} docker pull ${IMAGE_NAME}:${IMAGE_TAG} sh ssh ${DEPLOY_HOST} docker rm -f ${SERVICE_NAME} || true sh ssh ${DEPLOY_HOST} docker run -d --name ${SERVICE_NAME} -p 8101:8101 ${IMAGE_NAME}:${IMAGE_TAG} } } } }九、部署策略与运维9.1 镜像版本标记与回滚每次构建使用递增的构建号作为镜像标签可以保证历史版本可追溯。回滚时只需要重新启动上一个版本号对应的镜像不必重新编译代码bash# 查看已有镜像版本 docker images | grep user-service # 回滚到上一版本假设上一版本标签为 87 docker pull harbor.example.com/microservice/user-service:87 docker rm -f user-service docker run -d --name user-service -p 8101:8101 \ harbor.example.com/microservice/user-service:879.2 健康检查与日志部署成功并不代表服务一定可用还应配置健康检查。可以在 Docker 中通过 HEALTHCHECK 让容器周期性检查服务状态也可以由 Jenkins 在部署后主动调用健康检查接口bash# 等待服务启动 sleep 15 # 检查容器状态 docker ps | grep user-service # 调用健康检查接口 curl -f http://localhost:8101/api/user/health || exit 1日常运维中建议统一收集 stdout 日志并配置按大小滚动例如 Docker 默认的 json-file 驱动配合 max-size 和 max-file 配置可以较好地避免日志无限占用磁盘。9.3 常见问题排查Maven 下载依赖超时检查阿里云镜像地址是否配置正确必要时清空本地仓库的.lastUpdated文件后重试。Docker 镜像拉取缓慢确认/etc/docker/daemon.json中镜像加速器可用并重启 Docker。Jenkins 无法连接 GitLab检查 Webhook 地址、Access Token 权限以及 Jenkins 所在机器到 GitLab 的网络与端口连通性。容器启动后立即退出通过docker logs查看异常堆栈常见原因包括端口冲突、数据库连接失败或配置项缺失。部署后接口 404确认应用端口与容器暴露端口一致同时检查网关路由配置和服务注册状态。十、总结本文围绕 Spring Cloud 微服务自动化构建与部署串起了 GitLab、Maven、Docker、Jenkins 和 Harbor 等核心组件形成了一条从代码提交到容器上线的完整 CI/CD 链路。文章先从环境准备和代码仓库搭建入手再到多模块项目改造、Docker 镜像构建与 Jenkins 流水线设计最后给出部署、回滚和排障方法。