Kubernetes 执行器

使用 Kubernetes 执行器为您的构建使用 Kubernetes 集群。执行器调用 Kubernetes 集群 API 并为每个极狐GitLab CI 作业创建 Pod。

工作流

Kubernetes 执行器将构建分为以下步骤:

  1. 准备:创建与 Kubernetes 集群相关的 Pod。 创建运行构建和服务所需的容器。
  2. 构建前:克隆、还原缓存并从之前阶段下载产物。此步骤作为 Pod 的一部分在特殊容器上运行。
  3. 构建:用户构建。
  4. 构建后:创建缓存,向极狐GitLab 上传产物。此步骤也将特殊容器用作 Pod 的一部分。

Runner 如何创建 Kubernetes Pod

下图显示了极狐GitLab 实例与托管在 Kubernetes 集群上的 Runner 之间的交互。Runner 调用 Kubernetes API 在集群上创建 Pod。

对于在 .gitlab-ci.ymlconfig.toml 文件中定义的每个 service,Pod 都由以下容器组成:

  • 定义为 build 的构建容器。
  • 定义为 helper 的 helper 容器。
  • 定义为 svc-X 的服务容器,其中 X[0-9]+

服务和容器运行在同一个 Kubernetes Pod 中并共享相同的本地主机地址。以下限制适用:

  • 在极狐GitLab Runner 12.8 和 Kubernetes 1.7 及更高版本中,可以通过其 DNS 名称访问服务。如果您使用的是旧版本,则必须使用 localhost
  • 您使用的服务不能使用同一端口。例如,您不能同时使用两个 mysql 服务。
sequenceDiagram participant G as GitLab instance participant R as Runner on Kubernetes cluster participant Kube as Kubernetes API participant P as POD R->>+G: Get a CI job. loop G-->R: ; end Note over R,G: POST /api/v4/jobs/request G->>+R: CI job data. R-->>-Kube: Create a POD to run the CI job. Note over R,Kube: POST to Kube API P->>+P: Execute job. Note over P: CI build job = Prepare + Pre-build + Build + Post-build P->>+G: Job logs

图中的交互对任何 Kubernetes 集群都有效。例如,托管在主要公共云提供商或私有化部署的 Kubernetes 安装上的 turnkey 解决方案。

连接到 Kubernetes API

使用以下选项连接到 Kubernetes API。提供的用户帐户必须具有在指定命名空间中创建、列出和附加到 Pod 的权限。

