Amazon Web Services 한국 블로그

AWS Lambda와 Slack을 이용한 DevOps Chatroom 구현하기

Slack이 제공하는 협업을 위한 채팅을 통해 DevOps팀에서 실제 운영이나 서로 의견이나 정보를 다양한 플랫폼에서 동시에 주고 받을 수 있습니다. 예를 들어, 스마트폰 앱을 이용하거나 맥에서는 클라이언트 도구도 제공하고 있습니다. 메시지를 다양한 형태로 전달할 수 있는 Amazon SNS(Simple Notification Service)를 통해 이벤트를 발생시키고, AWS Lambda 는 SNS를 통해 전달된 메시지를 이벤트 트리거(Event trigger)를 통해 원하는 코드를 직접 수행 할 수 있습니다.

Slack은 API를 통해 메시지를 보낼 수 있는 방법을 제공하고 있습니다. AWS Lambda에서 알림을 받아 Slack 대화 채널로 메시지를 전달할 수 있습니다. 이에 관한 간략한 소개는 ChatOps를 위한 AWS Lambda를 통한 Slack 통합 샘플 코드에서 제공하고 있습니다. 이번 글에서는 좀 더 상세하게 해당 기능을 소개하여 쉽게 구성할 수 있도록 도움을 드리고자합니다.

1. AWS Elastic Beanstalk로 웹 서비스 구성하기
AWS Elastic Beanstalk을 사용하면 아주 쉽게 웹 서비스를 바로 만들 수 있습니다. 또한, ELB(Elastic Load Balancing)과 RDS(Relational Database Service)를 같이 생성해 주며, Auto Scaling Group도 자동으로 생성하여 트래픽을 분산하고 스케일링을 자동화 할 수 있습니다. Auto Scaling을 위한 Policy 역시 생성되며, 확장을 위한 조건 및 정책 변경이 가능합니다.

Beanstalk은 로컬에서 개발한 코드를 AWS 환경에 바로 배포가 가능합니다. 로컬에서 Git을 이용하여 개발 후 EB CLI(Elastic Beanstalk CLI)를 통해 쉽게 업로드하여 배포할 수 있습니다. 로컬 환경에 EB CLI를 설치하고 웹서비스는 Github awslabs에 있는 Node.js로 만들어진 예제를 사용하겠습니다.

원하는 폴더를 생성 후 Github에서 예제를 다운로드합니다.

$ git clone https://github.com/awslabs/eb-node-express-sample.git
Cloning into 'eb-node-express-sample'...
remote: Counting objects: 56, done.
remote: Total 56 (delta 0), reused 0 (delta 0), pack-reused 56
Unpacking objects: 100% (56/56), done.
Checking connectivity... done.

Git사용을 위한 초기화를 진행합니다.

$ git init .
Reinitialized existing Git repository in /Users/ilho/Github/ebsample1/ebsample2/eb-node-express-sample/.git/

EB CLI를 설정하면 $ eb라는 명령을 통해 Elastic Beanstalk을 로컬 환경에서 구성하여 사용할 수 있습니다. 처음에 개발하는 폴더를 Root로 지정하고 Beanstalk 환경에 대한 설정을 진행합니다. eb init이란 명령으로 수행합니다. Beanstalk을 어느 Region에 생성할지부터 서비스 이름 등 기본적인 설정을 진행합니다. 현재 AWS Lambda가 도쿄 리전에서 지원하고 있기 이를 활용하기 위해 Elastic Beanstalk을 도쿄 리전에서 시작해 보겠습니다.

AWS Elastic Beanstalk은 Node.js외에도 Java, .NET, PHP, Python, Ruby, Go, Docker를 모두 지원합니다.

