WWW.YOUINFO.SITE
标签聚合 优雅

/tag/优雅

LinuxDo 最新话题 · 2026-06-11 17:33:51+08:00 · tech

NewAPI 的日志目前是单表存储,已经看到太多佬友因为日志撑爆磁盘了,下面给出一种解决方式,可能不完全兼容,但是个人测试下来没有什么问题 与 NewAPI 版本无关,基本所有版本都可以用,我用的版本是 1.0.0-rc.10 自带的日志删除 最常用的是使用 NewAPI 自带的 “清理历史日志” 来进行删除 // 代码逻辑如下 // 直接按照时间戳删除 LOG_DB.Where("created_at < ?", targetTimestamp).Limit(limit).Delete(&Log{}) 这里有两个问题: Delete 的效率不高,或者说在数据量过大的情况下使用 Delete 带来的 IO 可能成为灾难 Delete 之后磁盘空间并不会回收,需要手动回收 通过数据库分片解决日志删除 适用于 MySQL,如果您是其他的 DB 如 Postgres / SQLite,将下面的内容提供给 AI 相信也会有对应的解决方案 分片的核心逻辑是,将日志拆分到对应的日期上,比如分片 logs_20260601 只存储当天的数据,在超出需要删除的日期后,比如 1 个月后的 2026/07/01,直接删除 logs_20260601 分片,分片占用的空间会立刻释放,对应的日志数据也会直接删除 不过这里要提示您,所有的 DB 变更都可能引入风险,请您在执行前通过 mysqldump 或者类似工具,导出完整的数据结构和数据内容,确保执行出现异常可以随时回滚 执行下面的步骤前,建议停机,否则可能导致数据不完整 ,如果数据完整性不在考虑范围内,也可以在线更新 Step 1 检查数据符合预期 (Read) 执行下面的查询语句,确保 count 为 0,即所有的日志的创建时间不为空 SELECT COUNT(*) AS null_created_at_count FROM logs WHERE created_at IS NULL; Step 2 创建临时表 (Write) CREATE TABLE logs_new LIKE logs; Step 3 添加准备分区语句 (Read) 通过下面的 SQL,可以获得一个建表 SQL,对 DB 无任何副作用,可以放心执行 下面的 @start_date 和 @end_date 两行,可以按照您的实际数据存储情况调整,下面的配置为创建 180 天前到 7 天后的分区,如果您的日志已经回收或删除过了,可以考虑缩减分区数量 SET time_zone = '+08:00'; 这里可以按照您的需求调整为具体的时区 分区必须提前创建,否则插入数据会有问题 ,所以您至少需要创建 7 天后的备用 SET SESSION group_concat_max_len = 1024 * 1024; SET time_zone = '+08:00'; SET @start_date = DATE_SUB(CURDATE(), INTERVAL 180 DAY); SET @end_date = DATE_ADD(CURDATE(), INTERVAL 7 DAY); WITH RECURSIVE dates AS ( SELECT @start_date AS d UNION ALL SELECT DATE_ADD(d, INTERVAL 1 DAY) FROM dates WHERE d < @end_date ) SELECT GROUP_CONCAT( CONCAT( ' PARTITION p', DATE_FORMAT(d, '%Y%m%d'), ' VALUES LESS THAN (', UNIX_TIMESTAMP(DATE_ADD(d, INTERVAL 1 DAY)), ')' ) ORDER BY d SEPARATOR ',\n' ) INTO @parts FROM dates; SET @sql = CONCAT( 'ALTER TABLE logs_new MODIFY `created_at` bigint NOT NULL, DROP PRIMARY KEY, DROP INDEX `idx_created_at_id`, ADD PRIMARY KEY (`id`, `created_at`) PARTITION BY RANGE (`created_at`) ( ', @parts, ', PARTITION pmax VALUES LESS THAN MAXVALUE )' ); SELECT @sql; Step 4 执行分区语句 (Write) 执行上一步输出的 SQL,会将新的 logs_new 表调整为分区表 Step 5 导入旧的数据 (Write) 取决于您的数据量,这一步可能会花费一些时间 INSERT INTO logs_new SELECT * FROM logs; Step 6 校验数据已经导入完成 (Read) 如果您是停机更新,确保两条 SQL 输出的内容是一致的 如果您是在线更新,确保数据接近或一致 SELECT "logs" as `table`, COUNT(*), MIN(created_at), MAX(created_at) FROM logs UNION SELECT "logs_new" as `table`, COUNT(*), MIN(created_at), MAX(created_at) FROM logs_new; Step 7 确认分区 (Read) 下面的 SQL 会打印新表的所有分区,以及每个分区的数据量 SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs_new' ORDER BY PARTITION_ORDINAL_POSITION; Step 8 切表 (Write) 切换流量到新表,执行完成后请检查 NewAPI 各组件各页面是否能正常工作,特别是依赖日志的内容 RENAME TABLE logs TO logs_old, logs_new TO logs; Step 9 创建自动回收创建/分区任务 (Write) MySQL 不会自动创建或者删除分区,需要您创建定时任务来实现,下面会给出创建/删除分区的任务,您可以按需添加 Step 9.1 开启 MySQL Event Scheduler 在数据库中执行下面的命令,通常需要 root 用户,无需重启 SET GLOBAL event_scheduler = ON; SHOW VARIABLES LIKE 'event_scheduler'; 在配置文件的 mysqld 章节,增加下面的配置,无需重启 这一步是为了保证,即使后面重启数据库,Event Scheduler 仍然会是开启状态 [mysqld] event_scheduler=ON Step 9.2 创建新建分区任务 下面的两个 SQL 都需要执行 第一个 SQL 中的 SET time_zone = '+08:00'; 可按需修改为您的时区 第二个 SQL 中的时间您可以修改为适用于您的服务的时间,目前设置的是每天的 02:00,用于控制定时触发的时机 DELIMITER $$ DROP PROCEDURE IF EXISTS sp_logs_create_future_partitions$$ CREATE PROCEDURE sp_logs_create_future_partitions() BEGIN DECLARE v_i INT DEFAULT 0; DECLARE v_d DATE; DECLARE v_partition_name VARCHAR(32); DECLARE v_less_than BIGINT; DECLARE v_exists INT DEFAULT 0; DECLARE v_sql TEXT; SET time_zone = '+08:00'; WHILE v_i <= 7 DO SET v_d = DATE_ADD(CURDATE(), INTERVAL v_i DAY); SET v_partition_name = CONCAT('p', DATE_FORMAT(v_d, '%Y%m%d')); SET v_less_than = UNIX_TIMESTAMP(DATE_ADD(v_d, INTERVAL 1 DAY)); SELECT COUNT(*) INTO v_exists FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' AND PARTITION_NAME = v_partition_name; IF v_exists = 0 THEN SET v_sql = CONCAT( 'ALTER TABLE logs REORGANIZE PARTITION pmax INTO (', 'PARTITION ', v_partition_name, ' VALUES LESS THAN (', v_less_than, '), ', 'PARTITION pmax VALUES LESS THAN MAXVALUE', ')' ); SET @sql = v_sql; PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; SET v_i = v_i + 1; END WHILE; END$$ DELIMITER ; DROP EVENT IF EXISTS ev_logs_create_future_partitions; CREATE EVENT ev_logs_create_future_partitions ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE, '02:00:00') DO CALL sp_logs_create_future_partitions(); Step 9.3 创建删除分区任务 (可选) 这个是用于替代 NewAPI 自带的删除数据任务,如果您有需要可以配置这里的自动删除 第一个 SQL 中的 INTERVAL 180 DAY 表示删除 180 天之前的数据,可以按需修改, SET time_zone = '+08:00'; 也可按需修改为您的时区 第二个 SQL 中的时间您可以修改为适用于您的服务的时间,目前设置的是每天的 03:00,用于控制定时触发的时机 DELIMITER $$ DROP PROCEDURE IF EXISTS sp_logs_drop_old_partitions$$ CREATE PROCEDURE sp_logs_drop_old_partitions() BEGIN DECLARE v_cutoff_date DATE; DECLARE v_cutoff_ts BIGINT; DECLARE v_drop_partitions TEXT; DECLARE v_sql TEXT; SET time_zone = '+08:00'; SET v_cutoff_date = DATE_SUB(CURDATE(), INTERVAL 180 DAY); SET v_cutoff_ts = UNIX_TIMESTAMP(v_cutoff_date); SELECT GROUP_CONCAT(PARTITION_NAME ORDER BY PARTITION_ORDINAL_POSITION) INTO v_drop_partitions FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' AND PARTITION_NAME REGEXP '^p[0-9]{8}$' AND CAST(PARTITION_DESCRIPTION AS UNSIGNED) <= v_cutoff_ts; IF v_drop_partitions IS NOT NULL AND v_drop_partitions <> '' THEN SET v_sql = CONCAT( 'ALTER TABLE logs DROP PARTITION ', v_drop_partitions ); SET @sql = v_sql; PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; END IF; END$$ DELIMITER ; DROP EVENT IF EXISTS ev_logs_drop_old_partitions; CREATE EVENT ev_logs_drop_old_partitions ON SCHEDULE EVERY 1 DAY STARTS TIMESTAMP(CURRENT_DATE, '00:10:00') DO CALL sp_logs_drop_old_partitions(); Step 9.4 检查任务 show events; SHOW PROCEDURE STATUS WHERE Db = DATABASE(); Step 10 大功告成 一切已准备就绪,请使用吧!数据库将按照您的配置自动创建新的分片,回收旧的分片,后续如果有调整,也可以直接修改 SQL 配置再次执行。 您可以定期通过下面的 SQL 来检查分区任务的运行状态和分区的数据量,请检查 pmax 分区数据量为 0,且已经创建了 7 天后的分区 SELECT PARTITION_NAME, PARTITION_DESCRIPTION, TABLE_ROWS FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'logs' ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 15; 1 个帖子 - 1 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-06-04 09:26:44+08:00 · tech

