개발자의 길

[mail] 기업 환경을 위한 리눅스 블랙박스 만들기 본문

6. 리눅스

[mail] 기업 환경을 위한 리눅스 블랙박스 만들기

자르르 2010. 1. 20. 13:52


7.1 20만통의 메일과 sendmail

이번달에는 webmin등을 이용한 서버 설정에서 잠시 벗어나 본격적인 서버 설정에 대해서 알아보자. 아무리 기업환경을 대상으로 한다고 해도 리눅스에 대한 개념을 갖추지 못한 사용자만이 있는 것은 아니며 유닉스에 익숙한 관리자들도 많을 것인데 이들은 유닉스에 익숙하고 리눅스를 유닉스 중의 하나로 보고 일반적인 관리방법만을 적용하고 있다.

왜냐하면 현재 인터넷등에서 리눅스에 대한 글은 초보와 중급 정도의 독자를 대상으로 하고 있으며 대부분의 글이 처음 설치와 제대로 동작하기 정도에서 멈추기 때문이다. 여기저기 흩어져 있는 설정 방법들을 모두 섭렵하기에는 관리자들이 시간이 없으며 그런 문제를 전문적으로 해결해 줄 곳도 없기 때문이다. 또한 아무리 오픈소스가 인기가 있고 리눅스 열풍이 한반도를 휘몰아쳐도 여전히 나는 남의 정보를 얻어 오지만 내 것은 줄 수 없다는 생각이 지배적이기 때문이다.

필자는 여기 저기서 특별한 개발이 진행되고 있다는 소식을 주워 듣고는 있는데 제품화가 진행되고 있다는 소리는 있지만 이 것을 공개하고 있다는 이야기는 듣지 못하고 있다. 아무리 리눅스가 바람을 타더라도 이런 상황에서는 아무도 도약할 수 없다. 리눅스 마케팅의 대상은 한국이 아니라 전세계 시장이며 이런 마케팅은 정보의 공개에 의해서 그 힘을 얻을 수 있다. 각자 알고 있는 것을 일단 공개하자. 얻은 만큼 주어야 클 수 있고 보답이 돌아온다는 것을 명심해야 하지 않을까? 이 글은 필자가 대규모 메일 중계 서버를 관리하면서 알게된 경험을 바탕으로 쓴 글이다. webmin으로 하는 서버 설정을 기대하고 이 글을 읽는 독자들에게는 다소 어려운 글이겠지만 나중에는 결국 도움을 줄 수 있는 글이 될 것이다.

한 개의 서비스가 대규모화 되고 소위 엔터프라이즈급의 처리가 가능하기 위해서는 소규모일 때에는 전혀 문제가 되지 않던 것들이 엄청난 문제를 일으키게 된다. 리눅스가 대규모 서버로서 제대로 설 수 있기 위해서는 어떤 것들을 고려해야 하는지에 대해 알아보자. 이 글에서는 메일서버에 대해서만 언급하고 있지만 꼭 그 것에만 국한 된 것은 아니며 웹서버나 삼바등에서도 활용가능하다.

7.2 숙제풀이

지난 달에 sendmail이 가능하면 처리할 메일을 즉시 보내기를 하도록 하는 방법에 대해 물었다.

그 방법은 다양한데 가장 빠른 방법은 실제로 과중한 부담을 받는 메일서버를 관리하면서 고쳐 나가보는 것이다. 숙제의 답은 본문 속에서 찾아 볼 수 있다. 리눅스에서 하루에 20만통의 메일을 전송, 수신, 중계를 해야할 때 생길 수 있는 문제에 대해서 생각하다보면 이 정도의 문제는 쉽게 답을 낼 수 있을 것이다.

7.3 이글의 범위

이 글은 sendmail을 인스톨하고 설정하는 법에 대한 설명이 아니다.

기본적인 설정에 대해서는 언급하지 않는다. 네임서버 설정과 MX 레코드등에 대해서도 언급하지 않는다. 사용자명 재지정을 위한 alias, 수신 메일의 도메인을 처리할 것인가 여부를 결정하는 sendmail.cw, 수신 메일의 도메인과 사용자명을 바꿔치기하는 virtusertable, 변경전의 도메인을 새 도메인과 일치시키는 domaintable, 송신메일에서 사용자와 도메인명을 바꿔치기하는 genericstable, 상태편 사이트의 메일서버 문제를 처리하는 mailertable, 메일을 처리해 주는데 문제가 없는 사이트로 인정하는 sendmail.ct 등에 대해서 구체적으로 언급하지 않는다. 독자적인 sendmail.cf를 만들기 위해서 필요한 m4 프로그램 사용법도 마찬가지이다. 알고 싶으면 /usr/lib/sendmail-cf를 조사해 보기 바란다. 배포본의 설정은 /usr/lib/sendmail-cf/cf/redhat.mc에 들어 있다. m4 사용법은 이 글의 주제가 아니며 자세한 관련 문서가 같은 디렉토리에 있고 인터넷을 통하면 쉽게 한글문서도 구할 수 있다.

이 글에서 언급하는 대부분의 프로그램에 대한 사용법은 알고 있어야 한다. 모른다면 프로그램 예가 나올 때 마다 메뉴얼 페이지를 조사할 것을 권한다. sendmail 자체에 대해서 잘 모르는 사용자들이나 하루에 100개 미만의 메일을 처리하는 서버를 다룬다면 이 글을 읽지 않아도 좋다. 일반적인 sendmail의 옵션을 알고 싶으면 소스에 함께 배포되는 설명파일을 읽던지 인터넷을 뒤지던지 관련 서적을 구입할 것을 권한다. 시스템 설정 과정에 대한 사항도 자세히 설명하지 않는다. 이 글의 목적은 엔터프라이즈급 환경에서 리눅스를 운용할 때, 특히 메일서버로 사용할 때 제기될 수 있는 문제에 대해서 쓰는 글이며 메일서버 설정법에 대한 초보적이고 지루한 강의가 아니기 때문이다.

7.4 필요한 것들

리눅스를 메일서버로 사용하기 위해서는 sendmail 패키지가 필요하다. 물론 대부분의 배포본에는 기본적으로 메일서버가 인스톨되도록 만들어져 있다. 대규모 사이트가 아니라면 기본적인 설정으로 문제가 없다. 대규모 사이트에서 메일분산을 위해서는 sendmail 소스 배포본의 프로그램이 필요하므로 소스도 가져오기 바란다. 자체 네임서버도 필요하다. 배포본의 캐싱만 하는 네임서버를 인스톨해 둘 것. 그외 syslogd와 logrotate, top, killall 프로그램 사용법에 대한 지식도 필요하다. 하드웨어는 하루 전송량 1000통이하라면 팬티엄200, 메모리 64메가정도, 그 이상이라면 팬티엄II-450, 메모리 128혹은 256이상이 필요하다. 서버라면 스카시 하드 디스크를 사용하고 있을 것이다. IDE도 상관없지만 속도가 문제라면 스카시 하드 디스크가 적당할 것이다. 네트웍은 가능한한 빠른 것이 좋으며 트래픽을 감시하면서 메일서버의 처리량이 한계를 넘어가면 네트웍 속도를 업그레이드 하던지 속도에 맞게 메일서버의 처리량을 조절하면 될 것이다.

7.5 1천통의 이메일

하루에 1천통 이하의 메일을 처리하는 사이트가 있다.

사용자들은 윈도우에서 메일을 전송하고 리눅스의 계정으로 메일을 받는다.

사용자는 1-200명 정도이고 각 사용자는 10개 정도의 메일을 주고 받는다. 보내는 메일은 리눅스서버가 중계해 주고 받는 메일은 사용자들이 5분 단위로 pop3나 IMAP으로 가져 간다.

이 정도의 부담이라면 기본 배포본의 설정과 팬티엄 정도의 하드웨어 56K-128K정도의 네트웍 속도에서도 부담이 없다. 한 사람당 메일 쿼타로 5M를 준다면 1G 정도의 하드 디스크 여유가 있으면 된다.

파일서버, 프린터서버, 웹서버로 같이 사용해도 된다.

자체 네임서버를 구축할 필요도 없으며 메일 큐 디렉토리의 크기에도 신경 쓸 필요가 없다.

로그파일도 크지 않을 것이다. 90퍼센트 이상의 사이트에서 이 정도의 부담으로 리눅스를 사용하고 있는데 시스템 관리자도 거의 필요 없고 서버를 구석에 놓고 그냥 쓰면 된다. 정전이 되었거나 무심코 리셋키를 눌러서 하드디스크에 문제가 생기지 않는다면 일년이상 신경쓰지 않아도 된다.

sendmail의 기동은 /etc/rc.d/init.d/sendmail에 의해서 시작된다. 중요부분은 다음과 같을 것이다.

 start) # Start daemons. echo -n "Starting sendmail: " daemon /usr/sbin/sendmail -bd -q1h echo touch /var/lock/subsys/sendmail
 

sendmail은 데몬 모드(-bd)로 큐는 한 시간에 한 번씩 뒤져 보게(-q1h) 되어 있다. sendmail은 메일 접속을 받으면 자식 프로세스를 생성하여 처리하며 1시간에 한 번씩, 전송이 되지 않은 메일을 다시 보내기를 시도한다. 1천통의 메일은 이 방식으로 별 문제 없이 처리가 될 것이다. 이런 시스템 관리자는 복잡한 sendmail에 대해서 신경 쓰지 말고 다른 작업에 시간을 투자하기를 권한다.

7.6 1만통의 이메일

1만통의 메일은 사용자가 500명 정도, 1-2개의 메일링 리스트, 그리고 중계해 주어야 할 윈도우 클라이언트가 50-100대 정도 있게 된다. 한 개의 도메인 전체에 대한 메일을 담당하는 메일서버로 사용하는 경우일 것이다. 기본 설정으로 사용하기에는 몇가지의 문제가 발생한다. 500명의 사용자가 5분에 한 번씩 pop3을 사용하고 큐에는 평균 100통 이상의 메일이 저장되어 있게 된다. 가장 문제가 되는 것은 한 개의 데몬이 주고 받는 메일을 처리하기에는 힘들게 된다. 이제 데몬을 구분하도록 하자. 중계와 수신을 위해서, 그리고 큐에 쌓여 있는 메일을 처리할 두 개의 데몬으로 나눈다.

 daemon /usr/sbin/sendmail -bd /usr/sbin/sendmail -q30m
 

수신,전송을 위한 데몬은 몇가지 제한 사항에 따라 메일을 직접 전송할 것인지 큐에 쌓아 놓고 나중에 보낼 것인지 결정한다. 해당 옵션은 /etc/sendmail.cf에 있는데 다음과 같은 것이다.

 # load average at which we just queue messages #O QueueLA=8 # load average at which we refuse connections #O RefuseLA=12 # maximum number of children we allow at one time #O MaxDaemonChildren=12
 

LA란 Load Average라는 뜻인데 오렐리의 sendmail 책에 보면 복잡한 식으로 설명되어 있지만 경험적으로는 동시에 처리하고 있는 메일 프로세스의 수와 일치하는 것으로 보인다. QueueLA=8이란 현재 처리하고 있는 메일 프로세스의 수가 8개 이상이면 전송을 중단하고 일단 큐에 쌓은 다음에 나중에 처리하라는 뜻이다. 또한 RefuseLA=12란 메일 처리 프로세스 수가 12개 이상이면 접속을 거부하라는 의미가 된다. 마찬가지로 MaxDaemonChildren=12란 한개의 sendmail 데몬이 만들어 낼 수 있는 자식 프로세스의 수를 12개로 제한한다는 뜻이다. 이제 이 값을 바꾸어 준다. 큐에 메일을 쌓지 않고, 네임서버에 의뢰한 결과 보낼 주소가 유효한 것이고 상대편 메일서버가 전송을 허가해 준 메일이라면 즉시 전송을 시도하도록 한다. 그리고 가능한한 지연 없이 메일을 받아 들이고 원하는 모든 접속을 수용할 수 있도록, 접속 거부를 시작할 프로세스 수와 자식 프로세스의 최대값을 큰 값으로 유지한다.

 O QueueLA=128 O RefuseLA=128 O MaxDaemonChildren=64
 

첫칸에 #가 붙은 것은 sendmail에 설정되지 않으므로 변경하고 싶으면 #를 제거하고 값을 바꾸어 준다. 이제 sendmail은 동시에 128개(수신, 중계 sendmail 64, 큐처리 64)까지 뜰 수 있다. 수신메일은 최대 64개 까지 동시에 처리되며 중계나 수신은 64개 까지는 거부없이 받아 들인다. 물론 메일 거부는 부하가 많이 걸릴 때 발생하며 상대편 메일서버는 이 때에 메일 전송에 실패하지만 잠시 후에는 접속이 가능하게 되기 때문에 일시적 메일 서버 접속 거부는 아무런 문제가 없다. 잘못된 주소로 메일을 보내거나 이 메일 서버가 전송을 시도하는 상대편 메일서버가 다운되어 메일을 보낼 수 없을 때만 메일이 큐에 쌓인다. 큐에 쌓인 메일은 큐처리 전용의 메일서버가 계속해서 전송을 시도하게 되며 정말로 갈 수 없는 메일이 아니라면 몇 시간 안에 메일은 보내지게 될 것이다. 한 개의 메일이 계속 전송이 불가능 할 때 최대로 큐에 저장될 수 있는 값은 5일이다. 그 후에는 큐처리 메일서버가 이 메일을 강제로 삭제하게 된다. 이 값은 sendmail.cf에 있으며 바꾸기를 원하면 직접 찾아 보기 바란다.

