Русский
Русский
English
Статистика
Реклама

K8s

Не 1000 и 1 ночь, но 1 год и 10 дней в Слёрме

14.11.2020 06:22:30 | Автор: admin

Начнём с того, что к устойчивым предостерегающим выражениям, когда стоит отбежать в сторону, пригнуться и закрыть уши, или просто сигануть в траншею, теперь относятся не только бойтесь данайцев, дары приносящих, ты куда гранату бросил? и вспышка слева, но и:


  • Мы бирюзовая компания
  • Мы никого не увольняем и у нас нет текучки
  • У нас компания только с белой зарплатой, все работают удалённо
  • Мы не назначаем человеку должность, он сам врастает в обязанности
  • У нас с этой недели Agile

Я до сих пор не решил, повезло мне или нет оказаться в Слёрме. С одной стороны, на игры в оловянные солдатики потрачен целый год, с другой стороны такого опыта, право слово, за десять лет в классической красной корпорации не получишь.


N.B. Для хранителей "Хабра". Да, "Хабр" не жалобная книга. И это правильно. Но в данном тексте я рассказываю о работе в течение 1 года в компании, которае ныне находится в ТОП-10 (моими усилиями тоже). Думаю, что отправлять в черновики не стоит. Пусть "хабровчане" решат полезен им такой опыт или нет. А в некоторой степени он был уникален. Лично я знаю, что я своим знакомым и друзьям никогда не посоветую на Слёрма, ни Southbridge. Ниже вы прочитаете почему. Даже с документами.

Началось всё относительно спокойно. Я прогуливался с женой по Коломенскому парку и мне позвонил мой старый друг Нияз Абдуллин и говорит: Слушай, хочешь попробовать себя IT-редактором. Ты уже уже пять лет журналистикой и публицистикой занимаешься. А давай!, ответил я.


И понеслось.



Москва, гостиница Ленинград, примерно трети из этих улыбчивых людей в компании уже нет. Кто вовремя и вежливо "свинтил", вроде Павла Селиванова, кого "свинтили".


Первая встреча и первые обещания


3 сентября в Питере в конференц-зале Selectel Слёрм проводил мероприятие, а так же ещё проходил День техдира Димы Симонова.


Генеральный директор Игорь Олемской пригласил меня в переговорку. И первым вопросом перед здрасте было: Ты, наверное, запойный, да?. Классический, как потом выяснилось, его подход к общению с сотрудниками.


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


То ещё было приключение: и медведица, которая бродила рядом с поисковыми отрядами, и соответственно сотрудники Росгвардии с грачами, хотя хотя у меня очень большие сомнения, что 9х19 Para сможет остановить взрослого медведя. Потом мы ещё нашли следы медвежонка и поняли, что медведица нас просто уводит от места лежбища, но если мы туда случайно нагрянем, порвёт на множество японских флагов и пистолеты Ярыгина Грачи никак не помогут. Тут нужен 7,62х54 минимум.


Поиск: https://drive.google.com/file/d/13nzmjhgqzthYRqN-pHM0YwFx0d2FY0ON/view?usp=sharing
Поиск: https://drive.google.com/file/d/13vVFhbCT6kJ7krrKz0BN2oLsVATnDF5W/view?usp=sharing
Поиск: https://drive.google.com/file/d/14-BFJf_0b0a0t8tVTYR9tKIKMHJZhC-g/view?usp=sharing
Девочку нашли на 4 сутки: https://drive.google.com/file/d/14mwdzsnt9Vru07z66vd8QcjaTtYh7K68/view?usp=sharing


Девочка, слава богу, нашлась, правда не спали мы четверо суток. Но на мероприятия я успел. Был свеж, бодр. Вот только не готов к вопросу: запойный я алкоголик или нет. В ответ просилась фраза: А не удак ли ты, человече?. Но так как у меня хорошее воспитании и врождённое любопытство, я решил посмотреть, чтобы будет далее.


А далее руководство Слёрма рассказало, что в компании работает 600 человек (потом выяснилось, что около 50, может я ослышался, не знаю), что Слёрм одна из немногих в России бюрюзовых компаний, что всё у них по-белому от и до, что их задача выстраивать IT-комьюнити в России, что в компании никого и никогда не увольняют и нет текучки. И если человек не встраивается в одну из команд, ему подбирают место в другой. Что не человек получает должность, а набирает на себя компетенции и постепенно должность обрастает вокруг него.


Райское место, тысяча чертей, подумал я. А говорил мне папа ещё в 5 лет не верить в сказки.


Нияза уволили через два месяца. Это я к тому, что мы никого не увольняем, у нас нет текучки. Так как мы с Ниязом знакомы уже 16 лет, то естественно я с ним связался и спросил Why?. Ну, просто, вот так, сказали и всё, ответил жизнерадостно Нияз. Он вообще не умеет расстраиваться светлый человечище.


N.B. Рекомендую Нияза Абдуллина, как (говорю, не стесняясь) великолепного художественного и технического переводчика. Мы с начинали вместе в фантастике ещё в 2004 году. Только я свернул на художественные тексты и публицистику, а Нияз на переводику, где достиг поистине впечатляющих профессиональных высот.

Это я уже потом понял, что мотивация решений в Слёрме не подчиняется законам логики и диалектики. Просто так самый частый ответ. Например, так решил Игорь Олемской, генеральный директор. Или коммерческий директор Антон Скобин сообщает, что не может с этим человеком работать, так как они психологически несовместимы. Бывало и такое.


Главные актёры труппы образовательно-развлекательного центра Слёрм


В принципе Игорь olemskoi и Антон aSkobin это центральные персонажи повествования, потому, если вы не против, я уделю каждому их них несколько строк.


Игорь olemskoi, генеральный директор человек обладающий напором, уверенностью, неплохими интеллектуальными задатками, отсутствием морали и нарциссизмом. Получает удовольствием продавливать людей чаще всего сотрудников, так безопаснее. То есть вперившись взором задавать порой странные вопросы, выводить из себя, провоцировать. В случае возможности девушек доводить до слёз и откровенной истерики. Ему достаточно почувствовать хоть раз слабое место человека, и он это будет использовать постоянно хоть на daily, хоть на demo, хоть на retro. Он считает это сильными качествами руководителя обесценивать работу, чтобы никто даже не думал попросить повышение зарплаты (aSkobin некогда мне в красках рассказывал, как Игорь матами объяснял Владимиру Гурьянову, опытному и отличному DevOps-инженеру, что он никто и не имеет даже права требовать повышения заработной платы), минут пять поизбивать морально за ошибку, а потом сказать но это не важно, главное, мы получили ценный опыт и так далее. Я к этому относился нормально, потому что ещё при первой встрече отметил нарциссические черты его личности. А потом только многократно убеждался, что он классический нарцисс и его реакции банальные и скучные в рамках этой патологии психики. Нарциссы сами по себе скучны у них очень скудна вариативность поведения. В целом, он меня так и не удивил не вышел за рамки.


Антон aSkobin, коммерческий директор вторая его половинка. Именно потому руководство Слёрм настолько целостно. Антон пограничник. Он открыто называет себя consigliere Игоря. Если кто не помнит, этот термин вошёл в литературную среду из Крёстного отца. У Антона пограничное психическое расстройство и потому он полностью зависим от Игоря. Достаточно одной уничижительной фразы Игоря, чтобы Антон впал в молчаливую меланхолию. И опять же достаточно похвалы, чтобы Антона выбросило в деятельную фазу мании с кучей идей, бешеной работоспособностью, работой по ночам, беспрецедентной верностью. Думаю, такой верности не было даже у доктора Гёббельса по отношению к Фюреру а любил он его беззаветно.


У них сложилась невероятно крепкая, хотя и немного банальная по своей сути пара нарцисс-пограничник


Я наравне с другими
Хочу тебе служить.
От ревности сухими
Губами ворожить.
Я больше не ревную.
Но я тебя хочу.
И сам себя несу я,
Как жертва палачу.

Осип Мандельштам


Сэм Вакнин, автор книги Злокачественная самовлюбленность: Нарциссизм пересмотренный (2001), считает, что идеал нарцисса обожающий, покорный и самопорочащий партнёр. Кто работал или работает в Слёрме, думаю, сразу увидит этот образ и назовут имя.


Совсем недавно, когда обсуждалось, что зарплатного фонда хватит на 2-3 месяца и ещё 1 миллион на овердрафте в банке и рассматривались варианты слияния с крупной компанией или присоединение крупного инвестора с правом голоса, Антон в рамках своей роли пограничника заявил: Что уйдёт из компании, если будет третий человек принимать решения, а не только двое он и Игорь. Чем только подтвердил мою уверенность в диагнозе взаимоотношений этой пары.


Потом Игорь вопросил: Кто готов отказаться от части зарплаты ради компании? Я готов отказаться ещё от трети зарплаты, сразу же воскликнул Антон. Ещё?, недоумённо переспросил Игорь. Он просто забыл или даже не заметил, что Антон уже отказывался от трети своей зарплаты весной, когда ударил карантин по всем офлайн мероприятиям. И все увидели, как Антон просто поник, сполз в кресле его весеннюю жертву не просто не оценили, её даже не заметили.


Если обратиться к классической теории, то нарцисс может быть счастлив с:


инвертированным нарциссом;
мазохистичной личностью;
пограничником, то есть, человеком с пограничным расстройством личности.


Чаще всего это биполярное аффективное расстройство второго типа с быстрой сменой фаз, что даёт и огромную трудоспособность в период мании, фонтанирование идеями и гипотезами, и полную управляемость в период депрессии.


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


Agile, и никаких гвоздей


В какой-то момент игры в оловянных солдатиков Игорю наскучили и он захотел трансформеров. В том смысле, что бирюзовость была забыта, вышла из лексикона предприятия. Зато прочно вошло словосочетанием Agile-трансформация. Со всеми необходимым артефактами Scrum.


Хорошее по-сути начинание, если бы не превратилось в карго-культ.


Помню первое проявление blameless-культуры. Тогда, в феврале 2020 года формировалась команда маркетинга. Появились новые люди. И Слёрм проводил в Москве в гостинице Севастополь Слёрм Agile. И была там новенькая девушка Юля Остапенко. Она даже сама сшила двух плюшевых слёрмиков, так хотела стать частью проекта и так старался влиться в него.


Вечером после мероприятия вся команда собралась в ресторане "Крым" на втором этаже. Я расположился в уголочке, чтобы спокойно за всеми наблюдать и релаксировать. А Юля совершила три ошибки:


  • Села напротив Антона
  • Включилась в разговор, который вёл Антон
  • В какой-то момент возразила ему и решила отстоять своё мнение

Дальнейшее можно было охарактеризовать, как попала белка в мясорубку. Вначале с ней спорил Антон, а когда стал проигрывать по аргументам, включился Игорь. Ну, вы же помните, Слёрм бюрюзовая компания, каждый специалист равен другому, каждый может высказать своё мнение. Плюс мы внедряем Agile и его фреймворк Scrum c его blameless-культурой.


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


В какой-то момент я не выдержал: Эй, коллеги, может хватит?. За девушку заступился Александр Зосим, которого тоже уволили через пару месяцев. Остальные молчали ошарашенные, что воообще сейчас это происходит. А происходила банальная вещь девочка зацепила главную ценность нарцисса, а именно его пограничника. Психопатологического любовника. И потом Игорь морально избивал её до тех пор, пока она не разрыдалась и не убежала. А когда мы поднимались обратно на 4 этаж похвастался перед несколькими людьми в моём присутствии: Как я её, а?. В тот же день Юлю уволили. Антон сказал, что он психологически не может с ней работать.


В Agile важна ещё честность, открытость, прозрачность. Постепенно в процессах выбыло и первое, и второе, и третье.


Зачем-то, хотя в компании это не принято, Игорь и маркетингу, а затем технической поддержке, указывая на меня, в тот же февральский вечер рассказал без моего согласия, сколько я получаю: Вот, смотрите, берите пример. Человек зарабатывает 200 000. И вы можете. Главное стараться. Как выяснилось всего дней через 10, он прекрасно понимал, зачем это делает.


Как 4 раза за год изменить условия оплаты и чувствовать при этом свою правоту


Да, для меня это, честно, было впервые, когда обещания ничего не значат, а главенствует принцип сегодня барин дал, завтра барин взял. А если не нравится, можешь увольняться.


Изначально в сентябре у меня с Игорем сложилась договорённость, что я получаю за статью 10 000 рублей + 1 рубль за каждый просмотр. Правда, они малость офигели от результатов, всё же у меня был пятилетний опыт публициста и даже военного журналиста. Создать в комментариях дискуссию, подобрать заголовок и 50 000 70 000 просмотров обеспечены.


Зато я смог поднять Слёрм в рейтинге Хабра где-то с 40-го места на 3-е. Выше уже никак. Выше уже Selectel и mail.ru, у которых полноценные редакторские отделы с парой десятков людей.


За что я благодарен Слёрму, так за то, что столь высокая зарплата в первые месяцы дала мне возможность не вылечить, но сильно поправить здоровье моей мамы. У неё ревматоидный артрит это невероятные боли и порой даже невозможность встать с постели. Всё, что я зарабатывал в Слёрме уходило на оплату квартиры в Царицыно и на лечение мамы в Институте ревматологии им. Насоновой. Спасибо и низкий поклон московским врачам, они многое сделали и поставили маму на ноги. Но денег уходило просто невероятное количество, слишком запущенная была болезнь и слишком разрушено здравоохранение на Украине.


А в январе случился первый красный разговор с Игорем. У нас же бирюзовая компания, вы помните? Но Игорь считал, что иногда имеет право поговорить по-красном. С художественным, очень жалко звучащим матом и прочим. Мне объясняли, что моё подразделение и я, как руководитель, слишком много кушаем. Не поднимались вопросы, что мы даём компании, сколько от нас приходит заявок, формирование облика компании, благодарственные письма читателей (были и такие), что такое долгосрочный эффект, лояльность клиентов, сторителлинг, репутация. Просто много кушаем.


N.B. Вы не поверите, но мама одной девушки прочитала мою статью на Хабре о депрессии, дала почитать ей. И она связалась со мной через нашего контент-менеджера. Спасибо Жене. И теперь я веду эту девушку в Германии, поддерживаю её в очень тяжёлом депрессивном эпизоде. Я помог ей подобрать хорошего врача, вместе с врачом мы выбрали верный SNRIs антидепрессант и стратегию лечения. Девушке всё ещё тяжело, но она уже оценивает своё состояние на 70%. И у неё нет суицидальных мыслей. Я могу сказать, что уже в этом в этом от Слёрма получилась хоть какая-то польза. Благодаря публикациям я спас (совсем без шуток и бахвальства, кто испытывал депрессию, тот знает) спас человека от гибели. И продолжаю поддерживать в лечении сейчас. Каждый день. Каждое утро.

Я слушал Игоря примерно 5 минут. После этого очень мягко и корректно, только литературными словами объяснил: Игорь, если мы решили поговорить по-красному, то теперь мой черёд. Говорить со мной в таком тоне и даже с такими словами может крайне ограниченный круг лиц. Во-первых, это очень близкие друзья и я к ним прислушаюсь. Я тоже могу ошибаться, как любой человек. Во-вторых, это люди, которых я очень уважаю. Это три человека. Оператор Генштаба полковник ВС в запасе, полковник ГРУ в запасе и подполковник Вымпела в запасе за их невероятную тяжёлую и долгую службы стране, за военный опыт и за жизненный опыт. Кроме того, я готов выслушать по-красному моих однополчан, когда я работал военным журналистом. Всё. А тебе, Игорь, я права говорить со мной по-красному не давал. Никогда. Ни разу. И до этого права ты никогда не дорастёшь. Весь страшномосковский твой риск, который у тебя в жизни был и будет, это подвернуть ногу, вылезая с кожаного кресла внедорожника. И когда ты со мной пытаешься говорить по-красному, то вызываешь исключительно молчаливое веселье и каменное выражение морды лица, чтобы не заржать в голос.


Больше со мной по-красному говорить не пытались. Говорили с другими и не раз. Но это уже другие истории, принадлежащие другим людям.


В итоге Игорь сказал, что правила оплаты для меня меняются. Я больше не получаю 10 000 рублей за каждую статью и 1 рубль за комментарий. Теперь я просто получаю 20 000 за статью. Плюс мне были запрещены любые побочные темы, кроме технических. Никаких статей в хаб "Здоровье гика", никаких историй от военных о других странах, которые "хабровчане" просто нигде больше бы не прочитали чтобы услышать эти истории, чтобы добиться согласия на публикацию, нужен очень высокий уровень доверия или ряд военных операций бок о бок. Я сразу сказал, что рейтинг обвалится так и случилось. Но когда это нарциссы кого-то слушали, кроме себя?


Это был первый раз, когда без предупреждения за месяц изменились правила игры.


Я долечил маму, хотя и влез в кредитки потому что даже зарплаты в 200 000 не хватило. Завершался февраль. Тут я напомню, как Игорь без моего согласия всем рассказал, сколько я получаю. Тогда я не понял зачем.


Через 10 дней я узнал ответ. 4 марта, как говорится в 4 часа утра, без объявления войны, а так-то в 10 утра мне по Zoom вызвонили Игорь с Антоном и сказали простую вещь: Мы решили, что ты слишком много зарабатываешь. Скажу честно, я слышал и в свой адрес, и в адрес других людей разные поводы и увольнений, и снижение зарплат, и отмены бонусов. Из-за тебя сорвалась сделка, Из-за тебя ушёл клиент, Ты не выполняешь KPI уже полгода, Ты непонятно чем занимался полмесяца. Но такое


Определённо в бриллиантовую книжку цитат: Ты слишком много зарабатываешь.


И мне сообщили, что в компании сейчас есть только одно место, в маркетинге. С окладом 80 000 рублей. Как я сейчас уже понимаю, тогда им очень хотелось, чтобы я их двоих паровозиком направил в одно известное место. Но на мне висели кредиты и долги, которые остались после лечения мамы. И просто так оказаться без работы я физически не мог.


Если бы по-человечески они предупредили меня за месяц, что мой оклад изменится, я бы многое скорректировал, подогнал бюджет, от чего-то бы отказался. А так они изменили мне оклад задним числом и оставили с 80 000 рублей и долгами.


Окей. И не из таких передряг выбирались. Главное, что маме легче. Это был второй раз, когда Слёрм без предупреждения изменил условия оплаты.


В итоге мне такая ситуация надоела, я вызвал Антона на разговор и мы пришли к договорённости, что в очередной раз возвращаю рейтинг Southbridge на Хабре в десятку, держу его не меньше 300, и создаю более 2000 переходов на slurm.io. И за это получаю бонус к окладу. При этом Антон утверждал, что снижение оклада произошло потому, что я стал хуже писать. Эх Ему бы ещё логи научиться подчищать.



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


Скажу честно, непростая задача, потому что хабровчан уже легонько поташнивало от бесконечных статей об Agile и статей купи-купи-купи. Как и меня, скажу честно. Я не для этого пришёл в "Слёрм". Но поделать я уж ничего не мог такие ставились задачи. Разве что по мере сил давал свободу переводчику Паше Демковичу Finnix он очень талантливый DevOps-инженер и хороший переводчик. Пока я был в Слёрме, я прикрывал его, как мог. Да и после увольнения сделал так, чтобы у него появилась возможность сбежать с этой пиратской шхуны, где генеральный с коммерческим дуют шмаль, по вантам бегают офигевшие от разнонаправленных команд матросы, все днища у бочек с ромом выбиты, а попугай охрип от воплей Полундрррра!. Я не бросаю своих.


Ах да, чуть не забыл ещё один эпизод. В конце июля мне не от главного бухгалтера Ирины Шуруповой, а от её помощницы пришла неожиданно Дополнение к договору 2, в котором мой оклад неожиданно стал 22989 рублей, причём это дополнение должен было вступить в силу с 1 марта. И опять-таки задним числом. Во дворе июль, а допсоглашение меняет правила игры с 1 марта.



Я сразу же позвонил коммерческому директору Антону и вопрос: Что есть это, о юный падаван? А так как я давно понял, что слёрмовским шулерам доверять нельзя, то уютно расположил свой Samsung Note 10+ чуть ниже стола, чтобы выглядывал блок камеры, и снял на всякий случай весь видео-разговор по Zoom. На всякий случай, вдруг мне захочется показать, как Антон лично эмоционально жестикулируя, рассказывает:


  • Что это нормально для компании, все подписывают это соглашение на 22 000 рублей;
  • Сделано это, чтобы уйти от налогов это к слову о белой зарплате и белой компании;
  • Сделано на всякий случай, если с человеком приходится расставаться по-плохому, то чтобы заплатить ему меньше на выходе;

У меня почему-то стойкое ощущение, что и налоговая, и трудовая инспекции крайне заинтересуются, что там мне Антон сообщил. Им тоже надо план выполнять.


Насколько я понимаю, если бы я подписал это нарушающее закон соглашение, то со мной по-хорошему бы тогда и расстались. Но и сделал морду ничего непонимающего кота и просто забил на этот договор. Законодательство Российской Федерации предусматривает, что сотрудник не обязан подписывать дискриминирующие его договора, дополнения к ним и прочие документы.


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


План закон, выполнение долг, перевыполнение честь


Вот прям духом 80-х повеяло, как вспомнил. Каждый месяц в Слёрме начинался с плана. Собиралось общее партсобрание маркетинга. Каждый должен был включить в Zoom видео, иначе, по словам Игоря это неуважение к команде.


"У ну-ка, возьмём (условно) 10 000 000?" вопрошал генеральный директор Игорь Олемской с ленинским прищуром в глазах.


Обычно воцарялось молчание. Потому что (условно) 8 миллионов в прошлом месяце наскребли еле-еле и то только с применением китайских пыток по отношению к корпоративным клиентам.


Чтобы беломорина табачной фабрики имени Урицкого казалось слаще и выглядела, как Cohiba Esplendidos, Игорь добавлял: Если возьмём 10 000 000 всему маркетингу премия. 100 000 рублей.


Нет, если вы подумали, что каждому по сто штук, это громадная, чуть ли не классово-социальная ошибка. Эти самые сто тысяч делились на количество человек в маркетинге в разные периоды от 8 до 11. Согласитесь, отличная мотивация для свершения невозможного. Игорь тоже там думал потому в его взгляде сверкала гордость за команду личных оловянных солдатиков.


А так как никому особо не хотелось с спорить с генеральным, а потом неожиданно оказаться "слабым звеном", то все с каменными лицами строителей коммунизма кивали. Как сделать эти (условно) 10 миллионов, когда по всем прикидкам, дай бог, наскребается в лучшем случае семь непонятно.


Но, как в старом советском анекдоте: У нас на партсобрании каждый немножко против, но все вместе категорически за


Что? Не слышу? Погромче товарищи с галёрки Какие-то показатели? Сравнение с предыдущими периодами или предыдущими мероприятиями? Какие-то предиктивные модели? Retention rate? Churn rate?


Это мелко для настоящих комсомольцев-слёрмовцев. Партия сказала Надо, комсомол ответил Есть. Потому что в бирюзовой компании все равны, каждый может высказать мнение, возразить, усомниться в достижении плана. Да вообще всё может. Кроме того, что не может. Все животные равны, но некоторые равнее других.


Гвозди бы делать из этих людей


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


Теперь поговорим о том, что не человек получает должность, а набирает на себя компетенции и постепенно должность обрастает вокруг него.


С некоторых пор Игорь начал повторять, что Иркутск это Клондайк IT-специалистов. И начал массово набирать оттуда сотрудников. Логику его можно было понять, если в центральных регионах специалист стоил, предположим, 80 000, то в Иркутске 40 000. Сплошная выгода. Клондайк.


Оттуда же он выкопал одну девоньку. ЧуПАКабру tsunami_shu, одним словом. Которая мигом начала всех строить и ровнять чисто корпоративными методами, а заодно набирать в команду своих знакомых что в целом логично для захвата власти. Ну, вот есть у этого человека такая слабость. Хочется ему. Вот и проявляется практически во всех действиях.


Через две недели после появления чуПАКабры Игорь сделал её product owner и маркетига, и отдела по производству. Напомню мы не назначаем человеку должность, он сам врастает в обязанности.


Мы новое домоуправление нашего дома, в сдержанной ярости заговорил черный.
Я Швондер, она Вяземская, он товарищ Пеструхин и Шаровкин. И вот мы
Это вас вселили в квартиру Федора Павловича Саблина?
Нас, ответил Швондер.
Боже, пропал калабуховский дом! в отчаянии воскликнул Филипп Филиппович и всплеснул руками.

Не помню с тех пор ни одного спринта, который был бы выполнен на 100%. А ситуация с курсом по Ceph, когда провалили все сроки, решили отмолчаться и просто тихонько менять даты на сайте. А курс по мониторингу и логированию, где ровно то же самое? А


Сейчас "Слёрм" объективно провалил по срокам три курса.


Антон в объяснительной записке перед вкладчиками написал, что они попали в идеальный шторм. Хороший ответ, который ни на что не отвечает. Если это происходит с тремя курсами одновременно это три идеальных шторма или один? Или же кто-то с кадровой политикой промахнулись, как армянский затонированный Шаттл мимо Луны? Или когда-то случайно попали в "голубой океан" Kubrnetes и не поняли, когда он стали "красным"?


Недавно взяли девочку 1995 года рождения в качестве product owner. В понедельник взяли, а в четверг её имя уже начало системно удаляться из всех внутренних телеграм-чатов Слёрма. Это так в Слёрме знаменуются увольнения без фейерверков и шампанского, зато все знают и понимают. Я тогда, помнится, даже прокомментировал: Бедная девочка, даже чихнуть не успела. Скорее всего, оценила этот балаган изнутри и решила на кабаньих копытцах пересечь границу у реки, как в песне поётся. Ну, или в очередной раз коммерческий директор Антон Скобин ультимативно заявил, что работать с ней не сможет это нарушает его хрупкое душевное равновесие.


Недавно вспоминали с сотрудниками, а кто продержался больше года? А никто. Ну, разве что я 1 год и 10 дней.


Недавно взяли специалиста по экспансии. Не шучу, так и назвали. А что, дело правильное, хорошее и светлое, как призрак коммунизма. В конце-то концов для Слёрма не охвачены такие перспективные рынки, как Марс, Венера, спутники Юпитера, да и Сатурн, чего он там прячется, тоже громадная территория для экспансии. А там и до Урана на ионных движках доползти можно будет.


Потому что с таким подходом на развитом и давно уже забитом англоязычном рынке сожрут и не заметят. А китайцы просто не пустят.


Но зато звучит мощно специалист по экспансии. Осталось только Звезду Смерти построить и править Галактикой. Уверен, у них получится.


Сеем разумное, доброе, вечное


В Слёрм, то есть в Southbridge, есть одна маленькая особенность. Насколько издевательски относятся к работникам Слёрма, настолько же уважительно к работникам Southbridge. Потому что без инженеров, эта шхуна капитана Джека Воробья затонет, не выходя из порта.


В Southbridge много классных DevOps инженеров и просто классных ребят. И многие уже сейчас ищут работу достойные компании с 300+. Я с ними общался. Одного уже переманил на нормальную должность в нормальной компании. Это HR на заметку.


Отличный специалист и оратор Марсель Ибраев. Просто классный человечище и гениальный DevOps Павел Селиванов, который вовремя свалил с Титаника в mail.ru, где хотя бы понятны правила игры. Ироничный, хоть и молчаливый, и невероятно опытный инженер Владимир Гурьянов. Морально твёрдый, правильный по-человечески, опытный и надёжный Николай Месропян. Прекрасная и внешне, и внутренне Ирина Кубанева корпоративным клиентам повезло с ней. Мягкая, добрая, способная терпеливо и понятно объяснять все финансовые вопросы главный бухгалтер Ирина Шурупова. Они слишком хороши для патологической парочки нарцисса-пограничника, чтобы оставаться в Слёрме там долго.


Увольнение


Признаюсь, я встретил его с лёгким сердцем. И когда в 10-00 понедельника мне позвонил Антон и сказал, что меня увольняет за то, что я не уважаю пользователей Хабра, я только улыбнулся и попросил уволить этим же числом.


Но даже тут Антон не смог не соврать сотрудникам Southbridge. Когда они честно ответили, что Хабр порой самих их вводит во фрустрацию и многое из них на него ложили.



То Антон тут же дал заднюю. Пограничник что с него взять, твёрдости, как в пластилине (мягко говоря, судя по запаху).



Никогда он меня не защищал. Вспомним его хитрый подход с окладом в 22000 рублей вместо 80 000. Да и никого он не ценит, кроме Игоря, своей бесценной нарциссической пары. И место в целом мне подходило, но мне крепко надоели Игорь с Антоном. Я не люблю психопатологические пары. И не люблю наркоманов, даже тех, кто просто дует шмаль. Я им не доверяю.


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


А чтобы будет дальше? Экспансирует ли Слёрм Галактику? Кто знает


Time will tell. Sooner or later time will tell.

Подробнее..

Kubernetes Мега от устройства Kubernetes до основ service mesh

11.05.2021 10:09:42 | Автор: admin
2729 мая пройдёт онлайн-интенсив Kubernetes Мега. Чему учить будем?



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

Павел Селиванов, Senior DevOps Engineer в Mail.ru Cloud Solutions, Сергей Бондарев, архитектор в Southbridge и Марсель Ибраев, CTO в Слёрм будут разбирать тонкости установки, конфигурации production-ready кластера (the-not-so-easy-way) и отвечать на ваши вопросы.

Отказоустойчивость


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

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

Тема 1: Процесс создания отказоустойчивого кластера изнутри

  • Работа с Kubeadm,
  • Тестирование и траблшутинг кластера.

Тема 9: Резервное копирование и восстановление после сбоев

  • Методы резервного копирования,
  • Бэкап и восстановление кластера с применением Heptio Velero (бывш. Ark) и etcd.

Говоря про кластер, мы думаем о некой распределённой отказоустойчивой системе, в которой задублировано практически всё: диски, процессоры, память, сетевые линки, системы хранения данных.

Подобный уровень резервирования позволяет снизить риски простоя при аппаратных сбоях.

Безопасность


Все компоненты кластера между друг другом аутентифицируются с помощью сертификатов. Мы расскажем, что делать, если сертификаты просрочились и как предотвратить такие ситуации.

Но для живых пользователей этот вариант подходит не очень хорошо, так как сложен в применении, обязательно расскажем про лучшие варианты: Authentication proxy и OIDC.

Ещё есть безопасный вариант с паролями или токенами, но это уже в теме хранения секретов, для пользователей он не очень удобен в реализации, особенно в случае с несколькими мастер-нодами.

Тема 2: Авторизация в кластере при помощи внешнего провайдера

  • LDAP (Nginx + Python),
  • OIDC (Dex + Gangway).

Тема 4: Безопасные и высокодоступные приложения в кластере

  • PodSecurityPolicy,
  • PodDisruptionBudget,
  • PriorityClass,
  • LimitRange/ResourceQuota.

Тема 7: Хранение секретов

  • Управления секретами в Kubernetes,
  • Vault.

Тема 10: Ежегодная ротация сертификатов в кластере

  • Сертификаты компонентов кластера,
  • Продление сертификатов control-plane с помощью kubeadm.

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

Устройство Kubernetes


Осознанный выбор наиболее эффективных инструментов приходит с пониманием устройства Kubernetes, его компонентов, устройства сети и процесса её создания.

Тема 3: Network policy

  • Введение в CNI,
  • Network Security Policy.

Тема 5: Kubernetes. Заглядываем под капот

  • Строение контроллера,
  • Операторы и CRD.

Более глубокое понимание, как работает Kubernetes, сеть в кластере позволит решать задачи продвинутого уровня, такие как, например, написание собственного контроллера, оптимальная организация сети в кластере Kubernetes.

Базы данных в Kubernetes


Можно ли запустить базу данных в Kubernetes можно, но есть нюансы. Надо задуматься: стоит ли запускать Stateful приложения, какие есть от этого выгоды и с какими проблемами вам предстоит столкнуться. Посмотрим на реальном примере, как можно запустить базу данных в Kubernetes.

Тема 6: Stateful приложения в кластере

  • Нюансы запуска базы данных в Kubernetes,
  • Запуск кластера базы данных на примере RabbitMQ и CockroachDB.

Единственный положительный момент это простота и скорость запуска stateful приложения, поэтому production базы, которые работают под нагрузкой, надо запускать на отдельных, выделенных серверах.

Но не всё так плохо. Для разработки и тестирования вполне можно запускать базы данных в kubernetes.

Масштабирование


Один из самых частых запросов в Kubernetes автоматически скейлить количество инстансов приложений в зависимости от нагрузки и других показателей.

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

Тема 8: Horizontal Pod Autoscaler

  • Скейлинг на основе встроенных метрик,
  • Кастомные метрики.

Деплой приложения


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

Тема 11: Деплой приложения

  • Инструменты темплэйтирования и деплоя,
  • Стратегии деплоя.

Знание и использование разнообразных стратегий деплоя позволит более гибко подходить к развёртыванию вашего приложения. Использовать преимущества одних стратегий и не обойти недостатки других, там, где это необходимо. Можно выбирать.

Service mesh


Обзорное представление технологии service mesh и её конкретной реализации Istio необходимо, чтобы понять, какие проблемы может решать service mesh, какими инструментами и когда её можно использовать.

Тема 12: Service mesh

  • Установка Istio,
  • Обзор основных абстракций.

Сертификация


Итоговая практическая работа

Сертификация от учебного центра Слёрм подтверждает, что вы действительно владеете материалом. Чтобы получить сертификат, нужно сдать внутренний экзамен: мы дадим задание и предоставим стенд для выполнения.

Выполнив задание, вы отправляете настроенный кластер на ревью. Мы оцениваем качество настройки, выставляем баллы по теме. Если вы набрали достаточно баллов, мы выдаём вам номерной именной сертификат.

Формат


Практические задания и сертификация будут выполняться из личного кабинета, трансляция со спикерами пройдёт в zoom, а для общения дополнительно будет telegram-чат.

