Come posso usare le variabili d'ambiente in Nginx.conf


183

[Pubblicazione incrociata e modifica da https://stackoverflow.com/questions/21933955 poiché era considerata troppo simile a sysadmin per StackOverflow.]

Ho un container docker che esegue Nginx, che si collega a un altro container docker. Il nome host e l'indirizzo IP del secondo contenitore vengono caricati nel contenitore Nginx come variabili di ambiente all'avvio, ma non si conoscono prima (è dinamico). Voglio che io nginx.confusi questi valori - ad es

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

Come posso ottenere variabili di ambiente nella configurazione di Nginx all'avvio?

MODIFICA 1

Questo è l'intero file, dopo la risposta suggerita di seguito:

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

Ricaricare nginx quindi errori:

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

EDIT 2: maggiori dettagli

Variabili d'ambiente correnti

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

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

Configurazione del sito 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;

Ricarica la configurazione di nginx:

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

8
Questa non è una soluzione generica per le variabili di ambiente, ma se vuoi usare le variabili di ambiente per i nomi host / indirizzi IP dei server upstream, nota che Docker (almeno nelle versioni recenti) modifica / etc / hosts per te. Vedi docs.docker.com/userguide/dockerlinks Questo significa che se il tuo contenitore collegato è chiamato 'app_web_1', la finestra mobile creerà una linea in / etc / hosts nel tuo contenitore Nginx. Quindi puoi semplicemente sostituire server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000; con server app_web_1:5000;
mozz100 il

1
Grazie @ mozz100 - è incredibilmente utile - le voci / etc / hosts sono molto più efficaci di env vars in questo caso. L'unico bit mancante è cosa succede se il contenitore upstream viene riavviato e acquisisce un nuovo IP. Presumo che i container secondari continueranno a indicare l'IP originale, non quello nuovo?
Hugo Rodger-Brown,

1
Sì, se riavviato app_web_1otterrebbe un nuovo indirizzo IP, quindi dovrai riavviare anche il tuo contenitore nginx. Docker lo riavvierebbe con un aggiornamento in /etc/hostsmodo da non dover modificare i file di configurazione di nginx.
mozz100,

Risposte:


100

Dal file docker Nginx ufficiale:

Utilizzo delle variabili d'ambiente nella configurazione di nginx:

Immediatamente, Nginx non supporta l'utilizzo delle variabili di ambiente all'interno della maggior parte dei blocchi di configurazione.

Ma envsubstpuò essere usato come soluzione alternativa se è necessario generare dinamicamente la configurazione di nginx prima dell'avvio di nginx.

Ecco un esempio 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;'" 

Il file mysite.template può quindi contenere riferimenti a variabili come questo:

listen ${NGINX_PORT};

Aggiornare:

Ma sai che questo ha causato le sue variabili Nginx in questo modo:

proxy_set_header        X-Forwarded-Host $host;

danneggiato a:

proxy_set_header        X-Forwarded-Host ;

Quindi, per evitarlo, uso questo trucco:

Ho uno script per eseguire Nginx, che ha usato il docker-composefile come opzione di comando per il server Nginx, l'ho chiamato run_nginx.sh:

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

E a causa della nuova DOLLARvariabile definita sullo run_nginx.shscript, ora il contenuto del mio nginx.conf.templatefile per la variabile Nginx stessa è così:

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

E per la mia variabile definita è così:

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

Anche qui , c'è il mio vero caso d'uso per quello.


2
Questo uccide le confusioni di nginx comeproxy_set_header Host $http_host;
distruggere il

22
@shredding puoi inserire i nomi delle variabili da sostituire - gli altri non vengono toccati: command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"funziona per me b / c so come si chiamano ...
pkyeck,

4
ok, $command: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
ho

2
In considerazione dell'evasione, questo funziona nel tuo 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,

1
Questa è un'ottima soluzione Grazie. Anche il link sopra menzionato github.com/docker-library/docs/issues/496 offre un'ottima soluzione al problema.
Kabirbaidhya,

34

Farlo con Lua è sostanzialmente più semplice di quanto sembri:

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

L'ho trovato qui:

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

Modificare:

Apparentemente ciò richiede l'installazione del modulo lua: https://github.com/openresty/lua-nginx-module

Modifica 2:

Nota che con questo approccio devi definire la envvariabile in Nginx:

env ENVIRONMENT_VARIABLE_NAME

Devi farlo nel contesto di livello superiore nginx.confo non funzionerà! Non nel blocco server o nella configurazione di alcuni siti /etc/nginx/sites-available, perché è incluso nginx.confnel httpcontesto (che non è un contesto di livello superiore).

Si noti inoltre che con questo approccio se si tenta di effettuare un reindirizzamento, ad esempio:

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

non funzionerà altrettanto bene:

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

E se gli dai un nome di variabile separato:

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 non lo interpreterà e ti reindirizzerà a https://%24server_name_from_env/.


3
potresti aver bisogno di un env NGINX_SERVERNAMEposto nel tuo nginx.conf.
Hiroshi,

Questo non ha funzionato per me, anche se ho il modulo lua nella mia immagine docker nginx. Questo potrebbe essere correlato al fatto che includo un file di configurazione nel mio nginx.conf? Stavo provando set_by_luala variabile nel file di configurazione incluso, mentre la env MY_VARdichiarazione era nel nginx.conf principale, come suggerito. Che peccato, questa sarebbe stata la soluzione più pulita!
pederpansen,

Qualcuno conosce i pro / contro dell'utilizzo di questo metodo di envsubst? Immagino che il pro sia che non è necessario eseguire il envsubstrcomando prima di avviare il server e la contro è che è necessario installare il modulo lua? Mi chiedo se ci siano implicazioni di sicurezza su entrambi gli approcci?
Mark Winterbottom

@MarkWinterbottom non lo ha ancora testato ma sembra che non dovresti concedere l'accesso in scrittura ai file di configurazione di nginx, che devi usare envsubste che è un no-go nel mio caso
Griddo,

14

Ho scritto qualcosa che potrebbe essere utile o meno: https://github.com/yawn/envplate

Inline modifica i file di configurazione con riferimenti $ {key} alle variabili di ambiente, creando opzionalmente backup / registrazione di ciò che fa. È scritto in Go e il binario statico risultante può essere semplicemente scaricato dalla scheda di rilascio per Linux e MacOS.

Può anche eseguire i processi (), sostituire i valori predefiniti, i registri e ha una semantica di errore sensibile.


10

L'immagine ufficiale di nginx consiglia di utilizzareenvsubst , ma come sottolineato da altri sostituirà anche $hostaltre variabili, il che non è desiderabile. Ma per fortuna envsubstpuò prendere come parametro i nomi delle variabili da sostituire .

Per evitare un parametro di comando molto complesso nel contenitore (come nell'esempio collegato), è possibile scrivere uno script entrypoint Docker che riempirà le variabili di ambiente prima di eseguire il comando. Lo script entrypoint è anche un buon posto per convalidare i parametri e impostare i valori predefiniti.

Ecco un esempio di un contenitore nginx che accetta API_HOSTe API_PORTparametri come variabili di ambiente.

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

5

Quello che ho fatto è stato usare l' ERB !

cat nginx.conf  | grep -i error_log

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

- Dopo aver utilizzato ERB

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

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

Questo è usato nel pacchetto di file statici Cloudfoundry

Esempio di configurazione di nginx: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

Nel tuo caso

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

diventare

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
}

4

Facendo riferimento alla risposta sull'uso di erb, si può fare come di seguito.

Scrivi il file di configurazione NGINX come file erb contenente la variabile di ambiente e valutalo usando il comando erb in un normale file di configurazione.

erb nginx.conf.erb > nginx.conf

All'interno del blocco server del file nginx.conf.erb potrebbe esserci

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

1
Devo installare Ruby quindi, all'interno del contenitore? (In caso contrario, come è erbpossibile effettuare la sostituzione variabile ... dopo l'avvio del contenitore, giusto?) - erbquesta roba Ruby è giusta: stuartellis.name/articles/erb
KajMagnus

