Puppeteer / docker (Target.setAutoAttach): Target is closed - node.js

There is a project in nestjs
build is produced in the docker
Dockerfile
###################
# BUILD FOR LOCAL DEVELOPMENT
###################
FROM --platform=linux/amd64 node:18-alpine As development
# Create app directory
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY --chown=node:node package*.json ./
# Install app dependencies using the `npm ci` command instead of `npm install`
ENV PUPPETEER_SKIP_DOWNLOAD=true
RUN npm ci
# Bundle app source
COPY --chown=node:node . .
# Use the node user from the image (instead of the root user)
USER node
###################
# BUILD FOR PRODUCTION
###################
FROM --platform=linux/amd64 node:18-alpine As build
WORKDIR /usr/src/app
COPY --chown=node:node package*.json ./
# In order to run `npm run build` we need access to the Nest CLI which is a dev dependency. In the previous development stage we ran `npm ci` which installed all dependencies, so we can copy over the node_modules directory from the development image
COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules
COPY --chown=node:node . .
# Run the build command which creates the production bundle
RUN npm run build
# Set NODE_ENV environment variable
ENV NODE_ENV production
# Running `npm ci` removes the existing node_modules directory and passing in --only=production ensures that only the production dependencies are installed. This ensures that the node_modules directory is as optimized as possible
ENV PUPPETEER_SKIP_DOWNLOAD=true
RUN npm ci --only=production && npm cache clean --force
USER node
###################
# PRODUCTION
###################
FROM --platform=linux/amd64 node:18-alpine As production
# Copy the bundled code from the build stage to the production image
COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules
COPY --chown=node:node --from=build /usr/src/app/dist ./dist
RUN apk add chromium
EXPOSE 3010
ENV PORT 3010
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Start the server using the production build
CMD [ "node", "dist/main.js" ]
An error appears in the console when starting the container
[Nest] ERROR [ExceptionHandler] Protocol error (Target.setAutoAttach): Target closed.
ProtocolError: Protocol error (Target.setAutoAttach): Target closed.
at /node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:104:24
at new Promise (<anonymous>)
at Connection.send (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Connection.js:100:16)
at ChromeTargetManager.initialize (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/ChromeTargetManager.js:247:82)
at CDPBrowser._attach (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Browser.js:156:76)
at CDPBrowser._create (/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Browser.js:49:23)
at ChromeLauncher.launch (/node_modules/puppeteer-core/lib/cjs/puppeteer/node/ChromeLauncher.js:130:53)
at async InstanceWrapper.useFactory [as metatype] (/node_modules/#noahqte/nest-puppeteer/dist/puppeteer-core.module.js:38:24)
at async Injector.instantiateClass (/node_modules/#nestjs/core/injector/injector.js:355:37)
at async callback (/node_modules/#nestjs/core/injector/injector.js:56:34)
I've been thinking about solving this error for quite some time now. Tried different methods to solve it
Puppeteer is connected via the nestjs-puppeteer library
Service
constructor(
private appService: AppService,
#InjectBrowser() private readonly browser: Browser,
) {}
async onSendForm1(data, #Ctx() ctx?: Context) {
const page = await this.browser.newPage()
await page.setRequestInterception(true)
await page.once('request', (interceptedRequest) => {
interceptedRequest.continue({ method: 'POST', postData: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } })
})
await page.goto('http://localhost:3010', { waitUntil: 'networkidle2' })
const fileName = Guid.newGuid().toString()
await page.pdf({
path: `${this.appService.pathFile}${fileName}.pdf`,
scale: 0.9,
format: 'A4',
landscape: false,
pageRanges: '1,3',
})
await page.close()
await this.browser.close()
}
app.module
#Module({
imports: [
PuppeteerModule.forRoot({ pipe: true, isGlobal: true }),
],
controllers: [AppController],
providers: [AppService],
exports: [AppService],
})
Chromium in a container works if you specify env
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
Can you help with this

