Gitlab CI/CD 实践一:Gitlab Runner 安装到 K8S 集群
本文最后更新于 410 天前,其中的信息可能已经有所发展或是发生改变。

背景

最近负责公司的 Devops 改造,使用 Gitlab CI/CD 替代之前的 Jenkins。

为什么选择 Gitlab CI/CD 而不是 Jenkins?

  • 不引入其他服务,降低复杂度。
    • 公司已经采用 Gitlab 来做源码管理了。
  • 触发更简单,更敏捷,更灵活。
    • 当在开发环境调试时,有的问题需要在环境里才能复现,就需要经常更新代码到环境,如果是 jenkins,需要到页面上点一下触发。而使用 Gitlab CI/CD,只需提交代码就能触发。一次两次倒没啥区别,但像刚才的情况,需要频繁改动代码,更新到环境,两者既有一点差别了。
    • 灵活配置,在合并请求下的提交,触发流水线更新到测试环境。打 tag 触发更新到发布环境。
  • 方便合并代码前的 CI
    • CI 可以检测代码,单元测试,在 pr 时可以明显的看到当前 pr 的流水线通过情况。
    • jenkins 需要通过 gitlab 的 webhook 才能实现,得去配置。流水线越复杂,需要配置的就越多。
  • Jenkins 支持插件,功能更强大,但是中小项目 Gitlab CI/CD 够用了。

所以,就开始了 3 个月的 Devops 改造,不仅完美替换了以前的 Jenkins,还实现了很多高效、便捷、以前没有的流水线。例如:

Gitlab Runner

Gitlab Runner 可以直接使用二进制、Docker 或者 k8s 来部署,而使用 k8s 部署带来的的好处是:合理利用资源,工作容器会被调度到资源相对空闲的节点(构建是一个比较耗费资源的过程)。

创建单独的 namespace

gitlab-namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
name: gitlab

注册 Runner

Gitlab Runner 有 3 种级别

  1. 全局共享
    1. 因为 executor 使用的是容器,不是 shell,所以非特殊要求,使用这个就行。如果是 shell,可能每个项目用到的环境不同,需要单独使用 runner
  2. 群组共享
  3. 项目独占

Runner 的并发性

  1. 每个 job 会单独起一个容器
  2. 不同流水线的 job 是并行处理
  3. 同一流水线同一阶段的 job 也是并行处理

获取 Gitlab CI Register Token

访问全局 Runner 配置地址:https://{gitlab 地址}/admin/runners

image-20220516104414540

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: XXXGYTdVdE1zc2lXeXpXcXRVTXI=
  • GITLAB_CI_TOKEN:Gitlab CI Register Token 的 base64 编码

配置存储

创建对象存储 bucket

因构建过程需要缓存一些文件,例如依赖,所以要给 Runner 配置存储。这里用到的分布式存储是 ceph,首先创建 Runner 专用的账户和对象存储 bucket,参照这篇教程:Go 项目基于 Gitlab CI/CD 实践二:Rook Ceph 创建 S3 bucket 用于 Gitlab Runner 缓存

gitlab-runner-cache-s3-secret.yaml

apiVersion: v1
kind: Secret
metadata:
name: gitlab-runner-cache-s3
namespace: gitlab
labels:
app: gitlab-ci-runner
data:
CACHE_S3_ACCESS_KEY: "MDZTSEs5T05OUkxYQjdLU1E0UDM="
CACHE_S3_SECRET_KEY: "QXVkc3JhQlN3alZtNWhqTFo3WG9CbUE2UGU1Q2o3SkJZblQ0R3lUQw=="
  • CACHE_S3_ACCESS_KEY:对象存储 bucket 的 AKbase64 编码
  • CACHE_S3_SECRET_KEY:对象存储 bucket 的 SKbase64 编码

配置 SSL 证书

由于 gitlab 服务是 https,runner 访问 gitlab 的注册接口时,需要证书。

