クラスタ環境デプロイ - EKSクラスタ(Kustomize導入)

| 11 min read
Author: noboru-kudo

本記事は、クラスタ環境デプロイ - EKSクラスタ(AWS環境準備)からの続きです。

ここでは、Kubernetesのマニフェストファイルを、Kustomizeに対応した構成へと見直していきます。

Kustomizeはbaseと呼ばれる環境共通部分にパッチを当てることで、各環境の完全なマニフェストを生成します。
以下のようなフローで最終的にデプロイする形になります。

Kustomizeのパッチは、以下の方法をサポートしています。

  1. Strategic merge patch
  2. JSON Patch

どちらを利用するかは好みの問題ですが、基本路線としては宣言的に記述するStrategic merge patch、複雑なパッチの場合は、JSON Patchを使用した方が簡潔になると思います。

以前(Kubernetesマニフェスト作成)は、app/k8s/v2ディレクトリ配下にKubernetesマニフェストを作成しました[1]
今回は、app/k8s/v3ディレクトリ配下に作成していきましょう。

事前に以下の構成を作成しておいてください。

app/k8s/v3
├── base
└── overlays
    ├── local
    │   └── patches
    └── prod
        └── patches

Contents

base#

それでは、base配下にローカル環境、商用環境のbaseリソースを配置していきましょう。 基本的な作業はapp/k8s/v2をコピーして、環境固有部分を消していけば作成できます。

task-service#

まずはタスク管理API(task-service)です。
task-serviceディレクトリを作成し、DeploymentとServiceの定義を移植します。

まずはDeploymentです。

apiVersion: apps/v1
kind: Deployment
metadata:
name: task-service
spec:
selector:
matchLabels:
app: task-service
template:
metadata:
labels:
app: task-service
spec:
containers:
- name: task-service
image: task-service
ports:
- name: http
containerPort: 3000
envFrom:
- configMapRef:
name: task-service-config
readinessProbe:
httpGet:
port: 3000
path: /health/readiness
livenessProbe:
httpGet:
port: 3000
path: /health/liveness
startupProbe:
httpGet:
port: 3000
path: /health/startup

app/k8s/v2/task-service/deployment.yamlとほぼ同じですが、コンテナのimagePullPolicyreplicasを削除しました。

以前imagePullPolicyNeverとしていましが、これはローカル環境でのみ有効な設定です。
クラスタ環境ではコンテナレジストリより取得する必要があり、この指定では動作しません。
したがって、この項目は環境固有値としてoverlaysで設定する必要があると判断できますので、ここでは削除しました。

同様に起動するレプリカ数も、ローカル環境とクラウド環境では要件が異なりますので削除しました。

続いてServiceですが、こちらは環境別の差分はありません。
app/k8s/v2/task-service/service.yamlをそのままapp/k8s/v3/task-serviceにコピーしてください。

最後にConfigMapです。以前はConfigMapのYAML形式で記述しましたが、KustomizeにはConfigMapを生成する機能(configMapGenerator)がありますので、こちらを利用します(後述)。
ここではシンプルにキーバリュー形式で設定ファイルを用意します。app/k8s/v2/task-service/.envを作成し、以下を記述します。

TZ=Asia/Tokyo

今回はbaseの定義として、Node.jsのタイムゾーンを指定するTZのみを定義しました。
基本的にConfigMapは環境固有のものが多いため、baseで記述できるものは限られてくるはずです。

task-reporter#

続いてタスクレポート出力バッチです。
app/k8s/v3/task-reporterディレクトリを作成し、app/k8s/v2/task-reporter/cronjob.yamlを移植します。

apiVersion: batch/v1
kind: CronJob
metadata:
name: task-reporter
labels:
app: task-reporter
spec:
schedule: "0 15 * * *"
failedJobsHistoryLimit: 3
successfulJobsHistoryLimit: 3
concurrencyPolicy: Allow
suspend: false
jobTemplate:
metadata:
labels:
app: task-reporter
spec:
completions: 1
parallelism: 1
activeDeadlineSeconds: 3600
backoffLimit: 10
template:
metadata:
labels:
app: task-reporter
spec:
restartPolicy: Never
containers:
- name: task-reporter
image: task-reporter
envFrom:
- configMapRef:
name: task-reporter-config
env:
- name: TEMP_DIR
value: /var/app/temp
volumeMounts:
- mountPath: /var/app/temp
name: app-temp-dir
volumes:
- name: app-temp-dir
emptyDir:
sizeLimit: 10Gi

こちらもタスク管理APIと同様の理由でimagePullPolicyは削除しました。

Ingress#

app/k8s/v3/ingress/ingress.yamlを作成し、以下を記述します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: task-service
port:
number: 3000
path: /api
pathType: Prefix

