HashiCorp Certified: Terraform Associateを受験した

本日(2020/4/22)HashiCorpの認定試験であるHashiCorp Cloud Engineer Certification Programが一般公開され、Terraform/Vaultのアソシエイトレベルの試験が開始されました。

去年のHashiConfでベータ版としてアナウンスされてたやつですね。

受験費用は$70.5(税別)となんとか手が出る価格だったので早速受験してみました。

結果

無事合格しました。満点取れるかなと思ったのですがいくつか取りこぼしちゃいましたね。。。

f:id:febc_yamamoto:20200423000343p:plain

ひとまず安心しました。

www.youracclaim.com

今後受験する人向けに書ける範囲でメモを残しておきます。

試験の概要/試験範囲

www.hashicorp.com

受験までの流れ

  • 上記サイトから申し込み
  • 申込サイト(ondemand.questionmark.com)にてアカウント作成
  • 同サイトにてシステム要件のチェック(Webカメラ、スピーカー、マイクなど。専用のチェックページがログイン後の画面にある)
  • Buy ExamというタブがあるのでそこからHashiCorp Certified: Terraform Associateを購入
  • 同サイトから受験する日時をスケジューリングする(多分PDTなのでJSTの日中は選べない)
  • (重要) 案内メールが来る
    このメールに受験のルールなどの詳細が記載されているので必ず目を通す
  • 時間になったら同サイトから試験開始

事前に用意しておくもの

国が発行した写真付きの身分証明書(パスポートなど)

勉強した内容

とりあえず試験範囲をざっと眺めて、Terraformのドキュメントを通読しました。

www.terraform.io

このドキュメントにはTerraform v0.12以前/以降両方のドキュメントが含まれるのですが、今回の試験はTerraform v0.12以降が対象ですのでv0.12以前のバージョンについて書かれたドキュメントについては無視して構いません。

また、試験用の教材として以下の2つが用意されていますのでこれらを読むのも良いかもしれません。

learn.hashicorp.com

learn.hashicorp.com

合格後の特典

Credly’s Acclaim platformの提供するデジタルバッジがもらえるとのことでした。

↓↓こんなやつです。

f:id:febc_yamamoto:20200423002531p:plain

出典: https://www.youracclaim.com/org/hashicorp/badge/hashicorp-certified-terraform-associate

合格後10日以内に案内のメールが来るらしいです。

4/29 追記: その後無事に届きました。

www.youracclaim.com

感想

内容よりも英語が大変でした。
記憶力だけを問う意地の悪い問題はなかったので、丁寧に試験範囲のドキュメントを読んでおけば十分じゃないかなと感じました。

終わりに

次はVaultを受けてみます。。。お小遣いが貯まったら。

以上です。

Zabbix+さくらのクラウドAPIでクラウド上のリソースを監視する

はじめに

先日teratailに以下のような質問が投稿されていました。

teratail.com

回答を書きながらいくつか資料を探してみたのですが、さくらのクラウド+Zabbix 4系の記事があまり見当たりませんでしたので自分で試した時のメモを残しておくことにしました。

この記事で扱う内容

この記事ではさくらのクラウド上のリソースをZabbix+さくらのクラウドAPIを利用して監視する方法について取り扱います。
具体的には以下のような内容となっています。

Zabbixのインストールや、そもそもの監視設計(監視対象の選択や通知の設計など)については扱いませんので他サイトなどを参照してください。

それでは早速本題に入っていきます。

ZabbixでさくらのクラウドAPIを通じてクラウド上のリソースを監視する

ZabbixからさくらのクラウドAPIを通じてクラウド上のリソースを監視するにはいくつかの方法があります。

  • 1: 外部チェック/ユーザーパラメータ
  • 2: HTTP agent(Zabbix 4.0以降)
  • 3: PrometheusのExporter + Prometheusチェック(Zabbix 4.2以降)

順番にみていきます。

1. 外部チェック/ユーザーパラメータ

