強制されていない状態が正常に見えてしまう
한국어 원문으로 표시합니다.
한 줄 요약
메시에서 가장 위험한 것은 틀린 설정이 아니라 아무 데도 적용되지 않은 설정입니다. 전자는 오류를 내지만 후자는 정상처럼 보입니다.
왜 이것이 문제인가
서비스 메시에서 가장 위험한 상태는 "설정이 틀린 것" 이 아니라 "설정이 아무 데도 적용되지 않는 것" 입니다. 전자는 오류를 내지만 후자는 조용하기 때문입니다.
사이드카가 없으면 전부 무의미하다
네임스페이스에 istio-injection=enabled 를 붙이기 전에 만든 파드는 사이드카가 없습니다. 그 파드는 Running 이고 서비스도 정상 응답합니다. 그런데 트래픽이 메시를 통과하지 않으므로 mTLS 도 인가 정책도 라우팅도 적용되지 않습니다.
보안 정책을 다 써 두고 아무것도 지켜지지 않는 상태입니다.
확인 방법이 바뀌었다
Istio 는 이제 쿠버네티스의 네이티브 사이드카를 씁니다. istio-proxy 가 spec.containers 가 아니라 spec.initContainers 에 들어갑니다(restartPolicy: Always 로).
그래서 kubectl get pod -o jsonpath='{.spec.containers[*].name}' 로 확인하던 습관은 이제 사이드카가 있는데도 없다고 답합니다. 그 답을 믿으면 주입이 안 됐다고 판단하고 엉뚱한 곳을 고치게 됩니다.
옮긴 이유는 순서 문제였습니다. 옛 방식에서는 앱이 사이드카보다 먼저 떠서 네트워크가 준비되지 않은 채 요청을 보내거나, 앱이 끝났는데 사이드카가 남아 Job 이 영원히 안 끝나는 일이 있었습니다. 네이티브 사이드카는 앱보다 먼저 시작하고 나중에 끝납니다.
두 종류의 "안 된다"
| 증상 | 원인 | 볼 곳 |
|---|---|---|
응답 코드가 없음 (curl 000, exit 56) |
mTLS 미충족 — TLS 핸드셰이크에서 끊김 | 상대가 메시 밖인가, 사이드카가 있는가 |
403 (RBAC: access denied) |
인가 거부 — 연결과 mTLS 는 성공 | AuthorizationPolicy 의 principals |
응답 코드 하나로 연결 계층인지 정책 계층인지 갈립니다. 이 구분이 없으면 메시 장애를 볼 때마다 처음부터 훑게 됩니다.
메시가 실제로 하는 일과 그 대가
사이드카가 붙으면 요청 하나가 지나는 길이 길어진다. 앱 → 자기 쪽 프록시 → 상대 쪽 프록시 → 상대 앱. 이 구조가 mTLS, 재시도, 회로 차단기, 세밀한 라우팅, 그리고 모든 호출의 지표를 앱 코드를 고치지 않고 얻게 해 준다. 대신 치르는 값이 있고, 그것을 알고 도입해야 한다.
지연이 더해진다. 프록시를 두 번 지나므로 호출마다 밀리초 단위가 붙는다. 한 번이면 무시할 만하지만 화면 하나가 내부 호출을 스무 번 하면 그만큼 쌓인다.
자원을 먹는다. 파드마다 프록시가 하나씩 붙으므로 파드 수만큼 메모리와 CPU 가 더 든다. 파드가 수백 개면 이 합계가 노드 몇 대 분량이 된다.
장애 지점이 하나 늘어난다. 프록시가 설정을 못 받으면 트래픽이 멈춘다. 제어 평면이 죽어도 이미 받아 둔 설정으로 한동안 버티지만, 그 사이의 변경은 반영되지 않는다.
그래서 판단 기준은 이렇게 세운다. 서비스가 몇 개 되지 않고 호출 관계가 단순하면 메시는 과한 선택이다. 라이브러리로도 재시도와 회로 차단기를 붙일 수 있고, mTLS 는 게이트웨이에서 끝낼 수 있다. 메시가 값어치를 하는 지점은 서비스가 수십 개를 넘고, 언어가 여러 가지라 라이브러리를 통일할 수 없고, 팀마다 배포 주기가 달라 공통 정책을 코드로 강제하기 어려울 때다.
도입할 때도 한 번에 전부 켜지 않는다. 네임스페이스 하나부터 주입을 켜고, mTLS 는 PERMISSIVE 로 시작한다. 이 모드는 암호화된 연결과 평문 연결을 둘 다 받으므로, 메시 안팎이 섞여 있어도 통신이 끊기지 않는다. 지표에서 평문 연결이 0이 된 것을 확인한 뒤에 STRICT 로 올린다. 이 순서를 건너뛰고 처음부터 STRICT 를 걸면, 아직 주입되지 않은 파드와의 통신이 전부 끊기면서 원인이 보이지 않는 장애가 된다. 게이트웨이로 들어오는 외부 트래픽과 메시 안의 트래픽은 규칙이 따로 놀기 쉬우므로, 정책을 쓸 때 어느 쪽을 말하는 것인지 매번 분명히 해 두는 편이 좋다.
실무에서 진짜 중요한 것
라벨을 붙인 뒤에는 반드시 파드를 다시 만듭니다. istio-injection=enabled 는 앞으로 만들어질 파드에만 적용됩니다. 이미 떠 있는 파드를 그대로 두면 영원히 메시 밖에 남는데, 그 상태로도 서비스는 정상 응답하므로 아무도 눈치채지 못합니다.
사이드카 확인은 initContainers 까지 봅니다. 네이티브 사이드카로 옮겨 간 뒤로 spec.containers 만 보는 습관은 있는 것을 없다고 답합니다. kubectl get pod -o jsonpath='{.spec.initContainers[*].name}' 를 함께 봐야 합니다.
응답 코드로 계층을 먼저 가릅니다. 코드가 아예 없으면(curl 000) 연결 계층이고 403 이면 정책 계층입니다. 이 한 번의 분기가 없으면 메시 장애를 볼 때마다 처음부터 훑게 됩니다.
다음 실습에서 이것들을 진짜 Istio 위에서 직접 확인합니다.