选项 描述
host 可选的 Kubernetes apiserver 主机 URL(如未指定,则尝试自动发现
cert_file 可选的 Kubernetes apiserver 用户授权证书
key_file 可选的 Kubernetes apiserver 用户授权私有 key
ca_file 可选的 Kubernetes apiserver ca 证书

如果您在 Kubernetes 集群中运行极狐GitLab Runner,您应该省略所有这些字段,以便极狐GitLab Runner 自动发现 Kubernetes API。

如果您在集群外部运行极狐GitLab Runner,则必须设置每个设置并确保极狐GitLab Runner 可以在集群上访问 Kubernetes API。

配置设置

使用 config.toml 文件中的以下设置配置 Kubernetes 执行器。

CPU 请求和限制

设置 描述
cpu_limit 构建容器所需的 CPU 分配。
cpu_limit_overwrite_max_allowed 为构建容器可写入的最大的 CPU 分配量。如果为空,则禁用了 CPU 限制覆盖功能。
cpu_request 构建容器所请求的 CPU 分配。
cpu_request_overwrite_max_allowed 为构建容器可写入的最大的 CPU 分配请求量。如果为空,则禁用了 CPU 请求覆盖功能。
helper_cpu_limit 给予构建 Helper 容器的 CPU 分配。
helper_cpu_limit_overwrite_max_allowed Helper 容器可写入的最大的 CPU 分配量。如果为空,则禁用了 CPU 限制覆盖功能。
helper_cpu_request 构建 Helper 容器所需的 CPU 分配。
helper_cpu_request_overwrite_max_allowed Helper 容器可写入的最大的 CPU 分配请求量。如果为空,则禁用了 CPU 请求覆盖功能。
service_cpu_limit 给予构建服务容器的 CPU 分配。
service_cpu_limit_overwrite_max_allowed 服务容器可写入的最大的 CPU 分配量。如果为空,则禁用了 CPU 限制覆盖功能。
service_cpu_request 构建服务容器所请求的 CPU 分配。
service_cpu_request_overwrite_max_allowed 服务容器可写入的最大的 CPU 分配请求量。如果为空,则禁用了 CPU 请求覆盖功能。

内存请求和限制

设置 描述
memory_limit 分配给构建容器的内存量。
memory_limit_overwrite_max_allowed 构建容器可写入的最大的内存分配量。如果为空,则禁用了内存限制覆盖功能。
memory_request 从构建容器请求的内存量。
memory_request_overwrite_max_allowed 构建容器可写入的最大的内存分配请求量。如果为空,则禁用了内存请求覆盖功能。
helper_memory_limit 构建 Helper 容器所分配的内存量。
helper_memory_limit_overwrite_max_allowed Helper 容器可写入的最大的内存分配量。如果为空,则禁用了内存限制覆盖功能。
helper_memory_request 构建 Helper 容器所请求的内存量。
helper_memory_request_overwrite_max_allowed Helper 容器可写入的最大的内存分配请求量。如果为空,则禁用了内存请求覆盖功能。
service_memory_limit 构建服务容器所分配的内存量。
service_memory_limit_overwrite_max_allowed 服务容器可写入的最大的内存分配量。如果为空,则禁用了内存限制覆盖功能。
service_memory_request 构建服务容器所请求的内存量。
service_memory_request_overwrite_max_allowed 服务容器可写入的最大的内存分配请求量。如果为空,则禁用了内存请求覆盖功能。

存储请求和限制

设置 描述
ephemeral_storage_limit 构建容器的临时存储限制。
ephemeral_storage_limit_overwrite_max_allowed 可以覆盖的构建容器的最大临时存储限制。如果为空,则禁用了临时存储限制覆盖功能。
ephemeral_storage_request 给予构建容器的临时存储请求。
ephemeral_storage_request_overwrite_max_allowed 构建容器可被覆盖的最大临时存储请求量。如果为空,则禁用了临时存储请求覆盖功能。
helper_ephemeral_storage_limit 给予构建容器的临时存储限制。
helper_ephemeral_storage_limit_overwrite_max_allowed Helper 容器可被覆盖的最大临时存储限制。如果为空,则禁用了临时存储请求覆盖功能。
helper_ephemeral_storage_request 给予 Helper 容器的临时存储请求。
helper_ephemeral_storage_request_overwrite_max_allowed Helper 容器可被覆盖的最大临时存储请求量。如果为空,则禁用了临时存储请求覆盖功能。
service_ephemeral_storage_limit 给予服务容器的临时存储限制。
service_ephemeral_storage_limit_overwrite_max_allowed 服务容器可被覆盖的最大临时存储限制。如果为空,则禁用了临时存储请求覆盖功能。
service_ephemeral_storage_request 给予服务容器的临时存储请求。
service_ephemeral_storage_request_overwrite_max_allowed 服务容器可被覆盖的最大临时存储请求量。如果为空,则禁用了临时存储请求覆盖功能。

其他 config.toml 设置

设置 描述
affinity 指定运行构建的节点的亲和规则。阅读更多关于使用亲和的内容。
allow_privilege_escalation 启用 allowPrivilegeEscalation 标志,运行所有容器。如果为空,它不定义 SecurityContext 容器中的 allowPrivilegeEscalation 标志并且允许 Kubernetes 使用默认的特权升级行为。
allowed_images .gitlab-ci.yml 中可指定的镜像的通配符列表。如果不存在,则允许所有镜像(等同于 ["*/*:*"])。查看详情
allowed_pull_policies 可以在 .gitlab-ci.yml 文件或 config.toml 文件中指定的拉取策略列表。
allowed_services .gitlab-ci.yml 中可以指定的服务的通配符列表。如果不存在,则允许所有镜像(等同于 ["*/*:*"])。查看详情
bearer_token 用于发布构建 Pod 的默认承载令牌。
bearer_token_overwrite_allowed 允许项目指定用于创建构建 Pod 的持有者令牌的布尔。
cap_add 指定应该被添加到作业 Pod 容器的 Linux 能力。阅读更多关于 Kubernetes 执行器中的能力配置的内容
cap_drop 指定应该从作业 Pod 容器 移除的 Linux 能力。阅读更多关于 Kubernetes 执行器中的能力配置的内容
cleanup_grace_period_seconds 作业完成后,Pod 优雅关闭的时间(秒)。关闭时间之后,使用停止信号强制停止进程。如果指定了 terminationGracePeriodSeconds,则忽略它。
helper_container_security_context 为 Helper 容器设置容器安全上下文。阅读更多安全上下文的内容
helper_image (高级)覆盖默认 Helper 镜像,该镜像用于克隆仓库和上传产物。
helper_image_flavor 设置 Helper 镜像类型 (alpinealpine3.15alpine3.16alpine3.17alpine3.18ubuntu)。默认为 alpine。使用 alpine 与使用 alpine3.18 相同。
host_aliases 添加到所有容器的额外主机别名列表。阅读更多关于使用额外主机别名的内容。
image_pull_secrets 包含用于验证从私有镜像库拉取的 Docker 镜像的 Kubernetes docker-registry 秘密名称的项目阵列。
namespace 运行 Kubernetes Pod 的命名空间。
namespace_overwrite_allowed 验证命名空间覆盖环境变量内容的正则表达式(以下记录)。如果为空,则禁用了命名空间覆盖功能。
node_selector string=string(环境变量的 string:string)形式的 key=value 对的 table。对其进行设置限制了向匹配所有 key=value 对的 Kubernetes 节点的 Pod 创建。阅读更多使用节点选择器的内容
node_tolerations string=string:string 形式的 "key=value" = "Effect" 对的 table。对其进行设置允许 Pod 使用所有或一子组的容忍污点。仅允许通过环境变量配置提供一个容忍。keyvalueeffect 与 Kubernetes Pod 容忍配置中的对应字段名称相匹配。
pod_annotations string=string 形式的 key=value 对的 table。这是添加到 Runner 创建的每个构建 Pod 的标注列表。为进行扩展,这些的值可以包括环境变量。在每个构建中,Pod 标注可被覆盖。
pod_annotations_overwrite_allowed 验证 Pod 标注覆盖环境变量内容的正则表达式。如果为空,则禁用了 Pod 标注覆盖功能。
pod_labels key=value 对的 table,格式为 string=string。是要添加到 Runner 创建的每个构建 pod 的标志列表。这些值可以包括用于扩展的环境变量。可以使用 pod_labels_overwrite_allowed 在每个构建中覆盖 Pod 标志。
pod_labels_overwrite_allowed 用于验证 Pod 标记内容覆盖环境变量的正则表达式。为空时,它会禁用 pod 标记覆盖功能。
pod_security_context 通过配置文件进行配置。为构建 Pod 设置了 Pod 安全上下文。阅读更多安全上下文的内容
init_permissions_container_security_context 为 init-permissions 容器设置容器安全上下文。 阅读更多安全上下文的内容
build_container_security_context 为构建容器设置容器安全上下文。阅读更多安全上下文的内容
helper_container_security_context 为 Helper 容器设置容器安全上下文。 阅读更多安全上下文的内容
service_container_security_context 为服务容器设置容器安全上下文。阅读更多安全上下文的内容
pod_termination_grace_period_seconds Pod 级别的设置,决定 Pod 优雅关闭的时间(秒)。在这之后,使用停止信号强制停止进程。如果指定了 terminationGracePeriodSeconds,则忽略。
poll_interval Runner 轮询它刚刚创建的 Kubernetes Pod 的频率(秒),以检查其状态(默认 = 3)。
poll_timeout Runner 试图连接它刚刚创建的容器的超时时间前需要传递的时间(秒)。在为超过集群一次可以处理的构建进行排队时有用。(默认 = 180)。
resource_availability_check_max_attempts 检查资源(服务账户和/或拉取密钥)组在放弃前是否可用所进行的尝试的最多次数。两次尝试中间间隔 5 秒(默认为 5)。
privileged 使用特权标记运行容器。
runtime_class_name 为所有创建的 Pod 所使用的运行时类。如果集群不支持这个功能,作业会退出或失败。
pull_policy 指定镜像拉取策略:neverif-not-presentalways。如果没有设置,会使用集群的镜像默认拉取策略。关于如何设置多种拉取策略,请参见使用拉取策略。您也可以参考if-not-presentnever 安全考虑。您也可以限制拉取策略
scheduler_name 用于调度构建 pod 的调度器。
service_account Pod 使用的与 Kubernetes API 通信的默认服务账户作业/执行器。
service_account_overwrite_allowed 验证服务账户覆盖环境变量内容的正则表达式。如果为空,则禁用了服务账户覆盖功能。
services 自极狐GitLab Runner 12.5 起,使用边车 (https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar)模式附着到构建容器的服务列表。阅读更多关于使用服务的内容。
terminationGracePeriodSeconds 向运行在 Pod 中的进程发送结束信号之后的时长和使用停止信号强制停止进程的时间。
volumes 通过配置文件进行配置,将要挂载到构建容器的卷列表。阅读更多使用卷的内容
dns_policy 指定构建 Pod 时应该使用的 DNS 策略:nonedefaultcluster-firstcluster-first-with-host-net。如果没有设置,将使用 Kubernetes 默认的 (cluster-first)。
dns_config 指定构建 Pod 时应该使用的 DNS 配置。阅读更多使用 Pod DNS 配置的内容
priority_class_name 指定要设置到 Pod 的优先级。如果未设置,将使用默认值。
pod_spec 此设置处于 Alpha 阶段。使用在用于运行 CI 作业的 Pod 上设置的配置列表覆盖 Runner Manager 生成的 Pod 规范。可以设置 Kubernetes Pod Specification 中列出的所有参数。有关详细信息,请参阅覆盖生成的 Pod 规范(Alpha)

覆盖生成的 Pod 规范 (Alpha)

引入于极狐GitLab Runner 15.10。

此功能处于 Alpha 阶段。我们强烈建议您先在测试 Kubernetes 集群上使用此功能,然后再将其用于生产集群。要使用此功能,您必须启用 FF_USE_ADVANCED_POD_SPEC_CONFIGURATION 功能标志

要修改 Runner Manager 生成的 PodSpec,请使用 config.toml 文件中的 pod_spec 设置。

pod_spec 设置覆盖并完成生成的 Pod 规范的字段。您可以配置多个 pod_spec 设置。

设置 描述
name 为自定义 pod_spec 指定的名称
patch_path 定义在生成前应用于最终 PodSpec 对象的更改的文件路径。该文件必须是 JSON 或 YAML 文件
patch JSON 或 YAML 格式字符串,描述在生成前必需应用到最终的 PodSpec 对象的更改
patch_type Runner 用于将指定更改应用于极狐GitLab Runner 生成的 PodSpec 对象的策略。可接受的值为 mergejsonstrategic

不能在同一个 pod_spec 配置中设置 patch_pathpatch ,否则会报错。

config.toml 中的多个 pod_spec 配置示例:

[[runners]]
  [runners.kubernetes]
    [[runners.kubernetes.pod_spec]]
      name = "hostname"
      patch = '''
        hostname: "custom-pod-hostname"
      '''
      patch_type = "merge"
    [[runners.kubernetes.pod_spec]]
      name = "subdomain"
      patch = '''
        subdomain: "subdomain"
      '''
      patch_type = "strategic"
    [[runners.kubernetes.pod_spec]]
      name = "terminationGracePeriodSeconds"
      patch = '''
        [{"op": "replace", "path": "/terminationGracePeriodSeconds", "value": 60}]
      '''
      patch_type = "json"

合并补丁策略

merge 补丁策略在现有的 PodSpec 上应用键值替换。 如果您使用此策略,则 config.toml 中的 pod_spec 配置会在生成之前覆盖最终 PodSpec 对象中的值。因为值被完全覆盖,所以您应该谨慎使用此补丁策略。

以下是使用 merge 补丁策略的 pod_spec 配置示例:

concurrent = 1
check_interval = 1
log_level = "debug"
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = ""
  url = "https://gitlab.example.com"
  id = 0
  token = "__REDACTED__"
  token_obtained_at = 0001-01-01T00:00:00Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "kubernetes"
  shell = "bash"
  environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"]
  [runners.kubernetes]
    image = "alpine"
    ...
    [[runners.kubernetes.pod_spec]]
      name = "build envvars"
      patch = '''
        containers:
        - env:
          - name: env1
            value: "value1"
          - name: env2
            value: "value2"
          name: build
      '''
      patch_type = "merge"

通过此配置,最终的 PodSpec 只有一个名为 build 的容器,且带有两个环境变量 env1env2。上面的示例会让相关的 CI 作业失败,因为:

  • 删除了 helper 容器规范。
  • build 容器规范丢失了极狐GitLab Runner 设置的所有必要配置。

为了防止作业失败,在此示例中,pod_spec 必须包含极狐GitLab Runner 生成的未修改的参数。

JSON 补丁策略

json 补丁策略使用 JSON 补丁规范来控制要更新的 PodSpec 对象和数组。您不能在 array 参数上使用此策略。

以下是使用 json 补丁策略的 pod_spec 配置示例。在此配置中,一个新的 key: value pair 被添加到现有的 nodeSelector 中。现有值不会被覆盖。

concurrent = 1
check_interval = 1
log_level = "debug"
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = ""
  url = "https://gitlab.example.com"
  id = 0
  token = "__REDACTED__"
  token_obtained_at = 0001-01-01T00:00:00Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "kubernetes"
  shell = "bash"
  environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"]
  [runners.kubernetes]
    image = "alpine"
    ...
    [[runners.kubernetes.pod_spec]]
      name = "val1 node"
      patch = '''
        { "op": "add", "path": "/nodeSelector", "value": { key1: "val1" } }
      '''
      patch_type = "json"

Strategic 补丁策略

strategic 补丁策略使用应用于 PodSpec 对象的每个字段的现有 patchStrategy

以下是使用 json 补丁策略的 pod_spec 配置示例。在此配置中,构建容器上设置了 resource request

concurrent = 1
check_interval = 1
log_level = "debug"
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = ""
  url = "https://gitlab.example.com"
  id = 0
  token = "__REDACTED__"
  token_obtained_at = 0001-01-01T00:00:00Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "kubernetes"
  shell = "bash"
  environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"]
  [runners.kubernetes]
    image = "alpine"
    ...
    [[runners.kubernetes.pod_spec]]
      name = "cpu request 500m"
      patch = '''
        containers:
        - name: build
          resources:
            requests:
              cpu: "500m"
      '''
      patch_type = "strategic"

使用此配置,在构建容器上设置了 resource request

最佳实践

  • 在生产环境中部署之前,需要在测试环境中测试添加的 pod_spec
  • 确保 pod_spec 配置不会对极狐GitLab Runner 生成的规范产生负面影响。
  • 不要对复杂的 Pod 规范更新使用 merge 补丁策略。
  • 如果可能,在配置可用时使用 config.toml。例如,以下配置将极狐GitLab Runner 设置的第一个环境变量替换为自定义 pod_spec 中设置的环境变量,而不是将环境变量集添加到现有列表中。
concurrent = 1
check_interval = 1
log_level = "debug"
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = ""
  url = "https://gitlab.example.com"
  id = 0
  token = "__REDACTED__"
  token_obtained_at = 0001-01-01T00:00:00Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "kubernetes"
  shell = "bash"
  environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"]
  [runners.kubernetes]
    image = "alpine"
    ...
    [[runners.kubernetes.pod_spec]]
      name = "build envvars"
      patch = '''
        containers:
        - env:
          - name: env1
            value: "value1"
          name: build
      '''
      patch_type = "strategic"

通过修改 Pod Spec 为每个构建作业创建 PVC

要为每个构建作业创建 PersistentVolumeClaim,请务必查看如何启用 Pod Spec 功能

Kubernetes 允许创建附加到 Pod 生命周期的临时 PersistentVolumeClaim。 如果在 Kubernetes 集群上启用了动态配置,允许每个 PVC 请求新的,卷也将与 Pod 的生命周期相关联。

启用动态配置后,可以按如下方式修改 config.toml 来创建临时 PVC

[[runners.kubernetes.pod_spec]]
  name = "ephemeral-pvc"
  patch = '''
    containers:
    - name: build
      volumeMounts:
      - name: builds
        mountPath: /builds
    - name: helper
      volumeMounts:
      - name: builds
        mountPath: /builds
    volumes:
    - name: builds
      ephemeral:
        volumeClaimTemplate:
          spec:
            storageClassName: <The Storage Class that will dynamically provision a Volume>
            accessModes: [ ReadWriteOnce ]
            resources:
              requests:
                storage: 1Gi
  '''

Pod 生命周期

Pod 的生命周期可能会受到以下因素的影响:

  • TOML 配置文件中设置 pod_termination_grace_period_seconds 参数。 在发送 TERM 信号后,在 Pod 上运行的进程可以运行给定时间。 如果 Pod 在此时间段后未成功终止,则会发送终止信号。
  • 启用 FF_USE_POD_ACTIVE_DEADLINE_SECONDS 功能标志。 当启用并且作业超时后,运行 CI/CD 作业的 Pod 会被标记为失败并且所有关联的容器都被终止。如果想先让极狐GitLab 中的作业超时,请将 activeDeadlineSeconds 设置为 configured timeout + 1 second
note如果启用了 FF_USE_POD_ACTIVE_DEADLINE_SECONDS 功能标志并且 pod_termination_grace_period_seconds 设置为非零值,作业超时的时候,CI 作业 Pod 不会立即终止。 Pod terminationGracePeriods 确保 Pod 仅在其过期时终止。

作业 Pod 的默认注释

引入于极狐GitLab Runner 15.9。

默认情况下,在运行作业的 Pod 上添加以下注释:

Key 说明
job.runner.gitlab.com/id 作业 ID,在极狐GitLab 实例的所有作业中是唯一的。
job.runner.gitlab.com/url 作业详细信息的 URL。
job.runner.gitlab.com/sha 构建项目的提交修订。
job.runner.gitlab.com/before_sha 分支或标签上的最后一次提交。
job.runner.gitlab.com/ref 构建项目的分支或标签名称。
job.runner.gitlab.com/name 作业名称。
project.runner.gitlab.com/id 作业的项目 ID。

要覆盖默认注释,请在极狐GitLab Runner 配置中使用 pod_annotations。 您还可以在 .gitlab-ci.yml 文件中覆盖每个 CI/CD 作业的注释。

配置执行器服务账户

您可以设置 KUBERNETES_SERVICE_ACCOUNT 环境变量或者使用 --kubernetes-service-account 标记。

覆盖 Kubernetes 命名空间

先决条件:

  • 在极狐GitLab Runner Helm Chart 的 values.yml 文件中,rbac.clusterWideAccess 设置为 true
  • Runner 在核心 API 群组中配置了权限

您可以覆盖 Kubernetes 命名空间以指定用于 CI 目的的命名空间,并向其部署自定义的 Pod 组。 Runner 生成的 Pod 位于被覆盖的命名空间中,以在 CI 阶段启用容器之间的访问。

要覆盖每个 CI/CD 作业的 Kubernetes 命名空间,请在 .gitlab-ci.yml 文件中设置 KUBERNETES_NAMESPACE_OVERWRITE 参数。

variables:
  KUBERNETES_NAMESPACE_OVERWRITE: ci-${CI_COMMIT_REF_SLUG}
note此变量不会在您的集群上创建命名空间。请在运行作业之前确保命名空间存在。

要在 CI 运行期间仅使用指定的命名空间,请在 config.toml 文件中为 namespace_overwrite_allowed 定义一个正则表达式:

[runners.kubernetes]
    ...
    namespace_overwrite_allowed = "ci-.*"

配置 Runner API 权限

要为核心 API 群组配置权限,请更新极狐GitLab Runner Helm Chart 的 values.yml 文件。

您可以:

  • rbac.create 设置为 true
  • values.yml 文件中指定具有以下权限的服务帐户 rbac.serviceAccountName: <service_account_name>

    • 对于 exec strategy

      资源 权限
      pods/exec 创建、打补丁、删除
      pods 获取、列出、查看、创建、打补丁、删除
      services 获取、列出、查看、创建、打补丁、删除
      secrets 获取、列出、查看、创建、更新、打补丁、删除
      serviceAccounts 获取(1)
    • 对于 attach strategy

      资源 权限
      pods/attach 创建、打补丁、删除
      pods/exec 创建、打补丁、删除
      pods 获取、查看、创建、删除
      services 获取、查看、创建、删除
      configmaps 获取、创建、更新、删除
      secrets 获取、创建、更新、删除
      serviceAccounts 获取(1)

(1) 仅在以下情况需要 serviceAccount 权限:

  • 15.0 和 15.1 版本中
  • 15.0.1、15.1.1 和 15.2 中,当 resource_availability_check_max_attempts 的值大于 0

覆盖 Kubernetes 默认服务账户

要覆盖 .gitlab-ci.yml 文件中每个 CI/CD 作业的 Kubernetes 服务帐户,请设置变量 KUBERNETES_SERVICE_ACCOUNT_OVERWRITE

您可以使用此变量指定附加到命名空间的服务帐户,复杂的 RBAC 配置可能需要使用。

variables:
  KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: ci-service-account

为确保在 CI 运行期间仅使用指定的服务帐户,为以下任意一项定义一个正则表达式:

  • service_account_overwrite_allowed 设置。
  • KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED 环境变量。

如果您都不设置,则覆盖将被禁用。

为 Kubernetes API 调用设置承载令牌

要为创建 Pod 的 API 调用设置承载令牌,请使用 KUBERNETES_BEARER_TOKEN 变量。 这允许项目所有者使用项目 secret 变量来指定承载令牌。

指定承载令牌时,您必须设置 Host 配置设置。

variables:
  KUBERNETES_BEARER_TOKEN: thebearertokenfromanothernamespace

覆盖 Pod 标记

为每个 CI/CD 作业覆盖 Kubernetes Pod 标记:

  1. .config.yaml 文件中,为 pod_labels_overwrite_allowed 定义一个正则表达式。
  2. .gitlab-ci.yml 文件中,将 KUBERNETES_POD_LABELS_* 变量赋值为 key=value。 Pod 标记被覆盖为 key=value。您可以应用多个值:

     variables:
       KUBERNETES_POD_LABELS_1: "Key1=Val1"
       KUBERNETES_POD_LABELS_2: "Key2=Val2"
       KUBERNETES_POD_LABELS_3: "Key3=Val3"
    

覆盖 Pod 注释

要为每个 CI/CD 作业覆盖 Kubernetes Pod 注释:

  1. .config.yaml 文件中,为 pod_annotations_overwrite_allowed 定义一个正则表达式。
  2. .gitlab-ci.yml 文件中,设置 KUBERNETES_POD_ANNOTATIONS_* 变量并使用 key=value 作为值。 Pod 注释被覆盖到 key=value。您可以指定多个注释:
   variables:
     KUBERNETES_POD_ANNOTATIONS_1: "Key1=Val1"
     KUBERNETES_POD_ANNOTATIONS_2: "Key2=Val2"
     KUBERNETES_POD_ANNOTATIONS_3: "Key3=Val3"

覆盖容器资源

您可以为每个 CI/CD 作业覆盖 Kubernetes CPU 和内存分配。您可以为构建、Helper 和服务容器应用请求和限制设置。

要覆盖容器资源,请在 .gitlab-ci.yml 文件中使用以下变量。

变量的值仅限于该资源的最大覆盖设置。如果没有为资源设置最大覆盖,则不使用该变量。

 variables:
   KUBERNETES_CPU_REQUEST: "3"
   KUBERNETES_CPU_LIMIT: "5"
   KUBERNETES_MEMORY_REQUEST: "2Gi"
   KUBERNETES_MEMORY_LIMIT: "4Gi"
   KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "512Mi"
   KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "1Gi"

   KUBERNETES_HELPER_CPU_REQUEST: "3"
   KUBERNETES_HELPER_CPU_LIMIT: "5"
   KUBERNETES_HELPER_MEMORY_REQUEST: "2Gi"
   KUBERNETES_HELPER_MEMORY_LIMIT: "4Gi"
   KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST: "512Mi"
   KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT: "1Gi"

   KUBERNETES_SERVICE_CPU_REQUEST: "3"
   KUBERNETES_SERVICE_CPU_LIMIT: "5"
   KUBERNETES_SERVICE_MEMORY_REQUEST: "2Gi"
   KUBERNETES_SERVICE_MEMORY_LIMIT: "4Gi"
   KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST: "512Mi"
   KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT: "1Gi"

配置示例

以下是 Kubernetes 执行器的 config.toml 文件的示例配置。

concurrent = 4

[[runners]]
  name = "myRunner"
  url = "https://gitlab.com/ci"
  token = "......"
  executor = "kubernetes"
  [runners.kubernetes]
    host = "https://45.67.34.123:4892"
    cert_file = "/etc/ssl/kubernetes/api.crt"
    key_file = "/etc/ssl/kubernetes/api.key"
    ca_file = "/etc/ssl/kubernetes/ca.crt"
    namespace = "gitlab"
    namespace_overwrite_allowed = "ci-.*"
    bearer_token_overwrite_allowed = true
    privileged = true
    cpu_limit = "1"
    memory_limit = "1Gi"
    service_cpu_limit = "1"
    service_memory_limit = "1Gi"
    helper_cpu_limit = "500m"
    helper_memory_limit = "100Mi"
    poll_interval = 5
    poll_timeout = 3600
    dns_policy = "cluster-first"
    [runners.kubernetes.node_selector]
      gitlab = "true"
    [runners.kubernetes.node_tolerations]
      "node-role.kubernetes.io/master" = "NoSchedule"
      "custom.toleration=value" = "NoSchedule"
      "empty.value=" = "PreferNoSchedule"
      "onlyKey" = ""

准备步骤的资源检查

  • 引入于极狐GitLab 15.0。
  • 更新于极狐GitLab 15.2。

先决条件:

  • 设置 image_pull_secretsservice_account
  • resource_availability_check_max_attempts 设置为大于零的数字。
  • Kubernetes serviceAccountgetlist 权限一起使用。

极狐GitLab Runner 每 5 秒钟检查一次新的服务账户或密钥是否可用。

  • 在 15.0 和 15.1 中,您无法禁用此功能,当设置负值时它默认为 5
  • 在 15.0.1、15.1.1、15.2 及更高版本中,此功能默认处于禁用状态。要启用此功能,请将 resource_availability_check_max_attempts 设置为 0 以外的任何值。 您设置的值定义 Runner 检查服务帐户或 secret 的次数。

通过 Kubernetes 执行器使用缓存

当缓存与 Kubernetes 执行器一起使用时,一个名为 /cache 的卷会挂载在 Pod 上。在作业执行期间,如果需要缓存数据,Runner 会检查缓存数据是否可用。如果压缩文件在缓存卷上可用,则缓存数据可用。

要设置缓存卷,请使用 config.toml 文件中的 cache_dir 设置。

  • 如果可用,压缩文件会被提取到构建文件夹,并且可以用在作业中。
  • 如果不可用,从配置存储下载缓存数据并作为压缩文件保存到 cache dir 中。 然后压缩文件被提取到 build 文件夹中。

配置卷类型

您可以挂载以下类型的卷:

  • hostPath
  • persistentVolumeClaim
  • configMap
  • secret
  • emptyDir
  • csi

具有多种卷类型的配置示例:

concurrent = 4

[[runners]]
  # usual configuration
  executor = "kubernetes"
  [runners.kubernetes]
    [[runners.kubernetes.volumes.host_path]]
      name = "hostpath-1"
      mount_path = "/path/to/mount/point"
      read_only = true
      host_path = "/path/on/host"
    [[runners.kubernetes.volumes.host_path]]
      name = "hostpath-2"
      mount_path = "/path/to/mount/point_2"
      read_only = true
    [[runners.kubernetes.volumes.pvc]]
      name = "pvc-1"
      mount_path = "/path/to/mount/point1"
    [[runners.kubernetes.volumes.config_map]]
      name = "config-map-1"
      mount_path = "/path/to/directory"
      [runners.kubernetes.volumes.config_map.items]
        "key_1" = "relative/path/to/key_1_file"
        "key_2" = "key_2"
    [[runners.kubernetes.volumes.secret]]
      name = "secrets"
      mount_path = "/path/to/directory1"
      read_only = true
      [runners.kubernetes.volumes.secret.items]
        "secret_1" = "relative/path/to/secret_1_file"
    [[runners.kubernetes.volumes.empty_dir]]
      name = "empty-dir"
      mount_path = "/path/to/empty_dir"
      medium = "Memory"
    [[runners.kubernetes.volumes.csi]]
      name = "csi-volume"
      mount_path = "/path/to/csi/volume"
      driver = "my-csi-driver"
      [runners.kubernetes.volumes.csi.volume_attributes]
        size = "2Gi"

hostPath

配置 hostPath以指导 Kubernetes 在容器中挂载指定的主机路径。

config.toml 文件中使用以下选项:

选项 类型 必须 描述
name 字符串 卷的名称。
mount_path 字符串 卷应该挂载的容器内部的路径。
sub_path 字符串 在卷而不是根的内部挂载子路径
host_path 字符串 挂载为卷的主机路径。如果未指定,则将其设置为与 mount_path 相同的路径。
read_only 布尔 以只读模式设置卷(默认为 False)。

persistentVolumeClaim

配置 persistentVolumeClaim以指导 Kubernetes 使用 Kubernetes 集群中定义的 persistentVolumeClaim 并将其挂载到容器中。

config.toml 文件中使用以下选项::

选项 类型 必须 描述
name 字符串 卷的名称,同时也是应该使用的 PersistentVolumeClaim 的名称。
mount_path 字符串 挂载卷的容器内部的路径。
read_only 布尔 将卷设置为只读模式(默认为 False。
sub_path 字符串 在卷而不是根内部挂载子路径

configMap

配置 configMap 卷以指导 Kubernetes 使用 Kubernetes 集群中定义的 configMap 并将其挂载到容器中。

config.toml 中使用以下选项:

选项 类型 必须 描述
name 字符串 卷的名称,同时也是应该使用的configMap的名称。
mount_path 字符串 挂载卷的容器内部的路径。
read_only 布尔 以只读模式设置卷(默认为 False)。
sub_path 字符串 在卷而不是根内部挂载子路径
items map[string]string 从应该使用的 configMap 进行密钥 Key-to-Path 映射。

configMap 中的每个 key 都被更改为一个文件并存储在挂载路径中。默认情况下:

  • 包括所有 key。
  • configMap key 用作文件名。
  • 该值存储在文件内容中。

要更改默认 key 和值存储,请使用 items 选项。如果您使用 items 选项,只有指定的 key会被添加到卷中,所有其他 key 都会被跳过。

note如果您使用不存在的 key,作业将在 Pod 创建阶段失败。

secret

配置一个 secret 以指导 Kubernetes 使用 Kubernetes 集群中定义的 secret 并将其挂载到容器中。

config.toml 文件中使用以下选项:

选项 类型 必须 描述
name 字符串 卷的名称,同时也是应该使用的 Secret 的名称。
mount_path 字符串 应该挂载卷的容器内部的路径。
read_only 布尔 以只读模式设置卷(默认为 False)。
sub_path 字符串 在卷而不是根内部挂载子路径
items map[string]string 从应该使用的 configMap 进行密钥 Key-to-Path 映射。

secret 中的每个 key 都被更改为一个文件并存储在选中的挂载路径中。默认情况下:

  • 包括所有 key。
  • configMap key 用作文件名。
  • 该值存储在文件内容中。

要更改默认 key 和值存储,请使用 items 选项。如果您使用 items 选项,只有指定的 key会被添加到卷中,所有其他 key 都会被跳过。

note如果您使用不存在的 key,作业将在 Pod 创建阶段失败。

emptyDir

配置一个 emptyDir以指导 Kubernetes 在容器中挂载一个空目录。

config.toml 文件中使用以下选项:

选项 类型 必须 描述
name 字符串 卷的名称。
mount_path 字符串 应该挂载卷的容器内部的路径。
sub_path 字符串 在卷而不是根内部挂载子路径
medium 字符串 “内存”会提供 tmpfs,否则默认为节点磁盘存储(默认为 ““)。

csi

配置容器存储接口 (csi) 卷以指导 Kubernetes 使用自定义的 csi 驱动程序挂载容器中的任意存储系统。

config.toml 中使用以下选项:

选项 类型 必须 描述
name 字符串 卷的名称。
mount_path 字符串 应该挂载卷的容器内部的路径。
driver 字符串 指定使用的卷驱动名称的字符串值。
fs_type 字符串 指定文件系统类型名称的字符串值(例如,”ext4”、”xfs”、”ntfs”)。
volume_attributes map[string]string CSI 卷的属性的键值对映射
sub_path 字符串 在卷而不是根内部挂载子路径
read_only 布尔 以只读模式设置卷(默认为 False)。

在服务容器上挂载卷

为构建容器定义的卷同样也为所有服务容器进行自动挂载。您可以使用此功能作为 services_tmpfs(仅适用于 Docker 执行器)的替代方法,在 RAM 中挂载数据库存储以加速测试。

以下是 config.toml 文件中的配置示例:

[[runners]]
  # usual configuration
  executor = "kubernetes"
  [runners.kubernetes]
    [[runners.kubernetes.volumes.empty_dir]]
      name = "mysql-tmpfs"
      mount_path = "/var/lib/mysql"
      medium = "Memory"

自定义卷挂载

引入于极狐 GitLab Runner 13.12。

为了为作业存储构建目录,向配置的 builds_dir(默认为 /builds)定义自定义卷挂载。 如果您使用 PVC卷,基于访问模式,您可能仅限于在一个节点上运行作业。

以下是 config.toml 文件中的配置示例:

concurrent = 4

[[runners]]
  # usual configuration
  executor = "kubernetes"
  builds_dir = "/builds"
  [runners.kubernetes]
    [[runners.kubernetes.volumes.empty_dir]]
      name = "repo"
      mount_path = "/builds"
      medium = "Memory"

为 Pod 设置安全策略

config.toml 中配置安全上下文,为构建 Pod 设置安全策略。

使用以下选项:

选项 类型 必须 描述
fs_group int 应用到 Pod 中所有容器的特殊补充组。
run_as_group int 运行容器进程的入口点的 GID。
run_as_non_root 布尔 表明容器必须以非根用户的身份运行。
run_as_user int 运行容器进程的入口点的 UID。
supplemental_groups int 列表 应用到每个容器第一个运行的进程和容器的主 GID 的组列表。
selinux_type string 适用于 Pod 中所有容器的 SELinux 类型标记。

以下为 config.toml 中的 Pod 安全上下文示例:

concurrent = %(concurrent)s
check_interval = 30
[[runners]]
name = "myRunner"
url = "gitlab.example.com"
executor = "kubernetes"
[runners.kubernetes]
helper_image = "gitlab-registry.example.com/helper:latest"
[runners.kubernetes.pod_security_context]
run_as_non_root = true
run_as_user = 59417
run_as_group = 59417
fs_group = 59417

使用 Helper 镜像

设置安全策略后,Helper 镜像必须符合该策略。 该镜像不会从根群组中获得权限,因此您必须确保用户 ID 是根群组的一部分。

note如果只需要 nonroot 环境,您可以使用极狐GitLab Runner UBI极狐GitLab Runner Helper UBI OpenShift (OCP) 镜像,而不是 Helper 镜像。

以下示例创建一个名为 nonroot 的用户和群组,并将 Helper 镜像设置为以该用户身份运行。

ARG tag
FROM registry.gitlab.com/gitlab-org/ci-cd/gitlab-runner-ubi-images/gitlab-runner-helper-ocp:${tag}
USER root
RUN groupadd -g 59417 nonroot && \
    useradd -u 59417 nonroot -g nonroot
WORKDIR /home/nonroot
USER 59417:59417

为容器设置安全策略

引入于极狐GitLab Runner 14.5。

config.toml 执行器中配置容器安全上下文,为构建、Helper 或服务 Pod 设置容器安全策略。

使用以下选项:

选项 类型 必须 描述
run_as_group 整数 运行容器进程的入口点的 GID。
run_as_non_root 布尔 表明容器必须以非根用户的身份运行。
run_as_user 整数 运行容器进程的入口点的 UID。
capabilities.add 字符串列表 运行容器时的添加能力。
capabilities.drop 字符串列表 运行容器时的丢弃能力。
selinux_type 字符串 与容器进程关联的 SELinux 类型标记。

config.toml 中的以下示例中,安全上下文配置:

  • 设置 Pod 安全上下文。
  • 为构建和 Helper 容器覆盖 run_as_userrun_as_group
  • 指定所有服务容器从 Pod 安全上下文继承 run_as_userrun_as_group
concurrent = 4
check_interval = 30
  [[runners]]
    name = "myRunner"
    url = "gitlab.example.com"
    executor = "kubernetes"
    [runners.kubernetes]
      helper_image = "gitlab-registry.example.com/helper:latest"
      [runners.kubernetes.pod_security_context]
        run_as_non_root = true
        run_as_user = 59417
        run_as_group = 59417
        fs_group = 59417
      [runners.kubernetes.build_container_security_context]
        run_as_user = 65534
        run_as_group = 65534
        [runners.kubernetes.build_container_security_context.capabilities]
          add = ["NET_ADMIN"]
      [runners.kubernetes.helper_container_security_context]
        run_as_user = 1000
        run_as_group = 1000
      [runners.kubernetes.service_container_security_context]
        run_as_user = 1000
        run_as_group = 1000

定义服务列表

  • 引入于极狐GitLab Runner 12.5。

  • 引入于极狐GitLab Runner 12.9。

  • 引入于极狐GitLab Runner 13.6。

config.toml 中定义服务列表。

concurrent = 1
check_interval = 30
  [[runners]]
    name = "myRunner"
    url = "gitlab.example.com"
    executor = "kubernetes"
    [runners.kubernetes]
      helper_image = "gitlab-registy.example.com/helper:latest"
      [[runners.kubernetes.services]]
        name = "postgres:12-alpine"
        alias = "db1"
      [[runners.kubernetes.services]]
        name = "registry.example.com/svc1"
        alias = "svc1"
        entrypoint = ["entrypoint.sh"]
        command = ["executable","param1","param2"]

设置拉取策略

支持多个引入于极狐GitLab 13.11 的拉取策略。

使用 config.toml 文件中的 pull_policy 参数来指定单个或多个拉取策略。 该策略控制镜像的获取和更新方式,并应用于构建镜像、Helper 镜像和任何服务。

要确定使用的策略,请参阅有关拉取策略的 Kubernetes 文档

对于单个拉取策略:

[runners.kubernetes]
  pull_policy = "never"

对于多个拉取策略:

[runners.kubernetes]
  # use multiple pull policies
  pull_policy = ["always", "if-not-present"]

当您定义多个策略时,每个策略都会使用,直到成功获取镜像。 例如,当您使用 [ always, if-not-present ] 时,如果 always 策略因临时注册表问题而失败,则使用 if-not-present 策略。

重试失败的拉取:

[runners.kubernetes]
  pull_policy = ["always", "always"]

极狐GitLab 和 Kubernetes 在命名规范方面不同。

Runner 拉取策略 Kubernetes 拉取策略 描述
使用 Kubernetes 指定的默认策略。
if-not-present IfNotPresent 只有在执行作业的节点上不存在时才能拉取镜像。详情请参见安全考虑
always Always 每次执行作业时都拉取镜像。
never Never 永不拉取镜像,并且要求节点已经拥有镜像。

指定执行构建的节点

使用 node_selector 选项指定要在 Kubernetes 集群中的哪个节点上执行构建。 它是一个 key=value 对的表,格式为 string=string(在环境变量的情况下为 string=string)。

Runner 使用提供的信息来确定构建的操作系统和体系结构。这确保使用了正确的 Helper 镜像。 默认情况下,操作系统和体系结构假定为 linux/amd64

您可以使用特定的标签来调度具有不同操作系统和架构的节点。

linux/arm64 示例

  [[runners]]
    name = "myRunner"
    url = "gitlab.example.com"
    executor = "kubernetes"

    [runners.kubernetes.node_selector]
      "kubernetes.io/arch" = "arm64"
      "kubernetes.io/os" = "linux"

windows/amd64 示例

Windows Kubernetes 拥有特定限制,所以如果使用了进程隔离,您必须使用 node.kubernetes.io/windows-build 标记提供特定 Windows 构建版本。

  [[runners]]
    name = "myRunner"
    url = "gitlab.example.com"
    executor = "kubernetes"

    # The FF_USE_POWERSHELL_PATH_RESOLVER feature flag has to be enabled for PowerShell
    # to resolve paths for Windows correctly when Runner is operating in a Linux environment
    # but targeting Windows nodes.
    environment = ["FF_USE_POWERSHELL_PATH_RESOLVER=1"]

    [runners.kubernetes.node_selector]
      "kubernetes.io/arch" = "amd64"
      "kubernetes.io/os" = "windows"
      "node.kubernetes.io/windows-build" = "10.0.20348"

定义节点亲和列表

节点亲和

引入于极狐GitLab Runner 13.4。

定义在构建时间添加到 Pod 规格的节点亲和列表。

以下是 config.toml 中的配置示例:

concurrent = 1
[[runners]]
  name = "myRunner"
  url = "gitlab.example.com"
  executor = "kubernetes"
  [runners.kubernetes]
    [runners.kubernetes.affinity]
      [runners.kubernetes.affinity.node_affinity]
        [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]]
          weight = 100
          [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference]
            [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]]
              key = "cpu_speed"
              operator = "In"
              values = ["fast"]
            [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]]
              key = "mem_speed"
              operator = "In"
              values = ["fast"]
        [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]]
          weight = 50
          [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference]
            [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]]
              key = "core_count"
              operator = "In"
              values = ["high", "32"]
            [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_fields]]
              key = "cpu_type"
              operator = "In"
              values = ["arm64"]
      [runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution]
        [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms]]
          [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms.match_expressions]]
            key = "kubernetes.io/e2e-az-name"
            operator = "In"
            values = [
              "e2e-az1",
              "e2e-az2"
            ]

