

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 Terraform 執行 Amazon Redshift SQL 查詢
<a name="execute-redshift-sql-queries-using-terraform"></a>

*Sylvia Qi 和 Aditya Ambati，Amazon Web Services*

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

使用基礎設施做為程式碼 (IaC) 來部署和管理 Amazon Redshift 是 DevOps 中普遍的做法。IaC 可促進各種 Amazon Redshift 資源的部署和組態，例如叢集、快照和參數群組。不過，IaC 不會延伸至管理資料庫資源，例如資料表、結構描述、檢視和預存程序。這些資料庫元素是透過 SQL 查詢管理，IaC 工具不會直接支援。雖然存在管理這些資源的解決方案和工具，但您可能不想在技術堆疊中引入其他工具。

此模式概述使用 Terraform 部署 Amazon Redshift 資料庫資源的方法，包括資料表、結構描述、檢視和預存程序。模式區分兩種類型的 SQL 查詢：
+ **不可重複的查詢** – 這些查詢會在初始 Amazon Redshift 部署期間執行一次，以建立必要的資料庫元件。
+ **可重複的查詢** – 這些查詢是不可變的，可以在不影響資料庫的情況下重新執行。解決方案使用 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)

**限制**
+ 此解決方案支援單一 Amazon Redshift 資料庫，因為 Terraform 僅允許在叢集建立期間建立一個資料庫。
+ 此模式不包含在套用可重複查詢之前驗證變更的測試。我們建議您整合此類測試，以獲得增強的可靠性。
+ 為了說明解決方案，此模式提供使用本機 Terraform 狀態`redshift.tf`檔案的範例檔案。不過，對於生產環境，我們強烈建議您使用具有鎖定機制的遠端狀態檔案，以增強穩定性和協同合作。
+ 有些 AWS 服務 不適用於所有 AWS 區域。如需區域可用性，請參閱[AWS 服務 依區域](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/)。如需特定端點，請參閱[服務端點和配額](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) 儲存庫中使用。

## Architecture
<a name="execute-redshift-sql-queries-using-terraform-architecture"></a>

下圖說明 Terraform 如何透過處理不可重複和可重複的 SQL 查詢來管理 Amazon Redshift 資料庫資源。

![\[使用 SQL 查詢管理 Amazon Redshift 資料庫資源的 Terraform 程序。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/patterns/images/pattern-img/0f4467ac-761b-4b6b-a32f-e18a2ca2245d/images/3b6ff9e8-e3d1-48ed-9fa1-4b14f7d3d65b.png)


圖表顯示下列步驟：

1. Terraform 會在初始 Amazon Redshift 叢集部署期間套用不可重複的 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) 是 中全受管的 PB 級資料倉儲服務 AWS 雲端。

**其他工具**
+ [Terraform](https://www.terraform.io/) 是 HashiCorp 的基礎設施即程式碼 (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)
+ [使用 Amazon Redshift Data API 與 Amazon Redshift 叢集互動](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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| **複製儲存庫。** | 若要複製包含 Terraform 程式碼的 Git 儲存庫來佈建 Amazon Redshift 叢集，請使用下列命令。<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/zh_tw/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>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 監控 SQL 陳述式的部署。 | 您可以監控 Amazon Redshift 叢集的 SQL 執行結果。如需顯示失敗和成功 SQL 執行的輸出範例，請參閱[其他資訊](#execute-redshift-sql-queries-using-terraform-additional)中*的範例 SQL 陳述*式。 | DBA，DevOps 工程師 | 
| 清除資源。 | 若要刪除 Terraform 部署的所有資源，請執行下列命令。<pre>terraform destroy</pre> | DevOps 工程師 | 

### 驗證結果
<a name="validate-the-results"></a>


| 任務 | Description | 所需的技能 | 
| --- | --- | --- | 
| 驗證 Amazon Redshift 叢集中的資料。 | [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/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)

**其他資源**
+ [命令：套用](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`、`/view`、 `/table`和 `/stored-procedure`。這些子資料夾指出 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 操作來執行查詢。您可能在檔案中有一或多個 SQL 查詢。下列程式碼片段顯示 Python 函數，該函數會對 Amazon Redshift 叢集執行存放在檔案中的 SQL 陳述式。

```
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) 資源。四個資料夾各有一個`terraform_data`資源：`/bootstrap`、`/repeatable`、 `/nonrepeatable`和 `/finalize`。下列程式碼片段顯示執行 `/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_repeatable`、 `sql_path_nonrepeatable`或 中執行查詢`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 會在下一個 期間再次執行目錄中的查詢`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]
```