docker-node icon indicating copy to clipboard operation
docker-node copied to clipboard

Image without npm nor yarn

Open rubennorte opened this issue 7 years ago • 39 comments

Now that Docker supports multi-stage builds it'd be nice to have an official base image with just the Node.js binary. A build image could extend from the current images and a production image could extend from the standalone version (copying the built application with all installed dependencies from the build container).

rubennorte avatar May 16 '17 13:05 rubennorte

I agree but we use the official distribution which include npm. The core node doesn't have a version of node without npm, as far as I am aware.

LaurentGoderre avatar May 16 '17 13:05 LaurentGoderre

Yeah, that would be great. I think if we do that we'd have to somehow build node without npm. Or convince the build group that we need that as another option.

It might make sense to do more of a clean break and create a new node-minimal image that better fits this use case.

chorrell avatar May 16 '17 14:05 chorrell

I just saw that https://github.com/mhart/alpine-node provides that kind of image (with the Node binary only). Maybe you can get some ideas from there.

rubennorte avatar May 16 '17 16:05 rubennorte

Yeah they build node with --without-npm. We could probably do the same thing but the build and release would take longer.

chorrell avatar May 16 '17 17:05 chorrell

Well, theoretically we could just rm -rf /usr/local/lib/node_modules/npm/ /usr/local/bin/npm after extracting the Node tarball. ~~I don't see much value in pursuing that though.~~

pesho avatar May 16 '17 17:05 pesho

@pesho I do see the value. You'd be reducing the image size by 40% (22MB from 54MB belong to npm and yarn in the Node 6.10 image). In some environments that's important.

rubennorte avatar May 16 '17 17:05 rubennorte

@rubennorte I spoke too soon before. Indeed, there is value in having a minimal image without package managers for production.

pesho avatar May 16 '17 17:05 pesho

I agree, a minimal image without npm nor yarn would be nice for production use cases. This only makes sens for the alpine variant (since the ones based on Debian are so huge anyways), and I think it can be solved as easy as rm -rf /path/to/npm.

Nice issue number #404 😜

Starefossen avatar May 18 '17 07:05 Starefossen

I had something in mind when multi-stage builds got introduced to make the smallest possible Node image:

# Dockerfile-alpine-minimal.template
FROM node:0.0.0-alpine AS builder
FROM alpine:0.0

COPY --from=builder /usr/local/bin/node /usr/local/bin/
COPY --from=builder /usr/lib/ /usr/lib/

CMD [ "node" ]

I'm not sure if it's OK to use multi-stage builds in docker-node yet, but I like the idea of using the previously built alpine image and multi-stage builds to make the minimal version (instead of having to compile Node again).

valeriangalliat avatar May 22 '17 11:05 valeriangalliat

The core node doesn't have a version of node without npm, as far as I am aware.

