AWS SDK 이용해서 CloudFront 캐시 무효화하기 (feat. deprecated AmazonCloudFrontClient)

effiRin·2023년 2월 28일
0

AWS

목록 보기
3/5
post-thumbnail

CloudFront API 이용해서 캐시 무효화하기

캐시 무효화에는 여러 방법이 있다.

  1. cloudfront 콘솔에서 직접 캐시 무효화 만들기 (but, 자동화 X, 일일이 만들어야 함)
  2. cloudfront 람다 함수로 자동화하기 (마찬가지로 콘솔에서 설정)
  3. cloudfront API createValidation 메소드 이용해서 코드로 짜기

나는 세 번째 방법, AWS SDK에서 createInvalidation을 활용하여 캐시 무효화하는 방법을 택했다.

파일 무효화 - Amazon CloudFront
CreateInvalidation - Amazon CloudFront


코드는 아래를 참고했는데...

How to invalidate a file(to be refreshed) served from Cloudfront CDN via Java AWS SDK?

import com.amazonaws.services.cloudfront;
import com.amazonaws.services.cloudfront.model.CreateInvalidationRequest;
import com.amazonaws.services.cloudfront.model.Paths;
import com.amazonaws.services.cloudfront.model.InvalidationBatch;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;

AWSCredentials awsCredentials = new DefaultAWSCredentialsProviderChain().getCredentials();
AmazonCloudFrontClient client = new AmazonCloudFrontClient(awsCredentials);

Paths invalidation_paths = new Paths().withItems("/path/to/invalidate/foo.jpg", "/path/file2.txt").withQuantity(2);
InvalidationBatch invalidation_batch = new InvalidationBatch(invalidation_paths, "unique_id_like_a_date");
CreateInvalidationRequest invalidation = new CreateInvalidationRequest("distributionID", invalidation_batch);
CreateInvalidationResult ret = client.createInvalidation(invalidation);

8년 전 글이다보니...
아래와 같이 AmazonCloudFrontClient에 deprecated 문제가 발생


스택 오버 플로우 찾아보니까 @Deprecated가 붙지 않은 동일한 이름의 다른 패키지가 있다며 공식 문서를 찾아보라는데...

What class should I use instead of the deprecated AmazonCloudFrontClient?

그치만 찾지 못 하고... 도대체 어디 있는겨?!?! 하다가 이상함을 느낌
com.amazonaws.services.cloudfront.AmazonCloudFrontClient
이 패키지의 클래스를 사용하라고 하는데 지금은 이게 deprecated 됨.

보니까 이것도 6년 전 글임...



그렇게 한참 삽질하다가...
다시 돌아와서 인텔리제이에서 안내글을 보니...
'AmazonCloudFrontClient 생성자가 deprecated 되었다'는 글을 발견.

하지만 여기서 더 중요한 것은...!!!

AmazonCloudFrontClient()
Deprecated.
use AmazonCloudFrontClientBuilder.withCredentials()

'AmazonCloudFrontClientBuilder를 사용하라'는 글이었다!!!

그래서 공식 문서를 봤다.

Fluent builder for AmazonCloudFront. Use of the builder is preferred over using constructors of the client class.
→ AmazonCloudFront의 Fluent 빌더. 클라이언트 클래스의 생성자를 사용하는 것보다 빌더를 사용하는 것이 좋습니다.
AmazonCloudFrontClientBuilder (AWS SDK for Java - 1.12.416)

오호 내가 사용한 거는 AmazonCloudFront 클래스의 생성자인데,
공식 문서에선 빌더(AmazonCloudFrontClientBuilder)를 사용하라고 권장하고 있다.


그리고 AmazonCloudFrontClient의 공식 문서를 보니까...
AmazonCloudFrontClient (AWS SDK for Java - 1.12.416)

생성자들이 deprecated 되었다며
AmazonCloudFrontClientBuilder.defaultClient() 를 사용하라고 한다.


즉, 생성자를 통해서 AmazonCloudFront의 인스턴스를 만드는 건 안되고,
builder를 통해서 만드는 건 된다는 이야기... ?!!!




standard()와 defaultClient()의 차이


그렇다면 코드를 짜서 테스트해보자...
AmazonCloudFrontClientBuilder의 메소드를 보니까... 두 가지가 있다.

standard()는 기본적인 세팅으로 Builder 인스턴스를 만들어주는 메소드고,

defaultClient()는 DefaultAWSCredentialsProviderChain과 DefaultAwsRegionProviderChain을 이용해서 AmazonCloudFront 인스턴스를 만들어주는 메소드라고 한다.