I found a solution to my problem
Step 1:
Delete nestjs-puppeteer
npm uninstall nestjs-puppeteer
Step 2:
I had to change the dockerfile
Dockerfile
FROM node:18-alpine As development
# Create app directory
WORKDIR /usr/src/app
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY --chown=node:node package*.json ./
# Install app dependencies using the `npm ci` command instead of `npm install`
RUN npm install
# Bundle app source
COPY --chown=node:node . .
# Use the node user from the image (instead of the root user)
USER node
###################
# BUILD FOR PRODUCTION
###################
FROM node:18-alpine As build
WORKDIR /usr/src/app
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
COPY --chown=node:node package*.json ./
# In order to run `npm run build` we need access to the Nest CLI which is a dev dependency. In the previous development stage we ran `npm ci` which installed all dependencies, so we can copy over the node_modules directory from the development image
COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules
COPY --chown=node:node . .
# Run the build command which creates the production bundle
RUN npm run build
# Set NODE_ENV environment variable
ENV NODE_ENV production
# Running `npm ci` removes the existing node_modules directory and passing in --only=production ensures that only the production dependencies are installed. This ensures that the node_modules directory is as optimized as possible
RUN npm install && npm cache clean --force
USER node
###################
# PRODUCTION
###################
FROM node:18-alpine As production
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
EXPOSE 3010
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont \
nano \
sudo \
bash
# Copy the bundled code from the build stage to the production image
COPY --chown=node:node --from=build /usr/src/app/dist ./dist
COPY --chown=node:node --from=build /usr/src/app/node_modules ./dist/node_modules
# Start the server using the production build
USER node
CMD [ "node", "dist/main.js" ]
Step 3: change init browser
service.ts
import puppeteer from 'puppeteer'
const browser = await puppeteer.launch({
headless: true,
args: ['--disable-gpu', '--disable-dev-shm-usage', '--disable-setuid-sandbox', '--no-sandbox'],
})
const page = await browser.newPage()
await page.setRequestInterception(true)
page.once('request', (interceptedRequest) => {
interceptedRequest.continue({
method: 'POST',
postData: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
})
})
await page.goto('http://localhost:3010', { waitUntil: 'networkidle2' })
await page.waitForSelector('body')
const fileName = Guid.newGuid().toString()
await page.pdf({
path: `${this.appService.pathFile}${fileName}.pdf`,
scale: 0.9,
format: 'A4',
landscape: false,
pageRanges: '1,2',
})
await browser.close()
The error occurs if the container is run as a root user

Related

How to perform healthcheck for an Angular container based on node.js?

I'm trying to perform a healthcheck for an Angular container. Here is the Dockerfile for it:
FROM node:18-alpine as builder
RUN npm install -g #angular/cli
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build:ssr
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/package.json /app
COPY --from=builder /app/dist /app/dist
ENV NODE_ENV=production
EXPOSE 4000
CMD ["npm", "run", "serve:ssr"]
As far what have I done is to add this into server.ts file:
server.get('/health', (req, res) => {
res.status(200).json({ status: 'OK' });
console.log('Received GET request to /health endpoint');
});
And here is my healthcheck from docker-compose.yml:
test: "curl -f http://localhost:4000/health || exit 1"
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
When I run http://localhost:4000/health in my browser I receive 200 OK as I should (and there are logs proving it was received), however when it is performed through executing "docker compose up" command it does not work. What may be the reason for that?

nextjs env not exposed to client

I use Next js 12.1 and I define some env in .env.local everything is fine in local but when I deploy in the server I realize environment variables that I define in server are undefined. when I log the env, it is undefined in the browser and is work in the server terminal.
here is my next.config.js
/** #type {import('next').NextConfig} */
const nextConfig = {
experimental: {
outputStandalone: true,
},
compiler: { styledComponents: true },
env: {
NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
},
};
module.exports = nextConfig;
Dockerfile
FROM node:16-alpine AS deps
ENV http_proxy=http://fodev.org:8118
ENV https_proxy=http://fodev.org:8118
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install --force
# If using yarn with a `yarn.lock` comment out above and use below instead
# COPY package.json yarn.lock ./
# RUN yarn install --frozen-lockfile
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# If using yarn comment out above and use below instead
# RUN yarn build
FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
and this is one of my components name Footer
import React from "react";
import Link from "next/link";
const Footer = () => {
console.log(process.env.NEXT_PUBLIC_COMPANY_NAME );
return (
<footer
className="md:px-10 lg:p-14 text-gray-600 relative flex flex-wrap justify-center mx-auto "
style={{ background: "var(--gray)" }}
dir="rtl"
>
<div>{process.env.NEXT_PUBLIC_COMPANY_NAME || "CmpName"}{" "}</div>
</footer>
);
};
export default Footer;
browser console
Browser console
and server logs
Server Logs
Update your question with example how you use this env.
From docs:
The value will be inlined into JavaScript sent to the browser because of the NEXT_PUBLIC_ prefix. This inlining occurs at build time, so your various NEXT_PUBLIC_ envs need to be set when the project is built.
Read this page https://nextjs.org/docs/basic-features/environment-variables
there is difference when you use process.env.NEXT_PUBLIC_ANALYTICS_ID or const env = process.env and then env.NEXT_PUBLIC_ANALYTICS_ID

Build Nest.js in docker got a Prisma erorr

