Beyond the Twelve-Factor Appとは
元の「The Twelve-Factor App」を現代のクラウドネイティブな環境に合わせて拡張・発展させたベストプラクティスである。
目的
クラウドコンピューティング時代においてアプリケーションを効率的に開発・運用するための基盤となり、アプリケーションの課題を解決すること。
要素
- One codebase, one application(1 コードベース、1 アプリケーション)
- API first(API ファースト)
- Dependency management(依存関係管理)
- Design, build, release, and run(デザイン、ビルド、リリース、実行)
- Configuration, credentials, and code(設定、機密情報、コード)
- Logs(ログ)
- Disposability(廃棄容易性)
- Backing services(バックエンドサービス)
- Environment parity(環境一致)
- Administrative processes(管理プロセス)
- Port binding(ポートバインディング)
- Stateless processes(ステートレスプロセス)
- Concurrency(並行性)
- Telemetry(テレメトリ)
- Authentication and authorization(認証/認可)
One codebase, one application(1 コードベース、1 アプリケーション)
意味
コードベース(リポジトリ)とアプリケーションを1対1にする。
アプリケーションとデプロイ先は1対N(Production, Staging, Development)とする。
目的
管理の一貫性と明瞭性を保つこと。
API first(API ファースト)
意味
アプリケーション開発の際に、まずAPIを先に設計・定義し、そのAPIを基盤として他のシステムやユーザーインターフェース(Web・モバイルアプリなど)を構築していく考え方。
目的
- 疎結合なシステムの実現
- APIを介してやり取りすることで、フロントエンドとバックエンド、社内・社外システム間の依存を減らし、柔軟に開発・拡張できるようにする。
- 再利用性の向上
- 一度作ったAPIを様々なクライアント(Web、モバイル、他サービス)から利用できる。
- 自動化・外部連携の促進
- 明確なAPI仕様があることで、自動テストやAPIドキュメント生成、他社サービスとの連携が容易になる。
手法/ベストプラクティス
- API仕様の先行設計
- OpenAPI(Swagger)などを使い、実装前にAPIの仕様書を作成する。
- モックAPIを先に用意し、フロントエンド・バックエンドを並行して開発可能にする。
- OpenAPI定義 → ドキュメント自動生成 → 仕様に基づく並行開発
- バックエンドでは、バリデーション・型安全化、スタブ/実装コード生成、自動テスト・モックサーバー、API Gatewayや外部サービス設定に使用するなどの活用方法がある。
- コードと同期する形でSwagger UIなどを利用しAPIドキュメントを自動生成する。
- RESTful/GraphQLなど標準的な設計パターンの採用
- RESTやGraphQLなど、広く認知されたAPI設計指針を踏襲する。
- バージョニングの導入
- 既存APIへの影響を最小限に抑えるため、バージョン管理を導入する(例:
/v1/、/v2/など)。
- 既存APIへの影響を最小限に抑えるため、バージョン管理を導入する(例:
- 認証・認可の設計をAPI単位で行う
- OAuth2やJWTなどのモダンな認証・認可方式を導入し、APIセキュリティを確保する。
- テストの自動化
- API単体テスト・統合テストを自動化し、品質を担保する。
- エラーハンドリングと一貫したレスポンス設計
- エラーレスポンスや成功時のレスポンス構造を統一し、利用しやすくする。
Dependency management(依存関係管理)
意味
アプリケーションが利用する外部ライブラリやパッケージ(例:gem、npmパッケージ、pipパッケージなど)を明示的に定義・管理する。
目的
- 再現性のある環境構築
誰が、どこでアプリケーションをデプロイ・実行しても、同じ依存関係で同じ環境が再現できるようにする。 - 安全性・保守性の向上
依存ライブラリのバージョンや更新履歴を明確にし、セキュリティリスクやバグの混入を防ぐ。 - トラブルシューティングの容易化
依存関係が明示的であれば、問題発生時に特定や修正がしやすい。 - セットアップの単純化
- 特定環境で暗黙的にインストールされていたパッケージによって動いていた、という事態を避けられる。
手法/ベストプラクティス
- 依存関係を明示的に宣言する
- Rubyなら
Gemfile、Node.jsならpackage.json、Pythonならrequirements.txtなどを使う。
- Rubyなら
- 依存関係のバージョンを固定・管理する
Gemfile.lock、package-lock.json、Pipfile.lockなどのlockファイルをリポジトリに含める。
- 依存関係のアップデート・セキュリティチェックを定期的に行う
- 依存パッケージの脆弱性チェックツール(Dependabot、npm audit、bundler-auditなど)を活用する。
- CI/CDで依存関係のインストール・検証プロセスを自動化する
- ビルド・テスト時に必ず依存関係をクリーンな環境でインストールし、動作確認を行う。
Design, build, release, and run(デザイン、ビルド、リリース、実行)
意味
開発から運用までの流れを、「設計、ビルド、リリース、実行」の4ステップに分離し、それぞれ独立して実行する。
目的
- ライフサイクルの分離による運用の効率化
各フェーズを明確に分けることで、責任範囲や作業内容を整理しやすくなる。 - 再現性の向上
ビルドとリリースを分離することで、同じビルド成果物を使いまわして異なる環境でのリリースやデプロイが容易になる。 - トラブル時の切り分けがしやすい
問題がどのフェーズで発生したのか特定しやすくなる。 - 自動化・CI/CDとの親和性
各フェーズごとに自動化しやすくなり、継続的デリバリーの実現にも繋がる。
手法/ベストプラクティス
- ビルドとリリース、実行を明確に分離する
- アプリのビルド工程(例:Dockerイメージの作成)と、リリース(設定や機密情報の注入)、実行(本番環境などでの起動)を別々のプロセス・タイミングで行う
- ビルド成果物はリリースごとに再利用する
- 一度ビルドした成果物(バイナリやイメージ)は、複数の環境やリリースで使い回す(再ビルドしない)
- リリースには「ビルド成果物+設定・機密情報」を組み合わせる
- 設定や機密情報(環境変数など)はビルド時ではなく、リリース時・実行時に注入する(コードにハードコーディングしない)
- リリースごとに一意のバージョンや識別子を付与する
- どのリリースがどのビルド・設定でできているか追跡できるようにする
- リリースのロールバックや再デプロイが簡単にできる仕組みを整える
- 以前のリリースに素早く戻せるようにしておく
- CI/CDパイプラインの導入
- ビルド、リリース、デプロイを自動化し、ヒューマンエラーを減らす
- 本番・検証・開発など各環境で同じビルド成果物を使う
- 環境ごとの差異によるバグを減らす
Configuration, credentials, and code(設定、機密情報、コード)
意味
環境ごとの設定や機密情報(パスワードなど)を、コードとは別の場所に保存する。
The Twelve-Factor Appでは、設定とコードを分離する手法として、環境変数の紹介がされていたが、Beyond the Twelve-Factor Appでは、設定を外部化するプラクティスが追加されている。
目的
- セキュリティ向上
機密情報や設定値をコードから分離することで、情報漏洩や不正アクセスのリスクを低減する。 - 柔軟な運用
デプロイ先ごとに設定や機密情報を容易に切り替えられるため、本番・開発・テスト環境を簡単に切り替えられる。 - 再利用性と保守性の向上
コードの変更なしで設定や機密情報を更新できるため、運用が容易になる。 - 一貫したデプロイ
同じコードベースでも、設定や機密情報を切り替えることで多様な環境に対応可能になる。
手法/ベストプラクティス
- AWS Systems Manager Parameter StoreやAWS Secrets Managerなどの外部シークレット管理サービスを活用する
→ 一般的な設定値や暗号化不要なものはParameter Store、パスワードやAPIキーなどの機密性が高いものはSecrets Managerなどを利用する。 - Infrastructure as Code(IaC)やCI/CDと連携させる
→ デプロイ時に必要な設定値や機密情報を自動で注入する仕組みを導入する。 - シークレットスキャンや監査の仕組みを導入する
→ 誤って機密情報をコードリポジトリに含めてしまった場合に検知する。
Logs(ログ)
意味
ログをイベントのストリームとして扱い、標準出力に出力する。
目的
ログをアプリケーションから独立して扱うことで、「ログの収集・保存・解析・転送」といった運用の柔軟性・拡張性が高まる。
手法/ベストプラクティス
- ログは標準出力(stdout)/標準エラー出力(stderr)に書く
→ アプリケーションが直接ファイルに書き込まず、プラットフォームや運用基盤側でログの収集・保存・転送を行う - ログの形式はシンプルに、できれば構造化する
例: JSON形式で出すとログ解析や検索が容易になる - ログのローテーションや保存期間管理はアプリケーション外部で行う
→ ログ管理はFluentd、Logstash、CloudWatch、Elasticsearch、Datadogなどの専用ツールやクラウドサービスに任せる - ログレベル(info、warn、errorなど)の統一と適切な運用
→ 不要なログを減らし、必要な情報だけを出力する - セキュリティ・プライバシーへの配慮
→ 個人情報や機密情報はログに残さない
Disposability(廃棄容易性)
意味
アプリケーションのプロセスが「素早く起動・正常にシャットダウン(graceful shutdown)できる」こと、そして「いつでも安全に破棄できる」性質のこと。
プロセスのライフサイクル管理が柔軟であり、障害時やスケール調整時にもスムーズにリソースの追加・削除ができる状態を目指す。
graceful shutdown: システムやアプリケーションを停止させる際に、現在の処理やタスクを可能な限り安全に完了させ、リソースの解放を適切に行いながら停止する一連の流れ。
目的
アプリケーションの可用性・スケーラビリティ・運用性を高めること。
- システム障害時やデプロイ時に、素早く安全にプロセスを再起動・再配置できる
- オートスケーリング環境やクラウドネイティブな基盤で、動的なプロセス増減に対応できる
- ロールバックやデプロイの失敗からも素早く復旧できる
手法/ベストプラクティス
- 素早い起動
アプリケーションは数秒以内に起動できるように設計する。 - 正常なシャットダウン処理
SIGTERMなどのシグナルを受け取ったとき、未処理のリクエストやジョブを安全に終了させる。 - ステートレスな設計
プロセス内部に状態を持たないことで、いつでも破棄しやすくする(セッション情報は外部に)。 - キューイングシステムの活用
ジョブキューやメッセージキューを利用し、プロセス停止時にジョブを安全に他プロセスへ引き継げるようにする。 - 短命なプロセスを前提とした運用
プロセスはいつでも終了・再起動される可能性を前提に、設計・運用する。 - 起動や終了時の副作用を最小限に
プロセス起動・終了時に重いバッチや外部サービス連携を行わない。
Backing services(バックエンドサービス)
意味
データベースやキャッシュなどのサービスを、外部の「アタッチ可能なリソース」として扱う。
具体例として、以下が存在する。
- データベース(PostgreSQL, MySQLなど)
- メッセージキュー(RabbitMQ, Amazon SQSなど)
- キャッシュストア(Redis, Memcachedなど)
- 外部API
- SMTPメール配信サービス
- ストレージサービス(Amazon S3など)
目的
アプリケーションの移植性・柔軟性・スケーラビリティを高めること。
- サービス間の依存を疎結合にし、開発・運用・スケールを容易にする
- 本番環境・開発環境・テスト環境でサービス構成を簡単に切り替えられる
- サービス障害時のリカバリやリプレース、拡張がしやすくなる
手法/ベストプラクティス
- サービスのURLやクレデンシャルを環境変数で管理
→ コード中に接続情報をハードコーディングせず、DATABASE_URLやREDIS_URLなどの環境変数で設定 - 本番・開発・テストでサービスを簡単に差し替え可能にする
→ 例えば、本番はAWS S3、開発はローカルファイルシステム、テストはモックサービスを利用 - すべてのバックエンドサービスを「アタッチ可能なリソース」として扱う
→ データベースやメールサービスも「外部サービス」とみなし、アプリと独立して管理・運用する - サービスの追加・削除・切替を再デプロイなしで行う
→ 依存サービスの切り替え時にアプリの再構築やデプロイを必要としない設計 - サービス障害時に備えたリトライやサーキットブレーカーの実装
→ 外部サービスの一時的な障害にも耐えられる堅牢な設計
Environment parity(環境一致)
意味
開発、ステージング、本番といった環境間の差異を最小限に抑える。
よくある環境の差分は三種類に分けられる。
- 時間のギャップ:開発→テスト→本番の反映に時間的差分がある
- 人材のギャップ:開発者とデプロイ担当が異なる
- ツールのギャップ:開発環境と本番環境でツールが異なる
目的
継続的デプロイがしやすい環境にする。
開発中に発生したバグが本番環境で再現されることを保証し、信頼性を高める。
手法/ベストプラクティス
Dockerなどのコンテナ技術を使い、すべての環境で同じイメージを実行する。
IaCを使いインフラをコードとして定義しバージョン管理し、手動設定のミスを防ぐ。
CI/CDパイプラインを統一し、環境でのコンテナイメージ差分をなくす。
Administrative processes(管理プロセス)
意味
管理プロセス:データベースのマイグレーションやデータインポート、バッチジョブ、一度限りのスクリプト実行など、アプリケーションの主要な機能とは直接関係ない、運用やメンテナンスのためのタスクを指す。
それらを、通常のアプリケーションプロセスとは分離して、より適切なデザインやアーキテクチャにリファクタリングする。
目的
- アプリケーションの役割と異なる管理タスクを分離し、単一責任の原則を守る。
- 管理タスクのサービスを切り離すことで、メインアプリケーションの可用性を確保する。
- クラウドのマネージドサービスを活用することで、インフラ管理の手間を削減し、より効率的な運用を行う。
手法/ベストプラクティス
- データベースのマイグレーションを自動化する
→ 手動でスクリプト実行せず、CI/CDパイプラインに組み込み、デプロイプロセスの一環として自動的に実行する。 - バッチジョブはサービスを活用する
→ アプリケーション内のタイマー機能で行わず、専用スケジューリングサービスやサーバーレスサービスを利用する。- 例)
・AWS LambdaのEventBridgeで定期実行。
・AWS Batch、またはECS Fargateでコンテナ化されたジョブとして実行。
- 例)
- 一度限りのスクリプトは専用にランナーを構築する
→ 本番環境のサーバーに直接ssh接続して実行するのではなく、専用のスクリプトランナーを構築し、必要に応じてトリガーする。- 例)
・Lambda関数
・AWS Fargateタスク
- 例)
Port binding(ポートバインディング)
意味
アプリケーション自体がWebサーバーとしてふるまい、外部からのリクエストを処理する。
アプリケーションが外部のWebサーバーやプロキシに依存せず、自身でネットワークポート(例: 8080 や 5000 など)を開いてHTTPリクエストを直接受け付ける仕組みを作る。
目的
疎結合で自己完結的なアプリケーションを実現し、デプロイやスケーリング、インフラ管理を容易にすること。
- 外部Webサーバー(ApacheやNginxなど)への依存をなくし、どこでも動かせるアプリケーションにする。
- クラウドやコンテナ環境での自動化・オーケストレーションに適したアーキテクチャになる。
手法/ベストプラクティス
- アプリケーション内でHTTPサーバーを実装する
→ 例: Node.jsのExpress、PythonのFlask、Goのnet/http, Rubyのsinatra, RailsのPumaなど - ポート番号は環境変数で指定する
→ デプロイ先環境によって異なるポート番号を柔軟に設定できるようにする(例:PORT環境変数) - 外部Webサーバーやミドルウェアへの依存を避ける
→ アプリの起動コマンドで直接リクエストを受け付ける - ヘルスチェックやメトリクス用のエンドポイントも同じポートで提供する
→ 一元的な監視・運用がしやすくなる - サービスディスカバリやロードバランサとの連携
→ 起動時に自動で自身のポートを宣言したり、ロードバランサに登録する仕組みと組み合わせる
Stateless processes(ステートレスプロセス)
意味
アプリケーションのプロセスを、セッション情報や一時的なデータを内部に保持しないように設計する。
プロセスはリクエストを受け取り、それを処理してレスポンスを返しますが、その間に生成された状態情報はすべて外部のストレージ(データベースやキャッシュなど)に保存する。
目的
スケーラビリティと信頼性を高めること。
- 複数のサーバー・プロセスでリクエストを分散処理できるため、簡単にスケールアウト(水平拡張)できる。
- プロセスの再起動や障害発生時にも、状態が外部にあるためシステム全体の安定性が保たれます。
手法/ベストプラクティス
- セッション情報は外部ストレージに保存する
- 例: データベース、Redis、Memcached など
- → Webサーバーのメモリやローカルディスクには保存しない
- 一時ファイルやアップロードデータも外部に保存
- 例: オブジェクトストレージ(Amazon S3など)を利用
- 「環境変数」で設定を管理
- → プロセス起動時に必要な情報のみを環境変数から取得し、状態管理に利用しない
- プロセスの再起動やスケールアウトを前提とした設計
- → どのプロセスにアクセスしても同じ結果が得られるようにする
- 分散キャッシュの活用
- → 状態を一時的に保持したい場合でも、複数プロセスからアクセスできる外部キャッシュを使う
Concurrency(並行性)
意味
アプリケーションを複数のプロセスやスレッドで同時に処理すること。
「プロセスモデル」が推奨されており、1つ1つのタスクを独立したプロセスとして動作させることが基本。
目的
- スケーラビリティの確保
- トラフィックの増加やジョブ数の増加に応じて、プロセス数を増やすことで柔軟に対応できるようにする
- パフォーマンスの向上
- ボトルネックとなるタスクを分離・分割し、効率的に処理できる
- フォールトトレランス(障害耐性)の向上
- 1つのプロセスが落ちても他のプロセスに影響を与えにくい
- 運用の柔軟性
- ワーカーやWebサーバーなど、役割ごとのプロセスを個別にスケール・再起動できる
手法/ベストプラクティス
- プロセスモデルの採用
- Webリクエスト処理、バッチ処理、ジョブキュー処理などを別々のプロセスとして分離。
- ステートレス設計
- 各プロセスが状態(セッションやキャッシュ)を持たず、必要な情報はDBや外部ストレージを利用する。
- プロセスの増減や再起動を柔軟に行える。
- ECSサービスで並行性を担保
- ECS(FargateまたはEC2)のサービス定義で、タスク(コンテナ)のdesired count(希望タスク数)を増やすことで、同じアプリケーションのインスタンスを水平スケールできる。
- Auto Scalingの活用
- トラフィックやキューの長さに応じて、ECSのタスク数(コンテナ数)を自動で増減させる
Telemetry(テレメトリ)
意味
アプリケーションやインフラの動作状況・パフォーマンス・利用状況などの「計測データ」を自動的に遠隔収集・送信し、 リアルタイムで監視・分析できるようにする仕組み。
例:リクエスト数、エラー件数、レスポンス時間、CPU/メモリ使用率、外部API呼び出し回数、カスタムイベントなど
目的
- 可観測性(Observability)の向上
- システムで何が起きているのか“見える化”し、障害や性能劣化の早期発見・原因特定を可能にする。
- 自動アラート・迅速な障害対応
- 異常値やエラー発生時に自動で通知・アラートを発報し、問題を素早く検知して対応できる。
- 運用・改善のためのデータ活用
- 利用状況や傾向、ボトルネックを把握し、運用やプロダクト改善の根拠とする。
- SLA/SLOの遵守・証跡管理
- サービス品質の証明や合意事項(SLA)を守るために、実際の動作記録を残す。
手法/ベストプラクティス
- アプリケーションからのメトリクス・イベント送信
- Web/APIサーバー、バッチ、ジョブワーカーなど、あらゆるコンポーネントから
- リクエスト数・エラー数・レスポンスタイム
- 外部サービス呼び出しの成否・回数
- カスタムイベント(例:ユーザー登録、注文完了) などを定期的に送信
- Web/APIサーバー、バッチ、ジョブワーカーなど、あらゆるコンポーネントから
- 分散トレーシングの導入
- マイクロサービスや分散システムの場合は、リクエストごとの流れを追跡(トレース)できるようにする
- OpenTelemetryなどの標準技術を活用
- 集中管理・可視化ツールの利用
- Datadog, New Relic, Prometheus, Grafana, Amazon CloudWatch などのSaaS/OSSを活用し、メトリクスを一元管理・ダッシュボード化
- アラートルールやレポート作成も自動化
- アプリ実装への組み込み
- テレメトリ送信はアプリケーション内に直接組み込むか、ミドルウェア/エージェントを利用
- ロギングと分離し、メトリクスは専用のチャネルで送信
- 自動アラート・SLO監視
- 重要な指標(例:エラー率、レスポンスタイム)にしきい値を設け、逸脱時に自動アラート
- SLO違反や障害の兆候も即時検知
Authentication and authorization(認証/認可)
意味
認証と認可を「アプリ個別実装」ではなく「外部化・一元管理」し、トークンベースでステートレスに扱う。
目的
- 安全にサービスを運用し、不正アクセスや情報漏洩を防ぐため
- サービスやAPIの利用権限を適切に管理し、権限のない操作を制限するため
- マイクロサービスやAPI連携時に、サービス間の信頼性・セキュリティを担保するため
手法/ベストプラクティス
- 外部化と一元管理
- 認証と認可のロジックをアプリケーションから切り離し、外部サービス(認証プロバイダ、API Gateway、IDaaSなど)で一元管理する。
- 例:Auth0, Amazon Cognito, Okta, Google Identity Platform などの利用
- トークンベース認証(JWTなど)
- APIやマイクロサービス間での認証は、**セッションではなくトークン(例: JWT)**を使う
- ステートレスな認証・認可ができるため、スケーラビリティや分散環境に適応しやすい
- 最小権限の原則(Principle of Least Privilege)
- 必要最小限の権限だけを付与し、不要な権限は与えないようにする
- 権限制御の粒度を細かく設計
- 認証・認可の分離
- 認証(誰かの確認)と認可(何ができるかの制御)は分離して設計
- たとえば、認証はIDプロバイダー、認可はポリシー管理サービス(例:OPA, AWS IAM)などで分担
- API Gatewayやサービスメッシュでの認証・認可
- API Gatewayやサービスメッシュ(Istioなど)で、サービス間通信の認証・認可を一元化する
- 各サービスが個別に認証・認可を持つのではなく、中央集権型で管理することでシンプルかつ安全に
参考
- AWSで実現するモダンアプリケーション入門 | 技術評論社 🔗
- The Twelve-Factor App on AWS - Speaker Deck 🔗
- 「The Twelve-Factor App」を15項目に見直した「Beyond the Twelve-Factor App」を読んだ - kakakakakku blog 🔗
- Beyond the Twelve-Factor App を元にした アプリケーション開発のプラクティス考察 - FLINTERS Engineer’s Blog 🔗
- Beyond the Twelve-Factor App の目的とやること 🔗
- Why and what you should do to follow Beyond the Twelve-Factor App - DEV Community 🔗
- Twelve-Factor Appを読み解いていく 🔗