允许外部通过容器名解析至 Docker

Docker
之前做好了 给 Docker 配置完整的 IPv6 macvlan 网络环境,局域网内可以直接通过路由器访问到容器网段,下一步便是让容器外也能直接 DNS 解析容器名,当然仅对加入到特定网络的容器生效。

相同的想法早有人实现了,这里照搬验证

首先创建网络,注意这里创建的网络名可以当做后缀来访问,比如这个网络可以解析 coredns.docker 这个域名到名为 coredns 的容器。

On Docker Engine Host
docker network create -d macvlan --ipv6 \
  -o parent=ens19 \
  --subnet=192.168.2.0/24 --gateway=192.168.2.1 \
  --ip-range=192.168.2.128/25 docker

然后创建 CoreDNS 配置文件,这里的 /etc/resolv.conf 其实就是 127.0.0.11 是 Docker Engine 的内置 DNS 服务器地址。在容器中查看该文件能发现这一点 從 KIND 環境中學到的 DNS 小趣聞 | hwchiu learning note

Corefile
. {
    # enables error logging
    errors
    # fast failling
    cancel
    # loop detection on startup
    # since container can have dot inside their name (pattern '[a-zA-Z0-9][a-zA-Z0-9_.-]'),
    # it's impossible to make a allowlist / blocklist to only resolve to docker internal names.
    # thus please make sure upstream have some way to break the infinite loop.
    loop
    # dump all queries on standard output
    log
    # blocks ANY queries
    any
 
    # bind to docker.kokomi.site subdomain
    rewrite stop {
        name suffix .docker.kokomi.site. .docker. answer auto
    }
 
    # only allow local network access
    acl {
        allow net 192.168.0.0/16
        block
    }
 
    # forward to docker internal DNS server
    forward . /etc/resolv.conf
}

这里额外绑定到 .docker.kokomi.site 下面了,本来想用 view 来限制只去支持子域名查询的,来避免查询一个 a.docker 传递到上游的 DNSmasq,再由 server=/docker/192.168.2.53/ 设置传递回来形成死循环。可想想这样并没法限制,因为如果 a 是个不存在的容器,docker internal DNS 自身就会转发到上游,所以想要杜绝死循环需要在上游做动作。

好在我这里其实有两层转发,Docker <> OpenWrt <> Router,所有主机的 DNS 都是 Router,DNSmasq 转发规则也设置在 Router 上。那么可以在 OpenWrt 层不响应相对应的查询,打破了死循环。

Router dnsmasq.conf (partial)
server=/docker.kokomi.site/192.168.2.53
server=/docker/192.168.2.53
OpenWrt /etc/config/dhcp (partial)
config dnsmasq
	option localise_queries '1'
	option rebind_protection '0'
	option local '/pve.kokomi.site/docker.kokomi.site/docker/'
	option domain 'pve.kokomi.site'
	option extraconftext 'enable-ra\ndhcp-range=::,constructor:br-lan,ra-names'

然后编辑 compose 文件,这里选个 53 尾号的 IP 好记。因为都是 macvlan 可以直接访问,也就没有必要设置 ports 端口转发了。

services:
  coredns:
    container_name: coredns
    image: coredns/coredns:latest
    networks:
      docker:
        ipv4_address: 192.168.2.53
    volumes:
      - ./Corefile:/etc/coredns/Corefile:ro
    command: -conf /etc/coredns/Corefile
networks:
  docker:
    external: true

清理 Docker Dead Container

没有正常关机、直接执行 reboot 命令,会在 Docker Host 上留下 Dead 状态的容器

# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED      STATUS    PORTS     NAMES
d26e77d28cc3   nginx     "/docker-entrypoint.…"   7 days ago   Dead
cf4e5bb6fd43   nginx     "/docker-entrypoint.…"   7 days ago   Dead

而且 docker rm 还无能为力,这时候只要去 /var/lib/docker/containers/,把目录里容器 ID 对应的目录删掉,然后 service docker restart 等方式重启 Host 就清除了。