获取 SSL 证书

如果 gitlab 是 docker 部署的,ssl 证书所在路径为:/etc/gitlab/ssl/xxx.com.crt

gitlab-certs-configmap.yaml

apiVersion: v1
data:
xxx.com.crt: |
-----BEGIN CERTIFICATE-----
MIIFSjCCBDKgAwIBAgIUSmsUy8IIhVT1Vl+RbRuO2DY5F3QwDQYJKoZIhvcNAQEN...
+xxxnphZ/4JE3n3OKiw=
-----END CERTIFICATE-----
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner-certs
namespace: gitlab

配置注册 & 注销脚本

默认只有当 Pod 正常通过 Kubernetes(TERM 信号)终止时,才会触发 Runner 取消注册。 如果强制终止 Pod(SIGKILL 信号),Runner 将不会注销自身。必须手动清理这种被杀死的 Runner 。

gitlab-runner-scripts-configmap.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
mkdir -p /home/gitlab-runner/.gitlab-runner/certs
cp /certs/xxx.com.crt /home/gitlab-runner/.gitlab-runner/certs/
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
cat >>/home/gitlab-runner/.gitlab-runner/config.toml <<EOF
[[runners.kubernetes.volumes.host_path]]
name = "docker"
mount_path = "/var/run/docker.sock"
read_only = true
host_path = "/var/run/docker.sock"
EOF
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

遇到的坑

我把证书的 configmap 挂载到 /certs/(这个步骤后面会提到),然后在上面的启动脚本里,将证书文件从 /certs/ 拷贝到 /home/gitlab-runner/.gitlab-runner/certs/,那为什么不直接把证书挂载到 /home/gitlab-runner/.gitlab-runner/certs/

如果这么干,/home/gitlab-runner/.gitlab-runner/certs 目录的所有者就是 root,而 runner 容器没有用 root 用户运行,用的是 gitlab-runner 用户。

在执行注册脚本 /usr/bin/gitlab-ci-multi-runner register -r ${GITLAB_CI_TOKEN} 成功时,会创建 runner 的配置文件 /home/gitlab-runner/.gitlab-runner/config.toml。这时就会报错:PANIC: open /home/gitlab-runner/.gitlab-runner/config.toml: permission denied,原因就是 gitlab-runner 用户操作 root 创建的目录造成的权限问题。

配置 RBAC

gitlab-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

配置 Runner

gitlab-runner-configmap.yaml

apiVersion: v1
data:
CACHE_TYPE: "s3"
CACHE_SHARED: "true"
CACHE_S3_SERVER_ADDRESS: "rook-ceph-rgw-my-store.rook-ceph.svc"
CACHE_S3_BUCKET_NAME: "gitlab-runner-cache-bucket"
CACHE_S3_INSECURE: "true"
REGISTER_NON_INTERACTIVE: "true"
REGISTER_LOCKED: "false"
METRICS_SERVER: "0.0.0.0:9100"
CI_SERVER_URL: "https://xxx.com/ci"
RUNNER_REQUEST_CONCURRENCY: "4"
RUNNER_EXECUTOR: "kubernetes"
KUBERNETES_NAMESPACE: "gitlab"
KUBERNETES_PRIVILEGED: "true"
KUBERNETES_CPU_LIMIT: "3"
KUBERNETES_MEMORY_LIMIT: "4Gi"
KUBERNETES_SERVICE_CPU_LIMIT: "3"
KUBERNETES_SERVICE_MEMORY_LIMIT: "4Gi"
KUBERNETES_HELPER_CPU_LIMIT: "500m"
KUBERNETES_HELPER_MEMORY_LIMIT: "500Mi"
KUBERNETES_PULL_POLICY: "if-not-present"
KUBERNETES_TERMINATIONGRACEPERIODSECONDS: "10"
KUBERNETES_POLL_INTERVAL: "5"
KUBERNETES_POLL_TIMEOUT: "360"
RUNNER_TAG_LIST: "k8s,100.30.30.192,share"
RUNNER_NAME: "192-k8s-runner"
kind: ConfigMap
metadata:
labels:
app: gitlab-ci-runner
name: gitlab-ci-runner
namespace: gitlab
  • CACHE_S3_SERVER_ADDRESS:ceph 对象存储 service
  • CI_SERVER_URL:gitlab 地址,如果是在 k8s 里,可以配置 service。记得末尾加上 /ci

