Asignar vhosts a puertos Docker

83

Tengo un DNS comodín configurado para que todas las solicitudes web a un dominio personalizado (* .foo) se asignen a la dirección IP del host de Docker. Si tengo varios contenedores que ejecutan instancias de Apache (o Nginx), cada contenedor asigna el puerto de Apache (80) a algún puerto de entrada externo.

Lo que me gustaría hacer es hacer una solicitud a container-1.foo, que ya está asignada a la dirección IP correcta (del host de Docker) a través de mi servidor DNS personalizado, pero envía la solicitud predeterminada del puerto 80 al Docker externo correcto. puerto de modo que la instancia de Apache correcta del contenedor especificado pueda responder según el dominio personalizado. Del mismo modo, container-2.foo haría un proxy al apache de un segundo contenedor, y así sucesivamente.

¿Existe una solución prediseñada para esto? ¿Es mi mejor opción ejecutar un proxy Nginx en el host de Docker, o debería escribir un proxy de node.js con el potencial de administrar contenedores de Docker (iniciar / detener / reconstruir a través de la web ), o ...? ¿Qué opciones tengo que harían que el uso de los contenedores Docker sea más un evento natural y no algo con puertos extraños y malabarismos de contenedores?

jefe de pista
fuente
También tengo esta pregunta: por lo que puedo decir, ejecutar cada aplicación en un contenedor Docker y luego hacer el enrutamiento en el host usando un servidor nginx (quizás en su propio contenedor) es la forma de hacerlo. Me pregunto si debería ejecutar el servidor de aplicaciones de forma independiente (es decir, exponer un servidor php-fpm, puma, etc.) o incluir también una instancia nginx (¿inútil?).
Ross
Eche un vistazo a github.com/dotcloud/hipache , que es un proxy inverso configurable a través de redis.
ZeissS

Respuestas:

81

Esta respuesta puede llegar un poco tarde, pero lo que necesita es un proxy inverso automático. He usado dos soluciones para eso:

  • jwilder / nginx-proxy
  • Traefik

