nokoのブログ

こちらは暫定のメモ置き場ですので悪しからず

MLSE機械学習基盤本番適用と運用の事例・知見共有会参加レポート

はじめに

メモ

ゼロから始めるKubeflowでの機械学習パイプライン構築

  • Reproさん
  • Cloud Composer → Kubeflow
  • プロセスに「実験デザイン」の手法を導入
    • 試行錯誤の手続きを標準化
    • 生命科学の実験デザイン」
  • TFXを導入
    • 機械学習を利用するシステムの設計思想
    • コンポーネントはストレージからデータを読み込み、処理結果をストレージに格納する
  • 実験基盤、検証基盤、サービス基盤
  • 机上実験 → パイプライン実装(FS)
  • Kubeflow Pipelines(AI Platform Pipelines)を選定
    • マネージド、スケーラビリティ、BQ、利用ライブラリを柔軟に選べる(コンテナ)
  • 可視化
    • Feature Importance
    • AUC
    • 損失
  • 通知
    • OOMとか
  • Terraform, CI/CDパイプライン
  • 残課題
    • データ取得のために似たようなコードを書いている
    • 「データサイエンスから秘密のベールを引き剥がし、退屈な仕事にする」
    • サービス基盤は「Human in the loop」
    • Feature Storeの導入
      • ただ、ハードル高い?(費用対効果)
  • 再現性
    • ノートブックの実装とパイプラインの実装は別
  • Apache Airflowはデバックが辛かった?
    • PodのOOMとか。Kubeflowなら検知できる?
    • AI Platformなら、DockerのログがCloud Monitoringに出力される?

リーガルテックにおけるMLOps構築事例の紹介

  • 研究開発チームは、マイクロサービスなAPIを提供するところまで
  • 機械学習のフローだいたいインフラ関わる説
  • 課題
    • マイクロサービス化されたAPI
    • 学習を回す場所がない
    • 実験管理・再現性の問題
      • 再度モデルを作ることができない
      • 問題が起きたときに前の状態に戻れない
      • API開発と機械学習の分離
    • →学習、評価・実験管理→デプロイ に絞って改善
  • 実験管理、モデル/データ管理はMLflow
  • 学習はAI Platform Training Job
  • モデルサービングはSeldon Core
  • 学習基盤の拡張性
    • 学習リソース管理
    • 導入・維持コスト
    • ドキュメント
    • 拡張性
  • MLflow Model Registry
    • Stageを設定し、モデルのバージョンを切り替えることが可能
  • Seldon Core
    • GKEにHelmで入る
    • 推論のPipeline化が特徴
      • エンドポイントを組み合わせ(前処理できる)
    • Dockerfile内でエンドポイント起動ができる
  • MLOpsの効果
    • アジリティ
    • 再現性
    • 自動化・非属人化
  • レポジトリ・コードの雛形化
  • MLを含んだインテグレーションテスト
  • アノテーションはツール使って人手
  • MLflowは小規模でも使いやすい

PLAIDにおけるリアルタイム予測基盤

  • KARTEの解析データはPB級
  • 予測結果は前のeventで予測済みの結果をDBから取ってくる
  • 非同期に渡しておく
  • 連続性のある事象を取り扱う場合は、過去のデータを利用することでサービス提供におけるレイテンシを守る、という事例
  • 推論APIに直接データを渡す訳ではなく、APIにDBのキャッシュを渡す
  • Cloud Spannerを利用
    • (もちろん)BQと全く同じSQLとはならない
    • Bigtableなども検討中
    • TTLがなく、キャッシュが貯まり続ける
    • 実行計画は見える
  • Prediction APIにはCloudRunを利用
    • イメージを指定すればコマンド一つでデプロイ可能
    • 切り戻しもしやすい

