Gitlab CI 在 Kubernetes 中的 Docker 緩存
前面我們有文章介紹過(guò)如何在 Kubernetes 集群中使用 GitLab CI 來(lái)實(shí)現(xiàn) CI/CD,在構(gòu)建鏡像的環(huán)節(jié)我們基本上都是使用的 Docker On Docker 的模式,這是因?yàn)?Kubernetes 集群使用的是 Docker 這種容器運(yùn)行時(shí),所以我們可以將宿主機(jī)的 docker.sock 文件掛載到容器中構(gòu)建鏡像,而最近我們?cè)谑褂?Kubernetes 1.22.X 版本后將容器運(yùn)行時(shí)更改為了 Containerd,這樣節(jié)點(diǎn)上沒(méi)有可用的 Docker 服務(wù)了,這個(gè)時(shí)候就需要更改構(gòu)建鏡像的模式了,當(dāng)然要實(shí)現(xiàn)構(gòu)建鏡像的方式有很多,我們這里還是選擇使用 Docker 來(lái)構(gòu)建我們的 Docker 鏡像,也就是使用 Docker IN Docker 的模式。
在每次構(gòu)建鏡像的時(shí)候,GitLab Runner 都會(huì)啟動(dòng)一個(gè)包含3個(gè)容器的 Pod,其中一個(gè)就是運(yùn)行 Docker 守護(hù)進(jìn)程的 Docker DIND 容器,構(gòu)建的容器會(huì)去連接到運(yùn)行在同一個(gè) Pod 上的 Docker 守護(hù)進(jìn)程,由于 Pod 中的所有容器共享同一個(gè) network namespace,構(gòu)建鏡像的 Docker CLI 能夠通過(guò) localhost 直接連接到 Docker 守護(hù)進(jìn)程進(jìn)行構(gòu)建。但是這種方式最大的一個(gè)問(wèn)題是每次構(gòu)建都是啟動(dòng)一個(gè)全新的 Docker 守護(hù)進(jìn)程,造成沒(méi)有緩存 Docker layer 層,這會(huì)顯著增加我們的構(gòu)建時(shí)間。
這個(gè)問(wèn)題的解決方法非常簡(jiǎn)單,與其為每個(gè) Pod 運(yùn)行一個(gè) Docker DIND 服務(wù)的 sidecar 容器,不如讓我們運(yùn)行一個(gè)獨(dú)立的 Docker DIND 容器,構(gòu)建容器的所有 Docker CLI 都連接到這個(gè)一個(gè) Docker 守護(hù)進(jìn)程上,這個(gè)時(shí)候我們將 Docker layer 層進(jìn)行持久化,也就起到了緩存的作用了。
首先創(chuàng)建一個(gè) PVC 來(lái)存儲(chǔ) Docker 的持久化數(shù)據(jù),為了性能考慮,這里我們使用的是一個(gè) 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??#?數(shù)據(jù)存儲(chǔ)的目錄
??nodeAffinity:
????required:
??????nodeSelectorTerms:
??????-?matchExpressions:
????????-?key:?kubernetes.io/hostname
??????????operator:?In
??????????values:
??????????-?node1??#?運(yùn)行在node1節(jié)點(diǎn)
---
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 部署一個(gè) Docker DIND 服務(wù):
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/??#?指定一個(gè)鏡像加速器地址
??????????env:
????????????-?name:?DOCKER_DRIVER
??????????????value:?overlay2
????????????-?name:?DOCKER_HOST
??????????????value:?tcp://0.0.0.0:2375
????????????-?name:?DOCKER_TLS_CERTDIR???#?禁用?TLS?
??????????????value:?""
??????????volumeMounts:
????????????-?name:?docker-dind-data-vol?#?持久化docker根目錄
??????????????mountPath:?/var/lib/docker/
??????????ports:
????????????-?name:?daemon-port
??????????????containerPort:?2375
??????????securityContext:
????????????privileged:?true?#?需要設(shè)置成特權(quán)模式
??????volumes:
????????-?name:?docker-dind-data-vol
??????????persistentVolumeClaim:
????????????claimName:?docker-dind-data
然后創(chuàng)建一個(gè) Service 以方便構(gòu)建的 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 服務(wù)部署完成后,我們就可以在 Gitlab CI 中使用這個(gè)守護(hù)程序來(lái)構(gòu)建鏡像了,如下所示:
tages:
??-?image
build_image:
??stage:?image
??image:?docker:latest
??variables:
????DOCKER_HOST:?tcp://docker-dind:2375??#?通過(guò)?service?dns?形式連接?docker?dind?服務(wù)
??script:
????-?docker?info
????-?docker?build?-t?xxxx?.
????-?docker?push?xxxx
??only:
????-?tags
由于我們緩存了 Docker layer 層,這個(gè)時(shí)候構(gòu)建的速度會(huì)明顯提升。最后隨著鏡像的大量構(gòu)建會(huì)產(chǎn)生很多鏡像數(shù)據(jù),我們可以寫(xiě)一個(gè) Cronjob 用來(lái)定時(shí)清除緩存:
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
