최근에 읽었던 글들 중에서 좋았던 글 중 1개 메모

 

https://www.allthingsdistributed.com/2023/07/building-and-operating-a-pretty-big-storage-system.html

 

Building and operating a pretty big storage system called S3

Three distinct perspectives on scale that come along with building and operating a storage system the size of S3.

www.allthingsdistributed.com


https://careerly.co.kr/comments/88808?utm_campaign=user-share
에 간략히 한글로 요약해두셨음

  1. 목적/배경
    1. https://blog.eomsh.com/187 에서 AWS SES를 사용해서 이메일 발송 서비스를 구축했다면 bounce 관리가 필요하다고 간단히만 코멘트
    2. 그런데, 메일함이 꽉 차서 유저에게 메시지를 띄워주거나, 반송되는 이메일 주소에게 본인의 어플리케이션이 메일을 다시 보내지 않게 하려면 메일 주소를 어플리케이션에 DB로 구축해야함
      1. 수신거부 등의 이벤트도 수집 가능
  2. 방법
    1. AWS SES 웹 콘솔에서 Notification 설정 -> SNS로 전송 -> Lambda 호출 -> 작성한 파이썬 코드 호출
      1. 추가 로직이 필요 없으면 SNS에서 바로 http api호출해도 될 것 같기는 함
      2. 참고 링크 
  3. 간단히 작성한 파이썬 소스코드
    1. AWS Lambda runtime 파이썬 레이어에 request 사용을 위해 추가 후 아래 코드 실행되게 함(참고 링크)
    2. 해당 API가 호출되어 저장되는 DB DDL과 Java 어플리케이션도 있는데 그건 추후 로그에 작성해보겠음

 

import json
import os

import requests

AWS_ACCOUNT_ID = "입력필요"  # AWS 계정ID(readable한 키워드-고유의 숫자ID 포맷 사용, 해당 파이썬 코드가 실행되는 AWS계정에 맞춰서 변경하세요)
AWS_REGION_CD = "입력필요"  # AWS 리전 코드(해당 파이썬 코드가 실행되는 lambda 리전에 맞춰서 변경하세요)

REQ_AUTH_KEY = "API호출할 때 보안을 위해서 사용하는 인증키"  # API 호출에 사용하는 인증용 키

# HTTP API로 SES알림 저장을 요청하는 end point URL
SES_NOTIFICATION_SAVE_API_URL = "https://블라블라/" + AWS_ACCOUNT_ID + "/" + AWS_REGION_CD + "/블라블라"

def lambda_handler(event, context):
    ses_notification = event['Records'][0]['Sns']['Message']  # SNS로부터 전달받는 이벤트 추출
    data_json = json.loads(ses_notification)  # json으로 파싱/로드

    notification_type = data_json['notificationType']
    if notification_type == "AmazonSnsSubscriptionSucceeded":
        print(f"AmazonSnsSubscriptionSucceeded는 저장하지 않습니다.(SNS로 구독 성공시 이벤트) data_json: {data_json}")
        return

    # 알림 저장 API 호출
    data_json_str = json.dumps(data_json, ensure_ascii=False).encode('utf-8')
    result_save_api = request_http_save(SES_NOTIFICATION_SAVE_API_URL, 10, data_json_str)
    print(f"result_save_api: {result_save_api}", end="\n\n")


def test_save_from_sample_file():  # 테스트 목적으로 만든 파일의 샘플 json을 읽어서 저장요청

    # 샘플: https://docs.aws.amazon.com/ko_kr/ses/latest/dg/notification-examples.html#notification-examples-bounce 를 파일로 만들어서 테스트
    file_name = "test-bounce-exist-dns-data.json"
    # file_name = "test-bounce-not-exist-dns-data.json"
    # file_name = "test-AmazonSnsSubscriptionSucceeded.json"
    current_directory = os.getcwd()
    test_json_file_path = os.path.join(current_directory, file_name)

    with open(test_json_file_path, 'r', encoding='UTF8') as f:
        data_json = json.load(f)  # 문자열 json을 파싱한 내용

    print(f"테스트 파일 {file_name}의 내용 ===>\n\t", json.dumps(data_json, indent=4, ensure_ascii=False), end="\n\n")

    notification_type = data_json['notificationType']
    if notification_type == "AmazonSnsSubscriptionSucceeded":
        print(f"AmazonSnsSubscriptionSucceeded는 저장하지 않습니다. data_json: {data_json}")
        return

    # 알림 저장 API 호출
    data_json_str = json.dumps(data_json, ensure_ascii=False).encode('utf-8')
    result_save_api = request_http_save(SES_NOTIFICATION_SAVE_API_URL, 10, data_json_str)
    print(f"result_save_api: {result_save_api}", end="\n\n")

