👾

Amazon ElastiCache for Redis のハンズオンをやってみた!

2023/11/27に公開

私の所属するチームでは現在オンプレのシステムを AWS 上にリフト&シフトしている最中で現在シフト作業の真っ最中です!
オンプレの構成から変更していくなかで Redis を使う必要が出てきましたがチームのメンバーには Redis を触ったことのない人もいたのでひとまずチームでモブハンズオンをすることになりました。
その下準備としてまずは自分が公式のハンズオンを試して情報をまとめてみようと思います。

やってみるハンズオン

Boosting MySQL database performance with Amazon ElastiCache for Redis

ちなみにこちらの記事から見つけました。
ハンズオンの「腹落ち問題」を改善する 5 Tips

このハンズオンの概要としては以下になります。
リレーショナルデータベースのパフォーマンス向上のために、 Amazon ElastiCache for Redis を使用したインメモリキャッシュレイヤーを実装する方法を学びます。
アプリケーションがデータを読み取る際にまずキャッシュをクエリし、データが見つからない場合にデータベースをクエリしてキャッシュに結果を保存します。リレーショナルデータベースにインメモリキャッシュを追加することで、アプリケーションのパフォーマンスを向上させる効果的な戦略を紹介しています。

ということで手順に沿ってやっていこうとしましたが、、記事が古いらしく色々と現在の画面と違っていたり、事前の準備が必要そうだったりしたので今回は公式の手順を参考にしつつ、以下の構成を作成して Amazon ElastiCache for Redis (以下 Redis)の動作を試してみたいと思います。

次の手順でやっていきます

  • VPC の作成
  • EC2 のロール作成
  • EC2 作成
  • EC2 の設定
  • RDS のサブネット作成
  • RDS のセキュリティグループの作成
  • RDS の作成
  • RDS にデータの投入
  • Redis のサブネット作成
  • Redis のセキュリティグループ作成
  • Redis の作成
  • EC2 から動作の確認

VPC の作成

まずはパブリックサブネット1つとプライベートサブネットを2つ作っていきます。
VPC を作成をクリックします。

次に以下の入力内でVPCを作成します。

これで VPC と サブネット、インターネットゲートウェイ、ルートテーブルが作成されました。

EC2 のロール作成

続いて EC2 で利用するロールを作成します。
セッションマネージャーで接続したいので AmazonSSMManagedInstanceCore ポリシーをアタッチします。
ロール名は ec2-ssm としておきます。

EC2 作成

次は EC2 の作成です。インスタンスを起動から作成していきます。
以下の設定で作成していきます。

ネットワークでは先ほど作成した VPC を選択しています。
サブネットは 1a 側のパブリックサブネットを選択しています。
セキュリティグループは作成していますが、SSHを許す必要が無いのでルールを削除しています。

高度な設定では IAM インスタンスプロフィールに先ほど作成したロールを設定しています。
それ以外はデフォルト値のままです。

インスタンスを起動をクリックして EC2 を立ち上げます。

EC2 の設定

EC2 の準備ができたので公式のハンズオンにあるコマンドを実行していきます。
今回は簡単にするために python の virtualenv は利用しません。
それでは 作成した EC2 に セッションマネージャーで接続します。

接続できたら次のコマンドを実行していきます。

$ cd
$ sudo yum install git -y
$ sudo yum install mysql -y
$ sudo yum install python3 -y
$ git clone https://github.com/aws-samples/amazon-elasticache-samples/
$ cd amazon-elasticache-samples/database-caching
$ pip3 install -r requirements.txt

これで公式のハンズオンにある python プログラムを実行する準備ができました。

RDS のサブネット作成

次に RDS 用のサブネットを作成していきます。
Amazon RDS の画面から「サブネットグループ」を選択して「DB サブネットグループを作成」をクリックします。
以下の値を入力してサブネットを作成します。

RDS のセキュリティグループの作成

続いて EC2 からアクセスするための RDS のセキュリティグループを作成します。
EC2 のセキュリティグループから「セキュリティグループを作成」をクリックし、
以下の内容を設定します。
ソースのカスタムには EC2 で作成したセキュリティグループを指定します。

RDS の作成

次の Amazon RDS のデータベースのページから「データベースの作成」をクリックします。

パスワードは後ほど使うので控えておきます。

VPC や DB サブネットグループ VPC セキュリティグループなどは先ほど作成したものを指定します。