モバイル向け機械学習モデル管理基盤

  • サーバで推論を行う場合、1secとかかかる。滑らかな体験のためには、100msec未満が必要。
  • クライアント側で実行すれば、セキュアでもある
  • TF Liteへの変換で軽くなる
    • 情報量は落ちたり
    • モデルの蒸留に加えて、変数の型を小さくしてメモリサイズをコントロールしたり
  • Edgeモデル検証用プラットフォーム
    • モデルごとやデバイスごとの精度・性能評価などを自動化
    • 物理デバイスでのテストをパイプラインに組み込む
  • Firebase Test Lab
    • カバーできないリアルデバイスもある
  • チームとして、Kubeflowを運用している

大規模・複雑な機械学習プロダクトの継続的な改善を支える実験プラットフォーム

  • ドライブチャート
    • エンジニア30名体制
  • エッジデバイスが収集したカメラデータ、センサデータがインプット
  • エッジ時点で、ディープラーニングモデルが特徴量を抽出
  • エッジとレポートの一貫したテスト
    • 結合テストの実施
    • → Kubeflowでパイプラインを
    • Feature Storeを内製
  • Kubeflow Pipeline
    • パラメータのセット、テストデータの準備、実行環境の構築
    • 実験の実行をWeb UIで完結できる
  • feature store
    • テストで抽出した特徴量をFeature Storeに保存
    • テストパイプラインと開発者の間で特徴量を共有する
      • 必要な機能が限定的なため、内製
    • 各データがバージョンを持つ
    • バージョンが同じ特徴量をテスト間で再利用する(キャッシュ)
    • dfをそのまま入出力できるインターフェイスを提供
  • データセット生成はETL Systemへ集約
    • データガバナンスを担保
    • テストで用いたデータは永続化する

