X. 開発/本番一致
開発、ステージング、本番環境をできるだけ一致させた状態を保つ
歴史的に、開発環境(開発者が直接変更するアプリケーションのローカルデプロイ)と本番環境(エンドユーザーからアクセスされるアプリケーションの実行中デプロイ)の間には大きなギャップがあった。これらのギャップは3つの領域で現れる。
- 時間のギャップ: 開発者が編集したコードが本番に反映されるまで数日、数週間、時には数ヶ月かかることがある。
- 人材のギャップ: 開発者が書いたコードを、インフラエンジニアがデプロイする。
- ツールのギャップ: 本番デプロイではApache、MySQL、Linuxを使うのに、開発者がNginx、SQLite、OS Xのようなスタックを使うことがある。
Twelve-Factor Appでは、継続的デプロイしやすいよう開発環境と本番環境のギャップを小さく保つ。 上で述べた3つのギャップを見る。
- 時間のギャップを小さくする: 開発者が書いたコードは数時間後、さらには数分後にはデプロイされる。
- 人材のギャップを小さくする: コードを書いた開発者はそのコードのデプロイに深く関わり、そのコードの本番環境での挙動をモニタリングする。
- ツールのギャップを小さくする: 開発環境と本番環境をできるだけ一致させた状態を保つ。
上で述べたことを表にまとめる。
伝統的なアプリケーション | Twelve-Factor App | |
---|---|---|
デプロイの間隔 | 数週間 | 数時間 |
コードを書く人とデプロイする人 | 異なる人 | 同じ人 |
開発環境と本番環境 | 異なる | できるだけ一致 |
バックエンドサービス(アプリケーションのデータベース、キューイングシステム、キャッシュなど)は、開発/本番一致が重要になる領域の一つである。多くの言語は、異なる種類のサービスへの アダプター を含め、バックエンド・サービスへのアクセスを単純化するライブラリを提供している。以下の表にいくつかの例を示す。
種類 | 言語 | ライブラリ | アダプター |
---|---|---|---|
データベース | Ruby/Rails | ActiveRecord | MySQL, PostgreSQL, SQLite |
キュー | Python/Django | Celery | RabbitMQ, Beanstalkd, Redis |
キャッシュ | Ruby/Rails | ActiveSupport::Cache | メモリ, ファイルシステム, Memcached |
本番環境ではより本格的で堅牢なバックエンドサービスが使われるにもかかわらず、開発者は自身のローカル開発環境で軽量なバックエンドサービスを使いたくなることがある。例えば、開発環境ではSQLiteを使い、本番ではPostgreSQLを使ったり、開発環境ではローカルプロセスのメモリをキャッシュに使い、本番ではMemcachedを使ったりするなどである。
たとえ理論的にはアダプターがバックエンドサービスの違いをすべて抽象化してくれるとしても、 Twelve-Factorの開発者は、開発と本番の間で異なるバックエンドサービスを使いたくなる衝動に抵抗する。 バックエンドサービスの違いは、わずかな非互換性が顕在化し、開発環境やステージング環境では正常に動作してテストも通過するコードが本番環境でエラーを起こす事態を招くことを意味する。この種のエラーは継続的デプロイを妨げる摩擦を生む。この摩擦とそれに伴って継続的デプロイが妨げられることのコストは、アプリケーションのライフサイクルに渡ってトータルで考えると非常に高くつく。
軽量なローカルサービスは、以前ほど魅力的なものではなくなっている。Memcached、PostgreSQLやRabbitMQなどのモダンなバックエンドサービスは、Homebrew や apt-get などのモダンなパッケージングシステムのおかげで、簡単にインストールして実行できる。あるいは Chef や Puppet などの宣言的なプロビジョニングツールと、Vagrant などの軽量な仮想環境を組み合わせることで、開発者は本番環境に限りなく近いローカル環境を作ることができる。開発/本番一致と継続的デプロイの利益に比べると、これらのシステムをインストールして利用するコストは低い。
異なるバックエンドサービスへのアダプターは依然有用である。これらのアダプターは、新しいバックエンドサービスに移植するときの苦痛を比較的和らげてくれるためである。しかし、アプリケーションのすべてのデプロイ(開発、ステージング、本番環境)は同じ種類かつ同じバージョンのバックエンドサービスを利用するべきである。