Terraform Provider実装 入門(1): Custom Providerの基礎

今回はTerraformから提供されているprovider frameworkを利用した独自のプロバイダーの実装について扱います。
カスタムプロバイダーについての基本的な知識〜実装上の注意点などをサンプル実装を通じて見ていきます。

注:この記事はTerraform v0.11に対応しています。

=== UPDATE: 2022/05
この記事執筆当時と筆者の状況が変わり、この一連の記事は完結する見込みがほぼ無くなっています。
また、Terraform側も進化しており、Terraform SDKが生まれ、SDK v2に変わり、現在ではTerraform Plugin Frameworkというものが生まれています。

この一連の記事の内容はSDK v2でも若干の調整(エラーハンドリングなど)だけで通用しますが、Plugin Frameworkについては全く新しい作りとなっていること、新規にプロバイダーを作成する場合はPlugin Frameworkの利用を検討するように公式ドキュメントに書かれていますので、これらの点に留意しつつお読みください。

www.terraform.io

=== UPDATE ここまで

はじめに

この記事は主にカスタムプロバイダーの実装をする方やAWS/GCP/Azureなどの既存のプロバイダーで発生した問題の解決のためにソースを読む/修正するといった方向けです。
このため、Terraform自体についての説明はかなり省いています。
とはいえ必要に応じて触れますので最低限以下の2点を押さえておけばOKです。

  • Terraformの基本的なコマンド(init/apply/destroyなど)の利用方法について
  • tfファイルの基本的な書き方について

Terraformを利用したことがある方であれば問題ないでしょう。

また、この記事は以下のような内容には触れず、より実装寄りの内容を中心とします。

  • どんな場合にカスタムプロバイダーが必要なのか
  • リソース提供側はどのようなAPIがあると望ましいのか

ということで早速本題に入っていきます。

目次(未確定)

1ポストでは収まらない量になりそうなので複数回に分けて投稿します。
今の所は以下のような感じになる予定です。(確定したら目次を更新します)

Terraform Custom Providerの基本

まずは前提知識としてTerraformでのプロバイダーの扱いについて押さえておきます。

前提知識: Terraformでのプロバイダーの扱い

Terraformはコアな処理を担当するTerraform本体とAWS/GCP/Azureといった各プラットフォームに依存した処理を担当するプロバイダーとで実行ファイルが分離されています。
Terraform本体は必要に応じてプロバイダーの実行ファイルを見つけだしてRPCを行います。

Terraform v0.11の時点ではRPCにnet/rpcが使われています。 v0.12以降ではgRPCに切り替える方向に進んでいます。 参考: v0.12でのprotoファイル

プロバイダーの実行ファイルの探し方についてはこの記事の本筋から外れるため省略します。 細かい例外はありますが、ひとまず以下のルールを覚えておけばOKです。

  • HashiCorp社が配布しているプロバイダーについてはterraform init時に自動でインストールされる
  • サードパーティにより配布されているプロバイダーについては以下のディレクトリから検索される

参考: https://www.terraform.io/docs/extend/how-terraform-works.html#discovery

いくつか例を通じて動きを見ておきます。

HashiCorp社により配布されているプロバイダーのインストール

この例はHashiCorp社により配布されているArukasプロバイダーをインストールする例です。

www.terraform.io

tfファイルを用意しterraform initを実行することでカレントディレクトリの.terraform/配下にプラグインがインストールされます。

# tfファイルを作成
$ echo "provider arukas{}" > test.tf

# プロバイダーをインストール
$ terraform init

# 確認
$ tree -a .

.
├── .terraform
│   └── plugins
│       └── darwin_amd64
│           ├── lock.json
│           └── terraform-provider-arukas_v1.0.0_x4
└── test.tf

なお、今回はtfファイルにproviderブロックを記載していますが、providerブロックは省略可能です。(プロバイダーの実装によります)
以下のようにいきなりリソース定義のみを書く形でもterraform init実行時にtfファイルが解析され必要なプロバイダーの検出が行われます。

resource "arukas_container" "foobar" {
  name      = "example"
  image     = "nginx:latest"
  ports = {
    protocol = "tcp"
    number   = "80"
  }
}

サードパーティにより配布されているプロバイダーのインストール

次にサードパーティにより配布されているプロバイダーをインストールする例です。
Terraform v0.11時点ではサードパーティプロバイダーを自動でインストールする仕組みはまだありません。
なので、自分でプロバイダーの実行ファイルをダウンロードし、前述の検索対象ディレクトリに格納しておく必要があります。

以下はさくらのクラウド向けプロバイダーをダウンロードして利用する例です。 ここではプロバイダーの実行ファイルは~/.terraform.d/plugins配下に格納します。

# プロバイダーの実行ファイルをダウンロード
$ curl -sL -o provider.zip https://github.com/sacloud/terraform-provider-sakuracloud/releases/download/v1.6.1/terraform-provider-sakuracloud_1.6.1_darwin-amd64.zip

# 展開して配置
$ unzip provider.zip ; rm provider.zip
$ mv terraform-provider-sakuracloud_v1.6.1_x4 ~/.terraform.d/plugins/

# tfファイルを作成
$ echo "provider sakuracloud{}" > test.tf

# プロバイダーをインストール
$ terraform init

# 確認
$ tree -a .

.
├── .terraform
│   └── plugins
│       └── darwin_amd64
│           └── lock.json
└── test.tf

先ほどと違い、.terraformディレクトリ配下にプラグインの実行ファイルが格納されないことに注意してください。

当記事ではサンプルのカスタムプロバイダーを作成し実際に動かして動作を確認していきます。
その際にこちらの方法を用いてカスタムプロバイダーをインストールし利用するようにします。

Custom Providerの実装

それでは本題のカスタムプロバイダーの実装について見ていきます。

最初に、公式ドキュメントのガイドの中にWriting Custom Providersというドキュメントが 用意されていますので目を通しておきます。

www.terraform.io

このドキュメントによると、カスタムプロバイダーを作成するには最低限以下が必要ということです。

  • (1) terraform.ResourceProviderを返すfunc
  • (2) plugin.Serve()に(1)のfuncを指定して呼び出すエントリーポイント
  • (3) terraform-provider-<プロバイダー名>という名前でビルド

それぞれを詳しく見ていきます。実際に手を動かしながら確認出来るようにソース一式を以下に準備しました。

github.com

サンプルとしてterraform-provider-minimumというカスタムプロバイダーを作成してみます。 以降はこのサンプルを元に各項目を見ていきます。

カスタムプロバイダーのサンプルのソースコードを取得

まずはサンプルをローカルマシンにクローンし、strucrureブランチをチェックアウトしてください。

# *GOPATHを設定していない場合は$HOME/goに読み替えてください*

# ソース一式を取得 & structureブランチのチェックアウト
$ go get github.com/yamamoto-febc/terraform-provider-minimum
$ cd $GOPATH/src/github.com/yamamoto-febc/terraform-provider-minimum
$ git checkout structure

以下のようなファイルが含まれているはずです。

$ tree .
 .
├── Gopkg.lock
├── Gopkg.toml
├── main.go
└── minimum
    └── provider.go

それでは順番に見ていきます。

(1) terraform.ResourceProviderを返すfunc

まずはminimum/provider.goを見てみます。

package minimum

import (
    "github.com/hashicorp/terraform/helper/schema"
    "github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
    return &schema.Provider{
        ResourcesMap: map[string]*schema.Resource{
            // ここにリソースの定義を書いていく
        },
    }
}

ここではterraform.ResourceProviderを返すfuncProvider()を定義しています。terraform.ResourceProviderはinterfaceです。

