Intro
argo-rollout은 kubernetes nativ의 deployment 기능에 넘어서, blue-green 및 canary 배포를 진행하며 워크로드를 안전하게 배포할 수 있게 도와준다. 그런데 canary 배포만하면 매트릭이 안정적인데 HPA가 증가하는 문제가 있었다.
지표가 잘못 보고된걸까? HPA가 잘못된걸까? argo 문제일까?
cpu/memory 지표는 cAdvisor에서 cgroups에서 매트릭을 참조하여 가져오는것이기 떄문에, 보통 잘못될 일이 없다. 잘못된거면, 커널 이슈를 찾아봐야겠지..?
argo-rollout
argo-rollout은 deployment와 비슷하게 template에 Pod설정과 replicas를 기입한다.
여여기에 추가로 strategy 으로 배포전략을 추가할 수 있다.
kubernetes native와 비교하면 아래 이미지와 같이, rollout CRD가 canary 배포시 replicaset을 2개를 소유한다는 점이다. 이 때, stable/canary replicaset의 label은 동일하다.
rollout에 stable/canaryService 를 연결하면 pod-hash-template을 argo-rollout에서 적절하게 업데이트 한다.

여기에 argo-rollout은 istio, AWS ALB 등 service mesh나 traffic management 도구를 명시적으로 연결할 수 있다.
문제 상황
canary 배포중, HPA에 의해 Pod가 스케일링 되지만 리소스 사용량은 이상이 없어보인다.
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
rollout Rollout/rollouts-demo memory: 30%/54% 4 10 5 6h9m
# HPA event
Normal SuccessfulRescale 17s (x4 over 6h4m) horizontal-pod-autoscaler New size: 5; reason: memory resource utilization (percentage of request) above target
promote를 계속 진행하게되면, pod수가 계속해서 늘어나는 것을 확인할 수 있었다.
troubleshooting
가장 먼저, HPA가 desiredCount를 어떻게 계산하는지 확인했다. [1]
여기서, HPA 매트릭은 작게 보고하고 있으므로 currentReplicas가 잘못보고된 것으로 예상했다.
HPA[2] 코드를 보니, selector만을 가지고 Pod를 선택하는 것을 볼 수 있었다.
validateAndParseSelector 으로, selector를 parse하고 computeReplicasForMetric 으로 넘겨서 replicas를 계산한다.
func (a *HorizontalController) computeReplicasForMetrics(ctx context.Context, hpa *autoscalingv2.HorizontalPodAutoscaler, scale *autoscalingv1.Scale,
metricSpecs []autoscalingv2.MetricSpec) (replicas int32, metric string, statuses []autoscalingv2.MetricStatus, timestamp time.Time, err error) {
selector, err := a.validateAndParseSelector(hpa, scale.Status.Selector)
...(중량)
replicaCountProposal, metricNameProposal, timestampProposal, condition, err := a.computeReplicasForMetric(ctx, hpa, metricSpec, specReplicas, statusReplicas, selector, &statuses[i])scale 값은 scaleForResourceMappings함수에서 가져오는데 “/scale” 엔드포인를 이용한다.
다음 argo-rollout 문서롤 보면 v0.3.0부터 /scale을 노출한다고 되어있다.
Since version v0.3.0, Argo Rollouts exposes its
/scalesubresource in the same way as a standard KubernetesDeploymentdoes. This allows the Horizontal Pod Autoscaler (HPA) to discover the Rollout resource. The HPA accesses the /scale subresource to get the current number of replicas from thestatus.replicasfield of the Rollout.
func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShared *autoscalingv2.HorizontalPodAutoscaler, key string) (retErr error) {
...(중략)
scale, targetGR, err := a.scaleForResourceMappings(ctx, hpa.Namespace, hpa.Spec.ScaleTargetRef.Name, mappings)결국 rollout의 selector를 가지고 pod를 다시 list해온다.
scale에 있는 current replicas를 쓰면 안되나..?
# rollout selector
selector:
matchLabels:
app: rollouts-demo
# HPA scaleTargetRef
scaleTargetRef:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
name: rollouts-demo
# Stable rs
Labels: app=rollouts-demo
istio-injection=enabled
rollouts-pod-template-hash=97c5784fd
sidecar.istio.io/inject=true
# canary rs
Labels: app=rollouts-demo
istio-injection=enabled
rollouts-pod-template-hash=546697457c
sidecar.istio.io/inject=true
따라서 배포시 위와 같이, rollout의 selector는 app=rollouts-demo만 있기 때문에, stable/canary pod를 전부 가져오게 된다. 그래서 HPA는 currentReplicas를 비정상적으로 크게 보고한다.
아래와 같은 시나리오로 테스트했고, canary/stable 갯수를 더해서 current가 증가하면 HPA도 증가하게 보고되는 것을 확인할 수 있었다.
Scenario 1:
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
rollout Rollout/rollouts-demo memory: 28%/54% 4 10 4 89m
# Pod count
canary pods: 3
stable pods: 4
--> HPA did not increase pod count
Calculation: ⌈(3 + 4) × (28 / 54)⌉ = ⌈3.62⌉ = 4
Scenario 2:
# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
rollout Rollout/rollouts-demo memory: 28%/54% 4 10 4 89m
# Pod count
canary pods: 4
stable pods: 4
--> HPA increased pod count to 5
Calculation: ⌈(4 + 4) × (28 / 54)⌉ = ⌈4.14⌉ = 5
좀 더 찾아보기
이러한 문제는 argo-rollout의 canary를 사용하는 경우뿐만 아니라 HPA 사용할 때 매우 빈번하게 발생할 것 같아서 좀 더 찾아보았다.
역시나 다음 KEP 문서에서 hpa의 Pod selection logic을 강화하는 문서를 찾을 수 있었다. 1.35 release 예정인거 같았고, 좀 지연된 것으로 보인다. [+] https://github.com/kubernetes/enhancements/tree/master/keps/sig-autoscaling/5325-hpa-pod-selection-accuracy
해당 문서의 골자는 OwnerReference으로 SelectionStrategy를 설정하면, replicaset에 연결된 OwnerReference를 참조해서 가져오는 것으로 보인다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
# Existing fields...
SelectionStrategy: OwnerReference # Default: LabelSelectorargo rollout은 canary 배포시 결국 stable/canary 둘다 생성되어서 OwnerReference가 같아서 해당 문제가 해결 될 수 있는 것으로 보이지는 않는다.
추후 보아야 알겠지만 HPA에 current/desired count도 제대로 표시되게 바뀌게 되면, argo-rollout을 사용할 때 명확하게 오류를 인지할 수 있을 것이다.
그 외 workaround가 없을까 하여 설정을 찬찬히 살펴보니 dynamicStableScale 설정을 지원한다.
spec:
strategy:
canary:
dynamicStableScale: true이는 canary 배포시 argo-rollout의 replicaset의 desiredCount를 일관되게 유지시켜주는 설정이다.
기존에 stable이 4개면, canary도 4개로 배포되는데 위 설정을하면, stable+canary count를 기존에 4개 값으로 유지시켜준다.
[as-is] canary/stable -> 4/4
[to-be] canary/stable -> 2/2
참고자료
[1] https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details