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 1 commit
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
61 changes: 61 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ 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.hpa.minreplicas | defines Horizontal Pod Autoscaler min pod replicas |
| kompose.hpa.maxreplicas | defines Horizontal Pod Autoscaler max pod replicas |
sosan marked this conversation as resolved.
Show resolved Hide resolved
| 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 @@ -467,6 +471,63 @@ services:
labels:
kompose.volume.sub-path: pg-data
```

- `kompose.hpa.minreplicas` defines minimum replicas from Horizontal Pod Autoscaler. Default value 1 [HPA Min Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).

For example:

```yaml
version: '3.8'
sosan marked this conversation as resolved.
Show resolved Hide resolved

services:
pgadmin:
image: postgres
labels:
kompose.hpa.minreplicas: 1
```

- `kompose.hpa.maxreplicas` defines maximum replicas from Horizontal Pod Autoscaler. Default value 10 [HPA Max Replicas](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).

For example:

```yaml
version: '3.8'
sosan marked this conversation as resolved.
Show resolved Hide resolved

services:
pgadmin:
image: postgres
labels:
kompose.hpa.maxreplicas: 10
```

- `kompose.hpa.cpu` defines % cpu utilization trigger scale from Horizontal Pod Autoscaler. It is represented as a percentage of a resource. Default value: 50 [HPA CPU Utilization](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).

For example:

```yaml
version: '3.8'
sosan marked this conversation as resolved.
Show resolved Hide resolved

services:
pgadmin:
image: postgres
labels:
kompose.hpa.cpu: 50
```

- `kompose.hpa.memory` defines memory utilization trigger scale from Horizontal Pod Autoscaler. It is represented as a percentage of a resource. Default value: 70 [HPA Memory Utilization](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/#autoscaling-on-multiple-metrics-and-custom-metrics).

For example:

```yaml
version: '3.8'
sosan marked this conversation as resolved.
Show resolved Hide resolved

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 @@ -87,6 +87,14 @@ const (
LabelCronJobConcurrencyPolicy = "kompose.cronjob.concurrency_policy"
// LabelCronJobBackoffLimit defines the job backoff limit
LabelCronJobBackoffLimit = "kompose.cronjob.backoff_limit"
// LabelHpaMinReplicas defines min pod replicas
LabelHpaMinReplicas = "kompose.hpa.minreplicas"
// LabelHpaMaxReplicas defines max pod replicas
LabelHpaMaxReplicas = "kompose.hpa.maxreplicas"
// 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
128 changes: 128 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 = 10
DefaultCpuUtilization = 50
DefaultMemoryUtilization = 70
)
sosan marked this conversation as resolved.
Show resolved Hide resolved

// 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 @@ -985,3 +1009,107 @@ func reformatSecretConfigUnderscoreWithDash(secretConfig types.ServiceSecretConf

return newSecretConfig
}

// searchHPAValues is useful to check if labels
// contains any labels related to Horizontal Pod Autoscaler
func searchHPAValues(labels map[string]string) bool {
found := true
sosan marked this conversation as resolved.
Show resolved Hide resolved
for _, value := range LabelKeys {
if _, ok := labels[value]; ok {
return found
sosan marked this conversation as resolved.
Show resolved Hide resolved
}
}
return !found
sosan marked this conversation as resolved.
Show resolved Hide resolved
}

// 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 {
maxReplicas = minReplicas
}
sosan marked this conversation as resolved.
Show resolved Hide resolved

return HpaValues{
MinReplicas: minReplicas,
MaxReplicas: maxReplicas,
CPUtilization: getHpaValue(service, compose.LabelHpaCPU, DefaultCpuUtilization),
MemoryUtilization: getHpaValue(service, compose.LabelHpaMemory, DefaultMemoryUtilization),
sosan marked this conversation as resolved.
Show resolved Hide resolved
}
}

// 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 {
return defaultValue
}
sosan marked this conversation as resolved.
Show resolved Hide resolved
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