コンテナーリンク(古い機能)

読む時間の目安: 5 分

警告

Docker の--linkフラグは過去の機能です。 そのうちに削除されるかもしれません。 この機能を確実に必要としているのでなければ--linkを使わず、2 つのコンテナー間の通信を実現するユーザー定義ネットワークの利用をお勧めします。 --linkに存在していて、ユーザー定義ネットワークにない機能は、コンテナー間での環境変数の共有機能です。 ただしボリュームのような別の機能を使えば、コンテナー間での環境変数の共有は、より制御しやすく利用できます。

ユーザー定義のブリッジとデフォルトブリッジの違い を参照して、--linkとは違う方法について確認してください。

ここでは古い機能であるコンテナーリンクについて説明します。 これは Docker のデフォルトであるbridgeネットワーク内にあるもので、このbridgeネットワークは Docker をインストールした際に自動的に生成されます。

Docker のネットワーク機能 が提供される以前は、Docker のリンク機能によって複数のコンテナーが互いを検出し、一方から他方への情報送信を安全に行うようにしていました。 Docker のネットワーク機能が導入されてからも、リンクを生成することはできます。 ただしデフォルトのbridgeネットワークであるか、ユーザー定義のネットワーク であるかによって、その動作は異なることになります。

この節においてはネットワークポートを通じてネットワークに接続する方法を簡単に説明した上で、デフォルトのbridgeネットワーク内でのコンテナーリンクを行う方法へ進んでいきます。

ネットワークポートマッピングを利用した接続

以下のコマンドによって Python Flask アプリケーションを起動しているとします。

$ docker run -d -P training/webapp python app.py

メモ: コンテナーには内部ネットワークと IP アドレスがあります。 そして Docker にはさまざまなネットワーク設定方法があります。 Docker のネットワーク機能の詳細は こちら を参照してください。

このコンテナーの生成時には-Pフラグが指定されているので、コンテナー内部のネットワークポートはすべて、Docker ホスト上の「エフェメラルポート」範囲内にあるランダムな高位ポートに自動的に割り当てられます。 その後にdocker psを実行すれば、コンテナー内の 5000 番ポートが、ホスト上の 49155 番ポートに割り当てられているのがわかります。

$ docker ps nostalgic_morse

CONTAINER ID  IMAGE                   COMMAND       CREATED        STATUS        PORTS                    NAMES
bc533791f3f5  training/webapp:latest  python app.py 5 seconds ago  Up 2 seconds  0.0.0.0:49155->5000/tcp  nostalgic_morse

またコンテナーのポートを特定のポートに割り当てるには-pフラグを使えばよいことも、すでに見てきました。 以下はホストの 80 番ポートを、コンテナーの 5000 番ポートに割り当てます。

$ docker run -d -p 80:5000 training/webapp python app.py

ただしこれはあまり良い方法でないのは、すでにお分かりでしょう。 これでは、特定のポートを指定できるのが、ただ一つのコンテナーでしかないからです。

上とは違って、コンテナーポートに対して、ホストのポート範囲を指定することができます。 この範囲は「エフェメラルポート」の範囲とは異なるものです。

$ docker run -d -p 8000-9000:5000 training/webapp python app.py

これによるとコンテナーの 5000 番ポートは、ホスト上の 8000 から 9000 の中で利用可能なポートがランダムに選び出されます。

-pフラグの設定方法には他にもいくつかあります。 デフォルトにおいて-pフラグは、ホストマシン上のすべてのインターフェースに対して、指定されたポートを割り当てます。 しかし特定のインターフェースに対しての割り当てを行うこともできます。 たとえば以下はloalhostにのみ割り当てる例です。

$ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py

上はコンテナー内の 5000 番ポートを、ホストマシン上の 80 番ポートに割り当てますが、これが行われるのはlocalhostつまり127.0.0.1インターフェースに対してのみです。

コンテナー内の 5000 番ポートをlocalhost上の動的ポートに割り当てるなら、以下のようにします。

$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py

UDP や SCTP を割り当てることもできます。 (SCTP は一般に SIGTRAN、Diameter、S1AP/X2AP といった通信プロトコルにおいて利用されています。) UDP や SCTP は、以下の例のように/udp/sctpをつけて指定します。

$ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py

便利なコマンドdocker portについてはこれまでにも使ってきました。 これによって現時点でのポート割り当ての状況がすぐにわかります。 また特定のポートがどのように設定されているかがわかります。 たとえばコンテナーの特定のポートを、ホストマシンのlocalhostに割り当てていたとします。 docker portコマンドの出力には、そのことが示されます。

$ docker port nostalgic_morse 5000

127.0.0.1:49155

メモ

-pフラグは複数個の指定が可能であり、これにより複数ポートの指定を行うことができます。

リンクシステムを用いた接続

メモ

この節ではデフォルトのbridgeネットワーク内の古い機能であるリンク機能について説明します。 ユーザー定義ネットワーク上のリンクに関しては ユーザー定義ネットワークでのコンテナーのリンク を参照してください。

Docker コンテナーを別のコンテナーと接続させるのは、ネットワークのポート割り当てだけが唯一の方法ではありません。 Docker にはリンクシステム(linking system)があります。 このシステムにより複数のコンテナーは互いにリンクすることが可能となり、接続情報をやり取りできるようになります。 複数のコンテナーがリンクされていると、1 つのコンテナーの情報を別のコンテナーに送信することが可能です。 つまり情報を受け取る側のコンテナーは、情報元のコンテナーに関する情報の中から、必要な情報を取り出して見ることができます。

名前づけの重要性

Docker がリンクを確立するためには、コンテナーの名前が重要になります。 これまでコンテナーを生成した際には、各コンテナーに自動的に名前がつけられることを見てきました。 実際にここまでの説明においては、おなじみのnostalgic_morseという名前を用いています。 コンテナーの名前は自由につけることができます。 名前をつけることによって、以下の 2 点が得られます。

  1. コンテナーが実現する特定の機能に合わせて、それを表わす名称にしておくと覚えやすく便利です。 たとえばウェブアプリケーションを含んだコンテナーにはwebという名前をつけます。

  2. 名前は、他のコンテナーから参照させるための参照名となります。 たとえばwebコンテナーからのリンクとして、dbという名前の別のコンテナーを指定することができます。

たとえば以下のようにして--nameフラグを使ってコンテナーに名前をつけることができます。

$ docker run -d -P --name web training/webapp python app.py

上のコマンドは、新規にコンテナーを起動させ、--nameフラグの情報からコンテナーにwebという名前をつけます。 docker psコマンドによってコンテナー名を確認することができます。

$ docker ps -l

CONTAINER ID  IMAGE                  COMMAND        CREATED       STATUS       PORTS                    NAMES
aed84ee21bde  training/webapp:latest python app.py  12 hours ago  Up 2 seconds 0.0.0.0:49154->5000/tcp  web

docker inspectの結果からも、コンテナー名を得ることができます。

メモ

コンテナー名は一意である必要があります。 つまりwebと呼ぶことができるコンテナーは 1 つだけということです。 コンテナー名を再利用したい場合は、それまでの古いコンテナーを(docker container rmを使って)削除する必要があります。 その後であれば、同一名のコンテナーを生成して利用することができます。 これとは別にdocker run--rmフラグを利用する方法もあります。 この方法ではそれまでのコンテナーが停止され、すぐに削除されます。

リンク機能によって複数のコンテナーが互いを検出し、一方から他方への情報送信を安全に行うことができます。 リンク機能を設定すると、情報発信元のコンテナーと受信先のコンテナーの間に経路が生成されます。 そして受信先コンテナーは、発信元コンテナーに関する情報を選び出してアクセスできるようになります。 リンクの生成には--linkフラグを使います。 そこで以下では、まず新たなコンテナーを生成します。 今回はデータベースを含むコンテナーです。

$ docker run -d --name db training/postgres

上のコマンドは、PostgreSQL データベースを含むtraining/postgresイメージからdbという新規のコンテナーを生成します。

先ほど生成したwebコンテナーは、リンクされた状態のコンテナーとするために、いったんここで削除する必要があります。

$ docker container rm -f web

新たなwebコンテナーを生成して、dbコンテナーにリンクします。

$ docker run -d -P --name web --link db:db training/webapp python app.py

これにより、新しいwebコンテナーが、直前に生成したdbコンテナーにリンクされます。 --linkフラグは以下のような書式です。

