機械学習手法を用いる研究向け基盤 on Kubernetes ~開発編~
はじめに
この記事では大学院修了にともなって、これまで構築してきた Kubernetes (K8s) を用いた ML 基盤のうち、開発したソフトウェアを中心に紹介していきます。 6 年間の振り返りは こちらの記事 を、運用に関する紹介は こちらの記事(執筆中)を参照してください。
システムの構成
構築・運用していたシステム構成は以下の図のようになっており、ユーザは専用のクライアントコマンド経由で Web IDE や Jupyter を起動してデータの分析ができるようになっています。
クライアントコマンド
前述のクライアントコマンド(以下 cmd)の機能などについて軽く説明します。 cmd のソースコードは以下で公開していますが、cmd は様々な環境で動作させるために作ったわけではないため、ソースコードは参考程度にしてください。
cmd は主に K8s API を叩くシンプルな物になっており、
- 永続ストレージの作成・削除・容量変更
- Web IDE や Jupyter の起動・削除・詳細の表示
- kubeconfig の入手
の 3 つの機能を提供しています。 永続ストレージの作成では、コードのこの部分 で示すように、
- CephFS 用 Persistent Volume Claim (PVC)
- NFS ストレージ用 PVC
- ストレージバックアップ用 CronJob
- 各種設定用 ConfigMap
を作成しており、WEB IDE や Jupyter の起動では コードのこの部分で示すように、後述する Custom Resource を作成しています。
また本システムはプライベートネットワークに対してのみ公開しているため、直接 kubeconfig を配布する形で認証を行なっています。cmd は コードのこの部分 で示すように、次節で紹介する authenticator に kubeconfig のリクエストを送り、受け取った kubeconfig をローカルに保存する仕組みになっています。
authenticator
前述した authenicator について紹介します。 authenticator は以下で公開しており、cmd と違い他の環境でも使用できるようになっていると思います。
authenticator は、
の 2 つのソフトウェアから構成されており、authenticator は
- 指定されたラベルを持つ、 ServiceAccount に対する kubeconfig の生成
- kubeconfig を配布するための HTTP サーバの起動
を行います。 1 の kubeconfig の生成では、 こちら に示すように、initContainer で kubeconfig を生成し、 2 のHTTP サーバの起動では、 こちらで示すように、container で HTTP サーバを起動します。
また config-reloader は authenticator Pod を再起動するだけの単純な物ですが、こちら に示すように Deployment の spec.template.metadata.annotations
フィールドを編集することによって、 Pod を再起動するように実装しています。この実装方法は、以下に示すように本家 kubectl rollout restart
コマンドでも同様の方法で実装されています。
... case *appsv1.Deployment: if obj.Spec.Paused { return nil, errors.New("can't restart paused deployment (run rollout resume first)") } if obj.Spec.Template.ObjectMeta.Annotations == nil { obj.Spec.Template.ObjectMeta.Annotations = make(map[string]string) } obj.Spec.Template.ObjectMeta.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339) return runtime.Encode(scheme.Codecs.LegacyCodec(appsv1.SchemeGroupVersion), obj) ...
この config-reloader をこちら で示すように、 CronJob で定期的に実行することで、authenticator server Pod を再起動し、kubeconfig の再生成を定期的に行っています。
K8s Custom Controller
続いて K8s Custom Controller についてですが、構築・運用していたシステムでは、cks-operator と imperator の 2 種類の K8s Custom Controller を実装して運用の自動化を行なっていました。
cks-operator
cks-operator は以下で公開していますが、汎用的に使用できるように実装はされていないので、参考程度にしてください。
cks-operator で使用する MachineLearning Custom Resource (ML CR) はこちら や以下で示すようになっています。
... // MachineLearningSpec defines the desired state of MachineLearning type MachineLearningSpec struct { // +kubebuilder:validation:Required MachineLearningApps []MachineLearningApps `json:"apps"` } // MachineLearningApps is apps for machine learning type MachineLearningApps struct { // +kubebuilder:validation:Required AppName string `json:"name"` // +kubebuilder:validation:Required Namespace string `json:"namespace"` // +kubebuilder:validation:Required MLAppSpec MLAppSpec `json:"spec"` } // MLAppSpec is detail spec of apps for machine learning type MLAppSpec struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=jupyterlab;codeserver Type ApplicationType `json:"type"` // +kubebuilder:validation:Required Machine MachineSpec `json:"machine"` // +kubebuilder:validation:Required AppImage string `json:"appImage"` // +optional ImagePullSecrets []string `json:"imagePullSecrets,omitempty"` } type ApplicationType string const ( CodeServer ApplicationType = "codeserver" JupyterLab ApplicationType = "jupyterlab" ) func (t ApplicationType) String() string { return string(t) } type MachineSpec struct { // +kubebuilder:validation:Required Type string `json:"type"` // +kubebuilder:validation:Required Group string `json:"group"` } // MachineLearningStatus defines the observed state of MachineLearning type MachineLearningStatus struct { Conditions []metav1.Condition `json:"conditions,omitempty"` } ...
ML CR が作成されると、 cks-operator 内の machinelearning-controller は ML CR に定義された
- Pod を起動するための Namespace
- 後述する Imperator で定義された MachineType (計算機スペック)
- 使用するアプリケーションの種類 (codeserver もしくは jupyterlab)
- 使用するコンテナイメージ
- etc...
をもとに
を行う仕組みになっており、Web IDE や Jupyter を起動するために必要な種々の作業を自動化しています。
また前述のクライアントコマンドでは、この ML CR を操作することで Web IDE や Jupyter の起動や削除を行なっています。
imperator
imperator (インペラトル) は以下で公開しており、汎用的に使用できるように実装されているので、ぜひ使用してみてください。
運用していたシステムでは、種々の事情で
- 様々な物理スペックのサーバを使用
- 研究室メンバー(ユーザ)全員が同時に使用できるほどの計算機スペックがない
という問題点があり、ユーザが同時の使用できる計算機リソースを制限した上で、できるだけサーバの物理スペックを使い切れるように Pod をスケジューリングし、どの計算リソース、とりわけどの GPU 種別がどれくらい空いているかをユーザに通知する必要がありました。
これらの課題を解決するために Imperator を開発しました。Imperator の思想はとても単純で、リソース制限がかかった何もしない Pod によって K8s 上のリソースをロックし、本物の Pod がスケジュールされた時に、偽物の Pod を削除して本物の Pod に計算機資源を譲る物となっています。特性上、GPU や FPGA などのデバイスをロックすることが主目的であるため、そのようなデバイスを使用しない環境などではあまり効果はないと思われます。
似たような仕組みとして Public Cloud 環境などで、Cluster Autoscaler を使用する際、負荷のスパイクにも対応するため、ダミーの Pod を配置することがあると思いますが、そこから着想を得ました。
仕組み
Imperator は以下のような Machine CR が定義されると動作するようになっています。
apiVersion: imperator.tenzen-y.io/v1alpha1 kind: Machine metadata: name: machine-with-gpus labels: imperator.tenzen-y.io/machine-group: gpu-machines spec: nodePool: - name: kind-control-plane mode: ready taint: false machineType: - name: compute-xmedium machineTypes: - name: compute-xmedium spec: cpu: 4000m memory: 12Gi gpu: type: nvidia.com/gpu num: 1 family: ampere available: 1
Imperator は主に
- Machine Controller
- MachineNodePool Controller
の 2 種の Custom Controller および、Admission Mutating Webhook である Pod Resource Injector から構成されており、管理者、ユーザそれぞれに対する Imperator の動作は以下のようになっています。
まず Machine Controller は、 Machine CR の spec.machineTypes
で定義された計算機スペック(MachineType) を満たす偽物の Pod (Reservation Pod) と本物の Pod (Guest Pod) の状態や個数を管理することを主な役割とし、どの Node がどの machineType で使用可能かを管理する MachineNodePool Controller 用の MachineNodePool CR の管理も行います。
次に MachineNodePool Controller は、MachineNodePool CR の定義に従ってどの Node にどの MachineType をスケジュール可能にするかを決定し、Node に Annotation や Label、Taint を付与する役割をもった Custom Controller です。
最後に Pod Resource Injector ですが、これは本物の Pod (Guest Pod) の作成要求が来た際に、Guest Pod を正しい Node へスケジューリングできるように Guest Pod の resource.request や resource.limit、NodeAffinity などにパラメータを注入する役割を持っています。
本記事ではこれ以上詳細な説明はしませんが、より詳しい情報は以下に記述してあるので、参照してください。
まとめ
今回は構築したシステムで使用するために開発したソフトウェアを中心に紹介しました。 本記事では全体的な紹介にとどめたため各ソフトウェアを詳しく掘り下げたりしていませんが、掘り下げて欲しい物などがあれば、Twitter で言っていただけると紹介記事を書くかもしれません。
次回は ”運用編” として、使用していたミドルウェアをはじめ、運用で使用していた Ansible PlayBook、CI でのマニフェストへのテストなどについて紹介したいと思います。
最後まで読んでいただきありがとうございました。