Docker,Alpine,Let's Encrypt,NginxでHTTPS化を行うリバースプロキシコンテナ

 Let's Encrypt、Docker、NginXを組み合わせて、自動で証明書更新をしてくれるリバースプロキシコンテナを用意している記事を見つけた。だがhttps-portalっていう、同じことをやるDockerイメージもすでにあったりする。
 https-portalとそのブログ記事のあいだに、ぼくも同様のものを作っている。このブログはそのDockerイメージを使ってHTTPS化して動いている。
 実装は自分に必要な機能に絞った。単一の証明書の取得、自動更新、リバースプロキシ。先に挙げた二つのDockerイメージは複数のサブドメインの証明書を取得できるようにしているが、個人的に必要なかったので証明書は一つにしておいた。今ならワイルドカード証明書取れるようになってるし。

https://github.com/hMatoba/cdcc
https://hub.docker.com/r/matoba/cdcc/

 ↑がそのソースとDockerイメージのリポジトリ。そもそもhttps-portalを使わなかったのは、そっちのソースを見てRuby入ってるのはなくてもできるだろうから軽量化できるなと思ったり、ちゃんとcertbotでの処理を理解しておきたかったから。
 機能を絞った代わりにちゃんと軽量化は果たしていて、https-portalは90MB超なところをぼくのは30MB未満。またSSLのセキュリティもちゃんと調整してSSLLabsでA+の評価を得られるようにしている。


 Let's Encryptやるコンテナを使うTipsとしては、ディレクトリ”/etc/letsencrypt”に外部ボリュームをマウントしたほうがいい。Dockerコマンドなりcomposeで設定できるから。じゃないと発行済み証明書がどんどん廃棄されていって、すぐに発行回数上限に達してしばらく使えなくなってしまう。
comment: 0

このブログについて

ソース: https://github.com/hMatoba/tetsujin

 ブログのバックエンドを作るのが趣味になっているような気がする。
 はじめはPython on GoogleAppEngineで作って、それのデータベースをNoSQLにしてみたらどうだとか始めた。そのあとでTornadoやFlask、BottleといったPythonのフレームワークをいろいろやってみて、Node.jsはasync,awaitが入る前にやってちょっときついと思ってストップした。
 その後WPFで好きだったC#で作ってみたくなって、.NET Coreのリリース直後にAzureに乗るものを作った。そして今回、.NET Core 2.0のリリースがあったのを契機にDocker運用するものに作り直した。

 ASP.NET Core MVCを使ってみた感想。モデル、ビュー(Razor)、コントローラのみを使うような、SinatraやFlaskのような、うっすい使い方もできる。スキャフォルディングとかあるけど無理に使う必要はない。フレームワークに縛られてるような感覚は強くない。その点、うまくやれるかは書き手次第。


 このブログはタグに並べた要素が全部盛りで実装されている。
 C#が好きなので、それがオープンソースになったのがうれしかった。だからほかのオープンソース技術をメインに組み合わせて、C#はMSの用意したセット(WindowsOSでIISとかSQLServerとかって構成)じゃないと使えないなんてことはないというのを実践してみたかった。DebianでNginx、MongoDBなどって構成。
 使用サービスの構成としては、Github、TravisCI、Docker Hub、Hyper.shが主なところ。
・Github→バージョン管理
・TravisCI→CIサービス
・Docker Hub→ビルドしたコンテナイメージの置き場所
・Hyper.sh→Docker開発者が「これほぼDockerやんけ」と言うぐらいにDockerライクなコンテナホスティングサービス。もちろんDockerで作ったコンテナをホスティングできる
 使用サービスの構成はシンプルなほうがいいという一般論も存在するだろうけど、これはこれで楽をするための構成を敷いている。Dockerを使ったサーバの構成管理、自動テスト、CIサービス上での自動テストからの自動デプロイなどなど。アプリの開発継続に注力できる。

 Githubへのプッシュで、TravisCIでのビルド、テストが行われる。テストにパスしてなおかつブランチがmasterだったらならDocker Hubへビルドされたコンテナイメージがアップされる。そののち、Hyper.shでそのコンテナイメージをプル、デプロイまでのコマンドが自動で流れる。Docker Hubを経由しているので、コンテナイメージがビルドの産物として残るようになっている。