7.7 5만통의 이메일

5만통의 메일을 주고 받게 되면 sendmail이 아닌 다른 부분에서 문제가 발생한다. 우선 수신, 중계 전용의 sendmail이 serial하다는 것이 문제가 된다. sendmail은 메일을 수신하면 이 메일이 로칼에 있는 사용자에게 오는 메일인지 중계를 해야 하는 것인지 판단해야 한다. 중계를 해야 하는 것이라면 네임서버에게 헤더에 적힌 메일 주소값을 보내 실제 숫자 IP 주소로 바꾸어 줄 것을 요청한다. 그 동안 이 프로세스는 다른 메일은 받아들이지 않는다. 또다른 메일을 받아들이기 위해서는 자식 프로세스가 fork되어야 하고 64개의 메일 프로세스는 각각 네임서버에게 주소값을 의뢰하는 시간 지연이 있다. 네임서버가 신속히 반응하지 않으면 그 프로세스는 최대 5분까지 1개의 메일 때문에 대기해야 한다. 그 동안 계속 메일이 전송되면 네임서버의 지연으로 대기하는 프로세스는 제외한 프로세스들이 처리해야 하므로 여러개의 프로세스가 네임서버 지연으로 묶이게 되면 금방 64개의 자식 프로세스가 생성되고 그 이상 생성될 수 없으므로 나머지 전송 메일은 전송 시도조차 할수 없게 된다.

즉 1분에 70개의 메일이 전송되고 그 중에서 5개의 메일이 네임서버 지연으로 묶여 있게 될 때는 그 중에서 59개만 처리되고 대기 메일이 5개, 나머지 6개는 전송 실패를 하게 된다. 상대편 메일 서버는 일정시간이 지나야 메일을 재전송하게 되기 때문에 전송지연이 생기게 된다. 물론 메일을 도착 즉시 읽어야 하는 것은 아니지만 이런 지연으로 인해 중요한 업무를 처리할 수 없게 되면 심각한 일이 아닐 수 없다.

또한 중계를 요청한 메일 주소가 잘못되어 있을 경우에는 5분의 지연이 있게 된다. 그래서 평균적으로 목적지 주소가 잘못된 중계메일이 30%정도의 메일서버 수행 성능 손실을 가져올 수 있다. 서버를 빠른 하드웨어로 대체해도 이 문제는 해결되지 않는다.

정상적으로 전송될 수 있는 메일은 1M 네트웍 환경에서, 크기가 10k 이내라면 3초 안에 네임서버 조회, 상대편 서버와 접속, 사용자 인증, 메일 본문 전송의 모든 작업이 끝나게 된다. 100개의 메일을 300초 동안에 모두 전송 할 수 있음을 뜻한다. 좀 더 빠른 하드웨어와 네트웍에서 같은 메일을 2초 만에 전송할 수 있다면 200초가 걸린다. 그러나 그 중에서 1개의 메일이 잘못되었다면 이 메일이 5분(300초)간의 전송 지연을 유발하게 되고 총 걸린 시간은 600(300+300)분과 500(200+300)분의 차이가 된다. 9개의 정상 메일과 1개의 비정상 메일에 대해서 이야기 했지만 1000개의 메일에서 20개의 메일이 이상하다면 총 걸린 시간은 거의 차이가 나지 않는다(11960초와 12940초).

이 문제를 해결하고자 단순히 빠른 서버를 구입한다고 했을 때 하드웨어 업그레이드에 비해서 전송효율이 같은 비율로 올라가는 것은 아니다. 이 것은 마치 99미터를 1초만에 달리고 나머지 1미터를 10초에 달리는 선수와, 99미터를 2초에 달리고 나머지를 같은 10초에 달리는 100M 경주와 비슷하다. 평균적으로 빠르지 않으면 결과는 차이가 거의 없는 것이다. serial 방식의 메일 전송의 병목은 시스템의 수행 성능이 아니라 잘못된 메일이 점유하는 프로세싱 타임에 있다.

5만통의 메일을 처리하기 위해서는 이런 serial 문제를 해결해야 한다. 수신, 중계 메일은 메일을 받아들이는 순간에는 네임서버에 유효한 주소인지 의뢰하는 등의 지연을 가져올 수 있는 모든 행위를 하지 않도록 하고 무조건 메일을 큐에 쌓도록 하는 것이다. 큐에 쌓인 메일은 큐 처리 전용의 메일서버가 병렬적으로 메일을 전송할 수 있도록 만든다.

 /usr/sbin/sendmail -bd -ODeliveryMode=defer /usr/sbin/sendmail -q1m -OMaxDaemonChildren=64
 

DeliveryMode는 4가지가 있다. 각각은 sendmail 책에 있으므로 관심있는 사용자는 찾아 보기 바란다. 그 중에서 defer 모드는 수신 메일을 그 즉시 직접 전송을 하지도 않을 뿐 아니라 받은 메일이 유효한 것인지 네임서버에 의뢰하는 등의 시간지연이 있을 수 있는 행위를 전혀 하지 않고 큐에 쌓기만 한다. -q1m란 큐처리 데몬은 1분 단위로 자식 프로세스를 만들고 이 프로세스가 처리할 메일이 있는지 확인하게 하는 것이다. 메일이 큐에 없을 때는 자식 프로세스가 뜬 후에 처리할 메일이 없음을 확인하고 스스로 죽게 된다. 만약 메일이 많이 수신되어 큐에 메일이 쌓이기 시작하면 1분에 한 번씩 큐 전용 메일서버가 자식 프로세스를 만들어 내고 이 들은 각자 메일 전송을 시작한다.

64개의 메일 프로세스가 뜨기까지는 64분이 걸리는데 처음 뜬 프로세스가 1분에 2개의 메일을 처리한다고 하면 평균적으로 64분 동안 64개의 프로세스가 각각 64개의 메일을 처리하는 것이다. 즉 수신 메일이 분당 32개의 메일을 큐에 쌓는 정도의 부하(하루 총 메일량 46080개)에서는 평균적으로 큐처리 메일이 32개 정도가 뜨게 된다. 수신 전용의 데몬은 평균 10개 이내로 떠있게 되는데 그 이유는 한 개의 메일을 단순히 큐에 쌓는 행위는 거의 시간이 걸리지 않기 때문에 뜨는 즉시 메일을 처리하고 죽게 되기 때문이다.

