ellioseven
ellioseven

Reputation: 141

GKE Error: listen EACCES: permission denied on any port

I'm attempting to start my Express.js application on GKE, however no matter which port I specify, I always get an error like so:

Error: listen EACCES: permission denied tcp://10.3.253.94:3000
    at Server.setupListenHandle [as _listen2] (net.js:1296:21)
    at listenInCluster (net.js:1361:12)
    at Server.listen (net.js:1458:5)
    at Function.listen (/srv/node_modules/express/lib/application.js:618:24)
    at Object.<anonymous> (/srv/src/index.js:42:5)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)

I've tried multiple ports (8080, 8000, 3000). I've set the user to root in the Docker image.

Here's my setup:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: api
  name: api
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      containers:
        - image: gcr.io/ellioseven-kbp/journal-api:1.0.14
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
          name: api
apiVersion: v1
kind: Service
metadata:
  labels:
    app: api
  name: api
  namespace: default
spec:
  ports:
    - port: 3000
      protocol: TCP
      targetPort: 3000
  selector:
    app: api
  type: NodePort
FROM node:12-alpine
ENV PATH /srv/node_modules/.bin:$PATH
ENV API_PORT 3000
ENV REDIS_HOST redis
COPY . /srv
WORKDIR /srv
ENV PATH /srv/node_modules/.bin:$PATH
RUN yarn install
CMD yarn start
EXPOSE 3000
USER root
const port = process.env.API_PORT || 3000
app.listen(port, () => console.log("Listening on " + port))

I'm at a complete loss trying to solve this, any help would be greatly appreciated.

Upvotes: 1

Views: 1958

Answers (3)

ThangLeQuoc
ThangLeQuoc

Reputation: 3158

I got the same issue on EKS. Same as your situation, the problem is because of automatic environment variable injection from the service name.

Back to your question context, the service name is api, with selector to the pod api.
K8s will inject the service host and port with the following service environment variables (see Cluster Information)

API_SERVICE_HOST=<the host the service is running on>
API_SERVICE_PORT=<the port the service is running on>

But wait, that's not all!. K8s also create variables that are compatible with Docker Engine's "legacy container links" feature.
From k8s Service Networking - Environment Variable source:

When a Pod is run on a Node, the kubelet adds a set of environment variables for each active Service. It adds {SVCNAME}_SERVICE_HOST and {SVCNAME}_SERVICE_PORT variables, where the Service name is upper-cased and dashes are converted to underscores. It also supports variables (see makeLinkVariables) that are compatible with Docker Engine's "legacy container links" feature.

So, here are the complete list of environment variable of api service that will be automatically created and injected to your api pod (you can see the example of redis-primary service in the original K8s doc above as well).

API_SERVICE_HOST = 10.3.253.94
API_SERVICE_PORT = 3000

API_PORT=tcp://10.3.253.94:3000   # @thang: This is the env var that cause the exception
API_PORT_3000_TCP=tcp://10.3.253.94:3000
API_PORT_3000_TCP_PROTO=tcp
API_PORT_3000_TCP_PORT=3000
API_PORT_3000_TCP_ADDR=10.3.253.94

As you can see, the auto generated env include the API_PORT variable, which conflict with your env name variable name

ENV API_PORT 3000

This env naming conflict causes the pod resolve the incorrect API_PORT value and throw the pod starting exception.

In order to fix this issue, (as already pointed out by @Anonymous answer above), you either have to:

  • Rename the api service to another name, to avoid env variable pattern conflict
  • Or use a different env variable name for the port, e.g: API_PORT_NUMBER

Upvotes: 1

Anonymous
Anonymous

Reputation: 21

Your issue is on environment variable conflicting with the service name.

According to Kubernetes docs

For a service named foo that maps to a Container named bar, the following variables are defined:

FOO_SERVICE_HOST=<the host the service is running on>
FOO_SERVICE_PORT=<the port the service is running on>

Change either the service name or the environment variable

For e.g. use API_PORT_NUMBER instead of API_PORT

Upvotes: 2

Rico
Rico

Reputation: 61689

Looks like it's not able just bind to the 3000 port. It could be any of:

  • Not binding to 0.0.0.0. You can try:

    app.listen('3000','0.0.0.0',()=>{
        console.log("server is listening on 3000 port");
    })
    
  • You are running as non-privileged user in the container. See if you can start a test pod and run the command once it started.

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: api
        name: api
        namespace: default
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: api
        template:
          metadata:
            labels:
              app: api
          spec:
            dnsPolicy: ClusterFirst
            restartPolicy: Always
            containers:
              - image: gcr.io/ellioseven-kbp/journal-api:1.0.14
                imagePullPolicy: IfNotPresent
                command: [ "/bin/bash", "-c", "--" ] šŸ‘ˆ
                args: [ "while true; do sleep 30; done;" ] šŸ‘ˆ
                ports:
                  - containerPort: 3000
                name: api
    

    Then you can connect to the pod and try to start your app

    kubectl exec -it <pod-name> bash
    

āœŒļø

Upvotes: 1

Related Questions