До интенсива осталось 2 недели, регистрируйтесь по ссылке: slurm.club/megamay21
Вопросы по интенсиву в комменты.
Подробнее..

Новая версия нашего самописного плагина, который скачали 250 тысяч раз

03.02.2021 14:08:11 | Автор: admin

Привет, коллеги!

В феврале необязательно доставать чернила и плакать можно и радоваться.

Во-первых, Grafana заапрувила новую версию нашего плагина для мониторинга Kubernetes: KubeGraf v.1.5.0 доступен для инсталляции.

Во-вторых, оказалось, что за полтора года с момента выхода первой версии плагин скачали четверть миллиона раз!

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

Маленький дисклеймер:

наш продукт является переработанной версией официального плагина от Grafana (который не поддерживается уже около двух лет). Об истории его создания мы уже писали здесь, на Хабре. Напомню ключевые особенности KubeGraf:

  • интеграция с k8s-api для построения карты ваших приложений, сгруппированных по неймспейсам / нодам-кластера + привязка к конкретным podам/сервисам;

  • сводная страница со всеми ошибками / предупреждениями о работе нод и приложений кластера;

  • возможность инсталляции плагина с облачными k8s-провайдерами через авторизацию с помощью bearer-tokena.

А что нового в пятом релизе? Много разного и полезного:

  • совместимость с последними версиями Grafana;

  • добавлена информация и визуализация лимитов по cpu/memory, добавлена индикация при превышении requested и приближению к limit, а также индикация того, что для какого-либо приложения не настроены requests/limits;

  • в дашбордах по мониторингу deployments/daemonsets/statefulsets в разделе Templating теперь выводятся только те namespaceы, в которых содержатся ресурсы данного типа;

  • в дашборде мониторинга конкретных podов исправлено отображение потребления cpu/memory;

  • таблица с алертами и предупреждениями отсортирована в соответствии с severity (info/warning/critical);

  • доработана инструкция по установке плагина и исправлены k8s-манифесты, необходимые для его установки (добавлены namespaceы);

+ мелкие доработки интерфейсов и навигации.

Пользуйтесь!

Кстати

Кстати, в качестве вспомогательной утилиты в процессе разработки нашего плагина мы создали helm-чарт, с помощью которого вы можете установить любую версию плагина из репозитория с его исходным кодом (например, используя определенный тег), не дожидаясь её появления в grafana-plugins-repository.

Все так же ждём ваших звездочек, issue и pull requests в нашем репозитории.

Обсудить плагин можно в нашем телеграм-чате или в Slack.

Подробнее..

Kubernetes для разработчиков трехдневный интенсив

19.11.2020 18:06:36 | Автор: admin
image

Спикеры Слёрма готовят обновленный интенсив, в котором не будет тем для администраторов. Мы убрали тему про обслуживание кластера и сосредоточились на особенностях разработки ПО в Kubernetes. В программе только то, что действительно нужно современному разработчику на проектах с K8s.

Почему трехдневный интенсив?


В Слёрме прошло уже 14 трехдневных интенсивов по Kubernetes, и мы уверены, что такой формат дает крутые результаты. К вечеру третьего дня студенты знают, что такое Kubernetes, что в нём есть и как в нём работать. Выпускник интенсива может свободно читать документацию по K8s и понимать её. Можно возвращаться и пересматривать материалы, когда понадобится. Интенсив возможность быстро запустить подготовку специалиста. Важно помнить, что такое обучение невозможно совместить с работой (учимся с 10 до 19, отвлекаться не получится).

Короткая история одного разработчика про K8s


Вот что рассказывает про обучение Артем из Gismeteo:
Я backend-разработчик в команде Gismeteo. Занимаюсь поддержкой и разработкой существующего погодного API, настройкой CI|/CD Gitlab, написанием ролей на Ansible, выкладкой на продакшн.

В компании мы в какой-то момент приняли решение избавиться от LXC-контейнеров в пользу Docker. Так как у нас highload, одним контейнером мы бы не отделались. Из этого появился вопрос: как за этим всем следить и управлять. Поэтому мы и решили присмотреться к Kubernetes, я стал искать возможности для обучения. Читал документацию, но информация, которая там есть, подходит только для ознакомления. Без практики это пустая трата времени. Пытался смотреть видео на Ютюбе, но, опять же, не хватало практики.

Тогда я решил пойти на практический курс, выбрал интенсив Слёрма. Хотел познакомиться ближе с принципами работы k8s, узнать best practices от спикеров. Интенсивное обучение мне хорошо подошло, помощь техподдержки не понадобилась. Самым интересным моментом на курсе, по-моему, было добавление rollback piplin'a для отката версии Docker-образа. Я любитель CI/CD, поэтому для меня это было особенно актуально. Также понравилась тема про интеграцию CI/CD с Kubernetes через Helm. Сложной практической темой оказалось написание своего helm chart'a.

После курса я убедился в том, что Kubernetes на текущий момент времени лучший оркестратор для контейнеров. Продолжаю повышать квалификацию в этом направлении, теперь уже на рабочих задачах. Сейчас пересматриваю материал курса, связанный с helm, так как пересобираю сейчас все наши ci/cd под диплойку через helm. Очень удобная вещь.

Ссылка на отзыв в ВК

Артем, как и многие другие разработчики, проходил Базовый интенсив по Kubernetes. Теперь есть возможность выбрать интенсив в зависимости от специализации и рабочих задач.

Kubernetes для разработчиков пройдет 35 марта 2021, но уже сейчас можно забронировать место до конца 2020 участие стоит 20 000 рублей.
Подробнее..

Нужно ли разработчику знать Kubernetes и в какой мере? Круглый стол Слёрма и MCS

27.02.2021 16:15:57 | Автор: admin

Когда компании пора внедрять микросервисы? Как быть разработчику, который хочет или которого заставляют использовать Kubernetes? Где ответственность разработчика, а где админа? Кто виноват, если после внедрения прилёг продакшен? Об этом говорили на круглом столе учебного центра Слёрм и облачной платформы Mail.ru Cloud Solutions.
Под катом текстовая запись разговора.



Участники круглого стола:


  • Марсель Ибраев, CTO Слёрма, сертифицированный администратор Kubernetes.
  • Сергей Бондарев, администратор с более чем 20 годами опыта, Southbridge.
  • Павел Селиванов, senior DevOps engineer в MCS.
  • Дмитрий Лазаренко, директор по продукту в MCS, разработчик на Java.
  • Виктор Гамов, Developer Advocate в Confluent (ведущий митапа).

Встреча приурочена к старту онлайн-интенсива Kubernetes для разработчиков. В числе спикеров: Сергей Бондарев, Марсель Ибраев и Павел Селиванов. Интенсив начнётся 3 марта, успевайте!


Когда компании пора внедрять Kubernetes?


ВИКТОР ГАМОВ: В своих подкастах и шоу я всегда говорю, что мы как разработчики должны помогать бизнесу существовать. Все наши свистелки, моторчики и прочие клёвые штуки, которые мы сегодня нашли на GitHub, а завтра хотим отправить в прод, тоже должны работать на бизнес. Поэтому давайте подумаем, зачем компании Kubernetes и когда его пора внедрять?


ПАВЕЛ СЕЛИВАНОВ: У меня есть хороший ответ: когда CTO начитался Хабра и таки заметил слово Kubernetes. Срочно нужно тащить к себе!


ВИКТОР ГАМОВ: Отлично. Еще какие версии? На самом деле, ребят, не делайте так. Технические решения внедряют, чтобы решить какую-то конкретную проблему. Какую проблему решает Kubernetes?


ДМИТРИЙ ЛАЗАРЕНКО: В Mail.ru Cloud Solutions есть не только Kubernetes-как-сервис, но и Kubernetes внутри, для собственной разработки. Несколько лет мы жили просто деплоем на bare metal с помощью Ansible. У нас классическая эксплуатация: SRE-инженеры (как сейчас любят себя называть админы) и много команд разработки. Админы всё катили, всё верифицировали и стали узким горлышком. Разработчики перформили гораздо больше, чем могли выкатить админы.


Переход на Kubernetes решил проблему. Сейчас разработчики могут сами выкатывать и эксплуатировать свой сервис, разбираться, что происходит внутри. Фактически они выступают в роли DevOps/SRE, то есть отвечают в том числе и за успешность работы этого сервиса.


Когда мы работали в Ansible и делали подборки на Docker, такого не получалось. Перемены заняли примерно год. Паша [Павел Селиванов] как раз очень активно помогал с переходом на Kubernetes и внедрением идеологии в мозги как админов, так и разработчиков.


ВИКТОР ГАМОВ: Ты сейчас подставился, когда сказал, что разработчики могут доставлять кода больше, чем админы способны поддержать. Нужны ли нам админы, если они не могут поддержать и доставить годноту, которую выкатывают разработчики?


ДМИТРИЙ ЛАЗАРЕНКО: Это хороший вопрос для обсуждения, у меня есть мнение, но я не буду отвечать первым.


ПАВЕЛ СЕЛИВАНОВ: Всё зависит от специфики конкретной компании, инфраструктуры и области бизнеса. Понятно, что когда мы строим облако, мы категорически не можем обходиться без админов, причем админов в классическом смысле слова. Мы эксплуатируем железки в дата-центрах, и их нужно обслуживать: подключать, настраивать, задумываться о рейдах, дисках, процессорах, памяти и т. д.


При этом, учитывая развитие облаков, подходов DevOps и Infrastructure as a Code, многие компании могут обходиться без классических админов. Но скорее всего, без людей, которые отвечают за эксплуатацию и инфраструктуру, не могут. Всё же нужен тот человек, который это на себя возьмет. Но не факт, что он обязательно должен быть админом от сахи, человеком, который пришёл из железок.


ВИКТОР ГАМОВ: То есть если вы деплоите Kubernetes у себя, всё равно должны быть люди, которые немножечко в этом разбираются. Другой вопрос: должны ли разработчики иметь доступ к продакшену?


ПАВЕЛ СЕЛИВАНОВ: Отвечу по своему опыту. Я наблюдал не в одной компании, как это работает. Принцип разделения, когда к продакшену имеют доступ только отдельные люди, конечно, прекрасный. Но как происходит в реальности: есть разработчик, который знает, что ему надо сделать, как это работает, как это осуществить; есть админ с доступом; они объединяются. Разработчик ставит стульчик рядом с рабочим местом админа и на ухо рассказывает тому, что нужно делать. Админ нажимает кнопочки, при этом периодически нажимает что-то не то.


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


Отвечая на твой вопрос: мне кажется, что в очень небольшом количестве компаний безопасность выстроена так, что разработчик действительно не может получить доступ к продакшену. Здесь скорее Security through obscurity (безопасность через неясность) получается.


ВИКТОР ГАМОВ: Но если разделение обязанностей мешает, как найти виновного тогда? Вот деплоили, что-то не заработало, у нас убытки, кто виноват?


ПАВЕЛ СЕЛИВАНОВ: Расскажу, как мы в MCS решаем эту проблему. За сервис в любом случае отвечает разработчик, который его разрабатывал. Мы идём в таком направлении: команда разработки сервис пишет, она же за него и отвечает, и она же его эксплуатирует. Когда сервис сломался, задача разработки понять, что там сломалось и найти ответственных за этот компонент. Понятно, что если сломалась база данных, которую предоставляет команда сопровождения, то разработка вряд ли пойдёт поднимать эту базу данных, но задача разработки разобраться и поднять нужных людей.


ВИКТОР ГАМОВ: У нас как раз следующий вопрос связан с этим рассуждениями.


За что отвечают разработчики, а за что DevOps при работе с Kubernetes? Кто более компетентен?


ВИКТОР ГАМОВ: Вот Паша говорит, что разработчики должны поддерживать то, что они написали. Как по мне, это история не про поиск виноватых, а про делегирование и доверие команде. Когда людям предоставляют возможность доказать, что они могут, они начинают немного по-другому мыслить. Начинают работать не с девяти и до обеда, а ради цели.


ДМИТРИЙ ЛАЗАРЕНКО: Это про вовлечение разработчиков, когда они перестают быть кодерами и становятся архитекторами сервисов. Они становятся причастными к созданию чего-то великого, ну и к провалам в том числе, если что-то идет не так.


ВИКТОР ГАМОВ: Поэтому мне всегда больно видеть термин DevOps в значении человек, а не в значении культура. Необходимо изменять сознание и подходы к тому, как работают люди, иначе никакие технологии нам не помогут. Нам необходимо менять культуру и восприятие людей. Кто со мной поспорит?


СЕРГЕЙ БОНДАРЕВ: Культура, конечно, вещь хорошая, но не главная. Ты можешь быть сколь угодно культурным, но если у тебя нет компетенций, тебя ждут только великие провалы.


ВИКТОР ГАМОВ: То есть тот, кто более компетентен, тот и берёт на себя ответственность? Есть админы, есть опсы, кто из них важнее на проекте?


СЕРГЕЙ БОНДАРЕВ: Наверное, никто из них. Продукт оунер, скорее.


ПАВЕЛ СЕЛИВАНОВ: Архитектурный комитет.


ВИКТОР ГАМОВ: Ага, то есть никто. Если есть архитектурный комитет, то дело может сильно затянуться. Марсель, у тебя какие соображения?


МАРСЕЛЬ ИБРАЕВ: Соглашусь, что никто не главный, но зоны ответственности разные. Есть инфраструктурная команда, и все-таки ключевые решения по инфраструктуре, я думаю, за ними. А разработчики уже решают, что и как кодить под эту инфраструктуру, какие инструменты использовать. При этом в идеальном мире должна происходить синергия, и решаться всё это должно не лоб в лоб, а сообща.


ВИКТОР ГАМОВ: Я ждал, кто первым скажет слово синергия. Ты выбиваешь булшит-бинго!


ДМИТРИЙ ЛАЗАРЕНКО: Я добавлю. Паша говорил про архитектурный комитет. В чём сложность ситуации? Вот откройте карту CNCF: там сотни проектов, сотни сервисов, и все это новое. В реальности мало кто годами использовал, например, Istio. И на вопрос, кто главный при выборе Istio, ответ: да хрен его знает. Потому что кто глубже копнет, разберётся и аргументированнее даст ответ, тот и главный. Часто эти люди первопроходцы. И это одна из проблем.


ВИКТОР ГАМОВ: Я сейчас вброшу, а вы поспорьте со мной. В чём DevOps меняет восприятие разработки, так это в том, что можно проводить эксперименты и изучать технологии прямо в процессе. Благодаря Kubernetes мы можем делать AB-тестинг: например, на одной части контура использовать Istio, на другой части ещё какого-нибудь вендора. Таким образом обучение идёт сильно быстрее. Поэтому Kubernetes это не платформа, это базис для построения ваших платформ. В этом смысле он совершенно потрясающий.


ПАВЕЛ СЕЛИВАНОВ: Вот именно, что Kubernetes это не законченная вещь, это конструктор, который нужно собрать под себя. Я видел в нескольких проектах такое разделение: некие мифические DevOpsы строят платформу для разработки, чтобы разработчик мог как можно меньше заморачиваться на этапе разработки по поводу инфраструктурных моментов, меньше ломать, если что-то пойдет не так, и как можно более гибко её использовать. Вот DevOpsы строят платформу на основе Kubernetes, разработчики её эксплуатируют, то есть запускают там свои сервисы, деплоят то, что им нужно, настраивают между ними связи и так далее.


СЕРГЕЙ БОНДАРЕВ: Можно мне поныть на тему изучения новых технологий. Неумеренность в этом вопросе приводит к тому, что приходишь на проект, видишь кучу новых слов, начинаешь в них разбираться, и оказывается, что часть технологий уже прекратила своё развитие, часть из них ещё в глубокой альфе. Но разработчики категорически настаивают, что они хотят это использовать, им это необходимо, и вообще по-другому они никак не могут жить. И тут задача опсов убедить разработчиков, что какие-то вещи ещё рано использовать, а какие-то поздно.


ВИКТОР ГАМОВ: Тут речь как раз про культуру. Когда в команде есть доверие, сложные вопросы решаются легче.


Что для вас знать Kubernetes?


ВИКТОР ГАМОВ: Что для вас знание Kubernetes? Знание ключей и опций kubectl? Архитектуры? Компонентов? Отличий от Docker и Docker Compose? Сертификаты CKA/CKAD? Расшифруйте, чтобы, выровнять понятийный уровень. Что важнее: сертификаты или опыт?


МАРСЕЛЬ ИБРАЕВ: Знания, конечно. Но сертификат штука приятная. Вы можете указать длинный списочек: ваши регалии, грамоты школьные приложить.


ВИКТОР ГАМОВ: Какие сертификаты получат слушатели курса по Kubernetes для разработчиков? Или курс больше нацелен на получение практических знаний?


МАРСЕЛЬ ИБРАЕВ: Мы всегда делали акцент на практику. Наша цель не подготовить к сертификации, а подготовить к применению знаний на практике. В первую очередь знание, а нужен сертификат или нет, каждый решает сам.


СЕРГЕЙ БОНДАРЕВ: Система сертификации пришла к нам, наверное, с Запада. Она рассчитана на то, что незнакомый человек посмотрит на твою доску почета и проникнется, поймёт, что какой-то дядя проверил твои знания и выдал бумажку, что ты вот эту штуку знаешь хорошо. Если он этому дяде доверяет, значит, сертификат хороший. Если это выдано какой-нибудь Районной Академией Наук, то, наверное, сертификат не очень. Сертификация тоже бывает очень разная.


С тех пор как за сертификацию начали брать деньги, это стало отдельным бизнесом. Ситуация такая: если я сделаю нормальную сертификацию, на которой задаются действительно сложные вопросы, никто не придет ее сдавать, я не получу своих денег, поэтому сертификацию мы делаем по низу средних способностей, чтобы хотя бы 70-80% людей, которые к нам приходят, ее сдавали. Другая крайность брать за сертификацию большие деньги, делать ее сложной и ожидать, что придет мало людей, но за этих людей будет платить работодатель. А компаниям, в которых работают крутые специалисты, сертификация особо и не нужна: они уже знают, что у них есть крутые специалисты, зачем их сертифицировать.


ВИКТОР ГАМОВ: Дима или Паша, когда к вам приходят соискатели на вакансию SRE или разработчика, вы смотрите на сертификаты? Или делаете прожарку тестовым заданием?


ДМИТРИЙ ЛАЗАРЕНКО: В MCS мы не смотрим на сертификаты. А смотрим на то, как человек подходит к решению проблемы, насколько попытается разобраться в причине, как коммуницирует с другими людьми (это уже история про софт-скилы), и в целом, насколько разбирается в архитектуре, понимает репликацию в базах данных или как работает Kubernetes. Это лучше, чем просто формальная сдача экзамена на сертификат. Мы смотрим на problem-solving и на то, как ты коммуницируешь с другими людьми во время решения проблемы. Потому что герои-одиночки, которые могут быть токсичными, никому не интересны. Мы отказывали людям, которые не могут хорошо общаться с командой, но при этом очень крутые технические специалисты.


ПАВЕЛ СЕЛИВАНОВ: Когда проходишь собеседование и говоришь, что есть сертификат, обычно на это отвечают: Ага, давайте к следующему вопросу. Мне кажется, сертификаты, особенно кубернетесовские, в основном нужны компаниям. Я работал в двух компаниях, у которых есть сертификация Linux Foundation от CNCF. Соответственно, чтобы компании пройти такую сертификацию, в штате должно быть определённое количество специалистов, которые сертифицированы как администраторы или девелоперы Kubernetes. Это тот случай, когда реально стоит пройти CK или CKD. Если вы думаете, что CK или CKD поднимет зарплату или увеличит шансы устроиться на работу, то вы, вероятно, ошибаетесь. По крайней мере, в России на эту бумажку вообще никто не смотрит.


В какой момент среднестатистическому разработчику стоит задуматься об умении использовать Kubernetes?


ВИКТОР ГАМОВ: Стоит ли полагаться на разработчиков при внедрении или лучше найти DevOps-инженера?


ПАВЕЛ СЕЛИВАНОВ: Что касается внедрения Kubernetes и вообще любых инфраструктурных вещей Если вы стартап, вам надо просто запуститься и показать, какая ваша разработка крутая, то можно обойтись без выделенного человека. Дальше, я боюсь, если вы не наймёте такого человека, то у вас в команде быстренько найдется разработчик, на которого все это свалится. И тут всё зависит от того, хотел разработчик, чтобы на него это свалилось, или не хотел. Он может проклясть вас, а может, и наоборот, обрадоваться, что больше не надо писать эти дурацкие бизнес-приложения.


Отвечая на вопрос, насколько разработчику вообще необходимо уметь использовать Kubernetes, я бы не стал этим заморачиваться до тех пор, пока в компании нет Kubernetes или нет возможности его использовать. Но как только админы или DevOps припрут Kubernetes, вам так или иначе придётся с ним разобраться. Потому что вряд ли вам сразу же напишут красивые дашборд-кнопки, которые нажимаешь и всё разворачивается. Придётся узнать, что такое поды, реплика-сеты, деплойменты, зачем нужны стейтфул-сеты и почему не надо писать в Kubernetes базу данных.


ВИКТОР ГАМОВ: Ты ведь из облачного провайдера, почему ты не говоришь, что cloud-провайдер даёт тебе не просто голый Kubernetes, a managed-Kubernetes, снимает головняк?


ДМИТРИЙ ЛАЗАРЕНКО: Дьявол в деталях. Наверное, самое интересное начинается, когда вы внедрили Kubernetes и первый раз прилёг продакшен. Что происходит потом, можно долго описывать. Разработчик должен понимать все эти примитивы внутри Kubernetes, он должен понимать интеграцию с разными инструментами: зачем ему Prometheus, какие метрики нужно мониторить, как настроить алёрты, как использовать все фишки Prometheus и как правильно работать с логами.


Без понимания эксплуатации всё становится грустно, и приложение чинится сутками. DevOpsы не понимают бизнес-логику работы приложения, а разработчики не понимают, как работает Kubernetes и что там сломалось. Одна из ключевых проблем, которая была в начале с OpenShift, в том, что никто не понимал, как вся эта машина работает. С Kubernetes всё становится попроще, потому что там прям такие кубики. Но в кубиках нужно разбираться. Часть, которая связана с эксплуатацией и реальной жизнью, не менее важна, чем просто научиться ямлики писать.


ВИКТОР ГАМОВ: То есть ты хочешь сказать, что мало знать Java, например, или Golang, и разбираться, как алгоритмы на них написать, ещё нужно понимать, где и как это будет выполняться? Мало того что это будет выполняться в какой-то среде, так эта среда ещё не настоящая. Потому что вокруг этой среды ещё много всего.


ДМИТРИЙ ЛАЗАРЕНКО: Не совсем так. Kubernetes в этом отношении даёт унификацию. В целом понятно, какая среда повторяемая. Пускай даже используются разные облачные провайдеры, но в целом все работает более-менее одинаково. Но среда всё равно непривычная, другая. Там ещё много всего вокруг, и оно может стрельнуть.


ВИКТОР ГАМОВ: Может быть, стоит провайдеру снимать эти головняки? У нас есть админы, которые умеют готовить Kubernetes, есть разработчики, которые в принципе знают, как писать приложение, но ещё им нужно понимать, как варить Kubernetes. Может быть, их кто-то встретит как раз хорошим сервисом, чтобы уменьшить головняк?


ДМИТРИЙ ЛАЗАРЕНКО: Идея хорошая, но это как с безопасностью. Если всё будет безопасно, работать будет невозможно. За всё приходится платить. За автоматизацию, о которой ты говоришь, приходится платить очень жёсткими правилами. К сожалению или к счастью, у нас мультикультурное общество, каждый думает по-своему, и невозможно придумать унифицированные правила для работы приложения. Все пытаются это делать, изобретают фреймворки, но идея утопична. Всякие спинакеры и подобные продукты позволяют унифицировать и упростить процесс CI/CD. Но это не единственный процесс. Обилие open source инструментов делает задачу нерешаемой. Да, облачный провайдер упрощает жизнь и позволяет системным администраторам и DevOps экономить время на выполнении рутинных операций, но это не серебряная пуля. Невозможно всё автоматизировать, иначе люди не нужны будут. И плюс у облачных провайдеров не столько разработчиков, чтобы сделать эту магическую оптимизацию.


СЕРГЕЙ БОНДАРЕВ: Cloud-провайдеры предоставляют набор готовых решений, в которые ты волей-неволей должен упихиваться. Если тебя этот набор удовлетворяет, то в принципе жить в облаке не проблема. Но иногда есть потребности или идеи, которые в облаке реализованы не так, как ты хотел. И ты отказываешься от каких-то частей cloud-решений, потому что они тебя не удовлетворяет по своим возможностям и своим ограничениям. Кроме того, облачные решения консервативны. Если что-то сломалось, чинить это будут долго. А сам ты себе это можешь поставить и нормально работать.


ВИКТОР ГАМОВ: Да, такое будет в любой managed-системе.


Зачем перегружать разработчика информацией, когда можно просто дать ему кнопку?


ПАВЕЛ СЕЛИВАНОВ: Это такой комплексный вопрос, столько сразу слоев. Давайте представим ситуацию: у нас есть бизнес, техническая команда решила, что им нужен Kubernetes, и вся бизнес-разработка останавливается, пока DevOpsы не напилят кнопку, которая удовлетворит все потребности разработчиков. Я думаю, что это нереальная ситуация. В любом случае разработчики будут жить с Kubernetes, сами будут с ним разбираться. Со временем, наверное, это взаимодействие будет уменьшаться, но я не верю, что это произойдёт быстро и разработчик избавится от необходимости знакомиться с Kubernetes.


Опять же, Kubernetes, да и микросервисы вообще, позволяют выкатывать фичи быстро. Так быстро, как это нужно бизнесу, при этом не сильно заморачиваться, как что-то работает под капотом. Гораздо проще написать простой микросервис, какую-нибудь фиговину, которая будет базу данных перекладывать в информацию в формат Json, чем действительно разбираться с новыми или старыми технологиями и писать что-то низкоуровневое. Мне кажется, Kubernetes как раз-таки позволяет удовлетворять современные требования бизнеса.


МАРСЕЛЬ ИБРАЕВ: Тут, конечно, вопрос, как Паша сказал, многослойный. И без привязки к кому-то примеру сложно сказать. Но если мы хостим всё у себя, без облаков, и при этом мы говорим, что у нас DevOps, то, мне кажется, DevOps у нас не получится, если не будет тесной взаимосвязи и понимания, что делает разработка, а что админы.


Что будет на курсе?


ВИКТОР ГАМОВ: Давайте конкретики добавим: что будет на курсе, что поможет, например, Java-разработчику разбираться глубже в вопросе деплоя?


МАРСЕЛЬ ИБРАЕВ: Кстати, вопрос, который был на слайде: что есть Kubernetes? Мы как-то без конкретики его пробежали. Можем вернуться и поконкретнее рассказать.


Что нужно знать разработчику: это, разумеется, основные абстракции куба в чем ваше приложение запускается и какие инструменты поддерживаются. Далее, вы хотите, чтобы ваше приложение работало только со stateful-данными, или вы хотите, чтобы ваше приложение запускалась на каждой ноде кластера, все это разные абстракции. Их определенно нужно знать и админам, и разработчику. Банальный пример: что-то не заработало, надо срочно фиксить. Инфраструктура, конечно, полезет и на крайний случай откатит. Но факт, что проблема возникла после деплоя, намекает: проблема может быть в коде, надо садиться и разбираться. Разбираться лучше уже на стейдже, поэтому какая-то база в голове должна быть.


ПАВЕЛ СЕЛИВАНОВ: Про курсы любят спрашивать: а что мне на курсе расскажут такого, чего нет в документации? зачем мне эти курсы нужны?. Я могу ответить, что в этом курсе гарантированно есть вещи, которых нет в документации.


Среди присланных вопросов был вопрос, как дебажить Java или что-то такое. Это вообще распространенная в Kubernetes штука: у вас есть продакшен, и естественно разработчики хотят прийти на продакшен, залезть в контейнеры и начать там на живых пользователях что-то дебажить. Вот что нужно знать разработчику про Kubernetes.


Про абстракции, джобы, стейтфул-сеты можно почитать документацию недельки чтения будет достаточно. А вот реальная эксплуатация кластера Kubernetes и задачи разработки С этими задачами вам админы не пойдут помогать. Как ваше приложение дебажить, какие инструменты для этого есть, как вам построить локальную разработку так, чтобы код, который вы пишете, одинаково хорошо работал в Docker, Docker-compose, Kubernetres и так далее. Как это все собирать между собой. Вот это те вещи, которые мы хотим разобрать в курсе.

Что использовать для локальной разработки под Kubernetes?


ВИКТОР ГАМОВ: Давайте по тулам поговорим. Здесь был вопрос, мне очень он нравится самому: Что думаете по поводу k3s как способе развернуть кластер и разобраться в его устройстве?.


СЕРГЕЙ БОНДАРЕВ: Это детище компании Rancher. k3s заявлялся как Kubernetes, который будет запускаться на холодильнике.


ВИКТОР ГАМОВ: Это такая штука, которая полностью обрезана.


СЕРГЕЙ БОНДАРЕВ: У него отрезано не так уж и много. Вырезаны облачные контроллеры и прочее, но самое главное, у него вырезан etcd, вместо него используется SK Light, и всё это работало на одной-единственной машинке, грубо говоря. Использовать эту штуку для изучения Kubernetes не самый лучший вариант. Проще и быстрее поставить себе Minikube, или почитать документацию, взять kubeadm в зубы и поставить его на машину с Docker из одного узла и на нём изучать.


ВИКТОР ГАМОВ: То есть ты поклонник подхода Kubernetes the Hard Way? Когда нужно пойти и разобраться.


СЕРГЕЙ БОНДАРЕВ: Я поклонник установки с помощью kubespray.


ВИКТОР ГАМОВ: А ведь мы не сказали, что Сергей Бондарев один из тех, кто коммитит в kubespray.


СЕРГЕЙ БОНДАРЕВ: Я в нём разбираюсь, время от времени чиню баги и дополняю функционал.


ВИКТОР ГАМОВ: Раз уж мы заговорили про тулы внедрения. Какая там сейчас обстановка? Раньше был капс, теперь есть kubeadm, k3s, kubespray что их отличает и что взять? Ты упомянул Minikube, но по моему опыту он достаточно капризный. Под MacOS, например, он очень странно жил.


СЕРГЕЙ БОНДАРЕВ: Это не ко мне вопрос. У меня всегда есть несколько виртуалок с Docker, которые объединены в локальную сеть. Я на них могу поставить что угодно. Необходимости поставить что-то на локальном компьютере у меня не возникает.


ВИКТОР ГАМОВ: k3s был популярен, потому что в нём обрезаны многие лишние вещи. Например, обратная совместимость


СЕРГЕЙ БОНДАРЕВ: Ну как же! Обратная совместимость это самое важное, что есть в Kubernetes.


ВИКТОР ГАМОВ: Мы ведь не говорим про использование k3s в продакшене. Речь про локальную разработку. Паша, как вы в Mail.ru разрабатываете вещи, которые пишете под Kubernetes?


ПАВЕЛ СЕЛИВАНОВ: Очень просто. Если мне нужно сделать что-то локально с самим Kubernetes, то есть разработать то, что я буду запускать в Kubernetes, это Minikube. С MacOS недавно только одну проблемы заметил: они по умолчанию перешли на использование драйвера Docker, там теперь запускается контейнер в контейнере. И в таком случае у него не работают ingress-контроллеры. Они просто изнутри Docker недоступны становятся. Пришлось откатиться локально, использовать Virtualbox или X-hive (встроенную маковскую виртуализацию).


Если нужно решать какие-то админские задачи: нужно что-то в самом кластере проверить, протестировать, как конфигурации какие-то будут работать, взаимодействие между нодами, я использую какое-то облако. В Google, AWS или даже у нас за бонусные баллы его легко получить. Поднимаю на виртуалке, настраиваю там Kubernetes и так далее.


Что касается самих установщиков: Kops это утилита номер один для развёртывания Kubernetes в облаке, для остальных случаев Kubespray.


ВИКТОР ГАМОВ: Марсель, Дима, есть что добавить по поводу тулов, что мы посоветуем разработчикам использовать локально?


МАРСЕЛЬ ИБРАЕВ: Для разработки действительно удобнее Minikube чтобы что-то локально потестить, посмотреть, потыкать. А если есть задача поизучать его, поковырять, разобраться в логике, в компонентах, то поддержу Сергея: лучше взять какой-нибудь kubeadm и развернуть хотя бы однонодный кластер, там уже будет что-то близкое к продакшену.


А что с безопасностью?


ВИКТОР ГАМОВ: Самый важный вопрос обсудить забыли: что у нас с безопасностью? Что есть в Kubernetes, что позволяет обеспечить безопасность.


ПАВЕЛ СЕЛИВАНОВ: В чатике как раз был вопрос, как в синергию админов и разработчиков вписать безопасников, то есть как сделать DevSecOps как создавать тулы безопасников, как их встраивать в общий пайплайн.


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


Инструментов на сегодняшний день огромное количество. Мне как DevOps инженеру, человеку, который пишет пайплайны, было бы интересно всё это дело в свои пайплайны встраивать, просто времени не хватает. Я бы хотел, чтобы кто-то этим занимался. Но у самого Kubernetes есть airbag, это встроенная фишка. Можно подключить какой-нибудь hashicorp vault, чтобы хранить секреты, это, наверное, самое распространенное. Есть еще штуки типа Open Policy Agent, который позволяет просматривать и писать конкретные правила под всё, что запускается в Kubernetes, делать свои собственные политики безопасности, настраивается все это очень гибко, проверяется и так далее. Есть инструменты, которые добавляют просто авторизацию в кластер и делают это нормально, типа Keycloack, Dex. Есть штуки, которые позволяют анализировать содержимое контейнеров, то, что вы собираетесь деплоить на свои продакшен-серверы. Например, Harbor, JFrog и т д. Инструментов огромное количество. Вопрос: кто бы ими занимался, потому что отставные подполковники явно не станут это делать.


ВИКТОР ГАМОВ: Поддержу Пашу. Сегодня безопасность перестаёт быть чем-то загадочным из серии мы сгенерировали ключи, запечатали в конверты и разослали. Есть набор совершенно понятных решений, которые надо внедрять. А вот всеми любимый Kerberos. Как делать Kerberos на Kubernetes?


МАРСЕЛЬ ИБРАЕВ: В Kubernetes точно есть инструментарий, который может с LDAP-ами всякими дружить, и делает это достаточно хорошо. Kerberos я не пробовал.


СЕРГЕЙ БОНДАРЕВ: Для Kerberos мы слишком молоды.


ДМИТРИЙ ЛАЗАРЕНКО: В Keycloak есть штука, которая позволяет обменивать токены одного типа, например, самловские токены в open d connect токены. И за счёт такого пайплайна обмена токенов Kerberosа в open d connect, которые понимает Kubernetes, наверное, можно сделать подобный костыль.


ВИКТОР ГАМОВ. В завершение давайте ещё раз поговорим про тулы. Манифесты пишутся с использованием Helm, как разработчику упростить себе жизнь при работе с ним?


ПАВЕЛ СЕЛИВАНОВ: Могу поделиться, как мы сейчас делаем это в MCS. В третьем Helm есть библиотечные чарты, на базе которых мы собрали единый Helm-чарт. И фактически всё, что нужно знать разработчику, это посмотреть документацию к единому Helm-чарту, посмотреть, какие там есть вэльюсы, и в своем проекте заполнить просто один файлик с вэльюсами (на самом деле, не один, а по одному файлику на каждое окружение). Они еще между собой мёрджатся.


Благодаря этому разработчики не касаются Kubernetes, при этом мы применяем все best practices, которые хочется применять к манифестам, используемым разработчиками. Фактически мы из этих вэльюсов разработки генерим готовые чарты. Точнее, подставляем их в наш общий чарт и деплоим это в кластер. Вот как упрощается жизнь разработчика: они не касаются Helma и Kubernetesa вообще никаким образом.


ВИКТОР ГАМОВ: И хорошо! В конце разговора ещё раз напомню, что 3-5 марта будет проходить интенсив по Kubernetes для разработчиков на платформе Слёрм. Kubernetes можно будет не только изучить, но потрогать и пощупать, потому что ребята из Mail.ru дадут всем участникам бонусные баллы.

Подробнее..

Продвинутые абстракции Kubernetes Job, CronJob

05.11.2020 04:10:16 | Автор: admin


Что такое Job и CronJob в Kubernetes, для чего они нужны, а для чего их использовать не стоит.
Эта статья выжимка из лекции вечерней школы Слёрм Kubernetes.


Job: сущность для разовых задач


Job (работа, задание) это yaml-манифест, который создаёт под для выполнения разовой задачи. Если запуск задачи завершается с ошибкой, Job перезапускает поды до успешного выполнения или до истечения таймаутов. Когда задача выполнена, Job считается завершённым и больше никогда в кластере не запускается. Job это сущность для разовых задач.


Когда используют Job


При установке и настройке окружения. Например, мы построили CI/CD, который при создании новой ветки автоматически создаёт для неё окружение для тестирования. Появилась ветка в неё пошли коммиты CI/CD создал в кластере отдельный namespace и запустил Job тот, в свою очередь, создал базу данных, налил туда данные, все конфиги сохранил в Secret и ConfigMap. То есть Job подготовил цельное окружение, на котором можно тестировать и отлаживать новую функциональность.


При выкатке helm chart. После развёртывания helm chart с помощью хуков (hook) запускается Job, чтобы проверить, как раскатилось приложение и работает ли оно.


Таймауты, ограничивающие время выполнения Job


Job будет создавать поды до тех пор, пока под не завершится с успешным результатом. Это значит, что если в поде есть ошибка, которая приводит к неуспешному результату (exit code не равен 0), то Job будет пересоздавать этот под до бесконечности. Чтобы ограничить перезапуски, в описании Job есть два таймаута: activeDeadlineSeconds и backoffLimit.


activeDeadlineSeconds это количество секунд, которое отводится всему Job на выполнение. Обратите внимание, это ограничение не для одного пода или одной попытки запуска, а для всего Job.


Например, если указать в Job, что activeDeadlineSeconds равен 200 сек., а наше приложение падает с ошибкой через 5 сек., то Job сделает 40 попыток и только после этого остановится.


backoffLimit это количество попыток. Если указать 2, то Job дважды попробует запустить под и остановится.


Параметр backoffLimit очень важен, потому что, если его не задать, контроллер будет создавать поды бесконечно. А ведь чем больше объектов в кластере, тем больше ресурсов API нужно серверам, и что самое главное: каждый такой под это как минимум два контейнера в остановленном состоянии на узлах кластера. При этом поды в состоянии Completed или Failed не учитываются в ограничении 110 подов на узел, и в итоге, когда на узле будет несколько тысяч контейнеров, докер-демону скорее всего будет очень плохо.


Учитывая, что контроллер постоянно увеличивает время между попытками запуска подов, проблемы могут начаться в ночь с пятницы на понедельник. Особенно, если вы не мониторите количество подов в кластере, которые не находятся в статусе Running.


Удаление Job


После успешного завершения задания манифесты Job и подов, созданных им, остаются в кластере навсегда. Все поля Job имеют статус Immutable, то есть неизменяемый, и поэтому обычно при создании Job из различных автоматических сценариев сначала удаляют Job, который остался от предыдущего запуска. Практика генерации уникальных имен для запуска таких Job может привести к накоплению большого количества ненужных манифестов.


В Kubernetes есть специальный TTL Controller, который умеет удалять завершенные Job вместе с подами. Вот только он появился в версии 1.12 и до сих пор находится в статусе alpha, поэтому его необходимо включать с помощью соответствующего feature gate TTLAfterFinished.


ttlSecondsAfterFinished указывает, через сколько секунд специальный TimeToLive контроллер должен удалить завершившийся Job вместе с подами и их логами.


spec:  ttlSecondsAfterFinished: 100

Манифест


Посмотрим на пример Job-манифеста.


apiVersion: batch/v1kind: Jobmetadata:  name: hellospec:  backoffLimit: 2  activeDeadlineSeconds: 60  template:    spec:      containers:      - name: hello        image: busybox        args:         - /bin/sh        - -c        - date; echo Hello from the Kubernetes cluster      restartPolicy: Never

В начале указаны название api-группы, тип сущности, имя и дальше спецификация.


В спецификации указаны таймауты и темплейт пода, который будет запускаться. Опции backoffLimit: 2 и activeDeadlineSeconds: 60 значат, что Job будет пытаться выполнить задачу не более двух раз и в общей сложности не дольше 60 секунд.


template это описание пода, который будет выполнять задачу; в нашем случае запускается простой контейнер busybox, который выводит текущую дату и передаёт привет из Kubernetes.


Практические примеры


kubectl apply -f job.yaml

И посмотрим, что получилось.


k get pod -w

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


Статистику по Job можно посмотреть следующей командой.


kubectl get job

Видим, что завершились все задания, время выполнения 5 секунд.


Ненужный Job обязательно надо удалять. Потому что, если мы не удалим его руками, Job и под будут висеть в кластере всегда никакой garbage collector не придёт и не удалит их.


Если у вас запускаются по 10-20 заданий в час, и никто их не удаляет, они копятся и в кластере появляется много абстракций, которые никому не нужны, но место занимают. А как я уже говорил выше, каждый под в состоянии Completed это, как минимум, два остановленных контейнера на узле. А докер демон начинает притормаживать, если на узле оказывается несколько сотен контейнеров, и не важно, работают они или остановлены.


Команда для удаления:


kubectl delete job hello

Что будет, если сломать Job


Job, который выполняется без проблем, не очень интересен. Давайте мы над ним немного поиздеваемся.


Поправим yaml: добавим в темплейт контейнера exit 1. То есть скажем Jobу, чтобы он завершался с кодом завершения 1. Для Kubernetes это будет сигналом о том, что Job завершился неуспешно.


    containers:      - name: hello        image: busybox        args:         - /bin/sh        - -c        - date; echo Hello from the Kubernetes cluster; exit 1      restartPolicy: Never

Применяем и смотрим, что происходит: один контейнер создался и упал с ошибкой, затем ещё и ещё один. Больше ничего не создаётся.



В статистике подов видим, что создано три пода, у каждого статус Error. Из статистики Job следует, что у нас создан один Job и он не завершился.



Если посмотреть описание, то увидим, что было создано три пода, и Job завершился, потому что был достигнут backoffLimit.



Обратите внимание! В yaml лимит равен 2. То есть, если следовать документации, Job должен был остановиться после двух раз, но мы видим три пода. В данном случае после выполнения двух раз значит 3 попытки. Когда мы проделываем то же самое на интенсиве с сотней студентов, то примерно у половины создаётся два пода, а у оставшихся три. Это надо понять и простить.


Проверка ограничения по времени


Сделаем бесконечный цикл и посмотрим, как работает ограничение по времени activeDeadlineSeconds.


Ограничения оставим теми же (60 секунд), изменим описание контейнера: сделаем бесконечный цикл.


    containers:      - name: hello        image: busybox        args:         - /bin/sh        - -c        - while true; do date; echo Hello from the Kubernetes cluster; sleep 1; done      restartPolicy: Never

Под запустился.



Если посмотреть в логи, то увидим, что каждую секунду у нас появляется новый Hello всё как надо.


Через 60 секунд под оказывается в статусе Terminating (иногда это происходит через 10 сек).



Вспомним, как в Kubernetes реализована концепция остановки подов. Когда приходит время остановить под, то есть все контейнеры в поде, контейнерам посылается sigterm-сигнал и Kubernetes ждёт определённое время, чтобы приложение внутри контейнера отреагировало на этот сигнал.
В нашем случае приложение это простой bash-скрипт с бесконечным циклом, реагировать на сигнал некому. Kubernetes ждёт время, которое задано в параметре graceful shutdown. По дефолту 30 секунд. То есть если за 30 секунд приложение на sigterm не среагировало, дальше посылается sigkill и процесс с pid 1 внутри контейнера убивается, контейнер останавливается.


Спустя чуть более 100 секунд под удалился. Причем ничего в кластере не осталось, потому что единственный способ остановить что-то в контейнере это послать sigterm и sigkill. После этого приходит garbage collector, который удаляет все поды в статусе Terminating, чтобы они не засоряли кластер.


В описании Job мы увидим, что он был остановлен, так как активность превысила допустимую.



Поле restartPolycy


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


apiVersion: batch/v1kind: Jobmetadata:  name: hellospec:  backoffLimit: 2  activeDeadlineSeconds: 60  template:    spec:      containers:      - name: hello        image: busybox        args:         - /bin/sh        - -c        - date; echo Hello from the Kubernetes cluster      restartPolicy: Never

Этот параметр говорит kubelet, что делать с контейнером после того, как он был завершён с ошибкой. По умолчанию стоит политика Always, то есть если у нас контейнер в поде завершился, kubelet этот контейнер перезапускает. Причем, все остальные контейнеры в поде продолжают работать, а перезапускается только упавший контейнер.


Это политика по умолчанию, и если её применить в Job, то Job-контроллер не сможет получить информацию о том, что под был завершён с ошибкой. С его точки зрения под будет очень долго выполняться, а то, что kubelet перезапускает упавший контейнер, Job-контроллер не увидит.


Если вы укажете только backoffLimit, но забудете указать restartPolicy, то Job будет выполняться бесконечно.
Поэтому в Job надо всегда указывать:
  • backoffLimit (количество попыток),
  • activeDeadlineSeconds (общее время),
  • restartPolycy: Never (сказать kubelet, чтобы он никогда не перезапускал контейнер в поде; если контейнер в поде упал, то и сам под считается упавшим, то есть завершённым. Пусть Job-контроллер разбирается, что произошло).

Инструкция по Jobам в документации Kubernetes


CronJob: создание объектов Job по расписанию


Job позволяет выполнить разовые задачи, но на практике постоянно возникает потребность выполнять что-то по расписанию. И вот здесь Kubernetes предлагает CronJob.


CronJob это yaml-манифест, на основании которого по расписанию создаются Jobы, которые в свою очередь создают поды, а те делают полезную работу.


На первый взгляд, всё вроде бы просто, но, как и в предыдущем случае, тут есть куча мест, где можно испытать боль.


В манифесте CronJob указывают расписание и ещё несколько важных параметров.


  • startingDeadlineSeconds,
  • concurrencyPolicy.

И два параметра, которые влияют на историю выполнения.


  • successfulJobsHistoryLimit,
  • failedJobsHistoryLimit.

Посмотрим на манифест CronJob и поговорим о каждом параметре подробнее.


apiVersion: batch/v1beta1kind: CronJobmetadata:  name: hellospec:  schedule: "*/1 * * * *"  concurrencyPolicy: Allow  jobTemplate:    spec:      backoffLimit: 2      activeDeadlineSeconds: 100      template:        spec:          containers:          - name: hello            image: busybox            args:            - /bin/sh            - -c            - date; echo Hello from the Kubernetes cluster          restartPolicy: Never

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


concurrencyPolicy этот параметр отвечает за одновременное выполнение заданий. Бывает трёх видов: Allow, Forbid, Replace.


Allow позволяет подам запускаться. Если за минуту Job не отработал, все равно будет создан ещё один. Одновременно могут выполняться несколько Jobов.


Например, если один Job выполняется 100 сек., а Cron выполняется раз в минуту, то запускается Job, выполняется 61 сек., в это время запускается ещё один Job. В итоге в кластере одновременно работают два Joba, которые выполняют одну и ту же работу. Возникает положительная обратная связь: чем больше Jobов запущено, тем больше нагрузка на кластер, тем медленнее они работают, тем дольше они работают и тем больше одновременных подов запускается в итоге всё застывает под бешеной нагрузкой.


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


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


jobTemplate это шаблон, из которого создаётся объект Job. Ну а всё остальное мы уже видели в манифесте Job.


Применим манифест.


kubectl apply -f cronjob.yaml

Посмотрим, что получилось:


k get cronjobs.batch

Увидим название CronJob, расписание, параметр запуска, количество активных Jobов и сколько времени они работают.



Раздел Suspend временная приостановка CronJob. В данном случае указано значение False. Это значит, что CronJob выполняется. Можно отредактировать манифест и поставить опцию True, и тогда он не будет выполняться, пока мы его снова не запустим.


Active сколько Jobов создано, Last Schedule когда последний раз исполнялся.


Теперь можно посмотреть статистику по Jobам и подам.


kubectl get job

Видно, что создан один Job.


kubectl get pod

Под создан, он выполнил полезную работу.



Что получается: CronJob создал Job, Job создал под, под отработал, завершился всё здорово.


Ещё раз посмотрим на CronJob:



Last Schedule был 19 секунд назад. Если посмотреть на Job, то увидим, что у нас появился следующий Job и следующий под.


Возникает вопрос: а что будет, если CronJob отработает хотя бы пару недель? Неужели у нас в кластере будет столько же Jobов и подов в статусе Completed, сколько в этой паре недель минут?


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


Снова откроем манифест и посмотрим, что было добавлено:


k get cronjobs.batch hello -o yaml



Появились опции failedJobHistorLimit со значением 1 и successfulJobHistoryLimit со значением 3. Они отвечают за количество Jobов, которые остаются одновременно в кластере. То есть CronJob не только создаёт новые Jobы, но и удаляет старые.


Когда только контроллер CronJob создавался, эти опции не были установлены по умолчанию и CronJob за собой ничего не удалял. Было много возмущений от пользователей, и тогда поставили дефолтные лимиты.


И на сладкое про startingDeadlineSeconds


В параметре startingDeadlineSeconds указывают количество секунд, на которое можно просрочить запуск Job. Если по каким-то причинам Job не создался и с момента, когда его надо было создать, прошло больше секунд, чем указано в этом параметре, то он и не будет создан. А если меньше, то хоть и с опозданием, Job будет создан.


Тут есть небольшая ловушка, если concurrencyPolicy разрешают одновременное создание Job, то при большом значении параметра startingDeadlineSeconds возможен одновременный запуск десятков пропущенных Job одновременно. Для уменьшения всплеска нагрузки в код Kubernetes захардкожены лимиты и запрещающие процедуры:


Если параметр startingDeadlineSeconds не указан в манифесте:
CronJob контроллер при создании Job смотрит на время последнего запуска значение LastscheduleTime в status: и считает, сколько времени прошло с последнего запуска. И если это время достаточно велико, а точнее за этот промежуток времени CronJob должен был отработать 100 раз или больше, но у нее этого не получилось:


  • например конкурент полиси стоит форбид, крон запускается раз в минуту, и один из запусков подзавис и работал 2 часа, то есть 120 минут;
  • или у нас один мастер в кластере, который упал на пару часов.

В этом случае происходит нечто странное: CronJob перестает работать, новые Jobы больше не создаются. Сообщение об этом приходит в Events, но хранится там недолго. Для восстановления работы приходится удалять CronJob и создавать его заново.


И еще более странное:
Если установлен параметр startingDeadlineSeconds, то поведение немного меняется. 100 пропущенных запусков должны уложиться в количество секунд, указанных в этом параметре. т. е. если в расписании стоит выполняться раз в минуту, а startingDeadlineSeconds меньше 6000 секунд, тогда CronJob будет работать всегда, но в этом случае при политике Allow возможен одновременный запуск множества Job.


И наконец, любопытный side-эффект:
Если установить опцию startingDeadlineSeconds равной нулю, Jobы вообще перестают создаваться.


Если вы используете опцию startingDeadlineSeconds, указывайте её значение меньше, чем интервал выполнения в расписании, но не ноль.
Применяйте политику Forbid. Например, если было пропущено 5 вызовов и наступило очередное время исполнения, то контроллер не будет 5 раз запускать пропущенные задачи, а создаст только один Job. Это логичное поведение.

Особенность работы CronJob


Цитата из документации Kubernetes:


A cron job creates a job object about once per execution time of its schedule. We say "about" because there are certain circumstances where two jobs might be created, or no job might be created. We attempt to make these rare, but do not completely prevent them. Therefore, jobs should be idempotent.


Вольный перевод на русский:


CronJob создаёт объект Job примерно один раз на каждое время исполнения по расписанию. Мы говорим примерно, потому что иногда бывают случаи, когда создаются два Jobа одновременно или ни одного. Мы делаем всё, чтобы сделать подобные случаи как можно более редкими, но полностью избежать этого не получается. Следовательно, Jobы должны быть идемпотенты.


Идемпотенты должны выполняться на одной и той же среде несколько раз и всегда возвращать одинаковый результат.


В общем, используйте CronJob на свой страх и риск.


В качестве альтернативы CronJob можно использовать под, в котором запущен самый обычный crond. Без выкрутасов. Старый добрый cron работает без проблем, и мы всегда знаем, что задачи будут выполнены один раз. Надо только побеспокоиться, чтобы внутри пода с кроном не выполнялись одновременно несколько задач.


Изучить продвинутые абстракции и попрактиковаться в работе с Kubernetes можно с помощью видеокурса Kubernetes База. В октябре 2020 мы обновили курс, подробности здесь.


Автор статьи: Сергей Бондарев практикующий архитектор Southbridge, Certified Kubernetes Administrator, один из разработчиков kubespray с правами на принятие pull request.

Подробнее..

Docker is deprecated и как теперь быть?

08.12.2020 14:22:23 | Автор: admin

Kubernetes объявил Docker устаревшим и планирует прекратить его использование примерно через год, в версии 1.22 или 1.23. Эта новость вызвала много вопросов и непонимания. В блоге Kubernetes появилось целых две статьи, разъясняющих смысл записи в Changelog (раз и два). Если все обобщить, то для разработчиков (те, которые Dev) ничего не меняется они все так же могут продолжать использовать docker build для сборки своих контейнеров, а вот для инженеров, ответственных за эксплуатацию кластера (Ops), пришла пора разобраться и освоить несколько новых инструментов.

Но для начала давайте вернемся в 2016 год, когда Kubernetes представил Container Runtime Interface (CRI). До версии Kubernetes 1.3 kubelet умел работать только с Docker, а в 1.3 анонсировали поддержку rkt Container Engine (этот проект уже прекратил свое существование). При этом код, отвечающий за взаимодействие с Docker и rkt, был глубоко и широко интегрирован в исходники kubelet. Процесс добавления поддержки rkt показал, что для добавления новых container runtime необходимо хорошо разбираться в устройстве и алгоритмах работы kubelet, а результат получился очень сложным для поддержки и развития.

Поэтому решили все упростить, и добавили новую абстракцию Container Runtime Interface. Весь код, реализующий высокоуровневый интерфейс работы с Docker, был убран из kubelet, а вместо него kubelet стал использовать CRI. Но тут возникла небольшая проблемка Docker не умеет в CRI. Чтобы ее решить, в Kubernetes написали программу-прокладку между kubelet и Docker. С одной стороны она принимает запросы от kubelet через CRI, а с другой транслирует их в docker-демон.

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

Возвращаемся назад, в конец 2020 года, и смотрим чего у нас новенького по сравнению с 2016 в плане развития Container Runtime Interface.

  • Проект rkt закрыт.

  • RedHat создали и всячески продвигают свой собственный движок для запуска контейнеров CRI-O.

  • Монолитный Docker разделили на containerd, dockerd и docker-cli.

В итоге у нас сейчас есть два движка для запуска контейнеров, которые умеют в CRI это containerd и cri-o. Естественно, все эти события произошли не прямо в 2020 году, но массовый интерес к ним возник именно после объявления Kubernetes о прекращении поддержки Docker.

Сборка

Давайте же разберемся, чем нам это грозит. Начнем с наиболее частого вопроса: Как же собирать образ контейнера с помощью containerd или cri-o?

И ответ очень простой: Никак. Потому что containerd и cri-o предназначены только для запуска контейнеров из готовых образов. Плюс они умеют еще несколько вещей, необходимых для эксплуатации:

  • загрузка образов из интернета;

  • просмотр информации о запущенных подах и контейнерах;

  • удаление образов, подов и контейнеров;

  • подключение внутрь контейнера (запуск внутри контейнера процесса с bash).

Но они не умеют ни собирать новый образ, ни пушить его в какой-либо registry. И как теперь быть ?

Начнем с того, что в 2015 году Docker и остальные лидеры индустрии контейнеризации основали Open Container Initiative (OCI) и опубликовали в ее рамках спецификацию формата хранения образов контейнеров. Эту спецификацию поддерживают Docker, containerd и cri-o, так что образ, собранный с помощью docker build на машине разработчика, должен без проблем запустится с помощью containerd или cri-o. И так и происходит в подавляющем большинстве случаев, но время от времени попадаются нюансы реализации. Эти нюансы, конечно, исправляются, но иногда не так быстро, как хотелось бы.

Если вы за стабильность и минимальные изменения запускайте новые кластера с containerd в качестве движка контейнеризации. А на машинах разработчиков и CI runners сборки смело оставляйте Docker.

Если вы выбрали безопасность из коробки, кровавый enterpise и платную поддержку, то наверняка используете Openshift (платформа на базе Kubernetes), в котором для запуска контейнеров применяется cri-o. А для сборки образов RedHat создала отдельную утилиту, которую назвала buildah. В отличие от Docker, этой утилите для сборки образов не нужен запущенный движок контейнеризации.

Из альтернатив можно еще посоветовать kaniko утилиту для сборки образов от Google. Она поставляется в виде образа контейнера, так что для запуска ей нужен движок контейнеризации, но для сборки уже нет.

Эксплуатация

С разработкой разобрались, теперь пора перейти к самому интересному: что делать инженеру, который зашел на узел, чтобы разобраться, почему статус узла стал NotReady, набрал привычную команду docker ps, а в ответ получил docker: command not found.

Фундаментальная проблема тут в том, что Docker изначально ориентировался на взаимодействие с человеком, и к последним версиям у него появился очень классный и удобный консольный интерфейс, а вот компоненты, реализующие CRI они by design ориентированы на взаимодействие с программой-оркестратором, а не с человеком.

Но внезапно оказалось, что человек тоже хочет взаимодействовать с CRI, и для этого была написана отдельная утилита crictl, которая представляет собой CLI-консоль к CRI-движку. То есть одна и та же утилита crictl позволяет посмотреть список контейнеров запущенных с помощью containerd или cri-o.

Очень часто встречается информация, что можно просто заменить docker на crictl и вместо docker ps набирать crictl ps. Сказка заканчивается, когда вы попробуете запустить контейнер с помощью crictl run , и внезапно оказывается, что сначала надо создать манифест для PodSandbox, а уже потом написать манифест, описывающий контейнер, который будет запущен в этом поде.

С другой стороны надо помнить, что CRI был придуман командой разработки Kubernetes и для Kubernetes, и поэтому, на мой взгляд, правильнее было бы его назвать Pod Runtime Interface.

И crictl оптимизирован для работы с контейнерами, созданными kubelet. Например, в выводе не показываются служебные PODSandbox контейнеры, а имена контейнеров показываются в более понятном виде, чем в Docker. Да и возможности по работе с CLI постоянно улучшаются.

Еще одна вещь, с которой часто возникают проблемы понимания. Многие путают docker (бинарник из пакета docker-cli) и dockerd (демон, управляющий контейнерами). Пытаются сделать crictl image save/load и внезапно оказывается, что в crictl таких команд нет. А на созданный issue авторы устало отвечают, что crictl консоль для CRI runtime, который должен только запускать контейнеры, ну еще образ скачать из registry. А про манипуляции с образами в спецификации ничего нет.

Но выход есть! Для манипуляции с образами контейнеров можно воспользоваться дополнительной утилитой skopeo, если вы используете cri-o, или утилитой ctr из комплекта containerd.

И в завершение дам ответ на идею, которая наверняка возникнет после прочтения этой статьи:

Подождите, но ведь есть containerd, с которым умеет работать kubelet и dockerd! Давайте поставим Docker (три пакета docker-cli, docker, containerd), kubelet настроим на работу с containerd напрямую, и у нас при этом останется старая добрая команда docker.

Вот только docker ps не покажет контейнеров, запущенных kubelet через CRI. Произойдет это из-за того, что containerd умеет работать в многопользовательском режиме, и docker с kubelet запускают контейнеры в различных containerd namespaces moby и k8s.io соответственно (не надо путать их с kubernetes namespaces). Посмотреть на контейнеры в разных неймспейсах можно с помощью утилиты ctr -n <ns_name> container ls.

Подробнее..

Kubernetes 1.20 и что же слома...починили на этот раз?

09.12.2020 16:05:25 | Автор: admin
Release Logo c сайта kubernetes.ioRelease Logo c сайта kubernetes.io

Поздравляем с выходом версии 1.20. Третий релиз в 2020 году, в котором 11 фич объявили stable, 15 перевели в beta и добавили 16 новых в alpha-стадии.

Судя по Release Logo, котики уже захватили мир и даже не скрывают этого. Давайте посмотрим, какие блюдца с молоком они заботливо положили нам под ноги.


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

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

В описании контейнеров в манифесте подов есть специальный раздел, описывающий каким образом можно узнать, что приложение живое и готово обрабатывать входящие запросы. С помощью HTTP/TCP-запроса или запуска (exec probe) какого-либо процесса внутри контейнера (например, rabbitmqctl status) и анализа кода завершения.

Суть проблемы в следующем:

В манифесте есть поле timeoutSeconds:, которое задает максимальное время ожидания ответа на запрос или время выполнения процесса внутри контейнера. Ошибка была в том, что при выполнении exec probe этот параметр не учитывался и процесс внутри контейнера мог работать бесконечно долгое время, пока сам не завершится.

Ошибку исправили, и в версии 1.20 все стало прекрасно. Но не для всех.

Если вы используете exec probe и указывали timeoutSeconds: в своих манифестах самое время проверить, укладывается ли время выполнения вашей пробы в таймаут.

А вот если вы не указывали timeoutSeconds:, то у меня для вас не очень хорошая новость. Значение по умолчанию для этого параметра 1 секунда, если ваша проба выполняется дольше, то успешной она явно не будет.

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

И к другим новостям:

Если вы пользовались alpha feature API Priority and Fairness (APF) в своем мульти-мастер кластере 1.19 выключайте ее перед обновлением. В 1.20 она переведена в статус beta, но в процессе перевода отломали совместимость с alpha. После обновления она будет включена по умолчанию.

Мастеров больше не будет! Метка node-role.kubernetes.io/master и taint node-role.kubernetes.io/master:NoSchedule, которые по умолчанию ставил kubeadm на узлы с запущенными компонентами control-plane, объявлены deprecated и скоро будут убраны. Вместо них будут использоваться метка node-role.kubernetes.io/control-plane и taint node-role.kubernetes.io/control-plane:NoSchedule.

В kubeadm усилили проверки входящих параметров serviceSubnet и podSubnet.

Сеть для сервисов теперь имеет ограничение на размер и не должна аллоцировать более 20 бит. В переводе на человеческий это означает, что размер сети для IPv4 должен быть /12 или меньше (/13, /14 и т.п.), а для IPv6 /108, но лучше будет использовать /112.

Сеть для подов теперь проверяется на совместимость с параметром --node-cidr-mask-size, размер сетей, выделяемых на узел должен быть меньше, чем сеть для подов.

Из API сервера убрали возможность беспарольного входа. Теперь не получится восстановить потерянный доступ к кластеру, просто зайдя на мастер и добавив в ключи запуска API сервера ключ --insecure-port 8080

IPv4/IPv6 dual stack был немножко переписан в части реализации сервисов. Один сервис теперь может иметь как IPv4, так и IPv6 адрес (при включении "IPv6DualStack" feature gate). Но это привело к несовместимым изменениям в API. Изменился манифест Kind: Service вместо одного поля ipFamily теперь будет целых три: ipFamilyPolicy, ipFamilies, clusterIPs. Пишут, что большинству пользователей ничего не надо будет делать, сервисы останутся только IPv4, пока не включен соответствующий feature gate. TODO: Посмотреть, что там с Headless Service, которым в манифесте указываем clusterIP: None

