Docker + Alpine LinuxでMustacheを使う

最近はテンプレートを使った簡易な処理を書くのにMustacheを多用しています。Mustacheはシンプルなテンプレートエンジンで、各言語に実装が存在するので一度書き方を覚えてしまえば多くの言語で同じテンプレートを使える事が利点です。[公式](https://mustache.github.io/)には以下の言語に対応していると記載があります。

> Available in Ruby, JavaScript, Python, Erlang, node.js, PHP, Perl, Perl6, Objective-C, Java, C#/.NET, Android, C++, Go, Lua, ooc, ActionScript, ColdFusion, Scala, Clojure, Fantom, CoffeeScript, D, Haskell, XQuery, ASP, Io, Dart, Haxe, Delphi, Racket, Rust, OCaml, Swift, Bash, Julia, R, Crystal, and for Common Lisp

今回はDocker + Alpine Linux + Nginxでnginx.confの一部を環境によって書き換えたいと考えて、いつも使っているbashのMustacheで処理を書きましたが、標準のAlpine LinuxのDockerイメージにはbashが入っていないので使えませんでした。

何か良い方法がないか探していると、shでMustacheを使えるライブラリがありました。これはshの関数だけで作られているので、関数をロードすれば基本的にどの環境でも動く可能性があります。定義ファイルは8kb弱なのでとても軽いですね。

[rcrowley/mustache.sh: Mustache in POSIX shell](https://github.com/rcrowley/mustache.sh)

## 使い方

`./lib/mustache.sh` をロードするだけて使えるので、このファイルとLICENSEファイルをDockerイメージに含めれば良いのではないでしょうか。

```
$ git clone git@github.com:rcrowley/mustache.sh.git
$ sh
$ cd mustache.sh
$ source ./lib/mustache.sh
$ echo 'Hello, {{NAME}}!' | NAME=world mustache
Hello, world!
```


`{{{}}}` の表記には対応していませんでした。大体の機能が動作することは確認できていますが、一部動作しないものもあるかもしれません。

```
$ echo 'Hello, {{{NAME}}}!' | NAME=world mustache
Hello, {world}!
```

## Alpine Linux + Nginxでnginx.confを書き換えるサンプル

nginx.confのworker_processesを書き換えてみます。

### ファイル構成

```
$ tree . -N
.
├── Dockerfile
├── bin
│   └── run
├── conf
│   └── nginx.conf.mustache
└── lib
    ├── LICENSE      # mustache.sh/LICENSE
    └── mustache.sh  # mustache.sh/lib/mustache.sh

3 directories, 5 files
```

*Dockerfile*

```
FROM nginx:1.8.1-alpine

WORKDIR /opt/alpine-nginx/current

RUN mkdir bin lib conf
COPY bin/* bin/
COPY lib/* lib/
COPY conf/* conf/

CMD ["./bin/run"]
```

*bin/run*

Mustacheでテンプレートを置換する処理を挟んでNginxを起動。

```
#!/bin/sh

cd $(dirname $0)/..

. lib/mustache.sh
cat conf/nginx.conf.mustache | mustache > /etc/nginx/nginx.conf

nginx
```

*conf/nginx.conf.mustache*

NGINX_WORKER_PROCESSESの環境変数を置換するように設定。

```
user  nginx;
worker_processes  {{NGINX_WORKER_PROCESSES}};
daemon off;

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;

    include /etc/nginx/conf.d/*.conf;
}
```

### 動作確認

イメージをビルド。

```
$ docker build -t qiita/alpine-nginx-mustache .
Sending build context to Docker daemon 17.41 kB
Step 1 : FROM nginx:1.8.1-alpine
 ---> c0dddb65129b
Step 2 : WORKDIR /opt/alpine-nginx/current
 ---> Running in 65f2ea41af52
 ---> 3bf3081ecaba
Removing intermediate container 65f2ea41af52
Step 3 : RUN mkdir bin lib conf
 ---> Running in 900c7bcebf4c
 ---> 30d34bca8070
Removing intermediate container 900c7bcebf4c
Step 4 : COPY bin/* bin/
 ---> e7b0b96841ea
Removing intermediate container 1d5645839d05
Step 5 : COPY lib/* lib/
 ---> a3d1b4f303b3
Removing intermediate container 71c6455716bd
Step 6 : COPY conf/* conf/
 ---> 14f3d7204e7b
Removing intermediate container 58485eff3fb8
Step 7 : CMD ./bin/run
 ---> Running in ee7c082b2e69
 ---> 634865d06deb
Removing intermediate container ee7c082b2e69
Successfully built 634865d06deb
```

コンテナを起動。

```
$ docker run -d -e NGINX_WORKER_PROCESSES=2 -p 80:80 qiita/alpine-nginx-mustache
e141b6fed6d10b4e120f11277ca21b0d235024ef8313f35f7be17a82146ca819
```

コンテナの動作確認。

```
$ curl -I http://$(docker-machine ip default)
HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Mon, 25 Apr 2016 21:55:17 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 05 Apr 2016 19:06:11 GMT
Connection: keep-alive
ETag: "57040ca3-264"
Accept-Ranges: bytes
```

worker_processesがNGINX_WORKER_PROCESSESの値に書き換わっていることを確認。

```
$ docker exec -it e141b6fed6d10b4e120f11277ca21b0d235024ef8313f35f7be17a82146ca819 head -n 2 /etc/nginx/nginx.conf
user  nginx;
worker_processes  2;
```