Accessing a service in a different namespace from a single ingress in Kubernetes

IPv4. Its rare when its public, and annoying when its private. So we try and conserve this precious resource. One of the things that makes it complex is Kubernetes namespaces. A Kubernetes Ingress controller is not namespace aware (you can't have a shared Ingress that has services in multiple namespaces). Or can you?

What if I told you you could install a single Ingress (and cert-manager etc) and then have a service in each namespace served by it? Would you rejoice over saving a few $100/mo on public IP rental in 'the cloud'?

Lets dig in. Imagine we have 3 namespaces with interesting services. 'foo', 'bar' and 'kube-system' (which has our dashboard).

Lets assume we have 'kibana' running in kube-system. We want to expose this to the 'public internet'. Likely we would also use oauth2 proxy here to sign in, but I'll ignore auth for now.  We are going to use a new service (synthetic) which lives in the default namespace alongside our Ingress controller as 'glue'. Its kind of like a DNS CNAME.

First we install a single global ingress. Lets use helm:

helm install stable/nginx-ingress --name ingress --set controller.service.externalTrafficPolicy=Local --set rbac.create=true

Wait for the LoadBalancer to get a public IP, register it in DNS. You can either use a wildcard (*.something.MYDOMAIN.CA) or register each service, your call. All will use the same IP.

(To avoid complicating this, I'll show the cert-manager etc at the end, but its optional, we just need the next step with the Ingress + Service)

Once we have installed the below yaml we can now browse https://kibana.MYDOMAIN.CA/ and we are there. Repeat for the other services. Done! We have a single public IP

apiVersion: extensions/v1beta1
kind: Ingress
  name: kibana
  annotations: nginx letsencrypt "true" 'true' 'true'
  - hosts:
    - kibana.MYDOMAIN.CA
    secretName: tls-secret-kibana
  - host: kibana.MYDOMAIN.CA
      - path: /
          serviceName: kibana
          servicePort: 5601
kind: Service
apiVersion: v1
  name: kibana
  namespace: default
  type: ExternalName
  externalName: kibana.kube-system.svc.cluster.local
  - port: 5601

Now, to complete this and show with SSL certificates. You don't need this, above is all you need to expose the service, but why not do it on TLS at the same time? Its free!.

helm install stable/cert-manager --namespace kube-system --set ingressShim.defaultIssuerName=letsencrypt --set ingressShim.defaultIssuerKind=ClusterIssuer --name cert

cat << EOF | kubectl -n kube-system apply -f -
kind: ClusterIssuer
  name: letsencrypt
      name: letsencrypt
    http01: {}
Tagged with: , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *