Pulumi 시작하기(1) - Introduction
Pulumi 시작하기(2) - 코드 작성 및 배포
Pulumi 시작하기(3) - API Gateway v2 + Lambda 예제
Pulumi 시작하기(4) - S3와 Lambda를 이용한 썸네일 추출기
프로젝트 시작
프로젝트 구성
.mp4
파일에서 썸네일을 추출하는 서버리스 앱을 만드는 예제이다. 프로젝트 구조를 보면, S3에 새로운 .mp4
가 업로드되면, onNewVideo
함수가 이를 감지한다. 그리고 Fargate에서 Node로 작성된 컨테이너를 실행시킨다. 이 컨테이너는 썸네일을 추출해서 버킷에 업로드한다.
프로젝트 설정
예제 프로젝트를 다운받아 시작한다.
pulumi new https://github.com/pulumi/pulumitv/tree/master/modern-infrastructure-wednesday/2020-12-09/lambda-containers
프로젝트 구성은 다음과 같다.
.
├── Pulumi.dev.yaml
├── Pulumi.yaml
├── README.md
├── __main__.py
├── __pycache__
├── app
├── build-ffmpeg
├── requirements.txt
└── venv
소스코드
먼저 리소스를 정의하고 있는 __main__.py
를 살펴보자.
bucket = aws.s3.Bucket("bucket")
repo = aws.ecr.Repository("sampleapp")
ecr_creds = aws.ecr.get_authorization_token()
image = docker.Image("sampleapp",
build="./build-ffmpeg",
image_name=repo.repository_url,
registry=docker.ImageRegistry(server=repo.repository_url, username=ecr_creds.user_name, password=ecr_creds.password))
버킷, ECR 저장소를 만들었다. 그리고 ECR에 접근하기 위한 접근 권한을 ecr_creds
에 저장하고, 이를 도커 이미지를 ECR에 업로드할 때 registry=docker.ImageRegistry
에 인자로 넣어주었다.
여기서 build
파라미터를 보면, 로컬 경로를 참조하고 있는 것을 알 수 있다. 이는 루트 경로의 build-ffmpeg
폴더를 확인하면 알 수 있는데, index.js
와 Dockerfile
이 있는 것을 알 수 있다. index.js
는 ffmpeg 관련 작업을 수행하는 파일이므로 패스하고, Dockerfile
을 확인해보자.
FROM public.ecr.aws/lambda/nodejs:10.2020.12.01.12
ARG FUNCTION_DIR="/var/task"
...
# Install ffmpeg
RUN curl https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o ffmpeg.tar.xz -s
RUN tar -xf ffmpeg.tar.xz
RUN mv ffmpeg-*-amd64-static/ffmpeg /usr/bin
...
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "index.handler" ]
여기서 알 수 있는 사실은 이 도커 이미지는 index.handler
핸들을 통해 함수가 실행된다는 것이고, node 런타임에서 작동한다는 것이다.
Pulumi는 Python으로 작성했지만, 실제 컨테이너 런타임은 Node이다.
Dockerfile에 ffmpeg 버전 관련 버그가 있는데, RUN mv ffmpeg-4.3.1-amd64-static/ffmpeg /usr/bin
라인의 4.3.1
을 *
로 수정한다.
role = aws.iam.Role("thumbnailerRole", assume_role_policy=f"""{{
"Version": "2012-10-17",
"Statement": [
{{
"Effect": "Allow",
"Principal": {{ "Service": "lambda.amazonaws.com" }},
"Action": "sts:AssumeRole"
}}
]
}}""")
aws.iam.RolePolicyAttachment("lambdaFullAccess", role=role.name, policy_arn=aws.iam.ManagedPolicy.AWS_LAMBDA_FULL_ACCESS)
다음으로는 thumbnailerRole
라는 이름의 iam Role을 하나 선언하고, 람다의 Full Access 권한을 부여한다. 여기서 aws.iam.ManagedPolicy.AWS_LAMBDA_FULL_ACCESS
가 enum
으로 미리 정의되어 있는(pre-defined) 리터럴이다.
aws.iam.RolePolicyAttachment(
"lambdaFullAccess",
role=role.name,
policy_arn=aws.iam.ManagedPolicy.LAMBDA_FULL_ACCESS,
)
aws.iam.RolePolicyAttachment(
"lambdaBasicRole",
role=role.name,
policy_arn=aws.iam.ManagedPolicy.AWS_LAMBDA_BASIC_EXECUTION_ROLE,
)
aws.iam.RolePolicyAttachment(
"lambdaS3Role",
role=role.name,
policy_arn=aws.iam.ManagedPolicy.AMAZON_S3_FULL_ACCESS,
)
하지만 AWS에서 해당 Policy를 deprecate 시켰기 때문에 이 부분을 위 코드로 수정해준다.
thumbnailer = aws.lambda_.Function("thumbnailer", package_type="Image", image_uri=image.image_name, role=role.arn, timeout=60);
allow_bucket = aws.lambda_.Permission("allowBucket",
action="lambda:InvokeFunction",
function=thumbnailer.arn,
principal="s3.amazonaws.com",
source_arn=bucket.arn)
bucket_notification = aws.s3.BucketNotification("bucketNotification",
bucket=bucket.id,
lambda_functions=[aws.s3.BucketNotificationLambdaFunctionArgs(
lambda_function_arn=thumbnailer.arn,
events=["s3:ObjectCreated:*"],
filter_suffix=".mp4",
)],
opts=pulumi.ResourceOptions(depends_on=[allow_bucket]))
pulumi.export("bucketName", bucket.bucket)
이제 Lambda 함수 thumbnailer
를 선언하고, 아까 정의한 Role의 arn을 넣어준다.
다음으로 버킷에 파일이 추가될 경우 람다를 trigger하도록 allow_bucket
권한을 S3 버킷에 추가한다. 이 권한의 action이 action="lambda:InvokeFunction"
인 것을 확인할 수 있다. 이제 버킷에 .mp4
파일이 추가될 경우 람다 함수가 trigger된다.
프로젝트 배포
이제 pulumi up
을 입력해 프로젝트를 AWS에 배포해보자.
$ pulumi up
Type Name Plan
+ pulumi:pulumi:Stack lambda-containers-dev create
+ ├─ aws:ecr:Repository sampleapp create
+ ├─ aws:s3:Bucket bucket create
+ ├─ docker:image:Image sampleapp create
+ ├─ aws:iam:Role thumbnailerRole create
+ ├─ aws:iam:RolePolicyAttachment lambdaFullAccess create
+ ├─ aws:lambda:Function thumbnailer create
+ ├─ aws:lambda:Permission allowBucket create
+ └─ aws:s3:BucketNotification bucketNotification create
lambda-containers-dev
스택에 정의된 리소스 리스트가 출력된다. 하나하나 맞는지 확인을 해보면,
- ECR 레포지터리
- S3 버킷
- Docker image
- S3 Permission & Lambda trigger
- Lambda IAM Role
이렇게 9개 리소스가 생성된다고 한다. yes를 선택해 계속 진행한다.
테스트
이제 샘플 mp4 파일을 다운받은 다음 S3 업로드해, 우리가 생각한대로 작동하는지 보자.
wget https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_480_1_5MG.mp4 -O sample_00-01.mp4
pulumi logs --follow
위 명령어를 입력한 다음, 다른 셸에서 S3에 파일을 업로드한다.
aws s3 cp sample_00-01.mp4 s3://$(pulumi stack output bucketName)
그 다음 s3에서 썸네일을 다운로드 받아보자.
aws s3 cp s3://$(pulumi stack output bucketName)/sample.jpg sample.jpg
정상적으로 썸네일이 열리는 것을 확인할 수 있다.
리소스 정리
pulumi destroy
테스트를 끝낸 다음엔 관련 리소스를 모두 삭제해준다.