0%

DockerのHTTP Routing - Part8: xip.io と Nginx と confd 0.6 で動的リバースプロキシ

以下のサイトを参考に、confdとNginxで動的リバースプロキシを作成してみます。
特にconfdは新しいテンプレートを使いたいので、0.6.0-alpha1を利用しています。

xip.ioのサブドメインにリバースプロキシ先のポート番号をprefixして、Nginxは80ポートで複数のserver_nameに応答することができます。

データコンテナの作成

Dockerfileを作成します。VOLUMEに格納するディレクトリを書きます。nsenterでアタッチするときにbashが必要になるので、busyboxではなくubuntuにしました。

~/docker_apps/confd-data/Dockerfile
FROM ubuntu:trusty
MAINTAINER Masato Shimizu <ma6ato@gmail.com>

VOLUME ["/etc/confd","/registry_conf","/data","/etc/supervisor/conf.d","/etc/nginx/sites-enabled"]
CMD ["/bin/bash"]

イメージをビルドします。

$ docker build -t masato/confd-data .

データコンテナを起動します。

$ docker run --name confd-data -i -t -d masato/confd-data /bin/bash

condの設定

nsenterを使い、データコンテナにアタッチします。
前回作成した~/bin/docker-nesterコマンドを使います。

$ docker-nsenter cd887aad372d

データコンテナに/etc/confd/conf.d/nginx.tomlファイルを作成します。まずディレクトリの作成から。

# mkdir -p /etc/confd/conf.d

nginx.tomlファイルを作成します。

myapp1とmyapp2のサブドメイン毎に2つ設定します。これは0.6.xから採用された書式です。

/etc/confd/conf.d/myapp1.nginx.toml
[template]
prefix = "/myapp1"
keys = [
"/subdomain",
"/upstream",
]
owner = "nginx"
mode = "0644"
src = "nginx.conf.tmpl"
dest = "/etc/nginx/sites-enabled/myapp1.conf"
check_cmd = "/usr/sbin/nginx -t -c /etc/nginx/nginx.conf"
reload_cmd = "/usr/sbin/service nginx reload"

myapp2サブドメイン用のTOMLファイルです。

/etc/confd/conf.d/myapp2.nginx.toml
[template]
prefix = "/myapp2"
keys = [
"/subdomain",
"/upstream",
]
owner = "nginx"
mode = "0644"
src = "nginx.conf.tmpl"
dest = "/etc/nginx/sites-enabled/myapp2.conf"
check_cmd = "/usr/sbin/nginx -t -c /etc/nginx/nginx.conf"
reload_cmd = "/usr/sbin/service nginx reload"

DockerのHTTP Routing - Part7: Nginx multiple upstreamsで調べた書き方で、Nginxの設定ファイルのテンプレートを作成します。0.6.xからgetvgetvsが使えます。

Goのtext/templateを使っていますが、Railsに慣れていると表現力が乏しいです。特にconfdではユーザー定義関数のFuncMapが自分で追加できないので不便です。テンプレート内ではパスを操作したい場合があるので、strings.Splitも追加して欲しいです。

/etc/confd/templates/nginx.conf.tmpl
upstream &#123;&#123;getv "/subdomain"}} {
&#123;&#123;range getvs "/upstream/*"}}
server &#123;&#123;.&#125;&#125;;
&#123;&#123;end}}
}

server {
listen 80;
server_name ~^&#123;&#123;getv "/subdomain"&#125;&#125;\.(.*)\.xip\.io$;

location / {
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_pass http://&#123;&#123;getv "/subdomain"&#125;&#125;;
}
}

Nginx + etcd + confdのイメージを作成

今回作成したDockerfileです。etcd, confd, Nginxを1つのコンテナにインストールして使います。

etcdのインストールはいろいろ方法がありますがGOPATHを指定してからgo getすることにします。

confdもgo getしたかったのですがGitHubからタグをしてしてインストールできないようなので、バイナリをダウンロードします。
0.6.x Quick Start Guideで採用された書式を利用します。

~/docker_apps/nginx-confd/Dockerfile
FROM dockerfile/supervisor
MAINTAINER Masato Shimizu <ma6ato@gmail.com>
# apt-get update
RUN sed -i~ -e 's/archive.ubuntu.com/ftp.jaist.ac.jp/' /etc/apt/sources.list \
&& apt-get update
# nginx install
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y software-properties-common \
&& add-apt-repository -y ppa:nginx/stable \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y nginx \
&& sed -i 's/^\t\(listen \[::\]:80 .*\)$/\t#\1/' /etc/nginx/sites-available/default
# Go install
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y golang
ENV GOPATH /gopath
ENV PATH $PATH:$GOPATH/bin
# etcd and confd install
RUN go get github.com/coreos/etcd
RUN go get github.com/coreos/etcdctl
RUN curl -qL https://github.com/kelseyhightower/confd/releases/download/v0.6.0-alpha1/confd-0.6.0-alpha1-linux-amd64 \
-o /usr/local/bin/confd && chmod +x /usr/local/bin/confd
CMD ["supervisord", "-c", "/etc/supervisor/supervisord.conf"]
EXPOSE 80 4001 7001

Nginx + etcd + confdンテナの起動

データコンテナを--volumes-fromに指定して、Nginxのコンテナを起動します。

$ docker run --name nginx-confd -d -p 80:80 --volumes-from confd-data masato/nginx-confd

起動したコンテナのIPアドレスを確認します。

$ docker inspect 095b93ed0e25 | jq -r '.[0].NetworkSettings.IPAddress'
172.17.0.209

etcdの動作確認

Dockerホストからetcdへputとgetのテストをします。

$ curl -L http://172.17.0.209:4001/v2/keys/mykey -XPUT -d value="this is awesome"
{"action":"set","node":{"key":"/mykey","value":"this is awesome","modifiedIndex":3,"createdIndex":3}}
$ curl -L http://172.17.0.209:4001/v2/keys/mykey
{"action":"get","node":{"key":"/mykey","value":"this is awesome","modifiedIndex":3,"createdIndex":3}}

Dockerホストのetcdctlからもpeersを指定して確認してみます。etcdクラスタに参加しないので、-no-syncオプションを指定します。

$ etcdctl -C "172.17.0.209:4001" -no-sync get /mykey
this is awesome

Sinatraコンテナを2つ起動

Sinatraのサンプルアプリはmarceldegraaf/sinatraを利用します。それぞれポートは5001と5002にマッピングします。

$ docker pull marceldegraaf/sinatra
$ docker run --name sinatra-5001 -p 5001:5000 -e PORT=5000 -d marceldegraaf/sinatra
$ docker run --name sinatra-5002 -p 5002:5000 -e PORT=5000 -d marceldegraaf/sinatra

Sinatraコンテナをconfdに登録

DockerホストからNginxコンテナのIPアドレスを確認します。

$ docker-get-ip 095b93ed0e25
172.17.0.209

5001ポートでリバースプロキシするSinatraコンテナのIPアドレスを確認します。

$ docker-get-ip b6ea1bb67b7f
172.17.0.37

5002ポートでリバースプロキシするSinatraコンテナのIPアドレスを確認します。

$ docker-get-ip 93f545c79ce0
172.17.0.191

etcdctlコマンドで動的にルーティングを設定します。Dockerホストのetcdはクラスタに参加しないため-no-syncを指定します。

/myapp1/subdomainのキーに、upstreamの名前を登録します。
/myapp1/upstreamのキーに、upstream先のserverのIPアドレスとポートを登録します。

$ etcdctl -C "172.17.0.209:4001" -no-sync set /myapp1/subdomain p5001
$ etcdctl -C "172.17.0.209:4001" -no-sync set /myapp1/upstream/p5001 172.17.0.37:5000
$ etcdctl -C "172.17.0.209:4001" -no-sync set /myapp2/subdomain p5002
$ etcdctl -C "172.17.0.209:4001" -no-sync set /myapp2/upstream/p5002 172.17.0.191:5000

確認

xip.ioを使い、NginxからSinatraアプリへ動的リバースプロキシができました。

$ curl p5001.10.1.2.164.xip.io
Hello world!
$ curl p5002.10.1.2.164.xip.io
Hello world!

confdが作成した、myapp1(p5001サブドメイン)用のNginxの設定ファイルです。

/etc/nginx/sites-enabled/myapp1.conf
upstream p5001 {

server 172.17.0.37:5000;

}

server {
listen 80;
server_name ~^p5001\.(.*)\.xip\.io$;

location / {
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_pass http://p5001;
}
}

confdが作成した、myapp2(p5002サブドメイン)用のNginxの設定ファイルです。

/etc/nginx/sites-enabled/myapp2.conf
upstream p5002 {

server 172.17.0.191:5000;

}

server {
listen 80;
server_name ~^p5002\.(.*)\.xip\.io$;

location / {
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_pass http://p5002;
}
}

まとめ