콘텐츠로 이동

Istio 기반 마이크로서비스 관측 가능성 모범 사례


Istio

Service Mesh란?

지난 몇 년간 마이크로서비스가 소프트웨어 애플리케이션에서 빠르게 보급되면서 대규모 애플리케이션이 여러 개의 마이크로서비스로 분할되었습니다. 각 마이크로서비스는 컨테이너화를 통해 개별 컨테이너에서 실행될 수 있지만, 서비스 간 통신의 네트워크 토폴로지는 여전히 매우 복잡합니다. 마이크로서비스 간 네트워크 통신이 매우 중요하기 때문에, 여러 서비스 프록시를 구현하여 제어된 서비스 간 통신 채널을 안전하고 견고하게 보장하는 기본 구성 요소가 필요합니다.
서비스 메시(Service Mesh)는 이러한 애플리케이션을 구성하는 마이크로서비스 네트워크와 그들 간의 상호작용을 설명하는 데 사용됩니다. 개별 서비스 호출은 Sidecar로 나타납니다. 많은 수의 서비스가 있는 경우 메시 형태로 나타나며, 아래 그림에서 녹색 사각형은 애플리케이션 마이크로서비스를, 파란색 사각형은 Sidecar를, 선은 서비스 간 호출 관계를 나타내며, Sidecar 간의 연결은 네트워크를 형성합니다. image

Istio 개요

Istio는 기존 분산형 애플리케이션 위에 투명하게 계층화되는 오픈소스 서비스 메시입니다. 전체 서비스 메시에 대한 동작 인사이트와 운영 제어 기능, 그리고 마이크로서비스 애플리케이션의 다양한 요구 사항을 충족하는 완전한 솔루션을 제공합니다.

Istio 핵심 컴포넌트

Istio 서비스 메시는 데이터 플레인과 컨트롤 플레인으로 구성됩니다.

  • 데이터 플레인은 지능형 프록시(Envoy) 세트로 구성되며, Envoy는 사이드카로 배포됩니다. 마이크로서비스 간 Sidecar 통신은 정책 제어 및 텔레메트리 수집(Mixer)을 통해 구현됩니다.
  • 컨트롤 플레인은 프록시를 관리 및 구성하여 트래픽을 라우팅합니다. Citadel은 기본 제공 ID 및 자격 증명 관리를 통해 강력한 서비스 간 및 최종 사용자 인증을 제공합니다. Pilot은 Envoy 사이드카에 서비스 디스커버리, 지능형 라우팅(예: A/B 테스트, 카나리아 배포 등), 트래픽 관리 및 오류 처리(타임아웃, 재시도 및 서킷 브레이커) 기능을 제공합니다. Galley는 Istio 구성 검증, 획득, 처리 및 배포 컴포넌트입니다.

image

Istio 분산 추적

Envoy는 Jaeger를 기본 지원하며, 트레이스에 필요한 x-b3로 시작하는 헤더(x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags)와 x-request-id는 비즈니스 로직에 의해 서비스 간에 전달되고 Envoy가 Jaeger에 보고하여 최종적으로 Jaeger가 완전한 트레이스 정보를 생성합니다.
Istio에서 Envoy와 Jaeger의 관계는 다음과 같습니다.

image

그림에서 Front Envoy는 요청을 처음 수신하는 Envoy Sidecar를 의미하며, 루트 스팬을 생성하여 요청 헤더에 추가합니다. 요청이 다른 서비스에 도달하면 Envoy Sidecar가 트레이스 정보를 보고합니다.
Envoy는 분산 추적에서 Jaeger를 기본 지원하며, 외부 추적 서비스(Zipkin, Zipkin 호환 백엔드(Jaeger)) 통합을 지원합니다. Istio 분산 추적은 전역 설정 zipkinAddress를 제공하며, Envoy의 보고 주소는 proxy_init의 --zipkinAddress 파라미터를 통해 전달됩니다.

Istio 관측 가능성