追加設定ではバックアップを外しておきます。

以上の設定でデータベースを作成します。

作成された DB のエンドポイントも次で使うので控えておきます。

RDS にデータの投入

それでは今作成したデータベースにテストデータを投入していきます。
EC2 にセッションマネージャーで接続し以下のコマンドを実行します。

$ cd /home/ssm-user/amazon-elasticache-samples/database-caching/
$ mysql -h ここに DB エンドポイント -P 3306 -u admin -p < seed.sql

成功すると次のようなデータが入ります。

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| tutorial           |
+--------------------+
5 rows in set (0.00 sec)

MySQL [(none)]> use tutorial
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [tutorial]> show tables;
+--------------------+
| Tables_in_tutorial |
+--------------------+
| planet             |
+--------------------+
1 row in set (0.00 sec)

MySQL [tutorial]> select * from planet;
+----+---------+
| id | name    |
+----+---------+
|  1 | Mercury |
|  2 | Venus   |
|  3 | Earth   |
|  4 | Mars    |
|  5 | Jupiter |
|  6 | Saturn  |
|  7 | Uranus  |
|  8 | Neptune |
+----+---------+
8 rows in set (0.00 sec)

これでデータベースの準備は終了です。

Redis のサブネット作成

次に Redisのサブネットを作成していきます。
ElastiCache の画面に移動し、サブネットグループのページで「サブネットグループを作成」をクリックします。

今回作成した VPC を選択してサブネットにはプライベートサブネットだけを選択しました。

Redis のセキュリティグループ作成

次に RDS の時と同様にセキュリティグループを作成していきます。

今回のポートは6379です。 EC2 から接続したいのでソースは redis-test-ec2 を選択します。

Redis の作成

ElastiCache の画面に移動し、リソースの Redis クラスターで「 Redis クラスターを作成」をクリックして以下の情報を入力していきます。

この状態で一度作成しますが、セキュリティグループがdefaultのままなので先ほど作成したセキュリティグループに変更します。

変更が反映されたらエンドポイントを控えておきます。

EC2 から動作の確認

まずは EC2 から Redis に疎通できるか確認をします。
EC2 にセッションマネージャで接続して以下のコマンドを実行してみます。

$ cd /home/ssm-user/amazon-elasticache-samples/database-caching/
$ pytho3
>>> import redis
>>> client = redis.Redis.from_url('redis://ここに Redis のエンドポイント')
>>> client.ping()
True

ping の結果が True であれば Redis との疎通ができています。

これで Redis のハンズオンをおこなう環境構築が完了しました。

次はハンズオンのリポジトリにある python のコードを見ながら実際にプログラムを動かしていきます。

ハンズオンのプログラム内容の確認

EC2 に持ってきた今回のハンズオンのプログラムは以下の構成になっています。

https://github.com/aws-samples/amazon-elasticache-samples/

$ pwd
/home/ssm-user/amazon-elasticache-samples/database-caching
$ ls -alsh
total 24K
   0 drwxr-xr-x  3 ssm-user ssm-user  110 Nov 25 05:55 .
   0 drwxr-xr-x 10 ssm-user ssm-user  282 Nov 25 05:55 ..
4.0K -rw-r--r--  1 ssm-user ssm-user  567 Nov 25 05:55 INSTALL
4.0K -rw-r--r--  1 ssm-user ssm-user  257 Nov 25 05:55 README.md
4.0K -rw-r--r--  1 ssm-user ssm-user 1.9K Nov 25 05:55 example.py
4.0K drwxr-xr-x  2 ssm-user ssm-user 4.0K Nov 25 05:55 images
4.0K -rw-r--r--  1 ssm-user ssm-user   28 Nov 25 05:55 requirements.txt
4.0K -rw-r--r--  1 ssm-user ssm-user  495 Nov 25 05:55 seed.sql

この中で Redis に対する操作を行なっているのは example.py なのでこの中身を確認していきます。

example.py
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

import os
import json
import redis
import pymysql


class DB:
    def __init__(self, **params):
        params.setdefault("charset", "utf8mb4")
        params.setdefault("cursorclass", pymysql.cursors.DictCursor)

        self.mysql = pymysql.connect(**params)

    def query(self, sql):
        with self.mysql.cursor() as cursor:
            cursor.execute(sql)
            return cursor.fetchall()

    def record(self, sql, values):
        with self.mysql.cursor() as cursor:
            cursor.execute(sql, values)
            return cursor.fetchone()


# Time to live for cached data
TTL = 10

# Read the Redis credentials from the REDIS_URL environment variable.
REDIS_URL = os.environ.get('REDIS_URL')

# Read the DB credentials from the DB_* environment variables.
DB_HOST = os.environ.get('DB_HOST')
DB_USER = os.environ.get('DB_USER')
DB_PASS = os.environ.get('DB_PASS')
DB_NAME = os.environ.get('DB_NAME')

# Initialize the database
Database = DB(host=DB_HOST, user=DB_USER, password=DB_PASS, db=DB_NAME)

# Initialize the cache
Cache = redis.Redis.from_url(REDIS_URL)


def fetch(sql):
    """Retrieve records from the cache, or else from the database."""
    res = Cache.get(sql)

    if res:
        return json.loads(res)

    res = Database.query(sql)
    Cache.setex(sql, TTL, json.dumps(res))
    return res


def planet(id):
    """Retrieve a record from the cache, or else from the database."""
    key = f"planet:{id}"
    res = Cache.hgetall(key)

    if res:
        return res

    sql = "SELECT `id`, `name` FROM `planet` WHERE `id`=%s"
    res = Database.record(sql, (id,))

    if res:
        Cache.hmset(key, res)
        Cache.expire(key, TTL)

    return res


# Display the result of some queries
print(fetch("SELECT * FROM planet"))
print(planet(1))
print(planet(2))
print(planet(3))

使っている python の redis ライブラリは 3.2.1 と少し古いもののようです。
https://pypi.org/project/redis/3.2.1/

このプログラムを動かすためには環境変数の設定が必要なので以下のコマンドを先に実行しておきます。

$ export REDIS_URL=redis://Redis のエンドポイント/
$ export DB_HOST=RDS のエンドポイント
$ export DB_USER=admin
$ export DB_PASS=RDS のパスワード
$ export DB_NAME=tutorial

一旦実行してみます。

$ python3 example.py
[{'id': 1, 'name': 'Mercury'}, {'id': 2, 'name': 'Venus'}, {'id': 3, 'name': 'Earth'}, {'id': 4, 'name': 'Mars'}, {'id': 5, 'name': 'Jupiter'}, {'id': 6, 'name': 'Saturn'}, {'id': 7, 'name': 'Uranus'}, {'id': 8, 'name': 'Neptune'}]
{'id': 1, 'name': 'Mercury'}
{'id': 2, 'name': 'Venus'}
{'id': 3, 'name': 'Earth'}

動きました!
プログラムの最後のところで fetch 関数と planet 関数の結果を出力しています。

それぞれの関数を見ていきます。

fetch 関数

fetch 関数は、与えられた SQL クエリに基づいてデータを取得します。
まずRedisキャッシュをチェックし、キャッシュが存在すればそれを返します。
キャッシュがない場合はデータベースからデータを取得し、新たにキャッシュに保存する動きをしています。

def fetch(sql):
    """Retrieve records from the cache, or else from the database."""
    res = Cache.get(sql)

    if res:
        return json.loads(res)

    res = Database.query(sql)
    Cache.setex(sql, TTL, json.dumps(res))
    return res

planet 関数

planet関数は、特定の惑星のデータを取得するためのものです。
これも同様にRedisキャッシュを先に確認し、キャッシュがなければデータベースからデータを取得してキャッシュします。

def planet(id):
    """Retrieve a record from the cache, or else from the database."""
    key = f"planet:{id}"
    res = Cache.hgetall(key)

    if res:
        return res

    sql = "SELECT `id`, `name` FROM `planet` WHERE `id`=%s"
    res = Database.record(sql, (id,))

    if res:
        Cache.hmset(key, res)
        Cache.expire(key, TTL)

    return res

これで公式のハンズオンをざっくり試すところまでできました。
あとは公式の Module 4: Caching and Best Practices の内容を詳細にみていこうと思います。

まとめ

今回は AWS 公式の ElastiCache のハンズオンを行なってみました。
AWS の画面が結構変わっていたり、事前準備やネットワーク構成の部分がわかりにくかったので一旦自分なりにまとめてから環境構築を行なってみました。実は構築していく中で結構躓いたのでかなりいい勉強になりました。
これで準備はできたのでチームメンバーと一緒にモブハンズオンで学習していきます!

レスキューナウテックブログ

Discussion