La respuesta de @ T0xicCode es correcta, pero pensé que ampliaría los detalles, ya que en realidad me tomó alrededor de 20 horas implementar finalmente una solución funcional.
Si está buscando ejecutar Nginx en su propio contenedor y usarlo como un proxy inverso para equilibrar la carga de varias aplicaciones en la misma instancia de servidor, los pasos que debe seguir son los siguientes:
Vincula tus contenedores
Cuando utiliza docker run
sus contenedores, generalmente ingresando un script de shell 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 a los primeros. 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 API
contenedor no está vinculado a ningún otro, pero el
App
contenedor está vinculado API
y Nginx
está vinculado a ambos API
y App
.
El resultado de esto son cambios en las env
vars y los /etc/hosts
archivos que residen dentro de los contenedores API
y App
. Los resultados se ven así:
/ etc / hosts
Ejecutar cat /etc/hosts
dentro de su Nginx
contenedor 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 Nginx
contenedor 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 muchas de las variables reales, pero los anteriores son los valores clave que necesita para el tráfico proxy 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 /etc/hosts
entradas de archivo y env
variables que contienen la dirección IP local para cualquiera de los contenedores que estaban vinculados. Por lo que puedo decir, esto es todo lo que sucede cuando ejecuta contenedores con opciones de enlace declaradas. Pero ahora puede usar esta información para configurar nginx
dentro de su Nginx
contenedor.
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
archivo que docker
creó, o puede utilizar las ENV
vars y ejecutar un reemplazo de cadena (yo usé sed
) en su nginx.conf
y cualquier otro archivo conf que pueda estar en su /etc/nginx/sites-enabled
carpeta para insertar la IP valores.
OPCIÓN A: Configurar Nginx usando ENV Vars
Esta es la opción que elegí porque no pude hacer que la
/etc/hosts
opción de archivo funcionara. Pronto intentaré la Opción B y actualizaré esta publicación con cualquier hallazgo.
La diferencia clave entre esta opción y el uso de la /etc/hosts
opción de archivo es cómo escribe su Dockerfile
para usar un script de shell como CMD
argumento, que a su vez maneja el reemplazo de la cadena para copiar los valores de IP ENV
a sus archivos conf.
Aquí está el conjunto de archivos de configuración con los que terminé:
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;
}
NOTA: Es importante incluirlo daemon off;
en su nginx.conf
archivo 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
Dejaré que usted haga su tarea sobre la mayoría de los contenidos de nginx.conf
y api.myapp.conf
.
La magia ocurre Nginx-Startup.sh
cuando usamos sed
para hacer el reemplazo de cadenas en el APP_IP
marcador de posición que hemos escrito en el upstream
bloque de nuestros archivos api.myapp.conf
y app.myapp.conf
.
Esta pregunta de ask.ubuntu.com lo explica muy bien: busque
y reemplace texto dentro de un archivo usando comandos
GOTCHA
En OSX, sed
maneja las opciones de manera diferente, -i
específicamente la bandera. En Ubuntu, la -i
bandera manejará el reemplazo 'en su lugar'; abrirá el archivo, cambiará el texto y luego 'guardará' el mismo archivo. En OSX, la -i
bandera requiere la extensión de archivo que le gustaría que tuviera el archivo resultante. Si está trabajando con un archivo que no tiene extensión, debe ingresar "" como el valor de la -i
bandera.
GOTCHA
Para usar ENV vars dentro de la expresión regular que sed
usa para encontrar la cadena que desea reemplazar, debe envolver la var entre comillas dobles. Entonces, la sintaxis correcta, aunque de aspecto extraño, es la anterior.
Entonces, Docker ha lanzado nuestro contenedor y ha activado la Nginx-Startup.sh
secuencia de comandos para que se ejecute, que ha utilizado sed
para cambiar el valor APP_IP
a la ENV
variable correspondiente que proporcionamos en el sed
comando. Ahora tenemos archivos conf dentro de nuestro /etc/nginx/sites-enabled
directorio que tienen las direcciones IP de las ENV
vars que Docker configuró al iniciar el contenedor. Dentro de su api.myapp.conf
archivo, verá que el upstream
bloque 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 normalmente lo es 172.0.0.x
.
Ahora debería tener todo enrutado correctamente.
GOTCHA
No puede reiniciar / volver a ejecutar ningún contenedor una vez que haya ejecutado el lanzamiento inicial de la instancia. Docker proporciona a cada contenedor una nueva IP al iniciarse y no parece reutilizar ninguna de las que se usó antes. Entonces api.myapp.com
obtendrá 172.0.0.2 la primera vez, pero luego obtendrá 172.0.0.4 la próxima vez. Pero Nginx
ya habrá configurado la primera IP en sus archivos conf, o en su /etc/hosts
archivo, por lo que no podrá determinar la nueva IP para api.myapp.com
. Es probable que se utilice la solución a esto CoreOS
y su etcd
servicio que, según mi entendimiento limitado, actúa como compartido ENV
para todas las máquinas registradas en el mismo CoreOS
clúster. Este es el próximo juguete con el que voy a jugar.
OPCIÓN B: Usar /etc/hosts
entradas de archivo
Esta debería ser la forma más rápida y fácil de hacer esto, pero no pude hacer que funcione. Aparentemente, solo /etc/hosts
ingresó el valor de la entrada en sus archivos api.myapp.conf
y app.myapp.conf
, pero no pude hacer que este método funcione.
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
archivo como esa: 172.0.0.2 API
pensé que solo obtendría el valor, pero no parece serlo.
También tuve un par de problemas auxiliares con mi Elastic Load Balancer
abastecimiento de todas las AZ, por lo que ese pudo haber sido el problema cuando probé esta ruta. En su lugar, tuve que aprender a manejar la sustitución de cadenas en Linux, así que fue divertido. Lo intentaré en un momento y veré cómo funciona.