簡介
從 Gitlab 8.0 開始,Gitlab CI 就已經(jīng)集成在 Gitlab 中,只要在項(xiàng)目中添加一個.gitlab-ci.yml文件,然后添加一個Runner,即可進(jìn)行持續(xù)集成。在介紹 Gitlab CI 之前,先看看一些 Gitlab CI 的一些相關(guān)概念。
流程邏輯如下:
Jobs->Stages->Pipeline
Pipeline
一次 Pipeline 其實(shí)相當(dāng)于一次構(gòu)建任務(wù),里面可以包含很多個流程,如安裝依賴、運(yùn)行測試、編譯、部署測試服務(wù)器、部署生產(chǎn)服務(wù)器等流程。任何提交或者 Merge Request 的合并都可以觸發(fā) Pipeline 構(gòu)建,如下圖所示:
+------------------+ +----------------+
| | trigger | |
| Commit / MR +---------->+ Pipeline |
| | | |
+------------------+ +----------------+
Stages
Stages 表示一個構(gòu)建階段,也就是上面提到的一個流程??梢栽谝淮?Pipeline 中定義多個 Stages,這些 Stages 會有以下特點(diǎn):
- 所有 Stages 會按照順序運(yùn)行,即當(dāng)一個 Stage 完成后,下一個 Stage 才會開始
- 只有當(dāng)所有 Stages 完成后,該構(gòu)建任務(wù) (Pipeline) 才會成功
- 如果任何一個 Stage 失敗,那么后面的 Stages 不會執(zhí)行,該構(gòu)建任務(wù) (Pipeline) 失敗
Stages 和 Pipeline 的關(guān)系如下所示:
| |
| Pipeline |
| |
| +-----------+ +------------+ +------------+ |
| | Stage 1 |---->| Stage 2 |----->| Stage 3 | |
| +-----------+ +------------+ +------------+ |
| |
+--------------------------------------------------------+
Jobs
Jobs 表示構(gòu)建工作,表示某個 Stage 里面執(zhí)行的工作??梢栽?Stages 里面定義多個 Jobs,這些 Jobs 會有以下特點(diǎn):
- 相同 Stage 中的 Jobs 會并行執(zhí)行
- 相同 Stage 中的 Jobs 都執(zhí)行成功時,該 Stage 才會成功
- 如果任何一個 Job 失敗,那么該 Stage 失敗,即該構(gòu)建任務(wù) (Pipeline) 失敗
Jobs 和 Stage 的關(guān)系如下所示:
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| | Job 1 | | Job 2 | | Job 3 | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+
Gitlab Runner
如果理解了上面的基本概念之后,可能我們就會發(fā)現(xiàn)一個問題,我們的構(gòu)建任務(wù)在什么地方來執(zhí)行呢,以前用 Jenkins 在 Master 和 Slave 節(jié)點(diǎn)都可以用來運(yùn)行構(gòu)建任務(wù),而來執(zhí)行我們的 Gitlab CI 構(gòu)建任務(wù)的就是 Gitlab Runner。
我們知道大多數(shù)情況下構(gòu)建任務(wù)都是會占用大量的系統(tǒng)資源的,如果直接讓 Gitlab 本身來運(yùn)行構(gòu)建任務(wù)的話,顯然 Gitlab 的性能會大幅度下降的。GitLab CI 最大的作用是管理各個項(xiàng)目的構(gòu)建狀態(tài),因此,運(yùn)行構(gòu)建任務(wù)這種浪費(fèi)資源的事情交給一個獨(dú)立的 Gitlab Runner 來做就會好很多,更重要的是 Gitlab Runner 可以安裝到不同的機(jī)器上,甚至是我們本機(jī),這樣完全就不會影響到 Gitlab 本身了。
安裝
安裝 Gitlab Runner 非常簡單,我們可以完全安裝官方文檔:https://docs.gitlab.com/runner/install/即可,比如可以直接使用二進(jìn)制、Docker 等來安裝。同樣的,我們這里還是將 Gitlab Runner 安裝到 Kubernetes 集群中來,讓我們的集群來統(tǒng)一管理 Gitlab 相關(guān)的服務(wù)。
1.驗(yàn)證 Kubernetes 集群
執(zhí)行下面的命令驗(yàn)證 Kubernetes 集群:
$ kubectl cluster-info
Kubernetes master is running at https://10.8.13.81:6443
CoreDNS is running at https://10.8.13.81:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
kubernetes-dashboard is running at https://10.8.13.81:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
Metrics-server is running at https://10.8.13.81:6443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
cluster-info這個命令會顯示當(dāng)前鏈接的集群狀態(tài)和可用的集群服務(wù)列表。
2.獲取 Gitlab CI Register Token
上節(jié)已經(jīng)成功安裝了 Gitlab,在瀏覽器中打開hwzxgit.sinoing.net頁面,然后登錄后進(jìn)入到管理頁面http://hwzxgit.sinoing.net/admin,然后點(diǎn)擊導(dǎo)航欄中的Runner,可以看到該頁面中有兩個總要的參數(shù),一個是 URL,另外一個就是 Register Token,下面的步驟中需要用到這兩個參數(shù)值。
圖一、

