소통하는 개발자 Sean
article thumbnail

쿠버네티스(Kubernetes) 환경에서 스프링부트(Spring Boot) 애플리케이션을 배포하고, Pod을 2개로 설정했을 때 로그인 문제가 발생했습니다. 로그인을 해도 다시 로그인을 하게 되는 상황이였는데요, 스프링 시큐리티(Spring Security)를 사용할 때, 세션 정보가 한 Pod에만 저장되어 있기 때문에 로드 밸런싱으로 인해 사용자 요청이 다른 Pod로 전송될 때 세션 정보를 찾을 수 없어 로그인 문제가 발생한것으로 보입니다.

 

스프링 세션 + Redis를 사용하여 문제를 해결해보도록 하겠습니다.

 

 

Redis를 사용하여 세션을 공유하는 과정을 단계별로 설명하겠습니다. 이 과정은 스프링 부트와 스프링 세션(Spring Session) 라이브러리를 사용하여, 세션 정보를 중앙 집중식으로 관리하는 Redis 저장소에 저장함으로써, 여러 스프링 부트 인스턴스(또는 쿠버네티스 상의 Pod)가 같은 세션 정보를 공유할 수 있게 해줍니다.

 

 

 

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.session:spring-session-data-redis'
    implementation 'org.springframework.data:spring-data-redis'
    runtimeOnly 'redis.clients:jedis'
}

 

 

application.properties 또는 application.yml 파일에 Redis 서버의 호스트 이름과 포트 번호를 설정합니다. 이 설정을 통해 스프링 애플리케이션은 Redis 서버에 연결할 수 있습니다.

(session-redis 관련 yaml 파일 내용은 밑에 올리겠습니다)

spring.redis.host=session-redis
spring.redis.port=6379

 

 

스프링 세션을 활성화하기 위해 @EnableRedisHttpSession 애노테이션을 추가합니다. 이 애노테이션은 스프링 세션의 구성을 활성화하고, 세션을 Redis에 저장하도록 스프링 부트 애플리케이션을 구성합니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

@Configuration
@EnableRedisHttpSession
public class RedisConfig {
}

 

스프링 시큐리티(Spring Security) 설정에서는 특별한 변경이 필요 없습니다.

이렇게 설정해주면 잘 동작할까요??

 

오류발생!!

org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.io.NotSerializableException: com.sjs.jsvill.entity.Member

 

java.io.NotSerializableException: com.sjs.jsvill.entity.Member라는 예외가 발생했습니다. 이 오류는 com.sjs.jsvill.entity.Member 클래스의 인스턴스를 Redis에 저장하려고 할 때, 해당 객체가 Java 직렬화 프로토콜을 따르지 않기 때문에 발생합니다. Redis에 객체를 저장하기 위해서는 해당 객체가 Serializable 인터페이스를 구현해야 합니다.

 

 

**serialVersionUID**는 직렬화된 객체의 버전 관리에 사용됩니다. 클래스가 Serializable 인터페이스를 구현한다면, 이 필드를 선언하는 것이 좋습니다. 클래스의 구조가 변경되어도, 같은 **serialVersionUID**를 가진 클래스로의 역직렬화가 가능합니다.

 

 

spring.redis.host=session-redis 설정처럼 구현하기 위해서는

Redis를 쿠버네티스 클러스터 내부에서 실행하려면, Redis 서버를 별도의 Pod로 배포하고, 이 Pod에 대한 서비스를 생성해야 합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: session-redis
spec:
  replicas: 1
  selector:
    matchLabels:
      app: session-redis
  template:
    metadata:
      labels:
        app: session-redis
    spec:
      containers:
        - name: session-redis
          image: redis:alpine
          ports:
            - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: session-redis
spec:
  ports:
    - port: 6379
      targetPort: 6379
  selector:
    app: session-redis

 

 

작동 원리

  • 사용자가 스프링 부트 애플리케이션에 로그인하면, 스프링 세션은 세션 정보를 Redis에 저장합니다.
  • 사용자의 브라우저는 쿠키를 통해 세션 ID를 저장하고 이를 사용하여 요청을 인증합니다.
  • 사용자 요청이 로드 밸런서를 통해 어떤 인스턴스로든 전달될 때, 인스턴스는 세션 ID를 통해 Redis에서 해당 세션 정보를 조회합니다.
  • 이 방식을 통해, 여러 인스턴스 간에 세션 정보가 공유되므로, 사용자는 어느 인스턴스로 요청을 보내도 로그인 상태를 유지할 수 있습니다.

 

쿠버네티스 환경에서 여러 Pod가 하나의 Redis 세션을 참조하는 구조를 완성했습니다!

 

 

 

잘 되는지 확인해보기 위해 간단하게 redis-session pod의 로그를 살펴보겠습니다.

'> kubectl logs pod/session-redis-765dd67477-vskcz'

Redis가 정상적으로 작동하고 있으며, 주기적으로 데이터를 디스크에 저장하고 있는 상태임을 보여줍니다.

 

 

profile

소통하는 개발자 Sean

@Sean-creative

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!