a0999b0edcf5:eb-node-express-sample ilho$ eb init
Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-southeast-1 : Asia Pacific (Singapore)
7) ap-southeast-2 : Asia Pacific (Sydney)
8) ap-northeast-1 : Asia Pacific (Tokyo)
9) ap-northeast-2 : Asia Pacific (Seoul)
10) sa-east-1 : South America (Sao Paulo)
11) cn-north-1 : China (Beijing)
(default is 3): 8

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

Enter Application Name
(default is "eb-node-express-sample"): ebsample2
Application ebsample2 has been created.
 
It appears you are using Node.js. Is this correct?
(y/n): y
Do you want to set up SSH for your instances?
(y/n): y
 
Select a keypair.
1) example_key1
2) example_key2
3) example_key3
4) example_key4
5) [ Create new KeyPair ]
(default is 5): 4

Beanstalk Application을 AWS 콘솔을 통해서도 만들 수 있지만, EB 명령을 이용하여 로컬에서 바로 생성을 시킬 수 있습니다. Eb create 명령을 사용하여 Beanstalk 환경을 구성합니다. 아래처럼 필요한 환경을 구성하면서 만들어지는 내용을 확인할 수 있습니다. 명령을 실행하여 AWS 콘솔에서 환경이 구성되는 것을 확인할 수도 있습니다. 환경이 구성 중에는 회색으로 표기되며 모두 정상적으로 만들어지면 녹색으로 변경되어 상태를 구분할 수도 있습니다.

$ eb create web2-env
WARNING: You have uncommitted changes.
Creating application version archive "app-5529-160121_085131".
Uploading ebsample2/app-5529-160121_085131.zip to S3. This may take a while.
Upload Complete.
Environment details for: web2-env
Application name: ebsample2
Region: ap-northeast-1
Deployed Version: app-5529-160121_085131
Environment ID: e-immiw7zwnk
Platform: 64bit Amazon Linux 2015.09 v2.0.6 running Node.js
Tier: WebServer-Standard
CNAME: UNKNOWN
Updated: 2016-01-20 23:51:35.207000+00:00
Printing Status:
INFO: createEnvironment is starting.
INFO: Using elasticbeanstalk-ap-northeast-1-1234567890 as Amazon S3 storage bucket for environment data.
INFO: Created load balancer named: awseb-e-i-AWSEBLoa-5F13U6NNRGJT
INFO: Environment health has transitioned to Pending. There are no instances.
INFO: Created security group named: awseb-e-immiw7zwnk-stack-AWSEBSecurityGroup-59FO03AWS48X
INFO: Created Auto Scaling launch configuration named: awseb-e-immiw7zwnk-stack-AWSEBAutoScalingLaunchConfiguration-42NJ3UCM61ZD
INFO: Added instance [i-00952c8f] to your environment.
INFO: Created Auto Scaling group named: awseb-e-immiw7zwnk-stack-AWSEBAutoScalingGroup-1QP37B57TQ43O
INFO: Waiting for EC2 instances to launch. This may take a few minutes.
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:1234567890:scalingPolicy:6b7c0afe-3c1e-436d-a2e0-fcbc96d9c3c2:autoScalingGroupName/awseb-e-immiw7zwnk-stack-AWSEBAutoScalingGroup-1QP37B57TQ43O:policyName/awseb-e-immiw7zwnk-stack-AWSEBAutoScalingScaleUpPolicy-6FX2ZY0A9VIG
INFO: Created Auto Scaling group policy named: arn:aws:autoscaling:ap-northeast-1:1234567890:scalingPolicy:e8e7a4c3-af2f-4557-98e2-2dc472470f2e:autoScalingGroupName/awseb-e-immiw7zwnk-stack-AWSEBAutoScalingGroup-1QP37B57TQ43O:policyName/awseb-e-immiw7zwnk-stack-AWSEBAutoScalingScaleDownPolicy-1RY0SZO4CF9VP
INFO: Created CloudWatch alarm named: awseb-e-immiw7zwnk-stack-AWSEBCloudwatchAlarmHigh-10PTC4TZJA6XR
INFO: Created CloudWatch alarm named: awseb-e-immiw7zwnk-stack-AWSEBCloudwatchAlarmLow-1485FJORPZ9R3
INFO: Environment health has transitioned from Pending to Ok.
INFO: Successfully launched environment: web2-env

