Cannot access exposed Dockerized React app on Kubernetes

7/21/2018

I'm trying to deploy my Dockerized React app to Kubernetes. I believe I've dockerized it correctly, but i'm having trouble accessing the exposed pod.

I don't have experience in Docker or Kubernetes, so any help would be appreciated.

My React app is just static files (from npm run build) being served from Tomcat.

My Dockerfile is below. In summary, I put my app in the Tomcat folder and expose port 8080.

FROM private-docker-registry.com/repo/tomcat:latest

EXPOSE 8080:8080

# Copy build directory to Tomcat webapps directory
RUN mkdir -p /tomcat/webapps/app
COPY /build/sample-app /tomcat/webapps/app

# Create a symbolic link to ROOT -- this way app starts at root path 
(localhost:8080)

RUN ln -s /tomcat/webapps/app /tomcat/webapps/ROOT

# Start Tomcat
ENTRYPOINT ["catalina.sh", "run"]

I build and pushed the Docker image to the Private Docker Registry. I verified that container runs correctly by running it like this:

docker run -p 8080:8080 private-docker-registry.com/repo/sample-app:latest

Then, if I go to localhost:8080, I see the homepage of my React app.

Now, the trouble I'm having is deploying to Kubernetes and accessing the app externally.

Here's my deployment.yaml file:

kind: Deployment
apiVersion: apps/v1beta2
metadata:
  name: sample-app
  namespace: dev
  labels:
    app: sample-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-app
        image: private-docker-registry.com/repo/sample-app:latest
        ports:
        - containerPort: 8080
          protocol: TCP
      nodeSelector:
        TNTRole: luxkube
---
kind: Service
apiVersion: v1
metadata:
  name: sample-app
  labels:
    app: sample-app
spec:
  selector:
    app: sample-app
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

I created the deployment and service by running kubectl --namespace=dev create -f deployment.yaml

Output of 'describe deployment'

    Name:                   sample-app
    Namespace:              dev
    CreationTimestamp:      Sat, 21 Jul 2018 12:27:30 -0400
    Labels:                 app=sample-app
    Annotations:            deployment.kubernetes.io/revision=1
    Selector:               app=sample-app
    Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=sample-app
      Containers:
       sample-app:
        Image:        private-docker-registry.com/repo/sample-app:latest
        Port:         8080/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   sample-app-bb6f59b9 (1/1 replicas created)
    Events:          <none>

Output of 'describe service'

Name:                     sample-app
Namespace:                fab-dev
Labels:                   app=sample-app
Annotations:              <none>
Selector:                 app=sample-app
Type:                     NodePort
IP:                       10.96.29.199
Port:                     <unset>  80/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  34604/TCP
Endpoints:                192.168.138.145:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Now I don't know which IP and port I should be using to access the app. I have tried every combination but none has loaded my app. I believe the port should be 80, so if I just have the IP, i shuold be able to go to the browser and access the React app by going to http://.

Does anyone have suggestions?

-- user1596241
docker
dockerfile
kubernetes
reactjs
tomcat

1 Answer

7/21/2018

The short version is that the Service is listening on the same TCP/IP port on every Node in your cluster (34604) as is shown in the output of describe service:

NodePort:                 <unset>  34604

If you wish to access the application through a "nice" URL, you'll want a load balancer that can translate the hostname into the in-cluster IP and port combination. That's what an Ingress controller is designed to do, but it isn't the only way -- changing the Service to be type: LoadBalancer will do that for you, if you're running in a cloud environment where Kubernetes knows how to programmatically create load balancers for you.

-- mdaniel
Source: StackOverflow