Istio의 강력한 트레이스, 모니터링 및 로깅 기능을 통해 서비스 메시 배포를 심층적으로 이해할 수 있습니다. Istio의 모니터링 기능을 통해 서비스 성능이 업스트림 및 다운스트림에 어떤 영향을 미치는지 실제로 파악할 수 있으며, 맞춤형 대시보드는 모든 서비스 성능에 대한 시각화 기능을 제공하여 다른 프로세스에 미치는 영향을 확인할 수 있습니다. 이러한 모든 기능을 통해 서비스 SLO를 보다 효과적으로 설정, 모니터링 및 강화할 수 있습니다.

BookInfo 개요

이 예시는 여러 Istio 기능을 보여주기 위한 애플리케이션을 배포하며, 이 애플리케이션은 4개의 개별 마이크로서비스로 구성됩니다. 이 애플리케이션은 온라인 서점의 카테고리를 모방하여 책 한 권의 정보를 표시합니다. 페이지에는 책 설명, 책 세부 정보(ISBN, 페이지 수 등) 및 책에 대한 리뷰가 표시됩니다.
Bookinfo 애플리케이션은 4개의 개별 마이크로서비스로 구성됩니다.

  • productpage: productpage(Python) 마이크로서비스는 details 및 reviews 마이크로서비스를 호출하여 페이지를 채웁니다.
  • details: details(Ruby) 마이크로서비스는 책의 상세 정보를 포함합니다.
  • reviews: reviews(Java) 마이크로서비스는 책 리뷰를 포함하며, ratings 마이크로서비스도 호출합니다.
  • ratings: ratings(Node.js) 마이크로서비스는 책의 순위 정보를 포함합니다.

reviews 마이크로서비스는 3가지 버전을 제공합니다.

  • 버전 v1은 ratings 서비스를 호출하지 않습니다.
  • 버전 v2는 ratings 서비스를 호출하고 각 등급을 1~5개의 검은색 별로 표시합니다.
  • 버전 v3는 ratings 서비스를 호출하고 각 등급을 1~5개의 빨간색 별표로 표시합니다.

image

Bookinfo의 트레이스 데이터는 istio의 configmap에서 zipkin.address를 DataKit 주소로 수정하기만 하면 되며, DataKit은 Zipkin 수집기를 활성화해야 트레이스 데이터를 DataKit에 푸시할 수 있습니다.

image

환경 배포

전제 조건

Kubernetes

이 예시는 CentOS 7.9에서 minikube로 생성한 버전 1.21.2의 Kubernetes 클러스터를 사용합니다.

DataKit 배포

<DaemonSet으로 DataKit 배포>를 참조하세요.

수집기 활성화

DaemonSet으로 DataKit 배포datakit.yaml 파일을 사용하여 Kubernetes 클러스터의 마스터 노드 /usr/local/df-demo/datakit.yaml에 업로드하고, datakit.yaml 파일을 수정하여 ConfigMap을 추가하고 파일을 마운트하여 Zipkin 및 Prom 수집기를 활성화합니다. 최종 결과는 배포가 완료된 DataKit입니다.

  • /usr/local/datakit/conf.d/zipkin/zipkin.conf 파일을 추가하여 Zipkin 메트릭 수집기를 활성화합니다.
  • /usr/local/datakit/conf.d/prom/prom_istiod.conf 파일을 추가하여 Istiod Pod의 메트릭 수집기를 활성화합니다.
  • /usr/local/datakit/conf.d/prom/prom-ingressgateway.conf 파일을 추가하여 Ingressgateway의 메트릭 수집기를 활성화합니다.
  • /usr/local/datakit/conf.d/prom/prom-egressgateway.conf 파일을 추가하여 Egressgateway의 메트릭 수집기를 활성화합니다.

image

ingressgateway 및 egressgateway를 수집하려면 Service를 사용하여 15020 포트에 액세스하므로, ingressgateway 및 egressgateway의 Service를 새로 생성해야 합니다.

