들어가며

이번주에는 Service Mesh와 대표적인 오픈소스 프로젝트인 Istio에 대해 알아 보겠습니다. KANS 3기 7주차 스터디를 시작하겠습니다.


Istio 소개

Istio는 서비스 메시를 구축하고 관리하기 위한 오픈소스 플랫폼입니다. Istio를 알아보기에 앞서 서비스 메시에 대해 알아보겠습니다.

서비스 메시 (Service Mesh)란?

  • 서비스 메시는 서비스 간 통신을 제어하고 모니터링하는 레이어를 제공하는 인프라스트럭처 계층입니다.
  • 등장배경 : MSA 환경에서 서비스가 많아지다 보니 서비스 간 통신이 복잡해지고, 이로 인해 서비스 간 통신을 관리하고 모니터링하는 것이 어려워졌습니다. 이로인해 장애가 발생하거나 병목 현상이 발생했을때 원인과 발생하는 구간을 찾기가 어려워졌습니다. 이것을 해결 하기 위해 등장한 것이 서비스 메시입니다. img.png
  • 개념 : 마이크로 서비스 간에 통신이나 경로를 제어 - 예) istio, linkerd, consul, envoy, …
  • 기본 동작 : 파드간 통신경로에 프록시를 두고 트래픽을 모니터링하거나 컨트롤 합니다. 따라서 기존 어플리케이션을 수정하지 않고도 적용할 수 있습니다. img.png 위의 그림 처럼 서비스 메시는 각 파드에 프록시를 두고 프록시를 통해 통신을 하도록한 다음 프록시를 통해 트래픽을 모니터링하거나 컨트롤 합니다.
    • 이때 프록시는 Sidecar 모드로 동작하거나 Ambient 모드로 동작하며 대표적인 프록시로는 Envoys가 있습니다.
    • Envoy는 구글, IBM, Lyft가 중심이 되어 개발하고 있는 오픈소스 프록시입니다.
    • 네트워크 투명성을 목표로 다양한 필터 체인 지원(L3, L4, L7), 동적 Configuration API를 제공하고, hot reload를 지원합니다.
  • 주요기능
    • 트래픽 모니터링 : 요청의 에러율, 지연시간, 컨넥션 개수, 요청개수 등의 메트릭을 수집하여 모니터링하고, 서비스간 혹은 특정 요청 경로를 필터링 할 수 있습니다. => 원인 파악 용이
    • 트래픽 컨트롤
      • 트래픽 시프팅(traffic shifting) : 트래픽을 서비스간에 분산시키는 기능으로, 특정 단말/사용자는 신규 어플리케이션에 연결하도록 하는 카나리 배포등에 활용할 수도 있습니다.
      • 서킷 브레이커(circuit breaker) : 특정 서비스에 문제가 있을때 접속을 차단하고, 출발지 서비스에 에러를 반환하도록 하는 기능입니다. (연쇄장애, 시스템 전체 장애 방지)
      • 플트 인젝션(fault injection) : 의도적으로 요청을 지연시키거나 실패하도록 할 수 있습니다. (비정상 상황 테스트)
      • 속도 제한(rate limiting) : 특정 서비스에 대한 요청 개수를 제한하는 기능입니다.

Envoy

  • 지난주에 살짝 언급되었던 내용인데 이번 주에 좀 더 자세히 알아보겠습니다.
  • Envoy는 구글, IBM, Lyft가 중심이 되어 개발하고 있는 오픈소스 프록시입니다.
  • Istio의 핵심 기능들은 Envoy를 감싼 istio proxy를 통해 이루어지므로 Envoy에 대한 이해가 필요합니다.

20241019_kans_w7_4.svg

  • Envoy에서 사용하는 용어 들을 정리해 보았습니다.
용어 설명
Cluster envoy가 트래픽을 포워딩할 수 있는 논리적 서비스 (엔드포인트 셋트)
Endpoint IP 주소와 포트 번호로 구성된 서비스의 실제 인스턴스. 엔드포인트가 모여서 하나의 Cluster를 이룸
Listener 클라이언트가 접속하는 포트, 유닉스 도메인 소켓 등을 노출하고, 다운스트림으로 부터 받은 요청을 처리
Route Listener로 들어온 요청을 어떤 클러스터로 보낼지 정의
Filter Listener로 부터 서비스에 트래픽 전달하기 전에 트래픽을 가공하거나 차단하는 역할을 하는 컴포넌트
UpStream envoy 요청을 포워딩해서 연결하는 백엔드 네트워크 노드 - 사이드카일때는 application app, 아닐때는 원격 백엔드
DownStream envoy로 연결하여 요청을 보내는 개체. 사이드카가 아닐때는 원격지의 클라이언트
Host 네트워크 통신이 가능한 개체 (PC, 서버, 휴대폰, 네트워크 어플리케이션 등)
  • 많은 Service Mesh 솔루션이나, Gateway API 구현체들이 내부적으로 Envoy를 사용하고 있으며, Envoy가 제공하는 동적 구성을 위한 API(xDS Sync API)를 이용하여 다양한 네트워크 정책을 구성하게 됩니다.
  • Envoy의 xDS Sync API는 아래와 같은 레이어에서 동작합니다.
    • LDS - Listener Discovery Service
    • RDS - Route Discovery Service
    • CDS - Cluster Discovery Service
    • EDS - Endpoint Discovery Service

img.png

img.png

Envoy 실습

  • test pc에 Envoy 설치
# 설치
# echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io focal main" | sudo tee /etc/apt/sources.list.d/envoy.list
$ wget -O- https://apt.envoyproxy.io/signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/envoy-keyring.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io jammy main" | sudo tee /etc/apt/sources.list.d/envoy.list
$ sudo apt-get update && sudo apt-get install envoy -y

# 확인
$ envoy --version
# => envoy  version: e3b4a6e9570da15ac1caffdded17a8bebdc7dfc9/1.32.0/Clean/RELEASE/BoringSSL

# 도움말
$ envoy --help
  • Envoy proxy 실습 - Link
    • envoy-demo.yml 작성
      # envoy-demo.yml
      static_resources:
          
        listeners:
        - name: listener_0
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 10000
          filter_chains:
          - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: ingress_http
                access_log:
                - name: envoy.access_loggers.stdout
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
                http_filters:
                - name: envoy.filters.http.router
                route_config:
                  name: local_route
                  virtual_hosts:
                  - name: local_service
                    domains: ["*"]
                    routes:
                    - match:
                        prefix: "/"
                      route:
                        host_rewrite_literal: www.envoyproxy.io
                        cluster: service_envoyproxy_io
          
        clusters:
        - name: service_envoyproxy_io
          type: LOGICAL_DNS
          # Comment out the following line to test on v6 networks
          dns_lookup_family: V4_ONLY
          connect_timeout: 5s
          load_assignment:
            cluster_name: service_envoyproxy_io
            endpoints:
            - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: www.envoyproxy.io
                      port_value: 443
          transport_socket:
            name: envoy.transport_sockets.tls
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
              sni: www.envoyproxy.io
      
    • 실행

        # (터미널1) 데모 config 적용하여 실행
        $ curl -O https://www.envoyproxy.io/docs/envoy/latest/_downloads/92dcb9714fb6bc288d042029b34c0de4/envoy-demo.yaml
        $ envoy -c envoy-demo.yaml
        # => [2024-10-01 16:41:51.547][4479][info][main] [source/server/server.cc:426] initializing epoch 0 (base id=0, hot restart version=11.104)
        #    ...
          
        # (터미널2) 정보 확인
        $ ss -tnlp
        # => State           Recv-Q           Send-Q                     Local Address:Port                      Peer Address:Port          Process
        #    LISTEN          0                4096                             0.0.0.0:10000                          0.0.0.0:*              users:(("envoy",pid=4479,fd=35))
        #    LISTEN          0                4096                             0.0.0.0:10000                          0.0.0.0:*              users:(("envoy",pid=4479,fd=34))
        #    LISTEN          0                4096                             0.0.0.0:10000                          0.0.0.0:*              users:(("envoy",pid=4479,fd=33))
        #    LISTEN          0                4096                             0.0.0.0:10000                          0.0.0.0:*              users:(("envoy",pid=4479,fd=32))
        #    ...
          
        # 접속 테스트
        $ curl -s http://127.0.0.1:10000 | grep -o "<title>.*</title>"
        # => &lt;title&gt;Envoy proxy - home&lt;/title&gt;
          
        # 외부 접속 정보 출력
        $ echo -e "http://$(curl -s ipinfo.io/ip):10000"
        # => http://54.123.42.212:10000
          
        --------------------
        # 자신의 PC 웹브라우저에서 외부 접속 정보 접속 확인!
          
        # k3s-m 에서 접속 테스트
        $ curl -s http://192.168.56.104:10000 | grep -o "<title>.*</title>"
        # => &lt;title&gt;Envoy proxy - home&lt;/title&gt; 
        --------------------
          
        # 연결 정보 확인
        $ ss -tnp
          
        # (터미널1) envoy 실행 취소(CTRL+C) 후 (관리자페이지) 설정 덮어쓰기 - 링크
        $ cat <<EOT> envoy-override.yaml
        admin:
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 9902
        EOT
        $ envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"
          
        # envoy 관리페이지 외부 접속 정보 출력
        $ echo -e "http://$(curl -s ipinfo.io/ip):9902"
        # => http://54.123.42.212:9902
          
        --------------------
        # 자신의 PC 웹브라우저에서 관리 페이지 외부 접속 정보 접속 확인!
      

      img.png PC에서 접속한 화면

Istio 소개

  • Istio는 서비스 메시를 구축하고 관리하기 위한 오픈소스 플랫폼입니다.
  • Istio의 구성 20241019_kans_w7_3.svg
    • 파일럿(Pilot) : 모든 Envoy 사이드카에서 프록시 라우팅 규칙을 관리하며, 서비스 디스커버리, 로드밸런싱 설정을 제공합니다.
    • 겔리(Galley) : Istio와 쿠버네티스를 연결하는 역할을 합니다. 서비스 메시 구성 데이터를 검증하고 변환합니다.
    • 시타델(Citadel) : 서비스 간의 인증과 보안을 관리합니다. 서비스 간의 TLS 통신을 제공하고, 서비스 간의 인증을 관리합니다.
  • Istio의 구성요소
    • istiod : Istio의 중앙 제어 플레인으로, Pilot, Citadel, Galley를 포함합니다.
    • istio proxy : Envoy 기반의 프록시로, istiod와 통신하며, 서비스 트래픽을 통제하고 옵저빌리티를 위한 메트릭을 제공합니다.
  • 특징
    • Istio는 각 파드안에서 사이드카로 동작하는 Envoy가 트래픽을 제어하고 모니터링합니다.
    • 모든 마이크로 서비스간 통신은 Envoy를 통해 이루어지며, 이를 통해 메트릭을 수집하거나 컨트롤 할 수 있습니다.
    • 트래픽을 컨트롤 하기 위해서 Envoy 프록시에 전송룰을 정의 합니다.
    • 마이크로 서비스간의 통신을 mutual TLS 인증(mTLS)을 통해 보안합니다.
    • 각 어플리케이션은 파드 내의 엔보이 프록시에 접속하기 위해 localhost에 TCP 접속을 합니다.

