AWS lambda로 EFS에 파일 업로드

파워소동·2022년 11월 15일
3

AWS

목록 보기
1/4
post-thumbnail

오늘은 Lamba를 통해 EFS(Elastic File System)에 이미지 파일(jpg)을 업로드하는 과정을 작성하려고 합니다.

EFS, API gateway, lambda와 같은 AWS자원을 생성하고 이미지를 저장하는 python코드를 작성합니다.

마지막으로는 EFS를 EC2 mount해 이미지 파일이 정상적으로 올라갔는지 확인해보도록 하겠습니다.

VPC 만드는 것부터 글 쓰면 글이 너무 길어질 것 같아서 default VPC를 사용했습니다만... 웬만하면 VPC부터 만드시는 것을 추천드립니다!

1. EFS 생성

1-1. EFS 페이지에서 파일 시스템 생성

저는 기본 설정으로 만들어주었습니다~ VPC는 default VPC를 선택했습니다!

1-2. EFS endpoint 생성

추후에 lambda와 연결해야 하기 때문에 endpoint를 생성해 주어야 합니다.
루트 디렉터리 경로는 바꾸셔도 됩니다.
그리고 아래 이미지처럼 설정하여 생성합니다.


2. lambda 생성

API Gateway와 연결하기 전에 lambda를 간단히 생성해 줄게요.
함수의 이름, 런타임(함수 언어)만 선택하고 다른 내용은 기본으로 설정하고 생성했습니다.

3. API gateway 생성 및 설정

앞서 생성한 lambda를 연결할 API를 API Gateway로 생성하도록 하겠습니다.

3-1. API gateway 생성

[api 생성]을 선택합니다.

API 유형은 REST API를 선택해 주었습니다.

아래 이미지처럼 선택하여 API를 생성합니다.

생성이 완료되면 아래 이미지처럼 보입니다.

3-2. API gateway api 생성

람다와 연결될 API를 생성해 줄 거예요.
리소스는 /image, 메서드는 이미지를 업로드할 것이기 때문에 post로 생성했습니다.

3-3. lambda 연결

생성한 POST API에 lambda를 연결하겠습니다.

post를 클릭하고 아래 이미지처럼 연결해주시면 됩니다.
Lambda 함수 란에는 앞서 생성한 Lambda함수의 이름을 작성합니다.

성공적으로 api와 lambda가 연결되면 아래 이미지처럼 보여요.

람다에서도 API Gateway가 연결된것을 확인할 수 있습니다.

3-4. API 설정

이미지를 업로드해야 하기 때문에 content type을 multipart/form-data로 설정해 주어야 해요.

설정으로 가서 이진 미디어 형식을 multipart/form-data로 설정하고 저장해 주세요

그리고 리소스 > 통합 요청에 들어갑니다.

Lambda 프록시 통합 사용을 체크해주세요

3-5. 스테이지 생성 및 배포

이제 앞서 수정한 설정들을 적용시켜야합니다.
배포를 진행합니다.

첫 1회는 스테이지가 없기 때문에 [새 스테이지]를 선택하여 배포를 진행해 주세요.

후에 배포할 때는 이번에 생성한 스테이지를 필요에 따라 재사용/생성하면 됩니다!
저는 스테이지 이름을 적당히 v1로 했는데 바꾸셔도 됩니당

4. lambda 설정

4-1. form-data가 정상적으로 들어오는지 확인 (optional)

본격적으로 lambda 설정 전에 form-data가 정상적으로 api를 통해 들어오는지 확인하려고 합니다.

lambda 함수 > 구성 > 트리거에서 API endpoint를 copy 합니다.

postman을 통해 [POST] 요청을 보내봅니다.
body의 형식은 form-data이며 이미지도 하나 올려주었습니다.

request가 잘 들어오는지 print하여 출력하도록 lambda에 코드를 추가했습니다.

import json

def lambda_handler(event, context):
    print(event) # 이부분만 추가
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

만약 설정이 잘 안됐으면 form-data가 담겨있는 event가 출력되지 않을 거예요.

CloudWatch를 확인하니 정상적으로 출력이 되는 것을 확인할 수 있습니다.


잘 되는군요!

4-2. VPC 설정

lambda와 EFS를 연결 시 lambda가 EFS와 같은 VPC 내에 있어야 합니다.

저는 VPC를 따로 만들지 않고 default VPC를 사용했기 때문에 아래 이미지처럼 default VPC를 선택했고 서브넷은 a, b, c, d 전부 선택해 주었습니다.

보안그룹도 default로 선택했습니다.

4-2-1. 에러 해결

