본문 바로가기
프로그래밍/로깅과 트레이싱

[MSA] Spring Cloud Sleuth와 Zipkin을 이용한 분산 시스템 Tracing_2

by 사바라다 2019. 12. 7.

안녕하세요. 오늘은 저번 포스팅에 이어서 Zipkin과 Sleuth를 연동하여 구현해보겠습니다. sleuth로 id를 주입하여 zipkin에서 취합여 보기좋게 트레이싱 하는 것 까지 입니다.

오늘 이 테스트를 진행하기 위해서는 2개의 프로젝트가 필요합니다.

그리고 오늘 프로젝트의 소스는 github에 올려두겠습니다.

Zipkin과 Sleuth 연동

zipkin에서 데이터를 모으기 위한 시퀀스 다이어그램은 아래와 같습니다.

zipkin Sequence Diagram 출처 : zipkin.io

보시면 sleuth에서 trace에 대한 ID를 헤더에 기입하며, 타 시스템어 전송 후 돌아온 응답에 대해서 zipkin으로 비동기로 전송합니다. 이렇게 zipkin에 보내진 데이터는 취합되어 유저에게 보여주게 되는것입니다. 그렇다면 이제 본격적으로 구현해보도록 하겠습니다.

Zipkin 의존성 추가

zipkin을 spring boot에서 사용하기 위해서는 spring-cloud-zipkin에 대한 의존성을 추가해주어야만합니다. 추가해야 할 의존성은 아래와 같습니다.

implementation("org.springframework.cloud:spring-cloud-starter-zipkin:2.1.4.RELEASE")
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>

Zipkin 환경 설정

의존성을 설정했으니 이번에는 환경 설정을 해야합니다. 설정한 환경 설정은 아래와 같습니다.

server:
  port: 8081

spring:
  application:
    name: start-point
  sleuth:
    sampler:
      probability: 1.0
  zipkin:
    base-url: http://localhost:9411

zipkin과 sleuth부분만 보도록하겠습니다.

spring.zipkin.base-url : zipkin 웹서버가 있는 곳을 지정합니다. 기본 값은 http://localhost:9411입니다. 따라서 local에서 테스트를 하실 때는 별도로 기술하시지 않으셔도 됩니다만 저는 명확하게 하기위해 기술했습니다.

spring.sleuth.sampler.probability : 해당 설정은 얼마나 많은 비율로 zipkin에 transaction을 전송할 것인가 입니다. 즉, 1초에 1000건이 들어왔다고하면 여기에서 100건을 zipkin에게 던질지 500건을 던질지 비율적으로 설정할 수 있다는 것입니다. 기본값은 10%(0.1)입니다. 저는 테스트를 위해서 100%(1.0)을 설정했습니다.

  • 2.2.0 버전부터는 설정에 관계없이 무조건적으로 1초에 1000샘플링까지 가능하다고 합니다.

코드 작성

이제 테스트를 위해 코드를 작성하겠습니다. 사전 조건적으로 추적을 하려고 하는 것이기 때문에 적어도 2개의 프로젝트를 만드셔야 합니다. 저는 멀티모듈로 만들었습니다만, 멀티모듈이 접근하기 어려우신 분들은 2개의 프로젝트로 만드셔도 됩니다.

1번째 코드는 아래와 같습니다.

@RestController
public class StartController {

    // 1 - 2. end-point를 호출하기위한 DI, 맴버변수.
    @Autowired
    RestTemplate restTemplate;

    // 2. 로그 확인을 위한 맴버변수
    final Logger log = LoggerFactory.getLogger(StartController.class);

    @GetMapping("/ping")
    public String start() {

        log.info("###access start method");

        // 3. end-point를 직접 호출후 결과값을 String의 형태로 변환하여 forObject에 담는다. 
        final String forObject = restTemplate.getForObject("http://localhost:8082/ping", String.class);

        log.info("###end-point`s return value : {}", forObject);
        return "pong";
    }

    // 1 - 1. end-point를 호출하가위한 Bean 생성 
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

흐름을 보자면 먼저 외부와의 통신을 위해 RestTemplate을 Bean으로 만든 후 @Autowired로 주입받았습니다. 그리고 외부에서 "/ping"이라는 handler를 호출한다면 start라는 메서드가 http://localhost:8082/ping을 http의 get method로 호출합니다.

그럼 이제 end-point의 Controller를 보도록 하겠습니다.

@RestController
public class EndController {

    final Logger log = LoggerFactory.getLogger(EndController.class);

    @GetMapping("/ping")
    public String end() {
        log.info("###access end method");
        return "pong";
    }
}

음~ 별것 없습니다. 그냥 요청에대해서 log를 출력하고 String형태로 pong을 리턴합니다.

사실 zipkin, sleuth의 의존성을 주입했다고 하지만, 우리가 Contorller에서 일반적으로 작성하는 코드와 다를게 없다는 것을 알 수 있습니다. 이걸로 우리는 감탄뿐만이 아니라 SOLID의 원칙이 잘 지켜졌구나 하는 것을 알 수 있습니다. SOLID에 대해서 궁금하신 분은 여기에 제가 자세하게 설명해 두었으니 참고바랍니다.

테스트

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class StartControllerTest {

    @Autowired
    MockMvc mockMvc;

    @Test
    public void start() throws Exception {
        mockMvc.perform(get("/ping"))
               .andDo(print())
               .andExpect(status().isOk())
               .andExpect(content().string("pong"));
    }
}

위와 같이 테스트 코드를 Start쪽에 작성 후 확인해보도록하겠습니다.

  1. 먼저 오래동안 묵혀두었던 zipkin서버를 키도록 하겠습니다.
  2. 그리고 end-point 서버를 올립니다.
  3. 테스트 코드를 실행합니다.

이렇게 테스트를 진행하고 zipkin에 저장된 데이터를 한번 보도록 하겠습니다.

첫 화면에 tracing이 잡힌 것을 볼 수 있습니다. 그리고 그 tracing을 클릭하면 아래와 같이 자세히 볼 수 있는 화면이 나오는것을 알 수 있습니다.

마무리

오늘은 sleuth와 zipkin을 연동하여 트레이싱하는 법을 직접 구현해가며 알아보았습니다.

구현된 소스는 github에 넣어두었습니다.

다음시간부터는 중앙 집중형 로깅에 대해서 알아보도록 하겠습니다.

감사합니다.

참조

https://spring.io/projects/spring-cloud-sleuth

댓글