385 votos

Cómo puedo usar variables de entorno en Nginx.conf

Tengo un contenedor de Docker ejecutando Nginx, que se vincula a otro contenedor de Docker. El nombre de host y la dirección IP del segundo contenedor se cargan en el contenedor de Nginx como variables de entorno al iniciarse, pero antes de eso no se conocen (son dinámicas). Quiero que mi nginx.conf use estos valores - por ejemplo,

upstream gunicorn {
    servidor $APP_HOST_NAME:$APP_HOST_PORT;
}

¿Cómo puedo ingresar variables de entorno en la configuración de Nginx al iniciarse?

EDICIÓN 1

Este es todo el archivo, después de la respuesta sugerida a continuación:

env APP_WEB_1_PORT_5000_TCP_ADDR;
# Configuración de host de Nginx para la aplicación django_app

# La aplicación de Django es atendida por Gunicorn, funcionando bajo el puerto 5000 (a través de Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

servidor {
    escucha 80;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ubicación /static/ {
        alias /app/static/;
    }
    ubicación /media/ {
        alias /app/media/;
    }
    ubicación / {
        proxy_pass http://gunicorn;
    }
}

Recargando nginx luego muestra errores:

$ nginx -s reload
nginx: [emerg] directiva desconocida "env" en /etc/nginx/sites-enabled/default:1

EDICIÓN 2: más detalles

Variables de entorno actuales

root@87ede56e0b11:/# env | grep APP_WEB_1
APP_WEB_1_NAME=/furious_turing/app_web_1
APP_WEB_1_PORT=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP=tcp://172.17.0.63:5000
APP_WEB_1_PORT_5000_TCP_PROTO=tcp
APP_WEB_1_PORT_5000_TCP_PORT=5000
APP_WEB_1_PORT_5000_TCP_ADDR=172.17.0.63

Archivo nginx.conf root:

root@87ede56e0b11:/# head /etc/nginx/nginx.conf
usuario www-data;
procesos_del_worker 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;

Configuración de nginx del sitio:

root@87ede56e0b11:/# head /etc/nginx/sites-available/default
# La aplicación de Django es atendida por Gunicorn, funcionando bajo el puerto 5000 (a través de Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

servidor {
    escucha 80;

Recargar configuración de nginx:

root@87ede56e0b11:/# nginx -s reload
nginx: [emerg] la directiva "server" no está terminada por ";" en /etc/nginx/sites-enabled/default:3

11 votos

Esta no es una solución genérica para variables de entorno, pero si desea usar variables de entorno para los nombres de host/direcciones IP de los servidores upstream, tenga en cuenta que Docker (al menos en versiones recientes) modifica /etc/hosts por usted. Consulte docs.docker.com/userguide/dockerlinks. Esto significa que si su contenedor vinculado se llama 'app_web_1', Docker creará una línea en /etc/hosts en su contenedor Nginx. Por lo tanto, simplemente puede reemplazar server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000; con server app_web_1:5000;.

1 votos

Gracias @mozz100 - eso es increíblemente útil - las entradas de /etc/hosts son mucho más efectivas que las variables de entorno en este caso. La única parte que falta es qué sucede si el contenedor upstream se reinicia y adquiere una nueva IP. ¿Presumo que los contenedores secundarios seguirán apuntando a la IP original, no a la nueva?

1 votos

Sí, si reinicias app_web_1 obtendría una nueva dirección IP, por lo que también deberías reiniciar tu contenedor de nginx. Docker lo reiniciaría con un /etc/hosts actualizado por lo que no necesitarías modificar el(los) archivo(s) de configuración de nginx.

197voto

danseery Puntos 111

Desde el archivo docker oficial de Nginx:

Usando variables de entorno en la configuración de nginx:

Por defecto, Nginx no admite el uso de variables de entorno dentro de la mayoría de los bloques de configuración.

Pero envsubst se puede usar como una solución alternativa si necesita generar su configuración de nginx dinámicamente antes de que nginx se inicie.

Aquí tienes un ejemplo usando docker-compose.yml:

image: nginx
volumes:
 - ./mysite.template:/etc/nginx/conf.d/mysite.template
ports:
 - "8080:80"
environment:
 - NGINX_HOST=foobar.com
 - NGINX_PORT=80
command: /bin/bash -c "envsubst < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" 

El archivo mysite.template puede contener referencias de variables como esta:

listen ${NGINX_PORT};

Actualización:

Pero sabes que esto causó que sus variables de Nginx como esta:

proxy_set_header        X-Forwarded-Host $host;

se dañaran:

proxy_set_header        X-Forwarded-Host ;

Entonces, para evitar eso, uso este truco:

Tengo un script para ejecutar Nginx, que se usa en el archivo docker-compose como opción de comando para el servidor Nginx, lo llamé run_nginx.sh:

#!/usr/bin/env bash
export DOLLAR='$'
envsubst < nginx.conf.template > /etc/nginx/nginx.conf
nginx -g "daemon off;"

Y debido a la nueva variable DOLLAR definida en el script run_nginx.sh, ahora el contenido de mi archivo nginx.conf.template para la variable de Nginx en sí es como esto:

proxy_set_header        X-Forwarded-Host ${DOLLAR}host;

Y para mi variable definida es como esto:

server_name  ${WEB_DOMAIN} www.${WEB_DOMAIN};

También aquí, está mi caso de uso real para eso.

108voto

Esko Luontola Puntos 158

La imagen oficial de nginx recomienda usar envsubst, pero como señalan otros, también reemplazará $host y otras variables, lo cual no es deseable. Sin embargo, afortunadamente envsubst puede tomar como parámetro los nombres de las variables a reemplazar.

Para evitar un comando de parámetro muy complejo en el contenedor (como en el ejemplo vinculado), puedes escribir un script de entrada de Docker que complete las variables de entorno antes de ejecutar el comando. El script de entrada también es un buen lugar para validar los parámetros y establecer valores predeterminados.

Aquí tienes un ejemplo de un contenedor de nginx que toma los parámetros de API_HOST y API_PORT como variables de entorno.

nginx-default.conf.template

resolver  127.0.0.11 valid=10s;  # recuperarse del cambio de IP del backend

server {
  listen  80;

  location / {
    root  /usr/share/nginx/html;
  }

  location /api {
    proxy_pass  http://${API_HOST}:${API_PORT};
    proxy_set_header  Host $http_host;
  }
}

docker-entrypoint.sh

#!/usr/bin/env sh
set -eu

envsubst '${API_HOST} ${API_PORT}' < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf

exec "$@"

Dockerfile

FROM nginx:1.15-alpine

COPY nginx-default.conf.template /etc/nginx/conf.d/default.conf.template

COPY docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

69voto

Kaleb Puntos 23

envsubstr ahora es manejado automáticamente por la imagen de nginx.

Desde la documentación:

Por defecto, nginx no admite variables de entorno dentro de la mayoría de los bloques de configuración. Pero esta imagen tiene una función que extraerá las variables de entorno antes de que nginx se inicie.

De forma predeterminada, esta función lee archivos de plantilla en /etc/nginx/templates/*.template y produce el resultado de ejecutar envsubst en /etc/nginx/conf.d.

56voto

Khaled Ahmed Puntos 31

Hacer esto con Lua es considerablemente más fácil de lo que parece:

server {
    set_by_lua $server_name 'return os.getenv("NGINX_SERVERNAME")';
}

Encontré eso aquí:

https://docs.apitools.com/blog/2014/07/02/using-environment-variables-in-nginx-conf.html

Editar:

Aparentemente, esto requiere instalar el módulo lua: https://github.com/openresty/lua-nginx-module

Editar 2:

Tenga en cuenta que con este enfoque debe definir la variable de env en Nginx:

env ENVIRONMENT_VARIABLE_NAME

Debe hacer esto en el contexto de nivel superior en nginx.conf ¡o no funcionará! No en el bloque del servidor o en la configuración de algún sitio en /etc/nginx/sites-available, porque se incluye en nginx.conf en el contexto de http (que no es un contexto de nivel superior).

También tenga en cuenta que con este enfoque si intenta hacer una redirección por ejemplo:

server {
    listen 80;
    server_name $server_name;
    return 301 https://$server_name$request_uri;
}

tampoco funcionará:

2016/08/30 14:49:35 [emerg] 1#0: the duplicate "server_name" variable in /etc/nginx/sites-enabled/default:8

Y si le da un nombre de variable separado:

set_by_lua $server_name_from_env 'return os.getenv("NGINX_SERVERNAME")';

server {
    listen 80;
    server_name $server_name_from_env;
    return 301 https://$server_name$request_uri;
}

Nginx no lo interpretará y lo redirigirá a https://%24server_name_from_env/.

36voto

MH. Puntos 111

Aquí hay otro con envsubst.

La imagen de docker nginx ya ejecuta envsubst a través de /docker-entrypoint.d/20-envsubst-on-templates.sh. Solo necesitas colocar un archivo de plantilla en el lugar correcto: /etc/nginx/templates/my-file.conf.template.

Luego, puedes crear un archivo template dedicado solo para variables con una o más cláusulas map que utilizaremos como nuestras variables personalizadas. Debes elegir un nombre para el archivo conf.d para que sea el primero que tome nginx.

Este es el concepto básico explicado con docker-compose y tres archivos:

docker-compose.yml:

version: "3.9"
services:
  nginx:
    image: nginx:1.23
    volumes:
        - ./template-variables:/etc/nginx/templates/10-variables.conf.template:ro

    environment:
        EXTERNAL_IP: "1.2.3.4"

template-variables:

map $host $external_ip {
  default "$EXTERNAL_IP";
}

nginx.conf:

server {
     location / {

          proxy_set_header X-Real-IP $external_ip;
      }
}

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X