极狐GitLab Runner 的 Kubernetes 执行器

极狐GitLab Runner 可以使用 Kubernetes 在 Kubernetes 集群上运行构建,这离不开 Kubernetes 执行器。

当 Kubernetes 执行器和极狐GitLab CI 一起使用时,它会连接到为每个极狐GitLab CI 作业创建 Pod 的集群中的 Kubernetes API。这个 Pod 至少由构建容器、Helper 容器和 .gitlab-ci.ymlconfig.toml 文件中定义的 service 的额外容器组成。 这些容器的名称如下:

  • 构建容器是 build
  • Helper 容器是 helper
  • 服务容器是 svc-X,其中 X[0-9]+

请注意当服务和容器在同一个 Kubernetes Pod 中运行时,他们共享同一个 localhost 地址。以下限制适用:

极狐GitLab Runner 12.8 和 Kubernetes 1.7 之后,可以通过服务的 DNS 名称对其进行访问。如果您使用的是较早的版本,您需要使用 localhost

  • 多个服务不能使用同一个端口(例如,您不能同时拥有两个 mysql 服务)。

工作流

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

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

连接 Kubernetes API

提供以下选项,允许您连接 Kubernetes API:

  • host:可选的 Kubernetes API 服务器主机 URL(如未指定,会自动发现)。
  • cert_file:可选的 Kubernetes API 服务器主机用户鉴权证书。
  • key_file:可选的 Kubernetes API 服务器主机用户鉴权私钥。
  • ca_file:可选的 Kubernetes API 服务器主机 CA 证书。

提供的用户账户必须有权限在特定命名空间创建、列出并附着 Pod,以正常使用功能。

如果您在 Kubernetes 集群内运行极狐GitLab Runner,您可以忽略以上所有字段,让极狐GitLab Runner 自动发现 Kubernetes API。这是推荐的方法。

如果您在集群外运行它,您需要进行所有设置,并确保极狐GitLab Runner 能够在集群上访问 Kubernetes API。

Kubernetes 执行器交互图

下图描述了托管在 Kubernetes 集群上的极狐GitLab Runner 和 Kubernetes API 之间的交互。Kubernetes API 是极狐GitLab Runner 在 Kubernetes 上使用的机制,用于在集群上创建 Pod。这个图中描述的交互在任何 Kubernetes 集群上都有效,无论是托管在主公共云提供商上的一站式解决方案还是私有化部署 Kubernetes 安装。

note极狐GitLab Runner Helm Chart 已经在谷歌 Kubernetes 引擎和 Azure 容器服务上进行了测试。
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

可用 config.toml 设置

