[Project] Spring Cloud + Kubernetes

mDev_97·2023년 12월 18일

Goormthon-Project

목록 보기
2/4
post-thumbnail

프로젝트에 MSA를 도입하기 위해서 이전 게시글에서 Spring Cloud와 Kubernetes에 대한
비교를 간단하게나마 진행하였다.

이전 게시글에 비교했듯이 Spring Cloud와 Kubernetes는 각각 특정 영역에서의 강점을 가지고,
다른 부분에서는 개선해야할 점들이 존재한다.

우리 팀은 위처럼 두 플랫폼의 강점과 약점을 비교하고, 아래의 기준으로 프로젝트에 MSA를 도입하고자 한다.

1. 코드 변경 없이 애플리케이션을 쿠버네티스에 적용

이전 게시글에서 말한 것과 같이 우리 팀은 프로젝트 구상 단계에서 막연하게 MSA를 도입한다면
Spring Cloud를 통해서 코드를 작성하고, 작성한 코드를 Kubernetes에 배포하기만 하면 된다고 생각하였다.
하지만, Spring Cloud와 Kubernetes의 차이로 개발 과정과 배포 과정의 코드가 변경된다는 것을 알았다.

두 플랫폼의 차이가 있더라도, 개발 과정과 배포 과정의 코드가 다르면 무슨 의미가 있을까?
또한, 쿠버네티스를 도입함으로써 코드가 변경된다는 것은 코드가 쿠버네티스에 의존적이라는 것을 의미한다.
이에 우리 팀은 코드 변경이 없이 애플리케이션을 쿠버네티스에 배포하는 것을 목표로
Spring Cloud와 Kubernetes를 통합하여 사용하고자 한다.

2. 쿠버네티스에서 제공하는 기능들을 적극 활용

Spring Cloud와 Kubernetes를 비교하는 스택오버플로우나 깃허브 등 개발자 커뮤니티를 보면
"어차피 쿠버네티스에 배포하고 사용할 것이라면, 뭐하러 스프링 클라우드를 사용하느냐?,
쿠버네티스에서 제공하는 기능을 활용해라" 라는 내용을 쉽게 볼 수 있다.

그 이유는 쿠버네티스가 스프링 클라우드보다 MSA를 도입하는 과정에서 발생할 수 있는 문제들을
좀 더 광범위하게 해결할 수 있는 방법을 제공하기 때문이다.

이에 우리 팀은 쿠버네티스에서 제공하는 기능들을 적극 활용하여 프로젝트에 MSA를 도입하고자 한다.

Dependency(디펜던시) 조사

Spring Cloud와 Kubernetes를 함께 사용하기 위해서 디펜던시 조사를 하였다.

API Gateway

쿠버네티스에서는 L7 레이어에서의 라우팅이나 API Gateway로써의 기능을 Ingress라는 리소스를 통해서 제공한다.
이러한 점에서 Spring Cloud API Gateway를 Ingress로 대처할 수 있는지 고려하였다.

구글에 검색해보면 Spring Cloud API Gateway 대신에 Ingress를 사용하라는 내용을 많다.
하지만, 우리 프로젝트에서는 API Gateway에 JWT 인증 기능을 위임하고,
API Gateway는 Ingress와는 다르게 Java 언어를 사용하여 Custom Filter와 같이
우리가 원하는 기능들을 작성할 수 있기 때문에 Spring Cloud API Gateway를 사용하기로 하였다.

정리하자면, 우리 프로젝트에서는 Ingress 대신에 Spring Cloud API Gateway를 사용하면 다음과 같은 장점들이 있다.

  • Spring Framework 기반으로 동작
  • Host와 Path 기반의 L7 레이어에서의 라우팅
  • Custom Filter 구현이 가능
  • Path Rewrite 기능 사용 가능

내부 통신

내부 통신의 경우 Spring Cloud의 OpenFeign을 사용하였다.
OpenFeign 자체는 코드 레벨이기 때문에 플랫폼 레벨인 쿠버네티스를 적용하는 데에 있어서
별도로 고려하지는 않았다.

조금 더 자세하게 말하자면, OpenFeign은 어노테이션을 기반으로 작성되는 선언적 REST Client이다.

