서버리스 마이크로서비스(Serverless Microservice) with Zappa -2-

이전의 포스팅Zappa와 함께하는 Serverless Microservice - 희망편이었다면

이번의 포스팅은 Zappa와 함께하는 Serverless Microservice - 파멸편이라고 할 수 있다.

Zappa를 도입하면서 삽질했던 것들을 적어보려한다.

삽질 1. Package

Lambda에서는 순수 Python으로 작성된 모듈만 사용할 수 있다. 별도의 컴파일 과정이 불가능하기 때문이다.

그래서 MySQL-Python 모듈도 쓰지못하는 문제가 있었다.

이럴 때는 Lambda가 돌아가는 환경과 제일 비슷한 Amazon Linux에서 패키지를 빌드해서 사용하는 방법과

같은 기능을 하는 다른 모듈로 대체하는 방법(MySQL-Python -> pymysql)이 있다.

하지만 Zappa에서는 미리 컴파일된 패키지를 제공해서 이를 가능하게 해준다. 모든 패키지는 아니고 사람들이 자주 쓰는 일부 지원하는 패키지가 있다.

찾아보니 docker-lambda라는 프로젝트도 있어서 활용할 수 있을 것 같다. Amazon Linux의 도커 이미지로 도커 컨테이너 안에서 Lambda에 사용할 패키지를 구성해서 테스트해볼 수 있다고 한다.

삽질 2. VPC 설정

배포는 성공했다. 하지만 여전히 500 Server Error 만 뱉어대고 있다.

로그를 살펴보니 DB에 접근을 하지 못하고 있었다.

지금 사용하고 있는 DB는 AWS RDS를 VPC안에서 사용하고 있기 때문에 Lambda에서 접근하지 못했던 것이다.

따라서 Lambda에서도 VPC를 사용하면 문제해결이었다. DB에서 사용하고 있는 VPC의 Subnet과 적절한 Securety Group을 zappa_settings.json 파일에 다음과 같이 설정을 추가하자.

1
2
3
4
5
6
{
"vpc_config": {
"SubnetIds": [ "subnet-12345678" ],
"SecurityGroupIds": [ "sg-12345678" ]
}
}

이제 DB에도 잘 연결되고 만사형통인 줄 알았지만 그게 아니었다.

삽질 3. Internet Gateway 설정

보통의 기능은 정상적으로 동작하지만 외부 망으로 요청을 보내면 연결을 하지 못해서 에러가 났다.

구글링을 해보니 VPC를 사용하는 Lambda는 Public Internet에 접속하지 못한다고 한다. 하지만 역시 방법이 있었다.

해결과정

1. VPC 설정

VPC를 하나 생성하자. 이미 있다면 그것을 사용하면 된다.
CIDR Block은 충분히 크게 잡는 것이 좋다.(ex. 192.168.0.0/16)

2. Subnet 설정

2개의 Subnet이 필요하다. CIDR Block이 겹치지 않게 설정하자.
하나는 Public Subnet 으로, 다른 하나는 Private Subnet 으로 사용할 것이다.

3. Internet Geteway 설정

1번에서 생성(사용)한 VPC를 attach한 Internet Gateway를 생성한다.

4. NAT Gateway 설정

2번에서 생성한 Public subnet 을 선택하고 생성함(Elastic IP 필요)

5. Route Table 설정

1번에서 생성(사용)한 VPC를 사용하는 2개의 Route Table이 필요하다.
이 역시 Public Route Table, Private Route Table 이다.

  • Public Route Table 의 Routes 탭에서 Destination: 0.0.0.0/0, Target을 3번에서 생성한 Internet Gateway로 설정하고, Public VPC를 연결한다.
  • Private Route Table 의 Routes 탭에서 Destination: 0.0.0.0/0, Target을 4번에서 생성한 NAT Gateway로 설정하고, Private VPC를 연결한다.

여기까지하면 AWS에서 설정은 완료된다.

6. zappa_settings.json 파일을 수정한다. SubnetIdsPrivate SubnetSubnet Id를 넣는다.
1
2
3
4
5
6
{
"vpc_config": {
"SubnetIds": [ "subnet-privateSubnetId" ],
"SecurityGroupIds": [ "sg-12345678" ]
}
}

설정이 다 되었다면 zappa update로 다시 배포를 하면 된다.

삽질 4. 잔잔한 버그들!

