fix: Use node image instead of curl download#256
Conversation
|
|
||
| # Copy plugins folder and install dependencies | ||
| COPY ./plugins /app/plugins | ||
| RUN chown -R nonroot:nonroot /app/plugins | ||
| WORKDIR /app/plugins | ||
| RUN pnpm install --frozen-lockfile |
There was a problem hiding this comment.
After installing switch back to nonroot user that way the app will run as nonroot instead of root
There was a problem hiding this comment.
Using the node image will bring an image with unnecessary stuff in it. After the tests I did (just after the alpha release of monitors), the smallest working image was using the wolfi image (check here for monitors example).
In the example linked, just like we pinned python's version, it should also be possible to pin node's version (it may be simpler to pin the major one, as it should always have the latest patched and verified one in the wolfi repo).
There was a problem hiding this comment.
The reason for using the Node image is to support plugins. As far as I know, it's based on Wolfi with only the minimal software required to run. I haven't done the POC, but installing Node manually would likely result in a larger image.
One improvement we can make is adopting a two-step build process. We could use node:latest-dev (which includes pnpm) to build the plugin components into CJS output, and then switch to node:22-slim for the final image. Since we won't need ts-node, typescript, or pnpm at runtime, this would reduce the final image size to ~51MB with a pinned version.
While the Wolfi base is only ~6.5MB, node:latest-dev is ~262MB, so ~51MB seems reasonable.
There was a problem hiding this comment.
Here is a quick comparison:
Wolfie Base: 134MB (without node)
Node latest: 185MB (doesn’t include pnpm, ts-node, typescript)
Manual Installation: 204MB (doesn’t include pnpm, ts-node, typescript)
This has been done in macOS with targeting Linux amd64
There was a problem hiding this comment.
The reason for using the Node image is to support plugins. As far as I know, it's based on Wolfi with only the minimal software required to run. I haven't done the POC, but installing Node manually would likely result in a larger image.
One improvement we can make is adopting a two-step build process. We could use
node:latest-dev(which includes pnpm) to build the plugin components into CJS output, and then switch tonode:22-slimfor the final image. Since we won't needts-node,typescript, orpnpmat runtime, this would reduce the final image size to ~51MB with a pinned version.While the Wolfi base is only ~6.5MB,
node:latest-devis ~262MB, so ~51MB seems reasonable.
I think this is making the process more complex than required. Later on, if we want to also allow plugins in another language (e.g: python) we will need to add one more image to the build step. Let's also keep in mind that the OSS (non-subscription) set of chainguard images do not allow to specify some versions (e.g: node will always be their latest tag), so support efforts will be something to take into consideration.
With the case of the base wolfi-base image, we can pin the version down (as long as it exists in their repos - they usually have the stable and the latest to pick from). what we need to be careful is to remove the apk toolset as a last step on the image building process, reducing further the fingerprint and the attack surface.
There was a problem hiding this comment.
Let's also keep in mind that the OSS (non-subscription) set of chainguard images do not allow to specify some versions (e.g: node will always be their latest tag), so support efforts will be something to take into consideration.
This is something I discussed with @MCarlomagno. Marcos mentioned we could just add it to docs "for every release we will have latest node release" which i'm ok with. Yes things will break but it's the same as using chainguard latest images...we either have to pay them for images for specific tags or just live with knowing things will break. My opinion we should just use images other than chainguard and take care of security ourselves that way we pin to specific tags and digests.
There was a problem hiding this comment.
I think this is making the process more complex than required. Later on, if we want to also allow plugins in another language (e.g: python) we will need to add one more image to the build step
100% agree this is a problem, One of the reason why i thought sidecar is a better option for plugins
There was a problem hiding this comment.
I think this is making the process more complex than required. Later on, if we want to also allow plugins in another language (e.g: python) we will need to add one more image to the build step
100% agree this is a problem, One of the reason why i thought sidecar is a better option for plugins
Sidecar of shared volume - in monitor we support python, bash and TS scripts and it works perfectly fine (shared volume).
There was a problem hiding this comment.
Let's also keep in mind that the OSS (non-subscription) set of chainguard images do not allow to specify some versions (e.g: node will always be their latest tag), so support efforts will be something to take into consideration.
This is something I discussed with @MCarlomagno. Marcos mentioned we could just add it to docs "for every release we will have latest node release" which i'm ok with. Yes things will break but it's the same as using chainguard latest images...we either have to pay them for images for specific tags or just live with knowing things will break. My opinion we should just use images other than chainguard and take care of security ourselves that way we pin to specific tags and digests.
That's something to take up with @son-oz . All in all, it won't be a big deal using the wolfi image and pinning the node version (it was done for monitors). As wolfi is the base image for chainguard, it will always have a few different major versions of node, allowing more flexibility. Going with a generic image and trimming it down/patching everything ourselves will be more hassle than what's worth in the mid/long term.
I'll try to get some time on the side to present a proposal for the image.
| USER root | ||
| RUN npm install -g pnpm ts-node typescript | ||
|
|
||
| # Copy plugins folder and install dependencies | ||
| COPY ./plugins /app/plugins | ||
| RUN chown -R nonroot:nonroot /app/plugins | ||
| WORKDIR /app/plugins | ||
| RUN pnpm install --frozen-lockfile |
There was a problem hiding this comment.
I've added USER nobody at the end, I think node stage does not include "nonroot" user
Dockerfile.production
Outdated
| FROM --platform=${BUILDPLATFORM} cgr.dev/chainguard/glibc-dynamic:latest | ||
|
|
||
| # Node image | ||
| FROM cgr.dev/chainguard/node:latest-dev AS node |
There was a problem hiding this comment.
I think we should use latest instead. Maybe both in dev and prod images.
There was a problem hiding this comment.
The advantage of the -dev versions is that they come with some extra tools or debugging modes enabled, making it easier to troubleshoot. The downside is that, if run in production, the attack surface will be a bit bigger (and also the final image size).
|
|
||
| # Node image | ||
| FROM cgr.dev/chainguard/node:latest-dev AS node | ||
|
|
There was a problem hiding this comment.
We could also add:
ENV NODE_ENV=production
Dockerfile.development
Outdated
| RUN npm install -g pnpm ts-node typescript | ||
|
|
||
| # Copy plugins folder and install dependencies | ||
| COPY ./plugins /app/plugins |
There was a problem hiding this comment.
It should probably be: COPY --chown=nonroot:nonroot ./plugins /app/plugins so we are sure the user is correct.
There was a problem hiding this comment.
Using --chown=nobody as nonroot doesnt exist at that stage
d-carmo
left a comment
There was a problem hiding this comment.
User is nonroot for chainguard images.
Dockerfile.production
Outdated
| # Setting up build directories | ||
| FROM --platform=${BUILDPLATFORM} cgr.dev/chainguard/glibc-dynamic:latest |
There was a problem hiding this comment.
We can remove this, looks like it's not being used
@MCarlomagno @tirumerla this is what I had in mind. We can pin down the node version (I left the patch version out of the pinning - it will ensure it always installs the latest patch level) and makes it easier to add support for other stuff in the future (e.g: python, perl, java, bash, ...) with no need to add layers of images. |
|
Closing in favor of #266 |

Summary
Installs nodejs through the chainguards node image instead of using curl download for installation
Testing Process
Run
Go to docker terminal and run
Checklist