『The Twelve-Factor App (日本語訳)』を読んだ

Posted on
architecture

The Twelve-Factor App (日本語訳)を読んだ。

はじめに

これは、Herokuの中の人が提唱した、モダンなWebアプリケーションとしてあるべき姿をベストプラクティスとしてまとめたもの。自分が5年ほどWebとはほぼ関係ない仕事をしていて知識が足りてないのと、仕事がマイクロサービスっぽい基盤を作っていこうぜ!というフェーズなので、基本的な考え方を習得する為に読んだ。ただ、2012年に発表されたものなので、2019年の今に思考停止ですべて当てはめるのは危険だという前提を持つ必要があると思う。

お決まりだが、読むだけだとすぐ忘れるので、箇条書きでポイントになる部分を羅列しておくことにする。

要約

コードベース

  • サーバで可動するアプリケーションと、コードベース(リポジトリ)は、常に1:1の関係であるべき
  • 普通は、stg,prod環境など複数の環境がある。単一のリポジトリであれば、N個のデプロイ(≒N個の環境)があるのは問題ない。

依存関係

  • システム全体にインストールされるパッケージが暗黙的に存在する事に依存してはいけない
    • Gemfileは依存関係宣言の為に存在する
    • bundle execは依存関係分離の為に存在する
  • いかなりシステムツールの暗黙的な存在に依存してはいけない
    • curlなんでどんな環境でも絶対あるよねーと思っても、ちゃんとアプリケーションで使うライブラリの依存関係に組み込む必要がある

設定

  • 設定をコードから厳密に分離する必要がある(デプロイ毎に変わるものを設定と考えると、コードはデプロイ毎に変わる必要はないはず)
    • 認証情報を漏洩させずに今すぐOSSとしてコードを公開できるか??を問え!
  • 結論、設定は環境変数に格納するべきである。
    • コードを変更することなく、デプロイ毎に簡単に変更できる
    • 誤ってリポジトリにチェックインされる可能性は殆ど無い
    • 言語やOSに依存しない

バックエンドサービス

  • アプリケーションがネットワーク越しに使うすべてのサービス(バックエンドサービス)は、アタッチされたリソースとして扱い、接続情報などは設定として(環境変数で)管理する
    • DBとかPostfixとかS3とか、、
  • ローカルサービスとサードパーティサービスを区別しない!
    • DBもTwitterAPIも、アプリケーションからは同じようにバックエンドサービスとして扱う

ビルド、リリース、実行

  • ビルド、リリース、実行のステージを、お互いの影響範囲を分離する為に、厳密に分離する
    • ビルド:コードを実行可能な形式にする
    • リリース:ビルドとデプロイ設定を統合し、各サーバマシン上ですぐに実行できる状態にする
    • 実行:サーバ上でアプリケーションを起動する

プロセス

  • アプリケーションは、実行環境の中で1つor複数のプロセスとして実行される
    • プロセスはステートレスかつシェアードナッシングであるべき。永続化が必要なものはバックエンドサービス(DBなど)に格納する必要がある
  • 例えば、スティッキーセッションに頼り、セッションデータをメモリに置くのは良くなくて、MemcachedやRedisに格納するべきである

ポートバインディング

  • Webアプリケーションは、ポートにバインドすることでHTTPをサービスとして公開し、ポートに対してリクエストが来ることを待つ
  • Webアプリにとどまらず、あらゆるサーバソフトウェアは特定にポートでLISTENし、リクエストを待ち受ける、という作りになる
  • こうすることで、あるアプリケーションが他のアプリケーションにとってのバックエンドサービスとして振る舞うことができるようになる

並行性

  • スレッドではなく、プロセスを用いて(複数台での)スケールアウトするべき
  • 基礎知識が足りてないので、もう少し深掘りが必要

廃棄容易性

  • すばやく柔軟なスケールと、コードや設定に対する変更の素早いデプロイの為に、即座に起動・終了できるよう、廃棄容易であるべき
    • 起動時間を最小化するよう努力するべき
  • プロセスは、SIGTERMシグナルを受け取った時に、グレースフルにシャットダウンするべき
    • ワーカープロセスの場合は、処理中のジョブをワーカーキューに戻すことで実現される

開発/本番一致

  • 継続的なデプロイの為に、開発環境と本番環境のギャップを小さく保つ必要がある
  • 開発と本番でバックエンドサービスを使い分けるのもやめたほうがいい。すべてのデプロイで同じ種類かつ同じバージョンのバックエンドサービスを使うべき
    • 本番ではMySQL使ってローカルではSQLite使うみたいなのも、想定外の非互換性に苦しめられる可能性がある

ログ

  • アプリケーション側でログの出力先を管理するべきではない。(ログ保存やローテーションなどは考えなくて良い)
  • stdoutに書き出して、別サービスでよしなにやるべし。

管理プロセス

  • アプリケーションの為の1回限りの管理・メンテ用のタスクが存在する
    • rails db:migrate
    • rails console
  • これらは、アプリケーションの通常の長時間実行されるプロセスと同じ環境で実行されるべきである
    • 同じリポジトリに入れて、同期の問題が発生しないようにしよう