この記事は、Envision仮想マシンの内部動作に関する4部作の最初の記事です:Envisionスクリプトを実行するソフトウェアです。part 2part 3、およびpart 4を参照してください。このシリーズではEnvisionコンパイラはカバーしていません(別の機会にお願いします)、したがって、スクリプトはどのようにしてEnvision仮想マシンが入力として受け取るバイトコードに変換されたかを想定してください。

サプライチェーン最適化パイプラインは、データの取り込みと拡張、特徴抽出、確率的予測、制約条件下での最適な意思決定、データのエクスポート、分析、およびダッシュボードの作成など、さまざまなデータ処理ニーズをカバーします。各企業のパイプラインは異なり、独自の入力、ルール、制約条件、および出力を持っています。データのボリュームは大きく、Lokadの経験では、最も小さなアカウントでも毎日ギガバイト単位のデータを処理する必要があり、より大きなアカウントでは1テラバイトを超えるデータを処理します。最適化パイプラインは通常、企業の他の内部データ処理からの毎日の入力を待っているため、今日のデータに基づいて明日の意思決定を最適化するための計算時間はわずか数時間しかありません。

Beyond time-series

それほど難しくありません。本当にそうです。数時間で数テラバイトを処理することは、熟練したソフトウェアエンジニアのチームにとっては容易なパフォーマンス目標です。

Lokadの課題は、ソフトウェアエンジニアなしでこのようなパイプラインを構築することです。もちろん、Lokadではソフトウェアエンジニアが働いていますが、彼らは個々の顧客ではなく、会社全体のためのツールとインフラストラクチャを開発しています。代わりに、私たちはサプライチェーンサイエンティストを持っています。これは、各顧客の特定の問題を理解し、ソリューションを設計するサプライチェーンの専門家チームです。より伝統的な企業構造では、これらは製品マネージャーであり、顧客の要件を詳細に聞き、その後、ソフトウェアエンジニアに実装する必要があるものを正確に伝える人々です。私たちの開発哲学、および私たち自身のプログラミング言語であるEnvisionを作成した理由は、サプライチェーンサイエンティストが自分自身でソリューションを実装する方が、他の誰かに実装するための仕様を書くよりも速いはずだからです。

これを実現するために、サプライチェーンサイエンティストの努力なしにEnvisionが提供する必要がある3つの機能があります。

  • メモリ管理は自動である必要があります。これにはガベージコレクターを備えることはもちろんですが、Envisionは大規模なデータセットを透過的にサポートする必要もあります。10ギガバイトの配列を作成することは当然のことです。それはたった30億の数値に過ぎません!単一のマシンのメモリよりも大きなデータセットで作業できる能力が期待されています。実際、Envisionは、巧妙にNVMeドライブにスピルすることで、クラスタ全体のメモリよりも大きなデータセットをサポートしています。
  • マルチコアおよびマルチマシンの並列処理は自動である必要があります。並列処理可能な操作は、人間の介入なしにクラスタのできるだけ多くのコアに分散されるべきです。クラスタ内の単一のマシンのクラッシュによってスクリプトが再起動する必要はありません。クラスタが単一のマシンに減少した場合でも、スクリプトは完了する必要があります。そして、もちろん、2つのEnvision実行は同じクラスタ上で同時に実行できるはずです。
  • Envisionの実行には少ないハードウェアが必要です。パフォーマンスの問題は、高品質のハードウェアやサーバーライセンスに数百万ドルを費やすことで解決できるかもしれませんが、スクリプトの「実行」ボタンをクリックするだけで数百ドルかかる状況を避けたいと考えています。

一般的なプログラミング言語にはこれらの機能が備わっていません。通常、これらの機能を備えたフレームワークと組み合わせることができます(Scala + Spark、Python + Daskなど)、しかし、これによりユーザーに多くの鋭いエッジが露出することになります。この意味では、EnvisionはBigQueryで実行されるSQLにより類似しています。

環境

Envisionはローカルにインストールまたは実行することはできません。代わりに、すべてのユーザーはLokadのオンラインプラットフォームに接続し、ブラウザベースのIDEでスクリプトを編集および実行します。データとダッシュボードもオンラインで保存され、WebインターフェースおよびSFTPおよびFTPSを介してアクセスされます。

ユーザーがWebインターフェースを介してスクリプトを実行すると、実行用のEnvisionクラスタにディスパッチされる「ミッション」が作成されます。

Envisionはバッチモードで実行されます。各実行は入力データ全体を読み込み、完全な出力を生成します。これには、非常に単純なスクリプトを実行する場合であっても、少量のデータで実行する場合であっても5秒から、大規模なデータ拡張スクリプトの場合は30〜40分、一部の機械学習タスクの場合は数時間かかることがあります。

