前置条件
CLIProxyAPI 重启或更新后,usage 统计容易丢失。本文给出一个可落地方案:把统计定时15分钟(东八时区的:00:00开始的每个15分钟间隔)导出,导出到 /data/stats/usage-latest.json,重启时自动导入恢复。并且实现命令保存,手动更新doker来进行重启。
1.修改Command
先修改服务器的command(服务器启动执行的代码,包括重启),替换成
/bin/sh /opt/entrypoint.sh
2.修改Envirnment Variables
添加两个环节变量,YWYDIY_BACKUP_TZ代表时区,YWYDIY_BACKUP_INTERVAL_MINUTES存储的时间间隔。(名字取得复杂点,主要是担心名字冲突的问题,如果想要更简洁,请在代码里修改对应的变量名)
YWYDIY_BACKUP_TZ=Asia/Shanghai
YWYDIY_BACKUP_INTERVAL_MINUTES=15
3.修改和添加Configmaps
修改的启动路径:
/opt/entrypoint.sh
修改的启动代码
展开 / 收起
#!/bin/sh
set -eu
APP="/CLIProxyAPI/CLIProxyAPI --config /data/config.yaml"
API="http://127.0.0.1:8317/v0/management"
BACKUP_DIR="/data/stats"
BACKUP_FILE="${BACKUP_DIR}/usage-latest.json"
KEY="${MANAGEMENT_PASSWORD:-}"
# Two schedule variables only (with safe defaults):
# - YWYDIY_BACKUP_TZ: IANA timezone, e.g. Asia/Shanghai
# - YWYDIY_BACKUP_INTERVAL_MINUTES: <=0 means disabled
# Backward compatible:
# - BACKUP_TZ / BACKUP_INTERVAL_MINUTES are accepted as fallback.
BACKUP_TZ="${YWYDIY_BACKUP_TZ:-${BACKUP_TZ:-Asia/Shanghai}}"
BACKUP_INTERVAL_MINUTES="${YWYDIY_BACKUP_INTERVAL_MINUTES:-${BACKUP_INTERVAL_MINUTES:-10}}"
mkdir -p "${BACKUP_DIR}"
log() {
now="$(TZ="${BACKUP_TZ}" date "+%Y-%m-%d %H:%M:%S %z" 2>/dev/null || date "+%Y-%m-%d %H:%M:%S %z")"
echo "[entrypoint][${now}][tz=${BACKUP_TZ}] $*"
}
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
http_export() {
if has_cmd curl; then
curl -fsS -H "X-Management-Key: ${KEY}" "${API}/usage/export"
elif has_cmd wget; then
wget -qO- --header="X-Management-Key: ${KEY}" "${API}/usage/export"
else
log "error: neither curl nor wget found"
return 1
fi
}
http_import() {
if has_cmd curl; then
curl -fsS -X POST \
-H "X-Management-Key: ${KEY}" \
-H "Content-Type: application/json" \
-d @"${BACKUP_FILE}" \
"${API}/usage/import" >/dev/null
elif has_cmd wget; then
wget -qO- \
--header="X-Management-Key: ${KEY}" \
--header="Content-Type: application/json" \
--post-file="${BACKUP_FILE}" \
"${API}/usage/import" >/dev/null
else
log "error: neither curl nor wget found"
return 1
fi
}
wait_api() {
i=0
while [ "$i" -lt 90 ]; do
if has_cmd curl; then
code="$(curl -s -o /dev/null -w "%{http_code}" "${API}/usage/export" || true)"
elif has_cmd wget; then
if wget -qO- --spider --header="X-Management-Key: ${KEY}" "${API}/usage/export" >/dev/null 2>&1; then
code="200"
else
code="000"
fi
else
code="000"
fi
[ "$code" = "200" ] && return 0
i=$((i + 1))
sleep 1
done
return 1
}
restore_usage() {
[ -n "$KEY" ] || {
log "restore skip: MANAGEMENT_PASSWORD empty"
return 0
}
[ -f "$BACKUP_FILE" ] || {
log "restore skip: backup not found"
return 0
}
if http_import; then
log "restore ok"
else
log "restore failed"
fi
}
backup_usage() {
[ -n "$KEY" ] || {
log "backup skip: MANAGEMENT_PASSWORD empty"
return 0
}
if http_export > "${BACKUP_FILE}.tmp"; then
mv "${BACKUP_FILE}.tmp" "${BACKUP_FILE}"
log "backup ok"
else
rm -f "${BACKUP_FILE}.tmp" || true
log "backup failed"
fi
}
is_positive_int() {
case "$1" in
''|*[!0-9]*) return 1 ;;
*) return 0 ;;
esac
}
tz_now_hms() {
TZ="${BACKUP_TZ}" date "+%H %M %S" 2>/dev/null || date "+%H %M %S"
}
to_dec() {
v="$1"
while [ "${#v}" -gt 1 ] && [ "${v#0}" != "$v" ]; do
v="${v#0}"
done
[ -z "$v" ] && v=0
echo "$v"
}
sleep_with_watch() {
remain="$1"
while [ "$remain" -gt 0 ]; do
if ! kill -0 "$APP_PID" 2>/dev/null; then
return 1
fi
sleep 1
remain=$((remain - 1))
done
return 0
}
run_backup_scheduler() {
if ! is_positive_int "$BACKUP_INTERVAL_MINUTES"; then
log "invalid BACKUP_INTERVAL_MINUTES=${BACKUP_INTERVAL_MINUTES}, fallback to 10"
BACKUP_INTERVAL_MINUTES=10
fi
if [ "$BACKUP_INTERVAL_MINUTES" -le 0 ]; then
log "backup disabled: BACKUP_INTERVAL_MINUTES<=0"
return 0
fi
interval_sec=$((BACKUP_INTERVAL_MINUTES * 60))
log "backup scheduler started: interval=${BACKUP_INTERVAL_MINUTES}m, aligned-from-00:00"
first_cycle=1
while kill -0 "$APP_PID" 2>/dev/null; do
set -- $(tz_now_hms)
hh="$(to_dec "$1")"
mm="$(to_dec "$2")"
ss="$(to_dec "$3")"
since_midnight=$((hh * 3600 + mm * 60 + ss))
mod=$((since_midnight % interval_sec))
if [ "$first_cycle" -eq 1 ] && [ "$mod" -eq 0 ]; then
log "trigger backup now: current time already aligned"
backup_usage
first_cycle=0
continue
fi
wait_sec=$((interval_sec - mod))
[ "$wait_sec" -le 0 ] && wait_sec="$interval_sec"
log "next backup in ${wait_sec}s"
if ! sleep_with_watch "$wait_sec"; then
log "scheduler stopping: app process exited"
break
fi
log "trigger backup: aligned slot reached"
backup_usage
first_cycle=0
done
}
sh -c "$APP" &
APP_PID=$!
if wait_api; then
log "api ready, starting restore"
restore_usage
else
log "api not ready in time, skip restore"
fi
run_backup_scheduler
wait "$APP_PID"
添加的强制保存路径
/opt/force-save.sh
添加的强制保存代码
展开 / 收起
#!/bin/sh
set -eu
API="${YWYDIY_MANAGEMENT_API:-http://127.0.0.1:8317/v0/management}"
BACKUP_DIR="${YWYDIY_BACKUP_DIR:-/data/stats}"
BACKUP_FILE="${YWYDIY_BACKUP_FILE:-${BACKUP_DIR}/usage-latest.json}"
KEY="${MANAGEMENT_PASSWORD:-}"
BACKUP_TZ="${YWYDIY_BACKUP_TZ:-${BACKUP_TZ:-Asia/Shanghai}}"
log() {
now="$(TZ="${BACKUP_TZ}" date "+%Y-%m-%d %H:%M:%S %z" 2>/dev/null || date "+%Y-%m-%d %H:%M:%S %z")"
echo "[force-save][${now}][tz=${BACKUP_TZ}] $*"
}
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
http_export() {
if has_cmd curl; then
curl -fsS -H "X-Management-Key: ${KEY}" "${API}/usage/export"
elif has_cmd wget; then
wget -qO- --header="X-Management-Key: ${KEY}" "${API}/usage/export"
else
log "error: neither curl nor wget found"
return 1
fi
}
main() {
[ -n "$KEY" ] || {
log "force-save failed: MANAGEMENT_PASSWORD empty"
return 1
}
mkdir -p "${BACKUP_DIR}"
tmp="${BACKUP_FILE}.tmp"
if http_export > "${tmp}" && [ -s "${tmp}" ]; then
mv "${tmp}" "${BACKUP_FILE}"
log "force-save ok: ${BACKUP_FILE}"
return 0
fi
rm -f "${tmp}" || true
log "force-save failed: export error or empty output"
return 1
}
main "$@"
4.执行强制更新
终端执行命令(可直接手动触发):
/bin/sh /opt/force-save.sh
最后在系统上update就能更新了
Comments 1 条评论
崇拜尧总大佬😍😍