5부: 라이브 데모, 직접 돌려 본 결과

gateway 전환, bursting, GPU 추론, Istio 공존, SNAT 없는 연결의 실측

온프렘을 시뮬레이션한 환경에서 돌렸다

클라우드 VPC 10.0.0.0/16

  • EKS 1.36, system 노드 2대
  • gateway 노드 2대 (c6in.xlarge, 2개 AZ)
  • Karpenter burst 노드 (m6i, m7i)
  • 클라우드 노드는 VPC CNI 사용
Transit Gateway

시뮬레이션 온프렘 VPC 172.18.0.0/16

  • hybrid 노드 호스트 2대 (m7i.xlarge)
  • GPU 호스트 1대 (g6.xlarge, 데모 3 때만)
  • Pod CIDR 172.17.0.0/16 (Cilium)
  • SSH는 EC2 Instance Connect Endpoint만
프로덕션 구성이 아님
EC2를 hybrid node로 쓰는 것은 지원되지 않고, join하면 hybrid node 요금이 부과됩니다.
실제 구성과의 차이
온프렘 서버 + VPN/Direct Connect로 바뀌고, 달라지는 값은 노드 CIDR과 호스트뿐입니다.

데모 1 녹화 영상: Gateway 리더 전환

실측 결과 재구성 (2026-10 시뮬레이션 환경), 실제 터미널 녹화 포함, 무음

데모 1: 리더가 바뀌어도 끊김은 3~5초

리더 Pod 삭제
T0
→
standby가 Lease 획득
3~5초
→
VPC 라우트 교체
새 리더 ENI
→
VTEP 갱신, 트래픽 복구
1초 간격 요청 1~2개 실패
실행Lease 전환실패한 요청 (1초 간격)라우트
1회차3초60개 중 1개새 리더 ENI로 이동
2회차5초60개 중 1개새 리더 ENI로 이동
3회차 (최소 권한 IAM)5초45개 중 2개새 리더 ENI로 이동
알아 둘 점 Lease HOLDER는 Pod 이름이 아니라 노드이름_uuid 형식입니다. 라우트 쓰기 권한은 gateway의 route table로만 제한해도 동작했습니다.

데모 2: 같은 워크로드를 세 가지로 줄여 본다

3개
모두 hybrid
→
15개
hybrid 8 + cloud 7
→
12개
warm-up
→
10개
배치 측정
→
3개
최종 배치와 정리 시간
A. 정책 없음
ReplicaSet 기본 규칙. Karpenter 기능도 끔.
B. Kyverno
hybrid 노드에 뜬 Pod에만 pod-deletion-cost 1을 부여.
C. Kyverno + Karpenter
B에 더해 Karpenter가 burst 노드 Pod의 cost를 순위로 기록.

데모 2 녹화 영상: 넘칠 때와 줄일 때

실측 결과 재구성 (2026-10 시뮬레이션 환경), 실제 터미널 녹화 포함, 무음

데모 2 결과: 온프렘 Pod를 지킨 것은 B와 C

hybrid Podcloud Pod10개로 줄였을 때의 배치
A 정책 없음
6
4
최종 3개: hybrid 2 / cloud 1
EC2 3대, 정리 123초
B Kyverno
8
2
최종 3개: hybrid 3 / cloud 0
EC2 4대, 정리 118초
C Kyverno + Karpenter
8
2
최종 3개: hybrid 3 / cloud 0
EC2 3대, 정리 87초
A는 온프렘을 먼저 지움
기본 규칙은 위치를 보지 않아 hybrid Pod 2개가 먼저 사라졌습니다.
C의 이득은 작다
EC2 4대에서 3대, 정리 118초에서 87초. 시나리오당 1회 측정이라 반복 측정이 필요합니다.

데모가 알려 준 함정 세 가지

