WellSpring

7주차 - 12장, 14장 이스티오 스케일링, 데이터 플레인 확장 본문

ISTIO

7주차 - 12장, 14장 이스티오 스케일링, 데이터 플레인 확장

daniel00324 2025. 5. 18. 21:07

 

 

목차

     

     

     

    ※ 본 게재 글은 gasida님의 'Istio' 스터디 강의 및 실습예제와 'Istio in Action' 서적 참고하여 작성하였습니다.


    12장. 조직 내에서 이스티오 스케일링하기

    더보기

    This chapter covers Scaling Istio in your organization

    • 여러 클러스터에서 서비스 메시 스케일링하기 Scaling the service mesh in multiple clusters
    • 두 클러스터를 합치기 위한 전체 조건 해결하기 Resolving the prerequisites to join two clusters
    • 여러 클러스터의 워크로드 간에 공통 신뢰 common trust 설정하기 Setting up common trust between workloads of different clusters
    • 클러스터 간 워크로드 찾기 Discovering cross-cluster workloads
    • east-west 트래픽을 위한 이스티오 인그레스 게이트웨이 설정하기 Configuring Istio’s ingress gateway for east-west traffic

    ▶ 들어가며 : 다중 클러스터 서비스 메시

    더보기
    • 지금까지는 단일 클러스터의 메시 내에서 활성화하는 이스티오의 여러 기능을 살펴봤다.
    • 그러나 서비스 메시는 단일 클러스터에 종속되지 않는다.
    • 서비스 메시는 여러 클러스터에 걸쳐 있을 수 있으며, 클러스터 모두에 동일한 기능을 제공할 수 있다.
    • 사실 메시의 가치는 워크로드가 많아질수록 증가한다.

    • 그런데 우리는 언제 서비스 메시가 여러 클러스터에 걸쳐 있길 원하는가?
    • 단일 클러스터와 비교할 때 다중 클러스터 서비스 메시는 어떤 이점을 갖는가?
    • 이와 같은 질문에 답하기 위해 가상의 ACME사를 다시 살펴보자.
    • 이 회사는 클라우드 플랫폼으로 이전했고, 마이크로서비스 아키텍처 때문에 늘어난 네트워크 복잡성을 모두 경험했다.

    12.1 다중 클러스터 서비스 메시의 이점

    ▶ 소개 : The benefits of a multi-cluster service mesh - Docs

    더보기
    • 클라우드 마이그레이션 초기에 ACME는 클러스터의 규모를 어떻게 조정할지에 대한 딜레마에 직면했다.
    • 이 회사는 단일 클러스터로 시작했지만 그 결정을 신속하게 바꿨다.
    • ACME는 다음과 같은 이점 때문에 작은 클러스터 다수을 쓰기로 결정했다.
      • 격리성 강화 Improved isolation
        • 한 팀의 사고가 다른 팀에 영향을 미치지 않게 한다.
      • 장애 경계 Failure boundary
        • 클러스터 전체에 영향을 미칠 수 있는 설정이나 운영에 경계선을 설정해 클러스터가 다운될 때 아키텍처의 다른 부분에 미치는 영향을 줄인다.
      • 규정 준수 Regulatory and compliance
        • 아키텍처의 다른 부분에서 민감 데이터로 접근하는 서비스를 제한한다.
      • 가용성 및 성능 향상 Increased availability and performance
        • 가용성을 늘리기 위해 여러 리전에서 클러스터를 운영하고, 가장 가까운 클러스터로 트래픽을 라우팅해 지연 시간을 줄인다.
      • 다중 및 하이브리드 클라우드 Multi and hybrid clouds
        • 서로 다른 클라우드 프로바이더 또는 하이브리드 클라우드와 같이 다양한 환경에서 워크로드를 실행할 수 있게 한다.

    • 초기 평가에서 ACME가 서비시 메시를 채택하는 데 주요 동기로 삼았던 것은 여러 클러스터 간에 서비스 메시를 확장할 수 있는 기능과, 클러스터 간에 트래픽 관리, 관찰 가능성, 보안을 지원하는 기능이었다.
    • 다중 클러스터 작업을 지원하고자 회사는 두 가지 접근법을 고려했다 - Docs

     

    A. 다중 클러스터 서비스 메시 Multi-cluster service mesh

    https://istio.io/latest/docs/ops/deployment/deployment-models/#multiple-clusters

     

    • 여러 클러스터에 걸쳐 있고 워크로드가 클러스터 간에 트래픽을 라우팅하도록 설정하는 메시. A mesh that spans multiple clusters and configures workloads to route cross-cluster traffic.
    • 이 모든 것은 VirtualService, DestinationRule, Sidecar 등이 적용된 Istio 구성을 따른다. All of this is in accordance with the applied Istio configuration

    B. 메시 연합 (다중 메시) Mesh federation, also known as multi-mesh

    https://istio.io/latest/docs/ops/deployment/deployment-models/#multiple-meshes

     

    • 개별 서비스 메시 2개의 워크로드 간 통신을 노출하고 활성화한다. Exposes and enables the communication of workloads of two separate service meshes.
    • 이 선택지는 덜 자동화돼 있어서 서비스 간 트래픽을 설정하려면 두 메시 모두에게 수동 설정이 필요한다.
    • 그러나 서로 다른 팀에서 두 메시를 운영하거나 보안 격리 요구 사항이 엄격한 경우 좋은 선택지다.

    ☞ 이 책에서 다루는 선택지는 다중 클러스터 서비스 메시다. 메시 연합 같은 경우에는 공식 문서를 참고하자 - Docs 

     

     


    12.2 다중 클러스터 서비스 메시 개요 multi-cluster service mesh

    ▶ 소개 : 필요 사항

    더보기
    • 다중 클러스터 서비스 메시는 앱에는 완전히 투명한 방식으로 클러스터 간에 서비스를 연결하면서, 클러스터 간 통신을 위한 서비스 메시의 기능(세밀한 트래픽 제오, 복원력, 관찰 가능성, 보안 등)은 모두 유지한다.
    • 이스티오가 다중 클러스터 서비스 메시를 구현하는 방법은 모든 클러스터의 서비스를 퀴리한 다음 이 정보로 서비스 프록시에 클러스터 사이에서 서비스 간 트래픽을 라우팅하는 방법을 설정하는 것이다.
    • 그림 12.1은 클러스터들을 하나의 메시로 결합하는 데 필요한 것을 보여준다.

     

    • 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
      • Istio 컨트롤 플레인은 서비스 프록시를 설정하기 위해 동료 K8S 클러스터의 워크로드를 찾아야 한다. (K8S 클러스터 API 서버를 상대편 클러스터의 이스티오 컨트롤 플레인에서 접근할 수 있어야 한다. The control planes must discover the workloads in the peer clusters in order to configure the service proxies (the API server of the clusters must be accessible to Istio’s control plane in the opposite cluster).
    • 클러스터 간 워크로드 연결성 Cross-cluster workload connectivity
      • 워크로드는 서로 연결돼 있어야 한다. 워크로드 엔드포인트를 인지하고 있어도 커넥션을 시작할 수 없다면 쓸모가 없다.
    • 클러스터 간 공통 신뢰 Common trust between clusters
      • 이스티오의 보안 기능을 활성화하려면 클러스터 간 워크로드가 서로 인증해야 한다. Cross-cluster workloads must mutually authenticate to enable the security features of Istio.

    • 이 기준들을 충족하면 클러스터가 다른 클러스터에서 실행 중인 워크로드를 인지할 수 있고, 워크로드가 서로 연결될 수 있으며, 워크로드가 이스티오 정책을 사용해 인증하고 인가할 수 있다.
    • 이들 모두가 다중 클러스터 서비스 메시를 설정하기 위한 전체 조건이다.

    다중 클러스터 연결성과 보안 (Multi-cluster connectivity and security) 상술했듯이, 이스티오가 클러스터 간에 다중 클러스터 연결을 수립하려면 워크로드는 동료 클러스터의 쿠버네티스 API에 접근하는 방법으로만 찾을 수 있다. 어떤 조직에서는 이것이 바람직하지 않는 보안 태세일 수 있다. 각 클러스터가 다른 클러스터의 API 모두에 접근할 수 있기 때문이다. For some organizations, this may be an undesirable security posture where each cluster has access to all other clusters’ APIs. 이 경우에는 메시 연합이 더 나은 방식이다. In this case, mesh federation is a better approach. 

     

     


    12.2.1 이스티오 다중 클러스터 배포 모델 Istio multi-cluster deployment models - Blog

    더보기
    • 다중 클러스터 서비스 메시에서는 istio 클러스터 유형을 두 가지로 구분한다.
      • 기본 클러스터 primary cluster : 이스티오 컨트롤 플레인이 설치된 K8S 클러스터 → 아래 그림 중 왼쪽
      • 원격 클러스터 remote cluster : 설치된 이스티오 컨트롤 플레인과 떨어져 있는 K8S 클러스터 → 아래 그림 중 오른쪽
    기본-원격 배포 모델
    • 달성하고자 하는 가용성 수준에 따라 배포 모델 3가지
      1. 기본-원격(컨트롤 플레인 공유) 배포 모델 primary-remote (shared control plane) → 위 그림 12.2
      2. 기본-기본(복제된 컨트롤 플레인) 배포 모델 primary-primary (replicated control plane) → 아래 그림 12.3
      3. 외부 컨트롤 플레인 배포 모델 external control plane → 아래 그림 12.4

    • 기본-기본(복제된 컨트롤 플레인) 배포 모델 primary-primary (replicated control plane) 에는 컨트롤 플레인이 여럿이므로 가용성이 높지만, 트레이드오프로 리소스가 더 많이 필요하다.
    • 이렇게 되면 가용성이 향향되는데, 중단 범위가 중단이 발생한 클러스터로 국한되기 때문이다.
    • 이 모델을 복제된 컨트롤 플레인 배포 모델이라고 부른다. We refer to this model as the replicated control plane deployment model.
    기본-기본 모델
    • 외부 컨트롤 플레인 배포 모델 external control plane 은 모든 클러스터가 컨트롤 플레인과 떨어져 있는 배포 모델이다.
    • 이 배포 모델로 클라우드 프로바이더가 이스티오를 관리형 서비스로 제공할 수 있다.
    외부 컨트롤 플레인 배포 모델

     

     


    12.2.2 다중 클러스터 배포에서 워크로드는 어떻게 찾는가?    
         - How workloads are discovered in multi-cluster deployments

    더보기
    • 이스티오의 컨트롤 플레인은 쿠버네티스 API 서버와 통신해 서비스 뒤의 서비스와 엔드포인트 같은 서비스 프록시 설정 관련 정보를 수집해야 한다.
    • 쿠버네티스 API 서버에 요청하는 것은 막강한 일종의 권한인데, 리소스 세부 정보를 조회하고, 민감 정보를 쿼리할 수 있으며, 클러스터를 불량하고 되돌이킬 수 없는 상태로 만들 수 있는 정도로 리소스를 업데이트하거나 지울 수 있기 때문이다.

    토큰RBAC으로 원격 쿠버네티스 API로의 접근을 보안 처리하는 방법을 다루겠지만, 통찰력 있는 독자는 이 접근법의 트레이드오프를 고려해야 한다. 메시 연합어떻게 이 위험성을 완화할 수 있는지 앞 절을 참조하자.


    • 쿠버네티스는 RBAC를 이용해 API 서버로의 접근을 보호한다. 쿠버네티스 RBAC은 광범위한 주제로, 이 책의 범위를 벗어난다.
    • 그러나 클러스터 간 디스커버리를 용이하게 하는데 사용하는 개념 일부를 잠시 언급할 수는 있다.
      • 서비스 어카운트는 기계나 서비스 등 사람이 아닌 클라이언트에 ID를 제공한다. Service accounts provide identity to non-human clients such as machines or services.
      • 서비스 어카운트 토큰은 서비스 어카운트마다 자동으로 생성돼 해당 ID 클레임을 나타낸다. 토큰은 JWT 형식이며 쿠버네티스가 파드에 주입한다. 파드는 이 토큰을 사용해 API 서버에게 (자신의 신원) 인증할 수 있다. Service account tokens are automatically generated for every service account and represent its identity claim. Tokens are formatted as JSON Web Tokens and are injected by Kubernetes into Pods that can use the tokens to authenticate to the API server.
      • role 과 클러스터롤 clusterrole 은 서비스 어카운트나 일반 사용자 같은 ID에 대한 권한 집합을 정의한다. Roles and cluster roles define the set of permissions for identity, such as a service account or a regular user.
    • 그림 12.5는 istiod인증인가를 제공하는 쿠버네티스 리소스를 시각화한다.
    istiod의 ID와 접근을 설정하는 리소스
    • 클러스터 간 워크로드 디스커버리는 기술적으로 동일한다. Cross-cluster workload discovery is technically the same.
    • 그러나 그림 12.6과 같이 istiod원격 클러스터의 서비스 어카운트 토큰을 제공해야 한다 (구체적인 예제에서 볼 수 있듯이 API 서버로 보안 통신을 시작할 수 있는 인증서와 함께). However, as shown in figure 12.6, we need to provide istiod with the service account token of the remote cluster (along with the certificates to initiate a secure connection to the API server, as we see when we get to the concrete examples).
    • istiod는 토큰을 사용해 원격 클러스터에 인증하고, 클러스터에서 실행 중인 워크로드를 찾는다. istiod uses the token to authenticate to remote clusters and discover workloads running in them.
    • 험난한 과정처럼 들릴지도 모르지만 걱정할 필요는 없다.
    • 이 장 뒷부분에서 보겠지만 istioctl이 이 과정을 자동화해주기 때문이다.

    12.2.3 클러스터 간 워크로드 연결 Cross-cluster workload connectivity - Docs

    • 다른 전체 조건은 워크로드가 클러스터를 건너 연결할 수 있어야 한다는 것이다.
    • 클러스터가 플랫 네트워크 flat network 에 있다면, 그러나까 단일 네트워크를 공유하거나(Amazon VPC 처럼) 네트워크가 네트워크 피어링 network peering 으로 연결된 경우 등에서는 워크로드가 IP 주소로 연결 할 수 있으므로 조건은 이미 충족됐다!
    • 그러나 클러스터가 서로 다른 네트워크에 있다면 네트워크의 에지에 위치클러스터 간 트래픽을 프록시해주는 특수한 이스티오 인그레스 게이트웨이를 사용해야 한다. However, when clusters are in different networks, we have to use special Istio ingress gateways that are located at the edge of the network and proxy cross-cluster traffic.
    • 다중 네트워크 메시에서 클러스터를 잇는 인그레스 게이트웨이를 east-west 게이트웨이라고 부른다 (그림 12.7 참조). Ingress gateways that bridge clusters in multi-network meshes are known as east-west gateways

    [공식 문서] Multiple networks 제공 기능 : 중복 IP 환경 시 해결 등 - Docs

    • Overlapping IP or VIP ranges for service endpoints
    • Crossing of administrative boundaries
    • Fault tolerance
    • Scaling of network addresses
    • Compliance with standards that require network segmentation

    12.2.4 클러스터 간 공통 신뢰 Common trust between clusters - Docs

    더보기
    • 우리가 해결해야 하는 마지막 요소는 다중 클러스터 서비스 메시 내 클러스터들이 공통된 신뢰를 가져야 한다는 것이다. The last factor we need to resolve is that clusters in a multi-cluster service mesh must have common trust.

    • 공통 신뢰를 가지면 반대편 클러스터의 워크로드들이 상호 인증할 수 있게 된다.
      Having common trust ensures that workloads of opposite clusters can mutually authenticate.

    • 반대편 클러스터의 워크로드들 사이에 공통 신뢰를 달성하는 방법에는 두 가지가 있다.

    • 첫 번째 방법은 플러그인 CA 인증서라고 부르는, 공통된 루트 CA가 발급한 사용자 정의 인증서를 사용한다. The first uses what we call plug-in CA certificates: userdefined certificates issued from a common root CA.

    • 두 번째 방법은 두 클러스터가 인증서를 서명하는 데 사용하는 외부 CA를 통합한다.
      The second integrates an external CA that both clusters use to sign certificates. 

    플로그인 CA 인증서 PLUG-IN CA CERTIFICATES - Docs  

    같은 루트가 서명한 중간 CA 인증서 사용하기
    https://istio.io/v1.17/docs/tasks/security/cert-management/plugin-ca-cert/
    • 플러그인 중간 CA 인증서를 사용하는 것은 쉽다! Using plug-in intermediate CA certificates is easy!
    • 중간 CA를 이스티오가 만들게 하는 대신에 사용할 인증서를 지정하면 된다. 이 인증서는 이스티오가 설치한 네임스페이스에 시크릿으로 제공한다. Instead of letting Istio generate an intermediate CA, you specify the certificate to be used by providing it as a secret on the Istio installation namespace.
    • 두 클러스터 모두에서 그렇게 함으로써 같은 루트 CA가 서명한 중간 CA를 사용한다. (그림 12.8) You do so for both clusters and use intermediate CAs that were both signed by the common root CA.
    • 이 방법은 간단해서 좋다. 그러나 중간 CA가 노출될 경우 보안 위험이 있다. This method is favorable due to its simplicity; however, it poses a security risk if the intermediate CAs are exposed.
    • 노출을 감지해 중간 CA의 인증서를 취소할 때까지 공격자들은 중간 CA로 신뢰받는 인증서를 서명할 수 있다. Attackers can use them to sign certificates that are trusted until the exposure is detected and the intermediate CA’s certificate is revoked.
    • 이런 이유로 조직은 중간 CA를 넘겨주기를(사용하기를) 꺼린다. For this reason, organizations are reluctant to hand over intermediate CAs.
    • 노출 위험은 중간 CA를 메모리에만 올리고 etcd(시크릿 같은 쿠버네티스 리소스를 저장하는 저장소)에 쿠버네티스 시크릿으로 유지하지 않음으로써 줄일 수 있다 ⇒ ??? 아래 Secret 내용 참고 The exposure risk can be reduced by only loading the intermediate CAs into memory and not persisting them as Kubernetes secrets into etcd (the datastore where Kubernetes resources such as secrets are stored).

    ☞ 더 안전한 대안인증서에 서명하는 외부 CA를 통합하는 것이다. An even safer alternative is to integrate an external CA that signs the certificates.


    외부 인증 기관 통합 EXTERNAL CERTIFICATE AUTHORITY INTEGRATION

    • 이 방법에서 istiod는 쿠버네티스 CSR 리소스로 저장된 인증서 서명 요청 CSRs 을 검증하고 승인하는 등록 기관 역할을 한다. istiod acts as a registration authority that validates and approves certificate signing requests (CSRs) stored as Kubernetes CSRs.
    • 승인된 쿠버네티스 CSR 리소스는 다음 방법 중 하나로 외부 CA에 제출된다. The Kubernetes CSRs that are approved are submitted to the external CA in one of the following ways:
      • cert-manager 사용하기 - Github , Home , Docs
        • cert-manager 가 우리의 외부 CA를 지원할 때만 가능하다. Using cert-manager - Only viable when our external CA is supported by certmanager.
        • 지원하는 경우, cert-manager 의 istio-csr로 쿠버네티스 CSR을 지켜보고 서명을 위해 외부 CA에 제출할 수 있다.
        • 도전과제1 Istio 에 cert-manager 외부 인증 기간 통합 설정 실습 - Docs , Docs2
    Signature Algorithm: ecdsa-with-SHA256
            Issuer: O=cert-manager, O=cluster.local, CN=istio-ca
            Validity
                Not Before: Jan 13 16:51:59 2022 GMT
                Not After : Jan 13 17:51:59 2022 GMT
    ...
                X509v3 Subject Alternative Name:
                    URI:spiffe://cluster.local/ns/default/sa/httpbin

    • 맞춤 개발 Custom development
      • 승인된 쿠버네티스 CSR을 지켜보고 이를 서명을 위해 외부 CA에 제출하는 쿠버네티스 컨트롤러를 만든다. Create a Kubernetes controller that listens for approved Kubernetes CSRs and submits them to an external CA for signing.
        • 커스텀 CA에 대한 이스티오 문서 참고 - Docs , Old
      • 그러나 로컬 키가 있는 자체 서명 인증서 대신 외부 CA를 사용하도록 솔루션을 조정해야 한다. however, the solution needs to be adapted to use an external CA instead of self-signing certificates with local keys.
      • 외부 CA가 인증서를 서명한 후에는 쿠버네티스 CSR에 저장하는데, istiod가 SDS를 사용해 이를 워크로드에 전달한다. After the external CA signs the certificate, it is stored in the Kubernetes CSR, which istiod forwards to the workload using the Secret Discovery Service (SDS).

     

    • 이 장에서는 플러그인 CA 인증서를 사용해 클러스터 사이에 공통 신뢰를 설정한다. 그러면 좀 더 간단하고 다중 클러스터 서비스 메시에 초점을 유지하기 때문이다. In this chapter, we set up common trust between clusters using plug-in CA certificates, because it’s simpler and maintains focus on multi-cluster service meshes.
    • 이제 다중 클러스터 서비스 메시를 구축하는 데 필요한 조건 모두를 고수준으로 다뤘다. We have now covered at a high level all the required conditions to set up a multi-cluster service mesh.

     


    12.3 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시 개요

    인프라 구성 환경

    • 실세계 엔터프라이즈 서비스를 모방하는 인프라를 준비해보자.
    • 이 서비스는 여러 클러스터에서 실행되고, 여러 리전에 걸쳐 배포되며, 서로 다른 네트워크에 위치한다.
      • west-cluster : 사설 네트워크가 us-west 리전에 있는 쿠버네티스 클러스터. 여기서 webapp 서비스를 실행.
      • east-cluster : 사설 네트워크가 us-east 리전에 있는 쿠버네티스 클러스터. 여기서 catalog 서비스를 실행할 것이다.

    다중 클러스터 서비스 메시 다이러그램

    • 클러스터를 2개의 서로 다른 리전에 두면, 둘 중 하나에 재난이 일어난 경우의 서비스 중단으로부터 우리를 보호할 수 있다.
    • webapp 과 catalog 워크로드가 별개의 클러스터에 있어야 하는 기술적인 이유는 없으며, 단지 시연하려는 것뿐이다.
    • ‘수다스러운’ 워크로드는 지연 시간을 줄이기 위해 가능하면 가까이 있어야 한다.

    12.3.1 다중 클러스터 배포 모델 선택하기 Choosing the multi-cluster deployment model

    더보기
    • 다중 네트워크 인프라는 클러스터 사이를 연결할 수 있게 네트워크를 잇는 east-west 게이트웨이를 사용해야 하지만, 복제 컨트롤 플레인 배포 모델을 사용할지, 단일 컨트롤 플레인을 사용할지 여부는 정해져 있지 않다. 결정은 비즈니스 요구 사항에 달려 있다. The multi-network infrastructure dictates that we need to use an east-west gateway to bridge the networks to achieve cross-cluster connectivity but leaves open the decision whether to use the replicated control-plane deployment model or a single control plane. The decision is driven by the business requirements.

    • ACME의 경우에는 온라인 상점이 아주 인기가 많으며, 서비스가 중단되는 1분마다 수백만 달러의 비용이 발생한다. 정말 그렇다! 따라서 고가용성이 최우선 과제이므로, 이스티오 컨트롤 플레인각 클러스터에 배포되는 기본-기본 배포 모델을 사용할 것이다. In ACME’s case, its online store is highly popular: every minute of it being down would cost the business millions, for real! Hence high availability is a top priority, and we’ll use the primary-primary deployment model, where the Istio control plane is deployed in each cluster.

    • 이 모든 것을 종합하면, 우리는 네트워크를 잇는 east-west 게이트웨이를 사용해 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시를 구축할 것이고, 기본-기본 배포 모델을 사용할 것이다. 그럼 시작해보자! Putting it all together, we’ll set up a multi-cluster, multi-network, multi-control-plane service mesh using an east-west gateway to bridge the networks and use the primary-primary deployment model. Let’s get started!

     


    12.3.2 클라우드 인프라 준비하기 Setting up the cloud infrastructure (실습 **)

    (참고) 책은 Azure AKS 클러스터 2대를 배포함.

     

    [ 실습 환경 구성 - default ]

    더보기

    1. git 으로 istio-book source 가져오기

     ## 소스 가져오기
     git clone https://github.com/AcornPublishing/istio-in-action
     cd istio-in-action/book-source-code-master
     
     ## Visual Studio Code 실행
     code .

     

    2. helm 설치 - Link

    ## v3.18.0 설치
    $ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
    $ chmod 700 get_helm.sh
    $ ./get_helm.sh
    
    # (옵션) kube-ops-view
    helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:30007/#scale=1.5"
    open "http://localhost:30007/#scale=1.3"
    
    # (옵션) metrics-server
    helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
    helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
    kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server

     

    [실습 환경 구성] k8s(1.23.17) 배포 : west k8s 클러스터 배포 , east k8s 클러스터 배포 - (참고)Github & mypc 컨테이너

    더보기

    실습 과정 중 NodePort 사용하는 Service 에서 중복 될 수 있음. 중복 될 경우 직접 edit 수정 할 것.

    • west k8s 클러스터 배포
    # 
    kind create cluster --name west --image kindest/node:v1.23.17 --kubeconfig ./west-kubeconfig --config - <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 30000 # istio-ingrssgateway HTTP
        hostPort: 30000
      - containerPort: 30001 # Prometheus
        hostPort: 30001
      - containerPort: 30002 # Grafana
        hostPort: 30002
      - containerPort: 30003 # Kiali
        hostPort: 30003
      - containerPort: 30004 # Tracing
        hostPort: 30004
      - containerPort: 30005 # kube-ops-view
        hostPort: 30005
    networking:
      podSubnet: 10.10.0.0/16
      serviceSubnet: 10.100.0.0/24
    EOF
    
    # 설치 확인
    docker ps
    cat west-kubeconfig
    kubectl get node --kubeconfig=./west-kubeconfig
    kubectl get pod -A --kubeconfig=./west-kubeconfig
        
    # 노드에 기본 툴 설치
    docker exec -it west-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
    
    # (옵션) kube-ops-view
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30005 --set env.TZ="Asia/Seoul" --namespace kube-system --kubeconfig=./west-kubeconfig
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./west-kubeconfig
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:30005/#scale=1.5"
    open "http://localhost:30005/#scale=1.3"

    • east k8s 클러스터 배포
    # 
    kind create cluster --name east --image kindest/node:v1.23.17 --kubeconfig ./east-kubeconfig --config - <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 31000 # istio-ingrssgateway HTTP
        hostPort: 31000
      - containerPort: 31001 # Prometheus
        hostPort: 31001
      - containerPort: 31002 # Grafana
        hostPort: 31002
      - containerPort: 31003 # Kiali
        hostPort: 31003
      - containerPort: 31004 # Tracing
        hostPort: 31004
      - containerPort: 31005 # kube-ops-view
        hostPort: 31005
    networking:
      podSubnet: 10.20.0.0/16
      serviceSubnet: 10.200.0.0/24
    EOF
    
    # 설치 확인
    docker ps
    cat east-kubeconfig
    kubectl get node --kubeconfig=./east-kubeconfig
    kubectl get pod -A --kubeconfig=./east-kubeconfig
    
    # 노드에 기본 툴 설치
    docker exec -it east-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
    
    # (옵션) kube-ops-view
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=31005 --set env.TZ="Asia/Seoul" --namespace kube-system --kubeconfig=./east-kubeconfig
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view --kubeconfig=./east-kubeconfig
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:31005/#scale=1.5"
    open "http://localhost:31005/#scale=1.3"

    • kind docker network테스트용 PC(실제로는 컨테이너) 배포
    # kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
    docker network ls
    docker inspect kind
    
    # mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용
    docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
    혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
    docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
    docker ps
    
    # kind network 중 컨테이너(노드) IP(대역) 확인
    docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
    /mypc 172.18.0.100
    /east-control-plane 172.18.0.3
    /west-control-plane 172.18.0.2
    
    # 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
    docker exec -it mypc ping -c 1 172.18.0.2
    docker exec -it mypc ping -c 1 172.18.0.3
    docker exec -it mypc ping -c 1 west-control-plane
    docker exec -it mypc ping -c 1 east-control-plane
    
    #
    docker exec -it west-control-plane ping -c 1 east-control-plane
    docker exec -it east-control-plane ping -c 1 west-control-plane
    
    docker exec -it west-control-plane ping -c 1 mypc
    docker exec -it east-control-plane ping -c 1 mypc

    [ 실행 결과 한 눈에 보기 ]

    1. 클러스터 및 netshoot POD 설치

     

    2. 메탈 LB설치

     3. 서비스 설치 및 확인

     

    [실습 환경 구성] (편리성 설정) MetalLB 배포 - Github & Alias 설정(kwest, keast)

    더보기

    Step1. MetalLB 배포

    # MetalLB 배포
    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
      --kubeconfig=./west-kubeconfig
    
    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml \
      --kubeconfig=./east-kubeconfig
    
    # 확인
    kubectl get crd --kubeconfig=./west-kubeconfig                  
    kubectl get crd --kubeconfig=./east-kubeconfig           
           
    kubectl get pod -n metallb-system --kubeconfig=./west-kubeconfig
    kubectl get pod -n metallb-system --kubeconfig=./east-kubeconfig
    
    
    # IPAddressPool, L2Advertisement 설정
    cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f -
    apiVersion: metallb.io/v1beta1
    kind: IPAddressPool
    metadata:
      name: default
      namespace: metallb-system
    spec:
      addresses:
      - 172.18.255.101-172.18.255.120
    ---
    apiVersion: metallb.io/v1beta1
    kind: L2Advertisement
    metadata:
      name: default
      namespace: metallb-system
    spec:
      ipAddressPools:
      - default
    EOF
    
    cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -f -
    apiVersion: metallb.io/v1beta1
    kind: IPAddressPool
    metadata:
      name: default
      namespace: metallb-system
    spec:
      addresses:
      - 172.18.255.201-172.18.255.220
    ---
    apiVersion: metallb.io/v1beta1
    kind: L2Advertisement
    metadata:
      name: default
      namespace: metallb-system
    spec:
      ipAddressPools:
      - default
    EOF
    
    # 확인
    kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./west-kubeconfig
    kubectl get IPAddressPool,L2Advertisement -A --kubeconfig=./east-kubeconfig

    Step2. 샘플 애플리케이션 배포 후 확인  

    #
    cat << EOF | kubectl apply --kubeconfig=./west-kubeconfig -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
      - port: 80
        targetPort: 80
      type: LoadBalancer
    EOF
    
    #
    cat << EOF | kubectl apply --kubeconfig=./east-kubeconfig -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
      - port: 80
        targetPort: 80
      type: LoadBalancer
    EOF
    
    # 확인
    kubectl get deploy,pod,svc,ep --kubeconfig=./west-kubeconfig
    kubectl get deploy,pod,svc,ep --kubeconfig=./east-kubeconfig
    
    kubectl get svc nginx-service --kubeconfig=./west-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
    kubectl get svc nginx-service --kubeconfig=./east-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
    
    WNIP=$(kubectl get svc nginx-service --kubeconfig=./west-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    ENIP=$(kubectl get svc nginx-service --kubeconfig=./east-kubeconfig -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
    # 외부(mypc)에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다!
    docker exec -it mypc curl -s $WNIP
    docker exec -it mypc curl -s $WNIP -v -I
    
    docker exec -it mypc curl -s $ENIP
    docker exec -it mypc curl -s $ENIP -v -I
    
    
    # 확인 후 삭제
    kubectl delete deploy,svc --all --kubeconfig=./west-kubeconfig
    kubectl delete deploy,svc --all --kubeconfig=./east-kubeconfig

    Step3. 자주 사용하는 Alias 설정

    #
    alias kwest='kubectl --kubeconfig=./west-kubeconfig'
    alias keast='kubectl --kubeconfig=./east-kubeconfig'
    
    # 확인
    kwest get node
    keast get node

     

    [ 자주 사용하는 Alias ]

    alias kwest='kubectl --kubeconfig=./west-kubeconfig'
    alias keast='kubectl --kubeconfig=./east-kubeconfig'
    
    alias iwest='docker exec -it west-control-plane istioctl'
    alias ieast='docker exec -it east-control-plane istioctl'

     


    12.3.3 플러그인 CA 인증서 설정하기*  Configuring plug-in CA certificates

    더보기
    • 9장에서 워크로드 ID를 부트스트래핑(워크로드가 자신의 정체를 입증하는 서명된 인증서를 받는 방법)을 다룰 때는 설치할 때 인증서에 서명할 CA를 생성한다는 사실을 생략했다.
      In chapter 9, when we covered bootstrapping of workload identity—which is how workloads get a signed certificate that proves their identity—for simplicity we omitted the fact that Istio generates a CA to sign the certificates upon installation.
    • 이렇게 만들어진 CA는 이스티오를 설치한 네임스페이스에 istio-ca-secret 이라는 시크릿으로 저장되고, istiod 복제본들에게 공유된다.
      This generated CA is stored as a secret named istio-ca-secret in the Istio installation namespace and is shared with istiod replicas.
    • 이 기본 동작은 우리의 자체 CA를 사용하도록 변경할 수 있는데, 그러면 이스티오 CA가 CA를 새로 만드는 대신 이 CA 를 사용하게 된다.
      The default behavior can be overridden by plugging in our CA, which the Istio CA picks up instead of generating a new one.
    • 그렇게 하려면 CA 인증서를 이스티오를 설치한 네임스페이스인 istio-system 에 cacerts 라는 시크릿으로 저장해야 하며, 다음과 같은 데이터를 포함해야 한다.

     

    같은 루트가 서명한 중간 CA 인증서 사용하기
    • ca.cert.pem : 중간 CA의 인증서 The intermediate CA’s certificate.
    • ca-key.pem : 중간 CA의 개인 키 The intermediate CA’s private key.
    • root-cert.pem : 중간 CA를 발급한 루트 CA의 인증서. 루트 CA는 중간 CA들의 인증서를 검증하며, 이것이 클러스터 간 상호 신뢰의 핵심이다. The root CA’s certificate that issued the intermediate CA. The root CA validates the certificates issued by any of its intermediate CAs, which is key for mutual trust across clusters.
    • cert-chain.pem : 중간 CA의 인증서와 루트 인증서를 이어 붙인 신뢰 체인 The concatenation of the intermediate CA’s certificate and the root CA certificate that forms the trust chain.
    • (참고) 인증서 구조
      • 루트 CA: 모든 클러스터가 신뢰하는 최상위 인증서.
      • 중간 CA: 각 클러스터별로 발급되어 워크로드 인증서 서명에 사용.
      • 인증서 체인 : 신뢰 체인을 구성하여 클라이언트가 인증서를 검증할 수 있도록 함.

    ▶ (참고) SSL과 인증서 구조 이해하기 : 인증서 계층 구조 설명 - Blog

    cacerts 시크릿은 루트 CA의 공개 키와 중간 CA의 공개 키 및 비밀 키로 구성된다. 루트 CA의 비밀 키는 클러스터 외부에 안전하게 저장된다.

     

    • 저자가 제공하는 ./ch12/scripts/generate-certificates.sh 스크립트를 사용하면 중간 CA와 루트 CA는 ./ch12/certs 디렉터리에 만들어진다.
    • 이 스크립트는 루트 CA를 만들고, 이 CA를 사용해 중간 CA 2개에 서명한다. 그 결과 신뢰가 같은 중간 CA가 만들어진다.

     플러그인 CA 인증서 적용하기 APPLYING PLUG-IN CA CERTIFICATES 

    1) ./ch12/scripts/generate-certificates.sh 스크립트 : Smallstep - Github , Install

    더보기
    # cat ./ch12/scripts/generate-certificates.sh
    #!/bin/bash
    
    set -ex
    
    cert_dir=`dirname "$BASH_SOURCE"`/../certs
    
    echo "Clean up contents of dir './chapter12/certs'"
    rm -rf ${cert_dir}
    
    echo "Generating new certificates"
    mkdir -p ${cert_dir}/west-cluster
    mkdir -p ${cert_dir}/east-cluster
    
    # step CLI 설치 확인 : step CLI가 설치되어 있지 않으면 에러 출력 후 종료.
    ## macOS : brew install step
    if ! [ -x "$(command -v step)" ]; then 
      echo 'Error: Install the smallstep cli (https://github.com/smallstep/cli)'
      exit 1
    fi
    
    step certificate create root.istio.in.action ${cert_dir}/root-cert.pem ${cert_dir}/root-ca.key \
      --profile root-ca --no-password --insecure --san root.istio.in.action \
      --not-after 87600h --kty RSA 
    
    step certificate create west.intermediate.istio.in.action ${cert_dir}/west-cluster/ca-cert.pem ${cert_dir}/west-cluster/ca-key.pem --ca ${cert_dir}/root-cert.pem --ca-key ${cert_dir}/root-ca.key --profile intermediate-ca --not-after 87600h --no-password --insecure --san west.intermediate.istio.in.action --kty RSA 
    step certificate create east.intermediate.istio.in.action ${cert_dir}/east-cluster/ca-cert.pem ${cert_dir}/east-cluster/ca-key.pem --ca ${cert_dir}/root-cert.pem --ca-key ${cert_dir}/root-ca.key --profile intermediate-ca --not-after 87600h --no-password --insecure --san east.intermediate.istio.in.action --kty RSA 
    
    cat ${cert_dir}/west-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/west-cluster/cert-chain.pem
    cat ${cert_dir}/east-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/east-cluster/cert-chain.pem

    2) 기본 정보 확인

    더보기
    #
    tree ch12/certs 
    ch12/certs
    ├── east-cluster
    │   ├── ca-cert.pem    # 중간 CA 인증서
    │   ├── ca-key.pem     # 중간 CA 개인zl
    │   └── cert-chain.pem # 인증서 체인
    ├── root-ca.key        # 루트 인증서
    ├── root-cert.pem      # 루트 개인키
    └── west-cluster
        ├── ca-cert.pem
        ├── ca-key.pem
        └── cert-chain.pem
    
    # (참고) 인증서 체인 생성
    ## 중간 CA 인증서(ca-cert.pem)와 루트 CA 인증서(root-cert.pem)를 결합 -> 결과는 {west/east}-cluster/cert-chain.pem 에 저장
    cat ${cert_dir}/west-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/west-cluster/cert-chain.pem
    cat ${cert_dir}/east-cluster/ca-cert.pem ${cert_dir}/root-cert.pem > ${cert_dir}/east-cluster/cert-chain.pem
    
    
    # 인증서 계층 구조
    root.istio.in.action (Root CA)
       │
       └── east.intermediate.istio.in.action (Intermediate CA)
    
    
    # 루트 CA 인증서 확인
    openssl x509 -in ch12/certs/root-cert.pem -noout -text
    ...
            Issuer: CN=root.istio.in.action
            Validity
                Not Before: Jun 28 14:11:35 2022 GMT
                Not After : Jun 25 14:11:35 2032 GMT
            Subject: CN=root.istio.in.action
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE, pathlen:1
    ...
    
    #
    openssl x509 -in ch12/certs/east-cluster/ca-cert.pem -noout -text
    ...
            Issuer: CN=root.istio.in.action
            Validity
                Not Before: Jun 28 14:11:35 2022 GMT
                Not After : Jun 25 14:11:35 2032 GMT
            Subject: CN=east.intermediate.istio.in.action
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE, pathlen:0 # 이 중간 CA는 추가적인 하위 CA를 만들 수 없음
    ...
    
    openssl x509 -in ch12/certs/east-cluster/cert-chain.pem -noout -text
            Issuer: CN=root.istio.in.action
            Validity
                Not Before: Jun 28 14:11:35 2022 GMT
                Not After : Jun 25 14:11:35 2032 GMT
            Subject: CN=east.intermediate.istio.in.action
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE, pathlen:0
    
    #
    openssl x509 -in ch12/certs/west-cluster/ca-cert.pem -noout -text
    openssl x509 -in ch12/certs/west-cluster/cert-chain.pem -noout -text

     

     

     

     

    3) (옵션) step 로 인증서 생성 시

    더보기
    # step CLI 설치
    # macOS : brew install step
    
    # 스크립트 실행
    ./ch12/scripts/generate-certificates.sh
    ...
    
    # 생성 결과 확인
    tree ch12/certs 
    ...

    4) istio-system 네임스페이스를 만든 후 인증서를 cacerts 라는 시크릿으로 배포하여 각 클러스터에 중간 CA를 만들어두기

    더보기
    # west-cluster 용 인증서 설정하기
    kwest create namespace istio-system
    kwest create secret generic cacerts -n istio-system \
    --from-file=ch12/certs/west-cluster/ca-cert.pem \
    --from-file=ch12/certs/west-cluster/ca-key.pem \
    --from-file=ch12/certs/root-cert.pem \
    --from-file=ch12/certs/west-cluster/cert-chain.pem
    
    # east-cluster 용 인증서 설정하기
    keast create namespace istio-system
    keast create secret generic cacerts -n istio-system \
    --from-file=ch12/certs/east-cluster/ca-cert.pem \
    --from-file=ch12/certs/east-cluster/ca-key.pem \
    --from-file=ch12/certs/root-cert.pem \
    --from-file=ch12/certs/east-cluster/cert-chain.pem
    
    # 확인
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get ns istio-system --kubeconfig=./$i-kubeconfig; echo; done
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret cacerts  -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl view-secret cacerts -n istio-system --all --kubeconfig=./$i-kubeconfig; echo; done

     

     

    • 플러그인 인증서가 설정되면 이스티오 컨트롤 플레인을 설치할 수 있다. 컨트롤 플레인은 플러그인 CA 인증서(사용자가 정의한 중간 인증서)를 갖고 워크로드 인증서에 서명한다.
      With the plug-in certificates configured, we can install the Istio control plane, which picks up the plug-in CA certificates (the user-defined intermediate certificates) to sign workload certificates.

    12.3.4 각 클러스터에 (Istiod) 컨트롤 플레인 설치하기* Installing the control planes in each cluster

    ▶ 들어가며

    더보기
    • 이스티오의 컨트롤 플레인을 설치하기에 앞서, 각 클러스터네트워크 메타데이터를 더하자.
      network metadata for each cluster
    • 네트워크 메타데이터를 통해 이스티오는 토폴로지 정보를 사용할 수 있고, 그 정보에 기반해 워크로드를 설정할 수 있다. Network metadata enables Istio to utilize the topology information and configure workloads based on it.
    • 따라서 워크로드는 지역 정보를 사용해 트래픽을 라우팅할 때 가까운 워크로드를 우선할 수 있다.
      Thus workloads can use locality information and prioritize routing traffic to workloads in close proximity.
    • 이스티오가 네트워크 토폴로지를 이해할 때 얻는 또 다른 이점은 워크로드가 다른 네트워크에 있는 원격 클러스터의 워크로드로 트래픽을 라우팅할 때 east-west 게이트웨이를 사용하도록 설정한다는 것이다.
      Another benefit when Istio understands the network topology is that it configures workloads to use east-west gateways when routing traffic to workloads in remote clusters that are in different networks.

    클러스터 간 연결을 위해 네트워크에 레이블 붙이기

      -  LABELING NETWORKS FOR CROSS-CLUSTER CONNECTIVITY

    더보기
    • 네트워크 토롤로지는 이스티오를 설치할 때 MeshNetwork 설정을 사용해 설정할 수 있다 - Docs
    • 그러나 이것은 드문 고급 사용 사례에서나 유지하는 레거시 설정이며, 더 간단한 방법은 이스티오를 설치한 네임스페이스에 네트워크 토폴로지 정보레이블로 붙이는 것이다.
    • 우리의 경우 이스티오를 설치한 네임스페이스는 istio-system 이고, west-cluster 의 네트워크는 west-network 이다.
    • 그러므로 west-clusteristio-system 에 topology.istio.io/network=**west-network** 레이블을 붙인다. east 도 설정하자.
    #
    kwest label namespace istio-system topology.istio.io/network=west-network
    keast label namespace istio-system topology.istio.io/network=east-network
    
    # 확인
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get ns istio-system --show-labels --kubeconfig=./$i-kubeconfig; echo; done

     

    • 이 레이블들을 사용해 이스티오는 네트워크 토폴로지를 이해하고, 그 이해를 바탕으로 워크로드 설정법을 결정한다.

     

    IstioOperator 리소스를 사용해 컨트롤 플레인 설치하기*

     - INSTALLING THE CONTROL PLANES USING ISTIOOPERATOR RESOURCES & Alias 설정(iwest, ieast)

     

    1) 많이 수정해야 하므로, IstioOperator 리소스를 사용해 west-cluster 의 이스티오 설치를 정의하자.

    더보기
    # cat ./ch12/controlplanes/cluster-west.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: istio-controlplane
      namespace: istio-system
    spec:
      profile: demo
      components:
        egressGateways: # 이그레스 게이트웨이 비활성화
        - name: istio-egressgateway
          enabled: false
      values:
        global:
          meshID: usmesh # 메시 이름
          multiCluster:
            clusterName: west-cluster # 멀티 클러스터 메시 내부의 클러스터 식별자
          network: west-network # 이 설치가 이뤄지는 네트워크
    # cat ./ch12/controlplanes/cluster-east.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: istio-controlplane
      namespace: istio-system
    spec:
      profile: demo
      components:
        egressGateways: # 이그레스 게이트웨이 비활성화
        - name: istio-egressgateway
          enabled: false
      values:
        global:
          meshID: usmesh # 메시 이름
          multiCluster:
            clusterName: east-cluster
          network: east-network

     

    2) west-cluster 를 성공적으로 설치한 후, east-cluster 에 복제한 컨트롤 플레인을 설치할 수 있다.

    더보기
    # west-control-plane 진입 후 설치 진행
    docker exec -it west-control-plane bash
    -----------------------------------
    # istioctl 설치
    export ISTIOV=1.17.8
    echo 'export ISTIOV=1.17.8' >> /root/.bashrc
    
    curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
    cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
    
    # IstioOperator 파일 작성
    cat << EOF > west-istio.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: istio-controlplane
      namespace: istio-system
    spec:
      profile: demo
      components:
        egressGateways:
        - name: istio-egressgateway
          enabled: false
      values:
        global:
          meshID: usmesh
          multiCluster:
            clusterName: west-cluster
          network: west-network
    EOF
    
    # 컨트롤 플레인 배포
    istioctl install -f west-istio.yaml --set values.global.proxy.privileged=true -y
    
    # 보조 도구 설치
    kubectl apply -f istio-$ISTIOV/samples/addons
    
    # 빠져나오기
    exit
    -----------------------------------
    
    # 설치 확인 : istiod, istio-ingressgateway, crd 등
    kwest get istiooperators -n istio-system -o yaml
    ...
            meshID: usmesh
            meshNetworks: {}
            mountMtlsCerts: false
            multiCluster:
              clusterName: west-cluster
              enabled: false
            network: west-network
    ...
    
    kwest get all,svc,ep,sa,cm,secret,pdb -n istio-system
    kwest get secret -n istio-system cacerts -o json # 미리 만들어둔 인증서/키 확인
    
    # istio-ingressgateway 서비스 : NodePort 변경 및 nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
    kwest patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
    kwest patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
    kwest describe svc -n istio-system istio-ingressgateway
    
    # NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
    kwest patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
    kwest patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
    kwest patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
    kwest patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
    
    # Prometheus 접속 : envoy, istio 메트릭 확인
    open http://127.0.0.1:30001
    
    # Grafana 접속
    open http://127.0.0.1:30002
    
    # Kiali 접속 : NodePort
    open http://127.0.0.1:30003
    
    # tracing 접속 : 예거 트레이싱 대시보드
    open http://127.0.0.1:30004

     

    1. Istiooperator  구성확인

     2. istio-ingressgateway Nodeport 지정 확인

    3) east-cluster 용 IstioOperator 정의는 west-cluster 용과 비교했을 때 클러스터 이름과 네트워크만 다르다.

    더보기

    그리고 두 컨트롤 플레인이 동일한 메시를 형성하길 원하므로, west-cluster 를 설치할 때 사용했던 meshID 를 그대로 지정한다.

    # west-control-plane 진입 후 설치 진행
    docker exec -it east-control-plane bash
    -----------------------------------
    # istioctl 설치
    export ISTIOV=1.17.8
    echo 'export ISTIOV=1.17.8' >> /root/.bashrc
    
    curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
    cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
    
    # IstioOperator 파일 작성
    cat << EOF > east-istio.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: istio-controlplane
      namespace: istio-system
    spec:
      profile: demo
      components:
        egressGateways:
        - name: istio-egressgateway
          enabled: false
      values:
        global:
          meshID: usmesh
          multiCluster:
            clusterName: east-cluster
          network: east-network
    EOF
    
    # 컨트롤 플레인 배포
    istioctl install -f east-istio.yaml --set values.global.proxy.privileged=true -y
    
    # 보조 도구 설치
    kubectl apply -f istio-$ISTIOV/samples/addons
    
    # 빠져나오기
    exit
    -----------------------------------
    
    # 설치 확인 : istiod, istio-ingressgateway, crd 등
    keast get istiooperators -n istio-system -o yaml
    ...
            meshID: usmesh
            meshNetworks: {}
            mountMtlsCerts: false
            multiCluster:
              clusterName: east-cluster
              enabled: false
            network: east-network
    ...
    
    keast get all,svc,ep,sa,cm,secret,pdb -n istio-system
    keast get secret -n istio-system cacerts -o json # 미리 만들어둔 인증서/키 확인
    
    
    # NodePort 변경 및 nodeport 31001~31003으로 변경 : prometheus(31001), grafana(31002), kiali(31003), tracing(31004)
    keast patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 31001}]}}'
    keast patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 31002}]}}'
    keast patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 31003}]}}'
    keast patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 31004}]}}'
    
    # Prometheus 접속 : envoy, istio 메트릭 확인
    open http://127.0.0.1:31001
    
    # Grafana 접속
    open http://127.0.0.1:31002
    
    # Kiali 접속
    open http://127.0.0.1:31003
    
    # tracing 접속 : 예거 트레이싱 대시보드
    open http://127.0.0.1:31004

     

     

    4) istioctl alias 설정 (iwest, ieast) 및 미리 만들어둔 인증서 적용 확인

    더보기
    #
    docker exec -it west-control-plane istioctl -h
    docker exec -it east-control-plane istioctl -h
    
    #
    alias iwest='docker exec -it west-control-plane istioctl'
    alias ieast='docker exec -it east-control-plane istioctl'
    
    #
    iwest proxy-status
    NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                      VERSION
    istio-ingressgateway-5db74c978c-9qmt9.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-5585445f4c-zf8g6     1.17.8
    
    ieast proxy-status
    NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
    istio-ingressgateway-7f6f8f8d99-4mlp5.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-vp4zg     1.17.8
    
    #
    iwest proxy-config secret deploy/istio-ingressgateway.istio-system
    RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
    default           Cert Chain     ACTIVE     true           80349990876331570640723939723244297816      2025-05-17T08:47:28Z     2025-05-16T08:45:28Z
    ROOTCA            CA             ACTIVE     true           100900981840825465297757884708490534092     2032-06-25T14:11:35Z     2022-06-28T14:11:35Z
    
    iwest proxy-config secret deploy/istio-ingressgateway.istio-system -o json
    ...
    
    # 아래는 default 에 inlineBytes 값을 decode64 -d 시 3개의 인증서 정보 출력 후 각 개별 인증서를 openssl x509 -in Y.pem -noout -text 로 출력 확인 
    ## (1) 사용자 인증서
    -----BEGIN CERTIFICATE-----
    MIIDdjCCAl6gAwIBAgIQPHLYaJhiIjAwJkg6cAVeWDANBgkqhkiG9w0BAQsFADAs
    ...
    5xYNi7u0APTzE1swNbx2TF5eeTsFvYcbFh56ahLp0izGkahOv97bEgnZdeTsLRyH
    K+5+1ZdJ2n8CuxoSY+FXUlMDwGjdvCXAKBM=
    -----END CERTIFICATE-----
    
            Issuer: CN=west.intermediate.istio.in.action
            Validity
                Not Before: May 16 08:45:28 2025 GMT
                Not After : May 17 08:47:28 2025 GMT
            Subject: 
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Digital Signature, Key Encipherment
                X509v3 Extended Key Usage: 
                    TLS Web Server Authentication, TLS Web Client Authentication
                X509v3 Basic Constraints: critical
                    CA:FALSE
                X509v3 Authority Key Identifier: 
                    D3:83:9A:3A:51:D9:03:62:35:8F:6A:A4:DA:99:88:BB:74:70:4F:33
                X509v3 Subject Alternative Name: critical
                    URI:spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account
    
    
    ## (2) 중간 CA 루트 인증서
    -----BEGIN CERTIFICATE-----
    MIIDPDCCAiSgAwIBAgIRAMkJ23sotpqiiWps+38Df/YwDQYJKoZIhvcNAQELBQAw
    ...
    usSjiM6KR77xogslodbQw4QQG+w5HQOwMa1k8WTCNrplxdsnaQJjdqUwCdixicq2
    DeHuSkz4cykAI/NWc2cZIw==
    -----END CERTIFICATE-----
    
            Issuer: CN=root.istio.in.action
            Validity
                Not Before: Jun 28 14:11:35 2022 GMT
                Not After : Jun 25 14:11:35 2032 GMT
            Subject: CN=west.intermediate.istio.in.action
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE, pathlen:0
    
    ## (3) 최상위 루트 인증서
    -----BEGIN CERTIFICATE-----
    MIIDDTCCAfWgAwIBAgIQS+jSffZX7itohjyrautczDANBgkqhkiG9w0BAQsFADAf
    ...
    3fRtDApNHbbmi7WXrM+pG4D+Buk2FUEHJVpu16Ch2K2vpRzpkliqes+T/5E92uY9
    ob7MBgt61g4VZ/p8+RMJWYw=
    -----END CERTIFICATE-----
    
            Issuer: CN=root.istio.in.action
            Validity
                Not Before: Jun 28 14:11:35 2022 GMT
                Not After : Jun 25 14:11:35 2032 GMT
            Subject: CN=root.istio.in.action
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE, pathlen:1
    
    #
    iwest proxy-config listener deploy/istio-ingressgateway.istio-system
    iwest proxy-config route deploy/istio-ingressgateway.istio-system
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system
    iwest proxy-config secret deploy/istio-ingressgateway.istio-system
    iwest proxy-config bootstrap deploy/istio-ingressgateway.istio-system
    iwest proxy-config ecds deploy/istio-ingressgateway.istio-system
    
    #
    ieast proxy-config listener deploy/istio-ingressgateway.istio-system
    ieast proxy-config route deploy/istio-ingressgateway.istio-system
    ieast proxy-config cluster deploy/istio-ingressgateway.istio-system
    ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system
    ieast proxy-config secret deploy/istio-ingressgateway.istio-system
    ieast proxy-config bootstrap deploy/istio-ingressgateway.istio-system
    ieast proxy-config ecds deploy/istio-ingressgateway.istio-system

    [ 실행 결과 한 눈에 보기 ]

     

    1. proxy-status 확인

     

    2. proxy-config 에서 secret 정보 확인 후, certification Chain 정보를 2단계에 걸쳐서 decoding 및 내용을 확인해 보자!!

    -  iwest proxy-config secret deploy/istio-ingressgateway.istio-system -o json  결과에서 발췌 -> 개별 pem 저장

     

    1단계) echo "MII ~~~ " | base64 -d

     2단계) base64로 decoding 되어 나온 인증서를 "openssl x509 -in Y.pem -noout -text" 로 출력하여 확인!!

     3. istio-config  구성요소 확인


    두 클러스터 모두에 컨트롤 플레인을 설치하면 개별 메시 2개를 갖게 되는데, 각자 로컬 서비스만 찾는 istiod 복제복 하나와 수신 트래픽용 게이트웨이 하나를 실행한다. (그림 12.11 참조)

    메시의 현재 설정

     

    • 메시에는 다음 절에서 설정할 클러스터 간 워크로드 디스커버리 및 연결이 없다. 그러나 더 진행하기 전에 각 클러스터에 일부 워크로드를 실행해보자.
    • 이 워크로드는 클러스터 간 디스커버리와 연결성이 올바르게 준비됐는지 확인하는 데 유용할 것이다.

    두 클러스터 모두에 워크로드 실행하기 Running workloads on both clusters

    더보기
    • 컨트롤 플레인을 설치했으니 몇 가지 워크로드를 실행해보자.
    • west-cluster 에서는 webapp 을 배포한다.
    #
    kwest create ns istioinaction
    kwest label namespace istioinaction istio-injection=enabled
    kwest -n istioinaction apply -f ch12/webapp-deployment-svc.yaml
    kwest -n istioinaction apply -f ch12/webapp-gw-vs.yaml
    kwest -n istioinaction apply -f ch12/catalog-svc.yaml # Stub catalog service to which webapp makes request
    cat ch12/catalog-svc.yaml
    piVersion: v1
    kind: Service
    metadata:
      labels:
        app: catalog
      name: catalog
    spec:
      ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 3000
      selector:
        app: catalog
    
    
    # 확인
    kwest get deploy,pod,svc,ep -n istioinaction
    kwest get svc,ep catalog -n istioinaction
    NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.100.2.43   <none>        80/TCP    2m
    
    NAME                ENDPOINTS   AGE
    endpoints/catalog   <none>      2m
    
    kwest get gw,vs,dr -A
    NAMESPACE       NAME                                            AGE
    istioinaction   gateway.networking.istio.io/coolstore-gateway   16s
    
    NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
    istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   16s
    
    #
    iwest proxy-status
    NAME                                                   CLUSTER          CDS        LDS        EDS        RDS        ECDS         ISTIOD                      VERSION
    istio-ingressgateway-5db74c978c-9j96n.istio-system     west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-zcvp4     1.17.8
    webapp-5c8b4fff64-tfs8m.istioinaction                  west-cluster     SYNCED     SYNCED     SYNCED     SYNCED     NOT SENT     istiod-5585445f4c-zcvp4     1.17.8
    
    # endpoint 에 IP 는 10.10.0.0/16 대역들 다수 확인
    for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
    catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS 
    
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog

     

     * 현재, west cluster에는 webapp 서비스만 배포되어 있다!!

     

    ** east-cluster 에 배포되어 있는 catalog 서비스는 찾아갈 길이 없다!!

     


    • 그런데 east-cluster 에서만 실행하려고 하는 catalog 워크로드용 서비스가 필요한 이유는 무엇일까?
    • 이 서비스가 없으면 webapp 컨테이너가 FQDN을 IP 주소로 해석할 수 없고, 따라서 트래픽이 애플리케이션을 떠나 프록시로 리다이렉트되는 지점에 도달하기도 전에 요청이 실패하기 때문이다.
      The reason for adding this stub service is that in its absence, the webapp container cannot resolve the fully qualified domain name (FQDN) to any IP address, and thus the request would fail prior to reaching the point where traffic leaves the application and is redirected to the proxy.
    • 스텁 catalog 서비스를 추가함으로써, FQDN은 서비스 클러스터 IP로 해석되고, 애플리케이션이 트래픽을 시작해 실제 엔보이 설정을 갖고 클러스터 사이의 라우팅을 처리하는 엔보이 프록시로 리다이렉트될 수 있다.
      By adding a stub catalog service, the FQDN is resolved to the service cluster IP and traffic is initiated by the application, which makes it possible for it to be redirected to the Envoy proxy where the actual Envoy configuration exists and handles the cross-cluster routing.
    • 이는 이스티오 커뮤니티가 향후 버전에서 DNS 프록시개선할 때 해결할 예정인 에지 케이스로, 다음 장에서 살펴볼 주제다. This is an edge case that the Istio community plans to fix in upcoming versions when the DNS proxy is enhanced further, which is a topic we will examine in the next chapter.

     

    [ 참고 ] istio xDS 에 관하여

    더보기
    출처:  https://devlos.tistory.com/100  [Devlos Archive:티스토리]

     

    Configuration (좌측):여러 소스에서 수집된 설정 데이터입니다. 이는 다양한 네트워크, 보안, 라우팅, 엔드포인트 정보를 포함할 수 있습니다. 컨트롤 플레인은 이러한 설정 데이터를 모아서 Envoy에 전달합니다.

    Control Plane (중앙):Envoy가 사용할 설정 정보를 수집하고 관리하는 역할을 합니다. 컨트롤 플레인은 xDS API를 통해 Envoy에게 다양한 설정 데이터를 제공합니다. 여기서 각 API는 서로 다른 유형의 설정을 제공하며, 이를 통해 Envoy가 트래픽을 효과적으로 제어할 수 있습니다.

    xDS APIs (우측):컨트롤 플레인은 다양한 xDS API를 통해 Envoy로 데이터를 전송하며, 각 API의 역할은 다음과 같습니다

    • SDS (Secret Discovery Service): Envoy가 TLS 인증서와 같은 보안 정보를 동적으로 가져오는 서비스
    • EDS (Endpoint Discovery Service): 백엔드 서비스의 엔드포인트 정보를 Envoy에게 제공
    • CDS (Cluster Discovery Service): 클러스터 정보를 전달합니다. 클러스터는 백엔드 서버의 그룹으로, Envoy는 이를 통해 어떤 서버로 트래픽을 보낼지 결정
    • SRDS (Scoped Route Discovery Service): 특정 범위 내에서 라우팅 규칙을 제공하여,Envoy가 요청을 적절한 경로로 전달
    • LDS (Listener Discovery Service): Envoy의 리스너 설정을 관리합니다. 리스너는 네트워크 요청을 수신하는 엔티티
    • ECDS (Extension Configuration Discovery Service): Envoy의 동작을 확장하는 데 사용되는 추가적인 설정 데이터를 제공
    • RDS (Route Discovery Service): 트래픽 라우팅 규칙을 제공합니다. Envoy는 이를 통해 클라이언트 요청을 적절한 백엔드 서비스로 라우팅
    • VHDS (Virtual Host Discovery Service): 가상 호스트에 대한 라우팅 정보를 제공
    • RTDS (Runtime Discovery Service): 런타임 중에 변경 가능한 설정 값을 동적으로 제공하는 서비스


    GRPC (Envoy컨테이너와의 통신):앞서 설명했듯이 컨트롤 플레인은 Envoy와 gRPC를 통해 통신합니다. gRPC는 고성능 원격 프로시저 호출(Remote Procedure Call) 시스템으로, Envoy는 이 프로토콜을 통해 컨트롤 플레인으로부터 동적으로 설정을 받아 트래픽을 제어 

    • east-cluster 에 catalog 서비스를 배포하자.
    더보기
    #
    keast create ns istioinaction
    keast label namespace istioinaction istio-injection=enabled
    keast -n istioinaction apply -f ch12/catalog.yaml
    cat ch12/catalog.yaml
    
    
    # 확인
    keast get deploy,pod,svc,ep -n istioinaction
    keast get svc,ep catalog -n istioinaction
    keast get gw,vs,dr -A # 없음
    
    #
    ieast proxy-status
    NAME                                                   CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
    catalog-6cf4b97d-ff7lq.istioinaction                   east-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-85976468f-9q8ck     1.17.8
    istio-ingressgateway-7f6f8f8d99-fjn92.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-9q8ck     1.17.8
    
    # endpoint 에 IP 는 10.20.0.0/16 대역들 다수 확인
    for i in listener route cluster endpoint; do echo ">> k8s cluster : east - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
    catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS
    
    ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
    10.20.0.15:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

     

     

     

    ☞ 클러스터 2개를 각자 연결해야 하는 워크로드가 있는 지금을 시작점이라고 해보자. 그러나 클러스터 간 워크로드 디스커버리 없이는 사이드카 프록시에 반대 클러스터의 워크로드가 설정되지 않는다. 그러므로 다음 단계는 클러스터 간 디스커버리를 활성화해보자.


    12.3.5 클러스터 간 워크로드 디스커버리 활성화하기 Enabling cross-cluster workload discovery

    ▶ 들어가며

    더보기
    • 이스티오가 원격 클러스터에서 정보를 쿼리하기 위해 인증하려면, ID를 정의하는 서비스 어카운트와 권한에 대한 롤 바인딩 role binding 이 필요하다.
    • 이런 이유로 이스티오는 설치 시 istio-reader-service-account 라는 서비스 어카운트최소 권한으로 생성한다.
    • 이 서비스 어카운트는 다른 컨트롤 플레인이 자신을 인증하고 서비스나 엔드포인트 같은 워크로드 관련 정보를 조회하는 데 사용할 수 있다.
    • 그러나 서비스 어카운트 토큰과 원격 클러스터로 보안 커넥션을 시작할 인증서를 상대 클러스터가 사용할 수 있게 해야 한다.

    원격 클러스터 접근용 시크릿 만들기 CREATING THE SECRETS FOR REMOTE CLUSTER ACCESS

    • istioctl 에는 create-remote-secret 명령어가 있는데, 기본 istio-reader-service-account 서비스 어카운트를 사용해 원격 클러스터 접근용으로 시크릿을 만드는 것이다.
    • 시크릿을 만들 때는 이스티오를 설치할 때 IstioOperator 에서 사용한 클러스터 이름을 지정하는 것이 중요하다 (east-cluster 및 west-cluster 값은 앞서 ‘IstioOperator 리소스를 사용해 컨트롤 플레인 설치하기’ 절 참조).

    a) 원격 클러스터에 접근하기 위한 설정의 식별자로 클러스터 이름이 어떻게 사용되는지에는 주의를 기울이자*

    더보기
    #
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get sa -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    
    # east
    keast describe sa -n istio-system istio-reader-service-account      
    ...
    Mountable secrets:   istio-reader-service-account-token-566n2
    Tokens:              istio-reader-service-account-token-566n2
    
    keast get sa -n istio-system istio-reader-service-account -o yaml
    ...
      name: istio-reader-service-account
      namespace: istio-system
      resourceVersion: "16805"
      uid: 40fa07c3-2e49-4003-a09b-afccdbcec7a2
    secrets:
    - name: istio-reader-service-account-token-566n2
    
    keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}'
    eirsa=$(keast get sa -n istio-system istio-reader-service-account -o jsonpath='{.secrets[0].name}')
    keast get secret -n istio-system $eirsa
    keast get secret -n istio-system $eirsa -o json
    {
        "apiVersion": "v1",
        "data": {
            "ca.crt": "LS0t,,,==",
            "namespace": "aXN0aW8tc3lzdGVt", # istio-system
            "token": "ZXl...VN2Zw=="
        },
    ...
    
    kubectl rolesum istio-reader-service-account -n istio-system --kubeconfig=./east-kubeconfig
    ...
    Policies:
    
    • [CRB] */istio-reader-clusterrole-istio-system ⟶  [CR] */istio-reader-clusterrole-istio-system
      Resource                                                                                         Name  Exclude  Verbs  G L W C U P D DC  
      *.[config.istio.io,security.istio.io,networking.istio.io,authentication.istio.io,rbac.istio.io]  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      customresourcedefinitions.apiextensions.k8s.io                                                   [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      endpoints                                                                                        [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      endpointslices.discovery.k8s.io                                                                  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      namespaces                                                                                       [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      nodes                                                                                            [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      pods                                                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      replicasets.apps                                                                                 [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      replicationcontrollers                                                                           [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      secrets                                                                                          [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      serviceexports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✔ ✖ ✖ ✔ ✖   
      serviceimports.multicluster.x-k8s.io                                                             [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      services                                                                                         [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
      subjectaccessreviews.authorization.k8s.io                                                        [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
      tokenreviews.authentication.k8s.io                                                               [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
      workloadentries.networking.istio.io                                                              [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
    
    
    • [CRB] */istio-reader-istio-system ⟶  [CR] */istio-reader-istio-system
      Resource                                                                                         Name  Exclude  Verbs  G L W C U P D DC  
      *.[config.istio.io,security.istio.io,networking.istio.io,authentication.istio.io,rbac.istio.io]  [*]     [-]     [-]   ✔ ✔ ✔ ✖ ✖ ✖ ✖ ✖   
    ...
    
    keast auth can-i --list
    keast auth can-i --as=system:serviceaccount:istio-system:istio-reader-service-account --list
    Resources                                        Non-Resource URLs                     Resource Names   Verbs
    tokenreviews.authentication.k8s.io               []                                    []               [create]
    selfsubjectaccessreviews.authorization.k8s.io    []                                    []               [create]
    selfsubjectrulesreviews.authorization.k8s.io     []                                    []               [create]
    subjectaccessreviews.authorization.k8s.io        []                                    []               [create]
    serviceexports.multicluster.x-k8s.io             []                                    []               [get list watch create delete]
    endpoints                                        []                                    []               [get list watch]
    namespaces                                       []                                    []               [get list watch]
    nodes                                            []                                    []               [get list watch]
    pods                                             []                                    []               [get list watch]
    replicationcontrollers                           []                                    []               [get list watch]
    secrets                                          []                                    []               [get list watch]
    services                                         []                                    []               [get list watch]
    ...
    
    #
    ieast x create-remote-secret --help
    Create a secret with credentials to allow Istio to access remote Kubernetes apiservers
    
    ieast x create-remote-secret --name="east-cluster"
    # This file is autogenerated, do not edit.
    apiVersion: v1
    kind: Secret
    metadata:
      annotations:
        networking.istio.io/cluster: east-cluster
      creationTimestamp: null
      labels:
        istio/multiCluster: "true" # 이 레이블이 true로 설정된 시크릿은 이스티오의 컨트롤 플레인이 새 클러스터를 등록하기 위해 감시한다
      name: istio-remote-secret-east-cluster
      namespace: istio-system
    stringData:
      east-cluster: |
        apiVersion: v1
        clusters:
        - cluster: # 아래 'certificate-authority-data'는 이 클러스터에 보안 커넥션을 시작하는 데 사용하는 CA
            certificate-authority-data: LS0tLS1CR....
            server: https://east-control-plane:6443
          name: east-cluster
        contexts:
        - context:
            cluster: east-cluster
            user: east-cluster
          name: east-cluster
        current-context: east-cluster
        kind: Config
        preferences: {}
        users:
        - name: east-cluster
          user: # 아래 'token'은 서비스 어카운트의 ID를 나타내는 토큰
            token: eyJhb...
    ---
    
    ## certificate-authority-data 정보 : k8s 루트 인증서
    openssl x509 -in YYY -noout -text
    ...
            Issuer: CN=kubernetes
            Validity
                Not Before: May 16 05:13:20 2025 GMT
                Not After : May 14 05:13:20 2035 GMT
            Subject: CN=kubernetes
            ...
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Digital Signature, Key Encipherment, Certificate Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE
    
    ## user.token 정보 : 
    jwt decode YYY
    Token header
    ------------
    {
      "alg": "RS256",
      "kid": "oyrLHJhI-1aEvcPmbnFZEI6avASPC3fJttjoEaXt-iU"
    }
    
    Token claims
    ------------
    {
      "iss": "kubernetes/serviceaccount",
      "kubernetes.io/serviceaccount/namespace": "istio-system",
      "kubernetes.io/serviceaccount/secret.name": "istio-reader-service-account-token-566n2",
      "kubernetes.io/serviceaccount/service-account.name": "istio-reader-service-account",
      "kubernetes.io/serviceaccount/service-account.uid": "40fa07c3-2e49-4003-a09b-afccdbcec7a2",
      "sub": "system:serviceaccount:istio-system:istio-reader-service-account"
    }

    * 상대방 클러스터의 서비스 상태를 확인 할 수 있는 최소 권한이 할당되어 있음을 알 수 있다!!

     * 'multiCluster' 라벨 확인 & user token을 통한 클러스터 간 인증 허용이 가능!!

     

    b) 시크릿 내용을 출력하는 대신, kubectl 명령어에 파이프해 west-cluster 에 적용하자

    더보기
    # west 에 시크릿 생성
    ieast x create-remote-secret --name="east-cluster" | kwest apply -f -
    secret/istio-remote-secret-east-cluster created
    
    # istiod 로그 확인 : 시크릿이 생성되면, 바로 istiod가 이 시크릿을 가지고 새로 추가된 원격 클러스터(east)에 워크로드를 쿼리한다.
    kwest logs deploy/istiod -n istio-system | grep 'Adding cluster'
    2025-05-16T22:45:00.679666Z     info    Adding cluster  cluster=east-cluster secret=istio-system/istio-remote-secret-east-cluster
    
    #
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    kwest get secret -n istio-system istio-remote-secret-east-cluster
    kwest get secret -n istio-system istio-remote-secret-east-cluster -o yaml
    ...
    
    # west 확인 : east 의 모든 CDS/EDS 정보를 west 에서도 확인 가능!
    for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
    10.20.0.15:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
    
    # west 에서 10.20.0.15(10.20.0.0/16)로 라우팅이 가능한 상태인가?
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||catalog.istioinaction.svc.cluster.local' -o json
    ...
                    "address": {
                        "socketAddress": {
                            "address": "10.20.0.15",
                            "portValue": 3000
    ...
    
    # east 확인 : west 의 CDS/EDS 정보를 아직 모름!
    for i in listener route cluster endpoint; do echo ">> k8s cluster : east - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep catalog
    ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
    10.20.0.15:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local

    ** west-cluster 에 east-cluster의 정보가 추가되었다!!

     

     


    • 기본-기본 배포 primary-primary deployment 에서는 east 가 west 쿼리 할 수 있게 반대로도 설정하자.
    # east 에 시크릿 생성
    iwest x create-remote-secret --name="west-cluster" | keast apply -f -
    secret/istio-remote-secret-west-cluster created
    
    # istiod 로그 확인 : 시크릿이 생성되면, 바로 istiod가 이 시크릿을 가지고 새로 추가된 원격 클러스터(east)에 워크로드를 쿼리한다.
    keast logs deploy/istiod -n istio-system | grep 'Adding cluster'
    2025-05-17T00:09:02.438756Z     info    Adding cluster  cluster=west-cluster secret=istio-system/istio-remote-secret-west-cluster
    
    #
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get secret -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    keast get secret -n istio-system istio-remote-secret-west-cluster
    keast get secret -n istio-system istio-remote-secret-west-cluster -o yaml
    ...
    
    # east 확인 : west 의 모든 CDS/EDS 정보를 east 에서도 확인 가능!
    for i in listener route cluster endpoint; do echo ">> k8s cluster : east - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    ieast proxy-config cluster deploy/istio-ingressgateway.istio-system | grep webapp
    
    # east 에서 10.10.0.15(10.10.0.0/16)로 라우팅이 가능한 상태인가?
    ieast proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep webapp
    10.10.0.15:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local

    [ 실행 결과 한 눈에 보기 ]

    * east-Cluster에 west-Cluster의 Token이 등록되었다!!

     

    ** 이제 east-Cluster에서 west-Cluster의 정보가 보이는지 확인해 보자!!

     

     

    c) 이제 컨트롤 플레인이 상대 클러스터의 워크로드를 퀴리할 수 있다.

    더보기
    # catalog stub service 정보 확인 : endpoints 는 아직도 none.
    kwest get svc,ep -n istioinaction
    NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.100.2.43    <none>        80/TCP    14h
    service/webapp    ClusterIP   10.100.2.172   <none>        80/TCP    14h
    
    NAME                ENDPOINTS        AGE
    endpoints/catalog   <none>           14h
    endpoints/webapp    10.10.0.8:8080   14h
    
    # webapp stub service 생성하지 않았으므로 별도 west 의 webapp service 정보가 없다
    keast get svc,ep -n istioinaction
    NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.200.3.0   <none>        80/TCP    14h
    
    NAME                ENDPOINTS        AGE
    endpoints/catalog   10.20.0.8:3000   14h
    
    # 혹시나 west 에 webapp 에서 east 에 catalog 로 curl 호출 시도 : 10.100.2.43은 어떤 IP/대역 인가?
    kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
    *   Trying 10.100.2.43:80...
    * connect to 10.100.2.43 port 80 failed: Connection refused
    * Failed to connect to catalog.istioinaction.svc.cluster.local port 80 after 1 ms: Connection refused
    • 다음으로는 클러스터 간 연결을 설정해보자.

    12.3.6 클러스터 간 연결 설정하기 Setting up cross-cluster connectivity

    ▶ 들어가며 : north-south 트래픽, east-west 트래픽

    더보기
    • 4장에서는 이스티오의 인그레스 게이트웨이를 다루면서 엔보이 프록시 기반이라는 것을 살펴봤다.
    • 인그레스 게이트웨이는 퍼블릭 네트워크에서 시작한 트래픽의 진입점으로, 트래픽을 조직의 내부 네트워크로 보낸다.
    • 이런 유형의 트래픽을 north-south 트래픽이라고도 한다.
    • 한편 서로 다른 내부 네트워크(여기서는 클러스터의 네트워크) 간의 트래픽은 east-west 트래픽이라고 한다. (그림 12.12 참조)

    north-south 와 east-west 트래픽
    • east-west 트래픽을 단순화하기 위해, 대부분의 클라우드 프로바이더들은 네트워크 주소 공간이 겹치지 않는다면 가상 네트워크의 피어링을 활성화한다.
    • 피어링된 가상 네트워크의 서비스들은 IPv4 및 IPv6 주소로 직접 커넥션을 시작한다. 그러나 네트워크 피어링은 클라우드 전용 기능이다.
    • 네트워크 피어링이 불가능한, 다른 클라우드 프로바이더나 온프레미스에 있는 클러스터에 연결하고 싶을 때 이스티오가 제공하는 선택지가 east-west 게이트웨이다.
    • 게이트웨이는 반대편 클러스터의 워크로드에 접근할 수 있는 로드 밸런서로 노출돼야 한다.
    • 이 절에서는 클러스터 간 연결을 설정하고, 내부적으로 어떻게 동작하는지 보여준다.

     

     

    이스티오의 east-west 게이트웨이 ISTIO’S EAST-WEST GATEWAY

    더보기
    • east-west 게이트웨이의 목적은 클러스터 간 east-west 트래픽의 진입점 역할을 하는 것 뿐 아니라, 이 과정을 서비스 운영 팀에게 투명하게 만드는 것이다.
    • 이 목표를 달성하려면 게이트웨이는 반드시 다음을 수행해야 한다.
      • 클러스터 사이의 정밀한 트래픽 관리 Enable fine-grained traffic management across clusters
      • 암호화된 트래픽 라우팅, 암호화된 상태로 라우팅해야 워크로드 간 상호 인증이 가능하다.
      • Route encrypted traffic to enable mutual authentication between workloads
    • 그리고 서비스 메시 운영자는 어떤 추가 리소스도 구성할 필요가 없어야 한다! 즉, 어떤 이스티오 리소스도 추가 구성할 필요가 없어야 한다! And service mesh operators shouldn’t have to configure any additional resources! In other words, you shouldn’t have to configure any additional Istio resources!
    • 이렇게 하면, 트래픽을 클러스터 내에서 라우팅할 때나 클러스터 간에 라우팅할 때 차이가 없다.
    • 두 시나리오 모두에서 워크로드는 서비스를 세밀하게 겨냥할 수 있고 상호 인증된 커넥션을 시작할 수 있다 (미묘한 차이가 하나 있는데, 클러스터 경계를 넘을 때의 로드 밸런싱이 조금 다르다. 이 부분은 다음 절에서 살펴본다).
    • 이것이 어떻게 구현되는지 이해하려면, 이스티오의 두 기능인 SNI 클러스터SNI 자동 통과를 소개하고 이들이 게이트웨이의 동작을 어떻게 바꾸는지 살펴봐야 한다. SNI clusters and SNI auto passthrough.

     

     

    SNI 클러스터로 east-west 게이트웨이 설정하기 CONFIGURING EAST-WEST GATEWAYS WITH SNI CLUSTERS

    더보기
    • east-west 게이트웨이SNI 클러스터를 모든 서비스에 추가 설정인그레스 게이트웨이다. East-west gateways are ingress gateways with additional configuration for Server Name Indication (SNI) clusters for every service.
    • 그럼 SNI 클러스터란 무엇일까?
    • SNI 클러스터는 단순히 보통의 엔보이 클러스터 Envoy clusters 같은 것으로(10장 10.3.2절의 ‘엔보이 클러스터 설정 쿼리하기’ 절 참조), 트래픽이 라우팅될 수 있는 유사 워크로드 집합을 묶는 속성인 방향, 부분집합, 포트, FQDN을 포함한다.
      SNI clusters are just like regular Envoy clusters (see chapter 10, section 10.3.2, Querying the Envoy Cluster Configuration subsection), consisting of the direction, subset, port, and FQDN that group a set of similar workloads where traffic can be routed.
    • 그러나 SNI 클러스터에는 한 가지 주요 차이점이 있다. SNI 클러스터는 모든 엔보이 클러스터 정보를 SNI에 인코딩한다. However, SNI clusters have one key difference: they encode all Envoy cluster information in the SNI.
    • 이 덕분에 east-west 게이트웨이는 클러스터가 SNI 안에 지정한 클러스터로 암호화된 트래픽을 라우팅할 수 있는 것이다. This enables the east-west gateway to proxy encrypted traffic to the cluster specified by the client within the SNI.
    • 구체적인 예를 들면, 클라이언트(webapp 같은)가 원격 클러스터의 워크로드(catalog 같은)로 커넥션을 시작할 때 그림 12.13 처럼 겨냥하는 클러스터를 SNI에 인코딩한다.
      To take a concrete example, when one client(such as the webapp) initiates a connection to a workload in a remote cluster(such as the catalog workload) it encodes the cluster that it targets into the SNI, as shown in figure 12.13.

     

     

    1. 클러스터 정보는 SNI에 인코딩된다.
    2. SNI에는 라우팅 결정을 지시하는 방향, 포트, 버전, 서비스 이름이 포함된다.
    • 그러므로 클라이언트는 세밀한 라우팅 결정을 내릴 수 있으며, 게이트웨이는 SNI 헤더에서 클러스터 정보를 읽어 트래픽을 클라이언트가 의도한 워크로드로 프록시할 수 있다.Thus the client can make fine-grained routing decisions, and the gateway can read the cluster information from the SNI header and then proxy the traffic to the workload intended by the client.
    • 이 모든 작업이 안전하고 상호 인증한 커넥션을 워크로드 사이에 유지하는 중에 일어난다. All this happens while maintaining a secure and mutually authenticated connection between the workloads.

     

    SNI 클러스터가 있는 east-west 게이트웨이 설치하기 Installing the east-west gateway with SNI clusters

    더보기

    SNI 클러스터 설정은 옵트인 기능으로, 다음 IstioOpertor 정의처럼 환경 변수 ISTIO_META_ROUTER_MODE 로 게이트웨이 라우터 모드를 sni-dnat 으로 설정해서 활성화할 수 있다. For a gateway, the configuration of SNI clusters is an opt-in feature that can be enabled by setting the gateway router mode to sni-dnat using the environment variable ISTIO_META_ROUTER_MODE, as shown in the following IstioOperator definition:

     

    # cat ch12/gateways/cluster-east-eastwest-gateway.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: istio-eastwestgateway # IstioOperator 이름은 앞 선 이스티오 설정 이름과 겹치지 않아야 한다
      namespace: istio-system
    spec:
      profile: empty # empty 프로필은 추가 이스티오 구성 요소를 설치하지 않는다
      components:
        ingressGateways:
        - name: istio-eastwestgateway # 게이트웨이 이름
          label:
            istio: eastwestgateway
            app: istio-eastwestgateway
            topology.istio.io/network: east-network
          enabled: true
          k8s:
            env:
            - name: ISTIO_META_ROUTER_MODE # sni-dnat 모드는 트래픽을 프록시하는 데 필요한 SNI 클러스터를 추가한다
              value: "sni-dnat"
            # The network to which traffic is routed
            - name: ISTIO_META_REQUESTED_NETWORK_VIEW # 게이트웨이가 트래픽을 라우팅하는 네트워크
              value: east-network
            service:
              ports:
              ... (생략) ...
      values:
        global:
          meshID: usmesh # 메시, 클러스터, 네트워크 식별 정보
          multiCluster:
            clusterName: east-cluster
          network: east-network
    • IstioOperator 리소스 이름은 처음에 컨트롤 플레인 설치하는 데 사용한 리소스와 같아서는 안 된다. 같은 이름을 사용하면 앞 서 설치한 것을 덮어 쓸 것이다.
    • ISTIO_META_ROUTER_MODE 를 sni-dnat 으로 설정하면 SNI 클러스터를 자동으로 구성한다. 지정하지 않으면 standard 모드로 돌아가며, 이는 SNI 클러스터를 설정하지 않는다.
    • ISTIO_META_REQUESTED_NETWORK_VIEW 는 네트워크 트래픽이 프록시되는 곳을 정의한다.

    • IstioOperator 로 east 클러스터에 east-west 게이트웨이를 설치하자.
    # 설치 전 확인
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get pod -n istio-system -l istio.io/rev=default --kubeconfig=./$i-kubeconfig; echo; done
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    kwest get IstioOperator -n istio-system installed-state-istio-controlplane -o yaml
    keast get IstioOperator -n istio-system installed-state-istio-controlplane -o yaml
    
    # 설치 전 확인 : west 에서 catalog endpoint 에 IP 확인
    for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep catalog
    10.20.0.15:3000                                         HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
    
    # IstioOperator 로 east 클러스터에 east-west 게이트웨이를 설치
    cat ch12/gateways/cluster-east-eastwest-gateway.yaml
    docker cp ./ch12/gateways/cluster-east-eastwest-gateway.yaml east-control-plane:/cluster-east-eastwest-gateway.yaml
    ieast install -f /cluster-east-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y
    
    # east 클러스터에 east-west 게이트웨이를 설치 확인
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get pod -n istio-system -l istio.io/rev=default --kubeconfig=./$i-kubeconfig; echo; done
    ...
    NAME                                     READY   STATUS    RESTARTS        AGE
    istio-eastwestgateway-866794c798-k9xfl   1/1     Running   0               14s
    istio-ingressgateway-7f6f8f8d99-4mlp5    1/1     Running   1 (3h38m ago)   17h
    istiod-85976468f-vp4zg                   1/1     Running   1 (3h38m ago)   17h
    
    keast get IstioOperator -n istio-system installed-state-istio-eastwestgateway -o yaml
    ...
        ingressGateways:
        - enabled: true
          k8s:
            env:
            - name: ISTIO_META_ROUTER_MODE
              value: sni-dnat
            - name: ISTIO_META_REQUESTED_NETWORK_VIEW
              value: east-network
            service:
              ports:
              - name: status-port
                port: 15021
                targetPort: 15021
              - name: mtls
                port: 15443
                targetPort: 15443
              - name: tcp-istiod
                port: 15012
                targetPort: 15012
              - name: tcp-webhook
                port: 15017
                targetPort: 15017
          label:
            app: istio-eastwestgateway
            istio: eastwestgateway
            topology.istio.io/network: east-network
          name: istio-eastwestgateway
    ...
    
    # east 정보 확인
    ieast proxy-status
    NAME                                                    CLUSTER          CDS        LDS        EDS        RDS          ECDS         ISTIOD                     VERSION
    catalog-6cf4b97d-9c995.istioinaction                    east-cluster     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-85976468f-vp4zg     1.17.8
    istio-eastwestgateway-866794c798-k9xfl.istio-system     east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-vp4zg     1.17.8
    istio-ingressgateway-7f6f8f8d99-4mlp5.istio-system      east-cluster     SYNCED     SYNCED     SYNCED     NOT SENT     NOT SENT     istiod-85976468f-vp4zg     1.17.8
    
    # east 에 istio-ingressgateway  에 istio-config 정보 확인 : west 의 CDS/EDS 모두 알고 있음!
    for i in listener route cluster endpoint; do echo ">> east k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    
    # east 에 istio-eastwestgateway 에 istio-config 정보 확인 : webapp(CDS) OK, west 에 EDS 아직 모름!
    for i in listener route cluster endpoint; do echo ">> east k8s cluster : eastwestgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-eastwestgateway.istio-system; echo; done
    
    ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system | grep istioinaction
    catalog.istioinaction.svc.cluster.local                      80        -          outbound      EDS            
    webapp.istioinaction.svc.cluster.local                       80        -          outbound      EDS
    
    ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system --fqdn webapp.istioinaction.svc.cluster.local -o json
    ieast proxy-config cluster deploy/istio-eastwestgateway.istio-system --fqdn webapp.istioinaction.svc.cluster.local -o json | grep sni
                            "sni": "outbound_.80_._.webapp.istioinaction.svc.cluster.local"
    
    ieast proxy-config endpoint deploy/istio-eastwestgateway.istio-system | grep istioinaction
    ieast proxy-config endpoint deploy/istio-eastwestgateway.istio-system --cluster 'outbound|80||catalog.istioinaction.svc.cluster.local' -o json
    
    
    # west 정보 확인
    iwest proxy-status
    
    # west 에 istio-ingressgateway 에 istio-config 정보 확인
    for i in listener route cluster endpoint; do echo ">> west k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system | grep istioinaction
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn catalog.istioinaction.svc.cluster.local -o json | grep sni
                            "sni": "outbound_.80_._.catalog.istioinaction.svc.cluster.local"
    
    # west 에 istio-ingressgateway : east EDS 모든 정보에서 east의 eastwestgateway에 mtls 정보로 변경!
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep istioinaction
    10.10.0.15:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
    172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
    
    # 출력되는 172.18.X.Y에 IP 확인 : east 에 eastwestgateway 의 Service(LoadBalancer)의 External-IP.
    keast get svc,ep -n istio-system istio-eastwestgateway
    NAME                            TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                                                           AGE
    service/istio-eastwestgateway   LoadBalancer   10.200.0.90   172.18.255.202   15021:32281/TCP,15443:30196/TCP,15012:32628/TCP,15017:32346/TCP   27m
    
    NAME                              ENDPOINTS                                                        AGE
    endpoints/istio-eastwestgateway   10.20.0.16:15021,10.20.0.16:15017,10.20.0.16:15012 + 1 more...   27m
    
    
    # west 에 webapp 에 istio-config 정보 확인
    for i in listener route cluster endpoint; do echo ">> west k8s cluster : webapp - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/webapp.istioinaction; echo; done
    iwest proxy-config endpoint deploy/webapp.istioinaction | grep istioinaction
    10.10.0.15:8080                                         HEALTHY     OK                outbound|80||webapp.istioinaction.svc.cluster.local
    172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
    
    
    # west 에서 호출 시도
    kwest get svc,ep -n istioinaction
    NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.100.0.170   <none>        80/TCP    63m
    service/webapp    ClusterIP   10.100.0.141   <none>        80/TCP    63m
    
    NAME                ENDPOINTS         AGE
    endpoints/catalog   <none>            63m
    endpoints/webapp    10.10.0.15:8080   63m
    
    kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
    *   Trying 10.100.0.170:80...
    * connect to 10.100.0.170 port 80 failed: Connection refused
    ...
    • east-west 게이트웨이를 설치하고 라우터 모드를 sni-dnat으로 설정했으면, 다음 단계는 SNI 자동 통과 모드 SNI auto passthrough mode 를 사용해 east-west 게이트웨이에서 다중 클러스터 상호 TLS 포트를 노출하는 것이다. With the east-west gateway installed and the router mode set to sni-dnat the next step is to expose the multi-cluster mTLS port through the east-west gateway using the SNI auto passthrough mode.
    • 이스티오는 영리해서 딱 그때만 게이트웨이에 SNI 클러스터를 설정한다. Istio is clever and only then configures the gateway with the SNI clusters.

    [ 실행 결과 - 한 눈에 보기 ] 

    1) eastwestgateway 생성 이전 정보 확인

    2) eastwestgateway 생성

     

    3) eastCluster istio proxy-status ( eastCluster에서 westCluster 의 CDS, EDS 정보를 알고 있다!! )

     

     

    4) west endpoint 정보가 eastwestgateway 주소로 변경되었다!!

    gateway가 노출하는 ALB 의 ip-nodeport로 변경되었다!!

     5) eastwestgateway에 의해 노출되는 서비스에 접근 테스트 및 결과 확인

     

     

    SNI 자동 통과로 클러스터 간 트래픽 라우팅하기 ROUTING CROSS-CLUSTER TRAFFIC USING SNI AUTO PASSTHROUGH

    더보기
    • SNI 자동 통과 auto passthrough 를 이해하기 위해, 수동 SNI 통과가 SNI 헤더에 기반해 트래픽을 허용하도록 인그레스 게이트웨이를 설정한다는 것을 떠올려보자 (4장의 4.4.2절 참고). To understand SNI auto passthrough, let’s recall that the manual SNI passthrough configures the ingress gateway to admit traffic based on the SNI header.
    • 이는 허용된 트래픽을 라우팅하려면 서비스 운영자가 수작업으로 VirtualService 리소스를 정의해야 함을 보여준다 (그림 12.14) This shows that to route admitted traffic, service operators have to manually define a VirtualService resource.

     

    • SNI 자동 통과는 그 이름에서 짐작할 수 있듯이, 허용된 트래픽을 라우팅하자고 VirtualService 를 수작업으로 만들 필요가 없다. SNI auto passthrough, as the name suggests, doesn’t require manually creating a VirtualService to route admitted traffic.
    • 라우팅은 SNI 클러스터를 사용해 수행할 수 있는데, SNI 클러스터는 라우터 모드가 sni-dnat 일때 east-west 게이트웨이에서 자동으로 설정된다. It is done using the SNI clusters, which are configured automatically in the east-west gateway when its router mode is set to sni-dnat.
    • SNI 자동 통과는 이스티오 Gateway 리소스로 설정할 수 있다. 다음 정의에서는 SNI 헤더의 값이 *.local 인 모든 트래픽에 대해 SNI 자동 통과를 사용한는데, 이는 모든 쿠버네티스 서비스에 해당한다. SNI auto passthrough mode is configured using the Istio Gateway resource. In the following definition, we use SNI auto passthrough for all traffic where the SNI header matches the expression *.local, which is the case for all Kubernetes services. 
    # cat ch12/gateways/expose-services.yaml              
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: cross-network-gateway
      namespace: istio-system
    spec:
      selector:
        istio: eastwestgateway # 셀렉터와 일치하는 게이트웨이에만 설정이 적용된다.
      servers:
        - port:
            number: 15443 # 이스티오에서 15443 포트는 멀티 클러스터 상호 TLS 트래픽 용도로 지정된 특수 포트다
            name: tls
            protocol: TLS
          tls:
            mode: AUTO_PASSTHROUGH # SNI 헤더를 사용해 목적지를 해석하고 SNI 클러스터를 사용한다.
          hosts:
            - "*.local" # 정규식 *.local 과 일치하는 SNI에 대해서만 트래픽을 허용한다.

     


    • east 클러스터에 적용 시, east-cluster의 워크로드를 west-cluster 에 노출한다.
    # east 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출한다.
    cat ch12/gateways/expose-services.yaml
    keast apply -n istio-system -f ch12/gateways/expose-services.yaml
    
    # 확인
    keast get gw,vs,dr -A
    NAMESPACE      NAME                                                AGE
    istio-system   gateway.networking.istio.io/cross-network-gateway   85s
    
    # west 에서 호출 시도
    kwest get svc,ep -n istioinaction
    NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.100.0.170   <none>        80/TCP    63m
    service/webapp    ClusterIP   10.100.0.141   <none>        80/TCP    63m
    
    NAME                ENDPOINTS         AGE
    endpoints/catalog   <none>            63m
    endpoints/webapp    10.10.0.15:8080   63m
    
    kwest exec -it deploy/webapp -c istio-proxy -n istioinaction -- curl catalog.istioinaction.svc.cluster.local -v
    *   Trying 10.100.0.170:80...
    * connect to 10.100.0.170 port 80 failed: Connection refused
    ...
    
    
    # east 에 istio-ingressgateway  에 istio-config 정보 확인 : 이전 내용과 동일하게 west 의 CDS/EDS 모두 알고 있음!
    for i in listener route cluster endpoint; do echo ">> east k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    
    # east 에 istio-eastwestgateway 에 istio-config 정보 확인 : SNI 자동 통과를 위한 listener 추가 확인!
    for i in listener route cluster endpoint; do echo ">> east k8s cluster : eastwestgateway - istio-config $i <<"; docker exec -it east-control-plane istioctl proxy-config $i deploy/istio-eastwestgateway.istio-system; echo; done
    
    ieast proxy-config listener deploy/istio-eastwestgateway.istio-system
    ieast proxy-config listener deploy/istio-eastwestgateway.istio-system | grep istioinaction
    0.0.0.0 15443 SNI: outbound_.80_._.webapp.istioinaction.svc.cluster.local; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2                    Cluster: outbound_.80_._.webapp.istioinaction.svc.cluster.local
    0.0.0.0 15443 SNI: outbound_.80_._.catalog.istioinaction.svc.cluster.local; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2                   Cluster: outbound_.80_._.catalog.istioinaction.svc.cluster.local
    
    ieast proxy-config listener deploy/istio-eastwestgateway.istio-system --port 15443  -o json
    ...
                    "filterChainMatch": {
                        "serverNames": [
                            "outbound_.80_._.catalog.istioinaction.svc.cluster.local"
                     ...
                    },
                    "filters": [
                        ...
                        {
                            "name": "envoy.filters.network.tcp_proxy",
                            "typedConfig": {
                                "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
                                "statPrefix": "outbound_.80_._.catalog.istioinaction.svc.cluster.local",
                                "cluster": "outbound_.80_._.catalog.istioinaction.svc.cluster.local",
    ...
    
    
    
    # west 정보 확인
    iwest proxy-status
    
    # west 에 istio-ingressgateway 에 istio-config 정보 확인
    for i in listener route cluster endpoint; do echo ">> west k8s cluster : ingressgateway - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    
    # west 에 webapp 에 istio-config 정보 확인
    for i in listener route cluster endpoint; do echo ">> west k8s cluster : webapp - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/webapp.istioinaction; echo; done

     

    [ 실행 결과 한 눈에 보기 ]

    * east-Cluster에 west에 있는 webapp 서비스 노출 !!

     

    ieast proxy-config listener deploy/istio-eastwestgateway.istio-system --port 15443 -o json

     


    • 반대편 클러스터에도 작업 수행. west-cluster 에 east-west 게이트웨이를 만들고, 그 서비스를 east-cluster 워크로드에 노출하자!
    # IstioOperator 로 west 클러스터에 east-west 게이트웨이를 설치
    cat ch12/gateways/cluster-west-eastwest-gateway.yaml
    docker cp ./ch12/gateways/cluster-west-eastwest-gateway.yaml west-control-plane:/cluster-west-eastwest-gateway.yaml
    iwest install -f /cluster-west-eastwest-gateway.yaml --set values.global.proxy.privileged=true -y
    
    # west 클러스터에 east-west 게이트웨이를 설치 확인
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get IstioOperator -n istio-system --kubeconfig=./$i-kubeconfig; echo; done
    for i in west east; do echo ">> k8s cluster : $i <<"; kubectl get pod -n istio-system -l istio.io/rev=default --kubeconfig=./$i-kubeconfig; echo; done
    kwest get IstioOperator -n istio-system installed-state-istio-eastwestgateway -o yaml
    iwest proxy-status
    
    
    # west 클러스터에 적용하자. east-cluster의 워크로드를 west-cluster 에 노출한다.
    cat ch12/gateways/expose-services.yaml
    kwest apply -n istio-system -f ch12/gateways/expose-services.yaml
    
    # 확인
    kwest get gw,vs,dr -A
    NAMESPACE       NAME                                                AGE
    istio-system    gateway.networking.istio.io/cross-network-gateway   5s
    istioinaction   gateway.networking.istio.io/coolstore-gateway       89m
    
    NAMESPACE       NAME                                                       GATEWAYS                HOSTS                         AGE
    istioinaction   virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   89m
    
    kwest get svc,ep -n istioinaction
    NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    service/catalog   ClusterIP   10.100.0.170   <none>        80/TCP    93m
    service/webapp    ClusterIP   10.100.0.141   <none>        80/TCP    93m
    
    NAME                ENDPOINTS         AGE
    endpoints/catalog   <none>            93m
    endpoints/webapp    10.10.0.15:8080   93m
    
    ieast pc clusters deploy/istio-eastwestgateway.istio-system | grep catalog
    catalog.istioinaction.svc.cluster.local                                       80        -          outbound      EDS            
    outbound_.80_._.catalog.istioinaction.svc.cluster.local                       -         -          -             EDS 
    
    # sni 클러스터 확인
    ieast pc clusters deploy/istio-eastwestgateway.istio-system | grep catalog | awk '{printf "CLUSTER: %s\n", $1}'
    CLUSTER: catalog.istioinaction.svc.cluster.local
    CLUSTER: outbound_.80_._.catalog.istioinaction.svc.cluster.local # catalog 서비스용 SNI 클러스터

     

    • 출력은 catalog 워크로드에 SNI 클러스터가 정의됐음을 보여준다! The output shows the SNI cluster is defined for the catalog workload!
    • 그리고 SNI 자동 통과로 게이트웨이를 설정했으므로, 게이트웨이에 들어오는 트래픽은 SNI 클러스터를 사용해 의도한 워크로드로 라우팅한다. And as we configured the gateway with SNI auto passthrough, incoming traffic on the gateway uses the SNI clusters to route to the intended workloads.
    • 이스티오 컨트롤 플레인은 이 리소스들의 생성을 지켜보고 있다가, 이제 클러스터 간 트래픽을 라우팅할 수 있는 경로가 존재함을 발견한다. Istio’s control plane listens for the creation of these resources and discovers that now a path exists to route crosscluster traffic.
    • 따라서 컨트롤 플레인은 원격 클러스터에서 새로 발견한 엔드포인트로 모든 워크로드를 설정한다. Thus it updates all workloads with the newly discovered endpoints in the remote cluster.

    [ 실행 결과 - 한 눈에 보기 ]

    * cross-gateway 배포 후 확인

     

    * 서비스 노출

     

    * east-Cluster 에서 west 에 있는 app service 호출

     

    클러스터 간 워크로드 디스커버리 검증하기* VALIDATING CROSS-CLUSTER WORKLOAD DISCOVERY

    더보기
    • 이제 east-cluster 의 워크로드가 west-cluster 에 노출됐으므로, webapp 엔보이 클러스터가 catalog 워크로드로 향햐는 엔드포인트를 갖고 있으리라 기대된다.
    • 이 엔드포인트는 네트워크의 catalog 에 대한 요청을 프록시하는 east-west 게이트웨이 주소를 가리켜야 한다.
    • 이를 확인하기 위해 east-cluster 의 east-west 게이트웨이 주소(Service)를 알아보자.
    #
    keast -n istio-system get svc istio-eastwestgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
    172.18.255.202
    • 이제 이 값을 west-cluster 의 워크로드가 클러스터 간 트래픽을 라우팅할 때 사용하는 주소와 비교해보자.
    # 
    iwest pc endpoints deploy/webapp.istioinaction | grep catalog
    172.18.255.202:15443                                    HEALTHY     OK                outbound|80||catalog.istioinaction.svc.cluster.local
    • catalog 리소스의 엔드포인트가 east-west 게이트웨이의 주소와 일치하면, 워크로드를 찾을 수 있고 클러스터 간 트래픽이 가능하다.
    • 프록시 설정을 고려하면 모든 것이 올바르게 설정됐다.
    • 직접 요청을 트리거해 마지막으로 확인해보자.
    # west 에 istio-ingressgateway 인입을 위한 접속 정보 확인
    kwest get svc -n istio-system istio-ingressgateway
    NAME                           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                                                                      AGE
    service/istio-ingressgateway   LoadBalancer   10.100.0.82   172.18.255.101   15021:30627/TCP,80:30000/TCP,443:31615/TCP,31400:32694/TCP,15443:32016/TCP   119m
    
    EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $EXT_IP
    172.18.255.101
    
    #
    docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog | jq
    [
      {
        "id": 1,
        "color": "amber",
    ...
    
    # 신규 터미널 : 반복 접속
    alias kwest='kubectl --kubeconfig=./west-kubeconfig'
    EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    while true; do docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
    • 키알리로 확인해 보자!! ( west-cluster 확인)
    • kiali : east-cluster 확인 , mTLS 통신이며 TCP Traffic 와 HTTP(S) 2개 트래픽 흐름으로 확인됨.

    (참고) Istio 의 TCP Traffic 라우팅과 Traffic routing with SNI passthrough4.4.2 를 확인하자. 

     

    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: sni-passthrough-gateway
    spec:
      selector:
        istio: ingressgateway
      servers:
      - port:
          number: 31400 #1 HTTP 포트가 아닌 특정 포트 열기
          name: tcp-sni
          protocol: TLS
        hosts:
        - "simple-sni-1.istioinaction.io" #2 이 호스트를 포트와 연결
        tls:
          mode: PASSTHROUGH #3 통과 트래픽으로 처리
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: simple-sni-1-vs
    spec:
      hosts:
      - "simple-sni-1.istioinaction.io"
      gateways:
      - sni-passthrough-gateway
      tls: 
      - match: #1 특정 포트와 호스트의 비교 부분
        - port: 31400
          sniHosts:
          - simple-sni-1.istioinaction.io
        route:
        - destination: #2 트래픽이 일치하는 경우 라우팅 목적지
            host: simple-tls-service-1
            port:
              number: 80 #3 서비스 포트로 라우팅

     


    • jaeger : west-cluster 확인

     

    • jaeger : east-cluster 확인

     

    • istio-proxy 로그 확인
    # west 에 istio-ingressgateway 로그
    kwest logs -n istio-system -l app=istio-ingressgateway -f
    [2025-05-17T09:59:18.894Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 8 8 "172.18.0.100" "curl/8.7.1" "381b6402-245b-95c4-a090-1bf3362df73b" "webapp.istioinaction.io" "10.10.0.15:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.14:57738 10.10.0.14:8080 172.18.0.100:40922 - -
    
    # west 에 webapp 로그
    kwest logs -n istioinaction -l app=webapp -c istio-proxy -f
    [2025-05-17T09:56:43.148Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 6 5 "172.18.0.100" "curl/8.7.1" "78234f30-cdbc-9924-82b8-86a0b8c0b74c" "webapp.istioinaction.io" "10.10.0.15:8080" inbound|8080|| 127.0.0.6:52445 10.10.0.15:8080 172.18.0.100:0 outbound_.80_._.webapp.istioinaction.svc.cluster.local default
    [2025-05-17T09:56:44.279Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 4 4 "172.18.0.100" "beegoServer" "2181d9d6-50ba-97b7-b4d8-fb175257b5a8" "catalog.istioinaction.svc.cluster.local:80" "172.18.255.202:15443" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.15:36158 10.100.0.170:80 172.18.0.100:0 - default
    
    kwest logs -n istioinaction -l app=webapp -c webapp -f
    2025/05/17 09:57:39.141 [M] [router.go:1014]  172.18.0.100 - - [17/May/2025 09:57:39] "GET /api/catalog HTTP/1.1 200 0" 0.010450  curl/8.7.1
    
    # west 에 istio-eastwestgateway 로그
    kwest exec -it -n istio-system deploy/istio-eastwestgateway -- curl -X POST http://localhost:15000/logging?level=debug
    kwest logs -n istio-system -l app=istio-eastwestgateway -f
    ...
    
    
    
    # east 에 istio-eastwestgateway 로그
    keast exec -it -n istio-system deploy/istio-eastwestgateway -- curl -X POST http://localhost:15000/logging?level=debug
    keast logs -n istio-system -l app=istio-eastwestgateway -f
    ...
    
    # east 에 catalog 로그
    keast logs -n istioinaction -l app=catalog -c istio-proxy -f
    [2025-05-17T10:04:07.297Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 1 1 "172.18.0.100" "beegoServer" "4b164067-fbee-9ada-a9b3-84ed7b1cac1e" "catalog.istioinaction.svc.cluster.local:80" "10.20.0.15:3000" inbound|3000|| 127.0.0.6:59201 10.20.0.15:3000 172.18.0.100:0 outbound_.80_._.catalog.istioinaction.svc.cluster.local default
    
    keast logs -n istioinaction -l app=catalog -c catalog -f
    request path: /items
    blowups: {}
    number of blowups: 0
    GET catalog.istioinaction.svc.cluster.local:80 /items 200 502 - 0.559 ms
    GET /items 200 0.559 ms - 502

     

     1) west : webapp

    #
    kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i any tcp -nn
    kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i lo tcp -nn
    kwest exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -i eth0 tcp -nn
    10:31:42.955656 eth0  Out IP 10.10.0.15.36168 > 172.18.255.202.15443: Flags [P.], seq 2756:4134, ack 3577, win 696, options [nop,nop,TS val 3483540647 ecr 101206706], length 1378
    10:31:42.957686 eth0  In  IP 172.18.255.202.15443 > 10.10.0.15.36168: Flags [P.], seq 3577:5365, ack 4134, win 826, options [nop,nop,TS val 101207823 ecr 3483540647], length 1788
    ...
    
    # 
    keast get svc -n istio-system istio-eastwestgateway
    NAME                    TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                                                           AGE
    istio-eastwestgateway   LoadBalancer   10.200.0.90   172.18.255.202   15021:32281/TCP,15443:30196/TCP,15012:32628/TCP,15017:32346/TCP   156m

     

     2) east : catalog

    # istio-proxy 에 tcp port 3000 에서 패킷 덤프에 출력 결과를 파일로 저장 
    keast exec -it -n istioinaction deploy/catalog -c istio-proxy -- sudo tcpdump -i any tcp port 3000 -w /var/lib/istio/data/dump.pcap
    keast exec -it -n istioinaction deploy/catalog -c istio-proxy -- ls -l /var/lib/istio/data/
    
    # 출력 결과 파일을 로컬로 다운로드
    keast get pod -n istioinaction -l app=catalog -oname
    pod/catalog-6cf4b97d-ff7lq
    
    keast cp -n istioinaction -c istio-proxy catalog-6cf4b97d-ff7lq:var/lib/istio/data/dump.pcap ./dump.pcap
    
    # 로컬로 다운 받은 파일을 wireshark 로 불러오기 : XFF 로 요청 Client IP 확인
    wireshark dump.pcap

     

    1. west의 인그레스 게이트웨이에 요청을 트리거하면 요청이 west-cluster 의 webapp 으로 라우팅됨을 알 수 있다.
    2. 그런 다음, 요청을 처리하는 east-cluster 의 catalog 워크로드로 해석된다.
    • 이로써 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시가 설정됐고 두 클러스터가 서로의 워크로드를 찾을 수 있음을 확인했다.
    • 워크로드들은 east-west 게이트웨이를 통과 지점으로 사용해 상호 인증 커넥션을 시작한다.

     

     

     

     

     

    [ 요약 ] 다중 클러스터 서비스 메시를 설정하는데 필요한 것 

    더보기
    1. 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
      • 서비스 어카운트 토큰과 인증서가 포함된 kubeconfig 를 사용해 각 컨트롤 플레인에 동료 클러스터에 대한 접근 권한을 제공함으로써 구현한다.
      • 이 과정은 istioctl 을 사용해 쉽게 진행했고, east-cluster 에만 적용했다.
    2. 클러스터 간 워크로드 연결 Cross-cluster workload connectivity
      • 다른 클러스터의 워크로드(다른 네트워크에 위치) 간에 트래픽을 라우팅하도록 east-west 게이트웨이를 설정하고, 이스티오가 워크로드가 위치한 네트워크를 알 수 있도록 각 클러스터에 네트워크 정보 레이블을 지정해 구현한다.
    3. 클러스터 간 신뢰 설정 Configuring trust between clusters
      • 상대 클러스터와 동일한 루트 신뢰로 중간 인증서를 발급함으로써 설정한다.
    • 겨우 몇 단계 정도이며, 대부분 자동화돼 다중 클러스터 서비스 메시를 설정한다.
    • 다음 절에서는 클러스터 간의 서비스 메시 동작을 몇 가지 확인해본다.

    12.3.7 클러스터 간 로드 밸런싱 Load-balancing across clusters

    클러스터 간 로드 밸런싱

    더보기
    • 6장에서는 클러스터 지역 인식 로드 밸런싱을 살펴보겠다고 약속했다. In chapter 6, we promised to explore cross-cluster, locality-aware load balancing.
    • 그리고 이제 다중 클러스터 서비스 메시가 있으니 그럴 준비가 됐다. 이를 시연하기 위해 2개의 샘플 서비스를 배포할 것이다.
    • 이 서비스들은 워크로드가 실행 중인 클러스터의 이름을 반환하도록 설정돼 있다. 그러므로 요청을 처리하는 워크로드의 위치를 쉽게 확인할 수 있다. To demonstrate this, we’ll deploy two sample services, each of which is configured to return the name of the cluster in which the workload is running.

    • west-cluster 에 첫 번째 서비스를 배포해보자. Thus we can easily determine the locality of the workload that served the request.
    #
    tree ch12/locality-aware/west
    ch12/locality-aware/west
    ├── simple-backend-deployment.yaml
    ├── simple-backend-dr.yaml
    ├── simple-backend-gw.yaml
    ├── simple-backend-svc.yaml
    └── simple-backend-vs.yaml
    
    # west-cluster 에 간단한 백엔드 디플로이먼트/서비스를 배포
    cat ch12/locality-aware/west/simple-backend-deployment.yaml
    ...
            - name: "MESSAGE"
              value: "Hello from WEST"    
    ...
    
    cat ch12/locality-aware/west/simple-backend-svc.yaml
    kwest apply -f ch12/locality-aware/west/simple-backend-deployment.yaml
    kwest apply -f ch12/locality-aware/west/simple-backend-svc.yaml
    kwest get deploy -n istioinaction simple-backend-west
    kwest get svc,ep -n istioinaction simple-backend
    
    # 트래픽을 허용하기 위해 Gateway, 게이트웨이에서 백엔드 워크로드로 트래픽을 라우팅하기 위해 VirtualService 적용
    cat ch12/locality-aware/west/simple-backend-gw.yaml
    cat ch12/locality-aware/west/simple-backend-vs.yaml
    kwest apply -f ch12/locality-aware/west/simple-backend-gw.yaml
    kwest apply -f ch12/locality-aware/west/simple-backend-vs.yaml
    kwest get gw,vs,dr -n istioinaction
    NAME                                                 AGE
    gateway.networking.istio.io/coolstore-gateway        7h15m
    gateway.networking.istio.io/simple-backend-gateway   3m10s
    
    NAME                                                               GATEWAYS                     HOSTS                                 AGE
    virtualservice.networking.istio.io/simple-backend-vs-for-gateway   ["simple-backend-gateway"]   ["simple-backend.istioinaction.io"]   3m10s
    virtualservice.networking.istio.io/webapp-virtualservice           ["coolstore-gateway"]        ["webapp.istioinaction.io"]           7h15m

     

    • west-cluster의 서비스로 요청하고 클러스터 이름을 반환하는지 확인하자.
    # west-cluster의 서비스로 요청하고 클러스터 이름을 반환하는지 확인
    EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
    docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP
    {
      "name": "simple-backend-west",
      "uri": "/",
      "type": "HTTP",
      "ip_addresses": [
        "10.10.0.17"
      ],
      "start_time": "2025-05-17T14:48:43.973591",
      "end_time": "2025-05-17T14:48:44.124935",
      "duration": "151.346ms",
      "body": "Hello from WEST",
      "code": 200
    }
    
    # 신규 터미널 : 반복 접속
    alias kwest='kubectl --kubeconfig=./west-kubeconfig'
    EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    while true; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

     

    • 이제 east-cluster 에 서비스를 배포하자.
    #
    tree ch12/locality-aware/east
    ch12/locality-aware/east
    ├── simple-backend-deployment.yaml
    └── simple-backend-svc.yaml
    
    # east-cluster 에 서비스를 배포
    cat ch12/locality-aware/east/simple-backend-deployment.yaml
    ...
            - name: "MESSAGE"
              value: "Hello from EAST"
    ...
    
    cat ch12/locality-aware/east/simple-backend-svc.yaml
    keast apply -f ch12/locality-aware/east/simple-backend-deployment.yaml
    keast apply -f ch12/locality-aware/east/simple-backend-svc.yaml
    keast get deploy -n istioinaction simple-backend-east
    keast get svc,ep -n istioinaction simple-backend

     

    • 양쪽 클러스터에서 서비스가 실행되면서 인그레스 게이트웨이에 엔드포인트가 설정돼 요청이 양쪽으로 분산된다 (그림 12.17 참조) 

     

    • kiali : west, east
    west-Cluster
    east-Cluster

     

    ☞ 기본적으로, 이스티오는 라운드 로빈 알고리듬으로 워크로드 간에 로드 밸런싱한다. 그러므로 트래픽은 고르게 분산된다.

    # 10회 요청 후 확인
    for i in {1..10}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; echo ; done
    for i in {1..10}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; echo ; done | sort | uniq -c
       4 "Hello from EAST"
       6 "Hello from WEST"
    
    
    # 정보 확인
    kwest get svc,ep -n istioinaction simple-backend
    NAME                     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
    service/simple-backend   ClusterIP   10.100.0.156   <none>        80/TCP    37m
    
    NAME                       ENDPOINTS         AGE
    endpoints/simple-backend   10.10.0.17:8080   37m # k8s service 에 endpoint 에는 west 에 파드 ip만 출력
    
    #
    for i in listener route cluster endpoint; do echo ">> k8s cluster : west - istio-config $i <<"; docker exec -it west-control-plane istioctl proxy-config $i deploy/istio-ingressgateway.istio-system; echo; done
    >> k8s cluster : west - istio-config listener <<
    ADDRESS PORT  MATCH DESTINATION
    0.0.0.0 8080  ALL   Route: http.8080
    ...
    
    >> k8s cluster : west - istio-config route <<
    NAME          DOMAINS                             MATCH                  VIRTUAL SERVICE
    http.8080     simple-backend.istioinaction.io     /*                     simple-backend-vs-for-gateway.istioinaction
    ...
    
    >> k8s cluster : west - istio-config cluster <<
    SERVICE FQDN                                                 PORT      SUBSET     DIRECTION     TYPE           DESTINATION RULE      
    simple-backend.istioinaction.svc.cluster.local               80        -          outbound      EDS     
    ...
    
    >> k8s cluster : west - istio-config endpoint <<
    ENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER
    10.10.0.17:8080                                         HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local
    172.18.255.202:15443                                    HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local
    ...
    
    #
    iwest proxy-config listener deploy/istio-ingressgateway.istio-system
    iwest proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
    
    iwest proxy-config route deploy/istio-ingressgateway.istio-system
    iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080
    iwest proxy-config route deploy/istio-ingressgateway.istio-system --name http.8080 -o json
    
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system
    iwest proxy-config cluster deploy/istio-ingressgateway.istio-system --fqdn simple-backend.istioinaction.svc.cluster.local -o json
    
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep simple
    10.10.0.17:8080                                         HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local
    172.18.255.202:15443                                    HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local
    
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
    [
        {
            "name": "outbound|80||simple-backend.istioinaction.svc.cluster.local",
            "addedViaApi": true,
            "hostStatuses": [
                {
                    "address": {
                        "socketAddress": {
                            "address": "10.10.0.17",
                            "portValue": 8080
                        }
                    "weight": 1,
                    "locality": {}
                ...
                {
                    "address": {
                        "socketAddress": {
                            "address": "172.18.255.202",
                            "portValue": 15443
                        }
                    "weight": 1,
                    "locality": {}
    ...

     

    • 좋다! 그러나 워크로드가 트래픽을 라우팅할 때 자신의 지역 내 워크로드를 우선하도록 지역 인식 로드 밸런싱을 사용하면 성능을 더 개선할 수 있다. However, performance can be improved further using locality-aware load balancing so workloads prioritize routing traffic to workloads within their locality.
    • 앞선 장들에서 클라우드 프로바이더가 지역성 정보를 노드에 레이블로 추가한다는 점을 언급한 바 있다. We mentioned in previous chapters that cloud providers add the locality information into nodes as labels.
    • 이스티오는 레이블을 추출한 이 정보를 사용해 워크로드의 지역을 설정한다. Istio uses this information retrieved from the labels to configure the locality of workloads.

    클러스터 간 지역 인식 라우팅 검증하기 VERIFYING LOCALITY-AWARE ROUTING ACROSS CLUSTERS

    더보기

    Step1. 실습을 위해 노드에 지역성 정보 레이블을 설정하자.

    #
    kwest label node west-control-plane 'topology.kubernetes.io/region=westus'
    kwest label node west-control-plane 'topology.kubernetes.io/zone=0'
    kwest get node -o yaml
    ...
          topology.kubernetes.io/region: westus
          topology.kubernetes.io/zone: "0"
    ...
    
    keast label node east-control-plane 'topology.kubernetes.io/region=eastus'
    keast label node east-control-plane 'topology.kubernetes.io/zone=0'
    keast get node -o yaml
    ...
          topology.kubernetes.io/region: eastus
          topology.kubernetes.io/zone: "0"
    ...
    
    # istio eds 에 정보 반영을 위해 파드 재기동하자 : isiotd 가 노드의 지역성 정보 레이블을 엔드포인트 설정할 때 워크로드로 전파.
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
    ...
                    "weight": 1,
                    "locality": {}
    ...
    
    kwest rollout restart -n istio-system deploy/istio-ingressgateway
    kwest rollout restart -n istio-system deploy/istio-eastwestgateway
    kwest rollout restart -n istioinaction deploy/simple-backend-west
    keast rollout restart -n istio-system deploy/istio-ingressgateway
    keast rollout restart -n istio-system deploy/istio-eastwestgateway
    keast rollout restart -n istioinaction deploy/simple-backend-east
    
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
    ...
                    "weight": 1,
                    "locality": {
                        "region": "eastus", # east-cluster 에 있는 워크로드의 위치 정보
                        "zone": "0"
                    }
       ...
                    "weight": 1,
                    "locality": {
                        "region": "westus", # west-cluster 에 있는 워크로드의 위치 정보
                        "zone": "0"
                    }
    ...

     

    • 지역성 정보를 사용하려면 수동적 passive 헬스 체크가 필수라고 했던 것을 기억하자.

    Step2. 엔드포인트 상태를 수동적으로 확인하도록, 이상값 감지를 사용하는 DestinationRole 을 적용해보자.

    #
    cat ch12/locality-aware/west/simple-backend-dr.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: simple-backend-dr
      namespace: istioinaction
    spec:
      host: simple-backend.istioinaction.svc.cluster.local
      trafficPolicy:
        connectionPool:
          http:
            http2MaxRequests: 10
            maxRequestsPerConnection: 10
        outlierDetection:
          consecutive5xxErrors: 1
          interval: 20s
          baseEjectionTime: 30s
    
    kwest apply -f ch12/locality-aware/west/simple-backend-dr.yaml
    kwest get gw,vs,dr -n istioinaction
    
    # 확인
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local'
    ENDPOINT                 STATUS      OUTLIER CHECK     CLUSTER
    10.10.0.18:8080          HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local
    172.18.255.202:15443     HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local

     

    • 몇 초 정도 걸려 설정이 전파되면, 요청이 지역 정보를 사용해 동일 클러스터 안에서 라우팅 되는 것을 확인할 수 있다.
    # 
    EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP
    docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body"
    
    # 동일 클러스터 안에서 라우팅 되는 것을 확인
    for i in {1..20}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; echo ; done | sort | uniq -c
     20 "Hello from WEST"

     

    • 기대한 대로 모든 요청은 west-cluster 내에서 라우팅되는데, 트래픽을 라우팅하는 인그레스 게이트웨이에서 가장 가깝기 때문이다.
    • 또한 모든 라우팅 결정을 엔보이 프록시가 내리므로, 컨트롤 플레인이 엔보이 프록시의 설정을 수정했으리라고 결론지을 수 있다.
    #
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json
    ...
                    "weight": 1,
                    "locality": { 
                        "region": "westus", # priority 가 없으면(생략 시), 0으로 우선 순위가 가장 높음
                        "zone": "0"
                    }
                ...
                    "weight": 1,
                    "priority": 1, # priority 0 다음으로, 두 번쨰 우선순위
                    "locality": {
                        "region": "eastus",
                        "zone": "0"
                    }
    ...

     

    • 우선순위가 가장 높은 호스트가 사용할 수 없는 상태가 되면, 트래픽우선순위가 낮은 호스트로 라우팅된다.

     


    클러스터 간 장애 극복 확인하기 VERIFYING CROSS-CLUSTER FAILOVER

    더보기
    • 간단한 백엔드 디플로이먼트가 실패하는 상황을 시뮬레이션하려면, 환경 변수 ERROR_RATE 값을 1로 설정해 요청이 실패하게 만들자.
    • 시간이 조금 지나면, 이상값 감지가 호스트가 비정상임을 감지하고 트래픽을 우선순위가 두 번째인 east-cluster 워크로드로 라우팅한다.
    # 신규 터미널 : 반복 접속 해두기
    while true; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
    ...
    "Hello from WEST"
                     2025-05-18 09:31:21
    
    "Hello from EAST" # failover 시점 
                     2025-05-18 09:31:23
    ...
    
    
    #
    kwest -n istioinaction set env deploy simple-backend-west ERROR_RATE='1'
    kwest exec -it -n istioinaction deploy/simple-backend-west -- env | grep ERROR
    ERROR_RATE=1
    
    #
    iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local'
    ENDPOINT                 STATUS      OUTLIER CHECK     CLUSTER
    10.10.0.21:8080          HEALTHY     FAILED            outbound|80||simple-backend.istioinaction.svc.cluster.local
    172.18.255.202:15443     HEALTHY     OK                outbound|80||simple-backend.istioinaction.svc.cluster.local

     

    이 결과는 클러스터 간 장애 극복이 작동함을 보여준다.
    • 클러스터 간 트래픽은 상대 클러스터의 east-west 게이트웨이를 통과하며 SNI 통과로 처리된다는 것을 알 수 있다. As seen in this detailed walkthrough, cross-cluster traffic traverses the opposite cluster’s east-west gateway and is treated as an SNI passthrough.
    • 이는 원격 클러스터에 도달한 트래픽의 로드 밸런싱에 영향을 미친다. This has implications for load balancing once traffic reaches the remote cluster.
    • 이 호출은 SNI/TCP 커넥션이라 게이트웨이가 TLS 커넥션을 종료하지 않으므로, east-west 게이트웨이는 커넥션을 그대로 백엔드 서비스에 전달할 수 밖에 없다. Since this call is an SNI/TCP connection and the gateway does not terminate the TLS connection, the east-west gateway can only forward the connection as is to the backend service.
    • 이렇게 되면 커넥션이 east-west 게이트웨이에서 백엔드 서비스까지 이어지기 때문에 요청 단위로 로드 밸런싱되지 않는다. This opens a connection from the east-west gateway to the backend service and does not have request-level load-balancing capabilities.
    • 그러므로 클러스터 사이의 장애 극복이나 로드 밸런싱에서는, 클라이언트의 관점으로는 부하 분산이나 장애 극복이 수행되지만 트래픽이 원격 클러스터의 모든 인스턴스 사이에서 반드시 균등하게 분산되는 것은 아니다. Thus, on failover or load balancing across multiple clusters, the load is balanced or failed over from the client’s point of view but not necessarily balanced evenly across all instances on the remote cluster.

    # (옵션)
    keast get deploy -n istioinaction simple-backend-east
    keast scale deploy -n istioinaction simple-backend-east --replicas 2
    keast get pod -n istioinaction -l app=simple-backend -owide
    NAME                                   READY   STATUS    RESTARTS   AGE   IP           NODE                 NOMINATED NODE   READINESS GATES
    simple-backend-east-7b7ccfcbcf-k7fm9   2/2     Running   0          26m   10.20.0.21   east-control-plane   <none>           <none>
    simple-backend-east-7b7ccfcbcf-pv85h   2/2     Running   0          26m   10.20.0.20   east-control-plane   <none>           <none>
    
    keast logs -n istio-system -l app=istio-eastwestgateway -f
    [2025-05-18T01:10:01.546Z] "- - -" 0 - - - "-" 20432 21767 56383 - "-" "-" "-" "-" "10.20.0.20:8080" outbound_.80_._.simple-backend.istioinaction.svc.cluster.local 10.20.0.17:45728 10.20.0.17:15443 10.20.0.1:40730 outbound_.80_._.simple-backend.istioinaction.svc.cluster.local -
    [2025-05-18T01:09:35.947Z] "- - -" 0 - - - "-" 20432 21768 84477 - "-" "-" "-" "-" "10.20.0.21:8080" outbound_.80_._.simple-backend.istioinaction.svc.cluster.local 10.20.0.17:40328 10.20.0.17:15443 10.20.0.1:59643 outbound_.80_._.simple-backend.istioinaction.svc.cluster.local -
    ...

     

     


    인가 정책을 사용해 클러스터 간 접근 제어 확인하기

        - VERIFYING CROSS-CLUSTER ACCESS CONTROL USING AUTHORIZATION POLICIES

    더보기
    • 마지막으로 확인할 기능은 클러스터 사이의 접근 제어다. verify is access control across clusters
    • 접근 제어를 하려면 워크로드가 트래픽을 서로 인증해, 트래픽을 승인하거나 거부하는 결정을 내리는 데 사용 가능한 믿을 수 있는 메타데이터를 만들어야 한다는 점을 기억하자.
    • 이를 시연하기 위해 시나리오를 생각해보자.
    • 트래픽 출처가 인그레스 게이트웨이인 경우에만 서비스로의 트래픽을 허용하고 싶다고 해보자. 출처가 다르면 거절한다.
    • 그렇게 하기 위한 정책을 정의해 ch12/security/allow-only-ingress-policy.yaml 파일에 저장해뒀다. 이를 east-cluster 에 적용하자.
    # 적용 전에 west-cluster 서비스를 제거해서 east 에서만 트래픽을 처리하게 하자 >> 이미 위에서 장애 상황이라 안해도 되긴함
    kwest delete deploy simple-backend-west -n istioinaction
    
    #
    cat ch12/security/allow-only-ingress-policy.yaml
    apiVersion: "security.istio.io/v1beta1"
    kind: "AuthorizationPolicy"
    metadata:
      name: "allow-only-ingress"
      namespace: istioinaction
    spec:
      selector:
        matchLabels:
          app: simple-backend
      rules:
      - from:
        - source:
            principals: ["cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"]
    
    keast apply -f ch12/security/allow-only-ingress-policy.yaml
    keast get authorizationpolicy -A

     

    • 업데이트가 전파되고 나면, west-cluster 의 워크로드에서 요청을 만들어 정책을 시험해보자. 이를 위해 임시 파드를 실행한다.
    #
    kwest run netshoot -n istioinaction --rm -it --image=nicolaka/netshoot -- zsh
    -----------------------------------
    #
    curl -s webapp.istioinaction/api/catalog
    
    # 직접 요청하면 실패!
    curl -s simple-backend.istioinaction.svc.cluster.local
    RBAC: access denied
    
    # istio-ingressgateway 로 요청하면 성공!
    curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system
    ...
    
    # kiali 등 확인을 위해 반복 접속 실행
    watch curl -s simple-backend.istioinaction.svc.cluster.local
    watch 'curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system'
    
    exit
    -----------------------------------
    • 정책이 인그레스 게이트웨이에서 들어온 트래픽을 허용한 것을 볼 수 있다.
    • 이는 워크로드가 클러스터 간에 상호 인증해서 정책이 ID 인증서에 인코딩된 인증 데이터를 접근 제어에 사용할 수 있음을 보여준다.

    • 로드 밸런싱, 지역 인식 라우팅, 클러스터 간 장애 극복, 상호 인증 트래픽, 접근 제어에 대한 우리의 모든 예제는 다중 클러스터 서비스 메시에서의 워크로드가 실행 중인 클러스터가 어디든 상관없이 이스티오의 모든 기능을 사용할 수 있음을 보여준다. 그리고 설정을 따로 더 하지 않아도 그럴 수 있다.
    • 바라건데, 이번 장이 이스티오가 조직 내에서 어떻게 확장하는지, 어떻게 여러 클러스터를 단일 메시로 통합하는지, 여러 조직에게 그것이 왜 중요한지를 충분히 보여줬길 바란다.
    • 다름장에서는 가상머신을 서비스 메시로 통합해본다. 이는 레거시 워크로드를 운영해야 하는 성숙한 기업에게 아주 가치 있는 기능이다.

     

     

    ▶ Summary

    더보기
    • 이스티오는 단일 컨트롤 플레인(기본-원격), 복제된 컨트롤 플레인(기본-기본), 외부 컨트롤 플레인이라는 세 가지 다중 서비스 메시 배포 모델을 지원한다. Istio supports three multi-cluster service mesh deployment models: single control plane (primary-remote), replicated control planes (primary-primary), and external control plane.
    • istio-system 네임스페이스에 중간 인증서를 설치해 플러그인 CA 인증서를 사용하면 클러스터 간에 공통 신뢰를 구축할 수 있다. We can establish common trust across clusters using plug-in CA certificates by installing intermediate certificates in the istio-system namespace.
    • 복제된 컨트롤 플레인 배포 모델에서 클러스터 간 워크로드를 찾는 방법은 원격 클러스터의 서비스 어카운트를 ID로 사용하고 시크릿으로 서비스 어카운트 토큰을 상대편 클러스터에서 사용할 수 있게 하는 것이다. Cross-cluster workloads are discovered in replicated control-plane deployment models using service accounts as an identity in the remote cluster and making the service account token available to the opposite cluster as a secret.
    • east-west 게이트웨이를 사용해 다중 네트워크 서비스 메시의 네트워크를 연결할 수 있다. sni-dnat 라우터 모드는 클러스터 간 트래픽을 세밀한 방식으로 라우팅하도록 SNI 클러스터를 설정한다. We can bridge the networks of multi-network service meshes using east-west gateways. The sni-dnat router mode configures SNI clusters to route crosscluster traffic in a fine-grained manner.
    • east-west 게이트웨이는 트래픽을 자동으로 통과시키고 자동으로 설정된 SNI 클러스터를 바탕으로 라우팅하도록 설정할 수 있다. The east-west gateway can be configured to auto passthrough traffic and route based on the automatically configured SNI clusters.
    • 이스티오의 기능은 클러스터 간에도 단일 클러스터일 때와 같은 방식으로 동작한다. Istio’s capabilities work across clusters in the same way they do within a cluster.

    [ 실습 후 kind 삭제 ]

    kind delete cluster --name west && kind delete cluster --name east && docker rm -f mypc

     


    14장 이스티오의 요청 처리 기능 확장하기

    This chapter covers Extending Istio on the request path

    https://www.anyflow.net/sw-engineer/istio-overview-4

    • 엔보이 필터 이해하기
    • 이스티오의 EnvoyFilter 리소스를 사용해 엔보이 직접 설정하기 Using Istio’s EnvoyFilter resource to configure Envoy directly
    • 루아 Lua 를 사용해 요청 경로 커스터마이징하기 Using Lua to customize the request path
    • 웹어셈블리 WebAssembly 를 사용해 요청 경로 커스터마이징하기 Using WebAssembly to customize the request path

    ▶ 들어가며

    더보기
    • 지금까지 살펴본 것처럼, 이스티오는 애플리케이션 네트워킹 기능으로 조직에 많은 가치를 제공할 수 있다.
    • 그렇지만 이스티오를 채택하는 조직에는 이스티오의 기본 버전이 충족하지 못하는 제약이나 가정이 있을 가능성이 높다.
    • 따라서 이런 제약을 잘 충족시키도록 이스티오 기능의 확장이 필요할 것이다. You will likely need to extend Istio’s capabilities to more nicely fit within these constraints.

    • 3장에서 살펴봤고 이 책에서 내내 강조했듯이, 엔보이 프록시는 이스티오 서비스 메시의 기본 구성 요소다.
    • 엔보이는 메시 내부 서비스 사이의 요청 경로에서 애플리케이션 인스턴스와 함께 있는 서비스 프록시다.
    • 엔보이에 서비스의 애플리케이션 네트워킹을 간단하게 만들어줄 수 있는 중요한 기능 집합이 있기는 하지만, 맞춤형 통합 혹은 통합 최종 단계를 위해서는 엔보이를 강화해야 하는 상황에 직면할 수 있다. 다음은 그 확장의 예시들이다.
      • 속도 제한 rate limiting 혹은 외부 인가 서비스와 통합 Integrating with rate limiting or external authorization services
      • 헤더 추가, 제거, 수정 Adding, removing, or modifying headers
      • 요청 페이로드를 보강하기 위해 다른 서비스 호출 Calling out to other services to enrich a request payload
      • HMAC 서명/검증 같은 사용자 정의 프로토콜 구현 Implementing custom protocols like HMAC signing/verification
      • 비표준 보안 토큰 처리 Non-standard security token handling
    • 엔보이는 사용자에게 필요한 거의 모든 것을 제공할 수 있지만, 결국은 사용자의 특정 사용 사례에 맞게 맞춤 조정해야 한다.
    • 이번 장에서는 요청 경로에서 이스티오를 확장하는 것을 다루는데, 이는 필연적으로 엔보이 확장을 의미하게 된다.

    14.1 엔보이의 확장 기능

    [강력 추천 공식 문서] Envoy Life of a Request - Docs

    더보기

    Request flow - Docs

    1. A TCP connection from downstream is accepted by an Envoy listener running on a worker thread.
    2. The listener filter chain is created and runs. It can provide SNI and other pre-TLS info. Once completed, the listener will match a network filter chain. Each listener may have multiple filter chains which match on some combination of destination IP CIDR range, SNI, ALPN, source ports, etc. A transport socket, in our case the TLS transport socket, is associated with this filter chain.
    3. On network reads, the TLS transport socket decrypts the data read from the TCP connection to a decrypted data stream for further processing.
    4. The network filter chain is created and runs. The most important filter for HTTP is the HTTP connection manager, which is the last network filter in the chain.
    5. The HTTP/2 codec in HTTP connection manager deframes and demultiplexes the decrypted data stream from the TLS connection to a number of independent streams. Each stream handles a single request and response.
    6. For each HTTP stream, an Downstream HTTP filter chain is created and runs. The request first passes through CustomFilter which may read and modify the request. The most important HTTP filter is the router filter which sits at the end of the HTTP filter chain. When decodeHeaders is invoked on the router filter, the route is selected and a cluster is picked. The request headers on the stream are forwarded to an upstream endpoint in that cluster. The router filter obtains an HTTP connection pool from the cluster manager for the matched cluster to do this.
    7. Cluster specific load balancing is performed to find an endpoint. The cluster’s circuit breakers are checked to determine if a new stream is allowed. A new connection to the endpoint is created if the endpoint’s connection pool is empty or lacks capacity.
    8. For each stream an Upstream HTTP filter chain is created and runs. By default this only includes the CodecFilter, sending data to the appropriate codec, but if the cluster is configured with an upstream HTTP filter chain, that filter chain will be created and run on each stream, which includes creating and running separate filter chains for retries and shadowed requests.
    9. The upstream endpoint connection’s HTTP/2 codec multiplexes and frames the request’s stream with any other streams going to that upstream over a single TCP connection.
    10. The upstream endpoint connection’s TLS transport socket encrypts these bytes and writes them to a TCP socket for the upstream connection.
    11. The request, consisting of headers, and optional body and trailers, is proxied upstream, and the response is proxied downstream. The response passes through the HTTP filters in the opposite order from the request, starting at the codec filter, traversing any upstream HTTP filters, then going through the router filter and passing through CustomFilter, before being sent downstream.
    12. If independent half-close is enabled the stream is destroyed after both request and response are complete (END_STREAM for the HTTP/2 stream is observed in both directions) AND response has success (2xx) status code. Otherwise the stream is destroyed when the response is complete, even if the request has not yet completed. Post-request processing will update stats, write to the access log and finalize trace spans.

    ▶ 들어가며 : 필터 확장

    더보기
    • 엔보이 프록시의 강점 중 하나는 확장할 수 있게 만들어졌다는 점이다.
    • 엔보이의 API를 설계하는 데 많은 고민과 주의가 반영됐으며, 다른 사람들이 작성한 확장 기능도 인기를 얻게 해준 큰 이유 중 하나다.
    • 엔보이를 확장할 수 있는 주요 방법은 필터 확장이다.
    • 엔보이를 확장할 수 있는 지점이 어디인지, 무엇이 애플리케이션에 가장 큰 이점을 주는지 이해하려면 먼저 엔보이의 아키텍처를 이해해야 한다.

    14.1.1 엔보이의 필터 체인 이해하기 Understanding Envoy’s filter chaining

    더보기
    • 3장에서는 그림 14.1과 같이 엔보이의 개념인 리스너, 루트, 클러스터를 소개헀다.
    • 이것들이 고수준 개념임을 강조했지만, 이번 장에서는 더 자세히 다루기로 약속한 바 있다.
    • 여기서는 리스너와, 필터 및 필터 체인을 사용해 리스너 모델을 확장하는 방법에 중점을 둔다.

     

    • 엔보이의 리스너는 네트워킹 인터페이스에 포트를 열고 들어오는 트래픽 수신을 시작하는 방법이다.
      A listener in Envoy is a way to open a port on a networking interface and start listening to incoming traffic.
    • 궁극적으로 엔보이네트워크 커넥션에서 바이트를 가져와 어떤 방식으로 처리하는 3계층과 4계층 (L3/L4) 프록시다.
      Envoy is ultimately a layer 3 and layer 4 (L3/L4) proxy that takes bytes off a network connection and processes them in some way.
    • 이것이 아키텍처의 첫 번째 중요 부분인 필터로 이어진다.
      This brings us to the first important part of the architecture: the filter.
    • 리스너는 네트워크 스트림에서 바이트를 읽어들인 후 그림 14.2와 같이 다양한 필터 혹은 기능 단계들을 거쳐 처리한다.
      A listener reads bytes off the networking stream and processes them through various filters or stages of functionality.

    • 엔보이에서 가장 기본적인 필터는 네트워크 필터로, 바이트 스트림에서 인코딩/디코딩 작업을 한다.
      Envoy’s most basic filters are network filters, which operate on a stream of bytes for either encoding or decoding.
    • 스트림에서 필터가 둘 이상 순서대로 동작하도록 설정할 수 있는데, 이를 필터 체인이라고 부른다. 필터 체인을 사용하면 프록시의 기능을 구현할 수 있다.
      You can configure more than one filter to operate on the stream in a sequence called a filter chain, and these chains can be used to implement the functionality of the proxy.
    • 예를 들어, 엔보이에는 대표적으로 다음과 같은 프로토콜용 네트워크 필터가 있다.
      network filters for the following protocols
      • MongoDB
      • Redis
      • Thrift
      • Kafka
      • HTTP Connection Manager (HCM)

    • 가장 많이 사용하는 네트워크 필터 중 하나가 HttpConnectionManager 이다.
    • 이 필터는 바이트 스트림을 HTTP 기반 프로토콜(즉, HTTP 1.1, HTTP 2, gRPC, 최근에는 HTTP 3 등)의 HTTP 헤더, 바디, 트레일러로 변환하는 것에 관한 세부 사항을 추상화하는 역할을 하며, 그림 14.3에서 볼 수 있다. This filter is responsible for abstracting away the details of converting a stream of bytes into HTTP headers, body, and trailers for HTTP-based protocols (that is, HTTP 1.1, HTTP 2, gRPC, and recently HTTP 3, and so on) and is shown in figure 14.3.
    HCM은 바이트 스트림을 HTTP 요청으로 변환하고, 이를 헤더나 바디 세부 정보 같은 L7 속성을 바탕으로 라우팅

     

    • HttpConnectionManager 는 HTTP 요청을 처리할 뿐 아니라 헤더, 경로 접두사 path prefix 등 요청 속성을 바탕으로 한 요청 라우팅, 액세스 로깅, 헤더 조작도 처리한다.
    • 또 HCM에는 필터 기반 아키텍처가 있어 HTTP 요청에서 동작하는 HTTP 필터를 순서대로 또는 연쇄로 설정하거나 구축할 수 있다. The HCM also has a filter-based architecture that allows you to build or configure HTTP filters into a sequence or chain of filters that operate on an HTTP request.
    • 기본 HTTP 필터의 예시는 다음과 같다, 전체목록 - Docs
      • Cross-origin resource sharing (CORS)
      • Cross-site request forgery prevention (CSRF)
      • ExternalAuth 외부 인증
      • RateLimit 속도 제한
      • Fault injection 결합 주입
      • gRPC/JSON transcoding 트랜스코딩
      • Gzip
      • Lua 루아
      • Role-based access control (RBAC) 역할 기반 접근 제어
      • Tap
      • Router 라우터
      • WebAssembly (Wasm) 웹어셈블리

    • HTTP 요청 하나에서 HTTP 필터가 순서대로 동작하도록 설정할 수 있다. HTTP filters can be configured in a sequence to operate on an HTTP request.
    • HTTP 필터들은 요청을 업스트림 클러스터로 보내는 터미널 필터로 끝나야 한다. The HTTP filters must end in a terminal filter that sends the request to an upstream cluster.
    • 이를 담당하는 HTTP 필터는 라우터 필터로 그림 14.14에 표시돼 있다. The HTTP filter responsible for this is the router filter, shown in figure 14.4.
    • 라우터 필터는 요청을 적절한 업스트림 클러스터를 찾아 전달하며, 이때 타임아웃과 재시도 파라미터를 설정할 수 있다 - Docs The router filter matches requests to upstream clusters with configurable timeout and retry parameters.
    HCM 에는 HTTP 요청을 처리하는 필터 체인이 있으며, 라우팅 필터로 끝난다

     

    https://www.anyflow.net/sw-engineer/istio-overview-4

     

    • 또한 사용자는 엔보이의 핵심 코드를 변경하지 않고도 자신만의 필터를 직접 작성하고 프록시 위에 얹어 계층화할 수 있다. Users can also write their own filters and layer them on top of the proxy without having to change any of Envoy’s core code.
    • 예를 들어, 이스티오의 프록시는 데이터 플레인용으로 엔보이 위에 필터를 추가해 커스텀 엔보이를 빌드한다. For example, Istio’s proxy adds filters on top of Envoy and builds a custom Envoy for its data plane.
    • 그렇지만 이렇게 커스텀 엔보이 프록시 빌드를 도입하면, 유지 보수할 것이 많아질 수 있고 개발자가 C++를 사용해야 한다. However, this introduces a custom Envoy proxy build that can be a lot to maintain and require developers to use C++.

    14.1.2 확장용 필터 Filters intended for extension

    더보기
    • 자신만의 필터를 C++로 작성해 프록시에 내장할 수 있지만, 이와 관련된 내용은 이 책의 범위를 벗어난다.
    • 필터 작성 등과 같이 엔보이 바이너리 자체에 변경 사항을 컴파일하지 않고도 엔보이의 HTTP 기능을 확장하는 방법이 있는데, 이 경우 다음 HTTP 필터들을 사용한다. There are ways to extend Envoy’s HTTP capabilities, including writing filters, without compiling changes into the Envoy binary itself, by using the following HTTP filters:
      • 외부 처리 External processing
      • 루아 Lua
      • 웹어셈블리 Wasm (WebAssembly)
    • 이런 필터를 사용하면 외부 서비스를 호출하도록 설정하거나, 루아 스크립트를 실행하거나, 커스텀 코드를 실행해 HTTP 요청이나 응답을 처리할 때 HCM의 기능을 강화할 수 있다.
      With these filters, you can configure calls out to an external service, run a Lua script, or run custom code to enhance the capabilities of the HCM when processing HTTP requests or responses.
    • 외부 서비스를 호출해 처리하는 부분에 대해 이야기할 때는 속도 제한 필터를 중점을 두겠다.
      For calling an external service for processing, we’ll focus on the rate-limiting filter.
    • 또한 9장에서 다룬 것처럼 외부 인가를 요청할 수도 있다.

    ☞ 엔보이에는 범용 처리 목적으로 외부 서비스를 호출하기 위한 외부 처리 필터가 있다. 이 필터는 코드베이스에 존재하기는 하지만, 저술 시점에는 아무것도 하지 않는다. 우리는 전역 속도 제한 필터를 사용하는 등 외부 서비스를 호출하는 다른 방법에 초점을 맞춘다.  

     

    14.1.3 이스티오의 데이터 플레인 커스터마이징하기 Customizing Istio’s data plane

    더보기
    • 엔보이의 필터 아키텍처를 깊이 있게 이해한 것을 바탕으로, 이후 절들에서 다음 방법 중 하나를 사용해 엔보이 데이터 플레인의 기능을 확장한다.
      • 엔보이 HTTP 필터를 이스티오 API에서 EnvoyFilter 리소스로 설정하기
      • 속도 제한 서버 RLS, Rate-Limit Server 호출하기
      • 루아 스크립트를 구현해 루아 HTTP 필터에 불러오기
      • 웹어셈블리 HTTP 필터용 웹어셈블리 모듈(Wasm 모듈) 구현하기

    • 엔보이의 필터를 직접 설정하는 방법을 이해해야 하며, 이를 위해 이스티오의 EnvoyFilter 리소스를 사용할 것이다.
    • 이전 장들에서 이미 사용해봤지만, 여기서 더 자세히 살펴본다.

    14.2 EnvoyFilter 리소스로 엔보이 필터 설정하기

    [실습 환경 구성] k8s(1.23.17) 배포 & mypc 컨테이너

    더보기
    #
    git clone https://github.com/AcornPublishing/istio-in-action
    cd istio-in-action/book-source-code-master
    pwd # 각자 자신의 pwd 경로
    code .
    
    # 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
    kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP
        hostPort: 30000
      - containerPort: 30001 # Prometheus
        hostPort: 30001
      - containerPort: 30002 # Grafana
        hostPort: 30002
      - containerPort: 30003 # Kiali
        hostPort: 30003
      - containerPort: 30004 # Tracing
        hostPort: 30004
      - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
        hostPort: 30005
      - containerPort: 30006 # TCP Route
        hostPort: 30006
      - containerPort: 30007 # kube-ops-view
        hostPort: 30007
      extraMounts: # 해당 부분 생략 가능
      - hostPath: /Users/daniel0324/Workspaces/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정
        containerPath: /istiobook
    networking:
      podSubnet: 10.10.0.0/16
      serviceSubnet: 10.200.0.0/22
    EOF
    
    # 설치 확인
    docker ps
    
    # 노드에 기본 툴 설치
    docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
    
    # (옵션) kube-ops-view
    helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:30007/#scale=1.5"
    open "http://localhost:30007/#scale=1.3"
    
    # (옵션) metrics-server
    helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
    helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
    kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
    • kind docker network 에 테스트용 PC(실제로는 컨테이너) 배포
    # kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역
    docker network ls
    docker inspect kind
    
    # mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용
    docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시
    혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행
    docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시
    docker ps
    
    # kind network 중 컨테이너(노드) IP(대역) 확인
    docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}'
    /mypc 172.18.0.100
    /myk8s-control-plane 172.18.0.2
    
    # 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인!
    docker exec -it mypc ping -c 1 172.18.0.2
    docker exec -it mypc ping -c 1 myk8s-control-plane
    docker exec -it myk8s-control-plane ping -c 1 mypc

     

    [실습 환경 구성] (편리성 설정) MetalLB 배포

    더보기

    Step1. MetalLB 배포

    # MetalLB 배포
    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml
    
    # 확인
    kubectl get crd  
    kubectl get pod -n metallb-system
    
    # IPAddressPool, L2Advertisement 설정 : MetalLB 파드(speaker) 배포 정상 완료 후 아래 설정 실행하자.
    cat << EOF | kubectl apply -f -
    apiVersion: metallb.io/v1beta1
    kind: IPAddressPool
    metadata:
      name: default
      namespace: metallb-system
    spec:
      addresses:
      - 172.18.255.101-172.18.255.120
    ---
    apiVersion: metallb.io/v1beta1
    kind: L2Advertisement
    metadata:
      name: default
      namespace: metallb-system
    spec:
      ipAddressPools:
      - default
    EOF
    
    # 확인
    kubectl get IPAddressPool,L2Advertisement -A

    Step2. 샘플 어플리케이션 배포 후 확인

    #
    cat << EOF | kubectl apply -f -
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx:latest
            ports:
            - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-service
    spec:
      selector:
        app: nginx
      ports:
      - port: 80
        targetPort: 80
      type: LoadBalancer
    EOF
    
    
    # 확인
    kubectl get deploy,pod,svc,ep 
    kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
    LBIP=$(kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    
    # 외부?에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다!
    docker exec -it mypc curl -s $LBIP
    docker exec -it mypc curl -s $LBIP -v -I
    
    
    # 확인 후 삭제
    kubectl delete deploy,svc --all

     

     


    [실습 환경 구성] istio 1.17.8 설치 - Docs , Install , profile

     

    # myk8s-control-plane 진입 후 설치 진행
    docker exec -it myk8s-control-plane bash
    -----------------------------------
    # (옵션) 코드 파일들 마운트 확인
    tree /istiobook/ -L 1
    혹은
    git clone ... /istiobook
    
    # istioctl 설치
    export ISTIOV=1.17.8
    echo 'export ISTIOV=1.17.8' >> /root/.bashrc
    
    curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
    cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
    
    # demo 프로파일 컨트롤 플레인 배포
    istioctl install --set profile=demo --set values.global.proxy.privileged=true -y
    
    # 보조 도구 설치
    kubectl apply -f istio-$ISTIOV/samples/addons
    
    # 빠져나오기
    exit
    -----------------------------------
    
    # 설치 확인 : istiod, istio-ingressgateway, crd 등
    kubectl get istiooperators -n istio-system -o yaml
    kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
    kubectl get cm -n istio-system istio -o yaml
    kubectl get crd | grep istio.io | sort
    
    # 실습을 위한 네임스페이스 설정
    kubectl create ns istioinaction
    kubectl label namespace istioinaction istio-injection=enabled
    kubectl get ns --show-labels
    
    # istio-ingressgateway 서비스 : nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
    kubectl describe svc -n istio-system istio-ingressgateway
    
    # NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
    kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
    kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
    kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
    kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
    
    # Prometheus 접속 : envoy, istio 메트릭 확인
    open http://127.0.0.1:30001
    
    # Grafana 접속
    open http://127.0.0.1:30002
    
    # Kiali 접속 : NodePort
    open http://127.0.0.1:30003
    
    # tracing 접속 : 예거 트레이싱 대시보드
    open http://127.0.0.1:30004

     들어가며 : tap 필터 (실습~)

    더보기
    • 이스티오의 데이터 플레인을 확장하는 첫 번째 단계는 엔보이의 기존 필터가 우리가 찾고 있는 확장 유형을 달성하는 데 충분하지 파악하는 것이다. The first step in extending Istio’s data plane is to figure out whether an existing filter in Envoy is sufficient to accomplish the type of extension we’re looking for.
    • 필터가 존재하는 경우, EnvoyFilter 리소스를 사용해 이스티오의 데이터 플레인을 직접 설정할 수 있다. If one exists, we can use the EnvoyFilter resource to directly configure Istio’s data plane.

    • 이스티오의 API는 일반적으로 특정 네트워크나 보안 시나리오에 초점을 맞춰 기저 underlying 엔보이설정을 추상화한다. Istio’s APIs generally abstract away the underlying Envoy configuration, focusing on specific networking or security scenarios.
    • VirtualService, DestinationRule, and AuthorizationPolicy 같은 리소스는 모두 결국 엔보이 설정으로 변환되며, 필터 체인에서 특정 HTTP 필터를 설정하기도 한다. Resources like VirtualService, DestinationRule, and AuthorizationPolicy all end up getting translated to an Envoy configuration and potentially configure specific HTTP filters in a filter chain.
    • 이스티오는 기저 엔보이에서 할 수 있는 모든 필터나 설정을 노출하려고 시도하지 않으며, 따라서 엔보이를 직접 설정해야 하는 상황이 있을 수 있다. Istio does not try to expose every possible filter or configuration for the underlying Envoy proxy, and there may be cases where we need to configure Envoy directly.
    • 이스티오의 EnvoyFilter 리소스는 이스티오의 고수준 API에서 노출하지 않는 엔보이의 일부를 설정하거나 조정해야 하는 고급 사용 사례를 위한 것이다. Istio’s EnvoyFilter resource is intended for advanced use cases where a user needs to either tweak or configure a portion of Envoy not exposed by Istio’s higher-level APIs.
    • 리소스는 엔보이의 거의 모든 것을 설정할 수 있으며(일부 제한 있음) 리스너, 루트, 클러스터, 필터가 포함된다. This resource can configure just about anything (with some limitations) in Envoy, including listeners, routes, clusters, and filters.

    • EnvoyFilter 리소스는 이스티오의 고급 사용자용이며 비상 수단이다. The EnvoyFilter resource is intended for advanced usage of Istio and is a 'break glass' solution.
    • 기저 엔보이 API는 이스티오 버전 간에 언제든 달라질 수 있으니, 배포하는 모든 EnvoyFilter 의 유효성을 반드시 확인해야 한다. The underlying Envoy API may change at any time between releases of Istio, so be sure to validate any EnvoyFilter you deploy.
    • 이전 버전과의 호환성은 어떤 것도 가정해서는 안 된다. 이 API를 잘못 설정하면 이스티오 데이터 플레인 전체가 멈출 수도 있다. Do not assume any backward compatibility here. Bad configuration with this API can potentially take down the entire Istio data plane.

     

    ▶ 예제를 통해 Istio Filter 의 동작원리에 대해 이해해 보도록 하자!!  ( 실습 ~ )

    더보기

    Step1. 서비스 배포 및 환경 설정

    #
    kubectl get envoyfilter -A
    NAMESPACE      NAME                    AGE
    istio-system   stats-filter-1.13       12m
    istio-system   stats-filter-1.14       12m
    istio-system   stats-filter-1.15       12m
    istio-system   stats-filter-1.16       12m
    istio-system   stats-filter-1.17       12m
    istio-system   tcp-stats-filter-1.13   12m
    istio-system   tcp-stats-filter-1.14   12m
    istio-system   tcp-stats-filter-1.15   12m
    istio-system   tcp-stats-filter-1.16   12m
    istio-system   tcp-stats-filter-1.17   12m
    
    # 배포
    kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
    kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
    kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
    kubectl apply -f ch9/sleep.yaml -n istioinaction
    
    # 확인
    kubectl get gw,vs,dr -n istioinaction
    NAME                                            AGE
    gateway.networking.istio.io/coolstore-gateway   50s
    
    NAME                                                       GATEWAYS                HOSTS                         AGE
    virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   50s
    
    
    # 호출 확인 : mypc
    EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP
    docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
    
    # 신규 터미널 : mypc 에서 반복 접속 
    EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    while true; do docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
    
    # 호출 확인 : 자신의 호스트 PC
    curl -s http://webapp.istioinaction.io:30000
    curl -s http://webapp.istioinaction.io:30000/api/catalog
    • webapp 서비스를 거쳐 흐르는 특정 요청을 디버깅할 도구로 데이터 플레인을 확장하고 싶다고 하자.
      Suppose we want to extend our data plane with tooling to debug certain requests that flow through the webapp service.
    • 엔보이를 커스텀 필터로 확장할 수도 있지만, 충분히 살펴보니 이런 기능을 위한 tap 필터가 있음을 발견했다. We can extend Envoy with some custom filters, but if we look thoroughly enough, we see that a Tap filter exists for this type of functionality.
    • 이스티오의 API가 이 필터를 노출하고 있지는 않으므로, webapp 서비스에 이 필터를 설정하려면 EnvoyFilter 리소스를 사용하면 된다.
      It is not exposed by Istio’s APIs, so we can use the EnvoyFilter resource to configure this filter for our webapp service.
    • EnvoyFilter 리소스에 대해 알아야 할 첫 번째 사항은, 달리 지정하지 않은 한 네임스페이스의 모든 워크로드에 적용된다는 것이다.
      The first thing to know about an EnvoyFilter resource is that it applies to all workloads in the namespace for which it is declared, unless you specify otherwise.
    • istio-system 네임스페이스에 EnvoyFilter 리소스를 만들었다면 메시의 모든 워크로드에 적용된다.
      If you create an EnvoyFilter resource in the istio-system namespace, it will be applied to all workloads in the mesh.
    • 네임스페이스 내에서 커스텀 EnvoyFilter 설정을 적용할 워크로드를 좀 더 특정하고 싶다면, 예제에서 보겠지만 workloadSelector 를 사용하면 된다.
      If you want to be more specific about the workloads in a namespace to which the custom EnvoyFilter configuration applies, you can use a workloadSelector, as we’ll see in our example.
    • EnvoyFilter 리소스에 대해 알아야 할 두 번째 사항은, 다른 이스티오 리소스가 모두 변환되고 설정된 후에야 적용된다는 것이다. The second thing to know about an EnvoyFilter resource is that it applies after all other Istio resources have been translated and configured.
    • 예를 들어 VirtualService or DestinationRule 리소스가 있다면, 이 설정들이 먼저 데이터 플레인에 적용된다. For example, if you have VirtualService or DestinationRule resources, those configurations are applied to the data plane first.
    • 마지막으로, EnvoyFilter 리소스로 워크로드를 설정할 때 각별히 주의해야 한다. Finally, you should take great care when configuring a workload with the Envoy Filter resource.
    • 따라서 엔보이 명명 규칙과 설정 세부 사항을 잘 알아두자. 이는 정말 이스티오 API의 고급 사용법으로, 잘못 설정하면 메시를 마비시킬 수 있다. You should be familiar with Envoy naming conventions and configuration specifics. This really is an advanced usage of Istio’s API and can bring down your mesh if misconfigured.

    Step2. Tap 필터 설정 

    • 이 예제에서는 그림 14.5와 같이 webapp 워크로드의 데이터 플레인을 거치는 메시지샘플링하도록 엔보이의 tap 필터를 설정하려고 한다 - Docs
      In our example, we want to configure Envoy’s tap filter to sample messages that go over the data plane for the webapp workload, as shown in figure 14.5.
    • 요청이나 응답이 tap 필터를 지나 흐를 때마다 tap 필터는 그 요청/응답을 어떤 수신 에이전트로 스트리밍한다. 이 예제에서는 콘솔/CLI로 스트리밍한다.
      Every time a request or response flows over the tap filter, it streams it out to some listening agent. In this example, we stream it out to the console/CLI.

     

    ▶ EnvoyFilter 리소스

    # cat ch14/tap-envoy-filter.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: tap-filter
      namespace: istioinaction
    spec:
      workloadSelector:
        labels:
          app: webapp # 워크로드 셀렉터
      configPatches:
      - applyTo: HTTP_FILTER # 설정할 위치
        match:
          context: SIDECAR_INBOUND
          listener:
            portNumber: 8080
            filterChain:
              filter:
                name: "envoy.filters.network.http_connection_manager"
                subFilter:
                  name: "envoy.filters.http.router"
        patch: # 엔보이 설정 패치
          operation: INSERT_BEFORE
          value:
           name: envoy.filters.http.tap
           typed_config:
              "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap"
              commonConfig:
                adminConfig:
                  configId: tap_config

     

    1. EnvoyFilter 를 istioinaction 네임스페이스에 배포했다.
      • workloadSelector를 사용해 이 설정을 적용할 워크로드를 구체적으로 지정했다.
    2. 다음으로 엔보이 설정에서 패치할 위치를 지정해야 한다.
      • 이 예제에서는 인바운드 리스너(SIDECAR_INBOUDN)의 HTTP_FILTER가 되도록 지정했다.
      • 앞서 언근한 바와 같이 리스너를 위한 네트워크 필터들이 있으며, 그중 하나가 HCM 이다.
      • HCM에도 HTTP 요청을 처리하는 HTTP용 필터 체인이 있다.
      • 또한 이 예제에서는 특정 리스너도 지정했는데, HCM을 8080 포트에 바인딩된 리스너를 지정했다.
      • 마지막으로, 이 HCM HTTP 필터 체인에서 envoy.filters.http.router HTTP 필터를 골랐다.
      • 이 필터를 고른 이유는, 다음 절에서 보겠지만 새로운 tap 필터를 이 router 필터 바로 앞에 배치할 것이기 때문이다.
    3. EnvoyFilter 리소스의 patch 부분에서 설정을 어떻게 패치할지 지정한다.
      • 여기서는 앞서 설정 부분에서 선택한 필터 앞에 설정을 병합한다.
      • 추가하는 필터인 envoy_filters.http.tap 은 HCM 필터 체인에서 envoy.filters.http.router 앞에 위치한다.
      • tap 필터 설정의 구조를 명확하게 해야 하므로 명시적인 타입을 지정한다 - Docs

    Step3. Envoy Filter 적용 

    ☞ EnvoyFilter를 istioinaction 네임스페이스에 webapp 워크로드에 적용

     

    #
    cat ch14/tap-envoy-filter.yaml
    kubectl apply -f ch14/tap-envoy-filter.yaml
    kubectl get envoyfilter -n istioinaction
    NAME         AGE
    tap-filter   10s
    
    #
    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction
    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006
    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json
    ...
                                    {
                                        "name": "envoy.filters.http.tap",
                                        "typedConfig": {
                                            "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap",
                                            "commonConfig": {
                                                "adminConfig": {
                                                    "configId": "tap_config"
                                                }
                                            }
                                        }
                                    },
                                    {
                                        "name": "envoy.filters.http.router",
                                        "typedConfig": {
                                            "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
                                        }
                                    }
    ...

    ▶ tap 기능이 동작하는지 확인해보자.  ( 2개의 터미널 창이 필요 / 창 하나에서는 curl 로 tap 설정을 전달해 webapp 워크로드에서 tap을 시작하자. )

    # 터미널 1 : 포트 포워딩 설정 후 tap 시작
    kubectl port-forward -n istioinaction deploy/webapp 15000 &
    curl -X POST -d @./ch14/tap-config.json localhost:15000/tap
    
    # 터미널 2 : 기존 반복 접속하는 것 활용
    EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
    docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog
    while true; do docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
    
    # 터미널 3 : 로그 확인
    docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level http:debug
    docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level tap:debug
    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
    2025-05-18T05:59:47.704918Z	debug	envoy http external/envoy/source/common/http/conn_manager_impl.cc:329	[C2100] new stream	thread=31
    2025-05-18T05:59:47.705028Z	debug	envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049	[C2100][S11845543740779751481] request headers complete (end_stream=true):
    ':authority', 'webapp.istioinaction.io'
    ':path', '/api/catalog'
    ':method', 'GET'
    'user-agent', 'curl/8.7.1'
    'accept', '*/*'
    'x-app-tap', 'true'
    ...
    2025-05-18T05:59:47.713852Z	debug	envoy tap external/envoy/source/extensions/filters/http/tap/tap_config_impl.cc:172	submitting buffered trace sink	thread=31
    2025-05-18T05:59:47.713860Z	debug	envoy tap external/envoy/source/extensions/common/tap/admin.cc:145	admin submitting buffered trace to main thread	thread=31
    ...
    • tap 을 시작한 창에 tap 출력이 표시돼야 한다. 이 출력은 헤더, 바디, 트레일러 등 요청에 대한 모드 정보를 준다.
    • 엔보이 tap 필터와 이 필터를 이스티오에서 사용해 네트워크 전반의 요청을 디버깅하는 방법을 계속해서 알아보자.

     


    14.3 외부 호출로 요청 속도 제한하기

    ▶ 들어가며 : 속도 제한 소개 - Docs   (Rate-limiting requests with external call-out)

    더보기
    • 앞 절에서는 기본 HTTP 필터에 있는 기능으로 이스티오 데이터 플레인을 확장해봤는데, 외부 호출 기능으로 데이터 플레인을 확장하는 기본 필터들도 있다.
      In the previous section, we extended the Istio data plane with functionality that exists in an out-of-the-box HTTP filter. There are also out-of-the-box filters that enhance the data plane with functionality that exists as a call-out.
    • 우리는 이 필터들을 사용해 외부 서비스를 호출하고, 요청을 계속할지 여부나 방법을 결정할 수 있는 어떤 기능을 수행하게 해본다.
      With these filters, we call out to an external service and have it perform some functionality that can determine how or whether to continue with a request.
    • 이번 절에서는 특정 워크로드에서 서비스 측 속도 제한을 적용하기 위해 속도 제한 서비스를 호출하도록 이스티오 데이터 플레인을 설정하는 방법을 살펴본다.
      In this section, we explore how to configure Istio’s data plane to call out to a rate-limiting service to enforce service-side rate-limiting for a particular workload

    동일한 서비스의 여러 복제본은 특정 서비스에 대한 글로벌 제한 속도를 받기 위해 동일한 속도 제한 서비스를 호출한다.

     

    • 이스티오가 데이터 플레인에 엔보이를 사용하는 것처럼, 속도 제한에 대한 특정 호출엔보이 HTTP 필터에서 나온다.
      Just as Istio uses Envoy for the data plane, the specific call-out for rate limiting comes from an Envoy HTTP filter.
    • 엔보이에서 속도 제한을 하는 방법은 여러 가지가 있지만 (네트워크 필터, 로컬 속도 제한, 글로벌 속도 제한), 우리는 특히 글로벌 속도 제한 기능을 살펴본다.
      There are several ways to do rate limiting in Envoy (as a network filter, local rate limiting, and global rate limiting), but we specifically explore the global rate-limiting functionality.
    • 글로벌 속도 제한을 사용하면 특정 워크로드의 모든 엔보이 프록시가 동일한 속도 제한 서비스를 호출하며, 이 속도 제한 서비스는 백엔드 글로벌 키-값 저장소를 호출한다. (그림 14.7 참조)
      With global rate limiting, you have all Envoy proxies for a particular workload calling the same rate-limiting service, which calls a backend global key-value store as shown in figure 14.7.
    • 이 아키텍처를 사용하면 서비스의 복제본 개수에 상관없이 속도 제한이 적용되도록 할 수 있다.
      With this architecture, we can ensure that a rate limit is enforced regardless of how many replicas of a service exist.

     

    • 엔보이 전역 속도 제한을 사용하면 속도 제한 서버를 호출해 특정 요청에 속도 제한을 적용해야 하는지 여부를 결정할 수 있다.
    • 결정을 내리고자 요청 속성을 속도 제한 서버로 전송한다.

    • 속도 제한을 하려면 엔보이 커뮤니티에서 제공하는 속도 제한 서버를 - Github 배포하거나, 더 정확히는 엔보이 속도 제한 API - Code구현하는 속도 제한 서버를 배포해야 한다. To configure rate limiting, we need to deploy the rate-limit server, which comes from the Envoy community or, more accurately, a rate-limit server that implements the Envoy rate-limiting API.
    • 이 서버는 백엔드 레디스 캐시와 통신하도록 설정돼 있으며, 레디스에 속도 제한 카운터를 저장한다 (원한다면 Memcached 를 사용할 수 있다). This server is configured to talk with a backend Redis cache and stores rate-limit counters in Redis (optionally, you can use Memcache).
    • 따라서 속도 제한 서버를 배포하기 전에 기대하는 속도 제한 동작으로 설정해야 한다. Before we deploy the rate-limit server, we need to configure it with the expected rate-limiting behavior.

    14.3.1 엔보이 속도 제한 이해하기 Understanding Envoy rate limiting

      들어가며

    더보기
    • 엔보이 속도 제한 서버 RLS 를 구성하기 전에 속도 제한이 어떻게 작동하는지 이해해야 한다. Before configuring the Envoy rate-limit server (RLS), we need to understand how rate limiting works.
    • 특히 엔보이의 HTTP 전역 속도 제한을 살펴볼 것인데, 이 속도 제한은 HTTP 필터로 존재하고 HCM에서 HTTP 필터 체인으로 설정해야 한다. We are specifically looking at understanding Envoy’s HTTP global rate limiting, which exists as an HTTP filter and needs to be configured into the HTTP filter chain on the HCM.
    • 속도 제한 필터는 HTTP 요청을 처리할 때 요청에서 특정 속성을 가져오고 RLS로 보내 평가를 받는다.
      When the rate-limit filter processes an HTTP request, it takes certain attributes from the request and sends them out to the RLS for evaluation.
    • 엔보이 속도 제한에서 이런 속성들 혹은 속성 그룹들을 가리켜 디스크립터 descripter 라고 한다.
      Envoy rate-limiting terminology uses the word descriptors to refer to attributes or groups of attributes.
    • 요청이 디스크립터 또는 속성은 원격 주소일 수도, 요청 헤더일 수도, 목적지일 수도, 혹은 요청의 어떤 일반 속성 같은 것일 수도 있다.
      These descriptors, or attributes, of the request can be things like remote address, request headers, destination, or any other generic attributes about the request.

    • 속성 제한 서버 RLS는 그림 14.8처럼 전송된 요청의 속성을 미리 정의한 속성 집합과 비교해 일치하는 속성의 카운터를 늘린다.
      The RLS evaluates the request attributes that have been sent as part of a request against a set of predefined attributes, as shown in figure 14.8, and increments counters for those attributes.
    • 카운트할 속성은 트리 형태로 그룹화하거나 정의해 결정할 수 있다.
      The request attributes may be grouped or defined in a tree to determine what attributes should be counted.
    • 속성 또는 속성 집합이 속도 제한 서버 정의와 일치하면 해당 제한의 횟수를 늘린다.
      If an attribute or set of attributes matches the RLS definitions, then the counts for those limits are incremented.
    • 횟수가 임계값을 초과하면 해당 요청에는 속도 제한이 적용된다.
      If a count exceeds a threshold, that request is rate-limited.
    요청 속성은 속도 제한 서버로 전송되며, 속도 제한 서버는 미리 설정한 디스크립터 집합과 비교해 속도 제한 결정을 내린다.

     

     

    엔보이 속도 제한 서버 설정하기 CONFIGURING THE ENVOY RATE-LIMIT SERVER

    더보기
    • 속성 카운터와 한도가 포함된 속도 제한 서버 설정을 만들어보자.
      Let’s configure the RLS with the attribute counters and limits.
    • 우리 예제에서는 예제 조직에서 보유한 로열티 등급에 따라 특정 사용자 집단을 제한하고자 한다.
      For our example use case, we want to limit certain groups of users depending on what loyalty tier they have with our example organization.
    • 요청의 로열티 등급은 x-loyalty 헤더를 검사해 판단할 수 있다.
      We can determine the loyalty tier in a request by examining the x-loyalty header.

    • 골드 등급(x-loyalty: gold)의 사용자 그룹에는 요청을 분당 10개까지 허용하고, 실버 등급(x-loyalty: silver)에는 요청을 분당 5개까지 허용한다. 또한 브론즈 등급(x-loyalty: bronze)에는 요청을 분당 3개까지 허용한다.
      For a particular group of users in the gold tier (x-loyalty: gold), we allow 10 requests per minute.For silver (x-loyalty: silver), we allow five requests per minute; and for bronze (x-loyalty: bronze), we allow three requests per minute.
    • 식별할 수 없는 로열티 등급의 경우, 분당 요청이 하나를 넘어가면 속도 제한이 처리를 제한한다.
      For a loyalty tier that cannot be identified, rate limiting will kick in after one request per minute.
    • 요청이 디스크립터를 포착하는 속도 제한 서버 설정은 다음과 같이 표현할 수 있다.
    # cat ch14/rate-limit/rlsconfig.yaml 
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: catalog-ratelimit-config
      namespace: istioinaction
    data:
      config.yaml: |
        domain: catalog-ratelimit
        descriptors:
          - key: header_match
            value: no_loyalty
            rate_limit:
              unit: MINUTE
              requests_per_unit: 1
          - key: header_match
            value: gold_request
            rate_limit:
              unit: MINUTE
              requests_per_unit: 10
          - key: header_match
            value: silver_request
            rate_limit:
              unit: MINUTE
              requests_per_unit: 5
          - key: header_match
            value: bronze_request
            rate_limit:
              unit: MINUTE
              requests_per_unit: 3
    • 실제 요청 헤더를 직접 다루지 않고, 요청의 일부로 전송된 속성만 다루고 있음을 유의하자. 다음 절에서는 이 속성들을 정의하는 방법을 살펴볼 것이다.
    • 앞서 언급했듯이, 속도 제한 서버 설정은 속도를 제한하기 위해 따라야 하는 규칙을 정의한다.
    • 이스티오 데이터 플레인을 거쳐 요청이 처리될 때 속성들이 속도 제한 서버로 보내진다. 속성이 규칙 조건에 일치하면 그에 따라 처리를 제한한다.

    요청 경로에 속도 제한 걸기* CONFIGURING THE REQUEST PATH FOR RATE LIMITING

    더보기
    • 속도 제한 서버 설정을 만들고 나면, 특정 요청에 대해 어떤 속성을 전송할 것인지 엔보이를 설정해야 한다.
    • 엔보이 용어로는 이 설정을 특정 요청 경로에 취하는 속도 제한 조치 rate-limit action 라고 부른다.
    • 예를 들어, catalog 서비스를 /items 경로로 호출하면 요청에 x-loyalty 헤더가 있는지와 어느 그룹에 속하는지를 포착하려고 한다.

     

    • 적절한 속성 action 을 속도 제한 서버로 보내도록 설정하려면 특정 엔보이 루트 설정에 rate_limit 설정을 지정해야 한다.
    • 이스티오에는 아직 전용 API가 없으므로(이 책을 저술하는 시점에는) EnvoyFilter 리소스를 사용해야 한다.

    • catalog 서비스의 모든 경로에 속도 제한 조치를 지정하는 방법은 다음과 같다.

    Step1. RLS 설정 ( loyalty 에 따른 Action 조건 설정 )

    # cat ch14/rate-limit/catalog-ratelimit-actions.yaml 
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: catalog-ratelimit-actions
      namespace: istioinaction
    spec:
      workloadSelector:
        labels:
          app: catalog
      configPatches:
        - applyTo: VIRTUAL_HOST
          match:
            context: SIDECAR_INBOUND
            routeConfiguration:
              vhost:
                route:
                  action: ANY
          patch:
            operation: MERGE
            # Applies the rate limit rules.
            value:
              rate_limits: # 속도 제한 조치
                - actions:
                  - header_value_match:
                      descriptor_value: no_loyalty
                      expect_match: false
                      headers:
                      - name: "x-loyalty"
                - actions:
                  - header_value_match:
                      descriptor_value: bronze_request
                      headers:
                      - name: "x-loyalty"
                        exact_match: bronze
                - actions:
                  - header_value_match:
                      descriptor_value: silver_request
                      headers:
                      - name: "x-loyalty"
                        exact_match: silver
                - actions:
                  - header_value_match:
                      descriptor_value: gold_request
                      headers:
                      - name: "x-loyalty"
                        exact_match: gold
    • 이제 이 규칙들을 속도 제한 서버와 함께 배포하고 데이터 플레인을 설정하는 방법을 살펴보자.

     Step2. k8s configmap 으로 규칙을 배포하고, 속도 제한 서버를 레디스 백엔드와 함께 배포하자.

    #
    tree ch14/rate-limit
    ch14/rate-limit
    ├── catalog-ratelimit-actions.yaml
    ├── catalog-ratelimit.yaml
    ├── rls.yaml
    └── rlsconfig.yaml
    
    cat ch14/rate-limit/rlsconfig.yaml
    cat ch14/rate-limit/rls.yaml
    
    #
    kubectl apply -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
    kubectl apply -f ch14/rate-limit/rls.yaml -n istioinaction
    
    # 확인
    kubectl get cm -n istioinaction catalog-ratelimit-config
    kubectl get pod -n istioinaction                                
    NAME                       READY   STATUS    RESTARTS      AGE
    ratelimit-99d5d9c5-q4zcm   1/1     Running   2 (25s ago)   34s
    redis-6cf4ff9768-x97m8     1/1     Running   0             34s
    ...
    • 엔보이가 속성을 속도 제한 서버로 보내게 해야 한다. 그래야 속도 제한 서버가 개수를 세어 속도 제한을 처리할 수 있기 때문이다.

     

     


    Step3. 엔보이 서버 생성

    # 기존에 반복 호출은 취소해두자
    
    # 
    cat ch14/rate-limit/catalog-ratelimit.yaml
    cat ch14/rate-limit/catalog-ratelimit-actions.yaml
    kubectl apply -f ch14/rate-limit/catalog-ratelimit.yaml -n istioinaction
    kubectl apply -f ch14/rate-limit/catalog-ratelimit-actions.yaml -n istioinaction
    kubectl get envoyfilter -A
    NAMESPACE       NAME                        AGE
    istioinaction   catalog-ratelimit-actions   27s
    istioinaction   catalog-ratelimit-filter    28s
    ...

     Step4. 속도 제한 기능을 시험해보기 위해 sleep 앱을 배포하고 catalog 서비스를 호출하는 클라이언트를 시뮬레이션해보자.

    # sleep 앱에서 catalog 서비스를 호출 시도 : 대략 1분에 한 번 정도 호출 성공! >> x-loyalty 헤더가 없을 때 속도 제한 값!
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
    ...
    
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v
    ...
    < HTTP/1.1 429 Too Many Requests
    < x-envoy-ratelimited: true
    ...
    
    # silver 헤더는? 
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v
    ...
    
    
    #
    docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4'
    docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4' -o json | grep actions
    
    docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||'
    docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json | grep actions
    docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json
    ...
                    "rateLimits": [
                        {
                            "actions": [
                                {
                                    "headerValueMatch": {
                                        "descriptorValue": "no_loyalty",
                                        "expectMatch": false,
                                        "headers": [
                                            {
                                                "name": "x-loyalty"
                                            }
                                        ]
                                    }
                                }
                            ]
                        },
                        {
                            "actions": [
                                {
                                    "headerValueMatch": {
                                        "descriptorValue": "bronze_request",
                                        "headers": [
                                            {
                                                "name": "x-loyalty",
                                                "exactMatch": "bronze"
    ...

     

    [ 실행 결과 한눈에 보기 ]

     


    [ 리소스 정리 ]  * 다음 실험을 위해 자원을 깨끗이 지우자!!

    #
    kubectl delete envoyfilter -n istioinaction --all
    kubectl get envoyfilter -A
    
    #
    kubectl delete -f ch14/rate-limit/rlsconfig.yaml -n istioinaction
    kubectl delete -f ch14/rate-limit/rls.yaml -n istioinaction

     

     


    14.4 Lua 소개

    ☞ 특징 : 가벼운 명령형/절차적 언어로, 확장 언어로 쓰일 수 있는 스크립팅 언어를 주 목적으로 설계 - Wiki Extending Istio’s data plane with Lua

    더보기
    • 이미 존재하는 엔보이 필터를 설정해 이스티오의 데이터 플레인을 확장하는 것은 편리하지만, 추가하려는 기능이 기본 엔보이 필터에 없는 경우에는 어떻게 해야 할까?
      Extending Istio’s data plane by configuring existing Envoy filters is convenient, but what if the functionality we want to add doesn’t already exist as an out-of-the box Envoy filter?
    • 요청 경로에 어떤 커스텀 로직을 구현하고 싶다면 어떻게 해야 할까? 이 절에서는 자체 커스텀 로직으로 데이터 플레인 동작을 확장하는 방법을 살펴본다.
      What if we want to implement some custom logic on the request path? In this section, we look at how to extend data-plane behavior with our own custom logic.
    • 이전 절에서 엔보이에는 데이터 플레인 동작을 강화하기 위해 필터 체인에 추가할 수 있는 기본 필터가 많이 있음을 확인했다. 그중 하나가 루아 필터로, 루아 필터를 사용하면 루아 스크립트를 작성해 프록시에 주입함으로써 요청/응답 경로의 동작을 커스텀할 수 있다 (그림 14.9 참조)
      One of those is the Lua filter, which allows us to customize the behavior of the request or response path by writing Lua scripts and injecting them into the proxy.
    • 이런 스크립트는 요청이나 응답의 헤더를 조작하고 바디조사하는 데 사용할 수 있다. 계속해서 루아 스크립트를 주입해 요청 경로의 처리를 변경하도록 EnvoyFilter 리소스를 사용해 데이터 플레인을 설정할 것이다. These scripts can be used to manipulate the headers and inspect the body of a request or response. We will continue to use the EnvoyFilter resource to configure the data plane to inject Lua scripts to change the processing of the request path.

     


    [ 특징 요약 ] 

     

    특징 설명
    경량성 (Lightweight) Lua는 메모리 사용량이 적고 실행 속도가 빠르며, 전체 인터프리터 크기가 작아 임베디드 시스템에 적합합니다.
    다중 패러다임 지원 절차적, 함수형, 객체지향 프로그래밍을 지원하여 다양한 프로그래밍 스타일에 유연하게 대응할 수 있습니다.
    동적 타이핑 (Dynamic Typing) 변수의 타입을 선언할 필요 없이 런타임에 결정되므로, 빠른 프로토타이핑과 유연한 코딩이 가능합니다.
    테이블 기반 구조 Lua의 테이블은 배열, 딕셔너리, 객체 등 다양한 데이터 구조를 표현할 수 있는 강력한 도구입니다.
    코루틴 (Coroutines) 비동기 작업을 위한 협력형 멀티태스킹을 지원하여, 복잡한 제어 흐름을 효율적으로 관리할 수 있습니다.
    가비지 컬렉션 자동 메모리 관리 기능을 통해 메모리 누수를 방지하고 개발자의 부담을 줄여줍니다.
    임베디드 용이성 C 언어로 구현되어 다른 애플리케이션에 쉽게 통합할 수 있으며, 확장성과 유연성이 뛰어납니다.
    크로스 플랫폼 지원 ANSI C로 작성되어 다양한 운영 체제에서 실행 가능하며, 이식성이 뛰어납니다.
    간결한 문법 학습 곡선이 완만하고 코드가 간결하여 유지보수가 용이합니다.
    확장성 C/C++로 작성된 라이브러리를 통해 기능을 확장할 수 있어, 다양한 응용 분야에 활용 가능합니다.

     

    ☞ 루아 프로그래밍 언어 Lua programming language  https://lua.org/ , https://luajit.org/

    루아는 시스템 기능을 강화할 수 있는 강력하면서도 내장 가능한 스크립트 언어다.
    Lua is a powerful, embeddable scripting language that can be used to enhance the capabilities of a system.

    루아는 동적 타입의 인터프리터 언어로, 루아 가상머신이 제공하는 메모리 자동 관리 기능을 갖추고 있다 (엔보이에서는 LuaJIT이다).

    Lua runs as a dynamically typed and interpreted language with automatic memory management provided by a Lua VM (in Envoy, this is LuaJIT).

     


     

    ☞ 요청 바디를 검사하면 프록시에서 스트림을 처리하는 방식에 영향을 줄 수 있다.

    예를 들어 바디 전체를 메모리에 적재하는 작업을 수행할 수도 있는데, 이는 성능에 영향을 줄 수 있다.

     

      실습 : 요청 경로의 동작을 커스텀 ⇒ 일부 실습은 실패!

    더보기

    [ 개요 ]

    • 요청 경로의 동작을 커스텀하는 일반적인 예를 들어본다. 들어오는 요청 모두를 A/B 테스트 그룹의 일부로 처리하고 싶다고 해보자.
    • 어떤 그룹인지는 요청의 속성을 기반으로 런타임에서만 판단할 수 있다.
    • 특정 요청이 어느 그룹에 속했는지 판단하려면 A/B 테스트 엔진을 호출해야 한다.
    • 이 호출의 응답은 요청의 헤더로 추가해야 하며, 업스트림 서비스이 헤더를 사용해 A/B 테스트 목적에 맞는 라우팅 결정을 내릴 수 있다.
    • 이 예제를 위해 몇 가지 보조 서비스를 배포해보자. 받은 요청의 헤더를 되돌려 보내는 샘플 httpbin 서비스를 배포할 것이다.
    • 또한 샘플 A/B 테스트 버킷 서비스도 배포해보겠다. 이 서비스는 요청의 헤더를 평가해 요청이 속해야 하는 그룹을 나타내는 문자열을 반환한다.

     Step1. 실험 환경 구성 & 보조 서비스 배포

    #
    cat ch14/bucket-tester-service.yaml
    ...
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: bucket-tester
        version: v1
      name: bucket-tester
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: bucket-tester
          version: v1
      template:
        metadata:
          labels:
            app: bucket-tester
            version: v1
        spec:
          containers:
          - image: hashicorp/http-echo:1.0 # 수정 https://hub.docker.com/r/hashicorp/http-echo/tags
            imagePullPolicy: IfNotPresent
            name: bucket-tester
            args:
            - "-text=dark-launch-7"        
            ports:
            - containerPort: 5678
              name: http
              protocol: TCP
            securityContext:
              privileged: false
    
    #
    kubectl apply -f ch14/httpbin.yaml -n istioinaction
    kubectl apply -f ch14/bucket-tester-service.yaml -n istioinaction
    
    # 확인
    kubectl get pod -n istioinaction
    NAME                             READY   STATUS    RESTARTS      AGE
    bucket-tester-688c598b47-86fbr   2/2     Running   0             25s
    httpbin-85d76b4bb6-dz6b5         2/2     Running   0             2m56s
    ...

     


    • 요청이나 응답 헤더를 조작하는 용도로 작성할 수 있는 루아 스크립트를 살펴보고, 이 사용 사례를 구현하는 방법도 살펴보자.
      Let’s look at a Lua script we can write to manipulate the request or response headers and how we can implement this use case.
    • 엔보이에서는 루아 함수 envoy_on_reqest() 혹은 envoy_on_response() 를 구현해 요청응답 각각을 확인하고 조작할 수 있다.
      In Envoy, we can implement the envoy_on_request () or envoy_on_response() Lua function to inspect and manipulate the request and response, respectively.
    • 루아 내에서 다른 서비스를 호출해야 한다면 엔보이가 제공하는 함수를 사용해야 한다 (RPC 호출을 하는데 범용 루아 라이브러리를 사용하면 안된다. 우리는 엔보이가 자체 논블로킹 non-blocking 스레딩 아키텍처로 호출을 올바르게 관리하길 원하기 때문이다).
      If we need to make a call to another service from within Lua, we have to use an Envoy-provided function (we should not use a general-purpose Lua library to make RPC calls, because we want Envoy to manage the call correctly with its non-blocking threading architecture).
    • httpCall() 함수를 사용하면 외부 서비스와 통신할 수 있다. 다음 스크립트는 우리의 사용 사례를 구현한 것이다. We can use the http-Call() function to communicate with an external service. The following script implements our use case:
    # cat ch14/lua-filter.yaml
    ...
                function envoy_on_request(request_handle)
                  local headers, test_bucket = request_handle:httpCall(
                  "bucket_tester",
                  {
                    [":method"] = "GET",
                    [":path"] = "/",
                    [":scheme"] = "http",
                    [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                    ["accept"] = "*/*"
                  }, "", 5000) 
    
                  request_handle:headers():add("x-test-cohort", test_bucket)               
                end          
                function envoy_on_response(response_handle)
                  response_handle:headers():add("istioinaction", "it works!")
                end
    ...

     

    • envoy_on_reqest() 함수를 구현하고, httpCall() 내장 함수를 사용해 외부 서비스와 통신하고 있다.
    • 또한 응답을 받아 x-test-cohort 라는 헤더를 추가하고 있다. httpCall() 포함한 내장 함수는 문서 참조 - Docs
    • 앞 절에서 했던 것처럼 이 스크립트를 EnvoyFilter 리소스추가할 수 있다.
    # cat ch14/lua-filter.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: httpbin-lua-extension
      namespace: istioinaction
    spec:
      workloadSelector:
        labels:
          app: httpbin
      configPatches:
      - applyTo: HTTP_FILTER
        match:
          context: SIDECAR_INBOUND
          listener:
            portNumber: 80
            filterChain:
              filter:
                name: "envoy.filters.network.http_connection_manager"
                subFilter:
                  name: "envoy.filters.http.router"
        patch:
          operation: INSERT_BEFORE
          value: 
           name: envoy.lua
           typed_config:
              "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
              inlineCode: |
                function envoy_on_request(request_handle) # 아래 줄에 코드 입력
                  local headers, test_bucket = request_handle:httpCall(
                  "bucket_tester",
                  {
                    [":method"] = "GET",
                    [":path"] = "/",
                    [":scheme"] = "http",
                    [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                    ["accept"] = "*/*"
                  }, "", 5000) 
    
                  request_handle:headers():add("x-test-cohort", test_bucket)               
                end          
                function envoy_on_response(response_handle) # 아래 줄에 코드 입력
                  response_handle:headers():add("istioinaction", "it works!")
                end    ## 요기까지~~
      - applyTo: CLUSTER
        match:
          context: SIDECAR_OUTBOUND
        patch:
          operation: ADD
          value: # cluster specification
            name: bucket_tester
            type: STRICT_DNS
            connect_timeout: 0.5s
            lb_policy: ROUND_ROBIN
            load_assignment:
              cluster_name: bucket_tester
              endpoints:
              - lb_endpoints:
                - endpoint:
                    address:
                      socket_address:
                        protocol: TCP
                        address: bucket-tester.istioinaction.svc.cluster.local
                        port_value: 80

     

    [ filter 수정본 ]

    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: httpbin-lua-extension
      namespace: istioinaction
    spec:
      workloadSelector:
        labels:
          app: httpbin
      configPatches:
      - applyTo: HTTP_FILTER
        match:
          context: SIDECAR_INBOUND
          listener:
            portNumber: 80
            filterChain:
              filter:
                name: "envoy.filters.network.http_connection_manager"
                subFilter:
                  name: "envoy.filters.http.router"
        patch:
          operation: INSERT_BEFORE
          value: 
           name: envoy.lua
           typed_config:
              "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
              inlineCode: |
                function envoy_on_request(request_handle)
                  local headers, test_bucket = request_handle:httpCall(
                  "bucket_tester",
                  {
                    [":method"] = "GET",
                    [":path"] = "/",
                    [":scheme"] = "http",
                    [":authority"] = "bucket-tester.istioinaction.svc.cluster.local",
                    ["accept"] = "*/*"
                  }, "", 5000) 
                  
                  sanitized_bucket = string.gsub(test_bucket, "[\r\n]", "") -- sanitize test_bucket string
                  request_handle:headers():add("x-test-cohort", sanitized_bucket)               
                end          
                function envoy_on_response(response_handle)
                  response_handle:headers():add("istioinaction", "it works!")
                end
      - applyTo: CLUSTER
        match:
          context: SIDECAR_OUTBOUND
        patch:
          operation: ADD
          value: # cluster specification
            name: bucket_tester
            type: STRICT_DNS
            connect_timeout: 0.5s
            lb_policy: ROUND_ROBIN
            load_assignment:
              cluster_name: bucket_tester
              endpoints:
              - lb_endpoints:
                - endpoint:
                    address:
                      socket_address:
                        protocol: TCP
                        address: bucket-tester.istioinaction.svc.cluster.local
                        port_value: 80

     Step2. 루아함수를 통해 envoy의 요청

    ☞ workloadSelector 정의한 대로 이 필터를 httpbin 워크로드에 적용 후 httpbin 서비스 호출 확인! ⇒ 실습은 실패!

    #
    kubectl apply -f ch14/lua-filter.yaml
    kubectl get envoyfilter -n istioinaction
    
    # istio-proxy config 확인 내용 추가해두자
    
    
    # httpbin 서비스 호출 확인! 
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/ -v
    ...
    < HTTP/1.1 503 Service Unavailable
    < content-length: 39
    < content-type: text/plain
    < istioinaction: it works!
    < date: Sun, 18 May 2025 07:59:34 GMT
    < server: envoy
    < x-envoy-upstream-service-time: 51
    ...
    invalid header value for: x-test-cohort
    
    kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/headers
    ...
    invalid header value for: x-test-cohort
    
    
    # 정상 실습 시..
    {
        "headers": {
            "Accept": "*/*",
            "Content-Length": "0",
            "Host": "httpbin.istioinaction:8000",
            "User-Agent": "curl/7.69.1",
            "X-B3-Sampled": "1",
            "X-B3-Spanid": "1d066f4b17ee147b",
            "X-B3-Traceid": "1ec27110e4141e131d066f4b17ee147b",
            "X-Test-Cohort": "dark-launch-7" # A/B 테스트 서비스를 호출할 때 덧붙이는 새 헤더 x-test-cohort 가 보임
        }
    }
    • 자세한 내용은 이 책 소스 코드에 있는 ch14/lua-filter.yaml 파일에서 살펴볼 수 있다.
    • 이 예제에서는 데이터 플레인의 기능을 확장하기 위해 의도적으로 구축된 필터를 사용하는 방법을 알아봤다.
    • 루아 스크립트 언어를 사용해 이 기능을 구현했으며, 내장 함수를 사용해 다른 함수를 호출했다.
    • 다음 절에서는 웹어셈블리를 사용해 다른 언어로 우리의 커스텀 기능을 구현하는 방법을 다룬다.

     


    [ 자원 삭제 - 다음 실습을 위해 자원을 정리합시다!! ]

    #
    kubectl delete envoyfilter -n istioinaction --all
    kubectl get envoyfilter -A
    
    #
    kubectl delete -f ch14/httpbin.yaml -n istioinaction
    kubectl delete -f ch14/bucket-tester-service.yaml -n istioinaction

     

     


    14.5 웹어셈블리로 이스티오의 데이터 플레인 확장하기

    들어가며

    더보기

    웹어셈블리로 새 엔보이 필터를 작성 - Docs Extending Istio’s data plane with WebAssembly

    • 이 장에서 살펴보는, 요청 경로상에서 이스티오를 확장하는 마지막 방법은 웹어셈블리로 새 엔보이 필터를 작성하는 것이다.
      The last approach to extend Istio on the request path that we cover in this chapter is writing new Envoy filters with WebAssembly.
    • 앞 절에서는 기존 엔보이 필터를 재사용하고 설정해 기본 이스티오 기능을 확장해봤으며, 요청 경로를 조작하는 우리의 자체 커스텀 스크립트를 주입하기도 했다.
    • 이번 절에서는 우리의 자체 엔보이 필터를 구축하는 방법과 이스티오 데이터 플레인에 배포하는 방법을 살펴본다. In this section, we explore how we can build our own Envoy filters and dynamically deploy them to the Istio data plane.

    14.5.1 웹어셈블리 소개 Introducing WebAssembly  

    더보기

    [ WASM 소개 ]

    • 웹어셈블리 Wasm, WebAssembly 란 바이너리 명령 형식으로, 여러 가지 환경 간에 이식할 수 있는 것을 목표로 했으며 여러 프로그래밍 언어로 컴파일해 가상머신에서 실행할 수 있다.
      WebAssembly (Wasm) is a binary instruction format that is intended to be portable across environments and that can be compiled from many different programming languages and run in a VM.
    • 본래 웹어셈블리는 브라우저의 웹 애플리케이션에서 CPU 집약적인 작업의 실행 속도를 높이고, 브라우저 기반 애플리케이션 지원을 자바스크립트 외의 언어로도 확장하기 위해 개발됐다 (그림 14.10 참조).
      Wasm was originally developed to speed up the execution of CPU-intensive operations for web apps in a browser and extend the support for browser-based apps to languages other than JavaScript.
    • 웹어셈블리는 2019년에 W3C 권고 사항이 됐으며 모든 주요 브라우저에서 지원하고 있다.
      It became a W3C Recommendation in 2019 and is supported in all major browsers.

     

     

    ☞ 웹어셈블리모듈로 패키징된 커스텀 코드로, 웹 브라우저 같은 대상 호스트 안의 격리된 가상머신에서 안전하게 실행할 수 있다.

     

     

     

     

     

     


     [ WASM의 메모리 사용 로직 ]

    WebAssembly(WASM)의 메모리 사용 로직은 선형 메모리(Linear Memory) 모델을 기반으로 하며, 이는 연속적인 바이트 배열로 구성되어 있습니다. 이러한 구조는 WASM 모듈과 호스트 환경(예: JavaScript) 간의 데이터 교환 및 메모리 관리를 효율적으로 수행할 수 있게 합니다. ( By ChatGPT )

     

    "WASM 메모리 사용 로직"

     

     

    • 웹어셈블리는 저장 공간과 메모리를 적게 차지하고 거의 네이티브에 가까운 속도로 실행되도록 설계됐다. Wasm is intended to have a compact size and load footprint and execute at near-native speeds.
    • 또한 호스트 애플리케이션(즉, 브라우저)에 내장돼도 안전한데, 메모리 안전 memory safe 하고 격리된 sandboxed 실행 환경(가상머신)에서 실행되기 때문이다. It is also safe to embed in host applications (that is, a browser) because it is memory safe and runs in a sandboxed execution environment (VM).
    • 웹어셈블리 모듈은 호스트 시스템이 허용하는 메모리와 기능에만 접근할 수 있다. A Wasm module only has access to memory and functionality that the host system allows.

    14.5.2 왜 엔보이에 웹어셈블리를 사용하는가?

     ( Why WebAssembly for Envoy? )

    더보기
    • 네이티브 엔보이 필터를 직접 작성하는 데는 크게 두 가지 단점이 있다
      • C++ 여야 한다.
      • 변경 사항을 엔보이의 새 바이너리로 정적으로 빌드해야 하는데, 이는 사실상 엔보이 ‘커스텀’ 빌드다.
    • 엔보이웹어셈블리 실행 엔진을 내장하고 있으므로, HTTP 필터를 포함해 엔보이의 다양한 영역을 커스터마이징하고 확장하는 데 사용할 수 있다.
      Envoy embeds a WebAssembly execution engine that can be used to customize and extend various areas of Envoy, including HTTP filters.
    • 웹어셈블리가 지원하는 언어라면 어느 것이든 엔보이 필터로 작성해 그림 14.11 처럼 런타임에 프록시로 동적으로 불러올 수 있다.
      You can write Envoy filters in any language supported by Wasm and dynamically load them into the proxy at run time, as shown in figure 14.11.
    • 즉, 이스티오에서 기본 엔보이 프록시를 계속 사용하면서 커스텀 필터를 런타임에 동적으로 불러올 수 있다는 것이다.
      This means you can keep using the out-of-the-box Envoy proxy in Istio and dynamically load your custom filters at run time.
    웹어셈블리 (Wasm) 모듈을 패키징해 웹어셈블리 HTTP 필터 내에서 실행할 수 있다.

    • How it works - Blog  

     

    • Set up local cache of Wasm extensions
    • Pull desired Wasm extension into the local cache
    • Mount the wasm-cache into appropriate workloads
    • Configure Envoy with EnvoyFilter CRD to use the Wasm filter

    • Image fetcher in Istio agent - Blog  
    • Istio 1.9부터 Istio-agent는 EnvoyFiltersistio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스(ECDS)를 활용하여 에 구성된 원격 HTTP 소스에서 가져온 Wasm 바이너리를 로드하는 안정적인 솔루션을 제공해 왔습니다. Istio 1.12의 새로운 Wasm API 구현에도 동일한 메커니즘이 적용됩니다. 원격 가져오기가 실패할 경우 Envoy가 잘못된 구성으로 인해 중단될 염려 없이 HTTP 원격 리소스를 안정적으로 사용할 수 있습니다.
    • 또한, Istio 1.12는 이 기능을 Wasm OCI 이미지로 확장합니다. 즉, Istio 에이전트는 이제 Docker Hub, Google Container Registry(GCR), Amazon Elastic Container Registry(ECR) 등 모든 OCI 레지스트리에서 Wasm 이미지를 가져올 수 있습니다. 이미지를 가져온 후, Istio 에이전트는 Wasm 바이너리를 추출하여 캐시한 다음 Envoy 필터 체인에 삽입합니다.

    • Wasm module distribution via the Istio Agent - Blog  
    • Istio 1.9 이전에는 원격 Wasm 모듈을 프록시에 배포하기 위해 Envoy 원격 데이터 소스가 필요했습니다. 이 예시에서는 두 가지 EnvoyFilter리소스가 정의되어 있습니다. 하나는 원격 가져오기 Envoy 클러스터를 추가하는 리소스이고, 다른 하나는 HTTP 필터 체인에 Wasm 필터를 삽입하는 리소스입니다. 이 방법에는 단점이 있습니다. 잘못된 구성이나 일시적인 오류로 인해 원격 가져오기가 실패하면 Envoy가 잘못된 구성에 고정됩니다. Wasm 확장 프로그램이 fail closed 로 구성된 경우 , 잘못된 원격 가져오기로 인해 Envoy 서비스가 중단됩니다. 이 문제를 해결하려면 Envoy xDS 프로토콜을 근본적으로 변경 하여 비동기 xDS 응답을 허용해야 합니다.
    • Istio 1.9는 istio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스 (ECDS) 를 활용하여 기본적으로 안정적인 배포 메커니즘을 제공합니다 .
    • istio-agent는 istiod에서 확장 구성 리소스 업데이트를 가로채고, 원격 페치 힌트를 읽어와 Wasm 모듈을 다운로드한 후, 다운로드된 Wasm 모듈의 경로로 ECDS 구성을 다시 작성합니다. 다운로드가 실패하면 istio-agent는 ECDS 업데이트를 거부하고 잘못된 구성이 Envoy에 도달하는 것을 방지합니다. 자세한 내용은 Wasm 모듈 배포 관련 문서를 참조하세요 .

    14.5.3 웹어셈블리로 새로운 엔보이 필터 만들기

     - Building a new Envoy filter with WebAssembly

    더보기
    • 웹어셈블리로 엔보이 필터를 빌드하려면 어떤 언어를 사용하고 싶은지, 어떤 엔보이 버전을 사용 중인지, 그 버전에서 어떤 엔보이 ABI Abstract Binary Interace 를 지원하는지 등을 알아야 한다.
    • 그러고 나서 적절한 언어 SDK를 골라 빌드 및 종속성 도구를 제대로 준비해야 한다.
    • 이절에서는 Sola.io의 wasme 라는 오픈소스 개발자 도구를 사용해 엔보이용 웹어셈블리 필터를 만들고 빌드해본다.
    • wasme 를 사용하면 엔보이 프로젝트용 웹어셈블리를 빠르게 부트스트랩하고 보일러플레이트 생성 boilerplate scaffolding 을 모두 자동화해준다.
    • 그럼 웹어셈블리로 엔보이 필터 빌드를 시작하는 방법을 살펴보자.
    • 이 책을 저술하는 시점에 엔보이 웹어셈블리 SDK가 있는 프래그래밍 언어는 다음 네 가지다.
      • C++
      • Rust
      • AssemblyScript 어셈블리스크립트 (TypeScript 타입스크립트)
      • TinyGo

     


    • 이 절에서는 어셈블리스크립트( https://www.assemblyscript.org/ ) 를 사용해 웹어셈블리로 새 엔보이 필터를 빌드해본다.
    • 어셈블리스크립트는 타입스크립트의 변형이므로 자바스크립트 개발자라면 익숙할 것이다.
    • 어셈블리스크립트는 엔보이 필터를 구축하는 데 있어 C++의 훌륭한 대체재다.

     [ 주의 ]

    ☞ 엔보이의 웹어셈블리의 지원은 실험적인 것으로 간주되므로 바뀔 수 있다. 따라서 직접 만들어 엔보이에 배포하는 웹어셈블리 모듈은 운영 환경에 들어가기 전에 모두 철저히 테스트해볼 것을 권장한다.


    (옵션) Istio 에 Coraza (코라자) WAF 적용 - KrBlog

    더보기

    1. OWASP Coraza란?

    - Go 언어로 개발된 오픈 소스 웹 애플리케이션 방화벽(WAF) 프레임워크

    - 고성능과 유연성을 갖추어 다양한 웹 애플리케이션을 보호하는 데 적합함

    - ModSecurity의 SecLang 규칙 언어를 지원하며, OWASP Core Rule Set(CRS)와 100% 호환됨

    2. 주요 특징


    항목 설명
    오픈 소스 Apache 2.0 라이선스로 배포되어 자유롭게 사용 및 수정이 가능합니다.
    고성능 대규모 트래픽을 처리할 수 있도록 설계되었으며, 성능 저하 없이 웹 애플리케이션을 보호합니다.
    확장성 플러그인 프레임워크를 통해 사용자 정의 기능(예: 연산자, 액션, 감사 로거 등)을 추가할 수 있습니다.
    통합 가능성 다양한 서버 및 프록시와 통합할 수 있도록 설계되어 있으며, 예를 들어 Caddy, HAProxy, Envoy, Traefik 등과의 통합이 가능합니다.
    보안 규칙 지원 OWASP CRS를 기본으로 지원하며, 사용자 정의 규칙을 작성하여 특정 보안 요구사항을 충족시킬 수 있습니다.

     

    3.  주요 구성요소 

    • SecLang 규칙 엔진: ModSecurity의 SecLang 규칙 언어를 지원하여 기존의 보안 규칙을 그대로 사용할 수 있습니다.
    • 플러그인 프레임워크: 사용자 정의 연산자, 액션, 감사 로거 등을 추가하여 기능을 확장할 수 있습니다.
    • 감사 로깅 시스템: 보안 이벤트를 상세히 기록하여 분석 및 규정 준수를 지원합니다.
    • 다양한 통합 옵션: 서버 및 프록시와의 통합을 위한 커넥터 및 플러그인을 제공합니다.

     

    4.  통합 사례

     

    • HAProxy: SPOA(Stream Processing Offload Agent)를 통해 Coraza를 백엔드 서비스로 실행하여 요청을 처리합니다. OWASP Coraza
    • Traefik: 미들웨어로 Coraza를 통합하여 WAF 기능을 제공합니다.
    • Envoy: WebAssembly(WASM) 플러그인으로 Coraza를 통합하여 서비스 메시 환경에서 보안을 강화합니다.
    • Istio: 사이드카 프록시로 Coraza를 배포하여 마이크로서비스 간의 보안을 강화합니다.

     [ Plug-In 지원 참조 링크 ]


    [ 추가 실습 참고자료 ]

    • Quick Start - Docs
    • OWASP Core Ruleset - Docs
    • Playground - Link

    [ 추천 영상 ]

    - https://www.youtube.com/watch?v=SCEZGRvcxc8


    • proxy-wasm filter based on Coraza WAF - Github
      • Coraza 기반으로 구축되고 proxy-wasm ABI를 구현하는 웹 애플리케이션 방화벽 WASM 필터입니다.
      • Envoy에서 직접 로드하거나 Istio 플러그인으로 사용할 수도 있습니다.
      • WAF는 플러그 앤 플레이 방식의 보안 솔루션이 아닙니다. WAF가 효과적으로 작동하려면 보호하려는 환경과 트래픽에 맞춰 구성 및 튜닝이 필요합니다.
      • 실제 운영 환경에서는 배포된 구성( @recommended-conf 및 @crs-setup-conf 참조 )을 완전히 숙지하고 사용된 규칙 세트에 대한 튜닝 단계를 수행하는 것이 좋습니다.
      • OWASP 핵심 규칙 세트(CRS) 튜닝에 대한 자세한 내용은 " False Positives and Tuning" 가이드를 참조하십시오.

    [ 실습 ]

     

    Step1. Istio WasmPlugin 설정

    #
    kubectl apply -f - <<EOF
    apiVersion: extensions.istio.io/v1alpha1
    kind: WasmPlugin
    metadata:
      name: coraza-ingressgateway
      namespace: istio-system
    spec:
      selector:
        matchLabels:
          istio: ingressgateway
      url: oci://ghcr.io/corazawaf/coraza-proxy-wasm
      phase: AUTHN
      pluginConfig:
        default_directives: default
        directives_map:
          default:
          - Include @demo-conf
          - SecDebugLogLevel 9
          - SecRuleEngine On
          - Include @crs-setup-conf
          - Include @owasp_crs/*.conf
    EOF
    
    #
    kubectl logs -n istio-system -l app=istio-ingressgateway | grep -i wasm 
    2025-05-18T09:22:39.344842Z     info    wasm    fetching image corazawaf/coraza-proxy-wasm from registry ghcr.io with tag latest
    
    #
    kubectl get WasmPlugin -A
    NAMESPACE      NAME                    AGE
    istio-system   coraza-ingressgateway   9s

     

    Step2. 동작 확인

    #
    curl -s http://webapp.istioinaction.io:30000/api/catalog
    
    # XSS (Cross-Site Scripting) 공격 시도 : REQUEST-941-APPLICATION-ATTACK-XSS
    curl -s 'http://webapp.istioinaction.io:30000/api/catalog?arg=<script>alert(0)</script>' -IL
    HTTP/1.1 403 Forbidden
    
    # 로그 모니터링
    kubectl logs -n istio-system -l app=istio-ingressgateway -f
    2025-05-18T09:34:21.049522Z     critical        envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157      wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 1). Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11347"] [id "949111"] [rev ""] [msg "Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog?arg=<script>alert(0)</script>"] [unique_id "ztBHScSiiDnOiwJfmOy"]      thread=30
    [2025-05-18T09:34:21.014Z] "HEAD /api/catalog?arg=<script>alert(0)</script> HTTP/1.1" 403 -  - "-" 0 0 35 - "172.18.0.1" "curl/8.7.1" "27f43c69-359d-9d95-99cd-553081bb9346" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.10:8080 172.18.0.1:65166 - -
    
    
    # SQLI phase 2 (reading the body request)
    curl -i -X POST 'http://webapp.istioinaction.io:30000/api/catalog' --data "1%27%20ORDER%20BY%203--%2B"
    HTTP/1.1 403 Forbidden
    
    # 로그 모니터링
    kubectl logs -n istio-system -l app=istio-ingressgateway -f
    2025-05-18T09:42:40.613124Z     critical        envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157      wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 10) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11358"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog"] [unique_id "GYSnZlurGTXFtujeNgl"]  thread=34
    [2025-05-18T09:42:40.575Z] "POST /api/catalog HTTP/1.1" 403 -  - "-" 26 0 39 - "172.18.0.1" "curl/8.7.1" "c30d47e1-5631-99ae-91f2-5dfd8174b421" "webapp.istioinaction.io:30000" "10.10.0.16:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.10:44518 10.10.0.10:8080 172.18.0.1:63266 - -
    ...

     

     Step3. 프로메테우스 메트릭 확인 : curl -s localhost:8082/stats/prometheus | grep waf_filter

     

    [ 참고영상 ]

    - https://www.youtube.com/watch?v=QJJBYOKKui0

    - https://www.youtube.com/watch?v=47MxrFX1rRg 

    - https://www.youtube.com/watch?v=NE9DvPwgNZc


    [ 실행 결과 한 눈에 보기 ] 

    1) 필터 적용

    2) 공격 시도 및 결과 확인

    3) 로그 확인  ** kubectl logs -n istio-system -l app=istio-ingressgateway -f 

     

    4) Prometheus Filter - Graph check


    Summary

    더보기
    • 엔보이의 내부 아키텍처는 리스너와 필터를 중심으로 구축됐다.Envoy’s internal architecture is built around listeners and filters.
    • 즉시 사용할 수 있는 엔보이 필터가 많다. There are many out-of-the-box Envoy filters.
    • 이스티오의 데이터 플레인(엔보이 프록시)을 확장할 수 있다. We can extend Istio’s data plane (Envoy proxy).
    • 이스티오의 EnvoyFilter 리소스를 사용해 엔보이의 HTTP 필터 구조를 직접 구성하면 더 세밀한 설정이 가능하며, 이스티오의 API로는 노출되지 않는 엔보이의 부분들도 설정할 수 있다. Envoy’s HTTP filter architecture can be configured directly with Istio’s EnvoyFilter resource for more fine-grained configuration or to configure aspects of Envoy not exposed by Istio’s API.
    • 속도 제한이나 tap 필터 같은 기능으로 서비스 간 통신에서 엔보이의 요청 경로를 확장할 수 있다. We can extend Envoy’s request path for service-to-service communication with functionality like rate limiting or the tap filter.
    • 루아와 웹어셈블리를 사용하면 엔보이를 다시 빌드하지 않고도 데이터 플레인을 고급 수준에서 커스터마이징할 수 있다. Lua and Wasm are available for more advanced customizations to the data plane without having to rebuild Envoy.

     

    [ 실습 후 자원정리 - Kind 삭제 ]

    ##  - cluster name 및 docker 는 자신의 것으로 대체하여 자원 정리할 것!!
    kind delete cluster --name **myk8s** && docker rm -f mypc

    [ 마무리 ]

    Istio Filter 를 통해 보안적인 요소의 개선과 Traffic 처리의 효율성을 개선 할 수 있다는 점이 흥미로웠다. 특히, Filtering 기법은 기존 아키텍쳐 기반에 add-on 식으로 기능을 덧 붙여 나갈 수 있어서 관리의 효율화를 모색할 수 있는 좋은 수단인 것 같다. 다만, 운영 적용을 위해서는 많은 사례와 정확한 용법에 대한 익숙해 질 수 있는 시간과 노오력(?!)이 필요하다는 점을 잊지 말아야 겠다.


    [ 참조 링크 모음 ]