Runner 资源

gitlab-runner-statefulset.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gitlab-ci-runner
namespace: gitlab
labels:
app: gitlab-ci-runner
spec:
selector:
matchLabels:
app: gitlab-ci-runner
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
- name: gitlab-ci-runner-certs
projected:
sources:
- configMap:
name: gitlab-ci-runner-certs
items:
- key: xxx.com.crt
path: xxx.com.crt
mode: 0777
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
- secretRef:
name: gitlab-ci-token
- secretRef:
name: gitlab-runner-cache-s3
ports:
- containerPort: 9100
name: http-metrics
protocol: TCP
volumeMounts:
- name: gitlab-ci-runner-scripts
mountPath: "/scripts"
readOnly: true
- name: gitlab-ci-runner-certs
mountPath: "/certs"
restartPolicy: Always

解决资源依赖关系

kubectl apply 是按照资源定义文件的文件名来创建资源的,并不会处理依赖关系。也就是可能出现 namespace 还没创建,就开始创建其他资源的情况。这里使用 Kustomize 来保证依赖关系(从 1.14 版本开始,kubectl 也开始支持使用 kustomization 文件来管理 Kubernetes 对象)。

kustomization.yaml

resources:
- gitlab-namespace.yaml
- gitlab-certs-configmap.yaml
- gitlab-ci-token-secret.yaml
- gitlab-runner-cache-s3-secret.yaml
- gitlab-runner-configmap.yaml
- gitlab-runner-rbac.yaml
- gitlab-runner-scripts-configmap.yaml
- gitlab-runner-statefulset.yaml
  • 这里的先后顺序不重要,Kustomize 会自动处理

部署

执行命令

进入资源文件目录

.
├── gitlab-certs-configmap.yaml
├── gitlab-ci-token-secret.yaml
├── gitlab-namespace.yaml
├── gitlab-runner-cache-s3-secret.yaml
├── gitlab-runner-configmap.yaml
├── gitlab-runner-rbac.yaml
├── gitlab-runner-scripts-configmap.yaml
├── gitlab-runner-statefulset.yaml
└── kustomization.yaml

执行命令即可:kubectl apply -k .

查看资源状态

kubectl get -k .

image-20220516163518037

查看 Runner 是否注册上

https://{gitlab 地址}/admin/runners

image-20220516163803084

参考资料

  1. 在 Kubernetes 上安装 Gitlab CI Runner

20220924 更新

Gitlab CI/CD 已在我司使用了 4 个月,期间不断迭代优化,现在基本稳定了。Gitlab CI/CD 系列 的文章是早期的版本,这里更新下最新的情况。

不建议将 Gitlab Runner 安装到 K8S 集群,因为流水线会使用缓存来提高效率,流水线每次使用缓存时会下载缓存并解压,修改缓存内容后再压缩、上传。而常见的流水线缓存是数量很大的小文件集合,例如 maven 依赖,go mod 依赖,nodejs 依赖等,解压缩、上传下载都很耗时。

经实测发现,直接挂载目录来实现缓存功能是最高效的,而不是使用 gitlab 的缓存关键字。所以建议使用 Docker 安装 Gitlab Runner,并通过挂载目录实现缓存功能。可参考 Gitlab CI/CD 实践三:Docker 安装 Gitlab Runner

作者:Yuyy
博客:https://yuyy.info
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