在GitLab CI中创建自定义工作流程的提示

注意事项 佩雷夫 :原始文章由波兰小公司Three Dots Labs的创始人之一MiłoszSmółka撰写,专门研究“先进的后端解决方案”。 作者借鉴了他在积极使用GitLab CI方面的经验,并向该开源产品的其他用户分享了积累的技巧。 阅读它们之后,我们意识到他所描述的问题离我们有多近,因此我们决定与更多的读者分享提议的解决方案。



这次,我将介绍GitLab CI中的更多高级主题。 这里的常见任务是在管道中实现非标准功能。 大多数技巧是特定于GitLab的,尽管其中一些技巧可以应用于其他CI系统。

运行集成测试


通常,使用单元测试的代码验证容易连接到任何CI系统。 通常,这并不比以编程语言运行内置在标准实用程序集中的命令之一复杂。 在这样的测试中,您可能会使用各种moki和stub来隐藏实现细节,并专注于测试特定的逻辑。 例如,您可以使用内存数据库作为存储或为HTTP客户端编写存根,这些存根将始终返回已准备好的响应。

但是,迟早您将需要集成测试,以涵盖测试中更特殊的情况。 我不会讨论所有可能的测试类型,而只是说集成是指使用某种外部资源的测试。 它可以是真实的数据库服务器,HTTP服务,连接的存储等。

在GitLab中,很容易将可插拔资源作为与运行脚本的容器关联的Docker容器来运行。 可以使用services定义这些依赖关系。 如果在alias字段中指定了图像,则可以按图像名称或选择的名称使用它们。

这是在MySQL中使用可插拔容器的简单示例:

 integration_tests: stage: tests services: - name: mysql:8 alias: db script: - ./run_tests.sh db:3306 

在这种情况下,在测试脚本中,您将需要连接到db主机。 使用别名通常是一个好主意,因为使用别名可以替换映像,而无需修改测试代码。 例如,您可以使用mariadb替换mysql映像,该脚本仍将正常运行。

等待容器


由于插件容器的加载需要时间,因此您可能需要等待才能发送任何请求。 一种简单的方法是使用定义的超时时间等待它的.sh脚本。

使用Docker Compose


在大多数情况下, services应足够。 但是,有时可能需要与外部服务进行交互。 例如,在两个单独的容器中启动Kafka和ZooKeeper的情况(这是收集官方图像的方式)。 另一个示例是使用动态数量的节点(例如Selenium)运行测试。 运行这些服务的最佳解决方案是Docker Compose

 version: '3' services: zookeeper: image: confluentinc/cp-zookeeper environment: ZOOKEEPER_CLIENT_PORT: 2181 kafka: image: confluentinc/cp-kafka environment: KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 ports: - 9092:9092 

如果将安装与可信赖服务器上的GitLab运行程序一起使用,则可以通过Shell执行程序运行Docker Composer。 另一个可能的选择是Docker in Dockerdinddind 。 但是在这种情况下,请先阅读本文

使用Compose的一种方法是设置环境,运行测试,然后销毁所有内容。 一个简单的bash脚本如下所示:

 docker-compose up -d ./run_tests.sh localhost:9092 docker-compose down 

只要您在最小的环境中运行测试,一切都会很好。 尽管可能会出现需要安装一些依赖项的情况……在Docker Compose中还有另一种运行测试的方法-它允许您在测试环境中创建自己的Docker映像。 在其中一个容器中,运行测试并使用相应的返回码退出:

 version: '3' services: zookeeper: image: confluentinc/cp-zookeeper environment: ZOOKEEPER_CLIENT_PORT: 2181 kafka: image: confluentinc/cp-kafka environment: KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 tests: image: registry.example.com/some-image command: ./run_tests.sh kafka:9092 

请注意,我们消除了映射端口的需要。 在此示例中,测试可以直接与所有服务交互。

它们的启动是通过一个命令执行的:

 docker-compose up --exit-code-from tests 

--exit-code-from --abort-on-container-exit意味着--abort-on-container-exit ,这意味着:由docker-compose up发起的整个环境将在其中一个容器完成后停止。 该命令的完成代码将等同于所选服务的退出代码(即,这些是上面示例中的tests )。 如果启动测试的命令以非零代码完成,则整个docker-compose up将随之退出。

将标签用作CI标签


警告 :这是一个不寻常的想法,但在我看来,它非常有用且灵活。

如您所知,GitLab具有在项目和组级别可用的标签功能。 可以在票证上设置标签并合并请求。 但是,它们与管道没有关系。



进行一些细化可以使您在作业脚本中访问合并请求的标签。 在GitLab 11.6中,一切变得更加容易,因为 如果管道only: merge_requests使用only: merge_requests ,则会出现CI_MERGE_REQUEST_IID环境变量(是的,它具有IID而不是ID )。

