Issue with running node ts project with docker - node.js

I just started learning Docker and i'm trying to run my node ts app with few simple commands. Already googled and tried a lot of things but still no success. App works perfectly when i run it directly from terminal, so mistake might only be in the Dockerfile, nothing else crossed my mind.
It always builds successfully, but breaks when i try to run.
FROM node:14.17.1-alpine
# Install base packages
RUN apk update
RUN apk upgrade
# Change TimeZone
RUN apk add --update tzdata
ENV TZ Europe/Berlin
# Clean APK cache
RUN rm -rf /var/cache/apk/*
WORKDIR /app
COPY package*.json ./
RUN npm i
COPY . .
ENV NODE_ENV=production
CMD ["node","src/app.ts"]
Any help would be really appreciated,
Thanks

Typscript app cannot be run with Node. You need to compile .ts files to .js files and then you can run .js file with Node.
For more detailed you can checkout a blogpost on how to containerize. For example: https://itnext.io/dockerize-a-typescript-app-in-15-mins-a0e8c1e904b3
But there is ts-node, REPL kind of thing for TS. You can add it in the package.json as devDependencies and then can do:
CMD ["npx", "ts-node", "src/app.ts"]
You can check more here: How to run TypeScript files from command line?
EDIT:
if you have tsconfig file then you can install tsc and run it in the Dockerfile to compile. Then you can run the compiled JS file in Node.
You can take hint from this: How to compile typescript in Dockerfile
RUN npm install tsc -g
RUN tsc
CMD ["node", "<urapppath>/app.js"]

Related

JS files built from typescript stops working if ran independently but not if ran from dist folder

So, this is a weird one but I'll try to explain. I have a typescript project (a discord.js bot to be exact). And I would like to have a dockerfile that builds and then runs said project.
I ended up with the following dockerfile, inspired by this post:
FROM node:16 AS builder
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
FROM node:16 AS production-prepare
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app/package.json /usr/src/app/yarn.lock ./
COPY --from=builder /usr/src/app/dist ./
RUN yarn install --production --frozen-lockfile
FROM gcr.io/distroless/nodejs:16
WORKDIR /app/src/app
COPY --from=production-prepare /usr/src/app ./
#This is correct for googles distroless image
CMD ["index.js"]
Now to me this should work, we build the typescript files to JS. We make sure we only have the required (production) packages, and we run it in a distroless image.
Now, the bot starts. However, it does not work correctly, certain commands do not function and some seemingly do, I haven't quite figured out why.
So I tried to debug this by doing a the same flow that the dockerfile does (as close as I can) by copying files and running the same command. So I copied the package.json and yarn.lock file to a new folder and ran yarn install --frozen-lockfile. I copied everything else (bar node_modules, TS dist folder, and my .env as those are in my .dockerignore) and ran yarn build (which is tsc --build).
I then made a second folder, copied the package.json and yarn.lock files from the first folder to there, copied everything from the first folders dist to there, and ran yarn install --production --frozen-lockfile and then tried doing node index.js.
Same result, the bot technically starts but doesn't work properly. However, if I go to the first folder and run node dist/index.js it does work properly! The only difference in packages is typescript and #types/glob (those are my only devDependencies).
So somewhere between running build with the original TS files, and moving the built JS files away, something breaks. And I am completely out of luck when it comes to figuring out what.

Patch Package with Docker

I'm working with a node js backend and I want to deploy the application via docker.
I changed two npm packages to get my application working and automatically install these changes with npm patch-package with the help of a post install script in my package.json.
"postinstall": "patch-package"
I installed both both postinstall-postinstall and patch-package as dev dependencies.
Running yarn install and yarn build seperatly this works fine but once I want to dockerize this application I get an error during the build phase, which basically says that the patch wasn't applied to the node_modules.
This is my dockerfile:
# stage 1
FROM node as builder
WORKDIR /srv
COPY package.json yarn.lock patches ./
RUN yarn install --frozen-lockfile --unsafe-perm
COPY . .
RUN yarn build
I don't really know if the yarn install script in the dockerfile is not running post install or if the error only happens in the yarn build script.
Thanks in advance
As #donjus mentioned in the comments, the patches were being copied to the root directory, not inside of patches.
The solution is to change:
COPY package.json yarn.lock patches ./
RUN yarn install --frozen-lockfile --unsafe-perm
to
COPY package.json yarn.lock ./
COPY ./patches ./patches
RUN yarn install --frozen-lockfile --unsafe-perm

What is the optimal way to exploit Docker multi-stage builds?

I'm using multi-stage builds in my Dockerfile (the first stage is a BUILD, and the second is the RUN).
I want to know if I should, in my second stage, copy the node_modules folder or if I should run an npm i. What is the optimal way ?
Note: All the apk packages that I install in the first stage are required to run npm ci properly (I had many errors : node-gyp, etc)
# Build container stage
FROM node:alpine AS BUILD_IMAGE
RUN apk --no-cache add -u --virtual build-dependencies \
g++ gcc libgcc libstdc++ linux-headers make python3
WORKDIR /app
COPY package*.json ./
RUN npm ci && npm clean cache --force && apk del build-dependencies
COPY . .
RUN npm run lint
RUN npm run tsc
RUN npm prune --production
# Run container stage
FROM node:alpine AS app
WORKDIR /app
COPY /package*.json ./
# Should I copy the `node_modules` folder or
# should I run an `npm i` ? What is the optimal method?
COPY --from=BUILD_IMAGE /app/dist ./dist
COPY --from=BUILD_IMAGE /app/node_modules ./node_modules
# Clean dev packages
EXPOSE 8080
# Run the container with a non-root User
USER node
CMD [ "node", "dist/src/app.js" ]
I always run npm install (ci) inside of the docker when I want to build the image, because if you copy node_modules from dev environment to docker image, depend on you OS you use for developing, many of the binary files inside node_modules are wrong ( Windows/mac => Linux ) also if you use ubuntu and your image is based on Alpine again you will have some problem.
The best option is always to make a layer just for node_modules build and after that, make an application layer based on your node_module layer => you will use cache and faster build.
Note: if you wanna copy node_modules on Windows use WSL2 (Ubuntu) and also build your images based on Ubuntu(Debian) then you don't worry about errors

'dist' folder is not generated while doing npm build in a Dockerfile

I usually write Dockerfiles for Java / Go applications and it's the first time I have encountered a situation where I have to write a Dockerfile for an already existing (and production running) Node.js application. As per my little knowledge about the Node.js which I acquired in the past couple of days, dist folder is generated after we build a Node.js project which carries the source code (please correct me if I am wrong here). So I am interested in copying the dist folder from parent Docker image to child Docker image.
However, after I copy everything from an application into my parent Docker image (line 6) and run 'npm run build' command, dist folder is not generated for me (please note the node_modules and package-lock.json are being generated).
My Dockerfile is as below:
FROM node:10-alpine as BUILD
WORKDIR /src
COPY package*.json /src
RUN apk add --update --no cache \
python \
make \
g++
RUN npm install
COPY . /src
RUN npm run build
How can I resolve this?
If you are using typescript in your node application then follow these instructions.
Please add the below entry under compilerOptions section on "tsconfig.json"
tsconfig.json
**"outDir": "./dist/"**
package.json - Add the below script too.
"scripts": { "build": "tsc" }
Now, re-run the "npm run build". You will see the dist folder.
Try by ignoring the 'tsconfig.tsbuildinfo' file.
don't copy this file into docker container.

How to shrink size of Docker image with NodeJs

I created new Angular2 app by angular-cli and run it in Docker.
At first I init app on my local machine:
ng new project && cd project && "put my Dockerfile there" && docker build -t my-ui && docker run.
My Dockerfile
FROM node
RUN npm install -g angular-cli#v1.0.0-beta.24 && npm cache clean && rm -rf ~/.npm
RUN mkdir -p /opt/client-ui/src
WORKDIR /opt/client-ui
COPY package.json /opt/client-ui/
COPY angular-cli.json /opt/client-ui/
COPY tslint.json /opt/client-ui/
ADD src/ /opt/client-ui/src
RUN npm install
RUN ng build --prod --aot
EXPOSE 4200
ENV PATH="$PATH:/usr/local/bin/"
CMD ["npm", "start"]
Everything is OK, problem is size of image: 939MB!!! I tried to use FROM: ubuntu:16.04 and install NodeJs on it (it works), but still my image has ~450 MB. I know that node:alpine exists, but I am not able to install angular-cli in it.
How can I shrink image size? Is it necessary to run "npm install" and "ng build" in Dockerfile? I would expect to build app on localhost and copy it to image. I tried to copy dist dir and and package.json etc files, but it does not work (app start fail). Thanks.
You can certainly use my alpine-ng image if you like.
You can also check out the dockerfile, if you want to try and modify it in some way.
I regret to inform you that even based on alpine, it is still 610MB. An improvement to be sure, but there is no getting around the fact that the angular compiler is grossly huge.
For production, you do not need to distribute an image with Node.js, NPM dependencies, etc. You simply need an image that can be used to start a data volume container that provides the compiled sources, release source maps and other assets, effectively no more than what you would redistributed with a package via NPM, that you can attach to your webserver.
So, for your CI host, you can pick one of the node:alpine distributions and copy the sources and install the dependencies therein, then you can re-use the image for running containers that test the builds until you finally run a container that performs a production compilation, which you can name.
docker run --name=compile-${RELEASE} ci-${RELEASE} npm run production
After you have finished compiling the sources within a container, run a container that has the volumes from the compilation container attached and copy the sources to a volume on the container and push that to your Docker upstream:
docker run --name=release-${RELEASE} --volumes-from=compile-${RELEASE} -v /srv/public busybox cp -R /myapp/dist /srv/public
docker commit release-${RELEASE} release-${RELEASE} myapp:${RELEASE}
Try FROM mhart/alpine-node:base-6 maybe it will work.

Resources