Как использовать docker на этапе разработки жизненного цикла DevOps?

У меня есть пара вопросов, связанных с использованием Docker на этапе разработки.

DevOps

Я собираюсь предложить три различных сценария использования Docker в среде разработки. Представим, что мы создаем REST API в Java и Spring Boot. Для этого мне понадобится база данных MySQL.

  1. Первый сценарий - это создание docker-compose для разработки с контейнером MySQL и производственного docker-compose с MySQL и Java-приложением (jar) в другом контейнере. Для разработки я запускаю docker-compose-dev.yml, чтобы запустить только базу данных. Приложение запускается и отлаживается с помощью IDE, например, IntelliJ Idea. Любые изменения, внесенные в код, IDE распознает и перезапустит приложение, применив изменения.

  2. Второй сценарий должен иметь как для среды разработки, так и для производственной среды docker-compose с контейнерами базы данных и приложений. Таким образом, каждый раз, когда я вношу изменения в код, я должен перестраивать изображение, чтобы изменения загружались в изображение, а контейнеры снова запускались. Этот сценарий может быть наиболее типичным и использоваться для разработки с помощью Docker, но он кажется очень медленным из-за необходимости перестраивать образ каждый раз, когда происходит изменение.

  3. Третий сценарий представляет собой смесь двух предыдущих. Два docker-compose. Docker-compose для разработки содержит оба контейнера, но с механизмами, которые позволяют выполнять перезагрузку приложения в реальном времени, отображать тома и использовать, например, Spring Dev Tools. Таким образом, контейнеры запускаются, и в случае любого изменения в файлах контейнер приложения обнаружит изменение и будет перезапущен. Для производства docker-compose будет создаваться просто с обоими контейнерами, но без функции live reload. На мой взгляд, это был бы идеальный сценарий, но я думаю, что он очень зависит от используемых технологий, поскольку не все допускают перезагрузку в реальном времени.

Вопросы следующие.

  • Какой из этих сценариев наиболее типичен при использовании Docker для фазы?

  • Хорошо ли рассмотрен сценарий 1? То есть докеризуйте только внешние службы, такие как базы данных, очереди и т. Д., И выполняйте разработку и отладку приложения с помощью IDE, не используя для этого Docker.

Сомнения и сценарии, которые я поднимаю, возникли после того, как я поднял проблему, имеющуюся в сценарии 2. При каждом изменении кода необходимость перестраивать образ и снова запускать контейнеры - значительная трата времени. Короче говоря, возникает вопрос: как этого избежать?

Спасибо заранее за ваше время.

ПРИМЕЧАНИЕ. Это может быть вопрос, требующий отдельного мнения, но было бы неплохо узнать, как разработчики обычно решают эти проблемы.


person Mr. Mars    schedule 09.03.2020    source источник
comment
Я бы порекомендовал ваш первый вариант из трех; это кажется хорошей комбинацией использования Docker и вашей IDE в режимах, в которых они оба хороши.   -  person David Maze    schedule 10.03.2020
comment
@DavidMaze. Вы когда-нибудь пробовали ситуацию, описанную в сценарии 3, с какой-либо технологией, позволяющей перезагрузку в реальном времени? Из простого любопытства. Спасибо!   -  person Mr. Mars    schedule 10.03.2020
comment
Решили ли вы свои вопросы / сомнения?   -  person JRichardsz    schedule 15.03.2020
comment
@JRichardsz, я готов выслушать максимально возможные ответы, поскольку это скорее упражнение, чтобы узнать, как разработчики обычно используют Docker для среды разработки.   -  person Mr. Mars    schedule 16.03.2020
comment
Когда вы говорите «среда разработки», вы имеете в виду только ту стадию, на которой программисты разрабатывают программное обеспечение на своем портативном компьютере (localhost)? Или ваш вопрос может быть следующим: как использовать докер на всех этапах жизненного цикла разработки программного обеспечения (код, сборка, тестирование, выпуск, развертывание и т. Д.)?   -  person JRichardsz    schedule 16.03.2020
comment
@JRichardsz Точно, я имею в виду, когда программисты разрабатывают на своих ноутбуках. Но было бы здорово дать краткое объяснение каждой из фаз, которые вы комментируете. Заранее спасибо. Например, в чем разница между использованием Docker на этапах сборки, выпуска или развертывания?   -  person Mr. Mars    schedule 19.03.2020


Ответы (4)


Заявление об ограничении ответственности: это мое собственное мнение по этому поводу, заданное г-ном Марсом. Несмотря на то, что я сделал все возможное, чтобы подкрепить свой ответ фактическими источниками, он в основном основан на моем собственном опыте и немного здравом смысле

Какой из этих сценариев наиболее типичен при использовании Docker для разработки?

Я видел все 3 сценария в нескольких проектах, каждый со своими достоинствами и недостатками. Однако я думаю, что сценарий 3 с Docker Compose, допускающим динамическую перезагрузку кода, является наиболее выгодным с точки зрения гибкости и согласованности:

  • Dev и Prod Docker Compose близки друг к другу, что означает, что среда разработки максимально приближена к среде Prod.
  • Вам не нужно постоянно перестраивать образ при разработке, но это легко сделать, когда вам нужно
  • Многие технологии поддерживают такой сценарий, такие как Spring Dev Tools, как вы упомянули, но также Python Flask и т. Д.
  • Вы можете легко использовать Docker Compose расширяет механизм общего доступа к конфигурации (также возможно в сценарии 2)

Хорошо ли рассмотрен сценарий 1? То есть докеризуйте только внешние службы, такие как базы данных, очереди и т. Д., И выполняйте разработку и отладку приложения с помощью IDE, не используя для этого Docker.

Сценарий 1 довольно распространен, но среда IDE, вероятно, будет отличаться от среды контейнера Docker (и было бы сложно поддерживать соответствие версии для каждой библиотеки, зависимостей и т. Д. Из среды IDE в среду Docker). Также, вероятно, потребуется пройти промежуточный этап между Dev и Production, чтобы фактически протестировать образ Docker, созданный после того, как Dev работает, прежде чем перейти в производство.

По моему собственному опыту, делать это здорово, когда вы не хотите слишком много работать с Docker, когда фактически занимаетесь разработкой и / или язык или технология, которые вы используете, не адаптированы для динамической перезагрузки, как описано в сценарии 3. Но в конце концов это только добавляет дрейф между средами и усложняет метод развертывания Dev и Prod.

необходимость перестроить образ и снова запустить контейнеры - значительная трата времени. Короче говоря, возникает вопрос: как этого избежать?

Помимо описанных вами сценариев, у вас есть способы прилично (даже резко) сократить время сборки образа, используя Кэш сборки Docker и создание файла Dockerfile. Например, приложение Python обычно копирует код в качестве последнего (или почти последнего) шага сборки, чтобы избежать недействительности кеша, а для приложения Java можно было бы разделить код, чтобы избежать компиляции всего приложения каждый раз. изменений кода - это будет зависеть от вашей реальной настройки.


Я лично использую рабочий процесс, примерно соответствующий сценарию 3, например:

  • файл docker-compose.yml, соответствующий моей производственной среде
  • docker-compose.dev.yml, который переопределит некоторые аспекты моего основного файла Docker Compose, такие как код подключения с моей машины, добавление специальных флагов разработчика к командам и т. д. - он будет запускаться, например,
    docker-compose -f docker-compose.yml -f docker-compose.dev.yml 
    
    , но также можно было бы иметь docker-compose.override.yml as Docker Compose по умолчанию использует для переопределения
  • в некоторых ситуациях мне пришлось бы использовать другие переопределения для конкретных ситуаций, таких как docker-compose.ci.yml на моем CI, но обычно основного файла Docker Compose достаточно для описания моей среды Prod (и если это не так, docker-compose.prod.yml делает трюк)
