UZABASE Tech Blog

株式会社ユーザベースの技術チームブログです。 主に週次の持ち回りLTやセミナー・イベント情報について書きます。

マルチホストでのDocker Container間通信 第2回 Port Forwarding と Overlay Network

こんにちは。SPEEDA開発チームの鈴木です。
前回はマルチホストでのDocker Container間通信の説明の前段として、Dockerのネットワークが次のようになっているという話をしました。  
 
f:id:kenji-suzuki:20170710131809p:plain

今回はいよいよ、マルチホストでどうやってDocker Container同士の通信を実現するのかを説明していきます。

はじめに

マルチホスト間でのDocker Container通信とは、次のようにホストをまたいでDocker Container同士が通信することを指します。

f:id:kenji-suzuki:20170707164743p:plain

図で見ると大分単純ですし、前提条件次第では実際簡単に実現できるのですが、Containerのオーケストレーションを前提におくと色々な仕組みが必要なことがわかってきます。

DockerによるContainerのマルチホスト間通信

例としてA,B,C,Dという4つのDocker Containerを、複数のホストに配置するとこのようになります(Host XとYは同じネットワークに属することとします)。
※以降の説明には不要なため、vethの記載は省略します。

f:id:kenji-suzuki:20170714142929p:plain

これをネットワーク的な視点で見ると、次のような感じです。 f:id:kenji-suzuki:20170714184336p:plain

Container AとContainer Bは当然通信できます。
また、Container AからHost Yまでも問題なく通信できます。 f:id:kenji-suzuki:20170714184907p:plain

しかし、Container Cとは通信できません。なぜならContainer AとContainer Cは異なるPrivate Network上に存在するからです。 f:id:kenji-suzuki:20170714185209p:plain

この問題を解決して、異なるホストのContainerと通信できるようにする方法はいくつかあります。
その1つがDockerのPort Forwarding機能です。

Port Forwarding

Port Forwarding機能を使うとContainerの外部からContainerにアクセスできるようになります。

# Host Yの8080番ポートにアクセスしたら、Container Cの80番ポートにForwardする
host-y: $ docker run --name container-c -d -p 8080:80 nginx

f:id:kenji-suzuki:20170714190341p:plain

iptablesの設定内容を見ると、docker runで指定したようにPort Forwardingが行われる設定がされていることがわかります。

host-y:/$ sudo iptables-save 
*nat
# 略
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
COMMIT
# 略

この通信方法は「接続元のContainerが接続先のContainerのホストのIPを知っている」という条件のもとに成り立っています。つまりContainerが起動するホストが動的に変わるようなケースには対応できません。
逆に言えばホストが固定されていて、今後も変わらないならばこれで十分です。

Overlay Network

Overlay Networkとは、あるNetworkの上に別のNetworkを構築する技術です。
異なるホストのContainer同士が通信できない原因はContainerが異なるネットワークに属していることにありました。
「じゃあOverlay Network構築して同じネットワークに属するようにしてしまえばいいじゃん」というのがこの方法です。 f:id:kenji-suzuki:20170714200720p:plain

Overlay Networkを使用する場合、Containerは同じネットワークに属していることになるので、異なるホストのContainerと直接通信することができるようになります。
続いてOverlay Networkの構築方法をいくつかご紹介します。

1. Docker Swarm mode

Dockerのオーケストレーション機能であるSwarm modeでContainerのマルチホスト間通信を実現している方法が、まさにこのOverlay Networkを利用する方法です。
Swarm modeでクラスタを構築した後、Docker用のネットワーク一覧を表示するdocker network lsコマンドを実行してみると、Overlay Networkが構築されていることがわかります。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
d95e8109f33a        bridge              bridge              local
e5430ed50f3d        docker_gwbridge     bridge              local
6f6c8c7f698d        host                host                local
1q38yeph9dnj        ingress             overlay             swarm
2bae9ccfec69        none                null                local

以前のSwarmではService Discoveryとしてconsul, etcd, zookeeperなどの分散KVSが必要でしたが、Swamr modeの登場以降は不要になっています(Dockerに組み込まれたようです)。

2. 分散KVSとdocker network create -d overlayコマンドを使って自前で構築

Swarm modeがやってくれているようなことを自前でやります。いまはSwarm modeがあるので、この方法が使われることはあまりないでしょう。

3. flannelのようなOverlay Network構築ツールを使う

flannelは公式ページの冒頭に「Flannel is a simple and easy to configure layer 3 network fabric designed for Kubernetes.」とあるように、元々Kubernetes向けに作られたものです。
flannelを使用する場合、次のイメージのように各ホストにflanneldとetcdをインストールします。

f:id:kenji-suzuki:20170717220751p:plain

flannelの主な役割は2つです。

  1. etcdを使ってNetwork情報を共有することで、docker0に重複しないSubNetを割り当てる。
    (割り当て済みのSubNetのアドレス帯を共有しているので重複を避けられる)
  2. 各ホストに仮想NICflannel0を作成。Container間でpacketを送受信する際、docker0と接続されたflannel0がVXLANでpacketのカプセル化・非カプセル化を行う。

VXLANでカプセル化されたpacketは次のような構造をしています。

f:id:kenji-suzuki:20170718012341p:plain

元のpacket(Inner MAC Address 〜 payloadまで)の送信先IPはContainer CのIPですが、Container Cは仮想的には同じNetworkに属しているものの物理的には別のNetworkに属しておりルーティングできないので、上記図でルータ的役割を果たしているHost Yのeth0を宛先とする別のHeader(Outer MAC Address 〜 VXLAN Headerまで)を付加しています。このようにあるプロトコルを別のプロトコルで包むことをカプセル化といいます。

packetを受信した側は、付加されたHeaderの情報(VXLAN Headerの情報など)から受信したpacketがVXLANのpacketであることが判別できるので、包んでいるプロトコルを剥がして本来送信したかった宛先に送ることができます。 例えばHost XのContainer AからHost YのContainer Cにpacketを送信すると次のようにpacketが送信されていきます。NICの右側が、そのNICのpacketをキャプチャしたものです(簡略化しています)。

f:id:kenji-suzuki:20170726032055p:plain f:id:kenji-suzuki:20170726032225p:plain

Overlay Networkだけで解決できないこと

Port Forwardingと同様に、Containerが起動するホストが動的に変わるようなケースには対応できません。
同じネットワークに属してはいますが、同じホストに存在するわけではないので、dockerのlink機能やdocker-composeを使っての名前解決ができないからです。
Docker swarmやKubernetesなどはこの問題を解決するための仕組みをもっています。

次回は、KubernetesにおけるContainerのマルチホスト間通信と合わせてこのあたりのことを書きたいと思います。

シリーズ

第1回: マルチホストでのDocker Container間通信 第1回: Dockerネットワークの基礎
第2回: マルチホストでのDocker Container間通信 第2回: Port Forwarding と Overlay Network (当記事)
第3回: マルチホストでのDocker Container間通信 第3回: Kubernetesのネットワーク(CNI, kube-proxy, kube-dns)