新的控制平面已经上线,却没有一个 Pod 迁移过去
한국어 원문으로 표시합니다.
목표
Istio 컨트롤 플레인을 리비전(revision) 으로 두 벌 나란히 올리고, 네임스페이스 라벨·default 태그로
워크로드를 옛 리비전(stable)에서 새 리비전(canary)으로 옮긴 뒤, 공유 자원을 지키면서 옛 리비전을 걷어 냅니다.
설치 맞춤(IstioOperator)도 렌더링 결과로 확인합니다.
왜 중요한가
인플레이스 업그레이드는 컨트롤 플레인을 한 번에 바꿉니다. 문제가 생기면 메시 전체가 함께 흔들립니다. 카나리 업그레이드는 새 컨트롤 플레인을 옆에 세우고 네임스페이스 단위로 옮기므로 되돌리기가 쉽지만, 그만큼 "지금 이 네임스페이스는 어느 컨트롤 플레인이 주입하나" 를 정확히 알아야 합니다. 현장에서 자주 나는 사고는 셋입니다.
- 리비전만 설치했더니
istio-injection=enabled네임스페이스에 사이드카가 붙지 않는다(default 태그가 없다). istio.io/rev=canary를 붙였는데 옛 라벨이 남아 여전히 stable 이 주입한다(istio-injection 이 우선한다).- 옛 리비전 매니페스트를 통째로 지웠더니 공유 CRD 까지 지워져 VirtualService 가 전부 사라진다.
이 실습 환경에서 진짜인 것과 아닌 것
이 파드의 클러스터는 kwok 입니다. API 서버·웹훅 선택·CRD 는 진짜이고 istiod 프로세스와 컨테이너는 없습니다.
그래서 파드 생성을 --dry-run=server 로 요청하면 API 서버가 네임스페이스 라벨로 주입 웹훅을 실제로 골라 부르고,
istiod 가 없어 호출이 실패하면서 어느 웹훅이 어느 istiod 서비스로 가려 했는지 를 오류 메시지에 남깁니다.
이것을 판정 근거로 씁니다. 반대로 사이드카가 새 istiod 에 실제로 붙는지는 이 환경에서 볼 수 없습니다
(그것은 VM 실습 "메시가 실제로 강제하는 것을 본다" 에서 다룹니다). 두 리비전은 같은 istioctl 1.24.2 로 만듭니다.
단계
- IstioOperator 로 맞춤 설치 설정을 쓰고 매니페스트를 렌더링합니다.
- 리비전 stable 컨트롤 플레인을 적용합니다.
- 리비전 설치만으로는
istio-injection=enabled가 주입되지 않는 것을 확인하고 default 태그로 해결합니다. - 같은 맞춤으로 canary 리비전을 올리고, 설치만으로는 아무 네임스페이스도 옮겨 가지 않는 것을 확인합니다.
- payments 네임스페이스를 canary 로 옮깁니다(옛 라벨 함정).
- canary 주입 설정으로 오프라인 주입해 사이드카가 어느 컨트롤 플레인에 붙도록 만들어지는지 봅니다.
- default 태그를 canary 로 옮겨 legacy 네임스페이스를 한꺼번에 옮깁니다.
- 공유 자원을 남기고 stable 만 걷어 냅니다.
참고
- 작업 파일은
/root/ica-upgrade/에 둡니다. 주입 웹훅 호출은 10초 제한이라 dry-run 한 번에 10초쯤 걸립니다. - 웹훅 설정 읽기:
kubectl get mutatingwebhookconfiguration <이름> -o yaml의namespaceSelector·clientConfig.service. - 태그 목록:
istioctl tag list. - 공식 문서: Canary Upgrades · Customizing the installation configuration · Installing the Sidecar · istioctl 명령 참조 · Dynamic Admission Control
설치하기 전에 무엇이 깔릴지 먼저 뽑아 보기
/root/ica-upgrade/stable.yaml 에 IstioOperator 를 씁니다: profile: minimal, revision: stable, meshConfig.accessLogFile: /dev/stdout, istiod(pilot) 컨테이너 요청량 cpu: 250m·memory: 512Mi. 그리고 istioctl manifest generate -f 로 렌더링한 결과를 /root/ica-upgrade/stable-manifest.yaml 에 저장합니다. 아직 클러스터에 적용하지 않습니다.
IstioOperator 의 spec.revision 은 컨트롤 플레인 오브젝트 이름 뒤에 붙는 꼬리표가 됩니다. 메시 전체 설정은 spec.meshConfig, 구성요소의 쿠버네티스 설정은 spec.components.<구성요소>.k8s 아래에 둡니다. 렌더링 결과에서 Deployment 이름과 요청량, ConfigMap 의 mesh 설정을 yq 로 찾아 원하는 값이 들어갔는지 확인하세요.
리비전 stable 컨트롤 플레인 올리기
네임스페이스 istio-system 을 만들고 stable-manifest.yaml 을 클러스터에 적용합니다. Istio CRD, istiod-stable Deployment·Service, istio-sidecar-injector-stable 웹훅 설정이 생겨야 합니다.
이 클러스터는 kwok 이라 istiod 파드는 Running 으로 보이지만 실제 프로세스는 없습니다. 그래도 API 서버·웹훅 설정·CRD 는 진짜입니다. 적용한 뒤 kubectl get mutatingwebhookconfiguration -o yaml 에서 웹훅이 어느 서비스를 부르는지(clientConfig.service), 어떤 네임스페이스 라벨을 고르는지(namespaceSelector)를 읽어 두세요 — 다음 단계의 열쇠입니다.
istio-injection=enabled 인데 사이드카가 붙지 않는다
네임스페이스 shop 에 라벨 istio.io/rev=stable, 네임스페이스 legacy 에 라벨 istio-injection=enabled 를 붙입니다. 파드 생성이 어느 주입 웹훅으로 가는지 kubectl -n <ns> run probe --image=registry.example.invalid/app:1 --restart=Never --dry-run=server 로 확인해 출력(오류 포함)을 저장합니다 — legacy 결과를 probe-legacy-before.txt 에. 그다음 istioctl tag generate default --revision stable 의 출력을 tag-default.yaml 로 저장해 적용하고, 다시 legacy 를 probe-legacy-after.txt 에, shop 을 probe-shop.txt 에 저장합니다(모두 /root/ica-upgrade/ 아래).
리비전 이름이 붙은 설치의 주입 웹훅은 istio.io/rev=<리비전> 라벨만 고릅니다. 예전 방식의 istio-injection=enabled 는 기본(default) 리비전을 뜻하는데, 리비전만 설치하면 그 역할을 맡은 웹훅이 없습니다. default 태그가 그 자리를 채웁니다. dry-run=server 요청도 변경 웹훅을 거치므로, istiod 가 없는 이 클러스터에서는 웹훅이 불렸다면 실패 메시지에 웹훅 이름과 istiod 서비스 주소가 찍히고, 불리지 않았다면 그냥 created 가 나옵니다. 웹훅 호출은 10초 제한이라 조금 기다립니다.
새 컨트롤 플레인을 올렸는데 아무도 옮겨 가지 않았다
/root/ica-upgrade/canary.yaml 에 stable 과 같은 맞춤(accessLogFile·요청량)으로 revision: canary 인 IstioOperator 를 쓰고, 렌더링한 canary-manifest.yaml 을 적용합니다. 네임스페이스 payments(라벨 istio-injection=enabled)를 만들고, 업그레이드 도중에도 기존 설정이 살아남는지 볼 표본으로 VirtualService payments/api(hosts [api], destination host api)를 만들어 그 uid 를 /root/ica-upgrade/sentinel-uid.txt 에 적습니다. 마지막으로 shop 을 다시 dry-run 해 probe-shop-after-canary.txt 에 저장합니다 — 새 리비전을 설치하는 것만으로는 shop 이 옮겨 가지 않아야 합니다.
두 리비전은 이름 꼬리표가 달라 나란히 존재합니다. 네임스페이스가 어느 쪽으로 가는지는 설치가 아니라 라벨과 태그가 정합니다. CRD 는 리비전과 무관하게 클러스터에 하나뿐인 공유 자원이라는 점도 매니페스트 두 개를 비교해 확인해 보세요. 지금은 default 태그의 검증 웹훅이 Istio 리소스 쓰기마다 불려(실패 시 무시) VirtualService 생성이 10초쯤 걸립니다.
라벨을 바꿨는데 여전히 옛 컨트롤 플레인으로 간다
네임스페이스 payments 를 canary 로 옮깁니다. 최종적으로 payments 에는 istio.io/rev=canary 가 있고 istio-injection 라벨은 없어야 하며, dry-run 결과가 canary 의 istiod 로 가야 합니다. 옮긴 뒤의 dry-run 출력을 /root/ica-upgrade/probe-payments.txt 에 저장합니다.
istio.io/rev=canary 만 더하고 dry-run 을 해 보세요. 두 라벨이 함께 있으면 어느 웹훅이 이기는지 — 리비전 웹훅의 namespaceSelector 에 istio-injection 에 대한 조건이 무엇인지 — 가 답입니다. 공식 카나리 업그레이드 문서도 같은 이유로 옛 라벨을 지우라고 합니다. 실제 클러스터라면 이 뒤에 파드를 재시작해야 새 사이드카가 주입됩니다.
재시작해야 옮겨 간다는 말은 어디에 쓰여 있나
/root/ica-upgrade/api.yaml 에 Deployment api(네임스페이스 payments, 레플리카 1, 컨테이너 api 이미지 nginx:1.27)를 쓰고, canary 리비전의 주입 설정으로 istioctl kube-inject 한 결과를 /root/ica-upgrade/api-injected.yaml 에 저장합니다. 주입 설정은 클러스터의 ConfigMap istio-sidecar-injector-canary(키 config·values)와 istio-canary(키 mesh)에서 꺼내 파일로 넘깁니다. 결과는 클러스터에 적용하지 않습니다.
kube-inject 는 기본으로 istiod 에 붙어 설정을 받는데 이 클러스터에는 istiod 프로세스가 없습니다. 대신 --injectConfigFile·--valuesFile·--meshConfigFile 세 파일을 모두 주면 오프라인으로 렌더링합니다. 결과에서 istio-proxy 가 어느 주소(discoveryAddress)의 컨트롤 플레인에 붙도록 만들어졌는지 찾아보세요. 그 값은 파드가 만들어질 때 박히므로, 이미 떠 있는 파드는 네임스페이스 라벨을 바꿔도 옛 istiod 에 붙어 있습니다.
옛 라벨을 쓰는 팀까지 한 번에 옮기기
default 태그가 canary 를 가리키게 바꿉니다(/root/ica-upgrade/tag-default-canary.yaml 로 저장해 적용). 네임스페이스 legacy 의 라벨은 건드리지 않은 채, legacy 의 dry-run 이 canary 의 istiod 로 가야 합니다. 그 출력을 /root/ica-upgrade/probe-legacy-canary.txt 에 저장합니다.
태그는 네임스페이스 라벨과 리비전 사이의 이름표입니다. 라벨을 수십 개 네임스페이스에서 바꾸는 대신 태그가 가리키는 리비전만 바꾸면 그 이름표를 쓰는 곳이 한꺼번에 옮겨 갑니다. 이미 있는 태그를 다시 만들 때 istioctl 이 무엇을 요구하는지 오류 메시지를 읽어 보세요.
옛 컨트롤 플레인을 지웠더니 설정까지 사라질 뻔했다
stable 을 걷어 냅니다. 먼저 아직 stable 을 쓰는 네임스페이스(shop)를 canary 로 옮기고, stable 매니페스트에만 있는 오브젝트를 지웁니다. 두 리비전이 함께 쓰는 오브젝트(Istio CRD, ServiceAccount istio-reader-service-account)는 지우면 안 됩니다 — CRD 를 지우면 그 종류의 모든 리소스(VirtualService payments/api 포함)가 함께 사라집니다. 지운 오브젝트 목록(종류/이름 한 줄씩)을 /root/ica-upgrade/retired.txt 에 남깁니다.
두 매니페스트에서 종류/이름 목록을 뽑아 비교하면 stable 에만 있는 것과 공유하는 것이 갈립니다(yq 로 뽑고 comm 으로 비교). 이 실습은 kubectl 로 적용했으므로 같은 매니페스트로 되돌립니다. 공식 문서의 istioctl uninstall --revision 은 istioctl 로 설치한 클러스터를 위한 명령인데, 이 클러스터에서 실측했을 때는 지울 대상을 찾지 못했습니다. 지운 뒤 shop 의 dry-run 이 canary 로 가는지, 표본 VirtualService 의 uid 가 그대로인지 확인하세요.