Frage Wie kann ich Umgebungsvariablen in Nginx.conf verwenden?


[Cross-gepostet und bearbeitet von https://stackoverflow.com/questions/21933955 wie es für StackOverflow zu Sysadmin-ähnlich angesehen wurde.]

Ich habe einen Andock-Container, auf dem Nginx ausgeführt wird, der mit einem anderen Andock-Container verknüpft ist. Der Hostname und die IP-Adresse des zweiten Containers werden beim Start in die Nginx-Container als Umgebungsvariablen geladen, sind aber vorher nicht bekannt (sie sind dynamisch). Ich will meine nginx.conf um diese Werte zu verwenden - z.B.

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

Wie kann ich Umgebungsvariablen beim Start in die Nginx-Konfiguration bekommen?

BEARBEITEN 1

Dies ist die gesamte Datei nach der vorgeschlagenen Antwort unten:

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;
    }
}

Nginx und dann Fehler neu laden:

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

EDIT 2: mehr Details

Aktuelle Umgebungsvariablen

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

Wurzel 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;

Site-Nginx-Konfiguration:

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;

Nginx-Konfiguration neu laden:

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

143
2018-02-21 15:02


Ursprung


Dies ist keine generische Lösung für Umgebungsvariablen, aber wenn Sie Umgebungsvariablen für die Hostnamen / IP-Adressen von Upstream-Servern verwenden möchten, beachten Sie, dass Docker (zumindest in neueren Versionen) / etc / hosts für Sie modifiziert. Sehen docs.docker.com/userguide/dockerlinks    Das heißt, wenn Ihr verknüpfter Container "app_web_1" heißt, erstellt docker in Ihrem Nginx-Container eine Zeile in / etc / hosts. Sie können also einfach ersetzen server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000;    mit server app_web_1:5000; - mozz100
Danke @ mozz100 - das ist unglaublich nützlich - / etc / hosts Einträge sind in diesem Fall viel effektiver als env vars. Das einzige fehlende Bit ist, was passiert, wenn der Upstream-Container neu gestartet wird und eine neue IP erhält. Ich nehme an, dass die Kindercontainer immer noch auf die ursprüngliche IP verweisen, nicht auf die neue? - Hugo Rodger-Brown
Ja, wenn Sie neu gestartet haben app_web_1 Es würde eine neue IP-Adresse erhalten, also müssten Sie auch Ihren nginx-Container neu starten. Docker würde es mit einem aktualisierten neu starten /etc/hosts Sie müssten also die nginx-Konfigurationsdatei (en) nicht ändern. - mozz100


Antworten:


Wenn Sie eine Verknüpfung verwenden, richtet docker Umgebungsvariablen ein und fügt einen Alias ​​für den verknüpften Container hinzu /etc/hosts. Wenn Sie den Port fest codieren können (oder wenn es nur Port 80 ist), können Sie einfach Folgendes tun:

upstream gunicorn {
    server linked-hostname:5000;
}

Der Port ist nur in einer Umgebungsvariable verfügbar, die nicht in der verwendet werden kann upstream oder in Server- oder Standortblöcken. Sie können nur in der Hauptkonfiguration referenziert werden, was Ihnen nicht weiterhilft. Du könntest das mit machen das Openresty-Bündel das schließt Lua ein.

Wenn Sie openresty / Lua nicht verwenden möchten, besteht eine andere Möglichkeit darin, beim Start des Containers eine Ersetzung vorzunehmen. Ihre docker run Der Befehl könnte die Verknüpfung erstellen und dann ein Wrapper-Skript ausführen, das die entsprechende Ersetzung durchführt:

#!/bin/bash
/usr/bin/sed -i "s/server<gunicorn_server_placeholder>/${APP_WEB_1_PORT_5000_TCP_ADDR}/" default
start nginx

56
2018-03-18 06:14



Yup - aktuelle (Arbeits) Lösung ist genau dies. Scheint nur ein bisschen hacky. (Das heißt, es ist nur eine lokale Entwicklungsumgebung, Hacking ist also kein großes Problem.) - Hugo Rodger-Brown
Ich bin darauf gestoßen, also habe ich etwas generischer gemacht Nginx-Container Dadurch werden Umgebungsvariablen in der Konfiguration automatisch erweitert. - Shepmaster


Von die offizielle Nginx-Docker-Datei:

Umgebungsvariablen in der nginx-Konfiguration verwenden:

Standardmäßig unterstützt Nginx die Verwendung von Umgebungsvariablen nicht   in den meisten Konfigurationsblöcken.

Aber envsubst kann als verwendet werden   Abhilfe, wenn Sie Ihre Nginx-Konfiguration generieren müssen   dynamisch bevor nginx startet.

Hier ist ein Beispiel mit 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;'" 

Die Datei mysite.template enthält dann möglicherweise Variablenreferenzen wie   diese :

listen ${NGINX_PORT};

Aktualisieren:

Aber Sie wissen, dass dies zu seinen Nginx-Variablen wie folgt verursacht:

