从 Gitlab 8.0 开始,Gitlab CI 就已经集成在 Gitlab 中,我们只要在项目中添加一个.gitlab-ci.yml文件,然后添加一个Runner,即可进行持续集成。在介绍 Gitlab CI 之前,我们先看看一些 Gitlab CI 的一些相关概念。

相关概念

Pipeline

一次 Pipeline 其实相当于一次构建任务,里面可以包含很多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。任何提交或者 Merge Request 的合并都可以触发 Pipeline 构建,如下图所示:

  1. +------------------+ +----------------+
  2. | | trigger | |
  3. | Commit / MR +---------->+ Pipeline |
  4. | | | |
  5. +------------------+ +----------------+

Stages

Stages 表示一个构建阶段,也就是上面提到的一个流程。我们可以在一次 Pipeline 中定义多个 Stages,这些 Stages 会有以下特点:

  • 所有 Stages 会按照顺序运行,即当一个 Stage 完成后,下一个 Stage 才会开始
  • 只有当所有 Stages 完成后,该构建任务 (Pipeline) 才会成功
  • 如果任何一个 Stage 失败,那么后面的 Stages 不会执行,该构建任务 (Pipeline) 失败

Stages 和 Pipeline 的关系如下所示:

  1. | |
  2. | Pipeline |
  3. | |
  4. | +-----------+ +------------+ +------------+ |
  5. | | Stage 1 |---->| Stage 2 |----->| Stage 3 | |
  6. | +-----------+ +------------+ +------------+ |
  7. | |
  8. +--------------------------------------------------------+

Jobs

Jobs 表示构建工作,表示某个 Stage 里面执行的工作。我们可以在 Stages 里面定义多个 Jobs,这些 Jobs 会有以下特点:

  • 相同 Stage 中的 Jobs 会并行执行
  • 相同 Stage 中的 Jobs 都执行成功时,该 Stage 才会成功
  • 如果任何一个 Job 失败,那么该 Stage 失败,即该构建任务 (Pipeline) 失败

Jobs 和 Stage 的关系如下所示:

  1. | |
  2. | Stage 1 |
  3. | |
  4. | +---------+ +---------+ +---------+ |
  5. | | Job 1 | | Job 2 | | Job 3 | |
  6. | +---------+ +---------+ +---------+ |
  7. | |
  8. +------------------------------------------+

Gitlab Runner

如果理解了上面的基本概念之后,可能我们就会发现一个问题,我们的构建任务在什么地方来执行呢,以前用 Jenkins 在 Master 和 Slave 节点都可以用来运行构建任务,而来执行我们的 Gitlab CI 构建任务的就是 Gitlab Runner。
我们知道大多数情况下构建任务都是会占用大量的系统资源的,如果直接让 Gitlab 本身来运行构建任务的话,显然 Gitlab 的性能会大幅度下降的。GitLab CI 最大的作用是管理各个项目的构建状态,因此,运行构建任务这种浪费资源的事情交给一个独立的 Gitlab Runner 来做就会好很多,更重要的是 Gitlab Runner 可以安装到不同的机器上,甚至是我们本机,这样完全就不会影响到 Gitlab 本身了。

安装

(1)、验证集群

  1. # kubectl cluster-info
  2. Kubernetes master is running at https://172.16.0.33:6443
  3. CoreDNS is running at https://172.16.0.33:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
  4. kubernetes-dashboard is running at https://172.16.0.33:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy
  5. Metrics-server is running at https://172.16.0.33:6443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy

(2)、获取Gitlab CI Register Token
image.png

