BOSHとリリースエンジニアリング

# BOSHとリリースエンジニアリング

この記事は、[Cloud Foundry Advent Calendar 2018](https://qiita.com/advent-calendar/2018/cloudfoundry)の9日目の記事です。

[このAdvent Calendarの2日目の記事](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4)において、 `BOSHの設計理念である「モダンな release engineering の実装」について` `やっぱりまだ語り足りないなあ` などと漏らしてしまいました。

[BOSH 101のアップデート](https://speakerdeck.com/ozzozz/bosh101-winter-2018)で燃え尽きてしまった感があったのに、なぜそういうことを勢い余って言ってしまったのか・・・

いや、しかし、この際がんばって語ろうと思います。

## リリースエンジニアリングとは

言葉の定義としては、[英語版Wikipedia](https://en.wikipedia.org/wiki/Release_engineering)に以下のように書いてあります。

> Release engineering, frequently abbreviated as RE or as the clipped compound Releng, is a sub-discipline in software engineering concerned with the compilation, assembly, and delivery of source code into finished products or other software components. 

すなわち、ソースコードからプロダクトを生み出す過程にある、コンパイル、結合、デリバリに関するソフトウェアエンジニアリングの取り組みの一種である、と。古臭い言い方をすれば、要件定義→設計→プログラミング→テストを経てリリース可能と判定されたソースコードを対象に、出荷可能な形に整えて実際に出荷するまでの作業を、いかに正確かつ効率的にやるかという課題を工学的に解決することである、ということですね。あまり噛み砕いてないですか。どうもすみません。

## よくあるリリースエンジニアリング

例えばサービスのリリース作業を正確かつ効率的にやろうと思うと、今どき普通は次のようなことに取り組むだろうと思います。

* Gitリポジトリのmasterブランチもしくはリリース用ブランチに release ready なソースコードをcommit/pushする
* JenkinsなどのCI/CDツールがリリース対象ブランチの更新を自動的に検知すると、そのブランチをcheckoutし、ビルドし、デプロイ対象サーバに配布するジョブを自動実行する
* 更に必要に応じてサーバ上のファイルを入れ替えたりプロセスを再起動したりするようなジョブも自動でリモート実行する

簡単なシステム構成のサービスであれば、リリースエンジニアリング的にはこれでほぼ目標達成だと思われます。

## リリースを正確かつ効率的にできてないとはどんな状態か

[この動画](https://www.youtube.com/watch?v=aPTY3CsIzCg)であるあるネタとして紹介されていますので、書き起こします。

1. ○○さんしかリリース作業ができないんだよなあ
1. リリース作業自体がとても面倒なので、いっそのことリリースの頻度を減らしてしまおう
1. 自分の環境では動いたんだけどなあ
1. リリース前テストは通ったのに商用環境だとなぜか動かないんだよなあ
1. ○○パッケージの○○バージョンがインストールされてないとちゃんと動かないということが、不具合解析の結果やっとわかったんだよなあ

いずれもとてもあるあるですねえ。

上で紹介した「よくあるリリースエンジニアリング」では、`1. ○○さんしかリリース作業ができないんだよなあ` と `2. いっそのことリリースの頻度を減らしてしまおう` は解決しそうですが、3. 4. 5. の解決策は含まれていません。
さらに、リリース対象のサービスがマイクロサービスアーキテクチャが採用された分散システムとして構成されるような場合は、リリース失敗による切り戻しも考えないといけないとすると、リリースエンジニアリング的に相当な工夫をこらさないといけなさそうです。

## モダンなRelease Engineeringとは

ところで、アップデートされたBOSH 101のこのスライドをご覧ください。
  ![slide12](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide12.png)

はい、そのとおりです。[ここ](https://bosh.io/docs/problems/)の文章を意訳したものですね。

ここで紹介されている4つの要素について、BOSHだと具体的にどういうことなのかを例示しつつ解説してみたいと思います。

## Identifiability

この単語は日本語に訳すと「同定可能性」になるそうですが、何のことかよくわかりませんので、上のスライドの文章をコピペしますと以下のとおりです。

> デプロイ対象となるソフトウェアの各構成要素と、デプロイ結果としての実システムとの対応関係を、一貫して管理できる

### 何をデプロイするのか

BOSHがデプロイを行う材料とするものは、[BOSH101のスライド](https://speakerdeck.com/ozzozz/bosh101-winter-2018)の14枚目にある通り、ざっくり言えば

* stemcell
* release
* deployment manifest

の3点です。

  ![slide14](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide14.png)

BOSHは本当に対応関係を一貫して管理しているのか。

私の手元にどの誤家庭にもあるOpenStackクラスタがございまして、BOSHが稼働しております。先ほどBOSHを使って[Concourse CI](https://concourse-ci.org/)をデプロイしてみました。BOSH CLIを使って、concourseのデプロイに使った材料について問い合わせてみます。

```
$ bosh deployments
Using environment '192.168.13.70' as user 'admin'

Name       Release(s)          Stemcell(s)                                      Team(s)  
concourse  concourse/4.2.2     bosh-openstack-kvm-ubuntu-xenial-go_agent/170.9  -  
           garden-runc/1.16.3                                                     
           postgres/30                                                            

1 deployments

Succeeded
```

なるほど、以下の材料を使ったのですね。

* stemcell
  * [bosh-openstack-kvm-ubuntu-xenial-go_agent](https://bosh.io/stemcells/bosh-openstack-kvm-ubuntu-xenial-go_agent) のバージョン170.9
* release
  * [concourse](https://bosh.io/releases/github.com/concourse/concourse-bosh-release) のバージョン4.2.2
  * [garden-runc](https://bosh.io/releases/github.com/cloudfoundry/garden-runc-release) のバージョン1.16.3
  * [postgres](https://bosh.io/releases/github.com/cloudfoundry/postgres-release) のバージョン30

なぜこれらが使われたかというと、 [concourse-bosh-deployment GitHubリポジトリ](https://github.com/concourse/concourse-bosh-deployment)に置いてあるdeployment manifestを使ったからです。
releaseのバージョンは[concourse-bosh-deployment リポジトリ内のversions.yml](https://github.com/concourse/concourse-bosh-deployment/blob/3cf2ec377705ff7a7d06a3a974e1fc2888b8e866/versions.yml)によって決まります。
stemcellは[ここ](https://github.com/concourse/concourse-bosh-deployment/blob/3cf2ec377705ff7a7d06a3a974e1fc2888b8e866/cluster/concourse.yml#L103-L106)にあるとおり、`ubuntu-xenial`というOSでつくられたstemcellのうち、BOSHが保持している最新バージョン(すなわち`latest`)を使用する、ということになります。
ちなみに、このconcourseをデプロイした時点で私のBOSHには以下のようなstemcellが保持されていました。

```
$ bosh stemcells
Using environment '192.168.13.70' as user 'admin'

Name                                       Version  OS             CPI  CID  
bosh-openstack-kvm-ubuntu-xenial-go_agent  170.9*   ubuntu-xenial  -    534ee6f2-b985-4ced-9ea5-cbfec8cc3018 light  

(*) Currently deployed

1 stemcells

Succeeded
```

stemcellは`ubuntu-xenial`の`170.9`たった１個しかBOSHが保持していなかったため、これを使った、ということになります。
なお余談ですが、concourseをデプロイした時点で最新のコミュニティ版のstemcellのバージョンは[170.13](https://bosh.io/stemcells/bosh-openstack-kvm-ubuntu-xenial-go_agent#v170.13)でした。また、`light`という表示が気になるところですが、実はこれはBOSH自体をデプロイするために使ったstemcell(=OpenStack Glance上のVMイメージ)をそのまま流用することで無駄なディスク容量を食わないようにするために[light stemcell](https://bosh.io/docs/build-stemcell/#light-stemcells)を使って登録したものである、という意味になります。

stemcellや各releaseがどういう構成になっているかについて調べる必要がある場合があるかもしれませんが、以下のようにすれば追うことができます。

* stemcell
  * UbuntuやCentOSのstemcellについては、[bosh-linux-stemcell-builder](https://github.com/cloudfoundry/bosh-linux-stemcell-builder)を使って作られます。stemcellのバージョンは、[bosh-linux-stemcell-builderのGitHub releaseのバージョン](https://github.com/cloudfoundry/bosh-linux-stemcell-builder/releases)に対応しています。
  * また余談ですが、Ubuntu XenialのstemcellのOSベースイメージは[このrakeタスク](https://github.com/cloudfoundry/bosh-linux-stemcell-builder/blob/master/bosh-stemcell/spec/os_image/ubuntu_xenial_spec.rb)を実行することにより作成されます。昔から今まで使われ続けてきたCloud Foundry関連のコンポーネントはRubyを使って書かれているものが多いですね。
  * さらに、[このへん](https://github.com/cloudfoundry/bosh-linux-stemcell-builder/blob/master/ci/ubuntu-xenial/pipeline-base.yml#L50-L57)を見るに、2週間ごとにOSベースイメージの作成が自動実行され、いずれかのdebパッケージにはだいたい更新が入っているので、2週間ごとにはだいたい新しいバージョンstemcellが作成されリリースされているようですね。そういった定期更新以外にも、ベースイメージに関わるCVEへの対応などに応じて都度新しいバージョンのstemcellが出ているようです。
* concourse release
  * [concourse-bosh-release](https://github.com/concourse/concourse-bosh-release)を使って作られます。releaseのバージョンは、[concourse-bosh-releaseのGitHub releaseのバージョン](https://github.com/concourse/concourse-bosh-release/releases)に対応しています。
  * BOSHによってビルド・デプロイされるconcourseの実体は[concourse GitHubリポジトリのこのバージョン](https://github.com/concourse/concourse/tree/v4.2.2)を用いて`bosh create-release`コマンドにより作成されAmazon S3にアップロードされたtarballです。[このへん](https://github.com/concourse/concourse/blob/v4.2.2/ci/scripts/finalize-release)を読み解くとわかる感じですね。
  * Amazon S3に置いてあるreleaseのtarballには、基本的に各パッケージのソースコードが含まれており、concourse-bosh-releaseもそうなっています。
    (人気があったり、開発元の気前がよかったりするreleaseは、コンパイル済みのバイナリを含めてS3などのオブジェクトストレージに置いてあったりしますが。)
    BOSHはデプロイにあたってreleaseに含まれるソースコード形式のパッケージを、デプロイ対象となるバージョンのstemcellから作成したVMを使ってビルドします。
    これにより、VMのカーネルやビルドツールのバージョンによってコンパイル＆ビルドで生成されるバイナリが変わる可能性が心配される点も解消されることになります。
    [BOSH 101のスライド](https://speakerdeck.com/ozzozz/bosh101-winter-2018)で言えば、以下の4枚くらいで説明したものですね。
    ![slide47](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide47.png)
    ![slide49](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide49.png)
    ![slide50](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide50.png)
    ![slide51](https://gist.github.com/ozzozz/23b941d4e468810ed06fd75d6d413cd4/raw/slide51.png)

ちなみに、concourseのデプロイに使用したdeployment manifestはBOSHが保持していますが、以下のように参照することもできます。

```
$ bosh -d concourse manifest

Using environment '192.168.13.70' as user 'admin'

Using deployment 'concourse'

instance_groups:
- azs:
  - z1
  instances: 1
  jobs:
  - name: atc

【中略】

name: concourse
releases:
- name: concourse
  sha1: 4107747823b7056c58620c34457746dcedb98cc5
  url: https://bosh.io/d/github.com/concourse/concourse-bosh-release?v=4.2.2
  version: 4.2.2
- name: garden-runc
  sha1: 2a7c813e7e4d862e19334addf022916fb6b91eb0
  url: https://bosh.io/d/github.com/cloudfoundry/garden-runc-release?v=1.16.3
  version: 1.16.3
- name: postgres
  sha1: a798999d29b9f5aa12035cff907b26674b491200
  url: https://bosh.io/d/github.com/cloudfoundry/postgres-release?v=30
  version: "30"

【中略】

variables:
- name: postgres_password
  type: password
- name: token_signing_key
  type: rsa
- name: tsa_host_key
  type: ssh
- name: worker_key
  type: ssh

Succeeded
```

### デプロイの結果何が動いているのか

BOSHを使ってOpenStack上に3つのVMを作成し、concourseをデプロイしました。
`bosh instances`コマンドによって参照できます。

```
$ bosh -d concourse instances

Using environment '192.168.13.70' as user 'admin'

Task 86. Done

Deployment 'concourse'

Instance                                     Process State  AZ  IPs  
db/a9a0b7ca-c89f-4335-88da-00be76c4e8a6      running        z1  10.0.0.201  
web/8352ef31-3891-4c80-8706-ae56325c4d33     running        z1  10.0.0.10  
                                                                192.168.13.80  
worker/9b93be47-15f6-454f-a20c-9793e762252d  running        z1  10.0.0.202  

3 instances

Succeeded
```

`bosh instances --ps`コマンドにより、各VM上で動作しているプロセスの状態まで参照することもできます。これは、BOSHによって作成された各VMに常駐しているBOSH agentからmonitにより監視されているプロセスの状態が定期的にBOSH directorに送信され保持されているためです。

```
$ bosh -d concourse instances --ps

Using environment '192.168.13.70' as user 'admin'

Task 87. Done

Deployment 'concourse'

Instance                                     Process       Process State  AZ  IPs  
db/a9a0b7ca-c89f-4335-88da-00be76c4e8a6      -             running        z1  10.0.0.201  
~                                            pg_janitor    running        -   -  
~                                            postgres      running        -   -  
web/8352ef31-3891-4c80-8706-ae56325c4d33     -             running        z1  10.0.0.10  
                                                                              192.168.13.80  
~                                            atc           running        -   -  
~                                            tsa           running        -   -  
worker/9b93be47-15f6-454f-a20c-9793e762252d  -             running        z1  10.0.0.202  
~                                            baggageclaim  running        -   -  
~                                            garden        running        -   -  
~                                            worker        running        -   -  

3 instances

Succeeded
```

各VMのIaaSとしてのIDやVMのタイプ(通常はフレーバと対応付けられる)まで参照したい場合は、`bosh vms`コマンドを使用します。

```
$ bosh -d concourse vms

Using environment '192.168.13.70' as user 'admin'

Task 81. Done 

Deployment 'concourse' 

Instance                                     Process State  AZ  IPs            VM CID                                VM Type  Active  
db/a9a0b7ca-c89f-4335-88da-00be76c4e8a6      running        z1  10.0.0.201     8bd0dd50-091b-43e7-a857-4e3fcaa6509b  small    true  
web/8352ef31-3891-4c80-8706-ae56325c4d33     running        z1  10.0.0.10      2988ff97-c609-4071-b70c-756f57eeb0e4  small    true  
                                                                192.168.13.80                                                   
worker/9b93be47-15f6-454f-a20c-9793e762252d  running        z1  10.0.0.202     1038fb16-6e98-4ae3-8dd3-64a17f007c58  large    true  

3 vms

Succeeded
```

さらに、各VMの基本的なメトリクス(Load Average、CPU使用率、メモリ使用率、ディスク使用率)まで参照したい場合は、`bosh vms --vitals`コマンドを使用します。これは、BOSHによって作成された各VMに常駐しているBOSH agentにおいて、当該VMの基本メトリクスを収集し、定期的にBOSH directorに送信され保持されているためです。

```
$ bosh -d concourse vms --vitals

Using environment '192.168.13.70' as user 'admin'

Task 89. Done

Deployment 'concourse'

Instance                                     Process State  AZ  IPs            VM CID                                VM Type  Active  VM Created At                 Uptime         Load              CPU    CPU   CPU   CPU   Memory        Swa      System      Ephemeral   Persistent  
                                                                                                                                                                                   (1m, 5m, 15m)     Total  User  Sys   Wait  Usage         Usage     Disk Usage  Disk Usage  Disk Usage  
db/a9a0b7ca-c89f-4335-88da-00be76c4e8a6      running        z1  10.0.0.201     8bd0dd50-091b-43e7-a857-4e3fcaa6509b  small    true    Wed Dec 12 02:05:49 UTC 2018  5d 9h 45m 58s  0.01, 0.01, 0.00  -      0.0%  0.1%  0.8%  15% (302 MB)  0% (0 B)  52% (35i%)  1% (0i%)    0% (0i%)  
web/8352ef31-3891-4c80-8706-ae56325c4d33     running        z1  10.0.0.10      2988ff97-c609-4071-b70c-756f57eeb0e4  small    true    Wed Dec 12 02:05:49 UTC 2018  5d 9h 46m 4s   0.00, 0.00, 0.00  -      0.0%  0.1%  0.4%  14% (283 MB)  0% (0 B)  52% (35i%)  3% (1i%)    -  
                                                                192.168.13.80                                                                                                                                                                                                   
worker/9b93be47-15f6-454f-a20c-9793e762252d  running        z1  10.0.0.202     1038fb16-6e98-4ae3-8dd3-64a17f007c58  large    true    Wed Dec 12 02:05:49 UTC 2018  5d 9h 45m 57s  0.00, 0.00, 0.00  -      0.0%  0.0%  0.1%  7% (536 MB)   0% (0 B)  52% (35i%)  2% (1i%)    -  

3 vms

Succeeded
```

### BOSHにおける Identifiability とは

以上のように、「何を」「どこに」デプロイしたのかを、BOSHにおいて厳密に一貫して管理しているので、仮に不具合が発生しても、どのコンポーネントのソースコードのどのバージョンの何行目に原因があるのか、あるいはいつの時点のOSベースイメージに原因があるのか、あるいはdeployment manifestによって注入した設定のどこを変えたことに原因があるのか、などを必ず見つけ出すことができます。この点において、BOSHにおいては Identifiability がとても高いレベルで達成されていると言えるでしょう。
なお、VMホストやネットワークなど、IaaS側の原因を見つけ出すことまではさすがに困難ですが、これはIaaSを直接使用する場合でも同様だと思います。


## Reproducibility

この単語は日本語に訳すと「再現可能性」ですね。

> ソースコードをはじめ、動作環境となるOSイメージ、永続的データ、外部環境との連携条件を含む構成情報まで、再現可能な形で統合管理できる

前節「Identifiability」で説明したとおり、BOSHを使用すると、何をどのようにデプロイしたのかが明確になっているため、カーネルから各種設定値に至るまでバージョンも含めて全く同一構成のシステムのクローンを再度デプロイすることが可能です。

Puppet、Chef、Ansibleなどを用いたビルドやデプロイ時に、aptやyumを使ってリモートのパッケージリポジトリからインストールを行ってしまうことがよくあると思います。このとき、manifest/cookbook/playbookで適切なバージョン固定を考慮しておかないと、全く同じ構成でのデプロイが行えず、再現性を実現できないことになります。さらに、デプロイ後に期間が空いてしまったあとに再デプロイしようとすると、前使っていたパッケージのバージョンがリモートリポジトリから消えてなくなっているということもありえるようです。

過去にデプロイしたことがあるstemcell/release/deployment manifestを残しておけば必ずシステムを再現できるというのは、特に分散システムの場合は極めてメリットが大きいと言えると思います。この点において、BOSHにおいては Reproducibility についても高いレベルで達成されていると言えるでしょう。


## Consistency

この単語は日本語に訳すと「一貫性」ですが、特にシステム運用の手順のことを指して言っています。

> ソフトウェアコンポーネントの開発から、デプロイ、更新、構成変更に至るまでの、日常的なワークフローの枠組みを提供する

`bosh deploy`という、BOSH CLIにおいて最も重要なコマンドがありますが、よくデプロイツールに求められる要件として言われる「冪等性」をもっています。
すなわち、deployment manifestによって「あるべきシステム」を定義すれば、現時点でシステムが未デプロイであっても、修正デプロイ前であっても、デプロイ失敗状態であっても、全く同じ`bosh deploy`コマンドを実行することにより、デプロイ成功の暁には「あるべきシステム」が稼働している状態にすることができる、ということです。


## Agility

これはおなじみの単語で、日本語では「俊敏さ」「軽快さ」という意味ですが、ザックリ言ってしまえば、アジャイル開発のプラクティスへの適合性を指しているということですね。

> ソフトウェアリリースの作成自動化やシンプルなコマンドによる複雑なシステムの更新の実現により、CI/CDなど日々進化するベストプラクティスとの統合を可能にする

`bosh deploy`というシンプルなコマンド一発で複雑なシステムの構築や更新が行えるというのはこれまでも説明してきた通りです。concourseやJenkinsなどのCI/CDツールと組み合わせ、stemcell/release/deployment manifestの更新を契機としたアップデートなどの自動化を図ることもできます。実際に、[Concourseを使ってPivotal CFのアップデートの自動化を図る例](https://github.com/pivotal-cf/pcf-pipelines)も存在します。
さらに「ソフトウェアリリース」、すなわちBOSHによるデプロイの材料であるreleaseに対する「作成自動化」についても、BOSH CLIを使い、concourseやJenkinsなどのCI/CDツールと組み合わせて自動化を図ることができます。
BOSH CLIがサポートするrelease周りの操作については[このへん](https://bosh.io/docs/cli-v2/#release-creation)や[このへん](https://bosh.io/docs/create-release/)を熟読して理解を進めることになります。

## さいごに

冒頭で紹介した5つの「あるある」は、BOSHを使うことで以下のように解消されます。

1. ○○さんしかリリース作業ができないんだよなあ
    * どのstemcell/release/deployment manifestを使ってリリースするのかが分かれば、あとは`bosh deploy`コマンドを叩くだけだから。大丈夫だから。
 
1. リリース作業自体がとても面倒なので、いっそのことリリースの頻度を減らしてしまおう
    * `bosh deploy`コマンドを叩くだけだから。もうめんどくさくないから。

1. 自分の環境では動いたんだけどなあ
    * 同じstemcell/release/deployment manifestを使って同じIaaSにデプロイしたら、IaaSに不具合がない限りは必ず同じように動くから。いいからやってみなよ。

1. リリース前テストは通ったのに商用環境だとなぜか動かないんだよなあ
    * テスト環境と商用環境で同じIaaSを使ってる限り、必ず同じように動くから。もう心配すんなよ！

1. ○○パッケージの○○バージョンがインストールされてないとちゃんと動かないということが、不具合解析の結果やっとわかったんだよなあ
    * いいから、BOSH使えよ！

## 今回は以上です