본문 바로가기
Flask

Flask API Server 를 AWS Lambda + API Gateway 에 배포하기

by ssury94 2025. 2. 20.

 

 

1️⃣ Flask + AWS Lambda + API Gateway 개념 정리

AWS Lambda는 서버를 직접 관리하지 않고, 코드만 실행할 수 있는 서버리스(Serverless) 컴퓨팅 서비스

이때, Lambda는 기본적으로 HTTP 요청을 직접 받을 수 없기 때문에 API Gateway와 연결해야 한다.

즉,

  • API Gateway: API 요청을 받아서 Lambda에 전달하는 역할
  • Lambda: API의 실제 동작(Flask 서버 역할)

 

 

2️⃣ serverless-wsgi가 무엇인지?

serverless-wsgi는 Flask 같은 WSGI 기반 프레임워크(Web Server Gateway Interface)를 AWS Lambda에서 실행할 수 있도록 도와주는 라이브러리.

AWS Lambda는 기본적으로 HTTP 서버가 없고, Lambda의 event와 context 객체를 다뤄야 한다.
Flask는 일반적으로 WSGI 서버(Gunicorn, uWSGI 등)가 필요하지만,
Lambda에는 그런 환경이 없기 때문에 serverless-wsgiWSGI 요청을 Lambda의 이벤트로 변환해주는 역할을 한다.

📌 serverless-wsgi의 역할

  • API Gateway 요청을 Flask의 WSGI 형식으로 변환
  • Flask에서 응답한 데이터를 API Gateway 형식으로 변환
  • Lambda 내에서 Flask 앱을 실행 가능하도록 만듦

📌 설치 및 사용 방법

pip install serverless-wsgi

Lambda 핸들러(lambda_function.py) 설정:

from app import app
from serverless_wsgi import handle_request

def lambda_handler(event, context):
    return handle_request(app, event, context)

 

 

 

 

3️⃣ IAM 사용자 권한 설정

AWS Lambda + API Gateway를 사용하려면 IAM 권한이 필요.
각각의 역할을 하는 정책(policy)들을 IAM에 추가한다.

📌 필요한 IAM 정책

정책 이름 설명

AmazonAPIGatewayAdministrator API Gateway 리소스 생성 및 관리
AmazonEC2ContainerRegistryFullAccess ECR (도커 이미지 저장소) 사용
AmazonRDSDataFullAccess RDS(Database) 접근
AmazonS3FullAccess S3 파일 저장소 접근
AmazonSSMFullAccess AWS Systems Manager 접근 (환경 변수, Parameter Store)
AWSCloudFormationFullAccess AWS CloudFormation 템플릿 사용
AWSLambda_FullAccess Lambda 함수 생성 및 관리
CloudFrontFullAccess CloudFront (CDN) 사용
CloudWatchFullAccess 로그 및 모니터링 데이터 접근
IAMFullAccess IAM 사용자 및 역할 관리

IAM 사용자 deploy에게 위 정책들을 추가하면, AWS 리소스를 배포하고 관리할 수 있다.

 

 

4️⃣ 전체 배포 과정 요약

AWS Lambda + API Gateway를 사용해서 Flask API를 배포하는 과정은 이렇게 진행된다.

📌 배포 흐름

1️⃣ Flask 앱 (app.py) 작성
2️⃣ Lambda에서 Flask 실행을 위한 핸들러 (lambda_function.py) 준비
3️⃣ Docker 컨테이너 이미지 생성 (Dockerfile)
4️⃣ AWS Lambda + API Gateway 리소스 정의 (template.yaml)
5️⃣ GitHub Actions (deploy-lambda.yaml) 설정하여 코드 푸시 시 자동 배포
6️⃣ API Gateway URL을 통해 Flask API 사용 가능

 

 

 

5️⃣ 폴더 구조

이제 폴더와 코드들을 하나씩 살펴보자.

