GitHub Actions가 엔지니어링 팀을 서서히 죽이고 있다
(iankduncan.com)- Repo에 기본으로 포함된 CI라는 이유로 널리 쓰이지만, 구조적 비효율성과 불안정한 사용자 경험이 개발자 생산성을 저하시킴
- 로그 뷰어의 느린 로딩과 브라우저 충돌, 복잡한 YAML 구문과 표현식 오류가 반복적인 디버깅을 초래
- 컴퓨트 자원을 소유하지 못하는 구조로 인해 성능·확장성·환경 제어 측면에서 한계를 드러냄
- 많은 문제를 우회하려다 복잡한 YAML 또는 거대한 Bash 스크립트로 CI 자체를 다시 만드는 상황이 반복됨
- 이에 비해 Buildkite는 단순한 YAML 구조, 자가 호스팅 가능한 에이전트, 실용적인 로그 경험을 통해 장기적으로 유지 가능한 CI 대안
GitHub Actions의 문제점
-
GitHub Actions 로그 뷰어는 비효율적임: 단순한 오류 확인에도 여러 단계의 클릭과 페이지 로딩이 필요
- 빌드 실패 시 체크 요약 페이지 → 워크플로 실행 페이지 → 잡 페이지 → 접힌 스텝 클릭까지 3~4단계의 페이지 전환이 필요하며, 각 단계마다 별도의 로딩 발생
- 대용량 빌드 로그에서 브라우저를 반복적으로 충돌시키며, 검색 기능 사용 시 Chrome이 멈추는 현상이 재현 가능한 수준
- 긴 로그에서는 스크롤 자체가 작동하지 않아 결국 원시 로그 아티팩트를 다운로드해 텍스트 에디터로 열어야 하는 상황 발생
- 뒤로 가기 버튼이 원래의 PR 페이지가 아닌 예측 불가능한 GitHub Actions UI 페이지로 이동시키며, 브라우저 히스토리가 Actions URL로 가득 차게 됨
-
디버깅 과정의 비생산성
- 환경 변수 확인을 위해
run: env스텝을 추가하고 다시 푸시하면 20분의 피드백 루프가 발생하며, 한 줄 변경에 이 과정을 십여 차례 반복하게 됨 - 20분 단위의 피드백 루프가 반복되어 하루 업무가 CI 대기 시간으로 소모됨
- 환경 변수 확인을 위해
-
YAML의 구조적 한계
- GitHub Actions YAML은 자체 표현식 언어, 컨텍스트 객체 모델, 문자열 보간 규칙이 결합된 특수한 형태
-
${{ }}표현식에서 따옴표 하나를 잘못 쓰면 러너가 스핀업되기까지 4분을 기다린 뒤에야 문자열이 손실된 것을 발견 - 표현식 문법은 설정으로 쓰기엔 너무 복잡하고, 정식 프로그래밍 언어로 쓰기엔 너무 제한적인 경계 영역(liminal space) 에 존재
- 문법을 문서가 아닌 실패 경험을 통해 학습하게 되는 구조
-
Marketplace의 보안 위험
-
uses:구문으로 외부 액션을 불러올 때, 리포지토리, 시크릿, 빌드 환경에 대한 접근 권한을 검증되지 않은 서드파티에 부여 - SHA 고정(pinning)이 가능하지만 실제로 하는 사람은 거의 없으며, 고정하더라도 읽어보지 않은 불투명한 코드를
GITHUB_TOKEN접근 권한과 함께 실행하는 구조 - Marketplace는 다양한 품질의 커뮤니티 유지 액션이 혼재하며, 대부분 셸 스크립트와 Dockerfile로 구성
- 의존성 관리가 불투명하며, 안전하지 않은 코드 실행 가능성이 있음
-
-
컴퓨트 환경의 제약
- GitHub Actions의 기본 러너는 Microsoft 소유의 공유 러너로, 느리고 리소스가 제한적이며 의미 있는 커스터마이징 불가
- 더 큰 러너 비용은 재무팀에서 "얘기 좀 하자"라는 미팅 요청이 올 수준이며, 그래도 환경 제어는 불가능
- Namespace, Blacksmith, Actuated, Runs-on, BuildJet 등 GitHub Actions 러너의 느린 속도만을 해결하는 데 특화된 스타트업이 최소 6개 이상 존재하며, 이 자체가 기본 컴퓨팅 환경의 부족함을 증명
- Self-hosted runner를 설정하면 컴퓨팅 문제는 해결되지만, YAML 표현식, 권한 모델, Marketplace, 로그 뷰어 등 나머지 문제는 그대로 존재
세부적이지만 누적되는 문제들
-
actions/cache: 캐시 키가 혼란스럽고, 캐시 미스는 조용히 발생하며, 캐시 제거(eviction)는 불투명하여 캐싱 디버깅에 절약하는 시간보다 더 많은 시간 소비 - 재사용 가능 워크플로: 일정 깊이 이상 중첩 불가, 호출 워크플로의 컨텍스트에 깔끔하게 접근 불가, 격리된 테스트 불가능
-
GITHUB_TOKEN권한 모델:permissions: write-all은 너무 광범위하고, 세분화된 권한은 리포지토리·워크플로·잡 레벨 설정 간 상호작용이 미로처럼 복잡 - 동시성(concurrency) 제어: 같은 브랜치에서 진행 중인 실행 취소는 한 줄로 가능하지만, 그 이상의 세밀한 제어는 지원하지 않음
-
시크릿의
if조건 사용 불가:if: secrets.DEPLOY_KEY != ''같은 조건부 실행이 불가능하며, 보안상 합리적이지만 포크와 메인 리포 양쪽에서 작동하는 워크플로 작성 시 우회 방법이 필요
"그냥 Bash 스크립트를 쓰자"의 함정
- CI YAML에 지친 엔지니어가 모든 것을
run:bash 스크립트로 대체하려는 유혹이 존재하지만, 시간이 지나면서 조건문, 함수, 인자 파싱, 병렬 처리가 추가됨 - 3개월 후에는 800줄의 bash가
wait와 PID 파일로 잡 병렬화를 재구현하고, 자체 재시도 로직과 출력 파싱 로직을 갖추게 됨 - 결국 CI 시스템을 탈출한 것이 아니라 테스트 프레임워크도 없고 아무도 따라갈 수 없는 더 나쁜 CI 시스템을 bash로 직접 만든 것
- Bash는 접착제(glue) 용도로는 적합하지만, 빌드 시스템이나 테스트 하네스로 사용하면 가드레일이 있는 곳에서 없는 곳으로 복잡성을 이동시키는 결과
Buildkite의 대안적 접근
-
안정적인 로그 뷰어
- Buildkite의 로그 뷰어는 브라우저를 충돌시키지 않고 정상적으로 로그를 표시하며, ANSI 컬러와 테스트 프레임워크의 포맷팅이 그대로 렌더링
- Annotation 기능으로 빌드 스텝이 테스트 실패 요약, 커버리지 리포트, 배포 링크 등을 빌드 페이지에 직접 Markdown으로 출력 가능
- 에이전트가 자체 인프라에서 실행되므로 SSH로 빌드 머신에 접속하여 직접 디버깅 가능
-
단순한 YAML 구조
- Buildkite의 YAML은 파이프라인을 기술하는 순수 데이터 구조로, 스텝, 커맨드, 플러그인만 선언
- 실제 로직이 필요하면 로컬에서 실행 가능한 실제 프로그래밍 언어로 스크립트를 작성
- "오케스트레이션은 설정에, 로직은 코드에"라는 경계를 명확히 유지하며, GitHub Actions가 흐리게 만드는 바로 그 경계
-
컴퓨트 환경의 완전한 제어권
- Buildkite 에이전트는 단일 바이너리로 자체 클라우드, 온프레미스, 커스텀 하드웨어 어디서든 실행 가능
- 인스턴스 타입, 캐싱, 로컬 스토리지, 네트워크를 완전히 제어 가능하며, NVMe 드라이브와 20GB Docker 레이어 캐시를 가진 대형 EC2 인스턴스부터 Raspberry Pi까지 지원
- "Buildkite인데 더 빠른" 서드파티 산업이 존재하지 않으며, 그냥 더 큰 머신을 돌리면 됨
- 소규모 오픈소스 라이브러리를 유지하는 개인 메인테이너에게는 GitHub Actions의 퍼블릭 리포 무료 티어가 여전히 가치 있음
- 이 글의 주요 대상은 프로덕션 시스템을 운영하는 팀으로, CI 시간이 주당 엔지니어링 시간 손실로 측정되고 45분짜리 빌드가 컴퓨팅 비용과 인건비 양면에서 비용을 발생시키는 환경
- 그런 환경에서는 Buildkite 에이전트 운영의 오버헤드가 빠르게 비용을 회수
-
동적 파이프라인 지원
- Buildkite에서 파이프라인 스텝은 데이터이며, 스크립트가 런타임에 동적으로 추가 스텝을 생성(emit)하고 업로드 가능
- 모노레포에서 변경된 파일을 기반으로 필요한 빌드·테스트 스텝만 정확히 생성하는 방식으로, 하드코딩된 매트릭스나
if: contains(...)스파게티 불필요 - GitHub Actions의
matrix,if조건, 재사용 워크플로는 이를 근사(approximate)하려 하지만, 표현력이 부족한 선언적 언어로 루브 골드버그 머신을 만들게 됨
-
플러그인 구조의 단순성
- 구조적으로는 GitHub Actions Marketplace와 유사하게 서드파티 리포에서 코드를 가져오는 방식
- 차이점은 Buildkite 플러그인이 대체로 Docker 이미지가 아닌 얇은 셸 훅(thin shell hook) 이어서 표면적이 작고, 몇 분 안에 전체를 읽을 수 있음
- 자체 인프라에서 실행되므로 폭발 반경(blast radius) 을 사용자가 제어 가능
-
사용자 경험 중심의 세부 기능
- Buildkite는 커스텀 이모지(
:parrot:,:docker:등)를 파이프라인 스텝 옆에 표시할 수 있으며, 사소해 보이지만 제품 사용 경험에 대한 세심한 배려를 보여주는 기능 - GitHub Actions는 "이것이 즐거운가?"라는 질문을 한 번도 하지 않은 듯한 위원회 설계 산물
- Buildkite는 커스텀 이모지(
결론: CI 시스템 선택의 기준
- GitHub Actions는 기본 탑재(default) 라는 이점으로 시장을 장악했으며, 퍼블릭 리포 무료, 이미 모두가 사용하는 플랫폼에 내장, "충분히 괜찮은(Good Enough)" 수준
- CI의 Internet Explorer와 같은 것이며, 전환 비용이 현실적이고 시간은 유한하기 때문에 사용이 지속됨
- Buildkite은 지속적 사용성과 개발자 경험 측면에서 우수함
- 단순한 오픈소스 프로젝트에는 GitHub Actions가 충분하지만, 대규모 프로덕션 환경에서는 Buildkite이 더 적합
- CI 시스템 역사에서 시장 점유율을 가져가는 것은 가장 우수한 시스템이 아니라 가장 쉽게 시작할 수 있는 시스템
- GitHub Actions는 시작하기 가장 쉬운 CI이고, Buildkite는 계속 사용하기 가장 좋은 CI이며, 장기적으로는 후자가 중요
- CI 도구가 개발자의 시간을 소모시키는 구조라면, 문제는 개발자가 아니라 도구 자체임
Hacker News 의견들
-
나는 여러 CI 시스템을 써봤음. CircleCI와 GitHub Actions를 많이 썼지만 글쓴이와는 결론이 다름
예전엔 Jenkins가 Java 전용, Travis가 Rails 전용이었는데 이런 전문화된 CI는 결국 막다른 길이었음. 지금은 CI가 단순히 워크플로 오케스트레이터로 진화했음
CircleCI 2에서 GitHub Actions로 옮긴 이유도 CircleCI가 이 전환을 제대로 못했기 때문임. GHA는 표현력이 충분했음
로그 브라우저나 YAML 문법 같은 건 사소한 문제임. 중요한 건 컴퓨팅 자원 소유와 동적 파이프라인인데, 전자는 모든 CI에서 가능하고 후자는 Buildkite의 장점임
내 결론은 Actions가 실질적으로 꽤 괜찮고, 새 회사를 시작한다면 Buildkite를, 오픈소스엔 Actions를 쓰겠음- 나는 반대 의견임. 게임 개발에서는 빌드 시스템이 핵심인데, 일반적인 CI 접근법은 너무 느림
빌드 그래프를 이해하지 못하면 증분 빌드 상태를 유지해야 하고, 그게 임시 버그를 유발함. 그래서 UnrealEngine Horde나 UBA처럼 빌드 구조를 깊이 이해하는 시스템이 필요함
일반화된 CI를 쓰면 빌드에 하루 넘게 걸릴 수도 있음 - Actions는 이벤트 디스패처이자 오케스트레이터, 실행 엔진, 캐시 시스템, 마켓플레이스, 시크릿 매니저 등 다기능 플랫폼임
나는 GHA의 좋은 부분만 쓰는 편임. 예를 들어 GitHub은 이벤트 디스패처로는 훌륭하지만 워크플로 오케스트레이터로는 별로라서, 그 부분은 다른 시스템에 위임함 - 로그 브라우저를 대수롭지 않게 보는 건 동의 못함. ANSI 컬러와 포맷팅이 유지된 로그를 읽는 경험은 중요함
하루에도 수십 번 보는 로그가 불편하면 생산성이 떨어짐. 원시 로그에서 이스케이프 코드를 무시하며 읽는 건 정말 고통스러움 - “컴퓨팅 자원 소유”가 중요하다고 했지만, GitHub은 자체 하드웨어에서 CI를 돌려도 요금을 부과함
- 나는 Buildkite 초기 고객이었는데, 인체공학적이고 제어권이 높은 시스템이라 정말 만족스러웠음
- 나는 반대 의견임. 게임 개발에서는 빌드 시스템이 핵심인데, 일반적인 CI 접근법은 너무 느림
-
나는 단순함을 유지함. 모든 오케스트레이션을 deploy.sh 스크립트에 넣고, 로컬 Mac이나 AWS CodeBuild에서 실행함
YAML은 단순히bash deploy.sh한 줄임. Docker 컨테이너만 있으면 Azure, GitHub Actions 등 어디서든 동일하게 동작함 -
모든 CI 환경의 핵심 전략은 로컬과 동일한 빌드 시스템을 갖추는 것임
나는 항상 Makefile로 시작함. Docker, CI 빌드, 린팅 등 모든 걸 Makefile이 구동함. 프로젝트가 커지면 다른 도구로 넘어가기도 하지만, 기본은 하나의 트리거 도구임- 이런 생각에서 mkincl을 만들었음. Makefile을 모듈화하고 재사용할 수 있게 해줌. 몇 년째 회사에서 쓰는데 직관적이고 유연함
- CI 제공자의 플러그인에 의존하지 않고, 상위 언어로 전환하는 게 좋음
나는 모바일에서 Fastlane을 많이 쓰는데, 보일러플레이트를 줄이고 구조를 제공함. 결국 Ruby라서 필요하면 탈출도 가능함 - Make는 정말 기이한 시스템임. 기본 규칙 중 하나가 버전 관리 시스템에서 파일을 추출하는 것임
(GNU Make 문서 링크)
결국 “그냥 Bash 스크립트를 써라”는 말인데, 불필요한 복잡함이 추가된 셈임 - 이 접근이 이상적이지만 중대형 프로젝트에서는 비현실적임
현재 회사에서도 로컬에서 전체 파이프라인을 돌릴 수 없게 되면서, MR 하나 테스트하는 데 빌드 10번씩 돌리는 거대한 CI 인프라가 생김
-
나는 이 글이 Nix/Buildkite 광고처럼 느껴졌음
CI는 단순히 스크립트나 빌드 타깃을 실행하는 수준이면 충분함. CI는 환경과 설정만 제공하고, 로직은 코드에 있어야 함
이렇게 하면 CI 독립성이 생겨서 시스템 간 이동이 쉬워짐- “CI를 단순하게 유지하라”는 건 현실적으로 불가능함. 규모가 커지면 조건부 트리거, 브랜치 전략, 권한, 로드 밸런싱 등 복잡성이 필연적으로 생김
GitLab CI는 이런 복잡성을 잘 다루는 편임. 템플릿과 잡 구성 기능이 훌륭하지만, 디버깅은 어렵고 조건 로직이 종종 예기치 않게 실패함 - 이건 광고가 아님. Buildkite 팀 입장에서도 기분 좋은 서프라이즈였음
- 나도 동의함. 예전에 CI 시스템을 옮길 때, 모든 로직을
.sh파일에 넣어둔 팀은 이전이 매우 쉬웠음
반면 UI로 세분화한 팀은 고생했음 - 최소한 “이건 AI가 쓴 글이네” 같은 반응이 없어서 다행임
- “CI를 단순하게 유지하라”는 건 현실적으로 불가능함. 규모가 커지면 조건부 트리거, 브랜치 전략, 권한, 로드 밸런싱 등 복잡성이 필연적으로 생김
-
문제는 CI/CD 자체가 아니라 설정 파일로 프로그래밍하는 문화임
git commit -m "try fix"후 10분 기다리는 루프가 너무 흔함. 로컬 재현 가능한 CI 환경이 여전히 부족함- 이건 도구 문제가 아니라 정책 실패의 결과임. 빌드와 릴리스, 디버그 빌드를 명확히 구분해야 함
환경 격리를 정책으로 설정하면 어떤 도구를 써도 문제없음. 결국 도구와 방법론의 조화가 핵심임 - 맞음, 이 부분이 글의 핵심 80%를 차지한다고 봄
-
act같은 도구가 로컬에서 CI를 재현하는 데 큰 도움이 됨
- 이건 도구 문제가 아니라 정책 실패의 결과임. 빌드와 릴리스, 디버그 빌드를 명확히 구분해야 함
-
“엔지니어 팀을 죽인다”는 제목은 과장임. GitHub Actions는 충분히 괜찮음
Bitbucket이나 GitLab보다 선호함- 나도 처음엔 “Microsoft가 사람을 죽인다고?” 하고 클릭했는데, 실망스러웠음
- 나도 GitLab 얘긴 줄 알았음. 특히 느린 피드백 루프 문제는 GitLab에도 똑같이 해당됨
-
최근 GitHub의 신뢰성 저하가 심함
actions/checkout이 이유 없이 실패하거나, 릴리스 잡이 두 번 실행되거나, 40분 동안 대기만 하는 경우도 있음
몇 년째 써왔지만 기본적인 안정성이 떨어지고 있음. Buildkite를 놓친 게 아쉬움- 맞음, GHA의 cron 작업은 불안정함. 문서에서도 신뢰할 수 없다고 언급되어 있음
-
GitHub Actions는 내가 써본 최악의 CI 도구 중 하나임 (Jenkins와 동급)
반면 Buildkite는 최고임. 동적 파이프라인 덕분에 테스트 실패 시 자동으로 재시도 스텝을 생성하거나, 코드 변경에 따라 병렬 테스트를 조정할 수 있음
각 CI 잡마다 다른 머신 구성을 쓸 수 있는 것도 큰 장점임. 강력히 추천함- Jenkins는 문제도 많았지만, Groovy 기반 파이프라인 정의는 좋았음. YAML보다 훨씬 낫다고 생각함
- Jenkins는 전투 검증된 안정적 시스템임. 수천 개 작업도 문제없이 처리하고, 설치 즉시 동작함. 지난 25년간 최고의 오픈소스 중 하나라고 생각함
-
“단순한 스크립트 기반 CI”를 주장하는 사람들은 대규모 실제 프로젝트 경험이 없을 것 같음