定义计划 Pod 的节点

引入于极狐GitLab Runner 14.3。

使用 Pod 亲和与反亲和来约束基于其他 Pod 上的标记,您的 Pod 可以在上面计划的节点。

config.toml 中的示例配置:

concurrent = 1
[[runners]]
  name = "myRunner"
  url = "gitlab.example.com"
  executor = "kubernetes"
  [runners.kubernetes]
    [runners.kubernetes.affinity]
      [runners.kubernetes.affinity.pod_affinity]
        [[runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution]]
          topology_key = "failure-domain.beta.kubernetes.io/zone"
          namespaces = ["namespace_1", "namespace_2"]
          [runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution.label_selector]
            [[runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution.label_selector.match_expressions]]
              key = "security"
              operator = "In"
              values = ["S1"]
        [[runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution]]
        weight = 100
        [runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term]
          topology_key = "failure-domain.beta.kubernetes.io/zone"
          [runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector]
            [[runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector.match_expressions]]
              key = "security_2"
              operator = "In"
              values = ["S2"]
      [runners.kubernetes.affinity.pod_anti_affinity]
        [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution]]
          topology_key = "failure-domain.beta.kubernetes.io/zone"
          namespaces = ["namespace_1", "namespace_2"]
          [runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.label_selector]
            [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.label_selector.match_expressions]]
              key = "security"
              operator = "In"
              values = ["S1"]
          [runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.namespace_selector]
            [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.namespace_selector.match_expressions]]
              key = "security"
              operator = "In"
              values = ["S1"]
        [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution]]
        weight = 100
        [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term]
          topology_key = "failure-domain.beta.kubernetes.io/zone"
          [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector]
            [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector.match_expressions]]
              key = "security_2"
              operator = "In"
              values = ["S2"]
          [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.namespace_selector]
            [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.namespace_selector.match_expressions]]
              key = "security_2"
              operator = "In"
              values = ["S2"]

