再起動しても壊れないように、Dockerコンテナ間のリンクを設定するにはどうすればよいですか?


80

次のように実行されているDockerコンテナがいくつかあります。

  • Nginx
  • Webアプリ1
  • Webアプリ2
  • PostgreSQL

NginxはWebアプリ1および2内のWebアプリケーションサーバーに接続する必要があり、WebアプリはPostgreSQLと通信する必要があるため、次のようなリンクがあります。

  • Nginx ---リンク---> Webアプリ1
  • Nginx ---リンク---> Webアプリ2
  • Webアプリ1 ---リンク---> PostgreSQL
  • Webアプリ2 ---リンク---> PostgreSQL

これは最初はかなりうまく機能します。ただし、新しいバージョンのWebアプリ1とWebアプリ2を開発するときは、それらを置き換える必要があります。私がしていることは、Webアプリのコンテナーを削除し、新しいコンテナーをセットアップして開始することです。

Webアプリコンテナの場合、最初のIPアドレスは次のようになります。

  • 172.17.0.2
  • 172.17.0.3

そして、私がそれらを交換した後、それらは新しいIPアドレスを持ちます:

  • 172.17.0.5
  • 172.17.0.6

現在、Nginxコンテナー内の公開された環境変数は、引き続き古いIPアドレスを指しています。ここに問題があります。コンテナ間のリンクを壊さずにコンテナを交換するにはどうすればよいですか?同じ問題がPostgreSQLでも発生します。PostgreSQLイメージのバージョンをアップグレードしたい場合は、必ずそれを削除して新しいバージョンを実行する必要がありますが、コンテナグラフ全体を再構築する必要があるため、これは実際のサーバー操作には理想的ではありません。

回答:


53

の効果--linkは静的であるため、シナリオでは機能しません(リンク削除することはできますが、現在、再リンクはありません)。

dockerize.itでは、リンクやアンバサダーを使用せずに、これを解決するために2つの異なるアプローチを使用しています(アンバサダーを追加することもできます)。

1)ダイナミックDNSを使用する

一般的な考え方は、データベース(またはその他のサービス)に単一の名前を指定し、コンテナーを開始および停止するときに、短期間のDNSサーバーを実際のIPで更新することです。

SkyDockから始めました。DNSサーバーとそれを自動的に更新し続けるモニターの2つのDockerコンテナーで動作します。その後、Consulを使用してよりカスタムなものに移行しました(dockerizedバージョン:docker -consulも使用)。

これの進化(私たちが試したことはありません)は、etcdなどをセットアップし、そのカスタムAPIを使用してIPとポートを学習することです。ソフトウェアは動的再構成もサポートする必要があります。

2)docker bridgeipを使用します

コンテナポートを公開するときdocker0は、よく知られているアドレスを持つ(または持つことができる)ブリッジにそれらをバインドするだけです。

コンテナを新しいバージョンに置き換えるときは、新しいコンテナに同じIPで同じポートを公開させるだけです。

これはより単純ですが、より制限されています。同様のソフトウェアを実行すると(たとえば、2つのコンテナがdocker0ブリッジの3306ポートでリッスンできない)、ポートの競合が発生する可能性があります。したがって、現在のお気に入りはオプション1です。


20

リンクは特定のコンテナ用であり、コンテナの名前に基づくものではありません。そのため、コンテナを削除すると、リンクが切断され、新しいコンテナ(同じ名前であっても)が自動的に配置されなくなります。

新しいネットワーク機能を使用すると、名前でコンテナに接続できるため、新しいネットワークを作成すると、そのネットワークに接続されているコンテナはすべて、名前で他のコンテナに到達できます。例:

1)新しいネットワークを作成する

$ docker network create <network-name>       

2)コンテナをネットワークに接続します

$ docker run --net=<network-name> ...

または

$ docker network connect <network-name> <container-name>

3)名前でコンテナにpingを実行します

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

ドキュメントのこのセクションを参照してください。

注:レガシーとは異なりlinks、新しいネットワーク環境変数を作成せず、他のコンテナーと環境変数を共有しません。

この機能は現在、エイリアスをサポートしていません


これはバージョン1.9以降でのみ機能することを指摘したいと思います。一部のディストリビューションは、最新のものでまだリリースされていません。
John Giotta 2016年

もう1つのオプションは、コンテナー名の代わりにネットワークスコープのエイリアスを使用することです(グローバルに一意である必要がありますが、必ずしも適切であるとは限りません)。しかし、それでも答えは絶対に正しいです。
Ivan Anishchuk 2016