istio-ingressgateway-service-ext.yaml
apiVersion: v1
kind: Service
metadata:
  name: istio-ingressgateway-ext
  namespace: istio-system
spec:
  ports:
    - name: http-monitoring
      port: 15020
      protocol: TCP
      targetPort: 15020
  selector:
    app: istio-ingressgateway
    istio: ingressgateway
  type: ClusterIP
istio-egressgateway-service-ext.yaml
apiVersion: v1
kind: Service
metadata:
  name: istio-egressgateway-ext
  namespace: istio-system
spec:
  ports:
    - name: http-monitoring
      port: 15020
      protocol: TCP
      targetPort: 15020
  selector:
    app: istio-egressgateway
    istio: egressgateway
  type: ClusterIP

Service 생성

kubectl apply -f istio-ingressgateway-service-ext.yaml
kubectl apply -f istio-egressgateway-service-ext.yaml

다음은 datakit.yaml 파일의 수정 부분입니다.

ConfigMap 추가
apiVersion: v1
kind: ConfigMap
metadata:
  name: datakit-conf
  namespace: datakit
data:
  zipkin.conf: |-
    [[inputs.zipkin]]
      pathV1 = "/api/v1/spans"
      pathV2 = "/api/v2/spans"

  prom_istiod.conf: |-
    [[inputs.prom]] 
      url = "http://istiod.istio-system.svc.cluster.local:15014/metrics"
      source = "prom-istiod"
      metric_types = ["counter", "gauge"]
      interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
      metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
      #measurement_prefix = ""
      measurement_name = "istio_prom"
      #[[inputs.prom.measurements]]
      # prefix = "cpu_"
      # name ="cpu"
      [inputs.prom.tags]
        app_id="istiod"

  prom-ingressgateway.conf: |-
    [[inputs.prom]] 
      url = "http://istio-ingressgateway-ext.istio-system.svc.cluster.local:15020/stats/prometheus"
      source = "prom-ingressgateway"
      metric_types = ["counter", "gauge"]
      interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
      metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
      #measurement_prefix = ""
      measurement_name = "istio_prom"
      #[[inputs.prom.measurements]]
      # prefix = "cpu_"
      # name ="cpu"

  prom-egressgateway.conf: |-
    [[inputs.prom]] 
      url = "http://istio-egressgateway-ext.istio-system.svc.cluster.local:15020/stats/prometheus"
      source = "prom-egressgateway"
      metric_types = ["counter", "gauge"]
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
      interval = "60s"
      metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
      #measurement_prefix = ""
      measurement_name = "istio_prom"
      #[[inputs.prom.measurements]]
      # prefix = "cpu_"
      # name ="cpu"
zipkin.confprom_istiod.conf 마운트
apiVersion: apps/v1
kind: DaemonSet
...
spec:
  template
    spec:
      containers:
      - env:
        volumeMounts: # 아래는 추가된 부분입니다
        - mountPath: /usr/local/datakit/conf.d/zipkin/zipkin.conf
          name: datakit-conf
          subPath: zipkin.conf
        - mountPath: /usr/local/datakit/conf.d/prom/prom_istiod.conf
          name: datakit-conf
          subPath: prom_istiod.conf
        - mountPath: /usr/local/datakit/conf.d/prom/prom-ingressgateway.conf
          name: datakit-conf
          subPath: prom-ingressgateway.conf
        - mountPath: /usr/local/datakit/conf.d/prom/prom-egressgateway.conf
          name: datakit-conf
          subPath: prom-egressgateway.conf

토큰 교체

Guance에 로그인하여 '통합' - 'DataKit'에서 토큰을 복사하고 datakit.yaml<your-token>를 교체합니다.

image

image

DataKit 재배포

cd /usr/local/df-demo
kubectl apply -f datakit.yaml

image

Istio 배포

Istio 다운로드

다운로드 소스 코드istio-1.11.2-linux-amd64.tar.gz

