Namespaces group related objects and logically segregate them from those in different Namespaces. If a Kubernetes cluster hosts multiple unrelated applications, it might be a good idea to divide them using Namespaces.

When using kubectl you can use the -n option to specify which Namespace it applies to, or you can indirectly define one via a kubeconfig (discussed in the page on kubeconfigs).

  • If you use any of these methods to define a Namespace, kubectl will operate only over objects in that Namespace.
  • If you don't define a Namespace, it is the same as defining the Namespace as "default", and kubectl will only operate over objects in this "default" Namespace.

Forgetting to use either -n or a kubeconfig is a common mistake. So if your commands are failing and it's not clear why, make sure you're using some way of scoping them to the correct Namespace.

Namespace manifests can be very simple. For example:

apiVersion: v1
kind: Namespace
metadata:
  name: ecommerce

Go ahead and put this manifest in a file and use kubectl apply on it. Then modify your Pod, Service, and Ingress manifest files to be under this Namespace, and apply them. See below for an example of doing this with the Service:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: ecommercespec:
  ports:
  - port: 80
  selector:
    component: webserver

To the metadata section was added namespace: ecommerce. Every Kubernetes object that is designated as belonging to a Namespace will have this field under the metadata section. If no Namespace were designated here, it would be the same as having namespace: default.

Speaking of the default Namespace, if you use kubectl get pods and kubectl get pods -n ecommerce, you'll see that the old Pod wasn't removed, but instead only a duplicate Pod was created in the ecommerce Namespace. This will be true of the Service and Ingress you just added, too. Remove these with kubectl delete.

$ kubectl delete pod nginx-pod
pod "nginx-pod" deleted
$ kubectl delete service nginx-service
service "nginx-service" deleted
$ kubectl delete ingress my-app-ingress
ingress.networking.k8s.io "my-app-ingress" deleted

(Note that deleting with the -f option won't work if you've already added the ecommerce Namespace into the files. This would delete the object in the ecommerce Namespace and leave the one in the default Namespace untouched.)

You can view the Pods and Services (but not the Ingresses) in a Namespace by using kubectl get all.

$ kubectl get all -n ecommerce
NAME            READY   STATUS    RESTARTS   AGE
pod/nginx-pod   1/1     Running   0          22m

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/nginx-service   ClusterIP   10.43.212.153   <none>        80/TCP    24m

Cluster-internal communication

The focus of the Services & Ingresses page was how to set up things so that requests from e.g. your browser could reach the Pod in the cluster. But there are many occasions on which Pods in the cluster will need to make requests to each other. For example, you might have a Pod setup as an API gateway, which then relays requests to other Pods having microservices.

So how should one Pod communicate with another? There is a cluster-internal IP address that the Service creates, but you don't know what this will be when writing your application, and it will likely be different across multiple environments, so it's not a good idea to use this IP address directly. Fortunately there are two better options:

  • The recommended way is to use the cluster-internal DNS name derived from the Service name.
  • You can also use environment variables that hold the Service's IP address and port.

DNS

The cluster creates DNS names for each Service. When communicating within a Namespace, this DNS name is simply the name of the Service. What we'll do now is:

  • Deploy another Pod that can make requests
  • Make sure that Pod uses the Service name (nginx-service) to direct its requests to the nginx-pod
  • Check to see that the requests are returning successfully

Add a Pod to your cluster using the following manifest:

apiVersion: v1
kind: Pod
metadata:
  name: request-sender
  namespace: ecommerce
spec:
  containers:
  - name: request-sender-ctr
    image: bmcase/repeat-request
    args: ["nginx-service"]

This container's default behavior would be to make a request every 10 seconds to google.com. The manifest's "args" line changes this to make it instead send requests to nginx-service, which will be used in the requests as a domain.

Check with kubectl get all -n ecommerce to confirm that you now have two Pods and a Service. If all is well, the request-sender Pod will be sending requests to the nginx-pod via the nginx-service. Check and make sure this is happening with kubectl logs -n ecommerce request-sender:

$ kubectl logs -n ecommerce request-sender
Status code from nginx-service: 200
Status code from nginx-service: 200

CoreDNS

The above DNS system applies to traffic within a Namespace. If you need a Pod in one Namespace to make a request to a Service in another, you can instead use the DNS names provided by CoreDNS. This will be the name of the Service with that of the Namespace appended to it, separated by a dot. (That is, it uses the name of the Namespace as the domain, and the name of the Service as a subdomain). For example, if a Pod in the ecommerce Namespace wanted to make a request to nginx-service in the default Namespace, it could use the DNS name nginx-service.default.

Clusters that use Kubernetes v1.13 and above have CoreDNS enabled by default. On older clusters, with version at or above v1.9, CoreDNS can be activated.

To test this, recreate an nginx-pod and an nginx-service in the default namespace. Then use kubectl apply to change the args used by your ecommerce request-sender to nginx-service.default. Finally, check to make sure the requests are returning with kubectl logs request-sender -n ecommerce.

Service environment variables

When a Pod is created, it is initialized with some environment variables that contain the host and port of all the Services in its Namespace. We'll now use these to recreate the request-sender functionality from the previous section.

These environment variables will only be created in the Pod if the Service already exists at the time of Pod creation. Since we're now going to check inside our Pod to find these environment variables, go ahead and delete and then recreate your nginx-pod with kubectl delete pod nginx-pod -n ecommerce && kubectl apply -f nginx-pod.yml -n ecommerce to make sure the Pod has been created after the Service. Once you've done this, you can run kubectl exec nginx-pod -n ecommerce -- printenv | grep SERVICE to list the environment variables related to Services. There will likely be several, but the two of interest now are:

NGINX_SERVICE_SERVICE_HOST=10.43.212.153
NGINX_SERVICE_SERVICE_PORT=80

The IP address in the HOST variable might be different for you.

Use the same request-sender manifest above, except change the value of the "args" field to be ["$(NGINX_SERVICE_SERVICE_HOST):$(NGINX_SERVICE_SERVICE_PORT)"]. (Parentheses are needed around environment variables when using them in the "args" field.)

Apply this manifest, and check the logs. You'll now see in the logs the IP address and port from the environment variables:

$ kubectl logs -n ecommerce request-sender
Status code from 10.43.212.153:80: 200
Status code from 10.43.212.153:80: 200

You can now delete the request-sender with kubectl delete -f request-sender.yml.