1
È uno strumento da riga di comando fornito con l'installazione standard di Ruby. Prova altre opzioni se non lo hai nel contenitore.
Ruifeng Ma

3

So che questa è una vecchia domanda, ma nel caso in cui qualcuno si imbatta in questo (come ho ora), c'è un modo molto migliore per farlo. Poiché la finestra mobile inserisce l'alias del contenitore collegato in / etc / hosts, puoi semplicemente farlo

upstream upstream_name {
    server docker_link_alias;
}

supponendo che il comando docker sia qualcosa di simile docker run --link othercontainer:docker_link_alias nginx_container.


1
È possibile ottenere l'host utilizzando questo metodo ma non la porta. Devi codificare la porta o supporre che stia usando la porta 80.
Ben Whaley

2

Un'altra opzione ... Ho appena trovato questo strumento oggi: https://github.com/kreuzwerker/envplate ... Scritto in Go, può essere installato molto facilmente. Usarlo è abbastanza semplice. Tuttavia, dovrai inserire le variabili modello nel tuo nginx.conf.

Ad esempio ${SOME_ENV_VAR}verrà sostituito quando epviene chiamato il comando envplate sul file. Quindi, se il tuo Dockerfile non riesce a ottenere quel file binario o non dovrebbe funzionare per qualche motivo, la tua configurazione non sarebbe valida. Solo una piccola nota rispetto ad altre soluzioni come l'utilizzo delle estensioni perl o lua.

