もりはやメモφ(・ω・ )

インフラなエンジニアからSREへ

マイクロサービスアーキテクチャ本の感想(6章)

お正月の長期連休が遥か昔のように感じていますが、ぼちぼちやっていきましょう。さて O'Reillyのマイクロサービスアーキテクチャ本感想の第6回です。

6章「デプロイ」を読んで

6章は以前から興味はあったけれど実践はできていない"CI/CD"について語られています。

CIって何よ

継続的インテグレーション(Continuous Integration)とは、すなわち「すべてを互いに常に同期させることであり、実際には新たにチェックインされたコードが既存コードと適切に統合(インテグレーション)されるようにすること」とあります。要するにgit commitしたら適切にマージされるってことだと理解しました。ポイントは「適切に統合」というところで、コードコンパイルやテストが自動で行われ、高速なフィードバックを得られる状態であることを指します。

あなたがCIを行えているかをチェックしよう

Jez Humble氏の質問を引用し、本当にCIがチェックできているかを振り返ってみよう!のコーナー

  • 1日に一度はメインラインにチェックインしているか?
  • 変更を検証するテストスイートがあるか?
  • ビルドが壊れたとき、それを修正するのがチームの最優先事項か?

上記を全て満たしていなければそれは適切なCIを行えていない状態だぞと、具体的な記述こそありませんが、文脈から伝わってきます。

マイクロサービスにふさわしいCIビルド

モノリシックな1レポジトリ&1パイプラインではなく、マイクロサービス単位でレポジトリとビルドパイプラインが用意されている状態が理想であるとしています。

継続的デップローイ!!(CD)

CIに続いてCDの話です。継続的デリバリ(Continuous Delivery)はビルドパイプラインを段階的なステージにモデル化し、各種テストやビルドを経て本番環境へ自信を持ってデプロイするための概念であるとしています。※詳しくは継続的デリバリー 一般的な例として以下が挙げられています。

  1. コンパイル&高速テスト
  2. 低速テスト
  3. UAT(User Acceptance Testing)=ユーザ受け入れテスト
  4. 性能テスト
  5. 本番環境

例外は避けられない(いきなり理想のCI/CDなんて無理だよ)

初期の開発時は無理にサービスごとのパイプラインを作らず、多少モノリシックでも運用がしやすいCDでOKという話。 理由としては3章のSnapCIが例であったように、初期のアプリケーションはBC(コンテキスト境界)が変動しやすいため。

固有の成果物について

  • RubyのGem、JavaのJARやWARなど、各種プログラミング言語のパッケージマネージャを使うと便利ではあるが、サービスごとに技術スタックがバラバラだとデプロイ環境を管理することが大変になる
  • OSのRPMdebMSIなどを作成すればデプロイ管理は楽になるが、パッケージ作成の難易度が高いのが大変だ
    • 特にWindowsMSIが大変だが、Chocolatey NuGetが状況を変えるかもしれない
    • できればOSレイヤのパッケージでデプロイができれば、管理はシンプルになるだろう(そりゃそうだ)

カスタムイメージでデプロイを楽にしよう

Ansible(Chef,Puppet)などの構成管理ツールを使う際の問題として実行に時間がかかるという点があります。対策としてOSをある程度ビルドしてイメージを準備しておきましょうという話。

  • 第一段階としてはサービスを入れるだけの状態のOSイメージを用意
  • 第二段階としてはサービスも入っているOSイメージを用意
  • 加えてイミュータブルサーバという概念を実施する
    • あらゆる変更は必ずビルドパイプラインを通して行うということ
    • sshを無効化してしまう(なんて恐ろしいと思ってしまうけど)

ステージごとの環境差異はバランスを考えること

次章のテストに繋がる話として、ビルドパイプラインのステージごとに環境が異なり、適切なバランスを取りましょうという話。 例として本番は複数台でレプリケーションを行っているが、ソフトウェアライセンスの理由で開発環境はシングル構成だったため、レプリケーションに影響が出るテストがスルーされてしまって大惨事となったことが紹介されています。

  • 本番に近い環境にすることで
    • 費用が上がる(ソフトウェアライセンスやサーバ費)
    • デプロイの時間がかかる(台数が増える)
  • 本番とは異なる環境にすることで
    • 費用削減
    • デプロイ時間短縮

という天秤のバランスを上手く取る必要があります。 これは私にも経験がありまして、CentOS6のTHPというメモリ管理の仕組みが本番の大規模なメモリを積んだホストではCPUを完全に奪うという事象にやられたことがあります。開発環境の少ないメモリでは全く問題が起きなかっただけに、環境差異がある状況での発見は難しかったんですよね。。

ビルドは1回、環境差異は構成で吸収する

各環境毎にビルドするのはナンセンス。1回のビルドでテスト、ステージ、本番環境の全てを通すべきで、各環境の際は構成を別に管理することで吸収するのがオススメとのこと。 加えて機密情報をビルドに加えるのもアンチパターンであり、こちらも構成管理で吸収するべきとしています。 具体的には以下のような方法があるとのことです。

  • 環境ごとのプロパティファイル
  • インストールプロセスに渡すパラメタ

個人的にはGo言語製のOSSとか起動時のパラメタでほぼ全ての設定が完結するイメージがあります。(Consulとかtraefikとか)

サービスのホストへのマッピング

この節ではサービスをホストへどのようにマッピングするかを取り上げています。 1サービス/1ホストでは物理と変わりがありませんし、多すぎても耐障害性が。。ということで、以下のパターンの良し悪しが挙げられています。

複数サービス/ホスト

1つのホストに複数のサービスを展開する手法。

  • メリット
    • ホストを管理するというシンプルさがある
  • デメリット
    • 仮想化によるオーバーヘッド
    • CPUなどのリソースがシェアされるため、リソース監視が難しい

アプリケーションコンテナ

複数サービスを1つのアプリケーションコンテナとしてパッケージしてホストに展開する手法。

  • メリット
    • 言語ランタイムのオーバヘッドが削減できる(Javaサーブレットコンテナ内で複数のJavaサービスが起動)
  • デメリット
    • 技術選択が限定されてしまう

1サービス/1ホスト

読んで字のごとく1ホスト上に1つしかサービスを展開しない手法。 著者はこの構成こそ目指すべきものだとしています。

  • メリット
    • 構成がわかりやすくシンプルになる
    • SPOFの削減
    • デプロイがやりやすい(イメージベースデプロイ、イミュータブルサーバパターン)
  • デメリット
    • コストがかかる

ここで言うホストとは物理的な物を指すのではなく、VMでも良いです。とにかく1:1の関係でサービスとホストを構築することで、 マイクロサービスの疎結合の考え方を適用しろと理解しました。

PasSについても

最上級のPaaSとしてHerokuが挙げられており、その可能性と有用性を評価する一方で以下のような厳しいコメントでした。 とはいえ本書が執筆された2015年から3年がたった2018年の今では、状況が変わっているのかもしれません。 (PCS[Pivotal Container Service]とかIBM Cloud privateとか、色々出てきてはいますしね)

  • 使用できる技術が限定される(GAEがruby使えないとかそういう話)
  • 標準的なアプリケーションに最適化されがち
  • 内部の細かい制御(チューニング)ができない
  • ホスト型(herokuのような)が主流で、オンプレに展開するPaaSはまだまだ発展途上だ

自動化!!自動化!!自動化!!!

本章ではこれが一番言いたかったんじゃないかと思います。 自動化によりサービスのスケールにオペレーション工数が比例しない状況を作るべきで、 そのための最適な技術を選択して利用する必要があるとのこと。

また、初期は自動化のための作り込みが発生するのは仕方ない(RealEstate.com.auの事例も挙げつつ)が、 後になって確実に効いてくるから頑張れとエールをもらいました(気がします)。

デプロイ先の環境の変化(物理 -> 仮想)

デプロイ先の仮想化技術の選択肢も挙げられています。

  • 仮想化
  • コンテナ
    • Linuxコンテナ
    • Dockerコンテナ

デプロイはコマンド化しろ

節のタイトルは「デプロイのインターフェース」で、あらゆるプラットフォームに共通して必要なデプロイ方法が提示されます。 それは以下の3点を含むインターフェースであることです。

  1. 環境に依存しない統一されたインターフェースを用意し、かつ環境を指定できること
  2. サービス名を指定できること
  3. バージョンを明確に指定できること

上記を考慮すると必然的に以下の様なコマンドになるはずとしています。

deploy service=<サービス名> env=<環境名> ver=<バージョン番号>
※簡略化してます。そのまま引用ではありません。

さらに面白いのが、開発者であれば自分の環境へデプロイするだろうから以下のようになり、

deploy service=<サービス名> env=local ver=local

開発者がコミットすればCIツールが以下のようにバージョンを振り、

deploy service=<サービス名> env=ci ver=1.2.34

QAチームであれば常に最新のビルドをテストしたいから以下のようになるといった、具体的な説明もしています。

deploy service=<サービス名> env=qa ver=latest

環境定義は外に切り出せ

デプロイ方法を記載したコードと、各環境を定義したパラメタは別に管理するべしとあり、Terraformへの期待が記載されています。 HashicorpはVaultという機密情報管理のサービスも行っており、著者が期待したサービスの展開を行っているのだなぁとしみじみ思いました。

具体的には以下のような差異を管理できるようにします。

  • 本番と開発環境ではマシン性能を変える
  • 本番と開発環境では認証情報を変える
  • 本番と開発環境ではスケールを変える

6章の感想

デプロイを従来の手作業でハートフルな方法でやるのは極めてナンセンスであるというのがよくわかり、読みながら胸が痛みました(現状がそうではないのでorz)。 CI/CD環境を初期投資を惜しまずに構築し、自動化を進め、デプロイの速度・制度・開発側への移譲(セルフデプロイ)を高める必要がありますし、実現に向けたモチベーションと作業イメージを得ることができました。 また、パイプラインの粒度も可能な限り小さく、サービス単位とすべきであるという文を読み、本書が掲げるマイクロサービスという言葉の具体的なイメージを得ることができたとも感じています。(この章まではどちらかというと抽象的な話が多い印象でした)