圖二、

注意:不要隨便泄露 Token
3.編寫 Gitlab CI Runner 資源清單文件
同樣將 Runner 相關(guān)的資源對象都安裝到kube-ops這個 namespace 下面,首先,通過 ConfigMap 資源來傳遞 Runner 鏡像所需的環(huán)境變量(runner-cm.yaml):
$ cd /gitlab/
$ mkdir runner
apiVersion: v1
data:
REGISTER_NON_INTERACTIVE: "true"
REGISTER_LOCKED: "false"
METRICS_SERVER: "0.0.0.0:9100"
CI_SERVER_URL: "http://gitlab.kube-ops.svc.cluster.local/ci"
RUNNER_REQUEST_CONCURRENCY: "4"
RUNNER_EXECUTOR: "kubernetes"
KUBERNETES_NAMESPACE: "kube-ops"
KUBERNETES_PRIVILEGED: "true"
KUBERNETES_CPU_LIMIT: "1"
KUBERNETES_CPU_REQUEST: "500m"
KUBERNETES_MEMORY_LIMIT: "1Gi"
KUBERNETES_SERVICE_CPU_LIMIT: "1"
KUBERNETES_SERVICE_MEMORY_LIMIT: "1Gi"
KUBERNETES_HELPER_CPU_LIMIT: "500m"
KUBERNETES_HELPER_MEMORY_LIMIT: "100Mi"
KUBERNETES_PULL_POLICY: "if-not-present"
KUBERNETES_TERMINATIONGRACEPERIODSECONDS: "10"
KUBERNETES_POLL_INTERVAL: "5"
KUBERNETES_POLL_TIMEOUT: "360"
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-cm
namespace: kube-ops
要注意CI_SERVER_URL對應(yīng)的值需要指向 Gitlab 實(shí)例的 URL(可以是外網(wǎng)地址,也可以是 Kubernetes 集群內(nèi)部的 Service DNS 地址,因?yàn)?Runner 也是運(yùn)行在 Kubernetes 集群中的),并加上/ci( http://gitlab.kube-ops.svc.cluster.local/ci ),也可以是CI_SERVER_URL: "http://hwzxgit.sinoing.net/ci"此外還添加了一些構(gòu)建容器運(yùn)行的資源限制,可以自己根據(jù)需要進(jìn)行更改即可。
除了上面的一些環(huán)境變量相關(guān)的配置外,還需要一個用于注冊、運(yùn)行和取消注冊 Gitlab CI Runner 的小腳本。只有當(dāng) Pod 正常通過 Kubernetes(TERM信號)終止時,才會觸發(fā)轉(zhuǎn)輪取消注冊。 如果強(qiáng)制終止 Pod(SIGKILL信號),Runner 將不會注銷自身。必須手動完成對這種被殺死的 Runner 的清理,配置清單文件如下:(runner-scripts-cm.yaml)
apiVersion: v1
data:
run.sh: |
#!/bin/bash
unregister() {
kill %1
echo "Unregistering runner ${RUNNER_NAME} ..."
/usr/bin/gitlab-ci-multi-runner unregister -t "$(/usr/bin/gitlab-ci-multi-runner list 2>&1 | tail -n1 | awk '{print $4}' | cut -d'=' -f2)" -n ${RUNNER_NAME}
exit $?
}
trap 'unregister' EXIT HUP INT QUIT PIPE TERM
echo "Registering runner ${RUNNER_NAME} ..."
/usr/bin/gitlab-ci-multi-runner register -r ${GITLAB_CI_TOKEN}
sed -i 's/^concurrent.*/concurrent = '"${RUNNER_REQUEST_CONCURRENCY}"'/' /home/gitlab-runner/.gitlab-runner/config.toml
echo "Starting runner ${RUNNER_NAME} ..."
/usr/bin/gitlab-ci-multi-runner run -n ${RUNNER_NAME} &
wait
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-scripts
namespace: kube-ops
可以看到需要一個 GITLAB_CI_TOKEN,然后復(fù)制下圖中的Gitlab CI runner token 來創(chuàng)建一個 Kubernetes secret 對象。將 token 進(jìn)行 base64 編碼:

$ echo VhSA7XiDmU65LoHxN-Gs | base64 -w0
VmhTQTdYaURtVTY1TG9IeE4tR3MK
base64 命令在大部分 Linux 發(fā)行版中都是可用的。
然后使用上面的 token 創(chuàng)建一個 Secret 對象:(gitlab-ci-token-secret.yaml)
apiVersion: v1
kind: Secret
metadata:
name: gitlab-ci-token
namespace: kube-ops
labels:
app: gitlab-ci-runner
data:
GITLAB_CI_TOKEN: VmhTQTdYaURtVTY1TG9IeE4tR3MK
然后接下來就可以來編寫一個用于真正運(yùn)行 Runner 的控制器對象,這里使用 Statefulset。首先,在開始運(yùn)行的時候,嘗試取消注冊所有的同名 Runner,當(dāng)節(jié)點(diǎn)丟失時(即NodeLost事件),這尤其有用。然后再嘗試重新注冊自己并開始運(yùn)行。在正常停止 Pod 的時候,Runner 將會運(yùn)行unregister命令來嘗試取消自己,所以 Gitlab 就不能再使用這個 Runner 了,這個是通過 Kubernetes Pod 生命周期中的hooks來完成的。
另外通過使用envFrom來指定Secrets和ConfigMaps來用作環(huán)境變量,對應(yīng)的資源清單文件如下:(runner-statefulset.yaml)
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: gitlab-ci-runner
namespace: kube-ops
labels:
app: gitlab-ci-runner
spec:
updateStrategy:
type: RollingUpdate
replicas: 2
serviceName: gitlab-ci-runner
template:
metadata:
labels:
app: gitlab-ci-runner
spec:
volumes:
- name: gitlab-ci-runner-scripts
projected:
sources:
- configMap:
name: gitlab-ci-runner-scripts
items:
- key: run.sh
path: run.sh
mode: 0755
serviceAccountName: gitlab-ci
securityContext:
runAsNonRoot: true
runAsUser: 999
supplementalGroups: [999]
containers:
- image: gitlab/gitlab-runner:latest
name: gitlab-ci-runner
command:
- /scripts/run.sh
envFrom:
- configMapRef:
name: gitlab-ci-runner-cm
- secretRef:
name: gitlab-ci-token
env:
- name: RUNNER_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 9100
name: http-metrics
protocol: TCP
volumeMounts:
- name: gitlab-ci-runner-scripts
mountPath: "/scripts"
readOnly: true
restartPolicy: Always
可以看到上面我們使用了一個名為 gitlab-ci 的 serviceAccount,新建一個 rbac 資源清單文件:(runner-rbac.yaml)
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-ci
namespace: kube-ops
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: kube-ops
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: kube-ops
subjects:
- kind: ServiceAccount
name: gitlab-ci
namespace: kube-ops
roleRef:
kind: Role
name: gitlab-ci
apiGroup: rbac.authorization.k8s.io
4.創(chuàng)建 Runner 資源對象
資源清單文件準(zhǔn)備好后,直接創(chuàng)建上面的資源對象:
$ ls
gitlab-ci-token-secret.yaml runner-cm.yaml runner-rbac.yaml runner-scripts-cm.yaml runner-statefulset.yaml
$ kubectl create -f .
secret "gitlab-ci-token" created
configmap "gitlab-ci-runner-cm" created
serviceaccount "gitlab-ci" created
role.rbac.authorization.k8s.io "gitlab-ci" created
rolebinding.rbac.authorization.k8s.io "gitlab-ci" created
configmap "gitlab-ci-runner-scripts" created
statefulset.apps "gitlab-ci-runner" created
創(chuàng)建完成后,可以通過查看 Pod 狀態(tài)判斷 Runner 是否運(yùn)行成功:
$ kubectl get pods -n kube-ops
NAME READY STATUS RESTARTS AGE
gitlab-0 1/1 Running 0 4d
gitlab-ci-runner-0 1/1 Running 0 17m
gitlab-ci-runner-1 1/1 Running 0 16m
jenkins2-7cc9df89dd-bwqwd 1/1 Running 9 6d3h
postgresql-0 1/1 Running 0 4d1h
redis-0 1/1 Running 0 4d1h
可以看到已經(jīng)成功運(yùn)行了兩個(具體取決于StatefulSet清單中的副本數(shù)) Runner 實(shí)例,然后切換到 Gitlab Admin 頁面下面的 Runner 頁面:

至此,在kubernetes中安裝Gitlab CI Runner結(jié)束,當(dāng)然也可以根據(jù)需要更改 Runner 的一些配置,比如添加 tag 標(biāo)簽等。