https://github.com/hashicorp/terraform/blob/v0.11.8/terraform/resource_provider.go#L19

このインターフェースを実装することでTerraform本体とのやり取りが可能になります。
実際にはこのインターフェースを実装したstructschema.Providerが用意されていますので、これを利用します。

https://github.com/hashicorp/terraform/blob/v0.11.8/helper/schema/provider.go#L25

現在はリソースの定義は空になっています。次回記事でここに定義を追記していきます。

(2) plugin.Serve()に(1)のfuncを指定して呼び出すエントリーポイント

次にエントリーポイントであるmain.goを見てみます。

package main

import (
    "github.com/hashicorp/terraform/plugin"
    "github.com/yamamoto-febc/terraform-provider-minimum/minimum"
)

func main() {
    plugin.Serve(&plugin.ServeOpts{
        ProviderFunc: minimum.Provider,
    })
}

plugin.Serveに引数として(1)で定義したProvider()を指定しています。
plugin.Serve()を実行することで、Terraform本体とRPCでやり取りするための諸々を行ってくれます。
これによりカスタムプロバイダーの作成者はRPCについて意識することなくそれぞれが担当するリソースの操作に集中できます。

(3) terraform-provider-<プロバイダー名>という名前でビルド

あとはgo buildコマンドでビルドするだけです。Terraformは前提知識の項で触れたようにカスタムプロバイダーを検出してくれる仕組みがありますが、
そのためにはカスタムプロバイダーの実行ファイルを特定のディレクトリに配置することに加え、 実行ファイルの名前をterraform-provider-<プロバイダー名>という形式にしておく必要があります。

このため、ビルド時に-oオプションで出力されるファイル名を指定する必要があります。 また、ビルドのために依存ライブラリを用意しておく必要があります。

今回はdepで依存関係を管理するようにしておきましたので、dep ensureを実行してからビルドするようにしてください。

# depがない場合は以下でインストール
# go get -u github.com/golang/dep/cmd/dep

$ dep ensure
$ go build -o terraform-provider-minimum main.go

これでカレントディレクトリにterraform-provider-minimumという実行ファイルが作成されているはずです。

余談1: カスタムプロバイダーでの依存ライブラリの管理方法

Terraform本体はgovendorを利用して依存ライブラリを管理していますが、 本体とカスタムプロバイダーは実行ファイルが分かれているためビルドさえできれば無理にgovendorを利用する必要はありません。

terraform-providers配下のプロバイダーにおいても統一されてない状況で、 AWS/GCP/AzureRMといったプロバイダーはgovendorを利用していますが、 Herokuプロバイダーなどではdepが使われています。

動作確認

ビルドしたカスタムプロバイダーが動作するか確認します。

実際にtfファイルを作成しterraform initを実行してみましょう。 terraform versionでminimumプロバイダーが表示されていればOKです。

# tfファイルを作成
$ echo "provider minimum{}" > test.tf

# プロバイダーをインストール
$ terraform init

# 確認
$ terraform version

Terraform v0.11.8
+ provider.minimum (unversioned)

余談2: プロバイダーのバージョンについて

terraform versionを実行するとminimumプロバイダーのバージョンがunversionedになっているのに気づかれたかもしれません。
実はプロバイダーの実装としてはバージョン情報を持っておらず、プロバイダーのファイル名からバージョン情報を取得しています。

先ほどArukasプロバイダーを自動でインストールする例が出てきましたが、そこではプロバイダーのファイル名は以下のようになっていました。

terraform-provider-arukas_v1.0.0_x4

プロバイダーのファイル名は正式には以下の形式を持ちます。

terraform-<プラグインのタイプ>-<名称>_v<プラグインのバージョン>_x<プラグインプロトコルのバージョン>

リリース時のバージョニング処理

HashiCorp社が配布しているプロバイダーの場合はリリース時にCIサーバによってバージョンが決定されファイル名が付与されます。
(CIにはTeamCityが利用されており、CI/CDパイプラインの中でCHANGELOG.mdの解析をしてリリースするバージョンを決定しています)

サードパーティのプロバイダーについてはこの辺の処理を自前で行う必要があります。
参考までに、さくらのクラウド向けプロバイダーではこの辺の処理を行なっていますので興味のある方はMakefileあたりから眺めてみてください。

余談3: ソースコードのレイアウト/パッケージ構成について

公式ガイドのWriting Custom Providersexampleプロバイダーとminimumプロバイダーとで ソースコードのレイアウトが違うことにお気付きの方もいらっしゃるかと思いますが、これはterraform-providersでのレイアウトに合わせているからです。

公式ガイドでのサンプルexampleプロバイダーのソースコードレイアウト
.
├── main.go
└── provider.go
minimumプロバイダーのソースコードレイアウト
 .
├── main.go
└── minimum          # プロバイダー固有コードを格納するパッケージ(リソースを追加する際もここに追加する)
    └── provider.go

公開予定の無い自作プロバイダーであればどちらを選んでも良いですが、特に理由がなければ後者のように別途パッケージを設けて その中にプロバイダー固有のコードを置き、ルートディレクトリにはmain.goだけおく形にしておくのが良さそうです。


ここまででカスタムプロバイダーとしての最低限の体裁が整いました。
しかし、リソースの実装が空ですので現時点では特に何もできない状態です。
実装は次回行います。

第1回 まとめ

今回はカスタムプロバイダーの基礎知識をおさえ、カスタムプロバイダーとしての最小限のコードを書いた上でビルドしてTerraformから利用してみました。
今回はリソースの実装が空のためtfファイルを書く機会もなかったですが、次回の記事でリソース実装を追加していくことでよりプロバイダーらしくしていきます。

以上です。

Next: リソース実装 基礎 - schema.Resourceでのリソース実装の基礎

[asin:B06XKHGJHP:detail]

「terraform-bundle」 Terraformとプロバイダーの実行ファイルのパッケージングツール

今日は使い道は限定されますが意外と便利なツールterraform-bundleの紹介です。

github.com

terraform-bundleって?

Terraformではコア部分とAWS/GCP/Azureといった各プラットフォーム固有の処理を担当する部分(プロバイダー)とで実行ファイルが分かれています。

通常、HashiCorp社により配布されているプロバイダーはterraform init時に必要な分だけ自動でhttps://releases.hashicorp.comからダウンロードされるようになっています。
また、サードパーティにより配布されているプロバイダーについては手動でプラグイン格納ディレクトリ(~/.terraform.d/plugins配下など)に配置する必要があります。

これらのプロバイダーをあらかじめダウンロードしておき、Terraform本体とパッケージングするためのツールがterraform-bundleです。
Terraform本体のソースに同梱されており、誰でも利用できるようになっています。

いつ使うの?

例えばオンプレミス上でTerraformやTerraform Enterpriseを利用する場合で、外部との接続が制限されている場合などに利用します。
また、カスタムプロバイダーの開発者であれば自身が開発したプロバイダーを配布するのに利用するといったこともあると思います。

使い方

インストール

現在はterraform-bundleはバイナリでの配布は行われていません。
このため、Go言語の開発環境を用意した上で自前でビルドする必要があります。

Goの開発環境を用意し、以下のコマンドを実行することでインストールできます。

$ go install github.com/hashicorp/terraform/tools/terraform-bundle

これで$GOPATH/bin配下にバイナリが作成されているはずです。適切にPATHを通す/PATHが通った場所に配置してください。

パッケージング

まず定義ファイルを用意する必要があります。
以下のような定義ファイルを作成しておきます。
ファイル名はなんでも良いですが、ここではexample.hclとしておきます。

