1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Dockerにおけるmacvlanという技術

Last updated at Posted at 2025-02-09

Dockerのコンテナネットワークにおいて外部ネットワークとの接続でmacvlanを使ったので、その仕様や特性について述べます。

初めに

複数dockerコンテナを立ち上げ、外部ネットワークから特定のコンテナに対してIPを用いた直接通信しようとした際にmacvlanを使うと解決したので述べます。
前提知識として、以下の知識があれば理解しやすいかと思われます。

  • Dockerを触ったことがある
  • コンテナネットワークについてある程度の知識がある
  • ネットワークインタフェース周りの話についてある程度の知識がある

Dockerのデフォルトネットワーク

macvlanの話をする前に、Dockerのデフォルトネットワークについて軽く触れます。
内容としてはDocker-docs-jaからの内容からこの記事に関する部分についてざっくり解説しているので、詳細な説明はリンク先で確認してください。

ホストマシン(コンテナホスト)にDockerをインストールすると自動的に3つのネットワークが作成されます。

sh
$ docker network ls
NETWORK ID          NAME                DRIVER
7fca4eb8c647        bridge              bridge
9f904ee27bf5        none                null
cf03ee007fb4        host                host

これらのネットワークは、コンテナを実行する際に --netフラグで指定可能であり、下記の表は大まかな説明です。

名前 説明
bridge ホストマシンのカーネル内で動作するソフトウェアブリッジで、フラグ指定しない場合にコンテナが接続される
none コンテナはネットワークインタフェースを持たない状態で起動する
host コンテナはホストマシンのネットワークスタックを直接利用

noneに関しては、説明通りなのでこの記事では触れません。
bridgehostについてはもう少し詳細を述べます。

bridgeネットワーク

ホストマシン内でネットワークインタフェースを確認するとdocker0というインタフェースが作成されていることが確認できます。

sh
ubuntu@ip-172-31-36-118:~$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:47:bc:3a:eb
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:47ff:febc:3aeb/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
          RX packets:17 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1100 (1.1 KB)  TX bytes:648 (648.0 B)

このdocker0は、Dockerがデフォルトで作成する仮想ブリッジインタフェースであり、コンテナ間通信やホストとの通信を仲介します。詳細については以下のようになっています。

sh
$ docker network inspect bridge
[
   {
       "Name": "bridge",
       "Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
       "Scope": "local",
       "Driver": "bridge",
       "IPAM": {
           "Driver": "default",
           "Config": [
               {
                   "Subnet": "172.17.0.1/16",
                   "Gateway": "172.17.0.1"
               }
           ]
       },
       "Containers": {},
       "Options": {
           "com.docker.network.bridge.default_bridge": "true",
           "com.docker.network.bridge.enable_icc": "true",
           "com.docker.network.bridge.enable_ip_masquerade": "true",
           "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
           "com.docker.network.bridge.name": "docker0",
           "com.docker.network.driver.mtu": "9001"
       }
   }
]

デフォルトネットワーク以外にも、ユーザ定義でブリッジネットワークやオーバーレイネットワークを作成できますがここでは割愛します。
ここで重要なのは、docker0がデフォルトで Network Address Translation(NAT) を利用するということです。
つまり、コンテナの内部IPはホストマシンの外部ネットワークからは直接アクセスすることが不可能です。
この問題は、ポートフォーワードやリバースプロキシを使うことで回避できますが、オーバーヘッドが増加したり、レイテンシが発生したりするため留意する必要があります。

hostネットワーク

hostネットワークを指定すると、コンテナは独自の仮想ネットワークを持たず、ホストマシンと同じネットワークインタフェースを使用します。
特徴としては以下のような特徴があります。

  1. ネットワークの分離がない
    • コンテナには、ホストマシンのIPアドレスが適用される
    • コンテナは、コンテナ独自のIPを不保持
  2. ポートマッピングが無効
    • -pオプションによるポートマッピングが無効
    • ホストマシンとコンテナは同じポートを利用
    • ポート競合に留意する必要あり
  3. パフォーマンス向上の可能性
    • 仮想ネットワークを利用しないためレイテンシが削減
    • ホストマシンとコンテナ間での通信が多い場合に効果が発揮
  4. セキュリティリスク
    • コンテナがホストマシンに完全に統合されることから、他のコンテナやホスト上のプロセスと直接通信可能
    • コンテナの分離が必要な環境では避けるべき

