0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GKE + Argo Workflows で構築するセキュアで効率的なMLOpsパイプライン

Posted at

GKE + Argo Workflows で構築するセキュアで効率的なMLOpsパイプライン

はじめに

本記事では、Google Kubernetes Engine(GKE)とArgo Workflowsを活用した機械学習パイプラインの実装について、Kubernetes設定の観点から詳しく解説します。

特に以下の技術的なポイントに焦点を当てます:

  • Terraformによる Infrastructure as Code
  • Workload Identity による安全な認証
  • Argo Workflows による ML パイプライン実行
  • GKE Spot Instances を活用したコスト最適化
  • 永続リソースと一時リソースの分離設計
  • RBAC(Role-Based Access Control)の適切な設定

システムアーキテクチャ概要

このシステムは、金融データの分析パイプラインとして以下の構成で実装されています:

リソース分離の設計思想

このアーキテクチャの最大の特徴は、永続リソース(Persistent)と一時リソース(Ephemeral)を完全に分離していることです。

リソース種別 管理場所 ライフサイクル 目的
Persistent terraform/persistent/ 長期保持 データ、認証情報など重要資産
Ephemeral terraform/ephemeral/ 実行時のみ GKEクラスタ、ワークフロー実行環境

この設計により、以下のメリットがあります:

  • ✅ データを失うことなくクラスタを破棄できる
  • ✅ コスト削減(使用時のみGKEクラスタを起動)
  • ✅ インフラの再現性が高い
  • ✅ 環境の汚染を防ぐ

1. Terraform による永続リソースの管理

1.1 永続リソースの定義

まず、データや認証情報など、絶対に削除してはいけないリソースを定義します。

terraform/persistent/main.tf

# GCS Bucket(データストレージ)
resource "google_storage_bucket" "data_bucket" {
  name          = var.bucket_name
  location      = "US"
  force_destroy = false

  uniform_bucket_level_access = true

  # 誤削除防止
  lifecycle {
    prevent_destroy = true
  }
}

# BigQuery Dataset(分析用データセット)
resource "google_bigquery_dataset" "dataset" {
  dataset_id = var.bq_dataset_name
  location   = var.region
}

# Service Account(GKEから使用)
resource "google_service_account" "app_sa" {
  account_id   = "chart-movement-sa"
  display_name = "Chart Movement Detection App SA"
}

1.2 IAM 権限の設定

Service Account に必要最小限の権限を付与します。

# BigQuery データ編集権限
resource "google_project_iam_member" "bq_editor" {
  project = var.project_id
  role    = "roles/bigquery.dataEditor"
  member  = "serviceAccount:${google_service_account.app_sa.email}"
}

# BigQuery ジョブ実行権限
resource "google_project_iam_member" "bq_job_user" {
  project = var.project_id
  role    = "roles/bigquery.jobUser"
  member  = "serviceAccount:${google_service_account.app_sa.email}"
}

# BigQuery Read Session(大規模データ読み取り用)
resource "google_project_iam_member" "bq_read_session_user" {
  project = var.project_id
  role    = "roles/bigquery.readSessionUser"
  member  = "serviceAccount:${google_service_account.app_sa.email}"
}

# GCS バケット管理権限
resource "google_storage_bucket_iam_member" "bucket_admin" {
  bucket = var.bucket_name
  role   = "roles/storage.objectAdmin"
  member = "serviceAccount:${google_service_account.app_sa.email}"
}

[TIP]
roles/bigquery.readSessionUser は、BigQuery Storage Read API を使用する場合に必要です。大量データを効率的に読み取れます。

1.3 Terraform State の管理

永続リソースの状態は GCS バケットで管理します。

terraform/persistent/backend.tf

terraform {
  backend "gcs" {
    bucket = "terraform-state-btcusd-analysis"
    prefix = "terraform/state-persistent"
  }
}

これにより、複数の環境や開発者間で状態を共有できます。

2. GKE クラスタの構成(Ephemeral)

