极狐 GitLab

Kubernetes 执行器

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

Offering: JihuLab.com, 极狐GitLab 私有化部署

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

Kubernetes 执行器将构建分为多个步骤:

  1. 准备:在 Kubernetes 集群上创建 Pod。此步骤会创建构建和服务运行所需的容器。
  2. 预构建:克隆、恢复缓存,并下载之前阶段的产物。此步骤在 Pod 中的一个特殊容器上运行。
  3. 构建:用户构建。
  4. 构建后:创建缓存,将产物上传至极狐GitLab。此步骤同样使用 Pod 中的特殊容器。

Runner 如何创建 Kubernetes Pod#

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

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

  • 一个定义为 build 的构建容器。
  • 一个定义为 helper 的辅助容器。
  • 一个服务容器,其命名遵循以下逻辑:
    • 如果服务具有一个有效的 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)
eventslist (print_pod_warning_events=true), watch (FF_PRINT_POD_EVENTS=true)
namespacescreate (kubernetes.NamespacePerJob=true), delete (kubernetes.NamespacePerJob=true)
poddisruptionbudgetscreate (pod_disruption_budget=true), get (pod_disruption_budget=true)
podscreate, delete, get, list (使用 Informer), watch (使用 Informer, 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_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)
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: ["events"] 17 verbs: 18 - "list" # Required when `print_pod_warning_events=true` 19 - "watch" # Required when `FF_PRINT_POD_EVENTS=true` 20- apiGroups: [""] 21 resources: ["namespaces"] 22 verbs: 23 - "create" # Required when `kubernetes.NamespacePerJob=true` 24 - "delete" # Required when `kubernetes.NamespacePerJob=true` 25- apiGroups: ["policy"] 26 resources: ["poddisruptionbudgets"] 27 verbs: 28 - "create" # Required when `pod_disruption_budget=true` 29 - "get" # Required when `pod_disruption_budget=true` 30- apiGroups: [""] 31 resources: ["pods"] 32 verbs: 33 - "create" 34 - "delete" 35 - "get" 36 - "list" # Required when using Informers (https://docs.gitlab.com/runner/executors/kubernetes/#informers) 37 - "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) 38- apiGroups: [""] 39 resources: ["pods/attach"] 40 verbs: 41 - "create" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 42 - "delete" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 43 - "get" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 44 - "patch" # Required when `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 45- apiGroups: [""] 46 resources: ["pods/exec"] 47 verbs: 48 - "create" 49 - "delete" 50 - "get" 51 - "patch" 52- apiGroups: [""] 53 resources: ["pods/log"] 54 verbs: 55 - "get" # Required when `FF_KUBERNETES_HONOR_ENTRYPOINT=true`, `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false`, `FF_WAIT_FOR_POD_TO_BE_REACHABLE=true` 56 - "list" # Required when `FF_KUBERNETES_HONOR_ENTRYPOINT=true`, `FF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY=false` 57- apiGroups: ["scheduling.k8s.io"] 58 resources: ["priorityclasses"] 59 verbs: 60 - "create" # Required when `kubernetes.autoscaler` 61 - "get" # Required when `kubernetes.autoscaler` 62- apiGroups: [""] 63 resources: ["secrets"] 64 verbs: 65 - "create" 66 - "delete" 67 - "get" 68 - "update" 69- apiGroups: [""] 70 resources: ["serviceaccounts"] 71 verbs: 72 - "get" 73- apiGroups: [""] 74 resources: ["services"] 75 verbs: 76 - "create" 77 - "get"

其他详细信息:

Informer#

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

Informer 需要针对 podslistwatch 权限。当执行器启动构建时,它会检查 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 令牌。
bearer_token_overwrite_allowed布尔值,允许项目指定用于创建构建 Pod 的 bearer 令牌。
build_container_security_context为构建容器设置容器安全上下文。阅读更多关于安全上下文的内容
cap_add指定应添加到作业 Pod 容器的 Linux capabilities。阅读更多关于 Kubernetes 执行器中 capabilities 配置的内容
cap_drop指定应从作业 Pod 容器中移除的 Linux capabilities。阅读更多关于 Kubernetes 执行器中 capabilities 配置的内容
cleanup_grace_period_seconds作业完成后,Pod 优雅终止的持续时间(秒)。超过此时间后,进程将被 kill 信号强制终止。如果指定了 terminationGracePeriodSeconds,则忽略此设置。
contextkubectl 配置中使用的 Kubernetes 上下文名称(当未指定 host 时)。
dns_policy指定构建 Pod 时应使用的 DNS 策略:nonedefaultcluster-firstcluster-first-with-host-net。如果未设置,则使用 Kubernetes 默认值(cluster-first)。
dns_config指定构建 Pod 时应使用的 DNS 配置。阅读更多关于使用 Pod DNS 配置的内容
helper_container_security_context为辅助容器设置容器安全上下文。阅读更多关于安全上下文的内容
helper_image(高级)覆盖用于克隆代码仓库和上传产物的默认辅助镜像
helper_image_flavor设置辅助镜像 flavor(alpinealpine3.21alpine-latestubuntu)。默认为 alpine。使用 alpine 等同于 alpine-latest
host_aliases添加到所有容器的额外主机名别名列表。阅读更多关于使用额外主机别名的内容
image_pull_secrets包含 Kubernetes docker-registry 密钥名称的数组项,用于从私有镜像仓库拉取 Docker 镜像时进行身份验证。
init_permissions_container_security_context为 init-permissions 容器设置容器安全上下文。阅读更多关于安全上下文的内容
namespace运行 Kubernetes Pod 的命名空间。
namespace_per_job在单独的命名空间中隔离作业。如果启用,则忽略 namespacenamespace_overwrite_allowed
namespace_overwrite_allowed用于验证命名空间覆盖环境变量内容的正则表达式(下文有说明)。为空时,禁用命名空间覆盖功能。
node_selector一个 table,包含格式为 string=stringkey=value 键值对(环境变量情况下为 string:string)。设置此项可将 Pod 的创建限制在匹配所有 key=value 键值对的 Kubernetes 节点上。阅读更多关于使用节点选择器的内容
node_tolerations一个 table,包含格式为 string=string:string"key=value" = "Effect" 键值对。设置此项允许 Pod 调度到具有全部或部分可容忍污点的节点。通过环境变量配置只能提供一个容忍度。keyvalueeffect 与 Kubernetes Pod 容忍度配置中相应的字段名匹配。
pod_annotations一个 table,包含格式为 string=stringkey=value 键值对。table 包含要添加到 Runner 创建的每个构建 Pod 的注解列表。其值可以包含用于展开的环境变量。Pod 注解可以在每次构建中被覆盖。
pod_annotations_overwrite_allowed用于验证 Pod 注解覆盖环境变量内容的正则表达式。为空时,禁用 Pod 注解覆盖功能。
pod_labels一个 table,包含格式为 string=stringkey=value 键值对。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 资源的总时间。支持的语法:1h30m300s10m。默认为 5 分钟(5m)。
priority_class_name指定要设置到 Pod 的 Priority Class。如果未设置,则使用默认值。
privileged使用特权标志运行容器。
pull_policy指定镜像拉取策略:neverif-not-presentalways。如果未设置,则使用集群的镜像默认拉取策略。有关如何设置多个拉取策略的更多信息和说明,请参见使用拉取策略。另请参见 if-not-presentnever 安全注意事项。您还可以限制拉取策略
resource_availability_check_max_attempts在放弃之前检查资源(服务账户和/或拉取密钥)集是否可用的最大尝试次数。每次尝试之间间隔 5 秒。阅读更多关于准备步骤中资源检查的内容
runtime_class_name用于所有创建的 Pod 的 Runtime class。如果集群不支持该功能,作业将退出或失败。
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。这会导致 Pod 使用来自服务账户的 imagePullSecrets(如果已设置)创建。
terminationGracePeriodSecondsPod 中运行的进程收到终止信号后到进程被 kill 信号强制终止之间的持续时间。已弃用,改用 cleanup_grace_period_secondspod_termination_grace_period_seconds
volumes通过配置文件配置,挂载到构建容器中的卷列表。阅读更多关于使用卷的内容
pod_spec此设置为实验性功能。使用运行 CI 作业的 Pod 上设置的一组配置覆盖 Runner 管理器生成的 Pod 规格。可以设置 Kubernetes Pod Specification 中列出的所有属性。更多信息,请参见覆盖生成的 Pod 规格(实验性)
retry_limit与 Kubernetes API 通信的最大尝试次数。每次尝试之间的重试间隔基于从 500 ms 开始的退避算法。
retry_backoff_max每次尝试重试间隔可达到的自定义最大退避值(毫秒)。默认值为 2000 ms,且不能低于 500 ms。每次尝试可达到的默认最大重试间隔为 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-capacityPriorityClass,优先级为 -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 服务账户配置额外权限以管理 DeploymentsPriorityClasses

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 是集群范围的资源。命名空间级别的 RoleRoleBinding 无法授予 scheduling.k8s.io/priorityclasses 权限。 请改用 ClusterRoleClusterRoleBinding