Команда для работы с сертификатам (проверки, генерации или продления kubeadm alpha certs переименована в kubeadm certs. Старый вариант вызова еще работает, но в следующих релизах его уберут.

Анонсировали фичу GracefulNodeShutdown при ее включении kubelet будет самостоятельно завершать все запущенные поды, при выключении узла. Соблюдая все обычные gracefulShutdown таймауты и вызывая preStop хуки.

Команда kubectl debug повышена до беты, и в ее описании появилось много различных возможностей. Запуск дополнительных эфемерных дебаг контейнеров в поде, создание копий пода и замена команды запуска. Единственная тонкость новое название команды конфликтует с плагином debug, и теперь вместо плагина будет выполнятся встроенная команда. Если вы хотите продолжать пользоваться плагином, то его придется переименовать.

В процессе починки CVE-2020-8559 немножко усилили безопасность, что может привести к поломке работы бэкендов расширения API, таких как metrics-server или prometheus adapter.

Выпущен новый CronJob controller v2. Видимо, чинить баги старого контроллера больше не могут, и решили выпустить новый, с новыми багами. Пока в стадии альфа, так что включать его надо с помощью специального feature gate.

Подробнее..

Микросервисы на монолите

15.12.2020 12:20:26 | Автор: admin

Всем привет!

Скажу сразу, эта статья не про очередное переписывание монолита на микросервисы, а о применении микросервисных практик в рамках существующего проекта с использованием интересных, как мне кажется, подходов. Наверное, уже нет смысла объяснять, почему многие проекты активно используют микросервисную архитектуру. Сегодня в IT возможности таких инструментов как Docker, Kubernetes, Service Mesh и прочих сильно меняют наше представление об архитектуре современного приложения, вынуждая пересматривать подходы и переписывать целые проекты на микросервисы. Но так ли это необходимо для всех частей проекта?

В нашем проекте есть несколько систем, которые писались в те времена, когда преимущества микросервисного подхода были не столь очевидны, а инструментов, позволяющих использовать такой подход, было очень мало, и переписывать системы полностью просто нецелесообразно. Для адаптации к новой архитектуре мы решили в части задач использовать асинхронный подход, а также перейти к хранению части данных на общих сервисах. Само приложение при этом осталось на Django (API для SPA). При переходе на k8s деплой приложения был разбит на несколько команд: HTTP-часть (API), Celery-воркеры и RabbitMQ-консьюмеры. Причем было именно три развёртывания, то есть все воркеры, как и консьюмеры, крутились в одном контейнере. Быстрое и простое решение. Но этого оказалось недостаточно, так как это решение не обеспечивало нужный уровень надежности.

Начнем с RabbitMQ-консьюмеров. Основная проблема была в том, что внутри контейнера стартовал воркер, который запускал много потоков на каждый консьюмер, и пока их было пару штук, всё было хорошо, но сейчас их уже десятки. Решение нашли простое: каждый консьюмер вывели в отдельную команду manage.py и деплоим отдельно. Таким образом, у нас несколько десятков k8s-развёртываний на одном образе, с разными параметрами запуска. Ресурсы мы тоже выставляем для каждого консьюмера отдельно, и реальное потребление у консьюмеров достаточно невысокое. В результате для одного репозитория у нас десятки реальных отдельных сервисов в k8s, которые можно масштабировать, и при этом разработчикам намного удобнее работать только с одним репозиторием. То же самое и для развёртываний Сelery.

Если решение с воркерами достаточно простое и очевидное, то с HTTP-частью было немного сложнее. В нашей системе внешний межсервисный API и внутренний для фронтенда были в одном развёртывании с большим лимитом ресурсов и большим количеством инстансов.

Вроде бы рабочее решение? Да, но ДомКлик большой сложный механизм с сотнями сервисов, и иногда выход одного стороннего (вне конкретной системы) сервиса, который используется в одном единственном API-методе, может привести к пробке на сервере uwsgi. К чему это приводит, надеюсь, объяснять не нужно, всё встает или тормозит. В такие моменты должен приходить Кэп и говорить что-то вроде: Нужно было делать отдельные микросервисы и тогда упал бы только тот, что связан с отказавшим внешним сервисом. Но у нас-то монолит.

В Kubernetes есть такой сервис, как Ingress Controller, его основная задача распределять нагрузку между репликами. Но он, по сути, nginx, а значит Ingress Controller можно использовать для того, чтобы роутить запросы на разные сервисы.

Примерно вот так выглядит схема:

Проанализировав наш API, мы разбили его на несколько групп:

  • Внешний API (методы для других сервисов).

  • Внутренний API (методы для фронтенда).

  • Некритичные API-методы, которые зависимы от внешних сервисов, но не влияют на работу системы (статистика, счетчики и т. п.)

Таким образом можно легко создавать новые сервисы. Управлять ресурсами для каждого из них, масштабировать те методы, которые несут большую нагрузку, и получать другие преимущества. Если один из методов перестанет отвечать из-за каких-то внешних причин, то его можно изолировать в отдельное развёртывание и снизить нагрузку на другие части системы.

В конечном итоге наша система, имея один репозиторий и Django под капотом, раздроблена благодаря Kubernetes на 42 сервиса, 5 из которых делят HTTP-трафик, а остальные 37 консьюмеры и Celery-задачи. И в таком виде она может быть актуальна еще пару лет, несмотря на использование относительно старого стека технологий.

Подробнее..

Перевод Vanilla All the Way. Ванильное облачное решение на K8s

29.01.2021 16:08:57 | Автор: admin
Публикуем перевод статьи о Vanilla Stack новой облачной open-source технологии на основе Kubernetes.

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



Кратко о Vanilla Stack


Vanilla Stack можно определить как кластер Kubernetes с большим количеством open-source компонентов.

Среди этих решений:

  • Rook управляет распределенным хранилищем (файловая система, блок, объект),
  • OpenStack предоставляет инфраструктуру как сервис (IaaS),
  • Cloud Foundry предоставляет платформу как сервис (PaaS).


Следующая схема из официальной документации иллюстрирует глобальную архитектуру стека.



Примечание: Все решения, которые поставляются с Vanilla Stack, (Rook, Cloud Foundry, OpenStack и другие инструменты), работают как поды в базовом кластере Kubernetes.

Как мы видим из схемы выше, юзеры могут:

  • запускать приложения в базовом кластере Kubernetes через создание деплойментов, сервисов, Helm чартов и т. д.;
  • использовать Cloud Foundry для запуска приложения из кода (с помощью простого cf push);
  • использовать OpenStack для создания новых инфраструктур (виртуальных машин, программно-определяемых сетей и т. д.).


Примечание: Требования к оборудованию зависят от компонентов, которые будут установлены в стеке.

Следующая схема более детальная и предоставляет список всех компонентов, организованных по категориям, которые могут поставляться с Vanilla Stack:



Схема довольно точная, не правда ли?
Некоторые компоненты установлены по умолчанию, другие можно выбрать во время установки.

В этой статье мы покажем основные этапы процесса установки. Для упрощения задачи мы установим Vanilla Stack на шести виртуальных машинах, подготовленных у хостера: три будут выступать в качестве главных узлов (master) Kubernetes, а остальные других в качестве рабочих (workers).

Примечание: Облачные провайдеры следует выбирать в зависимости от компонентов, которые мы хотим установить, так как некоторые из них могут не соответствовать требованиям установки. Например, чтобы установить Rook, нам нужен поставщик инфраструктуры, который предоставляет block storage.

Запускаем установку


Есть два способа установить Vanilla Stack, они описаны в разделе загрузки. Мы будем использовать установку Docker, которую можно запустить с помощью следующей команды:

$ docker run \  --name VanillaStack-installer -d \  -p 8080:8080 \  harbor.vanillastack.io/vanillastack/installer:latest

Веб-интерфейс установщика доступен через порт 8080. Установку можно будет выполнить в десять несложных этапов, о которых мы расскажем далее.

Требования





Первый шаг, Start, определяет требования к оборудованию и программному обеспечению для машин, на которых будет установлен стек.
В этой статье мы будем использовать шесть виртуальных машин. Каждая из них работает на Ubuntu 20.04 и имеет 4 ГБ оперативной памяти / 2 процессора.

Как мы писали ранее, три виртуальные машины будут выступать в качестве главных узлов Kubernetes, а остальные будут рабочими.

# IP addresses of the master nodesmaster1 - 159.65.92.123master2 - 178.62.32.212master3 - 178.62.40.225# IP addresses of the worker nodesworker1 - 178.62.41.87worker2 - 178.62.27.97worker3 - 178.62.67.29

Кроме того, нам необходимо настроить домен (и пару поддоменов) для доступа к кластеру. Мы вернемся к этому шагу позже при настройке балансировщика нагрузки.

Условия и положения


Принятие условий использования (Terms) это второй шаг. Обязательно ознакомьтесь с информацией о лицензии.



Общие настройки


Далее идет шаг General Settings. Нужно указать тип установки, который нам подходит: количество главных и рабочих узлов и начальную нагрузку.





В нашем примере мы выбираем HA installation с тремя главными узлами, три оставшихся узла действуют как рабочие. Rook выбран по умолчанию для установки хранилища. Пока мы не используем OpenStack или Cloud Foundry. Это может быть темой для другой статьи.

Доступ к виртуальной машине через SSH-ключ


Раздел Public Key предоставляет открытый ключ RSA, его необходимо скопировать на каждой из виртуальных машин. Этот шаг необходим, чтобы разрешить установочной машине (на которой запущен установщик) запускать Ansible playbooks для настройки каждой VM.

Примечание: Ansible это система управления конфигурациями. Он запускает команды на машинах только через SSH-соединение. Он не требует никакого агента, в отличие от других инструментов настройки Chef и Puppet.



Инструкция по копированию ключа указана в установщике:

  • сохраните ключ в файле с именем key.pub.
  • скопируйте ключ на целевой компьютер, используя ssh-copy-id:
  • повторите это со всеми узлами.


Для Windows следуйте инструкциям из этой статьи, начиная с Копировать SSH-ключ на удаленное устройство Linux.

Определите информацию об узлах


На этапе Nodes нам нужно определить IP-адреса и пользователя каждого из главного и рабочего узлов.




Также нам нужно определить узлы, которые запустят Rook Pods. Следуя требованиям, мы используем все три рабочих узла.



Проверка конфигурации узла


На шаге Node-Check мы проверяем, что все узлы соответствуют требованиям.



Перед кликом на кнопку Validates Nodes, нужно проскроллить и убедиться, что мы выполняем правила Rook: к каждому узлу должно быть подключено необработанное блочное устройство (raw block device).



Через команду lsblk -f мы можем проверить, что все настроено правильно.



Нажав кнопку Validate Nodes в установщике, мы можем убедиться, что все узлы настроены корректно.



Настройки кластера


Первое, что нужно настроить на этапе Cluster-Settings, это Pod CIDR и Service CIDR (то есть диапазоны IP, которые используются для предоставления IP-адресов подов и виртуальных IP-адресов сервисов). В этом примере мы используем значения по умолчанию.



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

Из-за особенностей используемого облачного провайдера использовать один и тот же балансировщик нагрузки для перенаправления трафика на другой набор узлов не получится.

Поэтому мы создадим два балансировщика нагрузки: перед главными узлами
и перед рабочими.

Балансировщик нагрузки перед главными узлами


Первый балансировщик нагрузки необходим для доступа к серверу API кластера. Он должен быть настроен следующим образом:

  • перенаправить TCP-трафик с порта 6443 на тот же порт на главных узлах;
  • регулярно проверять работу мастер-узлов, отправляя TCP-запросы на порт 6443.


В этом примере балансировщик нагрузки получает IP-адрес 159.65.211.35.

Балансировщик нагрузки перед рабочими узлами


Второй балансировщик нагрузки необходим для предоставления доступа к ingress-контроллеру кластера (компонент, дающий доступ к приложениям в кластере). Его настройки:

  • перенаправить HTTP-трафик ( порт 80) на порт 30080 рабочих узлов;
  • перенаправить HTTPS-трафик (порт 443) на порт 30443 рабочих узлов;
  • регулярно проверять работоспособность рабочих узлов, отправляющих TCP-запросы на порт 30080.


В этом примере балансировщик нагрузки получает IP 46.101.64.165.

Настройка DNS entries


IP-адрес балансировщика нагрузки должен быть связан с доменным именем кластера. В этой статье мы используем vanilla.techwhale.io. Дополнительные поддомены необходимы для доступа к серверу API и приложениям, предоставляемым через ingress-контроллер.



На этом этапе мы можем предоставить IP-адрес только одного балансировщика нагрузки, но в следующих релизах установщика будет больше возможностей.



Настройка Let's Encrypt


Let's Encrypt широко используемый бесплатный центр сертификатов. Он позволяет нам получать и автоматически обновлять сертификаты HTTPS. В Kubernetes Let's Encrypt часто используется через Cert Manager, компонент, установленный по умолчанию в Vanilla Stack. Cert Manager может получить сертификат от Lets Encrypt (а также из нескольких других источников) и сделать его доступным для приложения через Kubernetes Secret.



Rook Configuration


Следующие шаги позволяют выбрать компоненты Rook, которые будут установлены (в данном кейсе панель управления Rook и мониторинг), а также количество повторений одного фрагмента данных (replica level).



Установка дополнительных инструментов


В разделе Additional Tools можно указать, какие компоненты будут установлены в кластере.




Перечисленные здесь инструменты довольно распространенные. Они делятся на разные категории:

  • мониторинг (Prometheus, Jeager);
  • логирование (fluentd, Elasticsearch, Grafana);
  • реестр контейнеров и репозиторий Helm чартов (Harbour);
  • ingress контроллер (nginx).


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

Подписка


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



Поддержка предоставляется сервисом Cloudical. Он поддерживает Vanilla Stack и предоставляет Vanilla Cloud, новый управляемый вариант стека.

Проверка всех шагов


Здесь мы можем просмотреть и изменить (при необходимости) параметры конфигурации.



Прежде чем перейти к следующему шагу и установить весь стек, нам нужно убедиться, что
/etc/apt/sources.list правильно настроен на каждом узле и направляется на официальные репозитории.

Примечание: Этот шаг необходим, например, если Ansible недоступен из репозиториев, предоставленных по умолчанию.

Самый простой способ исправить это заменить содержимое /etc/apt/sources.list следующими инструкциями:



Установка


Теперь все готовы и мы можем запустить установку под чашечку чая.



В журналах установки мы увидим Ansible playbooks, примененные к каждой виртуальной машине, чтобы:

  • установить все необходимые пакеты;
  • настроить кластер Kubernetes (три главных узла / три рабочих);
  • установить Rook и все инструменты, выбранные выше.


Установка всего стека не займет много времени.



Примечание: При установке стека я столкнулся с небольшой проблемой, обусловленной особенностями используемого облачного провайдера: балансировщик нагрузки перенаправляет трафик на внутренний (частный) IP-адрес узла вместо общедоступных. Ingress контроллер отображается на внешнем (общедоступном) IP-адресе узлов, поэтому рабочие узлы помечаются как неисправные, что неправильно.

Доступ к кластеру


Файл kubeconfig можно извлечь из /etc/kubernetes/admin.conf file.

# Get kubeconfig file$ ssh root@master1 cat /etc/kubernetes/admin.conf > vanilla.cfg# Configure local kubectl$ export KUBECONFIG=$PWD/vanilla.cfg


Как обычно, мы начинаем с перечисления узлов нашего кластера:

$ kubectl get noNAME      STATUS   ROLES    AGE   VERSIONmaster1   Ready    master   38m   v1.19.6master2   Ready    master   37m   v1.19.6master3   Ready    master   37m   v1.19.6worker1   Ready    worker   37m   v1.19.6worker2   Ready    worker   37m   v1.19.6worker3   Ready    worker   37m   v1.19.6


Кроме того, мы можем проверить все поды, работающие в кластере, а также сервисы, предоставляющие их.




Похоже, всё работает! Следующим шагом может быть тестовый запуск, например, stateful-приложения, которое:

  • использует блочное хранилище, предоставляемое кластером Ceph, созданным через Rook;
  • пробрасывается через ingress-контроллер nginx;
  • использует сертификат TLS от Let's Encrypt CA, автоматически созданный с помощью Cert Manager.


Итоги


Настройка Vanilla Stack прошла быстро, установщик Docker прост и понятен. Вскоре должны быть добавлены дополнительные параметры для большего удобства.

Если вы не хотите устанавливать Vanilla Stack, то можете использовать тот же самый стек прямо из Vanilla Cloud. Это новое облачное предложение запущено в декабре 2020 года, а первая бета-версия уже доступна.

Я рекомендую поближе познакомиться с этим стеком, так как это отличный способ узнать больше популярных open-source проектов.
Подробнее..

Перевод Проблемные поды эскалация привилегий подов в Kubernetes

01.02.2021 12:14:39 | Автор: admin


Что будет, если разрешить лишнее при создании подов в Kubernetes? Зависит от того, какие неймспейсы хоста и контексты безопасности разрешены. Здесь мы поговорим о восьми небезопасных конфигурациях подов и методах эскалации привилегий. Эта статья и созданный для нее репозиторий помогут пентестерам и администраторам разобраться в распространенных ошибках конфигурации.


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


Пентестеры получат представление о том, как показать последствия слишком либеральной политики безопасности подов. В репозитории вы найдете для этого готовые манифесты и инструкции.


Краткий обзор


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


Слишком большие привилегии это вообще частая проблема Kubernetes и других технологий DevOps. В нашем исследовании Проблемные поды мы показали, что при использовании Kubernetes в инфраструктуре нужно выяснить у разработчиков, как они настраивают и защищают эту среду.


Защита подов: что может натворить один атрибут?


Любой эксперт по безопасности Kubernetes скажет, что при подготовке подов нужно использовать принцип наименьших привилегий. Но как выполнить детальную настройку и оценить риск каждого атрибута?


Администратор Kubernetes может реализовать принцип наименьших привилегий с помощью контроллеров допуска. Это может быть, допустим, встроенный контроллер PodSecurityPolicy или популярный сторонний контроллер OPA Gatekeeper. Контроллеры допуска запрещают поду доступ в кластер, если у него больше разрешений, чем предписано политикой.


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


Пентестеры часто получают доступ к созданию подов на кластере, где не действует никакая политика. Это я называю легким уровнем сложности. Используем этот манифест от Рори МакКьюна (Rory McCune) (@raesene), эту команду от Даффи Кули (Duffie Cooley) (@mauilion) или плагин node-shell krew и вот уже можно интерактивно выполнять код в привилегированном режиме на хосте. Проще уже не придумаешь.


А что если создавать поды можно только с hostNetwork, hostPID, hostIPC, hostPath или privileged? Что будет в каждом случае? Сейчас узнаем.



Проблемные поды: атрибуты и их худшие последствия


Я расположил проблемные поды по мере убывания рисков. Атаки, которым подвержены любые поды Kubernetes (например, проверка доступа пода к сервису метаданных облачного провайдера или поиск неправильно настроенного Kubernetes RBAC) описаны в разделе Проблемный под 8: все запрещено.


Проблемные поды:


Поды


Проблемный под 1: все разрешено


Проблемный под 2: Privileged и hostPid


Проблемный под 3: только Privileged


Проблемный под 4: только hostPath


Проблемный под 5: только hostPid


Проблемный под 6: только hostNetwork


Проблемный под 7: только hostIPC


Проблемный под 8: все запрещено.


Проблемный под 1: все разрешено



Что произойдет в худшем случае?


Много всего.


Как?


Созданный под монтирует файловую систему хоста к поду. Проще всего будет создать под в ноде control-plane с помощью селектора nodeName в манифесте. Потом можно выполнить в поде exec и сделать chroot для каталога, где примонтирована файловая система хоста. Теперь у нас есть права root в ноде, где запущен наш под.


  • Читаем секреты из etcd. Если мы запускаем под на ноде control-plane с помощью селектора nodeName в спецификации пода, мы легко получаем доступ к базе данных etcd, где хранится вся конфигурация кластера, включая секреты.
  • Ищем токены привилегированных учеток сервисов. Даже если удалось запустить под только на рабочей ноде, мы все равно можем получить доступ к секретам всех подов на этой ноде. В продакшен-кластере даже на рабочей ноде всегда есть хотя бы один под с токеном, который привязан к учетке сервиса, связанной с clusterrolebinding, так что мы можем, например, создавать поды или просматривать секреты во всех неймспейсах.

Больше схем эскалации привилегий см. в файле README (ниже) и в разделе Проблемный под 4: hostPath.


Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/everything-allowed


Дополнительные материалы



Проблемный под 2: privileged и hostpid



Что произойдет в худшем случае?


Много всего.


Как?


Единственное отличие от предыдущего варианта способ получения root-доступа к хосту. Там мы делали chroot для файловой системы хоста, а здесь используем nsenter, чтобы получить права root для ноды, где запущен наш под.


Почему это работает?


  • Privileged. Контекст безопасности privileged: true на уровне контейнера ломает почти любые преграды, которые по идее должен давать контейнер для защиты от атак. Остается разве что неймспейс PID. Без hostPID nsenter сможет входить только в неймспейсы процесса, который запущен в контейнере. Больше примеров действий, которые можно совершить с privileged: true, см. в следующем разделе Проблемный под 3: только Privileged.


  • Privileged + hostPID. Если одновременно задать hostPID: true и privileged: true, поду будут доступны все процессы на хосте, и мы сможем войти в систему init (PID 1) на хосте. А там уже можно запустить shell на ноде.



С root-доступом на хосте у нас есть те же пути эскалации привилегий, о которых мы говорили в разделе Проблемный под 1: все разрешено.


Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/priv-and-hostpid


Дополнительные материалы



Проблемный под 3: только privileged



Что произойдет в худшем случае?


Много всего.


Как?


С privileged: true у нас два пути:


  • Примонтировать файловую систему хоста. В привилегированном режиме /dev на хосте будет доступен нашему поду. Мы можем примонтировать к нашему поду диск с файловой системой хоста командой mount. По моему опыту, полной свободы действий нам это не даст. Некоторые файлы и пути эскалации привилегий недоступны из привилегированного пода, если только мы не эскалируем его до полноценной shell. Зато можно легко примонтировать диск и посмотреть, что мы там увидим.
  • Использовать уязвимость cgroup в user mode с помощью специальных программ. Лучше всего получить интерактивный root-доступ к ноде, но тут придется повозиться. Можем использовать прототип эксплойта Феликса Уилхелма (Felix Wilhelm) undock.sh, чтобы выполнять по команде за раз, или вариант, который Брэндон Эдвадрс (Brandon Edwards) и Ник Фриман (Nick Freeman) предложили в своем выступлении Краткое руководство по выходу из контейнера. В этом варианте мы заставляем хост подключиться к listener на поде для эскалации до интерактивного root-доступа на хосте. Еще один вариант взять модуль Metasploit Docker Privileged Container Escape, где используется тот же эксплойт для апгрейда shell на контейнере до shell на хосте.

При любом варианте пути эскалации привилегий в Kubernetes во многом похожи на те, которые мы описали для проблемного пода 1, где все разрешено.


Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/priv


Дополнительные материалы



Проблемный под 4: только hostpath



Что произойдет в худшем случае?


Много всего.


Как?


Допустим, у нас нет доступа к хостовым process- и network- неймспейсам, но администраторы не ограничили возможности монтирования. Тогда можно примонтировать всю файловую систему хоста к поду и получить к ней доступ на чтение и запись. Теперь можем использовать те же пути эскалации привилегий, что и раньше. Этих путей столько, что Иен Колдуотер (Ian Coldwater) и Даффи Кули (Duffie Cooley) посвятили им целое выступление на Black Hat 2019: Неизведанный путь: атака на Kubernetes с параметрами по умолчанию.


Вот несколько путей эскалации привилегий, которые можно использовать, когда у нас есть доступ к файловой системе ноды Kubernetes:


  • Ищем файлы kubeconfig в файловой системе хоста. Если повезет, можно даже найти конфигурацию cluster-admin с полным доступом вообще ко всему.
  • Ищем токены во всех подах на ноде. Возьмем что-то типа kubectl auth can-i --list или access-matrix и посмотрим, есть ли в подах токены, которые дадут нам больше разрешений. Ищем токены, с которыми можно получать секреты или создавать поды, деплойменты и т. д. в kube-system или создавать clusterrolebinding.
  • Добавляем свой SSH-ключ. Если у нас есть сетевой доступ к SSH-соединению с нодой, добавим к ноде свой публичный ключ и подключимся к ней по SSH, чтобы получить полный интерактивный доступ.
  • Ломаем хэшированные пароли. Взламываем хэши в /etc/shadow и пытаемся использовать их для доступа к другим нодам.

Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/hostpath


Дополнительные материалы



Проблемный под 5: только hostpid



Что произойдет в худшем случае?


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


Как?


С одним только hostPID не так просто получить root-доступ к ноде, но кое-что натворить можно.


  • Смотрим процессы на хосте. Если запустим ps в поде с hostPID: true, увидим все процессы на хосте, включая процессы на каждом поде.
  • Ищем пароли, токены, ключи и тому подобное.Если повезет, найдем что-нибудь для эскалации привилегий и доберемся до кластера, до сервисов, поддерживаемых кластером, или до сервисов, которые общаются с приложениями на кластере. Придется повозиться, но можно найти токен для учетки сервиса в Kubernetes или что-то подобное, чтобы раздобыть доступ к другим пространствам имен и в итоге получить привилегии cluster-admin.
  • Завершаем процессы. Можно завершить на ноде любой процесс, чтобы вызвать отказ в обслуживании (не советую пентестерам экспериментировать с этим пунктом).

Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/hostpid


Проблемный под 6: только hostnetwork



Что произойдет в худшем случае?


Риск компрометации кластера.


Как?


Если у нас есть только hostNetwork: true, напрямую выполнить код в привилегированном режиме на хосте мы не можем, но если сильно повезет, шанс добраться до cluster-admin все же есть. Путей эскалации может быть три:


  • Прослушиваем трафик. С tcpdump можно прослушивать незашифрованный трафик на любом интерфейсе хоста. Если звезды сложатся, нам попадутся токены учетки сервиса или другая конфиденциальная информация, которая передается по незашифрованным каналам.
  • Получаем доступ к сервисам, связанным с localhost. Мы можем получить доступ к сервисам, которые принимают трафик только через loopback интерфейс хоста или как-то еще ограничены сетевыми политиками. Оттуда можно найти какие-нибудь интересные пути эскалации привилегий.
  • Обходим сетевую политику. Если пространство имен ограничено сетевой политикой, можно задеплоить под с атрибутом hostNetwork: true, чтобы обойти эти ограничения. Суть в том, что мы привязываемся к сетевым интерфейсам хоста, а не подам.

Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/hostnetwork


Проблемный под 7: только hostipc



Что произойдет в худшем случае?


Можно получить доступ к данным, к которым обращаются поды, использующие пространство имен хоста IPC.


Как?


Если какой-то процесс на хосте или процессы в поде используют механизмы межпроцессного взаимодействия (IPC) хоста (общая память, массивы семафоров, очереди сообщений и т. д.), можно использовать эти механизмы для чтения/записи. Сначала ищем /dev/shm, общий для всех подов с атрибутом hostIPC: true и хоста. Можно проверить и другие механизмы IPC с помощью ipcs.


  • Проверяем /dev/shm. Ищем файлы в общей памяти.
  • Проверяем существующие объекты IPC. С помощью /usr/bin/ipcs можно узнать, используются ли какие-нибудь объекты IPC.

Примеры использования


https://github.com/BishopFox/badPods/tree/main/manifests/hostipc


Проблемный под 8: все запрещено



Что произойдет в худшем случае?


Много всего.


Как?


Даже если все атрибуты запрещены, мы все равно можем найти множество путей атак, если можно создать под или хотя бы получить к нему доступ. Имея доступ к поду Kubernetes, обращаем внимание на следующее:


  • Доступные облачные метаданные. Если под размещен в облаке, пытаемся получить доступ к облачному сервису метаданных. Там можно найти учетные данные IAM, связанные с нодой, или хотя бы облачные учетные данные IAM, созданные специально для этого пода. В обоих случаях можно добраться до кластера, до облачной среды или и туда, и туда.
  • Учетки сервисов с лишними разрешениями. Если учетка сервиса по умолчанию в пространстве имен примонтирована к /var/run/secrets/kubernetes.io/serviceaccount/token в поде и позволяет слишком многое, с этим токеном можно эскалировать привилегии в кластере.
  • Компоненты Kubernetes с неправильной конфигурацией. Если у apiserver или kubelet'ов для anonymous-auth задано true и никакие сетевые политики нам не мешают, можно взаимодействовать с ними напрямую без аутентификации.
  • Эксплойты в ядре, движке контейнера или Kubernetes. Эксплойт в этих компонентах можно использовать для выхода из контейнера или доступа к кластеру Kubernetes без дополнительных разрешений.
  • Уязвимые сервисы. Если смотреть на сетевые сервисы кластера через под, скорее всего, мы увидим больше, чем с машины, где создали этот под. Можем поискать уязвимые сервисы и приложения, направляя трафик через под.

Примеры использования
https://github.com/BishopFox/badPods/tree/main/manifests/nothing-allowed


Дополнительные материалы


Защита Kubernetes KubeCon NA 2019 CTF
Kubernetes Goat
Атака на Kubernetes через Kubelet
Подробный обзор угроз для Kubernetes в реальном мире
Краткое руководство по выходу из контейнера
CVE-2020-8558 POC


Заключение


Если не считать проблемный под номер 8, где все и так запрещено, все пути эскалации привилегий в этом посте (и репозитории к нему) можно перекрыть политиками безопасности.


Администраторы Kubernetes могут использовать и другие методы глубокой защиты, чтобы снизить или полностью устранить риски некоторых путей атак, даже если у злоумышленника есть доступ к некоторым или ко всем неймспейсам и возможностям на хосте (отключить автоматическое монтирование токенов учеток сервисов или запретить подам запуск с правами root, указав MustRunAsNonRoot=trueи allowPrivilegeEscalation=false). Как это обычно и бывает с пентестами, для взлома может потребоваться больше или меньше усилий.


Админам часто приходится применять методы защиты без примеров последствий, к которым может привести та или иная рискованная конфигурация. Надеюсь, примеры в этом посте и манифестах в репозитории Bad Pods помогут вам применять принцип наименьших привилегий при создании подов Kubernetes в вашей компании.


От редакции: подробнее о безопасности в K8s можно узнать на интенсиве Kubernetes Мега. Интенсив пройдет в онлайн-формате с 17 по 19 мая 2021.
Подробнее..

Перевод Как предоставить доступ к кластеру Kubernetes с помощью клиентского сертификата простое руководство

03.02.2021 18:19:31 | Автор: admin

Источник

Предположим, мы создали кластер Kubernetes. И кто-то из команды разработчиков хочет развернуть и протестировать на нем новое приложение. Как нам предоставить ему доступ в кластер?

Команда Kubernetes aaS Mail.ru Cloud Solutions перевела простое руководство по предоставлению доступа к новому кластеру Kubernetes, включая настройку аутентификации и привязку ролей. Автор показывает процесс, используя клиентский сертификат x509.

Управление пользователями в Kubernetes

Для управления кластером Kubernetes и запущенными в нем приложениями обычно используют утилиту kubectl или веб-интерфейс. Под капотом эти инструменты вызывают API Server: HTTP Rest API, открывающий конечные точки управления кластером. Этот HTTP API хорошо документирован посмотрите сами.

После отправки запроса на сервер API он проходит сначала аутентификацию, а затем авторизацию. Аутентификация позволяет убедиться, что запрашивающий известен системе, авторизация что отправителю запроса разрешено выполнить конкретное действие.

Аутентификацию выполняют с помощью плагинов, есть плагины с разными механизмами:

  • сертификаты клиентов о них в этой статье;

  • Bearer tokens (персональные токены);

  • аутентифицирующий прокси;

  • базовая аутентификация HTTP.

В зависимости от механизма аутентификации плагин ищет информацию о пользователе в определенных местах. Например, для аутентификации по сертификату клиента идентификацию пользователя (идентификатор, имя, адрес электронной почты и так далее) указывают в поле Common Name (CN) сертификата. Информацию о группе, если она есть, добавляют в поле Organisation (O).

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

Некоторые соображения и допущения

  1. Кластер используют несколько команд или клиентов (подход с несколькими пользователями), так что нужно изолировать рабочую нагрузку для каждого клиента. Мы создадим пространство имен для команды разработчиков, в которую входит разработчик, которому надо дать доступ (пусть его зовут Дейв). Это пространство имен мы назовем development.

  2. Дейву предстоит развернуть стандартные ресурсы Kubernetes. Затем он получит право создавать, просматривать, обновлять, получать список и удалять ресурсы Deployment и Service. Дополнительные права можно предоставить при необходимости, но они ограничены пространством имен development.

  3. Скорее всего, членам команды Дейва потребуется такой же уровень доступа. Мы заведем группу dev и предоставим права на уровне группы.

  4. Дейву потребуется kubectl, а также openssl он сгенерирует закрытый ключ и запрос на вход с сертификатом.

Создание закрытого ключа и запроса на подпись сертификата (CSR)

Сначала Дейв генерирует закрытый ключ RSA и CSR. Закрытый ключ можно создать с помощью команды:

$ openssl genrsa -out dave.key 4096

С CSR немного сложнее, поскольку Дейву нужно убедиться, что он:

  1. использует свое имя в поле Common Name (CN) оно требуется для идентификации на сервере API;

  2. использует имя группы в поле Organisation (O) это имя нужно для идентификации группы на сервере API.

Ниже файл конфигурации, который Дейв использует для создания CSR:

[ req ]default_bits = 2048prompt = nodefault_md = sha256distinguished_name = dn[ dn ]CN = daveO = dev[ v3_ext ]authorityKeyIdentifier=keyid,issuer:alwaysbasicConstraints=CA:FALSEkeyUsage=keyEncipherment,dataEnciphermentextendedKeyUsage=serverAuth,clientAuth

Примечание: запись clientAuth в поле extendedKeyUsage нужна, поскольку сертификат будут использовать для идентификации клиента.

С помощью указанного файла конфигурации, сохраненного в csr.cnf, CSR можно создать одной командой:

$ openssl req -config ./csr.cnf -new -key dave.key -nodes -out dave.csr

Создав файл .csr, Дейв отправляет его администраторам, чтобы они подписали его с помощью центра сертификации кластера.

Подписание CSR

После подписания файла .csr выпускается сертификат. Он будет использоваться для аутентификации запросов, который Дейв отправит на сервер API.

Начнем с создания ресурса Kubernetes Certificate Signing Request.

Примечание мы могли создать управляемый кластер (например, в DigitalOcean, Google GKE, Microsoft Azure, Mail.ru Cloud Solutions или другой платформе) или собственный (допустим, kubeadm или kubespray). Процесс подписи везде устроен одинаково.

Мы используем следующую спецификацию и сохраняем ее в csr.yaml:

apiVersion: certificates.k8s.io/v1beta1kind: CertificateSigningRequestmetadata:name: mycsrspec:groups:- system:authenticatedrequest: ${BASE64_CSR}usages:- digital signature- key encipherment- server auth- client auth

Значение ключа request содержимое переменной окружения BASE64_CSR. Первый шаг получить кодированный в base64 файл .csr, созданный Дейвом. Затем использовать envsubst, чтобы заменить значения этой переменной перед созданием ресурса.

# Кодируем файл .csr в base64$ export BASE64_CSR=$(cat ./dave.csr | base64 | tr -d '\n')# Подставляем переменную env BASE64_CSR и создаем ресурс CertificateSigninRequest$ cat csr.yaml | envsubst | kubectl apply -f -

Проверяем статус созданного CSR мы видим, что он находится в состоянии ожидания:

# Проверяем статус созданного CSR$ kubectl get csrNAME        AGE   REQUESTOR            CONDITIONmycsr       9s    28b93...d73801ee46   Pending

Подтверждаем CSR с помощью команды:

$ kubectl certificate approve mycsr

Еще раз проверяем статус CSR теперь он одобрен:

$ kubectl get csrNAME        AGE   REQUESTOR            CONDITIONmycsr       9s    28b93...d73801ee46   Approved,Issued

Сертификат создан, теперь извлечем его из ресурса CSR, сохраним в файле с именем dave.crt и проверим, что внутри:

$ kubectl get csr mycsr -o jsonpath='{.status.certificate}' \| base64 --decode > dave.crt

Следующая команда openssl показывает: сертификат подписан центром сертификации кластера DigitalOcean (часть Issuer). Subject содержит dave в полях CN (CommonName) и O (Organisation), как указал Дейв при создании файла .csr:

$ openssl x509 -in ./dave.crt -noout -textCertificate:Data:    Version: 3 (0x2)    Serial Number:        48:29:cf:ae:d6:...:09:33:ef:14:58Signature Algorithm: sha256WithRSAEncryption    Issuer: O=DigitalOcean, CN=k8saas Cluster CA    Validity        Not Before: Jun  3 07:56:00 2019 GMT        Not After : Jun  2 07:56:00 2020 GMT    Subject: O=dev, CN=dave    Subject Public Key Info:        Public Key Algorithm: rsaEncryption            Public-Key: (4096 bit)            Modulus:...

Примечание в примере используем управляемый кластер Kubernetes, созданный в DigitalOcean мы видим это в Issuer кластера. На другой платформе будет похоже.

Создание пространства имен

Начинаем с создания пространства имен development благодаря этому ресурсы, которые развернут Дейв и его команда, будут изолированы от остальной рабочей нагрузки кластера.

Его можно создать с помощью простой команды:

$ kubectl create ns development

или с помощью файла dev-ns.yaml:

apiVersion: v1kind: Namespacemetadata:name: development

Применяем dev-ns.yaml с помощью команды:

$ kubectl apply -f dev-ns.yaml

Примечание рекомендую создать ресурс ResourceQuota и связать его с пространством имен. Это позволит ограничить объем CPU и ОЗУ, которые можно использовать в пространстве имен.

Настройка правил RBAC

С помощью сертификата Дейв может пройти аутентификацию на сервере API. Но пока у него нет прав, так что он не может делать многие вещи. Давайте дадим ему права создавать, получать список, обновлять, просматривать и удалять ресурсы Deployment и Service в пространстве имен dev.

Ресурсы, задействованные в управлении доступом к базе ролей Kubernetes (RBAC)Ресурсы, задействованные в управлении доступом к базе ролей Kubernetes (RBAC)

Коротко: роль (то же самое справедливо и для ClusterRole) содержит список правил. Каждое правило определяет действия, которые могут быть выполнены (например: list, get, watch) со списком ресурсов (например: Pod, Service, Secret) в apiGroups (например: core, apps/v1). Роль определяет права для конкретного пространства имен, область ClusterRole весь кластер.

Создание роли

Создадим ресурс Role со следующей спецификацией:

kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:namespace: developmentname: devrules:- apiGroups: [""]resources: ["pods", "services"]verbs: ["create", "get", "update", "list", "delete"]- apiGroups: ["apps"]resources: ["deployments"]verbs: ["create", "get", "update", "list", "delete"]

Ресурсы подов и служб принадлежат основной группе API (значение ключа apiGroups пустая строка), а ресурсы развертывания группе API приложений. Для этих двух групп apiGroup мы определили список ресурсов и действия, которые нужно авторизовать на этих ресурсах.

Строки сохраняем в файл role.yaml, для создания роли используем команду:

$ kubectl apply -f role.yaml

Создание RoleBinding

Назначение RoleBinding связать роль, то есть список разрешенных действий, с пользователем или группой. Чтобы у Дейва были права, указанные в созданной выше роли, мы привязываем его к этой роли. Для этого используем ресурс RoleBinding:

kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:name: devnamespace: developmentsubjects:- kind: Username: daveapiGroup: rbac.authorization.k8s.ioroleRef:kind: Rolename: devapiGroup: rbac.authorization.k8s.io

Эта RoleBinding связывает:

  • субъект пользователь Дейв;

  • роль: с именем dev, которая позволяет создавать, просматривать, обновлять, получать список, удалять ресурсы Deployment и Service.

Примечание: поскольку Дейв входит в группу разработчиков, то можно использовать следующую привязку RoleBinding для связи роли с группой, а не отдельным пользователем. Помните: информация о группе указывается в поле Organisation (O) сертификата, его отправляют с каждым запросом.

kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:name: devnamespace: developmentsubjects:- kind: Groupname: devapiGroup: rbac.authorization.k8s.ioroleRef:kind: Rolename: devapiGroup: rbac.authorization.k8s.io

Мы сохранили спецификацию ресурса RoleBinding в файле role-binding.yaml и создаем его с помощью команды:

$ kubectl apply -f role-binding.yaml

Создание файла конфигурации KubeConfig

Все настроено. Теперь отправляем Дейву информацию, которая необходима для настройки его локального клиента kubectl для связи с нашим кластером. Сначала создаем файл kubeconfig.tpl со следующим содержанием, его мы будем использовать в качестве шаблона:

apiVersion: v1kind: Configclusters:- cluster:certificate-authority-data: ${CLUSTER_CA}server: ${CLUSTER_ENDPOINT}name: ${CLUSTER_NAME}users:- name: ${USER}user:client-certificate-data: ${CLIENT_CERTIFICATE_DATA}contexts:- context:cluster: ${CLUSTER_NAME}user: davename: ${USER}-${CLUSTER_NAME}current-context: ${USER}-${CLUSTER_NAME}

Чтобы создать kubeconfig из этого шаблона, нужно сначала установить переменные среды:

# Имя пользователя$ export USER="dave"# Имя кластера (полученное из текущего контекста)$ export CLUSTER_NAME=$(kubectl config view --minify -o jsonpath={.current-context})# Сертификат клиента$ export CLIENT_CERTIFICATE_DATA=$(kubectl get csr mycsr -o jsonpath='{.status.certificate}')# Данные центра сертификации кластера$ export CLUSTER_CA=$(kubectl config view --raw -o json | jq -r '.clusters[] | select(.name == "'$(kubectl config current-context)'") | .cluster."certificate-authority-data"')# Точка входа API$ export CLUSTER_ENDPOINT=$(kubectl config view --raw -o json | jq -r '.clusters[] | select(.name == "'$(kubectl config current-context)'") | .cluster."server"')

Подставляем их, используя удобную утилиту envsubst:

$ cat kubeconfig.tpl | envsubst > kubeconfig

Отправляем Дейву файл kubeconfig. Чтобы взаимодействовать с кластером, ему достаточно добавить в файл свой закрытый ключ.

Использование контекста

Чтобы использовать kubeconfig, Дейв устанавливает переменную среды KUBECONFIG, указав путь к файлу.

$ export KUBECONFIG=$PWD/kubeconfig

Примечание: есть разные способы использовать конфигурации Kubernetes. Можно установить переменную среду KUBECONFIG, добавить новую запись в файл $ HOME/.kube/config по умолчанию или использовать флаг --kubeconfig для каждой команды kubectl.

Чтобы добавить закрытый ключ dave.key, Дейв использует команду:

$ kubectl config set-credentials dave \--client-key=$PWD/dave.key \--embed-certs=true

Команда создает ключ client-key-data в записи пользователя файла kubeconfig и устанавливает dave.key в кодировку base64 в качестве значения.

Если все успешно, Дейв может проверить версию сервера (и клиента) с помощью команды:

$ kubectl versionClient Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:23:09Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"darwin/amd64"}Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea79a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:14:56Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"}

