자동화 파이프라인9 분 읽기

감시판을 산출물로 바꿨는데도 두 번의 실패를 놓쳤습니다

종료 코드는 거짓말을 하니 산출물 신선도를 보게 감시판을 다시 만들었습니다. 그런데 사전등록한 실험의 한쪽 팔이 두 번 연속 실패했는데 감시판은 계속 정상이었습니다. 폴백이 매번 다른 팔로 발행을 채웠기 때문입니다.

#monitoring#automation#measurement#gates#failure-handling
두 패널 도식. 왼쪽은 감시판이 본 것으로, 실험군 날짜마다 영상이 발행돼 산출물이 신선하고 상태가 정상으로 표시된다. 오른쪽은 실제로 일어난 것으로, 실험군 B가 두 번 연속 실패해 대조군 A로 폴백했고 B의 표본은 늘지 않아 판정일이 두 달 밀렸다.
무엇이 찍혔는지가 아니라, 어느 팔이 찍었는지를 세야 했습니다.

한 달 전에 감시판을 다시 만들었습니다. 이유가 있었습니다. 예약 실행 잡 43개가 전부 종료 코드 0이었는데, 그중 하나는 3주째 아무것도 만들지 않고 있었습니다.

그래서 감시판의 원칙을 바꿨습니다. "상태 코드는 묻지 않는다. 마지막으로 무엇을 만들었나만 본다." 잡마다 산출물 파일의 수정 시각과 로그 마지막 줄을 읽어서, 기대 주기를 넘기면 지연·실패로 칩니다.

오늘 그 감시판이 초록인 채로 두 번의 실패를 넘겼습니다.

당신의 감시판은 "무엇이 만들어졌나"를 봅니까, **"누가 만들었나"**를 봅니까?

사전등록한 실험

숏폼 채널에서 콘텐츠 유형 A/B를 돌리고 있습니다. 규칙을 미리 적어뒀습니다.

  • A = 템플릿형. 앱 이름과 훅을 얹는 기존 방식.
  • B = 실측 수치 1인칭. 실제 사용 데이터에서 숫자를 뽑아 만드는 방식.
  • 화·목·토를 B로 잡는다. 3주면 B가 9일 × 3채널 = 27편으로 필요 표본을 채운다.
  • B 생산이 실패하면 그날은 A로 되돌아간다 — 하루를 통째로 버리지 않는다.

마지막 줄이 오늘의 주인공입니다. 저는 이걸 방어 장치로 넣었습니다. 실제로는 계측을 가리는 장치였습니다.

감시판이 본 것

B군 날짜마다 발행은 정확히 나갔습니다. 산출물 파일이 갱신됐고, 로그 마지막 줄에 게시 URL이 찍혔고, 감시판은 정상이었습니다.

로그를 시간순으로 펼치면 이렇습니다.

[08-29 19:30] B군 실패 rc=1 ... 
[08-29 19:30] B군 실패 → 오늘은 A군(템플릿)으로 진행
[09-01 19:30] B군 게시 완료: ... OK https://youtu.be/...
[09-03 19:30] B군 실패 rc=1: ERR_MODULE_NOT_FOUND
[09-03 19:30] B군 실패 → 오늘은 A군(템플릿)으로 진행
[09-05 19:30] B군 실패 rc=1: ERR_MODULE_NOT_FOUND
[09-05 19:30] B군 실패 → 오늘은 A군(템플릿)으로 진행

B군 4회 시도, 1편. 그리고 그 4일 모두 발행 자체는 나갔습니다. 산출물 신선도만 보는 감시판에게 이 나흘은 구분이 없습니다.

실패 → A로 진행이라는 줄이 로그에 멀쩡히 있었는데, 감시판은 로그의 마지막 줄만 읽습니다. 마지막 줄은 언제나 A의 성공 메시지였습니다.

원인은 세 줄짜리였습니다

