TerraformでAlibaba Cloud上でSLB + WordPress ECS Servers + Multi-AZ Redis Cache + PolarDBの環境を構築してみた

# ゴール
以下のgithub repoをベースとして、TerraformでAlibaba Cloud上でSLB + WordPress ECS Servers + Multi-AZ Redis Cache + PolarDBの環境を構築してみた。
元の記事では一番簡単なall-in-one(mysqlもECS上で自前で構築する)から徐々に進化にして、最後にredisもPolarDBも利用するという複数のステップがありましたが、本記事は一番最後のフェーズのみ解説していきます。

https://github.com/alibabacloud-howto/solution-marketplace-wordpress
- outputの構成図：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/955cbd79-6c15-d185-c9b0-e6bdd562770b.png)


# 手順
- ソースコードをローカルに持ってくる
```
git clone git@github.com:outan/alibabacloud_terraform.git
cd wordpress_slb_redis_polardb
```
https://github.com/outan/alibabacloud_terraform/tree/main/wordpress_slb_redis_polardb


- terraform.tfvars.sampleをもとに変数を設定
  - `terraform.tfvars.sample`の中身：
    ```
    access_key = ""
    secret_key = ""
    region = "ap-northeast-1"
    zone_1 = "ap-northeast-1a"
    zone_2 = "ap-northeast-1b"
    project_name = "wordpress_slb_redis_polardb"
    ecs_password = "N1cetest"
    redis_account_name = "wordpress"
    redis_account_password="N1cetest"
    polardb_db_name="wordpress"
    polardb_account_name="wordpress"
    polardb_account_password="N1cetest"
    ```

  - `terraform.tfvars.sample`ファイルをcopyし、`terraform.tfvars`というファイルを作成
    ```    
    cp terraform.tfvars.sample terraform.tfvars
    ```
  - アリババクラウドで生成されていたRAM userの`access_key`と`secret_key`を`terraform.tfvars`ファイルに記載してください。
  - redisやpolardbやECSの認証情報はデフォルトの値が入っていますが、必要に応じて変更してください。
- terraformを実行してみる
    ```
    terraform init
    terraform plan
    terraform apply
    ```
- 問題なく実行できたら、以下outputsが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f283f96b-a07b-cbfc-cb3f-64e111bd6c2b.png)


## 中身の説明
```
$ tree
├── main.tf
├── terraform.tfvars
├── terraform.tfvars.sample
└── variables.tf
```

####  VPC
```
resource "alicloud_vpc" "vpc" {
  vpc_name   = "${var.project_name}-vpc"
  cidr_block = "192.168.0.0/16"
}

resource "alicloud_vswitch" "vswitch_1" {
  vpc_id       = alicloud_vpc.vpc.id
  cidr_block   = "192.168.0.0/24"
  zone_id      = var.zone_1
  vswitch_name = "vsw_on_zone_1"
}

resource "alicloud_vswitch" "vswitch_2" {
  vpc_id       = alicloud_vpc.vpc.id
  cidr_block   = "192.168.1.0/24"
  zone_id      = var.zone_2
  vswitch_name = "vsw_on_zone_2"
}
```
- 生成されていたVPC
  - `alicloud_vpc`のresourceに指定されていた`cidr_block`になる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/7b439f0e-aa1f-e05e-d88f-acba154cd355.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/bf41eb15-720e-c048-2149-4d4a5f27aabf.png)

- 生成されていたvSwitch(subnet)
  - `alicloud_vswitch`のresourceに指定されていた`cidr_block`になる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/e75d4b5d-45b8-b65c-334d-30449f350c77.png)

#### redis
```
resource "alicloud_kvstore_instance" "redis" {
  db_instance_name  = "wordpress"
  vswitch_id        = alicloud_vswitch.vswitch_1.id
  security_group_id = alicloud_security_group.group.id
  security_ips      = ["192.168.0.0/16"]
  instance_type     = "Redis"
  engine_version    = "5.0"
  config = {
    appendonly             = "yes",
    lazyfree-lazy-eviction = "yes",
  }
  tags = {
    Created = "TF",
    For     = "Test",
  }
  zone_id        = var.redis_multizone
  instance_class = "redis.master.small.default"
}

resource "alicloud_kvstore_account" "redis_account" {
  account_name     = var.redis_account_name
  account_password = var.redis_account_password
  instance_id      = alicloud_kvstore_instance.redis.id
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/1d970dbb-4e73-af6c-ebc5-77125886a209.png)

- 生成されていたredis:
  - `Tokyo MZone1 A+B`という２つのzoneにまたがって稼働していることが分かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/cbd5790b-ce0f-2851-d4fe-27d5d26268d2.png)
  - `Service Availability`で masterとslaveとれぞれZone AとZone Bで稼働していることが分かる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/68942c82-030c-6155-fd57-83be374105af.png)
  - 生成されていたredis account:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b1dd4fd4-7781-e3e2-2207-f9ae2ee4cfa4.png)
  - もうひとつinstance nameをAccount nameとして自動的に作られていたaccountが存在しています。

https://www.alibabacloud.com/help/en/apsaradb-for-redis/latest/create-and-manage-database-accounts

```
FAQ
Q: Why does an account already exist after an instance is created?

A: A default account whose name is the instance ID is automatically created after an instance is created to ensure data security. The password of this account has been specified when you create the instance. The password can be reset if you forget it.
```
→つまり、terraformで明示的にredisのアカウントを作らなくても、デフォルトで一つのアカウントが自動的に作られるようですが、パスワード不明なので、このアカウントを利用しないようにします。

  - `instance_class`について

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/kvstore_instance#instance_class

```
It can be retrieved by data source alicloud_kvstore_instance_classes or referring to help-docs Instance type table.
```
[Instance type table](https://www.alibabacloud.com/help/en/apsaradb-for-redis/latest/instance-specifications-overview)：
Community版とEnterprise版二種類がありまして、今回はCommunity版中の [Master-replica standard instances](https://www.alibabacloud.com/help/en/apsaradb-for-redis/latest/master-replica-standard-instances#concept-2348204) の`redis.master.small.default`を利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c186a5a6-4608-c936-03ed-a7893f7e8acc.png)
結果として `1 GB Master-replica`のredis instanceが作られました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/aa38ea43-d54f-cfb5-a58d-709bed7f40ce.png)
  - Whitelist:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/7a2b5e39-9ea4-46d5-27f8-41acf868034e.png)
`security_ips = ["192.168.0.0/16"]`で指定されている情報がwhitelistに反映されている。

#### PolarDB MySQL
```
resource "alicloud_polardb_cluster" "cluster" {
  db_type       = "MySQL"
  db_version    = "5.7"
  db_node_class = "polar.mysql.x4.medium"
  pay_type      = "PostPaid"
  security_ips  = ["192.168.0.0/16"]
  vswitch_id    = alicloud_vswitch.vswitch_2.id
  description   = "wpdb"
}

resource "alicloud_polardb_account" "account" {
  db_cluster_id       = alicloud_polardb_cluster.cluster.id
  account_name        = var.polardb_account_name
  account_password    = var.polardb_account_password
  account_description = "wpdb"
}

resource "alicloud_polardb_database" "default" {
  db_cluster_id = alicloud_polardb_cluster.cluster.id
  db_name       = var.polardb_db_name
}

resource "alicloud_polardb_account_privilege" "privilege" {
  db_cluster_id     = alicloud_polardb_cluster.cluster.id
  account_name      = alicloud_polardb_account.account.account_name
  account_privilege = "ReadWrite"
  db_names          = [alicloud_polardb_database.default.db_name]
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/ceea9965-7961-9927-4bcb-b9db6c9879dc.png)

https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/polardb_cluster#db_node_count

- PolarDB cluster nodesの数(デフォルト値= 2)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b8595ab3-b3f4-13ce-187a-8b4ecaec564c.png)

- whitelist:
  - `security_ips  = ["192.168.0.0/16"]`がwhitelistの`IP List`に反映されている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/df5f99d4-a490-d99d-5249-d02fe91511e3.png)
