node.js - Nextjsin dockerointi TypeScriptin kanssa


Yritän rakentaa Docker-kuvaa Nextjs-frontend (React) -sovelluksestani tuotantokäyttöä varten, ja olen tällä hetkellä jumissa TypeScript-integraation kanssa.

Tässä on Dockerfile.

FROM node:14-alpine3.14 as deps

RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
EXPOSE 4500
RUN apk add --no-cache libc6-compat

RUN mkdir /app && chown -R node:node /app
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force

FROM node:14-alpine3.14 as build
RUN mkdir /app && chown -R node:node /app
WORKDIR /app
ENV NODE_ENV=production
COPY --chown=node:node . ./
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build


FROM node:14-alpine3.14 as prod
RUN mkdir /app && chown -R node:node /app
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=7777
COPY --from=build /app ./
USER node
CMD ["node_modules/.bin/next", "start"]

Nyt tästä seuraa virhe:

It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Periaatteessa koska teen npm ci --production, se ei asenna devDependencies, joissa TypeScript on.

Etsimisen jälkeen olen tullut muutamiin ratkaisuihin.

Ratkaisu 1: Ensimmäinen on lisätä Typescript riippuvuuksiin. Vaikka on suositeltavaa, että koska Typescript on vain devDependency, sitä ei pitäisi olla tavanomaisissa riippuvuuksissa.

Ratkaisu 2: Typescriptin lisääminen komennolla npm install. Periaatteessa sama kuin ratkaisu 1. Muokkasin Dockerfilea näin:

FROM node:14-alpine3.14 as deps

COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force

Added typescript and node types here
RUN npm install --save-dev typescript @types/node

Tässä tapauksessa koko kuvan koko tulee olemaan: 981,58 MB.

Ratkaisu 3: Yksinkertaisesti npm install tekeminen npm ci --production sijaan.

FROM node:14-alpine3.14 as deps

COPY --chown=node:node package.json package-lock.json ./

Simple npm install
RUN npm install && npm cache clean --force

Tässä tapauksessa lopetan myös kaikkien devDependenciesien asentamisen. Tässä tapauksessa kokonaiskuvan koko on: 537,32 MB.

Nyt minulla on muutama kysymys tästä.

Kysymys 1: Miksi tyypin lisääminen TypeScriptin avulla komennolla npm install --save-dev typescript @types/node Ratkaisussa 2 johtaa suurempaan tiedostokokoon verrattuna Ratkaisuun 3, jossa asennamme kaikki riippuvuudet?

Kysymys 2: Jos Ratkaisussa 3 teen npm ci npm install sijaan, kokonaiskuvan koko on 972,59 Mt. Miksi npm ci käyttö lisää kuvan kokoa? Eikö sen pitäisi vain asentaa tarkat paketit package-lock.jsonin perusteella.

Kysymys 3: Katsoin keskustelua. Pyydettiin asentamaan Typescript, vaikka se oli jo asennettu Docker-kuvaa rakennettaessa.

Se ehdotti ratkaisua monivaiheisella rakentamisella kuten tämä.

FROM gcr.io/companyX/companyX-node-base:12-alpine AS build

Copy in only the parts needed to install dependencies
(This avoids rebuilds if the package.json hasn’t changed)
COPY package.json package.lock .

Install dependencies (including dev dependencies)
RUN npm install

Copy in the rest of the project
(include node_modules in a .dockerignore file)
COPY . .

Build the project
RUN npm run build

Second stage: runtime
FROM gcr.io/companyX/companyX-node-base:12-alpine

ENV NODE_ENV=production

Again get dependencies, but this time only install
runtime dependencies
COPY package.json package.lock .
RUN npm install

Get the built application from the first stage
COPY --from=build /app/dist dist

Set runtime metadata
EXPOSE 3000
CMD [ "npm", "start" ]
CMD ["node", "dist/index.js"]

Eikö tämä ratkaisu ole huono, koska joudut tässä tapauksessa asentamaan riippuvuuksia kahdesti. Kerran build-vaiheessa ja toisen kerran runner-vaiheessa, vaikka asentaisitkin vain tuotantoriippuvuudet runner-vaiheessa.

Kokeilin tätä ratkaisua ja kuten odotettua päädyin kuvatiedoston kokoon 1,18 Gt.

Kysymys 4: Kumpi yllä olevista ratkaisuista on parempi valita? Vai onko olemassa parempi tapa tehdä tämä?

Vastaus #1

Käytä välikonttia asentaaksesi vain tuotantopaketteja

FROM node:14-alpine AS build

Disable telemetry
ENV NEXT_TELEMETRY_DISABLED 1

WORKDIR /build

Copy package and package-lock 
COPY package.json package-lock.json ./

Clean install dependencies based package-lock
Note: We also install dev deps as typeScript may be needed
RUN npm ci

Copy files
Use .dockerignore to avoid copying node_modules and others folders and files
COPY . .

Build application
RUN npm run build

=======================================
Image generate dependencies production
=======================================
FROM node:14-alpine AS dependencies

Environment Production
ENV NODE_ENV production

WORKDIR /dependencies

Copy package and package-lock 
COPY --from=build /build/package.json .
COPY --from=build /build/package-lock.json ./

Clean install dependencies based package-lock
RUN npm ci --production

=======================================
Image distroless final
=======================================
FROM gcr.io/distroless/nodejs:14

Mark as prod, disable telemetry, set port
ENV NODE_ENV production
ENV PORT 3000
ENV NEXT_TELEMETRY_DISABLED 1

WORKDIR /app

Copy from build
COPY --from=build /build/next.config.js .
COPY --from=build /build/public/ ./public
COPY --from=build /build/.next ./.next
COPY --from=dependencies /dependencies/node_modules ./node_modules

EXPOSE 3000

Run app command
CMD ["node_modules/.bin/next", "start"]

Vastaus #2

Tässä tapauksessa voit käyttää pohjakuvana tuotantovaiheessa kuvaa https://github.com/ryanbekhen/feserve/pkgs/container/feserve. Tämä on kuva, jonka tein perustuen etupään esiintyneisiin valituksiin. Pohjakuva on vain noin 8 Mt, joten se ei vie paljon tallennustilaa.

Aiheeseen liittyviä artikkeleita