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

Блог компании vs robotics

Заряжай Patroni. Тестируем Patroni Zookeeper кластер (Часть первая)

24.12.2020 20:04:36 | Автор: admin
Кадр из фильма Рембо IVКадр из фильма Рембо IV

Вступление

Если выработаете сcrucial data, торано или поздно задумаетесь отом, что неплохобы поднять кластер отказоустойчивости. Даже если основной сервер сбазой улетит вглухой нокаут, show must goon, нетакли? При этом мыподразумеваем две вещи:

  • база данных совсей ееструктурой нам по-прежнему доступна;

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

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

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

Изминусов совершенно точно нужно назватьто, что это неout ofthe box solution. Как сказано вдоке:

Patroni is a template for you to create your own customized, high-availability solution using Python...

Иключевое здесь слово template. Тоесть все придется собирать самому. Новкаком-то смысле это плюс покрайней мере, мыдетально будем знать, что именно идёт впрод.

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

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

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

Что, ещё один туториал о Patroni?

Зачем читать именно этот туториал?

Есть уже немало туториалов, которые рассказывают, как поднять кластер Patroni. Этот затрагивает вопросы деплоя в среде docker swarm и использования Zookeeper в качестве DCS.

Почему Zookeeper?

Ясчитаю, это один измоментов, которые стоит рассмотреть всерьез, прежде чем выбрать конечный сетап для продакшена. Дело втом, что Patroni использует сторонние сервисы чтобы установить иобслуживать коммуникацию между своими нодами. Ихобщее название DCS (Dynamic Configuration Storage).

Если выуже смотрели какие-то изтуториалов оPatroni, то, должно быть, заметили, что самый частый кейс это когда вкачестве DCS используют Etcd кластер.

Интересный момент вработе Etcd кластера заключается втом, что:

Since etcd writes data to disk, its performance strongly depends on disk performance. For this reason, SSD is highly recommended.

(из документации Etcd)

Словом, если увас нет поSSD диску накаждой машине, где будут работать ноды Etcd, товывзоне опасности. Конечно, пока нагрузка небольшая, тоничего критичного происходить небудет, ноесли это рабочий, нагруженный прод, тоочень возможно (идаже вероятно), что выпросто перегрузите Etcd кластер. Аэто приведет кIO ошибкам при доступе кбазе. Звучит скверно? Насамом деле так иесть. Ловить такие ошибки напроде очень неприятно.

Здесь нам напомощь иможет прийти Zookeeper, который ничего непишет надиск ихранит все данные впамяти. Такой вариант оптимален вситуации, когда ненавсех серверах есть SSD, зато RAM хватает.

Почему Docker Swarm?

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

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

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

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

А в конце будет небольшой бонус

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

Окей, достаточно разговоров. Давайте перейдем кпрактике.

Docker Swarm

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

Яисхожу изпредположения, что увас уже установлен инастроен Docker Engine. Втаком случае, нужно только выполнить следующую команду:

docker swarm init//now check your single-node clusterdocker node lsID                            HOSTNAME       STATUS       AVAILABILITY          a9ej2flnv11ka1hencoc1mer2 *   floitet        Ready          Active       

Одна изважных фич Swarmа заключается втом, что теперь мыможем использовать нетолько обычные Docker контейнеры, ноитак называемые сервисы. Сервисы это посути дела абстракция над контейнерами. Если отталкиваться отаналогии сООП, тосервис это класс, аконтейнер конкретный объект класса. Параметры иправила сервиса задаются при деплое изyml-файла.

Рекомендую запомнить hostname ноды потом мыиспользуем его для указания constraint вконфигурационном файле.

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

Zookeeper

Прежде чем мыначнем деплой самого Patroni, нам нужно сначала развернуть кластер сDCS (внашем случае, как мыпомним, это Zookeeper). Явзял версию 3.4, иона работает вполне стабильно. Далее идет docker-compose конфиг инекоторые комментарии помоментам, которые, как мне кажется, имеет смысл отдельно упомянуть.

  • docker-compose-zookeeper.yml

