Nginxをアップストリームプロキシとして使用するようにDockerポートマッピングを構成するにはどうすればよいですか?


86

アップデートII

今は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/consulgliderlabs/registrator、およびGoogleのもチェックすることをお勧めします。Kubernetes

そこもだCoreOSの利用供物etcdfleetflannel

そして、あなたは本当にあなたが実行するには、クラスターをスピンアップすることができパーティーしたい場合Mesosphere、またはDeis、またはをFlynn

(私のように)ネットワーキングに不慣れな場合は、老眼鏡を取り出し、Wi-Hi-Fiで「PaintThe Sky With Stars — The Best of Enya」をポップして、ビールを割る必要があります。何をしようとしているのかを正確に理解する少し前に。ヒント:にを実装しようとしService Discovery LayerていますCluster Control Plane。土曜日の夜を過ごすのにとてもいい方法です。

それは楽しみがたくさんですが、私はダイビングの直前に、一般的にネットワーキングについてのより良い自分自身を教育するために時間を取らよかっ私は最終的には慈悲深いデジタルオーシャンチュートリアルの神々からのカップルの記事を見つけました:。Introduction to Networking TerminologyUnderstanding ... Networking。ダイビングする前に、まずそれらを数回読むことをお勧めします。

楽しんで!



元の投稿

Dockerコンテナのポートマッピングがわからないようです。具体的には、同じサーバー上の別のポートでリッスンしているNginxから別のコンテナーにリクエストを渡す方法。

私は次のようなNginxコンテナ用のDockerfileを持っています:

FROM ubuntu:14.04
MAINTAINER Me <me@myapp.com>

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で接続してログを調べると、エラーは表示されません。

何か助けはありますか?


1
質問の本文ではなく、回答に回答資料を入れてください。
jscs 2016

回答:


56

@ 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変数

コンテナ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

リンクされたコンテナのローカルIPアドレスを含む/etc/hostsファイルエントリとenv変数の両方があることがわかります。私の知る限り、リンクオプションが宣言された状態でコンテナを実行すると、これがすべて発生します。ただし、この情報を使用nginxしてNginxコンテナー内で構成できるようになりました。



Nginxの構成

ここで少し注意が必要ですが、いくつかのオプションがあります。作成した/etc/hostsファイルのエントリを指すようにサイトを構成するdockerか、ENVvarsを利用して、フォルダ内にある可能性のある他のconfファイルで文字列置換(私が使用sed)を実行してIPを挿入することができます値。nginx.conf/etc/nginx/sites-enabled



オプションA:ENV変数を使用してNginxを構成する

これは、/etc/hostsファイルオプションを機能させることができなかったために 使用したオプションです。私はすぐにオプションBを試し、発見があればこの投稿を更新します。

このオプションと/etc/hostsファイルオプションの使用の主な違いはDockerfileCMD引数としてシェルスクリプトを使用するように作成する方法です。これにより、文字列の置換が処理され、IP値がENVconfファイルにコピーされます。

これが私が最終的に得た構成ファイルのセットです:

Dockerfile

FROM ubuntu:14.04
MAINTAINER Your Name <you@myapp.com>

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フラグの値として ''を入力する必要があります。

GOTCHAsed置換する文字列を見つけるために使用する 正規表現内でENV変数を使用するには、変数を二重引用符で囲む必要があります。したがって、見た目は不安定ですが、正しい構文は上記のとおりです。

そのため、dockerはコンテナーを起動し、Nginx-Startup.shスクリプトの実行をトリガーしました。スクリプトは、コマンドで指定した対応する変数にsedAPP_IPを変更するために使用ENVされていましたsed。これで、コンテナの起動時にdockerが設定/etc/nginx/sites-enabledしたENV変数のIPアドレスを持つconfファイルがディレクトリ内にあります。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ているため、の新しいIPを判別することはできませんapi.myapp.com。これに対する解決策は、使用する可能性が高くCoreOS、そのetcdサービスは、私の限られた理解ではENV、同じCoreOSクラスターに登録されているすべてのマシンで共有されているように機能します。これは私がセットアップで遊ぶつもりの次のおもちゃです。



オプションB:/etc/hostsファイルエントリを使用する

これ、これを行うためのより速く、より簡単な方法であるはずですが、私はそれを機能させることができませんでした。表向きはあなただけの入力の値/etc/hostsあなたへの参入api.myapp.confapp.myapp.confファイルが、私は仕事に、この方法を得ることができませんでした。

更新:このメソッドを機能させる方法 については、@ WesTodの回答を参照してください。

これが私が行った試みですapi.myapp.conf

upstream api_upstream{
    server API:3000;
}

私の/etc/hostsファイルに次のようなエントリがあることを考えると172.0.0.2 API、値を取得するだけだと思いましたが、そうではないようです。

またElastic Load Balancer、すべてのAZからの調達に関して、いくつかの付随的な問題があったため、このルートを試したときに問題が発生した可能性があります。代わりに、Linuxで文字列の置換を処理する方法を学ぶ必要があったので、それは楽しかったです。しばらくしてから試してみて、どうなるか見ていきます。


