- 시스템이 어떻게 움직이고 있는가?



1. 시스템 모니터링 결과에 대해서 정확히 알고 있자

ㅇ 메모리가 왜 이리 모자라지? - free 에 대하여

밤에 잠을 자고 있는데 사무실에서 급하게 전화가 왔다. 웹서비스가 엄청 느려져서 도저히 웹사이트를 볼 수가 없다는 것이다. 잽싸게 일어나서 컴퓨터를 켰다. 원격으로 접속을 해서 서버를 관찰한다. 가장 먼저 무엇을 해야할까? 어디에서 시스템이 느려지고 있는지 찾아야한다. 그리고 평상시의 상태와 비교를 해야한다. 시스템 관리자라면 흔히 일어나는 경우이다. 시스템 관리의 첫 출발은 평상시에 계속 시스템의 상태를 파악하고 모니터링 결과를 비교하는 작업이다. 또한 나온 결과를 제대로 파악하고 있어야하는데 주기적으로 시스템을 모니터링해놓고도 그 결과를 엉뚱하게 해석한다면 전혀 다른 결과를 초래할 수 있다.



예를 한번 들어보자. 리눅스에서 메모리를 점검하기 위해 흔히 free를 사용한다. 그런데 free의 결과를 잘못 이해하는 경우가 상당히 많고 필자도 마찬가지였다. 아래 결과를 보자. 512M의 메모리를 가진 시스템이다.





$ free

total used free shared buffers cached

Mem: 513368 503180 10188 0 3772 332740

-/+ buffers/cache: 166668 346700

Swap: 1028152 84908 943244





이상하게 쓰는 것도 없는데 513M에서 현재 사용하는 것이 503M이다. 아무래도 시스템에 문제가 생겼나해서 재부팅을 했는데 조금 지나자 또 대부분의 메모리를 used에서 잡아먹는다. 에라 모르겠다 메모리를 512M 더 추가시키자. 농담이 아니라 실제로 충분히 발생할 수 있는 상황이다.



디스크를 읽는 일은 메모리에 비해서 아주 느리다. 수많은 사람들이 접속해서 ls 명령어를 모두 디스크에 읽어서 실행을 한다면 그 시스템의 속도가 아주 느려질 것이다. 물론 여기서는 ls라는 간단한 명령을 말했지만 시스템의 자원을 엄청 잡아먹는 프로그램이라면 문제가 또 달라질 것이다. 이런 경우 디스크에서 한번 읽어들인 정보를 메모리에 일정기간 보관하고 있다면 처음 읽을때만 속도가 느리지 이후에는 전반적으로 빨라질 것이다. 이것을 가르켜 디스크 버퍼링이라고 하며 이런 목적으로 사용되는 메모리가 위에서 나오는 버퍼 캐쉬이다.**주1 (실제로 버퍼 캐쉬는 파일을 버퍼링하는 것은 아니고 디스크 입출력의 가장 작은 단위인 블록을 버퍼링한다. 블록 디바이스 드라이버가 사용하는 데이터 버퍼를 가지고 있는 것이다) 만일 캐쉬의 크기가 고정되어 있다면 그 크기가 너무 커도 메모리 부족현상이 생길 수 있을 것이고 지나친 스와핑을 발생하게 해서 시스템이 느려질 가능성이 크다. 리눅스에서는 자동적으로 모든 램의 빈 공간을 버퍼 캐쉬로 사용하여 메모리를 효율성을 높이고 있으며 프로그램에서 많은 메모리를 필요로 하는 경우에는 자동으로 캐쉬의 크기를 줄인다. 그렇다면 위에서 실제로 사용가능한 메모리는 free+buffers+cached 이다. -/+ buffers/cache: 이 줄이 이러한 내용을 반영하고 있다. 일반적인 리눅스 시스템관리와 관련된 서적에는 이 부분에 대하여 지적을 한 곳이 거의 없으며 맨페이지에도 이런 설명은 아직 찾지 못했다. 운영체제와 커널의 원리에 대해서 모른다면 같은 결과를 놓고도 정말 엉뚱한 분석을 할 수 있는 것이다. 위의 내용을 아는 분이라면 모르겠지만 이 글을 보는 사람들중에 위의 내용을 몰랐던 사람들은 정말 모르고 있었구나 하는 생각이 들지 않을까?





2. 현재 시스템의 문제를 찾자

시스템의 성능은 현재의 시스템 자원을 여러 가지 프로그램들의 요청에 대하여 얼마나 효율적으로 적용을 하는가에 달려있다. 요즘처럼 인터넷이 보편화된 상황에서 네트워크도 중요한 부분이기는 하지만 일반적으로 성능에서 가장 중요한 시스템 자원은 CPU, 메모리, 디스크 입출력이다. 그래서 위의 부분들을 중심으로 성능에 문제가 생기기 전에 미리 시스템을 분석하는 것이 중요하다. 시스템에 문제가 생긴다면 다음을 먼저 점검해보자



 CPU 문제 점검 - top, ps, uptime, vmstat, pstree 등

 메모리 문제 점검 - free, vmstat 등

 메모리에 문제가 없다면 디스크 I/O 점검 - df, du, 쿼타 등

 디스크와 메모리에 문제가 없는데도 시스템에 문제가 생기면 CPU의 오버헤드에 문제가 있을 가능성이 크다

ㅇ 네트웍 문제 점검 - netstat, ping, traceroute, tcpdump 등



3. 사전 점검 사항

시스템이 정상적으로 작동하고 있을 때 정기적으로 모니터링을 해 두어야 시스템에 문제가 생겼을 때 대처를 할 수 있다. 아래를 참고로 하여 주기적으로 시스템 모니터링 결과를 모아두자.



 주요 사용자들한테 성능이 괜찮다는 동의를 먼저 얻어야한다. 그리고 시스템 성능을 계속 유지할 수 있도록 정기적으로 점검한다.

 시스템 accounting 프로그램을 이용한다. 시스템에서 CPU, I/O, 메모리 집약적인 프로그램들을 알고 있어야한다. (**주2)



$ sa



75563 169479.02re 510.44cp 0avio 506k

14056 450.37re 448.85cp 0avio 497k webalizer

361 91931.12re 37.86cp 0avio 1355k httpd*

275 7.50re 5.38cp 0avio 402k gawk

14056 455.55re 4.95cp 0avio 438k weblog

14226 588.92re 2.57cp 0avio 437k sh

11 18.88re 2.45cp 0avio 332k slocate

162 670.85re 1.46cp 0avio 666k in.telnetd

2298 45.83re 1.09cp 0avio 2858k mysqld*

325 0.86re 0.76cp 0avio 642k ps

358 1185.61re 0.66cp 0avio 454k bash

1419 0.48re 0.47cp 0avio 283k rmmod



accounting 결과 예제





 vmstat 등의 프로그램을 이용 I/O연산이 얼마나 분산되어있는지, CPU가 작동하지 않고 노는 시간(idle)은 얼마인지, 정상적인 부하가 걸릴 경우 메모리를 얼마나 사용하고 있는지 확인한다.



주기적으로 시스템 상황을 자동으로 모니터링하는 방법에 대해서는 세 번째 강좌에서 설명을 할 예정이다.







4. 문제가 발생했을 경우의 대처 방법

시스템이 정상적으로 작동하고 있을때 제대로 모니터링을 하고 분석을 해 두었다면, 사용자가 불평하거나 문제가 터지기 않더라도 언제 시스템의 성능이 나빠질지 알 수 있다. 그러면 시스템에 문제가 발생하였을 경우에는 어떻게 대처를 해야 할까?



 어떤 프로그램을 실행하고 있으며 어떻게 사용하고 있는가? 현재 시스템에서 주요하게 제공하는 서비스는 어떤 것인가? 예를 들어 NFS를 통해 파일에 접근을 한다면 네트웍 성능이 떨어지는게 문제의 한 부분이라는 것을 알 수 있다. 웹서비스를 한다면 시스템 자체의 부하가 아니라 네트웍 회선의 문제로 속도가 느려질 가능성도 있다.

 uptime을 이용하여 시스템의 부하를 확인한다. 시스템의 부하가 어떤 추세로 움직이고 있고 어느 정도 수치로 작동하는지 확인을 한다. 웹서비스의 경우 보통 낮시간대에 접속이 폭주하므로 S자 형태로 시스템 부하가 변화될 것이다. 그런데 최대 접속할 시간이 아닌데도 시스템의 부하가 높아진다면 불필요한 프로그램이 계속 돌면서 시스템의 자원을 소비할 가능성도 있고 DOS 공격 등을 받고 있을 가능성도 크다.



$ uptime

9:23pm up 61 days, 5:23, 1 user, load average: 0.02, 0.05, 0.00





 ps와 top를 활용한다.

- 디스크 액세스나 페이징을 기다리고 이는 프로세스가 있는가? 그렇다면 I/O와 메모리를 점검하자. 리눅스에서 프로세스 대기 상태는 인터럽트 허용과 인터럽트 금지의 두가지 형태가 있다. 세마포어를 기다리거나 파일을 읽을 수 있게 되길 기다리는 것처럼 자원을 기다리는 일반적인 대기상태는 대개 인터럽트로 처리가 가능하다. (인터럽트가 허용되는 sleep 상태는 ps,top 등에서 S로 나타난다.) 그렇지만 인터럽트가 금지되는 대기상태는 스왑파일에서 메모리로 페이지를 읽어들이는 것과 같이 임계지역에서 일이 끝마치기를 기다리고 있는 상태이다. 프로세스 상태에서 D 는 uninterruptible sleep로서 page fault 등을 의미하며 page fault 등을 통해 I/O중인 상태를 나타낸다. W는 has no resident pages를 의미하며 프로세스가 스왑아웃된 상태를 나타낸다. (W는 커널 프로세스에 대해서는 정확히 동작을 하지않는다.

- CPU와 메모리를 가장 많이 사용하는 프로세스를 찾으면 부하를 분산시키는데 도움이 될 것이다.



$ ps auxw

USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND

root 1 0.0 0.0 1120 68 ? S Jun17 0:06 init [3]

root 2 0.0 0.0 0 0 ? SW Jun17 0:00 [keventd]

root 3 0.0 0.0 0 0 ? SW Jun17 0:00 [kswapd]

root 4 0.0 0.0 0 0 ? SW Jun17 0:00 [kreclaimd]

root 5 0.0 0.0 0 0 ? SW Jun17 0:32 [bdflush]

root 6 0.0 0.0 0 0 ? SW Jun17 0:02 [kupdated]

root 351 0.0 0.0 1164 284 ? S Jun17 0:01 syslogd -m 0

root 360 0.0 0.0 1728 356 ? S Jun17 0:00 klogd

root 388 0.0 0.0 1156 220 ? S Jun17 0:00 inetd

root 402 0.0 0.2 3824 1452 ? S Jun17 3:09 /usr/sbin/snmpd

named 416 0.0 0.3 3376 1904 ? S Jun17 0:27 named -u named







$ top

7:34pm up 14:19, 3 users, load average: 1.20, 0.54, 0.20

57 processes: 53 sleeping, 4 running, 0 zombie, 0 stopped

CPU states: 94.6% user, 5.3% system, 0.0% nice, 0.0% idle

Mem: 513368K av, 321260K used, 192108K free, 0K shrd, 9208K buff

Swap: 1028152K av, 115000K used, 913152K free 270924K cached



PID USER PRI NI SIZE RSS SHARE STAT LIB %CPU %MEM TIME COMMAND

12436 root 20 0 6428 6428 1564 R 0 13.1 1.2 0:00 cc1

3570 nobody 12 0 11148 6140 5016 R 0 2.5 1.1 0:09 httpd

12435 root 8 0 1528 1528 388 S 0 1.3 0.2 0:00 cpp

4185 root 9 0 512 328 312 S 0 0.3 0.0 0:01 sshd2

11364 taejun 10 0 876 876 680 R 0 0.3 0.1 0:00 top

1 root 8 0 120 68 68 S 0 0.0 0.0 0:04 init

2 root 9 0 0 0 0 SW 0 0.0 0.0 0:00 keventd

3 root 9 0 0 0 0 SW 0 0.0 0.0 0:03 kswapd

4 root 9 0 0 0 0 SW 0 0.0 0.0 0:00 kreclaimd

5 root 9 0 0 0 0 SW 0 0.0 0.0 0:01 bdflush

6 root 9 0 0 0 0 SW 0 0.0 0.0 0:00 kupdated

348 root 9 0 208 156 156 S 0 0.0 0.0 0:00 syslogd

357 root 9 0 608 4 4 S 0 0.0 0.0 0:00 klogd

371 root 8 0 180 124 120 S 0 0.0 0.0 0:00 crond





 vmstat를 이용한다. (vmstat 5 5)



$ vmstat 5 5

procs memory swap io system cpu

r b w swpd free buff cache si so bi bo in cs us sy id

3 0 0 115000 189428 9220 272608 1 28 10 36 108 25 2 0 98

1 0 0 115000 189972 9220 272680 0 0 0 205 196 416 95 5 0

1 0 0 115000 187060 9220 272740 0 0 0 157 156 229 95 5 0

2 0 0 115000 194852 9220 272856 0 0 0 149 142 229 96 4 0





- vmstat에서 첫 번째 줄은 부팅 이후의 각 통계치에 대한 평균치를 보여주는데 이것은 무시해야 한다. 두 번째 줄부터 보면 된다.

- cpu에서 시스템에서 사용하는 cpu시간이 지나치게 높다면(50% 이상) 디스크 I/O에서 문제가 있을 가능성이 크다. 해당 프로그램의 소스코드를 구할 수 있다면 프로그램에서 얼마나 효율적으로 I/O를 사용하는지 확인해 보아야 한다. vmstat에서 sy 항목은 system time을 의미하며 커널모드로 넘어간 상태이다.

- 시스템 전체의 부하가 높은데도 cpu에서 휴지시간(idle time, id 항목)이 일반적으로 10%를 넘는다면 I/O나 메모리에 문제가 있을 가능성이 크다.

- 휴지시간이 항상 0이라면 CPU를 완전하게 100% 사용하고 있는 상태이다. CPU의 기능을 최대한 활용하는 것은 좋은 현상이다. 그런데 항상 100% 로 바쁜 상태라면 어떤 작업이 계속 축적되고 있다는 것이며 CPU의 과부하를 의미한다. CPU를 계속 사용하고 있는 프로세스를 찾아야한다.

- 디스크의 활동이 분산되지 않았다면, I/O 작업을 효율적으로 분산시켜야한다. 그런데 현재 리눅스용 vmstat에서는 이에 대한 정보는 나오지 않는다. 다른 유닉스의 경우에는 iostat나 sar을 이용하는데 인터넷에서 패키지를 받아서 설치할 수 있다. 이러한 프로그램이 sysstat 로 리눅스용 sar, iostat 패키지이다. 사이트 주소는 다음과 같다. (최근 레드햇 배포판 등에는 들어가고 있다)

http://perso.wanadoo.fr/sebastien.godard/



아래에서는 현재 두 개의 HDD가 장착되어 있는 시스템의 상태를 보여준다.



$ iostat

Linux 2.4.4 (tunelinux.pe.kr) 07/08/01



avg-cpu: %user %nice %sys %idle

5.55 0.00 1.20 93.26



Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn

dev8-0 6.06 9.49 102.23 50185324 540747898

dev8-1 1.99 1.51 25.05 7964700 132509976





위 사항에서 해당하는 것이 없고 메모리와 I/O 관련 튜닝을 할 필요가 없다고 분석이 되면 CPU쪽에서 과부하가 걸린 상태이다. CPU 과부하의 경우 대처할 수 있는 몇가지 방법은 있지만 CPU의 과부하는 메모리와 I/O 문제로 나누어지기 때문에 찾아내기 힘든 부분이다.



CPU의 과부하를 줄일 방법에 대해서 설명한다.

- 필요없는 데몬과 서비스를 줄인다. rwhod와 routed는 시스템의 성능을 저해하는 프로그램으로 이를 없애는 것만으로 많은 도움이 될 것이다. kudzu, portmap, nfslock, apmd, random, netfs, identd, atd, pcmcia, lpd, sendmail, gpm, xfs, linuxconf 등이 불필요하다면 사용하지 않아도 될 서비스들이다. cpu의 부하를 줄일 뿐만 아니라 보안 관점에서도 필요없으면 내리는 것이 낫다. 여기서 잘못 이해하기 쉬운 것이 sendmail 부분이다. pine등을 이용하여 메일을 발송할 때에는 센드메일 데몬을 이용하지 않는다. 센드메일을 백그라운드 작업으로 계속 작동시키는 목적은 외부로부터 메일을 받는데 있다. 그러므로 일반적인 메일서버의 기능을 하지 않고 단지 메일을 보내는 것이 목적이라면 sendmail을 가동시킬 필요가 없다. (**주3)

- at이나 cron 등을 이용 작업을 밤이나 시스템의 부하가 적을 때 실행하는 것도 좋은 방법이다. 보통 로그 로테이션, 백업, 로그분석 등은 새벽 시간을 이용하여 처리하는게 바람직하며 기본설정되어 있는 cron 작업은 대부분 새벽 4시에 가동이 된다.

- CPU 집약적인 작업은 nice를 이용 실행우선순위를 낮추면 편집작업과 갈은 상호대화적인 작업의 성능이 향상될 것이다.

- cpu 집약적인 작업의 실행우선순위를 높이면 작업 자체는 빨라지겠지만 상호대화적인 작업의 성능은 떨어질 것이다. 그런데 보통 일반적인 인터넷 서버로 운영을 한다면 CPU 집약적인 작업은 그렇게 많은 편은 아니며 꼭 서버에서 실행시켜도 되지 않는 작업이라면 다른 시스템을 이용하는 것이 좋다. 웹로그 분석도 이러한 경우에 속한다.

- nice를 이용하는것은 임시방편일 뿐이다. 부하가 계속 증가한다면 nice를 이용하는 것에도 한계가 있다. 시스템을 업그레이드하거나 부하를 분산할 시스템을 구입해야한다.

- 커널이나 프로그램의 컴파일을 하는 경우에도 nice를 이용하면 조금이나마 속도의 향상이 있다. 그렇지만 때로 nice를 잘못 사용하면 문제가 생길 수가 있으니 조심해야 한다. 예를 들어 오라클에서는 오라클 사용자 프로세스와 백그라운드 프로세스들을 같은 우선순위에 유지해야 한다. 설계가 그렇게 되어 있기 때문이다. 우선 순위를 변경할 경우 내용과 반응시간에 원치 않는 효과를 초래할 수도 있다. 예를 들어 log write process(LGWR)에 낮은 우선 순위를 부여할 경우, 이 프로세스는 충분한 횟수만큼 작동하지 못하고 LGWR은 병목현상을 일으키게 된다. 다른 한편, 만약 LGWR이 높은 우선 순위를 부여받게 되면, 사용자 프로세스는 느린 반응시간에 시달리게 될 것이다. 무엇이든 그냥 적용하는게 아니라 그 원리를 이해하고 조심스럽게 해야 한다. (**주4)





5. 메모리 문제 파악하기

시스템에 과부하가 걸려있는데도 휴지기간(idle time)이 많거나 ps에서 많은 양의 메모리를 필요로 하는 프로그램이 실행되고 있다면 메모리 문제를 생각해 볼 수 있다.



 vmstat 5 를 실행해보자.

- swap-out이 지속적으로 항상 발생한다면 메모리가 부족한 것이다. 주기적으로 swap-outs이 발생하는건 정상적인 것이다. BSD 시스템에서는 비상호대화적인 작업을 스왑아웃한다. 현재 실행하고 있는 프로그램에서 스왑아웃이 계속 발생한다면 프로그램이 죽을 수도 있으며심각하게 메모리가 부족하다는것을 가리킨다. 스왑아웃필드(so)가 항상 0에 가까워야한다. 그렇지 않다면 프로세스들간에 가상 메모리에 대하여 경쟁하고 있으며 시스템은 페이징 상태에 있다. 페이징 활동은 또한 심각한 가용 메모리(free)의 부족과 직접적인 관련을 가지고 있으며 간접적으로는 시스템의 높은 CPU 사용시간 비율(sy)과 관련이 있다. 프로세스가 시작을 할 때 항상 이미지(text)와 데이터를 page-in 해야 하므로 page-in 열에서 높은 숫자가 항상 심각한 것은 아니라는 사실은 기억하고 있어야 한다.

- ps나 accounting 시스템에서 메모리 집약적인 작업이 있는지 확인을 한다. RSS필드나 storage integral이 큰 프로그램을 찾아보자. ps나 top에서 RSS는 프로세스가 사용중인 물리적 메모리 크기로 kbytes 단위로 보여준다. ELF 프로세스에서 사용된 라이브러리 페이지도 여기에 같이 계산이 된다. storage integral은 accounting 패키지에 들어있는 sa -K 옵션을 이용해 볼수있다.





메모리 문제를 해결할 몇가지 방법을 찾아보자.

- 시스템에서 버퍼 캐쉬가 있다면 크기를 줄인다. 대신 디스크 I/O성능에 영향을 줄 수있다. 리눅스는 /proc를 이용 전체 메모리에서 버퍼 캐쉬의 사용량을 조절할 수 있다. 이에 대해서는 마지막 장 에서 다루겠다.

- 네트워크 버퍼를 줄인다. 웹서버의 응답은 클라이언트에 직접 전달되는 것이 아니라 소켓 버퍼라는 네트워크 버퍼에 저장을 한다. OS에서는 적절한 수와 크기의 네트워크 버퍼를 가지고 있어야 메모리를 낭비하지도 않고 서버에서 필요한 버퍼를 기다리는 동안 서버의 작업이 중단되지도 않는 상태로 클라이언트의 접속을 처리할 수 있다. 갑작스럽게 접속이 중단되는 클라이언트에서 차지하고 있던 네트워크 버퍼는 웹 서버에서 메모리가 낭비되는 주요한 원인중의 하나이다. 아파치에는 SendBufferSize 지시자가 있어서 운영체제에서 지정되어 있는 TCP의 전송 버퍼를 늘릴 수 있다. 그런데 아직까지 필자도 리눅스쪽에서는 이 부분에 대하여 자세히 소개된 자료를 찾지 못했다. 아직 정확히 이해하지 못하고 있는 부분이지만 도움이 될까하여 고민 끝에 관련 내용을 포함한다. (**주5)

- 커널 테이블의 크기를 줄인다. 이를 통해 시스템의 자원을 제약할 수 있다. 예를 들어 파일 개수, 프로세스 개수등이 해당한다. 운영체제에서 이와 관련된 설정을 높이면 그만큼 커널 테이블의 크기가 늘어나 메모리도 그만큼 더 사용하게 된다.

- 많은 메모리를 필요로 하는 프로그램은 밤에 돌리자.

- 많은 메모리를 필요로 하는 프로그램은 배치 큐를 이용해 작업하자. at, cron등 활용

- 자기만 사용하는 프로그램이라면 프로그램에서 메모리를 효율적으로 사용하는지 점검하자.

- 메모리 요구량을 줄이기 위해 공유 메모리를 사용하자. 공유 메모리는 다수의 프로세스 간에 데이터를 공유하고 전달하는 효과적인 방법을 제공한다. 또한 어떤 특정한 명령을 여러명이 사용하는 경우 가상 주소공간에 해당 프로그램의 여러 복사본을 갖는게 아니라 물리적 공간에 하나의 복사본을 만들고 모든 프로세스가 그것을 공유할 수 있다. 동적 라이브러리는 여러 프로세스가 실행 코드를 공유하는 대표적인 예이다. 그런데 공유 라이브러리를 사용하는 경우 메모리는 절약할 수 있겠지만 프로그램의 성능은 정적으로 컴파일하는 것이 더 빠르다. 예를 들어 Mysql의 경우는 -static을 사용하지 않고 동적으로 링크하면 대략 13%정도 속도가 느려진다고 한다. (**주6)

- sendmail은 메모리를 많이 사용하는 프로그램으로 sendmail을 실행하는데 사용되는 시간에 제한을 두자. 아니면 네트웍을 재구성해서 메일서버를 다른 시스템으로 옮길 수 있다. 위에서 설명을 했던대로 메일을 받는 경우에만 sendmail을 사용하면 된다.

- 이막스(Emacs)는 메모리를 많이 사용하는 프로그램으로 vi 등 다른 에디터를 사용하자.

- 이 모든게 안되면 메모리를 구입하자. 하드웨어의 가격이 예전에 비해 많이 저렴해졌으며 이제 1G 메모리도 그렇게 특별한 시스템은 아닌 상황이다. 대신 프로그램에서 요구하는 기본 메모리양도 커졌다.





6. 디스크 I/O 문제 파악하기

시스템에 과부하가 걸려있는데도 휴지기간(idle time)이 많다면 디스크 I/O 문제를 생각해 볼 수 있다. 보통 메모리 문제와 I/O문제는 서로 관련이 되어있다.



 vmstat 5 를 실행한다. 그리고 이것을 정상적인 시스템 상황과 비교해본다. 정상적인 경우보다 디스크 연산이 더 높은가?

 디스크 활동이 시스템 디스크에 골고루 분산되어있는가? iostat 등을 이용하여 모니터링하면 될 것이다.

 그렇지 않다면 가장 활동적인 디스크가 가장 빠른 디스크인가?

 디스크 활동이 디스크의 특정 영역에 집중되어있는가? 디스크에 적당히 분포되어있는가? 아니면 서로 다른 반대방향의 지점에 있는가?

 NFS를 사용하고 있는가? 사용자들이 자신의 지역파일에 접근하는데 속도가 느리다고 보고를 하는가? 원격 파일시스템을 사용하는가? 만약 원격 파일시스템을 사용하면 네트웍 상황에 대해서 살펴보자. 이경우에는 지역 디스크 I/O문제는 아니다.

 vmstat를 이용 메모리 상황을 살펴보자. 시스템에서 페이징이나 스와핑이 계속 일어나고 있다면, 메모리에 문제가 있으며 이경우 디스크 I/O에 심각한 문제를 초래할 수 있다. 먼저 메모리 문제를 살펴보아야한다.



이에 대한 해결책을 찾아보자.

 파일시스템을 재구성하고 가능한한 I/O작업을 분산시킨다.

 루트 파일시스템에 가장 빠른 디스크 드라이브와 컨트롤러를 사용한다. 루트 파일 시스템이 대부분 가장 많은 I/O작업을 한다. 특정한 파일의 성능이 중요하다면 성능이 중요한 파일을 하나의 파일시스템에 넣고 이 파일시스템에 가장 빠른 드라이브를 사용한다.

 퍼포먼스가 중요한 파일을 블락 크기가 큰 파일시스템에 넣는다. 리눅스에서 블락 크기는 블락당 1024, 2048, 4096이다. 파일시스템을 생성할 때 지정을 해야 한다. 블락 크기를 높이면 성능이 향상될 수는 있어도 불필요하게 낭비되는 하드디스크 공간이 늘어난다.

 버퍼 캐쉬의 크기를 늘린다. 그러면 대신 메모리에 문제가 생길 수 있다.

 단편화를 제거하기 위해 주기적으로 파일시스템을 재구성한다.

 자주 사용하는 파일을 파일시스템의 시작부분에 집중시키는 프로그램을 사용할수 있다.



운영체제 차원의 튜닝에서 가장 많은 성능향상을 얻을 수 있는 분야가 I/O 부분이다. I/O성능은 하드디스크의 속도뿐만 아니라 시스템 버스에 물려있는 여러 구성 요소와 함께 좌우가 된다. 그런데 리눅스를 주로 사용하는 인텔x86 계열의 하드웨어는 타상용 유닉스 시스템에 비하면 I/O와 관련되어 성능이 미약한 편이다. 또 비디오 카드등은 64비트를 넘어 128비트 체계로 간지 오래되었지만 마더보드의 PCI 슬롯은 32비트 체계를 가지고 있다. PC레벨에서는 32비트 CPU가 사요되고 있고 메모리를 부분만 64비트로 동작하고 있을 뿐이다. (**주 7) 그렇지만 PC서버가 일반화되면서 예전 상용 유닉스 장비에만 있었던 여러 가지 기능들이 속속 도입되고 있으며 앞으로도 발전이 기대된다. 여기에 몇가지 이야기를 덧붙이자면 디스크 I/O의 속도향상을 위해 RAID등을 사용할 수도 있고 소프트웨어 차원에서의 소프트웨어 레이드도 현재 리눅스에서 지원이 되고 있다. 저널링 파일 시스템이 최근부터 본격적으로 지원되기 시작하여 파일시스템의 안정성과 무결성을 높임과 더불어 속도도 꾸준한 향상이 되고 있다. 현재 리눅스에서 지원하는 저널링 파일 시스템으로는 reiserfs, SGI의 XFS 등이 있다. (**주8)



디스크 용량에 문제가 생길 수도 있다. 파일시스템에 여유공간이 부족한가? 그렇다면 몇가지 방법을 생각해보자.

ㅇ 필요없는 파일을 cron 등을 이용 정기적으로 삭제하자. 오래된 코어 덤프 파일, 에디터 백업파일, auto-save 파일 등등.

 디스크 쿼터를 이용해 사용자의 디스크 용량 사용을 제한할 수 있다.

 매우 작은 파일이 모여있는 파일시스템에는 작은 블럭 사이즈를 사용한다. (소스 코드, 작은 데이타 파일 등등)

ㅇ /tmp, /var/tmp 등에는 계속 임시파일이 생성되고 man 페이지를 위한 임시파일도 계속 생성이 된다. 이에 대한 처리는 레드햇 리눅스 계열에서/etc/cron.daily/tmpwatch를 이용하여 처리한다. 3장에서 다시 소개할 것이다.





7. 네트웍 문제 점검

네트웍 문제 점검

 rlogin이나 NFS를 이용하여 파일에 접근하는 사용자가 성능이 느리다고 생각이 든다면 이는 네트웍 용량이나 데이터 정합성에 문제가 있을 수 있다.

 netstat -i 를 실행하자. 충돌(collison)이 크면 네트웍에 오버헤드가 걸렸다고 생각할 수 있다. 충돌률은 ifconfig -a 등을 이용해서 나온 결과중 (collisons/TX packets) X 100을 하면 나온다. input이나 output 에러가 많다면 하드웨어 문제일 수 있다. 입력에러가 많다면 네트웍의 특정한 곳에 문제가 있을 가능성이 크며 출력에러가 많다면 시스템과 네트웍 인터페이스에 문제가 있을 가능성이 크다.

 충돌이나 네트웍 하드웨어의 문제가 아니라면, 어떤 시스템이 가장 느린지를 찾아야한다. dropped 패킷이 크다면, 원격 시스템은 아마도 들어오는 자료에 대해 충분히 빠르게 대응하지 못할 것이다. 원격 시스템에 cpu, 메모리, 디스크 I/O문제가 있는지 확인하자. 그게 아니라면 그 시스템은 네트웍의 과부하에 견디지 못할 것이다. 네트웍을 다시 재구성하고 느린 시스템을 파일 서버로 사용하지 말자.

ㅇ 기본적인 연결 테스트에는 ping 명령을 사용하며 네트워크 접속에 대한 점검을 한다면 ifconfig, arp, netsta 등을 이용하고 네트워크 하드웨어 문제를 점검해 보아야 한다.





# ifconfig -i

eth0 Link encap:Ethernet HWaddr 00:01:02:26:BD:CE

inet addr:211.111.111.111 Bcast:211.50.6.255 Mask:255.255.255.192

UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

RX packets:346297 errors:0 dropped:0 overruns:0 frame:0

TX packets:346683 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:100

Interrupt:10 Base address:0xd000



lo Link encap:Local Loopback

inet addr:127.0.0.1 Mask:255.0.0.0

UP LOOPBACK RUNNING MTU:16436 Metric:1

RX packets:7745 errors:0 dropped:0 overruns:0 frame:0

TX packets:7745 errors:0 dropped:0 overruns:0 carrier:0

collisions:0 txqueuelen:0





# netstat -i

Kernel Interface table

Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg

eth0 1500 0 349675 0 0 0 350447 0 0 0 BRU

lo 16436 0 7825 0 0 0 7825 0 0 0 LRU





네트웍 부하 줄이는 방법 (**주9)

 사용자가 네트웍을 통해 I/O집약적인 프로그램을 실행하지 않도록 막자. grep 프로그램이 I/O 집약적인 프로그램중의 대표적인 예이다. 대신 네트웍을 통해 로그인해서 작업하자.

 네트웍에 연결된 컴퓨터와 디스크를 재구성해서 가능한 많은 사용자가 지역 지역 시스템에서 작업을 하도론 만든다.

 디스크없는 워크스테이션의 숫자를 줄인다. 가능하다면 이런 워크스테이션은 제거한다.

 뛰어난 네트웍 성능을 가진 시스템을 파일서버로 사용한다.

ㅇ 충돌률을 줄이기 위해서는 네트워크 세그먼트 상의 트래픽양을 줄여야 하며 이 경우 이더넷을 분리할 수 있다.



데이터 정합성에 문제가 있다면 유일한 해결책은 문제가 있는 하드웨어를 찾아서 바꾸는 것이다. 네트웍 분석툴이 이러한 작업을 하는데 반드시 필요할 것이다.







8. 마치며

이번 강의에서는 시스템 최적화를 위한 준비 단계로 프로세스, 메모리, I/O 부분을 중심으로 어떻게 현재 시스템 상태를 모니터링하고 문제를 발견하여 해결을 할 수 있는지에 대하여 설명을 하였다. 중요한 것은 시스템을 모니터링하는 것과 더불어 나온 결과에 대하여 정확하게 이해를 해야 한다는 것이다. 이에 대해서는 free를 예로 들어 설명을 하였다. 가장 먼저 주기적으로 시스템을 모니터링하여 문제를 파악하고 있어야 하고 문제가 발생했을 경우 여러 가지 상황에 따라 CPU, 메모리, I/O부분을 점검해야 한다.
2005/07/04 14:24 2005/07/04 14:24
운영체제와 커널 차원에서의 튜닝과 보안에 대해서 중요한 부분을 설명하겠다. 보안도 다루기는 하지만 일반적인 차원에서의 보안에 대한 내용은 뺐다. 일반적인 보안에 대해서는 잘 다룬 서적이나 자료들이 많기 때문에 중복된 내용을 설명하기보다는 다른 곳에서는 자세히 다루지 않았지만 중요한 부분들을 설명하는게 나을 것 같다. 먼저 파일시스템 설정과 관련된 부분에 대해서 설명을 하고 운영체제 및 커널 차원의 튜닝과 보안에 대해서 설명을 하겠다.



1. 파일 시스템 관련

/etc/fstab에는 파일 시스템 관련된 정보가 들어있다.



$ cat /etc/fstab

/dev/sda2 /home ext2 defaults,rw,nosuid,nodev,noexec 1 2

/dev/sda3 /var ext2 defaults,rw,nosuid,nodev,noexec,noatime 1 2

/dev/sda4 /tmp ext2 defaults,rw,nosuid,nodev,noexec 1 2



여기서 세 번째 줄은 해당 파티션에 대한 제어정보가 들어있는데 의미는 다음과 같다.

default : quota, read-write, suid, async

noquota : 쿼터 사용하지 않음

nosuid : SUID/SGID 설정을 할 수 없음

nodev : 문자나 블락 특수 디바이스 설정을 할 수 없음

noexec : 바이너리 파일의 실행 권한을 주지 않음

quota : 쿼터 설정

ro : 읽기 전용 파일시스템

rw : 읽기/쓰기 파일시스템

suid : SUIG/SGID 설정 허용

noatime : atime(파일 접근 시간)을 업데이트하지 않음



리눅스 및 유닉스에서는 파일 생성 시간 및 마지막 변경시간과 함께 마지막 접근시간을 계속 기록한다. 그렇지만 자주 접근하고 변경이 되는 파일에 대해서는 파일 접근시간이 필요하지 않다면 굳이 마지막 파일 접근시간을 기록하지 않아도 된다. 웹서버, 캐쉬파일, 로그파일 등 주로 읽기전용 파일이 이에 해당할 것이다. 이 설정만으로 성능 병목현상의 가장 큰 주범인 디스크 I/O를 줄이는 데에 도움이 될 수 있다. 그렇지만 파일이 변경된 경우에는 업데이트가 되니 걱정하지 말기 바란다. (chattr 프로그램을 이용하여 개별 파일에 대해서도 지정할 수 있다)



nosuid는 SUID,SGID를 하지 못하도록 막는다. SET-UID 프로그램은 보안상 문제가 생길 여지가 많으며 루트 계정으로 접근할 수 있는 지름길이 되는 경우가 많다. 불가피하게 사용해야 할 경우가 있지만 그 외에는 nosuid 설정을 통해 보안을 강화하는게 좋다. 레드햇 계열에서 SET-UID root 프로그램은 다음과 같은 것들이 있다.



# find / -type f \( -perm -04000 -o -perm -02000 \) \-exec ls -alF {} \;

-rwsr-xr-x 1 root root 14188 Mar 7 2000 /bin/su*

-rwsr-xr-x 1 root root 86690 Oct 20 2000 /bin/ping*

-rwsr-xr-x 1 root root 62192 May 4 09:45 /bin/mount*

-rwsr-xr-x 1 root root 28432 May 4 09:45 /bin/umount*

-r-sr-xr-x 1 root root 15688 Dec 1 2000 /sbin/pwdb_chkpwd*

-r-sr-xr-x 1 root root 16312 Dec 1 2000 /sbin/unix_chkpwd*

-rwsr-xr-x 1 root root 22232 Feb 22 10:21 /usr/bin/crontab*

-rws--x--x 1 root root 14056 Jan 11 2001 /usr/bin/chfn*

-rws--x--x 1 root root 13800 Jan 11 2001 /usr/bin/chsh*

-rws--x--x 2 root root 563164 Aug 9 2000 /usr/bin/suidperl*

-rws--x--x 2 root root 563164 Aug 9 2000 /usr/bin/sperl5.00503*

-rwxr-sr-x 1 root man 36368 May 23 13:25 /usr/bin/man*

-r-s--x--x 1 root root 12244 Feb 8 2000 /usr/bin/passwd*

-rwxr-sr-x 1 root mail 11620 Feb 8 2000 /usr/bin/lockfile*

-rwsr-sr-x 1 root mail 76432 Feb 8 2000 /usr/bin/procmail*

-rwxr-sr-x 1 root slocate 23024 Dec 21 2000 /usr/bin/slocate*

-rwsr-xr-x 1 root root 198312 Nov 10 2000 /usr/bin/ssh1*

-rws--x--x 1 root root 5640 Jan 11 2001 /usr/bin/newgrp*

-rwxr-sr-x 1 root tty 8332 Jan 11 2001 /usr/bin/write*

-rwxr-sr-x 1 root utmp 6096 Feb 25 2000 /usr/sbin/utempter*

-rws--x--x 1 root root 9568 Mar 29 18:30 /usr/sbin/suexec*

-rwsr-sr-x 1 root mail 369340 Jun 19 14:31 /usr/sbin/sendmail*

-rwsr-xr-x 1 root root 17704 Oct 7 2000 /usr/sbin/traceroute*

-rwsr-xr-x 1 root root 34751 Jan 16 00:50 /usr/libexec/pt_chown*





여기서 불필요한 파일은 chmod를 이용 설정을 바꾸어야 한다. chage, gpasswd, wall, chfn, chsh, newgrp, write, usernetctl, traceroute, mount, umount, ping, netreport 등이 여기에 해당한다.



그런데 위의 프로그램은 서비스를 제공하기 위해 어쩔 수 없는 경우이지만 그러한 경우가 아니라면 특정한 파티선에 nosuid를 사용해서 계정 등의 사용자가 장난을 치는 경우를 막을 수 있다. 여기에 noexec까지 주면 바이너리 파일을 실행하지 못하기 때문에 더 안전하게 시스템을 유지할 수 있다. noexec를 준 파티션은 루트도 바이너리 파일을 실행하지 못한다. quota를 이용해서 사용자가 제한되어 있는 하드 디스크 공간을 모두 차지하는 일을 막을 수 있다. 쿼터 설정은 여기서는 생략을 하겠다. async 옵션은 파일시스템에 대한 I/O를 비동기적으로 메모리에 버퍼링하는 것인데 기본값으로 되어 있다. 디스크 접근 시간을 최소화하기 위한 부분이므로 바꿀 필요가 없다. 이것을 sync로 바꾸면 메모리에서 변경된 자료가 바로 디스크 I/O에 반영이 되므로 속도가 얼마나 느려질지 장담할 수 없다.





2. 보안관련 커널 설정

커널과 관련된 옵션은 /proc에서 직접 값을 써 줄 수도 있고 /etc/sysctl.conf 파일을 이용하여 바꿀 수도 있다. 커널과 관련된 옵션은 그냥 무조건 사용하는게 아니라 자신의 환경을 생각해야하고 그 원리를 제대로 이해했을 때 사용하는 것이 좋다. 그런데 이 작업이 그렇게 만만한 작업은 아니고 운영체제와 네트웍에 대해서 많은 지식이 있어야 이해가 가능하다. 여기서는 주로 많이 사용되는 커널 옵션에 대해서 살펴보겠다. 커널과 관련된 설정은 sysctl 프로그램을 이용하여 편리하게 설정할 수 있다. sysctl -a 하면 현재 설정된 값이 나오고 -w 옵션을 이용하여 특정값을 저장할 수 있다. 재부팅후에도 계속 사용하려면 /etc/sysctl.conf 파일에 저장하여 사용하면 된다.



ㅇ ping 요청에 반응하기 막기 : ping 요청에 반응하는 것을 막을 수 있다. 외부에서 누구도 서버에 핑을 보낼 수도 없고 보내도 반응을 하지 않기에 보안상으로 도움이 될 수 있고 네트웍 보안을 향상시킬 수 있다. 그러나 원격에서 서비스를 관리하는 경우 간단하게 시스템의 상태를 확인할 수 있는 수단을 막는 결과를 초래할 수도 있다.

net.ipv4.icmp_echo_ignore_all = 1



ㅇ Broadcast 로 오는 핑을 차단하기 : 브로드캐스트 주소에 ping을 쏘는 것을 막는다. smurf 공격 방지용이다. 스머프 공격은 에코 리퀘스트 패킷을 어떤 네트웍의 브로드캐스트 주소로 보내면 해당 네트워크의 모든 호스트가 request 패킷에 응답하느라 다른 일은 하지 못하는 것을 말한다. 그렇지만 정작 문제는 해당 네트워크의 호스트가 아니다. 이 엄청난 개수의 컴퓨터들에서 일시에 어느 한 곳으로 ICMP echo replay 패킷을 보내면 패킷을 보냈던 시스템은 마비상태로 가기 싶다. 하지만 이미 출발지 주소 자체가 실제 패킷을 보낸 시스템이 아니라 공격을 할 호스트로 바뀌어있는 상태일 것이다. (IP 스푸핑)

net.ipv4.icmp_echo_ignore_broadcasts = 1



ㅇ IP 소스 라우팅 막기 : 라우팅과 라우팅 프로토콜은 몇가지 문제를 생성해낸다. RFC 1122에 따르면 목적지의 경로에 대한 세부적인 내용을 담고 있는 IP 소스 라우팅은 목적지 호스트에서도 같은 경로를 따라 반응을 해야 하다는 문제점이 있다. 크래커가 특정 네트웍에 소스 라우팅 패킷을 보낼 수 있다면 돌아오는 반응을 가로채서 상대방의 호스트와 신뢰받은 호스트처럼 속일 수 있다. 기본값이 1이므로 0으로 바꾼다.

net.ipv4.conf.all.accept_source_route = 0



ㅇ backlog 늘리기와 syncookie 기능 사용하기 : SYN Attack 은 서비스 거부 공격(DOS)의 하나로 공격할 시스템의 모든 자원을 소비하게 해서 재부팅하게 만든다. 두 호스트 사이에 TCP 연결이 이루어지는 것은 클라이언트 호스트가 TCP 헤더에 SYN 플래그를 on한 상태로 서버 호스트로 연결 요청을 하면서 시작된다. 그러면 요청을 받은 서버는 클라이언트로 SYN/ACK를 보내고 이를 받은 클라이언트는 서버에게 ACK를 보낸다. 그런데 클라이언트에서 ACK를 보내지 않고 있으면 어떻게 될까? 문제는 TCP에서 한 소켓에서 동시에 SYN 요청을 처리하기에는 한계가 있으며 이 한계가 백로그(backlog)라고 한다. 백로그는 연결 요청이 아직 완전히 처리되지 아니한 대기상태에 있는 큐의 길이이다. 이 백로그 큐가 꽉 차게 되면 이후 들어오는 SYN 요청은 무시되며 이러한 공격을 SYN 플러딩이라고 한다. 다른 유닉스 중에서는 들어오는 소켓 요청에 대하여 두 개의 큐를 만드는 경우도 있다. 이 경우 하나는 half-open 소켓(SYN을 받고 SYN/ACK를 보낸 상태)을 위한 큐이고 또하나는 완전히 연결이 되었지만 애플리케이션에서 accept() 콜을 기다리는 큐이다. (솔라리스의 경우 tcp_conn_req_max_q0, tcp_conn_req_max_q가 이에 해당한다) backlog도 무조건 늘리는 것이 좋은 것은 아니라는 보고가 있으며 1024 이상으로 설정시에는 커널 소스를 수정해야 한다. syncookie는 SYN Flood 공격에 대응하기 위해 나온 것이지만 large windows에서는 성능에 문제가 발생할 가능성이 있다고 한다. syncookie를 사용하려면 커널에서 지정이 되어 있어야 한다.

net.ipv4.tcp_max_syn_backlog=1024

net.ipv4.tcp_syncookies = 1



ㅇ ICMP redirect를 막는다 : 호스트에서 특정 목적지로 최적화되지 않은 라우팅을 한다고 할때 ICMP redirect 패킷은 호스트에게 정확한 라우팅 경로를 알려주는 역할을 한다. 그런데 크래커가 ICMP redirect 패킷을 위조할 수 있게 된다면 호스트의 라우팅 테이블을 변경할 수 있게 되고 원하지 않는 경로로 트래픽을 보내 호스트의 보안을 해칠 수 있다. 잘 디자인된 네트웍에서는 최종 목적지로의 redirect가 필요하지 않다. redirect 보내고 받는 것을 모두 없애야 한다. (**주1)

net.ipv4.conf.all.send_redirects=0

net.ipv4.conf.all.accept_redirects=0





ㅇ bad icmp 패킷 차단 : 어떤 router 들은 broadcast frame들로 거짓 응답을 보냄으로서 RFC 1122를 위반한다. 이러한 것들은 보통 커널 경고를 통해 로깅이 된다. 이것을 TRUE로 설정을 할경우 커널은 이러한 경고를 하지 않을 것이며, 로그 파일이 지저분해 지는 것을 피할 수 있다. 기본값은 FALSE 이다. 1로 설정하여 활성화를 하면 IP나 TCP 헤더가 깨진 bad icmp packet을 무시한다.

net.ipv4.icmp_ignore_bogus_error_responses = 1



ㅇ IP 스푸핑 방지하기 : DOS 공격에서 자신의 네트워크가 스푸핑된 공격지의 소스로 쓰이는 것을 차단한다. RFC182에 따른 IP spoof 방지를 위한 설정이다.

net.ipv4.conf.all.rp_filter = 1



ㅇ IP 스푸핑된 패킷 로그에 기록하기 : 스푸핑된 패킷, 소스 라우팅 패킷, redirect 패킷에 대하여 로그파일에 기록을 남긴다.

net.ipv4.conf.all.log_martians = 1



참고로 /etc/host.conf 파일에 nospoof 옵션을 주어 IP 스푸핑 방지를 위한 설정을 추가하기 바란다.



# cat /etc/host.conf

order bind, hosts

multi on

nospoof on





3. 최적화 관련 커널 및 운영체제 설정

커널과 운영체제에서의 최적화는 네트웍, 가상 메모리, 디스크 I/O, 각종 자원 제한 등으로 나눌 수 있다. TCP-IP에서 네트웍 최적화는 TCP/IP의 기본값을 줄여서 TCP/IP에서 더 많은 연결을 처리할 수 있도록 하는게 목적이다. TCP의 연결 해제 시간을 줄이고 불필요한 IP 확장기능을 사용하지 않는다.



ㅇ 연결종료시간을 줄인다 : tcp_fin_timeout은 close되었을 때 FIN-WAIT-2 상태로 얼마나 오랫동안 소켓을 유지하고 있는가를 지정한다. 상대 호스트에 문제가 생겨서 close를 못할 수도 있고 예상치 않게 죽을 수도 있다. 커널 2.4에서 기본값은 60초인데 커널 2.2에서는 180초였다. 커널 2.2와 같이 더 늘릴 수는 있지만 시스템이 웹서버의 용도라면 수많은 소켓들 때문에 메모리에 문제가 생길 수 있다. FIN-WAIT-2 소켓은 최대 1.5K의 메모리를 사용하기 때문에 FIN-WAIT-1보다는 위험이 덜하지만 더 오래 가는 경향이 있다.

net.ipv4.tcp_fin_timeout = 30



ㅇ keepalive 시간 줄이기 : tcp_keepalive_time은 keepalive가 활성화되어 있을 때 얼마나 자주 keepalive 메시지를 보내줄지 정한다. 이는 HTTP 1.1의 연결지속 옵션과 관계없이 TCP 자체에 있는 기능이다. TCP 접속은 한쪽에서 다른 쪽으로 데이터를 전송할 필요가 없는 경우에는 트래픽이 전혀 발생하지 않으므로 휴지 상태인 접속의 경우에는 폴링이나 네트워크 부하가 없다. 그런데 더 이상 사용하지 않는 네트워크 접속을 위해 서버에서는 메모리 버퍼를 유지하고 있는 것으로 자원을 낭비하는 것이다. 사용자가 모뎀을 꺼버리거나 컴퓨터의 전원을 끄는 경우도 이에 해당한다. 기본값은 2시간(7200초)인데 처리량이 많은 웹서버인 경우에는 사용자가 페이지를 읽고 얼마나 오래 머무는지를 판단하여 이 대기시간을 최대 시간 간격으로 설정하면 되며 보통 30분정도면 적당하다.

net.ipv4.tcp_keepalive_time = 1800



ㅇ 열수 있는 포트 늘리기 : ip_local_port_range 옵션은 TCP와 UDP에서 사용할 수 있는 로컬 포트의 범위를 지정한다. 두가지 숫자로 이루어져 있는데 앞의 번호는 시작포트이고 뒤의 번호는 마지막 로컬 포트 번호이다. 메모리양에 따라 기본값이 조정되는데 128M 이하일 경우에는 1024-4999, 128M 이상일 경우에는 32768-61000 이다.

net.ipv4.ip_local_port_range = 32768 61000





ㅇ 컴파일 옵션 변경

x86 프로그램에서 최대의 성능을 내기 위해서는 컴파일 옵션에 -O9 플래그를 지정할 수 있다. 대부분의 프로그램은 Makefile에 -O2를 지정하는데 -O9는 코드를 가장 최고로 최적화하는 것이다. 프로그램의 크기는 커지지만 속도가 빨라진다. 그렇지만 CPU가 686이상에서만 성능 향상이 있다. -mcpu=cpu_type -march=cpu_type을 사용하면 각 CPU에 최적화된 코드가 생성된다. 이럴 경우에는 프로그램이 해당 CPU에서만 돈다는 단점이 있다. gcc에서 -S 옵션을 이용하면 어셈블리어로 번역되어 결과가 출력되는데 최적화를 할수록 어셈블리 코드가 짧게 변하는 것을 볼 수 있다. 이외에도 gcc를 egcs로 바꾸어서 사용할 수 있다. 이외에도 최적화된 컴파일을 하기 위해 몇가지 추천하는 옵션이 있었지만 커널 컴파일 등에서 제대로 부팅이 되지 않는 경우가 있어 여기서는 일반적인 옵션에 대해서만 소개를 하였다.



ㅇ bdflush 제어 : bdflush는 가상 메모리(VM) 서브시스템의 활동과 관련이 되어있는데 bdflush 커널 대몬에 대한 제어를 한다. bdflush는 블럭 디바이스 입출력 버퍼를 관리하는 커널 대몬으로 주로 버퍼를 비우는 역할을 맡고 있다. 하드 디스크에 쓰여질 데이터는 바로 하드 디스크로 저장되는 것이 아니라 중간에 버퍼를 통한다고 앞부분에서 설명을 하였다. 주기적으로 버퍼의 내용을 하드 디스크에 기록하는 형태로 하드 디스크 접근을 최대로 줄여서 파일 시스템의 성능을 높이기 위한 것이다. bdflush는 9개의 값으로 구성이 되어있으며 기본값은 커널 2.2에서는 40 500 64 256 500 3000 500 1884 2 이었다가 커널 2.4에서는 구성이 약간 바뀌었다. 기본값만 바뀐 것이 아니라 순서도 바뀌었다. 순서가 nfract, ndirty, nrefill, dummy1, interval, age_buffer, nfract_sync, dummy2, dummy3 이며 기본값은 “30 64 64 256 500 3000 60 0 0” 이다.

커널 소스는 /usr/src/linux/fs/buffer.c이다. 여기에서 중요한 부분은 앞의 세가지이다. nfract는 전체 버퍼 캐쉬중에서 더티 버퍼의 비율을 나타낸다. 여기서 더티는 프로세스에서 변경이 되어 하드 디스크에 쓰여질 데이터를 나타낸다. (반대는 clean 버퍼이다) 수치를 높이면 디스크에 쓰는 동작은 최대한 지연이 되지만 메모리가 부족할 경우 I/O부하가 걸릴 수 있다. 또 어느 책에서는 이 수치를 100%로 지정하는 것을 추천하고 있는데 메모리와 관련하여 "do_try_to_free_pages failed for ...." 등의 문제가 발생한 경우가 생기고 있다. ndirty는 한번에 기록될 더티 블락의 최대 숫자이다. 수치를 높이면 I/O작업을 지연시킬 수 있는데 수치가 너무 낮으면 bdflush가 오랫동안 작동을 하지 않아 메모리 부족 현상이 생길 수 있다. nrefill은 refill을 호출할 때마다 가져올 클린 버퍼 개수이다. 때로는 버퍼가 메모리 페이지에 비해 다른 크기가 될 수 있기 때문에 미리 free 버퍼를 할당하는 것이 필요하다. 수치가 클수록 메모리가 낭비될 확률은 높지만 refill_freelist()를 실행할 필요는 적어진다.

vm.bdflush = 30 64 64 256 500 3000 60 0 0



ㅇ 버퍼 메모리 사용량 조절 : buffermem 은 전체 메모리에서 버퍼 메모리 사용량을 % 비율로 조절한다. 기본값은 2 10 60으로 되어 있다. 여기서 실제 사용하는 값은 제일 앞의 한 개만 사용하며 전체 메모리에서 버퍼로 사용할 수 있는 최소량을 %로 나타낸다.

vm.buffermem = 2 10 60



위에서 말을 한 대로 가상 메모리와 관련한 설정은 시스템의 성능에 큰 영향을 미치지만 잘못 바꾸었을 경우에는 아예 시스템이 멎어버리는 현상이 생길 수가 있다. 테스팅 서버에서 조금씩 값을 바꾸어가면서 자신에게 맞는 설정을 찾아야 할 듯하다. 절대 현재 서비스되고 있는 시스템에서 테스팅을 하지는 말자.



ㅇ 스와핑 조절 : 가상 메모리와 관련된 설정 중 시스템의 성능과 연관이 깊은 것으로는 freepages(vm.freepages)가 있다. freepages는 커널에서 스와핑을 언제 어떻게 할지에 대한 동작을 제어한다. 첫 번째 값은 min으로 free pages가 이 숫자 이하로 넘어갈 때만 커널에서 더 많은 메모리를 할당한다. 두 번째 값은 low로 free pages가 이 숫자 이하로 넘어갈 때 커널에서 스와핑을 시작한다. 세 번째 값은 high로 시스템에서 스와핑을 원활하게 하기 시작하기 위해서 최소한 가지고 있어야 할 free 메모리 페이지를 지정한다. 그러므로 항상 시스템에서 여기에 해당하는 페이지를 free로 가지고 있으려 한다. 아래는 기본 설정값이다.

vm.freepages = 383 766 1149





ㅇ 열 수 있는 파일 핸들 조정 : file-max는 리눅스 커널에서 할당할 수 있는 최대 파일 핸들을 지정한다. “running out of file handles” 라는 에러 메시지가 나오는 경우에는 적절히 늘려주어야 한다. 이는 파일 오픈이 많은 사이트에서 나타나는 현상이다. 웹호스팅 업체의 경우 웹로그파일, 메일서버 관련파일 등을 제대로 관리하지 않으면 이런 경우가 생긴다. 불필요한 파일을 없애고 로그파일을 통합하여 처리한 후 이후에 별도로 도메인별로 분리하는 방법을 생각해 볼 수 있다. 4M당 256개의 파일을 할당해주면 된다. 메모리에 맞추어 설정해주면 된다.

fs.file-max = 8192



/proc/sys/fs에서 file-nr을 보면 현재 할당되어 있는 파일 개수를 알 수 있다.



# cat /proc/sys/fs/file-nr

696 158 32768



첫 번째 숫자는 현재 할당된 파일핸들, 두 번째 숫자는 그중 사용된 파일핸들이고 제일 뒤의 숫자는 파일핸들의 최대 숫자이다. 위에서 지정한 file-max 값이다.



시스템에서 현재 할당되어 있는 inode 개수는 inode-state 나 inode-nr의 제일 앞의 숫자를 보면 된다.



# cat /proc/sys/fs/inode-state

3457 6 0 0 0 0 0



ㅇ 프로세스 개수와 파일 개수 조정 : ulimit -a를 이용하여 사용자별 프로세스 숫자와 열수 있는 파일 개수를 제한할 수 있다. 커널 2.2에서는 동시에 생성 가능한 프로세스 수에 대한 제한이 있었는데 커널 2.4로 넘어오면서 이에 대한 제한이 없어졌다. (task.h에서 지정했는데 커널 2.4에는 파일이 없어졌다) 이제 유일한 제약은 메모리 한계이며 많은 수의 프로세스를 지원하기 위해 스케쥴러도 향상이 되었다. max user processes는 16383이며 open files는 1024이다. 슈퍼유저인 root에게는 max user processes 제한을 ulimit -n unlimited 라는 명령을 통하여 없앤다. 이후에도 계속 적용을 하려면 .bashrc 등에 지정을 하면 된다.



# ulimit -a

core file size (blocks) 0

data seg size (kbytes) unlimited

file size (blocks) unlimited

max memory size (kbytes) unlimited

stack size (kbytes) 8192

cpu time (seconds) unlimited

max user processes 16383

pipe size (512 bytes) 8

open files 1024

virtual memory (kbytes) 2105343



그런데 자원 한도를 변경한다고 하더라도 사용자가 소프트 한도를 변경할 수 있다. 또한 위의 모든 제한은 프로세스별로 되어 있는데 실제 작업은 여러개의 프로세스로 구성이 되어 있다. 그렇지만 코어 파일 크기의 한도를 제한하는 것은 유용한 일이다. 이를 보완할 수 있는 것이 PAM(Pluggable Authentication Module)을 이용한 limit.conf 파일의 활용이다. /etc/security 디렉토리에 있다. 이 파일을 이용하여 로그인한 사용자의 시스템 자원을 제한할 수 있다.



먼저 /etc/pam./login 파일에 아래 내용이 없으면 추가한다. limit.conf를 사용할 수 있도록 설정하는 것이다.



session required /lib/security/pam_limits.so



이제 /etc/security/limit.conf 파일을 필요에 따라 수정한다. 간단한 예를 들어 설명해보겠다.



* hard core 0

* hard rss 5000

* hard nproc 20

* hard cpu 5

* hard data 10000



코어 파일은 생성하지 못하고 최대 resident set size는 5M로, 프로세스는 최대 20개로 제한을 하였고 CPU 선점 시간은 최대 5분이며 최대 데이터는 10M이다. 계정을 가진 사용자가 아무런 제한없이 접속하여 메모리를 엄청나게 잡아먹는 프로그램을 만들었다고 해보자. (**주2)



$ cat memdown.c

#include

#include



int main()

{

char *memory;

int size=1024;

int m=0;

int i=0;



while(1)

{

for(i=0;i<1024;i++)

{

memory = (char *)malloc(size);

if(memory == NULL) exit(-1);

sprintf(memory, "메모리를 사랑해~\n");

}

m++;

printf("현재 %d M 소비\n",m);

}

exit(0);

}



$ gcc -o memdown memdown.c



그러면 memdown을 실행해보자. 절대 서버에서는 하지 말기 바란다.



아무런 제한이 없는 경우에는 메모리가 부족한 시스템에서는 현재 있는 메모리를 모두 잡아먹고 나중에는 스왑까지 전부 소비할 수가 있다. 이렇게 메모리 고갈, 프로세스 고갈 등을 막는데 유용하다.



여기서 제한을 걸 수 있는 사항은 다음과 같다.

- core : 코어파일 크기(KB)

- data : 최대 데이터 크기(KB)

- fsize : 최대 파일크기(KB)

- memlock - 최대 메모리잠김(locked-in-memory) 주소 공간(KB)

- nofile : 최대 열수 있는 파일수

- rss : 최대 상주셋(resident set) 크기(KB)

- stack : 최대 스택 크기(KB)

- cpu : 최대 CPU 시간(MIN)

- nproc : 최대 프로세스 개수

- as - 주소 공간 제한

- maxlogins - 최대 로그인 숫자

- priority - 사용자 프로세스 실행 우선순위



위에서 보았던 간단한 프로그램은 “username hard data 10000” 이렇게 간단한 한줄로 충분히 막을 수 있다. 다시 위의 프로그램을 실행해보면 9M까지 메모리를 사용한후 멈춘다.



$ ./memdown

현재 1 M 소비

현재 2 M 소비

현재 3 M 소비

현재 4 M 소비

현재 5 M 소비

현재 6 M 소비

현재 7 M 소비

현재 8 M 소비

현재 9 M 소비



maxlogin은 웹호스팅 업체에서 사용자들의 접속을 제한하는데 유용하다. 계정을 주고 몇십명이서 접속을 하도록 내버려두어서는 안되기 때문이다.



ㅇ 프로세스당 열 수 있는 파일 개수 : 위에서 설정을 바꾼다고 하더라도 프로세스당 열 수 있는 파일 개수는 1024로 되어 있다. 이를 수정하려면 커널 소스를 고쳐야하는데 커널 소스 디렉토리의 include/linux 디렉토리로 이동하여 fs.h 와 limits.h를 수정하면 된다. fs.h에서는 INR_OEPN 과 NR_OEPN을 원하는 수치로 조정하고 새로 컴파일을 하면 된다. 그런데 시스템에서 동시에 열 수 있는 파일수 이상으로 이 수치를 높이는 것은 불필요한 일이다. 그리고 굳이 수정하지 않더라도 일반적인 환경에서 하나의 프로세스가 1024개 이상의 파일을 여는 일은 드물다고 생각이 된다.
2005/07/04 14:23 2005/07/04 14:23

웹 퍼포먼스 튜닝

FAQ 2005/07/04 14:22
1. Web Performance Tunning 소개 및 정의
웹 서비스의 성능 최적화는 오직 서버측에서만의 튜닝을 생각하기 쉬운데 사실,
정확한 의미의 성능 최적화는 웹 서비스 이용자와 서버측 모두의 성능 개선에 있다.
이 글은 아파치 웹 서버나, 유닉스, 리눅스 그리고 웹 브라우저에 이르기까지
통합적인 웹 서비스 성능 개선 방안을 다루므로, 세부적인 웹 서버 구축에 대한
설명등은 포함하지 않았다. 그러나 글의 후반부에서는 가상 호스팅 관리자를 위한
아파치 웹 서버의 성능 개선에 필요한 요소나 관련 세부 옵션에 대한 언급을 하고
있다.
필자는 현재 인터넷 관리자를 위한 온라인 공동체라는 [ 넷킬러 웹 사이트 :
http://www.netkiller.com ]를 운영하고 있으며, 글에 대한 세부적인 질문과 의견은
해당 분야별 게시판을 이용하기 바란다. : - )

2. 기본적인 성능 개선 방안

가. 이용자측의 성능 개선 [익스플로러, 네스케이프]

웹 브라우저의 캐시를 최대한 활용한다.
브라우저의 옵션을 보면 다운 로드 받은 웹 문서를 브라우저의 캐시에 저장하여 다음
접속시 문서 변경 확인 여부 설정을 할수 있다.
여기서 가급적 자동(섹션당 한번)으로 설정해 준다. 익스플로러의 경우 캐시
사용량까지 미리 정해 줄수 있다.


Proxy 서비스를 활용한다.
프락시 서버는 이용자가 요청한 웹 페이지를 서버의 캐시에 임시로 저장하여 다음
요청이 있을시 해당 캐시 파일을 보내주어 속도의 개선 및 인터넷 부하를 줄여준다.

더 빠른 DNS 서버를 사용한다.
임의의 웹 사이트를 접속할 때 자주 볼수 있는 웹 브라우저의 접속 상황 메시지는
Contacting Host , Host Connected, File Transferred at 3.4kbps 등이다. 여기서
Contacting Host에서 Host Connected의 시간 간격이 비정상적으로 길다면 DNS 서버를
한번 체크해볼 필요가 있다. DNS 서버는 이용자가 인터넷 에 접속할 때마다 사용하는
서비스이므로, 가급적 자신의 ISP에서 제공하는 빠른 네임 서버를 쓰는 것이 좋다.

자신의 ISP가 제공하는 프로그램 배포 관련Mirror 사이트를 활용한다.
해외 인터넷 사이트에서 프로그램 다운로드등에 대한 부하를 감소시키기 위하여 국내
ISP들은 대부분 자사의 홈페이지아래 각종 Mirroring 사이트를 운영하고 있다. 예를
들어 tucows라는 인터넷 유틸리티 전문 사이트는 국내 isp에서 주기적으로 웹 사이트
전체를 복사하여 Mirror 사이트를 운영하고 있다.

자바 애플릿을 웹 브라우저에 미리 설치한다.
자바의 로딩 속도를 향상 시키기 위하여 필요한 자바 애플릿의 클라스 파일들을
이용자 웹 브라우저의 자바 라이브러리에 미리 설치한다. 가령 네스케이프의 경우
zip으로 압축된 라이브러리 파일을 열어 필요한 클라스 파일들을 추가하고 다시
zip으로 묵어주면 된다. 또는 임의의 디렉토리안에 클라스 파일들을 넣어 이용자가
직접 CLASSPATH에 해당 디렉토리를 추가하면 웹 서버로부터 애플릿을 다운받아 로컬에
설치된 코드를 참조하여 사용할 수 있다. 그러나 이 방법은 임의의 이용자를 위한
성능 개선안이 될순 없다.
[O’Reilly : Web Performance Tuning 인용]

더 빠른 모뎀과 시스템 그리고 더 나은 그래픽 카드를 구입한다.
모뎀은 인터넷 속도를 좌우하므로 빠를수록 좋으며 그래픽 카드가 좋을수록 화면에
브라우징하는 속도가 개선될 수 있다.

나. 서버측의 성능 개선 [ 웹 서버 ]

더 빠른 인터넷 전용선을 확보한다.
성능을 개선할수 있는 가장 확실한 선택은 바로 더 빠른 전용선을 확보하는 것이다.
그러나 비용면에서 월 유지비가 많이 들어 가급적 사용량에 따라 최적의 전용선
속도를 선택하는 것이 현명하다. 인터넷 전용선 사용량은 Unix, Linux 용 네트워크
모니터링 프로그램으로 널리 알려진
MRTG(http://ee-staff.ethz.ch/~oetiker/webtools/mrtg/mrtg.html)를 가 많이 쓰인다.
이미 국내 모 ISP에서는 전용선 가입자에 대한 MRTG 및 보안 체크 서비스까지
제공하고 있다. MRTG에서 자사의 인터넷 사용량(IN/OUT)이 전용선 속도의 80%
이상까지 다다르면 패킷 손실이 생겨 폭주 및 과부하가 자주 발생할수 있으므로,
전용선 업그레이드를 고려할 필요가 있다.


최적의 Subnet Mask를 설정한다.
라우터의 경우 불필요한 Subnet Mask를 줄여줄 필요가 있다.
가령 같은 라우터에 연결된 컴퓨터들의 IP address 갯수가 30개밖에 되지 않으면서
굳이 subnetmask를 255.255.255.0로 잡아 사용하게 되면 나머지 225개의 D class IP
address를 검색하느라 라우팅 속도가 느려질 수 있다.

웹 서비스만을 위한 단독 시스템을 확보한다.
웹 서버와 메일 서버, 그리고 DNS 서버에 이르기까지 하나의 시스템으로 사용하는
경우가 많은데, 이것은 웹 서비스로는 치명적이다. 보안 문제가 생길수도 있으며, 웹
서버의 부하에 의해 다른 서비스에 차질이 생길수 있다. 물론, 그 반대의 경우도
가능하다. 가급적이면 웹 서버 시스템을 따로 구성하는데, 웹 서버 시스템의 경우
보통 RAM 용량을 충분히 확보하고 하드 드라이브는 빠른 스카시 타입을 사용함으로써
IO의 부하를 줄여 속도 향상을 가져올 수 있다. 연산이 과도한 CGI 프로그램이나
대용량의 DB 서버가 없다면 CPU까지는 굳이 업그레이드 할 필요는 없다. ^^

운영체제의 각종 제한 수치를 조정한다.
Linux의 경우 Sun과 같이 대용량 시스템이 아닌 PC급 서버를 기본 플렛폼으로
개발되고 서비스되는 까닭에 OS의 기본 설정이 Solaris에 비해 낮게 설정되어 있다.
결국 Linux에서 과부하를 이겨내기 위해서는 Linux 커널의 수정이 불가피하다. 그리고
운영체제에서의 TCP 재전송 최대 대기 시간 (TCP retransmit timeout)을 증가시킬
필요가 있다. 보통200 msec로 잡혀 있는데 느린 인터넷 환경에서는 200msec의 경우
TCP의 재전송 최대 대기 시간으론 부족할 수 있다. 이 외에도 웹 서버의 성능을 위해
각종 프로세스 관련 제한 수치를 조정할 필요가 있다.

웹 서버 프로그램의 성능을 최적화 한다.
웹 서버의 config 파일등에서 성능 향상을 위한 적절한 설정이 필요하다. 특히 아파치
웹 서버의 경우 기본 디폴트 값이 중 대형 웹 서비스를 하기엔 부족한면이 없지 않다.
최신 버전으로 업그레이드를 거듭할수록 더 많은 부하 및 속도에 대한 향상이 이루어
지므로, 가급적이면 최신 버전으로 업데이트를 해주는것도 좋은 방안이 될것이다. 이
글의 후반부에서 아파치 웹 서버에 대한 자세한 튜닝을 언급할 것이다.

자주 사용되는 cgi 결과들은 FILE(HTML…) 로 만든다.
이용자가 자주 실행하는 CGI 프로그램에 대한 결과나 DB 검색 결과등은 주기적으로
File(HTML…)로 만들 필요가 있다. 실행할 때마다 DB connect 에 의한 부하등을
획기적으로 줄일수 있기 때문이다. 현재 대부분의 검색 사이트들은 위와 같이 자주
읽히는 검색 결과들을 crontab을 이용하여 주기적으로 File로 만들어 두고 있다.

웹 서버를 분산 시킬 필요가 있다.
그래도 과부하에 의해 서비스가 지연될 경우에는 부하가 많이 걸리는 웹 사이트들을
따로 분리할 필요가 있다. 또는 DNS에서의 Round Robin 설정을 통해 이용자가
nslookup할때마다 다수의 IP 주소를 순차적으로 보내어 같은 웹 페이지를 가진
여러대의 웹 서버로 접속을 분산하면 그만큼 부하가 줄어들 것이다. 이외에도 전문
로드 밸런싱 소프트웨어를 이용하면 웹 서버의 접속 분산에 있어서 보다 지능화된
연결이 가능하다. 가령 DNS에서의 Round Robin 방식의 경우 단순히 IP 주소만을 달리
말해줄 뿐이지만, 전문 로드 밸런싱 서버를 이용하면 그때 마다의 각 웹 서버별
부하량을 체크하여 가장 부하가 적은 웹 서버로 접속을 유도할 수 있다. 물론, 접속
분산에 대한 스케줄 설정까지 가능하다. 유명한 무료 개인 홈페이지 제공 업체인
Geocity의 경우 지능형 로드 벨런싱 서버를 이용하여 수십대의 웹 서버를 분산시켜
운영하고 있다.

다. 웹 페이지의 성능 개선 [ HTML 문서]

HTML 문서의 용량을 최소한으로 줄인다.
여기서 말하는 HTML 문서의 용량은 웹 페이지에 속하는 각종 이미지등을 모두 고려한
용량을 말한다. 가령 이미지가 너무 크거나 웹 페이지의 소스가 필요없이 길경우
전체적인 웹 페이지의 용량은 증가하기 마련이다. 가령 56kbit/sec 모뎀의 이용자는
최대 초당 7 kbyte/sec 의 용량을 받을 수 있으므로, 웹 페이지의 전체 용량이 약
70Kbyte를 넘어설 경우 최소 10초 이상의 다운 로딩 시간이 필요하게 된다. 국내 및
해외 유명 웹 사이트들은 그 용량이 보통 60K 이하이다.

웹 브라우저의 캐시 기능을 활용한다.
전체적인 웹 페이지의 용량을 고려할 때 가급적 70K를 넘지 않는 것이 좋다고 했는데,
용량이 큰 이미지가 있다면 바로 앞의 용량에 여유가 있는 웹 페이지안에 픽셀 크기를
1로 잡아 점으로 처리하여 이용자의 브라우저 캐시에 미리 넣어 두는것도 좋은
방법이다. 물론, 목적 페이지에 도달하면 브라우저의 캐시에 저장된 이미지가 단숨에
로딩된다.

3. 웹 부하량 체크 방법

제대로된 튜닝을 하기 위해서는 우선 자신의 네트워크 및 시스템에 대한 부하를
체크할 필요가 있다. 여기서는 네트워크 및 웹 서버에 대한 종합적인 부하량 측정
방안을 알아보겠다.

가. 네트워크 부하량 체크

Traceroute (ftp://ftp.ee.lbl.gov/traceroute.tar.Z)
네트워크 연결 경로에 대한 라우팅 홉(HOP)지점마다의 주소와 그 속도를 측정할수
있는 유틸리티로 Linux의 경우 기본적으로 설치되어 있다.
- Sample -
[root@ns named]# traceroute -q 3 www.netkiller.com
## www.netkiller.com 에 대한 각 지점간 연결 속도를 3번씩 측정

traceroute to www.netkiller.com (210.103.183.35), 30 hops max, 40 byte packets
1 204.252.144.146 (204.252.144.146) 1.465 ms 1.433 ms 1.419 ms
2 210.116.126.41 (210.116.126.41) 17.030 ms 17.389 ms 357.989 ms
3 203.235.126.250 (203.235.126.250) 132.341 ms 18.414 ms 19.626 ms
4 210.103.227.38 (210.103.227.38) 203.928 ms 206.006 ms 179.701 ms
5 210.103.183.35 (210.103.183.35) 243.521 ms 24.088 ms 20.738 ms

Ping
양 끝단의 전체적인 속도 및 패킷 에러율을 체크하고자 할 경우 ping을 통해 일정량의
패킷을 보내어 다시 받을 때까지 걸리는 전체 시간과 패킷 손실율을 측정할 수 있다.

- Sample -
[root@ns named]# ping -c 10 www.netkiller.com
## www.netkiller.com 으로 총 10번의 ping을 실행한다.
PING www.netkiller.com (210.103.183.35): 56 data bytes
64 bytes from 210.103.183.35: icmp_seq=0 ttl=251 time=377.5 ms
64 bytes from 210.103.183.35: icmp_seq=1 ttl=251 time=417.6 ms
64 bytes from 210.103.183.35: icmp_seq=2 ttl=251 time=247.7 ms
64 bytes from 210.103.183.35: icmp_seq=3 ttl=251 time=367.1 ms
64 bytes from 210.103.183.35: icmp_seq=4 ttl=251 time=462.1 ms
64 bytes from 210.103.183.35: icmp_seq=5 ttl=251 time=957.6 ms
64 bytes from 210.103.183.35: icmp_seq=6 ttl=251 time=179.3 ms
64 bytes from 210.103.183.35: icmp_seq=7 ttl=251 time=77.0 ms
64 bytes from 210.103.183.35: icmp_seq=8 ttl=251 time=69.9 ms
64 bytes from 210.103.183.35: icmp_seq=9 ttl=251 time=498.8 ms

--- www.netkiller.com ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 69.9/365.4/957.6 ms

이 경우 Avg Round-Trip time이 365.4 ms 가 나왔다.
일반적으로 자사의 라우터와 ISP간에 약 1,000 번의 ping 결과에서 에러율이 3~4%
미만이어야 한다.

netstat , ifconfig
서버의 전체적인 네트워크 패킷 충돌율을 체크할 경우 netstat – i 나
ifconfig –a 의 결과에서 직접 계산할 수 있다.
- Sample -
[root@ns named]# ifconfig -a
lo Link encap:Local Loopback
inet addr:127.0.0.1 Bcast:127.255.255.255 Mask:255.0.0.0
UP BROADCAST LOOPBACK RUNNING MTU:3584 Metric:1
RX packets:99477 errors:0 dropped:0 overruns:0 frame:0
TX packets:99477 errors:0 dropped:0 overruns:0 carrier:0
collisions:0

eth0 Link encap:Ethernet HWaddr 00:10:5A:69:ED:92
inet addr:203.231.38.2 Bcast:203.231.38.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:813704 errors:0 dropped:0 overruns:0 frame:0
TX packets:646433 errors:0 dropped:0 overruns:0 carrier:0
collisions:17681
Interrupt:10 Base address:0xec00
여기서 lo는 로컬이므로 의미가 없으며, eth0란에서 Collisions수만큼의 충돌이 TX
packets (출력 패킷)에 대해 발생하였으므로 (17681/646433) X 100 을 통해 충돌율이
약 2.7% 라는것을 확인할수 있다.
보통 약 8% 이상의 충돌율을 보인다면 네트워크를 점검해 보아야 한다.

MRTG등의 네트워크 전문 유틸리티를 통한 부하량 체크
앞서 언급한 MRTG나 XNI 등의 부하량 및 패킷 전문 체크 프로그램을 이용하면 보다
시각적으로 패킷 에러와 속도를 측정할 수 있다.

- Sample http://www.xni.com -


나. 웹 성능 측정 방법들

time
time은 유닉스에서 프로그램의 실행 시간을 측정해주는 기본 명렁어이다. 웹 서버의
접속 및 첫 Source를 받아오는데 걸리는 시간을 정확히 측정하고자 한다면 아래와
같이 해볼수 있다.

- Sample -

[root@ns named]# time lynx -source http://www.netkiller.com > /dev/null
0.05user 0.05system 0:02.34elapsed 4%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (1651major+639minor)pagefaults 0swaps

이것은 www.netkiller.com 의 웹 사이트에 lynx 브라우저로 접속하여 초기 웹
페이지의 source를 가져오는데 걸리는 총 시간을 측정한 결과인데, 소스의 경우 굳이
볼 필요가 없으므로 /dev/null로 보냈다. User : 0.05 , System : 0.05 그리고 총
실행 시간이 0:02.34 (2초 34) 가 걸렸으므로, 실제 접속 시간은 2.34 –
(0.05 + 0.05) = 2.24초가 된다. 보다 정확한 측정을 위하여 위의 명령을 몇번
실행시켜 평균 시간을 측정하여야 될것이다. 특히 처음 접속은 DNS서버의 캐시를
이용하지 않기 때문에 가장 느릴수 있기 때문이다.


ApacheBench (http://www.zeustech.net/)
아파치 서버를 위한 전문 측정 툴이다. 이 유틸리티는 레드햇 5.2 정품 리눅스의 경우
기타 application CD에 RPM으로 함께 제공되고 있는데 상당히 세밀하게 웹 서버의
성능을 체크할수 있다.

- Sample -
[root@ns named]# ab
ab: wrong number of arguments
Usage: ab [options] [http://]hostname[:port]/path
Options are:
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make
-t timelimit Seconds to max. wait for responses
-p postfile File containg data to POST
-T content-type Content-type header for POSTing
-v verbosity How much troubleshooting info to print
-V Print version number and exit
-k Use HTTP KeepAlive feature
-h Display usage information (this message)
[root@ns named]# ab -n 10 http://www.netkiller.com:80/
This is ApacheBench, Version 1.2
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 1998 The Apache Group, http://www.apache.org/

Server Software: Apache/1.3.1
Server Hostname: www.netkiller.com
Server Port: 80

Document Path: /
Document Length: 11417 bytes

Concurrency Level: 1
Time taken for tests: 16.625 seconds
Complete requests: 10
Failed requests: 0
Total transferred: 115460 bytes
HTML transferred: 114170 bytes
Requests per second: 0.60
Transfer rate: 6.94 kb/s received

Connnection Times (ms)
min avg max
Connect: 65 209 390
Processing: 886 1453 2210
Total: 951 1662 2600

TOP
상당히 많이 사용되고 있는 Processor의 상태를 보여주는 유틸리티로 RAM, CPU, IO ,
SwapSpace의 사용량까지 %로 자세히 보여준다.
특히 웹 서버의 경우 CPU 사용량이 계속적으로 50% 이상이고 IO쪽의 사용량이 크면서
하드 드라이브의 소음(?)이 심해지면 이것은 웹 서버 부하에 대한 시스템의 부족한
RAM에 의해 CPU가 과도한 일을 하고 있는 것이므로 CPU가 아닌 RAM의 증가가
필요하다.


4. 아파치 웹 서버의 성능 개선안

실제 유닉스 환경에서 가장 많이 사용되고 있는 웹 서버가 아파치 웹 서버인데, 이제
마지막으로 아파치 웹 서버에서의 성능을 개선하기 위한 방안을 한번 알아보자.

가. httpd.conf , srm.conf , access.conf 파일의 최적화 수정
예전에는 httpd.conf, srm.conf, access.conf 파일로 나누어져 있었지만 요즘의
아파치 웹 서버는 모든 config 관련 내용이 httpd.conf에 들어가 있다.

a. ServerType : StandAlone
이것은 이용자 접속 요청시 웹 서버의 Child Processor의 생성에 있어서 기존 Spare
Child Processor를 복사(Fork)하여 빠르게 대쳐할수 있도록 한다. 가령 100명의
이용자가 동시에 웹 서버로 접속할 경우 그때마다 config 파일을 참고로 하여 새로운
Child Processor가 만들어지는 inetd 방식보다 기존의 여유 Child processor를 fork
하여 대응하는 것이 훨씬 빠르고 효율적이다.
b. HostNameLookup off
이것은 접속자의 IP 주소에 대한 Reverse Lookup 을 방지한다. 대부분의 이용자들은
자신의 IP 주소에 대한 호스트, 도메인 이름이 없다. 그러므로 만일 HostNameLookup
on 을 하여 웹 서버가 매번 접속 요청자에 대한 네임 서버 검색 후 로그 파일을
작성하도록 하면 그 만큼 접속 시간과 부하량이 증가하게 된다.
c. Rotate log를 사용한다.
이용자가 접속할때마다 기록되는 access_log의 경우 한번 접속당 약 85Byte가
증가하여 하루 100만번의 접속으로 access_log 파일은 무려 약 405MByte가 증가한다.
이렇게 되면 접속때마다 log file을 access 하는데 상당한 시간과 부하가 발생된다.
그러므로 로그 파일을 일정 시간마다 초기화 함으로서 언제나 경량화 시켜줄 필요가
있는데, 이것은 httpd/support 디렉토리에 존재하는 rotatelogs라는 유틸리티를 쓰면
해결할 수 있다. 레드햇 리눅스 5.2 이상의 경우 syslog을 이용하여 log 파일을
관리하고 있다.
[ 형 식 : rotatelog logfilename time ]
httpd.conf의 TransferLog logs/access_log를 아래와 같이 수정
TransferLog “|/usr/loca/etc/httpd/support/rotatelogs
/usr/local/etc/httpd/logs/access_log 86400”
## 86,400초 (24시간)마다 access_log을 갱신한다는 뜻
참고 : error log 역시 위와같은 방법으로 수정 가능
d. ServerAlias를 이용한다.
가상 호스팅 추가시 ServerName www.domain.com 외에 ServerAlias *.domain.com
domain.com 를 아래에 추가하여 Alias 도메인의 가상 호스팅을 위해 불필요한
필드의 추가를 생략할 수 있다.
e. Error_log파일을 통합한다.
가상 호스팅의 경우 가급적 error_log 파일들은 하나의 error_log 파일로 지정해
관리한다. (운영체제의 동시 file open수 제한을 피하는 소극적 방법?)
f. KeepAlive on
이것은 접속 요청자에게 웹 서버 프로세스가 웹 페이지들을 전송할 때 내부의 여러
개체(그림파일) 전송까지 새로운 프로세스를 만들지 않고 담당 프로세스가 계속
접속을 유지 할 수 있도록 한다.
g. MaxRequestPerChild 를 증가시킨다.
프로세스가 일정 횟수의 클라이언트 요청을 처리하고 종료되는 추치인
MaxRequestPerChild 30을 시스템의 안정성에 따라 증가시켜준다. 보통 100 이상을
권한다. 만일 자신의 웹 서버 시스템이 상당히 안정적이라면 아예 1,000을 주기도
한다.
h. StartServer, Min, Max Spare Server 수를 가급적 증가시켜준다.
디폴트는 각각 5, 5, 10정도인데 웹 서버가 standalone 방식을 경우 새로운 접속
요청을 받으면 기존의 spare Child Processor를 Fork 하여 새로운 Child Processor를
만들어 내므로 기본적으로 Spare 프로세스가 많을 수 록 폭주에 빨리 대처할 수 있다.
각각 비례적으로 증가시키는 것이 바람직하다.
예) StartServer 20, Min SpareServer 20, Max SpareServer 40 (4배씩 증가)
i. MaxClient 150
이것은 동시에 떠 있을수 있는 최대 Processor 수를 제한하는 것인데, 이것은 결국
최대 동시 이용자수 제한과 같다. 물론, http 프로토콜의 특성상 접속자체가
비연결성을 가지므로, 150정도가 충분할수 있으나 필요시 더 늘려줄수도 있다. 그러나
OS에서의 기본 제한 수치를 넘어설 수 있으므로, 주의 해야 한다. 가령 nobody가
만들어낼수 있는 최대 프로세스의 개수가 150개로 OS에서 제한하고 있는데
MaxClient를 그 이상으로 조정하면 문제가 생길수 있다.

나. 운영체제(OS)의 각종 제한 설정 확인
앞서 언급한 각 Processor 수치와 가상 호스팅을 위한 동시에 Open되는 Log 파일의
개수를 확장하기 위해서는 해당 웹 서버의 운영체제의 자원에 대한 제한 수치를
확인할 필요가 있다.

ulimit
ulimit은 운영체제의 각종 Limit을 확인하는 명령어로서 아래의 수치를 확인할수
있다.
The Max number of system processess
The Max number of processes per user
The Max number of open files (can have open files)

웹 서버는 보통 nobody라는 이용자 권한을 가진 ChildProcessor에 의해 서비스된다.
가령 레드햇 리눅스의 경우 6.0 버전 이상부터 그 기본 수치가 상당히 증가되었는데,
이전 5.2버전의 경우 ulimit –a 으로 아래의 결과를 보인다.

[root@ns named]# ulimit -a
core file size (blocks) 1000000
data seg size (kbytes) unlimited
file size (blocks) unlimited
max memory size (kbytes) unlimited
stack size (kbytes) 8192
cpu time (seconds) unlimited
max user processes 256
pipe size (512 bytes) 8
open files 256
virtual memory (kbytes) 2105343

여기서 max user processes가 256 정도인데, 아파치 웹 서버의 conf 파일에서
MaxClient 를 256 이상 설정하면 OS의 기본 설정을 초과하므로 문제가 발생할 수
있다.
OpenFiles 역시 256이므로, access_log, error_log를 각 가상 호스팅 이용자에게 따로
부여할 경우에는 웹 서버의 log 파일의 총 개수가 256보다 아래여야 할것이다. 물론
이용자가 cgi 프로그램을 동시에 실행하여 open되는 file수도 고려해야 한다.

참고로 ulimit는 OS의 기본 설정을 확인하거나 기본 수치에 대해 줄일 때 사용할수
있다. 그러므로 ulimit을 통해 각 분야별 수치를 증가시키는 것은 아무런 효과가
없다.
OS의 기본 제한 수치를 증가시키고자 한다면 리눅스의 경우 별도의 커널 수정과 커널
컴파일이 필요하다. 그러나 레드헷 6.0부터 커널의 각종 설정들이 상당히 증가되어
(예 : openfiles, max user processes 가 1024로 증가) 이젠 굳이 커널을 수정하여
컴파일할 필요가 없다.


- 참고 자료 -

Apache Server Survival Guide [SAMS-Net]
Web Performance Tuning [O’REILLY]
TCP/IP [O’REILLY]
http://www.apache.org Web Site
http://www.linux.org Web Site
2005/07/04 14:22 2005/07/04 14:22