들어가며
안녕하세요. LINE NEXT DevOps 팀에서 쿠버네티스 운영 및 유지 보수, CI/CD 구축, 모니터링, 로그 수집 등 인프라 전반에 걸쳐 업무를 수행하고 있는 이동원입니다. 이번 글에서는 새로운 도구를 활용해 장애/변경 알림 및 관리 시스템을 구축한 과정을 소개하겠습니다.
이 글은 먼저 발행했던 ChatOps를 통한 업무 자동화(feat. Slack Hubot) 글과 이어지는 글로, 이번에 새로 소개할 도구는 Playwright라는 E2E(End-to-End) 모니터링 도구입니다. Playwright를 이용해 모니터링 관제를 수행하고, Jira를 이용해 장애 및 변경 티켓을 관리하며, 봇을 이용해 Slack으로 알림을 보내고 Jira를 제어하는 시스템을 구축했습니다. 그 과정을 상세히 소개하겠습니다.
새로운 시스템 도입 배경
LY에서는 타 회사의 TTS(Trouble Ticket System)라는 시스템을 이용해 변경 관리 및 장애 관제를 수행하고 있었습니다. 그러나 타 시스템에 의존적이었고, 시스템을 별도로 운영하다 보니 모니터링과 변경, 장애에 대한 시스템 제어권이 없었습니다.
이런 상황에서 장애 발생 후 이를 처리하는 데 소요되는 시간을 분석해 보았는데요. TTS로부터 장애 알림을 받은 후, 장애 대응 및 조치까지 걸리는 시간은 다음과 같았습니다. 2023년 1월부터 2024년 현재까지 발생한 이력을 x축으로, 장애 소요 시간을 y축으로 나타낸 그래프입니다.
대체로 장애 처리는 1시간 이내에 완료됐으나, 가장 오래 소요된 상위 5개 사례에서는 1시간을 초과한 것으로 나타났습니다. 결과를 분석해 보니 대체로 장애 급수가 낮을수록 장애 복구에 오래 걸리는 경향이 있다는 것을 알 수 있었는데요. 비즈니스에 끼치는 영향도가 낮은 장애라도 서비스의 이상 유무는 사용자에게 영향을 미칠 수 있기 때문에 모든 장애에 대해 신속하게 알림을 받고 처리하는 것이 필요했습니다.
보통 장애 처리는 크게 '장애 지속 시간을 줄이는 작업'과 '사후 처리'로 나눌 수 있습니다. 먼저 장애 지속 시간을 줄이는 작업은 다음과 같은 과정으로 진행됩니다.
- 이상 징후 탐지 : 시스템이나 서비스에서 정상적이지 않은 동작이나 성능 저하를 감지하는 과정
- 의사 결정 : 탐지된 이상 징후에 대한 대응 방안을 결정하는 과정
- 시스템 복구 : 장애를 해결하고 시스템이나 서비스를 정상 상태로 되돌리는 과정
다음으로 사후 처리 과정은 다음과 같습니다.
- 영향도 파악 : 장애가 시스템, 서비스, 사용자에게 미친 영향을 평가하는 과정
- 상세 원인 분석 및 재발 방지 : 장애의 근본 원인을 철저히 분석하고 동일한 문제가 다시 발생하지 않도록 예방 조치를 취하는 과정
저희는 그중에서 아래와 같이 장애 지속 시간을 줄이는 데 초점을 맞추고, 이를 위해 기존에 사용하던 TTS을 대체할 시스템 조사해 새로운 시스템을 구축하기로 결정했습니다.
장애 알림 및 관리 시스템에 사용할 대체 도구 조사 및 선정
기존 시스템을 대체할 새로운 시스템을 구축하기 위해서는 먼저 기존 시스템을 이해하는 것이 필요했습니다. 이에 기존 시스템이 제공하는 기능과 프로세스를 파악한 후 새로운 시스템이 이를 충분히 대체할 수 있는지 조사하기로 결정했습니다.
기존 시스템 - TTS(Ticket Trouble System)
저희가 기존에 장 애/변경 알림 및 관리에 사용하고 있던 시스템은 TTS라는 시스템입니다. 분석 결과 TTS는 다음과 같은 기능을 갖추고 있었습니다.
- 장애 등급 분류: 시나리오 테스트 케이스별로 장애 등급 분류
- E2E 시나리오 테스트 : 사용자 관점에서 시스템의 시작부터 끝까지 모든 기능이 의도한 대로 작동하는지 검증하는 테스트
- 알람 전파 시스템(메일): 감지된 이상 징후나 오류를 실시간으로 모든 이해관계자에게 전달
TTS는 사용할 때 먼저 거쳐야 하는 두 가지 과정이 있습니다.
첫째, TTS에 모니터링 관제를 등록하기 위해서는 ESM(Enterprise Service Master)이라는 시스템에서 서비스 코드를 발급받아 등록해야 합니다. ESM은 서비스 코드 단위로 회계 비용과 매출 성과를 집계하는 시스템으로 인프라 장비의 귀속이나 리소스 투입 실적 입력, TTS 장애 신고, 변경 공지 등에 사용하는데요. 신규 서비스가 출시되면 ESM에 등록하고 서비스 코드를 발급받으며, 이 서비스 코드가 있어야 TTS에 모니터링 관제를 등록할 수 있습니다.
둘째, TTS에 모니터링 관제를 등록할 때 장애 심각도(상/중/하)와 장애 등급 세부 기준, 모니터링 방식 및 범위 등을 TTS 담당자에게 전달해야 합니다. 예를 들어 TTS에서는 시나리오 테스트를 할 때 WMS(Web Monitoring System)라는 도구를 이용해 어느 URL에 접속해서 어디를 클릭해 어디를 확인해야 하는지 등의 시나리오를 정의해야 하는데 이와 같은 정보를 테스트할 각 페이지마다 정의해서 TTS 담당자에게 전달해야 합니다.
TTS를 사용할 때 이 두 가지 과정을 수행하기 위한 커뮤니케이션 비용이 많이 발생합니다. 서비스에 추가, 변경, 삭제 등의 변경이 발생하거나 모니터링 방식을 변경하고자 할 때에도 이에 따라 시나리오도 변경해야 했기 때문에 어려움이 많았습니다. 따라서 이 과정에 소요되는 시간을 줄이면 장애 관리가 좀 더 수월해질 것이라고 생각했습니다.
대체 시스템 조사
TTS의 기능과 사용 프로세스를 분석한 결과를 토대로 오픈소스에서 시나리오 테스트가 가능한 대체재를 찾아 각각의 장점과 단점을 아래와 같이 정리했습니다.
대체재 | 장점 | 단점 |
---|---|---|
Selenium |
|
|
Puppeteer |
|
|
Test Cafe |
|
|
Cypress |
|
|
Datadog |
|
|
Playwright |
|
|
위와 같이 여러 대안이 있었는데요. 저희는 각 대안의 단점에 주목하며 다음 기준을 바탕으로 대체재를 선정했습니다.
- 다중 브라우저 및 헤드리스 모드 지원: Chrome 외 다양한 브라우저에서도 테스트할 수 있으며, 모니터링 시스템의 탐지를 피할 수 있는 헤드리스 모드를 지원해야 합니다.
- 시나리오 스크립트 작성에 필요한 기능 지원: 다양한 API를 이용해 손쉽게 시나리오 스크립트를 작성할 수 있어야 합니다.
- 비용보다는 효용성에 방점: 비용이 발생하더라도 장애 관제 및 시나리오 테스트 측면에서 효용성이 높아야 합니다.
- 기업에서 사용 가능한 라이선스: 기업 환경에서도 자유롭게 사용할 수 있는 라이선스여야 합니다.
위 기준에 가장 부합하는 도구는 Playwright였고, 이 의견을 기반으로 최종적으로 Playwright가 선정됐습니다.
Playwright 소개
Playwright는 Microsoft에서 개발한 오픈소스 E2E 테스트 프레임워크로, 웹 애플리케이션을 다양한 브라우저 환경에서 자동화하고 테스트할 수 있도록 도와주는 도구입니다. Chromium과 Firefox, WebKit을 포함한 여러 브라우저를 지원하며, 단일 API로 다양한 브라우저와 상호작용할 수 있어서 이를 이용해 개발자와 테스터는 다양한 브라우저와 플랫폼에서 일관된 테스트를 실행할 수 있습니다.
Playwright에는 다양한 기능들이 있는데요. 그중 이번 시스템을 구축하면서 주로 사용했던 기능을 소개하겠습니다.
다중 브라우저 지원
Playwright는 Chromium과 Firefox, WebKit을 지원해 다양한 브라우저 환경에서 테스트를 실행할 수 있으며, 크로스 브라우저 테스트도 용이합니다.
const { chromium, firefox, webkit } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// 테스트 코드
await browser.close();
})();
헤드리스 모드
Playwright는 브라우저를 GUI 없이 실행할 수 있는 헤드리스 모드를 지원해 CI/CD 환경에서 쉽게 테스트를 자동화할 수 있습니다.
const browser = await chromium.launch({ headless: true });
스크린숏 및 비디오 녹화
Playwright는 테스트 실행 중 스크린숏과 비디오를 자동으로 캡처할 수 있어 디버깅과 문제 해결에 도움이 됩니다.
await page.screenshot({ path: 'example.png' });
테스트 재시도
Playwright는 실패한 테스트를 자동으로 재시도할 수 있는 기능을 제공해 일시적인 오류 때문에 발생하는 테스트 실패를 줄여줍니다.
const { test, expect } = require('@playwright/test');
test('example test', async ({ page }) => {
// 테스트 코드
}, { retries: 2 });
위와 같은 기능을 이용해서 Playwright를 다양하게 설정할 수 있는데요. 이와 관련된 상세한 테스팅 코드는 아래에서 살펴보겠습니다.
Playwright로 E2E 시나리오 테스트가 가능한지 PoC 진행
기존에 존재하던 시나리오를 기준으로 Playwright로 PoC(Proof of concept)를 진행했습니다. PoC 대상으로는 LINE NEXT에서 서비스하는 대표 사이트 중 하나인 DOSI Citizen을 선정했으며, 기존 시스템과의 정합성을 검증하기 위해 실제로 TTS에 등재돼 있는 시나리오 내용을 그대로 가져와서 진행했습니다.
아래 표는 각 시나리오의 상세 설명입니다. 각 시나리오에서는 사용자가 일반적으로 접근하는 기능(로그인 및 클릭)을 사용하거나 페이지에 접속한 후 영역 노출을 확인합니다. 이를 통해 페이지가 정상적으로 서비스되고 있는지 매분마다 자동으로 확인하고 있습니다.
시나리오 타입 | 시나리오 설명 | 검증 영역 |
---|---|---|
로그인 | https://citizen.dosi.world 접속 후 아래 과정 수행
| |
HOME | https://citizen.dosi.world 접속 | |
LNB(local navigation bar)(어드벤처) | https://citizen.dosi.world/adventure/1 접속 | |
LNB(공지) | https://citizen.dosi.world/support/notice 접속 | |
LNB(멤버십) | https://citizen.dosi.world/membership 접속 | |
상세(도움말) | https://citizen.dosi.world/support/help/category/20009798/document 접속 |
Playwright 코드는 Node.js 기반으로 작성했는데요. 각 요소별로 확인할 수 있는 API가 제공돼 손쉽게 시나리오 스크립트를 작성할 수 있었습니다.
PoC 결과
아래 영상은 위 시나리오를 기반으로 실제로 테스트한 결과입니다. Playwright 테스트는 헤드리스 모드로 실행할 수 있지만 작동 여부를 확인하기 위해 헤드리스 모드를 비활성화하고 테스트를 진행했습니다. 아래와 같이 내장된 Chromium을 실행해 테스트 결과를 기록해 보여주며, 이를 통해 어떤 부분을 확인하는지 알 수 있습니다.
아래와 같이 결과 리포트 출력도 가능합니다.
PoC를 통해 기존 TTS에서 지원하던 E2E 시나리오 테스트의 기능을 Playwright 대체할 수 있다는 결론을 내릴 수 있었습니다.
Playwright를 이용한 장애 알림 및 관리 시스템 설계 및 구현
PoC를 통해서 TTS 시스템의 E2E 시나리오 테스트가 Playwright에서도 가능하다는 것을 확인한 뒤 장애 등급 분류와 장애 티켓 관리, 알림 전파 시스템 기능도 대체할 수 있는지 확인했습니다. 확인 결과 아래와 같이 Jira와 봇(Bolt), Slack을 이용해서 기존의 TTS에서 수행하던 기능들을 대체할 수 있다는 결론을 내렸습니다. 이에 각 도구를 이용해 시스템을 구축한 뒤 Slack 봇과 연계해 모니터링부터 알림 전파, 장애 티켓 등록까지 Slack을 통해 가능하도록 구현 했습니다.
- 장애 등급 분류: Playwright 시나리오에서 제어합니다. 테스트 스크립트에서 장애의 중요도와 긴급성을 평가해 등급을 분류할 수 있습니다.
- 장애 티켓 관리: Jira의 일감 등록을 통해 제어합니다. Playwright의 테스트가 실패하면 해당 장애를 Jira의 일감으로 등록하고, 이후 Jira의 워크플로에 맞춰 "To Do", "In Progress", "Done", "Closed" 와 같은 상태로 관리합니다.
- 알림 전파 시스템: Slack을 연동해 전파합니다. Playwright의 테스트가 실패하면 Slack Webhook을 통해 해당 장애 관련 내용을 알림으로 전파합니다.
이와 같이 장애를 효율적으로 감지하고, 체계적으로 관리하며, 팀원들에게 신속하게 전파할 수 있도록 시스템을 설계 및 구현했습니다.
Playwright 폴더 구조 설계
저희가 설계한 폴더 구조는 아래와 같이 크게 네 부분으로 나눌 수 있는데요. tests 폴더 하위에 시나리오만 추가하면 공통 설정 및 함수를 그대로 사용할 수 있도록 설계했습니다.
- tests 폴더: .spec.ts 파일은 테스트 케이스를 정의합니다. 각 파일은 특정 기능이나 페이지를 테스트하는 여러 개의 테스트 케이스를 포함할 수 있습니다.
- .auth 폴더: 로그인 이후 브라우저 컨텍스트의 스토리지 정보(쿠키, 로컬 스토리지, 세션 스토리지 등)를 저장해 테스트 간 로그인이 지속되도록 만듭니다.
- Playwright.config.js: Playwright 테스트 프레임워크의 설정 파일로, 테스트 실행 환경과 관련된 다양한 설정을 정의합니다.
- commonFunctions.js: Slack 메시지 전송, 봇 호출, Jira 연동 등 공통으로 필요한 메서드들을 정의합니다.
|____Dockerfile (쿠버네 티스 컨테이너화를 위한 Dockerfile)
|____tests (Playwright 시나리오 테스트 파일)
| |____next-kaiawallet.spec.ts
| |____dosi-citizen.spec.ts
| |____dosi-store.spec.ts
| |____dosi-iam.spec.ts
| |____dosi-market.spec.ts
|____playwright.config.js (Playwright 설정 파일)
|____utils
| |____screenshots
| |____commonFunctions.js (Slack 연동, Jira 연동 등 공통 모듈)
|____playwright
| |____.auth(로그인 세션 정보)
| | |____dosi-store-user.json
| | |____dosi-citizen-user.json
| | |____dosi-iam-user.json
| | |____dosi-market-user.json
위와 같이 폴더 구조를 설계하면 Playwright의 tests 폴더에 새로운 시나리오만 추가하면 나머지 공통 설정 및 함수들은 이미 정의된 것을 그대로 활용할 수 있습니다. 이를 통해 소스 코드의 재사용성과 유지 보수성을 높였고, 시나리오를 일관되게 추가할 수 있습니다. 뒤에서 보다 자세히 살펴보겠지만 알림 전파와 장애 티켓 생성 및 관리도 자동으로 수행되기 때문에 새로운 프로덕트의 새로운 시나리오가 오면 시나리오에 집중해서 스크립트만 구성하면 됩니다.
Playwright 테스트 구조
다음으로 tests 폴더 하위에 위치한 시나리오 테스트 파일(xxxx.spec.ts)의 구조를 확인해 보겠습니다. 시나리오 테스트 파일 역시 구조가 잘 설계돼 있기 때문에 구조에 맞게 시나리오만 정의하면 되며, 대부분 아래 흐름에 맞춰 구성돼 있습니다.
- Before ALL: 모든 테스트 이전에 1회 수행(예: 리셋 캡챠)
- Serial TEST: 실제로 테스트를 수행하기 위한 시나리오 및 장애 등급을 매핑해 정의
- After EACH: 매 Serial TEST 실행 후 수행되며, 재시도 횟수만큼 모두 시도한 뒤에도 연속 실패가 발생했다면 알림 발송 및 Jira 장에 티켓 생성
- After ALL: 매분마다 테스트가 실행되며 모든 시나리오를 통과하면 현재 등록된 티켓을 완료 처리(auto-resolve).
위 구조에 맞춰서 Before ALL, After EACH, After ALL은 공통으로 사용하고 Serial TEST 부분만 각 프로덕트별 시나리오에 맞춰 작성할 수 있도록 구조를 설계했습니다. 새로운 프로덕트가 출시돼 장애 시나리오 시스템에 등록해야 할 때에는 개발 팀에서 장애 시나리오 정의서만 작성해 DevOps 팀에 전달하면 시나리오 스크립트를 작성해 바로 장애 시나리오 시스템에 적용해 모니터링할 수 있도록 구성했습니다.
테스트 시나리오 샘플
다음은 실제로 적용돼 있는 Serial TEST 시나리오 중 하나를 샘플링해서 기재한 것입니다. 위 내용 중 'Serial TEST > 시나리오 1'이라고 생각하시면 될 것 같습니다. 프런트엔드 테스트 스크립트와 백엔드(API) 테스트 스크립트를 각각 기재했습니다. LINE NEXT에서는 DOSI와 Alphacrewz, Kaia Wallet 등의 앱도 운영하고 있는데요. 앱은 현재 Playwright를 이용한 테스트가 불가능하기 때문에 백엔드 API 테스트가 되도록 설정했습니다.
- 프런트엔드 시나리오 스크립트
test('1 - BEERGANG STORE HOME', async ({page}, testInfo) => { testRanks['1 - BEERGANG STORE HOME'] = OUTAGE_RANK_MID; await page.goto('https://beergang.store.dosi.world/ko-KR/?_wms=true', { waitUntil: 'load' }); // 아이템 리스트 첫번째 요소 확인. const element = await page.getByRole('link').nth(1); await expect(element).toBeVisible({ timeout: 3000 }); await expect(element).toHaveCount(1); }); ...
- 테스트 정의
- 테스트의 이름을
'1 - BEERGANG STORE HOME'
으로 설정하고 비동기 함수로 정의합니다. page
객체는 Playwright에서 제공하는 페이지 객체로 브라우저 페이지와 상호작용할 수 있습니다.testInfo
객체는 테스트 실행 관련 추가 정보를 제공합니다.
- 테스트의 이름을
- 테스트 랭크 설정
testRanks
객체에'1 - BEERGANG STORE HOME'
테스트의 랭크를OUTAGE_RANK_MID
로 설정합니다.- 이를 통해 선정된 장애 등급과 시나리오를 연결할 수 있습니다.
- 페이지 이동
page.goto
메서드를 사용해 지정된 URL로 이동합니다.waitUntil
:'load'
옵션을 사용해 페이지가 완전히 로드될 때까지 대기합니다.
- 아이템 목록 첫 번째 요소 확인
page.getByRole('link').nth(1)
메서드를 사용해 역할이'link'
인 두 번째 요소를 선택합니다.element
변수에 해당 요소를 저장합니다.
- 요소 가시성 확인
expect
함수의toBeVisible
을 사용해element
가 페이지에서 보이는지 확인합니다.timeout: 3000
옵션을 사용해 타임아웃을 3초로 설정합니다.- 위 확인에 실패하면 테스트가 실패합니다.
- 요소 개수 확인
expect
함수의toHaveCount
어설션(assertion)을 사용해element
의 개수가 한 개인지 확인합니다.- 위 확인에 실패하면 테스트가 실패합니다.
- 테스트 정의
- 백엔드 시나리오 스크립트
async function apiTestAndRender(page, url, expectedKeys) { const context = await request.newContext(); const response = await context.get(url); expect(response.status()).toBe(200); const responseBody = await response.json(); // JSON 응답 본문에 필요한 키가 존재하는지 확인합니다. for (const key of expectedKeys) { expect(responseBody).toHaveProperty(key); } } ... test('6 - NFT 세부 정보 조회 불가', async ({ page }) => { testRanks['6 - NFT 세부 정보 조회 불가'] = OUTAGE_RANK_LOW; await apiTestAndRender(page, 'https://api.kaiawallet.io/api/v1/nft/8217/collections/0x2abec25064cbcd8d3c298903098840f95a432073/621', ['collection', 'holders']); }); ...
apiTestAndRender
함수 정의- 이 함수는 지정된 URL로
GET
요청을 보내고, 응답 상태 코드가 200인지 확인한 다음, 응답 본문에 지정된 키들이 존재하는지 검증합니다. request.newContext()
를 사용해 새로운 요청 컨텍스트를 생성합니다.context.get(url)
을 사용해 지정된 URL로GET
요청을 보냅니다.expect(response.status()).toBe(200)
를 사용해 응답 상태 코드가 200인지 확인합니다.response.json()
을 사용해 응답 본문을 JSON으로 파싱합니다.expectedKeys
배열에 있는 각 키가 응답 본문에 존재하는지 확인합니다.
- 이 함수는 지정된 URL로
- 테스트 정의
- 테스트의 이름을
'6 - NFT 세부 정보 조회 불가'
로 설정하고 비동기 함수로 정의합니다..
- 테스트의 이름을
- 테스트 랭크 설정
testRanks
객체에'6 - NFT 세부 정보 조회 불가'
테스트의 랭크를OUTAGE_RANK_LOW
로 설정합니다.- 이를 통해 선정된 장애 등급과 시나리오를 연결할 수 있습니다.
- API 호출 및 검증
apiTestAndRender
함수를 호출해 지정된 URL로GET
요청을 보냅니다.- 응답 상태 코드가 200인지 확인합니다.
- 응답 본문을 JSON으로 파싱합니다.
- 응답 본문에
'collection'
과'holders'
키가 존재하는지 확인합니다.
이 테스트는 지정된 URL로 이동한 후 역할이 'link'
인 두 번째 요소가 페이지에서 보이는지와 그 개수가 한 개인지 확인합니다. 이와 같은 방법으로 페이지의 특정 요소가 올바르게 로드되고 표시되는지 검증할 수 있으며, 이를 통해 기존 TTS에서 페이지 접속 후 영역 체크하는 기능을 대체할 수 있습니다.
또한 백엔드 API 같은 경우 해당 API를 호출해 설정된 키값들이 존재하는지 확인하는 방식으로 테스트하는데요. 이를 통해 사람이 직접 확인하지 않더라도 시나리오를 작성한 뒤 실행하기만 하면 자동으로 확인한 뒤 결과를 알림으로 보내줄 수 있는 시스템을 구축할 수 있겠다고 판단했습니다.
Playwright 운영 환경 구성 - 쿠버네티스 크론잡
Playwright는 위와 같이 각 팀별로 테스트 시나리오 정의서를 받아서 모두 스크립트로 등록했습니다. 이제 작성한 스크립트를 수행할 환경이 필요한데요. DevOps 팀에서 운영하는 쿠버네티스에 크론잡(cronjob) 형태로 수행할 수 있게 빌드 및 배포 환경을 구성했습니다. 구체적으로 말씀드리자면, Node.js 기반의 컨테이너에 Playwright를 설치하고 소스 코드를 옮겨 이미지를 빌드한 뒤, 빌드한 이미지를 기반으로 크론잡이 수행되도록 아래와 같이 크론잡 배포 파일을 작성했습니다. 배포 파일을 작성할 때에는 Helm 차트의 Values 파일에서 크론잡 내 여러 잡 파일의 배치 주기와 실행 인자를 개별적으로 설정할 수 있도록 구성했습니다.
crons:
- name: cronjob
list:
- name: "devops-dosiiamjob"
target: dosi-iam.spec.ts
schedule: "* * * * *"
suspend: false
- name: "devops-dosicitizenjob"
target: dosi-citizen.spec.ts
schedule: "* * * * *"
suspend: false
- name: "devops-dosistorejob"
target: dosi-store.spec.ts
schedule: "* * * * *"
suspend: false
- name: "devops-dosimarketjob"
target: dosi-market.spec.ts
schedule: "* * * * *"
suspend: false
위와 같이 구성한 뒤 쿠버네티스 크론잡을 실행하면 아래와 같습니다. 각 프로덕트별로 시나리오 구성 파일이 있고 이 파일이 매분 배치처럼 실행됩니다. 시나리오 구성 파일이 실행되면 각 프로덕트별로 작성된 시나리오가 매분 파드(pod)를 실행하고, 이를 통해 파드 내 Node.js 기반 Playwright가 실행됩니다. 실행 중 이상이 탐지되면 캡처하고 알림을 발송하는 등의 이후 액션이 실행되며, 이를 통해 유관 부서는 빠르게 장애를 탐지하고 상황을 공유하고 확인할 수 있습니다.
거짓 경보를 줄이기 위한 끝없는 튜닝
Playwright를 이용한 모니터링은 E2E로 사이트의 상태를 지속적으로 감시하면서 문제 발생 시 신속하게 경보를 발송해 운영 및 개발 팀이 문제를 해결할 수 있도록 도와야 하는데요. 이와 같은 경보 시스템이 항상 완벽하게 작동하는 것은 아닙니다. TTS 같은 경우 모니터링을 수행하는 인력이 24시간 배치돼 시스템이 감지한 알람을 2차로 확인한 뒤 장애 티켓을 발행하는 식으로 운영했는데요. 저희는 Playwright를 통해 이런 부분까지도 자동화해 최대한 사람의 개입 없이 장애를 제대로 탐지할 수 있도록 만들고 싶었습니다. 따라서 시스템을 구축하는 과정에서 발생했던 예기치 못한 거짓 경보(false positive)를 줄이기 위한 튜닝 작업이 매우 중요했습니다. 거짓 경보를 줄이기 위해 진행한 몇 가지 주요 튜닝 과정을 소개하겠습니다.
waitUntil 옵션 값 조정
waitUntil
옵션을 이용하면 페이지가 특정 상태에 도달할 때까지 대기하도록 만들 수 있습니다. 이 옵션은 페이지가 완전히 로드됐는지 혹은 특정 리소스가 모두 로드됐는지 등을 확인하는 데 유용하며, 다음과 같은 옵션 값을 설정할 수 있습니다.
networkidle
: 네트워크 연결의 유휴 상태가 일정 시간 동안 지속될 때까지 대기합니다. 페이지가 더 이상 네트워크 요청을 보내지 않을 때까지 대기하는 것을 의미합니다.load
: 페이지의load
이벤트가 발생할 때까지 대기합니다. 이 이벤트는 모든 리소스(이미지, 스타일 시트, 스크립트 등)가 로드된 후에 발생합니다.domcontentloaded
: 페이지의DOMContentLoaded
이벤트가 발생할 때까지 대기합니다. 앞서load
옵션은 여러 리소스를 로드할 때까지 기다리지만domcontentloaded
옵션은 DOM이 준비됐을 때 즉시 실행하므로 비교적 빠릅니다.
위 값 중 처음에는 networkidle
옵션을 사용했습니다. 대부분의 사이트가 API 호출을 통해서 작업을 진행하기 때문에 적절할 것이라고 생각했는데요. 특정 사이트(DOSI Market, DOSI Store 등)에서 간헐적으로 알림이 발생해 확인해 보니 해당 사이트에서 백그라운드로 지속적으로 호출하는 API가 있었고, 이 때문에 네트워크가 유휴 상태가 되지 못해 알림이 발생하고 있었습니다. 이와 관련해 확인해 보니 Playwright 공식 문서에서도 networkidle
옵션값은 가급적 사용하지 않도록 권고하고 있었습니다. 이에 기본값인 load
로 변경했고, 이후 더 이상 오탐이 발생하지 않았습 니다.
재시도 횟수값 조정
Playwright 수준에서의 재시도
Playwright는 테스트가 실패했을 때 자동으로 재시도하는 옵션을 제공하며, 이를 통해 일시적인 네트워크 문제나 서버 응답 지연 등으로 발생하는 오탐을 줄일 수 있습니다. 처음에는 오탐이 발생했을 때 이 옵션값을 1씩 늘리면서 조정했는데요. 재시도 수행이 늘어날수록 테스트 하나를 수행하는 데 걸리는 시간이 늘어나면서 오히려 테스트 수행이 지연돼 버렸습니다. 이를 해결하기 위해, 저희가 구축한 시스템은 크론잡을 통해서 매분 실행되기 때문에 최대 1분을 넘지 않는 기준에서 모든 시나리오가 적절하게 수행될 수 있도록 재시도 값을 조정해서 현재는 2로 설정돼 있습니다.
크론잡 수준에서의 재시도
며칠간 Playwright의 알림을 수신하며 확인해 보니 알림이 발생했지만 바로 다음 크론잡 파드가 정상 구동되면서 자동으로 해결(auto-resolve)되는 현상이 지속적으로 나타났습니다. 즉 Playwright의 재시도 설정 횟수만큼 재시도한 뒤 실패해서 알림이 발생했지만 다음 테스트가 성공하면서 정상화된 것입니다.
이런 현상을 개선하고자 Playwright 수준에서의 재시도 실패만으로 알림을 발송하지 않고 크론잡 수준에서도 살펴볼 수 있도록 파드 구동 수준에서도 재시도 횟수를 설정해 Playwright 수준에서의 재시도 실패 후 파드까지 연속으로 실패했을 때 알림이 발송되도록 설정했습니다.
아래와 같이 Playwright 수준에서 재시도 횟수만큼 모두 실패하고, 이후 파드 두 개가 연속적으로 실패하는 두 가지 조건에 해당할 때 알림이 발생하도록 설정했습니다.
이와 같이 일시적인 문제로 발생하는 오탐을 줄이고 연속적으로 실패했을 때 알림을 전송하는 방식을 적용, 애플리케이션의 안정성을 유지하고 신뢰할 수 있는 모니터링 시스템을 구현해 운영 팀의 효율을 높일 수 있었습니다.
봇 개발 및 Playwright와 연동
지금까지 TTS 기능 및 대체재 조사를 시작으로 Playwright 선정 과정과 주요 기능을 살펴본 뒤 Playwright를 이용해 어떻게 장애 알림 및 관리 시스템을 구성했는지 소개했습니다. 이제 봇을 통해 Playwright를 이용한 E2E 테스트부터 Jira 티켓까지 연계한 구성을 소개하겠습니다.
Playwright 전체 구성도
아래는 앞서 소개한 Playwright를 이용한 장애 알림 및 관리 시스템의 전체 구성도입니다. 장애와 변경의 두 가지 Jira 프로젝트를 관리하고 있습니다.
- 장애 관리
Playwright는 1분마다 각 프로덕트별로 E2E 테스트를 실시합니다. 만약 테스트에서 이상을 탐지하면 현재 같은 서비스 코드(제품 코드)로 장애 티켓이 등록돼 있는지 확인하고, 티켓이 없으면 장애 티켓을 생성하고 알림을 발송합니다. 유관 부서에서는 생성된 장애 티켓과 Slack으로 전송된 알림 스레드를 확인하고 장애 대응을 진행합니다. 장애 처리가 완료되면 해당 티켓에 사후 대응 방안을 기재한 뒤 포스트 모템(post-mortem)을 진행합니다. - 변경 관리
사용자(개발자)가 Slack 워크플로를 통해서 배포 요청서를 작성하면 봇이 해당 정보를 기반으로 Jira API를 호출해 변경 관리 티켓을 생성합니다. 유관 부서에서는 생성된 변경 티켓을 확인하고 변경 작업을 진행합니다.
모든 내용은 Jira 티켓으로 관리되며, 모든 히스토리와 내역이 Jira 티켓에 기재됩니다. 이를 통해 변경 혹은 장애와 관련된 데이터를 분석할 수 있는데요. 가령 데이터를 이용해 '연간 장애 건수 / 변경 내역 * 100'을 계산하면 변경에 따른 장애 건수를 파악할 수 있으며, 이와 같은 분석 결과를 토대로 변경 작업을 안전하게 진행해 사이트의 신뢰를 높일 수 있습니다.
봇과 Jira 연동하기
앞서 Playwright를 이용한 장애/변경 알림 및 관리 시스템과 Jira를 어떻게 연계했는지 살펴봤습니다. 이제 여기에 봇을 연동해
Playwright를 이용해 검증한 후 봇을 호출해 Jira의 티켓 생성되는 현황 및 기능들에 대해서 소개를 하도록 하겠습니다. 다음 정보를 각 시나리오별로 매핑할 수 있습니다
- 서비스 코드: 각 제품별로 유니크한 정보를 기재합니다. 키값이라고 이해하면 됩니다.
- 시나리오 장애 등급: 각 시나리오별로 경중을 나누어 장애 등급을 산정 후 연결합니다.
- 시나리오 이름: 현재 시나리오의 이름을 기재합니다.
- 그룹 멘션: 각 시나리오별로 알림을 보낼 멘션 그룹을 기재합니다.
알림이 발생하면 다음과 같은 모습으로 Slack에 알림을 발송하고 Jira에 티켓을 생성합니다.
캡처 기능
서두에 해당 시스템을 구성하는 목적 중 하나가 이상 탐지 후 유관 부서가 빠르게 장애 조치를 진행할 수 있도록 돕는 것이라고 했는데요. 이때 관련 정보를 단순 텍스트로만 안내하면 현재 장애가 어떤 내용인지 파악하기가 어려울 것입니다. 이에 현재 내용을 캡쳐해 Slack 파일 업로드 기능을 통해서 추가 정보를 전달합니다. 이와 같은 추가 정보는 유관 부서에서 빠르게 현재 상황을 캐치할 수 있도록 도와 어느 영역이 문제인지 파악하는 데 걸리는 시간을 줄일 수 있습니다.
예를 들어 아래는 https://beergang.store.dosi.world/ko-KR/marketplace/ 접속 후 API를 호출한 뒤 그 결과를 화면에 표시해 주지 못하는 문제를 예시로 캡처한 것입니다. 이 추가 정보를 이용해 유관 부서에서는 해당 API와 관련한 문제를 빠르게 확인할 수 있습니다.
Jira 티켓 생성 기능
알림을 발송하면 발생한 알림 정보를 웹훅(아래 화면의 Kisimen)을 이용해 봇에게 전달합니다. 봇은 일종의 프락시 역할을 하는 애플리케이션으로, 전달받은 정보를 기반으로 Jira API를 호출해 장애 티켓을 생성합니다. 이를 통해 티켓으로 장애를 관리할 수 있습니다.