@FeignClient("post-service")
interface PostFeignClient {
    @GetMapping(value = ["/post/{postNum}])
    Post getPostByNum(@PathVariable Long postNum);
}

이 때, @FeignClient 안에 사용되는 post-service 라는 값은 FeignClient가 사용할
로드밸런서 클라이언트를 만들기 위해서 필요한 값이다.
이 로드밸런서 클라이언트가 로드밸런싱할 엔트포인트는 DiscoveryClient가 찾아준다.

서비스 디스커버리 & 레지스트리

서비스 디스커버리와 레지스트리는 Spring Cloud에서는 Eureka를 활용한 방식을 사용했다.
반면, 쿠버네티스에서는 서비스 디스커버리와 로드밸런서의 기능을 Service라는 리소스가 제공을 한다.
이러한 Service 리소스를 사용하면 쿠버네티스 클러스터 내에 있는
모든 Pod들은 서비스에 접근할 수 있도록 쿠버네티스가 별도의 DNS를 제공한다.

ex) post-service.default.svc.cluster.local

이렇게 쿠버네티스에서 제공하는 DNS를 사용하려면 @FeignClient의 url을 사용하여 다음과 같이 작성할 수 있다.

@FeignClient(url = "post-service.default.svc.cluster.local")
interface PostFeignClient {
    @GetMapping(value = ["/post/{postNum}])
    Post getPostByNum(@PathVariable Long postNum);
}

하지만, 이렇게 되면 쿠버네티스를 도입함에 따라 코드가 변경이 되고,
코드가 쿠버네티스에 의존적이라는 문제가 발생하게 된다.
또한, 기존의 로컬에서도 작동하던 코드들이 동작하지 않는다는 문제가 있다.

이에 쿠버네티스에 적용할 때, 쿠버네티스의 서비스를 활용한 방식으로 전환할 수 있는지 고려하였다.

프로퍼티 파일 관리

프로퍼티 파일 관리의 경우, Spring Cloud Config Server를 두어서 외부로 관리할 수 있도록 하였다.
반면, 쿠버네티스에서는 애플리케이션에서 설정 파일들을 보통 ConfigMap 또는 Secret 이라는 리소스를 사용하여 프로퍼티를 관리한다.
이에 쿠버네티스를 적용한다면, ConfigMap과 Secret을 통해서 프로퍼티 파일을 관리할 수 있는 방법을 고려하였고,
우리 프로젝트에는 ConfigMap에 application.yml과 같은 프로퍼티 파일을 넣어 볼륨 마운트할 수 있도록 함으로써 MSA 요건에 맞게 각각의 서비스가 Stateless 하게 유지될 수 있는 방안에 대해서 생각하였다.

하지만, ConfigMap과 Secret만을 사용하여 유지한다면, 로컬 환경에서 동작시키기 어렵다는 문제를 직면한다.
이에 Local 환경에서는 Spring Cloud Config Server를 통해서 동작할 수 있도록 하였다.

그렇다면 간편하게 Config Server도 쿠버네티스에 함께 배포하면 될 것이라고 생각하였지만,
Config Server를 함께 배포하는 것은 서버 리소스와 비용적인 측면에서 비효율적이다.

이에 Local과 Kubernete로 Profile을 구별하여 관리함으로써, Local 환경에서는 Spring Cloud Config Server를 사용하고, Kubernetes에서는 ConfigMap과 Secret을 사용하는 방식을 채택하였다.

DB, Kafka, Redis 등

MySQL과 같은 DB나 Kafka, Redis 등이 현재 우리 프로젝트에서 적용 중에 있다.
현재로서는 이러한 요소들이 별도의 서버를 유지하는 것이 아니라,
로컬 환경에서 개별적으로 테스트가 진행되고 있기 때문에, 쿠버네티스를 적용할 때에는
Headless(헤드리스) 서비스로 배포하는 것을 고려하였다.

결론

앞서 진행한 고민들을 기반으로 하여 결정된 내용을 아래의 표와 같다.

profile
안녕하세요. 백엔드, 클라우드, 인프라에 관심과 열정이 있는 김문성입니다. 😊

0개의 댓글