시나리오와 병목: 국경 간 풀, Git gem, lock 일관성
bundle install은 인덱스 메타데이터·.gem 다운로드·Git 클론이 한 파이프라인으로 겹칩니다. 원격 Mac은 데이터센터 위치·회선·프록시가 로컬 개발기와 달라 동일 Gemfile이라도 CI만 느리거나 실패하는 경우가 흔합니다.
- 병렬 폭주:
BUNDLE_JOBS를 과도하게 올리면 NAT·방화벽·rubygems/GitHub 쪽 동시 연결 한도에 걸려 오히려 타임아웃이 늘 수 있습니다. - Git 소스 gem:
github:·git:의존은 깊은 히스토리·서브모듈·LFS까지 따라와 IO가 커집니다. 토큰 범위·URL 스킴(SSH vs HTTPS) 불일치 시 401/403만 반복됩니다. - lock 드리프트: 미러 전환·플랫폼 블록 추가·Bundler 메이저 업 후
Gemfile.lock미갱신이면--frozen단계에서 즉시 실패하거나, 반대로 조용히 다른 revision이 깔리는 사고로 이어집니다.
로그에서 Fetching gem metadata 대기인지, 특정 Git 리비전에서 멈추는지 먼저 나눈 뒤 아래 표의 파라미터를 고릅니다.
병렬과 연결 풀 파라미터: BUNDLE_JOBS·BUNDLE_RETRY·재시도 상한
Apple Silicon 원격 Mac에서 코어는 넉넉해도 외부 네트워크가 병목인 경우가 많습니다. 아래는 실행 가능한 기본치입니다(팀 회선에 맞게 조정).
| 항목 | 권장 시작값 | 의사결정 기준 |
|---|---|---|
BUNDLE_JOBS |
4 (M4 10코어 기준 4~6) | p95가 안정이면 +2씩. 타임아웃·429 증가 시 -2. |
BUNDLE_RETRY / bundle install --retry |
3 | 네트워크 플레이크 대응. 401·403에는 효과 없음→토큰·URL부터 수정. |
| 셸 백오프 (실패 시) | 2s → 4s → 8s, 최대 3회 | CI retry:와 중복 시 총 시도 횟수가 과다해지지 않게 설계. |
BUNDLE_WITHOUT |
development:test (배포 잡) |
풀 볼륨·보안 표면 감소. lock은 동일 저장소 기준으로 유지. |
BUNDLE_AUTO_INSTALL처럼 암묵적 동작은 러너 이미지마다 달라질 수 있으므로, CI 스크립트에 명시적 bundle install을 두는 편이 재현성에 유리합니다.
국경 간 미러와 Git 소스 전략
rubygems.org 직접 풀이 느리거나 불안정하면 조직 승인 미러를 둡니다. Git 소스는 HTTPS + 토큰 또는 git config url.insteadOf로 자격 증명을 일원화하세요.
| 소스 유형 | 설정 수단 | 복붙 예시 (URL은 조직 정책으로 교체) |
|---|---|---|
| rubygems 미러 | bundle config |
bundle config set mirror.https://rubygems.org https://<조직-미러>/rubygems/ |
| GitHub HTTPS gem | 호스트별 env | export BUNDLE_GITHUB__COM=x-access-token:${GITHUB_TOKEN} |
| 범용 Git 호스트 | git config (글로벌/CI) |
git config --global url."https://oauth2:${TOKEN}@gitlab.example.com/".insteadOf "https://gitlab.example.com/" |
| 사설 gem 서버 | bundle config + Gemfile source |
bundle config set --local rubygems.pkg.github.com USERNAME:${TOKEN} (GitHub Packages Rubygems) |
미러와 원본을 섞으면 Gemfile.lock의 remote: 블록이 팀원마다 달라질 수 있습니다. 한 미러 정책을 정하고 lock을 저장소에 고정하세요.
lockfile 드리프트, vendor/cache, CI 캐시 키
bundle package --all로 vendor/cache에 .gem을 넣으면 CI는 외부 풀 없이 bundle install --local로 닫을 수 있습니다(저장소 용량·갱신 프로세스는 비용). 반면 캐시만 쓰는 경우에는 키 설계가 성패를 가릅니다.
| 전략 | 캐시/아티팩트 키 구성요소 | 비고 |
|---|---|---|
| vendor/bundle | Gemfile.lock 해시 + Ruby 버전 + Bundler 버전 |
플랫폼별 젬이 있으면 bundle lock --add-platform 반영 여부를 키에 포함. |
| vendor/cache | Gemfile.lock + vendor/cache 디렉터리 지문 |
패키징 잡과 빌드 잡 키를 분리하면 무효화 범위를 줄일 수 있음. |
| 미러 전환 | 기존 키에 미러 호스트 문자열 추가 | 오래된 메타·체크섬 불일치 방지용 의도적 캐시 미스. |
bundle install --frozen또는--deployment로 lock 위반 시 즉시 실패.- 머지 전 로컬/동형 Docker에서 동일 명령으로 lock 재생성 필요 시 PR에 lock diff 포함.
bundle check로 경로·플랫폼 누락을 스모크.- Bundler 메이저 업 시
Gemfile.lock상단BUNDLED WITH와 CI 이미지 버전 일치.
FAQ
Q. SSH git@github.com: gem이 CI에서만 실패합니다.
A. 에이전트 포워딩 없는 러너에서는 HTTPS + 토큰이 단순합니다. BUNDLE_GITHUB__COM 또는 insteadOf로 팀 전체 URL 정책을 통일하세요.
Q. 캐시를 지웠는데도 특정 Git revision에서 멈춥니다.
A. shallow clone 한도·서브모듈·사내망 DNS를 확인하세요. 원격 Mac에서 git ls-remote로 동일 URL을 수동 재현하면 원인 분리가 빠릅니다.
Q. 실패 재시도를 몇 번까지 둘까요?
A. Bundler --retry 3 + 셸 백오프 3회를 넘기면 한도·비용만 커지는 경우가 많습니다. 근본 오류(인증·lock)는 재시도 전에 분류하세요.