はじめに
大学でインフラやネットワークを勉強していて、就活のポートフォリオも兼ねて自宅でホームラボを運用しています。古いノートPCを自宅サーバーにして、Proxmox VE をハイパーバイザーとして動かし、その上に Rocky Linux 9 を VM として立てています。
今回は、その Rocky Linux VM 上の Docker(Docker Compose)に Zabbix 7.0 LTS を導入した流れを記録として残します。監視サーバーを自前で持っておくと、後でスイッチやサーバーの監視を勉強するときに土台になるので、最初の一歩としてかなり良い題材でした。
構成イメージはこんな感じです。
ノートPC = Proxmox VE(ハイパーバイザー)
└─ VM: Rocky Linux 9
└─ Docker Compose
└─ MySQL 8.0 + Zabbix Server + Zabbix Web
この記事では、ディレクトリ構成のルールから docker-compose.yml の中身、起動確認、そして実際にハマった2つのトラブルまでをまとめます。SNMPでのスイッチ監視や外部公開(Cloudflare Tunnel)の話は別記事にする予定なので、ここでは「Zabbixが起動するところまで」をゴールにしています。
前提環境
- Proxmox VE 上の Rocky Linux 9 VM
- Docker / Docker Compose インストール済み
- Zabbix 7.0 LTS(LTS = 長期サポート版を選択)
- DBは MySQL 8.0
Zabbixはバージョンによってサポート期間が違うので、長く使うホームラボでは LTS 版を選んでおくと安心です。7.0 は LTS なので今回はこれにしました。
ディレクトリ構成のルール
自分のホームラボでは、Dockerで動かすサービスは ~/docker/<サービス名>/ という形で統一しています。こうしておくと、どのサービスがどこにあるか迷わないですし、バックアップを取るときも ~/docker/ をまるごと固めればいいので楽です。
今回はZabbix用に以下を用意しました。
mkdir -p ~/docker/zabbix
cd ~/docker/zabbix
共有ネットワークの作成
複数のサービス(今回はZabbixだけですが、将来的にcloudflaredやWebアプリなど)を Docker 上で動かすので、サービスをまたいで通信できる共有ネットワークを1つ作っておきます。自分は shared_network という名前で統一しています。
docker network create shared_network
すでに作成済みの場合は already exists と出るだけなので、気にせず進めてOKです。docker-compose.yml 側ではこれを「外部ネットワーク(external)」として参照します。
なお、Zabbix単体だけを動かすのであれば、この共有ネットワークは必須ではありません。Compose はプロジェクトごとに default ネットワークを自動で作るので、networks の記述を省略しても3つのコンテナは問題なく通信できます。共有ネットワークが効いてくるのは、別のComposeプロジェクト(cloudflaredやWebアプリなど)と相互に通信させたいケースです。自分は将来そうした構成を見越して最初から共有ネットワークに統一していますが、まずZabbixだけ動かしたい人はこの手順を飛ばしても大丈夫です。
.env ファイルの作成
パスワードなどの秘密情報は docker-compose.yml に直書きせず、.env ファイルに分けます。
nano ~/docker/zabbix/.env
MYSQL_ROOT_PASSWORD=(ルート用の強いパスワード)
MYSQL_DATABASE=zabbix
MYSQL_USER=zabbix
MYSQL_PASSWORD=(zabbixユーザー用のパスワード)
作成したら、パーミッションを絞っておきます。パスワードが入っているファイルなので、自分以外が読めないようにしておくのが基本です。
chmod 600 ~/docker/zabbix/.env
docker-compose.yml の作成
本体です。MySQL 8.0、Zabbix Server、Zabbix Web(nginx)の3つのコンテナを定義します。
nano ~/docker/zabbix/docker-compose.yml
services:
zabbix-db:
image: mysql:8.0
container_name: zabbix-db
restart: unless-stopped
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_bin
- --default-authentication-plugin=mysql_native_password
- --log_bin_trust_function_creators=1
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- ./data/mysql:/var/lib/mysql
networks:
- shared_network
zabbix-server:
image: zabbix/zabbix-server-mysql:alpine-7.0-latest
container_name: zabbix-server
restart: unless-stopped
environment:
DB_SERVER_HOST: zabbix-db
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "10051:10051"
depends_on:
- zabbix-db
networks:
- shared_network
zabbix-web:
image: zabbix/zabbix-web-nginx-mysql:alpine-7.0-latest
container_name: zabbix-web
restart: unless-stopped
environment:
ZBX_SERVER_HOST: zabbix-server
DB_SERVER_HOST: zabbix-db
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
PHP_TZ: Asia/Tokyo
ports:
- "8080:8080"
depends_on:
- zabbix-db
- zabbix-server
networks:
- shared_network
networks:
shared_network:
external: true
ポイントは MySQL の command ブロックです。--log_bin_trust_function_creators=1 を入れていますが、これがないと後述する ERROR 1419 でZabbixのスキーマ投入が止まります(実際にハマったので後で詳しく書きます)。
--default-authentication-plugin=mysql_native_password を付けているのは、Zabbix公式のコンテナ手順がMySQL 8.0構成でこの指定をしているためです。MySQL 8.0 のデフォルト認証は caching_sha2_password ですが、Zabbixは mysql_native_password を前提とする部分があるので、公式手順に合わせてこのオプションを入れています。
ただしこれはMySQL 8.0時点の書き方です。MySQL 8.4以降では認証プラグインの扱いが変わっており、--default-authentication-plugin=... ではなく --mysql-native-password=ON を指定する形に変わっていたり、そもそも caching_sha2_password への移行が推奨されていたりします。MySQLのバージョンを上げる際は、この部分の書き方が変わる点に注意してください。今回はMySQL 8.0を明示的に使っているので、上記の書き方で問題ありません。
ZBX_SERVER_HOST や DB_SERVER_HOST にコンテナ名(zabbix-server、zabbix-db)を指定していますが、同じDockerネットワーク内ならコンテナ名で名前解決ができるので、IPを直接書く必要はありません。
起動前に変数展開を確認する
docker compose up する前に、.env の値がちゃんと反映されているかを確認しておきます。これは後述のトラブルで痛い目を見たので、起動前のルーティンにしています。
cd ~/docker/zabbix
docker compose config
このコマンドは、変数を全部展開した状態の最終的な設定を表示してくれます。MYSQL_USER などが意図した値になっているかをここで確認します。
起動
確認できたら起動します。
docker compose up -d
初回はイメージのダウンロードと、Zabbix Server による MySQL へのスキーマ投入が走るので、少し時間がかかります。
起動ログの確認
スキーマ投入がちゃんと完走したかを、Zabbix Server のログで見守ります。
docker compose logs -f zabbix-server
以下のようなログが出れば成功です。
** Creating 'zabbix' schema in MySQL
...
Starting Zabbix Server. Zabbix 7.0.x
...
server #0 started [main process]
server #0 started [main process] が出ればゴールです。Ctrl+C でログ表示を抜けます。
Web画面へのアクセス
ブラウザから以下にアクセスします。
http://<VMのIPアドレス>:8080
ログイン画面が出れば成功です。初回起動時のデフォルトの認証情報は、Zabbixの仕様で以下に決まっています。
- ユーザー名:
Admin(A が大文字) - パスワード:
zabbix
これはあくまで初期状態のデフォルト値で、誰でも知っている情報です。ログインできたら真っ先にこの初期パスワードを変更しておきましょう。
ここからは、実際に構築中にハマった2つのトラブルを記録しておきます。どちらも検索してたどり着いた人の役に立つと思うので、原因と解決をセットで残します。
トラブル1:.env の値がシェル環境変数に上書きされる
最初に起動したとき、.env には MYSQL_USER=zabbix と書いていたのに、なぜかDBユーザーが別の名前で作られてしまい、Zabbix Server が DB に接続できないという症状が出ました。
原因は、docker-compose.yml 内の ${MYSQL_USER} を展開するときに、シェルの環境変数が .env の値よりも優先されていたことでした。Docker Compose が MYSQL_USER: ${MYSQL_USER} のような変数展開を行う際、シェル側に同名の環境変数(例:export MYSQL_USER=hoge)が設定されていると、.env ファイルの値ではなくそちらが使われます。過去の作業でこの環境変数がシェルに残っていて、それが効いてしまっていたわけです。
念のため補足すると、これはあくまで「Composeファイル内の ${...} 変数展開」の話です。.env ファイル、env_file ディレクティブ、コンテナ内の environment はそれぞれ別の仕組みなので混同しないよう注意してください。今回引っかかったのは、environment: に書いた ${MYSQL_USER} の展開元がシェル環境変数に奪われた、というケースです。
厄介なのは、エラーらしいエラーが出ずに「なぜか違う値で起動する」という形になる点です。.env を何度見直しても原因が分からず、しばらく悩みました。
解決
起動前に docker compose config で「最終的にどの値が使われるか」を確認するのが確実です。
docker compose config
ここで MYSQL_USER が意図しない値になっていたら、シェル側の環境変数を疑います。
# 現在の環境変数を確認
env | grep MYSQL
# 不要な環境変数を消す
unset MYSQL_USER
この経験以降、**「起動前に必ず docker compose config で変数展開を確認する」**を習慣にしました。.env を信じすぎず、実際に展開された結果を見るのが大事です。
トラブル2:MySQL 8.0 で ERROR 1419 が出てスキーマ投入が止まる
これが一番ハマったやつです。起動すると、Zabbix Server のログがこんなループに陥りました。
... database is not initialized: users table is empty
一見すると「users テーブルが空」が原因に見えますが、これは結果であって真の原因ではありませんでした。そもそも users table is empty というメッセージは、スキーマ投入の失敗・DB初期化の途中停止・権限の問題・DB接続の問題など、複数の原因で発生し得ます。なので、このメッセージだけ見ても原因は特定できません。ログをよく見て、ループの「最初の1回目」までさかのぼると、今回のケースの本当の原因が見つかりました。
ERROR 1419 (HY000) ... you *might* want to use the less safe log_bin_trust_function_creators variable
ERROR 1419 とは
MySQL 8.0 では、バイナリログが有効な状態でストアドファンクション(関数)を作成するには SUPER 権限が必要です。Zabbixのスキーマには関数定義が含まれていて、zabbix ユーザーには SUPER 権限がないため、関数を作る行でエラーになって止まっていました。
その結果、テーブルは途中まで作られるものの、初期データ(users テーブルへの管理者アカウント投入など)まで到達できず、2回目以降の起動で「テーブルはあるけど users が空」というループに陥っていた、という流れでした。
つまり今回のケースでは、
ERROR 1419(真の原因)
→ スキーマ投入が途中で停止
→ users テーブルにデータが入らない
→ "users table is empty" ループ(表面的な症状)
という因果関係だったわけです。users table is empty 自体は他の原因でも出るメッセージですが、今回はその大元をたどると ERROR 1419 だった、ということです。表面的なエラーメッセージだけ見ていると永遠に解決しない、という良い教訓になりました。
解決
MySQLの起動オプションに --log_bin_trust_function_creators=1 を追加します。これで SUPER 権限なしでも関数を作成できるようになります。zabbix-db の command ブロックに1行足します。
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_bin
- --default-authentication-plugin=mysql_native_password
- --log_bin_trust_function_creators=1 # ← これを追加
設定を追加したら、途中まで壊れたDBが残っているので、一度きれいに作り直します。
ここで注意点があります。今回の構成はDBデータを名前付きボリュームではなく bind mount(./data/mysql:/var/lib/mysql) で持っています。そのため docker compose down -v を実行しても、ホスト側の ~/docker/zabbix/data の中身は消えません。実際にDBデータを消しているのは、後続の rm -rf の方です。
# コンテナを停止・削除(bind mountの実データはこれでは消えない)
docker compose down -v
# 中途半端に作られたDBデータを実際に削除する(これが本体)
sudo rm -rf ~/docker/zabbix/data
# 起動し直し
docker compose up -d
# ERROR 1419 が出ないことを確認しながら見守る
docker compose logs -f zabbix-server
down -v の -v は名前付きボリュームを削除するオプションですが、今回はbind mount構成なので「念のため付けている」程度の意味しかありません。データを確実に初期化したいときは rm -rf ~/docker/zabbix/data が必須だと覚えておくと安全です。
今度は ERROR 1419 が出ずにスキーマ投入が最後まで完走し、server #0 started [main process] までたどり着けば成功です。
なお、この log_bin_trust_function_creators の問題は、MySQL側でバイナリログが有効になっている設定のときに発生します。MySQLの設定や使うイメージによっては起こらないこともあるので「必ず出る問題」ではありませんが、Zabbixのように関数定義を含むスキーマを投入するアプリでは踏みやすいポイントです。Cactiなど他のOSSでも同じ理屈でハマることがあるので、知っておくと役立ちます。
まとめ
Proxmox 上の Rocky Linux VM の Docker に、Zabbix 7.0 LTS を Docker Compose で導入しました。やったことを整理すると、
-
~/docker/<サービス名>/というディレクトリ規則で配置 - 共有ネットワーク
shared_networkを作成して external 参照(Zabbix単体なら省略も可) - 秘密情報は
.envに分けてchmod 600 - MySQL 8.0 + Zabbix Server + Zabbix Web の3コンテナ構成
- 起動前に
docker compose configで変数展開を確認 - Web画面(
:8080)でログインまで確認
ハマったポイントは2つで、どちらも「表面のエラーと真の原因が違う」タイプでした。
- compose.yml内の
${MYSQL_USER}展開がシェル環境変数に奪われる →docker compose configで確認する習慣 - MySQL 8.0 の
ERROR 1419→log_bin_trust_function_creators=1で解決
特に ERROR 1419 は、ログのループの「最初の1回目」を見て真因にたどり着けたのが良い経験でした。自分で原因を切り分けていく感覚が掴めたので、トラブルシュートの練習としてもおすすめの題材です。
次はこのZabbixで、SNMPを使った管理スイッチの監視を設定していく予定です。
参考リンク
- Zabbix公式ドキュメント(Docker):https://www.zabbix.com/documentation/current/
- Zabbix Docker イメージ(Docker Hub):https://hub.docker.com/u/zabbix