.
├── app.py                   # Flask 앱 (웹서버 코드)
├── config.py                 # 보안키 등 환경변수 관리
├── lambda_function.py       # AWS Lambda 핸들러
├── requirements.txt         # 파이썬 라이브러리 목록
├── Dockerfile               # Docker 컨테이너 설정 파일
├── template.yaml            # AWS SAM 템플릿 (Lambda, API Gateway 설정)
├── resources/               # 추가적인 리소스 폴더
│   ├── chat.py              # 채팅 API 기능 모듈
└── .github/
    └── workflows/
        └── deploy-lambda.yaml   # GitHub Actions 자동 배포 스크립트

 

 

 

 

6️⃣ 각 파일 코드 상세 설명

📌 template.yaml (AWS SAM 템플릿)

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Flask API with Container + API Gateway

Parameters:
  ImageUri:
    Type: String
    Description: "ECR Image URI (컨테이너 이미지 URI)"

Globals:
  Api:
    Cors:
      AllowMethods: "'GET,POST,OPTIONS'"
      AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
      AllowOrigin: "'*'"

Resources:
  ChatFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: flask-llm-chat-lambda
      PackageType: Image
      ImageUri: !Ref ImageUri
      Timeout: 120
      MemorySize: 2048
      Events:
        ChatApi:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

Outputs:
  ApiEndpoint:
    Description: "API Gateway Base URL"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod"

이 템플릿은 Lambda + API Gateway를 자동으로 생성하는 역할

FunctionName: flask-llm-chat-lambda 부분은 필요에 따라 변경하자.

 

 

 

📌 Dockerfile (컨테이너 설정)

#Python 3.10버전에서 테스트 했다.
#도커 소프트웨어에게 python3.10, aws의 람다와 같은 서버를 준비하도록 한다.
FROM public.ecr.aws/lambda/python:3.10

#라이브러리 설치를 위해 requirements.txt를 컨테이너에 복사한다.
# requirements.txt 현재경로
COPY requirements.txt ./ 
#람다에 파이썬 라이브러리 설치
RUN pip install --no-cache-dir -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

#소스코드를 컨테이너에 복사한다.
#. 현재경로의 모든 파일을 컨테이너의 LAMBDA_TASK_ROOT경로에 복사한다.
COPY . ${LAMBDA_TASK_ROOT}

#서버 동작
CMD [ "lambda_function.lambda_handler" ]
#lambda_handler.py의 lambda_function 함수를 실행-> 서버가 동작한다.
  • Python 가상 환경 (프로젝트에선 3.10ver 사용중이라 3.10으로 독커 컨테이너 설정처리)
  • Flask와 필요한 라이브러리를 설치 (requirements.txt)
  • Lambda에서 실행할 소스코드를 복사
  • lambda_function.lambda_handler를 실행하여 Lambda가 Flask를 호출하도록 설정

 

 

 

📌 lambda_function.py (Lambda 핸들러)

from app import app
from serverless_wsgi import handle_request

def lambda_handler(event, context):
    return handle_request(app, event, context)

API Gateway 요청을 Flask가 처리할 수 있도록 변환해줌.

 

 

 

7️⃣ GitHub Actions 배포 자동화

📌 .github/workflows/main.yaml

name: Build and Deploy Flask Container (SAM + API Gateway)

