GitLab CI/CD实践指南
前言
CI/CD是现代软件开发的基础设施,GitLab CI/CD作为GitLab内置的持续集成/部署工具,提供了强大而灵活的自动化能力。本文将系统讲解GitLab CI/CD的核心概念、配置方法和最佳实践。
一、CI/CD基础概念
1.1 什么是CI/CD
**持续集成(CI)**:
- 频繁地将代码集成到主干
- 自动化构建和测试
- 快速发现和定位错误
**持续部署(CD)**:
- 自动将代码部署到生产环境
- 减少人工干预
- 快速交付新功能
1.2 GitLab CI/CD架构
1 2 3 4 5 6 7 8 9
| 代码提交 ↓ 触发Pipeline ↓ GitLab Runner执行 ↓ 构建 → 测试 → 部署 ↓ 反馈结果
|
二、GitLab Runner安装
2.1 Docker方式安装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| docker run -d --name gitlab-runner --restart always \ -v /srv/gitlab-runner/config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:latest
docker exec -it gitlab-runner gitlab-runner register
|
2.2 Shell Executor配置
1 2 3 4 5 6 7
| gitlab-runner register \ --non-interactive \ --url "https://gitlab.com/" \ --registration-token "YOUR_TOKEN" \ --executor "shell" \ --description "shell-runner"
|
三、.gitlab-ci.yml配置
3.1 基础配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
stages: - build - test - deploy
variables: MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
cache: paths: - .m2/repository/ - node_modules/
build: stage: build image: maven:3.8-jdk-11 script: - mvn clean package artifacts: paths: - target/*.jar expire_in: 1 week only: - master - develop
test: stage: test image: maven:3.8-jdk-11 script: - mvn test coverage: '/Total.*?(\d+\.?\d*)%/' only: - master - develop
deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh script: - scp target/*.jar user@server:/app/ - ssh user@server "systemctl restart myapp" only: - master when: manual
|
3.2 多环境配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| deploy:dev: stage: deploy script: - echo "Deploying to dev environment" - kubectl config use-context dev - kubectl apply -f k8s/dev/ environment: name: development url: https://dev.example.com only: - develop
deploy:staging: stage: deploy script: - echo "Deploying to staging environment" - kubectl config use-context staging - kubectl apply -f k8s/staging/ environment: name: staging url: https://staging.example.com only: - master when: manual
deploy:prod: stage: deploy script: - echo "Deploying to production" - kubectl config use-context prod - kubectl apply -f k8s/prod/ environment: name: production url: https://example.com only: - tags when: manual
|
四、常用Job配置
4.1 Node.js项目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| image: node:16-alpine
stages: - install - lint - test - build - deploy
cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/
install: stage: install script: - npm ci artifacts: paths: - node_modules/ expire_in: 1 hour
lint: stage: lint script: - npm run lint dependencies: - install
test:unit: stage: test script: - npm run test:unit coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' dependencies: - install
test:e2e: stage: test services: - postgres:13 variables: POSTGRES_DB: testdb POSTGRES_USER: test POSTGRES_PASSWORD: test script: - npm run test:e2e dependencies: - install
build: stage: build script: - npm run build artifacts: paths: - dist/ expire_in: 1 week dependencies: - install only: - master - develop
deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache rsync openssh-client script: - rsync -avz --delete dist/ user@server:/var/www/html/ dependencies: - build only: - master
|
4.2 Docker镜像构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| stages: - build - push - deploy
variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build: stage: build image: docker:latest services: - docker:dind before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker build -t $IMAGE_TAG . - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest - docker push $IMAGE_TAG - docker push $CI_REGISTRY_IMAGE:latest
deploy:k8s: stage: deploy image: bitnami/kubectl:latest script: - kubectl config use-context $KUBE_CONTEXT - kubectl set image deployment/myapp myapp=$IMAGE_TAG - kubectl rollout status deployment/myapp only: - master
|
4.3 多架构构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| build:multiarch: stage: build image: docker:latest services: - docker:dind before_script: - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - docker buildx create --use - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - | docker buildx build \ --platform linux/amd64,linux/arm64 \ --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA \ --tag $CI_REGISTRY_IMAGE:latest \ --push \ .
|
五、高级功能
5.1 条件执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| job:master: script: - echo "Only on master" only: - master
job:tags: script: - echo "Only on tags" only: - tags
job:rules: script: - echo "Using rules" rules: - if: '$CI_COMMIT_BRANCH == "master"' - if: '$CI_COMMIT_TAG' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' changes: - src/**/* - when: manual allow_failure: true
job:except: script: - echo "Except develop" except: - develop
|
5.2 并行执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| test:parallel: stage: test parallel: matrix: - NODE_VERSION: ['14', '16', '18'] OS: ['ubuntu', 'alpine'] image: node:${NODE_VERSION}-${OS} script: - npm test
test:split: stage: test parallel: 5 script: - npm test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
|
5.3 依赖管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| build: stage: build script: - npm run build artifacts: paths: - dist/
deploy: stage: deploy dependencies: - build script: - deploy.sh
test:unit: stage: test dependencies: [] script: - npm test
|
5.4 动态子Pipeline
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| generate: stage: generate script: - python generate_pipeline.py > pipeline.yml artifacts: paths: - pipeline.yml
trigger: stage: deploy trigger: include: - artifact: pipeline.yml job: generate
trigger:other: stage: deploy trigger: project: group/other-project branch: master
|
六、实战案例
6.1 Spring Boot项目完整流水线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
image: maven:3.8-jdk-11
variables: MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository" SPRING_PROFILES_ACTIVE: "test" DOCKER_IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
stages: - build - test - quality - package - deploy
cache: paths: - .m2/repository/
compile: stage: build script: - mvn clean compile artifacts: paths: - target/classes/ expire_in: 1 hour
test:unit: stage: test script: - mvn test coverage: '/Total.*?([0-9]{1,3})%/' artifacts: reports: junit: target/surefire-reports/TEST-*.xml coverage_report: coverage_format: jacoco path: target/site/jacoco/jacoco.xml
sonarqube: stage: quality image: maven:3.8-jdk-11 script: - mvn sonar:sonar -Dsonar.projectKey=$CI_PROJECT_NAME -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_TOKEN only: - master - develop
package: stage: package script: - mvn package -DskipTests artifacts: paths: - target/*.jar expire_in: 1 week only: - master - develop - tags
docker:build: stage: package image: docker:latest services: - docker:dind before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker build -t $DOCKER_IMAGE . - docker tag $DOCKER_IMAGE $CI_REGISTRY_IMAGE:latest - docker push $DOCKER_IMAGE - docker push $CI_REGISTRY_IMAGE:latest dependencies: - package only: - master
deploy:dev: stage: deploy image: bitnami/kubectl:latest script: - kubectl config use-context dev - | kubectl set image deployment/myapp \ myapp=$DOCKER_IMAGE \ --record - kubectl rollout status deployment/myapp environment: name: development url: https://dev.example.com only: - develop
deploy:prod: stage: deploy image: bitnami/kubectl:latest script: - kubectl config use-context prod - kubectl set image deployment/myapp myapp=$DOCKER_IMAGE - kubectl rollout status deployment/myapp environment: name: production url: https://example.com only: - tags when: manual
|
6.2 Dockerfile
1 2 3 4 5 6 7 8 9
| FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
|
七、最佳实践
7.1 安全管理
1 2 3 4 5 6 7 8 9 10
| variables: DATABASE_URL: $DATABASE_URL API_KEY: $API_KEY
deploy: before_script: - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa
|
7.2 加速构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| cache: key: ${CI_COMMIT_REF_SLUG} paths: - .m2/repository/ - node_modules/ - .gradle/
build:frontend: rules: - changes: - frontend/**/* script: - cd frontend && npm run build
build:backend: rules: - changes: - backend/**/* script: - cd backend && mvn package
|
7.3 通知集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| notify:slack: stage: .post image: curlimages/curl:latest script: - | curl -X POST $SLACK_WEBHOOK_URL \ -H 'Content-Type: application/json' \ -d "{ \"text\": \"Pipeline $CI_PIPELINE_STATUS\", \"attachments\": [{ \"color\": \"$([[ $CI_JOB_STATUS == 'success' ]] && echo 'good' || echo 'danger')\", \"fields\": [ {\"title\": \"Project\", \"value\": \"$CI_PROJECT_NAME\", \"short\": true}, {\"title\": \"Branch\", \"value\": \"$CI_COMMIT_REF_NAME\", \"short\": true} ] }] }" when: always
|
八、故障排查
8.1 常见问题
1 2 3 4 5 6 7 8 9
| docker logs gitlab-runner
|
8.2 性能监控
1 2 3 4 5 6 7
| before_script: - export START_TIME=$(date +%s)
after_script: - export END_TIME=$(date +%s) - echo "Job duration: $((END_TIME - START_TIME)) seconds"
|
九、总结
GitLab CI/CD核心要点:
关键概念
- Pipeline:完整的CI/CD流程
- Stage:流程中的阶段
- Job:具体的执行任务
- Runner:执行任务的代理
最佳实践
- 合理划分阶段
- 使用缓存加速构建
- 保护敏感信息
- 实现环境隔离
- 设置通知机制
掌握GitLab CI/CD,让你的部署自动化!
参考资料