0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

大学でインフラやネットワークを勉強していて、就活のポートフォリオも兼ねて自宅でホームラボを運用しています。古いノート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_HOSTDB_SERVER_HOST にコンテナ名(zabbix-serverzabbix-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-dbcommand ブロックに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 1419log_bin_trust_function_creators=1 で解決

特に ERROR 1419 は、ログのループの「最初の1回目」を見て真因にたどり着けたのが良い経験でした。自分で原因を切り分けていく感覚が掴めたので、トラブルシュートの練習としてもおすすめの題材です。

次はこのZabbixで、SNMPを使った管理スイッチの監視を設定していく予定です。

参考リンク

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?