容器相比于 VM 虚拟机是属于弱隔离,在某些场景下需要增强容器的隔离或扩展应用场景来解决新的问题,如下场景在云原生实践过程中也经常会遇见,本文收集并分析了目前主流的容器嵌套方法,可根据当前团队的现状来综合评估后选择。
在 Docker 中运行 Docker 目前可提供三类方法
dockerd 服务在启动时会提供基于 socket 的连接方式,docker client 客户端可以选择使用 Socket 文件或 TCP/IP 的方式连接 Socket 来访问 dockerd 进行使用和管理功能,默认会使用 /var/run/docker.sock
的 socket 文件接口方式连接。
当在宿主机上运行容器 Container0 后,需要在该容器内继续运行 docker 命令来使用容器功能,可以参考如下步骤:
docker:latest
容器的嵌套和管理示意图:
docker exec
进入到该容器docker exec
进入到 Container3 容器中docker exec
进入到嵌套的内容容器该方法的容器嵌套命令参考
sudo docker run \
-v /var/run/docker.sock:/var/run/docker.sock \
--name docker0 \
-ti docker
执行输出内容:
# sudo docker run \
> -v /var/run/docker.sock:/var/run/docker.sock \
> -ti docker
/ # docker ps |grep docker0
b326055722fb docker "docker-entrypoint.s…" 29 seconds ago Up 28 seconds docker0
/ #
这里可以看到进入到容器内后,使用 docker 命令还可以看到当前容器本身。也使用 docker version
可以看到客户端和服务端是两个不同版本的 docker服务。
实例中使用的 socket 文件的方式连接,如果运行的容器和 dockerd 不在同一台机器,或者本地文件没有权限挂载,也可以通过指定容器的 IP 地址和端口的方式连接到 dockerd ,客户端连接参数 -H, --host list Daemon socket(s) to connect to
。
一般情况下操作系统在运行时会启动很多系统管理的服务,如 systemd;但容器启动时只有一个业务服务进程,即使安装了一些软件也无法像在操作系统中那样正常启动。
如下,是在 centos7 的基础镜像中安装了 docker 服务,在没有进行特殊改造的情况下,是无法来启动 docker.service 服务的。
[root@9952f799dab1 /]# systemctl start docker
Failed to get D-Bus connection: Operation not permitted
本篇文章不会深入的介绍如何构建这样的镜像,因为社区已经提供了相对完整的解决方案。这里主要提供
docker:dind
kicbase:v0.0.32
这种方式提供的容器中的容器运行环境和宿主机上的 dockerd 是相互独立的
docker exec
进入到该容器docker exec
进入到 Container3 容器中docker exec
进入到嵌套的内容容器docker:dind
该镜像的目标是提供 docker 运行环境,因此启动的容器中主要功能就是 docker 服务,其他 systemd 的相关服务是无法正常运行的。
docker run -itd \
--name dind-test \
--privileged docker:dind
运行示例:
# docker run -itd \
> --name dind-test \
> --privileged docker:dind
4e2b702710f742f34b59683542c64724bb36502daa23115c537d25223c4ba138
# docker exec -it dind-test sh
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
kicbase
kicbase 提供了完整的隔离环境,也提供 systemd 的系统服务的管理,和 VM 虚拟机使用体验很相似,也可以安装启动其他 systed 服务。
minikube 社区使用 kicbase 就用来提供类似虚拟化 VM 的驱动,使用该驱动来运行 Kubernetes 集群的。
该容器启动时默认
docker run -itd \
--name virtualvm1 \
--privileged \
-p 20022:22 \
registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.32
启动实例
[minikube@localhost ~]$ docker run -itd \
> --name virtualvm1 \
> --privileged \
> -p 20022:22 \
> registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.32
a06cc6ee28ebcc3eb5acfe7ac3f0fe171316693d67e68438e3c161efb043f98e
[minikube@localhost ~]$ docker exec -it virtualvm1 bash
root@a06cc6ee28eb:/# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@a06cc6ee28eb:/# ps auxf
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 350 0.0 0.0 4248 3404 pts/1 Ss 04:26 0:00 bash
root 359 0.0 0.0 5896 2960 pts/1 R+ 04:26 0:00 \_ ps auxf
root 1 0.5 0.0 21252 10168 ? Ss 04:26 0:00 /sbin/init
root 201 0.1 0.0 27472 8420 ? S
kicbase 的容器默认会启动 init、systemd、dbus、docker、sshd 等服务,在容器内的使用体验就是一个 Linux 操作系统环境。
使用这两种方式运行容器嵌套都需要提供给 --privileged
参数来使用特权权限,该权限在不安全的网络环境中提供服务时具有一定的风险,如若考虑安全可以使用 docker rootless 的方式运行
SysBox 是一种与 containerd、cri-o 类似的容器运行时。
与前两种容器嵌套方式不同的是,SysBox 从容器运行时的角度提供了新的解决方案,它可以在能够运行 systemd,docker,kubernetes 的容器内创建虚拟环境,而无需特权访问基础主机系统。
在使用前需要先安装 sysbox 运行时环境。请参阅此页面以获取有关安装sysbox运行时的最新官方说明[1]。
一旦拥有sysbox运行时可用,您要做的就是使用 sysbox 运行时标志启动 docker 容器,如下所示。
在这里,我们使用的是官方 docker dind 映像。
docker run -itd \
--runtime=sysbox-runc \
--name sysbox-dind docker:dind
容器启动后,就可以登录到容器中
# docker exec -it sysbox-dind /bin/sh
/ # docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
本文提供了三种从不同思路提供了容器嵌套的解决方案。
使用共享宿主机 dockerd 的方式是能够快速理解和使用的一种方式,不需要进行特殊的调整和改造,在使用上可以解决一些容器嵌套的需求,但是没有提供较好的隔离。
使用独立隔离的容器运行环境其实是目前相对较好的选择,他可以提供相对独立的隔离环境,特别是 minikbue 提供的 kicbase,基本实现了基于容器的 VM 虚拟机实现;但是这种方式需要 root 特权,在网络安全要求较高的情况下需要进行特殊的处理。
使用 Sysbox Runtime 运行时的方案解决了上面两种独立和安全的问题,但 Sysbox 目前并非主流的容器运行时,并没有大众广泛接受。