proxy_set_header        X-Forwarded-Host $host;

beschädigt zu:

proxy_set_header        X-Forwarded-Host ;

Um das zu verhindern, benutze ich diesen Trick:

Ich habe ein Skript, um Nginx auszuführen, das auf der docker-compose Datei als Befehlsoption für Nginx Server, ich nannte es run_nginx.sh:

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

Und wegen definiert neu DOLLAR Variable an run_nginx.sh Skript, jetzt Inhalt von meinem nginx.conf.template Datei für Nginx selbst ist wie folgt:

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

Und für meine definierte Variable ist das so:

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

Ebenfalls Hier, da ist mein wirklicher Anwendungsfall dafür.


61
2018-02-11 12:39



Das tötet nginx confs gerne proxy_set_header Host $http_host; - shredding
@ shredding Sie können Variablennamen übergeben, die ersetzt werden sollen - andere werden nicht berührt: command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" funktioniert für mich b / c Ich weiß, wie sie heißen ... - pkyeck
ok, vergaß zu entkommen $, sollte sein command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'" - pkyeck
Mit Rücksicht auf das Entkommen funktioniert das in Ihrer eigenen Dockerfile: CMD ["/bin/sh","-c", "if [ -n \"${SOME_ENV}\" ]; then echo envsubst '${SOME_ENV}' with ${SOME_ENV} && envsubst '${SOME_ENV}' < /etc/nginx/conf.d/default.template > /etc/nginx/conf.d/default.conf; fi ; nginx -g 'daemon off;'"] - KCD
Dies ist eine großartige Lösung. Vielen Dank. Auch der oben erwähnte Link github.com/docker-library/docs/issues/496 hat eine großartige Lösung für das Problem. - kabirbaidhya


Dies mit Lua zu tun ist wesentlich einfacher als es klingt:

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

Ich habe das hier gefunden:

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

Bearbeiten:

Offensichtlich erfordert dies die Installation des Lua-Moduls: https://github.com/openresty/lua-nginx-modul

Bearbeiten 2:

Beachten Sie, dass Sie bei diesem Ansatz definieren müssen env Variable in Nginx:

env ENVIRONMENT_VARIABLE_NAME

Sie müssen dies in Toplevel-Kontext in tun nginx.conf oder es wird nicht funktionieren! Nicht im Serverblock oder in der Konfiguration einiger Seiten in /etc/nginx/sites-available, weil es von enthalten ist nginx.conf im http Kontext (der kein Kontext der obersten Ebene ist).

Beachten Sie auch, dass bei diesem Ansatz, wenn Sie versuchen, eine Weiterleitung zu erstellen, z. B .:

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

es wird nicht so gut funktionieren:

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

Und wenn Sie ihm einen separaten Variablennamen geben:

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 interpretiert es nicht und leitet Sie zu https://%24server_name_from_env/.


26
2017-12-01 16:26



Sie brauchen eventuell env NGINX_SERVERNAME irgendwo in deiner nginx.conf. - hiroshi
Dies funktionierte nicht für mich, obwohl ich das Lua-Modul in meinem Nginx-Docker-Bild habe. Könnte das mit der Tatsache zusammenhängen, dass ich eine Konfigurationsdatei in meine nginx.conf einfüge? ich versuchte zu set_by_luadie Variable in der enthaltenen Konfigurationsdatei, während der env MY_VAR Die Deklaration war wie vorgeschlagen in der Haupt-nginx.conf. Was für eine Schande, das wäre die sauberste Lösung gewesen! - pederpansen
Kennt jemand die Vor- / Nachteile dieser Methode zu verwenden? envsubst? Ich schätze der Profi ist, dass du nicht rennen musst envsubstr Befehl vor dem Starten des Servers und der con ist, dass Sie das Lua-Modul installieren müssen? Ich frage mich, ob es irgendwelche Sicherheitsimplikationen für beide Ansätze gibt? - Mark Winterbottom
@MarkWinterbottom hat das noch nicht getestet, aber es sieht so aus, als müssten Sie keinen Schreibzugriff auf die nginx-Konfigurationsdateien gewähren, die Sie verwenden müssen envsubst und das ist in meinem Fall ein No-Go - Griddo


Ich habe etwas geschrieben, das hilfreich sein kann oder auch nicht: https://github.com/yawn/envplate

Es bearbeitet inline Konfigurationsdateien mit $ {key} Verweisen auf Umgebungsvariablen und erstellt optional Backups / Protokollierung dessen, was es tut. Es ist in Go geschrieben und die resultierende statische Binärdatei kann einfach von der Release-Registerkarte für Linux und MacOS heruntergeladen werden.

Es kann auch exec () Prozesse, ersetzt Standardwerte, Protokolle und verfügt über sinnvolle Fehler Semantik.


14
2017-11-15 16:02





Was ich getan habe, war das erb!

cat nginx.conf  | grep -i error_log

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

- Nach der Verwendung von erb

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

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

