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


Archive»


 
 




Kubernetes #1 - 소개

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

배경

도커와 쿠버네티스를 알게 된건 수년전인데, 근래에 들어서 다시 쿠버네티스를 보기 시작하였다.

컨테이너 기반의 환경은 배포에 장점이 있고 마이크로 서비스 아키텍쳐 구조에 잘 맞아들어가는 듯 싶지만, 컨테이너가 약간 빠르다는 장점은 있지만, 가상 머신으로도 충분히 패키징이 가능하고, 로컬의 개발환경을 동기화 시키는 장점은 vagrant 로도 충분하다는 생각을 가지고 있었다.


그리고 결정적으로 도커 컨테이너를 운용하기 위한 컨테이너 관리 환경이 그다지 성숙하지 못했었다. Mesosphere, Swarm, Kubernetes 등 다양한 환경이 나오기는 하였지만 기능적으로 부족한 부분도 많았고, 딱히 어떤 플랫폼이 대세라고 정해진것도 없었다.


마이크로 서비스 아키텍쳐 발전

그러나 근래에 들어서 재미있어지는 현상이 마이크로 서비스 아키텍쳐가 단순 개념에서 부터 점점 더 발전하기 시작하였고, 디자인 패턴과 이를 구현하기 위한 다양한 인프라 플랫폼들이 소개되기 시작하였다.

또한 서비스가 점점 작아지면서, 1~2 코어로도 운영할 수 있는 작은 서비스들이 다수 등장하게 되었고 이런 작은 서비스는 VM 환경으로 운영하기에는 낭비가 너무 심하다. (VM 이미지 크기도 너무 크고, 다양한 이미지를 VM으로 관리 배포하기에는 배포 속도등 다양한 문제가 발생한다.)


솔루션의 발전

배포 방식도 예전에 서버에 계속해서 애플리케이션 코드만 업데이트 하는 방식이 아니라, VM이나 컨테이너 단위로 배포하는 피닉스 서버 패턴과 이를 구현하기 위한 Spinnaker  와 같은 솔루션이 나오고 있고, 지능형 라우팅과 분산 트렌젝션 로그 추적을 하는 기능들이 Envoy 라는 솔루션으로 나오고 이를 중앙 통제하기 위한 Istio.io 와 같은 서비스 메쉬 솔루션 까지 나오기에 이르렀다.


데브옵스 모델의 성숙화

데브옵스 모델도 나온지는 오래되었지만, 운영을 데브옵스라는 이름으로 바꾼 것일뿐 실제적인 변화가 없는 팀들이 많았고, 또는 데브옵스라는 이름아래에서 개발팀이 개발과/운영 역할을 병행해서 하는 사례가 오히려 많았다.

이런 데브옵스의 개념도 근래에 들어서 정리가 되어가고 있는데, 개발팀이 개발과 시스템에 대한 배포/운영을 담당한다면, 데브옵스팀은 개발팀이 이를 쉽게할 수 있는 아랫단의 플랫폼과 자동화를 하는데 목표를 두는 역할로 역할이 명확해지고 있다.


이러한 배경에서 슬슬 컨테이너 기반의 환경이 실질적으로 적용될만하다는 것으로 판단하였고, 다시 컨테이너 환경에 대해서 살펴보기 시작하였다.

왜 하필이면 쿠버네티스인가?

그렇다면 Swarm,Mesosphere 가 아니라 왜 하필이면 쿠버네티스인가? 컨테이너 운용 환경은 여러 오픈소스에 의해서 표준이 없이 혼돈이었다가 작년말을 기점으로 해서 쿠버네티스가 de-facto 표준으로 되어가는 형국이다. 아래 트랜드 그래프에서 보면 알 수 있듯이 쿠버네티스의 트랜드가 지속적으로 올라가서 가장 높은 것을 확인할 수 있다.



또한 주요 클라우드 벤더인 아마존,구글,애저 모두 컨테이너 관리 환경을 쿠버네티스를 지원하는 정책으로 변화된것은 물론이고 IBM이나 시스코와 같은 온프렘(on-premise) 솔루션 업체들도 경쟁적으로 쿠버네티스를 지원하고 있다.

