GitHub Actionsとkubeconformでkubernetesのmanifestの文法を自動チェック

この記事は [Kubernetes Advent Calendar 2023](https://qiita.com/advent-calendar/2023/kubernetes) の 2 日目の記事です。

## 概要
kubeconformはKubernetesのmanifest fileのvalidationツールです。
CIに組み込んだり、ローカルで使用してKubernetesの設定を検証することができます。

- install方法
https://github.com/yannh/kubeconform#installation
以下からダウンロードも可能
https://github.com/yannh/kubeconform/releases


## 使用方法


例として以下のmanifestを使用します。

```
$ cat nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: 100M
            cpu: 100m
            ephemeral-storage: 100M
          limits:
            memory: 200M
            cpu: 200m
            ephemeral-storage: 100M
```

上記manifestに対してkubeconform実行
- 正常パターン

```
$ ~/kubeconform -summary -output json nginx-deployment.yaml
{
  "resources": [],
  "summary": {
    "valid": 1,
    "invalid": 0,
    "errors": 0,
    "skipped": 0
  }
}

```

- エラーパターン
port番号を文字列で定義すると、誤りを検出してくれます。
```
$ ~/kubeconform -summary -output json nginx-deployment.yaml
{
  "resources": [
    {
      "filename": "nginx-deployment.yaml",
      "kind": "Deployment",
      "name": "nginx-deployment",
      "version": "apps/v1",
      "status": "statusInvalid",
      "msg": "problem validating schema. Check JSON formatting: jsonschema: '/spec/template/spec/containers/0/ports/0/containerPort' does not validate with https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone/deployment-apps-v1.json#/properties/spec/properties/template/properties/spec/properties/containers/items/properties/ports/items/properties/containerPort/type: expected integer, but got string",
      "validationErrors": [
        {
          "path": "/spec/template/spec/containers/0/ports/0/containerPort",
          "msg": "expected integer, but got string"
        }
      ]
    }
  ],
  "summary": {
    "valid": 0,
    "invalid": 1,
    "errors": 0,
    "skipped": 0
  }
}
```



## kustomizeを使用している場合
- フォルダ構成
```
.
├── base
│   ├── kustomization.yaml
│   └── nginx.yaml
└── overlay
    ├── dev
    │   ├── kustomization.yaml
    │   └── patch.yaml
    :
    :
```

- 実行コマンド
```
$ kustomize build overlay/dev/ | kubeconform -summary -output json
{
  "resources": [],
  "summary": {
    "valid": 1,
    "invalid": 0,
    "errors": 0,
    "skipped": 0
  }
}

```

## カスタムリソースを扱う場合

- schemaについて

カスタムリソースを扱う場合は、schemaファイルを用意する必要があります。
以下記載の通り、`-schema-location`というオプションがあり、指定しない場合はdefaultのschemaが使用されます。defaultの場合、カスタムリソースに対応していません。
https://github.com/yannh/kubeconform/tree/master#usage

例えば、カスタムリソースである`targetgroupbinding`を例にdefaultのschemaでkubeconformを実行すると以下のようにエラーになります。

```
$ ~/kubeconform -summary -output json targegroup.yaml
{
  "resources": [
    {
      "filename": "targegroup.yaml",
      "kind": "TargetGroupBinding",
      "name": "nginx-tg",
      "version": "elbv2.k8s.aws/v1beta1",
      "status": "statusError",
      "msg": "could not find schema for TargetGroupBinding"
    }
  ],
  "summary": {
    "valid": 0,
    "invalid": 0,
    "errors": 1,
    "skipped": 0
  }
}
```

- schemaファイルの作成方法

以下のscriptを実行することでschemaファイルを作成することができます。

https://github.com/yannh/kubeconform/blob/master/scripts/openapi2jsonschema.py


- 作成手順

まず対象のリソースのcrdの情報を取得します
```
$ kubectl get crd targetgroupbindings.elbv2.k8s.aws -o yaml > targetgroup_crd.yaml
```
openapi2jsonschema.pyを実行するためにリポジトリをclone
```
$ git clone  https://github.com/yannh/kubeconform.git
```
schemaファイルのフォーマットを定義
(ソースを見るとkind,group,fullgroup,versionが指定できそう)
```
$ export FILENAME_FORMAT={kind}_{version}
```
取得したcrd情報を指定してopenapi2jsonschema.pyを実行
```
$ python kubeconform/scripts/openapi2jsonschema.py targetgroup_crd.yaml
JSON schema written to targetgroupbinding_v1alpha1.json
JSON schema written to targetgroupbinding_v1beta1.json
```

- kubeconform実行

取得したjsonファイルは適当にschemaフォルダに格納し、以下のように`-schema-location`でschemaファイルを指定して実行すればカスタムリソースも扱うことができるようになります。

```
$ ~/kubeconform -summary -output json -schema-location "schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json" targegroup.yaml
{
  "resources": [],
  "summary": {
    "valid": 1,
    "invalid": 0,
    "errors": 0,
    "skipped": 0
  }
}
```
また、`-schema-location`は複数指定できるので、defaultのschemaも使用したい場合は以下のように記載することができます。
```
$ kubeconform -summary -output json -schema-location default -schema-location "schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json" xxx.yaml
```

:::note info
以下のように指定することで、schemaファイルが複数あってもファイル名が指定したフォーマットに準拠していればすべて適用してくれます。
`"schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json"`
:::








## GitHub Actionsのworkflowから実行
以下、workflowの例が記載されています。
https://github.com/yannh/kubeconform#github-workflow

しかし、個人的には以下を使用することをお勧めします。
理由は、runでコマンド実行できるので自由度がかなり高いです。
https://github.com/yokawasa/action-setup-kube-tools


- workflow例

以下の構成で管理されているmanifestに対してkubeconformを実行します

```
.
├── schemas
│   ├── rollout_v1alpha1.json
:   :            :
│   └── targetgroupbinding_v1beta1.json
└── template
    └── kustomize
        ├── base
        │   ├── kustomization.yaml
        │   └── nginx.yaml
        └── overlay
           ├── dev
           │   ├── kustomization.yaml
           │   └── patch.yaml
           ├── prod
           │   ├── kustomization.yaml
           │   └── patch.yaml
           └── stg
               ├── kustomization.yaml
               └── patch.yaml

```

`action-setup-kube-tools`を使用し、以下のようにワークフローを作成することで、
PR作成時に複数のmanifestを一度にチェックできます。

```
name: Check manifest

on:
  pull_request:  

jobs:
  kubeconform:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: yokawasa/action-setup-kube-tools@v0.9.2
      with:
        setup-tools: |
          kubeconform
          kustomize
        kubeconform: '0.5.0'
        kustomize: '5.0.0'
    - run: |
        for ENV_NAME in dev stg prod
        do
          kustomize build template/kustomize/overlay/$ENV_NAME |
          kubeconform -summary \
          -schema-location default \
          -schema-location "schemas/{{ .ResourceKind }}_{{ .ResourceAPIVersion }}.json" \
          -output json
        done
```

`action-setup-kube-tools`を使用すれば実行環境にkustomizeやkubeconformをinstallしておく必要はなく、実行環境への依存を排除することができるメリットもあります。

- 実行結果
GitHubでPullRequestを作成すると以下のようにworkflowが動作します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/896507/dd74fa84-1bad-6c4a-2406-6b518a56e682.png)
*Warningでてますが、kustomizeでpatchesStrategicMergeを使用したことによるものなのでkubeconformのWarningではないです。

CIに組み込むことで、PullRequestの時点で確実に文法などの誤りを検出できるのはよいですね。