ABEJA Tech Blog

中の人の興味のある情報を発信していきます

Kubernetes & Helm を使ったミニデータパイプライン構築練習

1. はじめに

こんにちは、プラットフォーム共通基盤Gの内田です。

こちらはABEJAアドベントカレンダー2025の14日目の記事となります。

Kubernetes を触ることになったけど、「何から始めればいいのか」「選択肢が多すぎて迷う」と感じている方に向けて、Kubernetes初心者の自分が最近のキャッチアップをもとにミニデータパイプラインを作るところまでをまとめました。

Kubernetes を学び始めると、こんな疑問に直面します。

  • 用語が多い: Pod / Node / Cluster / Namespace… どれがどの入れ物?
  • ツールの選択肢が多い: Helm? Kustomize? 素の YAML? 何を使えばいい?
  • 環境構築の方法も多い: AKS / GKE / kind / minikube / Rancher Desktop… どれが自分に合ってる?

公式ドキュメントは充実していますが、初心者にとっては「どこから読めばいいか分からない」状態になりがちです。

この記事では、ローカル環境(Rancher Desktop)で小さなシステムを動かすところまでを、次の流れで整理します。

2章:最低限の基本概念
コンテナ → Pod → Node → Cluster の「箱の構造」と、Helm / Kustomize の位置づけを把握します。後半で実際に環境構築する際に出てくる用語はここで必要最小限に絞って説明します。

3章:実務を見据えたワークフロー
「マニフェストはどう管理するか」「フォルダ構成はどうするか」といった、実際に手を動かす前に知っておきたい判断軸について調べた内容を整理します。選択肢が複数ある場合は、それぞれの特徴と「なぜこの方法を選んだか」を明示します。

4章:Nginx で基本操作を体験
シンプルな Web サーバ(Nginx)を 1 個デプロイして、kubectl コマンドの使い方や Deployment / Service / Ingress の動きを確認します。

5章:データパイプラインで応用
Helm で外部 OSS(Airflow / MinIO)を導入し、複数コンポーネントを組み合わせた小さなデータパイプラインを構築します。「設計図を Kubernetes リソースにマッピングする」感覚を掴みます。

逆にこの記事に出てこないのは、ユーザー認証の追加やセキュリティの話、高度なインフラ設定などです。これらは、また別の機会に検証して記事にしようと思います。

それでは、2章から順に見ていきましょう。

2. Kubernetes・Helm・Kustomize の基本

この章では、後の章で kubectlhelmkustomize を叩いたときに迷子にならないように、Kubernetes とその周辺ツールの「最低限これだけは押さえておきたいポイント」をざっくり整理します。

Kubernetes については、container / pod / node / cluster / namespace の「入れ物の階層」と、Control Plane と Worker Nodeの役割分担を理解することが重要です。そのうえで、マニフェストをどう管理・デプロイしていくかという観点から、Helm と Kustomize の位置づけを簡単に紹介します。

細かい動作やオプションまで踏み込むとそれだけで本が一冊書けてしまうので、詳しく知りたい場合は公式ドキュメントや専門書を参照してください。

2.1 Kubernetesの役割

Kubernetes を一言で説明すると、「たくさんのコンテナを、複数のマシンの上に自動でいい感じに配置して、壊れたら再起動して、外からもアクセスしやすいように整えてくれる仕組み」です。

ここで重要なのが マニフェスト(manifest) という概念です。マニフェストは、「システムのあるべき姿」を記述した YAML ファイルのことで、「どんなコンテナを」「何個」「どんな設定で」動かすかを宣言的に書きます。このマニフェストを kubectl コマンドで Kubernetes に渡すと、Kubernetes が自動的にその状態を実現してくれます。コードとして管理できるため、再現性の確保や変更履歴の追跡が容易になります。

コンテナをそのまま単体で扱うのではなく、いくつかのレイヤに分けて管理しているのがポイントです。そのレイヤが、container / pod / node / cluster / namespace といった用語で表現されています。

2.2 Container / Pod / Node / Cluster / Namespace の関係

一番下にいるのはコンテナです。これは Docker イメージなどから起動した 1 プロセスの入れ物で、「アプリケーション本体そのもの」に一番近い存在です。 ただ、Kubernetes はコンテナを直接管理するのではなく、そのひとつ外側の「Pod」という単位を最小の実行単位として扱います。

Pod は 1 個以上のコンテナをまとめた箱のようなもので、中に入っているコンテナ同士は同じ IP アドレスとストレージの一部を共有します。多くの場合は「コンテナ 1 個だけが入った Pod」を使いますが、「アプリ本体」と「ログを横取りして別の場所に送るサイドカー」のように、2 個以上のコンテナを 1 Pod にまとめることもあります。

この Pod が乗っかっているのが Node です。Node はコンテナが実際に動いているマシン(VM や物理サーバ)です。1 台の Node の上に複数の Pod がスケジューリングされます。Rancher Desktop の場合は、Mac の中に小さな仮想マシンが用意されており、それが 1 つの Node として振る舞っています。

Node が何台かまとまったものが Cluster です。Cluster は Kubernetes が管理する単位で、「この Cluster に対して kubectl get pods を叩く」とか「この Cluster に Helm でアプリを入れる」といった操作をしています。現実には、Control Plane がいて全体を管理し、Worker Node がその指示に従って Pod を動かす、という構造になっているのですが、その話は次の節で触れます。

もうひとつよく出てくる概念が Namespace です。Namespace は Cluster の中の論理的な区切りで、コンピュータのフォルダのようなものだと思うとイメージしやすいかもしれません。同じ Cluster の中でも devstgprod のように環境ごとに Namespace を分けたり、チームやプロジェクト単位で Namespace を分けたりして、リソース名の衝突を避けたり、アクセス権限を分けたりします。kubectl get pods -n data-pipeline-n で指定しているのがこの Namespace です。

ここまでで説明した包含関係をまとめると以下のようになります。

%%{init: {'theme':'neutral'}}%%
flowchart TD
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    
    subgraph cluster["Cluster"]
      subgraph ns["Namespace"]
        subgraph node["Node"]
          subgraph pod["Pod"]
            c1["Container: my-app-container"]:::container
            c2["Container: sidecar (optional)"]:::container
          end
        end
      end
    end
    
    %% スタイルの適用
    class cluster cluster;
    class ns namespace;
    class node node;
    class pod pod;

2.3 Control Plane と Worker の役割分担

Kubernetes の Cluster の中では、「どの Pod をどの Node に置くか」「落ちた Pod をどう復旧するか」といったことを誰かが管理しなければいけません。その「司令塔」側が Control Plane、実際にコンテナを動かす側が Worker Node という役割です。

Control Plane 側には、いくつか代表的なコンポーネントが動いています。たとえば、kubectlhelm からのリクエストの入り口になっている API Server、Cluster 内の状態(どんな Pod や Service が存在するか)を保存する etcd、「この Pod をどの Node に置くか」を決める Scheduler、Deployment のようなオブジェクトの「あるべき状態」と「実際の状態」を監視して調整する Controller Manager などです。どれも聞き慣れない名前ですが、ここでは「Control Plane 側ではこういったコンポーネントが動いていて、Cluster 全体の調整をしている」とだけざっくりイメージできていれば十分です。詳しく知りたくなったときは Kubernetes の公式ドキュメントや専門書を読むと、それぞれの役割をかなり丁寧に追いかけることができます。

一方で、Worker Node 側には kubelet と呼ばれるエージェントプロセスが常駐しています。kubelet は Control Plane から「この Node にこの Pod を置いてください」といった指示を受け取り、Node 上のコンテナランタイム(containerd など)に具体的なコンテナの起動や停止を依頼します。また、kube-proxy などのコンポーネントが、Node 上での Service やロードバランシングに関わっています。

Rancher Desktop のようなローカル環境では、Control Plane と Worker が 1 台の Node 上に同居している構成になっていることが多く、利用者がそれぞれのプロセスを意識する場面はほとんどありません。ただ、「司令塔としての Control Plane と、実際にコンテナを動かす Worker Node がいる」という役割分担のイメージを持っておくと、後の章でログやイベントを見たときに理解しやすくなります。

ここまでの話から、Control PlaneとWorker Nodeを図示すると以下のような構成になっています。

%%{init: {'theme':'neutral'}}%%
flowchart LR
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;
    
    subgraph cp["Control Plane"]
      api["API Server"]:::container
      sched["Scheduler"]:::container
      cm["Controller Manager"]:::container
      etcd["etcd (状態ストア)"]:::container
    end

    subgraph workernode["Worker Node"]
      kubelet["kubelet"]:::container
      kproxy["kube-proxy"]:::container
      pod["Pod(s)"]:::pod
    end

    api <-->|指示/状態| kubelet
    cm --> api
    sched --> api
    api --> etcd
    
    %% スタイルの適用
    class cp node;
    class workernode node;

2.4 Helm 〜Kubernetes用パッケージマネージャ〜

次に、この記事の後半で使うことになる Helm について軽く触れておきます。

Helm は、Kubernetes の世界におけるパッケージマネージャです。Linux で言う apt や yum、Python で言う pip のようなものが、Kubernetes 向けに用意されているイメージです。単体の YAML ファイルを kubectl apply で流し込むのではなく、複数のマニフェストを「パッケージ」としてまとめて扱えるようにしてくれます。

