
Writing Dockerfiles for Node.js Web Apps - praveenweb
https://blog.hasura.io/an-exhaustive-guide-to-writing-dockerfiles-for-node-js-web-apps-bbee6bd2f3c4
======
BjoernKW
I completely get the purpose and benefit of Docker. More and more though I'm
becoming convinced that for many settings and problems Docker actually creates
more problems and meaningless ritual because Docker rapidly is becoming the
standard way of deploying software with hardly anyone asking about the reasons
anymore.

Docker recreates many of the benefits the Java platform has had from the start
(in addition to some other aspects but that's the main selling pont). So, if
you have a non-Java application Docker might indeed be a worthwhile choice.
The thing is, even with Java platform-independence and deployment
standardization for web apps / SaaS-type applications has always been a minor
selling point.

Those features are certainly important if you need to rapidly provision and
scale your application on many different machines in a distributed environment
you don't completely control.

In reality, for many applications today this simply isn't the case though. The
main motivator behind that feature after all was the concept of desktop
applications that are deployed to each user individually.

So, with Docker we now indiscriminately add an additional layer of complexity
on top of existing applications regardless of the actual problems this might
solve.

~~~
thamizhan2611
> So, with Docker we now indiscriminately add an additional layer of
> complexity on top of existing applications regardless of the actual problems
> this might solve.

I agree that it adds an extra layer of complexity but it also provides a lot
of things out of the box (like isolation, separation of concerns, platform
agnostic deployments, reproducability and lot more) which makes the life of a
developer so easy. If he wants to deploy his application, instead of SSHing
into the server, installing the dependency, managing the lifecycle of his
application. He just needs to dockerize his application and rest assured that
it will run irrespective of the platform on which it is put to run as long as
it has docker running.

I think what it boils down to is what you expect a tool to do and if it really
solves some hard hitting problems you have with your development life and took
away your nightmares then it is totally fine to invest in it and chill.

~~~
BjoernKW
The problem lies with the "just needs to dockerize his application" part.
These days I see many developers regularly servicing Docker containers where
they rather should be adding value to the product they're creating. It almost
seems as if Docker has become an end in itself rather than just a means.

In some ways with Docker you also only move concerns from one layer to
another. While in the past you had to manage dependencies on the target
machine you now have to manage Docker dependencies and environments on that
machine on top of the dependency management for your application.

~~~
thamizhan2611
Fair point. Again it is the call that the developer has to take depending upon
certain parameters. If he is shipping multiple applications built with Python
with one requiring Python 2.7 and the other requiring Python 3.4. He has to
somehow make sure that both the application will run peacefully together or he
can just create two docker images on his machine one contains Python2.7
installed and the other contains Python 3.0 installed and deploy it the docker
way without any hassle. At the end of the day it is the call he has to take
depending upon how much time each of the flow is going to consume and which
one makes his life easy.

------
politelemon
Ideally I'd like the same Dockerfile used for both local dev as well as
deployment. To do this, we're using something similar to the first Dockerfile,
with the `nodemon` install.

Then in a docker-compose.override.yml,

    
    
        command: nodemon --inspect=0.0.0.0:5858 /code/bin/www
    

To allow for debugging as well. We delete that .override.yml file as part of
the CI build/deploy.

------
jpallen
Nice post, and very close to my experiences with Dockerfiles for node.

To the author: could you elaborate on the differences between carbon and
alpine base images please? I can obviously go look this up, but this was the
one part of the guide that was new to me. Your description wasn't enough to
give me a complete understanding, so perhaps you could explain a bit more
about carbon and alpine? Thanks.

~~~
praveenweb
carbon is the latest LTS (Long Term Support) version of Node. Instead of using
the latest version, which can have breaking changes and possible drop of
support in the future, it is recommended to use LTS version just to ensure
using a stable + well supported version. alpine base images are generally used
to cut down the image size for production, because they are very minimal and
works well for most use-cases. But you would generally use carbon or specific
full blown version of node in development, because you can install system
dependencies for bundling code.

~~~
jpallen
Thanks! You've given me something new to try out :)

------
rshurts
No mention of using a non-root user? Any reason why or just oversight?

With Debian based images like node:carbon you could do something like this:

    
    
      ENV HOME /home/nodejs
      RUN groupadd -r nodejs \
      && useradd -r -g nodejs nodejs \
      && mkdir -p $HOME \
      && chown nodejs:nodejs $HOME
      USER nodejs
    

With alpine something like this:

    
    
      RUN addgroup -S nodejs && adduser -S -G nodejs nodejs
      USER nodejs

------
rid
I'm surprised they haven't included a process manager for the production
build. In my experience it allows for much easier 0-sec downtime updates and
load balancing.

We're using pm2 [1] for this if anyone's interested.

[1] [https://github.com/Unitech/pm2](https://github.com/Unitech/pm2)

~~~
alberteinstein
If you use container orchestration platforms like Kubernetes, this is not
required, as restarts load balancing etc. are handled by the orchestrator.

~~~
rid
Does Kubernetes support graceful reload of containers, with 0 downtime? I'd be
very interested if so.

~~~
alberteinstein
Yes, it does. Kubernetes can perform rolling updates [1].

One scenario is that when you rollout a new version, the old container will
only be killed when new one is up and running. If there are many replicas
running, Kubernetes will replace them with new versions one by one. And all of
this behaviour is highly configurable.

[1] [https://kubernetes.io/docs/tutorials/kubernetes-
basics/updat...](https://kubernetes.io/docs/tutorials/kubernetes-
basics/update-intro/)

~~~
rid
Thanks! I think for our scenario it's a bit overkill as we're only running one
container and pm2 seems a better fit for that.

It would be interesting to know how this works under the hood though, is this
just built on top of docker swarm load balancing?

~~~
alberteinstein
Kubernetes will definitely be an overkill if you run only one container. But,
if your application follows microservice architecture and have multiple
containers, Kubernetes is the best solution to run them.

Kubernetes architecture is entirely different from that of Docker Swarm.

A Kubernetes Service object [1] can load balance traffic to various Kubernetes
Pods (think containers) [2] as defined by it's " pod selectors". It can choose
to direct traffic only to running containers.

[1] [https://kubernetes.io/docs/concepts/services-
networking/serv...](https://kubernetes.io/docs/concepts/services-
networking/service/) [2]
[https://kubernetes.io/docs/concepts/workloads/pods/pod/](https://kubernetes.io/docs/concepts/workloads/pods/pod/)

------
snorremd
I like the new multi stage Dockerfile concept in the later Docker versions. It
makes it easy to define Dockerfiles that both builds and runs your code, while
still separating build and run dependencies. I have used this for both Node
and Go projects. As long as the build output is not too spread out in terms of
paths copying your build artifacts over to the other Dockerfile stages should
not be a big issue.

------
voidr
> As you can see above, we are using the npm package serve to serve static
> files.

I would recommend using nginx or apache instead. You get more performance and
better configurability.

------
Kiro
> root@id:/app# nodemon src/server.js

Why is this needed when "CMD [ "nodemon", "server.js" ]" already exists?

~~~
praveenweb
CMD will work in detached mode. i.e - "docker run -d". In an interactive mode
i.e - "docker run -ti", you can land up with shell access of the container,
where you can run commands / perform other tasks as required. In this case, we
are running nodemon.

------
alsadi
or not writing dockerfiles at all, using stock docker images. as in this
article [http://engineering.opensooq.com/on-the-fly-ad-hoc-docker-
com...](http://engineering.opensooq.com/on-the-fly-ad-hoc-docker-compose-
development-stack/)

~~~
praveenweb
Instead of writing Dockerfiles, this approach requires me to write a docker-
compose.yaml file. How is it more advantageous?

------
mfontani
> Using an appropriate base image (carbon for dev, alpine for production).

Isn't using the same container everywhere the actual selling point for
containers?

~~~
praveenweb
Depends on the use-case. For development, you would need to install some
system dependencies, access to bash for debugging etc and hence the full blown
base image. In production, you want the image sizes to be minimal and hence
alpine base image. Also reduces the attack vector.

~~~
solatic
But why are you doing minute-to-minute development within a container? What's
so hard about installing the official Node package for your development
system's distribution? When the developer is finished working on a feature,
then and only then build a production Docker image from the codebase that now
implements that feature, maybe run a few smoke tests, and push to CI. Building
special containers for development purposes is sheer madness.

~~~
pintxo
I am developing in a container - most of the time - as this allows me to run
the (micro-)service within it's defined ecosystem, so no need to mock any
service boundaries.

Note: New services and major functionalities are usually developed locally
with unit-tests ensuring compliance with the spec. But once this is done and
stuff needs to be integrated with the real system - or debugged later on -
there is IMHO nothing that can beat a complete copy of the real system running
locally where one can easily manipulate whatever is needed.

------
JohnnyConatus
When does it make sense to run Node on docker vs Heroku dynos or something
similar? Is it just a cost thing at a certain number? Are these significantly
specialized containers? The types mentioned in the doc seem like basic web
apps.

I've used Docker for things like complicated java dev environments. But it
seems over-applied for basic web apps.

~~~
praveenweb
> But it seems over-applied for basic web apps.

Generally your app will be composed of multiple services (like Java, Node.js,
some database etc) and there are benefits to run them as microservices
(isolation, easy to develop, easy to scale) and hence Docker makes sense in
those use cases. Though the app seems basic and overkill to use Docker, the
architecture enforces this and adds the above mentioned benefits.