2.1 GKE クラスタの作成

一時リソースとして、実行時のみ起動する GKE クラスタを定義します。

terraform/ephemeral/main.tf

resource "google_container_cluster" "primary" {
  name                = var.cluster_name
  location            = var.zone
  deletion_protection = false

  remove_default_node_pool = true
  initial_node_count       = 1

  # Workload Identity の有効化
  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }
}

[IMPORTANT]
deletion_protection = false により、クラスタを安全に削除できます。これはコスト削減の要です。

2.2 Spot Instances を使用したノードプール

コスト削減のため、GKE Spot Instances(プリエンプティブルVM)を使用します。

terraform/ephemeral/main.tf

resource "google_container_node_pool" "spot_nodes" {
  name     = "spot-node-pool"
  location = var.zone
  cluster  = google_container_cluster.primary.name

  # オートスケーリング設定
  autoscaling {
    min_node_count = 0  # 未使用時は0台
    max_node_count = 5  # 最大5台まで自動拡張
  }

  node_config {
    preemptible  = true              # Spot Instanceを使用
    machine_type = "e2-standard-2"   # 2vCPU, 8GB RAM
    oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"]

    # Workload Identity の有効化
    workload_metadata_config {
      mode = "GKE_METADATA"
    }

    # ノードにラベルを付与(Pod配置制御用)
    labels = {
      "cloud.google.com/gke-spot" = "true"
    }
  }
}

Spot Instances のメリット:

  • 📉 通常料金の最大91%割引
  • 🔄 自動スケーリング(min_node_count = 0)で未使用時のコストゼロ
  • ⚡ 短時間のML計算に最適

デメリットと対策:

  • ❌ 中断される可能性がある → リトライ戦略で対応(後述)

3. Workload Identity による安全な認証

3.1 Workload Identity とは

Workload Identity は、Kubernetes Service Account と GCP Service Account を紐付ける仕組みです。これにより、Pod から GCP リソースに安全にアクセスできます。

3.2 Kubernetes Service Account の作成

terraform/ephemeral/main.tf

resource "kubernetes_service_account_v1" "workflow_runner" {
  metadata {
    name      = "workflow-runner"
    namespace = "argo"
    annotations = {
      # GCP Service Account との紐付け
      "iam.gke.io/gcp-service-account" = data.terraform_remote_state.persistent.outputs.service_account_email
    }
  }
  depends_on = [helm_release.argo_workflows]
}

3.3 IAM Binding の設定

terraform/ephemeral/main.tf

resource "google_service_account_iam_member" "workload_identity_binding" {
  service_account_id = "projects/${var.project_id}/serviceAccounts/${data.terraform_remote_state.persistent.outputs.service_account_email}"
  role               = "roles/iam.workloadIdentityUser"
  member             = "serviceAccount:${var.project_id}.svc.id.goog[argo/workflow-runner]"
}

この設定により:

  • JSONキーが不要(セキュリティリスクを削減)
  • 自動ローテーション(トークンは短命)
  • 最小権限の原則を実現

[WARNING]
member の形式 serviceAccount:<PROJECT_ID>.svc.id.goog[<NAMESPACE>/<K8S_SA>] を正確に記述する必要があります。

4. Argo Workflows の構成

4.1 Helm による Argo Workflows のインストール

terraform/ephemeral/main.tf

resource "helm_release" "argo_workflows" {
  name             = "argo-workflows"
  repository       = "https://argoproj.github.io/argo-helm"
  chart            = "argo-workflows"
  namespace        = "argo"
  create_namespace = true
  version          = "0.46.2"

  timeout = 1200

  # 認証モードの設定
  set {
    name  = "server.extraArgs[0]"
    value = "--auth-mode=server"
  }

  # Controller 用 Spot Instance Toleration
  set {
    name  = "controller.tolerations[0].key"
    value = "cloud.google.com/gke-spot"
  }
  set {
    name  = "controller.tolerations[0].operator"
    value = "Equal"
  }
  set {
    name  = "controller.tolerations[0].value"
    value = "true"
    type  = "string"
  }
  set {
    name  = "controller.tolerations[0].effect"
    value = "NoSchedule"
  }

  # Server 用 Spot Instance Toleration(同様の設定)
  # ...
}