Istio 설치

istio-1.11.2-linux-amd64.tar.gz/usr/local/df-demo/ 디렉터리에 업로드합니다. Kubernetes 서버의 내부 네트워크 주소가 172.16.0.15인지 확인하고 172.16.0.15를 사용자 IP로 교체하세요.

su minikube
cd /usr/local/df-demo/
tar zxvf istio-1.11.2-linux-amd64.tar.gz
cd /usr/local/df-demo/istio-1.11.2
export PATH=$PWD/bin:$PATH$
cp -ar /usr/local/df-demo/istio-1.11.2/bin/istioctl /usr/bin/

istioctl install --set profile=demo

설치 검증

배포가 성공하면 ingressgateway, egressgateway, istiod가 Running 상태가 됩니다.

kubectl get pods -n istio-system

image

BookInfo 배포

파일 복사

소스 코드를 압축 해제하고 /usr/local/df-demo/istio-1.11.2/samples/bookinfo/src/productpage 디렉터리를 /usr/local/df-demo/bookinfo 디렉터리로 복사합니다. bookInfo 배포에 필요한 yaml을 복사합니다.

cp /usr/local/df-demo/istio-1.11.2/samples/bookinfo/networking/bookinfo-gateway.yaml /usr/local/df-demo/bookinfo/bookinfo-gateway.yaml
cp /usr/local/df-demo/istio-1.11.2/samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml /usr/local/df-demo/bookinfo/virtual-service-ratings-test-delay.yaml
cp /usr/local/df-demo/istio-1.11.2/samples/bookinfo/platform/kube/bookinfo.yaml /usr/local/df-demo/bookinfo/bookinfo.yaml

image

자동 주입 활성화

prod 네임스페이스를 생성하고, 해당 네임스페이스에서 Pod 생성 시 사이드카가 자동으로 주입되어 Pod의 인바운드/아웃바운드 트래픽이 모두 사이드카를 통해 처리되도록 활성화합니다.

kubectl create namespace prod
kubectl label namespace prod istio-injection=enabled

RUM 활성화

  • 1 Guance에 로그인하여 '실제 사용자 모니터링(RUM)' - '애플리케이션 생성'에서 bookinfo를 입력하고,
    JS를 /usr/local/df-demo/bookinfo/productpage/templates/productpage.html에 복사한 후 http://<your-外网 ip>:9529로 수정합니다.

image

image

  • 2 /usr/local/df-demo/bookinfo/productpage/Dockerfile 수정

image

  • 3 이미지 빌드
cd /usr/local/df-demo/bookinfo/productpage
eval $(minikube docker-env)
docker build -t product-page:v1  .
  • 4 이미지 교체

/usr/local/df-demo/bookinfo/bookinfo.yamlimage: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2image: product-page:v1로 교체합니다.

image

APM과 DataKit 연결

kubectl edit configmap istio -n istio-system -o yaml

image

위 그림에서 트레이스 데이터가 기본적으로 zipkin.istio-system:9411 주소로 푸시되는 것을 확인할 수 있습니다. DataKit 서비스의 네임스페이스는 datakit이고 포트는 9529이므로, 여기서 변환이 필요합니다.

자세한 내용은 <Kubernetes 클러스터에서 ExternalName을 사용하여 DataKit 서비스 매핑>을 참조하세요.

네임스페이스 추가

bookinfo yaml을 수정하여 모든 리소스의 metadata에 namespace: prod를 추가합니다.

vi /usr/local/df-demo/bookinfo/bookinfo.yaml
vi /usr/local/df-demo/bookinfo/bookinfo-gateway.yaml
vi /usr/local/df-demo/bookinfo/virtual-service-ratings-test-delay.yaml

image

Pod 사용자 정의 수집 활성화

bookinfo.yaml 수정

vi /usr/local/df-demo/bookinfo/bookinfo.yaml

모든 Deployment 컨트롤러의 Pod 템플릿에 annotations을 추가합니다.

