CoreOSに入門した

by Taichi Nakashima,

CoreOS is Linux for Massive Server Deployments · CoreOS

CoreOS + Docker Meetup Tokyo #1に参加してCoreOSにめっちゃ感動したので,CoreOSに入門していろいろ触ってみた.

まず,CoreOSの概要とそれを支える技術について説明する.次に実際にDigitalOcenan上にVagrantを使って実際にCoreOSクラスタを立てて,CoreOSで遊ぶ方法について書く.

CoreOSとは何か

CoreOSは,GoogleやFacebook,Twitterといった企業が実現している柔軟かつスケーラブル,耐障害性の高いインフラの構築を目的としたLinuxディストリビューションである.軽量かつ使い捨てを前提にしており,クラウドなアーキテクチャのベストプラクティスを取り入れている.CoreOSの特徴は大きく4つ挙げられる.

  • ミニマルなデザイン
  • 容易かつ安全なOSアップデート
  • Dockerコンテナによるアプリケーションの起動
  • クラスタリング

CoreOSはとてもミニマルである.従来のLinuxディストリビューションが機能を追加することでその価値を高めていったのに対して,CoreOSは必要最低限まで機能を削ぎ落としていることに価値がある(“CoreOS の調査:足し算から引き算へと,Linux ディストリビューションを再編する”).

CoreOSは安全かつ容易なOSアップデート機構を持っている.これにはOmahaというChromeOSやChromeの更新に利用されているUpdate Engineを使っており,RootFSを丸ごと入れ替えることでアップデートを行う.これによりShellShockのような脆弱性が発見されても,いちいちパッチを当てるといったことやらずに済む.

CoreOSは専用のパッケージマネージャーをもたない.またRubyやPythonといった言語のRuntimeも持たない.全てのアプリケーションをDockerコンテナとして動作させる.これによりプロセスの隔離と,安全なマシンリソースの共有,アプリケーションのポータビリティという恩恵を受けることができる.

CoreOSはクラスタリングの機構を標準で持っている.クラスタリングについては,先週来日していたCoreOSのKelsey氏は"Datacenter as a Computer"という言葉を使っていた.データセンターの大量のサーバー群からクラスタを構築してまるでそれが1つのコンピュータとして扱えるようにすることをゴールとしているといった説明をしていた.

CoreOSはクラウドネイティブなOSである.Amazon EC2,DigitalOcean,Rackspace,OpenStack,QEMU/KVMといったあらゆるプラットフォームが対応を始めている.1つのクラスタを異なる2つのクラウドサーバにまたがって構築することもできるし,クラウドと自社のベアメタルサーバーを使って構築することもできる.

CoreOSの特徴については,@mopemopeさんの “CoreOS入門 - Qiita”や,@yungsangさんの“CoreOS とその関連技術に関するここ半年間の私の活動まとめ”が詳しい.

CoreOSを支える技術

CoreOSを支える技術キーワードを挙げるとすれば以下の3つになる.

これらについてざっと説明する.

Docker

CoreOSは専用のパッケージマネージャーをもたない.またRubyやPythonといった言語のRuntimeも持たない.全てのアプリケーションをDockerコンテナとして動作させる.

https://coreos.com/assets/images/media/Host-Diagram.png

https://coreos.com/assets/images/media/Host-Diagram.png

Dockerを使うことで上図のようにコンテナによるプロセスの隔離と,安全なマシンリソースの共有,アプリケーションのポータビリティという恩恵を受けることができる.

etcd

CoreOSは複数のマシンからクラスタを形成する.クラスタを形成するために,CoreOSはetcdという分散Key-Valuesストアを使い,各種設定をノード間で共有する(etcdってのは"/etc distributed"という意味).

https://coreos.com/assets/images/media/Three-Tier-Webapp.png

etcdはクラスタのサービスディスカバリーとしても利用される.クラスタのメンバーの状態などを共有し,共有情報に基づき動的にアプリケーションの設定を行う.これらを行うetcdのコアはRaftのコンセンサスアルゴリズムである.Raftについては,“Raft - The Secret Lives of Data”を見るとビジュアルにその動作を見ることができる.

etcdはlocksmithというクラスタの再起動時のリブートマネジャーにも使われている.

fleet

コンテナによるサービスをクラスタ内のどのマシンで起動するかをいちいち人手で決めるわけにはいけない.クラスタ内のリソースの状態や動いているサービスに基づき,適切なマシンでコンテナを動かすスケジューリングの仕組みが必要になる.

このスケジューリングとコンテナの管理にCoreOSはfleetを用いる.fleetはクラスタ全体のinit systemとして,クラスタのプロセス管理を行う.fleetはこれを各マシンのsystemdを束ねることでこれを実現している.fleetで管理するサービスはsystemdのUnitファイルを改良したものを用いる.

https://coreos.com/assets/images/media/Fleet-Scheduling.png

https://coreos.com/assets/images/media/Fleet-Scheduling.png

例えば,上図のようにAPIサーバーを動かすコンテナを6つ,LB用のコンテナを2つ起動したいとする.fleetはマシンのリソース状態とsystemdのUnitファイルに既述した条件に基づき,これらを最も適したマシンで起動する.fleetはこれらのコンテナがどこで動いているかも常に管理しており,コンテナの停止,削除,ログの確認なども行うことができる.

もしマシンに障害が発生してもfleetは自動でフェイルオーバーを行う.適切なマシンを選択し直し,登録された分のコンテナが起動していることを保証する.

CoreOSの起動

CoreOSはcloud-configという設定ファイルを使って起動する.このファイルにより,CoreOSはクラスタ内の他のメンバーと接続し,基本的なサービスを起動し,重要なパラメータを設定する.

cloud-configcloud-initにインスパイアされている.cloud-initはCoreOSが使わないツールも含むので,cloud-configは必要最低限なものだけを実装したサブセットになる.さらに,例えばetcdの設定,systemdのunitなどといったCoreOS特有の設定項目も持つ.

最小限には,cloud-configは,ホストに既存のクラスタへの参入方法とetcdfleetの2つのサービスの起動方法を伝えればよい(これらは全て関連している).これにより,新しいホストを,既存のサーバーに接続させ,設定する必要のあるツールを提供する.基本的に,これらがCoreOSノードを既存クラスタへ参入させるための要件となる.

CoreOSクラスタを立てる

tcnksm/vagrant-digitalocean-coreos

Vagrantを使ってDigitalOcean上にCoreOSのクラスタを立ち上げてみる(ソースは全て上記のレポジトリにある).

SSHの準備

まず,全てのCoreOSは少なくとも1つのSSH公開鍵が登録されている必要がある.DigitalOceanの場合は,公開鍵をDropletsに登録しておき,以下で秘密鍵をエージェントに登録しておく必要がある.

$ ssh-add

fleetを使った他nodeへの接続でもCoreOSはsshを使う.CoreOSへログインした後も同様の秘密鍵の情報が利用される必要があるので,sshの際は必ず-Aオプションを指定する.

サービスディスカバリーの設定

etcdを使ってクラスタを形成するために,discovery用のサーバを別途準備する必要がある.CoreOSはhttps://discovery.etcd.ioというdiscoveryサービスを運用しており,それをそのまま使うことができる.今回はこれを利用する.

この/newページにアクセスすれば,新しいクラスタ用のTokenが生成されるので,それをdiscovery用のURLとして利用する.

$ curl -w "n" https://discovery.etcd.io/new
https://discovery.etcd.io/724ac1949c661bb3970ff8984b895bca

(1つのクラスタに付き1つの新しいTokenを生成する必要がある.また,同じIPで再びクラスタを再構成する場合も新しいTokenを取得する必要がある)

最小限のcloud-config

cloud-config#cloud-configで始まるYAMLフォーマットで既述する.以下は最小限の設定内容になる.これをuser-data.ymlという名前で作成する.

# cloud-config

coreos:
  etcd:
    discovery: https://discovery.etcd.io/c39365ee7d788fb27596150a451c2434
    addr: $private_ipv4:4001
    peer-addr: $private_ipv4:7001

  fleet:
    public-ip: $private_ipv4

units:
  - name: etcd.service
    command: start
  - name: fleet.service
    command: start

coreos.etcd.*パラメータには,etcdの設定ファイルとしてsystemdのunitファイルへ解釈される.もしプラットフォームがcoreos-cloudinitのテンプレート機能が使えるなら$public_ipv4$private_ipv4といった変数が使える.

coreos.fleet.*パラメータもcoreos.etcd.*のように,fleetの設定ファイルとして利用される.

coreos.units.*パラメータは,起動後のsystemd unitを定義する.これらは,基本的なサービスの起動を行う.nameはサービスの名前で必須,commandはunitが実行するコマンド(start, stop, reload, restart, try-restart, reload-or-restart,…)で,デフォルトは何もしない.contentを使うと直接unit fileの既述もできる.

起動後のetcdfleetの設定項目は以下で確認できる.

  • /run/systemd/system/etcd.service/20-cloundinit.conf
  • /run/systemd/system/fleet.service/20-cloundinit.conf 

Vagrantfile

vagrant-digitalocean pluginを使えばVagranを使ってDigitalOcean上にイメージを立てることができる.CoreOSには,現時点(2014年11月)では最新の494.0.0を利用する.以下のようなVagrantfileを準備する.

$num_instances = 1
$image         = '494.0.0 (alpha)'

if ENV["NUM_INSTANCES"].to_i > 0 && ENV["NUM_INSTANCES"]
  $num_instances = ENV["NUM_INSTANCES"].to_i
end

Vagrant.configure('2') do |config|
  config.ssh.username = 'core'
    (1..$num_instances).each do |i|
    config.vm.define "core-%02d"%i do |config|
      config.vm.provider :digital_ocean do |provider, override|
      override.ssh.private_key_path = '/.ssh/idrsa'
      override.vm.box               = 'digital_ocean'
      override.vm.box_url           = "https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box"
      provider.token                = ENV['TOKEN']
      provider.image                = $image
      provider.region               = 'nyc3'
      provider.size                 = '512MB'
      provider.ssh_key_name         = ENV['SSH_KEY_NAME']
      provider.setup                = false
      provider.private_networking   = true
      provider.user_data            = File.read('user-data.yml')
    end
   end
  end
end

また現時点(2014年11月)では,vagrant-digitalocean のバージョンは0.7であり,user_datacloud-configファイル)のポストに対応していない.GitHub上にある最新はそれに対応しているので,自分でビルドしてインストールする必要がある.

$ git clone https://github.com/smdahlen/vagrant-digitalocean
$ gem build vagrant-digitalocean.gemspec
$ vagrant plugin install vagrant-digitalocean-0.7.0.gem

CoreOSを起動する

では,実際に上記のuser-data.ymlVagrantfileを使ってDigitalOcean上にクラスタを立ててみる.今回は3つのインスタンスを立てる.以下のようにするだけ.

export TOKEN="YOUR_DIGITALOCEAN_TOKEN"
export SSH_KEY_NAME="YOUR_SSH_KEY"
export NUM_INSTANCES=3
$  vagrant up --provider=digital_ocean

クラスタの確認をする

クラスタがちゃんと形成されているかを確認する.core1にログインする.

$ ssh -A core@core1_public_IP

以下のコマンドでクラスタのマシンを確認する.それぞれのマシンのpeer-addrが確認できる.

$ fleetctl list-machines
MACHINE         IP              METADATA
12c469d0...     10.102.100.105  -
93093555...     10.102.101.102  -
b463af61...     10.102.101.101  -

etcdfleet,DockerといったCoreOSで重要なサービスも使えるようになっているはず.

参考