Translate

2015年3月13日金曜日

The Docker User Guide: Linking Containers Together を翻訳してみた

今回は以下のサイトを翻訳してみました。

 Linking Containers Together
 https://docs.docker.com/userguide/dockerlinks/

ここまでDockerユーザガイドを翻訳すると、
例えば、DBを使ったWebアプリケーションなんかを動作させるには
単一のコンテナではダメ(ダメじゃないけど)だということがわかってきた。

とするとコンテナ間の通信が必要になり
動的にIPアドレスがふられているDocker内で
どうしても名前解決ってやつが必要になってくる。

しかもDockerは思想的に
各コンテナはIPアドレスやホスト名が動的につけられてもうごくよ
って感じでデザインするのがかっこいいとおもっているらしい..

で、必要になってくるのが今回翻訳した部分の知識だ。



あ、翻訳を参照される方は at your own riskでお願いします。


----------

コンテナのリンク


コンテナの動作セクションでは、ネットワークポート経由でDockerコンテナ内部で動作中のサービスとの通信方法について学習してきました。しかし、1つのポートでは、Dockerコンテナ内で操作中のサービスやアプリケーションでの相互接続はたった1つだけしかできません。このセクションでは、1つのネットワークポート経由の接続を短く再学習して、もう一つのアクセス方法であるコンテナリンキングについて紹介します。


ネットワークポートマッピングを使った接続


コンテナの動作セクションでは、Python Flaskアプリケーションが動作するコンテナを生成しました:


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

 注意:
 (コンテナの動作セクションにて、docker inspectコマンドでコンテナのIPアドレスを
 確認したとおり)コンテナは1つの内部のネットワークと
 1つのIPアドレスを保有しています。
 Dockerは様々なネットワーク構成を持つことができます。
 Dockerネットワーキングにおけるより詳しい情報を以下のサイトで確認できます。

 Network Configuration
 https://docs.docker.com/articles/networking/

コンテナが生成された際に指定した「-P」フラグは自動的にコンテナ内部のネットワークポートをDockerホスト上のポート番号49153から65535までの範囲のランダムなポートにマップするために使用しています。docker ps を実行した際に、コンテナの5000番ポートが、ホストの49155番ポートへバインドされていることを確認しました。

 $ sudo 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」フラグを用いてあるコンテナ側のポートを特定のポートへバインドする方法についても学習しました。

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


そして、1つだけのコンテナの特定ポートに縛られるためこの方法がそれほど良いアイディアではないことも学びました。

-p」フラグを使った設定方法は他にもいくつかります。デフォルトでは「-p」フラグは特定のポートをホスト上のすべてのインターフェイスへバインドします。しかし、例えばlocalhostへなどのように、特定のインターフェイスへバインドすることも可能です。

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

この場合、コンテナ内部の5000番ポートからホストマシン上のlcalhostもしくは127.0.0.1インターフェイスの5000番ポートへバインドしています。

または、コンテナの5000番ポートをあるダイナミックポートへバインドしますが、ローカルホスト上のみ可能です。

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

/udp」を追加することでUDPポートをバインドすることも可能です。例えば:

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

カレントのポートバインディングを確認した際に便利なdocker ポートのショートカットについても学習しました。これは、特定のポート設定を確認する際にも便利です。例えば、ホストマシンでコンテナポートをのlocalhostへバインドすると、dockerポートのアウトプットに反映されます。

 $ sudo docker port nostalgic_morse 5000
 127.0.0.1:49155

 注意:
 複数ポートを設定するために、「-p」フラグを複数使用することができます。

リンクによる通信


ネットワークポートマッピングは、Dockerコンテナが別のコンテナと通信する唯一の方法ではありません。Dockerにも、複数のコンテナをリンクし次から次へと接続情報を送信するリンクがあります。コンテナがリンクされると、送信元コンテナについての情報は受信先コンテナへ送信できます。送信元コンテナの状況を説明している選択されたデータを参照することができます。


名前をつけることの重要性


Dockerはコンテナ名を頼りにリンクを確立します。これまで学習してきたとおり、生成したそれぞれのコンテナは自動的に名前が作成されます;このガイドをとおして、旧友 nostalgic_morse を本当によくしっているとおもいます。コンテナに名前をつけることもできます。この名前をつけることは2つの役に立つ機能を提供します:
 

  1. 例えば、Webアプリケーションを含むコンテナの名前を「web」にするなど、特定の機能を実行するコンテナに名前をつければ、より覚えやすくなり便利です。
  2. 例えば、「web」コンテナに「db」コンテナへリンクする設定が可能になるなど、Dockerに対して他のコンテナに参照させるリファレンスポイントを提供します。

