grep

DevOps

CloudFront Invalidations 구성

Hannibal여기어때

2022년 12월 14일

원문에서 보기 ↗

안녕하세요. 여기어때컴퍼니 인프라개발팀 한니발 입니다.

프론트개발팀과 협업을 하면서 AWS CDN 서비스인 CloudFront를 운영하는 중에 특정 오브젝트가 업데이트 되지 않는 이슈가 있었습니다. 이 이슈에 대한 해결책을 말씀드리기 전에 먼저 CloudFront 서비스의 기본적인 로직을 설명하려 합니다.

첫 번째 로직은 Cloudfront 에서 해당 오브젝트에 처음으로 요청이 올 경우입니다.

로직

  1. Client -> Internet -> CloudFront

  2. CloudFront -> Origin S3 -> jeju.mp4 호출 후 캐시 서버에 적재

  3. jeju.mp4 확인 (Miss From CloudFront 헤더 문구확인)

처음 오브젝트가 호출될 경우의 Flow

처음 오브젝트가 호출됐을 시 헤더 값

두 번째 로직은 캐시서버에 적재 후 같은 오브젝트를 호출 할 경우

로직

  1. Client -> Internet -> CloudFront -> 캐시버서에서 jeju.mp4 확인

  2. jeju.mp4 확인 ( Hit From CloudFront 헤더 문구 확인)

캐시서버에 적재된 오브젝트가 호출될 경우의 Flow

캐시서버에 적재된 오브젝트가 호출됐을 시 헤더 값

이 두 가지 로직 중 가장 큰 차이점은 Origin S3에 접근하는지 여부입니다.

두 번째 로직의 경우 Origin S3까지 트래픽이 가지 않기 때문에 부하가 적어

안정성을 확보할 수 있고 비용 절감 효과도 기대할 수 있습니다.

다만 한 가지 이슈가 있습니다. 같은 이름에 오브젝트 파일이 변경이 되었을 경우에 TTL 시간 동안 오브젝트가 자동으로 업데이트가 이루어 지지 않습니다. 예를 들어 jeju.mp4 파일이 변경 되어도 CloudFront 상 에서는 실시간 업데이트 가 아닌 TTL 기준으로 오브젝트 업데이트가 이루어 집니다. 이럴 경우 수동으로 CloudFront 에서 제공하는 무효화(Invalidations)를 사용하여 캐시서버에 적재된 오브젝트를 강제로 업데이트 하는 방법이 있습니다. 하지만 오브젝트가 업데이트 될 때 마다 수동으로 무효화를 진행해야 합니다.

이런 번거로움과 불필요한 리소스 낭비를 줄이기 위해 Origin S3에 Put 이벤트가 발생할 경우 해당 오브젝트에 관해 자동으로 무효화가 될 수 있게 Lambda를 도입하게 되었습니다. 수행 방식은 아래와 같습니다.

  1. 신규 컨텐츠 S3 Upload

  2. S3에서 해당 컨텐츠 감지

  3. Invalidations를 담당하는 Lambda 호출

  4. Lambda에서 해당하는 CloudFront에 Invalidations 수행

이로써 컨텐츠가 업로드 될 때 자동으로 CloudFront의 캐쉬를 Invalidations 할 수 있는 구성이 됩니다.

구성 방법

  1. IAM Role 생성 Lambda에서 사용할 Role이기 때문에 사용 사례 Lambda를 선택하여 생성 합니다.

정책은 3가지 정책을 넣어 줍니다.

  1. Lambda 생성

함수를 생성하게 되면 트리거에 기본적으로 S3가 설정되어 있습니다. 만약 설정되어 있지 않다면 트리거 추가를 클릭하여 S3를 추가한 후 트리거 구성을 합니다.

코드 탭으로 이동하여 Lambda 코드를 입력해 줍니다.

import boto3
import time
import os
def handle_s3_change(event, context):
  paths = []
  for items in event["Records"]:
    key = items["s3"]["object"]["key"]
    if key.endswith("index.html"):
      paths.append("/" + key[:-10])
    paths.append("/" + key)
  print("Invalidating " + str(paths))
   
  client = boto3.client('cloudfront')
  batch = {
    'Paths': {
      'Quantity': len(paths),
      'Items': paths
    },
    'CallerReference': str(time.time())
  }
  invalidation = client.create_invalidation(
    DistributionId=os.environ['CLOUDFRONT_DISTRIBUTION_ID'],
    InvalidationBatch=batch,
  )
  return batch

코드 아래쪽에 런타임 설정 → 핸들러도 수정해 줍니다.

마지막으로 구성탭의 환경변수로 이동하여 환경변수를 입력해 줍니다. 여기서 Value 값은 우리가 Invalidations를 수행할 CloudFront의 DISTRIBUTION ID 입니다.

모든 구성이 완료 되었습니다.

테스트

구성한 S3에 test 용으로 test.txt 파일을 Upload 하였습니다.

연결되어 있는 Cloudfront로 이동하여 Invalidations가 수행되는지 확인합니다.

test.txt에 대해서 자동으로 invalidations가 수행된 것을 확인할 수 있습니다.

마지막으로 CloudWatch 에서 정상적으로 Log가 생성되었는지 확인 합니다.

로그가 정상적으로 생성되는 것을 확인 할 수 있습니다.

해당 Lambda 적용 이후로는 따로 개발자가 오브젝트 업데이트에 대한 이슈를 해결하였습니다. TTL을 0으로 변경하는 방법도 있지만, 이럴 경우 CDN의 장점을 살릴 수 없다고 봅니다. 해당 이슈 계기로 CDN 서비스와 CloudFront에 대해 공부하는 계기가 되었습니다. 이 글이 AWS 서비스 이해하는데 조금이라도 도움이 되었으면 좋겠습니다.

감사합니다.