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

Блог компании ребреин

Разрабы становятся админами, а админы разрабами. Интервью с инженером Uber, где разделение исчезло совсем

11.02.2021 20:09:20 | Автор: admin

Данила Мигалин (@miga) живет в Вильнюсе и работает инженером в Uber.

Давным-давно контора, которая занималась русификацией игр, не взяла его работать переводчиком. На следующий день он устроился админом, потому что в школе увлекался программированием. Русское IT это большая деревня, одни и те же люди переходят из компании в компанию. До Uber я успел поработать в Яндексе и Майкрософте, говорит Данила.

С 2006 года он занимался ip-телефонией и админской работой. В свободное время писал на Перле, затем на Питоне, делал свои пет-проекты. Некоторые из них даже пошли в продакшн и в Яндексе, и в Майкрософте. Писать по-серьезному в продакшн он начал, только когда пришел в Uber. Место, которое мне предложили в компании, предполагало знание Golang. Меня не смутило то, что я иду админом на позицию разработчика. Я думал: отлично, наконец-то можно будет завязать с админским делом и спокойно писать код.

Мы поговорили с Данилой, и он рассказал, почему в Uber нет разделения на девов и опсов, трудно ли осваивать разработку, если всю жизнь был админом и почему Golang стал стандартом в сфере Devops.


Трудно ли админу стать разрабом, а разрабу админом

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

Сейчас мы все называемся Software Engineer и пишем код каждый день. В Майкрософте мы назывались Service Engineer (уже не Operations, но еще не Software Engineer) и занимались девопской работой. Писали разработчикам разные автоматизации для деплоя.

Сейчас в моем окружении девопсов уже практически не осталось. В Uber у нас нет ни Opsов, ни Devов. Все инженеры пишут, деплоят и онколят свой продукт от и до. Команды ответственны за полный жизненный цикл продукта. Больше нет злых дяденек админов или более добрых дяденек девопсов, которые что-то за тебя сделают, задеплоят, замониторят. И мне это нравится.

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

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

Я открыл вакансии на бэкенд и понял, что теперь бэкенд это не просто кодирование и БД. Это кодирование, БД и девопс. Как можно запихнуть столько знаний в одну голову?

Компьютеры и сервисы становятся сложнее. Требования к разработчикам естественным образом повышаются. Я считаю это нормальным. Когда Яндекс переходил от классической модели админ и разработчик к девопсу, и программистов начинали ставить в онкол, многие люди ушли. Им это очень не понравилось! Они нанимались писать код, а все остальное гори синим пламенем.

В Яндексе не было дежурств, у нас было понятие ответственный за сервис. Этот человек должен был быть доступным 24/7/365. В те времена я реально ходил везде с ноутбуком и повербанком. В любой глуши я был во всеоружии.

Круто, что в Uber дежурства, что называется, follow the sun. Когда у нас ночь дежурят пацаны по другую сторону океана. Нам остается день. До этого мы дежурили круглосуточно. У нас были проекты, которые мы делали в Вильнюсе, и были проекты из Сан-Франциско. Мы за свои проекты онколили 24/7 и ребята из Сан-Франциско онколили за свои проекты 24/7. Потом провели ряд вебинаров, рассказали что к чему друг другу и начали онколить днем за свое и за американское, а они своим днем за свое и за Вильнюсское.

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

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

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

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

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

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

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

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

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

Так было всегда.

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

Сейчас вакансию бэкендера без докера уже не найти. Ладно, а в обратную сторону это работает? Ты же не занимался продуктовой разработкой в конвейерном смысле. Сложно было?

Я не испытывал особых трудностей. Не скажу ничего насчет знания алгоритмов структур данных и прочего computer science, но уметь выразить свои мысли на языке программирования и уметь найти алгоритм это знание необходимо. И у меня оно было.

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

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

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

Мне кажется, нет особой проблемы в том, чтобы изучить +1 язык программирования. Я бы не испугался Java.


Почему Golang стал стандартом в сфере Devops

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

При переходе с Питона на Golang я не испытал страха. Golang в тысячу, в миллиард раз проще, чем Питон. Ты буквально можешь открыть http://tur.golang.org/, пройти его за день и на утро писать в продакшн. Я не шучу, это действительно так.

Но есть одно но. Golang очень простой и поэтому не выразительный. Лично меня это раздражает.

Знаю ребят, которые с Java переходят на Scala, потом на Хаскель, потом на Идрис, и все равно им не хватает выразительности.

Да, я постоянно страдаю. Пишу код и думаю, блин, занимаюсь ерундой! Будь это Erlang, я бы воспользовался паттерн-матчингом, все было бы изящненько, красивенько. С Golang никакого эстетического удовольствия. Он не про изящность. Это в Питоне ты можешь написать какой-нибудь comprehension и, откинувшись на спинку кресла, любоваться им 5 минут: как ловко получилось. Мало возможностей проявить хоть какую-то творческую жилку. Уверен, что топорность Go была одной из целей создания языка, чтобы гугловские ребята, вся эта армия программистов, не занималась творчеством, а просто писала понятный и надежный код.

Вы и разработку, и автоматизацию девопс тоже на Golang делаете?

Да, конкретно в нашей архитектуре нет такого разделения. Это все сервис. Просто один обслуживает юзерский трафик, другой оркеструет контейнеры.

Моя команда занимается автоматизацией баз данных. Мы предоставляем базы данных как сервис внутри Uber. У нас для этого есть свой оркестратор. Как кубернетес, только убернетес! Мы пишем этот оркестратор каждый день. Пишем его на Golang.

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

И дальше следует предложение, которое мне очень нравится: Если вам необходимо читать эту документацию дальше, то вы слишком умный! Вы делаете что-то слишком умное. Так и написано по-английски.

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

Ты хорошо заметил, Golang очень opinionated. Он сделан с такой жесткой визией. Ты называешь переменные, выравниваешь текст, организовываешь взаимодействие между потоками и все. Про остальное можно забыть. Это хорошо снижает когнитивную нагрузку.

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

Думаю, человек не из индустрии понятия не имеет, кто такой девопс. Я скажу программист. Я так и говорю. Это же всем понятно.


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

Подробнее..

Три года назад нужно было изучать Docker, два года назад Kubernetes. Сейчас Serverless

10.03.2021 20:17:40 | Автор: admin

Интервью с Антоном Черноусовым из Yandex.Cloud

Антон Черноусов developer advocate пришел в Yandex.Cloud, чтобы заниматься микросервисами, популярными в индустрии Docker и Kubernetes . Но главная его профессиональная страсть это Serverless. Мы поговорили с Антоном, и он объяснил, почему бессерверные платформы ждет большое будущее.


Зачем изучать Serverless

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

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

Docker и Kubernetes это инструменты. Как и любой инструмент, они не вечны. У каждого инструмента есть жизненный цикл. Как только мы переходим в высокотехнологичную сферу, жизненный цикл инструмента сильно укорачивается. Под этим углом я смотрю на технологии. Именно поэтому приходится следить за тем, что будет следующим новым молотком на ближайшие 2-3 года. Три года назад нужно было обратить внимание на Docker. Два года назад на Kubernetes. Сейчас нужно изучать Serverless.

15 лет назад мы делали CGI-скрипты на Перле. Это было то же самое, как сегодня написать Serverless функцию. Нам нужно было заботиться о физическом сервере, об Apache, мы постоянно лезли в конфигурацию. Теперь за сервер отвечает облако, вместо Apache у тебя есть сервер, который вызывает рантайм, и ты заботишься только о скрипте, который превратился, по сути, в отдельную функцию. Идея старая, а молоток новый. Мы ничего нового не придумали, просто взяли молоток, поняли, что у нас есть новые технологии, которые позволяют сделать все более эффективно. Переложить риски на облако и заниматься только бизнес-логикой это и есть самое крутое в Serverless! Ты занимаешься только бизнес-логикой. Это мечта для многих.

Должны ли обычные разработчики думать о Serverless

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

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

Но это все наши внутренние заморочки. Бизнес интересует стоимость конкретного процесса и скорость его реализации.

Используя Serverless, мы можем предоставить бизнесу четкий KPI, показать историю: смотрите, вот вход, вот выход, вот функция и вот ее стоимость. В бессерверных технологиях есть объем услуги, который ты потребляешь и за который ты платишь. Вызов конкретной функции стоит конкретных денег. Ты не платишь за какие-то эфемерные сервера, которые где-то находятся и требуют кеш каждый день. Все прозрачно, стоимость считать просто.

С этой точки зрения Serverless это крутая история разработки программного обеспечения, не просто кодирования, а именно подхода к разработке. Бизнес понимает, сколько по-настоящему это будет стоить. Он понимает, как это будет масштабироваться.

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

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

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

Как Serverless устроен на практике

Надо понимать, что это еще достаточно молодой инструмент. И у него есть некоторые ограничения, как и у любого инструмента.

Сейчас, когда я говорю про серверлесс, я подразумеваю все подмножество технологий:

Функция

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

База для хранения состояния

Вторая часть необходима для того чтобы сделать полноценное приложение. Когда мы выдерживаем нагрузку, нам нужно место, где мы будем хранить состояние. Там есть развилка: оперативное состояние и долговременное. Для долговременного состояния подходят БД. Уже появился целый класс баз данных, который так и называется Serverless Database.

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

Оперативное управление данными

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

У Амазона это SQS, в Yandex.Cloud Message Queue. Она работает именно в этой идеологии масштабируемое Serverless решение.

Хранилище

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

В Амазоне это S3, как прародитель, в Yandex.Cloud это Object Storage, который повторяет, по сути, протокол S3. Это возможность хранить и файлики, и статическую информацию, и долговременно хранить бекапы.

API

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

Есть сервис API Gateway. Он позволяет заявить наружу тот самый API, которым мы управляем, предоставить доступ к функциям и связать API вызов с конкретной функцией.

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

Почему весь рынок все еще не перешел на Serverless

Вижу три момента, которые влияют на переход и толкают к нему.

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

Цифровизация проникла во все сферы, как электричество. Представляете, Москва одновременно проснулась, все включили свои телефоны и посмотрели что-то в новостях.

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

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

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

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


Большие компании только начинают разглядывать огромный потенциал Serverless подхода. Самый крупный пример, про который я могу говорить, это Genotek. Данные генетических тестов обрабатываются в облаке. Человек плюет в пробирку, его материал изучают в лаборатории, а полученные данные заливают в облако. Поскольку это суперперсональная информация, внутри все построено на безопасности и максимальном уровне паранойи. Часть процессов реализована с использованием Serverless подхода, данные обрабатываются на функциях, и работает это все в Yandex.Cloud.

Сейчас в русскоязычном пространстве мало кейсов и практик использования Serverless и я стараюсь это исправить в рамках Yandex Serverless Ecosystemи хаба Serverless тут на Хабре. Например, недавно я был у мамы и нашел книжку-игру. Были такая серия Подземелья черного замка. Это фэнтези-роман, который разыгрывается с помощью кубиков. Можно было взять в дорогу и играть в поезде. Каждый абзац книги был пронумерован и содержал действие. Ты управлял персонажем, который ходил по книге и выполнял задания спасал принцессу, боролся с темным властелином.

Я делаю на этой основе приложение, хочу продемонстрировать некоторые особенности работы Serverless: хранение состояния, передачу и сохранение данных.

В результате, популярность Serverless это вопрос времени и нашей тяги к саморазвитию.

А что вы думаете про Serverless? Заходите к нам в сообщество там мы постоянно разбираем различные проблемы и задачи из сферы Devops, обсуждаем вещи, которые пригодятся и на собеседованиях, и в работе.

Подробнее..

Представь, что ты нашел решение, про которое можешь сказать оно лучшее в мире интервью с создателем ClickHouse

22.03.2021 20:04:15 | Автор: admin

Алексей Миловидов работал инженером в Яндекс.Метрике, и перед ним стояла непростая задача.

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

Долгое время такая СУБД разрабатывалась только для внутренних нужд но в 2016 вышла в опенсорс под названием ClickHouse, и сообщество встречает инструмент по-разному.

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


Расскажи про себя, про учебу, и про первый опыт в разработке.

Наверное, надо начинать очень издалека. В семье было еще два брата, я самый младший. Году в 90-м родители купили старшему брату бытовой компьютер БК-0010. Это была реально классная машина. Интересные игры, встроенные языки программирования: Бейсик Вильнюс 86 и Фокал (Focal, акроним от англ. formula calculator).

Мои братья играли в эти игры, я в основном смотрел, мне было года 3-4. Отец с работы приносил стопки ксерокопий. На них можно было рисовать. Я рисовал компьютерные игры, лабиринты. И все время пытался заставить старшего брата все это запрограммировать. И я его так достал, что он взял и выполнил мою просьбу. Оказывается, я стал менеджером гораздо раньше, чем программистом.

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

Брат рассказал про цикл и дал мне в руки толстую книгу под названием Язык программирования Бейсик. Уже позже папа принес домой IBM 286, и я стал что-то ковырять на нем. Мое первое достижение я написал игру и смог уговорить поиграть в нее маму. Мне было чуть меньше 10 лет.

В школе преподавали Паскаль. Писал на Паскале, потом на Делфи. Потом решил изучить С. С++ всегда был мощным, таким он и остался так было написано на диске старшего брата. Тем более я где-то услышал, что если писать игры то только на С++, другого варианта нет.

Это история была общей для всех программистов. Все учили С++, чтобы делать игры, а в итоге делают КликХаус.


Почему КликХаус настолько быстрее аналогов?

Тому есть две причины. Одна причина это просто более-менее подходящие технические решения. А вторая внимание к деталям. КликХаус был создан для обработки данных веб-аналитики в Яндекс.Метрике. Я решал задачи производительности. Что-нибудь подкрутить, как-нибудь схитрить, чтобы КликХаус работал хотя бы в 2 раза быстрее. Как только ты сделал его в 2 раза быстрее, сразу думаешь: а как еще в полтора раза быстрее сделать? А еще бы на 30%, и вообще было бы идеально! Приходится много ковыряться в деталях.

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

В такой работе должен быть инженерный кайф.

Да, он определенно присутствует. Представь, что ты наконец-то сделал решение, (может быть, впервые в жизни!), про которое ты можешь сказать: да, это лучшее решение в мире.

Я иронизирую, но, допустим, на работе ты делаешь круды. Или другие мемы индустрии: клепаешь формочки, перекладываешь джейсоны. Однажды ты настолько хорошо переложил джейсоны, что понял: ты сделал лучший в мире Перекладыватель Джейсонов! Он тратит мало оперативки, его можно быстро прикрутить. И главное, прийти в другую компанию, сказать, что с помощью этого классного инструмента я вам сейчас переложу все джейсоны из одного места в другое.

Но компаниям на самом деле наплевать, как ты перекладываешь джейсоны. Жрет оно в 5 раз больше оперативы, и бог с ним!

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

В самом начале работы над кликхаусом ты был человеком, который хорошо разбирался в оптимизации производительности?

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

Работа над кодом, главная черта которого производительность, накладывает большие требования на тестирование этой производительности?

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

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

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

Процессор говорит, в одной программе чуть-чуть больше промахов кеша инструкций. На каждое процессорное ядро есть 32 килобайта L1 кеша инструкций и почему-то туда стало хуже все помещаться. Может быть, проблема возникла из-за структуры устройства кеша. Из-за его ассоциативности. Какие-то куски кода попали на адреса, которые отличаются на энное число байт, кратное двум. Внутри процессора есть как бы такая маленькая хеш-таблица, и в этой таблице происходит вытеснение.

И как такие проблемы решаются? Ты их один решаешь, или у вас там консилиум собирается? Бывают ли тупики?

А теперь раскрою секрет, вот такие проблемы мы не решаем! Мы еще до этого не дошли. Мы просто научились наблюдать.

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

Есть и другие способы измерять производительность. Что называется, always on profiling. То есть всегда включенное профилирование на продакшене. В КликХаусе встроен семплирующий профайлер. С небольшим интервалом семплирования, всего лишь один раз в секунду, собираются стектрейсы во всех потоках. А потом по большому кластеру мы агрегируем все за неделю и делаем выводы, стал ли продукт быстрее.

Есть же дилемма чтобы написать более быстрый код, ты пишешь сильно менее читаемый код. Для вас это проблема в кодовой базе КликХауса?

Мне кажется, что так делать не нужно. Действительно, если ты пишешь более быстрый код, он получается идейно сложнее. Но его нужно просто максимально подробно описать. Я сторонник того, что в коде должны быть комментарии. Сам я пытаюсь писать такой код, чтобы другой человек мог прочитать его от начала и до конца прямо как будто это учебник или пост в блоге, который этот же код объясняет.

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

В C# или Java хороший код это максимум 200 строк на файл. Ты заходишь в любой файлик, и он умещается в экран. На C++ это вообще нереально, особенно на больших проектах. У вас есть файлы на 5.000 строк?

Сейчас посмотрю мой любимый StorageReplicatedMergeTree.cpp, сколько там строк...

Кстати, сейчас я использую Linux, и в моей любимой среде разработки что-то напутали со шрифтами, и номера строк отображаются так, что первая цифра наполовину не видна.

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

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

...итак в нашем файле 6.368 строк. Это один из самых больших файлов в нашем проекте. Таких немного. В основном у нас небольшие файлы.


Зачем КликХаус в опенсорсе?

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

Постепенно к технологии появился интерес и в других отделах Яндекса. Наша разработка давала возможность использовать систему внутри Метрики напрямую для аналитиков, чтобы можно было выполнить обобщенный запрос по данным всего интернета. На самом деле, это очень круто. Потому что метрика стояла на 70% сайтов: покрывается совершенно нереальное количество трафика и можно делать глобальный запрос, чтобы посчитать что-нибудь важное.

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

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

В конце 2015-го года я перешел в группу Кликхаус (ее выделили из группы Яндекс.Метрики). Мы обсуждали идею КликХауса на опенсорсе с моим руководителем, он предложил ее Михаилу Парахину (тогда он был СТО в Яндексе). Тот дал добро. Я тогда написал мотивационное письмо: какие возможности, какие риски. Мы стали пробовать, несколько месяцев ушло на подготовку, но летом 2016 года все было готово, и мы выложили КликХаус в опенсорс. Серьезных надежд не было. У меня был такой принцип: если ничего не получится, то ну и ладно, не надо будет тратить силы. Но если полетит и все будет хорошо, то у нас будет некоторое достижение. Возможность получить это достижение перекрывала риски.

Взлетела технология?

Уверенно растет. Будем стараться дальше.

Опенсорс принес какие-то плоды? Контрибьюторы со стороны приходят?

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

Слушай, вас очень ругают за документацию.

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

Почему Кликхаус так сильно отличается от других БД, что даже появилось выражение "ClickHouse way"?

Мы исходили из своих задач и брали самое простое естественное их решение, которое не обязательно соответствовало стандарту SQL. Я приведу простой пример: у нас в Кликхаусе можно написать не селект каунт звездочка, а просто: каунт и все! И я до сих пор думаю, что это логичнее.

Не страшно принимать такие решения? Привычка сообщества это тоже часть простоты инструмента.

Когда я сделал селект каунт, я вообще не парился. Тогда это не было опенсорсом. Сейчас любое решение по синтаксису я обязательно сравниванию с опытом нескольких других СУБД. И выбираю лучшее.

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

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

Что думаешь про компании, которые строят целые бизнесы на внедрении КликХауса?

Я всецело это приветствую. Чем больше будет этот бизнес, тем лучше.

Если КликХаус вырастет на рынке, не будешь переживать, что многие решения, которые ты принял в самом начале, уже не исправить?

Ничего, придется объяснять. Такое часто бывает. Взять линукс или BSD, в котором многие вещи тянутся из допотопных времен. Например, спрашивают, почему язык csh такой ужасный? Чем разработчик думал, когда его писал? Автора нашли (хотел сказать откопали). Он ответил: у меня просто не было опыта. Какую технологию не возьми, везде есть какие-нибудь грабли.


Как тебе индустрия прямо сейчас?

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

Ты видишь проблему в том, что порог входа снизился? В индустрии много программистов разного уровня.

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

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

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

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

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

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

Эти процессы не остановить. Люди пишут новые инструменты просто потому, что хотят. Все-таки в нашей профессии есть доля творчества. Человек может плохо разбираться в вопросе, но в процессе разработки очередного велосипеда он получит много пользы лично для себя. Возможно, все, что он сделает, нанесет вред индустрии. Но он точно станет более квалифицированным специалистом. Пусть свет увидит еще 10 новых конкурирующих JavaScript фреймворков, но один из них когда-нибудь да привнесет новую идею. Людям нельзя запрещать пытаться только потому, что большинство их разработок не будут нужны.

