Dockerが推奨するアーキテクチャは、それぞれが単一のコマンドを実行する分離されたコンテナーです。 これらのコンテナは、互いを見つける方法のみを知っている必要があります。つまり、コンテナのfqdnとポート、またはipとポート、つまり外部サービスに関する情報のみを知る必要があります。
このような座標をDockerで実行されているプロセスの内部に伝える推奨される方法は、環境変数です。
NODE_ENV
      
      は適用されないこのアプローチの典型的な例は、Railsフレームワークで採用されている
DATABASE_URL
      
      または
NODE_ENV
      
      フレームワークで採用されているNODE_ENVです。
そして、コンテナ内のアプリケーションがデータベースを便利かつ制約なく見つけることを可能にする環境変数があります。 ただし、このためには、アプリケーションを作成する人がそれについて知っている必要があります。 環境変数を使用したアプリケーションの構成は適切で正しいものですが、アプリケーションの記述が不十分な場合があり、何らかの方法で実行する必要があります。
Docker、環境変数およびリンク
2つのコンテナーをリンクし、Dockerリンクメカニズムを提供する場合、Dockerが役立ちます。 それらについての詳細はDockerのサイトのマニュアルで読むことができますが、要するに次のようになります:
-  起動時にコンテナに名前を付けdocker run -d --name db training/postgres
 
 
 
 :docker run -d --name db training/postgres
 
 
 
 。 これで、db
 
 
 
 という名前のこのコンテナを参照できます。
-   2番目のコンテナーを開始し、最初のコンテナーに関連付けdocker run -d -P --name web --link db:db training/webapp python app.py
 
 
 
 この行で最も興味深いのは、----link name:alias
 
 
 
 です。name
 
 
 
 はコンテナの名前、alias
 
 
 
 はこのコンテナがランナーに知られる名前です。
-  これにより、2つの結果がもたらされます:最初に、 db
 
 
 
 コンテナーを指すweb
 
 
 
 コンテナーに一連の環境変数が表示され、次に、データベースコンテナーを開始したipを指すweb
 
 
 
 コンテナーの/etc/hosts
 
 
 
 コンテナーにエイリアスdb
 
 
 
 が表示されます。web
 
 
 
 コンテナで使用できる環境変数のセットは次のとおりです。
 DB_NAME=/web/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
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      そして、アプリケーションがそのような変数を読む準備ができていない場合、
socat
      
      consoleユーティリティが助けになります。
socat
socat
      
      は、Unixポート転送ユーティリティです。 アイデアは、その助けを借りて、コンテナ内のアプリケーションの印象を作成することです。たとえば、開発者のコンピュータで発生するように、データベースが同じホストとその標準ポートの同じコンテナで実行されているという印象です。
socat
      
      、すべての低レベルUnixと同様、非常に軽量であり、コンテナのメインプロセスに負担をかけません。
リンク機構がコンテナにスローする環境変数を詳しく見てみましょう。 特に興味深いのは、
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
      
      です。 この変数には、必要なすべてのデータが含まれます:localhost(
DB_5432_TCP
      
      5432)でリッスンするポートと、データベース自体の座標(172.17.0.5:5432)。
このような変数は、データベース、
Redis
      
      および補助サービスの各送信リンクのコンテナーにスローされます。
次のようにコマンドをラップするスクリプトを作成します。目的の環境変数のリストをスキャンし、それぞれに対してsocatを実行し、転送されたコマンドを実行して制御を与えます。 スクリプトが終了すると、すべてのsocatプロセスが完了するはずです。
スクリプト
標準ヘッダー。
set -e
      
      は、最初のエラーでスクリプトを完了するようシェルに指示します。つまり、通常のプログラマーの動作が必要です。
 #!/bin/bash set -e
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      追加のsocatプロセスを生成するため、それらを監視して、後で完了し、完了するまで待機できるようにする必要があります。
 store_pid() { pids=("${pids[@]}" "$1") }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これで、記憶できる子プロセスを生成する関数を作成できます。
 start_command() { echo "Running $1" bash -c "$1" & pid="$!" store_pid "$pid" } start_commands() { while read cmd; do start_command "$cmd" done }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      アイデアは
(_,_,_)
      
      環境変数のセットからタプル
