マイナー・マイナー

隠れた名作の発掘が生きがい。

SaaSアプリを構築する前に『The Twelve-Factor App』を知っておけば良かった…


スポンサードリンク

クラウドエンジニアを目指すのであれば『The Twelve-Factor App (12 Factor App)』を知っておかなければ恥をかくんじゃないかな…


『The Twelve-Factor App』はPaaSで有名な企業「Heroku」の人が書いたWebアプリのビルド&デプロイの方法論です。SaaSアプリケーション開発のための原則が12個にまとまっています。内容は下記リンクに詳細に書いてあります。


12factor.net


少し前に『The Twelve-Factor App』を知らずにクラウドサービスを利用したシステムの新規構築を行ったのですが、構築前にこれを知っておけばもっと保守性の高いシステムになったんじゃないかと思います。反省点がいっぱいですよ。


同じ失敗をしないために、ここに『The Twelve-Factor App』の要点と所感をメモします。

I. コードベース

バージョン管理されている1つのコードベースと複数のデプロイ

コードベースとアプリは常に1対1に保つ、そして1つのコードベースは複数の環境(開発、ステージング、本番)にデプロイできる、という管理方針です。ほぅ、と思ったのが次の説明文。

同じコードを共有する複数のアプリケーションは、Twelve-Factorに違反している。その場合の解決策は、共通のコードをライブラリに分解し、そのライブラリを依存関係管理ツールで組み込むようにすることである。


アプリAとアプリBがあり、それらが同じDBを照会する機能があったとします。この場合、アプリA、Bは同じようなDBサービスクラスやDAOを実装する傾向にあるんですよね。こういうクラスは設計段階でライブラリとして利用できるように分離しておく、といのが良さそうです。

II. 依存関係

依存関係を明示的に宣言し分離する

暗黙的な依存関係を作らないという方針です。全ての依存関係はマニフェストファイルで定義し、ライブラリも管理ツールを使います。JavaであればMavenやGradleを使えば良いんじゃないかな?利点としては、新規参画者の環境構築が圧倒的に楽になること。


中にはImageMagickcurlを使うシステムもあると思いますが、「暗黙的な依存関係を作らない」という方針に従うのであれば、次のような強気の対応も必要です。

アプリケーションがシステムツールを必要とするならば、そのツールをアプリケーションに組み込むべきである。

III. 設定

設定を環境変数に格納する

アプリケーションの設定値をコードから分離する。設定値とはデプロイをする環境(開発、ステージング、本番)で違う値のことを言います。この設定値を環境変数に格納する。「これ、当然じゃない?」と思うんですけど、これが出来ていないシステムをいくつか見てきました…


設定値がコードから分離できているかの確認は次の方法で確認できそうです。

アプリケーションがすべての設定をコードの外部に正しく分離できているかどうかの簡単なテストは、認証情報を漏洩させることなく、コードベースを今すぐにでもオープンソースにすることができるかどうかである。

IV. バックエンドサービス

バックエンドサービスをアタッチされたリソースとして扱う

Twelve-Factor Appのデプロイは、アプリケーションのコードに変更を加えることなく、ローカルで管理されるMySQLデータベースをサードパーティに管理されるサービス(Amazon RDSなど)に切り替えることができるべきである。


ローカルで管理するサービスとサードパーティのサービスを区別しない。どちらもアプリケーションにアタッチされたリソースとして扱う。そして、サービスは設定に格納されたアクセス情報で切り替えられるようにする…マイクロサービスの考え方に通じるものがあると思います。


コードとDBはなかなか切り離せない、と思っていましたけど、今はREST API等手軽で標準化されたアクセス手段も充実してきているので、「今使っているDBよりもこっちのDBの方がコストメリットがあるので切り替えよう」みたいな戦略がより気軽にできるようになっていきそうですね。

V. ビルド、リリース、実行

ビルド、リリース、実行の3つのステージを厳密に分離する

  • ビルドステージ…コードリポジトリをビルドと呼ばれる実行可能な塊へと変える変換
  • リリースステージ…ビルドステージで生成されたビルドを受け取り、それをデプロイの現在の設定と結合
  • 実行ステージ…アプリケーションを実行環境の中で実行する


システムの全体的な設計としては次が大事なのではと思いました。

実行ステージはできるだけ可変部分を持たないようにするべきである。なぜなら、アプリケーションの実行を妨げるような問題が起きると、開発者が待機していない真夜中にアプリケーションが壊れる結果になるためである。

VI. プロセス

アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する

