はじめに
Docker使ってるとできるだけイメージは共通化して、設定ファイルはマウントで差し込み、環境ごとの設定差分は環境変数で埋め込む方向になってきます。
しかしながら、設定ファイルに環境変数を直接埋め込めるかは対象のソフトウェアの実装次第で、例えばfluentdは設定ファイルで環境変数を直接埋め込めるけど、nginxは環境変数を直接埋め込めなかったりします。
で、よくやるのは entrypoint.sh
みたいなのを作って起動時にsedするという原始的な方法もあるけど、もうちょっとスマートな方法として、 envsubst
という軽量なテンプレートエンジンを使うことで、設定ファイルに汎用的に環境変数を埋め込むことができて便利なので紹介したい。
インストール
nginx:alpineには既にenvsubstコマンド入ってるので、そのまま使える。
他のイメージで入ってなければ以下の方法でインストールできる。
alpine: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で設定ファイルに環境変数を埋め込めこむ汎用的なパターンとしていろいろ便利そうです。