본문 바로가기
MegazoneCloud
← 전체 글
AWS

[AWS] CloudFront 연속 배포 실측기: 가중치 15%를 걸었는데 분배가 0%였던 이유

운영 중인 CloudFront 배포에서 응답 헤더나 캐시 정책을 바꿔야 할 때, 변경 자체는 어렵지 않습니다. 부담스러운 건 “바꾼 설정이 실제 사용자 요청에서도 의도대로 동작하는가”를 전면 적용 전에는 확신하기 어렵다는 점입니다. 테스트용 배포를 따로 만들어 확인하는 방법도 있지만, 별도 도메인·별도 캐시·별도 트래픽이라 운영과 같은 조건은 아닙니다.

이번 글에서는 CloudFront의 연속 배포(Continuous Deployment)로, 운영 배포(Primary)는 그대로 둔 채 실제 트래픽의 일부만 스테이징 배포(Staging)로 흘려보내 설정 변경을 검증한 과정을 다룹니다. 그런데 검증 도중, 예상과 전혀 다른 결과를 만났습니다. 헤더 기반 라우팅은 정상 동작했지만, 가중치 기반으로 10~15%를 설정했는데 실측 분배가 0%로 나온 것입니다. 파고든 끝에 원인은 그 순간 CloudFront 서비스 전체가 피크였기 때문으로 확정됐습니다. 같은 설정, 같은 오후 시간대인데 어떤 날은 0%, 다른 날은 14.5%가 나왔습니다. 이 글의 절반은 이 “0%”의 원인을 추적한 기록입니다.

재현 가능하도록 사용한 배포 구성과 검증 방법, 명령어를 본문에 공개했으니, 비슷한 검증을 앞두고 계시다면 같은 방식으로 재현해보실 수 있습니다.

구성 환경

운영 배포 역할의 Primary 1개를 두고, 그 복제본으로 Staging 배포를 생성했습니다. Origin은 비공개 S3 버킷이며 OAC(Origin Access Control)로 연결했습니다. 뷰어는 Primary 도메인으로만 요청하고, CloudFront가 연속 배포 정책에 따라 일부를 Staging으로 라우팅합니다. 뷰어가 Staging 도메인으로 직접 요청할 수는 없습니다. (검증 수행일: 2026.10.19 ~ 10.22, 나흘에 걸쳐 측정)

Origin은 S3 버킷(`ap-northeast-2`)의 전용 경로 하나를 두고, OAC를 사용하므로 버킷 정책에 Primary와 Staging 두 배포의 접근을 각각 허용했습니다. 연속 배포로 Staging을 쓸 때 버킷 정책에 Staging 배포 접근을 추가하지 않으면 Staging이 403을 반환하므로, 이 설정은 빠뜨리면 안 됩니다.

이번 검증에서 바꾼 설정은 한 가지입니다. 여러 설정을 동시에 바꾸면 어느 변경이 어떤 영향을 줬는지 분리하기 어렵기 때문에, 변경은 하나로 좁혔습니다.

변경 대상: 기본 캐시 동작의 응답 헤더 정책(Response Headers Policy)

변경 전(Primary): 응답 헤더 정책 없음

변경 후(Staging): 신규 정책 연결 -> `X-Content-Type-Options: nosniff`(보안 헤더) + X-Staging-Marker: v2(식별 마커) 추가

기대 효과: 보안 헤더가 응답에 추가되며, 상태 코드·본문 등 기존 응답 동작에는 영향 없음

X-Staging-Marker 헤더는 “어느 배포가 응답했는가”를 식별하는 동시에 새 정책이 실제로 적용됐는가를 확인하는 역할을 겸합니다. 응답에 이 헤더가 있으면 Staging이 응답했고 새 정책도 적용됐다는 뜻입니다.

검증 방법

이번 검증의 핵심은 성능 수치가 아니라 어느 배포가, 설정한 비율대로 응답하는가입니다. 그래서 “어느 배포가 응답했는가를 식별하는 것이 출발점이었습니다.

방법역할확인 지표
응답 헤더확인(curl)요청 단위로 어느 배포가 응답했는지 식별X-Staging-Marker: v2 유무
CloudWatch 지표배포별 요청 수를 서버 측에서 교차 검증Requests(DistributionId별)

검증은 안전한 순서로 진행했습니다. 먼저 헤더 기반으로, 지정한 HTTP 헤더가 붙은 요청만 Staging으로 보내 동작을 확인했습니다. (이때 요청 헤더는 `aws-cf-cd-` 접두사를 사용해야 합니다. 여기서는 `aws-cf-cd-staging: true`.) 이상이 없음을 확인한 뒤 가중치 기반으로 전환해, 트래픽 일부가 자동으로 Staging에 분배되도록 했습니다. 모든 요청은 캐시 영향을 줄이기 위해 쿼리스트링을 바꿔가며 보냈습니다.