1. 조용히 실패하는 Kyverno
background controller에 nodes get 권한이 없으면 에러 없이 annotation이 붙지 않아 B가 A와 똑같이 나왔습니다. Pod/binding은 기본 필터에서 빠져 있어 다시 켜야 합니다.
2. Karpenter는 바로 순위를 매기지 않음
줄일 수 있는 노드에만 순위를 쓰고 갱신에 1~5분이 걸립니다. 꽉 찬 15개에서 바로 줄이면 C가 B와 같아져서 12개 warm-up이 필요했습니다.
3. Pod 요청은 init container까지
Pod의 유효 요청은 컨테이너 합과 가장 큰 init container 중 큰 값입니다. Cilium agent의 100m가 init에 있어 hybrid에 6개만 들어갔습니다.
그 밖에
ClusterPolicy는 deprecated, spec.failurePolicy는 거부되고, 기능을 끄면 Karpenter가 쓴 음수 값을 직접 지워야 합니다.

데모 3 녹화 영상: 온프렘 GPU 추론

실측 결과 재구성 (2026-10 시뮬레이션 환경), 실제 터미널 녹화 포함, 무음

데모 3: hybrid GPU에서 추론, 클라우드에서 호출

클라우드 Job
system 노드
→
gateway 경로
VXLAN, VPC 라우트
→
vLLM Pod
hybrid GPU 노드, L4 23GB
→
응답 약 1초
모델 로드 후
확인한 것
allocatable nvidia.com/gpu = 1, istio-proxy 없음, hostNetwork 끔, GPU taint와 toleration 동작.
설정
taint nvidia.com/gpu=Exists:NoSchedule, label node-role/gpu, device plugin v0.17.1, vLLM v0.19.1, Qwen2.5-0.5B.
첫 시작은 느림
vLLM 이미지(약 10GB) pull이 4분 28초. 발표 전에 미리 받아 두세요. GPU 1개라 Deployment는 Recreate.

Istio와 gateway는 함께 쓸 수 있다

검증 항목 (Istio 1.30.6, sidecar 모드)결과
hybrid 노드 Pod에도 sidecar 주입통과 4개 Pod 모두 2/2
클라우드와 hybrid 사이 양방향 호출통과 둘 다 200
STRICT mTLS통과 일반 Pod의 평문 호출은 거부, 메시 호출은 mutual_tls
Istio L7 정책 (/admin 거부)통과 /echo 200, /admin/users 403
이때 Cilium 설정enable-l7-proxy=false, enable-vtep=true 그대로
막히는 것은 Istio가 아니다
gateway의 l7Proxy=false로 막히는 것은 Cilium Ingress, Gateway API, L7 정책입니다. Istio의 L7은 sidecar Envoy가 처리해 영향이 없었습니다.
참고
최신 Istio는 sidecar를 native sidecar(init container, restartPolicy Always)로 주입합니다.

Istio 실측: 경로별 지연과 gateway 전환

p50p99값은 p50 / p99, sidecar에서 sidecar까지, 순차 300회 요청
cloud → cloud
1.32 / 1.99 ms
cloud → hybrid
2.46 / 3.00 ms
hybrid → hybrid
0.62 / 1.00 ms
hybrid → cloud
2.11 / 2.92 ms
gateway 전환 중에도 실패 0건
초당 40건을 45초간 보내는 동안 리더 Pod를 삭제했고(전환 5초) 1,800건이 모두 200이었습니다.
읽는 법
gateway를 거치는 경로가 같은 쪽 경로보다 약 1ms 느립니다. 요청 재시도는 sidecar의 기본 동작입니다.

ALB 데모 녹화 영상: ip 타깃과 역방향

실측 결과 재구성 (2026-10 시뮬레이션 환경), 실제 터미널 녹화 포함, 무음

ALB ip 타깃: hybrid Pod IP가 바뀌어도 LBC가 따라간다