ポイント

  • 実験デザインの手法
  • TFXの思想
  • Kubeflow Pipelines(AI Platform Pipelines)でできること・利用コスト
  • Human in the loopを上手く導入するには
  • OOMの通知
  • サービングのSanity Check
  • training-servinng skew対応として、DataflowとTFTを用いた変換(Google
  • delegation
  • Feature Storeに求める役割

監視を復習してみた

はじめに

  • 監視を復習してみたときのメモです

参考

検討項目とポイント

1. データ収集

  • 一般的にプルよりプッシュ(スケールしやすい)
  • とりあえずOSのメトリクスとかを監視しがちだが、監視の目的から逆算すると、「動いているか」の監視が重要
    • HTTP200やリクエストのレイテンシ
  • 設定追加は自動でされるようにするべき
  • 60sに1回はメトリクスを取得する
  • syslogでリモートに転送したり
  • ログの収集は、syslogデーモンを中心に考える
  • 監視サーバ自体の監視を忘れない

2. データストレージ

  • 時系列DB
    • 一定期間後に間引きしてくれたり
    • →ディスク容量、グラフ描画のため

3. 可視化

4. 分析とレポート

  • ペナルティ事項の存在しないSLAは、むしろ「目指すべき目標」

5. アラート

  • 「監視は、質問を投げかけるためにある」
  • メトリクスとアラートが一対一である必要はない
  • 固定の閾値だけでなく、変化量で検知してもよい
  • 全てのアラートが、誰かの対応必須のものになっているか定期的に見直す
  • 自動復旧を目指す
    • 特定の条件で落ちた場合、再起動するなど

監視設定書の項目例

監視項目

  • No
  • 分類
  • メトリクス名
  • 比較演算子
  • 閾値
  • 単位(Count/Percent,etc)
  • 説明

監視対象

監視詳細

  • レベル(error/warn/info)
  • 取得間隔
  • 評価間隔
  • 統計(Minimum/Maximum,etc)
  • 欠落データ(igonre,etc)
  • 方式概要(別ページへのリンク)
  • 通知メッセージ例
  • 有効/無効

監視対象例

ビジネスKPI監視

  • 現在サイトに滞在しているユーザ
  • ログイン

フロントエンド監視

アプリケーション監視

  • 関数の実行時間
    • StatsDを仕込んでおく
    • 特にサーバレスなどだと
  • デプロイタイミング、ビルドデータ、デプロイを実行した人
  • healthエンドポイントパターン

サーバ監視

  • CPU使用率
  • メモリ使用率
  • スワップ使用量
  • OOMKillerの発生
  • インターフェイスに対するイン・アウトのオクテット数、エラー数、ドロップ数
  • ディスク使用率
  • IOPS
  • マウントカウント
  • プロセスカウント
    • sshd
    • crond
    • vsftpd
    • ミドル
  • SSL証明書
  • 秒間リクエスト数
  • リクエスト時間
  • DBコネクション数
  • キューの長さ
  • 秒間メッセージ数
  • キャッシュから追い出されたアイテム数(evicted items)
  • ヒット・ミス率(hit/miss ratio)
  • NTP
  • ログ
    • /var/log/messages
    • HTTPレスポンス
    • sudoの使用
    • SSHログイン
    • cronジョブの結果
    • スロークエリ
    • auditd

実装例

MLOps勉強会Tokyo(Online)#3参加レポート

はじめに

メモ

異音検知プラットフォーム開発におけるMLOpsの実際と考察

  • リサーチャ、ソフトウェアエンジニア、ユーザのそれぞれの目的
    • → 結局、ユーザが課題を解決するためにみんな動いている
    • ResDevOpsでモデルを作っていく
  • リサーチャはクラウド前提の開発をしない
  • ソフトウェアエンジニアは、運用コストを最小限にするため、クラウドが前提
  • ソフトウェアエンジニアが前処理ロジックも学習モデル生成ロジックも移植する
  • PoCの進捗速度が速いとソースコードと結果の関連管理が乱雑になりがち
  • リサーチャが求めた精度が出ない場合があるとは?
    • 開発のデータとの差分
    • PyTorchのバージョンが変わっただけで精度が変わったり
  • 実験管理はGASとスプレッドシートでやったり
  • 本番はSageMakerで動かしている
  • モデルデプロイのタイミングでは、本来は精度比較のテストをするべき
  • コンテナで固めればバージョン固定できるが、SageMakerを使ってるからこそ固定できない
  • PMがリサーチャをまとめる。リードエンジニアがSWEをまとめる。という体制
コードの移植について、
リサーチャのコードをソフトウェアエンジニアが書き直しているとのことですが、
リサーチャが継続的に頻繁にコードを修正する場合、修正点のソフトウェアエンジニアへの連携が難しそうですが、
何か工夫している点はありますでしょうか。(ツールを利用しているなど)

プロダクトの更新スケジュールは決めている
それに向けて、いつ時点のリサーチャのコードを使うよ、と宣言

Webアプリケーションのアーキテクチャについて考えたこと

はじめに

  • 自分でちょっとしたWebアプリケーションを開発するときに、構成をどうするか悩んだときのメモです。

考えたこと

Webアプリケーションフレームワーク

比較

f:id:noko_htn:20201022023948p:plain

Django

  • Djangoディレクトリ構成
    • cookie cutterを踏襲したいが、ファイルが増え過ぎるので、参考にしつつ自分で作るのが良いかも
    • DjangoのMTV
■モデル
・データベースと連携を取るところ
■ビュー
・他のフレームワークでいうコントローラ
・モデルで引っ張ってきた情報をどのようにみせるか(ロジックで加工する)
■テンプレート
・HTMLファイルとか。ダイレクトにスクリーンに反映されるコード
・モデルから直接情報を持ってこないこと

FastAPI

  • FastAPIの良いところ

その他

  • マイクロサービス化も考慮したい

インフラ構成

AWS or GCP

  • Google BigQuery使うならGCP、というケースが多い?
  • 逆にElastic Search使うならAWS?

AWS

  • AWS Elastic beanstalkからAmazon EKSへ移行する
    • AWS Elastic beanstalkの辛い点
    • CI/CDをどのように構築するか
  • lambda
    • ライブラリが必要で、ってなっていくと、意外と容量制限が早くてだんだん辛くなってコンテナにしたくなる
    • NFSはエンドポイントとかちょっとめんどくさい(LambdaもVPCだし)
    • zipを開いたら、直下のディレクトリに展開される
    • layerは、optにできる
    • パスが通っているところに置きたかったら、解凍したらpython配下になるようにする

GCP

ネットワークを復習してみた

はじめに

  • ネットワークを復習してみたときのメモです

参考

ネットワーク

1. Webブラウザがリクエストを作る

  • リクエストメッセージ
    • リクエストライン + ヘッダー + ボディ
      • リクエストライン: 要求方法 + 要求するURL
      • ヘッダー: Hostヘッダー、ブラウザの種類、Cookieヘッダーなど
      • ボディ: Postメソッドのとき。ヘッダーとボディの間は空行で区切る。クライアントからWebサーバに向けて送信するデータ。
  • リクエストメッセージ: 何を + どうして 欲しいのか
    • 何を = URI
    • どうして = メソッド
  • ドメイン名からIPアドレスを調べるとき、ブラウザはソケットライブラリのリゾルバを利用する
  • 1つのドメインの情報を分割して複数のサーバに登録することはできない。逆に、1台のDNSサーバに複数のドメインの情報を登録することはできる
  • DNSでは、ドメインの階層を上から辿っていく
    • まずルートドメイン
    • インターネットに存在するDNS全てに、ルートドメインDNSサーバを登録する
    • "com", "net" はVerisignというところが管理している
  • DNSサーバは一度調べた名前をキャッシュに登録しておく機能がある

2. TCP/IPのデータを電気信号にして送る

  • イーサネットのパケット - MACヘッダー = IPヘッダー
  • TCP
    • 特徴
      • シーケンスナンバーによる順序制御
      • データの再送
      • データの重複除去
  • UDP
  • コネクションとセッションの違い
    • コネクション: トランスポート層TCPコネクション。セッションでデータ転送を行うための論理的な回線
    • セッション: アプリケーション層、セッション層。通信の開始から終了までを管理する
  • ソケットライブラリ

3. LANを通っていく

  • ネットワークは集約と切り替え(スイッチング)が大切
  • LANアダプタのPHY(MAU)回路で電気信号に変えられる
  • スイッチ
    • L2: スイッチングハブなど
      • 同じネットワークアドレスに所属する機器を集約する
      • イーサネットフレームという単位で通過が行われる
      • フレームの送信元のMACアドレス送信先MACアドレスの情報から転送する
      • MACアドレステーブルにのっていなければ、全ての物理ポートにフレームを送信する(フラッティング)
    • L3: ルータ機能のついたL2スイッチ
      • IPパケットを扱う
      • ルーティングテーブルで、ネットワークごとに転送させるべきゲートウェイIPアドレスと物理ポートの番号が記されている
      • 明記されていなければ、デフォルトゲートウェイ
      • ルーティングの交換は、手動だったり自動で他のルータとやり取りしたり
        • OSPF: RIPと異なり、ホップ数だけでなく、帯域幅も考慮して計算
    • L4/7: ロードバランサなど
  • ルータ = 中継部分 + ポート部分
    • 中継部分: パケットの中継先の判断を担当
    • ポート部分: パケット送受信の動作を担当
  • VLAN
    • スイッチ内を論理的に分割する機能
  • ポートVLAN
    • 1台のスイッチの物理ポートごとにVLAN番号を設定する
    • VLANを跨いで通信する場合は他のL3スイッチやルータを経由
  • タグVLAN
    • 複数台のスイッチにまたがってVLANを構築する
    • トランクポートを通じて他のスイッチと連結する
    • イーサネットフレームに4バイトのタグを付与
    • トランクポートを通る度にタグの付け外しを行う

4. プロバイダからインターネット内へ入っていく

  • インターネット = 家庭や会社のネットワークの規模が大きくなったもの
    • 例えば、インターネット内のルータには経路情報が10万件登録されていたり
  • PPPoE
    • PPPをイーサネット上で実現
      • 通信要求
      • 接続する資格を持ったユーザか確認
      • 通信条件について相談
      • 通信を開始
  • アクセス回線: プロバイダに接続する部分の回線
  • BAS(ブロードバンドアクセスサーバ)と普通のルータの違い
    • 本人確認機能、IPアドレスなどの設定情報をクライアントに通知する
  • IX(Internet eXchange): プロバイダ同士を一堂に集めて相互に接続する設備(実態は高性能なスイッチングハブ
  • ネットワーク機器の監視は、主にdiscard(パケットロス)とunbind(インターフェイスが外れた状態)
  • 切り分けコマンド
    • ping, traceroute, telnet, nslookup, dig
    • lsof: IPv6だけに見えても、IPv4互換アドレスで接続しても応答を返す
  • AWS)Internet Gateway = 自分のネットワークにインターネット回線を引き込むイメージ
  • EGP
    • AS番号をやり取りして、「どのネットワークの先に、どのネットワークが接続されているか」を大まかにやり取りする
    • 細かいところはIGPで

5. Webサーバにたどり着く

  • ファイアウォール
    • 今はパケットフィルタリング型が最も普及している
    • 動的ポートフィルタリング
      • 戻りの通信を考えないための機能
  • キャッシュサーバ
    • LBと同じように、Webサーバの代わりにDNSサーバに登録する。→キャッシュサーバにHTTPリクエストが届くようになる
    • コンテンツ配信サービスプロバイダは、主要なプロバイダと契約して、そこに多数のキャッシュサーバを設置する
  • SSLオフロード
    • 本来、Webサーバで行うSSL通信の処理をロードバランサ側に集約して行う機能
    • SSL処理はCPU負荷がかかる
    • ロードバランサはロードバランサで高価なので、他のシステムとの兼ね合いとかで、サーバ側でやるかとかは決める

6. レスポンスが作られ、Webブラウザに戻っていく

  • 1のリクエストメッセージ通り、WebサーバはURIで指定されたアプリケーションプログラムにリクエストメッセージ中にあるデータを渡して、処理が走る(レスポンスが作られる)
  • レスポンス
    • ステータスライン + ヘッダー + ボディ
    • Content-Typeヘッダー
      • ボディ部のコンテンツの種類を示す

Terraformのディレクトリ構成について考えたこと

前提

  • 小規模というほどではないが決して大規模ではない環境を組むときにちょうどいいTerraformのディレクトリ構成について悩んだときのメモです。
    • 環境ごとにVPCとかのネットワークを切って、それぞれにEC2(ECS)+RDS+S3とかとかを数台ずつ乗せていくくらい。

観点

  • [効率]環境ごとの差異を比較しやすいこと
    • dev→stg→prodとデプロイしていくとして、差分反映・確認がしやすいこと
  • [安定]安全にデプロイしやすいこと
    • 変更頻度や影響範囲を考慮

ディレクトリ構成

  • はじめに結論
├── env
│   └── stg01
│       |── network
│       |   └── main.tf
│       └── service-aaa
│           └── main.tf
└── modules
    └── vpc
        └── main.tf

ファイル例

  • env/network/stg01
#----------
# Terraform
#----------
terraform {
  required_version = "0.13.2"
  backend "s3" {
    bucket  = "system-stg01-deploy-tf-999999999"
    region  = "ap-northeast-1"
    key     = "tfstate"
    encrypt = true
  }
}

#----------
# Provider
#----------
provider "aws" {
  region = "ap-northeast-1"
}


#----------
# Remote State
#----------
data "terraform_remote_state" "XXX" {
  backend = "s3"

  config = {
    region = "ap-northeast-1"
    bucket = "system-stg01-deploy-tf-999999999"
    key    = "tfstate.XXX"
  }
}

#----------
# Local Values
#----------
locals {
  env = "stg01"
}

#----------
# Resource - Network
#----------
# vpc
module "vpc" {
  source = "../../modules/vpc"

  vpc_cidr_block = "192.168.10.0/24"
  vpc_tags_Name  = "system-${local.env}-vpc"
}

# public subnet
module "public-subnet01" {
  source = "../../modules/subnet"

  subnet_vpc_id                  = module.vpc.vpc_id
  subnet_cidr_block              = "192.168.10.0/26"
  subnet_map_public_ip_on_launch = "true"
  subnet_tags_Name               = "system-${local.env}-public-subnet01"
}

module "public-subnet02" {
  source = "../../modules/subnet"

  subnet_vpc_id                  = module.vpc.vpc_id
  subnet_cidr_block              = "192.168.1.64/26"
  subnet_map_public_ip_on_launch = "true"
  subnet_tags_Name               = "system-${local.env}-public-subnet02"
}
  • modules/vpc/main.tf
variable "vpc_cidr_block" {}
variable "vpc_tags_Name" {}

resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr_block
  instance_tenancy     = "default"
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = var.vpc_tags_Name
  }
}

