Dockerネットワークを少しだけ理解してみる

この記事のまとめ:
  • Docker デーモンを立ち上げた時や、 Docker コンテナーを立ち上げた時に Linux 上でどのような設定が行われているのか概要をまとめています。
  • docker-compose を実行した時には通常の Docker コマンドとのネットワーク的振る舞いが異なるかをまとめています。
  • docker-compose を使ったネットワークの作成方法と複数の仮想イーサポートの割り当て方法をまとめています。
背景

Dockerコンテナーに対して外部端末から接続できるようにしようと思ったのですが、ちゃんとDockerのネットワークについて理解していなかったので改めて理解できるようにいろいろ試してみました。

基本構成

次のような2台のホストがルーターにつながっている構成を考えます。

Docker コンテナーを立てる

基本的には公式の「Docker コンテナ・ネットワークの理解」のページに書いてある通りですが、一部詳しく見ていきます。

Host 2 に Docker コンテナーを立ち上げてみるとどうなるでしょうか。

まず、Dockerデーモンを立ち上げると docker0 というブリッジが作られます。デフォルトでは 172.17.0.1/16 の IP アドレスが割り振られます。

そして、Docker コンテナーを立ち上げると、docker0 に接続される仮想イーサポートが作られます。この仮想イーサポートの名前はランダムに生成されるため、 vethxxxxx といったような名前になります。また、Docker コンテナー内には eth0 という名前の仮想イーサポートを持つコンテナーが立ち上がり、この仮想イーサポートには 172.17.0.0./24 のアドレス帯の IP アドレスが自動的に払い出されます。

なお、オプションで docker run --net=<network_name> と指定すると docker0 ではなく、指定されたブリッジに接続され、別の IP アドレス帯 (e.g. 172.18.0.0/24 など) が払い出されます。

この詳細については、公式の「ユーザ定義ネットワーク」や「ネットワークの作成」のページにご覧ください。

また、オプションで docker run --net=none と指定すると eth0 の仮想イーサポートが生成されず、どのブリッジにも接続されないコンテナーが生成されます。

外部ネットワークからの接続

外部ネットワークからの接続については、公式の「外の世界との通信」で概要が説明されています。ここでは、具体的にどのような設定が

先ほどの図を例に挙げて説明すると、Docker コンテナ-を立ち上げると自動的に生成したコンテナーに対して IP フォワードの設定がされます。従って、ルーター側に docker0 である 172.17.0.0/24 に対して、もしくはユーザー定義ネットワークに対するルーティング設定をすれば、Host 1 からでもさきほど立ち上げたコンテナーに対してルーティングされます。

しかしながら、外部からの通信についてはホスト側の iptables でパケットフィルターされてしまいます。

これを回避するために、 Docker 実行時に docker run -p 80:80 のようにポートフォワードの設定を指定します。そうすると次のように iptables に設定されます。

# iptables -nvL
...
Chain DOCKER (13 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.2           tcp dpt:80
...

これは docker0 以外のインターフェースから docker0 への TCP:80ポート への通信を許可するということです。逆に言うと、どの外部端末からでも通信可能になっていることにセキュリティ上の注意が必要です。

なお、ポートフォワーディングの設定は下記を見ればよいです。

# iptables -t nat -nvL
...
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.17.0.2:80

また、ホストが指定したポートが待ち受け状態であるかは、下記のいずれかでも確認ができます。

$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
...
tcp6       0      0 [::]:80               [::]:*                  LISTEN
...
 
 
$ sudo lsof -i
COMMAND     PID     USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
...
docker-pr 13076     root    4u  IPv6 175148132      0t0  TCP *:80 (LISTEN)

セキュリティ対策を行う場合には下記の記事が参考になりました。

https://www.greptips.com/posts/1268/

docker-compose でコンテナーを立てる

docker-compose でコンテナーを立ち上げた場合には、デフォルトで docker-compose.yml ファイル単位で新しいブリッジを作り、そのファイルに記載されたすべてのコンテナーイメージはそのブリッジに接続されます。

docker-compose での IP アドレスの設定や複数付与

docker-compose を使うことで簡単にネットワークの作成や、コンテナーに対する複数仮想イーサポートの割り当てが簡単にできます。

例えば、次のような docker-compose.yml を用意します。

networks:
  test_network1:
    #enable_ipv6: true
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
          gateway: 192.168.1.1
  test_network2:
    #enable_ipv6: true
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.2.0/24
 
services:
  node:
    image: ubuntu:18.04
    networks:
      test_network1:
        ipv4_address: 192.168.1.2
      test_network2:
        ipv4_address: 192.168.2.2

これを下記のコマンドのように実行すると、異なるブリッジに接続された2つの仮想イーサポートが node コンテナーに割り当てられます。

$ docker-compose run node bash

なお、このあたりの公式ドキュメントを探したのですが下記くらいしかあまり参考になるものがなく、もしかすると間違っているかもしれませんので、その時はご容赦ください。

https://docs.docker.com/compose/compose-file/


今回は以上です。 最後まで読んでいただき、ありがとうございます。

コメント

このブログの人気の投稿

LinuxでのnVidia GPUのオーバークロック・電力チューニング方法

ネットワーク越しの RTL-SDR で SDR# を使う方法