How to create Kubernetes Namespace if it does not Exist?

58.2k views Asked by At

It's a simple question, but I could not find a definite answer for it.

Question

Is it possible to create a namespace only if it doesn't exist.
My objective is to create some service accounts without caring if their namespaces exist or not (if not, then they should be created on the fly).

The thing is I'm using CDK to deploy some basics K8S resources (including service accounts). To safely do this, I need to make sure the namespace (given in the service account manifest) already exists. However I'm not able to find any solution.

I tried patch, but it seems to expect the resource to exist already (i.e. it fails with NotFound error).

Two limitations:

  1. I can't query to see if the namespace exists or not.
  2. I can't use apply since I don't have the exact definition of the namespace.

Is there any way to achieve this?

14

There are 14 answers

2
gaolong zhao On
kubectl get ns <namespace-name> || kubectl create ns <namespace-name>
0
Panoptik On

It is not the answer to specified question, but it is ready to use solution for those who google for subject question.

Based on @Arghya Sadhu answer my bash solution for creating if not exist namespace looks next:

    echo "apiVersion: v1
kind: Namespace
metadata:
  name: ${NS_NAME}" | kubectl apply -f -
0
MarcoK On

The options highlighted by @Panoptik and @Arghya Sadhu got me to use this one liner in a deployment pipeline:

echo -e "apiVersion: v1\nkind: Namespace\nmetadata:\n  name: ${NS_NAME}" | kubectl 
apply -f - 

Why an one liner: I needed to avoid line breaks in the pipeline. The code was tested on Debian and also the official Google Cloud Build image "gcloud".

0
Fraser Goffin On

I mostly agree with @arghya-sadhu so far as declarative is nearly always the way to go. However, you could test for the existance of a namespace in bash, something like this:

#!/bin/bash

NAMESPACE_NAME=${1:-default};

export KUBECONFIG=your-kubeconfig.yml;

NS=$(kubectl get namespace $NAMESPACE_NAME --ignore-not-found);
if [[ "$NS" ]]; then
  echo "Skipping creation of namespace $NAMESPACE_NAME - already exists";
else
  echo "Creating namespace $NAMESPACE_NAME";
  kubectl create namespace $NAMESPACE_NAME;
fi;
0
Amol Ghotankar On

-q did not work for me but having -c worked below is the output

kubectl get ns | grep -c "^too " || kubectl create namespace too

First execution

$ kubectl get ns | grep -c "^too " || kubectl create namespace too
0
namespace/too created

Second execution

$ kubectl get ns | grep -c "^too " || kubectl create namespace too
1

The use-case where we needed just so people know is when you need to create a new namespace and inject it to istio before you install any charts or services etc.

4
Arghya Sadhu On

Given the limitations I can only think of one way which is to apply a namespace yaml always before you apply the service account yaml. If the namespace exists already it will give you a message that namespace already exists.You can ignore that message and move ahead.

apiVersion: v1
kind: Namespace
metadata:
  name: test

So here we are being declarative and it does not matter what exists and what does not. You just define what the desired state should look like and kubernetes will take care of making sure that happens.

2
user1568407 On

If you're using bash and just want to pipe any warnings that the namespace already exists when trying to create it you can pipe stderr to /dev/null

kubectl create namespace <namespace> 2>/dev/null
4
Corey P On

I was able to solve this problem using,

myNamespace="new-namespace"
kubectl get namespace | grep -q "^$myNamespace " || kubectl create namespace $myNamespace

The command kubectl get namespace gives an output like,

NAME                STATUS   AGE
alpha               Active   29m
default             Active   112m
gatekeeper-system   Active   111m
kube-node-lease     Active   112m
kube-public         Active   112m
kube-system         Active   112m
some-branch         Active   26m
something           Active   7m28s
something-else      Active   5m7s

Then, | grep -q "^$my-namespace " will look for your namespace in the output. The q will cause the command to return a 0 if your namespace is found. Otherwise it'll return a 1.

Note: the ^ the beginning and white-space at the end are important. This ensures the whole namespace is matched, and not just part of it. For example, if you were searching for the namespace something and did NOT include the space at the end, it would match both something and something-else from the example above.

Finally, || kubectl create namespace $my-namespace will create the namespace if it was found (i.e. the grep returned 1). Otherwise, it will not be created.

0
dom On

You can also consider using helm for this. Helm has a feature that creates the namespace for you if it doesn't exist and it simplifies the deployment of whatever app you want to deploy into that namespace. Notice the use of "--create-namespace", this will create my-namespace for you.

helm -n my-namespace upgrade --install --create-namespace my-app my-app-folder/

Tips:

  • Always use upgrade --install because it can do both those things
  • Use the option --set to set specific values in values.yaml at runtime of the command (useful i.e for secrets)
0
mirekphd On
NS_NAME=user3 && \
  kubectl create namespace $NS_NAME --dry-run=server && \
    echo "$NS_NAME namespace does not exist yet, creating it..." && \
      kubectl create namespace $NS_NAME

Explanation

Testing for existence can be achieved by attempting to create the new namespace, but only with --dry-run=server (caution: this is the only way to check if the namespace already exists, as --try-run=client would give false negatives). Then, if the dry run creation does NOT fail, we can create it for real (the chaining is done using &&, that requires that the previous command completed successfully, which it would only if the namespace did not exist).

6
Manuel On

i wouldn't go for any other solution except the following code snippet:

kubernetes 1.19 and above (included)

kubectl create namespace <add-namespace-here> --dry-run=client -o yaml | kubectl apply -f -

kubernetes 1.18 and below (included)

kubectl create namespace <add-namespace-here> --dry-run -o yaml | kubectl apply -f -

it creates a namespace in dry-run and outputs it as a yaml. The output will be passed as stdin to kubectl apply -f -

The last hyphen is important while passing kubectl to read from stdin.

Also see the examples in:

kubectl apply --help
0
Ray Hallquist On
kubectl create namespace <add-namespace-here> --dry-run=client -o yaml | kubectl apply -f -

This is solution from Arghya Sadhu an elegant. Although create is not a desired state, apply is. Thank you Arghya.

0
Yuriy On

I have tried most of the options but the latest works for my deployment script best:

if ! kubectl get namespaces -o json | jq -r ".items[].metadata.name" | grep staging;then
  echo 'HALLO'
fi
1
chriscatfr On
kubectl create namespace <add-namespace-here> || echo "namespace already exists"

If the first part of the command fails, most likely it would mean it already exists. By using || instead of | you won't get your script interrupted by the error

another idea is to try to describe it, it will fail if it doesn't exist. But you said you can't query

kubectl describe ns <add-namespace-here> || kubectl create namespace <add-namespace-here>