Con el tiempo, mi preferencia es utilizar Traefik. Principalmente porque está bien documentado y mantenido, y viene con más funciones (balanceo de carga con diferentes estrategias y prioridades, chequeos de estado, disyuntores, certificados SSL automáticos con ACME / Let's Encrypt, ...).


Usando jwilder / nginx-proxy

Cuando ejecuta un contenedor Docker , la imagen de Docker nginx-proxy de Jason Wilder , obtiene un servidor nginx configurado como un proxy inverso para sus otros contenedores sin configuración que mantener.

Simplemente ejecute sus otros contenedores con la VIRTUAL_HOSTvariable de entorno y nginx-proxy descubrirá su ip: puerto y actualizará la configuración de nginx por usted.

Supongamos que su DNS está configurado para que se *.test.localasigne a la dirección IP de su host de Docker, luego simplemente inicie los siguientes contenedores para ejecutar una demostración rápida:

# start the reverse proxy
docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock jwilder/nginx-proxy

# start a first container for http://tutum.test.local
docker run -d -e "VIRTUAL_HOST=tutum.test.local" tutum/hello-world

# start a second container for http://deis.test.local
docker run -d -e "VIRTUAL_HOST=deis.test.local" deis/helloworld

Usando Traefik

Cuando ejecuta un contenedor Traefik , obtiene una configuración de servidor proxy inverso que reconfigurará sus reglas de reenvío dadas las etiquetas de la ventana acoplable que se encuentran en sus contenedores.

Supongamos que su DNS está configurado para que se *.test.localasigne a la dirección IP de su host de Docker, luego simplemente inicie los siguientes contenedores para ejecutar una demostración rápida:

# start the reverse proxy
docker run --rm -it -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock traefik:1.7 --docker

# start a first container for http://tutum.test.local
docker run -d -l "traefik.frontend.rule=Host:tutum.test.local" tutum/hello-world

# start a second container for http://deis.test.local
docker run -d -l "traefik.frontend.rule=Host:deis.test.local" deis/helloworld
Thomasleveil
fuente
-v /var/run/docker.sock:/tmp/docker.sock¿Es una solución peligrosa? ¿Contenedor este proxy nginx tiene acceso al demonio de host de Docker? ¿Puede ser esto un posible agujero de seguridad?
Mikl
posiblemente. También tenga en cuenta que no compartir tampoco /var/run/docker.sockes una garantía de que el host de la ventana acoplable no se pueda explotar desde un contenedor. La seguridad de Docker es un tema en sí mismo.
Thomasleveil
¿Existe algún problema de seguridad conocido? Cuando puede acceder al host de Docker desde el contenedor.
Mikl
Un exploit existía en el pasado y el problema ahora está solucionado, pero se pueden encontrar nuevos exploits en el futuro. Docker no se trata de agregar seguridad, se trata de facilitar la implementación
Thomasleveil
5
También puede ejecutar nginx-proxy y docker-gen por separado para que el socket de la ventana acoplable no esté montado en el contenedor nginx.
Jason Wilder
42

Aquí hay dos posibles respuestas: (1) configure los puertos directamente con Docker y use Nginx / Apache para proxy los vhosts, o (2) use Dokku para administrar puertos y vhosts por usted (que es como aprendí a hacer el Método 1).

Método 1a (asignar puertos directamente con la ventana acoplable)

Paso 1: Configure nginx.conf o Apache en el host, con las asignaciones de número de puerto deseadas. Este servidor web, que se ejecuta en el host, hará el proxy vhost. No hay nada especial en esto con respecto a Docker: es un alojamiento de vhost normal. La parte especial viene a continuación, en el Paso 2, para que Docker use el número de puerto de host correcto.

Paso 2: fuerce las asignaciones de números de puerto en Docker con "-p" para configurar las asignaciones de puertos de Docker y "-e" para configurar las variables de entorno personalizadas dentro de Docker, de la siguiente manera:

port=12345 # <-- the vhost port setting used in nginx/apache
IMAGE=myapps/container-1
id=$(docker run -d -p :$port -e PORT=$port $IMAGE)
# -p :$port will establish a mapping of 12345->12345 from outside docker to
# inside of docker.
# Then, the application must observe the PORT environment variable
# to launch itself on that port; This is set by -e PORT=$port.

# Additional goodies:
echo $id # <-- the running id of your container
echo $id > /app/files/CONTAINER # <-- remember Docker id for this instance
docker ps # <-- check that the app is running
docker logs $id # <-- look at the output of the running instance
docker kill $id # <-- to kill the app

Método 1b Puerto de aplicación codificado de forma rígida

... si su aplicación utiliza un puerto codificado, por ejemplo, el puerto 5000 (es decir, no se puede configurar a través de la variable de entorno PORT, como en el Método 1a), entonces se puede codificar a través de Docker de esta manera:

publicPort=12345
id=$(docker run -d -p $publicPort:5000 $IMAGE)
# -p $publicPort:5000 will map port 12345 outside of Docker to port 5000 inside
# of Docker. Therefore, nginx/apache must be configured to vhost proxy to 12345,
# and the application within Docker must be listening on 5000.

Método 2 (deja que Dokku descubra los puertos)

Por el momento, una opción bastante buena para administrar los vhosts de Docker es Dokku . Una próxima opción puede ser usar Flynn , pero a partir de ahora Flynn recién está comenzando y no está del todo listo. Por lo tanto, vamos con Dokku por ahora: después de seguir las instrucciones de instalación de Dokku, para un solo dominio, habilite vhosts creando el archivo "VHOST":

echo yourdomain.com > /home/git/VHOST
# in your case: echo foo > /home/git/VHOST

Ahora, cuando una aplicación se envía a través de SSH a Dokku (consulte los documentos de Dokku para saber cómo hacer esto), Dokku verá el archivo VHOST y para la aplicación en particular presionada (digamos que presionó "contenedor-1"), generará el siguiente archivo:

/home/git/container-1/nginx.conf

Y tendrá los siguientes contenidos:

upstream container-1 { server 127.0.0.1:49162; }
server {
  listen      80;
  server_name container-1.yourdomain.com;
  location    / {
    proxy_pass  http://container-1;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

Cuando se reinicia el servidor, Dokku se asegurará de que Docker inicie la aplicación con el puerto asignado a su puerto implementado inicialmente (49162 aquí), en lugar de que se le asigne al azar otro puerto. Para lograr esta asignación determinista, Dokku guarda el puerto asignado inicialmente /home/git/container-1/PORTy en el próximo lanzamiento establece el PORTentorno en este valor, y también asigna las asignaciones de puertos de Docker para que sean este puerto tanto en el lado del host como en el lado de la aplicación. Esto se opone al primer lanzamiento, cuando Dokku configurará PORT=5000y luego determinará cualquier puerto aleatorio que Dokku asigne en el lado VPS a 5000 en el lado de la aplicación. Es redondo (e incluso podría cambiar en el futuro), ¡pero funciona!

La forma en que funciona VHOST, bajo el capó, es: al hacer un git push de la aplicación a través de SSH, Dokku ejecutará los ganchos que viven en /var/lib/dokku/plugins/nginx-vhosts. Estos ganchos también se encuentran en el código fuente Dokku aquí y son responsables de escribir los nginx.confarchivos con los ajustes correctos de hosts virtuales. Si no tiene este directorio /var/lib/dokku, intente ejecutarlo dokku plugins-install.

David Baird
fuente
3

Con Docker, desea que los ips internos permanezcan normales (por ejemplo, 80) y descubra cómo conectar los puertos aleatorios.

Una forma de manejarlos es con un proxy inverso como hipache. Apunte su dns hacia él, y luego puede reconfigurar el proxy a medida que sus contenedores suben y bajan. Eche un vistazo a http://txt.fliglio.com/2013/09/protyping-web-stuff-with-docker/ para ver cómo podría funcionar.

Si está buscando algo más sólido, es posible que desee echar un vistazo a "descubrimiento de servicios". (un vistazo al descubrimiento de servicios con Docker: http://txt.fliglio.com/2013/12/service-discovery-with-docker-docker-links-and-beyond/ )

ben Schwartz
fuente