如果你还搞不懂 Docker In Docker(DinD) 模式下的资源管理, 不妨看看这篇文章

如果在 公众号 文章发现状态为 已更新, 建议点击 查看原文 查看最新内容。

状态: 未更新

原文链接: https://typonotes.com/posts/2023/04/04/docker-dind-mode-introduce/

今天遇到了一个群友关于 Docker DinD 的问题。

有人熟悉docker in docker么 请教个问题, 我现在在docker-1里面挂载了本地的/root/.m2 想在里面启动一个新的docker在build的时候使用 这个目录下的缓存 有人做过么~

需求是想要通过 容器1 启动 容器2, 同时要求 容器2 也能挂载并使用 .m2 目录, 加入 JAVA 的应用的构建。

其实 DinD 说通了并不难。 想来难的地方应该也就是 理不清楚各个容器之间的关系

Docker 常规操作模式

首先抽象一下 Docker 的操作, 提出两个重点组件

  1. docker.sock/var/run/docker.sock套接字, 为用户提供管理 docker 引擎的接口。
  2. docker CLI: 命令行工具。 面向用户提供 语义化 的接口操作方式。

任何符合 docker.sock 通信的方式和操作, 都能控制 docker 引擎。

本机操作

我们从 本机操作 开始, 一步步扩展开, 比较容易理解

从图中可以看到, 当 cli 和 sock 都在本机的时候, 所有注入的 环境依赖(变量、路径挂载、端口映射等) 都是直接使用 宿主机 的。

这个时候的认知最简单。

启动

1
2
3
$ docker run -d --rm --name docker-dind-nginx-local nginx:alpine

6acd48977a82ed7fcbc0f3e2f9ae8b84675bfb7a26b9a2a27c36068b641d2fb1

远程操作

同时 Docker 还提供了远程操作方式。

这个时候保证本机的 docker 引擎 未安装 或者 未启动, 以避免产生理解歧义。

当在 cli 的机器上, 配置 DOCKER_HOST 变量的时候, 就可以实现远程的 sock 操作。

设置环境变量, 指定远程宿主机

1
$ export DOCKER_HOST=ssh://[email protected]

本地执行查看命令

1
2
3
$ docker ps |grep docker-dind

6acd48977a82   nginx:alpine "/docker-entrypoint.…"   47 seconds ago   Up 44 seconds   80/tcp  docker-dind-nginx-local

本地启动容器

1
2
3
$ docker run -d --rm --name docker-dind-nginx-remote nginx:alpine

c5b898d652ce1e9eb4d307501c36fd2aa9e9c64931eff59eb4458bdf4b092ec0

登陆到远程宿主机, 查看所启动的容器

1
2
3
4
$ docker ps |grep docker-dind

c5b898d652ce   nginx:alpine    "/docker-entrypoint.…"   About a minute ago   Up 59 seconds   80/tcp    docker-dind-nginx-remote
6acd48977a82   nginx:alpine    "/docker-entrypoint.…"   4 minutes ago        Up 4 minutes    80/tcp    docker-dind-nginx-local

可以看到, 本地操作远程是完全没有问题的。

注意 由于本地没有安装 docker 引擎, 且只下载了一个 cli 命令行工具。 所以在 注入环境依赖 的时候, 通常也不会造成困惑, 会默认使用 宿主机 资源。

除此之外, Docker 还提供了 HTTPS接口 的远程操作方式。 这里就不讨论了。 不过额外强调一句, 如果要开这种模式, 一定要配置证书和身份验证, 否则不小心开到公网, 就是被别人当成木马马场用。

Docker DinD 模式

所谓的 Docker-in-Docker 模式, 就是将 docker.sock 挂载到启动的 容器A 中, 而容器A提供 操作方式/界面(例如 cli 命令工具)

宿主机 执行 docker:dind 容器, 并挂载 docker.sock 到容器中

1
docker run --rm -it --name=container01 -v /var/run/docker.sock:/var/run/docker.sock docker:dind sh

通过上图可以看到, 我们通过挂载 docker.sock 到 container01 中后, 相当于在 container01 中同时拥有了 docker.sockdocker cli, 这种情况和 本地操作 类似。

这里就是困惑点: 当我在 container01 中新启动一个容器的时候, 此时如果需要挂载 资源(例如映射文件目录), 应该使用 container01 的路径? 还是 宿主机 的路径?

容器中 执行 docker run 命令, 启动一个新容器

1
2
3
container01 # docker run -d --rm --name docker-dind-nginx-inside nginx:alpine

786b0c555e7ba80c56638e65fb088252316d80699b6936549667c79c7637d4c2

重新看图,其实这点很简单了。

  1. 虽然是是在 container01 中执行的 docker run 命令, 但是调用的 docker.sock 套接字是 从宿主机挂载到container01中 的。
  2. 因此实际上还是通过 宿主机 启动的 container02。 换句话说,
  3. container02 的父节点是 宿主机
  4. contaienr02container01 是兄弟关系。

container01 中, 通过命令我们之前启动的三个容器。

1
2
3
4
5
container01 # docker ps |grep docker-dind

786b0c555e7b   nginx:alpine     "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp    docker-dind-nginx-inside
c5b898d652ce   nginx:alpine     "/docker-entrypoint.…"   40 hours ago    Up 40 hours    80/tcp    docker-dind-nginx-remote
6acd48977a82   nginx:alpine     "/docker-entrypoint.…"   40 hours ago    Up 40 hours    80/tcp    docker-dind-nginx-local

另外一种 DinD

如果有人说, 我启动 container01 的时候不挂载 docker.sock, 而是直接在 contianer01 中完整安装一个 docker 引擎。 这种情况应该怎么挂载呢?

如果是在 container01 中执行的的启动命令, 这个时候肯定就是挂在 container01 对应的路径。

不过, 不得不强调一下 这种把容器当成虚拟机的用法, 还是敬而远之吧。

总结

其实很简单, 就一句话: 谁 执行 容器的启动, 就挂载谁的路径。

思考题

我们知道在 k8s 中, 简略的调度过程是这样的 user -> master api-server -> node kubelet -> docker.sock/containerd.sock

那么, 用户在任意地方启动 pod 的时候, 如果需要挂载 node 的目录资源到 pod 中, 应该是用 用户所在机器的目录路径? master 节点路径? 还是 node 节点路径?