logo
Published on

Valkeyチュートリアル - Dockerでの構築からAmazon ElastiCacheまで

Authors
Valkey

tl:dr

携わっているプロダクトでキャッシュレイヤーを導入することになりました。 既存のインフラでAWSをメインに採用していることもあり、サービスとしてはAmazon ElastiCacheを利用することとしました。 RedisからForkしたValkeyも提供されていますが、2024年10月8日にServerless版もリリースされ、さらにRedisよりも安価に利用できること、Serverless版ではクラスターモードの利用になることから ローカル検証用にDockerでもValkey Clusterを構築し、またプロダクション用途でValkey for Serverlessを利用するところまでを整理します。

Valkey CLI

Macの場合、 brew install valkey でインストールできます。

Homebrew Formulae:valkey

Docker上でのValkey(No Cluster)構築

ValkeyのDockerイメージは公式で提供されているため、Docker Composeを使って簡単に構築できます。

以下のdocker-compose.ymlをそのまま利用することで簡単にValkeyのコンテナを起動できます。

valkey/valkey

version: "3.9"

services:
  valkey_container_1:
    image: valkey/valkey:8.0.1
    container_name: valkey_container_1
    ports:
      - "6379:6379"
    volumes:
      - valkey-data:/var/lib/valkey

volumes:
  valkey-data:

docker exec -it valkey_container_1 valkey-cliでValkeyのCLIに接続できます。

もしくはホストマシンからでも接続可能です。

$ valkey-cli -h localhost -p 6379

接続後、簡単な動作確認を行います。

127.0.0.1:6379> set test_key test_value
ok

127.0.0.1:6379> get test_key
"test_value"

Docker上でのValkey Cluster構築

Docker上でクラスターモードを構築する場合、以下のようなdocker-compose.ymlを利用します。 3つのノードを構築し、それぞれのノードには違うポートを割り当てます。 その上で1つのClusterとしてまとめるようにします。

version: "3.9"

services:
  valkey-nodes-1:
    image: valkey/valkey:8.0.1
    container_name: valkey-nodes-1
    ports:
      - "6379:6379"
    volumes:
      - valkey-nodes-data1:/var/lib/valkey
    networks:
      valkey-cluster:
        ipv4_address: 192.168.4.101
    command: >
      sh -c "
      valkey-server --port 6379 --cluster-enabled yes
      --cluster-config-file nodes.conf
      --cluster-node-timeout 5000
      --appendonly yes
      --protected-mode no
      --dir /var/lib/valkey &&
      tail -f /dev/null
      "

  valkey-nodes-2:
    image: valkey/valkey:8.0.1
    container_name: valkey-nodes-2
    ports:
      - "6380:6379"
    volumes:
      - valkey-nodes-data2:/var/lib/valkey
    networks:
      valkey-cluster:
        ipv4_address: 192.168.4.102
    command: >
      sh -c "
      valkey-server --port 6379 --cluster-enabled yes
      --cluster-config-file nodes.conf
      --cluster-node-timeout 5000
      --appendonly yes
      --protected-mode no
      --dir /var/lib/valkey &&
      tail -f /dev/null
      "

  valkey-nodes-3:
    image: valkey/valkey:8.0.1
    container_name: valkey-nodes-3
    ports:
      - "6381:6379"
    volumes:
      - valkey-nodes-data3:/var/lib/valkey
    networks:
      valkey-cluster:
        ipv4_address: 192.168.4.103
    command: >
      sh -c "
      valkey-server --port 6379 --cluster-enabled yes
      --cluster-config-file nodes.conf
      --cluster-node-timeout 5000
      --appendonly yes
      --protected-mode no
      --dir /var/lib/valkey &&
      tail -f /dev/null
      "

  valkey-cluster:
    image: valkey/valkey:8.0.1
    container_name: valkey-cluster
    depends_on:
      - valkey-nodes-1
      - valkey-nodes-2
      - valkey-nodes-3
    networks:
      - valkey-cluster
    entrypoint: >
      sh -c "
      sleep 10 &&
      echo 'yes' | valkey-cli --cluster create \
      192.168.4.101:6379 192.168.4.102:6379 192.168.4.103:6379 \
      --cluster-replicas 0 &&
      tail -f /dev/null
      "

volumes:
  valkey-nodes-data1:
  valkey-nodes-data2:
  valkey-nodes-data3:

networks:
  valkey-cluster:
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.4.0/24

3つのValkeyノードを構築し、それをまとめる形で valkey-cluster を構築しています。

