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
, Hashicorpconsul
, Weaveworksweave
, Jeff Lindsayprogrium/consul
&gliderlabs/registrator
, et GoogleKubernetes
.il y a aussi les CoreOS offres qui utilisent
etcd
,fleet
, etflannel
.et si vous voulez vraiment avoir une fête, vous pouvez lancer un cluster pour exécuter
Mesosphere
, ouDeis
, ouFlynn
.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 votreCluster 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
etUnderstanding ... 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?
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 fichiernginx.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. MaisNginx
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 pourapi.myapp.com
. La solution est d'utiliserCoreOS
et son serviceetcd
qui, à ma connaissance limitée, agit comme unENV
partagé pour toutes les machines enregistrées dans le même clusterCoreOS
. 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.
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.
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.
"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.
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"
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.