Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

KustomizeでCloud Run jobsのジョブ構成を管理する

本記事はSansan Advent Calendar 2024の24日目の記事です。

こんにちは。技術本部 Strategic Products Engineering Unit Contract One Devグループの髙野です。2024年4月に新卒で入社し、契約データベース「Contract One」の開発をしています。5人チームのリーダーとして、新規機能開発に向き合っています。

Cloud Run jobsのジョブ構成の管理に、KubernetesのManifest管理ツールであるKustomizeを導入したので紹介します。

バッチ処理実行基盤としてのCloud Run jobs

Contract Oneでは、バッチ処理の実行基盤としてCloud Run jobsを使用しています。基盤上にバッチ処理を実装していき、実行時にジョブ名を指定することで、目的のバッチ処理を動かします。コンテナイメージは1つですが、Google Cloud上では各ジョブが独立したリソースになっています。

ジョブ構成はCloud Run job YAMLで管理されており、gcloud run jobs replaceコマンドの引数にYAMLを指定することでジョブをデプロイできます。Contract Oneの実行基盤では、バッチ処理ごと・環境ごとに実行パラメータが違うCloud Run job YAMLファイルを定義しています。

Contract OneでのYAML管理の課題

事業のグロースとともにバッチ処理が増えていて、新しいバッチ処理を作る際には既存のCloud Run job YAMLファイルをコピペして作られることが多いです。しかし、どのパラメータがそのジョブ固有のものかすぐに判断できず、無駄なパラメータが指定されていることもあったため、Cloud Run job YAMLファイルの管理が課題となっていました。

そこで、KubernetesのManifest管理ツールであるKustomizeを導入し、課題解決に取り組みました。

Kustomizeの導入

次のようなディレクトリ構造で管理をしていきます。

.
├── base
└── overlays
    ├── production
    │   ├── base
    │   ├── service-a
    │   └── service-b
    └── staging
        ├── base
        ├── service-a
        └── service-b

baseディレクトリ

すべてのジョブの基本となるパラメータを定義していきます。

まずは、cloud-run-jobs.yaml で、次のようにCloud Run job YAMLの雛形を定義します。

apiVersion: run.googleapis.com/v1
kind: Job
metadata:
  name: batch-job
spec:
  template:
    spec:
      parallelism: 1 # ジョブ毎にpatchする
      taskCount: 1 # ジョブ毎にpatchする
      template:
        spec:
          serviceAccountName: dummy # 環境毎にpatchする
          maxRetries: 0 # ジョブ毎にpatchする
          timeoutSeconds: "86400" # ジョブ毎にpatchする
          containers:
            - image: dummy # 環境毎にpatchする
              resources:
                limits:
                  cpu: "2" # ジョブ毎にpatchする
                  memory: 2Gi # ジョブ毎にpatchする
              env: [ ] # 環境、ジョブ毎にpatchする

次に、shared-env.yamlで、全環境で共通の環境変数を定義します。

# key value のペアで環境変数を設定する
- op: add
  path: /spec/template/spec/template/spec/containers/0/env/-
  value:
    name: SERVICE_NAME
    value: batch-job
# Secret Managerからも値を取ってこれる
- op: add
  path: /spec/template/spec/template/spec/containers/0/env/-
  value:
    name: SERVICE_KEY
    valueFrom:
      secretKeyRef:
        key: latest
        name: service-key

最後に、kustomization.yamlで、これら2つのファイルを参照するベースファイルを定義します。

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - cloud-run-jobs.yaml # 雛形
patches:
  - path: shared-env.yaml # 環境変数のパッチ
    target:
      kind: Job
      name: batch-job

overlays/{環境名}/baseディレクトリ

環境毎に固有の値をoverlays/{環境名}/baseの中で定義します。staging環境であればoverlays/staging/baseを、production環境であればoverlays/production/baseを作成します。

まず、patch.yamlでサービスアカウントとコンテナイメージ名を置き換えます。

- op: replace
  path: /spec/template/spec/template/spec/containers/0/image
  value: us-docker.pkg.dev/cloudrun/container/job:latest

- op: replace
  path: /spec/template/spec/template/spec/serviceAccountName
  value: サービスアカウント名

次に、kustomization.yamlで、nameにsuffixをつけたり、上で行った置き換えを適用させるベースファイルを定義します。

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
nameSuffix: -prod
resources:
  - ../../../base