以下のコマンドで構築したClusterの状態を確認できます。

$ valkey-cli -h localhost -p 6379 cluster nodes

// 以下のような出力が得られる
192.168.4.103:6379@16379 master - 0 1733572817000 3 connected 10923-16383
192.168.4.101:6379@16379 myself,master - 0 0 1 connected 0-5460
192.168.4.102:6379@16379 master - 0 1733572818461 2 connected 5461-10922

connectedの後ろの部分の数字のレンジは、各ノードが担当するスロットの範囲を示しています。

クラスターモードではキーがハッシュスロットに基づいて分散されるので、入力値のキーは計算結果に基づいてノードに振り分けられます。

Valkey(Redis)ではキーが 0 - 16383 の範囲でハッシュされます。

入力キーに対してCRC16ハッシュを計算し、その結果を16384で割った余りがスロット番号となります。

クラスターモードのValkeyで値の追加と取得を行ってみます。

CLIで操作する場合は、特定のノードを指定して操作する必要があります。今回は valkey-nodes-1 に接続して操作します。

-c オプションをつけることでクラスターモードでの操作が可能になります。

$ docker exec -it valkey-nodes-1 valkey-cli -c -p 6379

以下、クラスターモードで接続したCLIでの操作例です。

$ docker exec -it valkey-nodes-1 valkey-cli -c -p 6379
127.0.0.1:6379> set test-data-1 "cluster mode test"
OK
127.0.0.1:6379> get test-data-1
"cluster mode test"

127.0.0.1:6379> set test-data-2 "cluster mode test-2"
-> Redirected to slot [14064] located at 192.168.4.103:6379
OK
127.0.0.1:6379> get test-data-2
-> Redirected to slot [14064] located at 192.168.4.103:6379
"cluster mode test-2"

1回目のデータの追加では test-data-1をキーにしていますが、これはハッシュスロットがvalkey-nodes-1に割り当てられているためそのまま追加できます。

2回目のデータの追加ではtest-data-2をキーにしていますが、これはハッシュスロットがvalkey-nodes-3に割り当てられているため、valkey-nodes-3(192.168.4.103:6379)にリダイレクトされて追加されます。

同様に、test-data-2の取得もリダイレクトされてvalkey-nodes-3から取得されます。

Node.jsでのValkeyの利用

Node.jsでValkeyを利用する場合、 ライブラリを用いて操作します。 今回はRedis用に作られたioredisというライブラリからValkey用にForkされたiovalkeyを利用します。

TypeORMなどのライブラリに組み込んで使用する場合、現時点でiovalkeyはサポートされていないため、直接iovalkeyを利用するか、ioredisを使用することになります。

この記事を書いている時点では双方に大きな差はないので、ioredisの使用でも問題ないかと思います。

一番シンプルに、これまで構築したDockerコンテナを使ってローカル環境で動かすのは以下のようになります。

import { Cluster } from 'iovalkey';

const cluster = new Cluster([
    {host: 'valkey-nodes-1', port: 6379},
    {host: 'valkey-nodes-2', port: 6379},
    {host: 'valkey-nodes-3', port: 6379}
])

実際にはオプションやパスワードの設定等の実装を行う場合がほとんどだと思います。

import { Cluster } from 'iovalkey';

const cluster = new Cluster([
    {host: 'valkey-nodes-1', port: 6379},
    {host: 'valkey-nodes-2', port: 6379},
    {host: 'valkey-nodes-3', port: 6379}
],{
    // @see: https://github.com/luin/ioredis#special-note-aws-elasticache-clusters-with-tls
    dnsLookup: (address, callback) => callback(null, address), // Amazon ElastiCacheの場合に必要
    clusterRetryStrategy(times) {
        if (times > 5) {
          console.error('Too many retries, giving up on connection.')
          return null
        }
        console.log(`Retrying cluster connection, attempt ${times}`)
        return 2000 // 2秒後に再試行
      },
      redisOptions: {
        username: 'username' // ElastiCacheでアクセスコントロール設定済みの場合、アプリケーション用に設定したユーザ名を指定
        password: 'password', // 上記ユーザ名に対応するパスワードを設定
        connectTimeout: 10000,
        tls: {} // TLS有効の場合に必要。Amazon ElastiCache for Valkey Serverlessの場合は必須
      }
})

Amazon ElastiCache for Valkey Serverlessの場合、TLSが必ず有効になっているため、tls: {}を設定する必要があります。 またdnsLookupもAmazon ElastiCacheの場合に必要になります。