Istio 설치 (Sidecar 모드)

  • Istio 공식 문서 : Link
    • Istio Sidecar mode 설치 : v1.23.2 - 버전 설치,
    • without GwApi - Docs
    • Operator 방식 설치 : https://istio.io/latest/docs/setup/install/operator/ (Istio Operator는 Deprecated 되었습니다.)
  • Istio 설치 ```bash
# istioctl 설치
$ export ISTIOV=1.23.2
$ echo "export ISTIOV=1.23.2" >> /etc/profile
$ curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV TARGET_ARCH=x86_64 sh -
$ tree istio-$ISTIOV -L 2 # sample yaml 포함
# => istio-1.23.2
#    ├── LICENSE
#    ├── README.md
#    ├── bin
#    │   └── istioctl
#    ├── manifest.yaml
#    ├── manifests
#    │   ├── charts
#    │   └── profiles
#    ├── samples
#    │   ...
#    └── tools
#        ├── _istioctl
#        ├── certs
#        └── istioctl.bash
$ cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
$ istioctl version --remote=false
# => client version: 1.23.2

# (demo 프로파일) 컨트롤 플레인 배포 - 링크 Customizing
# The istioctl command supports the full IstioOperator API via command-line options for individual settings or for passing a yaml file containing an IstioOperator custom resource (CR).
$ istioctl profile list
# => Istio configuration profiles:
#        ambient
#        default
#        demo
#        empty
#        minimal
#        openshift
#        openshift-ambient
#        preview
#        remote
#        stable
$ istioctl profile dump default
$ istioctl profile dump --config-path components.ingressGateways
# => - enabled: true
#      name: istio-ingressgateway
$ istioctl profile dump --config-path values.gateways.istio-ingressgateway
# => {}
$ istioctl profile dump demo
# => apiVersion: install.istio.io/v1alpha1
#    kind: IstioOperator
#    spec:
#      components:
#        base:
#          enabled: true
#        egressGateways:
#        - enabled: true
#          name: istio-egressgateway
#        ingressGateways:
#        - enabled: true
#          name: istio-ingressgateway
#        pilot:
#          enabled: true
#      hub: docker.io/istio
#      profile: demo
#      tag: 1.23.2
#      values:
#        defaultRevision: &quot;&quot;
#        gateways:
#          istio-egressgateway: {}
#          istio-ingressgateway: {}
#        global:
#          configValidation: true
#          istioNamespace: istio-system
#        profile: demo

$ istioctl profile dump demo > demo-profile.yaml
$ vi demo-profile.yaml # 복잡성을 줄이게 실습 시나리오 환경 맞춤
--------------------
    egressGateways:
    - enabled: false
--------------------    

$ istioctl install -f demo-profile.yaml -y
# => ✔ Istio core installed ⛵️
#    ✔ Istiod installed 🧠
#    ✔ Ingress gateways installed 🛬
#    ✔ Installation complete
#    Made this installation the default for cluster-wide operations.

# 설치 확인 : istiod, istio-ingressgateway
$ kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
# => NAME                                        READY   STATUS    RESTARTS      AGE
#    pod/istio-ingressgateway-5f9f654d46-l7mqp   1/1     Running   0             14m
#    pod/istiod-7f8b586864-8mc4c                 1/1     Running   1 (82s ago)   14m
#    
#    NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP                                    PORT(S)                                                                      AGE
#    service/istio-ingressgateway   LoadBalancer   10.10.200.171   192.168.10.101,192.168.10.102,192.168.10.103   15021:30953/TCP,80:31677/TCP,443:30737/TCP,31400:30617/TCP,15443:31668/TCP   14m
#    service/istiod                 ClusterIP      10.10.200.215   &lt;none&gt;                                         15010/TCP,15012/TCP,443/TCP,15014/TCP                                        14m
#    
#    NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
#    deployment.apps/istio-ingressgateway   1/1     1            1           14m
#    deployment.apps/istiod                 1/1     1            1           14m
#    
#    NAME                                              DESIRED   CURRENT   READY   AGE
#    replicaset.apps/istio-ingressgateway-5f9f654d46   1         1         1       14m
#    replicaset.apps/istiod-7f8b586864                 1         1         1       14m
#    
#    NAME                             ENDPOINTS                                                           AGE
#    endpoints/istio-ingressgateway   172.16.2.14:15443,172.16.2.14:15021,172.16.2.14:31400 + 2 more...   14m
#    endpoints/istiod                 172.16.3.16:15012,172.16.3.16:15010,172.16.3.16:15017 + 1 more...   14m
#    
#    NAME                                                  SECRETS   AGE
#    serviceaccount/istio-ingressgateway-service-account   0         14m
#    serviceaccount/istio-reader-service-account           0         14m
#    serviceaccount/istiod                                 0         14m
#    ...
#    
#    NAME                                            DATA   AGE
#    configmap/istio                                 2      14m
#    configmap/istio-sidecar-injector                2      14m
#    ...
#    
#    NAME                     TYPE               DATA   AGE
#    secret/istio-ca-secret   istio.io/ca-root   5      14m
#    
#    NAME                                              MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
#    poddisruptionbudget.policy/istio-ingressgateway   1               N/A               0                     14m
#    poddisruptionbudget.policy/istiod                 1               N/A               0                     14m
$ kubectl get crd | grep istio.io | sort
# => authorizationpolicies.security.istio.io      2024-10-01T05:26:47Z
#    destinationrules.networking.istio.io         2024-10-01T05:26:47Z
#    envoyfilters.networking.istio.io             2024-10-01T05:26:47Z
#    gateways.networking.istio.io                 2024-10-01T05:26:47Z
#    peerauthentications.security.istio.io        2024-10-01T05:26:47Z
#    proxyconfigs.networking.istio.io             2024-10-01T05:26:47Z
#    requestauthentications.security.istio.io     2024-10-01T05:26:47Z
#    serviceentries.networking.istio.io           2024-10-01T05:26:47Z
#    sidecars.networking.istio.io                 2024-10-01T05:26:47Z
#    telemetries.telemetry.istio.io               2024-10-01T05:26:48Z
#    virtualservices.networking.istio.io          2024-10-01T05:26:48Z
#    wasmplugins.extensions.istio.io              2024-10-01T05:26:48Z
#    workloadentries.networking.istio.io          2024-10-01T05:26:48Z
#    workloadgroups.networking.istio.io           2024-10-01T05:26:48Z

# istio-ingressgateway 의 envoy 버전 확인
$ kubectl exec -it deploy/istio-ingressgateway -n istio-system -c istio-proxy -- envoy --version
# => envoy  version: 6c72b2179f5a58988b920a55b0be8346de3f7b35/1.31.2-dev/Clean/RELEASE/BoringSSL

# istio-ingressgateway 서비스 NodePort로 변경
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"type":"NodePort"}}'
# => service/istio-ingressgateway patched

# istio-ingressgateway 서비스 확인
$ kubectl get svc,ep -n istio-system istio-ingressgateway
# => NAME                           TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                      AGE
#    service/istio-ingressgateway   NodePort   10.10.200.171   &lt;none&gt;        15021:30953/TCP,80:31677/TCP,443:30737/TCP,31400:30617/TCP,15443:31668/TCP   16m
#    
#    NAME                             ENDPOINTS                                                           AGE
#    endpoints/istio-ingressgateway   172.16.2.14:15443,172.16.2.14:15021,172.16.2.14:31400 + 2 more...   16m

## istio-ingressgateway 서비스 포트 정보 확인
$ kubectl get svc -n istio-system istio-ingressgateway -o jsonpath={.spec.ports[*]} | jq
# => {
#      &quot;name&quot;: &quot;https&quot;,
#      &quot;nodePort&quot;: 30737,
#      &quot;port&quot;: 443,
#      &quot;protocol&quot;: &quot;TCP&quot;,
#      &quot;targetPort&quot;: 8443
#    }
#    {
#      &quot;name&quot;: &quot;tcp&quot;,
#      &quot;nodePort&quot;: 30617,
#      &quot;port&quot;: 31400,
#      &quot;protocol&quot;: &quot;TCP&quot;,
#      &quot;targetPort&quot;: 31400
#    }
#    ...

## istio-ingressgateway 디플로이먼트 파드의 포트 정보 확인 
$ kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].ports[*]} | jq
# => ...
#    {
#      &quot;containerPort&quot;: 8443,
#      &quot;protocol&quot;: &quot;TCP&quot;
#    }
#    {
#      &quot;containerPort&quot;: 31400,
#      &quot;protocol&quot;: &quot;TCP&quot;
#    }
#    ...
$ kubectl get deploy/istio-ingressgateway -n istio-system -o jsonpath={.spec.template.spec.containers[0].readinessProbe} | jq
# => {
#      &quot;failureThreshold&quot;: 30,
#      &quot;httpGet&quot;: {
#        &quot;path&quot;: &quot;/healthz/ready&quot;,
#        &quot;port&quot;: 15021,
#        &quot;scheme&quot;: &quot;HTTP&quot;
#      },
#      &quot;initialDelaySeconds&quot;: 1,
#      &quot;periodSeconds&quot;: 2,
#      &quot;successThreshold&quot;: 1,
#      &quot;timeoutSeconds&quot;: 1
#    }

# istiod(컨트롤플레인) 디플로이먼트 정보 확인
$ kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnlp
$ kubectl exec -it deployment.apps/istiod -n istio-system -- ss -tnp
$ kubectl exec -it deployment.apps/istiod -n istio-system -- ps -ef
# => UID          PID    PPID  C STIME TTY          TIME CMD
#    istio-p+       1       0  0 05:39 ?        00:00:01 /usr/local/bin/pilot-discovery discovery --monitoringAddr=:15014 --log_output_level=default:info --domain cluster.local --ke

# istio-ingressgateway 디플로이먼트 정보 확인
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnlp
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -tnp
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ps -ef
# => UID          PID    PPID  C STIME TTY          TIME CMD
#    istio-p+       1       0  0 05:27 ?        00:00:08 /usr/local/bin/pilot-agent proxy router --domain istio-system.svc.cluster.local
#    istio-p+      16       1  0 05:27 ?        00:00:04 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drai
# # <span style="color: green;">👉 pilot-agent와 envoy가 동작 중입니다.</span>

# envoy 설정 확인
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- cat /etc/istio/proxy/envoy-rev.json
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnlp
# => Netid      State       Recv-Q      Send-Q                                          Local Address:Port              Peer Address:Port      Process
#    u_str      LISTEN      0           4096               var/run/secrets/workload-spiffe-uds/socket 32430                        * 0          users:((&quot;pilot-agent&quot;,pid=1,fd=9))
#    u_str      LISTEN      0           4096                                      etc/istio/proxy/XDS 32431                        * 0          users:((&quot;pilot-agent&quot;,pid=1,fd=10))
$ kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ss -xnp
# => Netid State Recv-Q Send-Q                              Local Address:Port  Peer Address:Port Process
#    u_str ESTAB 0      0                                               * 39978            * 37977 users:((&quot;envoy&quot;,pid=16,fd=19))
#    u_str ESTAB 0      0                                               * 37501            * 37981 users:((&quot;envoy&quot;,pid=16,fd=32))
#    u_str ESTAB 0      0                             etc/istio/proxy/XDS 37977            * 39978 users:((&quot;pilot-agent&quot;,pid=1,fd=11))
#    u_str ESTAB 0      0      var/run/secrets/workload-spiffe-uds/socket 37981            * 37501 users:((&quot;pilot-agent&quot;,pid=1,fd=15))
  • Auto Injection with namespace label
  • 해당 네임스페이스에 생성되는 모든 파드들은 istio 사이드카가 자동으로 injection 됩니다.

    # mutating Webhook admisstion controller 사용
    $ kubectl label namespace default istio-injection=enabled
    # => namespace/default labeled
    $ kubectl get ns -L istio-injection
    # => NAME              STATUS   AGE     ISTIO-INJECTION
    #    default           Active   7d      enabled
    #    ...
    

    img.png

  • Istio 접속 테스트를 위한 변수 지정 및 k3s-m에서 접속 테스트
# k3s-m)
# istio ingress gw NodePort(HTTP 접속용) 변수 지정 
$ export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
$ echo $IGWHTTP
# => 31677

# /etc/hosts 파일 수정
# $ MYDOMAIN=<각자 자신의 www 도메인> # 단, 사용하고 있지 않는 공인 도메인을 사용 할 것
# $ echo "<istio-ingressgateway 파드가 있는 워커 노드> $MYDOMAIN" >> /etc/hosts

$ export MYDOMAIN=sweetlittlebird.com
$ echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
$ echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile

# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 됩니다.
$ curl -v -s $MYDOMAIN:$IGWHTTP
# => *   Trying 192.168.10.10:31677...
#    * connect to 192.168.10.10 port 31677 failed: Connection refused
#    * Failed to connect to sweetlittlebird.com port 31677 after 3 ms: Connection refused
#    * Closing connection 0
  • testpc에서 접속 테스트
# 아래 변수는 각자 자신의 값을 직접 입력 할 것
# $ IGWHTTP=<각자 출력된 NodePort>
$ IGWHTTP=31677
$ export MYDOMAIN=sweetlittlebird.com
$ echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
$ echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile

# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 된다
$ curl -v -s $MYDOMAIN:$IGWHTTP
# => * Host sweetlittlebird.com:31677 was resolved.
#    * ...
#    * Failed to connect to sweetlittlebird.com port 31677 after 2 ms: Couldn't connect to server
#    * ...
  • 자신의 pc에서 접속 테스트
# 아래 변수는 각자 자신의 값을 직접 입력 할 것
# $ IGWHTTP=<각자 출력된 NodePort>
$ IGWHTTP=31677
# $ ISTIONODEIP=<k3s-m 의 유동 공인 IP>
$ ISTIONODEIP=54.123.42.212

$ export MYDOMAIN=sweetlittlebird.com
$ echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
$ echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile

# istio ingress gw 접속 테스트 : 아직은 설정이 없어서 접속 실패가 된다
$ curl -v -s $MYDOMAIN:$IGWHTTP
# => * Host sweetlittlebird.com:31677 was resolved.
#    * ...
#    * Failed to connect to sweetlittlebird.com port 31677 after 2 ms: Couldn't connect to server
#    * ...

Istio를 통한 외부 노출

  • Nginx 디플로이먼트와 서비스 배포
# 로그 모니터링
$ kubectl get pod -n istio-system -l app=istiod
# => NAME                      READY   STATUS    RESTARTS      AGE
#    istiod-7f8b586864-8mc4c   1/1     Running   1 (60m ago)   73m
$ kubetail -n istio-system -l app=istiod -f

$ kubectl get pod -n istio-system -l app=istio-ingressgateway
# => NAME                                    READY   STATUS    RESTARTS   AGE
#    istio-ingressgateway-5f9f654d46-l7mqp   1/1     Running   0          74m
$ kubetail -n istio-system -l app=istio-ingressgateway -f
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kans-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-websrv
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      serviceAccountName: kans-nginx
      terminationGracePeriodSeconds: 0
      containers:
      - name: deploy-websrv
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: svc-webport
      port: 80
      targetPort: 80
  selector:
    app: deploy-websrv
  type: ClusterIP
EOF
# => serviceaccount/kans-nginx created
#    deployment.apps/deploy-websrv created
#    service/svc-clusterip created

# 사이드카 컨테이너 배포 확인
$ kubectl get pod,svc,ep,sa -o wide
# => NAME                                 READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
#    pod/deploy-websrv-778ffd6947-cxf5k   2/2     Running   0          50s   172.16.1.13   k3s-w2   &lt;none&gt;           &lt;none&gt;
#    
#    NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE     SELECTOR
#    service/kubernetes      ClusterIP   10.10.200.1     &lt;none&gt;        443/TCP   6d16h   &lt;none&gt;
#    service/svc-clusterip   ClusterIP   10.10.200.243   &lt;none&gt;        80/TCP    50s     app=deploy-websrv
#    
#    NAME                      ENDPOINTS            AGE
#    endpoints/kubernetes      192.168.10.10:6443   6d16h
#    endpoints/svc-clusterip   172.16.1.13:80       50s
#    
#    NAME                        SECRETS   AGE
#    serviceaccount/default      0         7d1h
#    serviceaccount/kans-nginx   0         50s

$ kubectl describe pod
# => Name:             deploy-websrv-778ffd6947-cxf5k
#    Namespace:        default
#    Priority:         0
#    Service Account:  kans-nginx
#    Node:             k3s-w2/192.168.10.102
#    Labels:           app=deploy-websrv
#                      security.istio.io/tlsMode=istio
#                      ...
#    Annotations:      istio.io/rev: default
#                      sidecar.istio.io/status:
#                        {&quot;initContainers&quot;:[&quot;istio-init&quot;],&quot;containers&quot;:[&quot;istio-proxy&quot;],&quot;volumes&quot;:[&quot;workload-socket&quot;,&quot;credential-socket&quot;,&quot;workload-certs&quot;,&quot;istio-env...
#    Status:           Running
#    ...
#    Controlled By:  ReplicaSet/deploy-websrv-778ffd6947
#    <span style="color: red;">Init Containers:</span>  # <span style="color: green;">👉 init container가 파드내 iptables 셋팅</span>
#      istio-init:
#        Container ID:  containerd://2a114fe0624581b35bda9ca257c6d3c831138e8a44900a6130e988bb51eb05da
#        Image:         docker.io/istio/proxyv2:1.23.2
#        Image ID:      docker.io/istio/proxyv2@sha256:2876cfc2fdf47e4b9665390ccc9ccf2bf913b71379325b8438135c9f35578e1a
#        Port:          &lt;none&gt;
#        Host Port:     &lt;none&gt;
#        Args:
#          <span style="color: red;">istio-iptables</span>
#          -p
#          15001
#          -z
#          15006
#          -u
#          1337
#          -m
#          REDIRECT
#          -i
#          *
#          -x
#    
#          -b
#          *
#          -d
#          15090,15021,15020
#          --log_output_level=default:info
#        State:          Terminated
#          Reason:       Completed
#        ...
#    Containers:
#      deploy-websrv:
#        Container ID:   containerd://8918e0bb760bce8d090e84818bc189ae3ababdf9e74eb7dd3fb9709b356891f9
#        Image:          nginx:alpine
#        ...
#      <span style="color: red;">istio-proxy:</span>  # <span style="color: green;">👉 istio-proxy라는 컨테이너가 sidecar로 동작 중</span>
#        Container ID:  containerd://71d9e07a530dfce2ec34810d60a28dc3f9445b8eab714c2a7e204c459c59bcd3
#        Image:         docker.io/istio/proxyv2:1.23.2
#        Image ID:      docker.io/istio/proxyv2@sha256:2876cfc2fdf47e4b9665390ccc9ccf2bf913b71379325b8438135c9f35578e1a
#        Port:          15090/TCP
#        Host Port:     0/TCP
#        Args:
#          proxy
#          sidecar
#          --domain
#          $(POD_NAMESPACE).svc.cluster.local
#          --proxyLogLevel=warning
#          --proxyComponentLogLevel=misc:error
#          --log_output_level=default:info
#        State:          Running
#        ...
  • Istio Gateway/VirtualService 설정 - Host 기반 트래픽 라우팅 설정 링크 img.png
    • 클라이언트 PC → (Service:NodePort) Istio ingressgateway 파드 → (Gateway, VirtualService, Service 는 Bypass) → Endpoint(파드 : 사이드카 - Application 컨테이너)
    • Gateway : 지정한 인그레스 게이트웨이로부터 트래픽이 인입, 프로토콜 및 포트, HOSTS, Proxy 등 설정 가능 링크
    • VirtualService : 인입 처리할 hosts 설정, L7 PATH 별 라우팅, 목적지에 대한 정책 설정 가능 (envoy route config)
    • (참고) Introducing Istio v1 APIs - Blog
$ cat <<EOF | kubectl create -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: test-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: nginx-service
spec:
  hosts:
  - "$MYDOMAIN"
  gateways:
  - test-gateway
  http:
  - route:
    - destination:
        host: svc-clusterip
        port:
          number: 80
EOF
# => gateway.networking.istio.io/test-gateway created
#    virtualservice.networking.istio.io/nginx-service created

# Istio Gateway(=gw)/VirtualService(=vs) 설정 정보를 확인
$ kubectl explain gateways.networking.istio.io
$ kubectl explain virtualservices.networking.istio.io
$ kubectl api-resources  | grep istio
# => wasmplugins                                      extensions.istio.io/v1alpha1      true         WasmPlugin
#    destinationrules                    dr           networking.istio.io/v1            true         DestinationRule
#    envoyfilters                                     networking.istio.io/v1alpha3      true         EnvoyFilter
#    gateways                            gw           networking.istio.io/v1            true         Gateway
#    proxyconfigs                                     networking.istio.io/v1beta1       true         ProxyConfig
#    serviceentries                      se           networking.istio.io/v1            true         ServiceEntry
#    sidecars                                         networking.istio.io/v1            true         Sidecar
#    virtualservices                     vs           networking.istio.io/v1            true         VirtualService
#    workloadentries                     we           networking.istio.io/v1            true         WorkloadEntry
#    workloadgroups                      wg           networking.istio.io/v1            true         WorkloadGroup
#    authorizationpolicies               ap           security.istio.io/v1              true         AuthorizationPolicy
#    peerauthentications                 pa           security.istio.io/v1              true         PeerAuthentication
#    requestauthentications              ra           security.istio.io/v1              true         RequestAuthentication
#    telemetries                         telemetry    telemetry.istio.io/v1             true         Telemetry

# virtual service 는 다른 네임스페이스의 서비스(ex. svc-nn.<ns>)도 참조할 수 있다
$ kubectl get gw,vs
# => NAME                                       AGE
#    gateway.networking.istio.io/test-gateway   105s
#    
#    NAME                                               GATEWAYS           HOSTS                     AGE
#    virtualservice.networking.istio.io/nginx-service   [&quot;test-gateway&quot;]   [&quot;sweetlittlebird.com&quot;]   105s

# Retrieves last sent and last acknowledged xDS sync from Istiod to each Envoy in the mesh
# istioctl proxy-status command was improved to include the time since last change, and more relevant status values.
$ istioctl proxy-status # 단축어 ps
$ istioctl ps
# => NAME                                                   CLUSTER        CDS                LDS                EDS              RDS                ECDS        ISTIOD                      VERSION
#    deploy-websrv-778ffd6947-cxf5k.default                 Kubernetes     SYNCED (22m)       SYNCED (22m)       SYNCED (22m)     SYNCED (22m)       IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    istio-ingressgateway-5f9f654d46-l7mqp.istio-system     Kubernetes     SYNCED (2m14s)     SYNCED (2m14s)     SYNCED (22m)     SYNCED (2m14s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
  • Istio를 통한 Nginx 파드 접속 테스트
    • 외부 (자신의 PC, test pc)에서 접속 테스트
# istio ingress gw 를 통한 접속 테스트
$ curl -s $MYDOMAIN:$IGWHTTP | grep -o "<title>.*</title>"
# => <title>Welcome to nginx!</title>
$ curl -v -s $MYDOMAIN:$IGWHTTP
# => * Host sweetlittlebird.com:31677 was resolved.
#    * IPv6: (none)
#    * IPv4: 192.168.10.10, 192.168.10.10
#    *   Trying 192.168.10.10:31677...
#    * Connected to sweetlittlebird.com (192.168.10.10) port 31677
#    &gt; GET / HTTP/1.1
#    &gt; Host: sweetlittlebird.com:31677
#    &gt; User-Agent: curl/8.5.0
#    &gt; Accept: */*
#    &gt;
#    &lt; HTTP/1.1 200 OK
#    &lt; server: istio-envoy
#    &lt; date: Sat, 19 Oct 2024 07:14:25 GMT
#    &lt; content-type: text/html
#    &lt; content-length: 615
#    &lt; last-modified: Wed, 02 Oct 2024 16:07:39 GMT
#    &lt; etag: &quot;66fd6fcb-267&quot;
#    &lt; accept-ranges: bytes
#    &lt; x-envoy-upstream-service-time: 1
#    ...
# $ curl -v -s <유동공인이IP>:$IGWHTTP
$ curl -v -s 54.123.42.212:$IGWHTTP
  • 출력 로그 정보 확인
$ kubetail -n istio-system -l app=istio-ingressgateway -f
# => [istio-ingressgateway-5f9f654d46-l7mqp] [2024-10-01T07:49:20.833Z] &quot;GET / HTTP/1.1&quot; 200 - via_upstream - &quot;-&quot; 0 615 6 5 &quot;172.16.0.0&quot; &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15&quot; &quot;6f246b4e-d675-971e-9a9d-dd809f560c6f&quot; &quot;sweetlittlebird.com:31677&quot; &quot;172.16.1.13:80&quot; 
#    <span style="color: red;">outbound|80||svc-clusterip.default.svc.cluster.local</span> 172.16.2.14:60786 172.16.2.14:8080 172.16.0.0:8773 - -
$ kubetail -l app=deploy-websrv
# => [deploy-websrv-778ffd6947-cxf5k istio-proxy] [2024-10-01T07:49:20.866Z] &quot;GET / HTTP/1.1&quot; 200 - via_upstream - &quot;-&quot; 0 615 2 1 &quot;172.16.0.0&quot; &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15&quot; &quot;6f246b4e-d675-971e-9a9d-dd809f560c6f&quot; &quot;sweetlittlebird.com:31677&quot; &quot;172.16.1.13:80&quot; 
#    <span style="color: red;">inbound|80||</span> 127.0.0.6:40337 172.16.1.13:80 172.16.0.0:0 invalid:outbound_.80_._.svc-clusterip.default.svc.cluster.local default

img.png

  • istioctl 정보 확인
#
$ istioctl proxy-status
# => NAME                                                   CLUSTER        CDS              LDS              EDS              RDS              ECDS        ISTIOD                      VERSION
#    deploy-websrv-778ffd6947-cxf5k.default                 Kubernetes     SYNCED (19m)     SYNCED (19m)     SYNCED (19m)     SYNCED (19m)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    istio-ingressgateway-5f9f654d46-l7mqp.istio-system     Kubernetes     SYNCED (24m)     SYNCED (24m)     SYNCED (24m)     SYNCED (24m)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2

# Envoy config dump : all, cluster, endpoint, listener 등
$ istioctl proxy-config --help 
$ istioctl proxy-config all deploy-websrv-778ffd6947-cxf5k
$ istioctl proxy-config all deploy-websrv-778ffd6947-cxf5k -o json | jq
$ istioctl proxy-config route deploy-websrv-778ffd6947-cxf5k -o json | jq
  • pilot : istio-proxy내 uds로 envoy와 grpc통신, istiod에서 받아온 dynamic config를 envoy에 전달
# istio-proxy 사용자 정보 확인 : uid(1337):gid(1337) 확인 -> iptables rule 에서 사용됨
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- tail -n 3 /etc/passwd
# => ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
#    tcpdump:x:100:102::/nonexistent:/usr/sbin/nologin
#    istio-proxy:x:1337:1337::/home/istio-proxy:/bin/sh

# envoy 설정 정보 확인 : dynamic_resources , static_resources - listeners  
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- cat /etc/istio/proxy/envoy-rev.json
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ss -nlp
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ss -np
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- netstat -np
# => Active Internet connections (w/o servers)
#    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
#    tcp        0      0 127.0.0.1:33168         127.0.0.1:15020         ESTABLISHED 13/envoy
#    tcp        0      0 127.0.0.1:33156         127.0.0.1:15020         ESTABLISHED 13/envoy
#    tcp        0      0 172.16.1.13:15006       172.16.2.14:33006       ESTABLISHED 13/envoy
#    tcp        0      0 172.16.1.13:59774       10.10.200.215:15012     ESTABLISHED 1/pilot-agent
#    tcp        0      0 172.16.1.13:15006       172.16.2.14:60786       ESTABLISHED 13/envoy
# <span style="color: green;">👉 172.16.1.13 : deploy-websrv 파드 ip</span>
# <span style="color: green;">    172.16.2.14 : istio-ingressgateway 파드 ip</span>
# <span style="color: green;">    10.10.200.215 : istiod 서비스의 Cluster-IP</span>
#    ...
#    Active UNIX domain sockets (w/o servers)
#    Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
#    unix  3      [ ]         STREAM     CONNECTED     54162    13/envoy
#    unix  3      [ ]         STREAM     CONNECTED     54709    1/pilot-agent        var/run/secrets/workload-spiffe-uds/socket
#    unix  3      [ ]         STREAM     CONNECTED     71034    1/pilot-agent        etc/istio/proxy/XDS
#    unix  3      [ ]         STREAM     CONNECTED     72979    13/envoy
#    ...

#
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- ps -ef
# => UID          PID    PPID  C STIME TTY          TIME CMD
#    istio-p+       1       0  0 06:43 ?        00:00:01 /usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local --proxyLogLevel=warning --proxyComponentLogLevel=mi
#    istio-p+      13       1  0 06:43 ?        00:00:34 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drain-strategy immediate --local-address-ip-version

# 
$ kubectl get pod,svc -A -owide
# => NAMESPACE      NAME                                            READY   STATUS    RESTARTS        AGE     IP            NODE     NOMINATED NODE   READINESS GATES
#    default        pod/deploy-websrv-778ffd6947-cxf5k              2/2     Running   0               91m     172.16.1.13   k3s-w2   &lt;none&gt;           &lt;none&gt;
#    istio-system   pod/istio-ingressgateway-5f9f654d46-l7mqp       1/1     Running   0               167m    172.16.2.14   k3s-w3   &lt;none&gt;           &lt;none&gt;
#    istio-system   pod/istiod-7f8b586864-8mc4c                     1/1     Running   1 (154m ago)    167m    172.16.3.16   k3s-w1   &lt;none&gt;           &lt;none&gt;
#    ...
#    
#    NAMESPACE      NAME                                         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                      AGE     SELECTOR
#    default        service/kubernetes                           ClusterIP   10.10.200.1     &lt;none&gt;        443/TCP                                                                      6d17h   &lt;none&gt;
#    default        service/svc-clusterip                        ClusterIP   10.10.200.243   &lt;none&gt;        80/TCP                                                                       91m     app=deploy-websrv
#    istio-system   service/istio-ingressgateway                 NodePort    10.10.200.171   &lt;none&gt;        15021:30953/TCP,80:31677/TCP,443:30737/TCP,31400:30617/TCP,15443:31668/TCP   167m    app=istio-ingressgateway,istio=ingressgateway
#    istio-system   service/istiod                               ClusterIP   10.10.200.215   &lt;none&gt;        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        167m    app=istiod,istio=pilot
#    ...
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- netstat -antp
# => Active Internet connections (servers and established)
#    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
#    tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -
#    ...
#    tcp        0      0 127.0.0.1:33168         127.0.0.1:15020         ESTABLISHED 13/envoy
#    tcp        0      0 127.0.0.1:33156         127.0.0.1:15020         ESTABLISHED 13/envoy
#    tcp        0      0 172.16.1.13:15006       172.16.2.14:33006       ESTABLISHED 13/envoy
#    tcp        0      0 172.16.1.13:59774       10.10.200.215:15012     ESTABLISHED 1/pilot-agent
#    tcp        0      0 172.16.1.13:15006       172.16.2.14:60786       ESTABLISHED 13/envoy
#    tcp6       0      0 127.0.0.1:15020         127.0.0.1:33168         ESTABLISHED 1/pilot-agent
#    tcp6       0      0 127.0.0.1:15020         127.0.0.1:33156         ESTABLISHED 1/pilot-agent

# istiod 정보 같이 확인 : 출력되는 IP가 누구인지 확인 해보자
$ kubectl get pod,svc -A -owide
$ kubectl exec -it deploy/istiod -n istio-system -- ps -ef
$ kubectl exec -it deploy/istiod -n istio-system -- netstat -antp
$ kubectl exec -it deploy/istiod -n istio-system -- ss -nlp
$ kubectl exec -it deploy/istiod -n istio-system -- ss -np
# => Netid  State  Recv-Q  Send-Q         Local Address:Port           Peer Address:Port   Process
#    tcp    ESTAB  0       0                172.16.3.16:33552           10.10.200.1:443     users:((&quot;pilot-discovery&quot;,pid=1,fd=7))
#    tcp    ESTAB  0       0       [::ffff:172.16.3.16]:15012  [::ffff:172.16.2.14]:37102   users:((&quot;pilot-discovery&quot;,pid=1,fd=13))
#    tcp    ESTAB  0       0       [::ffff:172.16.3.16]:15012  [::ffff:172.16.1.13]:56560   users:((&quot;pilot-discovery&quot;,pid=1,fd=14))
  • istio-proxy, istiod가 각각 사용하는 포트 정보 링크
    • istio-proxy

      Port Protocol Description Pod-internal only
      15000 TCP Envoy admin port (commands/diagnostics) Yes
      15001 TCP Envoy outbound No
      15004 HTTP Debug port Yes
      15006 TCP Envoy inbound No
      15008 HTTP2 HBONE mTLS tunnel port No
      15020 HTTP Merged Prometheus telemetry from Istio agent, Envoy, and application No No
      15021 HTTP Health checks No
      15053 DNS DNS port, if capture is enabled Yes
      15090 HTTP Envoy Prometheus telemetry No
    • istiod (컨트롤플레인)

      Port Protocol Description Pod-internal only
      443 HTTPS Webhooks service port No
      8080 HTTP Debug interface (deprecated, container port only) No
      15010 GRPC XDS and CA services (Plaintext, only for secure networks) No
      15012 GRPC XDS and CA services (TLS and mTLS, recommended for production use) No
      15014 HTTP Control plane monitoring No
      15017 HTTPS Webhook container port, forwarded from 443 No
  • Istio - Istio proxy와 Envoy 프로세스간 유닉스 도메인 소켓 통신 확인
# 파드 확인
$ kubectl get pod
# => NAME                             READY   STATUS    RESTARTS   AGE
#    deploy-websrv-778ffd6947-cxf5k   2/2     Running   0          106m

# istio 컨테이너 접속
$ kubectl exec -it deploy/deploy-websrv -c istio-proxy -- bash
---------------------------------------------------------------
# SDS, XDS 는 소켓 타입
$ ls -al /etc/istio/proxy
# => total 24
#    drwxrwxrwt 2 root        root          100 Oct 19 06:43 .
#    drwxr-xr-x 4 root        root         4096 Oct 19 06:43 ..
#    srw-rw-rw- 1 istio-proxy istio-proxy     0 Oct 19 06:43 XDS
#    -rw-r--r-- 1 istio-proxy istio-proxy 13644 Oct 19 06:43 envoy-rev.json
#    -rw-r--r-- 1 istio-proxy istio-proxy  2747 Oct 19 06:43 grpc-bootstrap.json

# .json 파일 확인
$ more /etc/istio/proxy/envoy-rev.json
# => {
#      &quot;node&quot;: {
#        &quot;id&quot;: &quot;sidecar~172.16.1.13~deploy-websrv-778ffd6947-cxf5k.default~default.svc.cluster.local&quot;,
#        &quot;cluster&quot;: &quot;deploy-websrv.default&quot;,
#    ...
#      &quot;admin&quot;: {
#        &quot;access_log&quot;: [
#          {
#            &quot;name&quot;: &quot;envoy.access_loggers.file&quot;,
#            &quot;typed_config&quot;: {
#              &quot;@type&quot;: &quot;type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog&quot;,
#              &quot;path&quot;: &quot;/dev/null&quot;
#            }
#          }
#        ],
#        &quot;profile_path&quot;: &quot;/var/lib/istio/data/envoy.prof&quot;,
#        &quot;address&quot;: {
#          &quot;socket_address&quot;: {
#            &quot;address&quot;: &quot;127.0.0.1&quot;,
#            &quot;port_value&quot;: 15000
#          }
#        }
#      },
#    ...
#    &quot;dynamic_resources&quot;: {
#        &quot;lds_config&quot;: {
#          &quot;ads&quot;: {},
#          &quot;initial_fetch_timeout&quot;: &quot;0s&quot;,
#          &quot;resource_api_version&quot;: &quot;V3&quot;
#        },
#        &quot;cds_config&quot;: {
#          &quot;ads&quot;: {},
#          &quot;initial_fetch_timeout&quot;: &quot;0s&quot;,
#          &quot;resource_api_version&quot;: &quot;V3&quot;
#        },
#        &quot;ads_config&quot;: {
#          &quot;api_type&quot;: &quot;GRPC&quot;,
#          &quot;set_node_on_first_message_only&quot;: true,
#          &quot;transport_api_version&quot;: &quot;V3&quot;,
#          &quot;grpc_services&quot;: [
#            {
#              &quot;envoy_grpc&quot;: {
#                &quot;cluster_name&quot;: &quot;xds-grpc&quot;
#    ...
#    &quot;static_resources&quot;: {
#        &quot;clusters&quot;: [
#          {
#            ...
#            &quot;name&quot;: &quot;xds-grpc&quot;,
#            &quot;alt_stat_name&quot;: &quot;xds-grpc;&quot;,
#            &quot;type&quot; : &quot;STATIC&quot;,
#            &quot;connect_timeout&quot;: &quot;1s&quot;,
#            &quot;lb_policy&quot;: &quot;ROUND_ROBIN&quot;,
#            &quot;load_assignment&quot;: {
#              &quot;cluster_name&quot;: &quot;xds-grpc&quot;,
#              &quot;endpoints&quot;: [{
#                &quot;lb_endpoints&quot;: [{
#                  &quot;endpoint&quot;: {
#                    &quot;address&quot;:{
#                      &quot;pipe&quot;: {
#                        &quot;path&quot;: &quot;./etc/istio/proxy/XDS&quot;
#                      }
#    ...
#    &quot;listeners&quot;:[
#      ...
#      &quot;address&quot;: {
#        &quot;socket_address&quot;: {
#          &quot;protocol&quot;: &quot;TCP&quot;,
#          &quot;address&quot;: &quot;0.0.0.0&quot;,
#          &quot;port_value&quot;: 15090
#        }
#      },
#     &quot;filter_chains&quot;: [
#                &quot;filters&quot;: [
#                  {
#                    &quot;name&quot;: &quot;envoy.filters.network.http_connection_manager&quot;,
#                    &quot;typed_config&quot;: {
#                      &quot;@type&quot;: &quot;type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager&quot;,
#                      &quot;codec_type&quot;: &quot;AUTO&quot;,
#                      &quot;stat_prefix&quot;: &quot;agent&quot;,
#                      &quot;route_config&quot;: {
#                        &quot;virtual_hosts&quot;: [
#                          {
#                            &quot;name&quot;: &quot;backend&quot;,
#                            &quot;domains&quot;: [
#                              &quot;*&quot;
#                            ],
#                            &quot;routes&quot;: [
#                              {
#                                &quot;match&quot;: {
#                                  &quot;prefix&quot;: &quot;/healthz/ready&quot;
#                                },
#                                &quot;route&quot;: {
#                                  &quot;cluster&quot;: &quot;agent&quot;
#    ...

$ more /etc/istio/proxy/grpc-bootstrap.json
# => {
#      &quot;xds_servers&quot;: [
#        {
#          &quot;server_uri&quot;: <span style="color: red;">&quot;unix:///etc/istio/proxy/XDS&quot;,</span>
#          ...
#        }
#      ],
#      &quot;node&quot;: {
#        &quot;id&quot;: &quot;sidecar~172.16.1.13~deploy-websrv-778ffd6947-cxf5k.default~default.svc.cluster.local&quot;,
#        &quot;metadata&quot;: {
#          &quot;ANNOTATIONS&quot;: {
#            &quot;istio.io/rev&quot;: &quot;default&quot;,
#            &quot;kubectl.kubernetes.io/default-container&quot;: &quot;deploy-websrv&quot;,
#            &quot;kubectl.kubernetes.io/default-logs-container&quot;: &quot;deploy-websrv&quot;,
#            ...
#            &quot;sidecar.istio.io/status&quot;: &quot;{\&quot;initContainers\&quot;:[\&quot;istio-init\&quot;],\&quot;containers\&quot;:[\&quot;istio-proxy\&quot;],\&quot;volumes\&quot;:[\&quot;workload-socket\&quot;,\&quot;credential-socket\&quot;,\&quot;workload-certs\&quot;
#    ,\&quot;istio-envoy\&quot;,\&quot;istio-data\&quot;,\&quot;istio-podinfo\&quot;,\&quot;istio-token\&quot;,\&quot;istiod-ca-cert\&quot;],\&quot;imagePullSecrets\&quot;:null,\&quot;revision\&quot;:\&quot;default\&quot;}&quot;
#          },
#          ...
#          &quot;SERVICE_ACCOUNT&quot;: &quot;kans-nginx&quot;,
#          &quot;WORKLOAD_NAME&quot;: &quot;deploy-websrv&quot;
#        },
#        &quot;locality&quot;: {},
#        &quot;UserAgentVersionType&quot;: null
#      },
#      &quot;server_listener_resource_name_template&quot;: &quot;xds.istio.io/grpc/lds/inbound/%s&quot;
#    }

# display only Unix domain sockets : Listener 과 ESTAB 상태 정보 확인
$ ss -xpl
# => Netid State  Recv-Q Send-Q                              Local Address:Port  Peer Address:PortProcess
#    u_str LISTEN 0      4096                          etc/istio/proxy/XDS 54694            * 0    users:((&quot;pilot-agent&quot;,pid=1,fd=11))
#    u_str LISTEN 0      4096   var/run/secrets/workload-spiffe-uds/socket 54693            * 0    users:((&quot;pilot-agent&quot;,pid=1,fd=9))
$ ss -xp
# => Netid State Recv-Q Send-Q                              Local Address:Port  Peer Address:Port Process
#    u_str ESTAB 0      0                             etc/istio/proxy/XDS 79304            * 82228 users:((&quot;pilot-agent&quot;,pid=1,fd=10))
#    u_str ESTAB 0      0      var/run/secrets/workload-spiffe-uds/socket 54709            * 54162 users:((&quot;pilot-agent&quot;,pid=1,fd=16))
#    u_str ESTAB 0      0                                               * 82228            * 79304 users:((&quot;envoy&quot;,pid=13,fd=19))
#    u_str ESTAB 0      0                                               * 54162            * 54709 users:((&quot;envoy&quot;,pid=13,fd=32))

# display only TCP sockets and display only IP version 4 sockets : TCP 상태 정보 확인
$ ss -4tpl
# => LISTEN 0      4096         0.0.0.0:15090      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=21))
#    LISTEN 0      4096         0.0.0.0:15090      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=20))
#    LISTEN 0      4096         0.0.0.0:15021      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=23))
#    LISTEN 0      4096         0.0.0.0:15021      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=22))
#    LISTEN 0      4096         0.0.0.0:15001      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=35))
#    LISTEN 0      4096         0.0.0.0:15001      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=34))
#    LISTEN 0      4096         0.0.0.0:15006      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=37))
#    LISTEN 0      4096         0.0.0.0:15006      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=36))
#    LISTEN 0      4096       127.0.0.1:15000      0.0.0.0:*    users:((&quot;envoy&quot;,pid=13,fd=18))
#    LISTEN 0      4096       127.0.0.1:15004      0.0.0.0:*    users:((&quot;pilot-agent&quot;,pid=1,fd=13))
#    LISTEN 0      511          0.0.0.0:http       0.0.0.0:*

Bookinfo 실습 및 Istio 기능 확인

Bookinfo

  • Bookinfo는 istio의 기능을 설명하기위한 MSA(Microservices Architecture) 기반의 예제 어플리케이션입니다. img.png Bookinfo 어플리케이션 구성
    • productpage, details, reviews, ratings 서비스로 구성됩니다. 소개 링크
    • ProductPage 페이지에서 요청을 받으면, 도서 리뷰를 보여주는 Reviews 서비스와 도서 상세 정보를 보여주는 Details 서비스에 접속하고,
    • ProductPage 는 ReviewsDetails 결과를 사용자에게 응답합니다.
    • Reviews 서비스는 v1, v2, v3 세 개의 버전이 있고 v2, v3 버전의 경우 Ratings 서비스에 접소갛여 도서에 대한 5단계 평가를 가져옵니다.
    • Reviews 서비스의 차이는, v1은 Rating 이 없고, v2는 검은색 별로 Ratings 가 표시되며, v3는 색깔이 있는 별로 Ratings 가 표시됩니다.
  • 설치 및 확인
# 모니터링
$ watch -d 'kubectl get pod -owide;echo;kubectl get svc'

# Bookinfo 애플리케이션 배포
$ echo $ISTIOV
# => 1.23.2
$ cat ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
$ kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/platform/kube/bookinfo.yaml
# => service/details created
#    serviceaccount/bookinfo-details created
#    deployment.apps/details-v1 created
#    service/ratings created
#    serviceaccount/bookinfo-ratings created
#    deployment.apps/ratings-v1 created
#    service/reviews created
#    serviceaccount/bookinfo-reviews created
#    deployment.apps/reviews-v1 created
#    deployment.apps/reviews-v2 created
#    deployment.apps/reviews-v3 created
#    service/productpage created
#    serviceaccount/bookinfo-productpage created
#    deployment.apps/productpage-v1 created

# 확인
$ kubectl get all,sa
# => NAME                                 READY   STATUS    RESTARTS   AGE
#    pod/details-v1-65cfcf56f9-8465x      2/2     Running   0          87s
#    pod/productpage-v1-d5789fdfb-f8gdf   2/2     Running   0          86s
#    pod/ratings-v1-7c9bd4b87f-s9gxs      2/2     Running   0          87s
#    pod/reviews-v1-6584ddcf65-gnc9j      2/2     Running   0          87s
#    pod/reviews-v2-6f85cb9b7c-cfc68      2/2     Running   0          87s
#    pod/reviews-v3-6f5b775685-cw7tc      2/2     Running   0          87s
#    
#    NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
#    service/details         ClusterIP   10.10.200.54    &lt;none&gt;        9080/TCP   87s
#    service/productpage     ClusterIP   10.10.200.184   &lt;none&gt;        9080/TCP   87s
#    service/ratings         ClusterIP   10.10.200.163   &lt;none&gt;        9080/TCP   87s
#    service/reviews         ClusterIP   10.10.200.214   &lt;none&gt;        9080/TCP   87s
#    
#    NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
#    deployment.apps/details-v1       1/1     1            1           87s
#    deployment.apps/productpage-v1   1/1     1            1           86s
#    deployment.apps/ratings-v1       1/1     1            1           87s
#    deployment.apps/reviews-v1       1/1     1            1           87s
#    deployment.apps/reviews-v2       1/1     1            1           87s
#    deployment.apps/reviews-v3       1/1     1            1           87s
#    
#    NAME                                       DESIRED   CURRENT   READY   AGE
#    replicaset.apps/details-v1-65cfcf56f9      1         1         1       87s
#    replicaset.apps/productpage-v1-d5789fdfb   1         1         1       86s
#    replicaset.apps/ratings-v1-7c9bd4b87f      1         1         1       87s
#    replicaset.apps/reviews-v1-6584ddcf65      1         1         1       87s
#    replicaset.apps/reviews-v2-6f85cb9b7c      1         1         1       87s
#    replicaset.apps/reviews-v3-6f5b775685      1         1         1       87s
#    
#    NAME                                  SECRETS   AGE
#    serviceaccount/bookinfo-details       0         87s
#    serviceaccount/bookinfo-productpage   0         86s
#    serviceaccount/bookinfo-ratings       0         87s
#    serviceaccount/bookinfo-reviews       0         87s

# product 웹 접속 확인
$ kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
# => &lt;title&gt;Simple Bookstore App&lt;/title&gt;

# 로그
$ kubetail -l app=productpage -f

Istio를 통한 인입 기본 설정

Istio Gateway/VirtualService 설정
# Istio Gateway/VirtualService 설정
$ cat ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
# => apiVersion: networking.istio.io/v1alpha3
#    kind: Gateway
#    metadata:
#      name: bookinfo-gateway
#    spec:
#      # The selector matches the ingress gateway pod labels.
#      # If you installed Istio using Helm following the standard documentation, this would be &quot;istio=ingress&quot;
#      selector:
#        istio: ingressgateway # use istio default controller
#      servers:
#      - port:
#          number: 8080
#          name: http
#          protocol: HTTP
#        hosts:
#        - &quot;*&quot;
#    ---
#    apiVersion: networking.istio.io/v1alpha3
#    kind: VirtualService
#    metadata:
#      name: bookinfo
#    spec:
#      hosts:
#      - &quot;*&quot;
#      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
$ kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/networking/bookinfo-gateway.yaml
# => gateway.networking.istio.io/bookinfo-gateway created
#    virtualservice.networking.istio.io/bookinfo created

# 확인
$ kubectl get gw,vs
$ istioctl proxy-status
# => NAME                                                   CLUSTER        CDS                LDS                EDS                RDS                ECDS        ISTIOD                      VERSION
#    deploy-websrv-778ffd6947-cxf5k.default                 Kubernetes     SYNCED (5m9s)      SYNCED (5m9s)      SYNCED (4m24s)     SYNCED (5m9s)      IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    details-v1-65cfcf56f9-8465x.default                    Kubernetes     SYNCED (4m43s)     SYNCED (4m43s)     SYNCED (4m24s)     SYNCED (4m43s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    istio-ingressgateway-5f9f654d46-l7mqp.istio-system     Kubernetes     SYNCED (10s)       SYNCED (10s)       SYNCED (4m24s)     SYNCED (10s)       IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    productpage-v1-d5789fdfb-f8gdf.default                 Kubernetes     SYNCED (4m53s)     SYNCED (4m53s)     SYNCED (4m24s)     SYNCED (4m53s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    ratings-v1-7c9bd4b87f-s9gxs.default                    Kubernetes     SYNCED (4m24s)     SYNCED (4m24s)     SYNCED (4m24s)     SYNCED (4m24s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    reviews-v1-6584ddcf65-gnc9j.default                    Kubernetes     SYNCED (4m47s)     SYNCED (4m47s)     SYNCED (4m24s)     SYNCED (4m47s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    reviews-v2-6f85cb9b7c-cfc68.default                    Kubernetes     SYNCED (4m41s)     SYNCED (4m41s)     SYNCED (4m24s)     SYNCED (4m41s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2
#    reviews-v3-6f5b775685-cw7tc.default                    Kubernetes     SYNCED (4m43s)     SYNCED (4m43s)     SYNCED (4m24s)     SYNCED (4m43s)     IGNORED     istiod-7f8b586864-8mc4c     1.23.2

# productpage 파드의 istio-proxy 로그 확인 Access log 가 출력 - Default access log format : 링크
$ kubetail -l app=productpage -c istio-proxy -f
  • k3s-m NodePort 접속 확인
#
$ export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
$ echo $IGWHTTP
# => 31677

# 접속 확인
$ kubectl get svc -n istio-system istio-ingressgateway
# => NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                      AGE
#    istio-ingressgateway   NodePort   10.10.200.171   &lt;none&gt;        15021:30953/TCP,80:31677/TCP,443:30737/TCP,31400:30617/TCP,15443:31668/TCP   3h31m
$ curl -s http://localhost:$IGWHTTP/productpage
$ curl -s http://192.168.10.101:$IGWHTTP/productpage
$ curl -s http://192.168.10.102:$IGWHTTP/productpage
# => &lt;meta charset=&quot;utf-8&quot;&gt;
#    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
#    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
#    
#    &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    ...

# 정보 확인
$ echo $MYDOMAIN
# => sweetlittlebird.com
$ cat /etc/hosts
# => ...
#    127.0.2.1 k3s-m k3s-m
#    192.168.10.10 k3s-m
#    192.168.10.101 k3s-w1
#    192.168.10.102 k3s-w2
#    192.168.10.103 k3s-w3
#    192.168.10.10 sweetlittlebird.com

#
$ curl -s http://$MYDOMAIN:$IGWHTTP/productpage
# => &lt;meta charset=&quot;utf-8&quot;&gt;
#    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
#    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
#    
#    &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    ...
  • 자신의 PC에서 접속 확인
#
$ echo $MYDOMAIN $IGWHTTP
# => sweetlittlebird.com 31677
$ cat /etc/hosts

#
$ curl -v -s $MYDOMAIN:$IGWHTTP/productpage

img.png Bookinfo 접속 결과 - 새로고침 할 때 마다 다른 파드에 접속되면서 리뷰가 달라짐

  • testpc 에서 접속 실행
# istio ingress gw 를 통한 접속 테스트
$ curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>"
# => &lt;title&gt;Simple Bookstore App&lt;/title&gt;
$ while true; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 1; done
# => &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    --------------
#    &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    --------------
#    ...
$ for i in {1..100};  do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; done
# => &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    &lt;title&gt;Simple Bookstore App&lt;/title&gt;
#    ...

모니터링

  • 옵저빌리티 연동 문서 : 링크
Kiali (키알리) 소개
  • Kiali는 Istio 서비스 메시의 모니터링 및 시각화 도구입니다.
  • 주 데이터 소스는 Prometheus와 Jaeger 등입니다.
  • 특히 Jaeger와 연동하여 서비스 간의 호출 관계를 시각화하여 볼 수 있습니다.
  • Istiod의 health 상태를 확인하기 위해 istiod 파드를 직접 접속합니다. (기본 15014포트)
Kiali 설치 및 확인
# Install Kiali and the other addons and wait for them to be deployed. : Kiali dashboard, along with Prometheus, Grafana, and Jaeger.
$ tree ~/istio-$ISTIOV/samples/addons/
# => /root/istio-1.23.2/samples/addons/
#    ├── README.md
#    ├── extras
#    │   ├── prometheus-operator.yaml
#    │   ├── skywalking.yaml
#    │   └── zipkin.yaml
#    ├── grafana.yaml
#    ├── jaeger.yaml
#    ├── kiali.yaml
#    ├── loki.yaml
#    └── prometheus.yaml
$ kubectl apply -f ~/istio-$ISTIOV/samples/addons # 디렉터리에 있는 모든 yaml 자원을 생성
# => deployment.apps/grafana created
#    deployment.apps/jaeger created
#    deployment.apps/kiali created
#    deployment.apps/prometheus created
#    ...
$ kubectl rollout status deployment/kiali -n istio-system
# => deployment &quot;kiali&quot; successfully rolled out

# 확인
$ kubectl get all,sa,cm -n istio-system
$ kubectl get svc,ep -n istio-system
# => NAME                           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                                                      AGE
#    service/grafana                ClusterIP   10.10.200.178   &lt;none&gt;        3000/TCP                                                                     69s
#    service/istio-ingressgateway   NodePort    10.10.200.171   &lt;none&gt;        15021:30953/TCP,80:31677/TCP,443:30737/TCP,31400:30617/TCP,15443:31668/TCP   5h48m
#    service/istiod                 ClusterIP   10.10.200.215   &lt;none&gt;        15010/TCP,15012/TCP,443/TCP,15014/TCP                                        5h48m
#    service/jaeger-collector       ClusterIP   10.10.200.121   &lt;none&gt;        14268/TCP,14250/TCP,9411/TCP,4317/TCP,4318/TCP                               69s
#    service/kiali                  ClusterIP   10.10.200.19    &lt;none&gt;        20001/TCP,9090/TCP                                                           68s
#    service/loki                   ClusterIP   10.10.200.227   &lt;none&gt;        3100/TCP,9095/TCP                                                            68s
#    service/loki-headless          ClusterIP   None            &lt;none&gt;        3100/TCP                                                                     68s
#    service/loki-memberlist        ClusterIP   None            &lt;none&gt;        7946/TCP                                                                     68s
#    service/prometheus             ClusterIP   10.10.200.148   &lt;none&gt;        9090/TCP                                                                     68s
#    service/tracing                ClusterIP   10.10.200.133   &lt;none&gt;        80/TCP,16685/TCP                                                             69s
#    service/zipkin                 ClusterIP   10.10.200.29    &lt;none&gt;        9411/TCP                                                                     69s
#    
#    NAME                             ENDPOINTS                                                           AGE
#    endpoints/grafana                172.16.2.17:3000                                                    69s
#    endpoints/istio-ingressgateway   172.16.2.14:15443,172.16.2.14:15021,172.16.2.14:31400 + 2 more...   5h48m
#    endpoints/istiod                 172.16.3.16:15012,172.16.3.16:15010,172.16.3.16:15017 + 1 more...   5h48m
#    endpoints/jaeger-collector       172.16.3.19:9411,172.16.3.19:14250,172.16.3.19:4317 + 2 more...     69s
#    endpoints/kiali                  172.16.1.16:9090,172.16.1.16:20001                                  68s
#    endpoints/loki                                                                                       68s
#    endpoints/loki-headless                                                                              68s
#    endpoints/loki-memberlist                                                                            68s
#    endpoints/prometheus             172.16.3.20:9090                                                    67s
#    endpoints/tracing                172.16.3.19:16685,172.16.3.19:16686                                 69s
#    endpoints/zipkin                 172.16.3.19:9411                                                    69s

# kiali 서비스 변경
$ kubectl patch svc -n istio-system kiali -p '{"spec":{"type":"NodePort"}}'
# => service/kiali patched

# kiali 웹 접속 주소 확인
$ KIALINodePort=$(kubectl get svc -n istio-system kiali -o jsonpath={.spec.ports[0].nodePort})
$ echo -e "KIALI UI URL = http://$(curl -s ipinfo.io/ip):$KIALINodePort"
# => KIALI UI URL = http://54.123.42.212:31274

# Grafana 서비스 변경
$ kubectl patch svc -n istio-system grafana -p '{"spec":{"type":"NodePort"}}'
# => service/grafana patched

# Grafana 웹 접속 주소 확인 : 7개의 대시보드
$ GRAFANANodePort=$(kubectl get svc -n istio-system grafana -o jsonpath={.spec.ports[0].nodePort})
$ echo -e "Grafana URL = http://$(curl -s ipinfo.io/ip):$GRAFANANodePort"
# => Grafana URL = http://54.123.42.212:30266

# Prometheus 서비스 변경
$ kubectl patch svc -n istio-system prometheus -p '{"spec":{"type":"NodePort"}}'
# => service/prometheus patched

# Prometheus 웹 접속 주소 확인
$ PROMENodePort=$(kubectl get svc -n istio-system prometheus -o jsonpath={.spec.ports[0].nodePort})
$ echo -e "Prometheus URL = http://$(curl -s ipinfo.io/ip):$PROMENodePort"
# => Prometheus URL = http://54.123.42.212:30506
  • Prometheus : Targets - 파드별로 tcp/15020의 /stats/prometheus를 통해 수집

img.png

img.png

img.png

  • Grafana : 7개의 대시보드

img.png

img.png

  • Kiali : 서비스간의 호출 관계를 시각화

img.png

Kiali (키알리) 대시보드 둘러보기
  • Namespace 를 default 로 선택 후 Graph (Traffic, Versioned app graph) 에서 Display 옵션 중 ‘Traffic Distribution’과 ‘Traffic Animation’ 활성화, Security 체크 해서 확인해보겠습니다.
  • 트래픽을 발생시켜서 Kiali 대시보드를 확인해보겠습니다.
# testpc 에서 아래 실행
# 반복 접속 테스트
$ while true; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 1; done
$ while true; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 0.1; done
$ while true; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 0.5; done
$ for i in {1..100};  do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; done
$ for i in {1..1000}; do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -o "<title>.*</title>" ; done
  • Traffic Graph에서는 트래픽 흐름을 확인할 수 있습니다.

img.png

  • Workloads에서는 Log 등을 확인할 수 있고, Envoy 관련 설정 정보(Listener, Cluster, Route, Endpoint 등)를 확인할 수 있습니다.

img.png

  • Istio Config에서 Istio 관련 설정을 볼 수 있고, Action 으로 Istio 관련 오브젝트를 설정/삭제 할 수 있습니다.

Traffic Management

  • 동작 소개 : 클라이언트 PC → Istio ingressgateway 파드 → (Gateway, VirtualService + DestinationRule) → Cluster(Endpoint - 파드)
    • Gateway : 지정한 인그레스 게이트웨이로부터 트래픽이 인입, 프로토콜 및 포트, HOSTS, Proxy 등 설정이 가능합니다.
    • VirtualService : 인입 처리할 hosts 설정, L7 PATH 별 라우팅, 목적지에 대한 정책 설정 가능합니다. (envoy route config) - 링크
      • VirtualService 는 DestinationRule 에서 설정된 서브셋(subset)을 사용하여 트래픽 컨트롤을 할 수 있습니다.
      • hosts 필드 : 목적지 주소 - IP address, a DNS name (FQDN), 혹은 k8s svc 이름, wildcard (”*”) prefixes
      • Routing rules : HTTP 경우 - Match 필드(예) 헤더), Destination(istio/envoy 에 등록된 대상, subnet 에 DestinationRule 활용)
        • HTTPRoute : redirect , rewrite , fault(장애 주입) , mirror(복제, 기본 100%) , corsPolicy(CORS 삽입) , headers(헤더 조작) 등 - 링크
      • Routing rule precedence : Routing rules are evaluated in sequential order from top to bottom - 위에서 순차적 적용
    • DestinationRule : 실제 도착지(서비스와 1:1 연결)의 정교한 정책(부하분산, 연결 옵션, 서킷 브레이크, TLS 등)을 설정 - 링크
      • Load balancing options : Round robin(기본값) , Random , Weighted , Least requests - 링크
        • Destination Rule : TrafficPolicy , Subset , ConnectionPoolSettings 등 - 링크
        • 서브셋(subsets)을 정의할 수 있어 마이크로서비스 버전별로 라우팅할 때 사용한다
Request Routing 실습
  • 실습전 기본 DestinationRule 적용
# 샘플 파일들 확인
$ tree ~/istio-$ISTIOV/samples/bookinfo/networking
# => /root/istio-1.23.2/samples/bookinfo/networking
#    ├── bookinfo-gateway.yaml
#    ├── certmanager-gateway.yaml
#    ├── destination-rule-all-mtls.yaml
#    ├── destination-rule-all.yaml
#    ├── destination-rule-reviews.yaml
#    ├── egress-rule-google-apis.yaml
#    ├── fault-injection-details-v1.yaml
#    ├── virtual-service-all-v1.yaml
#    ├── virtual-service-details-v2.yaml
#    ├── virtual-service-ratings-db.yaml
#    ├── virtual-service-ratings-mysql-vm.yaml
#    ├── virtual-service-ratings-mysql.yaml
#    ├── virtual-service-ratings-test-abort.yaml
#    ├── virtual-service-ratings-test-delay.yaml
#    ├── virtual-service-reviews-50-v3.yaml
#    ├── virtual-service-reviews-80-20.yaml
#    ├── virtual-service-reviews-90-10.yaml
#    ├── virtual-service-reviews-jason-v2-v3.yaml
#    ├── virtual-service-reviews-test-v2.yaml
#    ├── virtual-service-reviews-v2-v3.yaml
#    └── virtual-service-reviews-v3.yaml

# 기본 DestinationRule 적용
$ kubectl apply -f ~/istio-$ISTIOV/samples/bookinfo/networking/destination-rule-all.yaml
# => destinationrule.networking.istio.io/productpage created
#    destinationrule.networking.istio.io/reviews created
#    destinationrule.networking.istio.io/ratings created
#    destinationrule.networking.istio.io/details created

# DestinationRule 확인 dr(=destinationrules) : KIALI Services 확인 시 GW, VS, DR 확인
$ kubectl get dr
# => NAME          HOST          AGE
#    details       details       31s
#    productpage   productpage   31s
#    ratings       ratings       31s
#    reviews       reviews       31s

img.png

  • virtual-service-all-v1.yaml : 4개 서비스 모두 v1 의 서브셋(subset) 에 전송하는 정책 테스트
# 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
---
# istio vs(virtualservices) 확인
$ kubectl get vs
# => NAME       GATEWAYS               HOSTS   AGE
#    bookinfo   [&quot;bookinfo-gateway&quot;]   [&quot;*&quot;]   3h30m

# 모든 마이크로서비스에 대해 v1 의 서브셋(subset) 에 전송되게 virtualservices 적용
$ kubectl apply -f virtual-service-all-v1.yaml
# => virtualservice.networking.istio.io/productpage created
#    virtualservice.networking.istio.io/reviews created
#    virtualservice.networking.istio.io/ratings created
#    virtualservice.networking.istio.io/details created

# istio vs(virtualservices) 확인 >> KIALI 에서 reviews v2,v3 향하는 트래픽 경로가 사라진다!
$ kubectl get virtualservices
# => NAME          GATEWAYS               HOSTS             AGE
#    bookinfo      [&quot;bookinfo-gateway&quot;]   [&quot;*&quot;]             3h30m
#    details                              [&quot;details&quot;]       10s
#    productpage                          [&quot;productpage&quot;]   10s
#    ratings                              [&quot;ratings&quot;]       10s
#    reviews                              [&quot;reviews&quot;]       10s

img.png

  • 모든 트래픽이 v1으로 향하게 되어서 브라우저를 새로고침해도 v1만 나오게 됩니다.

  • virtual-service-reviews-test-v2.yaml : User Identity 기반 라우팅, end-user 커스텀 헤더에 jason 매칭 시 reviews v2 로 전달

    • Match 조건에는 완전 일치(exact) , 전방 일치(prefix) , 정규 표현(regex) - 3가지 패턴을 선택할 수 있다
# 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
# 모든 마이크로서비스에 대해 v1 의 서브셋(subset) 에 전송되게 virtualservices 적용
$ kubectl apply -f virtual-service-reviews-test-v2.yaml
# => virtualservice.networking.istio.io/reviews configured

# jason 로그인 시 로그 확인
$ kubetail -l app=productpage -f
# => [productpage-v1-d5789fdfb-f8gdf productpage] DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): details:9080
#    [productpage-v1-d5789fdfb-f8gdf productpage] send: b'GET /details/0 HTTP/1.1\r\nHost: details:9080\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\nx-request-id: 3aa3c711-d014-930f-8c2a-563c3dbbd8b5\r\n\r\n'
#    [productpage-v1-d5789fdfb-f8gdf productpage] reply: 'HTTP/1.1 200 OK\r\n'
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: content-type: application/json
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: server: envoy
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: date: Sat, 19 Oct 2024 14:32:09 GMT
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: content-length: 178
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: x-envoy-upstream-service-time: 6
#    [productpage-v1-d5789fdfb-f8gdf productpage] DEBUG:urllib3.connectionpool:http://details:9080 &quot;GET /details/0 HTTP/1.1&quot; 200 178
#    [productpage-v1-d5789fdfb-f8gdf productpage] DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): reviews:9080
#    [productpage-v1-d5789fdfb-f8gdf productpage] send: b'GET /reviews/0 HTTP/1.1
#    Host: reviews:9080
#    user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
#    Accept-Encoding: gzip, deflate
#    Accept: */*
#    Connection: keep-alive
#    x-request-id: 3aa3c711-d014-930f-8c2a-563c3dbbd8b5\r\n\r\n'
#    [productpage-v1-d5789fdfb-f8gdf productpage] reply: 'HTTP/1.1 200 OK\r\n'
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: x-powered-by: Servlet/3.1
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: content-type: application/json
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: date: Sat, 19 Oct 2024 14:32:09 GMT
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: content-language: en-US
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: content-length: 358
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: x-envoy-upstream-service-time: 1
#    [productpage-v1-d5789fdfb-f8gdf productpage] header: server: envoy
#    [productpage-v1-d5789fdfb-f8gdf productpage] DEBUG:urllib3.connectionpool:http://reviews:9080 &quot;GET /reviews/0 HTTP/1.1&quot; 200 358
#    [productpage-v1-d5789fdfb-f8gdf productpage] INFO:werkzeug:::ffff:127.0.0.6 - - [19/Oct/2024 14:32:09] &quot;GET /productpage HTTP/1.1&quot; 200 -
#    ...
  • 웹브라우저에서 productpage로 접속 후 새로고침을 해보겠습니다.
    • 로그인 전에는 v1으로 접속 됩니다. img.png
  • 오른쪽 상단의 Sign in 클릭 후 jason으로 로그인 후 새로고침을 해보겠습니다.
    • 로그인 후에는 v2로 접속 됩니다. img_1.png
    • 헤더에는 end-user:jason 이 추가되어 있습니다.

      # 로그인 후 헤더헤더
      # => [productpage-v1-d5789fdfb-f8gdf productpage] send: b'GET /reviews/0 HTTP/1.1
      #    Host: reviews:9080
      #    user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
      #    Accept-Encoding: gzip, deflate
      #    Accept: */*
      #    Connection: keep-alive
      #    <span style="color: red;">end-user: jason</span>
      #    x-request-id: 03366677-7032-9291-a4b9-7009a6257394
      #    cookie: session=eyJ1c2VyIjoiamFzb24ifQ.ZxPUxA.3MJkXTFH8zJtg_YlXlvzq8xArpc'
      
Fault Injection 실습
  • virtual-service-ratings-test-delay.yaml : end-user 가 jason 는 ratings v1 에 7초 지연 발생, 그외 사용자는 ratings v1 정상 연결
# 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
# virtualservices 적용
$ kubectl apply -f virtual-service-ratings-test-delay.yaml
# => virtualservice.networking.istio.io/ratings configured

# 로그 확인 : product 입장에서 접속 사용자(clinet) 연결을 끊어버림 0 DC downstream_remote_disconnect
$ kubetail -l app=productpage -f
  • 웹브라우저에서 jason으로 로그인된 상태에서 접속시 6~7초 지연이 발생하는것을 확인할 수 있습니다.

img.png

  • virtual-service-ratings-test-abort.yaml : end-user 가 jason 는 ratings v1 에 500 에러 리턴, 그외 사용자는 ratings v1 정상 연결
# 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
# virtualservices 적용
$ kubectl apply -f virtual-service-ratings-test-abort.yaml
# => virtualservice.networking.istio.io/ratings configured

# 로그 확인
$ kubetail -l version=v2 -f

jason으로 로그인 했을때 Rating 서비스에 500 에러가 발생하는것을 확인할 수 있습니다.

img.png

또한 kiali에서도 어느 구간에서 오류가 발생했는지 확인할 수 있으며, Flags도 확인할 수 있습니다. 링크
(이경우 FI는 Fault Injection을 의미으로 일부러 오류를 일으킨 것을 확인 할 수 있습니다.)

img.png

Traffic Shifting 실습
  • 카나라 배포 전략 등 활용 - 링크

  • virtual-service-reviews-50-v3.yaml : 가중치 (weight-based routing), reviews 에 v1(50%), v3(50%)

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
# virtualservices 적용
$ kubectl apply -f virtual-service-reviews-50-v3.yaml
# => virtualservice.networking.istio.io/reviews configured

$ for i in {1..100};  do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -m 1 "reviews-v\?" ; done | sort | uniq -c
# =>      53                   reviews-v1-6584ddcf65-gnc9j
#         47                   reviews-v3-6f5b775685-cw7tc

대략 50%의 확률로 v1과 v3로 접속되는것을 확인할 수 있습니다.

  • virtual-service-reviews-80-20.yaml : 가중치 (weight-based routing), reviews 에 v1(80%), v2(20%)
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 80
    - destination:
        host: reviews
        subset: v2
      weight: 20
# virtualservices 적용
$ kubectl apply -f virtual-service-reviews-80-20.yaml
# => virtualservice.networking.istio.io/reviews configured

$ for i in {1..100};  do curl -s $MYDOMAIN:$IGWHTTP/productpage | grep -m 1 "reviews-v\?" ; done | sort | uniq -c
# =>      79                   reviews-v1-6584ddcf65-gnc9j
#         21                   reviews-v2-6f85cb9b7c-cfc68

대략 80%의 확률로 v1과 20%의 확률로 v2로 접속되는것을 확인할 수 있습니다.

kiali에서도 어느 구간에서 어떠한 비중으로 트래픽이 흘러가는지 확인할 수 있습니다. img.png

Security (보안)

  • 요구사항
    • MITM (Man-In-The-Middle) 공격 방지를 위해 모든 트래픽은 mTLS로 암호화 되어야 합니다.
    • 또한 접근 제어 정책이 필요하며, 감사 로깅을 통해 보안 이슈를 식별이 가능 해야 합니다.
  • 목표
    • 기본 셋팅을 안전하게 하기 : 별도의 셋팅이 없어도 보안을 유지할 수 있도록 설정
    • 깊은 방어 : 기존에 존재하는 보안 시스템과 통합되어, 다층 방어를 구성
    • Zero-trust network : 네트워크를 신뢰하지 않음으로써 보안 강화 https://genians.co.kr/genians-nac/zt/
  • 구성요소
    • Certification Authority (CA) : 인증서 발급, 관리, 갱신
    • 보안 정책 (인증정책, 인가정책 등) 관련 설정을 각 프록시에 전달하는 API 서버
    • 사이드카와 프록시를 정책 강제 지점(Policy Enforcement Point-PEPs)으로 사용하여 클라이언트와 서버간의 통신을 보호
    • Envoy Proxy 확장 기능을 통해 telemetry와 감사 로깅 수집

20241019_kans_w7_29.svg istio 보안 아키텍쳐

  • TLS와 mTLS
    • TLS (Transport Layer Security) : 통신 보안을 위한 프로토콜로, 인터넷 상에서 데이터를 암호화하는 표준화된 방법입니다. 기본적으로 서버의 인증서만 확인하는 방식을 사용합니다.
    • mTLS (mutual TLS) : 서버와 클라이언트가 서로 인증을 하고 서로 신뢰할 수 있는지 확인하는 방식입니다.
  • Authentication (인증), Authorization (인가) (Auto mTLS)
    • Istio는 모든 워크로드에 X.509 인증서를 부여하고, 서로 인증을 통해 통신을 보호합니다.
    • Envoy proxy와 함께 실행되는 Istio agent는 istiod와 함께 동작하면서 자동으로 인증서를 갱신합니다. 20241019_kans_w7_30.svg
      1. istiod는 CSR(인증서 서명 요청)을 수행하기 위해 gRPC 서비스를 제공합니다.
      2. Envoy는 SDS(Secret Discovery Serice) API를 통해 인증서와 키 요청을 보냅니다.
      3. istio-agent는 SDs 요청을 받으면 Private Key와 CSR을 생성한 후 자격증명 (credential)과 함께 CSR istiod에 전송하여 서명을 요청합니다.
      4. CA는 CSR에 포함된 자격증명(credential) 의 유효성을 검사하고 CSR에 서명하여 인증서를 생성합니다.
      5. istio-agent는 istiod로부터 받은 인증서(certiftcate)와 개인키 private Key)를 Envoy SDS API를 통해 Envoy에게 보냅니다.
      6. 위의 CSR 프로세스는 인증서 및 키 순환을 위해 주기적으로 반복됩니다
  • Authentication (인증) : 2가지 타입 제공 20241019_kans_w7_31.svg
    1. Peer Authentication :
      • 서비스간 인증에 사용되며 Client가 연결을 확인하는데 사용. 서비스 코드 변경없이 mTLS 제공
    2. Request Authentication :
      • Request에 첨부된 자격증명(Credential)을 통해 최종 사용자 인증에 사용
      • istio는 JWT (JSON Web Token)을 지원하여 최종 사용자 인증을 제공
      • 커스텀 인증 제공자를 비롯하여 OpenID Connect, Keycloak, Auth0 등 다양한 인증 방식을 지원
  • Authorization (인가) 20241019_kans_w7_32.svg
    • istio는 mesh 단위, 네임스페이스 단위, 워크로드 단위로 접근을 제어 할 수 있는 인가 기능을 제공합니다. 다음과 같은 이점이 있습니다.
      • 워크로드와 워크로드간, 또는 사용자와 워크로드간 인가 제공
      • 단일한 AuthorizationPolicy CRD를 사용한 단순한 API 제공
      • 유연한 정책 제공 : 커스텀 조건을 등록할 수 있고, CUSTOM, DENY, ALLOW 액션 지원
      • 고성능 : Envoy Proxy를 통해 인가 정책을 적용하므로 성능 저하가 없음
      • 높은 호환성 : gRPC, HTTP, HTTPS 등을 지원하며, 일반적인 TCP 프로토콜도 지원
Authentication (Auto mTLS) 실습
  • 기존 파드에 로그에서 인증서 등 보안 관련 내용 확인

      # CA Endpoint, CA(/var/run/secret/istio/root-cert,pem), citadelclient, SDS server 등등
      $ kubectl logs ratings-v1-7c9bd4b87f-s9gxs -c istio-proxy -f
      $ kubetail
      
      # 인증정책 확인
      $ kubectl get peerauthentications.security.istio.io
      # => No resources found 
        
      # envoy 에 cert 정보 확인 : istio-proxy 에 admin페이지 접속 or kaila 에서 envoy 에서 확인    
    
  • bookinfo → kiali → product 계속 접속
  • kiali 에서 Display(Security 체크) 후 자물쇠 클릭하면 오른쪽 창에서 보안설정을 확이할 수 있습니다. : mTLS Enabled, spiffe(Secure name)

img.png

  • test 네임스페이스 생성 후 파드 생성(sidecar 미적용) 후 ratings 접속
# 네임스페이스 생성
$ kubectl create ns test
# => namespace/test created

# 파드 생성
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: netpod
  namespace: test
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 확인 : sidecar 미적용
$ kubectl get pod -n test
# => NAME     READY   STATUS    RESTARTS   AGE
#    netpod   1/1     Running   0          19s

# ratings 서비스 확인
$ kubectl get svc ratings
# => NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
#    ratings   ClusterIP   10.10.200.163   &lt;none&gt;        9080/TCP   8h

# ratings 접속 시도 : 성공
$ kubectl exec -it -n test netpod -- curl ratings.default:9080/health ;echo
$ {"status":"Ratings is healthy"}

# 로그 확인
$ kubetail -l app=ratings -f

img.png NS(default, test 체크) netpod 에서 접속 시 unknown 으로 표기되며, 접근 성공(녹색) 확인

# Peer authentication 설정 변경 : PERMISSIVE(mTLS 사용/미사용 모두 허용) → STRICT(반드시 mTLS 사용, 미사용 시 거부)
$ cat <<EOF | kubectl create -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default-strict
spec:
  mtls:
    mode: STRICT
EOF

# ratings 접속 시도 : 실패!
$ kubectl exec -it -n test netpod -- curl ratings.default:9080/health ;echo
# => curl: (56) Recv failure: Connection reset by peer
#    command terminated with exit code 56

$ kubetail -l app=ratings -f
# => [ratings-v1-7c9bd4b87f-s9gxs istio-proxy] [2024-10-01T17:21:12.708Z] &quot;- - -&quot; 0 NR filter_chain_not_found - &quot;-&quot; 0 0 0 - &quot;-&quot; &quot;-&quot; &quot;-&quot; &quot;-&quot; &quot;-&quot; - - 172.16.3.17:9080 172.16.1.17:34938 - -
  • 실습 자원 삭제
$ kubectl delete PeerAuthentication default-strict
# => peerauthentication.security.istio.io &quot;default-strict&quot; deleted
$ kubectl exec -it -n test netpod -- curl ratings.default:9080/health ;echo
# => {&quot;status&quot;:&quot;Ratings is healthy&quot;}
# <span style="color: green;">👉 인증정책 강제 정책 삭제시 다시 통신이 됩니다.</span>

$ kubectl delete pod -n test netpod
$ kubectl delete ns test

Istio 통신 흐름

istio 사용시 트래픽은 호스트의 tcp/ip 스택과 iptables, 파드내의 iptables와 envoy를 경유하게 됩니다. istio는 강력하고 다양한 기능들을 제공하지만 비용(지연추가, 프로세서 사용량 추가, 복잡한 구조)이 필요합니다.

img.png

외부 클라이언트(PC 등)에서 파드로 접속되는 과정은 다음과 같습니다.

img_1.png

위의 그림에서 처럼 iptables를 여러번 거치고, DNAT등을 통해 포트번호등이 80 (http) => 15006 (istio-proxy) 로 변경되는 등의 작업을 여러번 거칩니다. 또한 파드와 호스트간 통신 envoy를 요청을 받을때와 응답할때 모두 거쳐가는 것을 확인할 수 있습니다.

  • 파드 내 Iptables 적용 흐름

img.png 출처 : Jimmy song 블로그

다음 블로그에서 자세한 내용을 확인할 수 있습니다. Understanding the Sidecar Injection, Traffic Intercepting & Routing Process in Istio


마치며

1주차 컨테이너 격리 이후에 또 다시 뇌정지가 찾아온 주차였습니다. 이번주에 학습한것도 많은데, 이것이 일부만 살펴본것이라니 놀랍습니다. 정말 만든 분들도, 쓰는 분들도, 스터디를 진행해주시는 분들도 대단합니다. :thumbsup:

인증이나 인가 등 gateway api에서 아쉬웠던 부분들이 나와서 좋았습니다. 찾던 기능인데 마침 이번주에 다루게 되어서 좋았습니다. Kiali를 통한 트래픽 시각화도 정말 유용하게 쓰일 것 같습니다. 복잡하긴 하지만 좋은 기능들이 많았습니다.

시간이 모자라서 미처 실습하지 못한 부분들과 Ambient Mesh도 바쁜일이 지나가면 다시 살펴봐야겠습니다. 스터디를 준비해주신 가시다님과 참여하신 분들 감사합니다. :pray: