Can not start nestjs project on the k8s server - node.js

I am working on a nestjs project, this project can start by running, and works well :
npm run start:test
However, if I deploy the project to k8s, it will show :
[10:44:59 PM] Starting compilation in watch mode...
[10:45:22 PM] Found 0 errors. Watching for file changes.
and never show that it is running.
here is my docker :
FROM node:16 AS builder
# Create app directory
WORKDIR /app
ENV NODE_ENV='prod'
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY tsconfig.build.json ./
COPY tsconfig.json ./
COPY ca-cert.pem ./
COPY prisma ./prisma/
COPY protos ./protos/
# Install app dependencies
RUN npm install
RUN npx prisma generate
COPY . .
RUN npm run build
FROM node:16-alpine
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/protos ./protos
COPY --from=builder /app/tsconfig.build.json ./
COPY --from=builder /app/tsconfig.json ./
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/ca-cert.pem ./
EXPOSE 3190
CMD [ "npm", "run", "start:test" ]
here is the package.json :
"start:test": "nest start --watch",
"start:dev": "set NODE_ENV=dev && nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": " nest start",
Am I missing anything?

you should not watch the file in Kubernetes it just creates overhead, plus make sure there is proper healthcheck and memory request.
so better to change cmd to
CMD [ "npm", "run", "start" ]
but why we need to build the same package again? its pretty heavy process to build, so better to just consume dist
nest start simply ensures the project has been built (same as nest build), then invokes the node command in a portable, easy way to execute the compiled application.
NestJs already provides the correct command start:prod, but I (and Google) assumed the naked start was the call to invoke. Probably others make the same assumption. So do this instead:
{
"start": "npm run start:prod",
"start:prod": "node dist/main",
}
How to get back to point 0 of memory consumption with NestJs
If you still want to work with the existing setup then make sure these
resources:
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
requests:
cpu: 1000m
memory: 2000Mi

Related

Why is my Docker Image so large for AstroJS app?

