Dockerでmacvlanを使う

Dockerのコンテナ技術は、DevOpsやCI/CDの実現に欠かすことのできない技術要素の一つです。Dockerコンテナを活用して構築されているシステムは、国内外問わずいくつも存在しています。

一般的なシステム構築の現場では、複数の物理サーバや論理サーバを利用するため、ネットワーク構成が重要なカギを握ります。Dockerを起動しているホスト上でコンテナを実行するだけであれば、bridgeネットワークを使うだけで解決できますが、他のサーバと通信する必要がある場合は一筋縄にはいきません。

bridgeネットワークを使ってコンテナを外部と通信させる場合は、ホストサーバとコンテナ間の通信ポートの紐づけや、iptablesでルーティングすることで実現することも可能です。

ただし、ネットワークに対するそれなりの知識が必要となり、Dockerに加えてホストサーバ側の設定作業も必要となるため、作業が複雑化してしまいます。また、設定ミスによってセキュリティの脆弱性を生じさせてしまう可能性もあります。

そこで今回は、Dockerを起動しているホストサーバ上のネットワークインターフェースを利用して、コンテナを外部と通信させるmacvlanの利用方法について解説します。

なお、Docker networkの基本的な使い方は、こちらで解説しています。

Dockerでmacvlanを使う

Dockerは、ホストサーバのネットワークインターフェースを利用して、コンテナを外部と通信できるようにする仕組みがあります。

ホストサーバのNIC(Network Interface Card)に仮想NICを作成し、その仮想NICに固有のMACアドレスを割り当てることにより、ホストサーバと異なるIPアドレスをコンテナに割り当てることができるようになります。これをmacvlanと呼びます。

macvlanのイメージ図

上の図では、ホストが所属するネットワークセグメント上に、4つのAlpine Linuxコンテナが接続されています。それぞれのコンテナは、割り当てられたIPアドレスで外部と通信することが可能です。

macvlanを作成する

macvlanを作成するためには、docker network createコマンドを使います。「-d」オプションでmacvlanのネットワークドライバを指定します。

「–subnet」オプションでホストサーバと同一のネットワークセグメントをCIDR形式で指定します。「–gateway」オプションでゲートウェイのIPアドレスを指定します。

「-o parent=XXX」で、ホストサーバに搭載されているNICのインターフェース名を指定します。インターフェース名は、ホストサーバ上で「ifconfig」コマンドから確認することができます。

コマンド行の最後に作成するmacvlanネットワークの名前を追加することができます。

$ docker network create -d macvlan --subnet=192.168.11.0/24 --gateway=192.168.11.1 -o parent=enp0s25 test_macvlan
49da1b896ce9ce5cd89d64ef8f2dc380ccf4f211945c5dccde94775ce88ed764

$ docker network ls
NETWORK ID     NAME           DRIVER    SCOPE
42333a39cd15   bridge         bridge    local
6be4541b5f79   host           host      local
894265cd5830   none           null      local
49da1b896ce9   test_macvlan   macvlan   local

作成したmacvlanの詳細を、docker network inspectコマンドで表示します。

$ docker network inspect test_macvlan
[
    {
        "Name": "test_macvlan",
        "Id": "49da1b896ce9ce5cd89d64ef8f2dc380ccf4f211945c5dccde94775ce88ed764",
        "Created": "2023-02-19T15:22:41.453444023+09:00",
        "Scope": "local",
        "Driver": "macvlan",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.11.0/24",
                    "Gateway": "192.168.11.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "parent": "enp0s25"
        },
        "Labels": {}
    }
]

Docker network createコマンドで指定した通りのmacvlanネットワークが作成できました。

macvlanにコンテナを接続する

次に、作成したmacvlanネットワークにAlpine Linuxコンテナを接続してみます。今回は、外部からAlpine LinuxコンテナにSSHで接続できるように設定します。

まず、Dockerfileを利用してAlpine LinuxのOSイメージにrootのパスワード(任意)とSSHデーモンを追加していきます。また、SSHデーモンを起動するために必要なopenrcというinitサービスも追加します。

Dockerfileは、以下のように記述します。

FROM alpine:latest

USER root

RUN echo root:rootabcd | chpasswd

RUN apk update && apk add --no-cache openrc openssh bash

RUN echo "Port 2222" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN mkdir -p /run/openrc
RUN touch /run/openrc/softlevel
RUN rc-update add sshd

ENTRYPOINT ["sh", "-c", "rc-status; rc-service sshd start; /bin/bash"]

rootのパスワードは「rootabcd」に設定します。SSHのポートはデフォルトの「22」から「2222」に変更します。また、コンテナ起動時にSSHデーモンを起動するように設定します。

Dockerfileの設定が完了したら、alpine LinuxのOSコンテナイメージを「alpine:1.0.0」というタグでbuildしていきます。

