이 글을 작성한지 꽤 오랜 시간이 흐른 후 읽는 사람이 있을 것 같다. 그리고 나와는 조금 다른 환경에서 다른 라이브러리가 필요한 일이 있을 것이다. 미래에는 lambda 기능 자체가 업데이트 되면서 변경될 수도 있다. 내가 문제를 해결한 방식을 잘 응용하면 해당 문제를 푸는데 도움일 될 수도 있을 것이다. 

aws 계층에 업로드된 파일의 경로 확인하기

내 경우는 python.zip 파일을 계층에 업로드 할 때 이 경로가 aws lambda 에서는 /opt/python 이었다. 이 경로가 어떻게 결정되는 것인지 모르겠지만 확인하는 방법이 있다. 

import requests
print(requests.__file__)

위 코드를 aws lambda 에 실행시켜 본다. 내 경우에는 /opt/python/requests/init.py  이라는 경로가 나왔다. __file__ 이라는 키워드는 파이썬 코드에서 사용이 가능하지만 module 에 대해서 경로를 확인하는 용도로도 사용이 가능하다. 


필요로 하는 shared_library 알기

참고 :  https://stackoverflow.com/a/47346043    
이번 작업에서 가장 어려웠던 일 중의 하나이다. https://aws.amazon.com/ko/blogs/compute/creating-faster-aws-lambda-functions-with-avx2/  에서 나왔던  파일들은 모든 파일을 포함되어 있지 않았다. 그래서  /usr/lib64 을 다 넣을까 하는 생각도 해봤지만 그렇기에는 zip 파일이 크게 나왔다. 어째든 stackoverflow 의 글을 잘 참고하면 된다. 그런데 lsof  가 없기 때문에 yum install lsof   로 설치해야 한다. 설치하고 나서 lsof 를 이용할 때는 /usr/sbin/lsof   라고 해야 한다. 기본 path 설정에 /usr/sbin 이 없는 것 같다. 어째든 저 방법을 이용해서 필요한 so 파일을 알 수 있다. 그리고 이 동작을 하기 위해서는 2개의 bash 터미널이 필요하다. 기존의 docker  container 에서 2개의 bash 터미널을 준비한다. 2개의 터미널을 각각 consoleA와 consoleB라고 하겠다. 

#### consoleA ####
# 파이썬 특정 경로 라이브러리 이용해서 실행
PYTHONPATH="/root/python" python3
>>> import os
>>> os.getpid()

아래 코드 동작전 consoleB의 before 파일 생성할 것

>>> from PIL import Image

위 코드 동작후 consoleB의 after 파일 생성할 것

############

#### consoleB ####
# os.getpid() 에서 나온 번호를 이용한다. 여기서는 1093 이라고 가정한다.
/usr/sbin/lsof  -p 1093 > before 

consoleA 에서 from PIL import Image 동작 후 
/usr/sbin/lsof  -p 1093 > after


# from PIL import Image 동작 후 추가된 so 파일 확인 하기 위해 
# before 파일과 after 파일 비교
diff <(awk '{print $NF}' after) <(awk '{print $NF}' before)

# 출력된 so 파일들이 로딩된 shared library 임

pillow-simd 의 경우 운이 좋아 from PIL import Image  할 때 shared library 가 호출 되었다. 경우에 따라서는 webp 파일을 불러올 때 특정 webp 라이브러리를 이용하는 식으로 얼마든지 lazy 하게 library 를 가져올 수도 있다. 이 경우는 이런 것들 동작까지 한 후 어떤 so 파일이 로딩되는지 확인해야 한다.

 

소스에 업로드된 파일 읽기 불가

내 경우에는 thumbnail 이미지를 만들때, png파일을 이용해서 watermark 를 붙인다. 이 watermark 이미지를 lambda 코드안에 파일로 업로드 했다. (python.zip 폴더 아니고 소스를 수정하는 vscode 화면에 직접 파일을 업로드 했음) 그런데 여기 올라온 이미지파일을 pillow로 열면 에러가 발생했다. 처음에는 png 문제라고 생각했는데, 웃기게도 aws s3 에 있으면 잘 읽어졌다. 파일사이즈나 데이터자체도  aws lambda 코드로 확인해봤는데 정상적이었다. 나 처럼 고생하지 말고 이런 현상 있으면 그냥 s3 를 이용하자. 


트리거 중복으로 추가할 수 없다고 에러 발생할 때

aws lambda 함수에 트리거를 걸어 넣고, 그냥 삭제해 버리면 이런 현상이 있다. s3 버킷 속성에 해당 트리거가 남아 있다. 해당 트리거를 지우도록 하자. 

 

 

기본적인 사항(권한설정, 트리거)은
자습서: Amazon S3 트리거를 사용하여 썸네일 이미지 생성 https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-tutorial.html  을 참고하면 된다. 

 앞에서 만든 python.zip 파일을 계층에 업로드 해야 한다. 호환 아키텍처은 x86_64  로 설정한다. 

그리고 pillow-simd 을 설치하는 경우 꼭 환경변수를 설정해야 한다. 
LD_LIBRARY_PATH  에 대해
/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib:/opt/python/lib
라고 설정해야 한다. 

LD_LIBRARY_PATH  는 shared library 경로를 지정하는 환경변수이다. 사실 일반적인 경우 

$LD_LIBRARY_PATH:/opt/python/lib

이런 식으로 기존환경변수에 concat 개념으로 추가할 수 있어야 한다. 그런데 내가 뭘 잘못했는지 잘 안된다. 그래서 좀 무식하게 저렇게 설정했다. 맨 끝에 붙은 /opt/python/lib  가 중요하다. 내가 올린 python.zip 파일이 이 경로에 풀린다는 것이다.  docker에서   /root/python/lib 에 넣었던 파일이 /opt/python/lib 에 위치하게 된다.  당연히  docker에서  /root/python 에서 넣었던 라이브러리는 /opt/python 에 위치하게 될 것이다. 
  어떻게 /opt/python 에 있는 라이브러리가 불러 올 수 있을 까 하는 의문이 들 수도 있다. PYTHONPATH  라는 환경변수를 이용하면 특정경로에 있는 라이브러리를 참고해서 python을 실행할 수 있다. 
 지난 글에서 사용했던 docker에서 

PYTHONPATH="/root/python" python3

라고 환경변수를 전달하면서 python 을 실행하면 /root/python 에 설치했던 라이브러리를 이용해서 파이썬을 실행할 수 있다. 


다음 글에서는 여러문제가 생겼을 때 대처하는 방법에 대해 적겠다. 누군가는 이 글을 나와 같은 버전과 같은 라이브러리를 이용하지 않고 유사 문제를 풀려고 할 수 있다. 내가 이글을 작성하면서 적용한 테크닉을 잘 활용하면 도움이 될수도 있을 것 같다. 

왜 시작인 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 를 설정하는 방법을 설명하겠다.