Zabbixサーバ上、またはエージェント上でスクリプト/バイナリを実行じその結果を受け取る方法です。
少々古いバージョンのZabbix(2.2とか)でも利用できます。

参考:

標準出力に何か出力しておけばそれをZabbixから参照できるということですね。

さくらのクラウドAPIを叩く場合はcurlコマンド、またはCLIであるUsacloudを利用し、 出力をjqコマンドなどで加工してあげると良いと思います。

単純な値をcurlで取得する

# curlでサーバのメモリサイズを取得
$ export TOKEN="さくらのクラウドAPIトークン"
$ export SECRET="さくらのクラウドAPIシークレット"
$ export ZONE=is1a
$ export SERVER_ID=123456789012 # サーバのID
$ curl --user "$TOKEN":"$SECRET" https://secure.sakura.ad.jp/cloud/zone/$ZONE/api/cloud/1.1/server/$SERVER_ID | jq ".Server.ServerPlan.MemoryMB"
4096

2020/01/23 コード誤りを修正

アクティビティグラフの値をcurlで取得する

スペックなどを取得する場合はjqコマンドが単純で済むのですが、アクティビティグラフAPIを使用してCPU利用時間やディスクの読み書き量を取得するとなるとちょっと難易度が上がります。

具体的には、アクティビティグラフAPIは以下のようなJSONを返します。

# サーバのCPU-TIMEを取得
$ curl -s --user "$TOKEN":"$SECRET" https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/server/$SERVER_ID/monitor | jq .
{
    "Data": {
        "2020-01-17T16:45:00+09:00": {
            "CPU-TIME": 0.043333333333
        },
        "2020-01-17T16:50:00+09:00": {
            "CPU-TIME": 0.046666666667
        },
        "2020-01-17T16:55:00+09:00": {
            "CPU-TIME": null
        },
        "2020-01-17T17:00:00+09:00": {
            "CPU-TIME": null
        },
        "2020-01-17T17:05:00+09:00": {
            "CPU-TIME": null
        },
        "2020-01-17T17:10:00+09:00": {
            "CPU-TIME": null
        },
        "2020-01-17T17:15:00+09:00": {
            "CPU-TIME": null
        },
        "2020-01-17T17:20:00+09:00": {
            "CPU-TIME": null
        }
    },
    "is_ok": true
}

日付がキーとなっていること、値にnullが入っていることでjqコマンドの書き方が難しいですね。
これを処理する場合、

  • Data配下の要素からCPU-TIMEの値を抜き出し
  • nullを除去
  • 配列の最後の値を取得(昇順になっているため、最新の値は末尾にある)

という感じになります。面倒ですね…
ひとまず手元で確認したところ、jq '[.Data[]["CPU-TIME"]] | del(.[] | nulls) | .[-1]'でいけました。

# curlでアクティビティグラフAPIを叩き値を抽出
$ curl --user "$TOKEN":"$SECRET" https://secure.sakura.ad.jp/cloud/zone/is1a/api/cloud/1.1/server/$SERVER_ID/monitor | jq '[.Data[]["CPU-TIME"]] | del(.[] | nulls) | .[-1]'
0.046666666667

これで外部チェック/ユーザーパラメータからアクティビティグラフAPIの値を活用できそうですね。

アクティビティグラフの値をusacloudで取得する

usacloudコマンドを利用すればさらに楽に値を抽出できます。 というのも、usacloudコマンドは以下のようにAPIから返されるJSONを変換し、単純な配列にしてから出力するからです。null値のフィルタリングもしてくれてます。
こちらの形の方がjqなどで加工しやすいですよね。

$ usacloud server monitor-cpu -o json $SERVER_ID
[
  {
    "CPUTime": "0.043333333333",
    "Key": "sakuracloud.server.123456789012.cpu",
    "TimeStamp": "2020-01-17 16:45:00 +0900 JST"
  },
  {
    "CPUTime": "0.046666666667",
    "Key": "sakuracloud.server.123456789012.cpu",
    "TimeStamp": "2020-01-17 16:50:00 +0900 JST"
  }
]

