Search code examples
node.jsdockerdistroless

Distroless Node server getting error - elf


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

Solution

  • 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"]