如何优雅地刷新 host 模式容器网络
Sep 9, 2020 22:30 · 779 words · 2 minute read
感谢春阳同学提供的案例。
我们探讨下这个需求:应用程序的容器以 host 网络模式在宿主机上运行,它们需要通过本地 /etc/hosts 来解析某些主机记录,但是因业务原因这些记录经常会改变,想要在不重启容器的情况下实现容器中的 /etc/hosts 与宿主机 /etc/hosts 保持一致。
我们可以做一个实验,首先以 host 网络模式运行一个 centos:7 镜像的 bash:
$ docker run --network host --rm -it centos:7 bash
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
容器运行起来时的 /etc/hosts 是与宿主机上的完全一致。我们手动修改宿主机上的 /etc/hosts:
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 example.com
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
会发现容器中的 /etc/hosts 还是与启动时一样,并没有新插入的 example.com。虽然容器没有网络隔离但 /etc/hosts 却是由 Docker 来维护的:
$ docker inspect -f {{.HostsPath}} $(docker ps -qa)
/var/lib/docker/containers/8be774dd30c4029569f11f818b6be181a29851907a2d3c243927a7359377a90d/hosts
$ cat /var/lib/docker/containers/8be774dd30c4029569f11f818b6be181a29851907a2d3c243927a7359377a90d/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
我们通过 mount
命令查看其具体挂载信息:
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/b0e940e108cf867f820ac38c8db04ee447050ea87806e35898c134aea4554843/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/MIRLIITPCCUUQLKUKTGP344T24:/var/lib/docker/overlay2/l/33RIHGQEPNQDSW3MP66N5PD4SI,upperdir=/var/lib/docker/overlay2/b0e940e108cf867f820ac38c8db04ee447050ea87806e35898c134aea4554843/diff,workdir=/var/lib/docker/overlay2/b0e940e108cf867f820ac38c8db04ee447050ea87806e35898c134aea4554843/work)
overlay2 是一个类似于 aufs 的现代的联合文件系统,并且更快。
如图 overlay2 包含 lowerdir
、upperdir
和 merged
三层:
- lowerdir 表示较为底层的目录
- upperdir 表示较为上层的目录
- merged 是 lowerdir 和 upperdir 合并后的联合挂载点
- workdir 用来存放挂载后的临时文件与间接文件
我们只需修改 merged 层中的文件即可更新容器内部 /etc/hosts:
$ vim /var/lib/docker/overlay2/b0e940e108cf867f820ac38c8db04ee447050ea87806e35898c134aea4554843/etc/hosts
我们只需实现脚本定时轮询宿主机上的 /etc/hosts 文件并同步至联合挂载后的 /var/lib/docker/overlay2/${layer_id}/etc/hosts 即可。
但是,春阳同学后来提供了一个更为优雅的方法。我们都知道 k8s Pod 中的网络栈由一个名为 pause 的容器来维护,那我们也可以效仿创建一个与业务无关的 “pause” 容器来维护所有以 host 模式运行的 Docker 容器的网络栈:
$ docker run --name pause --network host -d centos:7 sleep infinity
$ docker run --network container:pause --rm -it centos:7 bash
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 example.com
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
接着我们删除宿主机 /etc/hosts 文件中的 example.com
记录,然后重启 pause 容器来刷新其网络栈 docker restart pause
,再回临时容器中查看 /etc/hosts:
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
业务容器中的网络栈与 puase 容器与宿主机完全保持同步了!纵其业务容器再多,我们只需重启 pause 容器即可优雅地刷新所有容器中的网络栈,再回看 k8s 借助 pause 容器来维护整个 Pod 网络栈的设计可谓精妙绝伦。