本站之前基本上都是使用site-status+UptimeRobot这样的监控方案,算是常用且成熟的一种方式了,界面也美观,但问题是,它太大了,几个js+css轻松干到500k。放在本地占用带宽是个问题,放在oss上如果被无聊的人拿来做坏事也不是很好玩,于是还是要从源头解决,换用一个更小的方案,即目前的tinystatus方案。

官方提供的demo:here

tinystatus的构成包括三个文件:tinystatuschecks.csvincidents.txt

checks.csv是需要监控的服务列表,格式为:

Command, Expected Code, Status Text, Host to check
  • Command:监控服务类型,支持http ping port三种类型,可指定使用IPv4或IPv6监控,比如http4 http6。
  • Expected Code:返回的正常值,比如http服务返回200时为正常
  • Status Text:服务的名称
  • Host to check:服务的链接/IP/端口

    incidents.txt可以手工写入事件等信息,没有格式要求。

    tinystatus即主程序,一个简单的脚本,通过

tinystatus即主程序,很简单的一个脚本,作用就是通过nc curl ping三个命令实现对checks.csv内进行监控然后通过内置的html模版输出成html文件,因此它只有一个用法:

./tinystatus

或者

./tinystatus > index.html

输出的html大小在3k左右,可以说是非常小了。

原版非常简单,我在原版的基础上稍微美化了下:

#!/usr/bin/env sh

# Configuration variables
TITLE="狼的状态板 3.0"
HEADER="全局状态"
CHECKS_FILE="${1:-checks.csv}" #读取监控列表
INCIDENTS_FILE="${2:-incidents.txt}" #读取公告文件
OUTAGE_RC=false
TIMEOUT=10
USER_AGENT="User-Agent: Mozilla/5.0 (X11; Linux x86_64; Debian) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
TMP_DIR="$(mktemp -d)"

command_exists(){
    if ! command -v "${1}" >/dev/null 2>&1; then
        echo >&2 "错误:未找到组件 ${1} ,请自行安装"
        exit 1
    fi
}

get_element(){
    echo "${2}" | awk -v col="${1}" -F',' '{gsub(/^[ \t]+|[ \t]+$/, "", $col); print $col}'
}

check(){
    check="${1}"
    host="${2}"
    name="${3}"
    expected_rc="${4}"
    id="${5}"

    ipversion="$(echo "${check}" | grep -o '[46]$')"
    case "${check}" in
        http*)
            rc="$(curl -${ipversion}sSkLo /dev/null -H "${USER_AGENT}" -m "${TIMEOUT}" -w "%{http_code}" "${host}" 2> "${TMP_DIR}/${id}.ko.info")"
            if [ -s "${TMP_DIR}/${id}.ko.info" ]; then
                sed -e 's,curl: ([0-9]*) ,,' -i "${TMP_DIR}/${id}.ko.info"
            else
                echo "Status code: ${rc}, expected: ${expected_rc}" > "${TMP_DIR}/${id}.ko.info"
            fi;;
        ping*)
            ping -${ipversion}W "${TIMEOUT}" -c 1 "${host}" >/dev/null 2>&1
            rc=$?
            [ "${rc}" -ne "${expected_rc}" ] && echo 'Host unreachable' > "${TMP_DIR}/${id}.ko.info";;
        port*)
            error="$(nc -${ipversion}w "${TIMEOUT}" -zv ${host} 2>&1)"
            rc=$?
            [ "${rc}" -ne "${expected_rc}" ] && echo "${error}" | sed -e 's,nc: ,,' > "${TMP_DIR}/${id}.ko.info";;
    esac

    # verity status and write files
    if [ "${rc}" -eq "${expected_rc}" ]; then
        echo "${name}" > "${TMP_DIR}/${id}.ok"
    else
        echo "${name}" > "${TMP_DIR}/${id}.ko"
    fi
}

# Verify requirements
command_exists 'curl'
command_exists 'nc'
command_exists 'ping'
mkdir -p "${TMP_DIR}" || exit 1

