Home > Software design >  React serves Springboot response locally but not in Kubernetes
React serves Springboot response locally but not in Kubernetes

Time:11-08

I have a React app that I want to pull data from a springboot endpoint on load. If I run it locally (via intellij and webstorm) it loads and I see my page render. It looks like this:

enter image description here

The 1: course1 and 2: course2 are the response being read from my springboot call.

The ONLY thing I'm changing in the code between local run and local cluster is the endpoint that react calls. I change from http://localhost:8080/greeting to http://bootdemo:8080/greeting (bootdemo being the name of the service). When I run it in my local cluster (Im using docker-desktop and navigating my browser to http://localhost:31000/) I get a page that just says "Loading..." and nothing ever loads. Using google developer tools on the page I can see this:

[HMR] Waiting for update signal from WDS...
App.js:12 Fetching from http://bootdemo:8080/greeting
App.js:13 GET http://bootdemo:8080/greeting net::ERR_NAME_NOT_RESOLVED
App.js:24 error: TypeError: Failed to fetch

Any help would be appreciated. Below is the react code and the k8's files and output I thought relevant to the problem. Let me know what else I can add if needed!

React App.js:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
    state = {
        isLoading: true,
        groups: []
    };

    async componentDidMount() {
        console.log("Fetching from http://bootdemo:8080/greeting")
        await fetch('http://bootdemo:8080/greeting').then((response) => {
            if(!response.ok) throw new Error("Couldnt make the connection: "   response.status.toString());
            else return response.json();
        })
            .then((data) => {
                console.log("Testing Testing")
                this.setState({ isLoading: false, groups: data });
                console.log(data)
                console.log("DATA STORED");
            })
            .catch((error) => {
                console.log('error: '   error);
                this.setState({ requestFailed: true });
            });
    }

    render() {
        const {groups, isLoading} = this.state;
        if (isLoading) {
            return <p>Loading...</p>;
        }
        console.log("Made it here")
        return (
            <div className="App">
                <header className="App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <div className="App-intro">
                        <h2>Attempted Response</h2>
                        <div>
                            {groups.map((group) => (
                                <p>{group.id} : {group.name}</p>
                            ))}
                        </div>
                    </div>
                </header>
            </div>
        );
    }
}

export default App;

K8's Springboot setup:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: bootdemo
  labels:
    name: bootdemo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bootdemo
  template:
    metadata:
      labels:
        app: bootdemo
    spec:
      containers: 
      - name: bootdemo
        image: demo:latest
        imagePullPolicy: Never
        ports:
          - containerPort: 80 
 
---
kind: Service
apiVersion: v1
metadata:
  name: bootdemo
  labels:
    app: bootdemo
spec:
  selector:
    app: bootdemo
  ports:
    - name: http
      port: 8080
      targetPort: 80
 

K8's React setup:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo
  labels:
    name: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo
  template:
    metadata:
      labels:
        app: demo
    spec:
      containers:
      - name: reactdemo
        image: reactdemo:latest
        imagePullPolicy: Never
        ports:
        - name: http
          containerPort: 80 
 
---
apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  type: NodePort
  selector:
    app: demo
  ports:
    - name: ingressport
      port: 3000
      targetPort: 3000
      nodePort: 31000     

Kubenetes after applying the deployment yamls:

NAME                            READY   STATUS    RESTARTS   AGE
pod/bootdemo-7f84f48fc6-2zw4t   1/1     Running   0          30m
pod/demo-c7fbcd87b-w77xh        1/1     Running   0          30m

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/bootdemo     ClusterIP   10.102.241.8   <none>        8080/TCP         30m
service/demo         NodePort    10.108.61.5    <none>        3000:31000/TCP   30m
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP          23d

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/bootdemo   1/1     1            1           30m
deployment.apps/demo       1/1     1            1           30m

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/bootdemo-7f84f48fc6   1         1         1       30m
replicaset.apps/demo-c7fbcd87b        1         1         1       30m

It seems that the react service just cant reach the boot service. I suspect it is one or more of the following:

  1. a naming/mapping issue in the deployment files
  2. using two services instead of just one
  3. React served into my browser doesn't know how to reach back into my cluster and get to springboot. Do I need something like nginx in the mix?

CodePudding user response:

Your browser won't be able to resolve http://bootdemo:8080/ because bootdemo is only resolvable by pods running inside the same namespace.

You see, even if you run Kubernetes cluster on your local machine via Docker Desktop, the cluster and your local machine are effectively isolated by their separate networking layers and hence any access from your local machine to any service in your cluster requires special bridging mechanisms such as NodePort or LoadBalancer services (as I can see you are already using NodePort service for the demo).

In a proper clusters served by Kubernetes distributions like AKS, GCP, EKS, PKS etc, however, you would need to design how the cluster would accept and route inbound traffics to the component within the cluster, which would commonly require setting up ingress controllers (NGINX being most popular though you can also use Traefik or HAProxy) as well as creating ingress resources which will map specific URLs & paths to specific services inside the cluster and namespace.

  • Related