Làm cách nào tôi có thể sử dụng các biến môi trường trong Nginx.conf


183

[Đăng chéo và chỉnh sửa từ https://stackoverflow.com/questions/21933955 vì nó được coi là quá giống sysadmin cho StackOverflow.]

Tôi có một container docker chạy Nginx, liên kết đến một container docker khác. Tên máy chủ và địa chỉ IP của vùng chứa thứ hai được tải vào vùng chứa Nginx dưới dạng các biến môi trường khi khởi động, nhưng sau đó không biết (đó là động). Tôi muốn tôi nginx.confsử dụng các giá trị này - vd

upstream gunicorn {
    server $APP_HOST_NAME:$APP_HOST_PORT;
}

Làm cách nào tôi có thể nhận các biến môi trường vào cấu hình Nginx khi khởi động?

CHỈNH SỬA 1

Đây là toàn bộ tập tin, sau câu trả lời được đề xuất dưới đây:

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

Đang tải lại nginx thì bị lỗi:

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

EDIT 2: biết thêm chi tiết

Biến môi trường hiện tại

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;

Cấu hình trang web 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;

Tải lại cấu hình nginx:

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

8
Đây không phải là giải pháp chung cho các biến môi trường, nhưng nếu bạn muốn sử dụng biến môi trường cho tên máy chủ / địa chỉ IP của máy chủ ngược dòng, lưu ý rằng Docker (ít nhất là trong các phiên bản gần đây) sửa đổi / etc / hosts cho bạn. Xem docs.docker.com/userguide/dockerlinks Điều này có nghĩa là, nếu vùng chứa được liên kết của bạn được gọi là 'app_web_1', docker sẽ tạo một dòng trong / etc / hosts trong vùng chứa Nginx của bạn. Vì vậy, bạn chỉ có thể thay thế server $ENV{"APP_WEB_1_PORT_5000_TCP_ADDR"}:5000; bằng server app_web_1:5000;
mozz100

1
Cảm ơn @ mozz100 - điều đó cực kỳ hữu ích - / etc / hosts mục hiệu quả hơn nhiều so với env vars trong trường hợp này. Thiếu một chút là những gì xảy ra nếu container ngược dòng được khởi động lại và có được một IP mới. Tôi đoán rằng các container con vẫn sẽ trỏ đến IP gốc chứ không phải IP mới?
Hugo Rodger-Brown

1
Có, nếu bạn khởi động lại, app_web_1nó sẽ nhận được một địa chỉ IP mới, vì vậy bạn cũng cần phải khởi động lại bộ chứa nginx của mình. Docker sẽ khởi động lại nó với một bản cập nhật, /etc/hostsdo đó bạn không cần phải thay đổi (các) tệp cấu hình nginx.
mozz100

Câu trả lời:


100

Từ tệp docker Nginx chính thức:

Sử dụng các biến môi trường trong cấu hình nginx:

Ngoài hộp, Nginx không hỗ trợ sử dụng các biến môi trường bên trong hầu hết các khối cấu hình.

Nhưng envsubstcó thể được sử dụng như một cách giải quyết nếu bạn cần tạo cấu hình nginx của mình một cách linh hoạt trước khi nginx bắt đầu.

Dưới đây là một ví dụ sử dụng 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;'" 

Sau đó, tệp mysite.template có thể chứa các tham chiếu biến như thế này:

listen ${NGINX_PORT};

Cập nhật:

Nhưng bạn biết điều này gây ra cho các biến Nginx của nó như thế này:

proxy_set_header        X-Forwarded-Host $host;

hư hỏng:

proxy_set_header        X-Forwarded-Host ;

Vì vậy, để ngăn chặn điều đó, tôi sử dụng thủ thuật này:

Tôi có một tập lệnh để chạy Nginx, được sử dụng trên docker-composetệp dưới dạng tùy chọn lệnh cho máy chủ Nginx, tôi đặt tên cho nó run_nginx.sh:

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

Và do đã xác định DOLLARbiến mới trên run_nginx.shtập lệnh, nên bây giờ nội dung nginx.conf.templatetệp của tôi cho biến chính Nginx giống như sau:

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

Và đối với biến được xác định của tôi là như thế này:

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

Cũng ở đây , có trường hợp sử dụng thực sự của tôi cho điều đó.


2
Điều đó giết chết nginx confs nhưproxy_set_header Host $http_host;
băm nhỏ

22
@shrpping bạn có thể chuyển tên biến để thay thế - những tên khác không được chạm vào: command: /bin/bash -c "envsubst '$VAR1 $VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"hoạt động với tôi b / c Tôi biết chúng được gọi là gì ...
pkyeck 19/2/2016

4
ok, quên thoát khỏi $, nên làcommand: /bin/bash -c "envsubst '\$VAR1 \$VAR2' < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
pkyeck

2
Với việc xem xét để thoát khỏi công việc này trong Dockerfile của riêng bạn: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
Đây là một giải pháp tuyệt vời. Cảm ơn. Ngoài ra, liên kết được đề cập ở trên github.com/docker-l Library / docs /issues / 496 có một giải pháp tuyệt vời cho vấn đề này.
kabirbaidhya

34

Làm điều này với Lua thực sự dễ dàng hơn âm thanh:

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

Tôi thấy rằng ở đây:

https://docs.apitools.com/blog/2014/07/02/USE-en môi-biến-in-nginx-conf.html

Biên tập:

Rõ ràng điều này đòi hỏi phải cài đặt mô-đun lua: https://github.com/openresty/lua-nginx-module

Chỉnh sửa 2:

Lưu ý rằng với phương pháp này, bạn phải xác định envbiến trong Nginx:

env ENVIRONMENT_VARIABLE_NAME

Bạn phải làm điều này trong bối cảnh toplevel trong nginx.confhoặc nó sẽ không hoạt động! Không phải trong khối máy chủ hoặc trong cấu hình của một số trang web /etc/nginx/sites-available, bởi vì nó được bao gồm nginx.conftrong httpngữ cảnh (không phải là bối cảnh cấp cao nhất).

Cũng lưu ý rằng với phương pháp này nếu bạn cố gắng thực hiện chuyển hướng, ví dụ:

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

nó sẽ không hoạt động tốt:

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

Và nếu bạn đặt một tên biến riêng cho nó:

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 sẽ không giải thích nó và sẽ chuyển hướng bạn đến https://%24server_name_from_env/.


3
bạn có thể cần env NGINX_SERVERNAMEmột nơi nào đó trong nginx.conf của bạn.
hiroshi

Điều này không làm việc cho tôi, mặc dù tôi có mô-đun lua trong hình ảnh docker nginx của tôi. Điều này có thể liên quan đến thực tế là tôi bao gồm một tệp cấu hình trong nginx.conf của tôi không? Tôi đã cố gắng để set_by_luabiến trong tệp cấu hình bao gồm, trong khi env MY_VARkhai báo là trong nginx.conf chính, như được đề xuất. Thật là xấu hổ, đây sẽ là giải pháp sạch nhất!
pederpansen

Có ai biết những ưu / nhược điểm khi sử dụng phương pháp này envsubstkhông? Tôi đoán pro là bạn không cần chạy envsubstrlệnh trước khi khởi động máy chủ và con có phải là bạn cần cài đặt mô-đun lua không? Tôi tự hỏi nếu có bất kỳ ý nghĩa bảo mật trên một trong hai cách tiếp cận?
Đánh dấu Winterbottom

@MarkWinterbottom chưa kiểm tra điều này nhưng có vẻ như bạn sẽ không phải cấp quyền truy cập ghi vào các tệp cấu hình nginx mà bạn phải sử dụng envsubstvà không có trong trường hợp của tôi
Griddo

14

Tôi đã viết một cái gì đó có thể có hoặc không hữu ích: https://github.com/yawn/envplate

Nó chỉnh sửa nội tuyến các tệp cấu hình với tham chiếu $ {key} cho các biến môi trường, tùy ý tạo bản sao lưu / ghi nhật ký những gì nó làm. Nó được viết bằng Go và kết quả nhị phân tĩnh có thể được tải xuống đơn giản từ tab phát hành cho Linux và MacOS.

Nó cũng có thể thực thi các tiến trình (), mặc định thay thế, nhật ký và có ngữ nghĩa thất bại hợp lý.


10

Hình ảnh nginx chính thức khuyến nghị sử dụngenvsubst , nhưng như được chỉ ra bởi những người khác, nó cũng sẽ thay thế $hostvà các biến khác, điều không mong muốn. Nhưng may mắn thay envsubstcó thể lấy làm tham số tên của các biến để thay thế .

Để tránh một tham số lệnh rất phức tạp đối với vùng chứa (như trong ví dụ được liên kết), bạn có thể viết một tập lệnh nhập điểm Docker sẽ điền vào các biến môi trường trước khi thực hiện lệnh. Kịch bản điểm vào cũng là một nơi tốt để xác nhận các tham số và đặt giá trị mặc định.

Dưới đây là một ví dụ về một container nginx có các tham số API_HOSTAPI_PORTcác tham số như các biến môi trường.

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

Những gì tôi đã làm là sử dụng erb !

cat nginx.conf  | grep -i error_log

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

--Sau khi sử dụng erb

export APP_ROOT=/tmp

erb nginx.conf  | grep -i error_log

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

Điều này được sử dụng trong Cloudfoundry staticfile-buildpack

Cấu hình nginx mẫu: https://github.com/cloudfoundry/staticfile-buildpack/blob/master/conf/nginx.conf

Trong trường hợp của bạn

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

trở nên

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

Tham khảo câu trả lời về việc sử dụng erb, nó có thể được thực hiện như dưới đây.

Viết tệp cấu hình NGINX dưới dạng tệp erb chứa biến môi trường và đánh giá nó bằng lệnh erb thành tệp cấu hình bình thường.

erb nginx.conf.erb > nginx.conf

Bên trong khối máy chủ của tệp nginx.conf.erb có thể có

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

1
Tôi có cần cài đặt Ruby không, bên trong container? (Nếu không, làm thế nào là erb? Có thể làm thay biến ... sau khi container được bắt đầu, bên phải) - erblà này của Ruby thứ quyền: stuartellis.name/articles/erb
KajMagnus

1
Đây là một công cụ dòng lệnh đi kèm với cài đặt Ruby tiêu chuẩn. Hãy thử các tùy chọn khác nếu bạn không có nó trong container.
Ruifeng Ma

3

Tôi biết đây là một câu hỏi cũ, nhưng chỉ trong trường hợp ai đó tình cờ phát hiện ra điều này (như bây giờ tôi có), có một cách tốt hơn để làm điều này. Vì docker chèn bí danh của trình chứa được liên kết trong / etc / hosts, bạn chỉ có thể làm

upstream upstream_name {
    server docker_link_alias;
}

giả sử lệnh docker của bạn là một cái gì đó như docker run --link othercontainer:docker_link_alias nginx_container.


1
Bạn có thể lấy máy chủ bằng phương pháp này nhưng không phải cổng. Bạn phải mã cứng cổng hoặc giả sử nó sử dụng cổng 80.
Ben Whaley

2

Một tùy chọn khác ... Tôi vừa tìm thấy công cụ này ngày hôm nay: https://github.com/kreuzwerker/envplate ... Được viết bằng Go, nó có thể được cài đặt rất dễ dàng. Sử dụng nó khá đơn giản. Mặc dù bạn sẽ cần đặt các biến mẫu trong nginx.conf của bạn.

Ví dụ ${SOME_ENV_VAR}sẽ được thay thế khi eplệnh envplate được gọi trên tệp. Vì vậy, Dockerfile của bạn không nhận được nhị phân đó hoặc nếu nó không chạy vì một số lý do, nó sẽ khiến cấu hình của bạn không hợp lệ. Chỉ cần lưu ý một chút so với các giải pháp khác như sử dụng tiện ích mở rộng perl hoặc lua.

Tôi thực sự thích cách bạn có thể đặt giá trị mặc định quá khi biến môi trường không được đặt. Ví dụ. ${SOME_ENV_VAR:default-value}(và bạn có thể thoát các giá trị). Một lần nữa, hãy ghen tị vẫn phải chạy thành công.

Một lợi ích của việc sử dụng một cách tiếp cận như thế này là bạn không kết thúc với hình ảnh Docker lớn hơn mức cần thiết vì bạn đã ngừng cài đặt tất cả các loại mô-đun bổ sung mà bạn không cần. Nó cũng có thể dễ dàng hơn việc sử dụng sed nếu mọi thứ bắt đầu trở nên phức tạp và nó chứa chức năng giá trị mặc định đó.


Tôi khuyên github.com/gliderlabs/sigil khi tôi gặp phải nhiều lỗi và sự cố với envplate.
Nick

2

Tôi thực hiện điều này bằng cách sử dụng một kịch bản shell.

Đây là mẫu 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;
    }

}

