LabHub
배우기 러닝패스 코스

Istio Deep Dive — Why It Flows That Way

Read the Injected Numbers and Build Both Entrances

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

오프라인으로 사이드카 주입 산출물을 만들고, 가로채기 번호가 적힌 자리를 찾아, 그 번호가 Envoy 의 어느 리스너와 클러스터가 되는지 손으로 세워 확인한다.

왜 중요한가

사이드카가 이상하게 굴 때 가장 먼저 보는 것이 주입 산출물이다. 번호 하나가 어긋나면 프록시가 자기 요청을 다시 받거나(UID), 헬스체크가 라우팅을 타거나(-d), 외부 호출이 전부 503 이 된다(BlackHoleCluster). 번호표를 읽을 줄 알면 이 셋을 증상만 보고 가를 수 있다.

단계

  1. /root/ist2-inject 를 만들고 그 안에서 istioctl kube-inject/opt/lab/fixtures/istio/inject-target.yaml 에 사이드카를 넣어 /root/ist2-inject/inject.yaml 로 저장하세요(주입 설정 세 파일 /opt/istio/inject-config.yaml·mesh-config.yaml·values-config.yaml 을 모두 넘깁니다). 그다음 /root/ist2-inject/01-containers.txt 에 세 줄을 적으세요 — containers=(주입 뒤 컨테이너 이름을 순서대로 쉼표로), init=(초기화 컨테이너 이름), proxy_image=(istio-proxy 컨테이너의 이미지).
  2. /root/ist2-inject/inject.yamlistio-init 컨테이너 args 를 읽어 /root/ist2-inject/02-redirect.txt 에 다섯 줄을 적으세요 — outbound_port=(-p 의 값), inbound_port=(-z), proxy_uid=(-u), mode=(-m), excluded_inbound_ports=(-d, 쉼표로 이어진 그대로).
  3. /root/ist2-inject/inject.yaml 에서 세 포트를 찾아 /root/ist2-inject/03-ports.txt 에 적으세요 — readiness_port=(istio-proxyreadinessProbe.httpGet.port), merged_metrics_port=(파드 템플릿 애너테이션 prometheus.io/port), envoy_prom_port=(istio-proxy 의 포트 중 이름이 http-envoy-prom 인 것). 마지막 줄 all_excluded= 에는 세 포트가 모두 istio-init-d 목록에 들어 있으면 yes, 아니면 no 를 적으세요.
  4. /root/ist2-inject/inject.yamlsecurityContext 를 읽어 /root/ist2-inject/04-uid.txt 에 다섯 줄을 적으세요 — proxy_uid=(istio-proxyrunAsUser), proxy_run_as_non_root=(같은 컨테이너의 runAsNonRoot), proxy_caps_drop=(capabilities.drop 을 쉼표로), init_run_as_user=(istio-initrunAsUser), init_caps_add=(istio-initcapabilities.add 를 쉼표로, 매니페스트 순서대로).
  5. /root/ist2-inject/mesh.yaml 에 Envoy 설정을 쓰세요 — 관리 포트 9981, 리스너 이름 virtualInbound127.0.0.1:15006 에서 traffic_direction: INBOUND 로 듣고, 모든 경로를 클러스터 inbound|8101||(앱 역할, 127.0.0.1:8101)로 보냅니다. 업스트림을 8101ok 로 띄우고 Envoy 를 띄운 뒤 curl localhost:15006/orders 의 결과를 /root/ist2-inject/05-inbound.txtstatus=(HTTP 코드)와 body=(응답 본문 한 줄) 두 줄로 적으세요.
  6. /root/ist2-inject/mesh.yaml 에 두 번째 리스너를 더하세요(앞의 virtualInbound 는 그대로) — 이름 virtualOutbound, 127.0.0.1:10081, traffic_direction: OUTBOUND, 모든 경로를 클러스터 BlackHoleCluster 로 보냅니다. BlackHoleClustertype: STATIC 이고 엔드포인트가 하나도 없는 클러스터입니다. 다시 띄운 뒤 curl localhost:10081/ 의 결과를 /root/ist2-inject/06-outbound.txtstatus=, body=, cluster=(이 리스너의 라우트가 가리키는 클러스터 이름) 세 줄로 적으세요.
  7. 떠 있는 Envoy 의 localhost:9981/config_dump?resource=static_listeners 에서 리스너마다 이름 방향 포트 를 공백으로 나눠 한 줄씩 뽑아 /root/ist2-inject/07-listeners.txt 에 저장하세요(두 줄, 순서는 상관없습니다).
  8. /root/ist2-inject/08-report.mdoutbound_capture=, inbound_capture=, proxy_uid=, unknown_destination= 네 줄을 적고(각각 나가는 쪽을 꺾어 넣는 포트, 들어오는 쪽 포트, 가로채기에서 빠지는 사용자 ID, 6단계에서 목적지를 모르는 요청이 간 클러스터 이름), 그 아래 - 로 시작하는 설명을 네 줄 이상 적으세요.

