Helm 3 Namespace Creation
Helm 3 was released and works great except that it no longer creates namespaces for your deployments. Here is one solution along with alternatives worth looking into.
Introduction
I created a generic helm3 namespace chart for use with helmfile or similar helm gluing toolsets. This is just a carry over solution for helm 3’s inabilty to create namespaces for a release which likely will change in helm 3.1.
Chart Values
Pretty much just a list of namespaces to create as well as additional labels and annotations you’d like to append. You can also set if helm is allowed to delete the namespace or not. Default policy is ‘keep’.
namespaces:
- namespace1
- namespace2
helmResourcePolicy: keep
annotations:
certmanager.k8s.io/disable-validation: true
labels:
additional_label1: myvalue
Example - helmfile
A simple example helmfile that creates a namespace as part of a cert-manager deployment. The default helm resource policy of ‘keep’ is used so that the namespace will not be removed in a helm destroy operation. This means you will have to manually delete the namespace if you want to reinstall the deployment while testing things out. Default tillerless plugin options are also set if this helmfile is created with helm version 2. I only include the namespace generation in this example for brevity.
helmDefaults:
tillerless: true
tillerNamespace: platform
atomic: false
verify: false
wait: true
timeout: 1200
recreatePods: true
force: true
repositories:
- name: jetstack
url: "https://charts.jetstack.io"
- name: "incubator"
url: "https://kubernetes-charts-incubator.storage.googleapis.com"
- name: "zloeber"
url: "git+https://github.com/zloeber/helm-namespace@chart"
releases:
###############################################################################
## CERT-MANAGER - Automatic Let's Encrypt for Ingress ########################
## Also provides local CA for issuing locally valid TLS certificates #######
###############################################################################
# References:
# - https://github.com/jetstack/cert-manager/blob/v0.11.0/deploy/charts/cert-manager/values.yaml
# Instructions for installing and testing correct install are at
# - https://docs.cert-manager.io/en/release-0.9/getting-started/install/kubernetes.html
- name: namespace-cert-manager
# Helm 3 needs to put deployment info into a namespace. As this creates a namespace it will not exist yet so we use 'kube-system'
# which should exist in all clusters.
chart: zloeber/namespace
namespace: kube-system
labels:
chart: namespace-cert-manager
component: "cert-manager"
namespace: "cert-manager"
wait: true
installed: {{ env "STACK_CERTMANAGER" | default "true" }}
values:
- namespaces:
- cert-manager
helmResourcePolicy: delete
annotations:
certmanager.k8s.io/disable-validation: "true"
- name: cert-manager
namespace: "cert-manager"
labels:
chart: "cert-manager"
repo: "stable"
component: "kiam"
namespace: "cert-manager"
vendor: "jetstack"
default: "false"
chart: jetstack/cert-manager
version: "v0.9.0"
wait: true
installed: {{ env "STACK_CERTMANAGER" | default "true" }}
needs:
- kube-system/namespace-cert-manager
hooks:
# This hook adds the CRDs
- events: ["presync"]
showlogs: true
command: "/bin/sh"
args: ["-c", "kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.9/deploy/manifests/00-crds.yaml"]
values:
- fullnameOverride: cert-manager
rbac:
create: {{ env "RBAC_ENABLED" | default "true" }}
ingressShim:
defaultIssuerName: '{{ env "CERT_MANAGER_INGRESS_SHIM_DEFAULT_ISSUER_NAME" | default "letsencrypt-staging" }}'
defaultIssuerKind: '{{ env "CERT_MANAGER_INGRESS_SHIM_DEFAULT_ISSUER_KIND" | default "ClusterIssuer" }}'
{{ if env "CERT_MANAGER_IAM_ROLE" | default "" }}
podAnnotations:
iam.amazonaws.com/role: '{{ env "CERT_MANAGER_IAM_ROLE" }}'
{{ end }}
serviceAccount:
create: {{ env "RBAC_ENABLED" | default "true" }}
name: '{{ env "CERT_MANAGER_SERVICE_ACCOUNT_NAME" | default "" }}'
{{- if eq (env "MONITORING_ENABLED" | default "true") "true" }}
prometheus:
enabled: true
servicemonitor:
enabled: true
prometheusInstance: {{ env "PROMETHEUS_INSTANCE" | default "kube-prometheus" }}
targetPort: 9402
path: /metrics
interval: 60s
scrapeTimeout: 30s
{{ end }}
webhook:
enabled: false
cainjector:
enabled: true
resources:
limits:
cpu: "200m"
memory: "256Mi"
requests:
cpu: "50m"
memory: "128Mi"
- name: cert-manager-issuers
chart: "incubator/raw"
namespace: "cert-manager"
labels:
component: "cert-manager"
namespace: "cert-manager"
default: "true"
wait: true
force: true
recreatePods: true
installed: {{ env "STACK_CERTMANAGER" | default "true" }}
needs:
- kube-system/namespace-cert-manager
- cert-manager/cert-manager
values:
- resources:
- apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: {{ coalesce (env "SMTP_RECIPIENT") (env "CERT_MANAGER_EMAIL") (env "KUBE_LEGO_EMAIL") "[email protected]" }}
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: nginx
{{- if env "CERT_MANAGER_IAM_ROLE" | default "" }}
- dns01:
route53: {}
{{- end }}
- apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: {{ coalesce (env "SMTP_RECIPIENT") (env "CERT_MANAGER_EMAIL") (env "KUBE_LEGO_EMAIL") "[email protected]" }}
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
{{- if env "CERT_MANAGER_IAM_ROLE" | default "" }}
- dns01:
route53: {}
{{- end }}
This helmfile will require that you use the helm-git plugin and that the namespace not already exist. I also use pretty recent helmfile features like ‘needs’ to ensure that the namespace is created first.
helm plugin install https://github.com/aslafy-z/helm-git.git
Alternatives
There are some alternatives which may be better suited to your particular need. See this thread for more information on each of these.
Alternative 1 - helm-namespace (plugin)
I’ve also done some testing with the helm-namespace plugin and it works very well. Unfortunately this requires changing your helm commands and may interrupt existing workflows. This is the first alternative and honestly, probably the best one.
plugin install https://github.com/thomastaylor312/helm-namespace
Alternative 2 - presync hooks
There are also presync helm hooks which will allow you to run kubectl commands to create the namespace if it does not exist. A helmfile would have a presync hook like the following to accomplish this task.
- events: ["presync"]
showlogs: true
command: "/bin/sh"
args:
- "-c"
- >-
kubectl get namespace "{{`{{ .Release.Namespace }}`}}" >/dev/null 2>&1 || kubectl create namespace "{{`{{ .Release.Namespace }}`}}";
This has the drawback of requiring 100% certainty of your kubectl context and version. It also obscures your end helm state (imho). Benefits for using this would be that your helm deployment will not puke out on you if the resource (namespace) already exists.
Alternative 3 - raw charts
The incubator/raw helm chart is a wonderous chart that you can do so many cool things with that of course you can also create your namespaces with it if desired. Drawback is that it is pure kubernetes declarative manifest yaml (for the most part). Plus, I just wanted a small point solution for use in all my existing helm charts so I opted to not use the raw chart for this particular need.