[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
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.
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.)
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/
.
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;"]
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.
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"] %>;
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
}
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;'
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
.
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.
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
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
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.
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;
}