CI 네트워크도 장애 조치가 필요하다: GitHub Actions VNET failover로 보는 배포망 설계
배포 파이프라인이 사설망 안의 리소스에 접근해야 할 때, 그 네트워크가 죽어도 우리는 배포를 계속할 수 있을까?
CI 네트워크도 장애 조치가 필요하다: GitHub Actions VNET failover로 보는 배포망 설계
- 카테고리: infra_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 배포 파이프라인이 사설망 안의 리소스에 접근해야 할 때, 그 네트워크가 죽어도 우리는 배포를 계속할 수 있을까?
- 핵심 출처:
- GitHub Actions: Early April 2026 updates - 2026-04-02, 확인일 2026-06-21
- About Azure private networking for GitHub-hosted runners in your organization - 현행 문서, 확인일 2026-06-21
- Configuring private networking for GitHub-hosted runners in your organization - 현행 문서, 확인일 2026-06-21
- REST API endpoints for enterprise network configurations - 현행 문서, 확인일 2026-06-21
- Better diagnostics for VNET injected runners and required self-hosted runner upgrades - 2025-12-12, 확인일 2026-06-21
1. 왜 지금 봐야 하나
CI/CD는 보통 “코드를 빌드하고 배포하는 도구”로 생각한다. 하지만 운영 관점에서 보면 CI/CD는 production으로 가는 제어면(control plane) 이다. 이 제어면이 사설 데이터베이스, 내부 패키지 저장소, 사내 API, 클라우드 VPC 안의 배포 대상에 접근해야 한다면 CI는 단순 SaaS가 아니라 우리 네트워크 설계의 일부가 된다.
GitHub는 2026년 4월 GitHub Actions 업데이트에서 Azure private networking을 쓰는 GitHub-hosted runners에 VNET failover public preview를 추가했다고 공지했다. 핵심은 primary Azure subnet이 사용할 수 없을 때 secondary Azure subnet을 구성해 runner traffic을 우회시킬 수 있다는 것이다. 다만 GitHub Docs는 중요한 제한도 함께 말한다. VNET failover는 preview이고, primary와 failover 사이 전환은 사용자가 enable/disable하는 수동 프로세스다. failover subnet도 사전에 필요한 Azure 리소스와 GitHub network settings resource를 갖춰야 하며 supported region 안에 있어야 한다.
이 변화가 중요한 이유는 기능 자체보다 질문을 바꾸기 때문이다.
배포 파이프라인은 “인터넷이 되면 실행된다”가 아니라 “어떤 사설 경로, 어떤 subnet, 어떤 runner group, 어떤 fallback을 통해 production에 닿는가”로 설계해야 한다.
최근 많은 팀이 보안 때문에 self-hosted runner나 private networking runner를 쓴다. DB migration, Terraform apply, internal registry pull, private endpoint 호출을 public internet에 열지 않기 위해서다. 하지만 네트워크를 닫을수록 장애 모드는 더 복잡해진다. 방화벽, DNS, TLS inspection, subnet IP 고갈, region 장애, runner image 호환성 중 하나만 막혀도 배포가 멈춘다. 오늘 글은 GitHub Actions VNET failover를 계기로, CI 네트워크를 작은 팀도 운영 가능한 형태로 설계하는 방법을 정리한다.
2. 핵심 개념
오늘의 핵심 개념은 CI 네트워크 경로를 배포 의존성으로 다루기다.
일반적인 GitHub-hosted runner는 GitHub가 관리하는 환경에서 job을 실행하고 인터넷을 통해 외부 서비스에 접근한다. 반면 Azure private networking for GitHub-hosted runners를 쓰면 runner의 네트워크 인터페이스가 Azure VNET에 연결되어, private endpoint나 on-premises 리소스에 닿을 수 있다. GitHub Docs는 이 방식으로 세 가지를 얻는다고 설명한다.
첫째, 내부 리소스를 public port 없이 접근할 수 있다. 예를 들어 migration job이 private PostgreSQL endpoint에 접근하거나, deployment job이 내부 Kubernetes API 또는 private package registry에 접근할 수 있다.
둘째, outbound network policy를 조직이 통제할 수 있다. runner가 어디로 나갈 수 있는지 NSG, firewall, routing 정책으로 제한한다. supply chain 공격을 줄이는 데 도움이 되지만, 필요한 GitHub·Azure·artifact endpoint를 빠뜨리면 job이 실패한다.
셋째, VNET network logs로 runner traffic을 관찰할 수 있다. “빌드는 실패했다”가 아니라 “어떤 endpoint 연결이 timeout 되었는가”, “DNS가 실패했는가”, “TLS inspection 때문에 handshake가 깨졌는가”를 볼 수 있어야 한다.
VNET failover는 여기에 네 번째 질문을 추가한다. primary network path가 사라졌을 때 secondary path가 준비되어 있는가?
중요한 점은 failover가 magic 버튼이 아니라는 것이다. GitHub Docs 기준으로 failover network는 secondary Azure VNET subnet이며, primary와 다른 region에 둘 수 있다. 그러나 두 subnet 모두 사전에 Azure resource, network security group, network settings resource를 준비해야 한다. 전환도 문서상 manual enable/disable 프로세스다. 즉 “장애가 나면 알아서 해결”이 아니라 “장애 전에 같은 정책을 가진 두 번째 경로를 만들고, 전환 절차를 runbook으로 연습”해야 의미가 있다.
3. 최신 이슈와 연결
이번 GitHub Actions 업데이트는 여러 조각이 함께 움직인다.
첫째, 2026년 4월 changelog는 Azure private networking failover를 public preview로 소개했다. GitHub는 secondary Azure subnet을 구성할 수 있고, primary subnet이 unavailable할 때 workflow continuity를 유지하는 데 사용할 수 있다고 설명한다. 검색 결과에는 GitHub가 regional outage 중 자동 trigger도 언급한 흔적이 있지만, 현재 추출한 changelog 본문에는 2026년 4월 20일 편집자 주석으로 “preview에서 VNET failover는 manual이고 automated가 아니다”라고 정정되어 있다. 따라서 이 글에서는 수동 failover를 기준으로 설명한다.
둘째, GitHub Docs의 organization 문서는 failover network 조건을 더 운영적으로 풀어준다. failover subnet은 다른 Azure region에 있을 수 있지만 supported region이어야 하고, primary와 failover를 전환하는 것은 사용자의 판단이다. 또 두 subnet 모두 필요한 VNET/subnet/network settings 등의 리소스를 미리 갖춰야 한다.
셋째, Enterprise REST API 문서는 network configuration 객체에 network_settings_ids, failover_network_settings_ids, failover_network_enabled 필드가 있음을 보여준다. 이는 console에서만 누르는 기능이 아니라, enterprise 운영 자동화가 현재 어떤 network path가 켜져 있는지 조회하고 변경할 수 있는 대상이라는 뜻이다. 작은 팀이라도 “배포망 상태를 API로 점검할 수 있는가?”라는 관점은 가져갈 수 있다.
넷째, 2025년 12월 GitHub changelog는 VNET injected runners의 diagnostics가 개선되어 endpoint별 visibility, connection attempts, failure rate, success percentage, timeout/proxy/DNS/TLS interception/blocked domains 같은 failure classification을 제공한다고 소개했다. 사설망 runner의 장애는 “GitHub Actions가 느리다”가 아니라 “특정 endpoint로 나가는 경로가 막혔다”로 좁혀야 해결할 수 있다.
4. 개발자 관점 해석
작은 팀이 여기서 배워야 할 것은 “우리도 당장 GitHub Enterprise private networking을 사야 한다”가 아니다. 더 일반적인 원리는 이것이다.
CI/CD의 네트워크는 production 네트워크만큼 명시적으로 문서화되어야 한다.
배포 job이 private database migration을 실행한다면, 그 job은 database의 운영 경로에 포함된다. Terraform이 VPC 안의 private API를 호출한다면, 그 runner는 인프라 변경 권한과 네트워크 권한을 동시에 가진다. 이때 다음 질문에 답하지 못하면 사고가 난다.
- runner가 들어가는 subnet은 어디인가?
- 그 subnet의 outbound allowlist는 무엇인가?
- GitHub, package registry, container registry, cloud API, secret manager, artifact storage로 나가는 경로가 열려 있는가?
- TLS inspection을 한다면 runner image가 중간 인증서를 신뢰하는가?
- 동시에 뜰 runner 수보다 subnet IP가 충분한가?
- primary region 또는 subnet 장애 때 수동으로 전환할 secondary path가 있는가?
- 전환 후에도 같은 secret, 같은 policy, 같은 logging이 유지되는가?
여기서 tradeoff가 생긴다. public runner는 단순하고 빠르지만 내부 리소스를 public하게 열거나 별도 bastion을 만들어야 할 수 있다. self-hosted runner는 네트워크 통제가 쉽지만 runner patching, autoscaling, isolation, secret exposure를 직접 운영해야 한다. GitHub-hosted runner with private networking은 중간 지점이다. runner 관리는 GitHub에 맡기되, 네트워크 경계는 Azure VNET으로 가져온다. 대신 subnet, NSG, supported region, failover, diagnostics를 운영해야 한다.
AI 시대에는 이 문제가 더 중요해진다. coding agent, release bot, 자동 rollback bot이 CI 위에서 실행될수록 CI runner는 “사람이 가끔 누르는 빌드 머신”이 아니라 자동화된 운영 주체가 된다. 이 주체가 production private network에 닿는다면, 접근 경로와 failover 경로를 코드 리뷰 대상처럼 다뤄야 한다.
5. 내 프로젝트에 적용할 체크포인트
1) 배포 job의 네트워크 지도를 그린다
오늘 바로 할 수 있는 가장 작은 작업은 .github/workflows를 열고 network dependency를 표시하는 것이다.
- public internet만 필요한 job
- cloud API만 필요한 job
- private DB/API/registry가 필요한 job
- production secret을 읽는 job
- migration, rollback, Terraform처럼 실패 시 영향이 큰 job
이 중 private resource에 접근하는 job은 runner type과 네트워크 경로를 따로 적는다. “ubuntu-latest에서 실행”만으로는 부족하다. 어떤 VPC/VNET/subnet 또는 self-hosted runner group을 통과하는지 남겨야 한다.
2) failover는 버튼이 아니라 사전 준비다
GitHub VNET failover 문서처럼 secondary subnet을 쓰려면 미리 리소스가 있어야 한다. 이는 모든 CI 네트워크에 적용된다. 대체 runner group, 대체 region, 대체 NAT, 대체 registry mirror가 장애 시점에 처음 만들어지면 늦다.
runbook에는 최소한 다음이 있어야 한다.
조건: primary CI network path에서 private resource 접근 실패가 10분 이상 지속
확인: diagnostics에서 DNS/TLS/blocked-domain/IP exhaustion 중 원인 분류
전환: secondary runner group 또는 failover network enable
검증: smoke deploy 또는 read-only migration check
복귀: primary path 복구 후 disable failover, 실패 로그 보존
3) subnet 용량을 job concurrency와 연결한다
GitHub 구성 문서는 subnet sizing에서 예상 maximum job concurrency에 30% buffer를 더하라고 안내한다. 예를 들어 최대 동시 job 300개라면 최소 390 runner capacity를 고려하라는 식이다. 숫자 자체보다 중요한 원리는 “CI scale은 IP capacity를 소비한다”는 점이다.
작은 팀도 self-hosted runner autoscaling이나 private subnet runner를 쓰면 같은 문제가 생긴다. build queue가 늘어 runner가 늘고, runner가 늘어 subnet IP나 NAT port를 소진하고, 그 결과 배포가 더 느려지는 식이다. CI queue depth, runner concurrency, subnet IP, NAT gateway metric을 함께 봐야 한다.
4) diagnostics를 실패 분류에 맞춘다
GitHub의 VNET diagnostics 개선은 endpoint visibility, connection attempts, failure rate, success percentage, DNS/TLS/proxy/blocked domain 분류를 강조한다. 우리 시스템도 CI 실패를 다음처럼 분류할 수 있어야 한다.
- code/test failure
- dependency download failure
- cloud API auth failure
- private network routing failure
- DNS failure
- TLS interception/certificate failure
- IP/NAT capacity failure
- platform incident
이 분류가 없으면 모든 장애가 “Actions가 이상하다”로 뭉개지고, 잘못된 rollback이나 불필요한 retry로 이어진다.
5) 보안 정책을 failover에도 복제한다
secondary path는 보안 우회로가 되기 쉽다. 급하게 만든 failover subnet에 더 넓은 outbound rule, 약한 logging, 다른 secret scope가 들어가면 장애 대응 중 보안 경계가 무너진다.
따라서 failover network는 primary와 같은 원칙을 가져야 한다.
- inbound deny 기본값
- 필요한 outbound만 allow
- secret manager 접근 범위 동일
- runner group 접근 조직/저장소 제한 동일
- network log와 audit log 보존 동일
- 전환 권한은 소수 운영자에게 제한
6. 오늘 10분 액션
- 가장 중요한 배포 workflow 하나를 고른다.
- 그 workflow가 접근하는 네트워크 대상을
public,cloud-api,private-resource로 표시한다. - private-resource가 하나라도 있으면 runner가 어느 subnet/VPC/VNET에 붙는지 적는다.
- “이 경로가 죽으면 어떻게 배포/rollback할까?”를 한 문장으로 쓴다.
- 답이 없다면 backlog에
CI network failover runbook 작성이슈를 만든다.
예시 이슈 제목:
[infra] production deploy workflow의 private network dependency와 failover 절차 문서화
완료 조건은 거창할 필요 없다.
- workflow별 runner group 표
- private endpoint 목록
- primary/fallback network path
- 장애 시 수동 전환 담당자
- 전환 후 smoke test 명령
이 다섯 줄만 있어도 다음 장애 때 “어디부터 봐야 하지?”라는 시간이 줄어든다.
7. 더 볼 자료
- GitHub Actions: Early April 2026 updates — VNET failover public preview와 OIDC custom properties GA 공지.
- About Azure private networking for GitHub-hosted runners in your organization — private runner networking의 보안·region·failover 개념.
- Configuring private networking for GitHub-hosted runners in your organization — Azure resource, NSG, subnet sizing, failover 설정 절차.
- REST API endpoints for enterprise network configurations — network configuration과 failover 상태를 API로 관리하는 방법.
- Better diagnostics for VNET injected runners and required self-hosted runner upgrades — VNET injected runner 연결 진단과 self-hosted runner version enforcement.
중복 회피 메모
로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 글은 Docker Content Trust/이미지 서명, AI incident response agent guardrail, EventBridge 비동기 로깅, Lambda durable functions/idempotency, GitHub Actions SaaS 제어면 장애, Cloudflare Queues backlog/DLQ, PostgreSQL 관측 지표, GKE capacity buffer를 다뤘다. 이번 글은 GitHub Actions 자체 장애가 아니라 private networking runner의 primary/failover VNET 경로, subnet 용량, diagnostics, 보안 정책 복제에 초점을 맞춰 CI 네트워크를 배포 의존성으로 설계하는 관점으로 중복을 피했다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)