--name」フラグでコンテナに名前をつけることができます。例えば:

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

上記は、新規コンテナを起動し、「--name」フラグを使って「web」と名付けています。「docker ps」コマンドを使うとコンテナ名を参照できます。

 $ sudo 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」コマンドもコンテナ名を返却します。

 $ sudo docker inspect -f "{{ .Name }}" aed84ee21bde
 /web

 注意:
 コンテナ名は一意でなくてはなりません。
 これは「web」と呼ぶのは1つのコンテナだけであることを意味しています。
 もしコンテナ名を再利用したいのであれば、同じな目の新規コンテナを生成する前に、
 (docker rm で)古いコンテナ削除しなくてはなりません。
 他の方法として、docker runコマンドに「--rm」フラグを使うことができます。
 このフラグを使うと、停止した直後に対象のコンテナは削除されます。


リンクをまたがった通信


リンクはコンテナに対してお互いを見つけ、一方から他方へ情報をセキュアに伝達することを許可します。リンクをセットアップした時に、送信元コンテナと受信先コンテナとの間にパイプを構築します。受信側は送信元についての選択された情報へアクセスすることができます。リンクを作成するために、「--link」フラグを使います。最初に新規コンテナを生成します、この場合は新規コンテナにデータベースが含まれています。

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

上記の場合、「db」と呼ばれる新規コンテナを「training/postgres」イメージから生成しています。そしてそのコンテナはPostgreSQL データベースを含んでいます。

ここで前に作成した「web」コンテナを削除してリンクのあるコンテナを置き換える必要があります:


 $ sudo docker rm -f web

新規「web」コンテナを生成し「db」コンテナにリンクします。

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

これは新規の「web」コンテナを、以前に作成した「db」コンテナにリンクしています。「--link」フラグの書式は次のとおり:

 --link :alias

nameはリンク先コンテナの名前で、aliasはリンク名のエイリアスです。aliasがどのように使用されるかについては後述します。

次に、「docker inspect」を使ってリンクされたコンテナを確認します:

 $ sudo docker inspect -f "{{ .HostConfig.Links }}" web
 [/db:/web/db]

web/db」から「web」コンテナが「db」コンテナにリンクされていることがわかります。そして「db」コンテナについての情報へのアクセスを許可しています。

では、リンクは実際にコンテナに何をするのでしょうか?

さきほどの例では、受信側である「web」は送信元である「db」についての情報へアクセスできます。これを実現するには、Dockerはコンテナ間にセキュアなトンネルを構築するので、コンテナのポートを外部に露出させる必要がありません;「db」コンテナを開始した際に、「-P」や「-p」を使わなかったことに注意してください。これはリンクを使う大きな利点です:送信元コンテナ、ここではPostgreSQLデータベース、をネットワークへ公開させる必要がありません。

Dockerは送信元コンテナから受信先コンテナへ2つの方法で連結情報を公開します:
 

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


環境変数


コンテナへリンクした時、Dockerはいくつかの環境変数を生成します。Dockerは。「--link」パラメータを元にしてターゲットとなるコンテナ内に自動的に環境変数を生成します。Dockerから生成したすべての環境変数もまた送信元コンテナから公開します:
 

  • 送信元コンテナのDockerfile
  • 送信元コンテナ開始時における docker run コマンドの「-e」、「--env」、「--env-file」オプション

これらの環境変数は、ターゲットコンテナ内部のプログラムから送信元コンテナに関連する情報の発見を可能にします。

 警告:
 Dockerが生成しているコンテナ内部のすべての環境変数が
 リンク先に対して利用可能にしていることを理解することは重要です。
 もし機密データがリンク先に保管されているならば、
 これはセキュリティに対する深刻な影響をあたえることがあります

Dockerは、「--link」パラメータにリストされた各ターゲットコンテナへ _NAME 環境変数をセットします。例えば、もし「web」と呼ばれる新規コンテナが「--link db:webdb」を指定して「db」とよばれるデータベースコンテナにリンクされているならば、Dockerは「web」コンテナ上に「WEBDB_NAME=/web/webdb」を生成します。

Dockerはまた、送信元コンテナによって公開された各ポートの環境変数群をセットします。次の形式のような一意な prefix のついた環境変数となります:

 _PORT__

このprefixのコンポーネントは:

  • --link」パラメータ内で指定したエイリアス (例:webdb)
  • 公開されている 番号
  • TCPUDPかのどちらかである