그러나 이런 수치상의 결론이 실제와는 일치하지 않는데 그 이유는 전송될 수 없는 메일이 큐에 쌓이게 되면서 전체적인 성능을 떨어뜨리기 때문이다. 64분 동안 주소가 잘못된 1개의 메일은 각각의 프로세스가 1번씩 전송을 시도하므로 총 64번 시도된다. 전송될 수 없는 메일은 한 개의 프로세스를 5분동안 잡아 놓고 있으므로 10개의 메일이 전송될 수 없도록 하는 효과가 있고 64개의 프로세스에 대해서는 640개의 메일을 보낼 수 없도록 하는 것이다. 하루 동안 계산하면 총 15000여통의 메일을 보내지 못하게 만드는 결과를 낳는다. 한 개의 메일은 5일 동안 살아 있으므로 이런 메일이 다른 방식으로 처리되지 않는다면 메일서버는 엄청난 비효율에 시달려야 한다. 나중에는 큐에는 거의 이런 악성 메일로만 가득차 있고 정작 바로 보낼 수 있는 메일이 이들 속에 묻혀 전송이 되지 못하고 그대로 쌓이는 것을 보게 될 것이다.

sendmail의 소스 배포본의 contrib 디렉토리에는 이 문제를 해결하는데 도움을 주는 프로그램 re-mqueue.pl이 있다. 이 프로그램은 perl로 만들어져 있으며 어떻게 사용하는지는 그 프로그램 안에 자세한 설명이 있다. perl과 관련한 사항은 설명하지 않는다. 소스를 보고 어떻게 활용할 것인지는 스스로 판단하기 바란다.

큐에 쌓이는 메일은 방금 받은 메일과 1시간 전에 받았지만 아직 처리 되지 못한 메일, 그리고 여러 시간 혹은 며칠 전에 받았지만 전송할 수 없는 메일들이 함께 들어 있다. 우선 1-2시간 안에 모든 메일은 큐 처리 데몬이 한 번씩 전송을 시도하기 때문에 1-2시간 전에 받았지만 아직 한 번도 전송이 시도된 적이 없는 메일은 없다고 가정한다. 그러므로 cron을 돌려서 2시간(64개의 데몬이 각각의 메일 전송을 2번 시도한 시간)안에 처리되지 못한 메일은 일차 큐에서 분리하여 다른 큐에 보내고 이 이차큐에 있는 메일을 처리하는 sendmail 데몬을 돌려서 그 메일들을 전송 하도록 한다. 우선 데몬을 다른 방식으로 띄운다.

 /usr/sbin/sendmail -bd -ODeliveryMode=defer /usr/sbin/sendmail -q1m -OMaxDaemonChildren=64 \ -OQueueDirectory=/var/spool/mqueue /usr/sbin/sendmail -q10m -OMaxDaemonChildren=32 \ -OQueueDirectory=/var/spool/mqueue2
 

/var/spool/mqueue2를 만들고 여기에는 2시간이 지난 메일을 처리하는 데몬을 최대 32개 까지 띄울 수 있도록 한다. 전송 지연이 있는 메일은 문제가 있는 것이므로 새로운 프로세스는 10분 정도의 시간 간격으로 뜨게 하면 별로 시스템 부담이 없게 될 것이다.

이제 mqueue의 일차 큐에서 mqueue2의 이차 큐로 메일을 보내는 것은 crontab을 사용해서 처리한다.

 # crontab -e 1-59/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue2 7200
 

10분 단위로 mqueue에서 mqueue2로 2시간(7200초)가 지난 메일을 이동시킨다. 이제 일차 큐에는 전송 중이거나 전송을 기다리는 메일들만 남게 되고 악성메일(현재 보낼 수 없는 메일)은 제거되므로 효율이 눈에 띄게 증가하게 된다. redhat-5.2 이상의 설정이 이와 유사하지만 큐디렉토리간의 파일 이동을 위한 프로그램 설정이 되지 않았다. reahat-5.2 이상을 인스톨 했다면 re-mqueue.pl을 사용해 볼 것을 권한다.

7.8 10만통의 이메일

10만통의 메일을 하루에 처리하는 서버는 거의 엔터프라이즈급이라고 볼 수 있다. 하드웨어는 펜티엄II-dual, 메모리는 256M이상이 필요하고 네트웍은 1M급이 되어야 할 것이다. 리눅스의 사용자 계정은 현재 65536명까지 가능하지만 userid를 공유하는 서로 다른 사용자명을 사용할 수 있다. 또한 쉽게 사용자를 추가하고 제거하는 방법이 고려되어야 하고 사용자 인증 자체도 한 번의 메일을 위해서 64K를 뒤지는 비효율적인 방법이 아닌 다른 방법이 사용되어야 한다. 그러나 이 것은 이 글의 주제가 아니다. 10만통의 메일을 주고 받는 서버는 B급 도메인의 메일서버가 될 것인데 이 글에서는 계정 사용자보다는 주로 하위 클라이언트의 메일 중계를 중심으로 설명한다.

10만통의 메일을 처리하는 서버는 또다른 문제에 직면하게 된다. sendmail은 메일을 전송하면서 그 결과 메세지를 syslogd를 통해서 /var/log/maillog에 저장한다. /var/log/maillog에는 pop3를 사용하여 클라이언트가 메일을 가져가는 기록과 한 개의 메일이 전송될 때마다의 기록을 남긴다. maillog는 cron 이 작동하여 한 주마다 크기를 줄이게 되어 있다(/etc/logrotate.conf참조). 그 크기를 계산해 보자. 아래는 성공적으로 메일 중계가 이루어진 한 개의 메일에 대한 로그이다.


Apr xx xx:xx:xx mail sendmail[24543]: XAA24543: from=<xxx@xxx.com>, \ size=2909, class=0, pri=32909, nrcpts=1, \ msgid=<45672:phj650:99xxxx_23:45:57>, proto=SMTP, relay=[210.108.5.10] Apr xx xx:xx:xx mail sendmail[24543]: XAA24543: to=<xxxxx@xxxmail.net>, \ delay=00:00:00, mailer=esmtp, stat=queued Apr xx xx:xx:xx mail sendmail[26062]: XAA24543: to=<xxxxx@xxxmail.net>, \ claddr=<xxxxxxx@xxxinc.com> (504/504), delay=00:09:27, xdelay=00:00:01,\ mailer=esmtp, relay=r-xxxxx.xxxmail.net. [xxx.xxx.152.76], \ stat=Sent (AAA06884 Message accepted for delivery) 

한 개의 정상적인 메일이 전송될 때 나오는 메세지는 560여 바이트가 된다. 이 것은 정상적으로 전송된 메일의 경우에만 그렇고 여러가지 이유에 의해서 전송되지 않는 메일은 하루에 여러번 전송 시도를 하기 때문에 여기에 더해서 그 때마다 에라 메세지가 쌓인다. 그러므로 평균적으로 한 개의 메일이 1k정도의 메세지를 뿌린다고 하자. 하루에 10만개의 메일을 전송하니까 로그는 100M가 된다. 일주일 동안 로그 파일의 크기는 700M로 커진다.

메일계정이 1000명이라고 할 때 각각의 사용자가 5분마다 한 번씩 메일을 체크한다. 한 번의 메일 체크 로그는 아래와 같다.