output "vpc_id" {
  description = "The ID of the VPC"
  value       = concat(aws_vpc.this.*.id, [""])[0]
}

実装方針

  • モジュール切り出し+環境分離+コンポーネント分割
  • 変数の値は4箇所に集約(全て環境ファイルに)
    • 環境ファイルに直書き
    • 同一環境ファイルの別モジュールの値(output)
    • 環境ファイルのLocal Values
    • コンポーネントの値(outputをRemote Stateとして拾う)
  • 全てmain.tfに寄せる
    • 環境ファイルとmoduleファイル
    • variable.tfとか切り出さない

検討経緯

モジュール切り出し+環境分離+コンポーネント分割

  • 推奨されてそうなのでモジュール切り出しにして見た
  • 今後のことを考えて受け身が取れるように
  • モジュール切り出しは、ダイナミックとかロジック入れるときに役立つ?
  • ただ、ロジックは極力使わずベタ書きしている
    • EC2のcountとかあるが、tagのインデックスくらいならいいがEIPとか絡むとややこしくなる。だったら環境ファイル側でベタ書きする。
  • →が、環境ごとに下手書きで十分な気がする(モジュールを繰り返し利用したところで、記述量が減らない)
    • 結局、コードとは違う。設定値の羅列で、ロジックがないから。ベタ描きで十分 or 思い切ってWorkspace?
      • Workspaceも、比較しやすい?
  • moduleはあくまでパーツ。システム側の都合を考慮せず、疎結合に組み合わせられるように実装する
  • tf.stateの分割
    • 変更が多い/少ない
    • 非機能要件の違い

