生产环境 DevOps 实战 | 第 15 天:使用 kaniko 在 Tekton 中构建容器镜像(无需 Docker daemon)

第 15/60 天

引言

在 Kubernetes 原生 CI/CD 流水线中,构建容器镜像是一个核心环节。传统方式依赖 Docker daemon(需 docker build 命令),但在 Kubernetes 集群中运行 Docker daemon 存在诸多问题:

  • 特权模式风险:Docker-in-Docker(DinD)需要 privileged 容器,带来严重安全隐患
  • 存储开销:每个构建节点都需要完整的 Docker 镜像缓存
  • 调度复杂:DaemonSet 部署模式占用集群资源,且与 Kubernetes 调度器存在竞争

kaniko 应运而生。它是 Google 开源的容器镜像构建工具,无需 Docker daemon,完全在用户空间运行,以非 root 方式构建镜像,天然适合 Kubernetes 和 Tekton 环境。

本文将从安装配置、YAML 编写、Harbor 集成、缓存优化、踩坑排除五个维度,手把手带你掌握 kaniko + Tekton 的镜像构建最佳实践。

核心概念

kaniko 的工作原理

组件 说明
Executor 核心构建器,解析 Dockerfile、逐层构建、推送镜像
Warmer 预热缓存器,提前拉取基础镜像加速构建
缓存机制 支持 registry 缓存和本地缓存两种模式

kaniko 的工作原理不依赖任何容器运行时 daemon,它通过以下方式实现镜像构建:

  1. 解析 Dockerfile:逐条解析 FROMRUNCOPY 等指令
  2. 逐层构建:在用户空间执行每条指令,生成新的文件系统快照
  3. 快照比对:利用 fsutilproc 文件系统比对文件变更
  4. 层提交:将变更打包为 OCI 镜像层,推送到远程仓库

为什么选择 kaniko 而非 DinD

对比项 kaniko DinD (Docker-in-Docker)
特权模式 不需要 需要 --privileged
安全风险 低(非 root 运行) 高(宿主节点 root 权限)
Kubernetes 原生 天然支持 需额外配置容忍
缓存支持 Registry 缓存、本地缓存 Docker 层缓存
多架构构建 支持 --platform 支持
镜像大小 ~50MB ~200MB+

实战步骤

1. 准备 kaniko 镜像

kaniko 官方提供 gcr.io 镜像,但国内环境建议使用镜像站或自建代理。推荐使用 gcr.io/kaniko-project/executor:v1.23.2 镜像。

注意:国内用户可将 gcr.io 替换为 gcr.dockerproxy.com 或阿里云镜像加速。

2. 创建 Kubernetes Secret 用于 Harbor 认证

在 Tekton 中构建并推送镜像到 Harbor(或任何私有仓库),需要先创建 Docker Registry 凭据:

apiVersion: v1
kind: Secret
metadata:
  name: harbor-registry-cred
  namespace: tekton-pipelines
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded-docker-config>

创建命令:

# 使用 robot account 创建凭据
kubectl create secret docker-registry harbor-registry-cred 
  --docker-server=harbor.stellardata.top 
  --docker-username=robot$tekton-builder 
  --docker-password=<robot-account-token> 
  -n tekton-pipelines

关于 Harbor Robot Account 的详细配置,请参考第 21 天文章。

3. 编写 Tekton Task:kaniko 构建任务

以下是完整的 kaniko 构建 Task 定义:

apiVersion: tekton.dev/v1
kind: Task
metadata:
  name: kaniko-build
  namespace: tekton-pipelines
