CI系统中的Docker标记策略(GitLab)
Posted
技术标签:
【中文标题】CI系统中的Docker标记策略(GitLab)【英文标题】:Docker tagging strategy in CI systems (GitLab) 【发布时间】:2021-12-31 09:41:34 【问题描述】:我们正在使用 GitLab,因此可能会有一些特定于该平台的答案,但我认为这个问题在其他 CI 系统上也同样相关。
在我们的 CI 系统中,我们有一个步骤来构建 Docker 映像。实际上我们有两个不同的工作:
第一个作业将构建一个包含所有外部依赖项的基础映像。此作业仅在 Dockerfile 已更改时才会运行。这很少见。
然后我们有一些使用最新版本的基本映像的测试作业。
如果这也通过了,我们还有第二个工作是使用最新的基础镜像来创建一个已经包含我们的源代码以及需要编译的代码的编译版本的镜像。到目前为止,我们将所有内容都标记为“最新”,但这不是一个好的解决方案。我想知道是否有推荐的方法来标记图像。特别是考虑到我们也开始使用分支和合并请求(GitHub 中的拉取请求),所以我们必须决定如何标记可能已经在分支中创建的图像以及在合并请求期间使用什么?
对此是否有“最佳实践”或有几个“最佳实践”?
【问题讨论】:
我不完全确定这是否是最佳实践,但我们使用短提交 ID (git rev-parse --short HEAD
) 标记图像。根据正在运行的作业,标签以 ci_
为前缀(任何短暂的并在一段时间后自动删除,例如 MR-images)、develop_
(develop 的构建)、staging_
(符合条件的图像运行 E2E-Tests,主要是候选版本)和prod_
(生产分支的构建)。
@Turing85 谢谢。您是否将这些图像用于单元/集成测试?发送 PR 时使用哪张图片?
单元/集成测试在管道中运行(Java 应用程序/maven,不需要额外的基础设施)。对于 E2E-Tests,我们使用staging_
镜像在 K8s 集群中部署类似于生产环境的环境。如果您需要启动一些基础设施进行集成测试,您可以使用ci_
- 图像。但这意味着您需要为每个 MR 部署基础架构,可能是并行的,并在测试通过后销毁它。
当前日期、短提交 ID 或长提交 ID 或 CI 系统构建号都是合理的选择。在我的日常工作中,我们在构建 Docker 映像之前对每个 PR 运行单元测试,然后将每个 PR 部署到测试环境中以运行集成测试。除了“不要使用latest
或其他固定名称”之外,我认为没有单一的“最佳实践”本身。
【参考方案1】:
根据您的问题,我尝试简要解释一下我们如何使用 docker 标记以及如何从管道中引用正确的图像。我们在这方面遇到了很多问题,因为我们使用了多个注册表(包括 GitLab 自己的)。每个环境都必须创建自定义标签并将图像推送到不同的注册表。为了解决所有问题,我们执行了以下操作(希望这些信息对您有所帮助!):
使用 GitLab 变量文件来区分不同环境和注册表的变量 使用 Git 提交 SHA 标记 Docker 映像 在部署文件中使用变量字符串 通过 bash 或节点脚本插入变量使用 GitLab 变量文件
我们总是使用 3 个文件作为管道:
.gitlab-ci-vars.yml .gitlab-ci-jobs.yml .gitlab-ci.yml我们使用它是因为我们可以在每个环境中拥有不同的 docker 注册表。因此,如果我们愿意,现在我们可以轻松地定位我们的 dev、acc 和 prod 注册表。这样可以很好地将 dev、acc 和 prod 图像分开。
例子.gitlab-ci-vars.yml
:
# .gitlab-ci-vars.yml
variables:
FORCE_COLOR: 1
NODE_ENV: dev
CI_DOCKER_NAME: $CI_REGISTRY_IMAGE
.vars-dev:
variables:
NODE_ENV: dev
REGISTRY: my.dev.registry
IMAGE_NAME: $NODE_ENV-api:$CI_COMMIT_SHA
.vars-acc:
variables:
NODE_ENV: acc
REGISTRY: my.acc.registry
IMAGE_NAME: $NODE_ENV-api:$CI_COMMIT_SHA
.vars-prod:
variables:
NODE_ENV: prod
REGISTRY: my.prod.registry
IMAGE_NAME: $NODE_ENV-api:$CI_COMMIT_SHA
.gitlab-jobs.yml
中可重用作业的示例:
# .gitlab-jobs.yml
.publish_gitlab:
stage: publish
image: docker
services:
- name: docker:dind
alias: docker
before_script:
- echo -n $CI_JOB_TOKEN | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
script:
- docker build -t $REGISTRY/$IMAGE_NAME .
- docker push $REGISTRY/$IMAGE_NAME
我们现在可以从.gitlab-ci.yml
调用它,如下所示:
include:
- ".gitlab-ci-vars.yml"
- ".gitlab-ci-jobs.yml"
publish:image:dev:
extends: .publish_gitlab
# target dev env vars
variables: !reference [.vars-dev, variables]
resource_group: dev
environment:
name: dev
only:
- develop
publish:image:acc:
extends: .publish_gitlab
variables: !reference [.vars-acc, variables]
resource_group: acc
environment:
name: acc
only:
- /^release.*$/
publish:image:prod:
extends: .publish_gitlab
variables: !reference [.vars-prod, variables]
resource_group: prod
environment:
name: prod
only:
- master
在部署文件中使用变量字符串
由于标签是随机的(Git SHA),您不知道确切的名称。因此,我们在 K8s 或任何其他文件中使用我们需要使用标签来定位我们的新 docker 映像,我们使用 nodejs
脚本插入的字符串。在这种情况下,我们始终以正确的图像为目标,并且永远不会与其他标签发生冲突。
示例 k8s 文件:
apiVersion: apps/v1
...
spec:
containers:
- name: my-image-name
# target the name we defined in .gitlab-ci-vars.yml
image: __REPO__/__IMAGE_NAME__
ports:
- containerPort: __CONTAINER_PORT__
env:
- name: NODE_ENV
value: __NODE_ENV__
我们现在通过在应用部署(或运行测试或其他任何方式)之前覆盖它们来配置所有这些变量。
const fs = require('fs');
const path = require('path');
const
NODE_ENV,
REGISTRY,
IMAGE_NAME,
= process.env
const DEFAULT_PROVISION_MAP =
__REPO__: REGISTRY,
__IMAGE_NAME__: IMAGE_NAME,
__CONTAINER_PORT__: 3000,
__NODE_ENV__: NODE_ENV,
async function provisionK8sFiles()
const K8S_TEMPLATE_DIR = 'k8s';
const DIR = path.join('gitlab', K8S_TEMPLATE_DIR);
const K8S_FILES = await fs.promises.readdir(DIR);
const MAPPING =
...DEFAULT_PROVISION_MAP,
for (const file of K8S_FILES)
const filePath = path.join(DIR, file);
let content = await fs.promises.readFile(filePath,
encoding: 'utf-8'
);
content = Object.keys(MAPPING).reduce((acc, curr) =>
return acc.replace(new RegExp(curr, 'g'), MAPPING[curr])
, content)
fs.writeFileSync(filePath, content, 'utf8')
provisionK8sFiles()
为了更新管道中的所有变量,我们使用before_script
来定位正确的图像。
before_script:
- node ./bin/provision-k8s.js
结论
尝试使一切动态化,并通过 bash 或节点脚本插入正确的图像和 sha。
【讨论】:
以上是关于CI系统中的Docker标记策略(GitLab)的主要内容,如果未能解决你的问题,请参考以下文章
我需要通过 gitlab-ci 中的 ssh 将 env 变量传递给 docker