添加额外的主机别名

引入于极狐GitLab Runner 13.7。

此功能可用于 Kubernetes 1.7 及更高版本。

配置主机别名以指导 Kubernetes 在容器中向 /etc/hosts 文件添加条目。

使用以下选项:

选项 类型 是否必需 描述
IP string Yes 您想将主机添加到的 IP 地址
Hostnames string list Yes 将要添加到 IP 到主机别名列表

config.toml 文件中的配置示例:

concurrent = 4

[[runners]]
  # usual configuration
  executor = "kubernetes"
  [runners.kubernetes]
    [[runners.kubernetes.host_aliases]]
      ip = "127.0.0.1"
      hostnames = ["web1", "web2"]
    [[runners.kubernetes.host_aliases]]
      ip = "192.168.1.1"
      hostnames = ["web14", "web15"]

配置容器生命周期钩子

引入于极狐GitLab Runner 14.2。

当执行对应的生命周期钩子时,使用容器生命周期钩子运行为处理器所配置的代码。

您可以配置两种钩子:PreStopPostStart。每个都只允许设置一种处理器。

以下 config.toml 文件中到配置示例:

[[runners]]
  name = "kubernetes"
  url = "https://gitlab.example.com/"
  executor = "kubernetes"
  token = "yrnZW46BrtBFqM7xDzE7dddd"
  [runners.kubernetes]
    image = "alpine:3.11"
    privileged = true
    namespace = "default"
    [runners.kubernetes.container_lifecycle.post_start.exec]
      command = ["touch", "/builds/postStart.txt"]
    [runners.kubernetes.container_lifecycle.pre_stop.http_get]
      port = 8080
      host = "localhost"
      path = "/test"
      [[runners.kubernetes.container_lifecycle.pre_stop.http_get.http_headers]]
        name = "header_name_1"
        value = "header_value_1"
      [[runners.kubernetes.container_lifecycle.pre_stop.http_get.http_headers]]
        name = "header_name_2"
        value = "header_value_2"

