GitLabとGitLab Runnerを同一EKSクラスター上で稼働させつつワーカーノードを分離してみた
Amazon EKSでGitLabをセルフホストしています。
GitLab Runnerを利用するにあたり、同一EKSクラスター上で稼働させつつGitLabとは別のマシン上で動作させたいことがありました。
今回はこの方法についてブログにします。
前提
本記事では、以下を前提とします。
- GitLab・GitLab Runner をEKSにインストール済み
 
インストール方法については、以下の記事をご参照ください。
なぜGitLabとGitLab Runnerを別ノードで動作させたいのか?
公式ドキュメントには以下の記載があり、別のマシン上で動作させることが推奨されています。
For security and performance reasons, install GitLab Runner on a machine separate from the machine that hosts your GitLab instance.
セキュリティとパフォーマンス上の理由から、GitLab インスタンスをホストするマシンとは別のマシンに GitLab Runner をインストールします。
Install GitLab Runner | GitLab Docsから引用
GitLab Runnerでは、CI/CDのジョブ毎にRunnerノード上にPodが起動します。
同一マシン上で稼働させている場合、GitLab Runnerのジョブ数増加によるリソース逼迫でGitLab自体のパフォーマンスが落ちる可能性があります。
どのレベルでGitLabとGitLab Runnerを分離するか
今回GitLabはEKS上に構築していることを前提としています。
GitLab RunnerもEKS上で構築する場合、分離の選択肢は以下があると思います。
- EKSクラスター単位
 - EKSノードグループ単位
 
ざっくり以下のようなメリット・デメリットがあると思います。
| - | EKSクラスター | EKSノードグループ | 
|---|---|---|
| メリット | 分離の複雑性: Kubenetes側のノード分離設定が不要 | 
コスト: 単一EKSクラスター料金のみ(72USD/月) 管理: EKSバージョンアップが1回で済む  | 
| デメリット | コスト: EKSクラスター料金が2倍((72USD/月 * 2)) 管理: EKSバージョンアップが2回必要  | 
分離の複雑性: Kubenetes側のノード分離設定が必要(Node Afinity/Taints等) | 
今回はコストとEKSクラスター管理を重視して、ノードグループ単位の分離としました。
このブログでは、ノードグループ単位の分離を前提として、ノード分離設定について紹介します。
やってみた
GitLab Runner用のマネージドノードグループを作成
任意の方法でGitLab Runner用のマネージドノードグループを作成します。
今回はawscliを利用する方法を紹介します。
以下のファイルを用意します。
クラスター名やサブネットID、ノードIAMロールARNは各自の環境に合わせて置き換えてください。
動作させるだけであれば、Node Role ARNは既存マネージドノードグループにアタッチされているノードIAMロールでも問題ありません。
{
    "clusterName": "<クラスター名>",
    "nodegroupName": "gitlab-runner",
    "subnets": [
      "<サブネットID A>",
      "<サブネットID B>",
      "<サブネットID C>"
    ],
    "nodeRole": "<ノードIAMロールARN>",
    "scalingConfig": {
      "minSize": 1,
      "maxSize": 1,
      "desiredSize": 1
    },
    "instanceTypes": [
      "t3.medium"
    ],
    "diskSize": 30,
    "labels": {
      "workload": "gitlab-runner"
    },
    "taints": [
      {
        "key": "workload",
        "value": "gitlab-runner",
        "effect": "NO_SCHEDULE"
      }
    ]
}
GitLab Runner専用のノードとするために、taintsとlabelsを設定しておきます。
GitLab Runnerについては、必要スペックがCI/CDジョブによって異なります。
明示的な推奨スペックは無いため、各自の環境に合わせてインスタンスタイプを選定してください。
以下のコマンドを実行してマネージドノードグループを作成します。
aws eks create-nodegroup \
  --region <リージョン> # us-east-2 \
  --cli-input-json file://gitlab-runner-nodegroup.json
GitLab Runner用マネージドノードとGitLab用マネージドノード間では通信が必要です。
マネージドノードグループに以下のTerraformコードで作成したSecurityGroupをアタッチします。
resource "aws_security_group" "gitlab_internal_networking" {
  name_prefix = "${var.prefix}-internal-networking"
  vpc_id      = module.vpc.vpc_id
  description = "${var.prefix} - Internal Networking Security Group"
  tags = {
    Name = "${var.prefix}-internal-networking"
  }
  lifecycle {
    create_before_destroy = true
  }
}
resource "aws_vpc_security_group_ingress_rule" "gitlab_internal_networking" {
  security_group_id = aws_security_group.gitlab_internal_networking.id
  description = "Open internal networking for VMs and Node Groups"
  ip_protocol = "-1"
  referenced_security_group_id = aws_security_group.gitlab_internal_networking.id
  tags = {
    Name = "${var.prefix}-internal-networking"
  }
}
resource "aws_vpc_security_group_egress_rule" "gitlab_internal_networking_internet" {
  security_group_id = aws_security_group.gitlab_internal_networking.id
  description = "Open internet access for VMs"
  ip_protocol = "-1"
  cidr_ipv4 = "0.0.0.0/0"
  tags = {
    Name = "${var.prefix}-internal-networking-internet"
  }
}
GitLab RunnerマネージドノードグループEC2インスタンス及び、GitLabマネージドノードグループEC2に上記のセキュリティグループをアタッチします。
aws ec2 modify-instance-attribute \
  --instance-id <インスタンスID> \
  --groups <セキュリティグループID>
