Долго ли, коротко ли, служил я java-разработчиком, да судьба-злодейка, крутанула меня в Platform Engineer’ы. Овладел я ремеслом devops’ским да сисадминским, но по ночам снится мне low-level Java, но быль моя совсем чуть-чуть об этом будет — поглаголю о JVM опциях, дабы приложение в k8s без дури запускать. Расскажу, как доблестно (а может, и безрассудно) SeaweedFS S3 storage выбирал, как кластер k8s поднимал, не щадя живота своего. Читай сии записки, запивая иван-чаем или медовухой: авось, умная мысль глянет меж строк. Не глянет — так хоть посмеёшься над моим devops экспириенсом.
Сказ первый: как java-дев k8s-град каменный возводил (и что из этого вышло)
Из заморских далей вернувшись, пришлось доблестному ex-java-деву кластер k8sный возводить. Да не managed-облачный, подачку чужую, а свой — рукотворный, кровью и потом выкованный.
Но не робкого десятка наш молодец — не раз уже kops’ом кластера на облаках заморских разворачивал. Стоит теперь на распутье, как богатырь у камня былинного, и три пути перед ним яснеют:
-
Kubeadm
— легковесная приблуда от самогоk8s
, поднимает тот самый ванильныйk8s
кластер, да его аддоны. Помним, что в таком виде —k8s
кластер, является кластером в вакууме, требующим всевозможной “донастройки” (от сontainer runtime to cni). Отсюда вытекает необходимость в большом количестве ansible playbook. -
Kubespray
— по сути упомянутый kubeadm + содержит в себе все ansible плейбуки необходимые для установки и обновления (предустановка и обновление всех компонентов — runc, containerd и т.д), плейбуки для любых cni. Часть компонентов опциональна и конфигурируема в конфигурационном файле. В этом разнообразии ansible playbook и сила, и слабостьkubespay
— инсталляции большого количества лишних утилит/компонентов и т.д, отчего время разворачивания/инициализации новой ноды критично увеличивается. Также, как и у всех фактических компоновщиков ansible-playbook дляk8s
кластера (сравниваю с небезызвестным kops), имеется проблема с поддержкой новых версии того или иного тулинга или его конфигурации. Например, чтобы включить фичу cilliumgateway — пришлось хардкодить в конфигмапе (сорцах)kubespray
, такая же проблема/решение нашлись для bgp анонсов vip-адресов кластера. -
Rancher
— не ansible-like подход к инсталляции. Предоставляет не только спектр разнообразных возможностей дляk8s
кластера, а целую экосистему — там же и веб-интрефейс, и block storage — сeph. Те же проблемы с небольшим отставанием по наличию тех или иных фич. Не очень подходит для бюджетных инсталляций.
Сердце витязя чует, что грядёт время, когда быстрота присоединения нод станет мерилом силы кластерной — ибо autoscaling, словно вихрь весенний, потребует, дабы новые воины — виртуалки восставали из цифрового небытия в мгновение ока. Для сего замыслил devops-молодец cloudinit-заклятье
— дабы едва родившись, виртуалки сами kubeadm’ом
крестились, плейбуками доспехи надевали и в строй безропотно вставали. Ныне же путь Kubespray’ем держится, ибо стезя сия хоть и долгая, да проверенная.
Избрав kubespray
пришлось повоевать devops молодцу с плейбуками, нужды-то были — свежие фичи Ciliumовые во град Kubernetes’ский ввести: и BGP-announcement
, и CiliumGateway
, а хардкодить в сорцах не гоже. Решил молодец со иглы готовых плейбуков соскочить — взял Helm
, да как сокол на добычу пикирует, уставил Cilium
свежий, ядрёный, да завелся кластер могучий.
Однако не безмятежно выдался путь представления кластера миру — сети корпоративные, что драконы лютые, козни строили. По замыслу чистому, Cilium BGP Control Plane
должен был верой и правдой служить, да вот беда — роутеры внешние, как стены каменные, BGP не ведали (хотя в планах великих их к разуму привести). Пришлось горемычному девопсу на каждую ноду Bird'а
мудрого посадить — принимает тот BGP-весты от Cilium, да перешептывает их в сеть корпоративную через RIP
, словно гонец усталый.
Много еще подвигов предстоит витязю нашему: то динамический провижининг нод k8s
на виртуалках организовать, то роутеры корпоративные к BGP приобщить, то да се…. много чего еще в свитке задач нераскрытом значится!
Сказ второй: как Java-витязь S3-кладовую выбирал да обустраивал
Minio — как старый друг: и кривоват, но знаком. SeaweedFS — как русалка: манит, да кто знает, что на глубине?
Явились как-то к нашему молодцу собратья из соседнего подцарства IT-шного. «Дай нам, — молят, — сторадж вороватый: чтоб и быстрый был, и копеечный, а сколько добра в него складывать будем — и сами не ведаем. Может, архивные дани скопим, может, свежие потоки данных пустим».
Почесал молодец голову русистую, да и отринул Ceph
сходу: не по силам витязю-одиночке сего чудища многокомпонентного содержать — и ресурсов не хватит, и подмоги нет.
Задумался молодец меж двух стезей: Minio
, кое хоть и с документацией кривобокой, да знакомое, и SeaweedFS
неведомый, где лишь README на GitHub’е, словно свеча во тьме, светит. Но приглянулся SeaweedFS
манерой хитрою — файлы воедино сливать, да ноды добавлять без лишней кручины и ребалансировочных мук. Так и решили.
Пока летит, как сокол ясный. Коли ж баги нежданные нагрянут — пути три предстанут: либо на Ceph
с пулами разными для hot да cold данных перейти, либо Minio
кластерами раскидать, а то и вовсе на S3 облачный сдаться… Но то — сказ уже иной.
Сказ третий: как Java-витязь JVM-опции в k8s доспехи обряжал
Disclaimer. Актуально для версии
jdk >= 8u131
Узрел наш молодец шаблоны для запуска JVM-сервисов — и взгрустнулось ему: все как под копирку писано, да к тому же явно не времен JDK 11. «Не дело это!» — молвил он, засучив рукава цифровые, и принялся за перепись устаревших скрижалей.
Разные memory limits/requests — коль любишь unpredictable утечки memory исследовать

