AWS Lambda를 이용한 API 서비스 배포 (1/2)

AWS Lambda가 무엇 인가요?

AWS에서 설명하는 Lambda는 “이벤트에 응답하여 코드를 실행하고 자동으로 기본 컴퓨팅 리소스를 관리하는 서버 없는 컴퓨팅 서비스입니다.” (이전 AWS관련 글에도 적었지만 매번 씁니다. ^^ AWS에서 제공하는 문서를 보시면 더 자세한 내용이 있습니다. https://aws.amazon.com/ko/lambda/details/)여러 가지 특징중에 특히 강조할 만한 것은 서버없는 서비스(serverless services)가 가능하다는 것 입니다. 좀 더 구체적으로 말하면 내가 관리해야할 서버가 없이도 서비스를 제공할 수 있다 입니다. 서버 상태를 관리하지 않아도 되면 많은 비용과 스트레스를 줄일 수 있습니다. AWS Lambda를 사용해서 서버 관리에 필요한 당연히 예상되는 비용(시간, 돈, 사람등)을 획기적으로 줄일 수 있다면 거기에 포인트를 두고 서비스를 설계 개발 하는것이 필요합니다.

그럼 AWS Lambda는 어디에 쓰는 물건 인가요?

AWS의 서비스들과 관계가 적은 복잡한 기능을 수행해야만 하는 특별한 나만의 맞춤 서버가 필요하다면 AWS Lambda 보다는 EC2를 활용하는 것을 권장합니다. AWS Lambda에서 제공 하는 Lambda Trigger를 이용하여 AWS 서비스와 연계된 이벤트를 처리할 때 사용하기에 유용합니다. 그 외에 API 기능을 API서버 없이 구현하기에도 유용합니다. 물론 API 서버로서 활용하고 싶다면 AWS Lambda 뿐만 아니라 API Gateway와 연계하여 설계하는 것이 더 효율적입니다. (이 내용은 다음 포스팅에서 다룹니다.)

AWS 서비스 내의 이벤트에서 트리거를 만들어서 처리할 수 있는 서비스들은 아래와 같습니다.(스크롤로 안보이는 S3, SNS 서비스도 포함되어 있습니다.)

구글링해서 보통 많이 볼 수 있는 사용 예는 http api 서버 기능 제공, S3를 활용한 이미지 자동 처리, SNS를 활용한 notification에 대한 자동 처리, CloudWatch 특정 이벤트 발생시 원하는 액션 처리등이 있습니다. 이 글에서는 비교적 구현하기 간단하고 많이 사용하는 http api 서비스를 배포하는 것을 목적으로 아래의 4가지 내용을 다루겠습니다.

1. Python으로 function 작성하기 및 패키징
2. S3를 활용한 function 배포
3. AWS Lambda 설정
4. Testing

1. Python으로 function을 작성하고 패키징 하기

Lambda function 코드를 업로드 하는 방법중 S3을 이용한 방법이기 때문에 AWS Lambda 서비스를 설정하기 전에 코드를 작성하여 S3 버킷에 올려 놓겠습니다.

시행착오를 줄이고자 먼저 두 가지 유의 사항을 알려드립니다.

  • Lambda function 코드를 작성할때 추가로 사용하는 python 모듈이 있을경우 해당 모듈을 모두 함께 패키징 해서 zip파일로 올려야 합니다.
  • 높은 확률로 Linux 에서 패키징을 해야 정상 작동 합니다.

각각의 Lambda function은  논리적으로 독립된 공간에서 실행 된다고 가정하는 것이 맞을 듯 합니다. 그래서 다른 function을 작성할때 이전에 이미 필요한 모듈을 같이 올렸다고 하더라도 import 에러가 발생하는 것을 볼 수 있습니다. 또한 AWS Lambda는 Linux 기반에서 실행되니 Windows 파일 시스템에서 패키징 한것은 제대로 돌아 가지 않을 확률이 매우 높습니다.

이번 예제에서는 MariaDB 혹은 MySQL을 사용하기 때문에 pymysql 모듈을 함께 패키징 해야 합니다. EC2 우분투 에서 패키징하고 awscli를 이용하여 S3으로 바로 업로드 하는 방법을 사용하겠습니다.

코드는 Windows 용 IDE 와 같은 편하신 환경에서 작성하시고 ec2 에 올려서 패키징 하겠습니다. 코드가 간단해서 vim 에디터 사용 가능하시면 직접 vim으로 작성하셔도 좋습니다. 코드는 aws에서 제공하는 example을 사용하겠습니다.
(http://docs.aws.amazon.com/lambda/latest/dg/vpc-rds-deployment-pkg.html)

예제에 나온대로 2개의 파일을 작성합니다. 먼저 메인 fuction인 getProducts.py 파일 입니다.

# getProducts.py
import sys
import logging
import rds_config
import pymysql

# rds settings
rds_host = "rds-instance-endpoint"
name = rds_config.db_username
password = rds_config.db_password
db_name = rds_config.db_name

logger = logging.getLogger()
logger.setLevel(logging.INFO)

try:
    conn = pymysql.connect(rds_host, user=name, passwd=password, db=db_name, connect_timeout=5)
except:
    logger.error("ERROR: Unexpected error: Could not connect to MySql instance.")
    sys.exit()

logger.info("SUCCESS: Connection to RDS mysql instance succeeded")


def handler(event, context):

    item_count = 0

    with conn.cursor() as cur:
        cur.execute("create table IF NOT EXISTS employee ( EmpID  int NOT NULL, Name varchar(255) NOT NULL, "
                    "PRIMARY KEY (EmpID))")
        cur.execute('insert into employee (EmpID, Name) values(1, "Joe")')
        cur.execute('insert into employee (EmpID, Name) values(2, "Bob")')
        cur.execute('insert into employee (EmpID, Name) values(3, "Mary")')
        conn.commit()

        cur.execute("select * from employee")
        for row in cur:
            item_count += 1
            logger.info(row)
            print(row)

    return "Added %d items from RDS MySQL table" % (item_count)

def handler 의 arguments 를보시면 단박에 감 잡으실 것 같은데 그것 맞습니다!  다른 부분은 python 언어의 일반적인 내용인데 여기서 handler, event, context에 대한 개념은 꼭 이혀두시길 권장 합니다.

handler

Lambda funtion이 호출될때 실행하는 handler를 위와 같은 형식으로 정의 해놓고  c나 java의 Main 함수 처럼 사용합니다. handler라는 이름은 당연히 수정하셔도 됩니다. main으로 실행할 함수를 정의하시고 추후 aws Lambda 콘솔에서 handler로 지정합니다. handler (event, context)의 포맷만 기억하시면 됩니다.

event

Lambda function을 실행할때 넘겨주는 파라메터들을 이 event로 받아서 처리 할 수 있습니다. http로 lambda를 실행할때 get, post, put, delete 등의 메서드를 통해서 전달 할 수 있습니다. JAVA servlet 에서 doGet, doPost 등에 있는 requst 와 같은 역할을 합니다.

context

Lambda function이 실행완료 하고 context를 통해 return 할 수 있습니다. 성공 메시지, 실패 메시지등을 리턴 할 수 있습니다. JAVA Servlet으로 보면 doGet doPost등의 response와 같은 역할을 합니다.

해당 내용은 튜토리얼에 자세하게 나와 있으니 그냥 넘어가지 마시고 꼭 보시기를 권장합니다.
(http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/python-programming-model-handler-types.html)

다음은 rds 계정정보가 있는 rds_config.py 파일 입니다.

rds_config.
db_username = "db_user_name"
db_password= "db_password"
db_name = "algopie"

저는 임시로 ubuntu 계정 디렉터리 하위로 repo/lambda/getProudcts 라는 디렉터리를 생성하고 그 하위에 위 2개의 파일을 위치 시켰습니다. 이제 패키징 합니다.

python module installation

앞서 설명 했듯이 패키징시 필요한 외부 모듈들이 모두 함께 포함되어야 실제 function 실행시 import 에러가 나지 않습니다. 그래서 외부 모듈들을 같은 디렉토리에 인스톨 하는 과정이 필요합니다. 본인이 작성한 모듈을 다른곳에 배포하기 위해 setup.py를 작성하는 작업도 필요합니다.

aws 튜토리얼에 다 나와 있습니다.
(http://docs.aws.amazon.com/ko_kr/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html)

ec2-shell> cd /home/repo/lambda/getProducts
ec2-shell> vi setup.py

아래는 setup.py 내용입니다. py_module 부분과 setup.name 부분이 중요합니다. 외부 모듈을 사용하므로 아래 예시처럼 pymysql을 명시해주세요.

from distutils.core import setup

py_modules = [
 'pymysql',
]

setup(
    name='getProducts',
    version='0.1',
    packages=[''],
    url='',
    license='',
    author='YourName',
    author_email='',
    description=''
)

이제  pip install 로 외부 모듈을 같은 위치에 인스톨 합니다. 명령어 패턴은 aws 튜토리얼에 나온대로 pip install module-name -t /path/to/project-dir 입니다. 이미 change directory로 소스가 위치한 root 디렉터리에 와 있으므로 아래와 같이 typing  합니다.

 ec2-shell> pip install pymysql -t ./ 

성공적으로 모듈이 설치되면 아래와 같은 메시지가 나옵니다.

zip 으로 압축

하위디렉터리까지 몽땅 압축합니다.

ec2-shell> zip -r getProducts.zip *

2. S3를 활용한 function 배포

s3 버킷으로 업로드

awscli를 설치하신 상태라면 아래 커맨드라인으로 업로드 하시면 됩니다. 혹시 awscli가 무엇인지 모르시거나 아직 설치 하지 못하신 분들께서는 이전 포스트에 상세히 작성해 놓았으니 보시고 awscli를 설치해주신 다음에 아래 순서를 진행해 주세요. s3를 관리하는 cli 명령어 옵션에 대한 설명도 링크로 걸어둡니다.

링크1: AWS ec2 우분투(ubuntu)에 awscli 설치 하기

링크2: AWS CLI를 사용하여 ec2에서 s3로 업로드/다운로드 하기 (우분투 Ubuntu)

ec2-shell> aws s3 mb s3://algopie-functions
make_bucket: algopie-functions

ec2-shell> aws s3 cp ./getProducts.zip s3://algopie-functions/
upload: ./getProducts.zip to s3://algopie-functions/getProducts.zip

이제 가장 긴 고비를 넘기셨으니 얼마 안남았네요

3. AWS Lambda 설정

AWS 콘솔에서 AWS Lambda를 선택하면 제일 처음 대시보드가 나옵니다. Create Function 버튼을 누르고 새로운 funtion을 생성합니다. (저는 이미 2개의 사용중인 funtion이 있어서 아래와 같이 나오네요)

다음으로 넘어가면 각종 예제들이 포함되어 있는 Blueprint 항목이 나옵니다. 하지만 여기서는 그냥 그대로 따라하기 보다는 RDS와 연동하여 테이블 데이터를 쿼리하여 Json 포맷으로 뿌려주는 HTTP API를 만들기로 했으니 왼쪽 메뉴에서 “Configure Function” 버튼을 눌러줍니다. 아래와 같이 지원하는 언어는 5가지 입니다. 이 중에서 저는 Python으로 코드를 작성하겠습니다. (Python은 저도 몇번 접해 보기만 한 언어입니다. 이 참에 제대로 익혀 보자는 생각으로 골랐습니다. )

일단 이번 예제에서는 짧게 넘어가겠지만 AWS Lambda로 API 서비스를 구현하실 분들께서는 생각을 좀 해보셔야 하는 것이 있습니다. 바로 위 화면중 “Name”에 넣어야 할 Function Name 입니다. HTTP API 설계를 생각하시는 분들은 이미 머리속에 RESTful 이라는 단어가 있을 것 입니다. AWS Lambda 로 직접 API 서비스를 하실 계획을 가지신 분들은 별로 없을 것 같습니다. AWS에서도 API Gateway와 연동할 것을 권장하고 있습니다. 저도 API Gateway를 활용하여 RESTful한 멋진 API 서비스를 만들어 보시는 것을 추천합니다. 물론 이 AWS Lambda function을 direct로 HTTP API 서비스로 배포할 생각이 없으셔도 AWS Lambda Funtion naming 관련하여 시간을 내서 검색도 해보시고 본인에게 가장 설득력 있는 Naming 규칙을 사용하실 것을 권장합니다.

Name* : getProducts

네 그렇습니다. 전혀 RESTful 한 함수 이름이 아니야! 라고 할 수 있습니다. RESTful 한 naming 은 추후 API Gateway를 다룰 때 사용합니다!

Runtime*: Python 2.7

그리고 다음 아래로 내려 갑니다. 다음은 코드를 어디서 업로드할지 선택해주는 화면 입니다. 아래 화면과 같이 Upload a file from Amazon S3을 선택하시고 아래 s3 link URL에 아까 만들어준 s3 URL을 적어줍니다. 저와 동일하게 하셨다면 link url은  https://s3.amazonaws.com/algopie-functions/getProducts.zip 입니다.

환경변수를 셋팅하는 기능도 제공합니다. key-value 페어형태로 해당 functon 전역에서 사용할 수 있는 환경 변수를 셋팅할 수 있습니다. 이 예제에서는 넘어갑니다.

아래는 중요한 셋팅 내용들이 있으니 이왕 해보시는 김에 확실히 익혀두시기를 권장합니다. 위에 한번 중요하게 handler에 대한 부분을 작성했습니다. 드디어 여기에 나오는군요.

 

Handler*: 위 python 코드에서 handler(event, context) 를 정의 하였는데 Handler에 위에 정의해 놓은 함수명을 적어줍니다. 예시의 getProducts.py의 handler 이므로 getProducts.handler 입니다. 일종의 네임스페이스 개념이라고 생각하시면 될듯 합니다.

Advanced setting: 메모리나 타임아웃등에 대하여 셋팅할 수 있고 저는 기본으로 두고 진행했습니다. 튜토리얼을 보면 설명이 나옵니다. 간단하게 설명하면 디테일하게 셋팅하여 퍼포먼스를 높일 때 사용합니다.

Roll*: AWS의 IAM롤에서 lambd 전용 롤을 만들고 적용하겠습니다. 위 예시와 같이 new roll을 선택하시고 roll name을 적으시면 새로운 롤을 만드는 창이 오픈됩니다.

lambda basic execution 을 선택하고 policy name도 제공해주는 것을 사용하였습니다. IAM 관련하여서는 추후에 깊게 다룰 예정입니다. 이전 포스팅에서 awscli 의 access key id와 secret key를 발급 할때 다룬 내용을 참고하셔도 됩니다.

링크: IAM  유저생성

다시 Lambda 설정으로 돌아옵니다. 드디어 끝이 보이네요.
VPC는 virtual private cloud 로 쉽게 설명하면 가상 개인 네트워크 정도로 말 할 수 있습니다. vpc 셋팅을 통해서 aws 내부 접근과 외부접근에 대한 제어를 할 수 있습니다. 자세한 설명은 aws docs링크를 보시는 것을 권장합니다. http://docs.aws.amazon.com/ko_kr/AmazonVPC/latest/UserGuide/VPC_Introduction.html

Secret Group*: 저의 경우 Lambda 전용으로 하느를 만들었고 inbound, outbound 모두 모든 포트를 anywhere 로 오픈했습니다. Next 버튼을 누르면 review 화면이 나오고 마지막으로 creating 버튼을 누르면 약간의 시간을 대기하고 모든 과정이 완료됩니다. “Save and Test” 버튼을 누르시고 다음 과정으로 넘어 갑니다.

자 드디어 대망의 테스팅 입니다. 과연 잘 작동할까요? 두근두근!

4. Testing

Test 버튼을 누르시면 잠시후 테스트 결과가 나옵니다.

위에 우리가 작성한 대로 employee 라는 테이블을 생성하고 3개의 row를 insert 하여 위와 같은 결과가 리턴 되었습니다.  Lambda function 셋팅 자체보다는 선행해야할 셋팅들이 많아서 예상보다 긴 길이 되었습니다. 긴글 따라 오시느라 수고 많으셨습니다.