Contents
see List왜 배포 워크플로를 다시 점검해야 하나
GitHub Actions로 배포를 자동화하면 가장 먼저 빌드 속도와 편의성이 보입니다. 그러나 운영 단계에서 더 자주 문제가 되는 부분은 비밀키 보관, 중복 배포, 권한 과다 부여, 실패 시 추적성입니다. 특히 클라우드 접근 키를 GitHub Secrets에 장기간 저장해 두면 키 교체 주기가 늦어지고, 누가 어떤 권한으로 배포했는지 추적하기도 어려워집니다. OIDC 기반 배포는 워크플로 실행 시점에 짧은 수명의 토큰을 발급받아 클라우드 역할과 교환하는 방식이므로, 저장된 장기 접근 키를 줄이는 데 효과적입니다.
배포 워크플로는 단순히 main 브랜치에 push되면 서버에 파일을 올리는 수준에서 끝내면 안 됩니다. 같은 브랜치에 여러 커밋이 빠르게 들어왔을 때 오래된 배포가 나중에 끝나 최신 배포를 덮어쓰는 상황을 막아야 하고, 테스트 단계와 배포 단계의 권한도 분리해야 합니다. 또한 Actions의 기본 토큰은 편리하지만 모든 작업에 쓰기 권한을 열어 두면 위험합니다. 워크플로 상단에서 기본 권한을 최소화하고, 배포 job에만 필요한 권한을 명시하는 습관이 중요합니다.
OIDC, permissions, concurrency의 역할
- OIDC: GitHub Actions 실행 정보를 담은 토큰을 클라우드에 제출하고, 사전에 신뢰 조건이 맞을 때만 임시 권한을 받습니다. 저장된 클라우드 액세스 키를 줄일 수 있습니다.
- permissions: GITHUB_TOKEN과 OIDC 토큰 요청 권한을 job 단위로 제한합니다. 배포 job에는 보통
contents: read와id-token: write가 필요합니다. - concurrency: 같은 환경으로 향하는 배포를 하나의 그룹으로 묶어 중복 실행을 막습니다. 개발 브랜치는 이전 실행을 취소하고, 운영 배포는 정책에 따라 큐잉 또는 취소 방식을 정합니다.
- environment: production 같은 환경 이름을 명시하면 GitHub의 환경 보호 규칙, 승인자, 환경별 secrets와 함께 운영할 수 있습니다.
기본 워크플로 예시
아래 예시는 AWS를 기준으로 했지만 구조는 다른 클라우드에도 비슷하게 적용됩니다. 핵심은 워크플로 전체 기본 권한을 읽기 위주로 두고, 배포 job에서만 OIDC 토큰 요청 권한을 열어 주는 것입니다. 클라우드 역할의 신뢰 정책에는 저장소, 브랜치, 환경 같은 조건을 반드시 넣어야 합니다.
name: deploy-production
on:
push:
branches: [main]
permissions:
contents: read
concurrency:
group: deploy-production-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
environment: production
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials with OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-prod-deploy
aws-region: ap-northeast-2
- run: npm ci
- run: npm run build
- run: aws s3 sync ./dist s3://example-prod-web --delete
신뢰 조건은 넓게 열지 않는다
OIDC를 도입했다고 해서 자동으로 안전해지는 것은 아닙니다. 클라우드 쪽 역할 신뢰 정책이 너무 넓으면 같은 조직의 다른 저장소나 임의 브랜치에서도 역할을 받을 수 있습니다. 최소한 저장소 전체 이름, 브랜치, 환경을 조건으로 묶는 것이 좋습니다. 운영 배포라면 repo:OWNER/REPO:environment:production처럼 환경 기준 subject를 활용하고, GitHub environment에 승인 규칙을 붙이면 실수로 main에 들어간 커밋이 곧바로 운영에 반영되는 위험을 줄일 수 있습니다.
또한 pull_request 이벤트에서 외부 기여자의 코드를 실행하는 워크플로와 배포 워크플로를 분리해야 합니다. 테스트 워크플로는 읽기 권한만 사용하고, 배포 워크플로는 보호 브랜치 또는 수동 승인 환경에서만 실행되도록 구성합니다. 배포 권한을 가진 job에서 임의 스크립트나 외부 action을 과도하게 실행하면 공급망 위험이 커집니다. 사용 중인 action은 가능하면 공식 action 또는 신뢰할 수 있는 action으로 제한하고, 중요한 운영 배포에서는 버전 태그뿐 아니라 커밋 SHA 고정도 검토합니다.
중복 배포와 오래된 실행 정리
배포 자동화에서 자주 보이는 장애는 실패보다 순서 꼬임입니다. 예를 들어 10분 걸리는 배포가 시작된 직후 핫픽스 커밋이 추가되면 두 배포가 동시에 실행됩니다. 첫 번째 배포가 늦게 끝나면 운영 서버가 오래된 커밋으로 되돌아갈 수 있습니다. concurrency를 사용하면 같은 그룹의 실행을 제어할 수 있습니다. 빠르게 바뀌는 웹 프런트 배포는 cancel-in-progress: true로 오래된 실행을 취소하는 편이 적합합니다. 반대로 데이터베이스 마이그레이션처럼 순서가 중요한 작업은 자동 취소보다 별도 승인, 잠금, 수동 실행 정책이 더 안전합니다.
운영 체크리스트
- GitHub Secrets에 장기 클라우드 키가 남아 있다면 OIDC 전환 대상을 먼저 분류합니다.
- 워크플로 상단의
permissions기본값을 최소화하고 job마다 필요한 권한만 추가합니다. - 배포 job에는
id-token: write가 왜 필요한지 주석이나 문서로 남깁니다. - 클라우드 역할 신뢰 조건에 저장소, 브랜치, 환경 조건을 넣고 와일드카드 사용을 줄입니다.
- production 환경에는 승인자, 보호 브랜치, 배포 기록 확인 절차를 연결합니다.
concurrency그룹 이름은 워크플로와 환경이 섞이지 않게 명확히 만들고, 운영 정책에 맞게 취소 또는 대기 방식을 정합니다.- 배포 로그에는 커밋 SHA, 실행 번호, 배포 대상, 결과를 남겨 롤백 판단이 가능하게 합니다.
정리하면, 실무 배포 워크플로의 핵심은 빠른 자동화가 아니라 제한된 권한, 짧은 수명의 인증, 예측 가능한 실행 순서입니다. OIDC로 장기 토큰을 줄이고, permissions로 job 권한을 좁히고, concurrency로 중복 배포를 제어하면 배포 자동화의 편의성과 운영 안정성을 함께 얻을 수 있습니다.