4-2를 진행할 때
아래와 같은 에러 메시지가 발생할 수 있습니다.
lambda 역할에 권한이 없어 발생하는 오류입니다!
당황하지 않고 정책을 연결하면 됩니다.

lambda 함수 > 구성 > 권한에서 보이는 역할 이름을 클릭하면 IAM 페이지로 이동합니다.

권한 추가 > 정책 연결 선택.

AWSLambdaVPCAccessExecutionRole 권한을 선택하고 정책 연결을 해줍니다.

4-4. lambda와 EFS 연결

lambda 함수 > 구성 > 파일 시스템에서 파일 시스템 추가를 진행합니다.
앞서 1-1에서 만들었던 EFS와 1-2에서 만들었던 EFS endpoint를 연결합니다.
로컬 탑재 경로는 /mnt/images로 지정해 주었습니다.

5. lambda 이미지 저장 코드 작성 (python)

이제 코드만 작성하면 됩니다!

5-1. 코드 작성

코드는 아래를 참고해주세요!

import os
import fcntl
import datetime
import json
import base64
import io
import cgi
import os.path
from os import path
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)


MSG_FILE_PATH = '/mnt/images/'

def lambda_handler(event, context):
  fileResult = save_file(event['headers'], event['body'])

  return { 
    'body' : json.dumps({
      'fileUrl' : fileResult,    # 파일 경로
      'resultMessage' : 'successfully saved'
    })
  }
  
def save_file(headers, body):
  fp = io.BytesIO(base64.b64decode(body)) # decode
  
  environ = {"REQUEST_METHOD": "POST"}
  headersContent = {
    "content-type": headers["Content-Type"]
  }

  fs = cgi.FieldStorage(fp=fp, environ=environ, headers=headersContent) 
  
  
  nowDate = datetime.datetime.now()
  nowYear = nowDate.strftime("%Y")
  nowMonth = nowDate.strftime("%m")
  nowDateTime = str(nowDate.strftime("%Y%m%d%H%M%S"))
  
  dirPath = nowYear + "/" + nowMonth 
  fileName = fs["uploadFile"].filename
  
  # directory가 없는 경우 생성
  if not path.exists(MSG_FILE_PATH + dirPath):
    logger.info('create directory : '+ dirPath)
    os.makedirs(MSG_FILE_PATH + dirPath)
  
  # Ex) /mnt/images/2022/11/50970_51164_4758.jpg
  fileFullPath = MSG_FILE_PATH + dirPath + "/"+ fileName
  
  with open(fileFullPath, 'wb') as msg_file:
    fcntl.flock(msg_file, fcntl.LOCK_EX)
    msg_file.write(fs["uploadFile"].file.read())
    fcntl.flock(msg_file, fcntl.LOCK_UN)
  
  fileUrl = dirPath + "/" + fileName
  logger.info('successfully saved : '+ fileUrl)
  return fileUrl

5-2. postman request

아래 이미지처럼 [POST] request를 보내보겠습니다.


성공!했네용

참고로 이미지는 화려한 빅토림입니다. ^^

6. EFS 파일 TEST

이 방법은 야매입니다..! 더 좋은 방법은 댓글로 알려주세요!

6-1. EC2에 EFS mount

먼저 EC2에 amazon-efs-utils를 설치합니다.
이게 설치되어 있어야 EFS를 EC2에 mount를 할 수 있어요!

sudo yum install amazon-efs-utils

그리고 AWS의 EFS페이지로 돌아가 [연결]을 선택합니다.

그리고 EFS 탑재 헬퍼 사용: 의 내용을 copy합니다

아까 copy한 내용을 EC2에 paste해서 EFS를 mount 해줍니다.

그리고 df -H 명령어를 통해 mount가 된 것을 확인할 수 있습니다.

mount가 됐습니다!

아까 저장한 파일이 잘 보이네요!

6-2. FileZilla를 통해 이미지 조회해보기

FileZilla를 통해 EC2에 접근합니다.

EC2에 mount된 EFS내에 있는 파일을 local로 옮겨서 확인해봤어요
잘 보이네용

7. 끝

혹시 잘못된 부분이나 좋은 아이디어있으면 댓글 남겨주세요!!
다들 즐거운 하루 되세요!

그리고 빅토림이 부르는 베리멜론 함 들어보세용ㅎㅎ
베리멜론!!!

8. 참고

Amazon Web Services 한국 블로그

2개의 댓글

comment-user-thumbnail
2022년 11월 25일

좋은 글에 댓글이 없네요. 잘 봤습니다.

1개의 답글