ホスト名(host)は、通常環境によって変える必要があるため削除しました。
今回はどの環境でも、Ingress ControllerにNGINXを使うため、ingressClassNameはbase側に記述しました。環境(ローカル:Nginx, 商用:ALB等)によって変わるようであれば、こちらもoverlays側に記述するのが良いでしょう。

Kustomizationファイル#

これまで定義してきたマニフェストファイルを、ここでKustomizationファイルとして1つにまとめます。
Kustomizeを使う場合は、このファイルが必須になります。

詳細な内容は公式ドキュメントを参照してください。

baseディレクトリ直下にkustomization.yamlを作成して、以下を記述します。

commonLabels:
app.kubernetes.io/name: task-tool
app.kubernetes.io/created-by: mamezou-tech

resources:
- ingress/ingress.yaml
- task-service/service.yaml
- task-service/deployment.yaml
- task-reporter/cronjob.yaml

configMapGenerator:
- name: task-service-config
envs:
- task-service/.env
- name: task-reporter-config
envs:
- task-reporter/.env

commonLabelsフィールドに全てのリソース共通のラベル(metadata.labels)を定義しています。
ここに掲載しているもの以外でも、本家Kubernetesの公式ドキュメントに推奨ラベルが掲載されていますので、参考にするとよいでしょう。
他にも共通アノテーションを定義するcommonAnnotationsフィールドもあります。

resourcesフィールドでは、先程作成したリソースのマニフェストファイルを指定します。ここに指定しなかったものは、マニフェストファイルがあっても取り込まれないので注意してください。

configMapGeneratorフィールドで、.envファイルからConfigMapを生成する指定をしています。
これ以外にも、固定値(literals)やファイル自身(files)からConfigMapを作成可能です。
詳細は公式ドキュメントを参照してください。

ここまで終わると、app/k8s/v3/base配下は以下の構成になります。

base
├── kustomization.yaml
├── ingress
│   └── ingress.yaml
├── task-reporter
│   ├── .env
│   └── cronjob.yaml
└── task-service
    ├── .env
    ├── deployment.yaml
    └── service.yaml

ローカル環境向けのパッチ作成(overlays/local)#

base部分の作成が終わりましたので、ローカル環境(overlays/local)のパッチを作成しましょう。

task-service#

まずは、タスク管理サービス(task-service)のDeploymentに対するパッチを作成します。
app/k8s/v3/overlays/local/patches配下にtask-serviceディレクトリを作成します。
ここに、deployment.patch.yamlを配置し、以下を記述します。

apiVersion: apps/v1
kind: Deployment
metadata:
name: task-service
spec:
replicas: 2
template:
spec:
containers:
- name: task-service
imagePullPolicy: Never

ここはbaseからの差分を記述します。今回はローカル環境としてレプリカ数2、イメージはローカルビルドしたものを使用(imagePullPolicy: Never)するように設定しました。
これだけでは不完全なマニフェストですが、Kustomizeでbaseとマージされることによって完全なものになります。

続いて設定ファイルです。 ここでも.envファイルを作成し、以下を記述します。

STAGE=localstack
NODE_ENV=development
TASK_TABLE_NAME=tasks
AWS_ENDPOINT=http://localstack:4566
AWS_DEFAULT_REGION=local
AWS_ACCESS_KEY_ID=localstack
AWS_SECRET_ACCESS_KEY=localstack

先程はTZのみを記述しましたが、それ以外の環境固有の設定はここに記述します。

task-reporter#

続いて、タスクレポート出力バッチ(task-reporter)のCronJobに対するパッチを作成します。
app/k8s/v3/overlays/local/patches配下にtask-reporterディレクトリを作成します。
ここに、cronjob.patch.yamlを配置し、以下を記述します。

apiVersion: batch/v1
kind: CronJob
metadata:
name: task-reporter
spec:
jobTemplate:
spec:
template:
spec:
containers:
- name: task-reporter
imagePullPolicy: Never

ここでは、先程同様にimagePullPolicyNeverとし、ローカルビルドしたイメージを使うように指定します。

設定ファイルの方も同様です。.envファイルを作成し、以下を記述します。

STAGE=localstack
NODE_ENV=development
TASK_TABLE_NAME=tasks
REPORT_BUCKET=completed-task-report-bucket
TARGET_OFFSET_DAYS=0
AWS_ENDPOINT=http://localstack:4566
AWS_DEFAULT_REGION=local
AWS_ACCESS_KEY_ID=localstack
AWS_SECRET_ACCESS_KEY=localstack

baseに定義したTZ以外のものをここに定義します。

Ingress#

app/k8s/v3/overlays/local/patches配下にingressディレクトリを作成します。
ここに、ingress.patch.yamlを配置し、以下を記述します。

- op: replace
path: /spec/rules/0/host
value: task.minikube.local

