출처 : http://blog.naver.com/PostView.nhn?blogId=khi830&logNo=20118141893
Sendmail의 mqueue를 설정할 필요성을 느끼게 된 것은, 운영중인 한 시스템에서 대량으로 메일을 발송하는데, 없는 주소이거나 시스템 거부로 인하여 메일이 송신되지 않으면, 계속해서 재송신 하려하는 문제점을 해결하기 위해서 였다.
Mail Queue는 Redhat계열의 경우 보통 /var/spool/mqueue 에 위치하며, Sendmail을 통하여 메일을 송신하면 일단 이 queue에 저장이 된다. 그리고 시스템 자원을 보아 가며 메일을 조금씩 보내게 되는데, 메일 송신이 실패하면 해당 서버로부터 에러메시지나, 회송처리된 메일을 돌려받게 되는데, 이러한 메일들의 처리 역시 mqueue가 담당을 하기에 정상적인 메일이 아닌 메일로 다량 메일을 보내거나 하면, mqueue가 금방 수많은 파일들로 북적대게된다.
이번 서비스의 문제는 이렇다.
대량의 메일주소로 메일을 보낸다.
메일이 보내지면 문제가 없지만, 다시 대량으로 반송된다.
반송된 메일을 queue에 보관한다.
정기적으로 queue에 보관된, 송신되지 못한 메일을 보낸다. 다시 반송된다.
결국 언젠가는 파기되지만, 파기될 때 다시 보낸 사람에게 메일이 돌아온다.
여기서 정기적으로 다시 메일을 송신하는 부분과, queue에서 보관중인 메일을 파기할 때 다시 보낸 사람에게 메일을 돌려보내는 작업은 시스템 자원 낭비라고 판단. 이를 해결해 보고자한다.
우선 알아 두어야 할 명령어 사용 법을 알아보도록 한다.
1. mqueue의 처리 상황을 확인하는 명령
/usr/sbin/sendmail -bp
아래는 큐에 처리할 메일이 없을 경우 이다. 멀티큐 설정으로 q1 ~ q6의 디렉토리가 보인다.
[root@www mqueue]# sendmail -bp
/var/spool/mqueue/q2 is empty
/var/spool/mqueue/q1 is empty
/var/spool/mqueue/q3 is empty
/var/spool/mqueue/q6 is empty
/var/spool/mqueue/q5 is empty
/var/spool/mqueue/q4 is empty
Total requests: 0
아래는 mqueue에 처리되고 있는 메일이 있는 경우이다.
[root@www mqueue]# /usr/sbin/sendmail -bp
/var/spool/mqueue/q5 (20 requests)
??Q-ID?? ?Size? ??Q-Time?? ????Sender/Recipient????
lAF2oPUX014911 2295 Thu Nov 15 11:50 MAILER-DAEMON
(Deferred: local mailer (/usr/bin/procmail) exited with EX_TE)
apache
lAF2oPUZ014911 4071 Thu Nov 15 11:50 MAILER-DAEMON
(Deferred: local mailer (/usr/bin/procmail) exited with EX_TE)
root
apache
lAF2oPUp014911 8627 Thu Nov 15 11:50 MAILER-DAEMON
(Deferred: local mailer (/usr/bin/procmail) exited with EX_TE)
root
apache
lAF2oPUt014911 10026 Thu Nov 15 11:50 MAILER-DAEMON
(Deferred: local mailer (/usr/bin/procmail) exited with EX_TE)
root
apache
…
Total requests: 20
2. aliases 업데이트
sendmail -bi와 newaliases가 결국 같은 명령이다.
[root@www mqueue]# sendmail -bi
/etc/aliases: 78 aliases, longest 109 bytes, 1022 bytes total
[root@www mqueue]# newaliases
/etc/aliases: 78 aliases, longest 109 bytes, 1022 bytes total
그러면, 이제는 mqueue의 설정에 관하여 알아보도록 하자.
모두 /etc/mail/sendmail.cf 에서의 설정이다.
3. MinQueueAge=30m
mqueue의 메일 재발송 시간 설정. Timeout 설정 이전에 적용되는 우선적인 옵션으로, 발송에 실패한 메일이 다시 재발송을 시도하기까지의 대기 시간을 설정 한다. 기본값은 30분이며 기본적으로 서버부하를 줄이기 위해 # 주석처리 되어있지만 필요로 한다면 사용하여도 무방하다. /etc/rc.d/init.d/sendmail 스크립트에 /usr/sbin/sendmail -bd -q1h 로 설정이 되어 있는데 q1h가 queue 1hour 이라는 뜻으로 옵션으로 설정할 수도 있다.
4. Timeout.queuereturn=5d
특정한 사유로 인해 메일이 전달되지 못하고 mqueue 에보관되고 있을경우의 보관 기간을 지정하는것으로 기본값은 5일이다. 이기간을 넘길경우 메일은 자동적으로 최초 발신지로 되돌려져 반송처리가 된며, queue에 보관된 메일은 파기된다.
5. Timeout.queuewarn=4h
– 역시 특정한 사유로 인해 메일이 전달되지 못할경우, 최초 발신자에게 메일이 전달되지 못하고 있음을 경고하는 경고메일이 발송 되기까지의 대기 시간을 지정하며 기본값은 4시간이다.
실제 sendmail.cf에 설정한 내용은 다음과 같다.
# timeouts (many of these)
#O Timeout.queuereturn=5d
O Timeout.queuereturn=1s
O Timeout.queuewarn=4h
단지 Timeout.queuereturn=5d 를 1s 즉 1초로 바꾼 것으로 메일을 보내고 1초간 mqueue에 저장하며 이로 인하여 바로 메일을 송신자에게 반송 한 후, 송신한 메일queue는 파기한다. 다른 설정은 모두 mqueue에 메일이 있을 경우에 해당하는 설정이므로 Timeout.queuereturn설정만 바꿔주었다.
하지만 여기에도 문제가 있었다. 송신에 실패한 메일을 바로 파기하는것 까지는 좋았는데, 그 다음에는 되돌아온 메일을 송신자에게 보내려는 메일로 mqueue가 가득 차게 되는 것이었다. 그래서 반송되어진 메일큐의 메일들을 지우는 스크립트를 작성하고 cron에 등록하여 주기적으로 삭제해 주기로 결정하였다. 물론 대량으로 보내는 메일이 아닌, 개인이 주고받는 메일은 삭제하면 안되기 때문에, 메일의 내용을 보아가며 삭제할지 여부를 판단해야만 했다. – 여기서의 메일은 모두 메일 큐에 쌓여있는 메일이다.
결국 필요한동작은,
mqueue 이하의 모든 파일의 내용을 검색하여 특정 문자열 – 여기서는 E-mail주소 – 이 있는 파일들만 삭제
하는 기능인데 다음과 같이 구현하였다.
삭제 대상 파일은 두가지.
1초뒤 메일을 파기하여 송신자에게 보내는 메일
메일 송신 대상 서버로부터 송신자에게 온 에러 메일
6. 메일을 파기하고 송신자에게 반송하는 메일 찾아 지우기
송신자가 support@xxx.com 이라는점을 이용, ‘To:
’로 검색을 하였다.
[ 찾는 방법 ]
find /var/spool/mqueue -type f -exec egrep -i “To:
” /dev/null {} ; | awk -F: ‘{print $1}’
[ 찾아 지우는 방법 ]
find /var/spool/mqueue -type f -exec egrep -i “To:
” /dev/null {} ; | awk -F: ‘{print $1}’ | sed “s/.*/rm -rf /” | bash
※ 맨 뒤의 bash는 sh로 치환해도 동작한다. 해당 shell로 실행하라는 것임
7. 상대 서버로부터 송신자에게 온 에러 메일 찾아 지우기
송신자가 support@xxx.com 이라는점을 이용, ‘Return-Path:
’로 검색을 하였다.
[ 찾는 방법 ]
find /var/spool/mqueue -type f -exec egrep -i “Return-Path:
” /dev/null {} ; | awk -F: ‘{print $1}’
[ 찾아 지우는 방법 ]
find /var/spool/mqueue -type f -exec egrep -i “Return-Path:
” /dev/null {} ; | awk -F: ‘{print $1}’ | sed “s/.*/rm -rf /” | bash
※ 맨 뒤의 bash는 sh로 치환해도 동작한다. 해당 shell로 실행하라는 것임
이렇게 하여, 1초만에 메일을 파기하고, 이에따른 잔류메일을 mqueue에서 삭제하는 방법을 알아보았다.