라우팅 동작 검증 — 헤더 기반 (정상)

헤더 유무에 따라 요청이 올바른 배포로 가는지 먼저 확인했습니다.

요청기대 응답X-Staging-MarkerX-Content-Type-Optionsx-cache결과
헤더 없음Primary없음없음Hit정상
aws-cf-cd-staging: trueStagingv2nosniffMiss정상

두 요청 모두 의도한 배포로 정확히 갔습니다. 헤더를 붙인 요청에서만 X-Staging-Marker: v2와 X-Content-Type-Options: nosniff가 함께 나타나, 라우팅과 변경 적용이 한 번에 확인됐습니다. 흥미로운 건 Staging 응답의 `x-cache`가 `Miss`로 찍힌 점입니다. Primary와 Staging은 캐시를 공유하지 않으므로, 같은 경로라도 Staging은 자기 캐시가 비어 있어 첫 요청이 Miss가 됩니다. 공식 문서의 “캐시 비공유” 설명이 실측으로 드러난 부분입니다.

분배 비율 검증 — 가중치 기반 (1일차: 예상 밖의 0%)

헤더 기반이 정상임을 확인하고, 연속 배포 정책을 가중치 기반으로 바꿔 Staging에 트래픽을 보내도록 설정했습니다. 그리고 헤더 없이 동일 요청을 반복해 실제 분배 비율을 측정했습니다.

설정요청 수Staging 응답 수실측 비율측정 시각(KST)
가중치10%20000.0%10-19 16:13
가중치10%(재측정)40000.0%10-19 16:15
가중치15%+ 세션 고정20000.0%10-19 16:18
가중치 15%(세션 고정 OFF)30000.0%10-19 16:52
가중치 15%, 엣지 IP 8개 분산40000.0%10-19 17:55

설정은 10~15%였는데, 수천 건 중 단 한 건도 Staging으로 가지 않았습니다. 측정 방식을 의심해 표본을 늘리고, 연결을 매번 끊고(`Connection: close`) 요청 간격을 두고, 세션 고정을 껐다 켜고, 공개 DNS로 얻은 서로 다른 CloudFront 엣지 IP 8곳에 직접 분산 요청까지 해봤지만 결과는 모두 0%였습니다. 세션 고정을 켰는데도 세션 쿠키(`Set-Cookie`)가 한 번도 심기지 않았다는 건, CloudFront가 분배 자체를 시작하지 않았다는 신호였습니다.

원인 추적

curl 결과만으로 단정하지 않기 위해, CloudWatch의 배포별 요청 수 지표로 교차검증했습니다. (CloudFront 지표는 `us-east-1`, `Region=Global` 차원입니다.) 1일차 측정 구간에서 Primary는 862건 이상, Staging은 1건이었습니다. 서버 측 지표로도 Staging 분배는 사실상 0이었습니다.

설정 자체는 정상이었습니다. 정책은 Enabled: true, Primary에 정상 연결, 가중치 값도 API 응답에서 그대로 확인됐습니다. 그리고 같은 정책의 헤더 기반 모드는 바로 앞 단계에서 정상 동작했습니다. 즉 내 설정이 틀려서 0%”가 아니라, 설정은 맞는데 CloudFront가 가중치 분배를 적용하지 않고 트래픽을 전부 Primary로 보내고 있다는 것이었습니다.

통제한 변수를 정리하면 이렇습니다. 가중치 값(10%/15%), 세션 고정(on/off), 표본 수(60~400건), 연결 방식, 엣지 로케이션(IP 8개) — 이 모든 걸 바꿔도 전부 0%였습니다. 단 하나 통제하지 못한 변수가 측정 시각이었습니다. 1일차 측정은 모두 KST 오후 4~6시에 집중돼 있었습니다.

여기서 AWS 공식 문서의 한 문장이 결정적 단서였습니다. 피크 시간에는 연속 배포 정책과 무관하게 모든 요청을 Primary로 보낸다. 여기서 피크는 내 배포가 아니라 CloudFront 서비스 전체의 피크를 의미한다.” 가설은 분명해졌습니다 — 측정 시각이 CloudFront 글로벌 피크와 겹친 것이 아닐까? 이를 확인하는 방법은 하나, 시간대를 바꿔 다시 측정하는 것이었습니다.

분배 비율 검증 — 가중치 기반(2일차: 시간대 변경 후 살아난 분배)

다음 날 아침(KST 오전 9시경, 글로벌 트래픽이 낮은 시간)에 같은 머신, 같은 설정(가중치 15%), 같은 명령어로 다시 측정했습니다. 그리고 같은 날 오후에 한 번 더 측정했습니다.