[TIP]
Tolerations を設定することで、Argo の Controller と Server も Spot Instance 上で動作し、さらなるコスト削減が可能です。

4.2 Artifact Repository の設定(GCS)

Argo Workflows では、タスク間でデータを受け渡すために Artifact Repository が必要です。

terraform/ephemeral/main.tf

resource "kubernetes_config_map_v1" "artifact_repositories" {
  metadata {
    name      = "artifact-repositories"
    namespace = "argo"
  }

  data = {
    "default-v1" = yamlencode({
      gcs = {
        bucket    = data.terraform_remote_state.persistent.outputs.bucket_name
        keyFormat = "artifacts/{{workflow.name}}/{{pod.name}}"
      }
    })
  }

  depends_on = [helm_release.argo_workflows]
}

この設定により、ワークフロー間のデータは自動的に GCS に保存されます。

4.3 Workflow 定義

実際の ML パイプラインを定義します。

k8s/workflow.yaml

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: chart-movement-pipeline-
spec:
  entrypoint: unified-pipeline
  serviceAccountName: workflow-runner
  activeDeadlineSeconds: 10800  # 3時間でタイムアウト
  
  # Spot Instance の設定
  podGC:
    strategy: OnPodSuccess  # 成功したPodは削除、失敗したPodは保持
  nodeSelector:
    cloud.google.com/gke-spot: "true"  # Spot Instanceを指定
  tolerations:
  - key: "cloud.google.com/gke-spot"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  
  templates:
  - name: unified-pipeline
    dag:
      tasks:
      - name: ingest-data
        template: data-ingestion-container
        # Spot Instance 中断時のリトライ戦略
        retryStrategy:
          limit: "3"               # 最大3回リトライ
          retryPolicy: "Always"    # 常にリトライ
          backoff:
            duration: "1m"         # 初回リトライまで1分待機
            factor: "2"            # 待機時間を2倍ずつ増加
            maxDuration: "10m"     # 最大10分まで待機
      
      - name: train-model
        dependencies: [ingest-data]
        template: genetic-learning-container
        arguments:
          artifacts:
          - name: dataset
            from: "{{tasks.ingest-data.outputs.artifacts.dataset}}"
        retryStrategy:
          limit: "3"
          retryPolicy: "Always"
          backoff:
            duration: "1m"
            factor: "2"
            maxDuration: "10m"

ポイント解説:

設定項目 説明
serviceAccountName: workflow-runner Workload Identity を使用
activeDeadlineSeconds: 10800 3時間でタイムアウト(コスト暴走を防ぐ)
nodeSelector Spot Instance のみを使用
tolerations Spot Instance の taint を許容
retryStrategy Spot 中断時に自動リトライ

4.4 データ取得タスク

- name: data-ingestion-container
  outputs:
    artifacts:
    - name: dataset
      path: /tmp/dataset.pkl  # このファイルが自動的にGCSに保存される
  container:
    image: PLACEHOLDER_IMAGE
    imagePullPolicy: IfNotPresent
    command: ["uv", "run", "python", "src/data_ingestion.py", "--output-file", "/tmp/dataset.pkl"]
    env:
    - name: GOOGLE_CLOUD_PROJECT
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: bigquery-project-name
    - name: BIGQUERY_DATASET_NAME
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: bigquery-dataset-name
    # ...他の環境変数

4.5 モデル学習タスク