通过以下设置配置每个生命周期钩子:

选项 类型 必须 描述
exec KubernetesLifecycleExecAction Exec 指定采取的措施。
http_get KubernetesLifecycleHTTPGet HTTPGet 指定执行的 HTTP 请求。
tcp_socket KubernetesLifecycleTcpSocket TCPsocket 指定有关 TCP 端口的行动。

KubernetesLifecycleExecAction

选项 类型 必须 描述
command string list 容器内执行的命令行。

KubernetesLifecycleHTTPGet

选项 类型 必须 描述
port int 容器上访问的端口数量。
host 字符串 连接的主机名称,默认为 Pod IP(可选)。
path 字符串 HTTP 服务器上访问的路径(可选)。
scheme 字符串 连接主机的 Scheme,默认为 HTTP(可选)。
http_headers KubernetesLifecycleHTTPGetHeader 列表 请求中设置的自定义 Header(可选)。

KubernetesLifecycleHTTPGetHeader

选项 类型 必须 描述
name 字符串 HTTP Header 名称。
value 字符串 HTTP Header 值。

KubernetesLifecycleTcpSocket

选项 类型 必须 描述
port int 容器上访问的端口数量。
host 字符串 连接的主机名称,默认为 Pod IP(可选)。

配置 Pod DNS 设置