変数の値は4箇所に集約(全て環境ファイルに)

  • 同一環境ファイルの別モジュールの値(output)
module.vpc.vpc_id
  • 環境ファイルのLocal Values

    • 比較しやすくなるように、環境変数(dev, prodなど)はLocal Valuesとして一度だけ定義する
  • コンポーネントの値(outputをRemote Stateとして拾う)

    • 呼び出し先のmoduleではなく、呼び出し元でoutputとして定義する必要あり
data.terraform_remote_state.network.outputs.vpc_vpc_id

全てmain.tfに寄せる

  • 「観点」記載の通り、環境ごとの差分を比較しやすくしたいので、分けることにあまり意味がない
    • それぞれのファイルの量も少ない

参考

共通コードのディレクトリにはclassの定義が、 環境ごとのディレクトリにはclassのインスタンスを作る際に渡す実際の値が定義されていると イメージすると分かりやすいと思います。
# コンポーネント分割例
* network
  * vpc,subnet
* routing
  * route53,alb,acm
* services
  * ecsのサービス単位(ecs cluster,taskdifinition,service,それに付随するec2系リソース ecr pipeline系)
* opsserver
  * session manager,ec2
* events
  * やりたい処理単位(lambda cloudwatchEvents ここはApex+Terraform)
  * モニタリングとかもここに
* state machine
  * step function
* storage
  * s3
* datastore
  * elasticache,RDS
  * elasticache,RDSを別々に
* iam user
# コンポーネント分割例
* network
  * ネットワーク系の設定VPC、Subnet、NAT、Route Tables等の設定を管理
* securitygroup
  * セキュリティグループの管理
* iam
  * IAMの管理
* s3
  * S3回りの管理
* backend
  * DB等のデータストア関連のリソースを管理
* webservers
  * webサーバやALB等の管理
* opsservers
  * 運用系のサーバの管理