388 votes

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

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 (elles sont dynamiques). Je veux que mon nginx.conf utilise ces valeurs - par exemple

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

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

EDIT 1

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

env APP_WEB_1_PORT_5000_TCP_ADDR;
# Configuration hôte Nginx pour l'application django_app 

# L'application Django est servie par Gunicorn, fonctionnant sous le 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;
    }
}

Le rechargement de nginx produit une erreur :

$ nginx -s reload
nginx: [emerg] directive inconnue "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

Nginx.conf racine :

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
# L'application Django est servie par Gunicorn, fonctionnant sous le 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

11 votes

Ce n'est pas une solution générique pour les variables d'environnement, mais si vous souhaitez utiliser des variables d'environnement pour les noms d'hôtes / adresses IP des serveurs en amont, notez que Docker (du moins dans les versions récentes) modifie /etc/hosts pour vous. Voir docs.docker.com/userguide/dockerlinks Cela signifie que si votre conteneur lié s'appelle 'app_web_1', Docker créera une ligne dans /etc/hosts de votre conteneur Nginx. Vous pouvez donc simplement remplacer server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000; par server app_web_1:5000;

1 votes

Merci @mozz100 - c'est incroyablement utile - les entrées /etc/hosts sont beaucoup plus efficaces que les variables d'environnement dans ce cas. La seule chose qui manque, c'est ce qui se passe si le conteneur amont est redémarré et acquiert une nouvelle adresse IP. Je suppose que les conteneurs enfants pointeront toujours vers l'adresse IP d'origine, pas la nouvelle ?

1 votes

Oui, si vous redémarriez app_web_1, il recevrait une nouvelle adresse IP, vous devriez donc redémarrer votre conteneur nginx aussi. Docker le redémarrerait avec un /etc/hosts mis à jour, vous n'auriez donc pas besoin de modifier le(s) fichier(s) de configuration nginx.

3voto

cderwin Points 131

Je sais que c'est une vieille question, mais au cas où quelqu'un tomberait dessus (comme je viens de le faire), il y a une bien meilleure façon de faire cela. 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 autreconteneur:docker_link_alias conteneur_nginx.

1 votes

Vous pouvez obtenir l'hôte en utilisant cette méthode mais pas le port. Vous devez soit coder en dur le port, soit supposer qu'il utilise le port 80.

3voto

Arjan Points 403

Comme déjà expliqué dans la réponse de Martin, l'image Docker officielle prend en charge l'analyse de modèles avec envsubst de nos jours. Cependant, envsubst ne prend pas en charge les valeurs par défaut comme le fait un shell normal, comme dans ${MY_VAR:-My Default}.

Pour contourner cela, on peut utiliser un point d'entrée personnalisé, comme docker-defaults.sh:

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

# À partir de la version 1.19, l'image Docker officielle de Nginx prend en charge les modèles avec
# substitution de variables. Mais cela utilise `envsubst`, qui ne permet pas les
# valeurs par défaut pour les variables manquantes. Ici, utilisez d'abord le shell de commande régulier
# pour définir les valeurs par défaut :

export PROXY_API_DEST=${PROXY_API_DEST:-http://host.docker.internal:8000/api/}

echo "Va proxy les requêtes pour /api/* vers ${PROXY_API_DEST}*"

# Ensuite, laissez le point d'entrée original faire son travail :

/docker-entrypoint.sh "$@"

Ainsi que, disons, un docker-nginx.conf:

# Après la substitution des variables, cela remplacera /etc/nginx/conf.d/default.conf
serveur {
    écoute 80;
    écoute [::]:80;
    nom_serveur localhost;

    location / {
        racine /usr/share/nginx/html;
        # Rediriger tous les chemins inexistants, tels que /help et /file/..., vers l'application
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
       # Transférer les appels API vers une autre destination ; la valeur par défaut de la variable est
       # défini dans docker-defaults.sh
       proxy_pass $PROXY_API_DEST;
    }

    # Rediriger les pages d'erreur du serveur vers la page statique /50x.html
    error_page 500 502 503 504  /50x.html;
    location = /50x.html {
        racine /usr/share/nginx/html;
    }
}

Dans le Dockerfile, copiez-le dans /etc/nginx/templates/default.conf.template:

# Chaque fois que Nginx est démarré, il effectuera une substitution de variables dans le modèle
# fichier /etc/nginx/templates/default.conf.template et copiera le résultat dans
# /etc/nginx/conf.d/default.conf, remplaçant ainsi la configuration initiale ; voir
# aussi https://hub.docker.com/_/nginx
COPY docker-nginx.conf /etc/nginx/templates/default.conf.template
COPY docker-defaults.sh /

# Cela déléguera au docker-entrypoint.sh original
ENTRYPOINT ["/docker-defaults.sh"]

CMD ["nginx", "-g", "daemon off;"]

Maintenant en utilisant, par exemple, docker run --env PROXY_API_DEST=https://example.com/api/ ... définira une valeur, ou dans cet exemple par défaut à http://host.docker.internal:8000/api/ si non défini (qui est en fait http://localhost:8000/api/ sur la machine locale).

2voto

Tom Points 121

Une autre option... J'ai trouvé 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 est appelée sur le fichier. Ainsi, si votre Dockerfile échoue à obtenir ce binaire ou s'il ne s'exécute pas pour une raison quelconque, cela laisserait votre configuration invalide. Juste une petite note par rapport à d'autres solutions comme l'utilisation d'extensions perl ou lua.

J'aime vraiment le fait que vous puissiez également définir des valeurs par défaut lorsque la variable d'environnement n'est pas définie. Par exemple ${SOME_ENV_VAR:default-value} (et vous pouvez échapper les valeurs). Encore une fois, envplate doit réussir à s'exécuter.

Un avantage d'utiliser une approche comme celle-ci est que vous ne vous retrouvez pas avec une image Docker plus grande que nécessaire car vous avez installé 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 cela contient cette fonctionnalité de valeur par défaut.

0 votes

Je conseille github.com/gliderlabs/sigil car j'ai rencontré de nombreux bugs et problèmes avec envplate.

2voto

Jeff Points 21

Une autre possibilité consiste à utiliser la commande 'sed' avec des expressions régulières, ainsi vous n'aurez pas à vous embêter avec vos fichiers de configuration ! De cette façon, vous pouvez utiliser vos fichiers de configuration normalement, mais lorsque vous exécutez docker, il remplacera les valeurs par les variables d'environnement. Pas besoin d'"ajouter une chaîne de texte à vos fichiers de configuration que vous recherchez et remplacez".

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

Pour changer le "7s" 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 d'environnement 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 changez client_body_timeout par l'option de configuration et $client_body_timeout par la variable d'environnement à laquelle elle est associée. Utilisez avec un fichier de configuration existant et ça fonctionnera parfaitement.

1voto

Eric Meadows Points 111

Bonne façon de le faire avec lua, car la réponse ci-dessus est obsolète :

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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X