같은 코드를 두 장비에 올렸는데 한쪽만 느려서 코드가 아니라 코드 밖에서 차이를 찾아야 했다.
Table of contents
Open Table of contents
증상 — 사양이 달랐다
서버 A 와 서버 B 의 사양부터 비교했다.
서버 A CPU 8코어 RAM 32G
서버 B CPU 2코어 RAM 4G
서버 B 가 느렸는데 사양 차이만큼이 아니라 훨씬 느렸다.
메모리 상태를 봤다.
$ free -m
total used free
Mem: 3936 3820 41
Swap: 2047 1204 843
스왑을 1.2GB나 쓰고 있어서 디스크를 타느라 느린 것이었다.
원인 — 설정이 같았다
두 장비의 php-fpm 설정 파일이 같았다.
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
max_children 이 서버 A 기준으로 잡혀 있었다.
max_children 50개가 4GB 메모리에 안 들어가니 나머지가 스왑으로 밀린다. 기본 설정은 그 장비의 사양을 모른 채 그대로 적용된다.
조치 — 장비에 맞춘 계산
서버 B 에 맞춰 계산해서 다시 잡았다.
프로세스 하나가 쓰는 메모리 약 60MB (실측)
시스템·DB 용으로 남길 것 1.5G
쓸 수 있는 것 4G - 1.5G = 2.5G
최대 프로세스 수 2.5G / 60MB ≈ 40
여유를 두고 30
max_children 을 30으로 낮췄다.
프로세스 하나가 쓰는 양은 짐작하지 않고 쟀다.
$ ps --no-headers -o rss -C php-fpm | awk '{s+=$1; n++} END {print s/n/1024 " MB"}'
58.2 MB
rss 평균이 58.2MB라 그 값으로 계산했다.
문서에 적힌 일반적인 rss 값은 우리 코드와 부하에서 맞지 않을 수 있다. rss 를 짐작으로 잡았으면 너무 줄이거나 여전히 모자랐을 것이다.
설정 — 스왑과 장비별 프로파일
커널의 스왑 성향도 함께 봤다.
$ sysctl vm.swappiness
vm.swappiness = 60
메모리에 여유가 있어도 스왑을 쓰려는 값이었다.
$ sysctl -w vm.swappiness=10
낮추되 아예 끄지는 않았는데 끄면 메모리가 모자랄 때 프로세스가 강제 종료된다.
설정 파일은 장비별로 나눴다.
config/php-fpm/high.conf max_children = 50
config/php-fpm/low.conf max_children = 30
mem=$(free -m | awk 'NR==2 {print $2}')
if [ "$mem" -gt 16000 ]; then profile=high; else profile=low; fi
cp "config/php-fpm/$profile.conf" /etc/php/7.2/fpm/pool.d/app.conf
메모리 크기를 보고 high 와 low 중 하나를 고르므로 장비가 늘어도 손으로 고를 필요가 없다.
주의 — 설정끼리의 정합
사양에 묶여 있는 설정이 더 있었다.
DB 버퍼 풀 메모리에 비례
워커 수 코어 수에 비례
연결 수 상한 프로세스 수에 비례
셋 다 서버 A 기준이었다.
innodb_buffer_pool_size = 8G # B 에서는 1G
max_connections = 500 # B 에서는 100
프로세스가 30인데 max_connections 를 500 허용하면 의미가 없다.
설정끼리도 서로 맞아야 했다. max_children 만 고치면 연결이 남거나 모자라서 다른 자리에서 문제가 난다.
검증 — 전후 측정과 한계
바꾸고 나서 실제로 나아졌는지 쟀다.
전 평균 응답 1,840ms 스왑 사용 1.2G
후 평균 응답 320ms 스왑 사용 0
스왑 사용이 0이 된 것이 컸고 응답 시간과 둘 다 봤다.
한계도 부하를 걸어 확인했다.
$ ab -n 1000 -c 30 http://b/
동시 30까지는 견뎠고 50에서 대기가 생겨 그 숫자를 적어 뒀다.
상한에 닿았는지도 재게 했다.
# 프로세스가 상한에 닿았는지
$ curl -s http://localhost/status | grep "listen queue"
listen queue: 12
listen queue 에 대기가 쌓이면 프로세스가 모자란 것이다.
listen queue 에 계속 대기가 생기면 장비를 늘리거나 설정을 다시 봐야 한다. 그 판단을 숫자로 하게 됐다.
정리
- 기본 설정은 장비 사양을 모른다
- 큰 장비 기준 설정을 작은 장비에 쓰면 스왑을 쓴다
- 프로세스 하나가 쓰는 메모리를 짐작하지 말고 잰다
- 스왑 성향을 낮추되 끄지는 않는다
- 끄면 메모리가 모자랄 때 강제 종료된다
- 설정을 장비별로 두고 배포할 때 고른다
- 설정끼리도 맞아야 한다
- 프로세스 수와 연결 수 상한을 함께 본다
- 바꾸고 나서 스왑 사용량과 응답 시간을 둘 다 잰다
- 부하를 걸어 한계를 알아 두고 넘으면 알게 한다