상태: 초안 (2026-10-05), 일부 실측 반영. 「실측: Istio 사이드카의 비용」과 「서비스 사이 mTLS: 설정과 실제 동작」, 「실측: Service 가 만 개로 늘 때 kube-proxy 와 Cilium」 절은 필자의 실험 환경에 만든 VM 에서 직접 잰 값입니다. 나머지 서술은 공식 문서로 확인한 것이고 실측은 아직 없습니다. 글 끝의 「확인한 버전과 날짜」와 「확인하지 못한 것」을 먼저 읽으십시오. 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」으로 표시합니다.
목차
- 1. 왜 하이브리드인가
- 2. 클러스터 구성 선택지
- 3. 노드 합류와 네트워크 경로
- 4. Cilium의 역할
- 5. Istio의 역할
- 실측: Istio 사이드카의 비용
- 서비스 사이 mTLS: 설정과 실제 동작
- 실측: Service 가 만 개로 늘 때 kube-proxy 와 Cilium
- 6. 함께 쓸 때의 주의
- 7. LLM 서빙에서 특히 중요한 점
- 8. 서비스 디스커버리, 장애 도메인, 보안
- 흔히 겪는 함정
- 직접 해 보기
- 면접에서 나올 만한 질문
- 흔한 오해
- 참고 자료
- 외부 근거
- 확인한 버전과 날짜
하이브리드를 택하는 이유로는 온프레미스의 GPU 단가와 데이터 위치, 클라우드의 수급과 탄력이 흔히 거론됩니다. 둘을 함께 쓰면 클러스터 경계, 노드가 합류하는 경로, 서비스 디스커버리, 장애 범위를 모두 직접 설계해야 합니다. 이 글은 선택지와 제약을 공식 문서 기준으로 정리하고, LLM 서빙에서 특히 문제가 되는 긴 연결과 스트리밍을 따로 다룹니다.
이 글을 읽고 나면 면접에서 설명할 수 있는 것
- 클러스터를 하나로 묶을지 나눌지를 etcd 지연, 장애 범위, 운영 부담으로 비교하고, EKS Hybrid Nodes가 어디에 해당하는지 말할 수 있습니다.
- Cilium(컨테이너 네트워크 인터페이스(CNI), 정책, Gateway API, ClusterMesh)과 Istio(상호 TLS(mTLS), L7 라우팅)의 역할 경계와 함께 쓸 때 필요한 설정을 설명할 수 있습니다.
- 스트리밍 LLM 응답이 프록시 계층의 유휴 타임아웃, HTTP/2 멀티플렉싱, 얕은 헬스체크 때문에 어떻게 깨지는지 사례로 설명할 수 있습니다.
1. 왜 하이브리드인가
이유는 세 가지입니다. 첫째는 비용입니다. 상시 부하는 보유 장비에, 피크는 클라우드에 맡긴다는 논리가 흔하지만 이 글은 비용 수치를 검증하지 않았습니다. 둘째는 GPU 수급이고, 셋째는 데이터 위치입니다. AWS 블로그(2025-03-19)는 Hybrid Nodes의 용도로 데이터 가까이의 검색 증강 생성(RAG) 추론, 피크 때 클라우드 활용, 엣지 추론을 듭니다.
2. 클러스터 구성 선택지
| 선택지 | 장점 | 대가 |
|---|---|---|
| 환경별 클러스터를 나누고 메시나 Multi-Cluster Services(MCS)로 연결 | 장애와 보안 경계가 분리됩니다 | 서비스 디스커버리, 정책, 배포를 환경마다 중복 관리합니다 |
| 클라우드가 관리하는 제어 평면에 온프레미스 노드를 합류 (EKS Hybrid Nodes) | 제어 평면 운영을 AWS에 넘깁니다 | 연결이 끊기면 제어 평면과 단절됩니다 |
| 제어 평면 하나를 WAN으로 늘림 | 단일 클러스터입니다 | etcd 합의가 지연에 민감합니다 |
etcd 문서는 하트비트 간격을 구성원 간 왕복 시간(RTT)의 0.5~1.5배로, 선거 타임아웃을 RTT의 10배 이상으로 잡으라고 안내합니다. 기본값은 각각 100ms, 1000ms입니다. Kubernetes 문서는 가용성이 중요하면 제어 평면 구성요소를 최소 3개 장애 영역에 복제하라고 합니다.
EKS Hybrid Nodes 문서의 핵심 조건은 다음과 같습니다.
- AWS가 제어 평면을, 사용자가 온프레미스 노드를 관리하며, 시간당 vCPU 단위로 과금합니다.
- 연결이 안정적이어야 하며, 단절·중단·간헐·제한 환경(DDIL)에는 맞지 않습니다.
- 클라우드 인프라(다른 클라우드 포함) 위에서 하이브리드 노드를 돌리는 것은 지원 대상이 아닙니다.
- 노드와 파드 CIDR을
RemoteNodeNetwork와RemotePodNetwork로 전달하며, 각각 최대 15개이고 RFC 1918 또는 통신사급 NAT(CGNAT) 대역이어야 합니다. - 연결은 최소 100Mbps, RTT 200ms 이하를 권장하지만 엄격한 요구는 아닙니다.
- 혼합 클러스터에서는 클라우드 노드에 VPC CNI, 하이브리드 노드에 Cilium을 쓰고, 웹훅은 클라우드 노드에서 돌립니다. 양쪽 파드가 직접 통신하려면 온프레미스 파드 CIDR이 라우팅 가능해야 합니다.
GCP는 GKE Multi-cluster Services(ServiceExport로 서비스 공유)와 멀티클러스터 Gateway(설정 전용 클러스터 하나가 Gateway API 리소스 관리)만 확인했습니다.
3. 노드 합류와 네트워크 경로
flowchart LR
subgraph ONP["온프레미스"]
G1["GPU 노드 A"] --- R["라우터·방화벽"]
G2["GPU 노드 B"] --- R
end
R -- "Site-to-Site VPN 또는 Direct Connect" --> TGW["Transit Gateway 또는 가상 사설 게이트웨이"]
TGW --> VPC["VPC 서브넷"]
VPC --> CP["EKS 제어 평면"]
VPC --> CN["클라우드 노드"]
AWS Site-to-Site VPN은 터널 하나가 최대 1.25Gbps이고 대역폭 확장형은 5Gbps이며, Transit Gateway에서 동적 라우팅이면 등가 비용 다중 경로(ECMP)로 합산합니다. MTU는 1446바이트이고 경로 MTU 탐색(PMTUD)을 지원하지 않습니다. 오버레이 캡슐화 때문에 파드 MTU가 더 줄어드니 조각화를 측정으로 확인해야 합니다. Direct Connect는 사설 전용선이며 MACsec은 일부 전용 연결에서 선택 기능입니다. 모델 가중치처럼 큰 파일(설명용 예시: 140GB를 1Gbps로 받으면 이론상 약 19분)을 WAN으로 매번 당기지 말고 온프레미스 레지스트리와 캐시에 두는 것이 좋습니다.
Cilium이 노드 사이에 요구하는 포트는 VXLAN UDP 8472, WireGuard UDP 51871, 상태 점검 TCP 4240입니다. 하이브리드 환경에서는 이 포트를 중간 방화벽이 막는 일이 흔합니다(아래 「흔히 겪는 함정」).
4. Cilium의 역할
- kube-proxy 대체: 서비스 부하 분산을 eBPF(확장 Berkeley 패킷 필터)로 처리하며, 클러스터 내부 연결은 소켓이
connect()시점에 백엔드에 배정됩니다. 연결이 길면 백엔드가 한 번 정해진 뒤 바뀌지 않습니다. - 네트워크 정책: IP가 아니라 아이덴티티로 판단합니다.
- Gateway API: Gateway API v1.6.1을 지원하며
kubeProxyReplacement=true와 L7 프록시(l7Proxy)가 전제입니다. - ClusterMesh: 클러스터 사이 파드 직접 통신(평면 IP), 전역 서비스, 정책을 제공합니다. 조건은 모든 클러스터의 데이터패스 모드 동일, Cilium 마이너 버전 차이 1 이하, 파드 CIDR 비중복, 노드 사이 IP 연결입니다. 기본 최대 255개 클러스터이고
maxConnectedClusters로 511까지 늘릴 수 있으나 설치 시에만 정할 수 있습니다. 연결된 클러스터는 하나의 신뢰 도메인이므로 보안 수준이 같은 클러스터끼리만 연결하라고 문서가 경고합니다. 정책은 자동 배포되지 않으며, 1.19부터 정책은 기본으로 로컬 클러스터 엔드포인트만 선택합니다. - 서비스 디스커버리: 서비스 어노테이션 방식의 전역 서비스와 표준 MCS-API(
ServiceExport,ServiceImport,clusterset.local)를 모두 지원합니다.service.cilium.io/affinity: local이면 정상인 로컬 백엔드가 있는 동안 원격으로 보내지 않습니다. - 상호 인증(mTLS, SPIFFE/SPIRE 기반)은 문서상 베타입니다.
5. Istio의 역할
Istio는 서비스 간 mTLS 아이덴티티(SPIFFE 기반 인증서)와 L7 라우팅(VirtualService의 weight로 트래픽 분할, 재시도, 타임아웃)을 맡습니다.
| 항목 | 사이드카 모드 | 앰비언트 모드(1.24부터 GA) |
|---|---|---|
| 데이터 플레인 | 파드마다 Envoy | 노드마다 ztunnel(L4), 필요하면 waypoint(L7 Envoy) |
| mTLS | 사이드카 간 | ztunnel 간 HBONE 터널(포트 15008) |
| L7 기능 | 기본 포함 | waypoint 배포 시에만 |
멀티클러스터 앰비언트는 다중 네트워크 구성이 베타이며, 문서가 적은 제약은 다음과 같습니다. 기존 사이드카 프로덕션에는 권하지 않고, 단일 네트워크는 미검증이며, 프라이머리 클러스터 여러 개만 지원하고, waypoint 이름이 모든 클러스터에서 같아야 하며 설정을 직접 동기화해야 합니다. 1.31(2026-08-31)에는 존 인지 부하 분산(zoneAwareLbSetting)과 waypoint 카나리가 추가되었습니다.
실측: Istio 사이드카의 비용
문서가 말하는 「사이드카는 지연을 더한다」를 숫자로 확인했습니다.
환경. KubeVirt VM 한 대(8 vCPU, 7.9GiB, 게스트 커널 6.8.0-139), k3s v1.35.8, Istio 1.31.0(profile=default, 네이티브 사이드카), 부하 도구 fortio 1.75.3. 같은 VM 안에서 클라이언트 파드와 서버 파드를 띄워 plain(주입 없음)과 mesh(양쪽 사이드카, mTLS)를 번갈아 측정했습니다. 16 연결, 고정 500 QPS 25초 5회와 포화 20초 3회입니다.
| 지표 | 메시 없음 | 사이드카 mTLS | 차이 |
|---|---|---|---|
| 평균 지연 (500 QPS, 중앙값) | 0.241 ms | 2.450 ms | +2.2 ms (약 10배) |
| p50 / p90 / p99 | 0.232 / 0.349 / 0.538 ms | 2.474 / 3.068 / 3.726 ms | +2.2 / +2.7 / +3.2 ms |
| 포화 처리량 | 217,476 QPS | 16,246 QPS | 메시 없음의 7.5% |
5회 사이 흩어짐은 평균 지연 기준 ±3% 이내였고 실패한 호출은 없었습니다.
이 숫자가 말하는 것. 요청 한 번이 사이드카 두 개(클라이언트 쪽과 서버 쪽)를 지나면서 이 환경에서는 약 2.2ms 가 더해졌습니다. LLM 서빙에서 첫 토큰까지 수백 ms 가 걸리는 요청이라면 이 비용은 작지만, 토큰 하나하나를 스트리밍하는 응답이나 사내 마이크로서비스처럼 지연이 1ms 안팎인 호출에는 크게 보입니다. 포화 처리량이 7.5% 로 떨어진 것은 같은 CPU 를 프록시가 나눠 쓰기 때문이므로, 사이드카에는 지연뿐 아니라 CPU 예산을 따로 잡아야 한다는 뜻입니다.
이 숫자가 말하지 못하는 것.
- 이 표의 비교 대상은 「프록시 없음」 대 「프록시 두 개와 mTLS」입니다. 암호화와 프록시 홉을 가른 결과는 아래 「서비스 사이 mTLS」 절에 있습니다.
- 같은 노드 안의 통신이라 실제 NIC 와 노드 간 지연이 빠져 있습니다. 더해지는 크기로만 읽으십시오.
- 위 표는 일반 HTTP/1.1(본문 작음)입니다. gRPC, 큰 본문, 긴 스트리밍은 아래 「서비스 사이 mTLS」 절 끝의 별도 시험에 있습니다.
- 앰비언트 모드(ztunnel)는 시험하지 않았습니다. 「사이드카가 비싸므로 앰비언트가 낫다」는 말은 이 실측이 뒷받침하지 않습니다.
측정하다 얻은 교훈. 처음 측정은 fortio 의 기본 히스토그램 해상도(1ms) 때문에 틀렸습니다. 지연이 1ms 미만이면 백분위가 실제 값이 아니라 버킷 안의 위치(0.5, 0.9, 0.99ms)로 찍힙니다. 같은 실행의 평균 지연과 리틀의 법칙(동시 연결 수 / 처리량)이 어긋나는 것을 보고 알아챘습니다. 벤치마크 결과를 믿기 전에 도구 자체를 의심하고 두 지표를 교차 확인해야 한다는 사례이고, 5편에서 다시 다룹니다.
서비스 사이 mTLS: 설정과 실제 동작
「솔루션과 솔루션 사이에 mTLS 를 어떻게 거는가」를 Istio 1.31.0 에서 실제로 적용하며 확인한 결과입니다.
먼저 두 층을 구분해야 합니다. Cilium WireGuard 는 노드 사이를 오가는 파드 트래픽을 노드 단위 키로 암호화하지만(기본값은 Cilium 이 관리하는 파드 사이의 트래픽만이고, 노드와 호스트 트래픽은 베타 옵션을 켜야 하며, 같은 노드 안의 트래픽은 암호화하지 않습니다) 워크로드의 신원은 확인하지 않습니다. Istio mTLS 는 서비스 계정에서 발급한 인증서(SPIFFE 신원)로 양쪽이 서로를 인증하고, 그 신원을 기준으로 접근을 허용하거나 거절할 수 있습니다. 서비스 간 인증이 목적이라면 Istio 쪽이 맞는 도구입니다.
설정은 세 조각입니다. 아래 YAML 은 모두 시험 VM 에 적용해 동작을 확인했습니다. API 버전은 security.istio.io/v1 입니다.
- 네임스페이스에
istio-injection=enabled라벨을 붙이고, 이미 떠 있는 파드는 다시 시작해야 사이드카가 붙습니다. - 서버 쪽에서 mTLS 가 아닌 연결을 받을지를
PeerAuthentication으로 정합니다. 정책이 없을 때의 기본값은 PERMISSIVE 이므로, 먼저 관측한 뒤에 STRICT 로 올리는 순서가 안전합니다. - mTLS 가 성립해야 신원을 믿을 수 있으므로, 그 위에서
AuthorizationPolicy로 호출 가능한 서비스 계정을 제한합니다.
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: {name: default, namespace: <네임스페이스>}
spec: {mtls: {mode: STRICT}}
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata: {name: only-good, namespace: <네임스페이스>}
spec:
selector: {matchLabels: {app: <서버 앱>}}
action: ALLOW
rules:
- from: [{source: {principals: ["cluster.local/ns/<네임스페이스>/sa/good"]}}]
호출하는 쪽의 DestinationRule(tls.mode: ISTIO_MUTUAL)은 문서 기준으로 메시 안끼리는 자동 mTLS 가 적용되므로 필수가 아닙니다. 실험에서는 암호화를 확실히 켜고 끄기 위해 명시했습니다.
실제 동작. 클라이언트는 임시 curl 파드이고, 결과는 상태 코드와 curl 종료 코드입니다.
| 상황 | 결과 |
|---|---|
| 정책 없음(PERMISSIVE), 메시 밖 평문 클라이언트 | 200 |
| STRICT, 메시 밖 평문 클라이언트 | 000 exit=56 (연결이 끊김) |
| STRICT, 메시 안 클라이언트 | 200 |
| AuthorizationPolicy, 허용한 서비스 계정 | 200 |
같은 정책, 허용하지 않은 계정과 default 계정 |
403 |
운영에서 중요한 점이 두 가지 있습니다. 첫째, STRICT 가 거절하는 방식은 HTTP 오류가 아니라 연결 끊김입니다. 서버 로그에서 403 을 찾으면 아무것도 없고, 클라이언트에는 Connection reset 이나 curl: (56) 으로만 나타납니다. 장애가 났을 때 첫 의심은 「STRICT 인데 상대가 메시 밖인가」여야 합니다. 둘째, 인가 거절은 403 입니다. mTLS 연결은 성립했고 신원이 허용 목록에 없을 뿐이므로 두 증상이 서로 달라서 원인을 구분할 수 있습니다.
암호화와 프록시 홉의 비용 분리. 사이드카 두 개는 그대로 두고 mTLS 만 켜고 끈 비교입니다(같은 VM, 평균 지연은 500 QPS 5회 중앙값, 포화 처리량은 3회 중앙값).
| 구성 | 평균 지연 | 포화 처리량 |
|---|---|---|
| 메시 없음 | 0.241 ms | 217,476 QPS |
| 사이드카, mTLS 끔 | 2.252 ms | 18,339 QPS |
| 사이드카, mTLS 켬 | 2.437 ms | 16,511 QPS |
위에서 더해진 약 2.2ms 중 프록시 두 홉이 약 2.0ms(약 92%), 암호화가 약 0.19ms(약 8%) 입니다. 「mTLS 는 느리다」는 통념과 달리 이 환경에서는 비용 대부분이 암호화가 아니라 프록시의 HTTP 처리였습니다. 다만 이 VM 의 CPU 는 AES 하드웨어 가속(aes, vaes)을 노출했으므로, 가속이 없는 CPU 나 큰 본문, 긴 스트리밍에서도 같은 비율이라고 말할 수는 없습니다(미실측).
HTTP 해석과 프록시 홉을 가른 추가 시험. 같은 서버를 http- 이름의 포트(L7 해석)와 tcp- 이름의 포트(L4 바이트 중계)로 노출해 사이드카를 비교했습니다(같은 VM, 500 QPS 20초 5회 교차, 평균 지연 중앙값).
| 구성 | 평균 지연 | 메시 없음 대비 |
|---|---|---|
| 메시 없음 | 0.238 ms | 기준 |
사이드카, L4(tcp-) |
1.585 ms | +1.35 ms |
사이드카, L7(http-) |
2.459 ms | +2.22 ms |
더해진 2.2ms 에서 HTTP 해석이 차지한 몫은 약 0.87ms(약 39%)이고, 해석을 하지 않는 L4 로 두어도 사이드카 두 개를 거치는 것만으로 +1.35ms(약 61%)가 남았습니다. 따라서 「프록시 두 홉이 약 92%」는 암호화가 아닌 비용의 합이며, 그 안에서 HTTP 해석만의 몫은 40% 안팎입니다. 외부 연구(「Dissecting Service Mesh Overheads」)는 프로토콜 해석이 프록시 비용의 60~70% 라고 보고하는데, 이 환경의 L4 대 L7 차이로 보면 그보다 작았습니다(조건이 다르므로 단정하지 않습니다).
gRPC, 큰 본문, 긴 스트리밍, 평문 예외 포트.
| 시험 | 메시 없음 | 사이드카(mTLS) |
|---|---|---|
| gRPC 단일 호출 평균 지연(500 QPS 3회) | 0.397 ms(0.394~0.408) | 3.834 ms(3.054~3.961), 약 +3.4ms |
| 100MiB 응답 한 번 내려받기(3회) | 5,363~8,107 MB/s, 0.01~0.02초 | 1,517~1,895 MB/s, 0.06~0.07초 |
| SSE 이벤트 200개(서버가 20ms 마다 전송, 3회) | 간격 중앙 20.1ms, p99 20.3ms | 간격 중앙 20.1ms, p99 20.4ms(최대 22.0ms) |
| STRICT 에서 메시 밖 평문 클라이언트 | 000 exit=56(연결 끊김) |
|
STRICT 에 portLevelMtls 로 8080 만 PERMISSIVE |
200 |
- gRPC 는 같은 환경의 HTTP/1.1(+2.2ms)보다 더해진 값이 컸습니다(+3.4ms). 한 호출이 HTTP/2 스트림 하나라서 해석 비용이 더 드는 것으로 추측하지만 분해하지 않았습니다.
- 큰 본문은 처리량이 약 3.5~4.5배 낮았습니다. 같은 노드 안이라 평문 쪽이 메모리 복사 속도에 가깝습니다. LLM 가중치나 큰 응답을 사이드카로 지나가게 하면 이 비용을 치르므로 제외 대상을 정해야 합니다.
- 토큰 스트리밍 간격은 영향이 없었습니다. 20ms 간격의 이벤트가 사이드카를 지나도 중앙값이 같았고 p99 가 0.1ms 늘었습니다. 즉 사이드카의 지연은 이벤트마다 쌓이는 것이 아니라 요청 경로에 고정적으로 더해지는 값으로 읽힙니다(이 간격과 이벤트 크기에서).
- 예외 포트는
portLevelMtls로 열 수 있었습니다. STRICT 를 유지하면서 한 포트만 PERMISSIVE 로 두자 메시 밖 평문 클라이언트가200을 받았습니다. - 한계: 모두 같은 VM 안의 통신입니다. 큰 본문의 비율은 실제 NIC 와 CPU 가 다르면 달라집니다.
실제 서비스 클러스터에 적용할 때의 주의는 아직 시험하지 않은 추론입니다(미실측). 메시 밖에서 들어오는 평문 트래픽(예: Cilium Gateway)이 STRICT 서비스에 닿으면 위의 연결 끊김을 만나므로, 해당 포트만 portLevelMtls 로 PERMISSIVE 로 두는 방법을 씁니다(위 표에서 동작을 확인했습니다). 데이터베이스와 DaemonSet, 잡은 마지막에 전환하고, 잡은 사이드카가 종료되지 않는 문제를 따로 점검해야 합니다.
실측: Service 가 만 개로 늘 때 kube-proxy 와 Cilium
「Cilium 이 kube-proxy 를 대체하면 서비스가 많아져도 빠르다」는 말을 숫자로 확인했습니다.
환경. KubeVirt VM 두 대(각 8 vCPU, 7.9GiB, 게스트 커널 6.8.0-139, k3s v1.36.5). 하나는 k3s 내장 kube-proxy(iptables, nf_tables 백엔드)와 flannel, 다른 하나는 Cilium 1.20.1(KubeProxyReplacement=True, VXLAN)입니다. 두 VM 은 서로 다른 물리 호스트에서 돌았습니다. 서비스를 0, 100, 1,000, 5,000, 10,000개까지 늘리며 측정했습니다.
| 측정 | kube-proxy(iptables) | Cilium(eBPF) |
|---|---|---|
| 서비스 10,000개일 때 데이터플레인 항목 | NAT 규칙 110,296줄(서비스당 약 11줄) | BPF LB 항목 40,104개(서비스당 약 4개) |
| 새 Service 가 ClusterIP 로 연결되기까지, 서비스 0개 | 중앙값 약 0.96초 | 미재측 |
| 같은 지연, 서비스 10,000개 | 중앙값 약 2.3초 | 중앙값 0.31초 |
| 새 연결 평균 지연 증가(0개에서 10,000개) | 약 +0.05ms(약 15%) | 거의 없음 |
읽는 법.
- 규칙 수는 양쪽 모두 서비스 수에 선형으로 늘었습니다. 다른 점은 새 서비스가 데이터플레인에 반영되는 시간입니다. kube-proxy 쪽은 서비스가 없을 때도 1초 가까이 걸렸고(동기화 최소 간격이 1초라는 문서 내용과 맞지만 이 VM 에서 설정을 확인하지는 못했습니다), 만 개에서는 약 2.3초로 늘었습니다. Cilium 은 서비스 수와 무관하게 0.3초 안팎이었습니다.
- 대량으로 만든 직후에는 더 크게 벌어졌습니다. 만 개를 만든 직후 kube-proxy 쪽에서는 첫 새 서비스가 연결되기까지 45초가 걸렸고(두 번 중 한 번만 나타났습니다), Cilium 쪽은 0.25초였습니다. 많은 서비스를 한꺼번에 배포하는 상황에서 중요한 차이입니다.
- 새 연결 지연은 생각보다 작게 변했습니다. 규칙이 11만 줄이 되어도 평균 지연은 0.05ms 정도만 늘었습니다. 「iptables 는 서비스가 많으면 데이터패스가 느려진다」는 통념은 이 환경(같은 노드 안, 연결 8개)에서는 크게 확인되지 않았습니다. 실제 영향은 반영 지연 쪽이 더 컸습니다.
이 숫자가 말하지 못하는 것.
- 호스트가 다르므로 두 VM 의 지연 절대값은 비교할 수 없고, 각 VM 안의 증가 폭만 의미가 있습니다.
- 노드 사이 통신과 서비스당 백엔드 수 증가는 재지 않았습니다(미실측). 연결 수가 많을 때의 conntrack 은 아래 별도 시험에 있습니다.
- 측정한 서비스가
KUBE-SERVICES체인에서 어느 위치에 있는지는 확인하지 않았습니다. 외부 측정(Huawei 2017 발표, 아래 외부 근거)은 첫 패킷 지연이 체인 안의 위치에 따라 크게 달라질 수 있다고 보고하므로, 새 연결 지연이 작게 변한 결과는 서비스 위치에 따라 달라질 수 있습니다. - 새 연결 지연은 레벨마다 한 번만 쟀습니다.
ipvs와nftables모드의 kube-proxy 는 시험하지 않았습니다. 「Cilium 이 iptables 보다 낫다」가 아니라 「이 환경의 iptables 모드와 비교했을 때」입니다.
conntrack 테이블이 가득 차면. 일회용 VM 에서 nf_conntrack_max 를 낮추고, 루프백으로 동시 연결 4,000개를 한꺼번에 열어 보았습니다(연결 제한 시간 5초, 시험 전에 기존 항목이 500개 아래로 줄 때까지 기다림).
| 한도 | 시작 전 항목 | 성공 | 실패(5초 안에 못 맺음) | 비고 |
|---|---|---|---|---|
| 262,144(기본) | 223 | 4,000 | 0 | p99 14.5ms |
| 2,048 | 140 | 1,913 | 2,087 | 최대 3,068ms(SYN 재전송으로 늦게 성공한 연결) |
| 1,024 | 59 | 969 | 3,031 |
- 성공한 연결 수가 「한도에서 기존 항목을 뺀 값」과 거의 같았습니다(2,048-140=1,908 대 1,913, 1,024-59=965 대 969). 한도를 넘는 새 연결은 거절 응답 없이 패킷이 조용히 버려졌고, 클라이언트는 재전송 뒤 제한 시간까지 기다렸습니다.
- 커널 로그에는
nf_conntrack: table full, dropping packet이 남았지만 11~12줄뿐이었습니다. 로그가 속도 제한되므로 이 줄 수를 드롭 횟수로 읽으면 안 됩니다. - 기본 한도(이 VM 은 262,144)에서는 같은 부하가 문제없이 지나갔습니다. 즉 위험은 노드 메모리에 비례해 정해지는 한도와 동시 연결 수의 곱에서 오고, 한도를 넘는 순간 일부 연결만 무작위로 실패하는 형태로 나타납니다. 한도를 모니터링(
nf_conntrack_count대nf_conntrack_max)하는 이유입니다. - 한계: 같은 노드의 루프백 연결이고 연결이 모두 열려 있는 순간의 시험입니다. 실제 서비스의 짧은 연결 빈도와 타임아웃 분포에서의 실패율은 재지 않았습니다(미실측).
측정하다 얻은 교훈. 처음 측정에서 연결 시도 제한(0.2초)이 Cilium 쪽 값을 206ms 와 411ms 두 가지로 굳혔습니다. 같은 값이 반복해서 나오는 표는 도구의 해상도를 의심해야 합니다. 시도 제한을 20ms 로 줄여 다시 쟀습니다.
6. 함께 쓸 때의 주의
Cilium 문서 기준으로 kube-proxy를 Cilium이 대체하면서 Istio를 쓰려면 socketLB.hostNamespaceOnly=true와 cni.exclusive=false가 필요합니다. kube-proxy를 남기는 구성이 최소 변경이라고 권합니다. L7 HTTP 정책은 Cilium과 Istio 중 한쪽만 쓰라고 합니다. 앰비언트 mTLS 구간은 15008로 터널링되어 Cilium 정책이 출발지와 L7 정보를 볼 수 없습니다. 이 경우 메시 내부 L4 이상 정책은 Istio가 맡습니다.
7. LLM 서빙에서 특히 중요한 점
클라이언트 -> LB/Gateway -> (Envoy 사이드카 or waypoint) -> 모델 서버(vLLM 등)
각 홉마다: 요청 타임아웃 / 스트림 유휴 타임아웃 / 연결 유휴 타임아웃 / 드레인
- 긴 스트리밍: Envoy의 스트림 유휴 타임아웃 기본값은 5분이고 응답 헤더가 오기 전에 만료되면 408, 이후면 스트림 리셋입니다. 긴 프롬프트의 대기열 구간에서 무응답이 이어지면 걸릴 수 있습니다.
VirtualService의 요청timeout은 기본이 비활성입니다.DestinationRule의 연결 풀idleTimeout은 활성 요청이 없는 시간 기준이며 기본 1시간이고, HTTP/2 PING은 연결을 유지하지 못합니다. - 재시도: Istio 기본 재시도는 2회이고
connect-failure,refused-stream,unavailable,cancelled에만 적용됩니다. 비용이 큰 생성 요청이 중복되지 않는지 확인해야 합니다. - 부하 분산: 기본 알고리즘은 최소 요청(
LEAST_REQUEST)입니다. Istio 앰비언트 문서는 원격 네트워크로 장애 조치할 때 HTTP 멀티플렉싱과 연결 풀링 때문에 원격 엔드포인트 하나에 요청이 몰릴 수 있다고 적습니다. 생성 길이 편차가 큰 LLM은 요청 수만 세는 분산의 한계가 더 두드러집니다. Gateway API Inference Extension은 KV 캐시와 요청 비용을 고려하는 엔드포인트 선택기(Endpoint Picker)로 이 문제를 겨냥하며, README는 일반 공급(GA)이라고 하고 선택기 구현은 llm-d 저장소로 옮겨졌다고 적습니다. - 종료와 롤아웃: 사이드카 종료 드레인 기본값은 5초입니다. 몇 분짜리 스트림은
terminationDrainDuration과 파드 종료 유예 시간을 응답 길이에 맞춰야 합니다. - 헬스체크: 서버 프로세스가 살아 있다는 신호와 요청을 처리할 수 있다는 신호는 다릅니다. 아래 「흔히 겪는 함정」의 음성 합성(TTS) 사례처럼 실패율이 높아도 헬스체크가 200일 수 있습니다.
- 지연 분포: 입력 처리(prefill)와 토큰 생성(decode), 대기열이 섞여 꼬리 지연이 길어지므로 평균이 아니라 p95·p99로 타임아웃을 정합니다.
8. 서비스 디스커버리, 장애 도메인, 보안
장애 도메인은 클러스터, 네트워크 경로(VPN), 제어 평면으로 나눠 봅니다. 혼합 클러스터에서는 Service trafficDistribution(PreferSameZone)으로 트래픽을 같은 존에 머물게 하고, CoreDNS 복제본을 양쪽에 둡니다(EKS 문서 권고). 보안은 경계 방화벽에 의존하지 말고 서비스 간 mTLS, 공통 루트 아래의 클러스터별 중간 CA, cert-manager나 SPIRE 같은 자동 인증서 회전을 갖춥니다. 온프레미스 노드는 장기 키 대신 SSM 하이브리드 활성화나 IAM Roles Anywhere가 발급하는 임시 자격 증명으로 EKS에 인증합니다(EKS 문서). ClusterMesh와 Istio 다중 클러스터는 연결하는 순간 신뢰 도메인이 합쳐지므로 한쪽이 침해되면 다른 쪽으로 번질 수 있습니다.
흔히 겪는 함정
필자의 실험 환경은 하나의 사설 LAN 안에 있어 WAN, 클라우드, ClusterMesh, 앰비언트는 없습니다. 아래는 그 환경에서 겪은 일을 일반적인 사례로 정리한 것입니다.
- 터널 포트 차단: 윈도우 위 WSL2 노드에서 Hyper-V 방화벽이 UDP 8472를 막아 그 노드의 파드만 다른 노드와 통신하지 못했습니다(tcpdump로 확인). WireGuard UDP 51871도 같은 이유로 맺어지지 않았습니다.
- kubelet이 자기 노드 파드에 못 닿음: 같은 WSL2 노드에서는 HTTP·TCP 프로브가 모두 시간 초과여서
exec프로브로 바꿨습니다. - 게이트웨이 타임아웃: 경로마다 요청 타임아웃을 나눴습니다. 일반 경로는 120초, Server-Sent Events(SSE) 스트리밍 응답은
0s(제한 없음), 오래 걸리는 세션 생성은 900초였습니다. 게이트웨이가 끊으면 서버 로그에 오류가 남지 않아 원인 찾기가 어려웠습니다. - Istio 최소 설치: Cilium이
cni-exclusive=true여서istio-cni없이istio-init을 쓰게 되었고, 해당 네임스페이스 하나만 Pod Security Admission(PSA) 수준을 privileged로 낮췄습니다. 위 Cilium 문서는cni.exclusive=false를 권합니다. - 얕은 헬스체크: TTS 파드 여러 개를 한 서비스 뒤에 두었을 때 GPU를 다른 워크로드와 나눠 쓰던 파드의 요청 약 18%가 메모리 부족(OOM)으로 실패했는데도 파드는 Running이고 헬스체크는 200이었습니다. 결과물 하나가 수십 번의 호출로 이뤄져 74분 동안 완성된 결과물이 없었습니다.
직접 해 보기
아래는 모두 읽기 전용이거나 임시 네임스페이스에서 끝납니다. 명령은 이 글을 쓰며 실행해 검증하지 않았습니다.
# 읽기 전용: Cilium 설정 확인
kubectl -n kube-system get cm cilium-config -o yaml | grep -E "routing-mode|tunnel-protocol|enable-wireguard|kube-proxy-replacement|cni-exclusive|bpf-lb-sock-hostns-only"
kubectl get gateway,httproute -A
# 임시: 노드 사이 지연과 스트리밍 간격 (정리 포함)
kubectl create ns lab-net
kubectl -n lab-net run a --image=nicolaka/netshoot --overrides='{"spec":{"nodeName":"NODE_A"}}' -- sleep 3600
kubectl -n lab-net run b --image=nicolaka/netshoot --overrides='{"spec":{"nodeName":"NODE_B"}}' -- sleep 3600
kubectl -n lab-net exec a -- ping -c 10 $(kubectl -n lab-net get pod b -o jsonpath='{.status.podIP}')
kubectl delete ns lab-net
핑 결과는 평균보다 최대와 표준편차를 보십시오. 같은 방식으로 임시 파드에 SSE 서버를 띄워 10초 간격으로 청크를 보내고 curl -N으로 받으면 서비스 경로가 스트림을 끊는지 볼 수 있습니다. 게이트웨이 timeouts.request를 바꾸는 실험은 실제 서비스 라우트를 건드리므로 별도 테스트 게이트웨이에서만 하십시오.
면접에서 나올 만한 질문
- 클러스터를 하나로 할까요, 나눌까요? 장애와 보안 경계를 분리하려면 나누고, 관리 일원화가 중요하면 EKS Hybrid Nodes처럼 제어 평면 하나에 노드를 합류시킵니다. 제어 평면을 WAN으로 늘리면 etcd 타임아웃을 RTT에 맞춰야 하고, 연결 단절 시 영향 범위를 설계해야 합니다.
- EKS Hybrid Nodes에 필요한 네트워크는? VPN이나 Direct Connect로 VPC와 사설 연결이 필요합니다. 노드·파드 CIDR이 서로, VPC, 서비스 CIDR과 겹치면 안 되고, 웹훅과 노드 간 직접 통신에는 파드 CIDR이 라우팅 가능해야 합니다.
- Cilium과 Istio를 같이 쓰면? Cilium은 CNI, L3/L4 정책, 진입점을, Istio는 mTLS 아이덴티티와 L7 라우팅을 맡습니다. kube-proxy 대체 모드에서는 소켓 LB를 호스트 네임스페이스로 제한하고, CNI 독점을 풀어야 합니다. L7 정책은 한쪽에서만 관리합니다.
- LLM 스트리밍이 중간에 끊긴다면? 홉마다 요청, 스트림 유휴, 연결 유휴 타임아웃을 점검하고 로그가 남는 계층을 구분합니다. 스트리밍 경로의 타임아웃을 분리하고, 재시도·드레인·헬스체크를 응답 길이에 맞춥니다.
- 멀티클러스터 장애와 보안은? 평소 트래픽은 지역 선호(affinity)로 로컬에 두고 로컬 백엔드가 모두 비정상일 때만 원격으로 넘깁니다. 신뢰 도메인이 합쳐지므로 보안 수준이 같은 클러스터끼리만 연결합니다. 정책과 설정은 클러스터마다 배포되어 GitOps로 일관성을 유지해야 합니다.
흔한 오해
- ClusterMesh는 클러스터를 하나로 합치는 기능이 아닙니다. 각 클러스터의 API 서버와 정책은 독립입니다.
- Hybrid Nodes는 클라우드 노드를 붙이는 기능이 아니라 온프레미스 노드를 EKS에 붙이는 기능입니다.
- 앰비언트 모드라고 L7 기능이 자동으로 켜지지 않습니다. waypoint가 필요합니다.
- 전용선이 곧 암호화는 아닙니다. 암호화는 MACsec이나 mTLS로 따로 구성합니다.
참고 자료
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-overview.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-prereqs.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-webhooks.html
- https://aws.amazon.com/blogs/containers/run-genai-inference-across-environments-with-amazon-eks-hybrid-nodes/
- https://docs.aws.amazon.com/vpn/latest/s2svpn/vpn-limits.html
- https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html
- https://docs.cilium.io/en/stable/network/clustermesh/intro/
- https://docs.cilium.io/en/stable/network/clustermesh/setup/
- https://docs.cilium.io/en/stable/network/servicemesh/istio/
- https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/
- https://docs.cilium.io/en/stable/operations/system_requirements/
- https://istio.io/latest/docs/ambient/overview/
- https://github.com/istio/istio.io/blob/master/content/en/docs/ambient/install/multicluster/_index.md
- https://istio.io/latest/news/releases/1.31.x/announcing-1.31/
- https://istio.io/latest/docs/reference/config/networking/destination-rule/
- https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/
- https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
- https://etcd.io/docs/v3.6/tuning/
- https://cloud.google.com/kubernetes-engine/docs/concepts/multi-cluster-services
- https://github.com/kubernetes-sigs/gateway-api-inference-extension
외부 근거
직접 재지 못했거나 재는 것으로 부족한 주장은 아래 자료를 열어 확인했습니다.
| 주장 | 자료 | 확인한 내용 |
|---|---|---|
| 사이드카 비용의 대부분은 HTTP 해석 | Zhu 외, Dissecting Service Mesh Overheads(arXiv 2207.00592, 2022, 사전 발표본) | Istio 1.13 에서 HTTP 모드의 오버헤드가 TCP 모드의 지연 약 4배, CPU 약 3배이고 대부분(62~73%)이 프로토콜 해석. TLS 암호화 비용은 따로 재지 않음 |
| mTLS 암호화보다 프록시가 비싸다 | Bremler-Barr 외, Performance Comparison of Service Mesh Frameworks: the MTLS Test Case(arXiv 2411.02267, 2024, 기술 보고서) | 메시 없이 mTLS 만 켠 기준선은 p99 가 최대 3% 증가. Istio 사이드카는 mTLS 를 꺼도 처리량이 비슷했고 HTTP 해석까지 끈 TCP 모드는 약 5배. 앰비언트는 p99 증가가 훨씬 작음. 서버에 200ms 지연을 넣은 시험이라 절대값은 포화 영향이 섞임 |
| 요청당 추가 지연의 크기 | Istio 공식 성능 문서 | 1.16.2 문서는 프록시 두 개가 p90 약 1.7ms, p99 약 2.7ms 증가(1,000 QPS, 연결 16, mTLS). 1.20 문서는 p90 약 0.228ms. 이 글의 +2.2ms 는 앞의 값과 비슷하고 뒤의 값보다 약 10배 큼. 문서 하드웨어가 바뀌었는지 확인하지 못했고, 이 글의 VM 한 대에 클라이언트·서버·프록시가 함께 있어 CPU 경합이 한 원인일 수 있으나 검증하지 않음 |
| 앰비언트가 더 가볍다는 말 | 위 두 번째 논문, Istio 1.24 문서 | 3,200 QPS 에서 mTLS 강제 시 p99 증가 앰비언트 +0.03초 대 사이드카 +0.37초. ztunnel 약 0.06 vCPU·12MB 대 사이드카 약 0.20 vCPU·60MB(1,000 QPS). 이 글은 앰비언트를 시험하지 않았음 |
| kube-proxy 동기화 간격 | Kubernetes 문서, kube-proxy minSyncPeriod 기본 1초 |
이 글의 서비스 0개에서 약 0.96초와 맞음 |
| iptables 첫 패킷 지연의 규모 의존 | Huawei 2017 발표자료(서비스 수에 따른 규칙 순회 비용), Kubernetes 1.28 의 부분 갱신 | 규칙이 선형으로 순회되므로 체인 뒤쪽 서비스일수록 첫 패킷이 느려짐. ipvs 모드는 1.35 부터 deprecated 이고 nftables 가 대안 |
| WireGuard 암호화 범위와 비용 | Cilium 문서(WireGuard Transparent Encryption), WireGuard 논문(NDSS 2017) | 기본값은 Cilium 이 관리하는 파드 트래픽만, 노드 트래픽은 베타 옵션, 같은 노드 안은 암호화 안 함. 터널 라우팅 모드에서는 이중 캡슐화. 논문은 1GbE 구간에서 1,011Mbit/s 를 보고(저자 비교, 링크가 한계) |
| 하이브리드 제약 | AWS EKS Hybrid Nodes 문서, Site-to-Site VPN 한도 문서 | 터널당 1.25Gbps 한도, Cilium 은 하이브리드 노드의 지원 CNI, 하이브리드 노드는 시간당 vCPU 과금 |
| conntrack 포화의 동작 | 이 글의 시험(위) | 외부 문헌은 열지 않았고 직접 측정으로 대체함 |
확인한 버전과 날짜
2026-10-05 기준 Kubernetes 1.37.1(2026-09-23), Cilium 1.20.2(2026-09-16, 1.21.0-pre.3 존재), Istio 1.31.1(2026-09-21, 1.31은 Kubernetes 1.32~1.36 지원), Gateway API v1.6.1(Cilium 문서 기준). 실측에 쓴 Cilium 은 1.20.1, Istio 는 1.31.0 입니다.
확인하지 못한 것: EKS Hybrid Nodes 가격표의 구체 수치, 최신 지원 Cilium 버전(AWS 문서는 1.17과 1.18만 언급), GKE에 온프레미스 노드를 붙이는 제품, Direct Connect 기본 암호화 정책, ClusterMesh 원격 클러스터 단절 시 정확한 동작, 앰비언트의 WAN 지연 영향, 위 명령의 실제 출력입니다.