spec:
  description: >-
    Build container image using kaniko (rootless) and push to Harbor registry.
  params:
    - name: IMAGE
      description: Full image name including registry and tag
      type: string
      example: harbor.stellardata.top/library/my-app:v1.0.0
    - name: DOCKERFILE
      description: Path to Dockerfile within the source workspace
      type: string
      default: Dockerfile
    - name: CONTEXT
      description: Build context directory within the workspace
      type: string
      default: .
    - name: EXTRA_ARGS
      description: Additional kaniko flags
      type: string
      default: ""
    - name: CACHE_REPO
      description: Remote repository to cache intermediate layers
      type: string
      default: harbor.stellardata.top/library/cache
  workspaces:
    - name: source
      description: Git repository source code
  results:
    - name: IMAGE_DIGEST
      description: Digest of the built image
  steps:
    - name: build-and-push
      image: gcr.io/kaniko-project/executor:v1.23.2
      args:
        - --dockerfile=$(params.DOCKERFILE)
        - --context=$(workspaces.source.path)/$(params.CONTEXT)
        - --destination=$(params.IMAGE)
        - --cache=true
        - --cache-repo=$(params.CACHE_REPO)
        - --registry-mirror=harbor.stellardata.top/mirror
        - --skip-tls-verify=false
        - --verbosity=info
        - $(params.EXTRA_ARGS)
      env:
        - name: DOCKER_CONFIG
          value: /builder/home/.docker
      volumeMounts:
        - name: docker-config
          mountPath: /builder/home/.docker
  volumes:
    - name: docker-config
      secret:
        secretName: harbor-registry-cred
        items:
          - key: .dockerconfigjson
            path: config.json

4. 创建 Pipeline:从源码到镜像

将 git clone 与 kaniko 构建串联成完整流水线:

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
  name: gitops-build-pipeline
  namespace: tekton-pipelines
spec:
  params:
    - name: GIT_REPO
      type: string
      description: Git repository URL
    - name: GIT_REVISION
      type: string
      description: Git revision (branch/tag/commit)
      default: main
    - name: IMAGE_NAME
      type: string
      description: Image name (without tag)
    - name: IMAGE_TAG
      type: string
      description: Image tag (e.g., v1.0.0 or commit-sha)
      default: latest
  workspaces:
    - name: shared-workspace
      description: Workspace shared between tasks
  tasks:
    - name: fetch-source
      taskRef:
        resolver: git
        params:
          - name: url
            value: https://github.com/tektoncd/catalog.git
          - name: path
            value: task/git-clone/0.9/git-clone.yaml
          - name: revision
            value: main
      params:
        - name: url
          value: $(params.GIT_REPO)
        - name: revision
          value: $(params.GIT_REVISION)
      workspaces:
        - name: output
          workspace: shared-workspace

    - name: build-image
      taskRef:
        name: kaniko-build
      runAfter:
        - fetch-source
      params:
        - name: IMAGE
          value: "$(params.IMAGE_NAME):$(params.IMAGE_TAG)"
      workspaces:
        - name: source
          workspace: shared-workspace

5. 运行 PipelineRun

apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
  name: my-service-build-run
  namespace: tekton-pipelines
spec:
  pipelineRef:
    name: gitops-build-pipeline
  params:
    - name: GIT_REPO
      value: https://git.stellardata.top/devops/my-service.git
    - name: GIT_REVISION
      value: main
    - name: IMAGE_NAME
      value: harbor.stellardata.top/devops/my-service
    - name: IMAGE_TAG
      value: v1.0.0-20260826
  workspaces:
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 5Gi

使用 tkn CLI 启动流水线:

# 创建 PipelineRun
tkn pipeline start gitops-build-pipeline 
  -p GIT_REPO=https://git.stellardata.top/devops/my-service.git 
  -p GIT_REVISION=main 
  -p IMAGE_NAME=harbor.stellardata.top/devops/my-service 
  -p IMAGE_TAG=v1.0.0-$(date +%Y%m%d) 
  -w name=shared-workspace,claimName=build-workspace-pvc 
  --showlog

# 查看构建日志
tkn pipelinerun logs my-service-build-run -f

6. 编写 Dockerfile 优化建议

kaniko 构建同样遵循 Docker 最佳实践,但有一些特殊优化点:

# 多阶段构建示例
# 第一阶段:编译
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .

# 第二阶段:运行(注意 kaniko 下使用最小基础镜像)
FROM alpine:3.20
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
USER 1000:1000
ENTRYPOINT ["/server"]

