极狐 GitLab

Kubernetes 执行器

Tier: 基础版,专业版,旗舰版

Offering: JihuLab.com,私有化部署

使用 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.yml 或 config.toml 文件中定义的每个 service,pod 由以下容器组成:

  • 定义为 build的构建容器。
  • 定义为 helper的辅助容器。
  • 按以下逻辑命名的服务容器:
    • 如果该服务有一个有效的 DNS 标签名称别名, 且该别名尚未被其他服务容器使用,则使用该别名作为容器名称。
    • 如果没有可用的有效别名,则容器命名为 svc-N,其中 N 是从 0 开始的顺序索引。

服务和容器运行在同一个 Kubernetes pod 中,并共享同一个 localhost 地址。以下限制适用:

  • 服务可通过其 DNS 名称访问。如果您 使用较旧版本,则必须使用 localhost。
  • 您不能使用多个使用相同端口的服务。例如,您不能同时拥有两个 mysql 服务。
Rendering chart...

图中的交互适用于任何 Kubernetes 集群。例如,托管在主要公有云提供商上的开箱即用解决方案,或自管理的 Kubernetes 安装。

连接到 Kubernetes API#

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

选项描述
host可选的 Kubernetes API 服务器主机 URL(如果未指定,则尝试自动发现)。
context可选的 Kubernetes 上下文名称,从您的 kubectl 配置中使用。当您未指定 host 时使用此选项。
cert_file可选的 Kubernetes API 服务器用户认证证书。
key_file可选的 Kubernetes API 服务器用户认证私钥。
ca_file可选的 Kubernetes API 服务器 CA 证书。

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

如果您在集群外部运行极狐GitLab Runner,这些设置可确保极狐GitLab Runner 能够访问集群上的 Kubernetes API。您可以指定带有认证详情的 host,或使用 context 引用 kubectl 配置中的特定上下文。

为 Kubernetes API 调用设置 bearer token#

要为创建 pod 的 API 调用设置 bearer token,请使用 KUBERNETES_BEARER_TOKEN 变量。这允许项目所有者使用项目密钥变量来指定 bearer token。

指定 bearer token 时,您必须 设置 Host 配置设置。

yaml
variables: KUBERNETES_BEARER_TOKEN: thebearertokenfromanothernamespace

配置 Runner API 权限#

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

您可以:

  • 将 rbac.create 设置为 true。
  • 在 values.yml 文件中指定一个具有以下 权限的服务账号 serviceAccount.name: <service_account_name>。
资源动词(可选功能/配置标志)
apps/deploymentscreate (kubernetes.autoscaler), delete (kubernetes.autoscaler), get (kubernetes.autoscaler), list (kubernetes.autoscaler), update (kubernetes.autoscaler)
configmapscreate (FF_SUSPENDABLE_ENVIRONMENTS=true), delete (FF_SUSPENDABLE_ENVIRONMENTS=true), get (FF_SUSPENDABLE_ENVIRONMENTS=true), update (FF_SUSPENDABLE_ENVIRONMENTS=true)
eventslist (hostUsers=false, print_pod_warning_events=true), watch (FF_PRINT_POD_EVENTS=true)
namespacescreate (kubernetes.NamespacePerJob=true), delete (kubernetes.NamespacePerJob=true), get (FF_SUSPENDABLE_ENVIRONMENTS=true)
persistentvolumeclaimscreate (FF_SUSPENDABLE_ENVIRONMENTS=true), delete (FF_SUSPENDABLE_ENVIRONMENTS=true)
podscreate, delete, get, list(使用 Informers), watch(使用 Informers, FF_KUBERNETES_HONOR_ENTRYPOINT=true, FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false)
pods/attachcreate (FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false), delete (FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false), get (FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false), patch (FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false)
pods/execcreate, delete, get, patch
pods/logget (FF_CONCRETE=true, FF_KUBERNETES_HONOR_ENTRYPOINT=true, FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false, FF_WAIT_FOR_POD_TO_BE_REACHABLE=true), list (FF_KUBERNETES_HONOR_ENTRYPOINT=true, FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false)
policy/poddisruptionbudgetscreate (pod_disruption_budget=true), get (pod_disruption_budget=true)
scheduling.k8s.io/priorityclassescreate (kubernetes.autoscaler), get (kubernetes.autoscaler)
secretscreate, delete, get, update
serviceaccountsget
servicescreate, get

您可以使用以下 YAML 角色定义来创建具有所需权限的角色。

yaml
1apiVersion: rbac.authorization.k8s.io/v1 2kind: Role 3metadata: 4 name: gitlab-runner 5 namespace: default 6rules: 7- apiGroups: ["apps"] 8 resources: ["deployments"] 9 verbs: 10 - "create" # Required when `kubernetes.autoscaler` 11 - "delete" # Required when `kubernetes.autoscaler` 12 - "get" # Required when `kubernetes.autoscaler` 13 - "list" # Required when `kubernetes.autoscaler` 14 - "update" # Required when `kubernetes.autoscaler` 15- apiGroups: [""] 16 resources: ["configmaps"] 17 verbs: 18 - "create" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 19 - "delete" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 20 - "get" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 21 - "update" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 22- apiGroups: [""] 23 resources: ["events"] 24 verbs: 25 - "list" # Required when `hostUsers=false`, `print_pod_warning_events=true` 26 - "watch" # Required when `FF_PRINT_POD_EVENTS=true` 27- apiGroups: [""] 28 resources: ["namespaces"] 29 verbs: 30 - "create" # Required when `kubernetes.NamespacePerJob=true` 31 - "delete" # Required when `kubernetes.NamespacePerJob=true` 32 - "get" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 33- apiGroups: [""] 34 resources: ["persistentvolumeclaims"] 35 verbs: 36 - "create" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 37 - "delete" # Required when `FF_SUSPENDABLE_ENVIRONMENTS=true` 38- apiGroups: [""] 39 resources: ["pods"] 40 verbs: 41 - "create" 42 - "delete" 43 - "get" 44 - "list" # Required when using Informers (https://docs.gitlab.com/runner/executors/kubernetes/#informers) 45 - "watch" # Required when `FF_KUBERNETES_HONOR_ENTRYPOINT=true`, `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false`, using Informers (https://docs.gitlab.com/runner/executors/kubernetes/#informers) 46- apiGroups: [""] 47 resources: ["pods/attach"] 48 verbs: 49 - "create" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 50 - "delete" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 51 - "get" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 52 - "patch" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 53- apiGroups: [""] 54 resources: ["pods/exec"] 55 verbs: 56 - "create" 57 - "delete" 58 - "get" 59 - "patch" 60- apiGroups: [""] 61 resources: ["pods/log"] 62 verbs: 63 - "get" # Required when `FF_CONCRETE=true`, `FF_KUBERNETES_HONOR_ENTRYPOINT=true`, `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false`, `FF_WAIT_FOR_POD_TO_BE_REACHABLE=true` 64 - "list" # Required when `FF_KUBERNETES_HONOR_ENTRYPOINT=true`, `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 65- apiGroups: ["policy"] 66 resources: ["poddisruptionbudgets"] 67 verbs: 68 - "create" # Required when `pod_disruption_budget=true` 69 - "get" # Required when `pod_disruption_budget=true` 70- apiGroups: ["scheduling.k8s.io"] 71 resources: ["priorityclasses"] 72 verbs: 73 - "create" # Required when `kubernetes.autoscaler` 74 - "get" # Required when `kubernetes.autoscaler` 75- apiGroups: [""] 76 resources: ["secrets"] 77 verbs: 78 - "create" 79 - "delete" 80 - "get" 81 - "update" 82- apiGroups: [""] 83 resources: ["serviceaccounts"] 84 verbs: 85 - "get" 86- apiGroups: [""] 87 resources: ["services"] 88 verbs: 89 - "create" 90 - "get"

其他详细信息:

Informers#

在极狐GitLab Runner 17.9.0 及更高版本中,Kubernetes informer 会跟踪构建 pod 的变更。这有助于执行器更快地检测到变更。

informer 需要 pods的 list 和 watch 权限。当执行器启动构建时,它会检查 Kubernetes API 中的权限。如果所有权限均已授予,执行器会使用 informer。如果缺少任何权限,极狐GitLab Runner 会记录一条警告。构建会继续,并使用之前的机制来跟踪构建 pod 的状态和变更。

配置设置#

在 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分配给构建辅助容器的 CPU 配额。
helper_cpu_limit_overwrite_max_allowed辅助容器的 CPU 配额可被写入的最大值。为空时,禁用 CPU 限制覆盖功能。
helper_cpu_request为构建辅助容器请求的 CPU 配额。
helper_cpu_request_overwrite_max_allowed辅助容器的 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 请求覆盖功能。
pod_cpu_limit分配给构建 pod 的 CPU 配额。
pod_cpu_limit_overwrite_max_allowed构建 pod 的 CPU 配额可被写入的最大值。为空时,禁用 CPU 限制覆盖功能。
pod_cpu_request为构建 pod 请求的 CPU 配额。
pod_cpu_request_overwrite_max_allowed构建 pod 的 CPU 配额请求可被写入的最大值。为空时,禁用 CPU 请求覆盖功能。

Pod 级资源规格已在 Kubernetes v1.32中作为 alpha 功能引入,并在 Kubernetes v1.34中升级为 beta。

内存请求和限制#

设置描述
memory_limit分配给构建容器的内存量。
memory_limit_overwrite_max_allowed构建容器的内存配额可被写入的最大值。为空时,禁用内存限制覆盖功能。
memory_request从构建容器请求的内存量。
memory_request_overwrite_max_allowed构建容器的内存配额请求可被写入的最大值。为空时,禁用内存请求覆盖功能。
helper_memory_limit分配给构建辅助容器的内存量。
helper_memory_limit_overwrite_max_allowed辅助容器的内存配额可被写入的最大值。为空时,禁用内存限制覆盖功能。
helper_memory_request为构建辅助容器请求的内存量。
helper_memory_request_overwrite_max_allowed辅助容器的内存配额请求可被写入的最大值。为空时,禁用内存请求覆盖功能。
service_memory_limit分配给构建服务容器的内存量。
service_memory_limit_overwrite_max_allowed服务容器的内存配额可被写入的最大值。为空时,禁用内存限制覆盖功能。
service_memory_request为构建服务容器请求的内存量。
service_memory_request_overwrite_max_allowed服务容器的内存配额请求可被写入的最大值。为空时,禁用内存请求覆盖功能。
pod_memory_limit分配给构建 pod 的内存量。
pod_memory_limit_overwrite_max_allowed构建 pod 的内存配额可被写入的最大值。为空时,禁用内存限制覆盖功能。
pod_memory_request为构建 pod 请求的内存量。
pod_memory_request_overwrite_max_allowed构建 pod 的内存配额请求可被写入的最大值。为空时,禁用内存请求覆盖功能。

辅助容器内存大小建议#

为获得最佳性能,请根据您的工作负载需求设置辅助容器内存限制:

  • 具有缓存和产物生成的工作负载:至少 250 MiB
  • 无缓存/产物的基本工作负载:可能可以在较低限制(128-200 MiB)下运行

基本配置示例:

toml
1[[runners]] 2 executor = "kubernetes" 3 [runners.kubernetes] 4 helper_memory_limit = "250Mi" 5 helper_memory_request = "250Mi" 6 helper_memory_limit_overwrite_max_allowed = "1Gi"

作业特定的内存覆盖:

使用 KUBERNETES_HELPER_MEMORY_LIMIT 变量为特定作业调整内存,而无需管理员更改:

yaml
job_with_higher_helper_memory_limit: variables: KUBERNETES_HELPER_MEMORY_LIMIT: "512Mi" script:

此方法允许开发者按作业优化资源使用,同时通过 helper_memory_limit_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_ephemeral_storage_request分配给辅助容器的临时存储请求。
helper_ephemeral_storage_request_overwrite_max_allowed辅助容器的临时存储请求可被覆盖的最大值。为空时,禁用临时存储请求覆盖功能。
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_groups可为容器组指定的组 ID 数组。如果未提供,则允许所有组。有关更多信息,请参阅配置容器用户和组。
allowed_images可在 .gitlab-ci.yml中指定的镜像通配符列表。如果未提供,则允许所有镜像(等同于 ["*/*:*"])。查看详情。
allowed_pull_policies可在 .gitlab-ci.yml 文件或 config.toml 文件中指定的拉取策略列表。
allowed_services可在 .gitlab-ci.yml中指定的服务通配符列表。如果未提供,则允许所有镜像(等同于 ["*/*:*"])。查看详情。
allowed_users可为容器用户指定的用户 ID 数组。如果未提供,则允许所有用户。有关更多信息,请参阅配置容器用户和组。
automount_service_account_token布尔值,用于控制服务账号令牌是否自动挂载到构建 pod 中。
bearer_token用于启动构建 pod 的默认持有者令牌。
bearer_token_overwrite_allowed布尔值,用于允许项目指定用于创建构建 pod 的持有者令牌。
build_container_security_context为构建容器设置容器安全上下文。详细了解安全上下文。
cap_add指定应添加到作业 pod 容器的 Linux 能力。详细了解 Kubernetes 执行器中的能力配置。
cap_drop指定应从作业 pod 容器中移除的 Linux 能力。详细了解 Kubernetes 执行器中的能力配置。
cleanup_grace_period_seconds作业完成后,pod 必须优雅终止的时长(以秒为单位)。超过此时间后,进程将被强制终止并发送 kill 信号。如果指定了 terminationGracePeriodSeconds,则忽略此项。
context从 kubectl 配置中使用的 Kubernetes 上下文名称(当未指定 host 时)。
dns_policy指定构建 pod 时应使用的 DNS 策略:none、default、cluster-first、cluster-first-with-host-net。如果未设置,则使用 Kubernetes 默认值(cluster-first)。
dns_config指定构建 pod 时应使用的 DNS 配置。详细了解如何使用 pod 的 DNS 配置。
enable_service_links布尔值,用于控制构建 pod 是否使用 Docker links 语法注入与服务相关的环境变量。Kubernetes 默认启用此设置。设置为 false 可防止构建环境中出现不需要的变量。
helper_container_security_context为辅助容器设置容器安全上下文。详细了解安全上下文。
helper_image(高级)覆盖用于克隆代码仓库和上传产物的默认辅助镜像。
helper_image_flavor设置辅助镜像的变体(alpine、alpine3.21、alpine-latest 或 ubuntu)。默认为 alpine。使用 alpine 与 alpine-latest 相同。
host_aliases添加到所有容器的其他主机名别名列表。详细了解如何使用额外主机别名。
host_users布尔值,用于在 pod 规范中设置 hostUsers。如果为 true,则 pod 使用主机的用户命名空间。如果为 false,则 pod 在其自己的用户命名空间中运行。如果未设置,则应用集群默认值。如果容器使用 proc_mount = "unmasked" 或作业使用挂起和恢复,则 runner 始终将 hostUsers 设置为 false。有关更多信息,请参阅用户命名空间。
host_users_overwrite_allowed布尔值,用于允许作业通过 KUBERNETES_HOST_USERS CI/CD 变量覆盖 host_users。默认为 false。如果为 true,则作业可以将 KUBERNETES_HOST_USERS 设置为 true,并关闭 host_users = false 提供的用户命名空间隔离。
image_pull_secrets包含 Kubernetes docker-registry 密钥名称的条目数组,用于对从私有镜像仓库拉取 Docker 镜像进行身份验证。
init_permissions_container_security_context为 init-permissions 容器设置容器安全上下文。详细了解安全上下文。
namespace运行 Kubernetes Pod 的命名空间。
namespace_per_job将作业隔离到单独的命名空间中。如果启用,则忽略 namespace 和 namespace_overwrite_allowed。
namespace_overwrite_allowed用于验证命名空间覆盖环境变量(如下所述)内容的正则表达式。当为空时,它会禁用命名空间覆盖功能。
node_selector一个 table,包含 key=value 对,格式为 string=string(对于环境变量则为 string:string)。设置此项会将 pod 的创建限制为与所有 key=value 对匹配的 Kubernetes 节点。详细了解如何使用节点选择器。
node_tolerations一个 table,包含 "key=value" = "Effect" 对,格式为 string=string:string。设置此项允许 pod 调度到具有全部或部分容忍污点的节点。通过环境变量配置只能提供一个容忍。key、value 和 effect 与 Kubernetes pod 容忍配置中的相应字段名匹配。
pod_annotations一个 table,包含 key=value 对,格式为 string=string。该 table 包含要添加到 runner 创建的每个构建 pod 的注解列表。这些值可以包含用于展开的环境变量。Pod 注解可以在每次构建中覆盖。
pod_annotations_overwrite_allowed用于验证 pod 注解覆盖环境变量内容的正则表达式。当为空时,它会禁用 pod 注解覆盖功能。
pod_labels一个 table,包含 key=value 对,格式为 string=string。该 table 包含要添加到 runner 创建的每个构建 pod 的标签列表。这些值可以包含用于展开的环境变量。Pod 标签可以在每次构建中使用 pod_labels_overwrite_allowed 来覆盖。
pod_labels_overwrite_allowed用于验证 pod 标签覆盖环境变量内容的正则表达式。当为空时,它会禁用 pod 标签覆盖功能。请注意,runner.gitlab.com 标签命名空间中的 pod 标签无法覆盖。
pod_security_context通过配置文件进行配置,为构建 pod 设置 pod 安全上下文。详细了解安全上下文。
pod_termination_grace_period_secondsPod 级别的设置,用于确定 pod 必须优雅终止的时长(以秒为单位)。超过此时间后,进程将被强制终止并发送 kill 信号。如果指定了 terminationGracePeriodSeconds,则忽略此项。
poll_intervalrunner 轮询其刚刚创建的 Kubernetes pod 以检查其状态的频率(以秒为单位,默认值 = 3)。
poll_timeoutrunner 尝试连接到其刚刚创建的容器超时之前需要经过的时间(以秒为单位)。当排队的构建数量超过集群一次可处理的数量时,请使用此设置(默认值 = 180)。
cleanup_resources_timeout作业完成后清理 Kubernetes 资源的总时长。支持的语法:1h30m、300s、10m。默认值为 5 分钟(5m)。
priority_class_name指定要为 pod 设置的优先级类。如果未设置,则使用默认值。
privileged在启用特权标志的情况下运行容器。
pull_policy指定镜像拉取策略:never、if-not-present、always。如果未设置,则使用集群的镜像默认拉取策略。有关如何设置多个拉取策略的更多信息和说明,请参阅使用拉取策略。另请参阅 if-not-present、never 安全注意事项。您还可以限制拉取策略。
resource_availability_check_max_attempts在放弃之前检查资源(服务账号和/或拉取密钥)集是否可用的最大尝试次数。每次尝试之间有 5 秒的间隔。详细了解准备步骤期间的资源检查。
runtime_class_name用于所有已创建 pod 的运行时类。如果集群不支持该功能,作业将退出或失败。
service_container_security_context为服务容器设置容器安全上下文。详细了解安全上下文。
scheduler_name用于调度构建 pod 的调度器。
service_account作业/执行器 pod 用于与 Kubernetes API 通信的默认服务账号。
service_account_overwrite_allowed用于验证服务账号覆盖环境变量内容的正则表达式。当为空时,它会禁用服务账号覆盖功能。
services使用边车模式附加到构建容器的服务列表。详细了解使用服务。
use_service_account_image_pull_secrets启用后,由执行器创建的 pod 将缺少 imagePullSecrets。如果已设置,这会导致使用服务账号中的 imagePullSecrets 创建 pod。
suspend_pvc_size为可挂起作业环境创建的 PVC 大小。PVC 存储 overlayfs 上层、构建目录以及使用 suspend_data_path 配置的任何服务容器数据目录。默认为 20Gi。
suspend_pvc_storage_class由 suspend_pvc_size 创建的 PVC 的 StorageClass 名称。当为空时,PVC 使用集群的默认 StorageClass。
terminationGracePeriodSeconds向 pod 中运行的进程发送终止信号后,到使用 kill 信号强制终止进程之间的时长。已弃用,请改用 cleanup_grace_period_seconds 和 pod_termination_grace_period_seconds。
volumes通过配置文件进行配置,即挂载到构建容器中的卷列表。详细了解如何使用卷。
pod_spec此设置为实验性功能。使用在用于运行 CI 作业的 pod 上设置的一组配置来覆盖由 Runner Manager 生成的 pod 规范。可以设置 Kubernetes Pod Specification中列出的所有属性。有关更多信息,请参阅覆盖生成的 pod 规范(实验)。
retry_limit与 Kubernetes API 通信的最大尝试次数。每次尝试之间的重试间隔基于从 500 毫秒开始的退避算法。
retry_backoff_max每次尝试重试间隔要达到的自定义最大退避值(以毫秒为单位)。默认值为 2000 毫秒,且不能低于 500 毫秒。每次尝试要达到的默认最大重试间隔为 2 秒,可使用 retry_backoff_max 进行自定义。
retry_limits每个请求错误的重试次数。
logs_base_dir要添加到生成路径前面的基础目录,用于存储构建日志。有关更多信息,请参阅更改构建日志和脚本的基础目录。
scripts_base_dir要添加到生成路径前面的基础目录,用于存储构建脚本。有关更多信息,请参阅更改构建日志和脚本的基础目录。
print_pod_warning_events控制 runner 是否打印作业 pod 的 Kubernetes 警告消息,包括 pod 启动期间(例如调度失败)以及作业失败后检索警告事件时。默认启用。作业失败事件检索需要具有至少 events: list 权限的服务账号。
pod_disruption_budget启用后,会为每个作业 pod 创建一个 PodDisruptionBudget,以防止在节点排空和集群升级等自愿中断期间被驱逐。默认禁用。需要具有 poddisruptionbudgets 权限的服务账号。

配置示例#

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

toml
1concurrent = 4 2 3[[runners]] 4 name = "myRunner" 5 url = "https://gitlab.com/ci" 6 token = "......" 7 executor = "kubernetes" 8 [runners.kubernetes] 9 host = "https://45.67.34.123:4892" 10 cert_file = "/etc/ssl/kubernetes/api.crt" 11 key_file = "/etc/ssl/kubernetes/api.key" 12 ca_file = "/etc/ssl/kubernetes/ca.crt" 13 namespace = "gitlab" 14 namespace_overwrite_allowed = "ci-.*" 15 bearer_token_overwrite_allowed = true 16 privileged = true 17 cpu_limit = "1" 18 memory_limit = "1Gi" 19 service_cpu_limit = "1" 20 service_memory_limit = "1Gi" 21 helper_cpu_limit = "500m" 22 helper_memory_limit = "100Mi" 23 poll_interval = 5 24 poll_timeout = 3600 25 dns_policy = "cluster-first" 26 priority_class_name = "priority-1" 27 logs_base_dir = "/tmp" 28 scripts_base_dir = "/tmp" 29 [runners.kubernetes.node_selector] 30 gitlab = "true" 31 [runners.kubernetes.node_tolerations] 32 "node-role.kubernetes.io/master" = "NoSchedule" 33 "custom.toleration=value" = "NoSchedule" 34 "empty.value=" = "PreferNoSchedule" 35 "onlyKey" = ""

使用暂停 Pod 预热集群容量#

您可以配置 Kubernetes 执行器来维护暂停 Pod,以预热集群容量。当作业启动时,低优先级的暂停 Pod 会被抢占,作业 pod 会立即调度到现有节点上。此配置可减少作业启动延迟,无需等待集群自动扩缩器预配新节点。

暂停 Pod 的工作原理#

  1. Runner 根据配置的策略创建暂停 Pod 的 Deployment。
  2. 暂停 Pod 使用低优先级类,因此当更高优先级的作业 pod 需要资源时,Kubernetes 会抢占它们。
  3. 当暂停 Pod 被抢占时,作业 pod 会立即取代它。
  4. Deployment 会重新创建被抢占的暂停 Pod,这可能会触发集群自动扩缩器添加新节点。

配置暂停 Pod#

要启用暂停 Pod,请在 config.toml中添加[runners.kubernetes.autoscaler]部分:

toml
1[[runners]] 2 name = "kubernetes-runner" 3 executor = "kubernetes" 4 [runners.kubernetes] 5 namespace = "gitlab-runner" 6 cpu_request = "500m" 7 memory_request = "1Gi" 8 [runners.kubernetes.autoscaler] 9 max_pause_pods = 10 10 [[runners.kubernetes.autoscaler.policy]] 11 idle_count = 5 12 periods = ["* 8-17 * * mon-fri"] 13 timezone = "UTC" 14 [[runners.kubernetes.autoscaler.policy]] 15 idle_count = 0 16 periods = ["* * * * *"]

自动扩缩器设置#

设置描述
max_pause_pods要创建的暂停 Pod 最大数量。设置为 0 表示无限制。
pause_pod_image暂停 Pod 的镜像。默认为 registry.k8s.io/pause:3.10。
pause_pod_priority_class_name暂停 Pod 的优先级类。默认为 gitlab-runner-idle-capacity(自动创建,优先级为-1)。如果指定,则跳过自动创建。

用于抢占的优先级类#

要使暂停 Pod 被作业 pod 抢占,它们必须具有较低的优先级。默认情况下,Runner 会自动创建一个名为 gitlab-runner-idle-capacity的 PriorityClass,优先级为-1。由于没有优先级类的 pod 使用优先级 0,作业 pod 将抢占暂停 Pod。

要改用自定义 PriorityClass,请在配置中指定它:

toml
[runners.kubernetes.autoscaler] pause_pod_priority_class_name = "my-custom-priority-class"

如果您的作业 pod 使用自定义优先级类,请确保其值高于暂停 Pod 的优先级类。

策略设置#

您可以定义多个策略。将使用基于当前时间最后匹配的策略。

设置描述
periods定义此策略何时处于活动状态的 cron 表达式数组。默认为* * * * *(始终)。
timezone用于计算 cron 表达式的时区。默认为系统本地时间。
idle_count要维护的暂停 Pod 目标数量。默认为 0。
idle_time缩容冷却时间。当期望容量减少时,暂停 Pod 会在此等待时间后被移除。防止使用 scale_factor 时出现抖动。默认为 5m。
scale_factor根据活动作业缩放暂停 Pod:max(idle_count, active_jobs * scale_factor)。默认为 0(禁用)。
scale_factor_limit使用 scale_factor 时暂停 Pod 的最大数量。默认为 0(无限制)。

Cron 语法#

periods 设置使用包含五个字段的标准 cron 格式:

plaintext
1 ┌────────── minute (0 - 59) 2 │ ┌──────── hour (0 - 23) 3 │ │ ┌────── day of month (1 - 31) 4 │ │ │ ┌──── month (1 - 12) 5 │ │ │ │ ┌── day of week (0 - 7, where 0 and 7 are Sunday, or MON-SUN) 6 * * * * *

示例:

周期描述
* * * * *始终活动
* 8-17 * * mon-fri工作日 8:00-17:59
* 0-12 * * *每天午夜至 12:59

创建优先级类#

暂停 Pod 需要优先级低于作业 pod 的优先级类。在配置暂停 Pod 之前创建该优先级类:

yaml
1apiVersion: scheduling.k8s.io/v1 2kind: PriorityClass 3metadata: 4 name: pause-pods 5value: -10 6globalDefault: false 7description: "Low priority class for runner pause pods"

所需的 RBAC 权限#

要使用暂停 Pod,请为 Runner 服务账号配置其他权限,以管理 Deployments 和 PriorityClasses:

yaml
1- apiGroups: ["apps"] 2 resources: ["deployments"] 3 verbs: ["get", "list", "create", "update", "delete"] 4- apiGroups: ["scheduling.k8s.io"] 5 resources: ["priorityclasses"] 6 verbs: ["get", "create"]

PriorityClass 是集群范围的资源。命名空间级的 Role 和 RoleBinding 无法授予 scheduling.k8s.io/priorityclasses 权限。 请改用 ClusterRole 和 ClusterRoleBinding。

配置执行器服务账号#

要配置执行器服务账号,您可以设置 KUBERNETES_SERVICE_ACCOUNT 环境变量或使用--kubernetes-service-account 标志。

Pod 和容器#

您可以配置 Pod 和容器,以控制作业的执行方式。

作业 Pod 的默认标签#

您无法通过 Runner 配置或 .gitlab-ci.yml 文件覆盖这些标签。 任何在 runner.gitlab.com 命名空间中设置或修改标签的尝试 都会被忽略,并记录为调试消息。

键描述
project.runner.gitlab.com/id项目的 ID,在极狐GitLab 实例的所有项目中唯一。
project.runner.gitlab.com/name项目的名称。
project.runner.gitlab.com/namespace-id项目命名空间的 ID。
project.runner.gitlab.com/namespace项目命名空间的名称。
project.runner.gitlab.com/root-namespace项目根命名空间的名称。例如,/gitlab-org/group-a/subgroup-a/project,其中根命名空间为 gitlab-org
project.runner.gitlab.com/root-namespace-id项目根命名空间的 ID。
manager.runner.gitlab.com/name启动此作业的 Runner 配置的名称。
manager.runner.gitlab.com/id-short启动作业的 Runner 配置的 ID。
job.runner.gitlab.com/podKubernetes 执行器使用的内部标签。

作业 Pod 的默认注解#

以下注解默认添加到运行作业的 Pod 上:

键描述
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作业的名称。
job.runner.gitlab.com/timeout作业执行超时时间,采用时间长度格式。例如,2h3m0.5s。
project.runner.gitlab.com/id作业所属项目的 ID。

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

Pod 生命周期#

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

  • 在 TOML 配置文件中设置 pod_termination_grace_period_seconds 属性。 在发送 TERM 信号后,Pod 上运行的进程可以在给定的时长内继续运行。 如果 Pod 在此时间段后仍未成功终止,则会发送 kill 信号。
  • 启用 FF_USE_POD_ACTIVE_DEADLINE_SECONDS 功能标志。 启用后,当作业超时时,运行 CI/CD 作业的 Pod 会被标记为 失败,并且所有关联的容器都会被终止。为了让作业先在极狐GitLab 上超时, activeDeadlineSeconds 被设置为 configured timeout + 1 second。

如果您启用 FF_USE_POD_ACTIVE_DEADLINE_SECONDS 功能标志,并将 pod_termination_grace_period_seconds 设置为非零值,则 CI/CD 作业 Pod 不会立即终止。Pod 的 terminationGracePeriods 确保 Pod 仅在其到期时才被终止。

保护作业 Pod 免遭驱逐#

要保护作业 Pod 免遭自愿中断(例如节点排空和集群升级),请开启 pod_disruption_budget 选项。

开启后,此设置会为每个作业 Pod 创建一个 PodDisruptionBudget, 并设置 minAvailable: 1。此操作可防止 Kubernetes 驱逐 API 在自愿中断期间驱逐该 Pod。

toml
[runners.kubernetes] pod_disruption_budget = true

PodDisruptionBudget:

  • 当作业 Pod 通过 Kubernetes 所有者引用被删除时,会自动删除。
  • 无法防止非自愿中断,例如节点故障或内存不足导致的终止。
  • 需要额外的 RBAC 权限。有关详情,请参阅配置 Runner API 权限。

开启 PodDisruptionBudget 后,如果有作业正在运行,可能会导致节点排空挂起。请确保您的集群升级 策略考虑到可能的节点排空延迟,或使用作业超时来限制作业可以运行的时间。

覆盖 Pod 容忍#

要覆盖 Kubernetes Pod 容忍:

  1. 在 config.toml 或 Helm values.yaml 文件中,要启用 CI 作业 Pod 容忍的覆盖,请为 node_tolerations_overwrite_allowed 定义一个正则表达式。 此正则表达式会验证以 KUBERNETES_NODE_TOLERATIONS_ 开头的 CI 变量名称的值。

    toml
    1runners: 2 ... 3 config: | 4 [[runners]] 5 [runners.kubernetes] 6 node_tolerations_overwrite_allowed = ".*"
  2. 在 .gitlab-ci.yml 文件中,定义一个或多个 CI 变量以覆盖 CI 作业 Pod 容忍。

    yaml
    1variables: 2 KUBERNETES_NODE_TOLERATIONS_1: 'node-role.kubernetes.io/master:NoSchedule' 3 KUBERNETES_NODE_TOLERATIONS_2: 'custom.toleration=value:NoSchedule' 4 KUBERNETES_NODE_TOLERATIONS_3: 'empty.value=:PreferNoSchedule' 5 KUBERNETES_NODE_TOLERATIONS_4: 'onlyKey' 6 KUBERNETES_NODE_TOLERATIONS_5: '' # tolerate all taints

覆盖 Pod 标签#

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

  1. 在 .config.yaml 文件中,为 pod_labels_overwrite_allowed 定义一个正则表达式。

  2. 在 .gitlab-ci.yml 文件中,使用 key=value 值设置 KUBERNETES_POD_LABELS_* 变量。Pod 标签会被覆盖为 key=value。您可以应用多个值:

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

runner.gitlab.com 命名空间中的标签是只读的。极狐GitLab 会忽略任何添加、修改或删除这些极狐GitLab 内部标签的尝试。

覆盖 Pod 注解#

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

  1. 在 .config.yaml 文件中,为 pod_annotations_overwrite_allowed 定义一个正则表达式。

  2. 在 .gitlab-ci.yml 文件中,设置 KUBERNETES_POD_ANNOTATIONS_* 变量,并使用 key=value 作为值。 Pod 注解会被覆盖为 key=value。您可以指定多个注解:

    yaml
    variables: KUBERNETES_POD_ANNOTATIONS_1: "Key1=Val1" KUBERNETES_POD_ANNOTATIONS_2: "Key2=Val2" KUBERNETES_POD_ANNOTATIONS_3: "Key3=Val3"

在下面的示例中,设置了 pod_annotations 和 pod_annotations_overwrite_allowed。此配置允许覆盖在 config.toml中配置的任何 pod_annotations。

toml
1[[runners]] 2 # usual configuration 3 executor = "kubernetes" 4 [runners.kubernetes] 5 image = "alpine" 6 pod_annotations_overwrite_allowed = ".*" 7 [runners.kubernetes.pod_annotations] 8 "Key1" = "Val1" 9 "Key2" = "Val2" 10 "Key3" = "Val3" 11 "Key4" = "Val4"

覆盖生成的 Pod 规范#

Status: 测试版

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

要在该功能正式发布之前提供反馈,请在议题 556286中留下评论。

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

有关 Runner Operator 特定的配置,请参阅 patch 结构。

pod_spec 设置:

  • 覆盖并补全生成的 Pod 规范的字段。
  • 覆盖您可能在 config.toml中 [runners.kubernetes] 下设置的配置值。

您可以配置多个 pod_spec 设置。

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

您不能在同一 pod_spec 配置中同时设置 patch_path 和 patch,否则会发生错误。

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

