はじめに
この記事はラクスパートナーズ Advent Calendar 2024の21日目の記事です。
Kubernetesにまともに触れてこなかったエンジニアが、アドベントカレンダー参加を機にKubeflowの環境構築を行ってみました。
簡単にですが、その手順をまとめていこうと思います。
内容について誤りがございましたら、優しい言葉でご指摘いただけますと幸いです。
本記事のゴール
VMインスタンス上にシンプルなKubeflowの環境を構築すること。
Kubeflowってなに?
Kubernetes上で機械学習(ML)ワークフローを管理するためのオープンソースプラットフォームです。
モデルの開発、学習、デプロイといった MLライフサイクルのプロセスを自動化し、スケーラブルで再現性のある環境を実現することができます。
Kubeflowにおける各コンポーネントが、どのプロセスに対応するかは公式サイトの説明が理解しやすいです。
また、前提となる「MLOps」の考え方についてはこちらで非常に分かりやすくまとめてくださっています。
環境情報
マシン情報
今回はアカウント作成時にもらったクレジットが余っていたので、GCP上にVMインスタンスを構築しました。
項目 | 値 |
---|---|
OS | Ubuntu, 24.04 LTS |
CPUコア数 | 8コア |
メモリサイズ | 32GB |
ディスクサイズ | 64GB |
プロセッサアーキテクチャ | AMD64 |
バージョン情報
項目 | 値 |
---|---|
Kubernetes | v1.31 |
Kubeflow | v1.90 |
前提
Kubeflow環境を構築するにあたり、個人利用の範疇ということを加味してKubeflow Manifestsを利用することにしました。
また、GitHubの手順では Kind (Kubernetes in Docker) を利用してクラスターを構築していますが、今回はマシンスペックの都合によりパブクラ上のVMに構築するため、外部からの通信設定が比較的行いやすい kubeadm で構築することにしました。
手順
Swapの無効化
Kubeadmでクラスターを作成する際、Swapメモリが検出されると起動に失敗してしまうため、Swapの無効化を行います。
swapoff -a
# Swap領域の確認
free
IPv4 パケットのフォワーディング(転送)を有効にする
この設定により、Pod間通信を実現できるようになります。
PodはLinuxのNetwork Namespaceで分離されており、それぞれが独立した仮想的なネットワーク空間を持っています。
これらの仮想インターフェースは同一ノード上の他Podや、ホスト側の実ネットワークインターフェースとは直接的に一体化されていないため、パケットを正しいインターフェースへ「フォワーディング」する機能が必要となります。
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
# 設定の適用
sudo sysctl --system
# 設定値の確認
sysctl net.ipv4.ip_forward
br_netfilterモジュールをロード
このモジュールをロードしておくことで、Pod間通信にパケットフィルタリングルールを適用できるようになります。
この辺りの話はこちらの記事が大変参考になりました。
また、後述するflannelではbr_netfilterをロードしておかないと以下のようなエラーが発生してしまいます。
Failed to check br_netfilter: stat /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory
# モジュールがシステム起動時に自動的にロードされるようにしておく
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
# モジュールのロード
sudo modprobe br_netfilter
# モジュールがロードされていることを確認
lsmod | grep br_netfilter
CRI (Container Runtime Interface) のインストール
今回はDockerを利用する必要がないため、CRIにcontainerdを採用しました。
sudo apt-get update
sudo apt-get install -y containerd
sudo mkdir -p /etc/containerd
# デフォルトのconfigファイルを作成
containerd config default | sudo tee /etc/containerd/config.toml
containerdコンフィグファイルの修正
Ubuntu, 24.04 LTS の initシステムがsystemd
のため、cgroupドライバーとしてsystemd ドライバーを利用するようにcontainerdコンフィグファイルの一部を修正します。
cgroupsの話はこちらの記事が分かりやすかったです。
また、このままではkubeadmによってクラスターを作成する際、下記のような警告メッセージが表示されるため、こちらも該当箇所を修正していきます。
W1219 detected that the sandbox image "registry.k8s.io/pause:3.8" of the container runtime is inconsistent with that used by kubeadm.It is recommended to use "registry.k8s.io/pause:3.10" as the CRI sandbox image.
修正箇所
[plugins."io.containerd.grpc.v1.cri"]
- sandbox_image = "registry.k8s.io/pause:3.8"
+ sandbox_image = "registry.k8s.io/pause:3.10"
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
- SystemdCgroup = false
+ SystemdCgroup = true
# initシステムの確認
systemctl --version
# コンフィグファイルの修正
sudo vi /etc/containerd/config.toml
# 設定の反映
sudo systemctl restart containerd
inotifyの上限値を変更
Kubeflowの環境を構築する上で、下記のように上限を変更しておかないとToo many open files
のエラーが発生し、立ち上がらないPodが出てきてしまいます。
# ユーザID毎に作成可能なinotifyインスタンスの上限を指定する
sudo sysctl fs.inotify.max_user_watches=1255360
# ユーザID毎に同時に監視できるファイル/ディレクトリの上限を指定する
sudo sysctl fs.inotify.max_user_instances=2280
kubectl, kubeadm, kubeletのインストール
ざっくりとですが、それぞれ以下のような役割を担います。
- kubectl
- Kubernetesクラスターを管理するためのコマンドラインツール。マニフェストの適用やログ収集を行うことができます。
- kubelet
- 各ノードで動作し、コンテナの状態管理やPodのヘルスチェック・リソース管理などを担います。
- kubeadm
- Kubernetesクラスターを作成するためのツール。
sudo apt update
sudo apt-get install -y apt-transport-https ca-certificates curl
# Kubernetesリポジトリの追加
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# kubectlの自動補完を有効にする(必須でない)
source /usr/share/bash-completion/bash_completion
echo 'source <(kubectl completion bash)' >>~/.bashrc
source ~/.bashrc
Kustomizeのインストール
Kubeflow Manifestsは、Kustomizeによりマニフェストが管理されているためインストールしていきます。
sudo curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv ./kustomize /usr/local/bin/kustomize
クラスターの初期化
ネットワークアドオンとしてflannelを利用するため、PodネットワークのCIDR範囲を10.244.0.0/16
としてクラスターを作成します。
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --kubernetes-version=1.31.0
# kubectl コマンドを使用してクラスターを管理できるようにする
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
ネットワークアドオンのインストール
以下にも記載がある通り、Pod間の通信を実現するためにCNI (Container Network Interface) をベースとするネットワークアドオンをインストールする必要があります。
今回はメジャーな CNI プラグインであるflannelを使用します。
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
コントロールプレーンノードの隔離
デフォルトではコントロールプレーンノードに、Podがスケジューリングされることはありません。
今回はシングルノードで構築を行うため、設定を変更します。
# 設定を変更しない場合、下記のようなエラーが発生する。
# 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }.
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
kubectl describe nodes | grep -i taints
DVP (Dynamic Volume Provisoner) を有効にする
Kubeflow環境を立ち上げると複数のPodが作成されますが、その中にはPVCを必要とするPodも含まれます。
そのため、あらかじめ DVPを有効にしておくことで、動的にPVオブジェクトが作成されるようにしおきます。
今回は、DVPを有効にするためにストレージプロビジョナーとしてLocal Path Provisionerを利用していきます。
- https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/
- https://github.com/rancher/local-path-provisioner
# DVPを有効にしておかないと以下のようなエラーが出る
# 0/1 nodes are available: pod has unbound immediate PersistentVolumeClaims.
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.30/deploy/local-path-storage.yaml
# デフォルトのStorageClassに設定
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
kubectl get storageclass
Kubeflow Manifetsのクローン
git clone https://github.com/kubeflow/manifests.git
cd manifests
Kubeflow Platformの構築
全てのオブジェクトが構築されるまでに数分かかります。
while ! kustomize build example | kubectl apply --server-side --force-conflicts -f -; do echo "Retrying to apply resources"; sleep 20; done
kubectl get pods -n kubeflow
全てのPodのステータスがRunningとなっていれば完了です。
最後に
今回ハマったポイント
プロセッサアーキテクチャの違いによるエラー
当初ARM64のVMインスタンス上で環境構築を行っていたのですが、いくつかのPodがCrashLoopBackOFFの状態で再起動を繰り返していました。
出力されたエラーログから、どうやらアーキテクチャが原因そうということが分かったので、AMD64に変更してみたところ、無事すべてのPodを起動することができました。
ただ、ARM64だとダメだな理由が見つけられなかったので、この辺りに知見のある方がいらっしゃいましたらご教示いただきたいです。
今後やりたいこと
今回構築した環境を利用して、データの前処理、モデル学習、デプロイなど一連のMLライフサイクル管理を体験してみようと考えています。
また、Kubernetesについて色々調べていくうちに、「コンテナがLinuxカーネル上で動く仕組み」の一端を知れたことが個人的には大きな収穫でした。
とはいえ、まだまだ分かっていないことが多いためこの辺りも引き続き勉強していきたいです。