inverso - ¿Cómo configurar la asignación de puertos Docker para usar Nginx como proxy ascendente?
proxy inverso apache (6)
Actualización II
Ahora es el 16 de julio de 2015 y las cosas han cambiado nuevamente. He descubierto este contenedor automático de Jason Wilder :
https://github.com/jwilder/nginx-proxy
y resuelve este problema aproximadamente en el tiempo que le lleva adocker run
el contenedor. Esta es ahora la solución que estoy usando para resolver este problema.
Actualizar
Ahora es julio de 2015 y las cosas han cambiado drásticamente con respecto a los contenedores Docker en red. Ahora hay muchas ofertas diferentes que resuelven este problema (en una variedad de formas).
Debería usar esta publicación para obtener una comprensión básica del enfoque de
docker --link
para el descubrimiento de servicios, que es tan básico como parece, funciona muy bien y en realidad requiere menos baile elegante que la mayoría de las otras soluciones. Está limitado porque es bastante difícil conectar en red contenedores en hosts separados en cualquier clúster dado, y los contenedores no pueden reiniciarse una vez que están en red, pero ofrecen una forma rápida y relativamente fácil de conectar en red contenedores en el mismo host. Es una buena manera de tener una idea de lo que el software que probablemente usará para resolver este problema realmente está haciendo bajo el capó.Además, es probable que también desee ver la
network
naciente de Docker, elconsul
de Hashicorp, elweave
Weaveworks, elprogrium/consul
ygliderlabs/registrator
Jeff Lindsay y losKubernetes
de Google.También están las ofertas de CoreOS que utilizan
etcd
,fleet
yflannel
.Y si realmente quieres tener una fiesta, puedes girar un clúster para ejecutar
Mesosphere
, oDeis
oFlynn
.Si eres nuevo en la creación de redes (como yo), entonces deberías sacar tus lentes de lectura, hacer estallar "Paint The Sky With Stars - The Best of Enya" en el Wi-Hi-Fi y tomar una cerveza: será un tiempo antes de que realmente entiendas exactamente qué es lo que estás tratando de hacer. Sugerencia: está intentando implementar una
Service Discovery Layer
en suCluster Control Plane
. Es una muy buena manera de pasar un sábado por la noche.Es muy divertido, pero desearía haberme tomado el tiempo de educarme mejor sobre las redes en general antes de sumergirme. Finalmente encontré un par de publicaciones de los benevolentes dioses del Tutorial Digital Ocean:
Introduction to Networking Terminology
yUnderstanding ... Networking
Introduction to Networking Terminology
Understanding ... Networking
. Sugiero leerlas varias veces antes de sumergirse.¡Que te diviertas!
Publicación original
Parece que no puedo entender la asignación de puertos para los contenedores
Docker
.
Específicamente cómo pasar solicitudes de Nginx a otro contenedor, escuchando en otro puerto, en el mismo servidor.
Tengo un Dockerfile para un contenedor Nginx así:
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"]
Y luego el archivo de configuración
api.myapp.com
ve así:
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;
}
}
Y luego otro para
app.myapp.com
también.
Y luego corro:
sudo docker run -p 80:80 -p 443:443 -d --name Nginx myusername/nginx
Y todo funciona bien, pero las solicitudes no se transfieren a los otros contenedores / puertos.
Y cuando me meto en el contenedor Nginx e inspecciono los registros, no veo errores.
¿Alguna ayuda?
Acabo de encontrar un article de Anand Mani Sankar que muestra una manera simple de usar el proxy ascendente nginx con Docker Composer.
Básicamente, uno debe configurar el enlace de instancia y los puertos en el archivo docker-compose y actualizar en sentido ascendente en nginx.conf en consecuencia.
Con los enlaces de Docker , puede vincular el contenedor ascendente al contenedor nginx. Una característica adicional es que Docker administra el archivo host, lo que significa que podrá hacer referencia al contenedor vinculado mediante un nombre en lugar de la IP potencialmente aleatoria.
Intenté usar el popular proxy inverso Jason Wilder que funciona mágicamente por código para todos, y aprendí que no funciona para todos (es decir, para mí). Y soy nuevo en NGINX, y no me gustó que no entendía las tecnologías que estaba tratando de usar.
Quería agregar mis 2 centavos, porque la discusión anterior sobre la
linking
contenedores ahora está fechada ya que es una característica obsoleta.
Así que aquí hay una explicación sobre cómo hacerlo usando
networks
.
Esta respuesta es un ejemplo completo de cómo configurar nginx como proxy inverso para un sitio web estáticamente paginado usando
Docker Compose
y la configuración nginx.
TL; DR;
Agregue los servicios que necesitan comunicarse entre sí en una red predefinida. Para una discusión paso a paso en las redes de Docker, aprendí algunas cosas aquí: https://technologyconversations.com/2016/04/25/docker-networking-and-dns-the-good-the-bad-and-the-ugly/
Definir la red
En primer lugar, necesitamos una red en la que todos sus servicios de back-end puedan hablar.
Llamé a mi
web
pero puede ser lo que quieras.
docker network create web
Construye la aplicación
Simplemente haremos una aplicación de sitio web simple.
El sitio web es una simple página index.html que es servida por un contenedor nginx.
El contenido es un volumen montado para el host bajo el
content
una carpeta
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
Tenga en cuenta que ya no necesitamos mapeo de puertos aquí. Simplemente exponemos el puerto 80. Esto es útil para evitar colisiones de puertos.
Ejecute la aplicación
Enciende este sitio web con
docker-compose up -d
Algunas comprobaciones divertidas sobre las asignaciones de dns para su contenedor:
docker exec -it sample-site bash
ping sample-site
Este ping debería funcionar, dentro de su contenedor.
Construye el proxy
Proxy inverso de Nginx:
Dockerfile
FROM nginx
RUN rm /etc/nginx/conf.d/*
Restablecemos toda la configuración del host virtual, ya que la vamos a personalizar.
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
Ejecute el proxy
Encienda el proxy usando nuestro confiable
docker-compose up -d
Suponiendo que no haya problemas, tiene dos contenedores en ejecución que pueden comunicarse entre sí utilizando sus nombres. Probémoslo.
docker exec -it nginx-proxy bash
ping sample-site
ping nginx-proxy
Configurar host virtual
El último detalle es configurar el archivo de alojamiento virtual para que el proxy pueda dirigir el tráfico en función de cómo quiera configurar su coincidencia:
sample-site.conf para nuestra configuración de alojamiento virtual:
server {
listen 80;
listen [::]:80;
server_name my.domain.com;
location / {
proxy_pass http://sample-site;
}
}
Según cómo se configuró el proxy, necesitará este archivo almacenado en su carpeta
conf.d
local que montamos a través de la declaración de
volumes
en el archivo
docker-compose
.
Por último, pero no menos importante, dígale a nginx que vuelva a cargar su configuración.
docker exec nginx-proxy service nginx reload
Esta secuencia de pasos es la culminación de horas de fuertes dolores de cabeza mientras luchaba con el siempre doloroso error 502 Bad Gateway, y aprendía nginx por primera vez, ya que la mayor parte de mi experiencia fue con Apache.
Esta respuesta es para demostrar cómo eliminar el error 502 Bad Gateway que resulta de que los contenedores no puedan comunicarse entre sí.
Espero que esta respuesta le ahorre a alguien horas de dolor, ya que conseguir contenedores para hablar entre ellos fue realmente difícil de entender por alguna razón, a pesar de ser lo que esperaba que fuera un caso de uso obvio. Pero, de nuevo, yo tonto. Y háganme saber cómo puedo mejorar este enfoque.
La respuesta de @ gdbj es una gran explicación y la respuesta más actualizada. Sin embargo, aquí hay un enfoque más simple.
Entonces, si desea redirigir todo el tráfico desde nginx escuchando
80
a otro contenedor que expone
8080
, la configuración mínima puede ser tan pequeña como:
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"
La respuesta de @ T0xicCode es correcta, pero pensé en ampliar los detalles, ya que en realidad me tomó alrededor de 20 horas finalmente implementar una solución de trabajo.
Si está buscando ejecutar Nginx en su propio contenedor y usarlo como proxy inverso para equilibrar la carga de múltiples aplicaciones en la misma instancia del servidor, los pasos que debe seguir son los siguientes:
Vincula tus contenedores
Cuando la
docker run
sus contenedores, generalmente al ingresar un script de shell en los
User Data
, puede declarar enlaces a cualquier otro contenedor en
ejecución
.
Esto significa que debe iniciar sus contenedores en orden y solo los últimos contenedores pueden vincularse con los anteriores.
Al igual que:
#!/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
Entonces, en este ejemplo, el contenedor de la
API
no está vinculado a ningún otro, pero el contenedor de la
App
está vinculado a la
API
y
Nginx
está vinculado tanto a la
API
como a la
App
.
El resultado de esto son los cambios en los archivos
env
y los archivos
/etc/hosts
que residen dentro de los contenedores
API
y de
App
.
Los resultados se ven así:
/ etc / hosts
Ejecutar
cat /etc/hosts
dentro de su contenedor
Nginx
producirá lo siguiente:
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
Ejecutar
env
dentro de su contenedor
Nginx
producirá lo siguiente:
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
He truncado muchos de los vars reales, pero los anteriores son los valores clave que necesita para proxy del tráfico a sus contenedores.
Para obtener un shell para ejecutar los comandos anteriores dentro de un contenedor en ejecución, use lo siguiente:
sudo docker exec -i -t Nginx bash
Puede ver que ahora tiene entradas de archivos
/etc/hosts
y entornos que contienen la dirección IP local para cualquiera de los contenedores que estaban vinculados.
Hasta donde puedo decir, esto es todo lo que sucede cuando ejecutas contenedores con opciones de enlace declaradas.
Pero ahora puede usar esta información para configurar
nginx
dentro de su contenedor
Nginx
.
Configurando Nginx
Aquí es donde se pone un poco complicado, y hay un par de opciones.
Puede elegir configurar sus sitios para que apunten a una entrada en el
/etc/hosts
que creó
docker
, o puede utilizar los vars de
ENV
y ejecutar un reemplazo de cadena (utilicé
sed
) en su
nginx.conf
y cualquier otro archivo conf que puede estar en su carpeta
/etc/nginx/sites-enabled
para insertar los valores de IP.
OPCIÓN A: Configurar Nginx usando ENV Vars
Esta es la opción que utilicé porque no pude hacer que la opción de archivo
/etc/hosts
funcione. Probaré la Opción B pronto y actualizaré esta publicación con cualquier hallazgo.
La diferencia clave entre esta opción y el uso de la opción de archivo
/etc/hosts
es cómo escribir su
Dockerfile
para usar un script de shell como argumento
CMD
, que a su vez maneja el reemplazo de cadena para copiar los valores de IP de
ENV
a su archivo conf ( s)
Aquí está el conjunto de archivos de configuración con los que terminé:
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;
}
NOTA: Es importante incluir el
daemon off;
en su archivonginx.conf
para asegurarse de que su contenedor no salga inmediatamente después del lanzamiento.
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
Le dejaré a usted hacer su tarea sobre la mayoría de los contenidos de
nginx.conf
y
api.myapp.conf
.
La magia ocurre en
Nginx-Startup.sh
donde usamos
sed
para reemplazar las cadenas en el marcador de posición
APP_IP
que hemos escrito en el bloque
upstream
de nuestros archivos
api.myapp.conf
y
app.myapp.conf
.
Esta pregunta de ask.ubuntu.com lo explica muy bien: buscar y reemplazar texto dentro de un archivo usando comandos
GOTCHA En OSX,
sed
maneja las opciones de manera diferente, específicamente la bandera-i
. En Ubuntu, la bandera-i
manejará el reemplazo ''en su lugar''; abrirá el archivo, cambiará el texto y luego ''guardará'' el mismo archivo. En OSX, el indicador-i
requiere la extensión de archivo que desea que tenga el archivo resultante. Si está trabajando con un archivo que no tiene extensión, debe ingresar '''' como valor para el indicador-i
.GOTCHA Para usar ENV vars dentro de la expresión regular que
sed
usa para encontrar la cadena que desea reemplazar, necesita envolver la var entre comillas dobles. Entonces, la sintaxis correcta, aunque de aspecto extraño, es la anterior.
Entonces, Docker lanzó nuestro contenedor y activó la
Nginx-Startup.sh
comandos
Nginx-Startup.sh
, que utilizó
sed
para cambiar el valor
APP_IP
a la variable
ENV
correspondiente que proporcionamos en el comando
sed
.
Ahora tenemos archivos conf dentro de nuestro directorio
/etc/nginx/sites-enabled
que tienen las direcciones IP de los archivos
ENV
que la ventana acoplable establece al iniciar el contenedor.
Dentro de su archivo
api.myapp.conf
verá que el bloque
upstream
ha cambiado a esto:
upstream api_upstream{
server 172.0.0.2:3000;
}
La dirección IP que ve puede ser diferente, pero he notado que generalmente es
172.0.0.x
Ahora debería tener todo enrutado adecuadamente.
GOTCHA No puede reiniciar / volver a ejecutar ningún contenedor una vez que haya ejecutado el inicio de la instancia inicial. Docker proporciona a cada contenedor una nueva IP al momento del lanzamiento y no parece reutilizar ninguno de los que usaba antes. Entonces
api.myapp.com
obtendrá 172.0.0.2 la primera vez, pero luego obtendrá 172.0.0.4 la próxima vez. PeroNginx
ya habrá configurado la primera IP en sus archivos conf, o en su/etc/hosts
, por lo que no podrá determinar la nueva IP paraapi.myapp.com
. Es probable que la solución a esto useCoreOS
y su servicioetcd
que, en mi comprensión limitada, actúa como unENV
compartido para todas las máquinas registradas en el mismo clústerCoreOS
. Este es el próximo juguete con el que voy a jugar.
OPCIÓN B: Use
/etc/hosts
File Entries
Esta
debería
ser la forma más rápida y fácil de hacerlo, pero no pude hacerlo funcionar.
Aparentemente, simplemente ingresó el valor de la entrada
/etc/hosts
en sus archivos
api.myapp.conf
y
app.myapp.conf
, pero no pude hacer que este método funcionara.
ACTUALIZACIÓN: Consulte la respuesta de @Wes Tod para obtener instrucciones sobre cómo hacer que este método funcione.
Aquí está el intento que hice en
api.myapp.conf
:
upstream api_upstream{
server API:3000;
}
Teniendo en cuenta que hay una entrada en mi
/etc/hosts
así:
172.0.0.2 API
, pensé que solo obtendría el valor, pero no parece ser.
También tuve un par de problemas auxiliares con mi fuente de
Elastic Load Balancer
de todas las AZ, por lo que puede haber sido el problema cuando intenté esta ruta.
En cambio, tuve que aprender a manejar el reemplazo de cadenas en Linux, así que fue divertido.
Lo intentaré en un momento y veré cómo va.
share se puede hacer funcionar usando la imagen base de Ubuntu y configurando nginx por su cuenta. (No funcionó cuando usé la imagen Nginx de Docker Hub).
Aquí está el archivo Docker que utilicé:
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;"]
Mi configuración nginx (también conocido como: conf / mysite.com):
server {
listen 80 default;
server_name mysite.com;
location / {
proxy_pass http://website;
}
}
upstream website {
server website:3000;
}
Y finalmente, cómo comienzo mis contenedores:
$ docker run -dP --name website website
$ docker run -dP --name nginx --link website:website nginx
Esto me puso en funcionamiento, por lo que mi nginx apuntó hacia el segundo contenedor acoplable que expuso el puerto 3000.