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

Миграция

Ещё один шаг в сторону open source как и почему мы внедрили Arenadata DB

22.04.2021 10:12:30 | Автор: admin

Привет, Хабр! Меня зовут Станислав Маскайкин, я архитектор аналитических систем ВТБ. Сегодня я расскажу о том, почему мы перевели нашу систему подготовки отчётности с Oracle SuperCluster на российскую Arenadata DB. Как мы выбирали решение, почему не взяли чистый опенсорс, а также о некоторых результатах такой миграции под катом.

Зачем нужен был переход?

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

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

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

Исторически ВТБ24 был ориентирован на работу с физическими лицами, ВТБ на работу с юридическими лицами, а Банк Москвы на работу и с первыми, и со вторыми.

На момент объединения этих банков обязательная отчётность формировалась в следующих системах:

  1. Единое хранилище данных (ЕХД) хранилище данных Банка Москвы, реализованное на SuperCluster M8 и ETL-инструменте Informatica Power Center.

  1. Система подготовки отчётности хранилище данных ВТБ24, реализованное на Oracle SuperCluster M8 с программным обеспечением Diasoft Flextera BI. Данные для этой системы готовились в другом хранилище корпоративном хранилище данных (КХД), реализованном на СУБД Teradata и ETL-инструменте SAS Data Integration. КХД, в свою очередь, получало данные из оперативного хранилища данных, реализованного на Oracle SuperCluster M8. А туда они реплицировались из автоматизированных банковских систем при помощи инструмента Oracle Golden Gate.

  1. Корпоративное информационное хранилище хранилище данных ВТБ, реализованное на Oracle Exadata X8-2 и ETL-инструменте Informatica Power Center.

Чтобы не формировать обязательную отчётность по объединённому ВТБ в ручном режиме, были созданы интеграции между хранилищами данных.

Это привело к ещё двум большим проблемам:

  1. Увеличилось время получения данных, что часто приводило к срыву сроков предоставления информации.

По правилам ЦБ РФ отчётность за очередной месяц сдаётся в течение первых четырех дней следующего месяца. В банке под это задействуются огромные вычислительные мощности. Если первые дни месяца попадают на рабочие дни (как в марте), мы даже не имеем возможности вернуться и что-то пересчитать это риск для банка не сдать отчётность вовремя. Повышение доступности данных на этом уровне играет огромную роль.

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

Ещё один момент: многие компоненты нашей инфраструктуры, такие как Oracle SuperCluster, на котором у нас реализована большая часть аналитического ландшафта, попали под End of life. Они были сняты с поддержки производителем и больше не развивались, т.е. обновление необходимо было в любом случае.

Проблема окончания поддержки коснулась не только системы подготовки отчётности, но и озера данных на платформе Oracle Big Data Appliance. К тому моменту а происходило все в 20182019 годах сотрудники ВТБ уже в полной мере оценили data-driven подход и потребляли достаточно много данных. Поэтому с точки зрения бизнеса банка система была критичной. Т.е. перед нами стояла более глобальная задача масштабов всей инфраструктуры.

Параллельно в объединённом ВТБ началась масштабная цифровая трансформация, охватившая все уровни IT, начиная от создания новых ЦОДов и объединения сетей, и заканчивая унификацией автоматизированных банковских систем и созданием омниканальной платформы для фронтальных решений. Всё это кардинально меняло внутренний IT-ландшафт банка.

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

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

Что такое платформа данных? Для себя мы определили её так: это набор сквозных интегрированных технологических решений (технологическое ядро), которые являются основой для разработки и функционирования сервисов по работе с данными банка ВТБ.

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

Мы выделили 6 компонентов технологического ядра:

  1. Управление данными.

  2. Управление качеством данных.

  3. Управление доступом.

  4. Аналитические справочники.

  5. Корректировки.

  6. ETL Framework.

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

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

Выбор новой платформы

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

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

Мы рассматривали всех крупнейших производителей подобных решений: Oracle Exadata, Teradata, Huawei. Оценили отечественные разработки практически все, что есть на рынке. Нам показался интересным опенсорс, тем более для банка это не первый заход в тему открытого исходного кода.

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

При выборе платформы мы учитывали следующие критерии:

  • функциональность

  • качество поддержки

  • отсутствие санкционных рисков

  • возможность гибкого масштабирования

  • надёжность

  • безопасность

  • наличие Road Map развития платформы и возможность на него влиять

  • стоимость владения совокупные затраты на программно-аппаратный комплекс на горизонте 10 лет (TCO5).

Если взять последний критерий, то даже с учётом стоимости всех контуров Arenadata DB и самого проекта миграции мы получали существенную экономию на фоне Oracle SuperCluster.

В итоге по совокупности факторов мы выбрали Arenadata DB.

Тестирование платформы

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

В итоге нами были проведены следующие проверки.

  • Функциональное тестирование:

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

  • Отказоустойчивость и отключение компонентов:

  • Отключение дисковых устройств на уровне БД для проверки стабильности работы кластера.

  • Отключение питания одного блока питания на серверах для проверки стабильности работы кластера.

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

  • Совместимость со средствами резервного копирования банка:

    • Проведение цикла резервного копирования и восстановления БД на СРК Veritas Netbackup:

      • Полное резервное копирование БД

      • Инкрементальное резервное копирование БД

      • Восстановление БД

  • Управление и качество работы системы:

    • Перезагрузка кластера: фиксация успешного выполнения процедуры перезагрузки.

    • Мониторинг и управление: субъективная балльная оценка от 0 до 5.

    • Генерация тестового отчёта: прогон запроса изсистемы подготовки отчётности в Arenadata DB для анализа качества результата генерируемого отчёта результаты выполнения отчёта должны быть идентичны.

  • Нагрузочное тестирование:

    • Скорость загрузки данных

  • Интеграционное тестирование:

    • Интеграция с ПО Infomatica Power Center

    • Интеграция с Oracle BI

    • Интеграция с QlikView 12.

Результаты тестирования

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

Ниже приведено сравнение скорости выполнения запросов по сравнению с текущим Oracle Super Cluster T5-8.

Тестирование проводил системный интегратор IBS Platformix.

Скорость выполнения синтетического запроса Jmeter (сек)

Arenadata DB (сек.)

Oracle (сек.)

160.3

1291

Кластер показал высокую скорость загрузки данных через ETL-инструмент Informatica Power Center: 200 Мбит/с.

В ходе тестирования была также осуществлена интеграция с основными BI-инструментами, используемыми в Банке ВТБ (Oracle BI и QlikView), и протестирован их функционал.

В QlikView на простейших SQL-запросах протестированы соединение с БД и выборка данных с последующей загрузкой в модель BI-инструмента.

Результаты выполнения представлены в таблице ниже.

Тест 1

Тест 2

Драйвер

ODBC PostgreSQL35W

ODBC PostgreSQL35W

Запрос

select * from user.test1

// 3 коротких поля

select e.* from dds.accounts e

where

e.entry_dt ='2019-02-03'

-and e.partition_source_system_cd ='00006'

and e.src_deleted is null

Строк

20480000

45 920

Затраченное время

0:58

2:59

Скорость загрузки в модель, строк в сек.

353103

257

При выполнении тестов была замечена особенность: получение первых строк данных из БД происходило с задержкой примерно в 23 секунды. После этого скорость выборки данных из БД и их доставки в QlikView становилась очень высокой.

Предположительно данная особенность связана c неоптимальной настройкой коннектора.

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

Цель теста

Предварительные условия

Процедура

Результат

Тестирование отказа диска с данными

Отказ диска эмулируется физическим извлечением диска или логическим отключения дискового уст-ва из работающего сервера.

Подключиться к серверу

Провести процедуру unmount для физического диска

Проверить доступность данных

Данные доступны

Тестирование отказа кэширующего диска

Отказ диска эмулируется физическим извлечением диска или логическим отключения дискового устройства из работающего сервера

Подключиться к серверу

Провести процедуру unmount для физического диска

Проверить доступность данных

Данные доступны

(Отказ кэширующего диска не приводит к потере данных)

Тестирование включения кластера после эмуляции аварии

Отказ сервера при выполнении SQL запроса к базе данных, эмулируется отключением электропитания работающего сервера

Подключиться к серверу

Выполнить SQL запрос

Выключить 1 ноду

Перезапустить выполнение SQL

Данные получены при повторном SQL запросе

Благодарю Дениса Степанова и Никиту Клименко, экспертов IBS Platformix, за предоставленные результаты тестирования.

Сбор отчётности как пилот

Наша цель это миграция на Arenadata DB всех существующих хранилищ банка ВТБ.

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

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

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

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

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

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

Поскольку мы уже видим результаты и детали взаимодействия, около полугода назад стартовал наш основной проект - миграция на продукты Arenadata центрального единого хранилища данных и озера данных. Помимо Arenadata DB, мы используем Arenadata Streaming на базе Apache Kafka и Arenadata Hadoop на базе Apache Hadoop. В ближайшее время первые результаты пойдут в продакшен.

Целевая архитектура платформы данных к концу 2022 годаЦелевая архитектура платформы данных к концу 2022 года

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

Подробнее..

Перенос почты между серверами через интерфейс пользователя посредством IMAPSync

02.09.2020 04:14:03 | Автор: admin

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

