Обновить
Комментарии 31

Только первая часть связана с java, дальше это image и остальным штукам тип k8s пофиг. Из оркестраторов еще есть nomad, которым может запускать java вообще без docker.

Запускаем шлюз:

./mvnw package -DskipTests
java -jar target/microservices-backend-1.0.0.jar

Может быть имелось ввиду "java -jar target/microservices-gateway-1.0.0.jar", а то похоже на опечатку?

Да, опечатка. Поправил, спасибо
ENTRYPOINT ["java","-jar","target/microservices-gateway-1.0.0.jar"]

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


ENTRYPOINT ["sh", "-c", "java -jar target/microservices-gateway-1.0.0.jar"]

Далее, сборка приложения производит "мусор", который вам не нужен для запуска. Опять же инструменты вроде maven и сами исходники не нужны. Смотреть в сторону multi-stage builds.


Я понимаю, что "это-ж для начинающих". Но потом подобное кочует из проекта в проект.

Зачем вам в приложении бесполезный sh с pid=1? Переменные окружения и так передаются.


Если имелось в виду, что переменные в аргументах не интерполируются, то там нужен exec. Но там проще закинуть флаги через command

Ошибся. Должно быть "ENTRYPOINT ["sh", "-c", "exec java ...."


Бесполезного процесса при этом не создается.


Хотя если честно я distroless java использую от google, там проблемы с этим вообще нет.

Спасибо за комментарий. В статье описаны два Dockerfile — один наиболее примитивный и как раз multi-stage Dockerfile. Насколько могу судить, в итоговый образ не попадут ни maven, ни исходники.
НЛО прилетело и опубликовало эту надпись здесь
Можете дать что-нибудь почитать по части «Docker и Java айайай, только на железе»?
НЛО прилетело и опубликовало эту надпись здесь
  1. Docker для Java это не про "контейнер в контейнере", а про раздельное и унифицированное "сборка" и "запуск" приложений вне зависимости от того, на чём написано — Go, Java, Python, PHP, etc
  2. "Docker и Java айайай" на 8-ке с каких-то сборок уже поправили в плане ограничений по CPU и памяти — если именно об этом говорится про "ждали 10"
Anshelen Получается, что ваш Docker контейнер будет содержать исходники вашего приложения? В плане безопасности разве это нормальная практика? А так, в целом и общем идея понятна, т.е. готовим сразу целиковый проект с docker образом и пушим его в репозиторий и уже далее на продакшен сервере просто пулимся с имеющегося docker репозитория, спасибо за статью.

При использовании многошагового билда в итоговый Docker образ исходники включены не будут. На шаге 1 (он помечен как 'builder') выполняется сборка приложения, и генерируется jar-архив /src/target/microservices-backend-1.0.0.jar.
На 3 шаге мы копируем только этот jar-архив в итоговый образ:


COPY --from=builder /src/target/microservices-backend-*.jar app.jar

При формировании образа докер удалит все файлы, относящиеся к промежуточным шагам и не скопированные нами в итоговый образ явно. В итоге в конечном образе у нас будут только файлы из базового образа, jar-архив и JRE из шага 2.

Я не понимаю. Многошаговая сборка была дописана после публикации статьи, или народ на хабре настолько "не читал, но осуждает"?

Содержимое статьи не менялось с момента публикации (не считая опечатки, маленькие правки). Видимо, стоило более явно акцентировать, что первый Dockerfile "в лоб" применяться не должен. Внесу правку

А зачем, собирать прямо в докере?? Это как-то дико выглядит и не совсем понятно.

Оптимальный вариант — это конечно сборка в Jenkins-е с прогоном всех тестов, стат анализаторов, хранением исторических артефактов, с потенциальным использованием разных тулов, типа nodejs и пр… Контейнер — явно не настолько продвинутое окружение чтобы там устраивать сборку. Кроме того, требуется доступ в репозиторий исходников.

mvn deploy должен запускать докер и билдить образ из собранных артефактов и пушить в докер-репозиторий, а CI просто вызывать mvn deploy
НЛО прилетело и опубликовало эту надпись здесь

