前言
Gitlab Runner的执行器有8个,常见的有Shell、Dcoker和Kubernetes。
Shell执行器配置简单,但是构建工具都需要手动安装,例如:JDK、Maven、Nodejs。如果此时有需求需要升级所有Shell执行器的JDK版本,还需要重新手动升级?(虽然可以写个脚本)
Docker和Kubernetes执行器比较灵活,缺点就是Docker执行器和Kubenetes执行器每次启动的容器或者Pod依赖包都是干净的,就需要重新拉取相关依赖包,比较耽误构建时间。
此时Shell执行器就比较占优势,因为是使用系统本机的构建工具,会在系统上留有构建缓存,例如:.mvn
那么怎么做,也能缓存Docker执行器的layer层呢?
这个问题的解决方法非常简单,与其为每个 Pod 运行一个 Docker DIND 服务的 sidecar 容器,不如让我们运行一个独立的 Docker DIND 容器,构建容器的所有 Docker CLI 都连接到这个一个 Docker 守护进程上,这个时候我们将 Docker layer 层进行持久化,也就起到了缓存的作用了。
Docker 镜像说明
查看官方的 docker 镜像
docker:latest
该镜像只包含 Docker 客户端,需要有 Docker daemon 支持,可以使用 docker:dind
的,也可以挂载宿主机的 /var/run/docker.sock
。
该镜像启动不需要 --privileged
参数。
docker:dind
该镜像包含 Docker 客户端(命令行工具)和 Docker daemon。
通过 docker history docker:dind
命令我们发现 docker:dind
是在 docker:latest
基础上又安装了 Docker daemon
启动 docker:dind
容器时,参数 --privileged
必须加上,否则 Docker daemon 启动时会报错。
正文开始
首先创建一个 PVC 来存储 Docker 的持久化数据,为了性能考虑,这里我们使用的是一个 Local PV:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-volume provisioner: kubernetes.io/no-provisioner reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer
--- apiVersion: v1 kind: PersistentVolume metadata: name: docker-pv spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: local-volume local: path: /mnt/k8s/docker nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node1 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: labels: app: docker-dind name: docker-dind-data namespace: kube-ops spec: accessModes: - ReadWriteOnce storageClassName: local-volume resources: requests: storage: 5Gi
|
然后使用 Deployment 部署一个 Docker DIND 服务:
apiVersion: apps/v1 kind: Deployment metadata: name: docker-dind namespace: kube-ops labels: app: docker-dind spec: selector: matchLabels: app: docker-dind template: metadata: labels: app: docker-dind spec: containers: - image: docker:dind name: docker-dind args: - --registry-mirror=https://ot2k4d59.mirror.aliyuncs.com/ env: - name: DOCKER_DRIVER value: overlay2 - name: DOCKER_HOST value: tcp://0.0.0.0:2375 - name: DOCKER_TLS_CERTDIR value: "" volumeMounts: - name: docker-dind-data-vol mountPath: /var/lib/docker/ ports: - name: daemon-port containerPort: 2375 securityContext: privileged: true volumes: - name: docker-dind-data-vol persistentVolumeClaim: claimName: docker-dind-data
|
然后创建一个 Service 以方便构建的 Docker CLI 与其连接:
apiVersion: v1 kind: Service metadata: name: docker-dind namespace: kube-ops labels: app: docker-dind spec: ports: - port: 2375 targetPort: 2375 selector: app: docker-dind
|
将 Docker DIND 服务部署完成后,我们就可以在 Gitlab CI 中使用这个守护程序来构建镜像了,如下所示:
tages: - image
build_image: stage: image image: docker:latest variables: DOCKER_HOST: tcp://docker-dind:2375 script: - docker info - docker build -t xxxx . - docker push xxxx only: - tags
|
由于我们缓存了 Docker layer 层,这个时候构建的速度会明显提升。最后随着镜像的大量构建会产生很多镜像数据,我们可以写一个 Cronjob 用来定时清除缓存:
apiVersion: batch/v1 kind: CronJob metadata: name: docker-dind-clear-cache namespace: kube-ops spec: schedule: 0 0 * * 0 jobTemplate: metadata: labels: app: docker-dind name: docker-dind-clear-cache spec: template: spec: restartPolicy: OnFailure containers: - name: clear-cache image: docker:latest command: - docker - system - prune - -af env: - name: DOCKER_HOST value: tcp://docker-dind:2375
|