Skip to main content

Redis

Redis는 고성능 인메모리 데이터 구조 저장소입니다. Lumie 플랫폼에서 캐시, 세션 스토어, 메시지 브로커 등 다양한 용도로 사용됩니다.

개요

주요 기능

기능설명
인메모리 캐시빠른 데이터 접근 및 처리
고가용성Sentinel을 통한 자동 페일오버
복제Master-Replica 구조
모니터링Prometheus 메트릭 수집
세션 스토어사용자 세션 관리

아키텍처


설치 및 구성

Helm 차트 설정

# Redis Sentinel 구성
global:
imageRegistry: zot.lumie-infra.com
security:
allowInsecureImages: true

image:
repository: bitnamilegacy/redis
tag: "8.2.1-debian-12-r0"

# 복제 아키텍처 with Sentinel
architecture: replication

# 인증 비활성화 (클러스터 내부 통신)
auth:
enabled: false

Master 설정

master:
persistence:
enabled: false # 인메모리 전용
resources:
requests:
memory: 128Mi
cpu: 20m
limits:
memory: 128Mi

특징:

  • Persistence 비활성화: 순수 인메모리 캐시로 사용
  • 최소 리소스: 캐시 용도로 가벼운 설정
  • CPU 제한 없음: 안정성 우선

Replica 설정

replica:
replicaCount: 2
persistence:
enabled: false
resources:
requests:
memory: 35Mi
cpu: 22m
limits:
memory: 35Mi

2개의 Replica로 읽기 부하를 분산하고 가용성을 향상시킵니다.


Sentinel 구성

고가용성 설정

sentinel:
enabled: true
image:
repository: bitnamilegacy/redis-sentinel
tag: "8.2.1-debian-12-r0"
quorum: 2
downAfterMilliseconds: 5000
failoverTimeout: 60000
resources:
requests:
memory: 33Mi
cpu: 22m
limits:
memory: 33Mi

Sentinel 파라미터

파라미터설명
quorum2페일오버 결정을 위한 최소 Sentinel 수
downAfterMilliseconds5000Master 다운 판정 시간 (5초)
failoverTimeout60000페일오버 타임아웃 (60초)

페일오버 프로세스


연결 및 사용

연결 정보

서비스엔드포인트포트용도
Masterredis-master.lumie-cache.svc.cluster.local6379읽기/쓰기
Replicaredis-replica.lumie-cache.svc.cluster.local6379읽기 전용
Sentinelredis-sentinel.lumie-cache.svc.cluster.local26379페일오버 관리

애플리케이션 연결

Go 클라이언트 예시

import (
"github.com/go-redis/redis/v8"
"context"
)

// Sentinel 클라이언트 설정
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{
"redis-sentinel.lumie-cache.svc.cluster.local:26379",
},
})

// 사용 예시
ctx := context.Background()
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}

val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key", val)

Node.js 클라이언트 예시

const Redis = require('ioredis');

const redis = new Redis({
sentinels: [
{ host: 'redis-sentinel.lumie-cache.svc.cluster.local', port: 26379 }
],
name: 'mymaster',
role: 'master'
});

// 사용 예시
await redis.set('session:user123', JSON.stringify({
userId: 123,
loginTime: Date.now()
}));

const session = await redis.get('session:user123');
console.log(JSON.parse(session));

모니터링

Prometheus 메트릭

metrics:
enabled: true
serviceMonitor:
enabled: true
namespace: lumie-cache
resources:
requests:
cpu: 10m
memory: 33Mi
limits:
memory: 35Mi

주요 메트릭

메트릭설명
redis_upRedis 인스턴스 상태
redis_connected_clients연결된 클라이언트 수
redis_used_memory_bytes사용 중인 메모리
redis_keyspace_hits_total캐시 히트 수
redis_keyspace_misses_total캐시 미스 수
redis_commands_processed_total처리된 명령어 수

캐시 히트율 계산

# 캐시 히트율 (%)
(
rate(redis_keyspace_hits_total[5m]) /
(rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m]))
) * 100

알림 규칙

groups:
- name: redis.rules
rules:
- alert: RedisDown
expr: redis_up == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Redis instance is down"

- alert: RedisHighMemoryUsage
expr: redis_used_memory_bytes / redis_config_maxmemory * 100 > 90
for: 5m
labels:
severity: warning
annotations:
summary: "Redis memory usage is above 90%"

- alert: RedisLowCacheHitRate
expr: |
(
rate(redis_keyspace_hits_total[5m]) /
(rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m]))
) * 100 < 80
for: 10m
labels:
severity: warning
annotations:
summary: "Redis cache hit rate is below 80%"

운영 가이드

상태 확인

# Pod 상태 확인
kubectl get pods -n lumie-cache -l app.kubernetes.io/name=redis

# 서비스 상태 확인
kubectl get svc -n lumie-cache

# Redis 연결 테스트
kubectl exec -it redis-master-0 -n lumie-cache -- redis-cli ping

Redis CLI 사용

# Master에 연결
kubectl exec -it redis-master-0 -n lumie-cache -- redis-cli

# 기본 명령어
127.0.0.1:6379> INFO replication
127.0.0.1:6379> CLIENT LIST
127.0.0.1:6379> MONITOR
127.0.0.1:6379> KEYS *

Sentinel 상태 확인

# Sentinel에 연결
kubectl exec -it redis-sentinel-0 -n lumie-cache -- redis-cli -p 26379

# Sentinel 명령어
127.0.0.1:26379> SENTINEL masters
127.0.0.1:26379> SENTINEL slaves mymaster
127.0.0.1:26379> SENTINEL sentinels mymaster
127.0.0.1:26379> SENTINEL get-master-addr-by-name mymaster

페일오버 테스트

