Atlassian Statuspage 도입과 자동화 적용기

도입
Why you need a status page - Work Life by Atlassian
A status page is an essential part of any incident communication strategy; they can can turn negative customer experiences into positive ones.

많은 서비스들이 장애 발생과 해결 상태 공유를 그 사용자들에게 공유하기 위해 status 표시 페이지를 많이 사용하고 있다. Status page 운영을 위한 여러 오픈소스들과 옵션들이 있지만 Atlassian statuspage를 선택한 건 아래의 이유다.
- 조직 내에서 Jira를 사용하고 있으며, 이와의 연동이 매우 훌륭하다.
- Slack 노티피케이션 설정이 간단하다.
- Datadog 으로부터 메트릭을 활용할 수도 있다.
여러 플랜 중 제일 저렴한 hobby plan으로도 충분하다고 봤다. 결제 이후 커스터마이즈는 매우 쉽다.

저비용으로 Health-check API를 자동화해보자.

내가 회사에서 개발하고 운영 중인 서비스들은 Health Check API를 노출하고 있다. 이 엔드포인트를 주기적으로 요청하여 서비스 상태에 이상이 있을 경우 statuspage 에 노출시키도록 하였다.
Statuspage API Documentation
Welcome to Statuspage’s API! The Statuspage API empowers developers to automate, extend and combine Statuspage with other services.
HTTP API를 제공하기 때문에 손쉽게 statuspage에 에러 보고를 할 수 있었다.
github actions
name: "Status check every hour"
on:
schedule:
- cron: "*/30 * * * *"
workflow_dispatch:
jobs:
status_check:
name: Run Status check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
fetch-depth: 0
- name: Set up Python environment
uses: actions/setup-python@v2.3.1
with:
python-version: "3.9"
- name: Install requirements
run: pip install -r requirements.txt
- name: RUN HEALTHCHECK
run: python main.py
env:
STATUSPAGE_API_TOKEN : ${{ secrets.STATUSPAGE_API_TOKEN }}
main program
from datetime import datetime
import json
from os import environ
from requests import Response, Session
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
retry = Retry(
total=3,
read=3,
connect=3,
backoff_factor=0.3,
status_forcelist=(500, 502, 504),
)
adapter = HTTPAdapter(max_retries=retry)
session = Session()
session.mount('http://', adapter)
session.mount('https://', adapter)
with open('data.json', 'r') as fp:
data = json.load(fp)
PAGE_ID = data.get('page').get('id')
STATUSPAGE_API_TOKEN = environ.get('STATUSPAGE_API_TOKEN')
def check_statuspage_api_status():
response = session.get(
headers={'Authorization': f'OAuth {STATUSPAGE_API_TOKEN}'},
url=f'https://api.statuspage.io/v1/pages/{PAGE_ID}.json'
)
response.raise_for_status()
print('[INFO] STATUSPAGE API STATUS CHECK OK')
def report_incident(component_api_id: str) -> None:
body = {
"incident": {
"name": "API Health check가 실패합니다.",
"impact": "critical",
"impact_override": "critical",
"created_at": datetime.now().isoformat(),
"body": "자동화된 Health Check API 요청이 실패하였습니다. 개발팀이 조사하고 있습니다.",
"component_ids": [component_api_id],
"components": {
component_api_id: "partial_outage"
}
}
}
session.post(
headers={'Authorization': f'OAuth {STATUSPAGE_API_TOKEN}'},
json=body,
url=f'https://api.statuspage.io/v1/pages/{PAGE_ID}/incidents'
)
def health_check(url: str, component_api_id: str) -> bool:
response: Response = session.get(url=url, timeout=3)
print(f'[INFO] check result: {url} {"OK" if response.ok else "FAIL"}')
if not response.ok:
report_incident(component_api_id=component_api_id)
if __name__ == '__main__':
check_statuspage_api_status()
for item in data.get('components'):
health_check(
url=item.get('health_check_url'),
component_api_id=item.get('component_api_id')
)
Private Subnets 안에 있는 리소스들도 github actions self-hosted runner 기능을 이용한다면, 무리없이 작업에 포함시킬 수 있을 것이다.
GitHub - machulav/ec2-github-runner: On-demand self-hosted AWS EC2 runner for GitHub Actions
On-demand self-hosted AWS EC2 runner for GitHub Actions - GitHub - machulav/ec2-github-runner: On-demand self-hosted AWS EC2 runner for GitHub Actions
알림 설정

모든 멤버들이 사용하는 슬랙에 알림을 걸었다. 실시간으로 잘 오는 것을 여러 번 확인했다. 이를 통한 빠른 대응, Statuspage가 제공해주는 Post-mortem 기능과 함께 더 안정적인 서비스를 운영할 수 있어보인다.