Using cert-manager as a subchart
Hello.
In today’s post, I would like to show how to set up cert-manager as a subchart. Not only that,
but also, how to add a Job
to make sure that cert-manager is installed and its webhook is
up and running so it can process any Issuers
that are going to be applied with your own chart.
Let’s get to it.
Defining a subchart
To define a subchart, simply add it as a dependency in your Charts.yaml
like this:
dependencies:
- name: cert-manager
version: v1.14.5
repository: https://charts.jetstack.io
condition: cert-manager.enabled
Easy. Now, notice the condition. This value indicates that it needs to be set in order to install cert-manager. That happens by defining this in your values file:
cert-manager:
enabled: false
namespace: cert-manager
installCRDs: true
fullnameOverride: "cert-manager" # this is needed for the certificate issuer to not throw an unknown authority error
nameOverride: "cert-manager" # needed because otherwise it will call it `certManager`
Once these values are defined, now comes the hard part.
Post-install, Post-upgrade
With Helm chart what you want is to make sure all things are up and running before continuing on. This is sometimes not easy to determine for helm especially if the deployed resource gives a thumbs-up even before everything is actually running. That can happen if Readiness isn’t define properly.
With cert-manager it happens that cert-manager is up and running, yet when it tries to apply the next thing in your chart which is an Issuer, it will error that cert-manager webhook could not be reached.
To work around that problem, helm introduced chart hooks1. They allow you to control when then next thing should run.
This is somewhat helpful, but we need even more control. We need to be able to make sure cert-manager is up and running.
We can do that by running the following wait
commands with kubectl
.
kubectl wait --for=condition=Available=True Deployment/cert-manager -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-webhook -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-cainjector -n cert-manager --timeout=60s
But we need to be able to do that DURING helm install
. That’s where the fun begins.
Jobs
To run a command during install we can utilize a Job
. How? By creating a Job
deployment and putting it
into the templates so it’s applied. Like this:
apiVersion: batch/v1
kind: Job
metadata:
name: wait-for-cert-manager
namespace: cert-manager
labels:
app: {{ .Release.Name }}-wait-for-cert-manager
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
serviceAccountName: wait-for-cert-manager-sa
containers:
- name: wait-for-cert-manager
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
kubectl wait --for=condition=Available=True Deployment/cert-manager -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-webhook -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-cainjector -n cert-manager --timeout=60s
restartPolicy: OnFailure
However, we only want to do this if cert-manager is enabled so we put a little templating in there:
{{- if index .Values "cert-manager" "enabled" }}
apiVersion: batch/v1
kind: Job
metadata:
name: wait-for-cert-manager
namespace: cert-manager
labels:
app: {{ .Release.Name }}-wait-for-cert-manager
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
template:
spec:
serviceAccountName: wait-for-cert-manager-sa
containers:
- name: wait-for-cert-manager
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
kubectl wait --for=condition=Available=True Deployment/cert-manager -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-webhook -n cert-manager --timeout=60s
kubectl wait --for=condition=Available=True Deployment/cert-manager-cainjector -n cert-manager --timeout=60s
restartPolicy: OnFailure
{{- end}}
Now, what else we need are some roles and service account to run this with.
{{- if index .Values "cert-manager" "enabled" }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: wait-for-cert-manager-role
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
{{- end}}
{{- if index .Values "cert-manager" "enabled" }}
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: wait-for-cert-manager-rolebinding
subjects:
- kind: ServiceAccount
name: wait-for-cert-manager-sa
namespace: cert-manager
roleRef:
kind: ClusterRole
name: wait-for-cert-manager-role
apiGroup: rbac.authorization.k8s.io
{{- end}}
{{- if index .Values "cert-manager" "enabled" }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: wait-for-cert-manager-sa
namespace: cert-manager
{{- end}}
Put these into the template section too and run helm install
.
{{- if index .Values "cert-manager" "enabled" }}
This is using index
because the value in values.yaml is cert-manager
with a hyphen.
Conclusion
Once these values are set, you should be able to run helm install and have your Issuer run correctly AFTER cert-manager is up and running properly.
A sample implementation of all of this can be found in this2 repository.
Thank you for reading!