アーキテクチャ●●●○○

12 Factor App

The Twelve-Factor App ・ とぅえるぶふぁくたーあっぷ

クラウドで運用しやすいアプリの12の設計原則。Heroku 発で、コンテナ時代の共通の前提になった。

✦ 30秒で試す — 航海よく動くサーバほど、明日まったく同じものは作れないログインした利用者が操作を続けると、どうなる?予想してみる

概要

12 Factor App(The Twelve-Factor App)は、クラウドコンピューティングの上で運用しやすい Web アプリケーションを作るための12の設計原則をまとめた方法論です。「設定は環境変数に置く」「ログはファイルに書かずストリームとして流す」「プロセスはステートレスにする」といった、現代の開発者が半ば常識として従っているルールの多くは、この文書が出典です。

2011年、PaaS(Platform as a Service)の草分けである Heroku の共同創業者 Adam Wiggins が、同社が大量のアプリをホストして得た知見を公開文書としてまとめたのが始まりです。原則はいずれも特定の言語やフレームワークに依存せず、「アプリケーションと、それを動かす実行環境の間の約束をどう設計するか」という一段抽象的な問いに答えています。だからこそ登場から10年以上たったコンテナと Kubernetes の時代にも通用し、クラウドネイティブなアプリ設計の共通言語であり続けています。

12個の項目を暗記する必要はありません。貫かれている思想はひとつで、「アプリを実行環境から切り離し、いつでも捨てて作り直せる部品にする」ことです。この視点で束ね直すと、12の項目は自然に4つのグループに整理できます。

なぜ生まれたか

2000年代の Web アプリの典型的な運用は、長年育てた特定のサーバに手作業でデプロイし、設定ファイルはサーバ上で直接編集し、ログはローカルのファイルに溜め、セッションはプロセスのメモリに持つ、というものでした。サーバは一台ごとに微妙に構成の違う「手塩にかけたペット」になり、開発環境と本番環境は食い違い、「自分のマシンでは動くのに本番で動かない」が日常でした。増強はサーバのスペックを上げる垂直のスケーリング頼みで、台数を増やして分担する水平スケーリングは、状態がサーバに染みついているせいで容易ではありませんでした。

Heroku のような PaaS は「コードを push すれば勝手に動く」体験を売りにしましたが、それにはアプリ側が一定の作法を守っている必要があります。プラットフォームは、機械の故障や保守のたびに、アプリのプロセスを予告なく別の機械で立ち上げ直します。そのときローカルディスクに書いたファイルやメモリ上のセッションは消えます。アプリは環境に何かを染み込ませてはならない。この「プラットフォームが自由に動かせるアプリの条件」を、顧客アプリの成功例と失敗例から帰納してまとめたのが12 Factor です。机上の理論ではなく、大量のアプリを預かったホスティング事業者による運用知の結晶です。

手作業で育てたサーバサーバAOS 7.2・手作業パッチ入りサーバBOS 7.4・設定が微妙に違うサーバC直接編集の履歴が誰にも不明1台ごとに構成が違い再現も移設もできない12 Factor のアプリ同一のコードベースビルドプロセスプロセスプロセス全部同一・ステートレス・使い捨て環境変数で渡す設定環境ごとの違いは外から注入し、どこでも何個でも起動できる
手作業で育ったスノーフレークサーバと、同じコードベースから複製される12 Factorのプロセス

詳細

12の項目を、思想のまとまりごとに4つのグループに束ねて見ていきます。金の枠の2つ(III 設定、VI プロセス)が、それぞれのグループの要になる項目です。

コードと依存を一意にするI コードベースII 依存関係X 開発/本番一致どこでも同じものが再現できる環境との結合を切るIII 設定IV バックエンドサービスVII ポートバインディングXI ログ環境ごとの違いは外から注入するプロセスを使い捨てにするVI プロセスVIII 並行性IX 廃棄容易性状態を持たず、いつでも増減・再起動流れを一方向にするV ビルド・リリース・実行XII 管理プロセス変更は必ず同じ経路を一方向に流す
12の要素を4つの関心事に束ねた全体像

コードと依存を一意にする — I・II・X

土台になるのは「同じものをどこでも再現できる」ことです。**コードベース(I)**は、1つのアプリは Git などで管理された1つのリポジトリに対応し、そこから開発・ステージング・本番という複数のデプロイが生まれる、という関係を定めます。**依存関係(II)**は、必要なライブラリをすべてマニフェスト(package.json や go.mod など)で明示的に宣言し、「実行環境にたまたま入っているもの」に暗黙に頼らないこと。**開発/本番一致(X)**は、開発環境と本番環境の隔たりを、時間(書いてからデプロイまでの日数)、人(書く人と運用する人)、ツール(ローカルは SQLite・本番は RDBMS のような差)の3つの面で可能な限り小さく保つことです。3つ合わせて、「あるコミットを指させば、動くものが一意に決まる」状態を作ります。

環境との結合を切る — III・IV・VII・XI

