Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 1635 - added labels to generate HPA #1847

Merged
merged 5 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,13 @@ The currently supported options are:
| kompose.cronjob.schedule | kubernetes cronjob schedule (for example: '1 * * * *') |
| kompose.cronjob.concurrency_policy | 'Forbid' / 'Allow' / 'Never' / '' |
| kompose.cronjob.backoff_limit | kubernetes cronjob backoff limit (for example: '6') |
| kompose.init.containers.name | kubernetes init container name |
| kompose.init.containers.image | kubernetes init container image |
| kompose.init.containers.command | kubernetes init container commands |
| kompose.init.containers.name | kubernetes init container name |
| kompose.init.containers.image | kubernetes init container image |
| 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.

Expand Down Expand Up @@ -512,6 +516,55 @@ services:
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

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.
Expand Down
8 changes: 8 additions & 0 deletions pkg/loader/compose/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ const (
LabelInitContainerImage = "kompose.init.containers.image"
// LabelInitContainerCommand defines commands
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
Expand Down
143 changes: 143 additions & 0 deletions pkg/transformer/kubernetes/k8sutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,37 @@ import (
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
appsv1 "k8s.io/api/apps/v1"
hpa "k8s.io/api/autoscaling/v2beta2"
api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"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
*/
Expand Down Expand Up @@ -1030,3 +1054,122 @@ func parseContainerCommandsFromStr(line string) []string {
}
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
}
Loading
Loading