forked from LaconicNetwork/kompose
Merge pull request #1847 from sosan/feature-1631-add-hpa
Feature 1635 - added labels to generate HPA
This commit is contained in:
commit
cdcb0e7f61
@ -214,6 +214,10 @@ The currently supported options are:
|
|||||||
| kompose.init.containers.name | kubernetes init container name |
|
| kompose.init.containers.name | kubernetes init container name |
|
||||||
| kompose.init.containers.image | kubernetes init container image |
|
| kompose.init.containers.image | kubernetes init container image |
|
||||||
| kompose.init.containers.command | kubernetes init container commands |
|
| kompose.init.containers.command | kubernetes init container commands |
|
||||||
|
| kompose.hpa.replicas.min | defines Horizontal Pod Autoscaler minimum number of pod replicas |
|
||||||
|
| kompose.hpa.replicas.max | defines Horizontal Pod Autoscaler maximum number of pod replicas |
|
||||||
|
| kompose.hpa.cpu | defines Horizontal Pod Autoscaler cpu utilization trigger |
|
||||||
|
| kompose.hpa.memory | defines Horizontal Pod Autoscaler memory utilization trigger |
|
||||||
|
|
||||||
**Note**: `kompose.service.type` label should be defined with `ports` only (except for headless service), otherwise `kompose` will fail.
|
**Note**: `kompose.service.type` label should be defined with `ports` only (except for headless service), otherwise `kompose` will fail.
|
||||||
|
|
||||||
@ -512,6 +516,55 @@ services:
|
|||||||
kompose.init.containers.image: perl
|
kompose.init.containers.image: perl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
- `kompose.hpa.replicas.min` defines the floor for the number of replicas that the HPA can scale down to during a scaling event. Default value is set to 1. This means that, regardless of the load on the system, the HPA will always maintain at least one replica. More info: [HPA Min Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
pgadmin:
|
||||||
|
image: postgres
|
||||||
|
labels:
|
||||||
|
kompose.hpa.replicas.min: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
- `kompose.hpa.replicas.max` defines the upper limit for the number of replicas that the HPA can create during a scaling event. Default value is set to 3. This default value serves as a safeguard, providing a conservative starting point for your HPA configuration. More info: [HPA Max Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
pgadmin:
|
||||||
|
image: postgres
|
||||||
|
labels:
|
||||||
|
kompose.hpa.replicas.max: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
- `kompose.hpa.cpu` defines % cpu utilization that triggers to scale the number of pods. It is represented as a percentage of a resource. Default value is set to 50. This default value serves as a safeguard, providing a conservative starting point for your HPA configuration. More info: [HPA CPU Utilization](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
pgadmin:
|
||||||
|
image: postgres
|
||||||
|
labels:
|
||||||
|
kompose.hpa.cpu: 50
|
||||||
|
```
|
||||||
|
|
||||||
|
- `kompose.hpa.memory` defines memory utilization that triggers to scale the number of pods. It is represented as a percentage of a resource. Default value is set to 70. This default value serves as a safeguard, providing a conservative starting point for your HPA configuration. More info: [HPA Memory Utilization](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
pgadmin:
|
||||||
|
image: postgres
|
||||||
|
labels:
|
||||||
|
kompose.hpa.memory: 50
|
||||||
|
```
|
||||||
|
|
||||||
## Restart
|
## Restart
|
||||||
|
|
||||||
If you want to create normal pods without controller you can use `restart` construct of compose to define that. Follow table below to see what happens on the `restart` value.
|
If you want to create normal pods without controller you can use `restart` construct of compose to define that. Follow table below to see what happens on the `restart` value.
|
||||||
|
|||||||
@ -93,6 +93,14 @@ const (
|
|||||||
LabelInitContainerImage = "kompose.init.containers.image"
|
LabelInitContainerImage = "kompose.init.containers.image"
|
||||||
// LabelInitContainerCommand defines commands
|
// LabelInitContainerCommand defines commands
|
||||||
LabelInitContainerCommand = "kompose.init.containers.command"
|
LabelInitContainerCommand = "kompose.init.containers.command"
|
||||||
|
// LabelHpaMinReplicas defines min pod replicas
|
||||||
|
LabelHpaMinReplicas = "kompose.hpa.replicas.min"
|
||||||
|
// LabelHpaMaxReplicas defines max pod replicas
|
||||||
|
LabelHpaMaxReplicas = "kompose.hpa.replicas.max"
|
||||||
|
// LabelHpaCpu defines scaling decisions based on CPU utilization
|
||||||
|
LabelHpaCPU = "kompose.hpa.cpu"
|
||||||
|
// LabelHpaMemory defines scaling decisions based on memory utilization
|
||||||
|
LabelHpaMemory = "kompose.hpa.memory"
|
||||||
)
|
)
|
||||||
|
|
||||||
// load environment variables from compose file
|
// load environment variables from compose file
|
||||||
|
|||||||
@ -41,6 +41,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
hpa "k8s.io/api/autoscaling/v2beta2"
|
||||||
api "k8s.io/api/core/v1"
|
api "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -48,6 +49,29 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Default values for Horizontal Pod Autoscaler (HPA)
|
||||||
|
const (
|
||||||
|
DefaultMinReplicas = 1
|
||||||
|
DefaultMaxReplicas = 3
|
||||||
|
DefaultCPUUtilization = 50
|
||||||
|
DefaultMemoryUtilization = 70
|
||||||
|
)
|
||||||
|
|
||||||
|
// LabelKeys are the keys for HPA related labels in the service
|
||||||
|
var LabelKeys = []string{
|
||||||
|
compose.LabelHpaCPU,
|
||||||
|
compose.LabelHpaMemory,
|
||||||
|
compose.LabelHpaMinReplicas,
|
||||||
|
compose.LabelHpaMaxReplicas,
|
||||||
|
}
|
||||||
|
|
||||||
|
type HpaValues struct {
|
||||||
|
MinReplicas int32
|
||||||
|
MaxReplicas int32
|
||||||
|
CPUtilization int32
|
||||||
|
MemoryUtilization int32
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate Helm Chart configuration
|
* Generate Helm Chart configuration
|
||||||
*/
|
*/
|
||||||
@ -1030,3 +1054,122 @@ func parseContainerCommandsFromStr(line string) []string {
|
|||||||
}
|
}
|
||||||
return commands
|
return commands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// searchHPAValues is useful to check if labels
|
||||||
|
// contains any labels related to Horizontal Pod Autoscaler
|
||||||
|
func searchHPAValues(labels map[string]string) bool {
|
||||||
|
for _, value := range LabelKeys {
|
||||||
|
if _, ok := labels[value]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// createHPAResources creates a HorizontalPodAutoscaler (HPA) resource
|
||||||
|
// It sets the number of replicas in the service to 0 because
|
||||||
|
// the number of replicas will be managed by the HPA
|
||||||
|
func createHPAResources(name string, service *kobject.ServiceConfig) hpa.HorizontalPodAutoscaler {
|
||||||
|
valuesHpa := getResourceHpaValues(service)
|
||||||
|
service.Replicas = 0
|
||||||
|
metrics := getHpaMetricSpec(valuesHpa)
|
||||||
|
scalerSpecs := hpa.HorizontalPodAutoscaler{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "HorizontalPodAutoscaler",
|
||||||
|
APIVersion: "autoscaling/v2",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Spec: hpa.HorizontalPodAutoscalerSpec{
|
||||||
|
ScaleTargetRef: hpa.CrossVersionObjectReference{
|
||||||
|
Kind: "Deployment",
|
||||||
|
Name: name,
|
||||||
|
APIVersion: "apps/v1",
|
||||||
|
},
|
||||||
|
MinReplicas: &valuesHpa.MinReplicas,
|
||||||
|
MaxReplicas: valuesHpa.MaxReplicas,
|
||||||
|
Metrics: metrics,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return scalerSpecs
|
||||||
|
}
|
||||||
|
|
||||||
|
// getResourceHpaValues retrieves the min/max replicas and CPU/memory utilization values
|
||||||
|
// control if maxReplicas is less than minReplicas
|
||||||
|
func getResourceHpaValues(service *kobject.ServiceConfig) HpaValues {
|
||||||
|
minReplicas := getHpaValue(service, compose.LabelHpaMinReplicas, DefaultMinReplicas)
|
||||||
|
maxReplicas := getHpaValue(service, compose.LabelHpaMaxReplicas, DefaultMaxReplicas)
|
||||||
|
|
||||||
|
if maxReplicas < minReplicas {
|
||||||
|
log.Warnf("maxReplicas %d is less than minReplicas %d. Using minReplicas value %d", maxReplicas, minReplicas, minReplicas)
|
||||||
|
maxReplicas = minReplicas
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuUtilization := validatePercentageMetric(service, compose.LabelHpaCPU, DefaultCPUUtilization)
|
||||||
|
memoryUtilization := validatePercentageMetric(service, compose.LabelHpaMemory, DefaultMemoryUtilization)
|
||||||
|
|
||||||
|
return HpaValues{
|
||||||
|
MinReplicas: minReplicas,
|
||||||
|
MaxReplicas: maxReplicas,
|
||||||
|
CPUtilization: cpuUtilization,
|
||||||
|
MemoryUtilization: memoryUtilization,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validatePercentageMetric validates the CPU or memory metrics value
|
||||||
|
// ensuring that it falls within the acceptable range [1, 100].
|
||||||
|
func validatePercentageMetric(service *kobject.ServiceConfig, metricLabel string, defaultValue int32) int32 {
|
||||||
|
metricValue := getHpaValue(service, metricLabel, defaultValue)
|
||||||
|
if metricValue > 100 || metricValue < 1 {
|
||||||
|
log.Warnf("Metric value %d is not within the acceptable range [1, 100]. Using default value %d", metricValue, defaultValue)
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return metricValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHpaValue convert the label value to integer
|
||||||
|
// If the label is not present or the conversion fails
|
||||||
|
// it returns the provided default value
|
||||||
|
func getHpaValue(service *kobject.ServiceConfig, label string, defaultValue int32) int32 {
|
||||||
|
valueFromLabel, err := strconv.Atoi(service.Labels[label])
|
||||||
|
if err != nil || valueFromLabel < 0 {
|
||||||
|
log.Warnf("Error converting label %s. Using default value %d", label, defaultValue)
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
return int32(valueFromLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHpaMetricSpec returns a list of metric specs for the HPA resource
|
||||||
|
// Target type is hardcoded to hpa.UtilizationMetricType
|
||||||
|
// Each MetricSpec specifies the type metric CPU/memory and average utilization value
|
||||||
|
// to trigger scaling
|
||||||
|
func getHpaMetricSpec(hpaValues HpaValues) []hpa.MetricSpec {
|
||||||
|
var metrics []hpa.MetricSpec
|
||||||
|
if hpaValues.CPUtilization > 0 {
|
||||||
|
metrics = append(metrics, hpa.MetricSpec{
|
||||||
|
Type: hpa.ResourceMetricSourceType,
|
||||||
|
Resource: &hpa.ResourceMetricSource{
|
||||||
|
Name: api.ResourceCPU,
|
||||||
|
Target: hpa.MetricTarget{
|
||||||
|
Type: hpa.UtilizationMetricType,
|
||||||
|
AverageUtilization: &hpaValues.CPUtilization,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if hpaValues.MemoryUtilization > 0 {
|
||||||
|
metrics = append(metrics, hpa.MetricSpec{
|
||||||
|
Type: hpa.ResourceMetricSourceType,
|
||||||
|
Resource: &hpa.ResourceMetricSource{
|
||||||
|
Name: api.ResourceMemory,
|
||||||
|
Target: hpa.MetricTarget{
|
||||||
|
Type: hpa.UtilizationMetricType,
|
||||||
|
AverageUtilization: &hpaValues.MemoryUtilization,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1654,6 +1654,10 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err = k.configHorizontalPodScaler(name, service, opt, &objects)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Error creating Kubernetes HPA")
|
||||||
|
}
|
||||||
allobjects = append(allobjects, objects...)
|
allobjects = append(allobjects, objects...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1718,3 +1722,16 @@ func (k *Kubernetes) UpdateController(obj runtime.Object, updateTemplate func(*a
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configHorizontalPodScaler create Hpa resource also append to the objects
|
||||||
|
// first checks if the service labels contain any HPA labels using the searchHPAValues
|
||||||
|
func (k *Kubernetes) configHorizontalPodScaler(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, objects *[]runtime.Object) (err error) {
|
||||||
|
found := searchHPAValues(service.Labels)
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hpa := createHPAResources(name, &service)
|
||||||
|
*objects = append(*objects, &hpa)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -344,3 +344,8 @@ convert::expect_success "$os_cmd" "$os_output" || exit 1
|
|||||||
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/initcontainer/compose.yaml convert --stdout --with-kompose-annotation=false"
|
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/initcontainer/compose.yaml convert --stdout --with-kompose-annotation=false"
|
||||||
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/initcontainer/output-k8s.yaml"
|
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/initcontainer/output-k8s.yaml"
|
||||||
convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
|
convert::expect_success_and_warning "$k8s_cmd" "$k8s_output" || exit 1
|
||||||
|
|
||||||
|
# Test HPA
|
||||||
|
k8s_cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/hpa/compose.yaml convert --stdout --with-kompose-annotation=false"
|
||||||
|
k8s_output="$KOMPOSE_ROOT/script/test/fixtures/hpa/output-k8s.yaml"
|
||||||
|
convert::expect_success "$k8s_cmd" "$k8s_output" || exit 1
|
||||||
|
|||||||
10
script/test/fixtures/hpa/compose.yaml
vendored
Normal file
10
script/test/fixtures/hpa/compose.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx
|
||||||
|
labels:
|
||||||
|
kompose.hpa.cpu: 50
|
||||||
|
kompose.hpa.memory: 70
|
||||||
|
kompose.hpa.replicas.min: 1
|
||||||
|
kompose.hpa.replicas.max: 10
|
||||||
|
|
||||||
49
script/test/fixtures/hpa/output-k8s.yaml
vendored
Normal file
49
script/test/fixtures/hpa/output-k8s.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
io.kompose.service: web
|
||||||
|
name: web
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
io.kompose.service: web
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
io.kompose.network/hpa-default: "true"
|
||||||
|
io.kompose.service: web
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: web
|
||||||
|
restartPolicy: Always
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: web
|
||||||
|
spec:
|
||||||
|
maxReplicas: 10
|
||||||
|
metrics:
|
||||||
|
- resource:
|
||||||
|
name: cpu
|
||||||
|
target:
|
||||||
|
averageUtilization: 50
|
||||||
|
type: Utilization
|
||||||
|
type: Resource
|
||||||
|
- resource:
|
||||||
|
name: memory
|
||||||
|
target:
|
||||||
|
averageUtilization: 70
|
||||||
|
type: Utilization
|
||||||
|
type: Resource
|
||||||
|
minReplicas: 1
|
||||||
|
scaleTargetRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: web
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user