1. ホーム
  2. nginx

[解決済み] Nginxをアップストリームプロキシとして使用するためにDockerのポートマッピングを設定するには?

2023-05-14 03:41:06

質問

更新情報 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 コンテナは APINginx の両方とリンクしています。 APIApp .

この結果は env バーを変更し /etc/hosts の中に存在するファイル APIApp のコンテナを作成します。結果はこのようになります。



/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.confapi.myapp.conf .

マジックは Nginx-Startup.sh で、ここでは sed で文字列置換を行っています。 APP_IP に書き込んだプレースホルダーの文字列置換を行います。 upstream ブロックに書き込んだ api.myapp.confapp.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.confapp.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で文字列を置き換える処理の仕方を学ばなければならなかったので、それは楽しかったです。しばらくしたら、これを試して見ようと思います。