LabHub
배우기 러닝패스 코스

Istio深化 — なぜそう流れるのか

注入結果の番号を読み、二つの入口を立てる

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 를 다시 보세요. 설명 줄에는 '무엇을 했다' 보다 '왜 그렇게 설계됐는가' 를 적으면 다음에 장애를 만났을 때 이 표가 쓸모 있습니다.