NKE で Kubeflow してみる
こちらの記事は、Nutanix Advent Calendar 2022 3日目の記事です
NKEに再入門中ですので、なんか作ってみようと、MLOps入門として、KubeflowをとりあえずNKE上に構築してみようと思います
Kubeflowまだ何よくわかってないですが
Kubeflowとは
Kubeflowは、Kubernetesクラスター上で機械学習(ML)ワークフローを構築、実行、管理するためのオープンソースプラットフォームです。Kubeflowは、Kubernetesに特化したMLフレームワークとして構築されており、Kubernetesを使用したアプリケーションと同様に、スケーラブルで拡張可能なMLワークフローを作成することができます。Kubeflowを使用することで、データサイエンティストやMLエンジニアは、MLモデルのトレーニングやデプロイ、監視、更新を行うことができます。また、Kubeflowは、様々な環境やクラウドプロバイダーで利用できるため、場所に縛られないMLワークフローの構築が可能です。
って、はやりのchatGPT さんが教えてくれました
1.NKE で Kubernetesクラスタの作成
まずは、NKEでKubernetesクラスタを作ります
クラスタ名を「kon-nke」にして、あとはひたすらデフォルトの設定で進めてみます
2.Kubernetesクラスタの ssh接続する
Kubeflow をデプロイするためにまずデプロイする
ssh接続用の設定ファイルをダウンロード
kubernetesクラスタの画面より、接続用の設定ファイルをダウンロード
クラスタ名-access.sh になります
例)kon-nke-ssh-access.sh
設定ファイルを使って ssh接続
シェルが使える環境で(Windows だと WSL とか)
sh kon-nke-ssh-access.sh
(実行例)
% sh kon-nke-ssh-access.sh Enter KARBON VM IP: 10.38.55.10 ================== 10.38.55.10 ================== The authenticity of host '10.38.55.10 (10.38.55.10)' can't be established. RSA key fingerprint is SHA256:HR+kRDk4b/4bUANtEMs4wnSKoqLMSrbCi/3iW1Hdi2I. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.38.55.10' (RSA) to the list of known hosts. [nutanix@kon-nke-5b294f-master-0 ~]$
3.Kubeflowデプロイの下準備
基本的には、Kubeflowのサイトに書かれている方法に沿ってすすめていくのですが、
Kubeflow on Nutanix Karbon | Kubeflow
書かれているままの方法でいくと、いくつか躓くところがあったので事前に準備必要です
Terraformインストール
sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo sudo yum -y install terraform sudo yum -y install unzip
kustomizeインストール
cd /tmp wget https://github.com/kubernetes-sigs/kustomize/releases/download/v3.2.0/kustomize_3.2.0_linux_amd64 chmod +x kustomize_3.2.0_linux_amd64 sudo mv kustomize_3.2.0_linux_amd64 /usr/local/bin/kustomize cd
gitインストール
sudo yum -y install git git clone https://github.com/nutanix/karbon-platform-services.git cd karbon-platform-services/automation/infrastructure/terraform/kcs/install_kubeflow
env.tfvarsの作成
vi env.tfvars
prism_central_username = "admin" prism_central_password = "password" # PCのパスワード prism_central_endpoint = "xxx.xxx.xxx.xxx" # クラスタのエンドポイントIP karbon_cluster_name = "kon-nke" # クラスタ名に置き換える kubeconfig_filename = "config" kubeflow_version = "1.6.0"
Kubeflow のデプロイ
terraform init terraform plan --var-file=env.tfvars terraform apply --var-file=env.tfvars
Kubeflow のデプロイ状態の確認
kubeflowの namespaceがあることを確認
$ kubectl get namespaces NAME STATUS AGE auth Active 93s cert-manager Active 93s default Active 100m istio-system Active 93s knative-eventing Active 93s knative-serving Active 93s kube-node-lease Active 100m kube-public Active 100m kube-system Active 100m kubeflow Active 93s ntnx-system Active 96m
すべてが Running になるのを待つ
$ kubectl -n kubeflow get pods NAME READY STATUS RESTARTS AGE admission-webhook-deployment-7978f87497-9jjzp 1/1 Running 0 86s cache-server-7df4ccc756-z6dg4 2/2 Running 0 86s centraldashboard-78dc66bf79-gxkm2 0/2 PodInitializing 0 84s jupyter-web-app-deployment-54f5ff876-grkqt 1/1 Running 0 86s katib-controller-6478fbd64c-x4pwc 1/1 Running 0 85s katib-db-manager-78fc8b7895-jqwr9 1/1 Running 0 85s katib-mysql-6975d6c6c4-tcppj 1/1 Running 0 85s katib-ui-5cb6cc4d97-qr4tz 1/1 Running 0 84s kserve-controller-manager-0 0/2 ContainerCreating 0 77s kserve-models-web-app-5454bfdb86-v78nk 2/2 Running 0 86s kubeflow-pipelines-profile-controller-5b8474b7bc-fdlbr 1/1 Running 0 86s metacontroller-0 1/1 Running 0 77s metadata-envoy-deployment-f4c868c97-bxfk4 1/1 Running 0 86s metadata-grpc-deployment-679b49cc95-skwbd 0/2 PodInitializing 0 85s metadata-writer-7459bcd96b-vtbkw 2/2 Running 0 85s minio-7955cfc9fc-qp7r9 0/2 PodInitializing 0 84s ml-pipeline-799556bd9f-k4bsm 1/2 Running 0 87s ml-pipeline-persistenceagent-848b7bbc88-9dggl 2/2 Running 0 87s ml-pipeline-scheduledworkflow-546fc65b4c-zglls 2/2 Running 0 86s ml-pipeline-ui-555c4f4f5d-4r2vk 2/2 Running 0 86s ml-pipeline-viewer-crd-5c79ccf5b6-4sljj 2/2 Running 1 (36s ago) 85s ml-pipeline-visualizationserver-8666b88867-rmj58 0/2 PodInitializing 0 85s mysql-75f4964b48-6thjv 2/2 Running 0 85s notebook-controller-deployment-6f645f457b-nlt65 1/2 Running 1 (19s ago) 84s profiles-deployment-678fbc8b55-wwcqf 0/3 PodInitializing 0 84s tensorboard-controller-deployment-f4bfb987b-bhshx 2/3 Running 1 (9s ago) 83s tensorboards-web-app-deployment-65b8646ff6-bw55b 1/1 Running 0 83s training-operator-5cc8cdfdd6-6dsfq 1/1 Running 0 83s volumes-web-app-deployment-c49cd595f-scblx 1/1 Running 0 87s workflow-controller-555f64865-qzkx9 2/2 Running 1 (46s ago) 86s
すべてがRunningになったら完了
NKE の管理画面で、Persistent Volumeを確認すると Namespace 「kubeflow」、「istio-system」が増えてますね
4.KubeflowにWeb GUIにログイン
まずはポートフォワードして
kubectl port-forward svc/istio-ingressgateway -n istio-system 8080:80 --address 0.0.0.0
ブラウザで「http://master-node:8080」へアクセスしてみます
サンプルユーザーが用意されていますので、これでログインしてみます
Email Address:user@example.com
Password:12341234
ログインできたーーー
今回はとりあえず Kubeflow 環境が作れたここまで
NKE(旧Nutanix Karbon)のアップデートでハマった話
こちらの記事は、Nutanix Advent Calendar 2022 2日目の記事です
ちょっと昔に Nutanix Karbon で Kubernetes入門していました
【Nutanix Karbon】Xi IoTの為のKubernetes環境を作ってみる - konchangakita
今は Nutanix Karbon から NKE(Nutanix Kubernetes Engine) と名前にリブランドされています
この NKE を使って何かしてみようと思います
アイコンはそのまま継続の模様
だがしかし、その前にまずは NKE を最新版にアップデートしようとして、ハマったお話をご披露します
==アップデート前の環境=========
AOS 6.5
AHV 20201105.30398
Prism Central pc.2022.6
NKE 2.2.3
LCM 2.5.0.2
===========================
LCMを使ったアップデート
NKE をアップデートするには、Prism Central の LCM を使用します
1.メニューから LCM - Inventory
2.Perform Inventory
3.PCとNKEを選択してアップデートする
通常ならば、LCM を使ってこれで簡単に PC も NKE もなんならその他の機能も一発アップデートで簡単になったなぁ!、、、のはずが。。。
NKEが失敗する
PrismCentralのアップデートは正常に完了するのですが、NKEはなんだか失敗してサービスも落ちている模様
NKEのUIも落ちて無反応
いろいろ調べたところ、本当にいろいろ調べたところ、どうやら NKE 2.2.x や 2.3 からのLCMを使ったアップデートには、ちょっと作戦が必要の模様 。。。
対処法
NKE 2.2.x, 2.3 で LCM を実行すると Karbon サービスがダウンしてしまうようなので、Prism Central 上でおまじないを仕込んでやります
Karbon サービスが落ちている状態で、Prism Central に ssh 接続して、下記コマンドを順番に実行します
pcvm$ sudo cp /home/docker/karbon_core/karbon_core_config.json /home/nutanix/tmp/karbon_core_config.json.bkup
pcvm$ sudo cp /home/docker/karbon_core/karbon_core_config.json /home/nutanix/tmp/karbon_core_config_modified.json
次に、Prism Central で Karbon サービスの起動を行います
pcvm$ cluster start 2022-11-28 13:30:37,068Z INFO MainThread zookeeper_session.py:190 cluster is attempting to connect to Zookeeper 2022-11-28 13:30:37,073Z INFO Dummy-1 zookeeper_session.py:629 ZK session establishment complete, sessionId=0x184bda305f40d39, negotiated timeout=20 secs 2022-11-28 13:30:37,078Z INFO MainThread cluster:2918 Executing action start on SVMs 10.42.69.39 Waiting on 10.42.69.39 (Up, ZeusLeader) to start: KarbonUI KarbonCore Waiting on 10.42.69.39 (Up, ZeusLeader) to start: KarbonUI KarbonCore Waiting on 10.42.69.39 (Up, ZeusLeader) to start: KarbonUI KarbonCore ・ ・ ・ Waiting on 10.42.69.39 (Up, ZeusLeader) to start: The state of the cluster: start Lockdown mode: Disabled CVM: 10.42.69.39 Up, ZeusLeader Zeus UP [8514, 8561, 8562, 8563, 8573, 8590] Scavenger UP [10423, 11546, 11547, 11550] SysStatCollector UP [19449, 19531, 19532, 19533] IkatProxy UP [19894, 19980, 19981, 19982] IkatControlPlane UP [20192, 20271, 20272, 20273] SSLTerminator UP [20197, 20315, 20316] Medusa UP [20422, 20484, 20485, 20548, 20845] DynamicRingChanger UP [21116, 21223, 21224, 21301] InsightsDB UP [21127, 21259, 21260, 21345] Athena UP [21135, 21294, 21295, 21296] Mercury UP [21915, 21995, 21996, 22031] Mantle UP [21921, 22027, 22028, 22054] VipMonitor UP [25652, 25653, 25654, 25655, 25659] InsightsDataTransfer UP [21978, 22238, 22239, 22255, 22256, 22257, 22258, 22259, 22260, 22261, 22262, 22263] Ergon UP [22011, 22305, 22306, 22307, 22327] GoErgon UP [22084, 22435, 22436, 22467] Prism UP [22199, 22510, 22511, 22595, 23760, 23796] Adonis UP [22243, 22566, 22567, 22568, 22569] PrismNotification UP [22316, 22641, 22642, 22643] AlertManager UP [22398, 22674, 22675, 22737] Catalog UP [22731, 22985, 22986, 22987, 23011] Atlas UP [22761, 23045, 23046, 23047] Uhura UP [22835, 23075, 23076, 23077] ClusterConfig UP [22861, 23114, 23115, 23117] APLOSEngine UP [22907, 23146, 23147, 23148] APLOS UP [23390, 23493, 23494, 23495] StatsGateway UP [23401, 23555, 23556, 23637] Lazan UP [23418, 23604, 23605, 23606] Kanon UP [23437, 23669, 23670, 23671] Polaris UP [24044, 24154, 24155, 24219] Delphi UP [24054, 24190, 24191, 24192, 24206] Metropolis UP [24574, 24670, 24671, 24808] Flow UP [24582, 24709, 24710, 24711, 24728] Magneto UP [24593, 24774, 24775, 24776] Search UP [25657, 25746, 25747, 25748, 25754, 25755, 25756, 25924] XPlay UP [25672, 25784, 25785, 25832] KarbonUI UP [146155, 146238, 146239] KarbonCore UP [147155, 147212, 147213] XTrim UP [26456, 26616, 26617, 26618] DataProviderManager UP [26473, 26667, 26668, 26711] Pollux UP [26489, 26727, 26728, 26755, 26756] ClusterHealth UP [26511, 26822, 27189, 27191, 27195, 27202, 27258, 27259, 27268, 27280, 27281, 27291, 27292, 27294, 27300, 27301, 27306, 27346, 27347, 27351, 27387, 27388, 27423, 27424, 27428, 27429, 27431, 27432, 27626, 27627, 27628, 27629, 27631, 27632, 27641, 27642, 27658, 27659, 27762, 27765] Neuron UP [26563, 26879, 26880, 27097, 27126, 27127, 28236, 28249, 28272, 28273, 28284, 28292, 28293, 28303, 28326, 28327, 28334] Categories UP [26621, 26920, 26921, 26922, 26938] DomainManager UP [27844, 27959, 27960] 2022-11-28 13:31:16,441Z INFO MainThread cluster:3079 Success!
Karbon サービス起動を確認後に下記コマンドを実行
pcvm$ sudo sed -i '/^\s*"config".*/a \\t \t"env":{},' /home/nutanix/tmp/karbon_core_config_modified.json
pcvm$ sudo cp /home/nutanix/tmp/karbon_core_config_modified.json /home/docker/karbon_core/karbon_core_config.json
再度アップデートを試みる
LCMを使って再度アップデートを試みます
無事成功!
NKE 2.5.0 では、ちょっとインタフェースも変わったようですね
結構苦労したので今回はここまで
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 Layout を作る
SSR で Fetch して表示するデータはそろってきました
ここでページ移動しても共通で表示させる「ナビゲーションバー」を Layout コンポーネントを作ってみます
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン
・フォーム制御
・とりあえずFetch
・FetchからRouterでページ遷移
・SSR で Fetch する
・Layoutつくる ←イマココ
準備
今回の最終形態はコチラ(Github)で公開しています
Layoutファイルの設置場所
Next.js のプロジェクト直下に「components」フォルダを作って、「layout.tsx」ファイルを作ります
Layoutの作成
まず初めに、tailwindcss のコンフィグファイルに Layoutファイルの場所を追記します
... content: [ "./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", ], ...
layout.tsx をまず {children} (ベースとなるページ)を表示するだけの空っぽ状態を作ってみます
<components/layout.tsx>
import type { NextPage } from 'next' import { ReactElement } from 'react' type LayoutProps = Required<{ readonly children: ReactElement }> const Layout: NextPage<LayoutProps> = ({children}) => { return ( <> {children} </> ) } export default Layout
あとはレイアウトを使う側のページで、ファイルの場所をインポートし
<pages/home.tsx>
import Layout from '../components/layout' <Layout> ... </Layout>
ナビゲーションバーの実装
daisyUI の Navbar を参考に作ってみます
Navbar — Tailwind CSS Components
まだナビゲーションバーとしての役目は、まだ何もないですが、とりあえず見栄えだけ整えてみました
<components/layout.tsx>
<pages/home.tsx>
「http://localhost:7778/home?cluster_name=<クラスタ名>」
Elasticsearchからデータが取得できると一覧がドワーっと表示されます
うまくデータが取得できなかった時
さいごに
デザイン的には本当は CSS 使うのでしょうが、tailwindCSS と DaisyUI いれてるので、ほとんど使っていません
(きっともうちょっと大規模になれば必要かと)
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 SSR で Fetch する
Fetch で得た レスポンスデータ を使ってページ遷移までを行いました
次に、ページ遷移した後に表示するデータをバックエンド(Flask、Elasticsearch)から取得して サーバサイドレンダリング(SSR)を行います
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン
・フォーム制御
・とりあえずFetch
・FetchからRouterでページ遷移
・SSR で Fetch する ←イマココ
・Layoutつくる
SSR は、ユーザからのリクエストごとに動的にデータを取得してページが作成されます
今回でいうと、Elasticsearchからデータを取得しページに表示させることになります
Next.js で SSR するには 「getServerSideProps」 を 使います
準備
今回の最終形態はコチラ(Github)で公開しています
1.コンテナ起動
docker-compose.yml からコンテナたちを起動します
2.プログラム起動
コンテナ frontend、backend のコンテナ内で
・フロントエンドの起動
"/usr/src/next-app/uuid"
# npm run dev
・バックエンドの起動
"/usr/src/flaskr"
# python app.py
3.ブラウザアクセス
ブラウザから「http://localhost:7778/」にアクセス
4.Nutanixクラスタの IP/ユーザー/パスワード を入力してログイン
getServerSideProps の使い方
遷移先のページで 「getServerSidePropsType」作ってやることで、データ取得してからページレンダリングされます
(なのでページ表示まで時間がかかる)
以下の例では、URL の GETパラメータを Next.js コンソールに表示するようにしています
import type { GetServerSideProps, InferGetServerSidePropsType } from 'next' type Props = InferGetServerSidePropsType<typeof getServerSideProps> export const getServerSideProps: GetServerSideProps = async context => { console.log('query: ', context.query) /* ここに fetch などの処理 */ return { props: {} } }
TypeScript では 型を気にしないといけないので、いろいろつけてます
こんな URL なら
こんな 表示
SSR で Fetch
では、この GET パラメータを使って Fetch してみます
この記事のバックエンドで作ったAPI /api/latestdataset を少し改良したデータを取ってきます
※まだ return していません
フロントエンド<pages/home.tsx>
export const getServerSideProps: GetServerSideProps = async context => { //console.log('query: ', context.query) const keyword = context.query const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(keyword) } console.log(requestOptions) const fetchUrl = "http://backend:7777/api/latestdataset" const response = await fetch(fetchUrl, requestOptions) if (response.ok) { var res:dict = await response.json() } else { var res:dict = { 'list' : '' } } console.log(res) return { props: {} } }
Next.js のコンソール
SSRでデータを表示
getServerSidePropsType を使って、取得したデータを使ってページレンダリングするには、return で変数を返してやります
... return { props: { res } }
次に受け取る側の関数で、return された同じ変数名で受け取ります
const Home: NextPage<Props> = ({res}) => { console.log(res) .... } export default Home
バックエンドから受け取ったデータをそのまま表示する流れがコチラ
フロントエンド:<pages/home.tsx>
バックエンド:<flaskr/app.py>
getServerSidePropsType を使ってバックエンドから取得したデータをただ表示しているだけなのでだいぶブサイクです
次回、表示を整えます
SSRは終了
これでバックエンドとのやりとりの部分は完了です
ここからは Next.js の中でどんな風に表現したいのかを考えながら、データを取り扱っていきます
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 FetchからRouterでページ遷移
前回はとりあえず fetch してレスポンスを確認しただけだったので、フォーム入力されたデータを使って、ページ遷移します
下記エントリで Native React で Fetch を作り込んだ「/api/connect」、「/api/latestdataset」からデータひっぱってきたいと思います
konchangakita.hatenablog.com
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン
・フォーム制御
・とりあえずFetch
・FetchからRouterでページ遷移 ←イマココ
・SSR で Fetch する
・Layoutつくる
準備
今回の最終形態はコチラ(Github)で公開しています
この docker-compose.yml からコンテナたちを起動します
コンテナ frontend、backend のコンテナ内で
・フロントエンドの起動
"/usr/src/next-app/uuid"
# npm run dev
・バックエンドの起動
"/usr/src/flaskr"
# python app.py
ブラウザから「http://localhost:7778/」にアクセス
POST で fetch
前回とりあえず fetch の練習だけおこなっていましたが、
onConnect関数 にフォームにインプットされたデータを使って POST します
POSTで渡す値は、json形式にします
const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(data) }
関数に組み込んでみると
フロントエンド:<pages/index.tsx>
const onConnect: SubmitHandler<FormValues> = async data => { console.log(data) const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(data) } const response = await fetch('/api/connect', requestOptions) if(response.status === 200) { const res_json = await response.json() console.log(res_json) if(res_json.info === 'success') { console.log(res_json) } else { alert('login failed') } } }
CORS問題対策に間に挟む別ファイルの関数にも、POSTで受け取ったデータを使って、バックエンドへ POST で fetch しています
フロントエンド:<pages/api/connects.ts>
import { NextApiRequest, NextApiResponse } from 'next' export default async (req: NextApiRequest, res: NextApiResponse) => { const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(req.body) } const response = await fetch("http://backend:7777/api/connect", requestOptions) const data = await response.json() // response.status > success 200 res.status(response.status).json(data); }
fetch でデータをとってくる先は、バックエンドの flask で作った 「Nutanixクラスタに接続しクラスタ名を取得し、データを Elasticsearch へインプットする」関数です
関数の中身の説明はココ
【Nutanix UUIDエクスプローラーを作ってみよう】Flask表示までのまとめ - konchangakita
Nutanixクラスタのログイン情報をインプットし、無事接続できると Elasticsearch にデータ入力後にクラスタ名がレスポンスで返ってきます
fetch 後にページ遷移
fetch してデータ取得に成功後にページ遷移をさせたい場合には、Next.js機能の Router を使います
import { useRouter } from 'next/router' router.push({ pathname: '/home', query: { cluster: res_json.cluster_name, } })
こうすることで、ページ遷移の際に fetcth のレスポンスとして受け取ったクラスタ名を含んだ「http://~~/?cluster=クラスタ名」という GET のURLとなります
関数に組み込んでみます
const onConnect: SubmitHandler<FormValues> = async data => { console.log(data) const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(data) } const response = await fetch('/api/connect', requestOptions) if(response.status === 200) { const res_json = await response.json() console.log(res_json) if(res_json.info === 'success') { console.log(res_json) router.push({ pathname: '/home', query: { cluster: res_json.cluster_name, } }) } else { alert('login failed') } } }
遷移先のページ作成
遷移先のページを「pages/home.tsx」で作ります
URL の GETパラメータをページ上に表示するだけのページを作ります
フロントエンド:<pages/home.tsx>
import type { NextPage } from 'next' import { useRouter } from 'next/router' const Index: NextPage = () => { const router = useRouter() const {cluster} = router.query return ( <div className=""> <main data-theme="white" className="h-screen flex justify-center items-center"> {cluster} </main> </div> ) } export default Index
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 とりあえずFetchする
フォームで受け取ったデータ(Prism IP、ユーザ名、パスワード)を使ってバックエンドにリクエスト行い、レスポンスを取得する Fetch を実装します
※SSR(サーバサイドレンダリング)までは実装は次で
Next.jsの実装とともに、フロントエンドとバックエンドのコンテナを分けたことによって少々手間がかかります(CORS問題)
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン
・フォーム制御
・とりあえずFetch ←イマココ
・FetchからRouterでページ遷移
・SSR で Fetch する
・Layoutつくる
準備
今回の最終形態はコチラ(Github)で公開しています
この docker-compose.yml からコンテナたちを起動します
コンテナ frontend、backend のコンテナ内で
・フロントエンドの起動
"/usr/src/next-app/uuid"
# npm run dev
・バックエンドの起動
"/usr/src/flaskr"
# python app.py
ブラウザから「http://localhost:7778/」にアクセス
curl でレスポンスチェック
プログラムに組み込む前に、curlコマンドを使ってチェックします
まずはバックエンド側にテスト用に簡単なレスポンスを仕込みます
バックエンドコンテナ:<flaskr/app.py>
from flask import Flask from flask import render_template, request from flask import make_response, jsonify ......... @app.route('/api/fetchtest') def fetchtest(): return make_response(jsonify('Fetch Success')) .........
これでフロントエンドコンテナのコンソールから「http//コンテナ名:ポート番号/api/fetchtest」向けに、curlコマンド実行してやると、仕込んでおいたレスポンスを得られます
# curl http://backend:7777/api/fetchtest "Fetch Success"
フロントエンドに実装
前回までで、フォームをサブミットすると、入力内容をコンソールに表示するだけだったこの部分の関数を編集していきます
フロントエンド:<pages/index.tsx>
コレを
const onConnect: SubmitHandler<FormValues> = data => console.log(data)
こんな感じで
const onConnect: SubmitHandler<FormValues> = async data => { console.log(data) const requestOptions = { method: "POST", headers: {'Content-Type' : 'application/json'}, body: JSON.stringify(data) } const response = await fetch('http://backend:7777/api/fetchtest') if(response.status === 200) { const res_json = await response.json() console.log(res_json) } }
そうすると、、、なぜだかうまくいきません。。
いろいろ調べてみると、フロントエンドとバックエンドのコンテナを分けたことによってCORS問題が発生している模様
さらにいろいろ調べてみましたが、正直なところ生半可な知識では解決方法は見つかりませんでした。。。
なので、直接このプログラムファイルから Fetch するのではなく、あいだにファイルを一個噛ませて2段階でやるとうまく取得できました
とりあえずの Fetch
CORS問題で一晩以上悩んだ結果、別ファイルを用意するという回りくどい方法をとりました
次でフォームのデータを使っての、リクエストを行います
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓 フォームの実装
Nutanixクラスタに接続するための「Prism IP」、「ユーザ名」、「パスワード」フォーム入力の制御には、通常入力を必須にしたり、エラーメッセージの実装を作り込む必要があります
そうすると、フォームごとになかなかコードが増えてくし、なによりそれぞれ作るのはメンドウです。。。
というわけで、フォームの制御に Next.js に対応するReact Hook Formを使ってみます
ホーム | React Hook Form - Simple React forms validation
【Nutanix UUIDエクスプローラーを作ってみよう】Next.js特訓
・ログインページをデザイン
・フォーム制御 ←イマココ
・とりあえずFetch
・FetchからRouterでページ遷移
・SSR で Fetch する
・・Layoutつくる
今回の成果物
今回の最終形態はコチラ(Github)で公開しています
テスト実装
公式サイトの クイックスタート を参考にしながら進めていきます
サンプルコードの TS を押すと TypeScript 版での実装方法も書かれていて、親切で初心者にもウレシイ
これを参考に、現行のページに導入してみます
1つ目のポイントは、フォームの input に対応する Value を設定
type FormValues = { prism_ip: string prism_user: string prism_pass: string }
あとは、React Hook Form のお作法に従う
const { register, handleSubmit, formState: { errors }, } = useForm<FormValues>()
フォームの input には 「{...register("prism_ip")}」という書き方で 設定した Value を呼び出します
<input {...register("prism_ip")} type="text" placeholder="Cluster IP" className="input input-info input-bordered m-1 w-64 text-lg" />
inputのオプション
React Hook Form の機能で入力必須にしたり、エラー表示も簡単に
入力必須:{...register("prism_ip", {required: true})}
<input {...register("prism_ip", {required: true})} type="text" placeholder="Cluster IP" className="input input-info input-bordered m-1 w-64 text-lg" />
エラー表示:{errors.prism_ip && 表示する内容}
{errors.prism_ip && <p className='text-red-600'>required.</p>}
インプットされた内容の処理
フォームで入力された内容は、Submitボタンを押されたときに onSubmit={handleSubmit(関数名)}で処理されます
<form onSubmit={handleSubmit(onConnect)}>
一旦、入力内容をコンソールに表示するだけならば
const onConnect: SubmitHandler<FormValues> = data => console.log(data)
実装
ここまでの実装をまとめると
いったんここまで
そもそもフォーム制御するライブラリというものがあるなんて考えてなかったので、はじめはとまどいながら実装してみましたが、一回慣れる手放せません
フォームで受け取ったデータを使っての fetch を次回