hostネットワークの場合では、ホストマシンとコンテナは同一のIPであるため、IPアドレスによる外部ネットワークからの直接指定は不可能です。

macvlanネットワーク

ようやく本題のmacvlanについて語っていくのですが、その前に軽く関連する知識について述べます。
既知の方は読み飛ばしてください。

関連知識

Virtual LAN(VLAN)

物理的なネットワークの構成に関係なく、論理的にネットワークを分割する技術です。
スイッチなどのネットワーク機器を用いて、異なるネットーワークセグメントを作成し、特定のグループ内のデバイス同士のみが通信可能となるようにします。
VLANには以下の種類があります。

  • ポートベースVLAN:スイッチの特定ポートをVLANに割り当て
  • タグVLAN(IEEE 802.1Q):VLAN IDをパケットに付与して、異なるVLANを識別
  • MACアドレスベースVLAN:MACアドレスごとにVLANを割り当て

今回の記事ではMACアドレスベースVLAN(主役)とタグVLAN(少しだけ)の話が関連します。

Macvlan(Linux)

Linuxカーネルが提供する仮想ネットワークインタフェースの一種で、1つの物理ネットワークインタフェース(NIC)を仮想的に複数のインターフェースに分割する技術です。
Macvlanは以下のような特徴を持ちます。

  • vethbridgeと異なり各仮想インタフェースに異なるMACアドレスを割り当てる
  • 物理NICを共有しながら異なるIPを利用可能
  • ホストと仮想NIC間の通信制限(デフォルトではホストと仮想NICで通信不可能だが、別途bridgepassthruを利用することで解決可能)

Dockerにおけるmacvlanネットワーク(本題)

Dockerでは、Macvlanドライバを使用すると、コンテナに物理NICと同じL2ネットワーク上の独立したMACアドレスとIPアドレスを割り当てることが可能となります。デフォルトのDockerネットワークとは異なり、コンテナがホストマシンとは完全に独立したネットワークデバイスとして振る舞うという特徴があります。

macvlanネットワークの機能

以下に主な機能を記載します。

  1. 各コンテナが個別のIPアドレスとMACアドレスを保持
    • デフォルトネットワークでは、NATを利用してIPアドレスを共有するが、macvlanではコンテナごとに異なるIPアドレスとMACアドレスを保持可能
    • このMACアドレスは静的に指定および固定が可能
  2. デフォルトではホストマシンとコンテナ間の直接通信は不可能
    • macvlanネットワーク作成時にparentを明示的に指定し、bridgeモードを使用することでホストマシンとの通信も可能
    • 同一のmacvlanに存在する他のコンテナに対してはL2での通信が可能
  3. コンテナがホストの物理ネットワークと統合
    • 同一LAN内の他のデバイス(ルータ、スイッチ、他のサーバー)とも直接通信可能
    • DHCPサーバからコンテナに対してIPアドレスを割り当てることも可能

これらの機能により、ホストマシンに接続しているL2スイッチなどから見た場合、物理NICでは単一だが論理的には、ホストマシンとコンテナがそれぞれ独立したデバイスに見えます。
利用するときの注意点として、1つの物理インタフェースが複数のMACアドレスを割り当て可能とするため、ネットワーク機器にプロミスキャスモード機能が必要となります。

スクリーンショット 2025-02-10 2.15.07.png

タグあり(VLAN付き)macvlan

  • VLAN IDを指定することで、コンテナごとに異なるVLANへ接続可能
  • VLAN IDを持つネットワークはスイッチの設定が必要
  • 複数のVLANにまたがるコンテナネットワークを作成し、セグメントを分離可能

例:VLAN 100 (192.168.100.0/24)に接続するmacvlanネットワークの作成

sh
docker network create -d macvlan \
  --subnet=192.168.100.0/24 \
  --gateway=192.168.100.1 \
  -o parent=eth0.100 \
  my_vlan100_net

