Istio 1.30.0 — 기존 설치 teardown + Helm 클린 재설치
홈랩 클러스터의 깨진 Istio 1.30.0 설치를 전부 제거하고 순수 Helm chart(base → istiod → ingress/egress)로 클린 재설치한 런북. 흐름 0단계(설치·구조)의 실제 실행 기록 — 본 아카이브 전체가 이 설치 위에서 동작한다.
하나의 그림: Istio Helm 설치는 의존 스택이다 — base(CRD) → istiod(control plane) → gateways(data plane), 각 release가 앞 layer를 전제로 한다. 그리고 Helm은 자기가 만든 선언적 리소스만 소유하고, istiod가 런타임에 동적으로 만드는 리소스(CA cert, leader-election cm)는 소유 밖이다. 이 의존 방향 + ownership 경계 두 축이 install 순서·teardown 역순·CRD 잔여물·configmap 수동삭제를 전부 설명한다.
Date: 2026-06-01 호스트: homelab (kubespray bare-metal, k8s v1.30.6, 3노드) 도메인: Kubernetes / Istio 상태: ✅ 완료 — base/istiod/ingress/egress 4개 release 1.30.0 deployed, 전 pod 1/1 Running, analyze 경고 0 대상독자: Istio를 IaC로 굴리려는 DevOps/SRE. 선행개념: Helm release/values, CRD, control plane vs data plane.
⚠️ 선행: 이 작업 전 클러스터 control-plane 장애를 먼저 수리해야 했음. 상세 → control-plane 장애 수리 런북 (그 수리 없이는 istiod가
0/1로 안 뜸 — Deployment가 Pod를 못 만드는 상태였음)
1. 배경 지식 — 왜 "순수 Helm 스택"으로 까는가
Istio를 설치하는 길은 둘이다. istioctl install은 하나의 IstioOperator CR을 받아 control plane과 gateway를 한 번에 깔아주는 편의 경로다. 반면 순수 Helm chart는 base/istiod/gateway를 각각 별도 release로 깐다. 편해 보이는 전자를 두고 굳이 후자를 택하는 이유는 소유권(ownership) 에 있다.
쿠버네티스에서 어떤 도구가 리소스를 "관리한다"는 건 결국 그 리소스에 소유 라벨/애너테이션을 박는 것이다. istioctl은 operator 라벨로, Helm은 release 라벨(app.kubernetes.io/managed-by: Helm + release name)로 소유를 표시한다. 두 도구는 서로의 라벨을 모른다. 그래서 operator로 깐 위에 Helm으로 덮거나 그 반대를 하면, 같은 Deployment를 두 도구가 서로 자기 것이라 주장하며 충돌한다. 프로덕션처럼 GitOps로 굴릴 환경에선 "release/values 파일 = 형상의 단일 소스, 버전은 --version 1.30.0으로 핀"이라는 재현성이 핵심이라, ownership이 명확한 순수 Helm을 택한다.
이 문서가 푸는 문제는 두 가지다. (1) 깨진 기존 설치를 어떻게 깨끗이 들어내는가 — Helm uninstall 한 방으로 안 되는 이유가 있다. (2) 어떤 순서로 다시 까는가 — base→istiod→gw 순서가 단순 관례가 아니라 의존성에서 강제된다는 것. 이 둘을 이해하려면 먼저 Istio 설치가 3개 layer의 의존 스택이라는 그림을 세워야 한다.
| layer (release) | 무엇 | 의존 |
|---|---|---|
base |
CRD 15개 + cluster-scope 리소스(clusterrole 등) | 없음 (맨 아래) |
istiod |
control plane: deploy/svc/sa + webhook(inject/validate) | CRD가 있어야 자기 webhook·config를 등록 |
gateway |
data plane: ingress(NodePort)/egress(ClusterIP) Pod | istiod가 떠야 xDS로 설정받음 |
base가 CRD(=Gateway/VirtualService/DestinationRule 등의 타입 정의)를 먼저 깔지 않으면 istiod는 자기가 watch할 타입조차 모른다. gateway Pod는 빈 Envoy로 떠봤자 istiod가 없으면 라우팅 설정을 못 받아 무의미하다. 아래 layer가 위 layer의 전제 — 이게 install이 아래→위로 가는 이유 전부다.
2. 핵심 — 의존 방향 + ownership 경계 (한 장의 그림)
앵커 한 문장: install은 의존 스택을 아래→위로 쌓고, teardown은 정확히 역순으로 허문다 — 단 Helm이 소유한 박스만. 박스 밖(CRD keep + runtime cm)은 손이 안 닿아 수동 삭제가 필요하다.
이 한 문장에서 이후 모든 절차가 따라 나온다. 그림으로 고정하자.
왜 teardown은 역순인가 (참조 순서). base를 먼저 지우면 istiod가 참조하던 CRD가 사라진다. 그러면 istiod release를 정리할 때 finalizer/cleanup 로직이 참조 대상을 못 찾아 순서가 꼬일 수 있다. DB에서 외래키 걸린 행을 부모부터 지우면 깨지는 것과 같은 원리 — 그래서 자식(istiod)부터, 부모(base)는 나중에. (비유의 한계: Istio엔 진짜 FK 제약이 강제돼 있진 않다. "참조가 끊겨도 대개 견디지만 cleanup 경로에서 비결정적으로 꼬일 수 있다"가 정확한 표현.)
왜 helm uninstall 한 방으로 안 끝나는가 (ownership 경계). 위 그림의 두 박스가 답이다. Helm은 자기가 chart로 렌더해 apply한 선언적 리소스만 소유한다. 그런데 teardown을 막는 두 부류는 그 소유 밖에 있다.
- CRD — keep-policy로 일부러 남긴다. base chart는 CRD에
helm.sh/resource-policy: keep애너테이션을 박는다. 그래서helm uninstall istio-base를 해도 CRD 15개는 살아남는다. 이건 버그가 아니라 데이터 보호 장치다: CRD가 지워지면 그 타입의 모든 CR 인스턴스가 함께 cascade 삭제된다(Gateway/VirtualService 등 사용자 설정 전멸). 업그레이드 때 차트만 갈아끼우면서 사용자 리소스를 지키려는 의도. 완전 초기화를 원하면 명시적으로kubectl delete crd해야 한다. - runtime configmap — istiod가 런타임에 만든다.
istio-ca-root-cert(메시 CA 신뢰 앵커, 각 ns에 배포),istio-leader(leader-election),istio-ip-autoallocate,istio-namespace-controller-election등은 chart에 없다. istiod 프로세스가 살아 돌면서 동적으로 생성한 것이라 Helm 소유 라벨이 없다 →uninstall이 못 건드린다 → 수동 삭제.
이 경계가 곧 teardown이 2단계로 쪼개지는 이유다: ① Helm-owned 박스는 helm uninstall이 깔끔히 → ② 박스 밖(CRD + cm)은 kubectl delete로 손수.
3. 예시 — 실제 teardown → 재설치 → 검증
3.1 사전 상태 (teardown 직전)
$ helm -n istio-system list
istio-base base-1.30.0 deployed # CRD만
istiod istiod-1.30.0 deployed # 0/1, RS/Pod 없음(= control-plane 장애 탓)
# gateway chart 미설치
$ kubectl get crd | grep -c istio.io # 15
$ webhook: mutating istio-sidecar-injector, validating istio-validator/istiod-default-validator
# 전 네임스페이스 istio CR(Gateway/VS/DR/SE/PA/AP 등): 0개 ← teardown이 사용자 리소스에 영향 없음
보존 대상: service-a/backend(2/2, plain Deployment — CRD 삭제 무관), istioinaction ns(injection 라벨만). CR이 0개라 CRD를 통째로 지워도 cascade로 사라질 사용자 설정이 없다 — 그래서 완전 초기화가 안전한 타이밍이다.
3.2 Teardown — 2단계(Helm 역순 → 수동)
CTX=homelab; NS=istio-system
# (1) helm release 제거 — istiod 먼저, 그 다음 base (의존 역순)
helm --kube-context $CTX uninstall istiod -n $NS
helm --kube-context $CTX uninstall istio-base -n $NS
# → base 차트는 CRD를 'resource-policy: keep'로 남김(데이터 보호). 아래서 수동 삭제.
# → istiod 차트가 소유한 webhook/clusterrole/sa/deploy/svc 는 함께 제거됨.
# (2) CRD 15개 수동 삭제 (완전 초기화. CR 0개라 안전)
kubectl --context $CTX get crd -o name | grep 'istio.io' | xargs -r kubectl --context $CTX delete
# (3) istiod 런타임 생성 잔여 configmap 정리 (helm 미소유 → 수동)
kubectl --context $CTX -n $NS delete cm \
istio-ca-crl istio-ca-root-cert istio-ip-autoallocate \
istio-leader istio-namespace-controller-election
(1)은 그림의 Helm-owned 박스를, (2)는 keep-policy로 남은 CRD를, (3)은 박스 밖 runtime cm을 처리한다 — §2의 경계가 명령 3줄에 1:1로 대응한다.
teardown 검증:
istio CRD: 0 / helm release: 0 / istio webhook: 0 / clusterrole(istio): 0
istio-system: No resources found
service-a/backend: 2/2 (보존 OK)
3.3 재설치 — Helm, base → istiod → gateways (의존 정순)
repo IaC: install/helm/values-*.yaml. 동일 명령이 Makefile(make install) 및 install/helm/README.md에도 정리돼 있음. 버전 핀: --version 1.30.0.
CTX=homelab; NS=istio-system; VER=1.30.0
# repo 등록(최초 1회)
helm repo add istio https://istio-release.storage.googleapis.com/charts && helm repo update
# [1] base — CRD + cluster 리소스
helm --kube-context $CTX upgrade --install istio-base istio/base \
-n $NS --create-namespace --version $VER --wait --timeout 3m
# [2] istiod — control plane (values: access log on, 작은 리소스, autoscale off)
helm --kube-context $CTX upgrade --install istiod istio/istiod \
-n $NS --version $VER -f install/helm/values-istiod.yaml --wait --timeout 4m
# [3] gateways — ingress(NodePort) + egress(ClusterIP)
helm --kube-context $CTX upgrade --install istio-ingressgateway istio/gateway \
-n $NS --version $VER -f install/helm/values-ingress-gateway.yaml --wait --timeout 3m
helm --kube-context $CTX upgrade --install istio-egressgateway istio/gateway \
-n $NS --version $VER -f install/helm/values-egress-gateway.yaml --wait --timeout 3m
각 단계의 --wait는 단순 옵션이 아니라 의존 게이트다: 앞 release의 Pod가 Ready 될 때까지 블록하므로, base가 떠야 istiod로, istiod가 떠야 gw로 넘어간다. --timeout(3m/4m/3m)이 그 게이트의 상한 — 넘으면 다음 단계로 안 가고 실패한다. 즉 명령 나열 순서가 곧 의존 스택을 강제한다.
values 핵심 근거 (왜 이 값인가):
| chart | 파일 | 핵심 설정 | 왜 |
|---|---|---|---|
| istiod | values-istiod.yaml |
meshConfig.accessLogFile=/dev/stdout(TEXT), enablePrometheusMerge, pilot.autoscaleEnabled=false replicaCount=1, proxy 리소스 축소 |
관측성 시나리오 전제(access log on), 홈랩 단일 규모. outboundTrafficPolicy: REGISTRY_ONLY는 주석처리 → egress 시나리오에서 전환 |
| ingress gw | values-ingress-gateway.yaml |
service.type=NodePort (80→31080, 443→31443), istio: ingressgateway 라벨 |
bare-metal·LB 없음(MetalLB 도입 전). Gateway 리소스 selector와 라벨 일치 필요 |
| egress gw | values-egress-gateway.yaml |
service.type=ClusterIP, 포트 15443(tls) 포함 |
egress는 외부 노출 불필요, 메시 내부 트래픽만 경유 |
3.4 결과 검증
$ helm -n istio-system list
istio-base base-1.30.0 deployed
istiod istiod-1.30.0 deployed
istio-ingressgateway gateway-1.30.0 deployed
istio-egressgateway gateway-1.30.0 deployed
$ kubectl -n istio-system get deploy
istiod 1/1 (registry.istio.io/release/pilot:1.30.0)
istio-ingressgateway 1/1 (worker1)
istio-egressgateway 1/1 (worker2)
$ kubectl -n istio-system get svc
istio-ingressgateway NodePort 15021:32391, 80:31080, 443:31443
istio-egressgateway ClusterIP 15021,80,443,15443
istiod ClusterIP 15010,15012,443,15014
$ istioctl proxy-status # 데이터플레인 ↔ istiod xDS sync
istio-egressgateway ... 1.30.0 SYNCED(CDS,LDS,EDS)
istio-ingressgateway ... 1.30.0 SYNCED(CDS,LDS,EDS)
$ istioctl analyze -A
Info [IST0102] (Namespace default) ... not enabled for injection # 무해 Info, 경고 0
검증 합격선 4종: helm 4 release deployed / 전 pod 1/1 / proxy-status SYNCED / analyze 경고 0 — 전부 충족. proxy-status가 SYNCED라는 건 gateway(data plane)가 istiod(control plane)로부터 CDS/LDS/EDS를 정상 수신했다는 뜻 — §2의 스택이 위까지 살아 동작한다는 최종 증거다.
부수 효과: control-plane 수리 후
istioctl proxy-status·kubectl logs/exec의 기존nodes/proxy인증 오류도 해소됨(같은 뿌리였음). 데이터플레인 진단이 정상 동작.
4. 정리
멘탈모델 한 줄: Istio Helm 설치 = 의존 스택(base→istiod→gw) × ownership 경계(Helm-owned 박스 vs runtime 박스 밖). 이 2×2 좌표 하나로 install 순서·teardown 역순·CRD 잔여·cm 수동삭제가 전부 연역된다.
다음 작업
scenarios/00-sample-apps/배포(make apps) → 시나리오 10(ingress)부터 트래픽 검증.- 프로덕션 적용: 프로덕션은 NodePort 대신 LB/MetalLB, istiod HA(replica↑/PodDisruptionBudget),
revision기반 카나리 업그레이드 고려. 본 재설치는revision: ""(default) 단일.
관련 개념 / 파일
이 설치의 설계 원리는 → 컨트롤·데이터 플레인 설치 분리 · istiod 성능 4요인 참고.
- 📎 values-istiod.yaml — control plane (access log on, 단일 replica)
- 📎 values-ingress-gateway.yaml — NodePort 80→31080 / 443→31443
- 📎 values-egress-gateway.yaml — ClusterIP, 15443(tls)
- 📎 install/helm/README.md — 설치 순서 문서
- 📎 install/verify.sh — 설치 후 헬스체크 스크립트
- 선행: control-plane 장애 수리 런북
핵심 정리
- install은 의존 스택을 아래→위로, teardown은 위→아래로. base(CRD) → istiod(control plane) → gateway(data plane). istiod를 base보다 먼저 지워야 참조 순서가 안 꼬인다(DB 외래키 삭제 순서와 동형).
- Helm은 자기가 만든 선언적 리소스만 소유한다. CRD는 base 차트의
resource-policy: keep으로, runtime configmap은 istiod가 동적 생성한 것이라 — 둘 다uninstall밖. 완전 초기화엔 수동kubectl delete가 필요(teardown이 2단계인 이유). - 순수 Helm = release/values 단위 선언적·버전핀·재현 가능 IaC.
istioctl install(operator)과는 ownership 라벨이 달라 혼용하면 충돌 위험. --wait --timeout이 검증의 일부: 각 helm 단계가 Pod readiness까지 블록 → base→istiod→gw가 명령 나열이 아니라 "앞 layer Ready여야 진행"하는 게이트(상한 3m/4m/3m).- gateway 라벨
istio: ingressgateway는 이후Gateway리소스selector의 연결고리다. 일치해야 트래픽이 흐른다(시나리오 10). - 검증 합격선 4종: helm 4 release deployed / 전 pod 1/1 / proxy-status SYNCED / analyze 경고 0.
What you might be missing
- CRD keep-policy는 양날: teardown 때는 "왜 안 지워져?"의 함정이지만, 업그레이드 때는 CRD 유실(=모든 CR cascade 삭제) 없이 차트만 교체하게 해주는 데이터 보호 장치다.
helm uninstall이 CRD를 남기는 건 버그가 아니라 의도. - runtime configmap을 안 지우면:
istio-ca-root-cert는 각 ns에 뿌려지는 CA 신뢰 앵커다. 새 istiod가 재생성하므로 보통 무해하지만, 구 CA 잔재가 남으면 mTLS 신뢰 체인이 꼬일 수 있어 완전 초기화 시 정리한다. - base를 먼저 지우면 안 되는 이유는 "데이터" 순서가 아니라 "참조" 순서다. istiod가 참조하는 CRD가 먼저 사라지면 정리 로직이 참조 대상을 못 찾는다 — DB의 외래키 삭제 순서와 같은 원리.
- control-plane 장애와 같은 뿌리: 본 재설치 후
istioctl proxy-status·kubectl logs/exec의nodes/proxy인증 오류가 동시에 풀린 건 우연이 아니다 — control-plane이 Pod/RS를 못 만들던 동일 원인이 데이터플레인 진단까지 막고 있었다. 선행 control-plane 장애 수리 런북 참고. - operator와 Helm은 라벨로만 충돌을 안다: 두 설치 경로가 같은 Deployment를 서로 자기 것이라 주장하면 reconcile 루프가 싸운다. 한 메시는 한 설치 도구로 — 섞지 말 것.