Comment configurer la mise en correspondance des ports Docker pour utiliser Nginx en tant que proxy amont?

mise à Jour II

nous sommes maintenant le 16 juillet 2015 et les choses ont encore changé. J'ai découvert ce conteneur automatique de Jason Wilder : https://github.com/jwilder/nginx-proxy et il résout ce problème dans environ aussi longtemps qu'il faut à docker run le conteneur. C'est la solution que j'utilise pour résoudre ce problème.



mise à Jour

nous sommes maintenant en juillet 2015 et les choses ont changé radicalement en ce qui concerne à la mise en réseau des conteneurs Dockers. Il y a maintenant beaucoup de différents offres qui résolvent ce problème (de différentes façons).

vous devriez utiliser ce post pour obtenir une compréhension de base de la docker --link approche à la découverte de service, qui est à peu près aussi basique qu'il obtient, fonctionne très bien, et nécessite en fait moins de danse Fantaisie que la plupart des autres solutions. Il est limité en ce qu'il est assez difficile de mettre en réseau des conteneurs sur des hôtes séparés dans un cluster donné, et les conteneurs ne peuvent pas être redémarrés une fois mis en réseau, mais offre un moyen rapide et relativement facile de mettre en réseau des conteneurs sur le même hôte. C'est un bon moyen de se faire une idée de ce que le logiciel que vous allez probablement utiliser pour résoudre ce problème fait réellement sous le capot.

en plus, vous voudrez probablement vérifier aussi docker's nascent network , Hashicorp consul , Weaveworks weave , Jeff Lindsay progrium/consul & gliderlabs/registrator , et Google Kubernetes .

il y a aussi les CoreOS offres qui utilisent etcd , fleet , et flannel .

et si vous voulez vraiment avoir une fête, vous pouvez lancer un cluster pour exécuter Mesosphere , ou Deis , ou Flynn .

si vous êtes nouveau au réseautage (comme moi) alors vous devriez sortir vos lunettes de lecture, pop " peindre le ciel avec des étoiles - Le meilleur de Enya " sur le Wi-Hi-Fi, et de casser une bière - il va falloir un certain temps avant que vous comprenez vraiment exactement ce que vous essayez de faire. Conseil: vous essayez d'implémenter un Service Discovery Layer dans votre Cluster Control Plane . C'est une très belle façon de passer un samedi soir.

C'est très amusant, mais j'aurais aimé prendre le temps de mieux m'instruire sur le réseautage en général avant de plonger directement. J'ai finalement trouvé quelques messages de la benevolent Digital Ocean Tutoriel dieux: Introduction to Networking Terminology et Understanding ... Networking . Je suggère de les lire quelques fois avant de plonger.

amusez-vous bien!





Original Post

il semble que je n'arrive pas à saisir la cartographie des ports pour les conteneurs Docker . Comment transmettre les demandes de Nginx à un autre conteneur, en écoutant sur un autre port, sur le même serveur.

j'ai un fichier Dockerfile pour un conteneur Nginx comme ça:

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"]





Et puis le fichier de configuration api.myapp.com ressemble à ceci:

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;

    }

}

Et puis un autre pour app.myapp.com .

et puis je cours:

sudo docker run -p 80:80 -p 443:443 -d --name Nginx myusername/nginx



Et tout se passe très bien, mais les demandes ne sont pas transmises aux autres conteneurs/ports. Et quand j'entre dans le container Nginx et que j'inspecte les journaux, Je ne vois aucune erreur.

de l'aide?

77
demandé sur AJB 2015-01-13 03:03:58

6 réponses

la réponse de @T0xicCode est correcte, mais j'ai pensé que je voudrais développer sur les détails car il m'a réellement pris environ 20 heures pour finalement obtenir une solution de travail mis en œuvre.

si vous cherchez à exécuter Nginx dans son propre conteneur et l'utiliser comme un mandataire inverse pour équilibrer plusieurs applications sur la même instance de serveur, alors les étapes que vous devez suivre sont les suivantes::



Lien Vos Contenants

lorsque vous docker run vos conteneurs, typiquement en entrant un script shell dans User Data , vous pouvez déclarer des liens vers n'importe quel autre contenant "courant . Cela signifie que vous devez démarrer vos conteneurs en ordre et que seuls ces derniers peuvent être reliés aux premiers. Comme ceci:

#!/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

ainsi, dans cet exemple, le API conteneur n'est pas lié à d'autres, mais le App conteneur est lié à API et Nginx est liée à la fois API et App .

il en résulte des changements aux vars env et aux fichiers /etc/hosts qui résident dans les conteneurs API et App . Les résultats ressemblent tellement:



/etc / hosts

en cours d'exécution cat /etc/hosts dans votre conteneur Nginx produira le suivant:

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

en cours d'exécution env dans votre conteneur Nginx produira le suivant:

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

j'ai tronqué beaucoup de vars réels, mais les ci-dessus sont les valeurs clés dont vous avez besoin pour afficher le trafic sur vos conteneurs.

pour obtenir un shell pour exécuter les commandes ci-dessus dans un conteneur en cours d'exécution, utilisez ce qui suit:

sudo docker exec -i -t Nginx bash

vous pouvez voir que vous avez maintenant les deux entrées de fichier /etc/hosts et env vars qui contiennent l'adresse IP locale pour n'importe lequel des conteneurs qui ont été liés. Pour autant que je sache, c'est tout ce qui se passe quand vous utilisez des conteneurs avec options de lien déclarées. Mais vous pouvez maintenant utiliser cette information pour configurer nginx dans votre Nginx conteneur.





Configuration De Nginx

C'est là que ça devient un peu délicat, et il y a quelques options. Vous pouvez choisir de configurer vos sites pour pointer vers une entrée dans le /etc/hosts fichier que docker créé, ou vous pouvez utiliser le ENV vars et lancer un remplacement de chaîne de caractères (j'ai utilisé sed ) sur votre nginx.conf et tout autre fichier conf qui peut être dans votre /etc/nginx/sites-enabled dossier pour insérer les valeurs IP.





OPTION A: configurer Nginx en utilisant ENV Vars

C'est l'option que j'ai choisi parce que je ne pouvais pas obtenir le /etc/hosts option dans le fichier de travail. Je vais être essayer L'Option B assez tôt et de mettre à jour ce post avec toutes les conclusions.

la différence clé entre cette option et l'utilisation de l'option /etc/hosts est la façon dont vous écrivez votre Dockerfile pour utiliser un script shell comme argument CMD , qui à son tour gère le remplacement de la chaîne de caractères pour copier les valeurs IP de ENV vers votre fichier conf.

Voici l'ensemble des fichiers de configuration j'ai:

fichier 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;


}

NOTE: il est important d'inclure daemon off; dans votre fichier nginx.conf pour s'assurer que votre conteneur ne sort pas immédiatement après le lancement.

de l'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

je vous laisse faire vos recherches sur la plupart des contenus de nginx.conf et api.myapp.conf .

la magie se produit dans Nginx-Startup.sh où nous utilisons sed pour faire le remplacement de chaîne sur le placeholder APP_IP que nous avons écrit dans le upstream bloc de nos api.myapp.conf et app.myapp.conf fichiers.

Ce ask.ubuntu.com question il explique très bien: trouver et remplacer le texte dans un fichier en utilisant les commandes

GOTCHA Sur OSX, sed gère les options différemment, le drapeau -i spécifiquement. Sur Ubuntu, le drapeau -i traitera le remplacement "en place"; il ouvrir le fichier, modifier le texte, et puis " enregistrer sur le même fichier. Sur OSX, le -i drapeau exige l'extension de fichier que vous souhaitez que le fichier résultant d'avoir. Si vous travaillez avec un fichier qui n'a pas d'extension, vous devez d'entrée " comme valeur pour le -i pavillon.

GOTCHA Pour utiliser ENV vars dans le regex que sed utilise pour trouver la chaîne que vous voulez remplacer, vous devez envelopper le var dans des guillemets. Donc la bonne, quoique vilain-la recherche, la syntaxe est comme ci-dessus.

donc docker a lancé notre conteneur et déclenché le script Nginx-Startup.sh à exécuter, qui a utilisé sed pour changer la valeur APP_IP à la variable correspondante ENV que nous avons fournie dans la commande sed . Nous avons maintenant des fichiers conf dans notre répertoire /etc/nginx/sites-enabled qui ont les adresses IP du ENV que docker a défini lors du démarrage du conteneur. Dans votre fichier api.myapp.conf , vous verrez que le bloc upstream a changé pour ceci:

