간만의 휴식에 한일들

 3월 1일부터 새로운 회사에 입사하게 되어서 2월은 약 한달 정도 쉬게 되었다. 쉬기 전 까지는 이것도 하고 저것도 하고 계획도 많았지만 막상 쉬게 되면 이런 저런 제약 때문에 생각대로 되지 않는다. 특히 꼬맹이 학교에서 돌아오는 시간이 있으니 그에 맞춰 집에 있으려면 할 수 있는 것도 그렇게 많지 않은 것 같다. 그나마 한 것 들이라고는 영화본 것 밖에 없는 듯. 슬램덩크, 아바타2, 앤트맨&와스프 퀀텀 매니아. 요렇게 3개의 영화를 보고 왔다. 슬램덩크 추억의 만화책을 애니화 한 만화 영화. 뭐 나쁘지 않은 영화 였다. 전체적으로는 약간 루즈한 느낌이 들지만 마지막 클라이막스 부분에서는 음악과 함께 좋은 연출을 본 듯 한 느낌이다. 두번 본 사람도 있다고 하는데 그 정도 까지는 아닌 듯. 아바타2 볼까 말까 고민한 영화. 이미 뒷편 들까지 어느정도 촬영이 끝났다는 이야기를 듣고 보기로 결정. 처음으로 4dx 영화관에서 보았는데 의자의 흔들림 등 그 외 효과들이 그렇게 효과가 있다고는 생각을 못하겠다. 가~끔씩 타이밍이 딱 맞을 때도 있지만 오히려 신경이 쓰여서 영화에 집중 못하는 경우도 꽤 있었던 듯 하다.  화면에 집중하고 싶어서 더빙판으로 관람 했는데 더빙 판은 4dx밖에 없드라.. 내용도 괜찮고 그래픽도 괜찮고 눈도 즐거웠다. 다음 편이 나오면 또 보러 가고 싶다. 앤트맨&와스프 퀀텀 매니아 인터넷의 평가는 좋은편이 아닌 듯 하다. 이것도 4dx로 관람 하고 왔는데 나쁘지는 않은 듯 하다. 딱 거기까지. 양자 세계에 또하나의 우주가 존재한다는 가설은 이전부터 있었던 이야기. 결국 우리가 살고 있는 지구도 누군가의 양자 세계 일 수도 있다는 것.  하지만 양자 세계의 묘사가 뭐랄까 스타워즈 짝퉁이라는 느낌이 강하게 든다. 스타워즈를 본 적 없는 사람에게는 신선한 충격으로 다가 올지도 모르겠다. 영화를 볼때는 개연성같은걸 신경 쓰면 지는 거다. 그냥 무지성으로 생각하지 말고 시간 때우기로만 본다면 나쁘진 않은 듯 하지만 하나씩 따지기 시작

slack+slash command+API Gateway를 이용한 SSM 명령실행

목표

slack에서 slash command 를 이용하여 특정 명령어를 입력하면 SSM을 이용하여 지정한 ec2에서 지정된 명령을 실행하여 그 결과를 slack 에 출력한다. 여기에서는 각 ec2에서 free -m 명령을 실행 한 후 그 결과를 출력하도록 한다.

전제조건 : 대상 EC2는 ssm을 이용한 명령실행이 가능한 상황이어야 한다.

AWS설정

IAM 설정

slash command를 api gateway에서 받은 후, lambda에서 ssm을 호출한다. 그러기 위해서 lambda 함수에는 ssm과 로그 출력을 위한 cloudwatch logs 에 대한 권한이 필요하다.

  • IAM 에서 role을 선택하여 role을 작성
  • lambda를 선택해주고 일단 다음, 다음을 클릭하여 적당한 이름을 주고 role을 생성하였다.
  • 작성한 role을 선택후 「AmazonSSMFullAccess」AWS관리 Policy를 추가
  • inline policy를 선택하여 아래 json내용으로 적당한 이름을 지정하여 추가(이외에도 쓸일이 있다면 policy를 따로 생성한후 지정하는 것도 좋다. 이 policy가 없으면 cloudwatch logs에 로그 출력이 불가능.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "*"
        }
    ]
}

Lambda 함수 작성

lambda를 먼저 만드느냐, api gateway를 먼저 만드느냐 정해진 방법은 없다. 각 장단점이 있고 익숙한 방식이 있을터이니 편한 방법으로 작성해도 문제 없다. 최종적으로 api gateay를 trigger로 지정한 lambda function이 실행되면 OK.

  • lamda에서 함수 작성을 선택하여 Author from scratch를 선택
    • Function name을 적당히 넣어주고, Runtime은 Python3.8을 지정
    • Execution role에서 Use an existing role을 선택후 위에서 작성한 role을 지정하여 create function.

