nodejs / docker-node

Official Docker Image for Node.js :whale: :turtle: :rocket:
https://hub.docker.com/_/node/
MIT License
8.26k stars 1.97k forks source link

Image without npm nor yarn #404

Open rubennorte opened 7 years ago

rubennorte commented 7 years ago

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).

LaurentGoderre commented 7 years ago

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.

chorrell commented 7 years ago

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.

rubennorte commented 7 years ago

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.

chorrell commented 7 years ago

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

pesho commented 7 years ago

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.

rubennorte commented 7 years ago

@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.

pesho commented 7 years ago

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

Starefossen commented 7 years ago

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 😜

valeriangalliat commented 7 years ago

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).

Daniel15 commented 7 years ago

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.

chorrell commented 6 years ago

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?

pesho commented 6 years ago

And what would we call the variant?

A few suggestions: production, micro, tiny, nano

I like the idea in general.

LaurentGoderre commented 6 years ago

The complexity this would bring kind of scares me.

LaurentGoderre commented 6 years ago

@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.

chorrell commented 6 years ago

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 commented 6 years ago

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

LaurentGoderre commented 6 years ago

How much space is saved on Debian?

Daniel15 commented 6 years ago

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 notifications@github.com 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 .

chorrell commented 6 years ago

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 commented 6 years ago

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.

bobbui commented 6 years ago

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

bnb commented 6 years ago

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

SimenB commented 6 years ago

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

bnb commented 6 years ago

@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 💚

addaleax commented 6 years ago

@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:).

hulkish commented 6 years ago

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

mhdawson commented 6 years ago

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

mhdawson commented 6 years ago

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.

markmsmith commented 5 years ago

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.

SkeLLLa commented 5 years ago

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.

astefanutti commented 5 years ago

FWIW, I'm maintaining scratch Node images at https://github.com/astefanutti/scratch-node. The compressed size is around 14MB for Node 12.

SkeLLLa commented 5 years ago

@astefanutti that's very nice. I've created static built based on your idea with scratch. And with upx it became even smaller: ~12MB. You may apply upx packer in your repo as well. Also I've created autobuild repo, so if anyone interested: https://hub.docker.com/r/m03geek/alpine-node. Those are not well tested, but they should work.

If anyone here is interested in porting that into official repo - feel free to take, test, enhance the code (repo link is in docker repo description).

Rush commented 4 years ago

Agree it would be useful!

chorrell commented 4 years ago

fwiw, I experimented a bit with this over here using GitHub Actions to build a statically compiled node: https://github.com/chorrell/docker-node-minimal

hubgit commented 3 years ago

Building the node:14-alpine image without npm or yarn reduces the image size from 116MB to 86MB, most of which is the 71MB node binary.

gaby commented 2 years ago

Any news on this? Issue was created over 3 years ago :-S

marinpurgar commented 2 years ago

Almost one year in since last bump and still no info about minimal node base images suited for production workloads?

mknj commented 7 months ago

We currently struggle because Microsoft Defender for Cloud flags all our Production builds because of an outdated dependency in npm (-> ip@2.0.0).

LaurentGoderre commented 6 months ago

I created the debian core image in #2058