Dockerはこのprefix形式をつかって3つの個別の環境変数を定義します:

  • prefix_ADDR」:URLから取得したIPアドレス(例:WEBDB_PORT_8080_TCP_ADDR=172.17.0.82)
  • prefix_PORT」:URLから取得したポート番号(例:WEBDB_PORT_8080_TCP_PORT=8080)
  • prefix_PROTO」:URLから取得したプロトコル(例:WEBDB_PORT_8080_TCP_PROTO=tcp)

もしコンテナが複数ポートを公開するのであれば、環境変数はそれぞれに定義されます。これは、例えば、もしコンテナが4ポート公開するならば、各ポート3つづなので、Dockerは12個の環境変数を生成します。

それらにくわえて、Dockerは「_PORT」と呼ばれる環境変数を生成します。値には送信元コンテナの最初に公開したポートのURLが含まれます。"最初"のポートは最も低い番号を公開ポートとして定義します。たとえば、WEBDB_PORT=tcp://172.17.0.82:8080 のようになります。もしポートがtcp、udp ともに使用されるのであれば、tcp が指定されます。

最後に、Dockerはターゲット側の環境変数としてそれぞれのDockerが生成する環境変数を送信元コンテナから公開します。それぞれの環境変数は、Dockerがターゲットコンテナ上に変数「_ENV_ 」を生成します。変数値は送信元コンテナ開始時にDockerによって使用された値がセットされます。

データベースの例に戻ると、特定のコンテナの環境変数をリストする「env」コマンドを実行することができます。

 $ sudo 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
 ...

Dockerが送信元である「db」コンテナに関する便利な情報の一連の環境変数を構築したことがわかります。「DB_」から始まる各変数、これらは先ほど指定したエイリアスから書き込まれています。もしエイリアスが「db1」であれば、環境変数の先頭は「DB1_」から始まります。それらの環境変数は「db」コンテナ上のデータベースと接続するアプリケーションを設定するために使うことができます。通信はセキュアでプライベートです;リンクされた「web」コンテナにだけ「db」コンテナと通信することができます。


Docker 環境変数の重要な注意


/etc/hosts ファイルのホストエントリとは異なり、送信元コンテナが再実行されると、環境変数に格納されたIPアドレスは自動的に更新されません。リンクされたコンテナのIPアドレスの解決には/etc/hostsのホストエントリを使用することを推奨します

これらの環境変数はコンテナの最初のプロセスでのみセットされます。sshd のような、いくつかのデーモンは、接続のためのシェルを実行する際に環境変数をこすりだします。

/etc/hosts ファイルの更新


環境変数に加えて、Dockerは送信元コンテナのためにホストエントリを/etc/hostsファイルへ追加します。ここでは「web」コンテナのエントリを見てみましょう:

 $ sudo 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.5  db

2つの関連するホストエントリを見ることができます。最初の1つは、ホスト名としてコンテナIDを使用している「web」コンテナのエントリです。もう一つのエントリはリンクエイリアスを使って「db」コンテナのIPアドレスを参照しています。

 root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
 root@aed84ee21bde:/opt/webapp# ping db
 PING db (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をインストールする必要がありました。

ここでは、pingコマンドでホストエントリを使って、172.17.0.5としてresolveして、「db」コンテナへのpingを実行しました。ホストエントリを使ってアプリケーションにdbコンテナの利用を設定することができます。

 注意:
 単一の送信元コンテナから複数の受信先コンテナにリンクすることができます。
 例として、「db」コンテナに接続された複数の web
 (実際には異なる名前が付けられている)コンテナを持つ事ができました。

もし送信元コンテナを再起動したら、リンクされたコンテナの /etc/hosts ファイル群は新しい送信元コンテナのIPアドレスに自動更新され、リンクされた通信を継続させます。

 $ sudo docker restart db
 db
 $ sudo 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


次のステップ


このセクションでは複数のDockerのリンクについて学習したので、次のステップではデータ、ボリューム、コンテナ内部におけるマウントの方法について学習します。

コンテナにおけるデータ管理へ進む。

----------

dockerコマンドで、いかに露出させるホスト名やポートを減らし、
内部の通信で済ませてしまうようにDockerfileを書くには
=イメージをどうつくるか
には、ある程度デザインに対するパターン言語が必要なんでないのかな。

まあ、英語ではあるんだろうなあ..
ベストプラクティス云々の単語がちらほらしているし..

このあたりを日本語で華麗に書籍化とかしたら
ヒーローになれるんだろうけど..

超..めんどくさそうだ..

0 件のコメント:

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...