【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その5:Dockerのネットワークってどうなってるの? 〜

コンテナ・Docker

こんにちは、サイオステクノロジー武井です。いよいよ佳境に入ってきた連載「世界一わかりみが深いコンテナ & Docker入門 」ですが、今回はDockerのネットワークです。

★★★ 緊急告知 ★★★

本記事の内容をベースとした弊社主催オンラインセミナーを実施致します!!文字だけでは伝わらないエキサイティングな内容をお届けします!!

【2020/11/27(金) 19:00〜】サイオステクノロジーのエンジニアが語る!これを見ればお茶の子さいさいおすすめ最新テクノロジー!
https://tech-lab.connpass.com/event/193736/

全7回シリーズでお届けする予定で、今回は第5回目となります。

  1. その1:コンテナってなに?
  2. その2:Dockerってなに?
  3. その3:Dockerfileってなに?
  4. その4:docker-composeってなに?
  5. 今回はこちら → その5:Dockerのネットワークってどうなってるの?
  6. その6:Dockerのファイルシステムってどうなってるの?
  7. その7:実践!!Dockerでアプリケーション開発!!(執筆中)

Dockerのネットワークってどうなってるの?

今までDockerfileやdocker-composeでコンテナを作成した際、コンテナ間で通信できたり、コンテナから外部のネットワークへ通信できたりしましたが、今回はそのメカニズムについて説明します。

Dockerのネットワークは、大別しますと、none、host、bridgeという3つの構成を取ることができます。ただし、まず基本を理解するためには、最もよく使われる方式を説明するのが一番かと思いますので、Dockerのデフォルトネットワークであり最もよく使われているbridgeを説明します。

で、Dockerのネットワーク構成ですが、コンテナを作成すると、PCの中には以下のような構成が出来上がります。

コンテナには仮想NICがついています。そして、それが仮想スイッチに接続されています。そして仮想スイッチにはもう一つの仮想NICが接続されていて、OSがもつルーティングの機能により、仮想NICあてのパケットは物理NICにルーティングされます。そして、インターネットなどの外部ネットワークに出ていきます。

「仮想スイッチ」「仮想NIC」などの言葉が出てきますが、これはLinuxのOSが仮想的なNICやスイッチを作り出す機能を持っています。便利ですよね。これらの仮想なんたらによって、L2の通信に必要なEthernetプロトコルをPCの内部でしゃべることができます。こういう便利な機能があるので、コンテナ側で稼働するOSは、物理サーバーで動作してたときと全く同じように振る舞うことができます。

ちょっとまだしっくりこない人もいるかもしません。特に仮想スイッチに接続されている仮想NICで仮想NIC単体であって、どのコンテナにも接続されていない宙ぶらりんなNICがあります。私は最初この意味がわかりませんでした。

こういう場合は、従来のテクノロジーで実現した場合と比較してみるのがいいと思います。

ということで、先のネットワーク構成を物理サーバーで実現した場合と比較してみることにしました(図がちっちゃいのでクリックして拡大して見て下さい)。

 

上が先程ご紹介したDockerのネットワーク構成図、下がそれを物理環境に置き直した図です。上図の上半分「Dockerで構築した環境」にある「コンテナ1」「コンテナ2」が、それぞれ下半分「物理環境に置き直した図」の「サーバ1」「サーバ2」に相当します。仮想スイッチは物理スイッチに、仮想NICは物理NICに相当します。

つまり、物理環境でいうところの、物理スイッチにサーバーが2台つながっていて、さらに物理スイッチからPCの2つあるNICの方の片方につながっていて、PC内でルーティングして、もう一方の物理NIC経由でインターネットに出ていくのですが、これをPCの内部で仮想的にエミュレートしたのが「Dockerで構築した環境」なのです。

いかがでしょうか?なんとなく雰囲気がつかめてきましたでしょうか?私はこのように考えると、とても頭がスッキリしました。

どうやって実現しているの?

では、Dockerで利用される仮想的なネットワークを作成するためには、3つの技術が必要になります。順に紹介していきます。

