nginx, Dockerで動的プロキシ環境

nginxで動的プロキシ環境を作ります

バックエンドのアプリはdockerを使用してバックエンドで動くwebサーバーコンテナを立ててそれを動的にプロキシしてみます。

## システム構成

*OS*

`ubuntu 14.04 LTS`

*Middleware*

  `nginx`

  *リクエストを受けてバックエンドへプロキシします*

  `docker`

  *バックエンドのアプリを動かす環境として使用します*

  `redis`

  *ドメインとバックエンドのアプリのポートを紐付けます*

  `lua`

  *nginxの設定ファイルの中でredisに接続して動的に値を返す役割*

---

### まずはubuntuの設定

```
# アップデート
apt-get -y update && apt-get -y upgrade

# 必要なパッケージをインストールします
apt-get install -y build-essential autoconf libssl-dev curl libcurl4-gnutls-dev zlib1g zlib1g-dev libxml2 libxml2-dev libxslt-dev libreadline6-dev

# redisサーバーをインストール
apt-get install -y redis-server

# luaをインストール
apt-get install -y lua5.1 liblua5.1-0 liblua5.1-0-dev
ln -s /usr/lib/x86_64-linux-gnu/liblua5.1.so /usr/lib/liblua.so

# luaのソケットライブラリをインストール
apt-get install -y lua-svn

# nginxはluaモジュールを使用するためコンパイルします。

mkdir -p /tmp/src
cd /tmp/src

wget -O "nginx-1.7.0.tar.gz" "http://nginx.org/download/nginx-1.7.0.tar.gz"
wget -O "pcre-8.35.tar.gz" "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.35.tar.gz"
wget -O "zlib-1.2.8.tar.gz" "http://zlib.net/zlib-1.2.8.tar.gz"
wget -O "openssl-1.0.1g.tar.gz" "http://www.openssl.org/source/openssl-1.0.1g.tar.gz"
wget -O "nginx_devkit.tar.gz" "https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz"
wget -O "nginx_lua.tar.gz" "https://github.com/openresty/lua-nginx-module/archive/v0.9.7.tar.gz"

tar zxf nginx-1.7.0.tar.gz
tar zxf pcre-8.35.tar.gz
tar zxf zlib-1.2.8.tar.gz
tar zxf openssl-1.0.1g.tar.gz
tar zxf nginx_devkit.tar.gz
tar zxf nginx_lua.tar.gz

# opensslのコンパイルはdocの生成で失敗するのでpatchを当てます
wget https://gist.githubusercontent.com/martensms/10107481/raw/e6bf3488f476e7eb1dc4a5f182a75641f7615380/1.0.1g-docfixes-diff.patch
patch -p0 < 1.0.1g-docfixes-diff.patch

# configure
cd nginx-1.7.0
export LUA_LIB=/usr/lib
export LUA_INC=/usr/include/lua5.1

./configure --prefix=/usr/local/nginx \
            --with-http_gzip_static_module \
            --with-http_ssl_module \
            --with-http_stub_status_module \
            --with-zlib=../zlib-1.2.8 \
            --with-pcre=../pcre-8.35 \
            --with-openssl=../openssl-1.0.1g \
            --add-module=../ngx_devel_kit-0.2.19 \
            --add-module=../lua-nginx-module-0.9.7

# コンパイル
make && make install

# luaのredisスクリプト
mkdir -p /usr/local/lib/lua/5.1
cd /usr/local/lib/lua/5.1
wget https://raw.github.com/nrk/redis-lua/version-2.0/src/redis.lua
```

**nginxの設定ファイル**

```
server {
  listen 80;
  server_name _;
  server_name_in_redirect off;
  port_in_redirect off;
  root /root/html;

  location / {
    set $upstream "";
    rewrite_by_lua '
      -- load global route cache into current request scope
      -- by default vars are not shared between requests
      local routes = _G.routes

      -- setup routes cache if empty
      if routes == nil then
        routes = {}
        ngx.log(ngx.ALERT, "Route cache is empty.")
      end

      -- try cached route first
      local route = routes[ngx.var.http_host]
      if route == nil then
        local redis  = require "redis"
        local client = redis.connect("localhost", 6379)
        route        = client:get(ngx.var.http_host)
      end

      -- fallback to redis for lookups
      if route ~= nil then
        ngx.var.upstream = route
        routes[ngx.var.http_host] = route
        _G.routes = routes
      else
        ngx.exit(ngx.HTTP_NOT_FOUND)
      end
    ';

    proxy_buffering             off;
    proxy_set_header            Host $host;
    proxy_set_header            X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect              off;
    proxy_connect_timeout       10;
    proxy_send_timeout          30;
    proxy_read_timeout          30;
    proxy_pass                  http://$upstream;
  }
}
```

[こちら](http://sosedoff.com/2012/06/11/dynamic-nginx-upstreams-with-lua-and-redis.html)のものを使わせてもらっています。

---

### dockerでnginxが起動されるイメージを作成

**dockerをインストール**

```
curl -s https://get.docker.io/ubuntu/ | sudo sh
```

**コンテナイメージの作成**

*Dockerfile*

```
FROM ubuntu

ENV DEBIAN_FRONTEND noninteractive
RUN locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8
ENV LC_ALL C
ENV LC_ALL en_US.UTF-8

RUN echo "deb http://us.archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update && apt-get upgrade -y && apt-get install -y nginx supervisor openssh-server && apt-get clean && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

RUN dpkg-divert --local --rename --add /sbin/initctl && rm -f /sbin/initctl && ln -s /bin/true /sbin/initctl

RUN useradd -d /home/docker -m -s /bin/bash docker \
  && echo docker:docker | chpasswd \
  && echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

# sshd config
RUN sed -i -e '/^UsePAM\s\+yes/d' /etc/ssh/sshd_config \
  && mkdir -p /var/run/sshd \
  && /etc/init.d/ssh start \
  && /etc/init.d/ssh stop

RUN sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd
RUN /bin/echo -e "LANG=\"ja_JP.UTF-8\"" > /etc/default/local

# supervisor
RUN mkdir -p /var/log/supervisor
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf

EXPOSE 22 80

CMD ["/usr/bin/supervisord"]
```

*supervisord.conf*

```
[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:nginx]
command=/usr/sbin/nginx -c /etc/nginx/nginx.conf
process_name=%(program_name)s
numprocs=1
stopsignal=QUIT
```

*イメージをビルド*

```
cd <Dockerfileがあるpath>
docker build --no-cache --rm -t test/nginx .
```

**nginxコンテナを起動する**

```
docker run -d -i -p 49153:80 -t test/nginx /usr/bin/supervisord
docker run -d -i -p 49154:80 -t test/nginx /usr/bin/supervisord
```

２つのコンテナの80番をそれぞれ49153, 49154に割り当てました

**redisにドメインとコンテナを紐付けます**

`user1.example.com`を`49153`ポートのコンテナ
<br>
`user2.example.com`を`49154`ポートのコンテナ
<br>
に紐付けます


※とりあえずhostsとかでubuntuのIPに向けます。実際使う時はワイルドカードとかでやると思います。

```
redis-cli =>
SET user1.example.com 127.0.0.1:49153
SET user2.example.com 127.0.0.1:49154

```

**nginxを起動**

```
/usr/local/nginx/sbin/nginx
```

user1.example.comにアクセスすれば49153のコンテナにプロキシされ
<br>
user2.example.comにアクセスすれば49154のコンテナにプロキシされるようになります。
<br>