측정시각대결과
1일차(10-19)오후0.0%
2일차(10-20)오전13.6%
2일차(10-20)오후14.5%
3일차(10-21)오후0.0%
4일차(10-22)오전15.1%

시간대를 바꾸자 분배가 살아났습니다. 작은 표본(400건)에서는 8.0%로 설정값과 다소 차이가 났지만, 표본을 1,000건으로 늘리자 13.6%로 설정값 15%에 수렴했습니다. CloudWatch 서버 지표(13.3%)와도 일치합니다. 작은 표본의 편차였을 뿐, 분배는 설정대로 동작하고 있었습니다.

측정을 반복할수록 패턴이 더 또렷해졌습니다. 2일차 오후(15:08), 즉 1일차에 0%가 나왔던 것과 같은 오후 시간대에 다시 측정했더니 14.5%가 나왔고, 3일차 오후(17:01)에는 또 0%로 떨어졌으며, 4일차 아침(09:17)에는 15.1%로 설정값에 거의 정확히 일치했습니다. 정리하면 아침 측정은 두 번 모두 정상(13.6%, 15.1%), 오후 측정은 날에 따라 0%(10-19, 10-21)와 14.5%(10-20)로 갈렸습니다. 같은 설정인데 같은 오후끼리도 결과가 달랐다는 점이 핵심 단서입니다.

원인 확정 — 오후라서가 아니라, 그 순간 서비스가 바빠서

처음에는 오후 = 피크 = 0%, 아침 = 여유 = 정상이라는 시간대 가설을 세웠습니다. 하지만 2일차 오후에도 14.5%가 정상적으로 나오면서, 이 가설은 더 정밀하게 수정됐습니다.

측정시각대결과
1일차(10-19)오후0.0%
2일차(10-20)오전13.6%
2일차(10-20)오후14.5%
3일차(10-21)오후0.0%
4일차(10-22)오전15.1%

아침 측정은 두 번(13.6%, 15.1%) 모두 정상이었고, 오후 측정은 날에 따라 0%(10-19, 10-21)와 14.5%(10-20)로 갈렸습니다. 같은 오후인데 어떤 날은 0%, 어떤 날은 14.5%였습니다. 즉 오후라는 시각 자체”가 원인이 아니었습니다. 정확한 원인은 공식 문서 그대로 그 순간 CloudFront 서비스 전체의 부하 입니다. 가중치 분배는 CloudFront에 여유가 있을 때 수행하는 동작에 가깝고, 서비스 전체가 피크로 바쁜 순간에는 정책과 무관하게 전부 Primary로 보냅니다. 여기서 피크는 내 배포가 아니라 CloudFront 서비스 전체의 트래픽이라는 점이 핵심입니다. 내 사이트는 한가해도, 그 시점 CloudFront 글로벌 서비스가 바쁘면 분배가 멈출 수 있습니다.

1일차 오후에는 하필 그 순간 CloudFront 글로벌 피크가 걸렸고, 2일차 아침·오후에는 걸리지 않았던 것으로 해석됩니다. 따라서 0% 현상은 특정 시간대에 고정된 것이 아니라, 언제든 서비스가 피크면 발생할 수 있다가 최종 결론입니다.

설정 변경 효과 검증

분배 비율과 별개로, 변경한 응답 헤더 정책 자체는 의도대로 동작했습니다. 헤더 기반으로 Staging에 강제 라우팅했을 때 확인한 결과입니다.

지표Primary (변경 전)Staging (변경 후)해석
X-Staging-Marker없음V2새 정책이 Staging에 적용됨
X-Content-Type-Options없음nosniff보안 헤더 정상 추가
HTTP 상태200200응답 동작 변화 없음

응답 헤더 추가는 상태 코드나 본문을 바꾸지 않는 변경이라, Staging에서도 200 응답과 본문이 그대로 유지됐습니다. 변경 자체는 안전한 변경으로 판단할 수 있었습니다. 2일차에 가중치 분배가 정상 동작하는 것까지 확인한 뒤로는, 실제 트래픽 15% 비율에서도 Staging 응답이 200으로 유지돼 운영 트래픽 기준 안전성까지 확인할 수 있었습니다.

승격(Promote) — 검증한 설정을 운영에 반영

가중치 분배까지 정상 동작을 확인한 뒤, 마지막으로 승격(Promote)을 실행해 Staging 설정을 Primary로 반영했습니다. 승격은 Staging의 구성을 Primary로 복사하고, 연속 배포 정책을 비활성화해 모든 트래픽을 다시 Primary로 되돌립니다.

승격 전후를 비교하면 이렇습니다.

