如何使用 Kaniko在 Kubernetes 集群中構(gòu)建容器鏡像
前言
如何構(gòu)建 Kubernetes 所需的容器鏡像?我想你答案肯定是 Docker,沒錯(cuò),Docker 確實(shí)是首選。我們?cè)谑褂?Jenkins 進(jìn)行 CI/CD構(gòu)建容器鏡像的時(shí)候,做法通常是將 Jenkins 服務(wù)部署在物理機(jī)上,然后使用物理機(jī)的 docker build 命令來構(gòu)建鏡像。但是在 Jenkins on K8s 環(huán)境下,Jenkins Master 和 Jenkins Slave 都以 Pod 形式運(yùn)行在 Kubernetes 集群的 Node 上,我們的構(gòu)建環(huán)境都是 Pod ,沒有 docker 命令。
眾所周知 Kubernetes 在 V1.24.x 版本之后默認(rèn)采用 containerd 作為容器運(yùn)行時(shí),不再支持 Docker,我們想用宿主機(jī)上的 /var/run/docker.sock 也用不了了。我們就需要另一種方式在 Kubernetes 集群中構(gòu)建容器鏡像,本文將介紹 Kaniko工具。
什么是Kaniko
Github地址:https://github.com/GoogleContainerTools/kaniko
Kaniko 是Google開源的一款在 Kubernetes 用來構(gòu)建容器鏡像的工具,它是一個(gè)從 Dockerfile 構(gòu)建容器鏡像的工具,就像 Docker 一樣,但主要區(qū)別在于 Kaniko 可以在容器內(nèi)運(yùn)行,這意味著它可以在 Kubernetes 集群內(nèi)運(yùn)行。不需要特權(quán)模式,也不需要公開任何套接字。不需要在我們集群的節(jié)點(diǎn)上運(yùn)行 Docker,因此我們使用哪個(gè)容器引擎來運(yùn)行容器并不重要。重要的是 Kaniko 可以在容器內(nèi)構(gòu)建容器鏡像,并在 Kubernetes 集群內(nèi)自動(dòng)構(gòu)建,這就和宿主機(jī)上的 Docker 解綁了,更加安全可靠。特點(diǎn)如下:
- docker in docker
- 不依賴 Docker守護(hù)進(jìn)程
- 不需要 privilege 權(quán)限,安全構(gòu)建
- 僅需一條命令
- 支持 Dockerfile 構(gòu)建
現(xiàn)在,讓我們一步一步來探索 Kaniko。
在Kubernetes中使用Kaniko
Kaniko 以容器的方式來運(yùn)行的,同時(shí)需要三個(gè)參數(shù): Dockerfile,上下文,以及遠(yuǎn)端鏡像倉庫的地址。
工作原理:
- 讀取并解析指定的Dockerfile
- 提取基礎(chǔ)鏡像的文件系統(tǒng)(Dockerfile 中的 FROM 鏡像)
- 在獨(dú)立的Dockerfile中分別運(yùn)行每個(gè)命令
- 每次運(yùn)行后都會(huì)對(duì)用戶空間文件系統(tǒng)的做快照
- 每次運(yùn)行時(shí),將快照層附加到基礎(chǔ)層并更新鏡像元數(shù)據(jù)
- 最后推送鏡像
前提條件:
- 需要一個(gè)運(yùn)行的 kubernetes 集群
- 需要?jiǎng)?chuàng)建一個(gè) Kubernetes secret,其中包含推送到鏡像倉庫所需的身份驗(yàn)證信息
- 需要準(zhǔn)備好Dockerfile
創(chuàng)建Dockerfile
編寫一個(gè)Dockerfile 文件,并創(chuàng)建configmap 在k8s中:
[root@localhost ~]# cat Dockerfile
FROM ubuntu
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
[root@localhost ~]# kubectl create configmap kaniko-dockerfile --from-file=./Dockerfile
configmap/kaniko-dockerfile created
創(chuàng)建鏡像倉庫憑證的Secret
創(chuàng)建一個(gè)配置kaniko推送到 阿里云鏡像倉庫的憑證,找一臺(tái)有安裝docker的機(jī)子,登錄倉庫:
[root@localhost ~]# docker login --username=xxx registry.cn-shanghai.aliyuncs.com
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
登入成功之后,會(huì)生成一個(gè)config.json的文件,使用該文件創(chuàng)建一個(gè)secret供kaniko容器使用。
- centos環(huán)境中文件位置:/root/.docker/config.json
- ubuntu環(huán)境中文件位置:/home/ubuntu/.docker/config.json
[root@localhost ~]# kubectl create secret generic kaniko-secret --from-file=/root/.docker/config.json
secret/kaniko-secret created
啟動(dòng)kaniko容器用來構(gòu)建鏡像
kaniko_build_image.yaml:
apiVersion: v1
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args: ["--dockerfile=/workspace/Dockerfile",
"--context=dir://workspace",
"--destination=registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo:1.0"] # 替換成自己的倉庫地址
volumeMounts:
- name: kaniko-secret
mountPath: /kaniko/.docker
- name: dockerfile
mountPath: /workspace
volumes:
- name: kaniko-secret
secret:
secretName: kaniko-secret
items:
- key: config.json
path: config.json
- name: dockerfile
configMap:
name: kaniko-dockerfile
- args 部分:這部分就是上面所講的,kaniko運(yùn)行時(shí)需要三個(gè)參數(shù): Dockerfile(--dockerfile),上下文(--context),遠(yuǎn)端鏡像倉庫(--destination)
- secret 部分:推送至指定遠(yuǎn)端鏡像倉庫需要認(rèn)證,所以以secret的方式掛載到/kaniko/.docker/這個(gè)目錄下,文件名稱為config.json
創(chuàng)建pod,查看狀態(tài),并看log是否正常完成。
[root@localhost ~]# kubectl apply -f kaniko_build_image.yaml
pod/kaniko created
[root@localhost ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
kaniko 1/1 Running 0 3s
[root@localhost ~]# kubectl logs -f kaniko
INFO[0000] Retrieving image manifest ubuntu
INFO[0000] Retrieving image ubuntu from registry index.docker.io
INFO[0003] Built cross stage deps: map[]
INFO[0003] Retrieving image manifest ubuntu
INFO[0003] Returning cached image manifest
INFO[0003] Executing 0 build triggers
INFO[0003] Building stage 'ubuntu' [idx: '0', base-idx: '-1']
INFO[0003] Skipping unpacking as no commands require it.
INFO[0003] ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
INFO[0003] Pushing image to registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo:1.0
INFO[0004] Pushed registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo@sha256:d1855cc00550f9048c88b507626e0f24acf4c22e02856e006b2b9fdb0b80e567
查看阿里云鏡像倉庫已經(jīng)上傳上去了:
用kaniko生成的鏡像來測(cè)試下是否可用:
[root@localhost ~]# docker run -it --rm registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo:1.0
Unable to find image 'registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo:1.0' locally
1.0: Pulling from kubesre02/kaniko-demo
445a6a12be2b: Pull complete
Digest: sha256:49bb962115b70a15a99b87e48fda28a883758081e41aef14766833d3a1578069
Status: Downloaded newer image for registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko-demo:1.0
hello
出來hello即成功。
自定義一個(gè)Kaniko鏡像
官方 kaniko 鏡像是基于 scratch 構(gòu)建的,里面沒有 shell,想在 kaniko 原生鏡像里在調(diào)用 shell 命令是很麻煩的。所以我們可以自定義我們自己私有的 Kaniko 鏡像。kaniko 的關(guān)鍵文件其實(shí)是/kaniko目錄下的二進(jìn)制文件,官方推薦是用 gcr.io/kaniko-project/executor 鏡像,我們就可以拷貝這個(gè)/kaniko目錄下的 executor 到我們自己的私有鏡像 Dockerfile:
FROM gcr.io/kaniko-project/executor:latest AS plugin
FROM ubuntu
ENV DOCKER_CONFIG /kaniko/.docker
COPY --from=plugin /kaniko/executor /usr/local/bin/kaniko
RUN mkdir -p /kaniko/.docker/
COPY config.json /kaniko/.docker/
上面 Dockerfile 構(gòu)建了一個(gè)基于Ubuntu的Docker鏡像:
(1) FROM gcr.io/kaniko-project/executor:latest AS plugin
- 從名為gcr.io/kaniko-project/executor:latest的Docker鏡像開始構(gòu)建一個(gè)臨時(shí)階段,并將其命名為plugin。
- 這個(gè)步驟是在一個(gè)已經(jīng)包含了Kaniko工具的Docker鏡像中構(gòu)建一個(gè)中間容器。
(2) FROM ubuntu
接下來創(chuàng)建一個(gè)新的Docker鏡像,基于官方的Ubuntu基礎(chǔ)鏡像。
(3) ENV DOCKER_CONFIG /kaniko/.docker
設(shè)置環(huán)境變量DOCKER_CONFIG為/kaniko/.docker,這是Kaniko工具使用的Docker配置目錄。這是為了確保Kaniko可以找到必要的Docker配置信息。
(4) COPY --from=plugin /kaniko/executor /usr/local/bin/kaniko
從之前構(gòu)建的plugin階段的鏡像中復(fù)制/kaniko/executor文件到新的鏡像的/usr/local/bin/kaniko路徑下。這將把Kaniko二進(jìn)制文件復(fù)制到新的Ubuntu鏡像中,以便后續(xù)在該鏡像中使用。
(5) RUN mkdir -p /kaniko/.docker/
在新的鏡像中創(chuàng)建/kaniko/.docker/目錄,用于存放Docker配置文件。
(6) COPY config.json /kaniko/.docker/
將本地的config.json文件復(fù)制到新的鏡像的/kaniko/.docker/目錄下。這個(gè)config.json文件很可能包含了Docker倉庫的認(rèn)證信息和其他配置,以便Kaniko可以訪問和推送Docker鏡像。
最終,這個(gè)Docker鏡像將包含了Ubuntu操作系統(tǒng)和Kaniko工具,并配置了Kaniko所需的Docker環(huán)境,使得可以在容器內(nèi)使用Kaniko構(gòu)建Docker鏡像,并且有了 shell 環(huán)境,同時(shí)保持了一定程度的隔離性和安全性。構(gòu)建并上傳鏡像:
[root@localhost ~]# docker build -t registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko:latest .
Sending build context to Docker daemon 212.1MB
Step 1/6 : FROM gcr.io/kaniko-project/executor:latest AS plugin
---> 03375da0f864
Step 2/6 : FROM ubuntu
---> aa786d622bb0
Step 3/6 : ENV DOCKER_CONFIG /kaniko/.docker
---> Using cache
---> f7cd726aa130
Step 4/6 : COPY --from=plugin /kaniko/executor /usr/local/bin/kaniko
---> Using cache
---> 8fcac536196f
Step 5/6 : RUN mkdir -p /kaniko/.docker/
---> Using cache
---> 1afce75446a3
Step 6/6 : COPY config.json /kaniko/.docker/
---> Using cache
---> 4a2873a75a7c
Successfully built 4a2873a75a7c
Successfully tagged registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko:latest
[root@localhost ~]# docker push registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko:latest
The push refers to repository [registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko]
0cb4fb37580b: Pushed
c9dbd3644e5a: Pushed
33a2214b827c: Pushed
c5077dd8160b: Pushed
948c7f86fd48: Pushed
403aab81b15b: Pushed
7bff100f35cb: Pushed
latest: digest: sha256:b5642885f1333a757625cbe36a9b1102aba27646f2572acab79861a74dba1050 size: 1783
Kaniko 更多參數(shù)
- --context:指定構(gòu)建上下文的路徑。默認(rèn)情況下,上下文是Dockerfile所在的目錄??珊?jiǎn)寫 -c
- --dockerfile:指定要使用的Dockerfile的路徑。默認(rèn)情況下,Kaniko會(huì)在上下文中查找名為Dockerfile的文件??珊?jiǎn)寫 -f
- --destination:指定構(gòu)建完成后的Docker鏡像名稱,可以包括標(biāo)簽。例如:myregistry/myimage:tag。可簡(jiǎn)寫 -d
- --cache:?jiǎn)⒂没蚪肒aniko的構(gòu)建緩存功能。默認(rèn)情況下,緩存是啟用的。
- --cache-ttl:設(shè)置構(gòu)建緩存的生存時(shí)間。例如,--cache-ttl=10h表示緩存在構(gòu)建完成后的10小時(shí)內(nèi)有效。
- --cache-repo:指定用于存儲(chǔ)構(gòu)建緩存的Docker倉庫。默認(rèn)情況下,緩存存儲(chǔ)在本地。
- --cache-dir:指定用于存儲(chǔ)構(gòu)建緩存的本地目錄路徑。
- --skip-tls-verify:跳過TLS證書驗(yàn)證,用于不安全的Docker倉庫。
- --build-arg:傳遞構(gòu)建參數(shù)給Dockerfile中的ARG指令。例如:--build-arg key=value。
- --insecure:允許從不受信任的Registry拉取基礎(chǔ)鏡像。
- --insecure-registry:允許連接到不受信任的Registry。
- --verbosity:設(shè)置構(gòu)建的詳細(xì)程度,可以是panic、error、warning、info、debug或trace。
- --digest-file:指定一個(gè)文件,用于存儲(chǔ)構(gòu)建生成的鏡像的摘要(Digest)。
- --oci-layout-path:指定OCI(Open Container Initiative)布局文件的路徑,用于存儲(chǔ)構(gòu)建過程的元數(shù)據(jù)。
更多參考 官方:https://github.com/GoogleContainerTools/kaniko#additional-flags
Kaniko 構(gòu)建緩存
--cache-copy-layers 此參數(shù)在執(zhí)行命令之前 kaniko 會(huì)檢查層的緩存,如果存在 kaniko將拉取并提取緩存層,而不是執(zhí)行命令。如果沒有 kaniko將執(zhí)行命令,然后將新創(chuàng)建的層推送到緩存。用戶可以通過設(shè)置 --cache=true 參數(shù)決定是否啟用緩存,并且可以通過--cache-repo 標(biāo)志提供用于存儲(chǔ)緩存層的遠(yuǎn)程存儲(chǔ)庫,如果未提供此標(biāo)志則將從提供的 –destination 推斷緩存的repo。通常 --cache=true --cache-copy-layers=true 這兩個(gè)參數(shù)同時(shí)使用。
CI/CD 中使用Kaniko
jenkins pipline 中使用:
基于 Kubernetes 的 動(dòng)態(tài)生成 Jenkins Slave pod CI/CD,以下是一個(gè)簡(jiǎn)化的Pipeline示例:
// 鏡像倉庫地址
def registry = "registry.cn-shanghai.aliyuncs.com/kubesre02/demo"
pipeline {
agent {
kubernetes {
yaml """
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
# 使用自定義的 kaniko 鏡像
image: registry.cn-shanghai.aliyuncs.com/kubesre02/kaniko:latest
imagePullPolicy: Always
command:
- cat
tty: true
"""
}
}
stages {
stage('拉代碼') {
steps {
git clone ...
}
}
stage('構(gòu)建鏡像') {
steps {
// 使用 kaniko 來構(gòu)建鏡像
container(name: 'kaniko') {
Dockerfile 內(nèi)容...
sh "kaniko -f Dockerfile -c ./ -d $registry:$BUILD_NUMBER --force"
}
}
}
stage('部署') {
steps {
部署...
}
}
}
}
可以看出,Kaniko 是非常適合在Kubernetes平臺(tái)上構(gòu)建容器鏡像的,而且易于集成到 DevOps Jenkins pipeline 里,好了,kaniko 就介紹到這里。