Docker in docker

啥是docker in docker?字如其义,就是在docker里面实现docker,换句话说,就是如何在docker执行docker命令。

有人可能会问了,为啥要这么麻烦,直接裸机跑容器不就完了吗?

读完这篇文章,你应该会有所了解。

关键字:docker in docker

docker in docker(dind)

有时需要在容器里面执行docker命令,比如在jenkins 容器内运行 docker 命令执行构建镜像等。

事实上,docker是支持嵌套的。但是直接在docker容器里面安装docker有点臃肿。

更好的方法是:容器里面仅仅部署docker-cli(作为客户端),实际执行交给宿主机内的 docker-engine(服务端)

两种方式

一种方式是,通过宿主机的docker.sock文件,将宿主机docker服务端的socket文件挂载到容器内,同时将docker二进制文件挂载到容器内,这样,容器内就不需要安装docker程序。当容器内使用docker命令时,实际上调用的是宿主机的docker daemon和docker命令。也就是说,容器内并未真正运行docker server,但是能通过宿主机执行docker任务,从而实现轻量级docker in docker。这种情况下,真正执行docker命令的是跑在宿主机上的docker-engine,并不是真正的docker in docker。但是这种情况速度较快,然后存在一定的安全问题。不过,这种情况就是我们所需要的呀!!

还有一种方式是,启动一个docker:dind 容器A,再启动一个docker容器B,容器B指定DOCKER_HOST为A容器内的docker daemon。

使用宿主机的socket文件

本想着挂载宿主机的docker.sock文件和docker二进制文件到容器即可实现dind,无奈默认情况下docker.sock文件是经过tls证书加密的,而在默认情况下加密的根证书却没有找到,通过宿主机执行docker info命令会提示以下错误:

翻阅资料知道docker daemon可以通过暴露tcp端口实现通信,而默认情况不是加密的,于是这个路就通了!!

通过暴露宿主机的tcp端口实现通信

  1. 查看宿主机docker服务单元文件地址

  1. 修改宿主机docker服务单元文件
1
sudo vim /lib/systemd/system/docker.service

安全起见,指定IP地址为局域网内网地址即可。

  1. 重载服务单元文件
1
sudo systemctl daemon-reload
  1. 重启服务
1
sudo systemctl restart docker.service
  1. 设置容器环境实现和宿主机docker daemon通信
1
2
3
4
5
6
7
8
9
10
11
12
version: '3'
services:
jenkins:
container_name: jenkins
image: ubuntu
environment:
- DOCKER_HOST=tcp://192.168.15.118:2375
volumes:
- /var/lib/docker:/var/lib/docker
- /usr/bin/docker:/usr/bin/docker
- /usr/local/bin/docker-compose:/usr/local/bin/docker-compose
command: ["docker", "info"]
  1. 测试
1
docker-compose up

通过上述方法,即可实现在jenkins容器中操作docker容器啦

后记

本来是想通过上述第一种方式来实现jenkins容器中操作docker容器,但是发现这种方法有点问题:

  • 容器内使用的是实际上宿主机的docker daemon,因此挂载路径时,实际上挂上去的是宿主机的目录。而不是jenkins容器内的文件!!因此如果要使用上面第一种dind的方式,那么每次部署的时候都需要重新打包镜像!!而不是挂载目录!!!
  • 如果采用了上面的方案实现了dind,那么怎么监听部署没部署成功呢?看来又要写相应脚本判断了。
    • 更新:不用脚本,不用脚本,直接docker inspect即可获得容器的状态!!!