Kubernetes Communication between Frontend and Backend

First I will try to address your specific questions

The only working state was approached when I first started the Backend service with type NodePort and copied the url and port to the Frontends target URL. I think this is very unclean to me. Is there another approach to get the url for backend requests into the Frontend?

You have couple of options here 1) As you said, use type="LoadBalancer". OR 2) Proxy all your backend calls through your front end server

I know when we deploy a service on a production system with type="LoadBalancer" that the service is exposed by an external IP and I can access the service then from there. And that the external IP will be permanent at pod updates and so on. The problem I also see is that the backend IP needs to be injected into the docker container by an additional commit.

  1. Make it a 12-factor app (or 1 step closer to a 12-factor app :)) by moving the config out from your code to platform (let's say to k8s configmap or an external KV registry like consul/eureka)
  2. Even if it's left in code, as you said, the external IP will be referable and it's not going to change unless you do so. I don't see why you need another deployment

Proxy all your backend calls through your front end server

If you are routing (or willing to route) all your microservices/backend call thru the server side of your front end and if are deploying both your front end and backend in the same k8s cluster in the same namespace, then you can use KubeDNS add-on (If it is not available in your k8s cluster yet, you can check with the k8s admin) to resolve the backend service name to it's IP. From your front end server, Your backend service will always be resolvable by it's name.

Since you have kubeDNS in your k8s cluster, and both frontend and backend services resides in same k8s cluster and same namespace, we can make use of k8s' inbuilt service discovery mechanism. Backend service and frontend service will be discoverable each other by it's name. That means, you can simply use the DNS name "backend" to reach your backend service from your frontend pods. So, just proxy all the backend request through your front end nginx to your upstream backend service. In the frontend nginx pods, backend service's IP will resolvable for the domain name "backend". This will save you the CORS headache too. This setup is portable, meaning, it doesn't matter whether you are deploying in dev or stage or prod, name "backend" will always resolve to the corresponding backend.

A potential pitfall of this approach is, your backend may not be able to scale independent of frontend; Which is not a big deal in my humble opinion; In a k8s environment, it is just a matter of spinning up more pods if needed.

Just curious- What is serving your front end (which server technology is delivering your index.html to user's browser)? is it static servers like nginx or apache httpd or are you using nodejs here?


This is how i have configured Ingress to connect frontend app to backend using service names.

Bankend (Spring Boot App) Note the name attribute of service

  • Service config
apiVersion: v1
kind: Service
metadata:
  name: student-app-api
spec:
  selector:
    app: student-app-api
  ports:
    - port: 8080
      protocol: TCP
      targetPort: 8080

Frontend Frontend does not need to know about the backend server.

lets assume frontend needs to call getAllStudents API of backend. Frontend can call API like this Sample React code

const get = id => {
  return http.get(`api/students/`);
};

//this will send request to own server, and we will redirect that request to backend using Ingress config like below

Ingress

apiVersion: networking.k8s.io/v1beta1 # for versions before 1.14 use extensions/v1beta1
kind: Ingress
metadata:
  name: student-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
  - http:
      paths:
        - path: /?(.*)
          backend:
            serviceName: student-app-client-service
            servicePort: 80
        - path: /api/?(.*)   //Redirect all request to backend (backend service name)
          backend:
            serviceName: student-app-api
            servicePort: 8080

We use a different approach than in the answer of so-random-dude (which a nice solution): we let the backend server serve the frontend files. We have separated docker images for both, but use 1 pod only. The frontend runs as init container and copies the files to a emptydir volume. The backend also mounts that volume and serves it on / (all backend resources are served on other paths). This way the frontend and the backend are served on the same host.

You can get the current host (which also is the backend host now) in the Angular code with window.location.protocol + '//' + window.location.host.

During development on the local dev machine we run the frontend and backend on its own separated servers. So we have a little helper function for getting the correct backend url in all cases:

public getBackendUrl(): string {
  return this.getBackendUrlInternal(window.location.protocol, window.location.host);
}

private getBackendUrlInternal(protocol: string, host: string): string {
  if (host === 'localhost:3000') {
    // running in local dev server, connect to local dev backend
    return 'http://localhost:8585';
  } else {
    // running in docker compose or on k8s, backend is on same host and port as we are
    return protocol + '//' + host;
  }
}

(There 2 methods because we have some tests for the 2nd one)


I would sugest using Kubernetes own way of flexible tarffic ingestion with Ingress/IngressController. Havin ingress controller deployed in your cluster, you can easily create Ingress definition telling the controller to expose service under particular URL. All you need to do then is to point that name in DNS to the Ingress Controllers loadbalancer (in most cloud setups that will be with a CNAME to the LB fqdn).

https://kubernetes.io/docs/concepts/services-networking/ingress/ https://github.com/kubernetes/ingress

Tags:

Kubernetes