nginx-proxyとdnsmasqでローカルでの名前解決

本番に似た環境をローカルに用意する時って、Docker超便利ですよね。なんなら作ったDockerイメージをそのままデプロイできますし。
dnsmasqとnginx-proxyを使って、名前解決できるようにしました。

dnsmasqを導入する

pacman -S dnsmasq
ArchLinuxならこれだけでインストールできる!他のOS、Linuxディストリビューションは各々調べて入れてね。

次にdnsmasqを動作させるのですが、/etc/resolv.confを直接書き換えるだけで済む場合と、他のソフトとの兼ね合いで別の設定ファイルに書く場合があります。

/etc/resolv.confに直接書く

ネームサーバを127.0.0.1にすることで、localhostで動作するdnsmasqで名前解決させるようにする。

echo "nameserver 127.0.0.1" > /etc/resolv.conf.head
echo "address=/localnet/127.0.0.1" >> /etc/dnsmasq.conf
systemctl start dnsmasq && systemctl enable dnsmasq

これでdnsmasqの準備はok。nginx-proxyの導入に続く。
更にlocalnetというドメインを127.0.0.1に解決させる。
NetworkManagerなど/etc/resolv.confを自動で書き換えるソフトを使っている場合は、そのソフトに設定を書くので下を参考に。

/etc/resolv.confに直接書けない

自分はNetworkManagerというものを使っているので、上述の通り/etc/resolv.confに直接書けません。

echo "address=/localnet/127.0.0.1" > /etc/NetworkManager/dnsmasq.d/localnet
cat <<EOF > /etc/NetworkManager/NetworkManager.conf
dns=dnsmasq
EOF
systemctl restart NetworkManager

[main]

これでNetworkManagerがdnsmasqを起動します。systemctl start dnsmasqなどで単体で起動する必要はありません!

dnsmasqがちゃんと動いているか確かめる

dig aaa.localnetとかやると、最終的に127.0.0.1で解決するか確かめる。

nginx-proxyを導入する

次にnginx-proxyを導入して、DockerコンテナにVirtual Hostを割り当てる。

https://github.com/jwilder/nginx-proxy

nginx-proxyのREADMEにもある通り、普通は以下のコマンドで動作させる。

docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy

…だけど、複数のプロジェクトのdocker-compose.ymlでやりたいので、この方法じゃダメ。

docker-compose.yml

version: "3"
services:
  proxy:
    image: jwilder/nginx-proxy:alpine
    restart: always
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
    ports:
      - "80:80"

networks:
  default:
    external:
      name: localnet

localnetって名前のネットワークに繋ぐ。そしてVirtual Hostを割り当てたいDockerコンテナもlocalnetって名前のネットワークに繋ぐことで、nginx-proxyと通信できる。
restart: alwaysと書いているので、docker-compose up -dを実行しておけば、再起動後もnginx-proxyを動かし続けられる。

あとは、whoami.localnetみたいなアドレスで解決したいコンテナを立ち上げる。

version: "3"
services:
  app:
    image: jwilder/whoami
    environment:
      - VIRTUAL_HOST=whoami.localnet
    expose:
      - "80"

networks:
  default:
    external:
      name: localnet

これで名前解決できるようになった。趣味の開発をする分には最高。

注意書き

ただ、localnetで繋ぐと、localnet内のコンテナが相互に参照できるようになる。
怪しいイメージから成るコンテナや、コンプラ的にマズいコンテナは繋がない方が良い。