toml
1[[runners]] 2 [runners.kubernetes] 3 [[runners.kubernetes.pod_spec]] 4 name = "hostname" 5 patch = ''' 6 hostname: "custom-pod-hostname" 7 ''' 8 patch_type = "merge" 9 [[runners.kubernetes.pod_spec]] 10 name = "subdomain" 11 patch = ''' 12 subdomain: "subdomain" 13 ''' 14 patch_type = "strategic" 15 [[runners.kubernetes.pod_spec]] 16 name = "terminationGracePeriodSeconds" 17 patch = ''' 18 [{"op": "replace", "path": "/terminationGracePeriodSeconds", "value": 60}] 19 ''' 20 patch_type = "json"

合并补丁策略#

merge patch 策略会对现有 PodSpec 应用键值替换。如果您使用此策略,config.toml中的 pod_spec 配置覆盖生成前最终 PodSpec 对象中的值。由于这些值会被完全覆盖,因此应谨慎使用此 patch 策略。

使用 merge patch 策略的 pod_spec 配置示例:

toml
1concurrent = 1 2check_interval = 1 3log_level = "debug" 4shutdown_timeout = 0 5 6[session_server] 7 session_timeout = 1800 8 9[[runners]] 10 name = "" 11 url = "https://gitlab.example.com" 12 id = 0 13 token = "__REDACTED__" 14 token_obtained_at = 0001-01-01T00:00:00Z 15 token_expires_at = 0001-01-01T00:00:00Z 16 executor = "kubernetes" 17 shell = "bash" 18 environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"] 19 [runners.kubernetes] 20 image = "alpine" 21 ... 22 [[runners.kubernetes.pod_spec]] 23 name = "build envvars" 24 patch = ''' 25 containers: 26 - env: 27 - name: env1 28 value: "value1" 29 - name: env2 30 value: "value2" 31 name: build 32 ''' 33 patch_type = "merge"

使用此配置后,最终的 PodSpec 只有一个名为 build的容器,其中包含两个环境变量 env1 和 env2。上面的示例会导致相关 CI 作业失败,因为:

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

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

JSON 补丁策略#

json patch 策略使用 JSON Patch 规范 来控制要更新的 PodSpec 对象和数组。您不能对 array 属性使用此策略。

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

toml
1concurrent = 1 2check_interval = 1 3log_level = "debug" 4shutdown_timeout = 0 5 6[session_server] 7 session_timeout = 1800 8 9[[runners]] 10 name = "" 11 url = "https://gitlab.example.com" 12 id = 0 13 token = "__REDACTED__" 14 token_obtained_at = 0001-01-01T00:00:00Z 15 token_expires_at = 0001-01-01T00:00:00Z 16 executor = "kubernetes" 17 shell = "bash" 18 environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"] 19 [runners.kubernetes] 20 image = "alpine" 21 ... 22 [[runners.kubernetes.pod_spec]] 23 name = "val1 node" 24 patch = ''' 25 [{ "op": "add", "path": "/nodeSelector", "value": { key1: "val1" } }] 26 ''' 27 patch_type = "json"

策略合并补丁策略#

此 strategic patch 策略使用应用于 PodSpec 对象每个字段的现有 patchStrategy。

使用 strategic patch 策略的 pod_spec 配置示例。在此配置中,会在 build 容器上设置 resource request。

toml
1concurrent = 1 2check_interval = 1 3log_level = "debug" 4shutdown_timeout = 0 5 6[session_server] 7 session_timeout = 1800 8 9[[runners]] 10 name = "" 11 url = "https://gitlab.example.com" 12 id = 0 13 token = "__REDACTED__" 14 token_obtained_at = 0001-01-01T00:00:00Z 15 token_expires_at = 0001-01-01T00:00:00Z 16 executor = "kubernetes" 17 shell = "bash" 18 environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"] 19 [runners.kubernetes] 20 image = "alpine" 21 ... 22 [[runners.kubernetes.pod_spec]] 23 name = "cpu request 500m" 24 patch = ''' 25 containers: 26 - name: build 27 resources: 28 requests: 29 cpu: "500m" 30 ''' 31 patch_type = "strategic"

使用此配置后,会在 build 容器上设置 resource request。

最佳实践#

  • 在生产环境中部署之前,先在测试环境中测试添加的 pod_spec。
  • 确保 pod_spec 配置不会对极狐GitLab Runner 生成的规范产生负面影响。
  • 不要对复杂的 Pod 规范更新使用 merge patch 策略。
  • 在配置可用的情况下,尽可能使用 config.toml。例如,以下配置会将由极狐GitLab Runner 设置的第一个环境变量替换为自定义 pod_spec中设置的环境变量,而不是将环境变量集添加到现有列表中。
toml
1concurrent = 1 2check_interval = 1 3log_level = "debug" 4shutdown_timeout = 0 5 6[session_server] 7 session_timeout = 1800 8 9[[runners]] 10 name = "" 11 url = "https://gitlab.example.com" 12 id = 0 13 token = "__REDACTED__" 14 token_obtained_at = 0001-01-01T00:00:00Z 15 token_expires_at = 0001-01-01T00:00:00Z 16 executor = "kubernetes" 17 shell = "bash" 18 environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true", "CUSTOM_VAR=value"] 19 [runners.kubernetes] 20 image = "alpine" 21 ... 22 [[runners.kubernetes.pod_spec]] 23 name = "build envvars" 24 patch = ''' 25 containers: 26 - env: 27 - name: env1 28 value: "value1" 29 name: build 30 ''' 31 patch_type = "strategic"

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

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

Kubernetes 允许您创建一个附加到 Pod 生命周期的临时 PersistentVolumeClaim。 如果您的 Kubernetes 集群启用了动态预配,则此方法可行。每个 PVC 都可以请求一个新的 Volume。该卷也与 Pod 的生命周期绑定。

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

toml
1[[runners.kubernetes.pod_spec]] 2 name = "ephemeral-pvc" 3 patch = ''' 4 containers: 5 - name: build 6 volumeMounts: 7 - name: builds 8 mountPath: /builds 9 - name: helper 10 volumeMounts: 11 - name: builds 12 mountPath: /builds 13 volumes: 14 - name: builds 15 ephemeral: 16 volumeClaimTemplate: 17 spec: 18 storageClassName: <The Storage Class that will dynamically provision a Volume> 19 accessModes: [ ReadWriteOnce ] 20 resources: 21 requests: 22 storage: 1Gi 23 '''

为 Pod 设置安全策略#

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

使用以下选项:

选项类型必需描述
fs_groupint否应用于 Pod 中所有容器的特殊补充组。
run_as_groupint否用于运行容器进程入口点的 GID。
run_as_non_rootboolean否指示容器必须以非 root 用户运行。
run_as_userint否用于运行容器进程入口点的 UID。
supplemental_groupsint list否除容器的主 GID 外,应用于每个容器中运行的第一个进程的组列表。
selinux_typestring否应用于 Pod 中所有容器的 SELinux 类型标签。
seccomp_profile.typestring否seccomp 配置文件类型。有效值:RuntimeDefault、Localhost、Unconfined。
seccomp_profile.localhost_profilestring否节点上 seccomp 配置文件的路径。当类型为 Localhost 时必需。
app_armor_profile.typestring否AppArmor 配置文件类型。有效值:RuntimeDefault、Localhost、Unconfined。需要 Kubernetes 1.30 或更高版本。
app_armor_profile.localhost_profilestring否节点上 AppArmor 配置文件的名称。当类型为 Localhost 时必需。

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

toml
1concurrent = %(concurrent)s 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 helper_image = "gitlab-registry.example.com/helper:latest" 9 [runners.kubernetes.pod_security_context] 10 run_as_non_root = true 11 run_as_user = 59417 12 run_as_group = 59417 13 fs_group = 59417

移除旧的 Runner Pod#

有时旧的 Runner Pod 不会被清理。当 Runner Manager 被错误关闭时,可能会发生这种情况。

要处理这种情况,您可以使用极狐GitLab Runner Pod Cleanup 应用来安排旧 Pod 的清理。有关更多信息,请参阅:

  • 极狐GitLab Runner Pod Cleanup 项目 README。
  • 极狐GitLab Runner Pod Cleanup 文档。

为容器设置安全策略#

在 config.toml 执行器中配置容器安全上下文, 为 build、helper 或 service Pod 设置容器安全策略。

使用以下选项:

选项类型必需描述
run_as_groupint否用于运行容器进程入口点的 GID。
run_as_non_rootboolean否指示容器必须以非 root 用户运行。
run_as_userint否用于运行容器进程入口点的 UID。
capabilities.addstring list否运行容器时要添加的能力。
capabilities.dropstring list否运行容器时要移除的能力。
selinux_typestring否与容器进程关联的 SELinux 类型标签。
seccomp_profile.typestring否seccomp 配置文件类型。有效值:RuntimeDefault、Localhost、Unconfined。
seccomp_profile.localhost_profilestring否节点上 seccomp 配置文件的路径。当类型为 Localhost 时必需。
app_armor_profile.typestring否AppArmor 配置文件类型。有效值:RuntimeDefault、Localhost、Unconfined。需要 Kubernetes 1.30 或更高版本。
app_armor_profile.localhost_profilestring否节点上 AppArmor 配置文件的名称。当类型为 Localhost 时必需。

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

  • 设置 Pod 安全上下文。
  • 为 build 和 helper 容器覆盖 run_as_user 和 run_as_group。
  • 指定所有服务容器从 Pod 安全上下文继承 run_as_user 和 run_as_group。
toml
1concurrent = 4 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 helper_image = "gitlab-registry.example.com/helper:latest" 9 [runners.kubernetes.pod_security_context] 10 run_as_non_root = true 11 run_as_user = 59417 12 run_as_group = 59417 13 fs_group = 59417 14 [runners.kubernetes.init_permissions_container_security_context] 15 run_as_user = 1000 16 run_as_group = 1000 17 [runners.kubernetes.build_container_security_context] 18 run_as_user = 65534 19 run_as_group = 65534 20 [runners.kubernetes.build_container_security_context.capabilities] 21 add = ["NET_ADMIN"] 22 [runners.kubernetes.helper_container_security_context] 23 run_as_user = 1000 24 run_as_group = 1000 25 [runners.kubernetes.service_container_security_context] 26 run_as_user = 1000 27 run_as_group = 1000

设置 seccomp 和 AppArmor 配置文件#

您可以使用嵌套的 seccomp_profile 和 app_armor_profile 配置节, 为 build Pod 配置 seccomp 和 AppArmor 配置文件。

这些字段使用原生 Kubernetes API 字段替换了已弃用的基于注解的方法(container.apparmor.security.beta.kubernetes.io 和 seccomp.security.alpha.kubernetes.io 注解)。

字段最低 Kubernetes 版本
seccomp_profile1.19(正式发布)
app_armor_profile1.30(正式发布)

在下面的示例中,build 容器的 seccomp 和 AppArmor 配置文件被设置为 Unconfined, 以启用无 root 镜像构建(例如使用 BuildKit):

toml
1concurrent = 4 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 [runners.kubernetes.pod_security_context] 9 run_as_non_root = true 10 run_as_user = 1001 11 [runners.kubernetes.pod_security_context.seccomp_profile] 12 type = "RuntimeDefault" 13 [runners.kubernetes.build_container_security_context] 14 run_as_user = 1001 15 run_as_group = 1001 16 [runners.kubernetes.build_container_security_context.seccomp_profile] 17 type = "Unconfined" 18 [runners.kubernetes.build_container_security_context.app_armor_profile] 19 type = "Unconfined"

seccomp_profile 和 app_armor_profile 节在 pod_security_context 和所有容器安全上下文中均可用 (build_container_security_context、helper_container_security_context、 service_container_security_context、init_permissions_container_security_context)。

对于 Localhost 类型的配置文件,请指定配置文件路径:

toml
1[runners.kubernetes.build_container_security_context.seccomp_profile] 2 type = "Localhost" 3 localhost_profile = "profiles/my-seccomp-profile.json" 4 5[runners.kubernetes.build_container_security_context.app_armor_profile] 6 type = "Localhost" 7 localhost_profile = "my-apparmor-profile"

设置拉取策略#

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

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

对于单个拉取策略:

toml
[runners.kubernetes] pull_policy = "never"

对于多个拉取策略:

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

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

要重试失败的拉取:

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

极狐GitLab 的命名约定与 Kubernetes 的不同。

Runner 拉取策略Kubernetes 拉取策略描述
nonenone使用 Kubernetes 指定的默认策略。
if-not-presentIfNotPresent仅当执行作业的节点上尚不存在该镜像时才拉取镜像。在使用此拉取策略之前,请查看安全注意事项。
alwaysAlways每次执行作业时都会拉取镜像。
neverNever从不拉取镜像,要求节点上已有该镜像。

指定容器能力#

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

要指定容器能力,请使用 config.toml中的 cap_add 和 cap_drop 选项。容器运行时也可以定义默认的能力列表,例如 Docker 或 container中的列表。

Runner 默认会移除一系列能力。您在 cap_add 选项中列出的能力不会被移除。

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

toml
1concurrent = 1 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 # ... 9 cap_add = ["SYS_TIME", "IPC_LOCK"] 10 cap_drop = ["SYS_ADMIN"] 11 # ...

当您指定能力时:

  • 用户定义的 cap_drop 优先于用户定义的 cap_add。如果您在两个设置中定义了相同的能力, 则只有 cap_drop中的能力会传递给容器。
  • 从传递给容器配置的能力标识符中移除 CAP_ 前缀。 例如,如果您想添加或移除 CAP_SYS_TIME 能力, 则在配置文件中输入字符串 SYS_TIME。
  • Kubernetes 集群的所有者 可以定义 PodSecurityPolicy, 其中允许、限制或默认添加特定能力。这些规则优先于任何用户定义的配置。

配置容器用户和组#

使用 Kubernetes 安全上下文配置来配置容器运行所使用的用户和组。管理员可以控制容器安全性,并允许作业为特定容器类型指定用户。

不支持在 Windows 的作业定义中设置 runAsUser、runAsGroup 或 image:user。 建议改为通过 FF_USE_ADVANCED_POD_SPEC_CONFIGURATION 设置 runAsUserName。

配置优先级#

Runner 按以下顺序应用用户配置:

对于 build 和服务容器:

  1. 容器安全上下文(run_as_user/run_as_group):管理员控制此配置
  2. Pod 安全上下文(run_as_user/run_as_group):管理员控制 Pod 级别的默认值
  3. 作业配置(.gitlab-ci.yml):用户控制此配置

对于 helper 容器:

  1. Helper 容器安全上下文(run_as_user/run_as_group):管理员控制此配置
  2. Pod 安全上下文(run_as_user/run_as_group):管理员控制 Pod 级别的默认值

出于安全隔离考虑,作业配置不适用于 helper 容器。

管理员可以覆盖用户指定的值以满足安全合规要求。Helper 容器与作业规范保持隔离。

Kubernetes 的要求#

Kubernetes 要求用户和组 ID 使用数值:

  • 用户和组 ID 必须是整数
  • SecurityContext 使用 run_as_user 和 run_as_group,并且只接受数值
  • 在作业配置中,仅指定用户时使用 "1000",同时指定用户和组时使用 "1000:1001"

覆盖用户和组设置#

使用 Pod 和容器特定的安全上下文来覆盖用户和组设置:

toml
1[[runners]] 2 name = "k8s-runner" 3 url = "https://gitlab.example.com" 4 executor = "kubernetes" 5 [runners.kubernetes] 6 allowed_users = ["1000", "1001", "65534"] 7 allowed_groups = ["1001", "65534"] 8 9 # Pod security context - provides defaults for all containers 10 [runners.kubernetes.pod_security_context] 11 run_as_user = 1500 12 run_as_group = 1500 13 14 # Build container security context - overrides pod context 15 [runners.kubernetes.build_container_security_context] 16 run_as_user = 2000 17 run_as_group = 2001 18 19 # Helper container security context - overrides pod context 20 [runners.kubernetes.helper_container_security_context] 21 run_as_user = 3000 22 run_as_group = 3001 23 24 # Service container security context - overrides pod context 25 [runners.kubernetes.service_container_security_context] 26 run_as_user = 4000 27 run_as_group = 4001

在此示例中:

  • Pod 安全上下文为没有特定配置的容器设置默认值(1500:1500)
  • 容器安全上下文覆盖 Pod 默认值
  • 用户 1500、2000、3000 和 4000 不在 allowed_users 列表中,但安全上下文可以使用它们,因为这些值会绕过允许名单验证
  • 此能力使管理员可以在 Pod 和容器级别进行不受限制的覆盖控制

您可以独立配置每种容器类型。安全上下文配置优先于作业配置中的任何用户指定。

在作业配置中指定用户#

作业可以在镜像配置中指定用户:

yaml
1# Job with custom user 2job: 3 image: 4 name: alpine:latest 5 kubernetes: 6 user: "1000" 7 script: 8 - whoami 9 - id 10 11# Job with user and group 12job_with_group: 13 image: 14 name: alpine:latest 15 kubernetes: 16 user: "1000:1001" 17 script: 18 - whoami 19 - id 20 21# Job using environment variable 22job_dynamic: 23 image: 24 name: alpine:latest 25 kubernetes: 26 user: "${CUSTOM_USER_ID}" 27 variables: 28 CUSTOM_USER_ID: "1000" 29 script: 30 - whoami

安全验证#

Runner 仅针对作业级配置根据允许名单验证用户和组 ID:

  • Root 用户/组(UID/GID 0):作业配置始终需要显式的允许名单权限
  • 空的 allowed_users:允许任何非 root 作业用户
  • 指定了 allowed_users:仅允许列出的作业用户
  • 空的 allowed_groups:允许任何非 root 作业组
  • 指定了 allowed_groups:仅允许列出的作业组
  • 安全上下文配置:不根据允许名单验证(管理员覆盖)
toml
[runners.kubernetes] allowed_users = ["1000", "65534"] allowed_groups = ["1001", "65534"]

容器行为和优先级#

安全上下文配置遵循以下优先级顺序(从高到低):

  1. 容器安全上下文
  2. Pod 安全上下文
  3. 作业配置
toml
1[runners.kubernetes] 2 # Pod-level defaults 3 [runners.kubernetes.pod_security_context] 4 run_as_user = 1500 5 run_as_group = 1500 6 7 # Container-specific overrides 8 [runners.kubernetes.build_container_security_context] 9 run_as_user = 1000 10 run_as_group = 1001 11 [runners.kubernetes.helper_container_security_context] 12 run_as_user = 1000 13 run_as_group = 1001
yaml
job: image: name: alpine:latest kubernetes: user: "2000:2001" # Ignored - container security context uses 1000:1001

每种容器类型都使用其安全上下文配置,并以 Pod 级别作为回退:

  • Build 容器:首先使用 build_container_security_context,然后使用 pod_security_context,最后使用 .gitlab-ci.yml中的作业级用户配置。
  • Helper 容器:首先使用 helper_container_security_context,然后使用 pod_security_context。不继承作业级用户配置。
  • 服务容器:首先使用 service_container_security_context,然后使用 pod_security_context,最后使用作业级用户配置。

此方法使您可以对每种容器类型的安全配置进行精细控制,同时使 helper 容器与作业规范保持隔离。

与 Docker 执行器的比较#

功能Docker 执行器Kubernetes 执行器
用户格式用户名或 UID(root 或 1000)仅限数值 UID(1000)
组格式用户字段中不支持数值 GID(1000:1001)
管理员覆盖方法Runner user 字段容器和 Pod 安全上下文
优先级Runner > 作业容器上下文 > Pod 上下文 > 作业
安全验证用户名允许名单数值 UID/GID 允许名单
管理员覆盖支持支持(Pod 和容器级别)
Helper 容器用户与 build 容器相同使用自己的 helper_container_security_context
Pod 级别默认值不可用pod_security_context

排查用户和组配置问题#

错误:failed to parse UID 或 failed to parse GID#
  • 确保用户 ID 是数值:"1000" 而不是 "user"
  • 检查格式:用户和组使用 "1000:1001"
  • 不允许使用负值
错误:user "1000" is not in the allowed list#

此错误仅发生在作业级用户配置(.gitlab-ci.yml)中。请将该用户添加到 Runner 配置中的 allowed_users,或移除 allowed_users 以允许任何非 root 作业用户。安全上下文和 Pod 安全上下文中的用户不根据允许名单验证。

错误:group "1001" is not in the allowed list#

此错误仅发生在作业级组配置(.gitlab-ci.yml)中。请将该组添加到 Runner 配置中的 allowed_groups,或移除 allowed_groups 以允许任何非 root 作业组。安全上下文和 Pod 安全上下文中的组不根据允许名单验证。

错误:user "0" is not in the allowed list(Root 用户被阻止)#

此错误仅在作业配置(.gitlab-ci.yml)中指定 root 时发生。来自作业配置的 root 用户(UID 0)需要显式权限:将 "0" 添加到 allowed_users。或者,使用安全上下文或 Pod 安全上下文设置 root 用户:run_as_user = 0(绕过允许名单验证)。

容器以与预期不同的用户运行#

检查 Runner 配置是否使用安全上下文覆盖了作业配置(安全上下文始终优先)。如果仅使用作业配置,则验证 allowed_users 是否包含所需的用户 ID。安全上下文值不根据允许名单验证,并提供管理员覆盖能力。

覆盖容器资源#

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

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

变量的值受该资源的最大覆盖设置限制。如果未为某个资源设置最大覆盖,则不使用该变量。

yaml
1 variables: 2 KUBERNETES_CPU_REQUEST: "3" 3 KUBERNETES_CPU_LIMIT: "5" 4 KUBERNETES_MEMORY_REQUEST: "2Gi" 5 KUBERNETES_MEMORY_LIMIT: "4Gi" 6 KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "512Mi" 7 KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "1Gi" 8 9 KUBERNETES_HELPER_CPU_REQUEST: "3" 10 KUBERNETES_HELPER_CPU_LIMIT: "5" 11 KUBERNETES_HELPER_MEMORY_REQUEST: "2Gi" 12 KUBERNETES_HELPER_MEMORY_LIMIT: "4Gi" 13 KUBERNETES_HELPER_EPHEMERAL_STORAGE_REQUEST: "512Mi" 14 KUBERNETES_HELPER_EPHEMERAL_STORAGE_LIMIT: "1Gi" 15 16 KUBERNETES_SERVICE_CPU_REQUEST: "3" 17 KUBERNETES_SERVICE_CPU_LIMIT: "5" 18 KUBERNETES_SERVICE_MEMORY_REQUEST: "2Gi" 19 KUBERNETES_SERVICE_MEMORY_LIMIT: "4Gi" 20 KUBERNETES_SERVICE_EPHEMERAL_STORAGE_REQUEST: "512Mi" 21 KUBERNETES_SERVICE_EPHEMERAL_STORAGE_LIMIT: "1Gi"

定义服务列表#

在 config.toml中定义服务列表。

toml
1concurrent = 1 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 helper_image = "gitlab-registy.example.com/helper:latest" 9 [[runners.kubernetes.services]] 10 name = "postgres:12-alpine" 11 alias = "db1" 12 [[runners.kubernetes.services]] 13 name = "registry.example.com/svc1" 14 alias = "svc1" 15 entrypoint = ["entrypoint.sh"] 16 command = ["executable","param1","param2"] 17 environment = ["ENV=value1", "ENV2=value2"] 18 [[runners.kubernetes.services]] 19 name = "redis:7" 20 alias = "cache" 21 suspend_data_path = "/data" # persists Redis data across suspend/resume cycles

