同一组上游,只换算法来数
한국어 원문으로 표시합니다.
목표
부하 분산 방식을 하나씩 바꿔 가며 실제 분배를 세고, 해시 기반 방식의 두 가지 성질(같은 열쇠는 같은 곳으로 · 한 대를 빼도 그 대의 몫만 옮김)을 직접 확인한다.
왜 중요한가
부하 분산은 설정 한 줄이지만 그 한 줄이 캐시 적중률과 세션 유지, 그리고 서버를 뺄 때의 충격 크기를 모두 정한다. 문서로 읽으면 다 그럴듯해 보이는데, 숫자를 직접 세어 보면 '무작위는 작은 표본에서 이만큼 치우치는구나' 와 '한 대를 빼도 이 정도만 옮기는구나' 가 몸에 남는다. 특히 열쇠를 뽑지 못한 요청이 무작위로 흘러간다는 사실은 세션 고정 장애의 단골 원인인데, 설정만 봐서는 보이지 않는다.
단계
- 업스트림 셋을
ok로 띄우세요(8088·8089·8090)./root/envd-lb/lb-rr.yaml에lb_policy: ROUND_ROBIN인STATIC클러스터pool과/라우트를 두고--concurrency 1로 띄우세요(관리 9951, 리스너127.0.0.1:10051). 9번 요청해/root/envd-lb/01-rr.txt에p8088=·p8089=·p8090=·total=네 줄을 적으세요. /root/envd-lb/lb-rr.yaml을/root/envd-lb/lb-weight.yaml로 복사하고 엔드포인트마다load_balancing_weight를 주세요 —8088은 2, 나머지 둘은 1 입니다. 그 설정으로 다시 띄워 12번 요청하고/root/envd-lb/02-weight.txt에p8088=·p8089=·p8090=·total=네 줄을 적으세요./root/envd-lb/lb-random.yaml을 만드세요 — 가중치는 두지 않고lb_policy만RANDOM으로 바꾼 설정입니다. 그 설정으로 띄워 40번 요청해/root/envd-lb/03-random.txt에p8088=·p8089=·p8090=·total=·max_gap=다섯 줄을 적으세요(max_gap은 가장 많이 받은 수에서 가장 적게 받은 수를 뺀 값)./root/envd-lb/lb-ring.yaml을 만드세요 —lb_policy는RING_HASH,ring_hash_lb_config.minimum_ring_size는 1024, 라우트의hash_policy는 헤더x-user입니다. 그 설정으로 띄운 뒤u1부터u9까지 아홉 사용자로 각각 한 번씩 요청해/root/envd-lb/04-map.txt에사용자 포트아홉 줄을 적으세요.- 사용자
u1로 네 번,u2로 네 번 요청해/root/envd-lb/05-sticky.txt에u1_ports=(받은 포트 네 개를 공백으로),u1_distinct=(서로 다른 포트 수),u2_distinct=세 줄을 적으세요. /root/envd-lb/lb-ring2.yaml을 만드세요 — 4단계 설정에서 엔드포인트8090만 뺀 것입니다. 그 설정으로 다시 띄운 뒤 같은 아홉 사용자로 다시 요청해/root/envd-lb/06-remap.txt에사용자 포트아홉 줄을 적으세요. 그다음 4단계의 표와 견주어/root/envd-lb/06-moved.txt에moved=(자리가 바뀐 사용자 수),stayed=,total=9세 줄을 적으세요./root/envd-lb/lb-query.yaml을 만드세요 — 엔드포인트는 다시 셋이고,hash_policy는 헤더 대신 질의 파라미터uid입니다. 그 설정으로 띄운 뒤?uid=u1로 세 번,?uid=u2로 세 번 요청해/root/envd-lb/07-key.txt에u1_distinct=,u2_distinct=,header_ignored=(헤더x-user: u1만 붙이고 질의는 없이 세 번 요청했을 때 서로 다른 포트 수) 세 줄을 적으세요./root/envd-lb/08-report.md에even_spread=(1단계의 세 값을 쉼표로),heavy_share=(2단계에서 가중치 2 를 준 엔드포인트가 받은 비율, 백분율 정수),sticky=(5단계에서 같은 사용자가 한 곳에 붙었으면 yes),moved_users=(6단계 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.
참고
- 분배를 세는 모든 단계는
--concurrency 1로 띄워야 합니다. 워커가 여럿이면 차례가 워커마다 따로 놀아 숫자가 매번 달라집니다. - 업스트림은
python3 /opt/lab/envoy/upstream.py <포트> ok로 띄웁니다. 응답 본문이ok:<포트> <경로>라 어느 쪽이 받았는지 셀 수 있습니다. - Envoy 를 다시 띄우기 전에는
pkill -x envoy로 정리하고, 기동은/ready가 LIVE 를 돌려줄 때까지 도는 루프로 기다리세요. - 서로 다른 값의 개수는
sort -u | wc -l로 셉니다. - 흔한 실수 —
hash_policy를 라우트가 아니라 클러스터에 적는 것. 열쇠는 요청에서 뽑으므로 라우트의route아래에 적습니다.
고르게 나누는 것이 기본값이다
업스트림 셋을 ok 로 띄우세요(8088·8089·8090). /root/envd-lb/lb-rr.yaml 에 lb_policy: ROUND_ROBIN 인 STATIC 클러스터 pool 과 / 라우트를 두고 --concurrency 1 로 띄우세요(관리 9951, 리스너 127.0.0.1:10051). 9번 요청해 /root/envd-lb/01-rr.txt 에 p8088=·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.txt 에 p8088=·p8089=·p8090=·total= 네 줄을 적으세요.
서버를 늘릴 때 늘 같은 체급으로만 늘어나지는 않습니다. 새로 산 장비가 두 배 빠르면 두 배를 받게 하는 편이 낫고, 그때 쓰는 것이 엔드포인트 가중치입니다. 라운드 로빈은 가중치를 반영해 도는데, 가중치 2 는 '두 번에 한 번씩 더' 가 아니라 '전체에서 차지하는 몫이 두 배' 라는 뜻입니다. 합이 4 이므로 12번이면 6 대 3 대 3 이 됩니다.
고르게 보이지만 고르지 않다
/root/envd-lb/lb-random.yaml 을 만드세요 — 가중치는 두지 않고 lb_policy 만 RANDOM 으로 바꾼 설정입니다. 그 설정으로 띄워 40번 요청해 /root/envd-lb/03-random.txt 에 p8088=·p8089=·p8090=·total=·max_gap= 다섯 줄을 적으세요(max_gap 은 가장 많이 받은 수에서 가장 적게 받은 수를 뺀 값).
무작위는 상태를 하나도 기억하지 않는 방식입니다. 그래서 워커가 몇 개든 결과가 같고, 엔드포인트가 들고 날 때 재계산할 것도 없습니다. 대신 표본이 작으면 눈에 띄게 치우칩니다 — 라운드 로빈이 9번에 정확히 3 대 3 대 3 이던 것과 비교해 보세요. 요청이 초당 수천 건인 곳에서는 이 차이가 사라지므로, 규모가 큰 곳에서 기본으로 쓰기도 합니다.
같은 사용자는 늘 같은 서버로
/root/envd-lb/lb-ring.yaml 을 만드세요 — lb_policy 는 RING_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.txt 에 u1_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.txt 에 moved=(자리가 바뀐 사용자 수), stayed=, total=9 세 줄을 적으세요.
이것이 해시 링을 쓰는 진짜 이유입니다. 단순히 '해시 값을 서버 수로 나눈 나머지' 를 쓰면 서버 수가 바뀌는 순간 거의 모두가 자리를 옮깁니다. 링 방식은 사라진 서버에 걸려 있던 열쇠만 옮겨 가고 나머지는 그대로 있습니다. 옮긴 사람이 몇 명인지 직접 세어 보세요 — join 이나 paste 로 두 표를 나란히 놓고 비교하면 편합니다.
열쇠를 헤더에서 질의 문자열로 바꾼다
/root/envd-lb/lb-query.yaml 을 만드세요 — 엔드포인트는 다시 셋이고, hash_policy 는 헤더 대신 질의 파라미터 uid 입니다. 그 설정으로 띄운 뒤 ?uid=u1 로 세 번, ?uid=u2 로 세 번 요청해 /root/envd-lb/07-key.txt 에 u1_distinct=, u2_distinct=, header_ignored=(헤더 x-user: u1 만 붙이고 질의는 없이 세 번 요청했을 때 서로 다른 포트 수) 세 줄을 적으세요.
열쇠를 무엇에서 뽑느냐가 곧 '무엇을 같은 것으로 볼 것인가' 입니다. 사용자 세션이면 쿠키, 테넌트면 헤더, 캐시 키면 질의 파라미터가 자연스럽습니다. 열쇠를 못 뽑으면 해시가 없으므로 Envoy 는 그 요청을 무작위로 보냅니다 — 그래서 열쇠가 빠진 요청은 고정되지 않습니다. 마지막 줄은 그것을 확인하는 것입니다.
방식별 성질 표로 정리한다
/root/envd-lb/08-report.md 에 even_spread=(1단계의 세 값을 쉼표로), heavy_share=(2단계에서 가중치 2 를 준 엔드포인트가 받은 비율, 백분율 정수), sticky=(5단계에서 같은 사용자가 한 곳에 붙었으면 yes), moved_users=(6단계 값) 네 줄을 적고, 그 아래 배운 것을 네 줄 이상 적으세요.
표를 만드는 목적은 '어느 방식을 언제 쓰는가' 를 다음에 자기가 고를 수 있게 하는 것입니다. 값은 앞 단계 파일에서 가져오고, 설명 줄에는 각 방식이 무엇을 포기하고 무엇을 얻는지를 적으세요.