AWS Elastic Beanstalk

AWS Beanstalk는 AWS의 PaaS(Platform-as-a-service) 서비스입니다.

PaaS(Platform-as-a-service) 서비스란?

사전 구성된 플랫폼 환경에 애플리케이션만 배포해서 실행할 수 있게 해주는 서비스입니다.

웹 애플리케이션 실행 환경을 구성하기 위해서 일반적으로 아래의 과정이 필요합니다.

  • EC2 인스턴스 생성
  • OS 설치
  • 웹 서버(nginx) 설치
  • 데이터베이스 설치(선택)
  • 실행 환경 설치(Java면 Java와 Tomcat 등, Node면 Node)
  • 환경변수 설정
  • 실행 중단/재시작

이러한 과정을 수동으로 하는 대신, 이미 잘 구성된 플랫폼 환경을 준비해놓고 애플리케이션만 배포해서 실행할 수 있게 됩니다.

AWS Elastic Beanstalk와 유사한 다른 업체의 서비스

사용하기 쉬운가?

네, 적응하면 쉽습니다. (적응하면 안 편한게 어디 있을까…)

혹시 처음에 사용하기 힘드실까봐 실습 예제를 준비해봤습니다.

[실습] 무작정 따라하기: node 웹 애플리케이션 배포

이 예제는 ‘무작정 따라하기’입니다.
이게 뭔지 따지지 말고, 처음에는 일단 그대로 따라해보시기 바랍니다.

1. 프로젝트 디렉터리 구성

  • /frontend (프론트엔드): create-react-app으로 생성한 React 프로젝트
  • /api-server (백엔드): node + express로 구성된, API 서버 역할 + 프론트엔드 클라이언트 파일도 제공(serve static files)

2. 현행 배포 방법(AS-IS)

  1. $ yarn build로 클라이언트 번들 파일 등 정적 리소스 생성
  2. 백엔드 node-express 프로젝트의 /public 디렉터리에 1.에서 생성한 클라이언트 번들 파일 등 정적 리소스 복사
  3. 백엔드 node-express 프로젝트를 운영 환경에 복사
  4. $ tsc로 컴파일
  5. $ node dist/app.js 실행

TypeScript를 사용하는 node 애플리케이션의 기본적인 배포 방법입니다.

3. AWS IAM User 생성

AWS에서 Elastic Beanstalk를 위한 CLI를 제공합니다.

EB CLI (Elastic Beanstalk Command Line Interface)를 사용하려면 AWSElasticBeanstalkFullAccess 권한이 있는 IAM User가 필요합니다.

  1. Management Console에서 IAM을 클릭합니다. IAM in Service List

  2. ‘사용자’를 클립합니다. User in IAM Management Console

  3. ‘사용자 추가’를 클릭합니다. Add User

  4. 빌드 시스템인 CircleCI에서 사용할 것이므로 username을 circleci로 하겠습니다.
    이 계정으로는 AWS Management Console에 접속하지 않을 것이므로 액세스 유형에서 프로그래밍 방식 액세스만 체크해줍니다. User Details

  5. AWSElasticBeanstalkFullAccess 권한을 연결해줍니다.
    (그룹으로 넣어도 상관없고, 권한만 들어가면 됩니다.) User Details

  6. 생성된 Access Key Pair를 저장합니다. Access Key Pair

  7. circleci 유저가 생성되었습니다. User Created

4. eb-cli 설치

Elastic Beanstalk 명령줄 인터페이스(EB CLI) 설치

위 링크로 들어가시면 설치 방법이 나와 있습니다.

macOS에서는 간단합니다.

$ brew install awsebcli

awsebcli 패키지는 pip에 먼저 제공되고 며칠 뒤에 homebrew에도 업데이트됩니다.

5. aws-cli, eb-cli 프로필

eb-cli는 aws-cli를 확장한 프로그램이라서 aws-cli에서 사용하는 ~/.aws/credentials 파일을 공유합니다.

~/.aws/credentials 파일을 수정해서 circleci 사용자 자격증명을 추가합니다.