今回は手順の簡略化のために、マネージドノードグループ内のEC2インスタンスに直接セキュリティグループをアタッチしています。
この方法はノードのスケールに対応できないため、実環境ではEC2起動テンプレート側でセキュリティグループを設定することを推奨します。(スケールアップ等で新規に作成されたノードに上記のセキュリティグループがアタッチされない)
 values.yamlの更新
values.yamlを更新します。GitLab URLやトークンは置き換えてください。
gitlabUrl: <GitLab URL> # example: https://gitlab.exmaple.com/
runnerToken: "<トークン>"
rbac:
  create: true
serviceAccount:
  create: true
  name: "gitlab-runner"
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: workload
          operator: In
          values:
          - gitlab-runner
tolerations:
  - key: "workload"
    operator: "Equal"
    value: "gitlab-runner"
    effect: "NoSchedule"
runners:
  # tpl: https://helm.sh/docs/howto/charts_tips_and_tricks/#using-the-tpl-function
  # runner configuration: https://docs.gitlab.com/runner/configuration/advanced-configuration.html
  # kubernetes executor: https://docs.gitlab.com/runner/executors/kubernetes/
  config: |
    [[runners]]
      [runners.kubernetes]
        [runners.kubernetes.affinity]
          [runners.kubernetes.affinity.node_affinity]
            [runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution]
              [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms]]
                [[runners.kubernetes.affinity.node_affinity.required_during_scheduling_ignored_during_execution.node_selector_terms.match_expressions]]
                  key = "workload"
                  operator = "In"
                  values = ["gitlab-runner"]
        [runners.kubernetes.node_tolerations]
          "workload=gitlab-runner" = "NoSchedule"
追加したのはNode AffinityとTolerations関係の設定です。
これはGitLab Runner用マネージドノードグループを、GitLab Runnerの専用ノードにするための設定です。
- Node Affinity: GitLab Runner用のPodを、workload=gitlab-runnerラベルを持つノードにのみスケジュールする設定
 - Tolerations: GitLab Runner用のPodが、workload=gitlab-runner:NoScheduleのTaintを持つノードにスケジュールできるようにする設定
 
runners配下でも同様の設定をしています。
これはジョブPodの設定です。GitLab Runnerでは、CI/CDジョブ毎にPodが起動します。(ジョブPod)
ジョブPodはGitLab Runner管理用Podの設定を引き継ぐことはなく、TolerationsやNode Affinityを別途設定する必要があります。
上記のように設定することで、ジョブPodもGitLab Runner専用ノードで起動するように設定しています。
Helmリリースの更新
準備ができたら以下のコマンドで、Helmリリースを更新します。
# Dry-run
helm upgrade gitlab-runner gitlab/gitlab-runner \
  -f values.yaml \
  --dry-run \
  -n gitlab-runner
# Run
helm upgrade gitlab-runner gitlab/gitlab-runner \
  -f values.yaml \
  -n gitlab-runner
