본문 바로가기
백엔드 개발

#051. 자동화: cloudflare worker+ sentry + discord 웹훅으로 알림 시스템

by iamjoy 2023. 7. 19.

개념(다이어그램)

 

기존에 모니터링 도구로 sentry를 사용하고 있었는데, 알림을 제때 확인하는 것이 쉽지 않아 discord로 알림을 받을 수 있게 구성했다. (슬랙은 유료라..)

적용

(1) sentry에서 오는 json 훅 확인하기 
먼저 sentry에서 웹훅으로 보내는 알림의 json 모양을 알아야 해서 아래의 웹훅 사이트를 이용해 json 필드들을 확인했다.
(1) 웹훅 사이트에서 웹훅을 받을 주소를 확인한다.

웹훅 사이트: https://webhook.site/#!/9723359f-6f36-41a7-830b-e9aec78bb207/b2e4dfee-cc03-4a64-b061-00f9203304cf/1
이 주소를 센트리 settings -> 웹훅에 붙여넣으면 sentry -> 웹훅 사이트로 알림을 보낸다

아래와 같이 알림 받을 웹훅을 설정하고 알림을 보내도록 한다.

send Test Notification을 누르면 아래와 같이 sentry의 json 모양을 볼 수 있다.

서버는 ts로 만들기로 했기 때문에 json을 ts 타입으로 만들어주는 툴을 사용했다. 세상에 진짜 좋은 것 많은 것 같다.
https://transform.tools/json-to-typescript

 
(2) 디스코드 
디스코드에 훅을 만든다. 이 웹훅으로 POST + 임시 body를 만들어 디스코드에 알림이 잘 가는 지 확인해본다. 디스코드로 웹훅을 만드는 것은 자료가 많기 때문에 생략한다.

https://discord.com/developers/docs/resources/webhook#webhook-resource
참고로 디스코드에 맞는 body를 만들어줘야하는데, builder 사이트를 통해 embed를 설정하면 보다 편하게 작업할 수 있다. 다만 종종 이전 버전으로 만들어주는 경우가 있으니 공식 독스를 참고해서 확인할 필요가 있다. 
https://autocode.com/tools/discord/embed-builder/
이 작업을 하면서 어떤 정보를 기록하고 알려줄 지 파악할 필요가 있다. 우리팀은 아래와 같은 정보를 담기로 했다.
에러 내용/ 센트리 링크/ 환경/ 에러레벨/ method/ 유저 디바이스

위와 같이 통신이 오게 하기 위해서 아래 코드처럼 작성하면 된다.

{
    "content": "Sentry Error",
    "embeds": [
        {
            "title": "ApiException: Authentication failed, token expired!",
            "description": "Authentication failed, token expired!",
            "color": 255,
            "url": "https://sentry.io/organizations/ratatouille-vd/issues/4320852218/events/b768afe2d13a46c8a4e89e672fb36634",
            "fields": [
                {
                    "name": "environment",
                    "value": "production",
                    "inline": true
                },
                {
                    "name": "level",
                    "value": "Error",
                    "inline": true
                },
                {
                    "name": "url",
                    "value": "http: //localhost:8080/"
                },
                {
                    "name": "method",
                    "value": "GET",
                    "inline": true
                },
                {
                    "name": "User-Agent",
                    "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36"
                }
            ]
        }
    ]
}



이렇게 sentry 에서 어떻게 json을 보내주며, 이 중 어떤 정보를 선택할지 정했고. 이 정보들을 discord에서 어떤 식으로 받아볼 지 정했다면 이를 연결해 줄 서버를 띄워야한다. 이는 cloudflare에서 제공하는 serverless worker를 사용하기로 했다.
(3) 실제 연동하기
wranger는 cloudflare에서 제공하는 serverless worker를 cli 기반으로 작업할 수 있도록 제공하는 툴이다.
wrangler를 실행하고 관리하는 방법은 이후 다른 글에서 정리해보려고 한다. (여기서는 wrangler를 통해 이미 worker를 띄웠다고 가정하고 생략한다) 작업하며 참고한 링크들은 아래와 같다.
https://developers.cloudflare.com/workers/wrangler/configuration/
https://developers.cloudflare.com/workers/get-started/guide/
https://developers.cloudflare.com/workers/wrangler/configuration/#inheritable-keys
[참고]
wrangler를 통해 cloudflare에 환경변수 주입하기 
https://developers.cloudflare.com/workers/platform/environment-variables/#secrets-on-deployed-workers

# wranlger의 환경변수 입력 cli

$ wrangler secret put DISCORD_ALERT_CHANNEL_WEBHOOK 
 ⛅️ wrangler 2.14.0 (update available 3.2.0)
———————————————————————————
✔ Enter a secret value: … *************************************************************************************************************************
🌀 Creating the secret for the Worker "eva" 
✨ Success! Uploaded secret DISCORD_ALERT_CHANNEL_WEBHOOK

 
wrangler를 통해 cloudflare에 worker를 설정했다면 cloudflare 의 Workers & Pages 에서 리소스 확인할 수 있다.
$wrangler generate 를 통해 worker 내의 설정을 위한 .toml 파일을 생성해서 관련 환경변수 및 리소스등을 관리할 수 있다.

워커를 만들면 도메인을 제공해준다. (dev 또는 dev --remote로 실행할 경우 각각 로컬로 띄워주거나, 외부로 보낼 수 있는 임시 IP을 제공해준다.) deploy로 실제 작동하는 것 확인한다. router를 설정하는 방법은 관련 링크를 확인한다.(https://github.com/lukeed/worktop#usage

wrangler dev 로 개발모드를 실행하면 아래와 같이 띄워진 로컬 포트를 알려준다. (https://developers.cloudflare.com/workers/wrangler/commands/#dev) -> 

 
이제 서버가 잘 떠있다면 이 도메인주소를 sentry 웹훅으로 입력한다. 그러면 sentry가 cloudflare를 바라보게 된다.


서버에서는 전송된 sentry 알림 json 객체 중에 디스코드에 찍고자 하는 부분만 추출하도록 디스코드 형식에 맞춰 작성한다. 그리고 서버는 discord 웹훅을 바라보게 한다.

// sentry의 error payload를 받아 discord로 보내는 라우트를 만든다

import { DiscordPayload } from "./DiscordPayload";
import { isSentryEventPayload } from "./types";

async function receiver(payload: unknown) {
	if (isSentryEventPayload(payload)) {
		const message = DiscordPayload.bySentryInput(payload).toObject();

		fetch(DISCORD_ALERT_CHANNEL_WEBHOOK, {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
			},
			body: JSON.stringify(message),
		});
	}
}

export default receiver;

 
그리고 센트리에서 알림보내기를 누르면 다음과 같이 디스코드로 알림이 도착한다!

 
 

마무리하며

회사에서도 에러 알림 시스템을 사용하는데 사용하는 입장이었지 구성해본 경험은 없었는데, 이 작업을 하면서 어떤 식으로 구성되었을 지 생각해볼 수 있었다.