[解決済み] Nginxをアップストリームプロキシとして使用するためにDockerのポートマッピングを設定するには?
質問
更新情報 II
2015年7月16日になり、また状況が変わりました。Jason Wilder氏のこの自動化されたコンテナを発見したのです。 https://github.com/jwilder/nginx-proxy を発見し、このコンテナをdockerで実行するのと同じくらいの時間でこの問題を解決してくれました。これは現在、私がこの問題を解決するために使っているソリューションです。
更新
現在、2015 年 7 月ですが、Docker コンテナのネットワーク化に関して状況は大きく変化しています。現在では、この問題を解決するさまざまなオファリングが (さまざまな方法で) 存在します。
この投稿は、サービス発見に対する docker --link アプローチの基本的な理解を得るために使用されるべきもので、これと同じくらい基本的で、非常にうまく機能し、実際には他のソリューションのほとんどよりも派手なダンスは必要ありません。クラスタ内の別々のホストでコンテナをネットワーク化するのはかなり難しく、一度ネットワーク化したコンテナを再起動することはできないという制限がありますが、同じホストでコンテナをネットワーク化する迅速かつ比較的簡単な方法を提供しています。この問題を解決するために使用する可能性のあるソフトウェアが、その内部で実際に何を行っているかを知るには良い方法です。
さらに、Docker の nascent network、Hashicorp の consul、Weaveworks の weave、Jeff Lindsay の progrium/consul & gliderlabs/registrator および Google の Kubernetes もおそらくチェックしたいと思うことでしょう。
etcd、fleet、flannelを利用するCoreOSのオファリングもあります。
そして、本当にパーティーをしたいのであれば、MesosphereやDeis、Flynnを動かすためにクラスターをスピンアップすることができます。
もしあなたがネットワークの初心者なら (私のように)、老眼鏡を持って、Wi-Fi で "Paint The Sky With Stars - The Best of Enya" を起動して、ビールを飲みましょう - 自分がしようとしていることを正確に理解するまでしばらく時間がかかると思います。ヒント: あなたはクラスタコントロールプレーンにサービスディスカバリレイヤーを実装しようとしているのです。土曜の夜を過ごすにはとても良い方法です。
これはとても楽しいことですが、すぐに飛び込む前に、ネットワーク全般についてもっとよく学ぶ時間をとればよかったと思います。結局、Digital Ocean Tutorial の慈悲深い神々から、いくつかの投稿を見つけました。ネットワーク用語の紹介と理解 ... ネットワーキング 飛び込む前に、まずこれらを数回読むことをお勧めします。
楽しんでください。
オリジナル投稿
のポートマッピングを把握できないのですが。
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
へのリンクを宣言することができます。
実行中
コンテナへのリンクを宣言できます。つまり、コンテナを順番に起動する必要があり、後者のコンテナだけが前者のコンテナにリンクすることができるのです。このように
#!/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
コンテナは他のコンテナとはリンクしていませんが
App
コンテナは
API
と
Nginx
の両方とリンクしています。
API
と
App
.
この結果は
env
バーを変更し
/etc/hosts
の中に存在するファイル
API
と
App
のコンテナを作成します。結果はこのようになります。
/etc/hosts
実行中
cat /etc/hosts
の中で
Nginx
のコンテナ内では、以下のようになります。
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
の中で
Nginx
のコンテナ内では、以下のようになります。
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
の両方があることがわかると思います。
/etc/hosts
ファイルエントリと
env
バーには、リンクされたコンテナのどれかのローカル IP アドレスが含まれます。私が知る限り、リンク オプションを宣言したコンテナを実行したときに起こることはこれだけです。しかし、今度はこの情報を使って
nginx
の中で
Nginx
コンテナの中で
Nginxの設定
ここが少しやっかいなところで、いくつかの選択肢があります。のエントリを指すようにサイトを設定することができます。
/etc/hosts
というファイルを
docker
が作成したファイルを利用するか、あるいは
ENV
のバーを利用し、文字列置換を実行する(私は
sed
を実行します。
nginx.conf
の中にある他の conf ファイルも参照してください。
/etc/nginx/sites-enabled
フォルダにある他の conf ファイルを編集して、IP 値を挿入します。
選択肢A:ENVバーを使ってNginxを設定する
このオプションは、私は
/etc/hosts
ファイルオプションがうまくいかなかったので、このオプションを選択しました。私はすぐにオプション B を試して、何か発見があればこの記事を更新します。 を試してみて、何か発見があればこの投稿を更新します。
このオプションと
/etc/hosts
ファイルオプションを使う場合との大きな違いは
Dockerfile
としてシェルスクリプトを使用することです。
CMD
の引数として使用し、その引数が文字列置換を行なって
ENV
から 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
で文字列置換を行っています。
APP_IP
に書き込んだプレースホルダーの文字列置換を行います。
upstream
ブロックに書き込んだ
api.myapp.conf
と
app.myapp.conf
のファイルです。
このask.ubuntu.comの質問は、それを非常にうまく説明しています。 コマンドを使用してファイル内のテキストを検索および置換する
GOTCHA OSXでは
sed
はオプションの扱いが異なります。-i
フラグです。 Ubuntuでは-i
フラグは「その場」での置き換えを処理します。 ファイルを開き、テキストを変更し、そして同じファイルを「上書き保存」します。 ファイルを開きます。 OSX では-i
フラグ が必要です。 結果のファイルが持つべきファイル拡張子を指定します。拡張子のないファイルを扱う場合は、''を-i
フラグの値として '' を入力する必要があります。GOTCHA ENVバーを正規表現で使用するには、以下のようにします。
sed
が置換したい文字列を見つけるために使う正規表現内で ENV 変数を使うには、変数を二重引用符でくくる必要があります。つまり、正しい構文は、奇妙に見えるかもしれませんが、上記のとおりです。
docker はコンテナを起動し、コンテナ内で
Nginx-Startup.sh
スクリプトが実行され、そのスクリプトでは
sed
を使って値を変更します。
APP_IP
を対応する
ENV
変数で指定した
sed
コマンドで指定した変数です。これで、conf ファイルが
/etc/nginx/sites-enabled
ディレクトリの中に、IP アドレスを持つ
ENV
の IP アドレスが格納されています。あなたの
api.myapp.conf
ファイル内には
upstream
ブロックがこのように変更されていることがわかります。
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
はすでに最初の IP を conf ファイルに、あるいは/etc/hosts
ファイルに設定されているのでapi.myapp.com
. これに対する解決策は、おそらくCoreOS
とそのetcd
サービスは、私の限られた理解では、共有のENV
に登録されているすべてのマシンに対して、 同じCoreOS
クラスタに登録されます。これは、私がセットアップして遊ぼうと思っている次のおもちゃです。
オプション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
値を引っ張ってくるだけだと思ったのですが、そうでもなさそうです。
また、いくつかの付随的な問題があって、私の
Elastic Load Balancer
をすべての AZ から調達していたので、このルートを試したとき、それが問題になったかもしれません。そのかわり、Linuxで文字列を置き換える処理の仕方を学ばなければならなかったので、それは楽しかったです。しばらくしたら、これを試して見ようと思います。
関連
-
[解決済み】Docker Nginxが停止しました。[emerg] 1#1: ホストがアップストリームで見つかりません。
-
解決済み net::ERR_CONTENT_LENGTH_MISMATCH 200 (OK)
-
[解決済み] Dockerコンテナの中から、マシンのローカルホストに接続するにはどうすればよいですか?
-
[解決済み] ホストからDockerコンテナにファイルをコピーする方法は?
-
[解決済み] リポジトリを使用せずに、あるホストから別のホストにDockerイメージをコピーする方法
-
[解決済み] ホストからDockerコンテナのIPアドレスを取得する方法
-
[解決済み] Dockerコンテナのシェルに入るにはどうしたらいいですか?
-
[解決済み] 古いDockerコンテナを削除する方法
-
[解決済み] Dockerコンテナのランタイムパフォーマンスコストとは何ですか?
-
[解決済み】Dockerは仮想マシンとどう違うの?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Docker Nginxが停止しました。[emerg] 1#1: ホストがアップストリームで見つかりません。
-
nginx スタートアップ・エラー。nginx.serviceのジョブは、制御プロセスがエラーコードで終了したため失敗しました。
-
アップストリームエラーの読み込み中に、アップストリームが接続を早々に切断した
-
Nginxのエラー「The plain HTTP request was sent to HTTPS port」の解決方法。
-
[解決済み] Nginx -- rootとaliasによる静的ファイル提供の混乱
-
[解決済み】NGinx デフォルトの公開WWWの場所は?
-
[解決済み】Kubernetes サービス 外部 ip 保留
-
[解決済み] React-routerとnginx
-
[解決済み] nginxでproxy_passを使用しているときに応答ヘッダを追加するには?
-
[解決済み] NGINXのgzipでJavaScriptファイルが圧縮されない