왜 시작인 docker container 라고 생각하는 사람이 있을 것 같다. 결론부터 말하자면 파이썬 외부 라이브러리를 이용하기 위해서 이다. requests, pillow 같은 라이브러리는 python 설치시 같이 포함되는 기본 라이브러리가 아니다. 이러한 라이브러를 이용하기 위한 가장 정석적인 방법이 docker를 이용하는 방법이다.  AWS Lambda 가 사용하는 docker image 를 개발 PC에서 실행시켜 해당 환경을 docker container에서 동작시켜 파이썬 라이브러리를 특정 경로에 설치하고 이를 zip 로 만들어서 aws lambda 에 업로드 하는 방법이다. 
 (이것외에 남이 만든 zip 파일을 이용하는 방법도 있는 것 같다. 그런 것을 원한다면 https://github.com/keithrozario/Klayers?tab=readme-ov-file  에서 찾아 보길 바란다. 다만 내가 원하는 라이브러리가 존재하지 않을 수도 있다. ) 

딱히 docker  명령어에 대해 자세히 설명하지 않겠다. 이번에 처음으로 docker를 실무에 이용해봤지 나는 docker 사용에 익숙하지 못하다. 따라서 내가 docker에 대해 설명하는 것은 틀릴 수 있다. 그리고 docker 는 설치되어 있다고 가정하고 진행하겠다. 


 AWS Lambda가 사용하는  docker 이미지에 대한 정보는 https://gallery.ecr.aws/lambda/python  에서 찾을 수 있다. 이렇게 만들어진 docker 이미지는 AWS 에서만든 linux 배포판을  기반으로 만들어졌다. 여기에 대한 정보는 https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-image.html  에서 확인 할 수 있다. 내가 사용하려는 python3.11 은 Amazon Linux 2 을 기반으로 한다. python3.12 부터는 Amazon Linux 2023 을 기반으로 한다. 
Amazon Linux 2 은 redhat 을 기반으로 한 것 같다. (yum 을 이용해서 패키지를 설치한다. yum 은 ubuntu 에서의 apt 같은 것이다.) 

내 경우는 python3.11 이기 때문에  public.ecr.aws/lambda/python:3.11  을 다운받았다. 아래 과정은 docker 이미지를 설치컨테이너를 실행하는 명령어 이다. 

AWS Lambda 에 맞는  docker container 다운로드 및 설정

# 다운로드
docker pull public.ecr.aws/lambda/python:3.11

# 다운로드 확인
docker images

# 컨테이너 생성
# 시작 command 를 /bin/bash 로 변경하면서 생성한다. 
docker create -i  --name aws_py_311  public.ecr.aws/lambda/python:3.11 /bin/bash

# 컨테이너 실행
docker start aws_py_311

# 컨테이너 접속
docker exec -it aws_py_311 /bin/bash

# 컨테이너 빠져나오기
exit

위에서 시작 command 를 변경하는 것이 있는데, 이것이 좀 중요하다. 기본적으로 생성되는 docker container 가 파이썬 특정 경로의 lambda_handler 를 실행하는 것으로 설정되어 있다. 이것을 변경하지 않고 docker conainer 를 실행하면 그냥 에러를 내면서 종료되어 버린다. 


docker container 에 필요 파이썬 라이브러리 설치

이제 aws lambda 를 개발할 때 필요한 파이썬 라이브러리를 설치해보겠다. 그런데 이 파이썬 라이브러리를 특정 경로에 설치해야 한다. 그리고 이 파일들을 zip 파일로 만들어야 한다. 

cd ~
mkdir python
cd python

# /root/python 디렉토리에 파이썬 라이브러리 설치
pip3.11 install requests -t .
pip3.11 install boto3 -t .

# pillow-simd 가 아닌 pillow 를 설치하려면
pip3.11 install pillow -t .


# zip 프로그램 설치
yum install zip

# python.zip 으로 압축
cd ..
zip -r python.zip python/


컨테이너 안의 파일을 다운받기 위해서는 container shell 이 아닌 내 개발 PC의 shell 에서 

docker cp aws_py_311:/root/python.zip .

하면 된다. 

pillow-simd 를 설치하는 경우

pillow-simd 를 이용하는 경우 좀 복잡하다. 이 경우 컴파일이 필요하다. 그리고 추가적인 리눅스 패키지를 다운받아야 한다. 그리고 설치한 리눅스 패키지 때문에 shared library(so 파일, 윈도우로 치면 dll 같은 것)을 같이 zip 에 포함해야 한다. 
내 개인적인 경험으로 pillow 보다 pillow-simd 를 사용하면 40%는 빨라지는 것 같다.  pillow-simd 는 인텔 CPU의 avx2 명령어를 사용한다. 따라서 x86 전용이다. https://github.com/uploadcare/pillow-simd/issues/43  이 이슈가 open 상태인 것으로 보아 arm 은 지원하지 않는것 같다. 

내가 오늘 기준으로 컴파일 해봤을 때 pillow-simd==v8.4.0.post0 버전만 사용이 가능했다. 어째든 다음 과정을 거치면 된다. 

# pillow-simd 컴파일 준비
yum install -y \
    freetype-devel \
    gcc \
    ghostscript \
    lcms2-devel \
    libffi-devel \
    libimagequant-devel \
    libjpeg-devel \
    libraqm-devel \
    libtiff-devel \
    libwebp-devel \
    make \
    openjpeg2-devel \
    rh-python311 \
    rh-python311-python-virtualenv \
    sudo \
    tcl-devel \
    tk-devel \
    tkinter \
    which \
    xorg-x11-server-Xvfb \
    zlib-devel \
    && yum clean all
    
# 디렉토리에 파이썬 라이브러리 설치
cd ~
mkdir python
cd python

# pillow-simd 컴파일, 최신 버전은 에러가 발생했음
CC="cc -mavx2"  pip install -U --force-reinstall   -t . --compile pillow-simd==v8.4.0.post0 

# shared library 한 곳에 모우기
# /root/python/lib에 필요한 파일을 모운다.
mkdir lib
cp /usr/lib64/libtiff.so.5 lib/libtiff.so.5
cp /usr/lib64/libjpeg.so.62 lib/libjpeg.so.62
cp /usr/lib64/libjbig.so.2.0 lib/libjbig.so.2.0
cp /usr/lib64/libopenjp2.so.7 lib/libopenjp2.so.7
cp /usr/lib64/libxcb.so.1 lib/libxcb.so.1
cp /usr/lib64/liblcms2.so.2 lib/liblcms2.so.2
cp /usr/lib64/libpng15.so.15 lib/libpng15.so.15
cp /usr/lib64/libwebp.so.4 lib/libwebp.so.4
cp /usr/lib64/libwebpmux.so.0 lib/libwebpmux.so.0


# zip 프로그램 설치
yum install zip

# python.zip 으로 압축
cd ..
zip -r python.zip python/

python 버전이 다르거나 다른 pillow-simd 버전을 사용하는 경우 다른 so 파일로 변경될 수 있다. 이 경우 
AWS Lambda로 thumbnail 만들기(3) - 트러블 슈팅  
를 참고해서 어떤 파일의 so 파일이 필요한지 확인하기 바란다. 

이 경우도 동일하게  아래처럼 python.zip 파일을 내 개발 PC로 옮길 수 있다. 

docker cp aws_py_311:/root/python.zip .

 

다음에는 AWS lambda 를 설정하는 방법을 설명하겠다. 

 며칠전 https://yiunsr.tistory.com/922 이런 사고도 있기는 했지만 aws lambda python 으로 thumnail 만드는 것이 어느 정도 끝났다. 언제나 그렇지만 내가 하려는 것은 꼭 인터넷에 그대로 있지는 않았다. 있는 것을 여러개 조합해서 해야하고 기존 방법이 변경사항도 꽤 있었다. 보통 이런것은 정리하지 않는 편인데, 이건 내가봐도 너무 일을 잘 처리한 것 같아서 남기고 싶었다.  정리해서 글을 올린다. 누군가에게는 도움이 될길 바란다. 

 참고한 글 
* AWS Lambda Image Resize 도입기(올리브영 테크블로그) https://oliveyoung.tech/2023-05-19/aws-lambda-resize/   
    - 기본 개념을 배우기는 좋은 글이다.
* 자습서: Amazon S3 트리거를 사용하여 썸네일 이미지 생성 https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-tutorial.html  
    - 이것도 기본 개념을 배우기 좋은 글이다. 
* Creating faster AWS Lambda functions with AVX2  https://aws.amazon.com/ko/blogs/compute/creating-faster-aws-lambda-functions-with-avx2/
    - 나처럼 속도를 개선하고 싶어서 pillow-simd 라이브러리를 사용하는 사람들에게 유용한 글이다. 다만 좀 오래되어서 현재 사항에 안맞는 사항이 있다. 

 이 내용은 
1. AWS Lambda로 thumbnail 만들기(1) - docker container 사용하기 (https://yiunsr.tistory.com/924)
2. AWS Lambda로 thumbnail 만들기(2) - Lambda 설정하기 (https://yiunsr.tistory.com/925)
3. AWS Lambda로 thumbnail 만들기(3) - 트러블 슈팅 (https://yiunsr.tistory.com/926)
로 나눈다. 참고로 python 코드는 다루지 않겠다. 이건 찾아보면 많은 코드를 참고 할 수 있으리라 생각된다. 파이썬 코드를 만들 때 pillow 와 pillow-simd 차이는 없다. 다만 pillow-simd 가 pillow 보다는 버전업이 느린편이다. (이것 어쩔 수 없겠지.)

우선 내 개발환경은 intel mac 이라는 것을 밝힌다. 그리고 사용한 파이썬 버전은 3.11 이다. pillow-simd 를 사용하기 위해서는 x86-64 환경이어야 한다. 

다음 글에서 계속 이어 나가도록 하겠다. 

 

 AWS Lambda 를 처음으로 사용해 봤다. S3 에 업로드된 이미즈를 resize 하고 watermark 를 붙여서 thumbnail 이미지로 만드는 작업을 하고 싶었다. 기존에는 ec2 내 작업 중 하나였는데, 동작이 너무 느리고 간혹 서버가 죽는 문제가 발생했다. (내부적으로 multi processing 을 처리하는데 이게 좀 잘못된 것 같다.) 동시에 여러 job을 처리할 수 있는 aws lambda 를 사용해서 개선 작업을 하기로 했다.

lambda 의 Trigger 동작이 s3 put 동작으로 설정하고 이것 저것 작업 했다. 내부동작은 s3에 올라온 원본파일을 읽어  thumbnail 을 3가지로 만들고 다시 s3 에 업로드 하는 동작이다. 결과를 s3에 업로드하는 동작이 다시  3개의 Lambda 함수를 trigger 하는 recursive 동작이었다. 이 코드를 테스트 할 때 엄청나게 생성되는 파일을 보고  뭔가 잘못됨을 인지했다. 중지 시키는 것도 몰라서 그냥 에러를 발생시키는 코드를 배포했다. 

 한 1분 동안 작동한 것 같은데, 이게 3의 n 승번 동작이 발생해서  50만번 정도의 동작이 발생했다. 다행히 aws lambda 무료 사용횟수 내였다. 대신 S3 사용 비용이 $5 정도는 발생했다. 

테스트 하는 중이었기 때문에 따로 방어 코드는 넣지 않았다. 그리고 boto3 의 put_object 만 put 인줄 알았다. 그래서  upload_file 을 thumbnail 을 업로드 하면 다시 lambda 함수가 trigger 되는 일이 없을 거라고 생각했다. 그런데  upload_file  도 PUT 으로 인식하고 있었다. 

 방어 코드로 meta 정보를 이용하기로 했다. S3에 파일(Object)를 업로드 할 때  x-amz-meta-* 를 붙여 부가적인 정보를 전달할 수 있다. x-amz-meta-type : orgin 을 가질 때만 파일을 thumbnail 을 만드는 작업을 진행하기로 했다. 물론 이렇게 하면 의미없는 lambda 호출이 추가적으로 3번(내 경우 thumbnail 종류를 3가지 만든다.) 발생할 수 있다. 

 여기에 추가적인 트리거자체가 발생하지 않도록 put 이 아닌 다른 방법으로 s3 를 업로드 시켰다. S3 에 PresignedUrl을 통해 thumbnail 파일을 업로드 시키는 방식이 있다. 내 테스트에서는 이것은 Put trigger 가 발생하지 않았다. 이 방법은 보통 웹브라우저에서 사용자 PC에서 바로 S3 로 업로드 할 때 사용하는 방식이다. 마치 웹브라우가 업로드 하듯이 python requests 라이브러리를 이용해서 파일을 업로드 시키면 이 방법이 먹힌다. 

처음으로 새로운 것을 사용하다보니 좀 일이커지는 것 같다. 이 작업하면서 파이썬 library 를 설치하는 것도 꽤 큰일이었는데. 이것도 나중에 정리해서 올리도록 하겠다.