- name: genetic-learning-container
  inputs:
    artifacts:
    - name: dataset
      path: /tmp/dataset.pkl  # 前タスクのArtifactを受け取る
  container:
    image: PLACEHOLDER_IMAGE
    imagePullPolicy: IfNotPresent
    command: ["uv", "run", "python", "src/genetic_learning.py", "--input-file", "/tmp/dataset.pkl"]
    env:
    - name: GOOGLE_CLOUD_PROJECT
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: bigquery-project-name
    - name: GCS_BUCKET_NAME
      valueFrom:
        secretKeyRef:
          name: app-secrets
          key: gcs-bucket-name
    - name: GCS_CHECKPOINT_PATH
      value: "data/checkpoints/genetic_state.pkl"

5. RBAC(Role-Based Access Control)の設定

5.1 なぜRBAC設定が必要か

Argo Workflows が正常に動作するには、適切な Kubernetes 権限が必要です。

操作 必要な権限 理由
Workflow 実行 workflowtaskresults の作成/更新 タスク間のデータ受け渡し
Pod 監視 pods の取得/監視 ワークフロー状態の追跡
ログ取得 pods/log の取得 デバッグ用
Secret 参照 secrets の取得 環境変数の読み取り

5.2 Role の定義

terraform/ephemeral/main.tf

resource "kubernetes_role_v1" "workflow_runner" {
  metadata {
    name      = "workflow-runner-role"
    namespace = "argo"
  }

  rule {
    api_groups = ["argoproj.io"]
    resources  = ["workflowtaskresults"]
    verbs      = ["create", "get", "patch", "delete"]
  }

  depends_on = [helm_release.argo_workflows]
}

5.3 RoleBinding の設定

terraform/ephemeral/main.tf

resource "kubernetes_role_binding_v1" "workflow_runner" {
  metadata {
    name      = "workflow-runner-binding"
    namespace = "argo"
  }

  role_ref {
    api_group = "rbac.authorization.k8s.io"
    kind      = "Role"
    name      = kubernetes_role_v1.workflow_runner.metadata[0].name
  }

  subject {
    kind      = "ServiceAccount"
    name      = "workflow-runner"
    namespace = "argo"
  }

  depends_on = [kubernetes_service_account_v1.workflow_runner]
}

5.4 追加の RBAC 設定(手動適用)

Argo がすべての機能を使用するには、追加の権限が必要です。

k8s/argo-role-patch.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: argo-role
  namespace: argo
rules:
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases        # リーダー選出用
  verbs:
  - create
  - get
  - update
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
- apiGroups:
  - argoproj.io
  resources:
  - workflowtaskresults
  verbs:
  - create
  - patch
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - watch
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - pods/log
  verbs:
  - get

適用コマンド:

kubectl apply -f k8s/argo-role-patch.yaml

[NOTE]
この設定は Argo Workflows のデフォルト Role では不足している権限を補完します。

6. Kubernetes Secret の管理

6.1 Terraform による Secret 作成

terraform/ephemeral/main.tf

resource "kubernetes_secret_v1" "app_secrets" {
  metadata {
    name      = "app-secrets"
    namespace = "argo"
  }

  data = {
    "bigquery-project-name" = data.terraform_remote_state.persistent.outputs.project_id
    "bigquery-dataset-name" = data.terraform_remote_state.persistent.outputs.bq_dataset_name
    "bigquery-table-name"   = data.terraform_remote_state.persistent.outputs.bq_table_name
    "gcs-bucket-name"       = data.terraform_remote_state.persistent.outputs.bucket_name
  }

  depends_on = [helm_release.argo_workflows]
}

Terraform Remote State の活用:

data "terraform_remote_state" "persistent" {
  backend = "gcs"
  config = {
    bucket = var.state_bucket_name
    prefix = "terraform/state-persistent"
  }
}

これにより、Persistent リソースの情報を Ephemeral リソースから参照できます。

6.2 ローカル開発用の Secret 作成

ローカル環境では、以下のスクリプトで Secret を作成します。

# .env ファイルから Kubernetes 用の .env.k8s を生成
python clean_env.py

# Secret を作成
kubectl create secret generic app-secrets \
  --from-env-file=.env.k8s \
  -n argo \
  --dry-run=client -o yaml | kubectl apply -f -