今までは、KubernetesのStrategic merge patchでパッチファイルを作成しましたが、今回はJSON Patchを使用しました。
上記ではIngressのホスト(host)に、minikubeのドメインtask.minikube.localを設定(replace)するようにしています。

なお、ローカル環境のKubernetesにDocker Desktopを使用している場合は、このファイルの作成は不要です。

Kustomizationファイル#

最後に、overlays/local配下にkustomizton.yamlを作成します。
まずは、以下を記述します。

commonLabels:
env: local

namePrefix: local-

resources:
- ../../base

commonLabelsはローカル環境ではenvlocalと入れるようにしました。複数環境を1つのクラスタに配置する際は、こうしておくとラベルで絞り込むときに便利です。この設定はbaseの同フィールドとマージされます。

namePrefixにはlocal-としました。こうすると、Kustomizeで作成する全てのリソースの名前に対してこのプレフィックスが付与されます。
kubectl getコマンドでリソースを参照すると、名前ですぐにどの環境を見ているかが分かるようになります。誤操作による事故を防ぐために、この設定を入れることをお勧めします。

resourcesにローカル環境で対象とするリソースを指定します[2]。 ここでは、先程作成したbaseディレクトリのみを指定します。

続いて、パッチ部分を追記します。

patches:
- path: patches/task-service/deployment.patch.yaml
- path: patches/task-reporter/cronjob.patch.yaml
- path: patches/ingress/ingress.patch.yaml
target:
kind: Ingress
name: app-ingress

ここでは、先程作成したパッチファイルをそれぞれ適用しています。
Ingressのパッチファイルでは、どのリソースに対して適用するのかを指定していません。したがって、上記のようにtargetフィールドで対象を明示する必要があります。
targetフィールドは単一リソースだけでなく、複数リソースも指定可能です。詳細は公式ドキュメントを参照してください。

次はConfigMap(configMapGenerator)です。

configMapGenerator:
- name: task-service-config
behavior: merge
envs:
- patches/task-service/.env
- name: task-reporter-config
behavior: merge
envs:
- patches/task-reporter/.env

behavior: mergeとしている点に注目してください。こうすることでbaseに定義したConfigMapとマージするようにしています。

最後は利用するコンテナイメージの定義を追記します。

images:
- name: task-service
newName: task-service
newTag: latest
- name: task-reporter
newName: task-reporter
newTag: latest

ここでタスク管理サービス(task-service)、タスクレポート出力バッチ(task-reporter)のそれぞれで、上書き対象の名前(newName)とタグ(newTag)を指定します。
こうすることで、DeploymentやCronJobのイメージを変えなくても、Kustomizeがこの定義に従って上書きしてくれるようになります。
つまり、各環境別でどのバージョンのアプリケーションが動作しているかは、Gitで管理されているこのフィールドを見れば把握できるようになります。

imagesの詳細は公式ドキュメントを参照してください。

ここまで終わると、app/k8s/v3/overlays/local配下は以下の構成になります。

overlays/local
├── kustomization.yaml
└── patches
    ├── ingress
    │   └── ingress.patch.yaml
    ├── task-reporter
    │   ├── .env
    │   └── cronjob.patch.yaml
    └── task-service
        ├── .env
        └── deployment.patch.yaml

ローカル動作確認#

マニフェストの構成をKustomizeに移行しましたので、ローカル環境で引き続き動作するかを確認しましょう。
ローカル環境にデプロイするには、KustomizeでデプロイするようにSkaffoldの設定変更が必要です。
skaffold.yamlを以下のように修正しましょう。

skaffold.yaml#

apiVersion: skaffold/v2beta26
kind: Config
metadata:
name: app
build:
artifacts:
- image: task-service
context: apis/task-service
docker:
dockerfile: Dockerfile.local
- image: task-reporter
context: jobs/task-reporter
docker:
dockerfile: Dockerfile.local
deploy:
# kubectl -> kustomize
kustomize:
paths:
- k8s/v3/overlays/local

変更点はdeployステージ配下のみです。以前はkubectlとしていた部分をkustomizeとし、pathsにローカル環境のKustomizationファイルを配置したディレクトリを指定します。

デプロイの手順は変わりませんが、kubectlのコンテキストがEKS側の場合は、ローカル環境に切り替えてから実行してください。

# kubectlがEKSの方に向いている場合は以下を実行して切り替え
# minikube
kubectl config use-context minikube
# docker desktop
kubectl config use-context docker-desktop

# ローカルデプロイ
skaffold dev

動作確認の方法は以下を参照してください。以前と同じように操作できれば問題ありません。

以降はクラスタ環境デプロイ - EKSクラスタ(デプロイ)へと続きます。


  1. 未実施の場合はapp/k8s/v2-ansディレクトリを参照してください。 ↩︎

  2. Kustomizeの2.1.0まではbasesフィールドでbaseリソースを指定する必要がありましたが、現在はresourcesに統合されました。 ↩︎