2011년 8월 30일 화요일

적절한 서버측 스크립트 언어 선택하기

http://www.ibm.com/developerworks/kr/library/l-script-survey/index.html

적절한 서버측 스크립트 언어 선택하기

동일한 기본적인 작업을 다섯 가지 언어가 구현하는 방법
Craig McElwee, Independent Web developer
요약: Perl, PHP, Python, Tcl 및 Java 서블릿과 같은 인기 있는 다섯 가지 스크립트 툴(script tools)을 동일한 6개의 일반적인 서버 사이드(sever-side) 태스크에 적용시켜 비교해 본다. 각각의 문법(syntax)을 나란히 조사하고 어떻게 각 언어가 특정한 태스크를 처리하는지에 대해 평가 할 수 있다. 서버측 스크립트 언어를 사용해본 적이 없거나 이들 언어 중 일부만 사용해 보았다면 이들 언어들이 어떻게 닮았는지 확인할 수 있을 것이다. 이미 한 언어에 익숙해 있다고 하더라도 가용성, 기능성, 그리고 가독성에 있어서 다른 언어들이 어떤 특징을 갖는지 파악할 수 있을 것이다.
원문 게재일:  2000 년 5 월 01 일
난이도:  초급
페이지뷰: 762 회
의견: 0 (의견 추가)
1 star2 stars3 stars4 stars5 stars 평균 평가 등급 (총 2표)
현재 여러분의 웹사이트가 동적인 컨텐츠를 제공하고 있다고 가정할 때, 여러분은 사용 가능한 수많은 오픈 소스 서버측 스크립트 언어들 중 어떤 기준으로 하나를 선택한 것인가? 상부의 지시에 따라 선택했는가? 아니면 방법론적인 측면에서 각각의 언어의 장단점, 사이트의 요구사항, 데드라인, 여러분 자신의 스킬 등을 따져서 가장 나은 것을 선택한 것인가? 그렇지 않다면 이 글을 읽기 바란다. 사용하기 쉽고, 더욱 강력하고, 유지관리가 쉽고, 재미있는 스크립팅 솔루션을 찾게 될 것이다.
개인적으로 Java 서블릿, Perl, PHP, Python, Tcl등 5개 언어 선택에 있어서 나는 매우 과학적인 개념을 사용한다. 두 가지 유형으로 이 언어들을 분류할 수 있다. Common Gateway Interface(HTML을 리턴하는 외부 프로그램을 호출)와 일명 슈퍼 마크업(다른 언어 마크업 코드를 포함하는 HTML 페이지, 즉 HTML의 슈퍼셋(superset))이라고 하는 것이다. 여러분이 선호하는 서버 스크립팅 툴을 누락시켰다면 이해해주길 바란다. 모든 컴파일 가능한 툴을 가지고 있지 않기 때문이다.
각 언어들의 작동방법을 나란히 보여주기 위해, 각각의 언어로 다음과 같은 동일한 여섯 가지 태스크를 구현하였다.
6개의 태스크는 "실제" 데이터베이스 사용을 제외하고 기본적인 여러 기능을 포함하고 자질구레한 것은 약간의 수정하여 처리할 수 있다.
위의 기본 태스크를 위해서 각 언어 당 1 개씩, 모두 5개의 스크립트(참고자료)를 제공한다. 여기서 잠깐 시간을 내어 먼저 전반적으로 각 스크립트 언어를 검토한 후 상세한 분석에 들어 가도록 하겠다. 이 중 어떤 언어의 전문가가 이 코드를 본다면, 여기서 사용한 각 언어의 숙어가 매우 적음을 알게 될 것이다. 난 초보자의 편의를 위해 성능(performance)을 포기하고 가독성(readability)에만 신경을 썼다.
태스크별로 프로그램에 대해 상세히 알아보겠다.
태스크 1: time/date 불러오고 포맷하기
이것은 다른 어떤 언어보다도 스크립트 언어가 편리하다. 스크립트가 로그에 기록할 때 사람이 읽기 편한 포맷으로 time/date를 보여주길 원할 것이다. 이 기본 기능에 대한 두 가지 방법론적 입장(특히 Raw Date 와 Formatted Date)이 있다는 것은 매우 흥미로운 일이다.
Raw Date 방법은 Perl, Python, Tcl의 경우가 이에 포함되는데, 기준 시간 부터 초의 수를 리턴한 후 시간 및 날짜 포맷 함수를 거쳐 기술되는 방식이다. 날짜 데이터에 조작을 많이 가해야 하는 경우라면 Java 서블릿이나 PHP가 제공하는 완전히 포맷된 문자열로서의 날짜 데이터가 더 나을 것이다. 물론 예외 없는 규칙은 없으며 여기서 모든 것을 언급하지는 않겠다.
태스크2: 폼 필드의 데이터를 변수에 저장하기
각 언어는 환경에서 "name=value" 쌍을 불러오기 위해 자체 고유 방법과 숙어가 있다. 대부분 이 기본 트레이드 트릭은 각각 새로운 스크립트로 잘라서 붙여넣기의 단순한 방법이다. 또한 필요하면 수정도 하는데 많지는 않다. 웹 서버가 외부 프로그램을 호출할 경우는 클라이언트 브라우저에 의해 전송된 데이터는 그 프로그램 환경에 있는 특정 변수에 할당된다. 포스트 메소드를 사용하는 HTML 형식을 위해서 프로그램은 standard in(STDIN)에서 "Comment Length" 환경 변수에 있는 바이트 수를 읽어야 한다. STDIN에서 데이터를 보관하는 프로그램 변수는 지금 "name=value" 페어(pair) 문자열을 보관하고 URL 디코드 값, 단편으로 분리한 다음 프로세싱 변수로 입력할 필요가 있다.
다음이 그 예제이다(오류 검사가 없다는 것을 유의):
Python은 할당 변수 값에 직접 액세스를 허용하여 태스크를 단순화시키는 모듈을 사용한다 :
Python:
	form = cgi.FieldStorage()
		data = form['data'].value
		data2 = form['data2'].value

PPHP 및 Java 서블릿을 사용하는 경우 예상한 변수명을 받으면 첫번째 단계를 생략하고 바로 할당될 수 있다. 보다 더 일반적인 프로세스로 생성하여 처리하려면 접수된 변수 리스트 통해 프로세스를 반복하여 이 리스트를 동적 할당 시키는 것이 적합하다.

PHP:
	 $data = $HTTP_POST_VARS["data"];
	       $data2 = $HTTP_POST_VARS["data2"];

Java:
String data = request.getParameter("data");
		String data2 = request.getParameter("data2");

Perl의 표준 코드 블록은 모듈에 숨겨지는 것이 아니다. 여기서 모든"name=value" 쌍은 문자열에서 발췌되어 각각 @pairs array에서 슬롯에 할당된다.
Perl:
	read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
	@pairs = split(/&/, $buffer);

그 다음, 배열을 통해 이름 및 값의 변수 입력을 반복한다 :

	foreach $pair (@pairs) {
	   ($name, $value) = split(/=/, $pair);

브라우저로 제출된 값은 서버로 전송하기 위해 "URL encoded"된 것이므로, 먼저 모든 +s를 공백으로 변경한 다음에 모든 escape 코드를 원래의 값으로 변환시켜 "URL encoded"된 값을 가져야 한다.

	   $name =~ tr/+/ /;
		$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

	   $value =~ tr/+/ /;
	   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

마지막으로 = (equal)의 우측에 있는 무엇이든지 = sign 좌측의 명명된 변수 값으로 할당한다.

	   $Form{$name} = $value;
	}

Tcl 코드 방법도 이와 동일하다. 모든 프로그램에 이 논리를 적용시키게 되면 지루하면서 오류를 범할 수 있다. 다행히 어떤 CGI 프로그램에서 축어를 사용하여 이 블록을 잘라내어 붙여넣기(paste)할 수 있다.
태스크 3: 검색 및 대체
HTML 폼에 입력된 데이터는 빈번하게 수정이 필요하게 된다. 또한 사용자가 입력한 데이터를 이용하여 쉘 명령어를 실행하려면 절대적으로 필요 한 절차이다. 시스템의 무결성을 손상시키는 어떤 입력된 데이터라도 정리 작업이 필요하다.
Perl은 가장 강력한 정규식 엔진(regular expression engine)을 가지며 텍스트 조작에서 뛰어난 성능을 발휘한다. 여러분의 프로그램에 많은 텍스트 조작이 필요하다면 Perl을 사용하는 것이 좋을 것이다. Python, PHP, Tcl은 Perl의 화려한 구문(syntax)과 비교했을 때 각각의 인터페이스는 부자연스럽고 복잡해 보이긴 하나, 정규식의 검색 및 대체를 지원한다. 명확성을 위하여 각각 동일한 변수명 사용에 참조할 수 있는 수정된 스크립트 예제가 있다. 대소문자를 구별하지 않고 "cat"이라는 문자를 검색한 후 'data'변수의 값으로 할당한 후 다시 각각에 대해 "dog"단어로 대체된다 :
Perl:
	$data =~ s/cat/dog/gi;
	}


PHP:
$data = eregi_replace("cat","dog",$data);
	}


Python:
data = regsub.gsub("cat", "dog", data)
	}


Tcl:
regsub -all -nocase {cat} $data {dog} data	
	}


Java 언어는 정규식을 지원하지 않는다. 그러나 substring을 이용하여 쉽게 문자열 대체기능을 구현할 수 있다 :
Java:
	String findString = new String("cat");
			String replaceString = new String("dog");

			int x = data.indexOf(findString);
			while(x != -1)
			{
			  data = new String(data.substring(0,x) 
			         + replaceString 
			         + data.substring(x+findString.length()));
			  x = sourceString.indexOf(findString);
			}


별로 우아하게 보이지 않는다. 다른 언어로 같은 기능을 구현해보면 명확하고 간단 명료한 하나의 구문으로 표현할 수 있으므로 그것이 더 나아 보인다. 어쨌든 Sun은 표준 Java에 정규식 관련 기능을 넣지 않기로 결정했다.
태스크 4: 파일 쓰기
Java 서블릿을 제외한 나머지 언어의 경우 직관적이고 유사한 형태를 보인다. 각각의 언어에서 "file.txt"라는 파일을 추가(append) 모드로 여는 방법과 "out"이라는 변수에 파일 핸들로서 어떻게 할당하는지 비교해보라 :
Java:
PrintWriter out = new PrintWriter(new FileOutputStream("file.txt",true));


Perl:
open (OUT, ">>file.txt");


PHP:
$out = fopen("file.txt", "a");


Python:
out = open("file.txt", 'a')


Tcl:
set out [open "file.txt" a+]


PHP, Python, Tcl 코드의 단순성, 직관성 및 유사성을 주목하자. Perl 코드도 아주 비슷하면서 쉽고 직관적이다. Java에서의 "그것을 위한 클래스가 존재한다(There's a class for that)"라는 식의 접근법과 대조해 보라. 사실상 Java에는 이런 작업과 관련된 60개의 클래스가 존재한다. 언어 자체에서 알아서 수행하는 대신, 어떤 출력 스트림을 사용할 것인지, 그리고 모든 입출력 상황에서 어떤 특정한 프린트 라이터(print writer)를 사용할 것인지를 직접 결정해야 한다. 더구나 구문의 끝에 있는"true"가 추가 모드(append mode)의 의미로 받아들일 수 있을 만큼 직관적인가? PHP, Python, Tcl은 추가 모드의 경우 'a'를 사용한다. 그리고 파일 핸들링도 이해하기 쉬우며 몇 번의 타이핑으로 끝난다.
여기서 증명된 대로 파일 쓰기 태스크는 (파일 핸들 "out"에 변수 "joined"의 내용을 쓰는 것) 전체적으로 비슷하다 :
Java:
out.println(joined);


Perl:
print OUT "$joined\n";


PHP:
fwrite($out, $joined);



Python:
out.write (joined)



Tcl:
puts $out $joined



태스크 5: 파일 읽기
스크립트 언어에서 파일을 읽기 위해 여는 일은 매우 간단하지만, Java에서는 객체의 생성이 요구된다. 사실 파일 읽기는 각 언어마다 약간의 차이가 있고 언어 간의 원리적인 차이를 보여준다. Perl 및 Python은 각 행을 배열 또는 목록의 요소에 적절하게 할당하고 각 요소에 대해 이를 반복하여 처리함으로써 전체 파일 읽기를 용이하게 해준다. 다른 언어 툴은 한 개의 행을 읽고 이를 처리하고 나서 또 다른 행을 찾을 수 있게 되어 있다. 다음 사례는 가장 소수의 코드부터 최대수의 코드까지의 예이다. 각 사례에서 파일 핸들이 'in'이고 각 행은 STDOUT로 인쇄된다 :
Perl:
@lines = <IN>;
                  foreach $line (@lines){
                        print $line;
                  }

Note: Python 프로그래머들은 Perl은 추가적인 문자를 사용한다는 내용의 메일을 나에게 보내기 전에, "foreach" 대신 "for" 또는 아예 전체를 for (<IN>){ print }로 사용할 수도 있었다는 것을 알기 바란다.

Python:
lines = in.readlines()
                  for line in lines:
                        print line



PHP:
while (!feof($in)) {
                        $line = fgets($in, 4096);
                        print $line;
                  }



Tcl:
while {1} {
                        gets $in line
                        puts $line

                        if {$line == ""} {
                        break
                        }
                  }




Java:
do {
                        try {
                              line = in.readLine();

                              if (line != null){
                                    out.println(line);
                              }

                        }
                        catch(Exception e) {
                        	e.printStackTrace();
                        }
                  }




태스크 6: 콤마 구분 행(comma-delimited)을 변수로 분리
스트립트는 CSV 파일에서 행을 읽는다. 각 필드를 쉽게 개별 변수로(이 경우 a,b,c,d) 분리하는 방법은 무엇일까? Perl, PHP, Python은 구분자를 인수로 하여 문자열을 분리하는데 편리한 'split' 함수를 가지고 있다. Java 서블릿과 Tcl는 각 필드를 개별적으로 설정해야 한다. 이 예제에서는 문제가 없으나 각 행이 많은 필드를 가지는 경우에는 반드시 주의해야 한다.
Perl:
($a,$b,$c,$d) = split /,/, $lines[@lines-1];



PHP:
list( $a,$b,$c,$d ) = split( ",", $last, 4 );




Python:
a,b,c,d = splitfields(lines[-1], ',')




Java:
String a, b, c, d;

                  StringTokenizer st = new StringTokenizer(last, ",");
            a = st.nextToken();
            b = st.nextToken();
            c = st.nextToken();
            d = st.nextToken();




Tcl:
set fieldlist [split $last ,]
                  set a [lindex $fieldlist 0]
                  set b [lindex $fieldlist 1]
                  set c [lindex $fieldlist 2]
                  set d [lindex $fieldlist 3]




