OpenShift を Zabbix で監視する

## はじめに

本記事は、下記の続きです。

* [install_yamls で作る OpenStack in OpenShift #openshift - Qiita](https://qiita.com/xotaki/items/ca3a4a3cfafa45d84d17)

Zabbix6.0 から [Kubernetes監視機能が追加](https://www.zabbix.com/jp/whats_new_6_0#kubernetes_monitoring)されましたので、前回構築した OpenShift の監視設定をしてみました。

なお、筆者は OpenShift の理解が浅いため、記述に誤りがある可能性があります。

## 検証環境構成

* **OpenShift**
    * Code Ready Containers で構築したシングルノードの OpenShift (4.14.12)
* **Zabbix Server**
    * 7.0β を試したのですが、 [kubernetes-helm](https://cdn.zabbix.com/zabbix/integrations/kubernetes-helm/) の最新が 6.4 のため、Zabbix Proxy との連携ができず、6.4.13 をインストールしました
    * OpenShiftをインストールしているKVMホストの外部にインストールします
* **kubernetes-helm**
    * [kubernetes-helm](https://cdn.zabbix.com/zabbix/integrations/kubernetes-helm/) を使用し、以下の Pod を OpenShift 上にインストールします
        * **kube-state-metrics**: 容易に扱い可能なクラスタのstate情報を提供してくれるサービスです。Kubernetesオブジェクト全体(nodeやpod,Deploymentなど)のメトリクスをControl Plane APIから取得します
        * **zabbix-agent2**: DaemonSets を利用して各Nodeで起動します（今回の環境はシングルノードなので、1 Podのみ起動されます）
        * **zabbix-proxy**: 「Kubernetes nodes by HTTP」というテンプレートを使ってOpenShift Nodeが監視ホストに設定されるのですが、監視ホストのIPアドレスがインターナルなセグメント（internalEtcd というI/F名でした）のため、Zabbix Proxy経由で監視します
    * デプロイ先の Namespace は zabbix-monitoring とします

## 構築手順

### 1. CRCのリモート接続設定

CRCはKVMホストにインストールされ、KVMホストから接続することが前提になっています。
Zabbix Server をKVMホストの外部に建てたかったため、KVMホストに haproxy を建ててリモート接続できるようにします。
手順は下記の記事を参考にしました。

* [CodeReady Containers（CRC）をリモート接続してシングルノードのOpenShiftとして利用する - 赤帽エンジニアブログ](https://rheb.hatenablog.com/entry/crc_remote_connecting)

なお、haproxy.cfg の global の次の行に debug があるのですが、HAproxyの2.3-dev6でglobalセクションのdebugオプションが削除されたとのことです。（このブログの著者のかたに教えていただきました）
今回は、以下のように `daemon` を定義しましたが、 `log` でも動作します。

```haconfig.cfg
global
    daemon
```

### 2. Zabbix Server のインストール

下記の記事を参考にインストールします。Zabbixリポジトリを読み替えるだけでインストールできました。

* [Zabbix6.0をRockyLinux9にTimescaleDBとNginxで構築](https://zenn.dev/malwarekiddy/articles/29992b2cc3d9e9)

### 3. kubernetes-helm のインストール

OpenShiftクラスタに [kubernetes-helm](https://cdn.zabbix.com/zabbix/integrations/kubernetes-helm/) をインストールします。

#### 3.1. Helm のインストール

Helm とは、Kubernets 設定ファイルを１つの再利用可能なパッケージにまとめて、 Kubernets アプリケーションの作成、パッケージ化、構成、デプロイを自動化するツールです。

下記、公式サイトにあるインストール手順に従ってインストールします。
* [Helm | Helm のインストール](https://helm.sh/ja/docs/intro/install/)

```bash
curl -fsSL -o get_helm.sh  https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
```

#### 3.2. kubernetes-helm のインストール

Helmチャートリポジトリを追加します。

```bash
helm repo add zabbix-chart-6.4  https://cdn.zabbix.com/zabbix/integrations/kubernetes-helm/6.4
```

Helmチャートのデフォルト設定値を zabbix_values.yaml にエクスポートします。

```bash
helm show values zabbix-chart-6.4/zabbix-helm-chrt > zabbix_values.yaml
```

zabbix_values.yaml を以下のように編集します。

```bash
cp -p zabbix_values.yaml{,.default}
vi zabbix_values.yaml
diff -u zabbix_values.yaml{.default,}
```
```diff
--- zabbix_values.yaml.default  2024-04-13 23:35:11.935936835 +0900
+++ zabbix_values.yaml  2024-04-13 18:26:51.695877885 +0900
@@ -5,6 +5,11 @@
 ## kube-state-metrics.enabled -- If true, deploys the kube-state-metrics deployment
 kube-state-metrics:
   enabled: true
+  securityContext:
+    fsGroup: 1000780000
+    runAsGroup: 1000780000
+    runAsNonRoot: true
+    runAsUser: 1000780000
 
 ## Service accoun for Kubernetes API
 rbac:
@@ -56,7 +61,7 @@
 
     ## Note that since version 6.0 the variable ZBX_SERVER_PORT is not supported anymore. Instead, add a colon (:) followed by the port number to the end of ZBX_SERVER_HOST value.
     - name: ZBX_SERVER_HOST
-      value: "127.0.0.1"
+      value: "192.168.11.80"
     ## The variable is used to specify debug level. By default, value is 3
     - name: ZBX_DEBUGLEVEL
       value: 3
@@ -167,11 +172,11 @@
       ##   cpu: 100m
       ##   memory: 54Mi
 
-  securityContext: {}
-    # fsGroup: 65534
-    # runAsGroup: 65534
-    # runAsNonRoot: true
-    # runAsUser: 65534
+  securityContext:
+    fsGroup: 1995
+    runAsGroup: 1995
+    runAsNonRoot: true
+    runAsUser: 1997
 
   containerSecurityContext: {}
     ## capabilities:
```

* kube-state-metrics に securityContext の定義を追加する
* zabbixProxy の env の `ZBX_SERVER_HOST` を Zabbix Server のIPアドレスに書き換えます
* zabbixAgent の securityContext の定義を追加する。1995 は zabbixグループ、1997 は zabbixユーザーです。Pod起動時に zabbix_agent2.conf を書き換えるようになっているのですが、オーナーが zabbix:root のため、これに合わせて securityContext を定義する必要があります

#### 3.3. Zabbix用Namespaceの作成

zabbix-monitoring という Namespace を作成します。

```bash
oc new-project zabbix-monitoring
```

#### 3.4. Helm Chart のインストール

以下のように Helm Chart をインストールしてみます。

```bash
helm install zabbix zabbix-chart-6.4/zabbix-helm-chrt --dependency-update -f zabbix_values.yaml -n zabbix-monitoring
```

#### 3.5. SCCの作成と適用

Security Context Constraints(SCC)は、OpenShiftでPodが実行できるアクション及びアクセスを制御するリソースです。Podを作成するユーザーやリンクされているServiceAccountで許可されたSCCの制限を利用し、Podを起動させます。

カスタムSCCを作成せずに `helm install` を実行すると、以下のようなエラーになり、zabbix-kube-state-metrics が起動できませんでした。

```
Error creating: pods "zabbix-kube-state-metrics-64db48ddff-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, provider restricted-v2: .spec.securityContext.fsGroup: Invalid value: []int64{65534}: 65534 is not an allowed group, provider restricted-v2: .containers[0].runAsUser: Invalid value: 65534: must be in the ranges: [1000760000, 1000769999], provider "restricted": Forbidden: not usable by user or serviceaccount, provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "hostpath-provisioner": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]
```

これは、以下のような理由によってPodの作成が阻止されています:

1. **fsGroup**: Podの securityContext.fsGroup が 65534 に設定されていますが、OpenShiftのrestricted-v2 セキュリティコンテキストコンストレイント(SCC)では許可されていません
2. **runAsUser**: コンテナの runAsUser が 65534 に設定されていますが、restricted-v2 SCCでは許可されている範囲 [1000760000, 1000769999] から外れています

この問題を解決するために、以下ように対処します:

1. **Helm Chartの設定の見直し**: `securityContext` の設定を変更し、OpenShiftのSCCに適合するように修正する（上記で修正済みです）
2. **カスタムSCCの作成**: Podが必要とする権限を許可するカスタムSCCを定義する

同様に、 zabbix-agent-service-account も起動できていませんでした。

```
Error creating: pods "zabbix-agent-" is forbidden: unable to validate against any security context constraint: [provider "anyuid": Forbidden: not usable by user or serviceaccount, provider restricted-v2: .spec.securityContext.hostNetwork: Invalid value: true: Host network is not allowed to be used, provider restricted-v2: .spec.securityContext.hostPID: Invalid value: true: Host PID is not allowed to be used, spec.volumes[0]: Invalid value: "hostPath": hostPath volumes are not allowed to be used, spec.volumes[1]: Invalid value: "hostPath": hostPath volumes are not allowed to be used, spec.volumes[2]: Invalid value: "hostPath": hostPath volumes are not allowed to be used, provider restricted-v2: .containers[0].hostNetwork: Invalid value: true: Host network is not allowed to be used, provider restricted-v2: .containers[0].containers[0].hostPort: Invalid value: 10050: Host ports are not allowed to be used, provider restricted-v2: .containers[0].hostPID: Invalid value: true: Host PID is not allowed to be used, provider "restricted": Forbidden: not usable by user or serviceaccount, provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "hostpath-provisioner": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount]
```

これは、以下のような理由によってPodの作成が阻止されています:

1. **hostNetwork**: Podの securityContext.hostNetwork が true に設定されていますが、OpenShiftの restricted-v2 セキュリティコンテキストコンストレイント (SCC) では許可されていません
2. **hostPID**: Podの securityContext.hostPID が true に設定されていますが、restricted-v2 SCC では許可されていません
3. **hostPath volumes**: Podが hostPath のボリュームを使用しようとしていますが、restricted-v2 SCC ではこれらのボリュームが許可されていません
4. **hostNetwork and hostPort**: コンテナの hostNetwork が true に設定されており、hostPort が 10050 に設定されていますが、restricted-v2 SCC ではこれらの設定は許可されていません


カスタムSCCを restricted-v2 から作成することにします。

```bash
oc get scc restricted-v2 -o yaml > zabbix-scc.yaml
vi zabbix-scc.yaml
```
```yaml
allowHostDirVolumePlugin: true
allowHostIPC: true
allowHostNetwork: true
allowHostPID: true
allowHostPorts: true
allowPrivilegeEscalation: true
allowPrivilegedContainer: true
allowedCapabilities: null
apiVersion: security.openshift.io/v1
defaultAddCapabilities: null
fsGroup:
  type: RunAsAny
groups:
- system:authenticated
kind: SecurityContextConstraints
metadata:
(省略)
  name: zabbix-scc
(省略)
priority: null
readOnlyRootFilesystem: false
requiredDropCapabilities:
- KILL
- MKNOD
- SETUID
- SETGID
runAsUser:
  type: MustRunAsRange
  uidRangeMax: 1000789999
  uidRangeMin: 1000780000
seLinuxContext:
  type: RunAsAny
supplementalGroups:
  type: RunAsAny
users: []
volumes:
- configMap
- downwardAPI
- emptyDir
- hostPath
- persistentVolumeClaim
- projected
- secret
```

カスタムSCCを作成し、 zabbix-agent-service-account, zabbix-kube-state-metrics に適用します。

```bash
oc create -f zabbix-scc.yaml
oc adm policy add-scc-to-user zabbix-scc -z zabbix-agent-service-account
oc adm policy add-scc-to-user zabbix-scc -z zabbix-kube-state-metrics
```

helm で再インストールします。

```bash
helm uninstall zabbix -n zabbix-monitoring
helm install zabbix zabbix-chart-6.4/zabbix-helm-chrt --dependency-update -f zabbix_values.yaml -n zabbix-monitoring
```

うまくすると、以下のように Pod が起動されます。

```bash
$ oc get pod -n zabbix-monitoring
NAME                                         READY   STATUS    RESTARTS   AGE
zabbix-agent-pg749                           1/1     Running   0          2m13s
zabbix-kube-state-metrics-58fd6b6f54-ppm6w   1/1     Running   0          2m13s
zabbix-proxy-5d58759c69-8c9n4                1/1     Running   0          2m13s
```


### 4. Zabbix Server側の設定

#### 4.1. Zabbix Proxyの設定

管理 > プロキシ からプロキシを作成します。

| 項目 | 内容 |
|-----|------|
| プロキシ名 | zabbix-proxy |
| プロキシモード | アクティブ |
| プロキシのアドレス | 空白 |

#### 4.2. 監視ホストの作成とKubernetes nodes by HTTP テンプレートの適用

データ収集 > ホスト からホストを作成します。

| 大項目 | 小項目 | 内容 |
|-------|-------|------|
| ホスト | ホスト名 | ocp |
|| テンプレート | Kubernetes nodes by HTTP |
|| ホストグループ | OpenShift |
|| インターフェース | 127.0.0.1:10050 |
|| プロキシによる監視 | zabbix-proxy |
| マクロ | {$KUBE.API.TOKEN} | 下記参照 |
|| {$KUBE.API.URL} | https://api.crc.testing:6443 |

* `{$KUBE.API.TOKEN}` の値は、下記の実行結果を設定します。
    ```bash
    oc get secret zabbix-service-account -n monitoring -o jsonpath={.data.token} | base64 -d
    ```
* {$KUBE.API.URL} の値は、下記の実行結果で出力されるURLを設定します。
    ```bash
    oc cluster-info
    ```

#### 4.3. ディスカバリー後の確認

監視設定後しばらくすると、以下のようにディスカバリーされました。

![zabbix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56678/d450ef59-2ca7-5dfe-7ba3-a0b771e40d80.png)


## 参考

* [Zabbix 6.0 Kubernetes監視機能を使用してRed Hat OpenShiftを監視してみる #openshift - Qiita](https://qiita.com/mi_h/items/abacd562d81280a2d3ca)
* [Helm とは？: 概要、Kubernetes プロジェクト管理、ユースケース｜CircleCI](https://circleci.com/ja/blog/what-is-helm/)
* [Security Context Constrains(SCC)の適用プロセス on OpenShift - 赤帽エンジニアブログ](https://rheb.hatenablog.com/entry/scc-process-on-openshift-202008)