CoreOS の自動アップデートを理解する


## 概要
CoreOS は以下の画像のようにルートパーティションを 2 つ持っており、いわば OS のアップデートをブルーグリーンデプロイする。その自動アップデートの仕組みを理解し、状況に合わせたアップデート戦略を設定できるようにする。

<img src="https://coreos.com/assets/images/media/update-diagram.png" height=400px>

## 背景
CoreOS が実現したいものの一つとしてインターネットのバックエンドにあるインフラをもっとセキュアにしたいというのがある。個人的な印象として、セキュリティパッチを継続的にプロダクション環境に当て続けるのはそれなりの工夫やエネルギーを注ぐ必要があると思う。日々セキュリティ系以外の仕事もこなす必要があるはずなので、少なくとも何も考えずに運用しているだけでは対応できない。CoreOS は継続的にアップデートする方法として[自動アップデート](https://coreos.com/using-coreos/updates) というアプローチを取る。これはブラウザの Chrome が自動でバージョンアップしているのと（技術的にも）同じ話。

## アップデートの基本的な仕組み
まず、アップデートは常にアップデート郡が利用可能になったときに passive なパーティション（今 active じゃない方）にダウンロードされる。OS のリブートはアップデートの最後のステップとして実行され、active なパーティションと passive なパーティションが交換されて完了する。

## 4 種類のアップデート戦略

| アップデート戦略 | 説明 |
|---------------|------|
| `best-effort` | デフォルトの戦略。etcd が動いている場合は、 `etcd-lock` が実行される。そうでない場合は単純に `reboot` がかかる。
| `etcd-lock` | etcd に分散ロックをかけた上でリブートする。|
| `reboot` | アップデートが適用されたらすぐにリブートをかける。|
| `off` | アップデートが適用されたあとに自動でリブートをしない |


### Best Effort
クラスタのメンバーとしてそのマシンが動いているかどうかを etcd が起動しているかで判断し、クラスタのメンバであったら場合は以下の `etcd-lock` 戦略を実行。そうでなければ `reboot` を実行。

### etcd-Lock
`etcd-lock` 戦略は、各マシンを取得し、それらにリブートを許可する前にリブートロックを行う。
この戦略の主なゴールとしては、クラスタ上で動いているサービスのキャパシティを一気に減らしたり、クラスタを形成するのに必要なメンバー数以下にならないようにしながらクラスタに素早くアップデートを適用すること。
リブートロックはそのマシンのアップデートが無事に完了するまでの間かかっている。

### Reboot Immediatedly
`reboot` 戦略は名前の通りで、アップデートが passive パーティションにインストールされるとすぐにリブートがかかる。もしクラスタ上で動いているアプリケーションが回復性に優れている（リブートしてもちゃんと立ち上がり直すとか、さらに立ち上がりも速いとか）ならばこの戦略がよい。

### Off
`off` 戦略は単純。アップデートが passive パーティション上にインストールされ、アップデートを完了させるために手動でリブートがかかるのを待つ。通常のオペレーションワークフローの一部としてリブートを頻繁にかけているのでなければこの戦略はおすすめしない。

## アップデート戦略の設定を行う
アップデート戦略は [cloud-config](https://coreos.com/docs/cluster-management/setup/cloudinit-cloud-config/#coreos) にて定義できる。

```bash
#cloud-config
coreos:
  update:
    reboot-strategy: best-effort
```

### オプション

コマンドライン `locksmithctl` から「一度に何台までリブートをかけて良いか」を設定できる。

```bash
$ locksmithctl set-max 4
Old: 1
New: 4
```

この設定は etcd に保存されるため、その他のマシンで作業を行う必要はない。

利用可能なスロット数を見てクラスタ内のどのマシンにロックがかかっているか見るには

```bash
$ locksmithctl status
Available: 1
Max: 1
```

必要ならばマシン ID を渡すことで手動でリブートロックをクリアすることもできる。

```bash
locksmithctl unlock 69d27b356a94476da859461d3a3bc6fd
```


## 実際に自動アップデートさせてみる
[coreos-vagrant](https://github.com/coreos/coreos-vagrant) を使い、チャネルの最新バージョンではないクラスタを起動し自動アップデートを待つ。
（coreos-vagrant の導入・使い方に関しては [Vagrant(Virtualbox) 上で CoreOS を動かす](http://qiita.com/spesnova/items/17c3702b22f332352c9c) を参照）

```ruby
# config.rb
$num_instances=4
$update_channel='alpha'
```

etcd の discovery token を取得して

```bash
$ curl -s https://discovery.etcd.io/new
```

 `user-data` にセット

```bash
#cloud-config

coreos:
  update:
    group: alpha
    reboot-strategy: best-effort
  etcd:
    # generate a new token for each unique cluster from https://discovery.etcd.io/new
    # WARNING: replace each time you 'vagrant destroy'
    discovery: https://discovery.etcd.io/xxxxxxxxxxxxxxxxxxxxxxxxxxx
    addr: $public_ipv4:4001
    peer-addr: $public_ipv4:7001
  fleet:
    public-ip: $public_ipv4
  units:
    - name: etcd.service
      command: start
    - name: fleet.service
      command: start
    - name: docker-tcp.socket
      command: start
      enable: true
      content: |
        [Unit]
        Description=Docker Socket for the API

        [Socket]
        ListenStream=2375
        Service=docker.service
        BindIPv6Only=both

        [Install]
        WantedBy=sockets.target
```

CoreOS クラスタを起動する

```bash
$ vagrant up
```

アップデート戦略を確認


```bash
core@core-01 ~ $ cat /etc/coreos/update.conf
GROUP=alpha
REBOOT_STRATEGY=best-effort

core@core-01 ~ $ locksmithctl status
Available: 1
Max: 1
```

現在のバージョンを確認

```bash
core@core-01 ~ $ cat /etc/os-release
NAME=CoreOS
ID=coreos
VERSION=490.0.0
VERSION_ID=490.0.0
BUILD_ID=
PRETTY_NAME="CoreOS 490.0.0"
ANSI_COLOR="1;32"
HOME_URL="https://coreos.com/"
BUG_REPORT_URL="https://github.com/coreos/bugs/issues"
```

使用中のパーティションを確認

```bash
core@core-02 ~ $ sudo findmnt -n --raw --output=source --target=/usr
/dev/sda3
```

```bash
core@core-01 ~ $ sudo cgpt show -v /dev/sda3
       start        size    part  contents
      270336     2097152       3  Label: "USR-A"
                                  Type: Alias for coreos-rootfs
                                  UUID: 7130C94A-213A-4E5A-8E26-6CCE9662F132
                                  Attr: priority=1 tries=0 successful=1
core@core-01 ~ $ sudo cgpt show -v /dev/sda4
       start        size    part  contents
     2367488     2097152       4  Label: "USR-B"
                                  Type: Alias for coreos-rootfs
                                  UUID: E03DD35C-7C2D-4A47-B3FE-27F15780A57C
                                  Attr: priority=0 tries=0 successful=0
```

`Attr` に関して

* `priority` が大きい方が最初に active パーティションとして選択される
* `tries` の数だけリトライされ、0 になってもダメな場合は、次の priority をブートする。
* `successful=1` なもはブート可能のものと判断され、使い続けられる。

（[メモ：CoreOS の自動アップデートを手動でロールバックする](http://qiita.com/yungsang/items/cb56a4ce7d4cadec3826)より引用）


fleet 経由で unit を起動し、リブート時の挙動を見てみる。

```bash
# /etc/systemd/system/example@.service
[Unit]
Description=example

[Service]
ExecStartPre=-/usr/bin/docker kill example-%i
ExecStartPre=-/usr/bin/docker rm example-%i
ExecStart=/usr/bin/docker run --rm --name example-%i coreos/example:1.0.0
ExecStop=/usr/bin/docker stop example-%i
```

```bash
core@core-01 /etc/systemd/system $ fleetctl submit example@.service
core@core-01 /etc/systemd/system $ fleetctl start example@{1..4}.service
Unit example@3.service launched on e332f616.../172.17.8.101
Unit example@1.service launched on 056fd77b.../172.17.8.103
Unit example@2.service launched on 1b5490ea.../172.17.8.104
Unit example@4.service launched on f34ed28e.../172.17.8.102
```

```bash
core@core-01 ~ $ fleetctl list-units
UNIT			MACHINE				ACTIVE	SUB
example@1.service	056fd77b.../172.17.8.103	active	running
example@2.service	1b5490ea.../172.17.8.104	active	running
example@3.service	e332f616.../172.17.8.101	active	running
example@4.service	f34ed28e.../172.17.8.102	active	running
```

しばらく待つと自動アップデートがかかり以下のメッセージが出る。

```bash
Broadcast message from locksmithd at 2014-12-01 13:05:44.068872392 +0000 UTC:
System reboot in 5 minutes!

Connection to 127.0.0.1 closed by remote host.
Connection to 127.0.0.1 closed.
```

リブート中は core-01 上の unit が他のマシンに退避される

```bash
core@core-01 ~ $ fleetctl list-units
UNIT			MACHINE				ACTIVE	SUB
example@1.service	056fd77b.../172.17.8.103	active	running
example@2.service	1b5490ea.../172.17.8.104	active	running
example@3.service	f34ed28e.../172.17.8.102	active	running
example@4.service	f34ed28e.../172.17.8.102	active	running
```

リブートが終わると CoreOS がバージョンアップされている

```bash
core@core-01 ~ $ cat /etc/os-release
NAME=CoreOS
ID=coreos
VERSION=509.1.0
VERSION_ID=509.1.0
BUILD_ID=
PRETTY_NAME="CoreOS 509.1.0"
ANSI_COLOR="1;32"
HOME_URL="https://coreos.com/"
BUG_REPORT_URL="https://github.com/coreos/bugs/issues"
```

パーティションも交換されてる

```bash
core@core-01 ~ $ sudo findmnt -n --raw --output=source --target=/usr
/dev/sda4
```

`USR-B` パーティションの `priority=2` になり、`successful=1` となっている

```bash
core@core-01 ~ $ sudo cgpt show -v /dev/sda3
       start        size    part  contents
      270336     2097152       3  Label: "USR-A"
                                  Type: Alias for coreos-rootfs
                                  UUID: 7130C94A-213A-4E5A-8E26-6CCE9662F132
                                  Attr: priority=1 tries=0 successful=1
core@core-01 ~ $ sudo cgpt show -v /dev/sda4
       start        size    part  contents
     2367488     2097152       4  Label: "USR-B"
                                  Type: Alias for coreos-rootfs
                                  UUID: E03DD35C-7C2D-4A47-B3FE-27F15780A57C
                                  Attr: priority=2 tries=0 successful=1
```

## Memo
### 自動アップデートに失敗してる事例も見受けられる

> We began with one-machine-at-a-time restarts
> Fleet got into a bad state once after a big update to it (alpha channel). We never figured out what happened. Now we disable restarts and do planned updates
[CoreOS in Production](http://slides.com/lukebond/coreos-in-production#/)

### TODO: locksmith について記事を書く
### TODO: Anatomy of a CoreOS Update のメモを追記

## REF

* [FastPatch](https://coreos.com/using-coreos/updates/)
* [Update Strategies](https://coreos.com/docs/cluster-management/setup/update-strategies/)
* [Anatomy of a CoreOS Update](http://www.youtube.com/watch?v=JeICd9XyXfY)
* [Recoverable System Upgrades](https://coreos.com/blog/recoverable-system-upgrades/)
* [coreos/locksmith](https://github.com/coreos/locksmith)
* [CoreOS in Production](http://slides.com/lukebond/coreos-in-production#/)
* [メモ：CoreOS の自動アップデートを手動でロールバックする](http://qiita.com/yungsang/items/cb56a4ce7d4cadec3826)