在微服务架构下,手动构建和部署已成为交付效率的最大瓶颈。GitLab CI/CD凭借与代码仓库的深度集成、声明式的YAML配置和强大的Runner机制,是目前私有化部署场景下最流行的CI/CD方案。本文将从Runner部署、流水线设计、多环境部署到安全加固,给出一套Spring Boot+Docker技术栈的完整CI/CD工程化方案。
一、GitLab Runner部署与调度策略
Runner是执行CI/CD任务的引擎,部署方式直接影响构建性能和资源利用率:
# 注册Shared Runner(所有项目可用)
gitlab-runner register \
--url https://gitlab.example.com \
--registration-token $RUNNER_TOKEN \
--executor docker \
--docker-image maven:3.9-eclipse-temurin-21 \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock \
--docker-volumes /cache/maven:/root/.m2/repository \
--tag-list "docker,maven,jdk21" \
--run-untagged=false
# 注册Group Runner(特定组可用,推荐)
gitlab-runner register \
--url https://gitlab.example.com \
--registration-token $GROUP_RUNNER_TOKEN \
--executor kubernetes \
--kubernetes-namespace gitlab-runner
Runner调度优化配置(/etc/gitlab-runner/config.toml):
[[runners]]
name = "docker-builder"
executor = "docker"
concurrency = 4 # 并发任务数
request_concurrency = 2 # 向GitLab请求任务的并发数
[runners.docker]
pull_policy = ["if-not-present"] # 优先使用本地镜像
image = "maven:3.9-eclipse-temurin-21"
memory = "8g"
cpu_count = 4
[runners.cache]
Type = "s3"
Shared = true
[runners.cache.s3]
ServerAddress = "minio.example.com"
BucketName = "gitlab-runner-cache"
AccessKey = "AKIAXXXX"
SecretKey = "XXXX"
二、流水线设计:阶段与门控
一个成熟的CI/CD流水线应包含构建、测试、安全扫描、部署四个阶段,并在关键节点设置门控(manual gate):
# .gitlab-ci.yml
stages:
- build
- test
- security
- package
- deploy-staging
- deploy-production
variables:
M***EN_OPTS: "-Dmaven.repo.local=/cache/maven -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn"
DOCKER_REGISTRY: "registry.example.com"
APP_NAME: "user-service"
# ========== BUILD 阶段 ==========
build:
stage: build
script:
- mvn compile -q
- mvn package -DskipTests -q
artifacts:
paths:
- target/*.jar
expire_in: 1 hour
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- /cache/maven/
# ========== TEST 阶段 ==========
unit-test:
stage: test
script:
- mvn test -pl . -am
coverage: '/Total.*?([0-9]{1,3})%/'
artifacts:
reports:
junit: target/surefire-reports/TEST-*.xml
when: always
integration-test:
stage: test
services:
- name: postgres:16-alpine
alias: postgres-db
- name: redis:7-alpine
alias: redis-cache
variables:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres-db:5432/testdb"
SPRING_DATA_REDIS_HOST: redis-cache
script:
- mvn verify -DskipUnitTests -pl . -am
artifacts:
reports:
junit: target/failsafe-reports/TEST-*.xml
when: always
# ========== SECURITY 阶段 ==========
dependency-check:
stage: security
script:
- mvn org.owasp:dependency-check-maven:check
artifacts:
reports:
dependency_scanning: target/dependency-check-report.json
allow_failure: true
sast:
stage: security
trigger:
include:
- template: Security/SAST.gitlab-ci.yml
# ========== PACKAGE 阶段 ==========
docker-build:
stage: package
script:
- docker build --build-arg JAR_FILE=target/*.jar -t $DOCKER_REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA .
- docker push $DOCKER_REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA
- docker tag $DOCKER_REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA $DOCKER_REGISTRY/$APP_NAME:latest
- docker push $DOCKER_REGISTRY/$APP_NAME:latest
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# ========== DEPLOY 阶段 ==========
deploy-staging:
stage: deploy-staging
environment:
name: staging
url: https://staging.example.com
script:
- envsubst < k8s/deployment.yaml | kubectl apply -f -
- kubectl rollout status deployment/$APP_NAME -n staging --timeout=120s
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
stage: deploy-production
environment:
name: production
url: https://app.example.com
script:
- envsubst < k8s/deployment.yaml | kubectl apply -f -
- kubectl rollout status deployment/$APP_NAME -n production --timeout=180s
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual # 生产部署需要人工确认
三、多环境配置与变量管理
不同环境(dev/staging/production)的配置应通过GitLab环境变量注入,而非硬编码在代码中:
# Spring Boot的application.yml使用环境变量
server:
port: ${SERVER_PORT:8080}
spring:
datasource:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
hikari:
maximum-pool-size: ${DB_POOL_SIZE:20}
data:
redis:
host: ${SPRING_DATA_REDIS_HOST:localhost}
port: ${SPRING_DATA_REDIS_PORT:6379}
# GitLab CI/CD变量设置(Settings -> CI/CD -> Variables)
# staging环境变量:
# SPRING_DATASOURCE_URL = jdbc:postgresql://pg-staging:5432/appdb
# SPRING_DATASOURCE_USERNAME = app_staging
# SPRING_DATASOURCE_PASSWORD = [MASKED]
# production环境变量:
# SPRING_DATASOURCE_URL = jdbc:postgresql://pg-prod:5432/appdb
# SPRING_DATASOURCE_USERNAME = app_prod
# SPRING_DATASOURCE_PASSWORD = [MASKED]
环境保护(Environment Protection)确保只有特定分支能部署到对应环境:
# GitLab项目设置 -> CI/CD -> Environments
# staging: Protected, Only allow deployments from main/develop branches
# production: Protected, Only allow deployments from main branch
四、Dockerfile优化与镜像安全
# 多阶段构建:编译镜像与运行镜像分离
FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2/repository \
mvn package -DskipTests -q
# 运行镜像:使用JRE而非JDK,体积从400MB降至180MB
FROM eclipse-temurin:21-jre-alpine
ARG JAR_FILE
WORKDIR /app
# 非root用户运行
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder /app/target/*.jar app.jar
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", \
"-XX:MaxRAMPercentage=75.0", \
"-jar", "app.jar"]
镜像安全扫描集成:
# .gitlab-ci.yml 中添加 Trivy 扫描
container-scanning:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL \
--format json --output trivy-report.json \
$DOCKER_REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA
artifacts:
reports:
container_scanning: trivy-report.json
allow_failure: false # CRITICAL漏洞阻断部署
五、流水线性能优化
CI/CD流水线本身的性能直接影响开发体验。以下是常见优化手段:
- 缓存Maven/Gradle依赖:首次构建后缓存
.m2/repository,后续构建跳过依赖下载,节省60%以上时间 - 并行执行无依赖的Job:同stage内的Job自动并行,将独立测试拆分到多个Job
- 增量构建:利用Maven的
-pl和-am选项只构建变更的模块 - 镜像拉取策略:
pull_policy = "if-not-present"避免每次从远端拉取基础镜像 - Docker BuildKit:启用
DOCKER_BUILDKIT=1,支持--mount=type=cache缓存构建中间层
# 需要完整构建的场景触发
workflow:
rules:
# main分支和MR始终完整构建
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_MERGE_REQUEST_IID
# 其他分支只在变更关键文件时触发
- if: $CI_COMMIT_BRANCH
changes:
- pom.xml
- src/**
- Dockerfile
- k8s/**
六、回滚策略与部署安全网
# 快速回滚:回退到上一个稳定版本
deploy-rollback:
stage: deploy-production
environment:
name: production
script:
- kubectl rollout undo deployment/$APP_NAME -n production
- kubectl rollout status deployment/$APP_NAME -n production --timeout=60s
when: manual
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# 在deployment.yaml中配置就绪探针与存活探针
# 确保只有健康的新Pod才接收流量
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 始终保持可用实例数
template:
spec:
containers:
- name: app
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
从代码提交到自动部署,一个成熟的GitLab CI/CD流水线不仅仅是"脚本自动化",更是软件交付流程的工程化保障。通过阶段门控确保质量,通过环境变量解耦配置,通过安全扫描阻断风险,通过RollingUpdate+健康探针保障零停机部署,通过手动确认保护生产环境。这套方案在数十个微服务项目中经过验证,平均构建时间从15分钟优化到4分钟,部署频率从每周2次提升到每日多次。