Spring Eureka를 이용한 서버 IP 이동 문제 해결.

hynnch2·2022년 10월 20일
0
post-thumbnail

안녕하세요. 취직을 해서 사이드 프로젝트 개발에 시간을 가지지 못하였고, 그러다보니 프로젝트에서 얻은 지식을 공유할 기회가 없어서 한동안 블로그 글을 작성하지 않았던 것 같습니다.

이번에 마주친 문제는 계속해서 바뀌는 IP 값을 해결하기 위한 방법입니다.

📉 문제 발생.

한동안 신경쓰지 못한 프로젝트에서 어느순간 정상적으로 동작하지 않는다는 것을 인지하였고, 문제를 찾아본 결과 외부 DB의 IP 값이 변경되면서 정상적으로 Backend 서버가 동작하지 않는 것이었습니다.

여기서 IP가 바뀐 것에 의아해 할 수 있을텐데, 현재 제 서비스는 모 회사의 컨테이너 서비스를 이용하여 DB와 백엔드를 구성해 놓은 상태이며 IP 고정 기능을 통해 IP를 고정시켜두고 연결한 상태였습니다.

정상적인 상황이라면 IP가 바뀌지 않아야 하지만, 회사 사정에 따라 몇 번 바뀌다 보니 처리를 해두는 것이 좋다고 생각했고 이를 해결하기 위한 방법을 찾아보기 시작했습니다.

문제: DB 서버의 IP가 계속 바뀌기 떄문에, IP 값을 하드코딩으로 주입할 수 없게 되었음.

🤔 해결법 찾기.

이 문제를 해결하기 위해 몇 가지 방법을 생각했습니다.

1. 쿠버네티스를 이용해서 클러스터링 하기.

서버가 어떻게 동작할 지 모른다면, 여러 컨테이너를 쿠버네티스로 클러스터링하여 DB서버와 Backend서버가 선언적으로 구성되도록 설계하는 방법을 먼저 떠올렸습니다.

추가로 들어가는 작업은, 컨테이너 여러개를 쿠버네티스로 연결하는 작업, 데이터 영구 저장을 위해 PV(Persistence Volume) 설정, 서버와 DB 이미지 생성 정도가 있었습니다.

추후 다른 서비스를 간단하게 가져갈 수 있기 때문에, k8s를 선언해서 구성해놓으면 편할 것이라 생각했고, 작업을 시작했습니다.

하지만, 안타깝게도 컨테이너 제공 회사에서 이미 도커를 통해 서비스 하고 있었기 때문에, Docker를 설치하면 도커 인 도커 상태로 보안 등의 정책으로 인해 도커를 사용할 수 없었습니다.

2. AWS RDS 서비스 이용.

가장 쉽고 빠르게 DB 서버를 생성할 수 있으며, 서버 IP가 변경되는 문제도 해결 할 수 있었습니다. 물론, 지금 당장 서비스가 운영중이며, 크리티컬한 부분이라면 돈을 내고 RDS 서비스를 이용하는 것이 맞을 것입니다.

하지만, 사이드 프로젝트에서 빠르게 고쳐야 하는 이슈가 아니었습니다. 또한, DB 서버가 아닌 다른 서비스를 만들고 연결해야 할 때 AWS EC2를 생성해야 한다면, 많은 돈이 들어갈 것이라 생각했습니다.

즉, 충분한 여유시간, DB 뿐만 아니라 다른 서비스의 연동을 생각한다면 AWS 서비스가 아닌 다른 방법으로 해결하고 싶었습니다.

3. DB server를 만들고, Spring Eureka 서비스 이용.

이를 해결하기 위해 바뀐 IP 주소를 Client Server에 전달할 수 있는 방법을 찾아보다가 비슷한 방법인 Spring Eureka 서비스를 알게 되었습니다.

