페이지

2014년 6월 23일 월요일

sendmail 발송 정리

메일설정 및 속도 증가

-서버쪽-

1. sendmail.mc수정 addr을 안바꿔주면 로컬에서만 메일이 나감

vi /etc/mail/sendmail.mc

define(`SMART_HOST', `localhost')dnl
DAEMON_OPTIONS(`Port=smtp,Addr=0.0.0.0, Name=MTA')dnl

m4명령으로 sendmail.cf 생성
m4 /etc/mail/sendmail.mc > /etc/sendmail.cf




2. sendmail.cf에서 수정사항
O DeliveryMode=defer    //즉시 큐에 담는다
O Timeout.queuereturn=1h    //1시간후에 해당 메일 삭제
O QueueLA=512             //현재 처리하고 있는 메일 프로세스 512되면 전송중단
O RefuseLA=512            //메일 처리 프로세스 수가 512개 이상이면 접속을 거부
O MaxDaemonChildren=64    //메일서버가 만들 수 있는 자식서버 수

DSlocalhost -> DS
O Timeout.ident=0s

# queue directory 변경
#O QueueDirectory=/var/spool/mqueue
O QueueDirectory=/var/spool/mqueue/q*
#다음 L값을 다음과 같이 수정
Msmtp,          P=[IPC], F=mDFMuX, S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP, E=\r\n, L=2990,
                T=DNS/RFC822/SMTP,
                A=TCP $h
Mesmtp,         P=[IPC], F=mDFMuXa, S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP, E=\r\n, L=2990,
                T=DNS/RFC822/SMTP,
                A=TCP $h
Msmtp8,         P=[IPC], F=mDFMuX8, S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP, E=\r\n, L=2990,
                T=DNS/RFC822/SMTP,
                A=TCP $h
Mdsmtp,         P=[IPC], F=mDFMuXa%, S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP, E=\r\n, L=2990,
                T=DNS/RFC822/SMTP,
                A=TCP $h
Mrelay,         P=[IPC], F=mDFMuXa8, S=EnvFromSMTP/HdrFromSMTP, R=MasqSMTP, E=\r\n, L=12040,
                T=DNS/RFC822/SMTP,
                A=TCP $h




3. queue directory를 병렬로 만들 것
/var/spool/mqueue > # mkdir q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 q16
sendmail.cf 파일 내에 QueueDirectory 값도 같이 변경


병렬처리의 이점은

하나의 디렉토리 안에 몇 천 ~ 몇 만개의 파일들이 존재할 경우 ext3부터는 상당부분 개선 되었지만,

ext2에서는 어마어마한 부하가 유발 하게 되는데 이를 분산시켜서 인덱싱에서 발생하는 불필요한 로드를 방지할 수 있게 된다.

메일발송 될때도 큐에 들어간 메일들이 처리 될때 병렬처리 되어 보다 빠른 처리속도를 보여준다.

갯수에 대한 제한은 확인되지 않지만, 그렇다고 무한정 늘리는 것만이 능사는 아니다.



4. 큐 발송 프로세스 생성
다음을 대략 설명해보면 -bd는 데모모드로 실행하고 -ODeliveryMode는 메일을 받자마자 큐로 넣으라는 듯이다
그리고 -q10s이런거는 10초에한번 큐를 뒤져보라는 것이다

vi demon
/usr/sbin/sendmail -bd -ODeliveryMode=defer


vi queue1
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q1 -OMaxDaemonChildren=32 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s


/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q1 -OMaxDaemonChildren=4


vi queue2
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q2 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q2 -OMaxDaemonChildren=4


vi queue3
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q3 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q3 -OMaxDaemonChildren=4


vi queue4
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q4 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q4 -OMaxDaemonChildren=4

vi queue5
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q5 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q5 -OMaxDaemonChildren=4


vi queue6
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q6 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q6 -OMaxDaemonChildren=4



vi queue7
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q7 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q7 -OMaxDaemonChildren=4




vi queue8
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q8 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q8 -OMaxDaemonChildren=4


vi queue9
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q9 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q9 -OMaxDaemonChildren=4


vi queue10
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q10 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q10 -OMaxDaemonChildren=4


vi queue11
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q11 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q11 -OMaxDaemonChildren=4



vi queue12
/usr/sbin/sendmail -q10s -OQueueDirectory=/var/spool/mqueue/q12 -OMaxDaemonChildren=16 \
-OTimeout.initial=30s -OTimeout.connect=30s -OTimeout.iconnect=30s -OTimeout.helo=30s \
-OTimeout.mail=30s

/usr/sbin/sendmail -q30s -OQueueDirectory=/var/spool/mqueue/q12 -OMaxDaemonChildren=4


#chmod u+x daemon queue*



5. /etc/resolv.conf에서 자체 dns사용 그렇지 않으면 smpt접속시 속도가 안나옴
이때 named가 설치 되지 않았다면 직접설치
nameserver 168.126.63.1 -> 127.0.0.1


6. 큐 삭제 데몬 생성

#vi pdel

for FILE in /var/spool/mqueue/q*/[dq]f*;
do
      echo -ne \\r"Deleting $FILE... ";
      rm $FILE -f;
      echo -n ok;
done;

echo

#chmod u+x pdel

#crontab -e

*/60 * * * * /agent/shell/pdel




-클라이언트쪽-
1. 메일 발송프로세스를 멀티 쓰레드로 할것, 보통 메일은 여러 클라이언트에서 들어오는데 여기선 한개의 서버에서만 들오기때문에 속도가 안나옴
 - 현재 쓰레드 10개로 나누어서 돌림








-참고문서 정리-

대규모 사이트가 아니라면 기본적인 설정으로 문제가 없다. 대규모 사이트에서 메일분산을 위해서는 sendmail 소스 배포본의 프로그램이 필요하므로 소스도 가져오기 바란다. 자체 네임서버도 필요하다.


-------------------------------------------
하루 1천통의 이메일
-------------------------------------------
하루에 1천통 이하의 메일을 처리하는 사이트가 있다.
이정도면 파일서버, 프린터서버, 웹서버로 같이 사용해도 된다.
자체 네임서버를 구축할 필요도 없으며 메일 큐 디렉토리의 크기에도 신경 쓸 필요가 없다. 로그파일도 크지 않을 것이다.
90퍼센트 이상의 사이트에서 이 정도의 부담으로 리눅스를 사용하고 있는데 시스템 관리자도 거의 필요 없고
서버를 구석에 놓고 그냥 쓰면 된다. 정전이 되었거나 무심코 리셋키를 눌러서 하드디스크에 문제가 생기지 않는다면 일년이상 신경쓰지 않아도 된다.


-------------------------------------------
1만통의 이메일
-------------------------------------------
가장 문제가 되는 것은 한 개의 데몬이 주고 받는 메일을 처리하기에는 힘들게 된다.
이제 데몬을 구분하도록 하자. 중계와 수신을 위해서, 그리고 큐에 쌓여 있는 메일을 처리할 두 개의 데몬으로 나눈다
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은 동시에 128개(수신, 중계 sendmail 64, 큐처리 64)까지 뜰 수 있다. 수신메일은 최대 64개 까지 동시에 처리되며 중계나 수신은 64개 까지는 거부없이 받아 들인다
 큐에 쌓인 메일은 큐처리 전용의 메일서버가 계속해서 전송을 시도하게 되며 정말로 갈 수 없는 메일이 아니라면 몇 시간 안에 메일은 보내지게 될 것이다.


-------------------------------------------
5만통의 이메일
-------------------------------------------
5만통의 메일을 주고 받게 되면 sendmail이 아닌 다른 부분에서 문제가 발생한다.
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초).

이 문제를 해결하고자 단순히 빠른 서버를 구입한다고 했을 때 하드웨어 업그레이드에 비해서 전송효율이 같은 비율로 올라가는 것은 아니다
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개의 메일을 처리하는 것이다

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

그래서 잘못된 메일은 즉시 삭제하는 크론탭을 만든다
*/20 * * * * /agent/shell/pdel

vi pdel
for FILE in /var/spool/mqueue/q*/[dq]f*;
do
      echo -ne \\r"Deleting $FILE... ";
      rm $FILE -f;
      echo -n ok;
done;

echo


-------------------------------------------
10만통의 이메일
-------------------------------------------
10만통의 메일을 하루에 처리하는 서버는 거의 엔터프라이즈급이라고 볼 수 있다.

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

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

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

시스템 자원을 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분 대기 프로세스가 남겨둔 메일을 다시 전송 시도 하도록 하면 된다. 그 때문에 한 개의 큐 디렉토리를 대상으로 하는 두 개의 데몬을 띄운 것이다.



-------------------------------------------
20만통의 메일
-------------------------------------------
20만통의 메일을 한 개의 큐 디렉토리에 받게 되었을 때 시간당 약 1만통의 메일이 쌓인다
한 개의 메일은 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


큐에 쌓인 메일을 처리하는 sendmail 프로세스는 메일을 보내기 위해서 네임서버와 교신해야 한다. 메일 서버가 서브네트웍 바깥에 있다면 한 개의 메일을 보내기 위해서 외부까지 교신을 해야 하기 때문에 시간이 많이 걸린다. 한 시간에 1만번의 조회를 시도하면 아마 외부 메일서버도 견디지 못하고 다운될 확률이 있다. 또한 네트웍 자원은 네임서버와의 교신에 모두 소모되고 다른 사람들이 외부로 나가거나 들어오는 것초차 힘들게 된다. 네임서버가 로컬 네트웍의 다른 서버에 있어도 마찬가지이다. 내부 네트웍은 거의 네임서버 접속에 이용되므로 회사 전산망이 마비되고 네임서비스를 하는 서버가 다운된다
그러므로 가능하면 메일서버 자체에 메일 전송을 위한 네임서버를 띄우는 것이 좋다. 이 때의 네임서버는 단순한 캐싱만을 하는 네임서버만으로 족하다. 처음에는 네임서버 자체가 외부로 조회를 해야 하기 때문에 시간이 걸리겠지만 계속 사용한다면 중복되는 IP는 같은 메모리 안에서 처리가 가능하기 때문에 외부에 영향을 주지 않으며, 로컬 네트웍의 tcp/ip 자원도 소모시키지 않고 신속한 조회가 이루어 질 수 있다. 캐싱만 하는 네임서버는 간단히 다음과 같이 만들 수 있다


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

댓글 없음:

댓글 쓰기

image

image