背景
在云原生趋势下,用容器的方式来进行软件产品交付越来越普通,对于云原生的DevOps,它的CICD环境完全运行在容器中,镜像的构建也是在容器中完成的。而我们不仅要考虑如何在容器中成功构建镜像,也需要考虑如何以更安全的方式来构建容器镜像。
容器内构建镜像的方式
容器中构建镜像一般分为两种:
- 在Docker容器中运行Docker,依赖Docker Daemon
- Kaniko -K8s中构建镜像,不依赖Docker Daemon
下面分开来讨论两种方式
在Docker容器中运行Docker
在Docker中实现Docker的二种方法:
- 通过挂载docker.sock运行docker:需要root权限
- dind(docker in docker):需要privileged特权
通过挂载docker.sock运行docker
要在docker内部运行docker,要做的只是在默认Unix套接字docker.sock作为卷的情况下运行docker 。
docker run -it -v /var/run/docker.sock:/var/run/docker.sock docker
缺点:只有root权限才能访问docker daemon进程,在docker daemon无法暴露或者用户没有权限获取docker daemon进程的前提下,用 docker build 来构建镜像就不可行了。
dind(docker in docker)
docker build镜像就是需要docker命令可以成功运行,只要在容器里面安装一个docker就可以。这种方式不需要挂载宿主机的socket文件,但是需要以 --privileged 权限来以dind镜像创建一个容器
docker run --rm -it --privileged docker:dind
缺点:该方式需要提供特权,可能会看到宿主机上的一些设备,并且可以执行mount命令。
比较
这两种方式,一种需要root权限,一种需要privileged特权,从安全角度来看是有风险的。特别是在K8S多租户的场景下,这种方式不能被接受。同时当一台机器上同时运行多个docker build流水线时,因为这批流水线用的是宿主机上同一个docker进程,会出现阻塞的情况。因此Google提供了一个工具Kaniko用来解决这个问题
Kaniko
Kaniko是一个Google开源的方便我们在k8s中使用dockfile构建镜像的工具。它不依赖docker daemon进程,并完全在用户空间执行dockfile文件的每一条命令。这样我们就可以在一些没法获取docker daemon进程的环境下也可以构建镜像。比如在K8S上。
kaniko会先提取基础镜像的文件系统,然后根据Dockerfile中的描述,一条条执行命令,每一条命令执行完之后都会在用户空间创建一个snapshot,并于存储在内存中的上一个状态做对比,若有变化,将新的修改生成一个镜像层添加在基础镜像上面,并将相关修改信息写入到镜像元数据中,等全部命令执行完,Kaniko会将最终镜像推送到指定的远端镜像仓库。
下面给出一个利用kaniko来构建镜像的示例
前提条件
- 一个Kubernetes集群
- 一个dockerhub账户,用于push镜像
为kaniko准备配置文件
kaniko以容器镜像的方式运行,同时需要三个参数:Dockerfile,context 以及远端镜像仓库的地址。需要准备几个配置文件来在k8s中创建资源,他们是:
- kaniko_build_image.yaml 用于启动kaniko容器用来构建镜像
- configmap 存放Dockerfile文件
- secret 存放dockerhub授权信息
创建一个Dockerfile文件
编写一个Dockerfile文件:
FROM ubuntu
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
并创建一个configmap:
kubectl create configmap kanikodockerfile --from-file=./Dockerfile
创建一个保存远端镜像仓库凭证的Secret
k8s集群使用docker-registry类型的Secret去授权docker仓库去推送镜像。创建这个Secret:
kubectl create secret docker-registry regcred --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
<your-registry-server>: 你的私有Docker仓库地址,DockerHub是https://index.docker.io/v1/
<your-name>: 你的仓库用户名
<your-pword>: 你的仓库密码
<your-email>: 你的仓库邮箱 可选
这个Secret会在pod.yaml,也就是用kaniko创建镜像的容器中使用。
或者编辑名为config.json的文件,并生成Secret,后需要以secret的方式挂载到/kaniko/.docker/这个目录下。文件内容为:
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "AbcdEdfgEdggds="
}
}
其中auth的值为: echo"docker_registry_username:docker_registry_password"|base64
准备kaniko_build_image.yaml 用于启动kaniko容器用来构建镜像
apiVersion: v1
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args: ["--dockerfile=/workspace/dockerfile",
"--context=dir://workspace",
"--destination=<user-name>/<repo>"] # replace with your dockerhub account
volumeMounts:
- name: kaniko-secret
mountPath: /kaniko/.docker
- name: dockerfile
mountPath: /workspace
restartPolicy: Never
volumes:
- name: kaniko-secret
secret:
secretName: regcred
items:
- key: .dockerconfigjson
path: config.json
- name: dockerfile
configMap:
name: kanikodockerfile
构建
现在就是创建pod,查看状态,并看log是否正常完成。
创建构建podkubectl apply -f kaniko_build_image.yaml
测试
用kaniko生成的镜像来测试下是否可用:sudo docker run -it <user-name>/<repo-name>
参考链接
https://cloud.tencent.com/developer/article/1697053
https://www.shangmayuan.com/a/38d1cc5f039540899ceb0d43.html
https://www.yinyubo.com/2021/06/25/自定义一个kaniko镜像/