MLOps Path

3.2.3 · блок 3

Пайплайн качества: lint, test, build, push

Пайплайн качества: lint, test, build, push

Зачем это нужно

Зелёный Jenkins — не самоцель. Цель — гарантировать, что каждый артефакт в registry прошёл согласованный набор проверок. Для ML-сервиса недостаточно «pytest прошёл»: нужны проверки контракта API, размера образа, smoke-тест predict и воспроизводимости зависимостей.

Этот урок собирает типовой quality pipeline, который вы повторите в практикуме модуля 3.2 и будете расширять до Helm и Argo CD.

Основные идеи

Lint — дешёвая первая линия. Статический анализ ловит опечатки, неиспользуемый код, нарушение стиля до запуска тестов. Для Python: ruff, flake8, mypy (по зрелости проекта). Stage должен быть быстрым (< 1 мин).

Test — поведение системы. Unit-тесты функций препроцессинга; интеграционные — API с TestClient FastAPI; для модели — фиксированный набор входов и ожидаемых форматов выхода (не обязательно точное совпадение float, но shape и dtype). Храните маленькие fixture-файлы в tests/fixtures/, не полный датасет.

Build — воспроизводимый образ. docker build с --pull периодически обновляет базовый образ (patch security). Тегируйте однозначно:


environment {
    IMAGE = "registry.example.com/ml/scorer"
    TAG   = "${env.GIT_COMMIT.take(7)}-${env.BUILD_NUMBER}"
}

Не перезаписывайте тег latest без контроля — в prod деплоят immutable tags.

Push — только после успеха. Registry login через credentials; push в stage после test и build:


stage('Push') {
    steps {
        withCredentials([usernamePassword(
            credentialsId: 'registry-creds',
            usernameVariable: 'REG_USER',
            passwordVariable: 'REG_PASS'
        )]) {
            sh '''
                echo "$REG_PASS" | docker login registry.example.com -u "$REG_USER" --password-stdin
                docker push ${IMAGE}:${TAG}
            '''
        }
    }
}

Parallel stages. Независимые проверки (lint + unit tests) можно запускать параллельно — короче feedback.

Artifacts и отчёты. JUnit XML из pytest публикуют через junit 'reports/*.xml' — Jenkins показывает тренд тестов. Coverage — опционально, но полезно для кода inference (не для notebook DS).

Smoke после build. Лёгкий контейнерный тест: docker run --rm image curl localhost/health — ловит «образ собрался, но приложение не стартует».

Как это выглядит на практике

Полный Jenkinsfile для сервиса scoring:

| Stage | Команда | Gate |

|-------|---------|------|

| Lint | `ruff check .` | fail → stop |

| Test | `pytest -q --junitxml=reports/junit.xml` | fail → stop |

| Build | `docker build -t $IMAGE:$TAG .` | fail → stop |

| Smoke | run container + HTTP GET /health | fail → stop |

| Push | `docker push $IMAGE:$TAG` | только main |

В PR собирают и тестируют, push — только при merge в main (блок when { branch 'main' }).

Артефакт для следующих модулей: образ scorer:abc1234-57 в registry. Helm chart (3.4) будет ссылаться на этот тег через values.yaml. Jenkins не обязан делать kubectl apply — часто достаточно push + обновление values в Git для Argo CD.

Практикум модуля: добейтесь зелёного Jenkinsfile с минимум lint → test → build → push.

Что сделать после занятия

Официальные материалы

Открыть интерактивную версию