데이터 손상 주의
모든 입력 자료가 손상될 가능성 있다고 가정하고 최악의 사태를 대비해야 한다. 시스템 명령어를 데이터에 입력하여 해커들이 시스템 침입할 수 있다. 예를 들면 서버 스트립팅을 통해 사용자가 시스템에서 원격 프로그램을 실행할 수 있다. 시스템 침입에 대해 설명하는 것은 절대 아니다. 디렉터리 리스트를 불러오기와 같은 단순한 요청에서 발생될 수 있다는 것이다.
사용자에게 디렉터리명으로 "~" 또는 ".."과 같은 예상되는 입력 나열을 요청할 수 있다. 그러면 이 요청은 "ls ~" 같은 ls 명령어와 함께 쉘로 전송된다. 악의는 없지만 해커가 만약 "~;rm *"을 입력한다면? 쉘은 먼저 "ls ~" 다음에 "rm *"을 실행하면서 기꺼이 명령을 실행할 것이다.
의도한 바가 아니라 해도 그러한 데이터 손상에 주의를 하지 않으면 충분히 가능성이 있는 일이다. 예를 들면, Perl에서 알파벳과 숫자 또는 언더라인/별표/틸드 이외의 모든 것을 아예 제거할 수도 있다. 그런 경우에는 "ls ~;rm *" 명령은 "ls ~rm *"이 된다. 입력을 잘못해도 시스템에 큰 손상을 입히는 것이 아니라 단순한 오류만을 발생시킬 뿐이다.
적절한 언어 선택하기
자, 이제 교체하여 사용할 만한, 또는 현재 사용하는 것에 만족을 느낄 수 있을 만한 최상의 언어는 어떤 것일까? 사용하기에 최상의 언어란 가장 친숙한 언어이다. 별로 소용없는 대답 또는 완전 초보자에게는 쓸모없는 잔소리처럼 들릴 수도 있다. 간단한 사실은 Perl을 모르는 Tcl의 베테랑이라 하더라도 "다른 모든 사람들이 Perl을 사용할 줄 안다"는 이유로 전체 사이트의 CGI를 Perl로 작성하지는 못한다는 것이다. 더구나 개발 시간 확장이 필요한 것 외에도 매우 현실적인 위험이 도사리고 있다(우연히 보안 허점을 남기는 것 등). 그러나 잘 모르는 언어라도 바꿀만한 충분한 이유가 있다면, 중대한 웹 애플리케이션을 곧바로 작성하지 말고, 이전에 써왔던 스크립트를 새로운 언어로 포팅하는 작업부터 권유하고 싶다.
한가지 짚고 넘어가고 싶은 것은, 일반적인 생각과는 달리 Java 서블릿이나 PHP가 다른 스크립트 언어들보다 별로 빠르지 않다는 것이다. 이 언어의 엔진은 웹 서버의 일부로서 실행된다. 따라서 CGI 스크립트에서처럼 리소스를 요청할 때마다 새로운 프로세스 시작을 요구하지는 않는다는, 즉 Java 서블릿과 PHP가 다른 스크립트 언어보다 속도가 빠르다는 주장이 가능한 것이다. 그러나 위의 비교는 프로그램을 "CGI 방식"으로만 실행하는, 즉 Perl, Python 또는 Tcl 엔진을 서버에 탑재하지 않을 경우에만 사실이다. 이 언어들의 사용자는 깊이 들여다 볼 필요가 있는 모듈들이 존재하기 때문이다.
CGI 게임을 처음 접하려는 사람이라면, 이 몇 가지의 가능성에 대해 호기심을 가질 수 있다. 처음에 어떤 언어를 선택할 지에 대해 모든 프로그램을 검토해 보고 가장 적당한 언어를 검토해 본다. 직관적 또는 콘텍스트에서 쉽게 진행상황을 파악할 수 있는가? 스크래치부터 구성까지의 시도에서 더욱 편안하게 느껴지는 것이 어떤 것인가? dream과 speech에서 눈에 거슬리는 것이 어떤 것인가? 모두 무료이기 때문에 비용은 논의 대상에서 제외한다. 시스템에 웹 서버를 탑재하고 시작하자.
마지막으로 내가 Java 서블릿을 server-side 솔루션으로 비난한 것처럼 느껴질 수 있는데 그럴 의도는 전혀 없다. 대부분 다른 언어로는 server-side 애플리케이션을 잘 만들지 않는다. 또한 Java의 객체 지향적 문법 및 패키징의 오버헤드는 항상 개발 시간 및 노력을 투자할 가치가 있는 것은 아니다. 기타 언어 보다 Java 서블릿을 사용하는 것은 다음과 같은 실질적인 두 가지 이유 때문이다. 첫째, 여러분 회사의 시스템이 Java기반으로 되어 있고 server-side 프로그램밍 하는데 Java 프로그래머가 필요한 경우이다. 둘째, 여러분의 server-side 프로그래밍이 대규모의 복잡한 프로그램이 요구됨에 따라 "Java의 힘"이 필요하다고 판단 될 경우이다. 아는 채만 할 줄 아는 여러분의 보스가 이 같은 사항을 결정한 것이라면, 다른 한 언어를 사용하여 구축하고, 몇 주 동안 인터넷 서핑을 즐기다가, Java로 완성한 것이라고 나중에 보고하라.

참고자료
5개 언어로 구성된 홈 페이지:

CGI 성능 향상:
  • mod_perl로 Apache 모듈을 Perl에서 완전히 쓸 수 있다.
  • VelociGen은 서버사이드 성능 측정 툴이 있다.
필자소개
Craig McElwee는 웹 기반 애플리케이션 개발자이다.



UNIX 네트워크 분석

http://www.ibm.com/developerworks/kr/library/tutorial/au-unixnetworkanalysis/index.html

http://www.ibm.com/developerworks/kr/library/tutorial/au-unixnetworkanalysis/section2.html

UNIX 네트워크 분석

UNIX 시스템 네트워크 구성 이해하기
Martin Brown, Professional writer, 프리랜서
요약:  여러 가지 도구를 사용하여 네트워크에 대한 정보를 찾아볼 수 있습니다. 네트워크 레이아웃, 패킷의 이동 위치, 사용자가 수행 중인 작업 등을 확인하려면 네트워크 레이아웃과 네트워크에서 수행 중인 작업을 보여 주는 다양한 도구를 사용해야 합니다. 이 튜토리얼에서는 UNIX® 네트워크의 트래픽과 컨텐츠를 모니터링하는 기술과 네트워크의 문제점을 파악하여 진단하는 방법에 대해 설명합니다.
원문 게재일:  2009 년 5 월 05 일
난이도:  중급
PDF:  A4 and Letter (61 KB | 22 pages)Get Adobe® Reader®

페이지뷰: 4199 회
의견: 0 (의견 추가)
1 star2 stars3 stars4 stars5 stars 평균 평가 등급 (총 3표)
시작하기 전에
이 튜토리얼은 다양한 장치에서 실행되고 있는 서비스 및 시스템을 포함한 네트워크 구조 및 구성에 대한 정보를 효율적으로 검색 및 확인할 수 있는 방법을 찾고 있는 UNIX 시스템 관리자를 대상으로 한다. 이 튜토리얼을 이해하려면 UNIX 운영 체제, 네트워크 및 IP(Internet protocol)의 작동 방법에 대한 기본 지식이 있어야 한다.
이 튜토리얼의 정보
새 UNIX 시스템뿐 아니라 기존 UNIX 시스템의 복잡한 작동 방법을 이해하는 데 있어서 핵심이 되는 부분은 네트워크 구성이다. 문제점을 올바르게 식별하고 잠재적인 문제점을 예방하기 위해서는 네트워크의 여러 가지 특성을 숙지하고 있어야 한다. 몇 가지 기본적인 도구와 명령을 사용하여 단일 시스템의 구성에 대한 여러 가지 정보를 확인할 수 있으며, 이 기본적인 이해를 바탕으로 네트워크의 나머지 부분에 대한 구성도 이해할 수 있다. 또한 몇 가지 추가 도구를 사용하여 네트워크 내의 추가 시스템 및 서비스에 대한 정보를 확인함으로써 이해의 지평을 넓힐 수 있다.
이 튜토리얼에서는 UNIX 환경 내에서 몇 가지 기본 도구를 사용하여 시스템의 구성 정보를 확인한다. 이러한 도구와 출력되는 정보를 이해하게 되면 시스템 네트워크 구성과 네트워크의 작동 방법에 이해의 깊이도 깊어질 것이다. 또한 더 넓은 네트워크를 검색하여 네트워크의 세부 정보, 잠재적 보안 문제 및 문제가 발생했을 때 문제점을 식별 및 진단하는 데 도움이 되는 주요 정보를 제공할 수 있는 도구 및 솔루션에 대해서도 살펴본다.
16 | 다음

호스트의 네트워크 이해
네트워크를 이해하는 과정의 첫 번째 단계는 현재 사용 중인 시스템의 네트워크 구성을 이해하는 것이다. 이 단계에서는 현재 호스트의 IP 주소, DNS 구성 및 현재 호스트에서 연결하여 통신할 수 있는 시스템 등에 대한 여러 가지 기본적인 정보를 확인하게 된다.
구성 정보 찾기
작업 중인 시스템의 현재 구성을 보면 사용자의 환경에 대한 기본 정보를 파악할 수 있다. 우선 현재 시스템의 IP 주소와 네트워크 마스크를 확인할 수 있다. 이러한 두 값이 있으면 현재 시스템의 주소뿐만 아니라 현재 시스템에서 직접(예를 들어, 라우터를 사용하지 않고) 연결할 수 있는 네트워크 상의 다른 시스템을 확인할 수 있다.
IP 주소를 확인하기 전에 hostname 명령을 사용하여 시스템의 호스트 이름을 가져온다(Listing 1 참조).

Listing 1. 호스트 이름 가져오기
$  hostname
sulaco

-a 옵션을 지정하고서 ifconfig 명령을 실행하면 사용자의 시스템에 구성되어 있는 모든 네트워크 장치의 현재 구성 정보가 표시된다. 예를 들어, Listing 2에서는 Solaris 시스템에서 실행한 ifconfig 명령의 출력을 보여 준다.

Listing 2. Solaris에서 실행한 ipconfig의 출력
$ ifconfig -a
lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1
        inet 127.0.0.1 netmask ff000000 
pcn0: flags=201004843<UP,BROADCAST,RUNNING,MULTICAST,DHCP,IPv4,CoS> 
mtu 1500 index 2
        inet 192.168.1.25 netmask fffffc00 broadcast 192.168.3.255
lo0: flags=2002000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv6,VIRTUAL> mtu 8252 index 1
        inet6 ::1/128 
pcn0: flags=202004841<UP,RUNNING,MULTICAST,DHCP,IPv6,CoS> mtu 1500 index 2
        inet6 fe80::20c:29ff:fe7f:dc5/10 

이 출력을 보면 루프백 장치인 lo0이 있고, 이 장치의 주소가 로컬 호스트의 일반적인 주소인 127.0.0.1이라는 것을 알 수 있다. 또한 이 장치에 동등한 IPv6 주소도 있다는 것을 알 수 있다.
pcn0 장치는 네트워크 주소 192.168.1.25와 네트워크 마스크 fffffc00(255.255.252.0에 해당)으로 구성되어 있다. 또한 이 장치의 주소는 DHCP를 사용하여 설정되었다는 것을 알 수 있다(DHCP 플래그 목록 참조).
특히 네트워크 마스크가 중요하다. 왜냐하면 네트워크 마스크만 있으면 사용자가 직접 연결되어 있는 네트워크의 크기(등록된 IP 주소의 관점에서)를 확인할 수 있기 때문이다. 이 장치의 네트워크 마스크인 255.255.252.0은 클래스 C 주소가 4개임을 나타낸다(256(최대 호스트 수) - 252(마스킹된 호스트 수) = 4).
네트워크 마스크와 구성된 IP 주소를 결합하여 로컬 네트워크에 있는 IP 주소의 범위를 추측할 수 있다. 일반적으로 IP 블록은 전체 그룹별로 차례대로 분할되기 때문에 이 네트워크의 IP 주소 범위는 192.168.0.0부터 192.168.3.255까지이다. 4개의 클래스 C 주소로 구성된 네트워크 마스크를 사용하면 일반적으로 전체 범위(192.168.0.0-192.168.255.255)가 동일한 블록들로 분할되므로 주소 접두어가 192.168.1.x인 주소는 4개의 주소 블록 중 첫 번째 블록에 있다.
이 정보(및 세부 사항)는 운영 체제마다 각기 다른 방식으로 표시된다. Listing 3에서는 Linux® 시스템의 출력을 보여 준다.

Listing 3. Linux 시스템의 출력
eth0      Link encap:Ethernet  HWaddr 00:1d:60:1b:9a:2d  
          inet addr:192.168.0.2  Bcast:192.168.3.255  Mask:255.255.252.0
          inet6 addr: fe80::21d:60ff:fe1b:9a2d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2371085881 errors:36 dropped:0 overruns:0 frame:36
          TX packets:2861233776 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:913269364222 (850.5 GiB)  TX bytes:3093820025338 (2.8 TiB)
          Interrupt:23 Base address:0x4000 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:279755697 errors:0 dropped:0 overruns:0 frame:0
          TX packets:279755697 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:388038389807 (361.3 GiB)  TX bytes:388038389807 (361.3 GiB)

Listing 4에서는 Mac OS X™ 시스템의 출력을 보여 준다.

Listing 4. Mac OS X 시스템의 출력
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 
    inet 127.0.0.1 netmask 0xff000000 
    inet6 ::1 prefixlen 128 
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    inet 192.168.0.101 netmask 0xfffffc00 broadcast 192.168.3.255
    ether 00:16:cb:a0:3b:cb 
    media: autoselect (1000baseT <full-duplex,flow-control>) status: active
    supported media: autoselect 10baseT/UTP <half-duplex> 10baseT/UTP 
<full-duplex> 10baseT/UTP <full-duplex,hw-loopback> 10baseT/UTP 
<full-duplex,flow-control> 100baseTX <half-duplex> 100baseTX 
<full-duplex> 100baseTX <full-duplex,hw-loopback> 100baseTX 
<full-duplex,flow-control> 1000baseT <full-duplex> 1000baseT 
<full-duplex,hw-loopback> 1000baseT <full-duplex,flow-control> none
fw0: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 2030
    lladdr 00:17:f2:ff:fe:7b:84:d6 
    media: autoselect <full-duplex> status: inactive
    supported media: autoselect <full-duplex>
en1: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
    ether 00:17:f2:9b:3d:38 
    media: autoselect (<unknown type>)
    supported media: autoselect
en5: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    inet6 fe80::21c:42ff:fe00:8%en5 prefixlen 64 scopeid 0x7 
    inet 10.211.55.2 netmask 0xffffff00 broadcast 10.211.55.255
    ether 00:1c:42:00:00:08 
    media: autoselect status: active
    supported media: autoselect
en6: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
    inet6 fe80::21c:42ff:fe00:9%en6 prefixlen 64 scopeid 0x8 
    inet 10.37.129.2 netmask 0xffffff00 broadcast 10.37.129.255
    ether 00:1c:42:00:00:09 
    media: autoselect status: active
    supported media: autoselect

