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


Archive»


 
 

Istio #4 - 설치 및 BookInfo 예제

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

Istio 설치

그러면 직접 Istio 를 설치해보자, 설치 환경은 구글 클라우드의 쿠버네티스 환경을 사용한다. (쿠버네티스는 오픈소스이고, 대부분의 클라우드에서 지원하기 때문에 설치 방법은 크게 다르지 않다.)


참고 https://docs.google.com/document/d/1S5EaVR3Xq011JHcJQ0G84hkasboVUNUrcQzBlq9mJ14/edit

쿠버네티스 클러스터 생성

콘솔에서 아래 그림과 같이 istio 라는 이름으로 쿠버네티스 클러스터를 생성한다. 테스트용이기 때문에, 한존에 클러스터를 생성하고, 전체 노드는 3개 각 노드는 4 CPU/15G 메모리로 생성하였다.



다음 작업은 구글 클라우드 콘솔에서 Cloud Shell내에서 진행한다.

커맨드 라인에서 작업을 할것이기 때문에, gCloud SDK를 설치(https://cloud.google.com/sdk/gcloud/) 한후에,

%gcloud auth login

gcloud 명령어에 사용자 로그인을 한다.


그리고 작업을 편리하게 하기 위해서 아래와 같이 환경 변수를 설정한다. 쿠버네티스 클러스터를 생성한 리전과 존을 환경 변수에 아래와 같이 설정한다. 예제에서는 asia-southeast1 리전에 asia-southeast1-c 존에 생성하였다. 그리고 마지막으로 생성한 쿠버네티스 이름을 환경 변수로 설정한다. 예제에서 생성한 클러스터명은 istio이다.

export GCP_REGION=asia-southeast1
export GCP_ZONE=asia-southeast1-c
export GCP_PROJECT_ID=$(gcloud info --format='value(config.project)')
export K8S_CLUSTER_NAME=istio

다음 kubectl 명령어를 사용하기 위해서, 아래과 같이 gcloud 명령어를 이용하여 Credential을 세팅한다

% gcloud container clusters get-credentials $K8S_CLUSTER_NAME \
   --zone $GCP_ZONE \
   --project $GCP_PROJECT_ID

Credential 설정이 제대로 되었는지

% kubectl get pod -n kube-system

명령어를 실행하여, 쿠버네티스 시스템 관련 Pod 목록이 잘 나오는지 확인한다.

Istio 설치

쿠버네티스 클러스터가 준비되었으면, 이제 Istio를 설치한다.

Helm 설치

Istio는 Helm 패키지 매니져를 통해서 설치 한다.

% curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
% chmod 700 get_helm.sh
% ./get_helm.sh

Istio 다운로드

Istio 를 다운로드 받는다. 아래는 1.0.4 버전을 다운 받는 스크립트이다.

% cd ~

% curl -L https://git.io/getLatestIstio | sh -

% cd istio-1.0.4

% export PATH=$PWD/bin:$PATH

Helm 초기화

Istio를 설치하기 위해서 Helm용 서비스 어카운트를 생성하고, Helm을 초기화 한다.

% kubectl create -f install/kubernetes/helm/helm-service-account.yaml

% helm init --service-account tiller

Istio 설치

다음 명령어를 이용하여 Istio를 설치한다. 설치시 모니터링을 위해서 모니터링 도구인 kiali,servicegraph 그리고 grafana 설치 옵션을 설정하여 아래와 같이 추가 설치 한다.

% helm install install/kubernetes/helm/istio \

--name istio \

--namespace istio-system \

--set tracing.enabled=true \

--set global.mtls.enabled=true \

--set grafana.enabled=true \

--set kiali.enabled=true \

--set servicegraph.enabled=true


설치가 제대로 되었는지 kubectl get pod명령을 이용하여, istio 네임스페이스의 Pod 목록을 확인해보자

% kubectl get pod -n istio-system




BookInfo 샘플 애플리케이션 설치

Istio 설치가 끝났으면, 사용법을 알아보기 위해서 간단한 예제 애플리케이션을 설치해보자, Istio에는 BookInfo (https://istio.io/docs/examples/bookinfo/)  라는 샘플 애플리케이션이 있다.

BookInfo 애플리케이션의 구조

아래 그림과 같이 productpage 서비스 안에, 책의 상세 정보를 보여주는 details 서비스와 책에 대한 리뷰를 보여주는 reviews 서비스로 구성이 되어 있다.  


시스템의 구조는 아래와 같은데, 파이썬으로 개발된 productpage 서비스가, 자바로 개발된 review 서비스과 루비로 개발된 details 서비스를 호출하는 구조이며, review 서비스는 v1~v3 버전까지 배포가 되어 있다. Review 서비스 v2~v3는 책의 평가 (별점)를 보여주는  Rating 서비스를 호출하는 구조이다



< 그림 Book Info 마이크로 서비스 구조 >

출처 : https://istio.io/docs/examples/bookinfo/

BookInfo 서비스 설치

Istio의 sidecar injection 활성화

Bookinfo 서비스를 설치하기 전에, Istio의 sidecar injection 기능을 활성화 시켜야 한다.

앞에서도 설명하였듯이 Istio는 Pod에 envoy 를 sidecar 패턴으로 삽입하여, 트래픽을 컨트롤 하는 구조이 다. Istio는 이 sidecar를 Pod 생성시 자동으로 주입 (inject)하는 기능이 있는데, 이 기능을 활성화 하기 위해서는 쿠버네티스의 해당 네임스페이스에 istio-injection=enabled 라는 라벨을 추가해야 한다.

다음 명령어를 이용해서 default 네임 스페이스에 istio-injection=enabled 라벨을 추가 한다.

% kubectl label namespace default istio-injection=enabled


라벨이 추가되었으면

% kubectl get ns --show-labels

를 이용하여 라벨이 제대로 적용이 되었는지 확인한다.


Bookinfo 애플리케이션 배포

Bookinfo 애플리케이션의 쿠버네티스 배포 스크립트는 samples/bookinfo 디렉토리에 들어있다. 아래 명령어를 실행해서 Bookinfo 앺ㄹ리케이션을 배포하자.

% kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml


배포를 완료한 후 kubectl get pod 명령어를 실행해보면 다음과 같이 productpage, detail,rating 서비스가 배포되고, reviews 서비스는 v1~v3까지 배포된것을 확인할 수 있다.



Kubectl get svc 를 이용해서 배포되어 있는 서비스를 확인하자



Prodcutpcage,rating,reviews,details 서비스가 배포되어 있는데, 모두 ClusterIP 타입으로 배포가 되어 있기 때문에 외부에서는 접근이 불가능하다.


Istio gateway 설정

이 서비스를 외부로 노출 시키는데, 쿠버네티스의 Ingress나 Service는 사용하지 않고, Istio의 Gateway를 이용한다.

Istio의 Gateway는 쿠버네티스의 커스텀 리소스 타입으로, Istio로 들어오는 트래픽을 받아주는 엔드포인트 역할을 한다. 여러 방법으로 구현할 수 있으나, Istio에서는 디폴트로 배포되는 Gateway는 Pod 형식으로 배포되어 Load Balancer 타입의 서비스로 서비스 된다.


먼저 Istio Gateway를 등록한후에, Gateway를 통해 서비스할 호스트를 Virtual Service로 등록한다.


아래는 bookinfo에 대한 Gateway를 등록하는 Yaml 파일이다.


apiVersion: networking.istio.io/v1alpha3

kind: Gateway

metadata:

 name: bookinfo-gateway

spec:

 selector:

   istio: ingressgateway # use istio default controller

 servers:

 - port:

     number: 80

     name: http

     protocol: HTTP

   hosts:

   - "*"


selector를 이용해서 gateway 타입을 istio에서 디폴트로 제공하는 Gateway를 사용하였다. 그리고, HTTP프로토콜을 80 포트에서 받도록 하였다.

다음에는 이 Gateway를 통해서 트래픽을 받을 서비스를 Virtual Service로 등록해야 하는데, 그 구조는 다음과 같다.


apiVersion: networking.istio.io/v1alpha3

kind: VirtualService

metadata:

 name: bookinfo

spec:

 hosts:

 - "*"

 gateways:

 - bookinfo-gateway

 http:

 - match:

   - uri:

       exact: /productpage

   - uri:

       exact: /login

   - uri:

       exact: /logout

   - uri:

       prefix: /api/v1/products

   route:

   - destination:

       host: productpage

       port:

         number: 9080


spec에서 gateways 부분에 앞에서 정의한 bookinfo-gateway를 사용하도록 한다. 이렇게 하면 앞에서 만든 Gateway로 들어오는 트래픽은 이 Virtual Servivce로 들어와서 서비스 디는데, 여기서 라우팅 룰을 정의 한다 라우팅룰은 URL에 때해서 어느 서비스로 라우팅할 지를 정하는데 /productpage,/login,/lougout,/api/v1/products URL은 productpage:9080 으로 포워딩해서 서비스를 제공한다.


Gateway와 Virtual service 배포에 앞서서, Istio에 미리 설치되어 있는 gateway를 살펴보면, Istio default gateway는 pod로 배포되어 있는데, istio=ingressgateway 라는 라벨이 적용되어 있다. 확인을 위해서 kubectl get 명령을 이용해서 확인해보면 다음과 같다.

%kubectl get pod -n istio-system -l istio=ingressgateway



이 pod들은 istio-ingressgateway라는 이름으로 istio-system 네임스페이스에 배포되어 있다. kubectl get svc로 확인해보면 다음과 같다.

%kubectl get svc istio-ingressgateway -n istio-system --show-labels



그러면 bookinfo를 istio gateway에 등록해서 외부로 서비스를 제공해보자

% istioctl create -f samples/bookinfo/networking/bookinfo-gateway.yaml


게이트 웨이 배포가 끝나면, 앞에서 조회한 Istio gateway service의 IP (여기서는 35.197.159.13)에 접속해서 확인해보자

브라우져를 열고 http://35.197.159.13/productpage 로 접속해보면 아래와 같이 정상적으로 서비스에 접속할 수 있다.



모니터링 툴

서비스 설치가 끝났으면 간단한 테스트와 함께 모니터링 툴을 이용하여 서비스를 살펴보자

Istio를 설치하면 Prometheus, Grafana, Kiali,Jaeger 등의 모니터링 도구가 기본적으로 인스톨 되어 있다. 각각의 도구를 이용해서 지표들을 모니터링 해보자

Grafana를 이용한 서비스별 지표 모니터링

Grafana를 이용해서는 각 서비스들의 지표를 상세하게 모니터링할 수 있다.

먼저 아래 스크립트를 사용해서 간단하게 부하를 주자. 아래 스크립트는 curl 명령을 반복적으로 호출하여 http://35.197.159.13/productpage 페이지를 불러서 부하를 주는 스크립이다.


for i in {1..100}; do

curl -o /dev/null -s -w "%{http_code}" http://35.197.159.13/productpage

done


다음 Grafana 웹 콘솔에 접근해야 하는데, Grafana는 외부 서비스로 노출이 안되도록 설정이 되어 있기 때문에 kubectl을 이용해서 Grafana 콘솔에 트래픽을 포워딩 하도록 하자. Grafana는 3000번 포트에서 돌고 있기 때문에, localhost:3000 → Grafana Pod의 3000 번 포트로 트래픽을 포워딩 하도록 설정하자


kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 3000:3000 &


다음 localhost:3000 번으로 접속해보면 다음과 같은 화면을 볼 수 있다.

각 서비스 productpage,review,rating,detail 페이지의 응답시간과 OPS (Operation Per Sec : 초당 처리량)을 볼 수 있다.




각 서비스를 눌러보면 다음과 같이 서비스별로 상세한 내용을 볼 수 있다. 응답 시간이나 처리량에 대한 트렌드나, Request의 사이즈등 다양한 정보를 볼 수 있다.



Jaeger를 이용한 분산 트렌젝션 모니터링

다음은 Jaeger 를 이용해 개별 분산 트렌젝션에 대해서 각 구간별 응답 시간을 모니터링 할 수 있다.

Istio는 각 서비스별로 소요 시간을 수집하는데, 이를 Jaeger 오픈소스를 쓰면 손쉽게 모니터링이 가능하다.

마찬가지로 Jaeger 역시 외부 서비스로 노출이 되지 않았기 때문에, kubectl 명령을 이용해서 로컬 PC에서 jaeger pod로 포트를 포워딩하도록 한다. Jaerger는 16686 포트에서 돌고 있기 localhost:16686 → Jaeger pod:16686으로 포워딩한다.


kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath='{.items[0].metadata.name}') 16686:16686 &


Jaeger UI에 접속해서, 아래는 productpage의 호출 기록을 보는 화면이다. 화면 상단에는 각 호출별로 응답시간 분포가 나오고 아래는 개별 트렉젝션에 대한 히스토리가 나온다.



그중 하나를 선택해보면 다음과 같은 그림을 볼 수 있다.



호출이 istio-ingressgateway로 들어와서 Productpage를 호출하였다.

productpage는 순차적으로 productpage → detail 서비스를 호출하였고, 다음 productpage→ reviews → ratings 서비스를 호출한것을 볼 수 있고, 많은 시간이 reviews 호출에 소요된것을 확인할 수 있다.


Servicegraph를 이용한 서비스 토폴로지 모니터링

마이크로 서비스는 서비스간의 호출 관계가 복잡해서, 각 서비스의 관계를 시각화 해주는 툴이 있으면 유용한데, 대표적인 도구로는 service graph라는 툴과 kiali 라는 툴이 있다. BookInfo 예제를 위한 Istio 설정에는 servicegraph가 디폴트로 설치되어 있다.


마찬가지로 외부 서비스로 노출 되서 서비스 되지 않고 클러스터 주소의 8088 포트를 통해서 서비스 되고 있기 때문에, 아래와 같이 kubectl 명령을 이용해서 localhost:8088 → service graph pod의 8088포트로 포워딩하도록 한다.


kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=servicegraph -o jsonpath='{.items[0].metadata.name}') 8088:8088 &


그 후에, 웹 브루우져에서 http://localhost:8088/dotviz 를 접속해보면 서비스들의 관계를 볼 수 있다.



다음 글에서는 예제를 통해서 Istio에서 네트워크 경로 설정하는 부분에 대해서 더 자세히 알아보도록 하겠다.


ISTIO


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

Envoy를 이용해서 서비스 매쉬를 구현하기 위해서는 Envoy로 구성된 데이타 플레인을 컨트롤할 솔루션이 필요하다. Envoy를 데이타 플레인으로 사용하고 이를 컨트롤 해주는 오픈 소스 솔루션이 Istio 이다. (http://istio.io)

아키텍쳐

먼저 Istio의 구조를 보자


<그림, Istio 아키텍쳐 >

출처 : https://istio.io/docs/concepts/what-is-istio/

데이타 플레인

데이타 플레인은 envoy를 서비스 옆에 붙여서 사이드카 형식으로 배포를 해서, 서비스로 들어오고 나가는 트래픽을 envoy를 통해서 통제하게 된다.

envoy는 서비스에서 서비스로 호출할때 상대편 서비스의 IP 주소를 알아야 하는데, 이를 서비스 디스커버리 (Service discovery : 참고 http://bcho.tistory.com/1252?category=431297 )

그러면 envoy는 서비스들의 IP 주소 (엔드포인트)를 어떻게 알 수 있을까? 서비스들에 대한 엔드포인트 정보는 컨트롤 플레인의 파일럿(Pilot)이라는 컴포넌트에 저장되어 있고, envoy는 이 데이타를 참고하여 엔드포인트를 알 수 있다.

컨트롤 플레인

컨트롤 플레인은 데이타 플레인에 배포된 envoy를 컨트롤 하는 부분으로, 파일럿 (Pilot), 믹서 (Mixer), 시타델(Citadel) 3개의 모듈로 구성이 되어 있다.

파일럿 (Pilot)

파일럿은 envoy에 대한 설정 관리를 하는 역할을 한다.

먼저 앞에서 언급했듯이 서비스들의 엔드포인트(EndPoint)들의 주소를 얻을 수 있는 서비스 디스커버리 기능을 제공한다.

Istio에 유용한 기능중의 하나가 트래픽의 경로를 컨트롤 하는 기능인데, 서비스에서 서비스로 호출하는 경로를 컨트롤 할 수 있다. 이외도 서비스의 안정성을 제공하기 위해서 서비스간에 호출이 발생할때 재시도(retry), 장애 전파를 막기 위한 써킷 브레이커 (Circuit breaker), Timeout 등의 기능을 제공한다.

믹서(Mixer)

믹서가 하는 일은 액세스 컨트롤, 정책 통제 그리고 각종 모니터링 지표의 수집이다.

예를 들어서 서비스의 총 처리량을 정책으로 지정하여, 그 처리량 이상으로 요청을 못받게 하거나 특정 헤더값이 일치해야 요청을 받을 수 있게 하는 등의 다양한 정책을 정의하고 이를 컨트롤 할 수 있다.

또한 서비스의 응답 시간이나 평균 처리량과 같은 다양한 지표를 수집하여 저장하는 역할을 한다.

시타델(Citadel)

시타델은 보안에 관련된 기능을 담당하는 모듈이다. 서비스를 사용하기 위한 사용자 인증 (Authentication)과 인가 (Authorization)을 담당한다. 또한 Istio는 통신을 TLS(SSL)을 이용하여 암호화할 수 있는데, TLS 암호화나 또는 사용자 인증에 필요한 인증서(Certification)을 관리하는 역할을 한다.  

기능

대략적인 구조를 이해했으면, Istio가 어떤 기능을 제공하는지 주요 기능을 살펴보도록 하자.

트래픽 통제

트래픽 분할

트래픽 분할은 서로 다른 버전의 서비스를 배포해놓고, 버전별로 트래픽의 양을 조절할 수 있는 기능이다. 예를 들어 새 버전의 서비스를 배포할때, 기존 버전으로 95%의 트래픽을 보내고, 새 버전으로 5%의 트래픽만 보내서 테스트하는 것이 가능하다. (카날리 테스트)


컨텐츠 기반의 트래픽 분할

단순하게 커넥션 기반으로 트래픽을 분할하는 것이 아니라, 조금 더 발전된 기능으로 네트워크 패킷의 내용을 기반으로 라우팅이 가능하다. 예를 들어 아래 우측 그림과 같이 HTTP 헤더의 User-agent 필드에 따라서, 클라이언트가 안드로이드일 경우에는 안드로이드 서비스로 라우팅을 하고, IPhone일 경우에는 IOS 서비스로 라우팅을 할 수 있다.


서비스간 안정성 제공 (Resilience)

파일럿은 트래픽 통제를 통해서 서비스 호출에 대한 안정성을 제공한다.

헬스체크 및 서비스 디스커버리

파일럿은 대상 서비스가 여러개의 인스턴스로 구성이 되어 있으면 이를 로드 밸런싱하고, 이 서비스들에 대해서 주기적으로 상태 체크를 하고, 만약에 장애가 난 서비스가 있으면 자동으로 서비스에서 제거한다.


Retry,Timeout,Circuit breaker

서비스간의 호출 안정성을 위해서, 재시도 횟수를 통제할 수 있다. 호출을 했을때 일정 시간 (Timeout)이상 응답이 오지 않으면 에러 처리를 할 수 있고, 앞에서 설명한 마이크로 서비스 아키텍쳐 패턴중 하나인 써킷 브레이커 (Circuit breaker) 패턴을 지원한다.

보안

Istio의 특징은 서비스에 대한 보안 기능을 추가해준다가는 것이다.

통신 보안

기본적으로 envoy를 통해서 통신하는 모든 트래픽을 자동으로 TLS를 이용해서 암호화한다. 즉 서비스간의 통신이 디폴트로 암호화 된다.


암호화를 위해서 인증서를 사용하는데, 이 인증서는 시타델(Citadel)에 저장되어 있는 인증서를 다운 받아서, 이 인증서를 이용하여 암호화된 TLS 통신을 한다.

서비스 인증과 인가

Istio는 서비스에 대한 인증 (Authentication)을 제공하는데, 크게 서비스와 서비스간 호출에서, 서비스를 인증하는 기능과, 서비스를 호출하는 클라이언트를 직접인증 할 수 있다.  

서비스간 인증

서비스간 인증은 인증서를 이용하여 양방향 TLS (Mutual TLS) 인증을 이용하여, 서비스가 서로를 식별하고 인증한다.

서비스와 사용자간 인증

서비스간 인증뿐 아니라, 엔드 유저 즉 사용자 클라이언트를 인증할 수 있는 기능인데, JWT 토큰을 이용해서 서비스에 접근할 수 있는 클라이언트를 인증할 수 있다.

인가를 통한 권한 통제 (Authorization)

인증뿐만 아니라, 서비스에 대한 접근 권한을 통제 (Authorization)이 가능하다. 기본적으로 역할 기반의 권한 인증 (RBAC : Role based authorization control)을 지원한다.

앞에서 인증된 사용자(End User)나 서비스는 각각 사용자 계정이나 쿠버네티스의 서비스 어카운트로 계정이 정의 되고, 이 계정에 역할(Role)을 부여해서 역할을 기반으로 서비스 접근 권한을 정의할 수 있다.


apiVersion: "rbac.istio.io/v1alpha1"

kind: ServiceRole

metadata:

 name: products-viewer

 namespace: default

spec:

 rules:

 - services: ["products.default.svc.cluster.local"]

   methods: ["GET", "HEAD"]


예를 들어 위의 역할은 products-viewer라는 권한으로 products.default.svc.cluster.local 서비스에 대해서 HTTP GET, HEAD를 지원하는 권한을 정의한것이다.

이렇게 정의된 역할 (Role)을 계정에 적용해야 하는데 아래는 products-viewer 역할을 서비스 어카운트와 엔드유저 계정에 반영한 예제이다.

apiVersion: "rbac.istio.io/v1alpha1"

kind: ServiceRoleBinding

metadata:

 name: test-binding-products

 namespace: default

spec:

 subjects:

 - user: "service-account-a"

 - user: "istio-ingress-service-account"

   properties:

     request.auth.claims[email]: "a@foo.com"

 roleRef:

   kind: ServiceRole

   name: "products-viewer"


“Service-account-a” 서비스 어카운트에 권한을 부여한 것이고, Istio의 Ingress를 통해서 들어오는 사용자 트래픽에 대해서 사용자 ID가 “a@foo.com”인 사용자에게도 같은 역할을 부여한것이다.


모니터링

마이크로 서비스에서 문제점중의 하나는 서비스가 많아 지면서 어떤 서비스가 어떤 서비스를 부르는지 의존성을 알기가 어렵고, 각 서비스를 개별적으로 모니터링 하기가 어렵다는 문제가 있다. Istio는 네트워크 트래픽을 모니터링함으로써, 서비스간에 호출 관계가 어떻게 되고, 서비스의 응답 시간, 처리량등의 다양한 지표를 수집하여 모니터링할 수 있다.

<그림. Mixer에서 서비스 관련 지표를 수집하는 구조>


서비스 A가 서비스 B를 호출할때 호출 트래픽은 각각의 envoy 프록시를 통하게 되고, 호출을 할때, 응답 시간과 서비스의 처리량이 Mixer로 전달된다. 전달된 각종 지표는 Mixer에 연결된 Logging Backend에 저장된다.


Mixer는 위의 그림과 같이 플러그인이 가능한 아답터 구조로, 운영하는 인프라에 맞춰서 로깅 및 모니터링 시스템을 손쉽게 변환이 가능하다.  쿠버네티스에서 많이 사용되는 Heapster나 Prometheus에서 부터 구글 클라우드의 StackDriver 그리고, 전문 모니터링 서비스인 Datadog 등으로 저장이 가능하다.

이렇게 저장된 지표들은 여러 시각화 도구를 이용해서 시각화 될 수 있는데, 아래 그림은 Grafana를 이용해서 서비스의 지표를 시각화 한 그림이다.





그리고 근래에 소개된 오픈소스 중에서 흥미로운 오픈 소스중의 하나가 Kiali (https://www.kiali.io/)라는 오픈소스인데, Istio에 의해서 수집된 각종 지표를 기반으로, 서비스간의 관계를 아래 그림과 같이 시각화하여 나타낼 수 있다.  아래는 그림이라서 움직이는 모습이 보이지 않지만 실제로 트래픽이 흘러가는 경로로 에니메이션을 이용하여 표현하고 있고, 서비스의 각종 지표, 처리량, 정상 여부, 응답 시간등을 손쉽게 표현해준다.





다음 글에서는 쿠버네티스 클러스터에 Istio를 설치해보고, 마이크로 서비스를 배포해보도록 하겠다.


Istio #2 - Envoy Proxy


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

그럼 앞에서 설명한 서비스 매쉬의 구조를 구현한 Istio를 살펴보기전에, Istio에 사용되는 envoy 프록시에 대해서 먼저 알아보자.
(이 글은 예전에 포스팅한 내용이지만, Istio 글의 흐름상 다시 포스팅 한다.)

Envoy Proxy

먼저 istio에 사용되는 envory proxy를 살펴보자. Envoy 프록시는 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가지 구조에 배포가 가능하다.


<그림. Envoy 배포 방식>

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 }


다음글에서는 Istio에 대해서 알아보도록 하겠다.


Istio #1

마이크로 서비스 아키텍처와 서비스 매쉬

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


마이크로 서비스 아키텍쳐는 여러가지 장점을 가지고 있는 아키텍쳐 스타일이기는 하지만, 많은 단점도 가지고 있다. 마이크로 서비스는 기능을 서비스라는 단위로 잘게 나누다 보니, 전체 시스템이 커질 수 록 서비스가 많아지고, 그로 인해서 서비스간의 연결이 복잡해지고 여러가지 문제를 낳게 된다



<그림. 넷플릭스의 마이크로 서비스 구조 >

출처 : https://www.slideshare.net/BruceWong3/the-case-for-chaos?from_action=save


서비스간의 전체 연결 구조를 파악하기 어려우며 이로 인해서 장애가 났을때, 어느 서비스에서 장애가 났는지 추적이 어려워진다.

또한 특정 서비스의 장애가 다른 서비스에 영향을 주는 문제들을 겪을 수 있다.



예를 들어 클라이언트→ 서비스 A → 서비스 B의 호출 구조가 있다고 하자. 만약 서비스 B가 느려지거나 응답이 없는 상태가 되어 버리면, 서비스 B를 호출 하는 서비스 A 안의 쓰레드는 서비스 B로 부터 응답을 기다리기 위해 대기 상태가 되고, 이 상태에서 클라이언트에서 호출이 계속 되면, 같은 원리로 서비스 A의 다른 쓰레드들도 응답을 받기 위해서 대기 상태가 된다. 이런 상태가 반복되면, 서비스 A에 남은 쓰레드는 없어지고 결과적으로 서비스 A도 응답을 할 수 없는 상태가 되서 장애 상태가 된다. 이런 현상을 장애 전파 현상이라고 한다.  

마이크로 서비스 아키텍쳐 패턴

이런 문제들이 패턴화 되고 이를 풀어내기 위한 방법이 디자인 패턴으로 묶이기 시작하였다.

예를 들어 앞의 문제와 같은 장애 전파의 예는 써킷 브레이커 (Circuit breaker)라는 디자인 패턴으로 해결할 수 있다.



<그림, 써킷 브레이커(Circuit breaker) 패턴 >


서비스 A와 서비스 B에 써킷 브레이커라는 개념을 정의해서, 네트워크 트래픽을 통과 시키도록 하고, 서비스 B가 장애가 나거나 응답이 없을 경우에는 그 네트워크 연결을 끊어서 서비스 A가 바로 에러를 받도록 하는 것이다. 이렇게 하면 서비스 B가 응답이 느리거나 또는 응답을 할 수 없는 상태일 경우에는 써킷 브레이커가 바로 연결을 끊어서, 서비스 A내에서 서비스 B를 호출한 쓰레드가 바로 에러를 받아서 더 이상 서비스 B로 부터 응답을 기다리지 않고, 쓰레드를 풀어주서 서비스 A가 쓰레드 부족으로 장애가 되는 것을 막는다.

이 외에도 분산 시스템에 대한 로그 수집등 다양한 패턴들이 있는데, https://microservices.io/ 를 보면 잘 정리가 되어 있다.

이런 패턴은 디자인 패턴일 뿐이고, 이를 사용하기 위해서는 시스템에서 구현을 해야 하는데, 당연히 구현에 대한 노력이 많이 들어서 구체화 하기가 어려웠는데, 넷플릭스에서 이러한 마이크로 서비스 아키텍쳐 패턴을 오픈소스화 하여 구현하여 공개하였다. 예를 들어 위에서 언급한 써킷 브레이커 패턴의 경우에는 Hystrix (https://github.com/Netflix/hystrix/wiki)라는 오픈 소스로 공개가 되어 있다.

Hystrix 이외에도, 서비스 디스커버리 패턴은 Eureka, 모니터링 서비스인 Turbine 등 다양한 오픈 소스를 공개했다.



<그림. 넷플릭스의 마이크로 서비스 프레임웍 오픈소스 >

출처 : https://jsoftgroup.wordpress.com/2017/05/09/micro-service-using-spring-cloud-and-netflix-oss/


문제는 이렇게 오픈소스로 공개를 했지만, 여전히 그 사용법이 복잡하다는 것이다. Hystrix 하나만을 적용하는데도 많은 노력이 필요한데, 여러개의 프레임웍을 적용하는 것은 여간 어려운 일이 아니다.

그런데 여기서 스프링 프레임웍이 이런 문제를 풀어내는 기여를 한다. 스프링 프레임웍에 넷플릭스의 마이크로 서비스 오픈 소스 프레임웍을 통합 시켜 버린것이다. (http://spring.io/projects/spring-cloud-netflix)

복잡한 부분을 추상화해서 스프링 프레임웍을 적용하면 손쉽게 넷플릭스의 마이크로 서비스 프레임웍을 사용할 수 있게 해줬는데, 마지막 문제가 남게 된다. 스프링은 자바 개발 프레임웍이다. 즉 자바에만 적용이 가능하다.

서비스 매쉬

프록시

이러한 마이크로 서비스의 문제를 풀기 위해서 소프트웨어 계층이 아니라 인프라 측면에서 이를 풀기 위한 노력이 서비스 매쉬라는 아키텍쳐 컨셉이다.

아래와 같이 서비스와 서비스간의 호출이 있을때


이를 직접 서비스들이 호출을 하는 것이 아니라 서비스 마다 프록시를 넣는다.


이렇게 하면 서비스로 들어오거나 나가는 트래픽을 네트워크 단에서 모두 통제가 가능하게 되고, 트래픽에 대한 통제를 통해서 마이크로 서비스의 여러가지 문제를 해결할 수 있다.

예를 들어 앞에서 설명한 써킷 브레이커와 같은 경우에는 호출되는 서비스가 응답이 없을때 프록시에서 이 연결을 끊어서 장애가 전파되지 않도록 하면된다.


또는 서비스가 클라이언트 OS에 따라서 다른 서비스를 호출해야 한다면, 서비스가 다른 서비스를 호출할때, 프록시에서 메세지의 헤더를 보고 “Client”라는 필드가 Android면, 안드로이드 서비스로 라우팅을 하고, “IOS”면 IOS 서비스로 라우팅 하는 지능형 라우팅 서비스를 할 수 있다.


이런 다양한 기능을 수행하기 위해서는 기존의 HA Proxy,nginx, Apache 처럼 TCP 기반의 프록시로는 한계가 있다. 예를 들어서 위에서 언급한 HTTP 헤더 기반의 라우팅이나 조금더 나가면 메세지 본문을 기반으로 하는 라우팅들이 필요하기 때문에, L7 계층의 지능형 라우팅이 필요하다.

서비스 매쉬

그러면 이러한 마이크로 서비스에 대한 문제를 소프트웨어 계층이 아니라, 프록시를 이용해서 인프라 측면에서 풀어낼 수 있다는 것을 알았다. 그렇지만 마이크로 서비스는 한두개의 서비스가 아니라 수백, 수천의 서비스로 구성된다. 프록시를 사용해서 여러 기능을 구성할 수 있지만 문제는 서비스 수에 따라 프록시 수도 증가하기 때문에, 이 프록시에 대한 설정을 하기가 어려워진다는 것이다.



그래서 이런 문제를 해결하기 위해서, 각 프록시에 대한 설정 정보를 중앙 집중화된 컨트롤러가 통제하는 구조를 취할 수 있다. 아래 구조와 같이 되는데,

각 프록시들로 이루어져서 트래픽을 설정값에 따라 트래픽을 컨트롤 하는 부분을 데이타 플레인(Data Plane)이라고 하고, 데이타 플레인의 프록시 설정값들을 저장하고, 프록시들에 설정값을 전달하는 컨트롤러 역할을 하는 부분을 컨트롤 플레인(Control Plane) 이라고 한다.


다음 글에서는 이러한 서비스 매쉬 구조를 구현한 오픈 소스 솔루션인 Istio에 대해서 알아보도록 하겠다.



EAI, ESB, API 게이트 웨이,서비스 매쉬

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


서비스간의 연동은 작게 보면 마이크로 서비스 아키텍쳐로 인한 문제 같지만, 서비스간의 연동은 마이크로 서비스 아키텍쳐 이전에도 자주 있어왔던 전통적인 문제이다. 이러한 문제를 소프트웨어 개발 프레임웍이 아니라, 솔루션 차원에서 풀기 위한 여러가지 노력들이 있었다.


시스템 통합 문제

메인 프레임 시대에서 유닉스 시스템으로 내려오면서 부터 시스템들은 업무 단위로 분리가 되기 시작했다. ERP,CRM 등과 같은 시스템으로, 은행은 대내,대외,정보계와 같이 시스템으로 잘게 잘게 나눠지기 시작했는데, 당연히 이렇게 나눠진 시스템 사이에는 통신이 필요하게 되었고, 시스템이 거대화 되가면서, 시스템간에 직접 P2P로 통신하는 구조는 한계에 다다르기 시작하였다.  



시스템이 서로 얽히고 어느 시스템이 어떤 시스템과 통신하는지 통제가 어렵게 되는 상황이 되었다.

EAI (Enterprise Application Integration)

이러한 시스템간의 문제를 해결하기 위해서 등장한 솔루션이 EAI인데, 통합을 원하는 시스템을 기존에는 직접 1:1로 붙였다면, EAI는 EAI가 중앙에 허브 역할을 하면서, 모든 통신을 EAI를 거치도록 하였다.



EAI의 가장 큰 특징은 표준화 되지 않은 이기종 시스템간의 연동을 가능하게 해준다는 것이다. 메인프레임 → Unix ERP 시스템으로 데이타를 전송하게 한다던지 Oracle → CRM 시스템으로 데이타를 전송해주는 것과 같은 시스템 통합을 지원했는데, 이는 이기종간에 통신 프로토콜이나 통합 방식을 변경할 수 있는 아답터를 제공하기 때문이다. EAI는 복잡한 메세지 처리나 변동, 라우팅같은 다양한 기능을 가지고 있었지만, 주로 이 기종간의 메세지 변환이 가장 많이 사용되었다.

어쨌거나 이런 EAI는 중앙 통제를 통해서 1:1 / 다:다로 통신되는 복잡한 토폴로지를 통합하는 의미가 있다.


EAI 시스템이 점점 더 많아지자. 시스템 통합 아키텍쳐도 패턴화 되었다. EIP (Enterprise Integration Pattern)이라는 형태로 정리되었는데, 아직도 참고할만한 구조가 많으니 관심이 있으면 참고하기 바란다.

https://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html

SOA / ESB

이 기종간의 통합이 많아지고, 시스템이 점점 분리되다 보니, 아예 이를 표준화하고자 하는 작업이 진행되었는데, 이것이 바로 SOA (Service Oriented Archtiecture / 서비스 지향 아키텍쳐) 이다.

SOA 아키텍쳐의 컨셉 자체는 MSA와 유사하지만, XML 기반의 웹서비스와 맞춰져서 웹서비스를 대표하는 아키텍쳐가 되어버렸다.

(사실 SOA는 아키텍쳐 구현 컨셉이지 XML/HTTP를 대표하는 것이 아니지만, 시대적으로 벤더들에 의해 웹서비스로 포장되었다. SOA는 시스템을 서비스로 나눈 다음 표준화된 인터페이스로 통신한다는 컨셉으로, 요즘의 MSA도 이 SOA의 부분 집합이라고 할 수 있다. )


웹서비스 기반으로 통신이 표준화되었기 때문에 서비스간의 통신은 EAI처럼 별도의 아답터가 필요없어졌다. 대신 서비스간의 통신을 서비스 버스라는 통신 백본을 이용하여 통신을 하는 구조가 되었다.




이론적으로는 매우 좋은 구조지만 웹서비스 자체가 스펙이 너무 복잡했고, 그 당시의 컴퓨팅 파워가 복잡한 XML을 파싱하기에는 충분하지 않았기 때문에 그다지 성공하지는 못한 아키텍쳐가 되었다.

특히나 ESB내에서 서비스간의 통신시에, 복잡한 XML 변환등을 사용하다가 ESB에 부하가 걸리게 되고, 제대로된 성능을 내지 못하는 결과를 낳게 되었다.

API Gateway

엔터프라이즈에 의해서 IT가 주도되는 시대가 끝나고 웹과 서비스의 시대가 오면서 개방형 API 즉, 오픈 API가 각광 받는 시대가 오면서 시스템 내부간의 통합도 중요했지만 외부로 API를 서비스 하는 클라이언트 대 서버간의 통합 그리고 외부 서버와 내부 서버와의 통합이 중요한 요건으로 대두되었다.

웹 기반의 서비스들은 복잡한 형태의 메세징을 필요로하지 않았기 때문에 XML을 버리고 상대적으로 간단한 JSON이 메인으로 사용되었고, 이를 HTTP 통신에 사용하면서 HTTP/JSON 기반의 REST 아키텍쳐가 유행하기 시작했다.

그래서  API 게이트 웨이가 외부로 서비스를 제공하기 위한 솔루션으로 제공되고, 시스템 내부간의 통합도 HTTP REST 를 이용하여 마치 ESB처럼 내부 버스로써 내부 시스템 통합을 지원하였다.



그러나 API 게이트웨이의 주요 목적은 앞에서 언급한 시스템간의 통합보다는, 클라이언트와 서버 중간에서 API에 대한 라우팅이나 인증 처리와 같이 내부 API를 외부 서비스로 제공하는데 촛점이 맞추어졌다.

물론 API에 대한 중간 통로 역할을 한다는 의미에서 ESB의 대안 모델로 사용이 가능하기 때문에, API 게이트웨이를 내부 서비스간 통신용으로 위치시키는 아키텍쳐도 있지만,  원래 목적 자체가 API를 외부에 서비스 하기 위한 목적으로 디자인된 아키텍쳐이기 때문에 여러모로 맞지 않는 부분이 있다.

특히나 대부분의 API 게이트 웨이는 게이트 웨이 인스턴스(클러스터)를 포진 시키는 방식이라서 서비스간의 통합 포인트가 많아서 복잡도가 올라가는 경우 API 게이트 웨이에 많은 부하가 걸려서 성능 저하가 발생하고, API 게이트 웨이가 장애가 날 경우 전체 서비스에 영향을 주는 (SPOF : Single Point of Failure) 가 된다는 단점이 있다.  

Service Mesh

이런 단점을 보안한 아키텍쳐가 서비스 매쉬 아키텍쳐이다.

  • API 게이트웨이와는 달리 외부로 서비스를 노출하기 보다는 내부 서비스간의 통신을 조율에 중점을 둔다.

  • 구조적으로 중앙 집중화 구조를 벗어나서, 분산형 아키텍쳐를 취하는 구조이다. 그래서 SPOF를 생성하지 않고 스케일한 서비스를 지원하기 좋다.


그래서 근래의 마이크로 서비스 아키텍쳐에서 안정적인 구조는 외부 서비스는 API 게이트 웨이를 통해서 노출하고, 내부 서비스는 서비스 매쉬를 통해서 통제하는 구조가 된다.



서비스 매쉬의 개념은 다음과 같다.

서비스가 서비스를 호출 하는 구조가 있을때


위의 그림과 같이 서비스가 서비스를 바로 직접 호출 하는 것이 아니라, 아래 그림과 같이 프록시를 둬서, 프록시를 통해서 호출을 하게 하는 구조이다.


이러한 구조를 가지게 되면, 네트워크 트래픽을 컨트롤 하는 것을 서비스의 소스 코드 변경없이도 가능하다. 예를 들어 서비스가 다른 대상 서비스를 호출할때 아래 그림과 같이, HTTP 헤더에서, 클라이언트의 종류에 따라 트래픽을 안드로이드용 서비스나 IOS용 서비스로 라우팅을 할 수 있다.


이러한 구조의 장점은 서비스를 호출하는 쪽이나 호출 받는 쪽이나 소스 코드 변환이 필요 없이 가능하다는 것이다.

서비스 매쉬의 가장 대표적인 솔루션은 istio 라는 솔루션으로 다음 글에서는 Istio에 대해서 설명하도록 하겠다.




마이크로 서비스 아키텍쳐와 컨테이너

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

모노리틱 아키텍처

마이크로 서비스 아키텍쳐를 이해하려면 먼저 이에 상반되는 모노리틱 아키텍쳐를 이해할 필요가 있다. 모노리틱 아키텍쳐는 전통적인 아키텍쳐 스타일로 애플리케이션이 하나의 서버에 배포 되고, 데이타 베이스도 마찬가지로 하나의 데이타 베이스에 모든 데이타를 저장하는 방식이다.


예전에 하나의 큰 서버를 놓고, 그 안에 하나의 애플리케이션으로 개발하는 방식인데, 수퍼돔과 같이 큰 머신을 하나 놓고, 오라클 데이타베이스에 모든 데이타를 저장하고, 애플리케이션 바이너리를 하나로 개발하는 방식이다. 중앙 관리된 구조에서 통제가 편리하고, 같은 솔루션을 사용한다는데 있어서 장점이 있다.

마이크로 서비스 아키텍처

마이크로 서비스 아키텍처 (Micro service architecture,이하 MSA)는 비지니스 기능 마다 애플리케이션 서버와 데이타 베이스 서버를 분리하는 방식이다.

아래 그림과 같이 상품 정보, 상품 리뷰, 상품 상세 정보 서비스를 각각 별도의 애플리케이션으로 나누고 데이타 베이스도 각각 나누는 방식이다.  


이렇게 서버를 나누는 이유는 몇가지가 있는다.

첫번째는 서비스를 개발하는 조직이 다른 경우, 각 조직에 서비스 기획,개발,운영에 대한 독립적인 권한을 부여함으로써, 각 서비스 개발을 할때 다른 서비스에 대한 의존성이 없이 빠르게 개발할 수 있게 하는 목적을 가진다.

두번째는 인프라의 변화에 의한 요인을 들 수 있는데, 기존에는 수억의 고성능 서버에 하나의 애플리케이션을 넣는 방식을 이용했다면 근래에는 x86 서버기반으로 상대적으로 저비용,저성능 서버를 더 많이 사용하는 방식을 이용하기 때문에 하나의 큰 애플리케이션을 만드는 것보다, 작은 애플리케이션으로 나눠서 여러 서버에 분산하는 것이 유리하기 때문이다.

모노리틱 아키텍쳐와 비교

모노리틱 아키텍쳐에 비해서, 마이크로 서비스 아키텍쳐는 앞에서 언급한바와 같이 서비스 개발에 대해서 독립성을 가짐으로써 개발의 속도를 높일 수 있는 장점이 있고, 이외에도 서비스별로 다른 기술을 사용할 수 있다는 장점이 있는데, 이는 서비스 특성에 따라서 적절한 기술을 사용할 수 있다는 것을 의미한다.


반드시 장점만 있는 것이 아니라, 단점도 있는데, 여러가지 기술을 혼용해서 사용하다 보니, 그 기술의 표준을 통제하기가 어렵다. 희귀한 기술로 개발을 진행한 서비스가 있을때 개발자가 팀을 떠나게 되면, 해당 서비스를 계속해서 유지하기 어려울 수 있고, 같은 기술을 사용하더라도 프레임웍이나 코드 표준화 개발 프로세스 표준화 등에서 어려움을 겪을 수 있다.

마이크로 서비스 아키텍쳐가 어려운점 중의 하나는 서비스가 증가될 수 록, 서비스간의 연계가 복잡해져서 장애가 발생했을때 어느 서비스에서 장애가 발생했는지 찾아서 조치하는 것이 어렵다는 것이다. 그래서 마이크로서비스에서는 특히 분산된 서비스간의 모니터링을 구현하는데 더 많은 노력을 기울여야 한다.

조직 구조

마이크로 서비스의 목적은 서비스를 작은 단위로 쪼겐 후에, 각 팀이 각 서비스에 대한 개발과 운영 전체에 대한 책임을 부여함으로써, 서비스 개발의 속도를 높이는데 있다. 그래서, 마이크로 서비스 아키텍쳐를 적용하는데 있어서 가장 중요한 것은 조직 구조이다.


소프트웨어 아키텍쳐와 조직문화에 대해서 재미있는 이론이 하나 있는데, 콘웨이의 법칙이다.  콘웨이의 법칙은 “소프트웨어 아키텍쳐는, 소프트웨어를 개발하는 팀의 구조를 따라간다" 라는 이론이다.

이를 재 해석하면 마이크로 서비스 아키텍쳐에서 서비스의 분류 단위는 이를 개발하는 팀의 단위가 된다.


아무리 아키텍쳐를 논리적으로 잘 설계해서 서비스별로 나눴다하더라도, 팀의 구조와 맞지 않으면, 결국에는 아키텍쳐가 무너진다. 예전에 시스템을 설계할때, 미국,캐나다,인도,한국팀으로 팀을 나눠서 일한적이 있었다. 마이크로 서비스 아키텍쳐를 도입했었는데, 개발을 진행함에 따라서 서비스 컴포넌트의 기능이 처음 설계대로 진행되지 않고 한쪽으로 몰리는 현상이 발생하게 되었다. 한국에 기획이 있었기 때문에 비지니스에 연계된 결정은 대부분 한국에서 나오게 되었고 많은 커뮤니케이션이 필요하였기 때문에, 한국팀이 상대적으로 비지니스에 연계된 중요 컴포넌트를 많이 맏게 되었다. 미국과 캐나다는 기술이 상대적으로 좋았기 때문에 기술 난이도가 높은 기능들이 미국과 캐나다 서비스로 몰리게 되었고, 시간대가 같기 때문에 커뮤니케이션이 활발하게 일어났고 결과적으로 미국과 캐나다 서비스는 타이트하게 서로 맞물리는 구조가 되었다.

인도의 경우 시간대가 다르고 커뮤니케이션의 어려움 때문에 한번 커뮤니케이션하는데 시간이 많이 소요되었고 그로 인해서 상대적으로 덜 중요한 기능이 인도팀으로 점점 배정되었다.


사례에서 볼 수 있듯이, 마이크로 서비스 아키텍쳐 설계에서 가장 중요한 점은 기술적으로 아키텍쳐를 잘 설계하는 것이 아니라, 팀의 구조를 아키텍쳐에 맞는 구조로 만들고, 그 안에 원할한 커뮤니케이션 환경을 만들어주는 것이 가장 중요하다.  

조직문화

팀을 분할해서 독립성을 부여해서 개발의 속도를 높일려면 단순하게 팀만을 분할해서는 불가능하다. 각 팀이 맏은 컴포넌트 또는 서비스에 개발에 대한 독립적인 책임을 부여하려면 권한도 같이 부여해야 한다.

기존의 모노리틱 시스템의 개발팀을 운영하는 구조가 중앙 조직이 의사 결정을 하고 전체 프로젝트를 진행하는 중앙 집중형 거버넌스 모델이었다면, 마이크로 서비스 아키텍쳐는 설계와 프로젝트 진행 각각에 대한 의사 결정을 각 서비스 팀에 내리는 분산형 거버넌스 구조가 바람직하다. 중앙팀에서 의사 결정을 받고 그에 따라 개발을 진행하면 커뮤니케이션 오버헤드가 발생하고 그로 인하여 빠른 개발이 어렵기 때문에 그 권한을 개발 주체에게 분산하는 모델이다.


의사 결정 권한을 각 팀에게 분산하려면 각 팀이 의사 결정을 할 수 있는 능력이 있는 팀 구조가 되어야 한다.

팀내에서 의사 결정을 하고 스스로 서비스를 개발할 수 있는 능력을 갖춰야 하는데, 이를 위해서는 기획,개발, 테스트 및 운영등 모든 역할이 한 팀안에 있어야 한다. 팀의 크기는 보통 5~8명 정도가 적절한데, 이를 2-피자팀이라고도 이야기 한다. (2개의 피자로 팀원들이 모두 먹을 수 있는 정도의 팀의 크기) 이 인원을 넘어서면 커뮤니케이션이 어려워지고 작으면 제대로 된 서비스를 개발하기 어렵기 때문에 이 정도 팀의 크기를 추천한다.


DEVOPS

Devops는 운영과 개발을 한팀에서 하는 모델을 말하는데, 팀이 개발과 운영을 모두 담당함으로써 개발과 운영 사이에서 오는 간극을 해결하고 개발된 시스템을 빠르게 배포하고, 운영 과정에서 얻은 노하우를 개발에 반영해서 시장의 요구 사항에 빠르게 반응 하는데 그 목적을 둔다.

개발과 운영을 한팀에서 담당함에도 불구하고,  Devops 엔지니어, SRE (Site Reliability engineer)등과 같이 기존의 운영팀이 하던 일을 하는 역할이 여전히 남아 있는데, 그렇다면 Devops로 넘어왔음에도 불구하고 이러한 역할이 계속 남아 있는 이유와 정확한 역할은 무엇일까?


앞에서도 언급했듯이 Devops는 개발팀이 개발/배포/운영을 모두 담당하는 셀프 서비스 모델이다. 셀프 서비스를 하기 위해서는 인프라가 플랫폼화 되어 있어야 한다. 개발팀이 직접 데이타 센터에 가서 서버를 설치하고 OS를 설치하고 네트워크 구성을 하기는 어렵고, 온라인으로 서버를 설치하고 네트워크를 구성할 수 있어야 하고, 무엇보다 쉬워야 한다. 인프라 구성뿐 아니라 그위에 소프트웨어를 쉽게 빌드 및  배포하고 운영 중인 시스템에 대한 모니터링이 가능해야 하는데, 이러한 인프라를 일일이 구성하기는 어렵기 때문에 플랫폼화가 되어 있어야 하는데, Devops 엔지니어의 역할은 이러한 플랫폼을 만드는 역할이 된다.



위의 그림과 같이 Devops 팀은, 시스템을 실행할 수 있는 런타임 인프라를 개발 배포하고, 런타임 시스템에 대한 모니터링과 로깅을 제공하며, 이 시스템에 자동으로 배포할 수 있는 CI/CD 플랫폼을 구축한다.

이렇게 개발된 플랫폼에 개발팀은 개발된 시스템을 스스로 배포하고 운영하는 모델이다.

이러한 모델은 구글의 SRE (Site Reliability Engineering)에서 좋은 사례를 찾아볼 수 있는데, SRE 엔지니어는 시스템이 개발된 후에, 인프라 시스템에 대한 플랫폼화 작업을 수행하고, 이 플랫폼이 완성되어 안정화 될때까지 지속적인 지원을 하며, 플랫폼에 대한 안정화 작업이 끝나면 플랫폼의 운영을 개발팀에 맏기고 다른 프로젝트를 위한 플랫폼 작업을 하는 방식이다.

컨테이너

이러한 플랫폼을 지원하기 위해서는 벤더 종속적이지 않고, 개발자가 손쉽게 운영 및 접근할 수 있는 인프라 관리 기술이 필요한데, 이런 기술로 많이 언급되는 기술이 컨테이너이다.




가상머신 (VM)의 경우에는 하이퍼바이저의 종류에 따라서, 호환이 되지 않는 경우가 있고, 무엇보다 가상 머신 이미지의 사이즈가 매우 크기 때문에 (수백~기가 이상) 손쉽게 이식하기가 쉽지 않다.

또한 하드웨어 계층 부터 가상화 하기 때문에 실행하는데 컨테이너에 비해서 상대적으로 많은 자원이 소요된다.

컨테이너의 경우 가상 머신을 사용하지 않고 호스트 OS의 커널에서 바로 실행이 된다. 실행되는 컨테이너의 OS가 호스트 OS와 다른 경우, 이 다른 부분 (알파)만을 컨테이너에서 추가로 패키징하여 실행이 된다.

예를 들어 호스트 이미지에 기능이 A,B,C가 있고, 컨테이너는 A,B,C,D가 필요하다면, 컨테이너에는 다른 부분인 D만 묶어서 패키징 하는 개념이다.  그래서 가상머신에 비해서 크기가 훨씬 작고 가상화 계층을 거치지 않기 때문에 훨씬 효율적이라고 말할 수 있다.

컨테이너 관리 솔루션

컨테이너를 소규모로 사용한다면 물리 서버를 직접 지정해서 배포하면 되지만, 대규모로 컨테이너를 운영하고자 할때는 어떤 서버에 컨테이너를 배치해야 하는 가에 대한 문제가 생긴다.


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



이렇게 컨테이너 스케쥴링을 해주는 솔루션으로는 Kubernetes, Mesosphere, OpenStack 등 다양한 솔루션이 난립해서 혼돈이었는데, 작년말 (2017년말)을 기점으로 해서 쿠버네티스가 de-facto 표준으로 되어가는 형국이다. 아래 트랜드 그래프에서 보면 알 수 있듯이 쿠버네티스의 트랜드가 지속적으로 올라가서 가장 높은 것을 확인할 수 있다.



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

가상 머신위의 컨테이너

보통 이런 컨테이너 환경을 운영할때 베어메탈 (하드웨어)위에 바로 컨테이너 솔루션을 올리지 않고, 가상화 환경을 올린 후에, 그 위에 컨테이너 환경을 올리는 경우가 많다.


베어메탈 위에 바로 컨테이너 환경을 올리면 성능적 이점도 있고, 계층도 줄어들어 관리도 편리한데, 왜 가상화 계층을 한번 더 두는 것일까? 이유는 컨테이너 환경을 조금더 유연하게 사용할 수 있기 때문이다. 먼저 가상 머신을 이용해서 컨테이너 환경을 isolation할 수 있고, 가상화를 통해서 자원의 수를 더 늘려서 이를 잘게 쪼게서 사용이 가능하다. 예를 들어 설명하면, 8 CPU 머신을 쿠버네티스로 관리 운영하면, 8 CPU로밖에 사용할 수 없지만, 가상화 환경을 중간에 끼면, 8 CPU를 가상화 해서 2배일 경우 16 CPU로, 8배일 경우 64 CPU로 가상화 하여 좀 더 자원을 잘게 나눠서 사용이 가능하다.




구글 클라우드 해커톤 세션으로 진행한 강의 내용입니다

50분 정도 인데, 짧게 쿠버네티스에 대한 설명과, 마이크로서비스에 대한 설명

그리고 쿠버네티스 에코 시스템인 Spinnaker, Istio, KNative 등에 대해서 설명합니다



구글 클라우드 로드밸런서 소개

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


클라우드 플랫폼에서 가장 필요한 기능중의 하나가 로드밸런서이다.

그중에서 구글 클라우드의 로드밸런서는 L7 스위치 이상의 기능을 가지면서 로드밸런서와 api gateway의 일부 기능을 수행할 수 있는데, 어떤 특징이 있는지 살펴보자. (개인적인 생각이지만 이게 정말 물건이다..)

HTTP 프로토콜 지원

TCP,UDP 뿐 아니라 HTTP 레이어의 로드밸런싱을 지원한다. HTTPS Termination을 당연히 지원하고 HTTP 모드로 부하분산을 할 경우 HTTP URI에 따라 다양한 라우팅을 할 수 있다.

No warming

다른 클라우드 로드밸런서와는 달리 트래픽이 갑자기 많이 들어오더라도 별도의 워밍업작업 없이 트래픽을 받을 수 있다. 클라우드 로드밸런서의 경우에는 종종 수십분간 워밍(부하를 지속해서 넣어서 로드밸런서의 용량을 늘려주는 행위)을 해주지 않으면 큰 대역폭의 트래픽을 못 받는 경우가 있다.

리전간 부하 분산과 서버 그룹간 부하 분산

여기 부터가 재미있는 기능인데, 들어오는 트래픽에 따라서 특정 리전(국가)로 라우팅을 하도록 설정할 수 있고, 별도의 설정이 없으면 자동으로 클라이언트가 가까운 데이타 센터로 트래픽을 라우팅해줄 수 있다.


아래 그림은 HTTP 요청을 US와 EU 리전에 있는 각각의 서버 그룹으로 분산하고, HTTPS요청도 US와 EU에 있는 각각의 리전으로 분산하는 구조를 설명한 그림이다.


(출처 : https://cloud.google.com/compute/docs/load-balancing/http/cross-region-example#overview)


리전간 부하 분산을 하면 리전 단위의 장애 대응이 가능할 뿐만 아니라, 가장 가까운 데이타 센터로 부터 호출을 할 수 있기 때문에 네트워크 구간 시간을 줄일 수 있다.


위의 그림에서 봐야 할것중 하나가 서버 그룹간의 부하 분산인데, 같은 애플리케이션이라도 “인스턴스 그룹" 이라는 개념으로 서버들을 묶을 수 있다. 위에 HTTP 를 지원하는 애플리케이션은 두개의 그룹으로 정의되어 있는데, 이렇게 서버를 여러개의 그룹으로 묶으면 좋은 점이 롤링 업그레이드가 가능하다는 것이다. 즉 그룹 A,B가 있을때, A를 로드 밸런서에서 떼어서 업데에트 한 후 다시 붙이고, 그 다음은 그룹 B를 로드밸런서에서에서 떼어서 업데이트 한 후 다시 붙여서 서버 배포나 업데이트시에 무장애로 진행이 가능하다.

HTTP URI 기반의 부하 분산

매우 흥미로운 기능중 하나가 HTTP의 URI를 기반으로 특정 서버 그룹으로 요청을 라우팅 할 수 있는 기능이다. 일반적인 네트워크 장비인 L4등에서는 구현이 불가능한 기능인데 원리는 단순하다.

아래 그림처럼 /static URL를 갖는 요청은 “static-resources” 라는 서버 그룹으로 라우팅 하고, /video URL을 갖는 요청은 “video-resources”라는 서버 그룹으로 라우팅 하는 식이다.



출처 : https://cloud.google.com/compute/docs/load-balancing/http/content-based-example#overview


매우 간단한 기능이기는 하지만 그 활용도가 매우 높다.

웹서버, 스트리밍 서버등으로 컨텐츠 타입에 따라 서버를 나눌 수 도 있지만,

마이크로 서비스 아키텍쳐 (MSA)로 되어 있는 경우, 각 서비스 컴포넌트가 다른 URL을 가지기 때문에, 앞단에 API Gateway와 같이 URL에 따라 라우팅을 해주는 프록시 계층이 필요한데, 구글의 로드밸런서는 이 기능을 지원하기 때문에, 백앤드 서비스가 MSA로 구성되어 있을 경우 이 기능을 유용하게 사용할 수 있다.

글로벌 엣지 서버와 통합을 통한 네트워크 가속

다른 글에서도 설명했던 내용인데, 구글 클라우드 로드 밸런서를 사용하게 되면 전세계에 흩어져 있는 약 70여개의 엣지 노드를 통해서 요청 트래픽을 받는다. 가장 가까운 엣지 노드로 트래픽을 받은 후, 엣지노드에서 서버까지 구글의 광케이블을 통해서 트래픽을 전달해줘서, 글로벌 서비스의 경우 네트워크 지연 시간을 절약할 수 있다.



자세한 내용은 http://bcho.tistory.com/1109 를 참고


마이크로 서비스 아키텍쳐 (MSA의 이해)

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

 

배경


마이크로 서비스 아키텍쳐(이하 MSA)는 근래의 웹기반의 분산 시스템의 디자인에 많이 반영되고 있는 아키텍쳐 스타일로, 특정 사람이 정의한 아키텍쳐가 아니라, 분산 웹 시스템의 구조가 유사한 구조로 설계 되면서, 개념적으로만 존재하던 개념이다.

얼마전 마틴파울러(Martin folwer)가 이에 대한 MSA에 대한 개념을 글로 정리하여, 개념을 정립 시키는데 일조를 하였다.

이 글에서는 대규모 분산 웹시스템의 아키텍쳐 스타일로 주목 받고 있는 MSA에 대한 개념에 대해서 알아보도록 한다.


모노리틱 아키텍쳐(Monolithic Architecture)


마이크로 서비스 아키텍쳐를 이해하려면 먼저 모노리틱 아키텍쳐 스타일에 대해서 이해해야 한다

모노리틱 아키텍쳐 스타일은 기존의 전통적인 웹 시스템 개발 스타일로, 하나의 애플리케이션 내에 모든 로직들이 모두 들어 가 있는 “통짜 구조” 이다.

예를 들어, 온라인 쇼핑몰 애플리케이션이 있을때, 톰캣 서버에서 도는 WAR 파일(웹 애플리케이션 패키징 파일)내에, 사용자 관리,상품,주문 관리 모든 컴포넌트들이 들어 있고 이를 처리하는 UX 로직까지 하나로 포장되서 들어가 있는 구조이다.




각 컴포넌트들은 상호 호출을 함수를 이용한 call-by-reference 구조를 취한다.

전체 애플리케이션을 하나로 처리하기 때문에, 개발툴 등에서 하나의 애플리케이션만 개발하면 되고, 배포 역시 간편하며 테스트도 하나의 애플리케이션만 수행하면 되기 때문에 편리하다.

문제점

그러나 이러한 모노리틱 아키텍쳐 시스템은 대형 시스템 개발시 몇가지 문제점을 갖는다.

모노리틱 구조의 경우 작은 크기의 애플리케이션에서는 용이 하지만, 규모가 큰 애플리케이션에서는 불리한 점이 많다.

크기가 크기 때문에, 빌드 및 배포 시간, 서버의 기동 시간이 오래 걸리며 (서버 기동에만 2시간까지 걸리는 사례도 경험해봤음)

프로젝트를 진행하는 관점에서도, 한두사람의 실수는 전체 시스템의 빌드 실패를 유발하기 때문에, 프로젝트가 커질 수 록, 여러 사람들이 협업 개발하기가 쉽지 않다

또한 시스템 컴포넌트들이 서로 로컬 콜 (call-by-reference)기반으로 타이트하게 연결되어 있기 때문에, 전체 시스템의 구조를 제대로 파악하지 않고 개발을 진행하면, 특정 컴포넌트나 모듈에서의 성능 문제나 장애가 다른 컴포넌트에까지 영향을 주게 되며, 이런 문제를 예방하기 위해서는 개발자가 대략적인 전체 시스템의 구조 등을 이해 해야 하는데, 시스템의 구조가 커질 수 록, 개인이 전체 시스템의 구조와 특성을 이해하는 것은 어려워진다.

특정 컴포넌트를 수정하고자 했을때, 컴포넌트 재 배포시 수정된 컴포넌트만 재 배포 하는 것이 아니라 전체 애플리케이션을 재 컴파일 하여 전체를 다시 통으로 재배포 해야하기 때문에 잦은 배포가 있는 시스템의 경우 불리하며,

컴포넌트 별로, 기능/비기능적 특성에 맞춰서 다른 기술을 도입하고자 할때 유연하지 않다. 예를 들어서, 전체 애플리케이션을 자바로 개발했다고 했을 때, 파일 업로드/다운 로드와 같이 IO 작업이 많은 컴포넌트의 경우 node.js를 사용하는 것이 좋을 수 있으나, 애플리케이션이 자바로 개발되었기 때문에 다른 기술을 집어 넣기가 매우 어렵다.

※ 모노리틱 아키텍쳐가 꼭 나쁘다는 것이 아니다. 규모가 작은 애플리케이션에서는 배포가 용이하고, 규모가 크더라도, call-by-reference call에 의해서 컴포넌트간 호출시 성능에 제약이 덜하며, 운영 관리가 용이하다. 또한 하나의 구조로 되어 있기 때문에, 트렌젝션 관리등이 용이하다는 장점이 있다. 즉 마이크로 서비스 아키텍쳐가 모든 부분에 통용되는 정답은 아니며, 상황과 필요에 따라서 마이크로 서비스 아키텍쳐나 모노리틱 아키텍쳐를 적절하게 선별 선택 또는 변형화 해서 사용할 필요가 있다.


마이크로 서비스 아키텍쳐


마이크로 서비스 아키텍쳐는 대용량 웹서비스가 많아짐에 따라 정의된 아키텍쳐인데, 그 근간은 SOA (Service Oriented Architecture : 서비스 지향 아키텍쳐)에 두고 있다.

SOA는 엔터프라이즈 시스템을 중심으로 고안된 아키텍쳐라면, 마이크로 서비스 아키텍쳐는 SOA 사상에 근간을 두고, 대용량 웹서비스 개발에 맞는 구조로 사상이 경량화 되고, 대규모 개발팀의 조직 구조에 맞도록 변형된 아키텍쳐이다.


아키텍쳐 구조


서비스


마이크로 서비스 아키텍쳐에서는 각 컴포넌트를 서비스라는 개념으로 정의한다. 서비스는 데이타에서 부터 비지니스 로직까지 독립적으로 상호 컴포넌트간의 의존성이 없이 개발된 컴포넌트(이를 버티컬 슬라이싱/Vertical Slicing-수직적 분할이라고 한다.)로 REST API와 같은 표준 인터페이스로 그 기능을 외부로 제공한다.

서비스 경계는 구문 또는 도메인(업무)의 경계를 따른다. 예를 들어 사용자 관리, 상품 관리, 주문 관리와 같은 각 업무 별로 서비스를 나눠서 정의한다. 사용자/상품 관리 처럼 여러개의 업무를 동시에 하나의 서비스로 섞어서 정의하지 않는다.

REST API에서 /users, /products와 같이 주요 URI도 하나의 서비스 정의의 범위로 좋은 예가 된다.  


마이크로 서비스 아키텍쳐의 구조


마이크로 서비스 아키텍쳐의 구조는 다음과 같은 모양을 따른다.

각 컴포넌트는 서비스라는 형태로 구현되고 API를 이용하여 타 서비스와 통신을 한다.



배포 구조관점에서도 각 서비스는 독립된 서버로 타 컴포넌트와의 의존성이 없이 독립적으로 배포 된다.

예를 들어 사용자 관리 서비스는 독립적인 war파일로 개발되어, 독립된 톰캣 인스턴스에 배치된다. 확장을 위해서 서비스가 배치된 톰캣 인스턴스는 횡적으로 스케일 (인스턴스 수를 더함으로써)이 가능하고, 앞단에 로드 밸런서를 배치하여 서비스간의 로드를 분산 시킨다.



가장 큰 특징이, 애플리케이션 로직을 분리해서 여러개의 애플리케이션으로 나눠서 서비스화하고, 각 서비스별로 톰캣을 분산 배치한 것이 핵심이다.


데이타 분리


데이타 저장관점에서는 중앙 집중화된 하나의 통 데이타 베이스를 사용하는 것이 아니라 서비스 별로 별도의 데이타 베이스를 사용한다.       보통 모노리틱 서비스의 경우에는 하나의 통 데이타 베이스 (보통 RDBMS를 사용) 하는 경우가 일반적이지만, 마이크로 서비스 아키텍쳐의 경우, 서비스가 API에서 부터 데이타 베이스까지 분리되는 수직 분할 원칙 (Vertical Slicing)에 따라서 독립된 데이타 베이스를 갖는다.



데이타 베이스의 종류 자체를 다른 데이타 베이스를 사용할 수 도 있지만, 같은 데이타 베이스를 사용하더라도 db를 나누는 방법을 사용한다.

이 경우, 다른 서비스 컴포넌트에 대한 의존성이 없이 서비스를 독립적으로 개발 및 배포/운영할 수 있다는 장점을 가지고 있으나, 다른 컴포넌트의 데이타를 API 통신을 통해서만 가지고 와야 하기 때문에 성능상 문제를 야기할 수 있고, 또한 이 기종 데이타 베이스간의 트렌젝션을 묶을 수 없는 문제점을 가지고 있다. (이러한 데이타 분산에 의한 트렌젝션 문제는 SOA 때부터 있어 왔다.) 데이타 분산으로 인한 트렌젝션 문제는 뒤에서 조금 더 자세하게 설명하도록 한다.


API Gateway


마이크로 서비스 아키텍쳐 설계에 있어서 많이 언급되는 컴포넌트 중의 하나가 api gateway 라는 컴포넌트 이다. api gateway는 마치 프록시 서버 처럼 api들 앞에서 모든 api에 대한 end point를 통합하고, 몇가지 추가적인 기능을 제공하는 미들웨어로, SOA의 ESB (Enterprise Service Bus)의 경량화 버전이다. Apigateway가 마이크로 서비스 아키텍쳐 상에서 수행하는 주요 기능을 살펴보면 다음과 같다.

EndPoint 통합 및 토폴로지 정리

마이크로 서비스 아키텍쳐의 문제점 중의 하나는 각 서비스가 다른 서버에 분리 배포 되기 때문에, API의 End point 즉, 서버의 URL이 각기 다르다는 것이다.

사용자 컴포넌트는 http://user.server.com, 상품 컴포넌트는 http://product.server.com 과 같은 분리된 URL을 사용하는데, 이는 API 사용자 경험 관점에서도 사용하기가 불편하다. 특히 마이크로 서비스 아키텍쳐는 컴포넌트를 되도록이면 업무 단위로 잘게 짜르는 fine grained (작은 덩어리)의 서비스를 지향하기 때문에, 컴포넌트의 URL 수는 더 많이 늘어 날 수 있다.

API를 사용하는 클라이언트에서 서버간의 통신이나, 서버간의 API 통신의 경우 p2p(Point to Point)형태로 토폴로지가 복잡해지고 거미줄 모양의 서비스 컴포넌트간의 호출 구조는 향후 관리의 문제를 일으킬 수 있다. 하나의 end point를 변경하였을때, 제대로 관리가 되지 않을 경우가 있다.




<그림. P2P 형태의 토폴리지>

이러한 토폴로지상의 문제점을 해결하기 위해서 중앙에 서비스 버스와 같은 역할을 하는 채널을 배치 시켜서, 전체 토폴로지를 p2p에서 hub & spoke 방식으로 변환 시켜서, 서비스간 호출을 단순화 시킬 수 있다.




<그림. 버스 기반의 Hub & Spoke 토폴리지>

Orchestration


다른 기능으로는 orchestration 이라는 개념이 있다. 기존 open api의 mash up과 같은 개념으로, 여러개의 서비스를 묶어서 하나의 새로운 서비스를 만드는 개념이다.

예를 들어, 포인트 적립과, 물품 구매라는 서비스가 있을때, 이 두개의 서비스를 묶어서 “물품 구입시 포인트 적립”이라는 새로운 서비스를 만들어 낼 수 있다. 이러한 orchestration 기능은, api gateway를 통해서 구현될 수 있다.

이는 마이크로 서비스 아키텍쳐가 서비스 자체가 fine grained 형태로 잘게 쪼게졌기 때문에 가능한 일인데, 사실 orchestration을 api gateway 계층에서 하는 것은 gateway 입장에서 부담이 되는 일이다. 실제로 과거의 SOA 시절에 많은 ESB(Enterprise Service Bus) 프로젝트가 실패한 원인 중의 하나가 과도한 orchestration 로직을 넣어서 전체적인 성능 문제를 유발한 경우가 많았다. 그래서 orchestration 서비스의 활용은 마이크로 서비스 아키텍쳐에 대한 높은 이해와 api gateway 자체에 대한 높은 수준의 기술적인 이해를 필요로 한다.

실제로 넷플릭스의 경우 마이크로 서비스 아키텍쳐를 사용하면서, 여러개의 서비스들을 gateway 계층을 통해서 orchestration 하는 모델을 사용하고 있다. 


공통 기능 처리 (Cross cutting function handling)


또한 API에 대한 인증 (Authentication)이나, Logging과 같은 공통 기능에 대해서 서비스 컴포넌트 별로 중복 개발해야 하는 비효율성을 유발할 수 있다. api gateway에서 이러한 공통 기능을 처리하기 되면, api 자체는 비지니스 로직에만 집중을 하여 개발에 있어서의 중복등을 방지 할 수 있다.

mediation

이외에도 XML이나 네이티브 메세지 포맷을 json등으로 상호 변환해주는 message transformation 기능이나, 프로토콜을 변환하는 기능, 서비스간의 메세지를 라우팅해주는 기능등 여러가지 고급 mediation 기능을 제공을 하지만, api gateway를 최대한 가볍게 가져간다는 설계 원칙 아래서 가급 적이면 고급적인 mediation 기능을 사용할 때는 높은 수준의 설계와 기술적인 노하우를 동반해야 한다.


※ ESB vs APIgateway

SOA 프로젝트의 실패중의 하나가 ESB로 꼽히는 경우가 많은데, 이는 ESB를 Proxy나 Gateway처럼 가벼운 연산만이 아니라, 여러개의 서비스를 묶는 로직에  무겁게 사용했기 때문이다. (사용하면 안된다는 것이 아니라 잘 사용해야 한다는 것이다.) ESB는 메세지를 내부적으로 XML로 변환하여 처리하는데, XML 처리는 생각하는것 보다 파싱에 대한 오버헤드가 매우 크다.  또한 ESB의 고유적인 버스나 게이트웨이로써의 특성이 아니라 타 시스템을 통합 하기 위한 EAI적인 역할을 ESB를 이용해서 구현함으로써 많은 실패 사례를 만들어 내었다. 그래서 종종 ESB는 Enterprise Service Bus가 아니라 EnterpriSe nightmare Bus로 불리기도 한다. J

이러한 개념적인 문제를 해결하기 위해서 나온 제품군이 apigateway라는 미들웨어 제품군들인데, ESB와 기본적인 특성은 유사하나 기능을 낮추고 EAI의 통합 기능을 제거하고 API 처리에만 집중한 제품군들로, 클라우드상에서 작동하는 PaaS (Platform As A Service)형태의 서비스로는 apigee.com이나 3scale.com 등이 있고, 설치형 제품으로는 상용 제품인 CA社의 Layer7이나 오픈소스인 Apache Service Mix, MuleSoft의 ESB 제품 그리고 WSO2의 API Platform 등이 있다.

Apigateway 부분에 마이크로 서비스 아키텍쳐의 다른 부분 보다 많은 부분을 할애한 이유는, 컴포넌트를 서비스화 하는 부분에 까지는 대부분 큰 문제가 없이 적응을 하지만 apigateway의 도입 부분의 경우, 내부적인 많은 잡음이 날 수 있고, 또한 도입을 했더라도 잘못된 설계나 구현으로 인해서 실패 가능성이 비교적 높은 모듈이기 때문이다. 마이크로 서비스 아키텍쳐의 핵심 컴포넌트이기도 하지만, 도입을 위해서는 팀의 상당 수준의 높은 기술적인 이해와 개발 능력을 필요로 한다.


배포


마이크로 서비스 아키텍쳐의 가장 큰 장점 중의 하나가 유연한 배포 모델이다. 각 서비스가 다른 서비스와 물리적으로 완벽하게 분리되기 때문에 변경이 있는 서비스 부분만 부분 배포가 가능하다 예를 들어서, 사용자 관리 서비스 로직이 변경되었을 때, 모노리틱 아키텍쳐의 경우에는 전체 시스템을 재 배포해야 하지만, 마이크로 서비스 아키텍쳐의 경우에는 변경이 있는 사용자 관리 서비스 부분만 재 배포 하면 되기 때문에, 빠르고 전체 시스템의 영향도를 최소화한 수준에서 배포를 진행할 수 있다.


확장성


서비스 별로 독립된 배포 구조는 확장성에 있어서도 많은 장점을 가지고 오는데, 부하가 많은 특정 서비스에 대해서만 확장이 가능하여 조금 더 유연한 확장 모델을 가질 수 있다. 모노리틱 아키텍쳐의 경우에는 특정 서비스의 부하가 많아서 성능 확장이 필요할때, 전체 서버의 수를 늘리거나 각 서버의 CPU 수를 늘려줘야 하지만, 마이크로 서비스 아키텍쳐의 경우에는 부하를 많이 받는 서비스 컴포넌트 만 확장을 해주면 된다.


Conway’s Law (컨웨이의 법칙)


마이크로 서비스 아키텍쳐의 흥미로운 점중의 하나는 아키텍쳐 스타일의 조직 구조나 팀 운영 방식에 영향을 준다는 것인데, 마이크로 서비스 아키텍쳐는 컨웨이의 법칙에 근간을 두고 있다.

컨웨이의 법칙은 “소프트웨어의 구조는 그 소프트웨어를 만드는 조직의 구조와 일치한다”는 이론이다.

현대의 소프트웨어 개발은 주로 애자일 방법론을 기반으로 하는 경우가 많다. 애자일 팀의 구조는 2 피자팀(한팀의 인원수는 피자 두판을 먹을 수 있는 정도의 인원 수가 적절하다.)의 모델을 많이 따르는데, 한 팀이 7~10명정도로 이루어지고, 이 인원 수가 넘어가면 팀을 분리하는 모델이다.

마이크로 서비스 아키텍쳐는 각 컴포넌트를 팀에 배치해서 책임지고 개발하는 것을 근간으로 하며, 팀간의 의존성을 제거해서 각 팀이 컴포넌트 개발을 독립적으로할 수 있는 구조로 잡혀있다.


마이크로 서비스 아키텍쳐의 문제점


분홍빛 미래 처럼 보이는 마이크로 서비스 아키텍쳐는 아무런 문제가 없는 것일까? 당연히 여러가지 장점을 제공하는 대신에 그만한 단점을 가지고 있다.


성능


모노리틱 아키텍쳐는 하나의 프로세스 내에서 서비스간의 호출을 call-by-reference 모델을 이용한다. 반면 마이크로 서비스 아키텍쳐는 서비스간의 호출을 API 통신을 이용하기 때문에 값을 json이나 xml에서 프로그래밍에서 사용하는 데이타 모델 (java object등)으로 변환하는 marsharing 오버헤드가 발생하고 호출을 위해서 이 메세지들이 네트워크를 통해서 전송되기 때문에 그만한 시간이 더 추가로 소요된다.


메모리


마이크로 서비스 아키텍쳐는 각 서비스를 독립된 서버에 분할 배치하기 때문에, 중복되는 모듈에 대해서 그만큼 메모리 사용량이 늘어난다.

예를 들어 하나의 톰캣 인스턴스에서 사용자 관리와 상품 관리를 배포하여 운용할 경우, 하나의 톰캣을 운영하는데 드는 메모리와, 스프링 프레임웍과 같은 라이브러리를 사용하는데 소요되는 메모리 그리고 각각의 서비스 애플리케이션이 기동하는 메모리가 필요하다.

그러나 마이크로 서비스 아키텍쳐로 서비스를 배포할 경우 사용자 관리 서비스 배포와 상품 관리 서비스 배포를 위한 각각의 별도의 톰캣 인스턴스를 운용해야 하고, 스프링 프레임웍과 같은 공통 라이브러리도 각각 필요하기 때문에, 배포하고자 하는 서비스의 수 만큼 중복된 양의 메모리가 필요하게 된다.

위의 두 문제는 반드시 발생하는 문제점이기는 하나 현대의 인프라 환경에서는 크게 문제는 되지 않는다. (기존에 비해 상대적으로). 현대의 컴퓨팅 파워 자체가 워낙 발달하였고, 네트워크 인프라 역시 기존에 1G등에 비해서 내부 네트워크는 10G를 사용하는 등, 많은 성능상 발전이 있었다. 또한 메모리 역시 비용이 많이 낮춰지고 32bit에서 64bit로 OS들이 바뀌면서, 가용 메모리 용량이 크게 늘어나서 큰 문제는 되지 않는다. 또한 성능상의 문제는 비동기 패턴이나 캐슁등을 이용해서 해결할 수 있는 다른 방안이 많기 때문에 이 자체는 큰 문제가 되지 않는다.

그보다 더 문제점은 아래에서 언급하는 내용들인데,


테스팅이 더 어려움


마이크로 서비스 아키텍쳐의 경우 서비스들이 각각 분리가 되어 있고, 다른 서비스에 대한 종속성을 가지고 있기 때문에, 특정 사용자 시나리오나 기능을 테스트하고자 할 경우 여러 서비스에 걸쳐서 테스트를 진행해야 하기 때문에 테스트 환경 구축이나 문제 발생시 분리된 여러개의 시스템을 동시에 봐야 하기 때문에 테스팅의 복잡도가 올라간다.

운영 관점의 문제


운영 관점에서는 서비스 별로 서로 다른 기술을 사용할 수 있으며, 시스템이 아주 잘게 서비스 단위로 쪼게 지기 때문에 운영을 해야할 대상 시스템의 개수가 늘어나고, 필요한 기술의 수도 늘어나게 된다.


서비스간 트렌젝션 처리


구현상의 가장 어려운 점중의 하나가, 트렌젝션 처리이다. 모노리틱 아키텍쳐에서는 RDBMS를 사용하면서 하나의 애플리케이션 내에서 트렌젝션이 문제가 있으면 쉽게 데이타베이스의 기능을 이용해서 rollback을 할 수 있었다. 여러개의 데이타베이스를 사용하더라도, 분산 트렌젝션을 지원하는 트렌젝션 코디네이터 (JTS – Java Transaction Service)등을 이용해서 쉽게 구현이 가능했는데, API 기반의 여러 서비스를 하나의 트렌젝션으로 묶는 것은 불가능 하다.

쉽게 예를 들어서 설명을 하면, 계좌에서 돈을 빼는 서비스와, 계좌에 돈을 넣는 서비스가 있다고 하자. 이 둘은 API를 expose했을 때, 계좌에서 돈을 뺀 후, 계좌에 돈을 넣기 전에 시스템이 장애가 나면, 뺀 돈은 없어지게 된다. 모노리틱 아키텍쳐를 사용했을 경우에는 이러한 문제를 트렌젝션 레벨에서 롤백으로 쉽게 해결할 수 있지만 API 기반의 마이크로 서비스 아키텍쳐에서는 거의불가능하다.

사실 이 문제는 마이크로 서비스 아키텍쳐 이전에도, 서비스와 API를 기본 컨셉으로 하는 SOA에도 있었던 문제이다.

이러한 문제를 해결하기 위해서 몇가지 방안이 있는데,

그 첫번째 방법으로는 아예 애플리케이션 디자인 단계에서 여러개의 API를 하나의 트렌젝션으로 묶는 분산 트렌젝션 시나리오 자체를 없애는 방안이다. 분산 트렌젝션이 아주 꼭 필요할 경우에는 차라리 모노리틱 아키텍쳐로 접근하는 것이 맞는 방법이다. 앞서도 언급했듯이 마이크로 서비스 아키텍쳐의 경우, 금융이나 제조와 같이 트렌젝션 보장이 중요한 엔터프라이즈 시스템보다는 대규모 처리가 필요한 B2C 형 서비스에 적합하기 때문에, 아키텍쳐 스타일 자체가 트렌젝션을 중요시 하는 시나리오에서는 적절하지 않다.

그럼에도 불구하고, 트렌젝션 처리가 필요할 경우, 트렌젝션 실패시 이를 애플리케이션 적으로 처리해 줘야 하는 데, 이를 보상 트렌젝션(compensation transaction)이라고 한다. 앞의 계좌 이체 시나리오에서 돈을 뺀 후, 다른 계좌에 넣다가 에러가 났을 경우에, 명시적으로, 돈을 원래 계좌로 돌려주는 에러 처리 로직을 구현해야 한다.

마지막 방법으로 복합 서비스 (composite service)라는 것을 만들어서 활용하는 방법이 있는데, 복합 서비스란 트렌젝션을 묶어야 하는 두개의 시스템을 트렌젝션을 지원하는 네이티브 프로토콜을 이용해서 구현한 다음 이를 API로 노출 시키는 방법이다.

두개의 데이타 베이스는 XA(eXtended Architecture)와 같은 분산 트렌젝션 프로토콜을 써서 서비스를 개발하거나 또는 SAP나 Oracle 아답터와 같이 트렌젝션을 지원하는 네이티브 아답터를 사용하는 방법이다. 기존에 SOA에서 많이 했던 접근방법이기는 하나, 복합 서비스를 사용할 경우, 복합서비스가 서로 다른 두개의 서비스에 걸쳐서 tightly coupled하게 존재하기 때문에, 마이크로 서비스 아키텍쳐의 isolation(상호 독립적)인 사상에 위배되고 서비스 변경시에 이 부분을  항상 고려해야 하기 때문에 아키텍쳐상의 유연성이 훼손되기 때문에 꼭 필요하지 않은 경우라면 사용하지 않는 것이 좋다.


거버넌스 모델


거버넌스 (governance)란, 시스템을 개발하는 조직의 구조나 프로세스를 정의한 것으로, 일반적으로 중앙 집중화된 조직에서 표준 프로세스와 가이드를 기반으로 전체 팀을 운용하는 모델을 사용한다. 이를 중앙 집중형 거버넌스 모델 (Centralized governance model) 이라고 하는데, 이 경우 전체 시스템이 동일한 프로세스와 기술을 가지고 개발이 되기 때문에, 유지 보수가 용이하고 팀간의 인원 교체등이 편리하다는 장점을 가지고 있다. 전통적인 개발 모델들은 이러한 중앙 집중현 거버넌스 모델을 사용한다.

그러나 현대의 웹 개발의 경우, 오픈 소스 발달로 선택 가능한 기술들이 많고 각 요구 사항에 따라서 효율성 측면등을 고려할때 각각 최적화된 기술을 사용하는 것이 좋은 경우가 있다.

예를 들어, 전체 표준을 자바+RDBMS로 정했다 하더라도, 파일 업로드 다운로드 관련 컴포넌트는 io 성능과 많은 동시접속자를 처리할 수 있는 node.js가 유리하다던지, 데이타의 포맷은 복잡하지만, 복잡한 쿼리가 없을 경우에는 json document 기반의 mongodb와 같은 NoSQL등이 유리한 사례 등이 된다. 이러한 기술을 도입하기 위해서는 중앙 집중형 거버넌스 모델에서는 모든 개발팀을 교육 시키고, 운영 또한 준비를 해야하기 때문에 기술에 대한 적용 민첩성이 떨어지게 된다.

이러한 문제점을 해결 하는 거버넌스 모델이 분산형 거버넌스 모델 (De-Centralized governance model)인데, 이는 각 팀에 독립적인 프로세스와 기술 선택 권한을 주는 모델로, 각 서비스가 표준 API로 기능을 바깥으로 노출할 뿐 내부적인 구현 기술 구조는 추상화되어 가능한 사상이다

분산형 거버넌스 모델을 수행하려면, 이에 맞는 팀구조가 필요한데, 이 팀 구조는 다음과 같은 몇가지 특징을 가지고 있어야 한다.


Cross functional team


기존의 팀 모델은 역할별로 나뉘어진 모델로 팀을 구분한다. 기획팀,UX팀,개발팀,인프라 운영팀 등 공통적인 특성으로 나누는 것이 기존의 팀 모델이다. 이런 팀 모델은 리소스의 운영에 유연성을 부여한다. 개발 인력이 모자르면 팀 내에서 개발인원을 다른 프로젝트에서 충당하는 등의 리소스 운영이 가능하지만 반대로 팀간의 커뮤니케이션이 팀이라는 경계에 막혀서 원할하지 않고 협의에 걸리는 시간으로 인해서 팀의 운영 속도가 떨어진다.

cross function team 모델은 하나의 팀에 UX, 개발팀,인프라팀등 소프트웨어 시스템을 개발하는데 필요한 모든 역할을 하나의 팀에 구성하고 움직이는 모델로, 각 서비스 개발팀이 cross functional team이 되서 움직인다.



<그림 역할 중심의 개발팀 과 cross functional team에 대한 모델 비교>

이 경우 서비스 기획에서 부터 설계,개발,운영이 가능해지고 다른 팀에 대한 의존성이 없어짐으로써 빠른 서비스 개발이 가능해진다.


You build,You run-Devops


기술에 대한 독립성을 가지려면 구현 뿐만 아니라 운영 또한 직접할 수 있는 능력을 가져야 한다. 그래서 개발과 운영을 하나의 조직에 합쳐 놓는 구조를 Devops라고 한다.

Devops는 Development와 Operation을 합성한 단어로, 개발팀과 운영팀이 다른 팀으로 분리되어 있어서 발생하는 의사 소통의 문제점을 해결하고, 개발이 운영을 고려하고, 운영에서 발생하는 여러 문제점과 고객으로부터의 피드백을 빠르게 수용하여 서비스 개선에 반영하는 개발 모델이다.

이런 모델이 가능해진 이유는 운영팀만의 고유 영역이었던 인프라에 대한 핸들링이 클라우드의 도입으로 인해서 쉬워져서, 애플리케이션 개발자도 웹사이트를 통해서 손쉽게 디스크나 네트워크 설정, 서버 설정등이 가능해졌기 때문이다.

Devops는 대단히 좋은 모델이고 아마존이나 넷플릭스등이 적용하고 있는 모델이기는 하나, 이 역시 대단히 높은 수준의 팀의 성숙도가 필요하다.

개발자가 애플리케이션 개발 뿐만 아니라, 인프라에 대한 설계 및 운영까지 담당해야 하기 때문에 기존의 애플리케이션만 개발하던 입장에서는 대단히 부담이 되는 일이다.

좋은 모델이기는 하지만 충분히 준비가 되지 않은 상태에서 넘어가게 되면은 운영상의 많은 장애를 유발하기 때문에, 팀의 성숙도에 따라서 심각하게 고민해보고 적용을 해보기를 권장한다.


Project vs product


분산형 거버넌스 모델에서 중요한 점 중의 하나는 연속성이다. 거버넌스를 분산 시켜버렸기 때문에 팀별로 다른 형태의 표준과 기술 프로세스를 통해서 개발을 하기 때문에, 새로운 인원이 들어오거나 다른 팀으로 인원이 이동하였을 경우 팀에 맞는 형태의 재 교육이 필요하고 그간의 축적된 노하우가 100% 활용되지 못할 수 가 있기 때문에 가능하면 팀원들은 계속해서 해당 서비스 개발에 집중할 필요가 있다.

이를 위해서는 프로젝트의 컨셉 변화가 필요한데, 일반적으로 프로젝트란 일정 기간에 정해진 요구 사항을 구현하는데 목표가 잡혀 있으며, 프로젝트가 끝나면 인원은 다시 흩어져서 새로운 프로젝트에 투입 되는 형태로 역할 중심의 프로젝트팀 운용 방식에는 적절하다.

그러나 분산형 거버넌스 모델에서는 팀원의 영속성을 보장해줘야 하는데 이를 위해서는 프로젝트가 아니라 프로덕트(즉 상품)형태의 개념으로 개발 모델이 바뀌어야 한다. 팀은 상품에 대한 책임을 지고, 요구사항 정의 발굴에서 부터 개발 그리고 운영까지 책임을 지며, 계속해서 상품을 개선해 나가는 활동을 지속해야 한다. 이를 상품 중심의 개발팀 모델이라고 한다.


Self-organized team


이러한 요건등이 만족 되면, 팀은 독립적으로 서비스 개발을 할 수 있는 형태가 된다. 스스로 기획하고 개발하며 운영을 하며 스스로 서비스를 발전 시키는 하나의 회사와 같은 개념이 되는 것이다.

이렇게 독립적인 수행 능력을 가지고 있는 팀 모델을 self-organized team 모델이라고 한다.


Alignment 


이러한 분산형 거버넌스 모델을 수행하기 전에 반드시 주의해야 할 점이 있는데, alignment 라는 개념이다 alignment는 각 팀간의 커뮤니케이션 방법이나 프로세스등 최소한 표준과 기술적인 수준을 맞추는 과정인데, 쉽게 이야기해서 개발 경험이 전혀 없는 대학을 갓졸업한 사람들로 팀을 만들고, 기존의 팀들은 4~5년차 경력 인원들만으로 팀을 만들어서 전체 팀을 운용하면 어떻게 될까?

마이크로 서비스 아키텍쳐는 각 서비스들이 상호 의존성을 가지고 있기 때문에, 개발경험이 없는 팀이 전체 팀의 개발 속도를 못 따라오고, 또한 품질등에도 심각한 문제가 생긴다. 그래서 어느 일정 수준 이상으로 팀의 능력을 끌어 올려주고, 전체 팀에서 사용하는 최소한의 공통 프로세스등에 대해서는 서로 맞추어 놓을 필요가 있다. 이것이 바로 alignment 의 개념이다.

분산형 거버넌스 모델을 잘못 해석하거나 악용이 되면 팀에게 무조건적인 자치권을 부여하는 것으로 오역되서.. “분산형 거버넌스가 대세랍니다. 우리팀은 우리가 알아서 할테니 신경 끄세요.” 라는 형태의 잘못된 요청으로 전체 팀과 전체 시스템 아키텍쳐를 망쳐 버릴 수 있다.

제대로 된 해석은 “우리는 전체 팀이 나가야 할 방향과 비지니스 밸류에 대해서 이해를 하고 있습니다. 또한 이미 팀간의 커뮤니케이션이나 전체 시스템 구조에 대한 이해를 하고 있습니다. 이를 바탕으로 조금 더 빠른 개발과 효율성을 위한 모든 기능(역할)을 가지고 있는 팀을 운영하고자 합니다.” 가 제대로 된 해석이라고 볼 수 있겠다.


Evolutionary Model (진화형 모델)


지금 까지 간략하게나마 마이크로 서비스 아키텍쳐에 대해서 알아보았다.

마이크로 서비스 아키텍쳐는 서비스의 재사용성, 유연한 아키텍쳐 구조, 대용량 웹 서비스를 지원할 수 있는 구조등으로 많은 장점을 가지고 있지만, 운영하는 팀에 대해서 높은 성숙도를 필요로 한다. 그래서 충분한 능력을 가지지 못한 팀이 마이크로 서비스 아키텍쳐로 시스템을 개발할 경우에는 많은 시행 착오를 겪을 수 있다.

마이크로 서비스 아키텍쳐를 적용할때는 처음 부터 시스템을 마이크로 서비스 아키텍쳐 형태로 설계해서 구현할 수 도 있겠지만, 모노리틱 시스템에서 부터 시작하여, 비지니스 운용시 오는 문제점을 기반으로 점차적으로 마이크로 서비스 아키텍쳐 형태로 진화 시켜 나가는 방안도 좋은 모델이 된다. 비지니스와 고객으로 부터 오는 피드백을 점차적으로 반영 시켜나가면서 동시에 팀의 성숙도를 올려가면서 아키텍쳐 스타일을 변화 시켜가는 모델로, 많은 기업들이 이런 접근 방법을 사용했다. 트위터의 경우에도, 모노리틱 아키텍쳐에서 시작해서 팀의 구조를 점차적으로 변환 시켜 가면서 시스템의 구조 역시 마이크로 서비스 아키텍쳐 형태로 전환을 하였고, 커머스 시장에서 유명한 이베이 같은 경우에도, 그 시대의 기술적 특성을 반영하면서 비지니스의 요구 사항을 적절히 반영 시켜가면서 시스템을 변화 시켜 나가는 진화형 모델로 아키텍쳐를 전환 하였다.


SOA와 비교


마이크로 서비스 아키텍쳐는 종종 SOA와 비교 되며, SOA는 틀리고 마이크로 서비스 아키텍쳐는맞다 흑백 논리 싸움이 벌어지고는 하는데,

SOA와 마이크로 서비스 아키텍쳐는 사실상 다른 개념이 아니라 SOA가 마이크로 서비스 아키텍쳐에 대한 조상 또는 큰 수퍼셋의 개념이다. 흔히 SOA가 잘못되었다고 이야기 하는 이유는 SOA를 아키텍쳐 사상으로 보는 것이 아니라 SOAP 기반의 웹서비스나, Enterprise Service Bus와 같은 특정 제품을 SOA로 인식하기 때문이다. SOA는 말 그대로 설계에 대한 사상이지 특정 기술을 바탕으로 한 구현 아키텍쳐가 아니다.

큰 의미에서 보자면 마이크로 서비스 아키텍쳐의 서비스 역시, SOA에서 정의한 서비스 중에서 fine grained 서비스로 정의되는 하나의 종류이며, api gateway역시 SOA 에서 정의한 ESB의 하나의 구현방식에 불과 하다.

만약에 기회가 된다면 마이크로 서비스 아키텍쳐에 대해 제대로 이해하기 위해서 SOA 를 반드시 공부해보기를 바란다.


결론


마이크로 서비스 아키텍쳐는 대용량 웹시스템에 맞춰 개발된 API 기반의 아키텍쳐 스타일이다. 대규모 웹서비스를 하는 많은 기업들이 이와 유사한 아키텍쳐 설계를 가지고 있지만, 마이크로 서비스 아키텍쳐가 무조건 정답은 아니다. 하나의 설계에 대한 레퍼런스 모델이고, 각 업무나 비지니스에 대한 특성 그리고 팀에 대한 성숙도와 가지고 있는 시간과 돈과 같은 자원에 따라서 적절한 아키텍쳐 스타일이 선택되어야 하며, 또한 아키텍쳐는 처음 부터 완벽한 그림을 그리기 보다는 상황에 맞게 점진적으로 진화 시켜 나가는 모델이 바람직하다.

 특히 근래의 아키텍쳐 모델은 시스템에 대한 설계 사상 뿐만 아니라 개발 조직의 구조나 프로젝트 관리 방법론에 까지 영향을 미치기 때문에 단순히 기술적인 관점에서가 아니라 조금 더 거시적인 관점에서 고려를 해볼 필요가 있다.

 

참고 자료

Ebay 아키텍쳐 : http://www.addsimplicity.com/downloads/eBaySDForum2006-11-29.pdf

Netflix 아키텍쳐 : http://techblog.netflix.com/2013/01/optimizing-netflix-api.html

infoQ Microservice Architecture : http://www.infoq.com/articles/microservices-intro

MicroService 개념 http://microservices.io/patterns/microservices.html

Martin folwer : http://martinfowler.com/articles/microservices.html

Dzone microservice architecture : http://java.dzone.com/articles/microservice-architecture

Thought works의 PPT : http://www.infoq.com/presentations/Micro-Services

node.js로 apigateway 만들기 : 정리 잘되어 있음. http://plainoldobjects.com/presentations/nodejs-the-good-parts-a-skeptics-view/