每个 [[runners.kubernetes.services]] 条目支持以下字段:

字段描述
name服务的镜像路径(必需)。
alias用作容器和 DNS 名称的别名,以空格或逗号分隔。
entrypoint覆盖镜像入口点。
command覆盖镜像命令。
environment注入到服务容器中的 KEY=value 环境变量列表。
suspend_data_path服务容器内写入持久数据的路径(例如,Redis 使用 /data,PostgreSQL 使用 /var/lib/postgresql/data)。

suspend_data_path的行为取决于您的配置:

  • 在可挂起作业(启用了 FF_SUSPENDABLE_ENVIRONMENTS)中,如果未设置 command 或 entrypoint, Runner 会在该路径挂载挂起 PVC 的子目录,以便数据在挂起和 恢复周期中得以保留。
  • 如果设置了 command 或 entrypoint,Runner 会改为持久化整个服务容器, 因此此设置不会生效。
  • 当为空且未设置 command 或 entrypoint 时,服务容器没有持久存储。

如果服务环境包含 HEALTHCHECK_TCP_PORT,极狐GitLab Runner 会等待服务在该端口上响应,然后再启动用户 CI 脚本。您还可以在 .gitlab-ci.yml的 services 节中配置 HEALTHCHECK_TCP_PORT 环境变量。

覆盖服务容器资源#

如果作业有多个服务容器,您可以为每个服务容器设置显式的资源请求和限制。使用每个服务中的 variables 属性来覆盖在 .gitlab-ci.yml中指定的容器资源。

yaml
1 services: 2 - name: redis:5 3 alias: redis5 4 variables: 5 KUBERNETES_SERVICE_CPU_REQUEST: "3" 6 KUBERNETES_SERVICE_CPU_LIMIT: "6" 7 KUBERNETES_SERVICE_MEMORY_REQUEST: "3Gi" 8 KUBERNETES_SERVICE_MEMORY_LIMIT: "6Gi" 9 KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "2Gi" 10 KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "3Gi" 11 - name: postgres:12 12 alias: MY_relational-database.12 13 variables: 14 KUBERNETES_CPU_REQUEST: "2" 15 KUBERNETES_CPU_LIMIT: "4" 16 KUBERNETES_MEMORY_REQUEST: "1Gi" 17 KUBERNETES_MEMORY_LIMIT: "2Gi" 18 KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "1Gi" 19 KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "2Gi"

这些特定设置优先于作业的常规设置。这些值仍受该资源的最大覆盖设置限制。

覆盖 Kubernetes 默认服务账号#

要在 .gitlab-ci.yml 文件中为每个 CI/CD 作业覆盖 Kubernetes 服务账号,请设置变量 KUBERNETES_SERVICE_ACCOUNT_OVERWRITE。

您可以使用此变量指定附加到命名空间的服务账号,在复杂的 RBAC 配置中可能需要这样做。

yaml
variables: KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: ci-service-account

为确保 CI 运行期间仅使用指定的服务账号,请为以下任一项定义正则表达式:

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

如果两者都未设置,则覆盖处于禁用状态。

设置 RuntimeClass#

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

如果您指定了 RuntimeClass 名称,但未在集群中配置它,或者不支持该功能,则执行器无法创建作业。

toml
1concurrent = 1 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "gitlab.example.com" 6 executor = "kubernetes" 7 [runners.kubernetes] 8 runtime_class_name = "myclass"

更改构建日志和脚本的基础目录#

您可以更改将 emptyDir 卷挂载到 Pod 以用于构建日志和脚本的目录。您可以使用该目录来:

  • 使用修改后的镜像运行作业 Pod。
  • 以非特权用户运行。
  • 自定义 SecurityContext 设置。

要更改目录:

  • 对于构建日志,设置 logs_base_dir。
  • 对于构建脚本,设置 scripts_base_dir。

预期值是一个表示基础目录的字符串,不带尾部斜杠(例如,/tmp 或 /mydir/example)。该目录必须已存在。

此值会添加到构建日志和脚本生成的路径前面。例如:

toml
1[[runners]] 2 name = "myRunner" 3 url = "gitlab.example.com" 4 executor = "kubernetes" 5 [runners.kubernetes] 6 logs_base_dir = "/tmp" 7 scripts_base_dir = "/tmp"

此配置会导致 emptyDir 卷挂载到:

  • 构建日志挂载到 /tmp/logs-${CI_PROJECT_ID}-${CI_JOB_ID}, 而不是默认的 /logs-${CI_PROJECT_ID}-${CI_JOB_ID}。
  • 构建脚本挂载到 /tmp/scripts-${CI_PROJECT_ID}-${CI_JOB_ID}。

用户命名空间#

在 Kubernetes 1.30 及更高版本中,您可以使用用户命名空间将容器中运行的用户与主机上的用户隔离。在容器中以 root 身份运行的进程可以在主机上以不同的非特权用户身份运行。

使用用户命名空间,您可以更好地控制用于运行 CI/CD 作业的镜像。需要额外设置的操作(例如以 root 身份运行)也可以正常工作,而不会在主机上增加额外的攻击面。

要使用此功能,请确保您的集群已正确配置。以下示例为 hostUsers 键添加了 pod_spec,并同时禁用了特权 Pod 和权限提升:

toml
1[[runners]] 2 environment = ["FF_USE_ADVANCED_POD_SPEC_CONFIGURATION=true"] 3 builds_dir = "/tmp/builds" 4[runners.kubernetes] 5 logs_base_dir = "/tmp" 6 scripts_base_dir = "/tmp" 7 privileged = false 8 allowPrivilegeEscalation = false 9[[runners.kubernetes.pod_spec]] 10 name = "hostUsers" 11 patch = ''' 12 [{"op": "add", "path": "/hostUsers", "value": false}] 13 ''' 14 patch_type = "json"

使用用户命名空间时,您不能使用构建目录(builds_dir)、构建日志(logs_base_dir)或构建脚本(scripts_base_dir)的默认路径。即使是容器的 root 用户也没有挂载卷的权限。他们也无法在容器文件系统的根目录中创建目录。

您可以改为更改构建日志和脚本的基础目录。您还可以通过设置 [[runners]].builds_dir 来更改构建目录。

操作系统、架构和 Windows 内核版本#

使用 Kubernetes 执行器的极狐GitLab Runner 可以在不同的 操作系统上运行构建,前提是所配置的集群有运行这些操作系统的节点。

系统会确定 helper 镜像的操作系统、架构和 Windows 内核版本(如果适用)。然后它会将这些参数用于构建的其他方面,例如要使用的容器或镜像。

下图说明了系统如何检测这些详细信息:

Rendering chart...

以下是唯一会影响构建的操作系统、架构和 Windows 内核版本选择的参数。

  • helper_image_autoset_arch_and_os 配置
  • 来自以下来源的 kubernetes.io/os、kubernetes.io/arch 和 node.kubernetes.io/windows-build 标签选择器:
    • node_selector 配置
    • node_selector 覆盖

其他参数不会影响上述选择过程。但是,您可以使用 affinity 等参数来进一步限制调度构建的节点。

节点#

指定执行构建的节点#

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

Runner 使用提供的信息来确定构建的操作系统和架构。这可确保使用正确的 helper 镜像。默认的操作系统和架构是 linux/amd64。

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

linux/arm64 示例#

toml
1[[runners]] 2 name = "myRunner" 3 url = "gitlab.example.com" 4 executor = "kubernetes" 5 6 [runners.kubernetes.node_selector] 7 "kubernetes.io/arch" = "arm64" 8 "kubernetes.io/os" = "linux"

windows/amd64 示例#

适用于 Windows 的 Kubernetes 有某些限制。 如果您使用进程隔离,还必须使用 node.kubernetes.io/windows-build 标签提供特定的 Windows 构建版本。

toml
1[[runners]] 2 name = "myRunner" 3 url = "gitlab.example.com" 4 executor = "kubernetes" 5 6 # The FF_USE_POWERSHELL_PATH_RESOLVER feature flag has to be enabled for PowerShell 7 # to resolve paths for Windows correctly when Runner is operating in a Linux environment 8 # but targeting Windows nodes. 9 environment = ["FF_USE_POWERSHELL_PATH_RESOLVER=true"] 10 11 [runners.kubernetes.node_selector] 12 "kubernetes.io/arch" = "amd64" 13 "kubernetes.io/os" = "windows" 14 "node.kubernetes.io/windows-build" = "10.0.20348"

覆盖节点选择器#

要覆盖节点选择器:

  1. 在 config.toml 或 Helm values.yaml 文件中,启用节点选择器的覆盖:

    toml
    1runners: 2 ... 3 config: | 4 [[runners]] 5 [runners.kubernetes] 6 node_selector_overwrite_allowed = ".*"
  2. 在 .gitlab-ci.yml 文件中,定义变量以覆盖节点选择器:

    yaml
    variables: KUBERNETES_NODE_SELECTOR_* = ''

在下面的示例中,要覆盖 Kubernetes 节点架构,相关设置在 config.toml 和 .gitlab-ci.yml 文件中配置:

toml
1concurrent = 1 2check_interval = 1 3log_level = "debug" 4shutdown_timeout = 0 5 6listen_address = ':9252' 7 8[session_server] 9 session_timeout = 1800 10 11[[runners]] 12 name = "" 13 url = "https://gitlab.com/" 14 id = 0 15 token = "__REDACTED__" 16 token_obtained_at = "0001-01-01T00:00:00Z" 17 token_expires_at = "0001-01-01T00:00:00Z" 18 executor = "kubernetes" 19 shell = "bash" 20 [runners.kubernetes] 21 host = "" 22 bearer_token_overwrite_allowed = false 23 image = "alpine" 24 namespace = "" 25 namespace_overwrite_allowed = "" 26 pod_labels_overwrite_allowed = "" 27 service_account_overwrite_allowed = "" 28 pod_annotations_overwrite_allowed = "" 29 node_selector_overwrite_allowed = "kubernetes.io/arch=.*" # <--- allows overwrite of the architecture

定义节点亲和性列表#

定义要添加到构建时 Pod 规范的节点亲和性列表。

node_affinities 不会决定构建应使用哪个操作系统,只有 node_selectors 会。有关更多信息,请参阅操作系统、架构和 Windows 内核版本。 config.toml中的示例配置:

toml
1concurrent = 1 2[[runners]] 3 name = "myRunner" 4 url = "gitlab.example.com" 5 executor = "kubernetes" 6 [runners.kubernetes] 7 [runners.kubernetes.affinity] 8 [runners.kubernetes.affinity.node_affinity] 9 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] 10 weight = 100 11 [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] 12 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] 13 key = "cpu_speed" 14 operator = "In" 15 values = ["fast"] 16 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] 17 key = "mem_speed" 18 operator = "In" 19 values = ["fast"] 20 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] 21 weight = 50 22 [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] 23 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] 24 key = "core_count" 25 operator = "In" 26 values = ["high", "32"] 27 [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_fields]] 28 key = "cpu_type" 29 operator = "In" 30 values = ["arm64"] 31 [runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution] 32 [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms]] 33 [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms.match_expressions]] 34 key = "kubernetes.io/e2e-az-name" 35 operator = "In" 36 values = [ 37 "e2e-az1", 38 "e2e-az2" 39 ]

定义调度 Pod 的节点#

使用 Pod 亲和性和反亲和性,根据其他 Pod 上的标签来约束您的 Pod 有资格调度到的节点。

config.toml中的示例配置:

toml
1concurrent = 1 2[[runners]] 3 name = "myRunner" 4 url = "gitlab.example.com" 5 executor = "kubernetes" 6 [runners.kubernetes] 7 [runners.kubernetes.affinity] 8 [runners.kubernetes.affinity.pod_affinity] 9 [[runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution]] 10 topology_key = "failure-domain.beta.kubernetes.io/zone" 11 namespaces = ["namespace_1", "namespace_2"] 12 match_label_keys = ["pod-template-hash"] 13 mismatch_label_keys = ["tenant"] 14 [runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution.label_selector] 15 [[runners.kubernetes.affinity.pod_affinity.required_during_scheduling_ignored_during_execution.label_selector.match_expressions]] 16 key = "security" 17 operator = "In" 18 values = ["S1"] 19 [[runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution]] 20 weight = 100 21 [runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term] 22 topology_key = "failure-domain.beta.kubernetes.io/zone" 23 [runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector] 24 [[runners.kubernetes.affinity.pod_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector.match_expressions]] 25 key = "security_2" 26 operator = "In" 27 values = ["S2"] 28 [runners.kubernetes.affinity.pod_anti_affinity] 29 [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution]] 30 topology_key = "failure-domain.beta.kubernetes.io/zone" 31 namespaces = ["namespace_1", "namespace_2"] 32 [runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.label_selector] 33 [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.label_selector.match_expressions]] 34 key = "security" 35 operator = "In" 36 values = ["S1"] 37 [runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.namespace_selector] 38 [[runners.kubernetes.affinity.pod_anti_affinity.required_during_scheduling_ignored_during_execution.namespace_selector.match_expressions]] 39 key = "security" 40 operator = "In" 41 values = ["S1"] 42 [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution]] 43 weight = 100 44 [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term] 45 topology_key = "failure-domain.beta.kubernetes.io/zone" 46 [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector] 47 [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.label_selector.match_expressions]] 48 key = "security_2" 49 operator = "In" 50 values = ["S2"] 51 [runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.namespace_selector] 52 [[runners.kubernetes.affinity.pod_anti_affinity.preferred_during_scheduling_ignored_during_execution.pod_affinity_term.namespace_selector.match_expressions]] 53 key = "security_2" 54 operator = "In" 55 values = ["S2"]

match_label_keys 和 mismatch_label_keys 需要 Kubernetes 1.31 或更高版本。 在较早的 alpha 或 beta 集群上,请启用 MatchLabelKeysInPodAffinity 功能门控。在不支持该功能门控的集群上,API 服务器 会忽略这些字段,约束不会生效。

网络#

配置容器生命周期 钩子#

使用容器生命周期钩子,在相应生命周期钩子 执行时,运行针对处理程序配置的代码。

您可以配置两种类型的 钩子:PreStop 和 PostStart。每种 钩子 只允许设置一种处理程序。

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

toml
1[[runners]] 2 name = "kubernetes" 3 url = "https://gitlab.example.com/" 4 executor = "kubernetes" 5 token = "yrnZW46BrtBFqM7xDzE7dddd" 6 [runners.kubernetes] 7 image = "alpine:3.11" 8 privileged = true 9 namespace = "default" 10 [runners.kubernetes.container_lifecycle.post_start.exec] 11 command = ["touch", "/builds/postStart.txt"] 12 [runners.kubernetes.container_lifecycle.pre_stop.http_get] 13 port = 8080 14 host = "localhost" 15 path = "/test" 16 [[runners.kubernetes.container_lifecycle.pre_stop.http_get.http_headers]] 17 name = "header_name_1" 18 value = "header_value_1" 19 [[runners.kubernetes.container_lifecycle.pre_stop.http_get.http_headers]] 20 name = "header_name_2" 21 value = "header_value_2"

使用以下设置配置每个生命周期 钩子:

选项类型必需描述
execKubernetesLifecycleExecAction否Exec 指定要执行的操作。
http_getKubernetesLifecycleHTTPGet否HTTPGet 指定要执行的 HTTP 请求。
tcp_socketKubernetesLifecycleTcpSocket否TCPsocket 指定涉及 TCP 端口的操作。

KubernetesLifecycleExecAction#

选项类型必需描述
commandstring 列表是要在容器内执行的命令行。

KubernetesLifecycleHTTPGet#

选项类型必需描述
portint是要在容器上访问的端口号。
hoststring否要连接的主机名,默认为 Pod IP(可选)。
pathstring否要在 HTTP 服务器上访问的路径(可选)。
schemestring否用于连接主机的协议。默认为 HTTP(可选)。
http_headersKubernetesLifecycleHTTPGetHeader 列表否要在请求中设置的自定义请求头(可选)。

KubernetesLifecycleHTTPGetHeader#

选项类型必需描述
namestring是HTTP 请求头名称。
valuestring是HTTP 请求头值。

KubernetesLifecycleTcpSocket#

选项类型必需描述
portint是要在容器上访问的端口号。
hoststring否要连接的主机名,默认为 Pod IP(可选)。

配置 Pod DNS 设置#

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

选项类型必需描述
nameserversstring 列表否用作 Pod DNS 服务器的 IP 地址列表。
optionsKubernetesDNSConfigOption否可选的对象列表,其中每个对象可以有一个 name 属性(必需)和一个 value 属性(可选)。
searchesstring 列表否用于在 Pod 中进行主机名查找的 DNS 搜索域列表。

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

toml
1concurrent = 1 2check_interval = 30 3[[runners]] 4 name = "myRunner" 5 url = "https://gitlab.example.com" 6 token = "__REDACTED__" 7 executor = "kubernetes" 8 [runners.kubernetes] 9 image = "alpine:latest" 10 [runners.kubernetes.dns_config] 11 nameservers = [ 12 "1.2.3.4", 13 ] 14 searches = [ 15 "ns1.svc.cluster-domain.example", 16 "my.dns.search.suffix", 17 ] 18 19 [[runners.kubernetes.dns_config.options]] 20 name = "ndots" 21 value = "2" 22 23 [[runners.kubernetes.dns_config.options]] 24 name = "edns0"

KubernetesDNSConfigOption#

选项类型必需描述
namestring是配置选项名称。
value*string否配置选项值。

默认丢弃的能力列表#

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

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

  • NET_RAW

添加额外的主机别名#

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

配置主机别名,指示 Kubernetes 向容器中的 /etc/hosts 文件添加条目。

使用以下选项:

选项类型必需描述
IPstring是您要将主机附加到的 IP 地址。
Hostnamesstring 列表是附加到该 IP 的主机名别名列表。

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

toml
1concurrent = 4 2 3[[runners]] 4 # usual configuration 5 executor = "kubernetes" 6 [runners.kubernetes] 7 [[runners.kubernetes.host_aliases]] 8 ip = "127.0.0.1" 9 hostnames = ["web1", "web2"] 10 [[runners.kubernetes.host_aliases]] 11 ip = "192.168.1.1" 12 hostnames = ["web14", "web15"]

您还可以使用命令行参数 --kubernetes-host_aliases 并传入 JSON 输入来配置主机别名。例如:

shell
gitlab-runner register --kubernetes-host_aliases '[{"ip":"192.168.1.100","hostnames":["myservice.local"]},{"ip":"192.168.1.101","hostnames":["otherservice.local"]}]'

卷#

在 Kubernetes 执行器中使用缓存#

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

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

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

配置卷类型#

您可以挂载以下卷类型:

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

包含多种卷类型的配置示例:

toml
1concurrent = 4 2 3[[runners]] 4 # usual configuration 5 executor = "kubernetes" 6 [runners.kubernetes] 7 [[runners.kubernetes.volumes.host_path]] 8 name = "hostpath-1" 9 mount_path = "/path/to/mount/point" 10 read_only = true 11 host_path = "/path/on/host" 12 [[runners.kubernetes.volumes.host_path]] 13 name = "hostpath-2" 14 mount_path = "/path/to/mount/point_2" 15 read_only = true 16 [[runners.kubernetes.volumes.pvc]] 17 name = "pvc-1" 18 mount_path = "/path/to/mount/point1" 19 [[runners.kubernetes.volumes.config_map]] 20 name = "config-map-1" 21 mount_path = "/path/to/directory" 22 [runners.kubernetes.volumes.config_map.items] 23 "key_1" = "relative/path/to/key_1_file" 24 "key_2" = "key_2" 25 [[runners.kubernetes.volumes.secret]] 26 name = "secrets" 27 mount_path = "/path/to/directory1" 28 read_only = true 29 [runners.kubernetes.volumes.secret.items] 30 "secret_1" = "relative/path/to/secret_1_file" 31 [[runners.kubernetes.volumes.empty_dir]] 32 name = "empty-dir" 33 mount_path = "/path/to/empty_dir" 34 medium = "Memory" 35 [[runners.kubernetes.volumes.csi]] 36 name = "csi-volume" 37 mount_path = "/path/to/csi/volume" 38 driver = "my-csi-driver" 39 [runners.kubernetes.volumes.csi.volume_attributes] 40 size = "2Gi" 41 [[runners.kubernetes.volumes.nfs]] 42 name = "nfs" 43 mount_path = "/path/to/mount/point" 44 read_only = false 45 server = "foo.bar.com" 46 path = "/path/on/nfs-share"

hostPath 卷#

配置 hostPath 卷,指示 Kubernetes 在容器中挂载指定的主机路径。

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

选项类型必需描述
namestring是卷的名称。
mount_pathstring是卷在容器中挂载的路径。
sub_pathstring否挂载卷内部的子路径,而不是其根路径。
host_pathstring否作为卷挂载的主机上的路径。如果未指定值,则默认为与 mount_path 相同的路径。
read_onlyboolean否将卷设置为只读模式。默认为 false。
mount_propagationstring否在容器之间共享挂载的卷。更多信息,请参见挂载传播。

persistentVolumeClaim 卷#

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

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

选项类型必需描述
namestring是卷的名称,同时也是应使用的 PersistentVolumeClaim的名称。支持变量。更多信息,请参见持久化的每并发构建卷。
mount_pathstring是卷在容器中挂载的路径。
read_onlyboolean否将卷设置为只读模式(默认为 false)。
sub_pathstring否挂载卷中的子路径,而不是根路径。
mount_propagationstring否设置卷的挂载传播模式。更多详情,请参见 Kubernetes 挂载传播。

configMap 卷#

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

在 config.toml中使用以下选项:

选项类型必需描述
namestring是卷的名称,同时也是应使用的 configMap的名称。
mount_pathstring是卷在容器中挂载的路径。
read_onlyboolean否将卷设置为只读模式(默认为 false)。
sub_pathstring否挂载卷中的子路径,而不是根路径。
itemsmap[string]string否应使用的 configMap中键到路径的映射。

configMap中的每个键都会转换为一个文件,并存储在挂载路径中。默认情况下:

  • 包含所有键。
  • configMap 键用作文件名。
  • 值存储在文件内容中。

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

如果使用不存在的键,作业会在 Pod 创建阶段失败。

secret 卷#

配置 secret 卷,指示 Kubernetes 使用 Kubernetes 集群中定义的 secret 并将其挂载到容器中。

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

选项类型必需描述
namestring是卷的名称,同时也是应使用的 secret 的名称。
mount_pathstring是卷应挂载到容器内的路径。
read_onlyboolean否将卷设置为只读模式(默认为 false)。
sub_pathstring否挂载卷中的子路径,而不是根路径。
itemsmap[string]string否应使用的 configMap 中键到路径的映射。

所选 secret中的每个键都会转换为一个文件,存储在所选挂载路径中。默认情况下:

  • 包含所有键。
  • configMap 键用作文件名。
  • 值存储在文件内容中。

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

如果使用不存在的键,作业会在 Pod 创建阶段失败。

emptyDir 卷#

配置 emptyDir 卷,指示 Kubernetes 在容器中挂载一个空目录。

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

