12 Factor App
トゥエルブファクターアップ
クラウドで運用しやすいアプリの12の設計原則。Heroku発、コンテナ時代の共通前提に。
概要
12 Factor App(Twelve-Factor App)は、クラウド上で運用しやすいWebアプリケーションを作るための12の設計原則をまとめた方法論です。「設定は環境変数に置く」「ログはファイルに書かずストリームとして流す」「プロセスはステートレスにする」といった、現代の開発者が半ば常識として従っているルールの多くは、この文書が出典です。
2011年、PaaS(Platform as a Service)の草分けであるHerokuの共同創業者アダム・ウィギンスが、同社が数十万のアプリをホストして得た知見を公開文書としてまとめたのが始まりです。原則はいずれも特定の言語やフレームワークに依存せず、「アプリケーションとそれを動かす実行環境の間の契約をどう設計するか」という一段抽象的な問いに答えています。だからこそ登場から10年以上経ったコンテナ・Kubernetes時代にも通用し、クラウドネイティブなアプリ設計の共通言語であり続けています。
12個の項目を暗記する必要はありません。貫かれている思想はひとつで、「アプリを実行環境から切り離し、いつでも捨てて作り直せる部品にする」ことです。この視点で束ね直すと、12の項目は自然に少数のグループに整理できます。
なぜ生まれたか
2000年代のWebアプリの典型的な運用は、長年育てた特定のサーバに手作業でデプロイし、設定ファイルはサーバ上で直接編集し、ログはローカルのファイルに溜め、セッションはプロセスのメモリに持つ、というものでした。サーバは一台ごとに微妙に構成の違う「手塩にかけたペット」になり、開発環境と本番環境は乖離し、「自分のマシンでは動くのに本番で動かない」が日常でした。増強はサーバのスペックを上げる垂直スケーリング頼みで、台数を増やして分散する水平スケーリングは、状態がサーバに染みついているせいで容易ではありませんでした。
Herokuのような PaaS は「コードを push すれば勝手に動く」体験を売りにしましたが、それにはアプリ側が一定の作法を守っている必要があります。実行環境がいつでもプロセスを別のマシンで立ち上げ直せるように、アプリは環境に何かを染み込ませてはならない — この「プラットフォームが自由に動かせるアプリの条件」を、顧客アプリの成功例と失敗例から帰納してまとめたのが12 Factorです。つまりこれは机上の理論ではなく、大量のアプリを預かったホスティング事業者による運用知の結晶です。
詳細
12の項目を、思想のまとまりごとに4つのグループに束ねて見ていきます。
コードと依存を一意にする — I・II・X
土台になるのは「同じものをどこでも再現できる」ことです。**コードベース(I)**は、1つのアプリはGitなどで管理された1つのリポジトリに対応し、そこから開発・ステージング・本番という複数のデプロイが生まれる、という関係を定めます。**依存関係(II)**は、必要なライブラリをすべてマニフェスト(package.json や go.mod など)で明示的に宣言し、「実行環境にたまたま入っているもの」に暗黙に頼らないこと。**開発/本番一致(X)**は、開発環境と本番環境の乖離 — 時間(デプロイまでの日数)、人(書く人と運用する人)、ツール(ローカルはSQLite・本番はRDBMSのような差) — を可能な限り小さく保つことです。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パイプラインの設計原則そのものです。**管理プロセス(XII)**は、データベースのマイグレーションや一回きりのスクリプトも、アプリと同じコードベース・同じ依存・同じ設定のもとで、通常のプロセスと同じ環境で実行することを求めます。「管理作業だけ別の環境から手作業で」を許すと、せっかく一方向にした流れに抜け道ができてしまうからです。
コンテナ・クラウドネイティブ時代の再解釈
12 Factorが書かれたのはDockerの登場(2013年)より前ですが、その後の技術はむしろこの方法論の実装として読めます。依存関係の宣言と開発/本番一致はコンテナイメージが強制的に実現し、ビルド・リリース・実行の分離は「イミュータブルなイメージ+環境変数の注入」という形で標準になりました。廃棄容易性とポートバインディングとステートレスなプロセスは、KubernetesがPodを自由に再配置・増減するための前提条件そのものです。サーバレスに至っては、プロセスの起動と廃棄を極限まで細かくした12 Factorの徹底形と言えます。また、サービスをバックエンドサービスとして疎結合につなぐ発想は、マイクロサービスアーキテクチャの各サービスの設計指針としてそのまま流用されています。
一方で、書かれた時代の制約による古さも指摘されています。たとえば環境変数による設定は、シークレット管理サービスやIaCツールが成熟した現在ではより安全な注入手段があり、ログやメトリクスの扱いも分散システムを前提とした分散トレーシングまで視野に入れると12 Factorの記述では足りません。それでも「アプリと実行環境の契約を明確にし、環境に状態を染み込ませない」という中心思想は色あせておらず、新しいプラットフォームが現れるたびに参照され続ける、クラウド時代の古典です。