$ docker build -t alpine:1.0.0 -f Dockerfile .
Sending build context to Docker daemon  7.168kB
Step 1/10 : FROM alpine:latest
・・<中略>・・
Successfully tagged alpine:1.0.0

$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
alpine       1.0.0     37ceca0c1f2c   45 seconds ago   19.7MB
alpine       latest    b2aa39c304c2   8 days ago       7.05MB

OSコンテナイメージが作成されたので、コンテナ起動します。その際、先ほど作成したmacvlanネットワークに接続するように指定します。

docker runコマンドの「–network」オプションで接続するネットワークを指定します。「–ip」オプションでコンテナに割り当てるIPアドレスを指定します。

$ docker run -it --rm -d --network test_macvlan --ip 192.168.11.2 --hostname test-alpine01 --name test-alpine01 alpine:1.0.0
2098d875c80e2c4d75d1db60e4d397af5985f9ad80aa2bed6ae0d8706af13a03

$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS     NAMES
2098d875c80e   alpine:1.0.0   "sh -c 'rc-status; r…"   36 seconds ago   Up 34 seconds             test-alpine01

macvlanに接続されたAlpine Linuxコンテナが立ち上がりました。コンテナに接続して、ネットワーク設定を確認します。

$ docker exec -it test-alpine01 /bin/sh

$ hostname
test-alpine01

$ ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:0B:02
          inet addr:192.168.11.2  Bcast:192.168.11.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:304 errors:0 dropped:164 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:18778 (18.3 KiB)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

「test-alpine01」という名前のホストが192.168.11.2のIPアドレスでmacvlanネットワークに接続されていることが確認できました。

外部からコンテナにSSH接続する

今度は、PCからTera Termを使って、外部からAlpine Linuxのコンテナにアクセスしてみます。

接続するコンテナ「test-alpine01」のIPアドレスは192.168.11.2です。ポートは「2222」で接続します。

Tera Termの接続のイメージ

ユーザー名は「root」で、パスワードはDockerfileで設定した「rootabcd」を入力します。入力後、「OK」ボタンを押します。

Tera Termのログインのイメージ

ログインできたら、ホスト名とネットワーク設定を確認してみます。

Welcome to Alpine!

The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.

You can setup the system with the command: setup-alpine

You may change this message by editing /etc/motd.

test-alpine01:~# hostname
test-alpine01

test-alpine01:~# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:0B:02
          inet addr:192.168.11.2  Bcast:192.168.11.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1198 errors:0 dropped:695 overruns:0 frame:0
          TX packets:70 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:78076 (76.2 KiB)  TX bytes:8962 (8.7 KiB)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

macvlanネットワークを利用することで、Dockerを起動しているホストのネットワークインターフェースに作成された仮想NIC(eth0)を経由して、外部とコンテナが通信できていることが確認できました。

また、macvlanネットワークで接続されたコンテナ同士も通信することが可能ですので、ぜひ試してみてください。

ちなみに、macvlanで接続されたコンテナは、ホストのOSとは通信することができないので、この点は注意してください。

まとめ

いかがでしたでしょうか。今回は、Dockerのmacvlanネットワークでコンテナを接続して、外部とコンテナを通信させる方法について解説しました。

macvlanを使うことで、ネットワークに関する専門的な知識がなくても、外部と通信できる論理ネットワークを簡単に作成することができます。

今回のように、Alpine Linuxコンテナ内でSSHデーモンを起動させ、macvlanネットワークに接続することで、外部から普通のLinuxサーバとしてアクセスすることができるので、開発や研究用途のサーバとしても十分活用できそうですね。

Dockerfileは、OSコンテナイメージをビルドする際に、サービス設定、ファイルシステム設定やユーザ設定も行うことができるため、何度でも同じ環境を簡単に再現することを可能にするIaC(Infrastructure as Code)と言うことができます。

IaCは、システム開発や運用の現場だけではなく、サイバーセキュリティの分野でも大変注目されています。

もし、システムがサイバー攻撃を受けてしまった場合でも、攻撃された一部分のコンテナを潰して新しいコンテナとして起動させることで、速やかにサイバー攻撃の影響を排除してシステムを正常復旧させることができます。しかも、自動化することで作業工数も少なくすることも可能です。

Dockerを体系的に学習することで、DevOps(または、DevSecOps)やCI/CDに対する理解も深まることでしょう。

参考になれば幸いです。

システムのお悩みについてご相談ください

本サイトの掲載内容に関するお問い合わせは、こちらから承ります。
SOHOのシステム運用管理に関するお悩みごとについて、なんでもお気兼ねなくご相談ください。
現役システムエンジニアのスタッフが、ボランティアでご相談にご対応させていただきます。