(3)、编写Gitlab Runner资源清单
配置ConfigMap(gitlab-runner-config.yaml)

  1. apiVersion: v1
  2. data:
  3. REGISTER_NON_INTERACTIVE: "true"
  4. REGISTER_LOCKED: "false"
  5. METRICS_SERVER: "0.0.0.0:9100"
  6. CI_SERVER_URL: "http://gitlab.kube-ops.svc.cluster.local/ci"
  7. RUNNER_REQUEST_CONCURRENCY: "4"
  8. RUNNER_EXECUTOR: "kubernetes"
  9. KUBERNETES_NAMESPACE: "kube-ops"
  10. KUBERNETES_PRIVILEGED: "true"
  11. KUBERNETES_CPU_LIMIT: "1"
  12. KUBERNETES_CPU_REQUEST: "500m"
  13. KUBERNETES_MEMORY_LIMIT: "1Gi"
  14. KUBERNETES_SERVICE_CPU_LIMIT: "1"
  15. KUBERNETES_SERVICE_MEMORY_LIMIT: "1Gi"
  16. KUBERNETES_HELPER_CPU_LIMIT: "500m"
  17. KUBERNETES_HELPER_MEMORY_LIMIT: "100Mi"
  18. KUBERNETES_PULL_POLICY: "if-not-present"
  19. KUBERNETES_TERMINATIONGRACEPERIODSECONDS: "10"
  20. KUBERNETES_POLL_INTERVAL: "5"
  21. KUBERNETES_POLL_TIMEOUT: "360"
  22. kind: ConfigMap
  23. metadata:
  24. labels:
  25. app: gitlab-ci-runner
  26. name: gitlab-ci-runner-cm
  27. namespace: kube-ops

此外,还需要一个用于注册、运行和取消注册 Gitlab CI Runner 的小脚本。只有当 Pod 正常通过 Kubernetes(TERM信号)终止时,才会触发转轮取消注册。 如果强制终止 Pod(SIGKILL信号),Runner 将不会注销自身。必须手动完成对这种被杀死的 Runner 的清理,配置清单文件如下:(gitlab-runner-scripts-cm.yaml)

  1. apiVersion: v1
  2. data:
  3. run.sh: |
  4. #!/bin/bash
  5. unregister() {
  6. kill %1
  7. echo "Unregistering runner ${RUNNER_NAME} ..."
  8. /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}
  9. exit $?
  10. }
  11. trap 'unregister' EXIT HUP INT QUIT PIPE TERM
  12. echo "Registering runner ${RUNNER_NAME} ..."
  13. /usr/bin/gitlab-ci-multi-runner register -r ${GITLAB_CI_TOKEN}
  14. sed -i 's/^concurrent.*/concurrent = '"${RUNNER_REQUEST_CONCURRENCY}"'/' /home/gitlab-runner/.gitlab-runner/config.toml
  15. echo "Starting runner ${RUNNER_NAME} ..."
  16. /usr/bin/gitlab-ci-multi-runner run -n ${RUNNER_NAME} &
  17. wait
  18. kind: ConfigMap
  19. metadata:
  20. labels:
  21. app: gitlab-ci-runner
  22. name: gitlab-ci-runner-scripts
  23. namespace: kube-ops

我们可以看到需要一个 GITLAB_CI_TOKEN,然后我们用 Gitlab CI runner token 来创建一个 Kubernetes secret 对象。将 token 进行 base64 编码:

  1. # echo 7vSqxVsS_U4yvDQpMr98 | base64 -w0
  2. N3ZTcXhWc1NfVTR5dkRRcE1yOTgK

然后配置secret清单(gitlab-ci-token-secret.yaml):

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: gitlab-ci-token
  5. namespace: kube-ops
  6. labels:
  7. app: gitlab-ci-runner
  8. data:
  9. GITLAB_CI_TOKEN: N3ZTcXhWc1NfVTR5dkRRcE1yOTgK