これを利用すると、先ほどのcurlコマンドの例は以下のように書き直せます。

# usacloudでアクティビティグラフAPIを叩き値を抽出
$ usacloud server monitor-cpu -o json $SERVER_ID | jq '.[-1].CPUTime|tonumber'
0.046666666667

2. HTTP agent(Zabbix 4.0以降)

Zabbix 4.0以降で利用できるHTTP agentを利用する方法です。

参考:

とりあえずAPIを叩くだけであれば簡単にできます。

まずアイテム作成画面でタイプがHTTPエージェントなアイテムを作成します。 この時、URLにはさくらのクラウドAPIのURLを入力し、http認証をBasicにしてユーザー名/パスワード欄にそれぞれAPIトークン/シークレットを入力します。

f:id:febc_yamamoto:20200123224743p:plain

f:id:febc_yamamoto:20200123224956p:plain

次にこのアイテムをマスターにした依存アイテムを作成します。

f:id:febc_yamamoto:20200123225035p:plain

ポイントは「保存前処理」を追加する部分ですね。 単純なJSONならJSONPathが使えるのですが、前述のアクティビティグラフAPIのレスポンスのような複雑なものを扱う場合はJavaScriptを使うのが良いと思います。

ここでは取得したJSONからCPU-TIMEの値を抜き出し、nullでない末尾の値を抽出するスクリプトを記載しておきます。

f:id:febc_yamamoto:20200123225306p:plain

// コピペ用
const data = JSON.parse(value).Data;
return Object.keys(data).map(function(v){return data[v]["CPU-TIME"]}).filter(function(v){ return v}).pop()

少々回りくどい書き方になっているのはZabbix 4.4の時点でもES6/ES7は部分的なサポートのみだからです。
なのでObject.values()アロー関数が使えないんですよね。。。

これはZabbixが採用しているJavaScriptのエンジンDuktape由来です https://duktape.org/

ともあれこれでHTTP agentを利用してアクティビティグラフAPIを叩くことができました。 テンプレート化して汎用化する必要はありますが、単体のリソースを監視する程度であればこの方法だけでも十分使えると思います。

PrometheusのExporter + Prometheusチェック(Zabbix 4.2以降)

Zabbix 4.2以降というのをクリアできれば正直この方法が一番楽だと思います。

さくらのクラウド向けのPrometheus Exporterであるsakuracloud_exporterと組み合わせる方法です。

github.com

設定自体も簡単で、以下の記事などを参考にすれば設定で迷うことはないと思います。

blog.zabbix.com

tech-lab.sios.jp

Exporterを別途起動しておく必要がある点には注意してください。


ということでZabbix+さくらのクラウドAPIを利用するための3つの方法について紹介しました。

終わりに

今回はZabbix+さくらのクラウドAPIクラウド上のリソースを監視する方法を紹介しました。
これだけで全ての監視を賄えるわけではないですが、多様な角度から監視する上での選択肢の1つにはなると思います。

以上です。

作業メモ: TeamCity on さくらのクラウド+エンハンスドロードバランサ

JetBrainsのTeamCityをさくらのクラウド上に構築した時のメモです。

www.jetbrains.com

概要

構成

f:id:febc_yamamoto:20191218174908p:plain:w500

  • サーバ1台(パケットフィルタ込み)
  • エンハンスドロードバランサ(100cps プラン)
    • SSL終端
    • Let's Encryptでの証明書取得/更新
  • TeamCityはDockerで起動

サーバ上で直接証明書取得 & SSL終端してもよかったのですが、環境構築がめんどくさかったのでエンハンスドロードバランサを利用します。
また、サーバへのアクセスはエンハンスドロードバランサに限定するためにパケットフィルタも合わせて利用します。