[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[circleci]
aws_access_key_id=AKIAI44QH8DHBEXAMPLE
aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY

주의: 위에 나온 aws_access_key_idaws_secret_access_key은 예시입니다.

~/.aws/config 파일도 수정합니다. (각자 환경에 맞게 설정합니다.)

[default]
region=ap-northeast-2
output=json

[profile circleci]
region=ap-northeast-2
output=json

~/.aws/config 파일에서는 [profile xxx]이라고 입력하는 것에 주의하십시오.

더 간편한 방법은 aws configure --profile [프로필 이름]를 이용하는 것입니다.

$ aws configure --profile circleci
AWS Access Key ID [None]: AKIAI44QH8DHBEXAMPLE
AWS Secret Access Key [None]: je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
Default region name [None]: ap-northeast-2
Default output format [None]: json

6. eb init

프로젝트 디렉터리를 eb-cli와 연동하도록 초기화합니다.

$ cd [프로젝트 디렉터리 경로]
$ eb init --profile circleci

아래와 같이 입력합니다.

Select an application to use
1) [ Create new Application ]
(default is 1): 1

애플리케이션 이름을 입력합니다. (예: oss-crane-saas)

Enter Application Name
(default is "eb"): oss-crane-saas
Application oss-crane-saas has been created.

플랫폼을 선택합니다. (예: Node.js)

Select a platform.
1) Node.js
2) PHP
3) Python
4) Ruby
5) Tomcat
6) IIS
7) Docker
8) Multi-container Docker
9) GlassFish
10) Go
11) Java
(default is 1): 1

SSH로 직접 접속은 안 합니다.

Do you want to set up SSH for your instances?
(y/n): n

이제 프로젝트 디렉터리 루트에 .elasticbeanstalk 디렉터리가 생성되고, ./elasticbeanstalk/config.yml 파일이 생성됩니다.

global:
  application_name: oss-crane-saas
  default_platform: Node.js
  default_region: ap-northeast-2
  workspace_type: Application

7. eb create

eb create 명령으로 Environment를 생성합니다.

Environment는 계층적으로 이전 단계에서 생성한 Application 아래에 여러 개가 있을 수 있습니다.

예를 들면, oss-crane-saas라는 Applicationoss-crane-saas-dev, oss-crane-saas-staging, oss-crane-saas-prod 등의 Environment가 있을 수 있습니다.

흔히 개발 환경, 테스트 환경, 운영 환경 할 때의 그 환경입니다.

이제 테스트 환경 oss-crane-saas-test를 만들어보겠습니다.

$ eb create --profile circleci
Enter Environment Name
(default is eb-dev): oss-crane-saas-test
Enter DNS CNAME prefix
(default is oss-crane-saas-test): oss-crane-saas-test

...

자동으로 준비 과정이 진행되면서 새로운 Environment가 생성됩니다.

새 Environment에 자동으로 현재 디렉터리의 소스코드를 사용해서 사전 정의된 동작을 실행합니다.

아마 처음에는 실패할 것입니다. 다음을 살펴봅시다.

8. eb deploy

방금 전에 새 Environment를 생성하면서, 동시에 현재 소스코드 상태로 배포가 자동으로 진행되었습니다.

Elastic Beanstalk에서 Node.js 플랫폼을 선택한 경우, 배포시 기본 동작은 대략 아래와 같습니다.

  • 현재 디렉터리 소스 코드 업로드 (.gitignore, .ebignore에 명시한 파일 제외)
  • 환경변수 설정
  • npm install
  • npm start

아직 프론트엔드 프로젝트를 빌드해서 번들 파일을 뽑지도 않았고, 번들 파일을 서버의 /public 디렉터리에 복사하지도 않았고, TypeScript로 된 서버 소스코드를 js로 빌드하지도 않았습니다.

그래서 방금 전에 한 것은 실패하였습니다.

이제 여러분의 PC가 빌드 시스템(여기서는 circleci라고 가정)이라고 생각하고 기본적인 빌드 과정을 진행하십시오.

주의: 빌드 중에서 환경변수를 잘 설정해야 합니다. (EXPORT나 .env 파일 등으로)
oss-crane-saas-test 환경에서 동작할 환경변수로 설정해줘야 합니다.

/api-server
  /public
    index.html
  /dist/
    app.js

환경 구성 설정의 일부부을 변경해야 합니다.

.ebextensions/environment.config 파일을 생성합니다. (사실 파일 이름은 확장자만 .config이면 상관 없습니다.)

option_settings:  
  aws:elasticbeanstalk:container:nodejs:
    ProxyServer: "nginx"
    NodeVersion: "8.15.0"
    GzipCompression: true
    NodeCommand: "node dist/app.js"
  aws:elasticbeanstalk:application:environment:
    DATABASE_HOST: ...
    DATABASE_NAME: ...
    DATABASE_PASSWORD: ...
    DATABASE_USER: ...
    HTTP_PORT: 8081
  aws:autoscaling:launchconfiguration:
    InstanceType: t2.micro
    SecurityGroups:
      - security-group-id-1
      - security-group-id-2

