コンテナーリンク(古い機能)
読む時間の目安: 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 点が得られます。
-
コンテナーが実現する特定の機能に合わせて、それを表わす名称にしておくと覚えやすく便利です。 たとえばウェブアプリケーションを含んだコンテナーには
web
という名前をつけます。 -
名前は、他のコンテナーから参照させるための参照名となります。 たとえば
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
から命名されたものです。
alias
をdb1
としていたら、環境変数のプリフィックスは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