・運用お値段
 VPSは安い。でもOSの管理が必要だからヤダ。AWSとかAzureのイカしたPaasを使いたいけど高い。
 DockerコンテナホスティングをしてくれるHyper.shというサービスがあり、これで月2000円以下で運用できている。リバースプロキシコンテナ+アプリコンテナの2コンテナをランニングさせるコスト1000円に加え、個人的にいろんなアプリで使うMongoDBを仕方なくVPSで運用しているコスト600円程度となっている。VPSとメジャーどころのPaasの中間あたりのお値段。
 問題としてHyper.shが日本にもアジアにもリージョンがないことによるレイテンシがががというのがあるけども妥協。


・構成
 ファイルはAzureStorage
 ↑にも書いたけどコンテナホスティングで運用していて、走っているコンテナはリバースプロキシ用一台とアプリ用一台
 証明書はLet's Encrypt
 コンテナのOSは、リバースプロキシのほうがAlpine、アプリはDebian
 テストはxUnitによる単体テストが実行できるようにしてある
 それに加えてSeleniumでのE2Eテストも用意してある。Seleniumは、一度C#で動かしたらきつかったのでPythonで動かしている
 バージョン管理はGithub
 Githubへのデプロイで、TravisCIでのデプロイが走る
 TravisCIではとりあえずSeleniumでE2Eテストをやっている
 TravisCIでテストに成功したら、そこからHyper.shへ自動デプロイが始まる
 ただし自動デプロイが始まるのはmasterブランチへのコミット時のみ


 いろいろ構成していて半分は趣味で試したかったものであるが、同時にデプロイの自動化という一年前から突き詰めたかった課題への挑戦もしている。これのおかげで、今後このブログエンジンを改良していくのにせっせと手作業でデプロイをする必要がなくなった。


・なおしたい点
 dotnetコマンドが現状docker composeを使ったプロジェクトをビルドしてくれない。だから一部プロジェクトでCI環境でのビルドができず、開発環境でビルドしたものをDocker Hub経由で使っている。CI環境でビルドができないのはSDKの不足が原因。そのSDKはVisualStudio Communityに用意されているのだが、SDKがオープンソースになっていないという理由でdotnet CLIに入れるかどうかもたついている。
'17/9/28追記
docker composeが入っているプロジェクトのビルド方法を変えて対応完了

comment: 0

DockerとAlpineOSとLet'sEncryptとcronと

 WebアプリをDockerコンテナでホスティングしたい。DockerってことでOSはAlpineで。Webアプリはイマドキ全ページHTTPSも普通だが、そこに金はかけたくないのでLet's Encryptで。まあHTTPSは、NginxのリバースプロキシをWebアプリの前に用意してそいつに任せよう。そんな構成を考えていたときのプラクティス。

 Let's Encryptの使用を考える。Let's Encryptは証明書を無料で取れるものだがその有効期間は短め。cronで定期的に証明書のアップデートをかける必要がある。
 処理としては、証明書のアップデートが必要か判断され、必要な場合のみ、certbotを使ったチャレンジが実行される。チャレンジにはいくつか方法があるが、certbotにサーバを立ててもらう方法が楽である。certbotにサーバを立ててもらっている間はコンフリクトが起こらないように、Nginxにはいったん止まってもらうのがよい。チャレンジが終わって証明書がアップデートされたら、その新しい証明書とともに起動し直してもらう。
 つまり、Let's Encryptで証明書を定期アップデートしていく構成を考えるなら、Nginxには一時的に止まってもらうタイミングが必要になりえるということ。

 Nginxを使ったリバースプロキシをコンテナで用意する場合、たいていはそのDockerfileの最終行には下記のように書いてある。
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]

Dockerコンテナはなにかしらがフォアグラウンドで動いていないと停止してしまう。だからNginxをフォアグラウンドで動かすことで、コンテナはダウンせずに走り続ける。
 しかし前段で、Nginxには、証明書アップデート時には止まってもらう必要が出てくるかもしれないと書いた。じゃあどうやってコンテナを走らせ続ける?

 フォアグラウンドにはcronに動いてもらうことにする。Alpineでのcronは下記の通りである。ヘルプを見たくて-hを付けたら、そんなオプションねーよと言いつつヘルプを返してくれた。
/ # crond -h

crond: unrecognized option: h
BusyBox v1.25.1 (2016-10-26 16:15:20 GMT) multi-call binary.

Usage: crond -fbS -l N -d N -L LOGFILE -c DIR

-f Foreground
-b Background (default)
-S Log to syslog (default)
-l N Set log level. Most verbose:0, default:8
-d N Set log level, log to stderr
-L FILE Log to FILE
-c DIR Cron dir. Default:/var/spool/cron/crontabs

 cronをフォアグラウンドで行うオプションがあるのでそれを実行すれば、コンテナはダウンせずに走り続ける。もちろんcronによる定期処理として、Let's Encryptの証明書アップデートチェックを行う。
certbot renew --pre-hook "nginx -s stop" --post-hook "nginx"

Nginxはデーモンで実行しておく。フォアグラウンドではcronが動いている。だから証明書アップデートのためにNginxをいったん止めることになってもなにも問題はない。

 そんなことで証明書更新を自動でやってくれるリバースプロキシコンテナとしてできたのが下記。
https://github.com/hMatoba/cdcc
comment: 0

Dockerで開発しているWebアプリのデバッグをHTTPSで行えるようにする

 VisualStudioでDockerサポートのASP.NET Core MVCアプリを作り始めた。

 イマドキWebサイトは全ページHTTPSがめずらしくない。それがSEO上は有利でもあるし、Let'sEncryptという無料で証明書を取得する手段も出てきたのでやらない手はない。
 全ページHTTPSを見越し、開発環境でのデバッグもHTTPSでできるようにしておく。Dockerサポートを有効にしているので、コンテナを一個つけて、VisualStudioのデバッグ設定を一つ行うだけ。

 まずデバッグ用のcomposeファイルを編集する。docker-compose.override.ymlにリバースプロキシをやってくれるコンテナを加える。あとWebアプリコンテナのポートはホストにバインドしておく必要がなくなったので設定を削除する。
docker-compose.override.yml

version: '3'

services:
tetsujin:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "80:80"
   ↓
version: '3'


services:
tetsujin:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ssldevenv:
image: matoba/ssldevenv
ports:
- "443:443"
links:
- tetsujin:webapp


追加されたssldevenvというコンテナは、OpenSSLで発行した証明書を入れたもの。中ではNginXがリバースプロキシの役割で動いている。リバースプロキシはwebappという名前で解決されるホストへつながれるようになっている。なのでlinksでWebアプリコンテナの名前認識を変えてつないでいる。
 実際の中身は下記リポジトリを参照。
https://github.com/hMatoba/ssldevenv



 あとはVisualStudioのデバッグ設定を行う。これまではデバッグ実行ごとにブラウザが立ち上がるようになっていたのでこれを切る。




 VisualStudioでデバッグを実行する。コンテナのビルドなどが終わってローカルにデプロイされるのを待つ。




 ブラウザでhttps://localhostへアクセスしてみる。Webアプリへの接続が確認できるはず。




 開発環境でのデバッグをHTTPS接続でできるようにした。Dockerコンテナを使っているので書き換えるところは多くなく楽だったはず。本番環境でもこれと同様に楽に証明書を取得して接続できるコンテナを用意してある。
https://absurd.azurewebsites.net/Article/121


今回のコミット
comment: 0

継続的デプロイと継続認証のコンテナ

 DockerコンテナでLet's Encryptと継続的デプロイをなにも考えずに組み合わせたら大失敗だった。Let's Encryptには証明書取得数の上限があった。
https://absurd.azurewebsites.net/Article/118

 上記の問題を、証明書を外部ボリュームに入れて運用するDockerイメージを作ることで解決した。
https://github.com/hMatoba/cdcc

 下記はコンテナホスティングサービスHyper.sh上で、このDockerイメージを作って運用するときのdocker-compose。証明書を自動で取りに行って、WebアプリをHTTPS運用するコンテナとしてよしなにやってくれる。
docker-compose.yml

version: '2'

services:
cdcc:
image: matoba/cdcc
ports:
- '80:80'
- '443:443'
fip: 199.245.56.122
size: s4
links:
- webapp:webapp1
volumes:
- cdcc:/etc/letsencrypt
environment:
DOMAIN: 'example.com'
E_MAIL: 'foobar@example.com'
LINK_NAME: 'webapp1'
webapp:
image: some_webapp
ports:
- '80:80'
comment: 0