--link <name または id>:alias

ここでnameはリンクするコンテナーの名前を指定します。 またaliasはリンク名に対するエイリアス名の定義です。 このエイリアス名は簡単に利用することができます。 --linkフラグは以下の書式でも構いません。

--link <name or id>

この場合、エイリアスはリンク名そのものになります。 先ほどの実行例は、以下のようにすることもできます。

$ docker run -d -P --name web --link db training/webapp python app.py

次にリンクしたコンテナーをdocker inspectによって確認してみます。

$ docker inspect -f "{{ .HostConfig.Links }}" web

[/db:/web/db]

webコンテナーがdbコンテナーにリンクされweb/dbとなっているのがわかります。 これによりdbコンテナーに関する情報にアクセスできるようになりました。

コンテナーのリンク機能は実際には何をしているでしょう? リンクを使うと、情報発信元となるコンテナーそのものの情報を、受信先コンテナーに提供できるということを、すでに学びました。 上の例においては情報を受け取るコンテナーがwebであり、情報元となるdbの情報にアクセスできるというものです。 Docker はこのとき、コンテナー間にセキュアなトンネルを作り出します。 そこではコンテナーから外部に向けてポートを公開する必要がないようにしています。 そもそもdbコンテナーを起動する際には、-Pフラグも-pフラグも使っていませんでした。 これこそがリンクシステムの優れたところです。 情報元となるコンテナー、つまり上の例では PostgreSQL データベースを、ネットワーク上に公開していなくても構わないということです。

情報元のコンテナーから受信先のコンテナーに公開される接続情報は、以下の 2 つの手段を通じて受け渡されます。

  • 環境変数
  • /etc/hostsファイルの更新

環境変数

コンテナーをリンクすると、環境変数が数種類作り出されます。 Docker は--linkパラメーターに基づいて、対象とするコンテナー上に自動的に環境変数を作り出すものです。 また発信元コンテナーからは、Docker がもともと提供している環境変数もすべて公開されています。 そういった環境変数は以下に基づくものです。

  • 情報元のコンテナーにおける Dockerfile に記述されたENVコマンド
  • 情報元のコンテナーをdocker runによって起動する際の-e--env--env-fileオプション

このような環境変数があることによって、発信元コンテナーに関する情報を、目的としているコンテナー内部においてプログラムレベルで検出できるようになります。

警告

コンテナー内の環境変数のうち Docker がもともと提供している環境変数はすべて、リンクしているどのコンテナーからも利用可能である点を、十分に留意しておいてください。 その環境変数の中に重要な機密情報が含まれていたら、重大なセキュリティ問題にもなります。

--linkパラメーターに指定されたコンテナーに対しては、<alias>_NAMEという名前の環境変数が定義されます。 たとえばwebという名前の新たなコンテナーが、--link db:webdbという指定を通じてデータベースコンテナーdbにリンクしているとします。 このときwebコンテナー内にはWEBDB_NAME=/web/webdbという環境変数が生成されます。

さらに情報発信元となるコンテナーが公開しているポートに対しても、環境変数が定義されます。 各変数には一意なプリフィックスがつけられます。

<name>_PORT_<port>_<protocol>

プリフィックスは以下のものから構成されます。

  • <name>--linkパラメーターによって指定されたエイリアス名。 (たとえば webdb)
  • <port>: 公開されているポート番号。
  • <protocol>: TCP、 UDP いずれかのプロトコル。

このプリフィックスの書式から、以下の 3 つの環境変数が定義されます。

  • prefix_ADDR変数: URL に対する IP アドレス。 たとえばWEBDB_PORT_5432_TCP_ADDR=172.17.0.82など。
  • prefix_PORT変数: URL に対するポート番号。 たとえばWEBDB_PORT_5432_TCP_PORT=5432など。
  • prefix_PROTO変数: URL に対するプロトコル。 たとえばWEBDB_PORT_5432_TCP_PROTO=tcpなど。

コンテナーが複数ポートを公開している場合は、個々のポートに対して環境変数が定義されます。 これはたとえば、コンテナーが 4 つのポートを公開していたとすると、1 つのポートに対して 3 つの環境変数、つまり全部で 12 個の環境変数が定義されることになります。

