GitLab CI/CD
一、什么是CI/CD
CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法
CI/CD 的核心概念是持续集成、持续交付和持续部署
具体而言,CI/CD 可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。这些关联的事务通常被统称为"CI/CD 管道",由开发和运维团队以敏捷方式协同支持
CI 是什么?CI 和 CD 有什么区别?
缩略词 CI/CD 具有几个不同的含义。CI/CD 中的"CI"始终指持续集成,它属于开发人员的自动化流程。成功的 CI 意味着应用代码的新更改会定期构建、测试并合并到共享存储库中。该解决方案可以解决在一次开发中有太多应用分支,从而导致相互冲突的问题
CI/CD 中的"CD"指的是持续交付和/或持续部署,这些相关概念有时会交叉使用。两者都事关管道后续阶段的自动化,但它们有时也会单独使用,用于说明自动化程度
持续交付通常是指开发人员对应用的更改会自动进行错误测试并上传到存储库(如 GitHub 或容器注册表),然后由运维团队将其部署到实时生产环境中。这旨在解决开发和运维团队之间可见性及沟通较差的问题。因此,持续交付的目的就是确保尽可能减少部署新代码时所需的工作量
持续部署(另一种"CD")指的是自动将开发人员的更改从存储库发布到生产环境,以供客户使用。它主要为了解决因手动流程降低应用交付速度,从而使运维团队超负荷的问题。持续部署以持续交付的优势为根基,实现了管道后续阶段的自动化
CI/CD 既可能仅指持续集成和持续交付构成的关联环节,也可以指持续集成、持续交付和持续部署这三项构成的关联环节。更为复杂的是,有时"持续交付"也包含了持续部署流程
归根结底,我们没必要纠结于这些语义,您只需记得 CI/CD 其实就是一个流程(通常形象地表述为管道),用于实现应用开发中的高度持续自动化和持续监控。因案例而异,该术语的具体含义取决于 CI/CD 管道的自动化程度。
CI 持续集成(Continuous Integration)
现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为"合并日"),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜
持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或"主干"中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误
CD 持续交付(Continuous Delivery)
完成 CI 中构建及单元测试和集成测试的自动化流程后,持续交付可自动将已验证的代码发布到存储库。为了实现高效的持续交付流程,务必要确保 CI 已内置于开发管道。持续交付的目标是拥有一个可随时部署到生产环境的代码库。
在持续交付中,每个阶段(从代码更改的合并,到生产就绪型构建版本的交付)都涉及测试自动化和代码发布自动化。在流程结束时,运维团队可以快速、轻松地将应用部署到生产环境中
CD 持续部署(Continuous Deployment)
对于一个成熟的 CI/CD 管道来说,最后的阶段是持续部署。作为持续交付——自动将生产就绪型构建版本发布到代码存储库——的延伸,持续部署可以自动将应用发布到生产环境。由于在生产之前的管道阶段没有手动门控,因此持续部署在很大程度上都得依赖精心设计的测试自动化
实际上,持续部署意味着开发人员对应用的更改在编写后的几分钟内就能生效(假设它通过了自动化测试)。这更加便于持续接收和整合用户反馈。总而言之,所有这些 CI/CD 的关联步骤都有助于降低应用的部署风险,因此更便于以小件的方式(而非一次性)发布对应用的更改。不过,由于还需要编写自动化测试以适应 CI/CD 管道中的各种测试和发布阶段,因此前期投资还是会很大
二、GitLab CI/CD
GitLab CI/CD是 GitLab 的一部分,可用于所有持续方法(持续集成、交付和部署)。使用 GitLab CI/CD,您可以测试、构建和发布您的软件,而无需第三方应用程序或集成
GitLab工作流程
如果您深入了解工作流程,您可以在 DevOps 生命周期的每个阶段看到 GitLab 中可用的功能。
三、CI配置
CI/CD管道的配置文件为 .gitlab-ci.yml , 位于项目根目录下
.gitlab-ci.yml 配置关键词
1、配置管道行为的全局关键词
关键词 | 描述 |
---|---|
default |
工作关键字的自定义默认值。 |
include |
从其他 YAML 文件导入配置。 |
stages |
流水线阶段的名称和顺序。 |
variables |
为管道中的所有作业定义 CI/CD 变量。 |
workflow |
控制运行什么类型的管道。 |
stages:
用于定义包括作业组的阶段。在作业中使用stage以将作业配置为在特定阶段运行
如果stages未在.gitlab-ci.yml文件中定义,则默认管道阶段为:
.pre - build - test - deploy - .post
stages定义了作业的执行顺序:
同一阶段的作业并行运行。
下一阶段的作业在上一阶段的作业成功完成后运行
示例:
stages: - build - test - deploy
2、配置作业(job)的关键词
关键词 | 描述 |
---|---|
after_script |
覆盖作业后执行的一组命令。 |
allow_failure |
允许作业失败。失败的作业不会导致管道失败。 |
artifacts |
成功后附加到作业的文件和目录列表。 |
before_script |
覆盖在作业之前执行的一组命令。 |
cache |
应在后续运行之间缓存的文件列表。 |
coverage |
给定作业的代码覆盖率设置。 |
dast_configuration |
在作业级别使用 DAST 配置文件中的配置。 |
dependencies |
通过提供要从中获取工件的作业列表来限制将哪些工件传递给特定作业。 |
environment |
作业部署到的环境的名称。 |
except |
控制何时不创建作业。 |
extends |
此作业继承的配置条目。 |
image |
使用 Docker 镜像。 |
inherit |
选择所有作业继承的全局默认值。 |
interruptible |
定义一个作业是否可以在被较新的运行冗余时取消。 |
needs |
在阶段排序之前执行作业。 |
only |
控制何时创建工作。 |
pages |
上传作业结果以与 GitLab 页面一起使用。 |
parallel |
应并行运行多少个作业实例。 |
release |
指示运行器生成释放对象。 |
resource_group |
限制作业并发。 |
retry |
发生故障时可以自动重试作业的时间和次数。 |
rules |
用于评估和确定作业的选定属性以及是否已创建的条件列表。 |
script |
由运行程序执行的 Shell 脚本。 |
secrets |
CI/CD 是工作需要的秘密。 |
services |
使用 Docker 服务镜像。 |
stage |
定义作业阶段。 |
tags |
用于选择跑步者的标签列表。 |
timeout |
定义优先于项目范围设置的自定义作业级超时。 |
trigger |
定义下游管道触发器。 |
variables |
在工作级别定义工作变量。 |
when |
何时运行作业。 |
only/except:控制何时将作业添加到管道
only用于定义作业何时运行
except用于定义作业何时不运行
以下四个关键字可以在only和except下结合使用:refs、variables、changes、kubernetes
only:refs/except:refs
使用only:refsandexcept:refs关键字来控制何时根据分支名称或管道类型将作业添加到管道,如:
job1: script: echo only: - main - /^issue-.*$/ - merge_requests job2: script: echo except: - main - /^stable-branch.*$/ - schedules
only:changes/except:changes
使用changes关键字,当 Git 推送事件修改文件时,only:changes下的文件运行作业,except:changes下的文件不运行作业
docker build: script: docker build -t my-image:$CI_COMMIT_REF_SLUG . only: refs: - branches changes: - Dockerfile - docker/scripts/* - dockerfiles/**/* - more_scripts/*.{rb,py,sh} - "**/*.json"
script:
使用script用于执行runner要执行的命令
除 trigger job之外,其他的作业都需要script
可能的输入:
单行命令
长命令分成多行
artifacts:
使用 artifacts 在 stage 之间传递中间构建结果
artifacts 由 Job 生成,存储在GitLab中,可以下载;
artifacts 和 cache 都定义了它们相对于项目目录的路径,并且不能链接到项目目录之外的文件
自定义CI/CD变量
①:在项目的 .gitlab-ci.yml 文件中
②:在 project's setting中
variables:
使用variables关键字在Job或者gitlab-ci.yml 文件的顶层定义自定义变量。
如果变量位于顶层,那么它是全局可用的,并且所有的Job都可以使用它。如果只在Job中定义,则只有Job可以使用它
variables: TEST_VAR: "All jobs can use this variable's value" job1: variables: TEST_VAR_JOB: "Only job1 can use this variable's value" script: - echo "$TEST_VAR" and "$TEST_VAR_JOB"
when:
when用于配置作业运行的条件。如果未在作业中定义,则默认值为 when:on_success
可能的值:
on_success(默认):仅在早期阶段的所有作业都成功或具有allow_failure: true.
manual:仅在手动触发时运行作业。
always:无论早期阶段的作业状态如何,都运行作业。
on_failure:仅当至少一个早期阶段的作业失败时才运行作业。
delayed:将作业的执行延迟 指定的持续时间。
never: 不要运行作业
image:
用于指定作业在其中运行的Docker镜像
可能的输入:
镜像的名称,如果需要,包括注册表路径,采用以下格式之一:
<image-name>(与使用<image-name>标签latest相同)
<image-name>:<tag>
<image-name>@<digest>
image:name
作业运行时所在的Docker镜像的名称,如:
image: name: "registry.example.com/my/image:latest"
image:image:entrypoint
作为容器入口点执行的命令或脚本,如:
image: name: super/sql:experimental entrypoint: [""]
更多配置:https://docs.gitlab.com/ee/ci/yaml/index.html
四、示例
1、.gitlab.yml
stages: - build_mimall_pre - build_mimall_prd - build_youpin_pre - build_youpin_prd - build_mimall_pre_image - build_youpin_pre_image - deploy_mimall_pre - deploy_youpin_pre - build_mimall_prd_image - build_youpin_prd_image build_mimall_pre: stage: build_mimall_pre image: name: cr.d.xiaomi.net/containercloud/ubuntu-openjdk1.8-maven3.5:latest script: - mvn clean package -P pre -Dmaven.test.skip=true artifacts: paths: - sync-mimall/target/sync-mimall-*.jar after_script: - echo "build_mimall_pre completed" only: refs: - preview changes: - sync-mimall/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml build_mimall_prd: stage: build_mimall_prd image: name: cr.d.xiaomi.net/containercloud/ubuntu-openjdk1.8-maven3.5:latest script: - mvn clean package -P prd -Dmaven.test.skip=true artifacts: paths: - sync-mimall/target/sync-mimall-*.jar after_script: - echo "build_mimall_prd completed" only: refs: - master changes: - sync-mimall/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml build_youpin_pre: stage: build_youpin_pre image: name: cr.d.xiaomi.net/containercloud/ubuntu-openjdk1.8-maven3.5:latest script: - mvn clean package -P pre -Dmaven.test.skip=true artifacts: paths: - sync-youpin/target/sync-youpin-*.jar after_script: - echo "build_youpin_pre completed" only: refs: - preview changes: - sync-youpin/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml build_youpin_prd: stage: build_youpin_prd image: name: cr.d.xiaomi.net/containercloud/ubuntu-openjdk1.8-maven3.5:latest script: - mvn clean package -P prd -Dmaven.test.skip=true artifacts: paths: - sync-youpin/target/sync-youpin-*.jar after_script: - echo "build_youpin_prd completed" only: refs: - master changes: - sync-youpin/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml build_mimall_pre_image: stage: build_mimall_pre_image image: name: cr.d.xiaomi.net/containercloud/kaniko-executor-xiaomi:release entrypoint: [""] script: - MIMALL_IMAGE=$REPO_SERVER/$REPO_NAMESPACE/sync-mimall:pre-$CI_COMMIT_SHORT_SHA - echo "{\"auths\":{\"$REPO_SERVER\":{\"username\":\"$REPO_USER\",\"password\":\"$REPO_PASS\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/sync-mimall/Dockerfile --destination $MIMALL_IMAGE --validate-image after_script: - echo "build_mimall_pre_image completed" only: refs: - preview changes: - sync-mimall/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml retry: 2 build_mimall_prd_image: stage: build_mimall_prd_image image: name: cr.d.xiaomi.net/containercloud/kaniko-executor-xiaomi:release entrypoint: [""] script: - MIMALL_IMAGE=$REPO_SERVER/$REPO_NAMESPACE/sync-mimall:prd-$CI_COMMIT_SHORT_SHA - echo "{\"auths\":{\"$REPO_SERVER\":{\"username\":\"$REPO_USER\",\"password\":\"$REPO_PASS\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/sync-mimall/Dockerfile --destination $MIMALL_IMAGE --validate-image after_script: - echo "build_mimall_prd_image completed" only: refs: - master changes: - sync-mimall/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml retry: 2 build_youpin_pre_image: stage: build_youpin_pre_image image: name: cr.d.xiaomi.net/containercloud/kaniko-executor-xiaomi:release entrypoint: [""] script: - YOUPIN_IMAGE=$REPO_SERVER/$REPO_NAMESPACE/sync-youpin:pre-$CI_COMMIT_SHORT_SHA - echo "{\"auths\":{\"$REPO_SERVER\":{\"username\":\"$REPO_USER\",\"password\":\"$REPO_PASS\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/sync-youpin/Dockerfile --destination $YOUPIN_IMAGE --validate-image after_script: - echo "build_youpin_pre_image completed" only: refs: - preview changes: - sync-youpin/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml retry: 2 build_youpin_prd_image: stage: build_youpin_prd_image image: name: cr.d.xiaomi.net/containercloud/kaniko-executor-xiaomi:release entrypoint: [""] script: - YOUPIN_IMAGE=$REPO_SERVER/$REPO_NAMESPACE/sync-youpin:prd-$CI_COMMIT_SHORT_SHA - echo "{\"auths\":{\"$REPO_SERVER\":{\"username\":\"$REPO_USER\",\"password\":\"$REPO_PASS\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/sync-youpin/Dockerfile --destination $YOUPIN_IMAGE --validate-image after_script: - echo "build_youpin_prd_image completed" only: refs: - master changes: - sync-youpin/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml retry: 2 deploy_mimall_pre: stage: deploy_mimall_pre image: name: cr.d.xiaomi.net/container/xiaomi_alpine_fusion_cli_mice:latest script: - fusion-cli --json config --access-key $CLOUD_MANAGER_AK --secret-key $CLOUD_MANAGER_SK - fusion-cli --json mice update sync-mimall-preview pre-$CI_COMMIT_SHORT_SHA --cluster c3 only: refs: - preview changes: - sync-mimall/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml when: on_success deploy_youpin_pre: stage: deploy_youpin_pre image: name: cr.d.xiaomi.net/container/xiaomi_alpine_fusion_cli_mice:latest script: - fusion-cli --json config --access-key $CLOUD_MANAGER_AK --secret-key $CLOUD_MANAGER_SK - fusion-cli --json mice update sync-youpin-preview pre-$CI_COMMIT_SHORT_SHA --cluster c3 only: refs: - preview changes: - sync-youpin/**/* - sync-common/**/* - sync-deploy/**/* - pom.xml when: on_success
2、Dockerfile
#指定了运行容器的镜像并把生成程序文件拷贝到镜像根目录用以运行 FROM cr.d.xiaomi.net/containercloud/openjdk:8-jre-alpine ENV TZ "Asia/Shanghai" COPY sync-youpin/target/sync-youpin-*.jar /opt/sync-youpin.jar WORKDIR /opt CMD ["java", "-jar", "sync-youpin.jar"]
cicd配置及解释:
打包:
build mico-core-api preview4test:
stage: build
image:
name: cr.d.xiaomi.net/containercloud/ubuntu-openjdk1.8-maven3.5:latest
script:
- mvn clean package -P preview4test -Dmaven.test.skip=true
artifacts:
paths:
- mico-core-api/target/*.jar
after_script:
- echo "build mico-core-api preview4test completed"
only:
refs:
- preview4test
changes:
- mico-common/**/*
- mico-core-api/**/*
- mico-core-api/.cicd.yml
- pom.xml
解释:
构建镜像:
15:51
build mico-core-api image preview4test:
stage: buildImage
image:
name: cr.d.xiaomi.net/containercloud/kaniko-executor-xiaomi:release
entrypoint: [ "" ]
script:
- export APP_IMAGE=$REPO_SERVER/$REPO_NAMESPACE/mico-core-api:preview4test-$CI_COMMIT_SHORT_SHA-$(date +%Y%m%d%H%M)
- echo "{\"auths\":{\"$REPO_SERVER\":{\"username\":\"$REPO_USER\",\"password\":\"$REPO_PASS\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile mico-core-api/Dockerfile --destination $APP_IMAGE --validate-image
after_script:
- echo "build mico-core-api image preview4test completed"
only:
refs:
- preview4test
changes:
- mico-common/**/*
- mico-core-api/**/*
- mico-core-api/.cicd.yml
- pom.xml
retry: 2
解释:
END.