文章目录
前面我们介绍了如何在 kubernetes 中快速部署 GitLab 代码托管系统、今天我们来继续看看如何实现基于 GitLab 的 CICD。GitLab CI 早在 8.0版本开始的时候就已经就已经集成到GitLab中了。我们只需要在项目中添加一个 .gitlab-ci.yaml
文件,然后添加一个 Runner 即可实现 CI。所以在实现GitLab的CI之前我们还需要先把 GitLab CI Runner 部署到 Kubernetes中。
GitLab CI 中有一些基本的概念:Pipeline、Stages、Jobs等。
Pipeline:Pipeline流水线相当于一次构建任务,Pipeline中可以包含很多个流程;例如安装依赖、运行测试、编译安装、部署测试服务器、部署生产服务器等流程。任何提交或者 Merge 的合并都可以触发 Pipeline 的构建。
Stages :而 Stages 则表示一个构建阶段,也就是我们在 Pipeline 中提到的流程;我们可以在一次 Pipeline 中定义多个 Stages 。这些 Stages 有以下特性:所有的 Stages 会按照先后顺序运行,当一个 Stage 完成以后才会继续下一个 Stage;当所有的 Stages 完成以后,该构建任务 Pipeline 才会成功;如果任何一个 Stage 失败,后面的 Stages 不会执行,则该构建任务 Pipeline 失败。
Jobs:Jobs表示构建工作,表示某个 Stage 里面执行的工作;我们可以在 Stages 里面定义多个 Jobs。相同的 Stage 中的 Jobs 会并行执行;相同的 Stage 中的 Jobs 都执行成功后,该 Stage 才会成功;如何任何一个 Job 失败,那么该 Stage 失败,换句话说就是该构建任务 Pipeline 失败。
1、部署 GitLab Runner
那么问题来了:在GitLab中我们在什么地方执行构建任务呢?大多数的构建任务会占用大量的系统资源,如果我们直接在 GitLab 中运行构建任务的话,GitLab 的性能肯定会大幅度下降。这里我们就需要讲到 GitLab Runner 了、在 GitLab 中我们需要通过 GitLab Runner 来执行 GitLab CI构建任务。GitLab CI 最大的作用是管理各个项目的构建状态;因此,运行构建任务这种浪费资源的事情交给一个独立的 GitLab Runner 来做就会好很多,更重要的是 Gitlab Runner 可以安装到不同的机器上;甚至是本机,这样完全就不会影响到 GitLab 本身了。
GitLab Runner 的安装非常简单,也有多种安装方式:比如二进制、Docker、Docker-Compose等方式。这里我们将 GitLab Runner 部署到 Kubernetes 集群中。
GitLab Runner官方文档:https://docs.gitlab.com/runner/install/
1.1、验证K8S集群
执行下面的命令验证 Kubernetes 集群:
[root@kubernetes01 ~]# kubectl cluster-info
Kubernetes master is running at https://172.16.200.11:6443
CoreDNS is running at https://172.16.200.11:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
[root@kubernetes01 ~]#
cluster-info
这个命令会显示当前链接的集群状态和可用的集群服务列表。
1.2、获取GitLab CI Register Token
我们可以在 GitLab Menu — Admin — Runners 页面看到两个重要的参数:一个是URL、另外一个是 Register Token ,我们在下面的操作步骤中会用到这两个参数值。
1.3、GitLab CI Runner 资源文件
我们将和GitLab相关的资源对象都部署到GitLab这个NS下面。首先我们就需要通过configmap资源来传递Runner镜像所需要的环境变量,详细的资源清单文件如下(gitlab-ci-config.yaml):
[root@kubernetes01 gitlab-ci]# cat gitlab-ci-config.yaml
apiVersion: v1
data:
REGISTER_NON_INTERACTIVE: "true"
REGISTER_LOCKED: "false"
# 根据实际情况修改你的CI_SERVER_URL
CI_SERVER_URL: "http://gitlab.gitlab.svc.cluster.local/ci"
METRICS_SERVER: "0.0.0.0:9100"
RUNNER_REQUEST_CONCURRENCY: "4"
RUNNER_EXECUTOR: "kubernetes"
KUBERNETES_NAMESPACE: "gitlab"
KUBERNETES_PRIVILEGED: "true"
KUBERNETES_CPU_REQUEST: "250m"
KUBERNETES_MEMORY_REQUEST: "256Mi"
KUBERNETES_CPU_LIMIT: "1"
KUBERNETES_MEMORY_LIMIT: "1Gi"
KUBERNETES_SERVICE_CPU_REQUEST: "150m"
KUBERNETES_SERVICE_MEMORY_REQUEST: "256Mi"
KUBERNETES_SERVICE_CPU_LIMIT: "1"
KUBERNETES_SERVICE_MEMORY_LIMIT: "1Gi"
KUBERNETES_HELPER_CPU_REQUEST: "150m"
KUBERNETES_HELPER_MEMORY_REQUEST: "100Mi"
KUBERNETES_HELPER_CPU_LIMIT: "500m"
KUBERNETES_HELPER_MEMORY_LIMIT: "200Mi"
KUBERNETES_PULL_POLICY: "if-not-present"
KUBERNETES_TERMINATIONGRACEPERIODSECONDS: "10"
KUBERNETES_POLL_INTERVAL: "5"
KUBERNETES_POLL_TIMEOUT: "360"
# 根据你的实际情况修改并添加对应的hosts映射文件、不然后面会报错
RUNNER_PRE_CLONE_SCRIPT : "echo '172.16.200.11 gitlab.z0ukun.com registry.z0ukun.com' >> /etc/hosts"
RUNNER_PRE_CLONE_SCRIPT : "echo '172.16.200.12 gitlab.z0ukun.com registry.z0ukun.com' >> /etc/hosts"
RUNNER_PRE_CLONE_SCRIPT : "echo '172.16.200.13 gitlab.z0ukun.com registry.z0ukun.com' >> /etc/hosts"
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-cm
namespace: gitlab
注:CI_SERVER_URL 对应的值需要指向 GitLab 实例的 URL(可以是外网地址,也可以是 Kubernetes 集群内部的 Service DNS 地址,因为 Runner 也是运行在 Kubernetes 集群中的)并加上/ci(我这里是 http://gitlab.gitlab.svc.cluster.local/ci )。此外还添加了一些构建容器运行的资源限制,我们可以自己根据需要进行更改即可。如果你在向configmap中添加了新的选项和参数后,需要把在运行的 GitLab CI Runner Pod 删除。因为我们是使用 envFrom 来注入上面的这些环境变量而不是直接使用 env 的(envFrom 通过将环境变量放置到ConfigMaps或Secrets来帮助减小清单文件)。
当然更详细的参数选项我们可以在Pod中运行 kubectl exec -it gitlab-ci-runner-0 /bin/bash -n gitlab 命令来查看,只需要为要配置的标志添加 env 变量即可,例如:
[root@kubernetes01 gitlab-ci]# kubectl exec -it gitlab-ci-runner-0 /bin/bash -n gitlab
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
gitlab-runner@gitlab-ci-runner-0:/$
gitlab-runner@gitlab-ci-runner-0:/$ gitlab-ci-multi-runner register --help | more
Runtime platform arch=amd64 os=linux pid=229 revision=8925d9a0 version=14.1.0
NAME:
gitlab-ci-multi-runner register - register a new runner
USAGE:
gitlab-ci-multi-runner register [command options] [arguments...]
OPTIONS:
-c value, --config value Config file (default: "/home/gitlab-runner/.gitlab-runne
r/config.toml") [$CONFIG_FILE]
--template-config value Path to the configuration template file [$TEMPLATE_CONFI
G_FILE]
--tag-list value Tag list [$RUNNER_TAG_LIST]
-n, --non-interactive Run registration unattended [$REGISTER_NON_INTERACTIVE]
--leave-runner Don't remove runner if registration fails [$REGISTER_LEA
VE_RUNNER]
-r value, --registration-token value Runner's registration token [$REGISTRATION_TOKEN]
--run-untagged Register to run untagged builds; defaults to 'true' when
'tag-list' is empty [$REGISTER_RUN_UNTAGGED]
--locked Lock Runner for current project, defaults to 'true' [$RE
GISTER_LOCKED]
--access-level value Set access_level of the runner to not_protected or ref_p
rotected; defaults to not_protected [$REGISTER_ACCESS_LEVEL]
--maximum-timeout value What is the maximum timeout (in seconds) that will be se
t for job when using this Runner (default: "0") [$REGISTER_MAXIMUM_TIMEOUT]
--paused Set Runner to be paused, defaults to 'false' [$REGISTER_
PAUSED]
--name value, --description value Runner name (default: "gitlab-ci-runner-0") [$RUNNER_NAM
E]
--limit value Maximum number of builds processed by this runner (defau
lt: "0") [$RUNNER_LIMIT]
--output-limit value Maximum build trace size in kilobytes (default: "0") [$R
UNNER_OUTPUT_LIMIT]
--request-concurrency value Maximum concurrency for job requests (default: "4") [$RU
NNER_REQUEST_CONCURRENCY]
-u value, --url value Runner URL (default: "http://gitlab.gitlab.svc.cluster.l
ocal/ci") [$CI_SERVER_URL]
-t value, --token value Runner token [$CI_SERVER_TOKEN]
--tls-ca-file value File containing the certificates to verify the peer when
using HTTPS [$CI_SERVER_TLS_CA_FILE]
--tls-cert-file value File containing certificate for TLS client auth when usi
ng HTTPS [$CI_SERVER_TLS_CERT_FILE]
--tls-key-file value File containing private key for TLS client auth when usi
ng HTTPS [$CI_SERVER_TLS_KEY_FILE]
--executor value Select executor, eg. shell, docker, etc. (default: "kube
--More--
1.4、GitLab CI Runner注册脚本
除了上面的一些环境变量相关的配置外,我们还需要一个用于注册、运行和取消注册 GitLab CI Runner 的小脚本。只有当 Pod 正常通过 Kubernetes(TERM信号)终止时,才会触发转轮取消注册。 如果强制终止 Pod(SIGKILL信号),Runner 将不会注销自身。必须手动完成对这种被杀死的 Runner 的清理,资源清单文件(runner-scripts-cm.yaml)如下:
[root@kubernetes01 gitlab-ci]# cat 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: gitlab
1.5、创建 Secret资源对象
从上面的资源清单文件我们可以看到、这里我们需要一个 GITLAB_CI_TOKEN
,我们用 GitLab CI Runner Token 来创建一个 Kubernetes secret 对象。然后将 token 进行 base64 编码:
# 注:这里的 M_cDBYmtaxTGZWy4megE 就是我们上图中看到的 registration token
[root@kubernetes01 gitlab-ci]# echo M_cDBYmtaxTGZWy4megE | base64 -w0
TV9jREJZbXRheFRHWld5NG1lZ0UK
[root@kubernetes01 gitlab-ci]#
然后我们使用上面的 Token 创建一个 Secret 对象,资源清单文件如下:
[root@kubernetes01 gitlab-ci]# cat gitlab-ci-token-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: gitlab-ci-token
namespace: gitlab
labels:
app: gitlab-ci-runner
data:
GITLAB_CI_TOKEN: TV9jREJZbXRheFRHWld5NG1lZ0UK
1.6、创建Runner StatefulSet 控制器
现在我们就可以来编写一个用于真正运行 Runner 的 StatefulSet 控制器资源对象。首先,在开始运行的时候,尝试取消注册所有的同名 Runner,当节点丢失时(即NodeLost
事件)然后再尝试重新注册自己并开始运行。在正常停止 Pod 的时候,Runner 将会运行unregister
命令来尝试取消自己,所以 GitLab 就不能再使用这个 Runner 了,这个是通过 Kubernetes Pod 生命周期中的hooks
来完成的。另外我们通过使用envFrom
来指定Secrets
和ConfigMaps
来用作环境变量,对应的资源清单文件(runner-statefulset.yaml )如下:
[root@kubernetes01 gitlab-ci]# cat runner-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gitlab-ci-runner
namespace: gitlab
labels:
app: gitlab-ci-runner
spec:
updateStrategy:
type: RollingUpdate
replicas: 3
serviceName: gitlab-ci-runner
selector:
matchLabels:
app: gitlab-ci-runner
template:
metadata:
labels:
app: gitlab-ci-runner
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: "kubernetes.io/hostname"
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- gitlab-ci-runner
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:v14.1.0
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
1.7、创建RBAC资源对象
我们还需要为上面的 Runner 控制器创建一个RBAC资源对象,详细的资源清单文件(runner-rbac.yaml)如下:
[root@kubernetes01 gitlab-ci]# cat runner-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-ci
namespace: gitlab
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: gitlab
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-ci
namespace: gitlab
subjects:
- kind: ServiceAccount
name: gitlab-ci
namespace: gitlab
roleRef:
kind: Role
name: gitlab-ci
apiGroup: rbac.authorization.k8s.io
1.8、创建 Runner 资源对象
资源清单文件都准备无误之后我们就直接创建这些资源对象:
[root@kubernetes01 gitlab-ci]# ls
gitlab-ci-config.yaml gitlab-ci-token-secret.yaml runner-rbac.yaml runner-statefulset.yaml
gitlab-ci-k8s-demo gitlab-sa.yaml runner-scripts-cm.yaml
[root@kubernetes01 gitlab-ci]#
[root@kubernetes01 gitlab-ci]# kubectl apply -f .
configmap/gitlab-ci-runner-cm created
secret/gitlab-ci-token created
serviceaccount/gitlab created
clusterrolebinding.rbac.authorization.k8s.io/gitlab 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
[root@kubernetes01 gitlab-ci]#
1.9、验证 Runners
创建完成后,可以通过查看 Pod 状态判断 Runner 是否运行成功:
[root@kubernetes01 gitlab-ci]# kubectl get pod -n gitlab
NAME READY STATUS RESTARTS AGE
gitlab-ci-runner-0 1/1 Running 0 3m47s
gitlab-ci-runner-1 1/1 Running 0 3m45s
gitlab-ci-runner-2 1/1 Running 0 3m41s
gitlab-f5d7f6c48-54pgk 1/1 Running 0 3d12h
gitlab-postgresql-678f8fc4c6-pcxzp 1/1 Running 0 3d12h
gitlab-redis-5899459775-s5xb9 1/1 Running 0 4d6h
[root@kubernetes01 gitlab-ci]#
从上面的信息我们可以看到已经成功运行了三个(具体取决于StatefulSet
清单中的副本数,这里我配置了3个副本) Runner 实例,然后我们可以刷新 GitLab Menu — Admin — Runners 页面:
好了、到这里 GitLab CI Runner 就已经不是成功了;当然我们也可以根据需要更改 Runner 的一些配置,比如添加 tag 标签等。