如果only: merge_requests未使用only: merge_requests或您正在使用旧版本的GitLab,仍然可以使用API​​调用获取MR:

 curl "$CI_API_V4_URL/projects/$CI_PROJECT_ID/repository/commits/$CI_COMMIT_SHA/merge_requests?private_token=$GITLAB_TOKEN" 

我们需要的领域是iid 。 但是,请记住,许多MR可以针对给定的提交返回。

收到MR IID后,仅需转到Merge Requests API并使用答案中的labels字段即可:

 curl "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID?private_token=$GITLAB_TOKEN" 

登入


不幸的是,目前无法使用$CI_JOB_TOKEN访问项目API(至少在项目不是公共的情况下)。 如果项目的访问权限受限(内部或私有),则要在GitLab API中进行授权,您将需要生成个人API令牌。



但是,这不是最安全的解决方案,因此请小心。 如果令牌落入坏人之手,则对您所有项目的写权限可能会随之出现。 降低风险的一种方法是创建一个单独的帐户,该帐户有权读取存储库并为该帐户生成个人令牌。

您的变量有多安全?


几个版本之前,“ 变量”部分称为“ 秘密变量” ,这听起来像是为了可靠地存储凭据和其他关键信息而创建的。 实际上,这些变量只是对没有维护者特权的用户隐藏。 它们未在磁盘上加密,并且泄漏很容易通过脚本中的环境变量发生。

添加任何变量时请记住这一点,并考虑在更安全的解决方案(例如, 来自HashiCorp的Vault )中保密

用例


由标签列表决定如何处理。 这里有一些想法:

  • 使用它们来细分测试。
  • 使用带有冒号作为分隔符的键值语义(例如,标签,如tests:authtests:user
  • 包括作业的某些功能。
  • 如果标签存在,则允许调试特定作业。

调用外部API


尽管GitLab随附了一组可用的功能,但您很有可能希望使用可以与管道集成的其他实用程序。 当然,最简单的实现方法是调用良好的旧curl

如果创建自己的工具,则可以教他们听GitLab Webhooks (请参阅项目设置中的“ 集成”选项卡)。 但是,如果您打算将它们与任何关键系统一起使用,请确保它们满足高可用性要求。

示例:Grafana批注


如果您使用Grafana ,则注释是在图表上标记随时间推移发生的事件的好方法。 不仅可以通过在GUI中单击来手动添加它们,还可以通过调用Grafana REST API来添加它们:



要访问API,您将需要生成一个API密钥。 考虑创建一个访问权限受限的单独用户:



在项目设置中定义两个变量:

  • GRAFANA_URL -Grafana安装的URL(例如https://grafana.example.com );
  • GRAFANA_APIKEY生成的API密钥。

为了能够重用它,请将脚本与常用脚本一起放入存储库中

 #!/bin/bash set -e if [ $# -lt 2 ]; then echo "Usage: $0 <text> <tag>" exit 1 fi readonly text="$1" readonly tag="$2" readonly time="$(date +%s)000" cat >./payload.json <<EOF { "text": "$text", "tags": ["$tag"], "time": $time, "timeEnd": $time } EOF curl -X POST "$GRAFANA_URL/api/annotations" \ -H "Authorization: Bearer $GRAFANA_APIKEY" \ -H "content-type: application/json" \ -d @./payload.json 

现在,您可以使用必要的参数将其调用添加到CI配置中:

 deploy: stage: deploy script: - $SCRIPTS_DIR/deploy.sh production - $SCRIPTS_DIR/grafana-annotation.sh "$VERSION deployed to production" deploy-production 

可以将这些调用放在deploy.sh脚本中,以简化CI配置。

奖励:快速提示


GitLab在所有可用于配置CI的关键字方面都有出色的文档 。 我不想在这里重复其内容,但我会指出一些有用的情况。 单击标题以查看相关文档。

仅/高级使用


通过为CI变量定义模式,可以为某些分支定义自定义程序集。 例如,这可能有助于识别紧急修复的推送修复,但不要滥用它:

 only: refs: - branches variables: - $CI_COMMIT_REF_NAME =~ /^hotfix/ 

GitLab在每个CI作业中都有许多预定义的变量 -请使用它们。

Yaml锚点


使用它们来避免重复。

从11.3版开始,您还可以使用extend关键字

 .common_before_script: &common_before_script before_script: - ... - ... deploy: <<: *common_before_script 

神器排除


默认情况下,管道中收集的所有工件将被转移到所有后续作业。 如果您明确列出作业所依赖的工件,则可以节省时间和磁盘空间:

 dependencies: - build 

或者-相反-如果不需要,则完全跳过所有内容:

 dependencies: [] 

git策略


如果作业不使用这些文件,请跳过存储库克隆:

 variables: GIT_STRATEGY: none 

仅此而已!

感谢您的阅读! 有关反馈和问题,请通过TwitterReddit与我联系。

更多的GitLab技巧可以在以前的文章中找到:


译者的PS


另请参阅我们的博客:

Source: https://habr.com/ru/post/zh-CN436910/


All Articles