背景

在云原生趋势下,用容器的方式来进行软件产品交付越来越普通,对于云原生的DevOps,它的CICD环境完全运行在容器中,镜像的构建也是在容器中完成的。而我们不仅要考虑如何在容器中成功构建镜像,也需要考虑如何以更安全的方式来构建容器镜像。

容器内构建镜像的方式

容器中构建镜像一般分为两种:

  • 在Docker容器中运行Docker,依赖Docker Daemon
  • Kaniko -K8s中构建镜像,不依赖Docker Daemon

在Docker容器中运行Docker

这两种方式,一种需要root权限,一种需要privileged特权,从安全角度来看是有风险的。特别是在K8S多租户的场景下,这种方式不能被接受。同时当一台机器上同时运行多个docker build流水线时,因为这批流水线用的是宿主机上同一个docker进程,会出现阻塞的情况。

  1. 通过挂载docker.sock运行docker:需要root权限

    通过挂载docker.sock运行docker,使用默认Unix套接字docker.sock作为卷的情况下运行docker 。
    缺点:只有root权限才能访问docker daemon进程,在docker daemon无法暴露或者用户没有权限获取docker daemon进程的前提下,用 docker build 来构建镜像就不可行了。

  2. dind(docker in docker):需要privileged特权

    docker build镜像就是需要docker命令可以成功运行,只要在容器里面安装一个docker就可以。这种方式不需要挂载宿主机的socket文件,但是需要以 –privileged 权限来以dind镜像创建一个容器
    缺点:该方式需要提供特权,可能会看到宿主机上的一些设备,并且可以执行mount命令。

Kaniko构建

前提
  • Kubernetes(K8S)运行环境
  • dockerhub账号或者私有镜像仓库,用于push镜像
准备Dockerfile
  1. 登录到K8S集群主节点,创建一个本地目录,该目录将被挂载到kaniko容器作为构建环境,在改目录创建一个简单的Dockerfile文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [root@VM-20-7-centos ~]# mkdir kaniko && cd kaniko
    [root@VM-20-7-centos kaniko]# pwd
    /root/kaniko
    [root@VM-20-7-centos kaniko]# cat > Dockerfile << EOF
    > FROM ubuntu
    > ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
    > EOF
    [root@VM-20-7-centos kaniko]# cat Dockerfile
    FROM ubuntu
    ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
  2. 创建docker secret秘钥

Kubernetes集群使用docker-registry类型的Secret来验证docker registry,用于推送镜像。

1
kubectl create secret docker-registry <secret-name> --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email>
  • docker秘钥名称
  • 是你的私有 Docker Registry FQDN。
  • 是你的 Docker 用户名。
  • 是你的 Docker 密码。
  • 是你的 Docker 电子邮件。
  1. 创建资源清单

创建manifests目录,在该目录下创建所需的资源清单

1
mkdir manifests && cd manifests

volume.yaml

1
touch volume.yaml && vi volume.yaml
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolume
metadata:
name: kaniko-hostpath-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
storageClassName: local-storage
hostPath:
path: /root/kaniko

volume-claim.yml

1
touch volume-claim.yml && vi volume-claim.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kaniko-hostpath-pvc
## namespace: admin
spec:
volumeName: kaniko-hostpath-pv
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-storage

pod.yml

1
touch pod.yml && vi pod.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: v1
kind: Pod
metadata:
name: kaniko
## namespace: admin
spec:
containers:
- name: kaniko
image: kubebiz/kaniko:executor-v1.9.1
args: ["--dockerfile=/workspace/dockerfile",
"--context=dir://workspace",
"--destination=bunny123/test-kanik0[:tag]"] # 你的dockerhub账户,DockerHub是https://index.docker.io/v1/
volumeMounts:
- name: kaniko-secret
mountPath: /kaniko/.docker
- name: dockerfile-storage
mountPath: /workspace
restartPolicy: Never
volumes:
- name: kaniko-secret
secret:
secretName: <secret-name> # 替换
items:
- key: .dockerconfigjson
path: config.json
- name: dockerfile-storage
persistentVolumeClaim:
claimName: kaniko-hostpath-pvc
  1. 在Kubernetes中创建资源
1
2
3
4
5
[root@VM-20-7-centos kaniko]# kubectl apply -f manifests/
## 输出结果
pod/kaniko created
persistentvolumeclaim/kaniko-hostpath-pvc created
persistentvolume/kaniko-hostpath-pv created
  1. 查看Pod运行日志
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@VM-20-7-centos kaniko]# kubectl logs -f kaniko
    # Pod日志内容
    INFO[0005] Retrieving image manifest ubuntu
    INFO[0005] Retrieving image ubuntu from registry index.docker.io
    INFO[0025] Built cross stage deps: map[]
    INFO[0025] Retrieving image manifest ubuntu
    INFO[0025] Returning cached image manifest
    INFO[0025] Executing 0 build triggers
    INFO[0025] Building stage 'ubuntu' [idx: '0', base-idx: '-1']
    INFO[0025] Skipping unpacking as no commands require it.
    INFO[0025] ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
    INFO[0025] Pushing image to bunny123/test-kanik0
    INFO[0039] Pushed index.docker.io/bunny123/test-kanik0@sha256:996220f9fb96b96fdeefb796d0fe02a8995a07b61c2e0ed9d74134e0c0e6ece8
    当看到日志中出现Pushed,表示镜像已经构建成并推送至指定仓库。