服务云部署
一、Dockerfile 手动部署方式:
二、Dockerfile+Docker Compose 容器编排手动部署方式:
三、Dockerfile+GitLab CI/CD 自动化部署方式:
1、GitLab CI/CD简介:
GitLab CI/CD 是 GitLab 内置的一套持续集成(CI)、持续部署(CD) 工具链,核心是通过 .gitlab-ci.yml 定义流程,由 GitLab Runner 执行任务,实现从代码提交到部署上线的全流程自动化。
(1)、持续集成(CI,Continuous Integration):
- 开发者频繁将代码提交到仓库后,系统自动执行构建、编译、单元测试等操作,快速发现代码中的错误(如语法问题、测试失败),确保团队协作时代码始终可集成。
(2)、持续部署(CD,Continuous Deployment):
- 在 CI 环节通过后,自动将代码部署到测试环境、预生产环境甚至生产环境,实现 “代码提交后自动上线”。
2、Docker-GitLab Runner安装:
# 拉取镜像: docker pull gitlab/gitlab-runner:latest # 创建持久化目录: mkdir -p /data/gitlab-runner/config # 启动容器: docker run -d \ --name gitlab-runner \ -v /data/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest
3、GitLab Runner注册配置:
方式一、GitLab Runner 15.6版本前:
- (1)、获取registration-token:


- (2)、注册Runner:
sudo docker exec -it gitlab-runner gitlab-runner register \ --non-interactive \ --url "http://127.0.0.1:8929/" \ --registration-token "xxxxxx-xxxx" \ --tag-list "my-demo-runner" \ --run-untagged="true" \ --locked="false" \ --access-level="not_protected" \ --executor "docker" \ --docker-image "docker:latest" \ --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"
|
参数格式 |
核心作用 |
取值说明 / 可选值 |
|
--non-interactive |
非交互式注册 |
无取值(开关参数),执行命令时无需手动输入任何内容,所有配置由命令行参数指定 |
|
--url "http://127.0.0.1:8929/" |
指定 GitLab 实例的地址 |
取值:GitLab 服务器的访问 URL(HTTP/HTTPS 均可) |
|
--registration-token "xxxxxx-xxxx" |
用于 Runner 与 GitLab 服务器认证的令牌 |
取值:从 GitLab 项目 / 组 / 实例的 “设置→CI/CD→Runner” 中获取的注册令牌 注意:新版 GitLab 也可用--token替代该参数,效果一致 |
|
--tag-list "xxxx" |
给 Runner 打标签,用于 CI Job 指定 Runner 执行 |
取值:多个标签用逗号分隔(如"my-demo-runner"),无标签可留空; 作用:只有 CI Job 指定对应标签时,该 Runner 才会承接任务 |
|
--run-untagged="true" |
控制 Runner 是否承接 “未打标签” 的 CI Job |
可选值: • true(承接) • false(仅承接带匹配标签的 Job) 默认:false |
|
--locked="false" |
控制 Runner 是否被锁定(仅归属指定项目) |
可选值: • true(锁定,仅绑定的项目可用) • false(不锁定,可被其他项目使用) 默认:true |
|
--access-level="not_protected" |
定义 Runner 可执行的 Job 的保护级别 |
可选值: • not_protected(可执行非保护分支 / 标签的 Job) • ref_protected(仅执行保护分支 / 标签的 Job) 默认:not_protected |
|
--executor "docker" |
指定 Runner 的执行器类型 |
可选值:docker、shell、kubernetes、virtualbox等核心; 选docker表示用 Docker 容器运行 CI Job |
|
--docker-image "docker:latest" |
指定 Docker Executor 默认使用的基础镜像(内置完整的 Docker CLI) |
取值:任意有效的 Docker 镜像名(如alpine:latest、ubuntu:22.04、docker:27.0-alpine); 作用:CI Job 未指定image时,默认使用该镜像 |
|
--docker-volumes "/var/run/docker.sock:/var/run/docker.sock" |
给Docker Executor的Job容器挂载卷 |
取值:宿主机路径,容器内路径(格式与docker run -v一致)多个卷用逗号分隔(如"/var/run/docker.sock:/var/run/docker.sock","/cache:/cache") 核心:挂载docker.sock让 Job 容器能执行 Docker 命令 |
方式二、GitLab Runner 15.6版本及之后:
- (1)、创建Runner,获取身份令牌token:



- (2)、令牌相关信息:
# 查看你相关令牌信息 cat /data/gitlab-runner/config/config.toml # 移除相关令牌信息,移除后需重启 rm -f /data/gitlab-runner/config/config.toml
- (3)、注册Runner:
sudo docker exec -it gitlab-runner gitlab-runner register \ --non-interactive \ --url "http://127.0.0.1:8929/" \ --token "glrt-xxx.xx.xx" \ --executor "docker" \ --docker-image "docker:latest" \ --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"

注:
|
Token 类型 |
核心定义 |
适用场景 |
归属范围 |
跨项目复用性 |
|
项目级 Token |
仅绑定单个特定项目的注册凭证,用于注册「项目专属 Runner」 |
单个项目独立使用(如生产环境项目、专属测试项目) |
仅当前注册的目标项目 |
不可复用 |
|
群组级 Token |
绑定整个 GitLab 群组的注册凭证,用于注册「群组共享 Runner」 |
群组下所有项目共用(如开发 / 测试团队共享执行器) |
该群组及子群组下所有项目 |
可复用 |
|
实例级 Token |
绑定整个 GitLab 服务器(实例)的注册凭证,用于注册「全实例共享 Runner」 |
GitLab 实例内所有项目共用(如企业级通用执行器) |
GitLab 实例内所有项目(所有群组) |
可复用 |
4、Gitlab CI-CD自动化部署SpringBoot项目:


(1)、父子项目工程配置:
- 1)、父POM配置:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.iven</groupId> <artifactId>my_demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <!--聚合机制--> <modules> <module>new-project1</module> <module>new-project2</module> </modules> <!-- 统一管理依赖版本(子模块无需写version,直接继承) --> <dependencyManagement> <dependencies> <!-- Spring Boot核心依赖(JDK8兼容版本:2.7.x) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.7.15</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 所有子模块共用的依赖(无需在子模块重复引入) --> <dependencies> <!-- Spring Boot Web依赖(提供HTTP服务) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <!-- 统一配置打包插件(确保子模块打出可执行Jar) --> <build> <!-- pluginManagement:子模块可继承,不强制引入 --> <pluginManagement> <plugins> <!-- Spring Boot打包插件(打可执行Jar,包含依赖) --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.7.15</version> <executions> <execution> <goals> <!-- 关键:将普通Jar转为可执行Jar --> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <!-- Maven编译插件(指定JDK8) --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <!-- 源码JDK版本 --> <source>8</source> <!-- 编译后JDK版本 --> <target>8</target> <!-- 编码统一 --> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
- 2)、new-project1子模块1配置(new-project2子模块2 同理):
POM配置:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.iven</groupId> <artifactId>my_demo</artifactId> <version>0.0.1-SNAPSHOT</version> <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>new-project1</artifactId> <name>new-project1</name> <description>new-project1</description> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
YML配置:
server: port: 8080 spring: application: name: new-project1
(2)、Dockerfile镜像构建配置:
# ======================== 基础镜像选择 ======================== # 核心原则:选择轻量、安全、与项目运行环境匹配的基础镜像 # openjdk:8-jdk-alpine 对比说明: # - alpine版本:基于Alpine Linux(轻量级发行版,镜像体积约100MB左右) # - jdk-alpine:包含JDK编译环境(如需在容器内编译可保留,纯运行可换jre-alpine) # - 避免使用openjdk:8(debian完整版,体积超600MB),生产环境优先slim/alpine版本 FROM openjdk:8-jdk-alpine # ======================== 可选配置 ======================== # 维护者标签:标准化镜像归属,便于团队协作维护 # 格式建议:姓名/团队 <邮箱> # LABEL maintainer="dev-team@your-company.com" # ======================== 工作目录配置 ======================== # 1. 统一容器内工作目录,避免文件散落在根目录,提升可维护性 # 2. Docker会自动创建/app目录(无需手动mkdir) # 3. 后续所有相对路径命令(COPY/ENTRYPOINT等)均基于此目录执行 WORKDIR /app # ======================== 复制应用包 ======================== # 1. COPY语法:COPY <宿主机文件> <容器内目标路径> # 2. target/*.jar:匹配宿主机子模块打包后的所有jar包(CI/CD构建时已进入子模块目录) # 3. app.jar:统一重命名为固定名称,避免因版本号不同导致启动命令变更 # 注意:确保target目录下只有1个可执行jar包(避免覆盖或复制多个) COPY target/*.jar app.jar # ======================== 端口声明 ======================== # 1. EXPOSE仅为"文档化声明",告知使用者容器内应用监听的端口,不实际映射端口 # 2. 实际端口映射由docker run -p 或 docker-compose.yml的ports配置决定 # 3. 声明多个端口(如8080主端口、8081监控端口),提升镜像可读性 EXPOSE 8080 8081 # ======================== 容器启动命令 ======================== # 1. ENTRYPOINT使用exec格式(["命令", "参数1", "参数2"]),避免shell层封装导致信号传递问题 # 2. JVM参数优化: # -Xms512m:JVM初始堆内存(设置为与-Xmx相同,避免运行时动态扩容,提升性能) # -Xmx1024m:JVM最大堆内存(根据服务器资源和应用需求调整) # 3. -jar app.jar:执行容器内/app目录下的app.jar包 # 扩展:可添加更多JVM参数,如: # -XX:+UseG1GC(使用G1垃圾收集器)、-Dspring.profiles.active=prod(指定生产环境配置) ENTRYPOINT ["java", "-Xms512m", "-Xmx1024m", "-jar", "app.jar"]
(3)、.gitlab-ci.yml-CI/CD流水线配置:
- 1)、流水线实现效果:
在test分支时,所有子模块会被打包、构建测试环境镜像,并在Docker容器中自动部署,适合进行自动化测试。
在main分支时,只有通过手动Tag触发时才会进行生产镜像的构建,并可以选择推送到私有仓库中供后续docker-compose手动部署。
- 2)、.gitlab-ci.yml配置:
# ============================================================================== # GitLab CI/CD 配置文件 (.gitlab-ci.yml) # 配置核心说明: # 1. 适配场景:多模块Spring Boot项目(JDK8)的自动化构建、镜像打包、环境部署 # 2. 分支策略: # - test分支:代码提交自动触发「打包→构建测试镜像→部署测试容器」全流程 # - main分支:仅Tag提交触发「生产打包→构建带版本的生产镜像」(避免误触发生产流程) # 3. 扩展能力:支持私有Docker仓库推送、手动清理Runner残留资源 # ============================================================================== # ==================== 【核心配置区】只需修改这里即可适配你的项目 ==================== # 基础配置 CONFIG_RUNNER_TAG: "my-demo-runner" # GitLab Runner标签 CONFIG_TEST_BRANCH: "test" # 测试分支名称 CONFIG_PROD_BRANCH: "main" # 生产分支名称 # 镜像版本 CONFIG_MAVEN_IMAGE: "maven:3.6-openjdk-8" # Maven构建镜像 CONFIG_DOCKER_IMAGE: "docker:24.0.2" # Docker构建镜像 # 项目模块 CONFIG_SERVICES: "new-project1,new-project2" # 子模块列表(逗号分隔) # 测试端口 CONFIG_SERVER_PORT_new_project1: "8080" CONFIG_SERVER_PORT_new_project2: "8081" # 生产镜像 CONFIG_PROD_IMAGE_PREFIX: "my-prod" # 生产镜像前缀 # CONFIG_PRIVATE_REGISTRY: "127.0.0.1:5000" # 私有仓库地址(启用需取消注释) # CONFIG_REGISTRY_USER: "registry-user" # 私有仓库用户名 # CONFIG_REGISTRY_PWD: "registry-password" # 私有仓库密码 # 产物有效期 CONFIG_TEST_ARTIFACTS_EXPIRE: "1 hour" # 测试产物过期时间 CONFIG_PROD_ARTIFACTS_EXPIRE: "1 week" # 生产产物过期时间 # ==================== 全局变量配置(基于核心配置区自动映射,无需修改) ==================== variables: # 1. Maven构建镜像: # - 选择maven:3.6-openjdk-8,与项目JDK8版本严格匹配,保证编译兼容性 # - 避免使用高版本Maven/JDK,防止编译产物不兼容 MAVEN_IMAGE: "${CONFIG_MAVEN_IMAGE}" # 2. Docker构建镜像: # - 选用docker:24.0.2稳定版,避免最新版API变更导致构建失败 # - 所有Docker相关作业统一使用该版本,保证环境一致性 DOCKER_IMAGE: "${CONFIG_DOCKER_IMAGE}" # 3. 子模块列表: # - 与项目根目录下的子模块目录名完全一致(逗号分隔,无空格) # - 后续循环处理所有子模块,无需为每个模块单独写作业 SERVICES: "${CONFIG_SERVICES}" # 4. 测试环境端口配置: # - 命名规则:SERVER_PORT_<子模块名(连字符换下划线)> # - 端口值需与子模块application.yml中server.port一致,避免端口映射错误 SERVER_PORT_new_project1: "${CONFIG_SERVER_PORT_new_project1}" SERVER_PORT_new_project2: "${CONFIG_SERVER_PORT_new_project2}" # 5. 生产镜像命名前缀: # - 统一命名规范:前缀/子模块名:版本号,便于镜像仓库管理和版本追溯 # - 示例:my-prod/new-project1:v1.0.0 PROD_IMAGE_PREFIX: "${CONFIG_PROD_IMAGE_PREFIX}" # 6. 私有Docker仓库配置(按需启用): # - 替换为实际的仓库地址、用户名、密码 # - 生产镜像构建完成后可自动推送到私有仓库 #PRIVATE_REGISTRY: "${CONFIG_PRIVATE_REGISTRY}" #REGISTRY_USER: "${CONFIG_REGISTRY_USER}" #REGISTRY_PWD: "${CONFIG_REGISTRY_PWD}" # ==================== 构建阶段定义(按顺序执行) ==================== stages: - test-build # test分支专属:源码编译打包(生成Jar包) - test-image # test分支专属:基于Jar包构建测试镜像 - test-deploy # test分支专属:启动测试容器(映射端口,对外提供服务) - prod-build # main分支专属:生产环境打包(仅Tag触发) - prod-image # main分支专属:构建带版本号的生产镜像(仅Tag触发) # ==================== test分支自动化流程(代码提交自动触发) ==================== # 作业1:test分支 - 统一打包所有子模块 test-build-all: stage: test-build # 绑定到test-build阶段 image: $MAVEN_IMAGE # 使用全局变量定义的Maven镜像 script: - echo "==== 开始执行test分支Maven打包 ====" - java -version # 校验JDK版本(必做,避免镜像版本不符) - mvn -v # 校验Maven版本(必做,避免编译命令不兼容) # Maven核心打包命令: # - clean:清理之前构建的旧产物(target目录) # - package:编译源码并打包为Jar包 # -DskipTests:跳过单元测试(加速构建,测试可单独做) # -B:批处理模式(非交互,适配CI/CD环境) - mvn clean package -DskipTests -B - echo "==== Maven打包完成,验证产物 ====" # 校验打包结果:查找所有target目录下的Jar包,确认打包成功 - find . -name "*.jar" -type f | grep "target" artifacts: paths: - "*/target/*.jar" # 保留所有子模块的Jar包(作为后续作业的依赖产物) expire_in: ${CONFIG_TEST_ARTIFACTS_EXPIRE} # 产物1小时后过期(节省GitLab存储资源,测试产物无需长期保留) only: - ${CONFIG_TEST_BRANCH} # 触发规则:仅test分支代码提交时执行(新增/修改/合并代码) tags: - ${CONFIG_RUNNER_TAG} # 绑定指定的GitLab Runner(确保使用正确的构建节点) # 作业2:test分支 - 为所有子模块构建测试镜像 test-image-all: stage: test-image # 绑定到test-image阶段 image: name: ${DOCKER_IMAGE} # 使用指定版本的Docker镜像 entrypoint: [""] # 禁用Docker镜像默认的entrypoint(避免与脚本冲突) variables: # 关键配置:让容器内的Docker命令调用宿主机的Docker守护进程 # 需提前配置Runner挂载/var/run/docker.sock DOCKER_HOST: "unix:///var/run/docker.sock" dependencies: - test-build-all # 依赖test-build-all作业的产物(Jar包),确保先打包再构建镜像 before_script: - docker --version # 校验Docker环境是否可用(提前发现环境问题) script: - | # 多行脚本开始标记(兼容shell语法) echo "==== 开始构建测试环境Docker镜像 ====" # 循环处理所有子模块:将逗号分隔的SERVICES转为换行,逐个读取 echo "$SERVICES" | tr ',' '\n' | while read service; do echo "==== 处理子模块:$service ====" # 前置校验:子模块目录是否存在(避免构建不存在的模块) if [ ! -d "$service" ]; then echo "错误:子模块目录 $service 不存在,请检查SERVICES配置" exit 1 # 退出并标记作业失败 fi # 进入子模块目录(Dockerfile读取当前目录的target/*.jar) cd "$service" # 构建测试镜像: # -t "$service:test":镜像标签(子模块名:test),便于识别测试镜像 # -f ../Dockerfile:指定项目根目录的Dockerfile(所有子模块共用) # .:构建上下文为当前子模块目录(确保COPY能找到target/*.jar) docker build -t "$service:test" -f ../Dockerfile . # 退回项目根目录(准备处理下一个模块) cd .. echo "==== 子模块 $service 测试镜像构建完成 ====" done only: - ${CONFIG_TEST_BRANCH} # 仅test分支触发 tags: - ${CONFIG_RUNNER_TAG} # 作业3:test分支 - 部署所有子模块的测试容器 test-deploy-all: stage: test-deploy # 绑定到test-deploy阶段 image: name: ${DOCKER_IMAGE} entrypoint: [""] # 禁用默认entrypoint variables: DOCKER_HOST: "unix:///var/run/docker.sock" # 调用宿主机Docker before_script: - docker ps # 校验Docker服务是否正常运行(提前发现服务异常) script: - | echo "==== 开始部署测试环境容器 ====" # 循环处理所有子模块 echo "$SERVICES" | tr ',' '\n' | while read service; do echo "==== 部署子模块:$service ====" # 动态获取端口: # 1. 将子模块名中的连字符(-)替换为下划线(_),匹配全局变量名 # 示例:new-project1 → SERVER_PORT_new_project1 var_name="SERVER_PORT_$(echo "$service" | tr '-' '_')" # 2. 读取变量值(兼容简单shell环境) eval "port=\${$var_name}" # 端口校验:确保子模块配置了对应的测试端口 if [ -z "$port" ]; then echo "错误:子模块 $service 未配置测试端口(请检查SERVER_PORT_*变量)" exit 1 fi # 清理旧容器:避免端口占用、容器名冲突 if docker ps -a | grep -q "${service}-test"; then docker stop "${service}-test" || true # 停止容器(失败不终止) docker rm "${service}-test" || true # 删除容器(失败不终止) fi # 启动新容器: # -d:后台运行(守护进程模式) # -p "${port}:${port}":端口映射(宿主机端口:容器内端口) # --name "${service}-test":容器名(子模块名-test),便于识别 # "$service:test":使用测试镜像启动 docker run -d -p "${port}:${port}" --name "${service}-test" "$service:test" echo "==== 子模块 $service 部署完成,访问地址:http://Runner服务器IP:$port ====" done only: - ${CONFIG_TEST_BRANCH} # 仅test分支触发 tags: - ${CONFIG_RUNNER_TAG} # ==================== main分支自动化流程(仅Tag提交触发) ==================== # 作业1:main分支 - 生产环境打包(仅Tag触发) prod-build-all: stage: prod-build # 绑定到prod-build阶段 image: $MAVEN_IMAGE # 使用全局Maven镜像 script: - | echo "==== 开始生产环境Maven打包 ====" echo "触发Tag:$CI_COMMIT_TAG" # 输出触发的Tag版本(便于日志追溯) # 生产打包命令(与测试打包逻辑一致,保证产物一致性) mvn clean package -DskipTests -B echo "==== 生产环境打包完成 ====" # 打印产物路径,确认文件存在(便于排查) ls -l new-project1/target/ ls -l new-project2/target/ artifacts: paths: - "*/target/*.jar" # 保留生产Jar包(用于构建生产镜像) expire_in: ${CONFIG_PROD_ARTIFACTS_EXPIRE} # 生产产物保留1周(便于问题排查) rules: # 触发规则:仅当提交为Tag时执行(避免main分支普通提交触发生产流程) - if: '$CI_COMMIT_TAG' when: always tags: - ${CONFIG_RUNNER_TAG} # 作业2:main分支 - 构建生产镜像(仅Tag触发) prod-image-all: stage: prod-image # 绑定到prod-image阶段 image: name: ${DOCKER_IMAGE} entrypoint: [""] # 禁用默认entrypoint variables: DOCKER_HOST: "unix:///var/run/docker.sock" # 调用宿主机Docker dependencies: - prod-build-all # 依赖生产打包阶段的Jar包产物 script: - | echo "==== 开始构建生产环境Docker镜像 ====" # 前置校验:确保触发源是Tag(防止规则配置错误) if [ -z "$CI_COMMIT_TAG" ]; then echo "错误:生产镜像仅支持Tag触发" exit 1 fi # 循环处理所有子模块 echo "$SERVICES" | tr ',' '\n' | while read service; do echo "==== 处理子模块:$service ====" # 校验子模块目录是否存在 if [ ! -d "$service" ]; then echo "错误:子模块目录 $service 不存在" exit 1 fi # 进入子模块目录 cd "$service" # 生产镜像命名规则:前缀/子模块名:Tag版本(符合生产镜像规范) image_name="${PROD_IMAGE_PREFIX}/${service}:${CI_COMMIT_TAG}" # 构建生产镜像(使用根目录Dockerfile) docker build -t "$image_name" -f ../Dockerfile . # 退回根目录 cd .. echo "==== 子模块 $service 生产镜像构建完成(标签:$image_name) ====" # 可选:推送镜像到私有仓库(需配置仓库信息) # if [ -n "$PRIVATE_REGISTRY" ] && [ -n "$REGISTRY_USER" ] && [ -n "$REGISTRY_PWD" ]; then # docker login -u "$REGISTRY_USER" -p "$REGISTRY_PWD" "$PRIVATE_REGISTRY" # 登录仓库 # docker tag "$image_name" "$PRIVATE_REGISTRY/$image_name" # 打仓库标签 # docker push "$PRIVATE_REGISTRY/$image_name" # 推送镜像 # docker logout "$PRIVATE_REGISTRY" # 退出登录(安全) # echo "==== 子模块 $service 镜像推送完成 ====" # else # echo "==== 跳过私有仓库推送(未配置完整信息) ====" # fi done rules: - if: '$CI_COMMIT_TAG' # 仅Tag提交触发 when: always tags: - ${CONFIG_RUNNER_TAG} # ==================== 辅助作业:手动清理Docker残留资源 ==================== cleanup: stage: .pre # GitLab内置前置阶段(所有自定义阶段前执行,不影响主流程) image: name: ${DOCKER_IMAGE} entrypoint: [""] variables: DOCKER_HOST: "unix:///var/run/docker.sock" script: - | echo "==== 开始清理Runner服务器Docker资源 ====" # 清理所有已停止的容器(-f:强制确认,无需交互) docker container prune -f # 清理所有无用镜像(-a:所有未使用,-f:强制) docker image prune -af # 针对性清理测试容器(避免残留容器占用端口) echo "$SERVICES" | tr ',' '\n' | while read service; do container_name="${service}-test" if docker ps -a | grep -q "$container_name"; then echo "清理残留测试容器:$container_name" docker stop "$container_name" || true docker rm "$container_name" || true fi done echo "==== 资源清理完成 ====" # 输出Docker资源使用情况(确认清理效果) docker system df when: manual # 触发规则:仅手动点击触发(避免自动清理导致数据丢失) tags: - ${CONFIG_RUNNER_TAG}
- 3)、.gitlab-ci.yml常用关键字说明:
|
关键字 |
作用说明 |
示例用法 |
|
variables |
定义全局变量,所有阶段可复用,集中管理配置(如镜像名、端口、仓库地址) |
MAVEN_IMAGE: "maven:3.6-openjdk-8" |
|
stages |
定义构建阶段,按顺序执行,相同阶段的任务并行执行 |
stages: - test-build - test-image - test-deploy |
|
stage |
绑定任务到指定阶段(必须在stages中定义) |
stage: test-build |
|
image |
指定执行该任务的 Docker 镜像(如 Maven、Docker 镜像) |
image: $DOCKER_IMAGE |
|
script |
任务的核心执行命令(如打包、构建镜像、部署命令) |
mvn clean package -DskipTests |
|
only |
限定任务触发条件(如分支、Tag) |
only: - test 或 only: refs: - tags |
|
except |
排除任务触发条件(与only相反) |
except: - branches |
|
tags |
匹配 GitLab Runner 的标签(仅带有该标签的 Runner 会执行任务) |
tags: - docker |
|
dependencies |
定义任务依赖(依赖的任务执行完成后,当前任务才会执行) |
dependencies: - test-build-all |
|
cache |
缓存依赖(如 Maven 依赖),加速后续构建(当前简化未用,可选添加) |
cache: paths: - /root/.m2/repository/ |
(4)、相关核心执行流程:
- 1)、测试环境:自动化部署
开发完成后,将代码推送到test分支,GitLab 自动触发 CI/CD 流水线。



