2015年7月16日であり、状況は再び変化しました。 Jason Wilder :
https://github.com/jwilder/nginx-proxy
からこの自動魔法のコンテナを発見し、docker run
になるまでこの問題を解決します。コンテナ。これが、この問題を解決するために使用しているソリューションです。
現在は2015年7月であり、Dockerコンテナーのネットワーキングに関しては大きく変化しています。現在、この問題を(さまざまな方法で)解決する多くの異なる製品があります。
この投稿を使用して、サービス検出への
docker --link
アプローチの基本的な理解を得る必要があります。他のほとんどのソリューションよりも。特定のクラスター内の個別のホストでコンテナーをネットワーク化することは非常に難しく、コンテナーをネットワーク化すると再起動できませんが、同じホスト上のコンテナーをネットワーク化するための迅速かつ比較的簡単な方法を提供します。これは、この問題を解決するために使用する可能性のあるソフトウェアが実際に内部で何をしているのかを知る良い方法です。さらに、おそらくDockerの初期
network
、Hashicorpのconsul
、Weaveworks も確認する必要があります。weave
、Jeff Lindsayのprogrium/consul
&gliderlabs/registrator
、およびGoogleのKubernetes
。CoreOS オファリングもあり、
etcd
、fleet
、およびflannel
。本当にパーティーをしたい場合は、クラスターを起動して
Mesosphere
、またはDeis
、またはFlynn
。ネットワーキングに慣れていない場合(私のように)、老眼鏡を取り出して、ポップ 「Wi-Hiで星に星を塗る-The Best of Enya」 -Fi、そしてビールを割る-それはあなたが本当に何をしようとしているのかを正確に理解するまでしばらくするでしょう。ヒント:
Service Discovery Layer
にCluster Control Plane
を実装しようとしています。土曜日の夜を過ごすにはとてもいい方法です。それはとても楽しいですが、すぐに飛び込む前に、一般的なネットワークについて自分自身をより良く教育するために時間をかけたいと思います。最終的に、慈悲深いデジタルオーシャンチュートリアルの神々からのいくつかの投稿を見つけました:
Introduction to Networking Terminology
およびUnderstanding ... Networking
。飛び込む前にそれらを数回読むことをお勧めします。楽しむ!
Docker
コンテナのポートマッピングを把握できないようです。具体的には、同じサーバー上の別のポートでリッスンして、Nginxから別のコンテナーにリクエストを渡す方法。
Nginxコンテナー用のDockerfileがあります。
FROM ubuntu:14.04
MAINTAINER Me <[email protected]>
RUN apt-get update && apt-get install -y htop git nginx
ADD sites-enabled/api.myapp.com /etc/nginx/sites-enabled/api.myapp.com
ADD sites-enabled/app.myapp.com /etc/nginx/sites-enabled/app.myapp.com
ADD nginx.conf /etc/nginx/nginx.conf
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
EXPOSE 80 443
CMD ["service", "nginx", "start"]
そして、api.myapp.com
設定ファイルは次のようになります。
upstream api_upstream{
server 0.0.0.0:3333;
}
server {
listen 80;
server_name api.myapp.com;
return 301 https://api.myapp.com/$request_uri;
}
server {
listen 443;
server_name api.mypp.com;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $Host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_pass http://api_upstream;
}
}
そして、app.myapp.com
についても同様です。
そして、私は実行します:
Sudo docker run -p 80:80 -p 443:443 -d --name Nginx myusername/nginx
そしてそれはすべて正常に立ち上がっていますが、リクエストは他のコンテナ/ポートにパススルーされていません。そして、Nginxコンテナーにsshしてログを検査すると、エラーは表示されません。
何か助け?
@ T0xicCodeの答え は正しいのですが、実際に有効なソリューションを実装するのに実際に約20時間かかったため、詳細を詳しく説明すると思いました。
独自のコンテナでNginxを実行し、それをリバースプロキシとして使用して同じサーバーインスタンス上の複数のアプリケーションの負荷を分散する場合は、次の手順を実行する必要があります。
コンテナをリンクする
通常、docker run
にシェルスクリプトを入力することで、コンテナをUser Data
すると、他のrunningコンテナへのリンクを宣言できます。これは、コンテナを順番に起動する必要があり、後者のコンテナのみが前者のコンテナにリンクできることを意味します。そのようです:
#!/bin/bash
Sudo docker run -p 3000:3000 --name API mydockerhub/api
Sudo docker run -p 3001:3001 --link API:API --name App mydockerhub/app
Sudo docker run -p 80:80 -p 443:443 --link API:API --link App:App --name Nginx mydockerhub/nginx
したがって、この例では、API
name__コンテナーは他のどのコンテナーにもリンクされていませんが、App
name__コンテナーはAPI
name__にリンクされ、Nginx
name__はAPI
name__とApp
name__の両方にリンクされます。
この結果、env
varsおよびAPI
name__およびApp
name__コンテナー内にある/etc/hosts
ファイルが変更されます。結果は次のようになります。
/ etc/hosts
Nginx
name__コンテナー内でcat /etc/hosts
を実行すると、次の結果が生成されます。
172.17.0.5 0fd9a40ab5ec
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 App
172.17.0.2 API
ENV Vars
env
name__コンテナー内でNginx
name__を実行すると、次の結果が生成されます。
API_PORT=tcp://172.17.0.2:3000
API_PORT_3000_TCP_PROTO=tcp
API_PORT_3000_TCP_PORT=3000
API_PORT_3000_TCP_ADDR=172.17.0.2
APP_PORT=tcp://172.17.0.3:3001
APP_PORT_3001_TCP_PROTO=tcp
APP_PORT_3001_TCP_PORT=3001
APP_PORT_3001_TCP_ADDR=172.17.0.3
実際の変数の多くは切り捨てましたが、上記はコンテナへのトラフィックをプロキシするために必要なキー値です。
実行中のコンテナ内で上記のコマンドを実行するシェルを取得するには、次を使用します。
Sudo docker exec -i -t Nginx bash
これで、リンクされたコンテナのいずれかのローカルIPアドレスを含む/etc/hosts
ファイルエントリとenv
varの両方があることがわかります。私が知る限り、リンクオプションを宣言してコンテナを実行すると、これがすべてです。ただし、この情報を使用して、nginx
name__コンテナー内でNginx
name__を構成できます。
Nginxの設定
ここで少し注意が必要です。いくつかのオプションがあります。 docker
name__が作成した/etc/hosts
ファイル内のエントリを指すようにサイトを構成することも、ENV
varsを利用して、nginx.conf
およびその他のconfファイルで文字列置換(sed
name__を使用)を実行することもできますIP値を挿入する/etc/nginx/sites-enabled
フォルダー。
オプションA:ENV Varsを使用してNginxを構成する
/etc/hosts
ファイルオプションを機能させることができなかったので、これは私が行ったオプションです。すぐにオプションBを試して、調査結果があればこの投稿を更新します。
このオプションと/etc/hosts
ファイルオプションを使用する主な違いは、Dockerfile
name__引数としてシェルスクリプトを使用してCMD
name__引数としてシェルスクリプトを使用する方法です。これにより、文字列置換が処理され、IP値がENV
name__からconfファイルにコピーされます。
構成ファイルのセットは次のとおりです。
Dockerfile
FROM ubuntu:14.04
MAINTAINER Your Name <[email protected]>
RUN apt-get update && apt-get install -y nano htop git nginx
ADD nginx.conf /etc/nginx/nginx.conf
ADD api.myapp.conf /etc/nginx/sites-enabled/api.myapp.conf
ADD app.myapp.conf /etc/nginx/sites-enabled/app.myapp.conf
ADD Nginx-Startup.sh /etc/nginx/Nginx-Startup.sh
EXPOSE 80 443
CMD ["/bin/bash","/etc/nginx/Nginx-Startup.sh"]
nginx.conf
daemon off;
user www-data;
pid /var/run/nginx.pid;
worker_processes 1;
events {
worker_connections 1024;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 33;
types_hash_max_size 2048;
server_tokens off;
server_names_hash_bucket_size 64;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging Settings
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip Settings
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 3;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/xml text/css application/x-javascript application/json;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
# Virtual Host Configs
include /etc/nginx/sites-enabled/*;
# Error Page Config
#error_page 403 404 500 502 /srv/Splash;
}
注:起動直後にコンテナが終了しないように、
daemon off;
をnginx.conf
ファイルに含めることが重要です。
api.myapp.conf
upstream api_upstream{
server APP_IP:3000;
}
server {
listen 80;
server_name api.myapp.com;
return 301 https://api.myapp.com/$request_uri;
}
server {
listen 443;
server_name api.myapp.com;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $Host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_pass http://api_upstream;
}
}
Nginx-Startup.sh
#!/bin/bash
sed -i 's/APP_IP/'"$API_PORT_3000_TCP_ADDR"'/g' /etc/nginx/sites-enabled/api.myapp.com
sed -i 's/APP_IP/'"$APP_PORT_3001_TCP_ADDR"'/g' /etc/nginx/sites-enabled/app.myapp.com
service nginx start
nginx.conf
とapi.myapp.conf
のほとんどの内容について宿題をするのはあなたにお任せします。
魔法はNginx-Startup.sh
で発生し、sed
name__を使用してAPP_IP
およびapi.myapp.conf
ファイルのupstream
name__ブロックに書き込んだapp.myapp.conf
プレースホルダーで文字列置換を行います。
このask.ubuntu.comの質問は非常にうまく説明しています: コマンドを使用してファイル内のテキストを検索して置換
GOTCHAOSXでは、
sed
name__はオプションを異なる方法で処理します。具体的には-i
フラグです。 Ubuntuでは、-i
フラグが置換を「インプレース」で処理します。ファイルを開き、テキストを変更してから、同じファイルを「上書き保存」します。 OSXでは、-i
フラグには、結果のファイルに含めるファイル拡張子が必要です。拡張子のないファイルを使用している場合は、-i
フラグの値として ''を入力する必要があります。GOTCHA置換したい文字列を見つけるために
sed
name__が使用する正規表現内でENV varsを使用するには、varを二重引用符で囲む必要があります。そのため、奇妙な見た目ですが、正しい構文は上記のとおりです。
そのため、Dockerはコンテナを起動し、Nginx-Startup.sh
スクリプトを実行してトリガーしました。sed
name__を使用して、値APP_IP
をENV
name__コマンドで指定した対応するsed
name__変数に変更しました。これで、/etc/nginx/sites-enabled
ディレクトリー内に、コンテナーの起動時にdockerが設定したENV
varsからのIPアドレスを持つconfファイルができました。 api.myapp.conf
ファイル内で、upstream
name__ブロックが次のように変更されていることがわかります。
upstream api_upstream{
server 172.0.0.2:3000;
}
表示されるIPアドレスは異なる場合がありますが、通常は172.0.0.x
であることに気付きました。
これで、すべてが適切にルーティングされるはずです。
GOTCHA最初のインスタンス起動を実行すると、コンテナを再起動/再実行することはできません。 Dockerは、起動時に各コンテナに新しいIPを提供し、以前使用していたものを再利用しないようです。したがって、
api.myapp.com
は最初に172.0.0.2を取得しますが、次に172.0.0.4を取得します。ただし、Nginx
name__は、最初のIPをconfファイルまたは/etc/hosts
ファイルに既に設定しているため、api.myapp.com
の新しいIPを特定できません。これに対する解決策は、CoreOS
name__およびそのetcd
name__サービスを使用する可能性があります。これは、私の限られた理解では、同じENV
name__クラスターに登録されたすべてのマシンに対して共有CoreOS
name__のように機能します。これは、セットアップで遊ぶ次のおもちゃです。
オプションB:/etc/hosts
ファイルエントリを使用
このは、これを行うためのより迅速で簡単な方法である必要がありますが、動作させることができませんでした。表向きは、/etc/hosts
エントリの値をapi.myapp.conf
およびapp.myapp.conf
ファイルに入力するだけですが、このメソッドを機能させることができませんでした。
UPDATE:このメソッドを機能させる方法については、 @ Wes Todの答え を参照してください。
api.myapp.conf
で行った試みは次のとおりです。
upstream api_upstream{
server API:3000;
}
/etc/hosts
ファイルに次のようなエントリがあることを考えると、172.0.0.2 API
値を取得するだけだと思いましたが、そうではないようです。
また、すべてのAZからのElastic Load Balancer
ソーシングに関するいくつかの補助的な問題があったため、このルートを試したときに問題になった可能性があります。代わりに、Linuxで文字列の置換を処理する方法を学ぶ必要があったので、それは楽しかったです。しばらくしてこれを試してみて、どのようになるかを確認します。
私はコードマジックですべての人に機能する人気のあるJason Wilderリバースプロキシを使用してみましたが、すべての人(つまり、私)には機能しないことがわかりました。そして、私はNGINXを初めて使用するので、使用しようとしているテクノロジーを理解していなかったことが好きではありませんでした。
上記のlinking
コンテナーに関する議論は、廃止された機能であるため、現在は日付が付けられているため、2セントを追加したいと考えました。そこで、networks
を使用してそれを行う方法について説明します。この回答は、Docker Compose
およびnginx構成を使用して、静的にページングされたWebサイトへのリバースプロキシとしてnginxを設定する完全な例です。
互いに通信する必要があるサービスを事前定義されたネットワークに追加します。 Dockerネットワークに関する段階的な議論のために、ここでいくつかのことを学びました: https://technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the- bad-and-the-ugly /
まず、すべてのバックエンドサービスが通信できるネットワークが必要です。私はweb
を呼び出しましたが、それは何でも構いません。
docker network create web
単純なWebサイトアプリを作成します。 Webサイトは、nginxコンテナーによって提供される単純なindex.htmlページです。コンテンツは、フォルダーcontent
の下のホストにマウントされたボリュームです
DockerFile:
FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf
default.conf
server {
listen 80;
server_name localhost;
location / {
root /var/www/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
docker-compose.yml
version: "2"
networks:
mynetwork:
external:
name: web
services:
nginx:
container_name: sample-site
build: .
expose:
- "80"
volumes:
- "./content/:/var/www/html/"
networks:
default: {}
mynetwork:
aliases:
- sample-site
ここでポートマッピングが不要になることに注意してください。ポート80を単純に公開します。これは、ポートの衝突を回避するのに便利です。
このウェブサイトを起動します
docker-compose up -d
コンテナのDNSマッピングに関するいくつかの楽しいチェック:
docker exec -it sample-site bash
ping sample-site
このpingは、コンテナ内で機能するはずです。
Nginxリバースプロキシ:
Dockerfile
FROM nginx
RUN rm /etc/nginx/conf.d/*
カスタマイズするため、すべての仮想ホスト設定をリセットします。
docker-compose.yml
version: "2"
networks:
mynetwork:
external:
name: web
services:
nginx:
container_name: nginx-proxy
build: .
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d/:/etc/nginx/conf.d/:ro
- ./sites/:/var/www/
networks:
default: {}
mynetwork:
aliases:
- nginx-proxy
トラスティを使用してプロキシを起動します
docker-compose up -d
問題がないと仮定すると、名前を使用して互いに通信できる2つのコンテナが実行されます。テストしてみましょう。
docker exec -it nginx-proxy bash
ping sample-site
ping nginx-proxy
最後の詳細は、仮想ホスティングファイルをセットアップして、プロキシを設定する方法に基づいてプロキシがトラフィックを転送できるようにすることです。
仮想ホスティング設定のsample-site.conf:
server {
listen 80;
listen [::]:80;
server_name my.domain.com;
location / {
proxy_pass http://sample-site;
}
}
プロキシの設定方法に基づいて、conf.d
ファイルのvolumes
宣言を介してマウントしたローカルのdocker-compose
フォルダーにこのファイルを保存する必要があります。
最後になりましたが、nginxに設定をリロードするように伝えてください。
docker exec nginx-proxy service nginx reload
これらの一連のステップは、私が経験したことのほとんどがApacheであったため、かつてないほどの痛烈な502 Bad Gatewayエラーに苦労し、ngginxを初めて学習したときの頭痛の数時間の頂点です。
この回答は、コンテナが相互に通信できないことから生じる502 Bad Gatewayエラーを強制終了する方法を示すことです。
コンテナが互いに話し合うことは、私が明らかなユースケースであると予想していたにもかかわらず、何らかの理由で理解するのが本当に困難だったので、この答えが誰かの苦痛を救うことを願っています。しかし、再び、私は愚かです。そして、このアプローチを改善する方法を教えてください。
docker links を使用すると、上流のコンテナをnginxコンテナにリンクできます。追加された機能は、dockerがホストファイルを管理することです。つまり、潜在的にランダムなIPではなく名前を使用して、リンクされたコンテナーを参照できます。
AJBの「オプションB」 は、ベースのUbuntuイメージを使用して、自分でnginxをセットアップすることで機能するようにできます。 (Docker HubのNginxイメージを使用した場合は機能しませんでした。)
私が使用したDockerファイルは次のとおりです。
FROM ubuntu
RUN apt-get update && apt-get install -y nginx
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
RUN rm -rf /etc/nginx/sites-enabled/default
EXPOSE 80 443
COPY conf/mysite.com /etc/nginx/sites-enabled/mysite.com
CMD ["nginx", "-g", "daemon off;"]
私のnginx設定(別名:conf/mysite.com):
server {
listen 80 default;
server_name mysite.com;
location / {
proxy_pass http://website;
}
}
upstream website {
server website:3000;
}
そして最後に、どのようにコンテナを開始するか:
$ docker run -dP --name website website
$ docker run -dP --name nginx --link website:website nginx
これにより、nginxがポート3000を公開する2番目のdockerコンテナーを指すようになりました。
@gdbjの答えは素晴らしい説明であり、最新の答えです。ただし、こちらはより簡単なアプローチです。
したがって、80
をリッスンしているnginxからのすべてのトラフィックを8080
を公開する別のコンテナにリダイレクトする場合、最小構成は次のようになります:
nginx.conf:
server {
listen 80;
location / {
proxy_pass http://client:8080; # this one here
proxy_redirect off;
}
}
docker-compose.yml
version: "2"
services:
entrypoint:
image: some-image-with-nginx
ports:
- "80:80"
links:
- client # will use this one here
client:
image: some-image-with-api
ports:
- "8080:8080"
Anand Mani Sankarから 記事 を見つけたところ、docker composerでnginxアップストリームプロキシを使用する簡単な方法を示しています。
基本的に、docker-composeファイルでインスタンスのリンクとポートを設定し、それに応じてnginx.confでアップストリームを更新する必要があります。