카테고리 없음
[Istio-7주차] Istio 스케일링, 데이터 플레인 확장 (이론, 실습)
- -
12장 조직 내에서 Istio 스케일링하기
배경 설명
- 여러 클러스터에서 서비스 메시 스케일링하기 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
들어가며: 다중 클러스터 서비스 메시
- 이전 장: 단일 클러스터 메시 내 활성화한 Istio 기능 확인.
하지만 서비스 메시는 단일 클러스터에 종속되지 않음- 서비스 메시는 여러 클러스터에 걸쳐 있을 수 있으며, 클러스터 모두에 동일한 기능을 제공할 수 있음
- 사실 메시의 가치는 워크로드가 많아질수록 증가
- 단일 클러스터와 비교할 때 다중 클러스터 서비스 메시는 어떤 이점이 있는 지 확인 (가상의 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가 서비스 메시를 채택 시 초창기 주요 동기가 된 기능들
- 여러 클러스터 간에 서비스 메시를 확장
- 클러스터 간에 트래픽 관리, 관찰 가능성
- 보안 지원
- ACME는 다중 클러스터 작업을 지원하고자 두 가지 접근법 고려 - Docs
- 다중 클러스터 서비스 메시 Multi-cluster service mesh
https://istio.io/latest/docs/ops/deployment/deployment-models/#multiple-clusters
- 여러 클러스터에 걸쳐 있고, 워크로드가 클러스터 간에 트래픽을 라우팅하도록 설정하는 메시
- VirtualService, DestinationRule, Sidecar 등이 적용된 Istio 구성을 전부 따름
- 메시 연합 (다중 메시) Mesh federation, also known as multi-mesh
https://istio.io/latest/docs/ops/deployment/deployment-models/#multiple-meshes
- 개별 서비스 메시 2개 워크로드 간 통신 노출 및 활성화
- 서비스 간 트래픽 설정 시 두 메시 모두에 수동 설정이 필요 (자동화가 덜 됨)
- 서로 다른 팀에서 두 메시를 운영하거나, 보안 격리 요구 사항이 엄격한 경우 좋은 선택지가 됨
- 하기에 다룰 케이스는 다중 클러스터 서비스 메시
(메시 연합 같은 경우에는 공식 문서 참고) - Docs
- 다중 클러스터 서비스 메시 Multi-cluster service mesh
12.2 다중 클러스터 서비스 메시 개요 multi-cluster service mesh
- 소개 : 필요 사항
- 다중 클러스터 서비스 메시에서 서비스 연결 방식
- 앱에는 완전히 투명한 방식으로 클러스터 간에 서비스를 연결
- 그와 동시에 클러스터 간 통신을 위한 서비스 메시의 기능(세밀한 트래픽 제오, 복원력, 관찰 가능성, 보안 등)은 모두 유지
- Istio가 다중 클러스터 서비스 메시를 구현하는 방법
- 모든 클러스터의 서비스를 쿼리 → 쿼리 정보로 서비스 프록시에 트래픽 라우팅 방식 설정 (클러스터 간에서, 서비스 간)
- 클러스터들을 하나의 메시로 결합하는 데 필요한 것 (전제 조건)
멀티 클러스터 서비스 메시는 클러스터 간 워크로드 탐색, 연결성, 공통된 신뢰 필요
- 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
- Istio 컨트롤 플레인은 서비스 프록시를 설정하기 위해 동료 k8s 클러스터의 워크로드를 탐색 가능해야 함
(k8s 클러스터 API 서버를 상대편 클러스터의 Istio 컨트롤 플레인에서 접근할 수 있어야 함)
- Istio 컨트롤 플레인은 서비스 프록시를 설정하기 위해 동료 k8s 클러스터의 워크로드를 탐색 가능해야 함
- 클러스터 간 워크로드 연결성 Cross-cluster workload connectivity
- 워크로드는 서로 연결돼 있어야 함
(워크로드 엔드포인트를 인지하고 있어도 커넥션을 시작할 수 없다면 쓸모가 없음)
- 워크로드는 서로 연결돼 있어야 함
- 클러스터 간 공통 신뢰 Common trust between clusters
- 클러스터 간 워크로드가 서로 인증해야 함
(인증해야 Istio의 보안 기능을 활성화할 수 있음)
- 클러스터 간 워크로드가 서로 인증해야 함
- 다중 클러스터 연결성과 보안 Multi-cluster connectivity and security
- Istio가 클러스터 간에 다중 클러스터 연결을 수립하려면, 워크로드는 동료 클러스터의 쿠버네티스 API에 접근하는 방법으로만 찾을 수 있고,
어떤 조직에서는 이것이 바람직하지 않는 보안 태세일 수 있음- 각 클러스터가 다른 클러스터의 API 모두에 접근할 수 있기 때문
- 이 경우에는 메시 연합이 더 나은 방식
- Istio가 클러스터 간에 다중 클러스터 연결을 수립하려면, 워크로드는 동료 클러스터의 쿠버네티스 API에 접근하는 방법으로만 찾을 수 있고,
- 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
- 다중 클러스터 서비스 메시에서 서비스 연결 방식
- Istio 다중 클러스터 배포 모델 Istio multi-cluster deployment models - Blog
- 다중 클러스터 서비스 메시에서의 Istio 클러스터 유형 (2가지)
- 기본 클러스터 primary cluster : Istio 컨트롤 플레인이 설치된 K8S 클러스터 → 12.2 그림 중 왼쪽
- 원격 클러스터 remote cluster : 설치된 Istio 컨트롤 플레인과 떨어져 있는 K8S 클러스터 → 12.2 그림 중 오른쪽
기본-원격 배포 모델
- 달성하고자 하는 가용성 수준에 따라 배포 모델 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)
- 복제된 컨트롤 플레인 배포 모델이라고도 부름
- 컨트롤 플레인이 여럿이므로 가용성이 높음
- 중단 범위가 중단이 발생한 클러스터로 국한되기 때문
- 트레이드오프로 인해 리소스가 더 많이 필요
기본-기본 배포 모델
- 외부 컨트롤 플레인 배포 모델 external control plane
- 모든 클러스터가 컨트롤 플레인과 떨어져 있는 배포 모델
- 이 배포 모델을 통해 클라우드 프로바이더가 Istio를 관리형 서비스로 제공 가능
외부 컨트롤 플레인 배포 모델
- 다중 클러스터 서비스 메시에서의 Istio 클러스터 유형 (2가지)
- 다중 클러스터 배포에서 워크로드는 어떻게 찾는가? How workloads are discovered in multi-cluster deployments
- Istio의 컨트롤 플레인은 쿠버네티스 API 서버와 통신해 서비스 프록시 설정 관련 정보 수집 필요
(서비스 뒤의 서비스, 엔드포인트 등) - 쿠버네티스 API 서버에 요청하는 것은 막강한 일종의 권한임
- 리소스 세부 정보를 조회
- 민감 정보를 쿼리 가능
- 클러스터를 불량하고 되돌이킬 수 없는 상태로 만들 수 있는 정도로 리소스를 업데이트하거나 지울 수 있음
- 토큰과 RBAC으로 원격 쿠버네티스 API로의 접근을 보안 처리할 수 있음
다만 이 접근법은 트레이드오프를 고려해야 함
(메시 연합 사용 시 위험성 완화 가능. 앞 장에서 실습한 내용 등을 활용)
- 토큰과 RBAC으로 원격 쿠버네티스 API로의 접근을 보안 처리할 수 있음
- 쿠버네티스는 RBAC를 이용해 API 서버로의 접근을 보호 가능
** 쿠버네티스 RBAC은 광범위한 주제로, Istio 공부 내 범위는 아님 - 쿠버네티스 RBAC에서 클러스터 간 디스커버리를 용이하게 하는 데 사용하는 일부 개념 살펴보기
- Service Account: 기계나 서비스 등 사람이 아닌 클라이언트에 ID를 제공
- Service Account Token
- Service Account마다 자동으로 생성돼 해당 ID claim을 나타냄
- 토큰은 JWT 형식이며 쿠버네티스가 Pod에 주입
- Pod는 이 토큰을 사용해 API 서버에게 (자신의 신원) 인증 가능
- Role 및 ClusterRole: Service Account나 일반 사용자 같은 ID에 대한 권한 집합 정의
- Istiod에 인증과 인가를 제공하는 쿠버네티스 리소스 시각화
istiod의 ID와 접근을 설정하는 리소스 - 클러스터 간 워크로드 디스커버리는 기술적으로 동일하나 다음 과정을 따름 (그림 12.6)
- istiod에 원격 클러스터의 Service Account Token을 및 인증서를 제공해야 함
(인증서 - API 서버로 보안 통신을 시작할 수 있음) - istiod는 토큰을 사용해 원격 클러스터에 인증
- 인증 후 클러스터에서 실행 중인 워크로드 탐색
- istiod에 원격 클러스터의 Service Account Token을 및 인증서를 제공해야 함
- 위 과정은 istioctl이 자동화 해줌
- Istio의 컨트롤 플레인은 쿠버네티스 API 서버와 통신해 서비스 프록시 설정 관련 정보 수집 필요
- 클러스터 간 워크로드 연결 Cross-cluster workload connectivity - Docs : east-west gw or 라우팅 처리
- 전제 조건 2: 워크로드가 클러스터를 건너 연결할 수 있어야 함
- 클러스터가 플랫 네트워크 flat network 에 있을 경우
- flat network 환경:
- 단일 네트워크를 공유 (ex. Amazon VPC)
- 네트워크가 네트워크 피어링 network peering 으로 연결된 경우 등
- 이미 조건 충족: 워크로드가 IP 주소로 연결 할 수 있음
- flat network 환경:
- 클러스터가 서로 다른 네트워크에 있을 경우
- east-west 게이트웨이(특수 Istio 인그레스 게이트웨이) 사용 필요
- 다중 네트워크 메시에서 클러스터를 잇는 인그레스 게이트웨이를 지칭
- 네트워크의 에지에 위치해 클러스터 간 트래픽을 프록시해 줌
east-west 게이트웨이는 각 클러스터의 워크로드에 대한 리버스 프록시 요청을 수행
- east-west 게이트웨이(특수 Istio 인그레스 게이트웨이) 사용 필요
- [공식 문서] Multiple networks 제공 기능 : 중복 IP 환경 시 해결 등 - Docs
- 서비스 엔드포인트의 IP 또는 VIP 범위 중복 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
- Multi-cluster 에 Multi-primary 환경에서 east-west gateway 없이도, 네트워크 장비에 L3 라우팅을 통해서 통신 가능!
https://www.youtube.com/watch?v=4aeeLGrS514
- 클러스터 간 공통 신뢰 Common trust between clusters - Docs
- 전제 조건 3(마지막): 다중 클러스터 서비스 메시 내 클러스터들이 공통된 신뢰를 가져야 함
- 반대편 클러스터의 워크로드들이 상호 인증할 수 있게 됨
- 반대편 클러스터의 워크로드들이 상호 인증할 수 있게 됨
- 반대편 클러스터의 워크로드들 사이에 공통 신뢰를 달성하는 방법 (두 가지)
- 첫 번째: 공통된 루트 CA가 발급한 사용자 정의 인증서(플러그인 CA 인증서) 를 사용
- 두 번째: 두 클러스터가 인증서를 서명하는 데 사용하는 외부 CA를 통합
- 플러그인 CA 인증서 PLUG-IN CA CERTIFICATES - Docs
같은 루트가 서명한 중간 CA 인증서 사용하기 https://istio.io/v1.17/docs/tasks/security/cert-management/plugin-ca-cert/ Istio에 대한 플러그인 CA로서의 중간 CA
- 플러그인 중간 CA 인증서를 사용하는 방식 (쉬움)
- 중간 CA를 Istio가 만들게 하는 대신에 사용할 인증서를 지정하면 됨
- 이 인증서는 Istio가 설치한 네임스페이스에 시크릿으로 제공
- 두 클러스터 모두에서 인증서를 지정하여, 같은 루트 CA가 서명한 중간 CA를 사용 (그림 12.8)
- 중간 CA를 Istio가 만들게 하는 대신에 사용할 인증서를 지정하면 됨
- 이 방법은 간단하지만, 중간 CA가 노출될 경우 보안 위험 존재
- 노출을 감지해 중간 CA의 인증서를 취소할 때까지 공격자들은 중간 CA로 신뢰받는 인증서 서명 가능
- 보안 취약점으로 인해 조직은 중간 CA를 넘겨주기를(사용하기를) 꺼림
- 중간 CA만 메모리에 로드하고, 이를 Kubernetes Secret(최종: etcd)에 저장하지 않음으로서 노출 위험을 줄일 수 있음
- etcd에 영구 저장을 피할 수 있음
- etcd: Secret과 같은 Kubernetes 리소스가 저장되는 데이터 저장소
- etcd에 영구 저장을 피할 수 있음
- 더 안전한 대안은 인증서에 서명하는 외부 CA를 통합하는 것
- 플러그인 중간 CA 인증서를 사용하는 방식 (쉬움)
- 외부 인증 기관 통합 EXTERNAL CERTIFICATE AUTHORITY INTEGRATION
- 외부 인증 기관 통합을 사용하는 방식
- istiod는 쿠버네티스 CSR 리소스로 저장된 인증서 서명 요청 CSRs 을 검증하고 승인하는 등록 기관 역할을 함
- 승인된 쿠버네티스 CSR 리소스는 다음 방법 중 하나로 외부 CA에 제출됨
- cert-manager 사용하기 - Github , Home , Docs
- cert-manager 가 우리의 외부 CA를 지원할 때만 가능
- 지원하는 외부 발급자 확인 https://cert-manager.io/docs/configuration/issuers/
- 지원하는 경우
- cert-manager 의 istio-csr로 쿠버네티스 CSR을 지켜봄
- 서명을 위해 외부 CA에 제출
- Istio 에 cert-manager 외부 인증 기간 통합 설정 - Docs , Docs2
Signature Algorithm: ecdsa-with-SHA256 Issuer: O=cert-manager, O=cluster.local, CN=istio-ca Validity Not Before: Jan 13 16:51:59 2022 GMT Not After : Jan 13 17:51:59 2022 GMT ... X509v3 Subject Alternative Name: URI:spiffe://cluster.local/ns/default/sa/httpbin
- cert-manager 가 우리의 외부 CA를 지원할 때만 가능
- 맞춤 개발 Custom development
- cert-manager 사용하기 - Github , Home , Docs
- 외부 인증 기관 통합을 사용하는 방식
- 전제 조건 3(마지막): 다중 클러스터 서비스 메시 내 클러스터들이 공통된 신뢰를 가져야 함
- (참고) Secrets - Docs , Task , Task2 , Task3
- Secret 동작 - 암호화 기본소개
- Secret 데이터는 ETCD에 저장
- Pod에 볼륨 마운트된 Secret 사용 시, 노드 상에 영구적으로 데이터가 남지 않도록 tmpfs 영역(메모리상에 구축된 임시 파일 시스템)에 저장됨
- ETCD에 저장된 Secret 데이터는 암호화되어 있지 않음
- ETCD에 대한 접근 통제와, 암호화를 권장 - Link
- ETCD에 대한 접근 통제와, 암호화를 권장 - Link
- Secret 데이터는 ETCD에 저장
- 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 (공개)
- Secret 동작 - 암호화 기본소개
12 .3 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시 개요
- 인프라 구성 환경
- 실세계 엔터프라이즈 서비스를 모방하는 인프라 준비
- 서비스 전제 조건
- 여러 클러스터에서 실행
- 여러 리전에 걸쳐 배포
- 서로 다른 네트워크에 위치
- 서비스 상세 구성
- west-cluster : 사설 네트워크가 us-west 리전에 있는 쿠버네티스 클러스터. 여기서 webapp 서비스를 실행
- east-cluster : 사설 네트워크가 us-east 리전에 있는 쿠버네티스 클러스터. 여기서 catalog 서비스를 실행
다중 클러스터 서비스 메시 다이어그램 - 이점
- 클러스터 2개를 서로 다른 2개의 리전에 설정
- 둘 중 하나에 재난이 일어난 경우, 서비스 중단에서 보호 가능
- 여담
- webapp 과 catalog 워크로드가 별개의 클러스터에 있어야 하는 기술적인 이유는 없으며, 단지 시연 상 그리 함
- ‘수다스러운’ 워크로드는 지연 시간을 줄이기 위해 가능하면 가까이 있는 것이 중요
- 다중 클러스터 배포 모델 선택 Choosing the multi-cluster deployment model
- 다중 네트워크 인프라에서 결정할 사항
- 클러스터 사이를 연결할 수 있게 네트워크를 잇는 east-west 게이트웨이를 사용해야 함
- 다만 복제 컨트롤 플레인 배포 모델을 사용할지, 단일 컨트롤 플레인을 사용할지 여부는 정해져 있지 않음
(결정은 비즈니스 요구 사항에 달려 있음)
- 가상 ACME의 서비스 현황
- 온라인 상점이 아주 인기가 많음 (서비스가 중단되는 1분마다 수백만 달러의 비용이 발생)
- 고가용성이 최우선 과제
- 기본-기본 배포 모델 사용: Istio 컨트롤 플레인이 각 클러스터에 배포되게끔 함
- 기본-기본 배포 모델 사용: Istio 컨트롤 플레인이 각 클러스터에 배포되게끔 함
- 종합
- 네트워크를 잇는 east-west 게이트웨이를 사용
- 다중 클러스터, 다중 네트워크, 다중 컨트롤 플레인 서비스 메시를 구축
- 기본-기본 배포 모델 사용
- 다중 네트워크 인프라에서 결정할 사항
- 클라우드 인프라 준비하기 Setting up the cloud infrastructure (실습)
- [실습 환경 구성] 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
- MetalLB 배포
- 자주 사용하는 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'
- [실습 환경 구성] k8s(1.23.17) 배포 : west k8s 클러스터 배포 , east k8s 클러스터 배포 - (참고)Github & mypc 컨테이너
- 플러그인 CA 인증서 설정하기 Configuring plug-in CA certificates
- 9장에서 워크로드 ID를 부트스트래핑(워크로드가 자신의 정체를 입증하는 서명된 인증서를 받는 방법)을 다룰 때
: 설치할 때 인증서에 서명할 CA를 생성한다는 사실을 생략한 바 있음 - 워크로드 ID 부트스트래핑용으로 생성된 CA의 기본 동작
- Istio를 설치한 네임스페이스에 istio-ca-secret 이라는 시크릿으로 저장됨
- istiod 복제본들에게 공유됨
- CA 기본 동작에서 자체 CA를 사용하도록 변경도 가능
- Istio CA가 CA를 새로 만드는 대신, 자체 CA 를 사용
- 자체 CA 사용하도록 변경 시 진행해야 할 사항
- CA 인증서를 Istio를 설치한 네임스페이스인 istio-system 에 cacerts 라는 시크릿으로 저장해야 함
- 다음과 같은 데이터를 포함해야 함
같은 루트가 서명한 중간 CA 인증서 사용하기
- ca.cert.pem : 중간 CA의 인증서
- ca-key.pem : 중간 CA의 개인 키
- root-cert.pem : 중간 CA를 발급한 루트 CA의 인증서
- 루트 CA는 중간 CA들의 인증서를 검증하며, 이것이 클러스터 간 상호 신뢰의 핵심
- cert-chain.pem : 중간 CA의 인증서와 루트 인증서를 이어 붙인 신뢰 체인
- (참고) 인증서 구조
- 루트 CA: 모든 클러스터가 신뢰하는 최상위 인증서.
- 중간 CA: 각 클러스터별로 발급되어 워크로드 인증서 서명에 사용.
- 인증서 체인 : 신뢰 체인을 구성하여 클라이언트가 인증서를 검증할 수 있도록 함.
- (참고) SSL과 인증서 구조 이해하기 : 인증서 계층 구조 설명 - Blog
Geotrust <-> Google CA 예시 (2계층) Geotrust <-> Google CA <-> 개인 인증서 예시 (3계층) - cacerts 시크릿
- 루트 CA의 공개 키와 중간 CA의 공개 키 및 비밀 키로 구성
- 루트 CA의 비밀 키는 클러스터 외부에 안전하게 저장
cacerts 시크릿은 루트 CA의 공개 키와 중간 CA의 공개 키 및 비밀 키로 구성된다. 루트 CA의 비밀 키는 클러스터 외부에 안전하게 저장된다.
- ./ch12/scripts/generate-certificates.sh 스크립트를 사용 시
- 중간 CA와 루트 CA는 ./ch12/certs 디렉터리에 만들어짐
- 스크립트 실행 시 루트 CA를 만들고, 이 CA를 사용해 중간 CA 2개에 서명
- 그 결과 신뢰가 같은 중간 CA가 만들어짐
- 그 결과 신뢰가 같은 중간 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
- 플러그인 인증서가 설정되면 Istio 컨트롤 플레인을 설치할 수 있음
- 컨트롤 플레인은 플러그인 CA 인증서(사용자가 정의한 중간 인증서)를 갖고 워크로드 인증서에 서명함
- ./ch12/scripts/generate-certificates.sh 스크립트 : Smallstep - Github , Install
- 9장에서 워크로드 ID를 부트스트래핑(워크로드가 자신의 정체를 입증하는 서명된 인증서를 받는 방법)을 다룰 때
- 각 클러스터에 (Istiod) 컨트롤 플레인 설치하기 Installing the control planes in each cluster
- 들어가며
- 각 클러스터에 네트워크 메타데이터를 설정 (Istio 컨트롤 플레인 설치 전)
- 네트워크 메타데이터 설정에 따른 이점
- Istio에서 토폴로지 정보 사용 가능해짐
- 토폴로지 정보에 기반해 워크로드 설정 가능
- 지역 정보를 사용해 트래픽을 라우팅할 때 가까운 워크로드를 우선할 수 있음
- 워크로드가 다른 네트워크에 있는 원격 클러스터의 워크로드로 트래픽을 라우팅할 때,
east-west 게이트웨이를 사용하도록 Istio 에서 설정함
- 각 클러스터에 네트워크 메타데이터를 설정 (Istio 컨트롤 플레인 설치 전)
- 클러스터 간 연결을 위해 네트워크에 레이블 붙이기 LABELING NETWORKS FOR CROSS-CLUSTER CONNECTIVITY
- 네트워크 토폴로지 설정 - Docs
- Istio 설치 시 MeshNetwork 설정을 사용 (고급 사용 사례에서나 유지하는 레거시 설정)
- Istio를 설치한 네임스페이스에 네트워크 토폴로지 정보를 레이블로 붙이는 것(더 간단한 방법)
- 실습에서는 간단한 방법 사용
- Istio를 설치한 네임스페이스: 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
- 레이블 사용 시 Istio에서의 효과
- 네트워크 토폴로지를 이해하고, 그 이해를 바탕으로 워크로드 설정법을 결정
- 네트워크 토폴로지를 이해하고, 그 이해를 바탕으로 워크로드 설정법을 결정
- 네트워크 토폴로지 설정 - Docs
- IstioOperator 리소스를 사용해 컨트롤 플레인 설치하기 INSTALLING THE CONTROL PLANES USING ISTIOOPERATOR RESOURCES & Alias 설정(iwest, ieast)
- IstioOperator 리소스를 사용해 west-cluster 의 Istio 설치를 정의 (잦은 수정으로 인해 IstioOperator 사용)
# 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 를 그대로 지정
(두 컨트롤 플레인의 동일한 메시 형성이 목적이므로 동일한 ID 지정)# 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
- west-cluster 를 설치할 때 사용했던 meshID 를 그대로 지정
- 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 복제본 1개
- 수신 트래픽용 게이트웨이 1개
메시에는 현재 다음 절에서 설정할 클러스터 간 워크로드 디스커버리 및 연결이 없음- 이 때 없는 환경에서 각 클러스터에 일부 워크로드를 실행해보기**
(클러스터 간 디스커버리와 연결성이 올바르게 준비됐는지 확인하는 데 유용)
- 이 때 없는 환경에서 각 클러스터에 일부 워크로드를 실행해보기**
- IstioOperator 리소스를 사용해 west-cluster 의 Istio 설치를 정의 (잦은 수정으로 인해 IstioOperator 사용)
- 두 클러스터 모두에 워크로드 실행하기 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 주소로 해석할 수 없음
- 따라서 트래픽이 애플리케이션을 떠나 프록시로 리다이렉트되는 지점에 도달하기도 전에 요청이 실패하기 때문
- 이 서비스가 없으면 webapp 컨테이너가 FQDN을 IP 주소로 해석할 수 없음
- 스텁 catalog 서비스를 추가하면 발생하는 현상
- FQDN은 서비스 클러스터 IP로 해석
- 애플리케이션이 트래픽을 시작해 실제 엔보이 설정을 갖음
- 애플리케이션은 클러스터 사이의 라우팅을 처리하는 엔보이 프록시로 리다이렉트될 수 있음
- Istio 향후 버전에서 DNS 프록시를 개선할 때 해결할 예정인 에지 케이스
- east-cluster 에서만 catalog 워크로드(실행 예정) 용 서비스가 필요한 이유는?
- 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개를 각자 연결해야 하는 워크로드가 있는 상태
- 현재 상태로는 클러스터 간 워크로드 디스커버리 없이는 사이드카 프록시에 반대 클러스터의 워크로드가 설정되지 않음
- 다음 단계: 클러스터 간 디스커버리를 활성화
- 들어가며
- 클러스터 간 워크로드 디스커버리 활성화하기 Enabling cross-cluster workload discovery
- 들어가며
- Istio가 원격 클러스터에서 정보를 쿼리하기 위해 인증할 때 필요한 것
- 롤 바인딩 role binding 필요: ID를 정의하는 서비스 어카운트와 권한에 대한 설정
- Istio는 설치 시 istio-reader-service-account 라는 서비스 어카운트를 최소 권한으로 생성
- 다른 컨트롤 플레인이 자신을 인증하고 서비스나 엔드포인트 같은 워크로드 관련 정보를 조회하는 데 사용
- 보안 커넥션 시작할 인증서를 상대 클러스터가 사용하게 하려면?
- 서비스 어카운트 토큰과 원격 클러스터 필요
- 서비스 어카운트 토큰과 원격 클러스터 필요
- Istio가 원격 클러스터에서 정보를 쿼리하기 위해 인증할 때 필요한 것
- 원격 클러스터 접근용 시크릿 만들기 CREATING THE SECRETS FOR REMOTE CLUSTER ACCESS
- istioctl 의 create-remote-secret 명령어 사용 (기본 istio-reader-service-account 서비스 어카운트를 사용)
- 원격 클러스터 접근용으로 시크릿 생성
- 시크릿 생성 시 클러스터 이름을 지정하는 것이 중요 (Istio를 설치할 때 IstioOperator 에서 사용)
- east-cluster 및 west-cluster 값은 앞서 ‘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
- 기본-기본 배포 primary-primary deployment 에서는 east 가 west 쿼리 할 수 있게 반대로도 설정
- 이제 컨트롤 플레인이 상대 클러스터의 워크로드를 쿼리 가능
# 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
- istioctl 의 create-remote-secret 명령어 사용 (기본 istio-reader-service-account 서비스 어카운트를 사용)
- 다음: 클러스터 간 연결 설정
- 들어가며
- 클러스터 간 연결 설정하기 Setting up cross-cluster connectivity
- 들어가며 : north-south 트래픽, east-west 트래픽
- Istio의 인그레스 게이트웨이는 엔보이 프록시 기반
- 트래픽 유형
- north-south 트래픽: 기본적인 트래픽 유형. 인그레스 게이트웨이는 퍼블릭 네트워크에서 시작한 트래픽의 진입점으로, 트래픽을 조직의 내부 네트워크로 보냄
- east-west 트래픽 (그림 12.12 참조) : 서로 다른 내부 네트워크(여기서는 클러스터의 네트워크) 간의 트래픽
north-south 와 east-west 트래픽
- east-west 트래픽을 단순화하기 위한 클라우드 프로바이더 동작
- 네트워크 주소 공간이 겹치지 않는다면 가상 네트워크의 피어링을 활성화
- 피어링된 가상 네트워크의 서비스들은 IPv4 및 IPv6 주소로 직접 커넥션 시작
- east-west 게이트웨이를 사용하는 이유
- 네트워크 피어링은 클라우드 전용 기능
- 네트워크 피어링이 불가능한, 다른 클라우드 프로바이더나 온프레미스에 있는 클러스터에 연결하고 싶을 때 Istio가 제공하는 기능
- east-west 게이트웨이 사용 방식
- 반대편 클러스터 워크로드에 접근 가능한 로드 밸런서로 노출하여 사용
- 반대편 클러스터 워크로드에 접근 가능한 로드 밸런서로 노출하여 사용
- Istio의 east-west 게이트웨이 ISTIO’S EAST-WEST GATEWAY
- east-west 게이트웨이 목적
- 클러스터 간 east-west 트래픽의 진입점 역할
- 트래픽 진입 과정을 서비스 운영 팀에게 투명하게 만드는 것
- east-west 게이트웨이에서 목적을 달성하기 위해 수행해야 할 것 (필수)
- 클러스터 사이의 정밀한 트래픽 관리
- 암호화된 트래픽 라우팅
: 암호화된 상태로 라우팅해야 워크로드 간 상호 인증이 가능
- east-west 게이트웨이 구성 후 추가 리소스가 필요한가?
- 서비스 메시 운영자는 어떤 추가 리소스도 구성할 필요가 없어야 함
(즉, 어떤 이스티오 리소스도 추가 구성할 필요가 없어야 함)
- 서비스 메시 운영자는 어떤 추가 리소스도 구성할 필요가 없어야 함
- east-west 게이트웨이 구성 시
- 트래픽을 클러스터 내에서 라우팅할 때나, 클러스터 간에 라우팅할 때 차이가 없음
- 트래픽을 클러스터 내에서 라우팅할 때나, 클러스터 간에 라우팅할 때 차이가 없음
- 클러스터 간 연결 (north-south 트래픽, east-west 트래픽) 에서 가능한 것
- 워크로드는 서비스를 세밀하게 겨냥할 수 있음
- 상호 인증된 커넥션 시작 가능
- 클러스터 간 연결 구현의 이해를 위한 Istio 기능 (2가지)
- SNI 클러스터
- SNI 자동 통과
- east-west 게이트웨이 목적
- SNI 클러스터로 east-west 게이트웨이 설정하기 CONFIGURING EAST-WEST GATEWAYS WITH SNI CLUSTERS
- east-west 게이트웨이: SNI 클러스터를 모든 서비스에 추가 설정한 인그레스 게이트웨이
- SNI 클러스터
- 단순히 보통의 엔보이 클러스터 Envoy clusters 같은 것
(10장 10.3.2절의 ‘엔보이 클러스터 설정 쿼리하기’ 절 참조) - 트래픽이 라우팅될 수 있는 유사 워크로드 집합을 묶는 속성인 방향, 부분집합, 포트, FQDN을 포함
- 단순히 보통의 엔보이 클러스터 Envoy clusters 같은 것
- 엔보이 클러스터와 SNI 클러스터의 주요 차이점
- SNI 클러스터는 모든 엔보이 클러스터 정보를 SNI에 인코딩
- SNI 클러스터 인코딩의 효과
- east-west 게이트웨이는 클러스터가 SNI 안에 지정한 클러스터로 암호화된 트래픽을 라우팅할 수 있음
- ex) 클라이언트(webapp 같은)가 원격 클러스터의 워크로드(catalog 같은)로 커넥션을 시작할 때 겨냥하는 클러스터를 SNI에 인코딩 (그림 12.13)
- 클러스터 정보는 SNI에 인코딩된다.
- SNI에는 라우팅 결정을 지시하는 방향, 포트, 버전, 서비스 이름이 포함된다.
- ex) 클라이언트(webapp 같은)가 원격 클러스터의 워크로드(catalog 같은)로 커넥션을 시작할 때 겨냥하는 클러스터를 SNI에 인코딩 (그림 12.13)
- 암호화된 트래픽 라우팅을 통해
- 클라이언트는 세밀한 라우팅 결정을 내릴 수 있음
- 게이트웨이는 SNI 헤더에서 클러스터 정보를 읽어 트래픽을 클라이언트가 의도한 워크로드로 프록시할 수 있음
- 이 모든 작업이 안전하고 상호 인증한 커넥션을 워크로드 사이에 유지하는 중에 일어남
- east-west 게이트웨이는 클러스터가 SNI 안에 지정한 클러스터로 암호화된 트래픽을 라우팅할 수 있음
- east-west 게이트웨이: SNI 클러스터를 모든 서비스에 추가 설정한 인그레스 게이트웨이
- SNI 클러스터가 있는 east-west 게이트웨이 설치하기 Installing the east-west gateway with SNI clusters
- SNI 클러스터 설정은 옵트인 기능으로, 다음 IstioOpertor 정의처럼 환경 변수 ISTIO_META_ROUTER_MODE 로 게이트웨이 라우터 모드를 sni-dnat 으로 설정해서 활성화할 수 있음
# 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 리소스 이름 설정 시 주의사항
- 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 포트를 노출하기
- Istio는 영리해서, 딱 그때만 게이트웨이에 SNI 클러스터를 설정
- east-west 게이트웨이를 설치하고 라우터 모드를 sni-dnat으로 설정한 후 진행할 다음 단계
- SNI 클러스터 설정은 옵트인 기능으로, 다음 IstioOpertor 정의처럼 환경 변수 ISTIO_META_ROUTER_MODE 로 게이트웨이 라우터 모드를 sni-dnat 으로 설정해서 활성화할 수 있음
- SNI 자동 통과로 클러스터 간 트래픽 라우팅하기 ROUTING CROSS-CLUSTER TRAFFIC USING SNI AUTO PASSTHROUGH
- SNI 자동 통과 auto passthrough 이해를 돕는 사항
- 수동 SNI 통과가 SNI 헤더에 기반해 트래픽을 허용하도록 인그레스 게이트웨이를 설정한다는 것 (4장의 4.4.2절 참고)
- 이같은 사실은 허용된 트래픽을 라우팅하려면 서비스 운영자가 수작업으로 VirtualService 리소스를 정의해야 함을 보여줌
SNI 자동 통과 auto passthrough 로 트래픽을 라우팅하려면 VirtualService 리소스를 정의하는 것이 필수
- SNI 자동 통과란
- 이름과 같이, 허용된 트래픽을 라우팅하자고 VirtualService 를 수작업으로 만들 필요가 없음
- 라우팅은 SNI 클러스터를 사용해 수행할 수 있음
- SNI 클러스터는 라우터 모드가 sni-dnat 일때 east-west 게이트웨이에서 자동으로 설정됨
SNI 자동 통과를 통한 트래픽 라우팅은 sni-dnat 라우터 모드에서 초기화된 SNI 클러스터를 사용
- SNI 클러스터는 라우터 모드가 sni-dnat 일때 east-west 게이트웨이에서 자동으로 설정됨
- SNI 자동 통과는 Istio Gateway 리소스로 설정 가능
- 다음 정의에서는 SNI 헤더의 값이 *.local 인 모든 트래픽에 대해 SNI 자동 통과를 사용함
(이는 모든 쿠버네티스 서비스에 해당)# 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에 대해서만 트래픽을 허용한다.
- 다음 정의에서는 SNI 헤더의 값이 *.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 클러스터가 정의됐음을 보여줌
- SNI 자동 통과로 게이트웨이를 설정한 효과
- 게이트웨이에 들어오는 트래픽은 SNI 클러스터를 사용해 의도한 워크로드로 라우팅
- Istio 컨트롤 플레인은 이 리소스들의 생성을 지켜보고 있다가, 이제 클러스터 간 트래픽을 라우팅할 수 있는 경로가 존재함을 발견
- 따라서 컨트롤 플레인은 원격 클러스터에서 새로 발견한 엔드포인트로 모든 워크로드를 설정함
- 클러스터 간 워크로드 디스커버리 검증하기 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
카탈로그 엔드포인트는 east-west 게이트웨이 멀티 클러스터 포트를 참조 - 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 서비스 포트로 라우팅
- (참고) Istio 의 TCP Traffic 라우팅과 Traffic routing with SNI passthrough 는 4.4.2 를 확인
- 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 게이트웨이를 통과 지점으로 사용해 상호 인증 커넥션을 시작
- 앞서 east-cluster 의 워크로드가 west-cluster 에 노출됨
- 다중 클러스터 서비스 메시를 설정하는데 필요한 것 요약
- 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
- 서비스 어카운트 토큰과 인증서가 포함된 kubeconfig 를 사용해 각 컨트롤 플레인에 동료 클러스터에 대한 접근 권한을 제공함으로써 구현
- 이 과정은 istioctl 을 사용해 쉽게 진행했고, east-cluster 에만 적용
- 클러스터 간 워크로드 연결 Cross-cluster workload connectivity
- 다른 클러스터의 워크로드(다른 네트워크에 위치) 간에 트래픽을 라우팅하도록 east-west 게이트웨이를 설정하고, Istio가 워크로드가 위치한 네트워크를 알 수 있도록 각 클러스터에 네트워크 정보 레이블을 지정해 구현
- 클러스터 간 신뢰 설정 Configuring trust between clusters
- 상대 클러스터와 동일한 루트 신뢰로 중간 인증서를 발급함으로써 설정
- 상대 클러스터와 동일한 루트 신뢰로 중간 인증서를 발급함으로써 설정
- 클러스터 간 워크로드 디스커버리 Cross-cluster workload discovery
- SNI 자동 통과 auto passthrough 이해를 돕는 사항
- 들어가며 : north-south 트래픽, east-west 트래픽
- 클러스터 간 로드 밸런싱 Load-balancing across clusters
- 클러스터 간 로드 밸런싱
- 클러스터 지역 인식 로드 밸런싱을 살펴보기 (6장 언급)
- 다중 클러스터 서비스 메시가 있어 확인 가능
- 이를 시연하기 위해 2개의 샘플 서비스를 배포할 것
- 이 서비스들은 워크로드가 실행 중인 클러스터의 이름을 반환하도록 설정돼 있음
- 요청을 처리하는 워크로드의 위치를 쉽게 확인할 수 있음
- 요청을 처리하는 워크로드의 위치를 쉽게 확인할 수 있음
- west-cluster 에 첫 번째 서비스를 배포
# 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
- 양쪽 클러스터에서 서비스가 실행되면서 인그레스 게이트웨이에 엔드포인트가 설정돼 요청이 양쪽으로 분산
클러스터 간 로드 밸런싱 - kiali: west,east
west-cluster east-cluster - 기본적으로, Istio는 라운드 로빈 알고리듬으로 워크로드 간에 로드 밸런싱함 (그러므로 트래픽은 고르게 분산)
# 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": {} ...
- 지금도 훌륭하지만, 워크로드가 트래픽을 라우팅할 때 자신의 지역 내 워크로드를 우선하도록 지역 인식 로드 밸런싱을 사용하면 성능을 더 개선할 수 있음
- 앞선 장들에서 클라우드 프로바이더가 지역성 정보를 노드에 레이블로 추가한다고 하였음
- Istio는 레이블을 추출한 이 정보를 사용해 워크로드의 지역을 설정
- 클러스터 간 지역 인식 라우팅 검증하기 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
- 몇 초 정도 걸려 설정이 전파되면, 요청이 지역 정보를 사용해 동일 클러스터 안에서 라우팅 되는 것을 확인할 수 있음
# EXT_IP=$(kwest -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" # 동일 클러스터 안에서 라우팅 되는 것을 확인 for i in {1..20}; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; echo ; done | sort | uniq -c 20 "Hello from WEST"
- 기대한 대로 모든 요청은 west-cluster 내에서 라우팅 됨
- 트래픽을 라우팅하는 인그레스 게이트웨이에서 가장 가깝기 때문
- 모든 라우팅 결정을 엔보이 프록시가 내리므로, 컨트롤 플레인이 엔보이 프록시의 설정을 수정했으리라고 판단 가능
# iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' -o json ... "weight": 1, "locality": { "region": "westus", # priority 가 없으면(생략 시), 0으로 우선 순위가 가장 높음 "zone": "0" } ... "weight": 1, "priority": 1, # priority 0 다음으로, 두 번쨰 우선순위 "locality": { "region": "eastus", "zone": "0" } ...
- 우선순위가 가장 높은 호스트가 사용할 수 없는 상태가 되면, 트래픽은 우선순위가 낮은 호스트로 라우팅
- 우선순위가 가장 높은 호스트가 사용할 수 없는 상태가 되면, 트래픽은 우선순위가 낮은 호스트로 라우팅
- 실습을 위해 노드에 지역성 정보 레이블을 설정
- 클러스터 간 장애 극복 확인하기 VERIFYING CROSS-CLUSTER FAILOVER
- 간단한 백엔드 디플로이먼트가 실패하는 상황을 시뮬레이션
- 환경 변수 ERROR_RATE 값을 1로 설정해 요청이 실패하게 만들기
- 시간이 조금 지나면, 이상값 감지가 호스트가 비정상임을 감지하고 트래픽을 우선순위가 두 번째인 east-cluster 워크로드로 라우팅
# 신규 터미널 : 반복 접속 해두기 while true; do docker exec -it mypc curl -s -H "Host: simple-backend.istioinaction.io" http://$EXT_IP | jq ".body" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done ... "Hello from WEST" 2025-05-18 09:31:21 "Hello from EAST" # failover 시점 2025-05-18 09:31:23 ... # kwest -n istioinaction set env deploy simple-backend-west ERROR_RATE='1' kwest exec -it -n istioinaction deploy/simple-backend-west -- env | grep ERROR ERROR_RATE=1 # iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system --cluster 'outbound|80||simple-backend.istioinaction.svc.cluster.local' ENDPOINT STATUS OUTLIER CHECK CLUSTER 10.10.0.21:8080 HEALTHY FAILED outbound|80||simple-backend.istioinaction.svc.cluster.local 172.18.255.202:15443 HEALTHY OK outbound|80||simple-backend.istioinaction.svc.cluster.local
- 결과를 통해 클러스터 간 장애 극복이 작동함을 확인 가능
- 결과를 통해 클러스터 간 장애 극복이 작동함을 확인 가능
- 클러스터 간 트래픽은 상대 클러스터의 east-west 게이트웨이를 통과하며 SNI 통과로 처리된다는 것을 알 수 있음
- 이는 원격 클러스터에 도달한 트래픽의 로드 밸런싱에 영향을 미침
- 이 호출은 SNI/TCP 커넥션이라 게이트웨이가 TLS 커넥션을 종료하지 않으므로, east-west 게이트웨이는 커넥션을 그대로 백엔드 서비스에 전달할 수 밖에 없음
- 커넥션이 east-west 게이트웨이에서 백엔드 서비스까지 이어지기 때문에 요청 단위로 로드 밸런싱되지 않음
- 그러므로 클러스터 사이의 장애 극복이나 로드 밸런싱에서는, 클라이언트의 관점으로는 부하 분산이나 장애 극복이 수행되지만 트래픽이 원격 클러스터의 모든 인스턴스 사이에서 반드시 균등하게 분산되는 것은 아님
# (옵션) 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
- 마지막으로 확인할 기능: 클러스터 사이의 접근 제어
- 접근 제어를 하려면 믿을 수 있는 메타데이터를 만들어야 함
- 워크로드가 트래픽을 서로 인증해, 트래픽을 승인하거나 거부하는 결정을 내리는 데 사용 가능한 메타데이터
- 이를 시연하기 위한 시나리오
- 트래픽 출처가 인그레스 게이트웨이인 경우에만 서비스로의 트래픽을 허용하고 싶음
- 출처가 다르면 거절
- 시연 정책은 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 의 워크로드에서 요청을 만들어 정책을 시험
(이를 위해 임시 Pod - netshoot를 실행)# kwest run netshoot -n istioinaction --rm -it --image=nicolaka/netshoot -- zsh ----------------------------------- # curl -s webapp.istioinaction/api/catalog # 직접 요청하면 실패! curl -s simple-backend.istioinaction.svc.cluster.local RBAC: access denied # istio-ingressgateway 로 요청하면 성공! curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system ... # kiali 등 확인을 위해 반복 접속 실행 watch curl -s simple-backend.istioinaction.svc.cluster.local watch 'curl -s -H "Host: simple-backend.istioinaction.io" http://istio-ingressgateway.istio-system' exit -----------------------------------
- 정책이 인그레스 게이트웨이에서 들어온 트래픽을 허용한 것을 볼 수 있음
- 워크로드가 클러스터 간에 상호 인증해서, 정책이 ID 인증서에 인코딩된 인증 데이터를 접근 제어에 사용할 수 있음을 보여준다.
- 워크로드가 클러스터 간에 상호 인증해서, 정책이 ID 인증서에 인코딩된 인증 데이터를 접근 제어에 사용할 수 있음을 보여준다.
- 정책이 인그레스 게이트웨이에서 들어온 트래픽을 허용한 것을 볼 수 있음
- 로드 밸런싱, 지역 인식 라우팅, 클러스터 간 장애 극복, 상호 인증 트래픽, 접근 제어에 대한 예제를 살펴봄
- 다중 클러스터 서비스 메시에서의 워크로드가, 실행 중인 클러스터가 어디든 상관없이 Istio의 모든 기능을 사용할 수 있음을 확인함
(별도 설정을 하지 않아도)
- 다중 클러스터 서비스 메시에서의 워크로드가, 실행 중인 클러스터가 어디든 상관없이 Istio의 모든 기능을 사용할 수 있음을 확인함
- 요약
- Istio는 단일 컨트롤 플레인(기본-원격), 복제된 컨트롤 플레인(기본-기본), 외부 컨트롤 플레인이라는 세 가지 다중 서비스 메시 배포 모델을 지원
- istio-system 네임스페이스에 중간 인증서를 설치해 플러그인 CA 인증서를 사용하면 클러스터 간에 공통 신뢰를 구축할 수 있음
- 복제된 컨트롤 플레인 배포 모델에서 클러스터 간 워크로드를 찾는 방법
- 원격 클러스터의 서비스 어카운트를 ID로 사용하기
- 시크릿으로 서비스 어카운트 토큰을 상대편 클러스터에서 사용할 수 있게 하기
- east-west 게이트웨이를 사용해 다중 네트워크 서비스 메시의 네트워크를 연결할 수 있음
- sni-dnat 라우터 모드는 클러스터 간 트래픽을 세밀한 방식으로 라우팅하도록 SNI 클러스터를 설정
- east-west 게이트웨이는 트래픽을 자동으로 통과시키고 자동으로 설정된 SNI 클러스터를 바탕으로 라우팅하도록 설정할 수 있음
- Istio의 기능은 클러스터 간에도 단일 클러스터일 때와 같은 방식으로 동작
- 클러스터 간 로드 밸런싱
추가 실습 1 : east-west gateway 없이 native routing 으로 통신
목표 : east-west gateway 없이 native routing 으로 통신
- 현재 상태 확인
# 통신에 관련하는 파드들 모두가 각 클러스터 내부에 할당된 파드 IP로 엔드포인트 정보 알고 있음 iwest proxy-config endpoint deploy/istio-ingressgateway.istio-system | grep istioinaction 10.10.0.12:8080 HEALTHY OK outbound|80||webapp.istioinaction.svc.cluster.local 10.20.0.12:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local iwest proxy-config endpoint deploy/webapp.istioinaction | grep istioinaction 10.10.0.12:8080 HEALTHY OK outbound|80||webapp.istioinaction.svc.cluster.local 10.20.0.12:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local ieast proxy-config endpoint deploy/catalog.istioinaction | grep istioinaction 10.10.0.12:8080 HEALTHY OK outbound|80||webapp.istioinaction.svc.cluster.local 10.20.0.12:3000 HEALTHY OK outbound|80||catalog.istioinaction.svc.cluster.local ## (참고) 비교해보자 kwest get svc,ep -n istioinaction keast get svc,ep -n istioinaction # west-control-plane 노드에서 east 파드 IP로 접근 시도 docker exec -it west-control-plane curl -s 10.10.0.12:8080 # 성공 docker exec -it west-control-plane curl -s 10.20.0.12:8080 # 실패, 이유는? # east-control-plane 노드에서 west 파드 IP로 접근 시도 docker exec -it east-control-plane curl -s 10.10.0.12:8080 docker exec -it east-control-plane curl -s 10.20.0.12:8080
- 해결 : k8s 클러스터 간 파드 대역을 native routing 으로 통신 설정
# west k8s(kind) 배포 시 정보 podSubnet: 10.10.0.0/16 serviceSubnet: 10.100.0.0/24 # west-control-plane 라우팅 정보 확인 : 10.20.0.0/16 대역 라우팅이 없어서, default 라우팅 처리됨을 확인! docker exec -it west-control-plane ip -c route docker exec -it west-control-plane ip -c route | grep -v veth default via 172.18.0.1 dev eth0 172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2 # east k8s(kind) 배포 시 정보 podSubnet: 10.20.0.0/16 serviceSubnet: 10.200.0.0/24 # east-control-plane 라우팅 정보 확인 : 10.10.0.0/16 대역 라우팅이 없어서, default 라우팅 처리됨을 확인! docker exec -it east-control-plane ip -c route docker exec -it east-control-plane ip -c route | grep -v veth default via 172.18.0.1 dev eth0 172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.3 # k8s 클러스터 간 파드 대역을 native routing 으로 통신 설정 docker exec -it west-control-plane ip route add 10.20.0.0/16 via 172.18.0.3 docker exec -it east-control-plane ip route add 10.10.0.0/16 via 172.18.0.2 docker exec -it west-control-plane ip -c route | grep -v veth docker exec -it east-control-plane ip -c route | grep -v veth --- # (참고) 라우팅 제거 docker exec -it west-control-plane ip route delete 10.20.0.0/16 via 172.18.0.3 docker exec -it east-control-plane ip route delete 10.10.0.0/16 via 172.18.0.2 --- # Service IP CIDR docker exec -it west-control-plane ip route add 10.200.0.0/24 via 172.18.0.3 docker exec -it east-control-plane ip route add 10.100.0.0/24 via 172.18.0.2 docker exec -it west-control-plane ip route delete 10.200.0.0/24 via 172.18.0.3 docker exec -it east-control-plane ip route delete 10.100.0.0/24 via 172.18.0.2
- 접속 확인 → 반복 접속 실행 후 kiali 에서 확인
# mypc 에서 west istio-ingressgateway 호출 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: 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 에서 확인
추가 실습 2 : west k8s 와 east k8s 간 PodCIDR 네트워크 대역 중복 시
- 준비 : kind k8s 배포 시, PodCIDR 네트워크 대역 중복 설정 ⇒ 이후 12.3.2 ~ 12.3.6 까지 실습 진행하면서 확인
# 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 # 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.10.0.0/16 serviceSubnet: 10.100.0.0/24 EOF
14장 Istio의 요청 처리 기능 확장
배경 설명
- 엔보이 필터 이해하기
- 이스티오의 EnvoyFilter 리소스를 사용해 엔보이 직접 설정하기 Using Istio’s EnvoyFilter resource to configure Envoy directly
- 루아 Lua 를 사용해 요청 경로 커스터마이징하기 Using Lua to customize the request path
- 웹어셈블리 WebAssembly 를 사용해 요청 경로 커스터마이징하기 Using WebAssembly to customize the request path
- 들어가며
- Istio는 애플리케이션 네트워킹 기능으로 조직에 많은 가치를 제공할 수 있지만,
조직에서 Istio의 기본 버전이 충족하지 못하는 제약이나 가정이 있을 가능성이 높음- 이런 제약을 잘 충족시키도록 Istio 기능의 확장이 필요
- 이런 제약을 잘 충족시키도록 Istio 기능의 확장이 필요
- Istio 서비스 메시의 기본 구성 요소인 엔보이 프록시 살펴보기
- 메시 내부 서비스 사이의 요청 경로에서 애플리케이션 인스턴스와 함께 있는 서비스 프록시
- 서비스의 애플리케이션 네트워킹을 간단하게 만들어줄 수 있는 중요한 기능 집합 기본 제공
- 다만 맞춤형 통합 혹은 통합 최종 단계를 위해서는 엔보이를 강화해야 하는 상황에 직면할 수 있음
- 엔보이 확장의 예시
- 속도 제한 rate limiting 혹은 외부 인가 서비스와 통합 Integrating with rate limiting or external authorization services
- 헤더 추가, 제거, 수정 Adding, removing, or modifying headers
- 요청 페이로드를 보강하기 위해 다른 서비스 호출 Calling out to other services to enrich a request payload
- HMAC 서명/검증 같은 사용자 정의 프로토콜 구현 Implementing custom protocols like HMAC signing/verification
- 비표준 보안 토큰 처리 Non-standard security token handling
- 엔보이는 사용자에게 필요한 거의 모든 것을 제공할 수 있지만, 결국은 사용자의 특정 사용 사례에 맞게 맞춤 조정 필요
- 이번 장에서는 요청 경로에서 Istio를 확장하는 것을 살펴보기 (이는 필연적으로 엔보이 확장을 의미)
- Istio는 애플리케이션 네트워킹 기능으로 조직에 많은 가치를 제공할 수 있지만,
14.1 엔보이의 확장 기능
- [강력 추천 공식 문서] Envoy Life of a Request - Docs
- Request flow - Docs
- 다운스트림의 TCP 연결은 워커 스레드에서 실행되는 Envoy 리스너에 의해 허용됨
A TCP connection from downstream is accepted by an Envoy listener running on a worker thread. - 리스너 필터 체인이 생성되어 실행됨. SNI 및 기타 TLS 이전 정보를 제공할 수 있음. 완료되면 리스너는 네트워크 필터 체인과 매칭됨. 각 리스너는 대상 IP CIDR 범위, SNI, ALPN, 소스 포트 등의 조합으로 매칭되는 여러 필터 체인을 가질 수 있음. 전송 소켓(이 경우 TLS 전송 소켓)이 이 필터 체인과 연결됨
The listener filter chain is created and runs. It can provide SNI and other pre-TLS info. Once completed, the listener will match a network filter chain. Each listener may have multiple filter chains which match on some combination of destination IP CIDR range, SNI, ALPN, source ports, etc. A transport socket, in our case the TLS transport socket, is associated with this filter chain. - 네트워크 읽기 시, TLS 전송 소켓은 TCP 연결에서 읽은 데이터를 해독하여 추가 처리를 위해 해독된 데이터 스트림으로 변환함
On network reads, the TLS transport socket decrypts the data read from the TCP connection to a decrypted data stream for further processing. - 네트워크 필터 체인이 생성되어 실행됨. HTTP에서 가장 중요한 필터는 체인의 마지막 네트워크 필터인 HTTP 연결 관리자
The network filter chain is created and runs. The most important filter for HTTP is the HTTP connection manager, which is the last network filter in the chain. - HTTP 연결 관리자의 HTTP/2 코덱은 TLS 연결에서 복호화된 데이터 스트림을 여러 개의 독립적인 스트림으로 디프레임화하고 역다중화함. 각 스트림은 단일 요청과 응답을 처리
The HTTP/2 codec in HTTP connection manager deframes and demultiplexes the decrypted data stream from the TLS connection to a number of independent streams. Each stream handles a single request and response. - 각 HTTP 스트림에 대해 다운스트림 HTTP 필터 체인이 생성되어 실행됨. 요청은 먼저 CustomFilter를 통과하며, CustomFilter는 요청을 읽고 수정할 수 있음. 가장 중요한 HTTP 필터는 HTTP 필터 체인의 끝에 있는 라우터 필터. 라우터 필터에서 decodeHeaders가 호출되면 경로가 선택되고 클러스터가 선택됨. 스트림의 요청 헤더는 해당 클러스터의 업스트림 엔드포인트로 전달. 라우터 필터는 일치하는 클러스터가 이를 수행할 수 있도록 클러스터 관리자로부터 HTTP 연결 풀을 가져옴
For each HTTP stream, an Downstream HTTP filter chain is created and runs. The request first passes through CustomFilter which may read and modify the request. The most important HTTP filter is the router filter which sits at the end of the HTTP filter chain. When decodeHeaders is invoked on the router filter, the route is selected and a cluster is picked. The request headers on the stream are forwarded to an upstream endpoint in that cluster. The router filter obtains an HTTP connection pool from the cluster manager for the matched cluster to do this. - 엔드포인트를 찾기 위해 클러스터별 부하 분산이 수행됨. 클러스터의 회로 차단기를 검사하여 새 스트림이 허용되는지 확인함. 엔드포인트의 연결 풀이 비어 있거나 용량이 부족한 경우 엔드포인트에 대한 새 연결이 생성됨.
Cluster specific load balancing is performed to find an endpoint. The cluster’s circuit breakers are checked to determine if a new stream is allowed. A new connection to the endpoint is created if the endpoint’s connection pool is empty or lacks capacity. - 각 스트림에 대해 업스트림 HTTP 필터 체인이 생성되고 실행됨. 기본적으로 여기에는 해당 코덱으로 데이터를 전송하는 CodecFilter만 포함되지만, 클러스터에 업스트림 HTTP 필터 체인이 구성된 경우 해당 필터 체인이 각 스트림에서 생성되고 실행됨. 여기에는 재시도 및 섀도 요청(shadowed requests)을 위한 별도의 필터 체인이 생성되고 실행되는 것이 포함됨
For each stream an Upstream HTTP filter chain is created and runs. By default this only includes the CodecFilter, sending data to the appropriate codec, but if the cluster is configured with an upstream HTTP filter chain, that filter chain will be created and run on each stream, which includes creating and running separate filter chains for retries and shadowed requests. - 업스트림 엔드포인트 연결의 HTTP/2 코덱은 단일 TCP 연결을 통해 해당 업스트림으로 전송되는 다른 스트림과 요청 스트림을 다중화하고 프레임화함
The upstream endpoint connection’s HTTP/2 codec multiplexes and frames the request’s stream with any other streams going to that upstream over a single TCP connection. - 업스트림 엔드포인트 연결의 TLS 전송 소켓은 이러한 바이트를 암호화하여 업스트림 연결의 TCP 소켓에 작성
The upstream endpoint connection’s TLS transport socket encrypts these bytes and writes them to a TCP socket for the upstream connection. - 헤더, 본문, 트레일러(선택 사항)로 구성된 요청은 업스트림 프록시를 통해 전달되고, 응답은 다운스트림 프록시를 통해 전달됨. 응답은 요청과 반대 순서로 HTTP 필터를 통과. 즉, 코덱 필터에서 시작하여 업스트림 HTTP 필터를 통과한 후 라우터 필터를 거쳐 CustomFilter를 거쳐 다운스트림으로 전송됨.
The request, consisting of headers, and optional body and trailers, is proxied upstream, and the response is proxied downstream. The response passes through the HTTP filters in the opposite order from the request, starting at the codec filter, traversing any upstream HTTP filters, then going through the router filter and passing through CustomFilter, before being sent downstream. - 독립적인 반쪽 닫기(half-close)가 활성화된 경우, 요청과 응답이 모두 완료된 후(HTTP/2 스트림의 경우 양방향에서 END_STREAM이 관찰됨) 스트림이 삭제되고 응답에 성공(2xx) 상태 코드가 표시됨. 그렇지 않은 경우, 요청이 아직 완료되지 않았더라도 응답이 완료되면 스트림 삭제. 요청 후 처리는 통계를 업데이트하고, 액세스 로그에 기록하고, 추적 기간을 완료
If independent half-close is enabled the stream is destroyed after both request and response are complete (END_STREAM for the HTTP/2 stream is observed in both directions) AND response has success (2xx) status code. Otherwise the stream is destroyed when the response is complete, even if the request has not yet completed. Post-request processing will update stats, write to the access log and finalize trace spans.
- 다운스트림의 TCP 연결은 워커 스레드에서 실행되는 Envoy 리스너에 의해 허용됨
- Request flow - Docs
- 들어가며 : 필터 확장
- 엔보이 프록시의 강점
- 확장할 수 있게 구성됨
- 엔보이의 API를 설계하는 데 많은 고민과 주의가 반영되어 있음
- 다른 사람들이 작성한 확장 기능도 인기를 얻게 해준 큰 이유 중 하나
- 엔보이를 확장할 수 있는 주요 방법
- 필터 확장
- 엔보이 구성도 이해 시 확장 지점 및 애플리케이션 상 이점을 알 수 있음
- 엔보이 프록시의 강점
- 엔보이의 필터 체인 이해하기 Understanding Envoy’s filter chaining
- 3장에서는 그림 14.1과 같이 엔보이의 개념인 리스너, 루트, 클러스터를 살펴봄
- 이번 장에서는 그보다 자세한 구성 살펴보기
- 리스너와, 필터 및 필터 체인을 사용해 리스너 모델을 확장하는 방법
요청은 리스너를 통해 다운스트림 시스템에서 들어온 다음 라우팅 규칙을 거쳐 업스트림 서비스로 전송하는 클러스터로 전달된다.
- 리스너와, 필터 및 필터 체인을 사용해 리스너 모델을 확장하는 방법
- 엔보이의 리스너
- 네트워킹 인터페이스에 포트를 열고 들어오는 트래픽 수신을 시작하는 방법
- 엔보이는의 궁극적인 기능
- 네트워크 커넥션에서 바이트를 가져와 어떤 방식으로 처리하는 3계층과 4계층(L3/L4) 프록시
- 이것이 아키텍처의 첫 번째 중요 부분인 필터로 이어짐
- 리스너는 네트워크 스트림에서 바이트를 읽어들인 후 다양한 필터 혹은 기능 단계들을 거쳐 처리 (그림 14.2)
바이트는 리스너를 통해 네트워크에서 들어오고, 리스너는 네트워크 필터를 통해 바이트를 처리 - 엔보이의 네트워크 필터
- 가장 기본적인 필터
- 바이트 스트림에서 인코딩/디코딩 작업을 수행
- 필터 체인
- 스트림에서 필터가 둘 이상 순서대로 동작하도록 설정할 수 있음
- 필터 체인을 사용하면 프록시의 기능을 구현할 수 있음
- ex) 엔보이에는 대표적으로 다음과 같은 프로토콜용 네트워크 필터 존재
- MongoDB
- Redis
- Thrift
- Kafka
- HTTP Connection Manager (HCM)
- HCM은 가장 많이 사용하는 네트워크 필터 중 하나
- 바이트 스트림을 HTTP 기반 프로토콜(즉, HTTP 1.1, HTTP 2, gRPC, 최근에는 HTTP 3 등)의 HTTP 헤더, 바디, 트레일러로 변환하는 것에 관한 세부 사항을 추상화하는 역할을 함 (그림 14.3)
HCM은 바이트 스트림을 HTTP 요청으로 변환하고, 이를 헤더나 바디 세부 정보 같은 L7 속성을 바탕으로 라우팅 - HTTP 요청을 처리할 뿐 아니라 헤더, 경로 접두사 path prefix 등 요청 속성을 바탕으로 한 요청 라우팅, 액세스 로깅, 헤더 조작도 처리
- 필터 기반 아키텍처가 있어 HTTP 요청에서 동작하는 HTTP 필터를 순서대로 또는 연쇄로 설정하거나 구축 가능
- 기본 HTTP 필터의 예시: 전체목록 - Docs
- Cross-origin resource sharing (CORS)
- Cross-site request forgery prevention (CSRF)
- ExternalAuth 외부 인증
- RateLimit 속도 제한
- Fault injection 결합 주입
- gRPC/JSON transcoding 트랜스코딩
- Gzip
- Lua 루아
- Role-based access control (RBAC) 역할 기반 접근 제어
- Tap
- Router 라우터
- WebAssembly (Wasm) 웹어셈블리
- HTTP 요청 하나에서 HTTP 필터가 순서대로 동작하도록 설정 가능
- HTTP 필터들은 요청을 업스트림 클러스터로 보내는 터미널 필터로 끝나야 함
- 이를 담당하는 HTTP 필터는 라우터 필터 (그림 14.14)
- 라우터 필터는 요청을 적절한 업스트림 클러스터를 찾아 전달하며, 이때 타임아웃과 재시도 파라미터를 설정할 수 있음 - Docs
HCM 에는 HTTP 요청을 처리하는 필터 체인이 있으며, 라우팅 필터로 끝난다 https://www.anyflow.net/sw-engineer/istio-overview-4
- 또한 사용자는 엔보이의 핵심 코드를 변경하지 않고도 자신만의 필터를 직접 작성하고 프록시 위에 얹어 계층화할 수 있음
- 예를 들어, 이스티오의 프록시는 데이터 플레인용으로 엔보이 위에 필터를 추가해 커스텀 엔보이를 빌드
- 그렇지만 이렇게 커스텀 엔보이 프록시 빌드를 도입하면, 유지 보수할 것이 많아질 수 있고 개발자가 C++를 사용해야 함
- 확장용 필터 Filters intended for extension
- 자신만의 필터를 C++로 작성해 프록시에 내장할 수 있지만, 이와 관련된 내용은 굉장히 방대 (Istio 스터디의 범위를 넘어섬)
- 다음 HTTP 필터들을 사용하여 엔보이의 HTTP 기능을 확장할 수 있음
(필터 작성 등과 같이 엔보이 바이너리 자체에 변경 사항을 컴파일하지 않아도 가능)- 외부 처리 External processing
- 루아 Lua
- 웹어셈블리 Wasm (WebAssembly)
- HTTP 필터를 사용 시 효과
- 외부 서비스를 호출하도록 설정
- 루아 스크립트를 실행
- 커스텀 코드를 실행해 HTTP 요청이나 응답을 처리할 때 HCM의 기능을 강화할 수 있음
- 외부 서비스를 호출해 처리하는 부분에 대해 이야기할 때는 속도 제한 필터를 중점으로 확인
- 또한 외부 인가를 요청할 수도 있음 (9장)
- 엔보이에는 범용 처리 목적으로 외부 서비스를 호출하기 위한 외부 처리 필터가 있음
- 외부 처리 필터는 코드베이스에 존재하기는 하지만, 아무것도 하지 못함
- 전역 속도 제한 필터를 사용하는 등 외부 서비스를 호출하는 다른 방법을 살펴볼 것
- Istio의 데이터 플레인 커스터마이징하기 Customizing Istio’s data plane
- 엔보이의 필터 아키텍처를 깊이 있게 이해한 것을 바탕으로, 이후 절들에서 다음 방법 중 하나를 사용해 엔보이 데이터 플레인의 기능을 확장하기
- 엔보이 HTTP 필터를 이스티오 API에서 EnvoyFilter 리소스로 설정하기
- 속도 제한 서버 RLS, Rate-Limit Server 호출하기
- 루아 스크립트를 구현해 루아 HTTP 필터에 불러오기
- 웹어셈블리 HTTP 필터용 웹어셈블리 모듈(Wasm 모듈) 구현하기
- 엔보이의 필터를 직접 설정하는 방법을 이해해야 함
- 이해를 위해 Istio의 EnvoyFilter 리소스를 사용할 것
- 엔보이의 필터 아키텍처를 깊이 있게 이해한 것을 바탕으로, 이후 절들에서 다음 방법 중 하나를 사용해 엔보이 데이터 플레인의 기능을 확장하기
14.2 EnvoyFilter 리소스로 엔보이 필터 설정하기
- [실습 환경 구성] k8s(1.23.17) 배포 & mypc 컨테이너
# git clone https://github.com/AcornPublishing/istio-in-action cd istio-in-action/book-source-code-master pwd # 각자 자신의 pwd 경로 code . # 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능 kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP hostPort: 30000 - containerPort: 30001 # Prometheus hostPort: 30001 - containerPort: 30002 # Grafana hostPort: 30002 - containerPort: 30003 # Kiali hostPort: 30003 - containerPort: 30004 # Tracing hostPort: 30004 - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS hostPort: 30005 - containerPort: 30006 # TCP Route hostPort: 30006 - containerPort: 30007 # kube-ops-view hostPort: 30007 extraMounts: # 해당 부분 생략 가능 - hostPath: /Users/gasida/Downloads/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정 containerPath: /istiobook networking: podSubnet: 10.10.0.0/16 serviceSubnet: 10.200.0.0/22 EOF # 설치 확인 docker ps # 노드에 기본 툴 설치 docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y' # (옵션) kube-ops-view helm repo add geek-cookbook https://geek-cookbook.github.io/charts/ helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view ## kube-ops-view 접속 URL 확인 open "http://localhost:30007/#scale=1.5" open "http://localhost:30007/#scale=1.3" # (옵션) metrics-server helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/ helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
- kind docker network 에 테스트용 PC(실제로는 컨테이너) 배포
# kind 설치 시 kind 이름의 도커 브리지가 생성된다 : 172.18.0.0/16 대역 docker network ls docker inspect kind # mypc 컨테이너 기동 : kind 도커 브리지를 사용하고, 컨테이너 IP를 지정 없이 혹은 지정 해서 사용 docker run -d --rm --name mypc --network kind --ip 172.18.0.100 nicolaka/netshoot sleep infinity # IP 지정 실행 시 혹은 IP 지정 실행 시 에러 발생 시 아래 처럼 IP 지정 없이 실행 docker run -d --rm --name mypc --network kind nicolaka/netshoot sleep infinity # IP 지정 없이 실행 시 docker ps # kind network 중 컨테이너(노드) IP(대역) 확인 docker ps -q | xargs docker inspect --format '{{.Name}} {{.NetworkSettings.Networks.kind.IPAddress}}' /mypc 172.18.0.100 /myk8s-control-plane 172.18.0.2 # 동일한 docker network(kind) 내부에서 컨테이너 이름 기반 도메인 통신 가능 확인! docker exec -it mypc ping -c 1 172.18.0.2 docker exec -it mypc ping -c 1 myk8s-control-plane docker exec -it myk8s-control-plane ping -c 1 mypc
- [실습 환경 구성] (편리성 설정) MetalLB 배포
- MetalLB 배포
# MetalLB 배포 kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.9/config/manifests/metallb-native.yaml # 확인 kubectl get crd kubectl get pod -n metallb-system # IPAddressPool, L2Advertisement 설정 : MetalLB 파드(speaker) 배포 정상 완료 후 아래 설정 실행하자. cat << EOF | kubectl apply -f - apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default namespace: metallb-system spec: addresses: - 172.18.255.101-172.18.255.120 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default namespace: metallb-system spec: ipAddressPools: - default EOF # 확인 kubectl get IPAddressPool,L2Advertisement -A
- 샘플 애플리케이션 배포 후 확인
# cat << EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: LoadBalancer EOF # 확인 kubectl get deploy,pod,svc,ep kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}' LBIP=$(kubectl get svc nginx-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') # 외부?에서 각 클러스터의 Service(LB)로 접속(인입) 확인 : TCP 80 포트 사용으로 편리하다! docker exec -it mypc curl -s $LBIP docker exec -it mypc curl -s $LBIP -v -I # 확인 후 삭제 kubectl delete deploy,svc --all
- MetalLB 배포
- [실습 환경 구성] istio 1.17.8 설치 - Docs , Install , profile
# myk8s-control-plane 진입 후 설치 진행 docker exec -it myk8s-control-plane bash ----------------------------------- # (옵션) 코드 파일들 마운트 확인 tree /istiobook/ -L 1 혹은 git clone ... /istiobook # istioctl 설치 export ISTIOV=1.17.8 echo 'export ISTIOV=1.17.8' >> /root/.bashrc curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh - cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl # demo 프로파일 컨트롤 플레인 배포 istioctl install --set profile=demo --set values.global.proxy.privileged=true -y # 보조 도구 설치 kubectl apply -f istio-$ISTIOV/samples/addons # 빠져나오기 exit ----------------------------------- # 설치 확인 : istiod, istio-ingressgateway, crd 등 kubectl get istiooperators -n istio-system -o yaml kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system kubectl get cm -n istio-system istio -o yaml kubectl get crd | grep istio.io | sort # 실습을 위한 네임스페이스 설정 kubectl create ns istioinaction kubectl label namespace istioinaction istio-injection=enabled kubectl get ns --show-labels # istio-ingressgateway 서비스 : nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집) kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}' kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "LoadBalancer", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}' kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}' kubectl describe svc -n istio-system istio-ingressgateway # NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004) kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}' kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}' kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}' kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}' # Prometheus 접속 : envoy, istio 메트릭 확인 open http://127.0.0.1:30001 # Grafana 접속 open http://127.0.0.1:30002 # Kiali 접속 : NodePort open http://127.0.0.1:30003 # tracing 접속 : 예거 트레이싱 대시보드 open http://127.0.0.1:30004
- 들어가며 : tap 필터 (실습)
- Istio의 데이터 플레인을 확장하는 첫 번째 단계
- 엔보이의 기존 필터가 우리가 찾고 있는 확장 유형을 달성하는 데 충분하지 파악하는 것
- 필터가 존재하는 경우, EnvoyFilter 리소스를 사용해 Istio의 데이터 플레인을 직접 설정할 수 있음
- Istio의 API 가 EnvoyFilter 리소스에 수행하는 것
- 일반적으로 특정 네트워크나 보안 시나리오에 초점을 맞춰 기저 underlying 엔보이의 설정을 추상화함
- VirtualService, DestinationRule, and AuthorizationPolicy 같은 리소스는 모두 결국 엔보이 설정으로 변환되며, 필터 체인에서 특정 HTTP 필터를 설정하기도 함
- Istio는 기저 엔보이에서 할 수 있는 모든 필터나 설정을 노출하려고 시도하지 않으며, 따라서 엔보이를 직접 설정해야 하는 상황이 발생할 수 있음
- Istio의 EnvoyFilter 리소스 존재 목적
- Istio의 고수준 API에서 노출하지 않는 엔보이의 일부를 설정하거나 조정해야 하는 고급 사용 사례를 위한 것
- 엔보이의 거의 모든 것을 설정할 수 있으며(일부 제한 있음) 리스너, 루트, 클러스터, 필터가 포함됨
- Istio의 고급 사용자용이며 비상 수단
- Istio API 로 EnvoyFilter 설정 시 유의사항
- 배포하는 모든 EnvoyFilter 의 유효성을 반드시 확인해야 함
- 기저 엔보이 API는 Istio 버전 간에 언제든 달라질 수 있음
- 이전 버전과의 호환성은 어떤 것도 가정해서는 안 됨
- 이 API를 잘못 설정하면 Istio 데이터 플레인 전체가 멈출 수도 있음
- 예제를 보고 어떻게 작동하는지 이해해보기 - 사용할 서비스 배포
# kubectl get envoyfilter -A NAMESPACE NAME AGE istio-system stats-filter-1.13 12m istio-system stats-filter-1.14 12m istio-system stats-filter-1.15 12m istio-system stats-filter-1.16 12m istio-system stats-filter-1.17 12m istio-system tcp-stats-filter-1.13 12m istio-system tcp-stats-filter-1.14 12m istio-system tcp-stats-filter-1.15 12m istio-system tcp-stats-filter-1.16 12m istio-system tcp-stats-filter-1.17 12m # 배포 kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction kubectl apply -f ch9/sleep.yaml -n istioinaction # 확인 kubectl get gw,vs,dr -n istioinaction NAME AGE gateway.networking.istio.io/coolstore-gateway 50s NAME GATEWAYS HOSTS AGE virtualservice.networking.istio.io/webapp-virtualservice ["coolstore-gateway"] ["webapp.istioinaction.io"] 50s # 호출 확인 : mypc EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog # 신규 터미널 : mypc 에서 반복 접속 EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') while true; do docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done # 호출 확인 : 자신의 호스트 PC curl -s http://webapp.istioinaction.io:30000 curl -s http://webapp.istioinaction.io:30000/api/catalog
- webapp 서비스를 거쳐 흐르는 특정 요청을 디버깅할 도구로 데이터 플레인을 확장하고 싶다고 가정
- 엔보이를 커스텀 필터로 확장할 수도 있겠지만, 이런 기능을 위한 tap 필터가 있음
- webapp 서비스에 이 필터를 설정하려면 EnvoyFilter 리소스를 사용하면 가능
(Istio의 API가 이 필터를 노출하고 있지는 않으니까)
- webapp 서비스에 이 필터를 설정하려면 EnvoyFilter 리소스를 사용하면 가능
- EnvoyFilter 리소스에 대해 알아야 할 첫 번째 사항
- 달리 지정하지 않은 한 네임스페이스의 모든 워크로드에 적용
- istio-system 네임스페이스에 EnvoyFilter 리소스를 만들었다면 메시의 모든 워크로드에 적용됨
- 네임스페이스 내에서 커스텀 EnvoyFilter 설정을 적용할 워크로드를 좀 더 특정하기
- workloadSelector 를 사용
- EnvoyFilter 리소스에 대해 알아야 할 두 번째 사항
- 다른 Istio 리소스가 모두 변환되고 설정된 후에야 적용됨
- ex) VirtualService or DestinationRule 리소스가 있다면, 이 설정들이 먼저 데이터 플레인에 적용
- EnvoyFilter 리소스로 워크로드를 설정할 때 각별히 주의해야 함
- 엔보이 명명 규칙과 설정 세부 사항을 잘 알아둬야 함
- 이는 정말 Istio API의 고급 사용법으로, 잘못 설정하면 메시를 마비시킬 수 있음
- 엔보이를 커스텀 필터로 확장할 수도 있겠지만, 이런 기능을 위한 tap 필터가 있음
- 예제에서 수행할 것 (그림 14.5)
- webapp 워크로드의 데이터 플레인을 거치는 메시지를 샘플링하도록 엔보이의 tap 필터를 설정하려고 함 - Docs
- 요청이나 응답이 tap 필터를 지나 흐를 때마다 tap 필터는 그 요청/응답을 어떤 수신 에이전트로 스트리밍
(예제에서는 콘솔/CLI로 스트리밍)Envoy HTTP 탭 필터 사용 시, 클라이언트나 업스트림에 영향을 주지 않고 요청과 응답을 수정하지 않고 스트리밍할 수 있어 데이터 플레인을 디버깅하고 검사할 수 있다.
- EnvoyFilter 리소스 살펴보기
# cat ch14/tap-envoy-filter.yaml apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: tap-filter namespace: istioinaction spec: workloadSelector: labels: app: webapp # 워크로드 셀렉터 configPatches: - applyTo: HTTP_FILTER # 설정할 위치 match: context: SIDECAR_INBOUND listener: portNumber: 8080 filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: # 엔보이 설정 패치 operation: INSERT_BEFORE value: name: envoy.filters.http.tap typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap" commonConfig: adminConfig: configId: tap_config
- EnvoyFilter 를 istioinaction 네임스페이스에 배포했음
- workloadSelector를 사용해 이 설정을 적용할 워크로드를 구체적으로 지정
- 다음으로 엔보이 설정에서 패치할 위치를 지정해야 함
- 예제에서는 인바운드 리스너(SIDECAR_INBOUDN)의 HTTP_FILTER가 되도록 지정
- 앞서 언근한 바와 같이 리스너를 위한 네트워크 필터들이 있으며, 그중 하나가 HCM
- HCM에도 HTTP 요청을 처리하는 HTTP용 필터 체인이 있음
- 또한 이 예제에서는 특정 리스너도 지정했는데, HCM을 8080 포트에 바인딩된 리스너를 지정함
- 마지막으로, 이 HCM HTTP 필터 체인에서 envoy.filters.http.router HTTP 필터를 고름
- 이 필터를 고른 이유는, 새로운 tap 필터를 이 router 필터 바로 앞에 배치할 것이기 때문 (다음 절)
- EnvoyFilter 리소스의 patch 부분에서 설정을 어떻게 패치할지 지정
- 여기서는 앞서 설정 부분에서 선택한 필터 앞에 설정을 병합함
- 추가하는 필터인 envoy_filters.http.tap 은 HCM 필터 체인에서 envoy.filters.http.router 앞에 위치함
- tap 필터 설정의 구조를 명확하게 해야 하므로 명시적인 타입을 지정 - Docs
- EnvoyFilter 를 istioinaction 네임스페이스에 배포했음
- EnvoyFilter를 istioinaction 네임스페이스에 webapp 워크로드에 적용
# cat ch14/tap-envoy-filter.yaml kubectl apply -f ch14/tap-envoy-filter.yaml kubectl get envoyfilter -n istioinaction NAME AGE tap-filter 10s # docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json ... { "name": "envoy.filters.http.tap", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.http.tap.v3.Tap", "commonConfig": { "adminConfig": { "configId": "tap_config" } } } }, { "name": "envoy.filters.http.router", "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router" } } ...
- tap 기능이 동작하는지 확인 - 2개의 터미널 창이 필요
- 창 하나에서는 curl 로 tap 설정을 전달해 webapp 워크로드에서 tap을 시작# 터미널 1 : 포트 포워딩 설정 후 tap 시작 kubectl port-forward -n istioinaction deploy/webapp 15000 & curl -X POST -d @./ch14/tap-config.json localhost:15000/tap # 터미널 2 : 기존 반복 접속하는 것 활용 EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog while true; do docker exec -it mypc curl -s -H "x-app-tap: true" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog ; echo ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done # 터미널 3 : 로그 확인 docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level http:debug docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level tap:debug kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f 2025-05-18T05:59:47.704918Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:329 [C2100] new stream thread=31 2025-05-18T05:59:47.705028Z debug envoy http external/envoy/source/common/http/conn_manager_impl.cc:1049 [C2100][S11845543740779751481] request headers complete (end_stream=true): ':authority', 'webapp.istioinaction.io' ':path', '/api/catalog' ':method', 'GET' 'user-agent', 'curl/8.7.1' 'accept', '*/*' 'x-app-tap', 'true' ... 2025-05-18T05:59:47.713852Z debug envoy tap external/envoy/source/extensions/filters/http/tap/tap_config_impl.cc:172 submitting buffered trace sink thread=31 2025-05-18T05:59:47.713860Z debug envoy tap external/envoy/source/extensions/common/tap/admin.cc:145 admin submitting buffered trace to main thread thread=31 ...
- tap 을 시작한 창에 tap 출력이 표시돼야 함
- tap 출력은 헤더, 바디, 트레일러 등 요청에 대한 모드 정보 제공
- tap 을 시작한 창에 tap 출력이 표시돼야 함
- Istio의 데이터 플레인을 확장하는 첫 번째 단계
14.3 외부 호출로 요청 속도 제한하기
- 들어가며 : 속도 제한 소개 - Docs Rate-limiting requests with external call-out
- 앞 절에서는 기본 HTTP 필터에 있는 기능으로 Istio 데이터 플레인을 확장
- 외부 호출 기능으로 데이터 플레인을 확장하는 기본 필터들도 있음
- 필터들을 사용해 외부 서비스를 호출하고, 요청을 계속할지 여부나 방법을 결정할 수 있는 기능을 수행하게 할 것
- 속도 제한 서비스를 호출하도록 Istio 데이터 플레인을 설정하는 방법 살펴보기
: 특정 워크로드에서 서비스 측 속도 제한을 적용하기 위함동일한 서비스의 여러 복제본은 특정 서비스에 대한 글로벌 제한 속도를 받기 위해 동일한 속도 제한 서비스를 호출한다. - 속도 제한에 대한 특정 호출이 나오는 곳
- 엔보이 HTTP 필터에서 나옴
- Istio가 데이터 플레인에 엔보이를 사용하는 것 같은 메커니즘
- 엔보이에서 속도 제한을 하는 방법
- 네트워크 필터
- 로컬 속도 제한
- 글로벌 속도 제한 (살펴볼 것)
- 글로벌 속도 제한 사용 시 동작 방식
- 특정 워크로드의 모든 엔보이 프록시가 동일한 속도 제한 서비스를 호출
- 이 속도 제한 서비스는 백엔드 글로벌 키-값 저장소를 호출 (그림 14.7 참조)
- 이 구조를 통해 서비스의 복제본 개수에 상관없이 속도 제한이 적용되도록 할 수 있음
Envoy의 전역 속도 제한을 사용하면 속도 제한 서버를 호출하여 특정 요청에 속도 제한이 필요한지 여부를 결정할 수 있다. 요청의 속성이 속도 제한 서버로 전송되어 결정을 내린다.
- 엔보이 글로벌(전역) 속도 제한 사용 시
- 속도 제한 서버를 호출해 특정 요청에 속도 제한을 적용해야 하는지 여부를 결정할 수 있음
- 결정을 내리고자 요청 속성을 속도 제한 서버로 전송
- 속도 제한을 수행하기 위한 전제 조건
- 엔보이 글로벌(전역) 속도 제한 사용 시
- 엔보이 속도 제한 이해하기 Understanding Envoy rate limiting
- 들어가며
- 속도 제한 작동 이해가 필요 (엔보이 속도 제한 서버 RLS 구성 전)
- 엔보이의 HTTP 전역 속도 제한을 살펴보기
- HTTP 필터로 존재
- HCM에서 HTTP 필터 체인으로 설정해야 함
- 속도 제한 필터에서 HTTP 요청을 처리하는 과정
- 요청에서 특정 속성을 가져옴
- RLS로 보내 평가를 받음
- 디스크립터 descripter: 엔보이 속도 제한에서 속성들 혹은 속성 그룹들을 가리킴
- HTTP 요청의 디스크립터 or 속성의 구성 요소
- 원격 주소일 수도, 요청 헤더일 수도, 목적지일 수도, 혹은 요청의 어떤 일반 속성 같은 것일 수도 있음
- 원격 주소일 수도, 요청 헤더일 수도, 목적지일 수도, 혹은 요청의 어떤 일반 속성 같은 것일 수도 있음
- 속성 제한 서버 RLS의 동작 (그림 14.8)
- 전송된 요청의 속성을 미리 정의한 속성 집합과 비교해 일치하는 속성의 카운터를 늘림
- 카운트할 속성은 트리 형태로 그룹화하거나 정의해 결정할 수 있음
- 속성 또는 속성 집합이 속도 제한 서버 정의와 일치하면, 해당 제한의 횟수를 늘림
- 횟수가 임계값을 초과하면 해당 요청에는 속도 제한이 적용
요청 속성은 속도 제한 서버로 전송되며, 속도 제한 서버는 미리 설정한 디스크립터 집합과 비교해 속도 제한 결정을 내린다.
- 속도 제한 작동 이해가 필요 (엔보이 속도 제한 서버 RLS 구성 전)
- 엔보이 속도 제한 서버 설정하기 CONFIGURING THE ENVOY RATE-LIMIT SERVER
- 속성 카운터와 한도가 포함된 속도 제한 서버 설정을 생성해보기
- 예제에서 수행할 것
- 조직에서 보유한 로열티 등급에 따라 특정 사용자 집단을 제한하고자 함
- 요청의 로열티 등급은 x-loyalty 헤더를 검사해 판단할 수 있음
- 골드 등급(x-loyalty: gold)의 사용자 그룹: 요청을 분당 10개까지 허용
- 실버 등급(x-loyalty: silver)의 사용자 그룹: 요청을 분당 5개까지 허용
- 브론즈 등급(x-loyalty: bronze)의 사용자 그룹: 요청을 분당 3개까지 허용
- 식별할 수 없는 로열티 등급의 사용자 그룹: 분당 요청이 하나를 넘어가면 속도 제한이 처리를 제한
- 요청이 디스크립터를 포착하는 속도 제한 서버 설정
# cat ch14/rate-limit/rlsconfig.yaml apiVersion: v1 kind: ConfigMap metadata: name: catalog-ratelimit-config namespace: istioinaction data: config.yaml: | domain: catalog-ratelimit descriptors: - key: header_match value: no_loyalty rate_limit: unit: MINUTE requests_per_unit: 1 - key: header_match value: gold_request rate_limit: unit: MINUTE requests_per_unit: 10 - key: header_match value: silver_request rate_limit: unit: MINUTE requests_per_unit: 5 - key: header_match value: bronze_request rate_limit: unit: MINUTE requests_per_unit: 3
- 실제 요청 헤더를 직접 다루지 않고, 요청의 일부로 전송된 속성만 다루고 있음을 유의
(아래에서 속성 정의 방법 살펴볼 ㅇ}정) - 속도 제한 서버 설정은 속도를 제한하기 위해 따라야 하는 규칙을 정의
- Istio 데이터 플레인을 거쳐 요청이 처리될 때 속성들이 속도 제한 서버로 전송
속성이 규칙 조건에 일치하면 그에 따라 처리를 제한
- 요청 경로에 속도 제한 걸기 CONFIGURING THE REQUEST PATH FOR RATE LIMITING
- 속도 제한 서버 설정을 만들고 난 후
- 특정 요청 경로에 취하는 속도 제한 조치 rate-limit action 설정 필요
- 특정 요청에 대해 어떤 속성을 전송할 것인지 엔보이에 설정하는 것
- ex) catalog 서비스를 /items 경로로 호출하면 요청에 x-loyalty 헤더가 있는지와 어느 그룹에 속하는지를 포착하고자 함
- 적절한 속성 action 을 속도 제한 서버로 보내도록 설정하기
- 특정 엔보이 루트 설정에 rate_limit 설정 지정 필요
- Istio에는 아직 전용 API가 없어, EnvoyFilter 리소스를 사용해야 함
- 특정 요청 경로에 취하는 속도 제한 조치 rate-limit action 설정 필요
- catalog 서비스의 모든 경로에 속도 제한 조치를 지정하는 방법
# cat ch14/rate-limit/catalog-ratelimit-actions.yaml apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: catalog-ratelimit-actions namespace: istioinaction spec: workloadSelector: labels: app: catalog configPatches: - applyTo: VIRTUAL_HOST match: context: SIDECAR_INBOUND routeConfiguration: vhost: route: action: ANY patch: operation: MERGE # Applies the rate limit rules. value: rate_limits: # 속도 제한 조치 - actions: - header_value_match: descriptor_value: no_loyalty expect_match: false headers: - name: "x-loyalty" - actions: - header_value_match: descriptor_value: bronze_request headers: - name: "x-loyalty" exact_match: bronze - actions: - header_value_match: descriptor_value: silver_request headers: - name: "x-loyalty" exact_match: silver - actions: - header_value_match: descriptor_value: gold_request headers: - name: "x-loyalty" exact_match: gold
- 규칙들을 속도 제한 서버와 함께 배포하고 데이터 플레인을 설정하는 방법 살펴보기
- k8s configmap 으로 규칙을 배포하고, 속도 제한 서버를 레디스 백엔드와 함께 배포
# tree ch14/rate-limit ch14/rate-limit ├── catalog-ratelimit-actions.yaml ├── catalog-ratelimit.yaml ├── rls.yaml └── rlsconfig.yaml cat ch14/rate-limit/rlsconfig.yaml cat ch14/rate-limit/rls.yaml # kubectl apply -f ch14/rate-limit/rlsconfig.yaml -n istioinaction kubectl apply -f ch14/rate-limit/rls.yaml -n istioinaction # 확인 kubectl get cm -n istioinaction catalog-ratelimit-config kubectl get pod -n istioinaction NAME READY STATUS RESTARTS AGE ratelimit-99d5d9c5-q4zcm 1/1 Running 2 (25s ago) 34s redis-6cf4ff9768-x97m8 1/1 Running 0 34s ...
- 엔보이가 속성을 속도 제한 서버로 보내게 설정하는 EnvoyFilter 리소스 적용
- 속도 제한 서버가 개수를 세어 속도 제한을 처리할 수 있게끔 함
# 기존에 반복 호출은 취소해두자 # cat ch14/rate-limit/catalog-ratelimit.yaml cat ch14/rate-limit/catalog-ratelimit-actions.yaml kubectl apply -f ch14/rate-limit/catalog-ratelimit.yaml -n istioinaction kubectl apply -f ch14/rate-limit/catalog-ratelimit-actions.yaml -n istioinaction kubectl get envoyfilter -A NAMESPACE NAME AGE istioinaction catalog-ratelimit-actions 27s istioinaction catalog-ratelimit-filter 28s ...
- 속도 제한 서버가 개수를 세어 속도 제한을 처리할 수 있게끔 함
- 속도 제한 기능을 시험해보기 위해 sleep 앱을 배포하고 catalog 서비스를 호출하는 클라이언트를 시뮬레이션
# sleep 앱에서 catalog 서비스를 호출 시도 : 대략 1분에 한 번 정도 호출 성공! >> x-loyalty 헤더가 없을 때 속도 제한 값! kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v ... kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://catalog/items -v ... < HTTP/1.1 429 Too Many Requests < x-envoy-ratelimited: true ... # silver 헤더는? kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl -H "x-loyalty: silver" http://catalog/items -v ... # docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4' docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'InboundPassthroughClusterIpv4' -o json | grep actions docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json | grep actions docker exec -it myk8s-control-plane istioctl proxy-config route deploy/catalog.istioinaction --name 'inbound|3000||' -o json ... "rateLimits": [ { "actions": [ { "headerValueMatch": { "descriptorValue": "no_loyalty", "expectMatch": false, "headers": [ { "name": "x-loyalty" } ] } } ] }, { "actions": [ { "headerValueMatch": { "descriptorValue": "bronze_request", "headers": [ { "name": "x-loyalty", "exactMatch": "bronze" ...
- 다음 실습을 위해 리소스 삭제
# kubectl delete envoyfilter -n istioinaction --all kubectl get envoyfilter -A # kubectl delete -f ch14/rate-limit/rlsconfig.yaml -n istioinaction kubectl delete -f ch14/rate-limit/rls.yaml -n istioinaction
- 속도 제한 서버 설정을 만들고 난 후
- 들어가며
14.4 루아로 이스티오의 데이터 플레인 확장하기
- Lua 소개 : 가벼운 명령형/절차적 언어로, 확장 언어로 쓰일 수 있는 스크립팅 언어를 주 목적으로 설계 - Wiki Extending Istio’s data plane with Lua
- 이미 존재하는 엔보이 필터를 설정해 Istio의 데이터 플레인을 확장하는 것은 편리하지만, 한계가 있음
- 추가하려는 기능이 기본 엔보이 필터에 없는 경우에는?
- 요청 경로에 어떤 커스텀 로직을 구현하고 싶다면?
- 자체 커스텀 로직으로 데이터 플레인 동작을 확장하는 방법을 살펴보기
- 엔보이에는 데이터 플레인 동작을 강화하기 위해 필터 체인에 추가할 수 있는 기본 필터가 다양하게 존재
- 루아 필터
- 기본 필터 중 하나
- 루아 스크립트를 작성해 프록시에 주입함으로써 요청/응답 경로의 동작을 커스텀 가능 (그림 14.9 참조)
- 요청이나 응답의 헤더를 조작하고 바디를 조사하는 데 사용할 수 있음
루아 스크립트 언어로 요청 경로 기능 확장
- 루아 필터
- 루아 스크립트를 주입해 요청 경로의 처리를 변경하도록 EnvoyFilter 리소스를 사용해 데이터 플레인을 설정할 것
- 루아 프로그래밍 언어 Lua programming language https://lua.org/ , https://luajit.org/
- 시스템 기능을 강화할 수 있는 강력하면서도 내장 가능한 스크립트 언어
- 동적 타입의 인터프리터 언어로, 루아 가상머신이 제공하는 메모리 자동 관리 기능을 갖추고 있음 (엔보이에서는 LuaJIT임).
- 요청 바디 검사 시 끼치는 영향
- 프록시에서 스트림을 처리하는 방식에 영향을 줄 수 있음
- ex) 바디 전체를 메모리에 적재하는 작업을 수행할 수도 있는데, 이는 성능에 영향을 줄 수 있음
- 루아 필터에 대한 엔보이 문서 참조: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter
- 이미 존재하는 엔보이 필터를 설정해 Istio의 데이터 플레인을 확장하는 것은 편리하지만, 한계가 있음
- 실습 : 요청 경로의 동작을 커스텀 ⇒ 일부 실습은 실패!
- 요청 경로의 동작을 커스텀하는 일반적인 예시로 실습해보기
- 들어오는 요청 모두를 A/B 테스트 그룹의 일부로 처리하고 싶다고 가정
- 어떤 그룹인지는 요청의 속성을 기반으로 런타임에서만 판단할 수 있음
- 특정 요청이 어느 그룹에 속했는지 판단하려면 A/B 테스트 엔진을 호출해야 함
- 이 호출의 응답은 요청의 헤더로 추가해야 함
- 업스트림 서비스는 이 헤더를 사용해 A/B 테스트 목적에 맞는 라우팅 결정을 내릴 수 있음
- 들어오는 요청 모두를 A/B 테스트 그룹의 일부로 처리하고 싶다고 가정
- 위 예제를 위해 몇 가지 보조 서비스를 배포해보기
- 샘플 httpbin 서비스를 배포
- 받은 요청의 헤더를 되돌려 보냄
- 샘플 A/B 테스트 버킷 서비스도 배포
- 서비스는 요청의 헤더를 평가하여,
요청이 속해야 하는 그룹을 나타내는 문자열을 반환# cat ch14/bucket-tester-service.yaml ... apiVersion: apps/v1 kind: Deployment metadata: labels: app: bucket-tester version: v1 name: bucket-tester spec: replicas: 1 selector: matchLabels: app: bucket-tester version: v1 template: metadata: labels: app: bucket-tester version: v1 spec: containers: - image: hashicorp/http-echo:1.0 # 수정 https://hub.docker.com/r/hashicorp/http-echo/tags imagePullPolicy: IfNotPresent name: bucket-tester args: - "-text=dark-launch-7" ports: - containerPort: 5678 name: http protocol: TCP securityContext: privileged: false # kubectl apply -f ch14/httpbin.yaml -n istioinaction kubectl apply -f ch14/bucket-tester-service.yaml -n istioinaction # 확인 kubectl get pod -n istioinaction NAME READY STATUS RESTARTS AGE bucket-tester-688c598b47-86fbr 2/2 Running 0 25s httpbin-85d76b4bb6-dz6b5 2/2 Running 0 2m56s ...
- 서비스는 요청의 헤더를 평가하여,
- 샘플 httpbin 서비스를 배포
- 루아 스크립트를 살펴보고, 이 사용 사례를 구현하는 방법도 살펴보기
- 요청이나 응답 헤더를 조작하는 용도로 작성할 수 있음
- 엔보이에서는 루아 함수 envoy_on_reqest() 혹은 envoy_on_response() 를 구현해 요청과 응답 각각을 확인하고 조작할 수 있음
- 루아 내에서 다른 서비스를 호출해야 한다면 엔보이가 제공하는 함수 사용 필요
- RPC 호출을 하는데 범용 루아 라이브러리를 사용하면 안 됨
- 실습에서 엔보이가 자체 논블로킹 non-blocking 스레딩 아키텍처로 호출을 올바르게 관리하길 원하기 때문
- RPC 호출을 하는데 범용 루아 라이브러리를 사용하면 안 됨
- httpCall() 함수를 사용하면 외부 서비스와 통신할 수 있음
- 다음 스크립트에서 조건에 충족하는 사용 사례 구현
# cat ch14/lua-filter.yaml ... function envoy_on_request(request_handle) local headers, test_bucket = request_handle:httpCall( "bucket_tester", { [":method"] = "GET", [":path"] = "/", [":scheme"] = "http", [":authority"] = "bucket-tester.istioinaction.svc.cluster.local", ["accept"] = "*/*" }, "", 5000) request_handle:headers():add("x-test-cohort", test_bucket) end function envoy_on_response(response_handle) response_handle:headers():add("istioinaction", "it works!") end ...
- 스크립트에서 구현하는 사항
- envoy_on_reqest() 함수를 구현
- httpCall() 내장 함수를 사용해 외부 서비스와 통신하고 있음
- 응답을 받아 x-test-cohort 라는 헤더를 추가하고 있음.
httpCall() 포함한 내장 함수는 문서 참조 - Docs
- 앞 절에서 했던 것처럼 이 스크립트를 EnvoyFilter 리소스에 추가 가능
# cat ch14/lua-filter.yaml apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: httpbin-lua-extension namespace: istioinaction spec: workloadSelector: labels: app: httpbin configPatches: - applyTo: HTTP_FILTER match: context: SIDECAR_INBOUND listener: portNumber: 80 filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE value: name: envoy.lua typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua" inlineCode: | function envoy_on_request(request_handle) # 아래 줄에 코드 입력 local headers, test_bucket = request_handle:httpCall( "bucket_tester", { [":method"] = "GET", [":path"] = "/", [":scheme"] = "http", [":authority"] = "bucket-tester.istioinaction.svc.cluster.local", ["accept"] = "*/*" }, "", 5000) request_handle:headers():add("x-test-cohort", test_bucket) end function envoy_on_response(response_handle) # 아래 줄에 코드 입력 response_handle:headers():add("istioinaction", "it works!") end - applyTo: CLUSTER match: context: SIDECAR_OUTBOUND patch: operation: ADD value: # cluster specification name: bucket_tester type: STRICT_DNS connect_timeout: 0.5s lb_policy: ROUND_ROBIN load_assignment: cluster_name: bucket_tester endpoints: - lb_endpoints: - endpoint: address: socket_address: protocol: TCP address: bucket-tester.istioinaction.svc.cluster.local port_value: 80
- workloadSelector 정의한 대로 이 필터를 httpbin 워크로드에 적용 후 httpbin 서비스 호출 확인! ⇒ 실습은 실패!
# kubectl apply -f ch14/lua-filter.yaml kubectl get envoyfilter -n istioinaction # istio-proxy config 확인 내용 추가해두자 # httpbin 서비스 호출 확인! kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/ -v ... < HTTP/1.1 503 Service Unavailable < content-length: 39 < content-type: text/plain < istioinaction: it works! < date: Sun, 18 May 2025 07:59:34 GMT < server: envoy < x-envoy-upstream-service-time: 51 ... invalid header value for: x-test-cohort kubectl exec -it deploy/sleep -n istioinaction -c sleep -- curl http://httpbin.istioinaction:8000/headers ... invalid header value for: x-test-cohort # 정상 실습 시.. { "headers": { "Accept": "*/*", "Content-Length": "0", "Host": "httpbin.istioinaction:8000", "User-Agent": "curl/7.69.1", "X-B3-Sampled": "1", "X-B3-Spanid": "1d066f4b17ee147b", "X-B3-Traceid": "1ec27110e4141e131d066f4b17ee147b", "X-Test-Cohort": "dark-launch-7" # A/B 테스트 서비스를 호출할 때 덧붙이는 새 헤더 x-test-cohort 가 보임 } }
- 상세 내용은 ch14/lua-filter.yaml 파일에서 제공됨
- 이 예제에서는 데이터 플레인의 기능을 확장하기 위해 의도적으로 구축된 필터를 사용하는 방법 확인
- 루아 스크립트 언어를 사용해 이 기능을 구현했으며, 내장 함수를 사용해 다른 함수를 호출함
- 다음 실습을 위해 리소스 삭제
# kubectl delete envoyfilter -n istioinaction --all kubectl get envoyfilter -A # kubectl delete -f ch14/httpbin.yaml -n istioinaction kubectl delete -f ch14/bucket-tester-service.yaml -n istioinaction
- 요청 경로의 동작을 커스텀하는 일반적인 예시로 실습해보기
14.5 웹어셈블리로 이스티오의 데이터 플레인 확장하기
- 들어가며 : 웹어셈블리로 새 엔보이 필터를 작성 - Docs Extending Istio’s data plane with WebAssembly
- 요청 경로상에서 Istio를 확장하는 마지막 방법
- 웹어셈블리로 새 엔보이 필터를 작성하는 것
- 자체 엔보이 필터를 구축하는 방법 확인
- Istio 데이터 플레인에 배포하는 방법 확인
- 웹어셈블리로 새 엔보이 필터를 작성하는 것
- 앞 절에서 진행했던 확장 기능
- 기존 엔보이 필터를 재사용하고 설정해 기본 Istio 기능을 확장
- 요청 경로를 조작하는 자체 커스텀 스크립트를 주입
- 참고 문서
- https://www.notion.so/gasidaseo/7-12-14-1f750aec5edf803cb69adfc9c0862d78?pvs=4#1f850aec5edf808d890dfdf49302353c
- https://istio.io/latest/blog/2021/wasm-api-alpha/
- https://istio.io/latest/blog/2021/wasm-progress/
- https://istio.io/v1.17/docs/reference/config/proxy_extensions/wasm-plugin/
- https://istio.io/v1.17/docs/ops/configuration/extensibility/wasm-pull-policy/
- 웹어셈블리 소개 Introducing WebAssembly
- 웹어셈블리 Wasm, WebAssembly 란?
- 바이너리 명령 형식으
- 여러 가지 환경 간에 이식할 수 있는 것을 목표로 하였음
- 여러 프로그래밍 언어로 컴파일해 가상머신에서 실행 가능
- 본래 웹어셈블리의 개발 목적
- 브라우저의 웹 애플리케이션에서 CPU 집약적인 작업의 실행 속도를 높이기 위함
- 브라우저 기반 애플리케이션 지원을 자바스크립트 외의 언어로도 확장하기 위함
- 웹어셈블리는 2019년에 W3C 권고 사항이 됨
- 모든 주요 브라우저에서 지원
- 모듈로 패키징된 커스텀 코드로, 웹 브라우저 같은 대상 호스트 안의 격리된 가상머신에서 안전하게 실행 가능
- 웹어셈블리의 특징
- 저장 공간과 메모리를 적게 차지하고 거의 네이티브에 가까운 속도로 실행되도록 설계됨
- 호스트 애플리케이션(즉, 브라우저)에 내장돼도 안전
- 메모리 안전 memory safe 하고 격리된 sandboxed 실행 환경(가상머신)에서 실행되기 때문
- 웹어셈블리 모듈은 호스트 시스템이 허용하는 메모리와 기능에만 접근할 수 있음
- 모든 주요 브라우저에서 지원
- 웹어셈블리 Wasm, WebAssembly 란?
- 왜 엔보이에 웹어셈블리를 사용하는가? Why WebAssembly for Envoy?
- 네이티브 엔보이 필터를 직접 작성 시 단점 (크게 두 가지)
- C++ 여야 함
- 변경 사항을 엔보이의 새 바이너리로 정적으로 빌드해야 함
- 이는 사실상 엔보이 ‘커스텀’ 빌드임
- 이는 사실상 엔보이 ‘커스텀’ 빌드임
- 엔보이에서 웹어셈블리 실행 엔진을 내장함으로서 가지는 이점
- HTTP 필터를 포함해 엔보이의 다양한 영역을 커스터마이징하고 확장하는 데 사용할 수 있음
- 웹어셈블리가 지원하는 언어라면 어느 것이든 엔보이 필터로 작성해 런타임에 프록시로 동적으로 불러올 수 있음 (그림 14.11)
- Istio에서 기본 엔보이 프록시를 계속 사용하면서 커스텀 필터를 런타임에 동적으로 불러올 수 있다는 뜻
웹어셈블리 (Wasm) 모듈을 패키징해 웹어셈블리 HTTP 필터 내에서 실행할 수 있다.
- 어떻게 동작하는지 - Blog
- Wasm 확장의 로컬 캐시 설정 Set up local cache of Wasm extensions
- 원하는 Wasm 확장을 로컬 캐시로 가져오기 Pull desired Wasm extension into the local cache
- 적절한 워크로드에 wasm-cache를 마운트 Mount the wasm-cache into appropriate workloads
- Wasm 필터를 사용하려면 EnvoyFilter CRD로 Envoy를 구성하기 Configure Envoy with EnvoyFilter CRD to use the Wasm filter
- Image fetcher in Istio agent - Blog
원격 Wasm 모듈 fetch 흐름
- Istio 1.9부터 Istio-agent는 EnvoyFiltersistio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스(ECDS)를 활용하여 원격 HTTP 소스에서 가져온 Wasm 바이너리를 로드
- 원격 가져오기가 실패할 경우에도, Envoy가 잘못된 구성으로 인해 중단될 염려 없이 HTTP 원격 리소스를 안정적으로 사용 가능
- Istio 1.12의 새로운 Wasm API 구현에도 동일한 메커니즘 적용
- Istio 1.12는 이 기능을 Wasm OCI 이미지로 확장
- Istio 에이전트는 이제 모든 OCI 레지스트리에서 Wasm 이미지를 가져올 수 있음
- Docker Hub, Google Container Registry(GCR), Amazon Elastic Container Registry(ECR) 등
- 이미지를 가져온 후, Istio 에이전트는 Wasm 바이너리를 추출하여 캐시한 다음 Envoy 필터 체인에 삽입
- 원격 가져오기가 실패할 경우에도, Envoy가 잘못된 구성으로 인해 중단될 염려 없이 HTTP 원격 리소스를 안정적으로 사용 가능
- Istio 1.9부터 Istio-agent는 EnvoyFiltersistio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스(ECDS)를 활용하여 원격 HTTP 소스에서 가져온 Wasm 바이너리를 로드
- Wasm module distribution via the Istio Agent - Blog
- Istio 1.9 이전에는 원격 Wasm 모듈을 프록시에 배포하기 위해 Envoy 원격 데이터 소스가 필요
- 이 예시에서는 두 가지 EnvoyFilter리소스가 정의됨
- 원격 가져오기 Envoy 클러스터를 추가하는 리소스
- HTTP 필터 체인에 Wasm 필터를 삽입하는 리소스
- 위 방법에는 단점 존재
- 잘못된 구성이나 일시적인 오류로 인해 원격 가져오기가 실패하면 Envoy가 잘못된 구성에 고정됨
- Wasm 확장 프로그램이 fail closed 로 구성된 경우 , 잘못된 원격 가져오기로 인해 Envoy 서비스가 중단
- 이 문제를 해결하려면?
- Envoy xDS 프로토콜을 근본적으로 변경 하여 비동기 xDS 응답을 허용해야 함
- 이 예시에서는 두 가지 EnvoyFilter리소스가 정의됨
- Istio 1.9는 istio-agent 내부의 xDS 프록시와 Envoy의 확장 구성 검색 서비스 (ECDS) 를 활용하여 기본적으로 안정적인 배포 메커니즘을 제공
- 배포 흐름
- istio-agent는 istiod에서 확장 구성 리소스 업데이트를 가로챔
- 원격 페치 힌트를 읽어와 Wasm 모듈을 다운로드
- 다운로드된 Wasm 모듈의 경로로 ECDS 구성을 다시 작성
- 배포 흐름에서 다운로드 실패 시
- istio-agent는 ECDS 업데이트를 거부하여, 잘못된 구성이 Envoy에 도달하는 것을 방지
- 자세한 내용은 Wasm 모듈 배포 관련 문서를 참조
- 배포 흐름
- Istio 1.9 이전에는 원격 Wasm 모듈을 프록시에 배포하기 위해 Envoy 원격 데이터 소스가 필요
- 네이티브 엔보이 필터를 직접 작성 시 단점 (크게 두 가지)
- 웹어셈블리로 새로운 엔보이 필터 만들기 Building a new Envoy filter with WebAssembly
- 웹어셈블리로 엔보이 필터를 빌드하려면 알아야 할 것
- 어떤 언어를 사용하고 싶은지
- 어떤 엔보이 버전을 사용 중인지
- 엔보이 버전에서 어떤 엔보이 ABI Abstract Binary Interace 를 지원하는지
- 웹어셈블리로 엔보이 필터를 빌드하려면 준비해야 할 것
- 적절한 언어 SDK를 고르기
- 빌드 및 종속성 도구를 제대로 준비하기
- Sola.io의 wasme 라는 오픈소스 개발자 도구를 사용해 엔보이용 웹어셈블리 필터를 만들고 빌드해보기
- wasme 를 사용 시 이점
- 엔보이 프로젝트용 웹어셈블리를 빠르게 부트스트랩하고 보일러플레이트 생성 boilerplate scaffolding 을 모두 자동화해 줌
- 엔보이 프로젝트용 웹어셈블리를 빠르게 부트스트랩하고 보일러플레이트 생성 boilerplate scaffolding 을 모두 자동화해 줌
- wasme 를 사용 시 이점
- 웹어셈블리로 엔보이 필터 빌드를 시작하기
- 엔보이 웹어셈블리 SDK가 있는 프래그래밍 언어 (네 가지)
- C++
- Rust
- AssemblyScript 어셈블리스크립트 (TypeScript 타입스크립트)
- TinyGo
- 어셈블리스크립트 https://www.assemblyscript.org/ 를 사용하여 웹어셈블리로 새 엔보이 필터를 빌드해볼 것
- 어셈블리스크립트는 타입스크립트의 변형이므로, 자바스크립트 개발자라면 익숙함
- 엔보이 필터를 구축하는 데 있어 C++의 훌륭한 대체재
- 엔보이의 웹어셈블리의 지원은 실험적인 것으로 간주되므로 바뀔 수 있음
- 따라서 직접 만들어 엔보이에 배포하는 웹어셈블리 모듈의 경우, 운영 환경에 들어가기 전에 모두 철저히 테스트해볼 것을 권장
- 따라서 직접 만들어 엔보이에 배포하는 웹어셈블리 모듈의 경우, 운영 환경에 들어가기 전에 모두 철저히 테스트해볼 것을 권장
- 어셈블리스크립트는 타입스크립트의 변형이므로, 자바스크립트 개발자라면 익숙함
- 웹어셈블리로 엔보이 필터를 빌드하려면 알아야 할 것
meshctl 로 새 엔보이 필터 빌드하기 Building a new Envoy filter with the meshctl tool버전 차이로 실습 불가능!- meshctl 란
- 웹어셈블리 모듈을 생성, 빌드, 게시, 배포하기 위한 도커 docker 류 도구
- 엔보이용 웹어셈블리 필터 빌드를 용이하게 함
- meshctl 다운로드하고 시스템 경로에 넣기 - Docs
# docker exec -it myk8s-control-plane bash ---------------------------------------- cd && pwd curl -sL https://run.solo.io/meshctl/install | GLOO_MESH_VERSION=v2.5.0 sh export PATH=$HOME/.gloo-mesh/bin:$PATH tree .gloo-mesh/ # 새 wasm 프로젝트 부트스트랩할 폴더를 고르고 다음 명령을 실행 : 버전 차이로 실습 불가능! meshctl wasm init ./hello-wasm --language=assemblyscript ERROR unknown command "wasm" for "meshctl" meshctl -h exit ----------------------------------------
- meshctl 란
- [공식 문서] Distributing WebAssembly Modules 실습 - Docs
- 소개
- Istio는 WebAssembly(Wasm)를 사용하여 프록시 기능을 확장할 수 있는 기능을 제공
- Wasm 확장성의 주요 장점 중 하나는 런타임에 확장 기능을 동적으로 로드할 수 있다는 것
- 이러한 확장 기능은 먼저 Envoy 프록시에 배포 필요
- Istio는 프록시 에이전트가 Wasm 모듈을 동적으로 다운로드할 수 있도록 허용하여 이를 가능하게 함
- Wasm 모듈 구성
- 이 예시에서는 메시에 HTTP 기본 인증 확장을 추가
- 원격 이미지 레지스트리에서 기본 인증 모듈을 가져와 로드하도록 Istio를 구성
- 이 모듈은 /api API 호출 시 실행되도록 구성
- 원격 Wasm 모듈로 WebAssembly 필터를 구성하려면 WasmPlugin리소스를 생성
# 설정 전 호출 확인 EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v # kubectl explain wasmplugins.extensions.istio.io ... # kubectl apply -f - <<EOF apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: basic-auth namespace: istio-system # 모든 네임스페이스에 영향 spec: selector: matchLabels: istio: ingressgateway url: oci://ghcr.io/istio-ecosystem/wasm-extensions/basic_auth:1.12.0 phase: AUTHN pluginConfig: basic_auth_rules: - prefix: "/api" request_methods: - "GET" - "POST" credentials: - "ok:test" - "YWRtaW4zOmFkbWluMw==" EOF # kubectl get WasmPlugin -A NAMESPACE NAME AGE istio-system basic-auth 21s
- 구성된 Wasm 모듈을 확인
# 자격 증명 없이 테스트 EXT_IP=$(kubectl -n istio-system get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') docker exec -it mypc curl -s -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v # 자격 증명으로 테스트 docker exec -it mypc curl -s -H "Authorization: Basic YWRtaW4zOmFkbWluMw==" -H "Host: webapp.istioinaction.io" http://$EXT_IP/api/catalog -v
- 실습 완료 후 리소스 삭제
# kubectl delete WasmPlugin -n istio-system basic-auth
- 소개
- Istio 에 Coraza (코라자) WAF 적용 - KrBlog
- Coraza는 애플리케이션을 보호할 수 있는 오픈 소스, 고성능 웹 애플리케이션 방화벽 - Home , Link
Coraza is an open source, high performance, Web Application Firewall ready to protect your beloved applications
- Go 언어로 작성되었으며, ModSecurity SecLang 규칙 세트를 지원하고 OWASP 핵심 규칙 세트와 100% 호환
- ⇲ Drop-in - Coraza는 부분 호환성이 있는 대체 엔진.
트러스트웨이브OWASP ModSecurity 엔진을 지원하며 업계 표준 SecLang 규칙 세트를 지원 - 🔥 보안 - Coraza는 OWASP CRS를 실행하여 OWASP Top Ten을 포함한 다양한 공격으로부터 웹 애플리케이션을 최소한의 오탐으로 보호. CRS는 SQL 인젝션(SQLi), 크로스 사이트 스크립팅(XSS), PHP 및 Java 코드 인젝션, HTTPoxy, Shellshock, 스크립팅/스캐너/봇 탐지, 메타데이터 및 오류 유출 등 다양한 일반적인 공격 유형을 차단
- 🔌 확장성 - Coraza는 핵심 라이브러리로, 온프레미스 웹 애플리케이션 방화벽 인스턴스를 구축하기 위한 다양한 통합 기능을 제공. 감사 로거, 지속성 엔진, 연산자, 액션 등 Coraza를 원하는 대로 확장할 수 있는 자체 기능을 생성할 수 있음
- 🚀 성능 - 대규모 웹사이트부터 소규모 블로그까지, Coraza는 성능 저하를 최소화하면서 부하를 처리할 수 있음. 벤치마크 확인
- ﹡ 단순성 - 누구나 Coraza 소스 코드를 이해하고 수정할 수 있음. 새로운 기능으로 Coraza를 쉽게 확장할 수 있음
- 플러그인 지원
- Proxy WASM extension for proxies with proxy-wasm support (e.g. Envoy) - stable, still under development
- Caddy Reverse Proxy and Webserver Plugin - stable, needs a maintainer
- HAProxy SPOE Plugin - preview
- Traefik Proxy Plugin - preview, needs maintainer
- Gin Web Framework Middleware - preview, needs maintainer
- Apache HTTP Server - experimental
- Nginx - experimental
- Coraza C Library - experimental
- Quick Start - Docs
- OWASP Core Ruleset - Docs
- Playground - Link
- 추천 영상 - Link
- proxy-wasm filter based on Coraza WAF - Github
- Coraza 기반으로 구축되고 proxy-wasm ABI를 구현하는 웹 애플리케이션 방화벽 WASM 필터
- Envoy에서 직접 로드하거나 Istio 플러그인으로 사용 가능
- WAF는 플러그 앤 플레이 방식의 보안 솔루션이 아님
- WAF가 효과적으로 작동하려면 보호하려는 환경과 트래픽에 맞춰 구성 및 튜닝이 필요
- 실제 운영 환경에서 사용할 때
- 배포된 구성( @recommended-conf 및 @crs-setup-conf 참조 )을 완전히 숙지
- 사용된 규칙 세트에 대한 튜닝 단계를 수행하는 것이 좋음
- OWASP 핵심 규칙 세트(CRS) 튜닝에 대한 상세 가이드: "False Positives and Tuning"
- Istio WasmPlugin 설정
# kubectl apply -f - <<EOF apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: coraza-ingressgateway namespace: istio-system spec: selector: matchLabels: istio: ingressgateway url: oci://ghcr.io/corazawaf/coraza-proxy-wasm phase: AUTHN pluginConfig: default_directives: default directives_map: default: - Include @demo-conf - SecDebugLogLevel 9 - SecRuleEngine On - Include @crs-setup-conf - Include @owasp_crs/*.conf EOF # kubectl logs -n istio-system -l app=istio-ingressgateway | grep -i wasm 2025-05-18T09:22:39.344842Z info wasm fetching image corazawaf/coraza-proxy-wasm from registry ghcr.io with tag latest # kubectl get WasmPlugin -A NAMESPACE NAME AGE istio-system coraza-ingressgateway 9s
- 동작 확인
# curl -s http://webapp.istioinaction.io:30000/api/catalog # XSS (Cross-Site Scripting) 공격 시도 : REQUEST-941-APPLICATION-ATTACK-XSS curl -s 'http://webapp.istioinaction.io:30000/api/catalog?arg=<script>alert(0)</script>' -IL HTTP/1.1 403 Forbidden # 로그 모니터링 kubectl logs -n istio-system -l app=istio-ingressgateway -f 2025-05-18T09:34:21.049522Z critical envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 1). Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11347"] [id "949111"] [rev ""] [msg "Inbound Anomaly Score Exceeded in phase 1 (Total Score: 20)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog?arg=<script>alert(0)</script>"] [unique_id "ztBHScSiiDnOiwJfmOy"] thread=30 [2025-05-18T09:34:21.014Z] "HEAD /api/catalog?arg=<script>alert(0)</script> HTTP/1.1" 403 - - "-" 0 0 35 - "172.18.0.1" "curl/8.7.1" "27f43c69-359d-9d95-99cd-553081bb9346" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.10:8080 172.18.0.1:65166 - - # SQLI phase 2 (reading the body request) curl -i -X POST 'http://webapp.istioinaction.io:30000/api/catalog' --data "1%27%20ORDER%20BY%203--%2B" HTTP/1.1 403 Forbidden # 로그 모니터링 kubectl logs -n istio-system -l app=istio-ingressgateway -f 2025-05-18T09:42:40.613124Z critical envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1157 wasm log istio-system.coraza-ingressgateway: [client "172.18.0.1"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 10) [file "@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11358"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 10)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.0.0-rc2"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [hostname "10.10.0.10"] [uri "/api/catalog"] [unique_id "GYSnZlurGTXFtujeNgl"] thread=34 [2025-05-18T09:42:40.575Z] "POST /api/catalog HTTP/1.1" 403 - - "-" 26 0 39 - "172.18.0.1" "curl/8.7.1" "c30d47e1-5631-99ae-91f2-5dfd8174b421" "webapp.istioinaction.io:30000" "10.10.0.16:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.10:44518 10.10.0.10:8080 172.18.0.1:63266 - - ...
- 프로메테우스 메트릭 확인 : curl -s localhost:8082/stats/prometheus | grep waf_filter
- Coraza는 애플리케이션을 보호할 수 있는 오픈 소스, 고성능 웹 애플리케이션 방화벽 - Home , Link
- 이번 장 요약
- 엔보이의 내부 아키텍처는 리스너와 필터를 중심으로 구축됨
- 즉시 사용할 수 있는 엔보이 필터가 많음
- Istio의 데이터 플레인(엔보이 프록시)을 확장할 수 있음
- Istio의 EnvoyFilter 리소스를 사용해 엔보이의 HTTP 필터 구조를 직접 구성하면 더 세밀한 설정이 가능하며,
Istio의 API로는 노출되지 않는 엔보이의 부분들도 설정할 수 있음 - 속도 제한이나 tap 필터 같은 기능으로 서비스 간 통신에서 엔보이의 요청 경로를 확장할 수 있음
- 루아와 웹어셈블리를 사용하면 엔보이를 다시 빌드하지 않고도 데이터 플레인을 고급 수준에서 커스터마이징할 수 있음
- 엔보이의 내부 아키텍처는 리스너와 필터를 중심으로 구축됨
- 실습 후 kind 삭제 : kind delete cluster --name myk8s && docker rm -f mypc
Contents
소중한 공감 감사합니다.