ElastiCache (Redis)のクラスターモードの作成メモ

# 概要

ElastiCache (Redis)のクラスターモードの作り方を確認した時のメモです。

**環境**

* ElastiCache
    * Redis 4.0.10

**参考**

* [Redis 用 Amazon ElastiCache] (https://aws.amazon.com/jp/elasticache/redis/)
* [Amazon ElastiCache のドキュメント] (https://aws.amazon.com/jp/documentation/elasticache/?id=docs_gateway)
* [Command reference - Redis] (https://redis.io/commands)
* [Redis latency problems troubleshooting] (https://redis.io/topics/latency)
* [redis-cli, the Redis command line interface] (https://redis.io/topics/rediscli)


# ElastiCache (Redis)をクラスターモードで作成する

## クラスターを作成する

「クラスターモードが有効」にチェックを入れます
![r0.png](https://qiita-image-store.s3.amazonaws.com/0/22772/6538ada0-9ef8-dd52-aebe-ecdbba44a027.png)

「シャード数」に3、「シャードあたりのレプリカ」に1を指定すると、マスターノードが3つ、それぞれのマスターに1つのスレーブノードが作成されるので、計6ノードが作成されます。
1つのマスターノードに複数のスレーブノードを作成する場合は「シャードあたりのレプリカ」の数を変更します。
![r1.png](https://qiita-image-store.s3.amazonaws.com/0/22772/fc233377-8f49-afb1-5dce-c29fe78cf8ad.png)

サブネットグループは新規に作成し、ノードを分散配置するためのサブネットを指定します。
![r2.png](https://qiita-image-store.s3.amazonaws.com/0/22772/029438a9-21bb-b20a-477d-e60e65be036b.png)

アベイラビリティーゾーンには「指定なし」と「アベイラビリティーゾーンの指定」のどちらかを選択します。

アベイラビリティーゾーンのヘルプテキストには下記の内容が書かれています。

> * HAモード
>     * AZを分散して、AZ分散を最大化します。第2のレベルでは、シャード内のノードをシャード内HAのAZ全体に分散させます。
> * Lowレイテンシモード
>     * 書き込みを高速にするために、すべてのシャードマスターを同一のAZに置きます。

**「指定なし」の場合**

AWSが自動的にノードを配置してくれます。今回は「指定なし」を選択しました。
この結果、マスターとスレーブは別々のアベイラビリティーゾーンに配置され、またマスターも別々のアベイラビリティーゾーンに配置されていました。（3つのマスターのうち、Aに2つ、Cに1つといった具合です）
![r3.png](https://qiita-image-store.s3.amazonaws.com/0/22772/cdd4335e-9a52-b3ff-d469-af8f963b46cb.png)

**「アベイラビリティーゾーンの指定」の場合**

ノードのアベイラビリティゾーンを任意の場所に選択できます。
![r3b.png](https://qiita-image-store.s3.amazonaws.com/0/22772/07059b5f-f08e-5f25-d92b-3d569e02afd6.png)

セキュリティグループはデフォルトのままにしました。
![r4.png](https://qiita-image-store.s3.amazonaws.com/0/22772/70be4e16-5608-2323-7d4a-1d9389e011b8.png)

ここもデフォルトのままにし、以上の設定でクラスターを作成します。
![r5.png](https://qiita-image-store.s3.amazonaws.com/0/22772/6e03e9d9-ed7a-a6f7-6e71-f61bce244a59.png)

## クラスターの作成後

クラスターが作成されるまで少し時間がかかりますが、作成されるとダッシュボードで下図のような情報が確認できるようになります。
アプリケーションからElastiCacheに接続するには設定エンドポイントを利用します。
（それぞれのノードにもエンドポイントがあり、それを使って直接接続することもできます。）
![r6.png](https://qiita-image-store.s3.amazonaws.com/0/22772/16844ffb-3c1a-0f55-fd53-870a33313df2.png)

**設定エンドポイントとは**

> [Redis 用 ElastiCache コンポーネントと機能] (https://docs.aws.amazon.com/ja_jp/AmazonElastiCache/latest/red-ug/WhatIs.Components.html)
> 
> **Redis (クラスターモードが有効) のエンドポイント**
> Redis (クラスターモードが有効) クラスターには、1 つの設定エンドポイントがあります。設定エンドポイントに接続することで、アプリケーションはクラスター内のシャードごとにプライマリおよびリードエンドポイントを検出できます。

クラスター名をクリックすると各シャードとスロット／キースペースが確認できます。
![r7.png](https://qiita-image-store.s3.amazonaws.com/0/22772/60e5dd0e-9b8c-37d6-0c3d-84207b3d82a1.png)

上図の画面でシャード名をクリックするとノードとノードのエンドポイントが確認できます。
![r8.png](https://qiita-image-store.s3.amazonaws.com/0/22772/76036621-ab2d-4d20-ce62-1ea7cfdceb44.png)

さらにノードにチェックを入れると、そのノードのCloudWatchメトリクスが確認できます。
![r9.png](https://qiita-image-store.s3.amazonaws.com/0/22772/bd704894-6e47-840f-308e-818bb8b13f07.png)


# redis-cliからアクセスする

EC2インスタンスにredisをインストールし、redis-cliからElastiCacheへアクセスしてみます。
（セキュリティグループの設定は済んでいるものとして進めます）

```bash
$ sudo amazon-linux-extras install redis4.0
```

インストールできたら、ホストに設定エンドポイントを指定して接続します。

```bash
$ redis-cli -h my-redis-cluster.xxxxxx.clustercfg.apne1.cache.amazonaws.com -c
```

クラスターノードの情報を確認します。
(プロンプトにはエンドポイント名とポートが表示されますが、長くなるので省略しています。）
masterやslaveと表示されている箇所にmyselfと付加されているノードが、設定エンドポイントで接続しているノードです。

```bash
> cluster nodes
5b12a3224aff9d4c5c1d312b94e5ddca84782801 172.30.2.11:6379@1122 slave 6107f27650e0837a2f39a7e1b50dae02b0d42119 0 1536419967252 0 connected
6107f27650e0837a2f39a7e1b50dae02b0d42119 172.30.0.52:6379@1122 master - 0 1536419965000 0 connected 0-5461
24dfbbd0897bf009d00aae91eb359f46b6f4f593 172.30.2.80:6379@1122 master - 0 1536419966246 2 connected 10923-16383
ea610022f0c81a408ec1c37b25cff8948cef90a3 172.30.2.10:6379@1122 slave 60067520f1d72958f6a0839a286a4127c33ee864 0 1536419966000 1 connected
b182f973228aeb1a2462f5987374614c2ae4af21 172.30.0.35:6379@1122 slave 24dfbbd0897bf009d00aae91eb359f46b6f4f593 0 1536419964231 2 connected
60067520f1d72958f6a0839a286a4127c33ee864 172.30.0.200:6379@1122 myself,master - 0 1536419963000 1 connected 5462-10922
```

masterノードの行の最後に表示されている数値の範囲はスロットです。

|シャード名            |スロット    |マスター                      |スレーブ|
|:--------------------|:----------|:-----------------------------|:------|
|my-redis-cluster-0001|0-5461     |ap-northeast-1a / 172.30.0.52 |ap-northeast-1c / 172.30.2.11 |
|my-redis-cluster-0002|5462-10922 |ap-northeast-1a / 172.30.0.200|ap-northeast-1c / 172.30.2.10 |
|my-redis-cluster-0003|10923-16383|ap-northeast-1c / 172.30.2.80 |ap-northeast-1a / 172.30.0.35 |

**スロットとは**

データを書き込んだり読み取ったりする場合、キー名のhash値からスロットが導出され、そのスロットが割り当てられたノードに書き込み・読み込みが行われます。
クラスターにはスロットが0から16383までの16384個あります。

## データを登録する

設定エンドポイントに接続します。実際に接続したのはmy-redis-cluster-0002というシャードのマスターノード(172.30.0.200)です。

```bash
$ redis-cli -h my-redis-cluster.xxxxxx.clustercfg.apne1.cache.amazonaws.com -c
my-redis-cluster.xxxxxx.clustercfg.apne1.cache.amazonaws.com:6379>
```

ここで"key1"というキーで文字列を保存します。

```bash
my-redis-cluster.xxxxxx.clustercfg.apne1.cache.amazonaws.com:6379> set key1 123
OK
```

続いて"key2"というキーで文字列を保存しますが、このキーはスロット4998になるので、my-redis-cluster-0001というシャードへリダイレクトされ、このシャードのマスターノードに保存されます。この時点でプロンプトがIPアドレスとポートに変わります。

```bash
my-redis-cluster.xxxxxx.clustercfg.apne1.cache.amazonaws.com:6379> set key2 456
-> Redirected to slot [4998] located at 172.30.0.52:6379
OK
```

さらに"key3"というキーで文字列を保存します。

```bash
172.30.0.52:6379> set key3 789
OK
```

今いるノードに保存されているデータを確認します。

```bash
172.30.0.52:6379> keys *
1) "key3"
2) "key2"
```

最初のノードに戻り保存されているデータを確認します。

```bash
172.30.0.52:6379> connect 172.30.0.200 6379
172.30.0.200:6379> keys *
1) "key1"
```

スレーブにも同じキーが存在するか確認します。

```bash
172.30.0.200:6379> connect 172.30.2.10 6379
172.30.2.10:6379> keys *
1) "key1"
```

# CloudWatchで監視する

基本的な使い方を確認してみました。（監視のベストプラクティスの紹介ではありません。）
CloudWatchにアクセスし「ダッシュボードの作成」ボタンをクリックします。
「ウィジェットの追加」ボタンをクリックし監視したいメトリクスにElastiCacheを選択します。
![c0.png](https://qiita-image-store.s3.amazonaws.com/0/22772/bccedbf8-4400-5508-3ccf-6164a21fdfc3.png)

ウィジェットにはいくつものメトリクス（監視対象の項目、CPU利用率、メモリ、コネクションなど）を追加できますが、たくさん追加すると視認性がさがるのでシンプルにしています。

コネクション数のメトリクスのウィジェット(CurrConnections/NewConnections)を作成
![c1.png](https://qiita-image-store.s3.amazonaws.com/0/22772/ced4377a-bcd2-3943-c3a9-572c68469a27.png)

CPU利用率のメトリクスのウィジェット(CPUUtilization/EngineCPUUtilization)を作成
![c2.png](https://qiita-image-store.s3.amazonaws.com/0/22772/c1e16ed6-2f31-f8f6-09d3-7eb75f7a0abf.png)

ダッシュボードにウィジェットを追加した状態です。この例ではクラスター単位で集計した値を表示していますが、ノード毎に監視することもできます。
![c3.png](https://qiita-image-store.s3.amazonaws.com/0/22772/c1505068-b288-ffe9-73e3-3f4a295b5e8f.png)


# アベイラビリティーゾーン間のレイテンシを確認する

redis-cli --latencyを使ってNetwork Latencyを計測することができます。
--latencyを付けると、pingコマンドを連続して実行し（止めるときはCtrl + C）、コマンドの実行に掛かった時間(min / max / avg)を計測します。
以下の実行結果は、ap-northeast-1aにあるEC2から各ノードのレイテンシを計測したものです。（samplesというのがコマンドの実行回数です）
この結果からアベイラビリティーゾーンをまたぐ場合は、同一の場合より約5.8から6.1倍ほど遅いということがわかります。

**注意**

この記事ではもっとも性能の低いインスタンスを利用しています。インスタンスの性能が高いとネットワークのスループットもよくなるので、必ずしもこの結果のようなレイテンシになるとは限りません。

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1a)

```bash
$ redis-cli -h my-redis-cluster-0001-001.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 0, max: 4, avg: 0.47 (1030 samples)^C
```

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1c)

```bash
$ redis-cli -h my-redis-cluster-0001-002.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 2, max: 18, avg: 2.79 (1037 samples)^C
```

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1a)

```bash
$ redis-cli -h my-redis-cluster-0002-001.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 0, max: 12, avg: 0.46 (1037 samples)^C
```

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1c)

```bash
$ redis-cli -h my-redis-cluster-0002-002.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 2, max: 20, avg: 2.71 (1027 samples)^C
```

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1c)

```bash
$ redis-cli -h my-redis-cluster-0003-001.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 2, max: 6, avg: 2.71 (1035 samples)^C
```

* EC2 (ap-northeast-1a) <---> Node (ap-northeast-1a)

```bash
$ redis-cli -h my-redis-cluster-0003-002.xxxxxx.0001.apne1.cache.amazonaws.com --latency
min: 0, max: 6, avg: 0.44 (1055 samples)^C
```