Только первая часть связана с 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, там проблемы с этим вообще нет.
- Docker для Java это не про "контейнер в контейнере", а про раздельное и унифицированное "сборка" и "запуск" приложений вне зависимости от того, на чём написано — Go, Java, Python, PHP, etc
- "Docker и Java айайай" на 8-ке с каких-то сборок уже поправили в плане ограничений по CPU и памяти — если именно об этом говорится про "ждали 10"
При использовании многошагового билда в итоговый 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 использовался бы так, как вы описали. Может быть в этом подходе есть что-то, что я упускаю?
Как бы само присутствие фазы deploy намекает. Ну не в этом суть.
Результатом работы девелоперов является нечто, что может быть без изменений развёртнуто как на локальной машине, так и на проде (как минимум для воспроизводимости). В контексте использования докера это нечто является, пожалуй, докер образ, который надо куда-то запушить, чтобы он физически существовал, как нечто воспроизводимое любым членом команды, либо админом.
Если мы собираем из исходников прямо в контейнере, то мы не можем гарантировать что получится точно такой же бинарник, которые собрался и оттестировался в CI.
В крайнем случае образ должен собираться из собранных бинарников, но ни в коем случае из исходников.
Разработчикам могут быть недоступны пароли от прода, но им несомненно доступны пароли от какого-то окружения, где они могут тестировать собранные образы.
Например, это может быть локальный докер-сервер в виртуальной машине, так что mvn deploy будет просто деплоить докер локально.
Увеличение тега-это просто технические мелочи, легко делается.
Спасибо за разъяснения. Соглашаюсь с тем, что в пайплайне образ должен формироваться из собранных бинарников. Обязательно сделаю на этом акцент в дальнейшем.
В моем понимании, когда разработчик пушит что-то в гит, тогда его ветка и должна быть протестирована. А давать возможность разработчику по сути вручную отправлять образ тестироваться — не вижу особого смысла в этом. Но, видимо, это просто альтернативный подход к организации рабочего процесса.
В этом и суть разработки — поправил, быстро протестировал локально, ещё поправил.
Есть много задач когда работоспособность не проверишь, пока весь докер целиком не соберёшь.
В идеале, разработчик должен иметь возможность полностью протестировать и воспроизвести любую багу на проде даже без постоянного подключения к сети
Сначала тестируем все локально мавеном. Если все ок, то создаем ветку для задачи, коммитим изменения. Jenkins видит это, тестирует наш коммит. Мы видим результат, и в случае успеха делаем PR
Спасибо, есть над чем подумать
А еще иногда тесты врут и их тоже приходится править, так что «смотреть глазками» собранный финальный результат — это обязательный этап перед даже ревью (а уж под докером это делать или локально — это детали).
Внимательный читатель отметит, что нам ничего не мешает обратиться к бекенду напрямую в обход шлюза (http://localhost:8081/requests). Чтоб это исправить, сервисы должны быть объединены в одну сеть.
Вот с этого места поподробнее, пожалуйста. Как всё-таки канонически закрыть микросервис от вызова извне?
Только через авторизацию?
Если мы говорим про авторизацию в микросервисах, то частый способ — это использование JWT-токенов. Пользователь логинится, получает токен и далее шлет его с каждым запросом в заголовке или куке. Далее этот токен передается между сервисами, позволяя авторизовывать пользователя непосредственно в каждом сервисе.
В фрагменте, который вы привели, я имел в виду изоляцию сервиса в рамках одной сети. Эта возможность может быть предоставлена облачным провайдером (№ VPC), либо же в других случаях настроена вручную (локальная сеть, VPN). Основной момент во всех решениях — для пользователя извне должен быть доступен только шлюз.
Как во время, прям то, что нужно в данный момент :) жду следующие части
Сборка у меня падает с «не найден 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. Сам дурак. Сорри за глупый вопрос.
Учимся разворачивать микросервисы. Часть 1. Spring Boot и Docker