Apr xx xx:xx:15 mail ipop3d[22650]: port 110 service init from 210.xxx.x.x Apr xx xx:xx:18 mail ipop3d[22650]: Login user=xxxxx host=[210.xxx.x.xx] nmsgs=7/7 Apr xx xx:xx:24 mail ipop3d[22650]: Logout user=xxxxx host=[210.xxx.x.xx] nmsgs=0 ndele=7 

한 번에 약 0.2k의 로그가 쌓인다.

윈도우에서 pop3을 사용해 메일을 가져가고 근무 시간 이후에는 컴퓨터를 끈다고 가정하면 하루에 8시간 근무하는 사용자는 192K의 로그를 만들어 내고, 1000명은 192M를, 일주일에는 1.3G의 로그를 생성시킨다. 메일 서버에 하드 디스크가 넉넉하다면 로그 파일의 크기가 커지더라도 하드 디스크 용량 때문에 문제가 생기지는 않을 것이다. 한 사용자당 5M씩 할당하면 메일을 저장할 영역으로 5G가 필요하고 로그를 위해서 2G이상이 필요하다(logrotate값이 4라면 8G.)

문제는 여기에 있지 않다. syslogd는 한 개의 파일, maillog를 열어 놓고 계속해서 로그 메세지를 쌓게 되는데 이 때 1M이상을 넘어가면 1개의 로그 메세지를 처리하기 위해서 시스템 자원을 10 퍼센트 이상 사용하며, 10M를 넘어가면 40 퍼센트 이상, 100M 메가를 넘어가면 거의 80퍼센트 이상의 시스템 자원을 사용하게 된다. 로그 파일이 커질 수록 점점 시스템 자원이 고갈 되어서 나중에는 메일 전송 보다는 로그 쓰기 작업에 모든 프로세싱 타임을 사용해야 한다. 아래의 예는 /usr 파일을 백업하기 위해 사용한 tar에서 부르는 gzip이 시스템 자원을 79.4 퍼센트를 사용하고 있음을 보여주는 예이다.


 x:xxxx up 1 day, 2:05, 2 users, load average: 0.22, 0.05, 0.02 49 processes: 46 sleeping, 3 running, 0 zombie, 0 stopped CPU states: 78.6% user, 5.7% system, 0.0% nice, 15.8% idle Mem: 192896K av, 189948K used, 2948K free, 283288K shrd, 12496K buff Swap: 104416K av, 0K used, 104416K free 133384K cached PID USER PRI NI SIZE RSS SHARE STAT LIB %CPU %MEM TIME COMMAND 4688 root 11 0 580 580 232 S 0 79.4 0.3 0:06 gzip 4687 root 4 0 532 532 432 R 0 3.9 0.2 0:00 tar 4689 root 2 0 708 708 548 R 0 0.9 0.3 0:00 top 

지금 여러분의 시스템에서 top 명령을 사용해 보기 바란다. 시스템 자원을 10퍼센트 이상 사용하고 있는 프로세스가 있다면 필시 하드 디스크를 접근하고 있는 프로그램일 것이다. 컴퓨터에서 하드 디스크를 빈번하게 사용하는 작업이 여러개 떠 있다면 성능은 급격하게 하락하게 된다. 메일서버 뿐만 아니라 웹서버와 같은 데몬들의 성능이 느려 졌다면 필시 이런 문제가 개입되어 있을 확률이 높다.

배포본에 있는 로그 파일 처리는 하루에 10만개의 메일을 처리하기에는 역부족이다. 좀 더 효율적인 처리를 생각해 보자. 우선 아래 파일을 만든다.

 /etc/logrotate.mail daily size 100k rotate 2 errors root create /var/log/maillog { postrotate /usr/bin/killall -HUP syslogd endscript } /var/log/messages { postrotate /usr/bin/killall -HUP syslogd endscript }
 

maillog, messages 외에 /var/log에 있는 파일 중에서 시간 별로 그 크기가 1M 이상씩 증가하는 것이 있다면 여기에 첨가한다. 로그 파일은 가능한한 작게 유지하는 것이 좋다. 위에서 로그 파일의 크기가 100k 이상이 되면 무조건 작게 만들도록 설정을 했다. 로그를 줄일 때 이전 로그는 log.1이 되고 log.1은 log.2가 된다. 여기서 rotate를 2로 만들었기 때문에 log.2는 삭제되고 log.1은 log.2가 되며 log는 log.1이 되고 새 파일 log가 만들어져 syslogd가 여기에 쓰기를 한다. cron 작업은 다음과 같이 설정한다.

 #crontab -e 1-59/10 * * * * /usr/sbin/logrotate /etc/logrotate.mail
 

10분 마다 로그를 점검하여 그 크기를 줄인다. 이렇게 10분마다 로그처리를 위해 프로세싱 타임을 소모하더라도, 로그의 크기를 줄여서 얻는 효과가 더 크기 때문에 빈번한 로그 처리 작업에 드는 프로세싱 타임을 상쇄하고 남는다. 여러분의 웹서버가 오래 켜 놓으면 시간이 지날 수록 점점 느려지는 이상한 증상은 없는가? 이런 증상 때문에 메모리를 늘이거나 대용량의 하드웨어를 구입할 계획을 세우고 있었다면 /var/log/httpd 디렉토리를 점검해 보기를 바란다. access_log가 10M 이상은 아닌가?

10만통의 메일을 처리하는 서버에는 일차큐에 시간당 5천통의 메일이 쌓인다. 이제 64개의 메일 프로세스로는 감당을 할 수 없다. 한개의 메일은 2시간 동안 큐에 대기하게 되는데 2시간 동안 1만통의 메일이 쌓이게 되므로 그 동안에 각각의 메일을 처리 할 수 있을 확률이 낮아 진다. 이렇게 64개의 큐처리 메일 서버가 동작하면서 두시간 안에 모든 메일을 한 번씩이라도 전송 시도 하기가 어려워지는 상황에서 더욱 상황을 나쁘게 만드는 것은 5퍼센트(500개)악성메일이 프로세싱 타임을 늘게 만드는 것이다. 이 문제를 해결하기 위해서 다음과 같은 방법을 사용할 수 있다. 즉 최대 자식 프로세스 숫자를 높이고 각각 다른 제한을 가진 프로세스를 띄운다.

 /usr/sbin/sendmail -bd -ODeliveryMode=defer /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue \ -OMaxDaemonChildren=96 \ -OTimeout.initial=1m -OTimeout.connect=1m \ -OTimeout.iconnect=1m -OTimeout.helo=1m \ -OTimeout.mail=1m /usr/sbin/sendmail -q1m -OQueueDirectory=/var/spool/mqueue \ -OMaxDaemonChildren=16 /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue2 \ -OMaxDaemonChildren=32
 