terraform {
  version = "0.11.8"
}

providers {
  vsphere     = ["~> 1.8"]
  arukas      = ["~> 1.0"]
  sakuracloud = ["1.6"]
}

この例では、Terraform本体のバージョンが0.11.8、バンドルするプロバイダーとしてvspherearukassakuracloudを指定しています。 バンドルしたいプロバイダーはprovidersブロック内に記載していく形です。
プロバイダーにはバージョン番号を複数指定可能で、複数指定した場合は対応するバージョンそれぞれがバンドルされることとなります。

HashiCorp社により配布されていないカスタムプロバイダーもバンドルできますが、こちらはもう一つ作業が必要です。

カスタムプロバイダーをバンドルする場合

HashiCorp社が配布しているプロバイダーについてはterraform-bundleでパッケージングする際に自動でダウンロードしてくれますが、 カスタムプロバイダーの場合はカレントディレクトリにpluginsというディレクトリを作成してその配下にプロバイダーの実行ファイルを格納しておく必要があります。
(terraform-bundle実行時のオプションでディレクトリの変更も可能)

この例の場合はさくらのクラウド向けプロバイダーの実行ファイルをpluginsディレクトリに格納しておきます。

パッケージング実行

準備ができたら以下のコマンドでパッケージングします。

$ terraform-bundle package example.hcl

これでカレントディレクトリにterraform_0.11.8-bundlexxxx_darwin_amd64.zipというような名前のファイルが作成されるはずです。
このファイルをTerraformを実行したい環境に持っていって展開するだけです。

なお、デフォルトではterraform-bundleを実行した環境(OS/ARCH)向けにパッケージングされます。
変更する場合はオプションで-os-archを指定してください。

オンプレ版のTerraform Enterpriseで利用する場合は-os=linux -arch=amd64と指定します。

終わりに

ということで、terraform-bundleの紹介でした。
以上です。

sacloudプロダクトとの連携を強化したvagrant-sakuraの新機能

f:id:febc_yamamoto:20180910233040p:plain

今日は最近開発が再開した、Vagrantさくらのクラウドのサーバを操作するためのプロバイダーvagrant-sakuraについて、 最近追加された新機能についてご紹介します。

github.com

vagrant-sakuraって?

一言で言うと「Vagrantさくらのクラウド上のサーバを操作する機能を追加するためのプラグイン」です。
基本的な機能は以下の記事が詳しいです。

knowledge.sakura.ad.jp

vagrant-sakuraの開発再開

このvagrant-sakuraですが、元々はtsaharaさんが開発されたものです。
2016年5月にリリースされたv0.0.8以降は新規機能追加などの活動があまり行われていませんでしたが、 2018年7月にさくらのクラウド関連のユーザーコミュニティによるOSSを置いているGitHub上のオーガニゼーションsacloudリポジトリ移転がなされ、 そこから開発を再開しいくつかの機能追加/改善を行ってきました。

github.com

sacloud配下にはVagrantをはじめ、PackerやTerraformといったHashiCorpプロダクトのさくらのクラウド向けプラグインCLI Usacloudなどもあります。

vagrant-sakuraの新機能には、これらのプロダクトとの相互運用を楽にするための機能追加なども含まれています。
ということで早速新機能を紹介していきます。

sacloud/vagrant-sakuraの新機能

本日(2018/9/10)リリースした vagrant-sakura v0.1.0時点では以下のような機能が追加されています。

  • 環境変数 SAKURACLOUD_xxxからのAPIキーの読み込み機能
  • Usacloudの設定ファイル(APIキー)の読み込み機能
  • パケットフィルター/スタートアップスクリプト指定機能
  • コピー元アーカイブを種別(os_typeパラメータ)で指定する機能
  • OSの再インストール機能

順にみていきます。

環境変数 SAKURACLOUD_xxxからのAPIキーの読み込み機能

以下の環境変数APIキーの指定が可能となりました。

  • SAKURACLOUD_ACCESS_TOKEN : APIアクセストーク
  • SAKURACLOUD_ACCESS_TOKEN_SECRET: APIアクセスシークレット
  • SAKURACLOUD_ZONE: 操作対象ゾーン

これまでもSAKURA_ACCESS_TOKENといった環境変数が利用できましたが、これはvagrant-sakura独自のものでした。
追加された環境変数はUsacloudやTerraform for さくらのクラウド、Packer for さくらのクラウドといったSacloudプロダクトと共通で利用できるもので、 既にSacloudのプロダクトをご利用中の方であれば設定していることの多い環境変数です。

Vagrantで開発してたものをPackerでパッケージングし、Terraformでproduction環境に反映、運用保守はUsacloudでといった場合に それぞれでAPIキーの設定をせずとも共通の環境変数が利用できるようになったのは地味ですが意外と便利です。

Usacloudの設定ファイル(APIキー)の読み込み機能

同じくAPIキーの設定関連の機能ですが、APIキーをUsacloudの設定ファイルから自動で読み込んでくれる機能です。 Usacloudは複数のアカウントを使い分けられるようにAPIキーを設定ファイルに複数保存しておき、必要に応じて切り替えて使う機能を持っています。

今回追加された機能により、この設定ファイルをvagrantでも認識できるようになりました。

前述の機能と合わせるとAPIキーの設定は以下3通りの設定方法があることになります。

複数の方法でAPIキーを指定した場合は、より上に記載されている方法が優先されます。

使い方

VagrantfileにAPIキーの指定を行っていない場合は自動的にUsacloud設定ファイルを読み込むようになっています。

Usacloudで複数のAPIキー設定がある場合はカレントの設定を利用しようとします。 (以下の例だとuser01の設定を利用する)

# 登録されているAPIキー設定(コンフィグ)を一覧表示
$ usacloud config list

default
user01
user02

# 現在利用しているAPIキー設定を表示
$ usacloud config current

user01

Usacloud側でカレントの設定を変更するとVagrant側も追随するようになっています。

# Usacloudでカレントを変更
$ usacloud config use user02

Current profile: "user02"

# 現在利用しているAPIキー設定を表示
$ usacloud config current

user02

Usacloudではconfig edit機能で対話的にAPIキーの設定が可能となっています。複数のAPIキーを利用している場合は手軽に切り替えができるようになりますのでぜひご利用ください。

パケットフィルター/スタートアップスクリプト指定機能

結構要望があった機能です。サーバ作成時にパケットフィルタやスタートアップスクリプトを指定可能となっています。

使い方

VagrantfileにてそれぞれにIDを指定すればOKです。

Vagrant.configure("2") do |config|
  # ...

  config.vm.provider :sakura do |sakura, override|
    # ...

    #============================================
    # スタートアップスクリプトの指定
    #============================================
    # IDで指定
    sakura.startup_scripts = 123456789012
    
    # 文字列でもOK
    # sakura.startup_scripts = "123456789012"
  
    # 複数指定する場合
    # sakura.startup_scripts = [123456789012, xxx, yyy, ...]

    #============================================
    # パケットフィルタの指定
    #============================================
    sakura.packet_filter = 123456789012

  end
end

コピー元アーカイブを種別(os_typeパラメータ)で指定する機能

これまではサーバ作成時にコピー元アーカイブのIDを指定する必要がありました。

Vagrant.configure("2") do |config|
  config.vm.box = "dummy"
  config.ssh.username = "root"

  config.vm.provider :sakura do |sakura|
    sakura.disk_source_archive = 111111111111 # アーカイブのIDを指定
  
    sakura.use_insecure_key = true
  end
end

アーカイブのIDを知るにはコントロールパネルで調べたり、vagrant-sakuraのsakura-list-idコマンドやUsacloudでアーカイブを検索して調べるという作業が必要でした。
さくらのクラウドではいくつかのOSについてはパブリックアーカイブというアーカイブが提供されているため、これらのIDを指定することでサーバの作成が可能となっています。

しかし、厄介なことにパブリックアーカイブのIDはゾーンごとに異なります。
例えば石狩第2ゾーンでのCentOS7と東京第1ゾーンでのCentOS7では違うIDとなっています。
また、同じCentOS7でも、さくらのクラウド側でアップデートが行われることがあり、その際にはIDが変更されることが多いです。

なのでIDを直接指定する形だとある日突然サーバの作成が行えなくなるといったことが発生していました。

UsacloudやTerraform、Packerでも同様の問題が発生しますが、これらのプロダクトではos_typeというOSの種別を指定できる機能で対応しています。
これは、centosubuntuといったOS種別を指定すると種別に対応する各ゾーンの最新安定板のアーカイブのIDを検索し設定してくれるものです。

例えばUbuntuパブリックアーカイブの場合は16.04.4や18.04といった複数のバージョンが存在するため、それらの中から最新安定板を選ぶようにしています

vagrant-sakuraでは以下の値が指定可能です。

  • ubuntu(デフォルト)
  • centos
  • centos6
  • debian
  • coreos
  • freebsd
  • rancheros

パブリックアーカイブを利用する場合はos_typeを用い、自分で作成したアーカイブ(マイアーカイブ)を利用する場合はdisk_source_archiveを利用するという使い分けがオススメです。

OSの再インストール機能

従来はvagrant-sakuraで作成したサーバのOS再インストールを行う場合は、一旦destroyしてから再度upする必要がありました。 こうするとさくらのクラウドで割り当てられたグローバルIPが変更されてしまうことがあり、DNS登録していた場合はそちらも変更しなければいけないケースがありました。

そこで今回作成されたサーバはそのまま(もちろん割り当てられたグローバルIPは確保したまま)でOSの再インストールを行うためのサブコマンドを追加しました。

使い方

sakura-reinstallというコマンドを実行することでOS再インストールが行えます。

$ vagrant sakura-reinstall

Ansibleなどのサーバ構成管理ツールを試行錯誤しながら作る際に便利だと思います。

終わりに

ということで、今後も様々な機能追加/改善を行う予定です。 質問やバグ報告、ご要望などはぜひGitHubにIssue/PRを投げてください。

github.com

なお、Sacloudプロダクトの使い方などが質問できるSlackワークスペースもあります。

https://slack.usacloud.jp/

どなたでも参加可能になっていますので、操作方法などでお困りの方はぜひご参加ください。

以上です。

TerraformのArukasプロバイダー再始動!!

f:id:febc_yamamoto:20180905150703p:plain

TerraformのArukasプロバイダーが正式サービス版になって復活!

TerraformのArukasプロバイダーArukas正式サービス版に対応したバージョン v1.0.0をリリースしました。

www.terraform.io

Arukasプロバイダー復活までの経緯

ArukasプロバイダーはHashiCorp社がコードレビュー/テスト/リリース(配布)を行う、いわゆる公式プロバイダーとして2017年1月にリリースされました。

f:id:febc_yamamoto:20180905155723p:plain

Arukasのβサービスが一旦終了…

f:id:febc_yamamoto:20180225174323p:plain

しかし、当時のArukasはβサービスというお試し期間中であり、βサービスは2017年7月31日をもって一旦終了となりました。

arukas.io

これに伴い、Arukasプロバイダーについても更新を一時停止していました。

github.com

復活のArukas!

f:id:febc_yamamoto:20180905165102p:plain

Arukasはその後半年近い充電?期間を経て2018年2月にβサービスを再開、翌3月には正式サービスとして課金が開始されました。

arukas.io

arukas.io

Arukasプロバイダーについてもすぐに復活させたかったのですが、当時と公式プロバイダーの公開手順などが変更されていたり、 ArukasのAPIがβサービスの時点から若干変更されていたりと様々な事情が重なった結果少々時間がかかってしまいました。

公式プロバイダーの公開までの手順はそのうち気が向いたら記事にします。

時間を見つけては細々と復活に向けた活動を行なってきましたが、とうとう先日(2018/9/4) Arukasプロバイダーの更新版である v1.0.0のリリースを行うことができました。

Arukasが正式サービスとなったことでコンテナ起動までの待ち時間が短くなったりと、より安定したパワフルなサービスになったと思います。
今回は改めてArukas正式サービスに対応したArukasプロバイダー v1.0.0の利用方法を紹介します。

Arukasプロバイダー(v1.0.0)の使い方

AWSやAzure(RM)、GCPなどといった多くのTerraformプロバイダーと同様の手順で利用可能です。
すなわち、Terraform自体のインストール、ArukasのAPIキー発行&環境変数の設定、tfファイルの作成〜適用という流れです。

順に見ていきます。

Terraformのインストール

以下のページからTerraformの実行ファイルをダウンロードし、PATHの通った場所に置きます。

www.terraform.io

必要に応じて実行権を付与しておきます。

Arukas APIキー発行 & 環境変数への設定

ArukasのコントロールパネルからAPIキーを発行します。 以下の記事などが参考になると思います。

blog.lorentzca.me

febc-yamamoto.hatenablog.com

APIキーを発行したら環境変数を設定しておきます。

# APIキーを環境変数に設定
$ export ARUKAS_JSON_API_TOKEN=<発行したAPIキーのトークン>
$ export ARUKAS_JSON_API_SECRET=<発行したAPIキーのシークレット>

tfファイル作成

次にtfファイルを作成します。以下のようなtfファイルを作成しておきます。

resource "arukas_container" "foobar" {
  name      = "arukas-provider-example"
  image     = "nginx:latest"

  ports = {
    protocol = "tcp"
    number   = "80"
  }
}

この例ではDockerHub上のnginx:latestイメージを利用してArukas上にコンテナを起動します。

なお、Arukasには料金プランが4つ(Free、Hobby、Standard-1、Standard-2)とありますが、 tfファイルにプランを明示しなかった場合、デフォルトで無料のFreeプランが利用されるようになっています。 実際に利用する際は適切なプランをtfファイルにて指定してください。

tfファイルに記載できる項目については以下のArukasプロバイダーのドキュメントを参照してください。

www.terraform.io

Note: Freeプランでは起動できるインスタンスが1に制限されています。 もしすでにインスタンスが存在する場合は後ほどterraform applyを実行する際にエラーとなりますのでご注意ください。

Note: v1.0.0以前のArukasプロバイダーを使われていた方はtfファイルの書き方が若干変更されている点にご注意ください。 変更点はCHANGELOGに記載されています。

適用

あとは通常のTerraformでの適用手順となります。 terraform initを実行することで必要なプロバイダーがダウンロードされます。
その後terraform applyを実行することでインフラの展開が行われます。

$ terraform init
$ terraform apply

確認

無事に展開されると以下のようにArukas上にコンテナが起動しているのが確認できるはずです。

f:id:febc_yamamoto:20180905160933p:plain

Arukasのβサービス中はコンテナの起動まで結構時間がかかることがありましたが、正式サービスになってからは割とサクサク動いている感じですね。

確認したら忘れずにterraform destroyを実行して削除しておきましょう。

終わりに

ということで今回は復活したArukasプロバイダーについて紹介しました。
手軽に利用できますのでぜひご利用ください!!

arukas.io

以上です。

