LabHub
学习 学习路径 课程

Envoy 内部结构

不重启也能换掉配置

在 LabHub 中继续学习

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

목표

정적 설정의 한계를 확인한 뒤 같은 프록시를 파일 구독 방식으로 바꾸고, 클러스터와 리스너를 재시작 없이 갈아 끼우고, 잘못된 갱신이 거절되는 것까지 본다.

왜 중요한가

서비스 메시와 인그레스 컨트롤러가 하는 일의 알맹이가 이것이다. 그런데 문서로만 읽으면 '컨트롤 플레인이 죽으면 메시가 죽는다' 같은 잘못된 문장이 머리에 남는다. 한 번 직접 거절을 만들어 보면, 거절된 뒤에도 트래픽이 흐른다는 사실과 그래서 아무도 모른 채 몇 주가 지날 수 있다는 사실이 함께 남는다. 그 둘을 아는 사람만 update_rejected 에 알림을 건다.

단계

  1. 업스트림 둘을 ok 로 띄우세요(8096·8097). /root/envd-xds/static.yaml/static=v1 을 돌려주는 정적 설정을 두고 띄운 뒤(관리 9977, 리스너 127.0.0.1:10077), 띄운 채로 그 파일의 v1v2 로 고치세요. 고치기 전과 후에 각각 요청해 /root/envd-xds/01-static.txtbefore=·after_edit= 두 줄을 적으세요(다시 띄우지 않습니다).
  2. /root/envd-xds/dyn.yaml 을 만드세요 — static_resources 없이 node(id envd-xds-1, cluster envd-xds)와 dynamic_resources 만 두고, lds_config/root/envd-xds/xds/lds.yaml 을, cds_config/root/envd-xds/xds/cds.yamlpath_config_source 로 구독합니다. 두 자원 파일도 만드세요 — 리스너는 /markerlds=v1 을 돌려주고 나머지는 클러스터 pool 로 보내며, 클러스터 pool 의 엔드포인트는 8096·8097 둘입니다. 띄운 뒤 /marker/ 를 요청해 /root/envd-xds/02-subscribe.txtmarker=·upstream_ok=(/ 요청이 200 이면 yes) 두 줄을 적으세요.
  3. /root/envd-xds/xds/cds.yaml 에서 엔드포인트 8097빼세요(엔드포인트 하나만 남깁니다). 프록시는 그대로 둔 채 반영될 때까지 기다린 뒤 여덟 번 요청해 /root/envd-xds/03-reload.txtp8096=·p8097=·total= 세 줄을 적으세요.
  4. /config_dump 에서 클러스터 절만 뽑아 /root/envd-xds/04-dump.json 에 저장하고, /root/envd-xds/04-dump.txtstatic_clusters=(정적 클러스터 수), dynamic_clusters=(동적 클러스터 수), dynamic_name=(동적 클러스터의 이름) 세 줄을 적으세요.
  5. /root/envd-xds/xds/lds.yaml/marker 응답을 lds=v1 에서 lds=v2 로 고치세요. 프록시는 그대로 둔 채 반영될 때까지 기다린 뒤 /root/envd-xds/05-lds.txtmarker_after=(/marker 응답)와 upstream_still_ok=(/ 요청이 여전히 200 이면 yes) 두 줄을 적으세요.
  6. /root/envd-xds/xds/cds.yaml 에서 클러스터의 name 줄을 지워 일부러 잘못된 갱신을 보내세요(이름은 스키마가 필수로 요구합니다). 잠시 뒤 /root/envd-xds/06-rejected.txtstill_serving=(/ 요청이 여전히 200 이면 yes), update_rejected=(cluster_manager.cds.update_rejected 통계 값), endpoints=(/clusters 에 남아 있는 pool 엔드포인트 수) 세 줄을 적으세요.
  7. /root/envd-xds/07-stats.txtcds_success=·cds_rejected=·lds_success=·cds_reload= 네 줄을 적으세요 — 각각 cluster_manager.cds.update_success, cluster_manager.cds.update_rejected, listener_manager.lds.update_success, cluster_manager.cds.config_reload 통계의 값입니다.
  8. /root/envd-xds/08-report.mdstatic_needs_restart=(1단계 결과가 '안 바뀐다' 면 yes), reload_count=(7단계의 cds_reload), rejected_kept_old=(6단계에서 거절 뒤에도 트래픽이 흘렀으면 yes), endpoints_after=(3단계 뒤 남은 엔드포인트 수) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

