이번에는 redis용 파드를 exqress-cluster에 띄운 다음에 도커라이징한 백엔드 파드들과 통신하도록 설계하여 redis를 사용할 수 있도록 해볼 것이다.
먼저 기본적인 redis 서버 실행이다. 로컬환경에서 도커를 실행시킨 이후 다음 명령을 통해 redis 이미지를 사용하는 컨테이너를 생성하면 redis 서버를 간편하게 실행할 수 있다.
docker pull redis:latest
docker run -{options} {이미지 이름}
우선 외부에서도 접근 가능하도록 bind를 수정해 줄 것이다. 따라서 하나의 디렉터리에 redis 설정파일을 만든다.
vi redis.conf
bind 0.0.0.0
모든 IP주소를 허용하기 위해 0.0.0.0으로 설정했고, 포트는 그대로 6379를 사용할 것이기 때문에 따로 설정은 안해주었다.
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 이미지 또한 새롭게 프라이빗 저장소를 만들어 저장시킨다.
생성한 이미지를 가지고 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
...
<서비스명>.<네임스페이스>.svc.cluster.local
이다.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 파드와 통신이 가능하였다.
위에 방법으로도 해결을 하였지만 사실 좀 이상한 해결방식이라고 볼 수 있다. 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
이 주소 형식때문에 문제가 발생했던 것이다.쿠버네티스 내부에서 통신을 진행하는데 http 프로토콜을 사용해서 통신하려 했기 때문에 에러가 나온 것이다. 따라서 http 통신을 사용하지 않고 쿠버네티스 내부의 DNS 서버를 통해 서비스를 찾아갈 수 있도록 my-redis.default.svc.cluster.local
주소를 host로 써주니 내부에서 잘 통신하는 것을 확인 할 수 있었다.
spring:
application:
name: capstone
redis:
...
port: 6379
host: my-redis.default.svc.cluster.local