CoreOS 入門
CoreOS は Alex Polvi が設立した会社であり、OS、新しい Linux Distribution である。OSS で公開されている。
Polvi 氏といえば Rackspace に 買収された CloudKick を立ち上げ、その後も Rackspace 働いていたクラウドの専門家とも言えるだろう。
その Polvi 氏以外にも Googler や Linux 関連の人材、アドバイザーに Linux の stable branch のメンテナ
を迎えるなど、Linux に関する知識がかなり豊富なメンバーが集まっている。
その彼らが作っているのが CoreOS である。
CoreOS は Google や Facebook などの環境を参考にしており、柔軟にスケールし、さらにはインフラ構築その
もののプロセス自体も効率よく合理的に行えるよう設計されている。
また運用、管理(セキュリティ)におけるコストを必要最低限に抑えるようにも考えられている。
現在のバージョンは以下である。
- Version: CoreOS 268.1.0
- Kernel: 3.13.6
- Docker: 0.9.0
4/1 Updateが入りました
- Version: CoreOS 273.0.0
- Kernel: 3.13.6
- Docker: 0.9.1
alpha ではあるがもう chaos monkey は飼っていないようなのでそれなりに使えるようである。
(100番台などでは chaos monkey がデフォルトで動作していて破滅するのを眺めるという楽しみがあった)
まだ alpha 版であるためよほど詳しくない限りまともに運用することは考えない方がよいだろう。
参考:
Linux for Massive Server Deployments
CoreOS の特徴
CoreOS の大きな特徴は以下である。
- 小さくかつ堅牢なコア
- 安全なアップデート
- アプリケーションコンテナ ( Docker )
- クラスタリング
- 分散システムツール
- カスタマイズ可能な SDK
それぞれを詳しく説明するとキリがないので大まかに各項目について触れておく。
小さく堅牢なコア
コアの Kernel 部は不必要であるような機能はそぎ落としてある。
また同時にインストールされているソフトウェアも最低限のものにとどまっており、イメージのサイズもかなり
小さく抑えられている。
(vim はインストールされている)
これによって OS 自体の管理コストが低くくなりアプリケーションの管理に集中できる。
使用されるメモリは 160M 程度とかなり小さく、仮想マシンなどで動作させるにしてもコンテナを使うにしてもかなりオーバーヘッドを
小さく抑えらている。
また最近の Linux らしく systemd を採用しており、ファイルシステムも btrfs である。
CoreOS は Chrome OS 派生の OS であり、パッケージマネージャーを持たない。
不必要にコア部にソフトウェアがインストールすることができないため、セキュリティリスクを低減できる。
またそもそも rootfs が書き込めない仕様である。
(前までは/etcすら書き込めないスパルタンな仕様)
これも同様にセキュリティリスクを低減につながっている。
CoreOS 自体に新しいソフトウェアをインストールするためには SDK を使用し、ソフトウェアをインストールしたイメージを作成し直す必要がある。
参考:
https://coreos.com/using-coreos/
安全なアップデート
上述した通り、パッケージマネージャーを持たない OS のため、アップデートは rootfs まるごと載せ替えてアップデートを行う仕組みとなる。
CoreOS は rootfs が 2 つ存在する。
Update Engine は片方にアップデートを行い、アップデート完了後、自動的に再起動が行われる。
この際に、rootfs を切り替えて起動するのである。
起動時に問題があった場合には、旧 rootfs に切り替えて起動することができる。
このようにアップデートに問題があっても簡単に元に戻せる仕組みになっている。
Update Engine は Chrome OS のものを使用している。
(omaha)
このように OS 部の管理はソフトウェアも含めまるごと管理されるため、個別にソフトウェアをアップデートするなどの細かい管理から開放され管理コストを減らすことができる。
4/1 にUpdateが入りました。
3/31 の時点で環境構築した方は自動でrebootされているはずです。
(私も確認しました)
参考:
https://coreos.com/using-coreos/updates/
アプリケーションコンテナ ( Docker )
CoreOS はアプリケーションはコンテナ上でのみ動作させるように設計されている。
そのため、CoreOS 自体はアプリケーションを動かすための環境としてはかなり貧弱と言えるかも知れない。
コンテナには Docker を採用しており、最初から Docker ありきの OS である。
CoreOS の Docker は 0.9 かつ btrfs driver を使用するという少しチャレンジな構成になっている。
CoreOS は分散システムツール fleet を使用するため、Docker のように高いポータビリティを確保する事が非常に重要になる。
参考:
https://coreos.com/using-coreos/docker/
クラスタリング
CoreOS の最も特徴的な機能のといえるのがクラスタリング機能を標準でもっている点ある。
クラスタリング機能は etcd で実現されており、それを土台とし fleet などの分散システムが構築されている。
etcd は耐故障性を持った key-value ストアのようなものである。
操作は http 経由で行い、簡単に動作を確認できるようになっている。
クラスタ上の etcd は leader, follower に分かれ、障害を検出すると自動で leader が入れかわるなどを行ってくれる。
クラスタを組む際に簡単に peers を検出するための Discovery の機能も有する。
またetcd は情報の共有に Raft というコンセンサスアルゴリズムを採用している。
CoreOS はクラウド環境でセットアップを簡単に行うための仕組みとして Cloud-Config の機能も提供する。
上述した通り、ファイルシステムはほぼ読み取り専用なため、普通にはセットアップできない事が多い。
そのため、起動時に cloud-config.yml を読み込み、設定を行う coreos-cloudinit が用意されている。
(旧バージョンでの oem の機能の多くもこれでカバーできるようになっている。)
参考:
https://coreos.com/using-coreos/etcd/
https://coreos.com/docs/#cluster-management
https://coreos.com/docs/cluster-management/setup/cloudinit-cloud-config/
分散システムツール
CoreOS では etcd が中心となり分散システムを構築している。
設定などを同期させるなどといった事も etcd 経由で行えば簡単にできる。
また、サービスのデプロイ、起動、停止といった事もできる。
これは fleet という分散システムによって実現されている。
通常、fleet は Docker と合わせて使用される。
apache を指定数分立ち上げることや、サービスを自動で別ホストにフェイルオーバーすることなどが簡単に行うことができる。
これらは Docker のポータビリティを活かして実現されている。
分散システムのため、クラスタに属しているマシンが1台あれば全て管理できるため、管理コストがかなり低減できる。
参考:
https://coreos.com/blog/cluster-level-container-orchestration/
カスタマイズ可能な SDK
CoreOS は上述した通り、パッケージマネージャーが存在せず、新しいソフトウェアをインストールすることが
実質できない。
そのため、新しいソフトウェアを使うためには新しいソフトウェアをインストールしたイメージを作成する必要
がある。
そのための仕組みが CoreOS SDK である。
こちらも OSS で提供されており個別でカスタマイズした CoreOS を作成することが可能となる。
アップデートの参照先も変更できるため、アップデートも含め独立したシステムを構築する事ができる。
SDK よりインストールするソフトウェアは Gentoo のリポジトリを参照することができ emerge でインストールする。
参考:
https://coreos.com/docs/sdk-distributors/sdk/modifying-coreos/
CoreOS のインストール、セットアップ
前置きはさておいて早速 CoreOS のインストールを行い試していこう。
一番簡単なのはクラウド環境で動作させる方法なのだが、せっかくなのでローカルLAN内に構築していこうと思
う。
さっさと試したい人は EC2 上で動作させるといいだろう。
先日、vagrant を便所に捨てたところなので libvirt を使って構築していこうと思う。
img のダウンロード
公式のドキュメントではまだ dev-channel のイメージを使用しているようだが alpha 版を使っていく。
$ wget http://storage.core-os.net/coreos/amd64-usr/alpha/coreos_production_qemu_image.img.bz2 -O - | bzcat > coreos_production_qemu_image.img
$ wget http://storage.core-os.net/coreos/amd64-usr/alpha/coreos_production_qemu.sh
$ chmod +x ./coreos_production_qemu.sh
せっかくなので一度起動してみると良いかも知れない。
$ ./coreos_production_qemu.sh -a ~/.ssh/authorized_keys -- -nographic
上述したとおり、OS のイメージに ssh のキーを送り込まないとログインすらできないので送り込んでから起動
しよう。
ssh でログインできるか確認してみる。
$ ssh -l core -p 2222 localhost
ログイン後以下が表示されるはずである。
______ ____ _____
/ ____/___ ________ / __ \/ ___/
/ / / __ \/ ___/ _ \/ / / /\__ \
/ /___/ /_/ / / / __/ /_/ /___/ /
\____/\____/_/ \___/\____//____/
core@localhost ~ $
おめでとう!これであなたも CoreOS デビューだ!
さっさと qemu のプロセスを殺して次の設定へ進もう。
cloud-config.ymlの記述, 配備
268.1.0 からは cloud-config が使用できる。
これはクラウド環境関係なく初期セットアップで使うのでこの仕組みを使ってセットアップする。
ネットワークはデフォルトで DHCP なのだが社内向けなので固定 IP を割り振る。
cloud-config.yml の例を以下に書いておく。
#cloud-config
hostname: coreos1
coreos:
etcd:
name: coreos1
discovery: http://192.168.2.XX:4001/v2/keys/cluster
addr: 192.168.2.YY:4001
peer-addr: 192.168.2.YY:7001
bind-addr: 0.0.0.0
units:
- name: etcd.service
command: start
- name: docker.service
command: start
- name: fleet.service
command: start
ssh_authorized_keys:
- ssh-rsa AAAA....
write_files:
- path: /etc/systemd/network/10-static.network
content: |
[Match]
Name=ens*
[Network]
Address=192.168.2.YY/24
Gateway=192.168.2.1
DNS=8.8.8.8
デフォルトでは service が起動しないので etcd, docker, fleet ともに立ち上げておく。
network 周りは networkd を使うように変更されたのでその設定を書いておく必要がある。
etcd の設定であるが、discovery の様子を確認するために、今回は社内に discovery 用に etcd を一台立てておくものとする。
また etcd の name 属性はユニークである必要があるので注意すること。
略するとマシンIDが使われる。マシンIDは /etc/machine_id で確認できる。マシンIDは環境を作成すると固定化されてしまうのでqemuのimgやvmを使いまわすとマシンIDが変わらず正しくクラスタが構築できないので注意すること。
rootをマウントして /etc/machine_id を削除し、再生成させるという方法もある。
static ip の場合 $private_ipv4 などが展開されないので書いておく必要がある。
ssh_authorized_key には ssh key を貼り付けておく。
これは core ユーザーのログインで使用される。
これがないとログインすらできないでくのぼうになるので必ず設定すること。
ユーザーを作りたい人は作っても良いかも知れない。
github から ssh key をインポートする便利機能があるのでかなり楽できる。
詳細は https://coreos.com/docs/cluster-management/setup/cloudinit-cloud-config/ 参照。
networkは以下を参照
https://coreos.com/docs/cluster-management/setup/network-config-with-networkd/
では cloud-config.yml をイメージに配備しよう。
$ sudo modprobe nbd max_part=63
$ sudo qemu-nbd -c /dev/nbd0 coreos_production_qemu_image.img
$ sudo mkdir -p /mnt/oem
$ sudo mount /dev/nbd0p6 /mnt/oem/
$ sudo cp cloud-config.yml /mnt/oem
$ sudo umount /mnt/oem
$ sudo sudo qemu-nbd -d /dev/nbd0
当たり前であるが、必ず umount などは行っておくこと。
初期設定を行う coreos-init は /usr/share/oem/cloud-config.yml を読み込むため、ここにファイルを配備す
る。
パーティションは p6
が /usr/share/oem
p9
が /
となっている。
どうしても /etc/
に何か書きたい場合などは p9
を mount すればよいだろう。
旧来の OEM の機能も健在である。
run.sh は coreos-cloudinit より先に読み込まれるので使用したい人は以下にシェルを仕込むと良いだろう。
(ほぼ使うことはないはずだが)
/usr/share/oem/run.sh
クラスタリングの準備
上記にも書いたが、クラスタの機能を使うため discovery 用の etcd を設定ファイルに書いたマシンに立てて
おく。
キリがないのでetcd のビルド方法や説明は割愛する。
-v
オプションをつけて起動すると JOIN してくる様子などが確認できる。
-vv
をつけるとログがでまくるのでオススメできない。
./bin/etcd -v
etcd は 0.3 以降、クラスタに属するマシンを検出するための Discovery API を提供している。
etcd は discovery オプションで設定した URL に自身の IP 情報を書き込む。
クラスタに属するマシンは設定した URL を参照すれば一目瞭然となる。
etcd はこの仕組みを使ってクラスタを構築している。
特にクラウド環境など DHCP だとどのマシンと通信していいのかわからないのでこの機能を使ってクラスタを構
築するのである。
参考:
https://coreos.com/using-coreos/etcd/
https://coreos.com/docs/cluster-management/setup/etcd-cluster-discovery/
CoreOS の起動
さて、あとは起動させるだけである。
libvirt を使って起動させるわけだが、virt-manger を使った方が手っ取り早いであろう。
イメージは qcow2 なので qcow2 を必ず指定しよう。でないと boot できない。
virsh 向けに参考用 xml を以下に書いておく。
<domain type='kvm'>
<name>CoreOS1</name>
<memory unit='KiB'>1048576</memory>
<currentMemory unit='KiB'>1048576</currentMemory>
<vcpu placement='static'>1</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-1.4'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<cpu mode='custom' match='exact'>
<model fallback='allow'>SandyBridge</model>
<vendor>Intel</vendor>
<feature policy='require' name='vme'/>
<feature policy='require' name='dtes64'/>
<feature policy='require' name='vmx'/>
<feature policy='require' name='erms'/>
<feature policy='require' name='xtpr'/>
<feature policy='require' name='smep'/>
<feature policy='require' name='pcid'/>
<feature policy='require' name='est'/>
<feature policy='require' name='monitor'/>
<feature policy='require' name='smx'/>
<feature policy='require' name='tm'/>
<feature policy='require' name='acpi'/>
<feature policy='require' name='osxsave'/>
<feature policy='require' name='ht'/>
<feature policy='require' name='pdcm'/>
<feature policy='require' name='fsgsbase'/>
<feature policy='require' name='f16c'/>
<feature policy='require' name='ds'/>
<feature policy='require' name='tm2'/>
<feature policy='require' name='ss'/>
<feature policy='require' name='pbe'/>
<feature policy='require' name='ds_cpl'/>
<feature policy='require' name='rdrand'/>
</cpu>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/bin/kvm-spice</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2' cache='writeback' io='native'/>
<source file='/xxxxx/vm/coreos/coreos_production_qemu_image.img'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</disk>
<controller type='usb' index='0'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<interface type='bridge'>
<mac address='52:54:00:36:ac:xx'/>
<source bridge='br0'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</interface>
<serial type='pty'>
<target port='0'/>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='-1' autoport='yes'/>
<sound model='ich6'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</sound>
<video>
<model type='cirrus' vram='9216' heads='1'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
</video>
<memballoon model='virtio'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</memballoon>
</devices>
</domain>
起動できたなら ssh で指定した IP にログインする。
ユーザーを作成していない場合はそのまま core ユーザーでログインすればよい。
$ ssh -C core@xxxxxxx
各種サービスの動作確認
ログイン後、各種サービスが動作しているか確認する。
まずは要である etcd の状態を確認する。
systemd で管理されているので以下のコマンドで確認する。
systemctl status <service>
core@coreos1 ~ $ systemctl status etcd
etcd.service - etcd
Loaded: loaded (/usr/lib64/systemd/system/etcd.service; static)
Drop-In: /run/systemd/system/etcd.service.d
└─20-cloudinit.conf
Active: active (running) since Sun 2014-03-30 07:54:25 UTC; 20h ago
Main PID: 2928 (etcd)
CGroup: /system.slice/etcd.service
└─2928 /usr/bin/etcd
Mar 30 15:43:25 coreos1 etcd[2928]: [etcd] Mar 30 15:43:25.465 INFO | coreos1: snapshot of 10002 events at index 60017 completed
.....
ログの内容は時間や PID は各自の環境によって異なるであろう。
active running になっているか確認すること。
起動できてない場合には以下のような表示になるはずである。
core@coreos1 ~ $ systemctl status etcd
etcd.service - etcd
Loaded: loaded (/usr/lib64/systemd/system/etcd.service; static)
Drop-In: /run/systemd/system/etcd.service.d
└─20-cloudinit.conf
Active: activating (auto-restart) (Result: exit-code) since Mon 2014-03-31 04:08:37 UTC; 2s ago
Process: 3980 ExecStart=/usr/bin/eftcd (code=exited, status=1/FAILURE)
Main PID: 3980 (code=exited, status=1/FAILURE)
Mar 31 04:08:58 coreos1 systemd[1]: etcd.service: main process exited, code=exited, status=1/FAILURE
Mar 31 04:08:58 coreos1 systemd[1]: Unit etcd.service entered failed state.
多くの場合は discovery 用の etcd と通信できてない事が原因である。
discovery 用の etcd のログを確認して以下のようにアクセスが来てなければ設定を見直す。
[etcd] Mar 31 13:12:29.583 DEBUG | [recv] PUT http://127.0.0.1:4001 /v2/keys/cluster/coreos1 [192.168.2.52:36510]
[etcd] Mar 31 13:12:29.584 DEBUG | [recv] PUT http://127.0.0.1:4001 /v2/keys/cluster/_state [192.168.2.52:36510]
cloud-config.yml の記述ミスである可能性もあり得るのでその場合は以下に設定が展開されているので確認し
てみるとよいだろう。
core@coreos1 ~ $ cat /run/systemd/system/etcd.service.d/20-cloudinit.conf
[Service]
Environment="ETCD_NAME=coreos1"
Environment="ETCD_DISCOVERY=http://192.168.2.xxx:4001/v2/keys/cluster"
Environment="ETCD_ADDR=192.168.2.xxx:4001"
Environment="ETCD_PEER_ADDR=192.168.2.xx:7001"
Environment="ETCD_BIND_ADDR=0.0.0.0"
etcd が問題なければ docker, fleet と順に確認する。
core@coreos1 ~ $ systemctl status docker
docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib64/systemd/system/docker.service; disabled)
Active: active (running) since Sun 2014-03-30 07:54:25 UTC; 20h ago
Docs: http://docs.docker.io
Main PID: 2938 (docker)
CGroup: /system.slice/docker.service
└─2938 /usr/bin/docker -d -s=btrfs -r=false -H fd://
.....
core@coreos1 ~ $ systemctl status fleet
fleet.service - fleet
Loaded: loaded (/usr/lib64/systemd/system/fleet.service; disabled)
Active: active (running) since Sun 2014-03-30 07:54:25 UTC; 20h ago
Main PID: 2943 (fleet)
CGroup: /system.slice/fleet.service
└─2943 /usr/bin/fleet
Mar 30 07:54:25 coreos1 systemd[1]: Started fleet.
Mar 30 07:54:25 coreos1 fleet[2943]: I0330 07:54:25.811560 02943 manager.go:195] Writing systemd unit file fleet-960a9f62-52a5-4c95-aaf6-e41852503436.target
ここまでこれば問題ないであろう。
同様の手順でもう 1 Node 立ち上げ、クラスタリングの機能を確認しよう。
クラスタリング機能の確認
2 つのマシンで問題なく service の確認できれば次は etcd のテストである。
etcdctl を使って値をやりとりできれば OK である。
core@coreos1 ~ $ etcdctl set /message Hello
Hello
core@coreos1 ~ $ etcdctl get /message
Hello
またetcd dashboard
を使って確認してもいいだろう。
http://<your ip>:4001/mod/dashboard/
にアクセスしてみよう。
Peer Listに etcd で指定した name が並んで見えるだろう。
(leader側じゃないと見えないかも知れない)
etcd dashboardのイメージは以下を参照。
https://www.youtube.com/watch?v=F_yeAx0l5T0
この状態であれあ fleet も認識しているであろう。
確認してみる。
core@coreos1 ~ $ fleetctl list-machines
MACHINE IP METADATA
deb19cd2... 192.168.2.53 -
960a9f62... 192.168.2.52 -
2 台正しくクラスタリングできているのが確認できた。
fleet はクラスタ上にサービスをデプロイ、起動、停止を行うツールである。
せっかくなので簡単なサービスをデプロイしてみる。
hello.service を作成して登録してみる。
アプリケーションは Docker によりポータビリティがあがっているはずなのでどこでも実行できるはずである。
core@coreos1 ~ $ cat hello.service
[Unit]
Description=MyApp
After=docker.service
Requires=docker.service
[Service]
ExecStart=/usr/bin/docker run busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
core@coreos1 ~ $ fleetctl submit hello.service
core@coreos1 ~ $ fleetctl list-units
UNIT LOAD ACTIVE SUB DESC MACHINE
hello.service - - - MyApp -
正しく unit 登録できていれば上記のように list-units で確認できる。
ではこの service を起動してみる。
service ファイル上、特に制限をかけていないため、どのマシンで実行されるかはわからない。
core@coreos1 ~ $ fleetctl start hello.service
core@coreos1 ~ $ fleetctl list-units
UNIT LOAD ACTIVE SUB DESC MACHINE
hello.service loaded active running MyApp 960a9f62.../192.168.2.52
起動が確認できたと思う。
次に停止を確認する。
core@coreos1 ~ $ fleetctl stop hello.service
core@coreos1 ~ $ fleetctl list-units
UNIT LOAD ACTIVE SUB DESC MACHINE
hello.service - - - MyApp -
2 台両方で起動するにはどうすればよいだろう。
apache.1.service apache.2.service というファイル名で以下のように記述する。
ファイルの内容は同じでよい。
[Unit]
Description=My Apache Frontend
After=docker.service
Requires=docker.service
[Service]
ExecStart=/usr/bin/docker run -name apache -p 80:80 coreos/apache /usr/sbin/apache2ctl -D FOREGROUND
ExecStop=/usr/bin/docker stop apache
[X-Fleet]
X-Conflicts=apache.*.service
まとめて登録するには以下のようにする。
core@coreos1 ~ $ vim apache.1.service
core@coreos1 ~ $ cp apache.1.service apache.2.service
core@coreos1 ~ $ fleetctl submit apache.*
core@coreos1 ~ $ fleetctl list-units
UNIT LOAD ACTIVE SUB DESC MACHINE
apache.1.service - - - My Apache Frontend -
apache.2.service - - - My Apache Frontend -
hello.service - - - MyApp -
スタートさせる際にも同じように行う。
core@coreos1 ~ $ fleetctl start apache.*
core@coreos1 ~ $ fleetctl list-units
UNIT LOAD ACTIVE SUB DESC MACHINE
apache.1.service loaded active running My Apache Frontend 960a9f62.../192.168.2.52
apache.2.service loaded active running My Apache Frontend deb19cd2.../192.168.2.53
このように fleet を使うと簡単に複数のサービスを管理することができる。
最後に
と駆け足で CoreOS を紹介して見ましたがいかがだったでしょうか?
正直、調べることが多く常人には難しいという印象がまだあります。
細かい部分で書ききれない事が多いのでそのうちまた書くとは思います。
がバージョンがあがるとまた事情が変わってくるので書かないかも知れません。
一応次は CoreOS SDK を使ってカスタマイズしたイメージの作成を書いてみたいと思います。