LoginSignup
3
6

DockerコンテナとブリッジとDHCP

Last updated at Posted at 2023-01-07

やること

  • DockerコンテナをDockerホストと同じネットワークに繋ぎます
  • DockerコンテナにDockerホストと同じDHCPサーバーからIPアドレスを割り当てます
  • Dockerコンテナとその他ホストで、ポートバインディングなしで双方向に通信します
  • Dockerコンテナ内でsystemdを動かします

つまり仮想マシンをブリッジ接続したときと同じようなことをDockerコンテナで行います。
これができると、特権ポートである80や443を使うhttpサーバーを、仮想マシンよりも軽量にいくらでも増やせるようになります。

環境

  • Dockerホスト:Fedora37
    • 物理PC上で動作している
    • firewalldとselinuxは無効
    • ブローバンドルーターからDHCPでIPアドレスをアサインされている
  • Dockerエンジン:Moby-Engine 20.10.21

Moby-Engineは、DockerのOSS版です。chromeに対するchromiumのような位置づけです。

仮想ブリッジ環境構築

まずDockerホスト側で仮想ブリッジを作ります。特にDockerに依存した話ではなく、ここで作成した仮想ブリッジをKVM仮想マシンでも使うことができます。

既存の接続情報を削除
# nmcli connection del 有線接続
ブリッジを追加
# nmcli connection add type bridge autoconnect yes con-name br0 ifname br0
IPv4はDHCP、IPv6は無効
# nmcli connection modify br0 ipv4.method auto
# nmcli connection modify br0 ipv6.method disabled

ブリッジbr0にネットワークインタフェースenp0s3を追加する。enp0s3の実際のイーサーネットデバイスに置き換える
# nmcli connection add type bridge-slave autoconnect yes con-name enp0s3 ifname enp0s3 master br0

Linuxカーネルのbridgeモジュールは、br_netfilterというモジュールを使ってパケットをフィルタしているので、フィルタを無効化する。

/etc/modules-load.d/br_netfilter.conf
br_netfilter
/etc/sysctl.d/bridge.conf
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-arptables=0

設定が終わったら再起動する

# shutdown -r now

再起動後、うまく行っていれば、br0にブロードバンドルーターのDHCPからIPアドレスがアサインされています

よくわからなかったもの

  • スパニングツリープロトコル
    • イーサーネットフレームがネットワークを循環しないようにするプロトコル
    • 仮想ブリッジについて調べると、これを無効にしている記事をよく見かける。今回のような仮想ブリッジの構成では、循環が発生しないので、不要なので無効にしているということか?
  • プロミスキャス・モード
    • Ethernetデバイスが自身宛のパケット以外も受け取るモード
    • 物理デバイスの方で設定が必要そうにも思えるが、今の所設定しなくても動いている。

docker-net-dhcp

ここからDockerコンテナを立ち上げていきます。

dockerエンジンは以下のようにインストールします。

$ sudo dnf install -y moby-engine
$ sudo systemctl enable docker --now
$ sudo usermod -aG docker $USER
$ shutdown -r now

Dockerコンテナのプラグインであるdocker-net-dhcpをインストールします。

# docker-net-dhcpのインストール
$ docker plugin install ghcr.io/devplayer0/docker-net-dhcp:release-linux-amd64
# docker-net-dhcpを使ったDockerネットワークを作成
$ docker network create -d ghcr.io/devplayer0/docker-net-dhcp:release-linux-amd64 --ipam-driver null -o bridge=br0 my-dhcp-net

上記でbridge=br0を指定しています。これでdocker-net-dhcpが、br0を経由してDHCPサーバーにアクセスするようになります。

ネットワークにmy-dhcp-netを指定してnginxコンテナを立ち上げて、そのIPアドレスを取得します。

$ docker run --rm -d --network my-dhcp-net --name nginx nginx
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
192.168.1.109

ブラウザで、 http://192.168.1.109 を開くとnginxのページが表示されます。
Dockerホスト以外のマシンからでもアクセス可能です。

追記 2024/4/7

最近のdockerでは、docker-net-dhcpはバージョンミスマッチで動かないようです。

修正版があるのでこちらを使えば動きます。

古いdocker-net-dhcpをインストールしている場合は、事前にネットワークとプラグイン削除しておきます(ブリッジは残します)。

$ docker network rm my-dhcp-net
$ docker plugin disable ghcr.io/devplayer0/docker-net-dhcp:release-linux-amd64
$ docker plugin rm ghcr.io/devplayer0/docker-net-dhcp:release-linux-amd64

修正版を使用します

$ git clone git@github.com:Celerway/docker-net-dhcp.git
$ cd docker-net-dhcp
$ make
$ docker network create -d ghcr.io/devplayer0/docker-net-dhcp:golang --ipam-driver null -o bridge=br0 my-dhcp-net

これでネットワークができたので、nginxの動作確認方法は同じです。

$ docker run --rm -d --network my-dhcp-net --name nginx nginx
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nginx
192.168.1.109

コンテナ内でsystemdを動かす

最後にコンテナ内でsystemdとsshdを動かして、そこにsshログインします。
ここまでくるとDockerコンテナを仮想マシンのように扱えます。
ただし、セキュリティ的によろしくないと言われる、priviledgeモードを使うので、あくまで実験ということで。パラメータによっては、Dockerホスト側にも影響するのでご注意ください。

まずsystemdを含んだコンテナイメージを作ります。
以下のようなDockerfileを作ります。

Dockerfile
FROM rockylinux/rockylinux:8.4
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
RUN dnf install -y openssh-server openssh-clients passwd procps-ng iproute
RUN echo "hogehoge" | passwd --stdin root
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

上記ではrootのパスワードをhogehogeにして、sshログインできるようにしています。
ビルドして実行します。

$ docker build -t myrocky .
$ docker run --rm -d -v /sys/fs/cgroup:/sys/fs/cgroup:rw --privileged --cgroupns=host --network my-dhcp-net  --name myrocky myrocky
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myrocky
192.168.1.111
$ ssh root@192.168.1.111

参考

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6