B군 렌더러가 브라우저 자동화 라이브러리를 씁니다. import 줄이 이랬습니다.

import { chromium } from "/Users/.../webApps/hwaksin/node_modules/playwright/index.mjs";

다른 프로젝트의 node_modules를 절대경로로 가리키고 있었습니다. 그 프로젝트에서 의존성을 정리한 순간 ERR_MODULE_NOT_FOUND가 났습니다. 이 렌더러는 그 프로젝트와 아무 관계도 없습니다. 한때 거기에 라이브러리가 설치돼 있었다는 것 말고는요.

고친 건 두 줄입니다. 렌더러 자기 디렉터리에 라이브러리를 설치하고, 베어 import로 바꿨습니다.

import { chromium } from "playwright";

진짜 대가는 영상이 아니었습니다

발행은 안 끊겼으니 손실이 없어 보입니다. 아닙니다. 사전등록한 실험의 분모가 조용히 안 차고 있었습니다.

B 27편 요건을 다시 계산하면 이렇습니다. 지금 B는 1편이고, B군 날짜는 주 3회입니다. 오늘부터 100% 성공해도 남은 26편에 약 8.7주 — 11월 초입니다. 원래 잡아둔 판정일보다 두 달 가까이 밀렸습니다.

즉 이 실패의 비용은 "영상 3편"이 아니라 **"실험 두 달"**입니다. 그리고 그 두 달은 감시판 어디에도 표시되지 않았습니다.

당신이라면?

폴백이 있는 파이프라인을 감시해야 합니다. 어느 쪽을 세겠습니까?

  • 산출물이 신선한가 — 지금 하는 것. 폴백이 채운 산출물과 본래 팔이 만든 산출물을 구분하지 못한다.
  • 폴백이 몇 번 발동했나 — 발동 횟수를 세서 임계를 넘으면 알린다. 원인은 안 보이지만 구분은 된다.
  • 팔별 산출물 수를 센다 — 실험을 하는 중이라면 이것이 곧 분모다.

세 번째가 답입니다. 그리고 이건 감시 기능이 아니라 게이트의 일부입니다. 사전등록한 규칙에 "임계"만 적고 "분모가 어떻게 차는지"를 안 적으면, 분모가 안 차는 상태와 실험이 실패한 상태가 같은 그림으로 보입니다. 분모가 없던 게이트에서 이미 한 번 배운 것이고, 판정일이 밀리는 동안 기준이 세지는 것도 같은 뿌리입니다.

자가진단 세 줄

  1. 폴백이 성공을 대신 채우고 있습니까? 그렇다면 산출물 신선도는 "무언가 만들어졌다"만 말하고 "의도한 것이 만들어졌다"는 말하지 않습니다.
  2. 감시판이 로그의 마지막 줄만 읽습니까? 폴백이 있는 파이프라인에서 마지막 줄은 언제나 성공입니다. 실패는 중간에 있습니다.
  3. 사전등록한 실험에 분모 진행률이 표시됩니까? "B 1/27"이 감시판에 있었다면 8월 29일에 알았을 겁니다. 없어서 9일 뒤에 알았습니다.

솔직한 부분

감시판을 산출물 기준으로 바꾼 건 여전히 옳은 결정이었습니다. 종료 코드 0에 3주째 0편이던 잡은 그렇게 잡았습니다.

다만 오늘 배운 건 이겁니다. 폴백은 신뢰성 장치이면서 동시에 계측 파괴 장치입니다. 하나를 넣으면 다른 하나가 생깁니다. 그래서 폴백을 넣는 순간 같이 넣어야 하는 게 있습니다 — 어느 팔이 그 산출물을 만들었는지 세는 칸.

지금 딱 하나만 해보세요. 폴백이 있는 잡의 로그에서 폴백·fallback 문자열을 grep -c 해보는 겁니다. 그 숫자가 0이 아니고 감시판이 초록이면, 오늘 제 자리에 앉아 있는 겁니다.

관련 글