引入于极狐GitLab Runner 13.7。

使用以下选项配置 Pod 的 DNS 设置

选项 类型 必须 描述
nameservers string 列表 将用作 Pod 的 DNS 服务器的 IP 地址列表
options KubernetesDNSConfigOption 一个可选的对象列表,其中每个对象可能有一个名称参数(必需)和一个值参数(可选)
searches string 列表 用于在 Pod 中查找主机名的 DNS 搜索域列表

config.toml 文件中的配置示例:

concurrent = 1
check_interval = 30
[[runners]]
  name = "myRunner"
  url = "https://gitlab.example.com"
  token = "__REDACTED__"
  executor = "kubernetes"
  [runners.kubernetes]
    image = "alpine:latest"
    [runners.kubernetes.dns_config]
      nameservers = [
        "1.2.3.4",
      ]
      searches = [
        "ns1.svc.cluster-domain.example",
        "my.dns.search.suffix",
      ]

      [[runners.kubernetes.dns_config.options]]
        name = "ndots"
        value = "2"

      [[runners.kubernetes.dns_config.options]]
        name = "edns0"

KubernetesDNSConfigOption

选项 类型 必须 描述
name 字符串 配置选项名称。
value *string 配置选项值。

指定容器能力

您可以指定在容器中使用的 Kubernetes 能力

