そろそろZabbix7.0が正式リリースされるらしいけど、HA構成のときのアップグレードの注意事項はもう読んだ？

この記事は[NTTコムウェア Advent Calendar 2023](https://qiita.com/advent-calendar/2023/nttcomware) 7日目の記事です。

## 自己紹介

NTTコムウェア コーポレート革新本部の小西と申します(余談ですが、弊社の登記上の名称はエヌ・ティ・ティコムウェアなのですが、最近「分かりにくいからNTTをエヌ・ティ・ティって言うの、正式文書じゃない限りやめようぜ(意訳)」ってお達しがありました。NTTのほうがわかりやすいですよね)。

今のお仕事はC/UT工程辺りにアプローチする技術の社内標準化だったり技術調査だったりみたいなことをしています。
Zabbix関係ないやんけ～。まぁまぁそう固いこと言わんでくださいな。

コーポレート革新本部に来る前は公共・小売・流通...etc向けのお仕事をする事業本部で基盤・方式担当をしていました。Zabbix自体は大体、2.0からの付き合いです。
一応Zabbix認定スペシャリストです( ･´ｰ･｀)ﾄﾞﾔ。

お仕事でもZabbixはまだまだ使っていて、社内標準化のために色々なツール類を各事業本部のエンジニア向けに出させてもらっている中で、それらのツールに関わるサーバ類の監視なんかに利用しています。実質一人adminタノシーってやつです。

## この記事のオチ(結論)

Zabbix6.0ではZabbix標準の機能としてHAクラスタが採用されました。
HAクラスタを組んだ環境ではアップグレードの際、DBのオートコンバートの都合で少しだけ気をつけないと上手くいかないので注意が必要です。というかあまり無理矢理やると壊れます。

公式では[こちら](https://www.zabbix.com/documentation/7.0/en/manual/concepts/server/ha#upgrading-ha-cluster)で説明されていますが、お作法通りの手順は以下になります。

1. すべてのノードの停止
1. DBのフルバックアップ
1. DBのコンバートは1つのノードからだけにしたいので
    1. スタンドアロンモード(HANodeNameのコメントアウトによるHA構成の停止)
    1. アップグレード実施
1. アップグレードが終わったノードのスタンドアロンモードの解除
1. 他のノードはいつも通り「えいや」でアップグレード

...ということをオチにしつつ、Zabbixのアップグレード時の動作について見ていく記事です。

## Zabbix7.0はすぐそこに

当初計画では2023/7-9のリリースが予定されていた7.0ですが、計画変更されて2024/1Qということでアナウンスされています。とはいえ、2023/11/28時点ではZabbix7.0のpre-Releaseプレビューはalpha8まで出ていて、実際に手元環境で試すことができます。

今回の記事では注釈なき場合にはRockey linux9.3の上でDBにはPostgreSQL 13.11を採用してお話を進めていきます。

### 本題、その前にZabbix7.0の新機能を少しだけ見ておきましょうか

内容は2023年11月28日時点の[ロードマップ](https://www.zabbix.com/roadmap)に基づきます。

#### 相互運用性の強化

- APIからのデータ追加
    - 6.0までのZabbixAPIでは原則的にデータ取得のみに特化していて、ヒストリになにか追加するような操作はできませんでした。そのため、任意のプロセス等からデータを送信するような場合にはZabbix_senderやカスタムスクリプトを利用する必要がありました。7.0からはAPIからも追加できるようになる見込みです。
    - 同様にWebhookからの送信もアナウンスされています

#### UIの改善

- 新しいウィジェット、グラフの追加
    - 色々追加されます。個人的にはゲージチャートがカッコいいなと(使い方思案中)

#### HAとか性能改善とか

- NWディスカバリーの負荷分散
- 非同期でのデータ収集
    - pollerを並列実行できるようになるのでパフォーマンスが良くなります
- Zabbix ProxyのHA対応
    - 実は6.0ではサーバだけだったので結構嬉しい。負荷分散もするっぽい

などなど、その他にも色々な機能がアナウンスされています。

## 本題①:Zabbix6.0でHA使ってますか？

Zabbix6.0からサーバのHA構成を標準でサポートするようになりました。結構便利だし設定も楽です。使っていますか？

### HA構成の実装とか制約とか

超シンプルにZabbixのHA構成を図示するとこうなります。

![ZabbixのHA構成.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/6f73ebae-4250-08e2-96a5-1a8fbf59aae1.png)

つまるところ、この下手っぴな図でお伝えしたいのは

- Zabbix標準のHAはDBを中心に構成されていて...
    - (標準では)5秒に1回、各ノードはDBに生存報告をします
    - Actのノードは生存報告に基づいて各ノードのステータスを管理しています
    - Actの生存報告が無くなったら **早いもの勝ち**で所定の遅延時間を待ったあとにActに昇格します
        - つまりプライオリティの設定はありません

下手な図より実装見たい人向けにDBのテーブルに基づいて書くと...ZabbixのDBの中でha_nodeというテーブルが登場します。ha_nodeの内容は以下の通りです。
![ha_nodeテーブル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/f25408f9-a29c-261e-b7d4-dd343753f3c5.png)
つまり、各ノードは定期的にlastaccessの時刻を更新していて、Actのノードは現在時刻とlastaccessを比較して、所定の時間を超えていたらstatusを更新するような動作をしています。

また、系切替のタイミングではActになったノードが自己申告でStatusをActにした上で、同時に旧Actのノードのステータスを適当なものに更新しています。

#### つまりこの実装から導かれる制約は？

- ノードのプライオリティは設定できません(テーブルの中身見てもらえば分かるかと思いますが、そんな項目はないです)
- 各ノードはすべて同等にZabbixのDBにフルアクセス権限が必要です

辺りであると言えます。**オチに使うのでわざわざ書きました。**

### 余談：Zabbix6.0でHAをさくさくっと組んでいく

手元にまっさらなZabbix環境がなかったのでゼロから作っています(これ書きながら)。
詳細なインストール方法はこの記事では取り扱いません。

この画面たどり着くまでは爆速です。もう10分あれば目をつぶってもたどり着けますわ。

![日本語がない.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/27330ed3-be8c-3c19-9724-4a9d1f5e2fc1.png)

~~...日本語がないので今日はもう寝ましょう。~~

```bash
[root@zabbix-test-act kuro]# dnf search zabbix
Last metadata expiration check: 2:22:11 ago on Tue Nov 28 13:36:15 2023.
=============================== Name & Summary Matched: zabbix ================================
pcp-export-pcp2zabbix.x86_64 : Performance Co-Pilot tools for exporting PCP metrics to Zabbix
pcp-export-zabbix-agent.x86_64 : Module for exporting PCP metrics to Zabbix agent
zabbix-agent.x86_64 : Zabbix agent
zabbix-agent2.x86_64 : Zabbix agent 2
zabbix-agent2-plugin-mongodb.x86_64 : Zabbix Agent2 plugin for monitoring MongoDB installations
zabbix-agent2-plugin-postgresql.x86_64 : Zabbix Agent2 plugin for monitoring PostgreSQL
                                       : installations
zabbix-apache-conf.noarch : Automatic zabbix frontend configuration with apache
zabbix-get.x86_64 : Zabbix get
zabbix-java-gateway.x86_64 : Zabbix java gateway
zabbix-js.x86_64 : Zabbix js
zabbix-nginx-conf.noarch : Zabbix frontend configuration for nginx and php-fpm
zabbix-proxy-mysql.x86_64 : Zabbix proxy for MySQL or MariaDB database
zabbix-proxy-pgsql.x86_64 : Zabbix proxy for PostgreSQL database
zabbix-proxy-sqlite3.x86_64 : Zabbix proxy for SQLite3 database
zabbix-release.noarch : Zabbix repository configuration
zabbix-selinux-policy.x86_64 : Zabbix SELinux policy
zabbix-sender.x86_64 : Zabbix sender
zabbix-server-mysql.x86_64 : Zabbix server for MySQL or MariaDB database
zabbix-server-pgsql.x86_64 : Zabbix server for PostgresSQL database
zabbix-sql-scripts.noarch : Zabbix database sql scripts
zabbix-web.noarch : Zabbix web frontend common package
zabbix-web-japanese.noarch : Japanese font settings for Zabbix frontend
zabbix-web-mysql.noarch : Zabbix web frontend for MySQL
zabbix-web-pgsql.noarch : Zabbix web frontend for PostgreSQL
zabbix-web-service.x86_64 : Zabbix web service
==================================== Name Matched: zabbix =====================================
zabbix-web-deps.noarch : PHP dependencies metapackage for frontend
```

そう、これ毎度毎度忘れるんですけどZabbixのいくつからか忘れましたが(たぶん正式に日本語対応してから)、日本語フロントエンドはパッケージが独立しているのでそれを入れてやらないといけないです。

```bash
dnf install -y zabbix-web-japanese
```

(余談も余談も余談ですが、rootで作業していたり-yオプション付けて確認省略していたり、色々お行儀悪いのはご容赦を)

![日本語有.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/e98f964b-2ad7-4bff-0021-3c077c992b18.png)

これで英語が苦手な俺も一安心( ･´ｰ･｀)( ･´ｰ･｀)( ･´ｰ･｀)ﾄﾞﾔ

![frontend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/ae52d6e6-18cd-ea1e-f197-b294acb09b3d.png)

見慣れた画面にも会えたところで早速HA組んで行きます。
手順ですがざっくりとは以下の通りです。今回はアップグレードの検証だけなのでものすごく雑に、メインっぽい位置づけにするZabbix-serverマシンの中にフロントエンドもDBも置いてしまっています。

1. クラスタ組みたい他の環境にZabbixをインストールしておく
    - 要は上の下手っぴな図になるようDBの向け先は設定しておいてくださいね ということです
        - なのでHAクラスタのDBhostはすべて一緒になるはず
1. クラスタ組むためにzabbix_server.confをいじる
    - 設定箇所は「HANodeName」と「NodeAdress」の2箇所です
        - 老婆心ですがHANodeNameもユニークである必要が(DB(Index)的に)あります
         ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/c58778e6-b333-2189-6f87-60126e294033.png)
1. エージェントのコンフィグもいじる
    - ちょっと面倒なんですがServerとServerActiveにすべてのHANodeを書く必要があります

正しく設定できていればシステム情報のウィジェットは
![sysw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/2de74138-76d7-88d0-7551-ea067facb73c.png)
こんな感じになっていて、かつシステム情報の項目には
![sysinfo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/6279f9ff-7480-0948-8f09-d03fb9578477.png)
このような感じで出てくるかと思います(実はPostgreSQLの接続許可設定をしくじってここで冷汗かいたのは内緒)。

随分簡単に組めるかと思います。

## 本題②:Zabbixってアップグレードするときに何が起きているの？

当然別のバージョンのリポジトリにパッケージの取得先を向けてあげてアップデートするとバイナリ類はアップデートされています。
その後、インストールが無事に終わって「さぁ行くぞ」とzabbix_serverのプロセスを上げた瞬間にはデータベースのコンバートも実は走っています。
ちなみにこの様子はきちんとlogが吐かれているのでアップグレード後の初回起動時にはZabbixサーバのログ(デフォルトなら/var/log/zabbix_server.log)を眺めてあげるとより安心できます。

コンバート処理自体は[このC言語実装のコード](https://github.com/zabbix/zabbix/blob/master/src/zabbix_server/dbconfigworker/dbconfig_worker.c)を読むのが良いです。

ざっくりと抜粋して説明します(以下[https://github.com/zabbix/zabbix/](https://github.com/zabbix/zabbix/)からの引用)が

```c
zbx_db_begin();
zbx_db_begin_multiple_update(&sql, &sql_alloc, &sql_offset);

// ホストIDごとにアイテム情報を取得
for (zbx_uint64_t *batch = hostids->values; batch < hostids->values + hostids->values_num;
        batch += ZBX_DBCONFIG_BATCH_SIZE)
{
    // ... (バッチ処理のSQL生成)

    result = zbx_db_select("%s", sql_select);

    while (NULL != (row = zbx_db_fetch(result)))
    {
        // ... (アイテム情報の取得とマクロ展開)

        // リアルタイム名が変更されている場合、データベースを更新
        if (0 != strcmp(row[3], name))
        {
            char *name_esc = zbx_db_dyn_escape_string(name);

            zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "update item_rtname set"
                    " name_resolved='%s',name_resolved_upper=upper('%s')"
                    " where itemid=%s;\n",
                    name_esc, name_esc, row[0]);
            zbx_db_execute_overflowed_sql(&sql, &sql_alloc, &sql_offset);

            zbx_free(name_esc);
            (*updated_num)++;
        }

        zbx_free(name);
    }
    zbx_db_free_result(result);
}

zbx_db_end_multiple_update(&sql, &sql_alloc, &sql_offset);

if (sql_offset > 16)    // In ORACLE always present begin..end;
    zbx_db_execute("%s", sql);

zbx_db_commit();
```

アイテム名にマクロが含まれている場合には展開してコンバート処理してくれています。

## 本題③:お作法に従わずHA構成のままアップグレードしてみる

結論から言うとDBのオートコンバートが走って、びっくりすることになります(DB構成に関して下方互換性が保証されているわけではないので動くかもしれないし動かないかもしれません。私も今、この瞬間、これを書きながら初めてやってみています)。

### 早速やってみる

手順ですが、ざっくりと以下です。

1. スタンバイ側のZabbixサーバを停止
1. リポジトリの設定を7.0のalpha8が取れるように書き直す
1. dnf update
1. スタンバイ側のZabbixサーバを上げてみる(必要なら設定もする)
1. スタンバイ側のログとかAct側のログとかWeb画面とか眺めてみる

止めてあげたのでステータスは「停止中」になりました。
![teishi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/ad909a60-a43c-1c51-b950-ed6162c07758.png)

リポジトリを設定してdnf updateしたらこんな感じなのでさっそくやっていきましょう。
![update.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/cf79cf09-19c5-5a8c-0a53-59e53146eefc.png)

完了したところでちょっとconf系のファイルを観察してみる。
![serverconf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/6a3754ca-e518-9b58-4fb9-1a2c2aba369d.png)
当たり前ですが維持されていますね。
ではさっそく起動してログを見ていきます。

起動前のsrv2側のログ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/89a39118-9be0-2dc1-b3ec-e73ce20ffa87.png)

まぁ結論から言うと起動に失敗してます(マジか、優秀じゃん)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/aeee171d-4f70-5682-a08c-4ba174825bfe.png)

抜粋なのですが、つまるところ「HAモードで起動中の他のホストがいるからダメです」と怒られています。
じゃあsrv1止めてみましょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/25693359-1eb5-bcc5-dc16-5450691a4c04.png)

止めてあげたので改めて。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/b01ab529-afe2-5875-6ca1-d9c9a14fd688.png)
スタンドアロンで起動しろって怒られていますね。やはりこの辺の防御策はしっかりと練られたようです。

ではスタンドアロンで起動した上で再挑戦。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/d367140c-54f0-d64d-2dc7-eb6cf253405b.png)
抜粋ですが、正しくオートコンバート走ってますね。

