namespace Terminating 고착과 연쇄 장애
서론
이번 트러블슈팅은 프로덕션 서비스 namespace Terminating 고착과 재배포 후 도메인 502 장애 두 단계를 다룬다. worker 서비스 추가 배포 과정에서 namespace 이중 관리 충돌이 생기고, 이를 해소하려다 예상치 못한 연쇄 장애로 이어진 사례다.
sequenceDiagram participant U as 운영자 participant Argo as ArgoCD participant K8s as Kubernetes participant AWS as AWS (ALB/SG) participant CF as CloudFront U->>Argo: namespace 삭제 (foreground) Argo->>K8s: myapp 리소스 전체 삭제 K8s->>AWS: Ingress finalizer → SG 삭제 시도 AWS--xK8s: DependencyViolation (SG 의존 객체) Note over K8s: namespace Terminating 고착 U->>K8s: finalizer 강제 제거 K8s-->>K8s: namespace 삭제 완료 U->>Argo: myapp-api Sync (재배포) Argo->>AWS: 신규 ALB 생성 (DNS 변경) U->>AWS: Route53 alias 갱신 Note over CF: origin은 여전히 구 ALB CF--xU: 502 Bad Gateway U->>CF: origin → 신규 ALB로 교체 CF-->>U: 200 OK
사전 배경 (왜 myapp ns 삭제를 시도했는지)
이번 트러블슈팅은 myapp-worker를 추가 배포하던 과정에서 시작됐다. 당시 myapp-worker 관련 PR을 먼저 반영했고, 이 변경에 namespace.yaml이 실수로 함께 포함되면서 myapp namespace가 ArgoCD에서 기존 관리 경로와 신규 경로 양쪽에서 동시에 관리되는 상태가 됐다.
이 상태에서 namespace 충돌을 해소하려고 myapp-worker ArgoCD 대시보드에서 myapp namespace를 foreground delete로 처리했다. myapp-api에서 관리하고 있으니, namespace를 삭제할 거라고 생각하지 못하고 추적하는 태그만 없앨 거라 착각하고 삭제 버튼을 눌렀는데 그게 문제였다. 버튼을 누르자마자 모든 myapp 관련 리소스들이 제거되었고, 배포 실패 알림이 울리고 ArgoCD는 노란색과 빨간색 상태로 변했다.
상황을 파악하고 일단 서버를 다시 띄워야 한다고 생각했다. 문제는 namespace가 없으면 생성조차 안 되니 일단 namespace가 삭제되길 기다린 다음 myapp-api를 sync하면 재생성될 거라고 생각했다. 그러나 namespace 삭제를 기다렸지만 좀처럼 삭제되지 않았다. 처음에는 삭제가 느린가보다 생각했지만, myapp namespace는 바로 정리되지 않고 계속 Terminating에 머물렀고 무언가 이유가 있을 거라 생각해 조사한 내용이 트러블슈팅으로 이어졌다.
finalizer란
들어가기 앞서 finalizer를 간단하게 알아보자.
Kubernetes finalizer는 리소스를 즉시 삭제하지 않고, 삭제 전에 반드시 끝내야 할 정리 작업을 수행하도록 만드는 메커니즘이다. 객체에 metadata.finalizers 값이 남아 있으면 deletionTimestamp가 찍혀도 API 서버에서 완전히 제거되지 않는다. 이번 케이스의 ingress.k8s.aws/resources는 AWS Load Balancer Controller가 ALB, Target Group, Security Group 같은 외부 리소스를 정리한 뒤 finalizer를 제거하는 용도다.
목적은 크게 두 가지다. 첫째, Kubernetes 객체만 지워지고 클라우드 리소스가 고아(orphan)로 남는 상황을 막는다. 둘째, 삭제 순서가 중요한 외부 의존성 정리를 컨트롤러가 통제하게 한다.
강제 삭제(수동으로 finalizer 제거) 시에는 정리 루틴을 우회하기 때문에, 외부 리소스가 남거나 예상하지 못한 의존성 끊김이 생길 수 있다. 예를 들면 ALB/SG/ENI가 계정에 잔존하거나, 반대로 아직 참조 중인 경로가 끊겨 후속 장애가 발생할 수 있다.
그래도 강제 삭제가 가능한 상황은 있다. 이번처럼 컨트롤러가 장시간 재시도해도 finalizer를 떼지 못하고, 서비스 복구가 더 우선이며, 잔여 리소스 점검 및 수동 정리 계획이 있는 경우다. 즉, “원인 확인 완료 + 복구 우선 + 후속 정리 가능”이 충족될 때 제한적으로 선택해야 한다.
관련문서
- Kubernetes 공식 문서
Finalizershttps://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/ finalizer 동작 방식(deletionTimestamp가 찍혀도 finalizers가 비워질 때까지 삭제 보류)과 수동 제거 주의사항을 명시한다. - Kubernetes 공식 블로그
Using Finalizers to Control Deletionhttps://kubernetes.io/blog/2021/05/14/using-finalizers-to-control-deletion/ 강제 finalization은 주의해서 사용해야 하며 orphan 상태가 생길 수 있음을 설명한다. - AWS Load Balancer Controller 공식 문서
How it works(v2.14) https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.14/how-it-works/ ingress 삭제 시 controller가 AWS 컴포넌트를 삭제/정리함을 설명한다. - GKE 공식 문서
Troubleshoot namespace stuck in the Terminating statehttps://docs.cloud.google.com/kubernetes-engine/docs/troubleshooting/terminating-namespaces finalizer 제거 기반 강제 삭제는 마지막 수단(last resort)으로 권고한다.
트러블슈팅 흐름
느린 삭제인지, 삭제 블로킹인지 구분
처음에는 Terminating이 오래 보인다는 표현 때문에 단순 지연처럼 보일 수 있었지만, 실제로는 어디에서 멈췄는지를 확인해야 했다. 그래서 namespace 상태를 직접 확인했다.
kubectl get ns myapp -o yaml
kubectl get ns myapp -o jsonpath='{.status.phase}{"\n"}{.metadata.deletionTimestamp}{"\n"}{.spec.finalizers}{"\n"}'Terminating
2026-02-12T08:38:47Z
["kubernetes"]
NamespaceContentRemaining=True
NamespaceFinalizersRemaining=True
Some resources are remaining: ingresses.networking.k8s.io has 1 resource instances
Some content in the namespace has finalizers remaining: ingress.k8s.aws/resources in 1 resource instances확인 결과 NamespaceContentRemaining=True, NamespaceFinalizersRemaining=True였고, 남은 리소스가 ingresses.networking.k8s.io 1개, 남은 finalizer가 ingress.k8s.aws/resources였다. 이 시점에서 “삭제가 느린 것”이 아니라 “특정 finalizer가 삭제를 막고 있음”으로 문제 정의를 바꿨다.
블로킹 객체를 ingress 단위로 좁히기
다음으로 어떤 Ingress가 막고 있는지 확인했다.
kubectl -n myapp get ingress -o wide
kubectl -n myapp get ingress -o yaml
kubectl -n myapp describe ingress myapp-worker-ingressNAME CLASS HOSTS ADDRESS PORTS AGE
myapp-worker-ingress alb * k8s-myapp-worker-01f28dfae5-1559597038.ap-northeast-2.elb.amazonaws.com 80 ...
metadata.deletionTimestamp: 2026-02-12T08:39:00Z
metadata.finalizers:
- ingress.k8s.aws/resourcesmyapp-worker-ingress에 deletionTimestamp가 이미 찍혀 있었지만 finalizer가 남아 있었다. 즉, 삭제 요청은 들어갔지만 정리 루틴이 끝나지 않은 상태였다.
ALB Controller 로그로 “왜 finalizer를 못 떼는지” 확인
로드밸런서 controller 로그를 확인했다.
kubectl -n kube-system get deploy | rg "load-balancer-controller|myapp"
kubectl -n kube-system logs <controller-pod> --since=4h \
| rg -i "myapp-worker-ingress|failed|error|finalizer|delete|securityGroup"# 실행 결과(요약)
aws-load-balancer-controller 2/2 ... 48d
myapp-aws-load-balancer-controller 2/2 ... 35d
Reconciler error ... myapp-worker-ingress ...
error="failed to delete securityGroup: timed out waiting for the condition"로그에는 failed to delete securityGroup: timed out waiting for the condition가 반복됐다. 이 결과로, Ingress 정리의 마지막 단계에서 SG 삭제가 타임아웃되어 finalizer가 잔존한다고 해석했다.
finalizer가 왜 SG를 삭제하지 못했을까?
핵심은 ingress.k8s.aws/resources finalizer가 “AWS 정리 완료”를 조건으로 빠지는데, 그 정리 단계에서 sg-xxxxxxxxx 삭제가 AWS에서 계속 거절됐다는 점이다. 먼저 확정 가능한 사실을 CloudTrail에서 찾아보면 다음과 같다.
AWS_PROFILE=myapp-test aws cloudtrail lookup-events --region ap-northeast-2 \
--lookup-attributes AttributeKey=EventName,AttributeValue=DeleteSecurityGroup \
--start-time 2026-02-12T08:35:00Z --end-time 2026-02-12T08:50:20Z --max-results 200 \
| jq -r '.Events[] | .CloudTrailEvent|fromjson
| select(.requestParameters.groupId=="sg-xxxxxxxxx")
| [.eventTime,.errorCode,.errorMessage,.userIdentity.sessionContext.sessionIssuer.userName] | @tsv' | sort# 실행 결과(요약)
2026-02-12T08:48:21Z ~ 2026-02-12T08:49:57Z
Client.DependencyViolation
resource sg-xxxxxxxxx has a dependent object
caller: myapp-test-aws-loadbalancer-controller이 결과로 “finalizer가 SG 삭제 단계에서 막혔다”는 사실은 확정된다. 문제는 “그 dependent object가 정확히 누구였는가”다. 이 부분은 같은 시간대의 이벤트를 합쳐 좁혔다.
# (1) 기존 ALB 삭제 시도
AWS_PROFILE=myapp-test aws cloudtrail lookup-events --region ap-northeast-2 \
--lookup-attributes AttributeKey=EventName,AttributeValue=DeleteLoadBalancer \
--start-time 2026-02-12T08:35:00Z --end-time 2026-02-12T08:45:00Z --max-results 100
# (2) ENI 삭제 이벤트
AWS_PROFILE=myapp-test aws cloudtrail lookup-events --region ap-northeast-2 \
--lookup-attributes AttributeKey=EventName,AttributeValue=DeleteNetworkInterface \
--start-time 2026-02-12T08:35:00Z --end-time 2026-02-12T08:55:30Z --max-results 200
# (3) SG 참조 규칙 변경 이벤트
AWS_PROFILE=myapp-test aws cloudtrail lookup-events --region ap-northeast-2 \
--lookup-attributes AttributeKey=EventName,AttributeValue=AuthorizeSecurityGroupIngress \
--start-time 2026-02-12T08:35:00Z --end-time 2026-02-12T08:55:30Z --max-results 200# 실행 결과(요약)
(1) DeleteLoadBalancer: 08:39:01Z~08:39:38Z에 기존 myapp ALB 7개 삭제 요청
(2) DeleteNetworkInterface: ELB 서비스 주체가 ENI 삭제를 여러 번 시도(일부 재시도/일시 실패 포함)
(3) Authorize/RevokeSecurityGroupIngress: node SG(sg-yyyy...) 규칙에서 sg-0512... 참조를 부여/회수하는 이벤트 존재sg-0512...는 단일 Ingress 전용 SG가 아니라 ALB Controller의 shared backend SG였고, 삭제 시점에 AWS는 이 SG를 참조하는 객체가 남아 있다고 판단했다. 후보는 두 축이다. 첫째, ALB 삭제 직후 남아 있는 ELB ENI/연결 상태. 둘째, 다른 SG 규칙에서의 source SG 참조. 둘 다 AWS에서 DependencyViolation을 유발할 수 있다.
다만 이 시점 로그만으로는 “정확히 어떤 단일 객체 ID 때문에 08:48:21Z~08:49:57Z에 막혔는지”까지 1:1로 확정할 수 없다. CloudTrail의 DeleteSecurityGroup 오류는 객체 타입/ID를 상세히 반환하지 않기 때문이다.
따라서 결론은 다음처럼 정리했다. 첫째, 원인은 확정이다(의존 객체 존재로 SG 삭제 실패 → finalizer 잔존 → namespace Terminating 고착). 둘째, 그 의존 객체가 누군지는 확정할 수 없다(그 순간의 단일 blocking 객체 ID는 미식별).
확실한 건 SG는 특정한 부분에 의존성이 생기면 해당 객체가 선행 삭제되기 전까지는 제거되지 않는다. 해당 객체를 판별할 수 없었으므로, finalizer를 수동으로 제거했다. namespace 삭제는 즉시 완료됐고, 이후 orphan 리소스 존재 여부를 서버 복구 이후 재검증했다.
더 이상의 객체 추적은 중단하고 리소스를 삭제할 때 의존 계층에 많이 주의를 기울여야 한다는 교훈을 얻는 걸로 만족했다.
복구 우선으로 finalizer 강제 제거
그 당시에는 서비스 복구가 우선이라 판단했기에 finalizer를 수동 제거했다. 아래 명령어를 사용하면 된다.
kubectl -n myapp patch ingress myapp-worker-ingress \
--type=merge -p '{"metadata":{"finalizers":[]}}'
kubectl get ns myapp# 실행 결과(요약)
ingress.networking.k8s.io/myapp-worker-ingress patched
Error from server (NotFound): namespaces "myapp" not foundIngress는 즉시 삭제되었고 namespace도 최종 삭제됐다. 여기까지가 namespace 블로킹 이슈를 직접 해소한 부분이다.
이 부분에서 GitOps 매니페스트 저장소에 두 건의 수정 PR을 반영했다:
- 중복된 namespace 설정 제거 (
merged_at: 2026-02-12T08:33:36Z) - worker Kustomization에서 namespace 설정 분리 (
merged_at: 2026-02-12T09:02:36Z)
재배포 후 도메인 장애를 ALB 변경 이슈로 분리
하지만 아직 끝나지 않았다. ArgoCD에 앱이 모두 올라왔지만 백엔드에서는 여전히 접근 오류를 겪고 있었다. DNS 문제를 놓치고 있었다.

