저자: 마이클 루카스(Michael Lucas), 역 송종범
이 기사는 시스템 패닉을 다루고 있으며 1부와 2부로 나누어 시스템 패닉을 다룰 것이다. 우선 제 1부에서는 FreeBSD 시스템에서 시스템 패닉을 방지하는 방법에 대해 설명하고 2부에서는 최악의 상황이 발생했을 때 어떤 조치를 취해야 하는지에 대해 설명할 것이다.
지금까지 필자는 FreeBSD로 매우 쉽게 작업을 해온 것으로 신뢰있는 명성을 쌓아왔다. 그래서 필자는 어떤 고객이 전화해서 "서버가 하루에 두 번이나 다운되었어요!"라고 말했을 때 놀라지 않을 수 없었다.
이 고객은 ISP를 운영하며 메일 서버와 웹 서버는 전적으로 FreeBSD에 의존하고 있었다. 그의 2.2.8-안정버전 컴퓨터들은 거의 1년의 업타임(uptime)을 가지고 있었지만(더 길지도 모른다), 어느날 밤 서버실에서 전원 케이블들을 다시 정리해야만 하는 불상사를 겪었다. 이 시스템들은 2.4-안정버전으로 운영되었고 몇 달간 가동중이다.
로그인 프롬프트대신, 콘솔은 아래와 같은 메세지를 보여주었다.
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x80c0153a
fault code / supervisor write, page not present
instruction pointer = 0x8:0xc015aa84
stack pointer = 0x10:0xc7377e7c
frame pointer = 0x10:0xc7377e80
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, def32 1, gran 1
processor eflags = interrupt enabled, resume, IOPL=0
current process = 5 (syncer)
interrupt mask = bio
trap number = 12
panic: page fault
만일 초보 시스템 관리자라면 위의 출력 메시지를 보고 간담이 서늘해 질 수도 있다. 보통의 유닉스, 특히 FreeBSD는 일반적으로 무엇이 잘못되었는지를 메시지로 친절히 보여주며, 최악의 경우라도 자주 사용하는 서치 엔지에서 검색할 수 있는 여지를 준다. 여기서 애매하게 보이는 유일한 단어는 "syncer"이다. 대부분의 사람들은 syncer가 무엇인지 모르기 때문이다. 이러한 사항에 처한 대부분의 사람들은 자신이 이러한 것을 고치기 위해 노력했던 것을 아주 잘 알고 있다. 그리고 이와 같은 "미스테리한 패닉"은 FreeBSD에서 가질 수 있는 최악의 상황이다.
몇년 전 필자에게 이런 일이 처음 일어났을 때 종이와 연필을 찾기 위해 온 사방을 허둥지둥 해맸던 것으로 기억된다. 결국 낡은 봉투 하나와 부러진 몽땅 연필을 찾아서 서버가 놓여진 선반과 거친 벽 사이를 기어갔다. 한 손에는 6인치의 흑백 모니터를 뒤로 끌면서 균형을 맞추고 다른 손에는 낡은 봉투를 잡고 벽에 기대었다. 이렇게 되고 보니 봉투에 패닉 메시지를 적을 세 번째 손이 필요했는데 어쨌든 성공했기 때문에 마치 내가 손이 세 개나 된 듯 했다. 마침내 비좁고 거칠은 선반을 빠져나와서 그 모든 패닉 메시지를 이메일로 성공적으로 보냈다. 최고의 FreeBSD 개발자들이 이제 이 메시지를 받아보고 무슨 일이 발생했는가를 정확히 말해줄 수 있을 것이다.
이런 고군분투 후에 온 즉각적인 응답은 나를 매우 좌절시켰다. "역추적을 보내주실 수 있습니까?"
이같은 문제를 보고한 수많은 메시지가 FreeBSD 메일링 리스트에 올라온 것을 볼 수 있었다. 그들은 내가 이미 받은 것과 같은 메시지를 남겼다. 이 사람들은 절대 다시 문제의 해결법을 듣지 못했으며 필자는 그들이 정확히 어떻게 느끼는지 이해할 수 있었다. 크래쉬되거나 더 악화되어 크래쉬되고 있는 중의 서버를 다루고 있을 때 취하는 마지막 조치는 서버를 재설정 하는 것이다.
그렇지만 이 문제를 해결할 간단한 방법이 있다. 패닉이 발생하기 전에 패닉을 조정할 수 있도록 서버를 설정해 놓는 것이다. 그리고 이 작업은 서버를 설치할 때 하는 것이 좋다. 이렇게 해두면 크래쉬가 발생할 경우 자동적으로 역추적을 얻을 수 있기 때문이다. 처음 듣게되는 개념일 수도 있으며 이것은 FreeBSD 문서에도 강조되는 부분은 아니지만 일리는 있다. 재해를 대비해두자. 미리 이렇게 해두면 절대 일어나지 않아도 불평할 일이 없고 패닉이 일어나면 이에 대응할 준비도 된 것이다. 이제 풀 디버깅 덤프로 FreeBSD Folk를 표현할 수 있다.
봉투위에 쓴 패닉 메시지는 단지 발생한 스토리의 일부만을 보여주는 것이다. 이것은 도난 차량을 "범퍼에 긁힌 자국 있는 빨간색 차"라고만 기술하는 것과 다를 바 없다. 자동차 제조사, 모델, 차량 번호나 자동차 등록번호를 알려주지 않으면 경찰관이 도난 차량을 쉽게 찾아 낼 수 없는 것과 같은 이치이다. 마찬가지로 크래쉬되는 커널로부터 자세한 정보를 얻을 수 없다면 FreeBSD 개발자들도 범죄코드를 잡을 수 없다.
표준 FreeBSD 커널을 설치하면 이전에 설치한 커널로부터 모든 디버깅 정보를 제거한다. 이 디버깅 정보는 머신 코드와 소스 코드 사이의 맵을 제공하는 "symbols"을 포함한다. 그런데 이러한 맵이 실제 프로그램보다 클 수도 있다. 하지만 누구도 원래보다 3배 이상 큰 커널을 운영하고 싶어하지 않는다. 이 맵은 또한 소스 코드 라인 넘버의 완전한 리스트를 포함하고 있기 때문에 개발자들은 어디서 무엇이 발생했는가도 정확히 알 수 있다. 이런 정보가 없다면, 개발자들은 커널 코어에서 소스코드까지 손수 매핑하는데 열중 할 수 밖에 없다. 이것은 그림 없이 백 만개의 퍼즐을 맞추려고 노력하는 것과 같다. 어쩌면 모든 조각을 가지고 있는지조차 모르는 것과 같다. 이는 어리석은 일이다. 그리고 이러한 작업을 해야 할 필요가 있는 개발자가 자원봉사자라면 이는 더욱 더 어리석은 짓이다.
커널 패닉에 대비하여, 시스템 소스 코드도 설치되어 있어야 할 필요가 있다. 하나 또는 하나 이상의 스왑 파티션이 필요한데 여기에는 최소한 1메가 이상의 물리적 메모리가 있어야 하며 RAM 메모리의 2배 이상을 권장한다. 예를 들어 RAM의 메모리가 512MB라면, 최소 513MB 이상의 스왑 파티션이 필요하며 1024MB를 권장하는 바이다. (서버에서는 확실히 여러 드라이브에 여러 스왑 파티션들이 필요하다!). 그 정도의 메모리도 없다면 알맞은 스왑 파티션이 있는 하드디스크를 추가하거나 재설치해야만 한다. /var 파티션 공간이 많은 도움은 되겠지만 여기서는 굳이 필요하지는 않다.
커널 크래쉬를 캡쳐하는 과정은 다음과 같다. 올바르게 설정된 시스템이 크래쉬되면, 시스템 메모리의 코어 덤프를 저장할 것이다. 파일로는 저장할 수 없다. 왜냐하면 크래쉬된 커널은 파티션에 관한 것만 알지 파일에 대해서는 모르기 때문이다. 이런 덤프를 쓸 장소는 스왑 파티션이다. 덤프는 가능한한 스왑 파티션의 목적에 가깝도록 대치된다. 일단 크래쉬된 시스템이 코어를 스왑에 저장하면, 시스템은 컴퓨터를 재부팅한다.
재부팅하는 동안 /etc/rc는 스왑파티션을 활성시킨다. 그리고 크래쉬된 디스크들에 fsck를 실행한다. fsck를 실행하기 전에 스와핑을 가능하게 해야 한다. 왜냐하면 fsck는 스왑공간을 필요로 할지도 모르기 때문이다. 스왑 파티션에 숨어 있는 덤프 파일을 덮어 쓰지 않고 fsck가 필요한 모든 것을 얻을 수 있는 충분한 스왑 공간을 가지고 있다고 희망을 가지자. 일단 시스템이 코어 덤프를 저장할 수 있는 장소를 가지고 있다면 덤프를 위해 스왑 파티션을 점검한다. 코어를 찾는 즉시 savecore는 그것을 swap에서 적당한 파일로 복사하고 스왑에서의 덤프를 깨끗이 처리한 후 재부팅을 한다. 이젠 커널 코어 파일을 가지고 역추적을 할 수 있다.
여기 주어진 예제는 FreeBSD 4-안정버전을 위한 것이다. 만일 2.3-안정버전을 운영중이라면, 방법이 약간 다를 수 있다. 지금 운영중이라면 이전에 했었어야 했다.
이 작업을 하기 위한 첫 단계는 디버깅 커널을 만드는 것이다. 커널을 어떻게 만드는지 안다는 가정 하에 설명을 계속하겠다. 만일 모른다면 FreeBSD의 목차들을 살펴보아라. 여러분이 해야 할 일의 전부는 커널 설정에 다음과 같은 라인을 추가하는 것 뿐이다.
options DDB
makeoptions DEBUG=-g
DDB 옵션은 DDB 커널 디버거를 설치한다.(엄밀하게 말해 정말로 필요한 것은 아니지만 도움이 될 수 있고 많은 공간을 차지하지 않기 때문이다.) 마지막으로, 여기서 설정한 makeoptions는 시스템에 디버깅 커널을 만들라고 알려준다.
시스템 설정시 패닉 후에 시스템이 어떻게 작동하길 원하는가를 결정해 줄 필요도 있다. 컴퓨터가 재부팅되길 원하는가? 아니면 일일이 재부팅을 할 때까지 패닉 스크린 상태로 지속할 것인가? 시스템이 멀리 떨어진 곳에 있다면 대부분 컴퓨터가 재부팅되기를 원할 것이다. 만일 콘솔 앞에 있다면 커널 변화들을 디버깅하고, 그렇지 않고 파일 시스템 오류들을 발견했다면 확실히 시스템이 재부팅하기를 원할 것이다.
시스템이 저절로 재부팅 되길 원한다면 커널 옵션에 DDB_UNATTENDED를 포함해라. 그렇지 않으면 시스템은 재부팅할 때까지 기다릴 것이다.(여러분을 위해 거의 알려지지 않은 BSD 트릭을 알려주겠다. 한 줄에 한 가지 이상의 옵션을 명기할 수 있다.)
options DDB, DDB_UNATTENDED
일단 커널을 원하는 방향으로 셋업했다면 일반적인 설정과 설치를 해주면 된다. 이것이 끝나면, kernel.debug라 불리는 커널 컴파일 디렉토리를 발견할 것이다. 이것은 심볼을 가진 커널이다. 어디에든지 저장하라. 이 과정이 실패하는 빈번한 원인 중 하나는 디버깅 커널을 잊고서 다른 kernel.debug를 가진 크래쉬된 커널을 디버그 하려고 노력하하기 때문에 발생한다. 이것은 작동하지 않는다. 필자는 일반적으로 kernel.debug를 /ar/crash/kernel.debug.date에 복사한다. 따라서 특별한 디버그 커널이 만들어질 때 나는 그 사실을 알려줄 수 있다. 이것은 필자로 하여금 현재 커널과 디버깅 커널을 매일 매치(match) 시켜주며 그것은 kernel.debug가 삭제해도 될 정도로 시간이 많이 흐르면 그 사실도 알려준다.
이제 /etc/rc.conf에 알맞은 옵션을 설정해보자. 우선 시스템에 덤프를 어디에 써야 할지부터 알려주어야 할 것이다. 이것은 dumpdev라 불리며, FreeBSD는 스왑 파티션을 덤프 장치처럼 사용한다. 이러한 이유로 스왑 파티션이 차지하는 메모리는 물리적 메모리보다 약간 커야 한다. (UFS 파티션을 사용할 수도 있지만 크래쉬 후에는 UFS 파티션을 사용할 수 없다!) 장치명은 /etc/fstab에서 얻을 수 있다. "스왑" 엔트리를 가진 파일 시스템 라인을 찾아보자. 그 줄의 첫 번째 엔트리는 물리적 장치명이다. 필자의 랩탑에는 /etc/fstab에 스왑 필드가 아래와 같이 나와있다.
/dev/ad0s4b none swap 0 0
필자의 스왑 파티션은 /dev/ad0s4b이기 때문에 /etc/rc.conf안에 덤프 장치로서 이것을 명기했다.
dumpdev="/dev/ad0s4b"
다음 단계는 재부팅 후에 덤프를 어디에 저장하는지 알려주는 것이다. 기본값은 /var/crash이지만 rc.conf의 dumpdir 설정에서 바꿀 수 있다.
패닉 상황을 대처해 나가는데 더욱 더 많은 경험을 쌓아갈 수록 코어 저장 작업을 더욱 더 잘 해야 한다는 사실을 깨닫게 될 것이다. savecore(8)장을 읽고 /etc/rc의 savecore_flags에 적당한 옵션을 설정해라. 많이 쓰이는 플래그는 -z로서 코어 파일을 압축시켜주고 디스크 공간을 일부 절약할 수 있다. savecore(8)은 덤프에서 사용되지 않는 메모리를 자동으로 제거해줄 만큼 영리하다.
컴퓨터가 다음 번에 크래쉬되고 여러분이 그 앞에 앉아 있다면 패닉 메시지를 볼 수 있을 것이다. 시스템은 자동으로 재부팅하도록 설정되었고, 숫자들이 흘러가기 시작함에 따라 디스크에 덤프된 메모리의 메가바이트를 카운팅할 것이다. 마지막으로, 컴퓨터를 재부팅하면 Fdisk가 실행되고, savecore가 배드 메모리 덤프에서 디스크로 복사되는 것을 볼 수 있다.
만일 시스템이 자동적으로 재부팅되지 않으면 패닉후에 디버깅 프롬프트에서 다음과 같은 명령어를 두 라인만 타이핑해 넣으면 된다. panic을 치면 디스크를 동기화(sync)해 줄 것이며, continue을 치면 재부팅 과정이 시작될 것이다.
/var/crash 파일 안에 코어 덤프 파일을 가지고 있어야 한다. 다음 기사에서는 이것을 가지고 무엇을 할지 논의하도록 하겠다.
마이클 루카스(Michael Lucas)는 미시간주 디트로이트에서 아내와 애완동물을 키우면서 살고 있다. 현재 Great Lakes Technologies Group에서 네트워크 아키텍처로 근무하고 있으며 자신이 일하고 있는 곳이 좋게 말해 자기가 가진 모든 골치 덩어리라고 한다.