В GetInData мы создаем эластичные платформы MLOps в соответствии с потребностями наших клиентов. Одной из ключевых функций платформы MLOps является возможность отслеживать эксперименты и управлять обученными моделями в виде репозитория моделей. Наша гибкая платформа позволяет нашим клиентам выбирать лучшие в своем классе технологии для обеспечения этих функций, независимо от того, являются ли они открытым исходным кодом или коммерческими. MLFlow — один из инструментов с открытым исходным кодом, который отлично подходит для этой цели. В этом сообщении блога я покажу вам, как развернуть MLFlow поверх Cloud Run, Cloud SQL и Google Cloud Storage, чтобы получить полностью управляемый бессерверный сервис для отслеживания экспериментов и хранилища моделей. Сервис будет защищен с помощью авторизации OAuth2.0 и SSO с учетными записями Google.

Предпосылки

  • доступ к облачной платформе Google
  • учетная запись службы, предназначенная для запуска MLFlow в Cloud Run
  • Идентификатор клиента OAuth 2.0 и секреты клиента для авторизации
  • Установка Docker (для сборки образа, но это можно сделать и в CI)

Настройка цели

Окончательная настройка, описанная в этом сообщении блога, будет выглядеть так:

Одним из наиболее важных аспектов здесь является использование OAuth2-Proxy в качестве промежуточного уровня в контейнере Cloud Run. Использование встроенного механизма авторизации Cloud Run (параметр «Требовать аутентификацию. Управление авторизованными пользователями с помощью Cloud IAM») можно использовать только при выполнении программных запросов. Когда необходимо сделать интерактивные запросы к веб-интерфейсу пользователя (например, в случае с веб-интерфейсом MLFlow), пока нет встроенного способа аутентификации в службе Cloud Run.

Шаг 1. Установка соответствующих разрешений

Учетная запись службы, которая будет использоваться для запуска MLFlow в Cloud Run, должна иметь следующие настроенные разрешения:

  • Облачный SQL-клиент
  • Secret Manager Secret Accessor (опционально это можно установить непосредственно для каждого секрета отдельно)
  • Средство просмотра объектов хранения

Шаг 2. Предварительная настройка клиента OAuth 2.0

Чтобы интегрировать авторизацию OAuth 2.0 с Cloud Run, OAuth2-Proxy будет использоваться в качестве прокси поверх MLFlow. OAuth2-Proxy может работать со многими поставщиками OAuth, включая GitHub, GitLab, Facebook, Google, Azure и другие. Использование поставщика Google позволяет легко интегрировать SSO в интерактивный пользовательский интерфейс MLFlow, а также упрощает авторизацию между службами с использованием токенов-носителей. Благодаря этому MLFlow будет безопасно доступен из своего Python SDK, например. когда выполняется задание по обучению модели.

Идентификатор клиента/секрет клиента можно создать, посетив https://console.cloud.google.com/apis/credentials/oauthclient и настроив новый клиент как веб-приложение, как показано ниже. Во время предварительной настройки поле URI авторизованного перенаправления останется пустым. Позже он будет заполнен URL-адресом развернутого экземпляра Cloud Run.

После создания отобразятся идентификатор клиента и секрет клиента. Они должны быть надежно сохранены для последующей настройки.

Шаг 3. Настройка Cloud SQL/облачного хранилища

Конфигурации Cloud SQL и Cloud Storage просты и не будут рассмотрены в этом сообщении блога. Официальная документация GCP о соединении Cloud SQL с Cloud Run хорошо описывает все аспекты этой настройки.

  • В зависимости от нагрузки экземпляры Cloud SQL можно масштабировать по мере необходимости. Для небольших развертываний должно быть достаточно стандартного экземпляра с 1 виртуальным ЦП.
  • Важно, чтобы и Cloud SQL, и корзина GCS находились в том же регионе GCP, что и экземпляр Cloud Run, чтобы свести к минимуму задержку и затраты на передачу данных.
  • Прежде чем использовать Cloud SQL, необходимо создать базу данных с именем mlflow.
  • Можно использовать любой вариант базы данных, поддерживаемый MLFlow.

Рекомендуется иметь отдельную корзину GCS для использования MLFlow.

Шаг 4: Сохранение конфигурации в Secret Manager

И MLFlow, и OAuth2-Proxy требуют конфигурации, содержащей конфиденциальные данные (например, секрет клиента OAuth2.0, строку подключения к базе данных). Эта конфигурация будет сохранена в Secret Manager, а затем смонтирована в службе Cloud Run через переменные среды и файлы. Должны быть созданы два секрета:

  1. Секрет со строкой подключения к Cloud SQL (для MLFlow)
  2. Секрет с файлом конфигурации прокси-сервера OAuth2.0 в соответствии с шаблоном ниже:
email_domains = [
    "<SSO EMAIL DOMAIN>"
]
provider = "google"
client_id = "<CLIENT ID>"
client_secret = "<SECRET>"
skip_jwt_bearer_tokens = true
extra_jwt_issuers = "https://accounts.google.com=32555940559.apps.googleusercontent.com"
cookie_secret = "<COOKIE SECRET IN BASE64>"

Секрет cookie может быть сгенерирован с помощью head -c 32 /dev/urandom | base64.

Дополнительный параметр издателей JWT необходим для правильной работы авторизации между службами.

Шаг 5. Подготовка образа MLFlow Docker

Образ Docker для запуска MLFlow с OAuth2-Proxy будет основан на общедоступном образе Docker MLFlow от GetInData из: https://github.com/getindata/mlflow-docker (поскольку MLFlow еще не предоставляет его). Требуются две модификации:

  1. Установка OAuth2-Proxy.
  2. Установка Тини входа.

OAuth2-Proxy будет уровнем авторизации для MLFlow, который будет выполняться в фоновом процессе контейнера. Tini используется для управления точкой входа контейнера.

Файл Docker

FROM gcr.io/getindata-images-public/mlflow:1.22.0
ENV TINI_VERSION v0.19.0
EXPOSE 4130

RUN apt update && apt install -y curl netcat && mkdir -p /oauth2-proxy && cd /oauth2-proxy && \
    curl -L -o proxy.tar.gz https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v6.1.1/oauth2-proxy-v6.1.1.linux-amd64.tar.gz && \
    tar -xzf proxy.tar.gz && mv oauth2-proxy-*.linux-amd64/oauth2-proxy . && rm proxy.tar.gz && \
    rm -rf /var/lib/apt/lists/*

ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini

COPY start.sh start.sh
RUN chmod +x start.sh

ENTRYPOINT ["/tini", "--", "./start.sh"]

start.sh

#!/usr/bin/env bash
set -e

mlflow server --host 0.0.0.0 --port 8080 --backend-store-uri ${BACKEND_STORE_URI} --default-artifact-root ${DEFAULT_ARTIFACT_ROOT} &
while ! nc -z localhost 8080 ; do sleep 1 ; done
/oauth2-proxy/oauth2-proxy --upstream=http://localhost:8080 --config=${OAUTH_PROXY_CONFIG} --http-address=0.0.0.0:4180 &

wait -n

Подробности об этой точке входа можно найти в разделе «Шаг 6» ниже. После создания образа его необходимо отправить в GCR, чтобы Cloud Run мог его развернуть.

Шаг 6. Развертывание MLFlow с OAuth2-Proxy в Cloud Run

После создания образа Docker его можно развернуть в Cloud Run.

Настройка параметров Cloud Run

Стандартные параметры должны быть установлены следующим образом:

  • URL-адрес изображения контейнера — путь к изображению, отправленному в GCR на шаге 5.
  • Контейнерный порт — 4180
  • Команда контейнера / аргументы контейнера — можно оставить пустыми, так как они настроены в Dockerfile.
  • Распределение ЦП и цены — см. ниже
  • Емкость — 1GiB памяти и 1 vCPU достаточно для небольшой нагрузки. Можно масштабировать по мере необходимости
  • Время ожидания запроса — 300 с (по умолчанию).
  • Максимальное количество запросов на контейнер — 80 (по умолчанию). Может корректироваться в зависимости от нагрузки.
  • Автомасштабирование — мин. 0, макс. Н (в зависимости от нагрузки).
  • Ingress — Разрешить весь трафик
  • Аутентификация — разрешить вызовы без аутентификации — они будут аутентифицированы OAuth2-Proxy.
  • Подключения / Подключения к Cloud SQL — выберите экземпляр Cloud SQL, созданный ранее.
  • Учетная запись безопасности/службы — выберите учетную запись службы, созданную для этого развертывания ранее.

Важное примечание о распределении ЦП и настройках ценообразования

Из кода точки входа видно, что внутри одного контейнера одновременно работают два сервиса. Интерфейсной службой является OAuth2-Proxy, а внутренней службой является фактический экземпляр MLFlow. Из-за этой конфигурации точке входа необходимо дождаться запуска сервера MLFlow, прежде чем переходить к следующему шагу, который запускает OAuth2-Proxy. Без цикла ожидания служба не запустится должным образом, а доступ к развернутой службе Cloud Run приведет к ошибке Bad Gateway: Error proxying to upstream server:

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

  • ЦП выделяется только во время обработки запроса — вы платите за каждый запрос и только тогда, когда экземпляр контейнера обрабатывает запрос.
  • ЦП всегда выделяется — вы платите за весь жизненный цикл экземпляра контейнера.

Первый вариант, используемый по умолчанию, который является «наиболее бессерверным», означает, что развернутый контейнер будет получать процессорное время только при выполнении запроса. Поскольку MLflow является фоновым процессом, он получает процессорное время для запуска сервера только тогда, когда интерфейсный процесс (в данном случае это OAuth2-Proxy) обрабатывает HTTP-запрос. Поскольку запросы к OAuth2-Proxy короткие, ЦП будет вытеснен, и сервер MLFlow не сможет запуститься. Принудительное ожидание точки входа для OAuth2-Proxy перед запуском сервера MLFlow предотвращает состояние гонки, когда сервер MLFlow может запускаться или нет, в зависимости от того, сколько циклов ЦП контейнер получил во время инициализации.

Второй вариант более затратный, так как ЦП, выделенный для контейнера, всегда доступен. Этот вариант хорошо подходит для служб, которые выполняют внутреннюю обработку (например, потребляют сообщения Pub/Sub). Хотя постоянное выделение ЦП является более дорогим, оно предотвратит дросселирование сервера MLFlow, и его процесс сможет правильно инициализироваться без выполнения HTTP-запросов к контейнеру (за исключением первого при масштабировании до 0). Однако более надежно сначала инициализировать все процессы в контейнере, прежде чем сообщать о состоянии готовности службе-исполнителю (здесь: Cloud Run).

Настройка секретов

Секреты, созданные на шаге 4, необходимо смонтировать в службе Cloud Run. Конфигурация показана ниже:

Строка подключения к базе данных (первый секрет) должна быть смонтирована как переменная среды BACKEND_STORE_URI — она будет использоваться сервером MLFlow. Конфигурация для OAuth2-Proxy должна быть смонтирована как том, а указанный здесь путь должен быть передан в переменной среды OAUTH_PROXY_CONFIG (см. setup.sh выше, чтобы понять передачу этих параметров).

Шаг 7: Завершение настройки OAuth2.0

На шаге 2 клиент OAuth 2.0 был предварительно настроен, но поле URI авторизованного перенаправления осталось пустым. После развертывания службы Cloud Run ее URL-адрес будет доступен в пользовательском интерфейсе:

Этот URL-адрес необходимо указать в конфигурации клиента OAuth 2.0 следующим образом:

https://mlflow-.a.run.app/oauth2/callback

так что перенаправления будут работать правильно.

Доступ к бессерверному развертыванию MLFlow

После развертывания к службе MLFlow можно получить доступ либо из браузера, либо из серверной службы.

Браузер

Развернутая служба Cloud Run активирует поток OAuth 2.0 для авторизации пользователя.

2.

3.

Сервис-сервис

При использовании заданий CI/CD, curl или Python SDK для MLFlow запросы необходимо авторизовать, передав HTTP-заголовок Authorization с токеном Bearer, предоставленным поставщиком токенов OAuth 2.0, которым в данном случае является Google. (обратите внимание, что не все провайдеры, поддерживаемые OAuth2-Proxy, позволяют вам получить токен между серверами).

Чтобы получить токен, можно использовать команду. Убедитесь, что учетная запись, запрашивающая токен, имеет соответствующие разрешения в проекте GCP (должно быть достаточно Viewer).

curl -X GET https://<redacted>.a.run.app/api/2.0/mlflow/experiments/list -H "Authorization: Bearer $(gcloud auth print-identity-token)"
{
  "experiments": [
    {
      "experiment_id": "0",
      "name": "Default",
      "artifact_location": "gs://<redacted>/0",
      "lifecycle_stage": "active"
    },
    {
      "experiment_id": "1",
      "name": "testasdasd",
      "artifact_location": "gs://<redacted>/1",
      "lifecycle_stage": "active"
    }
  ]
}

Для авторизации Python SDK требуется только передать сгенерированный токен в переменную MLFLOW_TRACKING_TOKENenvironment, никаких изменений исходного кода не требуется (пример взят из документации MLFlow):

MLFLOW_TRACKING_TOKEN=$(gcloud auth print-identitytoken)
MLFLOW_TRACKING_URI=https://<redacted>.a.run.app python sklearn_elasticnet_wine/train.py

Краткое содержание

Я надеюсь, что это руководство помогло вам быстро развернуть масштабируемые экземпляры MLFlow на Google Cloud Platform. Удачного (бессерверного) отслеживания экспериментов!

Особая благодарность Mateusz Pytel и Mariusz Wojakowski за помощь в исследовании этого развертывания.

Вам понравился наш пост? Если вы хотите узнать больше, скачайте нашу бесплатную электронную книгу «MLOps: Power Up Machine Learning Process. Введение в Vertex AI, Snowflake и dbt Cloud».

Автор блога: Marcin Zabłocki — MLOps Architect

Первоначально опубликовано на https://getindata.com.