[꿀팁] 고성능 Nginx를위한 튜닝 - (1) 디스크의 I/O 병목 줄이기
#Nginx웹서버 설정 #Nginx웹서버설치 #웹서버튜닝 #nginx-install #nginx-설치
[꿀팁] NGINX tunning for High Performance 연재 를 준비하며
고성능웹서버를 위한 리눅스 튜닝을 하는 방법들을 정리하여 보았습니다. 막상 관련 글을 쓰려니 내용이 길어져서 연재 형식으로 진행을 해보고자 합니다.
[꿀팁] 고성능 Nginx를위한 튜닝 - (1) 디스크의 I/O 병목 줄이기 (연재글)
2000년에는 CPU와 메모리의 수준이 지금보다 느렸기 때문에 시스템의 튜닝은 정말 중요한 요소였습니다.
일반 웹서버용 사양이 CPU 200Mhz, 메모리 256MB, SCSI 9GB 정도 였으니까요. 지금은 CPU가 24Core 2.4Ghz, 메모리 64GB, SSD 512GB 정도를 웹서버 급이라고 합니다. 그러나 지금은 클라우드가 대세가 되다 보니 이런 튜닝을 해볼 기회도 없어서 이것을 알아야 할 필요도 없다는 것입니다.
아마도 이런 글도 10년이 지나면 "왜 하니?" 라고 질문을 할 듯 하기는 합니다. 하지만 기술이 많이 변화 하더라도 핵심은 그리 많이 변화 하지 않기 때문에 잘 봐두시면 좋겠습니다.
저는 주로 개발도 같이 해왔기 때문에 이런 튜닝의 경험은 정말 도움이 많이 되었습니다. 대용량 처리에 필요한 프로그램 구현시 문제를 미리 예상하고 해결 할 수 있기 때문에 충분한 활용이 될 것이라고 봅니다.
1. 리눅스 시스템 튜닝 시작
일반적으로 시스템 튜닝하면 웹서버(Nginx) 환경 설정이나 리눅스의 일부 파라미터만 수정해도 튜닝이 됩니다. 좀더 실질적인 튜닝을 하려면 리눅스 커널 부터 시작해서 어플리케이션(Nginx)의 환경까지 전반적인 이해를 바탕으로 튜닝을 해 볼 필요가 있습니다.
실제 튜닝을 한다는 것은 자전거 변속 기어 변경과 같아서 고속 기어로 올린다고 빨라지지 않는 것과 비슷한 상황이 있습니다. 컴퓨터는 기본적인 물리 자원으로 CPU, Memory, Disk, Network 등이 생산 초기부터 성능과 용량의 한계를 가지고 있기 때문에 일부를 고친다고 엄청 빨라지는 것도 아니고 조정을 해도 별로 나아지는 것은 없습니다. 그래서 성능을 개선하려면 서로 연관된 부분들을 함께 튜닝해서 최대한의 성능을 확보하고 시스템의 부하를 줄이는데 목적을 두어야 합니다.
저는 튜닝의 순서를 제일 느린것 부터 시작해서 빠른 것을 나중에 해야 한다고 생각합니다. 실제 튜닝으로 10초에서 1초로 줄이기는 쉽지만, 1초를 0.9초나 0.1초로 줄이는 것은 무진장하게 어렵다는 것입니다. 이유는 앞에서 언급한 물리적인 한계가 정해져 있기 때문입니다. 또한 빠른 영역을 튜닝 하려면 리눅스 커널 소스에 대한 이해도 함께 해야 하기 때문에 뭐 하루 아침에 뚝딱 할 부분도 아니기 때문입니다. (예를 들면 리눅스 커널 컴파일을 하다보면 H/W 부품에 맞는 정식 드라이버 부터 CPU 스케줄러, 불필요한 커널 모듈까지 Disable 하려면 H/W구조와 사용된 부품 그리고 커널의 모든 옵션을 다 알아야 할 정도 입니다.)
리눅스 튜닝을 하는 연재 순서를 정리하면 다음과 같습니다.
1. 디스크의 I/O 병목 줄이기
2. 프로세스 처리량 늘리기 (Process)
3. TCP 관련 처리량 늘리기
4. 메모리 및 CPU 튜닝하기 (Processor)
5. 마이크로캐싱
6. 로그 부하 줄이기
7. Dos, DDos 방어 설정
1. 디스크의 I/O 병목 줄이기 (30% 성능 개선, 엄청 쉬운데 왜 안하니ㅠㅠ)
POSIX 표준은 운영 체제가 각 파일이 마지막으로 액세스 된 시간을 기록하는 파일 시스템 메타 데이터를 유지하도록 요구합니다. 이때 타임 스탬프(atime)로 인해 성능 저하가 발생 됩니다.
- 저는 무조건 file system 마운트 옵션 부터 수정 하라고 합니다. 리눅스의 /etc/fstab에 아래와 같이 noatime와 nodirtime을 추가 하는 것입니다. 이유는 적용이 아주 간단하다는 것입니다. 또 대략 시스템 성능의 10~30% 개선 효과가 있습니다.
※ 특히 파일을 많이 사용하는 도커 나 DBMS를 사용하는 영역에서는 더욱 효과가 크다고 보여집니다.
( 다만 앞에서 말했듯이 아주 빠른 SSD 같이 0.1초에서 0.09초로 줄이는 것은 측정이 안되긴 해서 알수는 없습니다. 그러나 시스템의 I/O는 하드 디스크의 불량도 줄이지만 시스템 코어에 필요한 I/O 입출력을 줄여서 속도를 향상하는 것이라는 것이라 비록 SSD라도 noatime이 왜 중요한지 이해를 반드시 해야 할 필요가 있습니다.)
[ /etc/fstab 수정과 mount 명령을 이용한 옵션 변경]
#@ fstab 를 이용한 파일시스템 마운트 옵션을 수정 합니다.
# vi /etc/fstab
UUID=b373f8f4-c3f0-4bed-b582-3e354d14548f / xfs defaults,noatime 0 0
/dev/sda1 /dbdata ext4 defaults,noatime,nodirtime 1 2
/dev/sdb1 /var/lib/docker ext4 defaults,noatime,nodirtime 1 2
#@ 이미 마운트된 상태에서 /data 디스크의 마운트 옵션을 바꿉니다.
# mount -o remount /data
#@ 변경된 마운트 상태를 확인 합니다.
# cat /proc/mounts
/dev/sda1 /data ext4 rw,noatime 0
- 파일의 timestamp 상태 와 disk I/O 영향
모든 파일 시스템은 파일의 생성과 읽고 쓰기 상태를 관리하기 위해서 생성(ctime), 쓰기(mtime), 읽기(atime) 등 3가지 timestamp 로 파일 상태 정보를 가지고 있습니다.
이들은 파일의 변경 사항이 발생하면 파일 시스템의 상태 정보에 기록합니다. 그 중에서 atime은 파일을 읽을때 마다 수정되는 정보입니다. 특히 웹서버, DB서버와 같이 빈번한 파일을 읽기와 쓰기가 일어나는 경우로 디스크에 파일 상태 정보 갱신을 위한 쓰기가 발생하므로 성능에 많은 영향을 주게 됩니다. 이러한 정보의 빈번한 파일 상태의 수정을 줄여 주는 것이 파일 시스템의 튜닝에서 가장 기초적이고 효과적인 방법입니다.
아래 stat (파일상태 확인) 명령으로 "/etc/fstab" 이라는 파일을 예로 보면 파일이 저장된 블록의 위치와 함께 3가지 timestamp 정보들이 변경되는 것을 볼 수 있습니다.
왜 noatime 과 nodirtime을 사용해야해요 ?
추가 적으로 중요한 내용으로는 "리눅스(유닉스)에서는 모든 파일과 디렉토리, 소켓 등을 내부적으로 파일로 간주 된다"는 것입니다. 빈번한 디렉토리를 조회하거나 파일을 찾는 모든 일들도 파일시스템에 영향을 주게 됩니다. 그래서 시스템의 마운트 시점에 noatime, nodirtime 같은 명령을 사용해서 불필요한 파일시스템의 블록정보 갱신을 방지해야 합니다. 물론 오늘날 SSD는 디스크가 회전하는 방식이 아니라서 파일을 읽고 쓰는 성능에는 별도의 영향을 주지 않을 수 도 있습니다. (일반 디스크 처럼 디스크 내부의 헤더가 블록을 찾지 않기 때문입니다.) 그러나 SSD의 경우도 내부의 블록을 저장한 Cell의 읽고 쓰는데 수명이 있다는 것을 감안한다면 중요한 내용일 것 입니다.
파일시스템은 3가지의 파일의 상태 시간을 관리합니다.
1) ctime(create time): 마지막 생성/수정된 시간
2) mtime(modify time): 마지막 변경된 시간
3) atime(access time): 마지막 읽은 시간 (Accessed)
* 파일시스템은 일반적인 파일 뿐아니라 디렉토리, 소켓 등도 파일로 취급하여 관리하므로 시스템의 I/O를 줄이는 것에 파일 상태 정보는 중요한 정보입니다.
다음과 같이 파일의 상태를 확인해 보면 파일의 읽기 쓰기에 따라 변화 되는 부분을 알 수 있습니다.
$ stat /etc/fstab
File: /etc/fstab
Size: 632 Blocks: 8 IO Block: 4096 regular file
Device: 803h/2051d Inode: 9861231 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Context: system_u:object_r:etc_t:s0
Access: 2020-07-03 04:53:50.382297969 -0400
Modify: 2020-07-03 04:49:59.135856270 -0400
Change: 2020-07-03 04:49:59.163856277 -0400
Birth: -
Docker 컨테이너를 위한 fstab의 noatime 설정
오늘날 가상화 기반의 시스템에서도 파일의 빈번한 접근이나 읽고 쓰기가 일어나는 시스템을 예로 들면 docker의 이미지 파일이 저장되는 공간이 될 것입니다.
도커는 실제 호스트 시스템의 파일 시스템을 사용하므로, 위의 예시와 같이 "/var/lib/docker" 에 "noatime"을 설정 하면 됩니다. 도커 이미지가 주로 있는 디렉토리이기 때문에 별도의 파티션을 구성해서 도커를 이용하는 경우 적합하고 또한 주로 SAN이나 NAS로 마운트 할때에도 "noatime 과 nodirtime" 설정을 하면 가장 빠른 시스템 튜닝 방법이 될 것입니다.
/dev/sdb1 /var/lib/docker ext4 defaults,noatime,nodirtime 1 2
가상 머신 VM 에서 noatime 설정
OpenVZ 컨테이너와 같은 가상 시스템은 "/etc/fstab" file 이 아닌 호스트에서 제어가 가능합니다. VM 의 설정에 "noatime" 을 설정하려면 다음과 같습니다.
vzctl set veid --noatime yes --save
# 컨테이너 101의 설정 수정
vzctl set 101 --noatime yes --save
# 컨테이너 101 재시작
vzctl restart 101
[ 파일시스템 마운트 관련 참고]
- [꿀팁] 고전적 리눅스 튜닝 - 파일 시스템 마운트 옵션으로 성능 개선하기
( https://couplewith.tistory.com/178 )
기타 참고 사항
Linux에서 시간 관련 마운트 옵션을 사용할 수 있습니다.
- noatime – 파일과 디렉토리 모두 에 대한 atime 업데이트를 비활성화합니다.
- nodiratime – 디렉토리의 atime 업데이트를 비활성화합니다.
- relatime – 이전 항목 atime이 <= mtime 또는 ctime이거나 이전 atime이 24 시간 이상 지난 경우 또는 inode가 더티 인 경우에만 atime 속성을 업데이트 합니다 . 주로 마지막 atime 업데이트 이후 파일이나 디렉토리가 수정 된 경우에만 한 번만 업데이트하는 더 나은 옵션입니다.
- strictatime – 전체 atime업데이트 허용
1. 디스크의 I/O 병목 줄이기
2. 프로세스 처리량 늘리기 (Process)
3. TCP 관련 처리량 늘리기
4. 메모리 및 CPU 튜닝하기 (Processor)
5. 마이크로캐싱
6. 로그 부하 줄이기
7. Dos, DDos 방어 설정
[관련 문헌]
1. couplewith.tistory.com/entry/꿀팁-고성능-Nginx를위한-튜닝-2-프로세스-처리량-늘리기
2. couplewith.tistory.com/entry/꿀팁-고성능-Nginx를위한-튜닝-3-TCP-관련-처리량-늘리기
3. [꿀팁] 고전적인 리눅스 튜닝 - 파일시스템 마운트 옵션 개선
4. Gain 30% Linux Disk Performance with noatime, nodiratime, and relatime
5. [Improve Linux Performance] opensource.com/article/20/6/linux-noatime
6. [mysql Write performance and I/O Barriors] davidalger.com/posts/mysql-write-performance-and-io-barriers/