LabHub
学习 学习路径 课程

Envoy 内部结构

拆开四个虚拟主机与五种匹配

在 LabHub 中继续学习

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

목표

Host 헤더로 가상 호스트가 먼저 정해지는 것을 확인하고, 매치의 종류와 목적지의 종류를 하나씩 붙여 가며 같은 요청이 어디로 가는지 본다.

왜 중요한가

라우팅 설정은 사람이 읽기에는 '규칙 목록' 처럼 보이지만 실제로는 두 단계의 선택이다. 그리고 두 단계가 서로 다른 규칙으로 돌아간다 — 앞은 구체성, 뒤는 순서. 이 차이를 모르면 규칙을 위아래로 옮기며 시간을 버리게 된다. 여기에 더해 weighted_clusters 가 보장이 아니라 확률이라는 것, direct_response 로 앱을 건드리지 않고 끝낼 수 있다는 것, 헤더가 세 층에서 각각 더해진다는 것까지 손으로 확인해 두면 남이 쓴 라우트 표도 몇 분이면 읽힌다.

단계

  1. /root/envd-route/route.yaml 에 가상 호스트 네 개를 두세요 — exact(shop.envd.test), suffix(*.envd.test), prefixw(shop.*), anyhost(*). 각각 / 에서 vh=exact·vh=suffix·vh=prefixw·vh=any 를 돌려줍니다(관리 9931, 리스너 127.0.0.1:10031). 띄운 뒤 네 가지 Host 헤더로 /zzz 를 요청해 /root/envd-route/01-vhosts.txtexact=·suffix=·prefixw=·any= 네 줄로 결과를 적으세요(요청한 Host 는 차례로 shop.envd.test, www.envd.test, shop.other.test, nowhere.example 입니다).
  2. /root/envd-route/route.yaml/root/envd-route/route-dup.yaml 로 복사한 뒤 domains: ["*"] 인 가상 호스트를 하나 더 만드세요(이름은 겹치지 않게). envoy --mode validate 로 검사해 출력과 종료 코드를 /root/envd-route/02-onestar.txt 에 저장하세요(마지막 줄은 rc=1).
  3. exact 가상 호스트의 / 라우트 위에 두 라우트를 더하세요 — path: "/exact"m=path 를, safe_regex^/id/[0-9]+$m=regex 를 돌려줍니다. /root/envd-route/03-match.txtpath=(/exact 요청), regex=(/id/42 요청), regex_miss=(/id/abc 요청) 세 줄을 적으세요. Host 는 모두 shop.envd.test 입니다.
  4. /api 로 가는 라우트를 두 개 더하세요(정규식 라우트 아래, / 라우트 위). 하나는 headers 조건으로 x-canary: yes 일 때 m=header 를, 다른 하나는 query_parameters 조건으로 debug=1 일 때 m=query 를 돌려줍니다. /root/envd-route/04-cond.txtheader=, query=, plain=(조건 없이 /api 만) 세 줄을 적으세요.
  5. 업스트림 둘을 띄우고(8082, 8083) 클러스터 blue·green 을 만든 뒤, /split 라우트가 weighted_clusters 로 75 대 25 로 나눠 보내게 하세요. --concurrency 1 로 띄우고 40번 요청해 /root/envd-route/05-weighted.txtblue=·green=·total= 세 줄(각 업스트림이 받은 횟수와 합)을 적으세요.
  6. 두 라우트를 더하세요 — path: "/healthz"direct_response 로 200 과 alive 를, prefix: "/old"redirect/new 로 301 을 보냅니다. /root/envd-route/06-direct.txthealth=(응답 본문), redirect_code=(HTTP 코드), redirect_url=(Location 헤더 값) 세 줄을 적으세요.
  7. 응답 헤더 x-level세 층에 각각 더하세요 — 라우트에 route, 가상 호스트에 virtualhost, 라우트 표에 routeconfig. shop.envd.test/zzz 로 요청해 돌아온 x-level 헤더를 모두 /root/envd-route/07-headers.txtlevels= 한 줄로 쉼표 없이 공백으로 이어 적고, count= 줄에 개수를 적으세요.
  8. /root/envd-route/08-report.mdvhost_order=(가상 호스트 구체성 순서를 exact,suffix,prefix,star 형식으로), star_limit=(라우트 표 하나에 둘 수 있는 * 가상 호스트 수), blue_share=(5단계에서 blue 가 받은 비율, 백분율 정수), redirect_code=(6단계의 코드) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

참고

라우트 표보다 먼저 가상 호스트가 정해진다

/root/envd-route/route.yaml 에 가상 호스트 네 개를 두세요 — exact(shop.envd.test), suffix(*.envd.test), prefixw(shop.*), anyhost(*). 각각 / 에서 vh=exact·vh=suffix·vh=prefixw·vh=any 를 돌려줍니다(관리 9931, 리스너 127.0.0.1:10031). 띄운 뒤 네 가지 Host 헤더로 /zzz 를 요청해 /root/envd-route/01-vhosts.txtexact=·suffix=·prefixw=·any= 네 줄로 결과를 적으세요(요청한 Host 는 차례로 shop.envd.test, www.envd.test, shop.other.test, nowhere.example 입니다).

라우트 표를 보기 전에 Host(또는 :authority) 헤더로 가상 호스트가 먼저 정해집니다. 그래서 '라우트를 분명히 썼는데 안 걸린다' 의 절반은 가상 호스트를 잘못 고른 것입니다. 와일드카드는 접미(*.foo.com)와 접두(foo.*) 두 가지가 있고, 어느 것도 빈 문자열에는 맞지 않습니다. 요청은 curl -H "Host: 이름" http://127.0.0.1:포트/zzz 로 보냅니다.

* 는 라우트 표 전체에 하나만 둘 수 있다

/root/envd-route/route.yaml/root/envd-route/route-dup.yaml 로 복사한 뒤 domains: ["*"] 인 가상 호스트를 하나 더 만드세요(이름은 겹치지 않게). envoy --mode validate 로 검사해 출력과 종료 코드를 /root/envd-route/02-onestar.txt 에 저장하세요(마지막 줄은 rc=1).

가상 호스트 선택은 '가장 구체적인 것이 이긴다' 로 돌아가는데, * 가 두 개면 누가 더 구체적인지 정할 방법이 없습니다. 그래서 Envoy 는 이 상태를 실행 중에 헷갈리게 두지 않고 설정을 읽는 시점에 거절합니다. 거절 메시지에 라우트 표의 이름이 함께 나오니 그 줄을 그대로 옮겨 두세요.

정확한 경로와 정규식으로 맞힌다

exact 가상 호스트의 / 라우트 위에 두 라우트를 더하세요 — path: "/exact"m=path 를, safe_regex^/id/[0-9]+$m=regex 를 돌려줍니다. /root/envd-route/03-match.txtpath=(/exact 요청), regex=(/id/42 요청), regex_miss=(/id/abc 요청) 세 줄을 적으세요. Host 는 모두 shop.envd.test 입니다.

매치의 종류는 셋입니다 — prefix(앞이 같으면), path(통째로 같아야), safe_regex(정규식). 앞의 둘이 빠르므로 정규식은 꼭 필요할 때만 씁니다. 정규식이 맞지 않으면 그 라우트를 건너뛰고 아래로 계속 내려갑니다 — 404 가 아니라 다음 라우트가 받는다는 점이 중요합니다. safe_regex{ regex: "..." } 형태로 씁니다.

같은 경로를 헤더와 질의 문자열로 가른다

/api 로 가는 라우트를 두 개 더하세요(정규식 라우트 아래, / 라우트 위). 하나는 headers 조건으로 x-canary: yes 일 때 m=header 를, 다른 하나는 query_parameters 조건으로 debug=1 일 때 m=query 를 돌려줍니다. /root/envd-route/04-cond.txtheader=, query=, plain=(조건 없이 /api 만) 세 줄을 적으세요.

경로만으로는 가를 수 없는 요구가 실무에는 많습니다 — 내부 테스터만 새 버전으로, 디버그 질의가 붙은 요청만 다른 백엔드로. 그래서 매치에는 경로 말고도 headersquery_parameters 조건을 함께 걸 수 있고, 한 매치 안의 조건은 모두 만족해야 합니다. 두 라우트가 같은 prefix 를 쓰면 위에 있는 것이 먼저 판정되니, 조건이 붙은 쪽을 위에 둡니다.

한 라우트가 두 클러스터로 나눠 보낸다

업스트림 둘을 띄우고(8082, 8083) 클러스터 blue·green 을 만든 뒤, /split 라우트가 weighted_clusters 로 75 대 25 로 나눠 보내게 하세요. --concurrency 1 로 띄우고 40번 요청해 /root/envd-route/05-weighted.txtblue=·green=·total= 세 줄(각 업스트림이 받은 횟수와 합)을 적으세요.

가중치는 비율이지 보장이 아닙니다. 그리고 워커 스레드마다 상태가 따로 놀기 때문에 기본 concurrency(코어 수)로 세면 숫자가 매번 달라집니다 — 그래서 이 단계는 워커 하나로 띄워야 합니다. 업스트림은 python3 /opt/lab/envoy/upstream.py <포트> ok 로 띄우고, 응답 본문에 포트가 들어 있으니 sort | uniq -c 로 세면 됩니다.

업스트림 없이 응답하고, 옛 경로를 옮겨 준다

두 라우트를 더하세요 — path: "/healthz"direct_response 로 200 과 alive 를, prefix: "/old"redirect/new 로 301 을 보냅니다. /root/envd-route/06-direct.txthealth=(응답 본문), redirect_code=(HTTP 코드), redirect_url=(Location 헤더 값) 세 줄을 적으세요.

direct_response 는 업스트림에 가지 않고 Envoy 가 직접 답합니다 — 헬스 체크 경로나 점검 안내 페이지를 앱에 넣지 않고 프록시에서 끝낼 수 있습니다. redirect 는 302 가 기본이라 영구 이전이면 response_code: MOVED_PERMANENTLY 를 적어야 합니다. curl -o /dev/null -w '%{http_code} %{redirect_url}' 로 두 값을 한 번에 받을 수 있습니다.

헤더를 붙이는 자리가 세 군데다

응답 헤더 x-level세 층에 각각 더하세요 — 라우트에 route, 가상 호스트에 virtualhost, 라우트 표에 routeconfig. shop.envd.test/zzz 로 요청해 돌아온 x-level 헤더를 모두 /root/envd-route/07-headers.txtlevels= 한 줄로 쉼표 없이 공백으로 이어 적고, count= 줄에 개수를 적으세요.

헤더 조작은 라우트 · 가상 호스트 · 라우트 표 세 곳에 쓸 수 있습니다. 덮어쓰는 것이 아니라 각각 더해집니다 — 그래서 같은 이름의 헤더가 여러 개 남을 수 있고, 클라이언트는 그걸 쉼표로 이은 하나로 보기도 합니다. 적용은 안쪽(라우트)부터 바깥쪽(라우트 표) 순입니다. curl -sI 로 헤더만 받아 grep -i x-level 하세요.

라우팅 표를 읽는 규칙으로 정리한다

/root/envd-route/08-report.mdvhost_order=(가상 호스트 구체성 순서를 exact,suffix,prefix,star 형식으로), star_limit=(라우트 표 하나에 둘 수 있는 * 가상 호스트 수), blue_share=(5단계에서 blue 가 받은 비율, 백분율 정수), redirect_code=(6단계의 코드) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

비율은 직접 계산하세요 — 40번 중 몇 번이었는지에서 나옵니다. 설명 줄에는 '가중치는 비율이지 보장이 아니다' 처럼 다음에 자기를 구해 줄 문장을 적으세요.