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
- 서로 다른 클라우드 프로바이더 또는 하이브리드 클라우드와 같이 다양한 환경에서 워크로드를 실행할 수 있게 한다.
- 격리성 강화 Improved isolation
- 초기 평가에서 ACME가 서비시 메시를 채택하는 데 주요 동기로 삼았던 것은 여러 클러스터 간에 서비스 메시를 확장할 수 있는 기능과, 클러스터 간에 트래픽 관리, 관찰 가능성, 보안을 지원하는 기능이었다.
- 다중 클러스터 작업을 지원하고자 회사는 두 가지 접근법을 고려했다 - Docs
- 다중 클러스터 서비스 메시 Multi-cluster service mesh
- 여러 클러스터에 걸쳐 있고 워크로드가 클러스터 간에 트래픽을 라우팅하도록 설정하는 메시. 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
- 메시 연합 (다중 메시) Mesh federation, also known as multi-mesh
- 개별 서비스 메시 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가지
- 기본-원격(컨트롤 플레인 공유) 배포 모델 primary-remote (shared control plane) → 위 그림 12.2
- 기본-기본(복제된 컨트롤 플레인) 배포 모델 primary-primary (replicated control plane) → 아래 그림 12.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에 인증과 인가를 제공하는 쿠버네티스 리소스를 시각화한다.
- 클러스터 간 워크로드 디스커버리는 기술적으로 동일한다. 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 인증서를 사용하는 것은 쉽다! 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 가 우리의 외부 CA를 지원할 때만 가능하다. Using cert-manager - Only viable when our external CA is supported by certmanager.
- 지원하는 외부 발급자 확인 https://cert-manager.io/docs/configuration/issuers/
- 지원하는 경우, cert-manager 의 istio-csr로 쿠버네티스 CSR을 지켜보고 서명을 위해 외부 CA에 제출할 수 있다.
- 맞춤 개발 Custom development
- 승인된 쿠버네티스 CSR을 지켜보고 이를 서명을 위해 외부 CA에 제출하는 쿠버네티스 컨트롤러를 만든다. Create a Kubernetes controller that listens for approved Kubernetes CSRs and submits them to an external CA for signing.
- 그러나 로컬 키가 있는 자체 서명 인증서 대신 외부 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.
(참고) Secrets - Docs , Task , Task2 , Task3
Secret 동작 - 암호화 기본소개
- Secret 데이터는 ETCD에 저장되며, 파드에 볼륨 마운트된 Secret 사용 시, 노드 상에 영구적으로 데이터가 남지 않도록 tmpfs 영역(메모리상에 구축된 임시 파일 시스템)에 저장됨.
- ETCD에 저장된 Secret 데이터는 암호화되어 있지 않으므로, ETCD에 대한 접근 통제와, 암호화를 권장합니다 - Link
주의
- 시크릿은 암호화되지 않기 때문에 버전 관리 시스템으로 관리하면 안 된다. 시크릿이 적절하게 보호되도록 하는 것은 플랫폼 엔지니어의 몫이다.
- 예를 들어, 내부 etcd 스토리지에 시크릿을 암호화 한 후에 저장하도록 쿠버네티스를 설정할 수 있다.
- → Sealed Secret, Vault 등 툴 사용 가능
- 이는 저장된 데이터에 대한 보안을 보장하는 데는 도움이 되지만 버전 제어 시스템에서 관리해야 하는 문제를 해결하지는 못한다.
Secret 볼륨 마운트 실습 : 변경 시 적용되는 동기화 시간 확인
#
echo -n 'myusername' | base64
echo -n 'mypassword' | base64
#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: bXl1c2VybmFtZQ==
password: bXlwYXNzd29yZA==
EOF
#
kubectl get secret my-secret
kubectl describe secret my-secret
Name: my-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 10 bytes
username: 10 bytes
#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: secret-volume-demo-pod
spec:
containers:
- name: secret-volume-demo-container
image: busybox
command: ["sh", "-c", "cat /etc/secret-volume/username; cat /etc/secret-volume/password; sleep 3600"]
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret-volume"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: my-secret
restartPolicy: Never
terminationGracePeriodSeconds: 0
EOF
#
kubectl get pod
#
kubectl exec -it secret-volume-demo-pod -- sh
---------------------------------------------
# 마운트 정보 확인
df -hT /etc/secret-volume
Filesystem Type Size Used Available Use% Mounted on
tmpfs tmpfs 15.6G 8.0K 15.6G 0% /etc/secret-volume
...
mount -t tmpfs
tmpfs on /etc/secret-volume type tmpfs (ro,relatime,size=16360396k)
tmpfs on /var/run/secrets/kubernetes.io/serviceaccount type tmpfs (ro,relatime,size=16360396k)
...
# 날짜와시간이 포함된 폴더가 있음
ls -al /etc/secret-volume
total 4
drwxrwxrwt 3 root root 120 Jul 30 08:13 .
drwxr-xr-x 1 root root 4096 Jul 30 08:13 ..
drwxr-xr-x 2 root root 80 Jul 30 08:13 ..2024_07_30_08_13_47.2779202592
lrwxrwxrwx 1 root root 32 Jul 30 08:13 ..data -> ..2024_07_30_08_13_47.2779202592
lrwxrwxrwx 1 root root 15 Jul 30 08:13 password -> ..data/password
lrwxrwxrwx 1 root root 15 Jul 30 08:13 username -> ..data/username
ls -l /etc/secret-volume/..data/
total 8
-rw-r--r-- 1 root root 10 Jul 30 08:13 password
-rw-r--r-- 1 root root 10 Jul 30 08:13 username
# 파일 정보 확인
cat /etc/secret-volume/..data/username ; echo
myusername
cat /etc/secret-volume/..data/password ; echo
mypassword
# 반복 확인
while true ; do ls -al /etc/secret-volume ; echo ; cat /etc/secret-volume/username ; echo ; cat /etc/secret-volume/password ; echo ; date ; echo; sleep 1; done
---------------------------------------------
# [터미널2]
# Secret 변경
echo -n 'testusername' | base64
echo -n 'testpassword' | base64
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque
data:
username: dGVzdHVzZXJuYW1l
password: dGVzdHBhc3N3b3Jk
EOF
date
# 결과 확인 : 디렉터리가 신규 생성되고, 심볼릭 링크가 신규 생성된 파일로 링크되며, 기존 폴더는 삭제됨
total 4
drwxrwxrwt 3 root root 120 Jul 30 08:13 .
drwxr-xr-x 1 root root 4096 Jul 30 08:13 ..
drwxr-xr-x 2 root root 80 Jul 30 08:13 ..2024_07_30_08_13_47.2779202592
lrwxrwxrwx 1 root root 32 Jul 30 08:13 ..data -> ..2024_07_30_08_13_47.2779202592
lrwxrwxrwx 1 root root 15 Jul 30 08:13 password -> ..data/password
lrwxrwxrwx 1 root root 15 Jul 30 08:13 username -> ..data/username
myusername
mypassword
Tue Jul 30 08:27:41 UTC 2024
total 4
drwxrwxrwt 3 root root 120 Jul 30 08:27 .
drwxr-xr-x 1 root root 4096 Jul 30 08:13 ..
drwxr-xr-x 2 root root 80 Jul 30 08:27 ..2024_07_30_08_27_41.2838531760
lrwxrwxrwx 1 root root 32 Jul 30 08:27 ..data -> ..2024_07_30_08_27_41.2838531760
lrwxrwxrwx 1 root root 15 Jul 30 08:13 password -> ..data/password
lrwxrwxrwx 1 root root 15 Jul 30 08:13 username -> ..data/username
testusername
testpassword
Tue Jul 30 08:27:42 UTC 2024
# 삭제
kubectl delete pod secret-volume-demo-pod
kubectl delete secret my-secret
Hashicorp Vault/VSO on K8S (공개)
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대를 배포함.
[실습 환경 구성] 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
[실습 환경 구성] (편리성 설정) MetalLB 배포 - Github & Alias 설정(kwest, keast)
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
샘플 애플리케이션 배포 후 확인
#
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
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.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
- 저자가 제공하는 ./ch12/scripts/generate-certificates.sh 스크립트를 사용하면 중간 CA와 루트 CA는 ./ch12/certs 디렉터리에 만들어진다.
- 이 스크립트는 루트 CA를 만들고, 이 CA를 사용해 중간 CA 2개에 서명한다. 그 결과 신뢰가 같은 중간 CA가 만들어진다.
플러그인 CA 인증서 적용하기 APPLYING PLUG-IN CA CERTIFICATES
./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
기본 정보 확인
#
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
(옵션) step 로 인증서 생성 시
# step CLI 설치
# macOS : brew install step
# 스크립트 실행
./ch12/scripts/generate-certificates.sh
...
# 생성 결과 확인
tree ch12/certs
...
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-cluster 의 istio-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)
많이 수정해야 하므로, 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
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
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
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
두 클러스터 모두에 컨트롤 플레인을 설치하면 개별 메시 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
- 그런데 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.
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 리소스를 사용해 컨트롤 플레인 설치하기’ 절 참조).
원격 클러스터에 접근하기 위한 설정의 식별자로 클러스터 이름이 어떻게 사용되는지에는 주의를 기울이자*
#
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"
}
시크릿 내용을 출력하는 대신, 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
기본-기본 배포 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
이제 컨트롤 플레인이 상대 클러스터의 워크로드를 퀴리할 수 있다.
# 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
다음으로는 클러스터 간 연결을 설정해보자.
12.3.6 클러스터 간 연결 설정하기 Setting up cross-cluster connectivity
들어가며 : north-south 트래픽, east-west 트래픽
- 4장에서는 이스티오의 인그레스 게이트웨이를 다루면서 엔보이 프록시 기반이라는 것을 살펴봤다.
- 인그레스 게이트웨이는 퍼블릭 네트워크에서 시작한 트래픽의 진입점으로, 트래픽을 조직의 내부 네트워크로 보낸다.
- 이런 유형의 트래픽을 north-south 트래픽이라고도 한다.
- 한편 서로 다른 내부 네트워크(여기서는 클러스터의 네트워크) 간의 트래픽은 east-west 트래픽이라고 한다. (그림 12.12 참조)
- 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.
- 클러스터 정보는 SNI에 인코딩된다.
- 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.
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
반대편 클러스터에도 작업 수행. 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.
클러스터 간 워크로드 디스커버리 검증하기* 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
kiali : west-cluster 확인
kiali : east-cluster 확인 , mTLS 통신이며 TCP Traffic 와 HTTP(S) 2개 트래픽 흐름으로 확인됨.
(참고) Istio 의 TCP Traffic 라우팅과 Traffic routing with SNI passthrough 는 4.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
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
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
- west의 인그레스 게이트웨이에 요청을 트리거하면 요청이 west-cluster 의 webapp 으로 라우팅됨을 알 수 있다.
- 그런 다음, 요청을 처리하는 east-cluster 의 catalog 워크로드로 해석된다.
- 이로써 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시가 설정됐고 두 클러스터가 서로의 워크로드를 찾을 수 있음을 확인했다.
- 워크로드들은 east-west 게이트웨이를 통과 지점으로 사용해 상호 인증 커넥션을 시작한다.
다중 클러스터 서비스 메시를 설정하는데 필요한 것 요약
- 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
- 서비스 어카운트 토큰과 인증서가 포함된 kubeconfig 를 사용해 각 컨트롤 플레인에 동료 클러스터에 대한 접근 권한을 제공함으로써 구현한다.
- 이 과정은 istioctl 을 사용해 쉽게 진행했고, east-cluster 에만 적용했다.
- 클러스터 간 워크로드 연결 Cross-cluster workload connectivity
- 다른 클러스터의 워크로드(다른 네트워크에 위치) 간에 트래픽을 라우팅하도록 east-west 게이트웨이를 설정하고, 이스티오가 워크로드가 위치한 네트워크를 알 수 있도록 각 클러스터에 네트워크 정보 레이블을 지정해 구현한다.
- 클러스터 간 신뢰 설정 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
기본적으로, 이스티오는 라운드 로빈 알고리듬으로 워크로드 간에 로드 밸런싱한다. 그러므로 트래픽은 고르게 분산된다.
# 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
실습을 위해 노드에 지역성 정보 레이블을 설정하자
#
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 헬스 체크가 필수라고 했던 것을 기억하자.
- 엔드포인트 상태를 수동적으로 확인하도록, 이상값 감지를 사용하는 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
- 기대한 대로 모든 요청은 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
-----------------------------------
- 로드 밸런싱, 지역 인식 라우팅, 클러스터 간 장애 극복, 상호 인증 트래픽, 접근 제어에 대한 우리의 모든 예제는 다중 클러스터 서비스 메시에서의 워크로드가 실행 중인 클러스터가 어디든 상관없이 이스티오의 모든 기능을 사용할 수 있음을 보여준다. 그리고 설정을 따로 더 하지 않아도 그럴 수 있다.
- 바라건데, 이번 장이 이스티오가 조직 내에서 어떻게 확장하는지, 어떻게 여러 클러스터를 단일 메시로 통합하는지, 여러 조직에게 그것이 왜 중요한지를 충분히 보여줬길 바란다.
- 다름장에서는 가상머신을 서비스 메시로 통합해본다. 이는 레거시 워크로드를 운영해야 하는 성숙한 기업에게 아주 가치 있는 기능이다.
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
'스터디 > Istio Hands-on Study' 카테고리의 다른 글
Istio Hands-on Study [1기] - 7주차 - 이스티오의 요청 처리 기능 확장하기 - 14장 (1) | 2025.05.25 |
---|---|
Istio Hands-on Study [1기] - 6주차 - 데이터 플레인 트러블 슈팅하기 - 11장 (1) | 2025.05.18 |
Istio Hands-on Study [1기] - 6주차 - 데이터 플레인 트러블 슈팅하기 - 10장 (0) | 2025.05.18 |
Istio Hands-on Study [1기] - 5주차 - 마이크로서비스 통신 보안 - 9장 (9.3이후) (2) | 2025.05.10 |
Istio Hands-on Study [1기] - 5주차 - 마이크로서비스 통신 보안 - 9장 (9.3까지) (0) | 2025.05.09 |