person Pierre B.    schedule 12.03.2020
comment
Большое спасибо за полный ответ, @Pierre B. Мы ценим, что вы делитесь своим опытом в этой области. Вопрос, связанный со случаем 3. Поскольку априори сценарий граничит с идеалом, отладка немного усложнит проблему по сравнению со сценарием 1, верно? В этом случае потребуется удаленная отладка, например, в случае Java, верно? Еще раз спасибо :) - person Mr. Mars; 12.03.2020
comment
В случае отладки Java вам, вероятно, придется настроить удаленную отладку. Да. Чтобы немного модерировать, сценарий 3 - мой личный фаворит, но я бы не сказал, что он идеальный, так как он будет сильно зависеть от вашей ситуации, и какой-то сценарий может быть гораздо более подходящим в некоторых контекстах. Другой вариант - сохранить файл Docker Compose, как в сценарии 3, но просто запустить приложение из среды IDE и настроить его для использования контейнера MySQL (и, при необходимости, остановить контейнер Java, хотя это может быть необязательно) - person Pierre B.; 12.03.2020
comment
Отлично, как я и думал. Если вы отлаживаете непосредственно в среде IDE, сохраняя состав докера из сценария 3, это будет очень близкое решение сценария 1, но без отказа от всего, что предлагает сценарий 3, поскольку вы также можете иметь приложение Java в контейнере. Это правильно? - person Mr. Mars; 12.03.2020
comment
Точно! Фактически, вы можете выполнить сценарий 1 со сценарием 3 - person Pierre B.; 12.03.2020
comment
Когда вашей среде IDE требуется сконфигурированный JDK или какой-либо другой SDK, как вы с этим справитесь? Вы указываете на установку в контейнере? - person Vidya; 28.07.2020

Я использую что-то похожее на ваш третий сценарий для своего веб-разработчика, но он основан на Node. Итак, у меня есть 3 файла docker-compose (на самом деле 4, один базовый, а содержит все обычные вещи для других) для сред разработки, подготовки и производства.

Поэтапная конфигурация docker-compose аналогична производственной конфигурации, за исключением SSL, портов и других вещей, которые могут не позволять использовать ее локально.

У меня есть отдельный контейнер для каждой службы (например, БД, очередь), а для разработчиков у меня также есть дополнительная база данных разработчика и контейнеры очереди, в основном для запуска автоматических тестов. В среде разработки весь исходный код монтируется в контейнеры, поэтому он позволяет использовать IDE / редактор по выбору вне контейнера и видеть изменения внутри.

Я использую supervisor для управления моими воркерами внутри контейнера с воркерами, и у меня есть несколько команд для перезапуска моих воркеров вручную, когда мне это нужно. . Может быть, у вас есть что-то похожее на перекомпиляцию / перезапуск вашего Java-приложения. Или, если у вас есть представление о том, как организовать обнаружение изменений исходного кода приложения и автоматическую перезагрузку приложения, это может быть лучший вариант. Кстати, вы подали мне идею исследовать что-то подобное, подходящее для моего случая.

Для промежуточной и производственной среды мой исходный код включен в соответствующий контейнер с использованием рабочего Dockerfile. И у меня есть несколько команд для перезапуска всего, что мне нужно, и это обычно включает перестройку контейнеров, но из-за кеша Docker это не занимает много времени (около 20 секунд). А учитывая, что переключение между средами - не слишком частая операция, мне это вполне комфортно.

Производственная конфигурация docker-compose используется только во время развертывания, потому что она включает SSL, правильные порты и имеет некоторые дополнительные производственные возможности.

Обновите информацию о перезапуске серверного приложения с помощью Supervisor:

Вот как я использую его в своих проектах:

Часть моего Dockerfile с установкой Supervisor:

FROM node:10.15.2-stretch-slim

RUN apt-get update && apt-get install -y \
  # Supervisor
    supervisor \
    ...

...

