티스토리 뷰

트러블 슈팅 : Eureka AWS 환경에서 사용하기


# Overview

MSA 프로젝트를 진행하며 Spring Cloud를 이용하여 Gateway를 구성하여 클라이언트의 모든 요청이 Gateway로 향할수 있게 하였습니다. 또한 라우터를 구현하여 각 요청이 각 서비스를 찾아 갈 수 있도록 하였습니다. 이때 로드밸런싱을 구현하고 싶어 Eureka를 사용하였습니다.

 

# 문제점 

localhost환경에서 각 마이크로 서비스를 Eureka에 등록을 하고 로드밸런싱이 잘 되는 것을 확인하였지만 docker-compose를 이용하여 배포를 하니 서비스 자체를 찾지 못하는 에러가 있었습니다. .

 

# Notion - 트러블 슈팅
노션에 있는 트러블 슈팅 페이지를 참고하면 보기 편리해요!

https://star-fighter-924.notion.site/2401fe46efc94286bb912db5957d61ad

 

 

 


개요

MSA 프로젝트를 진행하며 Spring Cloud를 이용하여 Gateway를 구성하여 클라이언트의 모든 요청이 Gateway로 향할수 있게 하였습니다. 또한 라우터를 구현하여 각 요청이 각 서비스를 찾아 갈 수 있도록 하였습니다. 이때 로드밸런싱을 구현하고 싶어 Eureka를 사용하였습니다.

 

# Eureka 대시보드에 보면 IP주소가 아닌 랜덤 hostname이 들어왔다.

# (게이트웨이) UnknownHostException이 발생하였다.

# 에러

java.net.UnknownHostException: failed to resolve 'DESKTOP-KD56ARJ' after 4 queries at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1013) ~[netty-resolver-dns-4.1.63.Final.jar:4.1.63.Final] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): |_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain] |_ checkpoint ⇢ HTTP GET "/first-service/welcome" [ExceptionHandlingWebHandler] Stack trace:

참고 : https://www.inflearn.com/course/스프링-클라우드-마이크로서비스/lecture/68423?tab=community&q=209157

 

 

# Eureka를 사용하지 않고 해결법

당연한 소리지만 Gateway에서 마이크로서비스 IP주소를 넣으면 당연히 바로 서비스를 찾을 수 있습니다!

하지만! 제가 원하는 것은 로드밸런서와 Application 네임을 통해 서비스를 찾으려고 합니다. 때문에 Eureka를 사용하였습니다.

 


 

에러의 원인

Q. 이렇게 로컬에서는 잘 되지만 도커 컴포즈로 실행하면 에러가 나는 이유는 뭘까?

A. Eureka는 기본적인 설정을 해주지 않으면 hostname을 기반으로 마이크로 서비스를 등록하고 검색합니다.

 

아마 다들 인프런이나 구글링을 통해서 간단한 코드를 작성하셨다면 대부분의 application.yml을 이렇게 작성하였을 것입니다.

 

1. application.yml (마이크로 서비스)

 

2. Eureka 대시보드

 

# 여기서 생각!

Q. AWS EC2 하나의 인스턴스에 Gateway, Eureka, UserService를 배포하고 싶은데 어떻게 해야 될까?

A. 저는 우선 MSA를 떠나 AWS 하나의 인스턴스에서 도커로 실행을 하여도 잘 돌아가는지 확인하고 싶었습니다. 하지만 당연히 Gateway는 마이크로 서비스를 찾지 못했고, 그 이유는 위에서 설명한 것과 같이 Eureka는 특별한 설정을 하지 않으면 IP가 아닌 hostname을 기반으로 서비스를 탐색하기 때문입니다.

 

 

# 하나의 인스턴스에서 Eureka 사용법!

eureka:
	instance:
		prefer-ip-address: true

위 명령어를 Gateway, Eureka, UserService에 붙여 준다면 하나의 인스턴스 안에서 통신이 잘 됩니다.

 

Q. 왜 되는 걸까?

A. 해당 코드를 작성하게 되면 서비스간 통신 시 hostname 보다 IP 를 우선으로 사용하게 되기 때문입니다.

 

오류해결전

 

오류해결후

 

 

# 질문! 

Q. 하나의 AWS에 올려서 인스턴스 IP가 같다고는 하지만 docker로 실행했기 때문에 내부 IP 주소가 다르지 않나?

A. 이미지를 도커로 실행시켜 도커내부 이미지 주소를 확인해 보았다.

 

prefer-ip-address: true 는 도커 내부가 아닌 AWS 주소를 사용하는 건가?

 

docker inspect <컨테이너 ID> | grep IPAddress #도커내부 주소 확인

 


 

궁극적인 문제! 

Q. MSA는 여러 인스턴스에 마이크로 서비스가 분산되어 있는데?

A. 우선 Eureka, Gateway, 마이크로 서비스는 하나의 서버에 두는 것은 좋지 못한 방법입니다. Scale Out을 하는 이유는 서버 증설도 있지만 서버가 다운이 되었을때 다른 서버가 있어 서비스 중단을 주지 않기 때문입니다. 즉 유레카와 게이트웨이, 마이크로서비스를 하나의 인스턴스(서버)에 올려 실행 하였다가 에러로 인핸 서버가 중단되면 모든 서비스가 중단되기 때문입니다. 따라서 유레카와 게이트웨이도 도커로 실행하는 것 보다 java -jar로 직접 실행하는 것이 맞다고 들었습니다.

 

 

하나의 인스턴스에서 사용한 방법으로 하면 여러 인스턴스에서 잘 되나?

절.대.안.된.다.

 

 

