SES (Simple E-mail Service) 을 사용중인데 어느날 AWS측에서 메일이 왔다.

이메일 반송률 (Bounce rate)이 10%를 넘어간단다...


들어가서 확인해보니 



bounce rate를 5%를 밑으로 유지해야하는데 10%를 왔다갔다 하고 있었다.

지속적으로 5%이상이면 해당 SES를 사용할수 없게 막혀버리던가 계정자체가 막힐수도 있단다.


보통 해결방법을 찾아보니


SES (반송) -> SNS -> Lambda -> DynamoDB


이렇게 구성하여 다이나모DB에서 모은 반송메일주소 리스트를 가지고 발송시에 거르는 방법으로 가고있더라.

그래서 일단 해당 방법으로 나도 구현하기로 하여 시작하였다.



----------------------------------------------------------------------------------

SNS 주제 생성


먼저 SNS (Simple Notification Service)로 가서 주제를 만들어야한다.

(SES 에서 해당 주제를 설정해놓으면 반송시에 해당 SNS로 메세지가 들어간다)



위와 같이 SES-Bounce 라는 이름으로 주제를 만들었다. 

(나같은 경우에는 [버지니아 북부] 리전에서 SES를 만들었었는데 해당 SNS주제를 설정하려면 같은리전에 있어야 설정할 수 있어서

SNS 주제 생성도 버지니아 북부에서 하였다.)



-------------------------------------------------------------------------------------

SES 설정


SES로 다시 가서 해당 발신 이메일을 클릭 후 

Notifications -> Edit Configuration 으로 간뒤

SNS Topic Configuration -> Bounces 에서 위에서 만든 SNS 주제를 선택해준다.



여기 까지하면 해당 발신이메일로 이메일 발송 후 반송시 해당 SNS주제로 알림이 전달된다.

이제는 SNS를 이용해 람다에서 다이나모DB로 넣어주면 될듯하다.



-------------------------------------------------------------------------------------

DynamoDB 테이블 생성


Lambda 생성전에 먼저 DynamoDB 를 만들자.


AWS DynamoDB로 가서 테이블 만들기를 한 후

테이블 이름과 기본키를 정한다.


내 경우에는 ses_bounce 라는 이름으로 테이블을 만들었고,

파티션 키는 단순하게 email (문자열) 이라고 넣었다. bounce처리된 이메일주소를 키로 raw들을 쌓을 것이다.

(이렇게 이메일을 키로 해놓으면 중복 수집 생각할 필요없을듯 하여...)




나중에 파이썬에서 boto3로 데이터를 넣을거니 boto3클라이언트에 넣을 IAM을 미리생성하여

access_key와 secret_key를 발급해놓자.



-------------------------------------------------------------------------------------

Lambda 생성, 설정


이제 람다를 만들러가면 된다.

나는 파이썬으로 다이나모DB에 넣어주는 로직을 만들거기 때문에 python3.6 으로 선택하여 만들었다.

각자 맞는 언어로 알아서 만들면될듯. 참고로 DB도 나는 dynamoDB를 정하였지만, 

몽고디비, RDB등 알아서 정하면된다.





람다를 생성 후 앞에 위에서 만들었던 SNS를 달아주면된다.

(마찬가지로 내가 SEN, SNS가 전부 버지니아 북부에 만들어서 해당 람다에서 SNS를 잡기위해 람다도 버지니아 북부에 만들었다)




-------------------------------------------------------------------------------------

테스트 발송, 데이터 확인


나는 boto3로 bounce처리되는 이메일을 python으로 현재 시간과 함께 다이나모DB에 적재할 것인데

SNS에서 람다로 들어왔을때의 데이터 형태를 모르기 때문에 우선 테스트발송을 해보기로 하였다.


SES에서 [Send a test email] 이라는 항목이 있는데 해당 테스트발송 기능을 이용하여

bounce@simulator.amazonses.com

이쪽으로 메일을 보내게 되면 반송이 된다




발송 후 람다로 들어오는 데이터를 찍어보니 아래와 같이 데이터가 들어왔다.


