Пошук уроків, статей та іншого контенту
Чому порядок інструкцій у Dockerfile впливає на швидкість повторних збірок — і .dockerignore.
Docker кешує результат кожної інструкції Dockerfile як окремий шар. При повторній збірці, якщо конкретна інструкція та все, що на неї впливає (попередні шари, вміст скопійованих файлів), не змінилось — Docker перевикористовує вже готовий шар із кешу замість повторного виконання, різко прискорюючи повторні збірки.
Кеш працює послідовно зверху вниз: щойно якийсь шар змінюється (наприклад, вміст скопійованого файлу став іншим), Docker вважає недійсним не лише цей шар, а й геть усі наступні за ним у файлі — навіть якщо самі вони не змінювались:
FROM node:20-alpine
WORKDIR /app
COPY . . # копіює УСІ файли проєкту одразу, включно з package.json ТА кодом застосунку
RUN npm ci # ця інструкція перевиконується щоразу, коли ЗМІНИВСЯ БУДЬ-ЯКИЙ файл проєкту,
# навіть якщо package.json (залежності) не змінювався взагаліОскільки код застосунку змінюється значно частіше, ніж список залежностей у package.json, копіювання всього одним рядком означає, що npm ci (найповільніший крок збірки) перевиконується практично на кожну зміну коду — навіть якщо жодна залежність не змінилась.
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./ # лише ці два файли спершу
RUN npm ci # кешується, поки package.json/lock не змінились
COPY . . # решта коду копіюється ОКРЕМО, пізнішеТепер npm ci перевиконується лише тоді, коли реально змінився package.json чи package-lock.json — зміна коду застосунку (COPY . . далі) інвалідує лише останній, дешевий шар копіювання, а не повільне повторне встановлення залежностей.
Загальний принцип: розташовуйте інструкції, що змінюються рідше (базовий образ, встановлення залежностей), раніше в Dockerfile, а ті, що змінюються часто (сам код застосунку), — пізніше. Це максимізує частку шарів, які реально перевикористовуються з кешу між збірками.
Так само, як .gitignore для Git (курс Git), .dockerignore виключає файли й папки з контексту збірки — не потрапляють ні в образ, ні навіть у дані, що відправляються демону збірки:
node_modules
.git
.env
*.log
distnode_modules у .dockerignore особливо важливий: локальні залежності хоста (можливо, для іншої операційної системи чи архітектури процесора) не повинні потрапляти в образ — RUN npm ci всередині Dockerfile встановлює їх заново, правильно, для середовища самого образу.
COPY . . одним рядком до встановлення залежностей — інвалідує кеш встановлення залежностей на кожну зміну коду, різко сповільнюючи повторні збірки.
Відсутній .dockerignore — node_modules хоста потрапляє в контекст збірки (і можливо, в сам образ), сповільнюючи docker build і потенційно конфліктуючи з залежностями, встановленими всередині образу для іншої платформи.
Забувати, що кеш шару інвалідується не лише зміною самої інструкції, а й зміною будь-якого файлу, який ця інструкція копіює, — модифікація одного рядка в package.json інвалідує RUN npm ci так само, як модифікація сотні рядків.
Docker кешує кожен шар Dockerfile окремо, але зміна одного шару інвалідує всі наступні за ним — тому інструкції, що змінюються рідко (залежності), варто розташовувати раніше за ті, що змінюються часто (код застосунку), копіюючи package.json окремо й раніше за решту коду. .dockerignore виключає файли (node_modules, .git, секрети), які не повинні потрапляти в контекст збірки чи сам образ.