아주 간헐적으로 배포를 하면 "TypeError: 'NoneType' object is not callable" 이런 에러를 뱉으면서 에러를 내뿜었다.

zappa tail로 로그를 확인해보니 ModuleNotFoundError: No module named 'numpy' Import 에러가!!

로컬에서 잘 동작했고 virtualenv에 패키지가 설치되어 있는지 확인했는데도 뭐가 문제인지 잘 몰랐다.

그래서 혹시 나와 같은 이슈가 있을까 싶어서 찾아봤더니 이런 이슈가 등록되어 있었다.

나와 같은 고민의 냄새가 난다...

평소같았으면 그저 누군가가 해결해주기를 기다렸겠지만 뭔가 파보고 싶었다.

조금 파보니 Zappa가 패키징을 할 때 로컬에서 .whl 파일을 캐싱해서 쓰는데, 나중에 다시 쓰려할 때 파일이 손상되어 있다면 exception이 발생하는 버그를 발견했다.

그래서 버그를 잡고 구글 번역기의 힘을 빌려 아래처럼 코멘트를 달았다.

Thanks to Google Translate! 👍

그리고 조금후에 PR을 달라는 답변이와서 그렇게 난 Zappa의 contributor가 될 수 있었다.(막 엄청 대단한 일은 아니지만 처음이라…)

그렇게 내가 기여한 부분이 포함되어 새 버젼이 릴리즈 되어서 사용해보는데 이번엔 다른 버그를 발견했다.

그래서 바로 다시 PR을 보냈다.(뭐든 처음이 어렵지 두번째는 아니다.)

그러다가 Zappa 슬랙을 눈팅하던 중 나처럼 버그를 발견한 사람이 나왔다.

버그 발견!

이 떄다 싶어 바로 리액션을 줬다.

내가 고쳐줄게!

그렇게 머지가 되서 핫픽스로 릴리즈 되었다. Change Log에 내 이름이!

이 맛에 오픈소스하는건가...

삽질기라고 시작은 했지만

사실 꽤나 재미있었던 과정이었다.

이번 삽질로 AWS 기반의 네트워크 환경에 대해서 좀 더 알게되었고, 그동안 오픈소스에 관심은 많았지만 어떻게 시작을 해야할지 몰랐었는데 이렇게 좋은 오픈소스 프로젝트에 기여도 해볼 수 있어서

뭔가 삽질기보단 성장기라고 해야 더 적절할 것 같다.

앞으로 더 성장(삽질)해야겠다.

서버리스 마이크로서비스(Serverless Microservice) with Zappa -1-

내가 입사할 때 까지만 해도 회사의 백엔드는 Monolithic Architecture로 하나의 거대한 백엔드 시스템에서 여러가지 기능을 담당하고 있었다.

그리고 몇가지 이유로 이제 Monolithic Architecture를 고집할 수 없겠다 싶어 점진적으로 Microservice Architecture화 하기로 결정했다.

1차적으로 백엔드의 특정 기능을 담당하는 API 서버를 분리하기로 결정했는데 이번 기회에 요즘 핫한 Serverless Architecture를 시도해보기로 했다.

AWS의 Lambda, Azure의 Functions 등 여러가지 서버리스 아키텍쳐를 위한 제품들이 나오면서 서버리스 아키텍쳐(Serverless Architecture)가 뜨고 있다.

아니, 이미 꽤나 인기있는 아키텍쳐로 자리잡은 것 같다.

언젠가 서버리스 아키텍쳐를 한번 사용해보고 싶었는데 마침 기회가 왔다.

Microservice와 Serverless

개인적으로 이 둘의 궁합은 매우 잘 맞는다고 생각한다.

마이크로서비스(Microservice)는 하나의 거대한 서비스를 백엔드 시스템으로 사용하는 것이 아닌 여러 개의 작은 모듈처럼 구축한 서비스를 백엔드 시스템으로 연결하여 사용하는 것이다.

각 서비스는 프로그래밍 언어, 프레임 워크 등을 독립적으로 선택할 수 있고, 다른 서비스들과 독립적으로 배포가 될 수 있어서 하나의 서비스로 사용할 수 있어야 한다.

이는 서비스들이 해당 서비스에 특화된 프로그래밍 언어, 프레임워크, 배포환경 등을 선택할 수 있다 는 장점을 가져온다.

또한 기존 다른 서비스들에 의존성이 없기 떄문에 서비스를 배포할 때 고려해야할 것들을 줄일 수 있다.

