본문 바로가기
프로그래밍/Spring Cloud

[MSA] Spring Cloud Eureka에 관하여 - 이론편

by 사바라다 2020. 2. 5.

안녕하세요. 오늘은 Spring Cloud의 구성요소 중 Eureka에 대해서 알아보도록 하겠습니다.

Eureka 란

개요

Eureka는 AWS와 같은 Cloud 시스템에서 서비스의 로드 밸런싱과 실패처리 등을 유연하게 가져가 위해 각 서비스들의 IP / Port / InstanceId를 가지고 있는 REST 기반의 미들웨어 서버입니다.

Eureka는 마이크로 서비스 기반의 아키텍처의 핵심 원칙 중 하나인 Service Discovery의 역할을 수행합니다. MSA에서는 Service의 IP와 Port가 일정하지 않고 지속적을 변화합니다. 그렇기 때문에 Client에 Service의 정보를 수동으로 입력하는 것은 한계가 분명합니다. Service Discovery란 이런 MSA의 상황에 적합합니다.

아키텍처 및 플로우

eureka에 각자의 ip / port를 등록하여 http 통신에 사용한다.

위의 이미지는 Eureka가 MSA에서 사용되는 방법을 나타낸 것입니다.

Eureka는 Client-Server의 방식으로 Eureka Server는 모든 Client 서버들이 본인의 IP와 Port, InstanceId를 Eureka-Server로 전달합니다. 그리고 Eureka에 있는 정보를 Fetch하여 Eureka-Client간 통신에 사용합니다.

그렇다면 Eureka Server와 Client의 상호작용하는 interface에 대해서 알아보도록 하겠습니다.

Eureka Server와 Client의 상호작용

Eureka-Server와 Eureka-Client는 REST 통신하여 상호작용을 합니다. 그렇기 때문에 java application이 아니더라도 통신할 수 있습니다. 지금부터 그 상호작용에 대해서 알아보도록 하겠습니다.

  • HTTP Operation 테이블에서 appID는 서비스의 이름입니다. instanceID는 서비스 중에서 특정 서버의 id입니다. Content-Type으로 application/xml 또는 application/json을 지원합니다.

ereka github에서 지원하는 docs와 실제로 eureka-server와 eureka-client가 통신하는것을 wireshark로 찍어보았습니다.

Register(등록)

Eureka Client는 실행중인 instance의 정보(IP, Port 등)를 Eureka Server로 새로 등록합니다. 1번째 heartbeat에 등록됩니다.

Operation HTTP action Description
Register POST /eureka/apps/{appID} HTTP/1.1 Input: JSON/XML payload, 아래 참조 HTTP Code: 204 on success


eureka-client -> eureka-server
com.netflix.appinfo.InstanceInfo
class를 참조하면 instance Register class는 아래와 같은 형상입니다. 그리고 등록에 대해서 wireshark로 찍어보았습니다.

http://localhost:8761/eureka/apps/sabarada-service

{
  "instance": {
    "instanceId": "392d9569-3a3a-4da0-9447-bfdcc14d8b30",
    "hostName": "x.x.x.x",
    "app": "sabarada-service",
    "ipAddr": "x.x.x.x",
    "status": "UP",
    "overriddenStatus": "UNKNOWN",
    "port": {
      "$": 8081,
      "@enabled": "true"
    },
    "securePort": {
      "$": 443,
      "@enabled": "false"
    },
    "countryId": 1,
    "dataCenterInfo": {
      "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
      "name": "MyOwn"
    },
    "leaseInfo": {
      "renewalIntervalInSecs": 30,
      "durationInSecs": 90,
      "registrationTimestamp": 0,
      "lastRenewalTimestamp": 0,
      "evictionTimestamp": 0,
      "serviceUpTimestamp": 0
    },
    "metadata": {
      "@class": "java.util.Collections$EmptyMap"
    },
    "homePageUrl": "http://192.168.4.91:8081/",
    "statusPageUrl": "http://192.168.4.91:8081/actuator/info",
    "healthCheckUrl": "http://192.168.4.91:8081/actuator/health",
    "vipAddress": "sabarada-service",
    "secureVipAddress": "sabarada-service",
    "isCoordinatingDiscoveryServer": "false",
    "lastUpdatedTimestamp": "1580878819825",
    "lastDirtyTimestamp": "1580878820311"
  }
}

Eureka-Client에서 Eureka-Server로 위와 같은 REST 통신을 통해 등록할 수 있습니다. 등록하게 되면 Eureka-Server에서는 Registered instance sabarada-service/392d9569-3a3a-4da0-9447-bfdcc14d8b30 with status UP (replication=false)로그와 함께 등록되게 됩니다.

Renew(갱신)

Eureka Client는 30초를 주기로 Eureka Server에 heartbeats를 보냅니다. 만약 Server가 90초 동안 heartheats를 받지 못하면 Server의 정보 저장소에서 제거됩니다(register시 leaseInfo해당부분을 참조).

Operation HTTP action Description
heartbeat PUT /eureka/apps/appID/instanceID HTTP Code: 
* 200 on success 
* 404 if instanceID doesn’t exist

 

wireshark로 찍어본 명령어는 아래와 같습니다.

PUT /eureka/apps/sabarada-service/392d9569-3a3a-4da0-9447-bfdcc14d8b30?status=UP&lastDirtyTimestamp=1580878820311 HTTP/1.1

Fetch Registry(등록 정보 획득)

Eureka Client는 서버가 올라올 때 eureka Server에 있는 모든 정보를 가져와 local Cache에 저장합니다. 그 후 이 정보를 http 통신에 이용합니다. 이 정보는 매 주기 30초마다 변경점에 대해서 부분업데이트(delta)를 진행합니다.

 

처음 모든 정보 가져오기 요청

Operation HTTP action Description
전체 업데이트 GET /eureka/apps HTTP Code: 200 on success Output: JSON/XML

 

GET /eureka/apps HTTP/1.1

 

30초에 한번씩 변경된 정보 가져오기 요청

Operation HTTP action Description
부분 업데이트 GET /eureka/apps/delta HTTP Code: 200 on success Output: JSON/XML
GET /eureka/apps/delta HTTP/1.1

응답

{
  "applications": {
    "versions__delta": "21",
    "apps__hashcode": "UP_2_",
    "application": [
      {
        "name": "sabarada-service",
        "instance": [
          {
            ...[중략]
            "status": "UP",
            ...[중략]
          },
          {
            ...[중략]
            "status": "DOWN",
            ...[중략]
          }
        ]
      }
    ]
  }
}

위의 예제 응답은 2개의 서버 UP 상태에서 1개의 서버를 Down한 후 살아있는 서버에서 요청한 갱신사항 요청에 대한 응답입니다. 1개의 서버는 UP(본인), 죽은 서버는 DOWN으로 해서 응답이 돌아오는 것을 알 수 있었습니다.

위의 명령어를 통해 처음에는 모든 정보를 가져오고, 30초에 한번씩은 변경된 정보를 가져오기위한 요청을 별도로 진행합니다. 만약 delta를 했을 시 service의 상태가 DOWN이라면 Client에서는 해당 Service를 local cache에서 삭제합니다.

Cancel(취소)

Cancle의 경우 Eureka에 있는 본인의 instance를 삭제합니다. 서비스가 종료될 때 Eureka-Client에서 Eureka-Server로 전송합니다. 그리고 evictionDurationInSecs를 설정해서 eureka-server에 등록한다면 해당 시간동안 renew가 없다면 자동적으로 삭제됩니다.

Operation HTTP action Description
De-register DELETE /eureka/apps/appID/instanceID HTTP Code: 200 on success

 

요청

DELETE /eureka/apps/sabarada-service/5a7af17d-8800-4408-9855-ac55246d070d HTTP/1.1

Time Lag(지연)

새로운 서버의 등록 및 기존 서버의 삭제에 대해서 모든 Client가 알기 까지는 시간적인 지연이 발생합니다. eureka server가 정보 변경에 대해서 push하는 것이 아닌 각 client가 주기적으로 polling하기 때문입니다. Eureka 문서에서는 모든 서버에 전송되기 까지는 최대 2분정도 걸릴 수 있다고 말합니다.

마무리

오늘은 이렇게 Spring Cloud Eureka의 이론과 wireshark를 통해 실제 Eureka-Client와 Eureka-Server가 주고받는 request와 response에 대해서 알아보았습니다.

다른 Spring Cloud 기술들과는 다르게 Eureka는 큰 버전 갱신은 앞으로 없을거라고 12월 Pivotal MeetUp에서 들었습니다. 이유는 서버를 등록하고 그 서버를 반환하는 간단한 역할만 하기 때문이라고 하는데 지켜봐야 할 것 같습니다.

다음 시간에는 eureka-client와 eureka-server를 실제로 등록하는 방법을 알아보도록 하겠습니다.

감사합니다.

참조

https://cloud.spring.io/spring-cloud-netflix/reference/html/#service-discovery-eureka-clients

https://github.com/Netflix/eureka/wiki

https://netflixtechblog.com/netflix-shares-cloud-load-balancing-and-failover-tool-eureka-c10647ef95e5

댓글