I'm trying to deploy my simple web application using Kubernetes. I completed making the Kubernetes cluster and successfully exposed my react app using ingress. But it seems that the domain URL of backend service received from manifest file's "env" field does not work.
Following is the manifest file of react application.
kind: Service
apiVersion: v1
metadata:
name: recofashion-client
labels:
app: recofashion-client
spec:
type: NodePort
selector:
app: recofashion-client
ports:
- name: http
port: 80
targetPort: 3000
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: recofashion-client
labels:
name: recofashion-client
spec:
replicas: 2
selector:
matchLabels:
app: recofashion-client
template:
metadata:
labels:
app: recofashion-client
spec:
containers:
- name: web
image: recofashion/client-runtime
imagePullPolicy: Always
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: production
- name: REACT_APP_API_V1_ENDPOINT
value: http://recofashion-api/api/v1
And I think there is no problem in k8s DNS itself. I tried to send request using curl in my "recofashion-client" pod, and it seems to work as I intended.
curl http://recofashion-api/api/v1/user/me
{"timestamp":"2020-02-03T06:55:20.748+0000","status":403,"error":"Forbidden","message":"Access Denied","path":"/api/v1/user/me"}
But when I try to send request in the browser, it fails like this:
And I'm receiving the external environment variables in react app from k8s like this:
const response = await getWithAuth(`${process.env.REACT_APP_API_V1_ENDPOINT}/user/me`)
So what's the problem??? I searched internet a lot, but I couldn't get any appopriate answer...
++ the manifest file of ingress
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: ingress
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: recofashion-client
servicePort: 80
Based on all informations provided I managed to reproduce your scenario using GKE.
TL;DR:
ClusterIP
LoadBalancer
, doc here.Your ENV REACT_APP_API_V1_ENDPOINT
must point to Api Service address, not to deploy or pod address. (i.e: value: http://recofashion-api-svc
)
You cannot use cluster DNS externally.
Since I don't have your react app, I'm using an echo-app to simulate the two parts of the communication. This way I'm manually reproducing what your application would do by itself.
internet
and recofashion-client
recofashion-client
and recofashion-api
.recofashion-client
- FrontEnd:
apiVersion: apps/v1
kind: Deployment
metadata:
name: recofashion-client
labels:
name: recofashion-client
spec:
replicas: 2
selector:
matchLabels:
app: recofashion-client
template:
metadata:
labels:
app: recofashion-client
spec:
containers:
- name: web
image: mendhak/http-https-echo
ports:
- name: http
containerPort: 80
env:
- name: NODE_ENV
value: production
- name: REACT_APP_API_V1_ENDPOINT
value: http://recofashion-api-svc
---
apiVersion: v1
kind: Service
metadata:
name: recofashion-cli-svc
labels:
app: recofashion-client
spec:
type: LoadBalancer
selector:
app: recofashion-client
ports:
- name: http
port: 3000
targetPort: 80
recofashion-api
- backend API:
kind: Deployment
apiVersion: apps/v1
metadata:
name: recofashion-api
labels:
name: recofashion-api
spec:
replicas: 2
selector:
matchLabels:
app: recofashion-api
template:
metadata:
labels:
app: recofashion-api
spec:
containers:
- name: api-web
image: mendhak/http-https-echo
imagePullPolicy: Always
ports:
- containerPort: 80
env:
- name: NODE_ENV
value: production
---
kind: Service
apiVersion: v1
metadata:
name: recofashion-api-svc
labels:
app: recofashion-api
spec:
selector:
app: recofashion-api
ports:
- name: http
port: 80
targetPort: 80
Note: I kept your Ingress intact.
Now to the terminal:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-cluster-1-default-pool-e0523823-06jt Ready <none> 2d v1.15.7-gke.23
gke-cluster-1-default-pool-e0523823-vklh Ready <none> 2d v1.15.7-gke.23
$ kubectl apply -f recofashion-full.yaml
deployment.apps/recofashion-client created
service/recofashion-cli-svc created
deployment.apps/recofashion-api created
service/recofashion-api-svc created
ingress.extensions/reco-ingress created
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/recofashion-api-784b4d9897-9256q 1/1 Running 0 12m
pod/recofashion-api-784b4d9897-ljkfs 1/1 Running 0 12m
pod/recofashion-client-75579c8499-wd5vj 1/1 Running 0 12m
pod/recofashion-client-75579c8499-x766s 1/1 Running 0 12m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 2d
service/recofashion-api-svc ClusterIP 10.0.4.73 <none> 80/TCP 12m
service/recofashion-cli-svc LoadBalancer 10.0.3.133 35.239.58.188 3000:31814/TCP 12m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/recofashion-api 2/2 2 2 13m
deployment.apps/recofashion-client 2/2 2 2 13m
NAME DESIRED CURRENT READY AGE
replicaset.apps/recofashion-api-784b4d9897 2 2 2 13m
replicaset.apps/recofashion-client-75579c8499 2 2 2 13m
$ curl http://35.239.58.188:3000
{
"path": "/",
"headers": {
"host": "35.239.58.188:3000",
"user-agent": "curl/7.66.0",
},
"method": "GET",
"body": "",
"hostname": "35.239.58.188",
"ip": "::ffff:10.8.1.1",
"protocol": "http",
"os": {
"hostname": "recofashion-client-75579c8499-x766s"
}
}
So far no problems, we are able to curl
from outside to the recofashion-client
.
Now let's connect inside recofashion-client
and try to connect to recofashion-api
using the ENV created during deploy:
❯ kubectl exec recofashion-client-75579c8499-x766s -it sh
/app # apk update && apk add curl
OK: 10 MiB in 20 packages
/app # env
REACT_APP_API_V1_ENDPOINT=http://recofashion-api-svc
NODE_ENV=production
/app # curl $REACT_APP_API_V1_ENDPOINT
{
"path": "/",
"headers": {
"host": "recofashion-api-svc",
"user-agent": "curl/7.61.1",
"accept": "*/*"
},
"method": "GET",
"body": "",
"hostname": "recofashion-api-svc",
"ip": "::ffff:10.8.1.21",
"protocol": "http",
"os": {
"hostname": "recofashion-api-784b4d9897-9256q"
}
}
/app # nslookup recofashion-api-svc
Name: recofashion-api-svc
Address 1: 10.0.4.73 recofashion-api-svc.default.svc.cluster.local
When we use the api-service
name in the ENV value
, it resolves the DNS because the service is the responsible for directing the load to the PODs.
Edit:
Ok, so you're not binding your ingress to any host name. So in order to access your service from the outside, you need to use the IP address associated to that ingress.
Using the service name won't have any affect from outside the cluster.
As the documentation says :
An optional host. In this example, no host is specified, so the rule applies to all inbound HTTP traffic through the IP address specified. If a host is provided (for example, foo.bar.com), the rules apply to that host.
In summary, if you have this configuration :
kubectl get ingress ingress # btw, this is a very bad name for your ingress !
NAME HOSTS ADDRESS PORTS AGE
ingress * 107.178.254.228 80 59s
You should be able to access your service with this url : http://107.178.254.228/
You can find more info about how Ingress
works (and this example in particulary) in the official documentation
After your clarification about the API, here is some precision :
As @saiteja pakalapati said, you can't access your API from outside your cluster using a ClusterIp
. While using a NodePort
can be a solution, you can just keep using your already existing Ingress
. Example below :
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: ingress
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: recofashion-client
servicePort: 80
- path: /api
backend:
serviceName: recofashion-api
servicePort: 80 # change with whatever port you're using
Now, in your code, in order to call the API, you'll need to use the ingress IP (as you're already doing with the client) and the path to your api : http://107.178.254.228/api
You are trying to access the micro service that is restricted to cluster
recofashion-api
can be resolved within the kubernetes cluster only not from outside
this line of code will be run in the browser who is accessing your site
const response = await getWithAuth(`${process.env.REACT_APP_API_V1_ENDPOINT}/user/me`)
so the browser cant resolve recofashion-api
and your get method is failing