docker-compose-zookeeper.yml
version: '3.7'services:  zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181    networks:      - patroni    environment:      ZOO_MY_ID: 1      ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo2:    image: zookeeper:3.4    hostname: zoo2    networks:      - patroni    ports:      - 2192:2181    environment:      ZOO_MY_ID: 2      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=0.0.0.0:2888:3888 server.3=zoo3:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: any  zoo3:    image: zookeeper:3.4    hostname: zoo3    networks:      - patroni    ports:      - 2193:2181    environment:      ZOO_MY_ID: 3      ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=0.0.0.0:2888:3888    deploy:      replicas: 1      placement:        constraints:          - node.hostname == floitet      restart_policy:        condition: anynetworks:  patroni:    driver: overlay    attachable: true
Details

Конечноже, важно дать каждой ноде уникальное имя ивнешний порт. Hostname лучше ставить одинаковое сименем сервиса.

zoo1:    image: zookeeper:3.4    hostname: zoo1    ports:      - 2191:2181

Стоит отметить ито, как мыперечисляем hostы встроке ниже: для первого сервиса server.1 будет привязан к0.0.0.0, а, например, для zoo2 это уже будет server.2 соответственно ит.д.

ZOO_SERVERS: server.1=0.0.0.0:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

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

     placement:        constraints:          - node.hostname == floitet

Ипоследний момент, который мыздесь обсудим, это network. Янамерен деплоить все сервисы Zookeeperа ивсе сервисы Patroni водну сеть сдрайвером overlay, чтобы они были изолированы отдругих сервисов имогли общаться между собой поименам, анепоIP (как это выглядит, будет видно дальше).

networks:  patroni:    driver: overlay// мы должны отметить сеть как attachable  // чтобы потом можно было присоединять к ней остальные сервисы    attachable: true

Итак, можно задеплоить Zookeeper:

sudo docker stack deploy --compose-file docker-compose-zookeeper.yml patroni

Теперь нужно проверить, что все работает. Первое что можно сделать это просто посмотреть список сервисов:

sudo docker service lsgxfj9rs3po7z        patroni_zoo1        replicated          1/1                 zookeeper:3.4         *:2191->2181/tcpibp0mevmiflw        patroni_zoo2        replicated          1/1                 zookeeper:3.4         *:2192->2181/tcpsrucfm8jrt57        patroni_zoo3        replicated          1/1                 zookeeper:3.4         *:2193->2181/tcp

Иследующим шагом можно сделать пинг сервисов спомощью специальной команды mntr:

echo mntr | nc localhost 2191// with the output being smth like thiszk_version3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMTzk_avg_latency6zk_max_latency205zk_min_latency0zk_packets_received1745zk_packets_sent1755zk_num_alive_connections3zk_outstanding_requests0zk_server_statefollowerzk_znode_count16zk_watch_count9zk_ephemerals_count4zk_approximate_data_size1370zk_open_file_descriptor_count34zk_max_file_descriptor_count1048576zk_fsync_threshold_exceed_count0

Также можно проверить логи сервиса, если есть желание:

docker service logs $zookeeper-service-id // service-id comes from 'docker service ls' command. // in my case it could be docker service logs gxfj9rs3po7z

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

Patroni

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

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

  • patroni.yml

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

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

patroni.yml
scope: patroninamespace: /service/bootstrap:    dcs:        ttl: 30        loop_wait: 10        retry_timeout: 10        maximum_lag_on_failover: 1048576        postgresql:            use_pg_rewind: true    postgresql:      use_pg_rewind: true    initdb:    - encoding: UTF8    - data-checksums    pg_hba:    - host replication all all md5    - host all all all md5zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin    pgpass: /tmp/pgpass    parameters:        unix_socket_directories: '.'tags:    nofailover: false    noloadbalance: false    clonefrom: false    nosync: false
Details

Важно указать Patroni путь кбинарным файлам Postgresа. Вмоем случае, так как яиспользую Postgres11, директория выглядит так: /usr/lib/postgresql/11/bin.

Вдиректории, внутри уже созданного контейнера, Patroni будет искать файлы Postgresа. Без этой настройки скорее всего ничего невзлетит (покрайней мере уменя невзлетело). Итакже еще есть data_dir это место вконтейнере, где будут храниться данные. Позже мысделаем mount этой директории кместу налокальном жестком диске, чтобы непотерять все полимеры, если кластер всеже упадет безнадежно. Это добавит нам работы посозданию этих папок локально, но, по-моему, оно того стоит.