Network Namespace

ルーティングテーブルなどネットワークを動作させるのに必要な機能が完全に分離された環境を作るためのものです。

なかなかに説明だけではわかりにくいので図を交えてみます。このようなややこしいことを理解するためには、「それがない世界」と「それがある世界」で比較してみるのがいいと思います。ということで、「Network Namespaceがない世界」と「Network Namespaceがある世界」を比べてみたいと思います。

まずは、「Network Namespaceがない世界」です。そう言えば、Dockerのネットワークでは、複数のネットワークインターフェースをPC内に作りたいのでした。では仮想NICを作ってみましょう(仮想NICの作り方は後ほどご説明します)。

Network Namespaceがない世界では、仮想NICはLinuxのルーティング機能を使って、ルーティングされてしまいます。それでは困ります。

 

だって、最終的に作りたいのは以下のようなネットワークです。以下のネットワークでは、コンテナ内の仮想NICは仮想スイッチに接続します。ルーティングされて物理NICにパケットが届くようなことがあってはいけないのです。

 

なので、以下のようにNetwork Namespaceで区切ると独立したネットワーク空間が作成できます。

Network Namespaceはip netnsコマンドで作成することができます。カーネルが持っている機能です。特に深いことは考えずに、Linuxのカーネルはこういう機能を持っているっていう理解で十分と思います。そして、後ほど実際にNetwork Namespaceを作ってみます。実際に手を動かすと、より理解が深まりますので、いまのところぼやっとした理解でも大丈夫ですので、ご安心下さい。

仮想スイッチ

PC内で物理のL2スイッチと同じように振る舞うのが仮想スイッチと言われるものです。これもカーネルが持っている機能で、ip linkコマンドで作成することができます。

この仮想スイッチは物理のL2スイッチと同じようにEthernetのプロトコルをしゃべることができます。なので、コンテナの中にあるOSは、物理サーバーの中で稼働するときと同じように振る舞うことができるのです。コンテナのためのネットワークのために新たなプロトコルで通信する必要はありません。物理環境で動いていたときのものをPCの中でエミュレートしているので、コンテナの中にあるOSは、その稼働環境が物理環境なのかコンテナなのかを意識する必要はありません。

仮想ネットワークインターフェース

先程の図の中で、「仮想NIC」と書いてあったものです。物理ネットワークで動いているEthernetを仮想的に再現するためには、先程ご紹介した仮想スイッチだけではなく、仮想NICも必要です。これもLinuxのカーネルが持っている機能で作ることができます。これもip link addコマンドで作成することができます。

実際にネットワークを作ってみよう!!

では、先程ご紹介した3つの技術「Network Namespace」「仮想スイッチ」「仮想ネットワークインターフェース」を使って、ネットワークを作ってみましょう。実際にDockerでネットワークを作成するときも、これらの技術が使われていますが、Dockerコマンドを使うと、Dockerが勝手に裏側でやってくれるので、普段は意識しなくてもよいです。でも、Dockerのネットワークへの理解を深めるためには、やっぱり、低レベル(?)のコマンドを使ってやってみるのが一番です。では、やってみましょう。

まず、ゴールを確認します。最終的に作りたいのは以下のようなネットワークです。

 

では、順を追って構築してみます。最初は何にもありません。

Network Namespaceの作成

まずNetwork Namespaceを2つ作ります。Network Namespace周りのコマンドは、ip netnsというのが基本で、Network Namespaceを追加するためには、ip netns addというコマンドを実行します。書式は以下のとおりです。

ip netns add [Network Namespace名]

ということで、ns1、ns2という名前のNetwork Namespaceを作成します。

できあがったNamespaceの中でコマンドを実行して、中の状態がどの様になっているかを見てみます。書式は以下のとおりです。

ip netns exec [Network Namespace名] [コマンド]

Network Namespaceの中で、ip lコマンドを実行してインターフェースの状態を確認します。

 