Dies wird in der verwendet Cloudfoundry staticfile-buildpack

Beispiel nginx-Konfiguration: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

In Ihrem Fall

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;
}

werden

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
2018-02-20 00:11



Können Sie diese Antwort erweitern? Es ist erwähnenswert, dass es eine bereits akzeptierte Antwort auf diese Frage oben gibt, aber wenn Sie der Meinung sind, dass Ihre Antwort einen Mehrwert bringt, dann erweitern Sie bitte weiter. - BE77Y


Mit Bezug auf die Antwort auf die Verwendung von Erb, kann es wie folgt getan werden.

Schreiben Sie die NGINX-Konfigurationsdatei als eine erb-Datei, die die Umgebungsvariable enthält, und bewerten Sie sie mit dem Befehl erb in einer normalen Konfigurationsdatei.

erb nginx.conf.erb > nginx.conf

Im Server-Block der Datei nginx.conf.erb könnte es sein

listen <%= ENV["PORT"] %>;

4
2017-08-01 11:39



Muss ich Ruby dann im Container installieren? (Wenn nicht, wie ist erb in der Lage, Variablensubstitution zu machen ... nachdem der Container gestartet wurde, oder?) - erb ist das Ruby Zeug richtig: stuartellis.name/articles/erb - KajMagnus
Es ist ein Befehlszeilentool, das mit der Standard-Ruby-Installation geliefert wird. Versuchen Sie andere Optionen, wenn Sie es nicht im Container haben. - Ruifeng Ma
Ok, danke für die Erklärung - KajMagnus


Ich weiß, dass dies eine alte Frage ist, aber für den Fall, dass jemand darüber stolpert (so wie ich es jetzt habe), gibt es einen viel besseren Weg, dies zu tun. Da docker den Alias ​​des verknüpften Containers in / etc / hosts einfügt, können Sie dies tun

upstream upstream_name {
    server docker_link_alias;
}

Angenommen, Ihr Docker-Befehl ist etwas wie docker run --link othercontainer:docker_link_alias nginx_container.


3
2018-03-17 05:16



Sie können den Host mit dieser Methode, aber nicht mit dem Port abrufen. Sie müssen den Port entweder fest codieren oder annehmen, dass Port 80 verwendet wird. - Ben Whaley


Eine weitere Option ... Ich habe dieses Tool gerade gefunden: https://github.com/kreuzwerker/envplate ... In Go geschrieben, kann es sehr einfach installiert werden. Die Verwendung ist ziemlich einfach. Allerdings müssen Sie Template-Variablen in Ihrer nginx.conf platzieren.

Zum Beispiel ${SOME_ENV_VAR} wird ersetzt, wenn envplate ist ep Befehl wird für die Datei aufgerufen. Sollte Ihre Dockerfile diese Binärdatei nicht bekommen oder sollte sie aus irgendeinem Grund nicht laufen, würde Ihre Konfiguration ungültig werden. Nur eine kleine Anmerkung im Vergleich zu anderen Lösungen wie perl oder lua Erweiterungen.

Ich mag es wirklich, wie Sie auch Standardwerte für die Einstellung festlegen können, wenn die Umgebungsvariable nicht gesetzt ist. Ex. ${SOME_ENV_VAR:default-value} (und Sie können Werte entziehen). Auch hier muss envplate noch erfolgreich ausgeführt werden.

Ein Vorteil eines solchen Ansatzes besteht darin, dass Sie nicht mit einem Docker-Image enden, das größer als nötig ist, weil Sie alle möglichen zusätzlichen Module installiert haben, die Sie sonst nicht benötigen. Es kann auch einfacher sein, als sed zu verwenden, wenn die Dinge komplexer werden und diese Standardfunktionalität enthält.


2
2017-07-20 00:14



ich rate github.com/gliderlabs/sigil als ich in viele Bugs und Probleme mit envplate geriet. - Nick


Ich beende das mit einem Shell-Skript.

Hier ist die nginx Vorlage:

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;
    }

}

Und das Skript zum Ersetzen der Umgebungsvariablen ist hier:

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;'

2
2018-01-20 06:35





Das offizielle nginx-Bild empfiehlt die Verwendung envsubst, aber wie von anderen dargelegt es wird auch ersetzen $host und andere Variablen, was nicht wünschenswert ist. Aber glücklicherweise envsubst können nimm die Namen der zu ersetzenden Variablen als Parameter.

Um einen sehr komplexen Befehlsparameter für den Container zu vermeiden (wie im verknüpften Beispiel), können Sie ein Docker-Eintragspunktskript schreiben, das die Umgebungsvariablen vor dem Ausführen des Befehls ausfüllt. Das Einstiegsskript ist auch ein guter Ort, um die Parameter zu validieren und Standardwerte zu setzen.

Hier ist ein Beispiel für einen Nginx-Container, der einnimmt API_HOST und API_PORT Parameter als Umgebungsvariablen.

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;"]

1
2017-07-02 17:28