일반적으로 모든 운영 체제의 출력에서 연결된 네트워크 장치의 인터넷 주소와 네트워크 마스크를 볼 수 있다. 여러 개의 네트워크 장치가 있는 경우에는 각 장치에 대한 정보가 출력에 표시되며 사용자는 단 하나의 시스템에서 폭넓은 범위의 다양한 네트워크 및 시스템에 연결할 수 있다.
이름 확인 서비스 찾기
현재 시스템의 구성을 확인하는 과정의 다음 단계는 다른 시스템의 서비스에 액세스할 때 사용자의 시스템 이름 및 도메인 이름을 IP 주소로 변환하는 이름 서비스 시스템의 구성과 관련된다.
대부분의 시스템에서 이 구성은 /etc/nsswitch.conf 파일을 통해 설정하며, 이 파일에는 다양한 이름 서비스(호스트, 사용자 등)의 목록과 이름 확인에 사용할 다양한 서비스(DNS, NIS 또는 로컬 파일)의 순서가 들어 있다. Listing 5에서 이 파일의 예를 볼 수 있다.

Listing 5. 이름 서비스 시스템 확인하기
passwd:     files
group:      files
hosts:      files dns 
ipnodes:    files dns
networks:   files
protocols:  files
rpc:        files
ethers:     files
netmasks:   files
bootparams: files
publickey:  files
netgroup:   files
automount:  files
aliases:    files
services:   files
printers:   user files
auth_attr:  files
prof_attr:  files
project:    files
tnrhtp:     files
tnrhdb:     files

예를 들어, Listing 5에서 호스트 이름 정보는 시스템에 있는 로컬 파일(예: /etc/hosts)과 DNS(Domain Name System)를 차례대로 검색하여 확인한다.
DNS가 구성되어 있는 경우에는 /etc/resolv.conf 파일에서 이름을 IP 주소로 변환하는 데 사용 중인 시스템을 확인할 수 있다. Listing 6에서는 이 파일의 예제를 보여 준다.

Listing 6. 이름을 IP 주소로 변환하는 데 사용 중인 시스템
domain example.pri
nameserver 192.168.0.2
nameserver 192.168.0.3

이 정보는 해당 시스템의 정보를 직접 쿼리하려는 경우에 유용하게 사용할 수 있다. dig 및 nslookup과 같은 도구를 사용하여 이름 및 IP 주소의 이름 서비스 및 확인에 대한 정보를 추출할 수 있다.
경로 검사하기
네트워크의 외부에 있는(즉, 현재 IP 주소와 비교하여 네트워크 마스크의 범위에서 벗어난) 호스트는 다른 시스템으로 전달되기 위해 라우터로 전송된다. 라우터는 부서 간, 다양한 물리적 사이트 및 인터넷과 같은 공용 외부 사이트를 포함한 네트워크의 모든 레벨에서 사용할 수 있다.
사용자의 시스템에서 '로컬' 네트워크의 외부에 있는 시스템과 통신하려는 경우 netstat 명령을 사용하여 연결된 시스템 또는 라우터를 구별할 수 있다. 예를 들어, 아래 Listing 7에서는 Solaris 시스템에서 실행한 netstat 명령의 출력을 보여 준다.

Listing 7. netstat 명령
$ netstat -r

Routing Table: IPv4
  Destination           Gateway           Flags  Ref     Use     Interface 
-------------------- -------------------- ----- ----- ---------- --------- 
default              voyager.example.pri  UG        1        139 pcn0      
192.168.0.0          solaris2.example.pri U         1        447 pcn0      
solaris2             solaris2             UH        1         35 lo0       

Routing Table: IPv6
  Destination/Mask            Gateway                   Flags Ref   Use    If   
--------------------------- --------------------------- ----- --- ------- ----- 
fe80::/10                   fe80::20c:29ff:fe7f:dc5     U       1       0 pcn0  
solaris2                    solaris2                    UH      1       0 lo0   

기본 경로는 현재 네트워크의 외부에 있거나 특정 IP 주소 또는 IP 주소 범위의 다른 경로에서 아직 처리되지 않은 패킷을 라우팅하는 데 사용되는 게이트웨이(라우터)를 보여 준다.
현재 이름 서비스가 작동되지 않거나 올바른 정보를 리턴하지 않는 경우에는 이 정보를 확인할 필요가 있으며 -n 옵션을 지정하여 이름 대신 IP 주소를 사용하여 이 정보를 표시할 수 있다.
지원되는 서비스 검사하기
netstat 명령은 현재 호스트에서 공유 또는 노출하고 있는 서비스를 확인할 때도 사용할 수 있다. 이러한 서비스에는 DNS, NFS, 웹 서비스 및 기타 정보를 포함한 모든 네트워크 서비스가 포함된다. 표시된 정보는 클라이언트 연결을 대기하기 위해 '수신 중' 상태로 열려 있는 포트 또는 이미 열려 있는 상태에서 클라이언트와 통신 중인 포트를 기반으로 한다.
이 정보를 통해 서비스가 실행 중인지 여부를 확인할 수 있을 뿐 아니라 표준 보안 검사의 일환으로 시스템이 공유되고 있거나 필요 이상의 위험에 노출되어 있는지 여부를 확인할 수 있다.
Listing 8에서는 이 명령의 출력 예제를 볼 수 있다. 이 예제에서는 -a를 사용하여 새 연결에 대해 설정된 상태로 수신 중인 모든 열려 있는 포트 및 서비스를 표시한다. 기본적으로 netstat 명령은 열려 있는 UNIX 도메인 소켓을 보여 주며 이러한 소켓은 현재 시스템에만 액세스할 수 있다. 간단히 설명하기 위해 이 출력 예제에서는 이들 소켓을 생략했다.

Listing 8. -a를 사용한 출력
$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 *:imaps                 *:*                     LISTEN     
tcp        0      0 *:nfs                   *:*                     LISTEN     
tcp        0      0 *:vmware-authd          *:*                     LISTEN     
tcp        0      0 localhost:10024         *:*                     LISTEN     
tcp        0      0 localhost:10025         *:*                     LISTEN     
tcp        0      0 *:mysql                 *:*                     LISTEN     
tcp        0      0 *:imap                  *:*                     LISTEN     
tcp        0      0 localhost:783           *:*                     LISTEN     
tcp        0      0 *:sunrpc                *:*                     LISTEN     
tcp        0      0 bear.example.pri:http     *:*                     LISTEN     
tcp        0      0 *:cisco-sccp            *:*                     LISTEN     
tcp        0      0 *:47506                 *:*                     LISTEN     
tcp        0      0 *:34452                 *:*                     LISTEN     
tcp        0      0 172.16.217.1:domain     *:*                     LISTEN     
tcp        0      0 192.168.92.1:domain     *:*                     LISTEN     
tcp        0      0 bear.example.pri:domain   *:*                     LISTEN     
tcp        0      0 localhost:domain        *:*                     LISTEN     
tcp        0      0 *:53941                 *:*                     LISTEN     
tcp        0      0 *:3128                  *:*                     LISTEN     
tcp        0      0 localhost:rndc          *:*                     LISTEN     
tcp        0      0 *:smtp                  *:*                     LISTEN     
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65452  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65459  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65412  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65417  ESTABLISHED
tcp        0      0 bear.example.pri:mysq   bear.example.pri:35475    TIME_WAIT  
tcp        0      0 bear.example.pri:http   sulaco.example.p:49603  FIN_WAIT2  
tcp        0      0 bear.example.pri:nfs    sulaco.example.p:49552  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65433  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65431  ESTABLISHED
tcp        1      0 bear.example.pri:nfs    sulaco.example.p:51900  CLOSE_WAIT 
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65415  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65475  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65472  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65429  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65430  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65438  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65443  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65418  ESTABLISHED
tcp        0      0 bear.example.pri:nfs    narcissus.exampl:62968 ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65448  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65423  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65468  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65445  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65476  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65453  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65456  ESTABLISHED
tcp        1      0 bear.example.pri:nfs    sulaco.example.p:59172  CLOSE_WAIT 
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65416  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65439  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65441  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65446  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65470  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65450  ESTABLISHED
tcp        0      0 bear.example.pri:nfs    sulaco.example.p:65320  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65465  ESTABLISHED
tcp        0      0 bear.example.pri:36230  solaris2.vmbear.mcs:ssh ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65421  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65464  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65474  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:64955  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65473  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65461  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65454  ESTABLISHED
tcp        0      0 bear.example.pri:http   sulaco.example.p:49608  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65471  ESTABLISHED
tcp        0      0 localhost:50123         localhost:ssh           ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65420  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65466  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65463  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65451  ESTABLISHED
tcp        0      0 bear.example.pri:35471  bear.example.pri:mysql    TIME_WAIT  
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65457  ESTABLISHED
tcp        1      0 bear.example.pri:nfs    sulaco.example.p:53877  CLOSE_WAIT 
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65432  ESTABLISHED
tcp        0      0 bear.example.pri:mysql  bear.example.pri:35470    TIME_WAIT  
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65467  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65414  ESTABLISHED
tcp        0      0 bear.example.pri:50112  bear.example.pri:imap     TIME_WAIT  
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65462  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65460  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65469  ESTABLISHED
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65422  ESTABLISHED
tcp        0      0 bear.example.pri:50110  bear.example.pri:imap     TIME_WAIT  
tcp        0      0 bear.example.pri:50111  bear.example.pri:imap     TIME_WAIT  
tcp        0      0 bear.example.pri:imap   sulaco.example.p:65442  ESTABLISHED
tcp6       0      0 [::]:imaps              [::]:*                  LISTEN     
tcp6       0      0 [::]:11211              [::]:*                  LISTEN     
tcp6       0      0 [::]:imap               [::]:*                  LISTEN     
tcp6       0      0 [::]:cisco-sccp         [::]:*                  LISTEN     
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN     
tcp6       0      0 localhost:rndc          [::]:*                  LISTEN     
tcp6       0      0 [::]:https              [::]:*                  LISTEN     
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:52786  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:56220  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:63895  ESTABLISHED
tcp6       0      0 localhost:ssh           localhost:50123         ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:60914  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:64669  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:56053  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:52268  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:49528  ESTABLISHED
tcp6       0      0 bear.example.pri:ssh    sulaco.example.p:65408  ESTABLISHED
udp        0      0 *:nfs                   *:*                                
udp        0      0 *:42498                 *:*                                
udp        0      0 *:54680                 *:*                                
udp        0      0 172.16.217.1:domain     *:*                                
udp        0      0 192.168.92.1:domain     *:*                                
udp        0      0 bear.example.p:domain   *:*                                
udp        0      0 localhost:domain        *:*                                
udp        0      0 *:45495                 *:*                                
udp        0      0 *:icpv2                 *:*                                
udp        0      0 *:bootps                *:*                                
udp        0      0 *:964                   *:*                                
udp        0      0 *:11211                 *:*                                
udp        0      0 *:sunrpc                *:*                                
udp        0      0 *:50042                 *:*                                
raw        0      0 *:icmp                  *:*                     7          

이 출력에서 볼 수 있듯이 이 시스템에서는 매우 많은 작업이 수행되고 있다. 세 번째 열은 열려 있거나 수신 중인 각 연결에 대한 호스트 이름과 포트(콜론으로 구분)를 보여 준다. TCP 또는 UDP 서비스 번호가 알려진 포트 번호(/etc/services 파일 내에 정의된 포트 번호)와 일치하면 해당 서비스 이름이 출력에 표시된다. 호스트의 경우 호스트 이름, 대체 IP 주소 또는 '*' 기호가 표시된다. 별표는 해당 서비스 및 포트가 모든 IP 주소에서 열려 있고 수신 중임을 나타낸다.
예를 들어, Listing 9의 출력에서 시스템이 NFS를 지원하도록 구성되어 있고 열린(설정된) 연결이 있음을 알 수 있다.

Listing 9. NFS를 지원하도록 구성된 시스템
$ netstat -a|grep nfs
tcp        0      0 *:nfs                *:*                     LISTEN     
tcp        1      0 bear.example.pri:nfs sulaco.example.p:51900  CLOSE_WAIT 
tcp        0      0 bear.example.pri:nfs narcissus.example.p:62968 ESTABLISHED
tcp        1      0 bear.example.pri:nfs sulaco.example.p:59172  CLOSE_WAIT 
tcp        0      0 bear.example.pri:nfs sulaco.example.p:65320  ESTABLISHED
tcp        1      0 bear.example.pri:nfs sulaco.example.p:53877  CLOSE_WAIT 
udp        0      0 *:nfs                   *:*                                

또한 이 출력에서는 현재 시스템과 통신 중인 시스템을 확인할 수도 있다. 예를 들어, 다섯 번째 열을 정렬한 다음 목록에서 중복된 항목을 제거하여 현재 시스템에 연결된 시스템 목록을 추출할 수 있다(Lisiting 10 참조).

Listing 10. 연결된 시스템 목록 추출하기
$ netstat -a|egrep 'tcp|udp'|grep ESTABLISHED|awk '{ print $5; }'|cut -d: -f1|sort|uniq
localhost
narcissus.mcslp.p
nautilus.wireless
polarbear.wireles
solaris2.vmbear.mcs
sulaco.mcslp.pri

이 기능은 인식하지 못하고 있거나 원하지 않는 사용자 또는 컴퓨터가 현재 시스템에 연결되어 있는지 확인할 때 유용하게 사용할 수 있다.
이러한 다른 시스템에 대한 정보를 확인하려면 먼저 사용자의 네트워크에 있는 다른 컴퓨터를 검색해야 한다.
26 | 이전 | 다음


다른 호스트에 대한 정보 찾기
이제 로컬 시스템에 대한 기본 정보를 알고 있으므로 이제 네트워크의 다른 시스템도 검색하여 사용 가능한 시스템과 각 시스템에서 제공하는 서비스를 확인할 수 있다. 올바른 도구를 사용하여 이러한 시스템에서 실행 중인 운영 체제와 공유하고 있는 서비스까지도 확인할 수 있다.
호스트 검사하기
원격 시스템을 가장 쉽고 명확하게 검사하는 방법은 ping 도구를 사용하여 특정 호스트가 실행 중이고 사용 가능한지 여부를 검사하는 것이다. 매우 단순한 작업을 수행하는 ping 도구는 응답을 요청하는 원격 호스트에 패킷을 보낸 후 응답이 수신되면 시간 차이를 계산한다. 이렇게 패킷을 보내고 받는데 걸린 시간을 사용하여 현재 시스템과 대상 시스템의 거리를 표시할 수 있다.
예를 들어, 사용자의 네트워크에 있는 시스템에 대해 ping을 실행할 경우에는 ping 패킷에 대한 응답이 매우 빠르게 수신된다(Listing 11 참조).

Listing 11. 사용자의 네트워크에 있는 시스템에 대해 ping 실행하기
$ ping bear
PING bear.mcslp.pri (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: icmp_seq=0 ttl=64 time=0.154 ms
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.162 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.149 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.161 ms
64 bytes from 192.168.0.2: icmp_seq=4 ttl=64 time=0.162 ms
64 bytes from 192.168.0.2: icmp_seq=5 ttl=64 time=0.161 ms
^C
--- bear.mcslp.pri ping statistics ---
6 packets transmitted, 6 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.149/0.158/0.162/0.005 ms

ping 도구는 구현 형태에 따라 각기 다른 방식으로 작동한다. Linux 및 Mac OS X의 경우 이 도구는 기본적으로 패킷을 지속적으로 보내면서 사용자가 Ctrl-C를 눌러서 애플리케이션을 종료할 때까지 응답을 기다린다.
Solaris™, AIX® 및 일부 기타 UNIX 운영 체제에서는 추가 인수 없이 ping 도구를 실행하면 원격 호스트가 응답했는지 여부만 표시된다(Listing 12 참조).

Listing 12. 기타 UNIX 운영 체제에서 추가 인수 없이 ping 실행하기
$ ping bear
bear is alive

더 긴 테스트를 수행하려면 Listing 13과 같이 -s 옵션을 사용한다.

Listing 13. -s 옵션을 사용하여 ping 실행하기
$ ping -s bear
PING bear: 56 data bytes
64 bytes from bear.mcslp.pri (192.168.0.2): icmp_seq=0. time=0.288 ms
64 bytes from bear.mcslp.pri (192.168.0.2): icmp_seq=1. time=0.247 ms
64 bytes from bear.mcslp.pri (192.168.0.2): icmp_seq=2. time=0.208 ms
64 bytes from bear.mcslp.pri (192.168.0.2): icmp_seq=3. time=0.230 ms
^C
----bear PING Statistics----
4 packets transmitted, 4 packets received, 0% packet loss
round-trip (ms)  min/avg/max/stddev = 0.208/0.243/0.288/0.034

각 행의 time 필드에는 각 패킷에 대한 속도와 지연 시간(응답이 수신되기 전까지의 지연 시간 및 활동 레벨을 나타내기도 함)이 표시된다. 출력을 중지하면 보내고 받은 패킷의 수와 시간 통계에 대한 요약 정보가 표시된다.
ping 패킷이 이동해야 하는 거리가 멀수록 원격 호스트의 응답 시간이 길어집니다. 예를 들어, 인터넷에 있는 공용 서버에 대해 ping을 실행할 경우에는 응답 패킷을 수신하는 데 걸린 시간이 상당히 커집니다(Listing 14 참조).

Listing 14. 인터넷에 있는 공용 서버에 대해 ping 실행하기
$ ping www.example.com
PING www.example.com (67.205.21.169) 56(84) bytes of data.
64 bytes from mcslp.com (67.205.21.169): icmp_seq=1 ttl=44 time=193 ms
64 bytes from mcslp.com (67.205.21.169): icmp_seq=2 ttl=44 time=194 ms
64 bytes from mcslp.com (67.205.21.169): icmp_seq=3 ttl=44 time=197 ms
64 bytes from mcslp.com (67.205.21.169): icmp_seq=4 ttl=44 time=194 ms
^C
--- www.example.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3039ms
rtt min/avg/max/mdev = 193.737/195.120/197.123/1.353 ms

인터넷 서비스에 연결하는 데 걸린 시간(193ms)과 로컬 호스트에 연결하는 데 걸린 시간(0.23ms)을 비교해 보자.
ping 도구를 사용하면 연결하려는 원격 호스트에 도달할 수 있는지 여부도 빠르게 확인할 수 있다. 존재하지 않는 호스트에 대해 ping을 실행하면 특정 오류가 리턴된다(Listing 15 참조).

Listing 15. 존재하지 않는 호스트에 대해 ping 실행하기
$ ping notinhere
PING notinhere (192.168.0.110) 56(84) bytes of data.
>From bear.mcslp.pri (192.168.0.2) icmp_seq=1 Destination Host Unreachable
>From bear.mcslp.pri (192.168.0.2) icmp_seq=2 Destination Host Unreachable
>From bear.mcslp.pri (192.168.0.2) icmp_seq=3 Destination Host Unreachable
^C
--- notinhere ping statistics ---
5 packets transmitted, 0 received, +3 errors, 100% packet loss, time 4039ms

ping 도구를 사용하려면 네트워크에 있는 다른 시스템에 대한 정보를 알아야 한다. 이제 이름이나 IP 주소를 사용하지 않고 네트워크에 있는 호스트를 확인하는 방법을 살펴보자.
네트워크에 있는 호스트 검색하기
이더넷 네트워크 시스템(및 기타 시스템) 내에서 네트워크의 모든 장치는 하드웨어 네트워크 장치와 연관된 고유 주소를 가지고 있다. MAC(Media Access Control) 번호는 네트워크 장치를 고유하게 식별하며 IP(Internet Protocol)와 같은 상위 레벨 프로토콜을 사용하여 MAC 주소와 호스트 이름을 연관시킬 수 있다.
MAC 주소는 운영 체제에서 패킷을 네트워크의 외부로 보낼 때(및 그 반대의 경우에) 사용된다. 패킷을 특정 호스트 이름으로 보낼 때 운영 체제에서는 호스트 이름을 MAC 주소로 변환하여 네트워크의 외부로 보낼 하드웨어(이더넷) 패킷을 생성한다.
ARP(Address Resolution Protocol)는 이 맵핑을 처리하는 프로토콜이며 arp 도구를 사용하여 현재 가지고 있는 호스트 및 해당 호스트 이름 또는 IP 주소에 대한 정보를 표시할 수 있다.
다른 시스템과 통신하려는 네트워크의 모든 시스템은 MAC 주소와 IP 주소로 구성된 패킷을 보내야 하므로 시스템의 ARP 캐시에 수집된 정보를 사용하여 네트워크에 있는 다른 시스템을 찾을 수 있다(Listing 16 참조).

Listing 16. arp 명령 사용하기
$ arp
Address                  HWtype  HWaddress           Flags Mask            Iface
gendarme.mcslp.pri       ether   00:1B:2F:F0:39:6A   C                     eth0
narcissus.mcslp.pri      ether   00:16:CB:85:2D:15   C                     eth0
solaris2.vmbear.mcslp.p  ether   00:0C:29:7F:0D:C5   C                     eth0
nautilus.wireless.mcslp  ether   00:17:F2:40:4D:1B   C                     eth0
sulaco.mcslp.pri         ether   00:16:CB:A0:3B:CB   C                     eth0

기존 허브 구조 대신 최신 이더넷 스위치를 사용할 경우에는 arp의 출력 정보를 특정 호스트와 보내고 받은 패킷으로 제한할 수 있다. 서버에서 arp를 실행할 수 있다면 더 긴 목록의 정보를 볼 수도 있지만 이 방법을 사용할 수 없는 경우도 있다.
일부 네트워크 스위치에는 모든 패킷이 에코되고 다른 네트워크 장치에 대한 정보를 수집하여 네트워크 구조를 파악하는 데 사용할 수 있는 네트워크 관리 또는 모니터링 포트가 있다. 이 정보에 액세스할 수 없는 경우에는 다른 강력한 도구를 통해 네트워크에 있는 호스트를 찾아야 한다.
네트워크에 있는 다른 호스트 검색하기
nmap 도구는 네트워크에 대해 다양한 유형의 검사를 수행하여 다양한 레벨의 정보를 찾고 확인할 수 있는 유틸리티이다. 기본 레벨에서는 지정된 네트워크 내의 모든 호스트를 찾는 데 사용할 수 있다.
이 기사의 앞 부분에서 호스트의 현재 IP 주소 및 네트워크 마스크 정보를 가져오는 방법을 살펴보았다. 이 정보를 사용하여 네트워크의 모든 호스트를 찾는 데 필요한 nmap의 기본 검색 매개변수를 설정할 수 있다. 이 정보를 지정하려면 CIDR 형식의 주소를 사용해야 한다. CIDR 형식은 호스트의 IP 주소와 네트워크 마스크의 비트 수를 사용하여 네트워크의 범위를 결정한다.
예제 호스트에서 192.168.1.25는 IP 주소이고 네트워크 마스크는 255.255.252.0이다. 이는 22비트에 해당한다. 즉, 첫 번째 부분에 8비트, 두 번째 부분에 8비트 그리고 세 번째 부분에 6비트가 해당한다.
이 주소를 사용하여 nmap을 실행하면 범위 내의 모든 단일 IP 주소(예를 들어, 192.168.0.0과 192.168.3.255 사이의 모든 주소)에 대한 검색이 수행되고 응답하는 호스트가 확인된다.
표준 ping 프로토콜을 사용하는 테스트나 다른 네트워크 포트를 시도하는 강력한 테스트(ping 프로토콜을 사용할 수 없는 경우)를 포함한 여러 가지 테스트를 수행할 수 있다. 예를 들어, ping 테스트를 수행하면 Listing 17과 같은 호스트 목록이 표시된다.

Listing 17. nmap을 실행하여 IP 주소 범위 검색하기
$ nmap -sP 192.168.1.25/22

Starting Nmap 4.76 ( http://nmap.org ) at 2009-03-24 15:59 GMT
Host 192.168.0.1 appears to be up.
Host bear.mcslp.pri (192.168.0.2) appears to be up.
Host narcissus.mcslp.pri (192.168.0.3) appears to be up.
Host 192.168.0.10 appears to be up.
Host 192.168.0.27 appears to be up.
Host sulaco.mcslp.pri (192.168.0.101) appears to be up.
Host nautilus.wireless.mcslp.pri (192.168.0.109) appears to be up.
Host 192.168.1.1 appears to be up.
Host 192.168.1.25 appears to be up.
Host gentoo1.vmbear.mcslp.pri (192.168.1.52) appears to be up.
Host gentoo2.vmbear.mcslp.pri (192.168.1.53) appears to be up.
Nmap done: 1024 IP addresses (11 hosts up) scanned in 5.78 seconds

ping 검사를 수행하면 네트워크에 있는 다른 시스템을 매우 빠르게 확인할 수 있다. 이 경우에는 11개의 호스트가 검색되었지만 이들 중 일부 호스트는 이름을 다시 확인할 수 없다. 이는 DNS 구성에 오류가 있음을 나타낸다. 일부 시스템에서는 역방향 조회(IP 주소로 이름을 확인하는 조회)를 사용하여 클라이언트 IP 주소가 변조되지 않았는지 확인하는 보안 검사를 수행하므로 이 오류는 수정되어야 한다.
네트워크에 있는 다른 서비스 찾기
ping 검사가 유용하기는 하지만 개별 시스템에서 실제로 노출하고 있는 서비스를 알고 싶을 때는 TCP 검사를 사용해야 한다. TCP 검사를 수행하면 nmap이 목록 내에 있는 각 호스트에서 TCP/IP 프로토콜을 사용하는 포트를 열려고 시도하므로 더 많은 시간이 소요된다. 이 검사는 네트워크에 있는 호스트를 표시하고 각 호스트의 열려 있는 포트에 대한 세부 정보를 제공하려는 경우에 효과적이다. Listing 18에서 이 검사의 출력 예제를 볼 수 있다.

Listing 18. TCP 검사 사용하기
$ nmap -sT 192.168.1.25/22

Starting Nmap 4.76 ( http://nmap.org ) at 2009-03-24 16:03 GMT
Interesting ports on 192.168.0.1:
Not shown: 997 closed ports
PORT      STATE SERVICE
80/tcp    open  http
8080/tcp  open  http-proxy
49153/tcp open  unknown

Interesting ports on bear.mcslp.pri (192.168.0.2):
Not shown: 987 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
25/tcp   open  smtp
53/tcp   open  domain
80/tcp   open  http
111/tcp  open  rpcbind
143/tcp  open  imap
443/tcp  open  https
902/tcp  open  iss-realsecure
993/tcp  open  imaps
2000/tcp open  callbook
2049/tcp open  nfs
3128/tcp open  squid-http
3306/tcp open  mysql

Interesting ports on narcissus.mcslp.pri (192.168.0.3):
Not shown: 982 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
88/tcp   open  kerberos-sec
106/tcp  open  pop3pw
111/tcp  open  rpcbind
311/tcp  open  asip-webadmin
389/tcp  open  ldap
548/tcp  open  afp
625/tcp  open  apple-xsrvr-admin
749/tcp  open  kerberos-adm
1021/tcp open  unknown
1022/tcp open  unknown
3659/tcp open  unknown
3689/tcp open  rendezvous
4111/tcp open  unknown
5900/tcp open  vnc
8086/tcp open  unknown
8087/tcp open  unknown

Interesting ports on 192.168.0.10:
Not shown: 997 closed ports
PORT    STATE SERVICE
23/tcp  open  telnet
80/tcp  open  http
443/tcp open  https

Interesting ports on 192.168.0.27:
Not shown: 999 closed ports
PORT   STATE SERVICE
22/tcp open  ssh

Interesting ports on sulaco.mcslp.pri (192.168.0.101):
Not shown: 995 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
88/tcp   open  kerberos-sec
548/tcp  open  afp
631/tcp  open  ipp
2170/tcp open  unknown

Interesting ports on nautilus.wireless.mcslp.pri (192.168.0.109):
Not shown: 995 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
88/tcp   open  kerberos-sec
111/tcp  open  rpcbind
1001/tcp open  unknown
5900/tcp open  vnc

Interesting ports on 192.168.1.1:
Not shown: 995 closed ports
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
23/tcp   open  telnet
80/tcp   open  http
5431/tcp open  unknown

Interesting ports on 192.168.1.25:
Not shown: 997 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
111/tcp  open  rpcbind
4045/tcp open  lockd

Interesting ports on gentoo1.vmbear.mcslp.pri (192.168.1.52):
Not shown: 997 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
111/tcp  open  rpcbind
3128/tcp open  squid-http

Interesting ports on gentoo2.vmbear.mcslp.pri (192.168.1.53):
Not shown: 998 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh
111/tcp open  rpcbind

Nmap done: 1024 IP addresses (11 hosts up) scanned in 32.27 seconds

이 출력을 보면 다양한 서비스를 제공하는 수많은 서버가 네트워크에 있다는 것을 알 수 있다. 예를 들어, 192.168.0.1의 장치는 HTTP 및 HTTP 프록시 서비스를 제공한다. 또한 bear.mcslp.pri는 http뿐만 아니라 smtp, imap, nfs 및 MySQL 서비스도 제공한다.
이러한 서비스에 대한 구체적인 정보를 확인하려는 경우에는 다시 한번 nmap을 버전 인수와 함께 사용하여 특정 호스트에 열려 있는 프로토콜 및 포트에 대한 구체적인 버전 정보 목록을 확인할 수 있다.
예를 들어, 메인 서버처럼 보이는 호스트(bear)를 검사해 보면 이러한 각 포트에서 실행되고 있는 서비스를 매우 정확히 파악할 수 있다(Listing 19 참조).

Listing 19. 버전 인수와 함께 nmap 사용하기
$ nmap -sT -sV bear

Starting Nmap 4.76 ( http://nmap.org ) at 2009-03-24 16:17 GMT
Interesting ports on localhost (127.0.0.1):
Not shown: 985 closed ports
PORT      STATE SERVICE         VERSION
22/tcp    open  ssh             OpenSSH 5.1 (protocol 2.0)
25/tcp    open  smtp            Postfix smtpd
53/tcp    open  domain          ISC BIND 9.4.3-P1
111/tcp   open  rpcbind
143/tcp   open  imap            Cyrus IMAP4 2.3.13-Gentoo
443/tcp   open  ssl/http        Apache httpd
783/tcp   open  spamassassin    SpamAssassin spamd
902/tcp   open  ssl/vmware-auth VMware Authentication Daemon 1.10 (Uses VNC)
993/tcp   open  ssl/imap        Cyrus imapd
2000/tcp  open  sieve           Cyrus timsieved 2.3.13-Gentoo (included w/cyrus imap)
2049/tcp  open  rpcbind
3128/tcp  open  http-proxy      Squid webproxy 2.7.STABLE6
3306/tcp  open  mysql           MySQL 5.0.60-log
10024/tcp open  smtp            amavisd smtpd
10025/tcp open  smtp            Postfix smtpd
Service Info: Hosts:  gendarme.mcslp.com, bear, 127.0.0.1

Service detection performed. Please report any incorrect results at
   http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.12 seconds

이 출력에서는 수많은 특정 서비스를 볼 수 있으며, 각 포트에서 제공하는 버전 정보뿐 아니라 애플리케이션 정보까지도 확인할 수 있다.
네트워크에서 식별되지 않은 호스트 확인하기
네트워크에 있는 호스트를 검색한 후 호스트에 대한 정보 특히, 즉시 인식되지 않는 호스트에 대한 정보를 확인할 수 있다. TCP 포트 검사는 호스트에서 지원하고 있는 서비스를 보여 주기는 하지만 반드시 모든 정보를 보여 주는 것은 아니다. 일부 장치 및 시스템의 경우에는 네트워크의 서비스를 즉시 확인할 수 없는 방식으로 포트를 노출하기도 하며 더 나아가 노출하지 않을 수도 있다.
nmap 운영 체제 검사는 열려 있는 포트를 검사한 후 다양한 서비스가 실행되고 있는 운영 체제를 확인한다. 이 기능은 네트워크에서 열려 있는 포트와 새 장치가 있는 서버를 식별하는 기능과는 다르다.
예를 들어, Listing 20에서와 같이 bear라는 서버에 대해 운영 체제 식별을 실행하면 일반적인 Linux 버전이 시스템에서 실행되고 있다는 것을 식별할 수 있으며 이는 해당 시스템이 표준 컴퓨터임을 나타낸다.

Listing 20. nmap 운영 체제 검사
# nmap -sT -O bear

Starting Nmap 4.76 ( http://nmap.org ) at 2009-03-24 16:20 GMT
Interesting ports on localhost (127.0.0.1):
Not shown: 985 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
25/tcp    open  smtp
53/tcp    open  domain
111/tcp   open  rpcbind
143/tcp   open  imap
443/tcp   open  https
783/tcp   open  spamassassin
902/tcp   open  iss-realsecure
993/tcp   open  imaps
2000/tcp  open  callbook
2049/tcp  open  nfs
3128/tcp  open  squid-http
3306/tcp  open  mysql
10024/tcp open  unknown
10025/tcp open  unknown
Device type: general purpose
Running: Linux 2.6.X
OS details: Linux 2.6.17 - 2.6.25
Network Distance: 0 hops

OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1.71 seconds

OS 검사는 완벽하지 않으며 finger printing 기술을 사용하여 열려 있는 포트 및 리턴된 버전 정보의 의미를 확인한다. 예를 들어, Listing 21의 검사에서는 포트 유형을 기반으로 잠재적 운영 체제의 수를 식별한다.

Listing 21. 잠재적 운영 체제의 수를 식별하는 검사
# nmap -sT -O some.faroffhost.com

Starting Nmap 4.76 ( http://nmap.org ) at 2009-03-24 16:23 GMT
Interesting ports on some.faroffhost.com (205.196.217.20):
Not shown: 976 closed ports
PORT     STATE    SERVICE
21/tcp   open     ftp
22/tcp   open     ssh
25/tcp   open     smtp
110/tcp  open     pop3
111/tcp  filtered rpcbind
113/tcp  open     auth
135/tcp  filtered msrpc
139/tcp  filtered netbios-ssn
143/tcp  open     imap
548/tcp  open     afp
554/tcp  open     rtsp
555/tcp  open     dsf
587/tcp  open     submission
687/tcp  open     unknown
993/tcp  open     imaps
995/tcp  open     pop3s
1720/tcp filtered H.323/Q.931
5222/tcp open     unknown
5269/tcp open     unknown
5666/tcp open     unknown
7070/tcp open     realserver
8000/tcp open     http-alt
8001/tcp open     unknown
8649/tcp open     unknown
Device type: print server|general purpose|storage-misc|WAP|switch|specialized
Running (JUST GUESSING) : HP embedded (92%), Linux 2.6.X|2.4.X (92%), Buffalo embedded 
(91%), Acorp embedded (89%), Actiontec Linux 2.4.X (89%), Linksys embedded (89%), 
Netgear embedded (89%), Infoblox NIOS 4.X (89%)
Aggressive OS guesses: HP 4200 PSA (Print Server Appliance) model J4117A (92%), 
Linux 2.6.20 (Ubuntu 7.04 server, x86) (92%), Linux 2.6.9 (92%), Buffalo TeraStation NAS 
device (91%), Linux 2.6.18 (CentOS 5.1, x86) (91%), OpenWrt 7.09 (Linux 2.4.34) (90%), 
Acorp W400G or W422G wireless ADSL modem (MontaVista Linux 2.4.17) (89%), HP Brocade 
4100 switch; or Actiontec MI-424-WR, Linksys WRVS4400N, or Netgear WNR834B wireless 
broadband router (89%), HP Brocade 4Gb SAN switch (89%), Infoblox NIOS Release 
4.1r2-5-22263 (89%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 18 hops

OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.66 seconds

nmap 검사는 로컬 및 원격 네트워크 둘 다에 대해 실행할 필요는 없다. 위 원격 테스트에서 nmap은 패킷이 대상 시스템에 도달하기 전까지 통과해야 하는 다양한 시스템을 확인했다. 사용자의 시스템과 다른 시스템 사이에 있는 다양한 장치를 파악하는 작업은 네트워크 레이아웃을 이해하고 파악하는 과정의 마지막 단계이다.
네트워크 구조 결정하기
IP 네트워크 패킷이 네트워크에서 전송될 경우, 시스템이 패킷을 다른 네트워크 또는 시스템으로 전달할 때마다 특수 카운터가 증가된다. 패킷 전달은 여러 다양한 시스템에서 발생한다. 여러 네트워크 스위치가 함께 연결되어 있는 경우 각 허브는 자신을 새 장치로 식별할 수 있다. 또한 무선 액세스 포인트와 기존 라우터 모두 패킷을 전달하는 장치이므로 패킷의 네트워크 경로의 일부로 간주된다.
대부분의 네트워크 환경에서 로컬 네트워크에 있는 허브, 스위치 및 기타 구성 요소는 이 값을 증가시키지 않는다. 하지만 외부 네트워크로까지 시선을 넓히면 네트워크 규모가 더 커지고 복잡해지므로 개별 패킷의 경로를 이해할 수 있다면 성능 및 연결 문제를 식별하는 데 많은 도움이 된다.
호스트와의 통신 경로에 대한 정보를 표시하는 데 주로 사용되는 도구는 traceroute이다. 이 도구는 현재 호스트에서 대상까지의 지정된 경로 내에 있는 각 호스트의 IP 주소를 확인한다. 대상 호스트가 로컬 호스트 자신이면 직접 경로로 표시된다(Listing 22 참조).

Listing 22. traceroute 사용하기
$ traceroute solaris2
traceroute to solaris2 (192.168.1.25), 30 hops max, 40 byte packets
 1  solaris2.mcslp.pri (192.168.1.25)  0.651 ms  0.892 ms  0.969 ms

Listing 23에서는 로컬 라우터나 브리지를 통해 액세스할 수 있는 로컬 네트워크에 있는 호스트를 보여 준다.

Listing 23. 로컬 네트워크에 있는 호스트
$ traceroute gentoo1
traceroute to gentoo1 (192.168.1.52), 30 hops max, 40 byte packets
 1  gendarme.mcslp.pri (192.168.0.1) 3.163 ms  3.159 ms  6.618 ms
 2  gentoo1.mcslp.pri (192.168.1.52)  34.336 ms  34.341 ms  34.341 ms

원격 네트워크에 대한 연결은 패킷이 통과하게 되는 각 라우터와 단계를 보여 준다(Listing 24 참조.

Listing 24. 원격 네트워크 연결
$ traceroute www.ibm.com
traceroute to www.ibm.com (129.42.58.216), 30 hops max, 40 byte packets
 1  gendarme.mcslp.pri (192.168.0.1)  3.163 ms  3.159 ms  6.618 ms
 2  gauthier-dsl1.hq.zen.net.uk (62.3.82.17)  34.336 ms  34.341 ms  34.341 ms
 3  lotze-ge-0-0-1-136.hq.zen.net.uk (62.3.80.137)  37.581 ms  47.276 ms  50.548 ms
 4  nietzsche-ae2-0.ls.zen.net.uk (62.3.80.70)  43.945 ms  47.239 ms  50.529 ms
 5  nozick-ge-3-1-0-0.ls.zen.net.uk (62.3.80.74)  55.343 ms  55.341 ms  55.339 ms
 6  lorenz-ge-3-0-0-0.te.zen.net.uk (62.3.80.78)  66.347 ms  63.118 ms  63.105 ms
 7  82.195.188.13 (82.195.188.13)  146.039 ms  118.175 ms  124.532 ms
 8  sl-bb22-lon-8-0.sprintlink.net (213.206.128.60)  50.460 ms  47.273 ms  40.991 ms
 9  sl-bb20-lon-12-0.sprintlink.net (213.206.128.52)  47.107 ms  47.094 ms  43.711 ms
10  sl-crs2-nyc-0-5-3-0.sprintlink.net (144.232.9.164)  111.579 ms  113.173 ms  
      113.159 ms
11  144.232.18.238 (144.232.18.238)  116.353 ms  111.633 ms  111.619 ms
12  0.xe-5-0-1.XL3.NYC4.ALTER.NET (152.63.3.125)  114.812 ms  111.788 ms  115.000 ms
13  0.so-7-1-0.XT3.STL3.ALTER.NET (152.63.0.6)  151.969 ms  142.573 ms  142.574 ms
14  POS6-0.GW8.STL3.ALTER.NET (152.63.92.37)  142.552 ms  253.001 ms  252.986 ms
15  ibm-gw.customer.alter.net (65.206.180.74)  179.655 ms  228.775 ms  228.751 ms
16  10.16.255.10 (10.16.255.10)  145.847 ms  139.310 ms  142.509 ms
17  * * *
18  129.42.58.216 (129.42.58.216)  143.118 ms  141.181 ms  141.152 ms

호스트 목록을 확인하는 nmap과 함께 이 방법을 사용하면 네트워크에 있는 호스트에 대한 정보와 이러한 시스템에 도달하는 데 사용되는 라우터 및 시스템에 대한 정보를 훨씬 더 자세히 볼 수 있다.
36 | 이전 | 다음

결론
이 튜토리얼에서는 네트워크에 있는 호스트, 이러한 호스트에 액세스하는 방법, 연결되어 있는 장치 및 시스템, 제공되는 서비스 및 시스템 등에 대한 다양한 정보를 확인하는 데 사용할 수 있는 여러 가지 UNIX 도구 및 기술을 살펴보았다.
이제 이러한 기술을 통해 UNIX 환경에 액세스하여 네트워크 구성을 확인할 수 있어야 하며, 필요한 정보를 기록하여 문제점 및 그 이유를 파악하고 해결 방법까지도 알아낼 수 있어야 한다.
기사의 원문보기
46 | 이전 | 다음



LAMP 시스템 조율, Part 3: MySQL 조율

http://www.ibm.com/developerworks/kr/library/l-tune-lamp-3.html


LAMP 시스템 조율, Part 3: MySQL 조율

서버 조율 팁으로 MySQL 서버에 날개를 달자
Sean A. Walberg, 선임 네트워크 엔지니어
요약: LAMP(Linux®, Apache, MySQL, PHP/Perl) 아키텍처를 활용하는 응용 프로그램은 끊임없이 개발되고 배포되고 있습니다. 하지만 때로 서버 관리자는 다른 사람이 작성했다는 이유만으로 응용 프로그램 자체에 대한 통제권이 거의 없습니다. 기사 셋으로 이뤄진 이번 연재물은 응용 프로그램 성능을 향상시킬 서버 환경 설정 항목을 다룹니다. 연재 마지막인 세 번째 기사에서는 최대 성능을 발휘하도록 데이터베이스 계층을 조율하는 데 초점을 맞춥니다.
원문 게재일:  2008 년 5 월 06 일
난이도:  중급 원문:  보기
페이지뷰: 1681 회
의견: 0 (의견 추가)
1 star2 stars3 stars4 stars5 stars 평균 평가 등급 (총 3표)
MySQL 조율에 대해
MySQL 서버를 빠르게 하기 위한 방법은 세 가지가 있는데, 효율이 낮은 쪽에서 높아지는 쪽으로 나열하면 다음과 같다.
  1. 하드웨어로 문제를 푼다.
  2. MySQL 프로세스 설정을 조율한다.
  3. 질의를 최적화한다.

DB2로 이주

MySQL에서 IBM DB2로 이주하는 명쾌하고 비용이 들지 않는 방법을 찾고 싶은가? "MySQL 또는 PostgreSQL에서 DB2 Express-C로의 마이그레이션 (영문)" 기사에서는 이주 도구를 활용해 쉽게 이전하는 방법을 보여준다. 공짜 DB2 Express-C를 내려받아 지금 바로 시도해보자.
하드웨어로 문제를 푸는 방법이 가장 먼저 떠오른다. 특히 데이터베이스가 자원을 잡아먹는 괴물이라는 사실을 감안하면 말이다. 하지만 이 해법에는 한계가 있다. 현실을 고려할 때 CPU나 디스크 속력은 두 배로, 메모리 용량은 네 배에서 여덟 배 정도만 늘일 수 있다.
두 번째로 좋은 방법은 mysqld라는 MySQL 서버 조율이다. 이 프로세스 조율은 올바른 위치에 메모리를 할당하고 어떤 부하가 걸릴지 mysqld에 알려주는 조정 기법을 의미한다. 디스크 속력을 좀 더 빠르게 만드는 대신, 필요한 디스크 접근 횟수를 줄이는 편이 유리하다. 비슷하게, MySQL 프로세스가 올바르게 동작하도록 만드는 조율은 개발자가 임시 디스크 테이블과 파일 여닫기 같은 배경 작업에 신경을 쓰는 대신 질의에 대한 서비스에 좀 더 많은 시간을 보낼 수 있음을 의미한다. mysqld 조율은 이번 기사에서 주로 다룰 내용이다.
최고로 좋은 방법은 질의 최적화다. 이는 적절한 색인을 테이블에 만들어 놓고, MySQL의 장점을 최대로 활용하는 방향으로 질의를 작성하는 조율 기법을 의미한다. 이번 기사에서 질의 조율을 다루지는 않지만(이 주제로 책을 써도 되겠다), mysqld 환경 설정을 변경해 조율이 필요한 질의를 보고하도록 만든다.
중요한 조율 순서를 제시하긴 했지만, 그렇다고 해서 적절히 조율을 마친 질의를 위해 하드웨어나 mysqld 설정을 무시하라는 말은 아니다. 느린 기계는 느린 기계일 뿐이며, 제대로 작성한 질의를 돌리더라도 부하가 걸려 실패하는 경우를 목격했는데, mysqld가 질의를 서비스하는 대신 바쁘게 움직이느라 시간을 소모하고 있었기 때문이었다.
느린 질의 기록
SQL 서버에서 자료 테이블은 디스크에 위치한다. 색인은 전체 테이블을 찾지 않고서 서버가 테이블에서 자료 열을 찾아내도록 도와준다. 전체 테이블을 찾을 때 테이블 탐색을 수행한다고 부른다. 종종 테이블에서 일부만 원하는 경우가 있는데, 전체 테이블 탐색은 디스크 I/O와 시간을 상당히 많이 소비한다. 이런 문제는 테이블 조인 과정에서 복합적으로 나타나는데, 양쪽 테이블에 들어있는 열을 하나씩 비교해야 하기 때문이다.
물론 테이블 탐색이 항상 두통거리만은 아니다. 종종 전체 테이블을 읽는 경우가 일부만 읽는 경우보다 더 효과적인 경우도 있다(이런 결정을 내리려면 질의 계획이라는 작업을 거쳐야 한다). 색인을 비효율적으로 사용하거나 전혀 색인을 사용하지 않으면 질의가 느려지며, 테이블 크기가 증가하면서 서버에 부하가 걸리면 이런 문제점은 더욱 두드러진다. 실행을 위해 주어진 시간보다 더 오래 걸리는 질의는 느린 질의라고 부른다.
mysqld 환경 설정에서 느린 질의 로그라고 적절히 이름이 붙은 느린 질의 기록을 활성화할 수 있다. 관리자는 이 로그 파일을 살펴 응용 프로그램에서 어느 곳을 추가로 조사할지 결정한다. Listing 1은 느린 질의 로그를 활성화하기 위해 my.cnf에 필요한 환경 설정을 보여준다.

Listing 1. MySQL 느린 질의 로그 활성
[mysqld]
; 느린 질의 로그를 활성화한다. 기본은 10초다.
log-slow-queries
; 5초 이상 걸리는 질의를 기록한다.
long_query_time = 5
; long_query_time보다 적게 걸릴 경우 색인을 사용하지 않는 질의를 기록한다.
; MySQL 4.1 이상 버전에만 통한다
log-queries-not-using-indexes

이와 같은 세 가지 설정을 함께 사용하면, 5초 이상 지속되는 질의나 색인을 사용하지 않는 질의를 기록한다. log-queries-not-using-indexes에 대한 경고가 하나 있다. 반드시 MySQL 4.1 이상 버전을 사용해야만 한다. 느린 질의 로그는 MySQL 자료 디렉터리에 들어 있으며, 파일 형식은 hostname-slow.log이다. 다른 이름이나 경로를 사용한다면, my.cnf에서 log-slow-queries = /new/path/to/file을 지정하자.
느린 질의 로그를 읽으려면 mysqldumpslow 명령을 내린다. 로그 파일 경로를 지정하는 방법으로 느린 질의를 발견 순서에 따라 정렬한 목록을 얻는다. 도움을 주는 기능 한 가지는 mysqldumpslow가 결과를 비교하기 앞서 사용자 정의 자료를 제거하므로 동일한 질의로 여러 번 수행해도 하나로 센다. 이는 대다수 작업에 필요한 질의를 찾아내는 데 도움을 준다.
질의 캐시
대다수 LAMP 응용 프로그램은 데이터베이스에 상당히 의존하며 동일한 질의를 여러 번 반복한다. 질의를 만들 때마다 데이터베이스는 똑같은 작업을 해야만 한다. 즉 질의를 해석해, 실행 방법을 결정하고, 디스크에서 정보를 메모리에 올리고, 클라이언트에 이를 반환한다. MySQL은 질의 캐시라는 기능을 사용해서 메모리에 질의 결과를 저장하며 필요할 때 찾아쓴다. 여러 인스턴스에서 이런 캐시는 극적으로 성능을 높힌다. 하지만 질의 캐시는 기본적으로 비활성화되어 있다는 사실을 염두에 두자.
query_cache_size = 32M를 /etc/my.conf에 추가하면 질의 캐시로 32MB를 잡는다.
질의 캐시 감시
질의 캐시를 활성화한 다음에, 효율적으로 사용하고 있는지 이해하는 과정이 중요하다. MySQL은 여러 변수를 사용해서 캐시에서 어떤 일이 벌어지는지 감시하도록 만든다. Listing 2는 캐시 상태를 보여준다.

Listing 2. 질의 캐시 통계 출력
mysql> SHOW STATUS LIKE 'qcache%';
+-------------------------+------------+
| Variable_name           | Value      |
+-------------------------+------------+
| Qcache_free_blocks      | 5216       |
| Qcache_free_memory      | 14640664   |
| Qcache_hits             | 2581646882 |
| Qcache_inserts          | 360210964  |
| Qcache_lowmem_prunes    | 281680433  |
| Qcache_not_cached       | 79740667   |
| Qcache_queries_in_cache | 16927      |
| Qcache_total_blocks     | 47042      |
+-------------------------+------------+
8 rows in set (0.00 sec)

각 항목을 분리하면 표 1과 같다.

표 1. MySQL 질의 캐시 변수
변수 이름설명
Qcache_free_blocks 캐시에 있는 연속적인 메모리 블록 숫자. 높은 숫자는 단편화가 일어난 징표다. FLUSH QUERY CACHE는 캐시 조각을 모아 자유 블록 하나로 만든다.
Qcache_free_memory 캐시에 있는 자유 메모리
Qcache_hits 캐시에서 질의를 가져올 때마다 값이 증가한다.
Qcache_inserts 질의가 들어올 때마다 증가한다. inserts를 hits로 나누면 비적중률을, 1에서 비적중률을 빼면 적중률을 구할 수 있다. 직전 에제에서 대략 질의 중 87%를 캐시에서 가져왔다.
Qcache_lowmem_prunes 캐시를 위한 메모리가 부족해져 더 많은 질의를 위한 공간을 확보하기 위해 정리되어야 하는 횟수. 이 숫자를 계속해서 살펴보는데, 증가 추세에 있다면 단편화가 심각하거나 메모리가 부족하다는 징표다(위에서 언급한 free_blocksfree_memory를 살펴본다).
Qcache_not_cached 일반적으로 SELECT 구문이 아니기 때문에 캐시 후보에서 제외된 질의 숫자
Qcache_queries_in_cache 현재 캐시되어 있는 질의 숫자(응답 숫자 포함)
Qcache_total_blocks 캐시에 있는 블록 숫자
종종 이런 값의 변화 추이를 살펴보면 캐시를 효율적으로 사용하는지 파악하는 데 도움을 준다. FLUSH STATUS는 몇몇 카운터를 초기화하므로 서버가 동작 중에 있을 경우 도움이 된다.
모든 내용을 캐시하도록 과도하게 큰 캐시를 잡고 싶은 유혹이 든다. mysqld는 메모리 부족으로 인한 정리 작업과 같은 캐시 관리 작업도 해야 하므로, 여기에만 신경을 쓸 경우 서버가 꼼짝달싹하지 못한다. 일반적인 규칙을 설명하자면 FLUSH QUERY CACHE가 오래 걸린다면 캐시가 너무 큰 상황이다.
제약 가하기
시스템 부하가 자원 부족으로 이어지지 않도록 mysqld에 몇 가지 제약을 가해야 한다. Listing 3은 my.cnf에서 몇 가지 중요한 자원 관련 설정을 보여준다.

Listing 3. MySQL 자원 설정
set-variable=max_connections=500
set-variable=wait_timeout=10
max_connect_errors = 100

최대 접속은 첫째 행에서 다룬다. 아파치가 사용하는 MaxClients와 같이, 서비스가 가능한 접속 수만 허용한다. 지금까지 서버가 처리한 최대 접속 수를 확인하려면 SHOW STATUS LIKE 'max_used_connections' 명령을 내린다.
둘째 행은 mysqld가 10초 이상 쉬고 있는 접속을 끊어버리도록 만든다. LAMP 응용 프로그램에서 데이터베이스 접속은 일반적으로 웹 서버가 요청을 처리하는 동안에만 이뤄진다. 종종 부하가 걸린 상태에서 연결이 일시 정지된 상황에서 접속 테이블 공간을 차지하는 경우가 있다. 활성 사용자가 많거나 데이터베이스에 영속적인 접속이 이뤄지고 있다면, 이 값을 낮춰잡는 정책은 바람직하지 않다.
마지막 행은 안전 벨트다. 호스트에 서버 접속 관련 문제가 생겨 너무 많이 요청을 취소한다면, FLUSH HOSTS를 수행할 때까지 호스트는 잠겨버린다. 기본적으로 열 번 정도 실패하면 잠겨버리도록 설명한다. 이 값을 100으로 바꾸면 문제가 무엇이든 복구할 시간을 서버에 충분히 준다. 더 높은 값으로 설정하더라도 그다지 도움을 주지 않는 이유는 서버가 한 번에 100번 연결해도 실패한다면, 이후 계속 시도하더라도 연결에 성공할 가능성이 희박하기 때문이다.
버퍼와 캐시
MySQL은 100개가 넘는 조율 설정값을 지원한다. 하지만 천만다행으로 이 중에서 몇 가지만 알면 충분하다. 설정을 올바르게 하려면, SHOW STATUS 명령을 통해 상태 변수를 살펴보고, 이를 통해 mysqld가 원하는 방식으로 움직이는지 파악한다. 시스템에 존재하는 메모리 자원을 넘어서 버퍼와 캐시를 할당할 수 없기에 종종 조율 과정에서 타협이 필요하다.
MySQL 조율값은 mysqld 프로세스 전체나 개별 클라이언트 세션에 대해 설정이 가능하다.
서버 단위 설정
각 테이블은 디스크에 파일 형태로 저장되며, 테이블을 읽기 위해서는 파일을 열어야 한다. 파일 읽기 과정을 빠르게 하기 위해, mysqld는 /etc/mysqld.conf에 지정된 숫자(table_cache)만큼 열린 파일을 캐시한다. Listing 4는 열린 테이블에 대한 활동 상황을 출력하는 방법을 보여준다.

Listing 4. 열린 테이블 활동 상황 출력
mysql> SHOW STATUS LIKE 'open%tables';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables   | 5000  |
| Opened_tables | 195   |
+---------------+-------+
2 rows in set (0.00 sec)

Listing 4는 현재 테이블 5000개가 열려있으며, 테이블 195개가 열려야 했음을 보여준다. 캐시에 유효한 파일 기술자가 없기 때문에 이런 현상이 일어난다(직전에 통계를 초기화했으므로 5000개 열린 테이블 중에 단지 195개만 열렸다고 기록이 남는다). SHOW STATUS 명령을 다시 실행할 때 Opened_tables가 급격하게 올라가면, 캐시 적중률이 떨어진 상황이다. table_cache 설정값보다 Open_tables 설정값이 훨씬 낮으면, 캐시를 너무 크게 잡았다(물론 여유있게 설정하는 방식은 나쁘지 않다). 예를 들어, table_cache = 5000으로 테이블 캐시 값을 조정한다.
테이블 캐시와 마찬가지로 스레드를 위한 캐시도 있다. mysqld는 접속을 받을 때 필요한 스레드를 만든다. 바쁜 서버에서 접속이 빠르게 연결되었다 끊어지면, 초기 접속 속력을 높아기 위해 나중에 사용할 요량으로 스레드를 캐시한다.
Listing 5는 충분한 스레드가 캐시되었는지 살펴보는 방법을 보여준다.

Listing 5. 스레드 사용량 통계 보기
mysql> SHOW STATUS LIKE 'threads%';
+-------------------+--------+
| Variable_name     | Value  |
+-------------------+--------+
| Threads_cached    | 27     |
| Threads_connected | 15     |
| Threads_created   | 838610 |
| Threads_running   | 3      |
+-------------------+--------+
4 rows in set (0.00 sec)

여기서 가장 중요한 값은 Threads_createdmysqld가 새로운 스레드를 생성할 때마다 하나씩 증가한다. 연속적으로 SHOW STATUS 명령을 내릴 때, 이 숫자가 급격하게 올라가면 스레드 캐시 수치를 높여야 한다. 예를 들어, my.cnf에서 thread_cache = 40을 설정하면 된다.
키 버퍼는 MyISAM 테이블을 위한 색인 블록을 저장한다. 이상적으로 이런 블록에 대한 요청은 디스크가 아니라 메모리에서 일어나야 한다. Listing 6은 메모리와 디스크에서 얼마나 많은 블록을 읽는지 확인하는 방법을 보여준다.

Listing 6. 키 효율성 확인
mysql> show status like '%key_read%';
+-------------------+-----------+
| Variable_name     | Value     |
+-------------------+-----------+
| Key_read_requests | 163554268 |
| Key_reads         | 98247     |
+-------------------+-----------+
2 rows in set (0.00 sec)

Key_reads는 디스크에서 요청한 숫자이며, Key_read_requests는 전체 숫자다. Key_reads를 Key_read_requests로 나누면 비적중률이 나온다. Listing 6을 보면 1000개 요청 당 0.6개가 적중하지 않았다. 1000개 요청 당 1개 이상 적중하지 않는다면 키 버퍼를 늘려야 한다. 예를 들어, key_buffer = 384M를 지정하면 버퍼를 384MB로 늘인다.
임시 테이블은 GROUP BY 절과 같이 추가 처리가 필요할 때 임시로 자료를 저장할 곳으로, 좀 더 고급 질의에서 사용된다. 이상적으로 이런 테이블은 메모리에 생성하지만, 임시 테이블이 너무 커질 경우 디스크에 써야 한다. Listing 7은 임시 테이블 생성과 관련한 통계를 보여준다.

Listing 7. 임시 테이블 사용량 보기
mysql> SHOW STATUS LIKE 'created_tmp%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 30660 |
| Created_tmp_files       | 2     |
| Created_tmp_tables      | 32912 |
+-------------------------+-------+
3 rows in set (0.00 sec)

임시 테이블을 사용하면 Created_tmp_tables가 증가한다. 디스크 기반 테이블을 사용하면 Created_tmp_disk_tables가 증가한다. 이 비율을 정확하고 빠르게 결정하지 못하는 이유는 질의에 의존하기 때문이다. 시차를 두고 Created_tmp_disk_tables를 관찰하면 생성된 디스크 테이블 비율을 알 수 있고, 설정 값이 유효한지 살펴볼 수 있다. tmp_table_sizemax_heap_table_size 둘 다 임시 테이블 최대 크기를 제어하므로, my.cnf에서 양쪽 설정을 모두 확인해야 한다.
세션 단위 설정
이어지는 설정은 세션 단위다. 이 값을 설정할 때 신경을 써야 하는 이유는 잠재적인 접속 숫자에 설정값이 곱해지므로 메모리 사용량이 늘어나기 때문이다. 코드에서 해당 세션 값을 변경하거나 my.cnf에서 모든 세션 값을 변경할 수 있다.
MySQL이 정렬 작업을 수행할 때, 디스크에서 읽는 열을 저장하기 위한 정렬 버퍼를 할당한다. 정렬할 자료 크기가 너무 크다면, 디스크에 임시 파일로 자료를 저장하고, 다시 한번 정렬해야 한다. sort_merge_passes 상태값이 높으면, 디스크 활동량이 많다는 증거다. Listing 8은 정렬 관련 상태 카운터 몇 가지를 보여준다.

Listing 8. 정렬 통계 보기
mysql> SHOW STATUS LIKE "sort%";
+-------------------+---------+
| Variable_name     | Value   |
+-------------------+---------+
| Sort_merge_passes | 1       |
| Sort_range        | 79192   |
| Sort_rows         | 2066532 |
| Sort_scan         | 44006   |
+-------------------+---------+
4 rows in set (0.00 sec)

sort_merge_passes가 높다면, sort_buffer_size 쪽에 관심을 기울여야 한다. 예를 들어, sort_buffer_size = 4M를 지정하면, 정렬 버퍼를 4MB로 늘인다.
MySQL은 또한 테이블을 읽기 위한 메모리를 할당한다. 이상적으로 보면 색인은 필요한 열에서만 읽도록 충분한 정보를 제공하지만, (자료 특성 때문이나 설계 잘못으로 인해) 읽어야 할 테이블이 많은 질의도 있기 마련이다. 이런 행동 양식을 이해하려면, (색인으로 직접 접근하는 대신) 테이블에서 다음 열을 직접 읽어야 하는 숫자와 SELECT 문 개수를 알아야 한다. 이렇게 하려면 Listing 9에서 소개하는 명령을 내린다.

Listing 9. 테이블 탐색 비율 확인
mysql> SHOW STATUS LIKE "com_select";
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| Com_select    | 318243 |
+---------------+--------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE "handler_read_rnd_next";
+-----------------------+-----------+
| Variable_name         | Value     |
+-----------------------+-----------+
| Handler_read_rnd_next | 165959471 |
+-----------------------+-----------+
1 row in set (0.00 sec)

Handler_read_rnd_next / Com_select는 테이블 탐색 비율을 보여주는데, Listing 9에서는 521:1이다. 4000이 넘어가면, read_buffer_size = 4M와 같이 read_buffer_size 값이 충분히 크게 설정되어 있는지 확인한다. 이 값이 8M 이상으로 커진다면, 개발자에게 질의 조율이 필요하다고 알려주자!
세 가지 필수 도구
구체적인 설정값을 파고 들 때, SHOW STATUS 명령이 유용하긴 하지만, mysqld에서 제공하는 방대한 자료를 해석하는 과정에 도움을 주는 몇 가지 도구가 필요하다. 세 가지 필수 도구를 찾아내었는데, 참고자료 절에 정리해놓았다.
대다수 시스템 관리자는 top 명령에 익숙하다. top은 태스크가 소비하는 CPU와 메모리를 주기적으로 갱신하면서 보여준다. mytoptop을 모델로 만든 프로그램으로 현재 동작 중인 질의와 연결된 모든 클라이언트를 보여준다. mytop은 또한 키 버퍼와 질의 캐시 효율성에 대한 실시간 및 과거 자료를 제공하며, 실행 중인 질의 통계도 보여준다. 상황 파악에 유용한 도구이며, 10초 내로 서버 상태와 문제를 초래하는 모든 접속을 표시할 수 있다.
mysqlard는 MySQL 서버에 접속하는 데몬으로, 5초마다 자료를 수집해 라운드 로빈으로 동작하는 데이터베이스 뒷단에 저장한다. 웹 페이지는 테이블 캐시 사용량, 키 효율성, 접속된 클라이언트, 임시 테이블 사용량과 같은 자료를 출력한다. mytop은 서버 상태를 스냅 사진으로 찍어주며, mysqlard는 장기간에 걸친 서버 상태를 보여준다. 보너스로, mysqlard는 수집한 몇몇 정보를 활용해 서버 조율 방법을 제안한다.
SHOW STATUS 정보를 수집하는 또 다른 도구는 mysqlreport다. 이 도구가 mysqlard가 보고하는 내용보다 훨씬 자세한 보고 내역을 제공하는 이유는 서버의 모든 측면을 분석하기 때문이다. mysqlreport가 서버 조율에 뛰어난 이유는 상태 변수를 적절히 계산해 수정이 필요한 내용을 알려주기 때문이다.
요약
MySQL 조율 기본기를 설명하는 이 기사로 LAMP 컴포넌트 조율을 다루는 연재물을 마무리한다. 조율은 대부분 동작 원리를 이해하고 적절하게 동작하는지 확인하고 조정하고, 다시 평가하는 작업이다. 리눅스, 아파치, PHP, MySQL로 대표되는 각 컴포넌트마다 각자 요구 사항이 존재한다. 개별적으로 컴포넌트를 이해하고 있으면, 응용 프로그램을 느리게 만드는 병목을 제거하는 데 도움이 된다.

참고자료
교육
제품 및 기술 얻기
  • 지금부터 3년 전에 나왔음에도 불구하고 High Performance MySQL는 여전히 가치있는 책이다. 저자는 또한 MySQL에 대한 다양한 기사를 제공하는 웹 사이트를 운영한다.
  • mytop은 정확하게 그 순간 MySQL 서버에서 어떤 일이 일어나는지를 말해주며, 몇몇 핵심 통계 자료를 제공한다. 데이터베이스 문제가 발생했을 때 처음 사용하는 프로그램이다.
  • mysqlard는 MySQL 서버에서 성능 지표를 그래프로 보여주며, 조율에 대한 조언도 한다.
  • mysqlreport는 필수 도구다. 이 도구는 여러분을 대신해 SHOW STATUS 값을 분석한다.
  • phpMyAdmin에 대한 링크 없이는 MySQL 기사가 끝나지 않는다. 상태 변수에 대한 몇 가지 해석과 더불어 관리를 쉽게 만드는 기능을 제공한다.
  • IBM 평가판 소프트웨어: developerWorks에서 직접 내려 받아 다음번 리눅스 프로젝트에 활용하자.
토론
필자소개
Author photo Sean Walberg는 1994년 이래로 학계, 회사, 인터넷 서비스 제공 업체 환경을 두루 거치며 리눅스와 유닉스 분야에서 일해왔다. Walberg는 여러 해 동안 시스템 관리 서적을 집필해왔다.

LAMP 시스템 조율, Part 2: 아파치와 PHP 최적화

http://www.ibm.com/developerworks/kr/library/l-tune-lamp-2.html


LAMP 시스템 조율, Part 2: 아파치와 PHP 최적화

아파치가 느려지는 이유와 PHP 성능을 최대로 끌어내는 방법
Sean A. Walberg, 선임 네트워크 엔지니어
요약: LAMP(Linux®, Apache, MySQL, PHP/Perl) 아키텍처를 활용하는 응용 프로그램은 끊임없이 개발되고 배포되고 있습니다. 하지만 때로 서버 관리자는 다른 사람이 작성했다는 이유만으로 응용 프로그램 자체에 대한 통제권이 거의 없습니다. 기사 셋으로 이뤄진 이번 연재물은 응용 프로그램 성능을 향상시킬 서버 환경 설정 항목을 다룹니다. 첫 번째 기사는 LAMP 아키텍처, 성능 기법, 기본적인 리눅스 커널, 디스크, 파일 시스템 미조정을 다뤘습니다. 두 번째 기사에서는 아파치와 PHP 컴포넌트를 최적화하는 방법에 초점을 맞춥니다.
원문 게재일:  2008 년 4 월 29 일
난이도:  중급 원문:  보기
페이지뷰: 1977 회
의견: 0 (의견 추가)
1 star2 stars3 stars4 stars5 stars 평균 평가 등급 (총 2표)
리눅스, 아파치, MySQL, PHP(또는 펄)은 일정 목록부터 블로그와 전자 상거래 사이트에 이르기까지 많은 웹 응용 프로그램의 토대가 된다. LAMP 컴포넌트에 의존하는 많은 오픈 소스 패키지는 다양한 문제를 해결한다. 응용 프로그램 부하가 증가할수록, 기반 구조에서 병목 현상이 발생해 사용자 요청에 대한 반응이 느려지는 형태로 나타난다. 직전 기사에서는 리눅스 시스템 조율 방법과 LAMP 기초, 성능 측정 방법에 대한 기초를 다뤘다. 이번 기사에서는 아파치와 PHP로 대표되는 웹 서버 구성 요소에 초점을 맞춘다.
아파치 조율
아파치는 환경 설정이 자유로운 소프트웨어다. 기능도 많지만 각 기능마다 비용을 치뤄야 한다. 아파치 조율을 위해 적절한 자원 할당이 필요하며, 환경 설정을 줄여 필요한 항목만 남겨두는 지혜가 필요하다.
MPM 환경 설정
아파치는 기능을 쉽게 추가하거나 삭제할 수 있는 모듈화된 구조를 따른다. MPM(Multi-Processing Module)은 이런 모듈화된 구조를 네트워크 연결 관리와 요청 처리를 위한 아파치 핵심 기능으로 제공한다. MPM은 스레드를 사용하도록 만들고 심지어 아파치를 다른 운영체제로 이동하도록 만들어준다.
한번에 MPM 하나만 활성화되며, --with-mpm=(worker|prefork|event)를 사용해 정적으로 컴파일해야 한다.
요청당 프로세스 하나를 띄우는 전통적인 모델을 prefork라고 한다. 스레드를 적용한 새로운 모델은 worker라고 하는데, 다중 프로세스를 사용하며 부하를 줄이고 성능을 높이기 위해 다중 스레드를 사용한다. 최종적으로 event MPM은 실험적인 모듈로 다양한 작업을 수행하도록 독립된 스레드 풀을 유지한다. 현재 사용 중인 MPM을 살펴보려면 httpd -l 명령을 수행한다.
MPM 선택은 여러 가지 요인에 달려있다. 실험 상태를 벗어날 때까지 event MPM 설정을 미뤄두야 하므로, 스레드를 사용할지 스레드를 사용하지 않을지를 놓고 선택이 필요하다. 표면적으로 PHP가 사용하는 모든 라이브러리를 비롯하여 모든 기반 모듈이 스레드 안전을 보장하면 thread 방식은 fork 방식보다 그럴 듯하게 들린다. prefork는 좀더 안전한 선택이다. worker 모델을 선택할 때는 신중하게 실험해야 한다. 성능 향상은 또한 배포판과 하드웨어에 따라오는 라이브러리에 달려있다.
선택한 MPM 종류가 무엇이든 제대로 설정해야 한다. 일반적으로 MPM 설정은 아파치에게 얼마나 많은 worker가 동작할지 제어하는 방법과 스레드나 프로세스 모델을 선택하는 기준을 알려준다. prefork MPM을 위한 중요한 환경 설정 옵션을 Listing 1에 제시한다.

Listing 1. prefork MPM을 위한 환경 설정
StartServers       50
MinSpareServers   15
MaxSpareServers   30
MaxClients       225
MaxRequestsPerChild  4000

소프트웨어 직접 컴파일하기

내가 유닉스(UNIX®)를 시작했을 때, 시스템에 집어 넣는 모든 소프트웨어를 직접 컴파일해야 한다고 우겼다. 업데이트 관리는 궁극적으로 내게 맡겨졌고, 이런 과업을 쉽게 처리하도록 빌드하는 방법을 익혔다. 결국 내가 소비한 시간 대부분은 배포판을 만들기 위한 노력과 중복됨을 깨달았다. 이제 가능하다면 대부분 배포판에서 제공하는 소프트웨어를 사용하며, 반드시 필요한 경우에만 직접 패키지를 만든다.
비슷한 상황으로, 업체에서 제공하는 패키지 유지보수는 최신이자 훌륭한 코드와 함께 하는 장점을 무색하게 만든다. 종종 성능 조율과 시스템 관리 목표가 충돌한다. 상용 리눅스를 사용하거나 외부 협력사 지원에 의존한다면 업체 지원을 고려해보자.
자립하고 싶다면, 배포판으로 패키지를 빌드하는 방법과 패치 시스템으로 통합하는 방법을 배우자. 이렇게 하면 미세 조정과 더불어 소프트웨어가 일관성 있게 만들어지고 다중 시스템에서 동작함을 보장한다. 적절한 메일링 리스트와 RSS 피드 구독을 통해 소프트웨어 업데이트를 최우선 목표로 삼자.
prefork 모형에서 새로운 프로세스는 요청 단위로 생성된다. 여분의 프로세스는 들어오는 요청을 처리하기 위해 쉬고 있으며, 이는 초기 시동 대기 시간을 줄여준다. 직전에 보여준 환경 설정 항목에 따르면 웹 서버가 시동하면서 프로세스 50개를 시작하며, 쉬고 있는 서버가 10개와 20개 사이를 유지하도록 시도한다. 프로세스 최대 한계 수치는 MaxClients로 지정한다. 프로세스가 연속적인 요청을 처리할 수 있음에도 불구하고, 아파치는 접속이 4000개가 넘어간 다음에 프로세스를 죽여 메모리 누수 위험을 방지한다.
스레드 MPM 설정은 비슷하지만 사용하는 스레드와 프로세스 개수를 결정해야만 한다는 점이 다르다. 아파치 문서는 모든 매개변수와 필요한 계산 방법을 설명한다.
사용할 값을 선택하는 동안 시행 착오가 필요하다. 가장 중요한 값은 MaxClients다. 목표는 충분한 작업 프로세스나 스레드가 과도하게 서버 스왑 현상을 일으키지 않으면서 동작하는 데 있다. 처리할 수 있는 용량보다 요청이 많이 들어오면, 최소한 들어온 요청까지는 서비스를 해줘야 하며, 나머지는 기다리도록 만든다.
MaxClients가 너무 높으면 모든 클라이언트가 형편 없는 서비스를 경험하게 된다. 웹 서버가 프로세스 하나를 스왑 아웃하고 다른 프로세스를 돌려야 하기 때문이다. 설정 값을 너무 낮춰잡으면 불필요하게 서비스를 거부할지도 모른다. 높은 부하에서 동작하는 프로세스 수와 아파치 프로세스가 사용하는 메모리 점유 상황을 보여주는 결과는 이 값을 설정할 때 힌트를 준다. MaxClients가 256을 넘어갈 경우 ServerLimit를 동일한 숫자로 설정해야 한다. 이와 관련한 경고를 설명하는 MPM 문서를 주의 깊게 읽어보자.
시작하고 여분으로 남겨둘 서버 수 조율은 서버쪽 역할에 의존한다. 서버가 아파치만 돌릴 경우 Listing 1에 보여준 적당한 값을 사용할 수 있다. 서버를 완전히 활용할 수 있기 때문이다. 시스템이 데이터베이스나 다른 서버를 공유한다면 동작할 서버 여분 숫자를 줄여야 한다.
옵션 활용과 효과적인 중복 지정
아파치가 처리하는 각 요청은 웹 서버가 반드시 따라야 할 제약이나 특별한 명령을 지정하기 위한 복잡한 설정 규칙을 거쳐야 한다. 폴더 접근은 특정 폴더에 대한 IP 주소로 제약을 가하거나 사용자와 암호로 설정할 수 있다. 또한 이런 옵션은 디렉터리 목록을 제공한다면, 특정 파일 유형 처리 방식이나 출력 결과 압축 같은 특정 파일 처리도 포함한다.
이와 같은 환경 설정은 httpd.conf에 디스크 위치를 참조하도록 설정을 명세하는 <Directory>나 참조 값이 URL에서 경로를 나타내는 <Location> 같은 컨테이너 형태를 따른다. Listing 2는 Directory 컨테이너를 예로 든다.

Listing 2. 루트 디렉터리에 적용한 디렉터리 컨테이너
<Directory />
    AllowOverride None
    Options FollowSymLinks
</Directory>

Listing 2에서, Directory/Directory 태그로 둘러쌓인 환경 설정은 특정 디렉터리와 이 디렉터리 하부에 적용된다. Listing 2의 경우에는 루트 디렉터리가 된다. 여기서 AllowOverride 태그는 사용자가 다른 옵션을 중복 지정하지 못하게 만든다(나중에 설명한다). FollowSymLinks 옵션을 활성화하면, 웹 파일을 포함하는 디렉터리 외부에 파일이 존재할지라도 아파치가 요청을 처리하기 위해 심볼릭 링크를 따라가도록 만든다. 이는 웹 디렉터리에 있는 파일이 /etc/passwd를 가리키는 심볼릭 링크일지라도 웹 서버가 기꺼히 요청한 파일을 제공하리라는 사실을 의미한다. 대신 -FollowSymLinks를 사용하면, 이 기능은 비활성화되며, 동일한 요청은 클라이언트에 오류로 반환된다.
마지막 시나리오는 두 가지 사항을 고려하도록 만든다. 먼저 성능 문제다. FollowSymLinks를 비활성화하면, 아파치는 심볼릭 링크가 아님을 확인하기 위해 파일 이름에 속한 각 컴포넌트(디렉터리와 파일 자체)를 점검해야만 한다. 이는 디스크 활동량을 높이므로 부하가 추가로 발생한다. 관련 옵션인 FollowSymLinksIfOwnerMatch는 파일 소유주가 링크 소유주일 경우에 심볼릭 링크를 따라가도록 만든다. 이는 symlinks를 따라가지 못하도록 막는 경우와 비슷한 성능 저하가 일어난다. 최고 성능을 위해서는 Listing 2에 나온 옵션을 사용한다.
보안을 염두에 두는 독자에게 지금 경종을 울리겠다. 보안은 항상 기능과 위험 사이에서 절충을 벌여야 한다. 이 경우에는 속력이 우선이므로 시스템에 존재하는 파일에 대해 무조건 접근을 허용하도록 위험을 감수한다. 한가지 다행인 점은 LAMP 응용 프로그램 서버는 일반적으로 특수 목적으로 운영되며, 사용자는 잠재적으로 위험한 심볼릭 링크를 걸지 못한다는 것이다. 심볼릭 링크 점검을 활성화해야 한다면, Listing 3에서 보여주듯 특정 파일 시스템 영역에만 적용한다.

Listing 3. 사용자 디렉터리로 FollowSymLinks를 제한하기
<Directory />
   Options FollowSymLinks
</Directory>

<Directory /home/*/public_html>
   Options -FollowSymLinks
</Directory>

Listing 3에서, 사용자 홈 디렉터리에 있는 public_html 디렉터리는 자신과 하위 디렉터리에 대해 FollowSymLinks 옵션을 제거한 상태가 된다.
지금까지 살펴보았듯이, 옵션은 주 서버 환경 설정을 통해 디렉터리 단위로 설정할 수 있다. (AllowOverrides로 관리자가 허락했다면) 사용자는 .htaccess라는 파일을 디렉터리에 놓아두는 방법으로 이런 서버 환경 설정 자체를 중복 지정할 수 있다. 시스템에 사용자가 없다는 초기 설명에도 불구하고, 많은 LAMP 응용 프로그램은 이런 기능을 활용해 접근 통제와 URL 다시쓰기를 구현하므로 동작 원리를 이해하는 편이 좋겠다.
AllowOverrides 구문이 원하지 않는 모든 작업을 막아버릴지라도, 아파치는 여전히 해야 할 일이 남아있는지 확인하기 위해 .htaccess 파일을 살펴봐야 한다. 어버이 디렉터리는 자식 디렉터리에서 오는 요청을 처리하도록 지시자를 명세할 수 있는데, 이렇게 하기 위해서는 아파치가 요청 파일 앞에 나오는 디렉터리 구조를 구성하는 각 요소를 살펴봐야만 한다. 이렇게 되면 당연히 각 요청마다 디스크 활동량이 상당히 늘어난다.
어떤 중복 지정도 허용하지 않는 가장 손쉬운 방법으로 아파치가 .htaccess 파일 점검을 하지 않도록 만들면 된다. 특별한 환경 설정은 httpd.conf에 직접 지정한다. Listing 4는 .htaccess 파일을 만들어넣고 AllowOverrides에 의존하는 대신에 사용자 프로젝트 디렉터리를 암호로 보호하도록 httpd.conf에 추가한 내용을 보여준다.

Listing 4. .htaccess 환경 설정을 httpd.conf로 옮기기
<Directory /home/user/public_html/project/>
  AuthUserFile /home/user/.htpasswd
  AuthName "uber secret project"
  AuthType basic
  Require valid-user
</Directory>

환경 설정을 httpd.conf로 옮기고 AllowOverrides를 비활성화하면 디스크 활동량이 줄어든다. 사용자 프로젝트만으로는 그다지 흥미를 끌지 못할지도 모르지만, 바쁜 사이트에 적용할 때 이런 기법이 얼마나 강력한지 생각해보기 바란다.
종종 .htaccess 파일을 제거하지 못하는 경우도 있다. 예를 들어 Listing 5를 보면 특정 파일 시스템에 제약을 두는 옵션이 있을 때, 중복 지정 옵션 자체도 제약을 가할 수 있다.

Listing 5. .htaccess 점검 범위를 줄이기
<Directory />
  AllowOverrides None
</Directory>

<Directory /home/*/public_html>
  AllowOverrides AuthConfig
</Directory>

Listing 5를 구현한 다음에, 아파치는 여전히 어버이 디렉터리에서 .htaccess 파일을 찾지만, public_html 디렉터리에서 멈춘다. 나머지 파일 시스템은 기능적으로 비활성화 상태가 되기 때문이다. 예를 들어, /home/user/public_html/project/notes.html에 사상된 파일을 요청할 경우, 단지 public_html과 project 디렉터리만 탐색한다.
마지막 주의 사항으로 디렉터리 단위 환경 설정에는 순서가 있다. 아파치 조율에 대한 문서는 HostnameLookups off 지시자를 통해 DNS 탐색을 비활성으로 만들어라고 조언한다. 서버에 연결한 각 IP 주소를 역으로 도메인 이름으로 바꾸는 시도는 자원 낭비다. 하지만 호스트 이름에 기반을 둔 제약을 통해 웹 서버가 클라이언트 IP 주소를 거꾸로 찾아내고 이름 진위를 파악하기 위한 결과를 찾아내도록 만든다. 따라서 클라이언트 호스트 이름을 기반으로 하는 접근 제어는 피하되 필요할 때만 기술하도록 영역을 줄이는 방식이 바람직하다.
지속적인 접속
클라이언트가 웹 서버에 접속할 때, 동일한 TCP 연결을 통한 다중 요청을 허용하면 다중 연결에 따른 접속 지연을 줄인다. 이런 방식은 웹 페이지가 여러 이미지를 참조할 경우 유용하다. 클라이언트는 페이지를 요청해 연결 하나로 모든 이미지를 받을 수 있다. 단점으로 서버 쪽 작업 프로세서가 다음 요청으로 옮겨가기 전에 클라이언트가 닫은 세션을 기다려야 한다.
아파치는 지속적인 접속 설정을 다루는 방법인 keepalives 값을 바꾸도록 허용한다. httpd.conf에서 KeepAlive 5를 전역으로 설정해 놓으면, 연결을 강제로 끊기 전에 서버가 연결 하나에 요청 다섯 개를 처리하도록 허용한다. 이 값을 0으로 만들면 지속적인 접속 사용을 비활성화한다. 또한 KeepAliveTimeout을 전역으로 설정해 놓으면 아파치가 세션을 닫기 전에 다른 요청을 얼마나 오랫동안 기다릴지 설정한다.
지속적인 접속 처리는 만능이 아니다. keepalives를 비활성(KeepAlive 0)으로 만들어 놓으면 좋은 사이트도 있고, 활성으로 만들어 놓는 편이 상당한 효과를 발휘하는 사이트도 있다. 유일한 해법을 찾으려면 둘 다 시도해 직접 실험해보면 된다. keepalives를 활성화할 경우 KeepAliveTimeout 2를 지정해 2초 정도로 낮은 타임아웃을 사용하는 편을 권장한다. 이렇게 하면 연속으로 요청을 만들어내기를 바라는 클라이언트에 충분한 시간을 제공하면서도 작업 프로세스가 결코 오지 않을 다른 요청을 기다리느라 시간을 낭비하지도 않게 만든다.
압축
웹 서버는 클라이언트에게 자료를 반환하기 전에 결과물을 압축할 수 있다. 이렇게 하면 웹 서버 CPU 사이클을 사용해 인터넷으로 전송하는 페이지를 좀더 작게 만들 수 있다. CPU 부하를 견딜만한 서버라면 이는 페이지를 훨씬 더 빨리 내려받도록 만드는 훌륭한 방법이다. 압축 후에 페이지 크기가 1/3로 줄어드는 경우도 흔하다.
이미지는 일반적으로 이미 압축되어 있으므로 압축은 텍스트 출력으로 제한해야 한다. 아파치는 mod_deflate를 통해 압축을 지원한다. mod_deflate 활성화 자체는 쉬울지 모르겠지만, 상당히 복잡한 기능을 제공하므로 매뉴얼을 살펴서 설명을 읽어봐야 한다. 이 기사는 적절한 문서 링크(참고자료 절 참조)를 제외한 나머지 압축 환경 설정은 다루지 않는다.
PHP 조율
PHP는 응용 프로그램 코드를 돌리는 엔진이다. 사용할 계획이 있는 모듈만 설치해야 하며, 정적 파일이 아니라 (일반적으로 .php로 끝나는 파일인) 스크립트 파일에 대해서만 PHP를 사용하도록 웹 서버 환경 설정을 바꿔야 한다.
중간 코드 캐싱
PHP 스크립트를 요청할 때, PHP는 스크립트를 읽어 실행하기 위한 코드의 이진 표현인 젠드 중간 코드로 컴파일한다. 이 중간 코드는 PHP 엔진이 수행한 다음에 버린다. 중간 코드 캐시는 컴파일된 중간 코드를 저장해 다음 번에 페이지를 호출할 때 재사용한다. 이런 캐시는 시간을 상당히 절약해준다. 여러 중간 코드 캐시가 존재하는데, 나는 eAccelerator로 재미를 봤다.
eAccelerator를 설치하려면 컴퓨터에 PHP 개발 라이브러리가 필요하다. 리눅스 배포판마다 다른 위치에 파일을 두므로, eAccelerator 웹 사이트에서 직접 설치 명령을 참조하는 편이 최선이다(참고자료 절에 있는 링크를 참조하자). 또한 배포판이 이미 패키지로 묶인 중간 코드 캐시를 탑재하고 있다면, 설치만 하면 끝난다.
eAccerlerator를 어떻게 가져왔거나에 상관없이 몇 가지 살펴볼 환경 설정 옵션이 있다. 이 환경 설정 파일은 /etc/php.d/eaccelerator.ini다. eaccelerator.shm_size는 컴파일된 스크립트를 저장하는 공간인 공유 메모리 캐시 크기를 정의한다. 이 값은 메가바이트 단위다. 적절한 크기는 응용 프로그램에 따라 다르다. eAccelerator는 메모리 사용을 포함하여 캐시 상태를 보여주는 스크립트를 제공한다. 64메가바이트면(eaccelerator.shm_size="64") 적당한 시작값이다. 또한 여러분이 선택한 값을 받아들이지 않을 경우 커널의 최대 공유 메모리 크기를 조정할 필요가 있다. kernel.shmmax=67108864 항목을 /etc/sysctl.conf에 넣고 sysctl -p 명령을 내리면 설정값이 반영된다. kernel.shmmax 값은 바이트 단위다.
공유 메모리 할당을 초과하면 eAccelerator는 메모리에서 옛날 스크립트를 제거한다. 기본적으로 이런 동작은 비활성으로 남아있다. eaccelerator.shm_ttl = "60"을 지정하면 eAccelerator가 공유 메모리 부족을 감지했을 때 60초 동안 접근하지 않은 스크립트를 제거한다.
eAccelerator를 대체할 다른 대안은 Alternative PHP Cache(APC)다. 젠드 제작사는 효율성을 높이기 위한 최적화기를 포함한 상용 중간 코드 캐시를 판매한다.
php.ini
php.ini에서 PHP 환경을 설정한다. 표 1에 정리한 네 가지 중요한 설정 값은 PHP가 얼마나 시스템 자원을 소비할지 통제한다.

표 1. php.ini에서 자원 관련 설정값
설정설명권장값
max_execution_time얼마나 많은 CPU-초를 스크립트가 소비하는지를 지정30
max_input_time얼마나 오랫동안(초) 스크립트가 입력 자료를 기다릴지를 지정60
memory_limit얼마나 많은 메모리를(바이트) 죽기 전에 스크립트가 소비할지를 지정32M
output_buffering얼마나 많은 자료를(바이트) 클라이언트에게 전달하기 전에 버퍼에 저장할지를 지정4096
여기서 설명하는 값은 대부분 응용 프로그램에 따라 달라진다. 사용자에게 대규모 파일을 받아들일 요량이라면, php.ini나 코드에서 max_input_time을 증가시켜야 한다. 비슷하게 CPU나 메모리를 많이 소비하는 프로그램은 더 큰 설정 값을 지정한다. 목적은 폭주한 프로그램을 막아내는 데 있으므로 전역 설정 값을 비활성화하는 상황은 바람직하지 못하다. max_execution_time에 대한 설명을 추가하겠다. 이 설정값은 절대 시간이 아니라 프로세스의 CPU 시간을 참조한다. 따라서 I/O 작업이 많고 계산이 적은 프로그램일 경우 max_execution_time보다 더 오래 동작할지도 모른다. 이는 한 max_input_timemax_execution_time보다 큰 경우를 설명한다.
PHP로 로그 파일을 남기는 양도 조정이 가능하다. 실제 운영 환경에서는 가장 중요한 로그 파일을 제외한 나머지 로그를 끄면 디스크 쓰기를 줄인다. 문제 해결용으로 로그가 필요하면 원하는 만큼 로그 단계를 높일 수 있다. error_reporting = E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR는 문제를 추적할 만큼 충분한 정보를 로그에 남기지만, 스크립트에서 나오는 수다스러운 내용은 제거한다.
요약
이번 기사는 아파치와 PHP라는 웹 서버 조율에 초점을 맞춘다. 아파치의 경우에는 .htaccess 파일 처리와 같은 웹 서버가 반드시 실행해야 하는 추가적인 점검 과정을 건너뛰도록 만든다. 또한 MPM을 조율해 작업 요청을 받아주는 일꾼 숫자와 시스템 자원 사이에 균형을 잡아줘야 한다. PHP로 할 수 있는 최선의 방법은 중간 코드 캐시 설치다. 모든 사람을 위해 스크립트가 시스템을 느리게 만들고 자원을 잡고 있지 않도록 만들기 위해 몇 가지 자원 설정에 촉각을 곤두세워야 한다.
다음에 소개할 연재 기사 마지막 회에서는 MySQL 데이터베이스 조율 기법을 살펴본다. 계속해서 기대하시라!

참고자료
교육
제품 및 기술 얻기
토론
블로그, 포럼, 포드캐스트, 새로운 developerWorks space에 있는 새로운 공동체 토픽을 통해 developerWorks 공동체에 참여한다.