На сервере назначения необходимо иметь ящик с нужным логином и паролем. Перед использованием Imapsync нужно обязательно установить его (http://personeltest.ru/aways/imapsync.lamiral.info/#install).

По причине запрета организацией использовать пароли от почтовых ящиков сотрудников в скрипте процесс миграции передаем пользователю. Для этого разработан web-интерфейс пользователя, который состоит из модуля формы (gis.html) и модуля запуска скрипта imapsync (gis.php). Заполнение серверов imap можно автоматизировать анализируя содержимое поля с названием почтового ящика. Использование Fetchmail как плагина roundcube не рассматривается, т.к. мною не был найден подробный связный разбор этого вопроса.

Web-интерфейс состоит из полей с информацией о почтовых ящиках, области вывода выполнения операции и кнопок управления (gis.html).

<html><head>    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">    <script>          //запуск скрипта sh из командной строки linux     function isexe() {      var ta = document.getElementById('output');      document.getElementById('output').value += 'Start import, please wait...\n';      var source = new EventSource('gis.php');      source.addEventListener('message', function(e) {       if (e.data !== '') {        ta.value += e.data + '\n';       }      }, false);      source.addEventListener('error', function(e) {       source.close();      }, false);     }//isexe    //кнопка Выполнить    function Complete() {      document.cookie = "mail1="+document.maildata.mail1.value;      document.cookie = "pass1="+document.maildata.pass1.value;      document.cookie = "mail2="+document.maildata.mail2.value;      document.cookie = "pass2="+document.maildata.pass2.value;      document.cookie = "msrv1="+document.maildata.msrv1.value;      document.cookie = "msrv2="+document.maildata.msrv2.value;      //alert(document.cookie); // показываем все куки      isexe();      document.cookie = "mail1="+document.maildata.mail1.value+"; max-age=0";      document.cookie = "pass1="+document.maildata.pass1.value+"; max-age=0";      document.cookie = "mail2="+document.maildata.mail2.value+"; max-age=0";      document.cookie = "pass2="+document.maildata.pass2.value+"; max-age=0";      document.cookie = "msrv1="+document.maildata.msrv1.value+"; max-age=0";      document.cookie = "msrv2="+document.maildata.msrv2.value+"; max-age=0";    }//Complete    function ShowCookie() {     alert(document.cookie); // показываем все куки    }    </script></head><body><H1>Миграция почтового ящика</H1><FORM NAME="maildata">   <TABLE>        <TR><TD><B>Исходный почтовый ящик:<B></TD>            <TD><INPUT NAME="mail1" SIZE=20 VALUE=""        <TR><TD><B>Пароль:<B>            <TD><INPUT TYPE="password" NAME="pass1" SIZE=20 VALUE=""        <TR><TD><B>IMAP сервер:<B></TD>            <TD><INPUT NAME="msrv1" SIZE=20 VALUE=""<TD>        <TR><TD><B>Конечный почтовый ящик:<B></TD>            <TD><INPUT NAME="mail2" SIZE=20 VALUE=""        <TR><TD><B>Пароль:<B>            <TD><INPUT TYPE="password" NAME="pass2" SIZE=20 VALUE=""        <TR><TD><B>IMAP сервер:<B></TD>            <TD><INPUT NAME="msrv2" SIZE=20 VALUE=""<TD>    </TABLE>    <p>Результат выполнения:<br/><textarea id="output" style="width: 50%; height: 25em;"></textarea></p>   <INPUT TYPE="button" VALUE="Выполнить" onClick="Complete();">    <INPUT TYPE="reset" VALUE="Сброс">    <INPUT TYPE="button" VALUE="Показать cookie" onClick="ShowCookie();"></FORM></body></html>

Модуль запуска скрипта imapsync (gis.php).

<?php ob_end_flush(); ini_set("output_buffering", "0"); ob_implicit_flush(true); header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); //вывод в область сообщенийfunction echoEvent($datatext) {  echo "data: ".implode("\ndata: ", explode("\n", $datatext))."\n\n"; }//echoEvent echoEvent("Start!"); //формируем строку запуска скрипта imapsync с параметрами $strexe = "/bin/bash /home/user/imapsync/startimapsync.sh "           .htmlspecialchars($_COOKIE["mail1"]).' '           .htmlspecialchars($_COOKIE["pass1"])." "           .htmlspecialchars($_COOKIE["mail2"])." "           .htmlspecialchars($_COOKIE["pass2"])." "           .htmlspecialchars($_COOKIE["msrv1"])." "           .htmlspecialchars($_COOKIE["msrv2"]); echoEvent($strexe);//запускаем sh скрипт из командной строки linux $proc = popen($strexe,'r');//могут быть ограничения php, поэтому достаточно вывести результат окончания миграции while (!feof($proc)) {  echoEvent(fread($proc, 4096)); }  echoEvent("Finish!");?>

Скрипт миграции почтового ящика (startimapsync.sh) принимает параметры командной строки: логины и пароли, imap-сервера исходного и конечного ящиков соответственно.

#!/bin/bash#переход в каталог откуда был запущен данный скриптcd `dirname $0`#запуск imapsync с нужными параметрами /home/user/imapsync/./imapsync \#сервер начального почтового ящика, логин, пароль  --host1 $5:993    --user1 $1 --password1 $2 \#сервер конечного почтового ящика, логин, пароль  --host2 $6:993    --user2 $3 --password2 $4 \#использование  шифрования при подключении к серверу  --ssl1  --ssl2 \#сопоставление папок  --automap \#первой синхронизируем папку входящих  --folderfirst INBOX \#сопоставление папок  --regextrans2 "s/&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-/Sent/" \  --regextrans2 "s/&BBoEPgRABDcEOAQ9BDA-/Trash/" \  --regextrans2 "s/&BCEEPwQwBDw-/Junk/" \  --regextrans2 "s/&BCcENQRABD0EPgQyBDgEOgQ4-/Drafts/" \#правильно принимаем не прочитанные письма во Входящих  --regexflag 's/\\Unseen//g' \#Сравнение полученных и отправленных писем по параметру Идентификационный номер  --useheader Message-Id
Подробнее..

Из Vue 2 на Vue 3 Migration Helper

09.06.2021 20:16:20 | Автор: admin

Предистория

Была у меня курсовая по веб-разработке, делать очередной интернет-магазин как-то не хотелось, и решил я написать помощник миграции из Vue 2 (options-api) в Vue 3 (composition-api) с авторазделением на композиции с помощью алгоритма Косарайю по поиску областей сильной связности

Для тех, кто не в теме, поясню, так выглядит код с options-api:

export default {  data () {    return {      foo: 0,      bar: 'hello',    }  },  watch: {    ...  },  methods: {    log(v) {      console.log(v);    },  },  mounted () {    this.log('Hello');  }}

и примерно так с composition-api:

export default {  setup (props) {    const foo = reactive(0);    const bar = reactive('hello');    watch(...);    const log = (v) => { console.log(v); };    onMounted(() => { log('hello'); });    return {      foo,      bar,      log,    };  }}

Автоматическое разделение на композиции

Дабы не отходить от самой идеи композиций, помимо трансляции кода под новый синтаксис composition-api, было принято решение добавить и возможность разделения монолитного компонента на самостоятельные композиции, и их последующее переиспользование в главном компоненте. Как же это сделать?

Сначала зададимся вопросом, что же такое композиции? Для себя я ответил так:

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

Блоками кода в нашем случае будем считать: свойства data, методы, вотчеры, хуки, и все то, из чего строится компонент Vue.

Теперь определимся на счёт зависимостей блоков кода между собой. С этим во Vue достаточно просто:

  • Если computed, method, hook, provide свойство внутри себя использует другие свойства, то оно от них и зависит

  • Если на свойство навешен вотчер, то вотчер зависит от наблюдаемого им свойства

  • и так далее :)

