前言
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
   |