mqueue2에는 2시간 안에 처리되지 못한 메일이 있으므로 악성메일이라고 판단하여 프로세스 수를 늘이지 않았다. 일차큐인 mqueue에는 두개의 각기 다른 데몬이 떠 있는데 첫번째 것은 96개까지 자식 프로세스를 만들어 낼 수 있고 두 번째 것은 16개 까지 만들어 낼 수 있다. 시스템에 생길 수 있는 총 메일서버 프로세스의 갯수는 수신 중계 전용(64+1), 일차큐(96+1, 16+1), 이차큐(32+1)로 212개이다. 이 때 쯤이면 각 경우에 맞추어 /etc/sendmail.cf의 변수를 조절해야 한다는 것은 스스로 알게 될 것이다. 지금은 아래와 같이 하면 된다.

 O QueueLA=256 O RefuseLA=256 O MaxDaemonChildren=64
 

mqueue에 실행되는 두 개의 각기 다른 큐전용 메일 프로세스의 차이는 무엇일까?

우선 한 개는 네임서버 그리고 상대편 메일서버와 교신하는 시간제한을 모두 1분으로 정하고 실행하도록 했다. RFC1123에 의하면 각각의 시간제한은 5분으로 설정하도록 되어 있지만 정상적인 메일은 거의 1분이 되기 전에 상대편과 교신하고 메일 본문 전송에 들어가게 되므로 초기 교신 과정에서 1분 이상의 시간 지연이 있으면 느린 네트웍에 있는 메일서버라고 판단하거나 상대편이 전송 불능 상태라고 간주하여 메일 전송을 중단하고 신속히 다음 메일을 처리하도록 한다.

이 프로세스는 10초 마다 한 개씩 자식을 만들게 되므로 메일이 쏟아져 들어오기 시작한 지 960초(16분)이 지나면 96개의 자식 프로세스가 전송이 되든 안되든 1분 안에 1개의 메일을 처리할 수 있으므로 한 시간에 들어오는 5000개의 메일과 거의 비슷한 양의 메일을 처리 할 수 있게 된다. 즉 각각의 메일을 큐에 쌓은 후에 반드시 한 번 이상의 전송 시도를 할 수 있다는 것이다. 악성 메일이 아니지만 상대편 메일서버의 속도가 느려서 1분 이상의 지연 후에 메일이 갈 수 있음에도 전송을 중단당한 메일이 쌓일 수 있는 문제가 있다. 이런 부적절한 처리를 해결하기 위해서 정상 대기 시간을 가진 16개의 메일 프로세스가 1분 대기 프로세스가 남겨둔 메일을 다시 전송 시도 하도록 하면 된다. 그 때문에 한 개의 큐 디렉토리를 대상으로 하는 두 개의 데몬을 띄운 것이다.

두 시간 동안 모든 메일은 최소한 한 번의 전송 시도를 할 수 있게 되며 그럼에도 전송되지 못하고 남겨진 악성 메일은 2차 큐로 이동된다. 일차큐는 항상 효율적으로 사용할 수 있다.

7.9 20만통의 메일

20만통의 메일을 한 개의 큐 디렉토리에 받게 되었을 때 시간당 약 1만통의 메일이 쌓인다. 일차큐에 있는 메일은 2시간 후에 이차큐로 옮겨지기 때문에 2시간 동안 약 2만통의 메일이 쌓이게 되고 메일 전송 효율이 70 퍼센트라면 일정 시점에 큐에 쌓이는 파일의 수는 약 6000개이다. 한 개의 메일은 dfxxxxxx라는 메일 본문과 qfxxxxxx라는 헤더 부분이 각각의 파일로 존재하므로 12000개의 파일이 한 개의 디렉토리에 생성되고 전송 중임을 나타내는 xfxxxxxx라는 파일이 최대 112개가 생긴다. 한 디렉토리에 12112개의 파일이 있다면 그 중에서 한 개의 파일을 열기 위해서는 허용하기 힘든 시간을 소모해야 한다. 지금 한 디렉토리에 10000개의 파일을 만들고 ls 라고 실행해 보기 바란다. 아마 10초 이상의 시간을 기다려야 겨우 그 결과를 볼 수 있을 것이다.

이제 큐에 쌓인 메일 파일의 갯수가 병목이 된다. 한 시간에 1만통의 메일이 쌓인다면 10분에 평균 1700개의 메일 즉 3400개의 실제 파일을 한 디렉토리에 생성하는 것이다. 물론 생성되는 순간에 즉시 전송 되는 경우가 많겠지만 여전히 프로세싱 타임은 한계를 넘어 가게 된다.

이를 위해서는 6개의 큐디렉토리를 새로 만들고 매 10분 마다 각각의 디렉토리에 파일을 옮겨서 전송을 시도하는 것이 좋다. 다음과 같이 할 수 있을 것이다.

 /usr/sbin/sendmail -bd -ODeliveryMode=defer # 일차큐 /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue \ -OMaxDaemonChildren=32 \ -OTimeout.initial=1m -OTimeout.connect=1m \ -OTimeout.iconnect=1m -OTimeout.helo=1m \ -OTimeout.mail=1m /usr/sbin/sendmail -q1m -OQueueDirectory=/var/spool/mqueue \ -OMaxDaemonChildren=4 # 이차 큐 1번 /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue_1 \ -OMaxDaemonChildren=16 \ -OTimeout.initial=1m -OTimeout.connect=1m \ -OTimeout.iconnect=1m -OTimeout.helo=1m \ -OTimeout.mail=1m /usr/sbin/sendmail -q1m -OQueueDirectory=/var/spool/mqueue_1 \ -OMaxDaemonChildren=4 # 이차 큐 2,3,4,5번 .... # 이차 큐 6번 /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue_6 \ -OMaxDaemonChildren=16 \ -OTimeout.initial=1m -OTimeout.connect=1m \ -OTimeout.iconnect=1m -OTimeout.helo=1m \ -OTimeout.mail=1m /usr/sbin/sendmail -q1m -OQueueDirectory=/var/spool/mqueue_6 \ -OMaxDaemonChildren=4 # 3차 큐 : 2시간 지난 메일 처리 /usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue2 \ -OMaxDaemonChildren=32
 

cron은 다음과 같이 구성할 수 있다.

 # crontab -e 0 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_1 600 10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_2 600 20 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_3 600 30 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_4 600 40 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_5 600 50 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue /var/spool/mqueue_6 600 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_1 /var/spool/mqueue2 7200 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_2 /var/spool/mqueue2 7200 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_3 /var/spool/mqueue2 7200 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_4 /var/spool/mqueue2 7200 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_5 /var/spool/mqueue2 7200 1-50/10 * * * * /usr/sbin/re-mqueue.pl /var/spool/mqueue_6 /var/spool/mqueue2 7200
 

