TerraformでのプロビジョニングにVNCを使う

TerraformでVNCでのプロビジョニングを行えるようにするプラグインterraform-provisioner-vncを公開しました。

github.com

Terraformでのプロビジョニング

Terraformではリソースの初期設定や削除時のクリーンアップ処理などを行えるようにプロビジョナーという仕組みが用意されています。

www.terraform.io

Terraform v0.12.1の時点では以下のようなプロビジョナーが利用可能となっています。

  • chef Provisioner
  • file Provisioner
  • habitat Provisioner
  • local-exec Provisioner
  • remote-exec Provisioner
  • salt-masterless Provisioner

SSH/WinRMが利用できないリソースのプロビジョニングは?

プロビジョナーのうちlocal-exec以外はSSHまたはWinRM(SSHのみの場合もある)でリモートに接続してなんらかの処理を行うようになっています。 これはSSH/WinRMが利用できないリソース、例えば初期状態ではリモート接続を許可していないようなリソースではこれらを利用できないということです。

この問題の解決方法の一つとして、VNC経由でコマンドを実行するのが今回公開したterraform-provisioner-vncです。 Packerでいうboot_commandを実行するものとなっており、主にSSH/WinRM出来るまでの環境を整えることを目的としています。

terraform-provisioner-vncの使い方

通常のプロビジョナーと同じく、対象のリソースやnull_resourceにprovisionerブロックを記載することで利用します。 例えばさくらのクラウドのサーバだと以下のように使います。

resource sakuracloud_server "example" {
  name     = "example"
  # ...中略

  provisioner vnc {
    host      = self.vnc_host
    port      = self.vnc_port
    password  = self.vnc_password
    boot_wait = "10s"

    # ここに実行したいコマンドを記載していく
    inline = [
      "<tab><wait>",
      "text ks=http://${var.ip}:${var.port}/anaconda-ks.cfg<enter>",
    ]
  }
}

remote-execと同じく、inline(文字列リストで指定)、script(スクリプトファイルのパスで指定)、またはscripts(ファイルパスのリストで指定)で実行するスクリプトを指定できます。
記載できる内容はPackerでのboot_commandと互換性があり、<enter>などの特殊キーもサポートしています。(入力処理はPackerそのものを利用してます)

指定できるスクリプトについてはPackerのQEMU Builderのboot_commandについてのドキュメントを参照してください。

www.packer.io

terraform-provisioner-vncのインストール

リリースページからバイナリファイルをダウンロードし~/.terraform.d/plugins配下に配置すればOKです。

利用例

利用例として、さくらのクラウド上のサーバでk3OSを起動〜ディスクへのインストールまでを行う例を挙げておきます。

参考: k3os on さくらのクラウド - febc技術メモ

k3OSをISOイメージから起動した場合、公開鍵を登録する or rancherユーザーのパスワードを設定するまでSSH接続できません。
このため、VNCプロビジョナーを用いてOSのインストール処理〜GitHubから公開鍵の取得/登録などを行います。
OSのインストール後はSSHプロビジョナーで接続して任意のプロビジョニングが実行できます。 この例ではkubectlコマンドを実行してみています。

variable server_name {
  default = "example"
}

variable github_username {
  default = "yamamoto-febc"
}

provider sakuracloud {
  zone = "is1b"
}

data sakuracloud_cdrom k3os {
  name_selectors = ["k3OS"]
}

resource sakuracloud_disk "disk" {
  name = var.server_name
}

resource sakuracloud_server "k3os" {
  name     = var.server_name
  core     = 2
  memory   = 4
  disks    = [sakuracloud_disk.disk.id]
  cdrom_id = data.sakuracloud_cdrom.k3os.id

  # VNC経由でOSのインストール処理
  provisioner vnc {
    host      = self.vnc_host
    port      = self.vnc_port
    password  = self.vnc_password
    boot_wait = "40s"

    inline = [
      "rancher<enter>",
      "<wait5>",

      "sudo os-config<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "y<enter>",
      "<wait5>",

      "${var.github_username}<enter>", # terraformの$記法も使えるしコメントも書ける
      "<wait10s>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait5>",

      "<enter>",
      "<wait10s>",

      "y<enter>",
      "<wait5>",
    ]
  }

  # VNCでプロビジョニング後はSSHで接続してプロビジョニング
  connection {
    type        = "ssh"
    host        = self.ipaddress
    user        = "rancher"
    private_key = file("/Users/kaz/.ssh/id_rsa")
    script_path = "/home/rancher/bootstrap.sh"
  }
  provisioner "remote-exec" {
    inline = [
      "echo ${self.ipaddress} `hostname` | sudo tee -a /etc/hosts", # /etc/hostsに自身のホスト名を追記
      "kubectl cluster-info",
      "kubectl get cs",
      "kubectl version",
    ]
  }
}

終わりに

使い所は限られますが、Packerを使うまでもないちょっとしたコマンドを実行しておきたいケースに便利だと思います。

なお、TerraformでのCustom Provisionerの仕組みはこちらにコメントされているように将来的に大きく変わる可能性もある点は注意しておいてください。
(利用する側としてはあまり関係ないかもしれませんが)

github.com

以上です。