構築手順

  • サーバ作成/Dockerインストール
  • エンハンスドロードバランサ作成
  • エンハンスドロードバランサで発行されたVIP or FQDNDNS登録(A or CNAMEレコード)
  • エンハンスドロードバランサの設定(待ち受けポート/実サーバ/Let's Encryptなど)
  • パケットフィルタ作成 & サーバにアタッチ
  • TeamCityをDocker上で起動
  • TeamCityのエージェントをDocker上で起動

各手順の詳細

サーバ作成/Dockerインストール

まずはサーバを作成します。今回は2CPU/4GBメモリ/共有セグメントに接続、というスペックで作成しました。
サーバ作成後はDockerがインストールされていない場合はインストールしておきます。

エンハンスドロードバランサ作成

次にエンハンスドロードバランサを作成します。
VIPフェイルオーバを有効にする/しないでこの後作成するDNSレコードの種別が変わりますのでご注意ください。

f:id:febc_yamamoto:20191218180325p:plain:w800

エンハンスドロードバランサで発行されたVIP or FQDNDNS登録(A or CNAMEレコード)

エンハンスドロードバランサを作成したらFQDN or VIPが払い出されます。この値をDNS登録しておきます。
前述の通りVIPフェイルオーバの有効/無効に応じて登録するDNSレコード種別が変わります。

VIPフェイルオーバを有効にした場合

FQDNが発行されますのでCNAMEレコードを登録します。

f:id:febc_yamamoto:20191218181219p:plain

VIPフェイルオーバを無効にした場合:

VIP(Virtual IP Address)が発行されますのでAレコードを登録します。

f:id:febc_yamamoto:20191218194905p:plain

エンハンスドロードバランサの設定

次にエンハンスドロードバランサの設定を行います。

待ち受けポート

以下のように2つ登録します。

f:id:febc_yamamoto:20191218181817p:plain:w800

(1) HTTP
  • プロキシ方式: http
  • 待受ポート番号: 80
  • httpsへのリダイレクト: 有効
(2) HTTPS
  • プロキシ方式: https
  • 待受ポート番号: 443
  • HTTP/2のサポート: 無効

実サーバ

IPアドレスに先ほど作成したサーバのIPを入力して登録します。
ポート番号は8111、サーバグループは空のままとします。

f:id:febc_yamamoto:20191218182449p:plain:w800

Let's Encryptの設定

次に右上のSSH証明書の設定 -> Let's Encryptの設定に進み、Let's Encryptでの証明書自動取得/更新を有効化します。

f:id:febc_yamamoto:20191218182754p:plain:w800

先ほどCNAMEレコードまたはAレコードを登録したFQDNを指定してください。 (エンハンスドロードバランサから払い出されたFQDNではありません)

パケットフィルタ作成 & サーバにアタッチ

次にサーバへのアクセスをエンハンスドロードバランサからだけに絞るためにパケットフィルタを作成してサーバにアタッチします。

まず、エンハンスドロードバランサが実サーバにアクセスする際のアクセス元IPレンジをメモしておきます。
エンハンスドロードバランサの詳細画面のプロキシ元ネットワークという項目です。

f:id:febc_yamamoto:20191218183630p:plain:w800

次にパケットフィルタを作成し、エンハンスドロードバランサからのアクセスだけ許可、以外は拒否するルールを設定します。
(この例では管理用にtcp/10022ポートの許可を追加してます)

f:id:febc_yamamoto:20191218183827p:plain:w800

作成したらサーバの詳細画面からNICに対してパケットフィルタを接続(アタッチ)しておきましょう。サーバを起動したままでアタッチ可能です。

TeamCityをDocker上で起動

ようやくTeamCityのインストールです。 以下のマニュアルからDockerでのインストール周りへと辿ります。

pleiades.io

余談ですが、日本語版マニュアルがjetbrains.comじゃなくてpleiades.ioなのが気になって調べたら↓↓が出てきました。
https://blog.jetbrains.com/jp/2018/10/18/1355
Pleiadesは昔大変お世話になりました。

今回は以下のように起動しました。

# bridgeネットワークを作成しておく
$ docker network create teamcity

# TeamCityサーバ起動
$ docker run -d --name teamcity-server-instance \
    -v teamcity-data:/data/teamcity_server/datadir \
    -v teamcity-log:/opt/teamcity/logs \
    -p 8111:8111  \
    --network teamcity \
    jetbrains/teamcity-server

面倒だったのでDocker ComposeやKubernetesを使わずにDockerコマンドを直接使ってます。

TeamCityのエージェントをDocker上で起動

次にTeamCityのエージェントをDocker上で起動します。 (リスクのある方法ですので以下のドキュメントをちゃんと読んでおきましょう)

https://hub.docker.com/r/jetbrains/teamcity-agent/

今回は以下のように起動しました。

$ docker run -d --name teamcity-agent-01 \
    -e SERVER_URL="http://teamcity-server-instance:8111" \
    -e AGENT_NAME="agent-01" \
    -v teamcity-agent-data:/data/teamcity_agent/conf \
    --network teamcity \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /opt/buildagent/work:/opt/buildagent/work \
    -v /opt/buildagent/temp:/opt/buildagent/temp \
    -v /opt/buildagent/tools:/opt/buildagent/tools \
    -v /opt/buildagent/plugins:/opt/buildagent/plugins \
    -v /opt/buildagent/system:/opt/buildagent/system \
    jetbrains/teamcity-agent

アクセスしてみる

ブラウザでAレコード or CNAMEレコードを登録したFQDNにアクセスするとTeamCityの画面が開くはずです。

f:id:febc_yamamoto:20191218191231p:plain:w800

あとは画面の指示/マニュアルを参考に進めていけばOKなはずです。

終わりに

今回はバックアップなど運用面をあまり気にしてませんので実運用の際はオプションの精査などをしっかり行ってください。

以上です。

TerraformのRKEプロバイダーをRancher Labsに移管しました

f:id:febc_yamamoto:20191118145250p:plain

はじめに

Rancher Labsが提供するRKE(Rancher Kubernetes Engine)をTerraformから扱えるようにするRKEプロバイダー私の個人リポジトリからRancher Labs配下へ移管しました。

github.com

移管によりこれまで以上にRKE本体との連携も進むでしょうし、今後はより一層安定してご利用いただけるようになりそうです。

TerraformのRKEプロバイダーって何?

こちらで紹介しています。

febc-yamamoto.hatenablog.jp

speakerdeck.com

どんな人がRKEプロバイダーを使ってるの?

国外での利用が多いです。AWS(EC2)やAzure、OpenStack、vSphere、DigitalOceanとの組み合わせあたりでご利用いただくケースが多いです。

Rancher Labsが公開しているブログやYouTubeチャンネルなどで紹介されたこともあります。
以下のようなところで紹介されました。

Rancher Labsブログ

rancher.com

RanchCast(YouTube)

www.youtube.com (32:50秒あたりから)

Medium

使ってみた系記事: medium.com

RKEプロバイダーを組み込んだtk8というツール blog.kubernauts.io

これからRKEプロバイダーはどうなるの?

私は開発から一歩引き、今後はRancher Labs主導で開発が進められます。

Rancher LabsにはすでにRancher2プロバイダーがあり、一見RKEプロバイダーと機能的に重複して見えますが 利用シーンが異なるためある程度コードベースの統合はあってもプロバイダー自体は独立して開発が続くのではないかと思っています。

ちょうどそろそろRKEもv1.0に到達しようとしています。
(2019/11/18現在はv1.0.0-rc5までリリースされています)
これに追随してRKEプロバイダーもバージョンアップしていくのではないかと期待しています。

終わりに

Rancher Labs主導で開発が行われることでより安心して利用できるようになると思います。
ぜひご利用ください!

RancherによるKubernetes活用完全ガイド (Think IT Books)

RancherによるKubernetes活用完全ガイド (Think IT Books)

Terraformからウェブアクセラレータの証明書を管理する

Terraformからウェブアクセラレータを参照/一部の操作ができるように

Terraformのさくらのクラウドプロバイダー v1.18.1からウェブアクセラレータ向けのリソースが追加されました。

github.com

sakuracloud_webaccelデータソース

サイト情報を参照するためのものです。
名前 or ドメインを指定して対象サイトの情報を参照できます。

以下のようなtfファイルで利用します。

data sakuracloud_webaccel "example" {
  domain = "www.example.com"
  # または
  # name = "example"
}

以下のような情報が参照可能です。

$ terraform show
data "sakuracloud_webaccel" "example" {
    cname_record_value = "xxxxxx.user.webaccel.jp."
    domain             = "www.example.com"
    domain_type        = "own_domain"
    has_certificate    = true
    id                 = "xxx"
    name               = "example"
    origin             = "192.0.2.1"
    site_id            = "xxx"
    status             = "enabled"
    subdomain          = "xxxxx.user.webaccel.jp"
    txt_record_value   = "webaccel=xxxxx.user.webaccel.jp"
}

利用例: サイト有効化に必要なDNSレコードの登録

ウェブアクセラレータでは独自ドメイン利用時にCNAMEまたはTXTレコードの設定が必要になります。
このデータソースはそのための値を持っており、以下のようにすることでレコードの登録が簡単に行えます。

data sakuracloud_webaccel "example" {
  domain = "www.example.com"
}

data sakuracloud_dns "zone" {
  name_selectors = ["example.com"]
}

# webaccelデータソースの値を参照してDNSレコード登録
resource "sakuracloud_dns_record" "webaccel" {
  dns_id = sakuracloud_dns.zone.id
  name   = "www"
  type   = "CNAME"
  value  = data.sakuracloud_webaccel.example.cname_record_value
}

証明書の登録/更新が行えるsakuracloud_webaccel_certificateリソース

以下のようなコードで証明書の登録/更新が行えるようになりました。

data sakuracloud_webaccel "site" {
  name = "example"
}

resource sakuracloud_webaccel_certificate "example" {
  site_id           = data.sakuracloud_webaccel.site.id
  certificate_chain = file("crt")
  private_key       = file("key")
}

応用例: TerraformのACMEプロバイダーと組み合わせる

ウェブアクセラレータに登録する証明書をTerraformのACMEプロバイダーを用いてLet's Encryptで取得してみます。

www.terraform.io

今回はDNSを用いて取得します。ACMEプロバイダーではDNSプロバイダーとしてさくらのクラウドDNSがサポートされていますのでこちらを利用します。

www.terraform.io

コードは以下のようになります。

data sakuracloud_webaccel "site" {
  name = "example"
}

resource sakuracloud_webaccel_certificate "foobar" {
  site_id           = data.sakuracloud_webaccel.site.id
  certificate_chain = "${acme_certificate.cert.certificate_pem}${acme_certificate.cert.issuer_pem}"
  private_key       = acme_certificate.cert.private_key_pem
}

provider "acme" {
  # レートリミットに注意
  server_url = "https://acme-v02.api.letsencrypt.org/directory"
}

resource "tls_private_key" "private_key" {
  algorithm = "RSA"
}

resource "acme_registration" "reg" {
  account_key_pem = "${tls_private_key.private_key.private_key_pem}"
  email_address   = "nobody@example.com" #自分のメールアドレスに置き換える
}

resource "acme_certificate" "cert" {
  account_key_pem           = "${acme_registration.reg.account_key_pem}"
  common_name               = "www.example.com"    # 任意のドメインに変更
  subject_alternative_names = ["www2.example.com"] # 任意のドメインに変更

  dns_challenge {
    provider = "sakuracloud"
    config = {
      # APIキーを環境変数から指定する場合は以下2つの指定は不要
      #      SAKURACLOUD_ACCESS_TOKEN = "APIトークン"
      #      SAKURACLOUD_ACCESS_TOKEN_SECRET = "APIシークレット"
      SAKURACLOUD_PROPAGATION_TIMEOUT = "300"
    }
  }
}

あとは定期的にterraform applyするようにすれば良いでしょう。

終わりに

ぜひご利用ください!

さくらのクラウドでUbuntuのクラウドイメージ+cloud-initを利用する

はじめに

さくらのクラウド上でUbuntuの「クラウドイメージ」を利用する機会がありましたので方法をまとめておきます。

手順

順番にみていきます。

Ubuntuクラウドイメージの準備

cloud-initが構築済みの「クラウドイメージ」というものがUbuntuで提供されています。

gihyo.jp

今回はこれを利用します。ただし、さくらのクラウドはraw形式にのみ対応していますので、イメージをダウンロードした後変換してからさくらのクラウドへアップロードします。

なお、以下の手順では作業マシン上でqemu-imgコマンドとusacloudコマンドを利用します。

qemu-imgコマンドについてはyum install qemu-imgapt install qemu-utilsなどでインストールしておきます。
usacloudコマンドはこちらを参考にインストール&APIキーの設定を済ませておきます。

# ダウンロード(今回は18.04を利用)
curl -sL -o ubuntu.img https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img 

# qcowからraw形式(sparse)へ変換
qemu-img convert ubuntu.img ubuntu-sparse.raw

# non-sparseファイルにする
cp --sparse=never ubuntu-sparse.raw ubuntu.raw

# さくらのクラウド上にアーカイブとしてアップロード
usacloud archive create --name my-ubuntu --size 20 --archive-file ubuntu.raw  

qemu-imgでraw形式に変換後、non-sparseファイルにするのがポイントです。
(試しにsparseファイルのまま試したらサーバ起動できませんでした)

NoCloudデータソース用にISOイメージを作成&アップロード

次にNoCloudデータソース用のISOイメージを作成、アップロードを行います。 NoCloudデータソースについては以下の記事を参考に作成しました。 gihyo.jp

今回はubuntuユーザーにパスワードを設定しパスワード認証でSSH接続できるようにするという内容になってます。

# user-dataファイルの作成(!パスワードは任意の値に置き換えてください)
PASSWORD=your-password
cat >user-data <<EOF
#cloud-config
password: $PASSWORD
chpasswd: { expire: False }
ssh_pwauth: True
EOF

# meta-dataファイルの作成(とりあえず空でOK)
touch meta-data

# ISOイメージの作成(linux上で作業する場合)
mkisofs -R -V cidata -o cloud-init.iso ./

# ISOイメージの作成)mac上で作業する場合)
# hdiutil makehybrid -iso -joliet -default-volume-name cidata -o cloud-init.iso ./

# さくらのクラウド上にISOファイルをアップロード
usacloud iso-image create --name cloud-init --iso-file cloud-init.iso

作成したアーカイブ/ISOイメージを利用してサーバ作成

次にサーバの作成を行います。

usacloud server build \
  --source-archive-id=`usacloud archive read -q my-ubuntu` \
  --iso-image-id=`usacloud iso-image read -q cloud-init` \
  --name=example

この後作成したサーバに対しSSH接続できるようになっているはずです。

まとめ

  • Ubuntuクラウドイメージをダウンロード後、non-sparseなraw形式に変換してアップロード
  • cloud-initのデータソースにはISOイメージファイルを作成してNoCloudデータソースを利用

ISOイメージファイルの作成部分をうまく自動化すれば色々活用できますね。

以上です。

GitHub ActionsでTerraformを実行する時にTerraform Cloudをバックエンドに指定する

某所で「GitHub ActionsからTerraform Cloudを使おうとしたが上手くいかなかった」という投稿を拝見しました。
うまいこと設定すればちゃんと動かせますのでその方法などについてまとめておきます。

TL; DR

  • Terraform CloudでのExecution ModeはデフォルトでRemoteになってる
  • クレデンシャルをGitHub側のSecretから環境変数として渡す場合はExecution ModeLocalに変更する

最初に: やりたいこと

このために、リポジトリ上でGitHub Actionsの設定を行い、リポジトリにtfファイルをコミット、PullRequestの作成を行います。

利用するtfファイル

# バックエンドの設定
terraform {
  backend "remote" {
    hostname     = "app.terraform.io"
    organization = "your-organization-name"

    workspaces {
      name = "your-workspace-name"
    }
  }
}

# プロバイダー
provider "google" {
  project = "your-project-name"
  region  = "us-west1"
}

# リソースの例
data "google_project" "project" {}

output "project_number" {
  value = "${data.google_project.project.number}"
}

この例はGCPプロバイダーを使うようにしています。

Terraform CloudとGCPプロバイダーのクレデンシャルについてはGitHub ActionsのSecretに保存しておいて環境変数(TF_ACTION_TFE_TOKENGOOGLE_CREDENTIALS)に指定します。

ワークフローファイルの定義

GitHub Actionsのワークフローファイルは以下を利用します。

参考: https://www.terraform.io/docs/github-actions/getting-started/index.html

on:
  pull_request:
    types: 
      - opened
      - synchronize
      - closed
    branches:
      - master
name: Terraform
jobs:
  terraform-fmt:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1.0.0

    - name: terraform-fmt
      uses: hashicorp/terraform-github-actions/fmt@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        # env
        TF_ACTION_WORKING_DIR: "."

  terraform-init:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1.0.0

    - name: terraform-init
      uses: hashicorp/terraform-github-actions/init@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

  terraform-validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1.0.0

    - name: terraform-init
      uses: hashicorp/terraform-github-actions/init@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

    - name: terraform-validate
      uses: hashicorp/terraform-github-actions/validate@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

  terraform-plan:
    needs:
    - terraform-fmt
    - terraform-init
    - terraform-validate
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1.0.0

    - name: terraform-init
      uses: hashicorp/terraform-github-actions/init@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

    - name: terraform-plan
      uses: hashicorp/terraform-github-actions/plan@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

  terraform-apply:
    needs:
    - terraform-plan
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1.0.0

    - name: terraform-init
      if: github.event.pull_request.merged == true
      uses: hashicorp/terraform-github-actions/init@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

    - name: terraform-apply
      if: github.event.pull_request.merged == true
      uses: hashicorp/terraform-github-actions/apply@v0.4.4
      env:
        # secrets
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
        TF_ACTION_TFE_TOKEN: ${{ secrets.TF_ACTION_TFE_TOKEN }}
        # env
        TF_ACTION_WORKING_DIR: "."

Secretに忘れずにTerraform CloudとGCPプロバイダーのクレデンシャルを登録しておいてください。

これでPR時にinit,fmt,validate,planが実行され、マージしたらapplyも実行されます。

クレデンシャルが見つからないエラーとなる

このままこのアクションを動かすとplan実行時に以下のようなエラーが出ます。

Error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.

  on terraform.tf line 14, in provider "google":
  14: provider "google" ***

原因

tfファイルなどでTerraform Cloudをバックエンドとして指定する際、指定したワークスペースが存在しない場合は新たに作成されます。
そして、新規作成されたワークスペースの場合、Remote Operationsがデフォルトで有効となっています。

Remote Operationsとは、Terraform CLIからplanapplyを実行することで実際の処理をリモートのインフラ上で行える仕組みで、現在はTerraform Cloudでのみサポートされています。

www.terraform.io

Remote Operationsでは環境変数はTerraform Cloudのワークスペース上で定義されたものが利用されます。
なのでGitHub ActionsのSecretを設定、環境変数に指定してもそれが利用されず、クレデンシャルが見つからないというエラーとなります。

対応

ワークスペースの設定でExecution Modeという項目があります。
これはRemote Operationsの有効/無効を切り替えられるもので、この項目をLocalに設定することでplanapplyはローカル(GitHub Actions上)で実行され、tfstateはTerraform Cloudに保存されるようになります。

f:id:febc_yamamoto:20191010204044p:plain

まとめ

以下2点を押さえておきましょう。

  • Terraform CloudでのExecution ModeはデフォルトでRemoteになってる
  • クレデンシャルをGitHub側のSecretから環境変数として渡す場合はExecution ModeLocalに変更する

あと、パブリックなリポジトリGitHub Actionsを使ってapplyを実行するのは推奨されていない*1のでこの辺も理解した上で利用しましょう。

以上です。