내용은 이렇습니다.

Configuration

  • ProxyServer: nginx (기본값: 건드릴 필요가 없습니다.) 혹은 웹서버가 필요없고 트래픽을 application으로 바로 붙이려면 none 하면 됩니다. 이때는 express가 80(http) 포트를 listening 해야합니다.
  • NodeVersion: 실행할 Node의 버전을 적어줍니다.
  • GzipCompression: 웹 서버에 내장된 압축 기능 사용 여부입니다. (건드릴 필요가 없습니다.)
  • NodeCommand: 준비 과정을 다 끝내고 애플리케이션을 실행할 때 사용할 명령입니다.
  • SecurityGroups: 외부 RDS와 연결하는 등, 추가 보안 그룹을 지정할 때 사용합니다.

ProxyServer를 nginx로 고르면, 기본적으로 80(http) 포트로 트래픽이 유입됩니다.

그러면 파일 같은 static resource는 바로 응답을 주고, 나머지는 8080(http-alt) 포트로 내부 리다이렉트 됩니다. 여기까지는 nginx 기본 세팅이고, Elastic Beanstalk에서 애플리케이션 플랫폼을 Node.js로 선택하면, 이 8080 트래픽을 다시 8081 포트로 내부 리다이렉트합니다.

그래서 node express가 8081포트를 listening 하도록 해야합니다.

준비되었으면 아래의 명령으로 다시 배포합니다.

$ eb deploy --profile circleci

조금 기다리면 결과가 나옵니다.

9. static assets

백엔드 api-server는 URL Path에 따라 다른 서비스를 합니다.

express Route로 아래와 같이 설정했습니다.

  • /api/**/*: API 서비스, application/json으로 응답
  • 그외: /index.html 응답

Elastic Beanstalk에는 웹 서버가 들어가므로 정적 파일을 애플리케이션 서버가 담당할 필요가 없습니다.

그래서 일부 URL Path에 대해서 정적 파일을 서비스하도록 라우팅 할 수 있습니다.

아까 만들었던 .ebextensions/environment.config 파일을 열고 아래와 같이 설정을 추가합니다.

option_settings:
  // ...

  aws:elasticbeanstalk:container:nodejs:staticfiles:
    /static: /public/static
    /favicon_black.jpg: /public/favicon_black.jpg
    /index.html: /public/index.html
    /asset-manifest.json: /public/asset-manifest.json
    /manifest.json: /public/manifest.json
    /service-worker.js: /public/service-worker.js

위의 설정은 예시입니다. 각자 환경에 맞게 구성하시면 됩니다.

그러면 위 경로에 대해서 애플리케이션 서버로 리퀘스트를 전달하지 않고 웹서버가 바로 정적 자원을 서비스합니다.

10. eb logs

여기까지 잘 되었다면 이제 로그를 확인하는 방법을 알아보겠습니다.

$ eb logs --profile circleci

그러면 로그가 와장창(?) 뜹니다. 주요 로그 파일의 마지막 100줄을 보여줍니다.

파일로 리다이렉트 해서 저장해서 보시면 되겠습니다.

$ eb logs --profile circleci > oss-crane-saas-test.log

후속 과제

여기까지 한꺼번에 잘 따라하셨으면, 그것은 여러분이 아주 똑똑한 덕분입니다.

만약 잘 안 된다면, 그것은 저의 설명이 부족한 탓입니다. AWS Elastic Beanstalk가 매우 어려운 탓입니다.

사실 .ebextenstions에서 config 파일로 플랫폼을 커스터마이징하는 일은 매우 까다롭습니다.

어떤 AWS 솔루션 아키텍트도, 웬만하면 .ebextenstions를 건드리지 않고 사용할 수 있는 환경이면 가장 좋다고 했습니다.

코드로서의 인프라 도구인 Terraform으로도 역시 AWS Elastic Beanktalk를 관리할 수 있습니다. (단, Environment 수준까지, deploy는 eb cli가 필요합니다.)

그래서 다음에는언젠가는 terraform으로 AWS Elastic Beanstalk 관리하기에 대해서 알아보겠습니다.

감사합니다. :)


References