次はWeb画面を観察してみることにします。
![webfront.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/0e7abc0b-7f5b-e565-f316-7a9e5adb14b7.png)

Oh...見るも無惨なことになってしまいました。
つまるところ「Webフロントエンドは6.0に上がっていないので古いバージョンのDB要求しているのに、オートコンバート走ってしまってDBのバージョンが合わないんだけど！？」って怒っています。

## 結論

つまるところHA構成だと、どのnodeからにしても勢いでバージョンを上げてしまうとDBをオートコンバートしてしまい壊れてしまいます。そこでもう一度公式の注意事項に立ち返って

公式では[こちら](https://www.zabbix.com/documentation/7.0/en/manual/concepts/server/ha#upgrading-ha-cluster)で説明されていますが、お作法通りの手順は以下になります。

1. すべてのノードの停止
1. DBのフルバックアップ
1. DBのコンバートは1つのノードからだけにしたいので
    1. スタンドアロンモード(HANodeNameのコメントアウトによるHA構成の停止)
    1. アップグレード実施
1. アップグレードが終わったノードのスタンドアロンモードの解除
1. 他のノードはいつも通り「えいや」でアップグレード

という手順で正しくやっていきましょう。
また、HAを組むような構成の環境だとおそらくはWebフロントエンドやProxyも別立てしていることが多いと思います。
少なくともDBのオートコンバートを走らせている間、終わってからWebフロントエンドやProxy辺りは動作しなくなっている可能性があることを念頭に作業計画を考えることをおすすめします。

## おまけ①:修理する

壊してしまったので直します。たぶん...というか確実に今回の構成ならsrv1に全部入りなのでそっちを7.0に上げたら帰ってきてくれるはず。

```bash
#とりあえず私は面倒なのでrepoファイルをoldにしてしまいます
mv /etc/yum.repos.d/zabbix-agent2-plugins.repo /etc/yum.repos.d/zabbix-agent2-plugins.repo.old
mv /etc/yum.repos.d/zabbix.repo /etc/yum.repos.d/zabbix.repo.old

#紛らわしいですが6.5のリポジトリに7.0のプレビューが置いてあるので設定します
rpm -Uvh https://repo.zabbix.com/zabbix/6.5/rocky/9/x86_64/zabbix-release-6.5-2.el9.noarch.rpm
dnf clean all
dnf upgrade
```

無事に降ってきたのでインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/ba3326c4-2c6f-068e-dcca-c9ada0ccc107.png)