그래서 또 찾아본다.
standard와 default의 차이가 무엇인지...


우선 defaultClient()가 이용한다는 ProviderChain에 대한 설명을 찾아보았다.

DefaultAWSCredentialsProviderChain (AWS SDK for Java - 1.12.416)
DefaultAwsRegionProviderChain (AWS SDK for Java - 2.20.13)

  • DefaultAWSCredentialsProviderChain

다음 순서로 자격 증명을 찾는 AWS 자격 증명 공급자 체인:

1. 환경 변수 - AWS_ACCESS_KEY_ID및 AWS_SECRET_ACCESS_KEY (.NET을 제외한 모든 AWS SDK 및 CLI에서 인식하므로 권장됨) 또는 AWS_ACCESS_KEY및 AWS_SECRET_KEY(Java SDK에서만 인식됨)
2. Java 시스템 속성 - aws.accessKeyId 및 aws.secretKey
3. 환경 또는 컨테이너의 웹 ID 토큰 자격 증명
4. 모든 AWS SDK 및 AWS CLI에서 공유하는 기본 위치(~/.aws/credentials)의 자격 증명 프로필 파일
5. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" 환경 변수가 설정되고 보안 관리자가 변수에 액세스할 수 있는 권한이 있는 경우 Amazon EC2 컨테이너 서비스를 통해 제공되는 자격 증명,
6. Amazon EC2 메타데이터 서비스를 통해 전달되는 인스턴스 프로필 자격 증명

  • DefaultAWSRegionProviderChain

다음 순서로 리전을 찾는 AWS 리전 공급자:
1. 리전에 대한 'aws.region' 시스템 속성을 확인
2. 지역에 대한 'AWS_REGION' 환경 변수를 확인
3. 리전에 대한 {user.home}/.aws/credentials 및 {user.home}/.aws/config 파일을 확인
4. EC2에서 실행 중인 경우 해당 지역의 EC2 메타데이터 서비스를 확인


보니까 defaultClient()는 region이나 credential을 따로 설정하지 않아도
저 순서에 걸쳐서 알아서 찾아주는 메소드 이고,

standard()는 아래의 공식 문서에서 볼 수 있듯이
builder를 통해서 credential이나 region을 명시적으로 설정할 수 있는 메소드 같았다.




defaultClient() 테스트와 디버깅

그래서 일단 defaultClient()를 테스트하기로 했다.
위처럼 코드를 짜고 돌려보니... 캐시 무효화가 아주 잘 된다.

디버깅을 해서 내부 코드가 어떻게 돌아가는지 확인해보았다.



중간중간 캡쳐를 생략했지만 디버깅을 통해 알아낸 것은

1) defaultClient()도 결국 내부적으로는 standard()를 통해서 build를 해주는데
2) region(지역), credential(access key, secret key) 등의 설정값을 알아서 찾아서 세팅해준다.
(나의 경우엔 컴퓨터에 aws config 설정이 되어있어서 저렇게 conflgFile을 읽어서 설정을 가져오는 듯)



오 완전 편하잖아~~ DefaultClient() 쓰면 되겠네 ~~
라고 짧게 생각하고... 코드 짜다가 든 의문...

만약 aws config 설정이 안된 컴퓨터에서 돌리면...?



standard()로 설정을 해주는 이유

credentials 파일에 있는 설정되어 있는 인증 정보를 이용하는 것은 비단 AWS CLI뿐만이 아닙니다. 엄밀히 말하면 해당 머신에서 돌아가는 모든 앱들이 이 인증 정보를 이용하여 AWS SDK를 통해 AWS 서비스들과 연동할 수 있습니다. 환경 변수나 소스 코드 등의 명시적인 방법을 통해 인증 정보 설정해주지 않았다면 디폴트로 credentials 파일의 인증 정보가 사용되기 때문입니다.

특히, AWS 연동 기능을 가지고 있는 오픈 소스 소프트웨어들은 우선적으로 credentials 파일의 디폴트 프로파일에 설정된 인증 정보를 사용하는 경우가 대부분이기 때문에 아무 설정없이도 본인 AWS 계정으로 인증되어 작동되는 경우가 많습니다.
https://www.daleseo.com/aws-cli-configure/

즉, 혼자서 만드는 프로젝트면 편하게 defaultClient()를 써도 되지만
다른 사람도 함께하는 프로젝트라면,
그 사람의 컴퓨터가 어떤 이유로 (aws config가 안 되어있다던지 등) 설정값을 못 찾는다면,
문제가 발생할 수밖에 없었다.