Теперь проверим, позволяет ли связанная с Дейвом текущая роль отображать узлы кластера:

$ kubectl get nodesError from server (Forbidden): nodes is forbidden: User "dave" cannot list resource "nodes" in API group "" at the cluster scope

Конечно, нет! Но Дейв может что-то развертывать в кластере по крайней мере, в пространстве имен development. Давайте проверим это с помощью YAML-файла, который определяет Deployment на основе образа nginx и Service для его предоставления:

# www.yamlapiVersion: apps/v1kind: Deploymentmetadata:name: wwwnamespace: developmentspec:replicas: 3selector:matchLabels:    app: wwwtemplate:metadata:    labels:    app: wwwspec:    containers:    - name: nginx    image: nginx:1.14-alpine    ports:    - containerPort: 80---apiVersion: v1kind: Servicemetadata:name: wwwnamespace: developmentspec:selector:app: votetype: ClusterIPports:- port: 80targetPort: 80

Из результата следующей команды видно, что Дейв может создавать эти ресурсы в кластере:

$ kubectl apply -f www.yamldeployment.apps/www createdservice/www created

Дейв ограничен пространством имен development. Если он попытается получить список всех подов в пространстве имен по умолчанию, то получит сообщение об ошибке:

$ kubectl get podsError from server (Forbidden): pods is forbidden: User "dave" cannot list resource "pods" in API group "" in the namespace "default"

Еще он не может создавать другие ресурсы, кроме тех, к которым ему предоставили доступ. Например, мы можем попробовать следующую спецификацию ресурса типа Secret:

# credentials.yamlapiVersion: v1kind: Secretmetadata:name: mysecretnamespace: developmentdata:username: YWRtaW4=password: MWYyZDFlMmU2N2Rm

Давайте посмотрим, как Дейв попытается его создать:

$ kubectl apply -f credentials.yamlError from server (Forbidden): error when retrieving current configuration of:Resource: "/v1, Resource=secrets", GroupVersionKind: "/v1, Kind=Secret"Name: "mysecret", Namespace: "development"Object: &amp;{map["apiVersion":"v1" "data":map["password":"MWYyZDFlMmU2N2Rm" "username":"YWRtaW4="] "kind":"Secret" "metadata":map["annotations":map["kubectl.kubernetes.io/last-applied-configuration":""] "name":"mysecret" "namespace":"development"]]}from server for: "credentials.yaml": secrets "mysecret" is forbidden: User "dave" cannot get resource "secrets" in API group "" in the namespace "development"

Заключение

Мы показали, как использовать сертификат клиента для авторизации пользователей в кластере Kubernetes. Можно настраивать аутентификацию другим способом, но этот довольно прост.

После настройки аутентификации мы использовали роль, чтобы определить некоторые права, ограниченные пространством имен, и привязать их к пользователю с помощью RoleBinding. Если нам нужно будет предоставить права для всего кластера, мы сможем использовать ресурсы ClusterRole и ClusterRoleBinding.

Что еще почитать:

  1. 90+ инструментов, без которых не обойтись при использовании Kubernetes.

  2. Основы безопасности внутри Kubernetes: 10 простых советов.

  3. Как устроен Kubernetes as a Service на платформе Mail.ru Cloud Solutions.

Подробнее..

Как жили до Kubernetes сравниваем самый популярный оркестратор с другими решениями

24.02.2021 12:05:30 | Автор: admin


Kubernetes сейчас называют стандартом для оркестрации контейнеров. Он лежит в основе многих облачных платформ контейнеризации: например, мы давно развиваем наш Kubernetes aaS на платформе Mail.ru Cloud Solutions.


Однако Kubernetes далеко не первый подобный инструмент на рынке: некоторые из систем-предшественников продолжают активно использовать и вроде бы даже успешно.


Почему так происходит, несмотря на то, что Kubernetes, можно сказать, одержал победу в своем классе и мы видим много примеров, когда он приходит на смену другим решениям? Например, не так давно разработчики Mesosphere DC/OS, в основе которой лежал Apache Mesos, прекратили ее развитие и сфокусировались на другой своей платформе D2iQ Kubernetes (DKP). Думаю, что стоит разобраться, всегда ли хорош Kubernetes, когда оправдано использовать другие оркестраторы и о каких подводных камнях стоит знать.


Я Дмитрий Лазаренко, директор по продуктам облачной платформы Mail.ru Cloud Solutions (MCS). В этой статье расскажу об устройстве ряда оркестраторов-предшественников, сравню их с Kubernetes, посмотрю на его преимущества и недостатки по сравнению с ними.



Критерии для сравнения


В статье я сравню Kubernetes с наиболее известными и часто используемыми системами оркестрации: Docker Swarm, Nomad, Apache Mesos и Fleet.


Для сравнения использую следующие критерии:


  1. Типы рабочих нагрузок: предназначен ли оркестратор для управления Docker-контейнерами, иными контейнерами и неконтейнерными приложениями.
  2. Легкость управления: насколько оркестратор прост в установке и настройке.
  3. Требования к платформе для развертывания: является ли решение платформонезависимым или есть определенные требования к ОС и иные ограничения.
  4. Производительность: среднее время обработки API-вызовов, запуска контейнеров и других важных операций.
  5. Ограничения на количество узлов и контейнеров в кластере: какие существуют ограничения на число управляемых компонентов.
  6. Конфигурация как код: есть ли возможность загрузки схем приложений из YAML- или JSON-файлов.
  7. Шаблоны: доступна ли настройка пользовательских шаблонов.
  8. Сети: как строится сеть, есть ли собственное сетевое решение или необходимо использовать сторонние сетевые плагины.
  9. Возможность обнаружения сервисов: поддерживается ли динамическое обнаружение сервисов путем использования DNS, Proxy и так далее.
  10. Автомасштабирование: есть ли возможность автомасштабирования в зависимости от изменений нагрузки.
  11. Выполнение обновлений и отката: какие стратегии обновлений поддерживаются. Существует ли возможность последовательных обновлений (Rolling Update), когда старые инстансы приложения постепенно заменяются на новые. Насколько просто проводится откат.
  12. Отказоустойчивость: как выполняется обработка сбоев, что происходит при выходе из строя одного из узлов.
  13. Мониторинг: есть ли встроенные механизмы или необходимо использовать внешние инструменты и какие.
  14. Безопасность: есть ли встроенное решение для управления конфиденциальной информацией (логины и пароли).

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


I. Docker Swarm vs Kubernetes


Docker Swarm встроенное в сам Docker решение для управления контейнерами на физических или виртуальных машинах. Когда несколько хостов Docker используют режим Swarm, некоторые из них могут быть настроены как управляющие ноды, или менеджеры (Manager), а остальные как рабочие ноды (Worker). Задача менеджеров управлять кластером и делегировать задачи рабочим узлам.


1. Типы рабочих нагрузок


Если Docker Swarm предназначен для работы исключительно с Docker-контейнерами, то Kubernetes поддерживает несколько типов контейнеров: Docker, containerd, CRI-O и любое решение, придерживающееся стандарта CRI (Container Runtime Interface).


2. Легкость управления


По сравнению с Kubernetes Docker Swarm намного проще в установке вручную. Swarm работает поверх Docker, использует интерфейс его командной строки и легко интегрируется с Docker Compose. Kubernetes также может работать поверх Docker, но взаимодействовать с инфраструктурой в этом случае нужно будет при помощи двух разных утилит: Docker CLI и kubectl CLI.


Устройство Kubernetes на порядок сложнее. Он включает в себя множество компонентов: kube-api-server, kube-scheduler, kube-controller-manager, kube-proxy, kubelet и поддерживает несколько типов установщиков для разных типов инфраструктур и задач: Kubeadm, Kops, Kubespray и так далее. Все это требует дополнительного обучения.


То есть порог вхождения в Kubernetes выше по сравнению с Docker Swarm. Но все, что связано непосредственно с администрированием контейнеров: deployments, доведение кластера до нужного состояния, добавление новых нод, в K8s можно выполнить проще и быстрее. Также многие, поработав с обоими оркестраторами, отмечают, что Kubernetes более стабилен.


3. Требования к платформе для развертывания


Docker Swarm, как и Kubernetes, может использоваться в различных ОС: Windows, macOS, Linux. Однако Kubernetes использует различные настройки для каждой ОС. Если требуемые вам конфигурации выходят за рамки стандартной реализации, самостоятельная настройка Kubernetes может превратиться в гигантскую задачу. В этом плане Docker Swarm выигрывает.


4. Производительность


По сравнению с Kubernetes Docker Swarm быстрее. Kubernetes сложнее из-за большего числа дополнительных уровней в архитектуре (планировщик и так далее), что замедляет выполнение операций, но обеспечивает стабильность кластера в целом.


5. Ограничения на количество узлов и контейнеров в кластере


Kubernetes v1.20 поддерживает конфигурации, отвечающие следующим критериям:


  • не более 110 подов на узел, при этом параметр можно настраивать,
  • не более 5 000 узлов,
  • всего не более 150 000 подов,
  • всего не более 300 000 контейнеров.

В документации Docker Swarm даны рекомендации только по числу узлов-менеджеров 7 штук. Максимальное число контейнеров будет определяться, скорее, ограничениями ОС, но пользователи Swarm советуют придерживаться похожих рекомендаций: 100 контейнеров на ноду.


6. Конфигурация как код


В обоих оркестраторах можно использовать YAML-файлы для определения конфигураций. В Kubernetes также доступен JSON-формат, но YAML предпочтителен.


7. Шаблоны


В Docker Swarm нет шаблонизатора, аналогичного Helm в Kubernetes. Некий шаблонизатор приложений анонсировался в Docker Desktop Enterprise 3.0, но в настоящее время это решение не поддерживается.


8. Сети


С точки зрения сетевой модели Kubernetes смотрится предпочтительнее. Он работает с виртуальными сетями и поддерживает намного большее количество разных сетевых решений по сравнению со Swarm. Каждый под получает свой IP-адрес, что означает отсутствие необходимости сопоставления портов контейнера портам хоста. Kubernetes может работать с любым CNI-совместимым сетевым решением.


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


9. Возможность обнаружения сервисов


Реализована в обоих оркестраторах с использованием встроенного DNS-сервера.


10. Автомасштабирование


В Docker Swarm автомасштабирование недоступно. Для каждой службы вы можете вручную указать количество задач, которые хотите запустить. Когда это число увеличивается или уменьшается, управляющий узел (Manager) автоматически адаптируется, добавляя или удаляя задачи для поддержания желаемого состояния.


В Kubernetes поддерживается автомасштабирование на основе наблюдаемого использования ЦП, памяти и ряда других пользовательских метрик. При этом масштабирование доступно на нескольких уровнях:


  1. Автомасштабирование кластера с использованием Cluster Autoscaler, отвечающее за изменение числа узлов в кластере.
  2. Горизонтальное автомасштабирование подов (Horizontal Pod Autoscaler, HPA), которое автоматически изменяет количество подов в зависимости от значений выбранных показателей.
  3. Вертикальное автомасштабирование подов (Vertical Pod Autoscaler, VPA), которое автоматически изменяет объем ресурсов, выделяемых существующим подам.

11. Выполнение обновлений и отката


И Kubernetes, и Docker Swarm поддерживают последовательные обновления (Rolling Update): во время развертывания вы можете постепенно применять обновления служб к узлам. В случае сбоя можно вернуться к предыдущей версии сервиса. Однако Kubernetes предоставляет возможность более гибкой настройки.


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


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


12. Отказоустойчивость


В обоих оркестраторах поддерживается высокая доступность приложений.


В Docker Swarm управляющий узел (Manager) постоянно отслеживает состояние кластера и согласовывает любые различия между фактическим состоянием и настроенным вами желаемым состоянием.


В Kubernetes службы балансировки нагрузки обнаруживают поды со сбоями и удаляют их. Используются проверки работоспособности (Health Check).


13. Мониторинг


Использование в Docker Swarm решений для логирования и мониторинга (ELK, Prometheus) возможно, как и в Kubernetes, но требует больше кастомных настроек или использования дополнительных наборов инструментов, таких как Swarmprom.


14. Безопасность


С точки зрения безопасности в Swarm еще недавно не было практически ничего. Для хранения секретов приходилось использовать Vault. Сейчас каждый узел в Swarm применяет взаимную аутентификацию и шифрование TLS для защиты коммуникаций внутри себя и с другими узлами. Также есть возможность использовать самоподписанные корневые сертификаты или сертификаты от настраиваемого корневого Центра сертификации. Есть и встроенное решение для управления секретами.


Однако Kubernetes все равно находится на шаг впереди, предлагая гибкую настройку сетевых политик (Network Policies), использование пространств имен (Namespaces) и прочие преимущества.


II. Nomad vs Kubernetes


Nomad это оркестратор от компании HashiCorp для управления контейнерами и неконтейнерными приложениями. Основу его архитектуры составляют клиенты (Client), на которых можно запускать задачи, и серверы (Server), управляющие клиентами и задачами. Серверы выбирают лидера для обеспечения высокой доступности.


Рабочую нагрузку в Nomad определяет работа (Job), описывающая желаемое состояние кластера и состоящая из одной или нескольких групп задач. Задача (Task) самая маленькая единица работы, которая выполняется драйверами (Docker, QEMU, Java и так далее). Обязанность Nomad поддерживать соответствие между желаемым состоянием, описанным в Job, и фактическим состоянием кластера.


За консультацию по работе Nomad отдельное спасибо manefesto.

1. Типы рабочих нагрузок


В то время как Kubernetes ориентирован на использование контейнеров, Nomad работает с различными видами рабочих нагрузок. Он поддерживает виртуализированные, контейнерные и автономные, микросервисные и пакетные приложения, включая Docker, Java, Qemu и другие, полный список можно посмотреть здесь. Это позволяет оркестрировать не только контейнерами / виртуальными машинами, но и запускать приложения на нодах. Например, можно запустить на нодах тяжелые Java-приложения.


2. Легкость управления


Установка Nomad значительно проще, чем Kubernetes. Nomad доступен в виде предварительно скомпилированного двоичного файла (единого как для клиентов, так и для серверов) и в виде пакетов для различных операционных систем. Вы также можете собрать Nomad из исходников.


Минимальный тестовый стенд можно поднять на хосте без использования сторонних утилит типа Minikube. Достаточно запустить Nomad в dev-режим, и он будет выполнять роль сервера/агента, чего вполне достаточно для локального тестирования.


Однако если Kubernetes стремится предоставить все функции, необходимые для запуска контейнерных приложений (включая управление кластером, автообнаружение служб, обеспечение безопасности и многое другое), то Nomad сосредоточен только на управлении кластером и планировании.


K8s считается фреймворком для построения кластера, в то время как подход HashiCorp больше близок к UNIX-way. Настроить кластер Nomad под нужные требования можно с помощью внешних инструментов: Consul для обнаружения сервисов, Vault для управления конфиденциальной информацией и других. До определенного момента в Nomad не было возможности использовать внешние хранилища, сети, отличные от хостовых и Bridge, но сейчас есть поддержка плагинов CSI, CNI, появилась возможность использовать сети, хранилища так же, как это делает K8s.


В любом случае использовать Nomad несложно: по факту достаточно скопировать бинарный файл и создать Systemd-сервис, который работает в связке с Consul как Service Discovery.


3. Требования к платформе для развертывания


И Kubernetes, и Nomad могут быть развернуты в различных ОС: Linux, macOS, Windows. Однако Kubernetes требует различных настроек в зависимости от ОС поэтому его установка, проводимая вручную, сложнее.


4. Производительность


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


5. Ограничения на количество узлов и контейнеров в кластере


Kubernetes поддерживает кластеры до 5 000 узлов и 300 000 контейнеров. В документации Nomad указано, что он масштабируется до размеров кластера, превышающих 10 000 узлов в реальных производственных средах. Также Nomad успешно участвовал в ряде напряженных тестов на масштабируемость: 1 миллион контейнеров в 2016 году и 2 миллиона контейнеров в 2020 году. Однако на практике вероятность применения настолько масштабных кластеров невелика.


6. Конфигурация как код


Kubernetes поддерживает декларативные развертывания на основе YAML-файлов. Nomad использует собственный язык описания конфигураций HashiCorp HCL. То есть при использовании Nomad вам потребуется дополнительное обучение HCL. Но стоит отметить, что в Nomad присутствуют стандартные средства и команды для преобразования описаний из YAML и JSON в HCL (и обратно): yamlencode/yamldecode, jsonencode/jsondecode. Правда, работают они с определенными ограничениями, а некоторые в тестовом режиме.


7. Шаблоны


В Nomad нет инструмента для шаблонизации, аналогичного Helm для Kubernetes.


8. Сети


В плане организации сетей Kubernetes выглядит лаконичнее и стабильнее. В нем изначально была введена абстракция Pod, позволяющая запускать несколько контейнеров в одном сетевом пространстве, и использовался CNI (Container Networking Interface). Nomad же развивался от меньшего к большему и долгое время использовал исключительно хостовые сети без дополнительных слоев абстракции. Вместо Pod в нем использовались Job без возможности объединения контейнеров внутри них в единое сетевое пространство. Мультиинтерфейсные сети и возможность подключения CNI стали развиваться в Nomad относительно недавно. Можно подключить CNI-плагин для использования Calico, Cilium, Weave.


9. Возможность обнаружения сервисов


В отличие от Kubernetes, в Nomad нет Autodiscovery на основе DNS. Для обнаружения сервисов необходимо использовать дополнительный инструмент Consul. В этом Nomad сильно уступает Kubernetes. И многие считают это одной из основных причин, почему Nomad оказался менее востребован в свое время.


10. Автомасштабирование


Общая черта двух оркестраторов поддержка следующих типов автомасштабирования:


  • горизонтальное автомасштабирование приложений в Nomad и подов в Kubernetes,
  • автомасштабирование кластера.

Дополнительно в Kubernetes реализовано вертикальное автомасштабирование подов, которое автоматически изменяет объем ресурсов, выделяемых существующим подам. Похожее решение в Nomad, состоящее в построении рекомендаций по ЦП и памяти на основе анализа исторических данных, доступно исключительно в Enterprise-версии.


11. Выполнение обновлений и отката


Оба оркестратора предоставляют возможность гибкого управления обновлениями, включая стратегии последовательного (Rolling update), сине-зеленого (Blue and Green) и канареечного (Canary) обновлений. Для настройки доступен целый ряд показателей, влияющих на ход обновлений, включая временные интервалы для проверки работоспособности и прочее.


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


12. Отказоустойчивость


По умолчанию поведение у обеих систем схожее. В Kubernetes дается минута на определение узла со сбоем и до пяти минут на вытеснение подов на другую ноду. В Nomad примерно такие же дефолтные показатели. Но в Kubernetes с использованием опций kubelet и Control Manager реально добиться того, чтобы уже в течение 10 секунд поды были вытеснены на другую работоспособную ноду.


13. Мониторинг


Оба оркестратора совместимы с популярными инструментами логирования и мониторинга: ELK, Prometheus/Grafana и прочими. Ведется сбор метрик, которые можно получить впоследствии через API либо настроив автоматическую пересылку стороннему провайдеру.


14. Безопасность


Основное, в чем проигрывает Nomad в плане безопасности, это необходимость использования сторонней системы Vault для управления конфиденциальной информацией (логинами и паролями). Хотя безопаснее Vault на современном рынке Open Source-решений, пожалуй, нет его настройка и использование совместно с Nomad является довольно сложной задачей. Коробочный Kubernetes в этом отношении проще, так как может предложить встроенное решение.


Кроме этого, если в Kubernetes по умолчанию поддерживаются пространства имен (Namespaces), которые можно эффективно использовать для разделения сред разработки, то в Nomad данная функциональность доступна только в Enterprise-версии.


III. Apache Mesos (+ Maraphon, Aurora) vs Kubernetes


Apache Mesos это менеджер кластера, поддерживающий различные рабочие нагрузки. Основу архитектуры Mesos составляют мастер (Mesos Master), агенты (Mesos Agent), работающие на каждом узле кластера и управляемые мастером, и фреймворки (Mesos Frameworks), которые запускают задачи на агентах. Обычно разворачивается несколько резервных мастеров, готовых взять на себя управление в случае сбоя, а за выбор лидера среди них отвечает ZooKeeper.


Фреймворк состоит из двух компонентов: планировщика (Scheduler), он регистрируется на главном сервере, которому будут предлагаться ресурсы, и исполнителя (Executor), который запускается на узлах агентов для выполнения задач фреймворка. Мастер предлагает ресурсы агентов фреймворкам, а планировщики выбирают, какие из предложенных ресурсов использовать для запуска своих задач на агентах. В кластере Mesos может работать несколько фреймворков для разных типов задач.


То есть сам по себе Apache Mesos является лишь неким диспетчером, а конечная функциональность будет определяться используемым фреймворком. Поэтому при описании показателей будем иногда ссылаться на два конкретных фреймворка: Maraphon для управления контейнерными приложениями и Apache Aurora для планирования Cron-заданий и долго работающих служб. Aurora больше не поддерживается, но в свое время была довольно распространена и в том числе могла использоваться для контейнерных приложений. Также иногда будем упоминать Mesosphere DC/OS операционную систему, основанную на Apache Mesos, Maraphon и предлагающую ряд дополнительных возможностей (правда, часть из них доступна в платной версии).


1. Типы рабочих нагрузок


Apache Mesos предназначен для обработки различных типов рабочих нагрузок, которые могут быть как контейнерными, так и неконтейнерными. В качестве планировщика заданий (Scheduler) могут быть использованы, например, Hadoop (Big Data), MPI (обмен сообщениями), Jenkins (система непрерывной интеграции). Используя Apache Mesos, можно разработать даже собственный планировщик.


Kubernetes ориентирован исключительно на контейнерные приложения.


2. Легкость управления


Apache Mesos проще развернуть, чем Kubernetes, если мы говорим о ручной установке. Однако дальнейшее администрирование Apache Mesos сложнее по сравнению с K8s, так как необходимо работать с ZooKeeper, уметь администрировать Java и быть готовым к связанным с этим трудностям: утечкам памяти, Xmx, лимитам и так далее. Kubernetes с Golang в этом плане проще.


3. Требования к платформе для развертывания


Mesos работает на Linux и macOS. Агенты могут быть установлены и на Windows.
Kubernetes также поддерживает все ОС.


4. Производительность


Mesos изначально был ориентирован на работу с Big Data поэтому по производительности он опережает Kubernetes.


5. Ограничения на количество узлов и контейнеров в кластере


По масштабируемости Apache Mesos выигрывает у Kubernetes, так как способен поддерживать десятки тысяч узлов (K8s рассчитан на 5 000 максимум). Когда Mesos сочетается с Mesosphere DC/OS, получается платформа, предлагающая практически неограниченную масштабируемость, которая идеально подходит для больших систем. Разработчики заявили, что прекратили ее развитие, но существующих клиентов продолжают поддерживать. Еще в 2015 году Mesos с успехом выдержал тест на масштабирование до 50 000 узлов.


6. Конфигурация как код


В Apache Mesos в сочетании с Maraphon можно использовать определения JSON (для указания репозиториев, ресурсов, числа экземпляров и команд для выполнения). В Kubernetes для этой цели поддерживаются декларативные развертывания и в YAML, и в JSON (первый предпочтительнее).


7. Шаблоны


Если вы работаете в Mesosphere DC/OS, то можете использовать шаблоны конфигураций (с использованием Maraphon-LB). В Apache Aurora при настройке услуг в DSL также были доступны шаблоны во избежание дублирования конфигураций. Но Helm, используемый в K8s, будет более мощным и удобным инструментом.


8. Сети


Долгое время Apache Mesos отставал от Kubernetes в плане сетевой реализации, так как по умолчанию в нем не назначались IP-адреса контейнерам, требовалось проведение сопоставления портов контейнера с портами хоста, которые являются ограниченным ресурсом. Впоследствии была добавлена поддержка CNI (Container Networking Interface) и появилась возможность использования виртуальных сетей вида Calico, Cilium, Weave. Но в Kubernetes сейчас поддерживается большее число сетевых решений.


9. Возможность обнаружения сервисов


Присутствует в обеих системах.


Mesos-DNS обеспечивает обнаружение служб и базовую балансировку нагрузки для приложений. При использовании совместно с Marathon дополнительно доступен Marathon-LB для обнаружения на основе портов с помощью HAProxy. В Aurora была также представлена экспериментальная поддержка Mesos DiscoveryInfo для создания настраиваемой системы обнаружения без использования ZooKeeper.


В Kubernetes доступен встроенный DNS-сервер.


10. Автомасштабирование


Реализация автомасштабирования лучше в Kubernetes. Как уже отмечалось выше, доступны три стратегии: масштабирование кластера, горизонтальное масштабирование подов и вертикальное масштабирование подов. В качестве триггеров можно настраивать показатели ЦП, памяти и пользовательские показатели.


В Apache Mesos автомасштабирование доступно в комбинации с Maraphon (в Mesosphere DC/OS) на основе значений ЦП, памяти и числа запросов в секунду.


11. Выполнение обновлений и отката


Поддерживается в обеих системах. Сине-зеленое (Blue and Green) развертывание доступно в Apache Mesos, также его можно реализовать в Kubernetes, используя встроенные механизмы.


12. Отказоустойчивость


Обе системы отказоустойчивы.


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


Аналогично поды в Kubernetes реплицируются на несколько узлов. Обычно кластер Kubernetes состоит из нескольких рабочих узлов. В нем также может быть несколько мастеров.


Кроме этого, оба оркестратора предоставляют проверки работоспособности (Health Check). Для Mesos они доступны в случае использования Mesosphere DC/OS.


13. Мониторинг


И в Mesos, и в Kubernetes доступно получение метрик, относящихся к работоспособности и другим показателям. Данные можно запрашивать и агрегировать с помощью внешних инструментов. Типичной практикой является развертывание ELK и Prometheus + Grafana.
Но подключение мониторинга в Kubernetes происходит проще, так как многое строится на готовых решениях благодаря поддержке многочисленного сообщества и богатому инструментарию. В Mesos многое приходится делать самостоятельно, вследствие чего неизбежно увеличивается Time to Market.


14. Безопасность


Сложно отдать кому-то предпочтение. Если говорить об Apache Mesos в связке с Maraphon или Aurora вне Mesosphere DC/OS, то Kubernetes выглядит лучше. Долгое время Mesos не мог предложить в плане безопасности практически ничего. Так, встроенное решение по работе с секретами не имело в нем надлежащей реализации, и пользователи предпочитали использовать сторонние инструменты, например Vault.


Но если обратиться к ОС DC/OS Enterprise сегодня она предлагает богатый функционал в плане корпоративной безопасности, возможно, даже лучший, чем в Kubernetes. Однако эта версия системы является платной, кроме того, ее развитие свернуто разработчиками в пользу новой платформы на основе как раз Kubernetes.


IV. Fleet vs Kubernetes


Fleet это низкоуровневый кластерный движок от CoreOS, похожий на распределенную систему инициализации. В настоящее время он не поддерживается, так как компания стала активно продвигать Kubernetes.


Fleet был основан на systemd. В то время как systemd обеспечивал инициализацию системы и служб на уровне одной машины, Fleet расширил этот процесс до кластера. В архитектуре Fleet на каждой машине запускался fleetd-демон, обеспечивающий две роли: движок (Engine) и агент (Agent). Движок принимал решения по планированию, а агент обрабатывал Systemd Unit-файлы (Unit). Эта обработка чаще всего сводилась к запуску контейнера.


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


1. Типы рабочих нагрузок


Если Kubernetes поддерживает работу с контейнерными приложениями, то Fleet использовал модули systemd для запуска контейнеров или любых других процессов на узлах кластера.


2. Легкость управления


Установка Fleet проводилась значительно проще и быстрее по сравнению с Kubernetes. Чтобы запустить свои службы в кластере, необходимо было отправить обычные модули systemd в сочетании с несколькими свойствами, специфичными для Fleet. То есть для управления кластером было достаточно знаний по работе с systemd.


3. Требования к платформе для развертывания


Fleet изначально разрабатывался на базе CoreOS. Kubernetes в настоящее время поддерживает практически все виды ОС.


4. Производительность


Сложно оценить, так как продукт более не поддерживается. Вероятно, Fleet был быстрее, учитывая его архитектурную простоту и ограниченную функциональность.


5. Ограничения на количество узлов и контейнеров в кластере


Fleet уступал по масштабируемости. В последней версии продукта не рекомендовалось запускать кластеры более 100 узлов или с более чем 1000 служб. Kubernetes сейчас поддерживает до 5 000 узлов и 300 000 контейнеров в кластере.


6. Конфигурация как код


