Bagaimana saya bisa menggunakan variabel lingkungan di Nginx.conf


183

[Diposting-silang dan diedit dari https://stackoverflow.com/questions/21933955 karena dianggap terlalu sysadmin untuk StackOverflow.]

Saya memiliki wadah buruh pelabuhan yang menjalankan Nginx, yang menghubungkan ke wadah buruh pelabuhan lainnya. Nama host dan alamat IP wadah kedua dimuat ke wadah Nginx sebagai variabel lingkungan saat startup, tetapi tidak tahu sebelumnya (dinamis). Saya ingin saya nginx.confmenggunakan nilai-nilai ini - misalnya

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

Bagaimana saya bisa memasukkan variabel lingkungan ke dalam konfigurasi Nginx saat startup?

EDIT 1

Ini adalah seluruh file, setelah jawaban yang disarankan di bawah ini:

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

Muat ulang nginx lalu galat:

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

EDIT 2: lebih detail

Variabel lingkungan saat ini

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

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

Konfigurasi nginx situs:

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;

Muat ulang konfigurasi nginx:

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

8
Ini bukan solusi umum untuk variabel lingkungan, tetapi jika Anda ingin menggunakan variabel lingkungan untuk nama host / alamat IP server hulu, perhatikan bahwa Docker (setidaknya dalam versi terbaru) memodifikasi / etc / hosts untuk Anda. Lihat docs.docker.com/userguide/dockerlinks Ini artinya, jika wadah tertaut Anda disebut 'app_web_1', buruh pelabuhan akan membuat garis di / etc / hosts di wadah Nginx Anda. Jadi Anda bisa mengganti server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000; dengan server app_web_1:5000;
mozz100

1
Terima kasih @ mozz100 - itu sangat berguna - / etc / hosts entri jauh lebih efektif daripada env vars dalam kasus ini. Satu-satunya bit yang hilang adalah apa yang terjadi jika wadah upstream dimulai kembali, dan memperoleh IP baru. Saya menduga bahwa wadah anak masih akan menunjuk ke IP asli, bukan yang baru?
Hugo Rodger-Brown

1
Ya, jika Anda app_web_1me- restart itu akan mendapatkan alamat IP baru, jadi Anda perlu me-restart wadah nginx Anda juga. Docker akan memulai ulang dengan yang diperbarui /etc/hostssehingga Anda tidak perlu mengubah file konfigurasi nginx.
mozz100

Jawaban:


100

Dari file docker Nginx resmi:

Menggunakan variabel lingkungan dalam konfigurasi nginx:

Nginx tidak mendukung, tidak menggunakan variabel lingkungan di sebagian besar blok konfigurasi.

Tetapi envsubstdapat digunakan sebagai solusi jika Anda perlu membuat konfigurasi nginx Anda secara dinamis sebelum nginx dimulai.

Berikut ini contoh menggunakan 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;'" 

File mysite.template kemudian dapat berisi referensi variabel seperti ini:

listen ${NGINX_PORT};

Memperbarui:

Tapi Anda tahu ini disebabkan oleh variabel Nginx-nya seperti ini:

proxy_set_header        X-Forwarded-Host $host;

rusak untuk:

proxy_set_header        X-Forwarded-Host ;

Jadi, untuk mencegahnya, saya menggunakan trik ini:

Saya memiliki skrip untuk menjalankan Nginx, yang digunakan pada docker-composefile sebagai opsi perintah untuk server Nginx, saya menamainya run_nginx.sh:

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

Dan karena ditentukannya DOLLARvariabel baru pada run_nginx.shskrip, sekarang konten nginx.conf.templatefile saya untuk variabel Nginx itu sendiri adalah seperti ini:

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

Dan untuk variabel yang saya tetapkan adalah seperti ini:

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

Juga di sini , ada kasus penggunaan nyata saya untuk itu.


2
Itu membunuh nginx confs likeproxy_set_header Host $http_host;
shredding

22
@shredding, Anda dapat mengirimkan nama variabel yang akan diganti - yang lain tidak tersentuh: command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"berfungsi untuk saya b / c Saya tahu apa itu namanya ...
pkyeck

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

2
Dengan pertimbangan untuk melarikan diri ini berfungsi di Dockerfile Anda sendiri: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
Ini solusi hebat. Terima kasih. Juga tautan yang disebutkan di atas github.com/docker-library/docs/issues/496 memiliki solusi hebat untuk masalah ini.
kabirbaidhya

34

Melakukan ini dengan Lua jauh lebih mudah daripada kedengarannya:

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

Saya menemukan itu di sini:

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

Sunting:

Rupanya ini membutuhkan pemasangan modul lua: https://github.com/openresty/lua-nginx-module

Edit 2:

Perhatikan bahwa dengan pendekatan ini Anda harus mendefinisikan envvariabel dalam Nginx:

env ENVIRONMENT_VARIABLE_NAME

Anda harus melakukan ini dalam konteks tingkat atas nginx.confatau tidak akan berhasil! Tidak di blok server atau dalam konfigurasi beberapa situs di /etc/nginx/sites-available, karena dimasukkan oleh nginx.confdalam httpkonteks (yang bukan konteks tingkat atas).

Perhatikan juga bahwa dengan pendekatan ini jika Anda mencoba membuat pengalihan misalnya:

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

itu tidak akan berfungsi juga:

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

Dan jika Anda memberikan nama variabel yang terpisah untuk itu:

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 tidak akan menafsirkannya dan akan mengarahkan Anda ke https://%24server_name_from_env/.


3
Anda mungkin memerlukan env NGINX_SERVERNAMEsuatu tempat di nginx.conf Anda.
hiroshi

Ini tidak berhasil untuk saya, walaupun saya memiliki modul lua di gambar nginx docker saya. Mungkinkah ini terkait dengan fakta bahwa saya memasukkan file konfigurasi ke dalam nginx.conf saya? Saya mencoba set_by_luavariabel di file config yang disertakan, sedangkan env MY_VARdeklarasi ada di nginx.conf utama, seperti yang disarankan. Sayang sekali, ini akan menjadi solusi terbersih!
pederpansen

Adakah yang tahu pro / kontra untuk menggunakan metode ini envsubst? Saya kira pro adalah Anda tidak perlu menjalankan envsubstrperintah sebelum memulai server dan con adalah bahwa Anda perlu menginstal modul lua? Saya ingin tahu apakah ada implikasi keamanan pada kedua pendekatan tersebut?
Mark Winterbottom

@MarkWinterbottom belum menguji ini, tetapi sepertinya Anda tidak perlu memberikan akses tulis ke file konfigurasi nginx, yang harus Anda gunakan envsubstdan yang tidak boleh digunakan dalam kasus saya
Griddo

14

Saya menulis sesuatu yang mungkin atau mungkin tidak membantu: https://github.com/yawn/envplate

Ini inline mengedit file konfigurasi dengan referensi $ {key} ke variabel lingkungan, secara opsional membuat backup / mencatat apa yang dilakukannya. Ini ditulis dalam Go dan biner statis yang dihasilkan dapat dengan mudah diunduh dari tab rilis untuk Linux dan MacOS.

Itu juga dapat mengeksekusi () proses, mengganti default, log dan memiliki semantik kegagalan yang masuk akal.


10

Gambar nginx resmi merekomendasikan penggunaanenvsubst , tetapi seperti yang ditunjukkan oleh orang lain itu akan menggantikan juga $hostdan variabel lain, yang tidak diinginkan. Tapi untungnya envsubstbisa mengambil sebagai parameter nama variabel untuk diganti .

Untuk menghindari parameter perintah yang sangat kompleks ke wadah (seperti dalam contoh yang ditautkan), Anda dapat menulis skrip entri titik Docker yang akan mengisi variabel lingkungan sebelum menjalankan perintah. Script entrypoint juga merupakan tempat yang baik untuk memvalidasi parameter dan menetapkan nilai default.

Berikut adalah contoh wadah nginx yang menerima API_HOSTdan API_PORTparameter sebagai variabel lingkungan.

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

Apa yang saya lakukan adalah menggunakan erb !

cat nginx.conf  | grep -i error_log

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

--Setelah menggunakan erb

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

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

Ini digunakan dalam Cloudfoundry staticfile-buildpack

Contoh konfigurasi nginx: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

Dalam kasus Anda

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

menjadi

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

Mengacu pada jawaban tentang menggunakan erb, itu bisa dilakukan seperti di bawah ini.

Tulis file konfigurasi NGINX sebagai file erb yang berisi variabel lingkungan, dan evaluasi dengan menggunakan perintah erb ke file konfigurasi normal.

erb nginx.conf.erb > nginx.conf

Di dalam blok server dari file nginx.conf.erb mungkin ada

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

1
Apakah saya perlu menginstal Ruby, di dalam wadah? (Jika tidak, bagaimana cara erbmelakukan substitusi variabel ... setelah wadah dimulai, kan?) - erbapakah ini benar-benar Ruby: stuartellis.name/articles/erb
KajMagnus

1
Ini adalah alat baris perintah yang dilengkapi dengan instalasi Ruby standar. Coba opsi lain jika Anda tidak memilikinya di dalam wadah.
Ruifeng Ma

3

Saya tahu ini adalah pertanyaan lama, tetapi untuk berjaga-jaga seandainya seseorang menemukan ini (seperti yang saya miliki sekarang), ada cara yang jauh lebih baik untuk melakukan ini. Karena buruh pelabuhan memasukkan alias penampung yang tertaut di / etc / hosts, Anda bisa melakukannya

upstream upstream_name {
    server docker_link_alias;
}

dengan asumsi perintah buruh pelabuhan Anda adalah sesuatu seperti docker run --link othercontainer:docker_link_alias nginx_container.


1
Anda bisa mendapatkan host menggunakan metode ini tetapi tidak port. Anda harus membuat hard code port atau menganggapnya menggunakan port 80.
Ben Whaley

2

Pilihan lain ... Saya baru saja menemukan alat ini hari ini: https://github.com/kreuzwerker/envplate ... Ditulis dalam Go, ini dapat diinstal dengan sangat mudah. Menggunakannya cukup sederhana. Meskipun Anda perlu menempatkan variabel template di nginx.conf Anda.

Misalnya ${SOME_ENV_VAR}akan diganti ketika epperintah envplate dipanggil pada file. Jadi seandainya Dockerfile Anda gagal mendapatkan biner itu atau seandainya itu tidak berjalan karena suatu alasan, itu akan membuat konfigurasi Anda tidak valid. Hanya sedikit catatan dibandingkan dengan solusi lain seperti menggunakan ekstensi perl atau lua.

Saya sangat suka bagaimana Anda dapat mengatur nilai default juga ketika variabel lingkungan tidak diatur. Ex. ${SOME_ENV_VAR:default-value}(dan Anda dapat menghindari nilai). Sekali lagi, envplate harus tetap berhasil dijalankan.

Salah satu manfaat menggunakan pendekatan seperti ini adalah Anda tidak berakhir dengan gambar Docker yang lebih besar dari yang diperlukan karena Anda menginstal semua jenis modul tambahan yang tidak Anda perlukan. Mungkin juga lebih mudah daripada menggunakan sed jika segala sesuatu mulai menjadi kompleks dan itu berisi fungsionalitas nilai default itu.


Saya menyarankan github.com/gliderlabs/sigil ketika saya menemukan banyak bug dan masalah dengan envplate.
Nick

2

Saya menyelesaikan ini menggunakan skrip shell.

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

}

