(java || kotlin) && devOps
369 subscribers
6 photos
1 video
6 files
306 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Всем привет! Меня зовут Денис, можно просто Дэн. Я пришёл в разработку 20 лет назад, когда ещё был жив Delphi, где-то через 3 года перешёл на Java. За это время успел покодить, побыть тимлидом, начальником отдела, ИТ-директором, техлидом и даже на пару лет отойти от Java. Каюсь) За это время накопилось немного опыта, мыслей, вопросов, идей, которыми хочется поделиться. Поэтому и решил завести канал про Java разработку и сопутствующие ей темы. Что здесь будет - да много чего :) В частности: сравнение технологий, может даже с холиваром, а ля Java vs Kotlin, Maven vs Gradle, Lombok или не Lombok. Еще: типичные проблемы при разработке и что с ними можно сделать, мысли про новые фреймворки, библиотеки, языки, и т.д. DevOps в части CI. Совсем базовых вещей здесь не будет, для этого есть Baeldung, куча бесплатных курсов и хороших книг
Послушал доклад на JPoint про уже подзабытую уязвимость log4j и удивился 3 вещам.
Напомню: оказывается некоторые версии log4j2 позволяли вставлять в выводимое в лог сообщение выражение, позволяющее сделать удалённый вызов через механизм JNDI lookup. Причём есть возможность не только слить какие-то критичные данные, но и что-то запустить на сервере, пишущем логи, через встроенный в JDK JS движок и его функцию eval.

Первое, что удивило: уязвимость появилась в 2014 году, найдена и обсуждена на конференции по ИБ в 2016 году. Примерно тогда же появился первый фикс, который ничего не фиксил) Выстрелила в конце 2021. Как можно было про нее забыть??? Неразвитость ИБ? Информационная перегрузка? Кому-то было выгодно?

Второе: на первый взгляд эксплуатировать уязвимость сложно - нужно чтобы совпали 3 фактора: использование log4j определённого диапазона версий, отсутствие валидации пользовательского ввода и либо возможность вызвать внешний сервер для передачи конфиденциальных данных или загрузки remote code из внутренней сети, либо сервер атакующего уже должен быть во внутренней сети. Ну и детали об атакуемой системе неплохо бы знать: куда передавать сообщение с exploit-ом, что можно слить, что можно запустить на сервере. Но если подумать, несколько широко распространён log4j, как много пользовательских данных, http заголовков к нам приходит, и что-то из этого точно логируется, а главное как много ИТ систем в мире и 100% не у всех хватает денег на выделенного ИБ спеца - уязвимость совсем не кажется абстрактной, скорее наоборот.

И наконец третье. Разные я видел способы пропатчить библиотеку, сам патчил, но чтобы взять и вырезать файл - это что новое. Но способ эффективный - учитывая редкость использования функционала, в котором была найдена уязвимость, появившийся на сервере ClassNotFoundException в 99,9% случаев будет указывать на атаку. Ну и напоследок - как можно было такое редкий и опасный функционал включать по умолчанию...

Вывод: следование практикам ИБ и feature toggling рулят