I am building an application in nest.js ,then I want to dockerize it by using docker, this is my docker file:
FROM node:14 AS builder
# Create app directory
WORKDIR /app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
COPY prisma ./prisma/
# Install app dependencies
RUN npm install
COPY . .
RUN npm run build
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
Then when I run :
docker build -t medicine-api .
I got this erorr from prisma
Module '"#prisma/client"' has no exported member 'User'.
3 import { User } from '#prisma/client';
and this is my prisma.schema file
/ This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
generator prismaClassGenerator {
provider = "prisma-class-generator"
dryRun = false
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int #id #default(autoincrement())
phoneNumber String #unique
lastName String
firstName String
role Role
bio String?
certificate String?
pic String?
verified Boolean #default(false)
medicine Medicine[]
pharmacyMedicine PharmacyMedicine[]
medicineCategory MedicineCategory[]
pharmacyPackage PharmacyPackage[]
pharmacistOrder Order[] #relation("pharmacistOrder")
userOrder Order[] #relation("userOrder")
}
I try to fix this by searching through difference resource and website, then they recommend me to put npx prisma generate in my dockefil. But still I get another erorr here:
Error: Generator at prisma-class-generator could not start:
/bin/sh: 1: prisma-class-generator: not found
If you have any solutions , I am really happy to try. Thanks in advance.
You have to generate the prisma client by running the command
yarn prisma generate
this should come before the step of coping the prisma folder
so I would suggest to change the dockerFile to be
FROM node:14 AS builder
# Create app directory
WORKDIR /app
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
# Install app dependencies
RUN npm install
COPY . .
RUN yarn prisma generate
COPY prisma ./prisma/
RUN npm run build
FROM node:14
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]
the prisma generate step will make sure you will have the prisma client in your node_modules so that it can be used to migrate the prisma models

NGINX fails at COPY --from=node /app/dist/comp-lib /usr/share/nginx/html,

COPY failed: stat /var/lib/docker/overlay2/1e9a0e53a11b406c13d4fc790336f37285927a1b87d1bac4d0e889c6d3cfed9b/merged/app/dist/comp-lib: no such file or directory
I tried running docker system prune, and restarted Docker a bunch of times. I also gave a shot at rm -rf /var/lib/docker in the docker VM, somehow that doesn't remove the directory.
Node version: v10.15.1
Docker version: 18.09.2, build 6247962
Dockerfile:
# stage-1
FROM node as builder
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
# stage -2
FROM nginx:alpine
COPY --from=node /app/dist/comp-lib /usr/share/nginx/html
I expect the build to be successful but the above mentioned is the error I'm experiencing.
In your stage 2
COPY --from=node /app/dist/comp-lib /usr/share/nginx/html
should be
COPY --from=builder /app/dist/comp-lib /usr/share/nginx/html
since stage 1 is called builder and not node.
This is the dockerfile that I use for my Angular apps:
FROM johnpapa/angular-cli as angular-built
WORKDIR /usr/src/app
COPY package.json package.json
RUN npm install --silent
COPY . .
RUN ng build --prod
FROM nginx:alpine
LABEL author="Preston Lamb"
COPY --from=angular-built /usr/src/app/dist /usr/share/nginx/html
EXPOSE 80 443
CMD [ "nginx", "-g", "daemon off;" ]
I've never had any issues with this configuration. There's more information as well in this article.

Run 2 different containers from same node project

I have a node project that has a web server and a service on the root.
--myNodeProj
--app.js //the web server
--service.js //an update service
In my package.json I have the following:
"scripts": {
"start": "node app.js",
"service": "node service.js"
},
For my DockerFile I have:
FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm install --only=production
# Bundle app source
COPY . .
EXPOSE 8080
CMD [ "npm", "start" ]
The CMD will run the app.js (webserver). How do I build another container with the service? Do I create another Dockerfile? Would the docker build command look different?
You can override the command -
docker run <image> node service.js
https://docs.docker.com/engine/reference/run/#general-form
I ended up using docker-compose.
You need to create a docker-compose.yml file with the following code:
version: '3'
services:
web:
# will build ./docker/web/Dockerfile
build:
context: .
dockerfile: ./docker/web/Dockerfile
ports:
- "3000:3000"
env_file:
- web.env
service:
# will build ./docker/service/Dockerfile
build:
context: .
dockerfile: ./docker/service/Dockerfile
env_file:
- service.env
This files reference 2 Dockerfiles that build the containers:
For service
FROM node:8
# Create app directory
WORKDIR /usr/src/service
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm install --only=production
# Bundle app source
COPY . .
CMD [ "node", "service.js" ]
For web:
FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm install --only=production
# Bundle app source
COPY . .
#EXPOSE 8080
CMD [ "npm", "start" ]
Notice that I can only do one NPM start. I call the service directly using node.
When I want to build containers, I issue the command:
docker-compose build

Resources