K8s Networking - 10. service-mesh concepts
왜 모든 앱에 "트래픽 관리 코드"를 중복해서 짜야 할까 — 서비스 메시가 푸는 문제
마이크로서비스가 30개로 늘었다. 각 서비스마다 같은 코드가 반복됐다: 재시도, 타임아웃, 서킷 브레이커, 메트릭, 분산 추적, mTLS 인증. 개발자는 비즈니스 로직보다 이 인프라 코드*를 더 짜고 있었다. 그리고 언어마다(Java/Go/Node) 각자 다시 구현했다. 한 팀이 물었다: "이걸 *앱 밖으로 뺄 수 없나?" 서비스 메시(service mesh)가 바로 그 답이다.
이 글이 푸는 것은: 서비스 메시가 어떤 문제를 풀고, 사이드카 패턴으로 어떻게 앱 코드에서 인프라 로직을 빼내는가다. 이해하면 11장(Istio)/12장(Cilium 메시)의 설계가 보인다.
서비스 간 통신에 필요한 것 — 그리고 그것이 앱마다 중복되는 문제
마이크로서비스 환경에서 서비스 간 통신에 필요한 것들을 나열해 보자:
- 신뢰성: 재시도, 타임아웃, 서킷 브레이커.
- 보안: 서비스 간 인증(mTLS), 권한.
- 관측성: 분산 추적(trace), 메트릭, 로그 상관.
- 트래픽 제어: 카나리, 가중치 분배, A/B.
이것들을 각 앱이 직접 구현하면 어떻게 되나:
- 모든 언어/프레임워크마다 다시 짠다(Java 팀, Go 팀, Node 팀이 각자 재시도 로직을 구현).
- 한 서비스가 잘못 구현하면(재시도 누락 등) 전체 연쇄 장애.
- 비즈니스 로직과 인프라 로직이 뒤섞여 변경이 어렵다 — "주문 로직 고치려다 재시도 코드까지 건드린다."
이 중복이 도입의 사례다. 30개 서비스, 3개 언어, 각자 인프라 코드. 서비스 메시는 이 인프라 로직을 앱 밖으로 빼낸다 — 앱은 비즈니스 로직만, 통신 인프라는 메시가 담당한다.
사이드카 패턴 — 옆에 앉아 투명하게 가로채기
"인프라 로직을 앱 밖으로"가 어떻게 가능한가? 앱 코드를 고치지 않으면서 트래픽을 통제하려면, 트래픽이 나가고 들어오는 경로에 무언가를 끼워넣어야 한다. 그것이 사이드카 프록시다. (Phil Calçado - Pattern: Service Mesh)
flowchart LR
APP1["앱1<br/>(비즈니스만)"] -->|"localhost"| PROXY1["프록시 사이드카<br/>(Envoy)"]
PROXY1 -. mTLS,재시도,메트릭 .-> PROXY2["프록시 사이드카"]
PROXY2 -->|"localhost"| APP2["앱2"]
전통적 서비스 메시의 구현: 사이드카 프록시(Istio는 Envoy)를 각 Pod에 앱 옆에 넣는다. 모든 인바운드/아웃바운드 트래픽이 사이드카를 거치게 해, 사이드카가 재시도/메트릭/mTLS를 처리한다.
- 앱은
localhost로 다른 서비스를 부른다(사이드카로 감). - 사이드카가 실제 원격 서비스의 사이드카로 mTLS 통신.
- 앱 코드는 한 줄도 안 바뀜. 04장의 "Pod 안 컨테이너 공유"가 사이드카를 가능하게 하는 기반이다.
사이드카가 "투명"하게 가로채는 법 — iptables 인터셉션
사이드카가 "앱 코드를 안 고친다"는 게 어떻게 가능한가? 핵심은 iptables 투명 인터셉션이다. 사이드카 주입 시(보통 namespace 라벨 + MutatingAdmissionWebhook), 다음이 일어난다:
- Pod에 istio-proxy(Envoy) 컨테이너가 추가된다.
- 동시에 Pod의
initContainers에istio-init이 추가 — 이 init 컨테이너가 Pod 네트워크 네임스페이스의 iptables 규칙을 재작성. - 규칙의 효과: 이 Pod의 모든 아웃바운드/인바운드 트래픽이 강제로 istio-proxy(15001 포트)를 경유.
flowchart LR
APP["앱 컨테이너"] -->|"connect 외부:8080"| IPT["iptables REDIRECT"]
IPT -->|"15001로 강제"| PROXY["istio-proxy (Envoy)"]
PROXY -->|"mTLS/재시도/메트릭 적용 후"| OUT["외부 목적지"]
앱은 connect("외부:8080")을 호출하지만, iptables가 그것을 가로채 15001(프록시)로 보낸다. 프록시가 실제 연결을 맺고, mTLS/재시도/메트릭을 적용. 앱은 자기가 직접 연결했다고 착각 — 사이드카의 존재를 모른다.
이것이 "투명(transparent)"의 의미다. 그리고 왜 "Pod의 컨테이너 공유"가 사이드카의 전제인지 — iptables 규칙이 Pod 네트워크 네임스페이스 단위로 적용되므로, 같은 Pod의 컨테이너들이 같은 규칙을 공유한다.
투명성의 역설 — 사이드카가 문제를 일으킬 때
이 투명성이 이점이지만 함정이기도 하다. 앱이 사이드카를 모르면 — 사이드카가 문제를 일으킬 때 원인이 보이지 않는다:
- 프록시 지연: 모든 트래픽이 한 단계 더 거치므로 RTT 증가. 앱이 "네트워크가 느리다"고 호소하지만 실제론 사이드카.
- 프록시 장애: istio-proxy가 크래시되면 Pod의 모든 통신이 막힘. 앱은 정상인데 통신 불가 — "앱 문제인 줄 알았는데 사이드카였다."
- 설정 오류: VirtualService 규칙이 트래픽을 의도치 않게 라우팅. 앱 로직엔 문제 없는데 통신이 이상.
이것이 메시 도입 후 디버깅 날이도가 올라가는 이유다. 문제의 층이 하나 더 생겼으니. Hubble/Istio의 proxy-status가 이 "보이지 않는 층"을 관측하게 해 준다.
데이터플레인과 컨트롤플레인 — 결정과 실행의 분리
500개의 사이드카에 정책을 일관되게 배포하려면, 누군가 중앙에서 결정하고 사이드카가 그것을 받아야 한다. 메시는 이를 두 층으로 나눈다:
- 데이터플레인: 사이드카 프록시들. 실제 트래픽을 처리.
- 컨트롤플레인: 사이드카들에게 "이런 정책을 적용해라"(라우팅 규칙, 인증서, 메트릭)를 배포. Istio는 Istiod, Linkerd는 자체 컨트롤러. (Istio - What is Istio?)
flowchart TD
CP["컨트롤플레인<br/>(Istiod 등)"] -. 설정 배포 .-> S1["사이드카1"]
CP -. 설정 배포 .-> S2["사이드카2"]
CP -. 인증서 발급 .-> S1
CP -. 인증서 발급 .-> S2
S1 == mTLS 트래픽 ==> S2
이 구조가 Kubernetes의 컨트롤플레인/노드와 비슷하다는 걸 눈치챌 것이다 — "결정은 중앙(컨트롤플레인), 실행은 분산(사이드카)"이라는 같은 패턴. 사이드카들은 컨트롤플레인을 watch해 자기 설정을 갱신한다.
mTLS — 코드 없는 서비스 간 암호화
서비스 간 통신을 암호화하려면(mTLS) 전통적으로 언어별 라이브러리와 인증서 관리가 필요했다. 메시는 이 전부를 mTLS 자동화로 흡수한다 — 사이드카들이 서로를 인증하고 트래픽을 암호화하고, 인증서는 컨트롤플레인이 발급/갱신한다. 앱은 한 줄도 안 건드린다.
이것이 03-k8s-security 영역의 mTLS와 직결된다 — "서비스 간 전송 구간 암호화"를 앱 코드 없이 달성. 전통적으로 mTLS를 앱에 넣으려면 언어별 라이브러리 + 인증서 관리가 필요했는데, 메시가 그 전부를 사이드카로 흡수.
관측의 세 기둥 — 메시가 주는 가시성
메시가 앱 코드 밖으로 빼는 건 통신 제어만이 아니다. 관측(observability)의 세 기둥을 메시가 수집:
| 기둥 | 메시가 수집하는 것 | 앱이 직접 안 해도 되는 이유 |
|---|---|---|
| 메트릭 | 요청 수/지연/에러율(Red)/연결 수 | 사이드카가 모든 트래픽을 지나치므로 자동 계산 |
| 분산 추적(trace) | 요청이 여러 서비스를 거치는 경로 + span | 사이드카가 헤더를 주입/전파 |
| 로그 | 접근 로그(누가 누구에게) | 사이드카가 연결 단위로 기록 |
이 세 가지를 각 앱이 언어마다 다시 구현하는 고통(앞 절의 중복 문제)을 메시가 한 번에 해결. 분산 추적의 경우 — 사이드카가 HTTP 헤더(B3, W3C Trace Context)를 자동 주입/전파하므로, 앱이 추적 헤더를 코드로 다루지 않아도 spans가 연결된다. 이것이 메시의 숨겨진 큰 가치.
트래픽 제어 패턴 — 메시가 여는 능력
메시가 주는 통신 제어는 단순 로드밸런싱을 넘어 여러 패턴을 가능하게:
- 카나리(canary): 신규 버전에 X% 트래픽. 11장 VirtualService의 weight.
- 미러링(mirror): 프로덕션 트래픽을 복사해 신규 버전에 보냄(응답 무시). 실전 부하로 신규 검증.
- 서킷 브레이커: 실패 임계 초과 시 자동 차단(연쇄 장애 방지).
- 재시도/타임아웃: 앱 대신 메시가 통신 신뢰성 담당.
- A/B 라우팅: 헤더/가중치 기반 사용자 분기.
이 패턴들을 코드 없이 메시 규칙만으로. 08장 Ingress가 외부 진입의 카나리를 다뤘다면, 메시는 내부 서비스 간의 트래픽 분배를 다룬다. "서비스A→서비스B 호출의 10%만 새 버전으로"를 메시 규칙으로. 이것이 카나리 배포의 핵심 도구.
사이드카 메시의 비용 — 만능이 아니다
사이드카 메시엔 비용이 있다:
- 자원 오버헤드: Pod마다 프록시 컨테이너 추가(CPU/메모리). Pod 1000개면 사이드카 1000개.
- 지연: 모든 트래픽이 사이드카를 두 번 거치므로 RTT 증가(~1-2ms 추가). 대부분 무시 가능하나 극단 지연 민감 서비스엔 의미.
- 디버깅 난이도: "보이지 않는 층"이 추가돼, 통신 문제의 원인이 앱/사이드카/메시 정책 어디인지 분리 어려움.
- 버전 업그레이드: 메시 자체(Istiod 등)의 업그레이드가 모든 사이드카에 영향. 잘못하면 전체 통신에 영향.
그래서 메시 도입은 트레이드오프다. 모든 클러스터가 메시를 필요로 하진 않는다.
언제 메시를 쓰지 말까 — 비용이 이익을 넘는 순간
flowchart LR
Q1{"서비스 > 20개?"} -->|아니오| NOPE["메시 과잉 가능성"]
Q1 -->|예| Q2{"다언어/다프레임워크?"}
Q2 -->|아니오| MAYBE["라이브러리로 충분할 수 있음"]
Q2 -->|예| Q3{"mTLS/분산추적/카나리 필요?"}
Q3 -->|예| MESH["메시 가치 높음"]
- 서비스 수가 적다(5-10개): 메시 오버헤드(자원/복잡성)가 이익(mTLS/관측)을 넘음.
- 한 언어/프레임워크: 라이브러리로 통일 가능하면, 사이드카보다 라이브러리가 가벼움.
- 단일 클러스터/단일 리전: cross-cluster/메시 통제가 과제가 아니면, 메시의 가치가 좁음.
- 팀이 메시 운영 숙련도 부족: 메시 디버깅이 어려움. 숙련도 없으면 메시가 장애 원인이 됨.
이 결정 트리가 "메시 도입이 가치 있는가"의 합리적 기준이다. "메시가 최신 트렌드라서"가 아니라 구체적 필요로. 잘못된 도입은 복잡성만 더하고 장애 디버깅을 어렵게 한다.
사이드카 vs 라이브러리 vs 사이드카리스 — 메시 기술의 흐름
메시 이전엔 이 기능들을 라이브러리*로 각 언어에 (Netflix Hystrix, Twitter Finagle 등). 문제: 언어/프레임워크마다 다시 구현, 버전 업데이트 부담, 다언어 환경에서 파편화. 메시가 이것을 *플랫폼 층(사이드카)으로 올려 — 언어 무관, 일관된 업데이트.
이 "라이브러리에서 사이드카로"의 이동이 서비스 메시의 본질적 가치. 단 사이드카 비용(자원/지연/복잡성)이 새로 생기므로, 12장의 Cilium(사이드카 없는)과 11장의 Ambient가 그 비용을 다시 줄이려는 다음 진화. 메시 기술은 라이브러리→사이드카→사이드카리스로 흐르는 중.
직접 확인하기 (개념 중심)
서비스 메시는 설치 없이 개념으로 이해해도 충분. Istio/Cilium 실측은 11/12장.
# Kubernetes 1.36 — 사이드카 주입 전후 Pod 컨테이너 수 비교 (개념)
# 일반 Pod: 앱 컨테이너 1개
kubectl get pod <normal-app> -o jsonpath='{.spec.containers[*].name}{"\n"}'
# Istio 주입 Pod: 앱 + istio-proxy(Envoy 사이드카) 2개
kubectl get pod <meshed-app> -o jsonpath='{.spec.containers[*].name}{"\n"}'
확인할 것: 메시 주입 Pod에 istio-proxy 사이드카가 추가.
흔히 묻는 것, 흔히 틀리는 것
| 오해 | 정정 |
|---|---|
| "서비스 메시는 쿠버네티스 필수다" | 아니다. 트레이드오프. 서비스 적으면 과잉 |
| "사이드카는 앱 코드를 고쳐야 한다" | 안 고침. iptables로 투명하게 가로챔 |
| "메시의 mTLS는 외부 사용자 대상이다" | 서비스 간. 외부 HTTPS는 Ingress/Gateway |
| "사이드카가 없으면 메시가 아니다" | Cilium은 eBPF로 사이드카 없이 메시 |
| "데이터플레인과 컨트롤플레인은 쿠버네티스 것과 같다" | 패턴은 비슷(결정-실행 분리)이나 별개 객체/구현 |
| "메시는 트래픽만 제어한다" | 보안(mTLS)/관측(추적)/신뢰성(재시도) 포괄 |
요약 — 이 글의 결론
- 서비스 메시가 푸는 문제: 마이크로서비스의 인프라 로직(재시도/인증/추적/트래픽 제어)을 앱 밖으로. 언어마다 중복 구현하는 고통 제거.
- 사이드카 패턴이 전통적 구현 — Pod에 프록시(Envoy 등)를 앱 옆에 넣어 iptables 투명 인터셉션으로 트래픽을 가로챔. 앱 코드 한 줄 안 바뀜.
- 투명성의 역설: 사이드카가 보이지 않아 디버깅이 어려워짐. "보이지 않는 층"이 추가.
- 데이터플레인(사이드카) + 컨트롤플레인(설정/인증서 배포)의 2층. 컨트롤플레인을 watch하는 구조.
- mTLS 자동화가 가장 가시적 이점 — 서비스 간 암호화를 코드 없이.
- 관측의 세 기둥(메트릭/추적/로그)도 메시가 수집. 분산 추적 헤더 자동 전파가 숨은 가치.
- 비용(자원/지연/복잡성)이 있어 만능 아님. 서비스 많고 세밀한 통제 필요할 때 가치. Cilium이 사이드카 없이 비용을 줄이는 대안.
- 메시 기술은 라이브러리→사이드카→사이드카리스로 진화 중. 이 철학을 알면 11(Istio)/12(Cilium) 설계가 보인다.
생각해 볼 문제
- 사이드카가 iptables로 트래픽을 가로챈다고 했다. 앱이 외부로 직접 소켓을 열려 하면 어떻게 되나?
- mTLS 인증서가 만료된다. 컨트롤플레인 없이 사이드카끼리만 통신하면 어떻게 되나?
- 사이드카 메시의 자원 오버헤드를, Pod 1000개 클러스터에서 정량적으로 생각해 보라.
- 메시 없이 재시도/서킷브레이커를 구현하면 언어별로 다시 짜야 한다. 이것이 왜 장애를 키우는가?
- Cilium이 "사이드카 없는 메시"를 가능케 하는 기술과의 연결을 설명하라.
- 서비스가 5개인 소규모 클러스터에 메시를 도입하면 얻는 것보다 잃는 게 큰 이유를 비용 항목별로 말하라.
참고
- Istio docs - What is Istio? - 접근 2026-07-13 (서비스 메시 정의)
- Linkerd docs - What is a service mesh? - 접근 2026-07-13
- Phil Calçado - Pattern: Service Mesh - 접근 2026-07-13 (사이드카 패턴 원류)
- Envoy Proxy - 접근 2026-07-13 (사이드카 데이터플레인)
- Kubernetes 공식 문서 - Service Mesh 개념 - 접근 2026-07-13
'Tech Artifacts > K8s Networking' 카테고리의 다른 글
| K8s Networking - 12. cilium-service-mesh (0) | 2026.07.14 |
|---|---|
| K8s Networking - 11. istio (0) | 2026.07.14 |
| K8s Networking - 09. gateway-api (0) | 2026.07.14 |
| K8s Networking - 08. ingress (1) | 2026.07.14 |
| K8s Networking - 07. calico advanced-policy (0) | 2026.07.14 |