본문 바로가기

DevOps

Slack으로 process가 정상적으로 시작되었는지 나타내는 이쁜 메시지 만들기(Incoming webhook과 Attachment message 활용)

Slack은 개발자들이 사랑하는 커뮤니케이션 도구 중 하나이다. Slack의 webhook을 사용하여 back-end서버에 개발한 application process가 정상적으로 실행되고 있는지, 정상적으로 꺼졌는지 여부 등을 알림으로 받을 수 있다.

 

배포하는 서버가 1, 2대라면 ssh 접속으로 언제든지 확인가능하지만 만약 10대, 100대의 서버에 다 들어가서 process가 정상작동 되는지 여부를 확인하기는 어렵다.(하지만 쿠버네티스의 probe를 사용하면 어떨까) 이때 slack으로 한번에 알림을 받는 자동화 과정을 shell로 만들어 놓으면 몸과 정신이 건강해질 수 있다.

 

시작하기 전에

이 포스팅에 설명하는 guide는 1개의 process로 돌아가는 application을 기준으로 설명합니다. docker을 통한 container application에서도 비슷하게 적용가능하지만, 이 포스팅에서는 설명하지 않습니다.

1단계 : slack webhook을 만들기

slack webhook은 workspace에 Slack app을 새로 생성하여 만들 수 있다. 이전의 slack token을 사용하여 webhook을 날리는 방식은 slack에서는 더이상 사용하지 않는다고 한다.(관련 정보 바로가기)

 

slack webhook 만들기 상세 guide : https://api.slack.com/incoming-webhooks

 

Incoming Webhooks

Learn how to build bot users, send notifications, and interact with workspaces using our APIs.

api.slack.com

1. Slack app을 생성

Slack의 incoming webhook은 App 기준으로 작동한다. Create New App(바로가기)에서 App을 새로 생성하도록 한다.

 

2. Incoming webhook을 생성

만들어진 App으로 들어가서 좌측을 보면 Feature > Incoming Webhook이 있다. 버튼을 눌러 아래로 내려가면 새로운 채널에 webhook을 만들 수 있다.

1개의 app당 여러개의 incoming webhook을 만들 수 있는데, 알림을 보내고 싶은 channel을 개설하고 해당 채널을 설정하면 만들어진 webhook은 그 채널에만 message를 보낼 수 있다.

You cannot override the default channel (chosen by the user who installed your app), username, or icon when you're using Incoming Webhooks to post messages. Instead, these values will always inherit from the associated Slack app configuration.
☞ Slack의 incoming webhook guide에 상기와 같이 나와있다. 말인 즉슨, 만들어진 webhook url은 1개의 channel에만 발송할 수 있다는 것이다. 또 다른 channel에 보내려면 또다른 webhook url을 생성해야한다.

 

3. webhook 메시지 생성

webhook을 정상적으로 만들게 되면 아래와 같은 형식의 webhook url을 받을 수 있다.

https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX

상기와 같은 형식의 url을 기본으로 url payload에 다양한 형태의 slack message를 보낼 수 있다. payload별 slack 메시지 형태는 아래와 같다.(참고 url)

 

(1) Basic formatting

기본 포맷으로 text만 보낼때 사용

curl -X POST -H 'Content-type: application/json' 
--data '{"text":"This is a line of text.\nAnd this is another one."}' 
https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX

결과물

 

(2) Attachment

image, url button, 기타 여러상태를 보여주고 싶을때 사용. 시각적으로 효과적 전달 가능

curl -X POST -H 'Content-type: application/json' 
--data '{
    "attachments": [
        {
            "fallback": "Required plain-text summary of the attachment.",
            "color": "#36a64f",
            "pretext": "Optional text that appears above the attachment block",
            "author_name": "Bobby Tables",
            "author_link": "http://flickr.com/bobby/",
            "author_icon": "http://flickr.com/icons/bobby.jpg",
            "title": "Slack API Documentation",
            "title_link": "https://api.slack.com/",
            "text": "Optional text that appears within the attachment",
            "fields": [
                {
                    "title": "Priority",
                    "value": "High",
                    "short": false
                }
            ],
            "image_url": "http://my-website.com/path/to/image.jpg",
            "thumb_url": "http://example.com/path/to/thumb.png",
            "footer": "Slack API",
            "footer_icon": "https://platform.slack-edge.com/img/default_application_icon.png",
            "ts": 123456789
        }
    ]
}' 
https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX

결과물

 

(3) Message buttons

button 추가 가능. 

curl -X POST -H 'Content-type: application/json' 
--data '{
    "text": "Would you like to play a game?",
    "attachments": [
        {
            "text": "Choose a game to play",
            "fallback": "You are unable to choose a game",
            "callback_id": "wopr_game",
            "color": "#3AA3E3",
            "attachment_type": "default",
            "actions": [
                {
                    "name": "game",
                    "text": "Chess",
                    "type": "button",
                    "value": "chess"
                },
                {
                    "name": "game",
                    "text": "Falken's Maze",
                    "type": "button",
                    "value": "maze"
                },
                {
                    "name": "game",
                    "text": "Thermonuclear War",
                    "style": "danger",
                    "type": "button",
                    "value": "war",
                    "confirm": {
                        "title": "Are you sure?",
                        "text": "Wouldn't you prefer a good game of chess?",
                        "ok_text": "Yes",
                        "dismiss_text": "No"
                    }
                }
            ]
        }
    ]
}' 
https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX

결과물


2단계 : shell script 짜기

shell script에서 slack message를 보내는 것을 범용적으로 사용하기 위해 function으로 개발을 우선적으로 하였다. slack_message() function은 2개의 인자를 받는데 첫번째 인자는 message를 담고, 두번째 인자는 true/false를 통해 message attachment의 색을 바꿔준다.

#!/usr/bin/env bash

function slack_message(){
    # $1 : message
    # $2 : true=good, false=danger

    COLOR="danger"
    if $2 ; then
        COLOR="good"
    fi
    curl -s -d 'payload={"attachments":[{"color":"'"$COLOR"'","pretext":"<!channel> *DEPLOY INFORMATION*","text":"*HOST* : '"$HOSTNAME"' \n*MESSAGE* : '"$1"'"}]}' https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX
}

3단계 : 적용하기

#!/usr/bin/env bash

PROGRAM_DATE="$(which date) -R"
PROGRAM_JPS=$(which jps)
PROGRAM_GREP=$(which grep)
PROGRAM_NOHUP=$(which nohup)
PROGRAM_WC=$(which wc)

function slack_message(){
    # $1 : message
    # $2 : true=good, false=danger

    COLOR="danger"
    if $2 ; then
        COLOR="good"
    fi
    curl -s -d 'payload={"attachments":[{"color":"'"$COLOR"'","pretext":"<!channel> *DEPLOY INFORMATION*","text":"*HOST* : '"$HOSTNAME"' \n*MESSAGE* : '"$1"'"}]}' https://hooks.slack.com/services/TXXXXX0/BXXXXX000/LXXXXXxxxxxXXXXX
}

if [ $($PROGRAM_JPS | $PROGRAM_GREP MyApp | $PROGRAM_GREP -v $PROGRAM_GREP | $PROGRAM_WC -l) != "0" ]; then
    echo "$($PROGRAM_DATE) [COMMAND] MyApp already running"
    slack_message "이미 MyApp 실행중" false
    exit 1
fi

echo "$($PROGRAM_DATE) [COMMAND] Starting..."

# start MyApp with nohup
$PROGRAM_NOHUP $SERVICE_BIN 2>&1 &


# check if MyApp is on in 50 seconds
START_COUNT=1
START_RESULT=false
while [ $START_COUNT -lt 10 ]
do
    sleep 5
    START_COUNT=`expr $START_COUNT + 1`

    CHECK_PS=$($PROGRAM_JPS | $PROGRAM_GREP MyApp | $PROGRAM_GREP -v $PROGRAM_GREP | $PROGRAM_WC -l)
    if [ $CHECK_PS == "1" ]; then
        echo "$($PROGRAM_DATE) [COMMAND] Started MyApp"
        START_RESULT=true
        break
    else
        echo "$($PROGRAM_DATE) [COMMAND] Starting.. MyApp"
    fi
done

if $START_RESULT ; then
    slack_message "Start success" true
    exit 0
else
    slack_message "Start failure" false
    exit 1
fi

상기 shell script는 application을 nohup으로 실행시킨 뒤 정상적으로 process가 떠있는지 jps를 이용하여 grep하여 확인한다.

만약 50초 안에 정상적으로 process가 실행되면 slack으로 Start success 메시지를 날리고 만약 실행되지 못한다면 Start failure 메시지를 날린다.

 

- 성공했을 경우

- 실패했을 경우