postgresql:    data_dir: /data/patroni    bin_dir: /usr/lib/postgresql/11/bin

Также яперечисляю все сервера Zookeeperа вэтом конфиг файле, чтобы потом передать информацию оних утилите patronictl. Стоит отметить, что если неуказать ихвpatroni.yml, томыостанемся витоге снерабочим patronictl. Как вывидите, перечисляя сервера, янепишу никакиеIP, аиспользую ихимена. Это тасамая фича Docker Swarmа окоторой ярассказывал выше.

zookeeper:  hosts:       - zoo1:2181      - zoo2:2181      - zoo3:2181
  • patroni-entrypoint.sh

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

patroni-entrypoint.sh
#!/bin/shreadonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"export PATRONI_NAME="${PATRONI_NAME:-$(hostname)}"export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"export PATRONI_POSTGRESQL_LISTEN="$CONTAINER_POSTGRE_ADDR"export PATRONI_REPLICATION_USERNAME="$REPLICATION_NAME"export PATRONI_REPLICATION_PASSWORD="$REPLICATION_PASS"export PATRONI_SUPERUSER_USERNAME="$SU_NAME"export PATRONI_SUPERUSER_PASSWORD="$SU_PASS"export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"exec /usr/local/bin/patroni /etc/patroni.yml
Details. Важно!

Насамом деле, основной смысл вообще делать такой скрипт заключается втом, что мыпросто несможем стартануть сервис сPatroni, незнаяIP адрес hostа. Ивтом случае, когда hostом оказывается Docker-контейнер, нам как-то нужно сначала узнать какойже IPэтот контейнер получил, итолько потом мыможем запустить Patroni. Эта потребность закрывается вот здесь:

readonly CONTAINER_IP=$(hostname --ip-address)readonly CONTAINER_API_ADDR="${CONTAINER_IP}:${PATRONI_API_CONNECT_PORT}"readonly CONTAINER_POSTGRE_ADDR="${CONTAINER_IP}:5432"...export PATRONI_RESTAPI_CONNECT_ADDRESS="$CONTAINER_API_ADDR"export PATRONI_RESTAPI_LISTEN="$CONTAINER_API_ADDR"export PATRONI_POSTGRESQL_CONNECT_ADDRESS="$CONTAINER_POSTGRE_ADDR"

Как яуже говорил раньше, параметры конфига Patroni можно передавать разными способами. Вэтом скрипте мыпользуемся тем, что один изтаких способов это Environment configuration. PATRONIRESTAPICONNECTADDRESS, PATRONIRESTAPILISTEN, PATRONIPOSTGRESQLCONNECTADDRESS специальные переменные среды, окоторых Patroni знает заранее икоторые будут считаны. Икстати, они переписывают локальные настройки изpatroni.yml, так что beaware!

Иеще момент. Документация Patroni нерекомендует использовать superuserа для подключения кбазе приложений. Т.е. нужно создать отдельного юзера, который мыбудем использовать непосредственно для коннекта, аsuperuserа иreplicatorа трогать небудем совсем. Создать такого юзера можно также через переменную среды. Если хотите, чтобы юзер назывался как-то иначе чем approle, просто замените вэтой строке approle начто-то другое.

export PATRONI_approle_PASSWORD="$POSTGRES_APP_ROLE_PASS"export PATRONI_approle_OPTIONS="${PATRONI_admin_OPTIONS:-createdb, createrole}"

Ивпоследней строчке, когда всё уже готово кстарту, мыделаем запуск Patroni сервиса суказанием откуда брать основной конфиг файл:

exec /usr/local/bin/patroni /etc/patroni.yml
  • Dockerfile

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

Dockerfile
FROM postgres:11 RUN apt-get update -y\     && apt-get install python3 python3-pip -y\    && pip3 install --upgrade setuptools\    && pip3 install psycopg2-binary \    && pip3 install patroni[zookeeper] \    && mkdir /data/patroni -p \    && chown postgres:postgres /data/patroni \    && chmod 700 /data/patroni COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.shUSER postgresENTRYPOINT ["bin/sh", "/entrypoint.sh"]
Details

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