namespace 재생성 뒤에는 ALB DNS가 바뀌는 것이 자연스러우므로, Route53 alias가 예전 대상을 가리키는지 확인했다.
aws elbv2 describe-load-balancers --profile myapp-test --region ap-northeast-2
aws elbv2 describe-tags --profile myapp-test --region ap-northeast-2 ...# 실행 결과(요약)
k8s-myapp-api-794921f445 -> ...-2111042198.ap-northeast-2.elb.amazonaws.com (myapp-api-ingress)
k8s-myapp-api-4c0a3e5fa1 -> ...-558441375.ap-northeast-2.elb.amazonaws.com (myapp-admin-api-ingress)
k8s-myapp-cms-a0422edf41 -> ...-1023907932.ap-northeast-2.elb.amazonaws.com (myapp-cms-api-ingress)
k8s-myapp-cms-61e05e99d0 -> ...-1899118715.ap-northeast-2.elb.amazonaws.com (myapp-cms-web-ingress)
k8s-myapp-cms-e3c7e4277d -> ...-152298066.ap-northeast-2.elb.amazonaws.com (myapp-cms-admin-ingress)
k8s-myapp-worker-01f28dfae5 -> ...-402685983.ap-northeast-2.elb.amazonaws.com (myapp-worker-ingress)ingress별 신규 ALB DNS를 확인한 뒤, IaC/services/myapp/test/variables.tf의 alias 대상(api/admin-api/cms-api/cms/admin/worker)을 새 값으로 갱신했다.
위 내용을 IaC 저장소에 반영했다. (merged_at: 2026-02-12T09:08:53Z)
web 502를 Route53 문제와 CloudFront 문제로 분리
test.example.com이 502였기 때문에, 경로를 한 단계 더 내려가 CloudFront origin을 확인했다.
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' https://test.example.com
aws route53 list-resource-record-sets --profile myapp-test --hosted-zone-id ZXXXXXXXXXXXXX
aws cloudfront get-distribution --profile myapp-test --id EXXXXXXXXXXXXX# 실행 결과(요약)
test.example.com -> HTTP 502
Route53 A(alias): test.example.com -> dXXXXXXXXXXX.cloudfront.net
CloudFront(EXXXXXXXXXXXXX) DefaultOrigin:
k8s-myapp-worker-85fc07725b-1570616586.ap-northeast-2.elb.amazonaws.com (구 ALB)Route53은 CloudFront를 정상 참조했지만, CloudFront 기본 origin이 삭제된 구 ALB를 가리키고 있었다. web 502의 원인은 Route53이 아니라 stale origin이었다.
CloudFront origin 교체 및 최종 검증
원인이 확정되었으므로 CloudFront distribution을 직접 업데이트했다.
aws cloudfront get-distribution-config --profile myapp-test --id EXXXXXXXXXXXXX
aws cloudfront update-distribution --profile myapp-test --id EXXXXXXXXXXXXX --if-match <ETag> --distribution-config file://...
aws cloudfront wait distribution-deployed --profile myapp-test --id EXXXXXXXXXXXXX# 실행 결과(요약)
Distribution EXXXXXXXXXXXXX Status: InProgress -> Deployed
DefaultOrigin:
k8s-myapp-worker-85fc07725b-260927434.ap-northeast-2.elb.amazonaws.com (신규 ALB)배포 상태가 Deployed로 돌아온 뒤, 실제 트래픽과 헬스체크를 재검증했다.
curl https://test.example.com
curl https://api.test.example.com/readyz
curl https://admin-api.test.example.com/readyz
curl https://cms-api.test.example.com/readyz# 실행 결과(요약)
test.example.com -> HTTP 200
api.test.example.com/readyz -> HTTP 200
admin-api.test.example.com/readyz -> HTTP 200
cms-api.test.example.com/readyz -> HTTP 200test.example.com는 200, API /readyz와 /livez도 200으로 확인됐다.
finalizer 강제 삭제 부작용(잔여 리소스) 검증
이제 서비스는 복구되었고 후속 처리를 할 시간이다.
강제 삭제 이후에는 정리 루틴 우회로 인한 orphan 가능성을 염두에 두어야 하므로, myapp-test 계정의 ALB/TG/SG/ENI를 교차 점검했다.
aws elbv2 describe-load-balancers --profile myapp-test --region ap-northeast-2 \
--query "LoadBalancers[?contains(LoadBalancerName, 'k8s-myapp-')].[LoadBalancerName,DNSName,State.Code]" --output table
aws elbv2 describe-target-groups --profile myapp-test --region ap-northeast-2 \
--query "TargetGroups[?contains(TargetGroupName, 'k8s-myapp-')].[TargetGroupName,LoadBalancerArns[0],Port]" --output table
aws ec2 describe-security-groups --profile myapp-test --region ap-northeast-2 \
--filters Name=group-name,Values='k8s-myapp-*','k8s-traffic-testeks-*' \
--query "SecurityGroups[*].[GroupId,GroupName]" --output table
aws ec2 describe-network-interfaces --profile myapp-test --region ap-northeast-2 \
--filters Name=description,Values='ELB app/k8s-myapp-*' \
--query "NetworkInterfaces[*].[NetworkInterfaceId,Status,Description]" --output table# 실행 결과(요약)
- k8s-myapp-* ALB: 7개 모두 active
- k8s-myapp-* Target Group: 모두 현재 ALB에 연결됨
- k8s-myapp-* / k8s-traffic-testeks-* SG: 전부 LB attach 상태
- ELB ENI: 모두 in-use구 ALB DNS가 남아 있는지 추가 확인했다.
nslookup k8s-myapp-worker-01f28dfae5-1559597038.ap-northeast-2.elb.amazonaws.com
nslookup k8s-myapp-api-794921f445-1384615053.ap-northeast-2.elb.amazonaws.com
... (구 suffix들 동일 방식 확인)# 실행 결과(요약)
구 ALB DNS 6개 전부 NXDOMAIN이 결과를 기준으로, 현재 시점에서는 finalizer 강제 삭제로 인한 ALB/SG/ENI orphan 증거는 확인되지 않았다. 따로 조치할 사항은 없었다.
이번 대응에서의 핵심 판단
이번 트러블슈팅의 핵심 판단은 네 가지였다.
첫째, Terminating을 단순 지연으로 보지 않고 “Ingress finalizer 블로킹”으로 재정의해 조사 범위를 즉시 좁혔다.
둘째, 네임스페이스 이슈와 도메인 502를 하나로 뭉개지 않고, Kubernetes 삭제 경로와 사용자 트래픽 경로(Route53 → CloudFront → ALB)를 분리해서 병렬로 확인했다.
셋째, 원인 흐름 확정 이후에는 장기 분석보다 복구를 우선해 finalizer 강제 제거와 CloudFront origin 교체를 실행했다.
넷째, 강제 조치로 끝내지 않고 orphan 가능성을 독립된 검증 단계로 두어, ALB/TG/SG/ENI 상태와 구 ALB DNS NXDOMAIN까지 확인해 후속 리스크를 닫았다.
이번 대응의 포인트는 “원인 분석의 깊이”와 “서비스 복구 속도”의 균형이었다. 원인 추적은 의존성으로 SG 삭제 실패까지 확정하고, 사용자 영향이 큰 구간은 우회 복구로 먼저 정상화한 다음, 사후 검증으로 안전성을 확인하는 순서로 진행했다.
교훈
- Terminating이 수 분 이상 지속되면 느린 삭제가 아니라 finalizer 블로킹이다.
kubectl get ns -o yaml로 남은 finalizer와 블로킹 리소스를 먼저 확인해라. - namespace 삭제는 blast radius가 크다. 한 번의 조작으로 다수 애플리케이션과 외부 리소스에 동시 영향을 준다. RBAC와 admission policy로 기본 차단하고, 예외적인 삭제만 승인 절차를 거쳐라.
- finalizer를 강제 제거하기 전에 세 조건을 확인해라. “원인 확인 완료 + 복구 우선 + 사후 정리 계획”이 충족될 때만 제한적으로 선택해야 한다.
- ALB가 바뀌는 배포에서는 Route53만 업데이트하면 안 된다.
Route53 → CloudFront → ALB경로 전체를 함께 검증하고 반영해야 한다. CloudFront origin은 자동으로 갱신되지 않는다. - 강제 조치 후에는 orphan 리소스를 독립된 단계에서 검증해라. ALB/TG/SG/ENI 상태와 구 ALB DNS NXDOMAIN 확인으로 후속 리스크를 닫아야 한다.
결과
이번 이슈 범위의 결과는 세 가지로 요약된다.
첫째, namespace 삭제 블로킹이 해소되어 작업이 다시 가능해졌다.
둘째, 재배포 이후 깨진 DNS 경로를 정리해 test.example.com를 HTTP 200으로 복구했다.
셋째, API 헬스체크(/readyz, /livez) 정상 응답을 확인해 서비스 레벨의 동작을 검증했다.
복구 이후에도 강제 삭제 부작용을 점검해 ALB/SG/ENI orphan이 없다는 사실을 확인했다.
재발 방지
재발을 막으려면 먼저 Namespace 관리 주체를 하나로 고정해야 한다. 이번 사고의 출발점은 같은 namespace가 여러 경로에서 동시에 관리되며 삭제와 재생성이 충돌한 점이었다. 앱 매니페스트에서는 Namespace와 같은 클러스터 스코프 리소스를 제거하고, namespace 생성/수정/삭제는 전용 경로에서만 수행되도록 운영 규칙을 명확히 두는 편이 안전하다. 이렇게 해야 변경 책임과 권한 경계가 분명해지고 Argo 재동기화 충돌 가능성이 줄어든다.
또한, 운영 namespace 삭제는 근본적으로 보호되는 작업이어야 한다. 심지어 관리자라 하더라도 namespace를 제거할 일은 많지 않다. namespace 삭제는 한 번의 조작으로 다수 애플리케이션과 외부 리소스에 동시 영향을 주기 때문에 blast radius가 크다. 따라서 RBAC와 admission policy로 기본 차단하고, 예외적인 삭제만 승인 절차를 거쳐 수행하도록 설계해야 동일 유형의 사고를 반복하지 않는다.
도메인 경로 정합성도 배포 단위에서 강제할 필요가 있다. 이번 502는 Route53 자체가 아니라 CloudFront origin이 구 ALB를 바라보는 불일치에서 발생했다. Route53만 자동화하면 충분하지 않고, ALB가 바뀌는 배포에서는 Route53 → CloudFront → ALB 경로를 함께 검증하고 함께 반영해야 한다. ExternalDNS는 Route53 레코드 동기화 자동화에는 분명히 도움이 되지만, CloudFront distribution origin 변경까지 해결하지는 못한다. ExternalDNS는 Route53 자동화 수단으로 검토하되, CloudFront origin 정합성은 별도의 IaC와 배포 검증 단계로 남겨 두는 구성이 현실적이다.
운영 절차 측면에서는 삭제 전 체크리스트를 표준화해야 한다. Argo 자동 동기화 상태, 삭제 대상 리소스의 finalizer 존재 여부, 외부 의존 리소스(ALB/SG), 도메인 경로(Route53/CloudFront)를 확인한 뒤 삭제를 진행하면, 이번처럼 하나의 조작이 예상치 못하게 여러 계층 문제로 번지는 상황을 크게 줄일 수 있다.