要指定容器能力,请使用 config.toml 中的 cap_addcap_drop 选项。容器运行时也可以定义默认能力列表,如 Docker容器中的那些能力。

这是 Runner 默认丢弃的能力列表。 您在 cap_add 选项中列出的功能不会被丢弃。

config.toml 文件中的配置示例:

concurrent = 1
check_interval = 30
[[runners]]
  name = "myRunner"
  url = "gitlab.example.com"
  executor = "kubernetes"
  [runners.kubernetes]
    # ...
    cap_add = ["SYS_TIME", "IPC_LOCK"]
    cap_drop = ["SYS_ADMIN"]
    # ...

当您指定能力:

  • 用户定义的 cap_drop 优先于用户定义的 cap_add。如果您在两个设置中定义相同的能力,则只有来自 cap_drop 的能力会传递给容器。

  • 从传递给容器配置的能力标识符中删除 CAP_ 前缀。 例如,如果要添加或删除 CAP_SYS_TIME 功能,请在配置文件中输入字符串 SYS_TIME

  • Kubernetes 集群的所有者可以定义一个 PodSecurityPolicy,其中默认允许、限制或添加特定能力。这些规则优先于任何用户定义的配置。

默认丢弃能力列表

极狐GitLab Runner 默认丢弃以下能力。

用户定义的 cap_add 优先于默认的已丢弃的能力列表。 如果要添加默认丢弃的功能,请将其添加到 cap_add

  • NET_RAW

设置 RuntimeClass

引入于极狐GitLab Runner 14.9。

使用 runtime_class_name 为每个作业容器设置RuntimeClass

如果您指定一个 RuntimeClass 名称,并且它未在您的集群中进行配置或者不支持这个功能,执行器会创建作业失败。

concurrent = 1
check_interval = 30
  [[runners]]
    name = "myRunner"
    url = "gitlab.example.com"
    executor = "kubernetes"
    [runners.kubernetes]
      runtime_class_name = "myclass"

在构建中使用 Docker

在构建中使用 Docker 时,您应该注意以下事项。

暴露的 /var/run/docker.sock

如果您使用 runners.kubernetes.volumes.host_path 选项将主机的 /var/run/docker.sock 暴露到您的构建容器中,则存在一定的风险。 可以从构建容器访问节点的容器,并且取决于您是否在与生产容器相同的集群中运行构建,这样做可能并不明智。

使用 docker:dind

如果运行 docker:dind,也称为 docker-in-docker 镜像,容器必须以特权模式运行。这可能有潜在的风险并导致其他问题。

Docker Daemon 在 Pod 中作为单独的容器运行,因为它以 service 的形式启动,通常在 .gitlab-ci.yml 中。Pod 中的容器仅共享分配给它们的卷和 IP 地址,它们使用 localhost 相互通信。docker:dind 容器不共享 /var/run/docker.sock 并且 docker 二进制文件尝试对其进行默认使用。

要配置客户端使用 TCP 联系 Docker Daemon,在另一个容器中包括构建容器的以下环境变量:

  • 非 TLS 连接:DOCKER_HOST=tcp://<hostname>:2375
  • TLS 连接:DOCKER_HOST=tcp://<hostname>:2376

对于 hostname,将值设置为:

  • 极狐GitLab Runner 12.7 及更早版本,以及 Kubernetes 1.6 及更早版本:localhost
  • 极狐GitLab Runner 12.8 及更高版本和 Kubernetes 1.7 及更高版本:docker

在 Docker 19.03 及更高版本中,TLS 默认开启,但您必须将证书映射到您的客户端。 您可以为 DIND 或安装证书启用非 TLS 连接。有关详细信息,请参阅在带有 Docker 执行器的 Docker 工作流中使用 Docker

防止主机内核暴露

如果您使用 docker:dind/var/run/docker.sock,Docker Daemon 可以访问主机的底层内核。这意味着在构建 Docker 镜像时,在 Pod 中设置的任何 limits 都不起作用。 Docker Daemon 报告节点的全部容量,而不管 Kubernetes 生成的 Docker 构建容器上的限制。

如果您以特权模式运行构建容器,或者暴露了 /var/run/docker.sock,则主机内核可能会暴露给构建容器。为了尽量减少曝光,请在 node_selector 选项中指定一个标记。这确保在任何容器可以部署到节点之前,节点匹配标记。 例如,如果您指定标记 role=ci,构建容器仅在标记为 role=ci 的节点上运行,所有其他生产服务在其他节点上运行。

要进一步分离构建容器,您可以使用 taints 节点。 Taints 可防止其他 Pod 调度到与构建 Pod 相同的节点上,而无需为其他 Pod 进行额外配置。

使用 kaniko 构建 Docker 镜像

您可以使用 kaniko 在 Kubernetes 集群中构建 Docker 镜像。

Kaniko 在没有 Docker Daemon 的情况下工作,并且在没有特权访问的情况下构建镜像。

有关详细信息,请参阅使用 kaniko 和极狐GitLab CI/CD 构建镜像

限制 Docker 镜像和服务

为 Kubernetes 执行器添加于极狐GitLab Runner 14.2。

您可以限制运行作业的 Docker 镜像。为此,您需要指定通配符类型。例如,为了仅允许您的私有 Docker 镜像库中的镜像:

[[runners]]
  (...)
  executor = "kubernetes"
  [runners.kubernetes]
    (...)
    allowed_images = ["my.registry.tld:5000/*:*"]
    allowed_services = ["my.registry.tld:5000/*:*"]

或者限定为镜像库中的特定镜像列表:

[[runners]]
  (...)
  executor = "kubernetes"
  [runners.kubernetes]
    (...)
    allowed_images = ["my.registry.tld:5000/ruby:*", "my.registry.tld:5000/node:*"]
    allowed_services = ["postgres:9.4", "postgres:latest"]

限制 Docker 拉取策略

引入于极狐GitLab 15.1。

.gitlab-ci.yml 文件中,您可以指定拉取策略。该策略决定 CI/CD 作业应该如何获取镜像。

要限制可以在 .gitlab-ci.yml 文件中使用的拉取策略,可以使用 allowed_pull_policies

例如,只允许 alwaysif-not-present 拉取策略:

[[runners]]
  (...)
  executor = "kubernetes"
  [runners.kubernetes]
    (...)
    allowed_pull_policies = ["always", "if-not-present"]
  • 如果未指定 allowed_pull_policies,则默认为 pull_policy 关键字中的值。
  • 如果未指定 pull_policy,则使用集群的镜像默认拉取策略
  • 现有的 pull_policy 关键字 不得包含未在 allowed_pull_policies 中指定的拉取策略。如果包含,则作业将会返回错误。

作业执行

极狐GitLab Runner 默认使用 kube attach 而不是 kube exec。这会避免作业因网络不稳定而导致的中途被标记为成功的问题。

如果您在升级到极狐GitLab Runner 14.0 后遇到问题,将 FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY 功能标志设置为 true

容器入口点已知问题

  • 引入于极狐GitLab Runner 14.5。
  • 更新于极狐GitLab Runner 15.1。
note在 14.5 到 15.0 中,极狐GitLab Runner 在与带有 kube attach 的 Kubernetes 执行器一起使用时,使用 Docker 镜像中定义的入口点。 在 15.1 及更高版本中,当设置了 FF_KUBERNETES_HONOR_ENTRYPOINT 时,Docker 镜像中定义的入口点将与 Kubernetes 执行器一起使用。

容器入口点存在以下已知问题:

移除旧的 Runner Pod