무엇보다 나는 clientFront 인스턴스를 만드는 메소드에 @PostConstruct를 써서
어플리케이션 run과 동시에
해당 메소드가 있는 클래스의 bean을 만들고 의존성 주입하도록 하려고 했는데...
(참고 : [Spring] @PostConstruct란?(정의와 장점))

만약 설정값이 없다?
그러면 creating bean 에러가 터져서 어플리케이션 자체가 안 돌아가게 된다.


그래서 결국 defaultClient()는 쓰지 않고,
standard()를 이용해 명시적으로 설정값을 주기로 했다.




standard()로 설정해서 CloudFrontClient 만들기


그리하여 standard()로 어떻게 설정하는지 찾게 되는데...
cloudFrontClient는 관련된 자료와 코드가 별로 없었다.

그러다가 s3Client 와 관련된 코드를 보았는데...
standard()를 쓰면서 설정값이 똑같았다. (region, accessKey, secretKey 등)

AmazonS3Client 생성자 대신해서 쓰기

똑같이 주면 되지 않을까??? 해서 위 글을 참고해서 코드를 작성했다.
그리고 돌려보니까 아주 잘 된다.


참고로 region은 지역이라서 ap-northeast-2 (아시아 태평양(서울)) 을 주고
(참고 : 리전 및 영역 - Amazon Elastic Compute Cloud)

credential은 아래 글을 참고해서 BasicAWSCredentials로 직접 설정을 주었다.



또한 accessKey, secretKey는 유출되면 해당 s3, cloudFront 등을 맘대로 접근할 수 있으니 보안에 굉장히 주의를 기할 것




그렇게 standard로 설정값을 주고, createValidation 메소드를 완성했다.

fun createInvalidation(invalidationList: List<String>) {
    val invalidationPaths = Paths().withItems(invalidationList).withQuantity(invalidationList.size)
    val invalidationBatch = InvalidationBatch(invalidationPaths, tempTimestamp())
    val createInvalidationRequest = CreateInvalidationRequest(distributionId, invalidationBatch)

    val result = cloudFrontClient.createInvalidation(createInvalidationRequest)

        println(result.toString())

참고로 InvalidationBatch에서 tempTimeStamp()는 timeStamp를 만들어주는 함수다.

callerReference라는 값인데 무효화 요청을 고유하게 인식하기 위해 주는 값이라고 한다.
새로운 무효화 요청을 만들 때마다 CallerReference는 새로운 값이어야만 한다.

그래서 공식 문서에서는 무효화 요청을 할 때마다 고유한 값을 줄 수 있는 timeStamp를 callerReference 값으로 주는 게 좋다고 권장하고 있다.

A value that you specify to uniquely identify an invalidation request. CloudFront uses the value to prevent you from accidentally resubmitting an identical request. Whenever you create a new invalidation request, you must specify a new value for CallerReference and change other values in the request as applicable. One way to ensure that the value of CallerReference is unique is to use a timestamp, for example, 20120301090000.
InvalidationBatch - Amazon CloudFront



사용자에게 createInvalidation 권한이 없다고 나오는 에러

AccessDenied: User is not authorized to perform: cloudfront:CreateInvalidation on resource
because no identity-based policy allows the cloudfront:CreateInvalidation action

만약 사용자에게 createInvalidation 권한이 없다면 이런 에러가 발생할 수 있다.

그럴 땐 createInvalidaion 권한을 주면 된다.
아래 글을 참고!!!

https://velog.io/@imhjnoh/AccessDenied-User-is-not-authorized-to-perform-cloudfrontCreateInvalidation-on-resource



요약

  1. CloudFrontClient 생성자는 deprecated 되었다. 그러니까 CloudFrontClientBuilder를 사용하자.
  2. CloudFrontClientBuilder는 defaultClient(), standard()가 있는데 defaultClient()는 설정값을 알아서 가져오고, standard()는 명시적으로 설정값을 줄 수 있다.
  3. 하지만 설정값을 못 가져오는 경우 defaultClient()는 에러를 발생시킬 수 있으니 standard()로 명시적으로 설정값 주는 것을 추천
  4. 참고로 accessKey, secretKey는 유출되면 해당 s3, cloudFront 등을 맘대로 접근할 수 있으니 보안에 굉장히 주의할 것
  5. 또한 InvalidationBatch에서 callerReference 값은 timeStamp로 고유하게 줄 것을 권장


profile
모종삽에서 포크레인까지

0개의 댓글