7. 実際の運用フロー

7.1 初回セットアップ

# 1. 永続リソースのデプロイ(一度だけ実行)
cd terraform/persistent
terraform init
terraform apply

# 2. State Bucket の設定確認
terraform output

7.2 パイプライン実行時

# 1. GKE クラスタの起動
cd terraform/ephemeral
terraform init
terraform apply

# 2. Argo UI へのアクセストークン取得
kubectl get secret argo-ui-admin-token -n argo -o jsonpath='{.data.token}' | base64 -d

# 3. Argo UI にアクセス
kubectl port-forward -n argo svc/argo-workflows-server 2746:2746

# ブラウザで https://localhost:2746 を開く

# 4. ワークフローの実行
argo submit -n argo k8s/workflow.yaml --watch

# 5. ログの確認
kubectl logs -n argo -l workflows.argoproj.io/workflow -f

7.3 実行後のクリーンアップ

# GKE クラスタを破棄してコスト削減
cd terraform/ephemeral
terraform destroy

[IMPORTANT]
terraform destroy でクラスタを削除しても、Persistent リソース(BigQuery、GCS)のデータは保持されます。

8. コスト最適化のまとめ

このアーキテクチャにより、以下のコスト削減を実現しています:

施策 削減効果 詳細
Spot Instances 最大91%削減 プリエンプティブルVMの利用
オートスケーリング min=0により未使用時ゼロ 実行時のみノードを起動
クラスタの完全破棄 24時間x30日のコスト削減 実行時のみクラスタ起動
リトライ戦略 Spot中断による再実行コストを最小化 効率的なバックオフ

試算例(東京リージョン):

  • 通常のGKE(常時稼働): 約 $150/月
  • Spot + 完全破棄: 実行時間が月10時間の場合 約 $5/月

97%のコスト削減

9. セキュリティのベストプラクティス

このシステムで実装しているセキュリティ対策:

✅ 認証・認可

  • Workload Identity(JSONキー不要)
  • RBAC による最小権限の原則
  • Service Account の適切な分離

✅ データ保護

  • GCS の Uniform Bucket-Level Access
  • BigQuery の IAM 権限制御
  • Terraform State の暗号化

✅ 運用

  • prevent_destroy による誤削除防止
  • activeDeadlineSeconds によるコスト暴走防止
  • Terraform による Infrastructure as Code

[CAUTION]
本番環境では、ネットワークポリシーや Pod Security Policy の追加を推奨します。

10. トラブルシューティング

問題1: Workflow が Pending のまま起動しない

原因: RBAC 権限不足

解決策:

kubectl apply -f k8s/argo-role-patch.yaml

問題2: GCS へのアクセスが拒否される

原因: Workload Identity の設定ミス

確認:

# Pod 内から確認
kubectl exec -it <pod-name> -n argo -- gcloud auth list

# 正しい Service Account が表示されるか確認

問題3: Spot Instance が頻繁に中断される

原因: リージョンのキャパシティ不足

解決策:

  • retryStrategy のパラメータを調整
  • 複数のゾーンを使用(Zonal → Regional クラスタ)

まとめ

本記事では、GKE と Argo Workflows を活用した MLOps パイプラインの実装について、以下のポイントを解説しました:

  1. Terraform による Infrastructure as Code

    • Persistent/Ephemeral の分離設計
    • Remote State による状態共有
  2. Workload Identity による安全な認証

    • JSONキー不要のセキュアな設定
    • IAM Binding の正確な構成
  3. Argo Workflows によるパイプライン実行

    • DAG による依存関係の定義
    • Artifact Repository による データ受け渡し
  4. Spot Instances によるコスト最適化

    • 最大97%のコスト削減
    • リトライ戦略による信頼性確保
  5. RBAC による適切な権限管理

    • 最小権限の原則
    • Role/RoleBinding の設定

この設計により、セキュアで、コスト効率の高い、再現性のある ML パイプラインを実現できました。

参考リンク


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?