🎣

cdktfでTiDB Clusterを作成してみる

2023/12/24に公開

https://qiita.com/advent-calendar/2023/tidb
https://qiita.com/advent-calendar/2023/aws-cdk

cdktf=>TiDB

cdktfを触る機会を探しており、折角なのでAWSでないサービスをと思い、最近一番好きで勉強しているTiDBのClusterを作成してみる事にしました。


やってみます。


cdktfをinstall

$ npm install --global cdktf-cli@latest

added 506 packages in 34s

101 packages are looking for funding
  run `npm fund` for details

typescriptを指定しinit

以前「cdktfというくらいだからHCLで書くんだろうな」と思っていましたが、確認してみるとTypescriptを含めたいくつかの言語で記述可能との事です。
https://tech.revcomm.co.jp/cdk-for-terraform-typescript-gcp

$ mkdir cdktfTiDB && cd cdktfTiDB && cdktf init --template=typescript --providers=hashicorp/aws
Welcome to CDK for Terraform!

By default, cdktf allows you to manage the state of your stacks using Terraform Cloud for free.
cdktf will request an API token for app.terraform.io using your browser.

If login is successful, cdktf will store the token in plain text in
the following file for use by subsequent Terraform commands:
    /Users/hoge/.terraform.d/credentials.tfrc.json

Note: The local storage mode isn't recommended for storing the state of your stacks.

? Do you want to continue with Terraform Cloud remote state management? no
? Project Name cdktfTiDB
? Project Description This is a test to create a TiDB resource with cdktf.
? Do you want to start from an existing Terraform project? no
? Do you want to send crash reports to the CDKTF team? Refer to https://developer.hashicorp.com/ter
raform/cdktf/create-and-deploy/configuration-file#enable-crash-reporting-for-the-cli for more
information no
結果は長いので畳んでいます。
結果
added 2 packages, and audited 57 packages in 2s

7 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

added 314 packages, and audited 371 packages in 27s

38 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
========================================================================================================

  Your CDKTF TypeScript project is ready!

  cat help                Print this message

  Compile:
    npm run get           Import/update Terraform providers and modules (you should check-in this directory)
    npm run compile       Compile typescript code to javascript (or "npm run watch")
    npm run watch         Watch for changes and compile typescript in the background
    npm run build         Compile typescript

  Synthesize:
    cdktf synth [stack]   Synthesize Terraform resources from stacks to cdktf.out/ (ready for 'terraform apply')

  Diff:
    cdktf diff [stack]    Perform a diff (terraform plan) for the given stack

  Deploy:
    cdktf deploy [stack]  Deploy the given stack

  Destroy:
    cdktf destroy [stack] Destroy the stack

  Test:
    npm run test        Runs unit tests (edit __tests__/main-test.ts to add your own tests)
    npm run test:watch  Watches the tests and reruns them on change

  Upgrades:
    npm run upgrade        Upgrade cdktf modules to latest version
    npm run upgrade:next   Upgrade cdktf modules to latest "@next" version (last commit)

 Use Providers:

  You can add prebuilt providers (if available) or locally generated ones using the add command:

  cdktf provider add "aws@~>3.0" null kreuzwerker/docker

  You can find all prebuilt providers on npm: https://www.npmjs.com/search?q=keywords:cdktf
  You can also install these providers directly through npm:

  npm install @cdktf/provider-aws
  npm install @cdktf/provider-google
  npm install @cdktf/provider-azurerm
  npm install @cdktf/provider-docker
  npm install @cdktf/provider-github
  npm install @cdktf/provider-null

  You can also build any module or provider locally. Learn more https://cdk.tf/modules-and-providers

========================================================================================================

[2023-12-24T09:37:26.639] [INFO] default - Checking whether pre-built provider exists for the following constraints:
  provider: aws
  version : latest
  language: typescript
  cdktf   : 0.19.2

[2023-12-24T09:37:29.580] [INFO] default - Found pre-built provider.
Adding package @cdktf/provider-aws @ 18.2.0
[2023-12-24T09:37:29.584] [INFO] default - Installing package @cdktf/provider-aws @ 18.2.0 using npm.
[2023-12-24T09:37:43.157] [INFO] default - Package installed.

initが完了しました。


provider addを行う

$ cdktf provider add "tidbcloud/tidbcloud"
[2023-12-24T10:00:03.442] [INFO] default - Checking whether pre-built provider exists for the following constraints:
  provider: tidbcloud/tidbcloud
  version : latest
  language: typescript
  cdktf   : 0.19.2