eth0.100はVLAN100に属する仮想NICです。
VLAN 100内のデバイスと通信可能になります。
VLANタグが付与されるため、パケット長が変わります。(MTU 1504以上を許容する設定に変更することで解決可能)

タグなしmacvlan

  • VLANタグ(802.1Q)が付与されない
  • コンテナはホストの物理NICが属するデフォルトVLAN(通常はVLAN ID 1)を使用

例:物理NICeth0を使用し、コンテナが192.168.2.xのネットワークに直接接続する場合

sh
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  my_macvlan_net

コンテナは192.168.1.0/24のネットワークに直接接続します。
ルータや他のデバイスとも直接通信が可能となります。
VLANタグがないため、パケット長の変化はありません。

タグありとタグなしの使い分け

項目 タグあり(Tagged) タグなし(Untagged)
VLAN IDの管理 VLAN IDを設定 なし
ネットワークの分離 VLANごとに分離 物理ネットワークと同じ
スイッチの設定 VLANタグ対応が必要 不要
用途 マルチテナントやセグメント化  シンプルなネットワーク統合

設定と使用例(タグなし)

(1)macvlanネットワークを作成

sh
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  my_macvlan

(2)macvlanネットワークにコンテナを接続

sh
docker run -it --rm --network=my_macvlan --name my_container alpine sh

my_container192.168.1.xのIPを持ち、物理ネットワーク上のデバイスと通信可能になります。

設定と使用例(タグあり)

(1)macvlanネットワークを作成

sh
docker network create -d macvlan \
  --subnet=192.168.100.0/24 \
  --gateway=192.168.100.1 \
  -o parent=eth0.100 \
  my_vlan100_net

(2)macvlanネットワークにコンテナを接続

sh
docker run -it --rm --network=my_vlan100_net --name vlan_container alpine sh

vlan_containerはVLAN 100に属し、他のVLANとは分離されます。

まとめ

今回の記事では主にDockerにおけるmacvlanネットワークについて記載しました。
コンテナをL2で独立した端末のように見せたい場合や、IPによる直接通信をしたい場合に活用すると良いと思います。

おまけ(1コンテナ1プロセス)

コンテナを触れたことがある人なら、一度は聞いたことがある「1コンテナ1プロセス」の原理ですがその詳細についてはご存知でしょうか。
大前提の理由として以下のものがあります

  • コンテナはプロセスの実行単位として扱われる
  • シンプルな設計で管理しやすい
  • プロセスのスケールや監視が容易になる
  • フェイル時に迅速に復旧可能

これらの原則に基づき、コンテナは単一のプロセスを起動するために使用されます。

PID 1 zombie reaping problem

Linuxシステムでは、すべてのプロセスに一意のプロセスID(PID)が割り当てられ、親プロセスと子プロセスの関係が形成される。

PID 1の役割

  • Linuxカーネルはシステム起動時にinit(またはsystemd)プロセスをPID 1に割り当てる
  • PID 1はすべての孤立した子プロセスを引き継ぎ、適切にwait()してゾンビプロセスを回収する責任を持つ

コンテナ環境においては、コンテナのエントリーポイントとなるプロセスがPID 1になります。一般的にENTRYPOINTCMDで指定されたプロセスがPID 1になります。

zombie process

  • プロセスがexit()すると、その終了ステータスは親プロセスがwait()するまで保持される
  • 親プロセスが適切にwait()しない場合、子プロセスのエントリーがプロセステーブルに残り、ゾンビプロセスとなる
  • ゾンビプロセスが大量に発生すると、システムリソース(プロセステーブル)が枯渇し、システムの不安定化を引き起こす可能性

コンテナにおけるzombie process problem

コンテナ内のプロセスがPID 1の場合、適切に子プロセスを管理しないとゾンビプロセスが発生しやすくなります。

  • 多くのアプリケーションはPID 1として動作することを想定していないため、子プロセスのwait()を適切に処理しない
  • その結果、ゾンビプロセスが増加し、コンテナの安定性に悪影響を与える

対処としてはtinidumb-initなどを使用しプロセスを管理する方法が挙げられます。
設計思想の他にこういう理由もあり、1コンテナ1プロセスを推奨しているのだと思われます。

参考記事まとめ

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?