CI 비용 관리 가이드
GitHub Actions 비용은 실행 시간 × 실행 횟수로 결정된다. 이 저장소들에서 두 값은 다음처럼 측정됐다(2026-09-14, 최근 30일).
측정
한 실행의 실제 작업 시간은 짧다. 문제는 횟수다.
| 저장소 | 실행(30일) | 내역 | 대략 소모 |
|---|---|---|---|
| PortalConsole | 444회 | push(main) 411 · PR 33 | 약 660분 |
| UiPlatform | 233회 | push(main) 220 · PR 13 | 약 280분 |
| portal-foundation | 88회 | 태그 게시 59 · 수동 실행 29 | 약 185분 |
| ModelGateway | 14회 | push 7 · PR 7 | 약 20분 |
| 합계 | 779회 | 약 1,150분/월 |
실행 1회의 실제 소모 시간(actions/runs 단계별 측정):
| 워크플로 | 설치 | 검증 | 빌드 | 작업 합계 |
|---|---|---|---|---|
| Verify Directory Service | 9초 | 47초 | 10초 | 약 1분 30초 |
| Publish portal-foundation | 6초 | — | 109초(검사·테스트·빌드) | 약 2분 |
횟수를 만든 것은 워크플로 설정이 아니라 main 커밋 수다.
| 저장소 | 최근 30일 main 커밋 | 하루 최대 |
|---|---|---|
| PortalConsole | 523 | 70 (09-07) |
| portal-foundation | 406 | 71 (09-07) |
| UiPlatform | 266 | 103 (09-06) |
커밋 1건이 곧 실행 1회이므로, 비용을 줄이는 일은 대부분 "푸시 횟수를 줄이는 일" 이다.
적용한 것 (워크플로)
- 중복 실행 취소 —
concurrency+cancel-in-progress: true. 같은 브랜치에 연달아 푸시하면 이전 실행을 취소하고 최신 커밋만 검증한다. 순간적으로 몰아치는 푸시에서 효과가 크다. - 문서 전용 변경 제외 —
paths-ignore: ["**/*.md", "docs/**"]. 문서만 바뀐 변경은 전체 검증을 돌리지 않는다. 문서 게시(publish-docs)는 별도 워크플로가 담당하므로 영향이 없다. - CI 생략 키워드 — 커밋 메시지에
[skip ci]·[ci skip]·[no ci]·[skip actions]·[actions skip]중 하나가 들어 있으면 GitHub가 실행 자체를 만들지 않는다. 워크플로에 조건을 넣는 방식보다 낫다(실행이 생성되지 않으므로 대기 시간도 안 쓴다) — 그래서 직접 만든 잡 조건은 두지 않았다. 버전 범프·생성 파일처럼 검증이 의미 없는 커밋에 쓴다. 몰아치는 푸시에서는 중간 커밋에 붙이면 마지막 한 번만 검증된다.- 주의 1: 메시지 어디에든(본문 포함) 들어 있으면 적용된다. 이 규칙을 설명하려고 커밋 메시지에 키워드를 적으면 정작 그 커밋의 CI 가 건너뛰어진다 — 실제로 그렇게 실행이 만들어지지 않아 원인을 찾느라 시간을 썼다.
- 주의 2: 그 푸시에서 트리거되는 모든 워크플로가 건너뛰어진다(문서 게시 포함). 게시가 필요한 커밋에는 쓰지 않는다. PR 제목·본문에 키워드가 있으면 스쿼시 머지 커밋 제목에 그대로 들어가 main 검증까지 건너뛴다 — 실제로 이 가드를 소개하는 PR 이 그렇게 됐다.
- 시간 상한 —
timeout-minutes로 실행 시간을 묶어 멈춘 작업이 과금으로 번지지 않게 한다(게시 워크플로는 게이트가 무거워 20분).
main push 검증 끄기 (2026-09-15 정리)
위 가드를 적용한 뒤에도 push 횟수가 비용을 지배했다. 그래서 main push로는 원격 검증을 돌리지 않고 로컬 게이트로 대체했다 — ProcessCenter에서 먼저 적용하고, 나머지 저장소에 같은 방식으로 옮겼다.
| 저장소 | 정리 전 실측 | 정리 내용 |
|---|---|---|
| ProcessCenter | 최근 30일 1,397회 · push 1회에 ci.yml·verify.yml이 같은 단계를 중복 실행 | ci.yml 삭제, verify.yml은 PR(main)·수동 실행에서만 |
| PortalConsole | 최근 6일 push 66회(1회 약 85초) | verify.yml push 트리거 제거 + workflow_dispatch 추가 |
| UiPlatform | 최근 8일 push 76회(1회 약 32초) | 같음. 문서 게시(Publish platform docs, docs/** 한정)는 유지 |
| ModelGateway | 최근 2일 push 8회(1회 약 26초) | 같음 |
| FileStore · ExploreGateway · RagService | 워크플로 없음(원격 검증 0회) | PR(main)·수동 실행 전용 검증을 새로 추가 |
| portal-foundation | 태그 게시 59 · 수동 실행 29(최근 30일) · push 검증 없음 | PR(main)·수동 실행 전용 검증을 새로 추가. 태그 게시는 그대로 |
- 로컬 게이트:
pnpm verify(check → build) ·pnpm verify:full(+단위 테스트) — FileStore는verify:admin(admin-web타입 검사·빌드) ·verify:all(둘 다)이 더 있고, portal-foundation은verify(check) ·verify:full(test+check)이며 각 스크립트가 내부에서 패키지 빌드를 돌린다. RagService는python scripts/verify.py(--syntax-only지원)다. - FileStore의
admin-web(vite, 자체 lockfile)도 같은 PR 잡의 마지막 단계에서 검사·빌드한다. - RagService는 Dockerfile 이미지와 같은 Python 3.11에서 돌리고, DB가 필요한 테스트는 각 테스트의
skipUnless가드로 건너뛴다(tests/e2e_rag_isolation.py는 수집 대상이 아니다). - 규칙은 각 저장소
AGENTS.md의 "로컬 검증과 원격 검증 (CI 비용)" 절에 있다. - Coolify 배포는 push 웹훅이라 영향받지 않는다. 다만 CI 생략 키워드가 그 푸시의 모든 커밋에 있으면 Coolify도 배포를 건너뛴다 — 이 정리 커밋에는 그 키워드를 쓰지 않았다.
남은 선택지 (효과 큰 순)
- 푸시를 묶는다 — 근본 해법. 하루 70커밋을 5푸시로 묶으면 실행도 1/10이 된다. 자동화·에이전트가 파일 단위로 커밋하지 않고 작업 단위로 한 번에 푸시하게 한다.
- 자체 러너(self-hosted). 이미 운영 중인 서버에 러너를 붙이면(
runs-on: self-hosted) 분 소모가 0이 된다. 저장소가 모두 비공개라 공개 저장소 포크 위험도 없다. 대신 러너 패치·격리 관리 주체가 우리가 된다. - PR 경유.
main직접 푸시 대신 PR로 묶으면 실행이 PR 단위로 줄어든다(현재 PR 실행은 전체의 7%). - 저장소 공개. 공개 저장소는 Actions 분이 무료다.
portal-foundation은 이미 공개 npm 패키지를 내보내므로 선택 여지가 있으나, 소스 전체를 공개한다는 판단이 필요하다.
하지 않은 것과 이유
- 게시 워크플로의
cancel-in-progress: false유지 — 게시 도중 취소는 배포 상태를 어긋나게 만든다. - 게시 파이프라인에 검증 생략 스위치를 두지 않음 — 릴리스 게이트는 우회구를 만들지 않는다. 검증을 건너뛰고 싶은 커밋은
[skip ci]로 아예 실행을 만들지 않는 편이 낫다. - 검증을 여러 잡으로 병렬화하지 않음 — 벽시계 시간은 줄지만 과금은 잡마다 붙어 오히려 늘어난다. 한 잡에서 순서대로 돌리는 지금 구조가 비용에는 유리하다.