• 람다를 이용한 이미지 리사이징 - 2 :: 마이구미
    AWS 2020. 8. 17. 15:03
    반응형
    이 글은 Lambda@Edge 를 활용한 이미지 리사이징을 다룬다.
    이전에 Lambda 를 활용한 이미지 리사이징을 다룬적이 있다.
    두 가지 방식을 비교하는 것을 중점으로 작성되었다.
    람다를 이용한 이미지 리사이징 1편 - https://mygumi.tistory.com/349
    Lambda@Edge 이미지 리사이징 예제
    실서비스 적용 사례

     

    1편의 내용을 간략히 정리하자면 다음과 같다.

    S3 는 Events 감지를 통해 이미지 업로드를 탐지한 후, 리사이징을 하는 Lambda 함수를 호출하게 된다.

    이로 인해, 원본 이미지를 업로드를 하면, 리사이징된 이미지들이 S3 에 자동으로 생성되게 된다.

    클라이언트에서는 생성된 리사이징된 이미지들을 제공받을 수 있게 된다.

    하지만 이 방식에는 단점들이 존재한다.

     

    원본 이미지 업로드 시 생성되는 리사이징 이미지 사이즈 형태가 5개라면?

     

    1개의 이미지 업로드 시 총 6개의 이미지가 저장된다.

    1000개의 이미지 업로드 시 6000개의 이미지가 저장된다.

    저장되어있는 이미지들은 사용되지 않더라도, 무조건 생성되기 때문에 불필요한 리소스들도 낭비되고 있을 수 있다.

     

    리사이징 이미지가 생성되는 과정에 요청이 들어오면?

     

    실제로 리사이징이 되고 있는 과정에 클라이언트 요청이 들어오면, 실제로는 없는 리소스이다.

    결과적으로, 원하는 응답을 내려줄 수 없다.

     

    width = 100, 200, 300, 400 이미지를 제공하고 있었는데, 500 을 추가하고 싶으면?

     

    이미 생성된 모든 원본 리소스를 대상으로 이를 요청해야한다.

    리소스 관리 측면에서도 효율적이지 못한 경우가 발생할 수 있다.

    결과적으로 이와 같은 흐름의 이슈들이 존재하게 된다.

     


     

    결과적으로 이 글에서 다루는 Lambda@Edge 를 활용한 방식은 위와 같은 이슈들을 해결할 수 있다.

    우선 Lambda@Edge 가 무엇인지를 확인해보자.

     

    Lambda@Edge 를 활용한 서비스는 다음과 같은 형태를 띈다.

     

    • https://{STATIC_DOMIAN}/images/image.jpg?d=100x100  
    • https://{STATIC_DOMIAN}/images/image.jpg?d=200x200
    • https://{STATIC_DOMIAN}/images/image.jpg?d=300x300

     

    URL 의 파라미터로 이미지 사이즈를 명시하여 실시간으로 이미지 리사이징을 제공할 수 있다.

    Lambda@Edge 는 실제로 aws 서비스 단위에서 분류되는 것이 아니라서, 서비스 단위에서 찾을 수 없다.

    이것은 CloudFront 에서 제공하는 기능 중 하나이다.

    CDN 에 의해 발생되는 이벤트를 통해 Lambda 함수를 호출하게 된다.

    그림으로 나타내면 다음과 같다.

     

     

    CloudFront 의 이벤트는 Viewer request, Viewer response, Origin request, Origin response 로 분류된다.

    실제 ClouldFront 설정 페이지에서는 다음과 같은 화면에서 확인할 수 있다. (CloudFront => Edit Behavior)

     

     

    Viewer request

    CloudFront 는 사용자의 요청을 받는 시점에 함수는 실행된다.

    요청에 대한 캐시 여부를 확인하기 전이다.

     

    Origin request

    요청이 Origin 으로 향할 때 함수는 실행된다.

    캐시되어있다면, Origin 으로 향하지 않으니, 함수는 실행되지 않는다.

     

    Origin response

    Origin 에서 응답을 반환한 후에 함수는 실행된다.

    응답은 캐시되기 전이다.

     

    Viewer response

    사용자가 응답을 반환하기 전에 함수는 실행된다.

    이미 캐시된 상황이다.

     

    이러한 흐름을 기반으로 동작하고, 이를 이미지 리사이징에 활용할 수 있다.

     

    AWS 에서는 Viewer request 와 Origin response 이벤트를 활용하여 이미지 리사이징을 구현하는 예제를 제공한다.

    예제에서 제공하는 Lambda@Edge 의 흐름은 다음과 같다.

     

     

    각 이벤트에서는 무엇을 하는가? 예제를 기반으로 설명하면 다음과 같다.

     

    VIewer request 에서는 지원하고 있는 포맷이나 사이즈를 판단하고, 실제 존재하는 리소스 경로를 제공한다.

    예를 들어, 안드로이드 환경에서 images/image.jpg?d=200x200 로 요청이 들어왔다.

    함수에서는 사이즈는 200x200 이고, webp 지원을 명시하여 Origin 에게 보내는 형식을 만든다. (/images/200x200/webp/image.jpg)

    Origin response 에서는 리사이징 작업을 실행하고, 완료되면 응답을 반환한다.

     

    제공하는 예제의 과정을 간략히 다뤄본다.

    진행함에 있어서, 간략한 설명과 조금 다르게 진행한 부분 등을 설명한다.

    그렇기에, 실습을 위한 전체적인 예제 흐름은 원본 글을 참고하는 것이 좋다.

     

    현재 Lambda@Edge 는 us-east-1 에서만 제공하고 있기에, 해당 리전을 기반으로 작업해야한다.

    우선 Lambda 함수 배포를 위한 프로젝트의 디렉토리 구조는 다음과 같다.

     


    ├ dist
    ├ lambda
         ┬
         ├ viewer-request-function
              ┬ 
              ├ index.js
         ├ origin-response-function
              ┬ 
              ├ index.js
    ├ Dockerfile

     

    도커를 굳이 안써도 되지만, 실제 Lambda 와 같은 환경을 구축할 수 있기에 익숙하지 않더라도 시도해보길 추천한다.

    index.js 파일들은 제공하는 그대로 사용하면 된다.

     

    Dockerfile 은 조금 변경해서 사용할 것이다.

    OS 는 amazonlinux:1 가 아닌 amazonlinux:2 로 셋팅한다.

    Node.js 버전은 6.10 를 12.18.3 으로 셋팅한다.

     

    Dockerfile

    FROM amazonlinux:2
    
    WORKDIR /tmp
    
    # replace shell with bash so we can source files
    RUN rm /bin/sh && ln -s /bin/bash /bin/sh
    
    #install the dependencies
    RUN yum -y install gcc-c++ tar gzip findutils
    
    RUN touch ~/.bashrc && chmod +x ~/.bashrc
    
    RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.5/install.sh | bash
    
    RUN source ~/.bashrc && nvm install 12.18.3
    
    WORKDIR /build

     

    도커를 빌드한 후, 컨테이너 실행을 통한 컴파일 및 zip 까지 만들어준다.(예제 참고)

    이 과정은 Lambda 함수를 위한 셋팅이다.

     

    전체적인 Lambda@Edge 배포를 위해서 CloudFormation 서비스를 이용할 수 있다.

    CloudFormation 서비스는 전체 인프라를 쉽게 모델링하게 도와준다.

    예를 들어, 이 글에서 다루는 이미지 리사이징을 위한 환경을 구축하기 위해서는 관련 서비스들의 디테일한 환경 셋팅이 필요하다.

     

    • S3
    • CloudFront
    • Lambda
    • IAM

     

    이런 셋팅을 쉽게 한번에 구축할 수 있다.

    예제에서 제공하는 yaml 을 확인해보자.

     

    ImageBucket

    이미지가 저장되는 버킷 생성

     

    ViewerRequestFunction

    Viewer request 이벤트에 실행되는 함수

    노드 버전 12.18.3 을 위해서는 Runtime: 6.10 => Runtime: nodejs12.x 으로 변경 필요

     

    OriginResponseFunction

    Origin response 이벤트에 실행되는 함수

    노드 버전 12.18.3 을 위해서는 Runtime: 6.10 => Runtime: nodejs12.x 으로 변경 필요

     

    MyDistribution

    CloudFront 관련 설정

    LambdaFunctionAssociations 를 통해 이벤트에 따른 Lambda 함수 연결

     

    본인은 진행함에 있어, ViewerRequestFunction, OriginResponseFunction 에서 계속 오류가 발생했다.

     

    Properties:
         CodeUri: s3://<code-bucket>/origin-response-function.zip

     

    yaml 에 명시된 내용으로는 해당 함수들을 CodeUri 에 존재하는 zip 파일을 통해 함수를 생성하겠다는 것이다.

     

    즉, 위에서 생성된 zip 파일들을 특정 버킷에 존재하고 있어야한다.

    하지만 버킷을 계속 찾을 수 없는 이슈가 발생해서 string 이 형태가 아닌 다른 형태로 변경하였다. 

     

    CodeUri:
        Bucket: <code-bucket>
        Key: viewer-request-function.zip

     

     

    CloudFormation 이 제대로 동작했다면, 모든 셋팅은 구축된 상태이다.

    그렇지 않다면, CloudWatch 의 로그를 확인해보면서 풀어나가면 된다.

     

    기본 예제에서는 버킷은 PublicRead 로 설정되어있다.

    이 의미는 버킷의 주소를 통해 직접 리소스에 접근할 수 있다는 것이다.

    이를 위한 대안으로는 CloudFront 에 한해서만 권한을 부여할 수 있다.

    Security>Origin access identity 탭을 통해 Identity 를 생성하여 CloudFront 에 설정할 수 있다.

     

     

    그리고 S3 정책에 Origin Access Identity 를 부여하면 된다.

     

    {
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {ID}"
        },
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::{BUCKET}/*"
    },
    {
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity {ID}"
        },
        "Action": "s3:ListBucket",
        "Resource": "arn:aws:s3:::{BUCKET}"
    },

     


     

     

    처음에 언급했던 방식의 이슈들을 어떻게 해결했는가?

     

    - 불필요할 수 있는 이미지 파일 생성

    기존 Lambda 방식은 1개의 이미지 업로드는 미리 여러 리사이징된 이미지들을 자동으로 생성하게 된다.

    Lambda@Edge 방식은 해당 요청을 받아야지만 관련 리사이징 이미지를 생성한다.

    즉, 실시간으로 판단하여 동작하게 된다.

    이로인해, 아예 리사이징된 이미지가 없는 경우에는 리사이징이 완료되서 반환되는 시점까지 지연이 발생할 수 있다.

    그 이후에는 모든 사용자는 CDN 에 캐시된 리소스를 사용하게 된다.

     

    - 리사이징 과정에서 같은 요청이 들어오는 경우

    기존 Lambda 방식은 리사이징 이미지가 생성되는 과정에 요청이 들어오면 제대로 된 응답을 할 수 없는 경우가 발생한다.

    근본적인 이유는 클라이언트 요청이 직접 Origin 에 접근하기에, 이러한 문제가 발생할 수 있다.

    하지만 Lambda@Edge 방식은 CDN 이라는 매체가 중간자 역할을 하고 있다.

    즉, 요청에 대한 응답은 Origin 에서 응답을 CDN 에게 반환해야 클라이언트에 응답을 반환하기에, 이러한 이슈는 발생하지 않는다.

     

    - 스펙 변경으로 인한 관리

    미리 생성해놓는 방식이 아닌 실시간으로 동작하는 방식이기에, 이는 유동적으로 대응할 수 있게 된다.

     


     

    또 다른 비슷한 대안으로 ec2 에 thumbor 와 같은 서비스를 구축하여 관리할 수 있다.

    이 방식은 의미 그대로, 서버를 별도로 구축해서 사용하는 장점이자 단점이 있다.

    추후에 다루어 보겠다.

     

    사실 본인은 보고 들은 것으로 접근하여, 기본적인 흐름을 다루는게 전부이다...

    그러니 최상단에 직접 실서비스에 활용된 사례의 글도 꼭 참고하길 바란다.

    반응형

    댓글

Designed by Tistory.