さらに<alias>_PORTという環境変数も生成されます。 この変数には、発信元コンテナーの一番初めの公開ポートを用いた URL が定義されます。 この「一番初めの」というのは、公開ポート番号の中で最も小さなものを指します。 たとえばWEBDB_PORT=tcp://172.17.0.82:5432という変数があったとして、このポートが tcp、udp の双方で利用されている場合、tcp が設定されます。

最後に、発信元コンテナーにおいて Docker が元から定義している環境変数が、対象とするコンテナー上の環境変数として公開されます。 各変数に対しては、対象コンテナー上に<alias>_ENV_<name>という変数が生成されます。 この変数の値は、発信元コンテナーが起動する際に、Docker が利用した値が設定されます。

データベースの例に戻ります。 envコマンドを実行して、指定するコンテナー上の環境変数を一覧表示してみます。

$ docker run --rm --name web2 --link db:db training/webapp env

<...>
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
<...>

この出力から、情報元であるdbコンテナーに関して必要となる情報が、環境変数としていくつも生成されているのがわかります。 各環境変数にはDB_というプリフィックスがつけられていて、これは上で指定したaliasから命名されたものです。 aliasdb1としていたら、環境変数のプリフィックスはDB1_になっていたはずです。 この環境変数を使えば、dbコンテナー上にあるデータベースに、アプリケーションから接続する設定を行うことができます。 その際の接続はセキュアでありプライベートなものです。 そしてリンクしているwebコンテナーだけが、dbコンテナーとの通信を行うことができます。

Docker 環境変数に関する重要事項

/etc/hostsファイル におけるホストの設定とは違って、環境変数内に保存された IP アドレスは、発信元のコンテナーが再起動されたときに自動的に更新されません。 リンクされたコンテナーの IP アドレスを取得するためには、/etc/hostsに設定することをお勧めします。

こういった環境変数の設定は、そのコンテナーの初期処理段階でのみ行われます。 デーモンの中にはsshdなどのように、接続を実現するために起動するシェルにおいて、そのような変数を破棄するものもあります。

/etc/hostsファイルの更新

環境変数とは別に Docker は、発信元コンテナーを示すホスト設定を/etc/hostsファイルに加えます。 以下はwebコンテナーに対するホスト設定の例です。

$ docker run -t -i --rm --link db:webdb training/webapp /bin/bash

root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7  aed84ee21bde
<...>
172.17.0.5  webdb 6e5cdeb2d300 db

上においては、関連する 2 つの設定を見ることができます。 1 つめはwebコンテナーに対する設定であり、ホスト名としてコンテナー ID が用いられています。 2 つめの設定では、dbコンテナーの IP アドレスを参照する、リンク機能のエイリアスを用いています。 エイリアス名の他に、リンクされたコンテナー名も追加されています。 そして--linkパラメーターによって指定されたエイリアスとホスト名が異なっていれば、このホスト名も、/etc/hosts内のリンクされたコンテナーの IP アドレスに対して追加されます。 設定項目の要素のどれを使っても ping を実行することができます。

root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping webdb

PING webdb (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms

メモ

ここに示す例においてはpingをインストールしています。 このコンテナーの初期状態ではインストールされていないためです。

上ではdbコンテナーに対してのpingコマンド実行において、/etc/hostsの設定項目を利用しました。 そしてそれは172.17.0.5であることがわかりました。 このように/etc/hostsの設定項目を用いてアプリケーションを設定すれば、dbコンテナーを利用することができます。

メモ

情報発信元となる 1 つのコンテナーに対して、受信先となるコンテナーを複数リンクすることができます。 たとえば複数の(名前の異なる)ウェブコンテナーをdbコンテナーにリンクすることもできます。

情報発信元となるコンテナーを再起動すると、リンクされたコンテナー内の/etc/hostsファイルは自動的に更新されて、発信元コンテナーの新たな IP アドレスが設定されます。 こうしてリンクされた通信状態が維持されます。

$ docker restart db
db

$ docker run -t -i --rm --link db:db training/webapp /bin/bash

root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7  aed84ee21bde
<...>
172.17.0.9  db
Examples, Usage, user guide, links, linking, docker, documentation, examples, names, name, container naming, port, map, network port, network