Loopbackインターフェースしかないようです。しかもDownしていますので、Upしましょう。

 

ということで現在の状態は以下の通りとなりました。ns1、ns2というNetwork Namespaceの中にLoopbackインターフェースが2つある状態です。

仮想スイッチの作成

仮想スイッチを作成します。これは特に難しいことはありません。コマンドの書式は以下のとおりです。

ip link add [仮想スイッチ名] type bridge

 

では、早速作ってみましょう。

 

できているかどうか確認してみます。

 

問題なくできてますね。そして今の状態は以下の図のとおりです。

仮想ネットワークインターフェースの作成

仮想ネットワークインターフェース(以下仮想NIC)をします。先ほど作成したNetwork NamespaceはLoopbackインターフェースしかないですし、今後作成する仮想スイッチにもネットワークインターフェースが必要です。

ということで、今回のゴールをまず確認します。今回のゴールは下図の赤枠の部分の仮想NICを作ることです。

では作ってみます。仮想NICは必ずペアで作成します。コマンドの書式は以下のとおりです。

ip link add name veth [仮想NIC名] type veth peer name [対向の仮想NIC名]

今回は以下の名前の仮想NICを作成します。

ns-veth1
先程作成したNetwork Namespaceであるns1の方に接続される仮想NIC
br-veth1 ns-veth1とペアで作成され、仮想スイッチに接続される仮想NIC
ns-veth2
先程作成したNetwork Namespaceであるns2の方に接続される仮想NIC
br-veth2
ns-veth2とペアで作成され、仮想スイッチに接続される仮想NIC
rt-veth
物理NICとルーティングされる仮想NIC
br-veth3
rt-vethとペアで作成され、仮想スイッチに接続される仮想NIC

 

ということで以下のコマンドを実施します。

 

できたかどうか確認してみましょう。

 

問題なくできてます。現在の状態は以下のとおりです。必要な仮想NICのペアは作成されました。しかし、どこにも接続されていません。次は、これらの仮想NICをNetwork Namespaceや仮想スイッチに接続する作業を行います。

Network Namespaceへの仮想ネットワークインターフェースの接続

Network Namespaceのns1、ns2に仮想NICを接続します。コマンドの書式は以下のとおりです。

ip link set [仮想NIC名] netns [Network Namespace名]

 

以下のコマンドを実行します。

 

確認してみましょう。先程までPC上で見えていた仮想NICのns-veth1とns-veth2が消えてしまいました。どこへ行ってしまったのでしょうか?

 

Network Namespaceの中を見てみますと、やはり先程のns-veth1とns-veth2はNetwork Namespaceの中に移動していることがわかります。これでバッチリです。

 

そいで、今の状態は以下のような感じです。

仮想スイッチへの仮想ネットワークインターフェースの接続

次は先程Network Namespaceに接続した仮想NICの対向の仮想NICを仮想スイッチに接続しましょう。コマンドの書式は以下のとおりです。

ip link set dev [仮想NIC名] master [仮想スイッチ名]

 

早速やってみましょう。

 

仮想スイッチにちゃんと仮想NICが結びついているかどうか確認します。

 

バッチリですーヮ(゚д゚)ォ!

今の状態は以下の図のとおりです。

仮想NICと仮想スイッチのUp

各仮想NICと仮想スイッチはデフォルトではDownという状態で使えません。これをUpという状態にして使えるようにします。

 

IPアドレスを付ける

このままではまだIPアドレスが付与されていません。よって、つけましょう!!

 

今の状態はこんな感じです。

動作確認しましょう!!

ns1からns2へPingを打ってみましょう。以下のような感じですね。

 

では、早速Pingってみます。

完璧ですーヮ(゚д゚)ォ!

では次にGoogleのパブリックDNS(8.8.8.8)にPingを打ってみましょう。

あれあれ?ダメでした。PC内ならOKですが、外部のネットワークには到達しません。実はこれには理由が3つあります。

■ IP転送が有効になっていない