2
リンクを使用するもう1つの落とし穴は、APIコンテナーを再起動すると、新しいIPが取得される可能性が高いことです。これは、古いIPを引き続き使用するnginxコンテナ/ etc / hostsファイルには反映されないため、再起動する必要があります。
judoole 2015年

13

コードが魔法のようにすべての人に機能する人気のあるJasonWilderリバースプロキシを使用してみましたが、すべての人(つまり私)に機能するとは限らないことがわかりました。そして、私はNGINXを初めて使用するので、使用しようとしているテクノロジーを理解できなかったことが気に入らなかった。

linkingコンテナに関する上記の説明は非推奨の機能であるため、現在は日付が付けられているため、2セントを追加したいと思いました。だからここにそれを使用してそれを行う方法の説明がありますnetworks。この回答は、Docker Composenginx構成を使用して静的にページングされたWebサイトへのリバースプロキシとしてnginxを設定する完全な例です。

TL; DR;

相互に通信する必要のあるサービスを事前定義されたネットワークに追加します。Dockerネットワークに関する段階的な議論のために、私はここでいくつかのことを学びました: https //technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and-ぶさいく/

ネットワークを定義する

まず、すべてのバックエンドサービスが通信できるネットワークが必要です。私は私のと呼んwebだが、それはあなたが望むものなら何でもよい。

docker network create 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介してマウントしたローカルフォルダーの下に保存する必要があります。volumesdocker-compose

大事なことを言い忘れましたが、nginxに設定をリロードするように指示します。

docker exec nginx-proxy service nginx reload

これらの一連の手順は、これまでにない痛みを伴う502 Bad Gatewayエラーに苦労し、私の経験のほとんどがApacheであったため、初めてnginxを学習したときの、何時間もの激しい頭痛の集大成です。

この答えは、コンテナが相互に通信できないことに起因する502 BadGatewayエラーを強制終了する方法を示すためのものです。

コンテナを相互に通信させることは、明らかなユースケースであると私が期待していたにもかかわらず、何らかの理由で理解するのが非常に困難だったため、この回答が誰かの苦痛を軽減することを願っています。しかし、もう一度、私はばかげています。そして、このアプローチを改善する方法を教えてください。


ああ!オレ502 Gateway Error、最近の悪名高い古典。会話を進め、そのような詳細な解決策を提供するために時間を割いてくれた@gdbjに感謝します。
AJB 2017年

これに時間を割いてくれてありがとうと言いたかっただけで、かなりの手間が省けました。ありがとうございました。
単一エンティティ

10

Dockerリンクを使用して、アップストリームコンテナをnginxコンテナにリンクできます。追加された機能は、dockerがホストファイルを管理することです。つまり、ランダムなIPではなく名前を使用してリンクされたコンテナーを参照できるようになります。


7

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コンテナを上流に向けました。


アシストウェスをありがとう!休暇から戻ったら、これを試してみます。
AJB 2015年

私は公式画像でこのようないくつかの問題を抱えています。私は、ubuntuボックスをベースにして、dockerファイルから直接行をプルする方がはるかに良いことを発見しました。これが必要なわけではありませんが、残念ながら....
ウェス・トッド

1
これは素晴らしいことですが、私が得られないのは、nginx構成が「website」の値を知っている方法です。NginxマジックまたはDockerマジック、または他の何か?
カイルチャダ2015年

1
この行upstream website {は、nginxのWebサイト値を定義します。それはあなたがあなたのでそれから使うものですproxy_pass。こののdocker部分は、一貫性を保つために同じ名前を使用していますが、nginxの設定とは関係ありません。それはもう少しお読みくださいプロキシパスをクリアするには:upstream website { server localhost:3000; }
ウェス・トッド

2
参考までに、最新の公式nginxイメージ(1.9.2)を使用していますが、機能しているようです。だから多分彼らは問題を修正しました。
Pejvan 2015

6

@gdbjの答えは素晴らしい説明であり、最新の答えです。ただし、これはより簡単なアプローチです。

したがって、nginxがリッスンし80ているすべてのトラフィックを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"

Dockerドキュメント


最初は、ポートの衝突に問題があると思いました。
gdbj 2018

@gdbj私の問題は、コンテナ間のURL / IP解決でした。私たちも同じだったと思います。あなたの場合、ネットワークを使用しますが、これも正常に機能しています。私の場合、コンテナをリンクするだけです
Diolor 2018

素晴らしい、それが私が必要としたすべてです!その答えをありがとう。
Floran Gmehlin 2018年

2

DockerComposerでnginxアップストリームプロキシを使用する簡単な方法を示しているAnandManiSankarの記事を見つけました。

基本的に、docker-composeファイルでインスタンスのリンクとポートを構成し、それに応じてnginx.confでアップストリームを更新する必要があります。


1
記事linksでは非推奨のを使用しています。今すぐネットワークを使用する: docs.docker.com/engine/userguide/networking
gdbj

この場合も、魔法のnginx conf置換は機能しますか?
ヤマネ2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.