GitHub Actions 캐시 무효화로 CI 시간이 폭증한 사례와 대응 가이드
사건 개요 — GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례
한 조직에서 갑작스럽게 GitHub Actions 파이프라인 실행 시간이 몇 분에서 수십 배로 늘었습니다. 원인은 의도치 않은 캐시 무효화였습니다. 반복해서 생성되던 의존성과 빌드 아티팩트가 매 실행마다 다시 내려받아지고 재생성되면서 전체 워크플로가 지연되었습니다.
- 증상: 캐시 히트율이 급격히 떨어졌고, 개별 잡 실행 시간이 10분에서 1시간 이상으로 늘었습니다. 동시에 실행 대기열(백로그)이 누적되었습니다.
- 영향 범위: PR 병합 지연과 배포 파이프라인 정체가 발생했습니다. 빌드 자원·네트워크 비용이 증가했고, 개발 생산성이 저하되었습니다.
- 발생 시점·배경: 특정 커밋에서 캐시 키 포맷이 변경되거나 외부 의존성의 버전 정책이 바뀐 직후에 문제가 시작되었습니다. 캐시 보존 정책과 용량 한계가 결합하며 문제는 빠르게 확산되었고, 실무 체크리스트로는 — 캐시 키 변경 시 영향 범위 검토(네이밍 표준화 포함), 보존 정책·사이즈 제한 문서화, CI 실행 전 캐시 히트율 모니터링을 권장합니다.
GitHub Actions 캐시의 동작 원리와 제한사항
actions/cache는 워크스페이스 파일을 키(key)로 구분해 GitHub 백엔드에 업로드하고, 필요할 때 해당 키로 다운로드합니다. 정확히 일치하는 키가 있으면 그 캐시를 우선 복원합니다. 일치하는 키가 없다면 restore-keys 목록을 위에서부터 순서대로 탐색해 접두사가 일치하는 첫 항목을 사용합니다. restore-keys는 완전 일치 실패에 대비한 그레이스케일 복원 전략이며, 여러 캐시를 병합해 복원하는 기능은 아닙니다.
- 업로드는 '마지막 쓰기 우선(Last write wins)' 방식입니다. 동시 업로드로 충돌이 나면 어느 워크플로가 먼저 업로드했느냐에 따라 최종 캐시 내용이 달라집니다. 이 때문에 캐시 불일치가 발생하거나 자주 재생성되면 CI 시간이 크게 늘 수 있습니다 — 예: GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례처럼.
- 캐시는 크기 제한, 동시 업로드 한도, TTL(만료 정책) 등 운영 제약을 갖습니다. 일정 기간 후 만료되며 저장소 전체 용량, 개별 캐시 최대 크기, 동시 업로드 횟수 같은 제한에 걸리기 쉽습니다.
- 키 설계가 부실하면 매 빌드마다 새로운 키가 만들어져 캐시 이점이 사라지고 빌드 시간이 급증합니다. 키는 변경 빈도와 범위를 최소화해야 하며, 재현 가능한 입력(예: 종속성 잠금 파일)을 기준으로 삼으세요. 실무 체크리스트: 종속성 잠금 파일 사용, 불필요한 파일 제외(.gitignore에 항목 추가), 키에 타임스탬프 포함 금지.
무효화를 촉발한 주요 원인들
실제 운영 환경에서는 캐시 무효화로 인해 CI 시간이 급증하는 사례에서 비슷한 원인이 반복되는 경우가 많다. 아래 네 가지로 요약하며, 각 항목에 발생 메커니즘과 빠른 진단 포인트를 함께 적어 문제 범위를 좁히는 데 도움을 준다.
- 잘못된 키 설계 — 타임스탬프, 전체 커밋 SHA 또는 CI 전역 환경변수를 캐시 키에 포함하면 키가 매 빌드마다 달라져 히트율이 사실상 0%가 된다. 점검 포인트: 최근 키 패턴에 불필요한 변수가 섞여 있는지 확인하고, 가능한 경우 빌드 입력만으로 키를 구성하라.
- 경로·파일명 변경 — 빌드 입력 경로나 캐시 대상 파일명이 바뀌면 기존 키로는 캐시를 찾지 못한다. 점검 포인트: 워크플로에서 참조하는 우선순위 파일과 캐시 경로가 일치하는지 검토하라.
- 의존성 변경·락 누락 — lockfile을 커밋하지 않거나 ^, ~ 같은 범위 연산자로 설치 결과가 달라지면 캐시와 실제 설치 결과가 불일치한다. 점검 포인트: lockfile이 저장소에 있는지, 패키지 매니저 옵션이 고정돼 있는지 확인하라.
- 브랜치/PR 전략 문제 — 브랜치별 키 분리, 포크 PR의 권한 제한, merge vs head 참조 혼용 등으로 캐시가 공유되지 않을 수 있다. 점검 포인트: 브랜치 네이밍과 PR 권한, 키 네이밍 전략을 일관되게 관리하라.
빠른 점검 체크리스트
우선 캐시 키에 불필요한 변수가 포함돼 있는지 확인하고, lockfile을 일관되게 관리하며, 브랜치 정책에 맞춰 키 네이밍을 표준화하면 상당 부분 문제를 예방할 수 있다. 실무 팁: package-lock.json 또는 yarn.lock이 커밋되어 있는지 한 번 점검하라. 이렇게 기본 점검만으로도 GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례를 크게 줄일 수 있다.
사례 진단 방법 — 로그와 메트릭으로 원인 추적하기
캐시 무효화로 CI 시간이 늘었다면, 로그와 메트릭을 신속히 교차검증하세요. 특히 GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례를 조사할 때 유용한 확인 포인트입니다.
- 캐시 히트·미스 로그: 워크플로 로그에서 "Cache restored from key" / "No cache found" 또는 "Cache not found for input keys" 메시지를 찾아보세요.
actions/cache가 출력하는 플래그(예:cache-hit)로 언제 키가 바뀌었는지 추적합니다. 복수의 restore key와 해시 기준(예: lockfile 변경)을 비교해 원인을 좁히세요. - 스텝별 실행시간: GitHub Actions UI에서 각 스텝과 잡의 실행 시간을 비교하세요. 캐시 복원 대신 실행된 단계(예: 의존성 설치)가 어느 정도 지연을 만들었는지 파악합니다. 동일 커밋 전후를 병렬로 보며 지연이 급증한 시점을 특정하세요.
- 아티팩트·네트워크 I/O: 아티팩트 업로드·다운로드 크기와 소요 시간, 로그에 기록된 전송량을 점검하세요. Self-hosted 러너라면 네트워크 대역폭과 디스크 I/O 지표(iftop, iostat 등)를 함께 확인해 병목을 찾습니다. 간단 체크리스트: 1) 캐시 restore 로그 확인, 2) 느려진 스텝 확인, 3) 아티팩트 전송량·I/O 점검.
즉시 적용 가능한 완화책과 빠른 복구 전략
GitHub Actions에서 캐시 무효화로 CI 시간이 급증했을 때 당장 적용할 수 있는 실무 조치들입니다. 우선순위가 높은 항목부터 빠르게 시도하세요. 체크리스트(간단): 1) 문제 격리 2) 임시 롤백 3) 캐시 분할 4) 히트율 모니터링- 키 버전화·범위 축소 — 새 문제가 발생하면 모든 키를 한꺼번에 교체하지 마십시오. 대신 기존 키를 고정(pin)하거나 버전 범위를 축소(예: major 버전만 포함)해 피해 확산을 억제합니다. 임시로 key: restore-v1-${{ runner.os }} 형태로 롤백하는 것이 빠른 복구에 도움이 됩니다.
- 부분 캐시화 — 전체 캐시를 복원하는 대신 빌드 비용이 큰 항목만 별도 캐시로 분리하세요. 예를 들어 대형 node_modules, 빌드 아티팩트, 의존성 캐시를 나누면 복원 부담이 크게 줄어듭니다.
- restore-keys 활용 — 폭넓은 restore-keys를 설정하면 완전 미스 상황에서도 일부 히트를 받아 재빌드 비용과 시간을 낮출 수 있습니다. 부분 히트가 전체 재빌드를 완화합니다.
- 압축·병렬 복원 — 업로드 시 zstd나 gzip 같은 효율적 압축을 사용하고, 복원은 여러 소규모 캐시로 나눠 병렬로 수행하세요. 네트워크 병목을 줄이고 복원 지연을 완화할 수 있습니다.
- 빠른 복구 전략 — 문제 확인 즉시 정상 키로 롤백하거나 캐시를 일시 비활성화한 뒤 병렬 빌드로 캐시를 다시 채웁니다. 이후 히트율과 빌드 시간을 주기적으로 모니터링하며 점진적으로 적용하세요.
운영 체크리스트와 장기적 예방 대책
아래 항목을 운영 체크리스트로 활용해, GitHub Actions에서 캐시 무효화로 CI 시간이 폭증하는 상황을 예방하고 신속히 대응하세요. (예: GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례에서 얻은 교훈을 반영했습니다.)
- CI 관찰성·알림: 평균 빌드 시간, 캐시 적중률(cache hit rate), 캐시 무효화 이벤트 빈도, 파이프라인 성공률을 수집합니다. 임계치(예: 빌드 시간 2배 증가, 적중률 50% 미만)를 넘기면 Slack/PagerDuty로 알림을 보내고 자동 티켓을 생성하세요.
- 캐시 정책 문서화: 캐시 키 전략, TTL, 적용 범위(브랜치별·워크플로별)와 어떤 변경에서 무효화되는지(예: 종속성 파일 수정)를 명확히 문서화합니다. PR 템플릿에 캐시 영향 확인 항목을 추가해 변경 시 점검을 의무화하세요.
- 주기적 검토·테스트: 주간 또는 월간으로 캐시 무효화 시나리오를 시뮬레이션하고, 의도적으로 캐시를 비활성화한 스모크 빌드로 회귀를 확인합니다. 간단 체크리스트 예시 — (1) 캐시 키 변경 여부 확인, (2) 스모크 빌드 실행, (3) 빌드 시간·아티팩트 비교 및 로그 검토. 변경 시마다 캐시 관련 통합 테스트도 실행하세요.
- 비용·SLA 관리: CI 실행 시간 기반 비용 대시보드와 예산 알람을 설정합니다. 캐시 미스로 인한 시간 증가가 SLO를 위협하면 에스컬레이션 경로를 정의하고, 비용 대비 성능 트레이드오프 기준을 문서로 남기세요.
- 운영 자동화·소유권: 캐시 재생성 스케줄(예: 야간)과 책임 팀을 지정하고, 문제가 발생했을 때 따를 간단한 런북(runbook)과 복구 절차를 마련해 유지합니다.
경험에서 배운 점
대형 리포지토리에서 캐시 키 설계가 부적절해 한 번의 키 변경으로 의존성·빌드 캐시가 전면 무효화된 적이 있습니다. 그 결과 모든 파이프라인이 캐시를 재생성하느라 CI 시간이 크게 늘었고, 릴리스 지연과 개발자 생산성 저하로 이어졌습니다. 이 사례는 GitHub Actions에서 캐시 무효화로 CI 시간이 폭증한 사례로 자주 언급됩니다. 주요 실수는 캐시 키에 불필요한 변수(예: commit SHA, 타임스탬프)를 포함해 재현성을 해친 점, 툴체인·의존성·아티팩트 등 용도가 다른 캐시를 단일 키로 묶어 단일 실패 지점을 만든 점, 그리고 캐시 미스 발생 시 이를 감지하거나 복구할 절차가 없던 점입니다.
실무 체크리스트(재발 방지 중심): 캐시 키에는 런타임·OS·도구 버전처럼 안정적인 식별자만 포함하고 commit SHA나 타임스탬프는 사용하지 않습니다. 캐시를 역할별로 분리해 툴체인, 패키지 의존성, 빌드 산출물을 따로 관리하세요. restore-keys(부분 키)를 활용해 히트율을 높이고, 키를 변경할 때는 명확한 마이그레이션 전략을 마련합니다. 변경은 Canary나 순차 롤아웃으로 적용하고, 사전 dry-run으로 예상 캐시 히트율을 확인하세요. 모니터링은 cache hit/miss 비율과 워크플로 실행 시간을 계측해 임계값 초과 시 알림을 받도록 설정합니다. 복구 계획은 대규모 무효화 시 이전 아티팩트 저장소에서 빠르게 복원하거나 임시 인스턴트 빌드로 대응할 수 있도록 준비합니다. 마지막으로 캐시 정책을 문서화하고 책임자(owner)를 지정해 임의 변경을 막으세요. 예: node_modules 캐시는 OS별·Node 버전별로 분리해 관리하면 부분 히트로 복구 가능성을 높일 수 있습니다.
댓글
댓글 쓰기