Skip to content
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
73 changes: 72 additions & 1 deletion vertical-pod-autoscaler/charts/vertical-pod-autoscaler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,54 @@ helm upgrade -i vertical-pod-autoscaler autoscalers/vertical-pod-autoscaler
| adrianmoisey | <[email protected]> | |
| omerap12 | <[email protected]> | |

## Webhook Management
The admission controller requires a `MutatingWebhookConfiguration` and TLS certificates. This chart supports two mutually exclusive modes:

### Helm-managed (default)
```yaml
admissionController:
registerWebhook: false
certGen:
enabled: true
```
In this mode:
- Helm creates the MutatingWebhookConfiguration
- The kube-webhook-certgen job generates TLS certificates and stores them in a Secret
- The certificates are automatically injected into the webhook configuration
### Application-managed
```yaml
admissionController:
registerWebhook: true
certGen:
enabled: false
```
In this mode:
- The VPA admission controller creates and manages the webhook itself
Important: You are responsible for creating the TLS secret before or after installing the chart. The admission controller will only create the `MutatingWebhookConfiguration` once the secret exists.
If the secret is created after the Helm install, you must restart the admission controller pod to trigger webhook registration.

## Migration Guides

### Migrating from vpa-up.sh script
TBD

### Migrating from Application-managed to Helm-managed webhook
If you previously deployed with registerWebhook: true and want to switch to Helm-managed:
- Delete the existing webhook:
```bash
kubectl delete mutatingwebhookconfiguration vpa-webhook-config
```
- Delete the existing secret (to allow certgen to create new certificates):
```bash
kubectl delete secret -n <namespace> vpa-tls-certs
```
- Upgrade with the new values:
```bash
helm upgrade <release-name> <chart> \
--set admissionController.registerWebhook=false \
--set admissionController.certGen.enabled=true
```
## Values

