DevOps
CloudFront Invalidations 구성
Hannibal여기어때
2022년 12월 14일
원문에서 보기 ↗안녕하세요. 여기어때컴퍼니 인프라개발팀 한니발 입니다.
프론트개발팀과 협업을 하면서 AWS CDN 서비스인 CloudFront를 운영하는 중에 특정 오브젝트가 업데이트 되지 않는 이슈가 있었습니다. 이 이슈에 대한 해결책을 말씀드리기 전에 먼저 CloudFront 서비스의 기본적인 로직을 설명하려 합니다.
첫 번째 로직은 Cloudfront 에서 해당 오브젝트에 처음으로 요청이 올 경우입니다.
로직
-
Client -> Internet -> CloudFront
-
CloudFront -> Origin S3 -> jeju.mp4 호출 후 캐시 서버에 적재
-
jeju.mp4 확인 (Miss From CloudFront 헤더 문구확인)

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

처음 오브젝트가 호출됐을 시 헤더 값
두 번째 로직은 캐시서버에 적재 후 같은 오브젝트를 호출 할 경우
로직
-
Client -> Internet -> CloudFront -> 캐시버서에서 jeju.mp4 확인
-
jeju.mp4 확인 ( Hit From CloudFront 헤더 문구 확인)

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

캐시서버에 적재된 오브젝트가 호출됐을 시 헤더 값
이 두 가지 로직 중 가장 큰 차이점은 Origin S3에 접근하는지 여부입니다.
두 번째 로직의 경우 Origin S3까지 트래픽이 가지 않기 때문에 부하가 적어
안정성을 확보할 수 있고 비용 절감 효과도 기대할 수 있습니다.
다만 한 가지 이슈가 있습니다. 같은 이름에 오브젝트 파일이 변경이 되었을 경우에 TTL 시간 동안 오브젝트가 자동으로 업데이트가 이루어 지지 않습니다. 예를 들어 jeju.mp4 파일이 변경 되어도 CloudFront 상 에서는 실시간 업데이트 가 아닌 TTL 기준으로 오브젝트 업데이트가 이루어 집니다. 이럴 경우 수동으로 CloudFront 에서 제공하는 무효화(Invalidations)를 사용하여 캐시서버에 적재된 오브젝트를 강제로 업데이트 하는 방법이 있습니다. 하지만 오브젝트가 업데이트 될 때 마다 수동으로 무효화를 진행해야 합니다.
이런 번거로움과 불필요한 리소스 낭비를 줄이기 위해 Origin S3에 Put 이벤트가 발생할 경우 해당 오브젝트에 관해 자동으로 무효화가 될 수 있게 Lambda를 도입하게 되었습니다. 수행 방식은 아래와 같습니다.
-
신규 컨텐츠 S3 Upload
-
S3에서 해당 컨텐츠 감지
-
Invalidations를 담당하는 Lambda 호출
-
Lambda에서 해당하는 CloudFront에 Invalidations 수행

이로써 컨텐츠가 업로드 될 때 자동으로 CloudFront의 캐쉬를 Invalidations 할 수 있는 구성이 됩니다.
구성 방법
- IAM Role 생성 Lambda에서 사용할 Role이기 때문에 사용 사례 Lambda를 선택하여 생성 합니다.

정책은 3가지 정책을 넣어 줍니다.
- AmazonS3FullAccess
- CloudWatchFullAccess
- CloudFrontFullAccess
- Lambda 생성
- 함수 생성 → 새로 작성
- 함수 이름 : <원하는 함수 이름>
- 런타임 : Python 3.7
- 실행 역할 : 기존 역할 사용 — <만들어 놓은 IAM Role>
- 함수 생성 클릭

함수를 생성하게 되면 트리거에 기본적으로 S3가 설정되어 있습니다. 만약 설정되어 있지 않다면 트리거 추가를 클릭하여 S3를 추가한 후 트리거 구성을 합니다.
- 버킷 : <CloudFront 오리진 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 입니다.
- Key : CLOUDFRONT_DISTRIBUTION_ID
- Value : <CloudFront 배포 이름>

모든 구성이 완료 되었습니다.
테스트
구성한 S3에 test 용으로 test.txt 파일을 Upload 하였습니다.

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

test.txt에 대해서 자동으로 invalidations가 수행된 것을 확인할 수 있습니다.
마지막으로 CloudWatch 에서 정상적으로 Log가 생성되었는지 확인 합니다.

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