Q. 안되는 이유가 뭘까?

docker-compose로 실행을 하면 도커의 내부 IP주소를 사용한다. (하나의 인스턴스는 뭐지..?)

즉, 각 도커내부의 IP주소는 유레카가 찾지를 못한다. 그렇기 때문에 도커내부의 IP주소 대신 AWS IP주소를 사용할 수 있게 해주어야 한다.

 

먼저 도커 내부 IP주소를 확인해 보자

docker inspect <컨테이너 ID> | grep IPAddress #도커내부 주소 확인

docker 명령어를 이용하여 IP주소를 확인할 수 있다.

 

도커는 host를 설정값을 지정해주지 않으면 내부 IP가 자동으로 할당되고 기본적으로 172.~.~.~ 처럼 IP가 할당된다.

 

 

여러 인스턴스에서 유레카를 사용하기 위한 방법으로는 2가지 정도가 있는것 같다.

  1. 네트워크를 두 인스턴스를 묶어서 클러스터링
  2. host IP로만 통신할 수 있도록 설정을 변경

지금 부터의 방법은 2번 방법이다.

 

 

# 코드 & 사진

1. application.yml 

server:
  port: 6000

spring:
  application:
    name: USER

eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://3.38.23.41:8761/eureka
  instance:
    prefer-ip-address: true  # 등록!!

 

2. docker-compose.yml

version: "3.7"
services:
  app:
    network_mode: host  # 제일 중요!
    build: .
    restart: always
    ports:
      - "6000:6000"  # 포트!! 조심

 

3. Dockerfile

FROM openjdk:11-jre-slim
LABEL maintainer="ensu6788@gmail.com"
VOLUME /tmp
ARG JAR_FILE=./build/libs/*.jar
ADD ${JAR_FILE} app.jar
EXPOSE 6000
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./uradom","-jar","/app.jar"]

 

cker-compose에서 network_mode:host 를 지정해 주고 docker를 실행할때 --network host 를 붙여주면 된다.

docker run --network host -d ensu6788/user_app

 

먼저 도커 내부 IP주소를 확인해 보자

docker inspect <컨테이너 ID> | grep IPAddress #도커내부 주소 확인

docker 내부 IP주소가 없다!!

 

 

Q. 왜 도커 내부 IP주소가 없는 걸까?

A. 호스트 네트워킹을 사용하는 이유

1. 호스트 네트워크를 사용하면 어떻게 될까?

 

host컨테이너에 네트워크 모드를 사용하는 경우 해당 컨테이너의 네트워크 스택은 Docker 호스트에서 격리되지 않으며(컨테이너는 호스트의 네트워킹 네임스페이스를 공유함) 컨테이너에 자체 IP 주소가 할당되지 않습니다.

예를 들어 포트 80에 바인딩된 컨테이너를 실행하고 host 네트워킹 을 사용 하는 경우 컨테이너의 응용 프로그램은 호스트 IP 주소의 포트 80에서 사용할 수 있습니다.

참고 : 감안할 때 컨테이너가 사용할 때 자신의 IP 주소를 가지고 있지 않습니다 host모드 네트워킹, 포트 매핑을 적용하지 않으며, -p, --publish, -P, 및 --publish-all옵션 대신 경고를 생산, 무시된다 :

WARNING: Published ports are discarded when using host network mode

 

호스트 모드 네트워킹은 네트워크 주소 변환(NAT)이 필요하지 않고 각 포트에 대해 "userland-proxy"가 생성되지 않기 때문에 컨테이너가 광범위한 포트를 처리해야 하는 상황과 성능을 최적화하는 데 유용할 수 있습니다.

--network host 를 이용하여 네트워크를 사용할 수도 있습니다 . 이 경우 제어 트래픽은 여전히 오버레이 네트워크를 통해 전송되지만 개별 Swarm 서비스 컨테이너는 Docker 데몬의 호스트 네트워크 및 포트를 사용하여 데이터를 전송합니다. 이것은 몇 가지 추가 제한을 만듭니다. 예를 들어 서비스 컨테이너가 포트 80에 바인딩된 경우 지정된 Swarm 노드에서 하나의 서비스 컨테이너만 실행할 수 있습니다.

 

나의 해석 : 호스트 모드의 네트워크를 사용하면 Docker host(IP)를 사용하지 않으며 포트는 docker-compose에서 설정한 포트를 사용하다. 즉, 포트 포워딩이 불가능 하다!

 

Reference

https://docs.docker.com/network/host/

 

 

A. 따라서 호스트 네트워킹을 사용하면 Docker 내부 Host IP를 사용하지 않기 때문에 AWS의 IP주소를 사용할 수 있는것 같다.

Eureka 대시보드를 보면 호스트 네트워킹을 사용하였을때는 AWS IP주소를 사용하지만 사용하지 않는 서비스들은 호스트 네임이 없어 랜덤값이 부여된 것을 확인할 수 있다.

 

즉, 컨테이너가 AWS의 IP주소를 가지게 하여 Eureka에 등록하였기 때문에 컨테이너를 유레카가 찾을수 있었던 것 같다!

 

 

  • [ ] 질문 1. 하나의 인스턴스에서는 어떻게 해결된건가?
  • [ ] 질문 2. 호스트 IP주소를 없애고 AWS 내부 IP주소를 가지게 했는데 그면은 Prefer-IP-Address가 있어야 하나? 호스트 주소를 AWS주소로 등록한거 아닌가? 여러 인스턴스에서 동작 기반을 잘 모르겠다.

 

끝!

Github

https://github.com/bithumb-talk/backend-gateway-service

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함