I faced with problem of resolving my backend IP address from frontend service. Both deployed into GKE. There are two deployment files - for the backend and frontend. The frontend app calls the backend app API. On the local kubernetes everything is fine, but when I deploy to GKE there are problems with calling the API from the front. There is an error in the browser console:
I tried to pass the service name of the backend to the front deployment file in different ways:
imageupload-service:9595
imageupload-service.default.svc.cluster.local:9595
imageupload-service.default.svc:9595
but no luck.
Also I tried to execute command curl http://imageupload-service:9595/api/v1/user-profile from inside of my frontend pod to check availability of backend thought DNS and it returned correct response.
Here is my deployment files and content of App.js file where call to backend executed:
backend-deployment.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: imageupload-deployment
namespace: default
labels:
app: imageupload-deployment
spec:
progressDeadlineSeconds: 600
replicas: 2
selector:
matchLabels:
app: imageupload-deployment
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: imageupload-deployment
spec:
containers:
- image:
imagePullPolicy: Always
name: imageupload
env:
- name: SPRING_DATASOURCE_URL
value: "jdbc:postgresql://postgres-service:5432/postgres?currentSchema=imageupload"
- name: SPRING_DATASOURCE_USER
value: postgres
- name: SPRING_JPA_HIBERNATE_DDL-AUTO
value: none
- name: SPRING_SQL_INIT_MODE
value: NEVER
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-password-secret
key: postgres-password
resources:
limits:
memory: "1024Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "200m"
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: imageupload-service
namespace: default
labels:
app: imageupload-service
spec:
selector:
app: imageupload-deployment
ports:
- name: imageupload-service-port
protocol: TCP
port: 9595
type: LoadBalancer
loadBalancerIP: ""
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: imageupload-deployment-hpa
namespace: default
labels:
app: imageupload-service
spec:
maxReplicas: 2
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: imageupload-deployment
targetCPUUtilizationPercentage: 70
frontend-deployment.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: imageupload-front-deployment
namespace: default
labels:
app: imageupload-front-deployment
spec:
selector:
matchLabels:
app: imageupload-front-deployment
replicas: 2
template:
metadata:
labels:
app: imageupload-front-deployment
spec:
containers:
- name: imageupload-front
image:
env:
- name: REACT_APP_API_URL
value: http://imageupload-service:9595
imagePullPolicy: Always
resources:
limits:
cpu: "500m"
memory: "1024Mi"
requests:
cpu: "200m"
memory: "256Mi"
---
apiVersion: v1
kind: Service
metadata:
name: imageupload-front-service
namespace: default
labels:
app: imageupload-front-service
spec:
selector:
app: imageupload-front-deployment
ports:
- name: imageupload-front-service-port
protocol: TCP
port: 8080
type: LoadBalancer
loadBalancerIP: ""
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: imageupload-front-deployment-hpa
namespace: default
labels:
app: imageupload-front-service
spec:
maxReplicas: 3
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: imageupload-front-deployment
targetCPUUtilizationPercentage: 70
Content of App.js file:
import logo from './logo.svg';
import './App.css';
import axios from "axios";
import React, {useState, useEffect, useCallback} from "react";
import {useDropzone} from 'react-dropzone'
const apiUrl = process.env.REACT_APP_API_URL || "http://localhost:9595";
console.log("API URL:", apiUrl);
const UserProfiles = () => {
const [userProfiles, setUserProfiles] = useState([]);
const fetchUserProfiles = () => {
axios.get(`${apiUrl}/api/v1/user-profile`).then(res => {
console.log(res);
setUserProfiles(res.data);
})
.catch(err => {
console.error("Axios request failed:", err);
});
}
useEffect(() => {
fetchUserProfiles();
}, []);
return userProfiles.map((userProfile, index) => {
return (
<div key={userProfile.userProfileImageLink}>
{userProfile.userProfileId ?
<img key={userProfile.userProfileImageLink}
src={`${apiUrl}/api/v1/user-profile/${userProfile.userProfileId}/image/download`}/> : null}
<br/>
<br/>
<h1>{userProfile.username}</h1>
<p>{userProfile.userProfileId}</p>
<Dropzone {...userProfile} updateImageEvent={fetchUserProfiles}/>
<br/>
</div>
)
})
}
function Dropzone({userProfileId, updateImageEvent}) {
const onDrop = useCallback(acceptedFiles => {
const file = acceptedFiles[0];
console.log(file);
const formData = new FormData();
formData.append("file", file);
axios.post(`${apiUrl}/api/v1/user-profile/${userProfileId}/image/upload`,
formData,
{
headers: {
"Content-Type": "multipart/form-data"
}
}).then(() => {
console.log("file uploaded successfully");
updateImageEvent();
}).catch(err => {
console.log(err);
});
}, [])
const {getRootProps, getInputProps, isDragActive} = useDropzone({onDrop})
return (
<div {...getRootProps()}>
<input {...getInputProps()} />
{
isDragActive ?
<p>Drop the image here ...</p> :
<p>Drag 'n' drop profile image, or click to select profile image</p>
}
</div>
)
}
function App() {
return (
<div className="App">
<UserProfiles/>
</div>
);
}
export default App;
I expected response in JSON format like:
{
"userProfileId": "bb5b9dd8-f6fc-4bd4-8050-e9aa947e80cb",
"username": "John Doe",
"imageLink": ""
}
Looking for help to understand what is wrong here and what am I doing wrong and why is this error happening? I have no ideas...
It's a common mistake with modern website deployment. Ask yourself: where is running my frontend?
If your answer is "on GKE", it's wrong. GKE serves the web files (JS, HTML, CSS), but the code run on the browser.
Therefore, the mention
Also I tried to execute command curl http://imageupload-service:9595/api/v1/user-profile from inside of my frontend pod to check availability of backend thought DNS and it returned correct response.
is correct FROM INSIDE.
But your browser is OUTSIDE. All this URL are only known by your cluster, not on the internet
imageupload-service:9595
imageupload-service.default.svc.cluster.local:9595
imageupload-service.default.svc:9595
You must use the public URL of your ingress gateway (an IP or a domain name) to access your cluster FROM OUTSIDE.