移动测试开发 消除对特权账户的依赖使用 Kaniko 构建镜像
前言
Kaniko 项目最初于 2018 年由谷歌提出。Kaniko 的创建之初是寻求在执行容器镜像构建时消除对特权账户的依赖。无特权的容器镜像构建是注重安全性的公司最需要的功能之一。这与在 Kubernetes 集群中构建容器镜像类似。
在了解如何使用 Kaniko 构建镜像之前,我们先了解一下几种构建镜像的方式。
docker 构建镜像
docker 构建镜像是常用的方法,在具备构建容器镜像所需要的两个要素(Dockerfile 和上下文)的前提下,用下命令就能构建一个容器镜像出来。
docker build -t your_registry/your_repository:tag
然后用 docker push 将镜像推送到镜像仓库。
docker push your_registry/your_repository:tag
容器内构建镜像
现在 DevOps 的 CI/CD 环境大多数都会运行在容器内,镜像的构成也是在容器内完成的。这时候,通常用以下两种方式来完成容器内构建镜像的工作:
docker run -it -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/kaniko:/tmp/kaniko docker
- 挂载宿主机的 socket 文件到容器内部,然后在容器内部用 docker build 构建镜像
$ docker build -t dllhb/kaniko-test:v0.1 .
Sending build context to Docker daemon 5.632kB
Step 1/4 : FROM alpine:latest
latest: Pulling from library/alpine
89d9c30c1d48: Already exists
Digest: sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a
Status: Downloaded newer image for alpine:latest
---> 965ea09ff2eb
Step 2/4 : MAINTAINER <devops008@sina.com xiaomage>
---> Running in 8a2b1dc13d6b
Removing intermediate container 8a2b1dc13d6b
---> bd535532278d
Step 3/4 : RUN apk add busybox-extras curl
---> Running in fc254ad3d088
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.10/community/x86_64/APKINDEX.tar.gz
(1/5) Installing busybox-extras (1.30.1-r3)
Executing busybox-extras-1.30.1-r3.post-install
(2/5) Installing ca-certificates (20190108-r0)
(3/5) Installing nghttp2-libs (1.39.2-r0)
(4/5) Installing libcurl (7.66.0-r0)
(5/5) Installing curl (7.66.0-r0)
Executing busybox-1.30.1-r2.trigger
Executing ca-certificates-20190108-r0.trigger
OK: 7 MiB in 19 packages
Removing intermediate container fc254ad3d088
---> 1bbe81600a67
Step 4/4 : CMD ["echo","Hello DevOps"]
---> Running in 4b92a6a4b37e
Removing intermediate container 4b92a6a4b37e
---> de712b8cd7e5
Successfully built de712b8cd7e5
Successfully tagged dllhb/kaniko-test:v0.1Sending build context to Docker daemon 5.632kB
Step 1/4 : FROM alpine:latest
latest: Pulling from library/alpine
89d9c30c1d48: Already exists
Digest: sha256:c19173c5ada610a5989151111163d28a67368362762534d8a8121ce95cf2bd5a
Status: Downloaded newer image for alpine:latest
---> 965ea09ff2eb
Step 2/4 : MAINTAINER <devops008@sina.com xiaomage>
---> Running in 8a2b1dc13d6b
Removing intermediate container 8a2b1dc13d6b
---> bd535532278d
Step 3/4 : RUN apk add busybox-extras curl
---> Running in fc254ad3d088
由于 docker 依赖于 docker daemon 进程, docker daemon 进程是一个 Unixsocket 连接,且 /var/run/docker.sock 文件是 root 权限,
$ ls -ltr /var/run/docker.sock
lrwxr-xr-x 1 root daemon 69 Nov 26 15:13 /var/run/docker.sock
说明只有 root 权限才能访问 docker daemon 进程,在 docker daemon 无法暴露或者用户没有权限获取 docker daemon 进程的前提下,用 docker build 来构建镜像就变的非常困难了。
可能我们会想,docker build 镜像无非就是需要 docker 命令能运行成功,只要在容器里面安装一个 docker 不就成功了吗?这也就是下面讲的第二种方法。
- dind(docker-in-docker) 这种方式不需要挂载宿主机的 socket 文件,但是需要以 --privileged 权限来以 dind 镜像创建一个容器:
$ docker run --rm -it --privileged docker:18.06-dind
然后在容器里面构建容器镜像并推送至远端仓库。
dind 能够满足构建容器镜像的需求,但是从上面的命令看,有一个参数:–privileged 。意味着这个容器具有一些特权,他可能会看到宿主机上的一些设备,而且能够执行 mount 命令。
dind 还有很多问题,只是方便 docker 开发人员来测试 docker,所以官方也说 running Docker inside Docker is generally not recommended。
上述两种方法,都能满足在容器内构建容器镜像且推送镜像至远端仓库的需求,但是从 security 角度来讲,需要 root 权限 (第一种方式),提供特权 (第二种方式) 都使得风险增大,在 Kubernetes 多租户的场景下,这种风险是不能接受的。那是否有一种不需要特殊权限,还能快速构建容器镜像的方法呢?答案就是下面讲的 Kaniko。
Kaniko 介绍
Kaniko 是谷歌开源的一款用来构建容器镜像的工具。与 docker 不同,Kaniko 并不依赖于 Docker daemon 进程,完全是在用户空间根据 Dockerfile 的内容逐行执行命令来构建镜像,这就使得在一些无法获取 docker daemon 进程的环境下也能够构建镜像,比如在标准的 Kubernetes Cluster 上。
kaniko 以容器镜像的方式来运行的,同时需要三个参数: Dockerfile、上下文、以及远端镜像仓库的地址
Kaniko 会先提取基础镜像 (Dockerfile FROM 之后的镜像) 的文件系统,然后根据 Dockerfile 中所描述的,一条条执行命令,每一条命令执行完以后会在用户空间下面创建一个 snapshot,并与存储与内存中的上一个状态进行比对,如果有变化,就将新的修改生成一个镜像层添加在基础镜像上,并且将相关的修改信息写入镜像元数据中。等所有命令执行完,kaniko 会将最终镜像推送到指定的远端镜像仓库。
- Kaniko Demo
这里选择的是 https://github.com/traefik/whoami 和 https://github.com/peishunwu/kaniko项目用来测试。访问该服务之后,接口会返回访。
对项目的要求是,通过 Dockerfile 能够直接编译得到镜像。一共有两个验证点:
① 能够构建镜像
② 构建的镜像能够运行
- 在 Docker 上运行 Kaniko 生成推送镜像的凭证 以下说明: YOUR_USERNAME(我的 dockerhub 账户) YOUR_PASSWORD(我的 dockerhub 账户密码)
执行以下命令:
export AUTH=$(echo -n YOUR_USERNAME:YOUR_PASSWORD | base64 )
cat > config.json <<-EOF
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "${AUTH}"
}
}
}
EOF
构建镜像
docker run \
--interactive -v `pwd`/config.json:/kaniko/.docker/config.json gcr.io/kaniko-project/executor:latest \
--context git://github.com/traefik/whoami \
--dockerfile Dockerfile \
--destination=xxx/kaniko-demo:v1
参数说明:
context:构建需要的上下文。支持多种格式,S3、本地目录、标准输入、Git 仓库等
dockerfile:Dockerfile 路径
destination:构建后推送的镜像地址
其中–destination=xxx/kaniko-demo:v1 :“xxx/kaniko-demo” 我的 dockerhub 的仓库
在生产环境,可以配置缓存,用于加速镜像构建。
查看日志
执行上一步的命令之后,可以看到如下输出:
如果没有问题会正常退出!
docker images|grep kaniko-demo
查看构建镜像是否落盘本地,执行命令:
执行完命令,没有任何输出,符合预期。构建之后的镜像,直接被推送到了远程 Registry。
DockerHub 查看镜像,在 DockerHub 页面可以查看到推送的镜像:
使用刚才上传到 dockerhub 镜像,创建容器
执行命令
docker run -d -p 8011:80 peishunwu/kaniko-demo:v1
验证服务是否正常,执行命令:
curl localhost:8011
服务正常!