AWS ElastiCache에서 Redis cluster 구축하기

2024.01.31

aws elasticache로 redis 클러스터를 생성하고 연결해본 경험을 작성하였습니다. 해당 포스트의 내용은 틀릴수도 있습니다.


무엇을 할것인가?

1. AWS ElastiCache redis 클러스터 생성
2. ec2 인스턴스에서 연결 테스트
3. Nest.js 앱에서 연결 테스트

ElastiCache for redis는 redis 서버를 구축할때 필요한 클러스터 구성, 자동 백업 및 복구등 상태관리까지 aws에서 알아서 해주는 참 편리한 서비스다.(그리고 엄청 비싸다) 나머지 잡다한 설명은 생략하고 elasticache redis를 구축하는 방법을 알아보자.


읽기전에

redis 의 노드샤드 그리고 클러스터에 대한 개념을 이해하고 아래의 글을 읽으면 좀더 쉽게 이해할수있다. 간략히 말하자면(내가 제대로 이해한게 맞다면) 노드 란 key를 저장하고 가져올수있는 실제 redis 인스턴스이며 여러개의 노드를 묶은 집합을 샤드라고 하며 이 샤드를 묶은게 클러스터이다. 자세한 설명은 이 포스트의 내용을 읽어보자.

ElastiCache 는 vpc 안에서만 생성이 가능하며 같은 vpc안에 있는 인스턴스(ec2, lambda, ecs 같은 서비스앱)에서만 접근이 가능하다. elasticache를 생성하기 위해선 vpc 구성이 반드시 필요하다. 이 포스트에선 vpc 생성에관한 내용은 다루지 않는다.


ElastiCache Redis 클러스터 생성

aws 콘솔에서 ElastiCache Redis를 생성한다. 이 포스트에서는 자체 설계 캐시로 생성할것이다.


cluster setting


redis 캐시를 생성할때 클러스터 모드 사용 여부를 결정할수있다. 클러스터 모드 비활성화는 읽기/쓰기 노드(프라이머리) 하나와 최대 5개의 읽기전용(복제용) 노드로 구성된 단일 샤드로만 생성되며 클러스트 모드 활성화는 최대 500개의 샤드로 클러스터를 구성할수있다.
aws 문서의 설명이 좀 헷갈리수있는데 간략하게 차이점을 설명하자면 클러스트 모드를 활성화하든 비활성화 하든 redis 캐시는 하나의 클러스터가 만들어지며 그 안에 샤드라는 그룹을 하나만 사용할껀지(클러스터 모드 비활성화) 여러개를 사용할껀지(클러스터 모드 활성화)를 선택하는 부분이다.
클러스터 모드 활성화 여부에따라 연결할때 사용하는 url 엔드포인트가 달라지며 나중에 알아볼 NestJS에서 연결하는 방법도 달라진다. 중요한 부분이니 꼭 기억해두자.


cluster info

cluster setting 2


파라미터 그룹

파라미터 그룹은 클러스터 또는 redis 노드를 실행할때 적용할 설정값들을 그룹으로 만들어서 적용시킬수있다. 말그대로 redis.conf 파일 옵션들을 지정한 그룹이다. aws에서는 기본 파라미터 그룹을 제공하고 있다. 노드 설정을 원하는대로 수정하고 싶다면 직접 파라미터 그룹을 생성하고 적용시킬수있다.
redis 7.0 버전 기준 지원하는 파라미터 옵션은 여기를 참조하자.

노드 유형

실행시킬 redis 인스턴의 스펙이다 테스트용 생성이니 가장 작은걸로 선택하자

샤드 수

클러스터안에 들어갈 샤드의 수

샤드당 복제본 수

하나의 샤드안에는 기본적으로 primary node(마스터 노드)와 그 값을 복제하는 복제용 노드의 갯수를 설정한다.


subnet group


서브넷 그룹

서브넷 그룹은 클러스터를 실행할 vpc의 subnet을 묶은 그룹이다 왜 필요한진 모르겠지만 아무튼 필수이다. 새 서브넷 그룹 생성을 눌러서 클러스터를 생성할 vpc의 subnet들을 선택하고 생성한다음 지정해주자.


AZ setting


슬롯 및 키스페이스

데이터 파티셔닝을위해 redis 내부적으로 사용하는 범위값이다. 10만개의 데이터가 있다면 3개의 샤드에 균등하게 저장할껀지 아니면 직접 범위를 지정해줄수있다. 이 파티셔닝이 무엇인지 궁금하다면 이 글을 읽어보자.
그 다음 보안설정으로 넘어간다.


