饮墨

子安饮墨馀三斗,留与卿儿作赋来

Kaniko:在 Kubernetes 中构建容器镜像,彻底告别 Docker Daemon

痛点

在 CI/CD 流水线中构建容器镜像,传统方案是 Docker-in-Docker(DinD)或挂载宿主机 Docker Socket。两种做法都有硬伤:

  • DinD:需要 --privileged 特权模式,Pod 拥有宿主机几乎所有权限,一旦被攻破等同于节点沦陷
  • 挂载 Socket:任何能访问 /var/run/docker.sock 的容器都能操控宿主机上所有容器,安全审计直接红灯
  • Kubernetes 1.24+ 已移除 dockershim:节点上可能根本没有 Docker Daemon,containerd/CRI-O 环境下 DinD 方案失效

生产环境需要一种 无特权、无 Daemon、纯用户态 的镜像构建方案。Kaniko 正是 Google 为此场景开源的工具。

方案

Kaniko 在容器内部以普通用户进程方式解析 Dockerfile,逐层构建镜像并推送到 Registry,全程 不依赖 Docker Daemon,不需要特权模式

核心优势:

对比项 DinD Socket 挂载 Kaniko
需要特权模式 ❌(但等效危险)
依赖 Docker Daemon
containerd 环境兼容
安全隔离
缓存支持 本地 本地 Registry 层级缓存

实操步骤

Step 1:准备 Registry 认证 Secret

Kaniko 构建完成后需要推送镜像到 Registry(Docker Hub、Harbor、ECR、GCR 等),先创建认证凭据:

# Docker Hub 示例
kubectl create secret docker-registry kaniko-secret \
  --docker-server=https://index.docker.io/v1/ \
  --docker-username=<your-user> \
  --docker-password=<your-token> \
  --docker-email=<your-email> \
  -n ci

# AWS ECR 示例(使用 ecr-credential-helper 更优,此处给基础方案)
TOKEN=$(aws ecr get-login-password --region ap-northeast-1)
kubectl create secret docker-registry kaniko-ecr \
  --docker-server=123456789.dkr.ecr.ap-northeast-1.amazonaws.com \
  --docker-username=AWS \
  --docker-password="$TOKEN" \
  -n ci

Step 2:编写 Kaniko 构建 Pod

最简单的用法——一个一次性 Pod 完成构建+推送:

# kaniko-build-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-build
  namespace: ci
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--dockerfile=Dockerfile"
    - "--context=git://github.com/your-org/your-app.git#refs/heads/main"
    - "--destination=your-registry.com/your-app:v1.2.3"
    - "--cache=true"
    - "--cache-repo=your-registry.com/your-app/cache"
    - "--snapshot-mode=redo"
    - "--push-retry=3"
    volumeMounts:
    - name: kaniko-secret
      mountPath: /kaniko/.docker
  volumes:
  - name: kaniko-secret
    secret:
      secretName: kaniko-secret
      items:
      - key: .dockerconfigjson
        path: config.json
  restartPolicy: Never

关键参数说明: - --context:支持 git://s3://gs:// 以及本地路径 - --cache=true:启用 Registry 层级缓存,重复构建速度提升 50%-80% - --snapshot-mode=redo:比默认 full 模式更快,适合大多数场景 - --push-retry:网络抖动时自动重试推送

kubectl apply -f kaniko-build-pod.yaml
kubectl logs -f kaniko-build -n ci

Step 3:集成到 CI/CD Pipeline(GitLab CI 示例)

# .gitlab-ci.yml
build_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - |
      cat > /kaniko/.docker/config.json << EOF
      {
        "auths": {
          "${CI_REGISTRY}": {
            "auth": "$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)"
          }
        }
      }
      EOF
    - >
      /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
      --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
      --destination "${CI_REGISTRY_IMAGE}:latest"
      --cache=true
      --cache-repo="${CI_REGISTRY_IMAGE}/cache"
  only:
    - main
    - tags

注意:使用 executor:debug 镜像(带 shell),而非 executor:latest(无 shell,只能用 args)。

Step 4:在 Tekton/Argo Workflows 中使用

# Tekton Task 片段
steps:
- name: build-and-push
  image: gcr.io/kaniko-project/executor:latest
  command:
  - /kaniko/executor
  args:
  - --dockerfile=$(params.dockerfile)
  - --context=$(workspaces.source.path)
  - --destination=$(params.image):$(params.tag)
  - --cache=true
  - --compressed-caching=false
  - --use-new-run

避坑指南

1. 构建上下文过大导致 OOM

Kaniko 会将整个 context 加载到内存。如果项目目录含大文件(日志、数据集),Pod 直接 OOMKilled。

解决方案

# .dockerignore(必须配置)
.git
node_modules
*.log
data/
tests/

同时给 Pod 设合理的 resources.limits.memory,一般 2-4Gi 可覆盖大部分项目。

2. 多阶段构建缓存失效

Kaniko 的 --cache 只缓存最终镜像的层。多阶段构建(multi-stage)中间阶段默认不缓存。

解决方案

/kaniko/executor \
  --dockerfile=Dockerfile \
  --destination=registry.com/app:latest \
  --cache=true \
  --cache-repo=registry.com/app/cache \
  --cache-copy-layers    # 缓存所有中间层

3. ECR Token 12 小时过期

AWS ECR 的认证 Token 有效期仅 12 小时,长时间运行的 CI 环境中 Secret 会失效。

解决方案:使用 ecr-credential-helper 或配合 CronJob 定时刷新 Secret:

# refresh-ecr-secret.sh(CronJob 每 6 小时执行)
#!/bin/bash
TOKEN=$(aws ecr get-login-password --region ap-northeast-1)
kubectl create secret docker-registry kaniko-ecr \
  --docker-server=123456789.dkr.ecr.ap-northeast-1.amazonaws.com \
  --docker-username=AWS \
  --docker-password="$TOKEN" \
  -n ci \
  --dry-run=client -o yaml | kubectl apply -f -

性能优化技巧

  1. Registry 缓存 + --compressed-caching=false:牺牲少量存储空间换取缓存读取速度提升 30%+
  2. --use-new-run:使用优化后的文件系统快照算法,大项目构建提速 20%
  3. --single-snapshot:如果 Dockerfile 最后一层是大量文件复制,可合并快照减少开销
  4. 并行构建:多微服务场景下,每个服务启一个 Kaniko Pod 并行构建,充分利用集群资源

总结

Kaniko 解决了 Kubernetes 环境中容器镜像构建的安全和兼容性问题:

  • 安全:无特权、无 Daemon,满足 PSA(Pod Security Admission)restricted 策略
  • 兼容:不依赖 Docker,在 containerd/CRI-O 节点上开箱即用
  • 高效:Registry 层级缓存 + 快照优化,生产环境构建速度可接受
  • 集成简单:支持所有主流 CI 系统(GitLab CI、GitHub Actions、Tekton、Argo Workflows、Jenkins)

如果你的集群已经升级到 Kubernetes 1.24+ 且启用了 Pod Security Standards,Kaniko 几乎是唯一的无痛镜像构建方案。建议配合 --cache + .dockerignore 优化后直接替换现有的 DinD 流水线。

您还没有登录,请登录后发表评论。