System Compleat.

고가용 서비스 - Spring Cloud - #4 Monitor your Micro services - Zipkin

Techs


(정윤진, younjin.jeong@gmail.com) 

서비스를 모니터링하는 것은 매우 중요하다. 모니터링 동작은 기본적으로 각종 시스템의 지표를 주기적으로 취득하여 이를 로그로 남기거나 또는 이 로그 스트림을 어딘가로 보내 그래프를 만들고, 특정 값 또는 특정 문자열이 발견되면 알람을 울리는 식의 단계를 가진다. 이런 각종 시스템의 지표, 이를 테면 CPU, 메모리, 디스크, 네트워크 사용율과 같은 정보들은 OS에서 제공하는 /proc 하위 디렉토리 내용을 참조하거나 별도의 커맨드를 크론으로 주기적으로 돌려서 실행하곤 한다. 

이런 행위는 애플리케이션 서버에서도 동일하게 발생하는데, 보통 웹 애플리케이션 서버와 관련된 프로세스를 체크하거나 역시 이 서버들이 내리는 로그를 모니터링 하고 여기에 문제가 발생한 경우 알람을 울리는 역할을 하도록 하는 경우가 대부분이다. 물론, 역시 커스텀 로그의 생성도 가능하겠다. 


로그는 서비스 운영에 있어 굉장히 중요한 자산이다. 하지만 대부분의 레거시 시스템들에서는 이런 로그를 별도로 모아서 서비스에 어떤일이 발생했었는지, 또는 고객들의 서비스 사용 패턴에 대해 분석한다던지 하는 작업을 하는 경우는 별로 없었다. 거의 대부분 운영체제와 프로세스가 만들어 내는 로그들은 디스크에 차곡차곡 쌓이다가 어느 순간 디스크 사용율이 98% 정도에 이르면 시원하게 로그를 비워주는 작업을 하고는 했다. 하지만 이런 로그의 처리 방식은 최근 클라우드 기반의 (또는 클라우드 기반이 아니더라도) 서비스에서는 도저히 용납될 수 없는 처리 방식이라고 할 수 있다. 따라서 각 서버들이 생산해 내는 로그들은 깔때기 처럼 로그를 어느 한군데로 원격으로 수집하고, 이를 별도의 클라우드 스토리지에 자연스럽게 연계하여 저장하고, 저장된 정보를 하둡이나 데이터웨어 하우징에서 처리하도록 구성한다. 이런 방식의 로그 처리에 대해서는 아래의 링크를 살펴보면 보다 더 자세한 정보와 함께 다양한 옵션을 확인할 수 있겠다.  (https://logging.apache.org/log4j/2.x/manual/appenders.html) 로그 스트림 처리에 대해서는 나중에 기회가 되면 소개를. 

http://www.slideshare.net/petervandenabeele/akka-streams-kafka-kinesis-49863296


사실 로그는 오늘 언급할 주된 범주가 아니다. 로그는 사실 "어떤 사건이 발생했다" 라는 정보를 감지하는데는 매우 중요하지만, "왜 발생했는지" 에 대해서 설명을 해 주진 않는다. 아니 사실 해 주기는 하는데, 이는 로그를 스트림으로 처리해서 일련의 사건 흐름을 되짚어 보는 형태의 분석에 대한 구성이 필요하고, 이는 생각보다 꽤나 노력이 드는 일이다. 어쨋든 우리에게는 "왜 이런일이 발생했는가" 에 대한 내용을 볼 필요가 있는데, 마이크로 서비스를 구성하게 되는 경우 이는 각각의 서버 인스턴스에서 발생하는 로그를 위에 소개한 방법으로 전부 취합해서 시각화 하고 분석을 돌려야 하는 노력이 필요하다. 그러므로, 그래서 오늘 소개하는 도구는 바로 이런 일을 간단히 해 줄 수 있는 도구다. 


Zipkin 이 하는 기본적인 일은, 외부로 받은 요청과 그 요청을 처리하기 위해 발생하는 내부 요청을 기록한다. 그리고 이 기록된 요청에 대해 일목 요연하게 시각화 해 주며, 따라서 특정 요청을 처리하기 위해 어떤 서비스들이 참조 되었고 또 어떤 서비스가 제 속도를 내고 있지 못하는지의 여부를 쉽게 확인할 수 있다. 이는 트위터에서 만든 도구인데, 홈페이지는 http://zipkin.io  


백문이 불여일견, 백견이 불여일행. 


스프링 클라우드에서는 이 Zipkin 을 정말로 매우 쉽게 사용할 수 있도록 한다. 먼저 이전 포스팅에서 사용했던 헬로월드 클라이언트와 서버에 이 Zipkin 을 붙여 보도록 하자. 


먼저 zipkin-service. properties 파일을 demo-config 에 추가한다. 

server.port=${PORT:9411}
spring.datasource.initialize = false
spring.sleuth.enabled = false
zipkin.store.type = mem

Config server 가 참조하는 코드 저장소에 커밋하고 푸시한다. 


아래의 설정을 application.properties 에 추가한다. 이 설정은 모든 서비스에 글로벌로 적용된다. 

spring.sleuth.sampler.percentage=1.0
spring.sleuth.log.json.enabled=true


아래의 순서로 Zipkin 서버를 준비한다. 

- http://start.spring.io 에 간다. 

- artifact 에 zipkin-service 이름을 준다. 

- dependencies 에 Config client, Discovery, Zipkin UI, Zipkin server 를 추가하고 프로젝트를 생성하여 다운로드 받는다. 

- IDE 를 열어 아래와 같이 애플리케이션에 Zipkin 과 Eureka 사용을 위한 어노테이션을 추가한다. 

spring-cloud-zuul-proxy-demo/zipkin-service/src/main/java/com/example/ZipkinServiceApplication.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import zipkin.server.EnableZipkinServer;

@SpringBootApplication
@EnableZipkinServer
@EnableEurekaClient
public class ZipkinServiceApplication {

public static void main(String[] args) {
SpringApplication.run(ZipkinServiceApplication.class, args);
}
}


spring-cloud-zuul-proxy-demo/zipkin-service/src/main/resources/bootstrap.properties

- bootstrap.properties 를 추가하고 Config server 참조를 위한 설정과 애플리케이션의 이름을 할당한다. 

mvn spring-boot:run 으로 서버를 구동시켜 보자. 아래와 같은 화면이 보인다면 성공. 

자, 그러면 이제 각 서비스로 부터 실제 리퀘스트 데이터를 모아보기로 한다. 별도로 해 줄 것은 없고, helloworld-client 와 helloworld-service 의 두 모듈의 pom.xml 에 아래의 dependancy 를 추가한다. 

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>


그리고 지난 포스팅에서와 마찬가지로 Config server, 유레카 서비스, 헬로 월드 서버, 헬로 월드 클라이언트, zipkin 서비스의 순서대로 서비스를 모두 구동한다. 구동이 완료되면, zuul 을 통해 서비스에 요청을 해 보도록 하자. 그리고 zipkin ui 로 진입하여 find 버튼을 누르면 아래와 같은 내용이 나타난다. 

Zipkin 에서 보이는 데이터는 크게 두개인데, 하나는 전체 요청을 추적하는 trace 와, 다른 하나는 각각의 내부 서비스간 요청과 응답을 기록하는 span 의 두가지다. 이 데이터를 통해 위와 같이 각 리퀘스트의 처리에 걸리는 시간을 보여준다. 하나를 클릭해 보면 아래와 같은 세부 정보를 볼 수 있다. 

이는 외부로 요청을 받은 시점부터 이 요청을 처리하기 위해 내부 서비스간 발생한 요청에 대한 처리 시간을 보여준다. 헬로 월드 클라이언트는 헬로월드 서비스로 요청을 포워딩 했고 여기에 각각이의 처리 시간이 나타난다. 그리고 이 전체 요청의 처리에는 7ms 가 걸렸음을 확인할 수 있다. 이런 방식으로 내부 서비스가 다수 존재한다면, 예를 들어 캐시 마이크로 서비스가 존재하는데 여기서 지연시간이 증가했다면 서비스가 느려진 이유는 캐시 미스가 증가한 것으로 생각할 수 있다. 또는 별도의 데이터베이스나 메세지 큐와 같은 서비스를 참조하는 마이크로 서비스가 있다면 여기에서 발생한 지연 역시 확인할 수 있다. 즉, 이 도구는 "왜" 를 설명하기 위한 것이다.

우측 상단의 JSON 을 살펴보면 친절하게 JSON으로 만들어진 이 요청에 대한 데이터를 확인할 수 있다. 

그리고 서비스간 구조가 복잡하다면 아래의 그림과 같이 서로 어떻게 연관되어 있는지 그림도 그려 준다. 


마지막으로 언급할 내용은, 이 데이터들은 무슨 데이터웨어 하우징 도구에 넣고 "우리 반년전 상태가 어땠지?" 하는 질문에 사용하는 도구가 아니다. 즉, 이 데이터는 길어야 하루나 이틀 정도를 두고 "현재 우리 서비스의 상태를 빠르게 참조할 수 있는" 도구로 사용하는 것이 올바르다. 필요하다면 pgsql 과 같은 데이터베이스를 사용하여 각 요청을 저장할 수도 있겠다. 본 데모에서는 메모리에 저장하는 옵션을 사용했다. 

한가지더, 모든 요청을 기록할 필요가 없다. 위의 설정 내용을 보면 spring.sleuth.sampler.percentage=1.0 이런 설정이 있는데, 이는 데모를 위해서 전체 요청을 추적하는 것이다. 이럴 필요가 없이 적절한 수량의 요청을 샘플로 사용하여 서비스에 불필요한 부하를 주지 않도록 하자. 트위터의 경우에는 1/6,000,000 의 비율로 샘플링을 수행한다고 한다. 즉, 6백만 요청당 1개를 zipkin 에 넣고 보는 것이다. 


서킷 브레이커 보다 zipkin 이 먼저 나왔다. 어쨌든 도움 되는 포스팅이기를. 


(정윤진, younjin.jeong@gmail.com)