「Rio」- 古き良きDockerのUXをモダンなクラウドネイティブソフトウェア達に

2018/10/1更新:
- v0.0.3を使うように修正
- 文言を微修正

Twitterにて「Rio」のデモ動画が発表された

つい先日(2018/8/7)、TwitterにてRancher社のDarren Shepherd氏がRioというプロダクトのデモ動画を発表してました。

前々から氏のツイートなどでRioの開発をしてる旨を匂わせていましたがいよいよ動く段階まできたようです。

Rancher 2.0の開発もまだまだ継続してる中、新しいプロダクトはしばらく先じゃないかと思ってましたが 思っていたより早い段階で動く姿を見れたので結構驚きました。

もうGitHubからダウンロードできる状態でしたので早速動かしてみました。

注: 以降は先のデモ動画の内容をなぞっていっただけのものです。 動画を見た方は多分この先読まなくてOKです。

Rio」って何?

Rancher社が開発している、新しいコンテナツールとのことです。

rancher/rio

READMEでは以下のように紹介されています。

  • Simple, fun, end-to-end container experience
  • Cloud Native Container Distribution

Rio is a user oriented end-to-end container solution with a focus on keeping containers simple and combating the current trend of complexity.
It's kept fun and simple through it's familiar and opinionated user experience.
Additionally, Rio is a "Cloud Native Container Distribution" meaning is includes builtin Cloud Native technologies such as Kubernetes, Istio, Containerd, etc.
so that the user need not be an expert in installing, using, and maintaining these system.

Cloud Native Container Distributionと掲げており、KubernetesやIstioをはじめとするCloud Nativeなプロダクトの組み合わせを シンプルなフロントエンドを通じて利用できるようになっています。

どんなプロダクトが含まれるかはデモ動画の中で紹介されていました(もちろんGitHub上にも書かれてます)。

f:id:febc_yamamoto:20180808195344p:plain

現在は基本となるコンテナランタイム(containerd)、オーケストレーション(kubernetes)、ネットワーク(flannel)、サービスメッシュ(istio)が組み込まれており、 今後はストレージ関連としてRancher社のLonghorn、モニタリングにPrometheus、イメージのビルドにBuildKit、イメージスキャンにCoreOSのClairなども組み込む予定のようです。

ということで早速動かしてみます。

Rioのインストール

Rioは既存のKubernetes(laptopの場合はMinikube or Docker for Win/Mac推奨とのこと)、またはLinux(4.x+)で動作するとのことです。

Prerequisites: If you want to run this on your laptop, then Minikube or Docker for Mac/Windows is recommended. If you don't have those then you need to run a Linux VM (or Linux itself, come to the darkside). We will make this easier in the future. Otherwise you can run this easily with any modern Linux server, nothing is needed to be installed except the kernel.

今回はクラウド上に立てたUbuntuで動かしてみます。 Rio本体以外はインストールする必要はないのですが、内部の調査をしたい方はkubectlくらいは入れておくと便利です。

余談1: Linux上でスタンドアロンで動かした場合はembededなk8sを起動して動作する、Rancher 2.0などと同じような動きをしてました
余談2: Docker for Macで試してたのですが外部からのアクセスができない箇所があって調べるのめんどくさくなったのでUbuntuにしました

まずRioの実行ファイルをGitHubからダウンロードし展開しておきます。

rancher/rio リリースページ

# rio v0.0.3のダウンロード
curl -L -o rio.tgz https://github.com/rancher/rio/releases/download/v0.0.3/rio-v0.0.3-linux-amd64.tar.gz

# 展開
tar zxvf rio.tgz 

# PATHの通った場所に移動
mv rio-v0.0.3-linux-amd64/rio /usr/local/bin/

続いてRioコンポーネント群を起動します。 今回はRioスタンドアロンで起動します。もし既存のKubernetesクラスタを利用する場合はGitHub上のREADMEを参照してください。

# Rioサーバの起動(embeded-k8sモード)
$ sudo rio server

少し待つとコンソール上に以下のようにログイン用のトークンが出力されるはずです。 これをコピペして別のターミナルを開き実行しましょう。

INFO[0010] To use CLI: rio login -s https://133.242.228.115:7443 -t xxxxxxxxxxxx::admin:xxxxxxxxxxxxxxxx 
# 起動したRioサーバへログイン(先ほどの表示をコピペ)
$ rio login -s https://133.242.228.115:7443 -t xxxxxxxxxxxx::admin:xxxxxxxxxxxxxxxx 

なお、rioコマンドをrootユーザで実行する場合はrio loginは不要とのことです。 以降はrioコマンドで様々な操作が可能になっているはずです。

余談: Rioスタンドアロンで起動した場合のkubectlの使い方

=== 2018/8/8 追記
rio -hには出てこないですが、よく見たらrio kubectlというサブコマンドがあるっぽいです。
なのでわざわざkubectlをインストールしなくても良さそうですね。
=== 追記ここまで

Rioスタンドアロンで起動するとRancher 2.0などと同じくembededモードでk8sを起動して利用するようになってました。 この場合、/var/lib/rancher/rio/server/cred/kubeconfig.yamlにkubeconfigが出力されてますのでこれを使えばkubectlも使えるようになります。 Rioを使うのであればkubectlは不要ですがやっぱりIstioのCRがどうなってるの?とか気になりますよね?そんな方向けの設定です。

# embededモードで起動しているk8sに接続するためのkubeconfigを環境変数に指定
$ export KUBECONFIG=/var/lib/rancher/rio/server/cred/kubeconfig.yaml

# kubectlが使えるはず
$ kubectl get cs
NAME                 STATUS    MESSAGE   ERROR
scheduler            Healthy   ok        
controller-manager   Healthy   ok 

$ kubectl get nodes #なんかバージョン表示がバグってるし
NAME             STATUS    ROLES     AGE       VERSION
node-conf-test   Ready     <none>    10m       v0.0.0-master+$Format:%h$

Rioでコンテナを起動してみる

まずは動かしてみます。 docker runするような感覚でrio runを実行することでコンテナの起動が可能です。

$ rio run --name foobar nginx  # --nameは省略可能

起動したか確認してみます。確認はrio psを実行します。

$ rio ps

NAME      IMAGE     CREATED          SCALE     STATE     ENDPOINT   DETAIL
foobar    nginx     10 seconds ago   1         active               

何か起動してるっぽいですね。 RioではDockerCompose風にServiceという単位でコンテナ(群)を管理しています。

Service

The main unit that is being dealt with in Rio are services. Services are just a collection of containers that provide a similar function. When you run containers in Rio you are really creating a Scalable Service. rio run and rio create will create a service. You can later scale that service with rio scale. Services are assigned a DNS name so that group of containers can be accessed from other services.

rio psで表示されるのはServiceの一覧ということですね。 さらにrio psに引数としてサービス名を与えることで詳細を確認可能です。

# サービスの詳細を確認
$ rio ps foobar

NAME                      IMAGE     CREATED              NODE                 IP         STATE     DETAIL
foobar/7946c5787c-l49rk   nginx     About a minute ago   node-conf-test       10.1.0.8   running   

コンテナへのattachやexecも可能です。

# コンテナ内でbashを実行(--interactive + --ttyオプションを指定 = dockerと同じ)
$ rio exec -it foobar bash

# *** ここはコンテナ内 ***
root@foobar-7946c5787c-l49rk:/# 

# 以下のようにサービス名/コンテナ名まで指定してもOK
# rio exec -it foobar/7946c5787c-l49rk bash

次にスケールアウトしてみます。

$ rio scale foobar=3