デフォルトでは、IP転送(PC内の異なるNIC間でのパケットのルーティング)が有効になっていません。つまり、以下の図で言うところのrt-vethと物理NIC間でパケットが転送されません。これを実現するには特殊な設定をする必要があります。

■ Network Namespace内でデフォルトゲートウェイが設定されてない

同じサブネット内(192.168.0.0/24)あてのパケットならいいですが、それ以外の宛先のパケットの場合、どこに届けるべきかを設定する必要があります。つまりデフォルトゲートウェイの設定なのですが、それが現在されていません。

■ NATされていない

ns1というNetwork Namespaceから8.8.8.8あてのパケットの送信元IPアドレスと宛先IPアドレスは以下のようになっています。

送信元IPアドレス
192.168.0.1
宛先IPアドレス
8.8.8.8

このアドレスでは外部ネットワークへは届きません。送信元IPアドレスがプライベートアドレスだからです。インターネットに出るときには、送信元を物理NICのIPアドレスに変換する必要はあります。つまりNATする必要があります。

送信元IPアドレス
物理NICのIPアドレス
宛先IPアドレス
8.8.8.8

外部ネットワークへの接続を有効にする

ということで、外部ネットワークへの接続を有効にする設定をしましょう。

IP転送の有効

以下の設定を行います。これだけでOKです。

デフォルトゲートウェイの設定

ちょっと確認してみると、たしかにデフォルトゲートウェイがありません。

 

Network Namespaceであるns1およびns2のデフォルトゲートウェイをrt-veth(192.168.0.100)にします。

 

では確認してみましょう。

ばっちりっす(^o^)

NATの設定

では、最後にNATの設定をしましょう。iptablesコマンドを利用します。iptablesはパケットの書き換えを行う機能です。ここではその詳細は説明しませんが、以下のコマンドは、ルーティングが終わった後に、送信元が192.168.0.0/24で、かつeth0というネットワークデバイスから出ていくパケットの送信元アドレスをeth0のアドレス、つまり物理NICのアドレスに変更するという内容です。

再び動作確認!!

再び動作確認してみます。

できたぞーヮ(゚д゚)ォ!

どんなふうにパケットが届いているの?

この動きをさわにわかりやすくするため、今のパケットの流れを図にしてみました。まずNetwork Namespaceのns1から出たパケットは、宛先が192.169.0.0/24宛ではないので、先程設定したデフォルトゲートウェイの設定により、rt-veth(192.168.0.100)に向かいます。

 

IP転送の設定によるrt-vethから物理NICにルーティングされます。

 

iptablesコマンドで行ったNATの設定により、送信元IPアドレスが物理NICのIPアドレスに変換されます。

 

パケットがインターネットに出ていきます。

こんな感じでパケットは外部ネットワークに出ていきます。

補足

先程以下の図の赤枠の仮想NICのペアを作成するときには結構面倒なことをしたと思います。

 

こんなことしたと思います。

  • br-veth3とrt-vethという仮想NICのペアの作成する
  • br-veth3 を仮想スイッチに接続する
  • rt-vethにIPアドレスを付与する。

コマンドにするとこんなことしましたよね。まず、br-veth3とrt-vethという仮想NICのペアの作成して、、、

 

br-veth3 を仮想スイッチに接続して、、、

 

rt-vethにIPアドレスを付与します。

 

こんなめんどくさいことしましたよね。。。

 

実は上記の3つのコマンドは、以下のコマンドで一発で代用できたりします。

 

仮想スイッチにIPアドレスを付けるというコマンドです。仮想スイッチにIPアドレスっていうのがなんともわかりにくいのですが、要は以下の赤枠のものを作ることと同じことをやっているのです。

 

仮想スイッチの状態を見ると、こんな見え方をします。

 

仮想スイッチにIPアドレスが付いているのです。私、最初みたとき戸惑ったのです。なんでL2のレイヤーであるはずの仮想スイッチにIPアドレスが付くのだろうと。。。でも実は、くどいようですが、これは以下の図の赤枠の部分を作る処理を簡略化したに過ぎないらしいです(同じ図を3度も登場させてすみません)。

 