security setting


전송 중 암호화(TLS)

전송되는 데이터(샤드의 노드간 복제, 서버-클라이언트간 전송)를 암호화시키는 옵션이다. 데이터를 암호화 하고 해독하는 과정이 추가되기때문에 성능저하가 발생할수있다. 그리고 tls 활성화 여부에 따라 클라이언트에서 연결하는 방법이 달라진다. 중요한 내용이니 꼭 기억해두자. 지금은 테스트용이니 따로 설정하지 않고 넘어간다. (나중에 tls 적용 클러스터 생성과 연결 방법 포스팅에서 자세히쓸 예정)

보안 그룹

해당 클러스터에 적용할 보안그룹이다 vpc 내부에서 연결하기 위해 redis 기본포트인 6379 인바운드 설정을 반드시 해줘야한다.


sg setting


나머지 설정 내용들은 redis 버전 업데이트, 백업, 로깅에 관한 사항이나 생략하도록한다.
마지막으로 최종검토를 하고 클러스터를 생성하자. 대략 10분정도 기다리면 클러스터가 실행중 상태로 바뀐다.


EC2에서 redis-cli로 클러스터 연결 확인하기

(이 부분은 선택사항이므로 넘어가고 싶다면 생략하자)
같은 vpc에 ec2 인스턴스를 생성하고 redis 클러스터 접속을 확인해보자. 마찬가지로 같은 vpc안에 ec2를 생성하는 설명은 생략하겠다.
이 예제에서는 amzazon linux AMI 를 기준으로 작성되었다.


ec2 인스턴스에 redis-cli 설치하기

ec2에 접속하여 아래의 명령어를 실행한다.

sudo yum install gcc jemalloc-devel openssl-devel tcl tcl-devel clang wget
sudo wget http://download.redis.io/redis-stable.tar.gz
sudo tar xvzf redis-stable.tar.gz
cd redis-stable
sudo CC=clang make BUILD_TLS=yes

redis 클러스터 접속 확인하기

생성했던 elasticache 클러스터의 접속 url을 확인하자.


cluster endpoint


이 접속 url은 클러스터 모드 활성화 여부에 따라서 달라진다. 클러스터모드가 비활성화 되었을경우 기본(primary) 엔드포인트리더(leader) 엔드포인트 가 있으며 기본 엔드포인트는 샤드안에 있는 기본노드를 나타내며 (읽기/쓰기) leader 엔드포인트는 복제노드(읽기 전용)를 나타낸다.
클러스터 모드가 활성화 되어있는경우 구성(configuration) 엔드포인트 하나만 존재한다. 이제 redis-cli를 통해 이 url에 접속해보자.
위에서 redis-cli를 설치한후 라면 현재 위치는 /home/ec2-user/redis-stable 일것이다.


/src/redis-cli -h {클러스터 url} -p {포트} -c

-c 옵션은 클러스터 모드 활성화를 나타낸다. redis-cli 에서 클러스터 모드가 활성화 되면 파티셔닝된 데이터들을 set 이나 get 할때 클러스터에 의해서 자동으로 리다이렉션 해준다.


cluster mode enabled


접속한 클러스터에서 set 명령어를 실행시켜보자. slot에 따라 파티셔닝이 되는 모습이다.


Nest.js 에서 클러스터에 연결하기

elasticache 클러스터는 같은 vpc 내부에서만 접근이 가능하기때문에 이 nestjs 앱은 같은 vpc에 있는 ec2에서 실행되고있다고 가정한다.
실제 api url을 통해서 확인할것이기 때문에 ec2에 nestjs 기본포트인 3000 또는 80 포트를 뚫어주자.
클러스터에 연결할때는 ioredis 라는 패키지를 사용할것이다.

nest 구성

redis 연결을 확인할 /redis 라는 라우트를 생성한다.

//redis.controller.ts
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { RedisService } from './redis.service';

@Controller('redis')
export class RedisController {
  constructor(private service: RedisService) {}

  @Get('/keys')
  async getKeys() {
    return await this.service.getKeys();
  }

  @Get('/')
  async getOne(@Query('key') key: string) {
    return await this.service.getOne(key);
  }

  @Post('/')
  async setOne(@Body() value: { key: string; value: string }) {
    return await this.service.setOne(value);
  }
}