- database:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/64a3f08d-951a-1f42-8cbf-6522ef587422.png)

- polardb account:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/5befcf0e-002d-c888-ce0e-e72d3467abbf.png)

#### ECS
```
resource "alicloud_instance" "instance" {
  security_groups = alicloud_security_group.group.*.id

  # series III
  instance_type           = "ecs.sn1.medium"
  system_disk_category    = "cloud_ssd"
  system_disk_name        = "wp_system_disk_name"
  system_disk_size        = 40
  system_disk_description = "wp_system_disk_description"
  image_id                = "m-6we5ts4nx5ox6jxi8agv"
  instance_name           = "wp"
  password                = var.ecs_password
  instance_charge_type    = "PostPaid"
  vswitch_id              = alicloud_vswitch.vswitch_1.id
  data_disks {
    name        = "disk2"
    size        = 100
    category    = "cloud_efficiency"
    description = "disk2"
    # encrypted   = true
    # kms_key_id  = alicloud_kms_key.key.id
  }
}
```
- image IDについて

https://marketplace.alibabacloud.com/products/56720001/WP_CMS_on_LAMP-sgcmjj00025386.html

東京regionのimage idを利用する必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/5e86ab8e-c08f-0147-33dd-b5c469d91a30.png)
- security group:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/008307e5-f90c-abba-019d-54ce1f53a021.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/eb6da520-e664-6781-71bd-1aeff6297958.png)
- EIP:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/e269106a-f393-d7ef-e27a-6b95d93b4d89.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/24ccc19c-1b90-8c93-71c3-c8e1136dcbb4.png)

#### SLB
```
resource "alicloud_slb_load_balancer" "default" {
  load_balancer_name = "wp_slb"
  load_balancer_spec = "slb.s2.medium"
  vswitch_id         = alicloud_vswitch.vswitch_1.id
  master_zone_id     = var.zone_1
  slave_zone_id      = var.zone_2
}

resource "alicloud_slb_listener" "default" {
  load_balancer_id          = alicloud_slb_load_balancer.default.id
  backend_port              = 80
  frontend_port             = 80
  protocol                  = "http"
  bandwidth                 = 10
  sticky_session            = "on"
  sticky_session_type       = "insert"
  cookie_timeout            = 86400
  cookie                    = "testslblistenercookie"
  health_check              = "on"
  health_check_uri          = "/"
  health_check_connect_port = 80
  healthy_threshold         = 8
  unhealthy_threshold       = 8
  health_check_timeout      = 8
  health_check_interval     = 5
  health_check_http_code    = "http_2xx,http_3xx,http_4xx"
  x_forwarded_for {
    retrive_slb_ip = true
    retrive_slb_id = true
  }
  request_timeout = 80
  idle_timeout    = 30
}

resource "alicloud_slb_backend_server" "default" {
  load_balancer_id = alicloud_slb_load_balancer.default.id

  backend_servers {
    server_id = alicloud_instance.instance.id
    weight    = 50
  }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f50681e9-37e9-0352-1b43-93921cbe00b3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/bfc5e982-ff0e-603d-b2a2-dc85132bfa71.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/9de04e39-d8f7-5fb2-6599-c9cb842bb425.png)

### WordPressの設定
- `ssh root@<EIP_ECS>` でwordpressのECSにログイン
- wordpressの設定ファイルでPolarDBのURL, database, account情報を設定する
`vim /data/wwwroot/wordpress/wp-config.php`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/ed980145-7948-684a-d563-1db4056b1d68.png)
- ブラウザ上でWordPressのIPにアクセスし、初期設定を行う。(IPはterraformのoutput `eip_ecs`です)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/59f3b0c8-6e49-6315-bb8e-433691983c78.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/d919c3b3-dff3-b5d5-af28-927ec02915ef.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/2c65e35c-3f02-fa20-26fa-e1c135f54767.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/9e8ba4e2-e02c-64b9-9df5-650ed0a7698d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/3afbf321-c429-82e2-0f5f-b6fd69a6b962.png)

- Redis cachingを設定する
ECS上で以下のコマンドを実行し、Redis object cache pluginをダウンロードし、解凍する
```
wget https://downloads.wordpress.org/plugin/redis-cache.2.0.18.zip 
unzip redis-cache.2.0.18.zip 
```
- redis-cache folderをcopyする
`cp -rf redis-cache /data/wwwroot/wordpress/wp-content/plugins/`
- WordPressをredisにアクエスできるように設定する
`vim /data/wwwroot/wordpress/wp-config.php`
  - wp-config.php ファイルの最初の設定の箇所に以下の内容を追加する(最初に設定しないとだめだそうです)
```
// Redis settings
define( 'WP_REDIS_HOST', '<Redis URL>' );
define( 'WP_REDIS_CLIENT', 'predis' );
define( 'WP_REDIS_PORT', '6379' );
define( 'WP_REDIS_DATABASE', '0');
define( 'WP_REDIS_PASSWORD', 'wordpress:N1cetest' );
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/7abf23f4-d946-7155-f127-b88d09cb773b.png)
- object-cache configuration ファイルをcopyする
`cp /data/wwwroot/wordpress/wp-content/plugins/redis-cache/includes/object-cache.php /data/wwwroot/wordpress/wp-content/ `
- WordPressにログインして、Redis object cacheを有効化にする
  - `Redis Object Cache` pluginを先に`Activate`してから、`Settings` ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/3d9dbf66-fe01-7963-c7b0-6d1f6e70ca96.png)
  - pluginのステータスが`Connected`であることが確認できたら、`Flush Cache`ボタンをクリックしcache dataをRedis instanceに同期させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/a631143e-f786-726c-9e46-4920ff542fa9.png)
- SLB EIPにアクエスし、以下の画面が表示されたらOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/d9f9903c-5963-36e4-9bbb-98c4c7429b5c.png)

### Redisのキャッシュ効果を確認
- ECS上で以下のコマンドを実行し、redisに接続する(-a の後ろはuserとpasswordになります)
`redis-cli -h r-0iw88071b19727c4.redis.japan.rds.aliyuncs.com -a 'wordpress:N1cetest'`
```
r-0iw88071b19727c4.redis.japan.rds.aliyuncs.com:6379> keys *
 1) "wp:options:auth_key"
 2) "wp:comment:1"
 3) "wp:category_relationships:1"
 4) "wp:options:can_compress_scripts"
 5) "wp:default:is_blog_installed"
 6) "wp:transient:global_styles_twentytwentyone"
 7) "wp:options:nonce_salt"
 8) "wp:users:1"
 9) "wp:user_meta:1"
10) "wp:userlogins:dennis"
11) "wp:options:nonce_key"
12) "wp:comment_meta:1"
13) "wp:site-options:1-notoptions"
14) "wp:post_tag_relationships:1"
15) "wp:comment:last_changed"
16) "wp:terms:get_terms-a217cd3f6ed4806e2fd8fc7ffa68df8b-0.28634600 1661250085"
17) "wp:post_meta:3"
18) "wp:post_format_relationships:1"
19) "wp:terms:last_changed"
20) "wp:options:alloptions"
21) "wp:posts:1"
22) "wp:userslugs:dennis"
23) "wp:comment:get_comments-522753603409e7a482bf9f7dbe7ea238-0.96158000 1661250122"
24) "wp:posts:3"
25) "wp:post_meta:1"
26) "wp:transient:global_styles_svg_filters_twentytwentyone"
27) "wp:options:notoptions"
28) "wp:redis-cache:metrics"
29) "wp:default:wp_global_styles_00b0e7fde1a7a17937e88b6df1cfbe6f"
30) "wp:terms:get_terms-cac253f41dff4350b0f851e64d3bb31b-0.28634600 1661250085"
31) "wp:useremail:outanwang@gmail.com"
32) "wp:options:auth_salt"
33) "wp:terms:1"

```
- `wp:posts:1`のkeyの中身を取得してみます(中身は初期設定で自動的に作られたpostの中身であることが分かる。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/87886c89-0ec3-e2c1-7363-307140e19634.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/dc9fc546-59fc-59b8-c129-3fd5df1177ca.png)