使用 Kaniko來構建鏡像
Kaniko 是一種專注于容器鏡像構建的開源工具,其核心設計理念與 Docker 存在顯著差異。以下從功能定位、技術實現和適用場景三方面進行對比分析:
一、Kaniko 的核心特性
- 無需 Docker 守護進程
Kaniko 直接在容器或 Kubernetes 集群中運行,完全脫離 Docker 守護進程(Docker Daemon)。這種設計通過消除對特權模式的依賴,提升了安全性和資源利用率。 - 基于用戶空間的構建流程
它會逐行解析 Dockerfile,提取基礎鏡像文件系統,并在用戶空間內執行每條指令,通過快照比對生成鏡像層,最后推送至鏡像倉庫。此過程避免了傳統 Docker 構建中的權限依賴。 - 云原生場景優化
專為 Kubernetes 和 CI/CD 流水線設計,支持通過 Secret 掛載憑證,可直接集成到 Jenkins 等工具中,實現自動化構建。
二、與 Docker 的主要區別
對比維度 | Kaniko | Docker |
---|---|---|
架構依賴 | 無需 Docker Daemon,獨立運行于容器環境 | 依賴 Docker Daemon 執行構建任務 |
安全性 | 容器隔離構建,避免掛載宿主機敏感文件 | 需掛載 /var/run/docker.sock ,存在特權風險 |
適用環境 | Kubernetes、無 Docker 環境的云原生場景 | 本地開發環境、傳統 Docker 宿主機 |
構建效率 | 并行構建技術縮短時間,但受網絡/緩存影響 | 本地構建速度快,但集群場景需額外配置 |
鏡像推送 | 強制要求構建后推送至遠程倉庫 | 支持本地存儲或手動推送 |
三、典型使用場景建議
- Kubernetes 集群內構建
當宿主機未安裝 Docker 或需避免特權模式時(如 Kubernetes v1.24+ 默認使用 containerd),Kaniko 是更安全的選擇。 - 高安全要求的 CI/CD 流水線
在 Jenkins 等工具中,通過 Pod 模板調用 Kaniko,可避免暴露宿主機 Docker 套接字,符合安全合規要求。 - 多平臺鏡像構建
支持--platform
參數指定目標架構(如linux/amd64
),適合跨平臺交付場景。
四、性能注意事項
盡管 Kaniko 具備并行構建優勢,但實際速度可能受基礎鏡像拉取延遲、緩存配置(如 --cache-repo
參數優化)和上下文傳輸方式(如 GCS/S3 存儲桶)影響,需結合網絡環境調優。
五、Kaniko的優勢
Kaniko作為一個創新的容器鏡像構建工具,在云原生時代提供了顯著的優勢,特別是在高效且靈活的容器鏡像構建方面。以下是Kaniko的主要優勢:
無守護進程構建
Kaniko無需Docker守護進程即可獨立運行,這不僅節省了資源,還提高了構建效率。這種設計使得Kaniko在資源有限的環境中也能高效工作,特別是在Kubernetes集群或容器環境中。
安全隔離
Kaniko在單獨的容器中執行構建,與宿主機系統隔離,從而增強了安全性。這種隔離機制可以有效防止潛在的安全威脅影響到宿主系統。
構建速度快
Kaniko使用并行構建技術,顯著縮短了構建時間。這意味著用戶可以在短時間內完成復雜的構建任務,節省寶貴的時間。
高度可定制
Kaniko允許用戶定制構建過程,包括添加自定義腳本和修改構建環境,以滿足特定的需求。這種靈活性使得Kaniko能夠滿足不同項目和環境的多樣化需求。
易于集成
Kaniko可以輕松集成到CI/CD流水線中,實現自動構建和部署。這使得Kaniko成為持續集成和持續部署流程中的重要組成部分,提高了開發和部署的自動化水平。
在Kubernetes環境中的應用
Kaniko非常適合在Kubernetes環境中構建鏡像,可以無縫集成,簡化鏡像構建和部署。這種集成方式不僅提高了效率,還增加了系統的可靠性。
對云原生應用的支持
Kaniko是云原生應用鏡像構建的理想選擇,可快速構建和部署,滿足敏捷開發的要求。這種快速構建和部署的能力使得Kaniko在云原生應用的開發和運維中發揮著重要作用。
微服務架構的支持
Kaniko也適用于微服務架構的鏡像構建,可獨立構建每個微服務,實現高效的開發和部署。這種支持使得Kaniko在微服務架構的推廣和應用中具有重要意義。
未來前景
隨著云原生應用和微服務架構的發展,Kaniko的作用將越來越重要,成為構建容器鏡像的標準工具,為開發者提供更快速、更安全、更定制化的構建體驗。
綜上所述,Kaniko以其無守護進程構建、安全隔離、快速構建、高度可定制、易于集成等優勢,在容器鏡像構建領域占據了重要地位,特別是在云原生和微服務架構的應用中,Kaniko展現出了巨大的潛力和價值。
kaniko 使用命令常用參數
說一下 kaniko 的使用 ,參數 其實和docker build 類似,需要知道 上下文 context
,dockerfile
的位置信息, destination
構建鏡像完成后 要推送的地址 ,skip-tls-verify
跳過檢查 https 以及配置緩存相關的。
// 構建鏡像
def buildImage(Map config) {def kanikoCommand = '/usr/local/bin/executor ' +"--context dir://${config.WORKSPACE} " +"--dockerfile ${config.WORKSPACE}/Dockerfile " +"--destination ${config.IMAGE_TAG} " +'--skip-tls-verify ' +'--verbosity=info ' +'--cache=true ' +"--cache-repo ${config.DOCKER_REGISTRY}/cache-repo/${config.IMAGE_NAME} " +'--cache-ttl=168h ' +'--cache-dir=/kaniko/.cache 'echo "Building Docker image for ${config.envType} environment with Kaniko"try {echo "Current working directory: ${pwd()}"container('kaniko') {sh '''/usr/local/bin/executor version'''sh "${kanikoCommand}"}} catch (err) {error "Failed to build Docker image: ${err}"}
}
創建秘鑰
配置 kaniko
使用 Docker 注冊表憑證
kubectl create secret docker-registry regcred \--namespace=dev \--docker-server=172.19.89.106:12300 \--docker-username=admin \--docker-password=xxxxxxxxxx
這個命令是用來在 Kubernetes 集群中創建一個名為 regcred
的 Docker registry 類型的 Secret。這個 Secret 用于存儲訪問私有 Docker registry 所需的認證信息,以便 Kubernetes 能夠拉取私有倉庫中的鏡像。具體來說,該命令的作用如下:
-
kubectl create secret docker-registry regcred
: 使用kubectl
命令行工具創建一個類型為docker-registry
的 Secret。regcred
是這個 Secret 的名稱,您可以在需要引用此 Secret 時使用這個名字。 -
--namespace=dev
: 指定這個 Secret 應該被創建在哪個命名空間(namespace)中。這里指定的是dev
命名空間。Kubernetes 中的資源是按命名空間來組織的,這有助于對不同環境或團隊的資源進行隔離。 -
--docker-server=172.19.89.106:12300
: 指定 Docker registry 的服務器地址。在這個例子中,它是一個位于172.19.89.106
的私有 Docker registry,并且監聽著12300
端口。 -
--docker-username=admin
: 指定用于登錄 Docker registry 的用戶名。這里是admin
。 -
--docker-password=xxxxxxxxxx
: 指定與上述用戶名關聯的密碼。這里是xxxxxxxxxx
。
創建了這個 Secret 后,您可以在 Pod 或其他 Kubernetes 資源定義中引用它,以確保 Kubernetes 在嘗試從指定的 Docker registry 拉取鏡像時能夠提供正確的認證信息。例如,在 Pod 的定義中,您可以添加 imagePullSecrets
字段,并將 regcred
作為值之一,這樣 Kubernetes 就知道使用這個 Secret 來獲取必要的認證憑據。
kubectl get secret regcred -n dev -o yamlapiVersion: v1
data:.dockerconfigjson: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxp7InVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6ImFkbWluNzMxIiwiYXV0aCI6IllXUnRhVzQ2WVdSdGFXNDNNekU9In19fQ==
kind: Secret
metadata:creationTimestamp: "2025-04-10T06:29:02Z"name: regcrednamespace: devresourceVersion: "58990830"uid: f50b2868-1a0f-46e1-88c1-a8bc70c119b1
type: kubernetes.io/dockerconfigjson
在Pipeline 中的一個Stage 中
stage('Build CODE') {steps {script {String branchName = params.BRANCHString envType = params.ENVecho """\Build CODE:WORKSPACE: ${WORKSPACE}envType: ${envType}branchName: ${branchName}""".stripIndent()sh 'ls -al'/* groovylint-disable-next-line VariableTypeRequired */def String gitSha = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()def String currentDate = sh(returnStdout: true, script: 'date +%Y%m%d').trim()// 設置 IMAGE_TAG 環境變量 動態傳值/* groovylint-disable-next-line LineLength */env.IMAGE_TAG = "${DOCKER_REGISTRY}/${IMAGE_NAME}/${SHORT_NAME}:${branchName}-${currentDate}-${gitSha}"// 打印 image tagecho "current tag: ${env.IMAGE_TAG}"// 構建鏡像 和推送鏡像buildImage([envType: envType,branchName: branchName,WORKSPACE: WORKSPACE,IMAGE_TAG: env.IMAGE_TAG,DOCKER_REGISTRY: DOCKER_REGISTRY,IMAGE_NAME: IMAGE_NAME,SHORT_NAME: SHORT_NAME])}}
}
#!/bin/bash # 重新打tag
docker tag gcr.io/kaniko-project/executor:v1.23.0 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0docker login -uadmin -padmin-xxxxx-xxxx 172.19.89.106:12300docker push 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0
把官方鏡像 推送的私有倉庫。 這個鏡像 一旦開始啟動 就停止了,不像其他鏡像 可以一直常駐在前臺,可以理解為 一次性容器我想在容器起來之后 ,根據需要 創建我的容器鏡像 tag ,需要根據 時間或者分支名 組合生成,而不是傳入一個destination 定義固定的值。
比如上面的例子中:TAG 生成邏輯,當前分支,當前日志,以及git提交的hash值
/* groovylint-disable-next-line VariableTypeRequired */
def String gitSha = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
def String currentDate = sh(returnStdout: true, script: 'date +%Y%m%d').trim()// 設置 IMAGE_TAG 環境變量 動態傳值
/* groovylint-disable-next-line LineLength */
env.IMAGE_TAG = "${DOCKER_REGISTRY}/${IMAGE_NAME}/${SHORT_NAME}:${branchName}-${currentDate}-${gitSha}"
// 打印 image tag
echo "current tag: ${env.IMAGE_TAG}"
官方鏡像改造
我對官方的鏡像 進行了一點點改造
# 第一階段:使用 kaniko 構建應用
FROM 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0 AS builder
# FROM gcr.io/kaniko-project/executor:v1.23.0 AS builder# 第二階段:使用一個適合運行時環境的基礎鏡像
FROM alpine:latest# 安裝 busybox
RUN apk add --no-cache busybox# 創建一個符號鏈接,使得 /bin/sh 指向 busybox 的 sh
RUN ln -sf /bin/busybox /bin/sh# 從第一階段復制 /kaniko/executor 到第二階段
COPY --from=builder /kaniko/executor /usr/local/bin/executor
COPY --from=builder /kaniko/executor /kaniko/executor
# 將 /usr/local/bin 添加到 PATH(如果它還沒有在 PATH 中)
ENV PATH=/kaniko:/usr/local/bin:$PATH# 設置默認的 ENTRYPOINT 和 CMD
ENTRYPOINT ["/usr/local/bin/executor"]CMD ["--help"]
# 重新構建鏡像
docker build . -t 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0-v1# 保存鏡像 到文件
docker save -o executor-v1.tar 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0-v1# 加載鏡像
docker load -i executor.tar# 推送到 私有倉庫中
docker push 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0-v1
這樣的好處 這樣鏡像中有了 sh
環境, 在啟動容器的 時候, 我就可以通過
- name: kanikoimage: 172.19.89.106:12300/library/kaniko-project/executor:v1.23.0-v1imagePullPolicy: IfNotPresentcommand: ["/bin/sh"]args: ["-c", "tail -f /dev/null"] # 保持容器運行env:- name: DOCKER_CONFIG # 這個配置指向該卷來傳遞憑證value: /home/jenkins/agent/.dockervolumeMounts:- name: docker-configmountPath: /home/jenkins/agent/.docker- name: workspace-volumemountPath: /home/jenkins/agent- name: kaniko-cache # kaniko 緩存相關mountPath: /kaniko/.cachesecurityContext:runAsUser: 0resources:requests:memory: "1Gi" # 根據實際需求調整cpu: "500m" # 根據實際需求調整limits:memory: "2Gi"cpu: "1000m"volumes:- name: workspace-volumeemptyDir: {}- name: docker-configsecret:secretName: regcreditems:- key: .dockerconfigjsonpath: config.json- name: kaniko-cachepersistentVolumeClaim:claimName: kaniko-cache-pvc
把docker-config 掛載到 /home/jenkins/agent/.docker
這個目錄下面,kanio 就可以有權限來推送鏡像了。
通過tail -f
讓容器 一直運行狀態,之后 我需要執行構建鏡像的時候 傳入 鏡像的名稱即可。
六、Q&A : Kaniko 構建完成后 會自動推送鏡像嗎?
A: Kaniko
在構建完成后會自動推送鏡像到指定的 Docker 注冊表。這是 Kaniko
的一個關鍵特性之一,它不僅負責構建 Docker 鏡像,還會將構建好的鏡像推送到你指定的目標倉庫。
是的,Kaniko
在構建完成后會自動推送鏡像到指定的 Docker 注冊表。這是 Kaniko
的一個關鍵特性之一,它不僅負責構建 Docker 鏡像,還會將構建好的鏡像推送到你指定的目標倉庫。
具體工作原理
- 構建鏡像:
Kaniko
會讀取Dockerfile
并按照其中的指令逐層構建鏡像。 - 推送鏡像:在構建完成后,
Kaniko
會直接將構建好的鏡像推送到你通過--destination
參數指定的 Docker 注冊表。這個過程不需要與 Docker 守護進程交互,也不需要掛載 Docker Socket。
如何確保 Kaniko
推送鏡像
為了確保 Kaniko
能夠成功推送鏡像,你需要滿足以下條件:
-
提供有效的 Docker 注冊表憑證:
Kaniko
需要訪問 Docker 注冊表的權限,因此你必須提供有效的用戶名和密碼(或令牌)。這通常通過 Kubernetes Secret 來實現。- 在 Jenkins Pipeline 中,我們通過掛載
docker-config
卷并將DOCKER_CONFIG
環境變量指向該卷來傳遞憑證。
-
正確設置
--destination
參數:-
--destination
參數指定了目標鏡像的完整路徑,包括注冊表地址、倉庫名稱和標簽。例如:--destination 172.19.89.106:12300/zhiexa-web-official/web-official:master-20241210-fd880b6
-
確保這個路徑是正確的,并且你有權限推送鏡像到該位置。
-
-
網絡連接:
Kaniko
需要能夠訪問 Docker 注冊表的網絡地址。確保你的 Kubernetes 集群中的 Pod 可以訪問外部網絡或私有注冊表的地址。
示例命令
在 Jenkins Pipeline 中,Kaniko
的調用命令如下:
sh """/kaniko/executor \--context dir://${WORKSPACE} \--dockerfile ${WORKSPACE}/Dockerfile \--destination ${env.IMAGE_TAG} \--build-arg BUILD_ENV=${envType}
"""
在這個命令中:
--context
指定了構建上下文的路徑,通常是代碼庫的根目錄。--dockerfile
指定了Dockerfile
的路徑。--destination
指定了目標鏡像的完整路徑,Kaniko
會在構建完成后將鏡像推送到這個位置。--build-arg
用于傳遞構建時的參數(如BUILD_ENV
)。
驗證推送是否成功
你可以通過以下方式驗證 Kaniko
是否成功推送了鏡像:
-
日志輸出:
Kaniko
會在控制臺輸出詳細的構建和推送日志。如果你看到類似以下的日志,說明推送成功:Pushing layer sha256:xxxxxxxxxxxxx Pushing manifest for 172.19.89.106:12300/zhiexa-web-official/web-official:master-20241210-fd880b6 Successfully pushed 172.19.89.106:12300/zhiexa-web-official/web-official:master-20241210-fd880b6
-
檢查注冊表:你也可以手動登錄到 Docker 注冊表,查看是否已經有新鏡像被推送。
注意事項
- 私有注冊表:如果你使用的是私有 Docker 注冊表,確保你已經正確配置了憑證,并且
Kaniko
有權限推送鏡像。 - 防火墻和網絡策略:確保 Kubernetes 集群中的網絡策略允許 Pod 訪問 Docker 注冊表的地址。
- 鏡像標簽:確保你為鏡像設置了唯一的標簽(如基于 Git 提交哈希或構建時間),以避免覆蓋舊版本的鏡像。
總結
Kaniko
在構建完成后會自動推送鏡像到指定的 Docker 注冊表,而無需額外的推送步驟。只要確保提供了正確的憑證和網絡訪問權限,Kaniko
就可以順利完成構建和推送任務。Kaniko 是不需要 Docker Daemon
比較適合K8S 環境來構建鏡像。
參考文檔
kanio github