Когда речь заходит о тяжелой промышленности и технологиях в ней, в большинстве случаев мы ожидаем услышать 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
Web client: aiohttp, requests
DB: aiopg, asyncpg
ORM: SQLAlchemy, alembic
(De-)Serialization: marshmallow, pydantic
Configuration: dynaconf, pydantic
Template: jinja2
Redis: aioredis, redis-py
RabbitMQ: pika, aio-pika
Avro: avro, fastavro
Kafka: aiokafka, confluent-kafka
MinIO: aiobotocore, minio, boto3
SOAP Client: zeep
SOAP Server: spyne
JSON: orjson, ujson

Test libs: pytest, unittest
Mock: unittest.mock
Fake data: faker, factory_boy
Coverage: coverage, pytest-cov
Exceptions: sentry-sdk
Metrics: prometheus-client, aioprometheus
Trace: jaeger-client, opentelemetry-python
Docs: pydoc3, sphinx

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

Приложения, которые мы пишем, делятся на два типа:

Простые приложения, реализующие API интерфейс
Простые приложения, реализующие API интерфейс
Более сложные приложения
Более сложные приложения

Для разрабатываемых приложений важна отказоустойчивость и возможность горизонтального масштабирования, к тому же почти все приложения запускаются в OpenShift. Чтобы реализовать подобные решения желательно придерживаться stateless концепции, а для оптимальной утилизации ресурсов наилучшим решением будет использовать асинхронные возможности Python, которые появились с 3.7 версии.

Проведя исследование и сравнив различные библиотеки и фреймворки, я остановился на двух представителях - AIOHTTP и FastAPI. Однако, в рамках наших проектов приоритет отдается именно AIOHTTP. 

FastAPI. Всё же это фреймворк, хоть и легковесный. С другой стороны, как только заходит речь про асинхронную сторону вопроса, например, получать события из Kafka и отправлять пользователю по WS/SSE - с FastAPI появляются сложности.

В подавляющем большинстве случаев в качестве WEB сервера и клиента мы используем AIOHTTP. На текущем этапе развития данный пакет закрывает большую часть потребностей. Кроме того, большинство пакетов из aio-libs регулярно используются в нашей повседневной работе. Для работы с базами данных и миграциями применяются SQLAlchemy и Alembic соответственно.

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

Внутреннее сообщество

На данный момент, наше Python-сообщество состоит из трех частей:

  • пространство в Confluence - много статей и ссылок, книги

  • тематические чаты - у кого-то есть вопрос или смешной мем

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

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

Заключение

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

Количество просмотров страниц Confluence в течение года
Количество просмотров страниц Confluence в течение года

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

P.S.:

Выражаю благодарность коллегам из ЕЦП и ЦТ за помощь и участие.

Комментарии (11)


  1. asvechkar
    17.01.2022 15:20
    +4

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

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


    1. LevZaplatin Автор
      17.01.2022 15:32

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


      1. Kekushiftkey
        17.01.2022 17:11

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

        пока что сам пользую python в jupyter notebooks, но жесть как не хватает нормальной бд, все лежит в экселях((


  1. zqqraa
    17.01.2022 17:39

    на сколько процентов сократились издержки или увеличилась прибыль(выручка, любой показатель роста) организации после всех этих изменений? Как считали экономическую выгоду?


    1. LevZaplatin Автор
      17.01.2022 18:37
      +1

      Издержки моего рабочего времени сократились процентов на 20-30%.

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

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


    1. overbanksy
      17.01.2022 19:28
      +1

      Прежде всего спасибо за интересный вопрос.

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

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

      2. Качество разработки позитивно влияет на лояльность пользователей

      3. Уровень разработчиков влияет на качество разработки

      4. Доклады, статьи, ретро влияют на уровень и мотивацию разработчиков

      Ну и всегда приятно пообщаться с единомышленниками, разумеется)


  1. Usul
    18.01.2022 07:20

    Спасибо за статью. Было интересно почитать о стеке технологий.

    Еще интереснее было бы узнать, как вы используете Prometheus. Храните ли вы в нем данные от "технологического сегмента" (тренды технологических параметров, полученные от АСУТП), и если да, как осуществляется интеграция?


    1. LevZaplatin Автор
      18.01.2022 17:23
      +2

      Это очень интересный вопрос, благодарю!

      Если говорить о метриках приложений, то используются Prometheus/VictoriaMetrics и Grafana для отображения данных.


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


      1. Usul
        18.01.2022 18:05

        Спасибо, будет очень интересно почитать такую статью.


  1. krids
    19.01.2022 10:20

    А сколько у вас девопсов на "вот это все", если не секрет ? За статью спасибо.


  1. akomiagin
    19.01.2022 15:15

    Спасибо за интересную статью! А можно чуть подробнее узначть про сравнительный анализ в части выбора в пользу AIOHTTP? И еще очень интересно почитать про то как вы пришли к использованию orjson, ujson для сериализации json.