"3128용 subset·443용 subset"으로 나눠 등록했는데 istioctl pc cluster에 모든 포트 × 모든 subset 조합이 보이는 이유, EDS·HEALTHY의 실제 의미, 그리고 의도한 포트만 남기는 세 가지 전략과 선택 기준을 정리함.
DR subset의 스키마는 name · labels · trafficPolicy 세 필드가 전부임. "이 subset은 3128 전용"이라고 선언할 자리가 없음. 포트별 subset이라는 의도는 리소스에 표현되지 않고, istiod는 다음 규칙으로 CDS를 생성함:
outbound|<port>|<subset>|<FQDN>따라서 pc cluster에 443용으로 만든 subset이 3128 포트 행으로도 보이는 것은 정확히 이 규칙의 결과임. 문제는 존재 자체가 아니라, 이 중 일부가 "설정상 존재하지만 실제로는 동작 불가능한 조합"이라는 점임 (→ 06).
pc endpoint에서 의도 밖 조합의 endpoint까지 HEALTHY로 보이는 이유도 같은 축 분리에서 나옴.
| 단계 | 동작 | 검사하는 것 | 검사하지 않는 것 |
|---|---|---|---|
| EDS endpoint 선정 | Service endpoint 중 subset labels 일치 Pod 선택 | label 일치 여부 | 해당 포트 listen 여부 |
| endpoint port 결정 | 그 Service port의 targetPort를 그대로 사용 | Service 선언 | Pod의 실제 open port |
| HEALTHY 판정 | readiness probe 통과 + outlier ejection 아님 | probe 결과 | 이 cluster의 포트로 통신 가능한지 |
outbound|3128|tls-gw|...의 endpoint = "tls-gw label Pod들 + 3128의 targetPort". 그 Pod가 443만 listen해도 목록에 들어감.둘 다 "Pod를 고르는 조건"일 뿐 Pod 배치를 강제하지 않음. 한 Pod가 여러 Service에 잡히는 것도, 여러 subset에 잡히는 것도 정상임. 따라서 "label이 다르니 Pod를 나눠야 한다"는 순서가 반대임 — Pod가 실제로 다른 워크로드인지가 먼저이고, label은 그 사실의 표현 수단임.
kubectl get pods -n egress -l app=squid --show-labels
kubectl get pods -n egress -l app=tls-gw --show-labels
# 같은 Pod가 나옴 → 상황 2 (단일 워크로드, 포트 구분 목적의 label이었음)
# 다른 Pod가 나옴 → 상황 1 (이미 분리된 워크로드)
| 상황 | 실체 | 조치 | Pod 변경 |
|---|---|---|---|
| 상황 1 | labels가 서로 다른 Pod 그룹을 가리킴 | Service 2개, selector 각각 (그룹별) | 없음 — 이미 나뉘어 있음 |
| 상황 2 | labels가 결국 같은 Pod들에 매칭됨 | Service 2개, selector 동일, 포트만 다름 | 없음 — subset이 잘못된 도구였을 뿐 |
표는 색인이며, 각 안의 메커니즘과 트레이드오프는 아래 해설 참조.
| 안 | 방법 | cross product | Pod 변경 | 적합 조건 |
|---|---|---|---|---|
| 1안 | Service만 분리 (Pod 공유) | 제거됨 | 없음 | 대부분의 경우 — 정석 |
| 2안 | Pod까지 분리 (워크로드 격리) | 제거됨 | Deployment 분리 | 격리가 별도로 필요할 때만 |
| 3안 | 수용 + VS destination.port로 정밀 라우팅 | 존재 (무해화) | 없음 | 규모 작음 · 무장애 우선 초기 단계 |
포트 스코핑은 Service의 책임이므로 "포트당 하나의 host"로 모델링함. cluster는 host 단위로 생성되므로 host가 갈라지는 순간 각 host엔 자기 포트만 존재하고, 그룹 구분이 host로 표현되므로 subset 자체가 불필요해짐. 상황 1이면 selector를 그룹별로, 상황 2면 동일 selector로 작성함 (→ 03). 리소스 추가는 Service 오브젝트뿐이라 무장애 전환과도 충돌 없음.
cross product 해소만이 목적이면 과잉임. 선택 기준은 별개의 운영 요구 — 리소스 경합 분리, 장애 반경 축소, HPA 개별 스케일링, 노드 배치(egress 노드 집약) 차이 — 가 있는지임. Squid 경유 TCP 터널 트래픽과 TLS 트래픽의 부하 특성이 크게 다르면 고려함.
조합 cluster는 트래픽에 무해함 — 라우팅되지 않는 cluster는 config로만 존재함. VS에서 destination.port.number를 항상 명시해 유효한 (port, subset) 조합만 참조하면 됨. 비용은 두 가지: ① proxy 메모리·xDS push 크기 증가 (Service·subset 수에 곱으로 비례), ② 사람 실수로 잘못된 조합을 라우팅할 위험 — 이 위험을 설정 리뷰(GitOps PR)로 막을 수 있는지가 채택 기준임. 포트별 정책 차이는 subset이 아니라 trafficPolicy.portLevelSettings로 표현함:
trafficPolicy:
portLevelSettings:
- port: { number: 3128 }
tls: { mode: DISABLE } # CONNECT 평문 터널
- port: { number: 443 }
tls: { mode: ISTIO_MUTUAL }
보조 수단 — Sidecar 리소스의 egress[](host+port 단위 import 제한)로 소비자 proxy에 밀리는 cluster 수를 줄일 수 있음. 단 이는 config push 절감 수단이지 subset-포트 매핑 수단이 아니며 관리 복잡도가 오르므로 1·3안으로 해결 안 될 때만 검토함.
# 같은 Deployment(egress-gw, 3128·443 모두 listen)를 두 Service가 공유
apiVersion: v1
kind: Service
metadata:
name: squid-path
namespace: egress
spec:
selector: { app: egress-gw } # 동일 selector
ports:
- name: tcp-proxy
port: 3128
targetPort: 3128
---
apiVersion: v1
kind: Service
metadata:
name: tls-path
namespace: egress
spec:
selector: { app: egress-gw } # 동일 selector
ports:
- name: tls
port: 443
targetPort: 8443
---
# DR은 host별로 — subset 없이 정책만
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: squid-path
namespace: egress
spec:
host: squid-path.egress.svc.cluster.local
trafficPolicy:
tls: { mode: DISABLE }
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: tls-path
namespace: egress
spec:
host: tls-path.egress.svc.cluster.local
trafficPolicy:
tls: { mode: ISTIO_MUTUAL }
# 1. host별로 자기 포트 cluster만 존재하는지
istioctl proxy-config cluster deploy/<caller> -n <ns> --fqdn squid-path.egress.svc.cluster.local
# 예상: PORT=3128 행만 존재. 443 행 없음
istioctl proxy-config cluster deploy/<caller> -n <ns> --port 443
# 예상: tls-path만 표시, squid-path 미표시
# 2. 두 host의 endpoint가 같은 Pod IP를 가리키는지 (Pod 공유 확인)
istioctl proxy-config endpoint deploy/<caller> -n <ns> \
--cluster "outbound|3128||squid-path.egress.svc.cluster.local"
istioctl proxy-config endpoint deploy/<caller> -n <ns> \
--cluster "outbound|443||tls-path.egress.svc.cluster.local"
# 예상: 동일한 Pod IP 집합, 포트만 3128 / 8443으로 다름
# 3. 정적 검증
istioctl analyze -n egress
# 예상: No validation issues found
VS에서 실수로 port: 3128 + 443만 listen하는 Pod의 subset으로 라우팅한 경우의 관측 시그니처:
| 진단 단계 | 결과 | 함정 |
|---|---|---|
pc cluster | cluster 존재 정상으로 보임 | NC 아님 — DR 계층 무혐의로 판단하기 쉬움 |
pc endpoint | endpoint HEALTHY 정상으로 보임 | UH 아님 — label 필터는 통과했기 때문 |
| 실제 요청 | connection refused → 503 UF | Pod가 그 포트를 listen하지 않음 |
kubectl logs deploy/<caller> -c istio-proxy | grep ' 503 '
# "... 503 UF ..." — UpstreamConnectionFailure
# cluster·endpoint가 모두 정상으로 보이면 listen 포트를 실측함
kubectl exec <pod> -n egress -c <app> -- ss -tlnp
# 예상: 443(8443)만 LISTEN, 3128 없음 → 조합 오류 확정