次のグループは「環境ごとに違うものは、コードの外から注入する」という原則です。**設定(III)**は最も引用される項目で、データベースの接続先や認証情報など、デプロイごとに変わる値はコードや設定ファイルに埋め込まず、環境変数で渡します。コードと設定を分離できているかの判定基準は「いまコードを公開リポジトリに晒しても、認証情報が漏れないか」です。**バックエンドサービス(IV)**は、RDBMS・キャッシュ・メッセージキュー・外部 API などをすべて「URL(と認証情報)で参照する、取り付け・取り外しのできる資源」として扱い、ローカルの DB と外部の SaaS をコード変更なしで差し替えられるようにします。**ポートバインディング(VII)**は、アプリが Web サーバの機能を自分で持ち、ポート番号を開いて待ち受ける形にすること(外から用意されたアプリケーションサーバに乗り込む形にしない)。**ログ(XI)**は、アプリはログの保存先や転送に関与せず、標準出力にイベントの流れとして書き出すだけにして、収集・集約・保管は実行環境の仕事にする、という分担です。これは現代の可観測性の基盤の入口の設計そのものです。

プロセスを使い捨てにする — VI・VIII・IX

3つ目のグループが、水平スケーリングを可能にする心臓部です。**プロセス(VI)**は、アプリを1つ以上のステートレスなプロセスとして実行し、セッションのデータや永続化すべき状態は、プロセスのメモリやローカルディスクではなくバックエンドサービスに置くこと。これができていれば、**並行性(VIII)**が言うように、負荷が増えたらプロセスの数を増やすだけでスケーリングでき、ロードバランサがどのプロセスにリクエストを送っても同じ結果になります。**廃棄容易性(IX)**は、プロセスが数秒で起動し、いつ SIGTERM(終了の合図)で止められても壊れない、つまり処理中の仕事を片付けてから終われる(グレースフルシャットダウン)ことを求めます。プロセスが「大切に延命する対象」ではなく「いつでも捨てて増やせる家畜」になることで、デプロイも障害からの復旧もオートスケールも、「プロセスを入れ替えるだけ」の操作に単純化されます。

流れを一方向にする — V・XII

最後のグループは変更の経路です。**ビルド・リリース・実行(V)**は、コードから実行物を作る「ビルド」、それに設定を結合する「リリース」、それを起動する「実行」の3段階を厳密に分離し、実行中の環境に手を入れて直す(本番サーバでコードを書き換える)ことを禁じます。すべての変更はビルドからやり直して一方向に流す。これは CI/CD のパイプラインの設計原則そのものです。リリースには番号が付き、問題があれば1つ前のリリースに戻せます。**管理プロセス(XII)**は、データベースのスキーママイグレーションや一回きりのスクリプトも、アプリと同じコードベース・同じ依存・同じ設定のもとで、通常のプロセスと同じ環境で実行することを求めます。「管理作業だけ別の環境から手作業で」を許すと、せっかく一方向にした流れに抜け道ができてしまうからです。

コンテナ・クラウドネイティブ時代の再解釈

12 Factor が書かれたのは Docker の登場(2013年)より前ですが、その後の技術はむしろこの方法論の実装として読めます。依存関係の宣言と開発/本番一致はコンテナのイメージが強制的に実現し、ビルド・リリース・実行の分離は「変更しないイメージ+環境変数の注入」という形で標準になりました。廃棄容易性とポートバインディングとステートレスなプロセスは、Kubernetes が Pod を自由に再配置・増減するための前提条件そのものです。サーバレスに至っては、プロセスの起動と廃棄を極限まで細かくした12 Factor の徹底形と言えます。また、サービス同士をバックエンドサービスとして疎結合につなぐ発想は、マイクロサービスの各サービスの設計指針としてそのまま使われています。

一方で、書かれた時代の制約による古さも指摘されています。環境変数による設定は、プロセスの一覧や障害時のダンプから値が漏れやすく、いまではシークレット管理のサービスや IaC のツールを通じて、より安全に注入する手段が整っています。ログの扱いも、メトリクスや、多数のサービスにまたがる分散システムを前提とした分散トレーシングまで視野に入れると、12 Factor の記述では足りません。こうした不足を補う試みとして、項目を足した「Beyond the Twelve-Factor App」(Kevin Hoffman、2016年)のような文書も書かれています。それでも「アプリと実行環境の約束を明確にし、環境に状態を染み込ませない」という中心思想は色あせておらず、新しいプラットフォームが現れるたびに参照され続ける、クラウド時代の古典です。

試問

クラウドの上で、同じアプリのプロセスを何個でも立ち上げ、いつでも捨てて作り直せるようにしたいチームがいます。チームの運用のやり方が8つ並んでいます。それぞれが「どこでも同じものを再現でき、いつ捨てても困らない」という原則に沿っているかを見分けます。

✦ 命題挑戦中

8つの運用のやり方を、使い捨てにできるアプリの原則に沿うものと、反するものに仕分けよ

原則に沿うどこでも再現でき、捨てても困らない
原則に反する特定の機械や環境に縛られる

項目を選んでから、入れる箱を選びます