컨테이너 운영환경이 무엇인데?

컨테이너 (도커)에 필요성과 마이크로 서비스의 관계등에 대해서는 워낙 소개된 글들이 많아서 생략한다. 그렇다면 쿠버네티스가 제공하는 컨테이너 운영환경이란 무엇인가? 이를 이해하기 위해서는 먼저 컨테이너에 대해서 이해할 필요가 있는데, 컨테이너의 가장 대표적인 예로는 도커가 있다. 도커에 대한 자세한 설명은 링크를 참고하기 바란다.


그러면 단순하게 도커 컨테이너를 하드웨어나 VM에 배포하면 사용하면 되지 왜 컨테이너 운영환경이 필요한가?


작은 수의 컨테이너라면 수동으로 VM이나 하드웨어에 직접 배포하면 되지만, VM이나 하드웨어의 수가 많아지고 컨테이너의 수가 많아지면, 이 컨테이너를 어디에 배포해야 하는지에 대한 결정이 필요하다.

16 CPU, 32 GB 메모리 머신들에 컨테이너를 배포할때 컨테이너 사이즈가 2 CPU, 3 CPU, 8 CPU등 다양할 수 있기 때문에, 자원을 최대한 최적으로 사용하기 위해서 적절한 위치에 배포해야 하고, 애플리케이션 특성들에 따라서, 같은 물리 서버에 배포가 되어야 하거나 또는 가용성을 위해서 일부러 다른 물리서버에 배포되어야 하는 일이 있다. 이렇게 컨테이너를 적절한 서버에 배포해주는 역할을 스케쥴링이라고 한다.


이러한 스케쥴링 뿐만이 아니라 컨테이너가 정상적으로 작동하고 있는지 체크하고 문제가 있으면 재 기동등을 해주고, 모니터링, 삭제관리등 컨테이너에 대한 종합적인 관리를 해주는 환경이 필요한데, 이를 컨테이너 운영환경이라고 한다.

쿠버네티스란?

이런 컨테이너 운영환경중 가장 널리 사용되는 솔루션이 쿠버네티스 (Kubernetes, 약어로 k8s)라고 한다.

구글은 내부 서비스를 클라우드 환경에서 운영하고 있으며, 일찌감치 컨테이너 환경을 사용해왔다. 구글의 내부 컨테이너 서비스를 Borg라고 하는데, 이 구조를 오픈소스화한것이 쿠버네티스이다.

GO 언어로 구현이되었으며, 특히 재미있는 것은 벤더나 플랫폼에 종속되지 않기 때문에, 대부분의 퍼블릭 클라우드 (구글,아마존,애저)등에 사용이 가능하고 오픈 스택과 같은 프라이빗 클라우드 구축 환경이나 또는 베어메탈 (가상화 환경을 사용하지 않는 일반 서버 하드웨어)에도 배포가 가능하다.

이런 이유 때문에 여러 퍼블릭 클라우드를 섞어서 사용하는 환경이나 온프렘/퍼블릭 클라우드를 혼용해서 쓰는 환경에도 동일하게 적용이 가능하기 때문에 하이브리드 클라우드 솔루션으로 많이 각광 받고 있다.