ストリーム処理(新しい入力をリッスンし、対応する出力をリアルタイムで生成する)やトランザクションアクセス(数行のデータのみを読み取り、数行を書き戻す)などの他の実行モードはサポートされていません。これらのモードを適切なパフォーマンスで実行するためには、バッチモード処理とは異なる言語のプリミティブおよび低レベルの実装の詳細が必要です。

クラスタ構造

2021年現在、Envisionは.NET 5上で実行され、Microsoft AzureクラウドのUbuntu 20.04仮想マシン上にホストされています。クラスタは2〜6台のStandard_L32s_v2インスタンスで構成されています:32コアのAMD EPYC 7551、256GiBのメモリ、および7.68TBのストレージスペースを持つ4つのNVMeドライブ。これらのマシンを「ワーカー」と呼びます。

クラスタはワーカーとミッションのM対Nの関連付けを持っています:単一のワーカーは複数のEnvisionスクリプトを同時に実行でき、単一のスクリプトが複数のワーカーに割り当てられた場合、それらは協力してより速く終了することができます。

各クラスタには、2つのコアと8GiBのメモリを持つ小さなStandard_B2msの「スケジューラ」もあります。スケジューラは、外部アプリケーションが新しいミッションを提出し、完了したミッションの結果を収集するためのAPIエンドポイントを提供します。また、ミッションをクラスタ上の1台以上のマシンにディスパッチする責任も持っています。負荷や各スクリプトの利用可能な並列性に応じて、スケジューラはミッションからワーカーを追加または削除することができます。

システム全体は耐障害性を持つように設計されています:ミッションがワーカーに割り当てられた後、他のすべてのワーカーおよびスケジューラがオフラインになっても、生き残ったワーカーはミッションを完了することができます。したがって、複数のワーカーの協力およびスケジューラによるミッションの再割り当ては、ミッションの完了には必要ありませんが、パフォーマンスの最適化です。

Blobストレージ

Lokadは顧客データにSQLデータベースを使用しません。ホステッドソリューションでは、大きな顧客のデータセット(通常4TBから16TBの間)を簡単に保持することができず、独自のサーバーを実行するには他の場所での努力が必要です。

一方、Envisionはバッチモードで実行されるため、「行Lから行Mの列Xを読み取る」というより複雑なクエリの必要性はありません:入力データがロードされた後、ワーカーは必要に応じてインデックスを作成および再処理することができます。

このため、私たちは主要なストレージとしてAzure Blob Storageを使用しています。ホステッドSQLデータベースのコストの1%以下でペタバイト以上のデータを保存することができ、読み取りクエリのパフォーマンスは常に30MB/sから60MB/sの間に安定しています。

私たちはまた、ブロブを変更不可能にしました。新しいブロブを作成することは可能ですが、既存のブロブを変更することはできません。これにより、スクリプトの入力が実行中に変更されることはなく、スクリプトの出力が実行が完了し、新しいブロブの識別子が返されるまで見ることができません。

正確に言うと、LokadはAzure Blob Storageの上にContent-Addressable Storeを構築しました。これはオープンソースであり、NuGetパッケージLokad.ContentAddrLokad.ContentAddr.Azureのペアとして利用できます。個々のファイルのハッシュを知ることで、Envisionは入力の一部が変更されていないことを判断し、以前の実行からキャッシュに保持された計算済みの値を再利用することができます。

デプロイ

Envisionは、追加の複雑さを正当化しないため、Dockerなどのコンテナ化を使用しません。

まず、高性能コンピューティングでは、ワーカーからCPU、RAM、ストレージの最後の一滴まで利用する必要があり、同じマシンで複数のアプリケーションを実行することはできません。

さらに、アプリケーションとその依存関係をプラットフォームに依存しない方法でパッケージ化するために、dotnet publishが十分であることがわかりました(実際にはdocker buildよりも高速です)。.NET 5では、Microsoftが優れたクロスプラットフォームサポートを提供しており、WindowsマシンからLinuxホストにビルドの結果を文字通りコピーするだけで十分です。

最後に、ゼロから新しいインスタンスを素早く作成することは積極的に避けています。コストを削減するためにワーカーをシャットダウンすることはできますが、新しいクラスタを作成したり、既存のクラスタにワーカーを追加することは「財務」の決定です。リソース使用量に基づいて顧客に請求することはないため、追加のコストを転送する方法はありません。

来週は、Envisionの実行モデルと、クラスタ内で行う作業の表現方法について詳しく説明します。

自己紹介: ソフトウェアエンジニアを募集しています。リモートワークも可能です。