[운송장 정보보호 서비스] EKS-redis 연동(feat. 백엔드 설정)

신현식·2023년 6월 1일
0

캡스톤디자인

목록 보기
6/7
post-thumbnail

이번에는 redis용 파드를 exqress-cluster에 띄운 다음에 도커라이징한 백엔드 파드들과 통신하도록 설계하여 redis를 사용할 수 있도록 해볼 것이다.

  • 현재 프로젝트에서 refresh 토큰(스프링 JWT), 휘발성 데이터, 로그아웃 처리 등을 효율적으로 관리하기 위해 Redis 서버를 구축하게 되었다.
    로컬환경에서는 백엔드 스프링과 잘 연동이 되지만 배포 환경에서는 계속 연결이 되지 않아 이유를 찾아보니, redis 관련 설정 파일을 작성하지 않으면 기본값으로 local주소만 허용한다는 것을 알게 되었다.

로컬환경 실행

먼저 기본적인 redis 서버 실행이다. 로컬환경에서 도커를 실행시킨 이후 다음 명령을 통해 redis 이미지를 사용하는 컨테이너를 생성하면 redis 서버를 간편하게 실행할 수 있다.

docker pull redis:latest
docker run -{options} {이미지 이름}

  • 3번째 라인에 , Warning: no config file specified, using the default config. 라는 로그가 보인다.
    이는 별도의 설정파일 없이 서버를 구동했을 때 bind 옵션(허용주소)의 기본값이 로컬주소인 127.0.0.1로 설정되어 실행되기 때문에 외부에서는 레디스 서버에 접근할 수 없다는 것을 의미한다.

Redis 서버의 외부접근 허용

우선 외부에서도 접근 가능하도록 bind를 수정해 줄 것이다. 따라서 하나의 디렉터리에 redis 설정파일을 만든다.

  • redis 설정파일 생성
vi redis.conf

bind 0.0.0.0

모든 IP주소를 허용하기 위해 0.0.0.0으로 설정했고, 포트는 그대로 6379를 사용할 것이기 때문에 따로 설정은 안해주었다.

  • Dockerfile 생성
FROM redis:alpine
COPY ./redis.conf /usr/local/etc/redis/redis.conf
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf"]

만든 설정파일을 적용시킨 이후 redis를 실행시키는 이미지를 생성한다. 우리는 현재 AWS ECR를 사용하고 있기 때문에 생성한 redis 이미지 또한 새롭게 프라이빗 저장소를 만들어 저장시킨다.

EKS Cluster 상에 redis 파드 생성

생성한 이미지를 가지고 redis용 파드를 만들었다. 또한 파드와 연결해주는 서비스를 생성하여 배포된 eks cluster 내부에서 백엔드 파드와 서로 통신 할수 있도록 해주었다.

apiVersion: v1
kind: Service
metadata:
  name: my-redis
  labels:
    app: counter
spec:
  selector:
    app: counter
  ports:
    - name: redis
      protocol: TCP
      port: 6379
      targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-depoly
spec:
  replicas: 1
  selector:
    matchLabels:
      app: counter
  template:
    metadata:
      labels:
        app: counter
    spec:
      containers:
        - name: redis-container
          image: <사용자 ID>.dkr.ecr.ap-northeast-2.amazonaws.com/redis:latest
          env:
            - name: MASTER
              value: "true"
          ports:
            - containerPort: 6379

백엔드 파드와 연결 문제 발생

스프링 백엔드에서 redis를 사용하기 위해 기존 로컬 개발환경에서 redis와 연결해준 방법은 스프링 설정파일에 redis 이미지를 사용하여 띄운 컨테이너의 주소를 입력해주면 자동으로 연결이 되는 방식이였다.

/src/main/resources/application.yml
...
spring:
  application:
    name: capstone
  redis:
    lettuce:
      pool:
        max-active: 5 # pool에 할당될 수 있는 커넥션 최대수
        max-idle: 5 # pool의 'idle' 커넥션 최대수
        min-idle: 2
    port: 6379
    host: 172.19.0.2 # redis 컨테이너 IP
...   
  • 따라서 배포 환경에서는 host에 eks 클러스터 내부에서 통신하는 것이기 때문에 redis 서비스의 도메인 네임을 입력하면 백엔드 파드와 redis 파드가 잘 통신이 될 것이라는 생각이였다.
    redis 서비스 도메인의 형식은 <서비스명>.<네임스페이스>.svc.cluster.local 이다.
    📢 서비스 및 파드용 DNS
spring:
  application:
    name: capstone
  redis:
    ...
    port: 6379
    host: http://my-redis.default.svc.cluster.local

하지만 실패하였다.
Request processing failed: org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis 에러 메시지가 나오면서 redis와 연결이 되지 않는다는 것이다.

해결책

우선 내부적으로 파드끼리 통신이 불가능한 것이라고 생각하여 redis 서비스에서 외부 로드밸런서를 생성하여 redis 파드에 접속이 가능하도록 하였다.

apiVersion: v1
kind: Service
metadata:
  name: my-redis
  labels:
    app: counter
spec:
  selector:
    app: counter
  type: LoadBalancer
  ports:
    - name: redis
      protocol: TCP
      port: 6379
      targetPort: 6379

이또한 처음에는 접근에 실패헀었는데 이는 백엔드에서 주소설정할때의 문제였다. 현재 redis 서비스의 로드밸런스 주소는
IP 주소와 TCP 포트를 기반으로 클라이언트와 백엔드 서버 간의 트래픽을 라우팅하는데 host 주소를 <redis 로드밸런스 주소>/ 로 뒤에 api까지 생각해서 주소를 적었는데 이는 현재 L4 layer에서 인식할 수 있는 것이 아니기 때문에 도메인을 파싱하지 못하여 연결 에러가 났었다. 따라서 뒤에 /를 제거하고 나니 redis 파드와 통신이 가능하였다.

해결책: EKS Cluster 내부 통신

위에 방법으로도 해결을 하였지만 사실 좀 이상한 해결방식이라고 볼 수 있다. redis 파드와 백엔드 파드 모두 eks cluster 내부에서 작동하고 있기 때문에 내부 통신을 이용하여 서로 통신을 할 수 있을 것이다. 하지만 위에 방식은 백엔드 파드가 외부에 노출된 redis 파드의 주소를 보고 다시 eks cluster에 들어와 redis 파드와 통신을 하고 있는 형태이기 때문이다. 따라서 내부에서 직접 통신 할 수 있는 해결책을 제시하고자 한다.

💡 쿠버네티스 서비스 프록시의 역할

kube-proxy는 클라이언트가 쿠버네티스 API로 정의한 서비스에 연결할 수 있도록 해주는 역할을 한다. 그리하여 서비스의 IP와 포트로 들어온 접속을 서비스를 지원하는 파드 중 하나와 연결시켜 준다.

💡 DNS 서버 동작 방식

클러스터의 모든 파드는 기본적으로 클러스터의 내부 DNS 서버를 사용하도록 설정돼 있다. 따라서 파드는 서비스를 이름으로 쉽게 찾을 수 있고 헤드리스 서비스 파드인 경우 해당 파드의 IP 주소를 찾을 수 있다.
DNS 서버 파드는 kube-dns 서비스로 노출되므로, 해당 파드를 다른 파드와 마찬가지로 클러스터 안에서 이동할 수 있다. 해당 서비스의 IP 주소는 클러스터에 배포된 모든 컨테이너가 가지고 있는 /etc/resolv.conf 파일 안에 nameserver로 지정돼 있다.
coredns 파드는 API 서버 감시 메커니즘을 이용하여 서비스와 엔드포인트 변화를 관찰하고 모든 변화를 DNS 레코드에 갱신한다.

결론부터 이야기 하자면 맨 처음 문제를 제기했엇던 http://my-redis.default.svc.cluster.local 이 주소 형식때문에 문제가 발생했던 것이다.

  • redis 도메인 주소는 <서비스명>.<네임스페이스>.svc.cluster.local인데 백엔드 상에서 주소를 입력할때 http://<도메인 주소>의 형태로 입력했던 것이다.

쿠버네티스 내부에서 통신을 진행하는데 http 프로토콜을 사용해서 통신하려 했기 때문에 에러가 나온 것이다. 따라서 http 통신을 사용하지 않고 쿠버네티스 내부의 DNS 서버를 통해 서비스를 찾아갈 수 있도록 my-redis.default.svc.cluster.local 주소를 host로 써주니 내부에서 잘 통신하는 것을 확인 할 수 있었다.

spring:
  application:
    name: capstone
  redis:
    ...
    port: 6379
    host: my-redis.default.svc.cluster.local
profile
전공 소개

0개의 댓글