참고

정적 설정은 파일을 고쳐도 바뀌지 않는다

업스트림 둘을 ok 로 띄우세요(8096·8097). /root/envd-xds/static.yaml/static=v1 을 돌려주는 정적 설정을 두고 띄운 뒤(관리 9977, 리스너 127.0.0.1:10077), 띄운 채로 그 파일의 v1v2 로 고치세요. 고치기 전과 후에 각각 요청해 /root/envd-xds/01-static.txtbefore=·after_edit= 두 줄을 적으세요(다시 띄우지 않습니다).

static_resources 는 프로세스가 뜰 때 한 번 읽힙니다. 그래서 파일을 고쳐도 다시 띄우기 전에는 아무 일도 일어나지 않습니다. 이것이 프록시 설정을 바꾸려면 재시작이 필요하다는 말의 정체이고, 서비스 메시가 풀려고 한 문제이기도 합니다. 고치는 데는 sed -i 가 편합니다. 다시 띄우지 않도록 조심하세요 — 이 단계의 요점은 '안 바뀐다' 를 보이는 것입니다.

설정을 밖에서 받아 오게 바꾼다

/root/envd-xds/dyn.yaml 을 만드세요 — static_resources 없이 node(id envd-xds-1, cluster envd-xds)와 dynamic_resources 만 두고, lds_config/root/envd-xds/xds/lds.yaml 을, cds_config/root/envd-xds/xds/cds.yamlpath_config_source 로 구독합니다. 두 자원 파일도 만드세요 — 리스너는 /markerlds=v1 을 돌려주고 나머지는 클러스터 pool 로 보내며, 클러스터 pool 의 엔드포인트는 8096·8097 둘입니다. 띄운 뒤 /marker/ 를 요청해 /root/envd-xds/02-subscribe.txtmarker=·upstream_ok=(/ 요청이 200 이면 yes) 두 줄을 적으세요.

부트스트랩에 static_resources 가 아예 없어도 됩니다. 그때 프록시는 뜨자마자 구독부터 하고, 자원이 도착해야 실제로 듣기 시작합니다. 자원 파일의 모양은 resources: 아래에 "@type" 을 밝힌 목록입니다 — 리스너면 Listener, 클러스터면 Cluster 타입입니다. 진짜 컨트롤 플레인은 이것을 gRPC 로 보내는데, 보내는 내용의 모양은 파일이든 gRPC 든 같습니다.

파일을 고치면 다시 띄우지 않아도 반영된다

/root/envd-xds/xds/cds.yaml 에서 엔드포인트 8097빼세요(엔드포인트 하나만 남깁니다). 프록시는 그대로 둔 채 반영될 때까지 기다린 뒤 여덟 번 요청해 /root/envd-xds/03-reload.txtp8096=·p8097=·total= 세 줄을 적으세요.

파일 구독 방식은 파일이 바뀐 것을 알아채고 다시 읽습니다. 곧바로는 아니고 몇 초가 걸릴 수 있으니 고정 sleep 대신 원하는 결과가 나올 때까지 도는 루프를 쓰세요. 이것이 서비스 메시에서 파드가 뜨고 질 때마다 일어나는 일입니다 — 프록시를 재시작하지 않고 엔드포인트 목록만 갈아 끼웁니다. 반영 여부는 /clusters 로도 볼 수 있습니다. 주의: 파일을 제자리에서 덮어쓰면 갱신이 오지 않습니다. 새 파일로 쓴 뒤 mv 로 갈아 끼우세요.

동적으로 받은 설정은 덤프의 다른 자리에 있다

/config_dump 에서 클러스터 절만 뽑아 /root/envd-xds/04-dump.json 에 저장하고, /root/envd-xds/04-dump.txtstatic_clusters=(정적 클러스터 수), dynamic_clusters=(동적 클러스터 수), dynamic_name=(동적 클러스터의 이름) 세 줄을 적으세요.

/config_dump 의 클러스터 절에는 static_clustersdynamic_active_clusters따로 있습니다. 부트스트랩에 적은 것과 밖에서 받아 온 것을 구분해야 하기 때문입니다. 운영에서 '내가 보낸 설정이 도착했나' 를 확인할 때 보는 자리가 바로 이 동적 쪽입니다. 정적 쪽이 비어 있고 동적 쪽에 들어 있으면 구독이 제대로 도는 것입니다. ?resource= 로 절을 골라 받을 수도 있지만, 이 단계에서는 전체를 받아 jq 로 두 자리를 함께 세는 편이 쉽습니다.