eb 명령어를 이용하면 바로 브라우져를 통해 Beanstalk으로 구성된 웹페이지를 열어볼 수 있습니다.

$ eb open

2. CloudWatch Alarm에 Notification 추가하기
Elastic Beanstalk은 Auto Scaling Group, CloudWatch Alarm, ScalingPolicy, SNS를 모두 구성해 줍니다. 자동으로 만들어진 SNS Topic을 이용하여 Lambda 함수와 연계되도록 구성을 할 수도 있고, 별도의 SNS Topic을 구성하여 Lambda 함수와 연계할 수도 있습니다.

CloudWatch 메뉴로 이동하여 자동으로 생성된 CloudWatch Alarm을 찾아 알람이 발생하면 SNS로 Notification을 전달 할 수 있도록 추가합니다.

기본으로 CPUUtilization을 모니터링하여 알람이 발생하도록 구성되어 있습니다. 스케일인-아웃(Scaling-in/out)을 위한 두 개의 알람이 생성되어 있으며, 해당 알람을 선택하여 Modify를 선택하고 Notification을 아래와 같이 추가합니다.

3. AWS Lambda에 Slack메시지 전송 함수 생성하기
Lambda에서는 Slack을 위한 함수가 예제로 추가되어 있습니다. 아래와 같이 slack으로 검색하면 Node.js와 Python 을 지원하는 Slack-echo-command, cloudwatch-alarm-to-slack 함수가 있습니다. 여기서는 Node.js를 사용하도록 하겠습니다.

cloudwatch-alarm-to-slack을 선택 후 Event Source로 앞에서 확인한 SNS Topic을 선택합니다. 해당 SNS Topic에 메시지가 전달되면 Lambda 함수가 자동으로 실행됩니다. 다음으로 제공하는 코드의 내용을 원하는 Slack 채팅 그룹, 채널에 메시지를 전달하기 위한 내용이 Comment에 설명되어 있습니다. 우선 Slack Incoming WebHooks 을 사용하여 메시지를 전달할 수 있도록 정보를 확인합니다.

  1. https://<your-team-domain>.slack.com/services/new로 이동합니다.
  2. “Incoming WebHooks”을 검색하여 채팅 그룹와 채널을 선택하고, 추가합니다.
  3. “Webhook URL” 을 확인하고 노트합니다.

Webhook URL이 노출되지 않도록 AWS KMS(Key Management Service)를 이용하여 Webhook URL을 암호화합니다. 별도의 암호화 관리를 하지 않고 쉽게 사용할 수 있는 서비스입니다. AWS CLI를 통해 직접 콘솔을 통하지 않고도 키 생성이 가능합니다.

1) AWS CLI로 KMS에 Key를 생성합니다. 이 예제에서는 Tokyo Region의 KMS에 키를 생성합니다.

$ aws kms create-key --region ap-northeast-1
{
"KeyMetadata": {
"KeyId": " xxxxxx-xxxx-xxx-xxxx-xxxxxxxx ",
"Description": "",
"Enabled": true,
"KeyUsage": "ENCRYPT_DECRYPT",
"KeyState": "Enabled",
"CreationDate": 1453342025.031,
"Arn": "arn:aws:kms:ap-northeast-1:1234567890:key/xxxxxx-xxxx-xxx-xxxx-xxxxxxxx",
"AWSAccountId": "123456789"
}
}

2) Key 사용이 쉽도록 Alias를 생성합니다. 이 예제에서는 SlackKey라는 이름으로 타겟 키를 지정하여 Alias를 생성합니다.

