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번 중 몇 번이었는지에서 나옵니다. 설명 줄에는 '가중치는 비율이지 보장이 아니다' 처럼 다음에 자기를 구해 줄 문장을 적으세요.