서버리스(Serverless)는 말 그대로 서버가 없는 환경을 의미한다.

클라우드 서비스가 발전하면서 물리적인 하드웨어 서버가 클라우드 환경의 가상머신으로 대체 된 것처럼, 여러 API들을 수행하는 백엔드 시스템이 각 API들을 수행하는 하나의 코드만을 따로 배포할 수 있게 되었다.

개발자는 그저 비즈니스 로직에 맞는 코드만 작성하면 되고 이를 배포하면 된다. 다른 인프라나 데이터베이스 등의 부가적인 환경에 대해서 신경쓰지 않아도 된다는 것을 의미한다.

Microservice와 Serverless가 공통적으로 추구하는 방향은 비즈니스 로직에 집중하는 것이다.

Microservice를 사용해 서비스를 구축하고 이를 Serverless 환경으로 배포한다면 개발자는 다른 것에는 신경쓰지 않고 오로지 비즈니스 로직을 충실히 수행하는 서비스를 구축하고 배포하면 된다.

자, 그럼 서버리스의 세계로 가봅시다.

회사에서 AWS를 사용하고 있기 때문에 AWS Lambda의 선택은 당연했지만, Lambda를 어떻게 쓰는가에 대한 선택지는 매우 다양했다.

Lambda에서 실행할 코드를 작성해 AWS 콘솔에서 업로드하고 API Gateway도 직접 설정하는 방법도 있지만,

요즘에는 서버리스 프레임 워크도 많이 나와서 매우 쉽게 서버리스 아키텍쳐를 구축할 수 있다.

또한 서버리스를 위한 프레임 워크도 매우 다양한데(Serverless, Apex, Chalice, Zappa 등) CTO님의 제안으로 Zappa라는 프레임 워크를 사용하기로 했다.

Zappa는

AWS Lambda & API Gateway에 Python WSGI Application을 배포해 서버리스 아키텍쳐를 구성해주는 프레임 워크다.

지금까지 사용하며 겪은 바로 Zappa의 (매우 주관적인)장점은 다음과 같다.

  • Python WSGI Application을 배포하기 떄문에 코드의 변경없이 Django, Flask와 같이 WSGI Application을 그대로 배포할 수 있다.(내 생각에 이게 가장 큰 장점이 아닐까 싶다.)
  • 서버리스로 가기 위한 최소한의 셋팅을 다 알아서 해준다. 프로젝트를 압축하여 Lambda에 배포하고 API Gateway에서 Lambda를 사용할 수 있게 알아서 설정해준다. 또한 몇가지 설정만 추가한다면 다른 AWS의 서비스도 사용가능하다.
  • API Gateway의 Stage를 구성하기가 매우 쉽다. 그냥 설정파일에 stage용 설정을 추가하기만 하면 끝이다. 도메인까지 설정해서 쓰면 요긴하기 쓸 수 있다.

하지만 뭐든 그렇듯 물론 장점만 있지는 않다. 내가 생각하는 Zappa의 단점은

  • 모니터링이 힘들다. New Relic을 붙여서 사용하려고 했더니 뭔가 로그가 불규칙적으로 들어온다. 문제가 뭔지 잘 모르겠다.(해결방법을 아시는 분 있다면 공유좀…)
  • 그래서 AWS X-Ray를 쓰려했더니 Node.js, Java, .Net만 지원하다고 한다. 그래서 찾아보니 xrayvision이라는 프로젝트가 있어서 사용 중이다. 하지만 X-Ray가 모니터링 하기에 뭔가 부족한 느낌의 서비스인 것 같다.
  • 아직 성숙한 프로젝트가 아니다. 그렇다고 막 버그 투성이인 프로젝트는 아니다. 그럼에도 단점에 적은 이유는 Zappa로 배포하고 테스트를 진행하던 중 뭔가 안되는 부분이 있었는데, 알고보니 아직 지원을 안하는 것이었다. 하지만 다행히(?) 내가 삽질하던 그즈음에 누군가 PR를 보냈고 머지 되어 지금은 잘 사용하고 있다. 이런것을 보면 오히려 좋은 오픈소스 프로젝트에 기여할 기회가 많다 고 생각할 수도 있다.

자, 그럼 Zappa를 써봅시다.

Zappa Github Page에 가서 보면 알겠지만 매우 간단하다.

3줄로 요약하면

1
2
3
(env) $ pip install zappa
(env) $ zappa init
(env) $ zappa deploy