然后接下来我们就可以来编写一个用于真正运行 Runner 的控制器对象,我们这里使用 Statefulset。首先,在开始运行的时候,尝试取消注册所有的同名 Runner,当节点丢失时(即NodeLost事件),这尤其有用。然后再尝试重新注册自己并开始运行。在正常停止 Pod 的时候,Runner 将会运行unregister命令来尝试取消自己,所以 Gitlab 就不能再使用这个 Runner 了,这个是通过 Kubernetes Pod 生命周期中的hooks来完成的。
另外我们通过使用envFrom来指定Secrets和ConfigMaps来用作环境变量,对应的资源清单文件如下:(gitlab-runner-statefulset.yaml)

  1. apiVersion: apps/v1beta1
  2. kind: StatefulSet
  3. metadata:
  4. name: gitlab-ci-runner
  5. namespace: kube-ops
  6. labels:
  7. app: gitlab-ci-runner
  8. spec:
  9. updateStrategy:
  10. type: RollingUpdate
  11. replicas: 2
  12. serviceName: gitlab-ci-runner
  13. template:
  14. metadata:
  15. labels:
  16. app: gitlab-ci-runner
  17. spec:
  18. volumes:
  19. - name: gitlab-ci-runner-scripts
  20. projected:
  21. sources:
  22. - configMap:
  23. name: gitlab-ci-runner-scripts
  24. items:
  25. - key: run.sh
  26. path: run.sh
  27. mode: 0755
  28. serviceAccountName: gitlab-ci
  29. securityContext:
  30. runAsNonRoot: true
  31. runAsUser: 999
  32. supplementalGroups: [999]
  33. containers:
  34. - image: gitlab/gitlab-runner:latest
  35. name: gitlab-ci-runner
  36. command:
  37. - /scripts/run.sh
  38. envFrom:
  39. - configMapRef:
  40. name: gitlab-ci-runner-cm
  41. - secretRef:
  42. name: gitlab-ci-token
  43. env:
  44. - name: RUNNER_NAME
  45. valueFrom:
  46. fieldRef:
  47. fieldPath: metadata.name
  48. ports:
  49. - containerPort: 9100
  50. name: http-metrics
  51. protocol: TCP
  52. volumeMounts:
  53. - name: gitlab-ci-runner-scripts
  54. mountPath: "/scripts"
  55. readOnly: true
  56. restartPolicy: Always

可以看到上面我们使用了一个名为 gitlab-ci 的 serviceAccount,新建一个 rbac 资源清单文件:(gitlab-runner-rbac.yaml)

  1. apiVersion: v1
  2. kind: ServiceAccount
  3. metadata:
  4. name: gitlab-ci
  5. namespace: kube-ops
  6. ---
  7. kind: Role
  8. apiVersion: rbac.authorization.k8s.io/v1
  9. metadata:
  10. name: gitlab-ci
  11. namespace: kube-ops
  12. rules:
  13. - apiGroups: [""]
  14. resources: ["*"]
  15. verbs: ["*"]
  16. ---
  17. kind: RoleBinding
  18. apiVersion: rbac.authorization.k8s.io/v1
  19. metadata:
  20. name: gitlab-ci
  21. namespace: kube-ops
  22. subjects:
  23. - kind: ServiceAccount
  24. name: gitlab-ci
  25. namespace: kube-ops
  26. roleRef:
  27. kind: Role
  28. name: gitlab-ci
  29. apiGroup: rbac.authorization.k8s.io

(4)、创建资源清单

  1. # kubectl apply -f .
  2. secret/gitlab-ci-token created
  3. configmap/gitlab-ci-runner-cm created
  4. serviceaccount/gitlab-ci created
  5. role.rbac.authorization.k8s.io/gitlab-ci created
  6. rolebinding.rbac.authorization.k8s.io/gitlab-ci created
  7. configmap/gitlab-ci-runner-scripts created
  8. statefulset.apps/gitlab-ci-runner created

查看结果:

  1. # kubectl get pod -n kube-ops
  2. NAME READY STATUS RESTARTS AGE
  3. dingtalk-hook-856c5dbbc9-srcm6 1/1 Running 0 6d20h
  4. gitlab-648d4cffc4-bhwxs 1/1 Running 0 42m
  5. gitlab-ci-runner-0 1/1 Running 0 18s
  6. gitlab-ci-runner-1 1/1 Running 0 84s

image.png

Gitlab CI

基本配置

接下来使用 Gitlab CI 所用到的代码库可以从 Github 上获得:cnych/presentation-gitlab-k8s,可以在 Gitlab 上新建一个项目导入该仓库,当然也可以新建一个空白的仓库,然后将 Github 上面的项目 Clone 到本地后,更改远程仓库地址即可:

  1. # git clone https://github.com/cnych/presentation-gitlab-k8s.git
  2. # cd presentation-gitlab-k8s/
  3. # 修改仓库地址
  4. # git remote set-url origin ssh://git@119.3.197.161:30022/root/presentation-gitlab-k8s.git
  5. # 推到我们的仓库种
  6. # git push -u origin master

当推完过后就自动执行了。
image.png