В документации Fleet отсутствуют данные о возможности загрузки определений приложений из YAML или JSON, доступной в Kubernetes. Но формировавшиеся представления сохранялись в etcd в формате JSON. Также Fleet предоставлял API для управления состоянием кластера с использованием JSON: создание и удаление модулей, изменение желаемого состояния, вывод списка модулей.


7. Шаблоны


Fleet поддерживал несколько простых шаблонов развертывания на основе настраиваемых параметров модуля systemd. В них, например, можно было использовать определения места запуска контейнеров (Affinity и Anti-Affinity), похожие на селекторы Kubernetes. Однако используемый в Kubernetes шаблонизатор Helm более мощный инструмент.


8. Сети


Fleet это низкоуровневое решение, и его нельзя сравнивать с K8s в плане организации сетей.


9. Возможность обнаружения сервисов


Присутствует в обоих. Но, в отличие от Kubernetes, Fleet не поддерживал интеграцию с DNS. Взамен этого он предлагал модель Sidekick, в которой отдельный агент обнаружения запускался рядом с основным контейнером.


10. Автомасштабирование


В документации Fleet не описана возможность автомасштабирования.


11. Выполнение обновлений и отката


Fleet не поддерживал последовательные обновления, в отличие от K8s: новые модули (Units) планировались, а старые уничтожались вручную.


12. Отказоустойчивость


Обеспечивается в обоих оркестраторах. Архитектура Fleet разработана так, чтобы быть отказоустойчивой: если машина выходила из строя, любые запланированные на ней задания (Unit) перезапускались на новых хостах.


13. Мониторинг


Fleet уступал Kubernetes по возможностям мониторинга. Он поддерживал в экспериментальном режиме получение незначительного количества метрик в формате Prometheus. О возможности использования других инструментов логирования и мониторинга (ELK и прочее) в документации не сказано.


14. Безопасность


Fleet как низкоуровневое решение предлагал меньше возможностей в плане безопасности по сравнению с K8s. Например, он не поддерживал контроль доступа (Access Control List, ACL): все, кто имели доступ к etcd, могли управлять Fleet. Также в нем не было инструментов для управления конфиденциальной информацией (логины и пароли).


V. Преимущества и недостатки систем оркестрации


1. Docker Swarm


Преимущества:


  1. Ниже порог входа: быстро устанавливается, легок в изучении, интегрирован с Docker Compose и Docker CLI.
  2. Позволяет быстрее разворачивать контейнеры.

Недостатки:


  1. Уступает Kubernetes в плане администрирования, особенно в Production-среде.
  2. Отсутствует автомасштабирование.
  3. Проигрывает в плане поддержки сообществом: инструментарий намного меньше, чем у Kubernetes, что приводит к отсутствию готовых решений и необходимости многое настраивать самостоятельно.

Для каких задач подходит: в небольших проектах, где предпочтение отдается простоте и быстрой разработке.


2. Nomad


Преимущества:


  1. Прост в установке и эксплуатации, так как сосредоточен только на управлении кластером.
  2. Поддерживает различные виды рабочих нагрузок.

Недостатки:


  1. Ограниченная функциональность. Требуется установка сторонних инструментов для решения задач, которые K8s реализует по умолчанию: Autodiscovery, управление секретами.
  2. Проигрывает Kubernetes в плане поддержки сообществом.

Для каких задач подходит:


  1. Для небольших/средних команд с ограниченными возможностями поддержки оркестратора.
  2. При сочетании контейнерных и неконтейнерных нагрузок.
  3. При построении сетей L2 с малым количеством контейнеров.

3. Apache Mesos (+ Maraphon, Aurora)


Преимущества:


  1. Высокая масштабируемость.
  2. Поддержка контейнерных и неконтейнерных приложений, возможность совмещения нескольких фреймворков (включая Kubernetes).

Недостатки:


  1. Сложность администрирования.
  2. Проигрывает в плане поддержки сообществом.

Для каких задач подходит:


  1. Масштабные проекты с участием нескольких центров обработки данных и/или десятков тысяч узлов.
  2. При сочетании контейнерных и неконтейнерных нагрузок. В частности, при работе с приложениями по обработке данных: Hadoop, Kafka, Spark.
  3. Комбинация Apache Mesos и Aurora отлично подходила для оркестрации задач, не связанных с контейнерами, но требующих аналогичной обработки, например перезапуска в случае сбоя.

4. Fleet


Преимущества:


  1. Простота

Недостатки:


  1. Ограниченная функциональность
  2. Продукт более не поддерживается

Для каких задач подходил: можно было использовать в качестве основы для запуска инструментов оркестровки более высокого уровня например, для распространения агентов Kubernetes и двоичных файлов на машины в кластере.


5. Kubernetes


Преимущества:


  1. Самодостаточный инструмент оркестровки, в который встроено множество сервисов. Kubernetes предоставляет все функции, необходимые для запуска приложений на основе контейнеров, включая: управление кластером, планирование, обнаружение служб, мониторинг, управление безопасностью и многое другое.
  2. Поддерживается фондом CNCF (Cloud Native Computing Foundation). У Kubernetes самое впечатляющее по числу участников сообщество среди всех оркестраторов, что обеспечивает богатый инструментарий и большое число готовых решений.
  3. Это бесплатный инструмент с открытым исходным кодом, который работает в любой ОС.

Недостатки:


  1. Сложнее настроить вручную, хотя это можно решить с помощью использования Kubernetes aaS.
  2. Предназначен только для контейнерных приложений.
  3. Меньшее количество поддерживаемых узлов по сравнению с Nomad и Mesos.

Для каких задач подходит: идеален в качестве платформы корпоративного уровня, способной запускать тысячи контейнеров в облаке и управлять ими.


Как перейти на Kubernetes с других оркестраторов


Предположим, вы использовали одно из рассмотренных решений и задумались о переходе на Kubernetes.


Что теперь предпринять?


Продумайте окружение. Kubernetes интегрирован со множеством инструментов мониторинга, логирования, построения CI/CD и так далее. Возможно, вы не захотите ограничиться лишь сменой оркестратора и построите более обширную инфраструктуру по сравнению с текущей, максимально задействовав возможности экосистемы K8s.


Перепишите манифесты на YAML Kubernetes. Сложность операции будет определяться их количеством и особенностями прежнего оркестратора:


  1. Переход с Docker Swarm наиболее прост. Можно либо самостоятельно переписать все манифесты, либо использовать доступные инструменты преобразования из файлов Docker Compose в YAML Kubernetes, например Kompose. Первый способ представляется более правильным, так как позволит учесть все преимущества Kubernetes, связанные с deployments, labels, tolerations и так далее. Второй способ позволит избежать переписывания, но менее нативен.
  2. Переход с Nomad и Apache Mesos наиболее сложен, так как их манифесты не совместимы с Kubernetes. Скорее всего, переход с Apache Mesos/Maraphon будет проще, так как в нем использовались JSON-определения в отличие от Apache Mesos/Aurora с собственным синтаксисом (похожим на Python) и Nomad с его HCL.
  3. Если вы использовали Fleet, то, вероятно, как основу для более высокоуровневого оркестратора. То есть для перехода на Kubernetes необходимо переписать манифесты, созданные для того оркестратора, что вы применяли. Либо ничего не переписывать, если у вас уже был Kubernetes поверх Fleet.

Разверните Kubernetes, вспомогательную инфраструктуру и настройте свой кластер. Если у вас нет команды, способной справиться с непростой задачей ручной установки, то можно использовать одно из готовых (Managed) решений. Их провайдеры не только предлагают услуги по развертыванию кластеров Kubernetes, но и оказывают дальнейшую техническую поддержку, а также могут помочь с миграцией (например, с Docker Swarm). В результате вы получите готовый кластер за считаные минуты и все преимущества Cloud Native-приложений.


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


Чек-лист: на что обратить внимание при выборе провайдера Kubernetes


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


Так, KaaS от Mail.ru Cloud Solutions из коробки включает:


  • автоматическое масштабирование кластера до сотен узлов с использованием Kubernetes Cluster Autoscaler;
  • наличие выделенных балансировщиков нагрузки для распределения трафика;
  • интегрированные Persistent Volumes на базе надежного блочного хранилища CEPH, виртуального файлового хранилища или SSD/NVME-дисков, подключенных по iSCSI к каждому вычислительному серверу;
  • собственный Terraform-провайдер для работы с инфраструктурой как кодом.

Совместимость со стандартными инструментами Kubernetes. Нужно проверить, поддерживает ли провайдер интеграцию с другими приложениями экосистемы K8s, например:


  • Serverless: OpenFaaS, Kubeless;
  • Service Mesh: Istio, Consul, Linkerd;
  • мониторинг: Prometheus, Fluentd, Jaeger, OpenTracing;
  • CI/CD: Gitlab, CircleCI, Travis CI;
  • IaC (описание приложений): Terraform, Helm.

Возможность подключения других сервисов провайдера. Обратите внимание на дополнительные решения провайдера и возможность их использования в своих кластерах. KaaS от MCS позволяет подключить объектное хранилище (Cloud Storage) и DBaaS для хранения данных Stateful-приложений, а также его легко совместно использовать с другими PaaS, например Cloud Big Data. Например, централизованно разворачивать нужные сервисы и управлять ими на одной платформе.


Сертификация CNCF. Подтверждает то, что сервис отвечает всем функциональным требованиям сообщества Cloud Native Computing Foundation (CNCF) и совместим со стандартным Kubernetes API. MCS пока единственный в России облачный провайдер, получивший такую сертификацию.


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


Выводы: сравнительный анализ систем оркестрации


Результаты сравнения Kubernetes и других известных оркестраторов наглядно демонстрирует таблица ниже:


  • самая удачная и удобная в использовании реализация;
  • удовлетворительная реализация, уступающая другим по ряду параметров;
  • реализация, уступающая другим по большинству параметров и/или требующая значительно больше времени и ресурсов для ее использования.

Критерий/Оркестратор Docker Swarm Nomad Apache Mesos Fleet K8s
Типы рабочих нагрузок
Легкость установки и исходной настройки (вручную)
Легкость администрирования кластеров
Требования к платформе для развертывания
Производительность
Ограничения на количество узлов и контейнеров в кластере
Конфигурация как код
Шаблоны
Сети
Возможность обнаружения сервисов
Автомасштабирование
Выполнение обновлений и отката
Отказоустойчивость
Мониторинг
Безопасность

Как мы увидели, Kubernetes лидирует по многим показателям. Но есть и недостатки, и самый весомый из них высокий порог входа для новых пользователей при выборе ручной установки. Однако современный рынок предлагает множество Managed-решений, способных преодолеть эту сложность. Главное грамотно подойти к выбору между ними, взвесив все за и против.


Что еще почитать по теме:


  1. Зачем крупным компаниям микросервисы, контейнеры и Kubernetes: как эти три технологии выводят IT на новый уровень.
  2. 10 антипаттернов деплоя в Kubernetes: распространенные практики, для которых есть другие решения.
  3. Наш Телеграм-канал Вокруг Kubernetes в Mail.ru Group.
Подробнее..

Перевод Argo CD готов к труду и обороне в Kubernetes

26.02.2021 20:18:58 | Автор: admin

Привет, Хабр. В рамках курса Инфраструктурная платформа на основе Kubernetes подготовили для вас перевод полезного материала.

Также приглашаем на открытый вебинар Работа с NoSQL базами в k8s (на примере Apache Cassandra). На вебинаре участники вместе с экспертом рассмотрят плюсы и минусы запуска Apache Cassandra в k8s: насколько такой вариант установки готов к продакшену, и какие подводные камни имеются.


В этой статье мы рассмотрим несколько вопросов касательно Argo CD: что это такое, зачем его используют, как его развернуть (в Kubernetes), как его использовать для реализации непрерывного развертывания (continuous deployment), как настроить SSO с помощью GitHub и разрешений и т. д.

Что такое Argo CD и GitOps

Argo CD это декларативный GitOps-инструмент непрерывной доставки (continuous delivery) для Kubernetes.

Но что же такое GitOps?

Официальное определение гласит, что GitOps это способ реализации непрерывного развертывания (continuous deployment) облачных приложений. Он фокусируется на создании ориентированного на разработчиков опыта эксплуатации инфраструктуры с использованием инструментов, с которыми разработчики уже знакомы, включая Git и Continuous Deployment.

Официальное определение не вдается в подробности, не так ли? Возможно, вы не так часто слышали об этой концепции, но, скорее всего, вы уже использовали ее: вы определяете свои K8s ресурсы в YAML или с помощью Helm-диаграммы, вы используете Git в качестве единого источника истины (single source of truth), вы запускаете одни автоматизированные CI задачи для деплоя в продакшн, когда ваша ветвь master изменена, и вы запускаете другие задачи по пул реквесту для деплоя на стейджи.

Почему GitOps

Определения, конфигурации и среды приложений должны быть декларативными и подлежать контролю версий.

Развертывание (деплой) приложений и управление жизненным циклом должны быть:

  • автоматизированными

  • проверяемыми

  • простыми для понимания

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

В этом и заключается концепция GitOps и то, почему он хорош.

Почему Argo CD

Можно ли достичь вышеупомянутых преимуществ GitOps, используя любые другие инструменты CI/CD? Скорее всего, да. Например, вы можете использовать старый добрый Jenkins, определить разные задачи для разных ветвей, после чего задачи будут следить за Git-репозиториями или использовать хуки для реакции на события, а в конвейере вы можете выполнить несколько git clone и helm install. Нет ничего плохого в Jenkins, на что я указывал в моей предыдущей статье о CI: Введение в CI: сравнение 17 основных инструментов CI или Как выбрать лучшее CI в 2020 году.

Если вы не читали ее, я рекомендую вам сделать это, чтобы лучше понять, что мы ищем при выборе инструмента.

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

Все еще неубедительно

Хорошо, я понимаю, что у вас есть опасения по поводу внедрения новых инструментов, поэтому вот несколько фактов, которые могут вас убедить:

Это часть фонда Cloud Native Computing Foundation (CNCF).

Он активно поддерживается и постоянно улучшается. Посмотрите количество на коммитов:

Мне порой доставляет удовольствие покопаться в git-репозиториях в поисках разнообразной информации, и вот что я нашел здесь:

  • первый релиз v0.1.0 состоялся в марте 2018 года (относительно новый проект)

  • v1.0.0 зарелижен в мае 2019 (быстро развивается)

  • на момент написания статьи имеет версию v1.7.8 (ноябрь 2020, развивается очень быстро)

  • 4,3 тыс. звезд в репозитории Argo CD (еще больше на других репозиториях того же проекта)

  • 60 открытых PR и 500 открытых задач на сегодняшний день (очень даже неплохо, это означает, что сообщество активно занимается им и постоянно исправляет и придумывает запросы на новый функционал)

Он также упоминается в техническом радаре CNCF:

Он все еще находится на стадии ОЦЕНКА (ASSESS), что означает, что члены CNCF протестировали его, и он показался многообещающим. Он рекомендован к рассмотрению, когда вы сталкиваетесь с конкретной потребностью в подобной технологии в вашем проекте.

Радар является инициативой CNCF End User Community. Это группа из более чем 140 ведущих компаний и стартапов, которые регулярно встречаются для обсуждения проблем и передовых методов внедрения облачных технологий. Если вам лень разбираться, какой инструмент использовать, или если вы чувствуете себя неуверенно в отношении новых вещей, которые вы еще не пробовали, довольно безопасно выбрать один из вариантов, которые предлагает радар, потому что многие крупные компании уже протестировали его за вас, множество из имен которых вы уже слышали. Если это подходит им, есть большая вероятность, что и вам это понравится.

Развертывание

Окей, приступим к практике. У вас должен быть запущен кластер K8s, потому что мы собираемся делать это внутри k8s.

kubectl create namespace argocdkubectl apply -n argocd -f \https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml 

Один YAML, чтоб править всеми.

После развертывания у вас будет несколько запущенных подов и сервисов. Примеры:

Pods:argocd-application-controller-6b47c9bd78-kp6djargocd-dex-server-7b6d8776d8-knsxxargocd-redis-99fb49846-l466kargocd-repo-server-b664bd94b-bmtwrargocd-server-768879948c-sx875Services:argocd-dex-serverargocd-metricsargocd-redisargocd-repo-serverargocd-serverargocd-server-metrics

Комментарии по доступу к сервису и ingress:

Обратите внимание, что в развертывании по умолчанию сервис работает как тип Cluster IP, и ingress по умолчанию нет. Поэтому, если вы хотите получить доступ к сервису, вам нужно будет либо выполнить переадресацию портов, либо изменить тип сервиса на балансировщик нагрузки, либо создать ingress.

Если вы делаете это в производственном кластере, например в EKS в AWS, скорее всего, вы хотите использовать ingress и, вероятно, у вас уже есть ingress-контроллер. Вход для Argo CD здесь немного сложен, потому что на порту 443 он имеет как HTTPS (для веб-интерфейса консоли), так и GRPC для вызовов API командной строки. Если вы используете EKS, например, с ingress-контроллером Nginx, скорее всего, вы уже выполнили завершение TLS там, поэтому вам может потребоваться несколько ingress-объектов и хостов, один для протокола HTTP, другой для GRPC. Подробнее смотрите здесь.

Установка CLI

Для Mac это всего лишь одна команда:

brew install argocd

Инициализация

После установки изначальный пароль совпадает с именем пода сервера. Мы можем войти в систему:

argocd login  (переадресация портов, служба балансировки нагрузки, ingress - на ваш выбор)

и изменить пароль:

argocd account update-password

Вы также можете войти в консоль пользовательского интерфейса со своим юзернеймом и паролем, чтобы прочувствовать:

Страница входа в пользовательский интерфейс Argo CD

Добавление кластеров

Вы можете управлять сразу несколькими кластерами внутри Argo CD, например, у вас могут быть разные кластеры для разных сред, таких как dev, test, staging, production или что-то еще.

По умолчанию кластер, в котором развернут Argo CD, уже настроен с помощью Argo CD:

Вы также можете увидеть список кластеров с помощью интерфейса командной строки:

argocd cluster list

Если вы хотите управлять другими кластерами, вы можете запустить:

argocd cluster add CONTEXTNAMECONTEXTNAME- имя kube контекста в вашей локальной конфигурации.

Helloworld-пример развертывания

Теперь мы можем попробовать создать приложение на Argo CD.

Версия TL;DR или версия Мне не нравится UI в этом разделе это одна команда:

argocd app create helloworld --repo https://github.com/ironcore864/go-hello-http.git --path helm --sync-policy automatic --dest-server https://kubernetes.default.svc --dest-namespace default --values values.yaml --values values.dev.yaml

Эта CLI-команда создаст приложение, развернет его и синхронизирует. Чтобы продемонстрировать простоту Argo CD, я буду делать то же самое в пользовательском интерфейсе, а именно:

Нажмите кнопку NEW APP в консоли пользовательского интерфейса:

Затем вам нужно ввести несколько параметров для задачи, например:

  • Application Name: имя этого приложения. Здесь я назову его просто helloworld

  • Project: вы можете выбрать default. Project (проект) это концепция внутри Argo CD, в рамках которой вы можете создать несколько приложений и связать каждое приложение с проектом

  • Sync policy: вы можете выбрать между Manual и Automatic (что даст вам настоящий GitOps). Здесь я выберу Automatic (обратите внимание, что здесь в пользовательском интерфейсе значение по умолчанию Manual).

Нажмите SYNC POLICY и выберите Automatic

Затем мы перейдем к SOURCE части. Нам нужно указать URL-адрес git. Я собираюсь использовать пример, расположенный по адресу. Если вы перейдете в этот репозиторий, вы обнаружите, что там нет ничего, кроме простого Golang приложения с папкой с именем helm, которая представляет собой диаграмму для развертывания с несколькими файлами значений.

После того, как вы ввели URL-адрес git-репозитория, кликните часть PATH, и вы обнаружите, что Argo CD уже автоматически обнаружил, что у нас есть папка helm в этом репозитории, которая содержит вещи, которые могут нас заинтересовать:

Кликните Path и выберите имя папки helm в раскрывающемся меню.

Итак, здесь мы просто кликаем раздел Path и выбираем папку helm.

Стоит отметить, что Argo CD поддерживает несколько инструментов для развертывания. Сам Argo CD не предвзят; он позволяет использовать собственный YAML k8s, или kustomize, или helm. Например, если файлы в Path представляют собой схему управления, Argo CD знает, что нужно запустить установку Helm; но если это просто файлы YAML k8s, Argo CD знает, что вместо этого нужно запустить kubectl apply. Умно, не правда ли?

В разделе Destination нам нужно выбрать, в каком кластере Kubernetes развернуть это приложение (выбор из раскрывающегося списка) и в каком пространстве имен (введите текст).

Щелкните URL-адрес кластера, выберите кластер для развертывания и введите пространство имен.

Поскольку в этом примере в нашей папке helm есть диаграмма, Argo CD автоматически загружает новый раздел с именем Helm, чтобы попросить вас выбрать, какой файл значений, который нужно применить:

Кликните раздел VALUES FILES, и вы можете выбрать один или несколько файлов из списка, который выбирается из Path, настроенного ранее.

Это раскрывающееся меню, вам даже не нужно вводить имена файлов. Эта часть хорошо интегрирована с helm причина, по которой я предпочитаю создавать приложение в пользовательском интерфейсе, хотя то же самое можно сделать с помощью одной единственной CLI-команды.

Здесь мы указали два файл со значениями, один по умолчанию, другой для dev стейджа, и в нем есть значения, которые переопределяют значения, определенные по умолчанию.

После нажатия кнопки Create Argo CD синхронизирует (sync) статус, определенный в git-репозитории, и если текущее состояние не совпадает с тем, что определено в git, Argo CD синхронизирует его и переведет состояние в то же, что определено в git. Через некоторое время вы увидите, что приложение синхронизировано, и какие компоненты развернуты:

Приложение синхронизированоПриложение синхронизированоПодробное представление приложенияПодробное представление приложения

В этом примере у нас есть развертывание, в котором развернут только 1 под (значения из values.dev.yaml переопределяет 3 пода по умолчанию, определенные в файле values.yaml), и сервис, раскрывающий развертывание. Теперь, если мы перейдем к целевому кластеру и проверим, он действительно уже развернут:

Приложение действительно развернуто, без шутокПриложение действительно развернуто, без шуток

Это демонстрирует весь процесс, в котором вы создаете приложение и развертываете его. Весь процесс занимает около 1 минуты, без написания bash-скриптов для cd в какую-либо папку, а затем для установки helm, или, что еще хуже, если у вас нет подходящего образа с helm, необходимости его собрать или найти.

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

GitHub SSO

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

После установки Argo CD имеет одного встроенного администратора, который имеет полный доступ к системе. Рекомендуется использовать пользователя с правами администратора только для изначальной настройки, а затем переключиться на локальных пользователей или настроить SSO-интеграцию.

Вы можете создать локального пользователя в Argo CD, но вы, вероятно, захотите настроить SSO.

Argo CD включает и поставляет Dex как часть установочного комплекта с целью делегирования аутентификации внешнему поставщику идентификации. Поддерживаются несколько типов поставщиков идентификации (OIDC, SAML, LDAP, GitHub и т. Д.). Для настройки единого входа (SSO) на Argo CD необходимо отредактировать файл конфигурации argocd-cm с настройками Dex-коннектора. После регистрации нового OAuth приложения в git вы можете отредактировать configmap argocd-cm, чтобы добавить следующие значения:

data:  url: https://argocd.example.com  dex.config: |    connectors:      # GitHub example      - type: github        id: github        name: GitHub        config:          clientID: aabbccddeeff00112233          clientSecret: $dex.github.clientSecret          orgs:          - name: your-github-org      # GitHub enterprise example      - type: github        id: acme-github        name: Acme GitHub        config:          hostName: github.acme.com          clientID: abcdefghijklmnopqrst          clientSecret: $dex.acme.clientSecret          orgs:          - name: your-github-org

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

GitHub SSO (здесь в примере корпоративный git)GitHub SSO (здесь в примере корпоративный git)

Если вы запустите GitHub SSO с новым пользователем, входящим в систему, он не увидит список кластеров или только что созданное приложение это из-за функций RBAC, позволяющих ограничивать доступ к ресурсам Argo CD. После того, как мы уже включили SSO, мы можем настроить RBAC, создав следующую configmap argocd-rbac-cm:

apiVersion: v1kind: ConfigMapmetadata:  name: argocd-rbac-cm  namespace: argocddata:  policy.default: role:readonly  policy.csv: |    p, role:org-admin, applications, *, */*, allow    p, role:org-admin, clusters, get, *, allow    p, role:org-admin, repositories, get, *, allow    p, role:org-admin, repositories, create, *, allow    p, role:org-admin, repositories, update, *, allow    p, role:org-admin, repositories, delete, *, allow    g, your-github-org:your-team, role:org-admin

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

Заключение

Установка, инициализация, управление (кластер, SSO, пользователь, RBAC), использование (создание приложения, развертывание) довольно просты. Я настоятельно рекомендую вам попробовать, в любом случае это займет у вас не больше часа. И я гарантирую, что вам понравится его простота и то, что он может вам принести.


Узнать подробнее о курсе Инфраструктурная платформа на основе Kubernetes.

Смотреть открытый вебинар Работа с NoSQL базами в k8s (на примере Apache Cassandra).

Подробнее..

Выступает DMN, дирижирует ZeeBe как использовать бизнес-правила в микросервисах

11.03.2021 18:13:13 | Автор: admin

Меня зовут Николай Первухин, я Senior Java Developer в Райффайзенбанке. Так сложилось, что, единожды попробовав бизнес-процессы на Camunda, я стал адептом этой технологии и стараюсь ее применять в проектах со сложной логикой. Действительно сама идея подкупает: рисуешь процесс в удобном GUI-редакторе (моделлере), а фреймворк выполняет эти действия по порядку, соблюдая большой спектр элементов нотации BPMN.

К тому же в Camunda есть встроенная поддержка еще одной нотации DMN (Decision Model and Notation): она позволяет в простой и понятной форме создавать таблицы принятия решений по входящим наборам данных.

Но чего-то все же не хватает... Может, добавим немного скорости?

Почему ускоряем процессы

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

Что обычно характеризует такие процессы:

  • от момента создания до завершения процесса может пройти несколько дней;

  • участвует большое количество сотрудников;

  • осуществляется интеграция со множеством банковских подсистем.

Фреймворк Camunda прекрасно справляется с такими задачами, однако, заглянем под капот: там находитсяклассическая база данных для осуществления транзакционности.

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

Отличные новости: воспользуемся ZeeBe

В июле 2019 года было официально объявлено, что после двух лет разработки фреймворк ZeeBe готов к использованию на боевой среде. ZeeBe специально разрабатывался под задачи highload и, по утверждению автора, был протестирован при 10 000 процессов в секунду. В отличие от Camunda, ядро фреймворка ZeeBe принципиально не использует базу данных из него убраны все вспомогательные подсистемы, в том числе и процессор правил DMN.

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

Итак, дано:

  • микросервис, инициирующий событие и запускающий процесс (event-handler);

  • микросервис обработки бизнес-правил (rules-engine);

  • микросервис, эмулирующий действия (action).

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

Из оркестрации у нас:

  • микросервис с брокером сообщений ZeeBe (zeebe);

  • микросервис визуализации работающих процессов simplemonitor (zeebe-simple-monitor).

А присматривать за всеми микросервисами будет кластер k8s.

Схема взаимодействия

С точки зрения бизнес-логики в примере будет рассмотрен следующий бизнес-сценарий:

  • из внешней системы происходит запрос в виде rest-обращения с передачей параметров;

  • запускается бизнес-процесс, который пропускает входящие параметры через бизнес-правило;

  • в зависимости от полученного решения из бизнес-правил запускается микросервис действий с различными параметрами.

Теперь поговорим подробнее о каждом микросервисе.

Микросервис zeebe

Данный микросервис состоит из брокера сообщений ZeeBe и экспортера сообщений для отображения в simple-monitor. Для ZeeBe используется готовая сборка, которую можно скачать с github. Подробно о сборке контейнера можно посмотреть в исходном коде в файле build.sh

Принцип ZeeBe минимальное число компонентов, входящих в ядро, поэтому по умолчанию ZeeBe это брокер сообщений, работающий по схемам BPMN. Дополнительные модули подключаются отдельно: например, для отображения процессов в GUI понадобится экспортер (доступны разные экспортеры, к примеру, в ElasticSearch, в базу данных и т.п.).

В данном примере возьмемэкспортер в Hazelcast. И подключим его:

  • добавим zeebe-hazelcast-exporter-0.10.0-jar-with-dependencies.jar в папку exporters;

  • добавим в файл config/application.yamlследующие настройки:

exporters:      hazelcast:        className: io.zeebe.hazelcast.exporter.HazelcastExporter        jarPath: exporters/zeebe-hazelcast-exporter-0.10.0-jar-with-dependencies.jar        args:          enabledValueTypes: "JOB,WORKFLOW_INSTANCE,DEPLOYMENT,INCIDENT,TIMER,VARIABLE,MESSAGE,MESSAGE_SUBSCRIPTION,MESSAGE_START_EVENT_SUBSCRIPTION"          # Hazelcast port          port: 5701

Данные активных процессов будут храниться в памяти, пока simplemonitor их не считает. Hazelcast будет доступен для подключения по порту 5701.

Микросервис zeebe-simplemonitor

Во фреймворке ZeeBe есть две версии GUI-интерфейса. Основная версия, Operate, обладает большим функционалом и удобным интерфейсом, однако, использование Operate ограничено специальной лицензией (доступна только версия для разработки, а лицензию для прода следует запрашивать у производителя).

Также есть облегченный вариант simplemonitor (лицензируется по Apache License, Version 2.0)

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

Можно выбрать любую базу данных Spring Data JDBC, в данном примере используется файловая h2, где настройки, как и в любом Spring Boot приложении, вынесены в application.yml

spring:  datasource:    url: jdbc:h2:file:/opt/simple-monitor/data/simple-monitor-db;DB_CLOSE_DELAY=-1

Микросервис event-handler

Это первый сервис в цепочке, он принимает данные по rest и запускает процесс.При старте сервис осуществляет поиск файлов bpmn в папке ресурсов:

private void deploy() throws IOException {        Arrays.stream(resourceResolver.getResources("classpath:workflow/*.bpmn"))                .forEach(resource -> {                    try {                        zeebeClient.newDeployCommand().addResourceStream(resource.getInputStream(), resource.getFilename())                                .send().join();                        logger.info("Deployed: {}", resource.getFilename());                    } catch (IOException e) {                        logger.error(e.getMessage(), e);                    }                });    }

Микросервис имеет endpoint, и для простоты принимает вызовы по rest. В нашем примере передаются 2 параметра, сумма и лимит:

http://адрес-сервиса:порт/start?sum=100&limit=500
@GetMapping    public String getLoad(@RequestParam Integer sum, @RequestParam Double limit) throws JsonProcessingException {        Map<String, Object> variables = new HashMap<>();        variables.put("sum", sum);        variables.put("limit", limit);        zeebeService.startProcess(processName, variables);        return "Process started";    }

Следующий код отвечает за запуск процесса:

 public void startProcess(String processName, Map<String, Object> variables) throws JsonProcessingException {         zeebeClient.newCreateInstanceCommand()                .bpmnProcessId(processName)                .latestVersion()                .variables(variables)                .send();    }

Сам процесс нарисован в специальной программе ZeeBe modeler (почти копия редактора Camunda modeler) и сохраняется в формате bpmn в папке workflow в ресурсах микросервиса. Графически процессвыглядит как:

У каждой задачи (обозначаем прямоугольником на схеме) есть свой тип задач, который устанавливается в свойствах задачи, например, для запуска правил:

Каждый дополнительный микросервис в данном примере будет использовать свой тип задач. Тип задач очень похож на очередь в Kafka: при возникновении задач к нему могут подключаться подписчики workerы.

После старта процесс продвинется на один шаг, и появится сообщение типа DMN.

Микросервис rules-engine

Благодаря прекрасной модульной архитектуре Camunda есть возможность использовать в своем приложении (отдельно от самого фреймворка Camunda) движок правил принятия решения.

Для его интеграции с вашим приложением достаточно добавить его в зависимости maven:

        <dependency>            <groupId>org.camunda.bpm.dmn</groupId>            <artifactId>camunda-engine-dmn</artifactId>            <version>${camunda.version}</version>        </dependency>

Сами правила создаются в специальном графическом редакторе Camunda modeler. Одна диаграмма DMN имеет два вида отображения.

Entity Relation Diagram (вид сверху) показывает зависимости правил друг от друга и от внешних параметров:

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

Само же бизнес-правило содержит более детальный вид:

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

Посмотрим на примере, где такой файл располагается в папке dmn-models в ресурсах микросервиса. Для регистрации диаграммы при старте микросервиса происходит его однократная загрузка:

 public void init() throws IOException {        Arrays.stream(resourceResolver.getResources("classpath:dmn-models/*.dmn"))                .forEach(resource -> {                    try {                        logger.debug("loading model: {}", resource.getFilename());                        final DmnModelInstance dmnModel = Dmn.readModelFromStream((InputStream) Resources                                .getResource("dmn-models/" + resource.getFilename()).getContent());                        dmnEngine.parseDecisions(dmnModel).forEach(decision -> {                            logger.debug("Found decision with id '{}' in file: {}", decision.getKey(),                                    resource.getFilename());                            registry.put(decision.getKey(), decision);                        });                    } catch (IOException e) {                        logger.error("Error parsing dmn: {}", resource, e);                    }        });    }

Для того, чтобы подписаться на сообщения от ZeeBe, требуется осуществить регистрацию workerа:

private void subscribeToDMNJob() {        zeebeClient.newWorker().jobType(String.valueOf(jobWorker)).handler(                (jobClient, activatedJob) -> {                    logger.debug("processing DMN");                    final String decisionId = readHeader(activatedJob, DECISION_ID_HEADER);                    final Map<String, Object> variables = activatedJob.getVariablesAsMap();                    DmnDecisionResult decisionResult = camundaService.evaluateDecision(decisionId, variables);                    if (decisionResult.size() == 1) {                        if (decisionResult.get(0).containsKey(RESULT_DECISION_FIELD)) {                            variables.put(RESULT_DECISION_FIELD, decisionResult.get(0).get(RESULT_DECISION_FIELD));                        }                    } else {                        throw new DecisionException("Нет результата решения.");                    }                    jobClient.newCompleteCommand(activatedJob.getKey())                            .variables(variables)                            .send()                            .join();                }        ).open();    }

В данном коде осуществляется подписка на событие DMN, вызов модели правил при получении сообщения от ZeeBe и результат выполнения правила сохранятся обратно в бизнес-процесс в виде переменной result (константа RESULT_DECISION_FIELD).

Когда данный микросервис отчитывается ZeeBe о выполнении операции, бизнес-процесс переходит к следующему шагу, где происходит выбор пути в зависимости от выполнения условия, заданного в свойствах стрелочки:

Микросервис action

Микросервис action совсем простой. Он также осуществляет подписку на сообщения от ZeeBe, но другого типа action.

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

Также передачу параметров можно сделать и через закладку Input/Output, тогда параметры придут вместе с переменными процесса, но передача через headers является более каноничной.

Посмотрим на получение сообщения в микросервисе:

private void subscribe() {        zeebeClient.newWorker().jobType(String.valueOf(jobWorker)).handler(                (jobClient, activatedJob) -> {                    logger.debug("Received message from Workflow");                    actionService.testAction(                            activatedJob.getCustomHeaders().get(STATUS_TYPE_FIELD),                            activatedJob.getVariablesAsMap());                    jobClient.newCompleteCommand(activatedJob.getKey())                            .send()                            .join();                }        ).open();    }

Здесь происходит логирование всех переменных бизнес-процесса:

 public void testAction(String statusType, Map<String, Object> variables) {        logger.info("Event Logged with statusType {}", statusType);        variables.entrySet().forEach(item -> logger.info("Variable {} = {}", item.getKey(), item.getValue()));    }

Исходный код

Весь исходный код прототипа можно найти в открытом репозитории GitLab.

Компиляция образов Docker

Все микросервисы проекта собираются командой ./build.sh

Для каждого микросервиса есть различный набор действий, направленных на подготовку образов docker и загрузки этих образов в открытые репозитории hub.docker.com.

Загрузка микросервисов в кластер k8s

Для развертывания в кластере необходимо сделать следующие действия:

  1. Создать namespace в кластере kubectl create namespace zeebe-dmn-example

  2. Создать config-map общих настроек

kind: ConfigMapapiVersion: v1metadata:  name: shared-settings  namespace: zeebe-dmn-exampledata:  shared_servers_zeebe: <IP адрес кластера>

Далее создаем два персистентных хранилища для хранения данных zeebe и simplemonitor. Это позволит осуществлять перезапуск соответствующих подов без потери информации:

kubectl apply -f zeebe--sm-volume.yml

kubectl apply -f zeebe-volume.yml

Yml-файлы находятся в соответствующих проектах:

Теперь осталось последовательно создать поды и сервисы. Указанные yml-файлы находятся в корне соответствующих проектов.

kubctl apply -f zeebe-deployment.yml

kubctl apply -f zeebe-sm-deployment.yml

kubctl apply -f event-handler-deployment.yml

kubctl apply -f rules-engine-deployment.yml

kubctl apply -f action-deployment.yml

Смотрим, как отображаются наборы подов в кластере:

И мы готовы к тестовому запуску!

Запуск тестового процесса

Запуск процесса осуществляется открытием в браузере соответствующий URL. К примеру, сервис event-handler имеет сервис с внешним IP и портом 81 для быстрого доступа.

http://адрес-кластера:81/start?sum=600&limit=5000
Process started

Далее можно проверить отображение процесса в simplemonitor. У данного микросевиса тоже есть внешний сервис с портом 82.

Зеленым цветом выделен путь, по которому прошел процесс. Серым выделены выполненные задачи, а снизу отображены переменные процесса.

Теперь можно просмотреть лог микросервиса action, там можно увидеть значение переменной statusType, которое соответствует варианту прохождения процесса.

Поделюсь, какими ресурсами пользовался для подготовки прототипа

Небольшое послесловие вместо итогов

Из плюсов:

  • архитектура разработанного прототипа получилась гибкой и расширяемой. Можно добавлять любое количество микросервисов для обработки сложной логики;

  • простая нотация BPMN и DMN позволяет привлекать аналитиков и бизнес к обсуждению сложной логики;

  • Zeebe показал себя как очень быстрый фреймворк, задержки на получение/отправку сообщений практически отсутствуют;

  • Zeebe изначально разрабатывался для работы в кластере, в случае необходимости можно быстро нарастить мощности;

  • без ZeeBe Operate можно вполне обойтись, Simple-Monitor отвечает минимальным требованиям.

Из минусов:

  • хотелось бы иметь возможность редактирования DMN непосредственно в ZeeBe modeler (как это реализовано в Camunda), на данный момент, приходится использовать оба моделлера;

  • к сожалению, только в Enterprise версии Camunda есть возможность просмотра пути, по которому принималось решение:

Это очень полезная функция при отладке правил. В Community версии приходится добавлять дополнительное поле типа output для логирования, либо разработать свое решение визуализации.

При развертывании прототипа в реальных условиях в кластере k8s необходимо добавить ограничения по ресурсам (CPU и RAM), также нужно будет подобрать лучшую систему хранения исторических данных.

Где применять такие технологии:

  • как оркестрация внутри одной команды или продукта в виде перекладывания сложной логики на диаграммы BPMN / DMN;

  • в сфере, где идет потоковая обработка данных с большим количеством интеграций. В банке это может быть проведение или проверка транзакций, обработка данных из внешних систем или просто многоэтапная трансформация данных;

  • как частичная альтернатива существующего стека ESB или Kafka для интеграции между командами.

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

Подробнее..

Перевод Миграция с Docker на containerd в среде Kubernetes

05.04.2021 10:08:01 | Автор: admin


Kubernetes отказывается от Docker для выполнения контейнеров после версии 1.20. (Прим. переводчика: в декабре мы уже писали о том, как это изменение повлияет на задачи разработчиков и инженеров эксплуатации: Docker is deprecated и как теперь быть?)


Без паники. Контейнеры Docker все еще поддерживаются, но без dockershim/Docker слоя между Kubernetes и containerd, который будет удален, начиная с версии 1.22+.


Если вы используете Docker, нужно перейти на поддерживаемый интерфейс container runtime interface (CRI). Хорошим вариантом будет containerd он уже есть у вас на ноде Kubernetes, если вы работаете с Docker.


Дополнительное преимущество меньше издержек благодаря отсутствию dockershim и уровней преобразования Docker, как видно на иллюстрации.



Переход с dockershim на containerd CRI


Как мигрировать?


Сначала проверяем, какая среда запуска контейнеров (container runtime) у нас используется. Это можно сделать командой kubectl get nodes -o wide


Как видите, здесь у нас Docker.


NAME       STATUS   ROLES                  AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION                             CONTAINER-RUNTIMEk8s-cn01   Ready    control-plane,master   78m     v1.20.4   10.65.79.164   <none>        Ubuntu 20.04.2 LTS   5.4.0-67-generic                           docker://20.10.5k8s-wn01   Ready    <none>                 64m     v1.20.4   10.65.79.131   <none>        Ubuntu 20.04.2 LTS   5.4.0-67-generic                           docker://20.10.5k8s-wn02   Ready    <none>                 4m16s   v1.20.4   10.65.79.244   <none>        CentOS Linux 8       4.18.0-240.15.1.el8_3.centos.plus.x86_64   docker://20.10.5

kubectl get nodes -o wide


Проверим, есть ли у нас containerd CLI /usr/bin/ctr и неймспейс moby из Docker.


NAME LABELSmoby

просмотр неймспейсов


Можно посмотреть запущенные контейнеры в этом неймспейсе.


CONTAINER                                                           IMAGE    RUNTIME04f9500885c473c9cb2b4f8d09dc4feea5c24838519b9a01251011830bab16a2    -        io.containerd.runc.v257d4c75ab9947829228a087b857b203c48a9d1c83de0a1b49af3624fb08c9d33    -        io.containerd.runc.v2934c007a259018a5cbda56dd8e066a66f2c9cfcb8003e7f8d25833fe462582fd    -        io.containerd.runc.v294315822d8f8a05e1be5adb7e5c18add33cbf2604057100c87572b5fc55169cd    -        io.containerd.runc.v2dfa01906e845239c74a0b35d457e845382468dd9ad6e99dd0c16be30f8a23a2d    -        io.containerd.runc.v2

Просмотр списка контейнеров в неймспейсе moby


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


Cordon и drain


Выполняем cordon и drain для нод, чтобы перенести поды на другие ноды.


root@k8s-cn01:~# kubectl cordon k8s-wn01node/k8s-wn01 cordonedroot@k8s-cn01:~# kubectl drain k8s-wn01 --ignore-daemonsetsnode/k8s-wn01 already cordonedWARNING: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-9wnh4, kube-system/weave-net-pgptmevicting pod default/nginx-6799fc88d8-r44x9pod/nginx-6799fc88d8-r44x9 evictednode/k8s-wn01 evictedroot@k8s-cn01:~# kubectl get nodesNAME       STATUS                     ROLES                  AGE    VERSIONk8s-cn01   Ready                      control-plane,master   138m   v1.20.4k8s-wn01   Ready,SchedulingDisabled   <none>                 124m   v1.20.4k8s-wn02   Ready                      <none>                 64m    v1.20.4

Останавливаем сервисы


 systemctl stop kubelet  systemctl stop docker

остановка kubelet и docker


Удаляем Docker (по желанию)


Удалять Docker не обязательно, но так все будет понятнее, на диске освободится немного места, а риска ошибок будет меньше.


apt purge docker-ce docker-ce-cliORyum remove docker-ce docker-ce-cli

удаление docker


Конфигурация containerd


Отключим строку disabled_plugins в /etc/containerd/config.toml, чтобы CRI загрузился.


#disabled_plugins = ["cri"]

/etc/containerd/config.tom


Если для cotainerd нет файла конфигурации, создайте новый дефолтный файл.


containerd config default > /etc/containerd/config.toml

создание файла конфигурации


Перезапустим containerd.


systemctl restart containerd

Меняем среду запуска


Правим файл /var/lib/kubelet/kubeadm-flags.env и добавляем среду containerd во флаги --container-runtimeremote и --container-runtimeendpoint=unix:///run/containerd/containerd.sock"


Файл kubeadm-flags будет выглядеть как-то так:


KUBELET_KUBEADM_ARGS="--cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2--resolv-conf=/run/systemd/resolve/resolv.conf --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock"

/var/lib/kubelet/kubeadm-flags.env


Запускаем kubelet


Изменив среду запуска, запускаем сервис kubelet.


systemctl start kubelet

запуск kubelet


Проверяем


Запускаем kubectl get nodes -o wide и видим, что у измененной ноды новая среда запуска.


NAME       STATUS                   ROLES                  AGE    VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION                             CONTAINER-RUNTIMEk8s-cn01   Ready                    control-plane,master   131m   v1.20.4   10.65.79.164   <none>        Ubuntu 20.04.2 LTS   5.4.0-67-generic                           docker://20.10.5k8s-wn01   Ready,,SchedulingDisabled        <none>                 117m   v1.20.4   10.65.79.131         <none>        Ubuntu 20.04.2 LTS   5.4.0-67-generic                           containerd://1.4.4k8s-wn02   Ready             <none>                 57m    v1.20.4   10.65.79.244   <none>        CentOS Linux 8       4.18.0-240.15.1.el8_3.centos.plus.x86_64   docker://20.10.5

Измененная нода все еще имеет статус cordoned. Отменим его.


root@k8s-cn01:~# kubectl uncordon k8s-wn01node/k8s-wn01 uncordonedroot@k8s-cn01:~# kubectl get nodesNAME       STATUS   ROLES                  AGE    VERSIONk8s-cn01   Ready    control-plane,master   143m   v1.20.4k8s-wn01   Ready    <none>                 129m   v1.20.4k8s-wn02   Ready    <none>                 69m    v1.20.4

Если проверить неймспейсы на ноде сейчас, увидим k8s.io. Неймспейс moby теперь пуст, никакие контейнеры в нем не выполняются все мигрировало в k8s.io.


root@k8s-wn01:~# ctr namespaces listNAME   LABELSk8s.iomobyroot@k8s-wn01:~# ctr --namespace moby container listCONTAINER    IMAGE    RUNTIMEroot@k8s-wn01:~# ctr --namespace k8s.io container listCONTAINER                                                           IMAGE                                    RUNTIME08d4b5ca1f0ddd08fff7f64ea4eb12be66b8ec860d119565e553c84d16942d26    docker.io/weaveworks/weave-kube:2.8.1    io.containerd.runc.v21c9e3f61f542b12abb4b849075b084fb7fe3f69b89ce668d73022c2cd647bcd1    k8s.gcr.io/pause:3.2                     io.containerd.runc.v21f8e0c5a6f8219de1c8f25bbb28d5d5c71b74e9ccfb9620007701847d29f23a2    k8s.gcr.io/kube-proxy:v1.20.4            io.containerd.runc.v239296ebd6017a7c83cd58004c94708c927f10a996a4e6ba0bbf003c6713fe713    docker.io/weaveworks/weave-kube:2.8.1    io.containerd.runc.v267f812954f46fa5c1f6ab39e681e2481f868309f28bd1b8ba44cce53f5c0071c    docker.io/weaveworks/weave-npc:2.8.1     io.containerd.runc.v29caed1d57d40cedef736e45adf550eda6a0befd32712e5b6af5d711681ba71f0    k8s.gcr.io/pause:3.2                     io.containerd.runc.v2

просмотр нового неймспейса k8s.io


Мы успешно изменили CRI, теперь можно повторить все то же самое для следующей ноды.

Подробнее..

Деплоим проект на Kubernetes в Mail.ru Cloud Solutions. Часть 1 архитектура приложения, запуск Kubernetes и RabbitMQ

07.04.2021 18:21:37 | Автор: admin

О Kubernetes и его роли в построении микросервисных приложений известно, пожалуй, большинству современных IT-компаний. Однако при его внедрении часто возникает вопрос какой вариант установки выбрать: Self-Hosted или Managed-решение от одного из облачных провайдеров. О недостатках первого варианта, думаю, известно всем, кто проходил через ручное конфигурирование K8s: сложно и трудоемко. Но в чем лучше Cloud-Native подход?

Я Василий Озеров, основатель агентства Fevlake и действующий DevOps-инженер (опыт в DevOps 8 лет), покажу развертывание Kubernetes-кластера на базе облака Mail.ru Cloud Solutions. В этом цикле статей мы создадим MVP для реального приложения, выполняющего транскрибацию видеофайлов из YouTube.

На его базе мы посмотрим все этапы разработки Cloud-Native приложений на K8s, включая проектирование, кодирование, создание и автомасштабирование кластера, подключение базы данных и S3-бакетов, построение CI/CD и даже разработку собственного Helm-чарта. Надеюсь, этот опыт позволит вам убедиться, что работа с K8s может быть по-настоящему удобной и быстрой.

В первой части статьи мы выберем архитектуру приложения, напишем API-сервер, запустим Kubernetes c балансировщиком и облачными базами, развернем кластер RabbitMQ через Helm в Kubernetes.

Также записи всех частей практикума можно посмотреть: часть 1, часть 2, часть 3.

Выбор архитектуры приложения

Определимся с архитектурой будущего приложения. В первую очередь нам потребуется API, к которому будет обращаться клиентское приложение. Будем использовать стандартные форматы: HTTPS и JSON. В JSON необходимо передавать URL видео, а также некоторый идентификатор или уникальное имя запроса для возможности отслеживания его статуса.

Следующий необходимый компонент очередь сообщений. Очевидно, что обработку видео не получится проводить в real-time режиме. Поэтому будем использовать RabbitMQ для асинхронной обработки.

Далее нам потребуются обработчики, которые будут читать сообщения из очереди и заниматься непосредственной конвертацией запрошенных видео в текст. Назовем их Worker. Для транскрибации будем использовать не внешнее API, а какую-нибудь библиотеку, установленную локально. Так как для этого потребуются ресурсы, обязательно настроим автомасштабирование в кластере, чтобы число обработчиков изменялось пропорционально количеству сообщений в очереди.

Для сохранения текстовых расшифровок видео, которые будут формировать обработчики Worker, потребуется хранилище. Будем использовать S3, которое идеально подходит для хранения неструктурированных данных в облаке.

Наконец, чтобы иметь возможность получать статус обработки запросов, их необходимо где-то сохранять. Для этого выберем обычную базу PostgreSQL.

Сценарий взаимодействия выбранных компонентов включает в себя следующие шаги:

  1. Клиент отправляет на API-сервер запрос POST, передавая в теле запроса имя и URL видео на YouTube, которое необходимо перевести в текст.

  2. API-сервер формирует сообщение с полученными параметрами и передает его в очередь RabbitMQ.

  3. API-сервер сохраняет информацию о полученном запросе на конвертацию видео в базе данных PostgreSQL. Статус обработки запроса по умолчанию равен false.

  4. API-сервер информирует клиента об успешном завершении операции. Клиент может продолжать свою работу, не дожидаясь конвертации видео.

  5. Свободный обработчик Worker извлекает сообщение из очереди RabbitMQ.

  6. Получив сообщение, Worker выполняет его обработку: загружает видео по указанному URL, получает из него аудио и переводит при помощи стороннего ПО в текст.

  7. Обработав видео, Worker сохраняет транскрипт видео в хранилище S3.

  8. Worker отправляет в API-сервер информацию об успешной обработке запроса с исходным именем. В запросе передается статус обработки, равный true, и ссылка на текстовый файл в S3. Endpoint для отправки статуса обработки запросов можно либо жестко прописывать в environment-переменных обработчика Worker, либо передавать его в теле сообщений наряду с другими параметрами. В нашем MVP будет реализован первый вариант. То есть обработчикам будет известно, какой API вызвать для обновления статуса запросов.

  9. API-сервер обновляет полученную от Worker информацию о запросе в базе данных PostgreSQL. Альтернативный вариант можно настроить обновление базы данных непосредственно из обработчиков Worker, однако это потребует знания структуры БД с их стороны, что чревато проблемами при миграциях БД. Поэтому в нашем приложении взаимодействие с БД будет происходить исключительно через API-сервер.

  10. Клиент спустя некоторое время после отправки исходного видео запрашивает статус его обработки, передавая в API-сервер имя исходного запроса.

  11. API-сервер извлекает данные о запросе из PostgreSQL по полученному имени.

  12. API-сервер получает информацию о запросе из PostgreSQL.

  13. API-сервер отправляет данные о запросе клиенту. Клиент получает статус обработки и URL, по которому сможет в дальнейшем загрузить транскрипт исходного видео из S3.

Упрощенная схема архитектуры будущего приложенияУпрощенная схема архитектуры будущего приложения

Настройка кластера Kubernetes в облаке MCS

Начинаем с создания кластера Kubernetes. Для этого в панели управления облаком MCS необходимо выбрать пункт меню Контейнеры Кластеры Kubernetes и добавить новый кластер.

На первом шаге настраивается конфигурация будущего кластера. Можно выбрать тип среды и один или несколько предустановленных сервисов. Мы выберем среду Dev и сразу добавим Ingress Controller Nginx для управления внешним доступом к кластеру:

На следующем шаге вводим название кластера и выбираем тип виртуальной машины для ноды Master. Оставим стандартную конфигурацию с 2 CPU и 4 ГБ памяти. Далее можно указать зону доступности мы оставим для нее автоматическое заполнение:

Далее на этом же шаге выбирается тип и размер диска. Нам достаточно HDD размером 20 Гб. Оставляем одну Master-ноду, выбираем предварительно добавленную подсеть и назначаем внешний IP для удобного доступа к кластеру извне:

На следующем шаге создаются группы рабочих узлов. В рамках проекта нам потребуются две группы. Сейчас создадим первую для развертывания API и RabbitMQ, а впоследствии добавим еще одну, для обработчиков Worker.

Вводим название группы узлов и указываем конфигурацию: 2 CPU и 4ГБ памяти. Для зоны доступности вновь выбираем автоматический выбор:

Чтобы обеспечить работу RabbitMQ, выбираем более производительный тип дисков SSD размером 50 ГБ. Оставляем один узел, автомасштабирование пока не указываем его рассмотрим позднее на примере другой группы узлов:

На последнем шаге запускается процесс формирования кластера, который может занять некоторое время: от 5 до 20 минут.

При успешном добавлении кластера на экране отобразится информация о его параметрах:

Для последующей работы с кластером необходимо:

  1. Установить локальный клиент kubectl и запустить его.

  2. Экспортировать в локальный клиент конфигурационный файл созданного кластера с расширением .yaml командой export KUBECONFIG=<путь к файлу>.

  3. Для безопасного подключения к кластеру запустить proxy-сервер командой kubectl proxy.

Эта инструкция отображается под списком параметров кластера после его добавления.

У нас kubectl установлен поэтому берем из загрузок сформированный конфигурационный файл kub-vc-dev_kubeconfig.yaml и экспортируем его в kubectl:

После экспорта конфигурационного файла можно убедиться в работоспособности кластера:

  1. Сначала смотрим доступные контексты: kubectl config get-contexts

    Видим, что у нас создался кластер kub-vc-dev:

  2. Смотрим доступные ноды: kubectl get nodes

    В кластере создались две ноды master и workload:

  3. Смотрим доступные Namespace: kubectl get ns

    Получаем ответ:

  4. Смотрим доступные поды: kubectl -n ingress-nginx get pods

    В Namespace ingress-nginx запущены поды для Nginx Controller:

  5. Смотрим доступные сервисы: kubectl -n ingress-nginx get svс

В списке сервисов также отображается Nginx Controller, для которого указан внешний адрес, который мы сможем прописывать в DNS, чтобы попадать в наши сервисы извне:

Разработка API-сервера на Go

Следующий шаг написать API для отправки запросов на конвертацию видео и получения статуса их обработки. С полной версией исходного кода можно ознакомиться здесь.

Ниже отображена структура проекта. Это стандартное Go-приложение. В файлах go.mod, go.sum описываются зависимости, в папке migrations миграции для базы данных PostgreSQL. В main.go содержится основная логика программы, в requests.go реализация API на добавление, редактирование, удаление и выборку запросов. И есть Dockerfile.

Структура API-сервераСтруктура API-сервера

Остановимся подробнее на содержимом main.go.

Вначале импортируем нужные зависимости. В первую очередь, это migrate для автоматического осуществления миграций, database/sql для работы с базами данных, go-env для работы с переменными окружения, web-фреймворк Gorilla и AMQP для работы с RabbitMQ:

package mainimport (    "encoding/json"    "os"    "github.com/golang-migrate/migrate/v4"    "github.com/golang-migrate/migrate/v4/database/postgres"    _ "github.com/golang-migrate/migrate/v4/source/file"    "database/sql"    env "github.com/Netflix/go-env"    _ "github.com/lib/pq"    "log"    "net/http"    "github.com/gorilla/handlers"    "github.com/gorilla/mux"    "github.com/streadway/amqp")

Далее идут environment, которые мы будем использовать. PGSQL_URI и RABBIT_URI нужны для того, чтобы подключиться к PostgreSQL и RabbitMQ соответственно, LISTEN номер порта, на котором необходимо слушать входящие запросы:

type environment struct {    PgsqlURI  string `env:"PGSQL_URI"`    Listen    string `env:"LISTEN"`    RabbitURI string `env:"RABBIT_URI"`}

Далее следует функция main, которая занимается инициализацией. Сначала происходит чтение environment-переменных, подключение к базе данных PostgreSQL и запуск миграций:

func main() {var err error// Getting configurationlog.Printf("INFO: Getting environment variables\n")cnf := environment{}_, err = env.UnmarshalFromEnviron(&cnf)if err != nil {    log.Fatal(err)}// Connecting to databaselog.Printf("INFO: Connecting to database")db, err = sql.Open("postgres", cnf.PgsqlURI)if err != nil {    log.Fatalf("Can't connect to postgresql: %v", err)}// Running migrationsdriver, err := postgres.WithInstance(db, &postgres.Config{})if err != nil {    log.Fatalf("Can't get postgres driver: %v", err)}m, err := migrate.NewWithDatabaseInstance("file://./migrations", "postgres", driver)if err != nil {    log.Fatalf("Can't get migration object: %v", err)}m.Up()

Затем следует подключение к RabbitMQ и инициализация работы с ним:

// Initialising rabbit mq// Initing rabbitmqconn, err := amqp.Dial(cnf.RabbitURI)if err != nil {    log.Fatalf("Can't connect to rabbitmq")}defer conn.Close()ch, err = conn.Channel()if err != nil {    log.Fatalf("Can't open channel")}defer ch.Close()err = initRabbit()if err != nil {    log.Fatalf("Can't create rabbitmq queues: %s\n", err)}

И в завершение запускается web-сервер. При этом каждому из возможных API-запросов сопоставляется функция обработки, описанная в отдельном файле requests.go:

// Setting handlers for querylog.Printf("INFO: Starting listening on %s\n", cnf.Listen)router := mux.NewRouter().StrictSlash(true)// PROJECTSrouter.HandleFunc("/requests", authMiddleware(getRequests)).Methods("GET")router.HandleFunc("/requests", authMiddleware(addRequest)).Methods("POST")router.HandleFunc("/requests/{name}", authMiddleware(getRequest)).Methods("GET")router.HandleFunc("/requests/{name}", authMiddleware(updRequest)).Methods("PUT")router.HandleFunc("/requests/{name}", authMiddleware(delRequest)).Methods("DELETE")http.ListenAndServe(cnf.Listen, handlers.LoggingHandler(os.Stdout, router))

Далее следует аутентификация в сильно упрощенном варианте, так как на стадии MVP этого достаточно. Разумеется, при разработке Enterprise-решений указание токенов и прочих переменных в явном виде неприемлемо:

func authMiddleware(next http.HandlerFunc) http.HandlerFunc {    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {        tokenString := r.Header.Get("X-API-KEY")        if tokenString != "804b95f13b714ee9912b19861faf3d25" {            w.WriteHeader(http.StatusUnauthorized)            w.Write([]byte("Missing Authorization Header\n"))            return        }        next(w, r)    })}

Переходим к инициализации RabbitMQ. Тут мы будем использовать два Exchange и три очереди.

Первый Exchange VideoParserExchange. К нему подключены две очереди:

  • VideoParserWorkerQueue это основная очередь, которую будут слушать обработчики (на иллюстрации для примера приведен один обработчик Worker-0).

  • VideoParserArchiveQueue архивная очередь, в которую дублируются сообщения на случай возникновения ошибок. Вместо нее можно использовать другие средства бэкапирования, например хранилище S3.

У VideoParserExchange тип fanout, это значит, что все сообщения из него будут отправляться во все подключенные очереди одновременно.

Второй Exchange VideoParserRetryExchange, к нему подключена очередь VideoParserWorkerRetryQueue. К ней не подключены обработчики.

Архитектура очередей сообщенийАрхитектура очередей сообщений

Цель такого решения отложить попытки отправки сообщений на вышедшие из строя Worker до момента, когда они с большей долей вероятности смогут вернуться к обработке.

Например, если во время обработки сообщения из основной очереди обработчик по какой-то причине отключится и не обработает сообщение, то оно отправится в VideoParserRetryExchange. Этот переход настроен при помощи параметра x-dead-letter-exchange.

Далее VideoParserRetryExchange отправит сообщение в очередь VideoParserWorkerRetryQueue. В ней при помощи параметра x-message-ttl ограничено время хранения сообщения. Также при помощи параметра x-dead-letter-exchange мы указываем, что по прошествии таймаута сообщение должно вернуться в VideoParserExchange для последующей обработки.

Алгоритм работы очередей сообщенийАлгоритм работы очередей сообщений

Вся эта логика описана в функции initRabbit. Сначала мы объявляем два Exchange:

func initRabbit() error {    err := ch.ExchangeDeclare(        "VideoParserExchange", // name        "fanout",              // type        true,                  // durable        false,                 // auto delete        false,                 // internal        false,                 // no wait        nil,                   // arguments    )    if err != nil {        return err    }    err = ch.ExchangeDeclare(        "VideoParserRetryExchange", // name        "fanout",                   // type        true,                       // durable        false,                      // auto delete        false,                      // internal        false,                      // no wait        nil,                        // arguments    )    if err != nil {        return err    }

Далее инициализируются три очереди:

args := amqp.Table{"x-dead-letter-exchange": "VideoParserRetryExchange"}    queue, err = ch.QueueDeclare(        "VideoParserWorkerQueue", // name        true,                     // durable - flush to disk        false,                    // delete when unused        false,                    // exclusive - only accessible by the connection that declares        false,                    // no-wait - the queue will assume to be declared on the server        args,                     // arguments -    )    if err != nil {        return err    }    args = amqp.Table{"x-dead-letter-exchange": "VideoParserExchange", "x-message-ttl": 60000}    queue, err = ch.QueueDeclare(        "VideoParserWorkerRetryQueue", // name        true,                          // durable - flush to disk        false,                         // delete when unused        false,                         // exclusive - only accessible by the connection that declares        false,                         // no-wait - the queue will assume to be declared on the server        args,                          // arguments -    )    if err != nil {        return err    }    queue, err = ch.QueueDeclare(        "VideoParserArchiveQueue", // name        true,                      // durable - flush to disk        false,                     // delete when unused        false,                     // exclusive - only accessible by the connection that declares        false,                     // no-wait - the queue will assume to be declared on the server        nil,                       // arguments -    )    if err != nil {        return err    }

И далее очереди связываются с соответствующими Exchange: VideoParserExchange с очередями VideoParserWorkerQueue и VideoParserArchiveQueue, а VideoParserRetryExchange с очередью VideoParserWorkerRetryQueue:

err = ch.QueueBind("VideoParserWorkerQueue", "*", "VideoParserExchange", false, nil)    if err != nil {        return err    }    err = ch.QueueBind("VideoParserArchiveQueue", "*", "VideoParserExchange", false, nil)    if err != nil {        return err    }    err = ch.QueueBind("VideoParserWorkerRetryQueue", "*", "VideoParserRetryExchange", false, nil)    if err != nil {        return err    }    return nil}

Переходим к файлам миграций БД. Они находятся в отдельной папке migrations:

Devices_up.sql предназначен для создания таблицы requests. В ней содержатся следующие поля:

  • id уникальный идентификатор запроса;

  • name уникальное имя, которое мы будем передавать в API при создании нового запроса и в дальнейшем использовать его для поиска нужного запроса;

  • description описание запроса;

  • video_url ссылка на исходное видео на YouTube, в котором необходимо распарсить текст;

  • text_url ссылка на место хранения результирующего текстового файла в S3;

  • processed логический признак того, что обработка запроса успешно завершена;

  • archived логический признак того, что запись таблицы архивирована. Будем использовать вместо физического удаления для сохранения истории;

  • created_at, updated_at временные метки для сохранения времени создания и последнего редактирования, соответственно.

Итак, создаем таблицу requests:

CREATE TABLE IF NOT EXISTS requests (    id SERIAL,    name VARCHAR(256),    description VARCHAR(2048),    video_url VARCHAR(64),    text_url VARCHAR(64),    processed BOOL DEFAULT FALSE,    archived BOOL DEFAULT FALSE,    created_at TIMESTAMP DEFAULT now(),    updated_at TIMESTAMP DEFAULT null,    UNIQUE(name));

В devices_down.sql описывается удаление таблицы requests:

DROP TABLE requests;

Переходим к файлу requests.go. В нем содержатся функции, которые обрабатывают запросы:

  • addRequest для добавления запроса;

  • updRequest для редактирования запроса;

  • delRequest для удаления запроса;

  • getRequest для получения запроса по имени;

  • getRequests для получения всех запросов.

Все функции довольно простые, в них выполняется проверка входных данных и отправка SQL-запроса в PostgreSQL. Поэтому приведем только фрагмент кода основной функции addRequest. Остальные функции можно посмотреть по ссылке выше.

Здесь происходит попытка отправить сообщение в VideoParserExchange, вывод сообщения в случае ошибки и добавление новой записи в таблицу requests, рассмотренную выше:

func addRequest(w http.ResponseWriter, r *http.Request) {    // Parsing event    req := postRequestRequest{}    err := json.NewDecoder(r.Body).Decode(&req)    if err != nil {        log.Printf("WARNING: Can't parse incoming request: %s\n", err)        returnResponse(400, "Can't parse json", nil, w)        return    }    request := Request{}    if req.Name == nil {        returnResponse(400, "name can't be null", nil, w)        return    }    request.Name = *req.Name    if req.Description != nil {        request.Description = *req.Description    }    if req.Processed != nil {        request.Processed = *req.Processed    }    if req.VideoURL != nil {        request.VideoURL = *req.VideoURL    }    if req.TextURL != nil {        request.TextURL = *req.TextURL    }    // Publishing data to rabbitmq    msg, err := json.Marshal(request)    if err != nil {        log.Printf("ERROR: Marshaling request: %s\n", err)        returnResponse(500, "Can't marshal request ", nil, w)        return    }    err = ch.Publish(        "VideoParserExchange", // exchange        "",                    // routing key        false,                 // mandatory - could return an error if there are no consumers or queue        false,                 // immediate        amqp.Publishing{            DeliveryMode: amqp.Persistent,            ContentType:  "application/json",            Body:         msg,        })    if err != nil {        log.Printf("ERROR: Publishing to rabbit: %s\n", err)        returnResponse(500, "Can't publish to rabbit ", nil, w)        return    }    stmt := `INSERT INTO requests (name, description, processed, video_url, text_url) VALUES ($1, $2, $3, $4, $5) RETURNING id`    err = db.QueryRow(stmt, &request.Name, &request.Description, &request.Processed, &request.VideoURL, &request.TextURL).Scan(&request.ID)    if err != nil {        log.Printf("ERROR: Adding new request to database: %s\n", err)        returnResponse(500, "Can't add new request ", nil, w)        return    }    returnResponse(200, "Successfully added new request", nil, w)}

В завершение рассмотрим Dockerfile, с помощью которого можно собрать приложение. Здесь используется образ golang-alpine, выполняется статическая компиляция, затем берется чистый alpine, куда переносится приложение со всеми миграциями и необходимыми файлами:

FROM golang:1.15-alpine AS build# Installing requirementsRUN apk add --update git && \    rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*# Creating workdir and copying dependenciesWORKDIR /go/src/appCOPY . .# Installing dependenciesRUN go getENV CGO_ENABLED=0RUN go build -o api main.go requests.goFROM alpine:3.9.6RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories && \    apk add --update bash && \    rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*WORKDIR /appCOPY --from=build /go/src/app/api /app/apiCOPY ./migrations/ /app/migrations/CMD ["/app/api"]

Создание БД PostgreSQL в облаке MCS

Базу данных для хранения статуса обработки запросов на конвертацию видео будем создавать из консоли управления облаком MCS. Для этого нужно выбрать пункт меню Базы данных и добавить БД PostgreSQL:

На первом шаге определяется конфигурация. Выберем последнюю версию PostgreSQL и тип конфигурации Single: для среды Dev нам достаточно единичного инстанса:

На следующем шаге указываем имя инстанса БД и выбираем конфигурацию виртуальной машины. Нам достаточно 1 CPU и 2 ГБ памяти. Для зоны доступности оставляем автоматический выбор:

В качестве диска выберем SSD размером 20 ГБ. Сеть можно создать отдельную, мы возьмем текущую. Внешний IP назначать не будем: база будет во внутренней сети. В настройках Firewall при необходимости можно указать ограничения на доступ, нам пока они не нужны все разрешаем. Создание реплики нам также не нужно. Ключ для доступа по SSH создаем свой. И устанавливаем периодичность резервного копирования раз в сутки:

На следующем шаге указываем имя БД, имя пользователя и генерируем пароль:

Далее запускается процесс создания инстанса, который займет некоторое время. После успешного создания параметры БД будут выведены на экран, в том числе внутренний IP-адрес сети, который впоследствии нам понадобится:

Установка RabbitMQ через Helm в Kubernetes

Для установки RabbitMQ воспользуемся Helm-чартом bitnami/rabbitmq. Достоинство чартов в том, что не нужно устанавливать по отдельности все необходимые сервису ресурсы: можно установить их одновременно в рамках общего релиза. А при изменениях в любом из ресурсов можно вынести новый релиз, в котором все обновления будут собраны воедино.

Создадим папку helm, добавим в нее репозиторий bitnami и найдем нужный нам Helm Chart bitnami/rabbitmq:

mkdir helmcd helmhelm repo add bitnami https://charts.bitnami.com/bitnamihelm search repo bitnami

Теперь мы нашли нужный чарт:

Копируем его имя, загружаем и распаковываем:

helm pull bitnami/rabbitmqtar zxv

Переходим в папку rabbitmq/templates. Здесь находятся все ресурсы, которые нужно будет создать в Kubernetes для корректной работы RabbitMQ: конфигурация, Ingress, сертификаты, сетевые политики, сервисные аккаунты, секреты, правила Prometheus и так далее. И Helm позволяет это сделать единой командой, без установки каждого файла по отдельности:

Возвращаемся в родительскую папку helm, чтобы посмотреть возможность настройки файла values.yaml. Скопируем содержимое rabbitmq/values.yaml в наш собственный файл values.dev.yaml и откроем его для редактирования:

cp rabbitmq/values.yaml ./values.dev.yamlvi values.dev.yaml

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

В данном файле содержится очень много параметров, которые можно настраивать под нужды своего проекта: режим debug, плагины RabbitMQ для подключения, необходимость включения TLS и memoryHighWatermark, аутентификация через LDAP, количество реплик, nodeSelector для создания RabbitMQ на нодах с определенной меткой, требования к CPU и памяти и многое другое.

Нас в первую очередь интересуют настройки Ingress. Находим секцию ingress, устанавливаем в enabled значение true и прописываем в поле hostname имя rabbitmq.stage.kis.im. Эта настройка необходима для внешнего доступа к RabbitMQ, без нее он будет доступен только внутри кластера. Kis.im это мой существующий домен:

Далее переходим непосредственно к развертыванию RabbitMQ. Создаем новый namespace stage и применяем к нему созданный файл values.stage.yaml (изменив dev на stage в названии для единообразия):

kubectl create ns stagehelm instal -n stage rabbitmq -f values.dev.yamlmv values.dev.yaml values. stage. yamlhelm install -n stage rabbitmq -f values.stage.yanl ./rabbitmq/

Вот, что получилось, когда Namespace создан:

После успешной установки можно посмотреть список подов и сервисов в Namespace stage rabbitmq успешно добавлен. Он имеет кластерный IP 10.254.178.84. Но так как наше приложение будет находиться в том же Namespace, мы сможем обращаться к нему по имени rabbitmq.

Еще один сервис rabbitmq-headless не имеет кластерного IP. Он используется при добавлении нескольких RabbitMQ для их автообнаружения и объединения в кластер с помощью kubectl -n stage get svc:

С помощью Helm можно получить дополнительные сведения о релизе: время последнего обновления, статус, название чарта, версию приложения, используем helm -n stage list:

Кроме этого, можно посмотреть Persistent Volumes, выделенные RabbitMQ, с помощью kubectl get pv. В нашем случае Volume имеет размер 8 ГБ и Storage Class csi-hdd:

При необходимости нужный Storage Class можно было прописать непосредственно в YAML-файле:

Список всех возможных классов можно вывести командой kubectl get storageclasses:

Здесь важен параметр RECLAIMPOLICY: в зависимости от его значения при удалении запроса на данный ресурс (PVC, Persistent Volume Claim) сам Persistent Volume будет удален или сохранен для будущего использования.

Осталось обеспечить внешний доступ к нашему сервису. Проверяем добавление ресурса Ingress для RabbitMQ командой kubectl -n stage get ingress:

Затем получаем внешний адрес Ingress Controller с помощью kubectl -n ingress-nginx get svc:

В Cloudflare прописываем DNS для RabbitMQ, связывая его внешний Hostname и IP-адрес Ingress Controller:

После этого RabbitMQ становится доступен по адресу rabbitmq.stage.kis.im:

Имя пользователя user. Пароль сохранился в переменные окружения после развертывания RabbitMQ, его можно получить с помощью команды env | grep RABBITMQ_PASSWORD.

Развертывание и предварительная проверка API

RabbitMQ мы развернули с помощью Helm. Для нашего приложения с API в последующем мы также создадим собственный Helm Chart, но пока посмотрим, как выполняется развертывание приложения вручную на основе YAML-файлов.

Образ приложения мною уже создан при помощи Dockerfile, который мы рассматривали ранее.

Далее определим необходимые ресурсы. Очевидно, что локальное хранилище приложению не нужно, так как приложение уже взаимодействует с PostgreSQL и RabbitMQ, размещенными в облаке. Поэтому Persistent Volumes создавать не будем. Основные ресурсы, которые нам потребуются, описывают файлы deployment.yaml, ingress.yaml и svc.yaml:

Начнем с deployment.yaml. Здесь описывается ресурс Deployment. Тут мы описываем шаблон пода, который будем запускать. Указываем, что будем запускать контейнер с именем api, образ vozerov/video-api:v1 (этот образ я уже залил на hub.docker.com).

Далее в блоке env указываем переменные, используемые в нашем API:

  • В переменной RABBIT_URI вводим сформированные при создании RabbitMQ имя и пароль пользователя, название сервиса rabbitmq и номер порта 5672 (имя сервиса можно проверить с помощью команды kubectl -n stage get svc).

  • В переменной LISTEN устанавливаем номер порта 8080.

  • В переменной PGSQL_URI заполняем сформированные при создании PostgreSQL имя и пароль пользователя, внутренний адрес БД 10.0.0.10, номер порта 5432 и название БД vc-dev. Все параметры БД можно найти в консоли управления облаком.

deployment.yaml: описываем шаблон подаdeployment.yaml: описываем шаблон пода

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

Применяем сформированный файл:

kubectl -n stage apply -f deployment.yamlkubectl -n stage get deploy

Video-api создан:

И проверяем создание нового пода с помощью kubectl -n stage get pods:

После успешного применения deployment.yaml можно зайти в RabbitMQ и убедиться в создании всех необходимых очередей и Exchange.

Созданные очередиСозданные очередиСозданные ExchangeСозданные Exchange

Следующий ресурс, который нам необходимо добавить для доступа к сервису извне это Service. Он описывается в файле svc.yaml. Мы указываем, что приложение video-api будет принимать входящие соединения на порт 8080 и пробрасывать их в контейнер на порт 8080. Применяем svc.yaml стандартной командой kubectl apply -n stage -f svc.yaml:

Последний ресурс, который необходим для нашего сервиса Ingress. В файле ingress.yaml мы указываем правила, по которым нужно направлять запросы к сервису. Заполняем внешнее имя api.stage.kis.im и в блоке path указываем, что все корневые запросы направляем на сервис video-api-svc, созданный на прошлом шаге. Применяем сформированный файл kubectl apply -n stage -f Ingress.yaml:

Убеждаемся в добавлении Ingress для нашего сервиса с помощью kubectl -n stage get ingress:

Затем добавляем запись в DNS аналогично тому, как делали это ранее для RabbitMQ:

Теперь можно провести первое тестирование API, используя отправку запросов через curl. В заголовках всех запросов нужно передавать X-API-KEY со значением токена из кода программы main.go.

Для начала с помощью метода GET получим список всех записей requests:

curl -H 'X-API-KEY: 804b95f13b714ee9912b19861faf3d25' -s http://api.stage.kis.im/requests | jq .

На текущий момент он пуст:

Отправим новый запрос на конвертацию видео, используя метод POST. В имени запроса (name) укажем test1. В ссылке на видео (video_url) введем тестовое значение, так как у нас пока нет обработчиков Worker:

curl -X POST -d '{"name": "test1", "video_url": "https://google.com" }' -H 'X-API-KEY: 804b95f13b714ee9912b19861faf3d25' -s http://api.stage.kis.im/requests | jq .

Запрос успешно создан:

Далее можно получить запрос по имени test1 и убедиться в наличии всех переданных при создании параметров:

curl -H 'X-API-KEY: 804b95f13b714ee9912b19861faf3d25' -s http://api.stage.kis.im/requests/request1 | jq .

Запрос создан, все параметры верные:

В очереди RabbitMQ сообщение также будет добавлено. Заходим в очередь:

Видим сообщение:

Осталось зайти в базу PostgreSQL и проверить ее структуру. Внешний доступ мы не настраивали поэтому можно подключиться, например, через psql из отдельно запущенного пода. Мы видим наличие таблицы requests, а в ней добавленный нами запрос:

Таким образом, проверка работы API пройдена.

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

Новым пользователям платформы Mail.ru Cloud Solutions доступны 3000 бонусов после полной верификации аккаунта. Вы сможете повторить сценарий из статьи или попробовать другие облачные сервисы.

И обязательно вступайте в сообществоRebrain в Telegram там постоянно разбирают различные проблемы и задачи из сферы Devops, обсуждают вещи, которые пригодятся и на собеседованиях, и в работе.

Что еще почитать по теме:

  1. Как развернуть кластер Kubernetes на платформе MCS.

  2. Запускаем etcd-кластер для Kubernetes.

  3. Как устроен Kubernetes aaS на платформе Mail.ru Cloud Solutions.

Подробнее..

Kubernets 1.21 неожиданно много изменений

12.04.2021 16:18:43 | Автор: admin

Новая эмблема символизирует распределение членов команды выпуска релиза по земному шару от UTC-8 до UTC+8 (похоже, ни японцев, ни корейцев в команде нет). Эмблему нарисовал Aravind Sekar, независимый дизайнер из Индии. На мой взгляд, котики были круче.

Но давайте перейдем к чтению changelog и особенно моему любимому разделу Urgent Upgrade Notes.


CronJob

Сообщение в блоге гласит, что CronJob объявлены stable, но далее есть небольшое уточнение стабильным объявлена версия API, то есть структура манифеста kind: cronJob, а вот с контроллером, который и отвечает за реализацию логики работы, все намного интереснее.

В версии 1.20 был добавлен CronJob контроллер версии 2. В новой версии 1.21 его перевели в стадию бета и включили по умолчанию. В версии 1.22 планируется удалить код старого CronJob контроллера. Очень, очень быстрые изменения, обычно не свойственные циклам релизов в Kubernetes.

Причем в документации все Cronjob limitations, о которых я писал на Хабре, остались без изменений.

Зачем тогда делали новый контроллер, если все проблемы с кронджобами остались нерешенными? Ответ есть в этой статье старый контроллер излишне нагружал API Kubernetes и не успевал создавать Job, если в кластере было больше 1000 Cronjob манифестов. Новая версия контроллера написана согласно последним гайдлайнам и намного быстрее.

Immutable Secret and ConfigMap

Добавили возможность создавать защищенные от изменений секреты и конфиг мапы. Видимо, защита от джунов, которые "pushed bad configuration". На мой взгляд, ConfigMap надо деплоить через helm чарты, а секреты хранить в Vault. Там, где есть история изменений, а ваш CI/CD не должен позволять выкатывать нерабочие конфиги на прод.

IPv4/IPv6 Dual-Stack support

Поддержка IPv6 теперь включена по умолчанию, единственная тонкость ваш CNI также должен уметь в Dual-Stack. Calico умеет)

Graceful Node Shutdown

Kubelet научился определять ситуацию, когда узел выключается командой shutdown, и теперь посылает подам sigterm. TODO: Протестировать, не завершается ли container runtime быстрее kubelet, и что будет при простом systemctl shutdown kubelet.

PodSecurityPolicy Deprecation

Еще одна неоднозначная новость. PSP объявлены устаревшими и запланированы к удалению в версии 1.25. Но при этом PSP Replacement Policy (полиси для замены полиси) находятся в состоянии проекта, а альфа-версию обещают показать только в Kubernetes 1.22. Кратко ознакомиться, что же там проектируется, можно в KEP #2582. Самое странное из того, что там написано, на мой взгляд, это предложение использовать namespace label, чтобы определять, по каким правилам проверять манифесты подов. Получается, что, выдав кому-либо права на редактирование неймспейса, вы даете ему и простой способ получить права администратора кластера.

Подождем и посмотрим, что же будет в итоге, а пока нам предлагают плавно переходить на использование стандартных PSP, аналоги которых в виде встроенных профилей будут захардкожены в новый admission plugin PSPv2.

Или переходить на использование сторонних решений, таких как Open Policy Agent Gatekeeper.

Urgent Upgrade Notes

По умолчанию теперь используется cgroupDriver systemd. Не забывайте проверять настройки своего containerd при установке нового кластера или добавлении узлов. Но и это еще не все. В версии 1.22 обещают принудительную смену cgroup driver в kubelet на systemd при обновлении кластера, поэтому пора уже почитать руководство по миграции и начать смену драйвера.

Много мелких изменений в районе CSI и PV: перестают использоваться старые метки, флаги и метрики. В принципе ничего страшного, скорее всего, надо будет просто обновить используемые CSI драйверы.

Команды kubeadm kubeconfig user, certs и debug переведены из экспериментальных в постоянные, и теперь их надо указывать без слова alpha.

Продолжают урезать функционал команды kubectl run. Убрали целый набор ключей для создания service и cronjob, объявили устаревшими ключи для установки реквестов и лимитов, сервисаккаунта и использования hostport. В общем активно заставляют использовать только готовые yaml-манифесты для создания объектов кластера.

К большому моему сожалению, окончательно убрали поддержку ключа kubectl --export. А как было удобно с помощью этого ключа получать из готового объекта кластера манифест для создания его копии, например, секрет с TLS сертификатом скопировать в другой namespace.

Всем, кто использует vSphere версии меньшей, чем 67u3, рекомендуют обновиться, время есть до выхода kubernetes 1.24.

Интересные мелкие новшества

В NetworkPolicy добавили поле endPort для поддержки диапазонов портов. Радуйтесь, любители запускать Asterisk в кластере.

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

В команды kubectl exec и portforward добавили keepalive пинги, и теперь промежуточные HTTP-балансировщики не будут обрывать соединение, если в нем нет активности.

В Job добавили поле suspend и написали про это целую статью в блоге. Вот только я не понял, какой в этом смысл имитация работы Kafka или Rabbitmq?

Появилась возможность выбирать неймспейсы по их имени. Просто в манифест неймспейса автоматически добавляют метку kubernetes.io/metadata.name.

В Service добавили поле InternalTrafficPolicy. Если указать в нем значение Local, трафик будет направляться только на поды, расположенные на том же узле кластера, что и под, отправивший запрос. Пока в альфа-статусе, для использования надо включить featureGate = ServiceInternalTrafficPolicy.

Наконец-то включили TTL Controller, который позволяет удалять манифесты завершившихся Job.

В манифест подов добавили аннотацию kubectl.kubernetes.io/default-container, с помощью которой можно указать, в какой контейнер пода делать exec, чьи логи смотреть и тому подобное, если при вызове не указан ключ -c.

Подробнее..

Перевод Как Asana использует Kubernetes

03.05.2021 06:23:19 | Автор: admin

image


В Asana мы используем Kubernetes для развертывания сервисов и управления ими независимо от монолитной инфраструктуры. Поначалу у нас были некоторые проблемы, и чтобы стандартизировать создание и обслуживание приложений Kubernetes, мы создали фреймворк с незамысловатым названием KubeApps. В последние два года наша команда по инфраструктурной платформе вносила улучшения в KubeApps, чтобы упростить развертывание сервисов в Asana. Здесь мы расскажем, какие проблемы мы хотели решить в Kubernetes и как мы это сделали с помощью фреймворка KubeApp.


Введение


Мы уже рассказывали о legacy-системе деплоймента в Asana наша основная инфраструктура организована как монолит инстансов AWS EC2 с кастомными скриптами конфигурации для каждого сервиса. Никакого масштабирования у нас не было: отправляешь новый код в монолит переконфигурируй все хосты. Чтобы добавить новый сервис, нужен был новый набор кастомных скриптов конфигурации. Все это было сложно обслуживать, плюс был риск увеличить нестабильность деплоев в монолите.


Когда мы обдумывали инфраструктуру для Luna2, решили использовать Kubernetes, чтобы деплоить сервисы и управлять ими независимо от монолита. Оркестрация контейнеров пошла хорошо, и остальные инженеры захотели использовать Kubernetes для других систем Asana. Мы доработали эти инструменты, чтобы и другие команды могли собирать и деплоить сервисы в контейнерах.


Kubernetes в Asana


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


Расширять инфраструктуру за пределы монолита EC2 стало, конечно, проще, но Kubernetes пока не решал всех наших проблем (примечание: В Kubernetes есть Cloud Controller Manager, который управляет нодами, настраивает маршруты между контейнерами и интегрируется с компонентами облачной инфраструктуры).


  • Управление ресурсами AWS никак не связано с Kubernetes. Разработчикам надо было заранее создавать ASG и готовить их для контейнеров Docker и любых других ресурсов, которые могут понадобиться приложению.
  • Мы тратили слишком много времени на инструментирование во всех сервисах, например, для сборки метрик и настройки разрешений для секретов, причем мы постоянно делали одно и то же настраивали ELB, управляли группами конфигурации для каждого приложения...
  • Continuous delivery в Kubernetes не встроена. На уровне абстракции Kubernetes логика приложения должна быть упакована в образы, доступные в container registry.

Чтобы решить эти проблемы и стандартизировать создание и обслуживание приложений Kubernetes, мы создали фреймворк с незамысловатым названием KubeApps. Изначально мы задумали KubeApp для обработки всех аспектов развертывания от сборки необходимых ресурсов до обновления DNS во время деплоя и плавного перехода между версиями кода. Каждый KubeApp определяется набором требований к железу, определений подов и спецификаций сервиса.


Конфигурация как код


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


На схеме показаны спецификации примера веб-приложения и способ его представления в системе конфигурации KubeApp. Деплоймент запрашивает оборудование, ELB и сборки образов. Мы предоставляем дефолтные спецификации для обработки конфигураций DNS и включаем определения подов для обработки метрик и логирования.



Образы Docker с continuous deployment


Код приложения пакуется в Docker-образы с помощью образов Bazel или традиционных Dockerfile. Наш KubeApps собирает и отправляет эти образы в рамках пайплайна CD, так что образы для любого сервиса доступны в нашем registry.


Для сборки зависимостей и управления ими в большинстве случаев мы используем Bazel. Это особенно удобно с контейнеризованными приложениями, потому что таким образом мы объявляем зависимости и упаковываем сторонние библиотеки в исполняемые файлы. Так мы получаем детерминированные выходные данные при настройке контейнерных окружений для каждого приложения.


По одному кластеру на KubeApp


При использовании Kubernetes нужно решить, как делить сервисы между кластерами (примечание: некоторые соображения о компромиссах при выборе числа кластеров приводятся в этой статье на learnk8s.io: Проектирование кластеров Kubernetes: сколько их должно быть?). Деплоить сервисы в один кластер Kubernetes это экономично и удобно в плане администрирования, потому что сервисы используют одни ресурсы мастер-ноды, а такие операции, как апгрейд версий Kubernetes и деплойменты, нужно выполнять всего один раз. Но модель с одним кластером не особо хороша для мультитенантности, а кластер становится единой точкой отказа. Один кластер Kubernetes ограничен в плане масштабирования, так что при достижении определенного размера неплохо было бы его разделить.


В Asana каждый KubeApp деплоится в своем кластере Kubernetes через AWS EKS. Такой подход гарантирует безопасность и устойчивость приложения. Каждый кластер отвечает за один сервис, так что можно не волноваться о состязании за ресурсы между сервисами, а если кластер упадет, пострадает всего один сервис.


Управлять несколькими кластерами сложновато, потому что дефолтные инструменты Kubernetes могут взаимодействовать только с одним кластером за раз. Поэтому мы создали инструменты для управления несколькими кластерами одновременно в KubeApps. Еще мы обнаружили, что такая модель позволяет владельцам отдельных KubeApp независимо управлять кластером (апгрейдить ноды, масштабировать деплои и т. д.)


Процесс развертывания KubeApp


image


Для развертываний KubeApp мы используем центральный хаб управления, который мы назвали kubecontrol. Его можно настроить для обновления вручную или автоматически, через cron. Развертывание KubeApp включает несколько шагов:


  1. Для обновления или создания KubeApp мы вводим команды в kubecontrol.
  2. Из спецификаций приложения мы запрашиваем набор ресурсов, необходимых для KubeApp (Auto Scaling Groups, спотовые инстансы и т. д.). Создается новый кластер EKS.
  3. Мы делаем запрос к сервису сборки образов, чтобы собрать образ docker с определенной версией кода. Сборщик образов компилирует код для KubeApp и отправляет образ в ECR (Elastic Container Registry), если его там еще нет.
  4. После сборки всех ресурсов мы передаем спецификации компонентов кластеру Kubernetes, чтобы он получил нужные контейнеры docker от ECR и задеплоил их на нодах.

Полное обновление KubeApps это blue-green деплой, для которого нужно запустить и настроить новый кластер EKS в AWS. Убедившись, что новый KubeApp работает, мы переключаем нагрузку на новый кластер и сносим старый. В KubeApps можно делать rolling апдейт, при котором обновляются образы на запущенном кластере. Это позволяет быстро и незаметно переходить с одной версии кода на другую без запуска целого нового кластера.


Консоль управления KubeApp


До недавнего времени единственным способом прямого мониторинга или управления KubeApp было ssh-подключение к kubecontrol и взаимодействие с приложением через CLI. Искать информацию о деплоях было непросто, так что пользователям приходилось изучать логи, чтобы понять, когда в KubeApp была развернута определенная версия кода. Чтобы внести больше ясности, мы создали KubeApp Management Console (KMC), которая отвечает за запись информации о прошлых деплоях. Мы хотим использовать эту консоль как централизованный веб-интерфейс, в котором пользователи могут взаимодействовать с KubeApps.


Что собой представляет KubeApps сегодня и что будет дальше


Сейчас у нас больше 60 KubeApp в Asana, которые поддерживают самые разные рабочие нагрузки от управления релизами до распределенного кэширования. Пока мы сохраняем монолит инстансов EC2, но уже переделываем эти сервисы в контейнеры в KubeApps.


Команда инфраструктурной платформы продолжает активную работу над KubeApps. В будущем мы планируем поддерживать больше архитектур (ARM64) и провайдеров инфраструктуры (AWS Fargate). Еще мы планируем создать инструменты для более удобной разработки в KubeApps, чтобы их можно было запускать в песочницах и локальных окружениях. Благодаря этим и другим изменениям фреймворк KubeApps можно будет приспособить для любой рабочей нагрузки, которая может нам понадобиться.

Подробнее..

Перевод Контролируем удаление с финализаторами

18.06.2021 08:07:31 | Автор: admin

image


В Kubernetes не так-то просто что-то удалить вы уверены, что удалили объект, но оказывается, что он все еще присутствует в кластере. Вы, конечно, можете выполнять команду kubectl delete в повседневных операциях и надеяться на лучшее, но знание принципов работы delete команд в Kubernetes поможет вам понять, почему некоторые объекты остаются после удаления.


В этой статье мы рассмотрим:


  • Какие свойства ресурса влияют на удаление.
  • Как финализаторы и ссылки на родителя-владельца управляют удалением объектов.
  • Как можно использовать propagationPolicy, чтобы изменить порядок удаления.
  • Как работает удаление, с примерами.

Для простоты во всех примерах мы используем ConfigMaps и базовые команды kubectl. Мы увидим, как работают эти команды, и обсудим результаты их использования на практике.


Базовая команда delete


В Kubernetes есть несколько команд для создания, чтения, обновления и удаления объектов. Здесь мы поговорим о четырех командах kubectl: create, get, patch и delete.
Примеры базовой команды kubectl delete:


kubectl create configmap mymapconfigmap/mymap created

kubectl get configmap/mymapNAME    DATA   AGEmymap   0      12s

kubectl delete configmap/mymapconfigmap "mymap" deleted

kubectl get configmap/mymapError from server (NotFound): configmaps "mymap" not found

Перед командой стоит знак $, далее следует ее результат. Как видите, мы начали с kubectl create configmap mymap, чтобы создать пустой configmap mymap. Теперь нужно выполнить get и убедиться, что он существует. Затем мы удалили configmap. Если снова сделать get, получим ошибку HTTP 404, потому что configmap не найден.


У команды deleteочень простая диаграмма состояний:


image
Диаграмма состояний для delete


Операция незамысловатая, но ей могут помешать некоторые факторы, включая финализаторы и ссылки на родителя-владельца.


Как работают финализаторы


Чтобы понять, как удаляются ресурсы в Kubernetes и почему они иногда все-таки не удаляются, полезно разобраться с финализаторами.


Финализаторы это ключи в манифесте ресурса, отвечающие за операции, которые надо выполнить до удаления объекта. Они контролируют сборку мусора для ресурсов и сообщают контроллерам, какие операции очистки нужно выполнить до удаления ресурса. Они не обязательно обозначают код, который нужно выполнить. По сути, это просто списки ключей, вроде аннотаций. Как и аннотациями, ими также можно управлять.


Скорее всего, вы встречали следующие финализаторы:


  • kubernetes.io/pv-protection
  • kubernetes.io/pvc-protection

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


Ниже приводится configmap без свойств, но с финализатором:


cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap  finalizers:  - kubernetesEOF

Контроллер ресурса configmap не понимает, что делать с ключом финализатора kubernetes. Такие финализаторы для configmap я называю мертвыми, и обычно они используются с неймспейсами. Вот что происходит при попытке удалить configmap:


kubectl delete configmap/mymap &configmap "mymap" deletedjobs[1]+  Running kubectl delete configmap/mymap

Kubernetes сообщает, что объект удален, но на самом деле, в традиционном понимании, никуда он не удален. Скорее он находится в процессе удаления. Если мы снова выполним get, то узнаем, что объект изменен и теперь содержит временную метку удаления deletionTimestamp.


kubectl get configmap/mymap -o yamlapiVersion: v1kind: ConfigMapmetadata:  creationTimestamp: "2020-10-22T21:30:18Z"  deletionGracePeriodSeconds: 0  deletionTimestamp: "2020-10-22T21:30:34Z"  finalizers:  - kubernetes  name: mymap  namespace: default  resourceVersion: "311456"  selfLink: /api/v1/namespaces/default/configmaps/mymap  uid: 93a37fed-23e3-45e8-b6ee-b2521db81638

Если в двух словах, объект был изменен, но не удален. Все потому, что Kubernetes увидел, что у объекта есть финализаторы, и поместил его в состояние read-only. Временная метка удаления показывает, что объект можно только читать а еще из него можно удалить апдейты ключа финализатора. Другими словами, удаление будет завершено, только когда мы удалим из объекта финализатор.


Удалить финализаторы можно с помощью команды patch. Фоновое удаление завершится, и объект будет удален. Если мы опять выполним get configmap, то ничего не увидим.


kubectl patch configmap/mymap \    --type json \    --patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'configmap/mymap patched[1]+  Done  kubectl delete configmap/mymapkubectl get configmap/mymap -o yamlError from server (NotFound): configmaps "mymap" not found

Диаграмма состояния для финализации выглядит так:


image
Диаграмма состояний для финализации


Если мы попытаемся удалить объект с финализатором, он останется на этапе финализации, пока контроллер не удалит ключи финализаторов или финализаторы не будут удалены через Kubectl. Когда список финализаторов будет пуст, Kubernetes поместит его в очередь на удаление из реестра.


Ссылки на владельца


Ссылки на владельца (owner references) описывают отношения между группами объектов. Это свойства ресурсов, которые указывают, как они связаны друг с другом, чтобы можно было удалять целые деревья ресурсов.


Правила финализатора обрабатываются, если есть ссылки на владельца. Ссылки на владельца состоят из имени и UID. Они связывают ресурсы в одном неймспейсе и им нужен UID. Ссылки на владельца для подов обычно указывают на replica set, которому они принадлежат. Если удалить deploy или stateful set, дочерние replica set и pod тоже удалятся.


Посмотрим на примере, как работают ссылки на владельца. В первом примере мы сначала создаем родительский объект, а потом дочерний. В результате получаем простейший configmap со ссылками на владельца, то есть родителя:


cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap-parentEOFCM_UID=$(kubectl get configmap mymap-parent -o jsonpath="{.metadata.uid}")cat <<EOF | kubectl create -f -apiVersion: v1kind: ConfigMapmetadata:  name: mymap-child  ownerReferences:  - apiVersion: v1    kind: ConfigMap    name: mymap-parent    uid: $CM_UIDEOF

Если мы удалим дочерний объект, в котором есть ссылка на владельца, родитель никуда не денется:


kubectl get configmapNAME           DATA   AGEmymap-child    0      12m4smymap-parent   0      12m4skubectl delete configmap/mymap-childconfigmap "mymap-child" deletedkubectl get configmapNAME           DATA   AGEmymap-parent   0      12m10s

В следующем случае мы воссоздали configmap из примера выше. Если мы удалим родителя, а не дочерний объект, в котором есть ссылка на него, а потом сделаем get для configmap, то увидим, что все configmap пропали:


kubectl get configmapNAME           DATA   AGEmymap-child    0      10m2smymap-parent   0      10m2skubectl delete configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNo resources found in default namespace.

Получается, что если в дочернем объекте есть ссылка на родителя, он автоматически удаляется при удалении родителя. Это называется cascade. По умолчанию cascade имеет значение true, но можно указать --cascade=false в kubectl delete, чтобы удалить родительский объект, не трогая дочерние.


В следующем примере у нас есть родительский и дочерний объекты, а еще ссылки на владельца. Если мы удалим родителя, указав --cascade=false, дочерние объекты сохранятся:


kubectl get configmapNAME           DATA   AGEmymap-child    0      13m8smymap-parent   0      13m8skubectl delete --cascade=false configmap/mymap-parentconfigmap "mymap-parent" deletedkubectl get configmapNAME          DATA   AGEmymap-child   0      13m21s

Параметр --cascade указывает на политику распространения в API, которая позволяет менять порядок объектов удаления в дереве. В следующем примере используется отправка запроса к API через curl для удаления с фоновой политикой распространения (propagation policy):


kubectl proxy --port=8080 &Starting to serve on 127.0.0.1:8080curl -X DELETE \  localhost:8080/api/v1/namespaces/default/configmaps/mymap-parent \  -d '{ "kind":"DeleteOptions", "apiVersion":"v1", "propagationPolicy":"Background" }' \  -H "Content-Type: application/json"{  "kind": "Status",  "apiVersion": "v1",  "metadata": {},  "status": "Success",  "details": { ... }}

Политику распространения нельзя указать в командной строке с помощью kubectl только при прямом вызове API. Просто создайте прокси, чтобы иметь доступ к серверу API со своего компьютера, и выполните команду curl с одним URL, чтобы послать команду delete.


Есть три варианта политики распространения:


  • Foreground: дочерние объекты удаляются до родительского (обратный порядок).
  • Background: родительский объект удаляется до дочерних (прямой порядок).
  • Orphan: ссылки на владельца игнорируются.

Если вы удаляете объект, в котором есть ссылки на владельца, то финализаторы также будут учитываться. В результаты деревья объектов могут сохраняться, и удаление будет частичным. В этом случае изучите имеющиеся ссылки на владельца и финализаторы, чтобы понять, что происходит.


Принудительное удаление неймспейса


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


cat <<EOF | curl -X PUT \  localhost:8080/api/v1/namespaces/test/finalize \  -H "Content-Type: application/json" \  --data-binary @-{  "kind": "Namespace",  "apiVersion": "v1",  "metadata": {    "name": "test"  },  "spec": {    "finalizers": null  }}EOF

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


Итоги


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


Видео от автора:


Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru