수집 대상을 여덟에서 스물로 늘리기로 했다. 지금 장비로 되는지 물었을 때 답을 못 했다.
Table of contents
Open Table of contents
사전 준비 — 대상당 자원을 쟀다
늘리기 전에 지금 도는 것부터 쟀다.
$ ps -o pid,rss,pcpu,args -C collector
PID RSS %CPU ARGS
2841 184320 6.2 collector --target=a
2847 179104 5.8 collector --target=b
...
대상 하나에 collector 프로세스 하나가 뜨고 RSS 가 180MB쯤 %CPU 가 6쯤이다.
8 대상 → 메모리 1.4GB, CPU 48%
20 대상 → 메모리 3.6GB, CPU 120%
코어가 둘이라 상한이 200%이니 20 대상 의 120%는 아직 여유가 있어 보인다.
다만 이 숫자는 %CPU 의 평균이라 평균으로 계산하면 충분해 보이는 것이 실제로는 모자라는 경우가 있다. 그래서 몰리는 때를 따로 봐야 했다.
몰리는 때를 봤다
수집이 언제 도는지부터 봤다.
* * * * * /usr/local/bin/collect-all.sh
collect-all.sh 하나에 여덟 대상이 전부 매분 0초에 함께 시작한다.
$ sar -u 1 60 | sort -k8 -n | head -3
14:22:00 all 91.20 0.00 6.10 1.90 0.00 0.80
sar 로 보니 시작하는 몇 초 동안 프로세서가 91%까지 올라간다.
평균 48%와 순간 91%의 차이가 이 구조의 성격이고 대상이 스물이 되면 그 몇 초가 다음 주기까지 밀린다.
장비가 견뎌야 하는 값은 평균이 아니라 이 순간의 합계이고 나머지 시간에는 자원이 대부분 놀고 있었다.
조치 — 시작 시각 흩기
전부 같은 초에 시작할 이유가 없었다.
#!/bin/sh
i=0
for t in $TARGETS; do
( sleep $(( i * 2 )); /usr/local/bin/collector --target="$t" ) &
i=$(( i + 1 ))
done
sleep 으로 2초씩 밀어 시작하니 스물이면 40초에 걸쳐 흩어진다.
같은 일을 같은 만큼 하는데 최대치만 내려갔고 장비를 안 늘리고 얻은 여유라 값이 쌌다.
다만 이 방법은 수집 주기가 1분일 때만 성립한다. 주기가 짧아지면 흩을 자리 자체가 없고 40초를 흩으면 다음 주기와 겹친다.
제약 — 대상 수가 곧 자원인 구조
sleep 으로 시작을 흩는 것은 최대치만 낮추고 총량은 그대로 둔다.
collector 하나가 대상 하나를 맡으면 대상 수가 곧 자원이다. 이 구조로는 늘릴 때마다 장비를 늘려야 한다.
프로세스가 실제로 무엇을 하는지 봤다.
- 대상에 접속해 값을 읽는다 (대기가 대부분)
- 파싱한다 (짧다)
- 저장소에 넣는다 (대기)
파싱은 짧고 도는 시간의 대부분이 응답을 기다리는 시간이었다.
이런 일은 collector 하나가 여럿을 맡아도 된다.
for (;;) {
int n = poll(fds, ntargets, 1000);
for (int i = 0; i < ntargets; i++) {
if (fds[i].revents & POLLIN) { handle(i); }
}
}
poll 로 여러 대상을 한 프로세스가 돌면 메모리가 대상 수에 비례하지 않는다.
선택지 — 격리와 자원
대신 내주는 것이 있었다.
[대상마다 프로세스] 하나가 죽어도 나머지가 산다. 자원이 대상 수에 비례한다
[하나가 전부] 자원이 적게 든다. 하나가 죽으면 전부 멈춘다
앞은 격리를 얻고 자원을 쓰며 뒤는 자원을 아끼고 격리를 내주는 쪽이다.
그래서 중간을 골라 대상을 넷씩 묶어 collector 하나가 맡게 했다.
20 대상 → 프로세스 5개
RSS 합이 3.6GB에서 1GB 아래로 내려갔고 하나가 죽어도 네 대상만 멈춘다.
묶는 크기는 설정으로 뺐는데 지금 넷이 맞는지는 돌려 봐야 알고 나중에 다시 정할 여지를 남겨 두는 편이 낫다.
대응 — 대상당 자원을 계속 기록
다음에도 같은 질문이 올 것이다.
* * * * * ps -o rss= -C collector | awk '{s+=$1} END {print strftime("%s"), s, NR}' >> /var/log/collector-usage
rss 합과 프로세스 수를 collector-usage 에 매분 남기고 대상 수로 나누면 대상당 값이 나온다.
물었을 때 재기 시작하면 며칠이 걸리고 그동안 답을 못 하는데 이미 재 둔 것이 있으면 그 자리에서 답한다.
이번에 여덟에서 스물 을 두고 답을 못 한 것도 재 둔 것이 없었기 때문이다. 늘릴지 말지를 정하는 자리에서 필요한 것은 정확한 예측이 아니라 근거가 되는 숫자였다.
정리
- 늘리기 전에 대상 하나가 무엇을 얼마나 쓰는지 잰다
- 평균이 아니라 몰리는 때를 본다
- 같은 시각에 시작하면 그 순간이 상한을 정한다
- 시작을 흩는 것만으로 최대치가 내려간다
- 총량은 그대로이고 최대치만 내려간다
- 주기가 짧으면 흩을 자리가 없다
- 대상마다 프로세스면 대상 수가 곧 자원이다
- 대기가 대부분인 일은 하나가 여럿을 맡을 수 있다
- 격리와 자원 중 무엇을 내줄지 정한다
- 묶는 크기를 설정으로 두면 나중에 다시 정할 수 있다
- 대상당 자원을 계속 기록해 두면 물었을 때 바로 답한다