以下设置帮助定义 Kubernetes 中的极狐GitLab Runner 的行为。

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 文件中指定的拉取策略列表。如果未指定,则允许 pull_policy 中指定的所有拉取策略。
allowed_services .gitlab-ci.yml 中可以指定的服务的通配符列表。如果不存在,则允许所有镜像(等同于 ["*/*:*"])。查看详情
bearer_token_overwrite_allowed 允许项目指定用于创建构建 Pod 的持有者令牌的布尔。
cap_add 指定应该被添加到作业 Pod 容器的 Linux 能力。阅读更多关于 Kubernetes 执行器中的能力配置的内容
cap_drop 指定应该从作业 Pod 容器 移除的 Linux 能力。阅读更多关于 Kubernetes 执行器中的能力配置的内容
cleanup_grace_period_seconds 作业完成后,Pod 优雅关闭的时间(秒)。关闭时间之后,使用停止信号强制停止进程。如果指定了 terminationGracePeriodSeconds,则忽略它。
helper_image (高级)覆盖默认 Helper 镜像,该镜像用于克隆仓库和上传产物。
helper_image_flavor 设置 Helper 镜像类型 (alpinealpine3.12alpine3.13alpine3.14alpine3.15ubuntu)。默认为 alpine。使用 alpine 与使用 alpine3.12 相同。
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 添加到 Runner 创建的每个构建 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 安全考虑。您也可以限制拉取策略
service_account Pod 使用的与 Kubernetes API 通信的默认服务账户作业/执行器。
service_account_overwrite_allowed 验证服务账户覆盖环境变量内容的正则表达式。如果为空,则禁用了服务账户覆盖功能。
services 自极狐GitLab Runner 12.5 起,使用边车 (https://docs.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 的优先级。如果未设置,将使用默认值。

配置执行器服务账户

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

覆盖 Kubernetes 命名空间

此外,您可以在 .gitlab-ci.yml 文件上使用 KUBERNETES_NAMESPACE_OVERWRITE 变量覆盖 Kubernetes 命名空间。

这个方法允许您创建专为 CI 使用的新的隔离的命名空间,并且部署一个自定义组的 Pod。对于 CI 阶段中容器之间简单 直接的访问,Runner 生成的 Pods 会在覆盖的命名空间中出现。

variables:
  KUBERNETES_NAMESPACE_OVERWRITE: ci-${CI_COMMIT_REF_SLUG}

此外,为确保在 CI 运行中仅使用指定的命名空间,使用合适的正则表达式设置 namespace_overwrite_allowed 配置。如果为空,则禁用了覆盖行为。

当您覆盖 Kubernetes 命名空间,请确保:

  • 在极狐GitLab Runner Helm Chart 的 values.yml 文件中,这个值被设定为 rbac.clusterWideAccess: true
  • Runner 在核心 API 组中有以下权限:

    资源 权限
    pods/exec 创建、修补、删除
    pods 获取、列出、监视、创建、修补、删除
    secrets 获取、列出、监视、创建、修补、删除

这可以通过使用 values.yml 文件中的上述权限来设置 rbac.create: true 或指定服务账户 rbac.serviceAccountName: <service_account_name> 实现。

覆盖 Kubernetes 默认服务账户

此外,可以使用 KUBERNETES_SERVICE_ACCOUNT_OVERWRITE 变量在 .gitlab-ci.yml 文件中覆盖 Kubernetes 服务账户。

这个方法允许您指定附着到命名空间的服务账户,当处理复杂 的 RBAC 配置时,这很有用。

variables:
  KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: ci-service-account

为确保在 CI 运行时仅使用特定的服务账户,使用合适的正则表达式设置 service_account_overwrite_allowed 配置或 KUBERNETES_SERVICE_ACCOUNT_OVERWRITE_ALLOWED 环境变量。 如果为空,则禁用了覆盖行为。

进行 Kubernetes API 调用时使用持有者令牌

除了以下描述的设置命名空间和服务账户,您可能也需要设置调用 API 创建构建 Pod 时所使用的持有者令牌。这允许项目所有者使用项目秘密变量指定持有者令牌。当指定持有者令牌时,您必须设置 Host 配置。

variables:
  KUBERNETES_BEARER_TOKEN: thebearertokenfromanothernamespace

覆盖 Pod 标记

您可以覆盖 Kubernetes Pod 标记。

首先,确保在 .config.yaml 文件中指定 pod_labels_overwrite_allowed

然后,在 .gitlab-ci.yml 文件中通过 key=value 使用 KUBERNETES_POD_LABELS_* 变量。 Pod 标记被覆盖为 key=value

您可以应用多个值。例如:

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

覆盖 Pod 标注

此外,可以使用 KUBERNETES_POD_ANNOTATIONS_* 变量和 key=value值在 .gitlab-ci.yml 文件中覆盖 Kubernetes Pod。Pod 标注将被覆盖到 key=value中。可以运用多个标注,例如:

variables:
  KUBERNETES_POD_ANNOTATIONS_1: "Key1=Val1"
  KUBERNETES_POD_ANNOTATIONS_2: "Key2=Val2"
  KUBERNETES_POD_ANNOTATIONS_3: "Key3=Val3"
note您必须指定 pod_annotations_overwrite_allowed,通过 .gitlab-ci.yml 文件覆盖 Pod 标注。

覆盖容器资源

此外,可以使用以下变量在 .gitlab-ci.yml 文件中覆盖构建、Helper 和服务容器的请求和限制的 Kubernetes CPU 和内存分配。

 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

这些变量的值仅限于那个资源的最大覆盖设置。 如果资源没有设置最大覆盖,则忽略变量。

在配置 TOML 中定义设置

可以在 config.toml 文件中定义每个设置。

以下是 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。

先决条件:

  • 设置 image_pull_secretsservice_account
  • resource_availability_check_max_attempts 设置为大于零的数字。

极狐GitLab Runner 每 5 秒钟检查一次新的服务账户或密钥是否可用。 检查的次数与 resource_availability_check_max_attempts 的值相同。 默认值为 5

通过 Kubernetes 执行器使用缓存

当通过Kubernetes 执行器使用缓存,会在 Pod 上挂载叫做 /cache 的特定的卷。 使用 cache_dir 设置在 config.toml 文件中配置缓存卷。

在作业执行过程中,如果需要缓存数据,Runner 会检查缓存数据是否可用(如果缓存卷上压缩文件可用)。

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

使用卷

如上所述,可以在构建容器中挂载卷。这次支持 hostPathPVCconfigMapsecretCSI 卷类型。 用户可以为每个提到的类型配置任何数量的卷。

以下是示例配置:

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 volume 配置指导 Kubernetes 在容器中挂载特定主机路径。可以使用以下选项配置卷:

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

PVC 卷

PVC volume 配置指导 Kubernetes 使用在 Kubernetes 集群中定义的 PersistentVolumeClaim 并在容器中进行挂载。使用以下选项配置卷:

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

ConfigMap 卷

ConfigMap 卷配置知道 Kubernetes 使用 Kubernetes 集群中定义的 configMap 并在容器内部进行挂载。

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

使用 configMap 卷时,选定的 configMap 中的每个密钥都将被更改为选定的挂载路径中存储的文件。 默认情况下所有密钥都存在,configMap 的密钥用作文件名,值存储为文件内容。可以使用 items 选项更改默认行为。

items 选项定义了应该使用的密钥和应该保存 configMap 的值的路径(相对于卷的挂载路径)之间的映射。使用 items 选项时, 只有选定的密钥被添加到卷中,跳过所有其他密钥。

注意:如果使用不存在的密钥,作业会在 Pod 创建阶段失败。

Secret 卷

Secret volume配置指导 Kubernetes 使用 Kubernetes 集群中定义的 secret,并在容器内部进行挂载。

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

使用 Secret 卷时,选定的 Secret 中的每个密钥都将被更改为选定的挂载路径中存储的文件。 默认情况下所有密钥都存在,Secret 的密钥用作文件名,值存储为文件内容。可以使用 items 选项更改默认行为。

items 选项定义了应该使用的密钥和应该保存 secret 的值的路径(相对于卷的挂载路径)之间的映射。使用 items 选项时, 只有选定的密钥被添加到卷中,跳过所有其他密钥。

注意:如果使用不存在的密钥,作业会在 Pod 创建阶段失败。

Empty Dir 卷

emptyDir volume 配置指导 Kubernetes 在容器内部挂载空目录。

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

CSI 卷

CSI volume 配置指导 Kubernetes 使用自定义 CSI 驱动挂载容器内部的任意存储系统。

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

在服务容器上挂载卷

为构建容器定义的卷同样也为所有服务容器进行自动挂载。例如,它可以在 RAM 中挂载数据库存储,加速测试,作为仅 Docker 执行器可用的服务_tmpfs 的替代。

以下是示例配置:

[[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 卷, 您需要知道,依靠访问模式, 您可能在一个节点上运行作业。

以下是示例配置:

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 安全上下文 配置指导执行器在构建 Pod 上设置 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 类型标记。

向 Pod 分配安全上下文为您的 Kubernetes 集群提供了安全性。为此,您需要提供符合您设置的策略的 Helper 镜像。

note

如果除了 nonroot 环境之外没有其他要求,极狐GitLab Runner UBI 和极狐GitLab Runner Helper UBI 镜像可以替代构建自己的 Helper 镜像。 需要注意的是,镜像的设计使其可以与任何用户 ID 一起使用。此用户 ID 必须是根组的一部分,这一点很重要。 作为根组的一部分并没有赋予它任何特权。 阅读更多关于 Helper 镜像的内容。 构建您自己的 Helper 镜像示例:

ARG tag
FROM gitlab/gitlab-runner-helper:${tag}
RUN addgroup -g 59417 -S nonroot && \
    adduser -u 59417 -S nonroot -G nonroot
WORKDIR /home/nonroot
USER 59417:59417

在这个示例中,创建了被称为 nonroot 的用户和组,并设置了以那个用户的身份运行的镜像。

在您的 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

容器安全上下文

引入于极狐GitLab Runner 14.5。

使用容器安全上下文配置以配置执行器在构建、Helper 或服务 Pod 上设置容器安全策略。

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

以下示例设置了 Pod 安全上下文,然后为构建和 Helper 容器覆盖了 run_as_userrun_as_group。在示例中,所有的服务容器都会从 Pod 安全上下文中继承 run_as_userrun_as_group

在您的 config.toml 中设置 Pod 安全上下文示例:

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。

定义服务列表。

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 的拉取策略。

使用 pull_policy 参数指定单个或多个拉取策略。

对于单个拉取策略:

[runners.kubernetes]
  pull_policy = "never"

对于多个拉取策略:

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

策略应用于构建镜像、Helper 镜像和任何服务。它控制如何获取和更新镜像。

关于何时使用哪个策略,请参见 Kubernetes 拉取策略文档

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

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

当定义了多个策略,会尝试每个策略,直到成功获取镜像。 例如,当您使用 [ always, if-not-present ] 时,如果 always 由于临时镜像库的问题失败了,您可以使用 if-not-present 策略。

使用 if-not-present 时,请了解安全考虑

使用节点选择器

node_selector 选项可用于指定 Kubernetes 集群中您想执行构建的节点。 它是 string=string(环境变量的情况下是 string:string)形式的 key=value 对表格。

Runner 额外使用提供的信息决定构建的 OS 和架构。这确保使用了正确的 Helper 镜像。默认情况下,OS 和架构假定为 linux/amd64

使用特定标记,可以在使用不同 OS 和架构的节点上调度构建。

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.19041"

添加额外主机别名

引入于极狐GitLab Runner 13.7。

本功能在 Kubernetes 1.7+ 中可用。

主机别名 配置指导 Kubernetes 向容器内的 /etc/hosts 文件添加条目。可以使用以下选项配置主机别名:

选项 类型 必须 描述
IP 字符串 您想附着的主机的 IP 地址。
Hostnames string 列表 附着到 IP 的主机别名列表。

以下是配置示例:

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 13.4。

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

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

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 14.2。

当执行对应的生命周期钩子时,使用容器生命周期钩子运行为处理器所配置的代码。 您可以配置两种钩子:PreStopPostStart。每个都只允许设置一种处理器。

以下是配置示例:

[[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 配置给予用户更多对创建的构建 Pod 的 DNS 设置的控制权。

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"
选项 类型 必须 描述
nameservers string 列表 用作为 Pod 的 DNS 服务器的 IP 地址列表。
options KubernetesDNSConfigOption 每个对象可能拥有名称属性(必须)和值属性(可选)的可选对象列表。
searches string 列表 Pod 中主机名称查询的 DNS 查询域名列表。

KubernetesDNSConfigOption

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

能力配置

Kubernetes 允许 配置不同的应该添加到或从容器中丢弃的 Linux 能力

极狐GitLab Runner 支持使用配置文件中 [runners.kubernetes] 部分的 cap_addcap_drop 设置进行能力配置。 默认情况下,极狐GitLab Runner 提供应该丢弃的能力列表

因为默认的丢弃能力列表可以与用户定义的能力交叉,使用以下规则确定最终列表:

  1. 用户定义的 cap_drop 优先于用户定义的 cap_add。 如果您在两个设置中定义了相同的能力,只有 cap_drop 中的那个能力可以传递到容器中。

  2. 用户定义的 cap_add 优先于丢弃能力的默认列表。 如果您想添加默认丢弃的能力,请将其明确添加到 cap_add 中。

最终能力列表添加到作业 Pod 的所有容器中。

提示:

  • 从传递到容器配置的能力标识符中移除 CAP_ 前缀。 例如,如果您想添加或丢弃 CAP_SYS_TIME 能力,请在配置文件中为 cap_addcap_drop 设置 SYS_TIME 字符串。

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

    容器运行时同样也可以定义默认的您可以添加的能力列表, 就像 Dockercontainerd 中的一样。

默认丢弃能力列表

极狐GitLab Runner 默认试图丢弃这些能力。如果有些能力对正常执行作业来说是必须的,您应该明确使用 cap_add 设置添加这些能力:

  • NET_RAW

配置示例

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"]
    # ...

使用运行时类

引入于极狐GitLab Runner 14.9。

使用 runtime_class_name 为每个作业容器设置运行时类

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

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

在构建中使用 Docker

在您的运行在 Kubernetes 集群的构建中使用 Docker 时需要注意一些方面。 大多数议题都在极狐 CI 文档的使用 Docker 构建部分中进行了讨论,但是当您在您的集群上运行的时候可能会遇到稍微不一样的事情,这个时候您可以重新查看一下这些内容。

暴露 /var/run/docker.sock

使用 runners.kubernetes.volumes.host_path 选项,将您主机的 /var/run/docker.sock 暴露到您的构建容器,会和往常一样给它带来同样的风险。 从构建容器可以访问那个节点的容器,如果您在运行您生产容器的集群中运行构建,或许那样做不太明智。

使用 docker:dind

运行被称为 docker-in-docker 镜像的 docker:dind 同样也是可能的,但是需要容器以特权模式运行。 如果您愿意承担这个风险,可能也会出现其他问题,而且它们不会像看起来那么简单。 因为 Docker Daemon 通常在 .gitlab-ci.yml 中作为 service 启动,它会在您的 Pod 中作为一个单独的容器进行运行。 基本上 Pod 中的容器仅仅共享分配给它们的卷和使用 localhost 相互触达的 IP 地址。 docker:dind 容器不分享 /var/run/docker.sockdocker 二进制文件试图默认使用它。

为了覆盖它并且让客户端使用 TCP 与 Docker Daemon 进行通信, 确保在其他容器中包含构建容器的环境变量:

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

如果您正在使用极狐GitLab Runner 12.7 或更早版本以及 Kubernetes 1.6 或更早版本, hostname 的值必须被设置为 localhost。否则,它应该被设置为 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 且在其他节点上运行其他生产服务的节点上。 使用污点节点可以实现构建容器的进一步分离。 这将禁止在对其他 Pod 未进行额外配置的情况下,其他 Pod 与构建 Pod 在一个节点上。

使用 kaniko

另外一个在 Kubernetes 集群上构建 Docker 镜像的方法是使用 kaniko。 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。
  • 从 14.5 到 15.0,当通过 kube attach 和 Kubernetes 执行器进行使用时,极狐GitLab Runner 接受 Docker 镜像中定义的入口点。
  • 从 15.1 开始,当设置 FF_KUBERNETES_HONOR_ENTRYPOINT 时,Docker 镜像中定义的入口点会被 Kubernetes 执行器所接受。
note如果在镜像的 Dockerfile 中定义了入口点,它必须打开一个有效的 Shell。否则,CI 作业会挂起。

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