

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Terraform を使用して Amazon Redshift SQL クエリを実行する
<a name="execute-redshift-sql-queries-using-terraform"></a>

*Amazon Web Services、Sylvia Qi、Aditya Ambati*

## 概要
<a name="execute-redshift-sql-queries-using-terraform-summary"></a>

Amazon Redshift のデプロイと管理に Infrastructure as Code (IaC) を使用することは、DevOps 内での一般的なプラクティスです。IaC を使用すると、クラスター、スナップショット、パラメータグループなど、さまざまな Amazon Redshift リソースのデプロイと設定が簡単に行えます。ただし、IaC はテーブル、スキーマ、ビュー、ストアドプロシージャなどのデータベースリソースの管理までは対応していません。これらのデータベース要素は SQL クエリによって管理されるもので、IaC ツールでは直接サポートされていません。これらのリソースを管理するためのソリューションとツールがありますが、テクノロジースタックへの追加ツールの導入を回避したい場合があります。

このパターンでは、Terraform を使用してテーブル、スキーマ、ビュー、ストアドプロシージャなどの Amazon Redshift データベースリソースをデプロイする方法の概要を示します。パターンでは、2 種類の SQL クエリを区別します。
+ **再現不可能なクエリ** – これらのクエリは、最初の Amazon Redshift デプロイ中に 1 回実行され、重要なデータベースコンポーネントを確立します。
+ **反復可能なクエリ** – これらのクエリはイミュータブルであり、データベースに影響を与えることなく再実行できます。このソリューションは、Terraform を使用して反復可能なクエリの変更をモニタリングし、それに応じて適用します。

詳細については、「[追加情報](#execute-redshift-sql-queries-using-terraform-additional)」の*「ソリューションの詳細*」を参照してください。

## 前提条件と制限
<a name="execute-redshift-sql-queries-using-terraform-prereqs"></a>

**前提条件**

をアクティブに AWS アカウント し、デプロイマシンに以下をインストールする必要があります。
+ [AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) (AWS CLI)
+ Amazon Redshift の読み取り/書き込みアクセス権限が設定された [AWS CLI プロファイル](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)
+ [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) バージョン 1.6.2 以降
+ [Python3](https://www.python.org/downloads/)
+ [Boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)

**制限事項**
+ Terraform でのクラスター作成中はデータベースを 1 つしか作成できないため、このソリューションでは単一の Amazon Redshift データベースをサポートします。
+ このパターンには、反復可能なクエリへの変更を適用する前に検証するテストは含まれません。信頼性を高めるために、このようなテストを組み込むことをお勧めします。
+ このソリューションを説明するために、このパターンではローカル Terraform ステートファイルを使用するサンプル `redshift.tf` ファイルを用意しています。ただし、本番環境では、安定性とコラボレーションを強化するために、ロック機構を備えたリモートステートファイルを使用することを強くお勧めします。
+ 一部の AWS のサービス は では使用できません AWS リージョン。利用可能なリージョンについては、「[AWS のサービス (リージョン別)](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/)」を参照してください。特定のエンドポイントについては、「[Service endpoints and quotas](https://docs.aws.amazon.com/general/latest/gr/aws-service-information.html)」で、サービスのリンクを選択してご確認ください。

**製品バージョン**

このソリューションは、[Amazon Redshift パッチ 179](https://docs.aws.amazon.com/redshift/latest/mgmt/cluster-versions.html#cluster-version-179) で開発およびテストされています。

**コードリポジトリ**

このパターンのコードは、GitHub の「[amazon-redshift-sql-deploy-terraform](https://github.com/aws-samples/amazon-redshift-sql-deploy-terraform)」リポジトリで入手できます。

## アーキテクチャ
<a name="execute-redshift-sql-queries-using-terraform-architecture"></a>

次の図に、Terraform が反復不可能な SQL クエリと反復不可能な SQL クエリの両方を処理することで Amazon Redshift データベースリソースを管理する方法を示します。

![\[Terraform が SQL クエリを使用して Amazon Redshift データベースリソースを管理するプロセス。\]](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/images/pattern-img/0f4467ac-761b-4b6b-a32f-e18a2ca2245d/images/3b6ff9e8-e3d1-48ed-9fa1-4b14f7d3d65b.png)


図表に示す内容は以下のステップです。

1. Amazon Redshift クラスターの初回デプロイ時に、Terraform が反復不可能な SQL クエリを適用します。

1. 開発者が、反復可能な SQL クエリの変更をコミットします。

1. Terraform が、反復可能な SQL クエリの変更をモニタリングします。

1. Terraform が、反復可能な SQL クエリを Amazon Redshift データベースに適用します。

このパターンで提供するソリューションは、[Amazon Redshift 用の Terraform モジュール](https://registry.terraform.io/modules/terraform-aws-modules/redshift/aws/latest)に基づいて構築されています。Terraform モジュールは Amazon Redshift クラスターとデータベースをプロビジョニングします。モジュールを強化するために、ここでは `terraform_data` リソースを使用しています。ここで、カスタム Python スクリプトを呼び出し、Amazon Redshift [ExecuteStatement](https://docs.aws.amazon.com/redshift-data/latest/APIReference/API_ExecuteStatement.html) API オペレーションを使用して SQL クエリを実行します。結果として、モジュールは以下を実行できます。
+ データベースのプロビジョニング後に SQL クエリを使用して、任意の数のデータベースリソースをデプロイする。
+ 反復可能な SQL クエリの変更を継続的にモニタリングし、Terraform を使用してそれらの変更を適用する。

詳細については、「[追加情報](#execute-redshift-sql-queries-using-terraform-additional)」の*「ソリューションの詳細*」を参照してください。

## ツール
<a name="execute-redshift-sql-queries-using-terraform-tools"></a>

**AWS のサービス**
+ [Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/mgmt/welcome.html) は、フルマネージド型で、ペタバイトスケールのデータウェアハウスを提供する、 AWS クラウドのサービスです。

**その他のツール**
+ [Terraform](https://www.terraform.io/) は HashiCorp の infrastructure as code (IaC) ツールで、クラウドとオンプレミスのリソースの作成と管理を支援します。
+ [Python](https://www.python.org/) は、SQL クエリを実行するためにこのパターンで使用する汎用プログラミング言語です。

## ベストプラクティス
<a name="execute-redshift-sql-queries-using-terraform-best-practices"></a>
+ [Amazon Redshift のベストプラクティス](https://docs.aws.amazon.com/redshift/latest/dg/best-practices.html)
+ [Using the Amazon Redshift Data API to interact with Amazon Redshift clusters](https://aws.amazon.com/blogs/big-data/using-the-amazon-redshift-data-api-to-interact-with-amazon-redshift-clusters/)

## エピック
<a name="execute-redshift-sql-queries-using-terraform-epics"></a>

### Terraform を使用してソリューションをデプロイする
<a name="deploy-the-solution-using-terraform"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| **リポジトリのクローンを作成します。** | Amazon Redshift クラスターをプロビジョニングするための Terraform コードを含む Git リポジトリのクローンを作成するには、次のコマンドを実行します。<pre>git clone https://github.com/aws-samples/amazon-redshift-sql-deploy-terraform.git</pre> | DevOps エンジニア | 
| **Terraform 変数を更新します。** | 特定の要件に従って Amazon Redshift クラスターのデプロイをカスタマイズするには、`terraform.tfvars` ファイル内の次のパラメータを更新します。<pre>region                    = "<AWS_REGION>"<br />cluster_identifier        = "<REDSHIFT_CLUSTER_IDENTIFIER>"<br />node_type                 = "<REDSHIFT_NODE_TYPE>"<br />number_of_nodes           = "<REDSHIFT_NODE_COUNT>"<br />database_name             = "<REDSHIFT_DB_NAME>"<br />subnet_ids                = "<REDSHIFT_SUBNET_IDS>"<br />vpc_security_group_ids    = "<REDSHIFT_SECURITY_GROUP_IDS>"<br />run_nonrepeatable_queries = true<br />run_repeatable_queries    = true<br />sql_path_bootstrap        = "<BOOTSTRAP_SQLS_PATH>"<br />sql_path_nonrepeatable    = "<NON-REPEATABLE_SQLS_PATH>"<br />sql_path_repeatable       = "<REPEATABLE_SQLS_PATH>"<br />sql_path_finalize         = "<FINALIZE_SQLS_PATH>"<br />create_random_password    = false<br />master_username           = "<REDSHIFT_MASTER_USERNAME>"</pre> | DevOps エンジニア | 
| Terraform を使用してリソースをデプロイします。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/execute-redshift-sql-queries-using-terraform.html) | DevOps エンジニア | 
| (オプション) 追加の SQL クエリを実行します。 | サンプルリポジトリには、デモ用の SQL クエリがいくつか用意されています。独自の SQL クエリを実行するには、それらを次のフォルダに追加します。`/bootstrap` `/nonrepeatable` `/repeatable` `/finalize` |  | 

### SQL ステートメントの実行をモニタリングする
<a name="monitor-the-execution-of-sql-statements"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| SQL ステートメントのデプロイをモニタリングします。 | Amazon Redshift クラスターに対する SQL 実行の結果をモニタリングできます。失敗した SQL 実行と成功した SQL 実行を示す出力の例については、「[追加情報](#execute-redshift-sql-queries-using-terraform-additional)」の「*SQL ステートメントの例*」を参照してください。 | DBA、DevOps エンジニア | 
| リソースをクリーンアップします。 | Terraform によってデプロイされたリソースをすべて削除するには、次のコマンドを実行します。<pre>terraform destroy</pre> | DevOps エンジニア | 

### 結果を検証する
<a name="validate-the-results"></a>


| タスク | 説明 | 必要なスキル | 
| --- | --- | --- | 
| Amazon Redshift クラスターのデータを検証します。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/execute-redshift-sql-queries-using-terraform.html) | DBA、AWS DevOps | 

## 関連リソース
<a name="execute-redshift-sql-queries-using-terraform-resources"></a>

**AWS ドキュメント**
+ [Amazon Redshift でプロビジョニングされたクラスター](https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-clusters.html)
+ [Amazon Redshift Data API の問題のトラブルシューティング](https://docs.aws.amazon.com/redshift/latest/mgmt/data-api-troubleshooting.html)

**その他のリソース**
+ [コマンド: apply](https://developer.hashicorp.com/terraform/cli/commands/apply) (Terraform ドキュメント)

## 追加情報
<a name="execute-redshift-sql-queries-using-terraform-additional"></a>

**ソリューションの詳細**

このソリューションを使用するには、Amazon Redshift SQL クエリを特定の方法で配置する必要があります。すべての SQL クエリは、`.sql` 拡張子が付いたファイルに保存する必要があります。

このパターンで提供するコード例では、SQL クエリは次のフォルダ構造内に配置されます。独自のユースケースに適した任意の構造に対応するように、コード (`sql-queries.tf` と `sql-queries.py`) を変更することができます。

```
/bootstrap
     |- Any # of files
     |- Any # of sub-folders
/nonrepeatable
     |- Any # of files
     |- Any # of sub-folders
/repeatable
     /udf
          |- Any # of files
          |- Any # of sub-folders
     /table
          |- Any # of files
          |- Any # of sub-folders
     /view
          |- Any # of files
          |- Any # of sub-folders
     /stored-procedure
          |- Any # of files
          |- Any # of sub-folders
/finalize
     |- Any # of files
     |- Any # of sub-folders
```

このようなフォルダ構造のため、Amazon Redshift クラスターのデプロイ中、Terraform でのクエリ実行は次の順序で行われます。

1. `/bootstrap`

1. `/nonrepeatable`

1. `/repeatable`

1. `/finalize`

`/repeatable` フォルダには、`/udf`、`/table`、`/view`、`/stored-procedure` の 4 つのサブフォルダがあります。これらのサブフォルダは、Terraform が SQL クエリを実行する順序を示しています。

SQL クエリを実行する Python スクリプトは `sql-queries.py` です。スクリプトは、まず `sql_path_bootstrap` パラメータなど、特定のソースディレクトリのすべてのファイルとサブフォルダを読み取ります。その次に、Amazon Redshift [ExecuteStatement](https://docs.aws.amazon.com/redshift-data/latest/APIReference/API_ExecuteStatement.html) API オペレーションを呼び出してクエリを実行します。ファイル内に 1 つ以上の SQL クエリがある場合があります。次のコードスニペットは、ファイルに保存されている SQL ステートメントを Amazon Redshift クラスターに対して実行する Python 関数を示しています。

```
def execute_sql_statement(filename, cluster_id, db_name, secret_arn, aws_region):
    """Execute SQL statements in a file"""
    redshift_client = boto3.client(
        'redshift-data', region_name=aws_region)
    contents = get_contents_from_file(filename),
    response = redshift_client.execute_statement(
        Sql=contents[0],
        ClusterIdentifier=cluster_id,
        Database=db_name,
        WithEvent=True,
        StatementName=filename,
        SecretArn=secret_arn
    )
    ...
```

Terraform スクリプト `sql-queries.tf` は、`sql-queries.py` スクリプトを呼び出す [terraform\$1data](https://developer.hashicorp.com/terraform/language/resources/terraform-data) リソースを作成します。`/bootstrap`、`/nonrepeatable`,`/repeatable`,`/finalize` の 4 つのフォルダのそれぞれに 1 つの `terraform_data` リソースがあります。次のコードスニペットは、`/bootstrap` フォルダ内の SQL クエリを実行する `terraform_data` リソースを示しています。

```
locals {
  program               = "${path.module}/sql-queries.py"
  redshift_cluster_name = try(aws_redshift_cluster.this[0].id, null)
}

resource "terraform_data" "run_bootstrap_queries" {
  count      = var.create && var.run_nonrepeatable_queries && (var.sql_path_bootstrap != "") && (var.snapshot_identifier == null) ? 1 : 0
  depends_on = [aws_redshift_cluster.this[0]]

  provisioner "local-exec" {
    command = "python3 ${local.program} ${var.sql_path_bootstrap} ${local.redshift_cluster_name} ${var.database_name} ${var.redshift_secret_arn} ${local.aws_region}"
  }
}
```

これらのクエリを実行するかどうかを制御するには、次の変数を使用できます。`sql_path_bootstrap`、`sql_path_nonrepeatable`、`sql_path_repeatable`、`sql_path_finalize` でクエリを実行しない場合は、それらの値を `""` に設定します。

```
  run_nonrepeatable_queries = true
  run_repeatable_queries    = true
  sql_path_bootstrap        = "src/redshift/bootstrap"
  sql_path_nonrepeatable    = "src/redshift/nonrepeatable"
  sql_path_repeatable       = "src/redshift/repeatable"
  sql_path_finalize         = "src/redshift/finalize"
```

`terraform apply` を実行すると、Terraform はスクリプトの結果に関係なく、スクリプトが完了した後に追加された `terraform_data` リソースを考慮します。一部の SQL クエリが失敗し、再実行する場合は、Terraform 状態からリソースを手動で削除して、`terraform apply` を再度実行できます。例えば、次のコマンドを実行すると、Terraform 状態から `run_bootstrap_queries` リソースが削除されます。

`terraform state rm module.redshift.terraform_data.run_bootstrap_queries[0]`

次のコード例は、`run_repeatable_queries` リソースが [sha256 ハッシュ](https://developer.hashicorp.com/terraform/language/functions/sha256)を使用して `repeatable` フォルダでの変更をモニタリングする方法を示しています。フォルダ内のファイルが更新されると、Terraform はディレクトリ全体に更新のマークを付けます。そして、次の `terraform apply` 中にディレクトリ内でクエリを再度実行します。

```
resource "terraform_data" "run_repeatable_queries" {
  count      = var.create_redshift && var.run_repeatable_queries && (var.sql_path_repeatable != "") ? 1 : 0
  depends_on = [terraform_data.run_nonrepeatable_queries]

  # Continuously monitor and apply changes in the repeatable folder
  triggers_replace = {
    dir_sha256 = sha256(join("", [for f in fileset("${var.sql_path_repeatable}", "**") : filesha256("${var.sql_path_repeatable}/${f}")]))
  }

  provisioner "local-exec" {
    command = "python3 ${local.sql_queries} ${var.sql_path_repeatable} ${local.redshift_cluster_name} ${var.database_name} ${var.redshift_secret_arn}"
  }
}
```

コードを効率化するために、すべてのファイルに無差別に変更を適用するのではなく、`repeatable` フォルダ内で更新されたファイルのみの変更を検出して適用するメカニズムを実装できます。

**SQL ステートメントの例**

次の出力は、失敗した SQL 実行とエラーメッセージを示しています。

```
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): Executing: ["/bin/sh" "-c" "python3 modules/redshift/sql-queries.py src/redshift/nonrepeatable testcluster-1 db1 arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:/redshift/master_user/password-8RapGH us-east-1"]
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): -------------------------------------------------------------------
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): src/redshift/nonrepeatable/table/admin/admin.application_family.sql
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): -------------------------------------------------------------------
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): Status: FAILED
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): SQL execution failed.
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec): Error message: ERROR: syntax error at or near ")"
module.redshift.terraform_data.run_nonrepeatable_queries[0] (local-exec):   Position: 244
module.redshift.terraform_data.run_nonrepeatable_queries[0]: Creation complete after 3s [id=ee50ba6c-11ae-5b64-7e2f-86fd8caa8b76]
```

次の出力は、成功した SQL 実行を示しています。

```
module.redshift.terraform_data.run_bootstrap_queries[0]: Provisioning with 'local-exec'...
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): Executing: ["/bin/sh" "-c" "python3 modules/redshift/sql-queries.py src/redshift/bootstrap testcluster-1 db1 arn:aws:secretsmanager:us-east-1:XXXXXXXXXXXX:secret:/redshift/master_user/password-8RapGH us-east-1"]
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): -------------------------------------------------------------------
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): src/redshift/bootstrap/db.sql
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): -------------------------------------------------------------------
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): Status: FINISHED
module.redshift.terraform_data.run_bootstrap_queries[0] (local-exec): SQL execution successful.
module.redshift.terraform_data.run_bootstrap_queries[0]: Creation complete after 2s [id=d565ef6d-be86-8afd-8e90-111e5ea4a1be]
```