리스너도 무중단으로 갈아 끼운다

/root/envd-xds/xds/lds.yaml/marker 응답을 lds=v1 에서 lds=v2 로 고치세요. 프록시는 그대로 둔 채 반영될 때까지 기다린 뒤 /root/envd-xds/05-lds.txtmarker_after=(/marker 응답)와 upstream_still_ok=(/ 요청이 여전히 200 이면 yes) 두 줄을 적으세요.

리스너를 갈아 끼우는 일은 클러스터보다 무겁습니다 — 소켓을 다시 여는 일이기 때문입니다. Envoy 는 새 리스너를 준비한 뒤 옛 리스너를 비우면서 바꿔 끼우기 때문에 그 사이에도 요청이 끊기지 않습니다. 그래서 라우트 규칙 하나를 고치려고 배포를 하지 않아도 되는 것입니다. 클러스터 쪽 설정은 건드리지 않았으니 업스트림으로 가는 경로는 그대로여야 합니다 — 그것도 함께 확인하세요.

잘못된 갱신은 거절되고 옛 설정이 남는다

/root/envd-xds/xds/cds.yaml 에서 클러스터의 name 줄을 지워 일부러 잘못된 갱신을 보내세요(이름은 스키마가 필수로 요구합니다). 잠시 뒤 /root/envd-xds/06-rejected.txtstill_serving=(/ 요청이 여전히 200 이면 yes), update_rejected=(cluster_manager.cds.update_rejected 통계 값), endpoints=(/clusters 에 남아 있는 pool 엔드포인트 수) 세 줄을 적으세요.

이것이 xDS 에서 가장 중요한 성질입니다. 갱신이 잘못되면 프록시는 그것을 거절하고 마지막으로 성공한 설정을 그대로 씁니다. 컨트롤 플레인이 망가진 설정을 보내도 트래픽은 계속 흐릅니다. 그래서 '컨트롤 플레인이 죽으면 메시가 죽는다' 는 말은 정확하지 않습니다 — 새 설정을 받지 못할 뿐 지금 설정으로는 계속 돕니다. 대신 아무도 알려 주지 않으므로 거절 통계와 버전을 감시해야 합니다. 통계 이름에 update_rejected 가 들어갑니다. 주의할 점 하나 — 모든 잘못이 거절되는 것은 아닙니다. 모르는 열거값처럼 기본 검증기가 경고만 내고 넘어가는 것도 있습니다. 필수 필드가 빠진 것처럼 스키마가 못 받아들이는 것만 거절로 잡힙니다.

갱신이 도착했는지는 숫자로 본다

/root/envd-xds/07-stats.txtcds_success=·cds_rejected=·lds_success=·cds_reload= 네 줄을 적으세요 — 각각 cluster_manager.cds.update_success, cluster_manager.cds.update_rejected, listener_manager.lds.update_success, cluster_manager.cds.config_reload 통계의 값입니다.

컨트롤 플레인이 있는 환경에서 가장 먼저 보는 숫자가 이것입니다. update_success 가 멈춰 있으면 설정이 오지 않는 것이고, update_rejected 가 올라가면 오긴 오는데 프록시가 받아들이지 못하는 것입니다. 둘은 전혀 다른 문제라 고칠 곳도 다릅니다 — 앞은 연결이나 구독 문제이고, 뒤는 보내는 쪽이 만든 설정의 문제입니다. 이 실습에서는 앞 단계에서 일부러 거절을 만들어 두었으니 그 숫자가 보일 것입니다.

동적 설정 운영 메모를 남긴다

/root/envd-xds/08-report.mdstatic_needs_restart=(1단계 결과가 '안 바뀐다' 면 yes), reload_count=(7단계의 cds_reload), rejected_kept_old=(6단계에서 거절 뒤에도 트래픽이 흘렀으면 yes), endpoints_after=(3단계 뒤 남은 엔드포인트 수) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

설명 줄에는 '컨트롤 플레인이 죽으면 무엇이 멈추고 무엇이 계속되는가' 를 자기 말로 적어 두세요. 이 한 문장이 메시 장애 대응에서 가장 자주 쓰입니다. 값은 앞 단계 파일에서 가져오세요.