이 연재글은 Service Mesh Architecture의 2번째 글입니다.

Istio

Istio는 서비스 메시 분야에서 오랫동안 주요 역할을 해온 Opensource Solution으로서 기존 분산 애플리케이션 계층에 투명성을 제공하는 완전한 오픈 소스 서비스 메시입니다. Istio를 사용하면 서비스에 대한 연결, 보안, 제어 및 모니터링을 할 수 있습니다. 높은 수준에서 Istio는 배포의 복잡성을 줄이고 개발 팀의 부담을 덜어줄 수 있습니다. Istio의 다양한 기능 세트를 사용하면 분산 마이크로 서비스 아키텍처를 성공적이고 효율적으로 실행할 수 있으며 마이크로 서비스를 보호, 연결 및 모니터링하는 일관된 방법을 제공받을 수 있습니다.

Istio 구조

Control Plain은 트래픽 동작을 제어하는 ​​높은 수준의 라우팅 규칙을 Envoy Proxy전용 구성으로 변환하고 런타임에 사이드카로 전파합니다. Data Plane은 사이드카로 배포된 지능형 Proxy(Envoy) 세트로 구성됩니다. Envoy Proxy는 마이크로 서비스 간의 모든 네트워크 통신을 중재하며 모든 Mesh 트래픽에 대한 원격 분석 데이터를 수집하고 보고합니다.

EKS(Elastic Kubernetes Service)에 Istio Mesh 적용

Install Istioctl(Istio control)

$ curl -L https://git.io/getLatestIstio | sh -
$ cd istio-*
$ sudo mv -v bin/istioctl /usr/local/bin/

Install Istio

$ istioctl install
This will install the default Istio profile into the cluster. Proceed? (y/N) y
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ Installation complete
$ kubectl -n istio-system get deploy
NAME                   READY     UP-TO-DATE   AVAILABLE   AGE
NAME                   READY     UP-TO-DATE   AVAILABLE   AGE
istio-ingressgateway   1/1       1            1           42s
istiod                 1/1       1            1           58s

Envoy 사이드카 Injection을 위한 label 설정

애플리케이션을 배포할 때 Istio가 Envoy 사이드카 Proxy를 자동으로 삽입하도록 지시하는 namespace label을 추가합니다. namespace에 istio-injection = enabled 레이블을 설정하고 injection webhook이 활성화되면 해당 namespace에서 생성된 모든 새 pod에는 자동으로 사이드카가 추가됩니다.

$ kubectl label namespace default istio-injection=enabled
// 확인
$ kubectl get namespaces --show-labels
NAME              STATUS    AGE       LABELS
default           Active    3d8h      istio-injection=enabled
istio-system      Active    2m28s     istio-injection=disabled
kube-node-lease   Active    3d8h      <none>
kube-public       Active    3d8h      <none>
kube-system       Active    3d8h      <none>

Sample App (BookInfo)

Istio 설치 시 제공되는 sample 애플리케이션을 구동하여 Mesh를 테스트 해보겠습니다. (istio-1.7.1/samples/bookinfo)

BookInfo는 총 4개의 마이크로 서비스로 나뉘어져 있습니다.

productpage
productpage 서비스는 Review, Detail 마이크로 서비스로 부터 데이터를 받아 Web Front 서비스를 제공합니다.

details
details 서비스는 book 정보를 제공합니다.

reviews
reviews 서비스는 book review 정보를 제공하며 내부적으로 ratings 마이크로 서비스를 호출합니다.

ratings
ratings 서비스는 book review와 함께 제공되는 book 등급 정보를 제공합니다.

reviews 서비스는 3가지 버전이 존재합니다.

Version v1
ratings 서비스를 호출하지 않습니다.

Version v2
ratings 서비스를 호출하고 등급은 1~5개의 검은 색 별표로 표시합니다.

Version v3
ratings 서비스를 호출하고 등급은 1~5개의 빨간 색 별표로 표시합니다.

Deply BookInfo

다음 명령을 통해 BookInfo 서비스를 EKS Cluster에 배포합니다.

bookinfo.yaml 펼치기
##################################################################################################
# Details service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: details
  labels:
    app: details
    service: details
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: details
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-details
  labels:
    account: details
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: details-v1
  labels:
    app: details
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: details
      version: v1
  template:
    metadata:
      labels:
        app: details
        version: v1
    spec:
      serviceAccountName: bookinfo-details
      containers:
      - name: details
        image: docker.io/istio/examples-bookinfo-details-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