Valkey(Redis)で使用できるGUIツール

CLIを使えばValkey内に保持したデータの確認などは可能ですが、毎度CLIを使うのは面倒なのでGUIツールを使うようにします。

個人的には以下のどちらかを使います。

Medis2

Medis2 MedisはMacのみ対応しているRedisのGUIツールです。

MacのApp Storeからもダウンロード可能で手軽に使用できます。

クラスターモードにも対応していますし、UIもシンプルで使いやすいのでローカルでの開発でも使いやすいGUIです。

RedisInsight

RedisInsight

RedisInsightはRedisの公式で提供されているGUIツールです。

プロダクション環境での利用も可能で、クラスターモードにも対応しています。

Redis CopilotというAI機能も搭載されており、データ分析等にも使えるようになっています。

EC2上に構築し、オンライでは運用しているRedis(Valkey)に接続して利用することも可能で、オフラインではS3等に保存しているデータを使った分析などが可能です。

RedisInsightの場合には、ローカル環境での使用の場合には提供されているDockerイメージを使って立ち上げます。

# これまでのdocker-compose.ymlに追記
  redisinsight:
    image: redislabs/redisinsight:latest
    container_name: redisinsight
    ports:
      - 5540:5540
    volumes:
      - redisinsight-data:/db
    networks:
      - valkey-cluster
    depends_on:
      - valkey-cluster

  volumes:
    valkey-nodes-data1:
    valkey-nodes-data2:
    valkey-nodes-data3:
    redisinsight-data: ## 追加

上記を追加して再度 docker compose upでRedisInsightが立ち上がるので、localhost:5540にアクセスすることでRedisInsightのGUIが表示されます。

起動したRedisInsight

ローカル環境の場合は、[+ Add Redis Connection]から接続先を追加します。

今回はhostにノードを指定することで接続できます

RedisInsight接続後

すべてのデータを確認できますし、Workbenchからはコマンドを使った操作も可能です。

またPub/Sub用の機能も提供されているので、Pub/Subの確認も可能です。

さらにAnalytics機能ではクラスター内のすべてのノードのデータを分析することも可能で、非常に便利なツールです。

RedisInsightのAnalytics機能

プロダクション環境で利用すれば各ノードの状況把握も容易になりますし、開発時点でも例えばテストデータを用いたデータ分散の確認なども行えるので、キャッシュ設計等でも非常に使えそうです。

Amazon ElastiCache for Valkey Serverless の構築と接続

構築の詳細は公式でも提供されているので省略しますが、プロダクトでの連携で注意すべきポイントだけ記載します。

  1. Amazon ElastiCache for Valkey Serverlessはクラスターモードのみの提供 これまではValkeyはServerless版の提供がなく、その時点まではクラスターモードを有効にするかどうかは自由に選択できました。

しかしServlerssではクラスターモードのみの提供となるため、クラスターモードでの運用を前提として設計する必要があります。

上記であげた iovalkey を使用する場合でも、new Valkey.Cluster でクラスターモードを有効にする必要があります。

  1. Serlerss版ではTLSが有効になっている こちらもServerless版から必須になった設定ですが、TLSが有効になっているため、tls: {}の設定が必要になります。

CLIでも --tls オプションをつける必要があるため、これまでよりもセキュリティが強化されている反面、接続の際には注意が必要です。

  1. クラスターモードでは dnsLookup: (address, callback) => callback(null, address) の設定が必要 Amazon ElastiCacheをClusterで利用する場合、証明書関係のエラーになり接続がうまくいかないケースがあります。

その回避のため、以下のように設定を行います。

import { Cluster } from 'iovalkey';

const cluster = new Cluster([
    {
        host: 'hostname',
        port: "port"
    }
], {
    dnsLookup: (address, callback) => callback(null, address), // Amazon ElastiCacheの場合に必要
    redisOptions: {
        password: 'password', // パスワードが設定されている場合
        connectTimeout: 10000,
        tls: {} // TLS有効の場合に必要。Amazon ElastiCache for Valkey Serverlessの場合は必須
    }
})

Amazon ElastiCache for Valkey Serverlessへの監視

EC2に上記で紹介したRedisInsightを構築するなどしてメモリ分析やキーの分散、保持しているデータの確認などが可能です。

方法については今後追記しますが、以下の記事などが参考になるかと思います。

Redisをグラフィカルに確認できるRedisInsightでElastiCache Redisのメモリ分析してみた

参考サイト