#!/usr/bin/env bash
#
# 服务器盘点采集脚本(只读,不做任何修改)
# 用途:服务器关机计划 - 第一步 A 服务器情况记录
# 用法:sudo bash server_inventory.sh [站点名,如 prod / dev]
# 不带参数则自动用主机名命名
# 输出:/root/inventory_<站点名>_<日期>.txt(同时回显保存路径)
#
# 原则:本脚本只采集信息,不修改任何配置;发现可疑内容人工判断后报甲方。
#
set -u
# ---------- 基本设置 ----------
SITE="${1:-$(hostname -s 2>/dev/null || echo unknown)}"
STAMP="$(date '+%Y-%m-%d %H:%M:%S %Z')"
OUT="/root/inventory_${SITE}_$(date '+%Y%m%d_%H%M%S').txt"
# 非root时输出到当前目录
if [ "$(id -u)" -ne 0 ]; then
OUT="$(pwd)/inventory_${SITE}_$(date '+%Y%m%d_%H%M%S').txt"
fi
log() { echo "[$(date '+%H:%M:%S')] $*" >&2; }
# ---------- 采集工具函数 ----------
run() {
# 用法: run "标题" 命令...
local title="$1"; shift
echo ""
echo "=============================================================="
echo "### $title"
echo "### 命令: $*"
echo "--------------------------------------------------------------"
"$@" 2>&1
echo ""
}
run_sh() {
# 用法: run_sh "标题" "shell语句"
local title="$1"; shift
echo ""
echo "=============================================================="
echo "### $title"
echo "### 语句: $*"
echo "--------------------------------------------------------------"
bash -c "$*" 2>&1
echo ""
}
# ---------- 开始写入报告 ----------
{
echo "##############################################################"
echo "# 服务器盘点报告"
echo "# 站点标记 : $SITE"
echo "# 主机名 : $(hostname 2>/dev/null)"
echo "# 采集时间 : $STAMP"
echo "# 采集人 : $(whoami)(执行方脚本 server_inventory.sh)"
echo "# 说明 : 本脚本只读采集,未修改任何服务器配置"
echo "##############################################################"
# ========== 1. 系统信息 ==========
run "操作系统版本" cat /etc/os-release
run "内核版本" uname -a
run "系统运行时间/负载" uptime
run "主机名与IP" hostname -I
# ========== 1.5 Docker 容器(本站 Nginx/MariaDB 跑在 Docker 里,重点盘点) ==========
if command -v docker >/dev/null 2>&1; then
run "全部容器(含已停止)" docker ps -a --no-trunc
run "容器镜像列表" docker images
run "Docker 卷" docker volume ls
run "Docker 网络" docker network ls
# 逐个运行中的容器记录关键信息:端口映射、挂载、重启策略、启动命令
for cid in $(docker ps -q 2>/dev/null); do
cname=$(docker inspect --format '{{.Name}}' "$cid" 2>/dev/null | sed 's|^/||')
echo ""
echo "=============================================================="
echo "### 容器详情: $cname"
echo "--------------------------------------------------------------"
docker inspect --format '镜像: {{.Config.Image}}
重启策略: {{.HostConfig.RestartPolicy.Name}}
端口映射: {{range $p, $b := .NetworkSettings.Ports}}{{$p}} -> {{range $b}}{{.HostIp}}:{{.HostPort}} {{end}}; {{end}}
挂载: {{range .Mounts}}{{.Source}} -> {{.Destination}} ({{.Mode}}); {{end}}
启动命令: {{.Path}} {{range .Args}}{{.}} {{end}}
网络: {{range $k, $v := .NetworkSettings.Networks}}{{$k}}({{$v.IPAddress}}) {{end}}' "$cid" 2>&1
echo ""
done
run_sh "docker-compose 文件位置" \
"find /root /home /opt /srv /etc /var/www -maxdepth 4 \\( -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -o -name 'compose.yml' \\) 2>/dev/null"
# 自动识别 nginx / 数据库容器,进容器取版本和配置
NGINX_C=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'nginx' | head -1)
DB_C=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'maria|mysql' | head -1)
echo ""
echo "=============================================================="
echo "### 容器内 Web/数据库版本与配置"
echo "--------------------------------------------------------------"
if [ -n "${NGINX_C:-}" ]; then
echo "---- Nginx 容器: $NGINX_C ----"
docker exec "$NGINX_C" nginx -v 2>&1
docker exec "$NGINX_C" nginx -T 2>/dev/null | grep -E "server_name|listen|root " | grep -v '^\s*#' | head -30
echo "(完整配置可用: docker exec $NGINX_C nginx -T 导出)"
else
echo "未找到运行中的 nginx 容器"
fi
echo ""
if [ -n "${DB_C:-}" ]; then
echo "---- 数据库容器: $DB_C ----"
docker exec "$DB_C" sh -c 'mysqld --version 2>/dev/null || mariadbd --version 2>/dev/null || mysql --version 2>/dev/null' 2>&1
echo "容器内数据目录大小:"
docker exec "$DB_C" sh -c 'du -sh /var/lib/mysql 2>/dev/null' 2>&1
echo "容器内数据库列表(root 无密码登录失败时留待人工补充):"
docker exec "$DB_C" sh -c 'mysql -uroot -e "show databases;" 2>/dev/null || mariadb -uroot -e "show databases;" 2>/dev/null' 2>&1 || true
else
echo "未找到运行中的 mariadb/mysql 容器"
fi
else
echo ""
echo "### ⚠ docker 命令不可用,但按已知信息 Nginx/MariaDB 跑在 Docker 里,需人工排查"
fi
# ========== 2. Web 服务 ==========
run_sh "Nginx 版本(宿主机)" "nginx -v 2>&1 || echo '宿主机未装 nginx(在 Docker 中属正常,见 1.5 节容器详情)'"
run_sh "Apache 版本" "apache2 -v 2>&1 || httpd -v 2>&1 || echo 'apache 未安装'"
run_sh "PHP 版本(宿主机)" "php -v 2>&1 | head -3 || echo 'php 未安装在宿主机'"
run_sh "Web/容器进程" "ps aux | grep -E 'nginx|apache|httpd|php-fpm|docker-proxy|containerd' | grep -v grep || echo '无'"
run_sh "Nginx/Apache 配置文件位置(宿主机侧)" \
"ls -la /etc/nginx/ 2>/dev/null; ls -la /etc/nginx/sites-enabled/ 2>/dev/null; ls -la /etc/httpd/conf.d/ 2>/dev/null; ls -la /etc/apache2/sites-enabled/ 2>/dev/null; echo '若以上为空,Nginx 配置在容器内或挂载目录中(见 1.5 节挂载信息)'"
# ========== 3. 数据库 ==========
run_sh "MySQL/MariaDB 版本(宿主机客户端)" "mysql --version 2>&1 || mariadb --version 2>&1 || echo '宿主机无 mysql 客户端(数据库在 Docker 中属正常,见 1.5 节)'"
run_sh "数据库进程" "ps aux | grep -E 'mysqld|mariadb' | grep -v grep || echo '宿主机无数据库进程(在 Docker 中属正常)'"
run_sh "数据库数据目录" "ls -ld /var/lib/mysql 2>/dev/null; du -sh /var/lib/mysql 2>/dev/null || echo '宿主机无 /var/lib/mysql(数据可能在 Docker 卷或挂载目录,见 1.5 节)'"
# ========== 4. 磁盘 ==========
run "磁盘使用 df -h" df -h
run "inode 使用" df -i
run_sh "大目录 TOP10 (/)" "du -xh --max-depth=3 / 2>/dev/null | sort -rh | head -10"
# ========== 5. 端口监听 ==========
run "监听端口 ss -tlnp" ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null
run "UDP 监听 ss -ulnp" ss -ulnp 2>/dev/null || true
# ========== 6. 运行中的服务 ==========
run "运行中的 systemd 服务" systemctl list-units --type=service --state=running
run "已启用但未运行的服务" systemctl list-units --type=service --state=inactive --no-pager | head -40 || true
run "自启动服务列表" systemctl list-unit-files --type=service --state=enabled
# ========== 7. 定时任务(全部用户) ==========
echo ""
echo "=============================================================="
echo "### 全部用户 crontab"
echo "--------------------------------------------------------------"
for user in $(cut -f1 -d: /etc/passwd); do
cron=$(crontab -l -u "$user" 2>/dev/null)
if [ -n "$cron" ]; then
echo ""
echo "---- crontab: $user ----"
echo "$cron"
fi
done
echo ""
run "/etc/cron.* 目录内容" "ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.weekly/ /etc/cron.monthly/ 2>/dev/null"
run "/etc/crontab" cat /etc/crontab
run "systemd timer 定时器" systemctl list-timers --all
run_sh "crontab 中调用 docker 的任务(容器内定时任务通常从这里触发)" \
"for user in \$(cut -f1 -d: /etc/passwd); do c=\$(crontab -l -u \$user 2>/dev/null); [ -n \"\$c\" ] && echo \"\$c\" | grep -i docker | sed \"s/^/\$user: /\"; done; echo '(若为空则无宿主机侧 docker 定时任务)'"
# ========== 8. /etc/hosts 及网络 ==========
run "/etc/hosts" cat /etc/hosts
run "/etc/resolv.conf" cat /etc/resolv.conf
run "iptables 规则" iptables -L -n 2>/dev/null | head -50 || echo "需要 root 或未安装"
# ========== 9. 网站目录 ==========
echo ""
echo "=============================================================="
echo "### 网站目录探测"
echo "--------------------------------------------------------------"
for d in /var/www /srv/www /home/*/public_html /opt /usr/share/nginx; do
[ -e "$d" ] && ls -la "$d" 2>/dev/null
done
echo ""
run_sh "wp-config.php 位置(不含内容,只列路径)" \
"find /var/www /srv /home -maxdepth 5 -name 'wp-config.php' 2>/dev/null"
run_sh "网站目录大小" "du -sh /var/www/* 2>/dev/null"
# ========== 10. WordPress 以外的服务/脚本排查 ==========
echo ""
echo "=============================================================="
echo "### 排查:WordPress 以外的服务 / 脚本 / 定时任务"
echo "### ⚠ 以下内容需人工核对,发现计划外服务先报甲方,不要自行处理"
echo "--------------------------------------------------------------"
echo ""
echo "---- 非系统自带的自写脚本(/root /home /opt /usr/local 下的 .sh/.py/.php)----"
find /root /home /opt /usr/local/bin /usr/local/sbin \
-maxdepth 4 -type f \( -name "*.sh" -o -name "*.py" -o -name "*.php" \) \
2>/dev/null | head -50
echo ""
echo "---- crontab 中引用的脚本文件是否存在(逐行带时间戳)----"
for user in $(cut -f1 -d: /etc/passwd); do
cron=$(crontab -l -u "$user" 2>/dev/null)
[ -z "$cron" ] && continue
echo "[$user 的 crontab]"
echo "$cron" | grep -v '^#' | grep -v '^$' || echo " (有效行)"
done
echo ""
echo "---- S3 上传相关脚本/配置(对应每日 S3 备份任务,含 docker exec 形式)----"
grep -rlE "aws s3|s3://|aws_access_key" /root /home /etc/cron* /usr/local 2>/dev/null | head -20
if command -v docker >/dev/null 2>&1; then
# 备份脚本可能通过 docker exec 在数据库容器里跑 mysqldump
DB_C=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -iE 'maria|mysql' | head -1)
if [ -n "${DB_C:-}" ]; then
echo "(进数据库容器 $DB_C 排查容器内 crontab 与备份脚本)"
docker exec "$DB_C" sh -c 'cat /etc/crontab 2>/dev/null; ls -la /etc/cron.d/ 2>/dev/null' 2>&1 | head -20
docker exec "$DB_C" sh -c 'ls -la /backup* /scripts* 2>/dev/null; ls -la / 2>/dev/null | head -30' 2>&1
fi
echo "---- 容器内是否有额外 cron 进程 ----"
for cid in $(docker ps -q 2>/dev/null); do
cname=$(docker inspect --format '{{.Name}}' "$cid" 2>/dev/null | sed 's|^/||')
has_cron=$(docker exec "$cid" sh -c 'ps aux 2>/dev/null | grep -i cron | grep -v grep' 2>/dev/null)
[ -n "$has_cron" ] && { echo "[$cname]"; echo "$has_cron"; }
done
echo "(若为空则各容器内无 cron 进程)"
fi
echo ""
echo "---- 非常规监听端口(80/443/22/3306 之外的)----"
ss -tlnp 2>/dev/null | awk 'NR>1 {print $4, $6}' | grep -vE ':(22|80|443|3306)$' || echo " 无"
echo ""
echo "---- 其他网站/虚拟主机(nginx、apache 配置里的 server_name)----"
grep -rE "server_name|ServerName|ServerAlias|root\s+/" \
/etc/nginx/sites-enabled /etc/nginx/conf.d /etc/httpd/conf.d /etc/apache2/sites-enabled \
2>/dev/null | grep -v '^\s*#' | head -40
# ========== 11. 报告结尾 ==========
echo ""
echo "##############################################################"
echo "# 报告结束:$STAMP"
echo "# 保存位置:$OUT"
echo "# 后续动作:检查本报告 -> 确认无计划外服务 -> 提交 A 步汇报"
echo "##############################################################"
} > "$OUT" 2>&1
log "盘点完成,报告已保存: $OUT"
log "文件大小: $(du -h "$OUT" | cut -f1)"
echo "$OUT"