LabHub
배우기 러닝패스 코스

CI/CD 파이프라인 · 실패 분류와 원인 좁히기 · 이론

빨간불이 다 같은 빨간불은 아니다

LabHub 에서 이어서 보기

한 줄 요약

파이프라인의 실패는 인프라 문제·불안정한 시험·진짜 결함 세 갈래로 갈린다. 갈래를 나누지 않으면 사람은 전부 "다시 돌려" 로 대응하고, 진짜 결함이 그 안에 숨는다.

왜 이게 필요했나

실패가 며칠에 한 번이면 사람이 로그를 읽는다. 하루에 열 번이면 읽지 않는다. 재시도 단추가 먼저 눌리고, 통과하면 잊힌다. 이 습관이 붙은 팀에서는 진짜 결함도 두세 번 재시도되다가 어쩌다 통과하면 그대로 병합된다. 빨간불이 신호이기를 그만두는 순간이다.

그래서 필요한 것은 더 좋은 로그가 아니라 분류다. 이 실패가 어느 갈래인지 먼저 정하고, 갈래마다 다른 대응을 한다.

가르는 방법은 의외로 단순하다. 같은 커밋을 다시 돌려 결과가 갈리는가. 갈리면 불안정이거나 인프라이고, 늘 같으면 진짜 결함이다. 그래서 파이프라인은 "이 실행이 어느 커밋이었는지" 를 결과에 반드시 남겨야 한다.

재현 가능한 실패로 만든다

불안정과 진짜 결함을 가르려면 같은 조건을 다시 만들 수 있어야 한다. 그래서 실행마다 다음을 결과에 적어 둔다.

이 세 가지를 남기는 것만으로 "재현이 안 돼요" 라는 보고가 크게 줄어든다.

언제 깨졌는지 찾는다

실패의 원인 커밋을 찾을 때 로그를 거슬러 읽는 방법은 커밋 수에 비례해 시간이 든다. 이분 탐색은 로그 곱하기 상수다. git bisect 문서는 675개 리비전을 대략 10단계, 337개를 대략 9단계로 좁힌다고 적는다. 커밋 1000개를 열 번으로 좁힌다는 뜻이다.

자동화하려면 조건이 하나 있다. 판정 스크립트가 결정적이어야 한다. 같은 커밋에서 같은 답을 내지 않으면 이분 탐색은 엉뚱한 커밋을 범인으로 지목한다. 그래서 불안정한 시험으로는 이분 탐색을 돌리면 안 된다.

git bisect run 은 스크립트의 종료 코드로 판정한다. 규약이 정확히 정해져 있다.

0          이 커밋은 정상(good/old)1..127     이 커밋은 문제 있음(bad/new)   단, 125 는 제외125        판정할 수 없음 — 이 커밋은 건너뛴다(skip)128..255   이분 탐색 자체를 중단한다

125 가 따로 있는 이유가 중요하다. 빌드가 아예 안 되는 커밋처럼 판정 불가를 "문제 있음" 으로 보고하면 범인이 그쪽으로 잘못 몰린다. 그리고 126과 127은 POSIX 셸이 "실행할 수 없음", "명령을 찾을 수 없음" 에 쓰는 값이라 125가 이 용도로 쓸 수 있는 가장 큰 값으로 선택됐다. 판정 스크립트를 쓸 때 exit -1 같은 것을 쓰지 않도록 조심한다. 그 값은 255가 되어 탐색을 통째로 중단시킨다.

되먹임 시간 예산

되먹임 시간에 숫자로 된 상한을 두지 않으면 파이프라인은 조용히 느려진다. 한 단계씩 늘어난 시간은 아무도 눈치채지 못하고, 어느 날 "원래 좀 오래 걸려요" 가 된다.

상한을 넘겼을 때 할 일은 정해 두는 편이 좋다. 병합을 막는 단계와 막지 않는 단계를 나눠 느린 것을 뒤로 보내거나, 시험을 나누거나, 자주 깨지지 않는 영역의 시험을 변경 범위에 따라 건너뛰는 식이다. 어느 쪽이든 무엇을 포기했는지 기록해야 한다. 포기한 것을 적어 두지 않으면 몇 달 뒤 그 영역에서 사고가 났을 때 왜 못 잡았는지 아무도 설명하지 못한다.

현장에서 만나는 모습

참고

다음 실습에서 할 것

git 저장소를 하나 만들어 커밋을 쌓고, 중간 어디선가 깨지게 해 둔 다음 범인을 이분 탐색으로 찾는다. 먼저 손으로 git bisect start / good / bad 를 돌려 몇 단계 만에 좁혀지는지 세고, 같은 일을 판정 스크립트로 자동화한다. 스크립트가 종료 코드 규약을 지키는지 확인하고, 빌드가 안 되는 커밋을 일부러 끼워 넣어 125로 건너뛰지 않으면 범인이 어떻게 잘못 지목되는지 를 직접 본다. 그다음 결과가 갈리는 판정 스크립트를 넣어 이분 탐색이 무너지는 것을 확인하고, 마지막으로 실패 로그를 갈래별로 세어 보고하는 분류 스크립트를 쓴다.