Skip to content

Commit 03a4b63

Browse files
authored
Add autoscale option to enable support for Horizontal Pod Autoscaling (open-telemetry#746)
* add autoscale, minReplicas maxReplicas to collector types Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * manage hpa with min/max replicas if autoscale is enablad Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * fix linter Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * use status if autoscale is true Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * add validation for autoscale Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * remove minReplicas Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * remove autoscale parameter, trigger HPA if maxReplicas is not nil Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * use collector replicas for minReplicas in HPA if HPA status is currently lower Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com> * remove nil check Signed-off-by: Sergei Semenchuk <pdp.eleven11@gmail.com>
1 parent c5ed219 commit 03a4b63

14 files changed

+449
-0
lines changed

apis/v1alpha1/opentelemetrycollector_types.go

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ type OpenTelemetryCollectorSpec struct {
3737
// +optional
3838
Replicas *int32 `json:"replicas,omitempty"`
3939

40+
// MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
41+
// +optional
42+
MaxReplicas *int32 `json:"maxReplicas,omitempty"`
43+
4044
// ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)
4145
// +optional
4246
ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"`

apis/v1alpha1/opentelemetrycollector_webhook.go

+11
Original file line numberDiff line numberDiff line change
@@ -109,5 +109,16 @@ func (r *OpenTelemetryCollector) validateCRDSpec() error {
109109
}
110110
}
111111

112+
// validate autoscale with horizontal pod autoscaler
113+
if r.Spec.MaxReplicas != nil {
114+
if *r.Spec.MaxReplicas < int32(1) {
115+
return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, maxReplicas should be defined and more than one")
116+
}
117+
118+
if r.Spec.Replicas != nil && *r.Spec.Replicas > *r.Spec.MaxReplicas {
119+
return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, replicas must not be greater than maxReplicas")
120+
}
121+
}
122+
112123
return nil
113124
}

apis/v1alpha1/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,18 @@ spec:
188188
- patch
189189
- update
190190
- watch
191+
- apiGroups:
192+
- autoscaling
193+
resources:
194+
- horizontalpodautoscalers
195+
verbs:
196+
- create
197+
- delete
198+
- get
199+
- list
200+
- patch
201+
- update
202+
- watch
191203
- apiGroups:
192204
- coordination.k8s.io
193205
resources:

bundle/manifests/opentelemetry.io_opentelemetrycollectors.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ spec:
218218
description: ImagePullPolicy indicates the pull policy to be used
219219
for retrieving the container image (Always, Never, IfNotPresent)
220220
type: string
221+
maxReplicas:
222+
description: MaxReplicas sets an upper bound to the autoscaling feature.
223+
If MaxReplicas is set autoscaling is enabled.
224+
format: int32
225+
type: integer
221226
mode:
222227
description: Mode represents how the collector should be deployed
223228
(deployment, daemonset, statefulset or sidecar)

config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ spec:
216216
description: ImagePullPolicy indicates the pull policy to be used
217217
for retrieving the container image (Always, Never, IfNotPresent)
218218
type: string
219+
maxReplicas:
220+
description: MaxReplicas sets an upper bound to the autoscaling feature.
221+
If MaxReplicas is set autoscaling is enabled.
222+
format: int32
223+
type: integer
219224
mode:
220225
description: Mode represents how the collector should be deployed
221226
(deployment, daemonset, statefulset or sidecar)

config/rbac/role.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ rules:
9999
- patch
100100
- update
101101
- watch
102+
- apiGroups:
103+
- autoscaling
104+
resources:
105+
- horizontalpodautoscalers
106+
verbs:
107+
- create
108+
- delete
109+
- get
110+
- list
111+
- patch
112+
- update
113+
- watch
102114
- apiGroups:
103115
- coordination.k8s.io
104116
resources:

controllers/opentelemetrycollector_controller.go

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/go-logr/logr"
2323
appsv1 "k8s.io/api/apps/v1"
24+
autoscalingv1 "k8s.io/api/autoscaling/v1"
2425
corev1 "k8s.io/api/core/v1"
2526
apierrors "k8s.io/apimachinery/pkg/api/errors"
2627
"k8s.io/apimachinery/pkg/runtime"
@@ -84,6 +85,11 @@ func NewReconciler(p Params) *OpenTelemetryCollectorReconciler {
8485
reconcile.Deployments,
8586
true,
8687
},
88+
{
89+
"horizontal pod autoscalers",
90+
reconcile.HorizontalPodAutoscalers,
91+
true,
92+
},
8793
{
8894
"daemon sets",
8995
reconcile.DaemonSets,
@@ -173,6 +179,7 @@ func (r *OpenTelemetryCollectorReconciler) SetupWithManager(mgr ctrl.Manager) er
173179
Owns(&corev1.ServiceAccount{}).
174180
Owns(&corev1.Service{}).
175181
Owns(&appsv1.Deployment{}).
182+
Owns(&autoscalingv1.HorizontalPodAutoscaler{}).
176183
Owns(&appsv1.DaemonSet{}).
177184
Owns(&appsv1.StatefulSet{}).
178185
Complete(r)

docs/api.md

+9
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,15 @@ OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector.
14461446
ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)<br/>
14471447
</td>
14481448
<td>false</td>
1449+
</tr><tr>
1450+
<td><b>maxReplicas</b></td>
1451+
<td>integer</td>
1452+
<td>
1453+
MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.<br/>
1454+
<br/>
1455+
<i>Format</i>: int32<br/>
1456+
</td>
1457+
<td>false</td>
14491458
</tr><tr>
14501459
<td><b>mode</b></td>
14511460
<td>enum</td>
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package collector
16+
17+
import (
18+
"github.com/go-logr/logr"
19+
autoscalingv1 "k8s.io/api/autoscaling/v1"
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
22+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
23+
"github.com/open-telemetry/opentelemetry-operator/internal/config"
24+
"github.com/open-telemetry/opentelemetry-operator/pkg/naming"
25+
)
26+
27+
const defaultCPUTarget int32 = 90
28+
29+
func HorizontalPodAutoscaler(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) autoscalingv1.HorizontalPodAutoscaler {
30+
labels := Labels(otelcol)
31+
labels["app.kubernetes.io/name"] = naming.Collector(otelcol)
32+
33+
annotations := Annotations(otelcol)
34+
cpuTarget := defaultCPUTarget
35+
36+
return autoscalingv1.HorizontalPodAutoscaler{
37+
ObjectMeta: metav1.ObjectMeta{
38+
Name: naming.Collector(otelcol),
39+
Namespace: otelcol.Namespace,
40+
Labels: labels,
41+
Annotations: annotations,
42+
},
43+
Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
44+
ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
45+
APIVersion: "apps/v1",
46+
Kind: "Deployment",
47+
Name: naming.Collector(otelcol),
48+
},
49+
MinReplicas: otelcol.Spec.Replicas,
50+
MaxReplicas: *otelcol.Spec.MaxReplicas,
51+
TargetCPUUtilizationPercentage: &cpuTarget,
52+
},
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package collector_test
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
23+
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
24+
"github.com/open-telemetry/opentelemetry-operator/internal/config"
25+
. "github.com/open-telemetry/opentelemetry-operator/pkg/collector"
26+
)
27+
28+
func TestHPA(t *testing.T) {
29+
// prepare
30+
var minReplicas int32 = 3
31+
var maxReplicas int32 = 5
32+
33+
otelcol := v1alpha1.OpenTelemetryCollector{
34+
ObjectMeta: metav1.ObjectMeta{
35+
Name: "my-instance",
36+
},
37+
Spec: v1alpha1.OpenTelemetryCollectorSpec{
38+
Replicas: &minReplicas,
39+
MaxReplicas: &maxReplicas,
40+
},
41+
}
42+
43+
cfg := config.New()
44+
hpa := HorizontalPodAutoscaler(cfg, logger, otelcol)
45+
46+
// verify
47+
assert.Equal(t, "my-instance-collector", hpa.Name)
48+
assert.Equal(t, "my-instance-collector", hpa.Labels["app.kubernetes.io/name"])
49+
assert.Equal(t, int32(3), *hpa.Spec.MinReplicas)
50+
assert.Equal(t, int32(5), hpa.Spec.MaxReplicas)
51+
assert.Equal(t, int32(90), *hpa.Spec.TargetCPUUtilizationPercentage)
52+
}

pkg/collector/reconcile/deployment.go

+11
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ func expectedDeployments(ctx context.Context, params Params, expected []appsv1.D
9898
updated.ObjectMeta.Labels[k] = v
9999
}
100100

101+
// if autoscale is enabled, use replicas from current Status
102+
if params.Instance.Spec.MaxReplicas != nil {
103+
currentReplicas := existing.Status.Replicas
104+
// if replicas (minReplicas from HPA perspective) is bigger than
105+
// current status use it.
106+
if *params.Instance.Spec.Replicas > currentReplicas {
107+
currentReplicas = *params.Instance.Spec.Replicas
108+
}
109+
updated.Spec.Replicas = &currentReplicas
110+
}
111+
101112
patch := client.MergeFrom(existing)
102113

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

0 commit comments

Comments
 (0)