ややこしいですね。実際にDockerでネットワークを作成するときも、仮想スイッチにIPアドレスが付きます。

Dockerを使えばこんなに簡単!!

さて、先程ipコマンドを色入こねくり回して作ったネットワークですが、実はこれ、dockerコマンドを使ってコンテナを作ると特に意識することもなく出来てしまいます。

CentOSのコンテナを2つ作ってみましょう。

 

実はこれだけで既に以下のようなネットワークが出来上がります。

 

docker0というのは、Dockerをインストールしたときにデフォルトで作成される仮想スイッチです。特に何も指定せずコンテナを作成すると、このdocker0に接続されます。docker0にはIPアドレスが付与されており、コンテナのデフォルトゲートウェイはこのアドレス(172.17.0.1)になっています。新しく2つのコンテナを作成したので、「veth9f0eb93」「veth7010a0b」という新たに2つの仮想NICが出来上がっています。

 

docker0の仮想スイッチに接続されている仮想NICを見てみます。確かに先程の「veth9f0eb93」「veth7010a0b」という仮想NICがdocker0という仮想スイッチに接続されているのがわかります。

 

では、各コンテナのデフォルトゲートウェイも調べてみましょう。docker inspectコマンドでコンテナの詳細情報を見ることが出来ます。–formatオプションを使うと、出力する情報を整形出来ます。デフォルトゲートウェイの情報のみを出すように整形してみます。

たしかにデフォルトゲートウェイは、docker0のIPアドレスになっていました。

Dockerであれば、ややこしいNetwork Namespaceや仮想スイッチ、仮想NICの作成をすべてやってくれるのです。スゴイですね、Dockerコマンド。

まとめ

★★★ 緊急告知 ★★★

本記事の内容をベースとした弊社主催オンラインセミナーを実施致します!!文字だけでは伝わらないエキサイティングな内容をお届けします!!

【2020/11/27(金) 19:00〜】サイオステクノロジーのエンジニアが語る!これを見ればお茶の子さいさいおすすめ最新テクノロジー!
https://tech-lab.connpass.com/event/193736/

いかがでしょうか?なかなかにややこしいDockerのネットワークについて、なるべくわかりみの深い説明をしたつもりです。本記事がみなさまのお役に立てれば幸いでございます。もしお役に立てたら、いいねをしたりTwitterとかSNSで拡散したりコメントしたりしてくれると、とってもうれしいです(/ω・\)チラッ

次回は、Dockerのファイルシステムについて説明いたします。

【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その6:Dockerのファイルシステムってどうなってるの? 〜





ご覧いただきありがとうございます。
ブログの最新情報はSNSでも発信しております。
ぜひTwitterのフォロー&Facebookページにいいねをお願い致します!



>> 雑誌等の執筆依頼を受付しております。
   ご希望の方はお気軽にお問い合わせください!


ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

38人がこの投稿は役に立ったと言っています。

「【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その5:Dockerのネットワークってどうなってるの? 〜」への4件のコメント

  1. めちゃくちゃ分かりやすいです!
    続きを楽しみにしております。

    • ありがとうございます!!そう言っていただきますと執筆意欲100倍です。続きもがんばります(๑•̀ㅂ•́)و✧

  2. サイオステクノロジー 武井さん

    とても勉強になりました!
    他社さんが公開しているDocker ハンズオンの内容に沿って
    Docker ネットワークを使って通信を行うことをやってみたものの、実態として何が起きているのかはよく分からず…。
    (ハンズオンなんで仕方ないですが)

    コマンド例と図解付きでよく理解できました!わかりみ深いです!!!(/・ω・)/

    お忙しいと思いますが続編もお待ちしています。
    よろしくお願いします!

    • ウチイダユウゴ様

      武井です。ありがとうございます!!そう言っていただきますと、ますます執筆意欲がわきてきます(●´ω`●)続編もがんばりますー。

コメント投稿

メールアドレスは表示されません。


*