on:
  push:
    branches: [ "main" ]  # main 브랜치에 push 발생 시 실행

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      # (1) GitHub 저장소 코드 체크아웃
      - name: Check out code
        uses: actions/checkout@v2

      # (2) AWS 자격 증명 설정 (GitHub Secrets에서 가져옴)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2  # 서울 리전

      # (3) 환경 변수 설정
      - name: Set environment variables
        run: |
          # GitHub Secrets에서 값을 가져와 환경 변수로 설정
          echo "ECR_REPO_NAME=${{ secrets.ECR_REPO_NAME }}" >> $GITHUB_ENV
          echo "ACCOUNT_ID=${{ secrets.AWS_ACCOUNT_ID }}" >> $GITHUB_ENV
          echo "AWS_REGION=ap-northeast-2" >> $GITHUB_ENV

      # (4) ECR 리포지토리 생성 (없는 경우) 컨테이너 관리할 리포지토리
      - name: Create ECR repository (if not exists)
        run: |
          aws ecr describe-repositories --repository-names $ECR_REPO_NAME || \
          aws ecr create-repository --repository-name $ECR_REPO_NAME
        env:
          ECR_REPO_NAME: ${{ env.ECR_REPO_NAME }}
          AWS_REGION: ap-northeast-2

      # (5) ECR 로그인 (Docker 이미지 push를 위해)
      - name: Login to Amazon ECR
        run: |
          aws ecr get-login-password --region ap-northeast-2 \
          | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com
        env:
          ACCOUNT_ID: ${{ env.ACCOUNT_ID }}

      # (6) Docker 이미지 빌드 및 태그 설정
      - name: Build Docker image
        run: |
          # 타임스탬프를 이용한 유니크한 태그 생성
          TIMESTAMP=$(date +%Y%m%d%H%M%S)
          echo "IMAGE_TAG=${TIMESTAMP}" >> $GITHUB_ENV
          
          # 로컬 태그로 빌드
          docker build -t $ECR_REPO_NAME .
          # ECR 푸시를 위한 태그 설정 (타임스탬프 태그 사용)
          docker tag $ECR_REPO_NAME:latest $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME:${TIMESTAMP}
        env:
          ECR_REPO_NAME: ${{ env.ECR_REPO_NAME }}
          AWS_REGION: ${{ env.AWS_REGION }}
          ACCOUNT_ID: ${{ env.ACCOUNT_ID }}

      # (7) Docker 이미지를 ECR에 푸시
      - name: Push Docker image
        run: |
          docker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME:${IMAGE_TAG}
        env:
          ECR_REPO_NAME: ${{ env.ECR_REPO_NAME }}
          AWS_REGION: ${{ env.AWS_REGION }}
          ACCOUNT_ID: ${{ env.ACCOUNT_ID }}

      # (8) AWS SAM CLI 설치 - Serverless Application Model
      - name: Install AWS SAM CLI
        run: |
          sudo pip install --ignore-installed aws-sam-cli

      # (9) SAM으로 Lambda 함수 배포 
      - name: Deploy with SAM
        run: |
          # 새로운 이미지 URI 설정 (타임스탬프 태그 사용)
          IMAGE_URI="$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME:${IMAGE_TAG}"
          ECR_REPO_URI="$ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPO_NAME"

          # SAM 배포 실행 (--force-upload 추가)
          sam deploy \
            --template-file template.yaml \
            --stack-name FlaskTranslateStack \
            --capabilities CAPABILITY_IAM \
            --parameter-overrides ImageUri=$IMAGE_URI \
            --image-repository $ECR_REPO_URI \
            --force-upload \
            --no-fail-on-empty-changeset
        env:
          ECR_REPO_NAME: ${{ env.ECR_REPO_NAME }}
          AWS_REGION: ${{ env.AWS_REGION }}
          ACCOUNT_ID: ${{ env.ACCOUNT_ID }}
  • git push 하면 자동으로 배포됨.
  • AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID 등의 GitHub Secrets 필요.
  • Actions >Secrets and varlables에서 필요 변수들을 설정하자. 계정 아이디는 AWS 프로필을 누르면 12개 숫자 확인가능

 

 

 

 

컨테이너는 커밋 푸쉬 후 자동으로 빌드 및 배포된다.

Amazon Elastic Container Registry (ECR)메뉴에서 컨테이너 확인가능

 

 

 

 

 

엔드 포인트 확인은

Lambda ➡️ 게이트웨이에서 확인가능

깃허브 액션즈 워크플로우 Deploy with SAM에서도 확인 가능하다.

 

 

 

 

8️⃣ 에러 디버깅 방법

  • Lambda에서 에러가 발생하면, AWS Lambda 콘솔 > 모니터링 탭에서 로그 확인
  • CloudWatch Logs에서 상세 에러 메시지 확인 가능

 

 

 

 

 

 

이제 git push 하면 Flask API가 자동 배포돼서, API Gateway URL로 호출할 수 있다!! 🚀