TerraformでCoreOSクラスタを構築する

CoreOSDigitalOceanAmazon EC2OpenStackなどあらゆるクラウドサービスやプラットフォームで動かすことができる.1つのCoreOSクラスタを複数のクラウドサービスや自社のベアメタルサーバーにまたがって構築することもできるし,それが奨励されている.また,クラスタのマシンの数はサービスの成長や負荷状況によって増減させる必要もある.

このようなCoreOSクラスタの構築を簡単に,かつInfrastructure as Code的に再現可能な形で行いたい場合,HashicorpのTerraformを使うのがよさそう(個人的に試しているだけなので数百規模のマシンではなく,数十規模の話.もし膨大なマシン数になったときにどうするのがよいのか知見があれば知りたい).

以下では,Terraformを使ってDigitalOcean上にCoreOSクラスタを構築する方法について書く.コードは全て以下のレポジトリにある.

CoreOSの設定ファイル

まず,CoreOSの設定ファイルであるcloud-configを準備する.

#cloud-config

coreos:
  etcd:
    discovery: https://discovery.etcd.io/XXXX
    addr: $private_ipv4:4001
    peer-addr: $private_ipv4:7001
  fleet:
    public-ip: $private_ipv4
    metadata: role=lb,provider=digitalocean
  units:
    - name: etcd.service
      command: start
    - name: fleet.service
      command: start
  update:
    group: alpha
    reboot-strategy: best-effort

cloud-configの設定項目についてはweb上に良い記事がたくさんあるのでここでは詳しくは書かない.ただしfleet.metadataroleproviderといった値を書いておくと,fleetでスケジューリングを行うときにより柔軟な設定ができるようになるので,状況に合わせて既述しておくとよい.

Terraformの設定ファイル

次にTerraformの設定ファイルである.tfファイルを準備する.ここでは以下の2つのファイルを準備する.

  • variables.tf - 外部から与える設定値を定義する
  • main.tf - ProviderResourceを定義する

(Terraformは実行時にカレントディレクトリの.tfファイルを全て読み込むのでファイルの分割は自由にやってよい.すべてを1つの.tfファイルに書いてしまうことも可能.ただし,Providerやマシンの数が増えると管理がキツくなるので適宜分けるのがよい)

variable.tf

まず,外部から与える設定値をvariables.tfに定義する.ここには,例えばDigitalOceanのAPI Tokenのような設定ファイルには直接書きたくない設定値を定義し,コマンド引数-varでそれを受け取れるようにする.

variable "digitalocean_token" {
  description = "DigitalOcean API token"
}

variable "ssh_key_id" {
  description = "ID of the SSH key to use DigitalOcean"
}

ssh_key_idはDigitalOceanに登録してあるSSH KeyのIDで以下で取得できる.

$ curl -X GET \
     -H "Authorization: Bearer ${DIGITALOCEAN_TOKEN}" \
     "https://api.digitalocean.com/v2/account/keys" | jq .

main.tf

次にmain.tfProviderResourceを定義する.今回の場合はDigitalOceanのResourceにはDropletの定義,例えばRegionやイメージのサイズなどを既述する.ここでは例としてCoreOSのStableイメージを2つ立ち上げる.

# Configure the DigitalOcean Provider
provider "digitalocean" {
  token = "${var.digitalocean_token}"
}

resource "digitalocean_droplet" "lb1" {
  name = "lb1"
  image = "coreos-stable"
  private_networking = true
  region = "sgp1"
  size = "512mb"
  ssh_keys = ["${var.ssh_key_id}"]
  user_data = "${file("cloud-config.yml")}"
}

resource "digitalocean_droplet" "web1" {
  name = "web1"
  image = "coreos-stable"
  private_networking = true
  region = "sgp1"
  size = "512mb"
  ssh_keys = ["${var.ssh_key_id}"]
  user_data = "${file("cloud-config.yml")}"
}

基本の設定項目はCoreOS以外のイメージを使った場合と同じだが,CoreOSの場合はuser_dataを使ってcloud-configを渡すようにする.Terraformのfile関数を使ってファイルの中身を与える.

クラスタの構築

実際にクラスタを立ち上げる..tfファイルのあるディレクトリで以下を実行する.まず,planで実行を確認する.

$ terraform plan \
    -var digitalocean_token=${YOUR_TOKEN} \
    -var ssh_key_id=${YOUR_SSH_KEY_ID}

良さそうなら以下で適用し,クラスタを立ち上げる.

$ terraform apply \
    -var digitalocean_token=${YOUR_TOKEN} \
    -var ssh_key_id=${YOUR_SSH_KEY_ID}

これでDigitalOcean上にCoreOSクラスタが立ち上がる.

とても簡単.さらに今どのような構成でクラスタが動いているのか,コードから確認することもできるし,terraform showコマンドでみることもできる.

クラスタのマシンの増減させる

クラスタのマシンを増減させたいときは,main.tfresourceを増やせばよい.resourceを書いただけマシンが立ち上がる.逆にresourceを消せば,次の実行時にはクラスタのメンバからは消える.

またDigitalOcean以外に,例えばAWSなどにマシンを立てたければ,ProviderとそのResourceを追加すればよい.自社のサーバーを使いたいときなどにProviderが提供されてなければPluginを作ればよい(参考,“TerraformのProviderを作った - tkak’s tech blog”).

落ち穂拾い

本筋とは外れるがいくつか軽い話題を書いておく.

DNSimpleと連携する

Terraformを使ってCoreOSクラスタのあるマシンをDNSimpleのレコードに登録するには,以下のような.tfファイルを準備し,上記の.tfファイルと一緒に実行すればよい.例えばlb1というResource名で立ち上げたマシンを登録する.

# Configure the DNSimple Provider
provider "dnsimple" {
  token = "${var.dnsimple_token}"
  email = "${var.dnsimple_email}"
}

# Add a record to the domain
resource "dnsimple_record" "coreos-test" {
  domain = "coreos-test.com"
  name = "@"
  value = "${digitalocean_droplet.lb1.ipv4_address}"
  type = "A"
  ttl = 3600
}

terraform.tfvarsを使う

Terraformを実行しているときに鬱陶しいなと思うのが-var引数.Providerが増える度にどんどん増えるし,覚えきれない.シェルスクリプトを書いても良いが,Terraformはterraform.tfvarsというファイルを準備すれば,それを読み込んでくれるという仕組みを持つ.

例えば,上記の場合digitalocean_tokenssh_keyを毎回与えないといけないが,以下のようなterraform.tfvarsを準備すると,-var引数なしで実行できるようになる(もちろんこのterraform.tfvarsはレポジトリからは外す).

digitalocean_token="YOUR_TOKEN"
ssh_key_id="YOUR_SSH_KEY_ID"

今後は,Packerのように${env.NAME}で環境変数から値を設定できるようなるかもしれない.

まとめ

Terraformを使ってDigitalOcean上にCoreOSクラスタを構築する方法について書いた.この方法は.tfファイルのresourceを増やすことでマシンを増減させる.ので,最初に書いたように数百規模ではなく,数十規模のマシン数では十分に使えるし,管理も楽にできる.

さらにマシン数が増えるなら.tfファイルをプログラムで生成するなどの工夫が必要かなと思う.

参考