Спасибо за комментарий.
Я полностью с вами согласен в том, что инструменты непрерывной интеграции наподобие Jenkins необходимы. Без хорошего пайплайна организовать эффективную работу с микросервисами вряд ли возможно. Создание такого пайплайна я попробую описать в 3 статье этой серии.
Однако не могу согласиться с таким использованием Maven. В моем понимании, Maven должен выступать исключительно инструментом сборки и тестирования проекта, но не деплоя, так как является в первую очередь инструментом разработчика, а не devops-инженера. Для того чтоб запушить образ в докер-репозиторий, плагину необходимо предоставить как минимум пароли. Вероятно это делается через переменные среды. Разработчикам эти пароли недоступны, соответственно для них команда mvn deploy закрыта. Так зачем же тогда нагружать этим проект? И как обеспечить увеличение версии (тега) образа — также передавать извне?
Честно сказать, мне не пришлось поработать в проектах, где Maven использовался бы так, как вы описали. Может быть в этом подходе есть что-то, что я упускаю?

Неправда, maven — универсальный инструмент, а gradle -ещё универсальнее.
Как бы само присутствие фазы deploy намекает. Ну не в этом суть.

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

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

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

Увеличение тега-это просто технические мелочи, легко делается.

Спасибо за разъяснения. Соглашаюсь с тем, что в пайплайне образ должен формироваться из собранных бинарников. Обязательно сделаю на этом акцент в дальнейшем.
В моем понимании, когда разработчик пушит что-то в гит, тогда его ветка и должна быть протестирована. А давать возможность разработчику по сути вручную отправлять образ тестироваться — не вижу особого смысла в этом. Но, видимо, это просто альтернативный подход к организации рабочего процесса.

Я извиняюсь, а как вы будете пушить не протестированный код?

В этом и суть разработки — поправил, быстро протестировал локально, ещё поправил.
Есть много задач когда работоспособность не проверишь, пока весь докер целиком не соберёшь.

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

Сначала тестируем все локально мавеном. Если все ок, то создаем ветку для задачи, коммитим изменения. Jenkins видит это, тестирует наш коммит. Мы видим результат, и в случае успеха делаем PR

Исключительно мавеном, вы максимум юнит тесты (да и то, только на Java часть ) прогоните, это крайне редко бывает достаточным.

Спасибо, есть над чем подумать

абсолютно согласен.
А еще иногда тесты врут и их тоже приходится править, так что «смотреть глазками» собранный финальный результат — это обязательный этап перед даже ревью (а уж под докером это делать или локально — это детали).
Внимательный читатель отметит, что нам ничего не мешает обратиться к бекенду напрямую в обход шлюза (http://localhost:8081/requests). Чтоб это исправить, сервисы должны быть объединены в одну сеть.


Вот с этого места поподробнее, пожалуйста. Как всё-таки канонически закрыть микросервис от вызова извне?
Только через авторизацию?

Если мы говорим про авторизацию в микросервисах, то частый способ — это использование JWT-токенов. Пользователь логинится, получает токен и далее шлет его с каждым запросом в заголовке или куке. Далее этот токен передается между сервисами, позволяя авторизовывать пользователя непосредственно в каждом сервисе.
В фрагменте, который вы привели, я имел в виду изоляцию сервиса в рамках одной сети. Эта возможность может быть предоставлена облачным провайдером (№ VPC), либо же в других случаях настроена вручную (локальная сеть, VPN). Основной момент во всех решениях — для пользователя извне должен быть доступен только шлюз.

Как во время, прям то, что нужно в данный момент :) жду следующие части

Вопросик (на карантине наконец-то добрался до статьи) про образ builder.
Сборка у меня падает с «не найден mvnw». Ну и собственно гляжу я в Dockerfile.

FROM adoptopenjdk/openjdk11:jdk-11.0.5_10-alpine as builder
ADD . /src <- добавляем директорию с src
WORKDIR /src <- переходим в нее
RUN ./mvnw package -DskipTests <- стартуем maven wrapper


ну дык pom.xml и mvnw у нас не скопировались. Они же не в src, а папкой выше.
Я чего-то не понимаю или какая-то ошибка?

В команде ADD первым аргументом передается директория на локальной машине, а вторым — в образе. Я согласен, что во избежание путаницы стоило назвать эту папку как-нибудь по-другому (№ /app).


Докер-файл я перепроверил — он должен работать. Проверьте, что у вас в репозитории в корне присутствует файл mvnw. Если он все же есть, то можно вставить строчку "RUN ls" после "WORKDIR /src" и посмотреть, что представляет из себя рабочая директория в контейнере.

Говорили умные люди — не собирай докер-имиджи из под винды :-)
CRLF. Сам дурак. Сорри за глупый вопрос.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.