【モダンTerraform】意外と便利!? Miscプロバイダーたち(概要編&Nullプロバイダー編)
今回はTerraformのプロバイダーのうち、最近充実してきているMiscプロバイダーについてご紹介します。
Miscプロバイダーってなに?
最近Terraformのプロダイバーが増えてきたため、ドキュメント上プロダイバーの分類が行われました。
現在は以下のような分類がなされています。
分類 | 概要 |
---|---|
Major Cloud | いわゆる3大クラウド(AWS/Azure/GCP)+α |
Cloud | ↑以外のパブリック/プライベートクラウドやHerokuなどのPaaSなど |
Infrastructure Software | KubernetesやRancherなどの基盤系ソフトウェア(ConsulやRabbitMQなども) |
Network | CDNやDNSなどネットワーク系 |
VCS | バージョン管理システム系(Bitbucket/GitHub/GitLab) |
Monitor & System Management | モニタリングやシステム管理系(OpsGenieやPagerDutyなども) |
Database | データベース(InfluxDB/MySQL/PostgreSQL) |
Misc. | その他諸々 |
Community | サードパーティ製プロバイダー |
Misc.プロダイバーはこの中でも分類しきれなかったその他のものですね。
Miscプロバイダーにはどんなものが含まれてるの?
現在は以下のプロバイダーが含まれています。
分類 | 概要 |
---|---|
Archive | ファイルやディレクトリのアーカイブを行う(現在はzipのみ対応) |
Cobbler | Linuxのネットワークインストール環境を構築/管理してくれるツールCobbler用プロバイダー |
External | 任意のプログラムをData Sourceとして利用する |
Ignition | ContainerLinux(CoreOS)で利用されているプロビジョニングユーティリティIgnition用の構成ファイルの出力 |
Local | ローカルファイルの作成/参照を行う |
Null | 特殊な空リソース |
Random | ランダム値の生成を行う |
Template | テンプレート機能の提供 |
TLS | キーペアの生成やCSRの生成、自己署名証明書の発行など |
この中でも特によく使われるのは以下2つだと思います。
今回はこの中からNullプロバイダーについて紹介し、Templateプロバイダーやそれ以外のプロバイダーについては次回以降に紹介します。
Nullプロバイダー
NullプロバイダーはTerraformのリソースライフサイクルにおいて何も処理を行わないという特殊なリソースを提供します。
と言われてもわからないですよね。
これは通常のTerraformのリソースだけでは実現が難しいいくつかのケースに対応するためのものです。
Terraformは待ち合わせ処理が苦手
Terraformではtfファイルで定義されているリソース間の依存関係を検出し、自動的に構築順序を制御してくれます。
例えば以下のtfファイルの場合、リソースの構築順序はどうなるでしょうか?
# ディスクの定義 resource sakuracloud_disk "disk" { name = "foobar" } # サーバの定義 resource sakuracloud_server "server" { name = "foobar" # 先程定義したディスクのIDを参照 disks = ["${sakuracloud_disk.disk.id}"] }
この場合、sakuracloud_server.server
リソースはsakuracloud_disk.disk
リソースのIDを参照しているため、
sakuracloud_server.server
リソースがsakuracloud_disk.disk
に依存している
状態となります。
Terraformはサーバを作成するにはディスクが必要という状態を検出し、
- 1) ディスク
- 2) サーバ
の順に作成してくれます。
(なお、依存関係はterraform graph
コマンドで以下例のように表示できるGraphViz形式でグラフを出力してくれます)
では以下の場合はどうなるでしょうか?
# ディスクの定義(1本目) resource sakuracloud_disk "disk01" { name = "disk01" } # ディスクの定義(2本目) resource sakuracloud_disk "disk02" { name = "disk02" } # サーバの定義(1台目) resource sakuracloud_server "server01" { name = "server01" # 先程定義したディスクのIDを参照 disks = ["${sakuracloud_disk.disk01.id}"] } # サーバの定義(2台目) resource sakuracloud_server "server02" { name = "server02" # 先程定義したディスクのIDを参照 disks = ["${sakuracloud_disk.disk02.id}"] }
先程の例にディスク/サーバを追加しそれぞれ2つずつリソースを作成する例となっています。 依存関係は以下のようになります。
この場合、ディスク -> サーバの組み合わせの作成順序はこれまで通りですが、2つのディスク/サーバの作成順序はどうなるのでしょうか?
この辺りはTerraformの賢いところで、互いに関連しない(依存しない)リソースについては並列で処理が行われます。
先程の図の①の2つのディスクについては互いに関連しないので並列で作成が行われます。
しかし、中には並列で作成されると困る場面もあります。
例えばクラスタの構築などでは、先にマスターとなるノードをプロビジョニングしてからスレーブ側のノードをプロビジョニングする必要がある場合があります。
並列でサーバ作成されるとこの順序を守ることができません。
また、全てのノードがクラスタに参加した後でプロビジョニングを別途実行したいというような場合もあるでしょう。
そこでTerraformではこの問題を解決するいくつかの方法を提供しており、そのうちの一つがNullリソースです。
以下の例では2台のサーバの作成が完了してからプロビジョニングを行う例となっています。
2台のサーバの作成後というタイミング指定のためにNullリソースを利用しています。
# ...ディスクやサーバの定義(省略)... # 2台のサーバ作成後に実行するプロビジョニング locals { server_ids = ["${sakuracloud_server.server01.id}", "${sakuracloud_server.server02.id}"] } resource null_resource "provisioning" { triggers = { depends = "${join(",", local.server_ids)}" # サーバ01と02のIDを指定 } provisioner "local-exec" { command = "run_initialize_cluster_playbook.sh" } }
この例の場合の依存関係は以下のようになります。
Nullリソースのtriggers
に(localsを通じて)サーバ01/02のIDを指定することで依存関係を示しています。
依存される側が先に作成されますので、この例では結果的にサーバ2台の作成後にNullリソースに設定されたプロビジョニング処理が実行されることになります。
おまけ: 別解としてdepends_on
今回みたいに単純な例ならNullリソースでなくてもMeta-Parametersの中のdepends_on
を利用することでも順序の制御が可能です。
各リソースにdepends_on
パラメータを指定することで依存関係が指定できますので、これを利用して順序の制御を行います。
# サーバの定義(2台目) resource sakuracloud_server "server02" { # ... # 1台目のサーバに依存することを明示 # (結果的に1台目のサーバ作成後に2台目の作成が行われる) depends_on = ["sakuracloud_server.server01"] provisioner "local-exec" { command = "run_initialize_cluster_playbook.sh" } }
この方法は単純な反面、関連するリソース数が増えてくるとdepends_on
を各所に書かないといけないというデメリットもありますので適材適所で使い分けましょう。
Null データソース(今ではほぼ出番なし)
NullプロバイダーにはNullリソースのデータソース版であるNullデータソースというものも存在します。
これは以下のようにOutput用に値を参照する部分を一箇所にまとめたり、再利用するために利用します。
data "null_data_source" "values" { inputs = { all_server_ids = "${concat(aws_instance.green.*.id, aws_instance.blue.*.id)}" all_server_ips = "${concat(aws_instance.green.*.private_ip, aws_instance.blue.*.private_ip)}" } } resource "aws_elb" "main" { # ... instances = "${data.null_data_source.values.outputs["all_server_ids"]}" } output "all_server_ids" { value = "${data.null_data_source.values.outputs["all_server_ids"]}" } output "all_server_ips" { value = "${data.null_data_source.values.outputs["all_server_ips"]}" }
が、現在ではLocal Valuesの仕組みがあるためほぼ使うことはないでしょう。
Local Valuesとの違いとしてはterraform graph
への表示有無があります(Local Valuesはgraph上表示されない)が、実用上これが問題になるケースはほとんどないと思います。
ここでもやっぱりLocal Valuesを積極的に使いましょう。
終わりに
ということで今回はNullプロバイダーを扱いました。
うまく使わないと見通しの悪いtfファイルになってしまいますが、順序や依存関係の細かな調整を行うのにはNullリソースが便利ですので状況に応じて適宜使っていきましょう。
以上です。
【モダンTerraform】v0.11以降でdynamicとfor_eachが実装されるかも
今回は最近のhashicorp/terraformでの開発状況から、現時点での最新版であるv0.11.3で未実装な機能の中で個人的にかなり期待している機能について紹介します。
countパラメータとその限界
全てのリソースにはMeta-parametersが指定できる
Terraformには全てのリソースに対しMeta-parametersという属性を指定できます。
これはData Sourcesに対しても指定可能となっています。
具体的には以下のようなものがあります。(詳細はドキュメントを参照)
count (int)
リソースの作成数を指定 ※現時点ではモジュールには指定不可depends_on (list of strings)
リソースが依存するリソースを指定 -> 具体的には作成順序などが制御されるprovider (string)
マルチプロバイダーの定義をしている場合に利用するプロバイダーを指定できる
(例: AWSのリージョンごとにプロバイダー定義している場合にaws.west
みたいに指定)lifecycle (configuration block)
リソースの作成/更新/破棄の挙動を調整する
それぞれに興味深いトピックはあるのですが、今日はcount
について扱います。
countパラメータの基本的な使い方
count
パラメータは以下のように利用します。
以下の例ではディスクリソースを2つ作成しています。
resource sakuracloud_disk "disks" { name = "${format("disk-%02d", count.index+1)}" count = 2 }
count
パラメータを指定すると、そのリソースの定義内でcount.index
という属性が利用できます。
これは自身のインデックスを参照するもので、0から始まる数値型の値となっています。
これを利用することで、locals
などとの併用で各リソースに固有の値を指定しておくことが可能です。
locals { # 監視先のドメインとパスを指定 targets = [ { domain = "example.com" path = "/" }, { domain = "example.jp" path = "/foo/" }, ] } resource sakuracloud_simple_monitor "monitors" { # 自身のインデックスでマップ(targetsの各要素)を取得、キーがdomainの値を参照する target = "${lookup(locals.target[count.index], "domain"}" health_check = { protocol = "https" delay_loop = 60 path = "${lookup(locals.target[count.index], "path"}" status = "200" } count = "${length(locals.target)}" }
countが利用できない場面
上手く使えばtfファイルを非常にシンプルにしてくれるcount
ですが、利用できない場面というのがあります。
- リソースの属性
- モジュール
例えば以下の例ではリソースにsetting
という属性があるのですが、これに対してのcount
指定はできません。
resource "aws_elastic_beanstalk_environment" "myEnv" { name = "test_environment" application = "testing" setting { # !ここではcountが使えない! namespace = "aws:elasticbeanstalk:application:environment" name = "HTTP_PROXY" value = "10.1.2.1:8080" } setting { namespace = "aws:elasticbeanstalk:application:environment" name = "TMPDIR" value = "/var/myapp/tmp" } }
議論中: dynanicとfor_each
この問題はGitHub上でも解決に向けた議論が行われています。
参考: GitHub(hashicorp/terraform) - Support count in resource fields #7034
このIssueの中で提案されているのがdynamic
ブロックとfor_each
です。
これを利用して先ほどの例を書き直すと以下のようになります。
# DRAFT: まだ提案段階でv0.11時点ではこの書き方は利用できません resource "aws_elastic_beanstalk_environment" "myEnv" { name = "test_environment" application = "testing" dynamic "setting" { for_each = var.environment_variables # map型の変数を指定 content { namespace = "aws:elasticbeanstalk:application:environment" name = setting.foreach.key # 現在のイテレーションの要素は`foreach`キーで参照できる value = setting.foreach.value } } }
マップ型変数や他で定義したリソースの属性などを利用して、各要素をイテレーションしながら値の参照ができるようです。
この例だとsetting
ブロック内のfor_each
でイテレーションされる各要素はsetting.foreach
で参照でき、それぞれkey
とvalue
のように参照できるようです。
以下のようにイテレーターに名前をつけることでネストも可能なようです。
# DRAFT: Not yet integrated into Terraform, and details may change before final release dynamic "example" { for_each = var.example_items iterator = parent # 外側のループのイテレータの名前を指定 content { dynamic "example" { for_each = parent.children iterator = child # 内側のループのイテレータの名前を指定 content { value = child.value parent_value = parent.value } } } }
これは嬉しいですね!
これまでこれを行うにはプロバイダー側で親リソース/子リソースに分割して実装する必要があり、
プロバイダーの実装者としてはなかなか辛い思いをしていました。
(リソースを親子に分割すると作成順序とかロックとか色々考慮が増えるのです…)
現在の実装状況は?
本日の時点では@apparentlymart氏が実験的に実装を進めているようです。
(前出のIssue内のコメントを参照)
期待して待ちましょう!!!
以上です。
【モダンTerraform】ベストプラクティスはTerraform Module Registryを参照しよう
今回は小ネタです。
Terraformでのベストプラクティス?
Terraformでのベストプラクティスは従来GitHubにて専用のリポジトリで公開されていました。
GitHub: hashicorp/best-practices
が、このリポジトリ、すでに「 Deprecated 」です。
じゃあどこ見ればいいのよ?
best-practicesリポジトリでも言及されていますが今後はTerraform Module Registryを参照すべしとのことです。
Twitterでも@mitchellh氏らが質問に答える形で言及していました。
ただし、RegistryにはHashiCorpの方が作成したもの以外も含まれるため、 参考にする際は「HashiCorp Verified Modules」を見るのがオススメです。
Registryでモジュールを検索する時に条件として「Verified」を指定できるのでそれを利用しましょう。
Terraform Module Registry 検索ページ
ということで
Terraform Module Registryを使いましょう。 以上です。
【モダンTerraform】VariableとLocal Valuesの使い分けについて
はじめに
ナウでイケてるヤングな皆様におかれましてはTerraformを使うのはもはや当たり前ですよね?
このTerraformですが日々バージョンが上がっており、ネット上で公開されているtfファイルの書き方が若干古いものもちょいちょい見受けられます。
特にTerraform v0.10.3(2017/8/30リリース)で導入されたLocal Valuesについては利用している例が少ないように思いますので今回通常のvariable
との違いなどについてまとめてみます。
TL; DR 今北産業
- tfファイル内の変数は基本的に
Local Values
を使おう - 特に判定処理は
Local Values
で明確な名前をつけよう Variable
を使うのは外部からのインプットにする場合だけ
そもそもLocal Valuesってなによ?
Local Valuesとはモジュール内に閉じて使える変数です。モジュール内でのローカル変数のようなものですね。
参考: Terraform ドキュメント - Local Values
なお通常の(これまでもあった)variableについてのドキュメントはこちらです。
参考: Terraform ドキュメント - Input Variables
Local Valuesは以下のように使います。
# Local Valuesとして変数定義 locals { switch_name = "my-switch-name" } # Local Valuesを使う resource sakuracloud_switch "sw" { # "local." というプレフィックスで参照できる name = "${local.switch_name}" }
localsブロックは複数記述可能
locals
ブロックはモジュール内に複数記述できます。もちろん変数名はモジュール内で一意である必要があります。
# サーバ関連の変数を定義 locals { server_name = "foobar" server_core = 2 server_memory = 4 } # ディスク関連の変数を定義 locals { disk_name = "foobar" disk_size = "20" }
様々なデータ型が使える
Terraformで使える様々なデータ型を指定することが可能です。
locals { # bool型 enabled = true # 数値型(10進数) num10 = 10 # 数値型(16進数) num16 = 0x16 # 文字列 strvar = "example" # リスト listvar = ["item1", "item2", "item3"] # マップ mapvar = { item1 = "foo" item2 = "bar" item3 = "baz" } # 複合型(map/リスト/文字列など) compvar = { tags = ["tag1", "tag2"] metadata = { foo = "1" bar = "2" nested = ["foo", "bar"] } } }
Variableとの違い
関数や他リソースの参照が使える
Local Values
には関数や他リソースの参照などが書けます。
例えば任意の変数が設定されているか(空文字以外が指定されているか)を判定した結果を変数として保持しておけます。
variableを利用する場合、三項演算子などでvariableの値を判定して分岐させるというような処理を行っていました。
variable use_load_balancer {} resource sakuracloud_load_balancer "lb" { # use_load_balancer変数が設定されていたらcountを1に、以外の場合は0にしてリソース作成しない count = "${var.use_load_balancer == "" ? 1 : 0}" } resource sakuracloud_switch "sw" { # use_load_balancer変数が設定されていたらcountを1に、以外の場合は0にしてリソース作成しない count = "${var.use_load_balancer == "" ? 1 : 0}" }
同じ判定を行なっている箇所が複数あってDRYじゃないですね。
これをLocal Values
を使って書き直すと以下のようになります。
variable use_load_balancer {} # 判定処理をlocalsブロック内に局所化 locals { load_balancer_count = "${var.use_load_balancer == "" ? 1 : 0}" switch_count = "${local.load_balancer_count}" } resource sakuracloud_load_balancer "lb" { count = "${local.load_balancer_count}" } resource sakuracloud_switch "sw" { count = "${local.switch_count}" }
判定処理についてはわかりやすい名前をつけておくことで可読性も上がりますし、どういう判定をしているのか追いやすい(定義を見ればよい)ですね。
Local Valuesは外部からの値の設定ができない
variableは以下のように様々な方法で値の設定を行うことができます。
apply
実行時に対話的に入力- コマンドラインから
-var
オプションや-ver-file
オプションで指定 terraform.tfvars
ファイルで指定- 環境変数(
TF_VAR_xxx
など)で指定 - variableの定義時にデフォルト値を明示
このため、variableをtfファイルの簡易化といった目的で利用していた場合は意図しない値が入力される可能性もあったりします。
例えばcount構文と組み合わせる場合に以下のような書き方をすることがありました。
# # Local Valuesがない時代の書き方 # # サーバに割り当てるIPアドレスのリスト variable ip_list { default = ["192.2.0.1", "192.2.0.2", "192.2.0.3"] } resource sakuracloud_server "servers" { # ip_listの要素数分のサーバを作成 count = "${length(var.ip_list)}" # 自身のインデックスでIPアドレスリストを参照 ipaddress = "${var.ip_list[count.index]}" }
ip_list
はvariableなため、外部から意図しない値が入力される可能性があります。
Local Valuesであればこの辺りを気にせずに使用可能です。
# # Local Valuesを利用した書き方 # # サーバに割り当てるIPアドレスのリスト locals { ip_list = ["192.2.0.1", "192.2.0.2", "192.2.0.3"] } resource sakuracloud_server "servers" { # ip_listの要素数分のサーバを作成 count = "${length(local.ip_list)}" # 自身のインデックスでIPアドレスリストを参照 ipaddress = "${local.ip_list[count.index]}" }
ということで、意図しない値の設定を防ぐためにもtfファイル上で変数を扱う際はまずLocal Valuesを利用し、外部から値の入力が必要な場合のみvariableを利用するのがオススメです。
まとめ
ということでLocal Valuesを積極的に使いましょう。 以上です。
続・さくらのクラウド上にMetabaseを構築する【HTTPS対応版】
前回はMetabase環境構築を行いました。
しかし、実運用の際はHTTPS対応は必須だと思いますので対応版を作りました。
Metabase環境構築 on さくらのクラウド(HTTPS版)
今回はLet's encryptにてHTTPS対応を行うバージョンとなっています。 Let's encrypt対応にはsteveltn/https-portalコンテナを利用します。
構築手順は前回とほぼ同じですが、あらかじめさくらのクラウド上にDNSゾーンの登録を行っておく必要があります。
テンプレート
前回との差分は以下の通りです。
- コメントを追加
- Let's encrypt用にDNS関連の記述を追加
- RancherOSのcloud-configにLet's encrypt用のコンテナを記述
### 概要 # # データベースアプライアンス(PostgreSQL)とRancherOSでMetabase実行環境を構築するテンプレート # # このテンプレートはRancherOS上のDockerでMetabaseを実行する構成となっています。 # Metabaseのバックエンドとしてデータベースアプライアンス(PostgreSQL)を利用します。 # # MetabaseサーバのHTTPS対応としてLet's encryptでの証明書取得も行います。 # # <事前準備> # # 1) さくらのクラウド上にSSH用の公開鍵を登録します。 # 2) さくらのクラウド上にDNSゾーンを登録しネームサーバの設定などを行っておきます。 # (すでに登録済みのゾーンがあればそれを利用可能です。) # # <構築手順> # 1) リソースマネージャーにて新しいテンプレートを作成し、このtffileの内容を貼り付けます。 # 2) tffile編集画面の"変数定義"タブにて以下の値を編集します。 # - サーバ管理者のパスワード(server_password) # - データベース接続ユーザーのパスワード(database_password) # - さくらのクラウドに登録済みの公開鍵の名称(ssh_public_key_name) # - さくらのクラウドに登録済みのDNSゾーン名(dns_zone_name) # 3) リソースマネージャー画面にて"計画/反映"を実行 # # <動作確認> # # ブラウザから以下のURLにアクセスするとMetabaseの画面が開きます。 # https://<MetabaseサーバのFQDN>/ # # MetabaseサーバのFQDNは以下の形式です。 # ${server_name}.${dns_zone_name} # # FQDNの例: # - server_name: "metabase" # - dns_zone_name: "example.com" # この場合FQDNは以下のようになります。 # FQDN: metabase.example.com # # <サーバへのSSH接続> # # サーバへのSSH接続は、指定した公開鍵による公開鍵認証のみ許可されるようになっています。 # SSH接続の際は秘密鍵を指定して接続してください。 # # > usacloudでのSSH接続例 # $ usacloud server ssh -i <your-private-key-file> <your-server-name> # # SSH接続後はdocker logsコマンドなどでMetabaseコンテナのログを確認可能です。 # ### 変数定義 locals { #********************************************* # パスワード/公開鍵関連(要変更) #********************************************* # サーバ管理者のパスワード server_password = "<put-your-password-here>" # データベース接続ユーザーのパスワード database_password = "<put-your-password-here>" # さくらのクラウドに登録済みの公開鍵の名称 ssh_public_key_name = "<put-your-public-key-name>" # さくらのクラウドに登録済みのDNSゾーン名 dns_zone_name = "<put-your-zone-name>" #********************************************* # サーバ/ディスク #********************************************* # サーバ名 server_name = "metabase" # サーバホスト名 host_name = "${local.server_name}" # サーバ コア数 server_core = 2 # サーバ メモリサイズ(GB) server_memory = 4 # ディスクサイズ disk_size = 20 #********************************************* # ネットワーク(スイッチ/パケットフィルタ) #********************************************* # スイッチ名 switch_name = "metabase-internal" # パケットフィルタ名 packet_filter_name = "metabase-filter" #********************************************* # データベースアプライアンス #********************************************* # データベースアプライアンス名 database_name = "metabase-db" # プラン database_plan = "30g" # 10g/30g/90g/240g # 接続ユーザー名 database_user_name = "metabase" # バックアップ時刻 database_backup_time = "01:00" } ### サーバ/ディスク # パブリックアーカイブ(OS)のID参照用のデータソース(RancherOS) data sakuracloud_archive "rancheros" { os_type = "rancheros" } # 公開鍵のID参照用のデータソース data "sakuracloud_ssh_key" "ssh_public_key" { name_selectors = ["${local.ssh_public_key_name}"] } # ディスク resource "sakuracloud_disk" "disk" { name = "${local.server_name}" source_archive_id = "${data.sakuracloud_archive.rancheros.id}" hostname = "${local.host_name}" password = "${local.server_password}" note_ids = ["${sakuracloud_note.provisioning.id}"] ssh_key_ids = ["${data.sakuracloud_ssh_key.ssh_public_key.id}"] disable_pw_auth = true lifecycle { ignore_changes = ["source_archive_id"] } } # サーバ resource "sakuracloud_server" "server" { name = "${local.server_name}" disks = ["${sakuracloud_disk.disk.id}"] core = "${local.server_core}" memory = "${local.server_memory}" packet_filter_ids = ["${sakuracloud_packet_filter.filter.id}"] additional_nics = ["${sakuracloud_switch.sw.id}"] } # スタートアップスクリプト(IP設定、metabaseコンテナ起動) locals { fqdn = "${local.server_name}.${local.dns_zone_name}" } resource "sakuracloud_note" "provisioning" { name = "provisioning-metabase" class = "yaml_cloud_config" content = <<EOF #cloud-config rancher: console: default docker: engine: docker-17.09.1-ce network: interfaces: eth1: address: 192.168.100.10/28 dhcp: false services: https-portal: image: sacloud/https-portal ports: - "80:80" - "443:443" volumes: - https-portal:/var/lib/https-portal environment: DOMAINS: "${local.fqdn} -> http://192.168.100.10:3000" STAGE: production restart: always metabase: image: metabase/metabase:latest ports: - "3000:3000" environment: MB_DB_TYPE: postgres MB_DB_DBNAME: ${local.database_user_name} MB_DB_PORT: 5432 MB_DB_USER: ${local.database_user_name} MB_DB_PASS: ${local.database_password} MB_DB_HOST: 192.168.100.2 restart: always EOF } ### データベースアプライアンス resource "sakuracloud_database" "db" { name = "${local.database_name}" database_type = "postgresql" plan = "${local.database_plan}" user_name = "${local.database_user_name}" user_password = "${local.database_password}" allow_networks = ["192.168.100.0/28"] port = 5432 backup_time = "${local.database_backup_time}" switch_id = "${sakuracloud_switch.sw.id}" ipaddress1 = "192.168.100.2" nw_mask_len = 28 default_route = "192.168.100.1" } ### パケットフィルタ resource "sakuracloud_packet_filter" "filter" { name = "${local.packet_filter_name}" expressions = { protocol = "tcp" dest_port = "22" description = "Allow external:SSH" } expressions = { protocol = "tcp" dest_port = "80" description = "Allow external:HTTP(for Let's encrypt)" } expressions = { protocol = "tcp" dest_port = "443" description = "Allow external:HTTPS" } expressions = { protocol = "icmp" } expressions = { protocol = "fragment" } expressions = { protocol = "udp" source_port = "123" } expressions = { protocol = "tcp" dest_port = "32768-61000" description = "Allow from server" } expressions = { protocol = "udp" dest_port = "32768-61000" description = "Allow from server" } expressions = { protocol = "ip" allow = false description = "Deny ALL" } } ### スイッチ resource sakuracloud_switch "sw" { name = "${local.switch_name}" } ### DNS data sakuracloud_dns "zone" { filter = { name = "Name" values = ["${local.dns_zone_name}"] } } #DNSレコード resource sakuracloud_dns_record "records" { dns_id = "${data.sakuracloud_dns.zone.id}" name = "${local.server_name}" type = "A" value = "${sakuracloud_server.server.ipaddress}" }
後はリソースマネージャーで展開するだけでOKです。
以上です。
【リソースマネージャー対応】さくらのクラウド上にMetabaseを構築する
Metabaseが流行ってきてますね。記事もちらほら見かけるようになりました。
- OSSのデータ可視化ツール「Metabase」が超使いやすい - Qiita
- MetabaseがRedashの苦労を吹き飛ばすくらい熱い
- Metabase BIツールをAWS Elastic Beanstalkで構築してみた - Developers.IO
- Metabaseがすごく良い - itFun.jp
- MetabaseをAzure Web App for Containersで動かしてみた
ということでさくらのクラウド上にMetabaseを構築してみました。
===>【2018/1/30追記】HTTPS対応版について記事書きました。
続・さくらのクラウド上にMetabaseを構築する【HTTPS対応版】
ドメインをお持ちの方はこちらも是非お試しください。
<=== 追記ここまで
Metabase環境構築 on さくらのクラウド
今回はさくらのクラウドのリソースマネージャーを利用して環境構築してみました。
今回の環境
今回MetabaseはDockerで動かします。Docker用のホストはRancherOSを使用します。 Metabase用のデータベースとしてPostgreSQL(さくらのクラウド上のデータベースアプライアンス)を利用します。
構築手順は以下の通りです。
- 1) サーバへのSSH用公開鍵をコンパネから登録
- 2) リソースマネージャでテンプレート作成&反映
1) サーバへのSSH用公開鍵をコンパネから登録
サーバへのSSH接続時に利用する公開鍵をコンパネなどから登録しておきます。 登録時に指定した名前を控えておいてください。
2) リソースマネージャーでテンプレート作成&反映
続いてリソースマネージャーにてテンプレートを作成します。 以下のtfファイルをコピペで登録してください。 なおtfファイルの最初の方にパスワードや先ほど登録した公開鍵の名称を指定している部分があります。 忘れずに各自で置き換えてください。
### 概要 # データベースアプライアンス(PostgreSQL)とRancherOSでMetabase実行環境を構築するテンプレート # # このテンプレートはRancherOS上のDockerでMetabaseを実行する構成となっています。 # Metabaseのバックエンドとしてデータベースアプライアンス(PostgreSQL)を利用します。 # ### 変数定義 locals { #********************************************* # パスワード/公開鍵関連(!!!要変更!!!) #********************************************* # サーバ管理者のパスワード server_password = "<put-your-password-here>" # データベース接続ユーザーのパスワード database_password = "<put-your-password-here>" # さくらのクラウドに登録済みの公開鍵の名称 ssh_public_key_name = "<put-your-public-key-name>" #********************************************* # サーバ/ディスク #********************************************* # サーバ名 server_name = "metabase-server" # サーバホスト名 host_name = "${local.server_name}" # サーバ コア数 server_core = 2 # サーバ メモリサイズ(GB) server_memory = 4 # ディスクサイズ disk_size = 20 #********************************************* # ネットワーク(スイッチ/パケットフィルタ) #********************************************* # スイッチ名 switch_name = "metabase-internal" # パケットフィルタ名 packet_filter_name = "metabase-filter" #********************************************* # データベースアプライアンス #********************************************* # データベースアプライアンス名 database_name = "metabase-db" # プラン database_plan = "30g" # 10g/30g/90g/240g # 接続ユーザー名 database_user_name = "metabase" # バックアップ時刻 database_backup_time = "01:00" } ### サーバ/ディスク # パブリックアーカイブ(OS)のID参照用のデータソース(RancherOS) data sakuracloud_archive "rancheros" { os_type = "rancheros" } # 公開鍵のID参照用のデータソース data "sakuracloud_ssh_key" "ssh_public_key" { name_selectors = ["${local.ssh_public_key_name}"] } # ディスク resource "sakuracloud_disk" "disk" { name = "${local.server_name}" source_archive_id = "${data.sakuracloud_archive.rancheros.id}" hostname = "${local.host_name}" password = "${local.server_password}" note_ids = ["${sakuracloud_note.provisioning.id}"] ssh_key_ids = ["${data.sakuracloud_ssh_key.ssh_public_key.id}"] disable_pw_auth = true lifecycle { ignore_changes = ["source_archive_id"] } } # サーバ resource "sakuracloud_server" "server" { name = "${local.server_name}" disks = ["${sakuracloud_disk.disk.id}"] core = "${local.server_core}" memory = "${local.server_memory}" packet_filter_ids = ["${sakuracloud_packet_filter.filter.id}"] additional_nics = ["${sakuracloud_switch.sw.id}"] } # スタートアップスクリプト(IP設定、metabaseコンテナ起動) resource "sakuracloud_note" "provisioning" { name = "provisioning-metabase" class = "yaml_cloud_config" content = <<EOF #cloud-config rancher: console: default docker: engine: docker-17.09.1-ce network: interfaces: eth1: address: 192.168.100.10/28 dhcp: false services: metabase: image: metabase/metabase:latest ports: - "80:3000" environment: MB_DB_TYPE: postgres MB_DB_DBNAME: metabase MB_DB_PORT: 5432 MB_DB_USER: ${local.database_user_name} MB_DB_PASS: ${local.database_password} MB_DB_HOST: 192.168.100.2 restart: always EOF } ### データベースアプライアンス resource "sakuracloud_database" "db" { name = "${local.database_name}" database_type = "postgresql" plan = "${local.database_plan}" user_name = "${local.database_user_name}" user_password = "${local.database_password}" allow_networks = ["192.168.100.0/28"] port = 5432 backup_time = "${local.database_backup_time}" switch_id = "${sakuracloud_switch.sw.id}" ipaddress1 = "192.168.100.2" nw_mask_len = 28 default_route = "192.168.100.1" } ### ネットワーク(パケットフィルタ) resource "sakuracloud_packet_filter" "filter" { name = "${local.packet_filter_name}" expressions = { protocol = "tcp" dest_port = "22" description = "Allow external:SSH" } expressions = { protocol = "tcp" dest_port = "80" description = "Allow external:HTTP" } expressions = { protocol = "icmp" } expressions = { protocol = "fragment" } expressions = { protocol = "udp" source_port = "123" } expressions = { protocol = "tcp" dest_port = "32768-61000" description = "Allow from server" } expressions = { protocol = "udp" dest_port = "32768-61000" description = "Allow from server" } expressions = { protocol = "ip" allow = false description = "Deny ALL" } } ### ネットワーク(スイッチ) resource sakuracloud_switch "sw" { name = "${local.switch_name}" }
tfファイル編集画面で「タブ分割/統合」ボタンを押すと以下のようにtfファイルをタブで分割してわかりやすく表示してくれます。 変更すべき内容は「変数定義」タブにまとめていますので目を通しておくのがオススメです。
登録後はリソースマネージャーのコマンドから計画/反映
を実行するだけです。
なお、手元の環境では2分ほどで構築完了しました。
動作確認
構築が完了したらhttp://<サーバのグローバルIP>
にアクセスするとmetabaseの画面が開くはずです。
後は画面に従って初期ユーザーの作成などを行うだけです。
終わりに
Metabaseいいですね! 以上です。
moby / linuxkit をさくらのクラウド対応させました
Linuxコンテナを実行できるコンテナプラットフォームを簡単に構築/展開できるmobyとlinuxkitをさくらのクラウドに対応させてみましたのでご紹介します。
(2017/10/23追記): システム要件としてDockerとGNU Makeのインストールが必要な旨を追記しました
TL; DR
以下のようにすればmoby
とlinuxkit
でさくらのクラウド上に簡単にコンテナプラットフォームを構築できます。
※あらかじめDocker
とGNU Make
をインストールしておく必要があります。
# sacloud/linuxkitのインストール $ brew tap sacloud/linuxkit $ brew install --HEAD moby $ brew install --HEAD linuxkit # さくらのクラウドAPIキーを環境変数に設定 $ export SAKURACLOUD_ACCESS_TOKEN="your-token" $ export SAKURACLOUD_ACCESS_TOKEN_SECRET="your-secret" $ export SAKURACLOUD_ZONE="tk1a" # mobyコマンドでraw形式のイメージ作成 $ moby build -format raw -size 256M sakuracloud.yml # linuxkit pushでさくらのクラウド上にアップロード $ linuxkit push sakuracloud sakuracloud.raw # linuxkit runでさくらのクラウド上にサーバ作成/起動 $ linuxkit run sakuracloud sakuracloud
moby/linuxkitとは?
mobyとlinuxkitについてはPublickeyの以下の記事にわかりやすくまとめられています。
Publickey: Docker、「LinuxKit」を発表。コンテナランタイムのためだけにゼロから開発されたセキュアなLinux Subsystem。DockerCon 2017
全てがコンテナで実行される軽量でimmutableなLinuxイメージを作成できるツールとなっています。
どうやって使うの?
LinuxKitを使ってLinuxイメージを作成するためにmoby
コマンドが提供されています。
moby
コマンドは、使用するカーネルやinitプロセス、動かしたいコンテナといった構成情報をyaml形式のファイルで定義し、定義に沿ったLinuxイメージを作成してくれます。
出力形式は以下のようなものがサポートされており、AWS/Azure/GCPといったクラウド上で利用できるイメージだけでなくOpenStackやオンプレのベアメタルサーバなどに対応できる形式のイメージが作成できます。
moby
コマンドで作成できるイメージの形式
作成したイメージはブータブルとなっており、自分でクラウド上にアップロードしたりオンプレの仮想化基盤に登録したりすることでイメージを利用したサーバを起動できるようになっています。
(ISOイメージで出力してCD/DVDなどのメディアを用意する方法も可能)
自分で(各クラウドの)コントロールパネルなどからアップロードしても良いですしCLIなどを利用してもよいですが、これらを簡単に行えるようにlinuxkit
コマンドが用意されています。
linuxkitコマンドは何をするもの?
linuxkit
コマンドは主にmoby
コマンドで作成したイメージをクラウド(など)へアップロードし、そのイメージを用いて起動するサーバの作成を行ってくれます。
メタデータ用ISOイメージ作成や構成要素として利用できるパッケージ作成などの補助機能もあります。
イメージ作成〜サーバ起動までの利用イメージは以下のようになります。
# mobyコマンドでイメージ作成(example.rawファイルが作成される) $ moby build -format raw example.yml # linuxkit pushでアップロード $ linuxkit push aws -bucket bucketname example.raw # linuxkit runでアップロードしたイメージを利用したサーバを作成/起動 $ linuxkit run aws example
定義ファイル(yaml)の作り方についてはこちらのドキュメントに詳しく記載されています。
linuxkitドキュメント: Yaml Configuration Document
現時点では以下のクラウド(など)に対応しています。
イメージのアップロード(linuxkit push
)対応先の一覧
イメージからのサーバ作成/起動(linuxkit run
)対応先の一覧
標準ではさくらのクラウドに対応していませんので今回対応させてみました。
linuxkitのさくらのクラウド対応
以下のリポジトリにlinuxkitをforkしてさくらのクラウド対応を行っています。
このリポジトリを利用することでlinuxkit
でさくらのクラウド上にイメージのアップロードを行いサーバ起動を行うことが可能となります。
sacloud/linuxkitのインストール
まずはforkしたsacloud/linuxkitのインストールを行う必要があります。 以下2つの方法があります。
- 方法1) homebrewを利用してインストール
- 方法2) 上記リポジトリをクローンして自分でビルド
方法1) homebrewでsacloud/linuxkitをインストール
本家linuxkitと同じくhomebrewでのインストールを行えるようにしています。
# まずはtap $ brew tap sacloud/linuxkit # インストール実施 $ brew install --HEAD moby $ brew install --HEAD linuxkit
もしすでにlinuxkit/linuxkit
をtapしている場合は名前が衝突しますのでbrew install
時に以下のように完全名で指定する必要があります。
$ brew install --HEAD sacloud/linuxkit/moby $ brew install --HEAD sacloud/linuxkit/linuxkit
方法2) sacloud/linuxkitをクローンして自分でビルド
ビルドにはGo言語の開発環境が必要です。$GOPATH
の設定なども行っておく必要があります。
$GOPATH/src/github.com/linuxkit/linuxkit
ディレクトリにsacloud/linuxkit
をクローンしてmake
を実行すればOKです。
# ディレクトリ作成 $ mkdir -p $GOPATH/src/github.com/linuxkit # クローン $ git clone https://github.com/sacloud/linuxkit.git $GOPATH/src/github.com/linuxkit/linuxkit # 移動 $ cd $GOPATH/src/github.com/linuxkit/linuxkit # ビルド(binディレクトリ配下にlinuxkit/mobyコマンドが作成される) $ make
必要に応じて$PATH
の設定を行ってください。
sacloud/linuxkitの実行
あとは通常のmoby
とlinuxkit
と同様の手順でOKです。
システム要件もmoby
とlinuxkit
と同じく以下がインストールされていること、となっています。
なお、linuxkit
でAzureを利用する場合などと同じくログイン情報(APIキー)を環境変数に登録しておく必要があります。
sacloud/linuxkit
では以下の環境変数の設定を行っておく必要があります。
さくらのクラウドのコントロールパネルでAPIキーを発行しておいてください。
(環境変数はusacloudやTerraform for さくらのクラウドと共通となっています)
# APIキー(アクセストークン) $ export SAKURACLOUD_ACCESS_TOKEN="your-access-token" # APIキー(アクセスシークレット) $ export SAKURACLOUD_ACCESS_TOKEN_SECRET="your-access-secret" # 対象ゾーン(石狩第1: is1a / 石狩第2: is1b / 東京第1: tk1a / サンドボックス: tk1v) $ export SAKURACLOUD_ZONE="tk1a"
イメージのビルド
定義ファイルを用意した上でmoby
コマンドでビルドを行います。
定義ファイルの例をGitHub上で公開していますのでそれを元に作成してください。
さくらのクラウドでの定義ファイルの例: GitHub: sacloud/linuxkit/examples/sakuracloud.yml
kernel: image: linuxkit/kernel:4.9.56 cmdline: "console=tty0 console=ttyS0 console=ttyAMA0" init: - linuxkit/init:6b3755e47f00d6027321d3fca99a19af6504be75 - linuxkit/runc:52f92cb577879ce4cfe4e89be2d63af82523fc92 - linuxkit/containerd:ed8e8f92e24dd4b94260cf147594ae3fd13a2182 - linuxkit/ca-certificates:ea3c4c120f929f4f07ac8535d75933365b5e9582 onboot: - name: sysctl image: linuxkit/sysctl:1644bf07edbcaf5ce0bb764fa925b544183547f9 - name: rngd1 image: linuxkit/rngd:45ed7759dd927f4cce3863073ea2e0da1d52a427 command: ["/sbin/rngd", "-1"] services: - name: getty image: linuxkit/getty:7abaf7b276c59f80891d92e9279e3e3ee8e2f512 env: - INSECURE=true - name: rngd image: linuxkit/rngd:45ed7759dd927f4cce3863073ea2e0da1d52a427 - name: dhcpcd image: linuxkit/dhcpcd:aa685261ceb2557990dcfe9dd8824c6b9ec416e2 - name: sshd image: linuxkit/sshd:4a2fc7be31fa57dcade391de6173e0af55296e7f files: - path: root/.ssh/authorized_keys source: ~/.ssh/id_rsa.pub mode: "0600" optional: true trust: org: - linuxkit
この例はSSHDを起動するイメージの例となっています。
SSH用のキーペアをssh-keygen
などで作成し定義ファイルに公開鍵のファイルパスを記載してください。(デフォルトでは~/.ssh/id_rsa.pub
が指定されています)
定義ファイルを作成したらmoby
コマンドを実行します。
さくらのクラウドではAWSなどと同じく-format
オプションにraw
を指定する必要があります。
# mobyコマンドでraw形式のイメージ作成 $ moby build -format raw -size 256M sakuracloud.yml
デフォルトでは定義ファイルの拡張子を除いたもの+.raw
というファイル名でイメージが作成されます。
これは-name
オプションで上書き可能です。
また、イメージのサイズはデフォルトで1024M
(1GB)となっています。
SSHを実行する程度のイメージであれば256Mもあれば十分ですので-size
オプションでサイズを明示しています。
定義ファイルの内容によってこの値は調節する必要があります。
うまくいくとsakuracloud.raw
というファイルが作成されるはずです。
イメージのアップロード
次にlinuxkit push
コマンドでアップロードを行います。
# 作成したイメージをさくらのクラウドへアップロード $ linuxkit push sakuracloud sakuracloud.raw
デフォルトではイメージファイル名の拡張子を除いたものがさくらのクラウドのアーカイブ名として利用されます。
この例ではsakuracloud
という名前でアーカイブが作成されます。
実行!!
いよいよサーバの作成/起動です。
linuxkit run
コマンドにアップロードしたアーカイブ名を指定することで起動できます。
# アップロードしたアーカイブを利用してサーバ作成/起動 $ linuxkit run sakuracloud sakuracloud
現在は以下のオプションが利用可能です。
-name
: 作成されるサーバの名称(デフォルトではアーカイブ名と同じになる)-core
: 作成されるサーバのコア数(デフォルト1
)-memory
: 作成されるサーバのメモリサイズ、単位はGB(デフォルト1
)-disk-size
: 作成されるサーバのディスクサイズ、単位はGB(デフォルト20
)
-core
と-memory
の組み合わせによってはサーバ作成時にエラーとなりますので、以下のドキュメントを参考にサポートされている組み合わせを指定してください。
作成されたら以下のコマンドでSSH接続可能です。
(定義ファイルの内容によってはSSH接続できない場合もあります)
$ ssh -i your-private-key-path root@サーバのグローバルIP
もしSSH接続できないイメージを作成した場合、SSH接続の代わりにさくらのクラウドCLIであるusacloud
を用いてVNC接続を行うことも可能です。
# usacloudでVNC接続 $ usacloud server vnc [サーバ名 or サーバID]
終わりに
今回はmoby
とlinuxkit
を用いてさくらのクラウド上でコンテナプラットフォームを簡単に構築する方法をご紹介しました。
linuxkit
を用いれば特定機能に特化したイメージを簡単に作成でき、アップロード/サーバ作成と起動も手軽に行えますね。
ぜひお試しください。以上です。