10

アンバサダーコンテナを使用できます。ただし、アンバサダーコンテナをクライアントにリンクしないでください。これにより、上記と同じ問題が発生します。代わりに、Dockerホスト上のアンバサダーコンテナーの公開ポート(通常は172.17.42.1)を使用します。例:

postgresボリューム:

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

postgres-コンテナ:

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

アンバサダー-postgresのコンテナー:

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

これで、アンバサダーコンテナをリンクせずにpostgresqlクライアントコンテナを起動し、ゲートウェイホスト(通常は172.17.42.1)でpostgresqlにアクセスできます。

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# psql -h $PGHOST --user postgres
Password for user postgres: 
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.

postgres=#
postgres=# select 6*7 as answer;
 answer 
--------
     42
(1 row)

bpostgres=# 

これで、クライアントを再起動しなくても、アンバサダーコンテナーを再起動できます。


「-p5432:5432」はPostgreSQLを外の世界に公開しませんか?
Fang-Pen Lin

2
はい、そうなります。これが不要な場合は、「-p172.17.42.1:5432:5432」を使用できます。
SWENThümmler

1
ちなみに、なぜその「PGDATA」コンテナを作成してpostgresqlコンテナにリンクする必要があるのでしょうか。わかりません。postgresqlコンテナを作成し、そのボリュームをホストディレクトリに直接マップしてみませんか?
Fang-Pen Lin

PGD​​ATAコンテナは必要ありません。関心の分離のためだけに使用します。posgresコンテナーを開始するとき、PGDATAコンテナー内のボリュームがどのようにマップされているかを覚えておく必要はありません。それが私が現在それをしている方法であるため、私はそれを追加しました。私は自分自身が...それはいいアイデアだかどうかわからまだないです-それは、基本的には好みの問題だ
SWENThümmler

Swenのようにデータボリュームコンテナを使用することは確かにベストプラクティスです。
derFunk 2015年

2

それでも興味がある場合は、各Dockerコンテナの/ etc / hostsファイルのホストエントリを使用する必要があります。ENV変数は自動的に更新されないため、これらに依存しないでください。

リンクされたコンテナごとに、LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCPなどの形式のホストファイルエントリがあります。

以下は、ドッカーからであるドキュメント

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

/ etc / hostsファイルのホストエントリとは異なり、環境変数に格納されているIPアドレスは、ソースコンテナが再起動されても自動的に更新されません。リンクされたコンテナのIPアドレスを解決するには、/ etc / hostsのホストエントリを使用することをお勧めします。

これらの環境変数は、コンテナ内の最初のプロセスに対してのみ設定されます。sshdなどの一部のデーモンは、接続のためにシェルを生成するときにそれらをスクラブします。


2

これは、3週間前のDockerの実験的なビルドに含まれており、サービスが導入されています:https//github.com/docker/docker/blob/master/experimental/networking.md

--publish-service <name>引数を指定してDockerコンテナーを実行することにより、動的リンクを適切に配置できるはずです。この名前には、DNSを介してアクセスできます。これは、コンテナーの再始動時に持続します(もちろん、同じサービス名でコンテナーを再始動する限り)


そのバージョンをどのようにインストールしますか?github.com/docker/docker/releases/tag/v1.8.0-rc1
m59 2015

1
詳細については、次のページを参照してください:github.com/docker/docker/tree/master/experimental。短いバージョン:wget -qO- https://experimental.docker.com/ | sh実験バージョンをインストールするために実行
Laurens Rietveld 2015

1
この回答は有効でしたが、dockerが実験的なpublish-serviceオプションを削除したため、現在は古くなっています。現在は、代わりにネットワークスコープのエイリアスがあります。でも本質的に同じことです。
Ivan Anishchuk 2016

1

これを解決するために、名前付きのdockerlinksを使用できます。

最も基本的な設定は、最初に名前付きデータベースコンテナを作成することです。

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

次に、dbに接続するWebコンテナを作成します。

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

これにより、コンテナをIPアドレスに手動で接続する必要がなくなります。


2
うーん... dockerはどういうわけかあなたのためにリンクされたホスト名を生成するように見えます、しかしそれがするように、それは/ etc / hostsに名前を生成します、それは静的です、私がリンクされたコンテナを再起動すると、IPは変わります、しかし/ etc / hostsは同じままなので、機能しません。
Fang-Pen Lin

