2019年2月25日月曜日

jwilder/nginx-proxy を使って docker のシングルホストでゼロダウンデプロイする

概要

かなり有名な手法ですが jwilder/nginx-proxy を使ってコンテナのゼロダウンデプロイを試してみました
nginx-proxy は VIRTUAL_HOST を使ってコンテナのロードバランスをしてくれるコンテナです

環境

  • macOS 10.14.3
  • docker 18.09.2

アプリ作成

なんでも OK です
今回はホスト名を返すアプリを Ruby で作成しました
docker で動かせるように Dockerfile も作成します

  • bundle init
  • vim Gemfile
gem "sinatra"
  • vim app.rb
require 'sinatra'

get '/' do
  `hostname`.strip
end
  • vim Dockerfile
FROM ruby

ADD . /home
WORKDIR /home
RUN bundle install --path vendor
EXPOSE 4567

CMD ["bundle", "exec", "ruby", "app.rb", "-o", "0.0.0.0"]

こんな感じです
テストするのであれば以下のような感じで実行してホスト名が取得できれば OK です

  • docker build -t app .
  • docker run --rm -p 4567:4567 app
  • curl localhost:4567

docker-compose に組み込む

作成したアプリを jwilder/nginx-proxy と連携するために docker-compose.yml を作成しましょう

  • vim docker-compose.yml
version: '2'
services:
  proxy:
    image: jwilder/nginx-proxy
    restart: always
    ports:
      - "14567:80"
    volumes:
      - "/var/run/docker.sock:/tmp/docker.sock:ro"
  app:
    build: .
    restart: always
    ports:
      - "4567"
    environment:
      - VIRTUAL_HOST=localhost
      - VIRTUAL_PORT=4567
  app_a:
    extends:
      service: app

appapp_a 2 つアプリを作成します
このアプリを nginx-proxy 配下にぶら下げます
3 つ 4 つ増やしても問題ないです
ポイントは VIRTUAL_HOST でバランシングしている点です

nginx-proxy はもしアプリが死んでいる場合、そちらにはリクエストを送信しません
なのでこれで appapp_a と交互に入れ替えることでゼロダウンタイムなデプロイを実現します

テスト

では実際に試してみます
まずは docker-compose でコンテナを立ち上げます
nginx-proxy にアクセスしてアプリにバランシングされているか確認しましょう
バランシング方式は単純なラウンドロビンっぽいです

  • docker-compose up -d
  • curl localhost:14567

ここからが本番です
まずは app_a を停止してみましょう

  • docker-compose stop app_a

これでアクセスしてもちゃんとエラーにならずにレスポンスが返り続けます
app_a コンテナを再作成してみましょう

  • docker-compose up -d --force-recreate app_a

再作成後しばらくするとリクエストが新しい app_a にも振られるようになると思います
あとは同じように app に対しても行えば OK です

今回は --force-recreate でコンテナを再作成しただけですがコードが変更されておりイメージのビルドも必要な場合は以下のような感じでイメージの再ビルドもしましょう

  • git pull
  • docker-compose rm app_a
  • docker-compose build app_a
  • docker-compose up -d app_a

レジストリに登録しておき pull しても OK だと思います

Tips

例えば Let's Encrypt で取得した証明書を使って SSL を有効にするには以下のように変更します

まず docker-compose.yml でアプリの VIRTUAL_HOST の部分をちゃんとしたドメインに変更します

environment:
  - VIRTUAL_HOST=hoge.fuga.com
  - VIRTUAL_PORT=9292

そして proxy 側の定義の部分に証明書をマウントする定義を記載します
443 を LISTEN しましょう
そして証明書をコンテナの /etc/nginx/certs にマウントします
この時、鍵 (.key) と証明書 (.crt) の名前は VIRTUAL_HOST で指定したドメイン名にします

ports:
  - "80:80"
  - "443:443"
volumes:
  - "/var/run/docker.sock:/tmp/docker.sock:ro"
  - "./keys/privkey.pem:/etc/nginx/certs/hoge.fuga.com.key"
  - "./keys/fullchain.pem:/etc/nginx/certs/hoge.fuga.com.crt"

すると SSL で受け付けるための conf ファイルを自動で作成してくれます
なお SSL を有効にすると http 側のアクセスはすべて https にリダイレクトするようになっています

最後に

nginx-proxy を使ってコンテナをバランシングしてダウンタイム無しでコンテナをデプロイする方法を紹介しました
実際はこれ以外に証明書やコンテナ間のセッション情報の共有やデータベースなどが絡むのでアプリ側の改修も必要になるかもしれません

過去に haproxy を使ってロードバランシングした記事を紹介したのですがこれを使っても同じようなことができると思います

参考サイト

0 件のコメント:

コメントを投稿