upstream api_upstream{
    server 172.0.0.2:3000;
}

l'adresse IP que vous voyez peut être différente, mais j'ai remarqué que c'est habituellement 172.0.0.x .

vous devriez maintenant avoir tout routage approprié.

GOTCHA Vous ne pouvez pas redémarrer/redémarrer les conteneurs une fois que vous avez lancé l'instance initiale. Docker fournit à chaque conteneur une nouvelle IP lors du lancement et ne semble pas réutiliser tout ce qu'il est utilisé avant. Donc api.myapp.com 172.0.0.2 la première fois, mais se 172.0.0.4 la prochaine fois. Mais Nginx aura déjà défini la première adresse IP dans ses fichiers conf, ou dans son fichier /etc/hosts , de sorte qu'il ne pourra pas déterminer la nouvelle adresse IP pour api.myapp.com . La solution est d'utiliser CoreOS et son service etcd qui, à ma connaissance limitée, agit comme un ENV partagé pour toutes les machines enregistrées dans le même cluster CoreOS . C'est le prochain jouet avec lequel je vais jouer.





OPTION B: utiliser /etc/hosts entrées de fichier

Ce devrait être le plus rapide, la plus facile de le faire, mais je ne pouvais pas le faire fonctionner. Ostensiblement vous venez d'entrer la valeur de la /etc/hosts entrée dans votre api.myapp.conf et app.myapp.conf fichiers, mais je ne pouvais pas obtenir cette méthode de travail.

mise à JOUR: Voir @Wes Tod's answer pour des instructions sur la façon de faire fonctionner cette méthode.

Voici la tentative que j'ai faite dans api.myapp.conf :

upstream api_upstream{
    server API:3000;
}

considérant Qu'il y a une entrée dans mon fichier /etc/hosts comme ceci: 172.0.0.2 API j'ai pensé qu'il serait juste tirer dans la valeur, mais il ne semble pas être.

j'ai également eu un couple de problèmes accessoires avec mon Elastic Load Balancer l'approvisionnement de tous les AZ de sorte que peut avoir été le problème lorsque j'ai essayé cette route. Au lieu de cela, j'ai dû apprendre à gérer le remplacement des chaînes sous Linux, alors c'était amusant. Je vais essayer ça dans un moment et voir comment ça se passe.

54
répondu AJB 2017-05-23 12:18:10

en utilisant docker links , vous pouvez relier le conteneur amont au conteneur nginx. Une fonctionnalité ajoutée est que docker gère le fichier hôte, ce qui signifie que vous serez en mesure de vous référer au conteneur lié en utilisant un nom plutôt que l'ip potentiellement aléatoire.

9
répondu T0xicCode 2015-01-13 00:10:38

j'ai essayé D'utiliser le populaire mandataire inversé Jason Wilder que le code-fonctionne magiquement pour tout le monde, et j'ai appris que cela ne fonctionne pas pour tout le monde (i.e.: moi). Et je suis tout nouveau à NGINX, et je n'ai pas aimé ça, Je ne comprenais pas les technologies que j'essayais d'utiliser.

voulait ajouter mes 2 cents, parce que la discussion ci-dessus autour de linking conteneurs ensemble est maintenant datée car il s'agit d'une caractéristique dépréciée. Voici donc une explication sur comment le faire en utilisant networks . Cette réponse est un exemple complet de mise en place de nginx en tant que mandataire inversé vers un site Web en pagination statique en utilisant la configuration Docker Compose et nginx.

TL; DR;

ajouter les services qui ont besoin de se parler sur un réseau prédéfini. Pour une discussion pas à pas sur les réseaux de Dockers, j'ai appris des choses ici.: https://technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and-the-ugly/

définir le réseau

tout d'abord, nous avons besoin d'un réseau sur lequel tous vos services backend peuvent parler. J'ai appelé le mien web mais il peut être ce que vous voulez.

docker network create web

Construire l'Application

nous allons juste faire une simple application de site web. Le site web est un indice simple.page html servie par un conteneur nginx. Le contenu est un volume monté sur l'hôte sous un dossier content

DockerFile:

FROM nginx
COPY default.conf /etc/nginx/conf.d/default.conf

par défaut.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-composer.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

