Повышайте уровень своих Dockerfiles NodeJS с помощью этих трех советов
Dockerfiles — это чертежи ваших контейнеров. Это простые текстовые файлы со списком команд, которые вы обычно выполняете вручную для создания образа контейнера. Dockerfile — это исходный код вашего контейнера.
Dockerfiles легко писать, но они могут быстро усложниться. Вот несколько советов, которые помогут вам лучше писать Dockerfiles.
Я собираюсь поделиться с вами тремя советами, которые я усвоил за эти годы. Эти советы помогут вам лучше писать Dockerfile. Они сделают ваши контейнеры более стабильными и безопасными. Они также сделают ваши контейнеры меньше и быстрее. Надеюсь, вы найдете их полезными.
Если ваш Dockerfile выглядит так, его можно улучшить:
FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "run", "start"]
1- Выберите правильное базовое изображение
Базовый образ — это отправная точка для вашего контейнера. Это первая строка в вашем Dockerfile. Базовый образ — это основа вашего контейнера. Это операционная система и программное обеспечение, которые вы будете использовать для создания контейнера.
Alpine — самый популярный базовый образ для контейнеров Docker. Это легкий дистрибутив Linux, оптимизированный для контейнеров. Он маленький, быстрый и безопасный. Это хороший выбор для большинства контейнеров. Но... Docker официально не поддерживает его. Он поддерживается сообществом Alpine Linux.
Лучшим подходом было бы использовать базовое изображение «яблочко». Это официальный базовый образ Debian для Docker. Он поддерживается сообществом Debian. Это хороший выбор для большинства контейнеров. Он маленький и быстрый, а если вы выберете тонкий вариант, он также безопасен и более легкий.
FROM node:20.9.0-bullseye-slim
Также укажите версию базового образа. Это гарантирует, что ваш контейнер всегда будет использовать одну и ту же версию базового образа. Это сделает ваш контейнер более стабильным и безопасным. Это не версия. Это тег. Не рекомендуется использовать теги для базовых изображений. Вместо этого используйте версию.
2- Вам нужно все скопировать?
Вероятно, нет, я почти уверен, что вам не нужны .prettierignore
или .gitignore
среди других файлов в рабочем контейнере Docker.
Вы можете решить эту проблему двумя способами:
- Укажите список директив
COPY
для каждого файла/папки.
- Укажите список того, что вы НЕ хотите копировать в файле
.dockerignore
.
Я предпочитаю указывать список файлов/папок, которые я хочу скопировать в изображение. Несмотря на это, я бы рекомендовал вам использовать файл .dockerignore
, чтобы избежать копирования ненужных файлов в образ, поскольку это хорошая практика.
Кроме того, из соображений безопасности вам следует использовать пользователя с меньшими привилегиями, чем root
, поскольку по умолчанию образы Docker запускаются пользователем root
(в зависимости от используемого вами базового образа). Вы можете проверить это, запустив whoami в своем Dockerfile.
FROM node:20.9.0-bullseye-slim
RUN echo "whoami: $(whoami)" # <--- whoami: root
⚠️ Если вы используете docker с buildkit вместо классического движка, передайте этот аргумент команде docker для сборки: --progress=plain
docker build -t my-node-app --no-cache --progress=plain .
В этом базовом образе узла и некоторых других, включая базовые образы Alpine, у нас есть пользователь узла, которого мы можем использовать вместо root
с меньшими привилегиями. Нам следует добавить этого пользователя в образ и использовать его вместо root
, если это возможно.
Дополнительную информацию об использовании USER в Dockerfile можно найти здесь.
FROM node:20.9.0-bullseye-slim
RUN echo "whoami: $(whoami)" # <--- whoami: root
USER node
RUN echo "whoami: $(whoami)" # <--- whoami: node
# Now we are using the 'node' user instead of root for the rest of the Dockerfile.
Опция --chown=node:node
гарантирует, что право собственности на скопированные файлы будет установлено для пользователя и группы узла.
FROM node:20.9.0-bullseye-slim
WORKDIR /app
USER node
COPY --chown=node:node ./src ./src
COPY --chown=node:node ./package*.json ./
Имейте в виду, что если вы установите для WORKDIR значение /app
, это означает, что директивы COPY
, среди других директив, будут копировать файлы в WORKDIR
, который вы можете указать с помощью.
Итак, это то же самое, что:
FROM node:20.9.0-bullseye-slim
WORKDIR /app
COPY --chown=node:node ./src /app/src/
чем
FROM node:20.9.0-bullseye-slim
WORKDIR /app
COPY --chown=node:node ./src ./src/
Если использовать RUN
FROM node:20.9.0-bullseye-slim
WORKDIR /app
RUN echo "Working dir: $(pwd)" # <-- Working dir: /app
Вы увидите результат: Working dir: /app
.
Оба подхода совершенно верны.
3- Многоэтапные сборки.
Почему? Безопасность и размер.
Размер: должен ли ваш окончательный образ иметь package-lock.json или даже package.json? Вероятно, нет, приложению NodeJS нужна папка node_modules и исходный код JS, обычно в проекте JS, расположенном в папке src, а если это проект TS, в папке dist, где находится скомпилированный код из TS в JS.
Если ваше приложение NodeJS написано на TypeScript, вы можете подумать:
FROM node:20.9.0-bullseye-slim
RUN apt-get update && apt-get install -y # <-- Maybe needed for your app
WORKDIR /app
COPY . /app
RUN npm ci --only=production
RUN npm run build # compile TS to JS
RUN rm -rf /app/src # <------- easy fix
CMD ["node", "dist/index.js"]
Хорошо... что, если для сборки или установки зависимостей вам нужно настроить NPM_TOKEN
?
FROM node:20.9.0-bullseye-slim
RUN apt-get update && apt-get install -y # <-- Maybe needed for your app
ENV NPM_TOKEN abc1234_=xyz
WORKDIR /app
COPY . /app
RUN npm ci --only=production
RUN npm run build # compile TS to JS
RUN rm -rf /app/src # <------- easy fix
CMD ["node", "dist/index.js"]
Если вы запустите docker history <name-of-the-image>
, вы сможете увидеть NPM_TOKEN. Конечно, вы всегда можете запустить RUN Export NPM_TOKEN=123
, чтобы удалить его из окончательного образа, но это не лучший подход, и он все равно будет виден в истории докера.
Кроме того, вам пришлось установить некоторые пакеты, такие как gcc
(apt-get update && apt-get install
), чтобы скомпилировать некоторые пакеты NodeJS. Конечно, вы всегда можете удалить эти пакеты, например gcc
, но... Проверьте следующий подход.
FROM node:20.9.0-bullseye-slim as builder
RUN apt-get update && apt-get install -y # <-- Maybe needed for your app
ENV NPM_TOKEN abc1234_=xyz
WORKDIR /app
COPY . /app
RUN npm ci --only=production
RUN npm run build
# Here down, we are creating a new image from a clean 'node:20.9.0-bullseye-slim' and this will be the final image.
FROM node:20.9.0-bullseye-slim as prod
USER node
WORKDIR /app
COPY --chown=node:node --from=builder /app/node_modules ./node_modules
COPY --chown=node:node --from=builder /app/dist ./dist
ENV production # <-- always good to have it
CMD ["node", "dist/index.js"]
При таком подходе вы получаете гораздо более чистый образ Docker без ненужных вещей и каких-либо частных аргументов ENV или чего-то подобного.
Бонусный совет ➡️
В образах и контейнерах Docker вам не обязательно иметь пакет dotenv
, поскольку Docker устанавливает для вас переменные ENV, если вы укажете их при запуске контейнера, например, через --env-file
.
Заключение
Написание Dockerfiles легко, но может быстро усложниться. Эти советы помогут вам лучше писать Dockerfile. Они сделают ваши контейнеры более стабильными и безопасными. Они также сделают ваши контейнеры меньше и быстрее.
Если вы хотите узнать больше о Docker, ознакомьтесь с официальной документацией Docker.
Вы можете связаться со мной на Github или Linkedin.