Автоматизируем FreeIPA: как устанавливать клиентов с помощью Ansible и управлять DNS записями через Terraform

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

Привет, меня зовут Денис, и я около двух лет работаю DevOps инженером в компании Altenar. Наша инфраструктура располагается как в Google Cloud, так и в собственном облаке на базе VMWare, в котором находится более 200 серверов и виртуальных машин.

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

FreeIPA (Free Identity, Policy and Audit) ─ это open-source решение для Linux (аналогичное MS Active Directory), которое обеспечивает централизованное управление учетными записями и централизованную аутентификацию.

Необходимо отметить, что у RedHat есть решение IdM, которое является частью RHEL. Они технически идентичны с FreeIPA и, можно сказать, что FreeIPA является upstream версией для IdM на которой обкатываются новые фичи (как Fedora для RHEL).

FreeIPA использует клиент-серверную модель. Клиентом может быть любая Linux машина, которая настроена для взаимодействия с FreeIPA (IdM) контроллером домена. Клиент осуществляет взаимодействие с помощью Kerberos, NTP, DNS сервисов и сертификатов.

Как настроить решение с репликацией из двух FreeIPA серверов с DNS можно почитать в этой статье, а для установки FreeIPA клиентов нам понадобится:
● Функционирующий FreeIPA контроллер с настроенным DNS сервером
● Один CentOS 7 сервер по крайней мере с 1GB памяти.
● Ansible version: 2.8+
● Terraform version: 0.13+

Конечно, можно устанавливать FreeIPA клиентов вручную. Это можно сделать в 3 шага:
1) Установить пакет yum install ipa-client
2) Запустить скрипт установки ipa-client-install
3) Пройти по шагам, ответив на вопросы установщика.

Но я уверен, что все прекрасно понимают плюсы автоматизации, поэтому мы пойдем немного другим путём. В Altenar мы используем два основных инструмента автоматизации ─ Ansible и Terraform. В данному случае мы решили использовать Ansible и взяли роль из официального репозитория freeipa.org с минимальным набором переменных в дополнение к перечисленным в /ipaclient/defaults/main.yml.

ipaclient_domain: nix.altenar.com ipaserver_domain: nix.altenar.com ipaserver_realm: NIX.ALTENAR.COM ipaclient_realm: NIX.ALTENAR.COM ipaclient_no_ntp: yes ipaclient_mkhomedir: yes

Playbook для запуска роли выглядит так:

--- - name: Playbook to configure IPA clients   hosts:   - linux   - tag_linux   become: yes   gather_facts: True   roles:   - { role: freeipa/ipaclient, state: present }

Изначально мы также использовали ansible для создания DNS записей.

tasks: - ipa_dnsrecord:     ipa_host: ipa.nix.altenar.com     ipa_pass: <removed>     state: present     zone_name: nix.altenar.com     record_name: "{{inventory_hostname}}"     record_type: 'A'     record_value: "{{ansible_host}}"     validate_certs: no     ipa_user: user

При этом возникала проблема: чтобы создать dns запись необходимо было сначала явно указать IP адрес "{{ansible_host}}" в inventory файле, что было не очень удобно.

[linux] xknm0.nix.altenar.com ansible_host=10.10.10.243

И так как мы используем в том числе Terraform в нашем частном облаке и стараемся придерживаться подхода IaC был найден terraform-provider-freeipa, который позволил нам добавлять DNS записи при создании виртуальных машин.

