Making Our Own Alpine Node.js Container
Making our own Node.js Alpine container
Let's take this exercise a bit further. Let's actually make our own Node.js Alpine container. NOTE: I'd suggest always using the official one. They'll keep it up to date with security fixes and they're real good at making containers. Better than I am, anyway. But this is a good exercise for us to go through to learn how to install system dependencies.
Start with this in a new Dockerfile. You can call it my-node.Dockerfile
. Some people will insist it should be Dockerfile.my-node
but the former doesn't break syntax highlighting and it doesn't matter since Docker doesn't actually care.
FROM alpine:3.10
RUN apk add --update nodejs npm
alpine:latest
would nab you the latest Alpine (3.10 as of writing, if you run into issues with versions, continue with alpine:3.10
instead of alpine:latest
. Otherwise feel free to truck on with alpine:latest
)
RUN apk add --update nodejs npm
will use the Alpine package manager to grab Node.js and npm (they're bundled separately for Alpine.)
If you encounter error like this
/home/node/code/node_modules/@hapi/hapi/lib/core.js:51
actives = new WeakMap(); // Active requests being processed
^
SyntaxError: Unexpected token =
Try using nodejs-current
instead of nodejs
RUN apk add --update nodejs-current npm
Okay so now if you do docker build -t my-node -f my-node.Dockerfile .
it'll build your new image. -t
is --tag
and -f
is --file
which is what tells Docker is the name of your Dockerfile you're using (otherwise it assumes you're using a file called exactly Dockerfile
.) Now try docker run -it my-node
. In here you should have a pretty bare bones Linux container but both node -v
and npm -v
should work.
Keep in mind that Alpine does not use bash for its shell; it uses a different shell called ash
or often just sh
. It's similar enough to bash but there are some differences. It's not really the point of this class so we'll keep the focus on learning just what's necessary.
Let's next make our node
user.
FROM alpine:3.10
RUN apk add --update nodejs npm
RUN addgroup -S node && adduser -S node -G node
USER node
I'm mimicking what the Node.js official container does, which is make a user group of node
with one user in it, node
. Feel free to name them different things if you feel so inclined. Notice we could conceivably combine the two RUN
instructions together but it's generally best practices to keep "ideas" separate. The first RUN
installs dependencies, the second one creates the node
user. Up to you how you do it, neither is wrong per se.
Now we can just copy the rest from the previous Dockerfile! Let's do that.
FROM alpine:3.10
RUN apk add --update nodejs npm
RUN addgroup -S node && adduser -S node -G node
USER node
RUN mkdir /home/node/code
WORKDIR /home/node/code
COPY package-lock.json package.json ./
RUN npm ci
COPY . .
CMD ["node", "index.js"]
It works! We're down to 56MB (compared to 86MB with the official node:12-alpine
container). Honestly, I'm not entirely sure what we cut out from the other node:12-alpine
container but it's probably important. Again, I'd stick to the official containers where they exist. But hey, we learned how to add a user and install system dependencies! Let's make it even small because why the hell not.