##################################################################################################
# Ratings service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: ratings
  labels:
    app: ratings
    service: ratings
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: ratings
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-ratings
  labels:
    account: ratings
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ratings-v1
  labels:
    app: ratings
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ratings
      version: v1
  template:
    metadata:
      labels:
        app: ratings
        version: v1
    spec:
      serviceAccountName: bookinfo-ratings
      containers:
      - name: ratings
        image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---
##################################################################################################
# Reviews service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: reviews
  labels:
    app: reviews
    service: reviews
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: reviews
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-reviews
  labels:
    account: reviews
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v1
  labels:
    app: reviews
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v1
  template:
    metadata:
      labels:
        app: reviews
        version: v1
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.2
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v2
  labels:
    app: reviews
    version: v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v2
  template:
    metadata:
      labels:
        app: reviews
        version: v2
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.2
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v3
  labels:
    app: reviews
    version: v3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v3
  template:
    metadata:
      labels:
        app: reviews
        version: v3
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
##################################################################################################
# Productpage services
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-productpage
  labels:
    account: productpage
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
  labels:
    app: productpage
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: productpage
      version: v1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
    spec:
      serviceAccountName: bookinfo-productpage
      containers:
      - name: productpage
        image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir: {}
---
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

배포내역 확인

details, productpage, ratings의 경우는 v1만 배포되고 review의 경우는 v3까지 배포된것을 확인할 수 있습니다.
또한 Pod에 Envoy가 Injection되어 컨테이너 개수가 READY 2/2로 표시됩니다.

$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
details-v1-5974b67c8-6hsqt        2/2       Running   0          82s
productpage-v1-64794f5db4-vgfpf   2/2       Running   0          82s
ratings-v1-c6cdf8d98-rxdfc        2/2       Running   0          81s
reviews-v1-7f6558b974-r57mj       2/2       Running   0          82s
reviews-v2-6cb6ccd848-q6v9b       2/2       Running   0          82s
reviews-v3-cc56b578-lzfnb         2/2       Running   0          82s

Request Routing

Istio ingress Gateway를 사용하여 서비스 메시 외부에 서비스를 노출하도록 구성할 수 있습니다. Gateway는 광범위한 사용자 정의 및 유연성을 제공하며 모니터링 및 라우팅 규칙과 같은 Istio 기능을 클러스터로 들어오는 트래픽에 적용할 수 있습니다.

예제에서는 80 port로 서비스되는 Istio Gateway를 만든 다음 VirtualService 설정을 통해 정의된 path로 들어오는 요청을 productService:9080 port에서 서비스 되도록 처리합니다.

$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
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:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080

기본 설정으로 Gateway를 생성하면 Classic Load Balancer로 생성됩니다. Network Load Balancer로 생성하려면 아래 내용을 수행합니다.

istio_enable_nlb.yaml

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    gateways:
      istio-ingressgateway:
        serviceAnnotations:
          service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

NLB 적용

$ istioctl upgrade -f istio_enable_nlb.yaml

확인

ingress gateway를 적용하면 loadbalancer가 생성되고 routing 규칙이 적용됩니다.

$ kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP                                                                   PORT(S)                                                      AGE
istio-ingressgateway   LoadBalancer   10.100.84.203   a03e42f6f424d465c949f29bcdcb592d-dceda3ad59570e1f.elb.ap-southeast-1.amazonaws.com   15021:30709/TCP,80:30936/TCP,443:32289/TCP,15443:30701/TCP   17m

새 브라우저를 열고 DNS 엔드 포인트를 붙여 넣습니다. 해당 DNS 엔드 포인트 끝에 /productpage를 추가하여 요청하면 Sample App을 확인할 수 있습니다. 리프레시 할 때마다 Review서비스의 다른 버전(v1, v2, v3)이 호출되어 화면 내용이 변경되는 것을 확인할 수 있습니다.

v1

v2

v3

Intelligent Routing

Istio 서비스 메시에 마이크로 서비스 기반 애플리케이션을 배포하면 일관된 방식으로 라우팅을 외부에서 제어할 수 있습니다.

Destination Rule 추가

Istio를 사용하여 버전 라우팅을 제어하려면 먼저 대상 규칙에서 사용 가능한 버전 (subset)을 정의해야 합니다.
아래와 같이 Destination Rule을 작성하여 Cluster에 적용합니다.

review 서비스의 경우 버전에 따라 subset이 3개가 작성됩니다.