I'm working on a webpage built with Astrojs. I'm fairly newbie as a frontend developer, and definitely not a full expert in Docker, but my current working folder is 270MB in size, dependencies included, yet when i build the docker image it gets to 1.32GB
This is my package.json in case it helps
{
"name": "personalsite",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"#astrojs/image": "^0.5.1",
"#astrojs/svelte": "^1.0.0",
"#astrojs/tailwind": "^1.0.0",
"svelte": "^3.50.1",
"#fortawesome/free-brands-svg-icons": "^6.2.0",
"#fortawesome/free-solid-svg-icons": "^6.2.0",
"#tailwindcss/typography": "^0.5.7",
"astro": "^1.2.1",
"autoprefixer": "^10.4.8",
"daisyui": "^2.25.0",
"postcss": "^8.4.16",
"svelte-fa": "^3.0.3"
}
}
This is my DockerFile
FROM node:lts-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN yarn install
COPY . .
EXPOSE 3000
RUN chown -R node /usr/src/app
USER node
CMD ["yarn", "run", "start", "--host"]
I've even used the alpine image for Node.js, but it still seems so large for me.
Do you know what might be the issue here?
EDIT: I followed the tips from the users in the comments and got a multistage dockerfile, but the image size is still kinda large? Now it's 654MB in size.
I know it's a big improvement but I'm still confused as how can this be still so large, since the source code is 60KB in size (it's just a small personal porfolio site with a couple animations)
This is the new updated dockerfile, did I miss something?
FROM node:lts as builder
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN yarn install --silent --production=true --frozen-lockfile
COPY . .
FROM node:lts-slim
# I used slim because there were people online who recommended to not
# mix and match distros and lts-alpine uses a different linux distro
WORKDIR /usr/src/app
COPY --from=builder /usr/src/app /usr/src/app
EXPOSE 3000
RUN chown -R node /usr/src/app
USER node
CMD ["yarn", "run", "start", "--host"]
I realized that, for my use case, I just wanted the output of astro build which is a bunch of static html files with some javascript on certain parts, since I don't have server side rendering enabled.
I just changed completely the Dockerfile to the following and now it's really small, only 9.81MB
# ---> Build stage
FROM node:18-bullseye as node-build
ENV NODE_ENV=production
WORKDIR /usr/src/app
COPY . /usr/src/app/
RUN yarn install --silent --production=true --frozen-lockfile
RUN yarn build --silent
# ---> Serve stage
FROM nginx:stable-alpine
COPY --from=node-build /usr/src/app/dist /usr/share/nginx/html

Couldn't find a `pages` directory with Next.js and customer server build

I'm currently trying to create a two stage build for an application that runs with Next.js (version 12.0.6) and has a custom server for an API. I do not want to rebuild the app each single time docker runs so I copy the compiled files into the /app directory. I'd assume that the pages directory would work automatically from .next folder but unfortunately it doesn't.
While the app builds all fine, I am plagued by this issue:
Error: > Couldn't find a `pages` directory. Please create one under the project root
at Object.findPagesDir (/app/node_modules/next/dist/lib/find-pages-dir.js:33:11)
at /app/node_modules/next/dist/build/index.js:113:45
at async Span.traceAsyncFn (/app/node_modules/next/dist/trace/trace.js:74:20)
at async Object.build [as default] (/app/node_modules/next/dist/build/index.js:82:25)
My Docker file looks like this:
FROM node:lts-slim AS base
WORKDIR /base
COPY package*.json ./
RUN npm install
COPY . .
FROM base AS build
ENV NODE_ENV=production
WORKDIR /build
COPY --from=base /base ./
CMD mkdir ./pages
RUN npm run build
FROM node:lts-slim AS production
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /build/next.config.js ./
COPY --from=build /build/package*.json ./
COPY --from=build /build/.next ./.next
COPY --from=build /build/public ./public
COPY --from=build /build/dist ./dist
# RUN mkdir pages; < uncommenting this will silence the error but it's an empty fake directory
RUN npm install
EXPOSE 3000
CMD npm run start
I start the Next.js sever like this (also tried with hardcoded false for the dev property with no success):
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
And the npm commands in the project are as follows:
"scripts": {
"prestart": "next build",
"dev": "NODE_ENV=development ts-node --project tsconfig.server.json server/index.ts",
"next-dev": "next dev",
"build:server": "tsc --project tsconfig.server.json",
"build:next": "next build",
"build": "npm run build:next && npm run build:server",
"next-start": "next start",
"start": "NODE_ENV=production node ./dist/server/index.js",
"test": "jest"
},
Things I have tried so far:
Fiddling with the dir directory when creating the server. Unfortunately it seems to be correct by default pointing to the /app directly and changing it doesn't help
Fiddling with the distDir in next.config. Unfortunately the same result as above
Creating an empty pages folder in /app. This doesn't seem to work - pages are loaded from that folder and since there is nothing inside everything yields 404.
Copying the entire build into app. While this creates pages folder and works, it completely defeats the purpose of a multi-stage build.
Copying only pages folder. This fails miserably as it has dependencies on all of the other folders.
Could anyone please direct me on how to tackle this? I simply ran out of ideas and hopefully I'm missing something silly here.
Thanks!
Answering this myself as I got it to "work" and it might help someone else.
For a reason which seems to be beyond my understanding, creating a separate run script in package.json stopped Next.js to recompile itself whenever the app starts.
To reiterate on the steps taken:
Using npm start caused the entire solution to recompile.
Creating a new run line in package.json (I called it npm run go) prevents this from happens. Server spins up perfectly from the compiled js files and runs immediatelly.
It works but I have no idea why. If anyone has any insight I'm more than happy to hear it!

Dockerfile how to make start command different between dev and prod?

I have a TypeScript Node app. I have a dev and start npm scritps:
"dev": "ts-node-dev src/index.ts",
"build": "npm run test:ci && tsc",
"start": "node dist/index"
When developing I watch changes on the .ts files and when running in production I want to run the .js files from the dist dir (which is generated using the npm build script).
This is my Dockerfile:
FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm i --only=prod
COPY . .
CMD ["npm", "run", "dev"]
When its running on dev env its good, but on production the CMD command should be like that:
CMD ["npm", "start"]
Also the RUN npm i --only-prod command also needs to be changed respectively.
How to make it adjustable to dev vs prod?
In kubernetes you can overwrite the default command args:
apiVersion: apps/v1
kind: Deployment
[...]
spec:
template:
spec:
containers:
- name: CONTAINER-NAME
image: IMAGE-NAME
args: [
"npm",
"start" ]
See the kubernetes documentation.
The detailed implementation depend on the deployment system you're using:
You can write two different .yaml files, one for the development and one for the production environment.
If you're deploying with helm, you can set this configuration in a value file per environment.
You can also use Kustomize as described in this example.

Docker typescript node.js can't start app?

I have an index.ts, then this is my script
"scripts": {
"test": "jest",
"start": "ts-node-dev ./index.ts"
},
I tried to dockerize it, what should I do? do I need to add another command for npm build? or generate a .js file?
my dockerfile like this
FROM node:10-alpine
WORKDIR /
# copy configs to /app folder
COPY package*.json ./
COPY tsconfig.json ./
COPY . .
# check files list
RUN ls -a
RUN npm install
EXPOSE 3001
CMD [ "npm", "start"]
I can't access localhost:3001 in my browser after I run
docker build -t testApp .
then
docker run -p 80:3001 testApp

Dockerfile to run nodejs static-content in docker container

Need an advice to dockerize and run a node JS static-content app on K8s cluster.
I have a static web-content which I run "npm run build” into the terminal which generates /build and direct my IIS webserver to /build/Index.html.
Now, I started creating a Docker file, how do I point my nodeJS image to invoke /build/Index.html file
FROM node:carbon
WORKDIR /app
COPY /Core/* ./app
npm run build
EXPOSE 8080
CMD [ "node", ".app/build/index.html" ]
Please how can I run this app only on node v8.9.3 and
npm 5.6.0 ?
Any inputs please ?
You can specify the version of node specifically:
FROM node:8.9.3
Assumptions:
package.json is under Code directory.
npm run build will be running outside of the container and a build directory will be created in Code directory.
We will copy the whole Code/build directory under /app directory of the container.
We will copy package.json to /app folder and will run the website through scripts available in package.json file.
Solution:
I would say add a script named start in the package.json and call that script from Dockerfile's CMD command. The script would look like:
"scripts": {
"start": "node ./index.html",
},
And the Dockerfile would look like:
FROM node:8.9.3
# Make app directory in the container.
RUN MKDIR /app
# Copy whole code to app directory.
COPY Code/build/ /app
# Copy package.json app directory.
COPY package.json /app
# make app directory as the working directory.
WORKDIR /app
# Install dependencies.
RUN npm install -only=production
# Expose the port
EXPOSE 8080
# Start the process
CMD ["npm", "start"]

Resources