Spring Eureka는 MSA 환경에서 다수의 서비스의 로드밸런싱을 수행하거나, 장애 조치를 위해 사용된다고 되어 있었습니다. 그 과정에서 다수의 서비스는 Eureka 서비스에 자신의 IP를 등록하고, 서비스가 다운되거나 장애가 발생하면 정상적인 서비스로 로드밸런싱을 수행하는 역할을 할 수 있었습니다.

제가 마주한 상황에서 사용할 수 있을 것이라 생각했고, 이를 위해 테스트 서버를 만들어서 동작을 직접 테스트 했습니다.


테스트 서버 구성

1.Eureka Server

EurekaServerApplication.kt

@SpringBootApplication
@EnableEurekaServer
class EurekaServerApplication

fun main(args: Array<String>) {
	runApplication<EurekaServerApplication>(*args)
}

application.yaml

server:
  port: 8761

eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
  server:
    waitTimeInMsWhenSyncEmpty: 5

2. DB Server

EurekaDbApplication.kt

@SpringBootApplication
class EurekaDbApplication

fun main(args: Array<String>) {
	runApplication<EurekaDbApplication>(*args)
}

application.yaml

server:
  port: 8082

spring:
  application:
    name: dbservice

eureka:
  instance:
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://127.0.0.1:8761/eureka/

3. Client Server

EurekaClientApplication.kt

@SpringBootApplication
@EnableFeignClients
class EurekaClientApplication

fun main(args: Array<String>) {
	runApplication<EurekaClientApplication>(*args)
}

application.yaml

server:
  port: 8081

spring:
  application:
    name: clientservice

eureka:
  instance:
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://127.0.0.1:8761/eureka/
  • Client Server에서 Spring FeignClient를 사용해서 쉽게 요청 보낼 수 있음.
  • Spring Cloud 진영 뿐만 아니라, Url을 입력해서 RestTemplate보다 가볍게 쓸 수 있음.

위의 서버 셋팅을 통해 EurekaServer에 등록된 것을 확인할 수 있습니다.

서비스 코드 작성.

테스트를 하기 위해 Client Server에서는 DB Server에 요청을 보내야 합니다. 이 때, DB Server의 정보는 Eureka에서 받아오도록 작성할 수 있습니다.

(Client Server)
UserInterface

@FeignClient(name="dbservice")
interface UserInterface {

    @GetMapping("getUserDetail")
    fun getUser(): User
}
  • FeignClient에 Eureka에 등록한 dbservice 를 사용합니다.

TestController

@RestController
class TestController(
    val userInterface: UserInterface,
) {

    @GetMapping("getUser")
    fun getUser(): User {
        return userInterface.getUser().apply { println(this) }
    }
}

(DB Sever)
UserController

@RestController
class UserController {

    @GetMapping("getUserDetail")
    fun getUser(): User {
        return User(1, 25, "qf9ar8nv")
    }
}

이제 Client Server는 DB Server에 원하는 데이터를 요청해서 받아 올 수 있습니다.

DB 서버를 끄고 Port를 변경해도 Client Server에서 정상적으로 동작하는 것을 확인할 수 있습니다.

이제 기존 CRUD 작업을 DB Server에서 작업하고, Client Server에서는 받아온 데이터를 가공하는 형태로 작업하면 될 것 같습니다.


✏️ 마무리.

이렇게 만들고 나니 생기는 추가로 생기는 문제점도 있었습니다.

  1. String으로 받지 않는 이상 Domain 규격을 동일하게 가져갈 수 있는 방법이 필요.
  2. 서버 사이에 하나의 커넥션이 생김. -> 속도, 보안, 에러 등 추가적인 공수 필요.
  3. 간단한 코드 추가여도 두 프로젝트를 수정해야 할 수 있음. 불필요한 작업 추가.

물론 위의 방법이 최고의 선택이라고 생각하지 않습니다. 하지만, 제가 가장 해결하고 싶었던 문제를 푸는 한가지 방법이었다고 소개할 수 있을 것 같습니다.

더 나은 아키텍쳐 및 설계가 있다면 과감하게 팩트로 때려주시면 감사하겠습니다. :)

profile
more than yesterday

0개의 댓글