흔히들 컨테이너를 이야기 하면 도커를 떠올리기 쉬운데, 도커가 물론 컨테이너 엔진의 대표격이기는 하지만 이 이외도 rkt나 Hyper container(https://hypercontainer.io/) 등 다양한 컨테이너 엔진들이 있으며, 쿠버네티스는 이런 다양한 컨테이너 엔진을 지원한다.

컨테이너 환경을 왜 VM에 올리는가?

온프렘 환경(데이타센터)에서 쿠버네티스를 올릴때 궁금했던점 중의 하나가, 바로 베어메탈 머신위에 쿠버네티스를 깔면 되는데, 보통 배포 구조는 VM(가상화 환경)을 올린 후에, 그 위에 쿠버네티스를 배포하는 구조를 갖는다. 왜 이렇게 할까 한동안 고민을 한적이 있었는데, 나름데로 내린 결론은 하드웨어 자원 활용의 효율성이다. 컨테이너 환경은 말그대로 하드웨어 자원을 컨테이너화하여 isolation 하는 기능이 주다. 그에 반해 가상화 환경은 isolation 기능도 가지고 있지만, 가상화를 통해서 자원 , 특히 CPU의 수를 늘릴 수 있다.


예를 들어 설명하면, 8 CPU 머신을 쿠버네티스로 관리 운영하면, 8 CPU로밖에 사용할 수 없지만, 가상화 환경을 중간에 끼면, 8 CPU를 가상화 해서 2배일 경우 16 CPU로, 8배일 경우 64 CPU로 가상화 하여 좀 더 자원을 잘게 나눠서 사용이 가능하기 때문이 아닌가 하는 결론을 내렸다.

이 이외에도 스토리지 자원의 활용 용이성이나 노드 확장등을 유연하게 할 수 있는 장점이 있다고 한다.


다음 글에서는 쿠버네티스를 구성하는 컴포넌트들의 구성과 개념에 대해서 설명하도록 한다.




Envoyproxy

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

배경

마이크로 서비스 아키텍쳐가 발전하면서 서비스간의 통신을 라우팅하는 요건이 많아지면서 이를 소프트웨어 단이 아리나 인프라 단에서 처리할 수 있는 기술로 프록시 서버가 매우 유용하다. 기존의 대표적인 프록시 솔루션으로는 nginx, haproxy, apache 서버등이 있는데, 이러한 프록시들은 보통 TCP/IP 레이어에서 L4 로 작동을 하였다. 그러나 마이크로 서비스에서는 조금더 복잡한 라우팅 요건이 필요한데 예를 들어서 HTTP URL에 따른 라우팅에서 부터, HTTP Header를 이용한 라우팅등 다양한 요건이 필요해지면서 L4보다는 애플리케이션 레이어인 L7 기능이 필요해지게 되었다.

마이크로 서비스 아키텍처

특히 마이크로 서비스 아키텍쳐 (이하 MSA) 가 유행하면서 서비스간 라우팅이나 인증등 여러 기능들이 소프트웨어 레이어에서 구현이 되었는데, (넷플릭스 OSS가 대표적인 사례) 이 경우 서비스를 개발하는 각팀의 능력에 따라서 아키텍쳐의 성숙도의 편차가 크게 되었고, 소프트웨어가 특정 기술에 종속성을 가질 수 있는 문제점이 있었다.

특히 서비스간 라우팅, 헬스체크등 서비스간의 통제 기능은 궂이 애플리케이션 코드단에서 구현을 하지 않더라도 프록시 서버와 같은 인프라 서버를 이용해서 구현이 가능하다


아래 그림과 같이 서비스 사이에 프록시를 위치 시키게 되면, 이 프록시가 서비스간의 부하 분산, 로그 수집, Circuit breaker와 같은 다양한 기능을 수행할 수 있다.


Envoy Proxy

이런 배경에 맞춰서 마이크로 서비스 아키텍쳐에 적절한 프록시로 envoy 라는 프록시가 2016년에 소개되었다. Lyft사에서 개발되었으면 오픈소스로 공개되었다.

기존 프록시 L4기능 뿐 아니라 L7 기능도 지원하면서 HTTP 뿐아니라 HTTP 2.0,TCP,gRPC까지 다양한 프로토콜을 지원한다.


성능 지표를 보면 아래 Twillo에서 2017년에 테스트 한 자료를 참고할만 한데, (원본 https://www.twilio.com/blog/2017/10/http2-issues.html) HAProxy 보다 약간 느린것을 확인할 수 있다. 아무래도 L4가 아닌 L7단의 로드밸런서이다 보니 다소 성능 감소는 부담해야 한다.




(참고. 위의 문서를 보면 Envoy HTTP2 의 성능이 낮게 나오는데, 이는 Envory 자체 문제라가 보다는 HTTP/2가 Connection을 reuse하는 특성에서 온다고 볼 수 있는데, 성능에 대한 이슈가 있는 만큼 HTTP/2를 사용할 경우에는 별도의 검증 등이 필요하리라 본다.)


주요 기능적인 특성을 보면 다음과 같다.


  • HTTP, TCP, gRPC 프로토콜을 지원

  • TLS client certification 지원

  • HTTP L7 라우팅 지원을 통한 URL 기반 라우팅, 버퍼링, 서버간 부하 분산량 조절등

  • HTTP2 지원

  • Auto retry, circuit breaker, 부하량 제한등 다양한 로드밸런싱 기능 제공

  • 다양한 통계 추적 기능 제공 및 Zipkin 통합을 통한 MSA 서비스간의 분산 트렌젝션 성능 측정 제공함으로써 서비스에 대한 다양한 가시성 (visibility)을 제공

  • Dynamic configuration 지원을 통해서, 중앙 레파지토리에 설정 정보를 동적으로 읽어와서 서버 재시작없이 라우팅 설정 변경이 가능함

  • MongoDB 및 AWS Dynamo 에 대한 L7 라우팅 기능 제공


등 매우 다양한 기능을 제공한다.


Envoy 배포 아키텍처

Envoy 프록시는 배포 위치에 따라서 다양한 기능을 수행할 수 있는데, 크게 다음과 같이 4가지 구조에 배포가 가능하다.



Front envoy proxy

특정 서비스가 아니라, 전체 시스템 앞의 위치하는 프록시로, 클라이언트에서 들어오는 호출을 받아서 각각의 서비스로 라우팅을 한다. URL 기반으로 라우팅을 하는 기능 이외에도, TLS(SSL) 처리를 하는 역할들을 할 수 있다. 통상적으로 nginx나 apache httpd가 리버스프록시로 이 용도로 많이 사용되었다.

Service to service ingress listener

특정 서비스 앞에 위치하는 배포 방식으로 서비스로 들어오는 트래픽에 대한 처리를 하는데, 트래픽에 대한 버퍼링이나 Circuit breaker 와 같은 역할을 수행한다.

Service to service egress listener

특정 서비스 뒤에서 서비스로부터 나가는 트래픽을 통제 하는데, 서비스로 부터 호출 대상이 되는 서비스에 대한 로드 밸런싱, 호출 횟수 통제 (Rate limiting)와 같은 기능을 수행한다.

External service egress listener

내부서비스에서 외부 서비스로 나가는 트래픽을 관리하는 역할인데, 외부 서비스에 대한 일종의 대행자(Delegator)와 같은 역할을 한다.


시스템 앞 부분이나 또는 시스템을 구성하는 서비스의 앞뒤에 배치할 수 있는 구조지만, 서비스 앞뒤로 붙는다고 실제로 배포를 할때 하나의 서비스 앞뒤로 두개의 envoy proxy를 배치하지는 않는다.

다음과 같이 하나의 서비스에 하나의 Envoy를 배치 한후, ingress/egress 두 가지 용도로 겸용해서 사용한다.



Envoy 설정 구조

다음은 Envoy 설정 파일을 살펴 보자

Envoy의 설정은 크게 아래 그림과 같이 크게 Listener, Filter, Cluster 세가지 파트로 구성된다.



  • Listener
    Listener는 클라이언트로 부터 프로토콜을 받는 부분으로, TCP Listener, HTTP Listener 등이 있다.

  • Filter
    Filter는 Listener 로 부터 많은 메시지를 중간 처리하는 부분으로, 압축이나 들어오는 Traffic 에 대한 제한 작업등을 한후, Router를 통해서 적절한 클러스터로 메시지를 라우팅 하는 역할을 한다.

  • Cluster
    Cluster는 실제로 라우팅이 될 대상 서버(서비스)를 지정한다.


이렇게 Listener를 통해서 메시지를 받고, Filter를 이용하여 받은 메시지를 처리한 후에, 라우팅 규칙에 따라서 적절한 Cluster로 라우팅을 해서 적절한 서비스로 메시지를 보내는 형식이다.


Envoy 설치

Envoyproxy를 빌드하고 설치하는 방법은 여러가지가 있다. 소스코드로 부터 빌드를 하는 방법이나 이미 빌드된 바이너리를 사용해서 설치하는 방법 그리고 이미 빌딩된 도커 이미지를 사용하는 방법이 있다.

소스코드로 빌드하는 방법의 경우에는 bazel (make와 같은 빌드 도구) 빌드를 이용해서 빌드해야 하고, 빌드된 바이너리는 특정 플랫폼에 대해서만 미리 빌드가 되어 있기 때문에, 모든 플랫폼에 사용하기가 어렵다.

마지막으로는 도커 이미지 방식이 있는데, 이 방식이 배포면에서 여러모로 편리하기 때문에 도커 이미지를 이용한 배포 방식을 설명하도록 하겠다.


다음 명령어 처럼

docker pull을 이용하여 envoyproxy 도커 이미지 최신 버전을 가지고 오고, 다음 docker run 명령을 이용하여, 해당 이미지  (envoyproxy/envoy:latest)를 기동한다. 이때 -p 10000:10000 포트를 도커의 10000번 포트를 VM의 10000포트로 포워딩하도록 설정한다.


$ docker pull envoyproxy/envoy:latest
$ docker run --rm -d -p 10000:10000 envoyproxy/envoy:latest
$ curl -v localhost:10000


배포가 끝났으면, curl을 이용하여 localhost:10000번에 호출 하는 테스트를 하도록 한다.

설정에는 디폴트로, 10000 번 포트로 들어오는 모든 트래픽을 *.google.com으로 라우팅 하도록 설정되어 있다.


원본 설정 파일은 https://github.com/envoyproxy/envoy/blob/master/configs/google_com_proxy.v2.yaml 에 있고,  상세 내용을 보면 아래와 같다.


  • admin:
    이 부분은 envoyproxy의 admin 서버를 기동하는 부분으로, envoy 서버의 각종 설정이나 상태 정보를 127.0.0.1:9901로 들어오는 요청은 admin 기능으로 라우팅하도록 한다.

  • static_resources:
    Listener와 Filter 설정에 해당하는 부분으로, 아래 부면, listeners로 정의가 되어 있고 socket_address 부분에 0.0.0.0에 포트 10000 으로 들어오는 요청을 처리하도록 하였다.

    다음 filter_chain 부분에 filter들을 연속해서 정의하는데, http_connection_manager를 이용하여 모든 트래픽을 service_google이라는 클러스터로 라우팅 하도록 설정하였다.

  • clusters:
    마지막으로 clusters 부분에는 “service_google”이라는 클러스터를 정의했으며, 이 호스트의 URL은 google.com 443 포트로 정의하였다.


admin:

access_log_path: /tmp/admin_access.log

address:

  socket_address: { address: 127.0.0.1, port_value: 9901 }


static_resources:

listeners:

- name: listener_0

  address:

    socket_address: { address: 0.0.0.0, port_value: 10000 }

  filter_chains:

  - filters:

    - name: envoy.http_connection_manager

      config:

        stat_prefix: ingress_http

        route_config:

          name: local_route

          virtual_hosts:

          - name: local_service

            domains: ["*"]

            routes:

            - match: { prefix: "/" }

              route: { host_rewrite: www.google.com, cluster: service_google }

        http_filters:

        - name: envoy.router

clusters:

- name: service_google

  connect_timeout: 0.25s

  type: LOGICAL_DNS

  # Comment out the following line to test on v6 networks

  dns_lookup_family: V4_ONLY

  lb_policy: ROUND_ROBIN

  hosts: [{ socket_address: { address: google.com, port_value: 443 }}]

  tls_context: { sni: www.google.com }


참고 자료


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/



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

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

개념

분산 트렌젝션이랑 여러개의 서비스를 걸쳐서 이루어 지는 트렌젝션을 추적하는 기능을 정의한다.

마이크로 서비스 아키텍쳐 (이하 MSA)와 같은 구조에서는 하나의 HTTP 호출이 내부적으로 여러개의 서비스를 거쳐서 일어나게 되는데, 그러면 어느 구간에서 병목이 생기는지 추적하기가 어려워진다.

아래 그림을 보면 클라이언트가 Service A를 호출하고, Service A 가 Service B,D 를, Service B가 Service C를 호출한다.


이렇게 트렌젝션이 여러 컴포넌트의 조합을 통해서 발생하기 때문에 Jennifer와 같은 전통적인 APM (Application Performance Monitoring) 도구를 이용해서 추적하기가 어렵기 때문에 별도의 분산 로그 추적 시스템이라는 것이 필요하다.

작동 원리

그러면 이러한 분산 로그는 어떻게 수집 및 추적하는 것일까? 통상적으로 Trace와 Span 이라는 개념을 사용한다.



클라이언트가 서버로 호출한 하나의 호출을 Trace라고 했을 때, 서비스 컴포넌트간의 호출을 Span이라고 한다.각 서비스 컴포넌트들은 하나의 클라이언트 호출을 추적하기 위해서 같은 Trace Id를 사용하고, 각 서비스간의 호출은 각각 다른 Span Id를 사용한다. 이렇게 함으로써 전체 트렌젝션 시간을 Trace로 추적이 가능하고, 각 서비스별 구간 시간은 Span으로 추적할 수 있다.

솔루션

이러한 분산 로그 추적을 위한 솔루션 중에 오픈소스로는 트위터에서 개발된 ZipKin(https://zipkin.io/) , Jagger(https://jaeger.readthedocs.io/en/latest/) , Opencensus(https://opencensus.io/) 등이 있는데, 이러한 분산 로그 추적은 구글의 Dapper 논문을 기초로 디자인 되어 개발되었다.

Zipkin

그 중에서, 가장 활성화 되어 있는 오픈소스 중 하나가 Zipkin인데, 오픈 소스 생태계가 활발해서 플러그인이나 부가적인 도구들이 많다.

전체적인 구조는 다음과 같다.


<그림 . Zipkin 아키텍쳐 >


지원 프로토콜

Zipkin으로 추적할 수 있는 분산 트렌젝션은 HTTP를 기본으로 지원하고 , 이외에도 많이 사용되는 리모트 프로토콜인 gRPC를 함께 지원한다.

클라이언트 라이브러리

Zipkin 클라이언트 SDK는 https://zipkin.io/pages/existing_instrumentations 에 있는데, Zipkin에서 공식적으로 지원하는 라이브러는 아래와 같이 C#, Go, Java, Javascript,Ruby,Scala 등이 있다.




이외에도 오픈 소스 커뮤니티에서 지원하는 라이브러리로 파이썬, PHP등 대부분의 언어가 지원이 가능하다.

Zipkin 라이브러리는 수집된 트렌젝션 정보를 zipkin 서버의 collector 모듈로 전송한다. 이 때 다양한 프로토콜을 사용할 수 있는데, 일반적으로 HTTP를 사용하고, 시스템의 규모가 클 경우에는 Kafka 큐를 넣어서 Kafka 프로토콜로 전송이 가능하다.

스토리지

Zipkin 클라이언트 SDK에 의해서 전송된 정보는 스토리지에 저장된다.

사용할 수 있는 스토리지는 다음과 같다

  • In-memory

  • MySQL

  • Cassandra

  • Elastic Search

메모리는 별도의 스토리지 설치가 필요없기 때문에 간단하게 로컬에서 테스트할 수 있는 정도로 사용하는 것이 좋고, MySQL은 소규모 서비스에 적절하다. 실제로 운영환경에 적용하려면 Cassandra나 Elastic Search를 저장소로 사용하는 것이 바람직하다.

대쉬 보드

이렇게 수집된 정보는 대쉬 보드를 이용하여 시각화가 가능하다. Zipkin 서버의 대쉬보드를 사용할 수 있고, Elastic Search 백앤드를 이용한 경우에는 Kibana를 이용하여 시각화가 가능하다.


Spring Sleuth

Zipkin 라이브러리 중에서 주목해서 살펴볼 부분은 Spring / Java 지원인데, Spring에서 Sleuth라는 모듈 이름으로 공식적으로 Zipkin을 지원하기 때문에, Spring (& Springboot) 연동이 매우 쉽다.

자바 애플리케이션에서 Trace 정보와 Span 정보를 넘기는 원리는 다음과 같다.


여러개의 클래스의 메서드들을 거쳐서 트렌젝션이 완성될때, Trace 정보와 Span 정보 Context가 유지가 되어야 하는데, 자바 애플리케이션에서는 쓰레드마다 할당되는 쓰레드의 일종의 전역변수인 Thread Local 변수에 이 Trace와 Span Context 정보를 저장하여 유지한다.


분산 트렌젝션은 HTTP나 gRPC로 들어오기 때문에, Spring Sleuth는 HTTP request가 들어오는 시점과 HTTP request가 다른 서비스로 나가는 부분을 랩핑하여 Trace와 Span Context를 전달한다.

아래 그림과 같이 HTTP로 들어오는 요청의 경우에는 Servlet filter를 이용하여, Trace Id와 Span Id를 받고 (만약에 이 서비스가 맨 처음 호출되는 서비스라서 Trace Id와 Span Id가 없을 경우에는 이를 생성한다.)

, 다른 서비스로 호출을 할 경우에는 RestTemplate 을 랩핑하여, Trace Id와 Span Id와 같은 Context 정보를 실어서 보낸다.



HTTP를 이용한 Trace와 Span 정보는 HTTP Header를 통해서 전달되는데


위의 그림과 같이 x-b3로 시작하는 헤더들과 x-span-name 등을 이용하여 컨택스트를 전달한다.

이렇게 ServletFilter와 RestTemplate을 Spring 프레임웍단에서 랩핑해줌으로써, 개발자는 별도의 트레이스 코드를 넣을 필요 없이 Spring을 이용한다면 분산 트렌젝션을 추적할 수 있도록 해준다.


다음글에서는 실제로 Spring Sleuth와 Zipkin을 이용하여 분산로그를 추적하는 예제를 구현해보도록 하겠다.


2015년 개발 트랜드-조대협

IT 이야기/트렌드 | 2015.01.12 10:09 | Posted by 조대협

2015년 개발 트랜드


조대협입니다. 2015년 개발 트렌드에 대해서 간략하게 정리해봅니다. 여러 기술들을 보고 정리한 개인적인 생각이며, 앞으로 저도 집중하려고 하는 분야이기도 합니다.


애자일 및 협업 문화

애자일 과 수평 조직 기반의 개발 문화에 대한 현상은 올해에도 쭈욱 지속될 듯 합니다. 기존의 워터폴이나 경직된 조직 문화와 방법론으로는 현대의 빠른 서비스 개발을 따라갈 수 가 없져

애자일은 워낙 오래전 부터 언급되고 나온거라서 별도로 언급을 하지 않겠습니다만, 왜 이 부분을 2015년의 트랜드로 잡았느냐 하면, 국내 기업의 경우 애자일 프로세스만을 도입하는 것이 아니라, 조직의 구조나 문화 자체를 애자일 사상으로 옮겨가는 경우가 많이 보이기 때문입니다. 기존에 무늬만 애자일이었다면, 작년부터 올해까지는 애자일 문화를 적용하기 위한 직급을 없애고 직책(ROLE) 기반으로 일하기 위한 변화, 수평적 조직 구조, 그리고 스크럼 마스터와 프러덕트 오너등이 조직내에 점점 더 확실하게 자리 잡아 가는 것 같습니다.


MSA 아키텍쳐

작년 중반 부터 떠오르기 시작하더니 국내에도 많은 시스템들이 MSA 사상으로 구현되가고 있는 것들이 보입니다. 이제 시작 단계들로 보이는데, MSA를 적용을 하고 있는 조직들은 MSA가 가지고 있는 전통적인 문제들, 분산 트렌젝션에 대한 처리, 여러개의 API를 모아서 새로운 기능을 만들어내는 aggregation 개념들에서 많은 고민들을 하고 있는 것이 보입니다.

그리고 MSA를 개발하기 위한 개발환경을 셋팅하는데 많은 고민들을 하는데, MSA의 특성상 서버 컴포넌트가 많이 분산이 되고 폴리그랏(다양한 언어로 개발)현상이 조금씩 가속화 됨에 따라서, 이러한 복잡한 개발환경을 어떻게 개발자에게 전달할것인가가 새로운 키워드가 될 듯 합니다.

이에 대한 대안으로는 Docker등이 빠르게 떠오르고 있고, 사내/사외 개발용 클라우드를 구축 하는 움직임이 생기지 않을까 조심스럽게 점쳐 봅니다.

MSA를 적용함에 있어서 앞단에 api gateway (또는 proxy)역할을 하는 것들이 중요해지고 있는데, 현재는 대부분 직접 개발해서 사용하는 경우가 많습니다. 그 만큼 거기에 사용할 제대로된 제품이나 오픈소스가 없다는 것인데, (오픈소스는 현재 WSO2 api gateway, 상용 CA Layer7, 클라우드 기반 서비스 apigee) 아마 금년에는 이러한 needs 때문에 다양한 오픈소스가 나오지 않을까 조심스럽게 기대해봅니다. 2013년까지만 해도 API gateway 오픈 소스 제품들은 손에 꼽을 정도였는데, 작년말에 한번 만들어 볼까 하는 마음으로 살펴보니, 벌써 몇개의 오픈소스들이 시작되고 있더군요

그리고 MSA에 맞춰서, SpringBoot도 같이 올라가면서, 자바 진영의 개발 주류가 되지 않을까 생각해봅니다.


데이타 스트리밍 프로세스

빅데이타 영역은 하둡을 중심으로 어느정도 정리가 되었으나, 근래에 들어서 실시간 데이타 분석에 대한 니즈(needs)가 올라오면서 실시간 스트리밍 처리가 작년말부터 다시 주목 받는것 같습니다. 람다 아키텍쳐나 데이타레이크 아키텍쳐가 다시 언급되는 것도 같은 선상이라고 보는데, 금년에는 Storm,Spark 중심의 실시간 데이타 처리 기술이 다시금 부각되지 않을까 합니다.


머신 러닝의 보편화

머신 러닝은 수학 통계적인 지식이 있어야 접근할 수 있는 분야였지만, 근래에는 Apache Mahout등의 프레임웍으로, 주로 사용되는 머신 러닝 알고리즘 들은 대부분 프레임웍화 되어 있어서 접근이 매우 쉽습니다. 약간의 지식만으로도 머신러닝을 사용할 수 있다는 겁니다.

여기에, Microsoft Azure ML 서비스와, IBM의 왓슨 서비스들은 클라우드 기반으로 머신 러닝 알고리즘을 서비스하는데, 사용이 매우 쉬워서, 일반 개발자들도 쉽게 머신 러닝 알고리즘을 구현 및 운영 환경에 적용이 가능합니다.

다른 빅데이타 분석들도 이런 흐름을 따라가지 않을까 싶은데 제가 보는 관점에서는 ML쪽이 선두가 되서 서비스화되는 현상이 작년말 부터 시작되고, 금년에는 초기 활성화 단계에 들지 않을까 합니다.


폴리 그랏

작년에도 그랬지만, 금년에도 여러가지 프로그래밍 언어를 사용하는 폴리그랏 현상은 더욱 더 가속되지 않을까 합니다. Node.js등은 계속해서 약진할거 같고, Ruby,Groovy와 같은 기존의 스크립트 언어 뿐만 아니라 Google Go, MS가 이번에 Linux까지 자사의 프로그래밍 언어를 지원하겠다고 한 이마당에, 금년에 프로그래밍 언어의 흐름은 지켜볼만 합니다.


기타

자바스크립트의 약진, 자바스크립트 기반의 Pure 웹 클라이언트, 클라우드의 적용 가속화

이런것들은 워낙 뻔한 이야기이니 별도로 언급하지 않겠다. 다만 마지막으로 지켜볼것은 중국 IT 기술의 약진으로, 금년에 중국발 오픈소스나 기술들이 인터넷으로 조금씩 공개되지 않을까 기대해봅니다.