Using Docker with nodejs with node-gyp dependencies

Viewed 15584

I'm planning to use Docker to deploy a node.js app. The app has several dependencies that require node-gyp. Node-gyp builds these modules (e.g. canvas, lwip, qrcode) against compiled libraries on the delivery platform, and in my experience these builds can be highly dependent on the o/s version and libraries installed, and they often break a simple npm install.

So is building my Dockerfile FROM node:version the correct approach? This seems to be the approach shown in every Docker/Node tutorial I've found so far. But if I build from a node image, what will happen when I deploy the container? How can I ensure the target host will have the libraries needed to compile the node-gyp modules?

The other way I'm looking at is to build the Dockerfile FROM ubuntu:version. But I think this would mean installing nodeJS into the Ubuntu image and the whole thing would be much larger.

Are there other ways of handling this?

3 Answers

If you need to build stuff using node-gyp, you can add the line below, replacing your npm install or yarn install:

RUN apk add --no-cache --virtual .build-deps make gcc g++ python \
RUN npm install --production --silent \
RUN apk del .build-deps

Or even simpler, you can install alpine-sdk which is similar to Debian's build-essentials

RUN apk add --no-cache --virtual .build-deps alpine-sdk python \
RUN npm install --production --silent \
RUN apk del .build-deps

Source: https://github.com/mhart/alpine-node/issues/27#issuecomment-390187978

Looking back (2 years later), managing node dependencies in a container is still a challenge. What I do now is:

  1. Build the docker image FROM node:10.16.0-alpine (or other node version). These are official node images on hub.docker.com. Docker recommends alpine, and Nodejs builds on top of that, including node-gyp, so it's a good starting point;

  2. Include a RUN apk add --no-cache to include all the libraries needed to build the dependent module, e.g. canvas (see example below);

  3. Include a RUN npm install canvas in the docker build file; this builds the node module (e.g. canvas) into the docker image, so it gets loaded into any container run from that image.

But this can get ugly. Alpine uses different libraries from more heavy-weight OS's: notably, alpine uses musl in place of glibc. The dependent module may need to link to glibc, so then you would have to add it to the image. Sasha Gerrand offers one way to do it with alpine-pkg-glibc

Example installing node-canvas v2.5, which links to glibc:

#  geo_core layer
#  build on a node image, in turn built on alpine linux, Docker's official linux pulled from hub.docker.com

FROM node:10.16.0-alpine

#  add libraries needed to build canvas
RUN apk add --no-cache \
    build-base \
    g++ \
    libpng \
    libpng-dev \
    jpeg-dev \
    pango-dev \
    cairo-dev \
    giflib-dev \
    python \
    ; \

#  add glibc and install canvas
RUN apk --no-cache add ca-certificates wget  && \
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
    wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk && \
    apk add glibc-2.29-r0.apk && \
    npm install canvas@2.5.0
    ;
Related