Современные языки программирования что ты о них думаешь?

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

Вспомнить JavaScript, который вырос из пережитков прошлого, или С++, рост которого был продиктован разными практическими соображениями. Принципы, по которым он построен, почти все разумные. Конечно, есть и неконсистентности. С++ сложный язык и это его основная проблема. Rust, кстати, тоже находится в группе риска, он не менее сложный и становится все труднее. Чтобы программировать простые вещи, нужно чувствовать себя уверенно при использовании языка.

По пятницам мы с ребятами из КликХауса собираемся и читаем чужой код ради смеха. Возможно, когда-нибудь выйдет юмористическое видео Разработчики КликХауса читают код и ржут. Вот недавно смотрели код на Rust. При этом никто из нас не знает этот язык. Можно открыть код КликХауса и тоже поугорать над этим.

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

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

Чуть ли не единственное решение, которое следит за производительностью по максимуму это КликХаус. Мы действительно докапываемся, чтобы каждая штука в нашем коде работала так, как позволяет железо. А современное железо, поверьте, очень крутое! Для многих это обыденность, я и сам воспринимаю эту силу как некую магию. История человечества, вся совокупность знаний привела нас к тому, что мы имеем процессор, который обрабатывает миллиарды операций. Это нереальное достижение. Ты приезжаешь в аэропорт, видишь там авиалайнер и восторгаешься тем, как он сделан. В кармане каждый день лежит обычный телефон, и на его счет я испытываю те же самые чувства.

Невероятно важно осознавать все то, что привело к этому.


От редакции Rebrain

Что вы сами думаете про ClickHouse? Лично мы накопили уже очень много опыта в работе с ним и постоянно этим опытом делимся рассказываем про все тонкости, где, как и что лучше применять.

Заходите к нам в сообщество там мы постоянно разбираем различные проблемы и задачи из сферы Devops, обсуждаем вещи, которые пригодятся и на собеседованиях, и в работе.

Подробнее..

Перевод Восторг безопасника технология для шифрования образов контейнеров

18.08.2020 18:15:11 | Автор: admin
На днях поступила интересная задачка необходимо найти способ защитить исходные данные контейнера (читай: не иметь возможности прочитать, что лежит внутри), когда он остановлен. В голову сразу пришла мысль про шифрование, а пальцы начали набирать в гугле заветные слова. Пока разбирался в теме, наткнулся на достаточно интересную статью, которую с удовольствием привожу вам.



Обеспечение конфиденциальности данных и кода в образах контейнеров


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

Успех технологии контейнеризации в основном зависит от безопасности контейнеров на различных этапах их жизненного цикла. Одна из проблем безопасности наличие уязвимостей внутри отдельных контейнеров. Для их выявления пайплайны DevOps, используемые для создания контейнеров, дополняют сканерами, которые ищут в контейнерах пакеты с возможными уязвимостями и предупреждают их владельцев или технических специалистов в случае их обнаружения. Vulnerability Advisor в IBM Cloud является примером такой утилиты.

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

Еще одна потенциальная проблема безопасности это изоляция контейнера. Технологии безопасности среды исполнения Linux, такие как пространство имен, контрольные группы (cgroups), возможности Linux, а также профили SELinux, AppArmor и Seccomp, помогают ограничить процессы контейнеров и изолировать контейнеры друг от друга во время исполнения.

В этой статье рассматривается все еще актуальная проблема безопасности предприятий в отношении конфиденциальности данных и кода в образах контейнеров. Основная цель безопасности при работе с образами контейнеров позволить создавать и распространять зашифрованные образы контейнеров, чтобы сделать их доступными только определенному кругу получателей. В этом случае другие могут иметь доступ к этим образам, но они не смогут запускать их или видеть конфиденциальные данные внутри них. Шифрование контейнеров основано на существующей криптографии, такой как технологии шифрования Ривеста-Шамира-Адлемана (RSA), эллиптической кривой и Advanced Encryption Standard (AES), также известный как Rijndael симметричный алгоритм блочного шифрования.

Вводные


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

Смежные работы по шифрованию и контейнерам


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

Зашифрованные файловые системы существуют во многих операционных системах на предприятиях и могут поддерживать монтирование зашифрованных разделов и каталогов. Зашифрованные файловые системы могут даже поддерживать загрузку с зашифрованного загрузочного диска. Linux поддерживает шифрование на уровне блочного устройства с помощью драйвера dm-encrypt; ecryptfs является одним из примеров зашифрованной файловой системы. Для Linux доступны другие решения для шифрования файлов с открытым исходным кодом. В ОС Windows шифрование поддерживает файловая система NTFS v3.0. Кроме того, многие производители создают диски с самошифрованием. Для образов виртуальных машин существует решение, подобное зашифрованным дискам. Эмулятор машины (ПК) QEMU с открытым исходным кодом и продукты виртуализации VMware поддерживают зашифрованные образы виртуальных машин.

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

Структура


Экосистема Docker сформировалась для того, чтобы стандартизировать форматы образов контейнеров с помощью группы стандартов Open Container Initiative (OCI), которая теперь контролирует формат времени исполнения контейнера (runtime-spec) и формат образа контейнера (image-spec). Поскольку работа команды требовала расширения существующего формата образа контейнера, мы выделили расширение стандарта для поддержки зашифрованных образов. В следующих разделах описывается существующий формат образа контейнера и расширения.

На верхнем уровне контейнер может состоять из документа в Java Script Object Notation (JSON), который представляет собой список манифестов образов. Например, вы можете использовать этот список манифестов, когда для образа контейнера используются несколько архитектур или платформ. Список манифестов содержит ссылки на манифесты контейнеров, по одной для каждой комбинации архитектуры и операционной системы. Например, поддерживаемые архитектуры включают amd64, arm и ppc64le, а к поддерживаемым операционным системам относится Linux или Windows. Пример списка манифестов показан на скриншоте ниже:



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

Уровень ниже списка манифестов это манифест. Манифест также является документом JSON и содержит упорядоченный список ссылок на слои образов. Эти ссылки содержат mediaType, описывающий формат слоя. Формат может описывать, сжимается ли слой, и если да, то каким образом. Например, каждый уровень может быть сохранен в виде файла .tar, содержащего файлы, которые были добавлены на определенном этапе сборки при выполнении docker build в файле Docker. Для повышения эффективности хранения слои часто также упаковываются с использованием сжатых файлов .gzip. Пример документа манифеста показан на следующем скриншоте:



Как показано, манифесты и уровни ссылаются через дайджест, который обычно представляет собой хеш-функцию sha256 в документах JSON. Манифесты и слои обычно хранятся в виде файлов в файловой системе. Зачастую имена файлов представляют собой хеш-функции над содержимым, что упрощает их поиск и загрузку. Следствием этого метода хеш-функции является то, что небольшое изменение в документе, на который есть ссылка, вызывает изменения во всех документах, которые на него ссылаются, вплоть до списка манифестов.

В рамках проекта нашей команды мы создали шифрование образов на основе гибридной схемы шифрования с использованием открытого и симметричного ключей. Симметричные ключи используются для массового шифрования данных (применяются для многоуровневого шифрования), а открытые ключи используются для упаковки симметричных ключей. Мы использовали три различные технологии шифрования на основе открытых ключей: OpenPGP, JSON Web Encryption (JWE) и PKCS#7.

OpenPGP


OpenPGP это технология шифрования и подписи, которая обычно используется для шифрования и подписи сообщений электронной почты. Сообщества с открытым исходным кодом также часто используют его для подписания коммитов (тегов) исходного кода в репозиториях git. Это интернет-стандарт, определенный IETF в RFC480, и его можно рассматривать как открытую версию предыдущей проприетарной технологии PGP.

OpenPGP имеет собственный формат для ключей RSA. Ключи обычно хранятся в файле keyring и могут быть импортированы из простых файлов ключей OpenPGP. Наиболее удобный аспект keyring OpenPGP состоит в том, что открытые ключи могут быть связаны с адресами электронной почты их владельцев. Вы можете работать с несколькими получателями сообщения, просто выбрав список получателей по их адресам электронной почты, которые затем отображаются в открытых ключах для этих получателей. Кроме того, вокруг этой технологии была создана сеть доверия: вы можете найти открытые ключи многих пользователей, упорядоченные по их адресам электронной почты. Например, эти ключи часто используются для подписания тегов git.

Можно использовать формат зашифрованного сообщения OpenPGP для шифрования массовой рассылки сообщения для нескольких получателей. Заголовок сообщения OpenPGP содержит по одному блоку для каждого получателя. Каждый блок содержит 64-битный идентификатор ключа, который указывает алгоритму дешифрования, где необходимо пытаться дешифровать соответствующий закрытый ключ. После того как зашифрованный большой двоичный объект внутри блока расшифрован, он показывает симметричный ключ, который можно использовать для дешифрования массовой рассылки сообщения. Зашифрованный большой двоичный объект с открытым ключом каждого получателя показывает один и тот же симметричный ключ.

Мы использовали OpenPGP аналогичным образом, но в данном случае зашифрованное сообщение, которое он передает, не является слоем. Вместо этого оно содержит документ JSON, который, в свою очередь, содержит симметричный ключ, используемый для шифрования и дешифрования как слоя, так и вектора инициализации. Мы называем этот ключ ключом шифрования слоя (LEK), он представляет собой форму ключа шифрования данных. Преимущество этого метода в том, что нам нужен только один LEK. С помощью LEK мы шифруем слой для одного или нескольких получателей. У каждого получателя (образа контейнера) может быть свой тип ключа, и это не обязательно должен быть ключ OpenPGP. Например, это может быть простой ключ RSA. Пока у нас есть возможность использовать этот ключ RSA для шифрования LEK, мы сможем работать с несколькими получателями с разными типами ключей.

JSON Web Encryption (JWE)


JSON Web Encryption, также известное как JWE, является еще одним интернет-стандартом IETF и определено в RFC7516. Это более новый стандарт шифрования, чем OpenPGP, поэтому в нем используются более свежие низкоуровневые шифры, предназначенные для удовлетворения более строгих требований к шифрованию.

Если смотреть укрупненно, JWE работает по тому же принципу, что и OpenPGP, поскольку он также поддерживает список получателей и массовую рассылку сообщения, зашифрованного с помощью симметричного ключа, к которому имеет доступ каждый получатель сообщения. Получатели сообщения JWE могут иметь разные типы ключей, такие как ключи RSA, определенные типы ключей эллиптической кривой, предназначенные для шифрования, и симметричные ключи. Поскольку это более новый стандарт, все еще есть возможность расширения JWE для поддержки ключей в аппаратных устройствах, таких как TPM или модули аппаратной защиты (HSM), с использованием интерфейсов PKCS#11 или Key Management and Interoperability Protocol (KMIP). Использование JWE происходит аналогичным OpenPGP образом, если получатели имеют ключи RSA или эллиптические кривые. В будущем мы могли бы расширить его для поддержки симметричных ключей, таких как KMIP внутри HSM.

PKCS#7


PKCS#7, также известный как синтаксис криптографических сообщений (CMS), определен в стандарте IEFT RFC5652. Согласно Википедии о CMS, его можно использовать для цифровой подписи, дайджеста, аутентификации или шифрования любых форм цифровых данных.

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

Для поддержки ранее описанных технологий шифрования мы расширили документ манифеста, добавив следующую информацию:
  • Сообщения OpenPGP, JWE и PKCS#7 хранятся в карте аннотаций, которая является частью манифеста.
  • В каждом указанном слое содержится по одной карте. Карта аннотаций в основном представляет собой словарь со строками в качестве ключей и строками в качестве значений (пары ключ-значение).

Для поддержки шифрования образов мы определили следующие ключи:
  • org.opencontainers.image.enc.keys.openpgp
  • org.opencontainers.image.enc.keys.jwe
  • org.opencontainers.image.enc.keys.pkcs7

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

Чтобы определить, что слой был зашифрован с помощью LEK, мы расширили существующие медиатипы суффиксом '+encrypted', как показано в следующих примерах:
  • application/vnd.docker.image.rootfs.diff.tar+encrypted
  • application/vnd.docker.image.rootfs.diff.tar.gzip+encrypted

Эти примеры показывают, что слой заархивирован в файле .tar и зашифрован или оба заархивированы в файле .tar и сжаты в файл .gzip и зашифрованы. На следующем скриншоте показан пример манифеста, который ссылается на зашифрованные слои. Он также показывает карту аннотаций, содержащую зашифрованное сообщение JWE.



Многослойное шифрование с использованием симметричных ключей


Для симметричного шифрования с помощью LEK наша команда выбрала шифр, который поддерживает аутентифицированное шифрование и основан на стандарте шифрования AES со 128- и 256-битными ключами.

Пример реализации: containerd


Мы реализовали наш вариант в новом проекте среды выполнения контейнеров под названием containerd. Его исходный код на golang можно посмотреть, перейдя по ссылке. Docker-демон использует containerd для запуска некоторых своих служб, а у Kubernetes есть плагин для прямого использования containerd. Поэтому мы надеемся, что наши расширения для поддержки зашифрованных образов контейнеров пригодятся обоим.

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

Поддержка алгоритмов аутентифицированного шифрования в Golang принимает байтовый массив в качестве входных данных и выполняет весь этап его шифрования (запечатывания) или дешифрования (открытия), не допуская передачи и добавления дополнительных массивов в поток. Поскольку этот криптографический API требовал загрузки всего уровня в память или изобретения некоторой схемы для изменения вектора инициализации (IV) для каждого блока, мы решили не использовать аутентифицированное шифрование golang с поддержкой связанных данных (AEAD). Вместо этого мы использовали крипто-библиотеку miscreant golang, которая поддерживает AEAD в потоках (блоках) и реализует собственную схему изменения IV в каждом блоке. В нашей реализации мы разбиваем уровень на блоки размером 1 МБ, которые и передаем один за другим для шифрования. Такой подход позволяет снизить объем памяти при использовании аутентифицированного шифра. На стороне дешифрования мы делаем обратное и обращаем внимание на ошибки, возвращаемые функцией Open (), чтобы убедиться, что блоки шифрования не были подделаны.

На уровне выше симметричного шифрования, асимметричные криптографические схемы шифруют LEK уровня и вектор инициализации (IV). Для добавления или удаления криптографических схем мы регистрируем каждую асимметричную криптографическую реализацию. Когда API асимметричного криптографического кода вызывается для шифрования уровня, мы вызываем один за другим зарегистрированные криптографические обработчики, передавая открытые ключи получателей. После того как все ключи получателей используются для шифрования, мы возвращаемся к карте аннотаций с идентификаторами асимметричных криптоалгоритмов в качестве ключей сопоставления и со значениями, содержащими сообщения в кодировке OpenPGP, JWE и PKCS#7. Каждое сообщение содержит упакованные LEK и IV. Карты аннотаций хранятся в документе манифеста, как показано на предыдущем скриншоте.

Мы также можем добавлять получателей к уже зашифрованному образу. Авторы образов добавляют получателей, если они есть в списке. Для списка получателей используется закрытый ключ, необходимый для распаковки LEK и IV уровней. Затем мы упаковываем LEK и IV в новое сообщение, используя ключ нового получателя, и добавляем это сообщение к карте аннотаций.

Мы использовали три типа асимметричных схем шифрования для разных типов ключей. Мы используем ключи OpenPGP для шифрования сообщений OpenPGP. PKCS#7, которую мы используем, требует сертификаты x.509 для ключей шифрования. JWE обрабатывает все остальные типы ключей, такие как простые ключи RSA, эллиптические кривые и симметричные ключи. Мы создали прототип расширения для JWE, который позволяет выполнять криптографические операции с использованием ключей, управляемых сервером KMIP.

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

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

Точно также мы можем расшифровывать отдельные слои отдельных архитектур. Мы добавили подкоманду layerinfo, которая показывает статус шифрования каждого уровня и отображает технологии шифрования, используемые для него. Для OpenPGP мы также можем отображать идентификаторы ключей, необходимые для дешифрования, или преобразовывать их в адреса электронной почты их получателей с помощью keyring.

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

Когда простой (незашифрованный) образ извлекается из реестра, он автоматически распаковывается и разархивируется, чтобы из него можно было сразу создавать контейнеры. Чтобы упростить это действие для зашифрованных образов, мы предлагаем передавать закрытый ключ команде, которая занимается распаковкой образов, чтобы они могли расшифровать слои до распаковки. Если образ зашифрован с помощью нескольких ключей, команде pull можно передать несколько. Такая передача также поддерживается. После успешного извлечения зашифрованного образа из реестра любой пользователь, имеющий доступ к containerd, может создать контейнер из образа. Чтобы подтвердить, что пользователь имеет права на использование образа контейнера, мы предлагаем ему предоставить закрытые ключи, используемые для расшифровки контейнера. Мы используем ключи для проверки авторизации пользователя, могут ли они использоваться для расшифровки LEK каждого зашифрованного уровня, и если это подтверждается, разрешаем запуск контейнера.

Пошаговое руководство шифрования с использованием containerd


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

В первую очередь, нужно клонировать репозиторий git containerd/imgcrypt, который является подпроектом и может шифровать/дешифровать образ контейнера. Затем необходимо собрать containerd и запустить его. Чтобы выполнить эти шаги, нужно знать, как настраивается среда разработки golang:

Для imgcrypt требуется использовать containerd версии не ниже 1.3.

Собираем и устанавливаем imgcrypt:

# make# sudo make install

Запустите containerd с файлом конфигурации, который можно увидеть на примере ниже. Чтобы избежать возникновения конфликта в containerd, используйте директорию /tmp для каталогов. Также соберите containerd версии 1.3 из исходника, но не устанавливайте его.

# cat config.tomldisable_plugins = ["cri"]root = "/tmp/var/lib/containerd"state = "/tmp/run/containerd"[grpc]  address = "/tmp/run/containerd/containerd.sock"  uid = 0  gid = 0[stream_processors]    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]        accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]        returns = "application/vnd.oci.image.layer.v1.tar+gzip"        path = "/usr/local/bin/ctd-decoder"    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]        accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]        returns = "application/vnd.oci.image.layer.v1.tar"        path = "/usr/local/bin/ctd-decoder"# sudo ~/src/github.com/containerd/containerd/bin/containerd -c config.toml

Создайте пару ключей RSA с помощью инструмента командной строки openssl и зашифруйте образ:

# openssl genrsa --out mykey.pemGenerating RSA private key, 2048 bit long modulus (2 primes)...............................................+++++............................+++++e is 65537 (0x010001)# openssl rsa -in mykey.pem -pubout -out mypubkey.pemwriting RSA key# sudo chmod 0666 /tmp/run/containerd/containerd.sock# CTR="/usr/local/bin/ctr-enc -a /tmp/run/containerd/containerd.sock"# $CTR images pull --all-platforms docker.io/library/bash:latest[...]# $CTR images layerinfo --platform linux/amd64 docker.io/library/bash:latest   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS   0   sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609   linux/amd64   2789669                             1   sha256:7dd01fd971d4ec7058c5636a505327b24e5fc8bd7f62816a9d518472bd9b15c0   linux/amd64   3174665                             2   sha256:691cfbca522787898c8b37f063dd20e5524e7d103e1a3b298bd2e2b8da54faf5   linux/amd64       340                          # $CTR images encrypt --recipient jwe:mypubkey.pem --platform linux/amd64 docker.io/library/bash:latest bash.enc:latestEncrypting docker.io/library/bash:latest to bash.enc:latest$ $CTR images layerinfo --platform linux/amd64 bash.enc:latest   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS   0   sha256:360be141b01f69b25427a9085b36ba8ad7d7a335449013fa6b32c1ecb894ab5b   linux/amd64   2789669          jwe        [jwe]   1   sha256:ac601e66cdd275ee0e10afead03a2722e153a60982122d2d369880ea54fe82f8   linux/amd64   3174665          jwe        [jwe]   2   sha256:41e47064fd00424e328915ad2f7f716bd86ea2d0d8315edaf33ecaa6a2464530   linux/amd64       340          jwe        [jwe]

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

# docker pull registry:latest# docker run -d -p 5000:5000 --restart=always --name registry registry

Отправьте зашифрованный образ в локальный реестр, извлеките его с помощью ctr-enc, а затем запустите образ:

# $CTR images tag bash.enc:latest localhost:5000/bash.enc:latest# $CTR images push localhost:5000/bash.enc:latest# $CTR images rm localhost:5000/bash.enc:latest bash.enc:latest# $CTR images pull localhost:5000/bash.enc:latest# sudo $CTR run --rm localhost:5000/bash.enc:latest test echo 'Hello World!'ctr: you are not authorized to use this image: missing private key needed for decryption# sudo $CTR run --rm --key mykey.pem localhost:5000/bash.enc:latest test echo 'Hello World!'Hello World!


Заключение


Шифрование образов контейнеров это хорошее дополнение к их безопасности, оно обеспечивает конфиденциальность данных и целостность образов контейнеров в месте хранения. Предложенная технология основана на общедоступных технологиях шифрования RSA, эллиптической кривой и AES. Она применяет ключи к схемам шифрования более высокого уровня, таким как OpenPGP, JWE и PKCS#7. Если вы умеете работать с OpenPGP, вы можете шифровать образы контейнеров для получателей OpenPGP, используя их адреса электронной почты, в то время как простые ключи RSA и эллиптические кривые используются для шифрования, например, JWE.
Подробнее..

В IT-образовании не работают лекции. Давайте это признаем и начнем учиться правильно

02.12.2020 20:12:10 | Автор: admin
Представьте мир, где внезапно произошли две фантастические вещи родители потеряли возможность влиять на решения своих детей, полностью, абсолютно. Просто физически не могут дать им ни малейшего совета и вызвать чувство вины.

Второе в этом мире отменили армию.

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



Когда я думаю об этом, мне становится обидно. У меня есть пара друзей, которых вышвырнули из университета. Они любят говорить про это так: А знаешь, кто еще бросил вышку? Гейтс и Джобс! Сам я не враг высшему образованию, но мне не хочется с ними спорить.

Я чувствую, что у образования в наши дни большие просто гигантские проблемы. Думаю, лучше всего об этом сказал основатель Valve Гейб Ньюэлл: За несколько месяцев в Microsoft я узнал о разработке программ больше, чем за пару лет в Гарварде. В Гарварде я научился пить пиво, стоя на руках, это полезный навык, но не настолько.

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

Лекции не нужны


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

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

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

Но проблема не в преподавателях и не в информации проблема в самой идее лекций


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

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

В группах с обычными лекциями экзамен не сдавали, в среднем, 34% студентов, а в группах с активным обучением 22%. Средняя оценка студентов из групп активного обучения была выше почти на целый балл.



У меня создается впечатление, что, если вы посмотрите на эти данные, продолжать читать лекции просто неэтично, отозвался об исследовании физик из Гарвардского университета Эрик Мазур. Он выступает против чтения лекций уже 27 лет. Приятно видеть, как из обилия доказательств вырисовывается четкая картина чтение лекций неактуально, старомодно и неэффективно.


Лекции стали чуть ли не главной формой учебы


Заучивание огромных объемов теории давно стало фундаментальным принципом образования. Причем конфликт теории и практики не новый. Об этом спорили еще в Древней Греции, а то и раньше. Тот же Сократ известный спорщик считал, что наставник должен лишь задавать ученику правильные вопросы, на которые тот сам будет искать ответы.

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

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

Но подход Дьюи приводил к странным перегибам. Например, он говорил, что все знания, у которых не может быть практического применения не нужны вообще, а истинно только то, что полезно. Тем не менее, его подход был популярен в Америке и СССР начала прошлого века.

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

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

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


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


Мозгу нужна практика


Наш ум, память, навыки это все мозг. А мозг материальный орган, и в нем много банальных физических ограничений. Я помню, как меня ошеломило осознание того, что все знания, которые мы получаем, имеют вполне реальный вес в голове.

Есть знаменитая история о лондонских таксистах, рассказывала об этом Ася Казанцева. Буквально несколько лет назад для того, чтобы стать настоящим таксистом в Лондоне, нужно было сдать экзамен по ориентации в городе без навигатора то есть знать как минимум две с половиной тысячи улиц, одностороннее движение, дорожные знаки, запреты на остановку, а также уметь выстроить оптимальный маршрут. Ученые сделали [таксистам] томограмму, чтобы посмотреть плотность серого вещества в гиппокампе. Это важная зона мозга, связанная с формированием памяти и пространственным мышлением. Обнаружилось, что если человек не хотел становиться таксистом или хотел, но не стал, то плотность серого вещества в его гиппокампе оставалась прежней. А вот если он хотел стать таксистом, прошел тренинг и действительно овладел новой профессией, то плотность серого вещества увеличилась на треть это очень много.

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

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

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

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

В своем исследовании биофизик Джоел Майкл из медицинского колледжа Чикаго писал: Вероятно, первым, кто указал на разницу между знать, что нечто является правдой, и тем, как что-то сделать, был Гилберт Райл в книге The Concept of Mind. Учить факты это декларативное знание, а учить, как что-то делать, процедурное. Это два абсолютно разных процесса. Если вы хотите научить студентов решать какие-либо проблемы, вам необходимо предоставить им возможность делать это на практике.

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

Например, такое исследование провел Эйдан Хорнер, психолог из университета Йорка. Он взял два одинаковых по длине текста и дал их прочитать группе испытуемых. Затем попросил перечитать первый текст еще раз, а второй постараться записать на бумагу по памяти.

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

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

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



Мозг должен конструировать знания, а не впитывать


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

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

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

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

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

Конструктивистскии подход имеет ограничения: для создания контекстно ориентированного содержания обучения требуется много времени и труда, и еще больше времени и труда нужно для создания контента, которыи согласовывался бы с индивидуальными интересами и опытом учащихся. Педагог, использующии конструктивистскии подход к обучению, неизбежно ограничен в возможности сфокусировать внимание учащихся и направить его в определенное русло, и учащиеся в отсутствие внешних источников мотивации могут утратить интерес к работе. И наконец, в условиях конструктивистского обучения не всегда легко и даже не всегда возможно корректно оценить знания студентов, пишет Джанелли.

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

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


Обучение это стихийный феномен


Не так давно я узнал историю индийского ученого и просветителя Сугата Митра. Сейчас он занимается созданием Самоорганизованных Учебных Пространств мест, где дети могут учиться почти без преподавателей. Это очень долгая и сложная затея. Она началась с эксперимента, который Митра провел в Нью-Дели в конце 90-х.

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

Затем он провел такой же эксперимент в индийском городе Хайдарабаде. Митра собрал детей, которые говорили по-английски с сильным акцентом, дал им компьютер с программой, которая преобразовывает речь в печатный текст. Он попросил детей надиктовать слова но компьютер, конечно, записывал все неправильно.

Тогда я сказал: Хорошо, я оставлю компьютер здесь на два месяца. Сделайте так, чтобы компьютер вас понимал. Они спросили: Но как? А я сказал, что не знаю, и уехал. Через два месяца, благодаря этой программе, акцент у детей почти полностью пропал и они разговаривали на идеальном английском этот факт задокументирован в журнале Information Technologies & International Development, рассказывает Митра.

С тех пор он проводил подобные эксперименты во многих городах по всему миру. Оставлял группу детей с одним компьютером, давал задание и уезжал. И каждый раз результаты были феноменальными. Например, 12-летние дети из индийской деревни самостоятельно изучили биотехнологии на английском. Они сдали тесты на проходной балл, и результаты эксперимента были опубликованы в British Journal of Educational Technology.

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

Знаете, что произошло? Я думаю, мы только что натолкнулись на самоорганизующуюся систему. Ее структура возникает без явного внешнего вмешательства. Самоорганизующиеся системы всегда непредсказуемы, они начинают делать такое, для чего не были задуманы. Поэтому мы так реагируем, ведь это кажется невозможным. Думаю, могу сейчас выдвинуть такой тезис: образование это самоорганизующаяся система, в которой обучение стихийный феномен.


Нужны задачи вместо лекторов


Несколько лет назад мы открыли свою компанию Fevlake чтобы настраивать IT-компаниям инфраструктуру. У нас был очень широкий стек технологий, и набрать на рынке много сильных девопсов в такую команду было очень сложно. Поэтому мы решили учить людей для себя сами.

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

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

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

И вот первый приятный сюрприз правильных и крутых решений, оказывается, намного больше, чем мы могли себе представить. То есть, мы составляем задачу, подбираем к ней идеальный правильный ответ, а на практике оказывается, что правильных ответов два, три, четыре и больше. Люди думают по-разному, находят разные решения это просто невероятно круто!

В итоге, Fevlake был укомплектован спецами. Ребята, которых мы учили, уже работают лидами в нашей компании. За два года эта методология так выстрелила, что нам пришлось открывать еще одну компанию Rebrain, которая занимается чисто подготовкой DevOps-специалистов. Крупнейшие IT-компании, банки и корпорации заказывают у нас разработки подобных программ для себя и отправляют к нам своих специалистов.

И вот я рассказываю бывалым специалистам, как у нас все устроено, и слышу Круто, мне такого не хватало, когда я учился.
Подробнее..

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

05.10.2020 20:08:11 | Автор: admin


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

Компания, которая написала, занималась аналитикой данных. Ежедневно она обрабатывала тысячи запросов. К нам они пришли со словами: ребят, у нас есть ClickHouse и мы хотим автоматизировать его настройку и установку. Хотим Ansible, Terraform, Докер и чтобы это все хранилось в гите. Хотим кластер из четырех нод по две реплики в каждой.

Стандартная просьба, каких десятки, и нужно такое же хорошее стандартное решение. Мы сказали окей, и через 2-3 недели все было готово. Работу они приняли и начали переезжать на новый кластер Кликхауса с помощью нашей утилиты.

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

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

Вместе мы принялись за бэкапы. Я предлагал бэкапить сразу исходные данные. Просто брать, зиповать и элегантно закидывать в какой-нибудь с3. Исходные данные это золото. Был и другой вариант бэкапить сами таблицы в Кликхаусе, с помощью фриза и копирования. Но Лёня придумал свое решение.

Он объявил, что нам нужен второй кластер Кликхауса. И отныне мы будем писать данные на два кластера основной и бэкапный. Я ему говорю, мол, Лёня, выйдет не бэкап а активная реплика. И если данные начнут теряться на продакшене, на твоем бэкапе будет то же самое.

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

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

Мы еще не подозревали, что получили этот заказ из-за страшного недопонимания внутри их команды.


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

Все это выяснилось очень-очень болезненно. А самое обидное, это было в мой день рождения.



Пятница, вечер. Я забронировал столик в любимом винном баре и позвал корешей.

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

В ресторане было по-пятничному шумно. Заказав выпивки, еды, развалились на диванчиках. Все это время мой слак потихоньку заваливали сообщения. Писали что-то про нехватку данных. Я подумал утро вечера мудренее. Особенно сегодняшнего.

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

И услышал что-то в духе: Вы просрали наши данные! Я плачу вам, но ничего не работает! Вы отвечали за бэкапы, и не сделали нихрена! Давайте чините! только еще грубее.

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

Вот так я не сказал. Вместо этого достал ноут и принялся за работу.

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

Остановили запись, посчитали число ивентов, которые там лежат за день. Закинули еще данных, от которых не записалась только треть. Три шарды по 2 реплики. Вставляешь 100.000 строк 33.000 не записываются.

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

Что происходило на самом деле никто не понимал.


Мы с парнями просто охренели, когда поняли, что треть всех данных не просто не записывалась она терялась! Оказалось, порядок в компании был такой: после вставки данные удалялись безвозвратно, ивенты просирались пачками. Я представил, как Сергей конвертирует все это в недополученные рубли.

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

Я не давал клятву разраба, но бросать парней на том конце провода было непорядочно пусть даже они во всем винили нас. Я на 99% был уверен, что проблема залегла не в наших решениях, не на нашей стороне. 1% шанса, что облажались все же мы, жег тревогой. Но на чьей бы стороне беда не находилась это надо было пофиксить. Оставлять клиентов, какими бы они ни были, с такой страшной течью данных это слишком жестоко.

До трех утра мы работали за столиком ресторана. Докидывали ивентов, insert select и погнали заполнять пробелы. Когда ты просрал данные, делается так ты берешь средние данные за предыдущие дни и вставляешь их в просранные.

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

К 6 утра я пересоздал таблицу заново, и данные начали заливаться. Все заработало без потерь.



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


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

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

Мне хотелось справедливости. Я откопал переписку и приложил всех скриншотами, где Леонид со всей силы заставляет делать такой бэкап, какой был сделан. Их СТО встал на нашу сторону после моего телефонного звонка. После свою вину признал и Леня.

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

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



Это была самая стремная встреча в моей карьере. Мой союзник от клиента СТО не смог найти время. На встречу я ехал к боссу и Лёне.

Раз за разом я прокручивал в голове наш возможный диалог. Умудрился приехать сильно заранее, за полчаса. Начался нервяк, я выкурил сигарет 10. Я понимал, всё я, нахрен, один. Я не смогу их переубедить. И шагнул в лифт.

Пока поднимался, так чиркал зажигалкой, что сломал ее.

В итоге Лёни на встрече не оказалось. И мы отлично обо всем поговорили с главным! Сергей рассказал мне про свою боль. Он хотел не автоматизировать Кликхаус он хотел, чтобы запросы работали.

Я увидел не козла, а хорошего парня, переживающего за свой бизнес, погруженного в работу 24/7. Чат частенько рисует нам злодеев, подлецов и тупиц. Но в жизни это такие же люди, как и ты.

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

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

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



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

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

Штатные разрабы некорректно использовали инструмент для аналитики. Они ходили в графану, писали свой царский запрос. Он выгружал данные за 2 недели. Получался красивый график. Но на деле запрос данных шел каждые 10 секунд. Все это копилось в очередь, поскольку Кликхаус попросту не вывозил обработку. Тут и скрывалась основная причина. В графане ничего не работало, запросы стояли в очереди, постоянно прилетали старые неактуальные данные.

Мы перенастроили кластер, переделали вставку. Штатные разработчики переписали свой вставлятор, и он начал шардировать данные корректно.

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

Собственно, на этом мы и расстались сделали, что смогли.



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

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

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

P.S. Так что, если у вас есть вопросы по вашей инфраструктуре, смело оставляйте заявку.

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

Когда-то я внедрял ClickHouse в стартапе, где даже алерты мониторили индийцы это был Дикий Запад

13.01.2021 20:16:14 | Автор: admin

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

Чтобы мониторить сеть и сервера, можно использовать самый разный софт. Главное быть уверенным: если что-то пойдет не так, то сработает алерт. Вы увидите уведомление и успеете все починить. У них же было свое представление о мониторинге. Представьте, несколько комнат со столами, стульями и огромными плазмами на стенах, внутри сидят почти два десятка человек. На каждый монитор выведены разноцветные графики и рядом приклеены бумажки с номерами телефонов.

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

Покупка компании обошлась недорого, но содержание такой инфраструктуры стоило заоблачных денег. Индусы использовали дорогущую Vertica, где, кроме оплаты железа, нужно было еще отстегивать за лицензию. Мы решили попробовать переезд на ClickHouse. Это практически бесплатный аналог Vertica. Оба продукта работают по схожему принципу: колоночное СУБД с шардированием, с партиционированием данных.

И это было то еще приключение.


Киллер-фича ClickHouse конечно, экономия денег

Как раз то, что нам было нужно. ClickHouse умеет писать миллионы ивентов в секунду и также быстро читать. Если правильно организовать агрегационную таблицу в ClickHouse, она будет выполняться за минуту, когда в классических агрегационных базах (например, PostgreSQL) запрос выполняется час.

Я загорелся ClickHouse на одном кейсе, когда провел тест на сжатие с новенькими кодеками: тот же DoubleDelta ужал наши полуслучайные последовательности в пять раз! И ClickHouse не смущает количество данных, если подобрать правильный ключ сортировки и степень детализации индекса в настройках MergeTree.

В результате увидел, что данные, которые на HDFS занимали гигабайты, в ClickHouse уместились в 700 с хвостиком мегабайт, а скорость запросов по ним была на порядок выше. Особенно, когда получается удачно переписать стандартный хакерский SQL-запрос на тыщу строк во что-то максимально понятное ClickHouse, с использованием корректных встроенных функций, массивов, агрегатов и других подходящих альтернатив.

Но в то время у инструмента был большой минус высокий порог вхождения. Результат комбинации маленького на тот момент комьюнити, не сильно выходящего за пределы СНГ, фундаментальных отличий ClickHouse от обычных СУБД и невнятной документации. Приходилось проводить личные опыты: грузить в него TSBS и экспериментировать с функциями, разными версиями, настройками движков и так далее доходило даже до флагов компиляции. Драйвер существовал для галочки он использовал http-протоколы ненативно, просто обертка над Rest клиентом.

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


Как построить бэкенд так, чтобы ничего не падало, не сжирало тонну денег, могло хранить и обеспечивать доступ к отчетам?

При условии, что есть миллиарды строк данных, терабайты, километры.

Компания занималась монетизацией пираток. Человек пиратил андроид-приложение, не платил за него, но видел рекламу. Для того, чтобы посчитать клиента по биллингу, посмотреть, сколько он накликал рекламы, компания начала собирать статистику. До моего прихода там были написаны собственное SDK и бэкенд. PHP принимал JSON ивент, его парсили и писали в MySQL. Никаких пулов соединений не было, пришел ивент открыли и записали.

Когда нагрузка стала расти (несколько тысяч событий в секунду), эта система перестала вывозить. Бэкенд-разработчик узнал про Hadoop, HDFS и решил его применить. Собрали кластер из нескольких машин. Идея была такой: просто пишем JSON-файлы, запихиваем в Hive. И вот уже все считается и работает.

Когда ребята начали мигрировать биллинг на Hive, поняли, что у них резко вырос чек за кластер. Все хранилось в виде несжатых JSON-файлов. К тому же HDFS и Hadoop не были заточены на риал-тайм вычисления. Приходилось планировать любую джобу заранее. Ставили на всю ночь, утром видели результат в виде огромной кучи неструктурированных данных, которые лежат прямо в тексте. И все это за деньги! Скорость никакая, запрос делать долго, на выходе свалка. Это никого не устраивало.Когда я начал выяснять, как устроена текущая архитектура проекта, то оказалось, что Spark используется в автономном режиме на нескольких узлах, что выглядело подозрительным и специфичным. Разобравшись в скрипте запуска, я понял, что текущие настройки приводили к тому, что узлы грузились на все двести, а RDD все равно читались в один поток.

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

Тем не менее, при правильном распараллеливании джобы на выходе получались не совсем корректные данные. Документация по коду, как это традиционно в наших краях, была устаревшая, поэтому пришлось максимально сосредоточиться, чтобы эффективно раскурить текущие исходники на Java, понять желаемую бизнес-логику и починить багу. В итоге получилось совершенно новое приложение на Scala, которое работало настолько лучше, что отдел инфраструктуры смог спланировать снижение стоимости поддержки бэкенда в тысячи долларов!

И наконец мы начали добавлять запись в ClickHouse. Исходный код сервера будет достаточно понятным любому, кто знает C++. Он также иногда покрыт тестами, а если и нет, то зачастую можно найти тест, реализующий схожий кейс. Так что в итоге у нас появился свой отказоустойчивый драйвер на Scala, работавший по TCP, в бинарном Native формате, учитывающий конфигурацию кластера и позволявший нам максимально эффективно писать в несколько потоков.

Я решил еще доработать бэкенд и рискнул убрал HDFS. Поставил нормальный кластер Kafka, бэкенд стал писать в нее, из Kafka все читалось консьюмером и писалось в ClickHouse. При этом я не исключил кластер HDFS из схемы совсем, сделав из него подобие бэкапа: данные залетали и в урезанный HDFS тоже.

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

Но главная проблема оказалась не в самом ClickHouse а в том, что никто не мог сходу в нем разобраться

Если для того, чтобы научиться эффективно писать в ClickHouse и создать production-ready сервис, достаточно было только моих вложений, то с запросами и, в целом, с полноценным использованием DWH ситуация обстояла несколько иначе.

По опыту получалось, что задачи, которые отдел аналитики и разработчики раньше могли решить с наскока в каком-нибудь Hive или MySQL, в ClickHouse так сходу не решались где-то функцию надо использовать, где-то JOIN из-за дистрибуции данных некорректный и т.д.

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

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

Например, можно вспомнить раннюю поддержку UUID, когда запрос вида:

```SELECT * FROM db PREWHERE uuid != '00000000-0000-0000-0000-000000000000'```

Приводил к segfault.

Или когда можно было случайно потерять данные в процессе дедупликации реплицированных партиций. Уникальных случаев у комьюнити наберется ого-го! Поэтому получается, что иногда даже полезнее и достаточно искать свою проблему именно в GitHub, в Issues, по ключевым словам.


А потом оказалось, что данные из ClickHouse слишком трудно визуализировать

Тот же Hive предлагал инструменты, которые позволяли писать запросы и делать визуализацию. Поначалу мы использовали Tabix. Это довольно примитивный клиент, он ходит напрямую в ClickHouse и отображает в браузере результат в виде таблички и простых графиков. Но в итоге взяли инструмент поинтереснее. Redash как раз выкатил бета-версию поддержки ClickHouse.

Параллельно у нас появилась часть данных на базе Redshift. В какой-то момент компания предложила брать данные из ClickHouse и перекладывать в Redshift (который, по сути, обычный SQL и можно использовать инструменты желанной визуализации). Но идея отпала сама собой я просто посчитал, сколько будет стоить кластер Redshift, который поддержит такой поток данных с ClickHouse. Мы тратили около тысячи долларов, если бы заморочились с Redshift платили бы все 30 тысяч. Поэтому продолжили работать с ClickHouse через Redash.

Но все пошло прахом, когда мы решили прикрутить к ClickHouse Tableau и в итоге влетели на 70 тысяч долларов!

Tableau это известная визуализация для баз данных. Когда мы решили это провернуть, официальной поддержки ClickHouse там не было. Зато у них была поддержка PostgreSQL. Какой-то безумец подсказал, что в PostgreSQL можно писать кастомные скрипты и протоколы форвардинга. Через питоний скрипт скрестить ClickHouse и PostgreSQL и добиться визуализации в Tableau! Это была очередная провальная идея. Данные шли только с PostgreSQL. Смысла в этом не было никакого.

Но наш аналитик настаивал. Ему нравился Tableau красивый, понятный, никаких тебе запросов SQL делать не надо, все кнопочками. И нам его все же купили. Потратили около 70 штук и принялись танцевать с бубном вокруг покупки.

Мы даже созвонились с продажниками из Tableau и убеждали их подружить свой продукт с новейшей российской технологией. Уже тогда было понятно, что ClickHouse потихоньку завоюет весь мир. Они лишь вежливо выслушали нас. Что забавно, недавно Tableau сделал кое-какую поддержку драйвера для ClickHouse. Не прошло и двух лет!

Перевести отчеты с Redash на Tableau у нас так и не получилось деньги улетели на ветер. Хорошо, что к тому моменту часть сотрудников освоила ClickHouse я потихоньку всех обучал. Оказалось, что он удобен для андроид-разработчиков и предлагает даже больше возможностей, чем красавчик Tableau.


Самая большая проблема ClickHouse в нем никто не шарит. Там много подводных камней, которые нигде не описаны. Хорошая технология с плохой документацией.

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

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

Подробнее..

Неужели нельзя обойтись без кафок и рэббитов, когда принимаешь 10 000 ивентов в секунду

21.01.2021 20:07:17 | Автор: admin

Однажды я вел вебинар про то, как принимать 10 000 ивентов в секунду. Показал вот такую картинку, зрители увидели сиреневый слой, и началось: Ребят, а зачем нам все эти кафки и рэббиты, неужели без них не обойтись? Мы и ответили: Зачем-зачем, чтобы пройти собес!

Очень смешно, но давайте я все-таки объясню.


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

Но кликхаус любит, когда в него пишут сообщения пачками

Другими словами, в него лучше запихнуть миллион сообщений, вместо того чтобы писать по одному. Kafka, Rabbit или Яндекс.Кью выступают как буфер, и мы можем контролировать с его помощью входящую нагрузку.

Как бывает: в одну секунду пришло 10 тысяч ивентов, в следующую тысяча, в другую 50 тысяч. Это нормально, пользователи рандомно включают свои мобильные приложения. В таком случае в кликхаус напрямую будет заходить то 2 тысячи, то 10 тысяч сообщений. Но с помощью буфера вы можете подкопить сообщения, потом достать из этой копилки миллион ивентов и направить в кликхаус. И вот она желанная стабильная нагрузка на ваш кластер.

Это все история про очереди

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

Например, для бэкграунд задач. Вы заходите в админку магазина и генерируете отчет по продажам за год. Задача трудоемкая: нужно прочитать миллионы строк из базы, это хлопотно и очень долго. Если клиент будет висеть постоянно с открытым http-коннектом 5, 10 минут связь может оборваться, и он не получит файл.

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

Второй кейс про кучу микросервисов, которые общаются через шину.

Например, один сервис принимает ивенты от пользователей, передает их в очередь. Следующий сервис вытаскивает ивенты и нормализует их, к примеру, проверяет, чтобы у них был валидный e-mail или телефон. Если все хорошо, он перекладывает сообщение дальше, в следующую очередь, из которой данные будут записываться в базу.

Еще один поинт это падение дата-центра, в котором хостится база.

Конец, ничего не работает. Что будет с сообщениями? Если писать без буфера, сообщения потеряются. Кликхаус недоступен, клиенты отвалились. До какого-то предела выручит память, но с буфером безопаснее вы просто взяли и записали сообщения в очередь (например, в кафку). И сообщения будут храниться там, пока не закончится место или пока их не прочитают и не обработают.


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

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

Мы создаем инстанс-группу. Задаем ей имя и указываем сервисный аккаунт. Он будет использоваться для создания виртуалок.

