読者です 読者をやめる 読者になる 読者になる

TLS/SSLサーバ証明書をDockerイメージで管理してみないか?

TLS/SSLサーバ証明書あるじゃないですか。アレ、Dockerイメージで管理して運用してみる方法もありじゃないか?という雑エントリです。

Data Volume Container

Dockerコンテナにファイルやディレクトリを供給する方法は色々あって

  1. docker build のときにイメージに閉じ込めてしまう
  2. ホストディレクトリをコンテナにマウントする
  3. 他のコンテナをデータボリュームとして参照する

といった手法。1のdocker buildで閉じ込める手法はお手軽だが、アプリケーションと特定の証明書が密結合になるのでなるべく避けたい。2のホストからマウントの手法もホストのメンテが入るのでポータビリティに乏しい。

このような用途では3の手法が良いと考えている。Data Volume Containerについては以下のリファレンスを参照すると良いです。

docs.docker.com

要は必要なファイル(やディレクトリ)だけのdockerイメージを作成して、それを必要とするコンテナから参照するし、そのパスを環境変数で与えればいいよねという話。

証明書を閉じ込めただけのdockerイメージ

Dockerfileにするとこんな感じになる。非常に軽量なbusyboxイメージをベースに利用する。

FROM busybox:latest 

RUN mkdir /cert
ADD server.pem /cert
ADD server.key /cert

VOLUME /cert

docker buildするホストから必要なファイルを追加すればいい。ここで追加するファイル名はserver.pemやserver.keyみたいにシンプルな名前にしておいた方がよく、証明書が更新されても同じファイル名でいい。その方が参照するアプリケーション(コンテナ)側で証明書のパスを変える必要がなくなるため。

タグ

証明書には期限があるので、日付で切っておくと良いと思う。発行日だったり、もしくは有効期限にしておくと更新を時期を意識できる。

$ docker build -d stormcat24/testcert:20170519 .
$ docker tag stormcat24/testcert:20170519 your_registry/stormcat24/testcert:20170519

このようにタグを運用しておけば、証明書を変えたい場合は参照するデータボリュームコンテナをすげ替えればいいだけの話になる。

注意事項

証明書という性質上、当然証明書や秘密鍵の漏洩を防止できるようにしておく必要がある。PublicなDockerレジストリに置くなんてのはもってのほかなので、最低限でもPrivateなDockerレジストリを用意して適切なACLを施されている状態という前提が必要。

その他応用

この手法は証明書以外にも色々とテクニックがあって、例えばWebフロントエンドのDockerイメージがあって、そのassetsをその前段に置くWebサーバ(Nginxとか最近だとVarnish)から配信させたいみたいな場合なんかでも使える。かなり前のエントリを参照してみてください。

blog.stormcat.io

第155回天皇賞(春)の写真を撮ってきた

この間、そうだ Go、京都。に行ったエントリ で書いたとおり、天皇賞のついでに京都に行ったわけですがそこで撮った写真を雑に貼ってみる。

f:id:a-yamada:20170513233710j:plain f:id:a-yamada:20170513233718j:plain f:id:a-yamada:20170513233727j:plain f:id:a-yamada:20170513233745j:plain f:id:a-yamada:20170513233749j:plain f:id:a-yamada:20170513233755j:plain f:id:a-yamada:20170513233802j:plain f:id:a-yamada:20170513233813j:plain f:id:a-yamada:20170513233823j:plain f:id:a-yamada:20170513233834j:plain f:id:a-yamada:20170513233846j:plain f:id:a-yamada:20170513233853j:plain

どうやって撮ったか

以前の残材エントリで書いたが、カメラはCanonEOS 7D Mark IIを利用してる。

APS-Cサイズのカメラだが10枚/secの撮影が可能なので、基本的に動的コンテンツを主に被写体としている自分にとっては非常に役に立ってる。

撮影場所は京都競馬場の7Fのi-Seat指定席付近のテラス。JRAの指定席には競馬場によって色々な形式があり、例えば東京競馬場のメモリアルスタンド(S指定席・i-Seat)ではガラス張りになっているので京都のようなテラスは無い。京都競馬場は指定席ながらも、このような写真を取ることができる。肉眼では結構遠いのだが、300mm砲を使えばスタンド前を走る馬をこれくらいの大きさで捉えることができる。

GIレースになるとガチのプロカメラマン勢が開門ダッシュで芝コース前最前列に陣取っているので、自分みたいに写真ガチ勢ではないクラスタにとってはなかなか厳しいものがある。GIでなければそこそこのポジションで写真が撮ることができるので、臨場感ある写真を取りたい人は試してみて欲しい。基本的に直線が長い競馬場の方が競争率は落ちるので、東京・中山・京都・阪神においては中山が一番しんどい。それ以外では新潟や中京は結構写真撮りやすい。新潟は直千のレースがあって、基本的に外ラチに寄って走ってくるので臨場感のある写真が撮りやすいと思う。

ところでレースは

キタサンブラックが連覇を果たしたわけだが、長距離レースとしてはここ20年で1番といっていいくらいの素晴らしいレースだった。淀みのないラップを刻んでなし崩し的に後続の脚を削るという、90年代前半の長距離戦を現代の高速馬場でやるとこうなるという感じ。メジロマックイーンが故障しなかったらキタサンブラックのようなキャリアを進んでいたんでしょうねぇ。

microservices間でデータ変更をReactiveに伝搬させる

microservices構成なものを運用していると、更新頻度が少ないデータなのに別のmicroservicesに都度リクエストをするということがよくあると思う。

例えば、Userのドメインを扱うサービスがいた場合、他のサービスはUserのデータを取得するためにUserサービスをHTTP(S)ないし、何らかのRPC的手段によって取得することになる。以下の図をイメージしてもらえるとよい。

f:id:a-yamada:20170503161220p:plain

UserサービスがUserドメインを担当する層なのでこのようになる。

データの更新頻度が少ないという現実

実際に運用してみると取得対象のデータ更新頻度が少ないというケースがあるので、Userサービスへのリクエストを減らしてキャッシュを活用したくなるところである。microservices間の通信を減らすこともできるし、対象のサービスにとってはデータストアへのクエリを減らすことにも繋がる。

というわけで以下のようにUserデータに依存したサービス毎にキャッシュを持たせる。

f:id:a-yamada:20170503175045p:plain

キャッシュストアについては、Redisやmemcached等を配置するのも良いし、サービスにインメモリに持たせるでも良い。コスト感を考えると後者の方が安上がりだが、サービスを跨いでキャッシュを共有しても良いという方針にするならば前者も選択肢に入ってくる。

キャッシュデータに関しては適切にTTLを設定し、expireした場合には対象にサービスに再度リクエストをしてキャッシュを更新するといった制御になる。

キャッシュを持つ分大きくリクエストを減らすことができるが、この方式の弱点はTTLの長さによってはキャッシュが長く滞留してしまい、キャッシュの更新に時間がかかってしまうということにある。

データ変更をトリガーに、依存サービスに変更データをばらまく

できることならばデータの変更をトリガーに、複数のmicroservicesに散らばっているキャッシュを更新したい。そこで、Server Push型ミドルウェアであるPlasmaを用いる。

github.com

Plasmaの特性については、以前にエントリを参照してほしい。

blog.stormcat.io

PlasmaはPublisherが複数のSubscriberに対して同じイベントデータをばらまくといった用途に向いている。今回の例でいうと、User ServiceがPublisherであり依存サービスがSubscriberとなる。この特性を利用して、Userデータの変更タイミングで依存サービスに対して新しいUserデータを伝搬させ、キャッシュを更新させる。

f:id:a-yamada:20170503183303p:plain

UserサービスはDBと共に、Plasma用RedisにデータをPublishするようにする。各microservicesがUserデータ変更イベントをSubscribeしていれば、データ変更後程なくして更新データをPlasma経由で受け取ることが出来る。受け取り次第キャッシュを更新すれば、キャッシュの良さを活かしつつも"ほぼ即時"に依存サービスへのリクエスト無しでキャッシュを更新することが可能。microservicesを跨いだ、Reactiveなデータ伝搬といった表現ができるかなと思っている。

このようなデータ伝搬の仕組みは、Plasmaのようなものを使わずとも用意せずにも可能ではある。例えば、各microservicesがキャッシュ更新用のエンドポイントを用意しておいて、Publisherがそこにリクエストをするといった形式だが、この場合は自分に依存しているサービスがどこにあるかを把握しておかなくてはならないのでService Discoveryが必要になってしまう。また、相互参照も避けた方がシンプル。

Plasmaを使わずにRedisのPubSubを使っても同じことが実現できる(Plasma-Redis間がそもそもPubSub)が、Plasmaの利点としてはgRPCやSSEでSubscribeが可能な点なので、RedisへのSubscirbe処理を色んなところで書くよりも、Plasmaのprotoから生成するgRPCのインタフェースに頼ったほうがいいし、SSEを利用すればWebフロントエンドからの利用も可能になるので何かと良いと考えている。

まとめ

PubSub型の特性を活かしたMicroservices間のデータ伝搬のパターンはWeb上文献でもちらほら散見される。microservicesは疎結合や障害の局所化を得やすい反面、どうしても通信は増えがちになるのでパフォーマンスをケアした仕組みというのがこれからもっと重要になるなーという感想。