// владелец должен быть 'postgres', а mode 700    mkdir /data/patroni -p \    chown postgres:postgres /data/patroni \    chmod 700 /data/patroni     ...// устанавливаем в кач-ве активного юзера внутри контейнера // юзера postgres        USER postgres

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

COPY patroni.yml /etc/patroni.ymlCOPY patroni-entrypoint.sh ./entrypoint.sh

И, как яуже упомянул ранее, мыхотим запустить этот скрипт сразу после создания контейнера:

ENTRYPOINT ["bin/sh", "/entrypoint.sh"]

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

docker build -t patroni-test .

Самое время обсудить последний посписку, нонеповажности файл для Patroni compose yml.

  • docker-compose-patroni.yml

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

docker-compose-patroni.yml
version: "3.4"networks:    patroni_patroni:         external: trueservices:    patroni1:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5441:5432            - 8091:8091        hostname: patroni1        volumes:          - /patroni1:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni2:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5442:5432            - 8092:8091        hostname: patroni2        volumes:          - /patroni2:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]    patroni3:        image: patroni-test        networks: [ patroni_patroni ]        ports:            - 5443:5432            - 8093:8091        hostname: patroni3        volumes:          - /patroni3:/data/patroni        environment:            PATRONI_API_CONNECT_PORT: 8091            REPLICATION_NAME: replicator             REPLICATION_PASS: replpass            SU_NAME: postgres            SU_PASS: supass            POSTGRES_APP_ROLE_PASS: appass        deploy:          replicas: 1          placement:            constraints: [node.hostname == floitet]
Details

Первое, очем хочется сказать, это момент сexternal network, окотором говорилось ранее. Мыхотим разместить Patroni сервисы тамже, где мыдержим исервисы Zookeeper. Таким образом мысможем обращаться ксервисам поименам, ивсе имена: zoo1, zoo2, zoo3, которые мыперечислили вpatroni.yml, задавая сервера Zookeeperа, будут работать, как надо.

networks:    patroni_patroni:         external: true

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

ports:    - 5441:5432    - 8091:8091...environment:    PATRONI_API_CONNECT_PORT: 8091// также нужно убедиться, что в PATRONI_API_CONNECT_PORT мы передаем// тот же самый, который мы открываем для сервиса   

Также, нам, конечно, нужно передать все переменные среды, которые мызаявили вentrypoint скрипте. Ноиэто еще невсё. Есть вопрос сдиректорией для mountа, который мытоже здесь решаем:

volumes:   - /patroni3:/data/patroni

Как видно изэтой строки, тудиректорию /data/patroni, которая была создана вDockerfile, мымонтируем клокальной директории. Так вот эту локальную директорию нам нужно создать. Инетолько создать, ноивыставить правильного юзера ирежим доступа, например так:

sudo mkdir /patroni3sudo chown 999:999 /patroni3sudo chmod 700 /patroni3// 999 это дефолтный uid для юзера postgres  // эти шаги нужно повторить для каждой ноды Patroni

Мы наконец готовы деплоить Patroni кластер:

sudo docker stack deploy --compose-file docker-compose-patroni.yml patroni

После деплоя влогах сервиса мыдолжны увидеть что-то втаком ключе:

INFO: Lock owner: patroni3; I am patroni1INFO: does not have lockINFO: no action.  i am a secondary and i am following a leader

Былобы печально, еслибы мымогли проверить статус кластера иноды только читая логи. Так что предлагаю коснуться немного способов проверки состояния кластера иначать ссамого простого patronictl. Для этого нужно сначала получить idлюбого контейнера Patroni:

sudo docker psCONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                          NAMESa0090ce33a05        patroni-test:latest   "bin/sh /entrypoint."   3 hours ago         Up 3 hours          5432/tcp                       patroni_patroni1.1.tgjzpjyuip6ge8szz5lsf8kcq...

Ипотом зайти вконтейнер спомощью exec команды:

sudo docker exec -ti a0090ce33a05 /bin/bash// при вызове команды мы должны передать имя кластера// это параметр 'scope' в patroni.yml ('patroni' в нашем случае)patronictl list patroni// и тут ошибка...Error: 'Can not find suitable configuration of distributed configuration store\nAvailable implementations: exhibitor, kubernetes, zookeeper'

Команда patronictl полагается наpatroni.yml, чтобы получить информацию осерверах Zookeeperа. Оннезнает, где мыэтот файл положили. Так что явно укажем ему путь:

patronictl -c /etc/patroni.yml list patroni// and here is the nice output with the current states+ Cluster: patroni (6893104757524385823) --+----+-----------+| Member   | Host      | Role    | State   | TL | Lag in MB |+----------+-----------+---------+---------+----+-----------+| patroni1 | 10.0.1.93 | Replica | running |  8 |         0 || patroni2 | 10.0.1.91 | Replica | running |  8 |         0 || patroni3 | 10.0.1.92 | Leader  | running |  8 |           |+----------+-----------+---------+---------+----+-----------+

PostgreSQL Connection

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

docker run --rm -ti --network=patroni_patroni postgres:11 /bin/bash// доступ к конкретной нодеpsql --host patroni3 --port 5432 -U approle -d postgres// доступ к лидеру через haproxy// так что нужно указать какую-либо через флаг '-d' 

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

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

Подробнее..

Как я предсказал LGD на хакатоне и устроился на работу

11.06.2021 14:22:51 | Автор: admin

Всем привет! Меня зовут Андрей, недавно яприсоединился ккоманде VSRobotics изанимаюсь проектом автопостроителя сценариев диалогов робота-оператора. Вэтом посте хочу поделиться историей своего трудоустройства ирешением задачи LGD prediction, которое мне вэтом очень помогло. Несекрет, что начинающим DS-специалистам приходится преодолевать серьезные трудности, чтобы получить начальную позицию. Мнеже повезло получить офер, поучаствовав всоревновании иминовав изнурительные интервью имуки сомнений всобственной компетенции. Надеюсь, мой рассказ будет полезен иобратит внимание новичков нахакатоны иконференции как наотличные инструменты для активного поиска работы.

Вступление прошлая жизнь ипервые шаги вData Science

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

Впериод начала пандемии уменя освободилось время засчет отсутствия поездок доофиса, которое удалось использовать для изучения основ языка Python. Постигать азы начал спрочтения первого тома Лутца Изучаем Python. Летом 2020я попрощался сработой, чтобы перезарядить батарейки иуйти вперестройку. Выбрал онлайн курс поData Science иначал учиться.

Для себя ясформулировал, что хочу работать вкрупной компании, чтобы быть частью большого сообщества после учебы поставил себе цель найти подходящую позицию вСбере. Яотслеживал информацию оконференциях имероприятиях, вкоторых компания участвует. Благодаря странице https://ict2go.ru/companies/19/, яузнал, что Сбер участвует вконференции ScoringDay Весна 2021 икэтой конференции приурочен хакатон наплощадке dsbattle.com под названием LGD Prediction. Призеры соревнования (топ-3) получают бесплатный билет наконференцию ивозможность присоединиться ккоманде блока Риски. Нучтож, вызов принят!

Тяжело вучении, легко вбою!

0. Дрожащими руками, терзаемый сомнениями смогули я?, открыл ссылку сbaseline-решением наколабе. Посмотрел. Смогу. Визуальное знакомство сданными показало, что вцелом такие задачки решать яумею. Обычные табличные данные, задача регрессии, призовем CatBoost. Отмечу, что натот момент опыта участия всоревнованиях, кроме как вТитанике наKaggle, уменя небыло.

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

Целью задания было построить модель машинного обучения, предсказывающую LGD (Loss Given Default), другими словами, тудолю отвыданного кредита, которую банк потеряет вслучае дефолта заемщика. Для оценки качества модели использовалась метрика MAE mean absolute error, средний модуль отклонений.

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

1. После знакомства сдатасетом яразбил 35признаков для обучения на2группы: первая группа 24признака это финансовые показатели (выручка, чистая прибыль, совокупные активы ит.п.), вторая группа 11признаков различные прочие характеристики предприятия (срок ведения бизнеса, величина уставного капитала, объект взалоге).

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

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

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

