使用envsubst和Docker,在配置文件中嵌入环境变量的通用模式
首先
使用Docker时,我们尽量将镜像通用化,通过挂载配置文件并通过环境变量嵌入来处理不同环境的配置差异。然而,能否直接将环境变量嵌入配置文件取决于目标软件的实现。例如,fluentd可以直接在配置文件中嵌入环境变量,但nginx却无法直接这么做。
所以,常常做的是创建一个类似于entrypoint.sh的文件,在启动时使用sed进行原始的方法,但作为更聪明的方法,我想介绍一下envsubst,它是一种轻量级的模板引擎,可以方便地将环境变量嵌入到配置文件中,非常方便。
安装
nginx:由于alpine镜像中已经内置了envsubst命令,因此可以直接使用。
如果未在其他影像镜像中预加载的话,可以通过以下方式安装。
阿尔卑斯山:3.5
以下的安装方法很简单。
$ apk --no-cache add gettext
如果你想要再削减一点尺寸的话,可以这样做。
FROM alpine:3.5
RUN apk --no-cache add libintl && \
apk --no-cache add --virtual .gettext gettext && \
cp /usr/bin/envsubst /usr/local/bin/envsubst && \
apk del .gettext
作为尺寸大约是这样,envsubst的文件大小大约是111KB左右。非常轻巧。
$ docker build -t envsubst .
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
envsubst latest 1bab5f1656d7 6 seconds ago 4.095 MB
alpine 3.5 88e169ea8f46 5 weeks ago 3.984 MB
Debian:Jessie
$ apt-get install gettext-base
用法
假设有一个简单的设置文件如下:
$ cat test.conf.template
hoge=$HOGE
这样的情况下,可以挂载配置文件,插入环境变量,然后将其传递给envsubst来进行嵌入。
$ docker run --rm \
-v $(pwd)/test.conf.template:/tmp/test.conf.template \
-e HOGE=aaa \
nginx:alpine \
/bin/sh -c "envsubst < /tmp/test.conf.template > /tmp/test.conf && cat /tmp/test.conf"
hoge=aaa
听起来很实用,好像什么都可以用得到。
如果像nginx.conf一样,在配置文件中可以使用$符号,则需要明确指定要嵌入的环境变量名称。
假设我们有一个类似于以下样式的nginx.conf文件,我们想要替换${NGINX_SERVER_NAME},${NGINX_UPSTREAM_HOST}和${NGINX_UPSTREAM_PORT},但不希望替换$remote_addr、$host等。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
resolver 127.0.0.11 valid=5s;
server {
listen 80;
set_real_ip_from '10.0.0.0/8';
real_ip_header X-Forwarded-For;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
server_name ${NGINX_SERVER_NAME};
set $upstream ${NGINX_UPSTREAM_HOST};
location / {
proxy_pass http://$upstream:${NGINX_UPSTREAM_PORT};
}
}
include /etc/nginx/conf.d/*.conf;
}
在这种情况下,您需要在envsubst中指定要替换的环境变量名称。
由于参数太长,因此将其放置在docker-compose.yml中。(2017/02/05: 有人指出不需要使用换行符来修饰,所以进行了修正)
version: '2'
services:
nginx:
image: nginx:alpine
command: >
/bin/sh -c
"envsubst '
$$NGINX_SERVER_NAME
$$NGINX_UPSTREAM_HOST
$$NGINX_UPSTREAM_PORT
'< /etc/nginx/nginx.conf.template
> /etc/nginx/nginx.conf
&& nginx -g 'daemon off;'"
volumes:
- ./nginx.conf.template:/etc/nginx/nginx.conf.template
ports:
- 8080:80
environment:
NGINX_SERVER_NAME: "test.example.com"
NGINX_UPSTREAM_HOST: "hoge"
NGINX_UPSTREAM_PORT: "80"
我会启动并查看配置文件的内容。
$ docker-compose up -d
Starting 20170204_nginx_1
$ docker exec -it $(docker ps -l -q) cat /etc/nginx/nginx.conf
环境变量已经嵌入正确指定的位置。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
resolver 127.0.0.11 valid=5s;
server {
listen 80;
set_real_ip_from '10.0.0.0/8';
real_ip_header X-Forwarded-For;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
server_name test.example.com;
set $upstream hoge;
location / {
proxy_pass http://$upstream:80;
}
}
include /etc/nginx/conf.d/*.conf;
}
总结
envsubst在Docker中使用可充当通用模式,方便地将环境变量嵌入到配置文件中。