选项类型必需描述
namestring是卷的名称。
mount_pathstring是卷应挂载到容器内的路径。
sub_pathstring否挂载卷中的子路径,而不是根路径。
mediumstring否"Memory" 提供 tmpfs,否则默认为节点磁盘存储(默认为 "")。
size_limitstring否emptyDir 卷所需的本地存储总量。
mount_propagationstring否设置卷的挂载传播模式。更多详情,请参见 Kubernetes 挂载传播。

csi 卷#

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

在 config.toml中使用以下选项:

选项类型必需描述
namestring是卷的名称。
mount_pathstring是卷应挂载到容器内的路径。
driverstring是一个字符串值,指定要使用的卷驱动程序名称。
fs_typestring否一个字符串值,指定文件系统类型名称(例如 ext4、xfs、ntfs)。
volume_attributesmap[string]string否csi 卷属性的键值对映射。
sub_pathstring否挂载卷中的子路径,而不是根路径。
read_onlyboolean否将卷设置为只读模式(默认为 false)。

在服务容器上挂载卷#

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

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

toml
1[[runners]] 2 # usual configuration 3 executor = "kubernetes" 4 [runners.kubernetes] 5 [[runners.kubernetes.volumes.empty_dir]] 6 name = "mysql-tmpfs" 7 mount_path = "/var/lib/mysql" 8 medium = "Memory"

自定义卷挂载#

要为作业存储构建目录,请为配置的 builds_dir(默认为 /builds)定义自定义卷挂载。如果使用 pvc 卷,根据访问模式,您可能被限制为只能在一个节点上运行作业。

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

toml
1concurrent = 4 2 3[[runners]] 4 # usual configuration 5 executor = "kubernetes" 6 builds_dir = "/builds" 7 [runners.kubernetes] 8 [[runners.kubernetes.volumes.empty_dir]] 9 name = "repo" 10 mount_path = "/builds" 11 medium = "Memory"

持久化的每并发构建卷#

Kubernetes CI 作业中的构建目录默认是临时的。如果您希望跨作业持久保留 Git 克隆(以使 GIT_STRATEGY=fetch 正常工作),则必须为构建文件夹挂载一个持久卷声明。由于多个作业可能并发运行,您必须使用 ReadWriteMany 卷,或者为同一 Runner 上每个可能的并发作业各准备一个卷。后者可能性能更好。以下是此类配置的示例:

toml
1concurrent = 4 2 3[[runners]] 4 executor = "kubernetes" 5 builds_dir = "/mnt/builds" 6 [runners.kubernetes] 7 [[runners.kubernetes.volumes.pvc]] 8 # CI_CONCURRENT_ID identifies parallel jobs of the same runner. 9 name = "build-pvc-$CI_CONCURRENT_ID" 10 mount_path = "/mnt/builds"

在此示例中,请自行创建名为 build-pvc-0 到 build-pvc-3的持久卷声明。创建的数量应与 Runner 的 concurrent 设置所指定的数量一致。

使用辅助镜像#

设置安全策略后,辅助镜像必须符合该策略。该镜像不会从 root 组获得权限,因此您必须确保用户 ID 属于 root 组。

如果您只需要 nonroot 环境,可以使用 GitLab Runner UBI OpenShift Container Platform 镜像,而不是辅助镜像。您也可以使用 GitLab Runner Helper UBI OpenShift Container Platform 镜像。

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

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

在构建中使用 Docker#

在构建中使用 Docker 时,您应注意以下几点。

暴露 /var/run/docker.sock#

如果您使用 runners.kubernetes.volumes.host_path 选项将主机的 /var/run/docker.sock 暴露到构建容器中,会存在风险。在与生产容器相同的集群中运行构建时请务必小心。节点的容器可以从构建容器中访问。

使用 docker:dind#

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

Docker 守护进程作为 Pod 中的独立容器运行,因为它作为 service 启动,通常在 .gitlab-ci.yml中。Pod 中的容器只共享分配给它们的卷和一个 IP 地址,它们使用该 IP 地址通过 localhost 相互通信。docker:dind 容器不共享 /var/run/docker.sock,而 docker 二进制文件默认会尝试使用它。

要让客户端通过 TCP 连接另一个容器中的 Docker 守护进程,请为构建容器设置以下环境变量:

  • DOCKER_HOST=tcp://docker:2375 用于非 TLS 连接。
  • DOCKER_HOST=tcp://docker:2376 用于 TLS 连接。

在 Docker 19.03 及更高版本中,TLS 默认启用,但您必须将证书映射到客户端。 您可以为 Docker-in-Docker 启用非 TLS 连接,或挂载证书。更多信息,请参见使用 Docker-in-Docker。

防止主机内核暴露#

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

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

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

限制 Docker 镜像和服务#

您可以限制用于运行作业的 Docker 镜像。为此,您可以指定通配符模式。例如,仅允许来自您的私有 Docker 镜像仓库的镜像:

toml
1[[runners]] 2 (...) 3 executor = "kubernetes" 4 [runners.kubernetes] 5 (...) 6 allowed_images = ["my.registry.tld:5000/*:*"] 7 allowed_services = ["my.registry.tld:5000/*:*"]

或者,限制为该镜像仓库中的特定镜像列表:

toml
1[[runners]] 2 (...) 3 executor = "kubernetes" 4 [runners.kubernetes] 5 (...) 6 allowed_images = ["my.registry.tld:5000/ruby:*", "my.registry.tld:5000/node:*"] 7 allowed_services = ["postgres:9.4", "postgres:latest"]

限制 Docker 拉取策略#

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

要限制 .gitlab-ci.yml 文件中指定的可用拉取策略,请使用 allowed_pull_policies。

例如,仅允许 always 和 if-not-present 拉取策略:

toml
1[[runners]] 2 (...) 3 executor = "kubernetes" 4 [runners.kubernetes] 5 (...) 6 allowed_pull_policies = ["always", "if-not-present"]
  • 如果未指定 allowed_pull_policies,则默认为 pull_policy 关键字中的值。
  • 如果未指定 pull_policy,则使用集群的镜像默认拉取策略。
  • 作业仅使用同时列在 pull_policy 和 allowed_pull_policies中的拉取策略。 实际生效的拉取策略通过比较 pull_policy 关键字和 allowed_pull_policies中的策略来确定。极狐GitLab 使用这两个策略列表的交集。 例如,如果 pull_policy 为 ["always", "if-not-present"],而 allowed_pull_policies 为 ["if-not-present"],则作业仅使用 if-not-present,因为它是两个列表中唯一定义的拉取策略。
  • 现有的 pull_policy 关键字必须至少包含 allowed_pull_policies中指定的一个拉取策略。 如果 pull_policy的值与 allowed_pull_policies 均不匹配,则作业失败。

作业执行#

极狐GitLab Runner 默认使用 kube attach 而不是 kube exec。这应能避免在网络不稳定的环境中出现作业中途被标记为成功之类的问题。

有关移除旧版执行策略的进展,请关注议题 #27976。

配置对 Kubernetes API 的请求尝试次数#

默认情况下,Kubernetes 执行器在五次失败尝试后重试对 Kubernetes API 的特定请求。延迟由退避算法控制,下限为 500 毫秒,上限可自定义,默认值为两秒。要配置重试次数,请使用 config.toml 文件中的 retry_limit 选项。同样,对于退避上限,请使用 retry_backoff_max 选项。以下失败会自动重试:

要控制每个错误的重试次数,请使用 retry_limits 选项。retry_limits 分别指定每个错误的重试次数,是一个从错误消息到重试次数的映射。错误消息可以是 Kubernetes API 返回的错误消息的子字符串。retry_limits 选项优先于 retry_limit 选项。

例如,配置 retry_limits 选项,将您环境中的 TLS 相关错误重试 10 次,而不是默认的 5 次:

toml
1[[runners]] 2 name = "myRunner" 3 url = "https://gitlab.example.com/" 4 executor = "kubernetes" 5 [runners.kubernetes] 6 retry_limit = 5 7 8 [runners.kubernetes.retry_limits] 9 "TLS handshake timeout" = 10 10 "tls: internal error" = 10

要重试完全不同的错误,例如 exceeded quota 20 次:

toml
1[[runners]] 2 name = "myRunner" 3 url = "https://gitlab.example.com/" 4 executor = "kubernetes" 5 [runners.kubernetes] 6 retry_limit = 5 7 8 [runners.kubernetes.retry_limits] 9 "exceeded quota" = 20

容器入口点已知问题#

在极狐GitLab 15.1 及更高版本中,当设置了 FF_KUBERNETES_HONOR_ENTRYPOINT 时,Docker 镜像中定义的入口点会与 Kubernetes 执行器一起使用。

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

  • 如果在镜像的 Dockerfile 中定义了入口点,它必须打开一个有效的 shell。否则,作业会挂起。

    • 要打开 shell,系统会将命令作为构建容器的 args 传入。
  • 执行入口点时,文件类型 CI/CD 变量不会写入磁盘。该文件仅在作业的脚本执行期间可访问。

  • 以下 CI/CD 变量在入口点中不可访问。您可以使用 before_script 在运行脚本命令之前进行任何设置更改:

在极狐GitLab Runner 17.4 之前:

  • 入口点日志不会转发到构建日志。
  • 使用带有 kube exec的 Kubernetes 执行器时,极狐GitLab Runner 不会等待入口点打开 shell(参见本节前面的内容)。

从极狐GitLab Runner 17.4 开始,入口点日志现在会被转发。系统会等待入口点运行并生成 shell。这有以下影响:

  • 如果设置了 FF_KUBERNETES_HONOR_ENTRYPOINT,并且镜像的入口点耗时超过 poll_timeout(默认值:180 秒),则构建失败。如果预计入口点运行时间较长,则必须调整 poll_timeout 值(可能还有 poll_interval)。
  • 当设置了 FF_KUBERNETES_HONOR_ENTRYPOINT 和 FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY 时,系统会向构建容器添加一个启动探针,以便了解入口点何时生成 shell。如果自定义入口点使用提供的 args 来生成预期的 shell,则启动探针会自动通过。但是,如果容器镜像生成 shell 时未使用通过 args 传入的命令,则入口点必须通过在构建目录根目录中创建名为 .gitlab-startup-marker的文件来使启动探针通过。 启动探针每隔 poll_interval 检查一次 .gitlab-startup-marker 文件。如果该文件在 poll_timeout 内不存在,则 Pod 被视为不健康,系统会中止构建。

限制对作业变量的访问#

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

  • Pod 的环境部分

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

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

  • 对于 pods 和 configmaps:
    • get
    • watch
    • list
  • 对于 pods/exec 和 pods/attach,使用 create。

授权用户的 RBAC 定义示例:

yaml
1kind: Role 2apiVersion: rbac.authorization.k8s.io/v1 3metadata: 4 name: gitlab-runner-authorized-users 5rules: 6- apiGroups: [""] 7 resources: ["configmaps", "pods"] 8 verbs: ["get", "watch", "list"] 9- apiGroups: [""] 10 resources: ["pods/exec", "pods/attach"] 11 verbs: ["create"]

准备步骤期间的资源检查#

先决条件:

  • 已设置 image_pull_secrets 或 service_account。
  • resource_availability_check_max_attempts 设置为大于零的数字。
  • Kubernetes serviceAccount 与 get 和 list 权限一起使用。

极狐GitLab Runner 检查新的服务账号或密钥是否可用,每次尝试之间间隔 5 秒。

  • 此功能默认禁用。要启用此功能,请将 resource_availability_check_max_attempts 设置为除 0 以外的任何值。 您设置的值定义了 Runner 检查服务账号或密钥的次数。

覆盖 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 变量。

yaml
variables: KUBERNETES_NAMESPACE_OVERWRITE: ci-${CI_COMMIT_REF_SLUG}

此变量不会在您的集群上创建命名空间。在运行作业之前,请确保该命名空间存在。

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

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