为什么是 Cacti + Spine
我们用 Cacti 来监控自家骨干和 IDC 的设备,看重的是它基于 RRDtool 的数据存储和出图能力,以及围绕它成熟的生态:远程和本地数据采集、设备扫描发现、设备与图形的自动化创建、自定义采集方法、用户/组/域的访问控制。对于动辄上千台设备的网络来说,这些功能是实打实有用的。
Cacti 最初是 Ian Berry 在 2000 年做的一个高中研究项目,如今已经是大量公司和网络工程师监控企业网络与数据中心的常用方案。
默认的 PHP poller(cmd.php)在设备一多的时候会吃力,所以我们配 Spine——它是 C 写的多线程采集器,直接连数据库、并发跑采集,能把成千上万台设备的轮询压在一个 poller 周期内完成。本文记录我们在 CentOS 7 上把这套(Cacti + Spine + PHP 7.4 + MySQL 5.7 + nginx)跑通的完整配置。
需要先说清楚的是:这套组合里真正容易踩的坑不在"装",而在几个配置细节——MySQL 的 socket 路径、字符集、innodb_file_format,以及 Remi 源 PHP 那套非标准的路径。下面会逐个点出来。
安装基础依赖和数据库
1. 基础依赖
先装好对应的 yum repo,避免后面部分依赖的 rpm 包找不到:
yum install epel-release.noarch -y
yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm -y
这里我们用的是 Remi 源封装的 PHP rpm 包。Remi 的好处是 PHP 版本齐全,代价是它把工作路径、配置路径、session 目录都放在 /etc/、/opt/、/var/ 下的 remi 子目录里,和标准位置不一样。后面配置 Cacti 的 PHP 可执行文件路径、session 目录权限时,都要迁就这一点,否则 cmd.php / poller.php 会跑不起来。这是第一个坑,记在这里。
接下来装基础库:
yum install wget gcc gcc-c++ zlib-devel wget libcurl-devel openssl-devel bzip2-devel gmp-devel.x86_64 mpfr-devel.x86_64 libmpc-devel.x86_64 libSM-devel.x86_64 libxml2-devel gcc-gfortran.x86_64 bzip2-devel libjpeg-turbo-devel.x86_64 openjpeg-devel.x86_64 libpng12-devel.x86_64 libtiff-devel cmake3.x86_64 autoconf automake libtool yum-utils libSM-devel.x86_64 bzip2-devel libffi-devel jemalloc-devel.x86_64 gperftools-devel.x86_64 jasper-devel.x86_64 libxml2-devel tbb-devel libdc1394-devel gstreamer-devel.x86_64 blosc-devel libaec-devel brotli-devel.x86_64 giflib-devel.x86_64 gtk2-devel libaio-devel libsqlite3x-devel.x86_64 gperftools-devel.x86_64 make eigen3-devel ntp ntpdate perl-devel perl-CPAN perl-YAML fontconfig ttmkfdir -y
这里装了相当多图像、甚至视频相关的库。觉得多余的话,gstreamer 那一类可以不装;但 ntp/ntpdate 这些时间相关的、以及 fontconfig/ttmkfdir 这些字体相关的,建议留着——Cacti 出图依赖字体,采集和 RRD 又对时间敏感。
2. 安装数据库
我们选 MySQL 5.7:
wget http://repo.mysql.com/mysql57-community-release-el7-10.noarch.rpm
rpm -Uvh mysql57-community-release-el7-10.noarch.rpm
yum install -y mysql-community-server
rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
装完先别急着启动。先把 my.cnf 改成下面这样再启:
[mysql]
socket=/var/lib/mysql/mysql.sock
[mysqld]
symbolic-links=0
collation-server=utf8mb4_unicode_ci
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
default-time_zone = '+8:00'
user = mysql
bind-address = 127.0.0.1
skip_name_resolve = 1
innodb_file_per_table=1
innodb_file_format=Barracuda
lower_case_table_names=0
max_connect_errors = 2000
max_allowed_packet = 1024M
interactive_timeout = 1200
read_buffer_size = 16M
join_buffer_size = 128M
sort_buffer_size = 2M
read_rnd_buffer_size = 8M
sort_buffer_size = 8M
table_open_cache = 8
slow_query_log = 1
long_query_time = 6
expire_logs_days = 90
thread_stack = 256K
max_connections = 2000
wait_timeout=3600
net_read_timeout=3600
net_write_timeout=3600
interactive_timeout=3600
connect_timeout=28800
这里面有三处是 Cacti/Spine 专门要盯的,不是随手抄的性能参数:
socket=/var/lib/mysql/mysql.sock:Spine 默认走 socket 连数据库,socket 路径对不上,Spine 就连不上库。collation-server=utf8mb4_unicode_ci:影响 Cacti 的编码。注意 utf8mb4 下某些表的索引 key 有长度上限,字符集/排序规则选错,导入 cacti.sql 时个别表会建不起来。innodb_file_per_table=1和innodb_file_format=Barracuda:影响数据库性能和行格式,Cacti 对大表的行格式有要求。
改完配置第一次启动 MySQL,它会生成一个临时 root 密码,在 /var/log/message 里能找到。首次登录后改掉:
ALTER USER 'root'@'%' IDENTIFIED BY 'your_new_root_password';
然后把时区数据导进 MySQL(Cacti 需要时区表来正确处理时间):
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
3. 安装 SNMP / RRDtool
Cacti 工作需要 fping、rrdtool、net-snmp 这一类依赖:
yum install net-snmp.x86_64 net-snmp-utils rrdtool-devel rrdtool.x86_64
4. 初始化 Cacti 数据库
登录数据库后,建库、建用户、导入 Cacti 的初始 schema:
create database cacti DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
GRANT ALL PRIVILEGES ON `cacti` . * TO 'cactiuser'@'localhost' IDENTIFIED BY 'your_cacti_password';
GRANT SELECT ON mysql.time_zone_name TO 'cactiuser'@'localhost';
FLUSH PRIVILEGES;
use cacti
source /your/cacti/path/cacti.sql
注意那条 GRANT SELECT ON mysql.time_zone_name——Cacti 要能读到时区表才能正确换算时间,这一步漏了图表时间会错乱。
安装配置 PHP 和 nginx
1. 安装 PHP
yum install php74.x86_64 php74-php-ffi php74-php-devel php74-php-cli php74-php-snmp php74-php-mysql php74-php-pdo php74-php-mysqlnd php74-php-bcmath.x86_64 php74-php-xml.x86_64 php74-php-gd.x86_64 php74-php-mbstring php74-php-process php74-php-ldap php74-php-gmp php74-php-intl
Remi 源的 PHP 版本齐全,但如前所述,它的工作路径、配置路径、session 目录都在 remi 的子目录里,所以要迁就它的环境变量和路径。
2. 配置 php.ini 和 php-fpm
Remi 装完后配置文件在 /etc/opt/remi/php版本/ 下。要配的几个值:
date.timezone =
cgi.fix_pathinfo=
memory_limit =
php-fpm 这样配:
user = nobody
group = nobody
listen = /var/run/php7.sock
listen.owner = nobody
listen.group = nobody
listen.mode = 0660
这里我们让 PHP 以 nobody 用户和组运行。于是又有一个和 Remi 路径相关的坑:Remi 装出来的 session 目录(php_value[session.save_path] = /var/opt/remi/php74/lib/php/session)即 /var/opt/remi/php74/lib/php/session,属主默认不是 nobody,必须把它改成 nobody:nobody 并设好权限,否则登录态写不进去。
3. 安装配置 nginx
yum install nginx -y
主配置文件:
user nobody;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex on;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
error_log /var/log/nginx/error.log;
include mime.types;
default_type application/octet-stream;
client_max_body_size 10000M;
keepalive_timeout 300s 300s;
keepalive_requests 10000;
server_names_hash_bucket_size 51200;
client_header_buffer_size 64M;
large_client_header_buffers 4 64k;
sendfile on;
autoindex off;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
server_tokens off;
access_log /var/log/nginx/access.log;
proxy_buffering on;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
proxy_max_temp_file_size 0;
proxy_cache_use_stale updating;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 301 1h;
proxy_cache_valid any 10m;
proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:512m max_size=512m inactive=16800h use_temp_path=off;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/vhost/*.conf;
}
建 /etc/nginx/vhost/ 目录,在里面放 cacti.conf:
server {
listen 8443; # Cacti 工作端口
server_name _; # Cacti 域名
index index.php index.htm index.html;
root /home/www/html/cacti; # Cacti 工作目录
charset utf-8;
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
deny all;
}
location ~ \.php$ {
try_files $uri /index.php =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php7.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
注意 fastcgi_pass 指向的 unix:/var/run/php7.sock 要和 php-fpm 里的 listen 一致。配完记得给 nginx 以下目录写入权限:/var/lib/nginx。
安装配置 Spine 采集
1. 安装 Spine
wget https://www.cacti.net/downloads/spine/cacti-spine-1.y.z.tar.gz
tar -zxvf cacti-spine-1.y.z.tar.gz
cd cacti-spine-1.y.z
./bootstrap
./configure
make & make install
Spine 的大版本要和 Cacti 对应,下载时把 1.y.z 换成和你 Cacti 版本匹配的号。
2. Spine 配置文件
DB_Host localhost
DB_Database cacti
DB_User 数据库用户
DB_Pass 数据库密码
DB_Port 3306
#DB_UseSSL 0
#DB_SSL_Key
#DB_SSL_Cert
#DB_SSL_CA
Cacti_Log /home/www/html/cacti/log/cacti.log # 按你的 Cacti 工作目录来
DB_Host localhost 配合前面 MySQL 的 socket 配置——Spine 会走 socket 连库,这也是为什么前面要把 socket=/var/lib/mysql/mysql.sock 定好。
启动并配置 Cacti
把 Cacti 解压到安装目录。安装向导里会连数据库,这时需要把 PHP 可执行文件的路径写对——因为用的是 Remi 的 PHP,路径不是标准位置:
INSERT INTO settings (name, value) VALUES ('path_php_binary', '/opt/remi/php74/root/bin/php');
不是用 Remi 装的 PHP,就改成你实际的路径。同时确保 Cacti 对工作目录下的 log 和 rra 目录有写权限,否则出不了图。
然后配 crontab 跑 poller:
*/1 * * * * php可执行文件绝对路径 /cacti工作路径/poller.php > /dev/null 2>&1
还有一处容易忽略:cmd.php / poller.php 开头是
#!/usr/bin/env php
它靠 PATH 环境变量去找 php 可执行文件。Remi 的 php 不在默认 PATH 里,所以要么人肉改脚本首行,要么改 /etc/profile,要么临时:
export PATH=$PATH:/opt/remi/php74/root/bin/
这之后,访问 Cacti 的 Web 端口就能进去做图形配置了。
小结
这套东西装起来不难,难的是三类路径/配置对齐:MySQL 的 socket 要和 Spine 一致、字符集和 innodb_file_format 要满足 Cacti 的 schema、Remi 的非标准 PHP 路径要在 php-fpm 运行用户、session 目录权限、path_php_binary 和 poller 的 PATH 这几处全部对上。把这几处盯住,剩下的照着走即可。
