Skip to content

Commit

Permalink
Add autoscale option to enable support for Horizontal Pod Autoscaling (
Browse files Browse the repository at this point in the history
…#746)

* add autoscale, minReplicas maxReplicas to collector types

Signed-off-by: Sergei Semenchuk <[email protected]>

* manage hpa with min/max replicas if autoscale is enablad

Signed-off-by: Sergei Semenchuk <[email protected]>

* fix linter

Signed-off-by: Sergei Semenchuk <[email protected]>

* use status if autoscale is true

Signed-off-by: Sergei Semenchuk <[email protected]>

* add validation for autoscale

Signed-off-by: Sergei Semenchuk <[email protected]>

* remove minReplicas

Signed-off-by: Sergei Semenchuk <[email protected]>

* remove autoscale parameter, trigger HPA if maxReplicas is not nil

Signed-off-by: Sergei Semenchuk <[email protected]>

* use collector replicas for minReplicas in HPA if HPA status is currently
lower

Signed-off-by: Sergei Semenchuk <[email protected]>

* remove nil check

Signed-off-by: Sergei Semenchuk <[email protected]>
  • Loading branch information
binjip978 authored Mar 8, 2022
1 parent f7cf694 commit cd4fa9b
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 0 deletions.
4 changes: 4 additions & 0 deletions apis/v1alpha1/opentelemetrycollector_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ type OpenTelemetryCollectorSpec struct {
// +optional
Replicas *int32 `json:"replicas,omitempty"`

// MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
// +optional
MaxReplicas *int32 `json:"maxReplicas,omitempty"`

// ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)
// +optional
ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"`
Expand Down
11 changes: 11 additions & 0 deletions apis/v1alpha1/opentelemetrycollector_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,16 @@ func (r *OpenTelemetryCollector) validateCRDSpec() error {
}
}

// validate autoscale with horizontal pod autoscaler
if r.Spec.MaxReplicas != nil {
if *r.Spec.MaxReplicas < int32(1) {
return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, maxReplicas should be defined and more than one")
}

if r.Spec.Replicas != nil && *r.Spec.Replicas > *r.Spec.MaxReplicas {
return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, replicas must not be greater than maxReplicas")
}
}

return nil
}
5 changes: 5 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ spec:
- patch
- update
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- coordination.k8s.io
resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ spec:
description: ImagePullPolicy indicates the pull policy to be used
for retrieving the container image (Always, Never, IfNotPresent)
type: string
maxReplicas:
description: MaxReplicas sets an upper bound to the autoscaling feature.
If MaxReplicas is set autoscaling is enabled.
format: int32
type: integer
mode:
description: Mode represents how the collector should be deployed
(deployment, daemonset, statefulset or sidecar)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ spec:
description: ImagePullPolicy indicates the pull policy to be used
for retrieving the container image (Always, Never, IfNotPresent)
type: string
maxReplicas:
description: MaxReplicas sets an upper bound to the autoscaling feature.
If MaxReplicas is set autoscaling is enabled.
format: int32
type: integer
mode:
description: Mode represents how the collector should be deployed
(deployment, daemonset, statefulset or sidecar)
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ rules:
- patch
- update
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- coordination.k8s.io
resources:
Expand Down
7 changes: 7 additions & 0 deletions controllers/opentelemetrycollector_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/go-logr/logr"
appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
Expand Down Expand Up @@ -84,6 +85,11 @@ func NewReconciler(p Params) *OpenTelemetryCollectorReconciler {
reconcile.Deployments,
true,
},
{
"horizontal pod autoscalers",
reconcile.HorizontalPodAutoscalers,
true,
},
{
"daemon sets",
reconcile.DaemonSets,
Expand Down Expand Up @@ -173,6 +179,7 @@ func (r *OpenTelemetryCollectorReconciler) SetupWithManager(mgr ctrl.Manager) er
Owns(&corev1.ServiceAccount{}).
Owns(&corev1.Service{}).
Owns(&appsv1.Deployment{}).
Owns(&autoscalingv1.HorizontalPodAutoscaler{}).
Owns(&appsv1.DaemonSet{}).
Owns(&appsv1.StatefulSet{}).
Complete(r)
Expand Down
9 changes: 9 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,15 @@ OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector.
ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>maxReplicas</b></td>
<td>integer</td>
<td>
MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.<br/>
<br/>
<i>Format</i>: int32<br/>
</td>
<td>false</td>
</tr><tr>
<td><b>mode</b></td>
<td>enum</td>
Expand Down
54 changes: 54 additions & 0 deletions pkg/collector/horizontalpodautoscaler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector

import (
"github.com/go-logr/logr"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
"github.com/open-telemetry/opentelemetry-operator/internal/config"
"github.com/open-telemetry/opentelemetry-operator/pkg/naming"
)

const defaultCPUTarget int32 = 90

func HorizontalPodAutoscaler(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) autoscalingv1.HorizontalPodAutoscaler {
labels := Labels(otelcol)
labels["app.kubernetes.io/name"] = naming.Collector(otelcol)

annotations := Annotations(otelcol)
cpuTarget := defaultCPUTarget

return autoscalingv1.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{
Name: naming.Collector(otelcol),
Namespace: otelcol.Namespace,
Labels: labels,
Annotations: annotations,
},
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Name: naming.Collector(otelcol),
},
MinReplicas: otelcol.Spec.Replicas,
MaxReplicas: *otelcol.Spec.MaxReplicas,
TargetCPUUtilizationPercentage: &cpuTarget,
},
}
}
52 changes: 52 additions & 0 deletions pkg/collector/horizontalpodautoscaler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector_test

import (
"testing"

"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
"github.com/open-telemetry/opentelemetry-operator/internal/config"
. "github.com/open-telemetry/opentelemetry-operator/pkg/collector"
)

func TestHPA(t *testing.T) {
// prepare
var minReplicas int32 = 3
var maxReplicas int32 = 5

otelcol := v1alpha1.OpenTelemetryCollector{
ObjectMeta: metav1.ObjectMeta{
Name: "my-instance",
},
Spec: v1alpha1.OpenTelemetryCollectorSpec{
Replicas: &minReplicas,
MaxReplicas: &maxReplicas,
},
}

cfg := config.New()
hpa := HorizontalPodAutoscaler(cfg, logger, otelcol)

// verify
assert.Equal(t, "my-instance-collector", hpa.Name)
assert.Equal(t, "my-instance-collector", hpa.Labels["app.kubernetes.io/name"])
assert.Equal(t, int32(3), *hpa.Spec.MinReplicas)
assert.Equal(t, int32(5), hpa.Spec.MaxReplicas)
assert.Equal(t, int32(90), *hpa.Spec.TargetCPUUtilizationPercentage)
}
11 changes: 11 additions & 0 deletions pkg/collector/reconcile/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ func expectedDeployments(ctx context.Context, params Params, expected []appsv1.D
updated.ObjectMeta.Labels[k] = v
}

// if autoscale is enabled, use replicas from current Status
if params.Instance.Spec.MaxReplicas != nil {
currentReplicas := existing.Status.Replicas
// if replicas (minReplicas from HPA perspective) is bigger than
// current status use it.
if *params.Instance.Spec.Replicas > currentReplicas {
currentReplicas = *params.Instance.Spec.Replicas
}
updated.Spec.Replicas = &currentReplicas
}

patch := client.MergeFrom(existing)

if err := params.Client.Patch(ctx, updated, patch); err != nil {
Expand Down
Loading

0 comments on commit cd4fa9b

Please sign in to comment.