$ aws kms create-alias --alias-name "alias/SlackKey" --target-key-id " xxxxxx-xxxx-xxx-xxxx-xxxxxxxx " --region ap-northeast-1

3) WebHook URL을 생성한 키와 AWS CLI를 이용하여 암호화합니다. URL은 프로토콜 부분은 제외하고 입력합니다.

$ aws kms encrypt --key-id alias/SlackKey --plaintext "hooks.slack.com/services/abcdefghijklmnopqrstuvwxyz" --region ap-northeast-1
{
"KeyId": "arn:aws:kms:ap-northeast-1:1234567890:key/ xxxxxx-xxxx-xxx-xxxx-xxxxxxxx ",
"CiphertextBlob": "AbcdfghijksN8ta1n/abdefjiowl/x5RwtKZGSctHgnMVKeNo2xZjoAAACnMIGkBgkqhkiG9abcdefghijklmnb3DQEHATAeBglghkgBZQMEAS4wEQQMPWtiU2OmhyBJ9/swAgEQgGCZJ7fabcdefghijklmnUt6ltc1avMiWQawdgW9tj8Xv3drF7savCoL0oVDzRYRabcdefghijklmncIjX4LdXbAIg6nNWcF2JEa4v9Ze9WWNA9yrzJmmNs="
}

4) 암호화되어 생성된 WebHook URL을 Lambda코드의 kmsEncryptHookUrl 변수에 입력합니다. 입력할 Slack 채널명도 slackChannel 변수에 입력합니다.

var AWS = require('aws-sdk');
var url = require('url');
var https = require('https');
var hookUrl, kmsEncyptedHookUrl, slackChannel;

kmsEncyptedHookUrl = 'AbcdfghijksN8ta1n/abdefjiowl/x5RwtKZGSctHgnMVKeNo2xZjoAAACnMIGkBgkqhkiG9abcdefghijklmnb3DQEHATAeBglghkgBZQMEAS4wEQQMPWtiU2OmhyBJ9/swAgEQgGCZJ7fabcdefghijklmnUt6ltc1avMiWQawdgW9tj8Xv3drF7savCoL0oVDzRYRabcdefghijklmncIjX4LdXbAIg6nNWcF2JEa4v9Ze9WWNA9yrzJmmNs="';  // Enter the base-64 encoded, encrypted key (CiphertextBlob)
slackChannel = '#general';  // Enter the Slack channel to send a message to

5) Lambda 함수가 KMS를 사용하여 URL Decryption을 해야하므로 함수가 KMS를 사용할 수 있는 권한이 필요합니다. Lambda 함수가 사용할 Role에 KMS의 Decrypt API 접근 허용을 추가합니다. 아래 예제는 lambda_basic_execution role의 Policy에 추가한 화면입니다.

6) Lambda 함수에 lambda_basic_execution role을 지정하고 함수를 생성합니다.

7) Lambda Event Source의 처음 설정은 Disabled 이므로 Enabled로 변경합니다.

4. Slack 대화방에서 메시지 확인하기
Beanstalk에서 만든 사이트는 현재 트래픽이 없으므로, CPUUtilization이 낮아 Low CPU Alarm이 생성되어 메시지가 전달되게 됩니다. 아래와 같이 Slack 대화방에 해당 Alarm이 전달된 것을 확인할 수 있습니다.

Beanstalk을 활용하여 아주 간단히 웹서비스를 만들고 CloudWatch 알람 이벤트를 소스로 Lambda 함수를 활용하여 Slack 서비스에 메시지를 자동으로 포스팅하는 서비스를 상세히 구현해 보았습니다. Ops 혹은 DevOps팀에서 AWS의 여러 상태 정보나 메시지를 받아서 활용할 수 있습니다. Slack에서는 메시지의 포맷을 변경하거나 할 수 있는 여러가지 예제를 제공하고 있어 보아 다양한 메시지 구현이 가능합니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 김일호 솔루션즈 아키텍트께서 작성해주셨습니다.