3줄의 명령어로 Python WSGI Application을 AWS Lambda & API Gateway에 배포할 수 있다.

시작하기 전에

Zappavirtualenv 안에 설치 되어야한다. Zappa가 배포할 때 virtualenv안에 있는 패키지들을 가져와서 압축하기 때문이다.

Zappapip를 사용해서 설치하면 된다.

1
(env) $ pip install zappa

Zappa를 사용하기 위해 설정파일을 만들어야하는데 zappa init 이라는 명령으로 사용하면 된다.

그럼 다음과 같이 몇가지 입력을 받고 설정파일을 만들어주는데 입력은 그냥 디폴트 값으로 넣어도 무관하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
(env) $ zappa init

███████╗ █████╗ ██████╗ ██████╗ █████╗
╚══███╔╝██╔══██╗██╔══██╗██╔══██╗██╔══██╗
███╔╝ ███████║██████╔╝██████╔╝███████║
███╔╝ ██╔══██║██╔═══╝ ██╔═══╝ ██╔══██║
███████╗██║ ██║██║ ██║ ██║ ██║
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝

Welcome to Zappa!

Zappa is a system for running server-less Python web applications on AWS Lambda and AWS API Gateway.
This `init` command will help you create and configure your new Zappa deployment.
Let's get started!

Your Zappa configuration can support multiple production stages, like 'dev', 'staging', and 'production'.
What do you want to call this environment (default 'dev'):

AWS Lambda and API Gateway are only available in certain regions. Let's check to make sure you have a profile set up in one that will work.
Okay, using profile default!

Your Zappa deployments will need to be uploaded to a private S3 bucket.
If you don't have a bucket yet, we'll create one for you too.
What do you want call your bucket? (default 'zappa-8wjmc0weu'):

What's the modular path to your app's function?
This will likely be something like 'your_module.app'.
Where is your app's function?: app.__init__.app

You can optionally deploy to all available regions in order to provide fast global service.
If you are using Zappa for the first time, you probably don't want to do this!
Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]:

Okay, here's your zappa_settings.json:

{
"dev": {
"app_function": "app.__init__.app",
"aws_region": "ap-northeast-1",
"profile_name": "default",
"s3_bucket": "zappa-8wjmc0weu"
}
}

Does this look okay? (default 'y') [y/n]:

Done! Now you can deploy your Zappa application by executing:

$ zappa deploy dev

After that, you can update your application code with:

$ zappa update dev

To learn more, check out our project page on GitHub here: https://github.com/Miserlou/Zappa
and stop by our Slack channel here: https://slack.zappa.io

Enjoy!,
~ Team Zappa!

위에 보이는 것처럼 배포는 zappa deploy <stage> 를 사용하면 된다.

최초 배포 후에는 zappa update <stage>를 사용하여 업데이트를 하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(env) $ zappa deploy dev
Calling deploy for stage dev..
Creating zappa-dashboard-dev-ZappaLambdaExecutionRole IAM Role..
Creating zappa-permissions policy on zappa-dashboard-dev-ZappaLambdaExecutionRole IAM Role.
Downloading and installing dependencies..
Packaging project as zip.
Uploading zappa-dashboard-dev-1505403223.zip (10.5MiB)..
100%|█████████████████████████████████████████████████| 11.0M/11.0M [00:04<00:00, 2.34MB/s]
Scheduling..
Scheduled zappa-dashboard-dev-zappa-keep-warm-handler.keep_warm_callback with expression rate(4 minutes)!
Uploading zappa-dashboard-dev-template-1505403250.json (1.6KiB)..
100%|█████████████████████████████████████████████████| 1.66K/1.66K [00:00<00:00, 3.32KB/s]
Waiting for stack zappa-dashboard-dev to create (this can take a bit)..
100%|█████████████████████████████████████████████████| 4/4 [00:14<00:00, 5.26s/res]
Deploying API Gateway..
Deployment complete!: https://pqj13rwzi5.execute-api.ap-northeast-1.amazonaws.com/dev

하지만

이렇게 모든일이 일사천리로 끝난다면 매우매우 좋았겠지만 꼭 예상하지 못한 이슈가 발생하기 마련이다.

Zappa를 도입하기로 했을 때 여러가지 이슈로 삽질을 하게 되었는데,(물론 내가 부족해서 그랬던것이 대부분이지만)

그런 이슈들을 기록으로 남겨두면 좋을 것 같아 다음 포스팅으로 써야겠다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×