Dockerfile:
# 1. build - create dist/ (from devDependencies)
FROM node:latest AS build-dist
WORKDIR /usr/src/app
COPY --chown=node:node node-files .
RUN npm ci
RUN npm run build
# 2. build - dependencies (production only dependencies)
FROM node:latest AS build-deps
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY --chown=node:node node-files/package.json node-files/package-lock.json .
RUN npm ci --only=production
FROM gcr.io/distroless/nodejs:debug
ENV PATH=/app/node_modules/.bin:$PATH
WORKDIR /app
COPY --from=build-deps --chown=node:node /usr/src/app .
COPY --from=build-dist --chown=node:node /usr/src/app/dist dist
ENV NODE_ENV=production
EXPOSE 80
CMD ["/nodejs/bin/node", "dist/node-server.js"]
My node-server
file is generated from TypeScript via another build;
node-files/node-server.js
gets generated into dist/node-server.js
:
import Fastify from "fastify";
const server = Fastify();
server.get("/", (req, res) => {
res.send({ message: "Success" });
});
const closeGracefully = async (signal) => {
console.log(`Received signal to terminate: ${signal}`)
await server.close()
process.kill(process.pid, signal);
}
process.once('SIGINT', closeGracefully)
process.once('SIGTERM', closeGracefully)
const init = async () => {
try {
server.listen({ port: 80, host: '0.0.0.0' }, (err, address) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log(`Server listening at ${address}`);
});
console.log(`*^!@4=> Process id: ${process.pid}`);
} catch (err) {
server.log.error(err);
process.exit(1);
}
};
init();
Docker build:
docker build -t distroless:1 .
docker run --name test-distroless -p 80:80 distroless:1
Error:
/nodejs/bin/node:1
ELF☻☺☺♥
SyntaxError: Invalid or unexpected token
SyntaxError: Invalid or unexpected token
at internalCompileFunction (node:internal/vm:73:18)
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1176:20
When doing exec
into the Docker container and running, then it works:
docker exec -it test-distroless sh
/nodejs/bin/node dist/node-server.js
So it turns out the cause of the problem was using this: CMD ["/nodejs/bin/node", "dist/node-server.js"]
I suspect the entrypoint for the distroless defaults to node.
Updating the dockerfile with this fixed it:
CMD ["dist/node-server.js"]
And this alternative solution also works:
ENTRYPOINT ["/nodejs/bin/node", "--"]
CMD ["dist/node-server.js"]