# 確認
$ rio ps
NAME      IMAGE     CREATED         SCALE     STATE     ENDPOINT   DETAIL
foobar    nginx     3 minutes ago   3         active               

$ rio ps foobar
NAME                      IMAGE     CREATED          NODE                 IP          STATE     DETAIL
foobar/7946c5787c-qqdkr   nginx     21 seconds ago   node-conf-test       10.1.0.9    running   
foobar/7946c5787c-snclf   nginx     21 seconds ago   node-conf-test       10.1.0.10   running   
foobar/7946c5787c-l49rk   nginx     3 minutes ago    node-conf-test       10.1.0.8    running   

端々がDockerっぽいですね。 他にもinspectといったおなじみのコマンドもありますので詳細はrio -hを実行して確認してみてください。

次に進む前に一旦起動したサービスを削除しておきます。

$ rio rm foobar

注: 今回は説明を簡単にするためにWorkSpaceやStackといった概念をすっ飛ばしてます。 詳細はGitHub上のConceptsを確認しておいてください。

Rioでサービスメッシュを体験してみる

RioにはIstioが組み込まれており簡単に利用できるようになっているようです。 ここでは

  • ステージング環境のデプロイ
  • カナリアリリース
  • ステージング環境を本番にプロモート

という流れで進めてみます。

サービスの起動 + 外部への公開

まずはサービスを起動し外部からアクセス可能にします。 ここではRioのREADME.mdに乗っているサンプルであるibuildthecloud/demo:v1というイメージを利用してます。 このイメージはHTTPでアクセスするとHello Worldという文字列を返すという単純なものです。

# -p(publish) オプションで80番ポートを公開
$ rio run -p 80/http --name rio-demo --scale=3 ibuildthecloud/demo:v1

確認しておきます。

$ rio ps

NAME       IMAGE                    CREATED          SCALE     STATE     ENDPOINT                                          DETAIL
rio-demo   ibuildthecloud/demo:v1   32 seconds ago   3         active    http://rio-demo.default.cr2ch1.lb.rancher.cloud 

なんだかエンドポイントとしてhttp://rio-demo.default.cr2ch1.lb.rancher.cloudというのが割り当てられてますね。 これはRioが割り当ててくれたもので、ノードのIPアドレスに解決されるようになってます。

# ノードのIPアドレス(この例では133.242.228.115/24)
$ ip a show dev eth0

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 9c:a3:ba:30:60:dd brd ff:ff:ff:ff:ff:ff
    inet 133.242.228.115/24 brd 133.242.228.255 scope global eth0
       valid_lft forever preferred_lft forever

       
# Aレコードを確認してみる
$ dig rio-demo.default.cr2ch1.lb.rancher.cloud. A

; <<>> DiG 9.10.3-P4-Ubuntu <<>> rio-demo.default.cr2ch1.lb.rancher.cloud. A
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45513
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rio-demo.default.cr2ch1.lb.rancher.cloud. IN A

;; ANSWER SECTION:
rio-demo.default.cr2ch1.lb.rancher.cloud. 60 IN A 133.242.228.115

;; Query time: 147 msec
;; SERVER: 133.242.0.3#53(133.242.0.3)
;; WHEN: Wed Aug 08 09:35:09 UTC 2018
;; MSG SIZE  rcvd: 85

エンドポイントのホスト名は<ServiceName>.<StackName>.<ClusterID>.<ROOT domain>となってます。
RootドメインRioスタンドアロンで起動した場合はlb.rancher.cloud、Docker for Macなどの場合は127.0.0.1.nip.ioが使われるようです。

この辺の処理は以下のあたりにありますので興味のある方はソース追ってみてください。

そしてRioが裏でIstioをいい感じに設定してくれるようになっており、このエンドポイントを使えばサービスにアクセスできるようになっています。

(Istioがどうなってるのか気になる方は記事の末尾の「おまけ: 段階ごとのkubectlなどの詳細」を参照してください)

起動したサービスにアクセスしてみる

ということで確認してみましょう。

$ curl -s http://rio-demo.default.cr2ch1.lb.rancher.cloud 
Hello World

無事にHello Worldと表示されるはずです。

ステージング環境のデプロイ

次にこのサービスを更新したイメージを用意し、ステージング環境をデプロイしてみます。 ここでは:v3というタグをつけたイメージを更新したイメージとして準備しています。

$ rio stage --image=ibuildthecloud/demo:v3 rio-demo:v3

psもみておきましょう。

$ rio ps
NAME          IMAGE                    CREATED          SCALE     STATE     ENDPOINT                                             DETAIL
rio-demo      ibuildthecloud/demo:v1   32 minutes ago   3         active    http://rio-demo.default.cr2ch1.lb.rancher.cloud      
rio-demo:v3   ibuildthecloud/demo:v3   32 minutes ago   3         active    http://rio-demo-v3.default.cr2ch1.lb.rancher.cloud 

rio-demo:v3というサービスが作成され、専用のエンドポイントが割り当てられています。 このエンドポイントを使ってステージング環境での動作確認が可能です。

$ curl -s http://rio-demo-v3.default.cr2ch1.lb.rancher.cloud
Hello World v3

カナリアリリース

ステージング環境での動作確認が済んだらカナリアリリースしてみます。

