docker: minimal runtime image — UI in devDeps, distroless final, slim bundle
Docker / build (push) Failing after 16s

Made-with: Cursor
This commit is contained in:
Denozordec
2026-03-21 01:27:26 +07:00
parent dae8bb8066
commit 446d70d3dc
3 changed files with 36 additions and 22 deletions
+7 -7
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
# Сборка фронта (devDependencies только здесь)
# Сборка фронта (все devDependencies + UI)
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
@@ -11,7 +11,7 @@ COPY public ./public
COPY src ./src
RUN npm run build
# Production node_modules + server + dist
# Только runtime-deps: express, cors, sql.js (+ транзитивные)
FROM node:22-alpine AS prod
WORKDIR /app
COPY package.json package-lock.json ./
@@ -20,17 +20,17 @@ RUN --mount=type=cache,target=/root/.npm \
COPY server ./server
COPY --from=build /app/dist ./dist
# Слой приложения одним COPY в финальный образ (меньше слоёв в image)
# Артефакты без package.json / lock — не нужны для node server/index.js
FROM node:22-alpine AS bundle
WORKDIR /out
COPY --from=prod /app/node_modules ./node_modules
COPY --from=prod /app/server ./server
COPY --from=prod /app/dist ./dist
COPY --from=prod /app/package.json ./package.json
FROM node:22-alpine
# Минимальный runtime (см. https://github.com/GoogleContainerTools/distroless/blob/main/nodejs/README.md)
FROM gcr.io/distroless/nodejs22-debian13:nonroot
WORKDIR /app
ENV NODE_ENV=production
COPY --from=bundle /out/ .
COPY --from=bundle --chown=nonroot:nonroot /out/ .
EXPOSE 3001
CMD ["node", "server/index.js"]
CMD ["server/index.js"]
+20 -6
View File
@@ -8,26 +8,26 @@
"name": "vps-tracker",
"version": "0.0.0",
"dependencies": {
"@tabler/core": "^1.4.0",
"@tabler/icons-react": "^3.40.0",
"chart.js": "^4.5.1",
"cors": "^2.8.5",
"express": "^4.21.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1",
"sql.js": "^1.14.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@tabler/core": "^1.4.0",
"@tabler/icons-react": "^3.40.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"chart.js": "^4.5.1",
"concurrently": "^9.1.0",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1",
"vite": "^7.3.1"
}
},
@@ -1018,12 +1018,14 @@
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
"dev": true,
"license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
@@ -1391,6 +1393,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@tabler/core/-/core-1.4.0.tgz",
"integrity": "sha512-5BigzOlbOH9N0Is4u0rYNRCiwtnUXWO57K9zwuscygcicAa8UV9MGaS4zTgQsZEtZ9tsNANhN/YD8gCBGKYCiw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
@@ -1408,6 +1411,7 @@
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.40.0.tgz",
"integrity": "sha512-V/Q4VgNPKubRTiLdmWjV/zscYcj5IIk+euicUtaVVqF6luSC9rDngYWgST5/yh3Mrg/mYUwRv1YVTk71Jp0twQ==",
"dev": true,
"license": "MIT",
"funding": {
"type": "github",
@@ -1418,6 +1422,7 @@
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.40.0.tgz",
"integrity": "sha512-oO5+6QCnna4a//mYubx4euZfECtzQZFDGsDMIdzZUhbdyBCT+3bRVFBPueGIcemWld4Vb/0UQ39C/cmGfGylAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@tabler/icons": "3.40.0"
@@ -1685,6 +1690,7 @@
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz",
"integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==",
"dev": true,
"funding": [
{
"type": "github",
@@ -1835,6 +1841,7 @@
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
@@ -1958,6 +1965,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3408,6 +3416,7 @@
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -3417,6 +3426,7 @@
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
@@ -3439,6 +3449,7 @@
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz",
"integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==",
"dev": true,
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -3461,6 +3472,7 @@
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz",
"integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==",
"dev": true,
"license": "MIT",
"dependencies": {
"react-router": "7.13.1"
@@ -3578,6 +3590,7 @@
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"dev": true,
"license": "MIT"
},
"node_modules/semver": {
@@ -3648,6 +3661,7 @@
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"dev": true,
"license": "MIT"
},
"node_modules/setprototypeof": {
+9 -9
View File
@@ -11,26 +11,26 @@
"preview": "vite preview"
},
"dependencies": {
"@tabler/core": "^1.4.0",
"@tabler/icons-react": "^3.40.0",
"chart.js": "^4.5.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1",
"sql.js": "^1.14.0",
"cors": "^2.8.5",
"express": "^4.21.1"
"express": "^4.21.1",
"sql.js": "^1.14.0"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@tabler/core": "^1.4.0",
"@tabler/icons-react": "^3.40.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.1",
"chart.js": "^4.5.1",
"concurrently": "^9.1.0",
"eslint": "^9.39.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24",
"concurrently": "^9.1.0",
"globals": "^16.5.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.13.1",
"vite": "^7.3.1"
}
}