They used to have a statically-linked node binary that you could download just by itself with no other stuff. It looks like they only still have that for Windows (eg. https://nodejs.org/dist/v6.11.1/win-x64/node.exe), the only Linux downloads I could find still contain npm. Having said that, you could just use their Linux tarball and delete the npm directory and executable.

Daniel15 avatar Jul 14 '17 05:07 Daniel15

Is this worth revisiting as a variant? Doing rm -rf /usr/local/lib/node_modules/npm/ /usr/local/bin/npm is easy enough and it would be useful with multi-stage builds. And what would we call the variant?

chorrell avatar May 25 '18 03:05 chorrell

And what would we call the variant?

A few suggestions: production, micro, tiny, nano

I like the idea in general.

pesho avatar May 25 '18 14:05 pesho

The complexity this would bring kind of scares me.

LaurentGoderre avatar May 25 '18 14:05 LaurentGoderre

@valeriangalliat I like your approach but in the current setup, that would mean building the alpine images twice because different images are not aware of each other.

LaurentGoderre avatar May 25 '18 14:05 LaurentGoderre

I think we should only do this for Debian, something like:

FROM debian:jessie-slim

RUN groupadd --gid 1000 node \
  && useradd --uid 1000 --gid node --shell /bin/bash --create-home node

# gpg keys listed at https://github.com/nodejs/node#release-team
RUN set -ex \
  && for key in \
    94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
    FD3A5288F042B6850C66B31F09FE44734EB7990E \
    71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
    DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
    C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
    B9AE9905FFD7803F25714661B63B535A4C206CA9 \
    56730D5401028683275BD23C23EFEFE93C4CFFFE \
    77984A986EBC2AA786BC0F66B01FBB92821C587A \
  ; do \
    gpg --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys "$key" || \
    gpg --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys "$key" || \
    gpg --keyserver hkp://pgp.mit.edu:80 --recv-keys "$key" ; \
  done

ENV NODE_VERSION 10.2.0

RUN buildDeps='ca-certificates curl xz-utils' \
    && ARCH= && dpkgArch="$(dpkg --print-architecture)" \
    && case "${dpkgArch##*-}" in \
      amd64) ARCH='x64';; \
      ppc64el) ARCH='ppc64le';; \
      s390x) ARCH='s390x';; \
      arm64) ARCH='arm64';; \
      armhf) ARCH='armv7l';; \
      i386) ARCH='x86';; \
      *) echo "unsupported architecture"; exit 1 ;; \
    esac \
    && set -x \
    && apt-get update && apt-get install -y $buildDeps --no-install-recommends \
    && rm -rf /var/lib/apt/lists/* \
    && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
    && curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
    && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
    && grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
    && tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
    && rm -rf /usr/local/lib/node_modules/ \
    && rm -rf /usr/local/bin/npm \
    && rm -rf /usr/local/bin/npx \
    && rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
    && apt-get purge -y --auto-remove $buildDeps \
    && ln -s /usr/local/bin/node /usr/local/bin/nodejs

CMD [ "node" ]

The above image is about 125MB

(edit: updated to delete /usr/local/lib/node_modules/)

chorrell avatar May 25 '18 15:05 chorrell

Also, for a variant name, I kind of like node:core, node:10-core etc.

chorrell avatar May 25 '18 15:05 chorrell

How much space is saved on Debian?

LaurentGoderre avatar May 25 '18 15:05 LaurentGoderre

You can delete the entire node_modules directory, not just the npm directory. The only other modules that ship with Node.js are npm's dependencies.

Instead of installing Node.js and npm and then deleting npm, could you just avoid installing npm in the first place? I think the Node.js site has a statically compiled version of Node.js with no npm (just the Node.js executable), and I believe Debian packages Node.js and npm in two separate packages.

Sent from my phone.

On Fri, May 25, 2018, 8:02 AM Christopher Horrell [email protected] wrote:

Also, for a variant name, I kind of like node:core, node:10-core

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/nodejs/docker-node/issues/404#issuecomment-392086064, or mute the thread https://github.com/notifications/unsubscribe-auth/AAFnHTq78qfmFrNjk4vx1TZyRxfj507kks5t2Bz4gaJpZM4Ncdol .

Daniel15 avatar May 25 '18 15:05 Daniel15

We're looking at a ~60MB difference using the rm -r approach:

[chorrell:~/GitHub/docker-node-main] master(+6/-6)* ± docker images | grep 10.2.0-slim
node                10.2.0-slim         c164ad185a38        20 hours ago        184MB

vs

[chorrell:~/GitHub/docker-node-main] master(+6/-6)* ± docker images | grep 10.2.0-core
node                10.2.0-core         68519b454a55        3 minutes ago       125MB

chorrell avatar May 25 '18 17:05 chorrell

Note that I don't think the statically compiled versions are available, or at least I cannot find them at https://nodejs.org/dist/ for v10. And we would want versions for each of the architectures we support too.

chorrell avatar May 25 '18 17:05 chorrell

love this idea, @chorrell how soon this can be released?

bobbui avatar Jun 23 '18 20:06 bobbui

Seems like this may be worth putting on the TSC agenda to see how the core project can help out with this 🤔

bnb avatar Jul 02 '18 16:07 bnb

@chorrell is currently working on it (see references). But if you think it's worth it feel free :)

SimenB avatar Jul 02 '18 18:07 SimenB

@SimenB definitely think it's worth it – the fact that y'all are getting the work done is super valuable if Node.js core were to do the same 💚

bnb avatar Jul 09 '18 18:07 bnb

@bnb Feel free to put this on the TSC agenda by adding the label to it and/or mentioning it in the current TSC meeting issue (https://github.com/nodejs/TSC/issues/565) – ideally with a short summary of the question you’d like to see answered, which decision you’d like to see made, or what you’d like to raise awareness about. (It’s not super-clear from this thread for me, so that’s why I’m mentioning it :slightly_smiling_face:).

addaleax avatar Jul 09 '18 18:07 addaleax

Would really like to see this as well. It's actually far-preferred to not involve global dependencies at all for your production apps.

hulkish avatar Jul 15 '18 18:07 hulkish

A potentially related issue: https://github.com/nodejs/Release/issues/341. The discussion is related to having different versions, some without extras.

mhdawson avatar Jul 16 '18 22:07 mhdawson

Discussed in last TSC meeting, but this should not block this issue and ongoing discussion about whether we should do something on the release side will continue in: https://github.com/nodejs/Release/issues/341. Removing from TSC agenda.

mhdawson avatar Aug 01 '18 14:08 mhdawson

FWIW, I would like to add a vote in favor of this work, including providing an alpine-based image without npm.

My needs are:

  1. For the 1st stage of the app build, I'm using yarn, so don't need npm (but it's not the end of the world if it's there).
  2. More importantly, when I'm creating the production docker image in the 2nd stage of the dockerfile, I don't need any package manager at all, and want the attack surface to be as small as possible. Making the image smaller is just a nice bonus.

I would really like to see an official image, since it's not obvious to someone like myself which things are necessary vs what can safely be deleted when pairing down a larger image, or which specific combination of flags need to be set to build it from source without npm, while still allowing native modules to work.

If one of the main obstacles is still the naming convention for the release, it would be nice if there could be a simple vote with github reactions by the maintainers so we could be unblocked. The suggestion of -nopm on the PR seemed pretty good to me.

Thanks.

markmsmith avatar Apr 17 '19 21:04 markmsmith

I've made some tests. Using multi-stage docker build and node static build I was able to reduce node alpine image size by two times. And upx-packed is four times less size.

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
node                alpine              d97a436daee9        8 days ago          79.3MB
mhart/alpine-node   slim                b8c1832fbf86        8 days ago          44.5MB
m03geek/alpine-node latest              4ed96bad5c6f        10 minutes ago      38.7MB
m03geek/alpine-node upx-latest          c3e52e2954ac        22 seconds ago      18.6MB

You can find dockerfile in my repo https://github.com/SkeLLLa/alpine-node, feel free to use it if needed.

SkeLLLa avatar Aug 02 '19 21:08 SkeLLLa