docker-composeでcacti1.2.11＋グラフ日本語化＋自動バックアップ＆リストア＋プラグインもりもりにした（2020/5/23更新）

# ごちゅうい
解消されました。
~~（2020/7/4 更新）
dnf install でインストールされるcactiが 1.2.12 にバージョンアップされてから、インストール後にログイン画面に遷移しない問題が確認できています。
手動インストールしても同様の問題が発生しますので、調査しています。
一度、キャッシュを削除する、もしくはシークレットモードなどでログインをお試しください。~~


# やり方だけまとめると

構成ファイル（**[内容は後述](#作業内容と構成ファイルの説明)**）を取得して、ビルドして、コンテナ生成する。

```shell-session
# git clone https://github.com/bashaway/cacti ; cd cacti
# docker-compose build ; docker-compose up -d
```

で、環境を作ってから、 **[web上での初期設定](https://qiita.com/bashaway/items/47d8eb868f31ab3c20e1#web%E4%B8%8A%E3%81%A7%E3%81%AE%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A)** のとおりセットアップする。
Dockerが無ければ **[補足説明](#補足説明)**  の通り準備が必要。  


**プラグインもりもりになります**
<img src="https://i.gyazo.com/e3238062c5e768ee2ef64b5a27856b34.png" width=50%>


# 更新履歴

## 2020/5/23
* 日次で定義データ（MySQL）をRRDファイルと同じ場所にダンプするようにしました。（２日＝２世代分）
* docker-composeで環境構築する際に、上記SQLバックアップがあれば読み込んで自動的にリストアするようにしました
* SQLバックアップがなければ、初期構築用のSQLを読み込むようにしています
* 公式＋αのプラグイン（１８個）を自動的に`git clone`するようにしました。インストールはしません。

## 2020/5/6
* `dnf install cacti` でインストールされるのが 1.2.11 になっていたので、動作確認しました。
* RRAファイルのディレクトリをdockerの外側に出してマウントさせるようにしました。
* 1.2.11 にあるcookie domain 問題で、インストール時にログインできない現象の **[ワークアラウンド](https://github.com/Cacti/cacti/issues/3436#issuecomment-611285060)** を適用させています
* mod_sslを入れたので、httpsでもアクセスできるようにしています（証明書はデフォルトで準備されてる自己署名ですが）


# はじめに
やってることは **[CentOS7にcacti1.2.8をインストールする＋グラフを日本語化する](https://qiita.com/bashaway/items/47d8eb868f31ab3c20e1)** とあまり変わりません。
dockerの勉強がてらdocker-composeでまとめてみました。


# 対象機器および環境

## 検証環境
* CentOS8(8.1.1911)
* Docker(19.03.8)
* Cacti(1.2.11)
* MariaDB(10.4.12) <- 公式のlatestイメージ利用


# 作業内容と構成ファイルの説明

docker-compose.yml はじめファイル一式は [GitHub](https://github.com/bashaway/cacti) にあるものを使います。

```
# git clone https://github.com/bashaway/cacti
```

## 全体構成の説明

`git clone`で引っ張ってくると、こんなディレクトリ構造になります。

```shell-session
cacti
|-- cacti_db
|   |-- Dockerfile
|   |-- cacti.cnf
|   |-- tz.sh
|   `-- tz.sql
|-- cacti_sv
|   |-- Dockerfile
|   |-- cacti_db_backup
|   |-- docker-entrypoint.sh
|   `-- opts
|       |-- cacti_host_template_gt_-_device_cisco.xml
|       |-- cacti_host_template_gt_-_device_snmp_minimal.xml
|       |-- cacti_host_template_gt_-_ping_only.xml
|       |-- cacti_host_template_gt_-_server_esxi.xml
|       |-- cacti_host_template_gt_-_server_linux.xml
|       |-- cacti_host_template_gt_-_server_windows.xml
|       `-- ss_netsnmp_memory.php
|-- docker-compose.yml
`-- rra
```


### docker-compose.yml
DBのアカウントや初期設定、各コンテナのポートバインド、タイムゾーン設定など

```yml:cacti/docker-compose.yml
version: '3'

services:
  cacti_db:
    build: ./cacti_db
    container_name: cacti_db
    hostname: cacti_db
    environment:
      MARIADB_DATABASE: cacti
      MARIADB_USER: cactiuser
      MARIADB_PASSWORD: cactipwd
      MARIADB_ROOT_PASSWORD: rootpwd
      TZ: 'Asia/Tokyo'
    networks:
      nw:
    ports:
      - "3306:3306"

  cacti_sv:
    build: ./cacti_sv
    container_name: cacti_sv
    hostname: cactia_sv
    restart: always
    networks:
      nw:
    ports:
      - 80:80
      - 443:443
    links:
      - cacti_db
    cap_add:
      - SYS_ADMIN
    security_opt:
      - seccomp:unconfined
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:ro
      - ./rra:/var/lib/cacti/rra
    environment:
      TZ: 'Asia/Tokyo'
    depends_on:
      - cacti_db

networks:
  nw:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.enable_ip_masquerade: "true"
      com.docker.network.bridge.host_binding_ipv4: "0.0.0.0"
      com.docker.network.bridge.name: "cacti_nw"
```


ホストからみて `cacti_nw` という名称でネットワークを新設します。
このネットワークに `cacti_db` と `cacti_sv` を所属させ、データベースは 3306/tcp で通信させ、cactiサーバへはhttp/httpsでアクセスさせるようにしています。
バックアップとリストアがやりやすいように、RRDファイルが配置されるディレクトリ `/var/lib/cacti/rra` はホスト側からマウントするようにしています。


### cacti_db ： DBサーバ（のコンテナ）

DBは MariaDB の公式イメージを利用しています。

```docker:cacti/cacti_db/Dockerfile
FROM mariadb/server:latest
COPY cacti.cnf /etc/mysql/conf.d
COPY tz.sql /docker-entrypoint-initdb.d
COPY tz.sh /docker-entrypoint-initdb.d
```

アカウント情報は `docker-compose.yml` の environmentセクションで指定しています。

#### タイムゾーン処理

cacti初期セットアップ時のエラー回避に必要なタイムゾーン処理は、MariaDBの初回起動時に処理される `docker-entrypoint-initdb.d` のディレクトリにコピーしています。
処理内容は、タイムゾーン情報の入れ込みと、DB操作ユーザへのタイムゾーン情報を取得する権限の付与です。

```sh:cacti/cacti_db/tz.sh
#!/bin/sh
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -uroot -prootpwd mysql
```

```sql:cacti/cacti_db/tz.sql
grant select on mysql.time_zone_name to cactiuser identified by 'cactipwd';
```

#### cacti用にチューニングするための設定ファイル

cactiはインストール時にDBの設定がうまくないとエラーで先に進めなくなります。
自身の環境でエラーが出ないようなパラメータを設定するファイルを配置しています。
（もしかしたら別の環境ではwarningが出るかもしれません。）

```:cacti/cacti_db/cacti.cnf
[mysqld]
collation_server=utf8mb4_unicode_ci
character-set-server=utf8mb4
character_set_client=utf8mb4
max_heap_table_size=64M
tmp_table_size=64M
join_buffer_size=128M
innodb_file_format=Barracuda
innodb_large_prefix=1
innodb_buffer_pool_size=1024M
innodb_flush_log_at_timeout=3
innodb_read_io_threads=32
innodb_write_io_threads=16
innodb_buffer_pool_instances=11
innodb_io_capacity=5000
```


### cacti_sv ： cactiサーバ 兼 webサーバ（のコンテナ）

cactiサーバはcentos8の公式イメージを利用し、ビルド時にcactiのインストール、webサーバ設定、日本語フォントのダウンロードと設定、プラグインの取得などを行います。

日本語フォントはCentOS7でやったようなリポジトリからのインストールができなかったので、フォントファイルをダウンロードして、フォントキャッシュ更新で対応しています。

```docker:cacti/cacti_sv/Dockerfile
FROM centos:centos8
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime ; \
    #
    dnf -y update ; dnf -y install epel-release rsyslog logrotate cronie fontconfig unzip ; \
    #
    mkdir /usr/share/fonts/ipa ; \
    curl -L https://ipafont.ipa.go.jp/IPAfont/IPAMTTC00303.zip > /usr/share/fonts/ipa/ttc.zip ; \
    unzip /usr/share/fonts/ipa/ttc.zip -d /usr/share/fonts/ipa/ ; \
    curl -L https://ipafont.ipa.go.jp/IPAfont/IPAfont00303.zip > /usr/share/fonts/ipa/ttf.zip ; \
    unzip /usr/share/fonts/ipa/ttf.zip -d /usr/share/fonts/ipa/ ; \
    rm -f /usr/share/fonts/ipa/*.zip ; \
    fc-cache -f ; \
    #
    dnf -y install cacti-spine php mod_ssl ;\
    echo 'HTTPD_LANG=ja_JP.UTF-8' >> /etc/sysconfig/httpd ;\
    sed -i 's/^[^#]..*Require\ host\ localhost/Require all granted/' /etc/httpd/conf.d/cacti.conf ;\
    sed -i 's/^#//' /etc/cron.d/cacti ; \
    sed -i 's/database_hostname\ =..*/database_hostname\ =\ '\''cacti_db'\'';/g' /usr/share/cacti/include/config.php ;\
    sed -i 's/database_username\ =..*/database_username\ =\ '\''cactiuser'\'';/g' /usr/share/cacti/include/config.php ;\
    sed -i 's/database_password\ =..*/database_password\ =\ '\''cactipwd'\'';/g' /usr/share/cacti/include/config.php ;\
    sed -i 's/^\$cacti_cookie_domain\ /#\$cacti_cookie_domain\ /g' /usr/share/cacti/include/config.php ;\
    sed -i 's/^max_execution_time\ =..*/max_execution_time\ =\ 60/' /etc/php.ini ;\
    sed -i 's/^memory_limit\ =..*/memory_limit\ =\ 800M/' /etc/php.ini ;\
    sed -i 's/^;date.timezone\ =.*/date.timezone\ = '\''Asia\/Tokyo'\''/' /etc/php.ini ;\
    sed -i 's/DB_Host..*/DB_Host\ cacti_db/g' /etc/spine.conf ;\
    sed -i 's/DB_User..*/DB_User\ cactiuser/g' /etc/spine.conf ;\
    sed -i 's/DB_Pass..*/DB_Pass\ cactipwd/g' /etc/spine.conf ;\
    systemctl enable httpd ;\

（～省略～：git clone で プラグインをもりもりしてる処理がありますが、省略しています）

COPY docker-entrypoint.sh /usr/local/bin/
COPY cacti_db_backup /etc/cron.d/
COPY opts/ss_netsnmp_memory.php /usr/share/cacti/scripts/
RUN ln -s usr/local/bin/docker-entrypoint.sh /
ENTRYPOINT ["docker-entrypoint.sh"]
CMD [ "/usr/sbin/init" ]
```

#### 初回起動時のヘルパースクリプト

`dnf -y install cacti`でインストールしただけでは、cactiの動作に必要なDBが準備されません。
そのため、テーブル定義、初期データ生成のために、docディレクトリに配置された .sql の流し込みが必要です。
（DBそのものは、docker-compose.ymlのenvironmentセクションで記載した通り、作成済みです）
初回起動時のみ、流し込みを行うため、以下の記事のようにヘルパースクリプトを利用しています。
**[dockerで初回起動時のみ特定の処理を行うヘルパースクリプト（docker-entrypoint.sh）](https://qiita.com/bashaway/items/e6279ba432d18c2d4299)**

docker-compose.ymlでデータベースを先に起動するけど、起動完了までまたないと流し込みでエラーになってしまう。
なので、応答があるまで `sleep 1` で待ちます。
その後、データベースはあっても、テーブルが無ければ、初期化の .sql を流し込む。ということをしています。
後述のSQLバックアップがあれば、初期化の .sql ではなくて、ダンプされたsqlを流し込むようにします。

```sh:cacti/cacti_sv/docker-entrypoint.sh
#!/bin/sh

until mysqladmin -ucactiuser -pcactipwd -h cacti_db  ping ; do
  sleep 1
done

if [ "`mysql -ucactiuser -pcactipwd  -h cacti_db cacti  -e 'show tables'`" = "" ]  ; then

  if [ -e /usr/share/cacti/rra/CACTI_DB_BACKUP.sql ]  ; then
    mysql -ucactiuser -pcactipwd cacti -h cacti_db < /usr/share/cacti/rra/CACTI_DB_BACKUP.sql
  else
    mysql -ucactiuser -pcactipwd cacti -h cacti_db < /usr/share/doc/cacti/cacti.sql
  fi

  chown -R apache.apache /var/lib/cacti/rra
fi

exec "$@"
```

#### 全自動バックアップとリストアのための cron

cronで日次SQLダンプを取得するようにしています。
前日のバックアップファイルを別名で保存し、最新のデータは常時 `CACTI_DB_BACKUP.sql` という名称にしています。
ダンプデータは rra と同じ、ホスト側からマウントするディレクトリに配置することで、コンテナ消失時でもホスト側に残ります。
対象機器やIF数量、取得データ種別が多いほど領域が必要になりますし、サーバ移行の際にも手順が簡単になると思います。

```cron:cacti/cacti_sv/cacti_db_backup
2 5 * * *       root    cp -f /usr/share/cacti/rra/CACTI_DB_BACKUP.sql /usr/share/cacti/rra/CACTI_DB_BACKUP.sql.old
5 5 * * *       root    mysqldump -u cactiuser -pcactipwd cacti -h cacti_db > /usr/share/cacti/rra/CACTI_DB_BACKUP.sql 2>&1
```

このようにしておくことで、コンテナを作り直したい、というときには `docker-compose down ; docker-compose up -d` するだけで、コンテナがきれいになった状態で、cactiサーバが元通りになります。
コンテナが配置されているホストを移動させる必要があっても、ホスト側のrraディレクトリを別のところに移して `docker-compose up -d` すれば元通りになります。


#### optsディレクトリ：メモリ取得スクリプトとテンプレート

`opts` ディレクトリには、自作テンプレートとLinuxのメモリ使用量の取得に必要なスクリプトがあり、スクリプトはビルド時にcactiサーバのスクリプトディレクトリにコピーされます。
テンプレートはビルトインのテンプレートを少し修正したものです。必要であれば、構築後にConsole->Importからインポートします。

自作テンプレートもインストール時に選択させるようにしたかったので、cactiサーバの`install/functions.php`に修正しようとしましたが、本体に手をかけるのはちょっと怖いので止めました。
たぶんこのあたりの修正でできそうです。

<details><summary>install/functions.php</summary><div>

```php:install/functions.php

function install_setup_get_templates() {
        global $config;

        @ini_set('zlib.output_compression', '0');

        $templates = array(
                'Cisco_Router.xml.gz',
                'Generic_SNMP_Device.xml.gz',
                'Local_Linux_Machine.xml.gz',
                'NetSNMP_Device.xml.gz',
                'Windows_Device.xml.gz',
                'Cacti_Stats.xml.gz'
        );
```

</div></details>


## コンテナをつくる

冒頭と同じ説明ではありますが、以下のコマンドで環境を構築します。

```shell-session
# git clone https://github.com/bashaway/cacti ; cd cacti
# docker-compose build ; docker-compose up -d
（～省略～）
Creating network "cacti_cacti_nw" with driver "bridge"
Creating cacti_db ... done
Creating cacti_sv ... done
```

## 初回ログインとセットアップ
初回起動直後は sql 流し込みやらなんやらが走るので、 `docker-compose up -d` したあと、30秒～1分くらい待ってからブラウザで `http[s]://アドレス or ホスト名/cacti/` にアクセスします。
（httpsアクセスの場合、秘密鍵と証明書はあらかじめ準備されている `/etc/pki/tls/certs/localhost.crt` と `/etc/pki/tls/private/localhost.key` が使われます。）

あとは、別エントリで記載した **[web上での初期設定](https://qiita.com/bashaway/items/47d8eb868f31ab3c20e1#web%E4%B8%8A%E3%81%A7%E3%81%AE%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A)** を参照して、セットアップします。


# 補足説明

Docker と Docker Compose が無ければ、以下のとおりに準備しておく。

```
// Dockerのインストール
# dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# dnf install --nobest docker-ce docker-ce-cli containerd.io
# dnf update https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.10-3.2.el7.x86_64.rpm
# dnf update
# systemctl enable docker
# systemctl start docker

// Docker-composeのインストール
# curl -L "https://github.com/docker/compose/releases/download/1.25.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

// firewallでコンテナへのNATを追加
# firewall-cmd --add-masquerade --zone=public --permanent
# firewall-cmd --reload
```

参考：
**[ESXi6.7にCentOS8を最小構成で構築](https://qiita.com/bashaway/items/ed9997629d5b02cfca05)**
**[CentOS8にDockerをインストール。名前解決できなかったのが解消した。](https://qiita.com/bashaway/items/4001c0ad5a6f21957986)**


# さいごに
本体がバージョンアップしてても、オフィシャルサイト上のドキュメントとかプラグインがVer.0.8.8系と1.x系が混ざってて分かりにくい。
有志のプラグインはVer.0.8.8 -> Ver.1.0 で動かないのが多くなり、さらにVer.1.2系ではかなりの数が動かなくなってしまった。
しかもメンテもされてないみたい。weathermapとかよかったのに、とうとう昨年に **[Ver.1.xでやりません](https://www.network-weathermap.com/2019/03/28/end-of-cacti-1-x-plans/)** とかになってしまった。
weathermapを使いたいがためにVer.0.8.8を使う人も多いんだろうな。

もういっそのことcactiから乗り換えるか、と思ってしまいますが、これはこれでベースがちょうどいい使いやすさなんですよね。


# 出典
https://qiita.com/bashaway/items/e6279ba432d18c2d4299
https://www.atmarkit.co.jp/ait/articles/1812/14/news034.html
https://github.com/Cacti/cacti/issues/3436