파라미터 설명

  • url: Exporter 주소
  • source: 수집기 이름
  • metric_types: 메트릭 유형 필터
  • measurement_name: 수집 후 메저먼트 이름
  • interval: 메트릭 수집 주기(초)
  • $IP: Pod의 내부 네트워크 IP 와일드카드
  • $NAMESPACE: Pod가 속한 네임스페이스
  • tags_ignore: 무시할 태그
annotations:
  datakit/prom.instances: |
    [[inputs.prom]]
      url = "http://$IP:15020/stats/prometheus"
      source = "minik8s-istio-product"
      metric_types = ["counter", "gauge"]
      interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
      metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
      #measurement_prefix = ""
      measurement_name = "istio_prom"
      #[[inputs.prom.measurements]]
      # prefix = "cpu_"
      # name = "cpu"         
      [inputs.prom.tags]
      namespace = "$NAMESPACE"
      pod_name = "$PODNAME"

image

  • 전체 bookinfo.yaml는 다음과 같습니다.
bookinfo.yaml
apiVersion: v1
kind: Service
metadata:
  name: details
  namespace: prod
  labels:
    app: details
    service: details
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: details
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-details
  namespace: prod
  labels:
    account: details
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: details-v1
  namespace: prod
  labels:
    app: details
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: details
      version: v1
  template:
    metadata:
      labels:
        app: details
        version: v1
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-details"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            #measurement_prefix = ""
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    spec:
      serviceAccountName: bookinfo-details
      containers:
      - name: details
        image: docker.io/istio/examples-bookinfo-details-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        securityContext:
          runAsUser: 1000
---
##################################################################################################
# Ratings service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: ratings
  namespace: prod
  labels:
    app: ratings
    service: ratings
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: ratings
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-ratings
  namespace: prod
  labels:
    account: ratings
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ratings-v1
  namespace: prod
  labels:
    app: ratings
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ratings
      version: v1
  template:
    metadata:
      labels:
        app: ratings
        version: v1
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-ratings"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            #measurement_prefix = ""
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    spec:
      serviceAccountName: bookinfo-ratings
      containers:
      - name: ratings
        image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        securityContext:
          runAsUser: 1000
---
##################################################################################################
# Reviews service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: reviews
  namespace: prod
  labels:
    app: reviews
    service: reviews
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: reviews
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-reviews
  namespace: prod
  labels:
    account: reviews
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v1
  namespace: prod
  labels:
    app: reviews
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v1
  template:
    metadata:
      labels:
        app: reviews
        version: v1
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-review1"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            #measurement_prefix = ""
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    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
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v2
  namespace: prod
  labels:
    app: reviews
    version: v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v2
  template:
    metadata:
      labels:
        app: reviews
        version: v2
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-review2"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            #measurement_prefix = ""
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    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
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v3
  namespace: prod
  labels:
    app: reviews
    version: v3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v3
  template:
    metadata:
      labels:
        app: reviews
        version: v3
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-review3"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            #measurement_prefix = ""
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    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
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}
---
##################################################################################################
# Productpage services
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: productpage
  namespace: prod
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-productpage
  namespace: prod
  labels:
    account: productpage
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
  namespace: prod
  labels:
    app: productpage
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: productpage
      version: v1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
      annotations:
        datakit/prom.instances: |
          [[inputs.prom]]
            url = "http://$IP:15020/stats/prometheus"
            source = "minik8s-istio-product"
            metric_types = ["counter", "gauge"]
            interval = "60s"
      tags_ignore = ["cache","cluster_type","component","destination_app","destination_canonical_revision","destination_canonical_service","destination_cluster","destination_principal","group","grpc_code","grpc_method","grpc_service","grpc_type","reason","request_protocol","request_type","resource","responce_code_class","response_flags","source_app","source_canonical_revision","source_canonical-service","source_cluster","source_principal","source_version","wasm_filter"]
            metric_name_filter = ["istio_requests_total","pilot_k8s_cfg_events","istio_build","process_virtual_memory_bytes","process_resident_memory_bytes","process_cpu_seconds_total","envoy_cluster_assignment_stale","go_goroutines","pilot_xds_pushes","pilot_proxy_convergence_time_bucket","citadel_server_root_cert_expiry_timestamp","pilot_conflict_inbound_listener","pilot_conflict_outbound_listener_http_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_tcp","pilot_conflict_outbound_listener_tcp_over_current_http","pilot_virt_services","galley_validation_failed","pilot_services","envoy_cluster_upstream_cx_total","envoy_cluster_upstream_cx_connect_fail","envoy_cluster_upstream_cx_active","envoy_cluster_upstream_cx_rx_bytes_total","envoy_cluster_upstream_cx_tx_bytes_total","istio_request_duration_milliseconds_bucket","istio_request_duration_seconds_bucket","istio_request_bytes_bucket","istio_response_bytes_bucket"]
            #measurement_prefix = ""
            measurement_name = "istio_prom"
            #[[inputs.prom.measurements]]
            # prefix = "cpu_"
            # name = "cpu"
            [inputs.prom.tags]
            namespace = "$NAMESPACE"
    spec:
      serviceAccountName: bookinfo-productpage
      containers:
      - name: productpage
        #image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2
        image: image: product-page:v1
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        securityContext:
          runAsUser: 1000
      volumes:
      - name: tmp
        emptyDir: {}
