나는 GitHub Actions가 정말 싫어요
(xlii.space)- GitHub Actions의 느린 피드백 루프와 복잡한 디버깅 과정에 대한 개발자의 좌절 경험 공유
-
tmplr 프로젝트에서
build.rs를 통해 CUE로 문서를 생성했으나, CI 빌드가 실패하면서 문제 시작 - 4개 플랫폼 중 Linux ARM만 빌드 실패, 원인은 cross build 시 x86_64 바이너리가 arm64 러너에서 숨겨지는 GitHub Actions의 동작 방식
- 단일 변경 사항 테스트에 2~3분이 소요되는 비효율적인 피드백 루프 반복
- 해결책으로
build.rs를 삭제하고 GNU Makefile로 전환, CI 로직을 직접 제어하는 방식으로 문제 해결
문제 발생 배경
- tmplr은 사람이 읽고 작성할 수 있는 템플릿 파일을 사용하는 파일/프로젝트 스캐폴딩 도구
-
build.rs에서 CUE를 사용해README.md,CHANGELOG.md, 버전/도움말 파일을 생성하여 일관성 보장 - 작업 자체는 약 1.5시간 만에 완료되었고, 관련 글도 작성완료
- 로컬에서는 정상 작동했으나, GitHub Actions의 CI 환경에서 CUE 바이너리 미설치로 빌드 실패
빌드 실패 원인
- 4개 플랫폼(Linux ARM, macOS ARM, Linux x86_64, macOS x86_64) 중 Linux ARM만 “command not found” 오류 발생
- 원인: matrix cross build가 강하게 격리되어, CUE는 x86_64 Linux 호스트와 macOS ARM 호스트에만 설치됨
- macOS는 x86_64 바이너리 실행에 문제없음
- Linux x86_64도 x86_64 바이너리 실행에 문제없음
- 그러나 GitHub Actions는 arm64 러너에서 x86_64 바이너리를 숨겨 실행 불가 상태로 만듦
비효율적인 피드백 루프
- 문제 해결을 위해 반복한 과정:
1. 가능한 수정 방법 검색
2.ci.yml변경
3. 커밋 및 푸시 (jj squash --ignore-immutable && jj git push)
4. "Actions" 탭 열기
5. 최신 실행 열기
6. Linux ARM 실행 열기
7. 몇 초 대기
8. 좌절
9. 반복 - 단일 변경 사항당 2~3분 소요
- 이상적으로는 GitHub가 모든 기능을 갖춘 로컬 러너를 제공하거나, 또는 푸시 후 빠르게 진행 상황을 확인할 수 있는 기능을 제공 가능
- "scratch commit" 같은 기능: Git 히스토리와 Action 실행 기록을 오염시키지 않고 다양한 실행을 테스트할 수 있는 방법
- 그러나 현재 그런 기능은 존재하지 않음
해결 방법
- 30분간 루프를 반복한 후 중단
- 인터넷에서 알려진 방법 적용: "GitHub Actions가 로직을 관리하게 하지 말고, 스크립트를 직접 제어하고 Actions는 그 스크립트를 호출만 하게 할 것"
-
build.rs삭제 (아쉬움이 있었으나 희생 필요) - 모든 생성 작업을 GNU Makefile로 이동
- 생성된 파일들을 저장소에 커밋하고, CI 변경 사항 되돌림
- 문제 해결 완료
결론
- GitHub Actions는 일부 좋은 것들을 가질 수 없게 만드는 원인임
- 러너 디버깅이나 빌드 프로세스 최적화에 많은 시간 손실
- 그러나 다른 방법으로는 얻기 어려운 macOS 빌드 같은 이점도 존재함
- 물론, GitHub Actions보다 설정하기 쉬운 다른 시스템은 알려지지 않음
We are all doomed to GitHub Actions. …but at least I dodged the bullet early.
깃합 액션은 환경 셋업(OS, 빌드툴체인, …)과 스크립트(셸, 파이썬, bat, ps1…) 실행만 해야함. 깃헙이 다운되도 환경만 갖춰지면 어디서나 빌드할 수 있어야 함. 요즘 깃헙 액션 워크플로 보고 있으면 이런거 까지 굳이 찾아서 써야하나 싶음. 먼 옛날(?) ansible이 그러다 망했음.
Hacker News 의견들
-
GitHub Actions의 핵심 문제는 피드백 루프가 너무 느리다는 점임
단순한 실패를 확인하려고 푸시 후 기다려야 하는 건 정말 답답함
로컬에서 실행 가능한 스크립트로 CI 작업을 분리하고, Actions 기능은 점진적 향상으로만 쓰는 게 좋다고 생각함
workflow_dispatch와gh workflow run조합도 괜찮지만, 후자가 실행된 워크플로의 URL을 바로 주지 않는 건 불편함-
nektos/act를 쓰면 로컬에서 Actions를 실행할 수 있음
빠른 피드백을 얻는 데 꽤 성공적이었음 - 나는 Actions가 도커 이미지를 빌드하고 테스트하도록 표준화했음
문제가 생기면 GitHub Actions 환경과 거의 동일한 상태에서 디버깅할 수 있음 - CI 단계를 로컬에서 실행할 수 없는 게 말이 안 된다고 생각함
모든 CI 시스템의 기본 요구사항이어야 함 - 로컬 실행이 가능한 CI 도구를 직접 만들까 고민한 적 있음
결국 중요한 건 큐잉, 출력 분석, 그리고 빌드 히스토리 텔레메트리 같은 기능임 -
gh workflow run을 쓸 때 URL을 얻으려면 GitHub API로 워크플로 실행 목록을 다시 불러와야 했음
동시에 여러 실행이 있으면 꼬일 수 있지만, 지금은 그럭저럭 잘 작동함
-
nektos/act를 쓰면 로컬에서 Actions를 실행할 수 있음
-
CI 설계 팁을 정리해봤음
- bash 대신 pwsh 같은 CI 친화적 스크립트 언어 사용
- 워크플로에는 로직을 넣지 말고 단순하게 유지
- 독립된 스크립트로 만들어 로컬에서 테스트 가능하게
- 디버깅을 쉽게 하기 위해 상태 출력과 버전 정보를 남길 것
- 디버깅 기능이 좋은 서드파티 러너 고려
- (1)은 동의하지 않음. 빌드나 테스트 과정이 복잡해지는 건 냄새라고 봄
단순한 셸로 충분해야 함 - (1)에는 동의하지 않지만 (2)는 맞다고 생각함
Makefile에 CI 타깃을 정의하고,make ci-test처럼 간단히 호출하는 게 좋음 - 예전에 Jenkins 플러그인 지옥을 겪은 적 있음
그 이후로는 모든 CI를make build같은 단순한 래퍼로 관리함 - 단일 스크립트로 모든 빌드를 감싸면 CI 시스템이 세부 단계 인사이트를 잃음
BeginStep("Step Name")같은 마커로 구분할 수 있으면 좋겠음 - 결국 이런 구조라면, 스크립트를 직접 실행하는 새로운 CI 도구가 필요하지 않을까 생각함
-
문제는 GitHub Actions 자체보다, 그 위에 엉망으로 얹은 자동화임
로직은 Python 같은 언어로 스크립트화해서 로컬에서도 실행 가능하게 해야 함- 많은 사람들이 불만인 건 실패한 VM에 SSH로 직접 접근해 디버깅할 수 없다는 점임
매번 워크플로를 수정하고 푸시하고 기다려야 함 - “그런 스크립트화 작업은 돈 받고 할 수 있는 몇 주짜리 일거리”라며 농담하는 사람도 있음
- 배포, 릴리스, 캐싱 같은 CI 전용 기능은 로컬에서 완전히 재현하기 어려움
- 이 접근은 마치 systemd가 init.d 스크립트를 호출하던 시절의 느낌이지만, 나쁘지 않음
- 어떤 사람은 “이건 100% GitHub Actions의 잘못”이라고 단언함
- 많은 사람들이 불만인 건 실패한 VM에 SSH로 직접 접근해 디버깅할 수 없다는 점임
-
나는 모든 CI를 컨테이너 안에서 처리함
CI 플랫폼은 단지 그 컨테이너를 실행할 뿐이라 로컬에서도 동일하게 돌릴 수 있음
플랫폼들은 이런 방식을 싫어함, 벤더 락인이 깨지니까-
SourceHut CI를 좋아함
실패 시 SSH로 바로 접속해 디버깅할 수 있고, 브랜치 푸시 없이 매니페스트를 수정해 재실행 가능함
다만 셀프호스팅이 필요함 - GitLab CI에서도 비슷하게 전용 도커 이미지를 만들어 사용했음
표준화는 쉬워지지만 이미지 유지보수라는 트레이드오프가 생김 - 단점은 macOS 빌드를 컨테이너화하기 어렵다는 점임
- GitHub의 웹훅은 매우 상세함
사실상 락인은 없는데, 사람들은 CI/CD 카고컬트에 빠져 있음 - “이 내용을 뉴스레터로 보내달라”는 반응도 있었음
-
SourceHut CI를 좋아함
-
나는 GitHub Actions를 좋아함
예전에 쓰던 Travis보다 낫고, OSS 프로젝트에는 무료 리소스로서 매우 유용함
Nix를 도입한 이후로 환경 재현성이 높아져서 Actions와의 궁합이 훨씬 좋아졌음- 나도 Nix 접근법에 동의함
flake로 만든 컨테이너를 Actions에서 그대로 실행할 수 있음
예시 프로젝트
- 나도 Nix 접근법에 동의함
-
GitHub Actions는 단순히 bash나 python 스크립트를 호출하는 구조여야 한다고 생각함
bash는 한계가 많고, Python은 더 유연하며 로컬 실행도 쉬움
이 글처럼 uv를 자동 설치하고 의존성을 관리하는 방식이 이상적임
bash보다 복잡하지만 Actions 환경에서는 성능 문제가 크지 않음- 실제로 YAML은 런타임 환경 정의용이고, 실행 로직은 외부 스크립트에 두는 게 맞음
-
Actions의 가장 큰 문제는 “워크플로를 조합하라”는 광고 방식임
디버깅이 거의 불가능하고, 캐시 설정이 까다로워 빌드가 느려짐
이런 이유로 지속 VM에서 직접 실행하는 방식이 매력적으로 느껴짐 -
나는 Depot의 창업자임
GitHub Actions 러너를 더 빠르고 저렴하게 제공하는 서비스를 운영 중임
많은 사람들이 느끼는 불만이 실제로 대다수의 의견임
Actions 시스템은 구조적으로 비효율적이고, 매주 새로운 병목 현상을 발견함
더 나은 방법이 있다고 확신하며, 이를 실험 중임
자세한 이야기는 depot.dev에서 확인 가능함 -
지난 주말에
gg watch action이라는 도구를 만들었음
현재 브랜치의 최신 혹은 실행 중인 액션을 찾아주는 툴임
깃허브 링크- “이건
ghCLI가 되었어야 할 기능”이라며 반응이 좋았음
다만gg tui명령에서 리포지토리가 안 보이는 버그가 있었음
- “이건
-
act같은 도구가 도움이 될까 궁금했음
nektos/act
아키텍처 차이로 로컬과 온라인 실행이 다를 수는 있지만 여전히 유용해 보임- 예전에 봤을 땐 완전한 대체는 아니었음
약 80% 정도의 호환성이 있었음 - 완벽히 동일하진 않지만, 빠른 피드백 루프를 만드는 데 매우 유용함
- 사실 가장 필요한 건 실패 후 SSH로 접근해 디버깅할 수 있는 기능임
SourceHut은 이걸 지원해서 정말 편리함
- 예전에 봤을 땐 완전한 대체는 아니었음