(_,_,_)
      
      を
_TCP
      
      、それらを
socat
      
      起動コマンドのセットに
socat
      
      です。
 to_link_tuple() { sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/\1,\2,\3/' } to_socat_call() { sed 's/\(.*\),\(.*\),\(.*\)/socat -ls TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3/' } env | grep '_TCP=' | to_link_tuple | sort | uniq | to_socat_call | start_commands
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      env
      
      は環境変数のリストを表示し、
grep
      
      は必要なもののみを残し、
to_link_tuple
      
      必要なトリプルを引き出し
sort | uniq
      
      sort | uniq
      
      1つのサービスに対して2つの
socat
      
      の起動
sort | uniq
      
      防ぎます
to_socat_call
      
      は必要なコマンドをすでに作成します。
また、メインプロセスが完了したときに
socat
      
      子プロセスを完了したいと考えました。 これを行うには、
SIGTERM
      
      送信します。
 onexit() { echo Exiting echo sending SIGTERM to all processes kill ${pids[*]} &>/dev/null } trap onexit EXIT
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      exec
      
      コマンドでメインプロセスを開始します。 その後、制御が彼に転送され、彼の
STDOUT
      
      が表示され、彼は
STDIN
      
      信号を受信し
STDIN
      
      。
 exec "$*"
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      スクリプト全体を1つのピースで表示できます。
それで何?
このスクリプトをコンテナー(たとえば、
/run/links.sh
      
      /
/run/links.sh
      
      入れ、次のように
/run/links.sh
      
      を開始します。
 $ docker run -d -P --name web --link db:db training/webapp /run/links.sh python app.py
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      出来上がり! ポート
5432
      
      127.0.0.1
      
      のコンテナでは、postgresが利用可能になります。
エントリーポイント
スクリプトについて覚えておく必要がないように、DockerfileのENTRYPOINTディレクティブを使用して 、イメージをエントリポイントに設定できます。 これにより、このようなイメージで起動されたコマンドは、最初にこのエントリポイントの形式のプレフィックスで補完されるという事実につながります。
Dockerfile
      
      追加します。
 ADD ./links.sh /run/links.sh ENTRYPOINT ["/run/links.sh"]
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      繰り返しますが、コンテナはコマンドを渡すだけで起動でき、関連付けられたコンテナのサービスがローカルホストで実行されているかのようにアプリケーションに表示されることを確認します。
そして、画像へのアクセスがない場合はどうなりますか?
上記に関連して、興味深い問題があります。画像内にアクセスがない場合に、サービスの同じ便利なプロキシを作成する方法は? つまり、彼らはイメージを与えて、内部に
socat
      
      があることを誓いますが、スクリプトはそこになく、添付することはできません。 しかし、起動チームをarbitrarily意的に複雑にすることができます。 ラッパーを内部にスローする方法は?
コンテナ内のホストファイルシステムの一部を転送する機能が役立ちます。 つまり、たとえば、ホストファイルシステムに
/usr/local/docker_bin
      
      フォルダーを
/usr/local/docker_bin
      
      、そこに
links.sh
      
      て、次のようにコンテナーを起動できます。
 $ docker run -d -P \ --name web \ --link db:db training/webapp \ -v /usr/local/docker_bin:/run:ro \ /run/links.sh python app.py
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      その結果、
/usr/local/docker_bin
      
      配置したスクリプトは、コンテナー内で実行可能になります。
roフラグを使用したことに注意してください。これは、コンテナが
/run
      
      フォルダーに書き込むことを許可しません。
別の方法は、イメージから継承し、そこにファイルを追加するだけです。
合計
socat
      
      と良い単語を使用すると、良い単語を単独で使用するよりもはるかに便利な方法でコンテナ間で通信できます。
あとがきの代わりに
気配りのある洗練された読者であれば、 アンバサダードライブラリが原則として同じことを行っていることに気付くでしょう。 そして、この読者は絶対に正しいでしょう。 システムを動作させるだけのユーザーは、おそらく既成の実績のあるソリューションを使用することを好むでしょうが、Habrはそのようなユーザーの間ではあまり人気がありません。 だからこそ、このオプスが生まれました。これは、良いジョークのように、明白なことを伝えるだけでなく、教えています。
ご清聴ありがとうございました。