patches:
  - path: patch.yaml
    target:
      kind: Job
      name: batch-job

これで環境毎のベースファイルの定義は完了です。環境固有の環境変数がある場合は、shared-env.yamlと同様にファイルを作成し、patchesに追加します。

overlays/{環境名}ディレクトリ

overlays/{環境名}ディレクトリの下に、各バッチ処理のパラメータを定義していきます。

今回は、環境変数とタスク数/並列数を変更したいservice-aと、環境変数のみを変更したいservice-bがあると仮定します。

このとき、service-aの定義は次のようになります。

# overlays/production/service-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: service-a-
resources:
  - ../base
patches:
  - path: patch.yaml
    target:
      kind: Job
      name: batch-job
  - path: env.yaml
    target:
      kind: Job
      name: batch-job

# overlays/production/service-a/env.yaml
- op: add
  path: /spec/template/spec/template/spec/containers/0/env/-
  value:
    name: JOB_NAME
    value: JOB_A

# overlays/production/service-a/patch.yaml
- op: replace
  path: /spec/template/spec/parallelism
  value: 2

- op: replace
  path: /spec/template/spec/taskCount
  value: 2

service-bの定義は次のようになります。

# overlays/production/service-b/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: service-b-
resources:
  - ../base
patches:
  - path: env.yaml
    target:
      kind: Job
      name: batch-job

# overlays/production/service-b/env.yaml
- op: add
  path: /spec/template/spec/template/spec/containers/0/env/-
  value:
    name: JOB_NAME
    value: JOB_B

最終的なファイル構造

上記の手順を環境毎に行うと、次のようなファイル構造になります。

.
├── base
│   ├── cloud-run-jobs.yaml
│   ├── kustomization.yaml
│   └── shared-env.yaml
└── overlays
    ├── production
    │   ├── base
    │   │   ├── kustomization.yaml
    │   │   ├── patch.yaml
    │   │   └── shared-env.yaml
    │   ├── service-a
    │   │   ├── env.yaml
    │   │   ├── kustomization.yaml
    │   │   └── patch.yaml
    │   └── service-b
    │       ├── env.yaml
    │       └── kustomization.yaml
    └── staging
        ├── base
        │   ├── kustomization.yaml
        │   ├── patch.yaml
        │   └── shared-env.yaml
        ├── service-a
        │   ├── env.yaml
        │   ├── kustomization.yaml
        │   └── patch.yaml
        └── service-b
            ├── env.yaml
            └── kustomization.yaml

レンダリング

kubectl kustomize ./overlays/{環境名}/{ジョブ名} コマンドで、最終的なCloud Run job YAMLファイルをレンダリングします。

試しに、production環境のservice-aをレンダリングすると、次のようなYAMLが生成されます。parallelismやenvなどの値が置き換えられていることがわかります。

apiVersion: run.googleapis.com/v1
kind: Job
metadata:
  name: service-a-batch-job-prod
spec:
  template:
    spec:
      parallelism: 2
      taskCount: 2
      template:
        spec:
          containers:
          - env:
            - name: SERVICE_NAME
              value: batch-job
            - name: SERVICE_KEY
              valueFrom:
                secretKeyRef:
                  key: latest
                  name: service-key
            - name: JOB_NAME
              value: JOB_A
            image: us-docker.pkg.dev/cloudrun/container/job:latest
            resources:
              limits:
                cpu: "2"
                memory: 2Gi
          maxRetries: 0
          serviceAccountName: サービスアカウント名
          timeoutSeconds: "86400"

Contract Oneでは、レンダリングをCDパイプラインに組み込み、デプロイを自動化しています。

まとめ

Kustomizeを使用して、Cloud Run jobsのジョブ構成を管理する方法を紹介しました。すべての環境に共通なbase overlayを元に、環境ごとに差異がある部分のみのoverlayを作成することで、複数ジョブを複数環境でわかりやすく管理できるようになりました。

実は、Kustomizeの導入検討はオフィスのふらっとした立ち話から始まり、開発チームのフットワークの軽さを感じました。そんなContract Oneでは、プロダクトの価値向上に向き合っていく仲間を募集しています! 詳しくは採用情報をご確認ください。

media.sansan-engineering.com

© Sansan, Inc.