对于 kaniko,建议使用 alpinedistroless 基础镜像,避免在构建过程中生成大量临时文件,减少镜像层数。

7. 缓存配置优化

# 启用缓存时的参数说明
# --cache=true                          # 开启缓存
# --cache-repo=...                      # 缓存仓库地址
# --cache-ttl=240h                      # 缓存有效期(默认 2 周)
# --snapshot-mode=time                  # 快照模式:time(更快)或 full(更准确)
# --compression=gzip                    # 压缩方式(默认 gzip,可选 zstd)
# --use-new-run=/kaniko/executor       # 使用新的 RUN 指令处理

# 预热缓存:在流水线前预先拉取基础镜像
docker run gcr.io/kaniko-project/warmer:v1.23.2 
  --image=golang:1.22-alpine 
  --image=alpine:3.20 
  --cache-dir=/cache

常见问题

Q1: kaniko 构建时出现 no space left on device

原因:kaniko 在 /tmp/kaniko 目录构建临时文件,默认 PVC 空间不足。

解决
– 确保 workspace 的 PVC 有足够空间(建议 5GB+)
– 在 Task 中设置 --cache-dir 到 workspace 挂载目录
– 清理旧构建缓存

# 在 Task 中添加临时目录挂载
workspaces:
  - name: kaniko-cache
    mountPath: /kaniko/.cache

Q2: 连接 Harbor 时出现 x509: certificate signed by unknown authority

原因:自签名证书未加入 kaniko 信任列表。

解决:通过 ConfigMap 挂载自定义 CA 证书:

# 创建 ConfigMap
kubectl create configmap custom-ca 
  --from-file=harbor-ca.crt=/path/to/harbor-ca.crt 
  -n tekton-pipelines

# 在 Task 中挂载
volumeMounts:
  - name: custom-ca
    mountPath: /kaniko/ssl/certs/ca-certificates.crt
    subPath: harbor-ca.crt

Q3: kaniko 构建速度慢,没有缓存命中

原因:大多数 Dockerfile 指令变更导致缓存失效,或缓存仓库配置不正确。

解决
– 将 COPY 指令放在 RUN apt-get 之后,最大化缓存命中
– 使用 .dockerignore 排除不必要的文件
– 检查 --cache-repo 的可访问性
– 考虑使用 --snapshot-mode=time 加快快照比对

Q4: 如何构建多架构镜像(arm64 + amd64)?

解决:kaniko 支持通过 --platform 参数构建多架构镜像:

# 并行构建不同架构
- name: build-amd64
  args:
    - --destination=harbor.stellardata.top/app/my-service:linux-amd64
    - --platform=linux/amd64

- name: build-arm64
  args:
    - --destination=harbor.stellardata.top/app/my-service:linux-arm64
    - --platform=linux/arm64

Q5: 构建 Java 应用时 kaniko 退出代码 137 被 OOM Kill

原因:Maven/Gradle 编译消耗大量内存,kaniko 默认内存限制不足。

解决
– 增大 Task 的资源限制
– 使用多阶段构建,编译阶段在专门的高内存节点运行

resources:
  requests:
    memory: 2Gi
    cpu: 1000m
  limits:
    memory: 4Gi
    cpu: 2000m

总结

本文从零到一实现了 kaniko + Tekton 的容器镜像构建流水线,核心要点如下:

  1. kaniko 是 Kubernetes 原生镜像构建的最佳选择,无需 Docker daemon,安全且高效
  2. Tekton Task 封装使得 kaniko 构建参数化、可复用,配合 Pipeline 与 Git clone 串联形成完整 CI 流程
  3. Harbor 集成通过 dockerconfigjson Secret 和 Robot Account 实现自动化认证,安全合规
  4. 缓存优化是 kaniko 生产化部署的关键,合理配置 --cache-repo 和构建顺序可大幅提升速度
  5. 多架构构建能力让 kaniko 在云原生异构环境中同样适用

下一篇文章将深入 Harbor 的安装与 HTTPS 配置,敬请期待第 16 天。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容