- 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 도구가 개발자의 시간을 소모시키는 구조라면, 문제는 개발자가 아니라 도구 자체임