Tweleve-Factor Appとは
アプリケーションをSaas
として提供するための12のベストプラクティスを記載したもの。
具体的には以下の項目を実現するためのベストプラクティスを集めたものだと理解。
-
セットアップ自動化のために 宣言的なフォーマットを使い、プロジェクトに新しく加わった開発者が要する時間とコストを最小化する。
-
下層のOSへの依存関係を明確化し、実行環境間での移植性を最大化する。
-
モダンなクラウドプラットフォーム上へのデプロイに適しており、サーバー管理やシステム管理を不要なものにする。
-
開発環境と本番環境の差異を最小限にし、アジリティを最大化する継続的デプロイを可能にする。
-
ツール、アーキテクチャ、開発プラクティスを大幅に変更することなくスケールアップできる。
一つずつの項目を読んで感じたことを備忘録的に記載していく。
バージョン管理されている1つのコードベースと複数のデプロイ
-
アプリケーションは一つのコードベースで管理されているべき。**同じコードを共有する複数のアプリケーションは、Twelve-Factorに違反している。**その場合の解決策は、共通のコードをライブラリに分解し、そのライブラリを依存関係管理ツールで組み込むようにすることである。と記載されている。
- 気になったのは太字の部分。これは品質管理の点で重要なポイントに感じる。あるアプリケーション起因の修正を、他のアプリケーションとも共有しているコードベースに対して行うと、他のアプリケーションでは予期せぬ修正となり、不具合を埋め込むことになるからアンチパターンとされていると理解した。依存関係管理ツールで共通部分のみをライブラリとして別コードベースにすることでバージョン管理が可能になり、予期せぬ修正がライブラリに入りこむことによる不具合発生を防げる。
依存関係を明示的に宣言し分離する
-
Twelve-Factor Appは、システム全体にインストールされるパッケージが暗黙的に存在することに決して依存しない。と記載されている。
- これは依存関係管理ツールにアプリケーションをビルド・実行するために必要な外部ライブラリを全て定義しておく必要があることを示していると理解。
java
だとmaven
やgradle
を依存関係管理ツールとして使うことが多いと思う。pom.xml
やbuild.gradle
に定義されていないが、アプリケーションを実行・ビルドするために必要なライブラリが存在すると、新規参入した開発者は環境構築に戸惑うだろうし、CI/CDパイプラインでの予期せぬ不具合に繋がりかねない。
- これは依存関係管理ツールにアプリケーションをビルド・実行するために必要な外部ライブラリを全て定義しておく必要があることを示していると理解。
設定を環境変数に格納する
-
アプリケーションの設定は、デプロイ(ステージング、本番、開発環境など)の間で異なり得る唯一のものである。設定には以下のものが含まれる。
- データベース、Memcached、他のバックエンドサービスなどのリソースへのハンドル
- Amazon S3やTwitterなどの外部サービスの認証情報
- デプロイされたホストの正規化されたホスト名など、デプロイごとの値
-
これらがコードベースに含まれていないことを保証すべきで、この保証ができていることを検証する方法は、コードベースを今すぐにOSS化できるかどうかを考えることと記載されている。
また、この「設定」にはデプロイごとに変更する必要のない設定は含まない。- これは開発効率と品質担保の2側面から重要な章だと感じた。環境別の設定を設定ファイルとして持つと環境ごとにモジュールをビルドする必要が出てくるため、開発リードタイムが大きくなってしまう。さらに環境別にモジュールを作るということは、本番環境にリリースされるモジュールとSTG環境でテストされたモジュールが異なるということになり、設定ファイル起因の不具合は本番環境でしか検知できない。
- 環境変数に設定を定義することで、開発環境から本番環境までアプリケーションの実行バイナリは一つでよい。また設定の修正でコードを修正する必要もない。環境変数は粒度の細かい設定もできるし、OSに依らない管理が可能。
バックエンドサービスをアタッチされたリソースとして扱う
これはアプリケーションの結合を疎に保つことで、デプロイや他のアプリケーションへの接続切替を迅速に低コストに行えるようにしようってことだと理解。コードの修正なしにバックエンドサービスを切り替えられるようになれば十分疎結合になっているはず。
ビルド、リリース、実行の3つのステージを厳密に分離する
実行ステージはできるだけ可変部分を持たないようにするべきである。なぜなら、アプリケーションの実行を妨げるような問題が起きると、開発者が待機していない真夜中にアプリケーションが壊れる結果になるためである。
上記の記載が何より気にしないといけないんだろうなと感じた。ただ可変部分ってどういうものだろうと具体例があまり思いつかない。。
アプリケーションを1つもしくは複数のステートレスなプロセスとして実行する
Twelve-Factorのプロセスはステートレスかつシェアードナッシング である。永続化する必要のあるすべてのデータは、ステートフルなバックエンドサービス(典型的にはデータベース)に格納しなければならない。
と記載されているが、結構難しい。厳しい性能要件が求められるアプリケーションの場合、DBアクセスは最小限に留めたいため、トランザクションデータをキャッシュしておいて、非同期でDBに書き込みを行うなどの工夫をしたくなってしまう。ただそうゆう工夫をしてしまうと、同一プロセスタイプのプロセスをスケールアウトしたいと思っても、プロセス内部にキャッシュしてあるデータまで含めて複製する必要が出てくる。
アプリケーションをステートレスに保つことと性能要件とのトレードオフをどう解決するかを考えなければならない。
ポートバインディングを通してサービスを公開する
これはいまいち何を伝えたいのか分からなかったが、ファイルシステムの共有など密結合な手法で外部サービスと連携しないようにしするべしってことだと理解した。
プロセスモデルによってスケールアウトする
アプリケーションをステートレスに保ち、アプリケーションの雛形からプロセスを起動できるようにしておけば、容易にアプリケーションをスケールアウト可能になる。
プロセスを自前でデーモン化すると、OSやプロセス管理ツールにプロセスの起動を任せることができなくなり、スケールアウトが複雑になってしまう。
高速な起動とグレースフルシャットダウンで堅牢性を最大化する
アプリケーションの起動時間は最小限になるように努力すべき。リリース作業やスケールアウトのアジリティを高めることで、ビジネスの機会損失を最小化できるから。
「クラッシュオンリー設計」は初めて聞いた。アプリケーションを正常な手順で終了するのではなく、クラッシュさせることによってのみ終了させるという考え方らしい。そうすることで、OSレベルのリソース解放、アプリケーション再起動までを速やかに行えるように設計することになるから、結果的に障害復旧が速やかに行えるようなアプリケーションになることを目的としている。
開発、ステージング、本番環境をできるだけ一致させた状態を保つ
三つのギャップを埋める必要があるとのこと。
- 時間のギャップ: 開発者が編集したコードが本番に反映されるまで数日、数週間、時には数ヶ月かかることがある。
- 人材のギャップ: 開発者が書いたコードを、インフラエンジニアがデプロイする。
- ツールのギャップ: 本番デプロイではApache、MySQL、Linuxを使うのに、開発者がNginx、SQLite、OS Xのようなスタックを使うことがある。
ツールのギャップ
や人材のギャップ
は品質を上げるために最小限にすべきだと感じる。ただ、時間のギャップ
はどうゆう観点で最小限にすべきなんだろう。
機能追加のためにコードを修正した後、リリースまでの時間が長くなれば、追加した機能はリリースされるまでずっとユーザからのFBを得られないままになる。Saas
アプリケーションはユーザからのFBを得て、機能に迅速に反映させていくことで収益化する。
よって、時間のギャップ
を最小限にすることは収益改善のためにもやるべきだと理解した。
ログをイベントストリームとして扱う
ログをファイルに出力するようにアプリケーションの設定で行ってしまうと、廃棄容易性が失われてしまう。そこでログ出力を標準出力に統一し、実行環境ごとのログ収集の仕組みに任せる方が良い。開発環境はターミナルに出力しておけば良い。
管理タスクを1回限りのプロセスとして実行する
アプリケーションを管理するタスクを実行するプロセスは、アプリケーションと同一の環境で実行すべき。管理タスクは単発実行できるスクリプトとしてコードリポジトリで管理すべき。
という記載があるが、これはアプリケーションと管理タスクスクリプトを常に同じ環境で動かせるように保つことでアジリティ高く開発が行えるということを示しているのかなと感じた。
12 Factor Appを読んでみて
基本的にWhy?
が記載されていないため、なぜそれがベストプラクティスなのか、に納得がしづらい。そのため、読んでみてなぜこれがプラクティスなのかを考察してみた。
開発効率やテストしにくいなど開発阻害要因があるが、原因が何なのか分からないといった場合に、立ち返ってみると原因が何なのか発見するための手掛かりにはなるかもな、と感じた。