【読書メモ】継続的デリバリ―② 2章 構成管理

前回の記事は[こちら](https://qiita.com/Jazuma/items/180b46947167b6347bef)から。

### 2.1 導入

構成管理の定義： プロジェクトに関連するあらゆる成果物とそれらの間にある関係性が、保存され、検索され、一意に特定され、修正されるプロセス

### 2.2 バージョン管理を使う

バージョン管理の目的は2つある。1つは一度格納されたあらゆるファイルに対して、全てのバージョンを保持し、アクセスできるようにすることである。 もう一つはチームが離れた場所・別の時間帯に作業していても協力できるようにすることである。

バージョン管理は突き詰めると以下の問いに答えるために存在する。

1. ソフトウエアのあるバージョンはどのような構成か？ 過去バージョンのバイナリと設定をどうすれば再現できるのか？
1. いつ誰が何のために何をしたのか？

#### 2.2.1 ひとつ残らずバージョン管理に保存せよ

バージョン管理はソースコードだけが対象ではない。
データ生成スクリプト・ビルド/デプロイスクリプト、設定ファイルもバージョン管理下に置かなければならない。同様に、環境を再現するのに必要な情報もバージョン管理の対象にする必要がある。(アプリケーションのMWスタックやファイアウォール定義等)
こうしておくとシステムの状態を正確に復元することができるようになる。

ソフトウエアで利用しているツールのバイナリイメージをバージョン管理下に置いているチームも存在する。
これにより、ソフトウエアの設定が完全に定義され、動作することが保証される。

しかし、バージョン管理すべきでないものが1つある。それはアプリケーションをコンパイルした結果のバイナリである。 理由は3つ挙げられる。

1つはバイナリはサイズが大きい上、チェックインのたびに生成されるためバージョン管理に入れるとリポジトリの容量が肥大化するためである。

2つ目は自動ビルドの仕組みがあればスクリプトを実行するだけでバイナリを生成できるためである。

3つ目はバイナリをバージョン管理対象にすると1つのバージョンに対してソースコードとバイナリという2つの成果物がコミットされてしまうためである。

#### 2.2.2 定期的にtrunkにチェックインせよ

自分の作業がシステムの他の機能に影響していないことを確認するためにチェックインは慎重になる必要がある。 しかし、チェックインするまでの期間が伸びるとコンフリクトの危険が大きくなる。 こまめにコミットすれば1回あたりのマージの量が減って管理しやすくなる。

チームによっては新しい機能に対してブランチを切ることもある。変更が完了したとみなされた時にメインの開発ブランチにマージする。 しかし、本書ではこのような方法は推奨されない。

- ブランチを作ると統合が先延ばしになる。加えて統合による問題がマージになるまで発覚しない。このアプローチは継続的インテグレーションとは相反する。
- 複数のメンバーがブランチを作ると問題が肥大化する。マージするプロセスも複雑化する。

そうではなく、**新しい機能をインクリメンタルに開発し、それをバージョン管理のtrunkにこまめにコミットするやり方**が望ましい。

こうするとソフトウエアが常にテストされている状態になる。チェックインのたびにCIサーバでテストされるためである。 これにより大規模なマージによってコンフリクトが発生する可能性を抑えることができる。 その上、統合時の問題が早期に検出される。 結果として、ソフトウエアの品質が高くなる。

### 2.3 依存関係の管理

#### 2.3.1 外部ライブラリを管理する

外部ライブラリをバージョン管理べきかということについては議論の余地がある。

リリース時にインターネットからダウンロードするようにすればバージョン管理すべきものが少なくて済む。
しかし、インターネットからダウングレードすることを強制されることはデメリットともいえる。

本書では外部ライブラリのコピーをローカルに保持しておくことが推奨される。
これによりビルドを常に再現することができる。 また、ビルドシステムでは外部ライブラリのバージョンを常に指定しなければならない。 しかし、外部ライブラリをバージョン管理対象にするとリポジトリの容量が肥大化してチェックアウトやビルドが遅くなるというデメリットもある。

外部ライブラリをバージョン管理するかどうかということはトレードオフの関係にある。

### 2.4 ソフトウエア設定を管理する

設定情報はソフトウエアの振る舞いをビルド時とデプロイ時、そして実行時に変更するのに使われる。
運用チームは設定上の選択肢にどのようなものがあるか・選択肢をどう管理するか・設定が一貫して管理されていることを保証するにはどうすればよいか考える必要がある。

システムの設定情報はコードと同じ方法、すなわちテストと管理の対象とすることで扱うことができる。

#### 2.4.1 設定と柔軟性

設定の柔軟性を求めると「究極の設定可能性」というアンチパターンに繋がる。ソースコードと比べて設定情報は誤った値・形式で書くことができてしまう。 ほとんどの場合、設定情報の誤りを実行時まで検出することができない。

#### 2.4.2 設定の種類

設定情報をアプリケーションに注入するタイミングはビルド・デプロイ・テスト・リリースプロセスにおいて何回かある。

- ビルドスクリプトで設定を選び、ビルド時にバイナリに取り込む
- パッケージソフトの場合、パッケージングの際に設定を注入する
- デプロイスクリプトやインストーラでインストールプロセスの一部としてアプリケーションに設定情報を渡す
- アプリケーションが起動時や実行時に設定を読み込む

ビルド時やパッケージング時に設定情報を注入するのは悪い運用である。あらゆる環境に同じバイナリをデプロイできるべきであるという原則から逸脱するためである。(この原則により、リリースするものがテストしたものと同じであることを担保することができる)

デプロイメントの度に変更されるものは全て設定として扱い、コンパイルやパッケージングの際に注入すべきでなはない。

アプリケーションの設定をデプロイ時に行い、依存しているサービス(DB・外部API等)のパスを定義することは多くの場合重要である。例えば、アプリケーション実行時の設定がDBに保持されている場合はデータベースの接続設定をデプロイ時にアプリケーションに渡せるようにしておく必要がある。

アプリケーションの設定を起動時に実行する場合も想定される。 環境変数や実行用コマンドの引数として渡されることが多い。

どのような方法で設定を注入する場合でも、全ての環境に対して同じ方法で設定情報を組み込むことが強く推奨される。

#### 2.4.3 アプリケーションの設定を管理する

アプリケーションの設定を管理する上で考慮しなければならない点が3つある。

1. 設定情報をどのように表現するか
1. デプロイメントスクリプトからどのようにアクセスするか
1. 環境やアプリケーションのバージョンによってどのように変更されるのか

設定情報はキーと値がセットになった文字列としてファイルに保持されることが多い。(ファイル形式はiniファイル・ymlファイル・xmlファイル等がある)

また、環境固有の情報はソースコードはソースコードとは分けて管理しておくことが重要である。

##### 設定にアクセスする

設定を管理する上で効果的な方法はアプリケーションが必要な設定を取得できるような中央集権的なサービスを置くことである。 このようなサービスに保持された設定にアプリケーションがアクセスするのに最も簡単な方法はファイルシステムを経由する方法である。 これにはあらゆる言語で利用可能であるというメリットがある。 一方で、アプリケーションをクラスタ上で実行する場合、ファイルシステム上の設定を同期させておかなければならない問題がある。

別の方法としてデータベースやLDAPなどのリポジトリから設定を取得してもよい。

どのような方法を使うにしろ、設定情報を取得するためのインタフェースを作成して技術的な詳細をアプリケーションから隔離しておくべきである。これにより、テスト時にモックを作ることも・必要に応じて格納方法を変えることが容易になる。

##### システム設定をテストする

システム設定もアプリケーションやビルドスクリプトと同様にテストする必要がある。設定のテストは2つの部分からなる。 まず、外部サービスへの参照設定が正常であることを担保する。メッセージングバスが実際に起動していること・テスト環境で利用するモックサービスが機能していること等を確認しなければならない。少なくとも外部サービスに対してpingを打つ必要はある。

デプロイメントスクリプトやインストーラは依存先のアプリケーションやAPIが一つでも失敗したら落ちる必要がある。デプロイメントスクリプトやインストーラが設定に対するスモークテストとして機能しなければならない。

次に、アプリケーションがインストールされた後に実際にスモークテストを実行して期待通りに動作することを確認する。 このためには設定が正しいことに依存するようなテストが必要になる。


#### 2.4.4 アプリケーションをまたいで設定を管理する

多くのアプリケーションを管理しなければならない場合に、設定管理の問題は特に複雑になる。
最も重要な作業は各アプリケーションにある設定オプションを一覧化しておくことである。どこに格納されていて、ライフサイクルがどうで、どのように変更するのかといった具合である。

可能であればこのような情報はビルドプロセスの一環としてアプリケーションのコードから自動生成されることが望ましい。

究極的にはWebアプリケーションを管理する場合には各アプリケーションの設定を本番監視システムによって見ることができるようにしておくことが望ましい。

#### 2.4.5 アプリケーションの構成管理の原則

アプリケーションの設定はコードと同じように扱う。すなわち適切に管理・テストする。
以下にアプリケーションの設定を行うシステムを作る際に考慮すべき原則をいくつか挙げる。

- アプリケーションで利用できる設定オプションをソースコードと同じリポジトリに保持する。しかし、値は別の場所に保管する。設定値のライフサイクルはソースコードとは異なる上、機密情報は決してバージョン管理に保持してはならない。
- 設定値を反映するためには自動化されたプロセスで設定リポジトリから取得した値を常に使うようにする。
- アプリケーションやバージョン・デプロイ先の環境に応じて個別の設定値を提供できる必要がある。
- 設定に対してはデプロイメントやインストール時にテストする必要がある。 依存先サービスにアクセスできることを確認し、スモークテストで設定に依存している機能が正常に動くことを確認する。

### 2.5 環境を管理する

アプリケーションと同様に環境設定も管理すべきである。環境設定情報を管理していれば動くとわかっている状態を簡単に再現することができる。

以下のような環境設定を考慮しておくべきである。

- OS。これにはバージョン、パッチレベル、設定も含む。
- 追加でインストールするソフトウェアパッケージ。パッケージの設定やバージョンも含めて管理する必要がある。
- アプリケーションが動作するのに必要なネットワークトポロジー
- アプリケーションが依存する外部サービス
- アプリケーション内容のあらゆるデータや状態

構成管理の原則は2つある。1つはバイナリファイルを構成情報とは独立させておくこと、もう一つはあらゆる設定情報を一か所にまとめておくことである。

この原則をシステムに適用することで、環境構築・システムのアップデート・設定を更新するといった作業を自動化することができる。

これらの原則をアプリケーションだけではなく、サードパーティのライブラリに対しても適用する必要がある。サードパーティライブラリを選ぶ際に重視すべき基準がある。それはユーザの介入が不要なインストーラが用意されていることである。このようなインストーラはバージョン管理可能でインストール時の手作業が不要である。

#### 2.5.1 環境管理のためのツール

環境管理を自動化するツールの例として、PuppetとCfEngineが挙げられる。
これらのツールを使うとユーザの権限制御・インストールすべきパッケージを宣言的に管理することができる。

他には仮想化を用いても環境管理っを効率化できる。正常動作する筐体のコピーをとり、それをベースラインとすればよい。

#### 2.5.2 変更プロセスを管理する

環境を変更するプロセスも管理できるようにしておく。
本番環境は定められらた構成管理プロセスを経由しないと変更できないようにしておくべきである。

本番環境に変更を反映する前にテストする必要がある。そのためには手順をスクリプト化し、バージョン管理に含める必要がある。そうしておくと、テストが通れば本番環境に変更を反映できる。

#### 2.6 まとめ

構成管理プロセスにおいては以下の問いに答えられることを目指す。

- バージョン管理されたファイルを使って本番システムを1から(データを除いて)再現することができるか？
- 正しく動くとわかっているバージョンにアプリケーションを戻すことができるか？
- 本番環境とステージング環境・テスト環境が同じ方法でデプロイされていることを保証できるか？

上記を実現するために以下のものをバージョン管理に格納し、変更を制御するための方法を検討すべきである。

- ソースコードやビルドスクリプト、テストドキュメント、要件、データベーススクリプト、ライブラリ、設定ファイル
- デプロイメントやテスト、運用に使うツール
- 開発環境やテスト環境、本番環境
- アプリケーションに関連する全てのアプリケーションスタック。
- あらゆるアプリケーションに関連する設定。アプリケーションのライフサイクル全体(ビルド・デプロイ・テスト・運用)をカバーしなければならない。