resource "yandex_compute_instance_group" "events-api-ig" {  name               = "events-api-ig"  service_account_id = yandex_iam_service_account.instances.id

Затем указываем шаблон виртуалки. Указываем CPU, память, размер диска и т.д.

instance_template {    platform_id = "standard-v2"    resources {      memory = 2      cores  = 2    }    boot_disk {      mode = "READ_WRITE"      initialize_params {        image_id = data.yandex_compute_image.container-optimized-image.id        size = 10      }

Указываем, к какому сетевому интерфейсу его подрубить.

}    network_interface {      network_id = yandex_vpc_network.internal.id      subnet_ids = [yandex_vpc_subnet.internal-a.id, yandex_vpc_subnet.internal-b.id, yandex_vpc_subnet.internal-c.id]      nat = true    }

Самое интересное это scale_policy.

Можно задать группу фиксированного размера fixed scale с тремя инстансами A, B, C.

scale_policy {    fixed_scale {      size = 3    }  }  allocation_policy {    zones = ["ru-central1-a", "ru-central1-b", "ru-central1-c"]  }

Либо использовать auto_scale тогда группа будет автоматически масштабироваться в зависимости от нагрузки и параметров.

scale_policy {auto_scale {    initial_size = 3    measurment_duration = 60    cpu_utilization_target = 60    min_zone_size = 1    max_size = 6    warmup_duration = 60    stabilization_duration = 180}

Главный параметр, на который надо обратить внимание, это cpu utilization target. Можно выставить значение, при превышении которого Яндекс.Облако автоматически создаст нам новую виртуалку.

Теперь протестируем автомасштабирование при увеличении нагрузки

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

Перед нашей инстанс-группой стоит load-балансер. Он принимает все запросы, которые приходят на адрес 84.201.147.84 на порту 80, и направляет их на нашу инстанс-группу на порт 8080.

У меня есть виртуалка, которая с помощью Yandex.Tank делает тестовую нагрузку. Для теста я установил 20 тысяч запросов в течение 5 минут.


Итак, нагрузка пошла.

Сначала все ноды будут загружены во всех трех зонах (A, B и C), но когда мы превысим нагрузку, Яндекс.Облако должно развернуть дополнительные инстансы.

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

При этом у меня был интересный момент. Один инстанс, который находится в регионе С, записывал данные (от момента приема данных до записи) за 23 миллисекунды, а у инстанса из региона А было 12,8 миллисекунд. Такое происходит из-за расположения кафки. Кафка находится в регионе А, поэтому в нее записи идут быстрее.

Ставить все инстансы кафки в одном регионе не надо.

Когда добавилась еще одна машина, новая нагрузка спала, показатель CPU вернулся к норме. Полную аналитику по тестовому запуску можно посмотреть по ссылке: overload.yandex.net/256194.


Как написать приложение для работы с очередями и буферами обмена

Приложение написано на golang. Сначала мы импортируем встроенные модули.

package mainimport (    "encoding/json"    "flag"    "io"    "io/ioutil"    "log"    "net/http"    "strings")

Затем подключаем github.com/Shopify/sarama это библиотека для работы с кафкой.

Прописываем github.com/prometheus/client_golang/prometheus, чтобы метрики передавались в API Metrics.

Также подключаем github.com/streadway/amqp для работы с rabbitmq.

Затем следуют параметры бэкендов, в которые мы будем записывать.

var (    // Config options    addr     = flag.String("addr", ":8080", "TCP address to listen to")    kafka    = flag.String("kafka", "127.0.0.1:9092", "Kafka endpoints")    enableKafka    = flag.Bool("enable-kafka", false, "Enable Kafka or not")amqp    = flag.String("amqp", "amqp://guest:guest@127.0.0.1:5672/", "AMQP URI")enableAmqp    = flag.Bool("enable-amqp", false, "Enable AMQP or not")sqsUri    = flag.String("sqs-uri", "", "SQS URI")sqsId    = flag.String("sqs-id", "", SQS Access id")sqsSecret    = flag.String("sqs-secret", "", "SQS Secret key")enableSqs    = flag.Bool("enable-sqs", false, "Enable SQS or not")        // Declaring prometheus metrics    apiDurations = prometheus.NewSummary(        prometheus.SummaryOpts{            Name:       "api_durations_seconds",            Help:       "API duration seconds",            Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},        },    )

Адрес кафки (строка).

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

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

Первое это кафка.

Второе amqp для рэббита.

И третья очередь sqs для Яндекс.Кью.

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

В main мы включаем кафку, рэббит и создаем очередь с названием Load.

И если у нас включен sqs, мы создаем клиент для Яндекс.Кью.

Дальше наше приложение по http принимает несколько инпоинтов:

/status просто отдает okey, это сигнал для load-балансера, что наше приложение работает.

Если вы кидаете запрос на /post/kafka, ваша джейсонка попадет в кафку. Также работают /post/amqp и /post/sqs.

Как работает кафка

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

Как-то на одном из проектов важно было уложиться в маленький бюджет. И вот представьте, мы берем самые дешевые машины без SSD (а кафка пишет последовательно и читает последовательно, так что можно не тратиться на дорогие диски), ставим кафку и zookeeper. Наше скромное решение на три ноды спокойно выдерживает нагрузку 200 тысяч сообщений в секунду! Кафка это про поставил и забыл, за пару лет работы кластер ни разу нас не потревожил. И стоил 120 евро в месяц.

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

Кафка устроена так: у вас есть topic, можно сказать, что это название очереди. Каждый topic бьется на части до 50 partitions. Эти партиции размещаются на разных серверах.

Как вы видите на схемке, topic load разбит на 3 партиции. Partition 1 оказывается на Kafka 1, вторая партиция на кафка 2, третья на 3. Тем самым нагрузка полностью распределяется. Когда кластер начинает принимать нагрузку, сообщения пишутся в один топик, а кафка раскидывает их по партициям, гоняет их по кругу. В итоге все ноды нагружаются равномерно.

Можно заморочиться и разбить топик на 50 партиций, поставить 50 серверов и расположить на каждом сервере по 1 партиции нагрузка распределится на 50 нод. И это очень круто.

Партиции могут реплицироваться благодаря zookeeper. Кафке необходимо минимум 3 ноды зукипера. Например, вы хотите, чтобы ваша партиция реплицировались на 2 ноды. Указываете репликейшн фактор 2 и каждая партиция будет закинута 2 раза на рандомные хосты. И если ваша нода упадет, то благодаря зукиперу кафка это увидит: ага, первая нода в дауне, кафка 2 заберет себе первую партицию.

Как я разворачивал кафку с помощью Terraform

В репозитории у нас есть terraform-файл, он называется kafka.tf .

Вначале мы поднимем 3 зукипера: resource yandex compute instance zookeeper count = 3.

Потом находим zookeeper_deploy, который деплоит наш зукипер. Хорошо, если он будет вынесен на отдельные машины, где кроме него ничего нет. Далее собираем айдишники нод и генерируем файл. Запускаем ansible для настройки зукипера.

Кафку поднимаем аналогично зукиперу и, что важно, после него.

Как работает RabbitMQ

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

Рэббит уже не так прост тут вам и exchanges с роутингом, и куча плагинов для delayed messages, deadletter и прочего хлама. За сообщениями следит сам кролик. Как только консьюмер подтвердил обработку сообщения, оно удаляется. Если консьюмер отвалился посередине рэббит вернет сообщение в очередь. В общем, хороший комбайн, когда нужно перекидывать сообщения между сервисами. Цена этого производительность.

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

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

И еще: рэббиту не нужен зукипер.

Подробнее..

Популярные задачи для собеседований бэкенд-разработчиков на Go и их решения

01.02.2021 20:15:32 | Автор: admin

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

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

Тогда я решил попробовать Go.


Go простой, классный и востребованный

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

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

Как говорил Роб Пайк: Простота это сложно. Философия, все дела. Кто-то ругает голанг за излишнюю простоту, а мне она нравится.

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

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

Эйчары написали мне через месяц и сказали, что уже готовят оффер. Потом написали еще через час и сказали, что, к сожалению, иностранцев они уже не берут.

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

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


Популярные задачи на собеседованиях

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

Стандартная задача с leetcode и ее довольно часто спрашивают на собеседованиях в качестве простой задачи для разогрева.

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

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

Решение

https://play.golang.org/p/1WQWZdsevf7

Написать генератор случайных чисел

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

Плюс ее можно использовать в немного измененном виде в задаче на слияние N каналов.

Решение

https://play.golang.org/p/T0McDy7TfTG

Слить N каналов в один

Даны n каналов типа chan int. Надо написать функцию, которая смерджит все данные из этих каналов в один и вернет его.

Мы хотим, чтобы результат работы функции выглядел примерно так:

for num := range joinChannels(a, b, c) {

fmt.Println(num)

}

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

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

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

Решение

https://play.golang.org/p/31sWw60MkFM

Сделать конвейер чисел

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

Довольно частая задача, более подробно можно почитать тут https://blog.golang.org/pipelines.

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

Решение

https://play.golang.org/p/BmVfWzGszEU

Написать WorkerPool с заданной функцией

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

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

Решение

https://play.golang.org/p/9aQi63fiL41

Сделать кастомную waitGroup на семафоре

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

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

Решение

https://play.golang.org/p/w-QokHyWcW5

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


Мысли о будущем Go

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

Никто не ругает топор, за то что он такой топорный, да и вообще он не бензопила.

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

Но сейчас на Go все чаще пишут гигантские системы. Наделали фреймворков (само по себе понятие фреймворк противоречит идее Go), разработали инструменты, чтобы затащить его на фронтенды, адаптировать к разработке огромных приложений, стали делать на нем все, что не приколочено. Начали требовать серьезного развития языка в сторону усложнения чтобы было как в Java и C#. Чтобы у нас, значит, тоже получалось пилить грандиозные монолитные бекенды, обмазывать всё тоннами декораторов, писать универсальный, сверхпереиспользуемый код и все такое.

Посмотрите на С++. Чтобы угодить потребностям всех разработчиков на плюсах, язык набили фичами настолько, что всерьез вот сесть и выучить С++ невозможно. А уж выбрать его в качестве инструмента для разработки, когда есть альтернативы абсолютно исключено.

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

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

Подробнее..

Деплоим проект на 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.

Подробнее..

Я 8 лет работал сисадмином в провинции но ушел в Devops, когда меня снова попросили чинить клавиатуры

01.06.2021 18:16:36 | Автор: admin

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

На втором курсе после практики в одной из двух IT-компаний моего города я напросился к ним на работу. Меня взяли на зарплату в 15 тысяч рублей. Шел 2012 год, понятие девопс только зарождалось, а я начал работать помощником системного администратора. Задачи в течение дня сводились к обслуживанию Windows серверов, установке поставок, написанию инструкций и проверке того, что сделали программисты. Я крутился между серверной и институтом во вред учебе, в приоритете было удержаться на полученном месте.

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

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

В свободное время я занимался интересными для себя вещами: начал автоматизировать, стал смотреть, что такое Ansible. Однажды это помогло, я подготовил и установил среду у клиента, которая состояла из Docker-контейнеров. Интернета не было, все было изолировано, с собой у меня были Docker-образы. Ansible помог раскатать все это на изолированном и открытом контурах.

Только к 2019 году в моей работе появилось хоть какое-то развитие. Пришел Линукс, на слуху уже был девопс. Мы стали писать микросервисы, можно было поковыряться с Docker-файлами, пособирать контейнеры из CI/CD. К тому моменту у меня прибавилось много обязанностей, навалилось командировок. Я работал свой дневной график и сидел в серверной вечерами. Паренек-сисадмин, я настолько приелся в офисе, что мое мнение никому не было интересным. Но случись любой факап крайним, конечно, становился я. Мне это совершенно не нравилось.

Единственной отдушиной стали курсы по Линуксу, которые компания мне оплатила. Тогда я впервые познакомился с дистанционным обучением. Потом увидел практикумы Васи Озерова из Ребрейн. Тема была про Nginx, про балансировщики. Я поразился тому, сколько у человека энергии, и впервые подумал, что занимаюсь чем-то не тем, и как круто было бы работать с такими спецами вместе.

8 лет я проработал в провинциальной компании сисадмином. Потом руководство сменилось, и я стал старшим сисадмином. Понимаете?

Зарплата специалиста с 7 летним опытом и высшим образованием еле-еле дотягивала до 50 тысяч рублей. Я понятия не имел, что такое айтишная зарплата. В другом регионе в таком же провинциальном городе у меня жил знакомый. Он был моим кумиром: работал в IT, схватывал все на лету, вместо того, чтобы сменить регион на Москву, уехал в Беларусь и попал в EPAM Systems. Прошел собеседование на английском с кодингом.

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

Но я быстро понял, что мои знания в девопс не рыночные.

Мой большой опыт в индустрии сводился к винде и поверхностным знанием Docker. Работая с Ansible я больше оперировал shell-командами, нежели использовал модули. Тем не менее, набравшись смелости, я попал на собеседование. Гуру Линукса я себя не считал, но ответил на все вопросы по нему. Человек, который меня собеседовал, даже отметил, что по Линуксу у меня и правда все хорошо. Но вот стек девопса и автоматической развертки, к сожалению, был слабоват для этой вакансии.

На работе все начало надоедать. Последней каплей стала очередная задача: срочно починить кому-то клавиатуру.

Это что насмешка над трудом сисадмина, который проработал в компании столько лет? Тогда я решился, взял практикумы Ребрейна в рассрочку, и этого момента у меня закончилось свободное время. Исчезли выходные и вечера. Я много работал и параллельно осваивал курсы. Спустя три месяца я закончил Git, прошел Terraform, прокачал связку Terraform + Ansible. Понял, как пользоваться модулями, скачивать плейбуки в Ansible Galaxy. И самое главное начал все это дело хоть как-то читать и понимать. Позже я защитил практикум по Docker. Это было похоже на дипломный проект. Мне дали задание, я его сделал, потом отстаивал работоспособность, отвечал на вопросы. Параллельно я начал проходить практикум по Кубернетису. И решил, наконец, сменить работу.

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

Никакой ручной правки. Не в обиду админам будет сказано, но когда я и другие новички пытались работать руками, нам говорили: Вы что как админы? Только через Git.

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

Я расправил плечи и успешно прошел собеседование на девопса в немецкую компанию. Для этого я подтянул английский, наняв себе репетитора. По ТЗ нужно было запустить приложение в Кубернетесе, с зависимым чат-сервисом, postgresql, и чтобы базы данных хранились на хостовом пути. Правда, совмещать две работы у меня не получилось. Немцы учинили жесткий контроль за моей активностью, даже за тем, как у меня дергается мышка. Каждые 3 минуты около 15 раз скринился экран. Я подумал, что это не дело. Да и оффер, если честно, был копеечный. 7 долларов в час, около 70 тысяч рублей в месяц и вывести их было адски сложно.

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

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

На предыдущей работе все сидели и пыхтели в свой мониторы.

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

Часто вспоминаю тот день, когда решил вкладывать в свое развитие. Все произошло довольно быстро, за два года. Переломный момент попробовать учиться дистанционно. Если бы так и сидел в серверной, наверняка бы ничего не достиг. При этом у меня нет ощущения, что я потерял время. Все таки я познакомился за это время с многими интересными людьми. Тот парень из EPAM переехал в Амстердам и работает в Google Cloud. У меня бывают вопросы по работе с клиентскими облаками на гугле. Я набираю его и мы общаемся, он меня прокачивает по клауду. Я ни разу не видел этого человека вживую, но дружба с ним дала мне очень многое.

Я понимаю, если я работаю девопсом в хорошей московской компании, то и в хорошей нью-йоркской буду востребован.


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

Подробнее..

Как создавать и использовать словари в ClickHouse

06.08.2020 12:10:31 | Автор: admin


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


Что такое словари в ClickHouse?


Словарь это отображение данных в виде key value. Появление словарей очень упростило использование сторонних источников данных в ClickHouse, автоматизировав все необходимые ETL-процессы для доставки данных в пригодную для запросов форму.
Из преимуществ использования словарей в ClickHouse можно выделить несколько пунктов:


  • ClickHouse имеет поддержку различных вариантов расположения словарей в памяти.
  • Поддержка TTL ClickHouse автоматически обновляет словари и подгружает отсутствующие значения.
  • ClickHouse предоставляет несколько вариантов для описания внешних словарей XML-файлы и DDL-запросы.

Подключение словарей


Подключить собственные словари можно из различных источников данных: локального текстового/исполняемого файла, HTTP(s) ресурса, другой СУБД и т.д.


Конфигурация этих словарей может находиться в одном или нескольких xml-файлах, путь к которым указывается в параметре dictionaries_config в конфигурационном файле ClickHouse.


Словари могут загружаться при старте сервера или при первом использовании, в зависимости от настройки dictionaries_lazy_load.


Также обновление словарей (кроме загрузки при первом использовании) не блокирует запросы во время обновления запросы используют старую версию словарей.


Для просмотра информации о словарях, сконфигурированных на сервере, есть таблица system.dictionaries, в ней можно найти:


  • статус словаря;
  • конфигурационные параметры;
  • метрики, наподобие количества занятой словарем RAM или количества запросов к словарю с момента его успешной загрузки.

Конфигурация словарей


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


Общий внешний вид конфигурации xml словаря:


<yandex>    <!--Необязательный элемент, комментарии к словарям-->    <comment>Some comments</comment>    <!--Необязательный элемент, имя файла с подстановками-->    <include_from>/etc/metrika.xml</include_from>    <dictionary>        <!-- Конфигурация словаря -->    </dictionary>    ...    <dictionary>        <!-- Конфигурация словаря -->    </dictionary></yandex>

Если вы выбрали создание словарей через DDL-запросы, то не задавайте конфигурацию словаря в конфигурации сервера.


Пример конфигурации словаря:


<dictionary>    <name>clients</name>    <sоurce>        <clickhouse>            <host>myHostName</host>            <port>9000</port>            <user>admin</user>            <password>secret_password</password>            <db>clients</db>            <table>users</table>            <where>id<=10</where>        </clickhouse>    </sоurce>    <lifetime>        <min>3600</min>        <max>5400</max>    </lifetime>    <layout>        <flat/>    </layout>    <structure>        <id>user_id</id>        <attribute>            <name>username</name>            <type>string</type>        </attribute>        <attribute>            <name>age</name>            <type>Int8</type>        </attribute>    </structure></dictionary>

Поля настройки:


  • name имя словаря;
  • source источник словаря;
  • lifetime периодичность обновления словарей;
  • layout размещение словаря в памяти. От этого значения зависит скорость обработки словаря;
  • structure структура словаря. Ключ и атрибуты, которые можно получить по ключу.

Пример создания словаря через DDL-запрос:


CREATE DICTIONARY dict_users_id (    id UInt64,    username String,    email String,    status UInt16,    hash String)PRIMARY KEY idSOURCE(MYSQL(    port 3306    user clickhouse    password secret_password    replica(host 'mysql1.fevlake.com' priority 1)    db fevlake_dicts    table users))LAYOUT(HASHED())LIFETIME(MIN 3600 MAX 5400);

Источники внешних словарей


Внешние словари можно подключить через множество разных источников. Основные из них это:


  • Локальный файл
  • Исполняемый файл
  • HTTP(s)
  • СУБД

Самые распространенные способы подключения словарей через локальный файл либо СУБД, поэтому именно их мы и рассмотрим далее.


Локальный файл


Пример подключения словаря через локальный файл имеет следующий вид:


<sоurce>    <file>      <path>/opt/dictionaries/clients.csv</path>      <format>CSV</format>    </file></sоurce>

Поля настройки:


  • path абсолютный путь к файлу.
  • format формат файла. Поддерживаются все форматы ClickHouse.

Или через DDL-запрос:


SOURCE(FILE(path '/opt/dictionaries/clients.csv' format 'CSV'))SETTINGS(format_csv_allow_single_quotes = 0)

СУБД


Рассмотрим подключение СУБД на примере MySQL базы данных.


Пример настройки:


<sоurce>    <mysql>        <port>3306</port>        <user>clickhouse</user>        <password>secret_password</password>        <replica>            <host>example01-1</host>            <priority>1</priority>        </replica>        <replica>            <host>example01-2</host>            <priority>1</priority>        </replica>        <db>db_name</db>        <table>table_name</table>        <where>id=10</where>        <invalidate_query>SQL_QUERY</invalidate_query>    </mysql></sоurce>

  • port порт сервера MySQL. Можно задать отдельно для каждой реплики внутри тега <replica>.
  • user имя пользователя MySQL. Можно задать отдельно для каждой реплики внутри тега <replica>.
  • password пароль пользователя MySQL. Можно задать отдельно для каждой реплики внутри тега <replica>.
  • replica блок конфигурации реплики. Блоков может быть несколько.
  • db имя базы данных.
  • table имя таблицы.
  • where условие выбора. Синтаксис полностью совпадает с синтаксисом секцииWHEREв MySQL, к примеру,id >= 3 AND id < 10 (необязательный параметр).
  • invalidate_query запрос для проверки статуса словаря (необязательный параметр).

Или через DDL-запрос:


SOURCE(MYSQL(    port 3306    user clickhouse    password secret_password    replica(host 'mysql1.fevlake.com' priority 1)    db fevlake_dicts    table users))

Хранение словарей в памяти


Существует много способов хранения словарей в памяти ClickHouse:


  • flat
  • hashed
  • sparse_hashed
  • cache
  • direct
  • range_hashed
  • complex_key_hashed
  • complex_key_cache
  • complex_key_direct
  • ip_trie

Самые популярные из них всего 3, поскольку скорость обработки словарей при этом максимальна, это flat, hashed и complex_key_hashed. Давайте рассмотрим примеры этих способов хранения.


Flat


Словари полностью хранятся в оперативной памяти в виде плоских массивов, при этом объем занятой памяти пропорционален размеру самого большого по размеру ключа словаря. Ключ словаря должен иметь тип UInt64 и не должен быть длиннее 500 000, иначе ClickHouse бросит исключение и не создаст словарь.


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


Пример конфигурации:


<layout>    <flat/></layout>

или


LAYOUT(FLAT())

Hashed


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


Пример конфигурации:


<layout>    <hashed/></layout>

или


LAYOUT(HASHED())

Сomplex_key_hashed


Этот тип размещения предназначен для использования с составнымиключами. Аналогиченhashed способу.


Пример конфигурации:


<layout>    <hashed/></layout>

или


LAYOUT(COMPLEX_KEY_HASHED())

Ключ и поля словаря


Секция<structure>описывает ключ словаря и поля, доступные для запросов.


Описание в формате XML:


<structure>    <id>user_id</id>    <attribute>        <name>username</name>        <type>string</type>    </attribute>    <attribute>        <name>age</name>        <type>Int8</type>    </attribute></structure>

Поля настройки:


  • <id>столбец с ключом;
  • <attribute>столбец данных. Можно задать несколько атрибутов.

Ключи


ClickHouse поддерживает следующие виды ключей:


  • Числовой ключ.UInt64. Описывается в теге<id>или ключевым словомPRIMARY KEY.
  • Составной ключ. Набор значений разного типа. Описывается в теге<key>или ключевым словомPRIMARY KEY.

Числовой ключ


Тип:UInt64.


Пример конфигурации:


<id>    <name>user_id</name></id>

или


CREATE DICTIONARY (    user_id UInt64,    ...)PRIMARY KEY user_id...

  • PRIMARY KEY имя столбца с ключами.

Составной ключ


Ключом может быть кортеж (tuple) из полей произвольных типов. В этом случаеlayoutдолжен бытьcomplex_key_hashedилиcomplex_key_cache.


Структура ключа задается в элементе<key>. Поля ключа задаются в том же формате, что иатрибутысловаря. Пример:


<key>    <attribute>        <name>field1</name>        <type>String</type>    </attribute>    <attrbute>        <name>field2</name>        <type>UInt32</type>    </attribute>    ...</key>

или


CREATE DICTIONARY ( field1 String, field2 String ... )PRIMARY KEY field1, field2...

Атрибуты


<structure>    ...    <attribute>        <name>Name</name>        <type>ClickHouseDataType</type>        <null_value></null_value>        <expression>rand64()</expression>        <hierarchical>true</hierarchical>        <injective>true</injective>        <is_object_id>true</is_object_id>    </attribute></structure>

или


CREATE DICTIONARY somename (    Name ClickHouseDataType DEFAULT '' EXPRESSION rand64() HIERARCHICAL INJECTIVE IS_OBJECT_ID)

Как можно использовать словари в ClickHouse


Один из популярных кейсов использования словарей в ClickHouse это агрегация данных по странам на основе IP (v4) адресов.


Представим, что перед нами задача: из данных колонки с ip String получить в запросе колонку с country String. Для решения данной задачи мы возьмем довольно популярные базы GeoIP2 от MaxMind.


MaxMind предоставляет со своими .mmdb базами API для большинства популярных языков программирования.


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


Для того чтобы связать IP со страной, нам необходимо скачать следующие файлы:


  • GeoIP2-Country-Blocks-IPv4.csv здесь содержатся связи IP префиксов и ID стран;
  • GeoIP2-Country-Locations-en.csv а здесь уже названия стран на английском.

Далее, заведем соответствующие словари с помощью DDL:


CREATE DICTIONARY dicts.geoip_country_blocks_ipv4 (    network String DEFAULT '',    geoname_id UInt64 DEFAULT 0,    registered_country_geoname_id UInt64 DEFAULT 0,    represented_country_geoname_id UInt64 DEFAULT 0,    is_anonymous_proxy UInt8 DEFAULT 0,    is_satellite_provider UInt8 DEFAULT 0) PRIMARY KEY networkSOURCE(FILE(    path '/var/lib/clickhouse/user_files/GeoIP2-Country-Blocks-IPv4.csv'    format 'CSVWithNames'))LAYOUT(IP_TRIE())LIFETIME(300);

В словаре geoip_country_blocks_ipv4 мы должны указать два основных атрибута:


  • network IP префикс сети, он же и будет ключом словаря.
  • geoname_id ID страны.

Остальные атрибуты в соответствии с заголовком в CSV.


Чтобы ClickHouse мог корректно сопоставить префикс сети и ID, нам необходимо использовать тип размещения ip_trie. Для получения значений из такого словаря необходимо будет передавать IP адрес в числовом представлении.


Теперь geoip_country_locations_en:


CREATE DICTIONARY dicts.geoip_country_locations_en (    geoname_id UInt64 DEFAULT 0,    locale_code String DEFAULT '',    continent_code String DEFAULT '',    continent_name String DEFAULT '',    country_iso_code String DEFAULT '',    country_name String DEFAULT '',    is_in_european_union UInt8 DEFAULT 0)PRIMARY KEY geoname_idSOURCE(FILE(    path '/var/lib/clickhouse/user_files/GeoIP2-Country-Locations-en.csv'    format 'CSVWithNames'))LAYOUT(HASHED())LIFETIME(300);

Нам нужно связать ID и название страны. В заголовках GeoIP2-Country-Locations-en.csv можно найти следующие атрибуты:


  • geoname_id ID страны, как в предыдущем словаре, но теперь в качестве ключа.
  • country_name название страны.

В качестве типа размещения указываем оптимизированный hashed.


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


Теперь, имея таблицу user_visits (user_ip String, user_id UUID), можем посчитать количество уникальных значений по странам. Один из способов это сделать использовать функции для работы со словарями dictGet*:


SELECT     dictGetString('dicts.geoip_city_locations_en', 'country_name', users_country_id) AS users_country,     uniqsFROM (    SELECT         dictGetUInt64('dicts.geoip_country_blocks_ipv4', 'geoname_id', tuple(IPv4StringToNum(user_ip))) AS users_country_id,         uniq(user_id) AS uniqs    FROM user_visits    GROUP BY users_country_id);

Разберем данный запрос:


  1. конвертируем строковое представление user_ip в числовое и оборачиваем в кортеж, чтобы соответствовать составному ключу ip_trie-словаря: tuple(IPv4StringToNum(user_ip));
  2. используем получившийся ключ, чтобы забрать ID страны как users_country_id: dictGetUInt64('geoip_country_blocks_ipv4', 'geoname_id', ...) as users_country_id;
  3. добавляем в запрос саму метрику: uniq(user_id) as uniq_users;
  4. агрегируем по ID страны, который взяли из словаря: GROUP BY users_country_id;
  5. результат, содержащий ID стран, сопоставляем с названиями: dictGetString('geoip_city_locations_en', 'country_name', users_country_id) AS users_country.

Таким образом возможно сопоставлять не только названия стран. В тех же GeoIP2 базах есть много другой полезной информации, не бойтесь пробовать :)


Заключение


На этом первичное знакомство со словарями закончено. Надеюсь, что данная информация расширит ваши возможности использования ClickHouse и поможет правильно настраивать внешние источники данных.

Подробнее..

Почему язык Go стал стандартом для DevOps-инженеров

25.02.2021 20:10:38 | Автор: admin

Иногда вещи находят себе применение неожиданно и не в том, для чего их задумывали.

В 1960-е годы Кен Томпсон легенда программирования написал компьютерную игру Space Travel для операционной системы Multics. Система была проектом компании Bell Lab, где он работал вместе с Денисом Ритчи. Позже проект закрыли, и чтобы продолжать играть в свою Space Travel, Томпсон решил портировать ее на компьютер PDP-7. Инструменты, которые он создал для порта, затем легли в основу операционной системы Unix.

Томпсон написал в одиночку первые три версии. Для Unix был нужен системный язык так появился B. Позже Денис Ритчи, коллега и друг Томпсона, подхватил разработку и написал язык C. Вот так в основе почти всего, на чем построены современные технологии, лежало желание поиграть в компьютерную игру и пет-проекты для забавы.

Кен Томпсон построил блестящую карьеру исследователя в области computer science. В середине 2000-х он устроился в Google, где вместе с Робом Пайком и Робертом Грейсмером создал Golang один из самых популярных языков последнего времени.


Go придумывали как простой язык для всего

К тому времени Google уже разрастался в гигантскую корпорацию, начинались проблемы со скоростью разработки. Там было очень много кода на C++, поддерживать его становилось все труднее. Задача, которую себе поставила компания, казалась почти наивной изобрести очень простой язык, на котором можно будет делать очень сложные вещи.

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

Кен Томпсон

Создатели хотели решить проблемы медленной сборки программ, неконтролируемых зависимостей, сложной разработки инструментов и проблем межъязыкового взаимодействия. За основу языка они взяли C, Pascal и Oberton и попытались собрать из них все лучшее. В итоге они стали делать язык с очень простым синтаксисом, строгой статической типизацией, автоматической сборкой мусора и простыми средствами для управления многопоточностью.

Golang анонсировали в 2009 году, в 2012 состоялся релиз. Язык быстро стал набирать популярность, но и сталкивался с критикой. Ключевую фишку языка простоту многие разработчики посчитали и главным минусом.

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

Но такая критика была и не совсем критикой для создателей языка. Сам Роб Пайк описывал язык почти также, но в отличие от критиков, не говорил о невыразительности как о чем-то плохом:

Ключевой момент здесь, что в гугле работают не исследователи. Они, как правило, молоды, идут работать после учебы, где, возможно, изучали Java, или C/C++, или Python. Они не смогут сразу разобраться в навороченном языке, но в то же время мы хотим, чтобы они создавали хорошее ПО. Именно поэтому язык должен быть прост для понимания и изучения.

Тем не менее, уже в 2012 году многие разработчики увидели перспективы Go в тех областях, для которых его изначально не задумывали. В своем твиттере Дерек Коллисон писал:

* Прогноз: в течение следующих двух лет Go станет доминирующим языком для IaaS, PaaS и оркестрации.

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

19% в DevOps планируют использовать Go в будущем, около 10% уже используют Go19% в DevOps планируют использовать Go в будущем, около 10% уже используют Go

DevOps придумывали как agile для инфраструктурщиков

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

Рано или поздно ошибку устраняли (или нет), и все начиналось заново.

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

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

В 2008 году администратор баз данных Патрик Дебуа поехал на конференцию Agile Toronto, где познакомился с другим админом Андрю Клэй Шафером. Оба интересовались аджайлом и тем, как бы получше внедрить его в сисадминское дело. На той же конференции они увидели выступление, где один из спикеров говорил, как в его команде разработчики и админы пытаются работать вместе.

Из всех этих дискуссий Дебуа и Шафер вынесли идею провести отдельную конференцию, посвященную только этим вопросам.

В то время еще не было термина Devops была только идея. Я написал в твиттере, что было бы здорово провести конференцию в Европе и обсудить саму идею. Agile System Administrators так себе название для мероприятия, поэтому мне предложили назвать его DevOpsDays с этого и началось движение Devops.

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

Суть была такова разработчики участвуют в деплое, администраторы пишут скрипты, все автоматизируется. С этого начался не только сдвиг в философии и отношению к ролям в команде, но и софтверные изменения. Новая практика требовала новых инструментов. Docker, Kubernetes, Terraform и прочие вещи, которые сейчас плотно ассоциируются с работой девопс-инженера появились на этой волне. И почти все они были написаны на Go.


Почему Go и DevOps идеально подошли друг другу

Когда в Google начали разрабатывать Kubernetes у команды уже накопилось огромное наследие инструментов для контейнеризации. Но Кубер начали писать с нуля, поэтому встал вопрос выбора языка. Джо Беда, основной разработчик сервиса, рассказывал, что в Гугле используются многие языки не только свои разработки и они выбирали между С/С++, Java и Python.

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

Java не подошла, потому что разработчики стремились сделать установку простой на как можно более широком количестве платформ. Python откинули из-за динамической типизации.

А вот Go по словам Беды был и не слишком высокоуровневым, и не слишком низкоуровневым. Список причин, почему команда Kubernetes выбрала его, довольно широк:

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

  • Быстрые инструменты. С ними мы подсели на скорость разработки, пишет Беда.

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

  • Анонимные функции.

  • Сборка мусора, о которой не надо задумываться.

  • Строгая типизация.

Но это плюсы для разработки инструмента. Когда обычные девопс-инженеры рассказывают, почему Go стал стандартом в их области, чаще всего называют

Три основные причины

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

  • У него самая простая для использования многопоточность. Горутины и Каналы фичи, которые хвалят даже те, кто невзлюбил Go.

  • Он невероятно прост в эксплуатации. И это, пожалуй, главная причина, почему язык так хорош для инфраструктурных инженеров.

По сравнению с Python, Ruby или Node.js, установка единственного исполняемого файла мечта инженеров по эксплуатации. Конечно, это вовсе не такая большая проблема с учетом все более широкого использования Docker, но отдельные исполняемые файлы еще и уменьшают размер контейнеров, пишет Сильвиан Валлез.

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

Что вы сами думаете про Go? Как и почему используете? Заходите к нам в сообщество там мы постоянно разбираем различные проблемы и задачи из сферы Devops, обсуждаем вещи, которые пригодятся и на собеседованиях, и в работе.

Подробнее..

Принимаем 10 000 ивентов в Яндекс.Облаке. Часть 1

23.07.2020 16:12:40 | Автор: admin
Привет всем, друзья!

* Эта статья написана по мотивам открытого практикума REBRAIN & Yandex.Cloud, если вам больше нравится смотреть видео, можете найти его по этой ссылке https://youtu.be/cZLezUm0ekE

Недавно нам представилась возможность пощупать вживую Яндекс.Облако. Поскольку щупать хотелось долго и плотно, то мы сразу отказались от идеи запуска простого wordpress блога с облачной базой слишком скучно. После непродолжительных раздумий решили развернуть что-то похожее на продакшн архитектуру сервиса для приема и анализа ивентов в near real time режиме.

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

Итак, наша история: как мы писали приложение на golang, тестировали kafka vs rabbitmq vs yqs, писали стриминг данных в Clickhouse кластер и визуализировали данные с помощью yandex datalens. Естественно, все это было приправлено инфраструктурными изысками в виде docker, terraform, gitlab ci и, конечно же, prometheus. Погнали!

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

1 часть (вы ее читаете). Мы определимся с ТЗ и архитектурой решения, а также напишем приложение на golang.
2 часть. Выливаем наше приложение на прод, делаем масштабируемым и тестируем нагрузку.
3 часть. Попробуем разобраться, зачем нам нужно хранить сообщения в буфере, а не в файлах, а также сравним между собой kafka, rabbitmq и yandex queue service.
4 часть. Будем разворачивать Clickhouse кластер, писать стриминг для перекладывания туда данных из буфера, настроим визуализацию в datalens.
5 часть. Приведем всю инфраструктуру в должный вид настроим ci/cd, используя gitlab ci, подключим мониторинг и service discovery с помощью prometheus и consul.

ТЗ


Сперва сформулируем техзадание что именно мы хотим получить на выходе.
1. Мы хотим иметь endpoint вида events.kis.im (kis.im тестовый домен, который будем использовать на протяжении всех статей), который должен принимать event-ы с помощью HTTPS.
2. События это простая jsonка вида: {event: view, os: linux, browser: chrome}. На финальном этапе мы добавим чуть больше полей, но большой роли это не сыграет. Если есть желание можно переключиться на protobuf.
3. Сервис должен уметь обрабатывать 10 000 событий в секунду.
4. Должна быть возможность масштабироваться горизонтально простым добавлением новых инстансов в наше решение. И будет неплохо, если мы сможем выносить фронт-часть в различные геолокации для уменьшения latency при запросах клиентов.
5. Отказоустойчивость. Решение должно быть достаточно стабильным и уметь выживать при падении любых частей (до определенного количества, естественно).

Архитектура


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



Итак, что у нас есть:
1. Слева изображены наши устройства, которые генерируют различные события, будь то прохождения уровня игроков в игрушке на смартфоне или создание заказа в интернет-магазине через обычный браузер. Событие, как указано в ТЗ, простой json, который отправляется на наш endpoint events.kis.im.
2. Первые два сервера простые балансировщики, их основные задачи:
  • Быть постоянно доступными. Для этого можно использовать, например, keepalived, который будет переключать виртуальный IP между нодами в случае проблем.
  • Терминировать TLS. Да, терминировать TLS мы будем именно на них. Во-первых, чтобы наше решение соответствовало ТЗ, а во-вторых, для того, чтобы снять нагрузку по установлению шифрованного соединения с наших backend серверов.
  • Балансировать входящие запросы на доступные backend сервера. Ключевое слово здесь доступные. Исходя из этого, мы приходим к пониманию, что load balancerы должны уметь мониторить наши сервера с приложениями и переставать балансировать трафик на упавшие ноды.

3. За балансировщиками у нас идут application сервера, на которых запущено достаточно простое приложение. Оно должно уметь принимать входящие запросы по HTTP, валидировать присланный json и складывать данные в буфер.
4. В качестве буфера на схеме изображена kafka, хотя, конечно, на этом уровне можно использовать и другие похожие сервисы. Kafka, rabbitmq и yqs мы сравним в третьей статье.
5. Предпоследней точкой нашей архитектуры является Clickhouse колоночная база данных, которая позволяет хранить и обрабатывать огромный объем данных. На данном уровне нам необходимо перенести данные из буфера в, собственно, систему хранения (об этом в 4 статье).

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

Кстати, если вы захотите реализовать опциональную часть нашего ТЗ и сделать масштабирование в различных геолокациях, то нет ничего проще:



В каждой геолокации мы разворачиваем load balancer с application и kafka. В целом, достаточно 2 application сервера, 3 kafka ноды и облачного балансировщика, например, cloudflare, который будет проверять доступность нод приложения и балансировать запросы по геолокациям на основе исходного IP-адреса клиента. Таким образом данные, отправленные американским клиентом, приземлятся на американских серверах. А данные из Африки на африканских.

Дальше все совсем просто используем mirror tool из набора кафки и копируем все данные из всех локаций в наш центральный дата-центр, расположенный в России. Внутри мы разбираем данные и записываем их в Clickhouse для последующей визуализации.

Итак, с архитектурой разобрались начинаем шатать Яндекс.Облако!

Пишем приложение


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

Потратив час времени (может, и пару часов), получаем примерно такую штуку: https://github.com/RebrainMe/yandex-cloud-events/blob/master/app/main.go.

Какие основные моменты здесь хотелось бы отметить:
1. При старте приложения можно указать два флага. Один отвечает за порт, на котором мы будем слушать входящие http запросы (-addr). Второй за адрес kafka сервера, куда мы будем записывать наши события (-kafka):

addr     = flag.String("addr", ":8080", "TCP address to listen to")kafka    = flag.String("kafka", "127.0.0.1:9092", "Kafka endpoints)


2. Приложение использует библиотеку sarama ([] github.com/Shopify/sarama) для отправки сообщений в kafka кластер. Мы сразу выставили настройки, ориентированные на максимальную скорость обработки:

config := sarama.NewConfig()config.Producer.RequiredAcks = sarama.WaitForLocalconfig.Producer.Compression = sarama.CompressionSnappyconfig.Producer.Return.Successes = true


3. Также в наше приложение встроен клиент prometheus, который собирает различные метрики, такие как:
  • количество запросов к нашему приложению;
  • количество ошибок при выполнении запроса (невозможно прочитать post запрос, битый json, невозможно записать в кафку);
  • время обработки одного запроса от клиента, включая время записи сообщения в кафку.

4. Три endpointа, которые обрабатывает наше приложение:
  • /status просто возвращаем ok, чтобы показать, что мы живы. Хотя можно и добавить некоторые проверки, типа доступности кафка кластера.
  • /metrics по этому url prometheus client будет возвращать собранные им метрики.
  • /post основной endpoint, куда будут приходить POST запросы с json внутри. Наше приложение проверяет json на валидность и если все ок записывает данные в кафка-кластер.


Оговорюсь, что код не идеален его можно (и нужно!) допиливать. К примеру, можно отказаться от использования встроенного net/http и переключиться на более быстрый fasthttp. Или же выиграть время обработки и cpu ресурсы, вынеся проверку валидности json на более поздний этап когда данные будут перекладываться из буфера в кликхаус кластер.

Помимо разработческой стороны вопроса, мы сразу подумали о нашей будущей инфраструктуре и приняли решение разворачивать наше приложение через docker. Итоговый Dockerfile для сборки приложения https://github.com/RebrainMe/yandex-cloud-events/blob/master/app/Dockerfile. В целом, он достаточно простой, единственный момент, на который хочется обратить внимание, это multistage сборка, которая позволяет уменьшить итоговый образ нашего контейнера.

Первые шаги в облаке


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

После регистрации для вас будет создано отдельное облако и каталог default, в котором можно начать создавать облачные ресурсы. Вообще в Яндекс.Облаке взаимосвязь ресурсов выглядит следующим образом:



На один аккаунт вы можете создать несколько облаков. А внутри облака сделать разные каталоги для разных проектов компании. Подробнее об этом можно почитать в документации https://cloud.yandex.ru/docs/resource-manager/concepts/resources-hierarchy. Кстати, ниже по тексту я буду часто на нее ссылаться. Когда я настраивал всю инфраструктуру с нуля документация выручала меня не раз, так что советую поизучать.

Для управления облаком можно использовать как веб-интерфейс, так и консольную утилиту yc. Установка выполняется одной командой (для Linux и Mac Os):

curl https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash


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

Если вы хотите установить клиента для windows, то можно воспользоваться инструкцией здесь и затем выполнить yc init, чтобы полностью ее настроить:

vozerov@mba:~ $ yc initWelcome! This command will take you through the configuration process.Please go to https://oauth.yandex.ru/authorize?response_type=token&client_id= in order to obtain OAuth token.Please enter OAuth token:Please select cloud to use: [1] cloud-b1gv67ihgfu3bp (id = b1gv67ihgfu3bpt24o0q) [2] fevlake-cloud (id = b1g6bvup3toribomnh30)Please enter your numeric choice: 2Your current cloud has been set to 'fevlake-cloud' (id = b1g6bvup3toribomnh30).Please choose folder to use: [1] default (id = b1g5r6h11knotfr8vjp7) [2] Create a new folderPlease enter your numeric choice: 1Your current folder has been set to 'default' (id = b1g5r6h11knotfr8vjp7).Do you want to configure a default Compute zone? [Y/n]Which zone do you want to use as a profile default? [1] ru-central1-a [2] ru-central1-b [3] ru-central1-c [4] Don't set default zonePlease enter your numeric choice: 1Your profile default Compute zone has been set to 'ru-central1-a'.vozerov@mba:~ $


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

Если у вас несколько аккаунтов или папок внутри одного облака, можете создать дополнительные профили с отдельными настройками через yc config profile create и переключаться между ними.

Помимо вышеперечисленных способов, команда Яндекс.Облака написала очень хороший плагин для terraform для управления облачными ресурсами. Со своей стороны, я подготовил git-репозиторий, где описал все ресурсы, которые будут созданы в рамках статьи https://github.com/rebrainme/yandex-cloud-events/. Нас интересует ветка master, давайте склонируем ее себе локально:

vozerov@mba:~ $ git clone https://github.com/rebrainme/yandex-cloud-events/ eventsCloning into 'events'...remote: Enumerating objects: 100, done.remote: Counting objects: 100% (100/100), done.remote: Compressing objects: 100% (68/68), done.remote: Total 100 (delta 37), reused 89 (delta 26), pack-reused 0Receiving objects: 100% (100/100), 25.65 KiB | 168.00 KiB/s, done.Resolving deltas: 100% (37/37), done.vozerov@mba:~ $ cd events/terraform/


Все основные переменные, которые используются в terraform, прописаны в файле main.tf. Для начала работы создаем в папке terraform файл private.auto.tfvars следующего содержания:

# Yandex Cloud Oauth tokenyc_token = ""# Yandex Cloud IDyc_cloud_id = ""# Yandex Cloud folder IDyc_folder_id = ""# Default Yandex Cloud Regionyc_region = "ru-central1-a"# Cloudflare emailcf_email = ""# Cloudflare tokencf_token = ""# Cloudflare zone idcf_zone_id = ""


Все переменные можно взять из yc config list, так как консольную утилиту мы уже настроили. Советую сразу добавить private.auto.tfvars в .gitignore, чтобы ненароком не опубликовать приватные данные.

В private.auto.tfvars мы также указали данные от Cloudflare для создания dns-записей и проксирования основного домена events.kis.im на наши сервера. Если вы не хотите использовать cloudflare, то удалите инициализацию провайдера cloudflare в main.tf и файл dns.tf, который отвечает за создание необходимых dns записей.

Мы в своей работе будем комбинировать все три способа и веб-интерфейс, и консольную утилиту, и terraform.

Виртуальные сети


Честно говоря, этот шаг можно было бы и пропустить, поскольку при создании нового облака у вас автоматически создастся отдельная сеть и 3 подсети по одной на каждую зону доступности. Но все же хотелось бы сделать для нашего проекта отдельную сеть со своей адресацией. Общая схема работы сети в Яндекс.Облаке представлена на рисунке ниже (честно взята с https://cloud.yandex.ru/docs/vpc/concepts/)



Итак, вы создаете общую сеть, внутри которой ресурсы смогут общаться между собой. Для каждой зоны доступности делается подсеть со своей адресацией и подключается к общей сети. В итоге, все облачные ресурсы в ней могут общаться, даже находясь в разных зонах доступности. Ресурсы, подключенные к разным облачным сетям, могут увидеть друг друга только через внешние адреса. Кстати, как эта магия работает внутри, было хорошо описано на хабре http://personeltest.ru/aways/habr.com/en/company/yandex/blog/487694/.

Создание сети описано в файле network.tf из репозитория. Там мы создаем одну общую приватную сеть internal и подключаем к ней три подсети в разных зонах доступности internal-a (172.16.1.0/24), internal-b (172.16.2.0/24), internal-c (172.16.3.0/24).

Инициализируем terraform и создаем сети:

vozerov@mba:~/events/terraform (master) $ terraform init... skipped ..vozerov@mba:~/events/terraform (master) $ terraform apply -target yandex_vpc_subnet.internal-a -target yandex_vpc_subnet.internal-b -target yandex_vpc_subnet.internal-c... skipped ...Plan: 4 to add, 0 to change, 0 to destroy.Do you want to perform these actions?  Terraform will perform the actions described above.  Only 'yes' will be accepted to approve.  Enter a value: yesyandex_vpc_network.internal: Creating...yandex_vpc_network.internal: Creation complete after 3s [id=enp2g2rhile7gbqlbrkr]yandex_vpc_subnet.internal-a: Creating...yandex_vpc_subnet.internal-b: Creating...yandex_vpc_subnet.internal-c: Creating...yandex_vpc_subnet.internal-a: Creation complete after 6s [id=e9b1dad6mgoj2v4funog]yandex_vpc_subnet.internal-b: Creation complete after 7s [id=e2liv5i4amu52p64ac9p]yandex_vpc_subnet.internal-c: Still creating... [10s elapsed]yandex_vpc_subnet.internal-c: Creation complete after 10s [id=b0c2qhsj2vranoc9vhcq]Apply complete! Resources: 4 added, 0 changed, 0 destroyed.


Отлично! Мы сделали свою сеть и теперь готовы к созданию наших внутренних сервисов.

Создание виртуальных машин


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

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

vozerov@mba:~/events/terraform (master) $ cd ../ansible/vozerov@mba:~/events/ansible (master) $ ansible-galaxy install -r requirements.yml- cloudalchemy-prometheus (master) is already installed, skipping.- cloudalchemy-grafana (master) is already installed, skipping.- sansible.kafka (master) is already installed, skipping.- sansible.zookeeper (master) is already installed, skipping.- geerlingguy.docker (master) is already installed, skipping.vozerov@mba:~/events/ansible (master) $


Внутри папки ansible есть пример конфигурационного файла .ansible.cfg, который использую я. Возможно, пригодится.

Перед тем, как создавать виртуалки, убедитесь, что у вас запущен ssh-agent и добавлен ssh ключик, иначе terraform не сможет подключиться к созданным машинкам. Я, конечно же, наткнулся на багу в os x: https://github.com/ansible/ansible/issues/32499#issuecomment-341578864. Чтобы у вас не повторилась такая история, перед запуском Terraform добавьте небольшую переменную в env:

vozerov@mba:~/events/terraform (master) $ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES


В папке с терраформом создаем необходимые ресурсы:

vozerov@mba:~/events/terraform (master) $ terraform apply -target yandex_compute_instance.build -target yandex_compute_instance.monitoring -target yandex_compute_instance.kafkayandex_vpc_network.internal: Refreshing state... [id=enp2g2rhile7gbqlbrkr]data.yandex_compute_image.ubuntu_image: Refreshing state...yandex_vpc_subnet.internal-a: Refreshing state... [id=e9b1dad6mgoj2v4funog]An execution plan has been generated and is shown below.Resource actions are indicated with the following symbols:  + create... skipped ...Plan: 3 to add, 0 to change, 0 to destroy.... skipped ...


Если все завершилось удачно (а так и должно быть), то у нас появятся три виртуальные машины:
  1. build машинка для тестирования и сборки приложения. Docker был установлен автоматически ansibleом.
  2. monitoring машинка для мониторинга на нее установлен prometheus & grafana. Логин / пароль стандартный: admin / admin
  3. kafka небольшая машинка с установленной kafka, доступная по порту 9092.


Убедимся, что все они на месте:

vozerov@mba:~/events (master) $ yc compute instance list+----------------------+------------+---------------+---------+---------------+-------------+|          ID          |    NAME    |    ZONE ID    | STATUS  |  EXTERNAL IP  | INTERNAL IP |+----------------------+------------+---------------+---------+---------------+-------------+| fhm081u8bkbqf1pa5kgj | monitoring | ru-central1-a | RUNNING | 84.201.159.71 | 172.16.1.35 || fhmf37k03oobgu9jmd7p | kafka      | ru-central1-a | RUNNING | 84.201.173.41 | 172.16.1.31 || fhmt9pl1i8sf7ga6flgp | build      | ru-central1-a | RUNNING | 84.201.132.3  | 172.16.1.26 |+----------------------+------------+---------------+---------+---------------+-------------+


Ресурсы на месте, и отсюда можем вытащить их ip-адреса. Везде далее я буду использовать ip-адреса для подключения по ssh и тестирования приложения. Если у вас есть аккаунт на cloudflare, подключенный к terraform, смело используйте свежесозданные DNS-имена.
Кстати, при создании виртуалке выдается внутренний ip и внутреннее DNS-имя, так что обращаться к серверам внутри сети можно по именам:

ubuntu@build:~$ ping kafka.ru-central1.internalPING kafka.ru-central1.internal (172.16.1.31) 56(84) bytes of data.64 bytes from kafka.ru-central1.internal (172.16.1.31): icmp_seq=1 ttl=63 time=1.23 ms64 bytes from kafka.ru-central1.internal (172.16.1.31): icmp_seq=2 ttl=63 time=0.625 ms^C--- kafka.ru-central1.internal ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1001msrtt min/avg/max/mdev = 0.625/0.931/1.238/0.308 ms


Это нам пригодится для указания приложению endpointа с kafkой.

Собираем приложение


Отлично, сервачки есть, приложение есть осталось только его собрать и опубликовать. Для сборки будем использовать обычный docker build, а вот в качестве хранилища образов возьмем сервис от яндекса container registry. Но обо всем по порядку.

Копируем на build машинку приложение, заходим по ssh и собираем образ:

vozerov@mba:~/events/terraform (master) $ cd ..vozerov@mba:~/events (master) $ rsync -av app/ ubuntu@84.201.132.3:app/... skipped ...sent 3849 bytes  received 70 bytes  7838.00 bytes/sectotal size is 3644  speedup is 0.93vozerov@mba:~/events (master) $ ssh 84.201.132.3 -l ubuntuubuntu@build:~$ cd appubuntu@build:~/app$ sudo docker build -t app .Sending build context to Docker daemon  6.144kBStep 1/9 : FROM golang:latest AS build... skipped ...Successfully built 9760afd8ef65Successfully tagged app:latest


Полдела сделано теперь можно проверить работоспособность нашего приложения, запустив его и направив на kafka:

ubuntu@build:~/app$ sudo docker run --name app -d -p 8080:8080 app /app/app -kafka=kafka.ru-central1.internal:9092</code>С локальной машинки можно отправить тестовый event и посмотреть на ответ:<code>vozerov@mba:~/events (master) $ curl -D - -s -X POST -d '{"key1":"data1"}' http://84.201.132.3:8080/postHTTP/1.1 200 OKContent-Type: application/jsonDate: Mon, 13 Apr 2020 13:53:54 GMTContent-Length: 41{"status":"ok","partition":0,"Offset":0}vozerov@mba:~/events (master) $


Приложение ответило успехом записи и указанием id партиции и оффсета, в который попало сообщение. Дело за малым создать registry в Яндекс.Облаке и закинуть туда наш образ (как это сделать при помощи трех строчек, описано в registry.tf файле). Создаем хранилище:

vozerov@mba:~/events/terraform (master) $ terraform apply -target yandex_container_registry.events... skipped ...Plan: 1 to add, 0 to change, 0 to destroy.... skipped ...Apply complete! Resources: 1 added, 0 changed, 0 destroyed.


Для аутентификации в container registry есть несколько способов с помощью oauth токена, iam токена или ключа сервисного аккаунта. Более подробно об этих способах в документации https://cloud.yandex.ru/docs/container-registry/operations/authentication. Мы будем использовать ключ сервисного аккаунта, поэтому создаем аккаунт:

vozerov@mba:~/events/terraform (master) $ terraform apply -target yandex_iam_service_account.docker -target yandex_resourcemanager_folder_iam_binding.puller -target yandex_resourcemanager_folder_iam_binding.pusher... skipped ...Apply complete! Resources: 3 added, 0 changed, 0 destroyed.


Теперь осталось сделать для него ключик:

vozerov@mba:~/events/terraform (master) $ yc iam key create --service-account-name docker -o key.jsonid: ajej8a06kdfbehbrh91pservice_account_id: ajep6d38k895srp9osijcreated_at: "2020-04-13T14:00:30Z"key_algorithm: RSA_2048


Получаем информацию об id нашего хранилища, перекидываем ключ и авторизуемся:

vozerov@mba:~/events/terraform (master) $ scp key.json ubuntu@84.201.132.3:key.json                                                                                                                    100% 2392   215.1KB/s   00:00vozerov@mba:~/events/terraform (master) $ ssh 84.201.132.3 -l ubuntuubuntu@build:~$ cat key.json | sudo docker login --username json_key --password-stdin cr.yandexWARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.Configure a credential helper to remove this warning. Seehttps://docs.docker.com/engine/reference/commandline/login/#credentials-storeLogin Succeededubuntu@build:~$


Для загрузки образа в реестр нам понадобится ID container registry, берем его из утилиты yc:

vozerov@mba:~ $ yc container registry get eventsid: crpdgj6c9umdhgaqjfmmfolder_id:name: eventsstatus: ACTIVEcreated_at: "2020-04-13T13:56:41.914Z"


После этого тегируем наш образ новым именем и загружаем:

ubuntu@build:~$ sudo docker tag app cr.yandex/crpdgj6c9umdhgaqjfmm/events:v1ubuntu@build:~$ sudo docker push cr.yandex/crpdgj6c9umdhgaqjfmm/events:v1The push refers to repository [cr.yandex/crpdgj6c9umdhgaqjfmm/events]8c286e154c6e: Pushed477c318b05cb: Pushedbeee9f30bc1f: Pushedv1: digest: sha256:1dd5aaa9dbdde2f60d833be0bed1c352724be3ea3158bcac3cdee41d47c5e380 size: 946


Можем убедиться, что образ успешно загрузился:

vozerov@mba:~/events/terraform (master) $ yc container repository list+----------------------+-----------------------------+|          ID          |            NAME             |+----------------------+-----------------------------+| crpe8mqtrgmuq07accvn | crpdgj6c9umdhgaqjfmm/events |+----------------------+-----------------------------+


Кстати, если вы установите утилиту yc на машинку с linux, то можете использовать команду
yc container registry configure-docker
для настройки докера.

Заключение


Мы проделали большую и трудную работу и в результате:
  1. Придумали архитектуру нашего будущего сервиса.
  2. Написали приложение на golang, которое реализует нашу бизнес-логику.
  3. Собрали его и вылили в приватный container registry.


В следующей части перейдем к интересному выльем наше приложение в production и наконец запустим на него нагрузку. Не переключайтесь!

Этот материал есть в видеозаписи открытого практикума REBRAIN & Yandex.Cloud: Принимаем 10 000 запросов в секунду на Яндекс Облако https://youtu.be/cZLezUm0ekE


Если вам интересно посещать такие мероприятия онлайн и задавать вопросы в режиме реального времени, подключайтесь к каналу DevOps by REBRAIN https://rebrainme.com/channel

Отдельное спасибо хотим сказать Yandex.Cloud за возможность проведения такого мерпориятия. Ссылочка на них https://cloud.yandex.ru/prices

Если вам нужен переезд в облако или есть вопросы по вашей инфраструктуре, смело оставляйте заявку https://fevlake.com

P.S. У нас есть 2 бесплатных аудита в месяц, возможно, именно ваш проект будет в их числе.
Подробнее..

Принимаем 10 000 ивентов в Яндекс.Облаке. Часть 2

28.07.2020 12:21:05 | Автор: admin
И снова здравствуйте! Продолжаем нашу серию статей про то, как мы щупали Яндекс.Облако.

Давайте вспомним план, по которому мы с вами двигаемся:
1 часть. Мы определились с ТЗ и архитектурой решения, написали приложение на golang.
2 часть (вы сейчас здесь). Выливаем наше приложение на прод, делаем масштабируемым и тестируем нагрузку.
3 часть. Попробуем разобраться, зачем нам нужно хранить сообщения в буфере, а не в файлах, а также сравним между собой kafka, rabbitmq и yandex queue service.
4 часть. Будем разворачивать Clickhouse кластер, писать стриминг для перекладывания туда данных из буфера, настроим визуализацию в datalens.
5 часть. Приведем всю инфраструктуру в должный вид настроим ci/cd, используя gitlab ci, подключим мониторинг и service discovery с помощью consul и prometheus.



Ну что, переходим к нашим задачкам.

Выливаемся в production


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

Вообще следующие шаги должны быть практически очевидными создаем виртуалки, настраиваем load balancer и прописываем DNS имя с проксированием на cloudflare. Но боюсь, такой вариант не сильно соответствует нашему техническому заданию. Мы хотим уметь масштабировать наш сервис в случае увеличения нагрузки и выкидывать из него битые ноды, которые не могут обслуживать запросы.

Для масштабирования мы будем использовать группы машин (instance groups), доступные в compute cloud. Они позволяют создавать виртуалки по шаблону, следить за их доступностью с помощью health checkов, а также автоматически увеличивать количество узлов в случае возрастания нагрузки. Подробнее здесь: https://cloud.yandex.ru/docs/compute/concepts/instance-groups/

Встает только один вопрос какой шаблон использовать для виртуальной машины? Конечно, можно установить linux, настроить его, сделать образ и залить в хранилище образов в Яндекс.Облаке. Но для нас это долгий и непростой путь. В процессе рассмотрения различных образов, доступных при создании виртуалки, мы наткнулись на интересный экземпляр container optimized image (https://cloud.yandex.ru/docs/cos/concepts/). Он позволяет запустить один docker контейнер в сетевом режиме host. То есть при создании виртуалки указывается примерно такая спецификация для образа container optimized image:
spec:  containers:    - name: api      image: vozerov/events-api:v1      command:        - /app/app      args:        - -kafka=kafka.ru-central1.internal:9092      securityContext:        privileged: false      tty: false      stdin: false      restartPolicy: Always

И после старта виртуальной машины этот контейнер будет скачан и запущен локально.

Схема вырисовывается вполне интересная:

  1. Мы создаем instance group с автоматическим масштабированием при превышении 60% cpu usage.
  2. В качестве шаблона указываем виртуальную машину с образом container optimized image и параметрами для запуска нашего Docker-контейнера.
  3. Создаем load balancer, который будет смотреть на нашу instance group и автоматически обновляться при добавлении или удалении виртуальных машин.
  4. Приложение будет мониториться и как instance group, и самим балансировщиком, который выкинет недоступные виртуалки из балансировки.

Звучит как план!

Попробуем создать instance group с помощью terraform. Все описание лежит в instance-group.tf, прокомментирую основные моменты:
  1. service account id будет использоваться для создания и удаления виртуалок. Кстати, нам его придется создать.
    service_account_id = yandex_iam_service_account.instances.id
    
  2. Данные для указания спецификации контейнера вынесены в отдельный файл spec.yml, где помимо прочего указывается образ, который необходимо запустить. Поскольку внутренний registry у нас появился во время написания данной статьи, а гит-репозиторий я выложил в паблик на гитхаб на данный момент там прописан образ с публичного docker hub. Чтобы использовать образ из нашего репозитория, достаточно прописать путь до него
                metadata = {  docker-container-declaration = file("spec.yml")  ssh-keys = "ubuntu:${file("~/.ssh/id_rsa.pub")}"}
    
  3. Укажем еще один service account id, он будет использоваться образом container optimized image, чтобы пройти аутентификацию в container registry и скачать образ нашего приложения. Сервисный аккаунт с доступом к registry мы уже создавали, поэтому будем использовать его:

    service_account_id = yandex_iam_service_account.docker.id
    
  4. Scale policy. Самая интересная часть:
    autoscale {  initialsize = 3  measurementduration = 60  cpuutilizationtarget = 60  minzonesize = 1  maxsize = 6  warmupduration = 60  stabilizationduration = 180}
    

    Здесь мы определяем политику масштабирования наших виртуалок. Есть два варианта либо fixed_scale с фиксированным числом виртуальных машин, либо auth_scale.

    Основные параметры такие:
    initial size первоначальный размер группы;
    measurement_duration период изменения целевого показателя;
    cpu_utilization_target целевое значение показателя, при большем значении которого надо расширить группу;
    min_zone_size минимальное количество нод в одной зоне доступности планировщик не будет удалять ноды, чтобы их количество стало ниже этого значения;
    max_size максимальное количество нод в группе;
    warmup_duration период прогрева, в течение которого планировщик не будет брать значения метрик с данной ноды, а будет использовать среднее значение в группе;
    stabilization_duration период стабилизации после увеличения количества нод в группе в течение указанного промежутка времени балансировщик не будет удалять созданные машины, даже если целевой показатель опустился ниже порогового значения.

    Теперь простыми словами о наших параметрах. На начальном этапе создаем 3 машинки (initial_size), минимум по одной в каждой зоне доступности (min_zone_size). Измеряем показатель cpu раз в минуту на всех машинках (measurement_duration). Если среднее значение превышает 60% (cpu_utilization_target), то создаем новые виртуалки, но не больше шести (max_size). После создания в течение 60 секунд не собираем метрики с новой машинки (warmup_duration), поскольку во время старта она может использовать достаточно много cpu. А 120 секунд после создания новой машинки не удаляем ничего (stabilization_duration), даже если среднее значение метрик в группе стало ниже 60% (cpu_utilization_target).

    Подробнее о масштабировании https://cloud.yandex.ru/docs/compute/concepts/instance-groups/policies#auto-scale-policy
  5. Allocation policy. Указывает, в каких зонах доступности создавать наши виртуальные машины, используем все три доступных зоны.

     allocationpolicy {  zones = ["ru-central1-a", "ru-central1-b", "ru-central1-c"]}
    
  6. Политика деплоя машинок:
    deploy_policy {  maxunavailable = 1  maxcreating = 1  maxexpansion = 1  maxdeleting = 1}
    


    max_creating максимальное число одновременно создаваемых машин;
    max_deleting максимальное число одновременно удаляемых машин;
    max_expansion на какое число виртуалок можно превысить размер группы при обновлении;
    max_unavailable максимальное число виртуалок в статусе RUNNING, которые могут быть удалены;

    Подробнее о параметрах https://cloud.yandex.ru/docs/compute/concepts/instance-groups/policies#deploy-policy
  7. Настройки балансировщика:
     load_balancer {  target_group_name = "events-api-tg"}
    

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


Вроде все основное прописали давайте создавать сервисный аккаунт для instance group и, собственно, саму группу.
vozerov@mba:~/events/terraform (master *) $ terraform apply -target yandex_iam_service_account.instances -target yandex_resourcemanager_folder_iam_binding.editor... skipped ...Apply complete! Resources: 2 added, 0 changed, 0 destroyed.vozerov@mba:~/events/terraform (master *) $ terraform apply -target yandex_compute_instance_group.events_api_ig... skipped ...Apply complete! Resources: 1 added, 0 changed, 0 destroyed.


Группа создалась можно посмотреть и проверить:
vozerov@mba:~/events/terraform (master *) $ yc compute instance-group list+----------------------+---------------+------+|          ID          |     NAME      | SIZE |+----------------------+---------------+------+| cl1s2tu8siei464pv1pn | events-api-ig |    3 |+----------------------+---------------+------+vozerov@mba:~/events/terraform (master *) $ yc compute instance list+----------------------+---------------------------+---------------+---------+----------------+-------------+|          ID          |           NAME            |    ZONE ID    | STATUS  |  EXTERNAL IP   | INTERNAL IP |+----------------------+---------------------------+---------------+---------+----------------+-------------+| ef3huodj8g4gc6afl0jg | cl1s2tu8siei464pv1pn-ocih | ru-central1-c | RUNNING | 130.193.44.106 | 172.16.3.3  || epdli4s24on2ceel46sr | cl1s2tu8siei464pv1pn-ipym | ru-central1-b | RUNNING | 84.201.164.196 | 172.16.2.31 || fhmf37k03oobgu9jmd7p | kafka                     | ru-central1-a | RUNNING | 84.201.173.41  | 172.16.1.31 || fhmh4la5dj0m82ihoskd | cl1s2tu8siei464pv1pn-ahuj | ru-central1-a | RUNNING | 130.193.37.94  | 172.16.1.37 || fhmr401mknb8omfnlrc0 | monitoring                | ru-central1-a | RUNNING | 84.201.159.71  | 172.16.1.14 || fhmt9pl1i8sf7ga6flgp | build                     | ru-central1-a | RUNNING | 84.201.132.3   | 172.16.1.26 |+----------------------+---------------------------+---------------+---------+----------------+-------------+vozerov@mba:~/events/terraform (master *) $

Три ноды с кривыми именами это наша группа. Проверяем, что приложения на них доступны:
vozerov@mba:~/events/terraform (master *) $ curl -D - -s http://130.193.44.106:8080/statusHTTP/1.1 200 OKDate: Mon, 13 Apr 2020 16:32:04 GMTContent-Length: 3Content-Type: text/plain; charset=utf-8okvozerov@mba:~/events/terraform (master *) $ curl -D - -s http://84.201.164.196:8080/statusHTTP/1.1 200 OKDate: Mon, 13 Apr 2020 16:32:09 GMTContent-Length: 3Content-Type: text/plain; charset=utf-8okvozerov@mba:~/events/terraform (master *) $ curl -D - -s http://130.193.37.94:8080/statusHTTP/1.1 200 OKDate: Mon, 13 Apr 2020 16:32:15 GMTContent-Length: 3Content-Type: text/plain; charset=utf-8okvozerov@mba:~/events/terraform (master *) $

К слову, можно зайти на виртуалки с логином ubuntu и посмотреть логи контейнера и как он запущен.

Для балансировщика также создалась целевая группа, на которую можно отправлять запросы:
vozerov@mba:~/events/terraform (master *) $ yc load-balancer target-group list+----------------------+---------------+---------------------+-------------+--------------+|          ID          |     NAME      |       CREATED       |  REGION ID  | TARGET COUNT |+----------------------+---------------+---------------------+-------------+--------------+| b7rhh6d4assoqrvqfr9g | events-api-tg | 2020-04-13 16:23:53 | ru-central1 |            3 |+----------------------+---------------+---------------------+-------------+--------------+vozerov@mba:~/events/terraform (master *) $

Давайте уже создадим балансировщик и попробуем пустить на него трафик! Этот процесс описан в load-balancer.tf, ключевые моменты:
  1. Указываем, какой внешний порт будет слушать балансировщик и на какой порт отправлять запрос к виртуальным машинам. Указываем тип внешнего адреса ip v4. На данный момент балансировщик работает на транспортном уровне, поэтому балансировать он может только tcp / udp соединения. Так что прикручивать ssl придется либо на своих виртуалках, либо на внешнем сервисе, который умеет https, например, cloudflare.
     listener {  name = "events-api-listener"  port = 80  target_port = 8080  external_address_spec {    ipversion = "ipv4"  }}
    
  2. healthcheck {  name = "http"  http_options {    port = 8080    path = "/status"  }        }
    



    Healthchecks. Здесь мы указываем параметры проверки наших узлов проверяем по http url /status на порту 8080. Если проверка будет провалена, то машинка будет выкинута из балансировки.
    Подробнее про балансировщик нагрузки cloud.yandex.ru/docs/load-balancer/concepts. Из интересного вы можете подключить услугу защиты от DDOS на балансировщике. Тогда на ваши сервера будет приходить уже очищенный трафик.

Создаем:
vozerov@mba:~/events/terraform (master *) $ terraform apply -target yandex_lb_network_load_balancer.events_api_lb... skipped ...Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Вытаскиваем ip созданного балансировщика и тестируем работу:
vozerov@mba:~/events/terraform (master *) $ yc load-balancer network-load-balancer get events-api-lbid:folder_id:created_at: "2020-04-13T16:34:28Z"name: events-api-lbregion_id: ru-central1status: ACTIVEtype: EXTERNALlisteners:- name: events-api-listener  address: 130.193.37.103  port: "80"  protocol: TCP  target_port: "8080"attached_target_groups:- target_group_id:  health_checks:  - name: http    interval: 2s    timeout: 1s    unhealthy_threshold: "2"    healthy_threshold: "2"    http_options:      port: "8080"      path: /status

Теперь можем покидать в него сообщения:
vozerov@mba:~/events/terraform (master *) $ curl -D - -s -X POST -d '{"key1":"data1"}' http://130.193.37.103/postHTTP/1.1 200 OKContent-Type: application/jsonDate: Mon, 13 Apr 2020 16:42:57 GMTContent-Length: 41{"status":"ok","partition":0,"Offset":1}vozerov@mba:~/events/terraform (master *) $ curl -D - -s -X POST -d '{"key1":"data1"}' http://130.193.37.103/postHTTP/1.1 200 OKContent-Type: application/jsonDate: Mon, 13 Apr 2020 16:42:58 GMTContent-Length: 41{"status":"ok","partition":0,"Offset":2}vozerov@mba:~/events/terraform (master *) $ curl -D - -s -X POST -d '{"key1":"data1"}' http://130.193.37.103/postHTTP/1.1 200 OKContent-Type: application/jsonDate: Mon, 13 Apr 2020 16:43:00 GMTContent-Length: 41{"status":"ok","partition":0,"Offset":3}vozerov@mba:~/events/terraform (master *) $

Отлично, все работает. Остался последний штрих, чтобы мы были доступны по https подключим cloudflare с проксированием. Если вы решили обойтись без cloudflare, можете пропустить данный шаг.
vozerov@mba:~/events/terraform (master *) $ terraform apply -target cloudflare_record.events... skipped ...Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Тестируем через HTTPS:
vozerov@mba:~/events/terraform (master *) $ curl -D - -s -X POST -d '{"key1":"data1"}' https://events.kis.im/postHTTP/2 200date: Mon, 13 Apr 2020 16:45:01 GMTcontent-type: application/jsoncontent-length: 41set-cookie: __cfduid=d7583eb5f791cd3c1bdd7ce2940c8a7981586796301; expires=Wed, 13-May-20 16:45:01 GMT; path=/; domain=.kis.im; HttpOnly; SameSite=Laxcf-cache-status: DYNAMICexpect-ct: max-age=604800, report-uri="http://personeltest.ru/aways/report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"server: cloudflarecf-ray: 5836a7b1bb037b2b-DME{"status":"ok","partition":0,"Offset":5}vozerov@mba:~/events/terraform (master *) $

Все наконец-то работает.

Тестируем нагрузку


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

Перед запуском тестирования стоит сделать одну простую штуку добавить наши application ноды в prometheus, чтобы следить за количеством запросов и временем обработки одного запроса. Поскольку мы пока не добавляли никакого service discovery (мы сделаем это в 5 статье серии), просто пропишем static_configs на нашем monitoring сервере. Узнать его ip можно стандартным способом через yc compute instance list, после чего дописать в /etc/prometheus/prometheus.yml следующие настройки:
  - job_name: api    metrics_path: /metrics    static_configs:    - targets:      - 172.16.3.3:8080      - 172.16.2.31:8080      - 172.16.1.37:8080

IP-адреса наших машинок также можно взять из yc compute instance list. Рестартуем prometheus через systemctl restart prometheus и проверяем, что ноды успешно опрашиваются, зайдя в веб-интерфейс, доступный на порту 9090 (84.201.159.71:9090).

Давайте еще добавим дашборд в графану из папки grafana. Заходим в Grafana по порту 3000 (84.201.159.71:3000) и с логином / паролем admin / Password. Далее добавляем локальный prometheus и импортируем dashboard. Собственно, на этом моменте подготовка завершена можно закидывать нашу инсталляцию запросами.

Для тестирования будем использовать yandex tank (https://yandex.ru/dev/tank/) с плагином для overload.yandex.net, который позволит нам визуализировать данные, полученные танком. Все необходимое для работы находится в папке load изначального гит-репозитория.

Немного о том, что там есть:
  1. token.txt файл с API ключиком от overload.yandex.net его можно получить, зарегистрировавшись на сервисе.
  2. load.yml конфигурационный файл для танка, там прописан домен для тестирования events.kis.im, тип нагрузки rps и количество запросов 15 000 в секунду в течение 3-х минут.
  3. data специальный файл для генерации конфига в формате ammo.txt. В нем прописываем тип запроса, url, группу для отображения статистики и собственно данные, которые необходимо отправлять.
  4. makeammo.py скрипт для генерации ammo.txt файла из data-файла. Подробнее про скрипт yandextank.readthedocs.io/en/latest/ammo_generators.html
  5. ammo.txt итоговый ammo файл, который будет использоваться для отправки запросов.

Для тестирования я взял виртуалку за пределами Яндекс.Облака (чтобы все было честно) и создал ей DNS запись load.kis.im. Накатил туда docker, поскольку танк мы будем запускать, используя образ https://hub.docker.com/r/direvius/yandex-tank/.

Ну что же, приступаем. Копируем нашу папку на сервер, добавляем токен и запускаем танк:
vozerov@mba:~/events (master *) $ rsync -av load/ cloud-user@load.kis.im:load/... skipped ...sent 2195 bytes  received 136 bytes  1554.00 bytes/sectotal size is 1810  speedup is 0.78vozerov@mba:~/events (master *) $ ssh load.kis.im -l cloud-usercloud-user@load:~$ cd load/cloud-user@load:~/load$ echo "TOKEN" > token.txtcloud-user@load:~/load$ sudo docker run -v $(pwd):/var/loadtest --net host --rm -it direvius/yandex-tank -c load.yaml ammo.txtNo handlers could be found for logger "netort.resource"17:25:25 [INFO] New test id 2020-04-13_17-25-25.35549017:25:25 [INFO] Logging handler <logging.StreamHandler object at 0x7f209a266850> added17:25:25 [INFO] Logging handler <logging.StreamHandler object at 0x7f209a20aa50> added17:25:25 [INFO] Created a folder for the test. /var/loadtest/logs/2020-04-13_17-25-25.35549017:25:25 [INFO] Configuring plugins...17:25:25 [INFO] Loading plugins...17:25:25 [INFO] Testing connection to resolved address 104.27.164.45 and port 8017:25:25 [INFO] Resolved events.kis.im into 104.27.164.45:8017:25:25 [INFO] Configuring StepperWrapper...17:25:25 [INFO] Making stpd-file: /var/loadtest/ammo.stpd17:25:25 [INFO] Default ammo type ('phantom') used, use 'phantom.ammo_type' option to override it... skipped ...

Все, процесс запущен. В консоли это выглядит примерно так:



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



Как можно заметить наши ноды не догрузились даже до 50% CPU, поэтому тест с автомасштабированием придется повторить. А пока глянем на время обработки запроса в Grafana:



Количество запросов порядка 3000 на одну ноду немного до 10 000 не догрузили. Время ответа радует порядка 11 ms на запрос. Единственный, кто выделяется, 172.16.1.37 у него время обработки запроса в два раза меньше. Но это и логично он находится в той же зоне доступности ru-central1-a, что и kafka, которая сохраняет сообщения.

Кстати, отчет по первому запуску доступен по ссылке: https://overload.yandex.net/265967.

Итак, давайте запустим тест повеселее добавим параметр instances: 2000, чтобы добиться 15 000 запросов в секунду, и увеличим время теста до 10 минут. Итоговый файл будет выглядеть так:
overload:  enabled: true  package: yandextank.plugins.DataUploader  token_file: "token.txt"phantom:  address: 130.193.37.103  load_profile:    load_type: rps    schedule: const(15000, 10m)  instances: 2000console:  enabled: truetelegraf:  enabled: false

Внимательный читатель обратит внимание, что я поменял адрес на IP балансировщика связано это с тем, что cloudflare начал меня блочить за огромное количество запросов с одного ip. Пришлось натравить tank напрямую на балансер Яндекс.Облака. После запуска можно наблюдать такую картину:



Использование CPU выросло, и планировщик принял решение увеличить количество нод в зоне B, что и сделал. Это можно увидеть по логам instance group:
vozerov@mba:~/events/load (master *) $ yc compute instance-group list-logs events-api-ig2020-04-13 18:26:47    cl1s2tu8siei464pv1pn-ejok.ru-central1.internal  1m AWAITING_WARMUP_DURATION -> RUNNING_ACTUAL2020-04-13 18:25:47    cl1s2tu8siei464pv1pn-ejok.ru-central1.internal 37s OPENING_TRAFFIC -> AWAITING_WARMUP_DURATION2020-04-13 18:25:09    cl1s2tu8siei464pv1pn-ejok.ru-central1.internal 43s CREATING_INSTANCE -> OPENING_TRAFFIC2020-04-13 18:24:26    cl1s2tu8siei464pv1pn-ejok.ru-central1.internal  6s DELETED  -> CREATING_INSTANCE2020-04-13 18:24:19    cl1s2tu8siei464pv1pn-ozix.ru-central1.internal  0s PREPARING_RESOURCES -> DELETED2020-04-13 18:24:19    cl1s2tu8siei464pv1pn-ejok.ru-central1.internal  0s PREPARING_RESOURCES -> DELETED2020-04-13 18:24:15    Target allocation changed in accordance with auto scale policy in zone ru-central1-a: 1 -> 22020-04-13 18:24:15    Target allocation changed in accordance with auto scale policy in zone ru-central1-b: 1 -> 2... skipped ...2020-04-13 16:23:57    Balancer target group b7rhh6d4assoqrvqfr9g created2020-04-13 16:23:43    Going to create balancer target group

Также планировщик решил увеличить количество серверов и в других зонах, но у меня закончился лимит на внешние ip-адреса :) Их, кстати, можно увеличить через запрос в техподдержку, указав квоты и желаемые значения.

Заключение


Нелегкой выдалась статья и по объему, и по количеству информации. Но самый трудный этап мы прошли и сделали следующее:

  1. Подняли мониторинг и кафку.
  2. Создали группу виртуалок с автомасштабированием, на которых запускается наше приложение.
  3. Подвязали к load balancerу cloudflare для использования ssl сертификатов.

В следующий раз займемся сравнением и тестированием rabbitmq / kafka / yandex queue service. Stay tuned!
*Этот материал есть в видеозаписи открытого практикума REBRAIN & Yandex.Cloud: Принимаем 10 000 запросов в секунду на Яндекс Облако https://youtu.be/cZLezUm0ekE

Если вам интересно посещать такие мероприятия онлайн и задавать вопросы в режиме реального времени, подключайтесь к каналу DevOps by REBRAIN.

Отдельное спасибо хотим сказать Yandex.Cloud за возможность проведения такого мерпориятия. Ссылочка на них https://cloud.yandex.ru/prices

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

P.S. У нас есть 2 бесплатных аудита в месяц, возможно, именно ваш проект будет в их числе.
Подробнее..

Я занялся преподаванием и не бросил работу. Совмещать офигенно

02.11.2020 18:18:10 | Автор: admin


В 11 классе я пошел на курсы для сертификации CISCO. Я, как всегда и везде, был самый молодой в группе. Вокруг сидели дядьки руководители IT-отделов, а мне было 16 лет.

У нас был очень крутой инструктор Сергей Петухов. Мы хорошо с ним общались, и он рассказывал про то, как трудно получить последний уровень ССIE. Его сдают в Брюсселе, в лаборатории. Привозят туда на 8 дней, дают огромный тест на теорию и реальную задачу: Вот железо, настрой вот такую схему, делай, как считаешь правильным.

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

У Сергея был друг, который работал в МТС, и он постоянно говорил: Серега, проблема в том, что ты преподаешь. Ты хорошо знаешь только теорию. Друг был наоборот первоклассным практиком.

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

Но соль в том, что поехать и сдать CCIE не смог ни тот, ни другой.



Вот я и думал, что сочетать невозможно


Когда я занимался сетями, у меня все строилось от практики. Я читал статью про IP-протокол и пытался понять, как это работает но нифига не понимал, зачем мне все это нужно. Потом я начал настраивать маршруты, AP-tables, роутинг, BGP. Набравшись практики, я перечитал статью про IP и вот тут у меня все сошлось.

Все, что было написано в теории, резонировало с практикой. С тех пор я всегда сначала пробовал, потом читал, как это работает.

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

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

В 2018 году мы начали проводить первые вебинары и постоянно собачились.

Я продалбывал все сроки, потому что очень боялся выступать хотя меня слушали всего 10-15 человек


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

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

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



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

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


И один их первых нафига?

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

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

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

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

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

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

То есть, преподавание спасло меня от скуки и рутины. И оно радует меня я рассказываю людям о вещах, которые мне нравятся.



Мои проблемы начались не из-за времени и сил. Беда была в другом:

Основной груз преподавания в ответственности


Я, например, никогда не любил Кубернетес, но мне приходилось проводить по нему вебинары. Он громоздкий, там куча фич, которыми, блин, никто не пользуется. Мне нравятся продукты с четким назначением и мне не нравятся вещи, в которых используют только 20%, а остальные нужны одному человеку раз в сто лет. Для меня это перегруженная система. Чем больше движущихся частей, тем больше может быть ошибок.

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

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

То, что преподавание тормозит твое развитие как профессионала это полная чушь


Вранье чистой воды.

Например, у меня запланирован вебинар, где я должен рассказывать про прием ивентов в Кликхаус. Я знаю тему частично знаю про шардирование, примерно представляю, что у меня есть апликейшн, который принимает джейсонку. Перед ним стоит nginx. Но я такой: Блин, меня сто процентов спросят про SSL и его настройку на энднпоинте.

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

То есть, я взял и прокачался просто потому, что готовился к выступлению


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

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

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

Поэтому в любом случае, когда ты преподаешь ты прокачиваешься, структурируешь свои знания. Людям нельзя рассказать хаотично. Ты дополняешь какие-то темы, изучаешь их, чтобы рассказать более подробно или заострить внимание.

Ты всегда отвечаешь на вопросы и знаешь ответы.

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

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

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

Ты, сам того не заметив, становишься известным в профессиональных кругах


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

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



Я до сих пор помню, как мы радовались нашему самому первому студенту. У парня дела шли не очень просто, но он был инициативный, старался, постоянно задавал вопросы, писал в личку. И в какой-то момент он нам написал: Ребят, меня взяли на новую работу с хорошим окладом. Спасибо огромное!.

Был парень, который три года назад занимался чем-то вроде диджитал-маркетинга, он имел косвенное отношение к айти, но для него все это было в новинку. И вот сейчас он очень мощно изучил Кубер, сдал по нему сертификацию и теперь сдает сертификацию на Google cloud professional architect. Сейчас он работает тимлидом в нашей команде.

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


Мне нравится такая практика люди начинают решать задачи под твоим присмотром, и ты сразу выцепляешь толковых спецов. У нас практически вся команда набрана из людей, которых мы сами учили. Я смотрю на них и понимаю хрен бы мы нашли таких спецов на хэдхантере.

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

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



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

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

Ультимативный гайд по собеседованию DevOps-инженеров что спрашивать и к чему готовиться

12.11.2020 20:10:42 | Автор: admin


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

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

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

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


Сперва я быстро просматриваю все резюме


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

Мимо идут резюме, в которых указан опыт работы в госструктурах, что-то в духе администратор первого разряда в ЦБ РФ. Все эти многословные рассказы про администрирование информационных систем я отсекаю сразу без раздумий. Чем официозней и канцеляритнее описание прошлой работы, тем выше шанс, что ничего в своей жизни, кроме винды и бэкапов с помощью графического интерфейса, такой соискатель не видел.

В быстром отборе резюме помогает правило: чем больше технологий я встречаю, тем лучше. Если у человека в резюме написано MySQL, Linux, Postgres, Apache и так далее шансы есть. Человек по крайней мере слышал о технологиях и, кто знает, возможно, даже сам с ними работал. Хотя будем честны в резюме можно написать все, что угодно.

На собеседовании первым делом проверяю базу


Когда я стану немощным стариком и меня будет стремно бить в ответ, я начну колотить палкой по спине всех, кто не знает сети! Это маст хэв для любого инженера. Мы живем в мире, где все происходит в сетях. Даже в закрытом госсекторе найдется локальный контур. И там сидит разработчик, который пишет какой-нибудь proxy-сервис или сочиняет сервис, работающий с API-шкой, и ничего не понимает в API.

Я не требую особых знаний, я не прошу настроить мне MPLS. Но что такое маска подсети, что такое IP-адрес в 21 веке должны знать все IT-специалисты. Я понятия не имею, что у человека в голове происходит, когда он не понимает, что такое 127.0.0.1. Он сидит на локальной машине, у него запущен сервис на виртуалке. У сервиса прописан эндпоинт 127.0.0.1, коннект к базе. От незнания наш герой вхреначивает то же самое. Как результат: у меня не коннектится к базе. Конечно, блин, не коннектится!

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


Вот, например, стандартная задачка из CCNA, на понимание того, как работают сети


Есть два свича из разных сетей, между ними стоит роутер. Компьютер А хочет отправить данные в компьютер Б.

Что происходит в этот момент?
Мне важно услышать нечто в духе: Ага, смотрим таблицу маршрутизации, видим, что до этой сети можно попасть через роутер. Компьютер отправляет запрос в сеть, пытается найти, определить MAC-адрес по айпишнику у этого роутера, передает ему данные. Роутер видит, сетка подключена, отдает данные компьютеру Б. Компьютер Б принимает их, хеппи энд.



Затем я спрашиваю по всем уровням модели OSI


Слышал ли что-нибудь человек про Spanning Tree протокол? Про корневой протокол, про IP-уровень? Хорошо, а как это все работает? Понимает ли он, как происходит роутинг? Ну и скопом: таблицы маршрутизации, динамический протокол маршрутизации, транспортный уровень TCP. И так далее, и так далее.

Я хочу услышать, чем отличаются TCP и UDP. Хороший спец ответит мне, почему критичные системы (например, Domain Name System) используют протокол без гарантированной доставки сообщений (UDP).

Ответ простой так быстрее. Пока устраиваешь TCP-сессию, ты можешь 3 раза отправить UDP пакетик туда и получить его обратно. И никакого оверхеда.

Задаю вопросы про DNS


Какие бывают типы записи? Знает ли мой собеседник, что такое MX-запись, как работает spf или как работает DKIM.

Да, в работе эти знания могут и не пригодиться. Но мне важно знать, понимает ли человек суть технологии, было ли ему интересно прочитать про это. Вот добавлял он какие-то записи в DNS и загуглил, что такое spf-запись, или не загуглил?

Абсолютно везде сейчас используется HTTP-протокол, и я про него спрашиваю


Я начинаю со стандартных вопросов об отличиях http-версий 1.0, 1.1 и второй версии. Интересуюсь, что такое headers и зачем они нужны. Как веб-сервер понимает, что ему пришел запрос на определенный virtual host, а не на любой другой. И всегда задаю пару вопросов по Nginx.

Дальше плавно переключаю внимание на TLS-протокол


Как работает SSL\TLS? Инженеру нужно понимать это хотя бы на базовом уровне есть корневой центр сертификации, он подписал сертификат, и сертификат где-то используется.

В TLS меня обычно интересует процесс установки соединения. Зачем нужны приватный и публичный ключи и как они взаимодействуют? Если человек шарит, то задаю вопрос с подвохом: можно ли иметь несколько разных сертификатов на одном IP-шнике?

Ответ под спойлером
Дело в том, что сначала устанавливается TLS-соединение, а потом уже следом идет HTTP-протокол, а веб-сервер может определить, к какому сайту обращается клиент только по HTTP-протоколу, по хедеру хоста. Nginx еще не знает, к какому сайту обратились, но он уже должен отдать сертификат клиенту. Есть расширения для TLS-протокола, которые позволяют клиенту передать хостнейм серверу, к которому он обращается при попытке установить TLS-соединение. И таким образом выбирать нужный сертификат. Когда этого расширения не было, на одном IP-шнике можно было держать только один сайт с включенным SSL.

Перехожу к Linux и BASH


Надо знать все юниксовое, все Unix-like системы. Нужно уметь работать с Shell и Bash, знать основные команды. ls-ы всякие, mkdir и т.д.

Хорошо, если кандидат может немного скриптовать на BASH значит, он пробовал хоть как-то автоматизировать эту историю.

По Linux спрошу, как заменить в файле строчки другими строчками. Или как распарсить какой-нибудь access.log в Nginx средствами BASH. Чтобы человек рассказал про awk, про cat, про sort, про все, что помогает быстрой работе.

Там всюду текстовые файлы, надо с ними правильно работать. Если соискатель предложит копировать их в эксельку и уже там что-то делать, то мне станет неловко.

Потом надо выяснить, как дела с виртуализацией


Обычная виртуализация, виртуализация через гипервизор. Если кандидат заводит разговор про паравиртуализацию значит, что-то знает.

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

Перемещаемся к контейнерам


Узнаю, работал ли собеседник с Docker? Собирал ли он образы, писал ли Docker-файлы, использовал ли Docker compose, деплоил ли с его помощью. Зачем вообще нужны эти контейнеры и как они работают? Поднимал ли наш соискатель систему оркестрации контейнеров Swarm или Kubernetes? Можно задать целый блок вопросов, но главное понять, делал человек работу своими руками с контейнерами или нет?

Спрашиваю про CI/CD deployment


Меня интересует огромный список вещей: как он настраивает автоматический deployment, как настраивает Continuous Integration? Как у него собираются приложения, использует ли он системы анализа кода (PVS-Studio, SonarQube). Как он пишет тесты или как интегрирует тесты, написанные разработчиками.

Делает ли он какое-то интеграционное тестирование этих сборок? Что дальше происходит с тем, что он собрал? Это как-то складывается в артфекаты или это упаковывается в docker-контейнеры, пушится в registry? Пусть расскажет, какие системы использовал для настройки CI/CD-процесса. Это могут быть и GitLab CI, и Circle CI, и какие-то облачные решения. А может быть, Jenkins, ну и про самописные скрипты на PowerShell не стоит забывать.

Скажи мне, как человек деплоит, и я все пойму. Он может деплоиться с помощью Helm в Kubernetes, Ansible, скриптами или чем-то еще самописным.

Про систему управления конфигурациями


Говорим чаще всего по Ansible. Поскольку его знают большинство. Итак, зачем нужны роли, как зашифровать и хранить секретные данные, как закидывать пароли на Git-репозиторий? И все в таком духе.

Узнаю про умение писать код


Разбираться в разработке важно, чтобы понимать, как взаимодействуют сервисы, зачем нужны API, протоколы аутентификации, авторизации. Славно, если кандидат что-то писал сам. Мне достаточно даже базовых скриптов на Python или Shell. Важен не код, а то, какие задачи хотел решить человек, чего хотел добиться.

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

Последние вопросы на собеседовании касаются хранилищ баз данных


SQL, NoSQL в чем различия, с чем работали? Чаще встречаю людей с опытом работы в PostgreSQL, реже MySQL. Задаю вопросы про индексы, может ли соискатель настроить реплику, может ли настроить логическую репликацию между табличками или просто данными. А что он будет мониторить в этом случае? А как ускорить базу?



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

Просто...
Просто: нужно выключить fsync, чтобы база не дожидалась записи с данных на диск, а как бы сохраняла. Linux может отдавать успешную запись, когда он к себе положил в буфер, а не когда на диск засинкал. Это немного костыльный режим работы, даже скорее опасный, рискованный. Питание вырубят в этот момент, и все навернется. Но зато этот метод ускоряет запись на порядок. Но не повторяйте и вообще никогда такого не делайте. И да, я вам этого не говорил.


Подытожу

  • сети
  • API-уровень
  • транспортный уровень TCP\UDP
  • протоколы DNS и HTTP
  • Linux
  • контейнерная и гипервизорная виртуализация
  • CI/CD
  • система управления конфигурациями
  • контейнеры
  • базы данных

Все это позволяет составить полную картину о человеке


Самое критичное в моем списке базовые знания. Не знаешь базу пора заканчивать собес.

Если человек не может мне ответить, что такое маска подсети, или не понимает, зачем нужны headers в HTTP в большинстве случаев такой соискатель получает наижирнейший минус в моей тетради. Разве тебе не любопытно было узнать, как работают все эти штуки, в которых ты тыкаешь курсором мышки?

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

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



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

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

Ну и по традиции, приглашаю присоединиться к нашему сообществу в телеге там регулярно разбираем все эти темы и многие другие, которые пригодятся и на собеседовании, и в работе.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    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