{
  'Records': [{
    'EventSource': 'aws:sns',
    'EventVersion': '1.0',
    'EventSubscriptionArn': 'arn:aws:sns:us-east-비밀비밀',
    'Sns': {
      'Type': 'Notification',
      'MessageId': 'd297a622-비밀비밀',
      'TopicArn': 'arn:aws:sns:us-east-1:비밀비밀:SES-Bounce',
      'Subject': None,
      'Message': '{"notificationType":"Bounce","bounce":{"bounceType":"Permanent","bounceSubType":"General","bouncedRecipients":[{"emailAddress":"bounce@simulator.amazonses.com","action":"failed","status":"5.1.1","diagnosticCode":"smtp; 550 5.1.1 user unknown"}],"timestamp":"2018-11-15T02:36:34.200Z","feedbackId":"비밀비밀","remoteMtaIp":"비밀비밀","reportingMTA":"dsn; 비밀비밀"},"mail":{"timestamp":"2018-11-17T02:36:33.000Z","source":"비밀비밀@naver.com","sourceArn":"arn:aws:ses:us-east-1:비밀비밀","sourceIp":"비밀비밀","sendingAccountId":"비밀비밀","messageId":"비밀비밀","destination":["bounce@simulator.amazonses.com"]}}',
      'Timestamp': '2018-11-17T02:36:34.217Z',
      'SignatureVersion': '1',
      'Signature': '비밀비밀',
      'SigningCertUrl': '비밀비밀',
      'UnsubscribeUrl': '비밀비밀',
      'MessageAttributes': {}
    }
  }]
}


어쩄든 여기서 필요한 데이터는 Records 안에 Sns 안에 Message 인데

해당 Message를 파싱하여 bounce 안에 bouncedRecipients의 emailAddress가 궁극적으로 가져오고싶은 반송 이메일 주소가 되겠다!



-------------------------------------------------------------------------------------

Python 소스작성


위의 데이터가 람다로 들어온다고 생각하고 이제 소스를 짜면 되겠다.


난 귀찮으니 따로 로컬에 프로젝트 만들지 않고 

람다 코드 인라인 편집기에서 바로 작성하였다.






import logging
import json
import boto3
import os
import datetime

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ACCESS_KEY = os.environ.get('ACCESS_KEY')
SECRET_KEY = os.environ.get('SECRET_KEY')

def lambda_handler(event, context):
    try:
        now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        message = event['Records'][0]['Sns']['Message']
        
        bounce_list = json.loads(message)["bounce"]["bouncedRecipients"]
        
        if len(bounce_list) > 0:
            for bounce in bounce_list:
                if str(bounce["emailAddress"]) is not None and str(bounce["emailAddress"]) != "":
                    put_email(str(bounce["emailAddress"]), str(now))
    
    except Exception as e:
        logger.error('error : {}'.format(str(e)))
    
    return True


def put_email(email, now):
    result = "fail"
    dynamo_client = boto3.client('dynamodb', region_name='ap-northeast-2',
                               aws_access_key_id=ACCESS_KEY,
                               aws_secret_access_key=SECRET_KEY)

    response = dynamo_client.put_item(
        TableName='ses_bounce',
        Item = {
            "email": {
                "S": email
            },
            "date": {
                "S": str(now)
            }
        }
    )
    
    if "ResponseMetadata" in response:
        if "HTTPStatusCode" in response["ResponseMetadata"]:
            if response["ResponseMetadata"]["HTTPStatusCode"] == 200:
                result = "success"
                
    logger.info('{} : {}, '.format(email, result))


딱히 어려움이 없는 소스이다...

데이터 들어오는거 파싱해서 emailAddress 추출한다음에 boto3를 이용하여 다이나모 디비에 넣어주면된다.

(boto3는 aws lambda python에 기본으로 들어있는듯하니 따로 모듈을 추가할 필요 없는듯 하다)


(다이나모쪽에 권한이 있는 IAM의 access_key와 secret_key를

람다 설정쪽의 [환경변수] 항목에 ACCESS_KEY, SECRET_KEY 라는 이름으로 각각 넣어놓으면 해당 키를 가져다가 boto3.client에서 설정한다)


 


작성 후 게시하여 위에서 했던 방법대로 테스트 발송을 해보도록 한다.


-------------------------------------------------------------------------------------

결과


테스트 발송을 해보거나 실제로 반영 후에 다이나모디비를 확인해보면




위 사진처럼 데이터가 쌓이게 된다.

발송 로직에서 해당 데이터를 긁어와 예외처리 해주면 ses bounce rate가 점차 줄어들것이다.


'공부 > Aws' 카테고리의 다른 글

boto3 s3 remove deletemarker  (7) 2018.05.22
AWS cli s3 덮어쓰기  (0) 2018.04.27
AWS cli s3 upload  (0) 2018.04.24
AWS cli config  (0) 2018.04.24
AWS s3 listObjects all  (1) 2018.04.12

+ Recent posts