참고

주입 산출물을 만들고 무엇이 늘었는지 센다

/root/ist2-inject 를 만들고 그 안에서 istioctl kube-inject/opt/lab/fixtures/istio/inject-target.yaml 에 사이드카를 넣어 /root/ist2-inject/inject.yaml 로 저장하세요(주입 설정 세 파일 /opt/istio/inject-config.yaml·mesh-config.yaml·values-config.yaml 을 모두 넘깁니다). 그다음 /root/ist2-inject/01-containers.txt 에 세 줄을 적으세요 — containers=(주입 뒤 컨테이너 이름을 순서대로 쉼표로), init=(초기화 컨테이너 이름), proxy_image=(istio-proxy 컨테이너의 이미지).

이 파드에는 istiod 가 없어서 kube-inject 가 평소처럼 클러스터에서 주입 설정을 받아 올 수 없습니다. 세 설정 파일을 모두 넘겨야 밖으로 나가지 않습니다 — 둘만 주면 나머지 하나를 찾으러 나갑니다. 산출물 끝에는 빈 문서가 붙으므로 yq 로 읽을 때 select(.kind=="Deployment") 로 거르세요. 값은 눈으로 베끼지 말고 yq 로 뽑아 적어야 틀리지 않습니다.

istio-init 의 인자에서 가로채기 설계를 읽는다

/root/ist2-inject/inject.yamlistio-init 컨테이너 args 를 읽어 /root/ist2-inject/02-redirect.txt 에 다섯 줄을 적으세요 — outbound_port=(-p 의 값), inbound_port=(-z), proxy_uid=(-u), mode=(-m), excluded_inbound_ports=(-d, 쉼표로 이어진 그대로).

istio-iptables 는 인자 하나하나가 규칙 한 덩어리입니다. -p 는 앱이 밖으로 보내는 연결을 꺾어 넣을 포트, -z밖에서 들어오는 연결을 꺾어 넣을 포트, -u 는 가로채지 않을 사용자 ID 입니다. args 는 문자열 목록이라 플래그 바로 다음 원소가 값입니다 — awk 로 '플래그를 본 다음 줄' 을 뽑으면 됩니다. -x 처럼 값이 빈 문자열인 플래그도 있으니 순서로 세지 마세요.

가로채기에서 빠진 포트가 누구를 위한 것인지 찾는다

/root/ist2-inject/inject.yaml 에서 세 포트를 찾아 /root/ist2-inject/03-ports.txt 에 적으세요 — readiness_port=(istio-proxyreadinessProbe.httpGet.port), merged_metrics_port=(파드 템플릿 애너테이션 prometheus.io/port), envoy_prom_port=(istio-proxy 의 포트 중 이름이 http-envoy-prom 인 것). 마지막 줄 all_excluded= 에는 세 포트가 모두 istio-init-d 목록에 들어 있으면 yes, 아니면 no 를 적으세요.

들어오는 연결은 전부 15006 으로 꺾인다고 했는데, kubelet 의 헬스체크와 프로메테우스의 긁기까지 꺾이면 Envoy 의 라우팅 규칙을 거쳐 엉뚱한 데로 갑니다. 그래서 프록시가 직접 받아야 하는 포트는 -d 로 가로채기에서 뺍니다. 애너테이션 이름에 점과 슬래시가 있으니 .metadata.annotations["prometheus.io/port"] 처럼 대괄호로 읽으세요.

UID 1337 과 권한이 어디에 나뉘어 있는지 본다

/root/ist2-inject/inject.yamlsecurityContext 를 읽어 /root/ist2-inject/04-uid.txt 에 다섯 줄을 적으세요 — proxy_uid=(istio-proxyrunAsUser), proxy_run_as_non_root=(같은 컨테이너의 runAsNonRoot), proxy_caps_drop=(capabilities.drop 을 쉼표로), init_run_as_user=(istio-initrunAsUser), init_caps_add=(istio-initcapabilities.add 를 쉼표로, 매니페스트 순서대로).