[2023-12-24T10:00:04.107] [INFO] default - Pre-built provider does not exist for the given constraints.
[2023-12-24T10:00:04.107] [INFO] default - Adding local provider registry.terraform.io/tidbcloud/tidbcloud with version constraint undefined to cdktf.json
Local providers have been updated. Running cdktf get to update...
Generated typescript constructs in the output directory: .gen


cdktf.jsonを確認してみるとterraformProvidersにtidbcloud/tidbcloud@~> 0.2が追加されていました。

cdktf.json
{
  "language": "typescript",
  "app": "npx ts-node main.ts",
  "projectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
  "sendCrashReports": "false",
  "terraformProviders": [
    "tidbcloud/tidbcloud@~> 0.2"
  ],
  "terraformModules": [],
  "context": {}
}


.gen/providers 以下にも反映されているのが確認出来ました。

上画像では解消されていますが、lazy-index.tsというファイルで赤文字エラーが発生していました。

lazy-index.ts(各exportsとrequireでエラーが発生)
// generated by cdktf get
Object.defineProperty(exports, 'backup', { get: function () { return require('./backup'); } });
Object.defineProperty(exports, 'cluster', { get: function () { return require('./cluster'); } });
Object.defineProperty(exports, 'import', { get: function () { return require('./import'); } });
Object.defineProperty(exports, 'restore', { get: function () { return require('./restore'); } });
Object.defineProperty(exports, 'dataTidbcloudBackups', { get: function () { return require('./data-tidbcloud-backups'); } });
Object.defineProperty(exports, 'dataTidbcloudClusterSpecs', { get: function () { return require('./data-tidbcloud-cluster-specs'); } });
Object.defineProperty(exports, 'dataTidbcloudClusters', { get: function () { return require('./data-tidbcloud-clusters'); } });
Object.defineProperty(exports, 'dataTidbcloudProjects', { get: function () { return require('./data-tidbcloud-projects'); } });
Object.defineProperty(exports, 'dataTidbcloudRestores', { get: function () { return require('./data-tidbcloud-restores'); } });
Object.defineProperty(exports, 'provider', { get: function () { return require('./provider'); } });


マウスオンすると「npm i --save-dev @types/nodeを試してください。」と表示された為、素直に実行します。

$ npm i --save-dev @types/node

added 314 packages, and audited 372 packages in 2s

38 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities


解消されました。


AWS Secrets ManagerにTiDBのAPI Key等を保存

cdktfの機密情報の扱いについては私も完全に理解しておりませんので、これからする事はそんな面倒な事しないで良いなのかもしれません。

私の頭の中の方針では、
①機密情報はAWS Secrets Managerに手動で保存。
②cdktfではTerraformVariableを利用してdeploy時に値を渡す。
③deployコマンドを叩く際に渡す環境変数の値にAWS Secrets Managerから取得した各値利用。
④deploy・destroyは.shファイルの実行で行う。

とします。(後で見た時に「この時何やってんだろう」と思いそうな気がすごくしますが、cdktf初日の私のベストですので生暖かい目でご覧ください。)

API Keyの作成についてはこちらをご覧ください。(projectIdもコンソールを見れば見つかると思います。今回rootPasswordはお好きな文字列で大丈夫です。)

Secrets Managerコンソールにアクセス→「新しいシークレットを作成する」を押下。


それぞれの値を以下のように用意し「次」


任意の名前をつけて「次」


「次」→「次」→「保存」で完了です。


main.tsを書いてみる

main.ts
import { Construct } from 'constructs';
import { App, TerraformStack } from 'cdktf';
import { TerraformVariable } from 'cdktf'
import { prodParameter } from './parameter'
import * as tidb from './.gen/providers/tidbcloud';

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
  super(scope, id);
  //-------------------------------------------------------//
  // 以下4項目についてDeploy実行時に環境変数を渡す形にする。
  const publicKey = new TerraformVariable (this, 'publicKey', {type : 'string', sensitive : true}); 
  const privateKey = new TerraformVariable (this, 'privateKey', {type : 'string', sensitive : true}); 
  const projectId = new TerraformVariable (this, 'projectId', {type : 'string', sensitive : true}); 
  const rootPassword = new TerraformVariable (this, 'rootPassword', {type : 'string', sensitive : true}); 
  //-------------------------------------------------------//
  // API Keyを環境変数を介して渡す。
  new tidb.provider.TidbcloudProvider(this, 'tidbcloudProvider', { 
    publicKey: publicKey.value,
    privateKey: privateKey.value,
  });
  //-------------------------------------------------------//
    // Clusterを作成する。(regionはparameter.tsを作成しそこに収めた値を参照)
  new tidb.cluster.Cluster(this, 'tidbCluster', {
    projectId: projectId.value,
    name : 'MyCluster',
    clusterType : 'DEDICATED',
    cloudProvider : 'AWS',
    region : prodParameter.region,
    config : {
      rootPassword :rootPassword.value,
      port : 4000,
      components : {
        tidb : {
          nodeSize : '8C16G',
          nodeQuantity : 1,
        },
        tikv: {
          nodeSize : '8C32G',
          storageSizeGib : 500,
          nodeQuantity : 3,
        }
      }
    },
  });
  //-------------------------------------------------------//
  }
}
const app = new App();
new MyStack(app, 'cdktfTiDB');
app.synth();