2
Dockerバージョン1.0は、IPアドレスの割り当てをより積極的に行うためです。コンテナ(dbこの場合)を再起動すると、新しいIPアドレスを受け取ります。他のコンテナ(再起動されているかどうかに関係なく)は、起動した瞬間からENV値を保持し、役に立たない。
GermanDZ 2014年

1
fyiは修正が行われているようです。リンクされたコンテナが再起動されると、/ etc / hostsが更新されます:github.com/docker/docker/issues/6350
jamshid

1
この問題は修正されたようで、提案された方法は私のために働いています。
イェンスピエグサ2015年

1
これがここでの最も正しい答えです。唯一の問題は、リンクが単方向であり、コンテナー間の依存関係を追加することです。2つのコンテナーをクロスリンクすることはできず、リンクされたコンテナーを停止してから(新しいオプションなどで)再開することはできません。いずれの場合も、ネットワーク(およびいずれかnet-aliasまたはコンテナー名)を使用してください。
Ivan Anishchuk 2016

1

OpenSVCアプローチを使用すると、次の方法で回避できます。

  • 独自のIPアドレス/ DNS名(エンドユーザーが接続するサービス)を持つサービスを使用する
  • この特定のIPアドレスにポートを公開するようにdockerに指示します( "-ip" dockerオプション)
  • サービスのIPアドレスに接続するようにアプリを構成します

コンテナを交換するたびに、正しいIPアドレスに接続されることを確認できます。

チュートリアルはこちら=> OpenSVCを使用したDockerマルチコンテナ

tutoの最後にある「複雑なオーケストレーション」の部分をお見逃しなく。これは、コンテナーを正しい順序で開始/停止するのに役立ちます(1つのpostgresqlサブセット+1つのwebappサブセット+1つのnginxサブセット)

主な欠点は、webappポートとPostgreSQLポートをパブリックアドレスに公開することです。実際には、nginxtcpポートのみをパブリックアドレスに公開する必要があります。


1

リンクをそのまま維持するためだけに中間コンテナーを持つアンバサダーメソッドを試すこともできます...(https://docs.docker.com/articles/ambassador_pattern_linking/を参照)詳細については


アンバサダーは素晴らしいパターンですが、同じ問題があります。IPアドレスは再起動時に必ずしも更新されません。ただし、ホスト間の接続には最適です。まあ、おそらく必要とされない新しいDockerリリースで。
Ivan Anishchuk 2016

@IvanAnishchuk真、しかし、一度コメントはこれが移動するための方法だった...(2年前に;)とした)
Gekkie

0

イメージの接続ポートをホストの固定ポートにバインドし、代わりにそれらを使用するようにサービスを構成できます。

これにも欠点がありますが、あなたの場合はうまくいくかもしれません。


ローカルホストポートのバインドには、確かに欠点があります。新しいDockerネットワークはそれを時代遅れにします。
Ivan Anishchuk 2016

0

別の方法は、--net container:$CONTAINER_IDオプションを使用することです。

ステップ1:「ネットワーク」コンテナを作成する

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

ステップ2:「ネットワーク」コンテナにサービスを挿入する

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

「ネットワーク」コンテナに触れない限り、リンクのIPアドレスは変更されません。


意味のある名前を持つユーザー作成のブリッジネットワークは、おそらくより良いオプションです。ネットワークを使用するためだけにコンテナを作成する必要はありません。
Ivan Anishchuk 2016

0

この場合、必要なのはネットワークスコープのエイリアスです。これはかなり新しい機能であり、1つのコンテナからのみアクセスできるリンクエイリアスとは異なり、ネットワーク全体にサービスを提供するコンテナを「公開」するために使用できます。

コンテナ間にはいかなる種類の依存関係も追加されません。再起動、交換、起動の順序に関係なく、両方が実行されている限り、コンテナは通信できます。/ etc / hostsの代わりにDNSを内部的に使用していると思います

このように使用しdocker run --net=some_user_definied_nw --net-alias postgres ...ます。同じネットワーク上の任意のコンテナからそのエイリアスを使用して接続できます。

デフォルトネットワークでは機能しません。残念ながら、で作成してから、すべてのコンテナでdocker network create <network>使用する必要があります--net=<network>composeもサポートしています)。

コンテナがダウンしているためエイリアスで到達できないことに加えて、複数のコンテナがエイリアスを共有することもあります。その場合、正しいコンテナに解決される保証はありません。しかし、場合によっては、シームレスなアップグレードに役立つ可能性があります。

現時点では、すべてが十分に文書化されておらず、manページを読んだだけでは理解するのが困難です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.