TerraformでAmazon Auroraクラスタを自動構築する(PostgreSQL互換編)

# はじめに
[以前の記事](https://qiita.com/neruneruo/items/a7c5f7fa80fbf9d6a828)ではAuroraのMySQL互換の基本を書いてみた。
今回は、PostgreSQL互換のAuroraクラスタ・インスタンスを作ってみよう。

前提知識についても前回同様、以下があれば良い。

- [Amazon AuroraのBlack Beltの資料](https://d1.awsstatic.com/webinars/jp/pdf/services/20200929_BlackBelt_HowToScale_Aurora_MySQL_pub.pdf)はざっと目を通して概要を理解している
- TerraformでのAWSサービスの構築経験がある

構成についても前回同様、Publicなサブネット上にライター・リーダーインスタンスを構築する。

![public_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/46a2548a-c1de-220c-a019-1ed2db199822.png)

実際に構築する上での注意点も、前回同様に、AuroraはPrivateサブネットに配置して踏み台経由でアクセスする。

![private_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/7040f7de-5d95-8340-bdda-3ae6e662e2d8.png)

なお、構築はPostgreSQL12.7互換で行っている。

# Terraform
[以前のAurora MySQL互換の記事](https://qiita.com/neruneruo/items/a7c5f7fa80fbf9d6a828)との差分になる部分を中心に記載していく。

## ネットワーク設定
この部分は以前の記事から差分はない。

```HCL
data "aws_subnet_ids" "my_vpc" {
  vpc_id = data.aws_vpc.my.id
}

resource "aws_db_subnet_group" "example" {
  name       = local.db_subnet_group_name
  subnet_ids = data.aws_subnet_ids.my_vpc.ids
}
```

## DBクラスタとパラメータ
DBクラスタの設定は以下の通り行う。
MySQL互換のときは```engine_version```の記載が面倒だったが、PostgreSQL互換の場合はシンプルだ。
ポートはPostgreSQL標準のポートである```5432```にしておこう。

```HCL
resource "aws_rds_cluster" "example" {
  cluster_identifier                  = local.aurora_cluster_identifier
  engine                              = "aurora-postgresql"
  engine_version                      = "12.7"
  master_username                     = local.aurora_master_username
  master_password                     = local.aurora_master_password
  port                                = 5432
  database_name                       = local.aurora_database_name
  vpc_security_group_ids              = [aws_security_group.aurora.id]
  db_subnet_group_name                = aws_db_subnet_group.example.name
  db_cluster_parameter_group_name     = aws_rds_cluster_parameter_group.example.name
  iam_database_authentication_enabled = true

  skip_final_snapshot = true
  apply_immediately   = true
}

resource "aws_rds_cluster_parameter_group" "example" {
  name   = local.rdscluster_parameter_group_name
  family = "aurora-postgresql12"
}
```

# Auroraインスタンスとパラメータ
ここも、インスタンスの設定はMySQL互換とは大きく変わらない。
ただし、PostgreSQL互換の方がリソース消費が多いようで、```instance_class```の設定可能なタイプが制限されている。詳細は[公式のユーザーガイド](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Concepts.DBInstanceClass.html#Concepts.DBInstanceClass.SupportAurora)に書かれているため、必要に応じて参照しよう。

インスタンスパラメータについては、```shared_preload_libraries```に```pg_stat_statements,pg_hint_plan```を入れておこう。
無くても動作するが、安定した動作の保証や、パフォーマンスに問題があった際の調査に必要になる。
厳密に言うと、DBクラスタのパラメータグループの```rds.extensions```にも同様の設定を入れておく必要があるが、デフォルトで設定されているため、自分で変更した場合以外は気にする必要はない。

```HCL
resource "aws_rds_cluster_instance" "example" {
  count = local.aurora_instance_count

  cluster_identifier = aws_rds_cluster.example.id
  identifier         = "${local.aurora_cluster_identifier}-instance-${count.index}"

  engine                  = aws_rds_cluster.example.engine
  engine_version          = aws_rds_cluster.example.engine_version
  instance_class          = "db.t4g.medium"
  db_subnet_group_name    = aws_db_subnet_group.example.name
  db_parameter_group_name = aws_db_parameter_group.example.name

  monitoring_role_arn = aws_iam_role.aurora_monitoring.arn
  monitoring_interval = 60

  publicly_accessible = true
}

resource "aws_db_parameter_group" "example" {
  name   = local.db_parameter_group_name
  family = "aurora-postgresql12"

  parameter {
    apply_method = "pending-reboot"
    name  = "shared_preload_libraries"
    value = "pg_stat_statements,pg_hint_plan"
  }
}
```

## 構築後のテーブル自動生成
MySQL互換同様に、テーブル自動生成は```null_resource```を使う。
```export PGPASSWORD```しておかないと、パスワードのプロンプトが表示されてしまい進まなくなるため、ここだけ設定する。

```CREATE EXTENSION```では、```shared_preload_libraries```で有効化した統計情報を参照するためのテーブルを作る。
このテーブルは、作成はライターインスタンスでしか行えないが、リーダーインスタンスでSQLを発行するとちゃんと更新されるようになっている。

```HCL
resource "null_resource" "db_setup" {
  depends_on = [
    aws_rds_cluster.example,
    aws_rds_cluster_instance.example,
  ]

  provisioner "local-exec" {
    command = "export PGPASSWORD=${local.aurora_master_password}; psql -h ${aws_rds_cluster.example.endpoint} -U administrator -d COMPANY -f ./aurora_init_db.sql"
  }
}
```

```SQL:aurora_init_db.sql
CREATE EXTENSION pg_stat_statements;

CREATE TABLE IF NOT EXISTS employee (
  id char(5) PRIMARY KEY,
  name CHAR(20) NOT NULL,
  age integer,
  update_dt timestamp
);

INSERT INTO employee VALUES ( '00001', 'Taro', 35, CURRENT_TIMESTAMP );
INSERT INTO employee VALUES ( '00002', 'Jiro', 30, CURRENT_TIMESTAMP );
INSERT INTO employee VALUES ( '00003', 'Saburo', 28, CURRENT_TIMESTAMP );
INSERT INTO employee VALUES ( '00004', 'Shiro', 24, CURRENT_TIMESTAMP );
INSERT INTO employee VALUES ( '00005', 'Goro', 40,CURRENT_TIMESTAMP );
```

# 拡張モニタリング
拡張モニタリングについては、基本的に作成方法に変更はないため、今回は記載を割愛する。

# アプリケーションからの接続に関するプラクティス
MySQL互換では```innodb_read_only```で確認可能だったステータスは、以下のように取得する。

```ライターインスタンスで実行した場合
COMPANY=> SHOW transaction_read_only;
 transaction_read_only 
-----------------------
 off
(1 row)
```

```リーダーインスタンスで実行した場合
COMPANY=> SHOW transaction_read_only;
 transaction_read_only 
-----------------------
 on
(1 row)
```

ライターインスタンスをフェイルオーバーした後は、応答がしっかり変わった。
ただし、psqlでは切替後の最初のSQLでは以下のエラーが発生した。
実際の業務アプリケーションでは、コネクションが切れた後にリーダーエンドポイントに再接続しにいけば、特にチェック等はしていなくても勝手に切り替わってくれるかもしれない。

```
COMPANY=> SHOW transaction_read_only;
SSL SYSCALL error: EOF detected
The connection to the server was lost. Attempting reset: Succeeded.
psql (12.9 (Ubuntu 12.9-0ubuntu0.20.04.1), server 12.7)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
```

これで、PostgreSQL互換のAuroraクラスタもサクッと作れるようになった！