nginx
docker

docker hubのnginxイメージで、起動時環境変数によるproxy_set_header制御を行う

タイトルがうまく表現できないので説明すると、

docker-composeで起動する構成
         +-------------------------------------+
         |                                     |
         |    +-------+              +-----+   |
--(req)--|--> | nginx | --(proxy)--> | app |   |
         |    +-------+              +-----+   |
         |                                     |
         +-------------------------------------+

このnginxがproxyするリクエストのヘッダーを起動時の環境変数で制御したいという話。

指定すればUser-Agentヘッダーを上書き
$ OVERRIDE_UA=iPhone docker-compose up
指定しない場合は上書きしない
$ docker-compose up

appでスマホUAかどうかをチェックしてリダイレクトなどありまして、ローカル開発時はそれをバイパスしたいってのがモチベーション。

変更前のDockerファイルとdocker-compose.yml(nginx部分のみ)

Dockerファイル
FROM nginx:latest
ADD nginx.conf /etc/nginx/nginx.conf
docker-compose.yml
services:
  nginx:
    build:
      context: .
      dockerfile: Dockerfiles/proxy
    ports:
     - "80:80"

1. perlモジュールが使えるnginx:perlイメージを利用する

library/nginx - Docker Hub

perlタグのものだとngx_http_perl_moduleが含まれている。
ただしnginxプロセス起動時にperlモジュールを有効にする必要があるみたいなので、

Perl module not enabled with alpine Dockerfile? · Issue #124 · nginxinc/docker-nginx

CMDディレクティブを上書く。

FROM nginx:perl
ADD nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off; load_module /etc/nginx/modules/ngx_http_perl_module-debug.so;"]

2. コンテナに環境変数を引き継ぐ

Environment variables in Compose | Docker Documentation

docker-compose.ymlでenvironmentを記述すれば環境変数を渡せる。また値を記述しなければdocker-composeコマンド実行時の環境変数が引き継がれる。

docker-compose.yml
services:
  nginx:
    build:
      context: .
      dockerfile: Dockerfiles/proxy
    ports:
     - "80:80"
    environment:
     - OVERRIDE_UA

3. nginx.confで環境変数を読み込む

nginx.conf で環境変数を読み込む - Qiita

上記記事にある通り、nginx.conf上で環境変数を制御するためにngx_http_perl_moduleが必要だったのです。

nginx.confのdiff
--- a/nginx.conf
+++ b/nginx.conf
@@ -1,6 +1,8 @@
 user  nginx;
 worker_processes  1;

+env OVERRIDE_UA;
+
 error_log  /var/log/nginx/error.log warn;
 pid        /var/run/nginx.pid;

@@ -11,6 +13,8 @@ events {


 http {
+    perl_set $override_ua 'sub { return $ENV{"OVERRIDE_UA"}; }';
+
     include       /etc/nginx/mime.types;
     default_type  application/octet-stream;

@@ -29,6 +33,7 @@ http {

     proxy_set_header Host $host;
     proxy_set_header Client-Ip $remote_addr;
+    proxy_set_header User-Agent $override_ua;

     server {
       listen 80;

4. 「上書く」or「上書かない」の制御を行う

proxy - nginx: Why I can't put proxy_set_header inside an if clause? - Server Fault

にある通り、nginx.confのif内ではproxy_set_headerが使えない。値の有無で制御しようと思ったがダメっぽい。
dirtyな方法だと思うが、起動時にコンテナ内でnginx.confを弄る方法をとってみた。

run_nginx.sh
perl -pi -e "s/# proxy_set_header User-Agent/proxy_set_header User-Agent/" /etc/nginx/nginx.conf
if [ -z "$OVERRIDE_UA" ]; then
  perl -pi -e "s/proxy_set_header User-Agent/# proxy_set_header User-Agent/" /etc/nginx/nginx.conf
fi

nginx -g "daemon off; load_module /etc/nginx/modules/ngx_http_perl_module-debug.so;"
Dockerファイル
FROM nginx:perl
ADD nginx.conf /etc/nginx/nginx.conf
ADD run_nginx.sh /
CMD ["sh", "/run_nginx.sh"]

5. 実行

これで期待した制御が実現できた。

指定すればUser-Agentヘッダーを上書き
$ OVERRIDE_UA=iPhone docker-compose up
指定しない場合は上書きしない
$ docker-compose up