Bring the server you could not migrate into the mesh: WorkloadGroup and WorkloadEntry
한국어 원문으로 표시합니다.
목표
WorkloadGroup 으로 메시 밖 서버 무리의 틀을 잡고, WorkloadEntry 로 서버 한 대씩 등록하고, ServiceEntry 로 메시 안의 서비스 이름에 묶는다. 마지막에는 그 서버가 받아 갈 설정 묶음을 istioctl x workload entry configure 로 실제로 만들어 보고, 셀렉터가 무엇을 잡는지 세는 보고서를 남긴다.
왜 중요한가
메시를 도입해도 쿠버네티스로 옮기지 못한 서버는 남는다. 이들을 바깥으로 두면 메시가 주는 것들이 거기서 끊긴다 — 신원, 암호화, 접근 정책, 그리고 무엇보다 한 장의 서비스 지도. 메시 확장은 그 서버를 손님이 아니라 구성원으로 들이는 일이다. 핵심은 신원이다. 쿠버네티스 안에서는 파드가 서비스어카운트로 신원을 받지만 VM 에는 그런 것이 없다. 그래서 WorkloadGroup 에 어느 서비스어카운트를 쓸지 적어 두고, 그 서버에는 클러스터가 발급한 토큰과 루트 인증서를 미리 넣어 준다. 이 세 가지가 맞아야 VM 의 프록시가 컨트롤 플레인에 붙어 자기 신원을 증명할 수 있다. 세 오브젝트의 관계도 헷갈리기 쉬운데, 무리의 틀이 WorkloadGroup, 서버 한 대가 WorkloadEntry, 부를 이름이 ServiceEntry 라고 보면 정리된다.
단계
/root/ist-expansion와 네임스페이스expansion를 만들고 그 안에 ServiceAccountbilling-sa를 만드세요. 그리고 이 서비스어카운트를 쓰는 워크로드가 메시에서 받게 될 신원 문자열을/root/ist-expansion/identity.txt에 한 줄로 적으세요 — 신뢰 도메인은 기본값cluster.local입니다./root/ist-expansion/wg.yaml에 WorkloadGrouplegacy-billing을 쓰고expansion에 적용하세요 —spec.metadata.labels는app: billing,spec.template은serviceAccount: billing-sa,network: onprem,ports.http: 8080이고,spec.probe는periodSeconds: 5에httpGet으로 포트 8080 의/healthz를 봅니다./root/ist-expansion/wg-bad-probe.yaml에 WorkloadGroupbad-probe를 쓰되spec.probe에httpGet과tcpSocket을 둘 다 넣으세요./root/ist-expansion/wg-no-template.yaml에는no-template을 쓰되spec.template을 아예 빼세요. 둘 다 서버 쪽 시험 적용만 해서 거절 문장을/root/ist-expansion/wg-reject.txt에 순서대로 모으세요./root/ist-expansion/we.yaml에 WorkloadEntrybilling-1을 쓰고 적용하세요 — 메타데이터 라벨app: billing,address는10.20.30.41,network는onprem,serviceAccount는billing-sa,locality는dc1/rack2,ports.http는 8080 입니다. 그다음/root/ist-expansion/we-typo.yaml에billing-typo를 쓰되 주소를10.20.30 .41처럼 띄어 써서 적용하고,istioctl analyze -n expansion -o json의 출력을/root/ist-expansion/analyze-address.json에 저장하세요. 라벨은app: billing-broken으로 둡니다./root/ist-expansion/se.yaml에 ServiceEntrybilling-svc를 쓰고 적용하세요 —hosts는billing.expansion.internal하나,location은MESH_INTERNAL,resolution은STATIC, 포트는 번호 8080 에 이름http, 프로토콜HTTP이고,workloadSelector.labels는app: billing입니다.- 세 개의 ServiceEntry 파일을 만들어 서버 쪽 시험 적용으로 규칙을 확인하세요 —
/root/ist-expansion/se-none-endpoints.yaml(resolution: NONE인데endpoints가 있음),/root/ist-expansion/se-roundrobin-two.yaml(resolution: DNS_ROUND_ROBIN에endpoints두 개),/root/ist-expansion/se-roundrobin-one.yaml(resolution: DNS_ROUND_ROBIN에endpoints하나). 결과를/root/ist-expansion/resolution-rules.tsv에<파일이름>\t<accepted|rejected>로 위 순서대로 적고, 거절 문장들은/root/ist-expansion/resolution-reject.txt에 모으세요. 세 파일 모두 실제로 적용하지는 마세요. - 먼저
istio-system네임스페이스를 만들고,istioctl manifest generate --set profile=minimal렌더에서 ConfigMapistio만 뽑아/root/ist-expansion/istio-cm.yaml에 저장한 뒤 적용하세요. 그다음expansion에 ConfigMapistio-ca-root-cert를 만드세요 — 키 이름은root-cert.pem이고 값은openssl로 만든 자체 서명 루트 인증서입니다(/root/ist-expansion/root-cert.pem). 마지막으로istioctl x workload entry configure -f /root/ist-expansion/wg.yaml -o /root/ist-expansion/vmcfg --clusterID lab-cluster를 돌려 설정 묶음을 만드세요. /root/ist-expansion/we2.yaml에 두 번째 서버billing-2를 쓰고 적용하세요 — 라벨app: billing, 주소10.20.30.42,network: onprem,serviceAccount: billing-sa,locality: dc1/rack3,ports.http8080 입니다. 그다음/root/ist-expansion/se-match.sh를 만들어expansion의 ServiceEntry 중workloadSelector가 있는 것마다<ServiceEntry 이름>\t<잡힌 WorkloadEntry 수>\t<이름들 쉼표>를 이름순으로 표준출력에만 찍게 하고, 그 출력을/root/ist-expansion/match-result.txt에 저장하세요.
참고
- 신원 형식은
spiffe://<신뢰도메인>/ns/<네임스페이스>/sa/<서비스어카운트>입니다. kubectl apply --dry-run=server는 만들지 않고 API 서버에게 물어만 봅니다.istioctl x workload entry configure는 클러스터의 ConfigMap 두 개와 서비스어카운트를 읽습니다.- 흔한 실수: WorkloadEntry 만 만들고 ServiceEntry 를 빠뜨립니다. 부를 이름이 없으면 아무도 못 부릅니다.
- 흔한 실수: 라벨을 WorkloadEntry 의
spec에 적습니다. 셀렉터가 보는 것은metadata.labels입니다. - 참고: https://istio.io/v1.24/docs/reference/config/networking/workload-group/
- 참고: https://istio.io/v1.24/docs/reference/config/networking/workload-entry/
- 참고: https://istio.io/v1.24/docs/ops/deployment/vm-architecture/
메시가 그 서버를 뭐라고 부를지 먼저 정한다
/root/ist-expansion 와 네임스페이스 expansion 를 만들고 그 안에 ServiceAccount billing-sa 를 만드세요. 그리고 이 서비스어카운트를 쓰는 워크로드가 메시에서 받게 될 신원 문자열을 /root/ist-expansion/identity.txt 에 한 줄로 적으세요 — 신뢰 도메인은 기본값 cluster.local 입니다.
메시의 신원은 IP 나 호스트 이름이 아니라 서비스어카운트에서 나옵니다. 형식은 spiffe://<신뢰도메인>/ns/<네임스페이스>/sa/<서비스어카운트> 입니다. 메시 밖의 서버도 결국 이 이름표를 받아야 안으로 들어올 수 있습니다.
VM 한 대가 아니라 VM 한 무리의 틀을 만든다
/root/ist-expansion/wg.yaml 에 WorkloadGroup legacy-billing 을 쓰고 expansion 에 적용하세요 — spec.metadata.labels 는 app: billing, spec.template 은 serviceAccount: billing-sa, network: onprem, ports.http: 8080 이고, spec.probe 는 periodSeconds: 5 에 httpGet 으로 포트 8080 의 /healthz 를 봅니다.
WorkloadGroup 은 파드의 Deployment 에 해당하는 자리입니다 — 개별 서버가 아니라 그 무리가 공통으로 가질 라벨·신원·네트워크·포트·헬스 확인 방법을 적습니다. network 값은 그 서버가 어느 네트워크에 있는지를 가리키고, 나중에 프록시가 그 값으로 경로를 고릅니다.
스키마가 거절하는 두 가지를 직접 받아 본다
/root/ist-expansion/wg-bad-probe.yaml 에 WorkloadGroup bad-probe 를 쓰되 spec.probe 에 httpGet 과 tcpSocket 을 둘 다 넣으세요. /root/ist-expansion/wg-no-template.yaml 에는 no-template 을 쓰되 spec.template 을 아예 빼세요. 둘 다 서버 쪽 시험 적용만 해서 거절 문장을 /root/ist-expansion/wg-reject.txt 에 순서대로 모으세요.
probe 는 검사 방법 중 하나만 고르게 돼 있습니다 — 스키마의 oneOf 제약입니다. template 은 없으면 무리의 기본값을 알 수 없으니 필수입니다. 거절 문장이 어느 필드를 가리키는지 읽어 보세요.
서버 한 대를 실제로 등록하고, 오타가 어디서 잡히는지 본다
/root/ist-expansion/we.yaml 에 WorkloadEntry billing-1 을 쓰고 적용하세요 — 메타데이터 라벨 app: billing, address 는 10.20.30.41, network 는 onprem, serviceAccount 는 billing-sa, locality 는 dc1/rack2, ports.http 는 8080 입니다. 그다음 /root/ist-expansion/we-typo.yaml 에 billing-typo 를 쓰되 주소를 10.20.30 .41 처럼 띄어 써서 적용하고, istioctl analyze -n expansion -o json 의 출력을 /root/ist-expansion/analyze-address.json 에 저장하세요. 라벨은 app: billing-broken 으로 둡니다.
WorkloadEntry 는 무리 안의 서버 한 대입니다. 주소 형식은 CRD 스키마가 검사하지 않아서 이상한 값도 만들어집니다 — 그런 것을 잡아 주는 쪽은 분석기입니다. 어느 코드로 나오는지 확인해 보세요.
등록한 서버를 메시의 서비스 이름에 묶는다
/root/ist-expansion/se.yaml 에 ServiceEntry billing-svc 를 쓰고 적용하세요 — hosts 는 billing.expansion.internal 하나, location 은 MESH_INTERNAL, resolution 은 STATIC, 포트는 번호 8080 에 이름 http, 프로토콜 HTTP 이고, workloadSelector.labels 는 app: billing 입니다.
WorkloadEntry 만 만들어 두면 부를 이름이 없습니다. ServiceEntry 가 메시 안의 서비스 이름을 만들고 workloadSelector 로 그 이름 뒤에 설 워크로드를 고릅니다. location 이 MESH_INTERNAL 이면 메시 구성원으로 취급돼 mTLS 와 정책이 적용되는 대상이 됩니다.
resolution 마다 endpoint 규칙이 다르다
세 개의 ServiceEntry 파일을 만들어 서버 쪽 시험 적용으로 규칙을 확인하세요 — /root/ist-expansion/se-none-endpoints.yaml(resolution: NONE 인데 endpoints 가 있음), /root/ist-expansion/se-roundrobin-two.yaml(resolution: DNS_ROUND_ROBIN 에 endpoints 두 개), /root/ist-expansion/se-roundrobin-one.yaml(resolution: DNS_ROUND_ROBIN 에 endpoints 하나). 결과를 /root/ist-expansion/resolution-rules.tsv 에 <파일이름>\t<accepted|rejected> 로 위 순서대로 적고, 거절 문장들은 /root/ist-expansion/resolution-reject.txt 에 모으세요. 세 파일 모두 실제로 적용하지는 마세요.
resolution 은 목적지를 어떻게 찾을지를 정합니다 — NONE 은 원래 목적지 IP 를 그대로 쓰고, STATIC 은 적어 준 endpoint 를, DNS 는 이름을 풀어 씁니다. 그래서 각 방식마다 endpoint 를 몇 개 적을 수 있는지가 다릅니다. 이 규칙은 istioctl 이 아니라 CRD 의 검증 규칙이 강제합니다.
그 서버가 받아 갈 설정 묶음을 실제로 만들어 본다
먼저 istio-system 네임스페이스를 만들고, istioctl manifest generate --set profile=minimal 렌더에서 ConfigMap istio 만 뽑아 /root/ist-expansion/istio-cm.yaml 에 저장한 뒤 적용하세요. 그다음 expansion 에 ConfigMap istio-ca-root-cert 를 만드세요 — 키 이름은 root-cert.pem 이고 값은 openssl 로 만든 자체 서명 루트 인증서입니다(/root/ist-expansion/root-cert.pem). 마지막으로 istioctl x workload entry configure -f /root/ist-expansion/wg.yaml -o /root/ist-expansion/vmcfg --clusterID lab-cluster 를 돌려 설정 묶음을 만드세요.
VM 쪽 프록시가 필요한 것은 세 가지입니다 — 메시 설정(어디로 붙을지), 루트 인증서(누구를 믿을지), 토큰(자기가 누구인지). 앞의 둘은 클러스터의 ConfigMap 에서 오고 토큰은 이 명령이 서비스어카운트로 직접 발급합니다. 발급된 토큰은 짧은 수명이니 화면에 찍지 마세요.
셀렉터가 실제로 무엇을 잡는지 세어 보고 보고서로 남긴다
/root/ist-expansion/we2.yaml 에 두 번째 서버 billing-2 를 쓰고 적용하세요 — 라벨 app: billing, 주소 10.20.30.42, network: onprem, serviceAccount: billing-sa, locality: dc1/rack3, ports.http 8080 입니다. 그다음 /root/ist-expansion/se-match.sh 를 만들어 expansion 의 ServiceEntry 중 workloadSelector 가 있는 것마다 <ServiceEntry 이름>\t<잡힌 WorkloadEntry 수>\t<이름들 쉼표> 를 이름순으로 표준출력에만 찍게 하고, 그 출력을 /root/ist-expansion/match-result.txt 에 저장하세요.
라벨 셀렉터는 kubectl get workloadentry -l app=billing 처럼 그대로 물어볼 수 있습니다. 셀렉터가 여러 라벨이면 쉼표로 이어 붙입니다. 잡힌 것이 없으면 0 과 하이픈을 찍게 해 두면 나중에 라벨이 어긋난 날 바로 보입니다.