web-dev-qa-db-fra.com

Comment puis-je utiliser des variables d'environnement dans Nginx.conf

[Post-cross et édité de https://stackoverflow.com/questions/21933955 car il était considéré comme trop sysadmin pour StackOverflow.]

J'ai un conteneur docker exécutant Nginx, qui est lié à un autre conteneur docker. Le nom d'hôte et l'adresse IP du deuxième conteneur sont chargés dans le conteneur Nginx en tant que variables d'environnement au démarrage, mais ne sont pas connus avant (c'est dynamique). Je veux mon nginx.conf pour utiliser ces valeurs - par exemple.

upstream gunicorn {
    server $APP_Host_NAME:$APP_Host_PORT;
}

Comment puis-je intégrer des variables d'environnement dans la configuration Nginx au démarrage?

EDIT 1

Voici l'intégralité du dossier, après la réponse suggérée ci-dessous:

env APP_WEB_1_PORT_5000_TCP_ADDR;
# Nginx Host configuration for Django_app

# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

server {
    listen 80;

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

    location /static/ {
        alias /app/static/;
    }
    location /media/ {
        alias /app/media/;
    }
    location / {
        proxy_pass http://gunicorn;
    }
}

Rechargement de nginx puis erreurs:

$ nginx -s reload
nginx: [emerg] unknown directive "env" in /etc/nginx/sites-enabled/default:1

EDIT 2: plus de détails

Variables d'environnement actuelles

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

Racine nginx.conf:

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

Configuration du site nginx:

root@87ede56e0b11:/# head /etc/nginx/sites-available/default
# Django app is served by Gunicorn, running under port 5000 (via Foreman)
upstream gunicorn {
    server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;
}

server {
    listen 80;

Recharger la configuration nginx:

root@87ede56e0b11:/# nginx -s reload
nginx: [emerg] directive "server" is not terminated by ";" in /etc/nginx/sites-enabled/default:3
208
Hugo Rodger-Brown

De le fichier docker officiel de Nginx:

Utilisation de variables d'environnement dans la configuration nginx:

Prêt à l'emploi, Nginx ne prend pas en charge l'utilisation de variables d'environnement dans la plupart des blocs de configuration.

Mais envsubst peut être utilisé comme solution de contournement si vous devez générer votre configuration nginx dynamiquement avant le démarrage de nginx.

Voici un exemple utilisant 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;'" 

Le fichier mysite.template peut alors contenir des références de variables comme ceci:

listen ${NGINX_PORT};

Mise à jour:

Mais vous savez que cela a causé à ses variables Nginx comme ceci:

proxy_set_header        X-Forwarded-Host $Host;

endommagé à:

proxy_set_header        X-Forwarded-Host ;

Donc, pour éviter cela, j'utilise cette astuce:

J'ai un script pour exécuter Nginx, celui utilisé sur le docker-compose fichier comme option de commande pour le serveur Nginx, je l'ai nommé run_nginx.sh:

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

Et à cause de la nouvelle variable DOLLAR définie sur run_nginx.sh script, maintenant contenu de mon nginx.conf.template Le fichier pour Nginx lui-même est comme ceci:

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

Et pour ma variable définie est comme ceci:

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

Aussi ici , il y a mon vrai cas d'utilisation pour cela.

116
Omid Raha

Faites-le avec Lua!

server {
    set_by_lua $curr_server_name 'return os.getenv("APP_Host_NAME")';
    set_by_lua $curr_server_port 'return os.getenv("APP_Host_PORT")';
    listen = $curr_server_port
    server_name = $curr_server_name;
}

(La réponse d'origine est obsolète et a été supprimée. Il s'agit d'une réponse mise à jour.)

59
Ben Whaley

Faire cela avec Lua est beaucoup plus facile qu'il n'y paraît:

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

J'ai trouvé ça ici:

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

Éditer:

Apparemment, cela nécessite l'installation du module lua: https://github.com/openresty/lua-nginx-module

Modifier 2:

Notez qu'avec cette approche, vous devez définir la variable env dans Nginx:

env ENVIRONMENT_VARIABLE_NAME

Vous devez le faire dans un contexte de haut niveau dans nginx.conf ou ça ne marchera pas! Pas dans le bloc serveur ou dans la configuration d'un site dans /etc/nginx/sites-available, car il est inclus par nginx.conf dans le contexte http (qui n'est pas un contexte de niveau supérieur).

Notez également qu'avec cette approche, si vous essayez de faire une redirection, par exemple:

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

ça ne marchera pas aussi bien:

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

Et si vous lui donnez un nom de variable distinct:

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 ne l'interprète pas et vous redirige vers https://%24server_name_from_env/.

36

L'image nginx officielle recommande d'utiliser envsubst , mais comme souligné par d'autres elle remplacera également $Host et d'autres variables, ce qui n'est pas souhaitable. Mais heureusement, envsubst peut prendre comme paramètre les noms des variables à remplacer .

Pour éviter un paramètre de commande très complexe dans le conteneur (comme dans l'exemple lié), vous pouvez écrire un script de point d'entrée Docker qui remplira les variables d'environnement avant d'exécuter la commande. Le script de point d'entrée est également un bon endroit pour valider les paramètres et définir les valeurs par défaut.

Voici un exemple de conteneur nginx qui prend API_Host et API_PORT paramètres comme variables d'environnement.

nginx-default.conf.template

resolver  127.0.0.11 valid=10s;  # recover from the backend's IP changing

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;"]
26
Esko Luontola

J'ai écrit quelque chose qui peut ou non être utile: https://github.com/yawn/envplate

Il modifie en ligne les fichiers de configuration avec des références $ {key} aux variables d'environnement, créant éventuellement des sauvegardes/journalisant ce qu'il fait. Il est écrit en Go et le binaire statique résultant peut être simplement téléchargé à partir de l'onglet de publication pour Linux et MacOS.

Il peut également exécuter des processus (), remplacer les valeurs par défaut, les journaux et possède une sémantique de défaillance sensible.

14
yawn

En vous référant à la réponse sur l'utilisation de erb, cela peut être fait comme ci-dessous.

Écrivez le fichier de configuration NGINX en tant que fichier erb contenant la variable d'environnement et évaluez-le à l'aide de la commande erb dans un fichier de configuration normal.

erb nginx.conf.erb > nginx.conf

À l'intérieur du bloc serveur du fichier nginx.conf.erb, il pourrait y avoir

listen <%= ENV["PORT"] %>;
5
Ruifeng Ma

Ce que j'ai fait, c'était d'utiliser le erb !

cat nginx.conf  | grep -i error_log

error_log <%= ENV["APP_ROOT"] %>/nginx/logs/error.log;

--Après avoir utilisé erb

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

error_log /tmp/nginx/logs/error.log;

Ceci est utilisé dans le Cloudfoundry staticfile-buildpack

Exemple de configuration nginx: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

Dans ton cas

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env APP_WEB_1_PORT_5000_TCP_ADDR;
upstream gunicorn {
    server $APP_Host_NAME:$APP_Host_PORT;
}

devenir

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env <%= ENV["APP_WEB_1_PORT_5000_TCP_ADDR"] %>
upstream gunicorn {
    server <%= ENV["APP_Host_NAME"] %>:<%= ENV["APP_Host_PORT"] %>
}

#After applying erb

export APP_WEB_1_PORT_5000_TCP_ADDR=12.12.12.12
export APP_Host_NAME=test
export APP_Host_PORT=7089 

erb /etc/nginx/nginx.conf

head /etc/nginx/nginx.conf
user www-data;
worker_processes 4;
pid /var/run/nginx.pid;
env 12.12.12.12
upstream gunicorn {
    server test: 7089
}
5
Sabith K S

J'accomplis cela en utilisant un script Shell.

Voici le modèle nginx:

server {

    listen 80;
    server_name ___MY_DOMAIN_NAME___;
    charset utf-8;

    location /proxy {
        proxy_pass http://___PROXY_IP___:___PROXY_PORT___;
        proxy_set_header Host            $Host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }

    location / {
        return         200;
    }

}

Et le script pour remplacer les variables d'environnement est ici:

echo sleep 3
sleep 3

echo build starting nginx config


echo replacing ___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME
echo replacing ___PROXY_IP___/$LETSENCRYPT_IP
echo replacing ___PROXY_PORT___/$PROXY_PORT

sed -i "s/___MY_DOMAIN_NAME___/$MY_DOMAIN_NAME/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_IP___/$PROXY_IP/g" /etc/nginx/nginx.conf
sed -i "s/___PROXY_PORT___/$PROXY_PORT/g" /etc/nginx/nginx.conf

cat /etc/nginx/nginx.conf

if [ -z "$MY_DOMAIN_NAME" ]; then
    echo "Need to set MY_DOMAIN_NAME"
    exit 1
fi  
if [ -z "$LETSENCRYPT_IP" ]; then
    echo "Need to set LETSENCRYPT_IP"
    exit 1
fi  
if [ -z "$LETSENCRYPT_PORT" ]; then
    echo "Need to set LETSENCRYPT_PORT"
    exit 1
fi
if [ -z "$LETSENCRYPT_HTTPS_IP" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_IP"
    exit 1
fi 
if [ -z "$LETSENCRYPT_HTTPS_PORT" ]; then
    echo "Need to set LETSENCRYPT_HTTPS_PORT"
    exit 1
fi

nginx -g 'daemon off;'
3
Geige V

Je sais que c'est une vieille question, mais juste au cas où quelqu'un tomberait dessus (comme je l'ai maintenant), il y a une bien meilleure façon de le faire. Parce que docker insère l'alias du conteneur lié dans/etc/hosts, vous pouvez simplement faire

upstream upstream_name {
    server docker_link_alias;
}

en supposant que votre commande docker ressemble à docker run --link othercontainer:docker_link_alias nginx_container.

3
cderwin

Une autre option ... Je viens de trouver cet outil aujourd'hui: https://github.com/kreuzwerker/envplate ... Écrit en Go, il peut être installé très facilement. Son utilisation est assez simple. Cependant, vous devrez placer des variables de modèle dans votre nginx.conf.

Par exemple ${SOME_ENV_VAR} sera remplacé lorsque la commande ep d'envplate sera appelée dans le fichier. Donc, si votre Dockerfile ne parvient pas à obtenir ce fichier binaire ou s'il ne s'exécute pas pour une raison quelconque, il rendra votre configuration invalide. Juste une petite note par rapport à d'autres solutions comme l'utilisation d'extensions Perl ou lua.

J'aime vraiment la façon dont vous pouvez également définir des valeurs par défaut lorsque la variable d'environnement n'est pas définie. Ex. ${SOME_ENV_VAR:default-value} (et vous pouvez échapper des valeurs). Encore une fois, envplate doit toujours fonctionner correctement.

Un avantage de l'utilisation d'une approche comme celle-ci est que vous ne vous retrouvez pas avec une image Docker plus grande que nécessaire car vous avez cessé d'installer toutes sortes de modules supplémentaires dont vous n'avez pas besoin autrement. Cela peut également être plus facile que d'utiliser sed si les choses commencent à devenir complexes et qu'il contient cette fonctionnalité de valeur par défaut.

2
Tom

Il y a plusieurs façons. Certains ont été décrits dans certaines réponses.

Si vous utilisez le ngx_http_js_module, il existe également un moyen de le faire avec JS:

## /etc/nginx/fetch_env.js
function fetch_upstream_Host(r) {
  return process.env.UPSTREAM_Host;
}

function fetch_upstream_port(r) {
  return process.env.UPSTREAM_PORT;
}

et

## /etc/nginx/nginx.conf
load_module modules/ngx_http_js_module.so;

env UPSTREAM_Host;
env UPSTREAM_PORT;

http {
  js_include fetch_env.js;
  js_set $upstream_Host fetch_upstream_Host;
  js_set $upstream_port fetch_upstream_port;

  server {
    ...

    location / {
      ...

      proxy_pass http://$upstream_Host:$upstream_port;
    }
  }

Veuillez vous assurer que vous utilisez le module njs 0.33 ou plus récent

2
Trung Lê

Voici un exemple d'utilisation de l'approche sed, pas nécessairement meilleure mais elle peut être utile à certains. Tout d'abord, ajoutez un mot-clé personnalisé à remplacer dans le fichier conf. Ensuite, créez un dockerfile qui déclare une variable ENV puis un CMD qui utilise sed pour modifier la configuration avant d'exécuter explicitement nginx.

Supposons donc que votre default.conf le contienne avec le mot clé docker_Host:

location /api { proxy_pass http://docker_Host:9000/api; }

Et écrivez votre Dockerfile comme:

ENV docker_Host localhost
ADD default.conf /etc/nginx/conf.d/default.conf
CMD sed -i.bak s/docker_Host/$docker_Host/g /etc/nginx/conf.d/default.conf &&   nginx -g "daemon off;"

Créez ensuite l'image et exécutez le conteneur à l'aide de

docker run -d -p 80:80 -e "docker_Host=${env:COMPUTERNAME}" imagename
1
Steven

Une autre possibilité est d'utiliser la commande 'sed' avec des expressions régulières, vous n'aurez donc pas du tout à vous soucier de vos fichiers de configuration! De cette façon, vous pouvez utiliser vos fichiers de configuration normalement, mais lorsque vous exécutez docker, il échangera les valeurs avec les variables env. Rien de tout cela "ajoutez une chaîne de texte à vos fichiers de configuration que vous recherchez et remplacez par."

Vous pouvez créer un fichier run.sh avec des valeurs de remplacement à l'aide de vos variables d'environnement.

Pour modifier les "7" sur cette ligne:

client_body_timeout 7s;         #Default 60s

Commande Sed utilisant client_body_timeout comme ligne de recherche et $ client_body_timeout comme variable env de remplacement:

sed -i "s/\(client_body_timeout\).*\?\;/\1 $client_body_timeout;/" /usr/local/nginx/conf/nginx.conf

Copiez/collez cette ligne pour chaque paramètre que vous souhaitez définir et modifiez le client_body_timeout avec l'option config et $ client_body_timeout avec la variable env à laquelle il est associé. Utilisez avec le fichier de configuration existant et cela fonctionnera.

1
Jeff

Bonne façon de le faire avec lua, car la réponse ci-dessus est périmée:

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

0
Eric Meadows