리전 클라이언트
10.0.x
→
internal ALB
ip 타깃 172.17.x, AZ all
→
VPC 라우트 172.17.0.0/16
gateway 리더 ENI
→
VXLAN → hybrid Pod
온프렘 노드
테스트무엇을 했나결과
등록Ingress target-type ip, hybrid Pod 2개172.17.0.202, .48이 healthy, AZ all
호출리전 Pod에서 ALB로 20회20/20
Pod 교체부하 중 rollout restart새 IP .230, .76로 54초에 수렴, 276건 중 실패 0
gateway 전환부하 중 리더 Pod 삭제인계 4초, 191건 중 실패 1 (재시도 없음)
역할 분담 타깃 등록과 해제는 AWS Load Balancer Controller가 하고, gateway는 VPC 밖 Pod IP까지 가는 경로만 줍니다. 그래서 Pod IP가 라우팅 가능해야 합니다.

반대 방향: 온프렘에서 LB로 들어오면

① internal ALB → 리전 Pod: 20/20
온프렘 → DX/VPN(데모는 TGW) → ALB private IP → VPC CNI Pod
gateway를 거치지 않습니다. 온프렘 DNS가 ALB 이름을 private IP로 풀어야 하므로 Route 53 Resolver inbound endpoint가 필요합니다.
② internal ALB → hybrid Pod: 20/20, 그러나 hairpin
온프렘 → ALB → gateway → 다시 온프렘
동작은 하지만 DX를 두 번 지나 지연과 전송 비용이 늘어납니다. 온프렘 Pod는 온프렘 LB로 받는 편이 낫습니다.
③ hybrid NodePort → 리전 Pod: 실패
온프렘 → hybrid 노드 :30080 → 노드 IP로 SNAT → underlay → 리전 Pod
Pod 출발 트래픽이 아니라 gateway를 타지 않습니다. 리전 노드 SG가 온프렘 노드 CIDR에서 오는 앱 포트를 허용해야 합니다(데모 SG는 443만 허용).
AWS 권장 리전에서 오는 트래픽은 ALB, 온프렘에서 오는 트래픽은 온프렘 쪽 Ingress나 LB로 받습니다. gateway를 쓰면 Cilium L7(Ingress, Gateway API)은 꺼지므로 온프렘은 L4 LoadBalancer나 기존 LB를 씁니다.

데모 5 녹화 영상: SNAT 없는 연결과 PrivateLink

실측 결과 재구성 (2026-10 시뮬레이션 환경), 실제 터미널 녹화 포함, 무음

native routing: 서버가 보는 출발지는 Pod IP

구간확인한 것결과
Pod 간 직접같은 hybrid 노드, hybrid 노드 사이, hybrid와 클라우드 양방향, 클라우드 기준선7/7
ClusterIPService는 목적지만 바꾸고 출발지 Pod IP 유지3/3
DNS, APICoreDNS(클라우드), hostNetwork 포함, /livez, SA 토큰6/6
control planewebhook 경로, exec와 logs, metrics-server3/3
온프렘 서버쿠버네티스 밖 socat 서버가 본 주소: Pod IP2/2
인터넷모든 Pod가 클라우드 NAT 하나로 나감3/3
정책, MTU, ALBfromCIDR 정책, 32 MiB 양방향, ip 타깃 healthy6/6
PrivateLinkSTS, S3 interface endpoint, IRSA, SG, 버킷 정책8/8
구성
Cilium routingMode: native, masquerade 끔. 온프렘 라우터(데모는 route table)가 노드별 Pod 블록을 노드로 보냅니다. VPC CNI도 172.17, 172.18 대역은 SNAT하지 않습니다.
처음 돌렸을 때 실패한 것
hostNetwork Pod의 DNS(클러스터 SG에 노드 대역 53 없음), 클라우드 Pod가 온프렘 서버로 갈 때 SNAT(VPC CNI 제외 대역 누락), 익명 /version 401(1.36은 health endpoint만 허용).
AWS 지원 범위 밖
지원 Networking Mode는 VXLAN뿐이고 gateway도 VXLAN이 필요합니다. 지원 범위 안의 대안은 VXLAN 유지, masquerade 끔, Cilium BGP로 Pod CIDR 광고입니다(개념, 이번에 측정하지 않음).