大家都应该遇到过,国内网络访问ghcr.io的超时问题,经过网上学习加AI沟通,如下是本次处理的总结记录,希望可以帮到有需要的人。 ps:国内大厂不代理加速ghcr.io,说是因为流量和审核问题,所以基本上都是临时的,不知道是不是真这个理由。 一、 问题背景与痛点 在国内折腾 Homelab 或 NAS(如飞牛 OS)时,很多开源项目(例如 wg-easy )的 Docker 镜像都逐步迁移到了 GitHub 的容器注册表 ghcr.io (GitHub Container Registry)。 由于国内特殊的网络环境,直接在 NAS 上拉取 ghcr.io 镜像极易出现两种错误: 网络直接超时 :完全无法连接官方源。 公共加速站失效 :使用第三方或公益代拉加速站(如已经关停的域名),会遭遇 dial tcp: lookup xxx: no such host 报错。 为了彻底、稳定地解决这一“最后一公里”的分发难题,最完美的方案是利用自己拥有的 海外原生网络服务器(如美国 VPS) ,通过 1Panel 搭建一个专属的私有镜像反代通道。 二、 踩坑历程与底层原理分析 在折腾自建反代的过程中,通常会经历三个阶段的报错,其背后的底层网络逻辑非常值得记录: 坑 1:直接反代返回 Whoa there! Not Found 错误 现象 :浏览器访问或 Docker 拉取时返回 GitHub 标准的 404 网页。 原因 :Nginx 默认的反代配置非常老实,在转发时将请求头中的 Host 保持为了用户自己的域名。而 GitHub API 服务器极为严格,发现 Host 不是 ghcr.io 时,会直接将请求重定向到网页端的 404 错误页。 坑 2:拉取时遭遇 TLS handshake timeout 认证超时 现象 :修改了 Host 后,Docker 客户端在连接时依然卡死,报错提示 Get "https://ghcr.io/token?scope=..." net/http: TLS handshake timeout 。 原因 :Docker 注册表有一套特殊的 令牌(Token)鉴权机制 。当 NAS 找反代服务器下载镜像时,GitHub 会要求客户端先去拿一个临时 Token。因为没有拦截重定向,GitHub 直接把官方的 ghcr.io/token 地址甩给了国内的 NAS。结果 NAS 绕过了中转站,自己“肉身”去连官方拿 Token ,直接被大墙拦截导致超时。 三、 终极解决方案 步骤 1:域名解析准备 在域名服务商(如 Cloudflare)中添加一条 A 记录(例如 ghcr.xxx.com ),解析到你的海外服务器真实公网 IP。 注意 :如果使用 Cloudflare, 必须关闭小黄云(改为仅限 DNS / 灰色云朵) 。因为 Cloudflare 的 CDN 节点 IP 在国内经常遭到大面积封锁或干扰,开启会导致国内 NAS 报 no such host 错误。 步骤 2:在 1Panel 中配置高级反代 在 1Panel 中创建一个反向代理网站,主域名填写你的域名,后端代理地址填写 https://ghcr.io 。 申请并强制开启 HTTPS / SSL 证书 (Docker 客户端强制要求安全连接)。 打开该网站的 配置文件(Nginx 源码) ,将 location / 块完全替换为以下“瞒天过海”终极版配置: Nginx location / { proxy_pass https://ghcr.io; # 核心修复 1:强制将 Host 设为后端目标域名,欺骗 GitHub 放行 proxy_set_header Host ghcr.io; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 核心修复 2:拦截并改写 GitHub 返回的鉴权重定向 Location 头 # 强制让 Token 请求也走你自己的中转域名,闭环解决国内超时问题 proxy_redirect https://ghcr.io/ https://ghcr.xxx.com/; # 隐藏可能暴露源站域名的特定 Link 响应头 proxy_hide_header Link; # 穿透 Docker 认证头(Authorization) proxy_set_header Authorization $http_authorization; proxy_pass_header Authorization; # 核心修复 3:关闭缓冲区,开启流式传输,防止大容量镜像拉取时断开或 504 超时 proxy_buffering off; proxy_read_timeout 900s; proxy_send_timeout 900s; } 步骤 3:国内 NAS 客户端测试与使用 配置保存重载后,回到国内飞牛 OS 的 SSH 终端,直接在原镜像名前加上你的专属域名即可丝滑下载: Bash sudo docker pull ghcr.xxx.com/wg-easy/wg-easy:latest 四、 进阶安全建议 由于公开的反代接口极易被全网扫描器扫到并疯狂盗刷流量,建议在 1Panel 中为该反代站点开启 Basic Auth(用户密码认证) 或限制访问 IP。开启后,在 NAS 终端执行一次 docker login ghcr.xxx.com 输入账号密码,即可安全、专享地白嫖自己服务器的流量,不用担心月流量被黑客一夜薅秃。 2 个帖子 - 2 位参与者 阅读完整话题