Предположил наш молодец, что разработчики голову чесали, когда OOMKiller
поды Java’шные
косил, а память из heap’а
не утекала (не ведали холопы про non heap и специфику OOMKillera
). И ведь правда — нашёл витязь в трекере пару инцидентов плавающих, уравнил все, и дальше править пошел.
Limits cpu — что ж экономим, батюшка?

Что ж, ты брат, девелопер, экономишь, троттлинга али боишься иль думаешь, что потоки к ядрам гвоздями окаянными прибиваешь? Так, чтоб с троттлингом бороться реквестов достаточно, ну а планировщики, что ядер, что потоков по-другому работают. А коль cpu на ноде хватать не будет, так под твой все равно заэвикчен не будет. Ведомо, что в большинстве случаев, Java-матушке cpu больше всего на старте надобно.
В 99% случаев не нужны тебе cpu лимиты в проде, может для тестов нагрузочных актуально будет в окружении каком-то.
За хардкод в аргументах нечаянный — бьют отчаянно

О Xmx
речь отдельная будет, но вот окаянный хардкод — зачем? Ведь коли в контроллере память увеличишь/уменьшишь, сии цифры в Xmx
уже не сменишь (разве что контроллер удалять да заново разворачивать). Пусть хоть так (хотя, вестимо, забудешь поменять при смене ресурсов — но об том в следующем абзаце). Зато когда гореть начнёт — хоть потушить быстро сможешь!
command: ["java"] args: ["-jar", "app.jar" , $(JVM_ARGS)] - name: JVM_ARGS value:”-Xmx2128"
На heap и gc надейся, но и сам не плошай
Что только не узрел девопс-молодец в тех пропертях: и Xmx
одинокий без пары Xmn
, и Xmx
с Xmn
разногласные, и вовсе пустоту — словно поле без ограды, где хип-память вольно гулять растекается. И молвил девопс молодец истины:
-
Heap ограничивать разработчик должен. Ведь Java работает не только с хипом, а всяко разное и в non-heap хранит.
-
Xmn
cXmx
одинаковые ставь — зачем ресурсы тратить на resize? -
Ну а мы же в k8s царстве живем, так вообще не надо тебе
Xmn
cXmx
. ИспользуйInitialRAMPercentage
иMaxRAMPercentage
, так хоть когда значение памяти меняешь, в одном месте достаточно контроллер редактировать. Коль heap у тебя маленький где-то будет(меньше 200Мб), не забудь поставитьMinRAMPercentage
. Значения эти никак не 100% должны быть, чем меньше memory даешь — тем больше процентов на non-heap оставлять надо. (Худо-бедно универсальное значение 70%, но каждый случай уникальный)
- name: JVM_ARGS value: "-XX:InitialRAMPercentage=60.0 -XX:MinRAMPercentage=50.0 -XX:MaxRAMPercentage=60.0" /* MinRAMPercentage устанавливает максимальный heap size для малого количества memory. Нейминг не самая сильная сторона Java */
-
А коли heap у тебя небольшой, не чурайся
AlwaysPreTouch
опцию попробовать, которая физически выделяет все страницы памяти, отведенные под heap
Старовером быть негоже витязю молодому!
С заменой Xms
и Xmn/Xmx
на InitialRAMPercentage
и MinRAMPercentage/MaxRAMPercentage
уже порешали. Но помнить хорошо, что Java на месте не стоит, и коль версию Java обновили, загляни в release notes, да попробуй (с прода не начинай) фичи свежевпиленные, ибо пользу принести они могут хорошую. Например, UseStringDeduplication
доступный с Java >=8u20 (но имеет смысл только для больших Heap, так как работает с G1 GC) может impact помочь тебе сыскать.
GC — поле битвы для отважного Java-витязя
GC тропы оказались нехоженными в этом царстве-государстве. Поэтому разгулялся наш devops молодец с экспериментами над GC, но помнил:
-
G1
хорош для больших heap . Коль JDK > 8 у тебя и heap скромный (точно <=2GB, а дальше смотреть надо), попробуй переключи GC -
А если heap большой и JDK поновее (>=15) не чурайся
ZGC
(но вернее с JDK >=21, когда ZGC лик дженеративный обрел) -
У каждого
GC
куча опций, разбирайся, экспериментируй — и получай профит на метриках
Коль heap большой, то попробуй включить THP
Тропа сия избита, да не всяк разработчик памятует: когда память великими объёмами оперируешь, можно попытаться аллоцировать её крупными страницами — а именно, THP
(Transparent Huge Pages) включить. Да вот незадача, часто выключена опция эта на уровне ядра, поэтому для экспериментов не достаточно Java опций, базовый image с JDK правильный собрать надобно. Однако редко такое встречается в ныне разрабатываемых приложениях корпоративных, пожалуй только в монолитах древних. Коли разуму не достает, в скрижали вечные загляни.
Негоже про non-heap забывать.
Память то коварная — течь может не только в heap. Смотри и non heap опции тоже. Для оптимизаций тоже полюшко не пахано — тот же codecache.
Далеко не все описал я, ибо точно уже не эксперт, но что сказать хотел, коль пишешь на Java, то не зазорно одним глазком на JVM проперти посматривать, да оптимизировать пытаться. Да и на девопса надейся, но и сам не плошай — проверяй чарты и темплейты сервисов своих, дабы онколл звонким не был.
Чтец благодарный! Спасибо, что прошёл сей путь до конца. Коли бредятину в тексте узришь — милости просим в комменты: поспорим, посмеёмся. А ежели ошибку найдёшь или мудрую мысль подкинешь — низко кланяюсь в пояс. До новых сказов!
ссылка на оригинал статьи https://habr.com/ru/articles/897992/
Добавить комментарий