Пошук уроків, статей та іншого контенту
Навчитеся описувати середовище Node.js у Dockerfile та збирати повторювані образи застосунків.
Dockerfile — це текстовий файл з інструкціями для створення Docker-образу.
Образ містить усе необхідне для запуску застосунку:
базову операційну систему;
середовище Node.js;
залежності;
файли застосунку;
команду запуску.
Після створення образ можна запускати як контейнер на будь-якому комп’ютері з Docker. Середовище запуску буде описане однаково, незалежно від локальної операційної системи.
Dockerfile зазвичай зберігають у корені проєкту та називають саме Dockerfile, без розширення.
Створимо простий Node.js-застосунок:
docker-node-app/
├── src/
│ └── server.js
├── package.json
├── package-lock.json
├── .dockerignore
└── DockerfileФайл src/server.js:
const http = require("node:http");
const port = process.env.PORT || 3000;
const server = http.createServer((request, response) => {
response.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
response.end("Застосунок працює в Docker!\n");
});
server.listen(port, "0.0.0.0", () => {
console.log(`Сервер запущено на порту ${port}`);
});Адреса 0.0.0.0 потрібна, щоб сервер був доступний ззовні контейнера, а не лише всередині нього.
Файл package.json:
{
"name": "docker-node-app",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "node src/server.js"
}
}Оскільки цей приклад не має зовнішніх залежностей, команда npm install створить простий файл package-lock.json:
npm installФайл блокування залежностей важливий для повторюваних збірок. Він фіксує точні версії пакетів, які використовує застосунок.
Створимо файл Dockerfile:
FROM node:22-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]Розглянемо його інструкції по черзі.
FROMFROM node:22-bookworm-slimFROM визначає базовий образ.
У цьому прикладі використовується образ із:
Node.js версії 22;
мінімальним середовищем Debian Bookworm;
інструментами, необхідними для запуску Node.js.
Усі наступні інструкції додають зміни поверх базового образу.
Тег образу краще вказувати явно, наприклад node:22-bookworm-slim, а не використовувати нечіткий тег на кшталт node:latest. Це робить вибір версії передбачуванішим.
WORKDIRWORKDIR /appWORKDIR встановлює поточну робочу директорію всередині образу.
Після цієї інструкції команди виконуються в /app, а файли копіюються туди, якщо не вказано інший шлях.
Якщо директорії /app ще немає, Docker створить її автоматично.
COPYCOPY package*.json ./COPY копіює файли з контексту збірки в образ.
Шаблон package*.json відповідає таким файлам:
package.json;
package-lock.json.
Крапка ./ означає поточну директорію всередині образу, тобто /app.
Далі копіюємо решту файлів:
COPY . .Перша крапка означає поточну директорію на комп’ютері, а друга — поточну директорію в образі.
RUNRUN npm ci --omit=devRUN виконує команду під час створення образу.
npm ci встановлює залежності з package-lock.json. Ця команда призначена для повторюваного встановлення залежностей і не змінює файл блокування.
Параметр --omit=dev не встановлює залежності, призначені лише для розробки. У нашому прикладі таких залежностей немає, але цей параметр часто використовують для production-образів.
Команди RUN виконуються під час docker build, а не під час кожного запуску контейнера.
EXPOSEEXPOSE 3000EXPOSE документує, що застосунок у контейнері використовує порт 3000.
Ця інструкція сама по собі не робить порт доступним на комп’ютері. Порт потрібно опублікувати під час запуску контейнера за допомогою параметра -p.
CMDCMD ["npm", "start"]CMD визначає стандартну команду, яка виконується під час запуску контейнера.
У цьому випадку Docker запускає:
npm startМасив у форматі ["команда", "аргумент"] називають exec-формою. Для команд запуску застосунків це рекомендований варіант.
.dockerignoreПередавання зайвих файлів у контекст збірки збільшує його розмір і може призвести до копіювання непотрібних даних в образ.
Створимо .dockerignore:
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignoreДиректорію node_modules не потрібно копіювати з комп’ютера розробника. Залежності встановлюються всередині образу командою npm ci.
Також не варто передавати в образ службові файли Git і локальні журнали помилок npm.
Перейдіть у директорію проєкту:
cd docker-node-appСтворіть образ командою:
docker build -t docker-node-app .У цій команді:
docker build створює образ;
-t docker-node-app задає ім’я образу;
. визначає поточну директорію як контекст збірки.
Під час збірки Docker виконає інструкції з Dockerfile по черзі:
завантажить базовий образ Node.js;
створить робочу директорію /app;
скопіює файли опису пакетів;
встановить залежності;
скопіює код застосунку;
збереже команду запуску контейнера.
Перевірити створені образи можна так:
docker imagesЗапустіть контейнер:
docker run --name docker-node-container -p 3000:3000 docker-node-appПараметри команди:
--name docker-node-container задає ім’я контейнера;
-p 3000:3000 пов’язує порт комп’ютера з портом контейнера;
docker-node-app — ім’я образу.
Формат параметра -p такий:
порт_комп’ютера:порт_контейнераПісля запуску застосунок буде доступний за адресою:
http://localhost:3000У відповідь сервер поверне:
Застосунок працює в Docker!Зупинити контейнер можна комбінацією Ctrl+C, якщо він запущений у поточному терміналі. Також можна виконати:
docker stop docker-node-containerВидалити зупинений контейнер:
docker rm docker-node-containerDocker створює образ пошарово. Багато інструкцій Dockerfile утворюють окремі шари, які можуть повторно використовуватися під час наступної збірки.
Саме тому залежності копіюють окремо від коду:
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .Якщо змінити лише src/server.js, Docker зможе використати вже створений шар із залежностями.
Якщо спочатку скопіювати весь проєкт:
COPY . .
RUN npm ci --omit=devто будь-яка зміна в коді змусить Docker повторно виконати npm ci. Це зробить збірку повільнішою.
Після зміни коду образ можна зібрати знову:
docker build -t docker-node-app .Docker використає кеш там, де це можливо, і виконає заново лише необхідні кроки.
Повторюваність означає, що однаковий Dockerfile і однаковий набір файлів повинні створювати однакове середовище запуску.
Для цього важливо:
використовувати визначений тег базового образу;
зберігати package-lock.json;
встановлювати залежності через npm ci;
не копіювати локальну node_modules;
зберігати Dockerfile у системі контролю версій.
Під час збірки Docker отримує Node.js, встановлює залежності й копіює застосунок за одним і тим самим описом.
Для Node.js-застосунку часто використовують такий порядок:
FROM node:22-bookworm-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["npm", "start"]Такий порядок поєднує:
зрозумілий опис середовища;
кешування залежностей;
запуск застосунку стандартною командою;
можливість опублікувати порт під час запуску контейнера.
package-lock.jsonЯкщо Dockerfile містить:
RUN npm ciале в контексті немає package-lock.json, збірка завершиться помилкою.
Спочатку створіть файл блокування:
npm installПісля цього повторіть збірку образу.
Інструкція:
EXPOSE 3000не публікує порт на комп’ютері.
Потрібно запустити контейнер із параметром:
docker run -p 3000:3000 docker-node-applocalhostЯкщо Node.js-сервер слухає 127.0.0.1, він може бути недоступним за межами контейнера.
Для доступу через опублікований порт сервер має слухати 0.0.0.0:
server.listen(port, "0.0.0.0");Наявність node_modules на комп’ютері не означає, що ця директорія є в образі.
Залежності потрібно встановлювати під час збірки через RUN npm ci.
Без .dockerignore в образ або контекст збірки можуть потрапити:
node_modules;
.git;
журнали помилок;
локальні конфігураційні файли.
Додайте .dockerignore до кореня проєкту.
Dockerfile описує кроки створення Docker-образу.
FROM задає базове середовище Node.js.
WORKDIR визначає робочу директорію в образі.
COPY переносить файли проєкту в образ.
RUN виконує команди під час збірки.
EXPOSE документує порт застосунку, але не публікує його.
CMD задає стандартну команду запуску контейнера.
npm ci і package-lock.json допомагають створювати повторювані образи.
.dockerignore виключає зайві файли з контексту збірки.
Образ створюють через docker build, а контейнер запускають через docker run.