---
  • 전체 bookinfo-gateway.yaml
bookinfo-gateway.yaml
kind: Gateway
metadata:
  name: bookinfo-gateway
  namespace: prod
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
  namespace: prod
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

서비스 배포

cd /usr/local/df-demo/bookinfo
kubectl apply -f bookinfo.yaml
kubectl apply -f bookinfo-gateway.yaml

nginx로 productpage 서비스 프록시

이 예시에서는 minikube를 사용하므로 nginx를 통해 클러스터 내 서비스를 프록시하도록 nginx를 구성해야 합니다.

  • minikube http2 URL 확인:
minikube service istio-ingressgateway -n istio-system

image

  • root 계정으로 서버에 로그인하여 proxy_pass를 http2 서비스 주소로 수정
vim  /etc/nginx/nginx.conf

image

  • nginx 재시작
systemctl restart nginx

productpage 액세스

http://121.43.225.226/productpage

관측 가능성 실습

메트릭

BookInfo 배포 시 Pod 사용자 정의 수집을 활성화할 때 measurement_name = "istio_prom"를 구성했습니다.

「Guance」 - 「메트릭」에 로그인하여 istio_prom 메저먼트를 확인합니다.

image

트레이스

RUM

실제 사용자 모니터링(RUM) 모듈을 통해 UV, PV, 세션 수, 액세스한 페이지 등의 정보를 확인합니다.

image

image

APM

애플리케이션 성능 모니터링(APM)을 통해 트레이스 데이터를 확인합니다.

image

image

로그

stdout

DataKit은 기본적으로 /dev/stdout에 출력되는 로그를 수집합니다. 더 고급 기능이 필요한 경우 <컨테이너 로그 수집>을 참조하세요.

image

로그 파일

이 예시에서는 로그 파일 수집을 다루지 않습니다. 필요한 경우 <Kubernetes 애플리케이션의 RUM-APM-LOG 연동 분석>을 참조하세요.

트레이스 타임아웃 분석

  • virtual-service-ratings-test-delay.yaml 실행
cd /usr/local/df-demo/bookinfo
kubectl apply -f virtual-service-ratings-test-delay.yaml
  • jason으로 로그인(비밀번호 없음)하고 productpage 인터페이스에 액세스

image

  • 타임아웃된 트레이스를 클릭하고 플레임 그래프를 관찰하여 타임아웃 호출을 찾습니다.

image

image

문서 평가

이 페이지가 도움이 되었나요?