Reputation: 141
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
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:
api
service to another name, to avoid env variable pattern conflictAPI_PORT_NUMBER
Upvotes: 1
Reputation: 21
Your issue is on environment variable conflicting with the service name.
For a service named
foo
that maps to a Container namedbar
, 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
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