はじめに
MLOpsを構成する機能要素としてモニタリングがあります。推論精度の変化やデータのドリフトの発生を捉えてモデルの再学習や再開発に繋げる役割を担うことになる重要な要素と考えています。このモニタリングを果たすOSSとしてEvidently AIがあり、様々なメトリクスを算出してレポートを作成できるライブラリがあります。
Evidently AIの使い方やサンプルはGitHubで公開されています。この中にはメトリクスの状況をPrometheusとGrafanaを利用してリアルタイムに可視化するReal-time ML monitoring with Evidently and Grafanaというサンプルがあります。この公開サンプルはdocker composeで実行するものになっていますが、今回はこれをKubernetes(minikube)上で動かすことにチャレンジしてみました。
システム環境
- PC: Windows 11
- WSL2: Ubuntu 20.04
- Docker 20.10.17
- Python 3.8.10
- Amazon Elastic Container Registry (ECR)
- AWS CLIがインストールされていることが必要です。
今回はローカルPC(Windows)にWSL2+Docker(Ubuntu 20.04)の環境を用意して、そこでminikubeを動かすことにしました。また、Dockerイメージの管理のためにAWSのECRを使いました。ですが、EKSなど他のKubernetes環境でも動作すると思いますし、DockerのコンテナレジストリもECR以外のものであっても動作するかと思います。
- この公式サンプルでは機械学習の推論を行っていません。推論実行済のデータ(productionデータ)を定期的に転送しています。
- Evidently AIでモニタリングする際はモデル学習時のデータ(referenceデータ)が必要になります。これも事前に用意したデータを読み取る形にしています。
また、フォルダ構成は以下のようになります。
構築手順
1. 環境準備
Minikubeおよび他のツール群をインストールします。インストール方法はいくつかありますが、今回はasdf
というminikube、kubectlなどのバージョン管理が行えるツールを使うことにしました。
# asdfのインストール
git clone https://github.com/asdf-vm/asdf.git ~/.asdf
cat <<EOF >> ~/.bashrc
# asdf setting
. $HOME/.asdf/asdf.sh
. $HOME/.asdf/completions/asdf.bash
EOF
exec $SHELL -l
# minikubeのインストール
# 今回はバージョンとして1.23.2を指定
asdf plugin-add minikube
asdf list-all minikube # minikubeのバージョン一覧が出てくる
asdf install minikube 1.23.2
asdf global minikube 1.23.2
# kubectlのインストール
# 今回はバージョンとして1.23.2を指定
asdf plugin-add kubectl
asdf list-all kubectl # kubectlのバージョン一覧が出てくる
asdf install kubectl 1.23.2
asdf global kubectl 1.23.2
minikubeを起動します。
minikube start --kubernetes-version=v1.23.2 --memory=6g
minikubeからECRにアクセスするための認証設定を行います。
# 認証情報の設定
minikube addons configure registry-creds
上記コマンドを実行すると対話形式で入力を促されます。以下のように入力します。
最後にregistry-creds was successfully configured
と表示されればOKです。
Do you want to enable AWS Elastic Container Registry? [y/n]: y
-- Enter AWS Access Key ID: <アクセスキー>
-- Enter AWS Secret Access Key: <シークレットアクセスキー>
-- (Optional) Enter AWS Session Token: <空でEnter>
-- Enter AWS Region: <リージョン>
-- Enter 12 digit AWS Account ID (Comma separated list): <AWSアカウントID>
-- (Optional) Enter ARN of AWS role to assume: <空でEnter>
Do you want to enable Google Container Registry? [y/n]: n
Do you want to enable Docker Registry? [y/n]: n
Do you want to enable Azure Container Registry? [y/n]: n
入力が終わったら認証設定の有効化を行います。
# 認証設定の有効化
minikube addons enable registry-creds
GitHubからEvidently AI 公開サンプルをクローンします。
git clone https://github.com/evidentlyai/evidently.git
2. Prometheus/Grafanaで利用される設定ファイルの作成
公開サンプルの中で使用される設定ファイルは、クローンしたリポジトリのevidently/examples/integrations/grafana_monitoring_service/config/
フォルダにyaml形式で格納されています。
- grafana_dashboards.yaml
- grafana_datasources.yaml
- prometheus.yml
今回はこれをConfigMapとして定義します。一部の設定を今回の環境に合わせて変更しています。また元のyamlファイルあったコメントは除外しています。
まずはマニフェストファイルを格納するフォルダを作成します。
mkdir manifests
次に各マニフェストファイルを作成します。
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-grafana-dashboards
data:
grafana-dashboards.yaml: |
apiVersion: 1
providers:
- name: 'Evidently Dashboards'
orgId: 1
folder: ''
folderUid: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: false
options:
path: /opt/grafana/dashboards
foldersFromFilesStructure: true
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-grafana-datasources
data:
grafana-datasources.yaml: |
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://localhost:9090
isDefault: true
【変更箇所】
- url:
http://prometheus.:9090
->http://localhost:9090
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-prometheus
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
monitor: 'my-project'
scrape_configs:
- job_name: 'prometheus'
scrape_interval: 5s
static_configs:
- targets: ['localhost:9090']
- job_name: 'service'
scrape_interval: 10s
static_configs:
- targets: ['ai-monitor-clusterip:8085']
【変更箇所】
- alertingの設定は今回利用しないので削除
- job_name: 'service' の targets:
['evidently_service.:8085']
->['ai-monitor-clusterip:8085']
3. AIモニタのDockerイメージのビルド、およびECRへのプッシュ
次にEvidently AIが動作するAIモニタのDockerイメージをビルドします。最初にECRにリポジトリを用意しておき、ビルド&プッシュします。Dockerイメージを作成するために必要なDockerfileはEvidently AI 公開サンプルをそのまま利用します。
# AWSアカウントとリージョンの定義
# ご自身の環境に合わせて適宜修正してください。xxxxxxxxxxxxは12桁のAWSアカウントID
AWS_ACCOUNT=xxxxxxxxxxxx
AWS_REGION=ap-northeast-1
# ECRにログイン
# 下記コマンドをこのまま実行(--usernameを書き換えたりする必要なし)
# 認証の有効期限は12時間。それ以上経過した場合は再度コマンドを実行してください。
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS \
--password-stdin $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com
# ECRへリポジトリの作成
# `ai-monitor`というリポジトリにします。
aws ecr create-repository --repository-name ai-monitor
# Evidently AIサンプルのフォルダに移動
cd evidently/examples/integrations/grafana_monitoring_service
# ai-monitorイメージの作成
# dockerイメージ名はリポジトリ名と同じである必要があります。
docker build -t $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/ai-monitor .
# イメージのプッシュ
docker push $AWS_ACCOUNT.dkr.ecr.$AWS_REGION.amazonaws.com/ai-monitor
# ホームディレクトリに戻る
cd ~
4. Kubernetesのマニフェストファイルの作成
Kubernetesの各種リソース(Pod, Deployment, Service)を作成するために、マニフェストを作成します。
- prometheus/grafana用のリソースのマニフェスト
- prometheus/grafanaの設定情報はConfigMapで定義したものを使用します。
- grafanaのダッシュボード定義は、jsonファイルをvolumeから読み取る形にしています。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: prometheus-grafana
name: prometheus-grafana
spec:
replicas: 1
selector:
matchLabels:
app: prometheus-grafana
template:
metadata:
labels:
app: prometheus-grafana
spec:
containers:
- image: prom/prometheus
name: prometheus
args: ["--config.file=/etc/prometheus/prometheus.yml", "--storage.tsdb.path=/prometheus"]
ports:
- containerPort: 9090
volumeMounts:
- name: prometheus-config
mountPath: /etc/prometheus
- image: grafana/grafana
name: grafana
securityContext:
runAsUser: 472
ports:
- containerPort: 3000
volumeMounts:
- name: grafana-datasources
mountPath: /etc/grafana/provisioning/datasources
readOnly: true
- name: grafana-dashboards
mountPath: /etc/grafana/provisioning/dashboards
readOnly: true
- name: dashboards
mountPath: /opt/grafana/dashboards
volumes:
- name: prometheus-config
configMap:
name: cm-prometheus
- name: grafana-datasources
configMap:
name: cm-grafana-datasources
- name: grafana-dashboards
configMap:
name: cm-grafana-dashboards
- name: dashboards
hostPath:
path: /data/grafana_monitoring_service/grafana-dashboards
type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
labels:
app: prometheus-grafana-clusterip
name: prometheus-grafana-clusterip
spec:
ports:
- name: prometheus
port: 9090
protocol: TCP
targetPort: 9090
- name: grafana
port: 3000
protocol: TCP
targetPort: 3000
selector:
app: prometheus-grafana
type: ClusterIP
- ai-monitor(Evidently AI)用のリソースのマニフェスト
- referenceデータはvolumeから読み取る形にしています。
- xxxxxxxxxxxx部分は各自のAWSアカウントIDを設定します。(リージョンも異なれば合わせて修正してください。)
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ai-monitor
name: ai-monitor
spec:
replicas: 1
selector:
matchLabels:
app: ai-monitor
template:
metadata:
labels:
app: ai-monitor
spec:
containers:
- image: xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/ai-monitor:latest
name: ai-monitor
ports:
- containerPort: 8085
volumeMounts:
- name: storage
mountPath: /app/datasets
volumes:
- name: storage
hostPath:
path: /data/grafana_monitoring_service/datasets
type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
labels:
app: ai-monitor-clusterip
name: ai-monitor-clusterip
spec:
ports:
- port: 8085
protocol: TCP
targetPort: 8085
selector:
app: ai-monitor
type: ClusterIP
5. Kubernetesリソースの生成
先ほど作成したマニフェストファイルからKubernetesリソースを生成します。
# ConfigMapの生成
kubectl apply -f manifests/cm_grafana-dashboards.yaml \
-f manifests/cm_grafana-datasource.yaml \
-f manifests/cm_prometheus.yaml
# prometheus/grafanaのリソース生成
kubectl apply -f manifests/depsvc_prometheus-grafana.yaml
# ai-monitorのリソース生成
kubectl apply -f manifests/depsvc_ai-monitor.yaml
# リソース生成の確認
# podのステータスが`Running`になることを確認します。初回起動時でも概ね1分以内でRunningになります。
# ※2秒間隔で表示が繰り返されます。podの起動が確認できたらctrl+cで終了してください
watch 'kubectl get pod,deploy,svc,cm'
6. データセットの生成およびMinikubeへの転送
データセットを生成しMinikubeへ転送します。この転送したデータはEvidently AIでのモニタリングでメトリクス算出する際のreferenceデータとして使用します。
# Evidently AIサンプルのフォルダに移動
cd evidently/examples/integrations/grafana_monitoring_service
# Pythonパッケージの追加
pip install -r requirements.txt
# データセットの生成
# ※サンプルにはkdd_k_neighbors_classifierというデータセットもあるのですが、
# 記事の執筆時点ではエラーが発生する現象が見られたので除外しています。
python scripts/prepare_datasets.py -d "bike_random_forest" -p "datasets/bike_random_forest"
python scripts/prepare_datasets.py -d "bike_gradient_boosting" -p "datasets/bike_gradient_boosting"
# minikube配下のローカルディレクトリへコピー
sudo cp -r datasets/bike_random_forest /var/lib/docker/volumes/minikube/_data/data/grafana_monitoring_service/datasets/
sudo cp -r datasets/bike_gradient_boosting /var/lib/docker/volumes/minikube/_data/data/grafana_monitoring_service/datasets/
# Grafanaダッシュボード定義ファイルのコピー
sudo cp -r dashboards/*.json /var/lib/docker/volumes/minikube/_data/data/grafana_monitoring_service/grafana-dashboards/
# ホームディレクトリに戻る
cd ~
7. ポートフォワードの実行
Minikubeの外部からアクセスできるようにポートフォワードを行います。
# prometheus、Grafanaのポートフォワード(バックグラウンド実行)
kubectl port-forward svc/prometheus-grafana-clusterip --address 0.0.0.0 9090 3000 >/dev/null &
# ai-monitorのポートフォワード(バックグラウンド実行)
kubectl port-forward svc/ai-monitor-clusterip --address 0.0.0.0 8085 >/dev/null &
8. データの送信とダッシュボードの確認
クライアントからデータを送信し、その結果をGrafanaのダッシュボードで確認します。
# Evidently AIサンプルのフォルダに移動
cd evidently/examples/integrations/grafana_monitoring_service
# データ送信のClient App実行
# これを実行すると2秒間隔で連続的にデータ送信する処理が約20分間動きます。
python scripts/example_run_request.py
データの送信が開始されたら、Grafanaのダッシュボード(http://localhost:3000)にアクセスします。
- 初回起動時のユーザIDと初期パスワードはadmin/adminです。初回ログイン後にパスワードの変更を求められます。
メニューのDashboards > Browse でダッシュボードを選択します。例えばEvidently Data Drift Dashboard
を選択すると以下のような画面が表示されます。
このダッシュボードでは、データドリフトの発生有無を時系列で表示しています。簡単に内部の動きを言うと、Client Appから送られてきたproductionデータとreferenceデータで統計的検定を行って両者のデータ分布に違いがあるかどうかで判定しています。検定は各特徴量ごとに行っており、一定数以上の特徴量において分布に違いがあった場合にデータドリフトが発生していると判断しています。
今回のサンプルはproductionデータとreferenceデータが時期の異なるデータを使っているので、ドリフトが発生していると判断されやすいものになっています。
終了・削除手順
今回作成したリソースは下記のように終了・削除します。
# ポートフォワードの終了
# job番号は実際の数値に変更してください
jobs
kill %1
kill %2
# kubernetesリソースの削除
cd ~
kubectl delete -f manifests/depsvc_ai-monitor.yaml \
-f manifests/depsvc_prometheus-grafana.yaml \
-f manifests/cm_grafana-dashboards.yaml \
-f manifests/cm_grafana-datasource.yaml \
-f manifests/cm_prometheus.yaml
# ECRリポジトリの削除
aws ecr delete-repository --repository-name ai-monitor --force
# minikubeの停止
minikube stop
# ※minikubeを削除したい場合は`minikube delete`
おわりに
Kubernetesの勉強をしながらのチャレンジだったのでかなり四苦八苦しましたが、おかげでKubernetesとEvidently AIの理解が深まりました。