지표승격 전 Primary승격 후 Primary확인
응답 헤더 정책없음msg-staging-headers 연결됨Staging 구성이 복사됨
X-Content-Type-Options (헤더 없는 일반 요청)없음nosniff보안 헤더가 운영에 반영됨
연속 배포 정책Enabled: trueEnabled: false승격과 함께 자동 비활성화
트래픽85% Primary / 15% Staging100% Primary분배 종료

승격 직후, 헤더를 붙이지 않은 일반 요청에도 `X-Content-Type-Options: nosniff`가 응답에 포함됐습니다. 검증 단계에서 Staging에만 적용했던 보안 헤더가 이제 Primary(운영)에서 모든 요청에 적용된 것입니다. 동시에 연속 배포 정책은 `Enabled: false`로 자동 전환되어 트래픽 분배가 종료됐고, 모든 요청이 Primary로 돌아갔습니다.

정리하면 전체 흐름은 이렇습니다. 운영(Primary)은 그대로 둔 채 복제본(Staging)에만 변경을 적용해, 헤더 기반으로 기능을 확인하고 가중치 기반으로 실제 트래픽에서 안전성을 확인한 뒤, 문제가 없다고 판단되면 승격으로 운영에 반영합니다. 만약 검증 중 문제가 보였다면 승격하지 않고 연속 배포 정책을 비활성화하는 것만으로 즉시 원복됩니다. 검증 내내 Primary는 변경 전 상태를 유지하므로, 롤백에 추가 작업이 필요 없다는 점이 별도 배포를 따로 만들어 테스트하는 방식과 가장 큰 차이입니다.

마치며

이번 검증은 “응답 헤더 정책 추가를 운영 트래픽으로 안전하게 검증한다”로 시작해, 헤더 기반 검증 → 가중치 기반 분배 검증 → 승격(Promote)으로 운영 반영까지 연속 배포의 전체 워크플로우를 한 번에 돌려봤습니다. 다만 실제로 가장 많은 시간을 쓴 건 예상 밖의 결과를 추적하는 일이었습니다.

정리하면 이렇습니다. 헤더 기반 라우팅은 설계대로 정확히 동작했고, 응답 헤더 정책 변경 자체도 Staging에서 의도대로 적용됐으며, 최종 승격 후에는 보안 헤더가 Primary 전체 응답에 반영되고 연속 배포 정책이 자동 비활성화되는 것까지 확인했습니다. 가중치 기반 라우팅은 1일차 오후에 10~15%를 설정했는데도 1,500건 이상의 요청과 CloudWatch 교차검증에서 모두 0%로 나와 애를 먹였지만, 이후 같은 설정으로 다시 측정하자 13.6%(아침)와 14.5%(오후)로 설정값에 수렴했습니다. 특히 1일차와 같은 오후 시간대인데도 2일차엔 정상 분배됐다는 점에서, 원인은 “특정 시각”이 아니라 그 순간 CloudFront 서비스 전체의 피크 여부임이 확정됐습니다. 공식 문서가 명시한 피크 시간엔 전부 Primary 동작이 실측으로 재현된 것입니다.

실무적으로 남는 교훈은 분명합니다. 연속 배포의 가중치 라우팅은 “설정한 비율이 항상 그대로 간다”고 전제하면 안 됩니다. 같은 설정, 같은 시간대라도 그 순간 CloudFront 서비스가 피크면 분배가 전혀 일어나지 않을 수 있습니다. 그래서 가중치 기반으로 검증·배포를 계획한다면 다음을 권합니다.

– 분배가 실제로 일어나는지 먼저 소량으로 확인하고, 시점을 달리해 여러 번 볼 것 (한 번 0%가 나왔다고 기능 고장으로 단정하지 말 것)

– 검증을 단일 클라이언트의 짧은 측정으로 끝내지 말 것. 작은 표본은 편차가 크고(400건에선 8%, 1000건에선 13.6~14.5%), 서비스 피크면 아예 0이 나올 수 있음

– 내부 테스트의 확실성이 필요하면, 서비스 부하 영향을 받지 않는 헤더 기반을 함께 쓸 것

– 가중치 상한은 15%라 100% 전환 검증은 애초에 불가능하다는 점도 염두에 둘 것

이 글의 수치는 2026.10.19~22 소규모 재현 환경의 결과입니다. 특히 같은 오후인데 0% / 14.5%처럼 가중치 분배는 그 순간의 서비스 부하에 의존하므로, 실제 도입을 검토하신다면 여러 시점에 걸쳐 직접 분배 비율을 재현해보시길 권합니다.

—

참고 문서

– 연속배포동작방식: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/understanding-continuous-deployment.html

-연속배포워크플로우:

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/continuous-deployment-workflow.html

-쿼터및고려사항(피크시간동작명시): https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/continuous-deployment-quotas-considerations.html

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다