LoginSignup
15
7

More than 3 years have passed since last update.

【2019年4月版】Terraform + GKE でのいろいろ小技メモ

Last updated at Posted at 2019-04-23

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 でそれをどうやるのか?です。

いろいろ頑張った結果、以下のようになりました。

main.tf
# サービスアカウント作成
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_pooloauth_scopesstorage-ro を追加すればOKです。

main.tf
    oauth_scopes = [
      "storage-ro",
      "logging-write",
      "monitoring",
    ]

・・・はい。

Cloud SQL のインスタンス名にランダム文字列を追加する

Terrform のスクラップ&ビルドしていて一番、悩ましかったのが以下の仕様。

削除したインスタンスのインスタンス名は、削除してから最大 1 週間は再利用できません。

お~。。。Terraform でCloud SQLインスタンスの設定変えて apply してエラー出たのでびっくりして調べたら公式の挙動ですよ。。。

apply & destroy をテスト環境で行うのに一々、名前の設定変えてられないのでランダムの文字列を追加することにいたしました。

以下のようになります。

main.tf
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_changesdisk_sizename を入れてます。

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_targettopic_nameprojects/${var.project_id}/topics/${google_pubsub_topic.topic.name}を指定してみたところ・・・

ちゃんと動くのです。

あらま、こっちだったの?!でもわざわざprojects/${var.project_id}/topics/${google_pubsub_topic.topic.name}って書くのおかしくないか?

というところで、最初の id を思い出したのです。idってそういうあれか?
試しにpubsub_targettopic_name${google_pubsub_topic.topic.id}を指定したところ・・・

普通に動くー

最初から公式ドキュメント通りでよかったのね~、ということでした。

今のところ以上です。
Terraform + GKE はやっぱり闇が深いな。。。

15
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
7