無事(?)復活
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/c34852bc-e9d3-8459-38b1-465132bcdea5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3624835/6aae305f-915b-7375-918e-dc02bc69a624.png)

## おまけ②:Zabbix6.0のDBの中身

意外とER図のようなドキュメントは出回っていないので今回生成しました。よろしければお使いください。
[こちら](https://kuro524.github.io/zabbix6ER.pdf)から

## おまけ③:Zabbix7.0のDBの中身

同様に2023/11/28時点でのER図を作成しましたのでよろしければお使いください。
[こちら](https://kuro524.github.io/zabbix7ER.pdf)から

これらのER図は[schemaspy](https://schemaspy.org/)を利用して生成しています。

## 終わりに

久々に気合い入れてZabbixを触りました。統合監視は面白さが伝わりにくいのですが、色々な機材やサービスからデータを吸い上げてきて手元に置いて加工して隠れた故障を明るみにしたり、最近ではアノマリー分析なんかで「不穏な様子」の予想をしたりもあるので、突き詰めると結構奥が深いです。
社内外問わずZabbixで盛り上がれる人、お友達になってください(?)。

また日本におけるZabbix普及展開に多大な貢献を頂いている[日本Zabbixユーザ会](https://www.zabbix.jp/)の皆様ならびに、高品質なドキュメントとトレーニングをご提供頂いている[Zabbix Japan](https://www.zabbix.com/jp)の皆様に感謝申し上げます。

記載されている会社名、製品名、サービス名は、各社の商標または登録商標です。

### 仲間の募集はこちらから

https://js02.jposting.net/nttcom/u/job.phtml