create-dwy 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/bin/index.js +0 -0
- package/package.json +3 -2
- package/src/searchable-select.js +419 -0
- package/src/sync-all.js +11 -7
- package/src/sync.js +38 -32
- package/templates/ai-tools/CHANGELOG.md +76 -0
- package/templates/ai-tools/VERSION +1 -0
- package/templates/ai-tools/hook-manifests/hooks.json +7 -0
- package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +57 -0
- package/templates/ai-tools/rules/Android/dwy-android-core.md +215 -4
- package/templates/ai-tools/rules/Docker/dwy-docker.md +52 -1
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +50 -1
- package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-design.md +139 -0
- package/templates/ai-tools/rules/HarmonyOS/dwy-harmony-pitfalls.md +72 -0
- package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +7 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +9 -6
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/SKILL.md +511 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-data.md +126 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-network.md +88 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-runtime.md +195 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/security-doc-search.md +179 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_base.sh +39 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_capacity.sh +278 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_db.sh +147 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_docker.sh +230 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_env.sh +64 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_https.sh +60 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_logs.sh +209 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_nginx.sh +100 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_resilience.sh +157 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_secrets.sh +265 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_services.sh +94 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_ssh.sh +56 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/run_all.sh +105 -0
- package/templates/ai-tools/skills//345/205/203/345/267/245/345/205/267/dwy-shared/SKILL.md +22 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +2 -2
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +3 -3
- package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_secrets.sh +1 -1
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/SKILL.md +176 -0
- package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/references/chapter-01-mirrors.md +112 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 环境变量与文件权限检查
|
|
3
|
+
TARGET="$1"; shift
|
|
4
|
+
SSH_OPTS=("$@")
|
|
5
|
+
|
|
6
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
7
|
+
echo "--- 全局搜索 .env 文件位置 (限制深度,跳过 /proc /sys) ---"
|
|
8
|
+
sudo -n find /home /opt /srv /var/www /root /etc -maxdepth 6 -type f \
|
|
9
|
+
\( -name ".env" -o -name ".env.*" -o -name "*.env" \) \
|
|
10
|
+
! -name "*.example" ! -name "*.sample" ! -name "*.template" \
|
|
11
|
+
-printf "%M %u:%g %p (%s bytes)\n" 2>/dev/null \
|
|
12
|
+
|| find /home /opt /srv /var/www -maxdepth 6 -type f \
|
|
13
|
+
\( -name ".env" -o -name ".env.*" \) \
|
|
14
|
+
! -name "*.example" ! -name "*.sample" \
|
|
15
|
+
-printf "%M %u:%g %p (%s bytes)\n" 2>/dev/null \
|
|
16
|
+
|| echo "[!] 无 sudo,部分目录无法扫描"
|
|
17
|
+
|
|
18
|
+
echo ""
|
|
19
|
+
echo "--- nginx 静态根目录是否包含敏感文件 ---"
|
|
20
|
+
NGINX_DUMP=$(sudo -n nginx -T 2>/dev/null || nginx -T 2>/dev/null || true)
|
|
21
|
+
if [[ -n "$NGINX_DUMP" ]]; then
|
|
22
|
+
ROOTS=$(echo "$NGINX_DUMP" | awk '/^[[:space:]]*root /{print $2}' | tr -d ';' | sort -u)
|
|
23
|
+
for r in $ROOTS; do
|
|
24
|
+
echo ">> root = $r"
|
|
25
|
+
[[ -d "$r" ]] || { echo " [!] 目录不存在或不可读"; continue; }
|
|
26
|
+
for s in ".env" ".git" ".DS_Store" "package.json" "composer.json" "wp-config.php"; do
|
|
27
|
+
if [[ -e "$r/$s" ]]; then
|
|
28
|
+
echo " [!] 暴露: $r/$s"
|
|
29
|
+
fi
|
|
30
|
+
done
|
|
31
|
+
done
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo ""
|
|
35
|
+
echo "--- 关键文件权限 ---"
|
|
36
|
+
for f in /etc/shadow /etc/passwd /etc/sudoers /etc/ssh/sshd_config /etc/nginx/nginx.conf; do
|
|
37
|
+
ls -l "$f" 2>/dev/null || echo " [i] $f 不可读"
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
echo ""
|
|
41
|
+
echo "--- 应用进程运行用户 (期望非 root) ---"
|
|
42
|
+
ps -eo user,pid,cmd --sort=user 2>/dev/null | grep -E "(node|python|uvicorn|gunicorn|java|php-fpm|fastapi|uwsgi)" | grep -v grep | head -20
|
|
43
|
+
|
|
44
|
+
echo ""
|
|
45
|
+
echo "--- sudo 免密配置 ---"
|
|
46
|
+
sudo -n cat /etc/sudoers 2>/dev/null | grep -v "^#" | grep -v "^$" | head -20 || echo "[i] 无 sudo 权限读取 /etc/sudoers"
|
|
47
|
+
sudo -n ls /etc/sudoers.d/ 2>/dev/null | head -10
|
|
48
|
+
|
|
49
|
+
echo ""
|
|
50
|
+
echo "--- 全局环境变量泄漏排查 (是否有密码/key 写在 /etc/environment 或 /etc/profile) ---"
|
|
51
|
+
for f in /etc/environment /etc/profile /etc/bash.bashrc; do
|
|
52
|
+
if [[ -r "$f" ]]; then
|
|
53
|
+
grep -iE "password|secret|token|api_key|apikey" "$f" 2>/dev/null \
|
|
54
|
+
| sed -E 's/(password|secret|token|api[_-]?key)([^=]*=)([^[:space:]]+)/\1\2<REDACTED>/gi' \
|
|
55
|
+
|| true
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
echo ""
|
|
60
|
+
echo "--- crontab 中是否有可疑命令 ---"
|
|
61
|
+
sudo -n crontab -l 2>/dev/null | head -20 || crontab -l 2>/dev/null | head -20
|
|
62
|
+
echo "--- /etc/cron.d/ /etc/cron.daily/ ---"
|
|
63
|
+
ls /etc/cron.d/ /etc/cron.daily/ 2>/dev/null | head -20
|
|
64
|
+
REMOTE
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# HTTPS / SSL 证书检查
|
|
3
|
+
TARGET="$1"; shift
|
|
4
|
+
SSH_OPTS=("$@")
|
|
5
|
+
|
|
6
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
7
|
+
echo "--- 检测可能的 HTTPS 站点 (从 nginx 配置) ---"
|
|
8
|
+
DOMAINS=()
|
|
9
|
+
NGINX_DUMP=$(sudo -n nginx -T 2>/dev/null || nginx -T 2>/dev/null || true)
|
|
10
|
+
if [[ -n "$NGINX_DUMP" ]]; then
|
|
11
|
+
while IFS= read -r d; do
|
|
12
|
+
[[ -n "$d" && "$d" != "_" && "$d" != "localhost" ]] && DOMAINS+=("$d")
|
|
13
|
+
done < <(echo "$NGINX_DUMP" | awk '/server_name/ {for(i=2;i<=NF;i++) print $i}' | tr -d ';' | sort -u)
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ ${#DOMAINS[@]} -eq 0 ]]; then
|
|
17
|
+
DOMAINS+=("localhost")
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo "目标域名: ${DOMAINS[*]}"
|
|
21
|
+
|
|
22
|
+
for D in "${DOMAINS[@]}"; do
|
|
23
|
+
echo ""
|
|
24
|
+
echo "===== $D ====="
|
|
25
|
+
|
|
26
|
+
echo "--- HTTPS 可达性 ---"
|
|
27
|
+
HTTPS_CODE=$(curl -k -sS -o /dev/null -w "%{http_code}" --max-time 5 "https://$D" 2>/dev/null || echo "000")
|
|
28
|
+
HTTP_CODE=$(curl -sS -o /dev/null -w "%{http_code} -> %{redirect_url}" --max-time 5 "http://$D" 2>/dev/null || echo "000")
|
|
29
|
+
echo " https://$D -> HTTP $HTTPS_CODE"
|
|
30
|
+
echo " http://$D -> HTTP $HTTP_CODE"
|
|
31
|
+
|
|
32
|
+
echo "--- 证书信息 ---"
|
|
33
|
+
CERT=$(echo | timeout 5 openssl s_client -servername "$D" -connect "${D}:443" -showcerts 2>/dev/null </dev/null)
|
|
34
|
+
if [[ -z "$CERT" ]]; then
|
|
35
|
+
echo " [!] 无法获取证书 (可能未启用 HTTPS 或域名无 DNS)"
|
|
36
|
+
continue
|
|
37
|
+
fi
|
|
38
|
+
echo "$CERT" | openssl x509 -noout -subject -issuer -dates 2>/dev/null
|
|
39
|
+
|
|
40
|
+
echo "--- SAN ---"
|
|
41
|
+
echo "$CERT" | openssl x509 -noout -ext subjectAltName 2>/dev/null | grep -i "DNS:" | head -3
|
|
42
|
+
|
|
43
|
+
echo "--- 剩余天数 ---"
|
|
44
|
+
END=$(echo "$CERT" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
|
|
45
|
+
if [[ -n "$END" ]]; then
|
|
46
|
+
END_TS=$(date -d "$END" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$END" +%s 2>/dev/null)
|
|
47
|
+
NOW_TS=$(date +%s)
|
|
48
|
+
if [[ -n "$END_TS" ]]; then
|
|
49
|
+
DAYS=$(( (END_TS - NOW_TS) / 86400 ))
|
|
50
|
+
echo " 剩余 $DAYS 天 (过期时间: $END)"
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo "--- TLS 协议支持 ---"
|
|
55
|
+
for proto in tls1 tls1_1 tls1_2 tls1_3; do
|
|
56
|
+
R=$(echo | timeout 3 openssl s_client -"$proto" -connect "${D}:443" 2>&1 </dev/null | grep -E "Protocol|handshake failure|wrong version" | head -1)
|
|
57
|
+
printf " %-8s %s\n" "$proto" "$R"
|
|
58
|
+
done
|
|
59
|
+
done
|
|
60
|
+
REMOTE
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 日志大小与防爆检查 (只读)
|
|
3
|
+
#
|
|
4
|
+
# 检查范围:
|
|
5
|
+
# - Docker 单容器 json-log 大小
|
|
6
|
+
# - Docker daemon log-opts 是否生效到具体容器
|
|
7
|
+
# - Nginx access/error log 大小 + logrotate 覆盖
|
|
8
|
+
# - systemd journal 占用 + SystemMaxUse 配置
|
|
9
|
+
# - 应用日志目录 (自动探测 /var/log/<svc>、/opt/*/logs、/home/*/logs)
|
|
10
|
+
# - 总日志占用 vs 根盘剩余, 估算撑天数
|
|
11
|
+
|
|
12
|
+
TARGET="$1"; shift
|
|
13
|
+
SSH_OPTS=("$@")
|
|
14
|
+
|
|
15
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
16
|
+
ROOT_FREE_MB=$(df -BM --output=avail / 2>/dev/null | tail -1 | tr -d ' M')
|
|
17
|
+
ROOT_FREE_MB=${ROOT_FREE_MB:-0}
|
|
18
|
+
NOW_TS=$(date +%s)
|
|
19
|
+
|
|
20
|
+
echo "--- 根盘可用 ---"
|
|
21
|
+
df -h / 2>/dev/null
|
|
22
|
+
echo "可用: ${ROOT_FREE_MB} MB"
|
|
23
|
+
|
|
24
|
+
# ============================================================
|
|
25
|
+
echo ""
|
|
26
|
+
echo "--- Docker 容器 json-log 大小 (单文件 Top 20) ---"
|
|
27
|
+
if command -v docker >/dev/null 2>&1; then
|
|
28
|
+
# 收集 daemon log-opts 用于关联判定
|
|
29
|
+
DAEMON_JSON=$(sudo -n cat /etc/docker/daemon.json 2>/dev/null \
|
|
30
|
+
|| cat /etc/docker/daemon.json 2>/dev/null || echo "")
|
|
31
|
+
DAEMON_MAX_SIZE=$(echo "$DAEMON_JSON" \
|
|
32
|
+
| grep -oE '"max-size"[[:space:]]*:[[:space:]]*"[^"]+"' \
|
|
33
|
+
| head -1 | grep -oE '"[^"]+"$' | tr -d '"')
|
|
34
|
+
echo "daemon.json log-opts max-size: ${DAEMON_MAX_SIZE:-(未配置)}"
|
|
35
|
+
echo ""
|
|
36
|
+
|
|
37
|
+
# 列出所有 json-log: <size> <path>
|
|
38
|
+
LOG_LIST=$(sudo -n find /var/lib/docker/containers -maxdepth 2 -name "*-json.log" \
|
|
39
|
+
-printf '%s %p\n' 2>/dev/null \
|
|
40
|
+
|| find /var/lib/docker/containers -maxdepth 2 -name "*-json.log" \
|
|
41
|
+
-printf '%s %p\n' 2>/dev/null)
|
|
42
|
+
|
|
43
|
+
if [[ -z "$LOG_LIST" ]]; then
|
|
44
|
+
echo "[i] 无法读取 /var/lib/docker/containers (需 sudo) 或无 json-log"
|
|
45
|
+
else
|
|
46
|
+
DOCKER_LOG_TOTAL_BYTES=0
|
|
47
|
+
# 排序+top 20, 反查容器名
|
|
48
|
+
while IFS=' ' read -r size path; do
|
|
49
|
+
[[ -z "$size" ]] && continue
|
|
50
|
+
DOCKER_LOG_TOTAL_BYTES=$(( DOCKER_LOG_TOTAL_BYTES + size ))
|
|
51
|
+
cid=$(basename "$(dirname "$path")")
|
|
52
|
+
cid_short="${cid:0:12}"
|
|
53
|
+
name=$(docker inspect -f '{{.Name}}' "$cid" 2>/dev/null | sed 's|^/||')
|
|
54
|
+
[[ -z "$name" ]] && name="<已删除>"
|
|
55
|
+
size_mb=$(( size / 1024 / 1024 ))
|
|
56
|
+
printf " %-32s %6d MB %s\n" "$name" "$size_mb" "$path"
|
|
57
|
+
|
|
58
|
+
# 单文件大小告警
|
|
59
|
+
if [[ "$size_mb" -ge 1024 ]] && [[ -z "$DAEMON_MAX_SIZE" ]]; then
|
|
60
|
+
echo " [!!!] CRITICAL: 单容器日志 ${size_mb} MB 且 daemon 无 log-opts 大小限制"
|
|
61
|
+
elif [[ "$size_mb" -ge 500 ]]; then
|
|
62
|
+
echo " [!!] HIGH: 单容器日志 ${size_mb} MB"
|
|
63
|
+
fi
|
|
64
|
+
done < <(echo "$LOG_LIST" | sort -rn | head -20)
|
|
65
|
+
|
|
66
|
+
DOCKER_LOG_TOTAL_MB=$(( DOCKER_LOG_TOTAL_BYTES / 1024 / 1024 ))
|
|
67
|
+
echo ""
|
|
68
|
+
echo "Docker json-log 合计: ${DOCKER_LOG_TOTAL_MB} MB"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo ""
|
|
72
|
+
echo "--- 各容器是否覆盖 logging driver (compose level 优先于 daemon level) ---"
|
|
73
|
+
docker ps -q 2>/dev/null | while read c; do
|
|
74
|
+
name=$(docker inspect -f '{{.Name}}' "$c" | sed 's|^/||')
|
|
75
|
+
lt=$(docker inspect -f '{{.HostConfig.LogConfig.Type}}' "$c" 2>/dev/null)
|
|
76
|
+
lc=$(docker inspect -f '{{json .HostConfig.LogConfig.Config}}' "$c" 2>/dev/null)
|
|
77
|
+
[[ -z "$lc" || "$lc" == "null" ]] && lc='{}'
|
|
78
|
+
printf " %-32s driver=%-12s opts=%s\n" "$name" "${lt:-?}" "$lc"
|
|
79
|
+
if [[ "$lt" == "json-file" && "$lc" == "{}" && -z "$DAEMON_MAX_SIZE" ]]; then
|
|
80
|
+
echo " [!!] HIGH: 容器无 logging.options 且 daemon 无 max-size, 日志可无限增长"
|
|
81
|
+
fi
|
|
82
|
+
done
|
|
83
|
+
else
|
|
84
|
+
echo "[i] Docker 未安装,跳过容器日志检查"
|
|
85
|
+
DOCKER_LOG_TOTAL_MB=0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# ============================================================
|
|
89
|
+
echo ""
|
|
90
|
+
echo "--- Nginx 日志 ---"
|
|
91
|
+
if [[ -d /var/log/nginx ]]; then
|
|
92
|
+
ls -lh /var/log/nginx/ 2>/dev/null | head -20
|
|
93
|
+
NGINX_LOG_BYTES=$(sudo -n du -sb /var/log/nginx 2>/dev/null | awk '{print $1}' \
|
|
94
|
+
|| du -sb /var/log/nginx 2>/dev/null | awk '{print $1}')
|
|
95
|
+
NGINX_LOG_MB=$(( ${NGINX_LOG_BYTES:-0} / 1024 / 1024 ))
|
|
96
|
+
echo "合计: ${NGINX_LOG_MB} MB"
|
|
97
|
+
|
|
98
|
+
# access.log / error.log 单文件大小
|
|
99
|
+
for f in /var/log/nginx/access.log /var/log/nginx/error.log; do
|
|
100
|
+
[[ -f "$f" ]] || continue
|
|
101
|
+
sz_mb=$(( $(stat -c %s "$f" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
|
102
|
+
if [[ "$sz_mb" -ge 500 ]]; then
|
|
103
|
+
echo "[!!] HIGH: $f 单文件 ${sz_mb} MB"
|
|
104
|
+
fi
|
|
105
|
+
done
|
|
106
|
+
|
|
107
|
+
# logrotate 覆盖
|
|
108
|
+
if [[ -f /etc/logrotate.d/nginx ]]; then
|
|
109
|
+
echo "[OK] /etc/logrotate.d/nginx 存在"
|
|
110
|
+
else
|
|
111
|
+
echo "[!!] HIGH: nginx 已部署但无 /etc/logrotate.d/nginx, 日志可能无限增长"
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
echo "[i] /var/log/nginx 不存在, 跳过"
|
|
115
|
+
NGINX_LOG_MB=0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# ============================================================
|
|
119
|
+
echo ""
|
|
120
|
+
echo "--- systemd journal 占用 ---"
|
|
121
|
+
if command -v journalctl >/dev/null 2>&1; then
|
|
122
|
+
JOURNAL_USAGE=$(journalctl --disk-usage 2>/dev/null | grep -oE '[0-9.]+[KMG]B?' | tail -1)
|
|
123
|
+
echo "journalctl --disk-usage: ${JOURNAL_USAGE:-?}"
|
|
124
|
+
# 转 MB (粗略)
|
|
125
|
+
case "$JOURNAL_USAGE" in
|
|
126
|
+
*G*) JOURNAL_MB=$(echo "$JOURNAL_USAGE" | sed 's/[GBb]//g' | awk '{print int($1*1024)}') ;;
|
|
127
|
+
*M*) JOURNAL_MB=$(echo "$JOURNAL_USAGE" | sed 's/[MBb]//g' | awk '{print int($1)}') ;;
|
|
128
|
+
*K*) JOURNAL_MB=0 ;;
|
|
129
|
+
*) JOURNAL_MB=0 ;;
|
|
130
|
+
esac
|
|
131
|
+
|
|
132
|
+
# SystemMaxUse 配置
|
|
133
|
+
SMU=$(grep -E "^[[:space:]]*SystemMaxUse" /etc/systemd/journald.conf 2>/dev/null \
|
|
134
|
+
| head -1 | awk -F= '{gsub(/[ \t]/,"",$2); print $2}')
|
|
135
|
+
echo "SystemMaxUse: ${SMU:-(未配置, 默认按磁盘 10% 自动控)}"
|
|
136
|
+
|
|
137
|
+
if [[ "$JOURNAL_MB" -ge 2048 && -z "$SMU" ]]; then
|
|
138
|
+
echo "[!!] HIGH: journal 已 ${JOURNAL_MB} MB 且未配 SystemMaxUse"
|
|
139
|
+
fi
|
|
140
|
+
else
|
|
141
|
+
JOURNAL_MB=0
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# ============================================================
|
|
145
|
+
echo ""
|
|
146
|
+
echo "--- 应用日志目录 (自动探测 Top 5) ---"
|
|
147
|
+
APP_LOG_TOTAL_MB=0
|
|
148
|
+
# 优先 sudo, 失败再退化; 不要用 OR fallback (du 退出码可能因部分不可读而非零, 会被双重执行)
|
|
149
|
+
APP_LOG_LIST=$(sudo -n du -smc /var/log/*/ /opt/*/logs /opt/*/log /home/*/logs /srv/*/logs 2>/dev/null \
|
|
150
|
+
| grep -v 'total$' | sort -rn | head -10)
|
|
151
|
+
if [[ -z "$APP_LOG_LIST" ]]; then
|
|
152
|
+
APP_LOG_LIST=$(du -smc /var/log/*/ /opt/*/logs /opt/*/log /home/*/logs /srv/*/logs 2>/dev/null \
|
|
153
|
+
| grep -v 'total$' | sort -rn | head -10)
|
|
154
|
+
fi
|
|
155
|
+
echo "$APP_LOG_LIST"
|
|
156
|
+
APP_LOG_TOTAL_MB=$(echo "$APP_LOG_LIST" | awk '{s+=$1} END{print s+0}')
|
|
157
|
+
|
|
158
|
+
# ============================================================
|
|
159
|
+
echo ""
|
|
160
|
+
echo "--- 日志总占用 vs 根盘剩余 (撑天数粗估) ---"
|
|
161
|
+
TOTAL_LOG_MB=$(( DOCKER_LOG_TOTAL_MB + NGINX_LOG_MB + JOURNAL_MB + APP_LOG_TOTAL_MB ))
|
|
162
|
+
echo "Docker json-log: ${DOCKER_LOG_TOTAL_MB} MB"
|
|
163
|
+
echo "Nginx 日志: ${NGINX_LOG_MB} MB"
|
|
164
|
+
echo "systemd journal: ${JOURNAL_MB} MB"
|
|
165
|
+
echo "应用日志合计: ${APP_LOG_TOTAL_MB} MB"
|
|
166
|
+
echo "日志合计: ${TOTAL_LOG_MB} MB"
|
|
167
|
+
echo "根盘可用: ${ROOT_FREE_MB} MB"
|
|
168
|
+
|
|
169
|
+
# 撑天数估算: 取所有 docker json-log 的 (size / 容器存活天数) 加总作为 daily growth
|
|
170
|
+
GROWTH_MB=0
|
|
171
|
+
if command -v docker >/dev/null 2>&1; then
|
|
172
|
+
for c in $(docker ps -q 2>/dev/null); do
|
|
173
|
+
cid_full=$(docker inspect -f '{{.Id}}' "$c" 2>/dev/null)
|
|
174
|
+
log_path="/var/lib/docker/containers/${cid_full}/${cid_full}-json.log"
|
|
175
|
+
sz=$(sudo -n stat -c %s "$log_path" 2>/dev/null || stat -c %s "$log_path" 2>/dev/null || echo 0)
|
|
176
|
+
started=$(docker inspect -f '{{.State.StartedAt}}' "$c" 2>/dev/null)
|
|
177
|
+
started_ts=$(date -d "$started" +%s 2>/dev/null || echo 0)
|
|
178
|
+
if [[ "$started_ts" -gt 0 && "$sz" -gt 0 ]]; then
|
|
179
|
+
age_sec=$(( NOW_TS - started_ts ))
|
|
180
|
+
[[ "$age_sec" -lt 60 ]] && age_sec=60 # 防除零, 至少按 1 分钟算
|
|
181
|
+
daily_bytes=$(( sz * 86400 / age_sec ))
|
|
182
|
+
daily_mb=$(( daily_bytes / 1024 / 1024 ))
|
|
183
|
+
GROWTH_MB=$(( GROWTH_MB + daily_mb ))
|
|
184
|
+
fi
|
|
185
|
+
done
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
echo "估算 Docker 日志日均增量: ${GROWTH_MB} MB/day (基于容器存活时长)"
|
|
189
|
+
|
|
190
|
+
if [[ "$GROWTH_MB" -gt 0 && "$ROOT_FREE_MB" -gt 0 ]]; then
|
|
191
|
+
DAYS=$(( ROOT_FREE_MB / GROWTH_MB ))
|
|
192
|
+
echo "按当前增速预计可撑: ${DAYS} 天 (粗估, 仅算 docker json-log)"
|
|
193
|
+
if [[ "$DAYS" -lt 30 ]]; then echo "[!!!] CRITICAL: 不足 30 天, 必须立即配 log-opts 或扩盘"
|
|
194
|
+
elif [[ "$DAYS" -lt 90 ]]; then echo "[!!] HIGH: 不足 90 天, 建议尽快补 log-opts max-size"
|
|
195
|
+
else echo "[OK] 撑天数 >= 90 天"
|
|
196
|
+
fi
|
|
197
|
+
else
|
|
198
|
+
echo "[i] 无法估算日均增量 (容器刚起或无日志)"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# ============================================================
|
|
202
|
+
echo ""
|
|
203
|
+
echo "--- 关键服务 logrotate 规则一览 (只读复述) ---"
|
|
204
|
+
for app in nginx postgresql redis docker rsyslog; do
|
|
205
|
+
if [[ -f "/etc/logrotate.d/$app" ]]; then
|
|
206
|
+
echo " [OK] /etc/logrotate.d/$app"
|
|
207
|
+
fi
|
|
208
|
+
done
|
|
209
|
+
REMOTE
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Nginx 配置检查 (只读)
|
|
3
|
+
TARGET="$1"; shift
|
|
4
|
+
SSH_OPTS=("$@")
|
|
5
|
+
|
|
6
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
7
|
+
if ! command -v nginx >/dev/null 2>&1; then
|
|
8
|
+
echo "[i] nginx 未安装,跳过"
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
echo "--- nginx version ---"
|
|
13
|
+
nginx -v 2>&1
|
|
14
|
+
NGINX_VERSION=$(nginx -v 2>&1 | sed -n 's#.*nginx/\([^ ]*\).*#\1#p' | head -1)
|
|
15
|
+
echo "nginx_version_normalized=${NGINX_VERSION:-unknown}"
|
|
16
|
+
echo "official_doc_headers=https://nginx.org/en/docs/http/ngx_http_headers_module.html"
|
|
17
|
+
echo "official_doc_ssl=https://nginx.org/en/docs/http/ngx_http_ssl_module.html"
|
|
18
|
+
if [[ -n "$NGINX_VERSION" ]]; then
|
|
19
|
+
echo "search_hint_1=nginx ${NGINX_VERSION} security advisory"
|
|
20
|
+
echo "search_hint_2=nginx ${NGINX_VERSION} CVE"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
echo ""
|
|
24
|
+
echo "--- nginx 进程运行用户 ---"
|
|
25
|
+
ps -eo user,pid,cmd | grep "[n]ginx: master" | head -1
|
|
26
|
+
|
|
27
|
+
echo ""
|
|
28
|
+
echo "--- nginx -T (effective config, 需 sudo 或 nginx 用户可读) ---"
|
|
29
|
+
NGINX_DUMP=$(sudo -n nginx -T 2>/dev/null || nginx -T 2>/dev/null || true)
|
|
30
|
+
if [[ -z "$NGINX_DUMP" ]]; then
|
|
31
|
+
echo "[!] 无法 dump nginx 配置,改读 /etc/nginx/"
|
|
32
|
+
ls -la /etc/nginx/ 2>/dev/null
|
|
33
|
+
echo "--- /etc/nginx/nginx.conf (head) ---"
|
|
34
|
+
head -80 /etc/nginx/nginx.conf 2>/dev/null
|
|
35
|
+
echo "--- sites-enabled / conf.d ---"
|
|
36
|
+
for d in /etc/nginx/sites-enabled /etc/nginx/conf.d; do
|
|
37
|
+
[[ -d "$d" ]] && ls -la "$d" 2>/dev/null
|
|
38
|
+
done
|
|
39
|
+
else
|
|
40
|
+
echo "$NGINX_DUMP" | grep -E "^# configuration file" | head -20
|
|
41
|
+
echo ""
|
|
42
|
+
echo "--- KEY DIRECTIVES (filtered from nginx -T) ---"
|
|
43
|
+
echo "$NGINX_DUMP" | grep -Ei "^[[:space:]]*(listen|server_name|return|rewrite|ssl_protocols|ssl_ciphers|ssl_prefer_server_ciphers|ssl_certificate|ssl_certificate_key|server_tokens|access_log|error_log|client_max_body_size|client_body_timeout|add_header|autoindex|location|root|alias|proxy_pass)" \
|
|
44
|
+
| grep -v "^[[:space:]]*#" | head -200
|
|
45
|
+
|
|
46
|
+
echo ""
|
|
47
|
+
echo "--- HSTS / Security Headers 检查 ---"
|
|
48
|
+
echo "$NGINX_DUMP" | grep -i "Strict-Transport-Security\|X-Frame-Options\|X-Content-Type-Options\|Content-Security-Policy\|Referrer-Policy" || echo "[!] 未找到任何安全 header"
|
|
49
|
+
|
|
50
|
+
echo ""
|
|
51
|
+
echo "--- HTTP→HTTPS 跳转检查 ---"
|
|
52
|
+
echo "$NGINX_DUMP" | awk '/listen[[:space:]]+80/,/}/' | grep -i "return\|rewrite" | head -10 || echo "[!] 80 端口可能未配置强制跳转"
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
echo "--- server_tokens ---"
|
|
56
|
+
echo "$NGINX_DUMP" | grep -E "^[[:space:]]*server_tokens" || echo "[!] server_tokens 未显式配置 (默认 on,会暴露版本)"
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo "--- access_log / error_log ---"
|
|
60
|
+
echo "$NGINX_DUMP" | grep -E "^[[:space:]]*(access_log|error_log)" | head -10
|
|
61
|
+
|
|
62
|
+
echo ""
|
|
63
|
+
echo "--- client_max_body_size (业务非上传 ≤ 10m) ---"
|
|
64
|
+
echo "$NGINX_DUMP" | grep -E "^[[:space:]]*client_max_body_size" || echo "[!] 未显式配置 client_max_body_size"
|
|
65
|
+
|
|
66
|
+
echo ""
|
|
67
|
+
echo "--- 限流配置 (limit_req / limit_conn 防 CC / 暴力请求) ---"
|
|
68
|
+
ZONE_DEFS=$(echo "$NGINX_DUMP" | grep -E "^[[:space:]]*limit_(req|conn)_zone" || true)
|
|
69
|
+
if [[ -z "$ZONE_DEFS" ]]; then
|
|
70
|
+
echo "[!] CRITICAL: 未发现任何 limit_req_zone / limit_conn_zone 定义,无 CC 攻击防护"
|
|
71
|
+
else
|
|
72
|
+
echo "已定义 zone:"
|
|
73
|
+
echo "$ZONE_DEFS"
|
|
74
|
+
echo ""
|
|
75
|
+
echo "应用位置 (limit_req / limit_conn):"
|
|
76
|
+
echo "$NGINX_DUMP" | grep -E "^[[:space:]]*limit_(req|conn)[[:space:]]" || echo "[!] zone 已定义但未在 server/location 中应用"
|
|
77
|
+
echo ""
|
|
78
|
+
LIMIT_STATUS=$(echo "$NGINX_DUMP" | grep -E "^[[:space:]]*limit_req_status" | head -1)
|
|
79
|
+
echo "limit_req_status: ${LIMIT_STATUS:-(默认 503,建议 429)}"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo ""
|
|
83
|
+
echo "--- 慢速攻击防护 (slowloris) ---"
|
|
84
|
+
for d in client_body_timeout client_header_timeout send_timeout keepalive_timeout; do
|
|
85
|
+
val=$(echo "$NGINX_DUMP" | grep -E "^[[:space:]]*${d}[[:space:]]" | head -1)
|
|
86
|
+
echo "${d}: ${val:-(未显式配置,使用默认)}"
|
|
87
|
+
done
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
echo ""
|
|
91
|
+
echo "--- nginx 配置语法检查 ---"
|
|
92
|
+
sudo -n nginx -t 2>&1 || nginx -t 2>&1 || echo "[i] 无法执行 nginx -t"
|
|
93
|
+
|
|
94
|
+
echo ""
|
|
95
|
+
echo "--- 危险路径暴露快测 (本机回环) ---"
|
|
96
|
+
for path in "/.env" "/.git/HEAD" "/.git/config" "/admin" "/phpmyadmin" "/.DS_Store"; do
|
|
97
|
+
code=$(curl -k -s -o /dev/null -w "%{http_code}" --max-time 3 "https://localhost${path}" 2>/dev/null || echo "000")
|
|
98
|
+
printf " %-20s -> https://localhost HTTP %s\n" "$path" "$code"
|
|
99
|
+
done
|
|
100
|
+
REMOTE
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 自愈能力 / 资源耗尽防护检查 (只读)
|
|
3
|
+
#
|
|
4
|
+
# 检查范围:
|
|
5
|
+
# B 类: 系统服务开机自启 (sshd / nginx / docker / postgres / redis / fail2ban / 自定义)
|
|
6
|
+
# D 类: 资源耗尽防护 (swap / ulimit / logrotate / 磁盘使用率 / 应用进程内存上限)
|
|
7
|
+
#
|
|
8
|
+
# 服务器意外重启后,这些配置决定了服务能否自动恢复;资源耗尽场景决定了
|
|
9
|
+
# 服务能否在异常下保住基本运行而不被全局拖垮。
|
|
10
|
+
|
|
11
|
+
TARGET="$1"; shift
|
|
12
|
+
SSH_OPTS=("$@")
|
|
13
|
+
|
|
14
|
+
ssh "${SSH_OPTS[@]}" "${TARGET}" bash -s <<'REMOTE'
|
|
15
|
+
echo "########## B. 系统服务开机自启 ##########"
|
|
16
|
+
|
|
17
|
+
# 默认必检清单
|
|
18
|
+
DEFAULT_SERVICES=(sshd ssh nginx docker docker.socket postgresql postgres redis redis-server fail2ban supervisor crond cron)
|
|
19
|
+
|
|
20
|
+
echo "--- 默认必检清单 ---"
|
|
21
|
+
for svc in "${DEFAULT_SERVICES[@]}"; do
|
|
22
|
+
# systemctl status 区分: not-found / disabled / enabled / static / masked
|
|
23
|
+
enabled=$(systemctl is-enabled "$svc" 2>/dev/null || echo "n/a")
|
|
24
|
+
active=$(systemctl is-active "$svc" 2>/dev/null || echo "n/a")
|
|
25
|
+
if [[ "$enabled" == "n/a" && "$active" == "n/a" ]]; then
|
|
26
|
+
continue # 该服务未安装,跳过
|
|
27
|
+
fi
|
|
28
|
+
case "$enabled" in
|
|
29
|
+
enabled|enabled-runtime|alias|static)
|
|
30
|
+
icon="[OK]" ;;
|
|
31
|
+
disabled)
|
|
32
|
+
icon="[!!!] CRITICAL: 已部署但未开机自启,服务器重启后不会启动" ;;
|
|
33
|
+
masked)
|
|
34
|
+
icon="[!!!] CRITICAL: 服务被 mask,无法启动" ;;
|
|
35
|
+
*)
|
|
36
|
+
icon="[?] enabled=$enabled" ;;
|
|
37
|
+
esac
|
|
38
|
+
printf " %-20s active=%-10s enabled=%-12s %s\n" "$svc" "$active" "$enabled" "$icon"
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
echo ""
|
|
42
|
+
echo "--- 自动探测: 当前 enabled 的所有 unit (供人工核对应用主进程是否在内) ---"
|
|
43
|
+
systemctl list-unit-files --type=service --state=enabled 2>/dev/null \
|
|
44
|
+
| awk 'NR>1 && $1 != "" {print " " $1}' \
|
|
45
|
+
| head -40 || echo "[i] 无法列出 unit-files"
|
|
46
|
+
|
|
47
|
+
echo ""
|
|
48
|
+
echo "--- 自动探测: 当前 running 但 disabled 的服务 (重启后会消失) ---"
|
|
49
|
+
running_services=$(systemctl list-units --type=service --state=running --no-pager --no-legend 2>/dev/null | awk '{print $1}')
|
|
50
|
+
for svc in $running_services; do
|
|
51
|
+
enabled=$(systemctl is-enabled "$svc" 2>/dev/null)
|
|
52
|
+
if [[ "$enabled" == "disabled" ]]; then
|
|
53
|
+
echo " [!!] $svc 正在运行但 disabled,重启后会丢失"
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo "########## D. 资源耗尽防护 ##########"
|
|
60
|
+
|
|
61
|
+
echo "--- 内存与 swap ---"
|
|
62
|
+
free -h 2>/dev/null || vmstat -s 2>/dev/null | head -10
|
|
63
|
+
SWAP_SIZE=$(awk '/SwapTotal/ {print $2}' /proc/meminfo 2>/dev/null)
|
|
64
|
+
if [[ -z "$SWAP_SIZE" || "$SWAP_SIZE" == "0" ]]; then
|
|
65
|
+
echo "[!] MEDIUM: 未配置 swap,内存吃紧时直接 OOM kill"
|
|
66
|
+
echo " 建议: 至少配置 1-2GB swap (fallocate / dd + mkswap + swapon + /etc/fstab)"
|
|
67
|
+
else
|
|
68
|
+
echo "[OK] swap 已配置: $((SWAP_SIZE / 1024)) MB"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo ""
|
|
72
|
+
echo "--- 根分区使用率 ---"
|
|
73
|
+
df -h / 2>/dev/null
|
|
74
|
+
ROOT_USE=$(df / --output=pcent 2>/dev/null | tail -1 | tr -d ' %')
|
|
75
|
+
if [[ -n "$ROOT_USE" ]]; then
|
|
76
|
+
if [[ "$ROOT_USE" -ge 90 ]]; then
|
|
77
|
+
echo "[!!!] CRITICAL: 根分区使用率 ${ROOT_USE}%, 即将写满"
|
|
78
|
+
elif [[ "$ROOT_USE" -ge 80 ]]; then
|
|
79
|
+
echo "[!] MEDIUM: 根分区使用率 ${ROOT_USE}%, 建议清理"
|
|
80
|
+
else
|
|
81
|
+
echo "[OK] 根分区使用率 ${ROOT_USE}%"
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
echo "--- /var/log 与 /var/lib/docker 大小 ---"
|
|
87
|
+
sudo -n du -sh /var/log /var/lib/docker /var/lib/postgresql 2>/dev/null \
|
|
88
|
+
|| du -sh /var/log /var/lib/docker /var/lib/postgresql 2>/dev/null \
|
|
89
|
+
|| echo "[i] 部分目录不可读"
|
|
90
|
+
|
|
91
|
+
echo ""
|
|
92
|
+
echo "--- logrotate 配置 ---"
|
|
93
|
+
if [[ -f /etc/logrotate.conf ]]; then
|
|
94
|
+
echo "[OK] /etc/logrotate.conf 存在"
|
|
95
|
+
ls /etc/logrotate.d/ 2>/dev/null | head -20 | sed 's|^| |'
|
|
96
|
+
else
|
|
97
|
+
echo "[!] HIGH: /etc/logrotate.conf 不存在,日志可能无限增长"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# 关键服务是否有 logrotate 规则
|
|
101
|
+
echo ""
|
|
102
|
+
echo "--- 关键服务 logrotate 规则 ---"
|
|
103
|
+
for app in nginx postgresql redis docker rsyslog; do
|
|
104
|
+
if [[ -f "/etc/logrotate.d/$app" ]]; then
|
|
105
|
+
echo " [OK] /etc/logrotate.d/$app"
|
|
106
|
+
else
|
|
107
|
+
if systemctl is-enabled "$app" >/dev/null 2>&1 || systemctl is-enabled "${app}-server" >/dev/null 2>&1; then
|
|
108
|
+
echo " [!] $app 已部署但无 logrotate 规则"
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
echo ""
|
|
114
|
+
echo "--- 文件描述符上限 (system + 关键服务) ---"
|
|
115
|
+
echo " 系统 fs.file-max: $(sysctl -n fs.file-max 2>/dev/null)"
|
|
116
|
+
echo " 系统 fs.nr_open: $(sysctl -n fs.nr_open 2>/dev/null)"
|
|
117
|
+
for app in nginx postgres redis-server; do
|
|
118
|
+
pid=$(pgrep -f "[${app:0:1}]${app:1}" | head -1)
|
|
119
|
+
if [[ -n "$pid" ]]; then
|
|
120
|
+
soft=$(awk '/Max open files/ {print $4}' /proc/$pid/limits 2>/dev/null)
|
|
121
|
+
hard=$(awk '/Max open files/ {print $5}' /proc/$pid/limits 2>/dev/null)
|
|
122
|
+
echo " $app (pid $pid): soft=$soft hard=$hard"
|
|
123
|
+
if [[ -n "$soft" && "$soft" -lt 4096 ]]; then
|
|
124
|
+
echo " [!] MEDIUM: ulimit -n 偏低 ($soft),高并发场景可能耗尽"
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
echo ""
|
|
130
|
+
echo "--- /etc/security/limits.conf 自定义条目 ---"
|
|
131
|
+
sudo -n grep -vE "^[[:space:]]*#|^[[:space:]]*$" /etc/security/limits.conf 2>/dev/null \
|
|
132
|
+
|| grep -vE "^[[:space:]]*#|^[[:space:]]*$" /etc/security/limits.conf 2>/dev/null \
|
|
133
|
+
|| echo "[i] limits.conf 不可读"
|
|
134
|
+
ls /etc/security/limits.d/ 2>/dev/null | sed 's|^| /etc/security/limits.d/|'
|
|
135
|
+
|
|
136
|
+
echo ""
|
|
137
|
+
echo "--- 内存压力指标 (PSI) ---"
|
|
138
|
+
if [[ -r /proc/pressure/memory ]]; then
|
|
139
|
+
cat /proc/pressure/memory
|
|
140
|
+
else
|
|
141
|
+
echo "[i] PSI 不可用 (内核 < 4.20 或未启用)"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
echo ""
|
|
145
|
+
echo "--- 关键容器内存上限 (避免容器吃掉宿主机) ---"
|
|
146
|
+
if command -v docker >/dev/null 2>&1; then
|
|
147
|
+
docker ps -q 2>/dev/null | while read c; do
|
|
148
|
+
name=$(docker inspect -f '{{.Name}}' "$c" 2>/dev/null | sed 's|^/||')
|
|
149
|
+
mem=$(docker inspect -f '{{.HostConfig.Memory}}' "$c" 2>/dev/null)
|
|
150
|
+
if [[ "$mem" == "0" ]]; then
|
|
151
|
+
echo " [!] $name 无内存上限,异常时可能耗尽宿主机"
|
|
152
|
+
else
|
|
153
|
+
echo " [OK] $name Memory=$((mem / 1024 / 1024)) MB"
|
|
154
|
+
fi
|
|
155
|
+
done
|
|
156
|
+
fi
|
|
157
|
+
REMOTE
|