각 디렉토리는 최대한 3000개의 정도의 메일을 보유하게 된다. 일차큐에서는 한 개의 메일에 대해서 최대 10분 동안 전송을 시도한 후에 전송에 실패했거나 미처 처리하지 못하고 남은 것은 각각 10분 단위로 다른 큐로 보내진다. 옮겨진 각 큐에서 2시간을 시도해 본 후에 전송이 되지 않았으면 6개의 이차큐에 남은 메일은 모두 mqueue2로 보내진다. 여기서 2시간 지난 후의 소위 악성 메일에 대한 또다른 처리는 고려하지 않았다. 모든 큐 디렉토리의 부하를 줄이기 위해서는 re-mqueue.pl을 사용하여 2,4,8시간 1,2,3,4일 단위의 큐를 따로 생성하는 것이 좋을 것이다.

위와 같이 했을 때 최대로 뜰 수 있는 메일 서버 프로세스의 갯 수는 268개이다. 이제 시스템에서 처리할 수 있는 프로세스 갯수와 열 수 있는 오픈 파일의 갯수가 문제가 된다. 기본적으로 한 개의 sendmail 프로그램이 기동하면서 여는 파일은 다음과 같다.

 0 -> /dev/null 1 -> /dev/null 2 -> /dev/null 3 -> /var/spool/mqueue/qfVAA14282 4 -> socket:[590] 5 -> /etc/mail/aliases.db 6 -> /etc/mail/access.db 7 -> /etc/mail/virtusertable.db 8 -> /etc/mail/domaintable.db 9 -> /etc/mail/genericstable.db 10 -> /etc/mail/mailertable.db 11 -> /var/spool/mqueue/xfVAA14282 12 -> /var/spool/mqueue/dfVAA14282 13 -> socket:[814754]
 

이 자료는 ps를 실행하고 현재 전송 중인 메일의 프로세스 번호(예를 들어 235번)에 따라 "ls -l /proc/235/fd" 라고 실행하면 볼 수 있다. 268개의 메일 프로세스가 각각 13개씩의 파일을 열면 모두 3484개의 파일이 열린다. 서버에는 메일 데몬만 떠 있는 것이 아니므로 실제로 열려 있게 되는 파일 수는 이 값을 훨씬 넘어 간다. 현재 리눅스에서 동시에 열 수 있는 파일의 디폴트값은 4096이다. 그 값은 아래의 파일에서 정의되어 있는데 이 것을 고치고 커널을 재컴파일 하면 변경 가능하다.

 /usr/src/linux/include/linux/fs.h #define NR_FILE 4096
 

같은 헤더 파일에 한 개의 프로세스가 열 수 있는 파일의 최대값(NR_OPEN)이 1024로 정해져 있는데 이 값은 변경하지 않아도 좋다. 물론 원하면 변경해도 된다. 그 결과는 각자 테스트 해 볼 것. NR_FILE과 NR_OPEN을 크게하면 주기억장치가 크지 않은(64M이하) 시스템의 경우에는 부팅도 불가능하게 되니 주의 할 것.

커널 컴파일이 귀찮다면 동적으로 이 값을 변경해 줄 수도 있다. /proc/sys/fs/file-max가 그 것인데 아래 명령으로 현재의 최대값과 할당된 값을 볼 수 있다.

 # cat /proc/sys/fs/file-max 4096 # cat /proc/sys/fs/file-nr 1143 274 4096 # cat /proc/sys/fs/inode-max 12288 # cat /proc/sys/fs/inode-nr 8340 34
 

이들 값의 의미와 읽는 방법은 /usr/src/linux/Documentation/proc.txt를 참고할 것. 커널 컴파일 없이 시스템에서 열 수 있는 최대값을 변경하기 위해서는 다음 명령을 사용하면 된다.

 echo 8192 >/proc/sys/fs/file-max echo 24576 >/proc/sys/fs/inode-max
 

inode-max값은 file-max값의 3배수 이상으로 하라고 proc.txt에서 조언하고 있다. 항상 이렇게 하고 싶으면 /etc/rc.d/rc.local에 적어 두면 될 것이다.

메일서버가 200개 이상 뜨거나 웹서버가 동시에 400개 이상 떠있어야 하는 대형 사이트에서는 최대값을 크게 하는 것이 최종적인 해결책일 수 있다.

그러나 이렇게 최대값을 크게 하여 문제를 해결하기 이전에 가능한 현 상태에서 최적화에 우선 신경을 쓰는 것이 더 좋은 방법이다.

왜냐하면 최대값 증가는 더많은 메모리를 요구하고 더 많은 자원 할당을 요구하기 때문이다.

200개 이상의 sendmail을 위해서 쓸 수 있는 최적화 방법으로는 한 개의 sendmail이 여는 파일의 갯수를 줄이는 것이다. 위에서 보듯이 virtusertable.db등은 가상 도메인을 사용하지 않으면 전혀 필요가 없다. 그러나 사용하지 않는 빈 파일인 /etc/mail/virtusertable을 그대로 둔다면 sendmail이 기동하면서 db파일을 만들게 되고 모든 프로세스가 이 파일을 열어 두게 된다.

그러므로 사용하지 않는 파일을 아예 없애버리면 sendmail이 db파일을 만들지 않고, 그에 따라 각 프로세스는 이 파일을 위해 파일 핸들을 할당하지 않으니까 열린 파일 갯수가 줄어든다. 한 개의 열린 파일의 갯수를 줄이면 268개의 파일을 닫는 효과가 있다. 사용하지 않는 domaintable, access, genericsable, mailertable 5개의 파일을 지워버리면 1340개의 열린 파일 수를 절약할 수 있으며 한 프로세스의 열린 파일 갯수가 적어지면 메일을 체크할 때 이 파일을 뒤지지 않아도 되기 때문에 그 만큼 프로세싱 타임이 줄어 들 것이다.

큐에 쌓인 메일을 처리하는 sendmail 프로세스는 메일을 보내기 위해서 네임서버와 교신해야 한다. 메일 서버가 서브네트웍 바깥에 있다면 한 개의 메일을 보내기 위해서 외부까지 교신을 해야 하기 때문에 시간이 많이 걸린다. 한 시간에 1만번의 조회를 시도하면 아마 외부 메일서버도 견디지 못하고 다운될 확률이 있다. 또한 네트웍 자원은 네임서버와의 교신에 모두 소모되고 다른 사람들이 외부로 나가거나 들어오는 것초차 힘들게 된다. 네임서버가 로컬 네트웍의 다른 서버에 있어도 마찬가지이다. 내부 네트웍은 거의 네임서버 접속에 이용되므로 회사 전산망이 마비되고 네임서비스를 하는 서버가 다운된다. 실제로 필자가 내부 네트웍의 다른 서버에 네임서버를 두고 메일서버가 네임 조회를 이 서버 쪽으로 하도록 했었는데 네임서버에 메모리가 고갈되어 named, httpd, cron등이 죽었다. 그 후에 데몬이 죽은 것을 알고 named와 httpd만 살리고 cron이 죽은 것은 알지 못하고 재실행하지 않았더니 cron으로 돌던 log파일 처리 프로그램이 동작하지 않아 로그 파일이 커지는 바람에 시스템이 움직이지 않을 정도로 부하가 걸린 적이 있었다. 필자는 외부에 의한 해킹으로 의심하고 보안관련 문서를 뒤적이느라고 시간을 소비해야 했다. 이렇게 한 개의 메일서버가 연속적으로 다른 서버들에게 영향을 미칠 수 있음을 염두에 두어야 할 것이다.