Dan skrip untuk mengganti variabel lingkungan ada di sini:

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

Kemungkinan lain adalah dengan menggunakan perintah 'sed' dengan ekspresi reguler, maka Anda tidak perlu mengacaukan file konfigurasi Anda sama sekali! Dengan begitu Anda dapat menggunakan file konfigurasi Anda secara normal, tetapi ketika Anda menjalankan buruh pelabuhan, itu akan menukar nilai-nilai dengan variabel env. Tidak satu pun dari ini "tambahkan string teks ke file konfigurasi Anda yang Anda cari dan gantikan dengan."

Anda dapat membuat file run.sh dengan nilai penggantian menggunakan variabel lingkungan Anda.

Untuk mengubah "7s" pada baris ini:

client_body_timeout 7s;         #Default 60s

Sed Perintah menggunakan client_body_timeout sebagai baris pencarian dan $ client_body_timeout sebagai variabel env pengganti:

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

Salin / rekatkan baris ini untuk setiap parameter yang ingin Anda atur dan ubah client_body_timeout dengan opsi konfigurasi dan $ client_body_timeout dengan variabel env yang dikaitkan dengannya. Gunakan dengan file konfigurasi yang ada dan itu hanya akan berfungsi.


0

Berikut adalah contoh penggunaan sedpendekatan, tidak selalu lebih baik tetapi mungkin bermanfaat bagi sebagian orang. Pertama, tambahkan kata kunci khusus untuk diganti dalam file conf. Kedua, buat dockerfile yang mendeklarasikan ENVvariabel dan kemudian CMDyang digunakan seduntuk mengedit konfigurasi sebelum menjalankan nginx secara eksplisit.

Jadi misalkan default.conf Anda berisi ini dengan kata kunci docker_host:

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

Dan tulis Dockerfile Anda mirip dengan:

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

Kemudian bangun gambar dan jalankan wadah menggunakan

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

0

Cara yang tepat untuk melakukan ini dengan lua, karena jawaban di atas basi:

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


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.