Kubernetes deploy a single NodePort for two deployments in different namespaces

4/13/2021

I have to deploy on my kubernetes cluster two deployments that use the same service for communicate but the two deployments are located into two differents namespaces:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1
  namespace: namespace1
  labels:
    app: app1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
        - name: app1
          image: eu.gcr.io/direct-variety-20998876/test1:dev
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
          ports:
            - containerPort: 8000
          imagePullPolicy: Always
          env:
              ...

and an identical second but in another amespace:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app2
  namespace: namespace2
  labels:
    app: app2
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
        - name: app2
          image: eu.gcr.io/direct-variety-20998876/test1:prod
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
          ports:
            - containerPort: 8000
          imagePullPolicy: Always
          env:
              ...

so i have to create a common service for bot deployment that run over the two namespaces: I try:

kind: Service
apiVersion: v1
metadata:
  name: apps-service
  namespace: ???
spec:
  selector:
    app: ???
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
  type: NodePort

Until now i create one service for any app in specific namespace but there is a method for create a single service for manage both deployment (and then associate an unique ingress)?

So many thanks in advance

-- Manuel Santi
kubernetes
kubernetes-deployment
kubernetes-service

1 Answer

4/14/2021

First, I would like to provide some general explanations. As we can see in the Ingress documentation:

You must have an Ingress controller to satisfy an Ingress. Only creating an Ingress resource has no effect.

Ingress Controller can be deployed in any namespace and is often deployed in a namespace separate from the application namespace.

Ingress resource (Ingress rules) should be deployed in the same namespace as the services they point to.

It is possible to have one ingress controller for multiple ingress resources.

Deploying an Ingress resource in the same namespace as the Services it points to is the most common approach (I recommend this approach). However, there is way to have Ingress in one namespace and Services in another namespaces using externalName Services.


I will create an example to illustrate how it may work.

Suppose, I have two Deployments (app1, app2) deployed in two different Namespaces (namespace1, namespace2):

$ cat app1.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: app1
  name: app1
  namespace: namespace1
spec:
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
      - image: nginx
        name: nginx
    
$ cat app2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: app2
  name: app2
  namespace: namespace2
spec:
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - image: nginx
        name: nginx
    

And I exposed these Deployments with ClusterIP Services:

$ cat svc-app1.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: app1
  name: app1
  namespace: namespace1
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app1
    
$ cat svc-app2.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: app2
  name: app2
  namespace: namespace2
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: app2

We want to have a single Ingress resource in a separate Namespace (default). First, we need to deploy Services of type ExternalName that map a Service to a DNS name.

$ cat external-app1.yml
kind: Service
apiVersion: v1
metadata:
  name: external-app1
spec:
  type: ExternalName
  externalName: app1.namespace1.svc
  
$ cat external-app2.yml
kind: Service
apiVersion: v1
metadata:
  name: external-app2
spec:
  type: ExternalName
  externalName: app2.namespace2.svc

Then we can deploy Ingress resource:

$ cat ingress.yml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /
  name: app-ingress
spec:
  rules:
    - http:
        paths:
          - path: /app1
            backend:
              serviceName: external-app1
              servicePort: 80
          - path: /app2
            backend:
              serviceName: external-app2
              servicePort: 80
              
$ kubectl apply -f ingress.yml
ingress.networking.k8s.io/app-ingress created
          

Finally, we can check if it works as expected:

$ curl 34.118.X.207/app1
app1
$ curl 34.118.X.207/app2
app2

NOTE: This is a workaround and may work differently with different ingress controllers. It is ususally better to have two or more Ingress resources in different namespaces.

-- matt_j
Source: StackOverflow