Какая сущность поддерживает нужное количество инстансов приложения
Читаем MSDN:
hInstance
Handle to the instance that contains the window procedure for the class.
Т.е. модуля, в котором находится оконная процедура.
PS А вообще, топик не в том разделе - в WinAPI надо бы.
Я машинально сюда тему влепил (восномном тока сюда захожу потому-что) - модеры перенесут.-------------------------------------------------------------------
хэндл модуля (возвращаемый по GetModuleHandle(NULL)) - один и тот же
Вот это дельное замечание. - потому как в MSDN написано:
If this parameter is NULL, GetModuleHandle returns a handle of the file used to create the calling process.
Я позырил в отладке - и неважно кто вызывает, хоть DLL, хоть екзешник
в обоих случаях одно значение, а в моём случае это ещё и важно
потому, что ресурсы лежат в DLL
Поэтому когда, я вызывал GetModuleHandle - я свято верил, что получаю хендл для DLL, а получал для приложения. - соответствено ресурсы и не грузились. Не знал что и думать. :)
[QUOTE=bave]Я машинально сюда тему влепил (восномном тока сюда захожу потому-что) - модеры перенесут.
-------------------------------------------------------------------
Вот это дельное замечание. - потому как в MSDN написано:
If this parameter is NULL, GetModuleHandle returns a handle of the file used to create the calling process.
Я позырил в отладке - и неважно кто вызывает, хоть DLL, хоть екзешник
в обоих случаях одно значение, а в моём случае это ещё и важно
потому, что ресурсы лежат в DLL
Поэтому когда, я вызывал GetModuleHandle - я свято верил, что получаю хендл для DLL, а получал для приложения. - соответствено ресурсы и не грузились. Не знал что и думать. :)[/QUOTE]
можт ты когда вызываешь для DLL забываешь ее имя указать ?
гым вот у рихтера:
Описатель экземпляра процесса
Любому EXE- или DLL-модулю, загружаемому в адресное пространство процесса, при сваивается уникальный описатель экземпляра. Описатель экземпляра Вашего EXE файла передается как первый параметр функции (w)WinMain - hinstExe. Это значе ние обычно требуется при вызовах функций, загружающих те или иные ресурсы. На пример, чтобы загрузить из образа ЕХЕ-файла такой ресурс, как значок, надо вызвать:
HICON LoadIcon( HINSTANCE hinst, PCTSTR pszIcori);
Первый параметр в LoadIcon указывает, в каком файле (EXE или DLL) содержится интересующий Вас ресурс. Многие приложения сохраняют параметр hinstExe функ ции (w)WinMain в глобальной переменной, благодаря чему он доступен из любой части кода ЕХЕ-файла.
В документации Platform SDK утверждается, что некоторые Windows-функции требуют параметр типа HMODULE. Пример — функция GetModuleFileName
DWORD GetModuleFileName( HMODULE hinstModule, PTSTR pszPath, DWORD cchPath);
NOTE:
Как оказалось, HMODULE и HINSTANCE — это идно и то же. Встретив в доку ментации указание передать какой-то функции HMODULE, смело передавайте HINSTANCE, и наоборот. Они существуют в таком виде лишь потому, что в l6 разрядпой Windows идентифицировали совершенно разные вещи.
Истинное значение параметра hinstExe функции (w)WinMain — базовый адрес в памяти, определяющий ту область в адресном пространстве процесса, куда был заг ружен образ данного ЕХЕ-файла, Например, если система открывает исполняемый файл и загружает его содержимое по адресу 0x00400000, то hinstExe функции (w)Win Main получает значение 0x00400000.
Базовый адрес, но которому загружается приложение, определяется компоновщи ком. Разные компоновщики выбирают и разные (no умолчанию) базовые адреса. Ком поновщик Visual С++ использует по умолчанию базовый адрес 0x00400000 — самый нижний в Windows 98, начиная с которого в ней допускается загрузка образа испол няемого файла. Указав параметр /BASE: адрес (в случае компоновщика от Microsoft), можно изменить базовый адрес, по которому будет загружаться приложение.
При попытке загрузить исполняемый файл в Windows 98 по базовому адресу ниже 0x00400000 загрузчик переместит его на другой адрес. Это увеличит время загрузки приложения, но оно по крайней мере будет выполнено. Если Вы разрабатываете про граммы и для Windows 98, и для Windows 2000, сделайте так, чтобы приложение заг ружалось по базовому адресу не ниже 0x00400000. Функция GetModuleHandle.
HMODULE GetModuleHandle( PCTSTR pszModule);
возвращает описатель/базовый адрес, указывающий, куда именно (в адресном про странстве процесса) загружается EXE- или DLL-файл. При вызове этой функции имя нужного EXE- или DLL-файла передается как строка с нулевым символом в конце. Если система находит указанный файл, GetModuleHandle возвращает базовый адрес, по которому располагается образ данного файла. Если же файл системой не найден, функция возвращает NULL. Кроме того, можно вызвать эту функцию, передав ей NULL вместо параметра pszModule, — тогда Вы узнаете базовый адрес EXE-файла. Именно это и делает стартовый код из библиотеки С/С++ при вызове (w)WinMain из Вашей программы.
Есть еще две важные вещи, касающиеся GetModuleHandle. Во-первых, она прове ряет адресное пространство только того процесса, который ее вызвал. Если этот про цесс не использует никаких функций, связанных со стандартными диалоговыми ок нами, то, вызвав GetModuleHandle и передав ей аргумент "ComDlg32", Вы получите NULL - пусть даже модуль ComDlg32.dll и загружен в адресное пространство какого нибудь другого процесса. Во-вторых, вызов этой функции и передача ей NULL дает в результате базовый адрес ЕХЕ-фяйла в адресном пространстве процесса. Так что, вы зывая функцию в виде GetModuleHandle(NULL — даже из кода в DLL, — Вы получаете базовый адрес EXE-, а не DLL-файла.
Если вы недавно познакомились с Docker, возможно, вы уже слышали о Kubernetes. Если вы не до конца понимаете связь, различия или схожесть этих технологий, эта статья для вас.
Мы расскажем, зачем нужен Kubernetes, если вы уже пользуетесь Docker-контейнерами. Так что же такого можно сделать в Kubernetes, чего нельзя достичь средствами Docker?
Что такое Kubernetes и сравнение его с Docker
Kubernetes — это платформа для оркестрации контейнеров, которая позволяет построить распределенную отказоустойчивую систему. Kubernetes автоматически управляет жизненным циклом контейнеров, поддерживает стабильное состояние системы и распределяет нагрузку по разным узлам.
Kubernetes и Docker нельзя сравнивать напрямую. Docker — инструмент для создания и запуска контейнеров, а Kubernetes — оркестратор, инструмент для управления контейнерами. Kubernetes позволяет построить кластер (Cluster) — распределенную отказоустойчивую систему, в то время как Docker работает на отдельном узле.
Еще есть Docker Swarm — это встроенный в докер инструмент оркестровки контейнеров. Его как как раз и можно полноценно сравнивать с Kubernetes, мы написали об этом отдельную статью.Важно понимать, что в этой статье мы говорим о простых Docker-контейнерах, без применения оркестратора Docker Swarm.
Главное преимущество Kubernetes — автоматизация
Основное преимущество Kubernetes перед простыми Docker-контейнерами можно описать одним словом — автоматизация. Но под этой автоматизацией понимается много различных возможностей, о которых мы расскажем далее и приведем несколько конкретных примеров.
Нужно отметить, что все эти автоматизации можно сделать и без Kubernetes, но для этого придется использовать много отдельных инструментов, настраивать их и писать скрипты. Например, для автоматизации выкатки обновлений приложений можно использовать Ansible — систему управления конфигурациями. Такая система может обновить все инстансы приложения и убедиться, что оно работает. Но для этого она должна знать, какие хосты сейчас работают, как к ним обратиться, на каких именно хостах работают инстансы нужного приложения. Для этого придется писать скрипты, которые будут за этим следить.
Заниматься поддержкой этих скриптов и инструментов придется самостоятельно. Иногда могут возникать проблемы, например, если у одного из инструментов выйдет новая версия, которая несовместима с другими инструментами. Или для новой версии инструмента придется переписывать скрипты, потому что поменяется логика работы этого инструмента.
В Kubernetes все эти автоматизации уже включены, они разрабатываются и поддерживаются большим сообществом разработчиков. Когда выходит новая версия Kubernetes, разработчики тестируют и проверяют все сами, а значит, все внутренние интеграции и автоматизации точно будут работать.Единое управление всеми узлами
Когда в инфраструктуре становится много узлов (серверов, на которых работают контейнеры), ими нужно централизованно управлять. Удобней всего иметь центральную точку управления, которая может принять команды и выполнить действия вне зависимости от того, на каком узле находятся контейнеры.
Docker работает в рамках отдельных узлов. Если у вас несколько узлов, то на каждом из них запущен докер-демон, который ничего не знает о существовании других узлов. Каждый демон знает лишь о том, что происходит на его узле. И поэтому обычными докер-контейнерами сложно управлять централизованно.
Управление конфигурациями и конфиденциальными данными
Приложениям часто требуются файлы настроек, переменные окружения или конфиденциальные данные (логины, пароли, токены и т. п.). Обычно такие файлы хранятся отдельно от приложений. Если приложение работает на нескольких узлах, нужно распространить эти файлы настроек на все узлы. Причем если настройки изменятся, их снова нужно обновить на всех узлах.
Docker. Так как докер-демон работает отдельно на каждом узле, все манипуляции по доставке и синхронизации этих файлов между узлами нужно проводить вручную. К тому же у Docker-контейнеров нет возможности хранения конфиденциальных данных в зашифрованном виде. Докер может использовать только переменные окружения.
В Kubernetes есть средство для центрального управления конфигурациями — ConfigsMaps для настроек и Secrets для конфиденциальных данных. Если разместить в них свои настройки, то приложения смогут получить к ним доступ из любого узла.
Автомасштабирование узлов
Технология контейнеризации позволяет легко масштабировать приложение. Если нагрузка на приложение растет, можно запустить дополнительные экземпляры, чтобы приложение не зависало. Но отслеживать изменение нагрузки вручную неправильно, гораздо удобнее это автоматизировать. Здесь под масштабированием мы имеем в виду как увеличение количества контейнеров, так и подключение дополнительных узлов, если ресурсов существующих узлов становится недостаточно.
Docker. Контейнеры не могут автомасштабироваться. Так как каждый докер-демон работает только в рамках своего узла, он не знает, что происходит на остальных. Докер-демон не может следить за нагрузкой, создавать дополнительные контейнеры или подключать новые узлы.
Kubernetes умеет автомасшабироваться. «Из коробки» он умеет масштабировать количество контейнеров, и дополнительно можно настроить подключение новых узлов. Когда нагрузка на приложение возрастет, Kubernetes добавит новые инстансы приложения, а при необходимости и подключит новые узлы. Когда нагрузка спадет, Kubernetes завершит лишние контейнеры и освободит узлы.
Обновления приложений и откаты
Приложения часто дорабатываются и изменяются. При обновлении контейнерного приложения нужно обновить инстансы на всех узлах. При этом на время обновления нужно сделать так, чтобы обновляемый контейнер не получал запросы, то есть его нужно вывести из балансировки нагрузки. Также часто требуется выполнять обновление без простоев по методу Rolling-Update, то есть чтобы все инстансы обновлялись последовательно и приложение было доступно в любой момент времени.
Другая задача — откат обновлений. Иногда для быстрого устранения ошибки нужно откатить обновление, чтобы потом спокойно разбираться и устранять ошибку. По сути, эта процедура аналогична обновлению, просто в обратном направлении.
В Docker нужно обновлять отдельно каждый контейнер. У контейнеров нет средства, чтобы одной или несколькими командами обновить все инстансы. Нужно найти все узлы, на которых работает приложение, и обновлять их по очереди.
Kubernetes. Чтобы выкатить новую версию приложения в Kubernetes, достаточно в Deployment-файле указать новую версию в имени образа. При этом если нужно выполнить обновление по типу Rolling-Update, это тоже достаточно указать в Deployment-файле. Если после обновления нужно откатиться, достаточно выполнить команду rollout, и Kubernetes сам откатит все контейнеры.
Система хранения данных
Многим приложениям нужен доступ к хранилищу постоянных данных. Так как контейнеры по своей природе изолированны, обычно настроить доступ к хранилищу не самая тривиальная задача.
У Docker есть тома (Volumes), которые нужны для упрощения работы с постоянными данными. Но в любом случае тома нужно дополнительно настраивать, причем делать это придется для каждого контейнера отдельно. Например, у нас есть сетевое хранилище Ceph. Чтобы подключить его к контейнеру, сначала нужно создать том, подключить его ко всем узлам с контейнерами и потом смонтировать этот том в каждый отдельный контейнер.
Kubernetes намного упрощает работу с дисками. Полностью «из коробки» подключить диски не получится, тут все равно потребуется некоторая настройка и дополнительные инструменты. Но сделать это гораздо проще за счет объектов PersistentVolume и PersistentVolumeClaim. Сначала нужно создать том и настроить его один раз, а потом каждый контейнер может самостоятельно запрашивать ресурсы, без необходимости дополнительных манипуляций. Подробнее об этом механизме читайте в нашей статье про основы Kubernetes.
Распределение нагрузки и маршрутизация трафика из внешней сети
Если у приложения есть несколько инстансов, то, чтобы обратиться к приложению, нужно знать его адреса. Причем внешние сервисы, которые обращаются к приложению, даже не должны знать, сколько инстансов у приложения, где они запущены и так далее. Они должны взаимодействовать с приложением как с единой сущностью, не задумываясь об его инстансах.
Другая задача — маршрутизация трафика из внешней сети. Если приложение должно быть доступно из интернета или любой другой внешней сети, необходимо настроить маршрутизацию. Чтобы внешний трафик правильно распределялся по нужным сервисам и приложениям.
Docker. В простых докер-контейнерах нет возможности распределять нагрузку между инстансами приложения. Например, приложение хочет обратиться к сервису. Чтобы это сделать, оно должно знать IP-адрес этого сервиса. А если у сервиса несколько инстансов, то приложение должно знать все адреса и само решать, в какой именно инстанс обратиться. Если один из контейнеров сервиса перезапустится и получит новый адрес, приложение должно как-то об этом узнать. Это неудобный и неправильный подход к разработке.
Проблема усугубляется, если к сервису нужно обращаться из внешней сети. Для этого придется поддерживать в актуальном состоянии и еще и таблицу маршрутизации для внешнего трафика.
Kubernetes берет распределение трафика на себя. Если рассматривать пример выше, то Kubernetes позволяет над всеми инстансами сервиса создать единый объект типа Service. Этот объект сам следит за всеми инстансами, и, если контейнер перезапускается, он сам обновит информацию о его новом адресе. И чтобы приложение могло обратиться к сервису, достаточно знать одно доменное имя, которое будет всегда постоянным.
Также Kubernetes умеет перенаправлять внешний трафик в контейнеры. Для этого нужно установить и настроить Ingress Controller, но это гораздо проще, чем самостоятельно настраивать и поддерживать маршрутизацию. Kubernetes позволяет сделать так, чтобы к приложениям можно было обращаться по доменным именам или URL-путям. Ingress Controller сам будет перенаправлять трафик на нужный инстанс контейнера.
Чем еще хорош Kubernetes
Кроме автоматизации, есть еще несколько особенностей, которые выделяют Kubernetes перед обычными докер-контейнерами.
- Kubernetes — это готовая платформа. С помощью Kubernetes можно построить платформу промышленного уровня. В нем есть механизмы авторизации, возможность разделять работу нескольких команд и проектов, отдельные пространства имен и многое другое. С простыми докер-контейнерами сложно построить такую систему самостоятельно.
- Kubernetes — это проект-конструктор. Для Kubernetes существует много сторонних расширений и интеграций с другими инструментами: мониторинг, безопасность, инструменты разработки и многое другое. Это позволяет настроить его под себя так, как нужно именно для ваших нужд.
Когда использовать Kubernetes, а когда достаточно Docker-контейнеров?
Если у вас мало контейнеров и они все работают на одном узле, вам будет достаточно простых Docker-контейнеров. Kubernetes нужен, когда у вас много контейнеров и узлов, которыми нужно управлять. Также Kubernetes подходит, если вам нужна распределенная отказоустойчивая система.
Сколько в вашем проекте тестовых стендов — 5, 10 или больше 10? Навскидку, нужны стенды для каждой команды разработки, стенды для QA под каждый проект, менеджерам проектов тоже нужны стенды, а еще CI — трудно это все точно разграничить и не вызвать конфликтные ситуации. Одним словом, почему бы нам не делать тестовый стенд ровно тогда, когда он нужен? Нужен сейчас тестовый стенд — мы его сделали, не нужен — мы его удалили.
Именно такой подход предложил Александр Дубровин (adbrvn) на Highload++ 2017 в своем докладе, расшифровку которого вы найдете под катом.
О спикере: Александр Дубровин работает в Superjob. Известно, что проекты этой компании высоконагруженные. Но сегодня мы не будем говорить о том, сколько пользователей посещают портал, и сколько данных хранится на серверах, а затронем другие показатели.
Забегая вперед, скажем, что, на самом деле, Superjob не знают, сколько у них тестовых стендов. Но обо всем по порядку. Начнем с небольшой истории.
Немного истории
Представим себе небольшой проект S. В нем есть команда разработчиков, которым надо где-то тестировать свой код. Чтобы организовать тестирование, мы поставим тестовую машину, сделаем ее похожей на продакшен, накатим туда код, запустим и разработчики смогут там что-то тестировать.
В какой-то момент команда начинает расти, и необходим штат тестировщиков. Появляются QA-специалисты, и им тоже нужно где-то тестировать.
Можно использовать простой подход — выделить какой-то участок для тестировщиков, накатить туда такую же копию, и вот они уже могут тестировать. Все замечательно и хорошо!
Проект продолжает расти, и появляется дополнительная команда разработки. Им также требуется где-то что-то тестировать. Подход уже знаком — мы отделяем еще одну часть тестового сервера.
Но на самом деле команды тоже растут — по одному тестовому стенду им становится мало. Задач они тоже делают больше, поэтому тестировщикам нужно много тестировать.
Примерно в такой стадии можно начать замечать интересные истории. Допустим, есть тестировщик Вася, который хочет протестировать какую-то задачу. Он выбирает тестовый стенд, раскатывает туда код и начинает тестировать. Кликает, кликает и понимает, что что-то не то, что-то не работает, и вообще задача не сделана.
В JIRA начинают падать тикеты, возле Васи начинают собираться разработчики со словами: «Да как же так? Все же сделано!» и кто-то наконец спрашивает: «А у тебя какая ветка на тест раскатана?» Вася смотрит — не та. Ветка быстро исправляется, тикеты в JIRA закрываются, все хорошо. Вася продолжает тестировать, у него все работает.
Но в это время в другом конце комнаты разработчик Вова думает: «Странно, а почему у меня не работает?» Но он быстро понимает, что ветка не та. Раскатывает ту, что нужно, и проблемы снова у Васи.
За пару итераций они понимают, что они просто тестируют на одном тестовом стенде и мешают друг другу. В результате время потрачено впустую, Вася и Вова недовольны.
Другая история. Разработчик Коля знает про Васины проблемы, заранее приходит к нему и спрашивает, какой тестовый стенд сейчас свободен. Вася указывает свободный, и все хорошо. Через пару дней они встречаются снова, и Вася спрашивает у Коли: «Ты нам тестовый стенд вернешь? Ты его занимал на часок, а уже 2 дня прошло».
И снова проблема — либо разработчику искать другой стенд, либо все будут бодро ждать, пока он закончит тестирование.
На самом деле на схеме выше отображено не все. Здесь не хватает менеджеров. Иногда менеджеры хотят смотреть еще не протестированный сырой код. Подход стандартный — мы снова выделяем уголок тестового сервера и делаем еще тестовые стенды.
Самая последняя часть этой системы — это конечно CI — он тоже хочет куда-то стрелять, где-то тестировать.
Плавно развивая такую схему, мы получаем бесконтрольное изменение тестовых стендов. Схема плоха тем, что мы действительно не контролируем такие стенды — мы не знаем:
- кто тестирует на этом стенде в данный момент;
- что там раскатано;
- мы вообще не знаем, занят он сейчас или свободен.
В этот момент мы задумались — что же делать? Зачем нам столько тестовых стендов? Почему бы нам не делать тестовый стенд ровно тогда, когда он нужен? Нужен сейчас тестовый стенд — мы его сделали, не нужен — мы его удалили.
Следующий шаг в этой идее — делать тестовый стенд под каждую ветку кода.
Вроде идея хорошая, но есть технические нюансы. Нам нужны стенды:
- независимые;
- очень похожие на продакшен — мы хотим тестировать ближе к продакшен-окружению;
- с возможностью быстро создавать стенды, потому что мы хотим, чтобы, как только ветка появилась, тут же под нее появился бы и тестовый стенд.
Суровая реальность
Еще есть суровая реальность, в которой у нас:
Сказано — сделано!
Docker/docker-compose
Во-первых, мы говорили о том, что тестовые стенды должны быть изолированными и максимально похожими. В наше время это позволяет реализовать docker. Он даст возможность запускать контейнеры. Очевидно, что одним контейнером мы не обойдемся, более того, нам надо запускать кучу похожих стеков. Поэтому нужен docker-compose.
Замечательно — мы будем использовать docker — это стильно, модно, молодежно.
Распиливаем монолит выделяем сервисы
Docker пропогандирует микросервисный подход и здесь мы встаем перед проблемой, потому что у нас монолит.
Вы когда-нибудь пробовали оценить, сколько стоит распилить монолит по микросервисам? Очевидно, что эта цифра измеряется в человеко-годах.
В какой-то момент мы посмотрели на компонентную схему нашей системы и увидели, что здесь у нас есть load-balancing, здесь — приложение на php, здесь — node.js-приложение. Почему бы нам не запускать именно это, как сервис. Давайте найдем то, что мы можем запускать в docker-контейнерах.
Настраиваем сеть
Дальше нам необходимо каким-то образом достучаться до нашего тестового стенда. Естественно, нам необходимо вытащить 80-ый порт наружу, чтобы браузер мог открыть наш тестовый стенд, но если такие стенды будут запускаться в рамках одной машины, нам нужно выдавать IP.
В документации имеется целый огромный раздел про настройку сетей.
Docker умеет использовать различные типы сетей. В нашем случае очень помогла сеть типа macvlan. Это технология, которая позволяет на одном физическом сетевом интерфейсе реализовывать пачку виртуальных сетевых интерфейсов. При этом docker сам будет управлять этими интерфейсами: создавать, добавлять на машину и получать уже внешние, по отношению к хост-машине, IP-адреса.
Таким образом мы можем запустить пачку контейнеров, дать фронт-контейнеру (балансеру) возможность получить внешний IP-адрес и открыть на нем 80-ый порт. Мы уже можем постучаться туда при помощи браузера.
Поднимаем DNS и API
Мы помним, что у нас есть доменные зоны и куча поддоменов. Таким образом, обратиться к тестовому стенду мы можем только по домену 2-го уровня. Здесь есть как колоссальный плюс, так и колоссальный минус:
- Минус — нам очевидно придется каким-то образом перекрывать реальные домены в зоне ru, ua, uz, by.
- Плюс — в качестве домена 2-го уровня можно зашить прямо конкретное название ветки — мы же делаем тестовые стенды под каждую ветку кода.
Минус обходится на самом деле просто. Если нам приходится перекрывать домены, мы просто добавляем префикс и таким образом ограничиваем набор перекрываемых доменов — с этим уже можно мириться.
В нашем случае мы выбрали префикс sj. Получается, нам приходится перекрывать домены только с префиксом sj — таких явно немного.
Еще одна часть DNS — это API. Как уже говорилось, необходимо поднимать тестовые стенды быстро. Поэтому нам нужен DNS-сервер, который позволяет быстро добавлять и быстро убирать запись по API в автоматическом режиме.
Решение — PowerDNS. Этот сервер позволяет достаточно быстро и просто прикрутить к нему API и при помощи скриптов добавлять и удалять тестовые стенды.
Замечательно! Мы подняли и настроили DNS, научили наши контейнеры в него прописывать свои IP, но чего-то не хватает.
Делаем SSL-CA
Мы живем в XXI веке. Очевидно, что весь интернет — SSL и тестовые стенды должны поддерживать SSL. Достаточно много багов специфичны для SSL, и mixed content — только вершина айсберга.
Итого, нам нужен способ быстро получить сертификат и быстро его применить на поднимающийся тестовый стенд. В нашей компании уже был центр сертификации, основанный на OpenSSL. Здесь мы пошли простым методом написания своего велосипеда.
Велосипед пишется за один день и позволяет при помощи GET-запросов получать сертификаты, сгенерированные уже на конкретное имя домена.
Осталось самое малое. Нужно это автоматизировать, потому что мы же хотим все это делать одной кнопкой.
Автоматизируем
Для себя на начальном этапе мы написали консольный скрипт, который позволяет просто поднять тестовый стенд или его удалить.
Очевидно, тестировщикам это не очень удобно. Поэтому можно, например, сделать специальную сборку, которая соберет тестовый стенд и запустит его.
Но на самом деле самый крутой шаг в этом плане — это добавить такую кнопку прямо в JIRA-тикет. Представьте, ваш тестировщик открывает JIRA-тикет, читает требование, нажимает кнопку и получает через пару минут тестовый стенд — здорово же?
Плюсы
- Изначально мы планировали это именно под ручное тестирование, чтобы тестировщик мог запустить и прокликать любую версию своего кода, и это круто заработало.
- Следующий дополнительный бонус — у нас появились демо-хосты. Это то же самое, только в JIRA-тикет заходит не тестировщик, а менеджер проекта. Он тоже может посмотреть и покликать сырой код.
- Мы получили колоссальный плюс для CI. Когда мы обучили CI точно так же поднимать тестовый стенд на конкретную версию кода и потом его удалять, у нас появилась возможность запускать абсолютно любые тесты для любой ветки. Даже самые сложные интерфейсные selenium тесты я могу одним кликом прогнать для любой ветки в моем проекте.
Минусы
Но есть и минусы, я бы сказал, нюансы в управлении стендов. Нужно научиться правильно ими управлять.
Первую версию своей системы мы выкатили на старый слабый сервер и настроили создание тестовых стендов под каждую новую ветку. Конечно, где-то через день-полтора сервер не справился — просто потому, что веток появляется очень много.
Тогда, мы перестали их создавать автоматически, а появилась кнопка в JIRA, CI научилась запускать и останавливать тестовые стенды, собирать с них логи.
Определенно есть класс задач, которые эта система не позволит решать. Например, часто всплывающая проблема — это общее время для всех контейнеров. Некоторые задачи было бы удобно тестировать, сдвинув время на сервере. Эта система, к сожалению, не позволяет решать такие задачи. Но такие задачи можно решить, добавив в код специальные ветки для тестирования, чтобы можно было, например, посмотреть, как поведет себя форма через 2 недели.
На входе мы имели систему тестовых стендов, которая заставляла нас искать тестовый стенд и не гарантировала нам то, что никто не будет мешать друг другу на этих тестовых стендах.
Было: «Вася, а какой тестовый свободный — мне свою задачу раскатить потестировать».
На выходе получилась одна кнопка, которую можно нажать и получить через пару минут готовый тестовый стенд под конкретную версию кода. Даже если этим стендом будет пользоваться несколько человек, гарантировано то, что эти люди хотят смотреть именно эту версию кода.
Стало: «Жму кнопку и через полторы минуты получаю новый тестовый стенд под конкретную задачу».
Бонусом мы получили все тесты в один клик. Как я уже говорил, любые тесты на любой ветке прямо из CI выбираются одной кнопкой. Дальше машина все сделает сама: поднимет тестовый стенд, обстреляет его, соберет с него логи и удалит.
Возвращаясь к своему первому вопросу, сколько же тестовых стендов нам нужно? Я не знаю, сколько нам нужно тестовых стендов, потому что сегодня их нужно 20, завтра — 15, послезавтра 25.
Но я точно знаю, что у нас ровно столько тестовых стендов, сколько нужно здесь и сейчас.
Время летит незаметно, и до фестиваля конференций РИТ++ осталось совсем немного, напомним он пройдет 28 и 29 мая в Сколково. Пользуясь случаем, приводим небольшую подборку заявок RootConf для широкого круга слушателей:
Kubernetes давно стал стандартом де-факто как среды для эксплуатации cloud native приложений. А именно такие приложения модно делать в настоящее время. Но для того, чтобы понимать почему он так популярен, надо знать подходы, которые он предлагает для решения стандартных проблем разработки. Например, эти проблемы упоминались еще в манифесте 12 factor app от Heroku.
В свое время мне пришлось знакомиться со всем э т им по официальной документации проекта на kubernetes.io. Я справился, прошел сертификацию и получил статус CKA (Certified Kubernetes Administrator). Но теперь есть лучший способ, чтобы познакомиться с функционалом этого оркестратора — а именно прочитав книгу Kubernetes Patterns… Или хотя бы это краткое саммари.
По структуре книга напоминает классическую книгу “Design Patterns” банды четырех, которая содержала creational, structural и behavioral паттерны. Но у нас здесь 5 категорий паттернов:
- Foundation patterns — базовые блоки k8s, на основе которых строится все остальное
- Behavioral patterns — поведенческие паттерны, которые позволяют добиться желаемого поведения, например, запуска периодических job или приложения синглтона
- Structural patterns — структурные паттерны, которые показывают как можно расширить функционал основного контейнера добавив другие контейнеры в pod
- Configuration patterns — конфигурационные паттерны, которые позволяют эффективнее управлять конфигурацией ваших приложений
- Advanced patterns — продвинутые паттерны, которые раскрывают темы того, как работает сам k8s и как его можно расширять
Общий список паттернов представлен на рисунке ниже.
Но начнем бы с базы, а именно с
Все начинается с паттерна Predictable Demands, который говорит о том, что ваше приложение должно декларировать свои потребности и придерживаться их. Это может относиться как runtime зависимостям (диск, configMap, …), так и к запрошенным ресурсам (cpu, memory), что позволяет как планировать мощности кластера в целом, так и помогает с шедулингом pod’ов на конкретные ноды в кластере.
Дальше идет декларативное развертывание (Declarative Deployment), которое позволяет нам описать как деплоить наше приложение и оркестратор сам позаботиться обо всем:) Среди вариантов развертываний есть: rolling deployment, fixed deployment, blue green, canary release. Здесь есть крутые gif, которые демонстрируют эти стратегии в динамике.
Для того, чтобы k8s мог поддерживать работоспособность приложения ему требуется некоторая помощь от разработчиков, которые должны реализовать health probes. Эти пробы включают Liveness, Readiness and Startup Probes, где каждая имеет свое назначение:
- liveness — проба, которая позволяет понять живо еще приложение или его надо развернуть заново
- readiness — проба, которая говорит о том, можно ли роутить запросы в приложение или оно не готово, например, в приложении возникли проблемы и мы не хот
- startup — проба для стартующих медленно приложений, которые на старте прогреваются (привет java)
Подробнее про настройку этих проб можно почитать в документации.
Следующий паттерн — это Managed Lifecycle, который говорит о том, что хорошие cloud native приложения должны определенным образом организовывать свой жизненный цикл. Фактически, за управление этим жизненным циклом отвечает платформа оркестрации, которая стартует приложения, отправляет при необходимости SIGTERM, SIGKILL и дергает хуки типа PreStop, PostStart и подобные. Важно сделать так, чтобы приложение умело работать с указанными выше событиями, а также использовало при необходимости нужные хуки.
И финальный паттерн в этом разделе Automated Placement, который относится к ключевому функционалу k8s, который планирует размещение подов на конкретные ноды кластера с учетом
- запросов конкретных контейнеров
- доступных нод и ресурсов в рамках них (cpu, memory, volumes, …)
- политик размещения (placement policies)
- правил affinity и antiaffinity, а также taints и tolerations
В общем, это ключевой функционал, который работает под капотом оркестратора, чтобы обеспечить наш декларативно оформленный deployment.
Паттерны из этой категории посвящены взаимодействию между подами и платформой оркестрации. Многие из этих паттернов напоминают старых знакомых из мира Linux, которые просто переехали в мир распределенных систем.
Начинается все с паттерна Batch Job, который описывает изолированную часть работы, которая работает до выполнения. Следующий паттерн Periodic Job напоминает работу cron job и заключается в выполнении какого-то объема работы, привязанное к определенному моменту во времени. Кстати, точных гарантий, что работа начнется ровно в это время нет.
Следующий паттерн — Daemon Service, который позволяет запускать специальные поды, больше сфокусированные на инфраструктурных задачах, перед запуском подов самого приложения.
Следующий паттерн singleton service, который позволяет гарантировать запуск только одной копии сервиса, но все еще в режиме высокой доступности (high availability). Тут интересно, что решение в лоб, через ReplicaSet с фактором репликации 1 не проходит, так как
Kubernetes primitives such as ReplicaSet, favor availability over consistency — a deliberate decision for achieving highly available and scalable distributed systems. That means a ReplicaSet applies “at least” rather than “at most” semantics for its replicas
Но для реализации поведения singleton service мы должны придерживаться подхода, что consistency важнее availability, и такой примитив в Kubernetes есть и это StatefulSet. Но если требуется действительно надежное поведение, то придется реализовывать in-app locking на уровне приложения.
Для реализации stateful приложений внутри k8s используется паттерн Stateful Service, который обеспечивает такие фичи как постоянные identity, сеть, диск, ординальность. Все это реализуется при помощи использования описания сервиса в виде StatefulSet. Помимо указанных выше есть и другие фичи, такие как partitioned updates, parallel deployments и at-most-one guarantee. Ну и напоследок цитата авторов книги насчет StatefulSet
It provides a good set of building blocks for managing stateful applications in an automated fashion, making them first-class citizens in the cloud-native world.
Следующий паттерн относится к тому, как клиенты получают доступ к информации о том, где развернуты инстансы, предоставляющие сервисы приложений. Суть в том, что оркестратор в любой момент может сделать rescheduling конкретных подов и они переедут на другую ноду, но трафик должен все так же успешно доходить до приложений. За это отвечает Service Discovery. Механизмы отличаются для service discovery внутри кластера и для внешнего трафика. Среди разных механизмов есть
- внутренние — ClusterIP, Manual IP, Manual FQDN, Headless Service
- внешние — NodePort, LoadBalancer, Ingress
Ну и последним паттерном из этой категории является Self Awareness, который позволяет приложению воспользоваться интроспекцией и получить метаданные о себе для того, чтобы вставить их в приложение. Данные можно получить через Env Variable и посредством Downward API, например, мы можем узнать про имя ноды, ip ноды, имя пода, ip пода, данные по request и limit пода и т.д. Эти данные можно использовать для того, чтобы подтюнить приложение или для расширения информации по логам и метрикам, которые отгружаются приложением.
Эти паттерны рассказывают про то, как эффективнее использовать такую структуру как pod, который является единицей развертывания и включает группу контейнеров. Основная логика этих паттернов в том, что мы добавляем специализированные контейнеры в под наряду с контейнерами основного приложения и это расширяет функционал или добавляет удобства.
Ну и все начинается с паттерна Init Container, который добавляет отдельный жизненный цикл для задач инициализации основного приложения, состоящего из группы контейнеров. Суть в том, что подготовительная работа выполняется init контейнером, после успеха которой стартует основные контейнеры.
Продолжается все с того, как использовать паттерн Sidecar для расширения функционала основного приложения без его изменения. По факту, это реализация подхода композиции из объектно-ориентированного мира.
Следующий паттерн Adapter, который является sidecar, но сфокусирован на том, чтобы стандартизировать и унифицировать распределенную гетерогенную систему, приведя к каноническому формату. Например, это отлично подходит для инфраструктурных задач типа логирования и мониторинга.
И последний паттерн из этой группы — это Ambassador, который является sidecar, но сфокусирован на упрощении обращений для приложений обращений во внешний мир. Например, этот паттерн может выступать в качестве прокси и отвязывать основной под от внешних зависимостей.
Каждое приложение требует конфигурации. Хранить конфигурацию захардкоженной внутри приложения — давно антипаттерн. В этих паттернах идет речь о том, какие возможности есть у Kubernetes для конфигурирования приложений.
Простейший подход — это EnvVar Configuration, в которой для конфигурирования приложения используются переменные окружения (environment variables). В Kubernetes их можно подтянуть из файла, из ConfigMap или из Secret. Этот подход хорош для небольших приложений и плохо масштабируется на большое количество параметров конфигурации.
Если параметров много, то удобнее использовать Configuration Resource для хранения и передачу параметров в приложения. Такими ресурсами являются ConfigMaps и Secrets. Эти ресурсы можно подключать в качестве environment variables или подключенных volumes.
Еще один вариант Immutable Configuration — это хранение конфигурации в отдельном контейнере, который подключается внутрь пода в качестве sidecar к нашему основному приложению.
И последний паттерн Configuration Template, который позволяет создавать и обрабатывать большие конфигурации во время старта приложений. Сгенерированные конфигурации специфичны для runtime окружения и внутри них используются параметры, которые были подставлены в конфигурационные темплейты.
В последней части рассматриваются более сложные паттерны, которые не попали в предыдущие категории. Первый паттерн Controller является базовым блоком Kubernetes, который отслеживает состояние и поддерживает ресурсы в нужном состоянии, например, количество реплик приложения для ReplicaSet.
Второй паттерн Operator объединяет концепцию Controller с кастомным определением ресурсов (CRD, Custom Resource Definitions). По-факту, этот паттерн позволяет реализовать эксплуатацию кастомного приложения в автоматической форме, напоминающей подход самого Kubernetes. Подробнее можно прочитать в документации Operator Framework.
Третий паттерн Elastic Scale очень интересен, так как именно он является killer фичей для заезжающих в kubernetes приложений. По-факту, мы можем декларативно описать правила масштабирования среди которых
- Horizontal Pod Autoscaling — увеличение количества инстансов приложения путем увеличения количества подов
- Vertical Pod Autoscaling — увеличение ресурсов, выделенных для конкретных подов
- Cluster Autoscaling — увеличение размеров кластера, а именно доступных нод
И последний паттерн Image Builder переносит аспект билда образов приложений прямо внутрь кластера.
Условия:
Срок рассрочки: 4 месяца
Первый платеж: от 0 руб.
Переплата: 0 руб. Вы оплачиваете только стоимость курса, без процентов.
Процесс:
1. Оставляете заявку и получаете на почту анкету для оформления рассрочки.
2. Банк принимает решение в течение нескольких минут.
3. Заключаете сделку с банком онлайн.
4. Мы отправляем кассовый чек на эл. почту
и предоставляем доступ к курсу.
Формат подобного обучения мы уже протестировали на онлайн-интенсивах, на практических занятиях Вечерней школы Слёрма и в корпоративном обучении.
Администраторам, которые работают c Kubernetes и знакомы с его основными компонентами и абстракциями, и чей заказчик или работодатель предъявляет повышенные требования к стабильности и безопасности своих систем. Kubernetes База + Kubernetes Мега дают все материалы, необходимые для сдачи экзамена на CKA в CNCF.
— Инженер с 25-летним стажем
— Certified Kubernetes Administrator
— Внедрения Кубернетес: все куб-проекты Southbridge, включая собственную инфраструктуру
— Один из разработчиков kubespray с правами на принятие pull request
- Работа с Kubeadm
- Тестирование и траблшутинг кластера
- Практическая работа
- LDAP (Nginx + Python)
- OIDC (Dex + Gangway)
- Практическая работа
- Введение в CNI
- Network Security Policy
- Практическая работа
- PodSecurityPolicy
- PodDisruptionBudget
- PriorityClass
- LimitRange/ResourceQuota
- Практическая работа
- Строение контроллера
- Операторы и CRD
- Практическая работа
- Нюансы запуска базы данных в Kubernetes.
- Запуск кластера базы данных на примере RabbitMQ и CockroachDB
- Практическая работа
- Управления секретами в Kubernetes
- Vault
- Практическая работа
- Бэкап и восстановление кластера с применением Heptio Velero (бывш. Ark) и etcd
- Практическая работа
- Продление сертифкатов control-plane с помощью kubeadm
- Практическая работа
- Инструменты темплэйтирования и деплоя
- Стратегии деплоя
- Практическая работа
- Установка Istio
- Обзор основных абстракций
- Практическая работа
Вы получите ссылку на Zoom, доступ в облако, предоставленное Selectel, и в телеграм-канал. Последовательно выполняя задания, с помощью kubeadm вы развернете кластер и запустите в нем приложение.
Практика проходит в облаке, предоставленном спонсором Selectel, поэтому требования к компьютерам минимальные: macOS, Linux или Windows с установленным git и ssh-клиентом.
Рекомендуем вывести Zoom и консоль на разные мониторы либо разные устройства.
В личном кабинете Слёрма будут доступны 2 года записи из Zoom. Доступ в git сохранится в течение месяца, чтобы у вас была возможность его склонировать.
Доступ к стендам будет закрыт по окончании курса. Если вы не можете проходить практику в общем потоке, сообщите об этом.
Какая сущность поддерживает нужное количество инстансов приложения?
В какой абстракции лучше запускать приложения, которые требуют персистентного хранения данных (например, база данных)?
Какая абстракция запускает приложение в количестве инстансов, равным количеству серверов кластера?
Какой компонент кластера отдает команды остальным компонентам?
Какой компонент кластера отвечает за назначение подов на ноды?
Где хранится информация о кластере, его настройки и манифесты?
Kubernetes не хранит никакой информации о кластере, настройках и манифестах Как создать новый доступ в кластер с определенными правами?
Как можно опубликовать свое приложение из кластера наружу?
Приложение, которое управляет входящим трафиком в кластер Манифест, в котором описываются правила управления трафиком Только чтобы опубликовать приложение наружу кластера Для настройки взаимодействия приложений внутри кластера Этот сервис имеет широкий спектр использования, но работает только у облачных провайдеров Только чтобы настроить взаимодействие приложения и API-сервера
Сертификация от учебного центра Слёрм подтверждает, что вы действительно владеете материалом. Чтобы получить сертификат, нужно сдать внутренний экзамен: мы дадим задание и предоставим стенд для выполнения.
По результатам экзамена выдается номерной именной сертификат, который заносится в реестр. Скан присылаем на эл. почту, а оригинал — почтой России.
Читайте также: