понедельник, 30 июля 2018 г.

Какая паспорта загружал

Поставили мне задачку сделать сервис устаревших паспортов. Просто REST которому кидаешь JSON содержащий серию и номер паспорта, а в ответ приходит Passed или Failed. Сама база недействительных паспортов общедоступна. Её можно скачать с сайта МВД по адресу https://guvm.mvd.ru/upload/expired-passports/list_of_expired_passports.csv.bz2. Беда только в том, что в разархивированном виде она занимает почти 1.5 гигабайта, и её необходимо регулярно обновлять. Хочется грузить её максимально быстро, поэтому первое, что приходит в голову, это загрузить её с помощью команды COPY https://www.postgresql.org/docs/9.3/static/sql-copy.html
План обновлений изначально был такой:
1. Загружаем и разархивируем свежий файл
2. Чистим (TRUNCATE) табличку с паспортами
3. Копируем (COPY) CSV-файл прямо в эту табличку
4. Подчищаем ненужные файлы. 
5. Радуемся результату.

Вроде просто и понятно, но возникает проблемка. Копирование 1.5 гигабайтного файла происходит небыстро и в одну транзакцию. То есть пока идет копирование, все запросы будут идти в пустую таблицу. Это, прямо скажем, не то, что хотелось. Для решения этой проблемы возникла идея вместо прямых запросов в таблицу использовать представление, которое будет смотреть в уже заранее загруженную таблицу. План получился такой:
1. Загружаем и разархивируем свежий файл
2. Создаём таблицу под новую версию
3. Копируем (COPY) CSV-файл в свежесрезанную табличку
4. Обновляем представление, чтобы оно смотрело на загруженную таблицу
5. Удаляем (DROP) старую таблицу
6. Подчищаем больше ненужные файлы. 
7. Радуемся результату.

Локально, на мощном железе разработчика, сработало отлично. Запускаем всё это дело в Kubernetes и пытаемся загрузить базу. Ждем, ждём, ждём... Прождал 4 часа и печально ушел домой. В момент ухода БД съела уже 6 Гб ОЗУ. Все-таки загрузка таких больших файлов за раз на маленькой БД оказалась плохой идеей. Видимо, эта тема хорошо работает только для небольших файлов или больших баз. Пришлось отказаться от COPY и переделать на загрузку пачками по 10000 штук. В результате загрузка паспортов прошла в разумное время.
Вот какие ИТ приключения.

суббота, 21 июля 2018 г.

Смотрим на Postgres в Kubernetes

В нашей компании мы используем Kubernetes в качестве системы оркестрации докеров. Судя по ИТ конференциям, которые я посещал в последнее время, K8s (так его называют коротко) стал фактически промышленным стандартом, поэтому думаю, что мой сегодняшний опыт будет интересным.
Собственно, K8s используют для запуска большого количества маленьких сервисов. В нашей команде их уже накопилось достаточно много. Зачастую сервисам необходимы базы данных. В случае, когда необходима реалиционная БД, мы используем Postgres. Иногда для анализа дефектов логов недостаточно, и хочется подключится к базе напрямую и посмотреть, что там происходит. К тому же такая возможность пригодилась бы и для других наших проектов.
Высунуть порт базы из K8s само по себе не сложно, но возникают проблемы с сетевым доступом, а в нашей финансовой компании с этим жестко. Дополнительно сразу возникает вопрос о том, что внешний балансировщик один, а высунуть захочется много БД, тогда придется вести учет - какой базе какой порт принадлежит. Так как в микросервисной архитектуре базы постоянно создаются и пропадают, это точно станет головной болью. Поэтому возникла идея запустить внутри кластера web-клиент. Ничего высовывать не придется. Все, кому это понадобится, смогут подключаться к внутренним базам без заморочек с доступом.
Так как у нас K8s, значит нужно найти что-то упакованное в Docker. Гугление первой строчкой выдало https://hub.docker.com/r/moritanosuke/docker-pgweb/, но он у меня с ходу не завёлся. Плюс насторожило, что проект совсем не развивается. Последнее изменение было сделано 2 года назад. Второй строчкой оказался https://hub.docker.com/r/fenglc/pgadmin4/. Его-то я и заиспользовал. Локально докер завелся сразу. Порадовал довольно продвинутый UI
dashboard
Быстренько накидал Helm-чарт и закинул его в K8s. Тут получилось не так гладко. Сервис сходу не стартанул и стал валиться с ошибкой

pgAdmin4 init process done. Ready for start up.
Traceback (most recent call last):
File "pgAdmin4.py", line 95, in
server_port = int(port)
ValueError: invalid literal for int() with base 10: 'tcp://10.103.24.187:5050'

Оказалось, что проблема известная: https://github.com/fenglc/dockercloud-pgadmin4/issues/17 - 
и решается добавлением переменной окружения PGADMIN_PORT. В Helm-чарте это получилось так:

- name: PGADMIN_PORT
value: "5050"

Странно, конечно, почему эта проблема не вылезла при локальном старте докера, но разбираться было уже лень.
Так как сервис необходим только для работы на тестовом стенде, то заморачиваться с выделением отдельного DNS-имени и настройкой Ingress-ов не хотелось, поэтому решил высунуть сервис, через уже запущенный Zuul. Но не тут то было. Оказалось, что данный проект использует абсолютные пути. То есть высунуть его можно только в корень. Даже дефект на эту тему есть: https://github.com/fenglc/dockercloud-pgadmin4/issues/19.  Пришлось высовывать из корня zuul-а. Хорошо, что у нас корень еще свободен, но жить, конечно, так нельзя, придется просить отдельное имя.
В целом получилось хорошо. Теперь можно делать любой запрос к тестовым базам. Коллеги тоже довольны:)