1. 概要
- AWS ElastiCache Redis の クラスターモード無効 で次の挙動を把握したい
- エンドポイント
- レプリカノードのスケールアウト/イン
- マルチAZ
- ノードのスペックアップ/ダウン
- フェイルオーバー
- Terraform の設定の理解や、実行手順を確認したい
2. 検証するElastiCacheの構成
-
ElastiCache のサブネットグループは次の3つのAZを選択
-
ap-northeast-1a
-
ap-northeast-1c
-
ap-northeast-1d
-
Redis の クラスターモードは「無効 (disabled)」を選択
-
小中規模のサービスで利用することを想定しているため
3. クラスターモードが無効の場合の機能や注意点
- レプリカノードのスケールアウト(追加)、スケールイン(削除) が可能
- ノードタイプのスケールアップ、スケールダウンが可能
- データのパーティション化は無い
- シャードは1つ
- 作成可能なリードレプリカ数は 0〜5 ノード
- マルチAZはデフォルトでオン
- スナップショット(バックアップ)可能
- 復元可能
- Redisのすべてのバージョンで利用可能
- エンジンバージョンのアップブレードは、制限あるが可能
- Redis エンジンバージョン 5.0.5 以降では、最小限のダウンタイムでクラスターバージョンをアップグレードできる
【補足】Redis (クラスターモードが無効) と Redis (クラスターモードが有効) の比較
-
クラスターモードが無効(disabled)
-
ノードグループは1つで、そのグループ内のクラスターは常に 1 個のシャードと最大 5 個のリードレプリカノードを持つ構成(MySQLのレプレケーション構成に近い)。
-
プライマリノードに問題が生じたときは、レプリカノードが昇格する (フェイルオーバー)。
-
クラスターモードが有効(enabled)
-
ノードグループは複数で、それぞれのグループ内のクラスターは 1 ~ 5 個のリードレプリカノードのある最大 90 個のシャードを持つクラスター。
-
プライマリノードの冗長構成が可能。
-
データセットが大規模な場合や、ピーク需要時のアクセスのボトルネックを減らしたい場合は、クラスタモード「有効」を使うべし。
5. クラスターモード無効 の構成を Terraform で作成する方法
- Redisクラスターモードが無効の構成を作成する方法は2通りある。
今回は、単に number_cache_clusters を直接調整する方法とする。
【参考】Resource: aws_elasticache_replication_group
6. 単一のプライマリノードを作成する
- 目的の構成
- プライマリノード 0⇒1個 (書き取り/読み取り)
6.1. terraform サンプル
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "1"
automatic_failover_enabled = "false"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- number_cache_clusters = 1
- プライマリノード1個
- automatic_failover_enabled = "false"
- ノードが1個の場合は「false」 とする。
- 「true」とした場合、terraform apply時に次のようなエラーとなる
Error: Error creating Elasticache Replication Group: InvalidParameterCombination: When using automatic failover, there must be at least 2 cache clusters in the replication group.
status code: 400, request id: ********-****-****-****-************
-
apply_immediately = "true"
-
いま時点では必要ないが、あとあと レプリカノードをすべて削除する時に automatic_failover_enabled の設定を即時に反映させるために必要なので、設定を入れておく。
-
availability_zones は不要
-
本家の Resource: aws_elasticache_replication_group のサンプルにはあるが、以降も不要。
-
サブネットグループで選択した AZ (ap-northeast-1a, ap-northeast-1c, ap-northeast-1d) が暗黙で利用される。
-
逆に、availability_zones を設定すると何かと問題がでるので、レプリカノード作成時も設定はしない。
【補足】availability_zones
- プライマリノード1個の場合に availability_zones を設定すると terraform apply時に 次のようなエラーとなるので、そもそも availability_zones は設定できない。
Error: Error creating Elasticache Replication Group: InvalidParameterValue: When specifying preferred availability zones, the number of cache clusters must be specified and must match the number of preferred availability zones.
status code: 400, request id: ********-****-****-****-************
- プライマリノード1個を作成するときに availability_zones は設定できないが、レプリカノードを作成するときは availability_zones を設定できる。後から availability_zones 設定を追加した場合は次のように forces replacement となるため、エンドポイントの変更はないが、プライマリノードを含む全てのノードが作り直されてしまう(データが飛ぶ)。
availability_zones = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
+ availability_zones = [
+ "ap-northeast-1a",
+ "ap-northeast-1c",
+ "ap-northeast-1d",
] # forces replacement
6.2. 作成されたクラスター/ノードの情報
-
クラスター
-
ノードの数: 1個のノード
-
マルチAZ: disabled
-
ノード
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
7. レプリカノードを1個作成する
- 目的の構成
- プライマリノード 1個 (書き取り)
- レプリカノード 0⇒1個 (読み取り)
7.1. terraform サンプル
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "2"
automatic_failover_enabled = "true"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- number_cache_clusters = 2
- プライマリノード1個
- レプリカノード1個
- automatic_failover_enabled = "true"
- 自動フェイルオーバーを有効にする
【補足】[automatic_failover_enabled] (https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticache_replication_group)
- 既存のプライマリに障害が発生した場合に、読み取り専用レプリカを読み取り/書き込みプライマリに自動的に昇格させるかどうかを指定します。
- trueの場合、このレプリケーショングループに対してマルチAZが有効になります。
- falseの場合、このレプリケーショングループのマルチAZは無効になります。
7.2. 作成されたクラスター/ノードの情報
-
クラスター
-
ノードの数: 2個のノード
-
マルチAZ: disabled
※この「disabled」表示、多分クラスターモード無効の場合は関係なさそう(暗黙でマルチAZはデフォルトがオンになっているので)。 -
ノード
-
replicaノードが ap-northeast-1c に作られた
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-002.※※※※※※.0001.apne1.cache.amazonaws.com
【補足】リーダーエンドポイントにデータを格納しようとするとエラーになる
$ curl -s telnet://test-nishimura-ro.******.ng.0001.apne1.cache.amazonaws.com:6379
set test1 aaaa
-READONLY You can't write against a read only replica.
8. レプリカノードを5個にする
- 目的の構成
- プライマリノード 1個 (書き取り)
- レプリカノード 1⇒5個 (読み取り)
8.1. terraform サンプル
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "6"
automatic_failover_enabled = "true"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- number_cache_clusters = 6
- プライマリノード1個
- レプリカノード5個
8.2. 作成されたノードの情報
-
ノード
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-002.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-003.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-004.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-005.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-006.※※※※※※.0001.apne1.cache.amazonaws.com
9. レプリカノードを6個にしてみる
- 目的の構成
- プライマリノード 1個 (書き取り)
- レプリカノード 5⇒6個 (読み取り)
9.1. terraform 設定
- number_cache_clusters = 7
- プライマリノード1個
- レプリカノード6個
9.2. terraform apply 実行
- 次のエラーが出た
- やはり、作成可能なリードレプリカ数は 0〜5 ノードだった
Error: error creating Elasticache Cache Cluster (adding replica): InvalidReplicationGroupState: Specified replication group test-nishimura has already met the number of read replicas allowed.
status code: 400, request id: ********-****-****-****-************
10. レプリカノードを減らす
- 目的の構成
- プライマリノード1 個 (書き取り)
- レプリカノード 5⇒2個 (読み取り)
10.1. terraform サンプル
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "3"
automatic_failover_enabled = "true"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- number_cache_clusters =5 ⇒ 3
- プライマリノード 1個
- レプリカノード 2個
10.2. 作成されたノードの情報
11. レプリカノードをすべて削除して、プライマリノード1個だけにする
-
目的の構成
-
プライマリノード1個 (書き取り/読み取り)
-
レプリカノード 2⇒0個
-
実施手順
※複数レプリカノードがある状態からすべてのレプリカノードを削除して、プライマリノード1個だけに変更する -
まずは、automatic_failover_enabled を false にして terraform apply
-
その後で、number_cache_clusters = 1 にして terraform apply
11.1. terraform サンプル その1
まずは automatic_failover_enabled を false にして terraform apply する
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "3"
automatic_failover_enabled = "false"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- automatic_failover_enabled = true ⇒ false
11.2. terraform サンプル その2
その1を terraform apply した後に number_cache_clusters を3から1へ減らす
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.small"
number_cache_clusters = "1"
automatic_failover_enabled = "false"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- number_cache_clusters = 3 ⇒ 1
- プライマリノード 1個
- レプリカノード 0個
【補足】最後のレプリカノードを削除するときの terraform エラー例
- automatic_failover_enabled = true のままで number_cache_clusters = 3 を 1 に変更して terraform apply すると、次のエラーが出て最後のレプリカノード(test-nishimura-002)が削除されない。
Error: error deleting Elasticache Cache Cluster (test-nishimura-002) (removing replica): InvalidCacheClusterState: Cannot delete the cache cluster because it is the only read replica for replication group test-nishimura which has autofailover enabled or pending. You can disable autofailover and then delete this cache cluster. Alternatively, to delete the entire replication group, use DeleteReplicationGroup.
status code: 400, request id: ********-****-****-****-************
- automatic_failover_enabled の true ⇒ false 変更と、number_cache_clusters の3⇒1 変更を同時に設定して terraform apply しても、次のエラーが出て最後のレプリカノード(test-nishimura-002)が削除されない。
Error: error deleting Elasticache Cache Cluster (test-nishimura-002) (removing replica): InvalidCacheClusterState: Cannot delete the cache cluster because it is the only read replica for replication group test-nishimura which has autofailover enabled or pending. You can disable autofailover and then delete this cache cluster. Alternatively, to delete the entire replication group, use DeleteReplicationGroup.
status code: 400, request id: ********-****-****-****-************
12. ノードのタイプ変更(スペックアップ)
- 実施内容
- プライマリノード1個 (書き取り)
⇒ cache.t3.small から cache.t3.medium へスペックアップ
⇒ cache.t3.small から cache.t3.medium へスペックアップ
12.1. ノードタイプ変更前の構成
-
クラスター
-
ノード
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-002.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-003.※※※※※※.0001.apne1.cache.amazonaws.com
12.2. 事前設定
- タイプ変更前後でデータが消えないかどうかの確認のため、事前にデータを格納しておく
$ curl -s telnet://test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
set A A
+OK
set B B
+OK
set C C
+OK
^C
- タイプ変更中にデータが書き込めるか確認するため、1秒おきにプライマリエンドポイントに接続してデータをsetするスクリプトを動かす
#!/bin/sh
LANG=C
for i in {1..2000}
do
date
echo key$i
echo "$i" | redis-cli -h test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com -x set key$i
sleep 1
done
- タイプ変更中にデータが読み込めるか確認するため、1秒おきにリーダーエンドポイントに接続してデータをgetするスクリプトを動かす
#!/bin/sh
LANG=C1
for i in {1..2000}
do
date
echo key$i
redis-cli -h test-nishimura-ro.※※※※※※.ng.0001.apne1.cache.amazonaws.com get key$i
sleep 1
done
12.3. terraform サンプル
resource "aws_elasticache_replication_group" "test-nishimura" {
replication_group_id = "test-nishimura"
replication_group_description = "test-nishimura"
node_type = "cache.t3.medium"
number_cache_clusters = "3"
automatic_failover_enabled = "true"
engine = "redis"
engine_version = "5.0.6"
port = 6379
parameter_group_name = "test-nishimura"
maintenance_window = "mon:21:30-mon:22:30"
security_group_ids = [
"sg-*****************"
]
subnet_group_name = "test-nishimura"
snapshot_retention_limit = 7
snapshot_window = "20:00-21:00"
apply_immediately = "true"
}
- node_type = cache.t3.small ⇒ cache.t3.medium
12.4. タイプ変更実施
12.5. ノードタイプ変更 完了後の確認
- terraform apply
- 23分31秒で完了
〜
aws_elasticache_replication_group.test-nishimura: Still modifying... [id=test-nishimura, 23m21s elapsed]
aws_elasticache_replication_group.test-nishimura: Still modifying... [id=test-nishimura, 23m31s elapsed]
aws_elasticache_replication_group.test-nishimura: Modifications complete after 23m35s [id=test-nishimura]
-
イベントログ
-
クラスター
-
ノード
-
ノード名とロールに変更はないので、ノードタイプ変更 によってレプリカノードがプリマリノードに昇格するなどのフェイルオーバーは発生しない
-
作成日の変更はないので、ノードが作り直されることはない
-
すべてのノードが同じスペックになるので、例えば、プライマリノードだけレプリカノードよりも高いスペックのインスタンスタイプにすることはできない
-
エンドポイント
-
変更なし
-
事前に格納したデータの確認
-
リーダーエンドポイントへgetして生存確認
$ curl -s telnet://test-nishimura-ro.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
get A
$1
A
get B
$1
B
get C
$1
C
^C
- データをsetするスクリプト
- 開始から終了まで毎秒止まることなく プライマリエンドポイントへ接続して set した
- 4〜5回同じ検証を試しけど、すべての検証において set が止まることはなかった
$ sh set.sh
Wed Oct 28 08:58:29 UTC 2020
key1
OK
Wed Oct 28 08:58:30 UTC 2020
key2
OK
Wed Oct 28 08:58:31 UTC 2020
key3
・
・
・
Wed Oct 28 09:22:35 UTC 2020
key1432
OK
Wed Oct 28 09:22:36 UTC 2020
key1433
OK
^C
- データをgetするスクリプト
- 途中、1回 リーダーエンドポインへの接続が数分切れた
- 4〜5回同じ検証を試しけど、すべての検証において1、2回 get が数分切れた
$ sh get.sh
・
・
Wed Oct 28 09:06:14 UTC 2020
key456
"456\n"
Wed Oct 28 09:06:15 UTC 2020
key457
Could not connect to Redis at test-nishimura-ro.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection timed out
Wed Oct 28 09:08:25 UTC 2020
key458
"458\n"
Wed Oct 28 09:08:26 UTC 2020
key459
"459\n"
・
・
13. フェイルオーバー (レプリカノードをプライマリノードへ昇格)
- 実施内容
- プライマリノード1個 (書き取り)
⇒ 手動でフェイルオーバーを実施
⇒ 自動でどちらかがプライマリノードへ昇格
13.1. ノードタイプ変更前の構成
-
ノード
-
ノードは3個
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-002.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-003.※※※※※※.0001.apne1.cache.amazonaws.com
13.2. 事前設定
- フェイルオーバー前後でデータが消えないかどうかの確認のため、事前にデータを格納しておく
$ curl -s telnet://test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
set A A
+OK
set B B
+OK
set C C
+OK
^C
-
フェイルオーバー中にデータが書き込めるか確認するため、1秒おきにプライマリエンドポイントに接続してデータをsetするスクリプトを動かす
-
タイプ変更中にデータが読み込めるか確認するため、1秒おきにリーダーエンドポイントに接続してデータをgetするスクリプトを動かす
13.3. プライマリノードのフェイルオーバーを実施
-
実施手順
-
まず、set.sh を走らせる
-
そのあと get.sh を走らせる
-
それから 手動で AWSマネジメントコンソールからプライマリノードのフェイルオーバーを実行する
13.4. ノードタイプ変更 完了後の確認
-
イベントログ
-
14時57分03秒〜15時02分29秒
-
フェイルオーバー自体は、14時57分03秒〜14時57分47秒 で完了して、クラスターのスタータスは available になった。
-
ノード
-
レプリカノードだった 001 が プライマリノード へ昇格した
-
エンドポイント
-
プライマリエンドポイント: test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
⇒ プライマリノード test-nishimura-003.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-002.※※※※※※.0001.apne1.cache.amazonaws.com
⇒ レプリカノード test-nishimura-001.※※※※※※.0001.apne1.cache.amazonaws.com
- 事前に格納したデータの確認
- リーダーエンドポイントへgetして生存確認
$ curl -s telnet://test-nishimura-ro.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379
get A
$1
A
get B
$1
B
get C
$1
C
^C
- データをsetするスクリプト
- 6秒ほど プライマリエンドポイント へ接続できなくなった
$ sh set.sh
・
・
Thu Oct 29 05:57:21 UTC 2020
key49
OK
Thu Oct 29 05:57:22 UTC 2020
key50
OK
Thu Oct 29 05:57:23 UTC 2020
key51
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:24 UTC 2020
key52
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:25 UTC 2020
key53
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:26 UTC 2020
key54
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:27 UTC 2020
key55
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:28 UTC 2020
key56
Could not connect to Redis at test-nishimura.※※※※※※.ng.0001.apne1.cache.amazonaws.com:6379: Connection refused
Thu Oct 29 05:57:29 UTC 2020
key57
^CThu Oct 29 05:58:10 UTC 2020
key58
OK
・
・
- データをgetするスクリプト
- 開始から終了まで毎秒止まることなく リーダーエンドポインへ接続して get した
14. まとめ
-
エンドポイント
-
リーダーエンドポイントは積極的に使ったほうがよい
-
特に、スケールアウト/イン や フェイルオーバー する場合は必須
-
レプリカノードのスケールアウト/イン
-
Auroraのレプリカ作成/削除するみたいな感じで、サービス中でも特に気にせず実行できる
※Auroraの場合は一度に大量のレプリカを作成するとプライマリに負荷がかかっちゃうけど -
レプリカノードをすべて削除する場合は、terraform の実行手順に注意
-
マルチAZ
-
デフォルトでオンになっている
-
サブネットグループで選択したAZに配置される
-
サブネットグループは3つのAZで作ったほうが良い
-
ノードのスペックアップ/ダウン
-
すべてのノードが同一のタイプになる
-
タイプ変更にかかる時間は20分 (ノードが3個で、cache.t3.small から cache.t3.medium の場合)
-
タイプ変更中は、書き込みはできる
-
タイプ変更中は、読み込みが数回、数分間できなくなる
-
これであれば、メンテ入れなくても深夜のアクセスが少ない時間帯にできそうな感じ
-
フェイルオーバー
-
レプリカノードの昇格は数秒で終わる
-
フェイルオーバー中は、書き込みが数秒できなくなる
-
フェイルオーバー中は、読み込みはできる