notez que nous n'avons plus besoin de cartographie des ports ici. Nous exposons le port 80. C'est pratique pour éviter les collisions avec les ports.

Exécuter l'Application

Fire ce site Web avec

docker-compose up -d

quelques vérifications amusantes en ce qui concerne les mappages dns pour votre conteneur:

docker exec -it sample-site bash
ping sample-site

Ce ping devrait fonctionner, à l'intérieur de votre conteneur.

Construire le Proxy

Nginx Proxy Inverse:

Dockerfile

FROM nginx

RUN rm /etc/nginx/conf.d/*

nous avons réinitialisé toute la configuration de l'hôte virtuel, puisque nous allons la personnaliser.

docker-composer.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

Exécuter le Proxy

Feu le proxy à l'aide de notre fidèle

docker-compose up -d

en supposant qu'il n'y a aucun problème, alors vous avez deux conteneurs qui fonctionnent qui peuvent se parler en utilisant leurs noms. Nous allons tester.

docker exec -it nginx-proxy bash
ping sample-site
ping nginx-proxy

configurer un hôte virtuel

le dernier détail est de configurer le fichier d'hébergement virtuel afin que le proxy puisse diriger le trafic en fonction de la façon dont vous voulez configurer votre correspondance:

exemple de site.conf pour notre config d'hébergement virtuel:

  server {
    listen 80;
    listen [::]:80;

    server_name my.domain.com;

    location / {
      proxy_pass http://sample-site;
    }

  }

basé sur la façon dont le proxy a été configuré, vous aurez besoin de ce fichier stocké dans votre dossier local conf.d que nous avons monté via la déclaration volumes dans le fichier docker-compose .

dernier mais non des moindres, dites à nginx de recharger sa configuration.

docker exec nginx-proxy service nginx reload

cette séquence d'étapes est le point culminant d'heures de coups de tête que j'ai lutté avec l'erreur 502 Bad Gateway toujours douloureuse, et apprendre nginx pour la première fois, puisque la plupart de mon expérience était avec Apache.

cette réponse est pour montrer comment tuer l'erreur de passerelle Mauvaise 502 qui résulte de conteneurs n'étant pas en mesure de parler les uns avec les autres.

j'espère que cette réponse sauve quelqu'un là-bas heures de douleur, depuis l'obtention de conteneurs à parler les uns aux autres a été vraiment difficile à comprendre pour une raison quelconque, bien que ce soit ce que je devrait être une évidence de cas d'utilisation. Mais encore une fois, moi muets. Et s'il vous plaît laissez-moi savoir comment je peux améliorer cette approche.

8
répondu gdbj 2017-05-06 01:12:57

"Option b" peut être fait pour fonctionner en utilisant L'image Ubuntu de base et la mise en place de nginx sur votre propre. (Cela n'a pas fonctionné quand j'ai utilisé L'image Nginx de Docker Hub.)

voici le fichier Docker que j'ai utilisé:

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;"]

My nginx config (alias: conf/mysite.com):

server {
    listen 80 default;
    server_name mysite.com;

    location / {
        proxy_pass http://website;
    }
}

upstream website {
    server website:3000;
}

et enfin, comment je démarre mes conteneurs:

$ docker run -dP --name website website
$ docker run -dP --name nginx --link website:website nginx

mon nginx a pointé l'amont vers le deuxième conteneur docker qui a exposé le port 3000.

7
répondu Wes Todd 2017-05-23 12:34:30

la réponse de @gdbj est une excellente explication et la réponse la plus récente. Voici cependant une approche plus simple.

donc si vous voulez rediriger tout le trafic de nginx écoutant 80 vers un autre conteneur exposant 8080 , la configuration minimale peut être aussi peu que:

nginx.conf:

server {
    listen 80;

    location / {
        proxy_pass http://client:8080; # this one here
        proxy_redirect off;
    }

}

docker-composer.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 docs

4
répondu Diolor 2018-04-08 21:18:56

vient de trouver un article de Anand Mani Sankar qui montre une façon simple d'utiliser Nginx en amont mandataire avec docker composer.

fondamentalement, il faut configurer la liaison d'instance et les ports au fichier docker-composer et mettre à jour en amont à nginx.conf en conséquence.

2
répondu lsborg 2015-11-14 00:07:14