- 2)、生产环境:手动部署
测试通过后,合并test分支代码到main分支,手动创建 Tag 标签并推送到远程仓库,触发生产镜像构建




- 2.1)、相关服务器操作:
docker-compose.yml容器编排配置:
# Docker Compose版本:3.8(兼容Docker 24.0.2,功能足够满足需求) version: '3.8' # 定义生产环境所有服务 services: # 微服务1:new-project1(与子模块名、镜像名一致) new-project1: # 镜像名:与CI/CD构建的生产镜像名完全一致(修改Tag即可升级/回滚) # 示例:my-prod/new-project1:v1.0.0(Tag为手动推送的Git Tag) image: my-prod/new-project1:v1.0.0 container_name: new-project1-prod # 容器名:子模块名-prod(区分测试环境容器) ports: - "8080:8080" # 端口映射(主机端口:容器端口,与application.yml一致) restart: always # 容器异常退出时自动重启(提高生产环境可用性) volumes: # 挂载日志目录:将容器内的/app/logs目录映射到服务器本地的logs/new-project1目录 # 进入docker-compose.yml 所在的目录下,创建持久化日志目录: mkdir -p logs/new-project1 logs/new-project2 # 作用:日志持久化,避免容器重启后日志丢失,方便排查问题 - ./logs/new-project1:/app/logs # 可选:挂载外部配置文件(如需动态修改生产环境配置,无需重新构建镜像) # - ./config/new-project1:/app/config # environment: # 可选:指定生产环境配置文件(如application-prod.yml) # - SPRING_PROFILES_ACTIVE=prod # 微服务2:new-project2 new-project2: image: my-prod/new-project2:v1.0.0 container_name: new-project2-prod ports: - "8081:8081" restart: always volumes: - ./logs/new-project2:/app/logs # - ./config/new-project2:/app/config # environment: # - SPRING_PROFILES_ACTIVE=prod
- 2.2)、手动创建持久化日志目录:
# 进入docker-compose.yml所在目录 # 当前目录下,创建日志目录 mkdir -p logs/new-project1 logs/new-project2
- 2.3)、部署与回滚操作:
# 部署与回滚均修改docker-compose.yml中的镜像Tag对应版本(如v1.0.0) docker-compose up -d
四、Dockerfile+Jenkins自动化部署方式:
1、Jenkins简介:
Jenkins是一款开源的自动化 CI/CD 服务器,核心作用是实现代码从提交到部署的全流程自动化,涵盖自动拉取代码、编译构建、测试验证、打包制品乃至部署上线等环节,它拥有丰富的插件生态,能无缝集成 Git、Maven、Docker 等主流开发运维工具,支持多语言项目和跨平台部署,架构上采用 Master-Agent 分布式模式,可灵活扩展任务执行能力,是 DevOps 体系中实现持续集成与持续交付的核心工具。
2、DevOps环境下JDK+Maven安装:
- (1)、相关目录创建:
sudo mkdir -p /opt/install sudo mkdir -p /opt/download/jdk sudo mkdir -p /opt/download/maven
- (2)、JDK安装:
# 下载JDK到指定目录 sudo wget --no-check-certificate -P /opt/download/jdk https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/jdk-8u341-linux-x64.tar.gz # 解压到/opt/install(解压后目录为jdk1.8.0_341) sudo tar -zxvf /opt/download/jdk/jdk-8u341-linux-x64.tar.gz -C /opt/install # 重命名解压后的目录 sudo mv /opt/install/jdk1.8.0_341 /opt/install/jdk # 配置 JDK 环境变量 # 1、编辑当前用户专属的环境配置文件 vim ~/.bashrc # 2、在profile中添加以下内容(复制到文件末尾) export JAVA_HOME=/opt/install/jdk export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH # 3、配置生效 source ~/.bashrc # 4、验证 java -version
(3)、Maven安装:
# 下载Maven到指定目录 sudo wget --no-check-certificate -P /opt/download/maven https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/apache-maven-3.8.6-bin.tar.gz # 解压到/opt/install(解压后目录为apache-maven-3.8.6) sudo tar -zxvf /opt/download/maven/apache-maven-3.8.6-bin.tar.gz -C /opt/install # 重命名解压后的目录 sudo mv /opt/install/apache-maven-3.8.6 /opt/install/maven # 切换阿里云的settings.xml配置: sudo rm -f /opt/install/maven/conf/settings.xml && sudo wget --no-check-certificate -O /opt/install/maven/conf/settings.xml https://manongbiji.oss-cn-beijing.aliyuncs.com/ittailkshow/devops/download/settings.xml # 配置 Maven环境变量 # 1、编辑当前用户专属的环境配置文件 vim ~/.bashrc # 2、在profile中添加以下内容(复制到文件末尾) export MAVEN_HOME=/opt/install/maven export PATH=${MAVEN_HOME}/bin:$PATH # 3、配置生效 source ~/.bashrc # 4、验证 mvn -v