#security #vulnerability
(java || kotlin) && devOps pinned «Всем привет! Меня зовут Денис, можно просто Дэн. Я пришёл в разработку 20 лет назад, когда ещё был жив Delphi, где-то через 3 года перешёл на Java. За это время успел покодить, побыть тимлидом, начальником отдела, ИТ-директором, техлидом и даже на пару лет…»
Чем kubernetes, он же k8s лучше контейнера сервлетов или сервера приложений.
Во-первых под капотом k8s лежит Docker, а значит мы получаем все его плюшки. Не зря k8s называют оркестратором контейнеров. Чем занимается оркестратор?
1) планированиеи ресурсов. Разработчик декларативно задает как он хочет задеплоить своё приложение в Deployment. А именно: сколько нужно экземпляров - replicas, сколько CPU и памяти - requests и limits, про соседству с какими приложениями его нужно размещать, по соседству с какими не нужно - labels и (anti) affinity. k8s исходя из имеющихся у него серверов, они же nodes, и их загрузки размещает экземпляры приложения - pods. Или процесс раскатки падает, если подходящих серверов нет( У серверов приложений такого механизма нет. Важно добавить - процесс поддержания требуемой конфигурации приложения непрерывный, при любых отклонениях k8s будет убивать и добавлять поды.
2) стратегия раскатки. k8s позволяет в том же Deployment в параметре strategy задать два базовых варианта: при появлении новой версии гасить все поды сразу и поднимать новые или раскатывать плавно, учитывая значения максимально допустимого превышения и нехватки числа экземпляров MaxSurge и MaxUnaveilable. Для серверов приложений такое поведение можно реализовать с помощью DevOps джобы.
3) автоматическое восстановление. Нода или под могут упасть. Падение определяется по healthcheck, заданному в том же Deployment, причём очень гибко, см livenessProbe, readinessProbe и startupProbe. k8s при наступлении такого события рестартует pod. Для серверов приложений это либо делается руками, либо автоматизируется извне.
4) горизонтальное масштабирование. С помощью HorizontalPodAutoscaller можно задать диапазон числа реплик и предельную загрузку CPU, при которой k8s добавляет или убирает реплики. Для серверов приложений - опять же ручное управление.
Отдельно хочу отменить внутреннюю DNS в k8s. Если у вас несколько приложений в одном облаке, он же cluster, то можно делать запросы по имени из Service. Вся внутренняя маршрутизация в облаке работает именно так. Для серверов приложений снова ничего такого нет.
Ну и load balancing, url/port mapping и SSL termination. В старом мире этим занимался отдельно стоящий nginx, в k8s его включили в периметр системы. #cloud
Всем привет! Решил написать пост про архитектурную проблему, с которой столкнулся недавно. Где делать сортировку данных? Обычно проблема звучит так: на клиенте, т.е в коде приложения, или на сервере, т.е. в БД? У нас все чуть сложнее. Дано:
1) БД
2) бэк-система
3) миддл-система, обслуживает веб-клиентов
4) веб-клиент.
Для начала я бы отбросил бэк-систему и веб-клиента. Веб-клиента - потому что в презентационном слое логики должно быть как можно меньше. При этом надо отметить, что исключения из этого правила могут быть, т.к клиенты сейчас мощные - 4+ ядра, 8+ Гб памяти. А бэк-систему исключаем потому, что с одной стороны рядом есть БД, а с другой - сортировка нужна для отображения пользователю, поэтому логичнее ее переместить поближе к месту использования. Т.е. сводим проблему к исходной - клиент (миддл) или сервер (БД). Исходя из каких аргументов можно сделать окончательный выбор:
1) является ли сортировка частью контракта, т.е API? Нужна ли она другим потребителям? Если является - то в БД
2) нужна ли пагинация? Если да - то сортируем в БД, т.к. только там есть полный набор данных
3) сложность условия сортировки. Можно ли его реализовать в SQL?
4) наличие индекса в БД и возможность его добавить. С индексом по полю сортировки в БД она в большинстве случае будет быстрее, чем на клиенте
5) число клиентов, обращающихся к БД. Если БД перегружена, а масштабирование невозможно, то имеет смысл вынести сортировку выше по цепочке. Еще важен тип БД, RDBMS можно масштабировать только горизонтально, 64 ядерные сервера дороги, а 128 ядерных не бывает)
6) может ли клиент менять способ сортировки при работе с данными? Если да, то стоит рассмотреть реализацию всех сортировок на клиенте
7) трудоемкость кодирования. В большинстве случаев тут "выигрывает" БД, т.к. ORDER BY декларативен и проще любых алгоритмов на Java. #arch
Всем доброго вечера. Возвращаясь к теме k8s - может возникнуть вопрос: как его можно запустить на машине разработчика? Покрутить, поиграться с конфигами. Все-таки кластер, nodes, БД для хранения конфигурации, контроллеры, ingress proxy, администратор в придачу - выглядит страшно) Но есть альтернативы.
1) minikube. Упомянут в официальной документации k8s - https://kubernetes.io/docs/tutorials/hello-minikube/ Давно развивается. Поддерживает Win, Mac, Lin. Позволяет запустить k8s внутри VM, Docker образа или даже на голой операционке через механизм драйверов https://minikube.sigs.k8s.io/docs/drivers/ Последние два варианта запуска, увы, доступны только на Linux. Скорость запуска и требования к ресурсам при этом меняются от медленно и много к быстро и мало) Позволяет выбрать Docker или Podman. Позволяет подключать частные Docker репозитории или создать локальный. По умолчанию, как впрочем и все его соперники, стартует в режиме одной node, на которой расположены как служебные модули k8s, так и ваш код. Но можно и добавить node, и создать несколько кластеров. Есть web dashboard. kubectl - утилиту по управлению кластером через CLI API и VM гипервизор нужно ставить отдельно, но есть хорошая инструкция.
Подробнее https://minikube.sigs.k8s.io/docs/start/
2) kind. Работает поверх Docker Desktop, т.е. внутри Docker контейнера. Поддерживает Win, Mac, Lin. В случае Win есть возможность запуска без VM через WSL - https://docs.docker.com/desktop/windows/wsl/. Можно выбрать Docker образ с разными версиями k8s. Запускается достаточно быстро, хорошо совместим с CI системами. Позволяет настраивать частные и локальный Docker Registry, мультикластерную систему. kubectl и Docker Desktop нужно ставить отдельно.
https://kind.sigs.k8s.io/docs/user/quick-start/
3) Docker Desktop. Как ни странно включает в себя урезанный вариант k8s. Включается в админке Docker Desktop. Не кастомизируется. Все в одном - включает kubectl, не нужно ставить отдельно гипервизор. Хорош для начального знакомства.
https://docs.docker.com/desktop/kubernetes/
4) k3s. Интересно происхождение названия - если в слове k8s\kubernetes 10 символов, то k3s - 5, т.к. по задумке он должен потреблять в 2 раза меньше ресурсов. Запускается внутри Docker контейнера. Заточен под запуск на слабых машинах, в том числе ARM. Состоит из одного бинарника, из которого выкинуто все лишнее. Заточен под Lin, но вроде бы появилась поддержка Win. Но не Mac. Самый быстрый и легковесный. Хорошо совместим с CI системами. Позволяет подключать частные репозитории Docker образов. Для хранения конфигурации кластера по умолчанию использует SQLite, но через адаптер позволяет подключить внешнюю СУБД. Подчеркивает, что по умолчанию безопасно настроен. Позиционируется как production ready. Может запускаться как в режиме одной ноды, так и с несколькими, а также в режиме мультикластера.
https://k3s.io/ #cloud #ci
Всем привет!
На данный момент для сборки Java/Kotlin проектов используются два основных инструмента: Maven и Gradle. Можно вспомнить Ant, сказать: «Покойся с миром» - и забыть) Их можно сравнивать по разным параметрам, но сегодня я хочу остановиться на инкрементальной сборке. Может возникнуть вопрос - что тут сравнивать, в Maven ее нет, в Gradle - есть. Но не все так очевидно) На самом деле в maven-compile-plugin такая опция заявлена https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html и даже включена по умолчанию. Но фактически не работает( Почему?
Для начала стоит разделить понятия сборки в целом и компиляции как одну из частей сборки. В Maven жизненный цикл сборки - это линейная последовательность фаз сборки, запуск каждой из которых не зависит от результата предыдущих фаз. В Gradle цикл - граф из task, а у каждой таски есть inputs и outputs, являющиеся файлами на диске. Т.к результат сборки в конечном итоге - файлы. Выходы одной таски являются входами другой. Если входы не изменились - таска пропускается. Причём проверка идёт по контрольной сумме, не по дате изменения. Т.об. если не менялся боевой код и код его теста, то тест не будет запущен повторно. В Maven этого нет в принципе.
По-разному работает и собственно компиляция. Если maven видит изменения в любом классе модуля или в модуле, от которого зависит текущий - идёт полная перекомпиляция модуля. И далее по цепочке( Gradle же во-первых умеет отслеживать зависимости по классам, а во-вторых проверять изменился ли интерфейс, application binary interface, ABI если быть точным, или только реализация, и в зависимости от этого перекомпилировать только нужные файлы. Кроме того в Gradle с появлением Java Library plugin появилась возможность разделять compile зависимости на api и implementation. Любые изменения в implementation зависимости не приводят к повторной компиляции использующих ее модулей.
Вывод: ребята из Gradle сильно заморочились скоростью сборки. Результаты можно увидеть тут: https://blog.gradle.org/incremental-compiler-avoidance. А ведь ещё есть Gradle Cache, от котором расскажу отдельно.
P.S. для Maven есть сторонний плагин, заменяющий 5 стандартных и предоставляющий настоящую инкрементальную сборку http://takari.io/book/40-lifecycle.html Честно скажу, не тестировал, смущает, что решение внешнее #buildtool #java #ci
Всем привет!

Хочу рассказать собрать в одном посте несколько мало и среднеизвестных фичей Maven.
Поехали!
Для начала немного теории. Жизненный цикл сборки = фиксированная последовательность фаз сборки. К фазе сборки можно подключить плагин(ы), а точнее goal плагина. goal плагина - это некое действие, которое реализовано в плагине. Например у плагина compile есть goal compile. БольшАя часть goal стандартных плагинов уже подключена к нужным фазам сборки, но есть возможность переконфигурации в pom файле. https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
Общего механизма исключения той или иной фазы нет, исключение должен реализовать конкретный плагин.
1) Пропуск тестов и их компиляции: mvn install -Dmaven.test.skip=true. В отличие от mvn install -DskipTests также пропускает компиляцию тестов. Может быть полезно, если тесты вдруг сломались, и надо быстро сделать сборку. https://maven.apache.org/surefire/maven-surefire-plugin/examples/skipping-tests.html На всякий случай уточню - сломавшиеся тесты нужно чинить, откладывать исправление в долгий ящик - не нужно!)
2) Пропуск компиляции. Придумать случай, когда это может быть полезно, сложно. Если нужно выполнить какой-то один шаг сборки, без компиляции, то для этого есть более простой вариант, см. ниже. Но знать о возможности будет полезно: mvn package -Dmaven.main.skip
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#skipMain
3) Запуск отдельной goal. Наряду с запуском цикла сборки можно запустить отдельную фазу. Например: mvn clean package sonar:sonar вначале выполняет два цикла сборки clean и package, а потом одну goal - sonar:sonar. В качестве альтернативы goal можно привязать к конкретной фазы. Кстати, порядок выполнения совпадает с порядком в коммандной строке.
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
4) Запуск падающего теста несколько раз, полезно в момент отладки, если тест "мигающий": mvn -Dsurefire.rerunFailingTestsCount=2 test
https://maven.apache.org/surefire/maven-surefire-plugin/examples/rerun-failing-tests.html
5) Среди стандартных плагинов Maven есть с виду неприметный help плагин. https://maven.apache.org/plugins/maven-help-plugin/
Среди его goal есть такие: mvn help:system - выдает информацию о системе: версию и производителя JDK, локаль, таймзоны, переменные среды.
mvn help:all-profiles - список активных профилей проекта.
На сегодня все. To be continued... #buildtool
Всем привет!

Про фишки Maven, часть вторая.
Поехали.
1) У maven, также как у Gradle, есть wrapper. Что это такое? Это по сути встроенный в проект maven. А точнее в проекте появляется .mvn\wrapper\maven-wrapper.jar, а также скачивается дистрибутив maven в папку локального репозитория .m2. Позволяет не зависеть от наличия и версии maven в системе.
Установка: mvn wrapper:wrapper
Использование: ./mvnw clean package вместо mvn clean package
Добавленные при установке wrapper в проект файлы нужно закоммитить в git.
https://maven.apache.org/wrapper/
2) Maven daemon. Тоже перетекание хороших идей из Gradle. Как следует из названия это Maven, который запускается в фоновом режиме. Что позволяет убрать фазу инициализации maven при втором и последующих запусках и существенно ускорить сборку на маленьких проектах. К сожалению, это сторонний проект, поэтому не совместим с maven wrapper.
https://github.com/apache/maven-mvnd
3) Maven умеет запускать сборку в несколько потоков. Рекомендуется сделать число потоков равное числу ядер процессора:
mvn -T 1C clean package
Но можно указать и конкретное число потоков:
mvn -T 4 clean package
И даже число потоков на ядро:
mvn -T 1.5C clean package
Список плагинов, которые могут работать параллельно и другую информацию можно найти здесь:
https://cwiki.apache.org/confluence/display/MAVEN/Parallel+builds+in+Maven+3
Также эта настройка есть в IntelliJ IDEA.
Про настройки параллельного запуска для JUnit тестов можно почитать тут: https://www.baeldung.com/maven-junit-parallel-tests
4) Плагин Maven Enforсer. Позволяет при сборке на фазе validate проверять:
а) версию и вендора JDK
б) версию maven
в) отсутствие SNAPSHOT версий
г) наличие и отсутствие в проекте определенных файлов и их контрольную сумму
д) отсутствие repositories в pom файлах (они должны быть в settings.xml)
е) ОС, на которой запущен maven
и многое другое.
https://maven.apache.org/enforcer/enforcer-rules/index.html
5) BuildPlan плагин. Показывает какие плагины используются при сборке, какие goal связаны с каждой фазой. Запускать нужно с теми же ключами -D и -P,
с которыми проводится сборка:
mvn buildplan:list
http://buildplan.jcgay.fr/
#buildtool