destination-rule-all.yaml 펼치기
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: productpage
spec:
  host: productpage
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: ratings
spec:
  host: ratings
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: details
spec:
  host: details
  subsets:
  - name: v1
    labels:
      version: v1
---
$ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml

특정 버전만 서비스하기

VirtualService에서 subset을 지정하면 특정 버전만 서비스할 수 있습니다. 아래 내용을 적용하면 review 서비스가 v1만 서비스 하게 됩니다.

virtual-service-all-v1.yaml 펼치기
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: productpage
spec:
  hosts:
  - productpage
  http:
  - route:
    - destination:
        host: productpage
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
spec:
  hosts:
  - ratings
  http:
  - route:
    - destination:
        host: ratings
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: details
spec:
  hosts:
  - details
  http:
  - route:
    - destination:
        host: details
        subset: v1
---
$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml

페이지를 여러 번 새로 고침하면 매번 v1의 리뷰만 표시되는 것을 확인할 수 있습니다.

특정 User에게 특정 버전만 서비스하기

아래 내용을 적용하면 Review서비스의 v1을 기본으로 서비스 하지만 jason으로 로그인한 경우에는 v2가 서비스 됩니다.

virtual-service-reviews-test-v2.yaml 펼치기
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    route:
    - destination:
        host: reviews
        subset: v2
  - route:
    - destination:
        host: reviews
        subset: v1
$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml

미로그인시

jason으로 로그인시

Fault Injection

애플리케이션의 복원력을 테스트하기 위해 오류를 삽입할 수 있습니다.

다음 내용을 적용하면 jason으로 로그인시 rating서비스가 100% 확률로 7초의 딜레이가 발생됩니다.

virtual-service-ratings-test-delay.yaml 펼치기
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
spec:
  hosts:
  - ratings
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    fault:
      delay:
        percentage:
          value: 100.0
        fixedDelay: 7s
    route:
    - destination:
        host: ratings
        subset: v1
  - route:
    - destination:
        host: ratings
        subset: v1
$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml

다음 내용을 적용하면 json으로 로그인한 경우 Ratings 서비스에서 무조건 500 에러가 발생합니다.

virtual-service-ratings-test-abort.yaml 펼치기
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
spec:
  hosts:
  - ratings
  http:
  - match:
    - headers:
        end-user:
          exact: jason
    fault:
      abort:
        percentage:
          value: 100.0
        httpStatus: 500
    route:
    - destination:
        host: ratings
        subset: v1
  - route:
    - destination:
        host: ratings
        subset: v1
$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml

한 버전에서 다른 버전으로 트래픽을 점진적으로 마이그레이션

아래 내용을 적용하면 모든 review 서비스 요청에 대해 트래픽의 50%는 v1으로 나머지 50%는 v3로 처리되도록 설정합니다.

review 서비스가 v1만 서비스 하도록 적용
virtual-service-all-v1.yaml 펼치기

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: productpage
spec:
  hosts:
  - productpage
  http:
  - route:
    - destination:
        host: productpage
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: ratings
spec:
  hosts:
  - ratings
  http:
  - route:
    - destination:
        host: ratings
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: details
spec:
  hosts:
  - details
  http:
  - route:
    - destination:
        host: details
        subset: v1
---
$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml


비율에 따라 v1, v3를 서비스하도록 적용
virtual-service-reviews-50-v3 펼치기

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 50
    - destination:
        host: reviews
        subset: v3
      weight: 50
$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml

테스트를 위해 브라우저를 여러번 리프레시 하면 v1과 v3버전이 전환되며 표시되는것을 확인 할 수 있습니다.

Circuit Breaking

Circuit breaking은 탄력적인 마이크로 서비스 애플리케이션을 만드는 데 중요한 패턴입니다. Circuit breaking을 사용하면 장애의 영향, 지연 시간 급증 및 기타 네트워크 특성의 바람직하지 않은 영향을 제한하는 애플리케이션을 작성할 수 있습니다.

  • 만일 하나의 서비스가 장애가 발생하게 되면 장애 서비스를 호출하는 서비스는 대기 상태가 되고 다시 대기 중인 서비스를 호출하는 또 다른 서비스도 대기하게 되어 장애가 차례대로 전파됩니다.
  • Circuit Breaker의 역할은 이렇게 문제가 되는 기능 자체를 동작하지 않도록하여 리소스 점유의 증가로 장애가 전파되지 않도록 하는데 목적이 있습니다.

circuit break 대상 대상이 되는 httpbin 앱을 설치합니다. httpbin 은 HTTP 프로토콜 echo 응답 앱입니다.