terraform {   required_providers {     freeipa = {       source = "camptocamp/freeipa"       version = "0.7.0"     }   } }   provider freeipa {   host = "ipa.nix.altenar.com" # or set $FREEIPA_HOST   username = "admin" # or set $FREEIPA_USERNAME   password = "<removed>" # or set $FREEIPA_PASSWORD   insecure = true }   resource freeipa_dns_record "test" {   dnszoneidnsname = "nix.altenar.com."   idnsname = "test"   records = ["10.10.10.10"]   type = "A" }

В результате мы получаем готовую DNS запись при создании виртуальной машины.

И ничто нам не мешает продолжить установку FreeIPA клиента с помощью Ansible роли.

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

P.S. Если есть вопросы или вы хотели бы рассказать о своем опыте по заявленной теме, смело приходите в комментарии.

ссылка на оригинал статьи https://habr.com/ru/post/568524/

Как устроена серверная UI-система Airbnb

Как Airbnb обеспечивает быстрое развертывание функций в браузерах, на iOS и Android с помощью серверной UI-системы Ghost Platform

Введение. Серверный UI

Прежде чем разбираться с реализацией серверного UI (SDUI) от Airbnb, важно понять, что это вообще такое и какие преимущества оно дает относительно традиционного клиентского UI.

Обычно данные обрабатываются серверной частью, а за работу интерфейса отвечает конкретный клиент (веб-браузер, приложения для iOS, Android). В качестве примера возьмем страницу Airbnb со списком предложений. Чтобы отобразить этот список пользователю, клиент может запросить данные у бэкенда, а затем преобразовать их в UI.

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

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

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

Применение SDUI

А что, если клиент даже не будет знать, что он отображает список предложений? Можно ли передавать клиенту напрямую UI, а не данные для построения интерфейса? Именно это и происходит в случае SDUI: мы передаем UI и данные вместе, а клиент отображает всё это независимо от того, что конкретно там внутри.

Реализация SDUI в Airbnb позволяет бэкенду контролировать одновременно и отображаемые данные, и способ их представления — причем для всех клиентов сразу. В браузере, приложениях для iOS и Android всё контролируется единственным ответом бэкенда: и макет экрана, и расположение разделов, и отображаемых в них данные, и даже действия, выполняемые при взаимодействии пользователей с разделами.

SDUI в Airbnb: Ghost Platform ​

Ghost Platform (GP) — это унифицированная гибкая серверная UI-система, которая позволяет быстро проводить итерации разработки и надежным образом запускать новые функции в браузерных клиентах, приложениях iOS и Android. Она называется Ghost, потому что наша основная цель — реализация функций «гость» (guest) и «хозяин» (host), представляющих две стороны приложений Airbnb.

GP предоставляет фреймворки для браузеров, iOS и Android на нативных языках каждого клиента (Typescript, Swift и Kotlin соответственно), которые позволяют разрабатывать управляемые сервером функции с минимальными усилиями.

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

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

Основа Ghost Platform — это стандартизированная модель данных, на базе которой клиенты отрисовывают UI. Чтобы это было осуществимо, GP использует общий уровень данных между бэкенд-сервисами и унифицированную сетку сервисов данных Viaduct.

Ключевым решением, которое помогло сделать серверную систему UI масштабируемой, стало использование единой совместно используемой схемы GraphQL для браузерного клиента, приложений iOS и Android — иными словами, для обработки ответов и создания строго типизированных моделей данных на всех платформах мы используем одну схему.

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

Ответ GP

Первый фундаментальный аспект GP — это структура ответа в целом. Для описания UI в ответе GP используются две основные концепции: разделы и экраны.

Рисунок 1. Как пользователи видят функции Airbnb на GP — и как GP видит те же функции как экраны и разделы
Рисунок 1. Как пользователи видят функции Airbnb на GP — и как GP видит те же функции как экраны и разделы
  • Разделы. Самый примитивный стандартный блок в GP — это раздел. Раздел описывает данные связанной группы компонентов UI, содержащие конкретную информацию для отображения — переведенную, локализованную и отформатированную. Клиенты берут данные раздела и преобразуют их непосредственно в интерфейс.

  • Экраны. В ответе GP может содержаться произвольное количество экранов. Экран описывает собственный макет, а также расположение разделов из массива разделов — «места размещения». Также экран определяет иные метаданные, например, способ отрисовки разделов (всплывающие, модальные, полноэкранные) и данные журнала.

interface GPResponse {   sections: [SectionContainer]   screens: [ScreenContainer]      # ... Other metadata, logging data or feature-specific logic } {"mode":"full","isActive":false}

Рисунок 2. Пример схемы GraphQL для ответа GP

Бэкенд функции на базе GP реализует этот GPResponse (рисунок 2) и заполняет экраны и разделы в зависимости от варианта использования. Клиентские GP-фреймворки в браузере, на iOS и Android предоставляют разработчику стандартный подход, позволяющий получить реализацию GPResponse и преобразовать ее в UI с минимальными усилиями.

Разделы

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

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

Схема разделов

В схеме GraphQL разделы GP представляют собой совокупность всех возможных типов разделов. Каждый тип раздела определяет поля, предоставляемые для отрисовки. Разделы передаются через реализацию GPResponse с определенными метаданными и предоставляются посредством оболочки SectionContainer, которая содержит сведения о состоянии раздела, данных журнала и фактической модели данных раздела.

# Example sections type HeroSection {   # Image urls   images: [String]! }  type TitleSection {   title: String!,   titleStyle: TextStyle!      # Optional subtitle   subtitle: String   subtitleStyle: TextStyle      # Action to be taken when tapping the optional subtitle   onSubtitleClickAction: IAction }  enum SectionComponentType {   HERO,   TITLE,   PLUS_TITLE,      # ... There's alot of these :) }  union Section = HeroSection   | TitleSection   | # ... More section data models    # The wrapper that wraps each section. Responsible for metadata, logging data and SectionComponentType type SectionContainer {   id: String!      # The key that determines how to render the section data model   sectionComponentType: SectionComponentType      # The data for this specific section   section: Section      # ... Metadata, logging data & more } {"mode":"full","isActive":false}

Рисунок 3. Фрагмент схемы GraphQL для раздела

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

Например, два типа SectionComponentType — TITLE и PLUS_TITLE — могут использовать одну модель данных TitleSection, но в реализации PLUS_TITLE для отображения раздела TitleSection будет использоваться логотип и стиль заголовка для Airbnb Plus. Такой подход обеспечивает универсальность функций на базе GP и возможность множественного использования схемы и данных.

Рисунок 4. Пример альтернативной отрисовки модели данных TitleSection с использованием SectionComponentType
Рисунок 4. Пример альтернативной отрисовки модели данных TitleSection с использованием SectionComponentType

Компоненты раздела

Данные раздела преобразуются в UI посредством так называемых «компонентов раздела». Каждый компонент отвечает за преобразование модели данных и SectionComponentType в элементы UI. GP для каждой платформы предоставляет абстрактные компоненты разделов на нативных языках (Typescript, Swift, Kotlin), поэтому разработчики могут расширять их и создавать новые разделы.

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

Возьмем пример для платформы Android (потому что я на ней разрабатываю — а еще потому, что Kotlin классный ​). Создадим раздел заголовка, используем фрагмент кода, приведенный ниже (рисунок 5). В реализациях для браузера и iOS построение компонентов раздела делается похожим образом (на Typescript и Swift соответственно).

// This annotation builds a Map<SectionComponentType, SectionComponent> that GP uses to render sections @SectionComponentType(SectionComponentType.TITLE) class TitleSectionComponent : SectionComponent<TitleSection>() {       // Developers override this method and build UI from TitleSection corresponding to TITLE     override fun buildSectionUI(section: TitleSection) {          // Text() Turns our title into a styled TextView         Text(             text = section.title,             style = section.titleStyle         )          // Optionally build a subtitle if present in the TitleSection data model         if (!section.subtitle.isNullOrEmpty() {              Text(                 text = section.subtitle,                 style = section.subtitleStyle             )         }     } } {"mode":"full","isActive":false}

Рисунок 5. Пример компонента раздела (Kotlin)

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

Рисунок 6. GP берет данные раздела, с помощью компонента раздела преобразует их в UI (TitleSectionComponent на рисунке 5) и показывает пользователю собранный интерфейс раздела
Рисунок 6. GP берет данные раздела, с помощью компонента раздела преобразует их в UI (TitleSectionComponent на рисунке 5) и показывает пользователю собранный интерфейс раздела

Экраны

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

Схема экранов

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

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

type ScreenContainer {   id: String      # Properties such as how to launch this screen (popup, sheet, etc.)   screenProperties: ScreenProperties      layout: LayoutsPerFormFactor }  # Specifies the ILayout type depending on rotation, client screen density, etc. type LayoutsPerFormFactor {      # Compact is usually used for portrait breakpoints (i.e. mobile phones)   compact: ILayout      # Wide is usually used for landscape breakpoints (i.e. web browsers, tablets)   wide: ILayout } {"mode":"full","isActive":false}

Рисунок 7. Пример схемы экранов GP

Макеты ILayout

Рисунок 8. Несколько примеров реализации ILayout, которые используются для указания различных мест размещения
Рисунок 8. Несколько примеров реализации ILayout, которые используются для указания различных мест размещения

ILayout позволяет менять макет экрана в зависимости от ответа. В схеме ILayout это интерфейс, в котором каждый ILayout представляет собой реализацию с указанием различных мест размещения. Места размещения включают в себя один или несколько типов SectionDetail, которые указывают на разделы во внешнем массиве sections ответа. Здесь мы указываем на модели данных раздела, а не включаем их в код, что сокращает размер ответа за счет повторного использования разделов в различных конфигурациях макета (LayoutsPerFormFactor на рисунке 7).

interface ILayout {}  type SectionDetail {   # References a SectionContainer in the GPResponse.sections array   sectionId: String      # Styling data   topPadding: Int   bottomPadding: Int      # ... Other styling data (margins, borders, etc) }  # A placement meat to display a single GP section type SingleSectionPlacement {   sectionDetail: SectionDetail! }  # A placement meat to display multiple GP sections in the order they appear in the sectionDetails array type MultipleSectionsPlacement {   sectionDetails: [SectionDetail]! }  # A layout implementation defines the placements that sections are inserted into. type SingleColumnLayout implements ILayout {      nav: SingleSectionPlacement      main: MultipleSectionsPlacement      floatingFooter: SingleSectionPlacement } {"mode":"full","isActive":false}

Рисунок 9. Пример схемы GP для ILayout

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

Действия

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

interface IAction {}  # A simple action that will navigate the user to the screen matching the screenId when invoked type NavigateToScreen implements IAction {   screenId: String }  # A sample TitleSection using an IAction type to handle the click of the subtitle type TitleSection {   ...      subtitle: String      # Action to be taken when tapping the subtitle   onSubtitleClickAction: IAction } {"mode":"full","isActive":false}

Рисунок 10. Пример схемы IAction в GP

Ранее (см. рисунок 6) мы видели, что компонент раздела на каждом клиенте преобразует TitleSection в UI. Взглянем на тот же пример TitleSectionComponent для Android с динамичным IAction, которое запускается по нажатию на текст подзаголовка.

@SectionComponentType(SectionComponentType.TITLE) class TitleSectionComponent : SectionComponent<TitleSection>() {       override fun buildSectionUI(section: TitleSection) {          // Build title UI elements          if (!section.subtitle.isNullOrEmpty() {              Text(                 ...                 onClick = {                   GPActionHandler.handleIAction(section.onSubtitleClickAction)                 }             )         }     } } {"mode":"full","isActive":false}

Рисунок 11. Пример компонента раздела с действием IAction, которое запускается при нажатии на подзаголовок

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

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

Собираем всё вместе

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

{   "screens": [     {       "id": "ROOT",       "screenProperties": {},       "layout": {         "wide": {},         "compact": {           "type": "SingleColumnLayout",           "main": {             "type": "MultipleSectionsPlacement",             "sectionDetails": [               {                 "sectionId": "hero_section"               },               {                 "sectionId": "title_section"               }             ]           },           "nav": {             "type": "SingleSectionPlacement",             "sectionDetail": {               "sectionId": "toolbar_section"             }           },           "footer": {             "type": "SingleSectionPlacement",             "sectionDetail": {               "sectionId": "book_bar_footer"             }           }         }       }     },   ],   "sections": [     {       "id": "toolbar_section",       "sectionComponentType": "TOOLBAR",       "section": {         "type": "ToolbarSection",         "nav_button": {           "onClickAction": {             "type": "NavigateBack",             "screenId": "previous_screen_id"           }         }       }     },     {       "id": "hero_section",       "sectionComponentType": "HERO",       "section": {          "type": "HeroSection",         "images": [           "api.airbnb.com/...",         ],       }     },     {       "id": "title_section",       "sectionComponentType": "TITLE",       "section": {          "type": "TitleSection",         "title": "Seamist Beach Cottage, Private Beach & Ocean Views",         "titleStyle": {}       }     },     {       "id": "book_bar_footer",       "sectionComponentType": "BOOK_BAR_FOOTER",       "section": {         "type": "ButtonSection",          "title": "$450/night",         "button": {           "text": "Check Availability",           "onClickAction": {             "type": "NavigateToScreen",             "screenId": "next_screen_id"           }         },       }     },   ] } {"mode":"full","isActive":false}

Рисунок 12. Пример действительного ответа GP в формате JSON

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

Функции, использующие GP, должны получить ответ, реализующий GPResponse (см. рисунок 2 выше). Получив GPResponse, инфраструктура GP анализирует этот ответ и формирует разделы.

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

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

Рисунок 13. Преобразование данных раздела в UI
Рисунок 13. Преобразование данных раздела в UI

Обработка действий

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

Напомню, что GP направляет действия к соответствующим обработчикам. В примере ответа выше (рисунок 12) есть два раздела, которые могут запускать действия: toolbar_section и book_bar_footer. Компонент раздела для создания этих разделов должен всего лишь принять действие IAction указать, когда его запускать (в обоих случаях — при нажатии кнопки).

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

button(   onClickListener = {     GPActionHandler.handleIAction(section.button.onClickAction)   } )

Подготовка экрана и макета

Чтобы сформировать для пользователя интерактивный экран, GP ищет в массиве экранов элемент с идентификатором ROOT (экран по умолчанию для GP). Затем GP ищет подходящий тип ILayout в зависимости от контрольной точки и других данных, относящихся к конкретному устройству пользователя. Для простоты используем макет из поля compact — SingleColumnLayout.

Затем GP ищет отрисовщик макета для SingleColumnLayout, где он наполняет макет верхним контейнером (место размещения nav), прокручиваемым списком (main) и плавающей нижней панелью (footer).

Этот отрисовщик макета берет модели для мест размещения, содержащие объекты SectionDetail. В каждом SectionDetail есть сведения о стиле, а также sectionId наполняемого раздела. GP проходит по объектам SectionDetail и с помощью созданных ранее компонентов раздела наполняет разделы, формируя соответствующие места размещения.

Рисунок 14. Инфраструктура GP берет созданные разделы с добавленными обработчиками действий и добавляет разделы в места размещения ILayout
Рисунок 14. Инфраструктура GP берет созданные разделы с добавленными обработчиками действий и добавляет разделы в места размещения ILayout

Развитие GP

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

Мы планируем расширить возможность компоновки UI за счет «вложенных разделов», повысить удобство поиска имеющихся элементов в инструментах для дизайна, таких как Figma, а также реализовать WYSIWYG-редактирования разделов и мест размещения, что позволит изменять функции без написания кода.

Если вам интересен серверный UI или создание масштабируемых UI-систем — работа для вас найдется. Смело подавайте резюме на вакансии в нашей команде разработчиков.

Конференция Re-engineering Travel

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

Если вам интересен более высокоуровневый обзор SDUI и GP — можете послушать мое недавнее выступление в рамках проводимого компанией Airbnb мероприятия Re-engineering Travel, где я представлял систему GP. Там я даю общий обзор серверного пользовательского интерфейса и GP (если хотите покороче — слушайте с 31-й минуты).

Благодарности

Хочу поблагодарить за неутомимую работу над созданием и поддержкой GP следующих людей: Abhi Vohra, Wensheng Mao, Jean-Nicolas Vollmer, Pranay Airan, Stephen Herring, Jasper Liu, Kevin Weber, Rodolphe Courtier, Daniel Garcia-Carrillo, Fidel Sosa, Roshan Goli, Cal Stephens, Chen Wu, Nick Miller, Yanlin Chen, Susan Dang, Amity Wang, а также многих, кто остался за кулисами.


О переводчике

Перевод статьи выполнен в Alconost.

Alconost занимается локализацией игр, приложений и сайтов на 70 языков. Переводчики-носители языка, лингвистическое тестирование, облачная платформа с API, непрерывная локализация, менеджеры проектов 24/7, любые форматы строковых ресурсов.

Мы также делаем рекламные и обучающие видеоролики — для сайтов, продающие, имиджевые, рекламные, обучающие, тизеры, эксплейнеры, трейлеры для Google Play и App Store.

ссылка на оригинал статьи https://habr.com/ru/company/alconost/blog/568444/

SiteAnalyzer 2.5 — Скрейпинг данных, проверка уникальности и скорость загрузки страниц

Всем привет! После весьма продолжительного периода мы, наконец, подготовили новый релиз SiteAnalyzer, который, надеемся, оправдает ваши ожидания и станет незаменимым помощником в SEO-продвижении.

SiteAnalyzer 2.5

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

Основные изменения

1. Скрейпинг данных с помощью XPath, CSS, XQuery, RegEx.

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

Скрейпинг данных XPath, CSS, XQuery, RegEx

Основными способами веб-скрейпинга являются методы разбора данных используя XPath, CSS-селекторы, XQuery, RegExp и HTML templates.

  • XPath представляет собой специальный язык запросов к элементам документа формата XML / XHTML. Для доступа к элементам XPath использует навигацию по DOM путем описания пути до нужного элемента на странице. С его помощью можно получить значение элемента по его порядковому номеру в документе, извлечь его текстовое содержимое или внутренний код, проверить наличие определенного элемента на странице.
  • CSS-селекторы используются для поиска элемента его части (атрибут). CSS синтаксически похож на XPath, при этом в некоторых случаях CSS-локаторы работают быстрее и описываются более наглядно и кратко. Минусом CSS является то, что он работает лишь в одном направлении – вглубь документа. XPath же работает в обе стороны (например, можно искать родительский элемент по дочернему).
  • XQuery имеет в качестве основы язык XPath. XQuery имитирует XML, что позволяет создавать вложенные выражения в таким способом, который невозможен в XSLT.
  • RegExp – формальный язык поиска для извлечения значений из множества текстовых строк, соответствующих требуемым условиям (регулярному выражению).
  • HTML templates – язык извлечения данных из HTML документов, который представляет собой комбинацию HTML-разметки для описания шаблона поиска нужного фрагмента плюс функции и операции для извлечения и преобразования данных.

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

Скрейпинг данных

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

Настройки скрейпинга

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

Тестирование правил скрейпинга

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

Экспорт данных в Excel

2. Проверка уникальности контента внутри сайта.

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

Это может быть полезно в случаях:

  • Для поиска полных дублей страниц (например, страница с параметрами и та же самая страница, но в виде ЧПУ).
  • Для поиска частичных совпадений контента (например, два рецепта борща в кулинарном блоге, которые схожи между собой на 96%, что наводит на мысль, что одну из статей лучше удалить, дабы избавиться от возможной каннибализации трафика).
  • Когда на статейном сайте вы случайно написали статью по теме, которую уже писали ранее 10 лет назад. В этом случае наш инструмент также выявит дубликат такой статьи.

Принцип работы инструмента проверки уникальности контента прост: по списку URL сайта программа скачивает их содержимое, получает текстовое содержимое страницы (без блока HEAD и без HTML-тегов), а затем при помощи алгоритма шинглов сравнивает их друг с другом.

Проверка уникальности контента

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

3. Проверка скорости загрузки страниц по Google PageSpeed.

Инструмент PageSpeed Insights поискового гиганта Google позволяет проверять скорость загрузки тех или иных элементов страниц, а также показывает общий бал скорости загрузки интересующих URL для десктопной и мобильной версии браузера.

Работа Google PageSpeed Insights

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

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

Проверка скорости загрузки страниц, Google PageSpeed

Основные анализируемые параметры:

  • FCP (First Contentful Paint) – время отображения первого контента.
  • SI (Speed Index) – показатель того, как быстро отображается контент на странице.
  • LCP (Largest Contentful Paint) – время отображения наибольшего по размеру элемента страницы.
  • TTI (Time to Interactive) – время, в течение которого страница становится полностью готова к взаимодействию с пользователем.
  • TBT (Total Blocking Time) – время от первой отрисовки контента до его готовности к взаимодействию с пользователем.
  • CLS (Cumulative Layout Shift) – накопительный сдвиг макета. Служит для измерения визуальной стабильности страницы.

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

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

4. Добавлена возможность группировки проектов по папкам.

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

Группировка проектов по папкам

Дополнительно появилась возможность фильтрации списка проектов по названию.

5. Обновлен интерфейс настроек программы.

Обновлен интерфейс настроек SiteAnalyzer

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

Прочие изменения

  • исправлен некорректный учет исключений URL
  • исправлен некорректный учет глубины сканирования сайта
  • восстановлено отображение редиректов для URL, импортированных из файла
  • восстановлена возможность перестановки и запоминания порядка столбцов на вкладках
  • восстановлен учет неканонических страниц, решена проблема с пустыми мета-тегами
  • восстановлено отображение анкоров ссылок на вкладке Инфо
  • ускорен импорт большого количества URL из буфера обмена
  • исправлен не всегда корректный парсинг title и description
  • восстановлено отображение alt и title у изображений
  • исправлено зависание при переходе на вкладку "Внешние ссылки" во время сканирования проекта
  • исправлена ошибка, возникающая при переключении между проектами и обновлении узлов вкладки "Статистика обхода сайта"
  • исправлено некорректное определение уровня вложенности для URL с параметрами
  • исправлена сортировка данных по полю HTML-hash в главной таблице
  • оптимизирована работа программы с кириллическими доменами
  • обновлен интерфейс настроек программы
  • обновлен дизайн логотипа

Буду рад любым замечаниям и предложениям по улучшению функционала программы.

ссылка на оригинал статьи https://habr.com/ru/post/568786/

Streamlit. Поиск кратчайшего пути

Самое длинное приключение начинается со слов «Я знаю короткую дорогу». Интересно о чем это? Читай дальше!

Streamlit — библиотека Python с открытым кодом. Она позволяет с легкостью создавать разные красивые веб-приложения для инженеров машинного обучения. Всего за несколько минут и пару строк кода можно создать стильные приложения.

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

pip install streamlit

Для написания приложения достаточно выполнить пару действий:

1.    Создать Python файл, например, first_app.py

2.    Импортировать библиотеку

import streamlit as st

3.    Запустить через консоль Python

streamlit run first_app.py

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

Примеры ниже:

Пример 1
Пример 1
Пример 2
Пример 2
Пример 3
Пример 3

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

 

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

st.title('My first app')

Streamlit поддерживает многие популярные библиотеки для работы с данными, такие как Matplotlib, Altair, deck.gl, Pandas и другие.

Например, для визуализации DataFrame’а с библиотеки Pandas достаточно создать его, и прописать st.write():

df = pd.DataFrame({   'first column': [1, 2, 3, 4],   'second column': [10, 20, 30, 40] }) st.write(df)

Получаем:

Если необходимо вывести данные в виде линейного графика, то вызываем команду:

chart_data = pd.DataFrame(      np.random.randn(20, 3),      columns=['a', 'b', 'c'])  st.line_chart(chart_data)

С помощью комманды st.map() можно отобразить точки с координатами

(широта, долгота) на карте. Пример:

df = pd.DataFrame(     np.random.randn(100, 2) / [0.5, 0.5] + [55.5, 37.33],     columns=['lat', 'lon']) st.map(df)

Попробуем отобразить на карте маршруты передвижений.

Датасет был взят отсюда https://data.world/cityofaustin/azhh-4hg8

В нем содержатся маршруты мусороуборочных машин в городе Остин, США.

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

Необходимые колонки из датасета:

the_geom — показывает координаты каждой точки маршрута.

GARB_DAY — отображает день недели маршрута.

Результат работы:

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

Полный код программы:

import pandas as pd import pydeck as pdk import streamlit as st  st.title('Маршруты мусороуборочных машин в городе Остин') #Цвета для маршрутов по дням недели colors = {         'Monday': [229,42,42],         'Thursday': [98,42,229],         'Wednesday': [42,229,61],         'Tuesday': [221,235,23],         'Friday': [144,108,26]     }  #Функция которая приводит столбец с геопозицией в необходимую форму def from_data_file(a):     res = pd.DataFrame(columns = ['latlng1','latlng2'])     for i in a:         route = i.split(', ')         for j in range(len(route)-1):             res = res.append(pd.DataFrame(data=[[route[j], route[j+1]]], columns = res.columns))     res['lon'] = res['latlng1'].apply(lambda x: float(x.split(' ')[0]))     res['lat'] = res['latlng1'].apply(lambda x: float(x.split(' ')[1]))     res['lon2'] = res['latlng2'].apply(lambda x: float(x.split(' ')[0]))     res['lat2'] = res['latlng2'].apply(lambda x: float(x.split(' ')[1]))     res['inbound'] = 100     res['outbound'] = 100     res = res.drop(['latlng1','latlng2'],1)     res = res.reset_index(drop=True)     return res  data = pd.read_csv('garbage-routes-1.csv') #Удаляем ненужные символы из столбца с геопозициями data['the_geom'] = data['the_geom'].apply(lambda x: x.replace(')','').replace('(','').replace('MULTIPOLYGON ','')) data = data.set_index('GARB_DAY') days = data.index.unique() ALL_LAYERS = {} #Добавляем слои на карту по дням недели for i in days:     ALL_LAYERS[i] = pdk.Layer(         "ArcLayer",         data = from_data_file(data['the_geom'][i]),         get_source_position=["lon", "lat"],         get_target_position=["lon2", "lat2"],         get_source_color=colors[i],         get_target_color=colors[i],         auto_highlight=True,         width_scale=0.001,         get_width="outbound",         width_min_pixels=3,         width_max_pixels=30,         ), st.sidebar.markdown('### Map Layers') selected_layers = [     layer for layer_name, layer in ALL_LAYERS.items()     if st.sidebar.checkbox(layer_name, True)] if selected_layers:     st.pydeck_chart(pdk.Deck(         map_style="mapbox://styles/mapbox/light-v10",         initial_view_state={"latitude": 30.367,                             "longitude": -97.6, "zoom": 11, "pitch": 50},         layers=selected_layers,     )) else:     st.error("Please choose at least one layer above.")

Здорово, не правда ли?! ​

ссылка на оригинал статьи https://habr.com/ru/post/568836/

Распознавание речи при помощи Arduino Nano: пример простой системы

image

Разработчик по имени Питер, как и многие из нас, попал на долгое время в ловушку карантина. У него появилось свободное время, причем много, и Питер решил разработать систему распознавания речи на базе Arduino Nano.

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

Основные элементы системы

Главный аппаратный компонент — Arduino Nano. Контроллер используется для запуска алгоритма распознавания речи. Также есть микрофонный усилитель MAX9814 для захвата голосовых команд.

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

image

Чтобы проанализировать речь, он сначала разделил каждый образец на сегменты по 50 мс. Это необходимо для разделении одного произнесенного слова на разные слоги. Возьмем, например, слово «seven». Как анализировать «se-» в «семи» отдельно от «-ven»? Нужно определить сегменты, каждый из которых сможет уместить произнесенный слог. 50 мс, как посчитал разработчик — оптимальное время для сегмента. Затем он рассчитал энергию 5 различных частотных диапазонов для каждого сегмента каждого высказывания. Обычно это делается с помощью преобразования Фурье, но у Nano недостаточно вычислительной мощности для вычисления преобразования Фурье в реальном времени. Поэтому Питер попробовал другой подход: он реализовал 5 наборов цифровых полосовых фильтров, что позволило ему более легко вычислять энергию сигнала в каждой полосе частот.

Энергия каждой полосы частот для каждого сегмента затем отправляется на ПК, где специализированный софт создает «шаблоны» на основе образцовых высказываний, которые он генерирует. Суть его алгоритма заключается в сравнении того, насколько близка энергия каждой полосы частот для каждого высказывания (и для каждого сегмента) к шаблону. Программа для ПК создает файл .h, который можно скомпилировать прямо на Nano. Питер использует пример распознавания произнесенных чисел — от 0 до 9, но вы можете изменить эти команды, например, на «старт» или «стоп», если захотите.

Результат

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

ссылка на оригинал статьи https://habr.com/ru/company/neuronet/blog/568838/