3、Docker-Jenkins安装:
# 拉取稳定版Jenkins镜像 sudo docker pull jenkins/jenkins:lts # 创建宿主机Jenkins数据目录 sudo mkdir -p /data/jenkins-data # 配置权限 sudo chmod 777 /data/jenkins-data # 创建宿主机部署根目录 sudo mkdir -p /data/module-data # 配置权限 sudo chmod 777 /data/module-data # 启动容器 sudo docker run -d \ --name jenkins \ -p 8930:8080 \ -p 50000:50000 \ -v /data/jenkins-data:/var/jenkins_home \ -v /data/module-data:/data/module-data \ -v /opt/install/jdk:/opt/install/jdk \ -v /opt/install/maven:/opt/install/maven \ -u root \ jenkins/jenkins:lts
|
参数 |
作用说明 |
必要性 |
|
-p 8930:8080 |
宿主机 8930 端口映射到容器内 8080 端口 (Jenkins Web 访问端口) |
必选 |
|
-p 50000:50000 |
宿主机 50000 端口映射到容器内 50000 端口 (Jenkins 代理 / 节点通信端口) |
可选 |
|
-v /data/jenkins-data:/var/jenkins_home |
将宿主机/data/jenkins-data目录挂载到容器内/var/jenkins_home (Jenkins 核心数据目录) |
必选 |
|
-v /data/module-data:/data/module-data |
将宿主机部署目录挂载到容器内同路径,供 Jenkins 进行部署 |
可选 |
|
-v /opt/install/jdk:/opt/install/jdk |
将宿主机 JDK 目录挂载到容器内同路径,供 Jenkins 调用 JDK 环境 |
可选 |
|
-v /opt/install/maven:/opt/install/maven |
将宿主机 Maven 目录挂载到容器内同路径,供 Jenkins 调用 Maven 环境 |
可选 |
|
-u root |
以 root 用户身份运行 Jenkins 容器 |
必选 |
|
jenkins/jenkins:lts |
指定启动容器使用的镜像(Jenkins 长期支持版,稳定) |
必选 |
4、Jenkins配置初始化:
# 访问 Jenkins地址,输入初始密码 http://127.0.0.1:8930 # 获取初始密码 # 通过日志方式:sudo docker logs jenkins sudo cat /data/jenkins-data/secrets/initialAdminPassword
- (1)、输入管理员密码:

- (2)、选择安装推荐的插件方式:


- (3)、安装完成后,创建管理员账号:

- (4)、进行实例配置,配置Jenkins URL:

- (5)、自定义插件安装:
系统管理 → 插件管理
|
用途 |
插件 |
|
角色权限管控插件 |
Role-based Authorization Strategy |
|
拉取源代码 |
Git Parameter |
|
通过SSH协议发布到远程服务器 |
Publish Over SSH |
|
用于创建和管理流水线的核心插件 |
Pipeline |



- (6)、全局配置-初始化JDK和Maven:
系统管理 → 全局工具配置



- (7)、系统配置- 配置Publish Over SSH远程服务器:
系统管理 → 系统配置
# 进入Jenkins容器 docker exec -it jenkins bash # 清空旧密钥和指纹-彻底重置 rm -rf ~/.ssh/id_rsa* && > ~/.ssh/known_hosts # 生成新SSH密钥-无密码短语 ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" # 信任宿主机IP指纹-(替换<宿主机IP>) ssh-keyscan -H 127.0.0.1 >> ~/.ssh/known_hosts # 拷贝公钥到宿主机xxx用户-输入密码(替换<宿主机用户与IP>) ssh-copy-id xxx@127.0.0.1 # 复制Jenkins私钥 cat ~/.ssh/id_rsa # 验证 ssh xxx@127.0.0.1 "echo ' SSH免密登录成功!'" # 退出容器 exit