$ kubectl label namespace default istio-injection=enabled
$ kubectl apply -f samples/httpbin/httpbin.yaml

마이크로서비스 로드 테스트 툴인 fortio를 설치합니다.

$ kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml
$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
details-v1-5974b67c8-6hsqt        2/2       Running   0          24h
fortio-deploy-7cb865f87f-vj22f    2/2       Running   0          25m
httpbin-779c54bf49-lmrz5          2/2       Running   0          25m

fortio pod에 접속하여 httpbin 으로 요청해봅니다. 200(정상) 응답 코드가 리턴됩니다.

$ export FORTIO_POD=$(kubectl get pods -lapp=fortio -o 'jsonpath={.items[0].metadata.name}')
$ kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio curl -quiet http://httpbin:8000/get
HTTP/1.1 200 OK
server: envoy
date: Tue, 15 Sep 2020 02:24:38 GMT
content-type: application/json
content-length: 621
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 3

{
  "args": {},
  "headers": {
    "Content-Length": "0",
    "Host": "httpbin:8000",
    "User-Agent": "fortio.org/fortio-1.6.8",
    "X-B3-Parentspanid": "173c3b9531a2da2c",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "a9b2ba6ff10a3e7d",
    "X-B3-Traceid": "64b2e647a1787138173c3b9531a2da2c",
    "X-Envoy-Attempt-Count": "1",
    "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=5f73a527d698903bbaffe50b5838079fc2f5be3ee394fe637e07c8efb1772475;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
  },
  "origin": "127.0.0.1",
  "url": "http://httpbin:8000/get"
}

kiali Dashboard Graph는 아래와 같습니다. circuit breaker가 설정되면 번개 표시로 나타납니다.

DestinationRule 작성

circuit break 설정을 적용하는 Destination Rule을 만들어 적용합니다. 테스트를 위해 최소한의 커넥션만 허용하도록 설정합니다.

Destination Rule 적용 펼치기

circuitbreak_rule.yaml

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 1
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1
    outlierDetection:
      consecutiveErrors: 1
      interval: 1s
      baseEjectionTime: 3m
      maxEjectionPercent: 100

DestinationRule 적용

$ kubectl apply -f circuitbreak_rule.yaml
destinationrule.networking.istio.io/httpbin created

테스트

두 개의 동시 연결 ( -c 2)로 서비스를 호출하고 20 개의 요청 ( -n 20)을 보냅니다 . 요청의 85%가 성공했고 15%는 circuit breaker에 의해 차단되었습니다.

$ kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 2 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get
02:34:15 I logger.go:115> Log level is now 3 Warning (was 2 Info)
Fortio 1.6.8 running at 0 queries per second, 2->2 procs, for 20 calls: http://httpbin:8000/get
Starting at max qps with 2 thread(s) [gomax 2] for exactly 20 calls (10 per thread + 0)
02:34:15 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:34:15 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:34:15 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
Ended after 124.335657ms : 20 calls. qps=160.85
Aggregated Function Time : count 20 avg 0.011879563 +/- 0.009008 min 0.001159891 max 0.03371059 sum 0.237591251
# range, mid point, percentile, count
>= 0.00115989 <= 0.002 , 0.00157995 , 5.00, 1
> 0.003 <= 0.004 , 0.0035 , 10.00, 1
> 0.004 <= 0.005 , 0.0045 , 30.00, 4
> 0.005 <= 0.006 , 0.0055 , 35.00, 1
> 0.006 <= 0.007 , 0.0065 , 45.00, 2
> 0.007 <= 0.008 , 0.0075 , 50.00, 1
> 0.008 <= 0.009 , 0.0085 , 55.00, 1
> 0.009 <= 0.01 , 0.0095 , 60.00, 1
> 0.011 <= 0.012 , 0.0115 , 65.00, 1
> 0.016 <= 0.018 , 0.017 , 75.00, 2
> 0.02 <= 0.025 , 0.0225 , 90.00, 3
> 0.025 <= 0.03 , 0.0275 , 95.00, 1
> 0.03 <= 0.0337106 , 0.0318553 , 100.00, 1
# target 50% 0.008
# target 75% 0.018
# target 90% 0.025
# target 99% 0.0329685
# target 99.9% 0.0336364
Sockets used: 5 (for perfect keepalive, would be 2)
Jitter: false
Code 200 : 17 (85.0 %)
Code 503 : 3 (15.0 %)
Response Header Sizes : count 20 avg 195.8 +/- 82.25 min 0 max 231 sum 3916
Response Body/Total Sizes : count 20 avg 759.8 +/- 217.9 min 241 max 852 sum 15196
All done 20 calls (plus 0 warmup) 11.880 ms avg, 160.9 qps

