Tempo Di Valse

[AWS] S3 에서 Lambda 를 통해 Cloudfront 바로 배포하기 본문

개발/AWS

[AWS] S3 에서 Lambda 를 통해 Cloudfront 바로 배포하기

TempoDiValse 2022. 7. 28. 12:47

특정 S3 버킷에 파일이나 폴더를 업로드 하게되면 자동으로 배포를 할 수 있도록 하는 로직이라고 할 수 있다.

 

0. S3 생성

 

S3 버킷을 콘솔에서 잘 만들어 준다. 단, 액세스를 퍼블릭으로 해줘야 한다는 것을 기억하자.

 

1. Lambda 생성

함수명을 입력하고, 나 같은 경우에는 Node.js 환경에서 Lambda 로직을 작성했다. Python 도 가능하기 때문에 각자 편한 언어를 선택하여 사용하면 될 것 같다. 그리고 실행 역할에 대하서는 일단 "기본 Lambda 권한을 가진 새 역할" 로 세팅을 해주고 생성을 하고, 생성 후에 IAM 창을 통해 변경하도록 한다.

 

2. Lambda 설정

TEST 라는 이름으로 Lambda 를 만들었다. 함수 개요에는 아무런 셋팅을 하지 않은 초기 상태이다. 여기에서 S3 에서 이벤트가 발생된 후에 TEST Lambda 를 실행 시키기 위해 "트리거 추가" 를 통해 이벤트를 연결한다. 트리거는 S3 를 검색하여 나오는 항목으로 한다.

선택을 하게되면 다음의 항목에 대해 입력을 해주어야 한다.

  • 버킷명
  • 어떤 이벤트에 반응을 할 지
  • Prefix (버킷 내에 특정 폴더에 타겟을 주고싶거나 특정 이름으로 시작되는 파일에 대한 조건이다)
  • Suffix (버킷에 들어가는 파일의 확장자를 특정하거나 특정 이름으로 끝나는 파일에 대한 조건이다)
  • Recursive Invocation (체크하고 넘어간다)

여기에서 어떤 이벤트에 반응을 하는 지를 정해주어야 하는데, 기본값으로는 All object create events 라고 설정이 되어있다. 기본 값이 아닌 특정 이벤트를 받고싶다면 기본값이 아닌, PUT / POST / COPY / Multipart 중에 하나를 선택하면 될 것이다. 일단 기본 값으로 설정하고 넘어가도록 한다.

 

3. Lambda 소스 작성

 

트리거 추가를 완료하게 되면 다시 Lambda 의 함수개요 화면으로 돌아오게 된다. 다음으로 '대상 추가' 에 대해 세팅을 해야 하는가 생각할 수 있겠지만, 대상 추가의 값은 Lambda 의 작업이 끝나고서의 결과값을 받고자 할 때 사용을 하기 때문에 현재에는 할 필요가 없다. 그렇다면 바로 하단에 소스를 작성하도록 한다.

 

소스의 기본은 다음과 같이 준비되어 있을 것이다. 

export.handler = async (event) => {

}

다른어로 따지면 Lambda 에서는 handler 가 main 함수라고 생각하면 된다. 변경이 필요하다면 '런타임 설정' 에서 변경이 가능하다.

여기에 AWS SDK 를 통해서 CloudFront API 를 사용해야 한다. 기본적으로 Node.js 안에 SDK 가 들어가 있기 때문에 npm 으로 패키지 설치에 대해 헤매지 않아도 된다. 그래서,

const AWS = require('aws-sdk')
const cf = new AWS.CloudFront()

exports.handler = async (event) => {

}

이런식으로 API 를 import 시켜주도록 하고, handler 내부에 로직을 작성해주면 된다.

 

