Skip to content

Commit e2ab612

Browse files
authored
fix: don't set Deployment replicas when an HPA is configured (#9594)
* fix: don't set Deployment replicas when an HPA is configured Envoy Gateway applies the proxy and rate limit Deployments with a server-side apply patch using ForceOwnership, so rendering spec.replicas made envoy-gateway the owner of that field. Every subsequent reconciliation then forced the replica count back to the statically configured value, overriding whatever the HPA had computed. Omit the replicas field from the rendered Deployment when an HPA is configured, so Envoy Gateway never claims ownership of spec.replicas and the HPA is free to scale the Deployment. This is the behavior already documented for both envoyHpa and rateLimitHpa, but it was never implemented. Use minReplicas to set a lower bound on the replica count instead. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> * docs: add release note Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> * test: regenerate helm CRD golden files Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com> --------- Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
1 parent f63003f commit e2ab612

15 files changed

Lines changed: 655 additions & 14 deletions

File tree

api/v1alpha1/envoyproxy_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ type EnvoyProxyKubernetesProvider struct {
528528
EnvoyService *KubernetesServiceSpec `json:"envoyService,omitempty"`
529529

530530
// EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment.
531+
// If the HPA is set, the Replicas field from EnvoyDeployment will be ignored, and the
532+
// number of replicas is solely managed by the HPA. Use MinReplicas to control the
533+
// lower bound of the replica count instead.
531534
//
532535
// +optional
533536
EnvoyHpa *KubernetesHorizontalPodAutoscalerSpec `json:"envoyHpa,omitempty"`

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyproxies.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10388,8 +10388,11 @@ spec:
1038810388
type: object
1038910389
type: object
1039010390
envoyHpa:
10391-
description: EnvoyHpa defines the Horizontal Pod Autoscaler
10392-
settings for Envoy Proxy Deployment.
10391+
description: |-
10392+
EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment.
10393+
If the HPA is set, the Replicas field from EnvoyDeployment will be ignored, and the
10394+
number of replicas is solely managed by the HPA. Use MinReplicas to control the
10395+
lower bound of the replica count instead.
1039310396
properties:
1039410397
behavior:
1039510398
description: |-

charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10387,8 +10387,11 @@ spec:
1038710387
type: object
1038810388
type: object
1038910389
envoyHpa:
10390-
description: EnvoyHpa defines the Horizontal Pod Autoscaler
10391-
settings for Envoy Proxy Deployment.
10390+
description: |-
10391+
EnvoyHpa defines the Horizontal Pod Autoscaler settings for Envoy Proxy Deployment.
10392+
If the HPA is set, the Replicas field from EnvoyDeployment will be ignored, and the
10393+
number of replicas is solely managed by the HPA. Use MinReplicas to control the
10394+
lower bound of the replica count instead.
1039210395
properties:
1039310396
behavior:
1039410397
description: |-

internal/infrastructure/kubernetes/proxy/resource_provider.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,15 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) {
393393
return nil, err
394394
}
395395

396+
// When an HPA is configured, the replica count is owned by the HPA, so the replicas
397+
// field is left unset here. Since the field is omitted from the server-side apply
398+
// patch, Envoy Gateway doesn't take ownership of it and won't revert the replica
399+
// count computed by the HPA on subsequent reconciliations.
400+
replicas := deploymentConfig.Replicas
401+
if provider.GetEnvoyProxyKubeProvider().EnvoyHpa != nil {
402+
replicas = nil
403+
}
404+
396405
deployment := &appsv1.Deployment{
397406
TypeMeta: metav1.TypeMeta{
398407
Kind: "Deployment",
@@ -405,7 +414,7 @@ func (r *ResourceRender) Deployment() (*appsv1.Deployment, error) {
405414
OwnerReferences: r.ownerReferences(),
406415
},
407416
Spec: appsv1.DeploymentSpec{
408-
Replicas: deploymentConfig.Replicas,
417+
Replicas: replicas,
409418
Strategy: *deploymentConfig.Strategy,
410419
// Deployment's selector is immutable.
411420
Selector: r.stableSelector(),

internal/infrastructure/kubernetes/proxy/resource_provider_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ func TestDeployment(t *testing.T) {
200200
caseName string
201201
infra *ir.Infra
202202
deploy *egv1a1.KubernetesDeploymentSpec
203+
hpa *egv1a1.KubernetesHorizontalPodAutoscalerSpec
203204
shutdown *egv1a1.ShutdownConfig
204205
shutdownManager *egv1a1.ShutdownManager
205206
proxyLogging map[egv1a1.ProxyLogComponent]egv1a1.LogLevel
@@ -674,6 +675,21 @@ func TestDeployment(t *testing.T) {
674675
Name: new("custom-deployment-name"),
675676
},
676677
},
678+
{
679+
// The replicas field must not be rendered when an HPA is configured, so that
680+
// Envoy Gateway doesn't own spec.replicas and revert the replica count
681+
// computed by the HPA.
682+
caseName: "with-hpa",
683+
infra: newTestInfra(),
684+
deploy: &egv1a1.KubernetesDeploymentSpec{
685+
Replicas: new(int32(2)),
686+
Strategy: egv1a1.DefaultKubernetesDeploymentStrategy(),
687+
},
688+
hpa: &egv1a1.KubernetesHorizontalPodAutoscalerSpec{
689+
MinReplicas: new(int32(3)),
690+
MaxReplicas: new(int32(10)),
691+
},
692+
},
677693
{
678694
caseName: "gateway-namespace-mode",
679695
infra: newTestInfraWithNamespacedName(types.NamespacedName{Namespace: "ns1", Name: "gateway-1"}),
@@ -693,6 +709,9 @@ func TestDeployment(t *testing.T) {
693709
if tc.deploy != nil {
694710
kube.EnvoyDeployment = tc.deploy
695711
}
712+
if tc.hpa != nil {
713+
kube.EnvoyHpa = tc.hpa
714+
}
696715

697716
replace := egv1a1.BootstrapTypeReplace
698717
if tc.bootstrap != "" {

0 commit comments

Comments
 (0)