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


Archive»


 
 

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

Stackdriver를 zipkin으로 사용하기


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


앞의 예제에서는 간단하게 Zipkin 서버를 메모리 스토리지를 이용해서 올렸는데, 운영환경에서는 적절하지 않다. 실 운영환경에서는 대규모 트래픽 저장 및 쿼리를 위해서 Cassandra나 Elastic Search 등을 사용해야 하는데, 설정과 운영이 어렵다.

이에 대한 대안으로 구글 클라우드에는 분산 트렌젝션 추적을 위한 Stack driver trace (https://cloud.google.com/trace/) 라는 기능이 있다. 자체적인 SDK를 이용하여 트렌젝션을 추적하는 것도 가능하지만, Zipkin 클라이언트로 부터 로그를 수집할 수 있다.

즉 개발단은 Zipkin을 사용하고, 뒷단에는 복잡한 Zipkin 서버 대신 Stack driver trace를 사용하는 방법이다.


개념적으로 보면 다음과 같다. Zipkin 서버 대신 Zipkin/stack driver collector 라는 서버를 띄우면 이 서버가 Stackdriver 로 로그를 저장하고 시각화 해준다.



Zipkin/stack driver collector는 zipkin 서버를 대치하는 역할로, zipkin 클라이언트가 zipkin 서버 대신 이 zipkin/stack driver collector 를 바라보도록 주소와 포트만 변경해주면 된다.

흥미로운 점은 구글 클라우드 뿐 아니라, 로컬 환경, AWS,Azure,On Prem 등 다양한 환경에 설치가 가능하다. 그래서 모든 애플리케이션 서비스를 통합해서 Stack driver 로 trace가 가능하다.


Zipkin/stack driver collector를 설치하는 방법은 다음과 같다.

https://cloud.google.com/trace/docs/zipkin

Docker 이미지를 이용해도 되고 java jar 파일을 다운로드 받아서 사용해도 된다.

구글 클라우드 VM이나 도커로 실행할때는 상관이 없지만 구글 클라우드 인프라 밖에서 Zipkin Stackdriver collector를 실행할때는 추가적인 인증 정보를 설정해야 한다.


Stack driver collector가 Stackdriver 서버(클라우드)로 로그를 전달하기 위해서는 아무 로그나 받으면 안되고 인증된 로그만 받아야 하니 추가 인증 체계가 필요한데, 구글 클라우드에서는 애플리케이션 인증을 위해서 Service Account라는 JSON 파일을 사용한다.  Service Account 생성 방법은 https://medium.com/google-cloud/distributed-tracing-spring-boot-microservices-with-stackdriver-trace-7fe42c6de3f3 문서를 참고하기 바란다.


Service Account 파일이 생성되면, 아래와 같이 GOOGLE_APPLICATION_CREDENTAILS 환경 변수에 Service account 파일의 경로를 지정한다.

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/credentials.json"
export PROJECT_ID="my_project_id"

다음 구글 클라우드의 어느 프로젝트에 있는 Stack Driver 와 연결할지를 지정해야 하는데, “PROJECT_ID” 환경 변수에 프로젝트 명을 지정해주면 된다.

환경 변수 설정이 끝나면 java -jar collector-0.6.0.jar 명령으로 collector를 실행한다.

아래는 환경 변수 설정과 collector 를 실행하는 스크립트 예제이다.


export GOOGLE_APPLICATION_CREDENTIALS="./terrycho-sandbox-zipkin-collector.json"

export PROJECT_ID="terrycho-sandbox"


echo $GOOGLE_APPLICATION_CREDENTIALS

echo $PROJECT_ID

java -jar collector-*.jar


포트는 디폴트로 9411을 사용하게 되어 있다. 이전 예제에서 zipkin 서버 대신 collector만 대신 띄운 후에 부하를 주면 로그를 수집할 수 있다.

아래는 로그를 수집한 후에, 분석화면의 일부분이다.


Zipkin UI와 동일하게 각 단일 트렌젝션에 대해서 Trace/Span 정보를 확인할 수 있고, Spot 그래프를 이용한 응답 시간 분포 확인이 가능하다.




아울러 각 서비스 별로 응답 시간에 대한 분포도를 아래와 같이 시각화 해준다.




참고

구글 클라우드내에서 Zipkin과 StackDriver 연결 방법 https://codelabs.developers.google.com/codelabs/cloud-spring-cloud-gcp-trace/index.html?index=..%2F..%2Findex#6





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/