動作確認
ノードを管理しているマネージドノードグループを確認します。
kubectl get nodes --label-columns=eks.amazonaws.com/nodegroup
NAME                                        STATUS   ROLES    AGE     VERSION               NODEGROUP
ip-10-0-100-67.us-east-2.compute.internal   Ready    <none>   105m    v1.30.9-eks-5d632ec   default-20250321091112021200000024
ip-10-0-11-239.us-east-2.compute.internal   Ready    <none>   105m    v1.30.9-eks-5d632ec   default-20250321091112021200000024
ip-10-0-19-178.us-east-2.compute.internal   Ready    <none>   7m50s   v1.30.9-eks-5d632ec   gitlab-runner
上記から、ip-10-0-19-178.us-east-2.compute.internalがGitLab Runner用のノードであることが分かりました。
このノードでGitLab Runner用のPodが稼働することを確認します。
kubectl get pods -o wide -n gitlab-runner
NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE                                        NOMINATED NODE   READINESS GATES
gitlab-runner-5b6cc88b68-gd5nt   1/1     Running   0          73s   10.0.45.240   ip-10-0-19-178.us-east-2.compute.internal   <none>           <none>
Podgitlab-runner-5b6cc88b68-gd5ntのNODEがip-10-0-19-178.us-east-2.compute.internalになっているため、GitLab Runner用のノードで起動していることが確認できます。
他のリソースがGitLab Runner用のノードで稼働していないことも確認できました。
kubectl get pods -o wide -n gitlab
NAME                                             READY   STATUS      RESTARTS      AGE   IP             NODE                                        NOMINATED NODE   READINESS GATES
gitlab-certmanager-7c6f6b775f-gszfh              1/1     Running     0             18m   10.0.118.119   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-certmanager-cainjector-75c88bbb77-spcx9   1/1     Running     0             18m   10.0.118.118   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-certmanager-webhook-86f7d45d4f-8hss6      1/1     Running     0             18m   10.0.40.169    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-gitaly-0                                  1/1     Running     0             18m   10.0.118.121   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-gitlab-exporter-8fd649d5c-cvbk4           1/1     Running     0             18m   10.0.40.167    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-kas-65d6c4dc77-5n62r                      1/1     Running     2 (17m ago)   18m   10.0.40.170    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-kas-65d6c4dc77-s9dx2                      1/1     Running     0             17m   10.0.118.123   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-migrations-6ff953e-cqw5c                  0/1     Completed   0             18m   10.0.103.48    ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-postgresql-0                              2/2     Running     0             18m   10.0.40.171    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-prometheus-server-c7f9d89dc-nnzzw         2/2     Running     0             18m   10.0.40.172    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-redis-master-0                            2/2     Running     0             18m   10.0.118.116   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-registry-5499795975-7wx8b                 1/1     Running     0             17m   10.0.118.117   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-registry-5499795975-dwkfg                 1/1     Running     0             18m   10.0.40.166    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-sidekiq-all-in-1-v2-6fdb8b7656-xmjmw      1/1     Running     1 (15m ago)   18m   10.0.118.120   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-toolbox-d76bf6d7-678f2                    1/1     Running     0             18m   10.0.40.168    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
gitlab-webservice-default-5bf789cbf5-9hghz       2/2     Running     1 (15m ago)   17m   10.0.118.122   ip-10-0-100-67.us-east-2.compute.internal   <none>           <none>
gitlab-webservice-default-5bf789cbf5-c2pbq       2/2     Running     1 (15m ago)   18m   10.0.40.165    ip-10-0-11-239.us-east-2.compute.internal   <none>           <none>
ip-10-0-19-178.us-east-2.compute.internalで稼働しているPodは無いため、問題なさそうです。
最後に、GitLabで適当なパイプラインを実行して、ジョブ用のPod(Pod名runner-から始まるもの)がGitLab Runner用のノードで起動するか確認します。
kubectl get pods -o wide -n gitlab-runner
NAME                                              READY   STATUS        RESTARTS   AGE     IP          NODE                                        NOMINATED NODE   READINESS GATES
gitlab-runner-5fcb7987c4-chwtj                    1/1     Running       0          5m32s   10.0.42.1   ip-10-0-19-178.us-east-2.compute.internal   <none>           <none>
runner-t1htgj4s-project-1-concurrent-0-areczxil   2/2     Running       0          19s     10.0.42.3   ip-10-0-19-178.us-east-2.compute.internal   <none>           <none>
runner-t1htgj4s-project-1-concurrent-0-hstmi422   2/2     Terminating   0          36s     10.0.42.2   ip-10-0-19-178.us-east-2.compute.internal   <none>           <none>
すべてのPodがGitLab Runner用ノード(ip-10-0-19-178.us-east-2.compute.internal)で稼働していることが確認できました。
おまけ: Node Affinity・Tolerationsの確認
Helmチャート側で設定したNode Affinity・Tolerationsの確認方法です。
ジョブPod・GitLab Runner管理用Podでそれぞれ設定されていることが確認できます。
POD_NAME="<Pod名>"
kubectl get pod $POD_NAME -o yaml | grep -A 10 affinity
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: workload
            operator: In
            values:
            - gitlab-runner
kubectl get pod -n gitlab-runner $POD_NAME -o yaml | grep -A 5 tolerations
  tolerations:
  - effect: NoSchedule
    key: workload
    operator: Equal
    value: gitlab-runner
おわりに
GitLab RunnerとGitLabを同一EKSクラスターで稼働させつつ、ノードを分ける方法についてでした。
この方法を使うことでEKSクラスターを分けることなく、GitLabとGitLab Runnerを別マシンで稼働させることが可能です。
GitLab Runner管理PodとジョブPod、それぞれにNode Affinity・Tolerations設定が必要なことにご注意ください。
特にジョブPodの設定を忘れると、他のノードでジョブPodが動いてノードのリソースを逼迫することがあります。
以上、AWS事業本部の佐藤(@chari7311)でした。







