Terraform にだいぶ馴染んでまいりました。
前回の記事(【2019 年 4 月版】Terraform+Spinnakerがよくね!? → 闇が深い。)で Spinnaker のインストールを Terraform でやってみましたが、Spinnaker 以上に Terraform でアレコレやりましたのでむしろ Terraform + GCP の闇を垣間見てしまいました。
前回の説明の流れではご紹介していない小技というか注意点をアラカルトでメモっておきます。
でもやっぱり、敵はロール。
GKE から Cloud SQL Proxy を通して SQL にアクセスする
基本的には以下の公式ページの手順でOKです。
ただし、以下のコマンドを Terraform でどうやるか、が問題です。
$ kubectl create secret generic cloudsql-oauth-credentials --from-file=credentials.json=[PATH_TO_CREDENTIAL_FILE]
手作業であればサービスアカウント作って json ファイルをダウンロードして上記のコマンドでOKですが、Terraform でそれをどうやるのか?です。
いろいろ頑張った結果、以下のようになりました。
# サービスアカウント作成
resource "google_service_account" "k8s-sql-proxy-connect" {
depends_on = ["google_container_node_pool.cluster"]
account_id = "k8s-sql-proxy-connect"
display_name = "SQL Proxy Connection from k8s by terraform"
}
# 役割に"編集者"を設定
resource "google_project_iam_member" "k8s-sql-proxy-connect-iam-projectEditor" {
depends_on = ["google_service_account.k8s-sql-proxy-connect"]
role = "roles/editor"
member = "serviceAccount:${google_service_account.k8s-sql-proxy-connect.email}"
}
# サービスアカウントキーを取得
resource "google_service_account_key" "k8s-sql-proxy-connect-cred" {
service_account_id = "${google_service_account.k8s-sql-proxy-connect.email}"
}
# kubernetes_secret で base64decodeしたprivate_keyを渡す!
resource "kubernetes_secret" "cloudsql-instance-credentials" {
depends_on = ["google_project_iam_member.k8s-sql-proxy-connect-iam-projectEditor"]
metadata {
name = "cloudsql-instance-credentials"
}
data {
"credentials.json" = "${base64decode(google_service_account_key.k8s-sql-proxy-connect-cred.private_key)}"
}
}
ヘルプにはちゃんと明記されてましたね。。。
The private key in JSON format, base64 encoded.
ちゃんと読め、ということで。。。
GKE の Pod で Google Container Repository から pull
GKE 使っているので GCR から pull したい、ということはよくあると思います。
普通に使っている分にはあまり意識しなかったのですが Terraform だと一つの設定ミスでハマるので気を付けないとね、ということです。
node_pool
の oauth_scopes
に storage-ro
を追加すればOKです。
oauth_scopes = [
"storage-ro",
"logging-write",
"monitoring",
]
・・・はい。
Cloud SQL のインスタンス名にランダム文字列を追加する
Terrform のスクラップ&ビルドしていて一番、悩ましかったのが以下の仕様。
削除したインスタンスのインスタンス名は、削除してから最大 1 週間は再利用できません。
お~。。。Terraform でCloud SQLインスタンスの設定変えて apply
してエラー出たのでびっくりして調べたら公式の挙動ですよ。。。
apply
& destroy
をテスト環境で行うのに一々、名前の設定変えてられないのでランダムの文字列を追加することにいたしました。
以下のようになります。
resource "random_id" "db_suffix" {
byte_length = 5
}
resource "google_sql_database_instance" "cloudsql-api" {
name = "mysql-${lower(random_id.db_suffix.b64)}"
database_version = "${var.mysql_version}"
region = "${var.gke_region}"
settings {
...
lifecycle {
ignore_changes = ["disk_size", "name"]
}
}
で、mysql-jr5ghe
みたいな適当な文字列が入ります。また、インスタンス名には小文字しか使えなかったので lower
関数入れてます。
また、何かのタイミングで name
が変わるたびにインスタンス再作成されるのは困るので ignore_changes
に disk_size
と name
を入れてます。
Cloud SQL のインスタンス、属性だけ変えて apply
たらエラー
属性によっては強制的にインスタンスを作り替えるものがあります。というかだいたいクリティカルな属性なんで作り直しになります。
そんな時・・・名前が変わらないんですね!
ランダム文字列は random_id.db_suffix
リソースが提供しますので、このリソースが再生成されない限りはランダム文字列も変わらないのです。。。
そんな時は taint
コマンドで、このリソースを指定します。
$ terraform taint random_id.db_suffix
taint
コマンドで指定したリソースは、次回のapply
時に強制的に再作成されます。
で、random_id.db_suffix
も次回の apply
時に強制的に再作成されてランダム文字列が再度、生成されます。
設定ファイルのフォーマット
terraform は *.tf
ファイルの自動整形してくれる fmt
コマンドがあります。
$ terraform fmt
で、インデントなど揃えてくれます。
VSCode の Terraform プラグインではファイル保存時に fmt
してくれる設定があるらしいですが・・・
kubernetes のネームスペース、その他リソースが消せなくなった。
GKE のクラスターが削除された後は kubernetes provider で操作する一切のリソースの状態は取得エラーとなってしまいます。
当たり前ですがこのあたりの連携がいまいちなんですよね。
とくにnamespaceはどうも消せるタイミングが絶妙なのでほぼ間違いなく削除時にエラーになります。
そんな時は状態(state)から削除してしまう以外にやりようがありません。
以下のコマンドで状態データから指定したリソースの状態を強制的に削除します。
$ terraform state rm kubernetes_namespace.default
次回のapply
時には新規作成が行われます。
GKEのクラスタが消えたらkubernetes配下のstateは全削除してほしいけどなぁ。。。
Google PubSub の Topic と Scheduler Job について
公式サイトの作り方、そのまんまなのですが・・・
初見で${google_pubsub_topic.topic.id}
とあるので以下のページで確認したところ・・・
id
というフィールドは載ってないわけです。
おいおい、公式ヘルプがタイポですかい・・・と思って ${google_pubsub_topic.topic.name}
を使ってみたところ・・・
google_cloud_scheduler_job.job: Error creation Job googleapi: Error 400 Job name must be formatted: "projects/<PROJECT_ID>/locations/<LOCATION_ID>/jobs/<JOB_ID>".
とかエラーが出たわけです。
いや、このエラーメッセージは明らかにjobのnameがおかしいっていうメッセージだし、公式のREST APIのページにもJobのnameのフォーマットについて同じことが書いてあるわけです。
いや~いろいろ試してみましたが全然このエラーが解消できず・・・ふと、見つけたissueで・・・
あれ?よく見ると projects/$PROJECT_ID/topics/cron-topic
の話になっててクローズ=解決してる・・・?
というわけでまず初めに pubsub_target
のtopic_name
にprojects/${var.project_id}/topics/${google_pubsub_topic.topic.name}
を指定してみたところ・・・
ちゃんと動くのです。
あらま、こっちだったの?!でもわざわざprojects/${var.project_id}/topics/${google_pubsub_topic.topic.name}
って書くのおかしくないか?
というところで、最初の id
を思い出したのです。id
ってそういうあれか?
試しにpubsub_target
のtopic_name
に${google_pubsub_topic.topic.id}
を指定したところ・・・
普通に動くー
最初から公式ドキュメント通りでよかったのね~、ということでした。
今のところ以上です。
Terraform + GKE はやっぱり闇が深いな。。。