Docker Compose Watch: попрощайтесь с томами Docker!

Перевод статьи «Say Goodbye to Docker Volumes».

Вы когда-нибудь пытались использовать тома Docker для горячей замены (hot-reloading) в своем веб-приложении? Если вы получили такой же ужасный опыт, как и я, вам понравится новейшая функция, которую только что выпустил Docker: docker-compose watch!

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

TL;DR: Ознакомьтесь с этим файлом docker-compose и официальной документацией.

От редакции Techrocks: если вы еще не знакомы с Docker, познакомиться можно здесь — «Docker: практическое руководство для начинающих».

Приступим!

Введение

Docker только что выпустил Docker Compose Watch с Docker Compose версии 2.22. С помощью этой новой функции вы можете использовать docker-compose watch вместо docker-compose up и автоматически синхронизировать локальный исходный код с кодом в контейнере Docker без необходимости использовать тома!

Давайте посмотрим, как это работает в реальном проекте. Об этом проекте я писал ранее.

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

├── apps
│   ├── api
│   └── web
└── packages
    ├── database
    ├── eslint-config-custom
    ├── tsconfig
    └── ui

Оба приложения (api и web) уже докеризованы, а Docker-файлы находятся в корне проекта (1, 2).

Файл docker-compose.yml будет выглядеть следующим образом:

services:
  web:
    build:
      dockerfile: web.Dockerfile
    ports:
      - "3000:3000"
    depends_on:
      - api
  api:
    build:
      dockerfile: api.Dockerfile
    ports:
      - "3001:3000"from within the Docker network

Файл неплох, но, как вы знаете, работать с ним во время разработки очень хлопотно. Вам придется пересобирать образы Docker при каждом изменении кода, даже если ваши приложения, вероятно, поддерживают hot-reloading из коробки (а если нет, то с помощью чего-то вроде Nodemon).

Чтобы улучшить ситуацию, Docker Compose Watch вводит новый атрибутwatch. Атрибут watch содержит список так называемых правил. Каждое правило содержит путь, за которым оно следит, и действие, которое будет выполнено, как только файл на этом пути изменится.

Синхронизация

Если вы хотите, чтобы между хостом и контейнером синхронизировалась папка, вы должны добавить следующий код:

services:
  web: # shortened for clarity
    build:
      dockerfile: web.Dockerfile
    develop:
      watch:
        - action: sync
          path: ./apps/web
          target: /app/apps/web

При изменении файла на хосте по пути ./apps/web/ он будет скопирован в ваш контейнер по адресу /app/apps/web. Дополнительный app в целевом пути необходим, потому что это наша рабочая директория WORKDIR, определенная в Dockerfile. Это главное, что вы, вероятно, будете использовать, если у вас есть приложения с возможностью hot-reloading.

Пересборка — rebuild

Если у вас есть приложения, которые нужно компилировать, или зависимости, которые нужно переустанавливать, вам поможет такое действие (action), как пересборка (rebuild). Вместо того чтобы просто копировать файлы между хостом и контейнером, оно перестроит и перезапустит контейнер. Это очень полезно для ваших npm-зависимостей! Давайте добавим это действие:

services:
  web: # shortened for clarity
    build:
      dockerfile: web.Dockerfile
    develop:
      watch:
        - action: sync
          path: ./apps/web
          target: /app/apps/web
        - action: rebuild
          path: ./package.json
          target: /app/package.json

При каждом изменении нашего package.json мы будем перестраивать весь наш Dockerfile для установки новых зависимостей.

Sync+Restart

Помимо синхронизации и пересборки есть еще и нечто среднее, называемое sync+restart. Это действие сначала синхронизирует каталоги, а затем сразу же перезапускает контейнер без пересборки.

Большинство фреймворков обычно имеют конфигурационные файлы (например, next.config.js), которые не могут быть загружены в горячем режиме (просто синхронизировать недостаточно), но и не требуют медленной пересборки.

Ваш compose-file изменится следующим образом:

services:
  web: # shortened for clarity
    build:
      dockerfile: web.Dockerfile
    develop:
      watch:
        - action: sync
          path: ./apps/web
          target: /app/apps/web
        - action: rebuild
          path: ./package.json
          target: /app/package.json
        - action: sync+restart
          path: ./apps/web/next.config.js
          target: /app/apps/web/next.config.js

Оговорки

Но не все так радужно, есть и несколько предостережений.

Самая большая проблема с новым атрибутом watch заключается в том, что пути все еще очень простые. В документации говорится, что шаблоны Glob пока не поддерживаются, что может привести к огромному количеству правил.

Вот несколько примеров того, что работает, а что нет:

✅ apps/web

Будет соответствовать всем файлам в ./apps/web (например, ./apps/web/README.md, а также ./apps/web/src/index.tsx)

❌ build/**/!(*.spec|*.bundle|*.min).js

Глобы, к сожалению, (пока?) не поддерживаются.

❌ ~/Downloads

Все пути относительны к корню проекта!

Следующие шаги

Если вы все еще недовольны своей настройкой Docker, есть еще много способов ее улучшить!

Сотрудничество — важная часть разработки программного обеспечения, и работа в одиночку может нанести серьезный ущерб вашей команде. Медленные сборки Docker и сложные настройки не способствуют сотрудничеству! Чтобы противостоять этому, вы можете использовать расширения Docker, такие как Livecycle, для мгновенного обмена локальными приложениями docker-compose с членами вашей команды.

Поскольку вы уже используете Docker и docker-compose, все, что вам нужно сделать, — это установить расширение Docker Desktop Extension и нажать на переключатель share. После этого вы сможете поделиться уникальным URL-адресом с командой, чтобы получить отзывы!

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

Создание базового файла .dockerignore и разделение установки зависимостей и сборки кода поможет вам в этом!