概要
CoreOS は以下の画像のようにルートパーティションを 2 つ持っており、いわば OS のアップデートをブルーグリーンデプロイする。その自動アップデートの仕組みを理解し、状況に合わせたアップデート戦略を設定できるようにする。
背景
CoreOS が実現したいものの一つとしてインターネットのバックエンドにあるインフラをもっとセキュアにしたいというのがある。個人的な印象として、セキュリティパッチを継続的にプロダクション環境に当て続けるのはそれなりの工夫やエネルギーを注ぐ必要があると思う。日々セキュリティ系以外の仕事もこなす必要があるはずなので、少なくとも何も考えずに運用しているだけでは対応できない。CoreOS は継続的にアップデートする方法として自動アップデート というアプローチを取る。これはブラウザの 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 にて定義できる。
#cloud-config
coreos:
update:
reboot-strategy: best-effort
オプション
コマンドライン locksmithctl
から「一度に何台までリブートをかけて良いか」を設定できる。
$ locksmithctl set-max 4
Old: 1
New: 4
この設定は etcd に保存されるため、その他のマシンで作業を行う必要はない。
利用可能なスロット数を見てクラスタ内のどのマシンにロックがかかっているか見るには
$ locksmithctl status
Available: 1
Max: 1
必要ならばマシン ID を渡すことで手動でリブートロックをクリアすることもできる。
locksmithctl unlock 69d27b356a94476da859461d3a3bc6fd
実際に自動アップデートさせてみる
coreos-vagrant を使い、チャネルの最新バージョンではないクラスタを起動し自動アップデートを待つ。
(coreos-vagrant の導入・使い方に関しては Vagrant(Virtualbox) 上で CoreOS を動かす を参照)
# config.rb
$num_instances=4
$update_channel='alpha'
etcd の discovery token を取得して
$ curl -s https://discovery.etcd.io/new
user-data
にセット
#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 クラスタを起動する
$ vagrant up
アップデート戦略を確認
core@core-01 ~ $ cat /etc/coreos/update.conf
GROUP=alpha
REBOOT_STRATEGY=best-effort
core@core-01 ~ $ locksmithctl status
Available: 1
Max: 1
現在のバージョンを確認
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"
使用中のパーティションを確認
core@core-02 ~ $ sudo findmnt -n --raw --output=source --target=/usr
/dev/sda3
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 の自動アップデートを手動でロールバックするより引用)
fleet 経由で unit を起動し、リブート時の挙動を見てみる。
# /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
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
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
しばらく待つと自動アップデートがかかり以下のメッセージが出る。
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 が他のマシンに退避される
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 がバージョンアップされている
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"
パーティションも交換されてる
core@core-01 ~ $ sudo findmnt -n --raw --output=source --target=/usr
/dev/sda4
USR-B
パーティションの priority=2
になり、successful=1
となっている
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