Первоначально опубликовано Рори Маллиганом на SitePen.com.

Итак, вы создали замечательное приложение с помощью Dojo и теперь готовы к запуску. После небольшого исследования вы узнаете, что традиционные развертывания сложны! К счастью, времена FTP-файлов давно прошли, и мы можем положиться на Docker для быстрого и надежного развертывания. Использование Docker не только задокументирует ваш процесс сборки, но также предоставит вам образ Docker, который вы можете легко развернуть в рабочей среде или запустить локально.

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

Сборка вручную

Прежде чем мы начнем настраивать нашу сборку Docker, полезно сделать шаг назад и подумать о шагах, которые мы выполняем вручную для сборки нашего приложения Dojo. Чтобы собрать приложение Dojo из исходного кода, нам необходимо:

  • Установите правильную версию Node.js и npm.
  • Установите интерфейс командной строки Dojo
  • Установить зависимости приложения
  • Создайте приложение (dojo build)

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

Создание с помощью Docker

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

Если вы просто хотите просмотреть, не стесняйтесь проверить пример проекта Docker + Dojo.

Создайте Dockerfile в каталоге проекта, который выглядит следующим образом:

FROM nginx:1.17 
RUN apt-get update && apt-get install -y curl && \\ 
    curl -sL https://deb.nodesource.com/setup_8.x | bash - && \\ 
    apt-get install -y build-essential nodejs && \\ 
    npm i -g @dojo/cli 
COPY . /usr/local/app 
WORKDIR /usr/local/app 
RUN npm i 
RUN dojo build 
RUN cp -R /usr/local/app/output/dist/* /usr/share/nginx/html/

Если приведенный выше пример Dockerfile не имеет смысла, это поможет объяснить детали:

  1. FROM nginx:1.17 — Мы начнем с официального образа nginx и применим наши изменения поверх него. Это здорово, потому что мы получаем веб-сервер, который предварительно создан и уже настроен для нас с разумными значениями по умолчанию. Вы можете узнать больше об официальном образе nginx на его странице Docker Hub.
  2. RUN apt-get update... — здесь мы устанавливаем наши инструменты сборки. В официальном образе nginx не установлен Node.js, поэтому нам нужно его установить. После установки Node.js мы можем установить Dojo CLI глобально.
  3. COPY . /usr/local/app — Здесь мы копируем все файлы из корневого каталога нашего проекта (то же самое, что и наш Dockerfile) в образ Docker. Мы копируем эти файлы во временный каталог, я выбрал /usr/local/app, потому что нам нужно место для сборки нашего приложения.
  4. WORKDIR /usr/local/app - Установите наш рабочий каталог в корень нашего проекта. Любые RUN команды, которые мы выполняем, теперь будут выполняться в контексте этого каталога.
  5. RUN npm i — Установить зависимости сборки нашего приложения.
  6. RUN dojo build — Создайте приложение Dojo. Когда это будет завершено, наши встроенные файлы будут находиться в /usr/local/app/output/dist.
  7. RUN cp -R /usr/local/app/output/dist/* /usr/share/nginx/html/ — Скопируйте наши скомпилированные файлы в каталог хостинга nginx. В нашем образе nginx все, что копируется в /usr/share/nginx/html, становится статическим содержимым.

Теперь, когда мы настроили Dockerfile, мы можем создать образ с помощью простой команды:

1 docker build -t my-amazing-app.

Эта команда указывает Docker выполнить инструкции в нашем Dockerfile и пометить финальное изображение тегом «my-amazing-app». Вам не нужно помечать свое изображение тегом, но это немного упрощает отслеживание его назначения.

После того, как образ создан, вы можете легко попробовать, выполнив эту команду:

1 docker run -p 8080:80 my-amazing-app

Образ nginx ничего не выводит на экран при запуске, поэтому вы не получите много отзывов. Когда ваш образ запущен, посетите http://localhost:8080, чтобы увидеть ваше приложение в действии. Вы можете нажать CTRL+C, когда закончите и захотите закрыть контейнер Docker.

Строительство умнее

Мы сделали это! Мы успешно создали Docker-образ нашего встроенного веб-приложения Dojo. Мы можем использовать этот образ для тестирования в среде непрерывной интеграции или просто развернуть этот образ на нашем веб-сервере и выпустить наше приложение в открытый доступ.

Хотя мы успешно построили свой имидж, мы использовали несколько наивный подход. Давайте проверим наше изображение, чтобы узнать, как мы можем улучшить его.

1 docker run -it my-amazing-app du -h --max-depth=1 /usr/local/app

Вы должны увидеть что-то вроде этого:

1  56K /usr/local/app/tests 
2  88K /usr/local/app/src
3  7.0M    /usr/local/app/output
4  24K /usr/local/app/.idea
5  450M    /usr/local/app/node_modules
6  458M    /usr/local/app

Если вы помните наш процесс сборки приложения, мы скопировали приложение в наш образ, построили его, а затем скопировали встроенные файлы в размещенный каталог. Глядя на вывод, который мы только что напечатали, старые файлы, которые мы оставили в результате нашей сборки, занимают 450 МБ! Мы можем подтвердить это, взглянув на размер нашего образа и сравнив его с размером образа nginx, который мы используем.

1  docker image inspect my-amazing-app --format='{{.Size}}' 
2  844208614 
3  docker image inspect nginx:1.17 --format='{{.Size}}' 
4  109258867

О, о! Похоже, проблема намного серьезнее, чем ожидалось. Наше приложение более чем на 700 МБ больше, чем базовый образ nginx. Это связано с тем, что мы также установили Node.js, curl и интерфейс командной строки Dojo, которые нам только нужны для создания нашего приложения.

Давайте переоценим наш подход. У нас действительно есть два разных процесса, которые мы объединили в один Dockerfile. Во-первых, мы хотим создать наше приложение (это включает в себя установку зависимостей Node.js и запуск интерфейса командной строки Dojo для создания готовых к использованию ресурсов). Во-вторых, мы хотим настроить образ nginx, чтобы он мог размещать наши файлы.

Мы могли бы добавить дополнительные команды RUN в наш Dockerfile, чтобы удалить наш старый каталог /usr/local/app после сборки, а также удалить Node.js и интерфейс командной строки додзё, но это действительно загрязнит наш Dockerfile посторонними командами. Кроме того, эти дополнительные шаги требуют дополнительного времени.

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

Давайте рефакторим наш Dockerfile для использования многоэтапной сборки.

1  FROM node AS builder 
2  RUN npm i -g @dojo/cli 
3 
4  COPY . /usr/local/app 
5  WORKDIR /usr/local/app 
6  
7  RUN npm i 
8  RUN dojo build 
9  
10 FROM nginx:1.17 
11 COPY --from=builder /usr/local/app/output/dist/ /usr/share/nginx/html/

Наш рефакторинг Dockerfile легче читать после преобразования его в многоэтапную сборку! Мы взяли два этапа, сборку и настройку, и выполнили их в разных образах Docker. На этапе сборки мы используем официальный образ Node.js, устанавливаем интерфейс командной строки Dojo, копируем файлы нашего проекта и выполняем сборку. Затем, на этапе настройки, мы копируем файлы, созданные на этапе сборки (-from=builder), и помещаем их в каталог статического хостинга nginx. Фаза сборки отбрасывается, оставляя нам образ nginx с скопированными файлами. Никаких дополнительных Node.js, node_modules, Dojo CLI или чего-либо еще, что нам нужно только для сборки нашего приложения.

Если мы создадим наш образ сейчас (та же команда сборки, что и раньше) и проверим размер нашего окончательного образа, мы увидим, что теперь он лишь незначительно больше, чем базовый образ nginx.

1  docker image inspect my-amazing-app --format='{{.Size}}' 
2  110671871

Строительство быстрее

Если вы используете Windows или Mac, вы могли заметить, что «Отправка контекста сборки демону Docker» занимает больше времени, чем хотелось бы, особенно если вы часто выполняете сборку. По умолчанию Docker отправляет весь каталог нашего проекта на виртуальную машину Docker, где будет происходить фактическое построение образа. На наших компьютерах этот каталог проекта состоит из таких вещей, как наши скрытые каталоги IDE, установленные node_modules и т. д. Нашему образу Docker они не нужны, и они просто тратят время на копирование с контекстом сборки. Подобно файлу .gitignore, который сообщает Git, какие файлы игнорировать, Docker позволяет нам игнорировать файлы при создании контекста сборки через файл .dockerignore.

Наш файл .dockerignore будет выглядеть так:

1  node_modules/ 
2  output/

С нашим файлом .dockerignore перестройте приложение, и вы заметите, что отправка контекста сборки теперь занимает лишь часть времени. Вы также можете заметить, что установка наших модулей node_modules занимает намного больше времени! Теперь, когда мы больше не отправляем node_modules, выполняемой нами команде npm i предстоит серьезная работа!

Возможно, вы не удивитесь, узнав, что у Docker есть решение и для этого! В конце каждой команды в нашем Dockerfile Docker кэширует результат команды. Если мы запустим команду сборки дважды подряд, второй запуск завершится очень быстро — все команды уже кэшированы. Если файл, участвующий в этапе сборки, изменяется, кеш становится недействительным, и каждая команда после также становится недействительной. В практическом смысле это означает, что каждый раз, когда мы изменяем наше приложение, что приводит к изменению нашего шага COPY . /usr/local/app, мы делаем недействительным каждый шаг сборки, зависящий от этих файлов, например наш RUN npm i. Это не идеально, так как наши зависимости должны переустанавливаться только тогда, когда мы меняем наш файл package.json.

С некоторыми небольшими рефакторингами нашего Dockerfile мы можем использовать кеш сборки Docker в наших интересах.

1  FROM node AS builder 
2  RUN npm i -g @dojo/cli 
3  
4  COPY package.json /usr/local/app/ 
5  COPY package-lock.json /usr/local/app/ 
6  
7  WORKDIR /usr/local/app 
8 
9  RUN npm i 
10 
11 COPY . /usr/local/app 
12 RUN dojo build 
13 
14 FROM nginx:1.17 
15 COPY --from=builder /usr/local/app/output/dist/ /usr/share/nginx/html/

Первое, что мы делаем сейчас, это копируем наши файлы package.json и package-lock.json. После внесения этих изменений мы можем установить наши зависимости Node.js. Теперь, если мы изменим один из файлов исходного кода нашего приложения, кэш не станет недействительным, поскольку эти файлы еще не были скопированы в образ. Это означает, что мы можем изменить исходные файлы нашего приложения, перестроить наш образ Docker, и наш кэш RUN npm i все еще действителен, полностью пропустив этот шаг и выбрав шаг COPY . /usr/local/app, который, в свою очередь, сделает недействительным шаг RUN dojo build и скопирует новые файлы в новый образ nginx.

Резюме

В этой статье мы взяли приложение Dojo и продемонстрировали, как собрать и создать Docker-образ приложения. Затем мы продемонстрировали, как можно использовать многоэтапную сборку, чтобы значительно упростить наш окончательный образ Docker. Мы завершили несколько простых изменений в нашем Dockerfile, чтобы показать вам, как мы можем использовать кэш сборки Docker для значительного повышения производительности наших повторных сборок.

Мы не рассказывали, как развернуть созданный вами образ Docker, но это может быть так же просто, как загрузить образ в концентратор Docker и вытащить его на свой веб-хост. Теперь, когда так много веб-хостов поддерживают Docker, развертывание вашего приложения Dojo никогда не было проще.

Вы можете ознакомиться с нашим окончательным Примером проекта Dojo + Docker.

Узнать больше

Нужна помощь в оптимизации вашего приложения для производства, определении идеального рабочего процесса DevOps для вашего следующего приложения JavaScript или TypeScript или в создании приложения, которое эффективно создавать, обновлять, развертывать и поддерживать? Если это так, свяжитесь с нами, чтобы обсудить, как мы можем с вашим следующим проектом!

Следите за SitePen в Twitter, Facebook, LinkedIn.

Первоначально опубликовано на https://www.sitepen.com 16 июля 2019 г.