配置执行器服务账户#

要配置执行器服务账户,您可以设置 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项目根命名空间的 ID。例如,/gitlab-org/group-a/subgroup-a/project,其中根命名空间为 gitlab-org
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 属性。 Pod 上运行的进程可以在 TERM 信号发送后继续运行给定的时长。 如果 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 免于驱逐 {#protect-job-pods-from-eviction}

为保护作业 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 文件中,为 node_tolerations_overwrite_allowed 定义一个正则表达式,以启用对 CI 作业 Pod 容忍度的覆盖。 此正则表达式用于验证以 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 文件中,将 KUBERNETES_POD_LABELS_* 变量设置为 key=value 的值。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_annotationspod_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 规格#

状态:测试版

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

要在该功能正式发布前提供反馈,请在 issue 556286 上留言。

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

有关 Runner Operator 的特定配置,请参见补丁结构

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 对象的策略。可接受的值为 mergejsonstrategic

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

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 补丁策略会对现有的 PodSpec 应用键值替换。 如果使用此策略,config.toml 中的 pod_spec 配置会在最终 PodSpec 对象生成前覆盖其中的值。由于值会被完全覆盖,您应谨慎使用此补丁策略。

使用 merge 补丁策略的 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 的容器,并带有两个环境变量 env1env2。上面的示例会导致相关 CI 作业失败,原因如下:

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

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

JSON 补丁策略#

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

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

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 补丁策略使用现有的 patchStrategy,将其应用于 PodSpec 对象的每个字段。

使用 strategic 补丁策略的 pod_spec 配置示例。在此配置中,构建容器上设置了一个 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"

使用此配置,构建容器上会设置一个 resource request

最佳实践#

  • 在生产环境中部署之前,先在测试环境中测试添加的 pod_spec
  • 确保 pod_spec 配置不会对 GitLab Runner 生成的规格产生负面影响。
  • 不要对复杂的 Pod 规格更新使用 merge 补丁策略。
  • 在配置可用的情况下,尽可能使用 config.toml。例如,以下配置会用自定义 pod_spec 中设置的环境变量替换由 GitLab Runner 设置的第一个环境变量,而不是将环境变量集添加到现有列表中。
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 列表除容器的主 GID 外,应用于每个容器中第一个进程的组列表。
selinux_typestring适用于 Pod 中所有容器的 SELinux 类型标记。
seccomp_profile.typestringseccomp 配置文件类型。有效值:RuntimeDefaultLocalhostUnconfined
seccomp_profile.localhost_profilestring节点上 seccomp 配置文件的路径。当类型为 Localhost 时必填。
app_armor_profile.typestringAppArmor 配置文件类型。有效值:RuntimeDefaultLocalhostUnconfined。需要 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 执行器中配置容器安全上下文,为构建、辅助或服务 Pod 设置容器安全策略。

使用以下选项:

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

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

  • 设置 Pod 安全上下文。
  • 为构建和辅助容器覆盖 run_as_userrun_as_group
  • 指定所有服务容器从 Pod 安全上下文继承 run_as_userrun_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_profileapp_armor_profile 配置部分,为构建 Pod 配置 seccompAppArmor 配置文件。

这些字段用原生 Kubernetes API 字段取代了已弃用的基于注解的方法(container.apparmor.security.beta.kubernetes.ioseccomp.security.alpha.kubernetes.io 注解)。

字段最低 Kubernetes 版本
seccomp_profile1.19(GA)
app_armor_profile1.30(GA)

在以下示例中,为构建容器将 seccomp 和 AppArmor 配置文件设置为 Unconfined,以启用无根镜像构建(例如,使用 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_profileapp_armor_profile 部分在 pod_security_context 和所有容器安全上下文(build_container_security_contexthelper_container_security_contextservice_container_security_contextinit_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 参数来指定一个或多个拉取策略。 该策略控制镜像的获取和更新方式,并适用于构建镜像、辅助镜像和任何服务。

要确定使用哪种策略,请参见关于拉取策略的 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_addcap_drop 选项。容器运行时也可以定义默认的能力列表,例如 Docker容器中的那些。

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 的作业定义中设置 runAsUserrunAsGroupimage:user。 建议通过 FF_USE_ADVANCED_POD_SPEC_CONFIGURATION 设置 runAsUserName

配置优先级#

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

对于构建容器和 service 容器:

  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_userrun_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:

  • 根用户/群组(UID/GID 0):作业配置始终需要明确的允许列表权限
  • allowed_users 为空:允许任何非根作业用户
  • 指定了 allowed_users:只允许列出的作业用户
  • allowed_groups 为空:允许任何非根作业群组
  • 指定了 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_container_security_context,然后使用 pod_security_context,最后使用来自 .gitlab-ci.yml 的作业级用户配置。
  • Helper 容器:首先使用 helper_container_security_context,然后使用 pod_security_context。不会继承作业级用户配置。
  • Service 容器:首先使用 service_container_security_context,然后使用 pod_security_context,最后使用作业级用户配置。

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

与 Docker 执行器的对比#

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

排查用户和群组配置问题#

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

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

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

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

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

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

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

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

覆盖容器资源#

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

要覆盖容器资源,请在 .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"

定义 service 列表#

config.toml 中定义 service 列表。

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

如果 service 环境包含 HEALTHCHECK_TCP_PORT,GitLab Runner 会等待该 service 在该端口上响应,然后才开始运行用户 CI 脚本。您也可以在 .gitlab-ci.ymlservices 部分中配置 HEALTHCHECK_TCP_PORT 环境变量。

覆盖 service 容器资源#

如果作业有多个 service 容器,您可以为每个 service 容器设置明确的 资源请求和限制。 使用每个 service 中的 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 默认 service account#

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

您可以使用此变量来指定附加到命名空间的 service account,这在复杂的 RBAC 配置中可能是必需的。

yaml
variables: KUBERNETES_SERVICE_ACCOUNT_OVERWRITE: ci-service-account

为确保 CI 运行期间只使用指定的 service account,请为以下任一项定义正则表达式:

  • 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 内核版本#

如果配置的集群中有运行不同操作系统的节点,GitLab Runner 使用 Kubernetes 执行器时可以在不同 操作系统上运行构建。

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

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

Rendering chart...

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

  • helper_image_autoset_arch_and_os 配置
  • 来自以下来源的 kubernetes.io/oskubernetes.io/archnode.kubernetes.io/windows-build 标记选择器:
    • node_selector 配置
    • node_selector 覆盖

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

节点#

指定执行构建的节点#

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

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_keysmismatch_label_keys 需要 Kubernetes 1.31 或更高版本。 在较早的 alpha 或 beta 集群上,请启用 MatchLabelKeysInPodAffinity 功能开关。在不支持该功能开关的集群上,API 服务器 会忽略这些字段,并且该约束不会生效。

## 网络 {#networking}

配置容器生命周期钩子#

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

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

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"

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

选项类型是否必需描述
execKubernetesLifecycleExecActionExec 指定要执行的操作。
http_getKubernetesLifecycleHTTPGetHTTPGet 指定要执行的 HTTP 请求。
tcp_socketKubernetesLifecycleTcpSocketTCPsocket 指定涉及 TCP 端口的操作。

KubernetesLifecycleExecAction#

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

KubernetesLifecycleHTTPGet#

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

KubernetesLifecycleHTTPGetHeader#

选项类型是否必需描述
name字符串HTTP 标头名称。
value字符串HTTP 标头值。

KubernetesLifecycleTcpSocket#

选项类型是否必需描述
portint要在容器上访问的端口号。
host字符串要连接的主机名,默认为 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#

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

默认丢弃的能力列表#

GitLab Runner 默认丢弃以下能力。

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

  • NET_RAW

添加额外的主机别名#

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

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

使用以下选项:

选项类型是否必需描述
IP字符串您想要将主机附加到的 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 文件中使用以下选项:

选项类型是否必需描述
name字符串卷的名称。
mount_path字符串卷在容器中挂载的路径。
sub_path字符串挂载卷内的子路径,而不是其根目录。
host_path字符串主机上作为卷挂载的路径。如果您不指定值,则默认为与 mount_path 相同的路径。
read_only布尔值将卷设置为只读模式。默认为 false
mount_propagation字符串在容器之间共享挂载的卷。更多信息,请参见挂载传播

persistentVolumeClaim#

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

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

选项类型是否必需描述
name字符串卷的名称,同时也是应使用的 PersistentVolumeClaim 的名称。支持变量。更多信息,请参见每个并发构建的持久卷
mount_path字符串卷在容器中挂载的路径。
read_only布尔值将卷设置为只读模式(默认为 false)。
sub_path字符串挂载卷中的子路径,而不是根目录。
mount_propagation字符串设置卷的挂载传播模式。更多详细信息,请参见 Kubernetes 挂载传播

configMap#

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

config.toml 中使用以下选项:

选项类型是否必需描述
name字符串卷的名称,同时也是应使用的 configMap 的名称。
mount_path字符串卷在容器中挂载的路径。
read_only布尔值将卷设置为只读模式(默认为 false)。
sub_path字符串挂载卷中的子路径,而不是根目录。
itemsmap[string]string应使用的 configMap 中键到路径的映射。

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

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

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

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

secret#

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

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

选项类型是否必需描述
name字符串卷的名称,同时也是应使用的 secret 的名称。
mount_path字符串卷应挂载的容器内路径。
read_only布尔值将卷设置为只读模式(默认为 false)。
sub_path字符串挂载卷中的子路径,而不是根目录。
itemsmap[string]string应使用的 configMap 中键到路径的映射。

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

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

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

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

emptyDir#

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

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

选项类型是否必需描述
name字符串卷的名称。
mount_path字符串卷应挂载的容器内路径。
sub_path字符串挂载卷中的子路径,而不是根目录。
medium字符串“Memory”提供 tmpfs,否则默认为节点磁盘存储(默认为“”)。
size_limit字符串emptyDir 卷所需的本地存储总量。
mount_propagation字符串设置卷的挂载传播模式。更多详细信息,请参见 Kubernetes 挂载传播

csi#

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

config.toml 中使用以下选项:

选项类型是否必需描述
name字符串卷的名称。
mount_path字符串卷应挂载的容器内路径。
driver字符串指定要使用的卷驱动名称的字符串值。
fs_type字符串指定文件系统类型名称的字符串值(例如,ext4xfsntfs)。
volume_attributesmap[string]stringcsi 卷属性的键值对映射。
sub_path字符串挂载卷中的子路径,而不是根目录。
read_only布尔值将卷设置为只读模式(默认为 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-0build-pvc-3 的持久卷声明。 创建的数量取决于 Runner 的 concurrent 设置。

使用辅助镜像#

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

如果您只需要 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 地址,它们使用该地址通过 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 执行器与 Docker-in-Docker 一起使用

防止主机内核暴露#

如果您使用 docker:dind/var/run/docker.sock,Docker 守护进程 可以访问主机机器的底层内核。这意味着 Pod 中设置的任何 limits 在构建 Docker 镜像时都不起作用。 无论 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

例如,仅允许 alwaysif-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_policyallowed_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。这应该可以避免诸如在网络不稳定的环境中作业中途被标记为成功之类的问题。

关注 issue #27976 了解旧版执行策略移除的进展。

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

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

要控制每种错误的重试次数,请使用 retry_limits 选项。 rety_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_ENTRYPOINTFF_USE_LEGACY_KUBERNETES_EXECUTION_STRATEGY 时,系统会向构建容器添加一个 启动探针, 以便了解入口点何时生成 shell。如果自定义入口点使用提供的 args 来生成预期的 shell,则启动探针会自动解析。 但是,如果容器镜像不使用通过 args 传入的命令来生成 shell,则入口点必须自行解析 启动探针,方法是在构建目录的根目录中创建一个名为 .gitlab-startup-marker 的文件。 启动探针每隔 poll_interval 检查一次 .gitlab-startup-marker 文件。如果该文件在 poll_timeout 内不存在,则 Pod 被视为不健康,系统会中止构建。

限制对作业变量的访问#

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

  • Pod 的环境部分

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

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

  • 对于 podsconfigmaps
    • get
    • watch
    • list
  • 对于 pods/execpods/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_secretsservice_account
  • resource_availability_check_max_attempts 设置为大于零的数字。
  • Kubernetes serviceAccountgetlist 权限一起使用。

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