bridge(default,user-defined),hostネットワークドライバのコンテナ間通信の方法について調べた


コンテナ間通信の方法について調べた時のメモです。
他のホストのコンテナへの通信ではなく、同じホストでのコンテナでの通信に関してです。
そのため、対象のネットワークは「bridge」及び「host」のみです。

TL:DR

  • host ネットワークの場合には localhost:port で接続出来る
  • bridge ネットワークについてはそもそも「default bridge」と「user-defined bridge」の2つがある
  • 「default bridge」の場合、--link オプションもしくは IP アドレスを指定して接続する必要がある
  • 「user-defined bridge」の場合、コンテナ名で名前解決が出来る
  • --linkオプションは将来的に廃止される可能性があるので「user-defined bridge」の利用が推奨されている

Docker のネットワークドライバについて

Docker ではいくつかのネットワークドライバを提供しています

Network drivers

  • bridge
  • host
  • overlay
  • macvlan
  • none
  • Network plugins

今回の確認対象である bridge 及び host の内容について以前確認した記事を参照下さい。

Docker の bridge と host ネットワークについて勉強する

なお、それぞれの使い分けは以下のように記載があります。

Network driver summary

Network driver summary

User-defined bridge networks are best when you need multiple containers to communicate on the same Docker host.

Host networks are best when the network stack should not be isolated from the Docker host, but you want other aspects of the container to be isolated.

Overlay networks are best when you need containers running on different Docker hosts to communicate, or when multiple applications work together using swarm services.

Macvlan networks are best when you are migrating from a VM setup or need your containers to look like physical hosts on your network, each with a unique MAC address.

Third-party network plugins allow you to integrate Docker with specialized network stacks.

bridge については同じ Docker ホストで複数のコンテナが通信をする時に最適と書いてあります。
host はコンテナの隔離(isolation)は行いたいが、ネットワークスタックの隔離をしたくない時に最適と書いてあります。

なお、あとで出てきますが、bridge については「default bridge」ではなく、「user-defined bridge」と書いてあることからもなるべく user-defined な bridge を使って欲しいんだろうなという気持ちが汲み取れました。

bridge の 「default bridge」と「user-defined bridge」って何?

以下に詳細がありますが、記載します。

Use bridge networks

bridge はデフォルトのネットワークドライバであり、docker runの時に何も指定しない場合には bridge が選ばれます。
そしてこの場合、「default bridge」が利用されます。
具体的には以下のような構成となります。

次に「user-defined bridge」ですが、docker network createコマンドで作る事ができます。
また、docker runの時に上記を利用するように指定できます。
具体的には以下のようなコマンドです。

$ docker network create -d bridge --subnet 10.0.0.0/24 my_bridge
$ docker run -itd --name c2 --net my_bridge busybox sh
$ docker run -itd --name c3 --net my_bridge --ip 10.0.0.254 busybox sh

この場合、以下のようになります。

状況としては

  • c1 は defautl bridge を使っている
  • c2,c3 は user-defined bridge を使っている

と言えると思います。
色々調査するまでこれらにあまり違いと思っていたのですが結構違いがありました。

「user-defind bridge」と「default beridge」の違いの詳細については以下に記載があります

Differences between user-defined bridges and the default bridge

  • User-defined bridges provide better isolation and interoperability between containerized applications.
  • User-defined bridges provide better isolation and interoperability between containerized applications.
  • Containers can be attached and detached from user-defined networks on the fly.
  • Each user-defined network creates a configurable bridge.
  • Linked containers on the default bridge network share environment variables.

default bridge でコンテナ間通信を行う場合

書いてある通りですが、--linkを使うか、IP アドレスを利用する方法のみとなっています。

Containers on the default bridge network can only access each other by IP addresses, unless you use the --link option, which is considered legacy

なお、--linkは legacy であり、user-defined bridge を使うことを推奨されています。

Legacy container links

Warning: The --link flag is a legacy feature of Docker. It may eventually be removed. Unless you absolutely need to continue using it, we recommend that you use user-defined networks to facilitate communication between two containers instead of using --link. One feature that user-defined networks do not support that you can do with --link is sharing environment variables between containers. However, you can use other mechanisms such as volumes to share environment variables between containers in a more controlled way.

具体的には以下のように確認できます。

# hostPort を開放せず、export 80 のコンテナを起動
$docker run -d --name web nginx

# curl が使える適当なコンテナを起動。リンクを指定
$docker run --link web -it amazonlinux /bin/bash

# 環境変数にアクセスするための情報がある
bash-4.2# env
WEB_ENV_PKG_RELEASE=1~buster
HOSTNAME=a36a9eeeb1d9
TERM=xterm
WEB_ENV_NJS_VERSION=0.3.6
WEB_ENV_NGINX_VERSION=1.17.5
WEB_PORT_80_TCP_PORT=80
WEB_PORT_80_TCP=tcp://172.17.0.2:80
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
SHLVL=1
HOME=/root
WEB_NAME=/jovial_shtern/web
WEB_PORT_80_TCP_PROTO=tcp
WEB_PORT_80_TCP_ADDR=172.17.0.2
WEB_PORT=tcp://172.17.0.2:80
_=/usr/bin/env

# 環境変数を使ってアクセス出来る
bash-4.2# curl http://${WEB_PORT_80_TCP_ADDR}

# 実際には IP アドレスがわかっていれば通信は出来る
bash-4.2# curl http://172.17.0.2/

user-defined bridge でコンテナ間通信を行う場合

user-defined bridge ではコンテナ名で名前解決が出来ます。

User-defined bridges provide automatic DNS resolution between containers.
On a user-defined bridge network, containers can resolve each other by name or alias.

上記より、--link指定は必要はなく、同じ user-defined bridge はコンテナ同士での通信が容易に出来ます。

実際に試された記事がありましたので記載します

host ネットワークでコンテナ間通信を行う

host network を使う場合、ネットワークの隔離は行われていません。

Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks

Host With the host driver, a container uses the networking stack of the host. There is no namespace separation, and all interfaces on the host can be used directly by the container

host network を使ってコンテナを2つ起動した場合、以下のようになります。

そのため、locahost で接続可能です。

# 8080 で expose するコンテナを host ネットワークで起動
$docker run -it --rm --net host -d tomcat:8.0

# host ネットワークでコンテナを起動して localhost で接続。
$docker run -it --rm --net host ubuntu /bin/bash
#apt-get update
#apt-get install curl -y
#curl http://localhost:8080
・・・
                <div class="col20">
                    <div class="container">
                        <h4>Apache Software Foundation</h4>
                        <ul>
                            <li><a href="https://tomcat.apache.org/whoweare.html">Who We Are</a></li>
                            <li><a href="https://tomcat.apache.org/heritage.html">Heritage</a></li>
                            <li><a href="https://www.apache.org">Apache Home</a></li>
                            <li><a href="https://tomcat.apache.org/resources.html">Resources</a></li>
                        </ul>
                    </div>
                </div>
                <br class="separator" />
            </div>
            <p class="copyright">Copyright &copy;1999-2019 Apache Software Foundation.  All Rights Reserved</p>
        </div>
    </body>

</html>