はじめに
GitLabはGitLab Runnerを組み合わせることでCI/CDを実現できます。
このGitLab CIでは.gitlab_ci.yml
でジョブを定義しますが、GitLab Runner自体がジョブを実行するのではなくGitLab Runnerで設定されたExecutorが実行します。
Executorは例えば次のものがあります。
- Shell Executor
- Docker Executor
- Kubernetes Executor
今回は、Kubernetes上にデプロイしたPodがジョブを実行するKubernetes Executorに焦点を絞って説明します。
Kubernetes Executorの構成
Kubernetes ExecutorはGitLab CIでジョブ実行時にKubernetes上にPodとして立ち上がります。
GitLab CI Job Podには次の3種類のコンテナが存在します。
- Helperコンテナ(1個)
- Buildコンテナ(1個)
- Serviceコンテナ(0個以上)
それぞれ説明していきます。
Helperコンテナ
主にGit操作を行うコンテナです。
プロジェクトのCI/CD設定でgit clone
とgit fetch
を選択できますが、実際にこれを行うのがHelperコンテナです。
コンテナイメージはGitLab Runnerに設定します。
Gitの設定を変更したい場合は公式のイメージをベースイメージとして独自の物を作成します。
Buildコンテナ
Helperコンテナが取得したソースコードをもとに.gitlab_ci.yml
の内容のジョブを実行するのがBuildコンテナです。
.gitlab_ci.yml
で指定したイメージがBuildコンテナとなりますが、指定していない場合はGitLab Runnerに設定したイメージが使用されます。
Serviceコンテナ
ジョブ実行時にBuildコンテナがアクセスできるコンテナがServiceコンテナです。
これは0個以上デプロイされます。すなわち.gitlab_ci.yml
に定義しなければデプロイされることはありません。
Serviceコンテナには例えばMysql、Redis、dindなどが挙げられます。
ジョブ実行までの流れ
順序は次のようになります。
- GitLab RunnerがGitLabでジョブが発動したかを検知する。
- GitLab RunnerがジョブPodを作成する。
- ジョブPod内のHelperコンテナがGitLabから
git clone
もしくはgit fetch
する。 - ジョブPod内のBuildコンテナが
.gitlab_ci.yml
に記述されたジョブを実行する。
図ではGitLab Runnerが同一クラスタにデプロイされているものとしています。
ポーリングの時間間隔はGitLab Runnerで設定できます。
Kubernetes Executorの検証
実際にJob Podをデプロイして検証します。
GitLab Runnerのデプロイ
Helmfileを使用します。
releases[].values[].runners.image
に設定するのはデフォルトのBuildコンテナのイメージです。
releases[].values[].runners.privileged
とreleases[].values[].runners.env
に設定している値はdindを使用するためのものです。
GitLabをKubernetes上にデプロイしている場合、releases[].values[].gitlabUrl
にService名を指定しても疎通はできますがArtifactsアップロードに失敗するので注意しないといけません。
repositories:
- name: gitlab
url: https://charts.gitlab.io/
releases:
- name: gitlab-runner
namespace: dev
chart: gitlab/gitlab-runner
version: 0.11.0
values:
- image: gitlab/gitlab-runner:alpine-v12.5.0
gitlabUrl: http://gitlab.example.com
runnerRegistrationToken: "XXXXXXXXXX"
rbac:
create: true
resources: ["pods", "pods/exec", "secrets"]
verbs: ["get", "list", "watch", "create", "patch", "delete"]
clusterWideAccess: false
metrics:
enabled: false
runners:
image: busybox:1.31.1
privileged: true
env:
DOCKER_HOST: tcp://localhost:2375
下記コマンドでデプロイします。
$ helmfile apply
ジョブの実行
GitLab RunnerをGitLabに紐付けた後にプロジェクトに.gitlab_ci.yml
を作成します。
Podの解析のためにsleep
させてます。
image: docker:18.09.7
services:
- docker:18.09.7-dind
build:
stage: test
script:
- sleep 3600
ジョブを実行させます。
Running with gitlab-runner 12.5.0 (577f813d)
on gitlab-runner-gitlab-runner-5cdf6fd4f6-vqrzq 3xycVVoJ
Using Kubernetes namespace: dev
Using Kubernetes executor with image docker:18.09.7 ...
Waiting for pod dev/runner-3xycvvoj-project-1-concurrent-0ngfr2 to be running, status is Pending
Running on runner-3xycvvoj-project-1-concurrent-0ngfr2 via gitlab-runner-gitlab-runner-5cdf6fd4f6-vqrzq...
Fetching changes with git depth set to 50...
Initialized empty Git repository in /builds/root/test/.git/
Created fresh repository.
From http://gitlab.moguta.ml/root/test
* [new ref] refs/pipelines/8 -> refs/pipelines/8
* [new branch] master -> origin/master
Checking out f493d24c as master...
Skipping Git submodules setup
$ sleep 3600
Job Podの確認
Podを確認すると3つのコンテナが存在することが分かります。
$ kubectl get pod -n dev runner-3xycvvoj-project-1-concurrent-0ngfr2
runner-3xycvvoj-project-1-concurrent-0ngfr2 3/3 Running 0 5m25s
さらに中身を見ていきます。なお、GitLabのドメインはgitlab.example.com
に変換していて載せています。
$ kubectl describe pod -n dev runner-3xycvvoj-project-1-concurrent-0ngfr2
...
Containers:
build:
Container ID: docker://f46846ec84f6517bb63dc1f2fd466126130b3014c344690debee465f2016aadc
Image: docker:18.09.7
Image ID: docker-pullable://docker@sha256:310156c95007d6cca1417d0692786fe4da816b886a08bc7de97edf02cab4db31
Port: <none>
Host Port: <none>
Command:
sh
-c
if [ -x /usr/local/bin/bash ]; then
exec /usr/local/bin/bash
elif [ -x /usr/bin/bash ]; then
exec /usr/bin/bash
elif [ -x /bin/bash ]; then
exec /bin/bash
elif [ -x /usr/local/bin/sh ]; then
exec /usr/local/bin/sh
elif [ -x /usr/bin/sh ]; then
exec /usr/bin/sh
elif [ -x /bin/sh ]; then
exec /bin/sh
elif [ -x /busybox/sh ]; then
exec /busybox/sh
else
echo shell not found
exit 1
fi
State: Running
Started: Sat, 21 Dec 2019 01:20:45 +0900
Ready: True
Restart Count: 0
Environment:
FF_CMD_DISABLE_DELAYED_ERROR_LEVEL_EXPANSION: false
FF_USE_LEGACY_BUILDS_DIR_FOR_DOCKER: false
FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER: false
CI_RUNNER_SHORT_TOKEN: 3xycVVoJ
CI_BUILDS_DIR: /builds
CI_PROJECT_DIR: /builds/root/test
CI_CONCURRENT_ID: 0
CI_CONCURRENT_PROJECT_ID: 0
CI_SERVER: yes
CI_PIPELINE_ID: 8
CI_PIPELINE_URL: http://gitlab.example.com/root/test/pipelines/8
CI_JOB_ID: 9
CI_JOB_URL: http://gitlab.example.com/root/test/-/jobs/9
CI_BUILD_ID: 9
CI_REGISTRY_USER: gitlab-ci-token
CI: true
GITLAB_CI: true
GITLAB_FEATURES:
CI_SERVER_HOST: gitlab.example.com
CI_SERVER_NAME: GitLab
CI_SERVER_VERSION: 12.5.3
CI_SERVER_VERSION_MAJOR: 12
CI_SERVER_VERSION_MINOR: 5
CI_SERVER_VERSION_PATCH: 3
CI_SERVER_REVISION:
CI_JOB_NAME: build
CI_JOB_STAGE: test
CI_COMMIT_SHA: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_COMMIT_SHORT_SHA: f493d24c
CI_COMMIT_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_COMMIT_REF_NAME: master
CI_COMMIT_REF_SLUG: master
CI_NODE_TOTAL: 1
CI_DEFAULT_BRANCH: master
CI_BUILD_REF: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_BUILD_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_BUILD_REF_NAME: master
CI_BUILD_REF_SLUG: master
CI_BUILD_NAME: build
CI_BUILD_STAGE: test
CI_PROJECT_ID: 1
CI_PROJECT_NAME: test
CI_PROJECT_TITLE: test
CI_PROJECT_PATH: root/test
CI_PROJECT_PATH_SLUG: root-test
CI_PROJECT_NAMESPACE: root
CI_PROJECT_URL: http://gitlab.example.com/root/test
CI_PROJECT_VISIBILITY: private
CI_PROJECT_REPOSITORY_LANGUAGES:
CI_PAGES_DOMAIN: example.com
CI_PAGES_URL: http://root.example.com/test
CI_REGISTRY: registry.example.com
CI_REGISTRY_IMAGE: registry.example.com/root/test
CI_API_V4_URL: http://gitlab.example.com/api/v4
CI_PIPELINE_IID: 8
CI_CONFIG_PATH: .gitlab-ci.yml
CI_PIPELINE_SOURCE: push
CI_COMMIT_MESSAGE: Update .gitlab-ci.yml
CI_COMMIT_TITLE: Update .gitlab-ci.yml
CI_COMMIT_DESCRIPTION:
CI_COMMIT_REF_PROTECTED: true
CI_RUNNER_ID: 1
CI_RUNNER_DESCRIPTION: gitlab-runner-gitlab-runner-5cdf6fd4f6-vqrzq
CI_RUNNER_TAGS:
GITLAB_USER_ID: 1
GITLAB_USER_EMAIL: admin@example.com
GITLAB_USER_LOGIN: root
GITLAB_USER_NAME: Administrator
CI_DISPOSABLE_ENVIRONMENT: true
CI_RUNNER_VERSION: 12.5.0
CI_RUNNER_REVISION: 577f813d
CI_RUNNER_EXECUTABLE_ARCH: linux/amd64
Mounts:
/builds from repo (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-8gjvw (ro)
helper:
Container ID: docker://de720b5d7dc94b57b47f8581b36ccf9ad263449ffdd69707e3a9c08c8c845dbd
Image: gitlab/gitlab-runner-helper:x86_64-577f813d
Image ID: docker-pullable://gitlab/gitlab-runner-helper@sha256:bd932463587dbd419c6f37667f7c3d63d5595ca2c5e446d81267838698828036
Port: <none>
Host Port: <none>
Command:
sh
-c
if [ -x /usr/local/bin/bash ]; then
exec /usr/local/bin/bash
elif [ -x /usr/bin/bash ]; then
exec /usr/bin/bash
elif [ -x /bin/bash ]; then
exec /bin/bash
elif [ -x /usr/local/bin/sh ]; then
exec /usr/local/bin/sh
elif [ -x /usr/bin/sh ]; then
exec /usr/bin/sh
elif [ -x /bin/sh ]; then
exec /bin/sh
elif [ -x /busybox/sh ]; then
exec /busybox/sh
else
echo shell not found
exit 1
fi
State: Running
Started: Sat, 21 Dec 2019 01:20:45 +0900
Ready: True
Restart Count: 0
Environment:
FF_CMD_DISABLE_DELAYED_ERROR_LEVEL_EXPANSION: false
FF_USE_LEGACY_BUILDS_DIR_FOR_DOCKER: false
FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER: false
CI_RUNNER_SHORT_TOKEN: 3xycVVoJ
CI_BUILDS_DIR: /builds
CI_PROJECT_DIR: /builds/root/test
CI_CONCURRENT_ID: 0
CI_CONCURRENT_PROJECT_ID: 0
CI_SERVER: yes
CI_PIPELINE_ID: 8
CI_PIPELINE_URL: http://gitlab.example.com/root/test/pipelines/8
CI_JOB_ID: 9
CI_JOB_URL: http://gitlab.example.com/root/test/-/jobs/9
CI_BUILD_ID: 9
CI_REGISTRY_USER: gitlab-ci-token
CI: true
GITLAB_CI: true
GITLAB_FEATURES:
CI_SERVER_HOST: gitlab.example.com
CI_SERVER_NAME: GitLab
CI_SERVER_VERSION: 12.5.3
CI_SERVER_VERSION_MAJOR: 12
CI_SERVER_VERSION_MINOR: 5
CI_SERVER_VERSION_PATCH: 3
CI_SERVER_REVISION:
CI_JOB_NAME: build
CI_JOB_STAGE: test
CI_COMMIT_SHA: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_COMMIT_SHORT_SHA: f493d24c
CI_COMMIT_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_COMMIT_REF_NAME: master
CI_COMMIT_REF_SLUG: master
CI_NODE_TOTAL: 1
CI_DEFAULT_BRANCH: master
CI_BUILD_REF: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_BUILD_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_BUILD_REF_NAME: master
CI_BUILD_REF_SLUG: master
CI_BUILD_NAME: build
CI_BUILD_STAGE: test
CI_PROJECT_ID: 1
CI_PROJECT_NAME: test
CI_PROJECT_TITLE: test
CI_PROJECT_PATH: root/test
CI_PROJECT_PATH_SLUG: root-test
CI_PROJECT_NAMESPACE: root
CI_PROJECT_URL: http://gitlab.example.com/root/test
CI_PROJECT_VISIBILITY: private
CI_PROJECT_REPOSITORY_LANGUAGES:
CI_PAGES_DOMAIN: example.com
CI_PAGES_URL: http://root.example.com/test
CI_REGISTRY: registry.example.com
CI_REGISTRY_IMAGE: registry.example.com/root/test
CI_API_V4_URL: http://gitlab.example.com/api/v4
CI_PIPELINE_IID: 8
CI_CONFIG_PATH: .gitlab-ci.yml
CI_PIPELINE_SOURCE: push
CI_COMMIT_MESSAGE: Update .gitlab-ci.yml
CI_COMMIT_TITLE: Update .gitlab-ci.yml
CI_COMMIT_DESCRIPTION:
CI_COMMIT_REF_PROTECTED: true
CI_RUNNER_ID: 1
CI_RUNNER_DESCRIPTION: gitlab-runner-gitlab-runner-5cdf6fd4f6-vqrzq
CI_RUNNER_TAGS:
GITLAB_USER_ID: 1
GITLAB_USER_EMAIL: admin@example.com
GITLAB_USER_LOGIN: root
GITLAB_USER_NAME: Administrator
CI_DISPOSABLE_ENVIRONMENT: true
CI_RUNNER_VERSION: 12.5.0
CI_RUNNER_REVISION: 577f813d
CI_RUNNER_EXECUTABLE_ARCH: linux/amd64
Mounts:
/builds from repo (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-8gjvw (ro)
svc-0:
Container ID: docker://cad84f7dad65eb0447190ea53df5dcbe608cb6937c1bc1bf74f9edc21a42f6a6
Image: docker:18.09.7-dind
Image ID: docker-pullable://docker@sha256:a490c83561c1cef49b6fe12aba2c31f908391ec3efe4eb173225809c981e50c3
Port: <none>
Host Port: <none>
State: Running
Started: Sat, 21 Dec 2019 01:20:45 +0900
Ready: True
Restart Count: 0
Environment:
FF_CMD_DISABLE_DELAYED_ERROR_LEVEL_EXPANSION: false
FF_USE_LEGACY_BUILDS_DIR_FOR_DOCKER: false
FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER: false
CI_RUNNER_SHORT_TOKEN: 3xycVVoJ
CI_BUILDS_DIR: /builds
CI_PROJECT_DIR: /builds/root/test
CI_CONCURRENT_ID: 0
CI_CONCURRENT_PROJECT_ID: 0
CI_SERVER: yes
CI_PIPELINE_ID: 8
CI_PIPELINE_URL: http://gitlab.example.com/root/test/pipelines/8
CI_JOB_ID: 9
CI_JOB_URL: http://gitlab.example.com/root/test/-/jobs/9
CI_BUILD_ID: 9
CI_REGISTRY_USER: gitlab-ci-token
CI: true
GITLAB_CI: true
GITLAB_FEATURES:
CI_SERVER_HOST: gitlab.example.com
CI_SERVER_NAME: GitLab
CI_SERVER_VERSION: 12.5.3
CI_SERVER_VERSION_MAJOR: 12
CI_SERVER_VERSION_MINOR: 5
CI_SERVER_VERSION_PATCH: 3
CI_SERVER_REVISION:
CI_JOB_NAME: build
CI_JOB_STAGE: test
CI_COMMIT_SHA: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_COMMIT_SHORT_SHA: f493d24c
CI_COMMIT_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_COMMIT_REF_NAME: master
CI_COMMIT_REF_SLUG: master
CI_NODE_TOTAL: 1
CI_DEFAULT_BRANCH: master
CI_BUILD_REF: f493d24c574d3deee3e0284f6f413cd20b9a7e83
CI_BUILD_BEFORE_SHA: 4bddec9c21af3e1fcedde2a81f62eb03d9afaa2b
CI_BUILD_REF_NAME: master
CI_BUILD_REF_SLUG: master
CI_BUILD_NAME: build
CI_BUILD_STAGE: test
CI_PROJECT_ID: 1
CI_PROJECT_NAME: test
CI_PROJECT_TITLE: test
CI_PROJECT_PATH: root/test
CI_PROJECT_PATH_SLUG: root-test
CI_PROJECT_NAMESPACE: root
CI_PROJECT_URL: http://gitlab.example.com/root/test
CI_PROJECT_VISIBILITY: private
CI_PROJECT_REPOSITORY_LANGUAGES:
CI_PAGES_DOMAIN: example.com
CI_PAGES_URL: http://root.example.com/test
CI_REGISTRY: registry.example.com
CI_REGISTRY_IMAGE: registry.example.com/root/test
CI_API_V4_URL: http://gitlab.example.com/api/v4
CI_PIPELINE_IID: 8
CI_CONFIG_PATH: .gitlab-ci.yml
CI_PIPELINE_SOURCE: push
CI_COMMIT_MESSAGE: Update .gitlab-ci.yml
CI_COMMIT_TITLE: Update .gitlab-ci.yml
CI_COMMIT_DESCRIPTION:
CI_COMMIT_REF_PROTECTED: true
CI_RUNNER_ID: 1
CI_RUNNER_DESCRIPTION: gitlab-runner-gitlab-runner-5cdf6fd4f6-vqrzq
CI_RUNNER_TAGS:
GITLAB_USER_ID: 1
GITLAB_USER_EMAIL: admin@example.com
GITLAB_USER_LOGIN: root
GITLAB_USER_NAME: Administrator
CI_DISPOSABLE_ENVIRONMENT: true
CI_RUNNER_VERSION: 12.5.0
CI_RUNNER_REVISION: 577f813d
CI_RUNNER_EXECUTABLE_ARCH: linux/amd64
Mounts:
/builds from repo (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-8gjvw (ro)
...
Volumes:
repo:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
default-token-8gjvw:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-8gjvw
Optional: false
...
コンテナはbuild
、helper
、svc-0
の3つです。
PodはEmptyDirをVolumeとして利用していて、/builds
以下にソースコードが配置されることが分かります。
Serviceコンテナが増えるとsvc-1
、svc-2
と増えていきます。
resourceのrequestsを大きめに取るとNodeのリソースが足らずにPending状態になってしまうので注意しましょう。
おわりに
GitLab RunnerのExecutorの1つであるKubernetes Executorを説明しました。
helmを使えば簡単にデプロイできるので導入コストは非常に低いと思います。
公式ドキュメントが豊富なので今後使用する人は増えていきそうです。