handler 에 전달되는 event 파라미터를 통해서 S3 가 특정 이벤트가 발생될 때의 데이터를 전달 받는데, 다음과 같은 형태가 담겨져 있다.

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "ap-northeast-2",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "test",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::test"
        },
        "object": {
          "key": "test_file.png",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

다른 것들은 잘 모르겠지만, s3.object 안에 있는 정보들이 제일 쓸모 있는 데이터라고 할 수 있다. 이 데이터를 가공하게 되면, CloudFront 에서 사용할 수 있는 애들로 탄생을 시킬 수가 있다.

 

event 파라미터의 데이터에서 필요한 것들을 추출/가공 한 다음에는 CloudFront 배포를 위해 전송해줘야 하는 옵션값들을 정의해 주어야 하는데, 이 부분이 여간 어려운 것이 아니다. 일단 공식 문서는 다음 홈페이지를 참조하도록 하자

 

Class: AWS.CloudFront — AWS SDK for JavaScript

The modular AWS SDK for JavaScript (v3), the latest major version of AWS SDK for JavaScript, is now stable and recommended for general use. For more information, see the Migration Guide and API Reference. Class: AWS.CloudFront Inherits: AWS.Service Object

docs.aws.amazon.com

배포를 위해서는 CloudFront API 안에 있는 createDistribution 이라는 메소드를 사용해야 하는데, 메소드의 들어가는 옵션 파라미터의 항목들이 모니터 한페이지에 담을 수 없을 만큼 많다. 그 중에서 정말 기본 세팅으로만 설정해보도록 한다면 다음 항목만 사용해 보도록 한다.

const param = {
    DistributionConfig: {
        CallerReference: `R${time}`, // 임의 값을 만들어준다. 
        DefaultCacheBehavior: {
            TargetOriginId: originId, // 식별하는 ID 값으로, 해당 값은 Origins.Items.Id 값과 같아야 한다.
            ViewerProtocolPolicy: 'https-only',
            Compress: true, // 웹페이지의 압축여부를 설정한다.
            MinTTL: 3600,
            ForwardedValues: {
                Cookies: { Forward: "all" },
                QueryString: false,
                Headers: { Quantity: 0 },
                QueryStringCacheKeys: { Quantity: 0 }
            }
        },
        Origins: {
            Items: [{
                DomainName: `${bucketName}.s3.${region}.amazonaws.com`, // 버킷명이 들어간 S3 URL을 입력한다. 호스트는 뺀다.
                Id: originId, // 해당 아이템을 식별하는 ID
                OriginShield: {
                    Enabled: true,
                    OriginShieldRegion: 'ap-northeast-2'
                },
                S3OriginConfig: {
                    OriginAccessIdentity: ''
                }
            }],
            Quantity: 1
        },
        Comment: 'Test Send', // 비고
        Enabled: true, // 작동 여부
    }
}

기본으로 들어가는 항목들만 해도 엄청 많다는 것을 알 수 있다. 여기에서 나머지는 잘 모르겠고, 주석 달아놓은 부분만 일단은 파악해놓은 상태이다. 자세한 설명은 위의 API 웹 페이지에 작성이 되어있으니 참고하도록 한다.

 

마지막으로 메소드 실행이 남았는데,

const p = cf.createDistribution(param, async (err, data) => {
    if(err) {
        console.log(err)

        return
    } 

    return data;
}).promise();

return p

 

 

데이터를 받아오기 위해서 Promise 형을 리턴시켜주면 완료가 된다. 그럼 전체 소스 형태를 한번 보도록 한다. 일부는 각자 스타일에 맞게 짜면 되기 때문에 형태만 보도록 하자.

const AWS = require('aws-sdk')
const cf = new AWS.CloudFront()

exports.handler = async (event) => {
    /* ... 알아서 짜는 부분 ... */
    
    const originId = `S3-${bucketName}`
    const time = new Date().getTime();

    const param = {
        DistributionConfig: {
            CallerReference: `R${time}`,
            DefaultCacheBehavior: {
                TargetOriginId: originId,
                ViewerProtocolPolicy: 'https-only',
                Compress: true,
                MinTTL: 3600,
                ForwardedValues: {
                    Cookies: { Forward: "all" },
                    QueryString: false,
                    Headers: { Quantity: 0 },
                    QueryStringCacheKeys: { Quantity: 0 }
                }
            },
            Origins: {
                Items: [{
                    DomainName: `${bucketName}.s3.${region}.amazonaws.com`,
                    Id: originId,
                    OriginShield: {
                        Enabled: true,
                        OriginShieldRegion: 'ap-northeast-2'
                    },
                    S3OriginConfig: {
                        OriginAccessIdentity: ''
                    }
                }],
                Quantity: 1
            },
            Comment: 'Test Send',
            Enabled: true,
        }
    }
    
    const p = cf.createDistribution(param, async (err, data) => {
        if(err) {
            console.log(err)
            
            return
        } 
        
        return data;
    }).promise();
    
    return p
};

 

4. IAM 설정

 

Lambda 소스를 다 만들었다 해서 완료되는 것이 아니다. 왜냐하면 Lambda 가 가지고 있는 권한에 CloudFront 관련 권한을 추가하지 않았기 때문이다. 테스트나 실제 실행을 하게되면 바로 에러가 날 것이다. 그래서 IAM 을 이동하여 권한을 추가하도록 한다.

 

우선, Lambda 와 물려있는 권한을 IAM 콘솔에서 찾은 후에 권한을 확인해보면,

Lambda 에서 기본값으로 추가해준 권한 정책만 있다. 그래서 여기에 CloudFront 권한을 찾아 추가할 것이다. 

 

권한 정책 화면에서 오른쪽에 있는 권한추가 > 정책연결 을 누르게 되면 무수히 많은 AWS 의 정책 리스트가 나오는데 여기에서 CloudFront 를 검색하고 나오는 CloudFrontFullAccess 정책을 찾아서 추가를 해준다.

이렇게 되었으면 완료!

 

5. 실행

 

이제 실제로 S3 에 업로드를 하게되면 CloudFront 에 리스트에 뜨는 것을 확인해 보도록 한다.

반응형
Comments