0
votes

I am trying to setup Kuberentes for my company. In that process I am trying to learn Helm.

One of the tasks I have is to setup automation to take a supplied namespace name parameter, and create a namespace and setup the correct permissions in that namespace for the deployment user account.

I can do this simply with a script that uses kubectl apply like this:

kubectl create namespace $namespaceName
kubectl create rolebinding deployer-edit --clusterrole edit --user deployer --namespace $namespaceName

But I am wondering if I should set up things like this using Helm charts. As I look at Helm charts, it seems that everything is a deployment. I am not sure that this fits the model of "deploying" things. It is more just a general setup of a namespace that will then allow deployments into it. But I want to try it out as a Helm chart if it is possible.

How can I create a Kubernetes namespace and rolebinding using Helm?

2

2 Answers

2
votes

A Namespace is a Kubernetes object and it can be described in YAML, so Helm can create one. @mdaniel's answer describes the syntax for doing it for a single Namespace and the corresponding RoleBinding.

There is a chicken-and-egg problem if you are trying to use this syntax to create the Helm installation namespace, though. In Helm 3, metadata about the installation is stored in Kubernetes objects, usually in the same namespace you're installing into

helm install release-name ./a-chart-that-creates-a-namespace --namespace ns

If the namespace doesn't already exist, then Helm can't retrieve the installation metadata; or, if it does, then the declaration of the Namespace object in the chart will conflict with an existing object in the cluster. You can create other objects this way (like RoleBindings) but Namespaces themselves are a problem.

But! You can create other namespaces safely. You can also use Helm's templating constructs to create multiple objects based on what's present in the .Values configuration. So if your values.yaml file (possibly environment-specific) has

namespaces: [service-a, service-b]
clusterRole: edit
user: deploy

Then you can write a template file like

{{- $top := . }}
{{- range $namespace := .Values.namespaces -}}
---
apiVersion: v1
kind: Namespace
metadata:
  name: {{ $namespace }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: {{ $namespace }}
  name: deployer-edit
roleRef:
  apiGroup: ""
  kind: ClusterRole
  name: {{ $top.Values.clusterRole }}
subjects:
- apiGroup: ""
  kind: User
  name: {{ $top.Values.user }}
{{ end -}}

This will create two YAML documents for each item in .Values.namespaces. Since the range looping construct overwrites the . special variable, we save its value in a $top local variable before we start, and then use $top.Values where we'd otherwise need to reference .Values. We also need to make sure to explicitly name the metadata: { namespace: } of each object we create, since we're not using the default installation namespace.

You need to make sure the helm install --namespace name isn't any of the namespaces you're managing with this chart.

This would let you have a single chart that manages all of the per-service namespaces. If you needed to change the set of services, you can just update the chart values and helm update. The one other caution is that this will happily delete namespaces with no warning if you remove a value from the .Values.namespaces list, and also take everything in that namespace with it (notably, any PersistentVolumeClaims that have data you might need).

2
votes

Almost any chart for an install that needs to interact with kubernetes itself will include RBAC resources, so it is for sure not just Deployments

# templates/rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: {{ .Release.Namespace }}
  name: {{ .Values.bindingName }}
roleRef:
  apiGroup: ""
  kind: ClusterRole
  name: {{ .Values.clusterRole }}
subjects:
- apiGroup: ""
  kind: User
  name: {{ .Values.user }}

then a values.yaml isn't strictly required, but helps folks know what values could be provided:

# values.yaml
bindingName: deployment-edit
clusterRole: edit
user: deployer

Helm v3 has --create-namespace which will create the provided --namespace if it doesn't already exist, which isn't very declarative but does achieve the end result just like the kubectl version

It's also theoretically possible to have the chart create the Namespace but I would not guess that helm remove the-namespaced-rolebinding will do the right thing, since the order of item removal matters a lot:

# templates/00namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: {{ .Values.theNamespace }}

and then run helm --namespace kube-system ... or any NS other than the real one, since it doesn't yet exist