引入于极狐GitLab Runner 14.6。

有时,在 Runner Manager 不正确关闭时,会出现旧的 Runner Pod 没有被清理的情况。

为解决这个问题,您可以使用极狐GitLab Runner Pod Cleanup 应用调度旧的 Pod 清理。详情请参见:

  • 极狐GitLab Runner Pod 清理项目自述文件。
  • 极狐GitLab Runner Pod 清理文档。

故障排除

以下是使用 Kubernetes 执行器时经常遇到的错误。

Job failed (system failure): timed out waiting for pod to start

如果集群无法在 poll_timeout 定义的超时时间到达之前调度构建 Pod,构建 Pod 就会返回错误。Kubernetes 调度器 应该能够删除它。

为解决这个问题,在您的 config.toml 文件夹中增加 poll_timeout 的值。

context deadline exceeded

作业日志中的 context deadline exceeded 错误通常表明 Kubernetes API 客户端达到了给定的集群 API 请求的超时时间。

检查 kube-apiserver 集群组件的指标,寻找以下内容的标志:

  • 增加的响应时延。
  • 对 Pod、密钥、ConfigMaps 和其他核心(v1)资源进行正常创建或删除操作时的错误率。

来自 kube-apiserver 操作的超时时间驱动的错误日志可能会显示为:

Job failed (system failure): prepare environment: context deadline exceeded
Job failed (system failure): prepare environment: setting up build pod: context deadline exceeded

在某些情况下,kube-apiserver 错误响应或许会提供子组件失败(例如 Kubernetes 集群的 etcdserver)的额外信息:

Job failed (system failure): prepare environment: etcdserver: request timed out
Job failed (system failure): prepare environment: etcdserver: leader changed
Job failed (system failure): prepare environment: Internal error occurred: resource quota evaluates timeout

在创建构建 Pod 和完成后进行清理时可能会发生 kube-apiserver 服务失败的情况:

Error cleaning up secrets: etcdserver: request timed out
Error cleaning up secrets: etcdserver: leader changed

Error cleaning up pod: etcdserver: request timed out, possibly due to previous leader failure
Error cleaning up pod: etcdserver: request timed out
Error cleaning up pod: context deadline exceeded

使用 Kubernetes API 试图通信时拒绝连接

当极狐GitLab Runner 向 Kubernetes API 发送请求并失败时,很可能因为 kube-apiserver 已经超载且不能接受或处理 API 请求。

Error cleaning up podJob failed (system failure): prepare environment: waiting for pod running

当 Kubernetes 未能及时调度作业 Pod 时会发生以下错误。 极狐GitLab Runner 等待 Pod 准备好,但是失败了,又试图清理 Pod,也可能会失败。

Error: Error cleaning up pod: Delete "https://xx.xx.xx.x:443/api/v1/namespaces/gitlab-runner/runner-0001": dial tcp xx.xx.xx.x:443 connect: connection refused

Error: Job failed (system failure): prepare environment: waiting for pod running: Get "https://xx.xx.xx.x:443/api/v1/namespaces/gitlab-runner/runner-0001": dial tcp xx.xx.xx.x:443 connect: connection refused

为解决这个问题,检查 Kubernetes 主要节点和运行 kube-apiserver 实例的所有节点。确保他们拥有管理目标数量的您希望在集群上增加的 Pod 所需的所有资源。

如果您想更改极狐GitLab Runner 等待 Pod 达到 Ready 状态的时间,请使用 poll_timeout设置。

为了更好地了解如何调度 Pod 或者他们没有被及时调度的原因,请阅读 Kubernetes 调度器

request did not complete within requested timeout

构建 Pod 创建过程中的 request did not complete within requested timeout 消息表明 Kubernetes 集群上配置的准入控制网络钩子超时了。

准入控制网络钩子是所有 API 请求的集群级别的管理控制拦截,如果没有及时执行会失败。

准入控制网络钩子支持可以精准控制其拦截的 API 请求和命名空间源的过滤器。如果极狐GitLab Runner 的 Kubernetes API 调用不需要通过准入控制网络钩子进行传递,那么您可以更改网络钩子的选择器/过滤配置,忽略极狐GitLab Runner 命名空间,或者通过配置极狐GitLab Runner Helm Chart values.yaml中的 podAnnotationspodLabels,将排除标记/标注应用到极狐GitLab Runner Pod。

例如,为了避免数据狗准入控制器网络钩子拦截极狐GitLab Runner Manager Pod 的 API 请求,可以添加以下内容:

podLabels:
  admission.datadoghq.com/enabled: false

如果您想列出 Kubernetes 集群的准入控制网络钩子,请运行:

kubectl get validatingwebhookconfiguration -o yaml
kubectl get mutatingwebhookconfiguration -o yaml

如果准入控制网络钩子超时,可以查看到以下格式的日志:

Job failed (system failure): prepare environment: Timeout: request did not complete within requested timeout
Job failed (system failure): prepare environment: setting up credentials: Timeout: request did not complete within requested timeout

准入控制网络钩子失败可能会显示为:

Job failed (system failure): prepare environment: setting up credentials: Internal error occurred: failed calling webhook "example.webhook.service"

fatal: unable to access 'https://gitlab-ci-token:token@example.com/repo/proj.git/': Could not resolve host: example.com

如果使用 Helper 镜像alpine 类型, 可能会引发有关 Alpine 的 musl 的 DNS 解析器的 DNS 问题。

使用 helper_image_flavor = "ubuntu" 选项应该能解决这个问题。

docker: Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running?

如果使用 Docker-in-Docker,在没有时间完全启动时试图访问 DIND 服务会发生这个错误。

curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443

使用 Docker-in-Docker 时,如果 DIND 最大传输单元(MTU)大于 Kubernetes 交叉网络,会发生这个错误。DIND 使用默认的 1500 MTU,这对于路由到默认交叉网络来说过大。可以在服务定义中更改 DIND MTU:

services:
  - name: docker:dind
    command: ["--mtu=1450"]

MountVolume.SetUp failed for volume "kube-api-access-xxxxx" : chown is not supported by windows

当运行 CI/CD 作业时,您或许会收到类似以下内容的错误:

MountVolume.SetUp failed for volume "kube-api-access-xxxxx" : chown c:\var\lib\kubelet\pods\xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\volumes\kubernetes.io~projected\kube-api-access-xxxxx\..2022_07_07_20_52_19.102630072\token: not supported by windows

当您使用节点选择器在具有不同操作系统和架构的节点上运行构建时,会出现此问题。

要解决此问题,请配置 nodeSelector,以便始终将 Runner Manager Pod 安排在 Linux 节点上。例如,您的 values.yaml 文件 应包含以下内容:

nodeSelector:
  kubernetes.io/os: linux

构建 Pod 被分配 Worker 节点的 IAM 角色而不是 Runner IAM 角色

当 Worker 节点 IAM 角色没有承担正确角色的权限时,会发生此问题。要解决此问题,请将 sts:AssumeRole 权限添加到 Worker 节点的 IAM 角色的信任关系中:

{
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::<AWS_ACCOUNT_NUMBER>:role/<IAM_ROLE_NAME>"
    },
    "Action": "sts:AssumeRole"
}

Preparation failed: failed to pull image 'image-name:latest': pull_policy ([Always]) defined in GitLab pipeline config is not one of the allowed_pull_policies ([])

如果您在 .gitlab-ci.yml 中指定了 pull_policy 但在 Runner 的配置文件中没有配置任何策略,则会发生此问题。要解决此问题,请根据限制 Docker 拉取策略allowed_pull_policies 添加到您的配置中。

后台进程导致作业挂起和超时

在作业执行期间启动的后台进程可以阻止构建作业退出。为避免这种情况,您可以:

  • 双分叉进程。例如,command_to_run < /dev/null &> /dev/null &
  • 在退出作业脚本之前终止进程。

限制对作业变量的访问

使用 Kubernetes 执行器时,有权访问 Kubernetes 集群的用户可以读取作业中使用的变量。默认情况下,作业变量存储在:

  • ConfigMap
  • Pod 的环境部分

要限制对作业变量数据的访问,您应该使用基于角色的访问控制 (RBAC),以便只有极狐GitLab 管理员才能访问极狐GitLab Runner 使用的命名空间。

如果您需要其他用户访问极狐GitLab Runner 命名空间,请设置以下 verbs 来限制用户在极狐GitLab Runner 命名空间中的访问类型:

  • 对于 podsconfigmaps
    • get
    • watch
    • list
  • 对于 pods/execpods/attach,使用 create

授权用户的示例 RBAC 定义:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: gitlab-runner-authorized-users
rules:
- apiGroups: [""]
  resources: ["configmaps", "pods"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["pods/exec", "pods/attach"]
  verbs: ["create"]