# Master Pod 삭제 (페일오버 테스트)
kubectl delete pod redis-master-0 -n lumie-cache

# 페일오버 진행 상황 확인
kubectl logs redis-sentinel-0 -n lumie-cache -f

# 새 Master 확인
kubectl exec -it redis-sentinel-0 -n lumie-cache -- \
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster

성능 튜닝

메모리 최적화

# 메모리 사용량 분석
redis-cli --bigkeys
redis-cli --memkeys

# 메모리 정리
redis-cli FLUSHDB
redis-cli MEMORY PURGE

연결 풀 설정

// Go 클라이언트 연결 풀 최적화
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{"redis-sentinel.lumie-cache.svc.cluster.local:26379"},

// 연결 풀 설정
PoolSize: 10, // 최대 연결 수
MinIdleConns: 5, // 최소 유휴 연결 수
MaxRetries: 3, // 재시도 횟수

// 타임아웃 설정
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})

캐시 전략

TTL 설정

# 키별 TTL 설정
SET user:123 "user_data" EX 3600 # 1시간
SET session:abc "session_data" EX 1800 # 30분

메모리 정책

# 메모리 부족 시 정책 (향후 설정)
CONFIG SET maxmemory-policy allkeys-lru

보안

네트워크 보안

  • 클러스터 내부 접근: Redis는 클러스터 내부에서만 접근 가능
  • 인증 비활성화: 내부 네트워크 신뢰 환경에서 성능 우선
  • 네트워크 정책: 필요시 Kubernetes NetworkPolicy로 접근 제한

보안 강화 (향후 구성)

# 인증 활성화 시
auth:
enabled: true
password: "secure_password"
existingSecret: "redis-auth"
existingSecretPasswordKey: "password"

# TLS 암호화
tls:
enabled: true
existingSecret: "redis-tls"

문제 해결

일반적인 문제

1. 연결 실패

# 네트워크 연결 확인
kubectl exec -it redis-master-0 -n lumie-cache -- netstat -tlnp

# DNS 해상도 확인
kubectl exec -it redis-master-0 -n lumie-cache -- nslookup redis-sentinel.lumie-cache.svc.cluster.local

2. 메모리 부족

# 메모리 사용량 확인
kubectl exec -it redis-master-0 -n lumie-cache -- redis-cli INFO memory

# 큰 키 찾기
kubectl exec -it redis-master-0 -n lumie-cache -- redis-cli --bigkeys

3. 복제 지연

# 복제 상태 확인
kubectl exec -it redis-master-0 -n lumie-cache -- redis-cli INFO replication

# 복제 지연 확인
kubectl exec -it redis-replica-0 -n lumie-cache -- redis-cli LASTSAVE

로그 분석

# Redis 로그 확인
kubectl logs redis-master-0 -n lumie-cache

# Sentinel 로그 확인
kubectl logs redis-sentinel-0 -n lumie-cache

# 실시간 로그 모니터링
kubectl logs -f redis-master-0 -n lumie-cache

데이터 패턴

캐시 패턴

Cache-Aside

func GetUser(userID string) (*User, error) {
// 1. 캐시에서 조회
cached, err := rdb.Get(ctx, "user:"+userID).Result()
if err == nil {
var user User
json.Unmarshal([]byte(cached), &user)
return &user, nil
}

// 2. DB에서 조회
user, err := db.GetUser(userID)
if err != nil {
return nil, err
}

// 3. 캐시에 저장
userData, _ := json.Marshal(user)
rdb.Set(ctx, "user:"+userID, userData, time.Hour)

return user, nil
}

Write-Through

func UpdateUser(user *User) error {
// 1. DB 업데이트
err := db.UpdateUser(user)
if err != nil {
return err
}

// 2. 캐시 업데이트
userData, _ := json.Marshal(user)
rdb.Set(ctx, "user:"+user.ID, userData, time.Hour)

return nil
}

세션 관리

// 세션 저장
func SaveSession(sessionID string, data SessionData) error {
sessionJSON, _ := json.Marshal(data)
return rdb.Set(ctx, "session:"+sessionID, sessionJSON, 30*time.Minute).Err()
}

// 세션 조회
func GetSession(sessionID string) (*SessionData, error) {
val, err := rdb.Get(ctx, "session:"+sessionID).Result()
if err != nil {
return nil, err
}

var data SessionData
err = json.Unmarshal([]byte(val), &data)
return &data, err
}

// 세션 연장
func ExtendSession(sessionID string) error {
return rdb.Expire(ctx, "session:"+sessionID, 30*time.Minute).Err()
}

고급 기능

Pub/Sub 메시징

// Publisher
func PublishMessage(channel, message string) error {
return rdb.Publish(ctx, channel, message).Err()
}

// Subscriber
func SubscribeToChannel(channel string) {
pubsub := rdb.Subscribe(ctx, channel)
defer pubsub.Close()

ch := pubsub.Channel()
for msg := range ch {
fmt.Printf("Received: %s from %s\n", msg.Payload, msg.Channel)
}
}

Lua 스크립트

// 원자적 증가 스크립트
var incrScript = redis.NewScript(`
local key = KEYS[1]
local increment = ARGV[1]
local max = ARGV[2]

local current = redis.call('GET', key) or 0
current = tonumber(current)

if current + tonumber(increment) <= tonumber(max) then
return redis.call('INCRBY', key, increment)
else
return current
end
`)

// 사용 예시
result, err := incrScript.Run(ctx, rdb, []string{"counter:api_calls"}, 1, 1000).Result()

이 문서는 Redis의 설치, 구성, 운영에 대한 포괄적인 가이드를 제공합니다. 고가용성 캐시 환경 구축과 성능 최적화에 대한 추가 정보는 Redis 공식 문서를 참조하세요.