data: () => ({  array: ['Hello', 'World'], // block 1}),watch: {  array() { // block 2 (watch handler) depends on block 1    console.log('array changed');  },},computed: {  arrayCount() { // block 3    return this.array.length; // block 3 depends on block 1  },},methods: {  arrayToString() { // block 4    return this.array.join(' '); // block 4 depends on block 1  }},

Допустим, мы смогли пройтись по коду и выделить все-все зависимости свойств между собой. Как всё это делить на композиции?

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

Выделим из этого ориентированный граф, где вершинами будут свойства, а ребрами - зависимости между свойствами. А теперь самое интересное!

Алгоритм Косарайю

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

Никогда бы не подумал, что простое переписывание реализации из C на TS может быть таким проблемным :)

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

Поиск зависимостей

Примечание: во всех функциях компонента в options-api свойства доступны через this

Здесь немного грусти, поскольку искать зависимости в .js приходится так:

const splitter = /this.[0-9a-zA-Z]{0,}/const splitterThis = 'this.'export const findDepsByString = (  vueExpression: string,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  return vueExpression    .match(splitter)    ?.map((match) => match.split(splitterThis)[1])    .filter((value) => instanceDeps[value])    .map((value) => value)

Да, просто проходясь регуляркой по строкому представлению функции в поисках всего, что идет после this. :(

Более продвинутый вариант, но такой же костыльный:

export const findDeps = (  vueExpression: Noop,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  const target = {}  const proxy = new Proxy(target, {  // прокси, который записывает в объект вызываемые им свойства    get(target: any, name) {      target[name] = 'get'      return true    },    set(target: any, name) {      target[name] = 'set'      return true    }  })  try {    vueExpression.bind(proxy)() // вызываем функцию в скоупе прокси    return Object.keys(target) || [] // все свойства которые вызвались при this.  } catch (e) { // при ошибке возвращаемся к первому способу    return findDepsByString(vueExpression.toString(), instanceDeps) || []  }}

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

  • не работает с анонимными функциями

  • при использовании вызывается сама функция а если вы там пентагон взламываете?

Создание файлов и кода

Вспомним зачем мы тут собрались: миграция.

Используя все вышеописанное, получив разбитые по полочкам свойства, нужно составить новый код в синтаксисе composition-api, то есть собрать строки, которые в конечном счете будут являться содержимыми файлов в проекте.

Для этого надо уметь представлять экземпляры объектов, строк, массивов и всего остального в их естественном, кодовом, виде. Вот эта функция:

const toString = (item: any): string => {  if (Array.isArray(item)) {    // array    const builder: string[] = []    item.forEach((_) => {      builder.push(toString(_)) // wow, it's recursion!    })    return `[${builder.join(',')}]`  }  if (typeof item === 'object' && item !== null) {    // object    const builder: string[] = []    Object.keys(item).forEach((name) => {      builder.push(`${name}: ${toString(item[name])}`) // wow, it's recursion!    })    return `{${builder.join(',')}}`  }  if (typeof item === 'string') {    // string    return `'${item}'`  }  return item // number, float, boolean}// Exampleconsole.log(toString([{ foo: { bar: 'hello', baz: 'hello', }}, 1]);// [{foo:{bar: 'hello',baz: 'hello'}},1]  т.е. то же самое, что и в коде

Про остальной говнокод я тактично промолчу :)

Итоговые строки мы записываем в новые файлы через простой fs.writeFile() в ноде и получаем результат

Пример работы

Собрав всё это в пакет, протестировав и опубликовав, можно наконец увидеть результат работы.

Ставим пакет vue2-to-3 глобально (иначе не будет работать через консоль) и проверяем!

Пример HelloWorld.js:

export default {  name: 'HelloWorld',  data: () => ({    some: 0,    another: 0,    foo: ['potato'],  }),  methods: {    somePlus() {      this.some++;    },    anotherPlus() {      this.another++;    },  },};

Пишем в консоли: migrate ./HelloWorld.js и получаем на выход 3 файла:

// CompositionSome.jsimport { reactive } from 'vue';export const CompositionSome = () => {  const some = reactive(0);  const somePlus = () => { some++ };  return {    some,    somePlus,  };};// CompositionAnother.jsimport { reactive } from 'vue';export const CompositionAnother = () => {  const another = reactive(0);  const anotherPlus = () => { another++ };  return {    another,    anotherPlus,  };};// HelloWorld.jsimport { reactive } from 'vue';import { CompositionSome } from './CompositionSome.js'import { CompositionAnother } from './CompositionAnother.js'export default {  name: 'HelloWorld',  setup() {    const _CompositionSome = CompositionSome();    const _CompositionAnother = CompositionAnother();    const foo = reactive(['potato']);    return {      foo,      some: _CompositionSome.some,      somePlus: _CompositionSome.somePlus,      another: _CompositionAnother.another,      anotherPlus: _CompositionAnother.anotherPlus,    };  },};

Итого

На данный момент все это доступно и работает, но ещё есть некоторые баги со строковым представлением не анонимных функций и путями (в некоторых случаях фатально для linux систем)

В планах запилить миграцию для single-file-components и .ts файлов (сейчас работает только для .js)

Спасибо за внимание!

npm, git

Подробнее..

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

21.01.2021 10:23:56 | Автор: admin

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

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

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

С чем приходится сталкиваться


Хорошо, если целевое облако использует ту же платформу виртуализации, что и исходное. Так, при миграции из VMware на VMware достаточно сделать снапшот и загрузить его в новое облако. Это просто и, как правило, перенос проходит без неожиданностей. Для самой системы не меняется ничего, кроме IP-адресов и железа, на котором работает виртуализация.
А вот при переходе с VMware на гипервизор KVM задача усложняется, ведь KVM использует другой формат виртуальных машин. Чтобы преодолеть несовместимость, необходимо сделать дамп дисков и провести конвертацию. Попутно придется записать в виртуалку драйвера, которые позволят запуститься на новом гипервизоре.

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

Поэтому в идеале миграция должна проходить как можно быстрее и, по возможности, без остановки сервисов. Для этого можно было использовать, например, Carbonite Migrate, но мы взвесили все за и против и нашли более подходящее решение для миграции из облака в облако Hystax Acura.

Как работает Hystax Acura: теория


Этот инструмент работает с VMware, KVM и Hyper-V, поддерживает перенос с физических серверов и при этом не дорог. Так что мы используем его для автоматической миграции в Облако КРОК, бекапов, резервирования и аварийного восстановления инфраструктуры заказчиков. Во всех этих случаях архитектура и принцип работы Hystax Acura одинаковы.
С одной стороны, у нас есть source-окружение, исходное облако или физический сервер, где запущена инфраструктура пользователя. С другой стороны находится Облако КРОК. И стоит задача перенести данные с source на target.
image
На стороне source-окружения устанавливается один из трех репликационных агентов.
Два из них предназначены для Linux или Windows соответственно. Они устанавливаются внутрь виртуальной машины и работают как сервисы на уровне операционной системы. Третий предназначен специально для миграции с VMware. Он развертывается на уровне ESXi-хоста и позволяет реплицировать сразу все находящиеся вокруг виртуальные машины.
Репликационные агенты отправляют данные на target-облако на уровне блоков. Причем репликация происходит в фоновом режиме, без необходимости останавливать виртуальные машины в source-облаке.
image
Ресивер-сервис Hystax Acura принимает данные и также поблочно записывает в их свежесозданный том в Облаке КРОК. По окончании репликации мы снимаем снапшот. Эти снимки образуют цепочку ресторпойнтов.Консистентность данных для Windows-машин обеспечивает стандартная служба VSS (Volume Shadow Copy Service). Для Linux-агента разработчики Hystax выпустили собственный проприетарный драйвер, который дублирует функциональность VSS. Что касается VMware там используется СBT API.
После создания снапшота остается лишь подать команду на запуск реплицированной виртуальной машины на новом месте.
image
Прямо перед поднятием машины на target-облаке Acura проводит автоматическое V2V-преобразование. Это одна из основных вещей, которые необходимо сделать, чтобы машина завелась при смене гипервизора.

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

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

Ограничения Hystax Acura


Такой подход позволяет мигрировать в Облако КРОК практически с любой технологии, но у Hystax Acura есть и ограничения.
image
Так, в случае Linux работоспособность репликации и P2V/V2V преобразования зависит от версии ядра.

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

Миграция с Hystax Acura на практике


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

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

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

При следующих, инкрементальных репликациях Acura отсылает только дельты, изменения на виртуальных машинах, и репликация проходит намного быстрее. Поэтому RTO (recovery time objective), время, которое система будет недоступна во время переезда, фактически равняется скорости загрузки машин на target-стороне.

image

Скриншот DR-плана (json)
Перед окончательным переездом мы проводим тестовый запуск и замеряем, сколько времени нужно на загрузку. Так мы определяем, на какое время необходимо остановить сервисы на source-стороне, чтобы не потерять ни байта данных. Около десяти минут даунтайма, и гигантская инфраструктура готова принимать трафик в нашем облаке.

Что касается аварийного восстановления, то, по нашим подсчетам, RPO (recovery point objective время, за которое можно потерять данные), составляет три минуты для виртуалок на Linux и пять минут для Windows. Это впечатляющие показатели, и теперь вы знаете, какие технические решения позволяют добиться их на практике.

Если у вас остались вопросы по работе Hystax Acura и Облака КРОК в целом, не стесняйтесь оставлять их в комментариях или присылайте на почту: RSayfutdinov@croc.ru
Подробнее..

Kafkaрианский переезд или как расшевелить партиции

06.02.2021 12:22:42 | Автор: admin

Вступление

Привет, Хабр! Работаю java программистом в одной финансовой организации. Решил оставить свой след на хабре и написать первую свою статью. В силу проблем с наличием девопсеров передо мной была поставлена задача обновить кластер кафки с 2.0 до 2.6 без даунтайма и потери сообщений (сами понимаете, никто не любит, когда денежки зависают в воздухе или где-то теряются). Хочу поделиться этим опытом с Вами и получить конструктивный фидбек. Итак, хватит воды, переходим сразу к делу.

Схема миграции

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

Ниже представлена схема миграции. Имеем 3 брокеров на старых ВМ, 3 брокеров на новых ВМ, также для каждого брокера имеется свой zookeeper.

План миграции

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

  1. Добавить в настройки приложения адреса новых брокеров

  2. Пробить доступы между всеми и вся

  3. Подготовить инфраструктуру на новых виртуалках

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

  5. Поднять новых брокеров кафки

  6. Мигрировать все партиции со старых брокеров на новые

  7. Отключить старые брокеры кафки и старых зукиперов

  8. Убрать из новых конфигов старых брокеров и зукиперов

А теперь поехали по порядку разбирать подробнее каждый пункт

Добавление брокеров в приложение

Самый простой пункт. В коде клиента, там где были прописаны адреса старых брокеров, туда же дописываем новых. В свойство "bootstrap.servers"

Строка настройки

old-server1:9092,old-server2:9092,old-server3:9092,new-server4:9092,new-server5:9092,new-server6:9092

Пробитие доступов

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

  1. Как вы уже догадались по строке настройки брокеров, нам нужен порт 9092 в направлении Приложение -> новые брокеры кафки (в сумме 3 доступа)

  2. Этот же порт для Новые брокеры <----> Старые брокеры (+18 доступов)

  3. Все тот же порт 9092 между каждым новым брокером (+6 доступов)

  4. Аналогичные доступы из пунктов 2 и 3 для портов зукиперов: 2181, 2888, 3888 ( (18+6)*3 = 72)

Итого: 99 доступов. Получите и распишитесь! А потом еще проверьте все.

обидно что до 100 не дотянули, было бы посолиднее

Когда мы прошли через эти страдания и настроили доступы, приступаем к настройке инфраструктуры.

Инфраструктура нового кластера

Для начала нам нужно создать пользователя kafka на новых виртуалках

Далее очень важный пункт - необходимо прописать разрешенное количество открытых файлов пользователю kafka. А именно, в файле /etc/security/limits.conf дописываем строки

kafka hard nofile 262144kafka soft nofile 262144

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

Честно говоря, число 262144, взято просто потому, что на старом кластере было установлено такое значение (исторически сложилось). Хотя у нас кафка открывает не больше 10 тысяч файлов, поэтому вы можете провести анализ и выставить более подходящее.

Далее нам надо прописать переменную среды к java

в файл /home/kafka/.bash_profile дописываем

export JAVA_HOME=/opt/javaexport PATH=JAVA_HOME/bin:PATH

Соответственно у вас должна лежать jre в папке /opt/java

Далее раскидываем папки по нужным директориям и устанавливаем права

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

setup.sh
tar -xf ../jdk1.8.0_181.tar.gz -C /opt/mv /home/kafka/kafka /optmv /home/kafka/zookeeper /optmv /home/kafka/kafka-start.sh /optmv /home/kafka/scripts /optln -sfn /opt/kafka/kafka_2.13-2.6.0 /opt/kafka/currentln -sfn /opt/zookeeper/zookeeper-3.6.2 /opt/zookeeper/currentln -sfn /opt/jdk1.8.0_181/ /opt/javachown -R kafka:kafka /optchmod -R 700 /opt#env_var start ------------------------------->kafkaProfile=/home/kafka/.bash_profilehomeVar="export JAVA_HOME=/opt/java"javaHome=$(cat $kafkaProfile | grep "$homeVar")if [ "$javaHome" != "$homeVar" ]; then    echo -e "\n$homeVar\n" >> $kafkaProfilefipathVar="export PATH=\$JAVA_HOME/bin:\$PATH"path=$(cat $kafkaProfile | grep "$pathVar")if [ "$path" != "$pathVar" ]; then    echo -e "\n$pathVar\n" >> $kafkaProfilefi#env_var end --------------------------------->#ulimit start >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>limitsFile=/etc/security/limits.confsoft="kafka soft nofile 262144"limitSoft=$(cat $limitsFile | grep "$soft")if [ "$limitSoft" != "$soft" ]; then    echo -e "\n$soft\n" >> $limitsFilefihard="kafka hard nofile 262144"limitHard=$(cat $limitsFile | grep "$hard")if [ "$limitHard" != "$hard" ]; then    echo -e "\n$hard\n" >> $limitsFilefi#ulimit end >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Далее из под пользователя kafka проверяем что у нас все установлено правильно

ulimit -n  = 262144echo $JAVA_HOME  = /opt/javaecho $PATH  должен содержать /opt/java/bin

Новый кластер зукиперов

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

ниже кусок файла конфигурации зукипера /opt/zookeeper/current/conf/zoo.cfg

zoo.cfg
dataDir=/opt/zookeeper/zookeeper-dataserver.1=server1:2888:3888server.2=server2:2888:3888server.3=server3:2888:3888server.4=server4:2888:3888server.5=server5:2888:3888server.6=server6:2888:3888

Стартуем все зукиперы /opt/zookeeper/current/bin/zkServer.sh start

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

zk-add-new-servers.sh
echo -e "\nserver.4=server4:2888:3888" >> /opt/zookeeper/zookeeper-3.4.13/conf/zoo.cfgecho -e "\nserver.5=server5:2888:3888" >> /opt/zookeeper/zookeeper-3.4.13/conf/zoo.cfgecho -e "\nserver.6=server6:2888:3888" >> /opt/zookeeper/zookeeper-3.4.13/conf/zoo.cfg

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

/opt/zookeeper/zookeeper-3.4.13/bin/zkServer.sh restart

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

.../zkServer.sh status

Настройка ансамбля зукиперов завершена.

Поднятие новых брокеров

Теперь можно заняться запуском новых брокеров, но перед этим нужно убедиться что в server.properties у нас проставлен broker.id и zookeeper.connect

Пример для 4 брокера

broker.id=4zookeeper.connect=server4:2181,server5:2181,server6:2181/cluster_name

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

Также необходимо там же в server.properties прописать текущую версию протокола взаимодействия между брокерами (2.0.0). Иначе мы не сможем переместить партиции.

inter.broker.protocol.version=2.0.0

Запускаем новых брокеров

nohup /opt/kafka/current/bin/kafka-server-start.sh /opt/kafka/config/server.properties > log.log 2>&1 &

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

/opt/zookeeper/current/bin/zkCli.sh

и выполняем там команду

ls /cluster_name/brokers/ids

Должен быть выведен такой ответ

[1,2,3,4,5,6]

Это значит что в кластере у нас все 6 штук брокеров, чего мы и добивались.

Перемещение партиций

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

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

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

{"topics": [{"topic": "foo1"},              {"topic": "foo2"}],  "version":1 }

Так как топиков у нас было много, я сгенерил несколько таких файлов

Далее все эти json файлы надо скормить утилите kafka-reassign-partitions.sh с аргументом --generate и указать id брокеров куда мы хотим переехать --broker-list "4,5,6".

У нас получится несколько файлов такого вида

generate1.json
{"version":1,  "partitions":[{"topic":"foo1","partition":2,"replicas":[1,2,3]},                {"topic":"foo1","partition":0,"replicas":[3,2,1]},                {"topic":"foo2","partition":2,"replicas":[1,2,3]},                {"topic":"foo2","partition":0,"replicas":[3,2,1]},                {"topic":"foo1","partition":1,"replicas":[2,3,1]},                {"topic":"foo2","partition":1,"replicas":[2,3,1]}]  }  Proposed partition reassignment configuration  {"version":1,  "partitions":[{"topic":"foo1","partition":2,"replicas":[5,4,6]},                {"topic":"foo1","partition":0,"replicas":[4,5,6]},                {"topic":"foo2","partition":2,"replicas":[6,4,5]},                {"topic":"foo2","partition":0,"replicas":[4,5,6]},                {"topic":"foo1","partition":1,"replicas":[5,4,6]},                {"topic":"foo2","partition":1,"replicas":[4,5,6]}]  }

Все что идет до Proposed partition reassignment configuration это текущее распределение партиций (может пригодиться для роллбека). Все что после - распределение после переезда. Соответственно нам надо собрать все эти нижние половины со всех файлов, сформировать новые json.

Так как все это делать руками ну ооочень долго. я "быстро" накатал скрипт, который вызывает все эти команды и режет файлы как нам надо. За резку файлов отвечает джарник kafka-reassign-helper.jar код можно глянуть тут.

Ниже скрипт подготовки файлов для переезда партиций

prepare-for-reassignment.sh
#параметр отвечает за количество топиков в одном файле. у нас было 20if [ "$#" -eq 0 ]then    echo "no arguments"    exit 1fiecho "Start reassignment preparing"/opt/kafka/current/bin/kafka-topics.sh --list --zookeeper localhost:2181/cluster_name >> topics.txtecho created topics.txtjava -jar kafka-reassign-helper.jar generate topics.txt $1fileCount=$(ls -dq generate*.json | wc -l)echo "created $fileCount file for topics to move"echo -e "\nCreating generated files\n"mkdir -p generatedfor ((i = 1; i < $fileCount+1; i++ ))do/opt/kafka/current/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/cluster_name --broker-list "4,5,6" --topics-to-move-json-file "generate$i.json" --generate >> "generated/generated$i.txt"echo "generated/generated$i.txt" createddoneecho -e "\nCreating execute/rollback files"java -jar kafka-reassign-helper.jar execute $fileCountecho -e "\nexecute/rollback files created"echo -e "\nPreparing finished successfully!"

После выполнения скрипта скармливаем сгенеренные файлы по очереди утилите kafka-reassign-partitions.sh, но на этот раз с параметром --execute

move-partitions.sh
#параметр отвечает за номер файла execute1.json, execute2.json ....if [ "$#" -eq 0 ]then    echo "no arguments"    exit 1fi        /opt/kafka/current/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/cluster_name --reassignment-json-file "execute/execute$1.json" --execute

Вот теперь кафка начнет перемещать партиции. В логе новых брокеров судорожно забегают строки, как и в логах старых. Пока все это происходит, чтобы не скучать, нам предоставили третий аргумент kafka-reassign-partitions.sh, а именно --verify, который предоставляет узнать текущий статус переезда.

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

reassign-verify.sh
progress=-1while [ $progress != 0 ]do    progress=$(/opt/kafka/current/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/cluster_name --reassignment-json-file execute/execute$1.json --verify | grep "in progress" -c)    complete=$(/opt/kafka/current/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/cluster_name --reassignment-json-file execute/execute$1.json --verify | grep "is complete" -c)    failed=$(/opt/kafka/current/bin/kafka-reassign-partitions.sh --zookeeper localhost:2181/cluster_name --reassignment-json-file execute/execute$1.json --verify | grep "failed" -c)    echo "In progress:" $progress;    echo "Is complete:" $complete;    echo "Failed:" $failed;    sleep 2sdone

Вызываем и ждем пока In progress станет равным 0. а Is complete примет какое-то конечное значение. И надеемся что Failed так и останется 0.

Когда все закончилось вызываем снова move-partitions.sh и reassign-verify.sh для следующего файла, до тех пор пока не переберем все наши файлы миграции.

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

Отключение старого кластера

Тут все просто. выполняем kafka-server-stop.sh и zkStop.sh для брокеров и зукиперов соответственно.

Чистим конфиги нового кластера

Начинаем с зукиперов. Из файлов zoo.cfg удаляем лишние записи

zk-remove-old-servers.sh
sed -i '/server.1/d' /opt/zookeeper/current/conf/zoo.cfgsed -i '/server.2/d' /opt/zookeeper/current/conf/zoo.cfgsed -i '/server.3/d' /opt/zookeeper/current/conf/zoo.cfg

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

Удаляем из server.properties брокеров старый протокол общения

remove-old-protocol.sh
sed -i '/inter.broker.protocol.version=2.0.0/d' /opt/kafka/config/server.properties

Теперь вроде как можно перезапустить брокеров, но надо убедиться, что все партиции находятся в состоянии insync, причем не в количестве min.insync.replicas (у нас равно 2), а в количестве default.replication.factor (у нас 3)

Иначе если где-то будет 2 реплики, а не 3, то при перезапуске брокера, на котором лежит одна из этих двух реплик, мы получим ошибку на клиенте, что не хватает insync реплик.

Ниже скриптик для проверки

check-insync.sh
input="check-topics.txt"rm -f $input/opt/kafka/current/bin/kafka-topics.sh --list --zookeeper localhost:2181/cluster_name >> check-topics.txtcheckPerIter=100i=0list=""notInsync=0while IFS= read -r linedo ((i=i+1)) list+="${line}|" if [ $i -eq $checkPerIter ]  then   list=${list::${#list}-1}   echo "checking $list"   count=$(/opt/kafka/current/bin/kafka-topics.sh --describe --topic $list --zookeeper localhost:2181/cluster_name | egrep "Isr: [4-6/,]{3}$" -c)   if [ "$count" -ne 0 ]    then     /opt/kafka/current/bin/kafka-topics.sh --describe --topic $list --zookeeper localhost:2181/cluster_name | egrep "Isr: [4-6/,]{3}$"   fi   ((notInsync=notInsync+count))   list=""   i=0 fidone < "$input"echo "not insync: $notInsync"

Если на выходе получаем not insync: 0, то можно по очереди перезапустить брокеров.

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

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

README.txt
Инструкция по миграции кафка 2.0.0 -> 2.6.01 этап. Подготовка инфраструктуры1.1 Скопировать архивы для миграции на сервера кафкиNEW4.tar.gz -> адрес 4 брокераmigration.tar.gz -> адрес 4 брокераNEW5.tar.gz -> адрес 5 брокераNEW6.tar.gz -> адрес 6 брокера1.2 Разархивировать архивыtar -xf NEW4.tar.gz -C /home/kafkatar -xf NEW5.tar.gz -C /home/kafkatar -xf NEW6.tar.gz -C /home/kafka1.3 От пользователя root выполнить скрипт на каждом новом сервере (установка файлов в нужные директории, прав и лимитов)/home/kafka/scripts/setup.sh1.4 Перейти на пользователя kafka (на каждом новом сервере)1.5 Проверить следующие параметры среды (на каждом новом сервере)ulimit -n  = 262144echo $JAVA_HOME  = /opt/javaecho $PATH  должен содержать /opt/java/binсодержимое папки /opt должно принадлежать kafka kafka2 этап. Запуск зукиперов и брокеров2.1 Перейти под пользователя kafka (на каждом новом сервере)2.2 Выполнить скрипт на каждом новом сервере (запускаются новые зукиперы)/opt/scripts/zkStart.sh2.3 Скопировать архивы на старые сервера кафки OLD1.tar.gz -> адрес 1 брокераOLD2.tar.gz -> адрес 2 брокераOLD3.tar.gz -> адрес 3 брокера2.4 Разархивировать архивыtar -xf OLD1.tar.gz -C /home/kafkatar -xf OLD2.tar.gz -C /home/kafkatar -xf OLD3.tar.gz -C /home/kafka2.5 От пользователя root выполнить скрипт на каждом старом сервере (установка прав на скрипты миграции)/home/kafka/scripts/setup-old.sh2.6 Перейти под пользователя kafka на каждом старом сервере2.7 Выполнить скрипт на каждом старом сервере (добавляем новые зукиперы в конфиг старых зукиперов)/home/kafka/scripts/zk-add-new-servers.sh2.8 Выполнить скрипт на каждом старом сервере (проверка статуса зукиперов)/home/kafka/scripts/zkStatus.shопределить какой из старых серверов является лидером2.9 Перезапустить по очереди зукиперы на старых серверахлидера перезапускать в последнюю очередь/home/kafka/scripts/zkRestart.sh2.10 Проверить что кластер зукиперов подхватил новые серверадля этого на всех серверах выполнить/home/kafka/scripts/zkStatus.sh (на старых)/opt/scripts/zkStatus.sh (на новых)все сервера кроме одного должны быть в состоянии followerодин сервер leader2.11 запуск новых брокеровна новых серверах выполнить скрипт/opt/kafka-start.sh2.12 проверить что новые брокеры в кластеревыполнить на новом сервере/home/kafka/migration/zkCli.shls /cluster_name/brokers/idsдолжно быть выведено [1,2,3,4,5,6]3 этап Перемещение партиций на новых брокеров3.1 Выполнить скрипт (подготовка файлов для перемещения партиций)/home/kafka/migration/prepare-for-assignment.sh  203.2 Выполнить данный пункт по очереди для каждого файла в директории /home/kafka/migration/executeПример:для файла execute1.json/home/kafka/migration/move-partitions.sh 1после начала выполнения скрипта выполнить скрипт Пример:/home/kafka/migration/reassign-verify.sh 1когда количество партиций в состоянии "in progress" достигнет 0 дождаться ОК от программиста и приступить к п.3.2 для следующего файла3.3 После завершения перемещения партиций производится проверка логов на наличие ошибок, а также проверка директории /opt/kafka/kafka-data на всех старых брокерахДолжны остаться только технические логи кафки3.4 По очереди выключить на старых серверах брокеров, выполнив скрипт/opt/kafka/current/bin/kafka-server-stop.sh3.5 По очереди выключить зукиперов на старых серверах, выполнив скрипт/home/kafka/scripts/zkStop.sh3.6 На новых серверах выполнить скрипт (удаляем старых зукиперов из конфига новых зукиперов)/opt/scripts/zk-remove-old-servers.sh3.7 На новых серверах перезапустить зукиперов/opt/scripts/zkRestart.sh3.8 На новых серверах проверить статус зукиперов (один лидер, 2 фоловера)/opt/scripts/zkStatus.sh3.9 На новых серверах выполнить скрипт (удаляем из конфига старый протокол общения между брокерами)/opt/scripts/remove-old-protocol.sh3.10 Проверить что все реплики в статусе insyncдля этого запустить скрипт /home/kafka/migration/check-insync.shnot insync должно быть равно 03.11 По очереди для каждого нового брокера выполнить/opt/kafka/current/bin/kafka-server-stop.shдождаться пока брокер остановиться (ps aux | grep kafka должен показывать только зукипера)выполнить /opt/kafka-start.shПриступить к 3.10 следующего брокераМиграция завершена!

Надеюсь, что кому-то помог в этом вопросе. Жду ваших комментов и замечаний.

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

Подробнее..

Перевод Как использовать GraphQL Federation для инкрементальной миграции с монолита (Python) на микросервисы (Go)

26.05.2021 16:17:30 | Автор: admin
Или как поменять фундамент старого дома, чтобы он не обвалился



Лет 10 назад мы выбрали 2-ю версию Python для разработки нашей обучающей платформы с монолитной архитектурой. Но с тех пор индустрия существенно изменилась. Python 2 был официально похоронен 1 января 2020 года. В предыдущей статье мы объясняли, почему решили отказаться от миграции на Python 3.

Каждый месяц нашей платформой пользуются миллионы людей.

Мы пошли на определённый риск, когда решили переписать наш бэкенд на Go и изменить архитектуру.

Язык Go мы выбрали по нескольким причинам:

  1. Высокая скорость компиляции.
  2. Экономия оперативной памяти.
  3. Достаточно широкий выбор IDE с поддержкой Go.

Но мы применили подход, который позволил минимизировать риск.

GraphQL Federation

Мы решили построить нашу новую архитектуру вокруг GraphQL Apollo Federation. GraphQL был создан разработчиками Facebook как альтернатива REST API. Федерация это построение единого шлюза для нескольких сервисов. Каждый сервис может иметь свою GraphQL-схему. Общий шлюз объединяет их схемы, генерирует единое API и позволяет выполнять запросы для нескольких сервисов одновременно.

Прежде чем, пойдём дальше, хотелось бы особо отметить следующее:

  1. В отличие от REST API, у каждого GraphQL-сервера есть собственная типизированная схема данных. Она позволяет получить любые комбинации именно тех данных с произвольными полями, которые вам нужны.

  2. Шлюз REST API позволяет отправить запрос только одному бэкенд-сервису; шлюз GraphQL генерирует план запросов для произвольного количества бэкенд-сервисов и позволяет вернуть выборки из них в одном общем ответе.

Итак, включив шлюз GraphQL в нашу систему, получим примерно такую картину:


URL картинки: https://lh6.googleusercontent.com/6GBj9z5WVnQnhqI19oNTRncw0LYDJM4U7FpWeGxVMaZlP46IAIcKfYZKTtHcl-bDFomedAoxSa9pFo6pdhL2daxyWNX2ZKVQIgqIIBWHxnXEouzcQhO9_mdf1tODwtti5OEOOFeb

Шлюз (он же сервис graphql-gateway) отвечает за создание плана запросов и отправки GraphQL-запросов другим нашим сервисам не только монолиту. Наши сервисы, написанные на Go, имеют свои собственные GraphQL-схемы. Для формирования ответов на запросы мы используем gqlgen (это GraphQL-библиотека для Go).

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

Далее пойдёт речь о том, как мы кастомизировали сервер Apollo GraphQL, чтобы безопасно перелезть с нашего монолита (Python) на микросервисную архитектуру (Go).

Side-by-side тестирование


GraphQL мыслит наборами объектов и полей определённых типов. Код, который знает, что делать с входящим запросом, как и какие данные извлечь из полей, называется распознавателем (resolver).

Рассмотрим процесс миграции на примере типа данных для assignments:

123 type Assignment {createdDate: Time.}

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

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

Шаг 1: Режим manual


Когда пользователь запрашивает значение поля createdDate, наш GraphQL-шлюз обращается сначала к монолиту (который, напоминаю, написан на Python).


На первом шаге нам нужно обеспечить возможность добавления поля в новый сервис assignments, уже написанный на Go. В файле с расширением .graphql должен лежать следующий код распознавателя (resolver):

12345 extend type Assignment key(fields: id) {id: ID! externalcreatedDate: Time @migrate(from: python, state: manual)}

Здесь мы используем Федерацию, чтобы сказать, что сервис добавляет поле createdDate к типу Assignment. Доступ к полю происходит по id. Мы также добавляем секретный ингредиент директиву migrate. Мы написали код, который понимает эти директивы и создаёт несколько схем, которые GraphQL-шлюз будет использовать при принятии решения о маршрутизации запроса.

В режиме manual запрос будет адресован только коду монолита. Мы должны предусмотреть эту возможность при разработке нового сервиса. Чтобы получить значение поля createdDate, мы по-прежнему можем обращаться к монолиту напрямую (в режиме primary), а можем запрашивать у GraphQL-шлюза схему в режиме manual. Оба варианта должны работать.

Шаг 2: Режим side-by-side


После того, как мы написали код распознавателя (resolver) для поля createdDate, мы переключаем его в режим side-by-side:

12345 extend type Assignment key(fields: id) {id: ID! externalcreatedDate: Time @migrate(from: python, state: side-by-side)}

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

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

В процессе тестирования мы получаем вот такие отчёты.


Эту картинку при вёрстке попытайся увеличить как-то без сильной потери качества.

В них акцент сделан на случаи, когда в работе монолита и нового сервиса обнаруживаются расхождения.

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

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

А что по мутациям?


Возможно, у вас возник вопрос: если мы запускаем одинаковую логику и в Python, и в Go, что произойдет с кодом, который изменяет данные, а не просто запрашивает их? В терминах GraphQL это называется мутациями (mutation).

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

Шаг 2.5: Режим сanary


Если у нас есть поле или мутация, которые успешно дожили до стадии продакшна, мы включаем режим canary (канареечный деплой).

12345 extend type Assignment key(fields: id) {id: ID! externalcreatedDate: Time @migrate(from: python, state: canary)}

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

Мы используем только одну канареечную схему за раз. На практике не так много полей и мутаций одновременно находятся в канареечном режиме. Так что, я думаю, проблем не будет и дальше. Это хороший компромисс, потому что схема довольно велика (более 5000 полей), а экземпляры шлюза должны хранить в памяти три схемы primary, manual и canary.

Шаг 3: Режим migrated


На этом шаге поле createdDate должно перейти в режим migrated:

12345 extend type Assignment key(fields: id) {id: ID! externalcreatedDate: Time @migrate(from: python, state: migrated)}

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

Шаг 4: Завершение миграции


После успешного деплоя нам больше не нужен код монолита для этого поля, и мы удаляем из кода распознавателя (resolver) директиву @migrate:

12345 extend type Assignment key(fields: id) {id: ID! externalcreatedDate: Time}

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

Вот такая она инкрементальная миграция!

И как далеко шагнули мы?


Мы завершили работу над нашей инфраструктурой side-by-side тестирования только в этом году. Это позволило нам безопасно, медленно, но верно переписать кучу кода на Go. В течение года мы поддерживали высокую доступность платформы на фоне роста объёма трафика в нашей системе. На момент написания этой статьи ~ 40% наших полей GraphQL вынесены в сервисы Go. Так что, описанный нами подход хорошо зарекомендовал себя в процессе миграции.

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

P.S. Стив Коффман делал доклад на эту тему (на Google Open Source Live). Вы можете посмотреть запись этого выступления на YouTube (или просто глянуть презентацию).



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Физика в мире животных как акулы ориентируются при помощи магнитного поля Земли

08.05.2021 18:17:24 | Автор: admin

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

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

Десятилетиями ученые обсуждали теорию, согласно которой акулы чувствуют магнитное поле Земли, используя его для ориентирования в пространстве. Но акула не голубь, практически подтвердить теорию достаточно сложно. Белые акулы вырастают до огромных размеров до 6 метров в длину и массой до тонны или даже больше. Сложно себе представить лабораторию, в которой можно создать водоем с этими огромными рыбами и что-то там тестировать. Но эту проблему все же удалось решить, результаты исследования опубликованы в Current Biology.

Для того, чтобы доказать гипотезу, Брайан Келлер, исследователь из Университета штата Флорида (англ. Florida State University, FSU), создал систему, способную создавать магнитные поля разной конфигурации. Он построил 3-метровый куб из дерева с большим резервуаром в центре. Также он намотал на куб около 2 км медной проволоки. При подключении к источнику питания эта обмотка генерирует магнитное поле. Регулируя подачу тока, Келлер получит возможность генерировать магнитное поле разной конфигурации, имитируя определенные условия, с которыми акулы могут столкнуться в океане. При помощи своей системы Келлер решил проверить, будет ли акула, помещенная в резервуар с водой, реагировать на изменение магнитного поля.

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

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

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

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


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

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

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

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

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

Подробнее..

Миграция с Zimbra OSE 8.8.15 на Zimbra 9 Open Source от Zextras

11.08.2020 14:11:07 | Автор: admin
После того как компания Zextras опубликовала собственные сборки Zimbra Collaboration Open-Source Edition 9, многие администраторы решили обновить свои почтовые серверы до новой версии и обратились в техническую поддержку Zextras с вопросом о том, как это можно сделать, не ставя под угрозу работоспособность одной из ключевых систем предприятия.

Перейти на Zimbra OSE 9 от Zextras можно двумя способами. Первый, он же самый простой и быстрый обновление Zimbra 8.8.15 OSE на сервере до новой версии. Минусов у такого подхода ровно два. Первый вам потребуется достаточно длительный технический перерыв для осуществления обновления, второй в случае, если что-то пойдет не по плану, вы рискуете остаться без работоспособной системы и можете потратить массу времени на то, чтобы она вновь начала функционировать.Вторым способом перехода на Zimbra OSE 9 является миграция с сервера на базе Zimbra OSE 8.8.15 на сервер с Zimbra OSE 9. Этот подход немного более сложен в исполнении, однако при этом не требует длительного технического перерыва, а в случае возникновения проблем на одном сервере, у вас под рукой всегда будет другой сервер с полностью работоспособной Zimbra OSE.

image

Для того, чтобы выполнить обновление, необходимо скачать дистрибутив Zimbra 9 OSE с сайта Zextras и запустить установщик, который самостоятельно обнаружит установленную Zimbra OSE 8.8.15 и предложит обновить почтовый сервер до новой версии. Процесс обновления схож с процессом установки Zimbra OSE 9, который детально описан в нашей предыдущей статье.

Процесс миграции мы рассмотрим на примере домена company.ru. Zimbra OSE 8.8.15 работает на узле mail.company.ru, а Zimbra OSE 9 будет установлена на узле zimbra9.company.ru. При этом MX-запись в DNS указывает именно на узел mail.company.ru. Нашей задачей будет перенести учетные записи сотрудников предприятия с почтовой системы на узлеmail.company.ru в систему, развернутую на узле zimbra9.company.ru.



Первым шагом на пути к её выполнению будет создание резервной копии на одном сервере и ее развертывание на другом. Выполняется эта задача с помощью расширения Zextras Backup, входящий в состав Zextras Suite Pro. Обращаем ваше внимание на то, что для успешного переноса резервной копии, на обоих серверах должна быть установлена одинаковая версия Zextras Suite Pro. Также обращаем ваше внимание на то, что минимальной версией, совместимой с Zimbra OSE 9 является Zextras Suite Pro 3.1, поэтому не стоит пытаться выполнить перенос данных с версией ниже, чем обозначенная.



Для выполнения миграции рекомендуется использовать внешний жесткий диск или сетевое запоминающее устройство, смонтированное в папку /opt/zimbra/backup/zextras/, в которую по умолчанию сохраняется резервная копия почтового сервера. Это делается для того, чтобы создание резервной копии не создавало дополнительную нагрузку на работающую систему.



Начнем миграцию с того, что отключим на обоих серверах функцию сканирования в реальном времени при помощи команды zxsuite backup setProperty ZxBackup_RealTimeScanner false. Затем на исходном сервере запустим SmartScan при помощи команды zxsuite backup doSmartScan. Благодаря этому все наши данные экспортируются в папку /opt/zimbra/backup/zextras/, то есть окажутся на внешнем носителе. После окончания операции смонтируйте носитель на целевом сервере. Также, если скорость внутренней сети позволяет, можно использовать для переноса резервной копии утилиту rsync.

После этого можно приступать к развертыванию резервной копии на целевой инфраструктуре. Делается это при помощи команды zxsuite backup doExternalRestore /opt/zimbra/backup/zextras/. По окончании развертываниявы получите работоспособную копию старого сервера, который можно вводить в работу. для этого нужно сразу внести изменения в MX-запись DNS-сервера и переключить поток писем на целевую инфраструктуру. Кроме того, необходимо внести изменения в имя хоста и DNS-запись узла zimbra9.company.ru, чтобы пользователи, входя в веб-клиент, попадали именно в Zimbra OSE 9.



Однако работа ещё не завершена. Дело в том, что письма, которые пришли после окончания резервного копирования и до переключения потока писем на новый сервер, пока что хранятся в Zimbra OSE 8.8.15, поэтому сразу после того как письма перестанут приходить на сервер с Zimbra OSE 8.8.15, необходимо будет снова сделать его резервную копию. Благодаря Смарт-сканированию, в нее попадут только те данные, которые отсутствовали в предыдущей резервной копии. Поэтому процесс переноса свежих данных не будет длиться долго.



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

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

По всем вопросам, связанными c Zextras Suite вы можете обратиться к Представителю компании Zextras Екатерине Триандафилиди по электронной почте katerina@zextras.com
Подробнее..

Переход с Azure на GCP, с ASP.NET MVC на ASP.NET Core 3.1

26.01.2021 20:09:19 | Автор: admin

Автор: Андрей Жуков, .NET Team Leader, DataArt

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

Задача, поставленная заказчиком: Azure -> GCP

Заказчик решил перейти из одного облака (Azure) в другое (Google Cloud Platform). В некотором отдаленном будущем вообще планировалось перевести серверную часть на Node.js и развивать систему силами команды full-stack typescript-разработчиков. На момент моего входа в проект там существовала пара ASP.NET MVC приложений, которым решили продлить жизнь. Их мне и предстояло перенести в GCP.

Начальная состояние, факторы, мешающие сразу перейти на GCP

Первоначально имелось два ASP.NET MVC-приложения, которые взаимодействовали с одной общей MS SQL базой данных. Они были развернуты на Azure App Services.

Первое приложение назовем его Web Portal имело пользовательский интерфейс, построенный на базе Razor, TypeScript, JavaScript, Knockout и Bootstrap. С этими клиентскими технологиями никаких проблем не предвиделось. Зато серверная часть приложения использовала несколько сервисов, специфичных для Azure: Azure Service Bus, Azure Blobs, Azure Tables storage, Azure Queue storage. С ними предстояло что-то делать, т. к. в GCP ни один из них не поддерживается. Кроме того, приложение использовало Azure Cache for Redis. Для обработки длительных запросов была задействована служба Azure WebJob, задачи которой передавались через Azure Service Bus. По словам программиста, занимавшегося поддержкой, фоновые задачи могли выполняться до получаса.

Изначально архитектура Web Portal в нашем проекте выглядела такИзначально архитектура Web Portal в нашем проекте выглядела так

Azure WebJobs тоже предстояло чем-то заменить. Архитектура с очередью заданий для длительных вычислений не единственное среди возможных решений можно использовать специализированные библиотеки для фоновых задач, например, Hangfire, или обратиться к IHostedService от Microsoft.

Второе приложение назовем его Web API представляло собой ASP.NET WEB API. Оно использовало только MS SQL базы данных. Вернее, в конфигурационном файле были ссылки на несколько баз данных, в реальности же приложение обращалось только к одной их них. Но об этом нюансе мне только предстояло узнать.

Оба приложения были в работающем, но плохом состоянии: отсутствовала архитектура как таковая, было много старого неиспользуемого кода, не соблюдались принципы построения ASP.NET MVC приложений и т. д. Заказчик и сам признавал низкое качество кода, а человек, изначально написавший приложения, уже несколько лет не работал в компании. Был дан зеленый свет любым изменениям и новым решениям.

Итак, нужно было перевести ASP.NET MVC приложения на ASP.NET Core 3.1, перевести WebJob c .NET Framework на .NET Core, чтобы можно было разворачивать их под Linux. Использовать Windows на GCP возможно, но не целесообразно. Надо было избавиться от сервисов, специфичных для Azure, заменить чем-то Azure WebJob, решить, как будем развертывать приложения в GCP, т. е. выбрать альтернативу Azure App Services. Требовалось добавить поддержку Docker. При этом неплохо было бы внести хоть какую-то архитектуру и поправить качество кода.

Общие принципы и соображения

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

В конце каждого этапа приложение должно находиться в стабильном состоянии, т. е. пройти хотя бы Smoke tests.

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

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

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

План работ

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

  1. Web Portal c ASP.NET MVC на ASP.NET Core

    1.1. Анализ кода и зависимостей Web Portal от сервисов Azure и сторонних библиотек, оценка необходимого времени.

    1.2. Перевод Web Portal на .NET Core.

    1.3. Рефакторинг с целью устранения основных проблем.

    1.4. Merge изменений Web Portal из основной ветки репозитория, сделанных параллельно другими разработчиками.

    1.5. Докеризация Web Portal.

    1.6. Тестирование Web Portal, устранение ошибок и развертывание новой версии на Azure.

  2. Web API c ASP.NET MVC на ASP.NET Core

    2.1. Написание E2E автоматических тестов для Web API.

    2.2. Анализ кода и зависимостей Web API от сервисов Azure и сторонних библиотек, оценка необходимого времени.

    2.3. Удаление неиспользуемого исходного кода из Web API.

    2.4. Перевод Web API на .NET Core.

    2.5. Рефакторинг Web API с целью устранения основных проблем.

    2.6. Merge изменений Web API из основной ветки репозитория, сделанных параллельно другими разработчиками.

    2.7. Докеризация Web API.

    2.8. Тестирование Web API, устранение ошибок и развертывание новой версии на Azure.

  3. Устранение зависимостей от Azure

    3.1. Устранение зависимостей Web Portal от Azure.

  4. Развертывание в GCP

    4.1. Развертывание Web Portal в тестовой среде в GCP.

    4.2. Тестирование Web Portal и устранение возможных ошибок.

    4.3. Миграция базы данных для тестовой среды.

    4.4. Развертывание Web API в тестовой среде в GCP.

    4.5. Тестирование Web API и устранение возможных ошибок.

    4.6. Миграция базы данных для prod-среды.

    4.7. Развертывание Web Portal и Web API в prod GCP.

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

.NET Framework -> .NET Core

Перед началом переноса кода я нашел статью о миграции .Net Framework на .Net Core от Microsoft и далее ссылку на миграцию ASP.NET на ASP.NET Core.

С миграцией не-Web-проектов все обстояло относительно просто:

  • преобразование формата хранения NuGet-пакетов с помощью Visual Studio 2019;

  • адаптирование списка этих пакетов и их версий;

  • переход с App.config в XML на settings.json и замена всех имеющихся обращений к конфигурационным значениям на новый синтаксис.

Некоторые версии NuGet-пакетов Azure SDK претерпели изменения, повлекшие несовместимость. В большинстве случаев удалось найти не всегда самую новую, зато поддерживаемую кодом .NET Core версию, которая не требовала бы изменений в логике старого программного кода. Исключением стали пакеты для работы с Azure Service Bus и WebJobs SDK. Пришлось с Azure Service Bus перейти на бинарную сериализацию, а WebJob перевести на новую, обратно несовместимую версию SDK.

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

  1. Создание нового проекта ASP.NET Core и перенос в него основного кода из старого ASP.NET MVC проекта.

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

  3. Замена Web.config на appsettings.json и все связанные с этим изменения в коде.

  4. Внедрение стандартного механизма Dependency injection от .NET Core вместо любой его альтернативы, использовавшейся в Asp.NET MVC проекте.

  5. Использование StaticFiles middleware для всех корневых папок статических файлов: изображений, шрифтов, JavaScript-скриптов, CSS-стилей и т. д.

app.UseStaticFiles(); // wwwrootapp.UseStaticFiles(new StaticFileOptions   {     FileProvider = new PhysicalFileProvider(         Path.Combine(Directory.GetCurrentDirectory(), "Scripts")),     RequestPath = "/Scripts"});

Можно перенести все статические файлы в wwwroot.

6. Переход к использованию bundleconfig.json для всех JavaScript и CSS-бандлов вместо старых механизмов. Изменение синтаксиса подключения JavaScript и CSS:

<link rel="stylesheet" href="~/bundles/Content.css" asp-append-version="true" /><script src="~/bundles/modernizr.js" asp-append-version="true"></script>

Чтобы директива asp-append-version="true" работала корректно, бандлы (bundles) должны находиться в корне, т. е. в папке wwwroot (смотри здесь).

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

7. Изменение механизма обработки UnhadledExceptions: в ASP.NET Core реализована его поддержка, остается с ней разобраться и использовать вместо того, что применялось в проекте раньше.

8. Логирование: я адаптировал старые механизмы логирования для использования стандартных в ASP.NET Core и внедрил Serilog. Последнее опционально, но, по-моему, сделать это стоит для получения гибкого structured logging c огромным количеством вариантов хранения логов.

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

10. Routing: в старом проекте использовался механизм, основанный на templates, его надо было чуть-чуть подправить.

11. JSON-сериализация: В ASP.NET Core по умолчанию используется библиотека System.Text.Json вместо Newtonsoft.Json. Microsoft утверждает, что она работает быстрее предшественницы, однако, в отличие от последней, она не поддерживает многое из того, что Newtonsoft.Json умела делать из коробки безо всякого участия программиста. Хорошо, что есть возможность переключиться обратно на Newtonsoft.Json. Именно это я и сделал, когда выяснил, что большая часть сериализации в Web API была сломана, и вернуть ее в рабочее состояние с помощью новой библиотеки, если и возможно, очень непросто. Подробнее об использовании Newtonsoft.Json можно прочитать здесь.

12. В старом проекте использовался Typescript 2.3. С его подключением пришлось повозиться, потребовалось установить Node.js, подобрать правильную версию пакета Microsoft.TypeScript.MSBuild, добавить и настроить tsconfig.json, поправить файл определений (Definitions) для библиотеки Knockout, кое-где добавить директивы //@ts-ignore.

13. Код для принудительной поддержки HTTPS включается автоматически при включении этой опции в визарде проекта. Старый код, использующий пользовательский атрибут HttpsOnly, был при этом убран.

14. Все низкоуровневые действия, такие как получение параметров из body запроса, URL запроса, HTTP Headers и HttpContext потребовали изменений, т. к. API для доступа к ним претерпел изменения по сравнению с ASP.NET MVC. Работы было бы заметно меньше, если бы в старом проекте чаще использовались стандартные binding механизмы через параметры экшенов (Actions) и контроллеров (Controllers).

15. Был добавлен Swagger c помощью библиотеки Swashbuckle.AspNetCore.Swagger.

16. Нестандартный механизм Authentication потребовал рефакторинга для приведения его к стандартному виду.

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

Что делать со специфичными сервисами Azure?

После перехода на ASP.NET Core предстояло избавиться от Azure-сервисов. Можно было либо подобрать решения, которые не зависят от облачной платформы, либо найти что-то подходящее из списка GCP. Благо у многих сервисов есть прямые альтернативы у других облачных провайдеров.

Azure Service Bus мы по настоятельной рекомендации заказчика решили заменить на Redis Pub/Sub. Это достаточно простой инструмент, не настолько мощный и гибкий как, например, RabbitMQ. Но для нашего простого сценария его хватало, а в пользу такого выбора говорило то, что Redis в проекте уже использовался. Время подтвердило решение было правильным. Логика работы с очередью была абстрагирована и выделена в два класса, один из которых реализует отправку произвольного объекта, другой получает сообщения и передает их на обработку. На выделение этих объектов ушло всего несколько часов, а если сам Redis Pub/Sub вдруг потребуется заменить, то и это будет очень просто.

Azure Blobs были заменены на GCP Blobs. Решение очевидное, но все-таки различие в функциональности сервисов нашлось: GCP Blobs не поддерживает добавление данных в конец существующего блоба. В нашем проекте такой блоб использовался для создания подобия логов в формате CSV. На платформе Google мы решили записывать эту информацию в Google Cloud operations suite, ранее известный как Stackdriver.

Хранилище Azure Table Storage использовалось для записи логов приложения и доступа к ним из Web Portal. Для этого существовал логгер, написанный самостоятельно. Мы решили привести этот процесс в соответствие с практиками от Microsoft, т. е. использовать их интерфейс ILogger. Кроме того, была внедрена библиотека для структурного логирования Serilog. В GCP логирование настроили в Stackdriver.

Какое-то время проект должен был параллельно работать и на GCP, и на Azure. Поэтому вся функциональность, зависящая от платформы, была выделена в отдельные классы, реализующие общие интерфейсы: IBlobService, IRequestLogger, ILogReader. Абстрагирование логирования было достигнуто автоматически за счет использования библиотеки Serilog. Но для того, чтобы показывать логи в Web Portal, как это делалось в старом приложении, понадобилось адаптировать порядок записей в Azure Table Storage, реализуя свой Serilog.Sinks.AzureTableStorage.KeyGenerator.IKeyGenerator. В GCP для чтения логов изGoogle Cloud operations были созданы Log Router Sinks, передающие данные в BigQuery, откуда приложение и получало их.

Что делать с Azure WebJobs?

Сервис Azure WebJobs доступен только для Azure App Services on Windows. По сути он представляет собой консольное приложение, использующее специальный Azure WebJobs SDK. Зависимость от этого SDK я убрал. Приложение осталось постоянно работающим консольным и следует похожей логике:

static async Task Main(string[] args){.   var builder = new HostBuilder();  ...              var host = builder.Build();  using (host)  {     await host.RunAsync();  }...}

За всю работу отвечает зарегистрированный с помощью Dependency Injection класс

public class RedisPubSubMessageProcessor : Microsoft.Extensions.Hosting.IHostedService{...public async Task StartAsync(CancellationToken cancellationToken)...public async Task StopAsync(CancellationToken cancellationToken)...}

Это стандартный для .NET Core механизм. Несмотря на отсутствие зависимости от Azure WebJob SDK, это консольное приложение успешно работает как Azure WebJob. Оно также без проблем работает в Linux Docker-контейнере под управлением Kubernetes, о чем речь в статье пойдет позже.

Рефакторинг по дороге

Архитектура и код приложения были далеки от идеала. В ходе многих шагов постепенно производились небольшие изменения кода, который они затрагивали. Были и специально запланированные этапы рефакторинга, согласованные и оцененные вместе с заказчиком. На этих этапах мы устраняли проблемы с аутентификацией и авторизацией, переводили их на практики от Microsoft. Был отдельный этап по внесению некой архитектуры, выделению слоев, устранению ненужных зависимостей. Работа с Web API началась с этапа удаления неиспользуемого кода. При замене многих Azure-сервисов на первом этапе производилось определение интерфейсов, выделение данных зависимостей в отдельные классы.

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

Docker

С поддержкой Docker все сложилось довольно гладко. Dockerfile можно легко добавить с помощью Visual Studio. Я добавил их для всех проектов, соответствующих приложениям, для Web Portal, Web API, WebJob (который в дальнейшем превратился просто в консольное приложение). Эти стандартные Dockerfile от Microsoft не претерпели особенных изменений и заработали из коробки за единственным исключением пришлось в Dockerfile для Web Portal добавить команды для установки Node.js. Этого требует build контейнер для работы с TypeScript.

RUN apt-get update && \apt-get -y install curl gnupg && \curl -sL https://deb.nodesource.com/setup_12.x  | bash - && \apt-get -y install nodejs

Azure App Services -> GKE

Нет единственно правильного решения для развертывания .NET Core-приложений в GCP, вы всегда можете выбрать из нескольких опций:

  • App Engine Flex.

  • Kubernetes Engine.

  • Compute Engine.

В нашем случае я остановился на Google Kubernetes Engine (GKE). Причем к этому моменту у нас уже были контейнеризованные приложения (Linux). GKE, оказалось, пожалуй, наиболее гибким из трех представленных выше решений. Оно позволяет разделять ресурсы кластера между несколькими приложениями, как в нашем случае. В принципе для выбора одного из трех вариантов можно воспользоваться блок-схемой по этой сслыке.

Выше описаны все решения по используемым сервисам GCP, кроме MS SQL Server, который мы заменили на Cloud SQL от Google.

Архитектура нашей системы после миграции в GCPАрхитектура нашей системы после миграции в GCP

Тестирование

Web Portal тестировался вручную, после каждого этапа я сам проводил простенький Smoke-тест. Это было обусловлено наличием пользовательского интерфейса. Если по завершении очередного этапа, новый кусок кода выпускался в Prod, к его тестированию подключались другие пользователи, в частности, Product Owner. Но выделенных QA-специалистов, в проекте, к сожалению, не было. Разумеется, все выявленные ошибки исправлялись до начала очередного этапа. Позднее был добавлен простой Puppeteer-тест, который исполнял сценарий загрузки одного из двух типов отчетов с какими-то параметрами и сравнивал полученный отчет с эталонным. Тест был интегрирован в CICD. Добавить какие-то юнит-тесты было проблематично по причине отсутствия какой-либо архитектуры.

Первым этапом миграции Web API, наоборот, было написание тестов. Для это использовался Postman, затем эти тесты вызывались в CICD с помощью Newman. Еще раньше к старому коду была добавлена интеграция со Swagger, который помог сформировать начальный список адресов методов и попробовать многие из них. Одним из следующих шагов было определение актуального перечня операций. Для этого использовались логи IIS (Internet Information Services), которые были доступны за полтора месяца. Для многих актуальных методов перечня было создано несколько тестов с разными параметрами. Тесты, приводящие к изменению данных в базе, были выделены в отдельную Postman-коллекцию и не запускались на общих средах выполнения. Разумеется, все это было параметризовано, чтобы можно было запускать и на Staging, и на Prod, и на Dev.

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

Azure MS SQL -> GCP Managed MS SQL

Миграция MS SQL из Managed Azure в GCP Cloud SQL оказалась не такой простой задачей, как представлялось вначале. Основных причин тому оказался несколько:

  • Очень большой размер базы данных (Azure портал показал: Database data storage /

    Used space 181GB).

  • Наличие зависимостей от внешних таблиц.

  • Отсутствие общего формата для экспорта из Azure и импорта в GCP Cloud SQL.

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

Перед началом миграции нужно удалить все ссылки на внешние таблицы и базы данных, иначе миграция будет неудачной. Azure SQL поддерживает экспорт только в формат bacpac, более компактный по сравнению со стандартным backup форматом. В нашем случае вышло 6 Гб в bacpac против 154 Гб в backup. Но GCP Cloud позволят импортировать только backup, поэтому нам потребовалась конвертация, сделать которую удалось лишь посредством восстановления в локальную MS SQL из bacpac и создания backup уже из нее. Для этих операций потребовалось установить последнюю версию Microsoft SQL Server Management Studio, причем локальный сервер MS SQL Server был версией ниже. Немало операций заняли по многу часов, некоторые и вовсе длились по несколько дней. Рекомендую увеличить квоту Azure SQL перед импортом и сделать копию prod базы, чтобы импортировать из нее. Где-то нам потребовалось передавать файл между облаками, чтобы ускорить загрузку на локальную машину. Мы также добавили SSD-диск на 1 Тб специально под файлы базы данных.

Задачи на будущее

При переходе с Azure App Services на GCP Kubernetes мы потеряли CICD, Feature Branch deployments, Blue/Green deployment. На Kubernetes все это несколько сложнее и требует иной реализации, но наверняка делается посредством все тех же Github Actions. В новом облаке следуем концепции Iac (Infrastructure-as-Code) вместе с Pulumi.

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

Подробнее..

Как дешево переехать в Европу

16.09.2020 16:23:32 | Автор: admin

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


Я начал делать стартап...

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

Мой стартап находится на стыке математики, машинного обучения и образования.В какой-то момент работы над проектом, я занялся research'ом рынка и поиском моей потенциальной аудиторию. В итоге, я пришел к выводу, что моя основная аудитория это клиенты из Северной Америки и Европы. И тут образовался вопрос:"А как принимать платежи?".

Традиционный бизнес в Европе это очень дорого

Я начал гуглить как удобно принимать платежи в евро/долларах. Для меня наиболее удобным вариантом оказалсяStripe, который в России не доступен. Посмотрел список стран, где естьStripeи посмотрел где можно недорого зарегистрировать бизнес. Оказалось, что зарегистрировать бизнес в Европе дорого, даже очень дорого:в Германии минимальный уставной капитал 25000 евро или в Швейцарии 100000 швейцарских франков(франк примерно равен доллару США). Эти варианты были явно не для меня -тратить сотни тысяч евро на открытие бизнеса мне не очень хотелось.

Получение стартап-визы

И я наткнулся на стартап визу.Оказалось, это наиболее дешевый и простой способ переехать в Европу. В странах, в которых работает Stripe увидел Эстонию и подумал, что Эстония для меня идеальный вариант: во-первых, очень близко к России - границу можно перейти пешком,и я даже перешел ее в коронавирус - это настолько увлекательная история, что я про нее напишу отдельную статью; во-вторых, простота введения бизнеса - Эстония занимает лидирующие места в различных рейтинга, например вDoing business; в-третьих, в Эстонии большое русскоязычное комьюнити и достаточно много крупныхстартапов, я сам пользуюсь TransferWise и Bolt - да, они из Эстонии. Как следствие, в Эстонии большое количество талантливых IT специалистов.

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

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

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

Проходит 10 дней и ответа нет, проходит 14 дней ответа все еще нет, и спустя 3 недели приходит положительный ответ. Стартап комитет одобряет мою анкету и теперь я могу открыть свой стартап в Европе. При этом затраты минимальные - не нужно тратить тысячи евро, все что необходимо - оплатить государственные пошлины на регистрацию в районе 300 евро. Теперь предстоял переезд, бумажная волокита и получение 5 летнего ВНЖ.

Это и правда было так легко?

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

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

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

ВНЖ на 5 лет

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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    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