はじめに
Field Notes には、ライセンスに従って利用できる庭園写真が 20 枚あり、スタッフがダウンロードできる小さなギャラリーが必要です。まず 1 グループを試し、その後 Kubernetes の Job で全 4 グループを処理します。最後のグループには写真が 2 枚しかありませんが、そのグループにも独自の完了インデックスの成功が必要です。
Pod、Deployment、Service、ラベル、簡単な YAML、基本的なターミナルのパスを理解していることを前提とします。Job、完了インデックス、この共有ボリューム、ここで使う編集コマンドは、以下で説明します。クラスター、処理用イメージ、元の写真、Web サイト、ストレージは準備済みです。Python の記述、イメージのビルド、クラウドアカウントは不要です。
コマンドには Workspace、Web サイトには Gallery を使います。両方の入口は 1 台の学習用 VM を使います。starter/ と inputs/ 内の配布ファイル、元の写真、参照用サンプル、処理プログラム、サービス、ストレージは変更しないでください。45 分は作者による見積もりで、読む時間と通常の操作ミスへの対応を含みます。
まず 1 グループを試す
このステップでは、正常な完成例をその元画像と比較してから、1 インデックスの Job で別の練習用サンプルを処理します。
Gallery を開きます。ホームページの大きな写真には Source collection · licensed garden photographs と表示されています。これは入力素材であり、自分が完成させた成果物ではありません。See the reference を選択します。Thumbnail は配布された 320 × 220 の JPEG で、Licensed source copy は元の縦横比を保持しています。端の部分が異なるのは、切り抜かれているためです。ページのナビゲーションで Gallery を選択します。最初は 0 of 20 images available と表示されます。
Workspace に切り替えます。このターミナルは /home/labex/project/photo-gallery から始まります。pwd は、その作業ディレクトリを表示します。
pwd
別の場所へ移動していたら、cd /home/labex/project/photo-gallery で戻ります。以下の相対ファイル名は、すべてこの場所を基準にしています。ファイルはこの VM 内に残りますが、別の実験は独立したファイルとクラスターリソースで始まります。
Deployment はサービスを稼働させ続けます。一方、Job は有限の作業が完了するまで Pod を実行します。用意されたワーカーは、写真の 1 グループを処理して終了します。Indexed Job では、必要な各完了に Kubernetes がゼロから始まる番号を割り当てます。ワーカーはネイティブの JOB_COMPLETION_INDEX の値を読み、対応するグループを選びます。ワーカーを書くのではなく、Job を設定します。
配布された練習用マニフェストを読みます。cat は、ファイルを変更せず表示します。
cat starter/sample-job.yaml
重要なフィールドは、completionMode: Indexed、completions: 1、parallelism: 1 です。これらはインデックス 0 のみを要求し、同時に実行したいワーカー数を 1 にします。restartPolicy: Never は、失敗したコンテナーをその Pod 内で失敗したままにすることを意味します。別の Pod が必要かどうかは Job のポリシーが決めます。backoffLimit: 0 は、正常なソースを使うこの演習では再試行しないことを意味し、activeDeadlineSeconds: 180 は Job のアクティブ期間を制限します。その他のフィールドは、準備済みのイメージ、入力計画、出力ボリュームを接続します。変更せずに残してください。
PersistentVolumeClaim、略して PVC は、永続ストレージを要求します。準備済みの gallery-output PVC は、ワーカーの終了後もファイルを保持します。ここでは、その ReadWriteOnce ボリュームを同じノード上の複数の Pod がマウントできます。複数ノードにまたがる共有書き込みを保証するものではありません。練習用サンプルは別の sample ディレクトリに書き込むため、知らないうちに最終ギャラリーを埋めることはありません。
練習用サンプルを適用します。apply -f は指定した YAML ファイルを読み、-n は、このプロジェクトの Kubernetes リソースをまとめる名前空間を選択します。
kubectl apply -n photo-workshop -f starter/sample-job.yaml
Job のネイティブな Complete 条件を待ちます。--timeout=180s は、このコマンドが待つ時間を制限します。Job の期限を変更するものではありません。
kubectl wait -n photo-workshop --for=condition=Complete job/gallery-sample --timeout=180s
期待される最後の出力は job.batch/gallery-sample condition met です。タイムアウトだけでは失敗の証拠にはなりません。変更を判断する前に、ステップ 3 で学ぶコマンドで Job とログを調べてください。セットアップを再実行したり、配布されたリソースを削除したりしてはいけません。
Gallery に切り替え、Sample を選択し、6 of 6 images available を確認します。Windmill garden を開き、サムネイルと元画像を比較して、Back to sample を選択します。ページのナビゲーションで Gallery を選択すると、最終コレクションはまだ空です。この区別により、練習の成功を全体の納品完了と取り違えることを防げます。
すべての完了インデックスを計画する
このステップでは、入力のグループ分けを、保存する Job マニフェストに反映します。このステップでは、まだ最終処理は開始しません。
Workspace に切り替え、/home/labex/project/photo-gallery から始めます。入力の対応表全体を読みます。
cat inputs/groups.txt
Index 0 から Index 3 まで、4 行あります。最初の 3 行にはそれぞれ写真が 6 枚あり、インデックス 3 には photo-19 と photo-20 があります。completions は写真の枚数ではなく、グループ数を数えます。この納品には、20 でも 3 でもなく、4 つのインデックスの成功が必要です。
スタッフからの作業指示を読みます。
cat inputs/brief.txt
要求する並列度は 1 から 2 の範囲で許可されています。2 グループが同時に処理を進められるよう、2 を要求します。リソースが利用できない間、Kubernetes が実行する Pod はそれより少ないことがあります。要求する並列度は、再試行や終了処理中のあらゆるプロセスに対する絶対的な上限ではありません。このプロジェクトは、分散処理プラットフォームではなく、準備済みの小さな 1 ノードを使います。
準備済みの最終 Job テンプレートを編集用ファイルにコピーします。cp はファイルをコピーし、starter/ 内のコピー元はそのまま残します。
cp starter/gallery-job.yaml gallery-job.yaml
cp が No such file or directory を返したら、pwd を確認し、上記のディレクトリに戻って、正確なファイル名で同じコピーを繰り返してください。空の代替テンプレートを作ってはいけません。
sed を使い、コピーしたファイルの 2 つのフィールド値を文字どおりに置き換えます。-i は、そのファイルを直接編集します。各式は s/old/new/ という形で、古いテキストを新しいテキストに置き換えます。単一引用符は、シェルに式をそのまま渡させます。配布されたこのテンプレートには、該当するフィールドがそれぞれ 1 回だけ現れます。
sed -i 's/completions: 1/completions: 4/' gallery-job.yaml
sed -i 's/parallelism: 1/parallelism: 2/' gallery-job.yaml
適用する前に、保存した結果を読みます。
cat gallery-job.yaml
metadata.name: gallery-batch、completionMode: Indexed、completions: 4、parallelism: 2 を確認します。準備済みの処理用イメージ、コマンド、環境、マウント、リソース設定、再試行しないポリシー、180 秒の期限は維持してください。同等の YAML の書式や、別の新しい最終 Job 名でも構いません。保存したファイルは、実際に実行する Job を記述している必要があります。
置換式を打ち間違えると、何にも一致しないまま正常終了することがあるため、結果を読むことが大切です。まだファイルを適用していない場合は、元のテンプレートを再度コピーして 2 つの編集を繰り返すことで復旧できます。Job を適用した後は、名前が衝突するコピーのし直しを避けてください。Job のタスクテンプレートは通常変更できないため、別の試行には新しい Job 名を用意します。
バッチ全体を実行する
このステップでは、保存した Job を適用し、そのネイティブな完了の証拠を、実際のワーカー Pod と結び付けます。
Workspace の /home/labex/project/photo-gallery で、保存したファイルを適用します。
kubectl apply -n photo-workshop -f gallery-job.yaml
必要なすべてのインデックスを待ちます。
kubectl wait -n photo-workshop --for=condition=Complete job/gallery-batch --timeout=180s
ネイティブの Job の概要を読みます。
kubectl get job gallery-batch -n photo-workshop
完了した Job には、完了数 4/4 が表示されるはずです。describe は詳細とイベントを追加します。
kubectl describe job gallery-batch -n photo-workshop
Completion Mode: Indexed と Completed Indexes: 0-3 を探します。範囲は両端を含むため、枚数の少ない最後のグループも含めて全 4 グループを網羅しています。完了の証拠だけでは画像内容の正しさは確定しません。次に画像を調べてダウンロードします。
この Job に属する Pod だけを一覧表示します。-l はラベルで選択します。-L は、別のラベルを表示する列を追加します。ここでは完了インデックスです。
kubectl get pods -n photo-workshop -l batch.kubernetes.io/job-name=gallery-batch -L batch.kubernetes.io/job-completion-index
インデックス 0、1、2、3 に対して完了した Pod があるはずです。この表の Completed は、Pod の成功フェーズに対応します。実際のワーカー出力を読みます。--all-pods=true は Job の Pod を含め、--prefix=true は各行の出力元の Pod を識別します。
kubectl logs -n photo-workshop job/gallery-batch --all-pods=true --prefix=true
各ワーカーは割り当てられた写真の名前を示し、320 x 220 の出力を報告します。インデックス 3 は 2 枚の写真を完了するはずです。これらのログは、何が起きたかを理解する助けになります。実際のファイル、元画像との対応関係、成功してその Job に所有されている Pod も検査されます。
待機がタイムアウトしたら、describe と同じ Pod・ログのコマンドを使い、未完了のワークロードと失敗したものを区別します。Job 名が不明というエラーは、通常、ファイルが未適用か、名前が異なることを意味します。gallery-job.yaml を読み、実際の名前を使ってください。完了した Pod や練習用サンプルを消してはいけません。次のガイド付き実験では、意図的な一時的ソース障害からの復旧を学びます。
実際の画像を引き渡す
このステップでは、完成した Web サイトを調べ、利用可能な納品用ファイルを 2 つとも保存します。
Gallery に切り替え、ページのナビゲーションで Gallery を選択して更新します。20 of 20 images available を確認します。枚数の少ない最後のグループから Blue and yellow flowers(photo-20)を開きます。サムネイルをその写真自身の元画像と比較してください。別の花を繰り返しコピーしたものであってはいけません。Download thumbnail、Back to gallery の順に選択します。

最終ギャラリーには、枚数の少ない最後のグループの 2 枚を含め、20 枚の異なる処理済み写真があります。ダウンロード操作はコレクションの下にあります。
Take the collection with you で、Download source index CSV と Download gallery ZIP を選択します。CSV は、各出力に元画像のハッシュ、サムネイルのハッシュ、作者、出典ページを対応付けます。ZIP には index.csv と、photos/ 以下の実際の JPEG が入ります。ページは、実際に利用可能なファイルを報告し、不足する成果物を元画像から再生成しません。不完全なコレクションでもダウンロードできるため、引き渡す前に結果全体を確認してください。
ページのナビゲーションで Reference を選択し、配布された元の例が引き続き機能することを確認します。Credits を選ぶと、作者と Wikimedia Commons の出典リンクを確認できます。写真は CC0 ライセンスの教材用素材であり、LabEx が撮影した写真ではありません。
Workspace の /home/labex/project/photo-gallery に切り替えます。同じ実際のダウンロードをここに保存します。curl は URL にリクエストを送ります。--fail は HTTP の失敗時にエラーを返し、--silent --show-error はエラーを残して進捗表示を隠します。-o は出力ファイル名を指定します。ポート 8091 を使うと、この VM から準備済みのギャラリー Service に到達できます。
curl --fail --silent --show-error http://127.0.0.1:8091/download/index.csv -o gallery-index.csv
curl --fail --silent --show-error http://127.0.0.1:8091/download/gallery.zip -o gallery.zip
ブラウザーでのダウンロードはブラウザーが保存します。この 2 つのコマンドは、VM のワークスペースに必要なコピーを保存します。リクエストが失敗したら、Web サイトを調べ、問題を解決してからリクエストを繰り返してください。画像の不足を隠すために、受信した索引を編集してはいけません。
ヘッダーと最初の 2 行を表示します。head -n 3 は先頭の 3 行を読みます。
head -n 3 gallery-index.csv
ヘッダーは id,title,source_file,thumbnail,width,height で始まります。各行は 1 枚の写真をその写真自身の photos/photo-NN.jpg に対応付け、寸法 320,220 と実際の出典情報を含みます。
展開せずにアーカイブの一覧を表示します。python3 -m zipfile は Python 標準の ZIP ユーティリティーを実行し、-l はメンバーを一覧表示します。
python3 -m zipfile -l gallery.zip
index.csv と、photos/photo-20.jpg を含む全 20 枚の JPEG のパスを探してください。名前や個数だけでは画像の正しさは証明できないため、機能チェックでは実際の JPEG をデコードし、それぞれを自身の元画像から生成された結果と比較します。gallery-job.yaml、両方のダウンロード、練習用サンプル、完了した Pod、ソースファイル、参照用サンプル、準備済みのサービスを利用可能なまま残してください。
まとめ
Indexed Job を使い、実際の写真ギャラリーを完成させました。写真を完了インデックスに対応付け、要求する並列度に上限を設け、Pod と Job のネイティブな証拠を確認し、出典索引を添えて実際の画像を引き渡しました。練習用サンプルと参照用サンプルは、最終成果物とは別に維持しました。



