리눅스 시스템을 사용하면서 겪게되는 문제점들에 대해서 원인 및 진단과 해결 방법을 설명하고 있다. 참고로 [데브옵스] 라는 책의 내용을 발췌한 것이다.
부팅 문제 해결하기
루트 파일 시스템을 마운트할 수 없는 경우
GRUB 오류와는 별도로 가장 일반적인 부팅 문제 중 하나는 루트 파일 시스템을 마운트할 수 없는 것이다. GRUB 이 커널과 initrd 파일을 RAM 에 적재하면 initrd 파일은 RAM 에 있는 initramfs 임시 루트 파일 시스템으로 확장한다. 이 파일 시스템에는 커널 모듈과 커널이 루트 파일 시스템을 찾아서 마운트하고 부팅 과정을 진행하는데 필요한 프로그램이 담겨져 있다.
커널이 루트 파일 시스템을 마운트 할 수 없다는 문제를 가장 잘 해결하기 위해 커널이 어떻게 루트 파일 시스템이 시작하는 곳을 아는지 알아둘 필요가 있다.
루트 커널 인자
커널은 GRUB 에 의해 root 옵션으로 루트 파일 시스템의 위치를 받기 때문에 루트 파일 시스템이 어디에 있는지 알고 있다. 커널 인자가 담긴 GRUB 설정 파일을 살펴보면 root=/dev/sda2 나 root=LABEL=/, root=UUID=528c6527-24bf-42dl-b908-c175f7b06a0f 같은 것을 볼 수 있다.
첫 번재 예에서 커널은 명시적으로 디스크 파티션인 /dev/sda2 를 받는다. 루트 기기를 명시하는 이 방식이 예전 시스템에서는 가장 일반적이고 디스크 레이블이나 UUID 를 대체해왔다. 왜냐하면 언제든지 디스크가 추가되거나 재분할 됐기 때문에 /dev/sda2 가 현재는 /dev/sdb2 나 /dev/sda3 으로 되는 것이 가능하다.
기기명이 변경되면서 발생하는 문제를 우회하기 위해 각 배포판에서는 마운트 지점을 토대로 파티션에 이름을 주기 시작했다. 그래서 루트 파티션은 / 나 root 로, /home 파티션은 home 이나 /home 으로 표시됐다. 그러고 나서 root= 라인에 기기를 명시하는 대신 GRUB 에서 root=LABEL=/ 처럼 기기를 표시할 수도 있다. 그런 방식은 실제 기기 이름이 변경되더라도 해당기기의 레이블은 여전히 같으므로 커널은 루트 파티션을 찾을 수 있을 것이다.
레이블은 기기명이 변경되는 문제를 해결하는 것처럼 보였지만 다른 문제를 낳았다. 두 파티션의 레이블이 같다면 어떤일 이 일어날까? 문제가 발생하기 시작하는 것은 서버에 다른 시스템에서 사용됐던 디스크를 두 번째 디스크로 추가할 때다. 이 신규 디스크는 이미 / 이나 /home 레이블을 갖고 있을 수도 있고 새 시스템에 추가됐을 때 커널은 여러분이 알고 있던 해당 레이블로 마운트하지 못할 수 있다. 이 문제를 해결하기 위해 일부 배포판에서는 파티션에 UUID(범용 고유 식별자, Universal Unique Identifiers)를 할당하기 시작했다.
UUID 는 세상의 모든 디스크 파티션을 통틀어 고유함을 보장하기 위한 긴 문자열이라서 시스템에 어떤 디스크든 추가할 수 있고 같은 UUID 를 중복해서 가질 수 없다고 확신할 수 있다. 이제 부팅 프롬프트에 디스크 레이블을 명시하는 대신 root=UUID=528c6527-24bf-42d1-b908-c175f7b06aof 같은 UUID 를 명시할 것이다.
루트 기기를 변경하는 경우
커널이 루트 파티션을 마운트할 수 없는 가장 일반적인 이유 중 하나는 커널에 설정된 루트 파티션이 변경됐기 때문이다. 루트 파티션이 변경됐을 때, “경고! /dev/sdb2 가 없습니다(ALERT! /dev/sdb2 does not exist)” 라는 오류메세지가 나타나고 기본 initramfs 쉘이 보일 것이다.
UUID 를 사용하지 않는 시스템에서는 새 디스크가 추가되고 해당 기기명이 바뀔 때 이러한 현상이 가장 흔히 발생한다(가령 과거 루트 파일 시스템은 /dev/sda2 에 있었지만, 지금은 /dev/sdb2 에 있는 경우).
파티션 레이블을 사용하는 시스템의 경우 루트 파일 시스템을 마운트하지 못하는 상황은 레이블이 같은 파티션이 포함된 디스크를 추가하는 경우나 루트 파티션 레이블을 변경한 관리자에게서 발생할 수 있다. 디스크 레이블 문제를 진단하는 가장 좋은 방법은 레이블 대신 root= 설정을 수정하고 디스크 기기 자체를 명시하는 것이다. 다시 말해, 디스크가 어떻게 배치돼 있는지 모른다면 복구 디스크로 부팅하고 fdisk -l 을 입력하는 것이다.
레이블 대신 루트를 디스크 기기에 설정했을 때 성공적으로 부팅됐다면 레이블 대신 해당 디스크 기기를 사용하기 위해 GRUB 설정 파일을 갱신하거나 루트 파티션의 레이블을 원래 되던 것으로 되돌리기 위해 e2label 프로그램을 사용할 수 있다.
예를 들면, /dev/sda2 에 / 라는 레이블을 할당하려면 다음과 같이 입력하면 된다.
#e2label /dev/sda2 /
중복된 레이블일 경우에도 중복된 루트 파티션을 다른 것으로 명명하기 위해 e2label 을 사용하면 된다. 현재 설정된 기기명이 무엇인지 보여주기 위해 디스크 기기명과 같이 e2label 을 사용할 수 있다
#e2label /dev/sda2
시스템에서 UUID 를 사용하고 커널이 루트 파티션을 찾을 수 없다면 UUID 가 변했을 수 있다. 보통 UUID 는 파티션이 포맷될 때 할당되어야 하므로 루트 파티션에 이런 상황이 일어나는 것은 일반적이지 않다. 그렇긴 하지만 UUID 를 사용하는 시스템을 오프라인 기반으로 복제할 때 종종 발생한다. 복제된 시스템에 대한 루트 파티션을 생성할 때 해당 파티션은 새로운 UUID 를 얻지만 GRUB 설정 파일이 복사됐을 때 해당 설정은 예전 UUID 를 가리키고 있다.
디스크 레이블 문제처럼 이 문제를 해결하는 빠른 방법은 GRUB 메뉴에 있는 부트 프롬프트를 수정하고 UUID 대신 지정된 기기를 명시하도록 root= 설정을 변경하는 것이다. 이 방식으로 부팅 과정을 더 살펴보면 특정 기기에 할당된 UUID 를 보기 위해 blkid 명령을 사용할 수 있다.
#blkid -s UUID /dev/sda2 /dev/sda2: UUID="528c6527-24bf-42d1-b908-c175f7b06aof"
어떤 UUID 가 설정되어야 할지 알았다면 GRUB 설정 파일 그리고 /etc/fstab 을 수정해서 적절한 UUID 를 참조하게 한다.
네트워크 문제 원인 추적하기
iftop 을 이용한 네트워크 대역폭 조사하기
때때로 네트워크는 원격 서버나 라우터 때문에 느려지는 것이 아니라 시스템 상의 뭔가가 사용 가능한 모든 대역폭을 사용해 버려서 느려지기도 한다. 어느 프로세스가 모든 대역폭을 사용하고 있는지 파악하는 것은 까다로운 일이 될 수도 있지만 원인을 파악하는 데 도움되는 몇가지 도구가 있다.
top 과는 다르게 iftop 은 프로세스에 대해서는 관여하지 않지만 서버와 원격 IP 사이에서 대부분의 대역폭을 사용하고 있는 연결목록을 보여준다. 예를들어, iftop 결과의 상단에 표시되는 백업 서버의 IP 주소를 확인함으로써 백업 작업이 모든 대역폭을 사용하고 있는지 금방 확인할 수 있다.
iftop 은 레드햇 기반의 시스템과 데비안 기반의 배포판 모두에서 같은 이름으로 패키지를 이용할 수 있다. 패키지 설치 후, iftop 명령어를 실행하기만 하면 된다. top 과 마찬가지로 Q 를 누르면 종료할 수 있다.
iftop 화면의 최상단에는 해당 인터페이스의 전반적인 트래픽을 볼 수 있는 표시줄이 있다. 바로 아래에는 송신(source) IP 주소와 수신(destination) IP 주소 칼럼이 있고, 두 칼럼 사이에 방향 표시가 있어 이를 통해 호스트에서 전송되는 패킷 혹은 원격 호스트로부터 전송되는 패킷에 대한 대역폭 사용량을 살펴볼 수 있다. 이러한 칼럼 다음에는 세 개의 칼럼이 더 있는데, 각각 2초, 10초, 40초 동안의 두 호스트 간의 데이터 전송률을 나타낸다. 평균 부하 값처럼 현재 대역폭이 급상승했는지 또는 과거에 급상승했었는지 할 수 있다.
화면의 맨 아래에는 전송된 데이터(TX)와 수신된 데이터(RX), 그리고 전체 합계에 대한 통계를 볼 수 있다. top 과 마찬가지로 인터페이스 화면은 주기적으로 갱신된다.
iftop 명령어를 인수 없이 실행해도 대부분의 경우 문제 해결에 필요한 거의 모든 정보를 얻을 수 있지만, 때로는 iftop 의 일부 옵션을 활용하고 싶을 수도 있다. iftop 명령어는 기본적으로 찾을 수 있는 첫 번째 인터페이스에 대한 통계정보를 보여 줄 것이다. 하지만 서버에 인터페이스가 여러 개 있고 두 번째 이더넷 인터페이스(eth1)에 대해 iftop 을 실행하고 싶다면, 'iftop -i eth1' 이라고 입력한다.
기본적으로 iftop 은 모든 IP 주소를 호스트명으로 변환하려고 시도한다. 이 경우 한가지 단점은 원격 DNS 서버가 느린 경우 iftop 의 결과 보고 속도가 느려질 수 있다는 것이다. 또한 모든 DNS 변환이 추가적인 네트워크 트래픽을 발생시키고 이것이 iftop 에 나타날 수 있다. 네트워크에서 DNS 변환을 비활성화하려면 iftop 을 -n 옵션과 함께 실행하면 된다.
일반적으로 iftop 은 호스트 사이에 사용되는 전체 대역폭을 표시하지만 범위를 좁히기 위해 호스트가 통신에 사용되는 포트를 확인할 수도 있다. 결국 호스트가 대부분의 대역폭을 웹 포트를 통해 소비한다는 사실을 알게 되었다면 FTP 포트 연결과는 다르게 문제를 해결해야 할 것이다. 일단 iftop 이 실행되었을 때, P 키를 누르면 모든 포트가 표시되고, 한번 더 누르면 모든 포트 정보가 감춰진다.
여기서 한 가지 주목해야 할 점은 때로는 모든 포트를 표시하게 하는 기능 때문에 여러분이 주의 깊게 살펴봐야 할 호스트가 화면 밖으로 밀려날 수도 있다는 것이다. 이와 같은 경우가 발생하면 S 혹은 D 키를 눌러 송신 호스트 혹은 수신 호스트의 포트 정보만 개별적으로 표시되게 할 수도 있다. iftop 을 서버에서 실행할 때는 송신 포트만 보이게 하는 것이 유용할 수 있다.
왜냐하면 많은 서비스들이 수신 호스트에서는 어떤 서비스인지 식별할 필요가 없는 임의의 높은 번호 포트를 생성해서 사용하기 때문이다.
그러나 여러분의 서버 포트는 각 서비스에 해당될 가능성이 높다. 그런 다음 netstat -lnp 명령어를 사용해 어떤 서비스가 어떤 포트로 리스닝하고 있는지 알아낼 수 있다. 대부분의 리눅스 명령어처럼 iftop 도 다양한 고급 옵션을 제공한다. 자세한 내용을 파악하고 싶다면, man iftop 을 입력하기 바란다.
ethtool 을 이용한 연결여부 확인하기
문제 해결을 위한 첫번째 단계는 클라이언트에서 수행한다. 맨 먼저 클라이언트의 네트워크 연결이 문제가 없는지 검증하고 싶을 것이다. 이를 위해 ethtool 프로그램(ethtool 패키지를 통해 설치된다)을 이용해 네트워크 연결이 활성화돼 있는지(이더넷 디바이스가 물리적으로 네트워크에 연결돼 있는지) 확인 할 수 있다. 어떤 인터페이스를 사용하는 지 확실하지 않다면 ifconfig 명령어를 실행해 사용 가능한 모든 네트워크 인터페이스 목록과 설정을 확인할 수 있다. 이더넷 디바이스가 eth0 에 있었다면 결과는 아래와 같다.
위의 마지막 줄에서 Link detected 가 yes 로 설정돼 있으므로 dev1 은 물리적으로 네트워크에 연결되어 있음을 확인할 수 있다. 이 값이 no 로 설정되어 있다면 dev1 의 네트워크 연결에 대해 물리적으로 검사해 볼 필요가 있고 실제로 연결됐는지 확인해야 한다. 여기서는 물리적으로 연결되어 있으므로 다음 단계로 넘어갈 수 있다.
ethtool 은 단순히 연결을 검사하는 것 이상의 쓰임새가 있다. ethtool 은 양방향(duplex) 문제를 진단하고 수정하는 데 사용할 수 있다. 리눅스 서버가 네트워크에 접속할 때 일반적으로 네트워크에서 사용할 속도를 확인하고 네트워크가 양방향 동시 전송방식(full-duplex)을 지원하는 지 파악하기 위해 자동 협상을 한다. 이 예제에서 속도와 양방향 회선(duplex lines)에 대한 ethtool 의 실행 결과는 100Mb/s 의 양방향 동시 전송 방식 네트워크를 지원한다는 것을 보여준다.
호스트에서 네트워크 속도가 느리다는 것을 발견했다면 그에 대한 속도와 이중화 설정을 살펴보는 것이 도움이 될 것이다. 앞의 예제에서와 같이 ethtool 을 실행하고 Duplex 값이 Half 로 설정되어 있다면 다음 명령을 실행한다.
#ethtool -s eth0 autoneg off duplex full
여기서 eth0 을 여러분의 이더넷 디바이스로 바꿔 실행하면 된다.
웹 서버 문제 추적하기
Curl 로 웹서버 테스트하기
해당 주소의 웹서버가 동작하는 지 확인하는 가장 간단한 방법은 웹 브라우저로 접속해보는 것이다. 만일 브라우저를 사용할 수 없는 환경이라면, 우리는 Curl 을 사용할 수 있다.
사용방법 역시 간단하다.
#curl http://likewind.org
정상적인 경우라면, HTML 태그와 함께 홈페이지의 내용이 출력될 것이다.
Telnet 으로 웹서버 테스트하기
만일 Curl 이 설치되어 있지 않은 환경이라면, 어떻게 해야 할까? 그렇다면, telnet 을 이용해서 테스트를 해볼 수 있다. 이 역시 사용방법은 간단하다.
#telnet likewind.org 80 Trying 183.111.141.49... Connected to likewind.org. Escape character is '^]'.
일단 웹서버에 연결되고 나면 몇 가지 기본적인 HTTP 를 입력해볼 수 있다.
GET / HTTP/1.1 host: www.example.net
이 예제에서는 HTTP 프로토콜(HTTP/1.1)에 이어서 해당 사이트에 대한 기본 인덱스 페이지를 요청하는 기본 GET 요청(GET /)을 사용한다. 예를 들어 /admin/inventory.cgi 페이지를 테스트하고 싶다면 GET /admin/inventory.cgi HTTP/1.1 이라고 입력할 것이다.
그런 다음 엔터키를 치고 host: 와 함께 연결하고자 하는 호스트의 이름을 입력한다(URL 에서 http:/// 직후에 나오는 부분).
이 host 파라미터는 웹 서버들이 종종 많은 가상 호스트를 동일한 두 장비에 두기 때문에 중요하다. 그래서 원하는 호스트를 지정하지 않은 경우 기대했던 웹 페이지가 나오지 않을 수도 있다.
일단 host: 가 있는 줄의 입력을 끝내고 엔터 키를 입력하면 서버로부터 완전한 응답을 받을 것이다.
GET /wiki.php HTTP/1.1 # 입력 host: likewind.org # 입력 # Enter 입력 HTTP/1.1 200 OK Server: Apache Date: Sat, 27 Jul 2013 15:38:45 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive Vary: Accept-Encoding P3P: CP='NOI CURa ADMa DEVa TAIa OUR DELa BUS IND PHY ONL UNI COM NAV INT DEM PRE' X-Powered-By: PHP/5.3.13p1 Last-Modified: Sat, 06 Jul 2013 06:50:13 GMT 1ea9 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> <script type="text/javascript"> /*<![CDATA[*/ _url_prefix=""; /*]]>*/ </script><meta name="robots" content="noindex,nofollow" /> <link rel="Start" href="FrontPage" /> <link rel="Index" href="TitleIndex" /> <link rel="Help" href="HelpOnFormatting" /> <link rel="Copyright" href="FrontPage" /> <link rel="Search" href="FindPage" /> <link rel="Glossary" href="WordIndex" /> <link rel="Alternate History" title="Page History" href="?action=info" /> <link rel="Alternate" title="xml" type="application/rss+xml" href="?action=rss_rc" /> <link rel="Alternate" title="Wiki Markup" type="text/plain" href="?action=raw" /> <script type='text/javascript' src='/local/js/i18n.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/Util.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/Toolbar.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/Wikitext.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/Preview.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/HTML.js'></script> <script type='text/javascript' src='/local/Wikiwyg/lib/Wikiwyg/Wysiwyg.js'></script> <script type='text/javascript' src='/local/ajax.js'></script> <script type='text/javascript' src='/local/moniwyg.js'></script> <script type='text/javascript' src='/local/ASCIIMathML.js'></script> <meta http-equiv="last-modified" content="Sat, 06 Jul 2013 06:50:13 GMT" > <title>Fat81 Wiki: Front Page</title> <link rel="Alternate" title="Wiki Markup" href="/wiki.php?FrontPage&action=raw&dummy=1" /> <link rel="Alternate" media="print" title="Print View" href="/wiki.php?FrontPage&action=print&dummy=1" /> <link rel="stylesheet" type="text/css" media="screen" href="/theme/blog/css/default.css" /> <script type="text/javascript"> /*<![CDATA[*/ url_prefix="/wiki.php"; _qp="?"; FrontPage= "FrontPage"; /*]]>*/ </script> <script type="text/javascript" src="/css/kbd.js"></script> </head> <div id='wikiContent'>
요청을 완료했다면 'Ctrl-]' 키를 눌러 telnet 을 종료할 수 있다. 기본적으로 더 많은 문제 해결 정보를 curl 보다 telnet 에서 얻을 수 있다. 위 예제에서는 HTTP 상태 코드(200 OK), 요청한 날짜와 함께 페이지가 수정된 최종날짜, 웹 서버 버전, 그리고 일반적으로 웹 브라우저에서는 표시되지 않는 다른 데이터를 볼 수 있다.