Кроме того, япроверял данные надубликаты построкам (ихнебыло) ипостолбцам (были).

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

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

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

Как уже можно было догадаться, наиболее очевидным вариантом разделения на2категории стало наличие/отсутствие финансовой отчетности. Разбив объекты, ясоставил сводную таблицу, чтобы убедиться всущественном различии целевого признака погруппам.

Заметно, что средний имедианный LGD укомпаний сфинансовыми данными существенно ниже, чем уоставшихся компаний.

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

Все пропуски вфинансовых данных язаполнил нулями.

Затем обратился кисследованию признаков выручка ивеличина уставного капитала. Пообоим столбцам япопытался выделить крупные компании вотдельную категорию. Исследовав медиану исредние при различных вариантах, остановился награнице в50млн руб. для выручки (больше категория corporation) и100тыс. руб. для уставного капитала (больше категория big).

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

Далее ярешил построить pairplot для признаков, которые есть увсех объектов это срок ведения бизнеса, срок смомента регистрации ОГРН, сгруппировав объекты покатегории залога.

Этот график позволил сделать следующие выводы:

  • срок ведения бизнеса достаточно тесно коррелирует с целевым признаком;

  • срок ведения бизнеса исрок смомента регистрации ОГРН доопределенного момента имеют тесную связь, нозначение второго ограничено сверху (поскольку пофизическому смыслу они выражают примерно одно итоже то, сколько компания существует, было принято решение остановиться наменее зашумленном признаке срок ведения бизнеса);

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

Здесь видно, что примерно после значения науровне 70срока ведения бизнеса LGD укредиторов, отдавших взалог жилое здание, падал донуля. Было только 4исключения. Ярешил ими пренебречь ивыделить такую группу вотдельную категорию безопасный заемщик.

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

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

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

Так моя обучающая выборка содержала всего 9признаков: 4количественных и5категориальных. Ярешил использовать признак выручка (ar_revenue), так как онвзначительной степени характеризует величину компании иявляется определяющим для получения денежных потоков.

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

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

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

Заметно, что самые серьезные ошибки возникали тогда, когда модель выдавала LGD = 1, когда это небыло нужно, инепредсказывала0, когда это было нужно.

Затем ясделал предсказания для тестовой выборки, исистема показала, что ихкачество около 0.087: налицо было переобучение, нопоиграв сколичеством итераций вCatBoost ирегуляризацией, ядобился лишь небольшого улучшения 0.086. Так или иначе, оценка качества модели, сделанной пометоду берем только необходимое, оказалась выше, чем модель отбрасываем ненужное.

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

Наиболее значимым является срок ведения бизнеса, который, как мыпомним, тесно коррелировал сlgd. Второе итретье места заняли созданные категории наличия отчетности ивида залога. Также сам параметр выручки (ar_revenue) оказался важен. Прочие созданные мной признаки были существенно менее важны, нобез них качество наоткрытой части тестовой выборки было ниже.

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

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

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

Большой итог

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

После всех докладов наконференции инаграждения (где мне также вручили приз засоответствие модели регуляторным требованиям), мне удалось приятно пообщаться спобедителем Артемом ируководителем направления вVSRobotics Александром. Выяснилось, что вкомпанию, посчастливому для меня стечению обстоятельств, нужны люди! После вопросов отом, знакомыли мне основные понятия изобласти обработки естественного языка ивообще интересноли мне это, мыобменялись контактами, ияотправил свое резюме ипортфолио нарассмотрение. Спустя некоторое время мне перезвонили, сделали предложение, ияприсоединился ккоманде VSRobotics!

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

  1. Считаю, что мне вомногом повезло ссоревнованием вчастности, там было мало участников только у45человек результат оказался выше baseline. Правда, наверное, главное все-таки неколичество, акачество.

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

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

  4. Лучше мало хороших признаков, чем много плохих.

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

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

Спасибо, что дочитали доконца! Буду рад выслушать конструктивную критику решения иответить навопросы!

Подробнее..

Категории

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

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