//redis.module.ts
import { Module } from '@nestjs/common';
import { RedisController } from './redis.controller';
import { RedisService } from './redis.service';
import RedisProvider from './redis.provider';

@Module({
  controllers: [RedisController],
  providers: [
    RedisService,
    {
      provide: 'REDIS_CLIENT',
      useFactory: () => {
        return RedisProvider();
      },
    },
  ],
})
export class RedisModule {}

//redis.provider.ts
import { Redis } from 'ioredis';

export default async () => {
  //elasticache가 클러스터로 구성되어있기때문에 Redis.Cluster() 인스턴스를 생성한다. 
  return new Redis.Cluster([
    {
      host: {elasticache 클러스터 url},
    },
  ]);
};

//redis.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { Cluster } from 'ioredis';

@Injectable()
export class RedisService {
  constructor(@Inject('REDIS_CLIENT') private redis: Cluster) {}

  async getKeys() {
    const keys = await this.redis.keys('*');
    return keys;
  }

  async getOne(key: string) {
    return await this.redis.get(key);
  }

  async setOne(data: { key: string; value: string }) {
    return await this.redis.set(data.key, data.value);
  }
}

간단하게 redis 클러스터에 set과 get을 할수있는 nest 앱을 만들었다. 실제로 실행시키기 전에 redis-cli로 먼저 값들을 넣어보자.


set keys in cli

그리고 key를 실제로 가져오는지 /redis?key=key1 url로 접속해보자.


get key test


제대로 동작하고있다.
그럼 이제 set 도 확인해보자.


set key test


방금 설정했던 `setTest` 라는 키를 요청해보자.

get key test2


set도 제데로 동작하고있다.
마지막으로 /keys 도 확인해보자(사실 이거 잘못된거임) 현재 redis에 저장된 모든 key값을 가져오는 명령어다.


get keys

key가 뜨긴하는데 뭔가 이상하다 원래 라면 지금 클러스터에는 [key1, key2, key3, hi, setTest] 이렇게 총 5개의 키가 있어야하는데 2개만 뜨고있다. 사실 이건 redis cluster와 ioredis의 동작방식을 모르고 그냥 keys * 명령어를 실행해서 그렇다. ioredis 문서에 따르면 클러스터 모드에서 keys 같은 키 값이 포함되지 않는 명령어를 실행하면 ioredis는 클러스터에 있는 임의의 노드에 명령어를 보낸다. 현재 클러스터는 샤딩으로 데이터가 파티셔닝이 되어있기때문에 저 keys 명령어를 실행할때마다 랜덤 노드에 저장하고 있는 key 들만 가져오게 된다. 실제로 계속 요청을 보낼때마다 다른 key 값들을 리턴할것이다. 이를 해결하기 위해선 모든 노드에 keys 명령어를 실행시켜야 한다고한다. 자세한 내용은 ioredis 문서를 읽어보자.


후기...

대충이지만 elasticache로 redis 클러스터를 생성하고 연결까지 해보았다. 요즘 클라우드 서비스가 워낙 좋아서 정말 클릭 몇번으로 복잡하고 자동관리까지 해주는 클러스터 구축이 가능해졌다. 사실 클러스터 생성하고 연결 테스트까지 성공하는데 온갖 시행착오를 겪으며 12시간이 넘게 걸렸고 단순 aws에서 생성하고 연결했다로 끝이 아닌, redis cluster의 자세한 개념자체를 이해 해야겠다고 생각하여 redis cluster의 개념을 공부하고 이해 하는데에만 수일이 걸렸다. 만약 클라우드 서비스가 아닌 진짜 리눅스 서버에 redis server를 설치하고 직접 클러스터 구성 설정하여 구축하였다면 더더욱 오래걸렸을것이다. 클라우드 서비스가 참 좋긴하지만 그래도 본인이 사용하는 서비스의 기본적인 개념은 알고있어야 할것같다.



주의사항

이번 포스팅 내용에따라 클러스터를 생성하고 테스트가 끝났으면 잊지말고 반드시 클러스터를 삭제하자. 아무리 낮은 스펙으로 노드를 생성했다 하더라도 클러스터에 생성된 노드 수 만큼 비용이 엄창나게 나간다. t2 micro 스펙으로 해도 샤드 하나에 노드 3개, 2개의 샤드(총 6개의 노드)로 구성된 클러스터면 t2 micro 요금의 6배가 된다.

Do you want something exciting?

© 2022. YSH All rights reserved.