Và kịch bản để thay thế các biến môi trường ở đây:

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

Một khả năng khác là sử dụng lệnh 'sed' với các biểu thức thông thường, sau đó bạn sẽ không phải gặp rắc rối với các tệp cấu hình của mình! Bằng cách đó bạn có thể sử dụng các tệp cấu hình của mình một cách bình thường, nhưng khi bạn chạy docker, nó sẽ trao đổi các giá trị với các biến env. Không ai trong số này "thêm một chuỗi văn bản vào tệp cấu hình của bạn mà bạn tìm kiếm và thay thế bằng."

Bạn có thể tạo tệp run.sh với các giá trị thay thế bằng các biến môi trường của bạn.

Để thay đổi "7s" trên dòng này:

client_body_timeout 7s;         #Default 60s

Lệnh Sed sử dụng client_body_timeout làm dòng tìm kiếm và $ client_body_timeout làm biến env thay thế:

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

Sao chép / dán dòng này cho mọi tham số bạn muốn đặt và thay đổi client_body_timeout với tùy chọn cấu hình và $ client_body_timeout với biến env được liên kết. Sử dụng với tập tin cấu hình hiện có và nó sẽ chỉ hoạt động.


0

Đây là một ví dụ về việc sử dụng sedphương pháp này, không nhất thiết phải tốt hơn nhưng nó có thể hữu ích với một số người. Đầu tiên, thêm một từ khóa tùy chỉnh sẽ được thay thế trong tệp conf. Thứ hai, tạo một dockerfile khai báo một ENVbiến và sau đó CMDsử dụng sedđể chỉnh sửa cấu hình trước khi chạy nginx một cách rõ ràng.

Vì vậy, giả sử default.conf của bạn chứa từ khóa này với từ khóa docker_host:

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

Và viết Dockerfile của bạn tương tự như:

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

Sau đó xây dựng hình ảnh và chạy container bằng cách sử dụng

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

0

Cách thích hợp để làm điều này với lua, vì câu trả lời ở trên đã cũ:

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


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.