여기서 add trigger를 클릭하여 api gateway를 지정할 수도 있지만, 난 따로 만들겠다.

API Gateway 설정

  • api gateway에서 Create API를 선택후, REST API를 작성
  • API name을 적당히 입력해 주고 API를 생성
  • Resource를 선택후 Action에서 Create Resource를 선택 후 적당한 이름을 주고(여기에선 free를 지정) resource를 생성
  • /free 를 선택후 Action에서 Create Method를 선택후 Any를 지정
  • Intergration type에서 Lambda Function지정후 Region확인 하고 Use Lambda Proxy Intergration을 선택
  • 위에서 작성한 Lamda Function 이름을 지정 하여 Save하고 Permisson 추가에서 OK
  • Method Execution이 표시되면 Method Execution에서 Response Body for 200의 application/json 을 삭제
  • 완성된 any를 선택 후, actions에서 deply api를 선택하여 deply한다.
  • 왼쪽에서 stages를 선택하면 지정한 stage가 생성되어 있다. /free 를 확인하면 Invoke URL가 생성되어 있으니 메모
  • lambda function을 확인하면 trigger에 지정한 api gateway가 설정되어 있는 것을 확인

Lamda 코드

잘 알지도 못하는 python으로 겨우겨우 만들어 냈다. python다운 코드가 아니지만 잘 모르니 어쩔 수 없다.

import boto3
import json
import time
import requests

from urllib.parse import parse_qs

SLACKTOKEN = '<slack token>'
target_instances = ['<대상 ec2 ID>','대상 ec2 ID','대상 ec2 ID']

def command(ssm, instance, commands):
    r = ssm.send_command(
        InstanceIds = [instance],
        DocumentName = "AWS-RunShellScript",
        Parameters = {
            "commands": [
                commands
            ]
        }
    )
    command_id = r['Command']['CommandId']
    while True:
        time.sleep(0.2)
        res = ssm.list_command_invocations(
            CommandId = command_id,
            Details = True
        )

        invocations = res['CommandInvocations']
        status = invocations[0]['Status']
        print ("Get status : " + status)
        if status == "Failed":
            return ("Command 실행 에러")
        elif status == "Success":
            print ("Success!! return command result at " + instance)
            account = invocations[0]['CommandPlugins'][0]['Output']
            return account
        else:
            print ("Waiting Lambda function for 0.2s")


def lambda_handler(event, context):
    # TODO implement
    print ("Start Script")
    params = parse_qs(event['body'])
    token = params['token'][0]
    user = params['user_id'][0]
    print ("Get Token from posted data : " + token)
    if token != SLACKTOKEN:
        print ("Invalied Token, terminate process")
        return {
            "statusCode": 400,
            "headers": {
                "Content-Type:": "application/json"
            },
            "body": json.dumps({
                "response_type": "ephemeral",
                "text": "Invalid Token"
            })
        }
    retpost_url = params['response_url'][0]
    print ("Get response_url from posted data : " + retpost_url)
    
    str_command = "date;hostname;free -m;echo ''"

    print ("Create boto3 client for ssm")
    client = boto3.client(
        'ssm',
        region_name='ap-northeast-1',
    )

    comment=""
    print ("Into the for Loop with array of Instances")
    for str_instance in target_instances:
        print ("Excute command with ssm :" + str_instance)
        comment += command(client, str_instance, str_command)
    
    print ("add codesnipet on result")
    comment = "<@" + user + ">\r\n" + "```\r\n" + comment + "\r\n```"
    
    print ("return result : \r\n" + comment)
    print ("Making Object for posting to slack")
    
    obj_slack = {
        "response_type": "in_channel",
        "text": comment
    }
    requests.post(retpost_url, data=json.dumps(obj_slack))
    
    return {
        "statusCode": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": json.dumps(obj_slack)
    }

Slack 설정

slack slash command 어플리케이션이 이미 등록되어 있다고 가정하고, slash command에서 새로운 request를 작성한다.

  • command 를 /free 로 지정
  • URL은 API Gateway에서 확인한 Invoke URL을 지정
  • Method는 Post
  • token은 메모하여 Lambda코드에 삽입
  • 그외에 적당한 내용을 지정하여 저장

최종확인

slack에서 /free 를 입력하여 지정한 ec2의 free 결과가 표시되는 것을 확인한다. 실행이 잘 안될 경우엔 cloudwatch logs를 확인하자.

  • slash command의 timeout이 3초이기 때문에 여러대의 ec2에서 free -m 을 실행한 결과가 timeout되어 버린다. 어떻게 해결은 했는데 기억이.... 기억나는 대로 추가.
 

댓글

이 블로그의 인기 게시물

curl 명령어 옵션

CISCO 2960s 초기화 후 기본 설정

AWS에서 zabbix를 이용한 autoscaling ec2 자동등록/자동삭제