8P by GN⁺ 20시간전 | ★ favorite | 댓글 2개
  • 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_dispatchgh workflow run 조합도 괜찮지만, 후자가 실행된 워크플로의 URL을 바로 주지 않는 건 불편함

    • nektos/act를 쓰면 로컬에서 Actions를 실행할 수 있음
      빠른 피드백을 얻는 데 꽤 성공적이었음
    • 나는 Actions가 도커 이미지를 빌드하고 테스트하도록 표준화했음
      문제가 생기면 GitHub Actions 환경과 거의 동일한 상태에서 디버깅할 수 있음
    • CI 단계를 로컬에서 실행할 수 없는 게 말이 안 된다고 생각함
      모든 CI 시스템의 기본 요구사항이어야 함
    • 로컬 실행이 가능한 CI 도구를 직접 만들까 고민한 적 있음
      결국 중요한 건 큐잉, 출력 분석, 그리고 빌드 히스토리 텔레메트리 같은 기능임
    • gh workflow run을 쓸 때 URL을 얻으려면 GitHub API로 워크플로 실행 목록을 다시 불러와야 했음
      동시에 여러 실행이 있으면 꼬일 수 있지만, 지금은 그럭저럭 잘 작동함
  • CI 설계 팁을 정리해봤음

    1. bash 대신 pwsh 같은 CI 친화적 스크립트 언어 사용
    2. 워크플로에는 로직을 넣지 말고 단순하게 유지
    3. 독립된 스크립트로 만들어 로컬에서 테스트 가능하게
    4. 디버깅을 쉽게 하기 위해 상태 출력과 버전 정보를 남길 것
    5. 디버깅 기능이 좋은 서드파티 러너 고려
    • (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의 잘못”이라고 단언함
  • 나는 모든 CI를 컨테이너 안에서 처리함
    CI 플랫폼은 단지 그 컨테이너를 실행할 뿐이라 로컬에서도 동일하게 돌릴 수 있음
    플랫폼들은 이런 방식을 싫어함, 벤더 락인이 깨지니까

    • SourceHut CI를 좋아함
      실패 시 SSH로 바로 접속해 디버깅할 수 있고, 브랜치 푸시 없이 매니페스트를 수정해 재실행 가능함
      다만 셀프호스팅이 필요함
    • GitLab CI에서도 비슷하게 전용 도커 이미지를 만들어 사용했음
      표준화는 쉬워지지만 이미지 유지보수라는 트레이드오프가 생김
    • 단점은 macOS 빌드를 컨테이너화하기 어렵다는 점임
    • GitHub의 웹훅은 매우 상세함
      사실상 락인은 없는데, 사람들은 CI/CD 카고컬트에 빠져 있음
    • “이 내용을 뉴스레터로 보내달라”는 반응도 있었음
  • 나는 GitHub Actions를 좋아함
    예전에 쓰던 Travis보다 낫고, OSS 프로젝트에는 무료 리소스로서 매우 유용함
    Nix를 도입한 이후로 환경 재현성이 높아져서 Actions와의 궁합이 훨씬 좋아졌음

    • 나도 Nix 접근법에 동의함
      flake로 만든 컨테이너를 Actions에서 그대로 실행할 수 있음
      예시 프로젝트
  • GitHub Actions는 단순히 bash나 python 스크립트를 호출하는 구조여야 한다고 생각함
    bash는 한계가 많고, Python은 더 유연하며 로컬 실행도 쉬움
    이 글처럼 uv를 자동 설치하고 의존성을 관리하는 방식이 이상적임
    bash보다 복잡하지만 Actions 환경에서는 성능 문제가 크지 않음

    • 실제로 YAML은 런타임 환경 정의용이고, 실행 로직은 외부 스크립트에 두는 게 맞음
  • Actions의 가장 큰 문제는 “워크플로를 조합하라”는 광고 방식
    디버깅이 거의 불가능하고, 캐시 설정이 까다로워 빌드가 느려짐
    이런 이유로 지속 VM에서 직접 실행하는 방식이 매력적으로 느껴짐

  • 나는 Depot의 창업자임
    GitHub Actions 러너를 더 빠르고 저렴하게 제공하는 서비스를 운영 중임
    많은 사람들이 느끼는 불만이 실제로 대다수의 의견임
    Actions 시스템은 구조적으로 비효율적이고, 매주 새로운 병목 현상을 발견함
    더 나은 방법이 있다고 확신하며, 이를 실험 중임
    자세한 이야기는 depot.dev에서 확인 가능함

  • 지난 주말에 gg watch action이라는 도구를 만들었음
    현재 브랜치의 최신 혹은 실행 중인 액션을 찾아주는 툴임
    깃허브 링크

    • “이건 gh CLI가 되었어야 할 기능”이라며 반응이 좋았음
      다만 gg tui 명령에서 리포지토리가 안 보이는 버그가 있었음
  • act 같은 도구가 도움이 될까 궁금했음
    nektos/act
    아키텍처 차이로 로컬과 온라인 실행이 다를 수는 있지만 여전히 유용해 보임

    • 예전에 봤을 땐 완전한 대체는 아니었음
      80% 정도의 호환성이 있었음
    • 완벽히 동일하진 않지만, 빠른 피드백 루프를 만드는 데 매우 유용함
    • 사실 가장 필요한 건 실패 후 SSH로 접근해 디버깅할 수 있는 기능임
      SourceHut은 이걸 지원해서 정말 편리함