이미지 수정이 발생했을 때 즉각적으로 반영되어야 하는 기능을 개발하고 있다.
수정은 S3에 업로드할 때 덮어쓰기를 하는 방식으로 진행했다.
그래서 AmazonS3의 putObject() 메소드를 활용하여 동일한 경로, 동일한 이름으로 새로운 이미지 파일을 업로드 했다.
수정 시간이 변경된 것을 확인했으며, 또한 s3에서 '열기'를 하여 새로운 이미지 파일로 덮어쓰기된 것까지 확인했다.
그런데... 해당 이미지를 s3에서 불러오는 path를 브라우저에 입력하여 확인해보니... 덮어쓰기 전 이미지가 나왔다...
S3 및 기타 대규모 분산 시스템의 더 흥미롭고 때로는 다소 혼란스러운 측면 중 하나가 바로 일반적으로 최종 일관성이라고 하는 개념입니다. 간단히 말해서 데이터를 저장하거나 수정하는 PUT과 같은 S3 API 함수를 호출한 후에는 데이터가 허용되고 안정적으로 저장되었지만 모든 GET 또는 LIST 요청에는 아직 표시되지 않는 약간의 시간 지연이 있습니다.
1) 어플리케이션이 PUT API 함수로 "1"이란 object를 넣고
2) GET API로 이 객체를 불러왔을 때 "1"이 반환된다.
3) 이때, 다시 한번 PUT API 함수로 "2"란 object를 넣어 수정한다면
4) GET API로 불렀을 때 "1"(이전 값) 또는 "2"(수정된 값)가 반환되는 현상 발생.
5) 그러나 일정 시간이 흐른 후에는 수정된 값 "2"가 일관적으로 반환된다.
[Re2020] S3가 더 넓은 범위의 Strong Consistency 모델을 제공합니다 (Feat. S3 업데이트 이모저모)
S3는 이렇게나 중요한 서비스이기 때문에, 기본적인 동작 특성을 명확히 이해할 필요가 있습니다. 그 중 가장 대표적인 것으로 Eventually Consistency라는 개념이 있습니다. 최종 일관성을 제공한다는 뜻인데요. 이는 쉽게 말해 "특정 요청에 대한 응답의 일관성을 언젠가는 보장하나, 보장할때까지 일정 시간이 소요될수도 있다"라는 뜻입니다. 일례로, 같은 Key로 된 Object를 덮어쓰기(Overwrite)하고 나서, 빠른 시간에 GET 요청을 했을때 최종 일관성이 보장되기 전까지 예전 데이터를 반환할 수도 있다는 뜻입니다.
이처럼 s3 상에서의 수정은 즉각 반영되었지만, 빠른 시간 내에 GET과 같은 요청을 했을 때 일정 시간동안 반영되지 않다가, 나중에 반영되는 것이 '최종 일관성'이다. 그래서 즉각적으로 일관성을 유지할 필요가 있는 어플리케이션의 경우, 즉시 일관성을 제공하는 S3Guard와 같은 것을 추가적으로 이용했다.
이번 업데이트로 객체(Object) 기준 PUTS(신규 또는 덮어쓰기 모두)와 DELETE 작업 모두 Strongly Consistency 모델을 제공하기 시작했습니다. 즉, 기존에는 덮어쓰기 후 읽기(GET) 요청시 일정 시간동안 이전 데이터를 읽어올 가능성도 있었으나, 앞으로는 덮어쓰기된 최신 데이터를 반환하게 됩니다. 삭제(DELETE) 후 읽기(GET) 요청 또한 기존에는 삭제 후에도 삭제 전 데이터를 읽어올 가능성이 있었으나, 앞으로는 그럴 가능성이 없는 셈입니다.
그렇다면 이런 변경사항이 적용되는 대상이 어떻게 될까요?
기존 버킷과 신규 버킷 모두 구분 없이 적용됩니다. 사용자가 별도로 무엇을 활성화할 필요도 없습니다. 모든 리전의 버킷에 즉시 적용됩니다. 만일 어플리케이션에서 즉시 일관성을 보장하기 위해 S3Guard와 같은 것을 사용중이었다면, 이제는 그런 추가 계층을 사용할 필요가 없어졌습니다.
이는 모든 기존 S3 객체는 물론 새 S3 객체에 적용되고 모든 리전에서 작동하며 추가 비용 없이 사용할 수 있습니다! 성능에 영향을 미치지 않으며, 원하는 경우 초당 수백 번 객체를 업데이트할 수 있고 전역 종속성이 없습니다.
CloudFront에서 업데이트된 Amazon S3 콘텐츠 푸시
Q. Amazon CloudFront를 사용하여 Amazon Simple Storage Service(Amazon S3)에 저장된 객체를 제공하고 있습니다. Amazon S3에서 객체를 업데이트했지만, CloudFront 배포는 여전히 이 파일의 이전 버전을 제공합니다. Amazon S3 콘텐츠가 CloudFront에서 업데이트되지 않는 이유는 무엇입니까?
A. 기본적으로 CloudFront는 24시간(86,400초의 기본 TTL) 동안 Amazon S3에서 응답을 캐시합니다. 24시간 이내에 Amazon S3 응답을 제공하는 엣지 로케이션에 요청이 도달하는 경우 CloudFront는 캐시된 응답을 사용합니다. 이는 Amazon S3에서 콘텐츠를 업데이트한 경우에도 발생합니다.
위 공식 문서에서 내가 겪는 이슈와 똑같은 현상을 언급하고 있었다.
CloudFront에서는 기본적으로 24시간 간격으로 캐싱을 하고 있기 때문에, 업데이트하고 24시간 이내에 GET 요청을 했을 때는 이미 캐시된 응답(이전 파일)을 제공할 수 있다는 이야기인 것 같았다.
CloudFront의 기본 Cache 수명은 24시간으로 설정되어 있기 때문에, 웹 서버(origin)에서 변경 사항이 발생해도 CloudFront 서버는 바로 반영하지 못한다.
그럼 캐시 설정을 커스터마이징 해주면 되지 않을까?
(만약 캐시 커스터마이징 설정하려면 이거 한번 읽어보기)
그러나 나의 경우엔 이미지를 수정했을 때마다 즉각적으로 반영해야하므로 캐시 커스터마이징 설정으로 해결할 수 없을 것 같았다.
즉, 이 현상을 해결하는 방법은 기본적으로 중간 서버 역할을 하는 CloudFront에 업데이트된 AmazonS3 콘텐츠를 푸시하는 것이었다.
이때 공식 문서에서 제시하는 방법은 2가지였다.
'캐시 무효화 또는 객체 버전 관리'
1. Amazon S3 객체 무효화
Amazon S3 객체를 무효화하여 CloudFront 배포의 캐시에서 해당 객체를 제거할 수 있습니다. 캐시에서 객체가 제거되면 다음 요청 시 Amazon S3에서 직접 객체를 검색합니다.유의 사항
(1) 응답을 다양화하도록 쿠키나 헤더를 사용하는 객체의 특정 버전은 무효화할 수 없습니다. 이 경우 CloudFront는 객체의 모든 버전을 무효화합니다.
(2) 각 AWS 계정에서는 매달 1,000건의 무료 무효화 경로가 허용됩니다. 매월 1,000건이 넘는 무효화 경로에 대한 요금은 Amazon CloudFront 요금의 무효화 요청을 참조하십시오.
2. 객체 버전 관리를 사용
콘텐츠를 자주 업데이트하는 경우 객체 버전 관리를 사용하여 CloudFront 배포의 캐시를 지우는 것이 모범 사례입니다.유의사항
(1) 새 버전의 객체를 오리진(Amazon S3)에 저장하면 이전 이름으로 계속 사용할 수 있는 이전 버전의 변경 사항을 되돌릴 수 있습니다. 하지만 여러 버전의 객체를 저장하면 스토리지 비용이 증가할 수 있습니다.
(2) 오리진에서 객체를 업데이트하지만 쿼리 문자열을 기준으로 캐싱하면 스토리지 비용을 절감할 수 있습니다. 하지만 롤백을 준비하려면 이전 객체 버전을 오프라인으로 유지하는 것이 좋습니다.
하지만 위에 서술된 것처럼 각기 장단점이 있어서, 상황에 맞는 방법을 택해야 할 듯하다.
여기서 나는 객체 버전 관리는 선택지에서 제외하고, 캐시 무효화에 대해 알아보기 시작했다.
(왜냐하면 회사에서 객체 버전 관리를 쓰지 않기 때문..)
객체 무효화는 CloudFront에서 캐시를 삭제하여 파일을 무효화하는 것을 말한다. 따라서 다음에 최종 사용자가 파일을 요청하면 CloudFront는 오리진으로 돌아가 파일의 최신 버전을 가져온다.
캐시 무효화 방법에 대해 더 알고싶다면 아래 링크 참고하기
[AWS] CloudFront Cache 삭제하기
[CloudFront] CloudFront 기본 사용법
CloudFront + S3 Bucket 콘텐츠 즉각 갱신을 위한 조치 (Create Invalidation)
Invalidating files
하지만 단순히 캐시 무효화를 하는 것은 다음과 같이 hotfix인 경우에 적합한 것 같았다.
??? : ” 정적 컨텐츠였는데 지금 이미지 하나를 급하게 최신화 해야합니다. 하지만 Cloudfront에 기존 TTL 값이 너무 높게 설정이 되어있습니다. 어떻게 방법이 없을까요? “
-> [Invalidation]을 사용한다면 캐시가 만료되기 전에 파일의 내용을 갱신할 수 있습니다. 이는 캐시 무효화 기능인데 CloudFront Edge 로케이션에 저장된 캐시를 삭제해줍니다.
(출처 : [AWS] CLOUDFRONT 캐시 적중률 늘리기 (CACHE HIT RATIO))
즉 평소에는 static 파일들을 추가할테니 aws에서 기본적값으로 제공하는 캐시 설정을 사용해도 문제가 없지만, hotfix와 같이 배포된 이미지 파일의 구문이 잘못되어 급히 변경해야 되는 상황 같은 경우, Invalidation 기능을 사용하는 것 같았다.
나의 경우엔 일회성이 아니라, 이미지를 수정했을 때마다 즉각적으로 반영해야했다.
그렇다고 수정할 때마다 일일이 캐시 무효화를 해줄 수도 없는 노릇이고...
뭔가 방법이 없을까?
AWS Lambda를 사용한 CloudFront 무효화 자동화 설정하기
매번 새로운 콘텐츠가 바뀔 때마다 AWS에 로그인하여 Invalidation을 실행하기에는 너무 레거시한 방법이라고 생각했다. 그리고 AWS Lambda에 S3 Bucket에 Object Created Trigger 이벤트를 등록하여 자동화를 만들 예정이다.
이거다!!!!!!!!!!!!!!!!!!!!!!!!
내가 고민한 것과 정확히 일치하는, 진짜 감사한 글을 발견했다.
바로 적용해볼까 했지만, 일단 이 방법을 쓰는 게 적합한지 검토를 한번 받아야할 것 같아서 (쫄보 신입) 아직 적용해보지 않았다.
만약 적용하게 되면 후기를 이어서 쓰도록 하겠다.