def request_http_save(uri: str, timeout_seconds: int, req_json_str: str) -> object:
    """
    외부 API에 SES 알림 데이터 전송하는 HTTP통신용 함수

    :param uri: 요청 URI
    :param timeout_seconds: 타임아웃(초)
    :param req_json_str: 요청할 json(문자가 아니라 json타입)
    :return:
    """
    print("\n === Start request_http_save. uri: " + uri)
    headers = {
        'reqAuthKey': REQ_AUTH_KEY,
        'Content-Type': 'application/json; charset=utf-8'
    }

    try:
        # 한글이 포함되어 있을 수 있음
        # req_json_str = json.dumps(ses_notification_json, ensure_ascii=False).encode('utf-8')
        response = requests.request("POST", uri, headers=headers, timeout=timeout_seconds, data=req_json_str)

        return {
            'statusCode': response.status_code,
            'body': response.text
        }

    except requests.exceptions.Timeout as errd:
        print("Timeout Error : ", errd)

    except requests.exceptions.ConnectionError as errc:
        print("Error Connecting : ", errc)

    except requests.exceptions.HTTPError as errb:
        print("Http Error : ", errb)

    # Any Error except upper exception
    except requests.exceptions.RequestException as erra:
        print("AnyException : ", erra)

# API에 저장요청(로컬 테스트를 위해서 파일에서 샘플 Json을 읽어서 호출)
#test_save_from_sample_file()

 

 

https://infrastructure.aws/


멋있게 나옴



개인적으로 API를 만들어서 AWS EC2에서 서비스 중인 사용처들에게 제공 중인데 이 때 JWT인증을 사용 중. 관련하여 메모


1. JWT토큰 인증이란?

AWS의 인스턴스는 IP가 변경될 수 있기에 기존의 IP인증 방식으로는 API를 사용할 수 없습니다.(Elastic ip)를 사용하면 고정되기는 함)
이처럼 IP인증 방식을 사용하기 어려울 때 사용하는 인증 방식입니다.

2. JWT 토큰의 포맷 및 규약은?

JWT(Json Web Token)이라는 RFC 7519 표준의 토큰방식을 따릅니다.(http://jwt.io/)
Header(알고리즘과 토큰타입), Payload(데이터), Verify signature(변조검증)로 구성되어 있으며
Header와 Payload는 base64로 디코딩시 내용을 확인 가능합니다.

토큰인증 방식을 사용하시려면 HTTP 헤더에 서비스코드와 인증토큰 2가지 값을 전달해주셔야합니다.

3. 디코딩시 내용을 볼 수 있는데 보안 문제는 없는건지?

API 인증 용도의 토큰은 유저정보 등의 노출되면 안되는 정보가 없기에 디코딩되도 무방합니다.
또한, Verify Signature가 존재하여 HMAC로 토큰을 검증하기에 Header와 Payload는 변조가 불가능합니다.
(필요시 토큰 자체를 암호화할 수도 있기는하지만, 필요성이 없는 작업에 암복호화를 위한 서버 리소스를 사용할 필요가 없어서 진행 안함 , 필요시 AES256으로 암복호화 처리 중)

4. 토큰 인증방식의 성능은?
성능 테스트결과 기존 IP인증 방식과 동일한 TPS를 보였으며 경우에 따라서는 더 좋은 결과를 나타냈습니다.

5. 토큰을 발급 받으려면?

"따로 제공하는 인증토큰 발급 API"를 이용해서 발급 받을 수 있습니다.
해당 API는 HTTPS로 통신하며 사전에 전달받은 서비스코드와 토큰발급용 암호 2가지 값이 필요합니다.

6. 사용 중 토큰이 만료되었다고 실패가 발생하면?
토큰을 재 발급 받아서 해당 API를 다시 호출하셔야 합니다.

7. 토큰은 서비스별로 유일한지?
유일하지 않습니다. 여러개일 수 있습니다.

8. 토큰이 여러개일 수 있는 이유는?

API를 호출하는 서버는 여러개일 수 있어서 특정 토큰 1개만 허용하게되면 동시성 이슈가 발생할 수 있습니다. 
또한, 토큰 1개만 유지할 경우 제공 및 사용측에서 글로벌캐쉬 등을 사용해야해서 복잡해집니다.
이를 해결하기 위해 토큰은 여러개 발급 가능하도록 허용해뒀습니다.




AWS ELB를 이용해서 로드밸런싱을 하는데 주기적으로 응답이 느려지는 현상(몇초마다 응답이 10초가 넘어감)이 발생했다.

어플리케이션 문제인가 싶어서 소거법으로 하나하나 테스트를 해봤는데 아니었다.


1. 테스트환경을 구축 후 반복해서 URL을 호출

  watch --interval=1 time curl "검증 URL" > /dev/null


2. ELB 모니터링 및 DNS도 확인




결론은, ELB를 새로 띄우니 감쪽같이 해결되었다.

아직 한국 리전에서 불안전한 케이스가 있는게 아닌가 싶다..(AWS 사용자 사이에서 몇가지 카더라도 있고..)




+ Recent posts