ブートストラップを作り、起動しない二つの場合を見分ける
한국어 원문으로 표시합니다.
목표
주입 산출물에서 pilot-agent 의 재료를 뽑고, istiod 식 부트스트랩을 손으로 써서 거르고, 사이드카가 뜨지 않는 두 경우(설정 거절과 설정 서버 부재)를 직접 만들어 가른다.
왜 중요한가
사이드카 장애의 절반은 두 증상으로 온다 — 컨테이너가 CrashLoop 이거나, 떠 있는데 Ready 가 아니거나. 앞의 것은 부트스트랩이 틀린 것이고 뒤의 것은 istiod 에서 설정을 못 받은 것이다. 부트스트랩이 무엇으로 만들어지는지 알면 로그 한 줄과 관리 포트 한 번으로 둘을 가를 수 있다.
단계
/root/ist2-boot에서istioctl kube-inject로/opt/lab/fixtures/istio/inject-target.yaml에 사이드카를 넣어/root/ist2-boot/inject.yaml로 저장하세요(주입 설정 세 파일을 모두 넘깁니다). 그다음istio-proxy컨테이너의args를 읽어/root/ist2-boot/01-args.txt에 네 줄을 적으세요 —role=(두 번째 인자),domain=(--domain다음 인자 그대로),proxy_log_level=(--proxyLogLevel=의 값),component_log_level=(--proxyComponentLogLevel=의 값)./root/ist2-boot/inject.yaml의istio-proxy환경 변수에서 일곱 개를 골라/root/ist2-boot/02-env.txt에이름=값으로 적으세요 —CA_ADDR,PILOT_CERT_PROVIDER,ISTIO_META_CLUSTER_ID,TRUST_DOMAIN,ISTIO_META_WORKLOAD_NAME은value를 그대로,POD_NAMESPACE,SERVICE_ACCOUNT는 값이 파드에서 오므로fieldRef:<fieldPath>꼴(예:fieldRef:metadata.name)로 적습니다./opt/istio/mesh-config.yaml을 읽어/root/ist2-boot/03-mesh.txt에 네 줄을 적으세요 —discovery_address=(defaultConfig.discoveryAddress),root_namespace=,trust_domain=, 그리고ca_addr_same=에는 그discoveryAddress가 2단계의CA_ADDR과 글자 그대로 같으면yes, 아니면no./root/ist2-boot/bootstrap.yaml에 부트스트랩을 쓰세요 — 관리 포트9982;node.id는 네 칸sidecar,10.0.0.7,payments-6d5f9.default,default.svc.cluster.local을 이 순서로 물결표(~)로 이은 것,node.cluster는payments.default,node.metadata에는CLUSTER_ID·MESH_ID·NAMESPACE·WORKLOAD_NAME네 열쇠(값은 2단계의ISTIO_META_변수와 네임스페이스default);dynamic_resources는 ADS(gRPC)로cds_config·lds_config를 받고initial_fetch_timeout: 0s; ADS 가 가리키는 정적 클러스터 이름은xds-grpc,STRICT_DNS로 3단계의discovery_address를 가리키며 HTTP/2 를 씁니다.envoy --mode validate의 출력과 종료 코드를/root/ist2-boot/04-validate.txt에 담으세요(마지막 줄rc=0)./root/ist2-boot/bootstrap.yaml을/root/ist2-boot/boot-typo.yaml로 복사한 뒤 ADS 쪽(ads_config의cluster_name)만istiod-xds로 바꾸세요(정적 클러스터 이름xds-grpc는 그대로).envoy --mode validate로 검사해 출력과 종료 코드를/root/ist2-boot/05-typo.txt에 담으세요(마지막 줄rc=로)./root/ist2-boot/bootstrap.yaml로 Envoy 를 띄우세요(이 파드에는 istiod 가 없습니다). 관리 포트의/config_dump에서 부트스트랩의node중id·cluster·metadata세 필드만 뽑아/root/ist2-boot/06-node.json에 저장하고,/root/ist2-boot/06-ready.txt에 세 줄을 적으세요 —ready_code=(/ready의 HTTP 코드),ready_body=(그 본문),connected_state=(통계control_plane.connected_state의 값)./root/ist2-boot/inject.yaml에서 신원의 재료를 찾아/root/ist2-boot/07-identity.txt에 다섯 줄을 적으세요 —token_volume=(serviceAccountToken을 투영하는 볼륨 이름),token_audience=(그 토큰의 audience),token_mount=(그 볼륨이istio-proxy에 붙는 경로),ca_configmap=(볼륨istiod-ca-cert가 읽는 ConfigMap 이름),ca_mount=(istiod-ca-cert가istio-proxy에 붙는 경로)./root/ist2-boot/08-report.md에xds_address=,xds_cluster=,typo_rc=,ready_without_istiod=네 줄을 적고(각각 xDS 를 받는 주소, ADS 가 가리켜야 하는 정적 클러스터 이름, 5단계의 종료 코드, 6단계에서 본/ready본문), 그 아래-로 시작하는 설명을 네 줄 이상 적으세요.
참고
- 이 파드에는 진짜 istiod 도 진짜 사이드카도 없습니다. 그래서
istioctl proxy-config로 실제 생성물을 볼 수 없고, 번역 규칙을 알고 손으로 등가 Envoy 설정을 만들어 동작을 확인합니다. 같은 규칙이 운영 클러스터의proxy-config출력에 그대로 보입니다. - 주입 산출물 끝의 빈 문서 때문에
yq는 항상select(.kind=="Deployment") | …로 거르세요. - 6단계의 Envoy 는 LIVE 가 되지 않는 것이 정상입니다. 기동을 기다릴 때
/ready가 LIVE 인지가 아니라 관리 포트가 응답하는지로 기다리세요. - Envoy 를 띄울 때는
setsid --fork nohup envoy -c <파일> --log-level warn > <로그> 2>&1 </dev/null로 셸에서 완전히 떼어 놓으세요. 다시 띄우기 전에는pkill -x envoy로 정리합니다 (pkill -f 'envoy -c'는 그 문자열이 든 셸 자신까지 죽입니다).
프록시 컨테이너는 Envoy 가 아니라 pilot-agent 를 띄운다
/root/ist2-boot 에서 istioctl kube-inject 로 /opt/lab/fixtures/istio/inject-target.yaml 에 사이드카를 넣어 /root/ist2-boot/inject.yaml 로 저장하세요(주입 설정 세 파일을 모두 넘깁니다). 그다음 istio-proxy 컨테이너의 args 를 읽어 /root/ist2-boot/01-args.txt 에 네 줄을 적으세요 — role=(두 번째 인자), domain=(--domain 다음 인자 그대로), proxy_log_level=(--proxyLogLevel= 의 값), component_log_level=(--proxyComponentLogLevel= 의 값).
이미지의 진입점은 pilot-agent 이고, 인자의 첫 두 낱말 proxy sidecar 는 '사이드카 역할의 프록시를 띄워라' 는 하위 명령입니다. --domain 의 값에 든 $(POD_NAMESPACE) 는 셸이 아니라 kubelet 이 컨테이너를 띄울 때 같은 이름의 환경 변수로 바꿔 넣는 쿠버네티스 문법입니다 — 매니페스트에는 글자 그대로 남아 있으니 그대로 옮기세요. --x=값 꼴은 sed 's/^--x=//' 로 값만 떼면 됩니다.
환경 변수가 프록시의 이름표와 주소록이 된다
/root/ist2-boot/inject.yaml 의 istio-proxy 환경 변수에서 일곱 개를 골라 /root/ist2-boot/02-env.txt 에 이름=값 으로 적으세요 — CA_ADDR, PILOT_CERT_PROVIDER, ISTIO_META_CLUSTER_ID, TRUST_DOMAIN, ISTIO_META_WORKLOAD_NAME 은 value 를 그대로, POD_NAMESPACE, SERVICE_ACCOUNT 는 값이 파드에서 오므로 fieldRef:<fieldPath> 꼴(예: fieldRef:metadata.name)로 적습니다.
환경 변수는 두 부류입니다. 주입할 때 이미 정해진 값(value)과, 파드가 뜰 때 kubelet 이 채우는 값(valueFrom.fieldRef)입니다. 파드 이름·네임스페이스·서비스 계정은 주입하는 순간에는 알 수 없으니 뒤쪽입니다. ISTIO_META_ 로 시작하는 것들은 pilot-agent 가 접두사를 떼어 Envoy 의 node.metadata 로 옮깁니다 — istiod 가 이 프록시를 알아보는 이름표입니다. yq 에서 .env[] | select(.name=="CA_ADDR") 로 하나씩 고르세요.
메시 설정의 기본값과 CA 주소를 대조한다
/opt/istio/mesh-config.yaml 을 읽어 /root/ist2-boot/03-mesh.txt 에 네 줄을 적으세요 — discovery_address=(defaultConfig.discoveryAddress), root_namespace=, trust_domain=, 그리고 ca_addr_same= 에는 그 discoveryAddress 가 2단계의 CA_ADDR 과 글자 그대로 같으면 yes, 아니면 no.
defaultConfig 는 메시 전체의 프록시 기본 설정이고, 그중 discoveryAddress 가 xDS 를 받아 올 주소입니다. istiod 는 설정 서버이면서 인증서 발급 기관(CA)이기도 해서, 기본 설치에서는 두 주소가 같은 15012 포트를 가리킵니다. 둘이 달라지는 것은 외부 CA 를 붙였을 때입니다. 값은 yq 로 뽑고 비교는 셸의 [ "$a" = "$b" ] 로 하세요.
istiod 식 부트스트랩을 손으로 써서 거른다
/root/ist2-boot/bootstrap.yaml 에 부트스트랩을 쓰세요 — 관리 포트 9982; node.id 는 네 칸 sidecar, 10.0.0.7, payments-6d5f9.default, default.svc.cluster.local 을 이 순서로 물결표(~)로 이은 것, node.cluster 는 payments.default, node.metadata 에는 CLUSTER_ID·MESH_ID·NAMESPACE·WORKLOAD_NAME 네 열쇠(값은 2단계의 ISTIO_META_ 변수와 네임스페이스 default); dynamic_resources 는 ADS(gRPC)로 cds_config·lds_config 를 받고 initial_fetch_timeout: 0s; ADS 가 가리키는 정적 클러스터 이름은 xds-grpc, STRICT_DNS 로 3단계의 discovery_address 를 가리키며 HTTP/2 를 씁니다. envoy --mode validate 의 출력과 종료 코드를 /root/ist2-boot/04-validate.txt 에 담으세요(마지막 줄 rc=0).
pilot-agent 가 만드는 부트스트랩에서 정적인 클러스터는 사실상 xds-grpc 하나뿐입니다. 리스너도 서비스 클러스터도 전부 그 클러스터를 통해 ADS 로 받아 옵니다. gRPC 는 HTTP/2 위에서 돌므로 클러스터에 http2_protocol_options 를 켜야 합니다. node.id 의 네 칸은 역할·파드 IP·파드이름.네임스페이스·네임스페이스.svc.cluster.local 순서입니다. initial_fetch_timeout: 0s 는 '설정을 받을 때까지 무한히 기다려라' 로, Istio 가 실제로 쓰는 값입니다.
클러스터 이름 한 글자가 CrashLoop 을 만든다
/root/ist2-boot/bootstrap.yaml 을 /root/ist2-boot/boot-typo.yaml 로 복사한 뒤 ADS 쪽(ads_config 의 cluster_name)만 istiod-xds 로 바꾸세요(정적 클러스터 이름 xds-grpc 는 그대로). envoy --mode validate 로 검사해 출력과 종료 코드를 /root/ist2-boot/05-typo.txt 에 담으세요(마지막 줄 rc= 로).
ADS 의 cluster_name 은 '이 이름의 정적 클러스터로 gRPC 연결을 열어라' 라는 뜻입니다. 그 이름이 정적 클러스터 목록에 없으면 Envoy 는 설정 서버에 갈 길이 없으므로 뜨기 전에 거절합니다. 쿠버네티스에서는 컨테이너가 곧바로 끝나고 다시 뜨기를 반복합니다 — CrashLoopBackOff 입니다. 거절 메시지에 틀린 이름이 그대로 찍히니 로그 한 줄로 원인을 짚을 수 있습니다.
istiod 에 닿지 못한 프록시는 준비되지 않는다
/root/ist2-boot/bootstrap.yaml 로 Envoy 를 띄우세요(이 파드에는 istiod 가 없습니다). 관리 포트의 /config_dump 에서 부트스트랩의 node 중 id·cluster·metadata 세 필드만 뽑아 /root/ist2-boot/06-node.json 에 저장하고, /root/ist2-boot/06-ready.txt 에 세 줄을 적으세요 — ready_code=(/ready 의 HTTP 코드), ready_body=(그 본문), connected_state=(통계 control_plane.connected_state 의 값).
사이드카의 준비 상태는 결국 Envoy 의 /ready 입니다(pilot-agent 가 15021 에서 그것을 대신 답합니다). initial_fetch_timeout: 0s 로 띄운 Envoy 는 첫 CDS·LDS 를 받을 때까지 초기화를 끝내지 않고 기다립니다. 이 상태에서도 관리 포트는 열려 있어서, 무엇이 적용됐는지는 config_dump 로 볼 수 있습니다. jq '.configs[0].bootstrap.node | {id, cluster, metadata}' 로 세 필드만 고르세요. /ready 의 코드는 curl -o /dev/null -w '%{http_code}' 로 받습니다.
신원은 두 볼륨에서 시작한다
/root/ist2-boot/inject.yaml 에서 신원의 재료를 찾아 /root/ist2-boot/07-identity.txt 에 다섯 줄을 적으세요 — token_volume=(serviceAccountToken 을 투영하는 볼륨 이름), token_audience=(그 토큰의 audience), token_mount=(그 볼륨이 istio-proxy 에 붙는 경로), ca_configmap=(볼륨 istiod-ca-cert 가 읽는 ConfigMap 이름), ca_mount=(istiod-ca-cert 가 istio-proxy 에 붙는 경로).
pilot-agent 는 뜨자마자 istiod(= CA_ADDR)에 인증서 서명 요청을 보냅니다. 그때 '나는 이 서비스 계정이다' 를 증명하는 것이 audience 가 istio-ca 로 좁혀진 투영 토큰이고, 상대가 진짜 istiod 인지 확인하는 것이 ConfigMap 으로 배포된 루트 인증서입니다. 볼륨은 .volumes[], 붙는 경로는 istio-proxy 의 .volumeMounts[] 에 있습니다. 투영 볼륨은 .projected.sources[].serviceAccountToken 아래를 보세요.
사이드카가 뜨지 않을 때의 점검표로 정리한다
/root/ist2-boot/08-report.md 에 xds_address=, xds_cluster=, typo_rc=, ready_without_istiod= 네 줄을 적고(각각 xDS 를 받는 주소, ADS 가 가리켜야 하는 정적 클러스터 이름, 5단계의 종료 코드, 6단계에서 본 /ready 본문), 그 아래 - 로 시작하는 설명을 네 줄 이상 적으세요.
값은 앞 단계 파일에서 옮기세요. 이 표는 '사이드카가 CrashLoop 이다', '파드가 Ready 가 안 된다' 를 만났을 때 어디부터 볼지를 정리하는 것입니다 — 설명 줄에는 증상과 원인을 짝지어 적으면 좋습니다.