archived-project
(Toy Project) Notice Bot with GCP Container Serverless
Index
- Introduction
- Why? How?
- Hands-on
- Conclusion
Introduction
안녕하세요 Yureutae입니다.
오늘은 저번 artifact registry migration의 대상이 되었던 개인 토이 프로젝트였던 학과 공지 봇에 대해 설명드리려합니다. 사실 클라우드 기술에 대해 아무런 지식이 없었던 2학년 말 시절에 하루정도 매달려 만든 프로젝트라 간단해 보일 수 있습니다. 하지만 이 프로젝트로 클라우드 기반 기술에 관심을 크게 가지게 된 면도 있어서 소개하려합니다.
yureutaejin/Serverless-notification-Slackbot

Why? How?
저는 대학교 생활 동안 성적 장학금 외에 학교에서 누릴 수 있는 혜택은 모두 누렸던 것 같습니다. 그냥 학과 사이트를 남들보다 많이 들어갔을 뿐인데 얻은 혜택이 많았던 것 같습니다. 제가 느꼈던 것은 학교 공식 앱이나 사이트가 있음에도 많은 학생들이 주로 사용하는 플랫폼이 아니기 때문에 공지를 잘 확인하지 않는다는 것입니다.
당연히 제 학과, 학교 내외로 공지를 돌리는 봇을 만든 프로젝트는 많습니다. 쉬우니까요. 당시에 누가 에브리타임에 디스코드 봇을 올렸던 것 같습니다. 근데 여러 프로젝트들을 보니 서버(대개 ec2)를 24/7 내내 띄어놓거나 개인 서버를 사용하던 분이 많더라구요.
저는 2022 당시 논문으로 concept만 접하던 Serverless라는 기술에 대해서 대충 context는 파악한 상태였고,
저런 batch 성 작업을 처리하기 위해서 서버를 유지해야하나? Serverless를 사용해서 Event Trigger할 때만 돌아가게해서 돈을 아끼면 안되나? 라는 생각이 들었습니다.그렇게 심플한 아이디어로 구체적인 계획을 짜보았습니다.
- Scraping 및 Message 기능을 수행할 Application과, 해당 Application을 구동할 환경(Docker Image) layer를 구분
- 학교 대부분이 Client Side Rendering이 아닌 Server Side Rendering 페이지이기 때문에, Application은 그냥 “request module GET → html element 뜯어내기”로 Scraping 수행. 최근 공지만 저장하고 비교하는 방식.
- Message 전달 대상 Platform은 우선 Slack으로 수행 (동아리 원이 많았기 때문에)
- Docker Image로 패키징 후 Cloud의 Container Image Storage에 저장
- Event Trigging으로 조건에 따라 해당 Container Life Cycle 동작
- 최근 공지는 Cloud의 Storage 서비스에 저장
찾아보니 GCP의 Cloud Run, Cloud Storage, Cloud Scheduler, Container Registry(현 Artifact Registry)가 저에게 적합했었습니다. 사실 그냥 비용이
0 라서 사용했어요.
아, 물론 Cloud Run과 Cloud Function 모두 Serverless라 고민했는데 전 환경까지 제가 세팅하는게 편해서 Container를 Job 단위로 돌릴 수 있는 Cloud Run - Jobs로 선택했습니다.
Cloud Run
Container + Serverless입니다. 딱 이렇게 이해하면 부가 설명이 필요없을 것 같아요.
당연히 실 사용시간으로 비용이 부과되는데, 2022 당시에는 300만건?까지 무료였던 것 같습니다. 지금은 가격 책정 방식이 바뀌었네요. 뭐 프로덕션으로 웹 애플리케이션이나 대량의 트래픽이 발생할 수 있는 서비스를 사용하는 거 아니면 전혀 신경 안써도 됩니다.

Cloud Storage
네 그냥 S3입니다. 오브젝트로 저장하면 되고
미국 region 에 한해 월 5GB 무료입니다. GCP처럼 5GB는 평생 꽁자네요.Cloud Scheduler
대충…AWS EventBridge?정도 되겠네요. 그냥 Cron으로 생각해도 무방합니다. 과금 방식 해석에 관해서는 커뮤니티들도 많이 헷갈려하는데,
총 3개의 Job은 무조건 무료 입니다. 그 이상부터 Job 개수 단위로 과금 된다보면 될 것 같습니다.Artifact Storage(구 Container Storage)
Container Image와 Language Package를 관리할 수 있는 곳입니다.
0.5GB까지 무료입니다. 좀 짜네요. 전송 가격은 또 별도라 단일 리전으로 us만 사용하면 됩니다.
생각해보니까 Google IDX가 나올 수 있던 건 Artifact Registry 덕분이지 않을까합니다.

Slack API
메시지를 만들어서 슬랙 채널에 쏴줘야합니다. API가 있어야겠죠?
뭐 보니까 엄청 간단한데 보안은 좀 약한 듯 합니다. Token 까먹어도 재발급 안해도 되더라고요. 그대로 볼 수 있고 복사해오면 됩니다.
Hands-On
그렇게 복잡한 프로젝트가 아니라 간단하게 적어보겠읍니다.
Application
로직은
1) 이전 저장된 공지 객체를 읽어온다. 2) 목표사이트 스크래핑 후 form에 집어넣기 3) 새 공지면 message 날리고 새 공지를 객체로 저장 입니다. 객체 Read/Post 부분은 GCP 쪽에서 볼게요.스크래핑을 해서 Message Form을 만듭니다. 옛날에 만들어서 코드가 좀 지저분해 보이긴 하네요. 여튼 scrape_site와 attachment method만 목표 사이트에 맞게 수정해주면 됩니다. (SSR 사이트 기준입니다. CSR이면 그냥 request 날려서 그대로 사용하세요)
# library
import requests
import json
from bs4 import BeautifulSoup
class CreateAttachment():
def __init__(self, target_link):
self.target_link = target_link
self.scrape_result = self.scrape_site()
def scrape_site(self):
html_text = requests.get(self.target_link, verify=False).text
soup = BeautifulSoup(html_text, 'html.parser')
latest_notification = soup.select_one('#sub > div > div.board_container > table > tbody > tr:nth-child(1)')
return latest_notification
def attachment(self):
new_noti_number = (self.scrape_result.find(class_="body_col_number dn1").text).strip()
new_noti_title = (self.scrape_result.find('a').text).strip()
new_noti_link = (self.scrape_result.find('a'))['href']
new_noti_date = (self.scrape_result.find(class_="body_col_regdate dn5").text).strip()
attach_dict = {
'color' : '#ff0000',
'author_name' : 'Slack Bot Notice',
'title' : new_noti_title,
'title_link' : self.target_link+new_noti_link,
'date' : new_noti_date,
}
return attach_dict다음은 Slack에 Message Post 관련해서 작성해주면 됩니다. 위 docs 사이트 보면 형식 다양한데 전 귀찮아서 https로 날렸습니다
def notice_message(token, channel, text, attachments):
attachments = json.dumps(attachments)
response = requests.post("https://slack.com/api/chat.postMessage",
headers={"Authorization": "Bearer "+token},
data={"channel": channel, "text": text ,"attachments": attachments})token은 본인이 만들 앱을 slack api에서 등록하고 OAuth token 발급 받으면 됩니다.

Docker Image
(최종 배포 때 수행. 아래 Cloud Setting Artifact Registry 부분까지만 하고 오세요)
최대한 가볍게 만들어 줍시다. 전 alpine을 사용했습니다. 애플리케이션 구동은 entrypoint.sh에 ‘python slack_bot.py’로 작성해주심 됩니다. 쉘 스크립트로 거쳐서 동작하는게 configure 안 길어지고 편합니다.
# Python image to use.
FROM python:3.9-alpine
# Set the working directory to /app
WORKDIR /app
COPY . .
# copy the requirements file used for dependencies
WORKDIR /app/project
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# Run app.py when the container launches
ENTRYPOINT ["/bin/sh", "entrypoint.sh"]
image 빌드 결과 73.8MB에 불과한 것을 알 수 있읍니다.

Cloud Setting
GCP는 Docs가 깔끔해서 편할 줄 알았더니 레퍼런스가 별로 없어서 오히려 귀찮은 것 같습니다. 하나씩 빨리 세팅해봅시다. 웹 콘솔이 아니라 로컬 콘솔이 훨씬 편합니다.
Service Account
Serverless는 Stateless입니다. 즉 “상태가 없음”인데, 저희는 “Application”이 이전 공지를 불러오고 저장하는게 필수여서 Cloud Storage를 추가로 활용합니다.
근데 마스터 계정이 아닌 “Application”이 Read, Write를 수행할 것이기 때문에, Application 전용으로 “Cloud Storage Service를 사용할 Account”를 하나 만들어 줍니다. K8s의 Service Account 개념과 유사합니다.

IAM의 Service Account를 들어가서 만들어주면 됩니다. 액세스 권한만 해당 프로젝트로 해주심 됩니다.

그럼 이제 키 추가를 할 수 있습니다. 여기서 추가를 하면 json이 하나 튀어나는데 파일이름 수정하고 Application 안에 넣어 주면 됩니다. (Docker build 전에 해야함)

이렇게 하면 이제 Application이 Cloud Storage에 접근해서 뭔가를 수행할 수 있게 됩니다.

Artifact Registry

저번 글과 위 문서를 참고해서 registry를 만들고 push 때림 됩니다.
위에서 docker container image 이름을 “artifact registry 내의 주소”로 지정하는게 핵심입니다.
IAM 세팅만 되면 앞으로 로컬 콘솔에선
gcloud auth login 으로 한방에 해결됩니다.

Cloud Run & Cloud Scheduler
거의 끝났습니다. 이제 만든 컨테이너를 Cloud Run Job에 등록하고 Event Trigger해주면 끝입니다.
Cloud Run Job에 들어가서 서비스 생성을 누르면 다음과 같은게 나옵니다. Github CD 옵션이 새로 생겼네요…ㅋㅋㅋ Artifact Registry나 Docker Hub없이 Dockerfile만 있어도 되는 것 같습니다.
여튼 아래 옵션을 쫙 채워주고 생성하면 됩니다. 인스턴스 최소 개수는 cold-start 방지 옵션이니 우린 필요없으니까 그대로 냅둡시다. 컨테이너 옵션 뭐 건드릴 거는 메모리 정도만 더 줄여놔도 될 것 같습니다.

Trigger없이 직접 실행도 되니 먼저 테스트 해보세요

Cloud Scheduler는 위에서 생성했던 Job을 잘 가리키면 됩니다.
Cloud Scheduler 서비스 들어가서 아래 문서 참고 후 직접 명세서를 작성해도 되고, 아니면 위 이미지에서
Trigger 라고 되어있는 부분을 클릭해서 초간단하게 만들 수 도 있습니다.
Cron 작성이 미숙하신 분들은 https://crontab.guru/#*_*_1_*_* 요기 들어가서 쉽게 만드심 됩니다.
Conclusion
Artifact Registry 작업 및 테스트 할 때 빼고는 아래 이미지와 같은 형태로 2년간 0원으로 잘 돌아가고 있습니다. 많은 사람들이(Slack을 보는 ㅋ) 공지를 잘 놓치지 않게 되고 저도 뭐 별다른 손해없이 서비스를 운영 경험을 만들어서 좋은 것 같습니다.
비교적 간단한 프로젝트지만 IAM, Event Trigger, Container 이미지 최적화, 서비스 요소 비용 등을 고려해야하고 Application Layer가 아닌 Cloud, Linux, Container 모두 연계해야하는 점에서 이런 개발 경험이 처음이신 분들께 좋은 토이 프로젝트라고 생각합니다.