# Execute checks
id=0
while IFS="$(printf '\n')" read -r line; do
    check="$(get_element 1 "${line}")"
    code="$(get_element 2 "${line}")"
    name="$(get_element 3 "${line}")"
    host="$(get_element 4 "${line}")"
    check "${check}" "${host}" "${name}" "${code}" "${id}" &
    : $((id++))
done < "${CHECKS_FILE}"
wait
OUTAGES_COUNT="$(ls "${TMP_DIR}/"*.ko | wc -l)"

# Generate HTML
cat << EOF
<!DOCTYPE html><html lang="zh_CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><title>${TITLE}</title><style>
body { font-family: segoe ui,Roboto,Oxygen-Sans,Ubuntu,Cantarell,helvetica neue,Verdana,sans-serif;background-image: url('background.png');background-size: cover;background-position: center;background-repeat: no-repeat;}
h1 { margin-top: 30px; }
ul { padding: 0px; }
li { list-style: none; margin-bottom: 2px; padding: 5px; border-bottom: 1px solid #ddd; }
.container { max-width: 600px; width: 100%; margin: 15px auto; background-color: rgba(0, 0, 0, 0.5); padding: 20px; border-radius: 10px; color: white; }
.panel { text-align: center; padding: 10px; border: 0px; border-radius: 5px; }
.failed-bg  { color: white; background-color: #E25D6A; }
.success-bg { color: white; background-color: #52B86A; }
.failed  { color: #E25D6A; }
.success { color: #52B86A; }
.small { font-size: 80%; }
.status { float: right; }
.footer { text-align: center; margin-top: 20px; }
.footer a { text-decoration: none; color: #52B86A; text-shadow: 1px 1px 2px black; }
</style></head>
<body>
<div class='container'>
<h1>${HEADER}</h1>
EOF
if [ "${OUTAGES_COUNT}" -ne 0 ]; then
    echo "<ul><li class='panel failed-bg'>${OUTAGES_COUNT} 个服务中断</li></ul>"
else
    echo "<ul><li class='panel success-bg'>所有服务运行正常</li></ul>"
fi
cat << EOF
<h1>服务</h1>
<ul>
EOF
for file in "${TMP_DIR}/"*.ko; do
    [ -e "${file}" ] || continue
    echo "<li>$(cat "${file}") <span class='small failed'>($(cat "${file}.info"))</span><span class='status failed'>已中断</span></li>"
done
for file in "${TMP_DIR}/"*.ok; do
    [ -e "${file}" ] || continue
    echo "<li>$(cat "${file}") <span class='status success'>正常运作</span></li>"
done
cat << EOF
</ul>
<p class=small> 最后检查时间: $(date +%FT%T%z)</p>
EOF
if [ -f "${INCIDENTS_FILE}" ]; then
    echo '<h1>事件</h1>'
    if [ -s "${INCIDENTS_FILE}" ]; then
        sed 's|^\(.*\)$|<p>\1</p>|' "${INCIDENTS_FILE}"
    else
        echo '<p>没有事件需要通报 ;)</p>'
    fi
fi
cat <<EOF
</div>
<div class='footer'>
    <a href="https://blog.tama.guru">公告板</a> | 
    <a href="https://box.tama.guru">提问箱</a> | 
    <a href="https://share.tama.guru">盘</a>
</div>
</body></html>
EOF

# Cleanup and exit
rm -r "${TMP_DIR}" 2>/dev/null
if "${OUTAGE_RC}"; then
    exit "${OUTAGES_COUNT}"
fi

主要就是汉化了添加了背景,添加了一点css和footer。

需要注意的是nc命令可能不是默认安装的,需要手工安装netcat才能正常运行。

最后可以通过crontab来定时执行,实现自动刷新:

10  *  *  *  *  $tinystatus目录/tinystatus > $网页目录/index.html

又或者可以直接整合进脚本里。

最后修改:2025 年 03 月 29 日
如果觉得我的文章对你有用,请不要赞赏