GitLab CI/CD 設定について、GitLab および GitLab Runner すべてを k8s 内で実行するサンプルを記載します。簡単のため minikube を利用します。
minikube config set memory 8192
minikube config set cpus 4
minikube start
minikube ip
192.168.99.103
minikube ssh
minikube addons enable ingress
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 5m39s v1.16.0
設定はサンプルです。minikube ホストの /tmp/gitlab
を GitLab の永続データ格納用のディレクトリとして設定しています。runner が hostname でアクセスするため hostname
を設定する必要があることにも注意します。
kubectl apply -f gitlab.yaml
gitlab.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab
labels:
app: gitlab
spec:
replicas: 1
selector:
matchLabels:
app: gitlab
template:
metadata:
labels:
app: gitlab
spec:
volumes:
- name: hostvolume
hostPath:
path: /tmp/gitlab
type: DirectoryOrCreate
hostname: gitlab
containers:
- name: gitlab
image: gitlab/gitlab-ce:latest
volumeMounts:
- mountPath: /etc/gitlab
subPath: config
name: hostvolume
- mountPath: /var/log/gitlab
subPath: logs
name: hostvolume
- mountPath: /var/opt/gitlab
subPath: data
name: hostvolume
---
apiVersion: v1
kind: Service
metadata:
name: gitlab
labels:
app: gitlab
spec:
selector:
app: gitlab
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
- name: ssh
port: 22
targetPort: 22
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gitlab
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: gitlab.minikube
http:
paths:
- path: /
backend:
serviceName: gitlab
servicePort: 80
minikube の IP を名前解決できるようにしてアクセスしてみます。
$ tail -1 /etc/hosts
192.168.99.103 gitlab.minikube
Go バイナリ gitlab-runner を k8s Pod 内で実行します。
Job の実行方法として選択できる executor は複数ありますが、毎回クリーンな環境で実行するためには SSH executor や Shell executor は不適です。Docker executor で Pod 内に docker コンテナを起動することもできそうですが、ここでは Kubernetes executor で runner Pod 外に job 用の docker コンテナを起動します。
そのため、gitlab-runner を実行する Pod には必要な k8s 権限を付与しておきます。
kubectl apply -f runner.yaml
runner.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: runner
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: default
name: runner
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: runner
subjects:
- kind: ServiceAccount
name: runner
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: runner
labels:
app: runner
spec:
replicas: 1
selector:
matchLabels:
app: runner
template:
metadata:
labels:
app: runner
spec:
containers:
- name: runner
image: gitlab/gitlab-runner:latest
serviceAccountName: runner
GitLab から事前に取得しておいた registration token を用いて、gitlab-runner の register サブコマンドで GitLab Runner を GitLab に登録します。
kubectl exec runner-9696bb9d4-hznr7 -- gitlab-runner register \
--non-interactive \
--description my-runner \
--url http://gitlab/ \
--registration-token HuTcoef5pErhtTwuYjjU \
--executor kubernetes
登録に成功すると gitlab-runner 内に config.toml が生成されます。config.toml 内の token は、gitlab-runner を register する際の registration token とは別物であることに注意します。
$ kubectl exec runner-9696bb9d4-hznr7 -- cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "my-runner"
url = "http://gitlab/"
token = "-mzLiiPmH153ANSiVz_Z"
executor = "kubernetes"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.kubernetes]
host = ""
bearer_token_overwrite_allowed = false
image = ""
namespace = ""
namespace_overwrite_allowed = ""
privileged = false
service_account_overwrite_allowed = ""
pod_annotations_overwrite_allowed = ""
[runners.kubernetes.pod_security_context]
[runners.kubernetes.volumes]
生成された config.toml を Pod から取り出して、k8s ConfigMap として runner にマウントされるように runner.yaml を編集すれば、register する必要のない runner deployment 設定を作成できます。
runner.yaml
labels:
app: runner
spec:
+ volumes:
+ - name: config
+ configMap:
+ name: runner
containers:
- name: runner
image: gitlab/gitlab-runner:latest
+ volumeMounts:
+ - name: config
+ subPath: config.toml
+ mountPath: /etc/gitlab-runner/config.toml
serviceAccountName: runner
+
+---
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: runner
+data:
+ config.toml: |
+ concurrent = 1
+ check_interval = 0
+
+ [session_server]
+ session_timeout = 1800
+
+ [[runners]]
+ name = "my-runner"
+ url = "http://gitlab/"
+ token = "-fseJu4_VcJwGu_-wPPg"
+ executor = "kubernetes"
+ [runners.custom_build_dir]
+ [runners.cache]
+ [runners.cache.s3]
+ [runners.cache.gcs]
+ [runners.kubernetes]
+ host = ""
+ bearer_token_overwrite_allowed = false
+ image = ""
+ namespace = ""
+ namespace_overwrite_allowed = ""
+ privileged = false
+ service_account_overwrite_allowed = ""
+ pod_annotations_overwrite_allowed = ""
+ [runners.kubernetes.pod_security_context]
+ [runners.kubernetes.volumes]
.gitlab-ci.yml
には Job を実行するために必要な環境が含まれる docker image を指定します。GitLab への push をトリガーとして、commit された Python のコードを flake8 でチェックする設定は以下のようになります。
kubectl port-forward gitlab-6577c9fff5-wlw75 2222:22
git clone ssh://git@localhost:2222/mygroup/myproject.git
.gitlab-ci.yml
run-test:
image: alpine/flake8:latest
script:
- echo ok
- which flake8
- ls
git push すると、指定した docker image を用いて Pod が新規に作成され、ソースコードが git clone されます。flake8 を利用する script
を設定すれば、簡単な構文チェックが行えます。
k8s deployment を更新する際に 現在実行中の Pod には SIGTERM
が送信されます。GitLab Runner は SIGTERM を受け取ると、現在実行中の Job を強制的に終了します。k8s の preStop
を設定して SIGQUIT
を送信することで、これを回避できます。GitLab Runner の docker image に killall
などをインストールしておくと簡単です。
Dockerfile
FROM gitlab/gitlab-runner:latest
RUN apt update && apt install -y psmisc
COPY config /etc/gitlab-runner
...
preStop は以下のように設定します。preStop
が完了すると SIGTERM
が送信されてしまうため sleep しています。3時間以内に終了しなければ SIGTERM
が送信された後、すぐに SIGKILL
が送信されます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: runner
labels:
app: runner
spec:
replicas: 1
selector:
matchLabels:
app: runner
template:
metadata:
labels:
app: runner
spec:
containers:
- name: runner
image: my/gitlab-runner:latest
imagePullPolicy: "Always"
lifecycle:
preStop:
exec:
command: ["bash", "-c", "killall -SIGQUIT gitlab-runner && sleep 10800"]
terminationGracePeriodSeconds: 10800
serviceAccountName: runner