|
「系统管理」→「系统配置」→「Publish over SSH」 |
|
|
参数 |
说明 |
|
Name |
自定义名称 |
|
Hostname |
宿主机 IP |
|
Username |
宿主机登录用户 |
|
Remote Directory |
留空 |
|
高级 |
勾选「Use password authentication or use a different key」 |
|
Key |
私钥 |
5、Jenkins Pipeline + Dockerfile实现通用化 CI/CD流水线:
(1)、整体流程总览:
Jenkins 构建触发 → 拉取 Git 代码 → Maven 编译生成 Jar → 传输 Jar/Dockerfile 到宿主机 → 宿主机创建部署/日志目录 → Docker 构建镜像 → 停止旧容器 → 启动新容器(挂载日志+映射端口)→ 外部通过配置端口访问应用
(2)、Spring boot父子工程项目构建:


- 1)、Dockerfile配置:
# 基础镜像:固定JDK8版本,避免依赖漂移 FROM openjdk:8-jdk-alpine # 构建参数:模块名、端口(由Docker build传入) ARG MODULE_NAME ARG PORT # 工作目录 WORKDIR /app # 创建容器内日志目录 RUN mkdir -p /app/log # 复制Jar包到容器 COPY ${MODULE_NAME}-0.0.1-SNAPSHOT.jar app.jar # 暴露应用端口 EXPOSE ${PORT} # 启动命令:指定日志输出路径,启用UTF-8编码 ENTRYPOINT ["java", "-Dfile.encoding=UTF-8", "-Dlogging.file.name=/app/log/app.log", "-jar", "app.jar"]
- 2)、Jenkinsfile流水线(Jenkins-Pipeline)配置:
/* * 通用化 Spring Boot CI/CD 流水线 * 功能:Maven编译 + Docker构建 + 宿主机部署(日志持久化) */ pipeline { // 执行节点(使用任意可用节点) agent any environment { // ===================== 核心配置区(仅需修改以下参数)===================== // 宿主机IP HOST_IP = "127.0.0.1" // 宿主机登录用户 HOST_USER = "xxx" // 宿主机部署根目录 HOST_BASE_DEPLOY_DIR = "/data/module-data" // 父工程名 PARENT_PROJECT_NAME = "jenkins-demo" // 模块配置(模块名:端口,多模块用逗号分隔) // MODULES = "new-project1:8080,new-project2:8081,xxxx:xxxx" MODULES = "new-project1:8080" // ====================================================================== } // 构建工具(引用Jenkins全局配置的JDK和Maven) tools { jdk 'jdk' maven 'maven' } stages { /* * 编译+部署阶段 * 调用外部deploy.sh脚本完成编译、构建、部署全流程 */ stage('编译与部署') { steps { // 给部署脚本添加执行权限 sh "chmod +x ./docker/deploy.sh" // 执行部署脚本(传递核心配置参数) sh "./docker/deploy.sh ${MODULES} ${HOST_IP} ${HOST_USER} ${HOST_BASE_DEPLOY_DIR} ${PARENT_PROJECT_NAME}" } } } /* * 构建后置操作 */ post { success { echo "构建号:${currentBuild.number}" echo "构建部署成功,应用访问地址与日志路径:" sh """ echo "${MODULES}" | tr ',' '\\n' | while read MODULE; do if [ -z "\${MODULE}" ]; then continue; fi MODULE_NAME=\$(echo \${MODULE} | cut -d':' -f1) MODULE_PORT=\$(echo \${MODULE} | cut -d':' -f2) echo " - \${MODULE_NAME}:http://${HOST_IP}:\${MODULE_PORT}" echo " - 日志路径:${HOST_BASE_DEPLOY_DIR}/${PARENT_PROJECT_NAME}/\${MODULE_NAME}/log/app.log" done """ echo "宿主机执行 docker images | grep ${PARENT_PROJECT_NAME} 可查看构建的镜像" } failure { echo "构建号:${currentBuild.number}" echo "构建部署失败,排查步骤:" echo "1. 登录宿主机:ssh ${HOST_USER}@${HOST_IP}" echo "2. 查看容器日志:docker logs 模块名-app(如new-project1-app)" echo "3. 检查端口占用:netstat -tulpn | grep 模块端口(如8080)" echo "4. 检查部署目录:cd ${HOST_BASE_DEPLOY_DIR}/${PARENT_PROJECT_NAME}/模块名" } } }
- 3)、deploy.sh部署脚本:
#!/bin/bash # 通用化 Spring Boot 部署脚本 # 接收参数: # $1: MODULES(模块配置,格式:模块名:端口,多模块用逗号分隔) # $2: HOST_IP(宿主机IP) # $3: HOST_USER(宿主机登录用户) # $4: HOST_BASE_DEPLOY_DIR(宿主机部署根目录) # $5: PARENT_PROJECT_NAME(父工程名) # 定义变量(接收参数) MODULES=$1 HOST_IP=$2 HOST_USER=$3 HOST_BASE_DEPLOY_DIR=$4 PARENT_PROJECT_NAME=$5 # 步骤1:Maven编译(固定跳过测试) echo "开始编译父工程:${PARENT_PROJECT_NAME}" MVN_CMD="mvn clean package -DskipTests" echo "执行Maven命令:${MVN_CMD}" ${MVN_CMD} || { echo "Maven编译失败,终止部署"; exit 1; } # 步骤2:解析模块列表并部署每个模块 echo "开始部署模块到宿主机:${HOST_IP}" echo "${MODULES}" | tr ',' '\n' > /tmp/modules.tmp # 遍历模块部署 while read MODULE; do # 跳过空行 [ -z "${MODULE}" ] && continue # 拆分模块配置(模块名:端口) MODULE_NAME=$(echo ${MODULE} | cut -d':' -f1) MODULE_PORT=$(echo ${MODULE} | cut -d':' -f2) # 定义部署相关路径 DEPLOY_DIR="${HOST_BASE_DEPLOY_DIR}/${PARENT_PROJECT_NAME}/${MODULE_NAME}" LOG_DIR="${DEPLOY_DIR}/log" IMAGE_NAME="${PARENT_PROJECT_NAME}-${MODULE_NAME}:latest" # 建议:若需区分版本,可手动给镜像加标签,如 IMAGE_NAME="${PARENT_PROJECT_NAME}-${MODULE_NAME}:v1.0.0" CONTAINER_NAME="${MODULE_NAME}-app" JAR_FILE="${MODULE_NAME}/target/${MODULE_NAME}-0.0.1-SNAPSHOT.jar" # 验证Jar包是否存在 if [ ! -f "${JAR_FILE}" ] || [ ! -s "${JAR_FILE}" ]; then echo "模块${MODULE_NAME}编译失败:Jar包不存在或为空(路径:${JAR_FILE})" rm -f /tmp/modules.tmp exit 1 fi echo "模块${MODULE_NAME}编译成功,Jar包路径:${JAR_FILE}" # 步骤3:宿主机操作(创建目录、上传文件、构建镜像、启动容器) echo "开始部署模块:${MODULE_NAME}" # 3.1 创建宿主机部署目录和日志目录 ssh -o StrictHostKeyChecking=no ${HOST_USER}@${HOST_IP} "mkdir -p ${DEPLOY_DIR} ${LOG_DIR} && chmod 775 ${LOG_DIR} && chown -R iven:iven ${LOG_DIR}" # 3.2 上传Dockerfile和Jar包到宿主机 scp -o StrictHostKeyChecking=no ./docker/Dockerfile ${HOST_USER}@${HOST_IP}:${DEPLOY_DIR}/ scp -o StrictHostKeyChecking=no ${JAR_FILE} ${HOST_USER}@${HOST_IP}:${DEPLOY_DIR}/ # 3.3 构建Docker镜像并启动容器(挂载日志目录) ssh -o StrictHostKeyChecking=no ${HOST_USER}@${HOST_IP} " set -e cd ${DEPLOY_DIR} # 构建Docker镜像 echo '构建Docker镜像:${IMAGE_NAME}' docker build -t ${IMAGE_NAME} --build-arg MODULE_NAME=${MODULE_NAME} --build-arg PORT=${MODULE_PORT} . # 停止并删除旧容器(避免端口占用) echo '停止旧容器:${CONTAINER_NAME}' docker stop ${CONTAINER_NAME} 2>/dev/null || true docker rm ${CONTAINER_NAME} 2>/dev/null || true # 启动新容器(设置重启策略+挂载日志目录) echo '启动新容器:${CONTAINER_NAME}' docker run -d \ --name ${CONTAINER_NAME} \ --restart=on-failure:3 \ -p ${MODULE_PORT}:${MODULE_PORT} \ -v ${LOG_DIR}:/app/log \ ${IMAGE_NAME} # 等待应用启动并验证状态 sleep 3 if docker ps --filter 'name=${CONTAINER_NAME}' --filter 'status=running' | grep -q ${CONTAINER_NAME}; then echo '模块${MODULE_NAME}部署成功' else echo '模块${MODULE_NAME}部署失败,容器日志:' docker logs ${CONTAINER_NAME} 2>/dev/null exit 1 fi # ===================== 第三方镜像仓库推送(按需开启)===================== # 说明:如需将镜像推送到Harbor/Docker Hub等仓库,取消以下注释并修改配置 # 前置操作:宿主机需先登录镜像仓库(docker login 仓库地址 -u 用户名 -p 密码) # 示例(Harbor): # REPOSITORY_URL=\"harbor.example.com/library\" # 镜像仓库地址 # IMAGE_TAGGED=\"\${REPOSITORY_URL}/\${IMAGE_NAME}\" # docker tag \${IMAGE_NAME} \${IMAGE_TAGGED} # 给镜像打仓库标签 # docker push \${IMAGE_TAGGED} # 推送镜像到仓库 # echo '镜像${IMAGE_TAGGED}已推送到第三方仓库' # ====================================================================== " echo "模块${MODULE_NAME}部署完成" done < /tmp/modules.tmp # 清理临时文件 rm -f /tmp/modules.tmp echo "所有模块部署完成,最终部署目录:${HOST_BASE_DEPLOY_DIR}/${PARENT_PROJECT_NAME}" echo "宿主机执行 docker images | grep ${PARENT_PROJECT_NAME} 可查看所有构建的镜像,手动记录版本便于回退"
(2)、Jenkins创建流水线项目:
新建任务 → 流水线
|
「流水线」→「Pipeline script from SCM」→「SCM」→「Git」 |
|
|
参数 |
说明 |
|
Repository URL |
Git项目地址 |
|
凭证 |
选择「用户名 + 密码」 |
|
指定分支 |
*/main(根据实际分支调整,如 */dev) |
|
脚本路径 |
docker/Jenkinsfile(指定 Git 项目中的 Jenkinsfile 路径) |
|
其余配置保持默认 → 点击「保存」 |
|




(3)、执行Jenkins构建:
- 1)、进入流水线项目,点击「立即构建」:

- 2)、构建成功后:


|
访问应用 |
http://宿主机IP:8080(new-project1) |
|
查看日志 |
/data/module-data/jenkins-demo/new-project1/log/app.log |
|
查看镜像 |
宿主机执行 docker images | grep jenkins-demo 即可看到构建的镜像 |
|
查看当前目录结构 |
find . -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g' |
6、学习参考:
(1)、DevOps-文章目录:
(2)、Docker+Jenkins+Git实现企业持续集成持续部署(CI/CD):
浙公网安备 33010602011771号