Практика оптимизации размера образа Nodejs Docker

Node.js

Вы ненавидите развертывание приложения, которое занимает слишком много времени? Превышать гигабайты — не лучшая практика для одного контейнера. Иметь дело с гигабайтами каждый раз, когда мы развертываем новую версию, нам кажется неправильным.

В этой статье будет показано несколько простых шагов по оптимизации образов Docker, чтобы сделать их меньше, быстрее и более подходящими для производственных сред с помощью программы Nodejs.

Простой фрагмент проекта Node.js

Сначала напишите простую программу веб-сервера на основе экспресс-

// package.json
{
  "name": "docker-test",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4"
  },
  "devDependencies": {
    "eslint": "^5.16.0"
  }
}
// app.js
const express = require('express')
const app = express()

app.get('/', function(req, res){
  res.send('hello world')
})

app.listen(3000)

Создайте новый DockerFile в корневом каталоге и напишите следующий код

# Dockerfile
FROM node

COPY . /home/app

RUN cd /home/app && npm install

WORKDIR /home/app

CMD ['npm', 'start']

воплощать в жизнь

  • docker build -t myapp .
  • docker images

结果

Вы можете видеть, что самая простая программа nodejs имеет 920 МБ, пожалуйста, не делайте этого. Далее мы будем постепенно уменьшать размер этого изображения.

Оптимизация образа производственной среды Docker

  • Использование образа Node.js Alpine

    Самый простой и быстрый способ резко уменьшить размер изображения — выбрать базовое изображение гораздо меньшего размера. Alpine — это крошечный дистрибутив Linux, который выполняет свою работу. Просто выберите версию Node.js для Alpine, и вы получите значительные улучшения.

    FROM node:alpine
    
    COPY . /home/app
    
    RUN cd /home/app && npm install
    
    WORKDIR /home/app
    
    CMD ['npm', 'start']

    после сборки

    结果

    Видно, что уменьшение800MB, что является очень большой оптимизацией.

  • Пакеты зависимостей, разработанные в среде сборки, не упакованы

    Но мы можем продолжить оптимизацию. Мы устанавливаем все зависимости, хотя нам нужны только зависимости среды сборки. Если вы упаковываете только производственную среду, она будет не так хороша, продолжайте ее улучшать.

      FROM node:alpine
    
      COPY . /home/app
    
      RUN cd /home/app && npm install --production
    
      WORKDIR /home/app
    
      CMD ['npm', 'start']

    после сборки

    结果

    Мы уменьшили его еще на 6 МБ, потому что в настоящее время у нас есть только одна зависимость для разработки, что, вероятно, было бы довольно большой оптимизацией и в обычном проекте.

  • Соберите Nodejs, используя базовую версию образа Alpine.

    Что, если мы воспользуемся базовой версией образа Alpine и сами установим Nodejs?

      FROM alpine:latest
    
      RUN apk add --no-cache --update nodejs nodejs-npm
    
      COPY . /home/app
    
      RUN cd /home/app && npm install --production
    
      WORKDIR /home/app
    
      CMD ['npm', 'start']

    после сборки

    结果

    Сейчас осталось всего 65Мб, что более чем в 10 раз меньше, чем в начале.

  • многоступенчатая сборка

    • Образы Docker многоуровневые, каждая инструкция в Dockerfile создает новый слой образа, который можно повторно использовать и кэшировать. Когда инструкции Dockerfile изменены, скопированные файлы изменены или переменные, указанные при построении образа, отличаются, соответствующий кеш слоя изображения станет недействительным.После того, как кеш изображения определенного слоя станет недействительным, последующий слой изображения кеш станет недействительным.

    • Следовательно, мы также можем комбинировать инструкции RUN, но нам нужно помнить, что мы можем комбинировать только инструкции с одинаковой частотой изменения.

    • Мы должны поместить части с наименьшими изменениями в начало Dockerfile, чтобы мы могли в полной мере использовать кеш изображений.

    • Минимизируя количество слоев изображения, мы можем получить изображения меньшего размера.

В приведенном выше примере исходный код будет часто меняться, поэтому каждый раз при сборке образа необходимо переустанавливать модуль NPM, что явно не то, что мы хотим видеть. Таким образом, мы можем сначала скопировать package.json, затем установить модули npm и, наконец, скопировать остальную часть исходного кода. Таким образом, вам не нужно переустанавливать модули npm, даже если исходный код изменится.

  FROM alpine AS builder
  WORKDIR /home/app
  RUN apk add --no-cache --update nodejs nodejs-npm
  COPY package.json package-lock.json ./
  RUN npm install --production

  FROM alpine
  WORKDIR /home/app
  RUN apk add --no-cache --update nodejs nodejs-npm
  COPY --from=builder /usr/src/app/node_modules ./node_modules
  COPY . .
  CMD [ 'npm', 'start' ]

结果

Окончательное изображение весит всего 51 МБ, что примерно в 17 раз меньше оригинала! И последующая скорость сборки также значительно улучшена.

Каждая инструкция FROM представляет собой этап построения, а несколько FROM представляют собой многоэтапное построение. Хотя окончательное сгенерированное изображение может быть только результатом последнего этапа, файлы на предыдущем этапе можно скопировать на более поздний этап. значение многоэтапного строительства.

В приведенном выше Dockerfile мы сначала скопировали package.json, а затем npm install.На втором этапе построения мы напрямую скопировали скачанный node_moduls на первом этапе.В следующей сборке, если не будут добавлены новые зависимости, Docker будет использовать node_modules из кэша, что сокращает время развертывания.

Используя команду docker inspect imageId, мы видим, что хотя у нас есть несколько инструкций, финальное изображение имеет только 5 слоев, что является механизмом совместного использования слоев.

Использование многоэтапных сборок позволяет использовать кэш образов Docker, что значительно сокращает время до окончательного развертывания в рабочей среде.

В заключение

В реальной производственной среде нет причин использовать размер образа в ГБ, если вам действительно нужно ускорить развертывание, и вас мучает медленный CI/CD, то многоэтапная сборка будет очень полезным способом.

Надеюсь, эта короткая статья будет полезна всем, кто рассматривает возможность использования Docker для разработки или развертывания приложений на основе Node.js.

Посмотреть исходный текст

Сфокусируйся наgithubПодробное объяснение одного вопроса интервью каждый день