Mi piace molto come è possibile impostare anche i valori predefiniti per quando la variabile di ambiente non è impostata. Ex. ${SOME_ENV_VAR:default-value}(e puoi sfuggire ai valori). Ancora una volta, Envplate deve ancora essere eseguito correttamente.

Uno dei vantaggi dell'utilizzo di un approccio come questo è che non si ottiene un'immagine Docker più grande del necessario perché si è interrotta l'installazione di tutti i tipi di moduli extra che altrimenti non sono necessari. Potrebbe anche essere più semplice dell'uso di sed se le cose iniziano a diventare complesse e contiene quella funzionalità di valore predefinito.


Consiglio github.com/gliderlabs/sigil mentre mi imbattevo in molti bug e problemi con envplate.
Nick,

2

Lo realizzo usando uno script di shell.

Ecco il modello 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;
    }

}

E lo script per sostituire le variabili di ambiente è qui:

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

1

Un'altra possibilità è usare il comando 'sed' con le espressioni regolari, quindi non dovrai assolutamente fare confusione con i tuoi file di configurazione! In questo modo è possibile utilizzare normalmente i file di configurazione, ma quando si esegue la finestra mobile, i valori verranno scambiati con le variabili env. Niente di tutto questo "aggiungi una stringa di testo ai tuoi file di configurazione che cerchi e sostituisci con."

È possibile creare un file run.sh con valori di sostituzione utilizzando le variabili di ambiente.

Per modificare i "7" su questa riga:

client_body_timeout 7s;         #Default 60s

Comando Sed utilizzando client_body_timeout come riga di ricerca e $ client_body_timeout come variabile env sostitutiva:

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

Copia / incolla questa riga per ogni parametro che desideri impostare e modifica client_body_timeout con l'opzione config e $ client_body_timeout con la variabile env a cui è associato. Utilizzare con il file di configurazione esistente e funzionerà.


0

Ecco un esempio dell'uso seddell'approccio, non necessariamente migliore ma potrebbe essere utile per alcuni. Innanzitutto, aggiungi una parola chiave personalizzata da sostituire nel file conf. In secondo luogo, creare un file docker che dichiari una ENVvariabile e quindi un file CMDutilizzato sedper modificare la configurazione prima di eseguire esplicitamente nginx.

Supponiamo quindi che default.conf contenga questo con la parola chiave docker_host:

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

E scrivi il tuo Dockerfile in modo simile a:

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

Quindi creare l'immagine ed eseguire il contenitore utilizzando

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

0

Modo corretto per farlo con lua, poiché la risposta sopra è stantia:

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.