| Key | Type | Default | Description |
Expand All @@ -33,19 +81,36 @@ helm upgrade -i vertical-pod-autoscaler autoscalers/vertical-pod-autoscaler
| admissionController.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchExpressions[0].operator | string | `"In"` | |
| admissionController.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchExpressions[0].values[0] | string | `"admission-controller"` | |
| admissionController.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey | string | `"kubernetes.io/hostname"` | |
| admissionController.certGen.affinity | object | `{}` | |
| admissionController.certGen.enabled | bool | `true` | |
| admissionController.certGen.env | object | `{}` | Additional environment variables to be added to the certgen container. Format is KEY: Value format |
| admissionController.certGen.image.pullPolicy | string | `"IfNotPresent"` | The pull policy for the certgen image. Recommend not changing this |
| admissionController.certGen.image.repository | string | `"registry.k8s.io/ingress-nginx/kube-webhook-certgen"` | An image that contains certgen for creating certificates. |
| admissionController.certGen.image.tag | string | `"v20231011-8b53cabe0"` | An image tag for the admissionController.certGen.image.repository image. |
| admissionController.certGen.nodeSelector | object | `{}` | |
| admissionController.certGen.podSecurityContext | object | `{"runAsNonRoot":true,"runAsUser":65534,"seccompProfile":{"type":"RuntimeDefault"}}` | The securityContext block for the certgen pod(s) |
| admissionController.certGen.resources | object | `{}` | The resources block for the certgen pod |
| admissionController.certGen.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"readOnlyRootFilesystem":true}` | The securityContext block for the certgen container(s) |
| admissionController.certGen.tolerations | list | `[]` | |
| admissionController.enabled | bool | `true` | |
| admissionController.extraArgs | list | `[]` | |
| admissionController.extraEnv | list | `[]` | |
| admissionController.image.pullPolicy | string | `"IfNotPresent"` | |
| admissionController.image.repository | string | `"registry.k8s.io/autoscaling/vpa-admission-controller"` | |
| admissionController.image.tag | string | `nil` | |
| admissionController.mutatingWebhookConfiguration.annotations | object | `{}` | Additional annotations for the MutatingWebhookConfiguration |
| admissionController.mutatingWebhookConfiguration.failurePolicy | string | `"Ignore"` | The failurePolicy for the mutating webhook. Allowed values are: Ignore, Fail |
| admissionController.mutatingWebhookConfiguration.namespaceSelector | object | `{}` | The namespaceSelector controls which namespaces are affected by the webhook |
| admissionController.mutatingWebhookConfiguration.objectSelector | object | `{}` | The objectSelector can filter objects on e.g. labels |
| admissionController.mutatingWebhookConfiguration.timeoutSeconds | int | `5` | Sets the amount of time the API server will wait on a response from the webhook service |
| admissionController.nodeSelector | object | `{}` | |
| admissionController.podAnnotations | object | `{}` | |
| admissionController.podDisruptionBudget.enabled | bool | `true` | |
| admissionController.podDisruptionBudget.maxUnavailable | int or string | `nil` | Maximum number/percentage of pods that can be unavailable after the eviction. IMPORTANT: You can specify either 'minAvailable' or 'maxUnavailable', but not both. |
| admissionController.podDisruptionBudget.minAvailable | int or string | `1` | Minimum number/percentage of pods that must be available after the eviction. IMPORTANT: You can specify either 'minAvailable' or 'maxUnavailable', but not both. |
| admissionController.podLabels | object | `{}` | |
| admissionController.priorityClassName | string | `nil` | |
| admissionController.registerWebhook | bool | `false` | |
| admissionController.replicas | int | `2` | |
| admissionController.resources | object | `{}` | |
| admissionController.service.annotations | object | `{}` | |
Expand All @@ -58,7 +123,7 @@ helm upgrade -i vertical-pod-autoscaler autoscalers/vertical-pod-autoscaler
| admissionController.serviceAccount.labels | object | `{}` | |
| admissionController.tls.caCert | string | `""` | |
| admissionController.tls.cert | string | `""` | |
| admissionController.tls.existingSecret | string | `""` | |
| admissionController.tls.create | bool | `false` | |
| admissionController.tls.key | string | `""` | |
| admissionController.tls.secretName | string | `"vpa-tls-certs"` | |
| admissionController.tolerations | list | `[]` | |
Expand All @@ -67,6 +132,12 @@ helm upgrade -i vertical-pod-autoscaler autoscalers/vertical-pod-autoscaler
| admissionController.volumeMounts[0].readOnly | bool | `true` | |
| admissionController.volumes[0].name | string | `"tls-certs"` | |
| admissionController.volumes[0].secret.defaultMode | int | `420` | |
| admissionController.volumes[0].secret.items[0].key | string | `"ca"` | |
| admissionController.volumes[0].secret.items[0].path | string | `"caCert.pem"` | |
| admissionController.volumes[0].secret.items[1].key | string | `"cert"` | |
| admissionController.volumes[0].secret.items[1].path | string | `"serverCert.pem"` | |
| admissionController.volumes[0].secret.items[2].key | string | `"key"` | |
| admissionController.volumes[0].secret.items[2].path | string | `"serverKey.pem"` | |
| admissionController.volumes[0].secret.secretName | string | `"vpa-tls-certs"` | |
| commonLabels | object | `{}` | |
| containerSecurityContext | object | `{}` | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,52 @@ helm upgrade -i vertical-pod-autoscaler autoscalers/vertical-pod-autoscaler

{{ template "chart.requirementsSection" . }}

## Webhook Management
The admission controller requires a `MutatingWebhookConfiguration` and TLS certificates. This chart supports two mutually exclusive modes:

### Helm-managed (default)
```yaml
admissionController:
registerWebhook: false
certGen:
enabled: true
```
In this mode:
- Helm creates the MutatingWebhookConfiguration
- The kube-webhook-certgen job generates TLS certificates and stores them in a Secret
- The certificates are automatically injected into the webhook configuration

### Application-managed
```yaml
admissionController:
registerWebhook: true
certGen:
enabled: false
```
In this mode:
- The VPA admission controller creates and manages the webhook itself
Important: You are responsible for creating the TLS secret before or after installing the chart. The admission controller will only create the `MutatingWebhookConfiguration` once the secret exists.
If the secret is created after the Helm install, you must restart the admission controller pod to trigger webhook registration.

## Migration Guides

### Migrating from vpa-up.sh script
TBD

### Migrating from Application-managed to Helm-managed webhook
If you previously deployed with registerWebhook: true and want to switch to Helm-managed:
- Delete the existing webhook:
```bash
kubectl delete mutatingwebhookconfiguration vpa-webhook-config
```
- Delete the existing secret (to allow certgen to create new certificates):
```bash
kubectl delete secret -n <namespace> vpa-tls-certs
```
- Upgrade with the new values:
```bash
helm upgrade <release-name> <chart> \
--set admissionController.registerWebhook=false \
--set admissionController.certGen.enabled=true
```
{{ template "chart.valuesSection" . }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{{- $componentsList := list }}
{{- if .Values.admissionController.enabled }}{{ $componentsList = append $componentsList "admission-controller" }}{{- end }}
{{- if .Values.recommender.enabled }}{{ $componentsList = append $componentsList "recommender" }}{{- end }}
{{- if .Values.updater.enabled }}{{ $componentsList = append $componentsList "updater" }}{{- end }}

Vertical Pod Autoscaler has been installed!

Components deployed:
{{- range $componentsList }}
✓ {{ . }}
{{- end }}

Namespace: {{ .Release.Namespace }}

{{- if .Values.admissionController.enabled }}

Webhook Configuration:
{{- if .Values.admissionController.registerWebhook }}
Mode: Application-managed
- Webhook registered by: admission-controller application
- Certificates managed by: User (you must provide the TLS secret)

⚠️ IMPORTANT: You must create the TLS secret '{{ include "vertical-pod-autoscaler.admissionController.tls.secretName" . }}'
The admission controller will only register the webhook once this secret exists.
If the secret is created after this install, restart the admission controller:

kubectl rollout restart deployment/{{ include "vertical-pod-autoscaler.admissionController.fullname" . }} -n {{ .Release.Namespace }}
{{- else if .Values.admissionController.certGen.enabled }}
Mode: Helm-managed (recommended)
- Webhook registered by: Helm (MutatingWebhookConfiguration)
- Certificates managed by: certgen job
{{- else if .Values.admissionController.tls.create }}
Copy link
Member

@iamzili iamzili Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please get rid of all Values.admissionController.tls.create related part, we don't need this helm value

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We chatted about this on Slack. I am not sure if we wanna get rid of this.

Mode: Helm-managed with Helm-generated certificates
- Webhook registered by: Helm (MutatingWebhookConfiguration)
- Certificates managed by: Helm (auto-generated)

⚠️ WARNING: Helm-generated certificates
The TLS certificates are generated by Helm and will be regenerated on each
'helm upgrade', which may cause brief disruptions. For production, consider:
- Using certGen (admissionController.certGen.enabled: true)
- Using cert-manager with a custom secret
{{- else }}
⚠️ WARNING: No TLS certificate source configured!
The admission controller may fail to start. Please set one of:
- admissionController.certGen.enabled: true (recommended)
- admissionController.tls.create: true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- admissionController.tls.create: true

- admissionController.registerWebhook: true (application-managed)
{{- end }}
{{- end }}

Documentation: https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,33 @@ app.kubernetes.io/component: admission-controller
Create the name of the tls secret to use
*/}}
{{- define "vertical-pod-autoscaler.admissionController.tls.secretName" -}}
{{- if .Values.admissionController.tls.existingSecret -}}
{{ .Values.admissionController.tls.existingSecret }}
{{- if .Values.admissionController.tls.secretName -}}
{{ .Values.admissionController.tls.secretName }}
{{- else -}}
{{- printf "%s-%s" (include "vertical-pod-autoscaler.admissionController.fullname" .) "tls" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}

{{/*
admissionController webhook
*/}}

{{- define "vertical-pod-autoscaler.admissionController.webhook.configName" -}}
{{ include "vertical-pod-autoscaler.fullname" . }}-webhook-config
{{- end }}

{{/*
admissionController certGen
*/}}
{{- define "vertical-pod-autoscaler.admissionController.certGen.fullname" -}}
{{ include "vertical-pod-autoscaler.fullname" . }}-admission-certgen
{{- end }}

{{- define "vertical-pod-autoscaler.admissionController.certGen.labels" -}}
{{ include "vertical-pod-autoscaler.labels" . }}
app.kubernetes.io/component: admission-certgen
{{- end }}


{{/*
updater
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{- if and .Values.admissionController.enabled .Values.admissionController.certGen.enabled -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a specific reason why this resource (and I see the same pattern in several others, such as ClusterRoleBinding, Role, RoleBinding) defines both pre and post hooks? I think pre-install,pre-upgrade would be sufficient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, you’re right - pre-install and pre-upgrade are enough, but it really depends on how we want to handle things going forward. As mentioned above, the hook currently creates a Secret containing all certificates, CA bundles, and the mutating webhook configuration. Because this Secret already exists during an upgrade, kube-webhook-certgen will not rotate the certificate values.

To address this, we may want to add post-install and post-upgrade hooks to delete the Secret, ensuring that on the next upgrade kube-webhook-certgen generates a fresh one.

Copy link
Member

@iamzili iamzili Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But using post-install and post-upgrade hooks to delete the Secret is still not a good idea, I assume because it would mean that after a Helm upgrade or install, there would be no Secret object in the cluster as Helm executes post-install and post-upgrade hooks after all non-hook resources have been deployed to the cluster.

If we want to delete the certificate before running a Helm upgrade or install, then we need to do it in pre-install and pre-upgrade hooks, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one more thing: kube-webhook-certgen generates certificates that expire after 100 years, so I assume we don't need to rotate them. We could just ignore the "secret already exists" log when kube-webhook-certgen create runs.

"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
"helm.sh/hook-weight": "-10"
labels:
{{- include "vertical-pod-autoscaler.admissionController.certGen.labels" . | nindent 4 }}
rules:
- apiGroups:
- admissionregistration.k8s.io
resources:
- mutatingwebhookconfigurations
verbs:
- get
- update
- patch
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- if and .Values.admissionController.enabled .Values.admissionController.certGen.enabled -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
"helm.sh/hook-weight": "-10"
labels:
{{- include "vertical-pod-autoscaler.admissionController.certGen.labels" . | nindent 4 }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
subjects:
- kind: ServiceAccount
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
namespace: {{ .Release.Namespace }}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{{- if and .Values.admissionController.enabled .Values.admissionController.certGen.enabled -}}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}-patch
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
labels:
{{- include "vertical-pod-autoscaler.admissionController.certGen.labels" . | nindent 4 }}
spec:
ttlSecondsAfterFinished: 300
template:
metadata:
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}-patch
labels:
{{- include "vertical-pod-autoscaler.admissionController.certGen.labels" . | nindent 8 }}
spec:
restartPolicy: OnFailure
serviceAccountName: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
containers:
- name: patch
image: {{ printf "%s:%s" .Values.admissionController.certGen.image.repository .Values.admissionController.certGen.image.tag }}
imagePullPolicy: {{ .Values.admissionController.certGen.image.pullPolicy }}
args:
- patch
- --webhook-name={{ include "vertical-pod-autoscaler.admissionController.webhook.configName" . }}
- --namespace={{ .Release.Namespace }}
- --secret-name={{ include "vertical-pod-autoscaler.admissionController.tls.secretName" . }}
- --patch-validating=false
{{- with .Values.admissionController.certGen.env }}
env:
{{- range $key, $value := . }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- end }}
{{- with .Values.admissionController.certGen.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.admissionController.certGen.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.admissionController.certGen.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.admissionController.certGen.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.admissionController.certGen.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.admissionController.certGen.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end -}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{- if and .Values.admissionController.enabled .Values.admissionController.certGen.enabled -}}
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ include "vertical-pod-autoscaler.admissionController.certGen.fullname" . }}
namespace: {{ .Release.Namespace }}
annotations:
"helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
"helm.sh/hook-weight": "-10"
labels:
{{- include "vertical-pod-autoscaler.admissionController.certGen.labels" . | nindent 4 }}
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- create
{{- end -}}
Loading