まずは元のサービス(この例だとhttp://rio-demo.default.cr2ch1.lb.rancher.cloud)にアクセスした際、20%の確率でv3にアクセスされるようにしてみます。

$ rio weight rio-demo:v3=20%

その後元のサービスのエンドポイントにアクセスすると時折v3の方にアクセスするようになっています。

# URLは同じであることに注目

$ curl -s http://rio-demo.default.cr2ch1.lb.rancher.cloud 
Hello World

$ curl -s http://rio-demo.default.cr2ch1.lb.rancher.cloud 
Hello World v3

ステージング -> 本番へプロモート

カナリアリリースの結果が良好だったのでいよいよステージング環境を本番環境に昇格させます。

$ rio promote rio-demo:v3

その後はv3の方にしかアクセスされなくなっています。

$ curl -s http://rio-demo.default.cr2ch1.lb.rancher.cloud 
Hello World v3

psをみてみるとステージング環境がなくなり、元のサービスと入れ替わっているのが確認できます。

$ rio ps
NAME       IMAGE                    CREATED         SCALE     STATE     ENDPOINT                                          DETAIL
rio-demo   ibuildthecloud/demo:v3   8 minutes ago   3         active    http://rio-demo.default.cr2ch1.lb.rancher.cloud   

ということでサービスメッシュ周りの確認をしてみました。

終わりに

Rancher社が発表した新しいツール「Rio」を試してみました。
Docker風の操作感なのでもともとDockerコマンドに慣れていた方には違和感なく取り組めそうな気がします。
今のところk8s+istioのラッパーといった感が強いですが、今後組み込みのプロダクトは順次増えていくはずです。

なお、現状ではまだまだ開発中ということもあり動作が不安定です。 随所にバグもみられますので今は雰囲気を感じてみる程度の使い方となります。

今後に期待しましょう。

以上です。

おまけ: 段階ごとのkubectlなどの詳細

ここからは各操作ごとにkubectlを実行してみた結果を貼っておきます。
どのようにistioを操作しているのかなどを見てフムフムしたい方向けです。

長いのでgistに貼っておきます。

途中で面倒になったので全ての変化を網羅したわけではないのでご注意ください。

Kubernetes完全ガイド (impress top gear)

Kubernetes完全ガイド (impress top gear)

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

RKEでAWS上にKubernetes(v1.10)をデプロイする

f:id:febc_yamamoto:20180529205711p:plain

Rancher Blogに新しい記事(RKE+AWS)が投稿されてた

今月半ばにRancher Labsのブログに以下の記事が投稿されていました。

Rancher Blog: How To Deploy Kubernetes Clusters On AWS Using RKE

AWS上にRKEを用いてKubernetesクラスタを構築するという内容です。

でもRKEの最新版だと動かない…

手順が丁寧に書かれていてわかりやすい内容なのですが、利用しているRKEのバージョン(v0.1.5)が古く、最新版を利用するとうまく動きませんでした。

そこで、最新版できちんと動かすために必要な作業についてこの記事にまとめてみました。

ついでにTerraformでコード化しました

記事を書くついでにこの構成を手軽に試せるようにTerraformTerraformのRKEプロバイダーを利用してコマンド一発で構築できるようにもしました。

Terraform + RKEプロバイダーのtfファイル

ということで早速RKEの最新版での動かし方をみていきます。

RKE(v0.1.7) + AWSでKubernetes

TL;DR

  • 基本はRancher Blogに記載の手順でOK
  • RKEの最新版であるv0.1.7はデフォルトではKubernetes v1.10がデプロイされる
  • Kubernetes v1.10でAWS向けのCloud Controller Managerを利用する場合、ClusterIDが指定されていないとkubeletの起動に失敗する
  • なので以下リソースにClusterID用のタグを付与すればOK

解説

RKEの最新版であるv0.1.7はデフォルトではKubernetes v1.10がデプロイされる

まず、Rancher Blogの該当記事で利用されているRKEのバージョンはv0.1.5です。
このバージョンではrke upを実行すると(デフォルトでは)Kubernetes v1.8がデプロイされていました。

なので、後述するClusterIDが設定されていなくても問題なく利用できていたようですね。

Kubernetes v1.10でAWS向けのCloud-Controller-Managerを利用する場合、ClusterIDが指定されていないとkubeletの起動に失敗する

Kubernetes v1.10のCHANGE LOGを見ると、以下のPRによるものです。

kubernetes/kubernetes #60125

対応: 各リソースにClusterID用のタグを付与する

ということで、Kubernetes v1.10を使う場合はClusterIDを指定すればOKということですね。
タグの付け方についてはRancherのドキュメントに記載がありました。

Rancher docs: Kubernetes - Cloud Providers / Configuring the ClusterID

このドキュメントによると、 - サブネット - セキュリティグループ - EC2インスタンス に対してkubernetes.io/cluster/CLUSTERID=ownedのようなタグをつければ良いとのことでした。
IDはCLUSTERIDの部分を任意の値に書き換えることで設定可能です。

Terraform + RKEプロバイダーで構築する

ClusterID用のタグをつけるのは割と面倒な作業なのでTerraformでコード化しました。
以下のリポジトリでコードを公開しています。

GitHub: yamamoto-febc/k8s_on_aws_with_terraform

なお、このコードではRKEプロバイダーを利用して、RKEでのKubernetesクラスタ構築までを一気に行なっています。

RKEプロバイダーについては以下の記事を参照ください。

Terraform + RKEでKubernetesクラスタ構築 - TerraformのRKEプロバイダー

以下コードのポイントを解説しておきます。

IAMプロファイルの作成

(※Rancher Blogでの該当部分)

この辺りは以下のtfファイルにまとめています。

https://github.com/yamamoto-febc/k8s_on_aws_with_terraform/blob/master/aws/iam.tf

ClusterID用のタグ付与

サブネット/セキュリティグループ/EC2インスタンスに対してタグを指定しています。
以下のような感じです。

# タグの定義
locals {
  cluster_id_tag = "${map("kubernetes.io/cluster/${var.cluster_id}", "owned")}"
}

# 利用するリージョン内の全AZのデフォルトサブネットを対象にClusterID用のタグを付与
data "aws_availability_zones" "az" {}
resource "aws_default_subnet" "default" {
  availability_zone = "${data.aws_availability_zones.az.names[count.index]}"
  count             = "${length(data.aws_availability_zones.az.names)}"
  
  tags              = "${local.cluster_id_tag}" #タグの指定
}

サブネットについてはデフォルトサブネットに対してタグ付与しています。
また、セキュリティグループについてはRancher Blogと同じく全許可のルールとしています。

ノードのプロビジョニング

(※Rancher Blogでの該当部分)

Dockerをインストールして、SSH接続の際に利用するユーザー(記事ではubuntu)をDockerグループに入れる必要があります。

tfファイル上はremote-execを利用して以下のようにしています。

resource "aws_instance" "rke-node" {
  count = 4

  ami                    = "${data.aws_ami.ubuntu.id}"
  instance_type          = "${var.instance_type}"
  key_name               = "${aws_key_pair.rke-node-key.id}"
  iam_instance_profile   = "${aws_iam_instance_profile.rke-aws.name}"
  vpc_security_group_ids = ["${aws_security_group.allow-all.id}"]
  tags                   = "${local.cluster_id_tag}"

  provisioner "remote-exec" {
    connection {
      user        = "ubuntu"
      private_key = "${tls_private_key.node-key.private_key_pem}"
    }

    # Dockerのインストールとdockerグループへの追加
    inline = [
      "curl releases.rancher.com/install-docker/1.12.sh | bash",
      "sudo usermod -a -G docker ubuntu",
    ]
  }
}

なお、インスタンスタイプは記事ではt2.largeが推奨されていますが、軽く試すだけならt2.microで十分でした。
(tfファイル上はデフォルトでt2.microにしました)

RKEの実行

(※Rancher Blogでの該当部分)

RKEの実行はRKEプロバイダーに任せます。
以下のようなtfファイルとなります。

module "nodes" {
  source = "./aws"
}

resource rke_cluster "cluster" {
  cloud_provider {
    name = "aws"
  }

  nodes = [
    {
      address = "${module.nodes.addresses[0]}"
      user    = "${module.nodes.ssh_username}"
      ssh_key = "${module.nodes.private_key}"
      role    = ["controlplane", "etcd"]
    },
    {
      address = "${module.nodes.addresses[1]}"
      user    = "${module.nodes.ssh_username}"
      ssh_key = "${module.nodes.private_key}"
      role    = ["worker"]
    },
    {
      address = "${module.nodes.addresses[2]}"
      user    = "${module.nodes.ssh_username}"
      ssh_key = "${module.nodes.private_key}"
      role    = ["worker"]
    },
    {
      address = "${module.nodes.addresses[3]}"
      user    = "${module.nodes.ssh_username}"
      ssh_key = "${module.nodes.private_key}"
      role    = ["worker"]
    },
  ]
}

RKEコマンドを直接実行する場合は、rke up実行後にカレントディレクトリにkube_config_cluster.ymlファイルが作成されます。
RKEプロバイダーの場合はデフォルトでは作成されませんので、以下のようにlocal-file機能でファイル出力しています。

resource "local_file" "kube_cluster_yaml" {
  filename = "./kube_config_cluster.yml"
  content  = "${rke_cluster.cluster.kube_config_yaml}"
}

terraform apply実行後にexport KUBECONFIG=$PWD/kube\_config\_cluster.ymlを実行しておけばkubectlが利用できるはずです。

後はRancher Blogの内容をそのまま進めていけるはずです。


ということでRancher Blogの補足をしつつ、Terraform + RKEプロバイダーで環境構築してみました。

なおこの構成は全通信許可などのガバガバ設定になっていますので検証などでのみ利用してくださいね!!

以上です。

Terraform + RKEでKubernetesクラスタ構築 - TerraformのRKEプロバイダー

f:id:febc_yamamoto:20180528165306p:plain

2018/5/18に東京で開催されたRancher2.0リリースパーティで以下のLTをさせていただきました。

このLTで発表したTerraformのRKEプロバイダーについてご紹介いたします。

その前に: RKE(Rancher Kubernetes Engine)とは?

RKEとは、一言で言うと「Kubernetesクラスタのデプロイツール」です。

f:id:febc_yamamoto:20180528155207p:plain

kubernetesクラスタのデプロイツールとしてはkubeadmkopsといったものもありますが、それらと比較するとRKEは以下のような特徴があります。

  • 対象ノードに外部からSSH接続してプロビジョニングを行う
  • kubernetesの各種コンポーネント(etcdやapiserverなど)をDockerコンテナとして起動する
  • 最初からHAなクラスタを構築可能

RKEの使い方

ノードの準備

まずk8sクラスタをプロビジョニングするノードを用意しておく必要があります。 以下の条件を満たすノードを用意します。

Requirements

  • Docker versions 1.11.2 up to 1.13.1 and 17.03.x are validated for Kubernetes versions 1.8, 1.9 and 1.10
  • OpenSSH 7.0+ must be installed on each node for stream local forwarding to work.
  • The SSH user used for node access must be a member of the docker group.
  • Ports 6443, 2379, and 2380 should be opened between cluster nodes.
  • Swap disabled on worker nodes.

https://github.com/rancher/rke#requirements

RKEのドキュメントには記載されていませんが、以下のポートも必要に応じて開けておく必要があります。

f:id:febc_yamamoto:20180528155213p:plain

(初回のみ)RKEのインストール

続いてRKEのインストールを行います。 RKEはGo言語で書かれており、実行ファイルをダウンロードして実行権を付与するだけでインストールできます。

以下のRKEのリリースページから各プラットフォーム向けのバイナリをダウンロードし実行権を付与しておきます。

https://github.com/rancher/rke/releases/latest

※当記事執筆時点での最新バージョンはv0.1.7です。

定義ファイル(cluster.yml)の作成

RKEではどのノードをプロビジョニングするかといった設定をcluster.ymlという定義ファイルで指定します。
(定義ファイル名はデフォルトではcluster.ymlですが、別の名前でもOKです。その場合はrke up時に--configオプションを指定します)

最小の定義ファイルは以下のようになります。

nodes:
  - address: <用意したノードのIPアドレス または ホスト名>
    user: <SSH接続用のユーザー名>
    role: [controlplane,worker,etcd]

nodesにはプロビジョニング対象となるノードをリストで指定します。 ここでは指定していませんが、SSH用にパスワードや秘密鍵SSHエージェント利用有無なども指定可能です。
(RKE v0.1.7以降は踏み台ホスト(bastion_host)も指定可能)

その他の指定できる項目についてはGitHub上にcluster.ymlのサンプルが公開されていますのでそちらを参照してみてください。
https://github.com/rancher/rke/blob/v0.1.7/cluster.yml

rke upコマンドの実行

定義ファイルが用意できたらrke upコマンドを実行することでk8sクラスタのプロビジョニングが行えます。

rke up

プロビジョニングが正常に終わるとkube_config_cluster.ymlというファイルが作成されます。 kubectlコマンドを利用する場合はこのファイルを利用するようにします。 ~/.kube/configを上書きするか以下のようにすればOKです。

# 環境変数を使う場合
$ export KUBECONFIG=${PWD}/kube_config_cluster.yml
$ kubectl get cs

# --kubeconfigオプションを使う場合
$ kubectl --kubeconfig kube_config_cluster.yml get nodes 

RKEについてはRancher Meetup大阪で発表したことがありますので、詳細はこちらも参照ください。

TerraformのRKEプロバイダー

ここまで見てきたように、RKEでクラスタをプロビジョニングするには

  • あらかじめノードを用意し
  • 定義ファイルを作成し
  • rke up実行

というステップを踏む必要があります。

この辺を毎回手作業で行うのは大変なので、RKEをTerraformから直接扱えるようにしたのがTerraformのRKEプロバイダーです。

TerraformのRKEプロバイダー

以下のようなtfファイルを用意してterraform applyを実行することで、RKEでクラスタのプロビジョニングが行えます。

resource rke_cluster "cluster" {
  nodes = [
    {
      address = "<用意したノードのIPアドレス または ホスト名>"
      user    = "<SSH接続用のユーザー名>"
      role    = ["controlplane", "worker", "etcd"]
    }
  ]
}

RKEの定義ファイル(cluster.yml)に書く内容をtfファイルに書けるようにしています。
tfファイルに書けるということは、${}記法などのTerraformの強力な機能をそのまま利用できるということです。

例えばEC2インスタンスを作成し、そこに対しRKEでk8sクラスタのプロビジョニングを行いたい場合は以下のように書けます。

# EC2インスタンス(Ubuntu)
resource "aws_instance" "rke-node" {
  // [...]
  ami = "${data.aws_ami.ubuntu.id}"

  // Dockerのインストール & ubuntuユーザーをdockerグループに追加
  provisioner "remote-exec" {
    connection {
      user        = "ubuntu"
      private_key = "${tls_private_key.node-key.private_key_pem}"
    }
    inline = [
      "curl releases.rancher.com/install-docker/1.12.sh | bash",
      "sudo usermod -a -G docker ubuntu",
    ]
  }
}

# RKE
resource rke_cluster "cluster" {
 nodes = [
    {
      address = "${aws_instance.rke-node.public_dns}"
      user    = "ubuntu"
      ssh_key = "${tls_private_key.node-key.private_key_pem}"
      role    = ["controlplane", "etcd", "worker"]
    },
  ]
}

ここではノードとしてAWSを利用しましたが、DigitalOceanやさくらのクラウドなどのTerraformが対応しているプラットフォームであればもちろん利用可能です。

RKEプロダイバーの便利機能

kubeconfigファイルの出力

以下のようにlocal_fileを利用すればrkeコマンド実行時と同じくkube_config_cluster.ymlを取得できます。

resource "local_file" "kube_cluster_yaml" {
  filename = "${path.root}/kube_config_cluster.yml"
  content = "${rke_cluster.cluster.kube_config_yaml}"
}
TerraformのKubernetesプロバイダーとの連携

TerraformのKubernetesプロバイダーと簡単に連携できるように認証情報などを参照可能な機能も用意しています。 以下のようにすることでRKEで作成したk8sクラスタの認証情報をKubernetesプロダイバーで利用可能です。

provider "kubernetes" {
  host     = "${rke_cluster.cluster.api_server_url}"
  username = "${rke_cluster.cluster.kube_admin_user}"

  client_certificate     = "${rke_cluster.cluster.client_cert}"
  client_key             = "${rke_cluster.cluster.client_key}"
  cluster_ca_certificate = "${rke_cluster.cluster.ca_crt}"
  
  # デフォルトでは~/.kube/configをロードしてしまうので
  # 必要に応じて以下コメントアウトを解除
  
  # load_config_file = false
}

# kubernetesプロバイダーを利用してネームスケースを作成する例
resource "kubernetes_namespace" "example" {
  metadata {
    name = "terraform-example-namespace"
  }
}

まとめ

今回はTerraformのRKEプロバイダーをご紹介しました。

Terraformでノードの作成からkubernetesクラスタのプロビジョニングまで一気に行えることで環境構築が非常に楽になると思います。
一歩進んでk8sクラスタの使い捨て運用なども可能になるかもしれないですね。

と言うことでぜひRKEプロバイダーをお試しください。

以上です。