LabHub
배우기 러닝패스 코스

Envoy Internals

Same Upstreams, Change Only the Policy

LabHub 에서 이어서 보기

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

목표

부하 분산 방식을 하나씩 바꿔 가며 실제 분배를 세고, 해시 기반 방식의 두 가지 성질(같은 열쇠는 같은 곳으로 · 한 대를 빼도 그 대의 몫만 옮김)을 직접 확인한다.

왜 중요한가

부하 분산은 설정 한 줄이지만 그 한 줄이 캐시 적중률과 세션 유지, 그리고 서버를 뺄 때의 충격 크기를 모두 정한다. 문서로 읽으면 다 그럴듯해 보이는데, 숫자를 직접 세어 보면 '무작위는 작은 표본에서 이만큼 치우치는구나' 와 '한 대를 빼도 이 정도만 옮기는구나' 가 몸에 남는다. 특히 열쇠를 뽑지 못한 요청이 무작위로 흘러간다는 사실은 세션 고정 장애의 단골 원인인데, 설정만 봐서는 보이지 않는다.

단계

  1. 업스트림 셋을 ok 로 띄우세요(8088·8089·8090). /root/envd-lb/lb-rr.yamllb_policy: ROUND_ROBINSTATIC 클러스터 pool/ 라우트를 두고 --concurrency 1 로 띄우세요(관리 9951, 리스너 127.0.0.1:10051). 9번 요청해 /root/envd-lb/01-rr.txtp8088=·p8089=·p8090=·total= 네 줄을 적으세요.
  2. /root/envd-lb/lb-rr.yaml/root/envd-lb/lb-weight.yaml 로 복사하고 엔드포인트마다 load_balancing_weight 를 주세요 — 8088 은 2, 나머지 둘은 1 입니다. 그 설정으로 다시 띄워 12번 요청하고 /root/envd-lb/02-weight.txtp8088=·p8089=·p8090=·total= 네 줄을 적으세요.
  3. /root/envd-lb/lb-random.yaml 을 만드세요 — 가중치는 두지 않고 lb_policyRANDOM 으로 바꾼 설정입니다. 그 설정으로 띄워 40번 요청해 /root/envd-lb/03-random.txtp8088=·p8089=·p8090=·total=·max_gap= 다섯 줄을 적으세요(max_gap 은 가장 많이 받은 수에서 가장 적게 받은 수를 뺀 값).
  4. /root/envd-lb/lb-ring.yaml 을 만드세요 — lb_policyRING_HASH, ring_hash_lb_config.minimum_ring_size 는 1024, 라우트의 hash_policy 는 헤더 x-user 입니다. 그 설정으로 띄운 뒤 u1 부터 u9 까지 아홉 사용자로 각각 한 번씩 요청해 /root/envd-lb/04-map.txt사용자 포트 아홉 줄을 적으세요.
  5. 사용자 u1 로 네 번, u2 로 네 번 요청해 /root/envd-lb/05-sticky.txtu1_ports=(받은 포트 네 개를 공백으로), u1_distinct=(서로 다른 포트 수), u2_distinct= 세 줄을 적으세요.
  6. /root/envd-lb/lb-ring2.yaml 을 만드세요 — 4단계 설정에서 엔드포인트 8090 만 뺀 것입니다. 그 설정으로 다시 띄운 뒤 같은 아홉 사용자로 다시 요청해 /root/envd-lb/06-remap.txt사용자 포트 아홉 줄을 적으세요. 그다음 4단계의 표와 견주어 /root/envd-lb/06-moved.txtmoved=(자리가 바뀐 사용자 수), stayed=, total=9 세 줄을 적으세요.
  7. /root/envd-lb/lb-query.yaml 을 만드세요 — 엔드포인트는 다시 셋이고, hash_policy 는 헤더 대신 질의 파라미터 uid 입니다. 그 설정으로 띄운 뒤 ?uid=u1 로 세 번, ?uid=u2 로 세 번 요청해 /root/envd-lb/07-key.txtu1_distinct=, u2_distinct=, header_ignored=(헤더 x-user: u1 만 붙이고 질의는 없이 세 번 요청했을 때 서로 다른 포트 수) 세 줄을 적으세요.
  8. /root/envd-lb/08-report.mdeven_spread=(1단계의 세 값을 쉼표로), heavy_share=(2단계에서 가중치 2 를 준 엔드포인트가 받은 비율, 백분율 정수), sticky=(5단계에서 같은 사용자가 한 곳에 붙었으면 yes), moved_users=(6단계 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

참고

고르게 나누는 것이 기본값이다

업스트림 셋을 ok 로 띄우세요(8088·8089·8090). /root/envd-lb/lb-rr.yamllb_policy: ROUND_ROBINSTATIC 클러스터 pool/ 라우트를 두고 --concurrency 1 로 띄우세요(관리 9951, 리스너 127.0.0.1:10051). 9번 요청해 /root/envd-lb/01-rr.txtp8088=·p8089=·p8090=·total= 네 줄을 적으세요.

라운드 로빈은 목록을 돌아가며 하나씩 고릅니다. 기본값이고, 서버 체급이 같고 요청 비용이 비슷할 때 가장 예측하기 쉽습니다. 워커 스레드마다 자기 차례를 따로 기억하므로 기본 concurrency(코어 수)로 세면 9번으로는 3 대 3 대 3 이 나오지 않습니다. 이 실습에서 워커를 하나로 묶는 이유가 그것입니다.

체급이 다른 서버를 한 묶음에 넣는다

/root/envd-lb/lb-rr.yaml/root/envd-lb/lb-weight.yaml 로 복사하고 엔드포인트마다 load_balancing_weight 를 주세요 — 8088 은 2, 나머지 둘은 1 입니다. 그 설정으로 다시 띄워 12번 요청하고 /root/envd-lb/02-weight.txtp8088=·p8089=·p8090=·total= 네 줄을 적으세요.

서버를 늘릴 때 늘 같은 체급으로만 늘어나지는 않습니다. 새로 산 장비가 두 배 빠르면 두 배를 받게 하는 편이 낫고, 그때 쓰는 것이 엔드포인트 가중치입니다. 라운드 로빈은 가중치를 반영해 도는데, 가중치 2 는 '두 번에 한 번씩 더' 가 아니라 '전체에서 차지하는 몫이 두 배' 라는 뜻입니다. 합이 4 이므로 12번이면 6 대 3 대 3 이 됩니다.

고르게 보이지만 고르지 않다

/root/envd-lb/lb-random.yaml 을 만드세요 — 가중치는 두지 않고 lb_policyRANDOM 으로 바꾼 설정입니다. 그 설정으로 띄워 40번 요청해 /root/envd-lb/03-random.txtp8088=·p8089=·p8090=·total=·max_gap= 다섯 줄을 적으세요(max_gap 은 가장 많이 받은 수에서 가장 적게 받은 수를 뺀 값).

무작위는 상태를 하나도 기억하지 않는 방식입니다. 그래서 워커가 몇 개든 결과가 같고, 엔드포인트가 들고 날 때 재계산할 것도 없습니다. 대신 표본이 작으면 눈에 띄게 치우칩니다 — 라운드 로빈이 9번에 정확히 3 대 3 대 3 이던 것과 비교해 보세요. 요청이 초당 수천 건인 곳에서는 이 차이가 사라지므로, 규모가 큰 곳에서 기본으로 쓰기도 합니다.

같은 사용자는 늘 같은 서버로

/root/envd-lb/lb-ring.yaml 을 만드세요 — lb_policyRING_HASH, ring_hash_lb_config.minimum_ring_size 는 1024, 라우트의 hash_policy 는 헤더 x-user 입니다. 그 설정으로 띄운 뒤 u1 부터 u9 까지 아홉 사용자로 각각 한 번씩 요청해 /root/envd-lb/04-map.txt사용자 포트 아홉 줄을 적으세요.

해시 기반 방식은 요청에서 뽑은 열쇠를 해시해 링 위의 자리를 찾고, 그 자리에서 시계 방향으로 가장 가까운 엔드포인트를 고릅니다. 같은 열쇠는 늘 같은 자리로 가므로 같은 사용자가 같은 서버에 붙습니다 — 로컬 캐시 적중률이 올라가고, 서버가 세션을 들고 있어도 됩니다. 열쇠는 hash_policy 가 고릅니다(헤더·쿠키·질의 파라미터·출발지 IP). minimum_ring_size 가 작으면 링이 성겨 분포가 치우칩니다.

같은 열쇠를 네 번 보내도 자리가 안 바뀐다

사용자 u1 로 네 번, u2 로 네 번 요청해 /root/envd-lb/05-sticky.txtu1_ports=(받은 포트 네 개를 공백으로), u1_distinct=(서로 다른 포트 수), u2_distinct= 세 줄을 적으세요.

이 성질이 없으면 로컬 캐시가 있는 서비스에서 같은 사용자의 요청이 매번 다른 서버로 가 캐시가 거의 맞지 않습니다. 반대로 이 성질에 기대면 특정 사용자 하나가 서버 한 대를 혼자 무겁게 만들 수 있다는 위험도 함께 생깁니다 — 열쇠를 무엇으로 고르느냐가 그래서 중요합니다. 서로 다른 값의 개수는 sort -u | wc -l 로 셉니다.

서버 한 대를 빼면 몇 명이 자리를 옮기나

/root/envd-lb/lb-ring2.yaml 을 만드세요 — 4단계 설정에서 엔드포인트 8090 만 뺀 것입니다. 그 설정으로 다시 띄운 뒤 같은 아홉 사용자로 다시 요청해 /root/envd-lb/06-remap.txt사용자 포트 아홉 줄을 적으세요. 그다음 4단계의 표와 견주어 /root/envd-lb/06-moved.txtmoved=(자리가 바뀐 사용자 수), stayed=, total=9 세 줄을 적으세요.

이것이 해시 링을 쓰는 진짜 이유입니다. 단순히 '해시 값을 서버 수로 나눈 나머지' 를 쓰면 서버 수가 바뀌는 순간 거의 모두가 자리를 옮깁니다. 링 방식은 사라진 서버에 걸려 있던 열쇠만 옮겨 가고 나머지는 그대로 있습니다. 옮긴 사람이 몇 명인지 직접 세어 보세요 — join 이나 paste 로 두 표를 나란히 놓고 비교하면 편합니다.

열쇠를 헤더에서 질의 문자열로 바꾼다

/root/envd-lb/lb-query.yaml 을 만드세요 — 엔드포인트는 다시 셋이고, hash_policy 는 헤더 대신 질의 파라미터 uid 입니다. 그 설정으로 띄운 뒤 ?uid=u1 로 세 번, ?uid=u2 로 세 번 요청해 /root/envd-lb/07-key.txtu1_distinct=, u2_distinct=, header_ignored=(헤더 x-user: u1 만 붙이고 질의는 없이 세 번 요청했을 때 서로 다른 포트 수) 세 줄을 적으세요.

열쇠를 무엇에서 뽑느냐가 곧 '무엇을 같은 것으로 볼 것인가' 입니다. 사용자 세션이면 쿠키, 테넌트면 헤더, 캐시 키면 질의 파라미터가 자연스럽습니다. 열쇠를 못 뽑으면 해시가 없으므로 Envoy 는 그 요청을 무작위로 보냅니다 — 그래서 열쇠가 빠진 요청은 고정되지 않습니다. 마지막 줄은 그것을 확인하는 것입니다.

방식별 성질 표로 정리한다

/root/envd-lb/08-report.mdeven_spread=(1단계의 세 값을 쉼표로), heavy_share=(2단계에서 가중치 2 를 준 엔드포인트가 받은 비율, 백분율 정수), sticky=(5단계에서 같은 사용자가 한 곳에 붙었으면 yes), moved_users=(6단계 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.

표를 만드는 목적은 '어느 방식을 언제 쓰는가' 를 다음에 자기가 고를 수 있게 하는 것입니다. 값은 앞 단계 파일에서 가져오고, 설명 줄에는 각 방식이 무엇을 포기하고 무엇을 얻는지를 적으세요.