synthしてみる。

$ cdktf synth

Generated Terraform code for the stacks: cdktfTiDB

cdktf.jsonを確認。(長いので畳んでいます。)
cdk.out > stacks/cdktfTiDB > cdktf.json
{
  "//": {
    "metadata": {
      "backend": "local",
      "stackName": "cdktfTiDB",
      "version": "0.19.2"
    },
    "outputs": {
    }
  },
  "provider": {
    "tidbcloud": [
      {
        "private_key": "${var.privateKey}",
        "public_key": "${var.publicKey}"
      }
    ]
  },
  "resource": {
    "tidbcloud_cluster": {
      "tidbCluster": {
        "//": {
          "metadata": {
            "path": "cdktfTiDB/tidbCluster",
            "uniqueId": "tidbCluster"
          }
        },
        "cloud_provider": "AWS",
        "cluster_type": "DEDICATED",
        "config": {
          "components": {
            "tidb": {
              "node_quantity": 1,
              "node_size": "8C16G"
            },
            "tikv": {
              "node_quantity": 3,
              "node_size": "8C32G",
              "storage_size_gib": 500
            }
          },
          "port": 4000,
          "root_password": "${var.rootPassword}"
        },
        "name": "MyCluster",
        "project_id": "${var.projectId}",
        "region": "us-east-1"
      }
    }
  },
  "terraform": {
    "backend": {
      "local": {
        "path": "/Users/hoge/cdktfTiDB/terraform.cdktfTiDB.tfstate"
      }
    },
    "required_providers": {
      "tidbcloud": {
        "source": "tidbcloud/tidbcloud",
        "version": "0.2.2"
      }
    }
  },
  "variable": {
    "privateKey": {
      "sensitive": true,
      "type": "string"
    },
    "projectId": {
      "sensitive": true,
      "type": "string"
    },
    "publicKey": {
      "sensitive": true,
      "type": "string"
    },
    "rootPassword": {
      "sensitive": true,
      "type": "string"
    }
  }
}

今回はdeploy・destroyを.shファイルを作成して行う

cdktfTiDB 直下にshファイルを2つ作成します。
※シェルはもっといい書き方があると思いますが一旦動いたものです。適宜書き換えてご利用下さい。

deploy.sh
#!/bin/bash
region=us-east-1  # 選択リージョンに適宜書き換えてください。
secrets=`aws secretsmanager get-secret-value --region ${region} --secret-id tidbSecret | jq '.SecretString'| tr -d '{}" \\' `
secret=(${secrets//,/ })
i=0
while [[ $i -lt 3 ]]
do
    arr+=(`echo ${secret[i]} | awk '{print substr($0,index($0,":")+1)}'`)
    i=`expr $i + 1`
done
TF_VAR_publicKey=`echo ${arr[0]}` TF_VAR_privateKey=`echo ${arr[1]}` TF_VAR_projectId=`echo ${arr[2]}` TF_VAR_rootPassword=`echo ${arr[3]}` cdktf deploy
destroy.sh
#!/bin/bash
region=us-east-1  # 選択リージョンに適宜書き換えてください。
secrets=`aws secretsmanager get-secret-value --region ${region} --secret-id tidbSecret | jq '.SecretString'| tr -d '{}" \\' `
secret=(${secrets//,/ })
i=0
while [[ $i -lt 3 ]]
do
    arr+=(`echo ${secret[i]} | awk '{print substr($0,index($0,":")+1)}'`)
    i=`expr $i + 1`
done
TF_VAR_publicKey=`echo ${arr[0]}` TF_VAR_privateKey=`echo ${arr[1]}` TF_VAR_projectId=`echo ${arr[2]}` TF_VAR_rootPassword=`echo ${arr[3]}` cdktf destroy


問題なければApproveしてあげてください。

$ bash deploy.sh

#(中略)
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

No outputs found.


作成が開始されました。


確認が出来たら削除します。
(Dedicatedは作成が完了するまでそこそこの時間(数十分)要しますので、完成まで待たずに削除してもOKです。)

$ bash destroy.sh

以上でした

AWSとのコネクション等についてはまた、次回以降に頑張りたいと思います。

有難うございました。

Discussion