그러므로 가능하면 메일서버 자체에 메일 전송을 위한 네임서버를 띄우는 것이 좋다. 이 때의 네임서버는 단순한 캐싱만을 하는 네임서버만으로 족하다. 처음에는 네임서버 자체가 외부로 조회를 해야 하기 때문에 시간이 걸리겠지만 계속 사용한다면 중복되는 IP는 같은 메모리 안에서 처리가 가능하기 때문에 외부에 영향을 주지 않으며, 로컬 네트웍의 tcp/ip 자원도 소모시키지 않고 신속한 조회가 이루어 질 수 있다. 캐싱만 하는 네임서버는 간단히 다음과 같이 만들 수 있다.


 /etc/named.conf options { directory "/var/named"; check-names master warn; datasize 20M; }; zone "." IN { type hint; file "root.hint"; }; zone "localhost" IN { type master; file "localhost"; check-names fail; allow-update { none; }; allow-transfer { any; }; }; /var/named/root.hint #dig >/var/named/root.hint /var/named/localhost @ IN SOA @ root ( 42 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum NS @ 1 A 127.0.0.1 /etc/resolv.conf nameserver 127.0.0.1 

이렇게 최소한의 네임서버만으로 네트웍 자원의 낭비를 막을 수 있으므로 20만개의 메일을 처리하기 위해서는 회사내의 공식 네임서버가 있다고 해도 독립적으로 메일서버에 설치하는 것이 좋을 것이다.

7.10 100만통의 이메일 처리를 위하여

소규모 처리를 담당하는 서버에서 대규모 서버로 진화하기 위해서 어떤 것이 필요하고 그 과정에서 중요한 단계마다의 병목은 무었인지 그리고 이 것을 어떻게 해결할 수 있는지에 대해서 알아 보았다. 한 개의 서비스가 커지면 가능한 옵션을 모두 다시 검토하여야하고, 하드웨어 자원을 추가로 요구하며, 로그 파일 처리등의 관련 프로그램의 효율성이 증대되어야 하고, 네임서버와 같은 또다른 서비스까지 자신에게 맞추어 줄 것을 요구한다. 또한 한 개의 서버가 문제를 일으키면 주변의 다른 서버까지 문제가 파급된다는 것도 보았다. tcpdump같이 다른 컴퓨터 사이의 동작을 감시하는 프로그램에 대한 사용법도 익혀 줄것을 부탁한다. 이모두는 서로 보완적이므로 한 개의 서비스를 위해서 그 프로그램만을 들여다 보아서는 안되고 전체를 볼 줄 알아야 할 것이다.

이 글에서는 TCP/IP 포트 재지정에 의한 분산, 메일 전송 정보가 들어 있는 qf* 파일을 분석하여 각각의 전송 실패 이유에 따른 큐디렉토리 분산, 전송할 수 없는 메일에 대한 처리, sendmail.cf의 각각의 옵션에 대한 관찰등은 하지 않았다. 관심있는 독자들은 sendmail 책과 /var/spool/mqueue/qf* 파일에 대해 조사해 보기 바란다.

100만통의 메일을 하루에 처리할 수 있는 초대규모 메일서버는 한 개의 서버로는 한계가 있다. 각각 20만통의 메일을 처리하는 5대의 메일서버가 필요하게 될 것이다. 문제는 이들을 묶어서 단일한 서버로 보이게 하는 방법이 필요하게 된다. 수백대의 서버를 묶어 놓고 IP 라운드로빈 방법을 사용할 수 있다. IP 라운드 로빈은 1.2.3.4,1.2.3.5가 같은 a.b.c.d 도메인명을 가지도록 하고 네임서버 조회 때마다 한 번씩 다른 숫자 IP를 넘겨 주는 방법을 사용한다.

필자가 최종적인 해결책으로 보고 있는 것은 IP-tunneling을 사용한 리눅스 virtual-sever(vs)를 사용하는 것이다. 이 방법은 한 개의 frond-end가 각각의 접속을 받아서 실제 서비스를 하는 back-end 서버에게 접속을 넘겨 주는 것이다. 한 번 접속이 이루어 지고 나서는 client와 back-end간에 직접 접속이 이루어지게 되므로 frond-end는 바로 다음 접속을 처리할 수 있다. vs 리눅스를 개발하는 팀에 의하면 한 대의 frond-end가 1G 급의 부하를 처리할 수 있었다고 한다. 관심있는 사람은 vs홈페이지(http://proxy.iinchina.net/ wensong/ippfvs/)를 방문해 보기 바란다. 최근 외국의 개발업체에서 이 기능을 특화시킨 분산 서버를 제작해서 상업화 시켰다는 이야기도 들은 바가 있다.

이미 리눅스의 메일서버로서의 기능은 신뢰할 만하고 추가 비용이 전혀 필요 없다. 사용자 수 추가에 따라 비싼 라이센스 비용을 지불해야 하는 상용 서버가 설 자리가 없어 지고 있다. 여기에 더해서 사용자 인증, 삭제, 추가 기능을 간편하게 할 수 있는 프로그램이 개발되어 100만통의 메일을 처리할 수 있는 분산 서버와 결합하게 되면 인터넷의 거의 모든 메일서버는 리눅스가 점령하게 될 것이다.

7.11 이 달의 숙제

메일서버와 마찬가지로 웹서버도 많은 히트수를 기록하게 되면 로그 파일이 커질 것이다. /var/log/httpd에 가보았더니 access_log가 30M였다. sendmail을 위해서 설명한 logrotate.conf와 같이 설정 파일을 만들고 하는 것이 시간이 걸려서 잠시 미루고 일단 수동으로 로그 파일을 빨리 정리하고 싶다. 어떻게 하면 될까? 참고로 "rm access_log"라고 해도 로그 파일이 줄어 들지는 않는다. 이건 또 왜 그런 것일까?


'6. 리눅스' 카테고리의 다른 글

[LINUX]smtp 서버 주소 가져오기  (0) 2010.03.25
리눅스 find 명령  (0) 2010.01.25
rsync 설정 -ssh를 이용한 설정  (0) 2010.01.20
crontab  (0) 2010.01.20
java.net.SocketException: Too many open files 대처법...  (0) 2009.12.02


이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공유하기 링크
Comments