Twelve-Factorのプロセスはステートレスかつシェアードナッシングである。永続化する必要のあるすべてのデータは、ステートフルなバックエンドサービス(典型的にはデータベース)に格納しなければならない。

  • ステートレス…サーバーがユーザーのセッション情報を持たない
  • シェアードナッシング…分散コンピューティングにおいて各コンピュータがリソースを共有しない


ユーザーからのリクエストで得た情報を、次のリクエストでアプリサーバーで使用できるように保持させないという方針です。セッションは基本的には頼らないという方針と理解しました。セッション情報が必要なのであれば、ユーザー側にセッション情報を持たせる、もしくはバックエンドサービスに格納するという設計が良さそうです。

VII. ポートバインディング

ポートバインディングを通してサービスを公開する

Twelve-Factor Appは完全に自己完結 し、Webに公開されるサービスを作成するために、コンテナが実行環境にWebサーバーランタイムを注入することを頼りにしない。Webアプリケーションは ポートにバインドすることでHTTPをサービスとして公開し、 そのポートにリクエストが来るのを待つ。


ここはよく分からないところなんですが…Webサーバーは独自のポートで待ち受けておいて、リクエストが来たらそこにルーティングさせるという、という構築方法のことかな?ポートフォワーディングをイメージしています。

VIII. 並行性

プロセスモデルによってスケールアウトする

このプロセスモデルが真価を発揮するのは、スケールアウトが必要になったときである。シェアードナッシングで水平分割可能なTwelve-Factor Appプロセスの性質は、並行性を高める操作が単純かつ確実なものであることを意味する。


プロセスでスケールアウトできる構成とし、並行性を実現します。プロセス間はメモリを共有しないという性質のため、サーバ台数を増やして処理能力を高める構成(水平分割)が容易となります。


Javaだとスレッドで並行処理もできますが、スレッドは同一のメモリ上で処理されるため、並行処理数を増やすにはメモリ容量を増やすといった対応になります(水平分割もできますが)。こういったスケールアップの考えではない、ということかな。


IX. 廃棄容易性

高速な起動とグレースフルシャットダウンで堅牢性を最大化する

Twelve-Factor Appの プロセス は 廃棄容易 である、すなわち即座に起動・終了することができる。 この性質が、素早く柔軟なスケールと、コード や 設定 に対する変更の素早いデプロイを容易にし、本番デプロイの堅牢性を高める。


グレースフルシャットダウン…正常なシャットダウン
堅牢性…データの壊れにくさ


HTTPリクエストの応答時間を短くすることがグレースフルシャットダウンと堅牢性に貢献する、と理解しました。データを安全に退避させつつプロセスを早く終了させることが、シャットダウンを即座に完了させることにつながります。

X. 開発/本番一致

開発、ステージング、本番環境をできるだけ一致させた状態を保つ

伝統的なアプリケーション Twelve-Factor App
デプロイの間隔 数週間 数時間
コードを書く人とデプロイする人 異なる人 同じ人
開発環境と本番環境 異なる できるだけ一致


この方針を実現するために継続的デリバリーサービス(例:AWS CodePipeline)を積極的に利用することが推進されそうです。ワンクリックでコードからビルド&デプロイまでを自動的に行ってくれる強力さを知ったのであれば、もうマニュアルデプロイに戻りたくなくなるはず。

XI. ログ

ログをイベントストリームとして扱う

Twelve-Factor Appはアプリケーションの出力ストリームの送り先やストレージについて一切関知しない。 アプリケーションはログファイルに書き込んだり管理しようとするべきではない。


アプリでは、ログはストリーム処理として書き出し、書き出し先はアプリケーションの外の設定で行う、という方針です。


JavaでいうとLog4j2のイメージですかね。設定ファイルの設定によってログをファイル出力やDB書き出しに振り分けることができたりします。AWSでいうとfluentdがこの方針を実現する強力なツールになります。

XII. 管理プロセス

管理タスクを1回限りのプロセスとして実行する

1回限りの管理プロセスは、アプリケーションの通常の長時間実行されるプロセスと全く同じ環境で実行されるべきである。これらのプロセスは、あるリリースに対して実行され、そのリリースに対して実行されるすべてのプロセスと同じコードベースと設定を使う。


各リリースで1回しか実行されないコード、例えばマイグレーションや初期データ作成に使用されるコードも同じコードベースで管理すべきという方針です。


大きな利点としてはコードの同期が取りやすい(バージョン管理がしやすい)というのがあると思います。1回しか実行されないコードもバージョンアップすることがあるため、外で管理してコードベースとのバージョン管理表を作るよりも楽になりそうです。