온프렘 Pod에서 PrivateLink로 AWS API 호출

hybrid Pod
172.17.x, IRSA 토큰
→
CoreDNS, VPC resolver
sts, s3 이름을 endpoint ENI로
→
TGW (실환경 DX/VPN)
출발지 Pod IP 유지
→
interface endpoint
STS, S3 (10.0.x)
IRSA: 토큰 교환부터 사설 경로
hybrid Pod가 STS endpoint로 IRSA 토큰을 교환하고 S3 endpoint로 PutObject, 클라우드 Pod가 같은 객체를 읽었습니다.
SG가 Pod IP로 구분
endpoint SG의 443은 클라우드 VPC와 hybrid Pod 대역 172.17.0.0/16에만, 노드 대역은 제외. 같은 노드에서 Pod는 302, hostNetwork(노드 IP)는 타임아웃.
버킷 정책
aws:SourceVpce 조건으로 테스트 역할은 endpoint를 거칠 때만 허용. 같은 역할로 밖에서 부르면 거부됐습니다.
S3는 interface endpoint
gateway endpoint는 온프렘에서 쓸 수 없습니다. 데모처럼 gateway endpoint 없이 만들 때는 DnsOptions를 AllResolvers로 줘야 생성됐습니다.

링크가 끊기면 hybrid Pod의 DNS는

CoreDNS 구성단절 약 95초 동안 hybrid Pod 이름 해석원인
기본: 2개 모두 클라우드 노드경로 반영 뒤 모두 실패 (43~44개 중 26개)ClusterIP 호출은 200, 이름만 못 풂
zone label, 3개, PreferSameZone (문서와 다른 값)절반가량 실패 (76~80개 중 38~44개)Cilium이 이 값을 무시하고 끊긴 클라우드 CoreDNS로도 분산
4개, PreferClose, zone 분산 강제, Cilium 재시작실패 0 (96~97개 중)hybrid Pod는 온프렘 CoreDNS만 사용
문서대로 PreferClose
hybrid 노드의 ClusterIP는 Cilium BPF가 분산합니다. Kubernetes 1.35의 새 이름 PreferSameZone은 Cilium 1.18이 무시했습니다.
zone label은 조인할 때
나중에 붙여서 Cilium을 재시작했습니다. nodeadm의 kubelet --node-labels로 조인할 때 붙이세요.
PrivateLink 우회
온프렘 CoreDNS는 온프렘 DNS로 넘겨 STS, S3가 공인 IP로 풀렸습니다. Resolver inbound endpoint로 amazonaws.com을 넘겨 해결.

정리: 무엇을 확인했고 무엇이 남았나

확인한 것
  • gateway 전환 3~5초, 그동안 요청 실패 있음
  • 온프렘 Pod 보호(B, C), GPU 추론, Istio sidecar 공존
  • ALB ip 타깃 자동 갱신, 온프렘에서 internal ALB
  • SNAT 없는 연결 38개, PrivateLink, 단절 중 DNS 실패 0
아직 남은 것
B와 C의 반복 측정, Istio ambient 모드, gateway 처리량, hostNetwork와 NodePort 경로(노드 SG), 실제 온프렘 서버에서의 재검증.
직접 해 보려면
데모 저장소의 RUNBOOK.md 순서를 따르고, 끝나면 teardown.sh로 정리해 비용을 멈추세요.
반드시 기억할 것
모든 수치는 EC2로 시뮬레이션한 환경의 값입니다. native routing은 AWS 지원 범위 밖입니다.

참고 자료 & Q&A

Q&A 예제는 검토 기준(EKS 1.36, nodeadm 1.0.20)이고, 5부 수치는 시뮬레이션 환경의 실측입니다. 적용 전에 최신 AWS 문서로 지원 조합을 확인하세요.

Thank You

EKS Hybrid Nodes, 감사합니다

수고하셨습니다!

← 목차로 돌아가기