동시 연결 수를 최대 3 개로 늘리고 30개의 요청을 보냅니다. 요청의 60%가 성공했고 40%는 circuit breaker에 의해 차단되었습니다.

$ kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 3 -qps 0 -n 30 -loglevel Warning http://httpbin:8000/get
02:36:18 I logger.go:115> Log level is now 3 Warning (was 2 Info)
Fortio 1.6.8 running at 0 queries per second, 2->2 procs, for 30 calls: http://httpbin:8000/get
Starting at max qps with 3 thread(s) [gomax 2] for exactly 30 calls (10 per thread + 0)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
02:36:18 W http_client.go:698> Parsed non ok code 503 (HTTP/1.1 503)
Ended after 110.666685ms : 30 calls. qps=271.08
Aggregated Function Time : count 30 avg 0.0095096311 +/- 0.007669 min 0.001468498 max 0.032481834 sum 0.285288932
# range, mid point, percentile, count
>= 0.0014685 <= 0.002 , 0.00173425 , 10.00, 3
> 0.003 <= 0.004 , 0.0035 , 20.00, 3
> 0.004 <= 0.005 , 0.0045 , 30.00, 3
> 0.005 <= 0.006 , 0.0055 , 40.00, 3
> 0.006 <= 0.007 , 0.0065 , 53.33, 4
> 0.007 <= 0.008 , 0.0075 , 63.33, 3
> 0.008 <= 0.009 , 0.0085 , 73.33, 3
> 0.011 <= 0.012 , 0.0115 , 76.67, 1
> 0.012 <= 0.014 , 0.013 , 80.00, 1
> 0.018 <= 0.02 , 0.019 , 86.67, 2
> 0.02 <= 0.025 , 0.0225 , 93.33, 2
> 0.025 <= 0.03 , 0.0275 , 96.67, 1
> 0.03 <= 0.0324818 , 0.0312409 , 100.00, 1
# target 50% 0.00675
# target 75% 0.0115
# target 90% 0.0225
# target 99% 0.0317373
# target 99.9% 0.0324074
Sockets used: 14 (for perfect keepalive, would be 3)
Jitter: false
Code 200 : 18 (60.0 %)
Code 503 : 12 (40.0 %)
Response Header Sizes : count 30 avg 138.2 +/- 112.8 min 0 max 231 sum 4146
Response Body/Total Sizes : count 30 avg 607.2 +/- 299 min 241 max 852 sum 18216
All done 30 calls (plus 0 warmup) 9.510 ms avg, 271.1 qps

circuit break가 작동되었을때 kiali 화면에는 아래와 같이 표시됩니다.

Visualizing Metrics

Prometheus

Prometheus는 오픈 소스 모니터링 시스템 및 시계열 데이터베이스입니다. Istio와 함께 Prometheus를 사용하여 Istio 및 서비스 메시 내 애플리케이션의 상태를 추적하는 메트릭을 기록할 수 있습니다. Grafana 및 Kiali와 같은 도구를 사용하여 메트릭을 시각화할 수 있습니다.

Prometheus Install

Prometheuse Install 펼치기
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/prometheus.yaml
serviceaccount/prometheus created
configmap/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus created
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
service/prometheus created
deployment.apps/prometheus created
$ kubectl -n istio-system get svc prometheus
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
prometheus   ClusterIP   10.100.160.49   <none>        9090/TCP   25s

Grafana

Grafana는 Istio 용 대시 보드를 구성하는 데 사용할 수 있는 오픈 소스 모니터링 Solution입니다. Grafana를 사용하여 Istio 및 서비스 메시 내 애플리케이션의 상태를 모니터링할 수 있습니다.

Grafana Install

Grafana Install 펼치기
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/grafana.yaml
serviceaccount/grafana created
configmap/grafana created
service/grafana created
deployment.apps/grafana created
configmap/istio-grafana-dashboards created
configmap/istio-services-grafana-dashboards created
$ kubectl -n istio-system get svc grafana
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
grafana   ClusterIP   10.100.182.135   <none>        3000/TCP   2m43s

Expose Service

grafana_service.yaml

apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: istio-system
spec:
  selector:
    app: grafana
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
$ kubectl apply -f grafana_service.yaml
$ kubectl get svc grafana -n istio-system
NAME      TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)        AGE
grafana   LoadBalancer   10.100.235.59   a6542d4281e384e0cb34c8536eecz678-1918338663.ap-southeast-1.elb.amazonaws.com   80:32390/TCP   3m15s

kiali

Kiali는 서비스 메시 구성 및 유효성 검사 기능이 있는 Istio 용 관찰 콘솔입니다. 트래픽 흐름을 모니터링하여 토폴로지를 추론하고 오류를 보고함으로써 서비스 메시의 구조와 상태를 이해하는 데 도움이 됩니다. Kiali는 고급 쿼리에 사용할 수 있는 자세한 메트릭과 기본 Grafana 통합을 제공합니다. Jaeger와의 통합을 통해 분산 추적이 제공됩니다.

kiali Install

Kiali Install 펼치기
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/kiali.yaml

Expose Service

kiali_service.yaml

apiVersion: v1
kind: Service
metadata:
  name: kiali
  namespace: istio-system
spec:
  selector:
    app: kiali
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: 20001
$ kubectl apply -f kiali_service.yaml
$ kubectl get svc kiali -n istio-system
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP                                                                   PORT(S)        AGE
kiali     LoadBalancer   10.100.205.7   aeae4ccc311404df4xc9b9e6d235377e-789779401.ap-southeast-1.elb.amazonaws.com   80:30653/TCP   8m46s

Jaeger

Jaeger는 오픈 소스 end to end 분산 추적 시스템으로, 사용자는 복잡한 분산 시스템에서 트랜잭션을 모니터링하고 문제를 해결할 수 있습니다. Jaeger를 이용하면 개별 분산 트렌젝션에 대해서 각 구간별 응답 시간을 모니터링할 수 있어 문제 지점을 파악하는데 도움을 받을 수 있습니다.

Jaeger Install 펼치기

jaeger.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jaeger
  namespace: istio-system
  labels:
    app: jaeger
spec:
  selector:
    matchLabels:
      app: jaeger
  template:
    metadata:
      labels:
        app: jaeger
      annotations:
        sidecar.istio.io/inject: "false"
        prometheus.io/scrape: "true"
        prometheus.io/port: "14269"
    spec:
      containers:
        - name: jaeger
          image: "docker.io/jaegertracing/all-in-one:1.18"
          env:
            - name: BADGER_EPHEMERAL
              value: "false"
            - name: SPAN_STORAGE_TYPE
              value: "badger"
            - name: BADGER_DIRECTORY_VALUE
              value: "/badger/data"
            - name: BADGER_DIRECTORY_KEY
              value: "/badger/key"
            - name: COLLECTOR_ZIPKIN_HTTP_PORT
              value: "9411"
            - name: MEMORY_MAX_TRACES
              value: "50000"
            - name: QUERY_BASE_PATH
              value: /jaeger
          livenessProbe:
            httpGet:
              path: /
              port: 14269
          readinessProbe:
            httpGet:
              path: /
              port: 14269
          volumeMounts:
            - name: data
              mountPath: /badger
          resources:
            requests:
              cpu: 10m
      volumes:
        - name: data
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: tracing
  namespace: istio-system
  labels:
    app: jaeger
spec:
  type: LoadBalancer
  ports:
    - name: http-query
      port: 80
      protocol: TCP
      targetPort: 16686
  selector:
    app: jaeger
---
# Jaeger implements the Zipkin API. To support swapping out the tracing backend, we use a Service named Zipkin.
apiVersion: v1
kind: Service
metadata:
  labels:
    name: zipkin
  name: zipkin
  namespace: istio-system
spec:
  ports:
    - port: 9411
      targetPort: 9411
      name: http-query
  selector:
    app: jaeger
$ kubectl apply -f jaeger.yaml
$ kubectl get svc tracing -n istio-system
NAME      TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)        AGE
tracing   LoadBalancer   10.100.210.106   aa34a215874bf4a33b8d27d4fdb1c1dd-696488506.ap-southeast-1.elb.amazonaws.com   80:32018/TCP   11m

Uninstall Istio

$ kubectl delete -f samples/addons
$ istioctl manifest generate --set profile=demo | kubectl delete --ignore-not-found=true -f -

namespaces는 지워지지 않으므로 아래와 같이 수동으로 삭제합니다.

$ kubectl delete namespace istio-system

[참고]

연재글 이동[이전글] 서비스 메시(Service Mesh)
[다음글] AWS App Mesh