# Configs for services/workers managed by supervisor
COPY some/path/worker-configs/*.conf /etc/supervisor/conf.d/

Это пример одной из конфигураций Supervisor для воркера:

[program:myWorkerName]
command=/usr/local/bin/node /app/workers/my-worker.js
user=root
numprocs=1
stopsignal=INT
autostart=true
autorestart=true
startretries=10

В этом примере в вашем случае command должно запустить ваше приложение Java.

А это пример псевдонимов команд для удобного управления Supervisor извне контейнеров. Я использую Makefile в качестве универсального средства запуска всех команд, но это может быть что-то еще.

# Used to run all workers
su-start:
    @docker exec -t MY-WORKERS-CONTAINER-NAME supervisorctl start all

# Used to stop all workers
su-stop:
    @docker exec -t MY-WORKERS-CONTAINER-NAME supervisorctl stop all

# Used to restart all workers
su-restart:
    @docker exec -t MY-WORKERS-CONTAINER-NAME supervisorctl restart all

# Used to check status of all workers
su-status:
    @docker exec -t MY-WORKERS-CONTAINER-NAME supervisorctl status

Как я описал выше, эти команды супервизора необходимо запускать вручную, но я думаю, что можно реализовать, возможно, другого воркера на основе узла или некоторого наблюдателя вне контейнера с рабочими, которые будут обнаруживать изменения файловой системы для каталога источников и запускать эти команды автоматически. . Я думаю, что можно реализовать что-то подобное с помощью Java, вот так или это.

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

person dajnz    schedule 12.03.2020
comment
Спасибо, @dajnz! Итак, чтобы сделать livereload использовать супервизор? Или вы используете другой механизм? Я думаю, что предлагаемое вами решение - очень хорошее решение. Цель вопроса - узнать, как обычно это делают разработчики. Если это не слишком много вопросов, не могли бы вы привести пример того, как делать то, что вы обсуждаете с руководителем? Для узла я видел это, и это выглядит очень хорошо: hackernoon.com/ - person Mr. Mars; 12.03.2020
comment
Хорошо, давайте проясним, что именно мы понимаем под livereload. Я вижу, что есть два значения: 1) перезапуск всех серверных приложений при изменении внутреннего источника, 2) перезагрузка ресурсов веб-страниц (JS, CSS, разметка) при изменении материала в редакторе. Так что вы это имеете в виду? - person dajnz; 12.03.2020
comment
Я имею в виду первый случай, который вы комментируете, - перезапуск серверного приложения перед изменением кода. Спасибо и извините. - person Mr. Mars; 12.03.2020
comment
Пожалуйста, проверьте мой ответ с подробностями о том, как я использую Supervisor. - person dajnz; 12.03.2020

Я видел, как все они использовались в разных сценариях. Есть несколько ошибок, которых следует избегать:

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

  2. Права доступа к файлам с хост-томами могут быть сложными в зависимости от вашей версии докера. Некоторые из новых установок Docker Desktop автоматически обрабатывают сопоставления uid, но если вы разрабатываете непосредственно в Linux, вам необходимо убедиться, что контейнеры работают с тем же uid, что и ваш пользователь хоста.

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

Рассматривая каждый из вариантов, вот моя оценка каждого:

  1. Контейнеризация только БД: это хорошо работает, когда у разработчиков уже есть среда разработки для выбранного языка, и нет риска появления внешних зависимостей, например разработчик обновляет свою установку JDK до более новой версии, чем создается образ. Он следует идее контейнеризации зависимостей в первую очередь, а также дает разработчикам знакомую интеграцию IDE с их приложением.

  2. Восстановление изображения для каждого изменения. Это, как правило, наименее идеальный вариант для рабочего процесса разработчика, но самый быстрый для реализации, когда вы не знакомы с инструментами. Я дам 4-й вариант, который считаю улучшением этого.

  3. Все в контейнере, том монтируется и выполняется оперативная перезагрузка: это наиболее сложно реализовать, и для его поддержки требуется, чтобы сам язык поддерживал такие вещи, как оперативная перезагрузка. Однако, когда они это делают, разработчики почти без проблем могут быстро освоить новый проект без необходимости устанавливать какие-либо другие инструменты для начала работы.

  4. Перестройте приложение в контейнере с монтированием тома: это промежуточная точка между 2 и 3. Если у вас нет оперативной перезагрузки, вам, вероятно, придется перекомпилировать или перезапустить интерпретатор, чтобы увидеть какие-либо изменения. Вместо того, чтобы перестраивать образ, я помещаю этап перекомпиляции в точку входа в образ для разработки. Я смонтирую код в контейнер и запускаю полный JDK вместо JRE (или любого другого компилятора). Я использую именованные тома для любых кешей зависимостей, поэтому их не нужно загружать при каждом перезапуске. Затем способ увидеть изменения - перезапустить этот контейнер. Шаги идентичны скомпилированному двоичному файлу вне контейнера: остановка старой службы, повторная компиляция и перезапуск службы, но теперь это происходит внутри контейнера, в котором должны быть те же инструменты, что и при создании производственного образа.

Для варианта 4 я обычно использую многоступенчатую сборку, в которой есть этапы сборки, разработки и выпуска. Этап сборки извлекает код и компилирует его, этап разработки - это тот же базовый образ, что и сборка, но с точкой входа, которая выполняет компиляцию / запуск, а этап выпуска копирует результат этапа сборки в минимальную среду выполнения. Затем у разработчиков есть файл набора для разработки, который создает образ разработки и запускает его с подключениями тома и открытыми портами отладки.

person BMitch    schedule 20.03.2020

Прежде всего, docker-compose предназначен только для фазы разработки и тестирования, а не для производства. Пример:

При минимальной и базовой компоновке докеров все ваши контейнеры будут работать на одном компьютере? Для целей разработки это нормально, но при производстве размещать все приложения на одном компьютере - это риск.

Мы будем предполагать

  • 01 Java API
  • 01 база данных mysql
  • 01 веб-приложение, которому требуется api
  • все эти приложения уже работают

Быстрый ответ

Если вам нужно исправить или добавить новую функцию в java api, я советую вам использовать ide, например eclipse или IntelliJ Idea. Почему?

  • Потому что Java требует компиляции.
  • Компиляция внутри контейнера докеров займет больше времени из-за зависимостей maven
  • В IDE есть автозаполнение кода
  • так далее

На этом этапе разработки Docker помогает вам с помощью одной из самых мощных функций: переносить производственные контейнеры на ваш локальный хост. Да, в этом случае docker-compose.yml - лучший вариант, потому что с одним файлом вы можете запустить все, что вам нужно: базу данных mysql и веб-приложение, но не свой java api. Откройте свой java api с вашим любимым ide.

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

Базовый жизненный цикл Devops с докером

  • Изменения исходного кода разработчика push с помощью git
  • Your continuous integration (C.I) platform detect this change and perform
    • docker build ... (In this step, unit test are triggered)
    • docker push в ваш частный хаб. Контейнер загружается на этом этапе и будет использоваться для развертывания на других серверах.
    • запуск докера или развертывание контейнера в следующей среде: тестирование
  • Люди-тестеры, селен или другая автоматизация начинают свою работу
  • Если ошибок не обнаружено, ваш C.I выполняет окончательное развертывание загруженного контейнера в производственной среде. Сборка докера не требуется, просто разверните или запустите докер.

Несколько советов

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

Java и Nodejs - стабильные языки, и ваши остальные api или веб-приложения не нуждаются в сумасшедших настройках. Просто компиляция maven и java -jar ... или npm install и npm run start.

Для журналов вы можете использовать https://www.graylog.org/, google stasckdriver или другое управление журналом.

И, как и в случае с Heroku, по возможности прекратите использовать зависимость от жесткого диска. В платформе heroku диски одноразовые, то есть исчезают при перезапуске приложения. Таким образом, вместо локального хранилища файлов вы можете использовать другую службу хранилища файлов с множеством функций.

Благодаря такому подходу ваши контейнеры могут быть легко развернуты в любом месте.

person JRichardsz    schedule 20.03.2020