Kaniko:容器构建的灵活解决方案
- August 8, 2023
背景
在云原生时代,容器技术如Docker和容器编排系统如Kubernetes的广泛应用推动了应用程序交付的变革。容器镜像作为应用程序的构建和交付单元,成为了现代软件开发和部署的重要组成部分。而Docker作为目前最流行的容器技术之一,也提供了强大的构建工具Docker Build,用于构建Docker容器镜像。然而,Docker Build存在一些限制,特别是在某些场景下无法满足需求,例如在没有Docker守护进程的环境中进行构建。
为了解决这些限制和问题,Google开源了Kaniko项目,这是一个独立于Docker守护进程的容器镜像构建工具,它可以在任何支持OCI(Open Container Initiative)镜像格式的容器运行时中进行构建,而不需要特权或守护进程。
本文将介绍Kaniko的特点和优势,并演示如何在Kubernetes集群中使用Kaniko来构建容器镜像。
什么是Kaniko?
Kaniko 🔗是由Google开发的一个开源项目,它是一个容器镜像构建工具,可以在纯粹的用户态(user space)环境中执行容器镜像构建操作,无需Docker守护进程。Kaniko支持OCI镜像规范,并能够与任何OCI兼容的容器运行时(如containerd、CRI-O等)集成,从而实现容器镜像的构建和打包。Kaniko的主要优势在于:
- 无需Docker守护进程: 在Kaniko中,构建过程完全在容器中进行,无需依赖宿主机的Docker守护进程。这使得Kaniko能够在不支持Docker守护进程或需要限制特权的环境中运行,提高了构建的灵活性和安全性。
- OCI兼容: Kaniko遵循OCI(Open Container Initiative)镜像规范,能够构建符合OCI标准的容器镜像,与OCI兼容的容器运行时完美集成。
- 并行构建: Kaniko支持并行下载和构建镜像层,从而提高了构建效率,特别是在构建复杂多层镜像时表现更加出色。
- 镜像缓存: Kaniko支持镜像缓存,可以在多个构建之间重复使用相同的镜像层,从而减少构建时间和网络带宽消耗。
Kaniko的使用(docker环境)
步骤一:预拉取Kaniko镜像
Kaniko作为一个独立的容器镜像,可以直接从官方仓库中中获取。您可以通过以下命令来拉取Kaniko镜像:
docker pull gcr.io/kaniko-project/executor:latest
或者使用bitnami
仓库中的镜像 🔗
docker pull docker.io/bitnami/kaniko:latest
步骤二:创建Dockerfile
在使用Kaniko构建镜像之前,首先需要准备一个Dockerfile,用于定义镜像的构建过程。可以根据项目需求自定义Dockerfile,指定所需的基础镜像、软件包、依赖项和应用程序等信息。
例如,以下是一个简单的Dockerfile示例:
FROM alpine:latest
RUN apk --no-cache add curl
CMD ["curl", "https://www.example.com"]
步骤三:使用Kaniko构建镜像
接下来,使用Kaniko镜像来执行构建操作。使用Kaniko的主要命令是/kaniko/executor
,它接受一些参数来指定构建过程的详细信息。
在此之前请务必确保:
- 已经登陆
<registry>/<image>:<tag>
的镜像仓库,且登陆信息已经保存到$HOME/.docker/config.json
中 Dockerfile
文件保存在/root/lomtom
目录下
docker run --rm -v /root/lomtom:/workspace \
-v $HOME/.docker/:/kaniko/.docker \
gcr.io/kaniko-project/executor:latest \
--context=/workspace \
--destination=<registry>/<image>:<tag>
-v /root/lomtom:/workspace
:将本地工作目录映射到容器中的/workspace
目录,用于存放构建上下文和Dockerfile,在/root/lomtom
存放着上面的Dockerfile
-v $HOME/.docker/:/kaniko/.docker
: 将主机的 Docker 配置目录(通常在用户主目录下的~/.docker/
)挂载到容器的/kaniko/.docker
目录。这是为了让 Kaniko 容器能够访问 Docker Hub 或其他私有 Docker 镜像仓库所需的 Docker 认证信息。更多认证方式可查看Kaniko的认证方式--context=/workspace
:指定构建的上下文路径,这是Dockerfile中所有文件和目录的根目录。--destination=<registry>/<image>:<tag>
:指定构建完成后生成的镜像的目标位置,即镜像仓库和标签。
日志:
INFO[0000] Retrieving image manifest alpine:latest
INFO[0000] Retrieving image alpine:latest from registry index.docker.io
INFO[0005] Built cross stage deps: map[]
INFO[0005] Retrieving image manifest alpine:latest
INFO[0005] Returning cached image manifest
INFO[0005] Executing 0 build triggers
INFO[0005] Building stage 'alpine:latest' [idx: '0', base-idx: '-1']
INFO[0005] Unpacking rootfs as cmd RUN apk --no-cache add curl requires it.
INFO[0014] RUN apk --no-cache add curl
INFO[0014] Initializing snapshotter ...
INFO[0014] Taking snapshot of full filesystem...
INFO[0014] Cmd: /bin/sh
INFO[0014] Args: [-c apk --no-cache add curl]
INFO[0014] Running: [/bin/sh -c apk --no-cache add curl]
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz
(1/7) Installing ca-certificates (20230506-r0)
(2/7) Installing brotli-libs (1.0.9-r14)
(3/7) Installing libunistring (1.1-r1)
(4/7) Installing libidn2 (2.3.4-r1)
(5/7) Installing nghttp2-libs (1.55.1-r0)
(6/7) Installing libcurl (8.2.1-r0)
(7/7) Installing curl (8.2.1-r0)
Executing busybox-1.36.1-r0.trigger
Executing ca-certificates-20230506-r0.trigger
OK: 12 MiB in 22 packages
INFO[0281] Taking snapshot of full filesystem...
INFO[0281] CMD ["curl", "https://www.example.com"]
INFO[0281] Pushing image to swr.cn-east-3.myhuaweicloud.com/x-test/lomtom:temp2
INFO[0287] Pushed swr.cn-east-3.myhuaweicloud.com/x-test/lomtom@sha256:2aeea24cd05697ce824f0f5a909d23326fd6a3b8e3b83d56125c7d60a08b0d5a
步骤四:确认完成构建
运行上述命令后,Kaniko将根据Dockerfile和构建上下文中的信息来执行构建操作。构建完成后,将在指定的目标位置生成镜像,您可以通过docker pull
命令来获取该镜像。
docker pull <registry>/<image>:<tag>
至此,您已经成功使用Kaniko完成了容器镜像的构建。
Kaniko的使用(containerd.io环境)
步骤一:预拉取Kaniko镜像
containerd
和docker
一样也可以通过命令行工具拉取镜像。您可以通过以下命令来拉取Kaniko镜像:
ctr images pull gcr.io/kaniko-project/executor:latest
或者使用bitnami
仓库中的镜像 🔗
ctr images pull docker.io/bitnami/kaniko:latest
步骤二:创建Dockerfile
这里仍然以该Dockerfile为例:
FROM alpine:latest
RUN apk --no-cache add curl
CMD ["curl", "https://www.example.com"]
步骤三:使用Kaniko构建镜像
接下来,使用Kaniko镜像来执行构建操作。使用Kaniko的主要命令是/kaniko/executor
,它接受一些参数来指定构建过程的详细信息。
在此之前请务必确保:
- 已经登陆
<registry>/<image>:<tag>
的镜像仓库,且登陆信息已经保存到$HOME/.docker/config.json
中 Dockerfile
文件保存在/root/lomtom
目录下
ctr run --rm \
--net-host \
--mount type=bind,src=/root/lomtom,dst=/workspace,options=rbind:rw \
--mount type=bind,src=$HOME/.docker,dst=/kaniko/.docker,options=rbind:rw \
gcr.io/kaniko-project/executor:latest kaniko-executor \
/kaniko/executor \
--context=/workspace \
--destination=<registry>/<image>:<tag>
--net-host
: 使用宿主机的网络命名空间,这样容器将共享宿主机的网络栈,可以访问宿主机网络。--mount type=bind,src=/root/lomtom,dst=/workspace,options=rbind:rw
: 将宿主机上的/root/lomtom
目录绑定挂载到容器内部的/workspace
目录,并使用rbind
选项以递归方式将宿主机目录挂载到容器中,同时设置挂载的权限为读写。并且在/root/lomtom
存放着上面的Dockerfile
--mount type=bind,src=$HOME/.docker,dst=/kaniko/.docker,options=rbind:rw
: 将宿主机上的$HOME/.docker
目录绑定挂载到容器内部的/kaniko/.docker
目录,并使用rbind
选项以递归方式将宿主机目录挂载到容器中,同时设置挂载的权限为读写。这是为了让 Kaniko 容器能够访问 Docker Hub 或其他私有 Docker 镜像仓库所需的 Docker 认证信息。更多认证方式可查看Kaniko的认证方式/kaniko/executor
:在Containerd中,它并不会像Docker引擎那样直接执行容器镜像中的ENTRYPOINT
或CMD
指令,所以需要手动指定运行程序。--context=/workspace
:指定构建的上下文路径,这是Dockerfile中所有文件和目录的根目录。--destination=<registry>/<image>:<tag>
:指定构建完成后生成的镜像的目标位置,即镜像仓库和标签。
日志如下:
INFO[0000] Retrieving image manifest alpine:latest
INFO[0000] Retrieving image alpine:latest from registry index.docker.io
INFO[0006] Built cross stage deps: map[]
INFO[0006] Retrieving image manifest alpine:latest
INFO[0006] Returning cached image manifest
INFO[0006] Executing 0 build triggers
INFO[0006] Building stage 'alpine:latest' [idx: '0', base-idx: '-1']
INFO[0006] Unpacking rootfs as cmd RUN apk --no-cache add curl requires it.
INFO[0036] RUN apk --no-cache add curl
INFO[0036] Initializing snapshotter ...
INFO[0036] Taking snapshot of full filesystem...
INFO[0037] Cmd: /bin/sh
INFO[0037] Args: [-c apk --no-cache add curl]
INFO[0037] Running: [/bin/sh -c apk --no-cache add curl]
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz
(1/7) Installing ca-certificates (20230506-r0)
(2/7) Installing brotli-libs (1.0.9-r14)
(3/7) Installing libunistring (1.1-r1)
(4/7) Installing libidn2 (2.3.4-r1)
(5/7) Installing nghttp2-libs (1.55.1-r0)
(6/7) Installing libcurl (8.2.1-r0)
(7/7) Installing curl (8.2.1-r0)
Executing busybox-1.36.1-r0.trigger
Executing ca-certificates-20230506-r0.trigger
OK: 12 MiB in 22 packages
INFO[0216] Taking snapshot of full filesystem...
INFO[0216] CMD ["curl", "https://www.example.com"]
INFO[0216] Pushing image to swr.cn-east-3.myhuaweicloud.com/x-test/lomtom:temp1
INFO[0221] Pushed swr.cn-east-3.myhuaweicloud.com/x-test/lomtom@sha256:9fa700b62e0df7775afc474128a88790367c471066bc426f561f134b4359e186
步骤四:确认完成构建
运行上述命令后,Kaniko将根据Dockerfile和构建上下文中的信息来执行构建操作。构建完成后,将在指定的目标位置生成镜像,您可以通过ctr images pull
命令来获取该镜像。
ctr images pull <registry>/<image>:<tag>
至此,您已经成功使用Kaniko完成了容器镜像的构建。
Kaniko的认证方式
/kaniko/.docker
是Kaniko容器中的默认目录,用于存放Docker配置文件(config.json
),这个配置文件包含Docker客户端的配置和认证信息。Kaniko是一个独立于Docker守护进程的容器镜像构建工具,因此无法直接使用主机上的Docker配置文件。为了让Kaniko能够访问和认证私有的Docker镜像仓库,您需要将Docker配置文件挂载到Kaniko容器中的/kaniko/.docker
目录下。
默认情况下,Kaniko会在/kaniko/.docker
目录中查找名为config.json
的配置文件,以获取Docker客户端的配置信息。通过挂载这个配置文件,Kaniko可以获得登录私有Docker镜像仓库所需的认证凭据,从而能够安全地拉取和推送镜像。
Kaniko支持在不依赖Docker守护进程的情况下进行镜像构建,并且能够通过一些配置选项实现对Docker镜像仓库的认证。
以下是Kaniko进行认证的方法:
- Docker配置文件挂载:Kaniko支持将Docker配置文件挂载到容器内,从而实现对私有Docker镜像仓库的认证。您可以将包含身份验证凭据的Docker配置文件(
config.json
)挂载到Kaniko容器中的/kaniko/.docker
目录。这样Kaniko就可以使用挂载的配置文件来获取登录凭据,以访问私有仓库。
# 假设您的Docker配置文件位于 /root/.docker/config.json
docker run -v /root/.docker/config.json:/kaniko/.docker/config.json gcr.io/kaniko-project/executor:latest \
--dockerfile /root/Dockerfile \
--destination your-registry/your-image:tag \
--context /root/build/context
- Docker配置文件内容传递:您还可以将Docker配置文件的内容直接传递给Kaniko容器,而不是挂载整个文件。这可以通过将配置文件的内容作为环境变量传递给Kaniko容器来实现。
# 将Docker配置文件的内容传递给环境变量DOCKER_CONFIG,然后作为参数传递给Kaniko容器
DOCKER_CONFIG=$(cat /root/.docker/config.json)
docker run -e DOCKER_CONFIG=$DOCKER_CONFIG gcr.io/kaniko-project/executor:latest \
--dockerfile /root/Dockerfile \
--destination your-registry/your-image:tag \
--context /root/build/context
- 使用镜像凭据文件:除了Docker配置文件,Kaniko还支持直接将镜像凭据文件传递给容器,以实现认证。通过
--secret
参数指定镜像凭据文件的路径,Kaniko将使用该文件中的凭据信息进行认证。
# 假设您有一个名为 image-creds.json 的镜像凭据文件
docker run -v /root/image-creds.json:/kaniko/secret/image-creds.json gcr.io/kaniko-project/executor:latest \
--dockerfile /root/Dockerfile \
--destination your-registry/your-image:tag \
--context /root/build/context \
--secret /kaniko/secret/image-creds.json
无论使用哪种方式,都可以实现对私有Docker镜像仓库的认证,确保Kaniko可以成功访问和推送镜像到私有仓库。请注意,在使用这些认证方法时,请确保对Docker镜像仓库的访问凭据是安全的,并且不会暴露敏感信息。
Kaniko缓存
Kaniko 提供了两种缓存模式:--cache
和 --cache-repo
。
--cache
缓存模式:该缓存模式用于在本地构建或与 Kubernetes 等环境结合构建时使用。在这个模式下,Kaniko 会尝试从之前构建的镜像中提取缓存。如果构建的上下文和 Dockerfile 没有发生变化,它会使用缓存来加速构建过程。但请注意,由于缓存是保存在本地的,因此在不同的机器或环境中构建时,缓存无法共享。--cache-repo
缓存模式:缓存模式适用于 CI/CD 等场景,可以在多个构建之间共享缓存,提高构建效率。在这个模式下,Kaniko 会将中间镜像层保存到指定的镜像仓库中。后续的构建任务可以从该仓库中提取缓存,避免重复构建相同的层。
使用缓存模式的命令示例:
# 使用 cache 缓存模式
docker run --rm \
-v /root/lomtom:/workspace \
-v /root/docker/config.json:/kaniko/.docker/config.json \
gcr.io/kaniko-project/executor:latest \
--context=/workspace \
--cache=true \
--cache-ttl=48h \
--destination=docker.io/<username>/<image>:<tag>
# 使用 cache-repo 缓存模式
docker run --rm \
-v /root/lomtom:/workspace \
-v /root/docker/config.json:/kaniko/.docker/config.json \
gcr.io/kaniko-project/executor:latest \
--context=/workspace \
--cache-repo=<cache-repo>/<image> \
--destination=docker.io/<username>/<image>:<tag>
其中,--cache=true
表示启用缓存模式,--cache-ttl
可以设置缓存的有效期,--cache-repo
指定了缓存镜像的仓库地址。
Kaniko Vs Docker
Kaniko和Docker都是用于构建容器镜像的工具,但它们之间有一些重要的区别:
- 构建环境:
- Docker:Docker构建镜像时,需要在主机上安装Docker引擎,因为Docker构建命令直接依赖于Docker引擎。
- Kaniko:Kaniko是一个独立的容器镜像构建工具,它不依赖于Docker引擎或其他容器运行时。Kaniko可以在不同的容器环境中使用,包括不安装Docker引擎的环境。
- 构建过程:
- Docker:Docker构建镜像时,将Dockerfile中的指令发送给Docker引擎,Docker引擎负责执行这些指令来构建镜像。构建过程在主机上执行,可能涉及到主机的权限和资源。
- Kaniko:Kaniko将Dockerfile中的指令解析并在一个独立的容器中执行构建过程,而不是在主机上执行。这意味着Kaniko构建是隔离的,不需要主机的特权或访问主机的资源。
- 权限和安全性:
- Docker:Docker构建镜像时,如果用户拥有足够的权限,可以访问主机上的文件和资源。这可能带来一些安全风险,特别是在CI/CD等自动化环境中。
- Kaniko:Kaniko的构建过程在一个独立的容器中进行,不需要访问主机的文件和资源。这增加了构建的安全性,因为构建过程受到隔离并且不会访问主机的敏感信息。
- 镜像获取:
- Docker:Docker构建完成后,镜像直接存储在Docker引擎中,并可以通过Docker命令进行访问和使用。
- Kaniko:Kaniko构建完成后,镜像保存在指定的目标位置,例如Docker仓库或其他容器镜像仓库。您可以通过容器运行时(如Docker或Containerd.io)来获取和使用构建好的镜像。
- 构建上下文:
- Docker:Docker构建镜像时,需要指定构建上下文,该上下文包含Dockerfile和构建所需的所有文件和目录。构建上下文会被发送到Docker引擎。
- Kaniko:Kaniko构建镜像时,也需要指定构建上下文,但是该上下文是在Kaniko容器中解析和使用的,而不是发送给主机。
总的来说,Kaniko相比于Docker在构建容器镜像时更加灵活和安全,尤其适用于CI/CD等自动化环境,可以避免Docker引擎的直接访问和隔离性的问题。同时,Kaniko的独立性使得它可以在各种容器环境中使用,而不受Docker引擎的限制。
拓展
除了kaniko,社区中目前还有其他两款款工具可以支持无 docker daemon 化的构建:img 和 buildkit。
- img:
img 🔗 是一个开源的、基于 Go 编写的命令行工具,用于在不需要 Docker 守护进程的情况下构建和管理 Docker 镜像。它旨在提供一个轻量级的替代方案,允许用户在不启动 Docker 守护进程的情况下执行镜像构建、层缓存和推送等操作。img 使用的是 BuildKit 的内部 API 来执行这些操作。
特点:
- 无需 Docker 守护进程(Daemonless 模式): img 不需要运行 Docker 守护进程,这使得它更轻量、更适合 CI/CD 等环境。
- 高效构建: 借助 BuildKit,img 可以执行高效的、并发的层缓存和构建操作。
- OCI 镜像支持: img 可以构建符合 OCI(Open Container Initiative)标准的镜像,这些镜像可以被任何 OCI 兼容的容器运行时所使用。
- BuildKit:
BuildKit 🔗 是 Docker 社区开发的一个高级镜像构建工具,它主要用于优化 Docker 镜像构建过程的性能和可维护性。BuildKit 可以使用 Docker CLI 或 Docker API 进行交互。与传统的 Docker 镜像构建方式相比,BuildKit 提供了更高的并发性、更好的缓存机制以及更高级的构建选项。
特点:
- 并发构建: BuildKit 支持并发构建,可以在同一时间构建多个层,加速构建过程。
- 缓存优化: BuildKit 的缓存机制更加智能,可以根据命令的不同部分进行缓存,从而提高层的重用率。
- 无需 Docker 守护进程(Daemonless 模式): BuildKit 支持 Daemonless 模式,允许在不需要 Docker 守护进程的情况下构建镜像,提高效率。
- 多平台构建: BuildKit 支持构建多个平台的镜像,例如 x86、ARM 等,可以在不同的体系结构上运行。
img 是一个特定于镜像构建的命令行工具,旨在提供一种无需 Docker 守护进程的构建方案,使用 BuildKit 的内部 API 进行操作。而 BuildKit 则是一个更为通用的镜像构建引擎,为 Docker 提供了高级的构建功能,包括高并发构建、智能缓存等。两者都旨在提升 Docker 镜像构建的性能和效率。
结论
Kaniko是一个强大的容器镜像构建工具,它在不依赖Docker守护进程的情况下实现了容器镜像的构建,具有较高的灵活性和安全性。通过并行构建和镜像缓存等特性,Kaniko能够高效地构建复杂的容器镜像,适用于各种构建场景和需求。因此,对于需要在Kubernetes集群中进行容器镜像构建的项目,Kaniko是一个值得考虑的优秀选择。您可以尝试使用Kaniko来提高构建效率并提升开发体验。