53
Pinpoint 대규모 분산 환경 추적 플랫폼 강운덕 2015-04-18

Pinpoint spring_camp 2015

Embed Size (px)

Citation preview

Page 1: Pinpoint spring_camp 2015

Pinpoint

대규모분산환경추적플랫폼

강운덕

2015-04-18

Page 2: Pinpoint spring_camp 2015

2

발표자

• Backend Java developer• 2006~

• JAVA FRAMEWORK개발/지원

• 비동기 JAVA 네크워크라이브러리

• JAVA 트러블슈팅• Heap dump, thread dump• Open source patch, Debugging• Multi Thread, Concurrency

• Pinpoint Technical Leader

Page 3: Pinpoint spring_camp 2015

3

발표자

Page 4: Pinpoint spring_camp 2015

4

목차

• Why

• 소개

• 기술개요

• 어려운점

• 로드맵

Page 5: Pinpoint spring_camp 2015

5

Why

A long time ago. in a galaxy far, far away

Page 6: Pinpoint spring_camp 2015

6

Why

NOW

Page 7: Pinpoint spring_camp 2015

7

Why

• 상황• 수십수백대의서버

• 많은소프트웨어모듈

• 복잡하게연동된서비스

• 문제• 어떻게연동되고있는지파악안됨

• 다른서비스에의해장애가발생

• 개별서버에대한모니터링으로는연관관계파악이안됨.

• 새로운해결책이필요

Page 8: Pinpoint spring_camp 2015

8

Why

Page 9: Pinpoint spring_camp 2015

9

Why

Page 10: Pinpoint spring_camp 2015

10

Why

Page 11: Pinpoint spring_camp 2015

11

Why

Page 12: Pinpoint spring_camp 2015

12

Why

Page 13: Pinpoint spring_camp 2015

13

Why

• 트러블슈팅이힘들다.

• 콘솔 들어가기 싫다

• 수십기가의 로그

• 환경, 옵션

• 수많은 종류의 라이브러리 + 다양한 버전의 라이브러리

• 야근하기 싫다

• 자신을자동화

• 트러블슈팅시 생각하던 바, 관찰하던뷰를 다른 사람에게도 제공

• 글로 문제를 설명하려니 어렵더라

• 잘못된 분석으로 인해 문제가 재발

Page 14: Pinpoint spring_camp 2015

14

Why

• 트러블슈팅을계속하다보니안정화되어할일이줄음

Page 15: Pinpoint spring_camp 2015

15

You’re Fired!!!

Page 16: Pinpoint spring_camp 2015

16

진정한 해결책

• http://www.hanbit.co.kr/events/eventview.html?event_id=freebook

Page 17: Pinpoint spring_camp 2015

17

소개

http://github.com/naver/pinpoint

• 분산트랜잭션 추적

• 애플리케이션 토폴로지 자동 발견

• 수평확장성

• 코드 수준의 가시성

• 코드를 수정하지 않고 성능정보 수집

Page 18: Pinpoint spring_camp 2015

18

소개

Page 19: Pinpoint spring_camp 2015

19

풀어야 할 문제

RPC 추적의 의미

Node1과 Node2 사이의 RPC간의관계를 어떻게 찾을것인가?

메시지를 연관관계를 나타내는 TAG

Page 20: Pinpoint spring_camp 2015

20

TAG 동작

• Span : RPC 추적을 위한 기본 단위. RPC가 도착했을 때 처리한 작업

• Trace : 연관된 Span의 집합. Span의 집합은 TransactionId가같음.

Trace는 SpanId와 ParaentSpanID를 통해 트리 구조로 정렬됨

• TraceId

• TrasantionId는 message id로 전체 서버군에서 unique 한 아이디• SpanId, ParentId로 message의관계를 정렬

Page 21: Pinpoint spring_camp 2015

21

Bytecode instrumentation

Page 22: Pinpoint spring_camp 2015

22

설치

• Java 실행시 JavaAgent 설정추가

-javaagent:$PINPOINT_PATH/pinpoint-bootstrap-$VERSION.jar

-Dpinpoint.applicationName=“$AGENT_GROUP_NAME”

-Dpinpoint.agentId =“$AGENT_UNIQUEUE_ID”

Page 23: Pinpoint spring_camp 2015

23

핵심가치

• 분산 RPC 추적

• 코드를수정하지않음

Page 24: Pinpoint spring_camp 2015

24

TomcatA

어플리케이션 적용 예

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Hello world! sample

Page 25: Pinpoint spring_camp 2015

25

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 26: Pinpoint spring_camp 2015

26

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 27: Pinpoint spring_camp 2015

27

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 28: Pinpoint spring_camp 2015

28

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 29: Pinpoint spring_camp 2015

29

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 30: Pinpoint spring_camp 2015

30

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

어플리케이션 적용 예

Page 31: Pinpoint spring_camp 2015

31

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• TraceId 생성

TRANSACTION_ID : 전체 RPC 호출을 하나로 묶을수 있는 Key

SPAN_ID : 나의 ID

PARENT_SPAN_ID : 부모의 ID

내부동작

Page 32: Pinpoint spring_camp 2015

32

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• TraceId 생성

TRANSACTION_ID : TomcatA^시작시간^1

SPAN_ID : 10 (Random)

PARENT_SPAN_ID : -1 (ROOT)

내부동작

Page 33: Pinpoint spring_camp 2015

33

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• Spring Controller Method 레코딩

내부동작

Page 34: Pinpoint spring_camp 2015

34

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• HttpClient.execute()의호출을 가로채서 HttpGet에 TRACE_ID를 세팅한다.

• Child TraceId 생성

TRANSACTION_ID : TomcatA^시작시간^1 -> TomcatA^시작시간^1

SPAN_ID : 10 -> 20

PARENT_SPAN_ID : -1 -> 10

• Child TraceId 를 HttpGet에세팅

HttpGet.setHeader(PINPOINT_TRANSACTION_ID, “TomcatA^시작시간^1”)

HttpGet.setHeader(PINPOINT_SPAN_ID, “20”)

HttpGet.setHeader(PINPOINT_PARENT_SPAN_ID, “10”)

내부동작

Page 35: Pinpoint spring_camp 2015

35

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• TAG된 Request를 TomcatB로전송.

Tag

Request

내부동작

Page 36: Pinpoint spring_camp 2015

36

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

• TomcatB accept

Check Header : HttpServletRequest.getHeader(PINPOINT_TRANSACTION_ID)

• Header에서 TraceId 를 인식하여 Child로 동작

TRANSACTION_ID : TomcatA^시작시간^1

SPAN_ID : 20

PARENT_SPAN_ID : 10

Tag

Request

내부동작

Page 37: Pinpoint spring_camp 2015

37

TomcatA@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

HBase

TRANSACTION_ID : TomcatA^시작시간^1

SPAN_ID : 20

PARENT_SPAN_ID : 10

Collector

RowKey

TomcatA^시작시간^1

20

10Hello() 호출정보

TraceData

내부동작

Page 38: Pinpoint spring_camp 2015

38

TomcatA

@Controller

public class TestController {

@RequestMapping("/test")

@ResponseBody

public String test() throws IOException {

HttpGet get = new HttpGet("http://TomcatB/hello");

HttpResponse response = httpClient.execute(get);

return EntityUtils.toString(response.getEntity());

}

}

TomcatB

@Controller

public class HelloController {

@RequestMapping("/hello")

@ResponseBody

public String hello() {

return "world!";

}

}

• Distributed Transaction TAG

HBase

TRANSACTION_ID : TomcatA^시작시간^1

SPAN_ID : 10

PARENT_SPAN_ID : -1

Collector

RowKey

TomcatA^시작시간^1

20 10

10Hello() 호출정보

-1Test() 호출정보

TraceData

내부동작

Page 39: Pinpoint spring_camp 2015

39

RowKey

TomcatA^시작시간^1

20 10

10Hello() 호출정보

-1Test() 호출정보

HBase

UI

내부동작

Page 40: Pinpoint spring_camp 2015

40

트러블 슈팅

• 인프라의 각 구성요소가 정상적으로구축이 되었는가?

• 해외Proxy에서 한국의 특정 서버로사용자 요청의 흐름이 기대한 대로 인가?

• 응답시간이 느린 구간과 API가 있는가?

• 느린 구간에 대한 프로파일링 데이타 제공

• 성능 패턴 데이타 제공

• 개발->QA->운영단계에서의 연속적인 확인

Page 41: Pinpoint spring_camp 2015

41

어려운점

Page 42: Pinpoint spring_camp 2015

42

WARNING

• HBase도죽고

• HBASE-7711 https://issues.apache.org/jira/browse/HBASE-7711

• Hadoop 무한루프까지

• HDFS-5225 https://issues.apache.org/jira/browse/HDFS-5225

• HBase, Hadoop 클러스터를재시작해도회복이안됨

Page 43: Pinpoint spring_camp 2015

43

WARNING

Page 44: Pinpoint spring_camp 2015

44

WARNING

• 가능한최신버전을 Hadoop 패밀리사용

• 관리되는 Cloudera(CDH), Hortonworks(HDP) 권장

• Pinpoint를사용하는사람은 Java 개발자

• 겸사겸사 HBase, Hadoop도살펴보고, 기왕이면소스도까보자

• 사실저도다 모릅니다.

- 뭐든지 아는건 아니야. 알고 있는것만알뿐

Page 45: Pinpoint spring_camp 2015

45

WARNING

혹시아나요?

HBase, Hadoop, Zookeeper의전문가가될지

망할거같지는않잖아요

개인의노력을투자해봅시다

Page 46: Pinpoint spring_camp 2015

46

WARNING

지금여러분이전문가가될 수도있지않을까요?

Page 47: Pinpoint spring_camp 2015

47

WARNING

사실그걸노리고선택한 Backend입니다.

Page 48: Pinpoint spring_camp 2015

48

다행인 점

• 설계사상

• Pinpoint의 backend가죽어도 Application은장애가발생하면

안된다.

• Collector

• HBase

• Hadoop

• 최우선순위는성능데이터수집 X

• Application이정상적으로돌아가는게최우선

Page 49: Pinpoint spring_camp 2015

49

다행인 점

• 뭐든그렇지만말로는다됨

• 큰소리땅땅~~

“아! 걱정마시라깐요. 저를 믿으셈.

모든건 계획대로~”

Page 50: Pinpoint spring_camp 2015

50

다행인 점

Page 51: Pinpoint spring_camp 2015

51

다행인 점

• 검증되었습니다

• 직접체험

• 한계는분명. 버그는커버불가능

• 개발에서검증 -> 운영

• 버그가 발생하면 신고해주세요. 큰 도움이 됩니다.

• 버그는 계획에 없었던 일

Page 52: Pinpoint spring_camp 2015

52

로드맵

• 알람 / Admin

• 실시간데이터

• EndUser 모니터링

• 지원라이브러리추가

• 개발가이드

• D2 Open Seminar (Java성능 + Pinpoint) 5월?

Page 53: Pinpoint spring_camp 2015

53

감사합니다.

http://github.com/naver/pinpoint

http://helloworld.naver.com/helloworld

/1194202