iptables 규칙을 고치려면 NET_ADMIN 이 필요하고, 그 권한은 한 번 돌고 끝나는 초기화 컨테이너에만 줍니다. 오래 사는 프록시는 root 도 아니고 모든 권한을 버린 채 뜹니다. 그리고 프록시의 UID 는 2단계의 -u 값과 같아야 합니다 — 같지 않으면 프록시가 보낸 요청이 다시 프록시로 꺾여 들어가 끝없이 돕니다. 목록은 yqjoin(",") 으로 이으세요.

15006 에 virtualInbound 를 세워 앱까지 보낸다

/root/ist2-inject/mesh.yaml 에 Envoy 설정을 쓰세요 — 관리 포트 9981, 리스너 이름 virtualInbound127.0.0.1:15006 에서 traffic_direction: INBOUND 로 듣고, 모든 경로를 클러스터 inbound|8101||(앱 역할, 127.0.0.1:8101)로 보냅니다. 업스트림을 8101ok 로 띄우고 Envoy 를 띄운 뒤 curl localhost:15006/orders 의 결과를 /root/ist2-inject/05-inbound.txtstatus=(HTTP 코드)와 body=(응답 본문 한 줄) 두 줄로 적으세요.

사이드카의 들어오는 쪽 입구가 바로 이 리스너입니다. iptables 가 파드로 오는 연결을 전부 15006 으로 꺾고, 여기서 원래 목적지 포트를 보고 inbound|<포트>|| 클러스터로 넘깁니다 — 이름의 가운데 칸이 비어 있는 것은 들어오는 쪽에는 서브셋이 없기 때문입니다. 클러스터 이름에 | 가 있으니 YAML 에서 따옴표로 감싸세요. traffic_direction 은 동작을 바꾸지 않지만 통계와 config_dump 에 방향이 남습니다.

목적지를 모르는 요청은 BlackHoleCluster 로 간다

/root/ist2-inject/mesh.yaml 에 두 번째 리스너를 더하세요(앞의 virtualInbound 는 그대로) — 이름 virtualOutbound, 127.0.0.1:10081, traffic_direction: OUTBOUND, 모든 경로를 클러스터 BlackHoleCluster 로 보냅니다. BlackHoleClustertype: STATIC 이고 엔드포인트가 하나도 없는 클러스터입니다. 다시 띄운 뒤 curl localhost:10081/ 의 결과를 /root/ist2-inject/06-outbound.txtstatus=, body=, cluster=(이 리스너의 라우트가 가리키는 클러스터 이름) 세 줄로 적으세요.

메시가 모르는 목적지로 가는 요청을 막을 때(outboundTrafficPolicy: REGISTRY_ONLY) Istio 는 그 요청을 엔드포인트가 없는 클러스터로 보냅니다. 기본값(ALLOW_ANY)에서는 대신 원래 목적지로 그냥 흘려보내는 PassthroughCluster 로 갑니다. 엔드포인트가 0개인 클러스터는 load_assignment 를 아예 적지 않으면 됩니다. Envoy 가 돌려주는 본문을 그대로 옮기세요 — 이 문구를 알아 두면 운영 로그에서 바로 알아봅니다.

config_dump 에서 두 입구의 이름·방향·포트를 되읽는다

떠 있는 Envoy 의 localhost:9981/config_dump?resource=static_listeners 에서 리스너마다 이름 방향 포트 를 공백으로 나눠 한 줄씩 뽑아 /root/ist2-inject/07-listeners.txt 에 저장하세요(두 줄, 순서는 상관없습니다).

파일에 적은 것과 Envoy 가 실제로 읽어 들인 것은 다를 수 있습니다. 운영에서 istioctl proxy-config listeners 가 보여 주는 것도 결국 이 덤프입니다. resource=static_listeners 로 좁히면 .configs[].listener 에 리스너가 하나씩 들어 있습니다. jq -r 의 문자열 보간으로 세 값을 한 줄에 이으세요.

가로채기 번호표로 정리한다

/root/ist2-inject/08-report.mdoutbound_capture=, inbound_capture=, proxy_uid=, unknown_destination= 네 줄을 적고(각각 나가는 쪽을 꺾어 넣는 포트, 들어오는 쪽 포트, 가로채기에서 빠지는 사용자 ID, 6단계에서 목적지를 모르는 요청이 간 클러스터 이름), 그 아래 - 로 시작하는 설명을 네 줄 이상 적으세요.

번호는 앞 단계의 파일에서 옮기면 됩니다 — 추측으로 적지 말고 02-redirect.txt06-outbound.txt 를 다시 보세요. 설명 줄에는 '무엇을 했다' 보다 '왜 그렇게 설계됐는가' 를 적으면 다음에 장애를 만났을 때 이 표가 쓸모 있습니다.