블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

Zipkin을 이용한 MSA 환경에서 분산 트렌젝션의 추적 #2 

 Spring Sleuth를 이용한 Zipkin 연동


조대협 (http://bcho.tistory.com)



앞글에 이어서 이번에는 실제로 어플리케이션에서 분산 로그를 추적해보도록 한다.

스프링 부트 애플리케이션을 Zipkin과 연동하기 위해서는 Sleuth라는 라이브러리를 사용하면 된다.

구조

우리가 구현하고자 하는 예제의 구조는 다음과 같다.


API Client는 User 서비스를 호출하고, User 서비스는 Item 서비스를 호출하여 사용자의 Item 정보를 리턴 받아서 리턴 받은 내용을 API Client에 호출한다.

User와 Item 서비스는 모두 Spring Boot 1.5 버전으로 개발하였다. Spring 2.0은 아직 나온지가 얼마되지 않아서 Zipkin 이 지원되지 않는다.

이 예제에 대한 전체 코드는 https://github.com/bwcho75/zipkin-spring-example 에 있다.

User 서비스 코드

User 서비스 코드를 살펴보도록 하자

maven pom.xml

먼저 maven 빌드 스크립트인 pom.xml에는, zipkin 연동을 위해서 sleuth 라이브러리를 사용하기 위해서 이에 대한 의존성을 추가한다. 아래와 같이 zipkin과 sleuth 라이브러리의 버전은 1.3.2.RELEASE 버전을 사용하였다. 참고로 스프링 부트의 버전은 1.5.5.RELEASE 버전을 사용하였다.


<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-zipkin</artifactId>

   <version>1.3.2.RELEASE</version>

</dependency>

<dependency>

   <groupId>org.springframework.cloud</groupId>

   <artifactId>spring-cloud-starter-sleuth</artifactId>

   <version>1.3.2.RELEASE</version>

</dependency>


Controller 클래스

다음은 /users URL을 처리하는  Rest Controller 부분의 코드를 살펴보자, 코드는 다음과 같다.


@RestController

@RequestMapping("/users")

public class UserController {

   @Autowired

   RestTemplate restTemplate;

   

   @Bean

   public RestTemplate getRestTemplate() {

       return new RestTemplate();

   }

   

   @Bean

   public AlwaysSampler alwaysSampler() {

       return new AlwaysSampler();

   }

private static final Logger logger = LoggerFactory.getLogger(UserController.class);

@RequestMapping(value="/{name}",method=RequestMethod.GET)

public List<User> getUsers(@PathVariable String name){

logger.info("User service "+name);

List<User> usersList = new ArrayList<User>();

List<Item> itemList = (List<Item>)restTemplate.exchange("http://localhost:8082/users/"+name+"/items"

,HttpMethod.GET,null

,new ParameterizedTypeReference<List<Item>>() {}).getBody();

usersList.add(new User(name,"myemail@mygoogle.com",itemList));

return usersList;

}


}


getUsers() 함수에서 /users/{name}으로 들어오는 요청을 받아서 RestTemplate을 이용하여 localhost:8082/users/{name}/items로 호출하는 코드이다.

여기서 중요한것이 RestTemplate 객체를 생성하는 방법은데, restTeamplte을 @AutoWrire로 하게 하고, getRestTemplate을 @Bean으로 정해줘야 한다. (아래 문서 참조 내용 참고)


https://cloud.spring.io/spring-cloud-static/spring-cloud-sleuth/1.2.1.RELEASE/#_baggage_vs_span_tags

그리고 @Bean으로 정의된 alwaysSampler()를 정의하는데, Sampler란 zipkin으로 트레이싱 하는 트렌젝션을 100%를 다할것인지 일부만 할것인지를 결정하는 것이다. 여기서는 100%를 다하도록 하였다.

100%를 샘플링하면 정확하게 트렌젝션을 추적할 수 있지만, 반대 급부로 매번 샘플링 및 로그를 서버에 전송해야하기 때문에 성능 저하를 유발할 수 있기 때문에 이 비율을 적절하게 조정할 수 있다. 비율 조정은 뒤에 설명할 설정파일에서 조정이 가능하다.

applicaiton.yml

Zipkin 서버의 URL과, 샘플링 비율등을 설정하기 위해서는 src/main/resources/application.yml에 이 설정 정보를 지정해놓는다. 아래는  application.yml 파일이다.


server:

 port: 8081

spring:

 application:

   name: zipkin-demo-server1

 zipkin:

   baseUrl: http://127.0.0.1:9411/

 sleuth:

   enabled: true

   sampler:

     probability: 1.0

sample:

 zipkin:

   enabled: true


port는 이 서비스가 listen할 TCP 포트로 8081로 listen을 하도록 하였다.

spring.zipkin에 baseUrl 부분에 zipkin 서버의 URL을 지정한다. 이 예제에서는 zipkin 서버를 localhost(127.0.0.1):9411 에 기동하였기 때문에 위와 같이 URL을 지정하였다.

다음은 sleuth 활성화를 위해서 spring.sleuth.enabled를 true로 하고 sampler에서 probability를 1.0으로 지정하였다.

Item 서비스 코드

Item 서비스 코드는 User 서비스 코드와 크게 다르지 않다. 전체 코드는 https://github.com/bwcho75/zipkin-spring-example/tree/master/zipkin-service2 를 참고하기 바란다.

Item 서비스는 8082 포트로 기동되도록 설정하였다.

테스트

서비스 개발이 끝났으면 컴파일을 한 후에 User 서비스와 Item  서비스를 기동해보자.

Zipkin 서버 구동

Zipkin 서버를 설치하는 방법은 https://zipkin.io/pages/quickstart 를 참고하면 된다. 도커 이미지를 사용하는 방법등 다양한 방법이 있지만 간단하게 자바 jar 파일을 다운 받은 후에, java -jar로 서버를 구동하는게 간편하다.

wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
java -jar zipkin.jar

이때 주의할점은 zipkin 서버를 통해서 HTTP로 Trace 로그를 받을때, 별도의 보안이나 인증 메커니즘이 없기 때문에, zipkin 서버는 반드시 방화벽 안에 놓고, 서비스 서버로부터만 HTTP 호출을 받을 수 있도록 해야 한다.

부하주기

모든 서버가 기동 되었으면 부하를 줘서 로그를 수집해보자. 부하 발생은 간단하게 apache ab 툴을 이용하였다.

%ab -n 1000 http://localhost:8081/users/terry

위의 명령어는  localhost:8081/users/terry로 HTTP GET 요청을 1000번 보내는 명령이다.

결과 확인

부하 발생이 끝난후에 http://localhost:9411 화면으로 들어가서 Find Traces 버튼을 눌러보면 다음과 같은 트레이스 화면을 볼 수 있다. 개개별 트렌젝션 결과가 나오고,


개별 트렌젝션을 눌러보면 다음과 같은 결과가 나오는 것을 볼 수 있다. 아래를 보면 /users/terry가 전체 58.944 ms가 소요되고, users/terry/items는 2 ms가 소요되는 것을 확인할 수 있다. 앞에는 서비스 명인데, 첫번째 서비스는 zipkin-demo-server1, 두번째 서버는 zipkin-demo-server2 로 출력이 된다. 이 서버명은 application.yml 파일에서 지정하면 된다.



재미있는 기능중 하나는 각 서비스의 의존성을 시각화 해주는 기능이 있는데, 화면 위쪽에 dependency 버튼을 누르면 아래 그림과 같이 로그 기반으로하여 서비스간의 호출 의존성을 보여준다.



지금까지 간략하게 Spring Sleuth와 Zipkin을 이용한 분산 로그 추적 기능을 구현해보았다.

여기서 구현한 내용은 어디까지나 튜토리얼 수준이다. Zipkin 서버의 스토리지 구성이 메모리로 되어 있기 때문에 실 운영환경에서는 적합하지 않다. 다음 글에서는 클라우드 환경을 이용하여 운영 수준의 Zipkin 서비스를 구성하는 방법에 대해서 알아보도록 하겠다.


참고 자료

https://howtodoinjava.com/spring/spring-boot/spring-boot-tutorial-with-hello-world-example/

https://howtodoinjava.com/spring/spring-cloud/spring-cloud-zipkin-sleuth-tutorial/



Spring boot 살펴본 느낌

프로그래밍/Spring & Maven | 2014.11.25 09:05 | Posted by 조대협

Spring boot 살펴본 느낌


요즘 Spring Boot가 하도 유행이길래,잠깐 REST API Expose하는 시나리오와 DB 연동 시나리오등 몇가지 일반적으로 웹 개발하는데 사용되는 시나리오들을 살펴 보았다.

전체적인 느낌은 node.js나 ruby on rails와 같은 모던 웹 기술의 생산성을 따라잡기 위해서 무단히 노력했다는 점. 그래서 코드 생산성은 유사하거나 약간 열세? 정도 일거 같은 느낌인데, 왠지 개운하지 않은 뒷맛이 남는다고나 할까? 컨셉적으로 복잡하고 정교한 프로그래밍을 위한 프레임웍을 이용해서 단순성을 부여하다 보니, 빠지거나 아니면 디폴트 설정으로 인해서 위험한 부분이 있을 것 같은 느낌인 든다.


일단 코드 생성 부분을 보면

Maven 프로젝트를 이용해서 생성한 후에, 


JSON 객체와

package hello;


public class Greeting {

long id;

String content;

public Greeting(long id,String content){

this.id = id;

this.content = content;

}

public long getId(){

return id;

}

public String getContent(){

return content;

}

}



그리고 이 객체를 Get 하는 간단한 REST API를 구현해보면


package hello;


import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;


@RestController

public class GreetingController {


    private static final String template = "Hello, %s!";

    private final AtomicLong counter = new AtomicLong();


    @RequestMapping("/greeting")

    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {

        return new Greeting(counter.incrementAndGet(),

                            String.format(template, name));

    }

}


이 정도 수준이다. 

처음 시작하는 수준에서, 문서만 보고 이렇게 구현하고 개념을 잡는데 까지 약 20여분이 걸렸으니, node.js나 python등의 여타 모던웹 언어와 크게 진입은 다르지 않다.


실행도, 복잡한 톰캣 설치나 배포 등이 필요 없이, jar 파일을 java -jar로 실행만 하면 spring boot가 tomcat이나 jetty를 임베드한 상태로 기동이 된다.


마치 node.js에서 코드 작성하고, node 소스.js를 기동하면 되는 것과 같은 편리함이다.

기존의 복잡한 XML 설정도 과감하게 생략할 수 있으며, 운영에서 필요한 모니터링과 같은 API등을 Actuator라는 이름으로 제공한다. (Hearbeat 체크, 모니터링 메트릭스 API등)


무엇보다 장점은, 기존의 자바와 스프링 프레임웍에 제공하는 프레임웍들을 그대로 사용할 수 있다는 것이다.

Spring Batch,Spring integratio이나 Spring XD등의 프레임웍을 그대로 사용이 가능하다. 그리고 이런 프레임웍을 이용해서 웹 기반의 애플리케이션 뿐만 아니라, Command Line에서 기동할 수 있는 일반적인 애플리케이션도 개발이 가능하다.(배치 데몬 등)


약간 신경이 쓰이는 부분은

데이타 베이스 엑세스 부분도 다른 일반 프레임웍 (Hibernate,MyBatis 등)을 당연히 사용할 수 있겠지만, Spring-JPA-Data로 사이트에서는 은근(?) 가이드를 하고 있다. Spring-JPA-Data가 은근 스프링에서 밀고 있는 느낌인데, SQL을 JPA에다가 한계층 더 올려서 Data 레벨까지 추상화 한 개념은 아무래도 아직까지는 개운하지는 않다. 요즘들어 많이 쓰이고는 있지만, Hibernate 조차도 복잡한 테이블 관계등을 가지는 경우 여전히 성능이나 메모리 문제를 야기 하는데, 잘 이해하고 쓰면 모르겠지만 Data 레벨을 추상화된 프레임웍이라니 아직은 걱정이 되는게 사실이다.


그리고, node.js = express, python = django, ruby = rails와 같이 모던웹 기술들은 널리 사용되는 웹 프레임웍과 템플릿 엔진이 있는데, Spring boot는 이 부분이 아직 부족해 보인다. (없다는 게 아니라, 그만큼 저변이 확대된 생산성이 높은 웹 프레임웍 엔진, 템플릿 엔진이 없다는 것이다.)

thymeleaf (http://www.thymeleaf.org/whoisusingthymeleaf.html) 템플릿 엔진이 많이 언급되고 있는 것은 같은데, 아직까지 메이져는 아닌거 같고, grails도 아직까지는 다른 스크립트 언어에 비해서 웹에서는 강세가 낮기 때문에 다소 지켜봐야할 부분이 아닌가도 싶다.

그렇지만, 이 부분은 모던 웹에서는 풀 자바스크립트 클라이언트 (angular.js)등을 사용하는 추세가 많아 짐에 따라 큰 장애가 되지는 않을 것으로 보인다.


오히려, API나 백엔드를 빠르게 만들 수 있는 생산성에 의해서 MSA (Micro Service Architecture)와 맞물려서, 백엔드 서버의 API 서버로 포지셔닝할 거 같고, 기존의 node나 ruby와 같은 타 스크립트에 기술을 아직 배우지 못한 자바 개발자들이 이쪽으로 넘어가지 않을까 싶다. 

그리고 JTA 연동을 통한 분산 트렌젝션이나 큐를 이용한 비동기 처리등은 기존의 스크립트 언어들이 가지지 못한 장점이기 때문에, 엔터프라이즈 수준의 프레임웍을 제공하는 것은 인정할만 장점이다.

시간내서 제대로 한번 정리해서 볼 필요는 있는듯.


'프로그래밍 > Spring & Maven' 카테고리의 다른 글

Spring boot 살펴본 느낌  (2) 2014.11.25
Spring Batch (DB2File)  (0) 2013.06.27
Spring Batch 개념 정리  (0) 2013.06.27
SpringBatch HelloWorld  (0) 2013.06.27
Eclipse에서 Spring Maven 개발환경 설정하기  (0) 2013.03.19
Maven 스터디  (0) 2013.03.19