같은 브랜치에 연속으로 push했더니 오래된 배포가 최신 배포를 덮어쓴 이유
문제 발생
짧은 시간 안에 커밋을 두 번 연달아 push했는데, 배포가 끝난 뒤 서버에 반영된 코드가 최신 커밋이 아니라 그 직전 커밋의 것이었습니다.
원인 분석
GitHub Actions는 기본적으로 같은 브랜치에 여러 번 push하면 각 push마다 독립적인 워크플로우 실행을 병렬로 시작합니다. 두 실행(A: 이전 커밋, B: 최신 커밋)이 동시에 진행될 때, 만약 A가 B보다 늦게 끝난다면(예: A가 실행되던 러너가 일시적으로 더 느렸거나, 큐잉 순서가 꼬였다면), A의 배포 단계가 B보다 나중에 실행되어 최신 코드를 이전 코드로 덮어쓰는 상황이 생길 수 있습니다.
이건 각 실행 자체는 정상적으로 성공했기 때문에 CI 로그만 봐서는 알아채기 어렵습니다 — 두 실행 다 초록색 체크가 뜨지만, 실행 순서가 꼬여 결과적으로 잘못된 버전이 배포된 것입니다.
해결 방안
concurrency 설정으로 같은 그룹의 이전 실행을 자동으로 취소하도록 만듭니다.
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
steps: [...]group으로 지정한 값이 같은 워크플로우 실행끼리 하나의 그룹으로 묶이고, 같은 그룹 안에서 새 실행이 시작되면 cancel-in-progress: true 설정에 따라 진행 중이던 이전 실행이 자동으로 취소됩니다. 결과적으로 항상 가장 최근에 시작된 실행만 끝까지 진행되므로, 오래된 실행이 나중에 완료되며 최신 배포를 덮어쓰는 문제 자체가 사라집니다.
group값에github.ref(브랜치/태그 참조)를 포함시키면 브랜치별로 독립된 그룹이 됩니다 —main에 대한 취소가 다른 브랜치의 실행에는 영향을 주지 않습니다.- 테스트/빌드 job에는 취소를, 프로덕션 배포 job에는 신중하게 적용하는 것이 일반적입니다 — 빌드 중간에 취소되는 것은 자원 낭비를 줄이는 순수한 이득이지만, 실제로 서버에 반영 중인 배포 단계를 무작정 취소하면 어중간한 상태로 배포가 끊길 위험도 있으므로, 배포 job은 어디까지 진행됐을 때 취소해도 안전한지 별도로 검토합니다.
- PR마다 여러 번 push하는 것이 흔한 워크플로우에서는,
pull_request트리거에 이 설정을 적용하는 것만으로도 불필요하게 쌓이는 CI 실행/비용을 크게 줄일 수 있습니다. cancel-in-progress: false(또는 생략)로 두면 취소 없이 그냥 순서대로 큐잉만 시키는 것도 가능합니다 — 취소가 아니라 "최신 것이 이전 것을 기다리지 않고 뒤에 줄 서게"만 하고 싶은 경우에 씁니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.