中心になる概念は Chart・Release・values.yaml の 3 つです。

  • Chart: アプリケーションを構成するマニフェスト(Deployment / Service など)のテンプレート一式。「インストール用の設計図」
  • Release: Chart を特定の Cluster と Namespace にインストールした実体。例えば、同じ Airflow の Chart から airflow-devairflow-prod という 2 つの Release を作れる
  • values.yaml: Chart に流し込むパラメータ(レプリカ数、イメージタグ、リソース制限など)。環境ごとに values.dev.yaml / values.prod.yaml と分けて管理するのが一般的

流れとしては、「Chart(設計図)+ values.yaml(パラメータ)→ 実際の YAML 群を生成 → Cluster に適用 → Release」というイメージです。

%%{init: {'theme':'neutral'}}%%
flowchart LR
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;
    
    subgraph dev["ローカル開発環境 / Git"]
      chart["Helm Chart<br>(templates/*.yaml)"]:::external
      values["values.yaml<br>(values.dev.yaml など)"]:::external
    end

    chart --> render["テンプレート展開 <br> helm install/upgrade"]:::external
    values --> render

    render --> rel["Release<br>(クラスタ上のインスタンス)"]:::container

    subgraph cls["Kubernetes Cluster"]
      rel --> deploy["Deployment / StatefulSet"]:::pod
      rel --> svc["Service / Ingress"]:::service
      rel --> cm["ConfigMap / Secret"]:::container
    end
    
    %% スタイルの適用
    class cls cluster;

2.5 Kustomizeで環境ごとの差分を管理する

この記事の後半では、Helm と並んで Kustomize も使います。Helm が「パッケージとしてまとめて配布する」ための仕組みだとすると、Kustomize は「ベースの YAML を環境ごとにカスタマイズする」ための仕組みだとイメージすると分かりやすいです。

Kustomize は「テンプレートエンジン」ではなく、「既存の YAML にパッチを当てて別バージョンを作るレイヤリングツール」です。deployment.yamlservice.yaml のような素のマニフェストをベースとして用意しておき、それに対して「dev 用にはレプリカ数を 1 にする」「prod 用には 3 にする」「この環境ではラベルを追加する」といった差分だけを overlay として記述します。最終的なマニフェストは、ベースと overlay を合成して生成されます。

Kustomize は kubectl にも統合されていて、kubectl apply -k のように -k オプションを付けると、指定したディレクトリ内の kustomization.yaml を解釈して、そこから生成されたマニフェストを apply してくれます。後の章で、base/overlays/dev/overlays/prod/ のようなフォルダ構成を使って環境ごとの差分を管理する場面が出てきますが、そのときに裏側で働いているのが Kustomize です。

%%{init: {'theme':'neutral'}}%%
flowchart TD
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;
    
    subgraph base["base/"]
      bdep["deployment.yaml"]:::external
      bsvc["service.yaml"]:::external
    end

    subgraph dev["overlays/dev/"]
      dpatch["kustomization.yaml<br>+ dev 用パッチ"]:::external
    end

    subgraph prod["overlays/prod/"]
      ppatch["kustomization.yaml<br>+ prod 用パッチ"]:::external
    end

    base --> dev
    base --> prod

    dev --> ydev["生成された dev 用マニフェスト"]:::external
    prod --> yprod["生成された prod 用マニフェスト"]:::external

    ydev --> applydev["kubectl apply -k overlays/dev"]:::external
    yprod --> applyprod["kubectl apply -k overlays/prod"]:::external

実務では、Helm と Kustomize を組み合わせるケースもよくあります。たとえば、外部の OSS を導入するときは Helm Chart をそのまま使い、自分たちのアプリケーションや環境ごとの差分は Kustomize で管理する、といった分担です。また、Argo CD や Flux のような GitOps ツールも、バックエンドで Kustomize を利用してマニフェストを組み立てることが多く、Kustomize でのレイヤ構造に慣れておくと、この手のツールにもスムーズに入っていけます。

ここでは、「Helm はパッケージ化・配布のための仕組み」「Kustomize は YAML を環境ごとに管理する仕組み」というくらいのざっくりしたイメージがあれば十分です。細かいフィールドの書き方やオプションは、4 章以降で実際に手を動かしながら見ていきます。

2.6 この章のまとめ

ここまでで、「Kubernetes という箱の中に、どんな入れ物や役割がいるのか」と、「その上で Helm や Kustomize がどんな位置づけなのか」という、大まかな地図を描きました。

コンテナは 1 プロセスの入れ物で、それをまとめた Pod が Kubernetes の最小実行単位になります。Pod は Node の上に配置され、複数 Node が集まって Cluster を構成します。その Cluster の中をさらに Namespace で論理的に区切ることで、環境やチームごとの分離を行います。Cluster の内部では、Control Plane にいる API Server や etcd などのコンポーネントが全体の状態を管理し、Worker Node 上の kubelet が実際にコンテナを起動・停止しています。

Helm は、その Kubernetes 環境にアプリケーション一式をデプロイするためのパッケージマネージャで、Chart・Release・values.yaml という単位でマニフェスト一式を扱います。Kustomize は、既存の YAML をベースに、環境ごとの差分を重ねていくためのツールで、kubectl apply -k の裏側で動いています。外部の OSS を導入するときには主に Helm、自分たちのマニフェストや環境差分の管理には Kustomize、といった役割分担で使われることも多いです。

この基本的なイメージを頭の片隅に置きながら、次の章では「実際のシステムを Kubernetes 上に構築するとき、どんなワークフローやフォルダ構成、ツールの組み合わせを意識すればよいのか」を整理していきます。

3. Kubernetes でシステム構築をする場合のワークフロー

2章では、Kubernetes の基本的な箱の構造と、Helm / Kustomize のざっくりした位置付けを確認しました。ここからは、実際に Kubernetes を使ってシステムを設計・実装・テストしていくときに、どんな流れで考えると整理しやすいかをまとめておきます。

Kubernetes のエコシステムには、同じことをやるのに複数の方法がある場面がたくさんあります。たとえば、

  • マニフェストは素の YAML で書くのか、Kustomize でレイヤリングするのか、Helm でパッケージ化するのか
  • ローカル環境は kind / minikube / Rancher Desktop のどれを使うのか
  • デプロイは手元から helm upgrade するのか、GitOps ツールに任せるのか

など、いきなり全部の選択肢が視界に入ると、初心者ほど「何から決めればいいのか」が分かりにくくなります。

この章では、あくまで一つの考え方として、

  • システム設計〜リソース設計〜マニフェスト管理の流れ(ワークフローの型)
  • フォルダ・ファイル構成のベストプラクティスの一例

を整理しておきます。4章と5章では、このワークフローを小さな Nginx アプリと最小データパイプラインに対して実際に試しながら調査・検討したワークフローがうまくいくか確認していきます。

3.1 まず「どんなシステムを Kubernetes に載せたいのか」をざっくり言語化する

最初にいきなり Deployment の YAML を書き始めるのではなく、「Kubernetes に載せたいシステムの輪郭」をテキストでざっくり整理しておくと、その後のリソース設計やフォルダ構成がぶれにくくなります。

たとえば、この記事の想定システムだと、

  • 外部から HTTP でアクセスされるフロント(Nginx や Web アプリ)がある
  • 内部には、メッセージブローカや、データ加工の処理コンポーネントがいる
  • 将来的には dev / stg / prod のように環境を分けていきたい

といったレベルの整理ができていれば十分です。

この段階では、シーケンス図や構成図まできっちり描かなくてもよくて、

  • どんなコンポーネントがいるか
  • それぞれは常駐なのか、バッチなのか
  • 外部と通信する入口はどこか

くらいを箇条書きにしておくだけでも、後で「どのサービスをどの Namespace に置くか」「どこまでを Helm 1 リリースにまとめるか」といった判断がしやすくなります。

3.2 アプリの構成を Kubernetes のリソースにマッピングする

アプリケーション側の構成をざっくり描けたら、それを Kubernetes のリソースに落としていきます。ここでは完璧を目指す必要はなく、「まずは動かすための最低限のマッピング」が出来ていれば十分です。

たとえば、次のような対応をイメージします。

  • 常駐して動き続けるプロセス(Web サーバやメッセージブローカ)は Deployment や StatefulSet にする
  • それぞれの Pod に対する入口として Service を用意する
  • 外部(ブラウザなど)から HTTP でアクセスする必要があるものは Ingress を定義して、Ingress Controller(Rancher Desktop の場合は Traefik)にルーティングしてもらう
  • 最初は 1つの Namespace(例:<system>-dev)に一式をまとめ、本番を見据える段階で Namespace を環境やドメインごとに分割する

ここで大事なのは、「アプリケーションの構成図」と「Kubernetes のリソース図」を頭の中で重ねてみることです。 4章では Nginx という単純なアプリを題材に、「この Web サーバは Deployment で、この Service 経由で、この Ingress から外部に公開する」といった最小のマッピングを実際にやってみます。5章では、その考え方を複数サービスからなるデータパイプラインに拡張していきます。

3.3 マニフェストをどう管理するか:素の YAML / Helm / Kustomize

アプリケーションの構成と Kubernetes リソースの対応がざっくり見えたら、次に「マニフェストをどう管理するか」を決めます。ここが Kubernetes エコシステムでよく迷いやすいポイントのひとつです。

素の YAML を手書きして kubectl apply しても構いませんが、環境ごとの差分や、依存関係のある複数のリソースをまとめて扱いたくなってくると、Helm や Kustomize を使った方が楽になります。

この記事では、役割のイメージとして次のように割り切ることにします。

  • 外部の OSS コンポーネント(メッセージブローカ、Airflow、cert-manager など)は、基本的に Helm Chart で導入する → すでにコミュニティやベンダーが用意してくれている Chart と values.yaml を活用する

  • 自分たちのアプリケーションや Namespace / Label のような「環境に依存する部分」は Kustomize で扱うbase に共通定義を置き、overlays/devoverlays/prod で差分を管理する

もちろん、Helm だけで最後までやりきる構成もありますし、逆にすべてを Kustomize で組んで Helm を使わないプロジェクトもあります。どの選択肢が絶対正解というわけではなく、チームの好みや既存資産との相性による部分も大きいです。

ここでは、「Chart で配布されている OSS は Helm で」「自作アプリや環境差分は Kustomize で」という分担を前提に話を進めます。4章・5章でも、この方針に沿ったディレクトリ構成とコマンドの打ち方で進めていきます。

3.4 base / overlay とフォルダ構成の基本パターン

ワークフローの話とセットで決めておきたいのが、共通部分(base)と環境差分(overlay)をどう分けるか、そしてそれを反映したフォルダ構成です。

考え方としては、

  • 「環境が変わっても基本的に固定であって欲しいもの」を base に寄せる
  • 「環境ごとに変わりやすいもの」を overlay(Kustomize)や values.yaml(Helm)に寄せる

というシンプルなルールで十分です。

base に置くものの例

  • Deployment / StatefulSet / Service / Ingress の「形」

    • リソース種別
    • 名前(原則として環境間で変えたくないもの)
    • ラベルのキー構造
    • ポート番号など、めったに変えたくない設定
  • アプリケーションが前提とする ConfigMap のキー構成
  • Pod 内のコンテナ構成(サイドカーの有無など)

overlay / values に寄せるものの例

  • replica 数(dev: 1, prod: 3 など)
  • コンテナイメージのタグ(1.0.0 / 1.0.1
  • CPU / メモリの requests / limits
  • 環境ごとの外部エンドポイント(DB の URL、外部 API の URL など)
  • Namespace 名・環境ラベル(env=dev / env=prod など)

これを反映したディレクトリ構成の一例は、たとえば次のようになります。

repo-root/
  apps/
    my-app/
      base/
        deployment.yaml
        service.yaml
        ingress.yaml
        kustomization.yaml
      overlays/
        dev/
          kustomization.yaml
          namespace.yaml
        prod/
          kustomization.yaml
          namespace.yaml

ここでのポイントは次の通りです。

  • apps/ 配下に、各システム(アプリケーション)ごとの定義を置く
  • base/ に共通マニフェストを配置
  • overlays/ に環境ごとの差分(Namespace / replica 数 / リソース量 / ラベル)を配置

この構成にしておくと、

  • 環境ごとの差分が overlay で明確に分離される
  • 共通部分の変更が base 一箇所で済む
  • 後から GitOps ツール(Argo CD や Flux)を導入したいときにも、そのまま流用しやすい

というメリットがあります。

4章では、この構成を使って Web アプリ(Nginx)を題材に、apps/my-app/baseapps/my-app/overlays/dev を実際に作りながら、Kustomize の挙動と Namespace の扱いを確認します。5章では、外部 OSS(Airflow / MinIO)を Helm で追加し、複数サービスからなるデータパイプラインに拡張していきます。

3.5 ローカル → ステージング → 本番への流れを最初にイメージしておく

最後に、少しだけ将来の話に触れておきます。 この記事では Rancher Desktop 上のローカルクラスタを対象にしていますが、実務では最終的にステージング環境や本番クラスタ(EKS / GKE / on-prem など)に展開していくことを想定しているはずです。

最初の段階から、

「このフォルダ構成とツールの組み合わせを、そのままステージングと本番にも持っていけそうか?」

を頭の片隅で意識しておくと、後から大きく作り直す必要が減ります。たとえば、次のような流れをイメージしておくと、今回の学習がそのまま実務にもつながりやすくなります。

  • ローカル(Rancher Desktop)では、手元のターミナルから kubectl / kustomize / helm を直接叩いて検証する
  • ステージング環境以降では、同じ k8s/ ディレクトリを Git リポジトリ上で管理し、GitOps ツールや CI/CD から apply / install する
  • ローカルとステージング / 本番で、「マニフェストの出どころ」は同じにしておき、操作の仕組みだけを少しずつ自動化していく

また、マニフェストのテストフローとしては、

  1. Kustomize / Helm から最終的な YAML を生成する

    • kustomize build k8s/apps/<system>/overlays/dev
    • helm template ... -f values.dev.yaml
  2. 生成された YAML を、kubectl apply --dry-run=serverkubeconform などでバリデーションする
  3. 検証用クラスタ(ローカル / kind / minikube / dev クラスタ)に apply して動作を確認する

といった流れを最初から決めておくと、そのまま GitHub Actions などの CI パイプラインにも乗せやすくなります。

4. シンプル環境の構築手順 〜とりあえず Nginx を立てる〜

この章では、実際に Rancher Desktop 上の Kubernetes クラスタに Nginx を 1 個デプロイし、Ingress 経由でローカルのブラウザからアクセスできる状態までを、ステップバイステップで作っていきます。

ここまでできると、次のあたりが体感で理解できるはずです。

  • kubectl でクラスタ / Node / Pod / Service / Ingress の状態を確認する流れ
  • Deployment → Pod → Service → Ingress のつながり
  • Kustomize の base / overlays/dev を使ってマニフェストを適用する手順

4.1 前提条件と環境セットアップ

この章を始める前に、以下の環境を準備します。

必要なツールのインストール

1. Rancher Desktop のインストール

Rancher Desktop の公式サイト から Mac 用のインストーラーをダウンロードしてインストールします。

インストール後、Rancher Desktop を起動して: - Kubernetes を有効化(Settings → Kubernetes → Enable Kubernetes) - コンテナランタイムは containerd または dockerd どちらでも OK

2. Homebrew で kubectl / helm / kustomize をインストール

# kubectl(Kubernetes のコマンドラインツール)
brew install kubectl

# Helm(Kubernetes パッケージマネージャ)
brew install helm

# Kustomize(YAML カスタマイズツール)
brew install kustomize

インストール確認:

kubectl version --client
helm version
kustomize version

📝 この記事で検証したバージョン - kubectl: v1.34.2 (Kustomize v5.7.1 内蔵) - Helm: v3.18.6 - Kustomize: v5.8.0

多少バージョンが異なっても基本的な操作は同じですが、大きく異なる場合は公式ドキュメントも参照してください。

3. 作業用ディレクトリの準備

このチュートリアルでは、GitHubリポジトリ learn-k8s をクローンして使用することを前提としています。

# リポジトリをクローン
git clone https://github.com/uchida-abeja/learn-k8s.git
cd learn-k8s

または、新しく練習用ディレクトリを作成する場合は:

mkdir -p ~/learn-k8s-practice
cd ~/learn-k8s-practice

以降、このディレクトリをリポジトリルートとして、ここを起点にコマンドを実行します。

接続確認とコンテキスト設定

環境が整ったら、Rancher Desktop のクラスタに接続できているか確認します。

# 現在のコンテキスト確認
kubectl config current-context

ここで rancher-desktop のような名前が返ってくれば、この記事の前提と一致しています。

💡 current-context って何?(要点だけ) kubectl~/.kube/config という設定ファイルを読み、

  • どの API サーバ(どのクラスタのエンドポイント)に
  • どの認証情報で リクエストを送るかを、「context」 という単位で持っています。 kubectl config current-context は、今アクティブな context(=今どのクラスタに対してコマンドが実行されるか)を教えてくれるものです。 Rancher Desktop を入れると rancher-desktop という context が、Docker Desktop を入れると docker-desktop という context が追加されます。 切り替えたいときは kubectl config use-context rancher-desktop のように指定します。

⚠ 注意1: Rancher Desktop と Rancher Manager は別物

Rancher 関連でいちばんハマりやすいポイントがここです。

  • Rancher Desktop

    • ローカル開発用のツール(Mac/Windows 向け)
    • 1台のマシンの中に小さな Kubernetes(k3s)とコンテナランタイムを用意してくれる
    • GUI 設定画面はあるが、「Web 管理画面でクラスタを管理するツール」ではない
  • Rancher Manager

    • 既存の Kubernetes クラスタをまとめて管理するための サーバアプリ
    • 別の Kubernetes クラスタや VM 上で rancher/rancher が動き、ブラウザから Web UI にアクセスして使う
    • EKS / GKE / on-prem など複数クラスタを一元管理する用途

この記事で扱っているのは Rancher Desktop のクラスタ(コンテキスト名:rancher-desktop)だけです。 Rancher Manager の Web 管理画面(https://<host>:8443 など)は、今回のハンズオンとは無関係です。

⚠ 注意2: Docker Desktop のコンテキストと取り違えない

Mac だと Docker Desktop も入っていることが多く、kubectl config current-contextdocker-desktop になっている場合があります。

その場合は、この記事のコマンドを実行する前に、コンテキストを切り替えておきます。

kubectl config use-context rancher-desktop

「Rancher Desktop 上で Nginx を立てる」はずが、気づいたら Docker Desktop 側のクラスタにデプロイしていた…という事故を防ぐために、コマンドを打つ前に一度だけ確認しておくと安全です。

4.2 Step 0: クラスタにつながっているか確認する

前提が揃ったら、本当に Rancher Desktop のクラスタに接続できているかを確認します。

# ノードの状態を確認
kubectl get nodes

# 全 Namespace の Pod を確認(現時点では system 系だけのはず)
kubectl get pods -A

Rancher Desktop を起動したての状態では、以下のような出力が得られるはずです:

NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   coredns-6d668d687-rxv5j                   1/1     Running     0          46s
kube-system   helm-install-traefik-crd-q5982            0/1     Completed   0          47s
kube-system   helm-install-traefik-phj4p                0/1     Completed   1          47s
kube-system   local-path-provisioner-869c44bfbd-472nm   1/1     Running     0          46s
kube-system   metrics-server-7bfffcd44-rlv95            1/1     Running     0          46s
kube-system   svclb-traefik-f0cdde31-cpsdp              2/2     Running     0          44s
kube-system   traefik-865bd56545-92rrp                  1/1     Running     0          44s

重要なのは、kube-system Namespace に traefik が動いていることです。これは Rancher Desktop に最初から含まれている Ingress Controller で、この章で外部からのアクセスを受け付ける役割を果たします。

4.3 Step 1: my-app 用のディレクトリを作る

次に、Nginx を動かすための最小アプリケーションとして my-app を用意します。 構成は、3章で出てきたパターンのミニ版です。

# リポジトリのルートディレクトリに移動(クローンした場合)
cd learn-k8s

# ディレクトリ構造を作成(まだ存在しない場合)
mkdir -p apps/my-app/base
mkdir -p apps/my-app/overlays/dev

💡 既存リポジトリを使う場合 このリポジトリをクローンした場合、すでに apps/my-app/ 以下のファイルが存在しているので、ディレクトリ作成はスキップして次のステップに進んでください。

最終的に、次のような構成にしていきます。

apps/
  my-app/
    base/
      deployment.yaml
      service.yaml
      ingress.yaml
      kustomization.yaml
    overlays/
      dev/
        kustomization.yaml
        namespace.yaml

この my-app が、4章の主役です。

4.4 Step 2: base(共通部分)のマニフェストを書く

まずは base/ 配下に、Nginx の Deployment / Service / Ingress を書いていきます。

4.4.1 Deployment

apps/my-app/base/deployment.yaml

apiVersion: apps/v1
kind: Deployment  # Pod を管理するリソース(常駐プロセス用)
metadata:
  name: my-app-nginx  # Deployment の名前
  labels:
    app.kubernetes.io/name: my-app  # アプリケーション名
    app.kubernetes.io/component: web  # コンポーネントの役割
spec:
  replicas: 1  # Pod の数(dev環境なので1個)
  selector:  # どの Pod を管理するか(template の labels と一致させる)
    matchLabels:
      app.kubernetes.io/name: my-app
      app.kubernetes.io/component: web
  template:  # Pod のテンプレート
    metadata:
      labels:  # Pod に付けるラベル(selector と一致させる)
        app.kubernetes.io/name: my-app
        app.kubernetes.io/component: web
    spec:
      containers:
        - name: nginx  # コンテナ名
          image: nginx:1.27  # 使用する Docker イメージ
          ports:
            - containerPort: 80  # コンテナが公開するポート

標準の Nginx イメージを 1 Pod だけ動かす、最小限の Deployment です。

4.4.2 Service

apps/my-app/base/service.yaml

apiVersion: v1
kind: Service  # Pod へのアクセスを提供するリソース
metadata:
  name: my-app-nginx  # Service の名前(Ingress から参照される)
  labels:
    app.kubernetes.io/name: my-app
    app.kubernetes.io/component: web
spec:
  type: ClusterIP  # クラスタ内部からのみアクセス可能
  selector:  # どの Pod にトラフィックを流すか(Deployment の labels と一致)
    app.kubernetes.io/name: my-app
    app.kubernetes.io/component: web
  ports:
    - port: 80  # Service が受け付けるポート
      targetPort: 80  # Pod(コンテナ)のポート
      protocol: TCP
      name: http

Deployment が作る Pod に対して、クラスタ内部からアクセスするための入口(Service)を定義しています。

4.4.3 Ingress

apps/my-app/base/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress  # 外部からのHTTPアクセスをルーティングするリソース
metadata:
  name: my-app-nginx  # Ingress の名前
  labels:
    app.kubernetes.io/name: my-app
    app.kubernetes.io/component: web
spec:
  ingressClassName: traefik  # Rancher Desktop のデフォルト Ingress Controller
  rules:  # ルーティングルール
    - http:  # host を指定しない = すべてのホストで有効
        paths:
          - path: /  # パス(/ = ルート)
            pathType: Prefix  # 前方一致
            backend:  # 転送先の Service
              service:
                name: my-app-nginx  # Service 名
                port:
                  number: 80  # Service のポート
  • Rancher Desktop では Traefik がデフォルトの Ingress Controller として動いているため、ingress.class: "traefik" を付けています。
  • 今回はホスト名を指定せず、「どのホストでも / に来たリクエストは my-app-nginx Service に流す」というシンプルなルールです。

⚠ 他の Ingress とバッティングしている場合 すでに Rancher Desktop 上で別の Ingress をいじっていると、/ パスが取り合いになることがあります。その場合は、この章の Ingress の path/my-app に変えて試してみてください。

4.4.4 base の kustomization.yaml

apps/my-app/base/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# base で管理するリソース一覧
resources:
  - deployment.yaml
  - service.yaml
  - ingress.yaml

これで、base/Nginx をデプロイするための共通定義が揃いました。

4.5 Step 3: dev 環境用の overlay を作る

次に、dev 環境用の Namespace と overlay を作成します。

4.5.1 Namespace マニフェスト

apps/my-app/overlays/dev/namespace.yaml

apiVersion: v1
kind: Namespace  # リソースを論理的にグループ化する単位
metadata:
  name: my-app-dev  # dev 環境用の Namespace 名
  labels:
    app.kubernetes.io/part-of: my-app  # このアプリケーションに属する
    env: dev  # 環境ラベル

ここでは Namespace 名を my-app-dev とし、env=dev ラベルを付与しています。

4.5.2 dev 用 kustomization.yaml

apps/my-app/overlays/dev/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# すべてのリソースをこの Namespace に配置
namespace: my-app-dev

# 使用するリソース
resources:
  - ../../base  # base の定義を参照
  - namespace.yaml  # Namespace 自体も作成
  • namespace: my-app-dev と書くことで、base 内のすべてのリソースを dev Namespace に配置します。
  • 同時に namespace.yamlresources に含めているので、Namespace 自体も一緒に作られます。

4.6 Step 4: Kustomize でデプロイして、状態を確認する

4.6.1 まずは build して目で確認(任意)

# リポジトリのルートディレクトリで実行
kustomize build apps/my-app/overlays/dev

namespace: my-app-dev が付いた Deployment / Service / Ingress / Namespace の YAML がまとめて表示されれば OK です。

4.6.2 クラスタに適用する

kubectl apply -k apps/my-app/overlays/dev

これで、

  • my-app-dev Namespace
  • その中の Deployment / Service / Ingress

が一気に作成されます。

4.7 Step 5: Pod / Service / Ingress の状態を確認する

4.7.1 Namespace

kubectl get namespaces | grep my-app

my-app-dev が表示されれば OK。

4.7.2 Pod

kubectl get pods -n my-app-dev

my-app-nginx-xxxxRunning になれば成功です。

もし ImagePullBackOffCrashLoopBackOff の場合は:

kubectl describe pod -n my-app-dev my-app-nginx-xxxxxxxxxx-xxxxx

で原因を確認します。

4.7.3 Service

kubectl get svc -n my-app-dev

my-app-nginxClusterIP が表示されれば OK です。

4.7.4 Ingress

kubectl get ingress -n my-app-dev
kubectl describe ingress -n my-app-dev my-app-nginx

イベントにエラーが出ていなければ、Traefik がルールを取り込めています。

⚠ Rancher Manager の画面に Ingress が見えない問題 Rancher Manager から別クラスタを眺めている場合、「今作った Ingress が見えない?」となることがあります。 この記事では Rancher Desktop のローカルクラスタを操作しているので、Rancher Manager の UI ではなく、kubectl の結果を一次情報として見るようにしてください。

4.8 Step 6: ブラウザから Nginx にアクセスしてみる

最後に、ローカルのブラウザから実際に Nginx にアクセスしてみます。

  • まず http://localhost/ を試します。
  • 他の Ingress と競合している場合は、Ingress の path/my-app にしたうえで http://localhost/my-app を試してください。

CLI で確認する場合は:

curl -v http://localhost/
# あるいは path を変えた場合
curl -v http://localhost/my-app

Nginx の HTML が返ってくれば成功です。

4.9 お片付け(削除の方法)

検証が終わったら、作ったリソースを削除しておきます。

# Kustomize 単位でまとめて削除
kubectl delete -k apps/my-app/overlays/dev

# または Namespace ごと削除(中のリソースもまとめて消える)
kubectl delete namespace my-app-dev

Kustomizeで削除する場合、手動で追加したリソースが残るので、Namespaceごと削除しちゃった方が楽かもしれません。

4.10 構築したシステムの全体像

この章で構築したリソースの関係を図で整理しておきます。

リソースの階層構造と対応するYAMLファイル

以下の図は、Rancher Desktop上のKubernetesクラスタ内に構築されたNginxアプリケーションの全体構成を示しています。ブラウザからのHTTPリクエストがTraefik(Ingress Controller)を経由し、Ingress、Service、Podと順に転送され、最終的にNginx Containerで処理される様子を表現しています。各要素の右側には、それを定義しているYAMLファイルのパスも記載しています。

%%{init: {'theme':'neutral'}}%%
flowchart TD
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;
    
    browser["ブラウザ<br>http://localhost/"]:::external
    
    subgraph cluster["Kubernetes Cluster (Rancher Desktop)"]
        subgraph kube-system["Namespace: kube-system"]
            traefik["Traefik<br>(Ingress Controller)<br>※ Rancher Desktop標準"]:::ingress
        end
        
        subgraph ns["Namespace: my-app-dev"]
            ing["Ingress: my-app-nginx<br>Path: /<br>📄 base/ingress.yaml"]:::ingress
            svc["Service: my-app-nginx<br>ClusterIP Port: 80<br>📄 base/service.yaml"]:::service
            
            subgraph node["Node (Rancher Desktop VM)"]
                subgraph pod["Pod: my-app-nginx-xxxxx"]
                    container["Container: nginx<br>(nginx:1.27) Port: 80<br>📄 base/deployment.yaml"]:::container
                end
            end
        end
    end
    
    browser -->|HTTP Request| traefik
    traefik -->|routing| ing
    ing -->|backend| svc
    svc -->|selector match| pod

    %% スタイルの適用
    class cluster cluster;
    class ns,kube-system namespace;
    class node node;
    class pod pod;

主要コンポーネントの役割と対応するYAMLファイル

コンポーネント 役割 定義ファイル
Browser http://localhost/ にアクセス -
Traefik Rancher Desktop に最初から入っている Ingress Controller。外部からのリクエストを受け付ける ※ Rancher Desktop標準
Ingress ルーティングルールを定義。/ パスへのリクエストを my-app-nginx Service に転送 base/ingress.yaml
Service Pod への入口。ClusterIP で内部からアクセス可能にし、label selector で Pod を特定 base/service.yaml
Deployment Pod の管理者。指定した数(replicas: 1)の Pod を維持 base/deployment.yaml
Pod コンテナの実行単位。この中で Nginx コンテナが動作。Deployment により自動作成される base/deployment.yaml の template
Container Nginx が実際に動いているプロセス。Port 80 で HTTP リクエストを処理 base/deployment.yaml の containers
Namespace リソースを論理的にグループ化する単位。今回は my-app-dev overlays/dev/namespace.yaml

YAMLファイルとKubernetesリソースの対応

この図は、作成したYAMLマニフェストファイルが実際にKubernetes上でどのようなリソースとして展開されるかを示しています。Kustomizeによる統合プロセスと、各YAMLファイルから生成されるKubernetesオブジェクトの関係性を可視化しています。特に、DeploymentからPod、そしてContainerへと自動的に展開される階層構造に注目してください。

%%{init: {'theme':'neutral'}}%%
flowchart TD
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;
    
    subgraph yaml["作成したYAMLファイル"]
        ns_yaml["overlays/dev/namespace.yaml"]:::external
        dep_yaml["base/deployment.yaml"]:::external
        svc_yaml["base/service.yaml"]:::external
        ing_yaml["base/ingress.yaml"]:::external
        kust_base["base/kustomization.yaml"]:::external
        kust_dev["overlays/dev/kustomization.yaml"]:::external
    end
    
    subgraph k8s["Kubernetesリソース"]
        ns["Namespace"]:::namespace
        dep["Deployment"]:::pod
        pod["Pod"]:::pod
        container["Container"]:::container
        svc["Service"]:::service
        ing["Ingress"]:::ingress
    end
    
    ns_yaml -->|作成| ns
    dep_yaml -->|作成| dep
    dep -->|自動作成| pod
    pod -->|含む| container
    svc_yaml -->|作成| svc
    ing_yaml -->|作成| ing
    
    kust_base -->|まとめる| dep_yaml
    kust_base -->|まとめる| svc_yaml
    kust_base -->|まとめる| ing_yaml
    
    kust_dev -->|参照| kust_base
    kust_dev -->|追加| ns_yaml
    kust_dev -->|namespace指定| ns

この章では、「とりあえず Nginx を立てる」までを一通り体験しました。図で見たとおり、ブラウザ → Traefik → Ingress → Service → Pod → Container という流れでリクエストが処理されます。この「Nginx 1 個」の例が理解できていると、5章で扱うような「複数コンポーネントからなるデータパイプライン」も、同じ要領で少しずつ積み上げていけます。

5. ミニデータパイプライン構築の手順

5章では、Nginx単体の最小構成デプロイから一歩進めて 「こんな構成のデータパイプラインを動かしたい」 という設計図を起点に話を始めます。

4章で学んだ「base / overlays」パターンに加えて、ここでは以下を組み合わせます:

  • Helm Chart(Airflow / MinIO のような大規模 OSS コンポーネント)
  • ConfigMap(Python スクリプトの疎結合な管理)
  • イベント駆動型アーキテクチャ(MinIO → Webhook → Airflow)
  • Job と CronJob(初期設定と定期実行タスク)

YAML や Python コードの全文は GitHub リポジトリに置く前提で、ここでは「設計の考え方」と「つながり方」に絞って説明します。

5.1 目指すシステム構成

1つの Kubernetes Namespace(data-pipeline-dev に、次のコンポーネントを配置したシステムの構築を目指します:

コンポーネント構成

%%{init: {'theme':'neutral'}}%%
graph RL
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;

    %% 構造定義
    subgraph Host["Host (Mac)"]
        Browser["User / Web Browser"]:::external

        subgraph K8s["k8s cluster via rancher-desktop"]
            
            subgraph MgmtNode["Management Node"]
                Ingress["Ingress Controller"]:::ingress
            end

            subgraph WorkerNode["Worker Node"]
                %% コンポーネント
                Generator["Container: Rosbag Generator<br>(CronJob)"]:::pod
                Scheduler["Container: Apache Airflow<br>(Scheduler/WebServer)"]:::pod
                
                Processor["Container: Apache Airflow<br>(Processor / Ephemeral Pod)"]:::pod
                
                MinIO[("Container: MinIO")]:::service
            end
        end
    end

    %% --- データフローと接続 ---

    %% 外部アクセス
    Browser -->|"Access Web Management UI"| Ingress
    Ingress --> Scheduler
    Ingress --> MinIO

    %% Step 1: Generator -> MinIO
    Generator -->|"1. Upload dummy rosbag file"| MinIO

    %% Step 2: Detection
    Scheduler -.->|"2. Detect file upload (Sensor)"| MinIO

    %% Spawn Process
    Scheduler -->|"Spawn (Trigger)"| Processor

    %% Step 3: Fetch
    Processor -->|"3. Fetch rosbag file"| MinIO

    %% Step 4: Convert (Self processing)
    Processor -->|"4. Convert rosbag to CSV"| Processor

    %% Step 5: Upload Result
    Processor -->|"5. Upload converted CSV"| MinIO

    %% スタイルの適用
    class Host external;
    class K8s cluster;
    class MgmtNode,WorkerNode node;

各コンポーネントの役割

このシステムは、以下の6つの主要コンポーネントで構成します。

  • MinIO(S3互換ストレージ) データの保存場所です。Helm Chart を使用して構築し、raw-data(生データ)と processed-data(加工後データ)という2つのバケットを用意します。認証情報は開発用として admin/password に設定します。

  • Airflow(ワークフロー管理) データ処理の司令塔です。Helm Chart で Scheduler、Webserver、Worker を構築します。実際のデータ処理は Airflow Worker 自身では行わず、KubernetesPodOperator を使って、処理ごとに独立した Pod を動的に起動・破棄する方式をとります。

  • CronJob(データジェネレータ) データの発生源です。1分ごとに generator.py を実行する Pod を起動し、ダミーの .mcap ファイルを生成して MinIO の raw-data バケットにアップロードします。スクリプトは ConfigMap で管理します。

  • Webhook Receiver(イベント仲介) MinIO と Airflow をつなぐ接着剤のような役割です。MinIO からの「ファイルがアップロードされた」というイベント通知を受け取り、Airflow の REST API を呼び出して DAG をトリガーします。シンプルな Python アプリ(receiver.py)として実装します。

  • Job(初期設定タスク) MinIOのイベント通知を設定する一度限りのタスクです。MinIO起動後に実行され、raw-dataバケットへのファイルアップロードイベントをWebhook Receiverに通知するよう設定します。

  • Ingress(外部アクセス) ブラウザから管理画面にアクセスするための入り口です。airflow.local で Airflow の画面、minio.local で MinIO のコンソールにアクセスできるようにルーティングします。

データ処理の流れ

データは以下のようなイベント駆動型のフローで処理します。

  1. まず、CronJob が定期的に起動し、ダミーデータを生成して MinIO にアップロードします。
  2. MinIO はファイルのアップロードを検知すると、即座に Webhook Receiver へイベント通知を送ります。
  3. 通知を受け取った Webhook Receiver は、Airflow の REST API を叩いて、対応する DAG をトリガーします。
  4. トリガーされた Airflow DAG は、KubernetesPodOperator を使ってデータ処理専用の Pod を起動します。
  5. 起動した処理 Pod は、MinIO からデータを読み込んで加工し、結果を再び MinIO に保存して終了します。

5.2 3章のワークフローに沿った設計

3章で説明した開発ワークフローのベストプラクティスに従って、上記のラフ案をシステム設計していきます。

5.2.1 システムの輪郭を言語化する

まず、「どんなシステムを Kubernetes に載せたいのか」をざっくり整理します:

  • 目的: ロボットのログデータ(.mcap形式)を自動的に収集・変換するパイプライン
  • コンポーネント:
    • ストレージ: MinIO(S3互換)
    • ワークフロー管理: Airflow
    • データ生成: CronJobによる定期実行
    • イベント連携: Webhook Receiver
  • 処理フロー: イベント駆動型(ファイルアップロード → 通知 → 処理実行)
  • 環境: 開発環境(data-pipeline-dev namespace)から始め、将来的にステージング・本番へ展開

5.2.2 アプリケーション構成を Kubernetes リソースにマッピングする

次に、各コンポーネントを Kubernetes リソースに落とし込みます:

アプリケーション Kubernetes リソース 管理方法
MinIO Helm Chart → Deployment + Service minio-values.yaml
Airflow Helm Chart → 複数のDeployment + Service airflow-values.yaml
データジェネレータ CronJob + ConfigMap cronjob.yaml + pipeline-scripts
Webhook Receiver Deployment + Service webhook-receiver.yaml + webhook-scripts
MinIO初期設定 Job + ConfigMap event-setup-job.yaml
外部アクセス Ingress ingress.yaml
まとめる場所 Namespace data-pipeline-dev

5.2.3 マニフェスト管理方針の決定

3章で説明した役割分担に従います:

  • 外部OSS(Airflow / MinIO): Kustomizeの helmCharts 機能でHelm Chartを管理
  • 自作アプリ(CronJob / Webhook / Ingress): 通常のKubernetesマニフェストとしてKustomizeで管理
  • Pythonスクリプト: ConfigMapとして生成し、各Podにマウント

5.2.4 base / overlays のフォルダ構成

3章のベストプラクティスに沿ったディレクトリ構造:

apps/
  data-pipeline/
    base/               # 共通の定義
      kustomization.yaml
      cronjob.yaml
      ...
    overlays/           # 環境ごとの差分
      dev/
        kustomization.yaml
        namespace.yaml
    src/                # アプリケーションコード
      scripts/
      dags/
      webhook/

5.3 新しい知識:Job と CronJob

データパイプラインの構築で新たに登場する Kubernetes リソースとして、JobCronJob があります。

5.3.1 Job とは

Job は、一度限りのタスクを実行するためのリソースです。Deploymentのように常駐するのではなく、タスクが完了したら終了します。

特徴:

  • タスクが正常終了するまで Pod を再起動する
  • 完了後、Pod は削除されるか保持される(ttlSecondsAfterFinishedで制御)
  • 失敗時のリトライ回数を指定可能(backoffLimit

このデータパイプラインでは、MinIOのイベント通知設定に使用しています(event-setup-job.yaml):

このJobは、MinIO起動後に一度だけ実行され、raw-dataバケットに.mcapファイルがアップロードされたときに、Webhook Receiverへ通知するよう設定します。

5.3.2 CronJob とは

CronJob は、定期的にタスクを実行するためのリソースです。Linuxのcronと同じように、スケジュールに従ってJobを起動します。

特徴:

  • scheduleフィールドでcron形式のスケジュールを指定
  • 指定した時刻に自動的にJobを作成・実行
  • 実行履歴の保持数を制御可能(successfulJobsHistoryLimit

このデータパイプラインでは、2つのCronJobを使用しています:

  1. データジェネレータ(cronjob.yaml): 1分ごとにダミーの.mcapファイルを生成してMinIOにアップロードします。

  2. イベント設定の同期(event-sync-cronjob.yaml): 5分ごとにMinIOのイベント通知設定を確認し、万が一失われていた場合は再設定します。

5.3.3 Job と CronJob の使い分け

リソース 用途
Job 一度限りの初期設定や移行タスク データベースのマイグレーション、初期設定
CronJob 定期的なバッチ処理 ログのアーカイブ、データの集計、バックアップ

5.4 Helm と Kustomize の統合

このデータパイプラインでは、Kustomizeの helmCharts 機能を使ってHelm ChartとKubernetesマニフェストを統合管理します。

5.4.1 Kustomizeによる Helm 管理

kustomization.yamlhelmCharts セクション:

helmCharts:
  - name: airflow
    repo: https://airflow.apache.org
    version: 1.15.0
    releaseName: airflow
    namespace: default
    valuesFile: airflow-values.yaml
  
  - name: minio
    repo: https://charts.min.io/
    version: 5.4.0
    releaseName: minio
    namespace: default
    valuesFile: minio-values.yaml

この方式のメリット:

  • 単一コマンドでデプロイ: 後述するコマンド一発で全コンポーネントが立ち上がる
  • values ファイルで環境差分を吸収: クラスタ固有の設定を調整
  • 再現性の確保: Chart のバージョンを明示的に指定

重要な注意点:

kubectl 1.29以降、Helm Chart統合機能を使うには --enable-helm フラグが必須です。また、kubectl apply -k では現在このフラグがサポートされていないため、以下のように2段階でコマンドを実行します:

kubectl kustomize --enable-helm --load-restrictor LoadRestrictionsNone apps/data-pipeline/overlays/dev | kubectl apply -f -

--load-restrictor LoadRestrictionsNone は、Kustomizeが base ディレクトリの外にある src/ ディレクトリのファイルを読み込むために必要です。

5.4.2 ConfigMap による疎結合な管理

Pythonスクリプトは ConfigMap として生成し、コンテナイメージの再ビルドなしでコード更新が可能にします。

kustomization.yamlconfigMapGenerator セクション:

configMapGenerator:
  - name: pipeline-scripts
    files:
      - ../src/scripts/generator.py
      - ../src/scripts/processor.py

これにより、スクリプトの変更時は kubectl apply を再実行するだけで、Podが新しいConfigMapをマウントします。

5.5 Airflow と MinIO の接続設定

設計図では Airflow と MinIO の間に矢印が引いてありますが、その中身はざっくり次の 3 層に分かれます。

5.5.1 MinIO 側:S3 互換ストレージとして立てる

MinIO は Helm Chart で立てると、minio という名前の Service が ClusterIP として立ちます。 クラスタ内からは http://minio:9000 でアクセスできます。

minio-values.yaml での主な設定:

environment:
  MINIO_NOTIFY_WEBHOOK_ENABLE_PRIMARY: "on"
  MINIO_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY: "http://webhook-receiver:5000/minio-event"

5.5.2 Airflow 側:Executor と DAG のマウント設定

Airflowで分散タスク実行を行うため、CeleryExecutor を使用します。また、DAGファイル(pipeline.py)はGitSyncやPVCを使わず、ConfigMapとして各コンポーネントにマウントする方式を採用しました。

airflow-values.yaml での主な設定:

# DAGをConfigMapからマウント(Scheduler, Webserver, Worker共通)
scheduler:
  extraVolumes:
    - name: dags-volume
      configMap:
        name: airflow-dags
  extraVolumeMounts:
    - name: dags-volume
      mountPath: /opt/airflow/dags/pipeline.py
      subPath: pipeline.py

# Executor設定
executor: CeleryExecutor
redis:
  enabled: true

# API認証とDAGの自動有効化
config:
  api:
    auth_backends: airflow.api.auth.backend.basic_auth,airflow.api.auth.backend.session
  core:
    dags_are_paused_at_creation: "False"

重要なポイント:

  • DAGのマウント: airflow-dags ConfigMap を /opt/airflow/dags/pipeline.py に直接マウントすることで、GitSyncなどの複雑な構成を回避しています。
  • CeleryExecutor: タスクを複数のWorkerに分散実行するためにRedisと組み合わせて使用します。
  • Basic認証: Webhook ReceiverからのREST API呼び出しを許可します。
  • DAG自動Unpause: デプロイ直後からDAGが有効(Active)な状態になるように設定しています。

5.5.3 DAG(pipeline.py)側:KubernetesPodOperator で処理 Pod を起動

DAG の中では、KubernetesPodOperator を使って処理用の Pod を動的に起動します。 これにより、Airflow Worker とは独立した環境で重い処理を実行できます。

実際のコード(pipeline.py)の主要部分:

    processor = KubernetesPodOperator(
        task_id='process_data',
        name='ros-processor-job',
        namespace='data-pipeline-dev',
        image='python:3.9-slim',
        
        cmds=["/bin/bash", "-c"],
        arguments=[
            """
            pip install --no-cache-dir boto3 pandas && 
            python3 /app/processor.py {{ dag_run.conf.get('key', '') }}
            """
        ],
        
        volumes=[...],  # ConfigMap から processor.py をマウント
        
        env_vars={
            'MINIO_ENDPOINT': 'http://minio:9000',
            'MINIO_ACCESS_KEY': 'admin',
            'MINIO_SECRET_KEY': 'password',
        },
        
        is_delete_operator_pod=True,
    )

ポイント:

  • schedule_interval=None: 定期実行ではなく、Webhook Receiver から REST API 経由でトリガーされる
  • processor.py のマウント: ConfigMap(pipeline-scripts)から /app/processor.py としてマウント
  • 環境変数で認証情報を注入: MinIO のエンドポイントと認証情報を Pod に渡す
  • 処理完了後に Pod を削除: is_delete_operator_pod=True でリソースを節約
  • DAG実行時のパラメータ: {{ dag_run.conf.get('key', '') }} でWebhookから渡されたファイル名を取得

processor.py が実際に行う処理:

「MinIO の raw-data バケットから .mcap ファイルを読み → CSV に変換 → processed-data バケットに書き戻す」

5.5.4 Webhook Receiver:MinIO と Airflow の橋渡し

Webhook Receiver(receiver.py)は、MinIOからのイベント通知を受け取り、AirflowのREST APIを呼び出します:

@app.route('/minio-event', methods=['POST'])
def handle_minio_event():
    # ... イベント解析 ...
    if event_name == 's3:ObjectCreated:Put' and key.endswith('.mcap'):
        # Airflow DAGをトリガー
        trigger_dag('rosbag_processor_poc', {'key': key})

5.6 データパイプライン処理の最終形

設計図とコンポーネントが揃ったところで、実際にどうデータが流れるか整理します。すべてのコンポーネントを配置した最終的な構成図は以下のようになります。

%%{init: {'theme':'neutral'}}%%
graph RL
    %% Kubernetes構成要素別クラス定義
    classDef cluster fill:#e8f4f8,stroke:#0077b6,stroke-width:2px;
    classDef namespace fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px;
    classDef node fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef pod fill:#fff2cc,stroke:#d6b656,stroke-width:2px;
    classDef container fill:#ffffff,stroke:#d79b00,stroke-width:2px;
    classDef service fill:#d5e8d4,stroke:#82b366,stroke-width:2px;
    classDef ingress fill:#ffe6cc,stroke:#d79b00,stroke-width:2px;
    classDef external fill:#f9f9f9,stroke:#333,stroke-width:2px;

    subgraph Host["Host Machine"]
        User["User / Browser"]:::external
    end

    subgraph K8s["Kubernetes Cluster"]
        subgraph NS["Namespace: data-pipeline-dev"]
            
            %% Persistent Apps (常駐アプリケーション)
            subgraph Apps["Persistent Applications"]
                Ingress["Ingress Controller"]:::ingress
                MinIO["MinIO (Storage)"]:::service
                Webhook["Webhook Receiver"]:::service
                
                subgraph Airflow["Airflow Components"]
                    Web["Webserver"]:::pod
                    Sch["Scheduler"]:::pod
                    Wkr["Worker"]:::pod
                    Redis["Redis"]:::pod
                end
            end

            %% Transient / Periodic Tasks (一時的・定期的なタスク)
            subgraph Tasks["Transient & Periodic Tasks"]
                Setup["Job: minio-event-setup<br>(Initial Setup / One-time)"]:::pod
                Sync["CronJob: minio-event-sync<br>(Config Watchdog / 5min)"]:::pod
                Gen["CronJob: rosbag-generator<br>(Data Producer / 1min)"]:::pod
            end

            %% Dynamic Workers (動的ワーカー)
            subgraph Dynamic["Dynamic Workers"]
                Proc["Pod: ros-processor-job<br>(KubernetesPodOperator)"]:::pod
            end
        end
    end

    %% Access Flow
    User -->|"http://airflow.local"| Ingress
    User -->|"http://minio.local"| Ingress
    Ingress --> Web
    Ingress --> MinIO

    %% Setup & Maintenance Flow
    Setup -.->|"Configure Events (Once)"| MinIO
    Sync -.->|"Ensure Config (Periodic)"| MinIO

    %% Data Pipeline Flow
    Gen -->|"1. Upload .mcap"| MinIO
    MinIO -.->|"2. Webhook Event"| Webhook
    Webhook -->|"3. Trigger DAG API"| Web
    
    %% Airflow Internal
    Web --> Redis
    Sch --> Redis
    Wkr --> Redis
    Sch -->|"4. Spawn Pod"| Proc
    
    %% Processing Flow
    Proc -->|"5. Read/Write Data"| MinIO

    %% Styles
    class Host external;
    class K8s cluster;
    class NS namespace;

5.6.1 Step 0: 初期設定(Job による一度限りのタスク)

システムのデプロイ時、最初に Job が実行されます:

  1. minio-event-setup Job が起動
  2. MinIO の起動を待機
  3. mc コマンドを使って raw-data バケットにイベント通知を設定
    • 通知先: Webhook Receiver
    • イベント: put (アップロード)
    • フィルタ: .mcap 拡張子

このJobは一度だけ実行され、完了後10分で自動削除されます(ttlSecondsAfterFinished: 600)。

5.6.2 Step 1: CronJob がダミーデータを MinIO に投げる

  1. Kubernetes の CronJobcronjob.yaml)が 1分ごと(*/1 * * * *)に Pod を起動
  2. Pod内で generator.py を実行し、「ROS 2 のログ形式(.mcap)を模擬したダミーデータ」を生成
  3. MinIO の raw-data バケットにアップロード(例:robot_log_20251127_120000.mcap
  4. 認証情報は ConfigMap(pipeline-config)から環境変数として注入

図にすると:

[ CronJob: rosbag-generator ]
    └─(毎1分)→ [ Pod ] → generator.py
                        └─(S3 PUT)→ MinIO: raw-data/robot_log_*.mcap

5.6.3 Step 2: MinIO がイベント通知を Webhook Receiver に送る

  1. MinIO は、raw-data バケットにファイルがアップロードされると、Webhook 通知を送信
  2. 通知先は minio-values.yaml の環境変数で設定:http://webhook-receiver:5000/minio-event
  3. Webhook Receiver(receiver.py)が以下の処理を実行:
    • MinIO からの POST リクエストを受信
    • イベント内容を解析(バケット名、ファイル名、イベントタイプ)
    • .mcap ファイルの場合、Airflow REST API を呼び出して DAG をトリガー

図にすると:

[ MinIO ]
    └─(イベント通知)→ [ Webhook Receiver: /minio-event ]
                            └─(REST API)→ [ Airflow: POST /api/v1/dags/rosbag_processor_poc/dagRuns ]

5.6.4 Step 3: Airflow DAG が処理 Pod を起動してデータを加工

  1. Webhook Receiver からトリガーされた DAG(rosbag_processor_poc)が起動
  2. DAG は KubernetesPodOperator を使って、専用の処理 Pod を起動:
    • イメージ: python:3.9-slim
    • マウント: ConfigMap(pipeline-scripts)から processor.py/app/ にマウント
    • 環境変数: MinIO のエンドポイントと認証情報(admin / password
  3. 処理 Pod 内で processor.py が実行され、以下の処理を実行:
    • MinIO の raw-data バケットから .mcap ファイルをダウンロード
    • CSV として読み込み、データを加工(processed_flagprocessed_at カラムを追加)
    • 結果を .csv ファイルとして processed-data バケットにアップロード
  4. 処理完了後、Pod は自動的に削除される(is_delete_operator_pod=True

図にすると:

[ Airflow DAG: rosbag_processor_poc ]
    └─[ KubernetesPodOperator ]
        └─ 処理 Pod を起動
            └─ processor.py
                ├─ MinIO: raw-data/*.mcap をダウンロード
                ├─ CSV に変換 & データ加工
                └─ MinIO: processed-data/*.csv にアップロード

この方式の利点:

  • リソースの効率化: 処理が必要な時だけ Pod を起動し、完了後は削除
  • 依存関係の分離: Airflow Worker に pandas などをインストールする必要がない
  • スケーラビリティ: 複数ファイルの並列処理が容易(max_active_runs=3

5.6.5 監視用CronJob:イベント設定の同期

バックグラウンドで、もう一つのCronJob(minio-event-sync)が5分ごとに実行されます:

  1. MinIO の現在のイベント設定を確認
  2. 設定が消えていれば再設定

これにより、何らかの理由でMinIOのイベント通知設定が失われた場合でも、自動的に復旧します。

5.7 実際にデプロイして動作確認する

ここまでで設計と実装が揃ったので、実際にRancher Desktop上にデプロイして動作を確認します。

5.7.1 デプロイコマンド

kubectl kustomize --enable-helm --load-restrictor LoadRestrictionsNone apps/data-pipeline/overlays/dev | kubectl apply -f -

このコマンドは以下を実行します:

  1. kustomization.yaml を読み込み
  2. base の定義をマージ
  3. Helm Chart(Airflow / MinIO)を展開
  4. ConfigMap(スクリプト類)を生成
  5. すべてのリソースを data-pipeline-dev namespace に適用

5.7.2 リソースの確認

kubectl get pods -n data-pipeline-dev

5.7.3 管理画面へのアクセス

方法1: Ingress経由(推奨)

/etc/hosts に以下を追加:

127.0.0.1 airflow.local minio.local

ブラウザでアクセス:

  • Airflow UI: http://airflow.local (admin/admin)
  • MinIO Console: http://minio.local (admin/password)

アクセスに成功すると以下の画面が確認できます。

airflow ログイン後画面

minio ログイン後画面

方法2: Port Forward

kubectl port-forward svc/airflow-webserver 8080:8080 -n data-pipeline-dev
kubectl port-forward svc/minio 9001:9001 -n data-pipeline-dev

5.7.4 動作確認

1. CronJobによるデータ生成を確認

kubectl logs -n data-pipeline-dev -l app.kubernetes.io/name=rosbag-generator --tail=20

Successfully uploaded robot_log_*.mcap というメッセージが1分ごとに出力されていれば成功です。

2. MinIOでファイルを確認

MinIO Console (http://minio.local) にアクセスし、raw-data バケットを確認。 .mcap ファイルが1分ごとに追加されているはずです。

3. Webhook Receiverのログを確認

kubectl logs -n data-pipeline-dev -l app.kubernetes.io/name=webhook-receiver --tail=20

Triggered Airflow DAG for file: robot_log_*.mcap というメッセージが表示されていれば、 MinIOからの通知を正しく受信し、Airflowをトリガーできています。

4. Airflow DAGの実行状況を確認

Airflow UI (http://airflow.local) にアクセスし、rosbag_processor_poc DAGを確認:

  • DAGがunpause状態(緑の再生ボタン)になっている
  • DAG Runsタブで実行履歴を確認
  • 各実行の process_data タスクが成功(緑)になっている

5. 処理済みデータを確認

MinIO Console で processed-data バケットを確認。 .csv ファイルが追加されていれば、データ処理パイプライン全体が正常に動作しています。

5.8 トラブルシューティング

ここまでの検証環境準備の中で出会ったトラブルをまとめておきます。

5.8.1 DAGが実行されない

症状: Webhook Receiverは動作しているが、DAGが実行されない

確認手順:

  1. DAGがPause状態になっていないか確認: Airflow UIで rosbag_processor_poc がONになっているか。

  2. Webhook Receiverのログを確認: bash kubectl logs -n data-pipeline-dev -l app.kubernetes.io/name=webhook-receiver 403 Forbiddenが表示されている場合、Basic認証が正しく設定されていません。 airflow-values.yamlconfig.api.auth_backends を確認してください。

  3. Airflow SchedulerがDAGを認識しているか確認: bash kubectl exec -n data-pipeline-dev deployment/airflow-scheduler -c scheduler -- airflow dags list | grep rosbag_processor_poc

5.8.2 Workerがタスクを実行しない

症状: DAGはトリガーされるが、タスクがqueued状態のまま

確認手順:

  1. RedisとWorkerの接続を確認: bash kubectl logs -n data-pipeline-dev deploy/airflow-worker Connected to redisが表示されれば正常。表示されない場合、Redisが起動していない可能性があります: bash kubectl get pods -n data-pipeline-dev -l component=redis

  2. Celeryのキュー状態を確認: bash kubectl exec -n data-pipeline-dev airflow-worker-0 -c worker -- celery -A airflow.providers.celery.executors.celery_executor_utils inspect stats

5.8.3 MinIOイベント通知が動作しない

症状: ファイルはアップロードされるが、Webhook Receiverに通知が届かない

確認手順:

  1. イベント設定Jobのログを確認: bash kubectl logs -n data-pipeline-dev job/minio-event-setup Event notification added successfullyが表示されていれば設定は完了しています。

  2. MinIOのイベント設定を直接確認: bash kubectl exec -n data-pipeline-dev deployment/minio -- mc alias set myminio http://localhost:9000 admin password kubectl exec -n data-pipeline-dev deployment/minio -- mc event list myminio/raw-data

  3. イベント設定を再適用: bash kubectl delete job minio-event-setup -n data-pipeline-dev kubectl kustomize apps/data-pipeline/overlays/dev --enable-helm --load-restrictor LoadRestrictionsNone | kubectl apply -f -

5.9 リソースのクリーンアップ

検証が完了したら、以下のコマンドでリソースを削除できます:

kubectl delete namespace data-pipeline-dev

個別のリソースを削除したい場合:

# Kustomize経由で削除(Helm Chartは削除されない場合があるため、Namespace削除を推奨)
kubectl delete namespace data-pipeline-dev

5章では、3章で説明した開発ワークフローのベストプラクティスに従って、実際のデータパイプラインを構築しました。

この構成は、ローカル環境(Rancher Desktop)で動作確認した後、同じマニフェストをステージング・本番環境にも展開できます。base/overlaysの分離により、環境ごとの差分管理が容易になっています。

実際の YAML や Python コードは GitHub リポジトリにまとめてありますので、手元の Rancher Desktop で試してみてください。

6. まとめ

この記事では、「Kubernetesよく分からん……」というところからスタートして、Rancher Desktop上に小さなシステムを2つ動かしてみました。1つ目はNginxだけのシンプルなWebアプリ、2つ目はAirflow + MinIO + CronJobを組み合わせたミニデータパイプラインです。

前半では、Pod / Deployment / Service / Ingress / Namespace、それからControl PlaneとWorker Nodeにどんなコンポーネントがいるのかをざっくり押さえつつ、「YAMLを書くとクラスタの中で何が起きるのか?」をイメージできるようにしてきました。kubectl config current-context を確認しながら、Kustomizeのbase/overlaysでDeployment → Service → Ingressを管理して、最終的にブラウザから http://localhost/ でNginxにアクセスできるところまで手を動かしました。

後半では、「こんな感じのデータパイプラインを動かしたい」というラフな設計図から出発して、1つのNamespaceの中にMinIO(S3互換ストレージ)とAirflow(DAG実行基盤)、ダミーデータを作るCronJob、MinIOとAirflowを繋ぐWebhook Receiverを置き、 CronJob → MinIO(raw) → Webhook Receiver → Airflow(DAG)→ MinIO(processed) というシンプルな流れを実際に動かしてみました。AirflowとMinIOの連携は、KubernetesPodOperatorで起動する処理用Podに環境変数(エンドポイント、アクセスキー、シークレットキー)を渡し、Pod内でboto3を使ってS3互換APIでMinIOにアクセスする形を確認しています。

このあたりまでできると、

  • 外部OSSはHelm Chartで箱ごと入れる
  • 自分たちのアプリや環境差分はKustomizeで面倒を見る

という役割分担のイメージがだいぶハッキリしてくると思います。Rancher Desktop上の「1ノード・1クラスタ」という小さな世界ではありますが、「自分の手でミニマムなデータ基盤を構築してみた」という感覚は残るはずです。

とはいえ、ここまでで作ったものはあくまで学習用の環境です。本番システムと比べると、まだまだ足りていないポイントがたくさんあります。

例えば:

  • 認証・認可(OIDC / SSO、NamespaceごとのRBAC、NetworkPolicyなど)
  • 複数ノード構成(Control Plane / Workerの分離、ストレージの冗長化)
  • 信頼性・可用性(PodDisruptionBudget、ローリングアップデート時の挙動設計)
  • 監視・ログ(メトリクス収集、ログ集約、ダッシュボード)
  • CI/CD・GitOps(ローカルからの kubectl apply ではなく、Git起点のデプロイフロー)

今回触れた infra/ や apps、base と overlays といったディレクトリ構成は、そのままGitHub ActionsやArgo CDに載せやすい形になっているので、「今はローカル検証だけど、この先ステージング・本番へ広げていくイメージ」もつかみやすいはずです。

ざっくり言うと、このブログでやったのは「Rancher Desktop上の小さなクラスタで、Kubernetes / Helm / Kustomizeの基本と、ミニなデータパイプライン構成を手を動かしながら一周してみた」というところまでです。本番へ向けた上記の課題は、今後別のブログ記事にまとめていければと思います。

参考資料

最後に、この記事から一歩進むための公式リソースをまとめておきます。

Kubernetes / Helm / Kustomize

  • Kubernetes公式ドキュメント(概念説明、チュートリアル、リファレンス)(Kubernetes Documentation)
  • kubectlコマンドのチートシート(kubectl Cheat Sheet)
  • Helm公式ドキュメント(Chartの構造、helm install / helm upgrade の詳細など)(Helm Documentation)
  • Kustomize公式ドキュメント(kubectl apply -k の仕組みやパッチの書き方)(Kustomize Documentation)

最初は Kubernetes の Basics チュートリアルと、kubectl チートシートに目を通しつつ、この記事で出てきたコマンドを照らし合わせてみると理解が深まりやすいです。

ローカル環境 / ツール

  • Rancher Desktop公式ドキュメント(インストール方法、Kubernetesクラスタ設定など)(Rancher Desktop Documentation)
  • Helm Chartの検索ポータル(Airflow / MinIO などのChartもここから探せます)(Artifact Hub)

この記事の構成を手元でなぞる場合は、Rancher Desktopのインストール手順と、helm の基本的な使い方の章をざっと確認しておくのがおすすめです。

Airflow / MinIO

  • Apache Airflow公式ドキュメント(Helm Chart、Kubernetes Executor関連の解説など)(Apache Airflow Documentation)
  • MinIO公式ドキュメント(Kubernetesへのデプロイ方法、S3互換APIの使い方)(MinIO Documentation)

5章のミニデータパイプラインをもう少し発展させたい場合は、Airflowのチュートリアルと、MinIOのKubernetes向けガイドが良い足がかりになります。

We Are Hiring!

ABEJAは、テクノロジーの社会実装に取り組んでいます。 技術はもちろん、技術をどのようにして社会やビジネスに組み込んでいくかを考えるのが好きな方は、下記採用ページからエントリーください! (新卒の方やインターンシップのエントリーもお待ちしております!) careers.abejainc.com

特に下記ポジションの募集を強化しています!ぜひ御覧ください!

トランスフォーメーション領域:データサイエンティスト

トランスフォーメーション領域:データサイエンティスト(ミドル)

トランスフォーメーション領域:データサイエンティスト(シニア)