Когда речь заходит о тяжелой промышленности и технологиях в ней, в большинстве случаев мы ожидаем услышать Java, а может быть и Java EE, или наоборот что-то очень низкоуровневое. Именно такие предположения я чаще всего слышу от друзей, когда рассказываю, где работаю.
Однако, в реальности всё немного иначе и на практике мои коллеги используют множество технологий. Это может быть C++ для низкоуровневой обработки данных или скрипты синхронизации, написанные на Bash, различные десктопные приложения, написанные на C# или PyQt, часто можно встретить серверы на NodeJS, но редко — приложения на R. Весь фронт мы стараемся писать на TypeScript (JavaScript), но иногда это реализовано через серверный рендеринг шаблонов и простой HTML. Это еще раз подтверждает очевидный факт — для каждой задачи свой инструмент.
Но что же с Python? В действительности мы часто используем Python. В этой статье я расскажу о том, зачем на металлургическом комбинате Python и с какими проблемами я столкнулся при работе над задачами.
Меня зовут Лев и я — Python разработчик в группе компаний НЛМК. Так вышло, что в зону моей ответственности попадает много различных проектов, а также в нее входит помощь коллегам с реализацией их задач. Поэтому моя работа сильно отличается от классической разработки по готовому техническому заданию. В этом есть свои плюсы и минусы, но в любом случае это интересно и, безусловно, это огромный опыт для меня.
Я мог бы рассказать про какой-то интересный случай использования Python для решения конкретной проблемы производства с применением Deep Learning. Но решил посвятить эту статью иным проблемам, а именно проблемам унификации подходов при разработке и внутреннего роста компетенций разработчиков.
Если сегментировать нашу разработку на Python, то ее можно разделить на три основные группы: оптимизация производства, внутренние продукты и интеграции.
Оптимизация производства
Больше нескольких десятков разработчиков в дюжине команд, которые ежедневно работают над конкретными продуктами, цель которых — улучшить производство. Достичь этого можно различными методами — ускорить процессы, повысить безопасность участка, снизить количество брака, снизить количество вредных выбросов или уменьшить количество ручного труда. В данной группе значительно преобладает количество Python разработчиков и данное направление очень важное по многим причинам.
Внутренние продукты
Кроме оптимизации производства, существуют различные проекты, связанные с внутренними продуктами. Над этими задачами работают несколько команд.
Ежедневно появляются новые запросы на перенос данных из одного хранилища в другое, на получение доступа из одной подсистемы к другой, на потоковую обработку тех или иных данных. Большинство таких запросов выливаются либо в отдельные микросервисы, либо в полноценные внутренние продукты. Как правило, далеко не все потребности производства мы можем закрыть готовыми продуктами. Причин на это множество: от невозможности использовать готовое решение из-за огромного объема обрабатываемых данных до банального отсутствия таких решений на рынке.
Интеграции
В данную группу попадают разработчики, которые привлекаются для интеграции с различными системами. Например, для взаимодействия с SAP. Из-за того, что в данных направлениях используются специфические технологии, такие как IronPython и другие, это заслуживает отдельной статьи, поэтому оставим на будущее.
Анализ
Работу над своими задачами я начал с анализа технологического ландшафта и проектов.
Концептуально все подсистемы комбината делятся на 2 части — технологический и бизнес сегменты. Возможен только односторонний доступ из технологического сегмента в бизнес-сегмент, обратное невозможно из соображений информационной безопасности.
Ландшафт
Технологический сегмент — это всё, что связано с тем или иным агрегатом и его подсистемами управления.
Бизнес-сегмент — зона, в которой мы можем строить связи между различными подсистемами. Именно об этой части и пойдет дальнейшее повествование.
Изначально, компоненты бизнес-сегмента представляли из себя набор виртуальных машин, выделяемых под тот или иной проект/команду с дальнейшей эксплуатацией систем. Если проектов немного, тогда никаких проблем в данной схеме не возникает. Однако, ранее я говорил, что у нас несколько десятков команд. Если каждой команде под каждый продукт будет выделяться отдельная виртуальная машина, то очень быстро это превратится в нечто не поддерживаемое. Для решения данной проблемы в НЛМК была создана Единая Цифровая Платформа, которая предоставляет большинство систем как сервисы.
Если коротко, то произошло примерно следующее:
-
код приложений переехал в GitLab репозитории;
-
bash скрипты запуска приложений превратились в Docker образы с хранением в jFrog Artifactory;
-
виртуальные машины превратились в OpenShift кластера;
-
файлы с логами переехали в ELK/Sentry/Jaeger;
-
файловые хранилища переехали в S3 на базе Minio и HDFS;
-
Basic Auth авторизация мутировала в SSO + ADFS на базе KeyCloak.
Также как сервис стали доступны следующие системы:
-
Kafka
-
NiFi
-
SonarQube
-
Matomo, аналог “Яндекс.Метрики”
-
Prometheus/VictoriaMetrics
-
Grafana
Проекты
По результатам анализа нескольких десятков различных продуктов стало понятно, что значительная часть Python продуктов, которые посвящены улучшению производства, имеют общую структуру:
-
API — отвечает за авторизацию и общую полезную нагрузку
-
Consumer — часть, которая потребляет данные из очередей, чаще всего из Kafka
-
Model — математическая модель, которая позволяет решить ту или иную проблему
Описанную структуру имеют не все проекты, некоторые имеют более сложную структуру и иные механики или, наоборот, более простую. В части проектов используется Machine Learning модель, а где-то используется Deep Learning.
Проблематика
После анализа проектов и сбора информации, я смог сегментировать технические проблемы по следующим группам:
-
всё, что связанно с выбором библиотек и подходов;
-
качество кода — нарушение принципов программирования и общепринятых практик;
-
безопасность — авторизация, уязвимости — Cmd-Injection, SQL-Injection, etc …
-
сопровождение — тесты, миграции, метрики, логи;
Выявить технические проблемы — это только часть решения. Другой проблемой стало то обстоятельство, что команд много, у каждой свои подходы, а уровень компетенций команд сильно отличался друг от друга. Простое требование использовать ту или иную технологию не привело бы к решению действительных проблем.
За время анализа я встретил значительное количество странного кода. Один раз я встретил Python код, который был полностью написан в стилистике Java. Иной раз это был файл на несколько тысяч строк с несколькими десятками функций, которые использовали глобальные переменные в качестве аргументов. В некоторых проектах разом использовались все парадигмы параллелизма и конкуренции — многопоточность, многопроцессорность и асинхронность. И многое-многое другое.
Все мы когда-то писали что-то странное и сложно объяснимое, однако, в рамках рабочих проектов такой подход вызывает много сложностей и болей. Сказать, что данный код не является поддерживаемым, это ничего не сказать. Как мне кажется, один из важнейших критериев любого проекта — его сопровождаемость. Поддерживать и как-либо модифицировать проект без документации, тестов и понятной структуры крайне сложно, а что более важно — дорого. Мы тратим время разработчика не на написание кода, а на понимание как этот код работает и зачем здесь тот или иной атрибут.
Пути решения
Потратив значительное время на размышления и попытки как-то всё систематизировать, я пришел к двум мыслям: что наилучшим решением будет двигаться поступательно и что быстрого решения нет.
Самым первым и простым действом было создать пространство в Confluence и заполнить его базовой информацией. Затем создать проекты, которые показывают как работать с той или иной технологией правильно. После был сформирован план — определить стек технологий, разработать стандарты и начать развивать Python-сообщество.
Стандартизация
Стало очевидно, для снижения временных издержек необходимо прийти к некоторым стандартным практикам. Начал с простого, со списка принципов разработки — DRY/DIE, KISS, YAGNI, SOLID и Дзен Python. Следующими шагами стали введение общего Style Guide и написание различных внутренних статей о повышении качества кода.
С каждой неделей статей становилось всё больше и на данный момент в нашем пространстве содержится около 100 статей, посвященных разработке на Python. Кроме Python, конечно же, есть мир и вокруг, поэтому мы уделяем время и другим вопросам:
-
Как правильно писать Dockerfile?
-
Зачем использовать [git|github|gitlab…] flow?
-
Чем отличается Pod в OpenShift от виртуальной машины?
-
Что такое идемпотентный метод в RESTful приложениях?
Статьи — это прекрасно, но нужны инструменты, которые помогут разработчикам избежать рутинных задач. Для решения данной проблемы мы создали типовой pyproject.toml, в котором описали настройки для различных линтеров и форматтеров. Для запуска всех необходимых проверок у нас рекомендуется использовать pre-commit. Примерный список плагинов, которые мы используем в pre-commit:
-
trailing-whitespace
-
end-of-file-fixer
-
check-yaml
-
check-added-large-files
-
check-ast
-
mypy
-
black
-
isort
-
pylint
-
darglint
-
bandit
-
vulture
Пример pyproject.toml
# … [tool.black] multi-line-output = 3 # Многострочный вывод (3 - вертикально висячий) для совместимости с isort. [tool.isort] multi_line_output = 3 # Многострочный вывод (3 - вертикально висячий). include_trailing_comma = true # Включает конечную запятую перед скобками в многострочных импортах. force_grid_wrap = 0 # Принудительное оборачивание импортов в таблицу. use_parentheses = true # Использует скобки вместо слеша для продолжения линии, превысившей лимит. ensure_newline_before_comments = true # вставляет пустую строку перед комментарием после импорта. line_length = 88 # Максимальная длина линии импорта. [tool.pylint.messages_control] disable = "C0330, C0326" # Отключить некоторые проверки для совместимости с SonarQube [tool.pylint.format] max-line-length = "88" # Максимальное количество символов в одной строке. [tool.pytest.ini_options] minversion = "6.2.5" # Версия pytest, начиная с которой будет работать этот файл (лучше указывать версию, которая установлена у вас). testpaths = ["tests"] # Путь до папки с тестами (в данном примере папка лежит в корне). [tool.coverage.run] branch = true # Включает анализ покрытия по веткам, а не по стокам кода. omit = [".venv/*", "tests/*"] # Файлы, которые не должны быть в отчетности или изменяться. [tool.coverage.report] omit = [".venv/*", "tests/*"] # Файлы, которые не должны быть в отчетности. [tool.mypy] ignore_missing_imports = true # …
Кроме написания статей и ответов на конкретные вопросы, весь функционал, который может быть использован в других проектах, выделяется в отдельные библиотеки. Это в значительной степени снижает дублирование кода. Создаваемые библиотеки развиваются по модели OpenSource внутри компании, что позволяет привлекать значительное количество разработчиков для повышения качества кода.
Рекомендуемый стек
Разработка |
Дополнительное |
Web server: aiohttp, fastapi, flask |
Test libs: pytest, unittest |
Мы стараемся придерживаться использования отдельных библиотек, а не “комбайнов”. Это одна из причин, почему в рекомендуемом стеке технологий нет Django. Django — великолепный фреймворк. Но, к сожалению, данный фреймворк не подходит под наши цели и задачи.
Приложения, которые мы пишем, делятся на два типа:
Для разрабатываемых приложений важна отказоустойчивость и возможность горизонтального масштабирования, к тому же почти все приложения запускаются в OpenShift. Чтобы реализовать подобные решения желательно придерживаться stateless концепции, а для оптимальной утилизации ресурсов наилучшим решением будет использовать асинхронные возможности Python, которые появились с 3.7 версии.
Проведя исследование и сравнив различные библиотеки и фреймворки, я остановился на двух представителях — AIOHTTP и FastAPI. Однако, в рамках наших проектов приоритет отдается именно AIOHTTP.
FastAPI. Всё же это фреймворк, хоть и легковесный. С другой стороны, как только заходит речь про асинхронную сторону вопроса, например, получать события из Kafka и отправлять пользователю по WS/SSE — с FastAPI появляются сложности.
В подавляющем большинстве случаев в качестве WEB сервера и клиента мы используем AIOHTTP. На текущем этапе развития данный пакет закрывает большую часть потребностей. Кроме того, большинство пакетов из aio-libs регулярно используются в нашей повседневной работе. Для работы с базами данных и миграциями применяются SQLAlchemy и Alembic соответственно.
После того, как был сформирован рекомендуемый стек и заполнены основные разделы, коллеги стали выражать большую заинтересованность и задавать вопросы. Таким образом стало формироваться внутреннее Python-сообщество.
Внутреннее сообщество
На данный момент, наше Python-сообщество состоит из трех частей:
-
пространство в Confluence — много статей и ссылок, книги
-
тематические чаты — у кого-то есть вопрос или смешной мем
-
регулярные еженедельные миты — в неформальной обстановке каждый участник может рассказать про свой продукт или послушать про использование конкретной технологии
Для поддержания интереса к технологиям и развития команд проводятся отдельные тематические сессии, доклады и презентации.
Заключение
От момента первых шагов прошло немногим меньше года и я могу с уверенностью сказать — описанный подход работает! Компетенции команд значительно выросли за этот период, а разработчики стали более вовлеченными в сообщество — пишут статьи, готовят доклады, спорят и вместе обсуждают технические решения. Систематизировались подходы и продолжают унифицироваться решения, как следствие, код становится чище, функциональнее и безопаснее.
Однако у данного подхода есть и минусы — это сложно, отнимает много сил и требует достаточного количества знаний, и, к сожалению, все процессы проходят медленнее, чем хотелось бы.
P.S.:
Выражаю благодарность коллегам из ЕЦП и ЦТ за помощь и участие.
ссылка на оригинал статьи https://habr.com/ru/company/nlmk/blog/645943/
Добавить комментарий