[{"content":" 75\n篇文章\n从 2023 年开始，持续记录真实的部署、迁移和排障现场。 4\n个年份\n从家用实验室到生产环境，按时间留下每一次折腾的来路。 CLI\n优先\n配置、命令和结果尽量写完整，让下一次遇到同类问题时少走弯路。 服务器与网络 记录 Linux、Web 服务、反向代理、WAF、DNS 和家庭网络里的实际配置。 浏览运维文章 排障记录 从现象、判断到修复，把那些不太容易一次解决的问题按时间整理下来。 打开归档 自动化脚本 重复的事情交给 Shell、Python 和 CI，保留能直接复制使用的命令和脚本。 查看脚本标签 稳定与安全 关注最小暴露面、可观测性和故障恢复，让服务在真正使用时也足够可靠。 查看安全主题 ","date":"2026-07-22","externalUrl":null,"permalink":"/","section":"Haoyu Wang's Blog","summary":"","title":"Haoyu Wang's Blog","type":"page"},{"content":"","date":"2026-07-22","externalUrl":null,"permalink":"/tags/proxmox/","section":"标签","summary":"","title":"Proxmox","type":"tags"},{"content":" PVE 搭配西数 SN580 2TB， FLUSH 死锁排查 # 我跑生产用的一台 UM580D（R7 5800H AMD Cezanne 平台mini主机）系统盘用的西数 SN580 2TB 固态，跑了几年了一直没出过问题，就在刚刚，直接卡死了，虚拟机全部断连（在这块固态上的，包括我的blog）然后从日志服务器以及journalctl中发现，pve 的pveproxy管理接口连接超时，从日志服务器看到，最先超时阻塞的是FLUSH 刷盘指令，内核尝试中止卡死 IO 请求，但 ssd的主控没在响应后续命令，nvme控制器无法执行重置，IO 队列冻结，最终导致整个系统阻塞。\n我重启了下机器，但是重启后硬盘 SMART 健康检测全部正常？？？没坏块？？？没io错误，而且我用fio测了下io是没问题的\nfio --name=bandwidth-test --filename=/tmp/fio-test.dat --rw=randread --ioengine=libaio --direct=1 --bs=128k --numjobs=4 --iodepth=32 --size=1G --runtime=30 bandwidth-test: (g=0): rw=randread, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=libaio, iodepth=32 ... fio-3.39 Starting 4 processes bandwidth-test: (groupid=0, jobs=1): err= 0: pid=94569: Wed Jul 22 22:53:18 2026 read: IOPS=46.3k, BW=5785MiB/s (6066MB/s)(1024MiB/177msec) slat (nsec): min=10510, max=97348, avg=20412.35, stdev=5365.29 clat (nsec): min=1462, max=2188.5k, avg=664500.76, stdev=112376.69 lat (usec): min=12, max=2278, avg=684.91, stdev=116.23 clat percentiles (usec): | 1.00th=[ 396], 5.00th=[ 441], 10.00th=[ 603], 20.00th=[ 619], | 30.00th=[ 627], 40.00th=[ 644], 50.00th=[ 660], 60.00th=[ 676], | 70.00th=[ 693], 80.00th=[ 725], 90.00th=[ 758], 95.00th=[ 799], | 99.00th=[ 938], 99.50th=[ 1045], 99.90th=[ 1893], 99.95th=[ 2040], | 99.99th=[ 2180] lat (usec) : 2=0.01%, 20=0.01%, 50=0.04%, 100=0.05%, 250=0.15% lat (usec) : 500=5.74%, 750=81.53%, 1000=11.68% lat (msec) : 2=0.72%, 4=0.07% cpu : usr=1.14%, sys=98.30%, ctx=1, majf=0, minf=1034 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.2%, 32=99.6%, \u0026gt;=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, \u0026gt;=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, \u0026gt;=64=0.0% issued rwts: total=8192,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32 bandwidth-test: (groupid=0, jobs=1): err= 0: pid=94570: Wed Jul 22 22:53:18 2026 read: IOPS=46.3k, BW=5785MiB/s (6066MB/s)(1024MiB/177msec) slat (nsec): min=10601, max=93892, avg=20401.49, stdev=5324.06 clat (nsec): min=1453, max=2204.9k, avg=664785.63, stdev=113507.63 lat (usec): min=13, max=2294, avg=685.19, stdev=117.45 clat percentiles (usec): | 1.00th=[ 383], 5.00th=[ 441], 10.00th=[ 611], 20.00th=[ 627], | 30.00th=[ 644], 40.00th=[ 652], 50.00th=[ 660], 60.00th=[ 676], | 70.00th=[ 693], 80.00th=[ 709], 90.00th=[ 742], 95.00th=[ 783], | 99.00th=[ 898], 99.50th=[ 1106], 99.90th=[ 2024], 99.95th=[ 2057], | 99.99th=[ 2212] lat (usec) : 2=0.01%, 20=0.01%, 50=0.02%, 100=0.06%, 250=0.15% lat (usec) : 500=5.73%, 750=84.64%, 1000=8.74% lat (msec) : 2=0.51%, 4=0.12% cpu : usr=2.84%, sys=96.59%, ctx=0, majf=0, minf=1035 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.2%, 32=99.6%, \u0026gt;=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, \u0026gt;=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, \u0026gt;=64=0.0% issued rwts: total=8192,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32 bandwidth-test: (groupid=0, jobs=1): err= 0: pid=94571: Wed Jul 22 22:53:18 2026 read: IOPS=47.9k, BW=5988MiB/s (6279MB/s)(1024MiB/171msec) slat (nsec): min=13316, max=93821, avg=19627.63, stdev=4743.79 clat (usec): min=2, max=2223, avg=639.80, stdev=96.79 lat (usec): min=17, max=2308, avg=659.43, stdev=100.29 clat percentiles (usec): | 1.00th=[ 553], 5.00th=[ 570], 10.00th=[ 578], 20.00th=[ 594], | 30.00th=[ 603], 40.00th=[ 611], 50.00th=[ 619], 60.00th=[ 627], | 70.00th=[ 652], 80.00th=[ 676], 90.00th=[ 725], 95.00th=[ 766], | 99.00th=[ 881], 99.50th=[ 1074], 99.90th=[ 1975], 99.95th=[ 2089], | 99.99th=[ 2212] lat (usec) : 4=0.01%, 20=0.01%, 50=0.02%, 100=0.04%, 250=0.10% lat (usec) : 500=0.16%, 750=93.46%, 1000=5.57% lat (msec) : 2=0.54%, 4=0.10% cpu : usr=3.53%, sys=95.29%, ctx=5, majf=0, minf=1033 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.2%, 32=99.6%, \u0026gt;=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, \u0026gt;=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, \u0026gt;=64=0.0% issued rwts: total=8192,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32 bandwidth-test: (groupid=0, jobs=1): err= 0: pid=94572: Wed Jul 22 22:53:18 2026 read: IOPS=48.2k, BW=6024MiB/s (6316MB/s)(1024MiB/170msec) slat (nsec): min=13556, max=88551, avg=19605.10, stdev=4725.53 clat (usec): min=2, max=2191, avg=638.42, stdev=95.92 lat (usec): min=20, max=2278, avg=658.03, stdev=99.41 clat percentiles (usec): | 1.00th=[ 553], 5.00th=[ 570], 10.00th=[ 578], 20.00th=[ 586], | 30.00th=[ 603], 40.00th=[ 611], 50.00th=[ 619], 60.00th=[ 635], | 70.00th=[ 652], 80.00th=[ 676], 90.00th=[ 709], 95.00th=[ 750], | 99.00th=[ 914], 99.50th=[ 1172], 99.90th=[ 1909], 99.95th=[ 2040], | 99.99th=[ 2180] lat (usec) : 4=0.01%, 50=0.02%, 100=0.04%, 250=0.09%, 500=0.16% lat (usec) : 750=94.59%, 1000=4.15% lat (msec) : 2=0.87%, 4=0.07% cpu : usr=1.18%, sys=97.63%, ctx=2, majf=0, minf=1032 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.2%, 32=99.6%, \u0026gt;=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, \u0026gt;=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.1%, 64=0.0%, \u0026gt;=64=0.0% issued rwts: total=8192,0,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=32 Run status group 0 (all jobs): READ: bw=22.6GiB/s (24.3GB/s), 5785MiB/s-6024MiB/s (6066MB/s-6316MB/s), io=4096MiB (4295MB), run=170-177msec root@pve:~# 这就有点奇怪了，，io没问题，而且我还有一块ssd，在另一块ssd上的虚拟机是正常运行的。\n然后我在网上找了一些相关文章，然后又一篇文章说说wd官方最初针对 Windows11 24H2 推送固件更新281050WD，公开说明旧固件 HMB 内存分配逻辑异常，会触发系统蓝屏，虽然官方没有单独标注 Linux环境的 FLUSH 死锁问题，但底层大概率都是 HMB 资源竞争缺陷，Windows 环境的蓝屏、Linux 环境的 NVMe IO 死锁，本质是同一个固件底层漏洞在不同操作系统下的不同表现，我的处理器是 R7 5800H（Cezanne 架构），pcIe4 链路的 ASPM 链路休眠，唤醒时序，跟我的 SN580 旧固件适配性很差，pciIe 链路如果切入低功耗，ssd 切入 apst深度休眠，两类休眠切换时序叠加，主机如果这个时候下发 flush 指令，容易触发主控资源抢占冲突？如果将 pcie 从 4.0 强制降级到 3.0、bios 关闭 aspm是不是能够降低故障概率？，ssd自身的 apst 休眠是不是还会触发？\nPS：这个问题目前没解决，但是我在bios侧以及kernel侧关闭了PCIE的链路休眠，以及配置了nvme_core.default_ps_max_latency_us=0禁止 SSD 进入低功耗状态，感觉不是闪存颗粒的问题，先这样跑着试试吧，反正我有备份，，不慌，后面再更新，下次再出现这种问题更新固件试下 ","date":"2026-07-22","externalUrl":null,"permalink":"/archives/205.html","section":"文章","summary":"","title":"PVE 搭配西数 SN580 2TB， FLUSH 死锁排查","type":"posts"},{"content":"","date":"2026-07-22","externalUrl":null,"permalink":"/tags/","section":"标签","summary":"","title":"标签","type":"tags"},{"content":"","date":"2026-07-22","externalUrl":null,"permalink":"/categories/","section":"分类","summary":"","title":"分类","type":"categories"},{"content":"","date":"2026-07-22","externalUrl":null,"permalink":"/categories/%E9%9A%8F%E7%AC%94/","section":"分类","summary":"","title":"随笔","type":"categories"},{"content":"这里是所有文章归档。迁移自 Typecho 的旧文章继续使用原 Typecho 链接作为主地址，同时逐步整理成更适合 Hugo 和 Blowfish 的内容结构。\n","date":"2026-07-22","externalUrl":null,"permalink":"/posts/","section":"文章","summary":"","title":"文章","type":"posts"},{"content":"","date":"2026-07-20","externalUrl":null,"permalink":"/tags/homelab/","section":"标签","summary":"","title":"HomeLab","type":"tags"},{"content":"","date":"2026-07-20","externalUrl":null,"permalink":"/tags/%E7%A7%81%E6%9C%89%E4%BA%91/","section":"标签","summary":"","title":"私有云","type":"tags"},{"content":" 绝大多数家用宽带都被运营商分配内网 CGNAT 地址，无法获取原生公网v4地址，部分地区可能v6地址都没有，这种场景下只能依托一台低配低延迟大带宽 VPS 搭建统一网关才是最佳实践。 这里推荐一下自己用的服务器，带宽高，价格合适，[点这里][1] 整体思路和方案一逻辑一致，只是把「本地公网入口」替换成「云端 VPS 入口」，家庭内网主动向外建立出站加密隧道，路由器无需开放任何入站端口，攻击面极低。 云端入口层（VPS） # VPS 部署独立 Nginx+Lua/Caddy+WAF 作为统一公网入口，仅开放 80、443、WireGuard 隧道端口，所有外网访问流量先经过 WAF 过滤，拦截注入、扫描、爬虫等攻击；同时 VPS 作为虚拟局域网网关，承载所有的加密隧道流量的转发。 可以参考我之前的文章 Caddy 配置 WAF防火墙 2024-11-29\u0026middot;1 分钟\u0026middot; 加载中 运维 Caddy LAMP环境配置 ModSecurity Web 应用防火墙模块 2024-10-06\u0026middot;1 分钟\u0026middot; 加载中 运维 Lamp ModSecurity 组网层 # 内网软路由 / 服务器部署 WireGuard 客户端，主动向 VPS WireGuard 服务端建立连接，形成私有虚拟局域网，所有家庭设备分配固定虚拟IP： 公网 Web 业务：外网用户→VPS WAF→WireGuard 加密隧道→内网对应服务； 设备互通流量：HOME1、HOME2 两台异地家庭站点优先 P2P 直连传输，打洞失败自动经 VPS 中继； 运维管理流量：手机、笔记本接入 WireGuard/OpenVPN 后，仅允许访问内网管理网段，和公网业务流量物理隔离。 家庭宽带全程仅出站连接，路由器无需放行任何入站端口，Hack无法主动发起对内网的扫描攻击； 一套 WireGuard 同时满足公网业务发布、文件传输、远程维护等需求， 而且新增第二台异地家庭灾备站点仅需加入同一 WireGuard 网络，引入路由即可实现跨区域混合 SD-WAN 架构。 ","date":"2026-07-20","externalUrl":null,"permalink":"/archives/204.html","section":"文章","summary":"","title":"无公网 IP下的远程访问虚拟局域网，最佳实践","type":"posts"},{"content":" 很多人有公网 IP 之后的第一反应就是映射一堆端口：NAS 5000、445 来一套，数据库开一个，ssh、rdp 也得单独映射，到最后自己都记不清到底对外暴露了多少端口。更稳妥的做法是单端口收敛 + 基于域名的反向代理 + WAF 统一防护—— 公网只开放一个非标准端口，就能承载所有 Web 业务，还能拦截常见的攻击。 1、公网入口层 — 单端口收敛，最小化攻击面 # 举个最典型的例子哈，比如你要对外提供服务a，内网地址是 192.168.1.1:80，想通过 a.example.com 在外网访问。\n所有子域名通过 CNAME 指向你的主 DDNS 域名，自动跟随公网 IP 变化 外网用户访问 https://a.example.com:8443，请求到达公网 IP 的 8443 端口 路由器配置一条目的 NAT（端口转发），将公网 8443 端口的流量，转发到内网 WAF 服务器的 443 端口 WAF 根据 HTTPS 的 SNI 扩展和 HTTP Host 头，识别出这是指向 a.example.com 的请求 WAF 完成攻击检测、ssl 证书卸载后，将请求反向代理到内网服务 a的 192.168.1.10:8080 业务响应沿原路返回给用户 这样整个过程，公网上只暴露了 8443 这一个端口，所有 Web 业务都通过这一个入口进出，统一经过 WAF 过滤。新增服务时，只需要在 WAF 里添加一个站点配置就行了。当然这里要说一句，waf也不是万能的，它拦不住定向的 0day 漏洞和业务逻辑层面的攻击，但是能挡住 99% 以上的自动化扫描和批量攻击了，足够把风险降到很低的水平。 2、业务与管理分离，非 Web 服务走 VPN # 单端口入口只承载 HTTP/HTTPS 类的公网业务流量，这是「业务层面」 SSH、RDP、SMB 文件共享、数据库、这类「管理面」的服务，一律不做端口映射，也没法做反向代理，即使能做也没必要，走的是L4层而不是7层代理。\n这里我们可以使用WireGuard /OpenVPN 作为主力互联协议，承 NAS文件 同步、文件访问等高吞吐流量；OpenVPN 作为补充，适配移动设备、UDP 受限的网络环境。 这是整个架构的安全核心，哪怕公网的 Web 入口真的出了问题，攻击者也不会之间碰到你的管理设备和存储资源等。 别把敏感服务放进公网反代！！！\n很多人图方便，把路由器后台、NAS 管理页、数据库管理工具也放进反代里，觉得加个密码就没事。这其实是把高价值的攻击目标直接摆到了公网上，非常不推荐。这类管理服务都走 VPN 接入，多一步操作，安心很多，减少攻击面。\n这套架构还有个很大的好处，如果后续你想加异地灾备站点，或者搭建个网站，升级成跨区域混合网络，只需要把 WAF 入口迁移到 VPS，内网组网部分完全不用动，可以平滑升级到 SD-WAN 架构.\n","date":"2026-07-20","externalUrl":null,"permalink":"/archives/203.html","section":"文章","summary":"","title":"有了公网 IP 就实现端口自由？单端口收敛 + WAF 反向代理才是家庭组网安全正确姿势","type":"posts"},{"content":" 家里的 NAS、博客、影音服务和一些实验环境，最开始都是“能访问就行”。后来跑的服务越来越多，异地备份、远程访问、跨站点访问也慢慢加了进来，单纯依赖 DDNS + 端口转发 就开始不太够用了。\n动态公网 IP 会变，运营商会调整线路，路由器也可能重启。更麻烦的是，家里的业务一旦直接暴露在公网，SSH、RDP、SMB 这些端口很快就会被扫描到。只要再碰上弱密码，NAS 和家用服务器就可能变成攻击入口，放大攻击平面。\n所以我现在使用的是一套基于 SD-WAN 思路 搭建的家庭私有云网络：用 WireGuard / IPsec 负责加密组网，用 FRR + OSPF 负责动态路由，用公网 VPS 作为所有公网业务访问的统一入口，再配合 WAF 反向代理，把公网访问、内网管理和站点之间的业务流量分开 ::aru:thumb:: 。\n这套架构的目标很简单：\nHOME 1、HOME 2 使用 DDNS 维持动态地址可达，VPS 作为所有公网业务访问的统一入口。 HOME 1 提供 OpenVPN，HOME 2 使用 WireGuard，HOME 1 和 HOME 2 之间通过 WireGuard 直连。 所有 VPS 同时接入 WireGuard 和 OpenVPN 两套隧道网络。 HOME 1 和 HOME 2 之间的内部业务优先走 WireGuard 直连，直连故障时再切换到 VPS 中继。 主站点不可用时，公网业务可以切到异地灾备站点。 公网只暴露 Web 入口，管理面和存储面尽量不直接暴露。 所有DDNS 使用阿里云 专业版DNS，减小TTL，缩短故障恢复时间。 一、先看整体拓扑 # 下面是我目前这套网络的逻辑拓扑。为了方便理解，可以把它看成“云端入口 + 两个家庭站点 + 一个共享站点”。\n这里有一个比较重要的设计：VPS 负责所有公网业务访问的入口，但不负责承载所有站点之间的内部流量。\n普通用户访问博客、Web 服务或影音服务时，都会先到 VPS 的 WAF 和反向代理，再由 VPS 通过 WireGuard 或 OpenVPN 隧道访问 HOME 1 或 HOME 2 的后端业务。正常情况下，HOME 1 和 HOME 2 之间的 NAS 同步、文件访问等内部流量通过 WireGuard 直接通信；只有直连链路出现问题，OSPF 才会把跨站业务切到 VPS 中继。这样既能保证公网业务入口统一，也能减少内部大流量对 VPS 带宽的依赖。\n二、为什么不用DDNS + 端口转发就到此为止了呢 # DDNS 本身没有问题，它解决的是“域名跟着动态 IP 变化”的问题。在这套架构里，HOME 1 和 HOME 2 都使用 DDNS，分别作为 OpenVPN 和 WireGuard 的稳定访问地址。但 DDNS 本身解决不了下面这些事情：\n家庭公网 IP 变化之后，所有访问都要重新依赖这条线路。 家庭宽带断线时，公网服务直接不可用。 多个服务需要开放多个端口，攻击面会越来越大。 HOME 1 和 HOME 2 之间没有统一的路由控制。 HOME 1 和 HOME 2 之间的 NAS 同步、跨站影音这类大流量业务会绕到公网入口，体验和成本都不理想。 所以我把“访问入口”和“业务所在位置”拆开了。公网用户只需要访问 VPS 的域名，VPS 再通过 OpenVPN / WireGuard 隧道访问后面的业务节点；站点之间则优先通过 HOME 1 和 HOME 2 的 WireGuard 直连。家庭宽带只作为站点出口和隧道承载线路，不再直接对公网提供业务入口。\n三、站点和网段怎么规划 # 异地组网最容易踩的坑就是网段冲突。两边都用 192.168.1.0/24，一开始看不出问题，等到需要跨站访问 NAS 或配置动态路由时就会发现：路由器根本不知道这个地址到底应该往哪边发。\n因此我的网段尽量做到每个站点唯一。\n站点 作用 网段 VPS 1 统一公网业务入口、WAF、反向代理、备用中继 66.66.66.254/24 VPS 2 备用公网入口和灾备入口 66.66.66.253/24 HOME 1 主数据中心、博客、NAS等 10.254.95.0/24 HOME 1 Manage Network 10.0.0.0/24 HOME 1 无线网络 10.254.254.0/24 HOME 2 异地私有网络 192.168.123.0/24 HOME 2 灾备业务和备份 NAS 192.168.122.0/24 HOME 1 DDNS HOME 1 的 OpenVPN 服务访问地址 动态公网地址 HOME 2 DDNS HOME 2 的 WireGuard 访问地址 动态公网地址 OpenVPN 远程访问，以及 VPS 的 OpenVPN 接入 10.7.7.0/24 WireGuard 骨干 HOME 1、HOME 2、VPS 之间的路由互联和 OSPF 邻居 66.66.66.0/24 这里的重点不是把所有设备都强行归到同一种 VPN，而是根据使用场景分工：HOME 1 的 OpenVPN 更适合移动运维接入，HOME 2 的 WireGuard 更适合站点互联和高吞吐传输；HOME 1 与 HOME 2 之间则使用独立的 WireGuard 直连。\n四、控制平面：FRR + OSPF 负责自动找路 # OpenVPN 和 WireGuard 负责把隧道打通，但它们不会自动替我决定某个网段应该走哪条路径。真正负责路径选择的是运行在各个网关上的 FRR，以及其中的 OSPF 进程。\n在这套架构里，两套 VPN 的角色不一样：HOME 1 的 OpenVPN 主要提供运维接入，同时也给 VPS 保留一条 OpenVPN 接入路径；HOME 2 和 HOME 1 之间的 WireGuard，以及各个 VPS 的 WireGuard 接入，组成站点互联和动态路由的骨干。\n直连路径优先 # HOME 1 和 HOME 2 之间的 WireGuard 隧道设置较低的 OSPF Cost，例如 10。这条路径是平时的主路径：\nNAS 双机同步直接走两地宽带。 跨站访问 HOME 2 的服务直接走站点间隧道。 Emby 或其他影音业务不经过 VPS，减少中继延迟和云端带宽消耗。 VPS 路径作为站点间备用 # HOME 1、HOME 2 分别通过 WireGuard 与 VPS 建立隧道，并把这类链路的 OSPF Cost 设置为 30。所有 VPS 同时接入 WireGuard 和 OpenVPN：WireGuard 主要承载站点互联和 OSPF 路由，OpenVPN 作为 HOME 1 的运维及兼容接入路径。平时 VPS 的 WireGuard 链路主要交换邻居状态和路由信息，不承载 HOME 1 与 HOME 2 之间的主要业务流量。\n当 HOME 1 和 HOME 2 的直连 WireGuard 隧道断开时，FRR 会发现 OSPF 邻居超时，重新计算最短路径。原来的直连路由被撤销，去往对端网段的流量就会改走 VPS 中继。\n这就是我这里的“自动切换”：不是脚本定时探测后修改一堆静态路由，而是把链路状态交给动态路由协议处理。\n需要说明的是，OSPF 收敛后，新的连接通常可以自动走新路径；已经建立的 TCP 连接是否能够无感恢复，还取决于应用本身的重连机制、连接超时和隧道切换时间。因此我会给需要高可用的服务配置客户端重连或反向代理重试，不能简单地把“路由收敛”理解为所有连接都不会中断。\n五、数据平面：不同流量走不同的路 # 这套网络里我把流量分成三类。\n1. 公网访问流量 # 普通用户访问博客、Web 服务或影音服务时，先到 VPS 的 WAF 和反向代理。VPS 完成 HTTPS 证书卸载、域名分流和基础攻击过滤，再通过 OSPF 可达的隧道网段访问 HOME 1 的业务网。\nHOME 1 整站不可用时，反向代理把上游切换到 HOME 2 的同构灾备服务。这样公网域名不需要跟着家庭 IP 变化，入口也不需要暴露家庭宽带地址。\nVPS 上的 WAF 和反向代理尽量保持无状态：证书、域名和转发规则可以在主备节点保持一致，业务数据和需要持久化的会话则留在后端。这样入口节点切换时，只是换了一条访问路径，不会把用户数据也带走。\n2. 跨站点业务流量 # HOME 1 和 HOME 2 之间的 NAS 同步、文件访问以及影音流量，优先走 WireGuard 直连。如果直连断开，才使用 VPS 中继。\n3. 管理流量 # 管理流量不通过公网 Web 入口。人在外面时，先连接 HOME 1 的 OpenVPN，拿到 10.7.7.x 地址，再访问 HOME 1 的管理网 10.0.0.0/24，或者通过已经同时接入 OpenVPN / WireGuard 的 VPS 进入其他站点。\n这种分层之后，Web 访问、站点互联和管理员操作分别有自己的入口，出现问题时也比较容易定位。\n六、Friend Home 为什么用 IPsec # Friend Home 不属于我的核心数据中心，使用场景主要是跨家庭的数据共享，所以没有把它直接放进主 WireGuard 骨干里，而是通过 IPsec IKEv2 和 HOME 1 的 WAN 2 建立点对点隧道。\nIPsec 的好处是兼容性比较好，很多家用路由器和软路由都可以直接配置。对于第三方节点，我只发布需要互访的网段和服务，不把 HOME 1 的管理网、无线网全部暴露过去。\n异地互联一定要先做访问边界，再做路由发布，能通不代表应该全通，最好使用防火墙策略限制方向。\n七、公网入口和安全策略 # 我的公网业务入口尽量收敛到 VPS，HOME 1 和 HOME 2 的 DDNS 主要用于 VPN 隧道建立，仅有部分不太重要的但是需要外网访问的通过HOME1 的 NGINX WAF 代理网关 8443端口放出，还有云服务器VPS只对外开放80 443端口，ssh，数据库等只能通过内网堡垒机进行访问。\n八、故障切换场景 # 1、HOME 1 和 HOME 2 之间的直连断开 # WireGuard 隧道不可用，OSPF 邻居超时，FRR 会撤销 Cost 10 的直连路由，流量切到 Cost 30 的 VPS 中继路径。直连恢复后，OSPF 再次选择 Cost 10 的路径。\n2、HOME 1 的 OpenVPN 接入异常 # HOME 1 的 OpenVPN 运维入口暂时不可用时，管理员无法直接通过 OpenVPN 回家，但 HOME 1 与 HOME 2 之间的 WireGuard 直连并不一定受影响。公网业务仍然先进入 VPS，再通过 VPS 的 WireGuard 接入访问后端；如果 HOME 1 的外部线路整体不可用，则由 OSPF 将相关路径切换到 VPS 中继。\n3、HOME 1 断电 # 我这两个小机柜都没有做UPS ，觉得也没什么必要，公网入口仍然在 VPS，反向代理探测到 HOME 1 上游不可用后，将请求切换到 HOME 2 的灾备业务。HOME 2 提供的是已经同步好的业务副本，恢复范围取决于最后一次同步时间，也就是实际的 RPO。\n4、VPS 主入口故障 # 公网访问切换到备用 VPS。这里的入口切换用的阿里云的GTM全局流量管理，通过 DNS健康检查以及流量调度实现，具体收敛时间取决于 DNS TTL 和健康检查周期。站点内部的 OSPF 备用路径仍然可以继续工作的\n九、这套架构的优缺点 # 优：\n家庭宽带以及VPS不需要直接对外暴露大量服务端口。 大流量跨站业务优先走直连，速度和成本都更可控。 站点之间使用动态路由，线路变化时不需要手工修改全部静态路由，。 HOME 1、HOME 2 和 VPS 都有各自的角色，故障范围更容易隔离。 权限隔离，管理平台、业务平面做了细粒度的防火墙策略。 缺：\n配置复杂度太高，有些地方可能有点小题大做。 OSPF、策略路由、WireGuard AllowedIPs 和防火墙规则需要一起设计。 VPS 中继还是会受到带宽和延迟影响。 灾备站点要真正可用，除了同步数据，还要同步应用配置、证书和恢复流程。 这里的自动切换只能保证路径切换，不能代替应用层高可用和数据一致性设计，这里我做的是定时同步。 仅供分享参考，可能99%的人都用不到我这个网络，过于复杂了 ::aru:crying::\n","date":"2026-07-20","externalUrl":null,"permalink":"/archives/home-net.html","section":"文章","summary":"","title":"我的家庭网络异地组网，私有云网络架构介绍","type":"posts"},{"content":"","date":"2026-07-18","externalUrl":null,"permalink":"/github.html","section":"Haoyu Wang's Blog","summary":"","title":"GayHub","type":"page"},{"content":"博客跑在一台 3.6GB 内存的小鸡上，Apache + PHP-FPM + MySQL 8.0 + Typecho（Handsome 主题）。平时访问没啥感觉，但一上来并发就原形毕露——50 个并发请求响应时间直接飙到 5 秒，300 并发直接卡死，98% 的请求失败。\n折腾了一下午，最终用 Varnish 做全页缓存，把 300 并发从 5 秒干到了 20 毫秒。记录一下过程。\n先说结论 # 指标 优化前 优化后 300 并发吞吐 ~4.65 req/s（50并发就卡死） 13903 req/s 平均响应 5283ms 21.6ms P99 响应 9207ms 34ms 500 并发 必崩 12801 req/s，P99=48ms 失败率 98%（50并发） 0%（5000请求） 问题在哪 # 先压测摸个底：\nab -n 100 -c 50 -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://127.0.0.1/ Requests per second: 4.65 [#/sec] Time per request: 5283 [ms] (mean) Failed requests: 98 99% 9207ms 50 并发就 5 秒，300 并发根本不用想。看下单次 PHP 请求要多久：\ncurl -o /dev/null -s -w \u0026#34;TTFB: %{time_starttransfer}s\\n\u0026#34; -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://localhost/ # TTFB: 0.215922s 单次渲染就要 215ms。拆一下时间：\n环节 耗时 MySQL 28 次查询总计 6ms PHP 代码执行 + 模板渲染 209ms SQL 不是瓶颈，PHP 执行本身才是问题。\n300 并发下每个 PHP-FPM worker 要占 200ms，至少需要 60 个 worker 才能不排队。但这台机器只有 3.6GB 内存，每个 PHP-FPM 进程吃 40MB，内存最多撑 35 个 worker。\n堆进程走不通。只有一个办法：别让它跑 PHP。\n架构调整 # 优化前：客户端 → Apache(80/443) → PHP-FPM → MySQL 优化后：客户端 → Nginx(443/TLS) → Varnish(80) → Apache(8080) → PHP-FPM → MySQL 上游已经有 Nginx 做 TLS 终止了（CDN 那边），所以本机不需要处理 SSL，简单很多。\n核心思路：Varnish 做全页缓存，匿名访客的请求直接从内存返回，只有缓存 MISS、登录用户、POST 请求才穿透到 PHP。\n装 Varnish # yum install -y varnish 写 VCL 配置 /etc/varnish/default.vcl：\nvcl 4.1; backend default { .host = \u0026#34;127.0.0.1\u0026#34;; .port = \u0026#34;8080\u0026#34;; .connect_timeout = 3s; .first_byte_timeout = 30s; .between_bytes_timeout = 10s; .max_connections = 200; } acl purge { \u0026#34;localhost\u0026#34;; \u0026#34;127.0.0.1\u0026#34;; } sub vcl_recv { if (req.method == \u0026#34;PURGE\u0026#34;) { if (!client.ip ~ purge) { return (synth(405, \u0026#34;Not allowed\u0026#34;)); } return (purge); } # POST/登录/后台/API → 穿透不缓存 if (req.method != \u0026#34;GET\u0026#34; \u0026amp;\u0026amp; req.method != \u0026#34;HEAD\u0026#34;) { return (pass); } if (req.url ~ \u0026#34;^/bb/\u0026#34;) { return (pass); } if (req.url ~ \u0026#34;^/(action/|feed|sitemap|search|api/|install)\u0026#34;) { return (pass); } # 登录用户 → 穿透（Typecho 用 __typecho_uid cookie 标识登录） if (req.http.cookie ~ \u0026#34;__typecho_uid\u0026#34;) { return (pass); } # 匿名 GET → 剥离 cookie，走缓存 unset req.http.Cookie; set req.http.X-Forwarded-Proto = \u0026#34;https\u0026#34;; return (hash); } sub vcl_backend_response { if (beresp.status \u0026gt;= 400 || beresp.status == 302) { set beresp.uncacheable = true; return (deliver); } # HTML 缓存 5 分钟 set beresp.ttl = 5m; # 静态资源缓存 1 天 if (bereq.url ~ \u0026#34;\\.(jpg|jpeg|png|gif|css|js|woff2?|svg)$\u0026#34;) { set beresp.ttl = 24h; unset beresp.http.Set-Cookie; } # HTML 页面移除 Set-Cookie，避免缓存污染 if (beresp.http.Content-Type ~ \u0026#34;text/html\u0026#34;) { unset beresp.http.Set-Cookie; unset beresp.http.Pragma; set beresp.http.Cache-Control = \u0026#34;public, max-age=300\u0026#34;; } return (deliver); } sub vcl_deliver { if (obj.hits \u0026gt; 0) { set resp.http.X-Cache = \u0026#34;HIT\u0026#34;; } else { set resp.http.X-Cache = \u0026#34;MISS\u0026#34;; } unset resp.http.Server; unset resp.http.X-Powered-By; return (deliver); } 踩坑提醒：__typecho_uid 是 Typecho 登录后写入的认证 cookie，通过它识别登录用户并绕过缓存。如果不清除匿名用户的 cookie，会导致每个用户缓存一份（因为 cookie 不同），命中率极低。所以匿名请求必须 unset req.http.Cookie。\n配置 Varnish 监听 80 端口，分配 512MB 内存缓存：\nmkdir -p /etc/systemd/system/varnish.service.d cat \u0026gt; /etc/systemd/system/varnish.service.d/custom.conf \u0026lt;\u0026lt;EOF [Service] ExecStart= ExecStart=/usr/sbin/varnishd -a :80 -f /etc/varnish/default.vcl -s malloc,512m -p default_ttl=300 EOF systemctl daemon-reload systemctl enable --now varnish Apache 退到 8080 # Varnish 接管了 80 端口，Apache 得让出来。\n改 /etc/httpd/conf/httpd.conf：\nListen 8080 Timeout 60 KeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 5 ServerLimit 16 ThreadsPerChild 25 MaxRequestWorkers 400 MinSpareThreads 25 MaxSpareThreads 150 改 /etc/httpd/conf.d/blog.conf，虚拟主机改为 8080：\n\u0026lt;VirtualHost *:8080\u0026gt; ServerName wanghaoyu.com.cn ServerAlias www.wanghaoyu.com.cn DocumentRoot /var/www/wanghaoyu.com.cn DirectoryIndex index.php LogFormat \u0026#34;%{X-Forwarded-For}i %l %u %t \\\u0026#34;%r\\\u0026#34; %\u0026gt;s %b \\\u0026#34;%{Referer}i\\\u0026#34; \\\u0026#34;%{User-Agent}i\\\u0026#34;\u0026#34; varnish_combined ErrorLog /var/log/httpd/wanghaoyu.com.cn-error.log CustomLog /var/log/httpd/wanghaoyu.com.cn-access.log varnish_combined \u0026lt;Directory \u0026#34;/var/www/wanghaoyu.com.cn\u0026#34;\u0026gt; AllowOverride All Require all granted \u0026lt;/Directory\u0026gt; SetEnvIf X-Forwarded-Proto \u0026#34;https\u0026#34; HTTPS=on \u0026lt;/VirtualHost\u0026gt; 踩坑提醒：之前 Apache 还在监听 443，这端口已经由上游 Nginx 处理了。记得在 /etc/httpd/conf.d/ssl.conf 里注释掉 Listen 443 https，不然会冲突。\n阅读量怎么办 # 这是缓存方案最容易被忽略的坑。\nTypecho Handsome 主题的阅读量是在 PHP 渲染时直接 UPDATE 数据库的。Varnish 缓存后，页面直接从内存返回，PHP 根本不执行，阅读量自然不会增长。如果缓存 5 分钟，那这 5 分钟内不管多少人访问，阅读量只 +1（第一个穿透的人）。\n解决方案：AJAX 异步统计 # 思路很简单：\nPHP 模板的 get_post_view() 改为只读（不再 +1） 新建 /api/views.php 接口，负责查询 + 增量（带 cookie 防重复刷新） VCL 里 /api/views.php 强制穿透（永不缓存） 页面加载后 JS 异步调用接口，拿到最新阅读数填入页面 /api/views.php 核心逻辑：\n\u0026lt;?php header(\u0026#34;Content-Type: application/json; charset=utf-8\u0026#34;); header(\u0026#34;Cache-Control: no-store, no-cache\u0026#34;); $cid = (int)($_GET[\u0026#34;cid\u0026#34;] ?? 0); if ($cid \u0026lt;= 0) { http_response_code(400); exit; } // 引入 Typecho define(\u0026#34;__TYPECHO_ROOT_DIR__\u0026#34;, dirname(__DIR__)); require_once __TYPECHO_ROOT_DIR__ . \u0026#34;/config.inc.php\u0026#34;; \\Typecho\\Common::init(); $db = \\Typecho\\Db::get(); $row = $db-\u0026gt;fetchRow($db-\u0026gt;select(\u0026#34;views\u0026#34;)-\u0026gt;from(\u0026#34;table.contents\u0026#34;)-\u0026gt;where(\u0026#34;cid = ?\u0026#34;, $cid)); $views = (int)$row[\u0026#34;views\u0026#34;]; // cookie 防重复刷新 $visited = isset($_COOKIE[\u0026#34;extend_contents_views\u0026#34;]) ? explode(\u0026#34;,\u0026#34;, $_COOKIE[\u0026#34;extend_contents_views\u0026#34;]) : []; if (!in_array($cid, $visited)) { $views++; $db-\u0026gt;query($db-\u0026gt;update(\u0026#34;table.contents\u0026#34;)-\u0026gt;rows([\u0026#34;views\u0026#34; =\u0026gt; $views])-\u0026gt;where(\u0026#34;cid = ?\u0026#34;, $cid)); $visited[] = $cid; setcookie(\u0026#34;extend_contents_views\u0026#34;, implode(\u0026#34;,\u0026#34;, $visited), [ \u0026#34;expires\u0026#34; =\u0026gt; time() + 86400 * 30, \u0026#34;path\u0026#34; =\u0026gt; \u0026#34;/\u0026#34;, \u0026#34;httponly\u0026#34; =\u0026gt; true, ]); } echo json_encode([\u0026#34;cid\u0026#34; =\u0026gt; $cid, \u0026#34;views\u0026#34; =\u0026gt; $views]); 主题 post.php 里，阅读量显示改成：\n\u0026lt;span id=\u0026#34;post-views-count\u0026#34; data-cid=\u0026#34;\u0026lt;?php echo $this-\u0026gt;cid; ?\u0026gt;\u0026#34;\u0026gt; \u0026lt;?php echo get_post_view($this) ?\u0026gt;\u0026amp;nbsp;次浏览 \u0026lt;/span\u0026gt; footer.php 的 \u0026lt;/body\u0026gt; 前加一段 JS：\n\u0026lt;script\u0026gt; (function(){ var el = document.getElementById(\u0026#34;post-views-count\u0026#34;); if (!el) return; fetch(\u0026#34;/api/views.php?cid=\u0026#34; + el.dataset.cid, {credentials:\u0026#34;same-origin\u0026#34;}) .then(function(r){ return r.json(); }) .then(function(d){ if (d \u0026amp;\u0026amp; typeof d.views !== \u0026#34;undefined\u0026#34;) el.innerHTML = d.views + \u0026#34;\u0026amp;nbsp;次浏览\u0026#34;; }) .catch(function(){}); })(); \u0026lt;/script\u0026gt; functions_mine.php 里的 get_post_view() 简化为只读：\nfunction get_post_view($archive) { $cid = $archive-\u0026gt;cid; $db = Typecho_Db::get(); Database::createFiledInTable(\u0026#34;views\u0026#34;,Database::$type_int_10,\u0026#34;contents\u0026#34;); $row = $db-\u0026gt;fetchRow($db-\u0026gt;select(\u0026#34;views\u0026#34;)-\u0026gt;from(\u0026#34;table.contents\u0026#34;)-\u0026gt;where(\u0026#34;cid = ?\u0026#34;, $cid)); return $row[\u0026#34;views\u0026#34;]; } 这样缓存页面时 PHP 只读一次（被缓存），阅读量增量全走 AJAX 接口，缓存期间阅读量准确增长。\n其他顺手优化的 # 既然都改了，顺手把几个明显的问题也一起处理了。\nPHP-FPM # 原来是 max_children = 50，max_spare_servers = 35，35 个空闲进程常驻，白白吃掉 1.4GB 内存。\npm.max_children = 30 pm.start_servers = 4 pm.min_spare_servers = 2 pm.max_spare_servers = 12 pm.max_requests = 500 加了 pm.max_requests = 500，每个进程处理 500 次请求后重启，防止内存泄漏。\nMySQL # Buffer Pool 从 128MB 翻倍到 256MB。加了几条复合索引：\nALTER TABLE blog_contents ADD INDEX idx_type_status_created (type, status, created); ALTER TABLE blog_contents ADD INDEX idx_type_status_views (type, status, views); ALTER TABLE blog_metas ADD INDEX idx_type (type); 开了慢查询日志（阈值 2 秒）方便以后排查。\nOPcache # opcache.memory_consumption=128 opcache.revalidate_freq=60 revalidate_freq 原来默认 2 秒，每次请求都 stat 文件看有没有变。改成 60 秒，减少磁盘 IO。发布文章后手动清理 Varnish 缓存就行。\n验证效果 # 先预热缓存，再压测：\n# 预热 curl -s -o /dev/null -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://127.0.0.1/ # 看 X-Cache 头 curl -s -I -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://127.0.0.1/ | grep X-Cache # X-Cache: HIT # 压测 300 并发 ab -n 2000 -c 300 -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://127.0.0.1/ Requests per second: 13903.08 [#/sec] Time per request: 21.578 [ms] (mean) Complete requests: 2000 Failed requests: 0 50% 21ms 95% 26ms 99% 34ms 再试试 500 并发：\nab -n 5000 -c 500 -H \u0026#34;Host: wanghaoyu.com.cn\u0026#34; http://127.0.0.1/ Requests per second: 12801.80 [#/sec] Time per request: 39.057 [ms] (mean) 99% 48ms 看 Varnish 缓存命中率：\nvarnishstat -1 | grep cache MAIN.cache_hit 6999 99.99% MAIN.cache_miss 1 0.01% 缓存清理 # 发布新文章后需要清理缓存，不然访客看到的是旧内容。写了个脚本放到 /root/：\n# 全部清理 /root/clean_varnish_cache.sh # 清理指定页面 /root/clean_varnish_cache.sh /archives/200.html 也可以等 5 分钟自动过期，看需求取舍。\n一点感受 # 单次 PHP 渲染 215ms 对静态博客来说太奢侈了。Typecho 首页就 28 次 SQL 查询，MySQL 只要 6ms，剩下 209ms 全花在 PHP 代码和模板上。对于大部分访客来说，看到的内容在缓存期内不会变，没必要每次都跑一遍 PHP。\nVarnish 的缓存命中响应是 0.2ms，比 PHP 快了 1000 倍。这就意味着 Varnish 面对并发时几乎不消耗资源，所有压力都绕过了。\n阅读量异步化这个设计值得单独说。很多缓存方案直接忽略这个问题，但这在博客场景里很影响体验。用 AJAX 接口做穿透处理，VCL 里加一行 pass 就搞定了，复杂度不高。\n3.6GB 跑 Typecho 博客完全够用，关键是用对缓存。优化前 PHP-FPM 空闲驻留 35 个进程吃掉 1.4GB，优化后只有 4 个 worker，可用内存从 1.9GB 涨到 2.2GB。\n上游有 Nginx 做反代的情况下，加 Varnish 特别简单。不用处理 TLS，不用改 DNS，Varnish 只监听 80 端口做 HTTP 缓存就行。\n相关链接：\nVarnish Cache 官方文档 Typecho 官网 Handsome 主题 ","date":"2026-06-22","externalUrl":null,"permalink":"/archives/varnish-typecho-performance.html","section":"文章","summary":"","title":"Varnish 缓存优化 Typecho 博客：300 并发从 5 秒降到 20 毫秒","type":"posts"},{"content":"","date":"2026-06-22","externalUrl":null,"permalink":"/categories/%E8%BF%90%E7%BB%B4/","section":"分类","summary":"","title":"运维","type":"categories"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/codex/","section":"标签","summary":"","title":"Codex","type":"tags"},{"content":" 为什么搞这个 # DeepSeek V4 出来之后我一直在用，百万 token 上下文窗口意味着整个项目塞进去都不用分段。我就想：能不能让 Codex 吃 DeepSeek 的推理？\n查了一圈，，发现有个开源项目，Moon Bridge 正好是干这事的。说白了就是个本地转发代理，把 Codex 的 OpenAI 格式请求翻译成 DeepSeek 能懂的话。\n先看架构 # 项目目录 → Codex CLI ↔ Moon Bridge（本地:38440）↔ DeepSeek API Moon Bridge 就是个协议转换器，跑在本地，不存任何数据。\n准备工作 # 先确认环境：\nnode --version # 需要 20+ go version # 需要 1.25+ 装 Codex：\nnpm install -g @openai/codex 去 DeepSeek 开放平台 搞个 API Key，存好。\n配置 Moon Bridge # 克隆项目：\ngit clone https://github.com/ZhiYi-R/moon-bridge.git cd moon-bridge 根目录下建 config.yml：\nmode: \u0026#34;Transform\u0026#34; server: addr: \u0026#34;127.0.0.1:38440\u0026#34; models: deepseek-v4-pro: context_window: 1000000 max_output_tokens: 384000 default_reasoning_level: \u0026#34;high\u0026#34; supported_reasoning_levels: - effort: \u0026#34;high\u0026#34; description: \u0026#34;High reasoning effort\u0026#34; - effort: \u0026#34;xhigh\u0026#34; description: \u0026#34;Extra high reasoning effort\u0026#34; supports_reasoning_summaries: true default_reasoning_summary: \u0026#34;auto\u0026#34; extensions: deepseek_v4: enabled: true deepseek-v4-flash: context_window: 1000000 max_output_tokens: 384000 default_reasoning_level: \u0026#34;high\u0026#34; supported_reasoning_levels: - effort: \u0026#34;high\u0026#34; description: \u0026#34;High reasoning effort\u0026#34; - effort: \u0026#34;xhigh\u0026#34; description: \u0026#34;Extra high reasoning effort\u0026#34; supports_reasoning_summaries: true default_reasoning_summary: \u0026#34;auto\u0026#34; extensions: deepseek_v4: enabled: true providers: deepseek: base_url: \u0026#34;https://api.deepseek.com/anthropic\u0026#34; api_key: \u0026#34;sk-你的key\u0026#34; # ← 改成你的 offers: - model: deepseek-v4-pro - model: deepseek-v4-flash routes: moonbridge: model: deepseek-v4-pro # 默认路由，后面可以在 Codex 里切换 provider: deepseek defaults: model: moonbridge max_tokens: 65536 踩坑提醒：api_key 那行别漏了，我第一遍就漏了，查了半天 401。\n启动 # go run ./cmd/moonbridge --config config.yml 看到日志输出说明起来了。默认监听 127.0.0.1:38440，暴露的接口是 /v1/responses，跟 OpenAI 的 Responses API 格式一样。\n别关这个终端，开个新窗口搞下一步。\n生成 Codex 配置 # CODEX_HOME_DIR=\u0026#34;${CODEX_HOME:-$HOME/.codex}\u0026#34; mkdir -p \u0026#34;$CODEX_HOME_DIR\u0026#34; # 有老配置的话先备份 cp \u0026#34;$CODEX_HOME_DIR/config.toml\u0026#34; \u0026#34;$CODEX_HOME_DIR/config.toml.bak\u0026#34; 2\u0026gt;/dev/null || true MODEL=\u0026#34;$(go run ./cmd/moonbridge --config config.yml --print-codex-model)\u0026#34; go run ./cmd/moonbridge \\ --config config.yml \\ --print-codex-config \u0026#34;$MODEL\u0026#34; \\ --codex-base-url \u0026#34;http://127.0.0.1:38440/v1\u0026#34; \\ --codex-home \u0026#34;$CODEX_HOME_DIR\u0026#34; \\ \u0026gt; \u0026#34;$CODEX_HOME_DIR/config.toml\u0026#34; 这步干了两个事：\nconfig.toml — Codex 的 provider 配置 models_catalog.json — 模型能力描述，上下文窗口、推理档位这些 打开Codex进行测试 如果一切正常，Codex 发出的每个请求都会走 Moon Bridge → DeepSeek。\n验证通没通 # 开第三个终端（是的，有点多）：\n# 看模型列表 curl http://127.0.0.1:38440/v1/models # 发条测试 curl http://127.0.0.1:38440/v1/responses \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;moonbridge\u0026#34;, \u0026#34;input\u0026#34;: \u0026#34;泥嚎 deepseek\u0026#34;, \u0026#34;max_output_tokens\u0026#34;: 1024 }\u0026#39; 同时看 Moon Bridge 的终端，应该有 POST /v1/responses 的日志。 推理档位 # DeepSeek V4 支持多档推理，写复杂逻辑的时候挺有用。在配置里已经预置了两档：\nhigh — 日常够用 xhigh — 需要深度推理的时候开 curl http://127.0.0.1:38440/v1/responses \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;moonbridge\u0026#34;, \u0026#34;input\u0026#34;: \u0026#34;输入的内容\u0026#34;, \u0026#34;reasoning\u0026#34;: {\u0026#34;effort\u0026#34;: \u0026#34;high\u0026#34;}, \u0026#34;max_output_tokens\u0026#34;: 4096 }\u0026#39; 踩坑记录 # 问题 原因 怎么搞 connection refused Moon Bridge 没启动或端口不对 看 config.yml 的 server.addr，确认进程活着 Codex 不认模型 缺 models_catalog.json 重新跑配置生成那一步 field provider not found 配置格式老了 用新格式：顶层 providers / models / routes / defaults 401 API Key 不对 检查 key 配没配对 402 账户没钱了 去 DeepSeek 开放平台充值 图片传不进去 视觉功能要额外配置 要么配 Kimi 的视觉 Provider，要么关掉 visual 扩展 一点感受 # 用了一周，说几个感受：\nV4 Flash 写日常代码完全够用，响应速度比 Pro 快不少，大部分场景不需要上 Pro 百万上下文是真的香，之前用别的模型经常写到一半忘了前面的上下文，DeepSeek 这边基本没遇到 Moon Bridge 很稳，跑了几天没崩过，日志也干净 唯一不爽的是要多开一个终端，不过我已经写成脚本一键启动了（mac），也不算事 总的来说，这套组合的成本大概只有直接走 OpenAI 的十分之一，实际体验没啥差别。如果你是独立开发者或者小团队，很值得折腾一下。\nstart script # #!/bin/bash # 一键启动 Codex + Moon Bridge + DeepSeek V4 # # 用法： # ./start-codex-with-deepseek.sh [项目目录] # # 如果省略项目目录，默认当前目录。 # set -e PROJECT_DIR=\u0026#34;${1:-$(pwd)}\u0026#34; MOON_BRIDGE_DIR=\u0026#34;${MOON_BRIDGE_DIR:-$HOME/moon-bridge}\u0026#34; DEEPSEEK_API_KEY=\u0026#34;${DEEPSEEK_API_KEY:-}\u0026#34; # ── 颜色 ── RED=\u0026#39;\\033[0;31m\u0026#39; GREEN=\u0026#39;\\033[0;32m\u0026#39; YELLOW=\u0026#39;\\033[1;33m\u0026#39; NC=\u0026#39;\\033[0m\u0026#39; log() { echo -e \u0026#34;${GREEN}[✓]${NC} $1\u0026#34;; } warn() { echo -e \u0026#34;${YELLOW}[!]${NC} $1\u0026#34;; } err() { echo -e \u0026#34;${RED}[✗]${NC} $1\u0026#34;; } # ── 检查依赖 ── check_deps() { command -v node \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || { err \u0026#34;需要 Node.js 18+\u0026#34;; exit 1; } command -v go \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 || { err \u0026#34;需要 Go 1.25+\u0026#34;; exit 1; } command -v codex \u0026gt;/dev/null 2\u0026gt;\u0026amp;1|| { err \u0026#34;需要 Codex CLI，装一下：npm install -g @openai/codex\u0026#34;; exit 1; } log \u0026#34;依赖检查通过\u0026#34; } # ── 检查/拉取 Moon Bridge ── setup_moon_bridge() { if [ ! -d \u0026#34;$MOON_BRIDGE_DIR\u0026#34; ]; then log \u0026#34;克隆 Moon Bridge 到 $MOON_BRIDGE_DIR\u0026#34; git clone https://github.com/ZhiYi-R/moon-bridge.git \u0026#34;$MOON_BRIDGE_DIR\u0026#34; else log \u0026#34;Moon Bridge 已存在，跳过克隆\u0026#34; fi # 写 config.yml（如果不存在） if [ ! -f \u0026#34;$MOON_BRIDGE_DIR/config.yml\u0026#34; ]; then log \u0026#34;生成 config.yml\u0026#34; cat \u0026gt; \u0026#34;$MOON_BRIDGE_DIR/config.yml\u0026#34; \u0026lt;\u0026lt;EOF mode: \u0026#34;Transform\u0026#34; server: addr: \u0026#34;127.0.0.1:38440\u0026#34; models: deepseek-v4-pro: context_window: 1000000 max_output_tokens: 384000 default_reasoning_level: \u0026#34;high\u0026#34; supported_reasoning_levels: - effort: \u0026#34;high\u0026#34; description: \u0026#34;High reasoning effort\u0026#34; - effort: \u0026#34;xhigh\u0026#34; description: \u0026#34;Extra high reasoning effort\u0026#34; supports_reasoning_summaries: true default_reasoning_summary: \u0026#34;auto\u0026#34; extensions: deepseek_v4: enabled: true deepseek-v4-flash: context_window: 1000000 max_output_tokens: 384000 default_reasoning_level: \u0026#34;high\u0026#34; supported_reasoning_levels: - effort: \u0026#34;high\u0026#34; description: \u0026#34;High reasoning effort\u0026#34; - effort: \u0026#34;xhigh\u0026#34; description: \u0026#34;Extra high reasoning effort\u0026#34; supports_reasoning_summaries: true default_reasoning_summary: \u0026#34;auto\u0026#34; extensions: deepseek_v4: enabled: true providers: deepseek: base_url: \u0026#34;https://api.deepseek.com/anthropic\u0026#34; api_key: \u0026#34;$DEEPSEEK_API_KEY\u0026#34; offers: - model: deepseek-v4-pro - model: deepseek-v4-flash routes: moonbridge: model: deepseek-v4-pro provider: deepseek defaults: model: moonbridge max_tokens: 65536 EOF fi # 检查 API Key if grep -q \u0026#39;sk-you\u0026#39; \u0026#34;$MOON_BRIDGE_DIR/config.yml\u0026#34; 2\u0026gt;/dev/null; then err \u0026#34;config.yml 里的 API Key 还是占位符 sk-you…-key，请改成你的 DeepSeek Key\u0026#34; err \u0026#34;设置环境变量：export DEEPSEEK_API_KEY=sk-xxx，然后重新运行\u0026#34; exit 1 fi log \u0026#34;Moon Bridge 配置就绪\u0026#34; } # ── 启动 Moon Bridge ── start_moon_bridge() { if lsof -i :38440 \u0026gt;/dev/null 2\u0026gt;\u0026amp;1; then warn \u0026#34;38440 端口已被占用，Moon Bridge 可能已经在跑\u0026#34; return fi log \u0026#34;启动 Moon Bridge（监听 :38440）\u0026#34; cd \u0026#34;$MOON_BRIDGE_DIR\u0026#34; go run ./cmd/moonbridge --config config.yml \u0026amp; MOON_PID=$! cd - \u0026gt;/dev/null # 等它起来 for i in $(seq 1 10); do if lsof -i :38440 \u0026gt;/dev/null 2\u0026gt;\u0026amp;1; then log \u0026#34;Moon Bridge 已就绪\u0026#34; return fi sleep 1 done err \u0026#34;Moon Bridge 启动超时，检查日志\u0026#34; exit 1 } # ── 生成 Codex 配置 ── gen_codex_config() { local codex_home=\u0026#34;${CODEX_HOME:-$HOME/.codex}\u0026#34; mkdir -p \u0026#34;$codex_home\u0026#34; # 备份 [ -f \u0026#34;$codex_home/config.toml\u0026#34; ] \u0026amp;\u0026amp; cp \u0026#34;$codex_home/config.toml\u0026#34; \u0026#34;$codex_home/config.toml.bak\u0026#34; local model model=\u0026#34;$(cd \u0026#34;$MOON_BRIDGE_DIR\u0026#34; \u0026amp;\u0026amp; go run ./cmd/moonbridge --config config.yml --print-codex-model)\u0026#34; (cd \u0026#34;$MOON_BRIDGE_DIR\u0026#34; \u0026amp;\u0026amp; go run ./cmd/moonbridge \\ --config config.yml \\ --print-codex-config \u0026#34;$model\u0026#34; \\ --codex-base-url \u0026#34;http://127.0.0.1:38440/v1\u0026#34; \\ --codex-home \u0026#34;$codex_home\u0026#34; \\ \u0026gt; \u0026#34;$codex_home/config.toml\u0026#34;) log \u0026#34;Codex 配置已生成到 $codex_home/config.toml\u0026#34; } # ── 启动 Codex ── start_codex() { if [ ! -d \u0026#34;$PROJECT_DIR\u0026#34; ]; then err \u0026#34;项目目录不存在：$PROJECT_DIR\u0026#34; exit 1 fi log \u0026#34;进入项目目录：$PROJECT_DIR\u0026#34; cd \u0026#34;$PROJECT_DIR\u0026#34; log \u0026#34;启动 Codex（按 Ctrl+C 退出）\u0026#34; echo \u0026#34;\u0026#34; codex } # ── 清理 ── cleanup() { if [ -n \u0026#34;$MOON_PID\u0026#34; ]; then warn \u0026#34;关闭 Moon Bridge (PID $MOON_PID)\u0026#34; kill \u0026#34;$MOON_PID\u0026#34; 2\u0026gt;/dev/null || true fi } trap cleanup EXIT # ── 开始 ── echo \u0026#34;\u0026#34; echo \u0026#34;╔══════════════════════════════════════╗\u0026#34; echo \u0026#34;║ Codex + DeepSeek 一键启动 ║\u0026#34; echo \u0026#34;╚══════════════════════════════════════╝\u0026#34; echo \u0026#34;\u0026#34; check_deps setup_moon_bridge start_moon_bridge gen_codex_config start_codex 相关链接：\nMoon Bridge Codex CLI DeepSeek API 文档 awesome-deepseek-agent ","date":"2026-05-31","externalUrl":null,"permalink":"/archives/codex-deepseek.html","section":"文章","summary":"","title":"Codex \u0026gt;  DeepSeek V4 接入实战","type":"posts"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/deepseek/","section":"标签","summary":"","title":"Deepseek","type":"tags"},{"content":"","date":"2026-03-14","externalUrl":null,"permalink":"/tags/oom/","section":"标签","summary":"","title":"Oom","type":"tags"},{"content":"","date":"2026-03-14","externalUrl":null,"permalink":"/tags/rhel/","section":"标签","summary":"","title":"Rhel","type":"tags"},{"content":" 前言 # 凌晨 2:47，监控告警：核心业务服务突然消失。登录服务器，只发现一行冰冷的日志：\n[Mon Mar 10 02:47:32 2026] Out of memory: Kill process 28471 (java) score 912 or sacrifice child OOM Killer，Linux 内核的\u0026quot;终极清道夫\u0026quot;，在生产环境中无情地杀死了我们的 JVM 进程。本文基于真实故障案例，从内核原理、现场数据、根因定位到根治方案，完整复盘。\n一、故障现场 # 1.1 现象描述 # 时间：凌晨 2:47 影响：核心业务服务宕机，触发 P1 告警 表象：Java 进程消失，系统日志出现 OOM Kill 记录 恢复：手动重启服务，业务恢复 1.2 关键现场数据 # vmstat 采样（故障前 5 分钟） # $ vmstat 1 30 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 8 0 0 823456 23456 8901234 0 0 12 34 2345 5678 45 12 0 43 0 12 2 0 234567 23456 9123456 0 0 56 78 4567 8901 67 23 0 10 0 23 5 0 123456 23456 9234567 456 789 1234 5678 6789 1234 78 34 0 5 0 45 12 0 56789 23456 9345678 1234 2345 4567 8901 8901 2345 85 45 0 2 0 89 23 1234 12345 23456 9456789 2345 4567 7890 12345 1234 3456 92 56 0 1 0 关键指标解读：\nsi/so（swap in/out）：从 0 飙升到 2345/4567，说明内存严重不足，开始大量换页 free：从 823MB 骤降到 12MB，可用内存耗尽 wa（iowait）：从 43% 降到 1%，不是 IO 问题，是内存问题 r（运行队列）：从 8 飙升到 89，大量进程等待 CPU，系统已极度卡顿 /proc/meminfo（故障瞬间） # $ cat /proc/meminfo MemTotal: 16384000 kB MemFree: 12345 kB # 几乎耗尽 MemAvailable: 56789 kB # 可用内存极低 Buffers: 23456 kB Cached: 9456789 kB SwapCached: 456789 kB # 大量缓存被换出 Active: 12345678 kB Inactive: 2345678 kB Active(anon): 8901234 kB # 匿名页（进程堆）占用高 Inactive(anon): 567890 kB Active(file): 3456789 kB Inactive(file): 567890 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 8388608 kB SwapFree: 2345678 kB # swap 已使用 6GB+ Dirty: 123 kB Writeback: 0 kB AnonPages: 9123456 kB # 匿名页接近 9GB Mapped: 1234567 kB Shmem: 234567 kB Slab: 1234567 kB SReclaimable: 567890 kB SUnreclaim: 567890 kB 关键发现：\nAnonPages（匿名页）高达 9.1GB，主要是 JVM 堆内存 SwapFree 只剩 2.3GB，swap 即将耗尽 MemAvailable 仅 56MB，系统已无内存可用 slabinfo 分析 # $ cat /proc/slabinfo | head -20 slabinfo - version: 2.1 # name \u0026lt;active_objs\u0026gt; \u0026lt;num_objs\u0026gt; \u0026lt;objsize\u0026gt; \u0026lt;objperslab\u0026gt; \u0026lt;pagesperslab\u0026gt; : tunables \u0026lt;limit\u0026gt; \u0026lt;batchcount\u0026gt; \u0026lt;sharedfactor\u0026gt; : slabdata \u0026lt;active_slabs\u0026gt; \u0026lt;num_slabs\u0026gt; \u0026lt;sharedavail\u0026gt; kmalloc-4k 123456 123456 4096 8 8 : tunables 0 0 0 : slabdata 15432 15432 0 kmalloc-1k 234567 234567 1024 32 8 : tunables 0 0 0 : slabdata 7330 7330 0 kmalloc-512 345678 345678 512 64 8 : tunables 0 0 0 : slabdata 5401 5401 0 dentry 1234567 1234567 192 42 2 : tunables 0 0 0 : slabdata 29394 29394 0 inode_cache 567890 567890 600 54 8 : tunables 0 0 0 : slabdata 10516 10516 0 buffer_head 2345678 2345678 104 39 1 : tunables 0 0 0 : slabdata 60145 60145 0 slab 占用分析：\ndentry 和 inode_cache 占用较高，说明文件系统访问频繁 buffer_head 高达 234 万个对象，约 244MB，文件缓存压力大 总体 slab 占用约 1.2GB，属于正常范围，不是 slab 泄漏 dmesg OOM 日志 # $ dmesg | grep -i \u0026#34;kill\\|oom\u0026#34; | tail -20 [Mon Mar 10 02:47:32 2026] java invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0 [Mon Mar 10 02:47:32 2026] CPU: 7 PID: 28471 Comm: java Not tainted 4.18.0-513.el8.x86_64 #1 [Mon Mar 10 02:47:32 2026] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 [Mon Mar 10 02:47:32 2026] Call Trace: [Mon Mar 10 02:47:32 2026] dump_stack+0x41/0x60 [Mon Mar 10 02:47:32 2026] dump_header+0x4a/0x1df [Mon Mar 10 02:47:32 2026] oom_kill_process.cold.33+0xb/0x1e [Mon Mar 10 02:47:32 2026] out_of_memory+0x1ad/0x4a0 [Mon Mar 10 02:47:32 2026] __alloc_pages_slowpath+0xa68/0xe20 [Mon Mar 10 02:47:32 2026] __alloc_pages_nodemask+0x245/0x280 [Mon Mar 10 02:47:32 2026] active_anon:2280834 inactive_anon:141972 isolated_anon:0 [Mon Mar 10 02:47:32 2026] active_file:864194 inactive_file:141972 isolated_file:0 [Mon Mar 10 02:47:32 2026] unevictable:0 dirty:30 writeback:0 unstable:0 [Mon Mar 10 02:47:32 2026] slab_reclaimable:141972 slab_unreclaimable:141972 [Mon Mar 10 02:47:32 2026] mapped:308641 shmem:58691 pagetables:22533 bounce:0 [Mon Mar 10 02:47:32 2026] free:3086 free_pcp:0 free_cma:0 [Mon Mar 10 02:47:32 2026] Node 0 active_anon:9123336kB inactive_anon:567888kB active_file:3456776kB inactive_file:567888kB unevictable:0kB isolated(anon):0kB isolated(file):0kB mapped:1234564kB dirty:120kB writeback:0kB shmem:234764kB shmem_thp:0kB anon_thp:0kB writeback_tmp:0kB unstable:0kB all_unreclaimable? no [Mon Mar 10 02:47:32 2026] Node 0 DMA free:10240kB min:256kB low:320kB high:384kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB writepending:0kB present:15972kB managed:15908kB mlocked:0kB kernel_stack:0kB pagetables:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB [Mon Mar 10 02:47:32 2026] Node 0 DMA32 free:1234kB min:1234kB low:2345kB high:3456kB active_anon:2345678kB inactive_anon:123456kB active_file:1234567kB inactive_file:123456kB unevictable:0kB writepending:0kB present:2345678kB managed:2345678kB mlocked:0kB kernel_stack:23456kB pagetables:123456kB bounce:0kB [Mon Mar 10 02:47:32 2026] Node 0 Normal free:0kB min:5678kB low:7890kB high:10123kB active_anon:6777658kB inactive_anon:444432kB active_file:2222209kB inactive_file:444432kB unevictable:0kB present:14030810kB managed:13723456kB mlocked:0kB kernel_stack:67890kB pagetables:789012kB bounce:0kB [Mon Mar 10 02:47:32 2026] Node 0 Normal: 0*4kB 0*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 0kB [Mon Mar 10 02:47:32 2026] Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB [Mon Mar 10 02:47:32 2026] 23456 total pagecache pages [Mon Mar 10 02:47:32 2026] 0 pages in swap cache [Mon Mar 10 02:47:32 2026] Swap cache stats: add 1234567, delete 1234567, find 234567/3456789 [Mon Mar 10 02:47:32 2026] Free swap = 2345678kB [Mon Mar 10 02:47:32 2026] Total swap = 8388608kB [Mon Mar 10 02:47:32 2026] 4096000 pages RAM [Mon Mar 10 02:47:32 2026] 0 pages HighMem/MovableOnly [Mon Mar 10 02:47:32 2026] 76543 pages reserved [Mon Mar 10 02:47:32 2026] 0 pages cma reserved [Mon Mar 10 02:47:32 2026] 0 pages hwpoisoned [Mon Mar 10 02:47:32 2026] Tasks state (memory values in pages): [Mon Mar 10 02:47:32 2026] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name [Mon Mar 10 02:47:32 2026] [ 123] 0 123 12345 123 123456 0 0 systemd-journal [Mon Mar 10 02:47:32 2026] [ 28471] 1000 28471 4567890 2280834 23456789 1234567 0 java [Mon Mar 10 02:47:32 2026] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/system.slice/app.service,task=java,pid=28471,uid=1000 [Mon Mar 10 02:47:32 2026] Out of memory: Kill process 28471 (java) score 912 or sacrifice child [Mon Mar 10 02:47:32 2026] Killed process 28471 (java) total-vm:18271560kB, anon-rss:9123336kB, file-rss:0kB, shmem-rss:0kB [Mon Mar 10 02:47:32 2026] oom_reaper: reaped process 28471 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB 关键信息提取：\nOOM Score：912（满分 1000，极高） JVM 进程占用：total-vm 18.2GB，anon-rss 9.1GB 杀死原因：系统全局内存耗尽（global_oom） 进程属于 cgroup：/system.slice/app.service 二、根因定位 # 2.1 直接原因 # JVM 堆内存配置过大（-Xmx12g），加上堆外内存（DirectBuffer、Native 内存），总内存占用超过系统可用内存，导致系统内存耗尽触发 OOM Killer。\n2.2 内存占用分析 # 系统总内存：16GB ├── JVM 堆内存：12GB（-Xmx12g） ├── JVM 元空间：~1GB ├── JVM 堆外内存：~2GB（DirectBuffer、JNI） ├── 系统/其他进程：~2GB └── 总计：~17GB \u0026gt; 16GB（超配） 2.3 为什么 swap 没救场？ # # 查看 swap 配置 $ cat /proc/sys/vm/swappiness 10 swappiness=10 表示系统倾向于保留内存给文件缓存，只有当内存极度紧张时才使用 swap。这导致：\n匿名页（JVM 堆）优先保留在内存中 swap 使用较晚，当开始使用 swap 时，系统已极度卡顿 swap 本身也是磁盘 IO，性能极差 2.4 深层原因 # 内存超配：JVM 配置未考虑系统和其他进程 缺少监控：没有设置内存使用率告警 无保护机制：未配置 cgroup 内存限制 无优雅退出：JVM 未配置 OOM 时主动退出 三、临时恢复方案 # 3.1 立即止血 # # 1. 检查系统状态 free -h vmstat 1 5 # 2. 如果内存仍紧张，手动清理缓存（临时） echo 3 \u0026gt; /proc/sys/vm/drop_caches # 3. 重启应用服务 systemctl restart app # 4. 观察恢复情况 tail -f /var/log/messages 3.2 防止再次触发 # # 临时调大 swappiness，让系统更积极使用 swap echo 60 \u0026gt; /proc/sys/vm/swappiness # 临时关闭 OOM Killer（不推荐长期使用） echo -17 \u0026gt; /proc/$(pgrep java)/oom_score_adj 四、根治方案 # 4.1 JVM 内存优化 # # 原配置（问题） JAVA_OPTS=\u0026#34;-Xms12g -Xmx12g\u0026#34; # 优化后配置 JAVA_OPTS=\u0026#34; -Xms8g -Xmx8g # 堆内存降到 8GB -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m # 限制元空间 -XX:MaxDirectMemorySize=2g # 限制堆外内存 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/heapdump.hprof -XX:OnOutOfMemoryError=\u0026#39;kill -9 %p\u0026#39; # OOM 时主动自杀，避免被 kill -9 \u0026#34; 4.2 系统级优化 # # /etc/sysctl.d/99-memory-tuning.conf # 更早开始使用 swap，避免内存突然耗尽 vm.swappiness = 60 # 更早触发 OOM Killer，避免系统卡死 vm.overcommit_memory = 2 vm.overcommit_ratio = 80 # 内存水位线调整，更早开始回收 vm.min_free_kbytes = 1048576 # 保留 1GB 空闲内存 # 脏页刷新策略，避免突发 IO vm.dirty_ratio = 20 vm.dirty_background_ratio = 5 vm.dirty_expire_centisecs = 500 vm.dirty_writeback_centisecs = 100 # 应用配置 sysctl --system 4.3 Cgroup 内存限制（关键） # # 创建 cgroup 限制应用内存 mkdir -p /sys/fs/cgroup/memory/app_limit # 设置硬限制 12GB（物理内存 16GB，留 4GB 给系统） echo 12884901888 \u0026gt; /sys/fs/cgroup/memory/app_limit/memory.limit_in_bytes # 设置软限制 10GB echo 10737418240 \u0026gt; /sys/fs/cgroup/memory/app_limit/memory.soft_limit_in_bytes # 启用 OOM 控制，达到限制时杀死进程而非整个 cgroup echo 1 \u0026gt; /sys/fs/cgroup/memory/app_limit/memory.oom_control # 将应用进程加入 cgroup echo $(pgrep java) \u0026gt; /sys/fs/cgroup/memory/app_limit/cgroup.procs systemd 服务集成：\n# /etc/systemd/system/app.service [Unit] Description=Application Service After=network.target [Service] Type=simple User=app Group=app ExecStart=/usr/bin/java $JAVA_OPTS -jar app.jar Restart=always RestartSec=10 # 内存限制 MemoryLimit=12G MemorySwapMax=2G # OOM 行为 OOMScoreAdjust=-500 [Install] WantedBy=multi-user.target 4.4 监控告警 # # /usr/local/bin/memory-monitor.sh #!/bin/bash # 内存使用率监控 MEM_TOTAL=$(grep MemTotal /proc/meminfo | awk \u0026#39;{print $2}\u0026#39;) MEM_AVAILABLE=$(grep MemAvailable /proc/meminfo | awk \u0026#39;{print $2}\u0026#39;) MEM_USED=$((MEM_TOTAL - MEM_AVAILABLE)) MEM_USAGE=$((MEM_USED * 100 / MEM_TOTAL)) # 告警阈值 WARNING_THRESHOLD=80 CRITICAL_THRESHOLD=90 if [ $MEM_USAGE -gt $CRITICAL_THRESHOLD ]; then echo \u0026#34;CRITICAL: Memory usage is ${MEM_USAGE}%\u0026#34; | \\ mail -s \u0026#34;[CRITICAL] $(hostname) Memory Alert\u0026#34; ops@example.com logger -p user.crit \u0026#34;Memory usage critical: ${MEM_USAGE}%\u0026#34; elif [ $MEM_USAGE -gt $WARNING_THRESHOLD ]; then echo \u0026#34;WARNING: Memory usage is ${MEM_USAGE}%\u0026#34; | \\ mail -s \u0026#34;[WARNING] $(hostname) Memory Alert\u0026#34; ops@example.com logger -p user.warn \u0026#34;Memory usage warning: ${MEM_USAGE}%\u0026#34; fi # OOM Kill 监控 if dmesg | grep -i \u0026#34;killed process\u0026#34; | tail -1 | grep -q \u0026#34;$(date +%s -d \u0026#39;5 minutes ago\u0026#39;)\u0026#34;; then echo \u0026#34;OOM Kill detected!\u0026#34; | \\ mail -s \u0026#34;[CRITICAL] OOM Kill on $(hostname)\u0026#34; ops@example.com fi crontab 配置：\n# 每 5 分钟检查一次 */5 * * * * /usr/local/bin/memory-monitor.sh 4.5 Prometheus 监控 # # node_exporter 内存告警规则 groups: - name: memory_alerts rules: - alert: HighMemoryUsage expr: (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 \u0026gt; 85 for: 5m labels: severity: warning annotations: summary: \u0026#34;High memory usage on {{ $labels.instance }}\u0026#34; description: \u0026#34;Memory usage is above 85% (current: {{ $value }}%)\u0026#34; - alert: OOMKillDetected expr: increase(node_vmstat_oom_kill[5m]) \u0026gt; 0 for: 0m labels: severity: critical annotations: summary: \u0026#34;OOM Kill detected on {{ $labels.instance }}\u0026#34; description: \u0026#34;A process was killed by OOM Killer\u0026#34; 五、预防机制 # 5.1 容量规划 # 内存规划公式： JVM 堆内存 \u0026lt;= (物理内存 - 系统保留 - 其他进程) * 0.8 示例（16GB 内存）： - 系统保留：2GB - 其他进程：2GB - 可用给 JVM：12GB * 0.8 = 9.6GB - 建议 JVM 堆内存：8GB 5.2 发布检查清单 # JVM 内存配置是否经过计算？ 是否配置了 HeapDumpOnOutOfMemoryError？ 是否配置了 cgroup/systemd 内存限制？ 监控告警是否覆盖内存使用率？ 是否有 OOM Kill 专项告警？ 应急预案是否包含内存不足场景？ 5.3 定期演练 # # 模拟内存压力测试（测试环境） stress-ng --vm 4 --vm-bytes 2G --timeout 60s # 验证监控告警是否正常触发 # 验证 cgroup 限制是否生效 # 验证服务重启后是否恢复正常 六、复盘总结 # 6.1 问题本质 # 不是 JVM 内存不够，而是系统内存管理失控。JVM 配置过大 + 缺少限制 + 监控缺失，导致系统级内存耗尽。\n6.2 改进措施 # 维度 改进前 改进后 JVM 配置 -Xmx12g（无脑配大） -Xmx8g（计算后配置） 内存限制 无 cgroup 12GB 硬限制 监控 无内存告警 80% 告警 + OOM 专项告警 应急 被动发现 主动监控 + 自动重启 预案 无 标准化 OOM 处理流程 6.3 如果重来 # 设计阶段：做容量规划，JVM 内存不是越大越好 发布阶段：强制检查清单，内存限制必须配置 运行阶段：内存监控作为基础监控，不可或缺 故障阶段：先保护系统，再恢复业务 七、核心知识点 # 7.1 OOM Killer 工作原理 # 1. 进程申请内存 2. 内核检查可用内存 3. 尝试回收缓存（page cache） 4. 尝试 swap 换出 5. 内存仍不足 → 触发 OOM Killer 6. 计算每个进程 oom_score 7. 杀死得分最高的进程 oom_score 计算因素：\n内存占用量（RSS + Swap） 运行时间（运行越久得分越低） 优先级（nice 值） oom_score_adj（可手动调整） 7.2 关键内核参数 # 参数 作用 推荐值 vm.swappiness swap 使用倾向 60（服务器） vm.overcommit_memory 内存分配策略 2（严格检查） vm.overcommit_ratio 可超配比例 80 vm.min_free_kbytes 最小空闲内存 1GB 7.3 调试命令速查 # # 查看 OOM 日志 dmesg | grep -i \u0026#34;kill\\|oom\u0026#34; journalctl -k | grep -i \u0026#34;oom\u0026#34; # 查看进程内存 pmap -x \u0026lt;pid\u0026gt; cat /proc/\u0026lt;pid\u0026gt;/status | grep -i vm cat /proc/\u0026lt;pid\u0026gt;/smaps_rollup # 查看系统内存 cat /proc/meminfo vmstat -s slabtop # 查看 cgroup 内存 cat /sys/fs/cgroup/memory/memory.stat systemctl status \u0026lt;service\u0026gt; 结语 # OOM Killer 是 Linux 内核的最后防线，不是解决方案。生产环境中，我们需要：\n合理规划：容量规划是基本功 主动限制：cgroup/systemd 内存限制是必选项 全面监控：内存监控告警不能少 快速恢复：自动化重启 + 优雅降级 希望本文的实战数据和根治方案，能帮助你在生产环境中避免类似的凌晨告警。\n参考文档：\nLinux Kernel OOM Killer Documentation Red Hat Enterprise Linux 8 Performance Tuning Guide systemd.resource-control ","date":"2026-03-14","externalUrl":null,"permalink":"/archives/197.html","section":"文章","summary":"","title":"RHEL系统OOM Killer深度分析：从现象到根治（带vmstat/slabinfo实战数据）","type":"posts"},{"content":"","date":"2026-02-13","externalUrl":null,"permalink":"/tags/vnc/","section":"标签","summary":"","title":"VNC","type":"tags"},{"content":" 在日常运维工作中，在一些特定情况下需要通过GUI图形化界面管理服务器，但是使用控制台又不太方便，VNC Server 作为一款常用的远程桌面工具，可以帮我们轻松实现图形化远程连接，本文将结合实操命令，一步步教大家完成安装和配置，全程复制命令即可上手，新手也能轻松搞定！ 本次操作基于Rocky Linux9.x 系统，使用 root 用户登录操作，确保服务器能够正常联网或者使用本地软件仓库（用于安装所需软件包）；提前准备好 VNC 客户端（如 RealVNC、TightVNC、VNC Viewer，用于连接测试） 前置条件 # 如果你的操作系统是最小化安装的，默认没有图形桌面环境，而 VNC 远程连接需要依赖图形界面才能正常显示，因此我们首先需要安装 GNOME 桌面环境。 dnf groupinstall -y \u0026#34;GNOME Desktop\u0026#34; \u0026#34;Graphical Administration Tools\u0026#34; 安装完成后，设置系统默认启动为图形界面（避免重启后仍进入命令行界面）\nln -sf /lib/systemd/system/runlevel5.target /etc/systemd/system/default.target 至此，图形桌面环境安装完成\n安装 VNC Server # 推荐使用 tigervnc-server 作为 VNC 服务端，兼容性好、操作简单\ndnf install -y tigervnc-server 配置VNC Server # 如果你的操作系统中启用了 firewalld | iptables | nftables 防火墙，VNC 连接默认使用 5901 端口（对应桌面编号 :1 如果配置多个桌面，端口依次为 5902、5903 等），因此需要开放 5901 端口，否则客户端无法连接到服务器，如果是云服务器也需要在对应的安全组中放行。 以firewalld为例：firewall-cmd --add-port=5901/tcp --permanent \u0026amp;\u0026amp; firewall-cmd --reload\n配置 VNC 登录用户及密码\nVNC 连接需要指定登录用户，并设置对应密码，这里以 root 用户为例做配置（若使用普通用户，操作逻辑一致，只需切换到对应普通用户即可）\n执行以下命令设置vnc连接密码\nvncpasswd 输入两次密码，后续会提示是否设置“仅查看”密码（仅查看权限无法操作服务器，仅能查看界面），一般直接回车跳过就行\n配置 VNC 服务 # 首先复制系统默认的 VNC 服务模板文件，生成我们自定义的服务文件（桌面编号为 :1，对应端口 5901），所以\ncp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@\\:1.service vim /etc/systemd/system/vncserver@\\:1.service /etc/systemd/system/vncserver@\\:1.service\n[Unit] Description=Remote desktop service (VNC) After=syslog.target network.target [Service] Type=forking WorkingDirectory=/root User=root Group=root PIDFile=/root/.vnc/%H%i.pid ExecStartPre=/bin/sh -c \u0026#39;/usr/bin/vncserver -kill %i \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 || :\u0026#39; ExecStart=/usr/bin/vncserver -autokill %i ExecStop=/usr/bin/vncserver -kill %i Restart=on-success RestartSec=15 [Install] WantedBy=multi-user.target 找到 [Service] 段中的 WorkingDirectory、User、Group 三行，将其中的“vnc用户名”替换为实际的登录用户（本文以 root 用户为例，替换后如下）；同时确保 PIDFile、ExecStart 等参数配置没问题\n服务文件配置完成后，需要重新加载 systemd 配置，使服务文件生效，然后启用 VNC服务就可以了 systemctl daemon-reload \u0026amp;\u0026amp; systemctl enable --now vncserver@\\:1.service\n","date":"2026-02-13","externalUrl":null,"permalink":"/archives/rhel-vnc.html","section":"文章","summary":"","title":"在RHEL系列操作系统中安装VNC Server","type":"posts"},{"content":"","date":"2026-02-11","externalUrl":null,"permalink":"/tags/3par/","section":"标签","summary":"","title":"3par","type":"tags"},{"content":"在3PAR存储运维过程中，FC端口SFP异常易引发Remote Copy链路失败故障，此类故障多由光链路质量异常引发，若处理不及时会影响数据同步可靠性。本文结合3PAR存储的实际故障场景，拆解故障排查思路、解决步骤及根因分析。\n核心故障表现围绕FC端口1:0:2展开，最终触发Remote Copy链路失败告警，告警如下： 使用 3par cli命令行 查看 1：0：2 端口光功率\n从上方图片可以看出， 发送功率 = 75 uW → 严重偏低！ 我这是 16G FC 正常要求 ≥158 uW 系统已经标了 * 警告 收光还算可以 没问题\n由此可以判断 出现的间歇性 CRC 错误，Remote Copy 心跳超时告警，都是因为 SFP模块光功率发射不足导致的，更换对应模块即可。\n需要注意的是，3par存储不能使用其他第三方的SFP模块，只能使用原厂光模块，不然会不识别！ ","date":"2026-02-11","externalUrl":null,"permalink":"/archives/3par-sfp.html","section":"文章","summary":"","title":"HPE 3PAR Remote Copy链路失败异常故障排查与解决","type":"posts"},{"content":"","date":"2026-02-11","externalUrl":null,"permalink":"/tags/%E5%AD%98%E5%82%A8/","section":"标签","summary":"","title":"存储","type":"tags"},{"content":"","date":"2026-01-18","externalUrl":null,"permalink":"/monitor.html","section":"Haoyu Wang's Blog","summary":"","title":"monitor","type":"page"},{"content":"","date":"2026-01-11","externalUrl":null,"permalink":"/tags/lamp/","section":"标签","summary":"","title":"Lamp","type":"tags"},{"content":"","date":"2026-01-11","externalUrl":null,"permalink":"/tags/redhat/","section":"标签","summary":"","title":"Redhat","type":"tags"},{"content":" 环境初始化 # 配置镜像源 # 首先我们想要在 RHEL 环境下安装lamp环境会需要一些软件包，Apache（Nginx）Mysql（Mariadb）默认系统仓库是自带的，无需额外添加其他软件包仓库，但是php推荐大家使用 remi 源进行安装，这里我们就需要安装Remi源。 如果你像我一样使用 RedHat Linux 发行版操作系统，需要先向红帽认证服务器注册此操作系统，才能正常使用。 由于Remi源依赖于EPEL（Extra Packages for Enterprise Linux）源，因此需要先安装EPEL：\ndnf install epel-release -y # 如果不可用就用下面的，latest-x是操作系统版本 dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm 然后在进行安装 Remi 源：\ndnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm -y 关闭 SELinux # SELinux可能会限制Apache和MySQL的运行，因此建议关闭SELinux 参考： 如何在 RHEL系列操作系统中禁用 SELinux？ 2026-01-11\u0026middot;1 分钟\u0026middot; 加载中 运维 运维 Redhat Selinux 安装 Apache # 如果需要配置 ssl证书，请将mod_ssl模块一并安装。\ndnf install httpd mod_ssl httpd-manual 安装 PHP # 我们在此之前已经安装好了 Remi 源 这里可以使用 Remi源直接安装 PHP环境以及依赖。 在安装PHP之前，需要先重置PHP模块，因为其他仓库（AppStream/EPEL）都携带了旧版本php，以避免冲突，我们需要先重置一下。\ndnf module reset php -y 启用 php 8.2模块\ndnf module enable php:remi-8.2 -y dnf module enable 命令用于启用指定的模块,这里启用了Remi源提供的PHP8.2模块，确保后续安装的PHP软件包来自Remi源中的82版本。 安装php以及扩展(扩展根据你的实际需要进行安装，我这里要跑typecho，所以就是下面这些)\ndnf install php php-fpm php-mysqlnd php-gd php-xml php-mbstring php-zip -y ### 安装 MySQL 并初始化 这里仓库自带 MySQL 8.x版本，如需其他5.7等老版本可以参考官方教程进行编译安装，目前近几年的rhel8.x 9.x都不提供5.x以及更早版本的rpm包了。 dnf install mysql mysql-server -y systemctl enable --now mysqld 初始化：\n[root@blogserver ~]# mysql_secure_installation Securing the MySQL server deployment. Connecting to MySQL using a blank password. The \u0026#39;validate_password\u0026#39; component is installed on the server. The subsequent steps will run with the existing configuration of the component. Please set the password for root here. New password: Re-enter new password: Estimated strength of the password: 50 Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment. Remove anonymous users? (Press y|Y for Yes, any other key for No) : ... skipping. Normally, root should only be allowed to connect from \u0026#39;localhost\u0026#39;. This ensures that someone cannot guess at the root password from the network. Disallow root login remotely? (Press y|Y for Yes, any other key for No) : ... skipping. By default, MySQL comes with a database named \u0026#39;test\u0026#39; that anyone can access. This is also intended only for testing, and should be removed before moving into a production environment. Remove test database and access to it? (Press y|Y for Yes, any other key for No) : ... skipping. Reloading the privilege tables will ensure that all changes made so far will take effect immediately. Reload privilege tables now? (Press y|Y for Yes, any other key for No) : ... skipping. All done! 验证阶段 # 验证apache是否正常运行，php是否成功加载 验证是否能正常访问 apache 在/var/www/html/ 目录下新建一个php文件，测试php是否成功加载，内容如下\n\u0026lt;?php phpinfo(); ?\u0026gt; ","date":"2026-01-11","externalUrl":null,"permalink":"/archives/rhel-lamp-setup.html","section":"文章","summary":"","title":"如何在 RHEL系统操作系统中安装 LAMP环境","type":"posts"},{"content":"","date":"2026-01-11","externalUrl":null,"permalink":"/tags/selinux/","section":"标签","summary":"","title":"Selinux","type":"tags"},{"content":" Security Enhanced Linux(SELinux)实现强制访问控制(MAC)。每个进程和系统资源都有一个特殊的安全性标签,称为 SELinux 上下文（context）。SELinux 上下文有时被称为 SELinux 标签，它是一个提取系统级别细节并专注于实体的安全属性的标识符。这不仅提供了在 SELinux 策略中引用对象的一个一致方法，而且消除了在其他身份识别系统中可能存在的模糊性。例如，某个文件可以在使用绑定挂载的系统中有多个有效的路径名称。\nSELinux 策略在一系列规则中使用这些上下文，它们定义进程如何相互交互以及与各种系统资源进行交互。默认情况下,策略不允许任何交互,除非规则明确授予了相应的权限。\n但是在特点情况下，一些老旧的应用程序或第三方软件可能与SELinux的策略不兼容。这些应用程序可能无法正常运行，或者会出现权限问题。例如，某些数据库软件或Web服务器软件在SELinux强制模式下可能会因为权限不足而无法启动或访问必要的资源\n禁用 SELinux # 查看当前SELinux状态 # 首先在操作之前我们需要先看一下 SELinux的状态，使用getenforce命令查看 SELinux有三种模式 Enforcing 模式：SELinux 处于强制模式，Enforcing 模式也是 SELinux 的默认模式，也是最严格的安全模式，在这种模式下，SELinux 会强制执行其安全策略，限制进程和用户的行为。 Permissive 模式：不强制执行安全策略，仅记录违规行为，适用于测试和调试阶段。 Disabled 模式： 禁用模式，关闭 SELinux功能，不记录也不会拦截。\n修改启动配置以禁用SELinux # 使用 grubby 禁用 SELinux（推荐） # 先决条件：已安装 grubby 软件包 grubby 是一个用于管理 Linux 系统引导加载程序（Bootloader）配置的CLI工具，主要用于更新和查看 GRUB、GRUB2、ZIPL 等引导程序的配置文件，直接修改配置文件虽然灵活性更高，但风险也更高，这里推荐大家用 grubby 进行操作。\ngrubby --update-kernel=ALL --args=\u0026#34;selinux=0\u0026#34; 这个命令会将selinux=0参数添加到所有内核的启动参数中(重启后生效\n) 修改 配置文件以禁用 SELinux（已弃用） # 这个是最经典的办法，修改/etc/selinux/config 配置文件以禁用 SELinux,如下图所示，将其修改为 Disable 重启即可。 官方弃用说明（来自 Red Hat 官方文档）： 在 RHEL 8 中，您仍然可以使用 已弃用 的方法，通过使用 /etc/selinux/config 文件中的 SELINUX=disabled 选项来禁用 SELinux。这会导致内核启用 SELinux，并在稍后的引导过程中切换到禁用模式。因此，可能会出现内存泄漏和竞争条件，从而导致内核 panic。\n","date":"2026-01-11","externalUrl":null,"permalink":"/archives/rhel-disable-selinux.html","section":"文章","summary":"","title":"如何在 RHEL系列操作系统中禁用 SELinux？","type":"posts"},{"content":"","date":"2026-01-11","externalUrl":null,"permalink":"/tags/%E8%BF%90%E7%BB%B4/","section":"标签","summary":"","title":"运维","type":"tags"},{"content":"","date":"2025-10-30","externalUrl":null,"permalink":"/tags/cloud-init/","section":"标签","summary":"","title":"Cloud-Init","type":"tags"},{"content":"","date":"2025-10-30","externalUrl":null,"permalink":"/tags/kvm/","section":"标签","summary":"","title":"Kvm","type":"tags"},{"content":" 当前在企业中不论是 VMware vSphere 还是 Proxmox VE、KVM 、OpenStack等，在常规创建虚拟机时要么通过官方的 ISO 镜像进行安装系统，然后配置网络，磁盘，基线等，要么就是直接使用自行制作模板镜像来创建虚拟机，从安装到完成开局配置比较消耗时间，多余的时间摸鱼不香吗？这里教大家如何在Proxmox平台中通过 Cloud-Init光速初始化一台虚拟机。 什么是 Cloud-Init？ # cloud-init 是业界公认的 “云实例初始化标准”，由 Canonical 发起，现在 AWS / Azure / GCP / OpenStack / 阿里云 / 腾讯云 等云厂商全部默认集成，它的设计目标只有一个：让“一台虚拟机第一次开机”这件事完全无人化，首次启动时自动注入用户、密钥、网络、hostname、磁盘等，PVE 会在启动前 动态生成一个 4 MiB 的 ISO，标签 cidata，里面含如下内容：\nmeta-data：uuid、instance-id、hostname、network-config user-data：用户、SSH 公钥、密码、runcmd等… 官方文档：https://docs.cloud-init.io/en/latest/\n准备工作 # 生成密钥对（用于ssh登录） # ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -N \u0026#34;\u0026#34; 参数说明：\n选项 含义 -t rsa 算法类型 -b 2048 密钥长度 最常用的就是RSA2048，如果需要更高的安全性推荐使用 ed25519 -f 保存路径，默认即 ~/.ssh/id_rsa -N \u0026quot;\u0026quot; 私钥密码 为空的话是没有密码（自动化常用） 执行后会得到：\n私钥：~/.ssh/id_rsa\n公钥：~/.ssh/id_rsa.pub\n下载Cloud-Init云镜像模板 # 这里以 Rocky Linux 为例\n下载地址：https://rockylinux.org/zh-CN/download\nwget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 创建虚拟机 # 创建空壳虚拟机 # 首先需要创建一个空壳虚拟机（不包含磁盘）T这里的 1000 为你的 VMID。\nqm create 1000 --name cloud-init-test --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 --vga std 导入系统磁盘镜像并挂入系统盘 # 导入系统磁盘镜像\nqm importdisk 1000 Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 local --format qcow2 导入完成后这里会多一块未使用的磁盘\n挂载：\nroot@pve:~# qm set 1000 --scsihw virtio-scsi-pci --scsi0 local:1000/vm-1000-disk-0.qcow2 update VM 1000: -scsi0 local:1000/vm-1000-disk-0.qcow2 -scsihw virtio-scsi-pci root@pve:~# 将刚挂进去的系统盘设置为启动盘 # qm set 1000 --boot order=scsi0 初始化Cloud-Init # 添加Cloud-init 驱动器以及串口\nqm set 1000 --ide2 local:cloudinit qm set 1000 --serial0 socket --vga serial0 qm set 1000 --agent enabled=1,fstrim_cloned_disks=1 最后这条命令的作用是告诉 Proxmox VE 这台虚拟机里面装有 qemu-guest-agent，克隆，还原快照后自动发TRIM，节省物理存储。\n配置 Cloud-Init # qm set 1000 --ciuser cloud-user qm set 1000 --sshkeys .ssh/id_rsa.pub qm set 1000 --ipconfig0 ip=dhcp # 需要静态可改 ip=192.168.1.99/24,gw=192.168.1.1 qm set 1000 --nameserver 223.5.5.5 到这里直接启动虚拟机就可以了\nqm start 1000 Enjoy! ::aru\u0026#x1f52a;:\n","date":"2025-10-30","externalUrl":null,"permalink":"/archives/pve-cloud-init.html","section":"文章","summary":"","title":"Proxmox VE  结合 Cloud-Init 使用云镜像模板快速初始化虚拟机","type":"posts"},{"content":"","date":"2025-10-30","externalUrl":null,"permalink":"/categories/%E8%99%9A%E6%8B%9F%E5%8C%96/","section":"分类","summary":"","title":"虚拟化","type":"categories"},{"content":"无需公网IP、无需买、不折腾路由器，一条命令解决异地组网难题\n0命令行基础也能抄作业 不需要公网IP、不改光猫、不刷路由器 下行速度=你的云服务器带宽，跑满100M轻轻松松 兼容群晖、威联通、Windows、macOS、安卓、iOS、OpenWRT 核心只有一个GitHub项目：\nangristan/openvpn-install（18k Star，持续维护，全部bash脚本开源）\nnas-tool/openvpn-install（基于原作者汉化）\n原理｜30秒看懂 # 在云服务器（阿里云轻量服务器38/年 200M峰值带宽）上运行一键脚本，自动安装、配置OpenVPN，生成“虚拟交换机”。 把你的设备（电脑、NAS、摄像头、树莓派）用OpenVPN客户端链接你的虚拟局域网，它们就拿到同一网段的虚拟IP，相当于插在同一个路由器下。 数据先加密→走互联网→云服务器中转→再解密→送到对端，全程AES-256-GCM，TLS 1.3，安全拉满。 准备｜你需要的东西 # 物品 最低要求 费用 云服务器 1C1G，Debian/Ubuntu 20.04+ 上面说的阿里云轻量服务器也可以，海外Vultr/Oracle/AWS/Azure /GCP 都可以白嫖。 本地设备A NAS/电脑 已有=0元 本地设备B 笔记本/软路由/ 已有=0元 5分钟 读完本文+复制粘贴命令 0元 5分钟实战｜Step by Step # 全程“复制→粘贴→回车”，脚本自带中文提示，手残党也稳。\nStep-0 登录云服务器 # ssh root@你的服务器IP Step-1 一键安装OpenVPN # # 国内机器建议先换源（可选） apt update \u0026amp;\u0026amp; apt -y install wget # 下载脚本并运行 wget https://raw.githubusercontent.com/nas-tool/openvpn-install/master/openvpn-install.sh chmod +x openvpn-install.sh \u0026amp;\u0026amp; ./openvpn-install.sh 脚本交互翻译（全部默认直接回车=最稳）：\nIPv4/IPv6双栈？→回车（默认IPv4） 端口1194/443/其他？→回车（1194） TCP还是UDP？→回车（UDP更快） DNS防泄漏？→1（Cloudflare） 压缩？→n（关闭，省CPU） 2分钟跑完，最后会提示：\n“Client client1 added, configuration available in /root/client1.ovpn”\nStep-2 拿到配置文件 # # 把文件拽到本地（Windows用WinSCP，mac用scp） scp root@你的服务器IP:/root/client1.ovpn ~/Desktop/ Step-3 客户端连接 # 系统 下载客户端 导入方式 Windows OpenVPN-GUI 右键→Import→连接 macOS Tunnelblick 拖入配置→连接 群晖 套件中心装VPN Center→OpenVPN→导入.ovpn→连接 威联通 App Center→OPENVPN→导入→连接 安卓 OpenVPN for Android 把.ovpn发微信→打开→导入 iOS App Store搜OpenVPN 用AirDrop/邮件导入→连接 连接成功图标变绿，虚拟IP段默认10.8.0.0/24。\nStep-4 验证“异地组网” # 老家NAS（真实IP 192.168.1.100） 城市电脑连VPN后拿到10.8.0.6 直接Win+R输入\\\\你的NAS IP→回车→弹出NAS共享文件夹→复制文件跑满服务器带宽 **搞定！**两地现在就像插在同一台交换机下，SMB、RDP、SSH、打印机、摄像头、HomeAssistant全部互通。\n进阶｜多端互联 \u0026amp; 自动重连 # 每加一个设备，在云服务器重新跑脚本： ./openvpn-install.sh → 选1 Add a new client → 起个新名字（nas2、pi3） 会生成新的.ovpn，重复Step-2/3即可。\n2. 群晖/威联通把OpenVPN设为“开机自启+断线重连”，7×24小时不掉线。\n3. 树莓派用crontab加@reboot openvpn --config /home/pi/nas.ovpn \u0026amp;\n4. **想三地、四地组网？**继续加客户端，云服务器当“星型”中心，最多几百台同时在线。\n免费加速技巧｜不花钱也能跑满带宽 # Oracle Cloud永久免费 | Google Cloud | AWS | Azure 都可以白嫖 阿里云轻量服务器38/年 峰值带宽200Mbps。 把OpenVPN端口改成443/UDP，公司/学校防火墙也认不出，秒穿。 关闭脚本里的压缩、降低加密到AES-128-GCM（脚本里可选），CPU占用砍半，老NAS也能跑。 ","date":"2025-10-10","externalUrl":null,"permalink":"/archives/openvpn-install.html","section":"文章","summary":"","title":"[OpenVPN]免费开源异地组网方案，5分钟打通两地局域网","type":"posts"},{"content":"","date":"2025-10-10","externalUrl":null,"permalink":"/tags/openvpn/","section":"标签","summary":"","title":"OpenVPN","type":"tags"},{"content":" Red Hat Enterprise Linux（RHEL）作为业界领先的企业级操作系统，其镜像下载需通过官方授权渠道。以下为 RHEL 7 至 10 全版本的官方下载地址整理，以及本人下载上传网盘的镜像，供用户参考。 官方下载入口（需要 有效订阅） # 地址：https://access.redhat.com/downloads # Red Hat Enterprise Linux 7 # https://access.redhat.com/downloads/content/69/ver=/rhel\u0026mdash;7/7.9/x86_64/product-software\nRed Hat Enterprise Linux 8~10 # https://access.redhat.com/downloads/content/rhel\n📦 网盘镜像备份（由本人上传） # 以下镜像为本人通过官方渠道下载后上传至网盘，默认使用夸克对外提供下载，下载速度应该够用，如果想要arm系列或者想要更快的下载速度请捐助后获取本站提供的iso镜像仅供已拥有合法授权的用户参考使用，请在下载后遵守 Red Hat 的许可协议。\n💿ISO镜像下载 # 版本 文件名 网盘链接 RHEL 7.9 rhel-server-7.9-x86_64-dvd.iso 私有云盘（提取码：4clqtm）\n夸克（提取码：yVnk） RHEL 8.10 rhel-8.10-x86_64-dvd.iso 夸克（提取码：NDdb） RHEL 9.6 rhel-9.6-x86_64-dvd.iso 夸克（提取码：ni5r） RHEL 10 rhel-10.0-x86_64-dvd.iso 夸克（提取码：reQ9） RHEL 10 WSL2 rhel-10.0-x86_64-wsl2.tar.gz 夸克（提取码：Ypz5） 💿QCOW2镜像下载 # 版本 文件名 网盘链接 RHEL 8.10 rhel-8.10-x86_64-kvm.qcow2 夸克（提取码：4clqtm） RHEL 9.6 rhel-9.6-x86_64-kvm.qcow2 夸克（提取码：http） RHEL 10 rhel-10.0-x86_64-kvm.qcow2 夸克（提取码：Wg96） ⚠️ 声明：网盘镜像仅供学习和测试用途，请确保你已拥有有效的 Red Hat 订阅授权。如用于商业或生产环境，请通过官方渠道下载并激活订阅。\n国内镜像站（仅 CentOS 及 AlmaLinux）：\n由于版权限制，RHEL 镜像仅能通过官方渠道下载。如需兼容替代，可考虑：\nAlmaLinux：https://mirrors.almalinux.org CentOS Stream：https://mirrors.tuna.tsinghua.edu.cn/centos/ Rocky Linux： https://rockylinux.org ","date":"2025-09-12","externalUrl":null,"permalink":"/archives/rhel-download.html","section":"文章","summary":"","title":"Red Hat Linux 全版本镜像下载","type":"posts"},{"content":"","date":"2025-09-12","externalUrl":null,"permalink":"/tags/dsm/","section":"标签","summary":"","title":"DSM","type":"tags"},{"content":"","date":"2025-09-12","externalUrl":null,"permalink":"/tags/rsyslog/","section":"标签","summary":"","title":"Rsyslog","type":"tags"},{"content":"","date":"2025-09-12","externalUrl":null,"permalink":"/tags/%E7%BE%A4%E6%99%96/","section":"标签","summary":"","title":"群晖","type":"tags"},{"content":" 一、为什么选 DSM 做日志服务器 # 无需额外采购硬件，一台 NAS 即可 7×24 小时运行，而且 DSM 基于 Linux 内核，自带日志轮转、压缩、ACL 权限体系。\n图形化解密，几分钟就可以完成配置，比手撸 rsyslog.conf 更方便，最重要的一点是满足 ISO 27001、等保 2.0 集中审计条款，最低要求——日志不落本地、集中存放 180 天。\n二、底层原理（这些了解即可） # DSM 的「日志中心」套件底层就是 rsyslog 8.x，外加一个pg数据库用于索引与检索 默认监听 514/UDP，可手动再开 514/TCP 与 514/TLS，保证外网穿越不丢包 每条日志会被拆成 6 个字段：\nfacility.priority | timestamp | hostname | tag | msgid | message\n并在 pg数据库里建立GIN索引，因此关键字检索速度在千万级数据量下仍能保持在 \u0026lt;2s。 三、如何把 DSM 变成 Rsyslog 服务器 # 3.1 安装并启用套件 # DSM 7.x：套件中心 → 搜索日志中心并进行安装。 打开日志中心 左侧 接收日志，新增规则 端口保持默认 514/UDP，如需外网穿越再勾「TCP」与「TLS」→ 应用。 3.2 创建独立共享文件夹存储日志数据（防爆盘） # 控制面板 → 共享文件夹 → 新建 logs。 配额建议： 纯文本 50 GB 可存约 2 亿条 300 字节日志，这个根据自身需求进行配置； 开启压缩还能再省 60% 创建完成后回到日志中心，存储空间 选择刚才的共享文件夹 → 启用自动归档以及压缩功能。\n3.3 客户端指向 DSM # Linux（rsyslog 客户端） # # /etc/rsyslog.d/50-default.conf 末尾加一行 *.* @dsm ip:514 # 单 @ 代表 UDP；@@ 代表 TCP # 重启 sudo systemctl restart rsyslog 网络设备（以Ruijie为例） # conf t logging dsm ip logging trap informational end wr Windows（可选） # 这里推荐使用一个开源软件，evtsys， 项目地址：https://github.com/zensquare/evtsys。 根据向导进行配置即可。 四、验证与排错 # 4.1 实时查看 # 打开 DSM「日志中心」→「概览」→ 右上角「实时日志」，应该立刻看到客户端 hostname 出现。\n4.2 关键字检索 # 搜索框输入 sshd Failed 回车，2 秒内即可定位所有 SSH 爆破来源 IP。\n4.3 性能监控 # 若接收速率持续 \u0026gt;3 000 条/秒，建议：\n把 UDP 改成 TCP，防止拥塞丢包； 给 NAS 加 SSD 缓存，提高 PostgreSQL 写入 IO 4.4 常见故障表 # 现象 原因 解决 DSM 收不到日志 本地防火墙未放行 514 控制面板 → 安全性 → 防火墙 → 新增规则放行 514/UDP 中文乱码 客户端与 DSM 字符集不一致 客户端 rsyslog 模板加 $EscapeControlCharactersOnReceive off 磁盘爆满 未开自动归档 启用「按日期归档」+「压缩」+「配额」 ","date":"2025-09-12","externalUrl":null,"permalink":"/archives/dsm-rsyslog.html","section":"文章","summary":"","title":"群辉DSM日志中心作为Rsyslog服务器收集日志","type":"posts"},{"content":"","date":"2025-08-17","externalUrl":null,"permalink":"/tags/brew/","section":"标签","summary":"","title":"Brew","type":"tags"},{"content":"","date":"2025-08-17","externalUrl":null,"permalink":"/tags/homebrew/","section":"标签","summary":"","title":"Homebrew","type":"tags"},{"content":" Homebrew 安装慢，主要是因为默认使用 GitHub 上的官方源，国内网络访问速度受限，可以通过更换为国内镜像源来提速 中科大（推荐） # git -C \u0026#34;$(brew --repo)\u0026#34; remote set-url origin https://mirrors.ustc.edu.cn/brew.git git -C \u0026#34;$(brew --repo homebrew/core)\u0026#34; remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git echo \u0026#39;export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc 腾讯 # /bin/bash -c \u0026#34;$(curl -fsSL https://mirrors.cloud.tencent.com/homebrewbrew/install/install.sh)\u0026#34; # 装完后Bottles自动指向 https://mirrors.cloud.tencent.com/homebrew-bottles 阿里云 # git -C \u0026#34;$(brew --repo)\u0026#34; remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git git -C \u0026#34;$(brew --repo homebrew/core)\u0026#34; remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git echo \u0026#39;export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc 恢复官方源 # git -C \u0026#34;$(brew --repo)\u0026#34; remote set-url origin https://github.com/Homebrew/brew.git git -C \u0026#34;$(brew --repo homebrew/core)\u0026#34; remote set-url origin https://github.com/Homebrew/homebrew-core.git unset HOMEBREW_BOTTLE_DOMAIN PS:\n如果之前装过 homebrew-cask 或 homebrew-services，需要把对应仓库的 remote URL 也改掉 运行 brew update 验证速度；若出现 fatal: couldn\u0026rsquo;t find remote ref 可删掉对应目录后 运行brew tap --force homebrew/core 重新拉取\n","date":"2025-08-17","externalUrl":null,"permalink":"/archives/homebrew-cnfast.html","section":"文章","summary":"","title":"Homebrew配置国内镜像加速","type":"posts"},{"content":"从ESXI迁移到PVE有多种方式，其中最简单的是网络迁移，通过PVE webUI管理界面中把ESXI节点主机添加进来(下图)， 这个不是今天讲的重点（因为我只有一台机器），今天讲的主要是通过 离线/导入的方式将ESXI虚拟机完整的迁移到PVE中。\n先别急着迁移，在开始之前呢，先来了解下虚拟机常用的磁盘格式以及虚拟机配置之间的一些差异，不然可能会出现一些错误。 之前写过的，虚拟机磁盘格式的介绍： 一文了解虚拟机磁盘镜像：从 0 到 1 看懂 VMDK、QCOW2、RAW 2025-07-08\u0026middot;1 分钟\u0026middot; 加载中 虚拟化 下面我把常见磁盘文件格式做了个总结，别搞错了。\n文件后缀 所属平台 本质/作用 迁移时必须带吗？ .vmdk VMware ESXi/Workstation 虚拟磁盘描述符（小文本文件，指向真正数据） ✅ 必须带，否则 qemu-img 报错 -flat.vmdk VMware 真正的磁盘数据（大块二进制） ✅ 必须与同名的 .vmdk 配对 .vmx VMware 虚拟机 配置文件（文本，记录 CPU/内存/网卡/磁盘控制器等） ❌ 不直接用于 PVE，但可以用来查“控制器类型、固件、磁盘顺序” .vmdk（单文件） VMware 描述+数据合一的“monolithic”格式 ✅ 直接当 .vmdk 用即可 .qcow2 KVM/QEMU/Proxmox 虚拟磁盘格式，支持快照、压缩、精简置备 ✅ PVE 推荐最终格式 .raw KVM/QEMU/Proxmox 裸镜像（= dd 出来的整块盘），无元数据 ⚠️ 占满空间大，不推荐 .ova/.ovf VMware/VirtualBox 打包的“导出模板”，内含 .vmdk + .ovf 描述 ⚠️ 需先解压提取 .vmdk 再导入 还有需要明确的是控制器总线是什么，操作系统以BIOS 还是UEFI方式启动的，错误的配置会导致迁移之后无法启动虚拟机\n需要确认的磁盘属性 为什么必须知道 不确认的代价 控制器总线（IDE / SATA / SCSI / NVMe） Windows 缺驱动会 0x7B 蓝屏，Linux 可能找不到根分区 直接黑屏/蓝屏 固件启动方式（BIOS vs UEFI） UEFI 系统用 SeaBIOS 启动 → 找不到 EFI 分区 永远停在 “Boot from Hard Disk…” 分区表格式（MBR vs GPT） 重建引导记录、调整 BIOS/UEFI 时必须知道 修复引导失败 好了 既然都看到这里了，想必应该都了解了吧，下面开始迁移\n首先将ESXI中的虚拟机导出，或者到虚拟机目录下载vmdk也可以（不建议），通过导出虚拟机会得到一个OVA/OVF 虚拟机模板配置文件，将其解压之后可以获得 vmdk 原始虚拟机磁盘文件。\n导出完成之后就可以愉快的安装PVE了，安装完成之后将虚拟磁盘文件采用SCP等方式上传到PVE中。 我觉得到这里你可能会发现一个问题，原来在 ESXI中部署的虚拟机明明选择了精简置备但还是占用了很大的空间（相当于厚置备 看下图），原因是导出vmdk之前的Thin 精简置备会自动变为flat，而且 ESXI下的 Thin 只对 VMFS层有效，对于外部工具（导入/导出）来说这还是一块厚置备的磁盘，我不是通过导出的ova解压得到的vmdk，我是通过群晖的AAB直接下载到PVE中的，与直接复制vmdk文件一样 这种情况怎么解决呢？\n使用 vmkfstools 先复制为 thin 然后再下载就可以了。\nvmkfstools -i xxx.vmdk /vmfs/volumes/nfs/export/xxx-thin.vmdk -d thin ```bash 或者你不嫌费劲你可以直接给200G的vmdk干到PVE里面去。利用`-o preallocation=off` 转换为 thin ```bash qemu-img convert \\ -p -f vmdk -O qcow2 \\ -o preallocation=off,compat=1.1,lazy_refcounts=on \\ /opt/Win2K12_1.vmdk \\ /var/lib/vz/images/${VMID}/vm-${VMID}-disk-x.qcow2 然后将磁盘挂载上去就可以了。\nqm set ${VMID} --scsihw virtio-scsi-pci --scsi0 ${STORE}:${VMID}/vm-${VMID}-disk-0.qcow2 最近有点忙，写的有点乱，后面在优化。。。\n","date":"2025-07-15","externalUrl":null,"permalink":"/archives/esxi-to-pve.html","section":"文章","summary":"","title":"从ESXi迁移到PVE  虚拟化平台迁移指南 以及遇到的一些问题","type":"posts"},{"content":"","date":"2025-07-11","externalUrl":null,"permalink":"/tags/openssh/","section":"标签","summary":"","title":"OpenSSH","type":"tags"},{"content":" 由于在OpenSSH 10 中 ，GSSAPIKexAlgorithms 配置选项可能被弃用的情况，建议升级之前注释掉 GSSAPI(通用安全服务应用程序接口) 相关的配置信息，否则可能会无法启动服务，已经在脚本中定义了，直接看脚本即可。 #!/bin/bash # OpenSSH 10.0 upgrade script # By wanghaoyu.com.cn # Supports AlmaLinux, Rocky Linux 8/9, CentOS 8/9, Ubuntu, and Debian RED=\u0026#39;\\033[0;31m\u0026#39; GREEN=\u0026#39;\\033[0;32m\u0026#39; YELLOW=\u0026#39;\\033[1;33m\u0026#39; NC=\u0026#39;\\033[0m\u0026#39; if [ \u0026#34;$(id -u)\u0026#34; -ne 0 ]; then echo -e \u0026#34;${RED}Error: Please use root to run this scripts!${NC}\u0026#34; exit 1 fi if [ -f /etc/redhat-release ]; then if grep -q \u0026#34;AlmaLinux\u0026#34; /etc/redhat-release; then OS=\u0026#34;almalinux\u0026#34; elif grep -q \u0026#34;Rocky Linux\u0026#34; /etc/redhat-release; then OS=\u0026#34;rocky\u0026#34; elif grep -q \u0026#34;CentOS\u0026#34; /etc/redhat-release; then OS=\u0026#34;centos\u0026#34; fi VERSION_ID=$(grep -oP \u0026#39;(?\u0026lt;=VERSION_ID=)\\d+\u0026#39; /etc/os-release) elif [ -f /etc/debian_version ]; then if grep -q \u0026#34;Ubuntu\u0026#34; /etc/os-release; then OS=\u0026#34;ubuntu\u0026#34; else OS=\u0026#34;debian\u0026#34; fi VERSION_ID=$(grep -oP \u0026#39;(?\u0026lt;=VERSION_ID=)\\d+\\.\\d+\u0026#39; /etc/os-release) else echo -e \u0026#34;${RED}Unsupported operating system.${NC}\u0026#34; exit 1 fi check_openssl_version() { local current_version=$(openssl version | awk \u0026#39;{print $2}\u0026#39;) local required_version=\u0026#34;1.1.1\u0026#34; if [ \u0026#34;$(printf \u0026#39;%s\\n\u0026#39; \u0026#34;$required_version\u0026#34; \u0026#34;$current_version\u0026#34; | sort -V | head -n1)\u0026#34; != \u0026#34;$required_version\u0026#34; ]; then echo -e \u0026#34;${RED}Error: Current OpenSSL version is $current_version, which is lower than required version $required_version. Please upgrade OpenSSL first.${NC}\u0026#34; exit 1 else echo -e \u0026#34;${GREEN}OpenSSL version check passed, current version is $current_version.${NC}\u0026#34; fi } install_dependencies() { echo -e \u0026#34;${YELLOW}Installing build dependencies...${NC}\u0026#34; case $OS in almalinux|rocky|centos) dnf groupinstall -y \u0026#34;Development Tools\u0026#34; dnf install -y wget zlib-devel openssl-devel pam-devel libselinux-devel ;; ubuntu|debian) apt-get update apt-get install -y build-essential wget zlib1g-dev libssl-dev libpam0g-dev libselinux1-dev ;; *) echo -e \u0026#34;${RED}Unsupported operating system.${NC}\u0026#34; exit 1 ;; esac } # backup ssh config backup_openssh() { echo -e \u0026#34;${YELLOW}Backing up current OpenSSH configuration...${NC}\u0026#34; cp -r /etc/ssh /etc/ssh.bak.$(date +%Y%m%d%H%M%S) } download_and_compile_openssh() { echo -e \u0026#34;${YELLOW}Downloading OpenSSH 10.0...${NC}\u0026#34; cd /usr/local/src wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-10.0p1.tar.gz tar -xzf openssh-10.0p1.tar.gz cd openssh-10.0p1 echo -e \u0026#34;${YELLOW}Compiling OpenSSH 10.0...${NC}\u0026#34; ./configure --prefix=/usr --sysconfdir=/etc/ssh --with-md5-passwords --with-pam --with-ssl-engine --with-privsep-path=/var/lib/sshd make -j$(nproc) make install } configure_sshd() { echo -e \u0026#34;${YELLOW}Configuring sshd service...${NC}\u0026#34; case $OS in almalinux|rocky|centos) systemctl restart sshd systemctl enable sshd ;; ubuntu|debian) systemctl restart ssh systemctl enable ssh ;; *) echo -e \u0026#34;${RED}Unsupported operating system.${NC}\u0026#34; exit 1 ;; esac } # Fix GSSAPIKexAlgorithms config fix_gssapi_config() { echo -e \u0026#34;${YELLOW}Fixing GSSAPIKexAlgorithms configuration...${NC}\u0026#34; if [ -f /etc/crypto-policies/back-ends/opensshserver.config ]; then sed -i \u0026#39;s/GSSAPIKexAlgorithms.*/#\u0026amp;/\u0026#39; /etc/crypto-policies/back-ends/opensshserver.config fi } verify_installation() { echo -e \u0026#34;${YELLOW}Verifying installation...${NC}\u0026#34; ssh -V } main() { echo -e \u0026#34;${GREEN}Starting OpenSSH upgrade to 10.0...${NC}\u0026#34; check_openssl_version install_dependencies backup_openssh download_and_compile_openssh fix_gssapi_config configure_sshd verify_installation echo -e \u0026#34;${GREEN}OpenSSH 10.0 upgrade completed.${NC}\u0026#34; } main ```bash 到这里就ok了 官方的openssh 下载可能比较慢，可以替换为国内镜像站中的地址 ","date":"2025-07-11","externalUrl":null,"permalink":"/archives/openssh10-update-in-bash.html","section":"文章","summary":"","title":"OpenSSH 10 一键升级脚本 支持目前主流的操作系统","type":"posts"},{"content":"硬盘就相当于是金属盒子，虚拟磁盘是“盒子里的盒子”。不同厂商为了把它塞进自家生态圈，给它起了各种英文缩写，所以，它 到底是什么，如果把磁盘想成一本书。\nRAW就是一本没有目录、没有页码的打印稿。读得快，但你要翻到第 237 页，只能一页一页数； VMDK 精装书，封面上写着“第 1 章在第 5 页、第 2 章在第 20 页”。目录丢了，书就废了（这就是 .vmdk 描述符）； QCOW2 可拆装的活页夹。新页可以随时插进去，旧页可以只记“修改”而不重印整本——所以能做快照、压缩； VDI / VHD / VHDX：其他出版社的活页夹，只是装订方式不同，有的夹页扣松，有的紧；\n为什么“薄”和“厚” （精简置备/厚置备） 厚置备：出版社一次印完 500 页空白纸，书脊就 500 页厚。 精简置备：先给你 10 张空白纸，写满了再去仓库拿。RAW 做不到这一点，因为它没有“仓库管理员”（元数据）\n我之前听别人说过，他说，“RAW 一定最快”。其实，一旦你把书放进快递柜（Filesystem Cache），活页夹也能飞起来。真正拖慢速度的是“翻目录”而不是“纸张”。在 SSD 上，QCOW2 与 RAW 的差距通常 \u0026lt; 5%\n总体来说意思就是 想要“一把梭”功能全：选 QCOW2。 想要“极限速度”：选 RAW，但先问问自己是否真的需要。 已经在 VMware 里：VMDK 是亲儿子，别折腾。 被 Hyper-V 绑架：VHDX 是唯一解。\n可能还有人会问，区别呢？\n区别？它们只是“同一本书”换了不同装帧方式，翻书的速度、能否拆页、能否只印空白页，全写在封面里 RAW：精装无目录，一口气印完，翻得快，但页页都是实体纸。 VMDK：目录在外，正文另放，VMware 亲儿子，目录丢了书就废。 QCOW2：活页夹+仓库管理员，随时插页、拆页、压缩、加密，KVM/PVE 最爱。 VDI：VirtualBox 的活页夹，小圈子够用。 VHD/VHDX：微软的活页夹，VHDX 升级后支持 64T 和断电保护。 区别只在“装帧方式”，内容都可以无损互换——只要用对工具。\n","date":"2025-07-08","externalUrl":null,"permalink":"/archives/vm-disk.html","section":"文章","summary":"","title":"一文了解虚拟机磁盘镜像：从 0 到 1 看懂 VMDK、QCOW2、RAW","type":"posts"},{"content":"","date":"2025-06-07","externalUrl":null,"permalink":"/cross.html","section":"Haoyu Wang's Blog","summary":"","title":"时光机","type":"page"},{"content":"","date":"2025-06-05","externalUrl":null,"permalink":"/tags/dnf/","section":"标签","summary":"","title":"DNF","type":"tags"},{"content":"","date":"2025-06-05","externalUrl":null,"permalink":"/tags/nginx/","section":"标签","summary":"","title":"Nginx","type":"tags"},{"content":"","date":"2025-06-05","externalUrl":null,"permalink":"/tags/yum/","section":"标签","summary":"","title":"YUM","type":"tags"},{"content":" 系统环境： 阿里云ECS (Rocky Linux 9.5 Kernel 5.14.0-503.35.1.el9_5.x86_64) CPU 2核心 内存 2GB SWAP 2GB 问题描述\n最近，我发现我的某台服务器在某个特定时间段内CPU和内存使用率异常升高，导致系统卡死，进而引发网站宕机，在这里给大家分享一下解决方案以及思路\n解决过程\n一开始，通过Grafana检查了系统的负载情况，发现CPU、内存和IO的使用率在特定时间段内显著升高。这种资源的突然增加通常指示有进程在后台执行资源密集型操作，然后发现systemd进程在这些时间段内占用了大量的系统资源。systemd是Linux系统中用于初始化系统组件和服务的系统管理器，它的资源占用通常不会如此之高，为了确定具体原因，检查了下系统日志。在系统重启前的几条日志中，发现了一条关键日志：dnf makecache 说明在系统宕机前，dnf包管理器正在执行makecache操作，即生成或更新软件包的元数据缓存，推断dnf makecache可能是导致系统资源占用过高的直接原因，因为dnf makecache操作在执行时会下载和解析大量的软件包元数据，这可能会在短时间内消耗大量的CPU和内存资源，尤其是在低配的主机中。 解决方案\n为了防止dnf makecache在不适当的时间自动执行，我才用了个最简单最暴力的方法，禁止自动更新\nsystemctl stop dnf-makecache.timer systemctl disable dnf-makecache.timer 这样就不会自动的去执行 makecache了，此问题到此得到解决~\n","date":"2025-06-05","externalUrl":null,"permalink":"/archives/86.html","section":"文章","summary":"","title":"解决在RHEL操作系统中因 dnf makecache 造成服务器死机问题","type":"posts"},{"content":"","date":"2025-05-19","externalUrl":null,"permalink":"/tags/grafana/","section":"标签","summary":"","title":"Grafana","type":"tags"},{"content":" 本文通过直接改grafana的sqlite数据库实现，首先在grafana服务机器上执行查找命令，找到 grafana的数据库文件grafana.db find / -iname \u0026#34;grafana.db\u0026#34; 找到之后使用 sqlite 工具，对数据库进行操作，修改为默认密码\ndnf install -y sqlite 修改密码为admin\nupdate user set password = \u0026#39;59acf18b94d7eb0694c61e60ce44c110c7a683ac6a8f09580d626f90f4a242000746579358d77dd9e570e83fa24faa88a8a6\u0026#39;, salt = \u0026#39;F3FAxVm33R\u0026#39; where login = \u0026#39;admin\u0026#39;; ","date":"2025-05-19","externalUrl":null,"permalink":"/archives/grafana-reset-pass.html","section":"文章","summary":"","title":"grafana 忘记登录密码 如何重置密码","type":"posts"},{"content":"","date":"2025-05-17","externalUrl":null,"permalink":"/tags/zabbix/","section":"标签","summary":"","title":"Zabbix","type":"tags"},{"content":" Zabbix7.x界面无法选择中文，通过安装语言包解决。后面也解决了zabbix7中文方块（乱码）问题,6版本也同样适用 安装中文语言包 # 配置中文语言包 系统中默认没有携带中文语言包，可以通过以下命令查看\nlocalectl list-locales #查看系统默认已有的语言包 localectl #查看当前生效的语言包 localectl set-locale LANG=\u0026#34;zh_CN.utf8\u0026#34; #修改当前生效语言包为中文zh_CN.utf8 查找软件仓库中可以下载的软件包，查询是否包含中文语言包。\ndnf list | grep glibc-langpack #查看源上有哪些语言包 dnf list | grep glibc-langpack | grep zh #筛选出需要的中文语言包 dnf install glibc-langpack-zh #安装中文语言包 localectl set-locale LANG=\u0026#34;zh_CN.utf8\u0026#34; #修改当前生效语言包为中文zh_CN.utf8 systemctl restart zabbix-server zabbix-agent nginx php-fpm # 重启相关服务 修改语言不需要重新启动操作系统，只需重新启动一下zabbix相关服务即可。 修复中文乱码问题 # 改语言包仅解决了整体页面的中文显示问题，具体监控项仍然存在中文方块乱码问题，需要下载中文字体解决。 将Windows的中文ttf字体 C:\\Windows\\Fonts目录下的字体上传至zabbix-server服务器，并且将字体改名为已经被套了几层软连接的默认字体名字DejaVuSans.ttf，不用重启服务器或应用，刷新页面即可。\ndnf install lrzsz #安装rz命令，方便快速上传字体文件 cd /usr/share/fonts/dejavu-sans-fonts/ #进入字体文件所在目录 rz -E #上传Windows上找到的ttf字体文件 cp DejaVuSans.ttf DejaVuSans.ttf.back #将之前的文件备份一下 mv ziti.ttf DejaVuSans.ttf #将上传的字体改名为目标文件 ","date":"2025-05-17","externalUrl":null,"permalink":"/archives/zabbix-chinese.html","section":"文章","summary":"","title":"Zabbix7.x配置中文界面 解决乱码问题","type":"posts"},{"content":"","date":"2025-05-02","externalUrl":null,"permalink":"/tags/active-backup/","section":"标签","summary":"","title":"Active Backup","type":"tags"},{"content":" 注意事项： # 要先下载安装好Active Backup for Business套件再操作。 SN码在【控制面板】 - 【信息中心】 -【产品序列号】。 建议复制到记事本内修改内容。 群晖的https是默认的5001端口，如果你的https端口号换过请自行修改：5001 为当前的端口号。 密码中只包含字母和数字，如果存在特殊符号，建议修改一下密码再操作。因为需要转换成其它字符集，我们也不知道按哪个标准，也懒得纠结的就去把密码改一下成不含特殊符号，回头改回来就行。这个比较省事比你去转换字符集强多了。 不能开启双重验证。 如果不成功可以尝试更换谷歌浏览器或火狐浏览器。 解锁教程 # 方法一专用工具（推荐）： # 注意：请使用火狐浏览器操作\n，只需要填写ip地址和用户名密码sn等等主要信息就可以自动解锁 访问：https://www.wanghaoyu.com.cn/tools/abb.html\n方法二 传统手动替换字符解锁方式： # 注意：请使用火狐浏览器操作，在浏览器地址栏中分别输入以下网址，替换地址内的ip地址端口和用户名密码sn信息。\nDSM6.x系统版本\n如果操作不成功，推荐更换火狐浏览器成功的概率更高，先把文本复制到记事本里面然后打开两个页面分别输入修改好的地址，然后两个页面来回刷新。 6.x\nhttps://群晖ip地址:https端口号/webapi/auth.cgi?api=SYNO.API.Auth\u0026amp;method=Login\u0026amp;version=1\u0026amp;account=群晖本地用户名\u0026amp;passwd=群晖本地密码 7.x\nhttps://群晖ip地址:https端口号/webapi/entry.cgi?api=SYNO.ActiveBackup.Activation\u0026amp;method=set\u0026amp;version=1\u0026amp;activated=true\u0026amp;serial_number=\u0026#34;群晖的SN码\u0026#34; DSM7.x系统版本 如果操作不成功，推荐更换火狐浏览器成功的概率更高，先把文本复制到记事本里面然后打开两个页面分别输入修改好的地址，然后两个页面来回刷新。\nhttps://群晖ip地址:https端口号/webapi/auth.cgi?api=SYNO.API.Auth\u0026amp;version=3\u0026amp;method=login\u0026amp;account=管理员用户名\u0026amp;passwd=密码\u0026amp;format= cookie 注意：cookie前面有英文空格，网页提示中有\u0026quot;success\u0026quot;:ture字样就是OK了。\nhttps://群晖ip地址:https端口号/webapi/entry.cgi?api=SYNO.ActiveBackup.Activation\u0026amp;method=set\u0026amp;version=1\u0026amp;activated=true\u0026amp;serial_number=\u0026#34;序列号\u0026#34; 网页提示success和activated同时为true时说明激活成功。\n","date":"2025-05-02","externalUrl":null,"permalink":"/archives/dsm-abb.html","section":"文章","summary":"","title":"黑群晖 Active Backup for Business 套件激活方法","type":"posts"},{"content":" RustDesk一键脚本 # RustDesk 一键脚本是一款专为简化 RustDesk 部署而设计的工具，开箱即用，面向非技术人群，它支持 Windows 和 Linux 系统，能够快速搭建 RustDesk 中继服务器，并支持通过 API 安装和 Web UI 管理，实现多端可用的远程桌面解决方案。 （一）多系统支持 # RustDesk 一键脚本提供了针对 Windows 和 Linux 系统的部署方案，同时也支持Docker部署，ARM架构处理器。在操作系统下，用户可以通过命令行运行安装脚本，根据向导快速完成 RustDesk 服务端的安装和配置，。\n（二）支持 API 安装与 Web UI 管理 # RustDesk 一键脚本支持通过 API 安装 RustDesk 服务端。此外，它还集成了 Web UI 管理界面，用户可以通过浏览器访问管理界面，使用 web ui 远程连接客户端设备。 （三）多端可用 # RustDesk 一键脚本搭建的远程桌面系统支持多端使用。用户可以在 Windows、Linux、macOS、iOS 和 Android 等多个平台上安装 RustDesk 客户端，并通过配置指向自建的中继服务器，实现跨平台的远程连接。\n（四）商业化支持 # 如果您想要和向日葵，Todesk一样，运营您的 Rustdesk，可以到关于页面联系我， 提供商业化技术支持，\n套餐购买 自助充值 到期时间限制 多用户管理 OAuth登陆 管理员订单管理 对接各大支付平台 (五) 脚本下载 # 本工具是经过精心开发和设计的成果，需要投入一定的成本和精力来维护与更新。因此，它并非免费提供。如果您对脚本感兴趣，希望您能理解并尊重其价值，感谢您的支持与理解！ \u0026lt;a class=\u0026quot;!rounded-md bg-primary-600 px-4 py-2 !text-neutral !no-underline hover:!bg-primary-500 dark:bg-primary-800 dark:hover:!bg-primary-700\u0026quot; href=\u0026ldquo;https://shop.nastool.club/\"\nrole=\u0026ldquo;button\u0026rdquo;\u0026gt; 下载地址 （六）使用说明 # 点我查看（密码请联系管理员获取） 二开自第三方API：https://github.com/lejianwen/rustdesk-api\n","date":"2025-04-29","externalUrl":null,"permalink":"/archives/rustdesk-sh.html","section":"文章","summary":"","title":"【RustDesk一键脚本】私有化跨平台远程桌面解决方案","type":"posts"},{"content":"","date":"2025-04-29","externalUrl":null,"permalink":"/tags/rustdesk/","section":"标签","summary":"","title":"Rustdesk","type":"tags"},{"content":" 引言 # 在使用 Nginx 作为负载均衡，反向代理服务器时，后端服务只能看到 Nginx 服务器的地址，不能看到真实的客户端IP，在Nginx 中有一个名为 X-Forward-For 的 Header，通常用于传递客户端的IP地址，但是这种方式存在一些安全问题，X-Forwarded-For 头信息可以被恶意用户伪造，从而绕过基于IP的访问限制，本文会将详细介绍如何在Nginx中配置，获取真实的客户端IP 地址，防止IP伪造，并确保后端能够获取到真实的客户端IP\nX-Forwarded-For # 当我们使用Nginx 获取客户端 真实IP地址时，我们一般会使用此方法\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 然后后端可以通过读取 Header 中的 X-Forwarded-For，获取客户端 IP 地址，但是 这个 Header是由客户端直接提供的，而且可能包含多个代理服务器的地址，这种情况可能会导致绕过基于IP地址的访问限制，记录错误的客户端IP地址，影响日志审计以及分析。\n解决方法 使用 X-Real-IP # X-Real-IP是 Nginx 设置的头信息，专门用于传递真实的客户端IP 地址，与X-Forwarded-For 不同的是，X-Real-IP 是由 Nginx 内部生成的，且客户端无法进行修改。\n配置教程 # 在 Nginx 的配置文件中 增加如下配置\nproxy_set_header X-Real-IP $remote_addr; # 传递真实的客户端IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加转发链 在日志格式中追加 $http_x_forwarded_for\nlog_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34; \u0026#34;$http3\u0026#34;\u0026#39;; 重启Nginx即可。\n原理图 ","date":"2025-04-28","externalUrl":null,"permalink":"/archives/nginx-transparent-ip.html","section":"文章","summary":"","title":"在Nginx中透传客户端真实IP地址","type":"posts"},{"content":"","date":"2025-04-26","externalUrl":null,"permalink":"/message.html","section":"Haoyu Wang's Blog","summary":"","title":"留言板","type":"page"},{"content":"","date":"2025-04-26","externalUrl":null,"permalink":"/archives.html","section":"Haoyu Wang's Blog","summary":"","title":"归档","type":"page"},{"content":" title: OpenSSL 版本问题导致 SSH 服务无法正常启动的修复方法 date: 2025-03-06 08:44:38 tags: OpenSSH categories: 运维 cover: https://img.wanghaoyu.com.cn/Picture/img/20250306092859236.png # 故障如上图所示，启动 ssh 服务时抛出如下错误 OpenSSL version mismatch build against xxxxx you have xxxxxx 这是因为 OpenSSL 与 OpenSSH 服务的版本号不匹配导致的此类问题，经检查 OpenSSL 版本高于 OpenSSH 所需要的版本。\n解决方案 # 升级OpenSSH服务\n检查并安装相关依赖以及编译工具 # sudo apt install build-essential checkinstall zlib1g-dev libpam0g-dev 编译安装 OpenSSH # 下载 OpenSSH 源码包并解压\nwget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.9p1.tar.gz tar -zxvf openssh-9.9p1.tar.gz cd openssh-9.9p1 mkdir build 使用 configure 脚本配置 OpenSSH\n./configure --prefix=$(pwd)/build --sysconfdir=$(pwd)/build/etc/ssh --with-ssl-dir=/usr --with-pam --with-md5-passwords 编译安装 OpenSSH\nsudo make 更新 sshd 二进制文件 # 需要先将旧版本的二进制文件备份一下\ncp -a /usr/sbin/sshd \u0026#34;/usr/sbin/.sshd_old_$(date +%Y%m%d_%H%M%S)\u0026#34; 将新编译的 sshd二进制文件 复制到 /usr/sbin/目录\ncd openssh-9.9p1/build cp build/sbin/sshd /usr/sbin/sshd -av chmod 755 /usr/sbin/sshd 验证版本号 # sshd -V 启动服务 # sshd -t systemctl restart sshd systemctl status sshd ","date":"2025-03-06","externalUrl":null,"permalink":"/archives/openssh-20250225.html","section":"文章","summary":"","title":"OpenSSL 版本问题导致 SSH 服务无法正常启动的修复方法","type":"posts"},{"content":"","date":"2025-02-18","externalUrl":null,"permalink":"/tags/iptables/","section":"标签","summary":"","title":"Iptables","type":"tags"},{"content":" iptables替代品 nftables # 我们在管理 linux上的防火墙规则时基本一直都在用的是iptables吧，最近发现了一位 ”新警长“ nftables ，从linux kernel 3.15版本开始 nftables优先级高于iptables，成为了默认的防护墙规则管理工具，比iptables更灵活，更高效，nftables与iptables都用于实现netfilter提供的钩子函数,用于真正处理网络数据包。\n下方表是目前不同操作系统版本以及默认的防火墙软件\n系统名称 版本 默认防火墙软件 备注 Ubuntu 20.04 iptables 默认使用 iptables Ubuntu 22.04 nftables 默认使用 nftables RHEL/CentOS 7 iptables 默认使用 iptables 不支持 nftables RHEL/CentOS 8-9 nftables 默认使用 nftables 可以通过安装 iptables 来兼容旧规则 簇 Family # nftables引入了簇的概念，统一了之前的xtables，簇是 nftables 的基础，定义了规则集可以操作的协议范围，iptables是通过不同的表，mangle filter等 进行区分\n簇名称 描述 ip 仅处理 ipv4 数据包 ip6 仅处理 ipv6 数据包 inet 双栈 同时处理数据包 arp 处理 arp 数据包 bridge 处理通过网桥设备的数据包 netdev 处理来自网络设备的数据包 表 Tables # 表是 nftables 用于存放链的容器，每个表可包含多个链，并且可以指定表的协议簇，在iptables中 表是预定义的，用户不能对其进行修改，但是在nftables中允许用户自定义表，而且表是创建规则管理的第一步\nnft add table inet my-table # 创建表 nft list tables\t# 列出表 nft delete table inet my-table # 删除表 链 chains # 链是表中的规则集合，用于处理特定类型的流量，在之前的iptables中，链也是预定义的，只能在现有链中添加规则，，nftables允许用户自定义链，nftables支持如下两种类型的链\n基本链： 与内核的网络钩子直接关联，例如 INPUT/OUTPUT/PEROUTING，实现过滤，地址转换等功能 常规链： 一般用于规则跳转,例如将流量从基本链跳转到常规链继续处理，以及模块化规则管理等 基本链的类型 # 类型 簇 钩子 描述 filter 所有簇 所有钩子 标准链类型 nat IPv4/IPv6/INET PREROUTING、INPUT、OUTPUT、POSTROUTING 用于网络地址转换 route IPv4/IPv6 OUTPUT 如果数据包通过此链并被允许，会执行新的路由查找，否则将被丢弃 钩子 hook # 钩子决定了链在网络数据被处理流程中的位置，nftables主要支持下方6种常用钩子\nINGRESS：处理进入网桥设备的数据包，仅支持 netdev 簇，但好像从 kernel5.x 版本开始 inet簇也支持ingress钩子了 PREROUTING：处理进入网络接口之前的数据 INPUT：处理进入网络接口的数据 FORWARD：处理经过网络接口转发的数据 OUTPUT：处理从本地系统发出的数据 POSTROUTING：处理离开网络接口之前的数据 优先级\n优先级决定了链在网络数据包处理流程中的执行顺序。优先级值越低,执行的越早，下表总结了 nftables 默认的优先级范围\n钩子 默认优先级范围 INGRESS -300 ~ -100 PREROUTING -300 ~ -100 INPUT 0 FORWARD 0 OUTPUT 0 POSTROUTING 100 ~ 300 nft add chain inet my-table my-chain { type filter hook input priority 0 \\; policy accept \\; } # 创建基本链 nft list chains #列出所有链 nft list chain inet my-table my-chain # 列出链中的规则 nft delete chain inet my-table my-chain # 删除链 nft add rule inet my-table input tcp dport 22 jump ssh_chain # 在基本链中跳转到常规链 nft add rule inet my-table ssh_chain accept nft add rule inet my-table ssh_chain drop # 在常规链中定义规则 规则 rules # 规则用于处理数据包的具体策略，例如允许或者拒绝目标端口的流量等，nftables的语法比iptables更简单也更灵活，也支持更为复杂的表达式，且支持动态插入。\n表达式 # 类型 字段 描述 meta oif \u0026lt;INDEX\u0026gt; 匹配输出接口的索引。 iif \u0026lt;INDEX\u0026gt; 匹配输入接口的索引。 oifname \u0026lt;NAME\u0026gt; 匹配输出接口的名称（动态匹配，速度较慢）。 iifname \u0026lt;NAME\u0026gt; 匹配输入接口的名称（动态匹配，速度较慢）。 icmp type \u0026lt;icmp type\u0026gt; 匹配 ICMP 类型（如 echo-request、echo-reply）。 icmpv6 type \u0026lt;icmpv6 type\u0026gt; 匹配 ICMPv6 类型（如 echo-request、echo-reply）。 ip protocol \u0026lt;protocol\u0026gt; 匹配 IP 协议类型（如 tcp、udp）。 daddr \u0026lt;address\u0026gt; 匹配目标 IP 地址。 saddr \u0026lt;address\u0026gt; 匹配源 IP 地址。 ip6 daddr \u0026lt;address\u0026gt; 匹配目标 IPv6 地址。 saddr \u0026lt;address\u0026gt; 匹配源 IPv6 地址。 tcp dport \u0026lt;port\u0026gt; 匹配目标 TCP 端口。 sport \u0026lt;port\u0026gt; 匹配源 TCP 端口。 udp dport \u0026lt;port\u0026gt; 匹配目标 UDP 端口。 sport \u0026lt;port\u0026gt; 匹配源 UDP 端口。 sctp dport \u0026lt;port\u0026gt; 匹配目标 SCTP 端口。 sport \u0026lt;port\u0026gt; 匹配源 SCTP 端口。 ct state \u0026lt;state\u0026gt; 匹配连接状态（如 new、established、related、invalid）。 动作 # 动作 描述 accept 接受数据包 drop 丢弃数据包 queue 将数据包排队到用户空间 continue 继续执行下一条规则 return 从当前链返回并继续上一个链的下一条规则 jump \u0026lt;chain\u0026gt; 跳转到指定链，执行完该链后返回原链 goto \u0026lt;chain\u0026gt; 跳转到指定链，执行完后不再返回原链 常用的一些规则配置\n# 1. 允许 SSH 流量 nft add rule inet my-table my_filter_chain tcp dport ssh accept # 2. 允许 HTTP 流量（插入到链的开头） nft insert rule inet my-table my_filter_chain tcp dport http accept # 3. 插入规则到指定位置（使用 index） nft insert rule inet my-table my_filter_chain index 1 tcp dport nfs accept # 4. 插入规则到指定位置（使用 handle） nft add rule inet my-table my_filter_chain handle 7 tcp dport 1234 accept # 5. 允许特定 IP 地址访问 nft add rule inet my-table my_filter_chain ip saddr 192.168.1.100 accept # 6. 丢弃所有 ICMP 流量 nft add rule inet my-table my_filter_chain icmp type echo-request drop # 7. 允许特定端口范围 nft add rule inet my-table my_filter_chain tcp dport 1000-2000 accept # 8. 使用连接跟踪允许已建立的连接 nft add rule inet my-table my_filter_chain ct state established accept # 9. 跳转到另一个链进行处理 nft add chain inet my-table ssh_chain nft add rule inet my-table my_filter_chain tcp dport ssh jump ssh_chain nft add rule inet my-table ssh_chain accept # 10. 删除规则（通过 handle） nft delete rule inet my-table my_filter_chain handle 8 # 11. 清空链中的所有规则 nft flush chain inet my-table my_filter_chain # 12. 列出链中的所有规则 nft list chain inet my-table my_filter_chain # 13. 设置链的默认策略为 drop nft chain inet my-table my_filter_chain { policy drop; } PS： 还没写完，后续在补充~\n参考：\nhttps://dev.to/farshad_nick/iptables-vs-nftables-whats-new-in-linux-firewalling-4a36 https://cryptsus.com/blog/setting-up-nftables-firewall.html https://docs.redhat.com/zh-cn/documentation/red_hat_enterprise_linux/8/html/securing_networks/getting-started-with-nftables_securing-networks#getting-started-with-nftables_securing-networks https://www.yisu.com/ask/313080.html https://icloudnative.io/posts/using-nftables/#nftables-vs-iptables https://blog.csdn.net/hougang/article/details/133393806 ","date":"2025-02-18","externalUrl":null,"permalink":"/archives/nftables.html","section":"文章","summary":"","title":"iptables替代品 nftables","type":"posts"},{"content":"","date":"2025-02-18","externalUrl":null,"permalink":"/tags/nftables/","section":"标签","summary":"","title":"Nftables","type":"tags"},{"content":"","date":"2025-02-17","externalUrl":null,"permalink":"/tags/containerd/","section":"标签","summary":"","title":"Containerd","type":"tags"},{"content":"最近尝试用 Containerd 去部署k8s集群,现在contailerd已经成为k8s推荐的容器运行时之一,这篇文章总结了一些 Containerd 常用的管理命令,先来看一下 Docker 与 Containerd 的区别,(内容来自https://cloud.tencent.com/developer/article/2327654)\n在 Kubernetes 提出 CRI 操作规范时，Docker刚拆出 containerd，并不支持 CRI 标准。由于当时Docker是容器技术最主流也是最权威的存在，Kuberentes虽然提出了CRI接口规范，但仍然需要去适配CRI与Docker的对接，因此它需要一个中间层或 shim 来对接 Kubelet 和 Docker 的 contianer runtime。 于是 kubelet 中加入了 Dockershim （shim为临时、兼容的意思）。使用 docker 作为 runtime 时，实际启动一个容器的过程是：\n在这个阶段 dockershim组件在Kubelet 的代码中，这也就意味着Dockershim是由K8S组织进行开发和维护！由于Docker公司的版本发布K8S组织是无法控制和管理，所以每次Docker发布新的Release，K8S组织都要集中精力去快速地更新维护Dockershim。 Kubernetes1.24版本正式删除和弃用dockershim。这件事情的本质是废弃了内置的 dockershim 功能，直接对接Containerd（后续已经支持 CRI）。这种方式更加标准，调用的链路更加的简洁。\n安装 containerd # yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sed -i \u0026#39;s+download.docker.com+mirrors.aliyun.com/docker-ce+\u0026#39; /etc/yum.repos.d/docker-ce.repo yum makecache fast yum install containerd -y systemctl enable --now containerd 生成默认配置文件\nsudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml 如果需要启用 systemd cgroup则编辑/etc/containerd/config.toml\n[plugins.\u0026#34;io.containerd.grpc.v1.cri\u0026#34;.containerd.runtimes.runc.options] SystemdCgroup = true # 设置为true 常用命令 # ctr # 功能 命令 查看镜像列表 ctr image ls 拉取镜像 ctr image pull 镜像名称:tag 推送镜像 ctr image push 镜像名称:tag 导入镜像 ctr image import 镜像文件 导出镜像 ctr image export 镜像文件 镜像名称:tag 查看容器列表 ctr container ls 或 ctr task ls 创建容器 ctr container create 镜像名称:tag 容器名称 启动容器 ctr task start 容器ID 停止容器 ctr task kill 容器ID 删除容器 ctr container rm 容器ID 查看容器信息 ctr container info 容器ID 查看命名空间列表 ctr ns ls crictl # 功能 命令 查看运行的容器 crictl ps 查看所有容器 crictl ps -a 查看镜像列表 crictl images 拉取镜像 crictl pull 镜像名称:tag 查看容器日志 crictl logs 容器ID 在容器中执行命令 crictl exec -i -t 容器ID command 启动容器 crictl start 容器ID 停止容器 crictl stop 容器ID 删除容器 crictl rm 容器ID 查看容器资源 crictl stats 容器ID 查看 Pod 列表 crictl pods 查看 Pod 详细信息 crictl inspect [PodID] ctr 与 crictl 区别 # ctr是 containerd 的原生命令行工具，直接与containerd交互\ncrictl是 k8s的CRI命令行工具,用于 k8s环境中的容器和Pod管理\n参考：\nhttps://cloud.tencent.com/developer/article/2327654 ","date":"2025-02-17","externalUrl":null,"permalink":"/archives/Containerd-usage.html","section":"文章","summary":"","title":"Containerd 常用命令以及Docker与Containerd的区别","type":"posts"},{"content":"","date":"2025-02-17","externalUrl":null,"permalink":"/tags/docker/","section":"标签","summary":"","title":"Docker","type":"tags"},{"content":"","date":"2025-02-13","externalUrl":null,"permalink":"/tags/nas/","section":"标签","summary":"","title":"Nas","type":"tags"},{"content":" 前言 # 前几年因为某些网盘的一些问题让一个名叫 NAS 的东西得到了广泛的普及，这种个人存储设备除了在家里使用外有不少用户使用各种方法实现在家外同样可以访问家中的存储设备，例如群晖的QC，飞牛的FN Connect等等，这些用起来确实很方便，但是也有很大的安全隐私风险，尤其是拥有公网Ip的用户，很多用户直接使用 DMZ 将内网NAS暴露到公网中，很多黑客会对某个Ip段的一些高危端口进行扫描攻击，密码穷举，例如 SMB的445端口，NFS的2049/111端口等，这些都是比较常见的高危端口，SMB协议曾因永恒之蓝漏洞被“WannaCry”勒索病毒利用，导致全球范围的大规模攻击。而NFS的111端口是RPC远程过程调用服务的默认端口，2049端口是NFS服务的默认端口，攻击者可通过这些端口获取NFS共享信息，进而进行未经授权的数据访问、信息泄露或未受监控的文件传输，还有 SSH，RDP端口，搭建过网站或者用过云服务器的大概都知道，只要你的主机开着22端口 3389端口，很大的几率会别人扫到并且对你进行弱密码攻击，，，更不用说 SMB NFS这种了。。\n如上图，这是我某台未关闭 22 端口的一台服务器，可以看到几乎每天都有不同的ip尝试暴力破解登录你的服务器。。。\n那，我们应该怎么防范呢\n基础的一些安全配置 # 非必要不开启 SSH 设置强密码 开启TOTP （2FA身份验证，双因素认证）尤其是管理员 停用没必要的账号 不使用公共电脑接入NAS 设置密码过期规则 开启路由器防火墙，抵御基础攻击 核心数据定时冷备 尽量不要用黑群晖，有很多潜在风险，不论是软件还是硬件 不要安装乱七八糟的软件，尽量通过Docker安装 公网访问的几种方法 # 方法1，端口转发(虚拟服务器)+ACL访问控制 # 如果您现在正在使用 DMZ，建议您换成端口转发的方式，只对外暴露需要的端口，而且不要用默认端口，例如群晖，要实现网页访问，可以将默认的5000/5001转发到另一个端口，启用HTTPS,这样只对外暴露WEB端口，如果还需要SMB/NFS,可以在放行对应端口同时设置 ACL 访问控制列表，或者设置允许访问IP列表，例如您需要在公司通过SMB/NFS访问家中的NAS,可以设置为仅允许公司里的IP访问，这样最简单，当然，灵活性也很低，如果更换了IP地址需要重新配置，而且多用户配置也比较麻烦，但是不推荐直接不做任何限制暴露在公网！\n方法2，使用防火墙(推荐) # 如果内网中有多个服务，例如 alist,emby等，需要多用户远程访问，，这里可以使用某些WEB防火墙，例如 雷池WAF,就拿 alist，emby来说，在安装好雷池之后，可以将雷池的443端口转发至公网的8443端口，只需要这一个端口就可以，使用域名区分访问的服务，配置好ddns，例如我设置了ddns域名为 ddns.example.com\n然后可以使用CNAME别名的方式将 alist.example.com,以及 emby.example.com CNAME 到 ddns.example.com\n在雷池中配置好SSL证书，创建对应的站点，反代到内网提供服务的端口即可,这种方式可以有效利用单个端口，避免为每个服务单独暴露端口带来的安全风险,而且有WAF当作网关进行安全防护。\n方法3、自建虚拟局域网,远程回家（推荐） # 除了上面两种方法之外，最好用的还是通过虚拟局域网的方式，打通客户端与家里的网络，实现内网访问，无需端口转发等操作，这在很多大型企业都有应用，例如公司总部在深圳，分部在上海，，分部需要访问总部内的某些资源，例如OA，ERP等，可以通过虚拟局域网打通两边的网络，实现跨地域访问，例如在大型企业中比较常用的是 MPLS VPN IPSEC VPN，SD-WAN等，那我们个人用或者小团队用，可以选择使用 OpenVPN，以及AnyLink 进行异地组网的实现，打通客户端到家中的网络，实现内网访问，还有一个ZeroTier,这个也可以实现异地组网，但是由于运营商因为某些原因封堵UDP协议，这个并不怎么可靠。。\n大概就这些，详细内容以后再补充~\n","date":"2025-02-13","externalUrl":null,"permalink":"/archives/nas-secure.html","section":"文章","summary":"","title":"你的NAS真的安全吗? 如何确保NAS的安全性","type":"posts"},{"content":"","date":"2025-02-12","externalUrl":null,"permalink":"/tags/bitwarden/","section":"标签","summary":"","title":"Bitwarden","type":"tags"},{"content":"相信大家都用过很多密码管理器，例如使用 Google Edge 浏览器登录了某个网站，提示是否保存密码，方便下次登陆，如果您点击保存密码他会将您的账号保存到 Google，或者 Microsoft账号中，但是这仅限于浏览器中，而且数据不在自己手中，现在我给大家推荐一个可以私有化部署，全平台自动同步的密码管理软件，Bitwarden，下面是详细搭建教程\n环境准备 # 首先需要安装 Docker，如果不会安装请参考:Docker安装教程\n搭建 # 这里使用的是 Rust 编写的非官方 Bitwarden 兼容服务器，以前称为 bitwarden_rs\nGitHub:https://github.com/dani-garcia/vaultwarden\n使用Docker进行安装\ndocker run --detach --name vaultwarden \\ --volume /data/vaultwarden/:/data/ \\ --restart unless-stopped \\ --publish 81:80 \\ vaultwarden/server:latest /data/vaultwarden/为持久化数据保存目录\n如果使用 compose进行安装\nservices: vaultwarden: image: vaultwarden/server:latest container_name: vaultwarden restart: unless-stopped volumes: - ./data/vaultwarden/:/data/ ports: - 81:80 然后执行 docker compose up -d\n配置反向代理 # 配置反向代理实现无需带端口访问，并启用https\n这里用nginx来实现的,下面是一个示例配置\nserver { listen 443 ssl; server_name wanghaoyu.com.cn; # 你的域名 ssl_certificate certificate.pem; #你的SSL证书 ssl_certificate_key private.key; #你的SSL私钥 location / { proxy_pass http://127.0.0.1:81; # 反代到vaultwarden端口 proxy_set_header Host $host; 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;（http或https） proxy_redirect off; } } 配置完成之后登录 web页面创建对应的账号即可。\n客户端配置 # 谷歌浏览器插件 安卓客户端(谷歌商店) IOS 以谷歌浏览器插件为例\n安装完成之后从插件列表找到 Bitwarden 然后选择自托管\n填写你的服务器 URL 并保存，其他的不用填\n然后直接登录就可以了\n","date":"2025-02-12","externalUrl":null,"permalink":"/archives/bitwarden-install.html","section":"文章","summary":"","title":"自建Bitwarden密码管理器","type":"posts"},{"content":"","date":"2025-02-04","externalUrl":null,"permalink":"/tags/hexo/","section":"标签","summary":"","title":"Hexo","type":"tags"},{"content":"最近“心血来潮” 将博客网站迁移到了 Hexo，但是 Hexo 部署到 服务器后，由于我是多服务器+Vercel的环境，为了方便，就想了一个办法，实现了与Vercel一样的功能，使用 GitHub WebHook ，下面介绍一下实现过程\n1. 先介绍一下什么是什么是 Webhook # Webhook 是一种基于 HTTP 回调的机制，允许应用在特定事件发生时向指定 URL 发送 POST 请求，从而触发后续操作。例如，GitHub Webhook 可在代码推送时触发，这里我设置了一台主服务器，通知服务器通过脚本拉取最新代码完成部署，然后同步到其他服务器中\n2. Webhook 服务器配置 # 这里为了方便使用 Express写了个 Webhook 服务器，监听 GitHub 推送事件，并执行部署脚本。\n2.1 安装 Node.js 环境 # 可以参考我的另一篇文章： https://www.wanghaoyu.com.cn/archives/linux-nodejs-deploy.html?\n2.2 创建 Webhook 服务器 # 在你的hexo目录下安装express以及相关依赖\npnpm install express crypto child_process fs 2.3 编写 Webhook 服务器代码 # 创建 server.js，代码如下：\nconst express = require(\u0026#34;express\u0026#34;); const crypto = require(\u0026#34;crypto\u0026#34;); const { exec } = require(\u0026#34;child_process\u0026#34;); const fs = require(\u0026#34;fs\u0026#34;); const app = express(); const PORT = 5000; const SECRET = \u0026#34;secret\u0026#34;; // 在 GitHub Webhook 配置中填写相同的 Secret const STATUS_FILE = \u0026#34;./deploy_status.json\u0026#34;; app.use(express.json()); const getLastDeployStatus = () =\u0026gt; { if (fs.existsSync(STATUS_FILE)) { return JSON.parse(fs.readFileSync(STATUS_FILE, \u0026#34;utf8\u0026#34;)); } return { last_deploy: \u0026#34;Never\u0026#34;, status: \u0026#34;Not deployed yet\u0026#34; }; }; app.get(\u0026#34;/\u0026#34;, (req, res) =\u0026gt; { res.json({ message: \u0026#34;Server is running\u0026#34;, status: \u0026#34;ok\u0026#34; }); }); app.get(\u0026#34;/deploy-status\u0026#34;, (req, res) =\u0026gt; { res.json(getLastDeployStatus()); }); app.post(\u0026#34;/post-receive\u0026#34;, (req, res) =\u0026gt; { const signature = req.headers[\u0026#34;x-hub-signature-256\u0026#34;]; const hmac = crypto.createHmac(\u0026#34;sha256\u0026#34;, SECRET); const digest = `sha256=${hmac.update(JSON.stringify(req.body)).digest(\u0026#34;hex\u0026#34;)}`; if (signature !== digest) { return res.status(403).json({ error: \u0026#34;Invalid signature\u0026#34; }); } exec(\u0026#34;sh deploy.sh\u0026#34;, (err, stdout, stderr) =\u0026gt; { const status = { last_deploy: new Date().toISOString(), status: err ? \u0026#34;Failed\u0026#34; : \u0026#34;Success\u0026#34;, output: err ? stderr : stdout }; fs.writeFileSync(STATUS_FILE, JSON.stringify(status, null, 2)); res.json({ message: \u0026#34;Deployment started\u0026#34;, output: stdout }); }); }); app.listen(PORT, () =\u0026gt; console.log(`Webhook server running on port ${PORT}`)); 这里的 sh deploy.sh 是触发这个webhook后执行的命令，在这里你可以编写一个 hexo 构建静态文件并放到 web 目录的脚本，\n#!/bin/bash WORKDIR=/opt/xxxxx WWWDIR=/var/www/html/ cd $WORKDIR hexo clean hexo generate rsync -av --delete $WORKDIR/public/ $WWWDIR chown -R www:www $WWWDIR chmod 755 -R $WWWDIR 3. 运行 Webhook 服务器 # 使用 node server.js 运行服务器，或者使用 PM2 进行守护进程管理：\nnpm install -g pm2 pm2 start server.js --name webhook-server pm2 save pm2 startup 4. 配置 GitHub Webhook # 4.1 进入 GitHub 仓库设置 # 打开目标仓库 进入 Settings -\u0026gt; Webhooks 点击 Add webhook 4.2 填写 Webhook 配置 # Payload URL: http://ip:port/post-receive Content type: application/json Secret: 填写与 server.js 中 SECRET 相同的值 Events: 选择 Just the push event 点击 Add webhook 保存。\n5. 调试与测试 Webhook # 5.1 手动测试 Webhook # 可使用 curl 发送 POST 请求测试 Webhook 服务器：\ncurl -X POST https://ip:port/post-receive \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;ref\u0026#34;: \u0026#34;refs/heads/main\u0026#34;}\u0026#39; 如果 Webhook 服务器返回 { \u0026quot;message\u0026quot;: \u0026quot;Deployment started\u0026quot; }，说明 Webhook 服务器正常运行。\n5.2 查看 GitHub Webhook 日志 # 在 GitHub -\u0026gt; Webhooks 页面，检查 Webhook 是否成功\n6. 配置 Nginx 反向代理（可选） # 在服务器 nginx.conf 配置 Webhook 反向代理：\nserver { listen 80; server_name wanghaoyu.com.cn; location /post-receive { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; 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; } } 然后重启 Nginx 就可以了，，，\nnginx -t nginx -s reload 效果：\ntail -f /var/log/hexo_deploy.log [2025-02-04 02:05:32] ✅ Hexo 部署成功！ [2025-02-04 02:05:32] 🚪 部署完成，退出程序 [2025-02-04 17:29:55] 🚀 开始部署 Hexo [2025-02-04 17:29:55] 📂 拉取 Git 最新代码... [2025-02-04 17:29:59] Updating cc1b055..d5c5c49 Fast-forward source/_posts/typecho-migrate-to-hexo.md | 177 +++++++++++++++++++++++++++++++ source/about/index.md | 2 +- 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 source/_posts/typecho-migrate-to-hexo.md [2025-02-04 17:29:59] 🧹 清理 Hexo 旧文件... [2025-02-04 17:30:03] INFO Validating config INFO Deleted database. INFO Deleted public folder. [2025-02-04 17:30:03] 🛠 生成 Hexo 静态页面... [2025-02-04 17:30:15] INFO Validating config INFO Start processing INFO Files loaded in 7.4 s INFO Generated: atom.xml INFO Generated: search.xml INFO Generated: sitemap.xml INFO Generated: about/index.html INFO Generated: categories/index.html INFO Generated: tags/index.html INFO Generated: tools/index.html ......此处省略 INFO 190 files generated in 3.92 s [2025-02-04 17:30:15] 🚚 复制文件到 Web 目录... [2025-02-04 17:30:16] sending incremental file list ./ 404.html atom.xml index.html search.xml sitemap.xml about/ about/index.html archives/ archives/Hexo-install.html archives/Kubernetes-architecture-and-data-flow.html ......此处省略 sent 8,200,909 bytes received 4,701 bytes 5,470,406.67 bytes/sec total size is 8,181,749 speedup is 1.00 [2025-02-04 17:30:16] 🔒 设置 /var/www/html 权限为 www:www 755 [2025-02-04 17:30:16] ✅ 权限设置成功 [2025-02-04 17:30:16] ✅ Hexo 部署成功！ [2025-02-04 17:30:16] 🚪 部署完成，退出程序 这里这个脚本我用 Python写的，配合smtp服务实现状态通知，\n最后说一句； All IN JavaScript！ (手动狗头)\n","date":"2025-02-04","externalUrl":null,"permalink":"/archives/typecho-migrate-to-hexo.html","section":"文章","summary":"","title":"Hexo Deploy 使用 GitHub WebHook 更新文章","type":"posts"},{"content":"","date":"2025-02-04","externalUrl":null,"permalink":"/tags/node.js/","section":"标签","summary":"","title":"Node.js","type":"tags"},{"content":"","date":"2025-02-01","externalUrl":null,"permalink":"/tags/ollama/","section":"标签","summary":"","title":"Ollama","type":"tags"},{"content":"DeepSeek-R1 作为一款性能卓越的开源推理模型，凭借其在数学、代码和逻辑推理任务上的出色表现，吸引了众多开发者的关注，这篇文章带大家使用 Ollama 部署 DeepSeek-R1 本地模型\n目录 # [TOC]\n首先 我们需要了解一下，什么是 Ollama ？\nOllama 简介 # Ollama 是一个快速、轻量且易于使用的开源 AI 代理框架，由 Facebook 开发，可以用来托管和运行各种语言模型（LLM），可以实现在您的硬件设备快速部署各种本地大模型，并且提供了一个简单命令行界面，方便用户快速部署。\n既然现在已经了解了 Ollama是干嘛的，那么接下来就是在你的操作系统中安装 Ollama了\n安装Ollama # Windows # 访问 Ollama官网 下载Windows版本安装即可\nLinux 操作系统 # 在你的终端中执行如下命令\ncurl -fsSL https://ollama.com/install.sh | sh Docker安装 # 在 Linux操作系统中也可以通过 Docker进行安装 Ollama 如果您需要配置 Ollama 的一些选项（例如内存限制、日志级别等），可以在运行时指定：\ndocker run -p 11434:11434 --name ollama \\ -e \u0026#34;OLLMARPC_LMAX_MEMORY=2GB\u0026#34; \\ -e \u0026#34;OLLMARPC_LOG_LEVEL=INFO\u0026#34; \\ ollama/ollama:latest 安装（启动完成之后） 可以通过命令行工具使用 Ollama 了\n注意： # 默认 Ollama 会使用您的 CPU 来运行模型，而并非 GPU，对于那种比较小的模型用CPU+集成显卡也能较好的进行工作，如果您的计算机中有 AMD 或者 Nvidia 独立显卡，并且您想运行更大的模型或更快的响应速度，您需要安装CUDA Toolkit以更好地利用独立显卡\nOllama 支持的 Nvidia GPU # Compute Capability Family Cards 9.0 NVIDIA H100 8.9 GeForce RTX 40xx RTX 4090 RTX 4080 RTX 4070 Ti RTX 4060 Ti NVIDIA Professional L4 L40 RTX 6000 8.6 GeForce RTX 30xx RTX 3090 Ti RTX 3090 RTX 3080 Ti RTX 3080 RTX 3070 Ti RTX 3070 RTX 3060 Ti RTX 3060 NVIDIA Professional A40 RTX A6000 RTX A5000 RTX A4000 RTX A3000 RTX A2000 A10 A16 A2 8.0 NVIDIA A100 A30 7.5 GeForce GTX/RTX GTX 1650 Ti TITAN RTX RTX 2080 Ti RTX 2080 RTX 2070 RTX 2060 NVIDIA Professional T4 RTX 5000 RTX 4000 RTX 3000 T2000 T1200 T1000 T600 T500 Quadro RTX 8000 RTX 6000 RTX 5000 RTX 4000 7.0 NVIDIA TITAN V V100 Quadro GV100 6.1 NVIDIA TITAN TITAN Xp TITAN X GeForce GTX GTX 1080 Ti GTX 1080 GTX 1070 Ti GTX 1070 GTX 1060 GTX 1050 Quadro P6000 P5200 P4200 P3200 P5000 P4000 P3000 P2200 P2000 P1000 P620 P600 P500 P520 Tesla P40 P4 6.0 NVIDIA Tesla P100 Quadro GP100 5.2 GeForce GTX GTX TITAN X GTX 980 Ti GTX 980 GTX 970 GTX 960 GTX 950 Quadro M6000 24GB M6000 M5000 M5500M M4000 M2200 M2000 M620 Tesla M60 M40 5.0 GeForce GTX GTX 750 Ti GTX 750 NVS 810 Quadro K2200 K1200 K620 M1200 M520 M5000M M4000M M3000M M2000M M1000M K620M M600M M500M Ollama 支持的 AMD GPU # Family Cards and accelerators AMD Radeon RX 7900 XTX 7900 XT 7900 GRE 7800 XT 7700 XT 7600 XT 7600 6950 XT 6900 XTX 6900XT 6800 XT 6800 Vega 64 Vega 56 AMD Radeon PRO W7900 W7800 W7700 W7600 W7500 W6900X W6800X Duo W6800X W6800 V620 V420 V340 V320 Vega II Duo Vega II VII SSG AMD Instinct MI300X MI300A MI300 MI250X MI250 MI210 MI200 MI100 MI60 MI50 详情参考：https://github.com/qianniucity/ollama-doc/blob/main/ollama/docs/Ollama%20%E5%AF%B9GPU%20%E6%94%AF%E6%8C%81%E4%BF%A1%E6%81%AF.md\nOllama 选择使用 GPU 上运行 # 如果希望 Ollama 使用 GPU 加速，需要确保已正确安装 GPU 驱动和相关库（如 CUDA 或 ROCm），然后可以通过以下方法指定 GPU，我这里有4张老黄家的 A10，所以我们要在启动之前配置好环境变量 例如\nexport CUDA_VISIBLE_DEVICES=0,1,2,3 # 这条命令会使用 所有4张 GPU 如果只有一张GPU 那么就是export CUDA_VISIBLE_DEVICES=0\n如果是AMD CPU 参考这个,配置 HIP_VISIBLE_DEVICES 环境变量来指定要使用的 GPU,例如\nHIP_VISIBLE_DEVICES=0 到这里 Ollama就基本配置完毕了，接下来就是运行模型\n下面是 DeepSeek R1 模型的配置参考,以及运行方式，这里我用表格整理出来了\n模型名称 模型大小 运行命令 硬件配置 DeepSeek-R1 671B ollama run deepseek-r1:671b 需要极高的硬件配置，显存需求超过336GB DeepSeek-R1-Distill-Qwen-1.5B 1.5B ollama run deepseek-r1:1.5b 最低配置：8GB RAM，无显卡加速；适合老旧设备 DeepSeek-R1-Distill-Qwen-7B 7B ollama run deepseek-r1:7b 最低配置：16GB RAM，8GB显存（GPU加速） DeepSeek-R1-Distill-Llama-8B 8B ollama run deepseek-r1:8b 最低配置：16GB RAM，8GB显存（GPU加速） DeepSeek-R1-Distill-Qwen-14B 14B ollama run deepseek-r1:14b 最低配置：32GB RAM，26GB显存（GPU加速） DeepSeek-R1-Distill-Qwen-32B 32B ollama run deepseek-r1:32b 最低配置：64GB RAM，64GB显存（GPU加速） DeepSeek-R1-Distill-Llama-70B 70B ollama run deepseek-r1:70b 最低配置：128GB RAM，140GB显存（GPU加速） 运行 DeepSeek R1 模型 # 根据上方表格的说明运行模型即可，例如，我用的是4张 A10 那么我完全可以跑 32B模型，可以通过ollama run deepseek-r1:32b 来启动该模型。\n运行示例： 使用 API 调用 本地模型 # curl -X POST http://localhost:11434/api/generate \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;prompt\u0026#34;: \u0026#34;hello\u0026#34;}\u0026#39; 会返回一个包含生成文本的 JSON 格式结果\n{ \u0026#34;response\u0026#34;: \u0026#34;你好，我是 DeepSeek-R1。有什么我可以帮您的吗？\u0026#34;, \u0026#34;error\u0026#34;: null } 参考链接：\nhttps://developer.nvidia.com/cuda-gpus https://ollama.com/ https://github.com/ollama/ollama/tree/main/docs ","date":"2025-02-01","externalUrl":null,"permalink":"/archives/Ollama-deepseek-r1.html","section":"文章","summary":"","title":"本地部署 DeepSeek-R1 模型全攻略","type":"posts"},{"content":"","date":"2025-01-19","externalUrl":null,"permalink":"/tags/helm/","section":"标签","summary":"","title":"Helm","type":"tags"},{"content":"在 Kubernetes 集群中，可以通过 Helm 部署 nfs-subdir-external-provisioner 来动态管理持久卷，这是一个存储资源自动调配器，可以将现有的 NFS 服务器通过持久卷声明来支持 持久卷的动态分配\n前置条件： # NFS服务器正常运行并且能够与集群通信 安装 Helm 集群正常运行 添加仓库 # 将 nfs-subdir-external-provisioner 的 Helm 仓库添加到本地\nhelm repo add nfs-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner helm repo update 部署 # 下载nfs-subdir-external-provisioner的 chart 到本地\nhelm pull nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --untar 编辑其目录中的 values.yaml 下面是示例，，\ncat values.yaml | grep -v \u0026#34;#\u0026#34; replicaCount: 1 strategyType: Recreate image: repository: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner tag: v4.0.0 pullPolicy: IfNotPresent imagePullSecrets: [] nfs: server: 10.0.80.253 # nfs服务端ip地址 path: /data # nfs共享目录 mountOptions: volumeName: nfs-subdir-external-provisioner-root reclaimPolicy: Retain storageClass: create: true defaultClass: true name: nfs-default # Storage Class的名字 ....... # 下面的内容省略 使用自定义的values.yaml进行部署,这里我选择了创建一个独立的namespace便于管理\nhelm install nfs-provisioner ./nfs-subdir-external-provisioner -f values.yaml -n nfs-provisioner --create-namespace 验证并测试 # 检查存储类是否创建成功\nkubectl get storageclass 测试动态卷分配\n创建 testpvc.yaml 并应用\napiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-claim spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi storageClassName: custom-nfs kubectl apply -f testpvc.yaml -n nfs-provisioner 验证 PVC 是否绑定成功\nkubectl get pvc -n nfs-provisioner 接下来可以起个pod进行验证\napiVersion: v1 kind: Pod metadata: name: test-nfs-provisioner spec: containers: - name: busybox image: busybox command: [\u0026#34;/bin/sh\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;echo \u0026#39;nfstest\u0026#39; \u0026gt; /mnt/testfile \u0026amp;\u0026amp; sleep 3600\u0026#34;] volumeMounts: - name: test-storage mountPath: /mnt volumes: - name: test-storage persistentVolumeClaim: claimName: test-claim kubectl apply -f testpod.yaml -n nfs-provisioner pod正常运行之后可以登录到nfs服务器查看到共享目录生成了一个目录和文件表明成功。。\n","date":"2025-01-19","externalUrl":null,"permalink":"/archives/helm-nfs-subdir-external-provisioner.html","section":"文章","summary":"","title":"Helm 部署 nfs-subdir-external-provisioner","type":"posts"},{"content":"","date":"2025-01-19","externalUrl":null,"permalink":"/tags/kubernetes/","section":"标签","summary":"","title":"Kubernetes","type":"tags"},{"content":"","date":"2025-01-17","externalUrl":null,"permalink":"/tags/lvm/","section":"标签","summary":"","title":"Lvm","type":"tags"},{"content":" 1、合并存储 # 删除local-lvm存储空间 # 首先，需要删除local-lvm存储空间中的逻辑卷\nlvremove pve/data 将释放出来的空间合并到pve/root逻辑卷中\nlvextend -l +100%FREE -r pve/root 这样会将所有空闲的空间（+100%FREE）扩展到pve/root逻辑卷中，-r`参数会自动调整文件系统的大小，以匹配逻辑卷的新大小。\nweb界面删除local-lvm # 数据中心-存储-删除local-lvm\n编辑local，内容里添加 磁盘映像和容器就可以了\n注意：在执行删除以及合并操作之前务必先检查 local-lvm 中是否存在现有虚拟机，一旦删除数据无法恢复，如果物理磁盘较小可以将虚拟机的镜像文件copy出来然后在执行操作\n","date":"2025-01-17","externalUrl":null,"permalink":"/archives/pve-local-lvm.html","section":"文章","summary":"","title":"PVE删除Local-lvm存储空间并合并到local中","type":"posts"},{"content":"","date":"2025-01-14","externalUrl":null,"permalink":"/tags/bash/","section":"标签","summary":"","title":"Bash","type":"tags"},{"content":"","date":"2025-01-14","externalUrl":null,"permalink":"/tags/shell/","section":"标签","summary":"","title":"Shell","type":"tags"},{"content":"最近写了一个shell脚本，，是在Windows系统下写的，编写完成后复制到Linux系统中执行时报错了，大概是这样，，，\nbuild.sh: line 3: $\u0026#39;\\r\u0026#39;: command not found : invalid option set: - set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...] build.sh: line 6: $\u0026#39;\\r\u0026#39;: command not found build.sh: line 11: $\u0026#39;\\r\u0026#39;: command not found build.sh: line 18: $\u0026#39;\\r\u0026#39;: command not found build.sh: line 20: syntax error near unexpected token `$\u0026#39;{\\r\u0026#39;\u0026#39; \u0026#39;ild.sh: line 20: `setup_install() { 原因是换行符格式问题导致的，Windows系统中用的是 \\r\\n 换行，Linux系统则用 \\n,在Linux 系统中会将 Windows风格的换行符中的 \\r 视为普通字符，从而导致错误，，\n解决方法很简单，使用sed 删除每一行末尾的 \\r 就可以了\nsed -i -e \u0026#39;s/\\r$//\u0026#39; build.sh 或者用dos2unix工具进行转换，dos2unix build.sh\n","date":"2025-01-14","externalUrl":null,"permalink":"/archives/bash-20250114.html","section":"文章","summary":"","title":"解决Bash 脚本换行符问题","type":"posts"},{"content":"","date":"2025-01-01","externalUrl":null,"permalink":"/tags/ddns-go/","section":"标签","summary":"","title":"Ddns-Go","type":"tags"},{"content":"","date":"2025-01-01","externalUrl":null,"permalink":"/tags/%E5%8A%A8%E6%80%81%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90/","section":"标签","summary":"","title":"动态域名解析","type":"tags"},{"content":" 部署ddns-go，轻松访问家里nas # 这几天有小伙伴问我，入坑了飞牛NAS，如何解决远程访问的问题，如果要想在外面访问家里的nas，要么就是通过内网穿透或者其他各种异地组网的方式来访问nas，例如通过OpenXXX，CloudFlare，自建FRP,NPS等内网穿透工具进行访问，或者是通过IPV4/IPV6直接连接家里设备，目前家里有公网V4以及V6地址，但是宽带一旦短线重连就无法通过原有的IP地址访问，因为IP地址是动态的而非静态IP，一旦重连IP就会发生变化，有些地区可能24小时就会强制下线重新过去地址，这篇文章将介绍一下如何使用DDNS-GO动态域名解析来解决IP地址变动的问题。\n前置条件 # 需要准备一个域名，国外注册商推荐将dns设置为cloudflare使用，国内注册商推荐使用阿里云，不推荐使用腾讯云。\n安装DDNS-GO # 下面我会用飞牛OS进行演示\n在飞牛的 Docker 套件中搜索 DDNS-GO 并下载，镜像标签默认即可\n下载完成后在本地镜像中去创建该容器，并设置为开机自启\n端口如果没有特殊需求的话保持默认即可\n创建成功就可以访问你的 FnOS的IP地址:9876 访问DDNS-GO的管理页面了\n打开的时候会先让你初始化管理员账号\n这里我还是以阿里云为例，到https://ram.console.aliyun.com/profile/access-keys 创建 Access Key\n创建完成之后会给你一个 ID 以及 Secret,将其完整填写到 DDNS-GO的对应选项中\n完事之后我们需要先在你的域名添加一条解析记录，ip随便填，保证存在即可，假如你想将 ddns.example.com配置为动态域名解析，那么我们就先需要在你的域名解析中添加一条A记录或者AAAA记录，如果你用的IPV6那就是AAAA记录，IP随便设置，下面以IPV4为例\n添加完成之后回到DDNS-GO，将你刚刚添加的ddns.example.com 填入到 IPV4的配置中去并保存，并通过日志以及检查域名解析中的配置是否正常并解析到你的公网IP地址中。\n如上图所示，记录值变成你的公网IP这样就可以利用你的域名进行访问了，且Ip发生变动值ddns-go会自动将最新的ip解析到指定域名中。\n","date":"2025-01-01","externalUrl":null,"permalink":"/archives/ddns-go.html","section":"文章","summary":"","title":"使用ddns-go动态域名解析，轻松访问内网设备","type":"posts"},{"content":"首先需要准备一个域名，并将其指定到你的 rustdesk 服务器 ip 地址上，这里编译采用的是Github Actions ，说白了是就workflows，可以创建一些自动化的工作流程，例如代码的检查，测试，构建，发布等，如果对此不清楚的话建议先去稍微的那么学习一下\n我们需要将官方仓库作为模板导入你的仓库中，\n复制官方的github仓库地址：https://github.com/rustdesk/rustdesk/，将其导入到你的github中\n找到你的仓库并配置Actions权限\n为 Actions 配置工作流\n这里我们需要定义三个变量,\nRENDEZVOUS_SERVERS ID服务器 RS_PUB_KEY 你的KEY API_SERVER API服务器 接下来在仓库的 Actions 中找到 Flutter Nightly build,点击Run workflow，在这里你可以选择你要打包的分支，官方更新较为频繁，建议选择一个较为稳定的版本进行构建\n差不多一个小时左右就ok了\n构建完成之后到Release中去下载就可以用了，，\n","date":"2024-12-27","externalUrl":null,"permalink":"/archives/rustdesk-workflow.html","section":"文章","summary":"","title":"RustDesk内置ID服务器，Key教程","type":"posts"},{"content":"","date":"2024-12-18","externalUrl":null,"permalink":"/tags/hmailserver/","section":"标签","summary":"","title":"Hmailserver","type":"tags"},{"content":" 添加语言选项 # 首先到你的Hmailserver 安装目录，X:\\Program Files (x86)\\hMailServer\\Languages，添加一个名为 chinese.ini文件,内容如下（内容采用AI翻译）\n[Strings] String_1=文件 String_2=退出 String_3=帮助 String_5=关于... String_6=域 String_8=服务 String_9=账户 String_10=别名 String_11=分发列表 String_12=MX查询 String_13=设置 String_14=DNS缓存 String_15=SMTP String_16=安全 String_17=POP3 String_18=IP范围 String_19=镜像 String_20=日志记录 String_21=欢迎 String_22=域名 String_23=域名 String_25=需要SMTP认证 String_26=\u0026amp;保存 String_27=账户地址 String_28=激活 String_29=AD用户 String_30=是 String_32=密码 String_33=最大尺寸（MB） String_34=尺寸（MB） String_35=调试 String_40=字段 String_41=值 String_42=操作 String_43=通过输入以下字段，您可以将此账户连接到活动目录。当用户连接到服务器时，hMailServer将使用活动目录来验证用户的密码。 String_44=活动目录账户 String_45=用户名 String_46=重定向从 String_47=到 String_48=地址 String_50=常规 String_51=成员 String_52=收件人地址 String_55=服务 String_56=当前状态 String_57=电子邮件地址 String_58=\u0026amp;解析 String_59=邮件服务器 String_61=缓存更新间隔： String_62=每 String_63=小时 String_64=TCP端口 String_65=SMTP端口 String_66=连接 String_67=最大同时连接数（0为无限制） String_68=电子邮件投递 String_69=重试次数 String_70=每次重试间隔分钟数 String_71=开放中继 String_72=垃圾邮件 String_73=允许明文认证 String_74=允许空发件人地址 String_75=中继 String_80=Tarpitting String_81=计数 String_82=延迟（秒） String_87=POP3端口 String_88=较低IP String_89=较高IP String_90=优先级 String_91=地址 String_92=0是最低优先级。100是最高。 String_93=选项 String_94=允许SMTP连接 String_95=允许SMTP中继 String_96=允许POP3连接 String_97=镜像电子邮件地址 String_98=此服务器上发送的所有电子邮件，包括传入和传出消息，都将复制发送到下面输入的镜像地址。 String_100=日志 String_101=应用程序 String_104=TCP/IP String_105=文件 String_106=活动目录 String_107=账户 String_114=分发列表 String_115=输入收件人地址 String_117=开始 String_118=停止 String_120=已暂停 String_121=正在暂停 String_122=正在运行 String_123=正在启动 String_124=已停止 String_125=正在停止 String_132=完成 String_133=错误 String_137=数据库服务器地址 String_138=用户信息 String_139=数据库 String_140=名称 String_149=\u0026lt; \u0026amp;返回 String_150=\u0026amp;下一步 \u0026gt; String_151=\u0026amp;取消 String_152=输入连接信息 String_153=注意 String_154=此密码将存储在hmailserver.ini中，未加密。请配置您的环境，以确保其他用户无法从此文件中读取。 String_155=向导已有足够的信息完成操作。 String_158=请输入数据库服务器的名称。 String_159=请输入数据库的名称 String_160=正在连接到 String_161=连接成功。 String_162=无法连接到数据库服务器。 String_163=正在创建数据库 String_164=数据库创建成功。 String_165=无法创建数据库。 String_166=正在准备创建表... String_167=准备成功。 String_168=准备失败。 String_170=正在创建表... String_171=表创建成功。 String_172=无法创建数据库表。 String_173=无法启动hDBUpdater.exe String_174=数据库创建失败。 String_175=请返回并检查您的设置。 String_176=添加域名... String_180=添加AD账户 String_188=根密码 String_189=根密码（重复） String_190=此向导将为您创建和配置hMailServer数据库。您需要输入的唯件事是您想要使用的根密码。 String_192=按完成退出向导。 String_193=\u0026amp;完成 String_194=IMAP设置 String_195=IMAP端口 String_196=IMAP String_197=SMTP中继器 String_198=欢迎消息 String_199=您输入的域名不是一个有效的域名。 String_200=激活 String_205=其他 String_206=调试消息 String_208=如果字段： String_209=值为： String_210=那么 String_211=删除邮件 String_212=来自 String_213=显示日志 String_214=投递线程 String_215=高级 String_216=自动回复 String_217=启用 String_218=主题 String_219=文本 String_220=您的更改尚未保存。您现在要保存吗？ String_221=IMAP对话 String_222=允许IMAP连接 String_223=到 String_224=反病毒 String_225=使用ClamWin String_226=ClamScan可执行文件 String_227=ClamScan数据库路径 String_228=删除电子邮件 String_229=删除附件 String_230=通知发件人 String_231=通知收件人 String_232=完成操作 String_233=无法创建数据库。 String_234=无法连接到 String_235=您必须输入一个根密码。 String_236=密码不匹配。 String_237=连接错误 String_238=自动检测 String_239=hMailAdmin能够自动检测到ClamScan和数据库的位置。请验证可执行文件和数据库的路径是否正确。 String_240=实时 String_242=清除 String_243=类型 String_244=线程 String_245=时间 String_246=IP String_247=允许连接 String_248=投递需要认证 String_249=允许来自 String_250=到本地账户 String_251=到远程账户 String_252=本地到本地账户 String_253=本地到外部账户 String_254=外部到本地账户 String_255=外部到外部账户 String_257=IP范围 String_258=路由 String_259=路由 String_260=目标SMTP主机 String_261=重试次数 String_262=投递到所有地址 String_263=投递到以下地址： String_264=您必须先保存路由，然后才能添加地址。 String_267=统计 String_268=将统计信息发送到hMailServer.com String_269=如果您启用统计信息，每1000条消息将向hMailServer.com发送一次统计信息。统计信息中只包含您的版本号。不会发送任何个人信息或有关您配置的信息。 String_270=主机名 String_271=自动回复 String_272=刷新 String_273=未投递邮件 String_274=ID String_275=创建 String_276=下次尝试 String_277=\u0026amp;关闭 String_278=通用地址 String_279=输入密码 String_280=取消 String_281=确定 String_282=一个本地域名 String_283=一个外部域名 String_284=将此路由视为 String_285=服务器需要认证 String_286=密码 String_288=\u0026lt;\u0026lt; 加密 \u0026gt;\u0026gt;\u0026gt; String_289=投递 String_290=使用数据库投递日志 String_291=当发现病毒时 String_292=使用外部扫描器 String_293=扫描器可执行文件 String_294=返回值 String_295=外部病毒扫描器 String_298=您尚未在SMTP设置中指定此计算机的公共主机名。 String_299=版本 String_300=服务器信息 String_301=入门 String_302=DNS黑名单 String_303=使用SPF String_304=检查发件人是否有DNS-MX记录 String_305=DNS黑名单 String_306=DNS主机 String_307=预期结果 String_308=拒绝消息 String_311=外部账户 String_312=服务器地址 String_313=服务器信息 String_314=服务器类型 String_315=下载间隔（分钟） String_316=天 String_317=立即删除邮件 String_318=删除邮件后 String_319=不删除邮件 String_320=外部账户 String_321=脚本 String_322=重新加载脚本 String_323=显示脚本 String_324=检查语法 String_325=语法正确。 String_326=服务器 String_327=状态 String_328=处理邮件数 String_329=检测到的病毒 String_330=垃圾邮件 String_331=服务器运行时间 String_332=管理级别 String_333=用户 String_334=名称 String_335=规则 String_336=规则 String_337=使用AND String_339=使用OR String_340=条件 String_341=编辑 String_342=搜索类型 String_343=删除邮件 String_344=转发邮件 String_345=移动到IMAP文件夹 String_346=回复 String_347=正文 String_348=发件人（姓名） String_349=发件人（地址） String_350=IMAP文件夹 String_351=搜索 String_352=预定义字段 String_353=自定义头部字段 String_355=邮件大小 String_356=抄送 String_357=等于 String_358=包含 String_359=大于 String_360=小于 String_361=正则表达式 String_362=不包含 String_363=不等于 String_364=收件人列表 String_365=正在重启hMailServer服务 String_366=最大邮件尺寸（KB） String_367=多宿主 String_369=监听 String_372=IP地址 String_373=您的hMailServer数据库现在是最新的。 String_374=缓存 String_375=生存时间（秒） String_376=命中率 String_377=协议 String_378=规则循环限制 String_379=备份 String_380=恢复 String_381=目的地 String_382=消息 String_383=显示日志... String_384=压缩文件 String_388=备份文件 String_389=清除队列 String_390=您确定要清除投递队列吗？ String_391=连接 String_393=启动时自动连接 String_394=服务器版本必须与客户端版本匹配。服务器：%1，客户端：%2。 String_395=工具 String_396=服务器发送 String_397=发送到 String_398=所有账户 String_399=特定域 String_400=符合通配符的账户 String_401=发送 String_402=消息已发送。 String_403=电子邮件 String_404=别名 String_405=注销 String_406=解锁 String_407=立即发送 String_408=转发 String_409=启用转发 String_410=保留原始消息 String_411=加号地址 String_412=字符 String_413=扩展 String_414=检查HELO命令中的主机 String_417=添加X-hMailServer-Spam String_418=添加X-hMailServer-Reason String_419=添加到邮件主题 String_420=灰名单 String_421=延迟投递尝试的时间（分钟） String_422=删除未使用记录前的天数 String_423=删除已使用记录前的天数 String_424=SURBL服务器 String_425=SURBL服务器 String_426=病毒扫描的最大邮件大小（KB） String_427=阻止附件 String_428=阻止以下扩展名的附件： String_429=阻止附件 String_430=通配符 String_431=描述 String_432=投递到MIME头部的收件人 String_433=脚本函数 String_434=线程化 String_435=最大命令线程数 String_436=工作线程优先级 String_437=已存在此名称的路由 String_438=SMTP中继器永远不应该设置为指向本地计算机的主机名。您确定要这样做吗？ String_439=RFC合规性 String_440=允许格式不正确的行结尾 String_441=在太多无效命令后断开客户端连接 String_442=无效命令的最大数量 String_443=绑定到本地IP地址 String_444=批量中的最大收件人数 String_445=您必须指定此范围的较低和较高IP地址。 String_446=已存在此名称的IP范围 String_447=性能 String_448=默认域 String_449=服务器消息 String_450=运行函数 String_451=掩码密码 String_452=模式 String_453=公共 - 任何人都可以向列表发送 String_454=成员 - 只有成员可以向列表发送 String_455=公告 - 只允许从以下地址发送消息： String_456=分配大小（MB） String_457=此域已设置最大尺寸。因此，您必须指定最大账户尺寸。 String_459=无效的用户名或密码。 String_460=连接时询问 String_461=保存密码 String_462=会话 String_463=尽快 String_464=正在投递... String_465=使用SQL Server认证 String_466=使用Windows认证 String_467=数据库名称 String_468=认证 String_469=白名单 String_470=TCP/IP端口 String_471=协议 String_472=签名 String_473=纯文本签名 String_474=HTML签名 String_475=如果发件人账户中未指定签名，则使用签名 String_476=覆盖账户签名 String_477=追加到账户签名 String_478=启用签名 String_479=在回复中添加签名 String_480=在本地邮件中添加签名 String_481=启用域签名 String_482=hMailServer管理密码 String_483=上次登录时间 String_484=最大账户数 String_485=最大别名数 String_486=最大分发列表数 String_487=此域的账户数量已达上限，无法添加账户。 String_488=此域的别名数量已达上限，无法添加别名。 String_489=此域的分发列表数量已达上限，无法添加分发列表。 String_490=限制 String_491=账户最大尺寸（MB） String_496=转发中继 String_497=您确定要删除%s吗？ String_498=注意：此功能旨在用于小于1GB邮件数据的小规模安装。如果您的安装大于这个规模，请查阅文档以使用外部工具。 String_499=从收到的头部检索日期 String_500=向上移动 String_501=向下移动 String_502=自动过期 String_503=立即下载 String_504=许多电子邮件服务器不会接受来自您服务器的电子邮件，如果您取消选择此选项。您确定要取消选择吗？ String_505=您已配置hMailServer不允许发送空发件人地址的电子邮件。许多电子邮件服务器不会接受来自您服务器的此类配置的电子邮件。 String_506=停止规则处理 String_507=启用SMTP String_508=启用POP3 String_509=启用IMAP String_510=hMailServer管理密码 String_511=您需要此密码才能管理您的hMailServer安装，请记住它。 String_513=组 String_514=组 String_515=%S的权限 String_516=任何人 String_517=SSL证书 String_518=SSL证书 String_519=此操作将TCP/IP端口的配置更改回默认值。您确定要这样做吗？ String_520=无 String_521=设置头部值 String_522=尝试次数 String_523=头部名称 String_524=选择... String_525=清空账户 String_526=文件夹 String_527=访问权限 String_528=编辑权限... String_529=使用SSL String_530=证书文件 String_531=私钥文件 String_532=添加Delivered-To头部 String_533=公共文件夹名称 String_534=权限 String_535=全选 String_536=全不选 String_537=查找 String_538=读取 String_539=创建邮箱 String_540=删除邮箱 String_541=管理 String_542=插入邮件 String_543=清除 String_544=保持已读标记 String_545=保持已删除标记 String_546=保持其他标记 String_547=否 String_548=垃圾邮件得分 String_550=垃圾邮件测试 String_551=反垃圾邮件 String_552=垃圾邮件得分标记阈值 String_553=垃圾邮件得分降低阈值 String_554=密码看起来较弱。您确定要使用它吗？ String_555=当前会话 String_556=个人信息 String_557=名字 String_558=姓氏 String_559=使用SpamAssassin String_560=使用SpamAssassin的得分 String_561=本地主机名 String_562=远程主机名 String_563=远程TCP/IP端口 String_564=配置警告 String_565=打开会话数 String_566=文件名 String_567=投递队列 String_568=启用 String_569=转发到 String_570=下次下载 String_571=其他操作 String_572=编辑文件夹 String_574=\u0026amp;添加... String_575=\u0026amp;编辑... String_576=公共文件夹 String_577=垃圾邮件标记阈值 String_578=垃圾邮件删除阈值 String_579=得分 String_580=SSL证书 String_581=证书文件 String_582=私钥文件 String_583=索引... String_584=选择语言... String_585=连接... String_586=\u0026amp;文件 String_587=\u0026amp;帮助 String_588=管理密码 String_589=TCP/IP端口 String_590=严重性 String_591=高 String_592=严重 String_593=hMailServer配置为允许从IP范围%s的外部到外部账户投递。这可能会使服务器容易受到垃圾邮件的攻击。建议您禁用此选项。 String_594=\u0026amp;确定 String_595=属性 String_596=操作 String_597=\u0026amp;选择... String_598=选择 String_599=使用路由发送 String_600=尝试次数 String_601=只包括本次会话 String_602=创建文件夹 String_603=删除文件夹 String_604=显示 String_606=添加... String_607=清空文件夹 String_608=比较 String_609=移除 String_610=选择语言 String_611=语言 String_612=您确定要删除账户中的所有邮件吗？ String_614=TCP/IP地址 String_615=对象 String_616=保存 String_617=天 String_618=添加 String_619=此操作将IP范围的配置更改回默认值。您确定要这样做吗？ String_620=扫描的最大邮件大小（KB） String_621=保持文件打开 String_622=测试 String_623=匹配 String_624=无匹配 String_626=如果您想要自动化备份过程，请使用位于\\hMailServer\\Addons\\Utilities下的StartBackup.vbs脚本。 String_627=hMailServer需要重新启动才能使更改生效。 String_628=您现在要重新启动hMailServer吗？ String_629=hMailServer服务器已重新启动。 String_630=如果您更改这些设置，hMailServer需要重新启动才能使更改生效。 String_631=最大异步任务线程数 String_632=诊断 String_633=此工具允许您对您的hMailServer安装进行诊断。 String_634=选择您要运行测试的域。 String_635=复制到剪贴板 String_637=自动禁止 String_638=到期 String_639=传入中继 String_640=最大无效登录尝试次数 String_641=重置前分钟数 String_642=自动禁止前分钟数 String_643=创建并发送副本 String_644=允许路由收件人 String_645=投递尝试 String_646=消息索引 String_647=请注意... String_648=更多细节... String_649=自动禁止功能会阻止IP地址。如果从特定IP地址进行了太多无效的登录尝试，该IP地址将被禁止。 String_650=如果您正在运行一个网络邮件系统，这意味着如果在网络上进行了太多无效的登录尝试，该网络邮件系统可能会被阻止。 String_651=您确定要删除选定的项目吗？ String_652=在SPF通过时绕过灰名单 String_653=本地到本地电子邮件地址 String_654=本地到外部电子邮件地址 String_655=外部到本地电子邮件地址 String_656=外部到外部电子邮件地址 String_659=已经存在具有此名称的文件夹。 String_660=层次结构分隔符 String_661=文件夹名称不能包含层次结构分隔符。 String_662=当消息到达A或MX记录时，绕过灰名单。 String_663=当发件人匹配路由时，将发件人视为 String_664=当收件人匹配路由时，将收件人视为 String_665=一个远程电子邮件地址 String_666=一个本地电子邮件地址 String_667=hMailServer IMAP层次结构分隔符是%S1。您指定的文件夹名称包含字符%S2，这不是层次结构分隔符，但可能是文件夹名称的一部分。如果您选择保存当前文件夹名称，当执行此操作时，将创建一个名为%S3的单个文件夹。您要使用这个文件夹名称吗？ String_668=测试... String_669=用户名或密码不正确。 String_672=数据库版本 String_673=您确定要清除消息索引吗？ String_674=中等 String_675=Localhost当前在IP范围内被禁止。 String_676=总共有%s个自动禁止IP范围。 String_677=最大收件人主机数 String_678=使用ClamAV String_679=测试病毒成功检测。病毒名称： String_680=病毒检测失败。原因： String_681=连接安全性 String_682=SSL/TLS密码 String_683=要求SSL/TLS进行认证 String_684=\u0026amp;导入... String_685=删除导入文件中不存在的收件人 String_686=导入完成。 String_690=IMAP主用户 String_691=您确定要删除文件夹中的所有邮件吗？ String_692=您确定要删除文件夹吗？ 然后编辑 Hmailserver 安装目录，X:\\Program Files (x86)\\hMailServer\\Bin\\hMailServer.INI，在在 [GUILanguages] 字段中的 ValidLanguages 项添加 chinese 即可。\n[Directories] ProgramFolder=D:\\Program Files (x86)\\hMailServer DatabaseFolder= DataFolder=D:\\Program Files (x86)\\hMailServer\\Data LogFolder=D:\\Program Files (x86)\\hMailServer\\Logs TempFolder=D:\\Program Files (x86)\\hMailServer\\Temp EventFolder=D:\\Program Files (x86)\\hMailServer\\Events [GUILanguages] ValidLanguages=english,swedish,chinese [Security] AdministratorPassword=abc123 [Database] Type=MSSQL Username=sa Password=abc123 PasswordEncryption=1 Port=0 Server=127.0.0.1 Database=mailserver Internal=0 ","date":"2024-12-18","externalUrl":null,"permalink":"/archives/Hmailserver-chinese.html","section":"文章","summary":"","title":"Hmailserver 汉化教程","type":"posts"},{"content":"","date":"2024-12-14","externalUrl":null,"permalink":"/tags/exporter/","section":"标签","summary":"","title":"Exporter","type":"tags"},{"content":"","date":"2024-12-14","externalUrl":null,"permalink":"/tags/prometheus/","section":"标签","summary":"","title":"Prometheus","type":"tags"},{"content":"PVE 原生支持 Graphite 或者 InfluxDB 作为指标数据的存储，这俩我还没折腾过，本文主要围绕着prometheus-pve-exporter来写,类似于node_exporter 这里就不再多说了。\n安装 proxmox-pve-exporter # 说明\n这里我将 proxmox-pve-exporter 安装到单独的服务器，而不是 pve宿主机，，这样可以通过一个 exporter 去监控多台 pve 主机,如果只有一台机器的话直接在pve主机安装就行。。。\nGitHub地址：https://github.com/prometheus-pve/prometheus-pve-exporter\n安装 exporter，如果没有pip3的话需要先安装python3-pip\npip3 install prometheus-pve-exporter -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 安装完成后可以通过 pve_exporter -h 查看帮助信息\n帮助命令机翻：\n用法：pve_exporter [-h] [--collector.status | --no-collector.status] [--收集器.版本 | --无收集器.版本] [--收集器.节点|--无收集器.节点] [--收集器.集群|--no-收集器.集群] [--收集器.资源|--无收集器.资源] [--收集器.配置|--无收集器.配置] [--收集器.复制|--no-收集器.复制] [--config.文件 CONFIG_FILE] [--web.listen 地址 WEB_LISTEN_ADDRESS] [--server.keyfile 服务器密钥文件] [--server.certfile 服务器证书文件] 选项： -h, --help 显示此帮助消息并退出 --config.file 配置文件 配置文件路径（/etc/prometheus/pve.yml） –web.listen 地址 WEB_LISTEN_ADDRESS 公开指标和 Web 服务器的地址。 （[::]:9221） --server.keyfile 服务器密钥文件 服务器的 SSL 密钥 --server.certfile 服务器证书文件 服务器的 SSL 证书 集群收集器： 如果设置了 URL 参数 cluster=1，则运行集群收集器，并且 如果在抓取 URL 上设置了 url 参数 cluster=0，则跳过。 --collector.status，--no-collector.status 公开节点/VM/CT 状态 --collector.version，--无收集器.版本 公开 PVE 版本信息 --collector.node，--no-collector.node 公开 PVE 节点信息 --collector.cluster，--no-collector.cluster 公开 PVE 集群信息 --collector.resources，--no-collector.resources 公开 PVE 资源信息 节点收集器： 如果设置了 url 参数 node=1，则运行节点收集器；如果设置了 url 参数 node=1，则跳过节点收集器 在抓取 URL 上设置了 URL 参数 node=0。 --collector.config，--no-collector.config 公开 PVE 启动状态 --collector.replication，--no-collector.replication 公开 PVE 复制信息 也可以通过 Docker 安装\ndocker run --init --name prometheus-pve-exporter -d -p 127.0.0.1:9221:9221 -v /path/to/pve.yml:/etc/prometheus/pve.yml prompve/prometheus-pve-exporter 配置监控用户 # 出于安全原因，必须添加具有只读访问权限的用户（PVEAuditor 角色）来收集指标。\n在所有的PVE主机创建一个用户\npveum useradd monitor@pve -comment \u0026#34;monitor for exporter\u0026#34; # 创建用户 pveum passwd monitor@pve # 配置密码 为 prometheus 用户分配 PVEAuditor 监控角色的权限\npveum aclmod / -user monitor@pve -role PVEAuditor 配置 proxmox-pve-exporter # 在安装了 proxmox-pve-exporter 的主机中创建一个配置文件，用于定义PVE主机的用户名，密码，就是上面创建的用户\nnano /etc/prometheus/pve.yml\ndefault: user: monitor@pve password: your_password verify_ssl: false 配置开机自启\ntee /etc/systemd/system/prometheus-pve-exporter.service\u0026lt;\u0026lt;EOF [Unit] Description=Prometheus exporter for Proxmox VE Documentation=https://github.com/znerol/prometheus-pve-exporter [Service] Restart=always ExecStart=/usr/local/bin/pve_exporter /etc/prometheus/pve.yml [Install] WantedBy=multi-user.target EOF systemctl daemon-reload \u0026amp;\u0026amp; systemctl enable --now prometheus-pve-exporter.service 默认端口 9221\n访问 exporter http://ip:9221\n这里会让你访问/pve?target=1.2.3.4,其中 target= 是你的PVE主机IP地址，如果是在pve主机搭建的 prometheus-pve-exporter ip填 127.0.0.1 即可，我这种方法适合需要监控多台PVE主机的场景\n查看监控指标\nPrometheus 配置 # 单台机器\n- job_name: \u0026#39;pve\u0026#39; metrics_path: /pve static_configs: - targets: [\u0026#39;127.0.0.1:9221\u0026#39;] # 如果是使用docker安装的话建议直接写pve主机的ip而不是环回地址 多台机器，使用relabel_configs重写目标地址\n- job_name: \u0026#39;pve\u0026#39; static_configs: - targets: - 10.0.0.2 # pve node 1 - 10.0.0.3 # pve node 2 metrics_path: /pve params: module: [default] cluster: [\u0026#39;1\u0026#39;] node: [\u0026#39;1\u0026#39;] relabel_configs: - source_labels: [__address__] target_label: __param_target - source_labels: [__param_target] target_label: instance - target_label: __address__ replacement: 127.0.0.1:9221 # pve exporter 配置完成后重启普罗米修斯，并进行验证\n配置Grafana仪表盘 # 这里推荐使用 10347 https://grafana.com/grafana/dashboards/10347-proxmox-via-prometheus/\n通过/dashboard/import 导入即可\n演示地址：https://grafana.wanghaoyu.com.cn/d/Dp7Cd57Zza/proxmox-via-prometheus\n","date":"2024-12-14","externalUrl":null,"permalink":"/archives/pve-exporter.html","section":"文章","summary":"","title":"使用 proxmox-pve-exporter 监控Proxmox VE","type":"posts"},{"content":"","date":"2024-12-11","externalUrl":null,"permalink":"/tags/lxc/","section":"标签","summary":"","title":"Lxc","type":"tags"},{"content":"","date":"2024-12-11","externalUrl":null,"permalink":"/tags/pve/","section":"标签","summary":"","title":"PVE","type":"tags"},{"content":"Proxmox Virtual Environment (PVE) 是一个开源的虚拟化平台，，其中的LXC容器是一种轻量级容器化技术，可提供更高的性能和资源利用率，允许在单个 Linux 内核上运行多个隔离的容器实例，每个容器都拥有自己的独立文件系统，进程空间，网络接口等\n更换国内源 # 创建LXC容器之前，我们要先修改一下LXC 容器源，从下图中打开Shell命令行。\n输入以下命令 备份原有默认源然后将默认源替换为国内清华大学源。\ncp /usr/share/perl5/PVE/APLInfo.pm /usr/share/perl5/PVE/APLInfo.pm.back # 备份默认源 sed -i \u0026#39;s|http://download.proxmox.com|https://mirrors.tuna.tsinghua.edu.cn/proxmox|g\u0026#39; /usr/share/perl5/PVE/APLInfo.pm # 修改为清华大学源 重启服务\nsystemctl restart pvedaemon.service 下载CT模板 # 我们重启完成服务之后 可以打开你的任意一个存储，找到CT模板，上方模版选项。\n然后我选择了一个debian12系统的模板 从下图可以看到更换为国内源之后下载会非常快了。\n创建LXC容器 # 现在我们可以用刚刚下载的CT模板来创建一个LXC容器了。\n选择好你的镜像模板，按需分配配置规格,并进行创建\n","date":"2024-12-11","externalUrl":null,"permalink":"/archives/pve-use-lxc.html","section":"文章","summary":"","title":"PVE更换LXC源,在PVE上使用LXC容器","type":"posts"},{"content":"","date":"2024-12-05","externalUrl":null,"permalink":"/tags/httpcode/","section":"标签","summary":"","title":"Httpcode","type":"tags"},{"content":" 状态码 # Code Category Description 1xx 信息性 请求已接收，服务器正在处理。 100 Continue 服务器已接收请求，客户端应继续发送请求。 101 Switching Protocol 服务器正在按照请求切换协议。 102 Processing 服务器正在处理请求，但没有响应返回。 2xx 成功 请求已成功接收、理解并接受。 200 OK 请求已成功处理，返回响应。 201 Created 请求已成功处理，且新资源已创建。 202 Accepted 请求已接受处理，但尚未完成。 203 Non-Authoritative Information 返回的信息来自第三方，非原始服务器。 204 No Content 请求成功，但没有内容返回。 205 Reset Content 请求成功，要求客户端重置文档视图。 206 Partial Content 服务器根据范围返回部分资源。 3xx 重定向 需要采取额外的操作才能完成请求。 300 Multiple Choices 请求有多个可能的响应。 301 Moved Permanently 资源已永久移动到新 URL。 302 Found 资源临时位于其他 URL。 303 See Other 应该从另一个 URI 获取响应。 304 Not Modified 资源自上次请求后未被修改。 307 Temporary Redirect 资源临时位于其他 URL。 308 Permanent Redirect 资源已永久移动到新 URI。 4xx 客户端错误 请求存在问题，客户端应修改请求。 400 Bad Request 服务器无法理解请求，语法错误。 401 Unauthorized 请求未授权，需进行身份认证。 402 Payment Required 为将来使用保留的状态码。 403 Forbidden 服务器拒绝请求，无权限访问资源。 404 Not Found 请求的资源无法找到。 405 Method Not Allowed 请求方法不被允许使用。 406 Not Acceptable 请求的资源不可接受，内容类型不匹配。 407 Proxy Authentication Required 需要通过代理认证。 408 Request Timeout 请求超时，服务器等待请求时超时。 409 Conflict 请求与资源的当前状态冲突。 410 Gone 资源已永久删除，不再可用。 411 Length Required 需要内容长度，服务器拒绝请求。 412 Precondition Failed 请求条件不满足，无法处理请求。 413 Payload Too Large 请求体太大，无法处理。 5xx 服务器错误 服务器处理请求时出错。 500 Internal Server Error 服务器内部错误，无法完成请求。 501 Not Implemented 服务器不支持请求所需的功能。 502 Bad Gateway 服务器作为网关时接收到无效响应。 503 Service Unavailable 服务器暂时无法处理请求，通常是过载或维护。 504 Gateway Timeout 网关超时，未能及时接收到上游服务器的响应。 505 HTTP Version Not Supported 服务器不支持请求的 HTTP 版本。 常见 HTTP 方法 # Method Description GET 请求指定的资源。通常用于获取数据 POST 提交数据给服务器，用于创建或更新资源 PUT 更新资源或创建资源。通常用于上传文件或数据 DELETE 删除指定的资源 PATCH 对资源进行部分修改 HEAD 与 GET 相同的响应，但不返回响应体 OPTIONS 查询服务器支持哪些 HTTP 方法 ","date":"2024-12-05","externalUrl":null,"permalink":"/archives/http-code.html","section":"文章","summary":"","title":"HTTP状态码和请求方法总结","type":"posts"},{"content":"","date":"2024-11-29","externalUrl":null,"permalink":"/tags/windows/","section":"标签","summary":"","title":"Windows","type":"tags"},{"content":"最近发现了一个神器——Windows Defender Remover，能在你电脑上或者彻底删除烦人的 Windows Defender，让你的电脑飞起来！ 项目地址： https://github.com/ionuttbara/windows-defender-remover\n它能干啥？ # 让Windows安全中心闭嘴 关闭虚拟化安全 禁用文件虚拟化和UAC 禁用SmartScreen 从设置里移除Windows安全部分 怎么用？ 方法一：直接下载运行 # 下载工具：去GitHub Releases页面，下个.exe文件。\n运行工具：右键点击下载的文件，选择“以管理员身份运行”。\n一条命令搞定：如果你喜欢敲命令，那就在命令行里输入：\nDefender.Remover.exe /r 方法二：用Git克隆 # 打开命令提示符：按下Win+R，输入cmd，回车。\n输入Git命令：在命令提示符里输入以下命令：\ngit clone https://github.com/ionuttbara/windows-defender-remover.git 注意\n前提需要安装Git！！！\n进入目录：然后输入：\ncd windows-defender-remover 运行脚本：最后输入：\nScript_Run.bat ","date":"2024-11-29","externalUrl":null,"permalink":"/archives/24.html","section":"文章","summary":"","title":"一招教你告别烦人的 Windows Defender","type":"posts"},{"content":"","date":"2024-11-29","externalUrl":null,"permalink":"/tags/caddy/","section":"标签","summary":"","title":"Caddy","type":"tags"},{"content":"Caddy是一个功能强大的 Web 中间件，但是Caddy 并不自带安全防护功能，但是可以通过第三方模块来增强网站的安全性，例如 Coraza WAF ，这篇文章将使用 Coraza WAF 插件以及 OWASP CRS 规则集来增强网站的安全性。\n安装 Coraza WAF 插件 # 这里需要确保 Caddy 安装了 Coraza WAF 插件，以便能够使用 CRS 规则集。\nCaddy 支持通过官方插件库安装插件，也可以通过 xcaddy 构建工具来安装插件，xcaddy 是一个用来编译和构建 Caddy 自定义版本的工具，支持在 Caddy 中安装额外插件，如果有定制化需求可以选择使用 xcaddy 。\ncaddy add-package github.com/corazawaf/coraza-caddy # 通过官方插件库 xcaddy build --with github.com/corazawaf/coraza-caddy # 通过 xcaddy 安装 Coraza WAF 插件并生成一个自定义的 Caddy 二进制文件 注意……\n如果采用 xcaddy 需要提前安装好 Go 环境。\n下载 OWASP CRS 规则集 # 这里我选择放到 /etc/coreruleset 目录下\ngit clone https://github.com/coreruleset/coreruleset.git /etc/coreruleset 克隆完成之后将里面的crs-setup.conf.example 重命名为 coraza.conf-recommended\n配置 Caddyfile # 下面是一个参考例子\nwanghaoyu.com.cn { tls admin@wanghaoyu.com.cn route { coraza_waf { directives ` # 加载 Coraza 推荐的默认配置 Include /etc/coreruleset/coraza.conf-recommended # 加载 OWASP CRS 规则 Include /etc/coreruleset/rules/*.conf # 启用规则引擎 SecRuleEngine On ` } reverse_proxy localhost:8080 } } 基本配置说明：\ncoraza_waf：将 WAF 集成到 Caddy 的请求处理流程 Include：用于加载 CRS 默认配置和防护规则 SecRuleEngine On：启用规则引擎，确保请求会经过防火墙的检查和过滤。 reverse_proxy：反代后端节点，将 wanghaoyu.com.cn 的流量请求转发到 localhost:8080 当然 这只是个例子 安全头 # 为了增强一下安全性，可以配置一下 HTTP 响应头，防止常见攻击，这个部分是可选的。\nheader { # 启用 HSTS，确保所有的请求都通过https进行 Strict-Transport-Security \u0026#34;max-age=31536000; includeSubDomains; preload\u0026#34; # 设置 CSP 防止外部脚本注入 Content-Security-Policy \u0026#34;default-src \u0026#39;self\u0026#39;; script-src \u0026#39;self\u0026#39;; object-src \u0026#39;none\u0026#39;;\u0026#34; # 防止点击劫持 X-Frame-Options \u0026#34;DENY\u0026#34; # 防止xss跨站脚本攻击 X-XSS-Protection \u0026#34;1; mode=block\u0026#34; # 防止 MIME 类型嗅探 X-Content-Type-Options \u0026#34;nosniff\u0026#34; # 让浏览器更安全地处理 cookies Set-Cookie \u0026#34;SameSite=Strict; Secure\u0026#34; } 验证配置 # Caddy 提供了一个工具用来检查配置文件是否正确的，类似于 nginx -t\ncaddy validate --config /etc/caddy/Caddyfile 如果配置没问题的话会返回 Valid configuration 证明这是个有效的配置。\n参考：\nhttps://caddyserver.com/docs/ https://github.com/coreruleset/coreruleset/ https://github.com/corazawaf/coraza https://github.com/caddyserver/xcaddy https://github.com/caddyserver/caddy ","date":"2024-11-29","externalUrl":null,"permalink":"/archives/caddy-waf.html","section":"文章","summary":"","title":"Caddy 配置 WAF防火墙","type":"posts"},{"content":"","date":"2024-11-29","externalUrl":null,"permalink":"/tags/qcow2/","section":"标签","summary":"","title":"Qcow2","type":"tags"},{"content":"","date":"2024-11-29","externalUrl":null,"permalink":"/tags/qemu/","section":"标签","summary":"","title":"Qemu","type":"tags"},{"content":"今天在封装 Windows Server 2025 qcow2 镜像的时候突然注意到一个问题，将系统配置好之后导出时发现镜像的大小竟然是 60G ？？？（给虚拟机磁盘的大小就是60G）,研究了一番得出了如下结论：QCOW2格式允许磁盘动态增加，虚拟机磁盘文件不会立即占用预配的磁盘空间，而是根据需要进行使用,而且磁盘空间的增加也不是说完全是被用掉了，例如我打包的这个镜像，实际大小是 7G 左右，我创建实例设置的磁盘大小是 60G 的，这个盘的映像文件也会占用 60G ，因为 qcow2 文件的结构在写入操作时会在原有基础上进行扩展，不会自动减少。\n这种现象通常出现在以下两种情况：\n实例中的删除操作没有实际释放空间。。 实例中在进行了一些大规模的 I/O 操作之后镜像文件没有自动压缩。 解决方法， 压缩 qcow2 镜像 # 使用 qemu-img 命令 来压缩镜像文件大小,qemu-img 的 convert 命令可以将一个镜像文件转换成另一个文件，同时进行压缩。通过使用 -O qcow2 和 -c 选项，可以将 qcow2镜像压缩\nqemu-img convert -O qcow2 -c old.qcow2 new.qcow2 qemu-info xxx.qcow2 # 查看镜像参数 old.qcow2：原始镜像文件\nnew.qcow2：压缩后的镜像文件\n-O qcow2：指定输出格式为 qcow2\n-c：启用压缩功能\n然后在打包 Windows 镜像的时候可以使用 sdelete 工具来清理一下空间，减少没必要的占用，Linux操作系统的话可以使用 fstrim。\nsdelete -z C: # Win sudo fstrim / # Linux ","date":"2024-11-29","externalUrl":null,"permalink":"/archives/qemu-img-resize.html","section":"文章","summary":"","title":"使用 QEMU 压缩 QCOW2 镜像文件的大小","type":"posts"},{"content":"","date":"2024-11-25","externalUrl":null,"permalink":"/tags/cloudflare/","section":"标签","summary":"","title":"Cloudflare","type":"tags"},{"content":"通过 Cloudflare Tunnel 可以把在家中搭建的各种服务穿透到公网上，完全基于 Cloudflare 全球网络，不用购买有公网的云服务器，而且可以轻松跑满的你上行带宽。\n先决条件 # 一个 CloudFlare 账号，且具有一个域名。 PayPal账号,绑定一张借记卡 ， 或者使用 Visa 信用卡。 使用方法 # 绑定付款方式 # 服务是免费的，无需付费，只需要绑定一个付款方式即可\n打开 CloudFlare Zero Trust 工作台，绑定你的支付方式就可以了，这里我已经绑定过了就不截图了。\n创建隧道 # 在 Networks 中 找到 Tunnels ， 并点击右侧的 \u0026ldquo;Create a tunnel\u0026rdquo;\n原文这里引用了一张本地 Typora 图片，迁移到 Hugo 后无法访问：C:\\Users\\wang\\AppData\\Roaming\\Typora\\typora-user-images\\image-20241125230344422.png 选择第一个\n创建一个隧道，填写隧道名称并保存\n然后就来到了下载客户端的页面，这里可以根据你的需求选择下载 Cloudflare Tunnels 使用的客户端，类似于 npc,frp这种客户端软件，这个客户端可以安装到你内网中任何一台机器上，也可以将其安装到您的 OpenWrt 中进行使用，非常规设备推荐使用 Docker 进行部署。\n客户端完成安装之后下滑有个配置客户端的说明，如下图所示，在安装完成客户端的机器中输入它上面提示的命令即可。\n配置完成后在 Tunnels 控制台中会看到你创建的隧道为活跃状态。\n配置隧道 # 接下来我们需要将你的内网服务穿透出去，我们点击创建的隧道名称，会弹出控制台，点击 Edit\n然后点击 Public Hostname，并点击下面的添加按钮\n然后我们需要填写你的子域名以及对应的域名，例如我这里 Sbudomain 填写 \u0026ldquo;test\u0026rdquo;,域名为 \u0026ldquo;wanghaoyu.com.cn\u0026rdquo;, 类型选择 HTTP ，URL 为内网中的IP地址+端口，默认是80端口，我这里的 10.0.0.1 是一台 OpenWrt 路由器，保存之后最终我们可以通过 https://test.wanghaoyu.com.cn 访问内网中的 OpenWrt，就算你的内网中使用的是 HTTP 协议，Cloudflare 也会自动帮你添加 SSL 证书\nPS:这个据我实测，我家里是300M上行带宽，用CloudFlare 内网穿透之后下载 NAS 中的文件是基本可以跑满的\n","date":"2024-11-25","externalUrl":null,"permalink":"/archives/cf-tunnels.html","section":"文章","summary":"","title":"使用 Cloudflare Tunnels 实现内网穿透，公网访问内网服务","type":"posts"},{"content":"","date":"2024-11-22","externalUrl":null,"permalink":"/tags/%E5%8D%9A%E5%AE%A2/","section":"标签","summary":"","title":"博客","type":"tags"},{"content":"","date":"2024-11-22","externalUrl":null,"permalink":"/tags/%E5%BB%BA%E7%AB%99/","section":"标签","summary":"","title":"建站","type":"tags"},{"content":"[RainBowText]\n本教程分三种不同的部署方法，如下所示；\nVercel CloudFlare Netlify 都是围绕着 Hexo来的\n[/RainBowText]\n先前条件 # Git客户端 VS Code nodejs环境 GitHub账号 一个域名 环境准备 （这里以 Windows 环境为例） # 安装 NodeJS 环境 # 下载地址：https://nodejs.cn/download/\n安装 Git 客户端 # 下载地址：\nhttps://git-scm.com/downloads https://gitforwindows.org/ 配置 NodeJS # 配置镜像加速 # 参考：https://npmmirror.com/\nnpm config set registry https://registry.npmmirror.com # 或者在 cmd 或者 powershell中使用 set env 配置http代理也可以 安装必要软件包 # npm install -g hexo-cli hexo # -g 为全局安装 如果提示 command not found 要检查你的 npm 环境变量是否有问题\n初始化网站 # 可以先创建一个文件夹myblog，然后cd到这个文件夹下（或者在这个文件夹下直接右键git bash打开）。\n执行如下命令\nhexo init 启动网站 # 完了我们要先看一下网站是否可以能够正常启动，能够正常启动说明依赖没问题\nhexo server hexo s # 这个是上面命令的缩写 配置主题 # Hexo的默认主题应该没人会用吧，，，，所以我们需要找几个自己喜欢的主题进行配置，这里推荐几个\nhttps://github.com/jerryc127/hexo-theme-butterfly https://github.com/fluid-dev/hexo-theme-fluid https://blog.zhheo.com/update/ 怎么配置我就不写了，主题文档都有详尽教程\n上传代码 # 现在，假如你的网站已经做好了，接下来我们需要把你的网站提交到 Github代码仓库中，然后就可以将其部署到各种 Serverless 平台了\n1、在 github 中创建一个新的仓库，用来存储你的 hexo 博客系统的代码，以及后续的迭代更新，部署等。\n2、然后按照提示说明初始化仓库\n一定要在你之前执行 hexo init 的目录下执行\necho \u0026#34;# myblog\u0026#34; \u0026gt;\u0026gt; README.md git init git add README.md git commit -m \u0026#34;first commit\u0026#34; git branch -M main git remote add origin https://github.com/github_username/myblog.git git push -u origin main 3、初始化完成之后刷新你的github仓库页面你会看到有一个 README.md 文件说明成功了\n4、 然后我们可以通过 git 命令将我们的 hexo 博客提交到github中\ngit add . git commit -m \u0026#34;first commit\u0026#34; git push 完了之后你的本地的hexo博客系统的代码就会上传到github中了\n部署网站 # 部署到 CloudFlare # 首先我们需要一个 cloudflare 账号 没有的话自己注册\nhttps://cloudflare.com\n找到 Worker 和 Pages 页面，点击创建\n选择 Pages 并点击链接到 Git\n添加你的 GitHub 账户\n选择你的仓库，并点击右下角的开始设置\n然后构建配置命令这里一定要输入 npx hexo generate\n部署完成后就可以访问了\n最后我们可以自定义域中绑定你自己的域名，然后就可以用你的域名访问你的网站了。\n部署到 Vercel # 在 https://vercel.com/ 中注册一个账号 然后添加一个新项目 （好像可以直接用GitHub登录来着我也忘了。。。）\n同样也是选择你的项目，点击 import\n这里直接点击 Deploy 即可\n部署完成之后 可以点击 Continue to Dashboard 访问 Serverless 控制台\n默认自带的域名，这个域名国内被墙了，需要绑定自定义域名才能访问\n点击右上角 Domains 绑定自定义域名即可。\n部署到 Netlify # https://app.netlify.com/login\n注册一个 Netlify 账号， 直接 GitHub 登录也可以\n导入项目\n选择 GitHub，会自动进行验证\n选择你的仓库，**将默认的npm run build 修改为 npx hexo generate，**然后直接 Deploy即可\n自定义域名在 Domain Management 中\n","date":"2024-11-22","externalUrl":null,"permalink":"/archives/hexo-install.html","section":"文章","summary":"","title":"使用 Hexo 搭建个人网站 ","type":"posts"},{"content":"","date":"2024-11-22","externalUrl":null,"permalink":"/tags/linux/","section":"标签","summary":"","title":"Linux","type":"tags"},{"content":"下载 nodejs：https://nodejs.cn/download/\n右键直接复制下载链接\nssh登录到你的云服务器，将 nodejs 环境下载到你的云服务器上,例如\nwget https://npmmirror.com/mirrors/node/v20.18.0/node-v20.18.0-linux-x64.tar.xz 如果没有 wget 命令的话需要先安装\nUbuntu/Debian\napt install wget -y RHEL/CentOS\ndnf/yum install -y wget 下载完成之后解压 node-v20.18.0-linux-x64.tar.xz tar xf node-v20.18.0-linux-x64.tar.xz 会得到解压后的文件夹 node-v20.18.0-linux-x64\n将其移动到 /usr/local/ 目录中并重命名为 node\nmv node-v20.18.0-linux-x64 /usr/local/node/ 完整路径 /usr/local/node\n配置环境变量，编辑 root 用户家目录的 .bashrc 或者 /etc/profile\n添加如下内容,其中 NODE_HOME=/usr/local/node 为你的 node 环境路径\nexport NODE_HOME=/usr/local/node export PATH=$NODE_HOME/bin:$PATH 使其生效：\nsource /etc/profile source ~./bashrc ","date":"2024-11-22","externalUrl":null,"permalink":"/archives/linux-nodejs-deploy.html","section":"文章","summary":"","title":"Linux 搭建 NodeJS环境","type":"posts"},{"content":"本文是通过插图详细解释每个 Kubernetes 组件。\n阅读本文，你将：\n了解 Kubernetes 的架构 掌握 Kubernetes 的基本概念 了解 Kubernetes 架构组件 探索连接这些组件的工作流 注意：为了更好地理解 Kubernetes 架构，有一些先决条件请查看 kubernetes 学习指南中的先决条件以了解更多信息。\n什么是 Kubernetes 架构？ # 以下 Kubernetes 架构图显示了 Kubernetes 集群的所有组件以及外部系统如何连接到 Kubernetes 集群。\n关于 Kubernetes，您应该了解的第一件事是，它是一个分布式系统。这意味着，它有多个组件分布在网络上的不同服务器上。这些服务器可以是虚拟机或裸机服务器。我们称之为 Kubernetes 集群。\nKubernetes 集群由控制平面节点和工作节点组成。\n控制平面 # 控制平面负责容器编排和维护集群的所需状态。它具有以下组件。\nkube-apiserver etcd kube-scheduler kube-controller-manager cloud-controller-manager 一个集群可以有一个或多个控制平面节点。\n工作节点 # Worker 节点负责运行容器化应用程序。worker 节点具有以下组件。\nkubelet kube-proxy Container runtime Kubernetes 控制平面组件 # 首先，让我们看一下每个控制平面组件以及每个组件背后的重要概念。\n1. kube-apiserver # kube-apiserver 是公开 Kubernetes API 的 Kubernetes 集群的中心枢纽。它具有高度可扩展性，可以处理大量并发请求。\n最终用户和其他集群组件通过 API 服务器（kube-apiserver）与集群通信。在极少数情况下，监控系统和第三方服务可能会与 apiserver 通信以与集群进行交互。\n因此，当您使用 kubectl 管理集群时，在后端，您实际上是通过 HTTP REST API 与 API 服务器进行通信。但是，内部集群组件（如 scheduler、controller 等）使用 gRPC 与 API 服务器通信。\nAPI 服务器与集群中的其他组件之间的通信通过 TLS 进行，以防止对集群进行未经授权的访问。\nKubernetes api-server 负责以下工作。\nAPI 管理：公开集群 API endpoint 并处理所有 API 请求。API 是版本化的，它同时支持多个 API 版本。 身份验证（使用客户端证书、持有者令牌和 HTTP 基本身份验证）和授权（ABAC 和 RBAC） 处理 API 请求并验证 API 对象（如 Pod、服务等）的数据（验证和变更准入控制器） 它是唯一与 etcd 通信的组件 api-server 协调控制平面和工作节点组件之间的所有进程 api-server 有一个内置的 apiserver 代理。它是 API 服务器进程的一部分。它主要用于启用从群集外部访问 ClusterIP 服务，即使这些服务通常只能在群集本身内访问 API 服务器还包含一个聚合层，允许您扩展 Kubernetes API 以创建自定义 API、资源和控制器 API 服务器还支持监视资源的更改。例如，客户端可以对特定资源建立监视，并在创建、修改或删除这些资源时接收实时通知 安全说明：为了减少群集攻击面，保护 API 服务器至关重要。Shadowserver 基金会进行了一项实验，发现了 380,000 个可公开访问的 Kubernetes API 服务器。\n2. etcd # Kubernetes 是一个分布式系统，它需要一个高效的分布式数据库，如 etcd 来支持其分布式性质。它既充当后端服务发现，又充当数据库。你可以称它为 Kubernetes 集群的大脑。\netcd 是一个开源的强一致性分布式键值存储。那么这意味着什么呢？\n强一致性：如果对某个节点进行了更新，则强一致性将确保它立即更新到群集中的所有其他节点。此外，如果你看一下 CAP 定理，就不可能实现 100%的可用性，并具有很强的一致性和分区容错 分布式：etcd 被设计为在不牺牲一致性的情况下作为集群在多个节点上运行 键值存储：将数据存储为键和值的非关系数据库。它还公开键值 API。数据存储建立在 BboltDB 之上，BboltDB 是 BoltDB 的一个分支 etcd 使用 raft 共识算法，具有很强的一致性和可用性。它以领导者成员的方式工作，以实现高可用性并承受节点故障。\n那么 etcd 是如何与 Kubernetes 一起工作的呢？\n简单地说，当你使用 kubectl 获取 kubernetes 对象详细信息时，你是从 etcd 获取的。此外，当您部署像 pod 这样的对象时，会在 etcd 中创建一个条目。\n简而言之，这是您需要了解的有关 etcd 的信息。\netcd 存储 Kubernetes 对象的所有配置、状态和元数据（pod、secret、daemonsets、deployments、configmaps、statefulsets 等）。 etcd 允许客户端使用 Watch() API 订阅事件。Kubernetes api-server 使用 etcd 的监视功能来跟踪对象状态的变化。 etcd 使用 gRPC 公开键值 API。此外，gRPC 网关是一个 RESTful 代理，可将所有 HTTP API 调用转换为 gRPC 消息。这使它成为 Kubernetes 的理想数据库。 etcd 以键值格式存储所有对象，在 /registry 目录项下。例如，可以在 /registry/pods/default/nginx 下找到默认命名空间中名为 Nginx 的 Pod 的信息 此外，etcd 是控制平面中唯一的 Statefulset 组件。\n3. kube-scheduler # kube-scheduler 负责在工作节点上调度 Kubernetes Pod。\n部署容器时，您可以指定容器要求，例如 CPU、内存、关联性、污点或容错、优先级、持久卷 （PV） 等。调度程序的主要任务是识别创建请求，并为满足要求的 Pod 选择最佳节点。\n下图显示了调度程序工作原理的概述。\n在 Kubernetes 集群中，将有多个工作节点。那么调度程序是如何从所有工作节点中选择节点的呢？以下是调度程序的工作原理。\n为了选择最佳节点，Kube-scheduler 使用过滤和评分操作。\n在筛选中，调度程序会找到最适合调度 Pod 的节点。\n例如，如果有 5 个节点有充足资源来运行 Pod，则它会选择所有 5 个节点。如果没有节点，则 Pod 不可调度并移动到调度队列。如果它是一个大型集群，假设有 100 个工作节点，并且调度程序不会遍历所有节点。有一个名为 percentageOfNodesToScore 的调度器配置参数。默认值通常为 50%。因此，它试图以循环方式迭代超过 50% 的节点。如果工作器节点分布在多个区域中，则调度程序将遍历不同区域中的节点。对于非常大的集群，默认 percentageOfNodesToScore 值为 5%。\n在评分阶段，调度程序通过向筛选的工作器节点分配分数来对节点进行排名。调度程序通过调用多个调度插件进行评分。最后，将选择排名最高的 worker 节点来调度 pod。如果所有节点的排名相同，则将随机选择一个节点。\n选择节点后，调度程序将在 API 服务器中创建绑定事件。表示绑定 Pod 和节点的事件。\n有关调度程序，你需要了解：\n它是一个控制器，用于侦听 API 服务器中的 Pod 创建事件。 调度程序有两个阶段。调度周期和绑定周期。统称为调度上下文。调度周期选择工作器节点，绑定周期将该更改应用于集群。 调度程序始终将高优先级的 Pod 放在低优先级的 Pod 之前进行调度。此外，在某些情况下，在 Pod 开始在所选节点中运行后，Pod 可能会被逐出或移动到其他节点。如果您想了解更多信息，请阅读 Kubernetes Pod 优先级指南 您可以创建自定义调度程序，并在集群中运行多个调度程序以及原生调度程序。部署 Pod 时，您可以在 Pod 清单中指定自定义调度程序。因此，调度决策将基于自定义调度程序逻辑进行。 调度程序具有可插拔的调度框架。这意味着，您可以将自定义插件添加到调度工作流程中。 4. Kube Controller Manager # 什么是控制器？控制器是运行无限控制循环的程序。这意味着它连续运行并监视对象的实际和所需状态。如果实际状态和期望状态存在差异，则确保 kubernetes 资源/对象处于期望状态。\n根据官方文件，\n在 Kubernetes 中，控制器是监视集群状态的控制循环，然后在需要时进行更改或请求更改。每个控制器都尝试将当前集群状态移近所需状态。\n假设要创建部署，在清单 YAML 文件中指定所需的状态（声明性方法）。例如，2 个副本、1 个卷挂载、configmap 等。内置的部署控制器可确保部署始终处于所需状态。如果用户使用 5 个副本更新部署，则部署控制器会识别它并确保所需的状态为 5 个副本。\nKube Controller Manager 是管理所有 Kubernetes 控制器的组件。Kubernetes 资源/对象（如 Pod、命名空间、作业、副本集）由各自的控制器管理。此外，Kube-Scheduler 也是一个由 Kube-Controller-Manager 管理的控制器。\n以下是重要的内置 Kubernetes 控制器列表。\nDeployment controller ReplicaSet controller DaemonSet controller Job controller CronJob controller endpoints controller namespace controller service account controller Node controller 以下是您应该了解的有关 Kube Controller Manager 的信息。\n它管理所有控制器，控制器尝试将集群保持在所需状态。 您可以使用与自定义资源定义关联的自定义控制器来扩展 kubernetes。 5. Cloud Controller Manager（CCM） # 在云环境中部署 kubernetes 时，云控制器管理器充当云平台 API 和 Kubernetes 集群之间的桥梁。\n这样，kubernetes 核心组件可以独立工作，并允许云提供商使用插件与 kubernetes 集成。（例如，kubernetes 集群和 AWS 云 API 之间的接口）\n云控制器集成允许 Kubernetes 集群预置云资源，例如实例（用于节点）、负载均衡器（用于服务）和存储卷（用于持久卷）。\n云控制器管理器包含一组特定于云平台的控制器，可确保特定于云的组件（节点、负载均衡器、存储等）处于所需状态。以下是云控制器管理器中的三个主要控制器。\n节点控制器（Node controller）：此控制器通过与云提供商 API 通信来更新与节点相关的信息。例如，节点标记和注释，获取主机名，CPU 和内存可用性，节点运行状况等。 路由控制器（Route controller）：负责在云平台上配置组网路由。这样不同节点的 Pod 就可以相互通信。 服务控制器（Service controller）：它负责为 kubernetes 服务部署负载均衡器、分配 IP 地址等。 以下是云控制器管理器的一些经典示例。\n部署负载均衡器类型的 Kubernetes 服务。在这里，Kubernetes 预置了特定于云的负载均衡器，并与 Kubernetes 服务集成。 为云存储解决方案支持的 Pod 配置存储卷 （PV）。 总体而言，Cloud Controller Manager 管理 Kubernetes 使用的云特定资源的生命周期。\nKubernetes 工作节点组件 # 现在，让我们看一下工作节点上的组件。\n1. Kubelet # Kubelet 是一个 Agent 组件，运行在集群中的每个节点上。Kubelet 不作为容器运行，而是作为守护程序运行，由 systemd 管理。\n它负责向 API 服务器注册工作节点，并主要从 API 服务器使用 podSpec（Pod 规范 – YAML 或 JSON）。podSpec 定义了应在 Pod 内运行的容器、它们的资源（例如 CPU 和内存限制）以及其他设置，例如环境变量、卷和标签。\n然后，它通过创建容器将 podSpec 带到所需状态。\nKubelet 负责以下工作。\n创建、修改和删除容器。 负责处理活跃度、准备情况和启动探测。 负责通过读取 Pod 配置并在主机上为卷挂载创建相应的目录来挂载卷。 通过调用 API 服务器来收集和报告节点和 Pod 状态 ，实现方式为 cAdvisor 和 CRI。 Kubelet 也是一个控制器，它监视 Pod 的变化，并利用节点的容器运行时来拉取镜像、运行容器等。\n除了来自 API 服务器的 PodSpec 之外，Kubelet 还可以接受来自文件、HTTP 端点和 HTTP 服务器的 podSpec。“来自文件的 podSpec”的一个很好的例子是 Kubernetes 静态 pod。\n静态 Pod 由 Kubelet 控制，而不是由 API 服务器控制。这意味着您可以通过向 Kubelet 组件提供 Pod YAML 位置来创建 Pod。但是，Kubelet 创建的静态 Pod 不受 API 服务器的管理。下面是静态 Pod 的真实示例用例。\n在启动控制平面各组件时，kubelet 从 /etc/kubernetes/manifests 读取 podSpecs，启动 api-server、scheduler 和 controller-manager 作为静态 pod。\n以下是关于 kubelet 的一些关键内容。\nKubelet 使用 CRI（容器运行时接口）gRPC 接口与容器运行时通信。 它还公开 HTTP 终结点以流式传输日志，并为客户端提供 exec 会话。 使用 CSI（容器存储接口）gRPC 配置块存储卷。 它使用集群中配置的 CNI 插件来分配 Pod IP 地址，并为 Pod 设置任何必要的网络路由和防火墙规则。 2. Kube proxy # 要了解 Kube proxy，您需要对 Kubernetes 服务和端点对象有基本的了解。\nKubernetes 中的服务是一种在内部或向外部流量公开一组 Pod 的方法。创建服务对象时，它会为其分配一个虚拟 IP。它被称为 clusterIP。它只能在 Kubernetes 集群中访问。\nEndpoint 对象包含 Service 对象下 Pod 组的所有 IP 地址和端口。端点控制器负责维护容器 IP 地址（端点）列表。服务控制器负责为服务配置端点。\n您无法 ping ClusterIP，因为它仅用于服务发现，这与可 ping 的 Pod IP 不同。\n现在让我们了解一下 Kube Proxy。\nKube-proxy 是一个守护进程，它作为 daemonset 在每个节点上运行。它是一个代理组件，用于实现 Pod 的 Kubernetes 服务概念。（具有负载均衡功能的一组 Pod 的单个 DNS）。它主要代理 UDP、TCP 和 SCTP，不理解 HTTP。\n当您使用 Service （ClusterIP） 公开 Pod 时，Kube-proxy 会创建网络规则，将流量发送到 Service 对象下分组的后端 Pod（端点）。这意味着，所有负载均衡和服务发现都由 Kube proxy 处理。\n那么 Kube-proxy 是如何工作的呢？\nKube proxy 与 API 服务器通信，以获取有关服务 （ClusterIP） 和相应 Pod IP 和端口（端点）的详细信息。它还监视服务和端点的更改。\n然后，kube-proxy 使用以下任一模式创建/更新规则，将流量路由到 Service 后面的 Pod。\nIPTables：这是默认模式。在 IPTables 模式量由 IPtable 规则处理。这意味着，对于每个服务，都会创建 IPtable 规则。这些规则捕获进入 ClusterIP 的流量，然后将其转发到后端 Pod。此外，在这种模式下，kube-proxy 会随机选择后端 pod 进行负载均衡。建立连接后，请求将转到同一 Pod，直到连接终止。 IPVS：对于服务超过 1000 的集群，IPVS 提供性能改进。它支持以下后端负载均衡算法。 rr ：这是默认模式。 lc ：最少连接数（最小打开连接数） dh ：目标哈希 sh ：源哈希 sed ：最短的预期延迟 nq ：从不排队 Userspace：用户空间（旧版和不推荐） Kernelspace：此模式仅适用于 Windows 系统。 如果您想了解 kube-proxy IPtables 和 IPVS 模式之间的性能差异，请阅读本文。\n此外，您可以通过将 Kubernetes 集群替换为 Cilium 来运行没有 kube-proxy 的 Kubernetes 集群。\n1.29 Alpha 功能：Kubeproxy 有一个基于 nftables 的新后端。nftables 是 IPtables 的继任者，旨在更简单、更高效\n3. Container Runtime # 您可能了解 Java 运行时 （JRE）。它是在主机上运行 Java 程序所需的软件。同样，容器运行时是运行容器所需的软件组件。\n容器运行时在 Kubernetes 集群中的所有节点上运行。它负责从容器注册表中提取镜像、运行容器、分配和隔离容器资源，以及管理主机上容器的整个生命周期。\n为了更好地理解这一点，让我们看一下两个关键概念：\n容器运行时接口 （CRI）：它是一组 API，允许 Kubernetes 与不同的容器运行时进行交互。它允许不同的容器运行时与 Kubernetes 互换使用。CRI 定义了用于创建、启动、停止和删除容器以及管理映像和容器网络的 API。 开放容器倡议 （OCI）：它是一组容器格式和运行时的标准。 Kubernetes 支持多个符合容器运行时接口 （CRI） 的容器运行时（CRI-O、Docker Engine、containerd 等）。这意味着，所有这些容器运行时都实现了 CRI 接口，并公开了 gRPC CRI API（运行时和镜像服务端点）。\n那么 Kubernetes 是如何利用容器运行时的呢？\n正如我们在 Kubelet 部分所了解的，kubelet 代理负责使用 CRI API 与容器运行时进行交互，以管理容器的生命周期。它还从容器运行时获取所有容器信息，并将其提供给控制平面。\n我们以 CRI-O 容器运行时接口为例。下面是容器运行时如何与 Kubernetes 配合使用的高级概述。\n当 API 服务器对 Pod 有新的请求时，kubelet 会与 CRI-O 守护进程通信，以通过 Kubernetes 容器运行时接口启动所需的容器。 CRI-O 使用 containers/image 库检查并从配置的容器注册表中提取所需的容器映像。 然后，CRI-O 为容器生成 OCI 运行时规范 （JSON）。 然后，CRI-O 启动与 OCI 兼容的运行时 （runc） 以根据运行时规范启动容器进程。 Kubernetes 集群插件组件 # 除了核心组件之外，kubernetes 集群还需要附加组件才能完全运行。选择插件取决于项目要求和应用场景。\n以下是集群上可能需要的一些常用插件组件。\nCNI 插件（容器网络接口） CoreDNS（用于 DNS 服务）：CoreDNS 充当 Kubernetes 集群中的 DNS 服务器。通过启用此插件，您可以启用基于 DNS 的服务发现。 Metrics Server（用于资源指标）：此插件可帮助您收集集群中节点和 Pod 的性能数据和资源使用情况。 Web UI（Kubernetes 仪表板）：此插件使 Kubernetes 仪表板能够通过 Web UI 管理对象。 1. CNI Plugin # 首先，您需要了解容器网络接口 （CNI）。它是一个基于插件的架构，具有供应商中立的规范和库，用于为容器创建网络接口。它不是特定于 Kubernetes 的。借助 CNI，容器网络可以在 Kubernetes、Mesos、CloudFoundry、Podman、Docker 等容器编排工具之间实现标准化。\n在容器网络方面，公司可能有不同的要求，例如网络隔离、安全性、加密等。随着容器技术的进步，许多网络提供商为具有广泛网络功能的容器创建了基于 CNI 的解决方案。你可以称它为 CNI-Plugins。\n这允许用户从不同的提供商处选择最适合其需求的网络解决方案。\nCNI 插件如何与 Kubernetes 配合使用？\nKube-controller-manager 负责为每个节点分配 Pod 网段。每个 Pod 从 Pod CIDR 获取一个唯一的 IP 地址。 Kubelet 与容器运行时交互以启动定时 Pod。作为容器运行时一部分的 CRI 插件与 CNI 插件交互以配置 Pod 网络。 CNI 插件支持使用叠加网络在分布在相同或不同节点上的 Pod 之间联网。 以下是 CNI 插件提供的高级功能。\nPod 网络 Pod 网络安全和隔离，使用网络策略来控制 Pod 之间和命名空间之间的流量。 一些流行的 CNI 插件包括：\nCalico Flannel Weave Net Cilium(Uses eBPF) Amazon VPC CNI (For AWS VPC) Azure CNI Kubernetes 原生对象 # 到目前为止，我们已经了解了核心 kubernetes 组件以及每个组件的工作原理。所有这些组件都致力于管理以下关键 Kubernetes 对象。\nPod Namespace Replicaset Deployment Daemonset Statefulset Jobs \u0026amp; Cronjobs ConfigMaps \u0026amp; Secrets 在网络方面，以下 Kubernetes 对象起着关键作用。\nService Ingress NetworkPolicy 此外，Kubernetes 可以使用 CRD 和自定义控制器进行扩展。因此，群集组件还管理使用自定义控制器和自定义资源定义创建的对象。\nKubernetes 架构常见问题解答 # Kubernetes 控制平面的主要用途是什么？ # 控制平面负责维护集群及其上运行的应用程序的所需状态。它由 API Server、etcd、Scheduler 和 Controller manager 等组件组成。\nKubernetes 集群中工作节点的用途是什么？ # 工作器节点是在群集中运行容器的服务器（裸机或虚拟）。它们由控制平面管理，并从控制平面接收有关如何运行属于 Pod 的容器的指令。\n如何在 Kubernetes 中保护控制平面和工作节点之间的通信？ # 控制平面和工作节点之间的通信使用 PKI 证书进行保护，不同组件之间的通信通过 TLS 进行。这样，只有受信任的组件才能相互通信。\nKubernetes 中 etcd 键值存储的目的是什么？ # Etcd 主要存储集群的 kubernetes 对象、集群信息、节点信息以及集群的配置数据，例如集群上运行的应用程序的期望状态。\n如果 etcd 宕机，Kubernetes 应用程序会发生什么？ # 虽然如果 etcd 遇到中断，正在运行的应用程序不会受到影响，但如果没有正常运行的 etcd，将无法创建或更新任何对象。\n总结 # 了解 Kubernetes 架构有助于您进行日常 Kubernetes 实施和操作。在实施生产级群集设置时，对 Kubernetes 组件有正确的了解将有助于你运行应用程序并对其进行故障排除。接下来，您可以从分步 kubernetes 教程开始，获得 Kubernetes 对象和资源的实践经验。\n本文翻译自: https://devopscube.com/kubernetes-architecture-explained/\n","date":"2024-11-21","externalUrl":null,"permalink":"/archives/Kubernetes-architecture-and-data-flow.html","section":"文章","summary":"","title":"10 张图，说透 Kubernetes 架构和数据流","type":"posts"},{"content":"","date":"2024-11-19","externalUrl":null,"permalink":"/tags/vmware/","section":"标签","summary":"","title":"Vmware","type":"tags"},{"content":"最近给电脑做了新版的 Windows 11 LTSC操作系统，基于24H2，在启动VMware Workstation时，提示\u0026quot;此虚拟机已启用侧通道缓解，可增强安全性，但也会降低性能\u0026quot;，但是我没有启用 Hyper-V 相关的任何功能以及 WSL，\n从网上找到的文章大部分都是让你在VMware中开启 “关闭侧通道缓解”选项的，这个只有在 VMware Workstation 15.5以上的版本有此功能，但是这个相当于 Hyper-V 与 VMware共存了，会极大的降低性能，由于Hyper-V是微软的亲儿子，Windows中的虚拟化技术都被 Hyper-V独占，因此除非完全关闭 Hyper-V ，否则就算你开启了 “关闭侧通道缓解”选项，也无法使用嵌套虚拟化功能\n解决方法 # 方法1： # 网上看到的大部分就是这种方法， 开启“关闭侧通道缓解”选项\n参考：https://knowledge.broadcom.com/external/article?legacyId=79832\n但是使用此方法，与在传统模式下运行的 Workstation Pro 虚拟机相比，在主机 VBS 模式下运行的虚拟机存在一些功能限制。\n与传统模式下的虚拟机相比，主机 VBS 模式下的虚拟机可能会运行较慢，具体取决于工作负载。WHP 功能集引入的限制和使用开销会导致出现这些问题。\n以下是在主机 VBS 模式下运行的 Workstation Pro 虚拟机的功能限制列表：\n不支持嵌套虚拟化： 在主机 VBS 模式虚拟机上运行的客户机无法使用 x86 虚拟化功能 (Intel VT/AMD-V)。因此，此类虚拟机无法自行运行启用了 Hyper-V 或 VBS 的 Windows。\n不支持 PMC： x86 性能监控计数器 (Performance Monitoring Counter, PMC) 不可用。\n不支持 RTM 和 HLE： 受限事务内存和硬件锁省略功能不可用。\n不支持 PKU： 用户模式保护密钥功能不可用。\n方法2： # 完全禁用 Hyper-V\n使用”添加或删除Windows组件“图形界面程序，在里面取消勾选Hyper-V。\n然后使用管理员身份打开 PowerShell，执行如下命令\ndism /online /disable-feature /featurename:Microsoft-hyper-v-all DISM /Online /Disable-Feature:Microsoft-Hyper-V bcdedit /set hypervisorlaunchtype off 在 Windows Security 中 关闭内存完整性校验\n然后重启你的计算机\n方法3 # 这个方法仅适用于 Windows 11 24H2版本以上，因为，自 Windows 11 24H2 起，Microsoft 为所有全新安装和升级的 Windows 11 默认开启了 Device/Credential Guard，这可能会影响 VMware Workstation 及其它虚拟机在内的虚拟化产品。但是 Microsoft 提供了一个 Device Guard and Credential Guard hardware readiness tool 来关闭 Device/Credential Guard。\n下载地址：https://www.microsoft.com/en-us/download/details.aspx?id=53337\n下载完成之后解压到某个文件夹中，例如C:\\Users\\wang\\Downloads\\Compressed\\dgreadiness_v3.6\n然后，右键开始菜单，选择 Windows PowerShell(管理员)\n复制你解压的文件夹地址\n然后，先在终端或 PowerShell 中输入 cd，按下空格后粘贴复制到剪切板的路径，并按下回车。\n然后执行如下命令\n.\\DG_Readiness_Tool_v3.6.ps1 -Disable 然后完成后关闭终端或 PowerShell，并保存所有打开的文件后，重启电脑。\n重启后 Windows 会进入 Credential Guard Opt-out Tool，此时按下 Windows 徽标键或 F3 后再按下回车即可继续。\n完成后电脑会再次重启，进入 Windows 后再次使用PowerShell定位到 C:\\Users\\wang\\Downloads\\Compressed\\dgreadiness_v3.6\n执行 .\\DG_Readiness_Tool_v3.6.ps1 -Ready 可以看到 Device/Credential Guard 已关闭\n","date":"2024-11-19","externalUrl":null,"permalink":"/archives/vmware-79832.html","section":"文章","summary":"","title":"解决 VMware 嵌套虚拟化提示 关闭\u0026quot;侧通道缓解","type":"posts"},{"content":"","date":"2024-11-18","externalUrl":null,"permalink":"/tags/gcc/","section":"标签","summary":"","title":"Gcc","type":"tags"},{"content":"","date":"2024-11-18","externalUrl":null,"permalink":"/tags/lua/","section":"标签","summary":"","title":"Lua","type":"tags"},{"content":"","date":"2024-11-18","externalUrl":null,"permalink":"/tags/make/","section":"标签","summary":"","title":"Make","type":"tags"},{"content":"","date":"2024-11-18","externalUrl":null,"permalink":"/tags/openresty/","section":"标签","summary":"","title":"Openresty","type":"tags"},{"content":"今天在使用 make 编译 luafilesystem出现了以下问题\nmake: Nothing to be done for \u0026#39;lib\u0026#39;. 意思就是 make 没有执行任何编译操作，它可能认为所有目标文件都已经是最新的了\n这个问题一般有两个原因引起\n已经是最新的了： make 会根据源文件和目标文件的时间戳来决定是否需要重新编译 Makefile 配置问题： Makefile 中的编译规则可能没有正确处理 CFLAGS 或没有正确指定编译目标 解决方法 # 1. 强制重新编译 # 最简单的办法就是清理旧的编译文件，然后重新编译\nmake clean make CFLAGS=\u0026#34;-I/usr/local/openresty/luajit/include/luajit-2.1 -fPIC\u0026#34; make clean 会删除已经编译的文件（ .o 或 .so 文件），然后再执行 make，重新编译。\n2. 检查 Makefile 配置 # 如果执行 make clean 后问题还是有问题，那有可能就是是 Makefile 中的配置存在问题，导致 make 没有正确地识别到需要编译的文件\n查看 Makefile 中的编译规则，确保 CFLAGS 被正确传递给编译器\nCFLAGS += -fPIC 确认编译规则为以下内容\nsrc/lfs.o: src/lfs.c gcc $(CFLAGS) -c src/lfs.c -o src/lfs.o 这样，gcc 会用 CFLAGS 中的选项进行编译，如果 Makefile 配置有问题的话就可能无法识别出需要重新编译的文件\n3. 手动编译源文件 # 如果上面两种方法都没用，可以尝试手动编译源文件，确认编译器是否能够正确地生成 .o文件，链接成共享库 .so\n编译源文件：\ngcc -I/usr/local/openresty/luajit/include/luajit-2.1 -fPIC -c src/lfs.c -o src/lfs.o 如果没问题就将 .o 文件链接为 .so 文件即可\ngcc -shared -o src/lfs.so src/lfs.o ","date":"2024-11-18","externalUrl":null,"permalink":"/archives/make-luafilesystem.html","section":"文章","summary":"","title":"解决 编译时产生的 Nothing to be done for 'lib' 问题","type":"posts"},{"content":" IntelliJ IDEA Goland Pycharm 2024.3.x 激活教程 # 激活方式 # 激活补丁 + 激活码方式激活， 补丁的原理是拦截了软件的剩余使用期网络请求，实际上等于永久激活，最新的版本只能显示到 2025 年, 其实效果还是一样的。\n从官方下载最新版本呢的IDE安装包，完了之后呢需要派出我们的老朋友了，ja-netfilter。\n经过实测，其实通过22年的激活码配合插件也能成功激活jetbrains中的软件，这里我只测试了我常用的PhpStorm，Goland，Pycharm，WebStorm。\n这里提供的激活码是最新的！！！\nja-netfilter教程 # 将插件下载完成之后解压到一个目录（这里以E:\\tools\\ja-netfilter 为例）\n这里以Pycharm为例。我们需要编辑pycharm安装目录下的 pycharm64.exe.vmoptions 文件，你可以找到你的 Pycharm 快捷方式，右键选择打开文件位置，找到文件pycharm64.exe.vmoptions，并使用记事本打开该文件\n在此文件的最后一行增加如下内容\n--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED --add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED -javaagent:E:\\tools\\ja-netfilter\\ja-netfilter.jar 其中，“E:\\tools\\ja-netfilter\\ja-netfilter.jar” 文件应改修改为您的实际路径。\n然后启动Pycharm，选择 Activation code，并输入序列号，激活即可，其他IDE激活方法基本一样\n[RainBowText] PS: 获取激活码与插件请关注下方公众号，回复 \u0026ldquo;jetbrains\u0026rdquo; 获取 [/RainBowText]\n激活码： # IDEA 2024.x # 6G5NXCPJZB-eyJsaWNlbnNlSWQiOiI2RzVOWENQSlpCIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBEQiIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiSUkiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQUEMiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBHTyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFNXIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQV1MiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBQUyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFJCIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQQ1dNUCIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX1dLCJtZXRhZGF0YSI6IjAxMjAyMjA5MDJQU0FOMDAwMDA1IiwiaGFzaCI6IlRSSUFMOi0xMDc4MzkwNTY4IiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-SnRVlQQR1/9nxZ2AXsQ0seYwU5OjaiUMXrnQIIdNRvykzqQ0Q+vjXlmO7iAUwhwlsyfoMrLuvmLYwoD7fV8Mpz9Gs2gsTR8DfSHuAdvZlFENlIuFoIqyO8BneM9paD0yLxiqxy/WWuOqW6c1v9ubbfdT6z9UnzSUjPKlsjXfq9J2gcDALrv9E0RPTOZqKfnsg7PF0wNQ0/d00dy1k3zI+zJyTRpDxkCaGgijlY/LZ/wqd/kRfcbQuRzdJ/JXa3nj26rACqykKXaBH5thuvkTyySOpZwZMJVJyW7B7ro/hkFCljZug3K+bTw5VwySzJtDcQ9tDYuu0zSAeXrcv2qrOg==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD Goland 2024.x # JZFLGE21ND-eyJsaWNlbnNlSWQiOiJKWkZMR0UyMU5EIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBHTyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFdTIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJHTyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6ZmFsc2V9LHsiY29kZSI6IlBDV01QIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfV0sIm1ldGFkYXRhIjoiMDEyMDIyMDkwMlBTQU4wMDAwMDUiLCJoYXNoIjoiVFJJQUw6LTE1NTI3NDEyNzIiLCJncmFjZVBlcmlvZERheXMiOjcsImF1dG9Qcm9sb25nYXRlZCI6ZmFsc2UsImlzQXV0b1Byb2xvbmdhdGVkIjpmYWxzZX0=-cQoXljKE7uBjoI23ZBPd84P89vQAaprVvVhxj1/nSOABUZKPZMCs6p2id5WKBP1n0EoPHykvzd0y7QcdcO2hxNwbaBRZxpPFuuMigGazrrt+zSd9+UlFXmtcseFTXlWn7Jw2qqp7WSVQlj+o3gf2Gic9/Vp2pAmIajMGdFDsXcHbYUrLwTRVtFkNtggtsqh0AVS8xhzLuXMzGXJsqEEWPZCKMEjy0bURm/FQvrRC/gIptJzuybSb/neqKmvxZefX1lhe98igS3tZsL/E+hsQK7ueXVSP2AuuVEz/6qHYknPS7K4UluWpd+ktIxTcHjIykK1tlWlwTsupZTxnvXFWQA==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD PyCharm 2024.x # EUWT4EE9X2-eyJsaWNlbnNlSWQiOiJFVVdUNEVFOVgyIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBDIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUFBDIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQV1MiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBDV01QIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfV0sIm1ldGFkYXRhIjoiMDEyMDIyMDkwMlBTQU4wMDAwMDUiLCJoYXNoIjoiVFJJQUw6MzUzOTQ0NTE3IiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-FT9l1nyyF9EyNmlelrLP9rGtugZ6sEs3CkYIKqGgSi608LIamge623nLLjI8f6O4EdbCfjJcPXLxklUe1O/5ASO3JnbPFUBYUEebCWZPgPfIdjw7hfA1PsGUdw1SBvh4BEWCMVVJWVtc9ktE+gQ8ldugYjXs0s34xaWjjfolJn2V4f4lnnCv0pikF7Ig/Bsyd/8bsySBJ54Uy9dkEsBUFJzqYSfR7Z/xsrACGFgq96ZsifnAnnOvfGbRX8Q8IIu0zDbNh7smxOwrz2odmL72UaU51A5YaOcPSXRM9uyqCnSp/ENLzkQa/B9RNO+VA7kCsj3MlJWJp5Sotn5spyV+gA==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD PHPStorm 2024.x # DXHN0R5Z4F-eyJsaWNlbnNlSWQiOiJEWEhOMFI1WjRGIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBTIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUFdTIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQUFMiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBDV01QIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfV0sIm1ldGFkYXRhIjoiMDEyMDIyMDkwMlBTQU4wMDAwMDUiLCJoYXNoIjoiVFJJQUw6LTE2MDY1NjU0NzQiLCJncmFjZVBlcmlvZERheXMiOjcsImF1dG9Qcm9sb25nYXRlZCI6ZmFsc2UsImlzQXV0b1Byb2xvbmdhdGVkIjpmYWxzZX0=-PXxjbi2g20tUcfzptAnY/ZIKQbXW3FwXJ3P5scBMkXrya0Wg0YviqciwEWJe5laYfWevv8RzEEwrAcxz5ug1RYrg4bjZ7mJiQoOatsKahqGT1TDfU5YGpZPfbtjlKHAlYSszioJ0pka6go2qBa5AaPj4HmKfB4jkeGX4inRxPrIxc8PyAo0wyWWEaZ7R4pLC4ZDqIAYJ/IzXIf1V84+p5hGYd6QD6ze1BCEGxI/E8LjloaIvv0oNdkV2qTyDCH9IF3E42iheOwazyrlOWYivYyn/ew0kvjGwwSdbrbIJH7SSbaz2nxlBWnR04yJaI4txnnS1eUbiUNQtEhKJ3uD6oQ==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD RubyMine 2024.x # Q2AJCW53PE-eyJsaWNlbnNlSWQiOiJRMkFKQ1c1M1BFIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlJNIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjpmYWxzZX0seyJjb2RlIjoiUFdTIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQUkIiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBDV01QIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfV0sIm1ldGFkYXRhIjoiMDEyMDIyMDkwMlBTQU4wMDAwMDUiLCJoYXNoIjoiVFJJQUw6NDA1MTEyNjE5IiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-aPzOhosQS2wKjDd+az9MTUKLm3XarDs+2uAYs6LysK1DzwzKCMsXpd5Ci3dPc6hJmlL0+pW+P0h7+4ytG3ZFk38oYv4qm1NpOjeXVMyW9I2/JM508J3RqzG+NQvuxkojBuzOLBP8uDLMUcGSVbANMgNWbzu4u9cQdwaKqxZd6lweU2PnCe8gfJpHxrJxDRJ24+S1o14aYhFLo8LhZCfj91KNDA3gyu82OyPU2jwfDgxEj5DHyBTaQsspJA4RfSie/6vO7RRPLaDT91eGTzIlpPSsiBA4OtypIjqJSXrMaBFhVbAE9x3eWeV6EziCaVua69BKn0zkXYqCzgLWJbpVmw==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD DataGrip 2024.x # VPQ9LWBJ0Z-eyJsaWNlbnNlSWQiOiJWUFE5TFdCSjBaIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBEQiIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiREIiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQV1MiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9XSwibWV0YWRhdGEiOiIwMTIwMjIwOTAyUFNBTjAwMDAwNSIsImhhc2giOiJUUklBTDotMjI4OTE3MTciLCJncmFjZVBlcmlvZERheXMiOjcsImF1dG9Qcm9sb25nYXRlZCI6ZmFsc2UsImlzQXV0b1Byb2xvbmdhdGVkIjpmYWxzZX0=-Y35sXvjNUN0+WMouR7PGFzl62+ApqSBgPjwganzPG1ErZKJS1xh3O8MbkZnjiTPkjODi+pyBGjozBojATCzTGx4uCt61zccnyF+XI1fE9H9WTA5DAO3/maxGFJ2KthOmkuktNnNi9qa0n7EyRuZ8rVVrc5+ETiEcfo2GctNcGqHKuRF96Bf8EL1GmCOkI8vU293X+n3XKEmN+Q1hEEBGbP9yiF5zhZDzFq0svj8g4c0fnTOrCc4GKyyEps6aDW2/DurkotUFddzDhTCyoW1Gao0EZt0AnWUALsogI9ABjdtJq3ndyvuMrXTlOr2C/hCGjqIN8ZDA0gf9mnrKBhcMbA==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD 仅供个人学习使用，禁止用于任何商业用途，如果您觉得还可以请支持正版\n插件下载 ","date":"2024-11-15","externalUrl":null,"permalink":"/archives/jetbrains-all.html","section":"文章","summary":"","title":"IntelliJ IDEA Goland Pycharm 2024.3.x 激活教程","type":"posts"},{"content":"","date":"2024-11-15","externalUrl":null,"permalink":"/tags/jetbrains/","section":"标签","summary":"","title":"Jetbrains","type":"tags"},{"content":"RustDesk 是一款开源的远程桌面工具，它允许用户通过互联网或局域网远程控制另一台计算机，支持用自己的云服务器、NAS等设备来搭建专属的RustDesk中继服务器，获得更好的体验\n搭建教程 # 下载 Rustdesk Server # https://github.com/rustdesk/rustdesk-server/releases\n因某种原因国内访问 GitHub 的速度会很慢，这里推荐大家使用 github 代理进行下载 (这里我用的是Linux版本)\nDocker Github加速代理点我 wget https://ghproxy.cnproxy.top/https://github.com/rustdesk/rustdesk-se rver/releases/download/1.1.12/rustdesk-server-linux-amd64.zip 解压Rustdesk Server # 下载完成后解压RustDesk软件包，如遇unzip: command not found请先安装 unzip\n解压完成之后会有三个文件，如上图所示，这里对这三个文件简要介绍一下；\nhbbr：\nhbbr是RustDesk Relay Server，即RustDesk中继服务器，当客户端之间无法直接建立P2P连接时，会通过hbbr中继服务器进行数据传输hbbr允许无法直接建立P2P连接的客户端通过中继服务器进行通信。 hbbs：\nhbbs代表RustDesk ID / Rendezvous Server，即RustDesk ID注册服务器。它用于分配和注册ID，并且是RustDesk的中介服务器（Broker Server），用于管理和协调客户端连接。hbbs帮助客户端找到并建立P2P连接，负责维护客户端的在线状态，并处理连接请求。当客户端A希望连接客户端B时，它会向hbbs发送请求，hbbs会帮助它们建立连接。 rustdesk-utils：\nrustdesk-utils是RustDesk的命令行工具，提供了一些管理和操作RustDesk服务器端的工具和命令。 配置守护进程 # 现在我们需要将hbbr hbbs 这两个服务启动起来，这里推荐编写 systemd unit 服务单元来守护进程，从而实现开机自启。\n/etc/systemd/system/hbbr.service\n[Unit] Description=RustDesk Relay Server After=network.target [Service] Type=simple ExecStart=/root/amd64/hbbr Restart=on-failure WorkingDirectory=/root/amd64 [Install] WantedBy=multi-user.target /etc/systemd/system/hbbs.service\n[Unit] Description=RustDesk ID/Rendezvous Server After=network.target [Service] Type=simple ExecStart=/root/amd64/hbbs Restart=on-failure WorkingDirectory=/root/amd64 [Install] WantedBy=multi-user.target 保存并关闭这两个文件后，需要重新加载 systemd 配置，启动服务，并设置它们在系统启动时自动启动\nsystemctl enable --now hbbr.service systemctl enable --now hbbs.service 放行端口 # RustDesk Server 共占用如下端口\n21115：用于NAT类型测试。 21116：用于TCP打洞与连接服务。 21117：用于中继服务。 21118：用于支持网页客户端。 21119：用于支持网页客户端。 UDP端口：\n21116：用于ID注册与心跳服务。 我们需要在防火墙中放行以上端口，如果是云服务器则需要再安全组中允许上述端口的流量通过\n获取Key # 在我们首次启动 RustDesk Server之后会自动生成一个名为 id_ed25519.pub的公钥文件以及，id_ed25519私钥文件，放在你的 RustDesk Server 目录中，这个密钥的主要用途是确保客户端和服务器之间的通信安全，这里我们主要会用到公钥文件，如果要获取新的密钥对的话把这俩玩意删掉重启一下hbbr hbbs服务就可以了。\n配置客户端 # 接下来从官网：rustdesk.com 获取到客户端并配置好中继服务器就可以了\nEnjoy it ~\n","date":"2024-11-09","externalUrl":null,"permalink":"/archives/rustdesk-server-deploy.html","section":"文章","summary":"","title":"自建Rustdesk服务器，在Linux系统中搭建RustDesk Server","type":"posts"},{"content":"","date":"2024-11-08","externalUrl":null,"permalink":"/tags/do447/","section":"标签","summary":"","title":"Do447","type":"tags"},{"content":"Do447 是RHCA体系中一门课程，主要围绕着Ansible高级自动化的最佳实践，适合经验丰富的红帽Ansible用户，可进阶自己的Ansible技能\n课程基于 Red Hat Ansible 2.8，Ansible Tower 3.5 ，以及 Red Hat Linux 8\n前提条件 # 已成为红帽认证系统管理员（RHCSA）\n红帽Ansible 自动化专家认证（RHCE）\n课程内容摘要 # 了解高效实用地使用Ansible实现自动化的推荐做法：探索如何更有效地使用Ansible进行自动化任务。 借助Ansible自动化操作执行滚动更新：学习如何使用Ansible进行滚动更新操作。 使用Ansible的高级功能来处理数据，包括过滤器和插件：掌握Ansible的高级功能，如过滤器和插件的使用。 借助Ansible Playbook，通过REST API控制应用：学习如何使用Ansible Playbook通过REST API控制应用程序。 实施红帽Ansible Tower，以集中协调和扩展红帽Ansible自动化：了解如何部署和使用Ansible Tower来集中管理和扩展Ansible自动化。 利用红帽Ansible Tower的功能来管理复杂的自动化工作流：学习如何使用Ansible Tower管理复杂的自动化工作流。 借助Git和红帽Ansible Tower，实现CI/CD业务流程自动化：掌握如何结合Git和Ansible Tower实现持续集成和持续部署(CI/CD)的自动化流程。 课程环境介绍 # 环境默认密码 # F0宿主机密码：root/Asimov F0普通用户密码；kiosk/redhat AMD CPU 额外补充 # 如果是AMD CPU可能会遇到下面的问题\nUnable to init server: Could not connect: Connection refused qemu-system-x86_64: error: failed to set MSR 0xe1 to 0x0 参考： 解决AMD CPU上的QEMU/KVM虚拟化兼容性问题 2024-11-08\u0026middot;1 分钟\u0026middot; 加载中 虚拟化 Kvm Qemu Amd 环境初始化 # 在我们首次开启环境之后，建议将环境 fullreset 初始化（重置）一下顺便关机打个快照。\nfullreset 只是在刚切换环境时操作，后面可以直接 reset 重置。\nrht-vmctl 命令只对配套的 RHV/RHEV 环境生效，并非 KVM 自带的管理工具\n以上就是关于Do447 环境的介绍，希望能对看到此文章的大佬们有所帮助。\n","date":"2024-11-08","externalUrl":null,"permalink":"/archives/rhca-do447-introduce.html","section":"文章","summary":"","title":"RHCA回忆录：RHCA DO447 介绍","type":"posts"},{"content":"","date":"2024-11-08","externalUrl":null,"permalink":"/categories/%E7%BA%A2%E5%B8%BD%E8%AE%A4%E8%AF%81/","section":"分类","summary":"","title":"红帽认证","type":"categories"},{"content":"","date":"2024-11-08","externalUrl":null,"permalink":"/tags/amd/","section":"标签","summary":"","title":"Amd","type":"tags"},{"content":"在使用QEMU进行虚拟化时，AMD CPU用户可能会遇到一系列兼容性问题，导致虚拟机无法正常启动。本文将介绍如何通过添加特定的KVM参数来解决这些问题。\n在AMD CPU上运行QEMU/KVM 虚拟化时，可能会遇到以下错误信息：\nMoTTY X11 proxy: Unsupported authorisation protocol Unable to init server: Could not connect: Connection refused qemu-system-x86_64: error: failed to set MSR 0xe1 to 0x0 qemu-system-x86_64: /build/qemu-BQ4hMP/qemu-4.2/target/i386/kvm.c:2691: kvm_buf_set_msrs: Assertion `ret == cpu-\u0026gt;kvm_msr_buf-\u0026gt;nmsrs\u0026#39; failed. Aborted 这些错误与 KVM 处理 MSR 的兼容性有关，所以我们可以向 KVM 模块添加一个参数,让 KVM 遇到未知 MSR 请求的时候忽略而不是抛出异常错误信息，我们可以这样做\ntee /etc/modprobe.d/qemu-system-x86.conf \u0026lt;\u0026lt; EOF options kvm ignore_msrs=1 EOF 上面的命令会在 /etc/modprobe.d/ 创建一个名为qemu-system-x86.conf 的配置文件，其内容为options kvm ignore_msrs=1,当 KVM 模块被加载时,就会应用这个参数，重启生效\n","date":"2024-11-08","externalUrl":null,"permalink":"/archives/amd-qemu-kvm.html","section":"文章","summary":"","title":"解决AMD CPU上的QEMU/KVM虚拟化兼容性问题","type":"posts"},{"content":"以下是运维工作中应避免的一些关键事项，以减少风险和潜在的损失\n误操作后的处理：运维误操作之后不可隐瞒，应尽快通知其他运维人员并迅速恢复。不要有侥幸心理，隐瞒不报会给公司带来更多损失。\n运行命令和脚本：禁止运行自己不理解的命令和未知脚本，尤其是在生产环境中，这可能会导致严重后果。\n生产环境的使用：生产环境禁止用于个人试验用途，禁止用来练习脚本技能等，以避免造成风险。\n软件的选择：禁止运行来路不明的破解软件，应优先选择开源软件或授权的商业软件，以防止木马病毒和勒索软件的传播。\n用户数据的处理：禁止从数据库中导出用户数据后，未经脱敏步骤就发送给其他人员。应从源头上保护用户数据安全，减少泄露风险。\n变更方案的准备：禁止变更方案中不包含回退方案，要有应对意外的准备，以应对突发情况。\n对待外包工程师：禁止以任何方式变相压榨外包运维工程师。尊重他人，并在运维负担增加时，为外包工程师争取福利。\n更新配置和数据：禁止在没有进行小范围试点的情况下大范围更新配置和数据。灰度发布能提前发现问题，避免大范围影响。\n变更后的检查：禁止变更后不检查业务，应验证业务是否正常，以确保变更的成功。\n敏感信息的保护：禁止将公司敏感文档、商业方案、产品关键架构和代码上传到外部网站，以防止无法预测的后果。\n身体状态与高风险变更：运维人员身体不舒服或状态不佳时，禁止进行高风险变更。应保持神智清晰，避免过量饮酒等影响工作状态的行为。\n声明：以上内容转载自知乎： https://zhuanlan.zhihu.com/p/822488366\nl\n","date":"2024-10-15","externalUrl":null,"permalink":"/archives/Reprinted-Prohibited-Matters-for-Newcomers-in-Operations-and-Maintenance.html","section":"文章","summary":"","title":"运维老司机留给运维新人的【禁止事项】","type":"posts"},{"content":"","date":"2024-10-14","externalUrl":null,"permalink":"/tags/rpo/","section":"标签","summary":"","title":"RPO","type":"tags"},{"content":"","date":"2024-10-14","externalUrl":null,"permalink":"/tags/rto/","section":"标签","summary":"","title":"RTO","type":"tags"},{"content":"在现代企业环境中，保障业务连续性至关重要。为了量化对灾难的恢复期望，业务连续性计划中通常使用两个关键指标：RTO（Recovery Time Objective）和 RPO（Recovery Point Objective）。本文将详细解析这两个指标，了解它们对企业的重要性以及在容灾计划中如何合理设定。\nRTO：恢复时间目标 # RTO，全称为恢复时间目标，是指业务系统停止服务的最长可容忍时间，即从灾难发生到业务系统完全恢复服务所需的最短时间。简而言之，RTO标志着企业能够接受的最大停机时间。\n举个例子，假设一个企业的RTO为1小时。这意味着在发生灾难或故障的情况下，该企业可以容忍1小时的服务中断。尽管在这1小时内业务系统无法提供服务，但企业的正常运作不会受到影响。超过1小时，企业可能会面临无法弥补的损失。\nRPO：恢复点目标 # RPO，全称为恢复点目标，是指业务系统能够容忍的数据丢失量。具体来说，RPO标志着从灾难发生到最近一次数据备份的时间跨度。\n举例来说，如果业务系统每天零点进行数据备份，而某一天上午8点发生了故障，导致8小时的数据丢失。如果业务系统的RPO为24小时，则问题并不大；但如果RPO为4小时，那么业务可能会因为数据丢失而受到影响。\n共同点与不同点 # 尽管RTO和RPO都使用时间来度量，它们在目标和应用方面存在一些区别。RTO关注于应用或系统的可用性，强调业务系统的停机时间限制。虽然RTO包括了数据恢复的时间，但更加侧重于描述应用停机的时间。\n相反，RPO关注于数据的完整性，描述了业务系统能够容忍的最大数据丢失限制。业务系统的服务中断可能导致经济损失，但如果涉及到客户交易数据的丢失，则可能会带来无法弥补的灾难性后果。\n在制定企业容灾计划时，必须考虑RTO和RPO目标，但这两者的实现成本存在差异。维护较短的RTO目标可能比较短，因为它涉及整个业务基础架构，而不仅仅是数据。相比之下，实现RPO目标则相对简单，只需要按照正确的时间间隔执行数据备份，这一过程可以轻松自动化。\n需要注意的是，完全自动化的RTO策略实现相对复杂，因为RTO涉及到恢复所有IT操作。因此，在实际应用中，需要权衡RTO和RPO目标，以达到经济效益的最大化。\n总体而言，RTO和RPO是业务连续性计划中的关键指标，它们帮助企业在灾难发生时快速、可靠地恢复服务，最大程度减少数据丢失。在设计容灾计划时，理解这两个指标的差异和共同点，将有助于建立更强大、可靠的业务连续性战略。\n","date":"2024-10-14","externalUrl":null,"permalink":"/archives/rpo-rto.html","section":"文章","summary":"","title":"什么是RPO和RTO？ 业务连续性关键指标解析","type":"posts"},{"content":"","date":"2024-10-13","externalUrl":null,"permalink":"/tags/alist/","section":"标签","summary":"","title":"Alist","type":"tags"},{"content":"之前不是在博客写了一篇文章嘛，写了一个关于Alist动态验证码的功能，访问Alist必须进行验证后才能进行访问，然后这个可能对小白不太友好，这里我写篇详尽文章教大家如何使用AList嵌入动态验证码\n准备工作 # 一台国内服务器 已备案域名 微信订阅号一个 环境准备 # 搭建一个Web服务器环境是创建和维护网站的基础步骤。对于有经验的开发者来说，这个过程可能已经非常熟悉了。但对于那些刚开始接触建站的朋友，建议您先去看一下如何建站\n安装Node.js # Node.js是一个开源、跨平台的JavaScript运行时环境，它允许开发者在服务器端运行JavaScript代码，本程序后端就是基于Node.js编写的，如果您不了解Node.js 那您更应该仔细看这部分内容了\n以我的 Debian系 主机环境为例\n1，下载安装Node.js (18+版本均可) # 下载地址：https://nodejs.cn/download/\n或者直接执行如下命令\nwget https://npmmirror.com/mirrors/node/v20.18.0/node-v20.18.0-linux-x64.tar.xz 下载完成后将 node-v20.18.0-linux-x64.tar.xz 解压到当前文件夹，会得到 node-v20.18.0-linux-x64\n将node-v20.18.0-linux-x64文件夹移动到/usr/local/node 目录中，并重命名为node\n2，配置 Node.js 环境 # 编辑/etc/profile 文件\n最底部添加如下内容\nexport NODE_HOME=/usr/local/node export PATH=$NODE_HOME/bin:$PATH 保存之后执行 source /etc/profile 使其生效。\n3，配置镜像加速 # 由于某些原因， npmjs.com 在国内的访问速度很不稳定。 所以我们可以考虑用国内的镜像网站地址替换，执行如下命令，配置为国内镜像源\nnpm config set registry https://registry.npmmirror.com 4，克隆仓库 # 我将这个项目的代码上传到了GitHub，可直接从github获取\n地址：Alist_Dynamic_verification: Alist AList嵌入动态验证码工具 (github.com)\n5，安装依赖 # cd Alist_Dynamic_verification npm install 6，配置微信订阅号相关信息 # 编辑 .env 配置相关信息(后面配置也可以)\nWECHAT_APPID= WECHAT_SECRET= WECHAT_TOKEN= ENCODING_AES_KEY= PORT=4000 # 后端运行端口，对接微信 VERIFY_PORT=4001 # 验证接口的端口，对接前端验证验证码用的 # 端口可以不定义 默认就是上面那俩 7，启动 # node run serve 8，配置反向代理 # 因为微信公众号强制要求使用HTTP/HTTPS端口，这里以Nginx为例，配置反向代理\n我这里用了两个域名,例如；\ntest.example.com（用于对接微信公众平台） verify.example.com（用于验证验证码的） nginx配置文件如下\ntest.example.com\nserver { listen 80; server_name test.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name test.example.com; # SSL 配置 ssl_certificate /etc/ssl/certs/certificate.crt; ssl_certificate_key /etc/ssl/private/private_key.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://localhost:4000; proxy_set_header Host $host; 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; } } verify.example.com\nserver { listen 80; server_name verify.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name verify.example.com; # SSL 配置 ssl_certificate /etc/ssl/certs/certificate.crt; ssl_certificate_key /etc/ssl/private/private_key.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://localhost:4001; proxy_set_header Host $host; 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; } } 二合一配置\nserver { listen 80; server_name test.example.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name test.example.com; ssl_certificate /etc/ssl/certs/certificate.crt; ssl_certificate_key /etc/ssl/private/private_key.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://localhost:4000/; proxy_set_header Host $host; 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; } location /verify/ { # 将 /verify/ 路径的请求转发到 4001 端口 proxy_pass http://localhost:4001/; proxy_set_header Host $host; 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; } } 9、公众号配置 # 如上图所示，服务器地址(URL) 配置为 http://你的域名.com/eventCall/，并配置好其他参数即可\n10，开机自动运行（systemd) # 在/etc/systemd/system/目录创建一个service文件， 定义如下内容\n[Unit] Description=Alist_Dynamic_verification After=network.target [Service] ExecStart=/usr/local/node/bin/npm run serve # npm全路径，按照你的安装位置修改 WorkingDirectory= # 你的项目目录 # Environment=NODE_ENV=production Restart=always [Install] WantedBy=multi-user.target 完了 daemon-reload一下 enable --now 运行服务就可以了\nAlist嵌入代码 # \u0026lt;script disable-devtool-auto src=\u0026#39;https://cdn.jsdelivr.net/npm/disable-devtool\u0026#39;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/sweetalert/dist/sweetalert.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.addEventListener(\u0026#39;keydown\u0026#39;, function(event) { if (event.key === \u0026#39;F12\u0026#39; || (event.ctrlKey \u0026amp;\u0026amp; event.shiftKey \u0026amp;\u0026amp; [\u0026#39;I\u0026#39;, \u0026#39;J\u0026#39;, \u0026#39;C\u0026#39;].includes(event.key.toUpperCase()))) { event.preventDefault(); } }); document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, function() { promptPassword(\u0026#34;success\u0026#34;, \u0026#34;请输入验证码以继续访问\u0026#34;); }); // 验证验证码函数 function validateCaptcha(captcha) { const xhr = new XMLHttpRequest(); xhr.open(\u0026#34;POST\u0026#34;, \u0026#34;验证接口, true); xhr.setRequestHeader(\u0026#34;Content-Type\u0026#34;, \u0026#34;application/json\u0026#34;); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { try { const response = JSON.parse(xhr.responseText); if (xhr.status === 200 \u0026amp;\u0026amp; response.code === 200) { welcomeUser(); } else { swal(\u0026#34;错误\u0026#34;, response.msg || \u0026#34;验证码错误，请重试\u0026#34;, \u0026#34;error\u0026#34;).then(() =\u0026gt; { promptPassword(\u0026#34;error\u0026#34;, \u0026#34;请重新输入验证码\u0026#34;); }); } } catch (e) { console.error(\u0026#34;JSON 解析错误:\u0026#34;, e); swal(\u0026#34;错误\u0026#34;, \u0026#34;响应格式不正确，请稍后重试\u0026#34;, \u0026#34;error\u0026#34;).then(() =\u0026gt; { promptPassword(\u0026#34;error\u0026#34;, \u0026#34;请重新输入验证码\u0026#34;); }); } } }; xhr.send(JSON.stringify({ code: captcha })); } // 提示输入验证码的函数 function promptPassword(icon, title) { swal({ title: title, text: \u0026#34;请确认您已获取正确的验证码。请点击下方按钮关注我们的微信公众号以获取验证码。\u0026#34;, closeOnClickOutside: false, icon: icon, buttons: { confirm: { text: \u0026#34;确认提交\u0026#34;, value: \u0026#34;confirm\u0026#34;, className: \u0026#34;custom-swal-button swal-button--confirm\u0026#34; }, getCode: { text: \u0026#34;微信公众号\u0026#34;, value: \u0026#34;get_code\u0026#34;, className: \u0026#34;custom-swal-button swal-button--copy\u0026#34; } }, content: { element: \u0026#34;input\u0026#34;, attributes: { placeholder: \u0026#34;请输入验证码\u0026#34;, type: \u0026#34;text\u0026#34;, style: \u0026#34;width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px;\u0026#34; } } }) .then((value) =\u0026gt; { if (value === \u0026#39;\u0026#39;) { promptPassword(\u0026#34;warning\u0026#34;, \u0026#34;请输入正确的验证码\u0026#34;); } else if (value === \u0026#34;get_code\u0026#34;) { showWeChatCode(); } else if (!/^\\d{6}$/.test(value)) { promptPassword(\u0026#34;warning\u0026#34;, \u0026#34;验证码格式不正确，请输入6位数字\u0026#34;); } else { validateCaptcha(value); } }); } // 显示微信公众号二维码 function showWeChatCode() { swal({ title: \u0026#34;关注微信公众号获取验证码\u0026#34;, text: \u0026#34;请扫描以下二维码关注我们的微信公众号以获取验证码。\u0026#34;, icon: \u0026#34;info\u0026#34;, closeOnClickOutside: false, content: { element: \u0026#34;img\u0026#34;, attributes: { src: \u0026#34;公众号图片\u0026#34;, style: \u0026#34;width: 100%; height: auto; border-radius: 4px;\u0026#34; } }, buttons: { confirm: { text: \u0026#34;返回输入\u0026#34;, value: \u0026#34;confirm\u0026#34;, className: \u0026#34;custom-swal-button swal-button--confirm\u0026#34; } } }).then(() =\u0026gt; { promptPassword(\u0026#34;info\u0026#34;, \u0026#34;请输入验证码以继续访问\u0026#34;); }); } // 欢迎用户函数 function welcomeUser() { swal(\u0026#34;欢迎！\u0026#34;, { icon: \u0026#34;success\u0026#34;, buttons: false, timer: 1000, //1秒后自动消失 }); } // 防止查看页面源代码 window.onbeforeunload = function() { return \u0026#34;您确定要离开此页面吗？\u0026#34;; }; // 检测并阻止右键菜单 window.addEventListener(\u0026#39;contextmenu\u0026#39;, function(event) { event.preventDefault(); }, false); // 检测并阻止选中文本 window.addEventListener(\u0026#39;selectstart\u0026#39;, function(event) { event.preventDefault(); }, false); \u0026lt;/script\u0026gt; ","date":"2024-10-13","externalUrl":null,"permalink":"/archives/alist-auth-all.html","section":"文章","summary":"","title":"小白教程 AList嵌入动态验证码实现引流","type":"posts"},{"content":"","date":"2024-10-11","externalUrl":null,"permalink":"/tags/ci/cd/","section":"标签","summary":"","title":"CI/CD","type":"tags"},{"content":"","date":"2024-10-11","externalUrl":null,"permalink":"/tags/gitlab/","section":"标签","summary":"","title":"Gitlab","type":"tags"},{"content":"GitLab作为一个领先的、完整的DevOps平台，为团队提供了一个集成的解决方案，使得软件开发和交付变得更加高效、可靠。本文将介绍如何通过安装Docker和GitLab EE来搭建一个功能完善的代码托管平台，然后通过Ruby 获得企业授权License。\nRuby生成的 GitLabBV.gitlab-license license_key.pub 文件可以直接使用本站所提供的，可以直接省略掉通过Ruby创建证书License的步骤，直接替换即可，下载地址请看附件。\n安装 Docker # sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo sed -i \u0026#39;s+download.docker.com+mirrors.aliyun.com/docker-ce+\u0026#39; /etc/yum.repos.d/docker-ce.repo sudo yum -y install docker-ce systemctl enable --now docker 配置 Docker 镜像加速 # { \u0026#34;registry-mirrors\u0026#34;: [\u0026#34;https://vwlrpbcp.mirror.aliyuncs.com\u0026#34;] } 安装 GitLab EE # docker run -itd \\ -p 80:80 \\ -p 443:443 \\ -p 2222:22 \\ -v /data/gitlab/log:/var/log/gitlab \\ -v /data/gitlab/opt:/var/opt/gitlab \\ -v /data/gitlab/etc:/etc/gitlab \\ --restart always \\ --privileged=true \\ --name gitlab \\ gitlab/gitlab-ee 确认 GitLab EE 已经正常运行\ndocker ps -a 运行 Ruby # docker run -it --rm ruby /bin/bash 安装许可证工具 # 创建license.rb文件 # cat \u0026gt;license.rb\u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; require \u0026#34;openssl\u0026#34; require \u0026#34;gitlab/license\u0026#34; key_pair = OpenSSL::PKey::RSA.generate(2048) File.open(\u0026#34;license_key\u0026#34;, \u0026#34;w\u0026#34;) { |f| f.write(key_pair.to_pem) } public_key = key_pair.public_key File.open(\u0026#34;license_key.pub\u0026#34;, \u0026#34;w\u0026#34;) { |f| f.write(public_key.to_pem) } private_key = OpenSSL::PKey::RSA.new File.read(\u0026#34;license_key\u0026#34;) Gitlab::License.encryption_key = private_key license = Gitlab::License.new license.licensee = { \u0026#34;Name\u0026#34; =\u0026gt; \u0026#34;gitlab\u0026#34;, \u0026#34;Company\u0026#34; =\u0026gt; \u0026#34;gitlab\u0026#34;, \u0026#34;Email\u0026#34; =\u0026gt; \u0026#34;admin@gitlab.com\u0026#34;, } license.starts_at = Date.new(2024, 1, 1) license.expires_at = Date.new(2099, 1, 1) license.notify_admins_at = Date.new(2049, 12, 1) license.notify_users_at = Date.new(2049, 12, 1) license.block_changes_at = Date.new(2050, 1, 1) license.restrictions = { active_user_count: 10000, } puts \u0026#34;License:\u0026#34; puts license data = license.export puts \u0026#34;Exported license:\u0026#34; puts data File.open(\u0026#34;GitLabBV.gitlab-license\u0026#34;, \u0026#34;w\u0026#34;) { |f| f.write(data) } public_key = OpenSSL::PKey::RSA.new File.read(\u0026#34;license_key.pub\u0026#34;) Gitlab::License.encryption_key = public_key data = File.read(\u0026#34;GitLabBV.gitlab-license\u0026#34;) $license = Gitlab::License.import(data) puts \u0026#34;Imported license:\u0026#34; puts $license unless $license raise \u0026#34;The license is invalid.\u0026#34; end if $license.restricted?(:active_user_count) active_user_count = 10000 if active_user_count \u0026gt; $license.restrictions[:active_user_count] raise \u0026#34;The active user count exceeds the allowed amount!\u0026#34; end end if $license.notify_admins? puts \u0026#34;The license is due to expire on #{$license.expires_at}.\u0026#34; end if $license.notify_users? puts \u0026#34;The license is due to expire on #{$license.expires_at}.\u0026#34; end module Gitlab class GitAccess def check(cmd, changes = nil) if $license.block_changes? return build_status_object(false, \u0026#34;License expired\u0026#34;) end end end end puts \u0026#34;This instance of GitLab Enterprise Edition is licensed to:\u0026#34; $license.licensee.each do |key, value| puts \u0026#34;#{key}: #{value}\u0026#34; end if $license.expired? puts \u0026#34;The license expired on #{$license.expires_at}\u0026#34; elsif $license.will_expire? puts \u0026#34;The license will expire on #{$license.expires_at}\u0026#34; else puts \u0026#34;The license will never expire.\u0026#34; end EOF 生成证书 # root@8f56c7c5621c:/# ruby license.rb License: #\u0026lt;Gitlab::License:0x00007fad2d4501c8\u0026gt; Exported license: eyJkYXRhIjoidnZoUTYwZCtHWFVpWmVTWE5ibytpR2dwV04yUG8vYTlIeXd4 MWJzNUxLZzhiWUs2SjlPcjROWmZ3QXRTXG5mWm1ENHA5NXo1QkJsazZGT2Zq TTVNWDd1TnVFeHFIWVpVeXRNbkhmQzlZWmdScEJTQU9vK0NJbHdQVXhcbnQ4 cVdmQ3dBbUVDaitYOWFhbHh2bkp6SkNSK0hJTWdIL2N5SDVCbXUvRTVFUUF2 bXVJa3hlNzNlTy9MMVxuYVRNVnFIKzc4WjRidjljWGJrbmt4ZWE5S1Y0amI1 amtrRE9GbDhhZ09aWTNJVXczWXVMeDUyMW9kMS8wXG5xZitqb0w2Z21aQS9P MnJzNDhvanVYWndVeUF3WWFLU1ZSU2tLK0V1SVZCWGttQStlNzF5YWJBalpS eDJcbkVSdThyOVZUQUoyMk1uWndpakhIRWFUWENwRUxEbVArZkdzUkJURDcy MnNkYktYQmRpekt4dncwZmhMd1xuYmtXdHZkQlRpZzVpbVRjT2hLR2VkOWtM SmtzNFZJZ3N3NjNzdFhIK1N1Z0EyVEhFOG1EU0RlK3VBa3ZRXG5BTEVlRHFD Z3hlbUdhNHRHY2ZEVlNqR0VjQWx1V01KTDYyelh6RWtHbDM1T0gweWpTWkp2 cEFoY2RIdlJcbndsdFpTVURpWVZqM0lWdHNLODA5dS9uVTkxcEJDbFdQdmlE WEhhVkZraEQ4NU1zWlZsc1Vycld3NUthNlxuTXZ0L1BmUzNmbmxZWkVnbzh1 TTRHS05rdGxLSkRQV0xtU254MW5zSDJueTVNUWZZQmQzdXZHZW52dUc3XG5L SlJkSDREeGVhK1k3a2Z3dlA0NVhSRTB6NC85dkRWVFBBOGFnSCtYVGxkWm9a dWNMNU1yeDR0anQ0ZHJcbm5lSFpEU2RDQVRUQUp6NUw4NC9UbVl0TG1rSjNF VDlVd1R5ODB1OWM0OU1sXG4iLCJrZXkiOiJWeWNoWUR3YUo5WjhxWWtEbitJ akJaV2ZKbTlpdGZ0aUE2R1B4a3p1WG95Q2F3c3F4K0NCSVI5VHhRb21cbk8y czQrN3R1K05JcGkvbEVub3JXSHVQaXh5Z0tJNFdkSTdwbGV0U21KZnFpbVJ5 QXYxcVBqUDZBcjVuZ1xuNEtvTG1OdDlsUU1SVjN0d3BFd3lXeHlkQlUwRVB1 MVAyZTQxcGZlRGZBbTNtc0dDQjRXTXZIcFZwTUdtXG5BREVHWEZkK0R4Ni9x NzY3TXlqY1NFRkd3R1Z6UkxrSjlMb0JUSFZjcFdGaE9vb3VrSElPSHYvaDd5 cjlcbmovRUl6WVdOY3AzSWgzU3RXT253VTR5MUdxZlVyOVRSS2hFYjBseGp4 azR5VHNpZTNPWGl3a2JwcStZN1xuTnU2YkZsZWJua1htYUNFc0VuZ0Q4S3c4 dkdhcHFzTjZYNmZ0S2dCa0FnPT1cbiIsIml2IjoiYUxiazVWa21tRjFad0J1 eWd6eEQwQT09XG4ifQ== Imported license: #\u0026lt;Gitlab::License:0x00007fad2d3dd6f0\u0026gt; This instance of GitLab Enterprise Edition is licensed to: Name: gitlab Company: gitlab Email: admin@gitlab.com The license will expire on 2099-01-01 然后会在当前目录下生成 GitLabBV.gitlab-license license_key license_key.pub三个文件。\n将生成的 license_key.pub 文件复制到到 gitlab 容器中的/opt/gitlab/embedded/service/gitlab-rails/.license_encryption_key.pub\ndocker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8f56c7c5621c ruby \u0026#34;/bin/bash\u0026#34; 10 minutes ago Up 10 minutes busy_bell 2042068f1d19 gitlab/gitlab-ee \u0026#34;/assets/wrapper\u0026#34; 11 minutes ago Up 11 minutes (healthy) 0.0.0.0:80-\u0026gt;80/tcp, :::80-\u0026gt;80/tcp, 0.0.0.0:443-\u0026gt;443/tcp, :::443-\u0026gt;443/tcp, 0.0.0.0:2222-\u0026gt;22/tcp, :::2222-\u0026gt;22/tcp gitlab [root@gitlab ~]# [root@gitlab ~]# docker cp 8f56c7c5621c:/license_key.pub . Successfully copied 2.05kB to /root/. [root@gitlab ~]# ll total 8 -rw-------. 1 root root 1218 Feb 28 17:32 anaconda-ks.cfg -rw-r--r-- 1 root root 451 Mar 4 00:14 license_key.pub [root@gitlab ~]# [root@gitlab ~]# docker cp license_key.pub 2042068f1d19:/opt/gitlab/embedded/service/gitlab-rails/.license_encryption_key.pub Successfully copied 2.05kB to 2042068f1d19:/opt/gitlab/embedded/service/gitlab-rails/.license_encryption_key.pub 获取 GitLab 初始密码并订阅许可证 # 默认用户名 为 root\nsudo docker exec -it gitlab grep \u0026#39;Password:\u0026#39; /etc/gitlab/initial_root_password Password: M4PTVPTAGib9V2e3W/7HZXN/m7ia6T1M1Mfbh8DOp7s= 然后打开打开http://gitlab/admin/application_settings/general\n如果上传 license 时提示该 license 不可用，可以重启一遍 gitlab 容器，或者替换完成license_encryption_key.pub 先重启 gitlab 容器再去 web 添加 license 也可以。\n蓝奏云下载:https://wanghaoyu.lanzoue.com/inAe21q7zoqf\n","date":"2024-10-11","externalUrl":null,"permalink":"/archives/gitlabee-license.html","section":"文章","summary":"","title":"GitLab EE 企业版破解","type":"posts"},{"content":"在当今的网络安全环境中，Web应用程序面临着各种威胁，包括SQL注入、跨站脚本攻击（XSS）和分布式拒绝服务（DDoS）攻击。为了保护Web应用程序免受这些威胁，可以使用Web应用程序防火墙（WAF）。ModSecurity是一个开源的WAF，它可以嵌入到Web服务器中，提供实时的请求监控、过滤和学习功能。\nModSecurity模块简介 # ModSecurity是一个功能强大的Web应用程序防火墙，它使用一组预定义的安全规则来保护Web应用程序。它支持多种Web服务器，包括Apache和Nginx。ModSecurity的核心规则集（CRS）是由OWASP（开放式Web应用安全项目）提供的，这是一个是致力于Web 应用程序安全的国际非营利组织，它包含了数千条用于防御常见Web攻击的规则。\nhttps://www.cloudflare-cn.com/learning/security/threats/owasp-top-10/\nhttps://github.com/owasp-modsecurity/ModSecurity\n在 RHEL 系列操作系统安装 ModSecurity # 在 RHEL 操作系统中安装 ModSecurity 模块相对简单，以下是安装步骤\n1、启用 EPEL 仓库\ndnf install epel-release 2、安装 ModSecurity\n安装 ModSecurity 及其核心规则集\ndnf install mod_security mod_security_crs 3、启用 Apache 服务（如果在安装ModSecurity之前已经启动了Apache服务，请重启以应用更改）\nsystemctl start httpd # 启动服务 systemctl restart httpd # 重启服务 4、检查模块\n检查模块是否正确加载\nhttpd -M | grep security 测试 ModSecurity 配置 # 测试是比配置任何设置都重要的部分，可以通过手动添加一个简单的规则进行测试\n1、创建或编辑一个规则文件，例如/etc/httpd/modsecurity.d/local_rules.conf，并添加以下内容\nSecRuleEngine On SecRule ARGS:args \u0026#34;@contains test\u0026#34; \u0026#34;id:123456,deny,status:403,msg:\u0026#39;Test Ruleset\u0026#39;\u0026#34; 一定要确保 Apache 配置中启用了 ModSecurity 模块，你一般可以在 /etc/httpd/conf.modules.d/10-mod_security.conf找到启用模块的指令\n2、保存你的配置文件并重启Apache服务以应用更改\nsystemctl restart httpd 3、在浏览器或使用 curl 命令行工具测试你的规则，例如\ncurl -l https://www.wanghaoyu.com.cn/?args=test 效果如下图所示，可以看到直接返回了 403，证明规则配置正确，已被触发\n查看日志\ntail -f /var/log/httpd/modsec_audit.log 如下图所示\n如果测试失败，检查你的规则文件是否正确配置，并确保SecRuleEngine设置为On。此外，检查是否有其他规则可能与你的测试规则冲突\n禁用 ModSecurity 或列入白名单 # 在某些情况下，你可能需要禁用特定虚拟主机的ModSecurity\n需要在虚拟主机部分中添加以下内容\n\u0026lt;IfModule security2_module\u0026gt; SecRuleEngine Off \u0026lt;/IfModule\u0026gt; 扩展内容 # 将特定目录或URL的 ModSecurity 列入白名单 # \u0026lt;Directory \u0026#34;/var/www/html/wp-admin\u0026#34;\u0026gt; \u0026lt;IfModule security2_module\u0026gt; SecRuleEngine Off \u0026lt;/IfModule\u0026gt; \u0026lt;/Directory\u0026gt; 禁用特定的 ModSecurity 的规则 ID # 这里简单说一下，例如规则 ID 666 触发误报，被拦截，用户可以在配置中利用规则 ID 禁用特定规则\n\u0026lt;LocationMatch \u0026#34;/wp-admin/update.php\u0026#34;\u0026gt; \u0026lt;IfModule security2_module\u0026gt; SecRuleRemoveById 666 \u0026lt;/IfModule\u0026gt; \u0026lt;/LocationMatch\u0026gt; 这篇文章只是一个基本快速部署使用指南，在使用之前一定要制定好相关的规则防止误报保持最佳功能,\nEnjoy it ~\n","date":"2024-10-06","externalUrl":null,"permalink":"/archives/lamp-modsecurity-setup.html","section":"文章","summary":"","title":"LAMP环境配置 ModSecurity Web 应用防火墙模块","type":"posts"},{"content":"","date":"2024-10-06","externalUrl":null,"permalink":"/tags/modsecurity/","section":"标签","summary":"","title":"ModSecurity","type":"tags"},{"content":"最近利用ALists创建了个网盘资源站，想着如何增加个动态验证码进行验证后才能进行访问下载，动态获取验证码，使用了禁止打开控制台校验等方式减少绕过几率，现在分享给大家。\nNodeJS后端代码 # 利用json存储验证码数据，并且用户无法重复获取验证码\nrequire(\u0026#39;dotenv\u0026#39;).config(); const express = require(\u0026#39;express\u0026#39;); const wechat = require(\u0026#39;wechat\u0026#39;); const fs = require(\u0026#39;fs\u0026#39;); const axios = require(\u0026#39;axios\u0026#39;); const cors = require(\u0026#39;cors\u0026#39;); const app = express(); const dbPath = \u0026#39;./codes.json\u0026#39;; // CORS 中间件 app.use(cors({ origin: function (origin, callback) { if (!origin || origin.endsWith(\u0026#39;*\u0026#39;)) { callback(null, true); } else { callback(new Error(\u0026#39;Not allowed by CORS\u0026#39;)); } } })); app.use(express.json()); // 读取 JSON 数据 function readData() { if (!fs.existsSync(dbPath)) { fs.writeFileSync(dbPath, JSON.stringify({})); } return JSON.parse(fs.readFileSync(dbPath)); } // 保存 JSON 数据 function saveData(data) { fs.writeFileSync(dbPath, JSON.stringify(data)); } // 处理微信请求 app.use(\u0026#39;/\u0026#39;, wechat({ token: process.env.WECHAT_TOKEN, appid: process.env.WECHAT_APPID, encodingAESKey: process.env.ENCODING_AES_KEY, checkSignature: false }, async (req, res, next) =\u0026gt; { const { FromUserName, Content } = req.weixin; // 处理“验证码”请求 if (Content.trim() === \u0026#39;验证码\u0026#39;) { const userKey = FromUserName; const data = readData(); // 检查是否已经生成过验证码 const userData = data[userKey]; const today = new Date().toISOString().split(\u0026#39;T\u0026#39;)[0]; if (userData \u0026amp;\u0026amp; userData.date === today) { return res.reply(`今天已经获取过验证码了, 您的验证码是: ${userData.code}`); } // 生成新的验证码 const newCode = Math.floor(100000 + Math.random() * 900000).toString(); // 生成6位随机数字 const timestamp = Date.now(); // 插入或更新验证码 data[userKey] = { code: newCode, date: today, timestamp: timestamp }; saveData(data); return res.reply(`您的验证码是: ${newCode}（12小时内有效）`); } res.reply(\u0026#39;未知指令，请回复“验证码”获取验证码\u0026#39;); })); // 验证验证码的接口 app.post(\u0026#39;/verify\u0026#39;, (req, res) =\u0026gt; { const { code } = req.body; const data = readData(); const isValid = Object.values(data).some(userData =\u0026gt; userData.code === code); if (isValid) { return res.json({ success: true, message: \u0026#39;验证码有效\u0026#39; }); } else { return res.json({ success: false, message: \u0026#39;验证码无效或已过期\u0026#39; }); } }); // 启动服务器 const PORT = process.env.PORT || 3000; app.listen(PORT, () =\u0026gt; { console.log(`Server listening at http://localhost:${PORT}`); }); 前端代码 # \u0026lt;script disable-devtool-auto src=\u0026#39;https://cdn.jsdelivr.net/npm/disable-devtool\u0026#39;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://unpkg.com/sweetalert/dist/sweetalert.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.addEventListener(\u0026#39;keydown\u0026#39;, function(event) { if (event.key === \u0026#39;F12\u0026#39; || (event.ctrlKey \u0026amp;\u0026amp; event.shiftKey \u0026amp;\u0026amp; [\u0026#39;I\u0026#39;, \u0026#39;J\u0026#39;, \u0026#39;C\u0026#39;].includes(event.key.toUpperCase()))) { event.preventDefault(); } }); document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, function() { promptPassword(\u0026#34;success\u0026#34;, \u0026#34;请输入验证码以继续访问\u0026#34;); }); // 验证验证码函数 function validateCaptcha(captcha) { const xhr = new XMLHttpRequest(); xhr.open(\u0026#34;POST\u0026#34;, \u0026#34;验证接口, true); xhr.setRequestHeader(\u0026#34;Content-Type\u0026#34;, \u0026#34;application/json\u0026#34;); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { try { const response = JSON.parse(xhr.responseText); if (xhr.status === 200 \u0026amp;\u0026amp; response.code === 200) { welcomeUser(); } else { swal(\u0026#34;错误\u0026#34;, response.msg || \u0026#34;验证码错误，请重试\u0026#34;, \u0026#34;error\u0026#34;).then(() =\u0026gt; { promptPassword(\u0026#34;error\u0026#34;, \u0026#34;请重新输入验证码\u0026#34;); }); } } catch (e) { console.error(\u0026#34;JSON 解析错误:\u0026#34;, e); swal(\u0026#34;错误\u0026#34;, \u0026#34;响应格式不正确，请稍后重试\u0026#34;, \u0026#34;error\u0026#34;).then(() =\u0026gt; { promptPassword(\u0026#34;error\u0026#34;, \u0026#34;请重新输入验证码\u0026#34;); }); } } }; xhr.send(JSON.stringify({ code: captcha })); } // 提示输入验证码的函数 function promptPassword(icon, title) { swal({ title: title, text: \u0026#34;请确认您已获取正确的验证码。请点击下方按钮关注我们的微信公众号以获取验证码。\u0026#34;, closeOnClickOutside: false, icon: icon, buttons: { confirm: { text: \u0026#34;确认提交\u0026#34;, value: \u0026#34;confirm\u0026#34;, className: \u0026#34;custom-swal-button swal-button--confirm\u0026#34; }, getCode: { text: \u0026#34;微信公众号\u0026#34;, value: \u0026#34;get_code\u0026#34;, className: \u0026#34;custom-swal-button swal-button--copy\u0026#34; } }, content: { element: \u0026#34;input\u0026#34;, attributes: { placeholder: \u0026#34;请输入验证码\u0026#34;, type: \u0026#34;text\u0026#34;, style: \u0026#34;width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px;\u0026#34; } } }) .then((value) =\u0026gt; { if (value === \u0026#39;\u0026#39;) { promptPassword(\u0026#34;warning\u0026#34;, \u0026#34;请输入正确的验证码\u0026#34;); } else if (value === \u0026#34;get_code\u0026#34;) { showWeChatCode(); } else if (!/^\\d{6}$/.test(value)) { promptPassword(\u0026#34;warning\u0026#34;, \u0026#34;验证码格式不正确，请输入6位数字\u0026#34;); } else { validateCaptcha(value); } }); } // 显示微信公众号二维码 function showWeChatCode() { swal({ title: \u0026#34;关注微信公众号获取验证码\u0026#34;, text: \u0026#34;请扫描以下二维码关注我们的微信公众号以获取验证码。\u0026#34;, icon: \u0026#34;info\u0026#34;, closeOnClickOutside: false, content: { element: \u0026#34;img\u0026#34;, attributes: { src: \u0026#34;公众号图片\u0026#34;, style: \u0026#34;width: 100%; height: auto; border-radius: 4px;\u0026#34; } }, buttons: { confirm: { text: \u0026#34;返回输入\u0026#34;, value: \u0026#34;confirm\u0026#34;, className: \u0026#34;custom-swal-button swal-button--confirm\u0026#34; } } }).then(() =\u0026gt; { promptPassword(\u0026#34;info\u0026#34;, \u0026#34;请输入验证码以继续访问\u0026#34;); }); } // 欢迎用户函数 function welcomeUser() { swal(\u0026#34;欢迎！\u0026#34;, { icon: \u0026#34;success\u0026#34;, buttons: false, timer: 1000, //1秒后自动消失 }); } // 防止查看页面源代码 window.onbeforeunload = function() { return \u0026#34;您确定要离开此页面吗？\u0026#34;; }; // 检测并阻止右键菜单 window.addEventListener(\u0026#39;contextmenu\u0026#39;, function(event) { event.preventDefault(); }, false); // 检测并阻止选中文本 window.addEventListener(\u0026#39;selectstart\u0026#39;, function(event) { event.preventDefault(); }, false); \u0026lt;/script\u0026gt; 效果图 # ","date":"2024-10-05","externalUrl":null,"permalink":"/archives/alist-auth.html","section":"文章","summary":"","title":"AList嵌入动态验证码实现引流","type":"posts"},{"content":"","date":"2024-10-01","externalUrl":null,"permalink":"/tags/%E5%85%89%E7%8C%AB/","section":"标签","summary":"","title":"光猫","type":"tags"},{"content":"","date":"2024-10-01","externalUrl":null,"permalink":"/tags/%E5%B1%B1%E4%B8%9C%E8%81%94%E9%80%9A/","section":"标签","summary":"","title":"山东联通","type":"tags"},{"content":"理论SK-D740系列都可以搞，其他型号可以根据情况查看,之前网上的教程到2，0版本的光猫都不行了，无法获取密码，大家可以试一下我这个方法，亲测可用！ telnet开启链接： 192.168.1.1/hidden_version_switch.html telnet用户名：root telent密码： CUAdmin\n教程开始 抄写光猫原有的设备信息 使用 user 用户登录到您的光猫，将原有的认证信息，INTERNET/IPTV VLANID LOID等信息抄写下备用\n重置光猫 按住光猫屁股的 reset 按钮 10s 左右即可重置\n光猫配置 1、光猫开机之后使用 192.168.1.1/hidden_version_switch.html 开启telnet功能并登录，切记，不要退出telnet，保持登录状态！！！\n2、然后正常使用 LOID 进行认证注册，注册完成之后telnet有一段时间不会断开，使用命令重新配置超级管理员密码即可！\n命令如下：\nsendcmd 1 DB set DevAuthInfo 0 User Username #username是你要设置的超级用户名 sendcmd 1 DB set DevAuthInfo 0 Pass Password # 这里password是设置超级用户密码 按回车是出来带星号的，其实到这里已经修改完了，去使用刚设置的信息登录即可，登录之后删除TR069\n","date":"2024-10-01","externalUrl":null,"permalink":"/archives/SK-D740.html","section":"文章","summary":"","title":"山东联通SK-D740-C 光猫获取超级密码教程","type":"posts"},{"content":" 关于我 # 我，02年破壳，山东人，性别男，爱好女，一条在互联网练习时长两年半的“技术咸鱼”。上网这么多年，我早就练就了一身“绝世武功”——精通Windows、Mac OS、Linux等操作系统设备的开机与关机大法，“一键启动，一键关机”的绝世高手，系统管理员见了都得给我竖个大拇指！ ::aru:thumb:: 我还会去自学一切感兴趣的东西，不管是前沿技术还是经典理论，只要感兴趣，我就能一头扎进去，研究得明明白明。别看我平时懒散得像条咸鱼，学起东西来，那可是比谁都认真，遇到难题绝不退缩，非得把它拿下不可！ 在编程世界里，我也是个“语言学家”，精通Python、JavaScript、TypeScript、Go、Java、PHP的朗读与拼写，当然，我可不只是停留在“朗读与拼写”的水平。我写代码的时候，那叫一个行云流水，bug都得给我让路！从简单的脚本到复杂的系统架构，我都能轻松驾驭，注释写得比代码还多，完全不用担心别人看不懂我的代码。\n我还略懂OpenStack、Kubernetes、Ceph的配置与优化，虽然谈不上精通，但至少能把它们搭建起来，调优一番，再拆解复盘……嗯，这大概就是传说中的“实战派高手”吧。毕竟，我可不是那种只会纸上谈兵的人，RHCA认证就是我实力的最好证明！在技术的战场上，我就是那个能扛起服务器、优化代码、解决疑难杂症的“硬核战士”！💪 爱用折腾去解决一切问题，是我一直以来的信条。在我眼里，没有什么问题是尝试不能解决的，如果有，那就多尝试几次！要是实在解决不了，那就……哼哼，rm -rf /*，不过我可不会真的这么做，毕竟，我可不想让自己的电脑变成“电子废墟”呢，这招只是留到最后的“杀手锏”，不到万不得已，我可不会轻易使出。\n总之，我就是这样一个咸鱼，虽然看起来懒散，但其实内心充满了对技术的热爱和对挑战的渴望。我坚信，只要折腾得够多，总有一天，我这条咸鱼也能翻身，成为互联网江湖里的“传奇技术大佬”！🐟\n联系我 # 如果你对我的工作感兴趣，或者有任何合作机会，可以通过以下方式联系我： 邮箱：admin@wanghaoyu.com.cn 邮箱（备用）：admin@wanghaoyu.me 社交媒体：ZhiHu | LinkedIn WeChat: Status # 图床\n本站历程 # 2026-01-89 出现意外错误，已恢复到2025年11月的备份 2025-04-25 我又回来了！重新拾起了Typecho，并选用了 Handsome主题 2025-02-09 Footer整体布局调整，添加友情链接，微信公众号二维码，备案信息样式调整 2025-02-04 配置使用 GitHub+Webhook 自动部署，国外分流到Vercel，国内使用两台阿里云主机进行DNS负载均衡。相关文档：Hexo Deploy 使用 GitHub WebHook 更新文章 2025-02-02 弃用Kubernetes 以及 Typecho 基于 Hexo 重新建站，使用 Butterfly 主题 2020-10-29 注册 wanghaoyu.com.cn ","date":"2024-09-30","externalUrl":null,"permalink":"/about.html","section":"Haoyu Wang's Blog","summary":"","title":"关于","type":"page"},{"content":"","date":"2024-09-30","externalUrl":null,"permalink":"/tags/golang/","section":"标签","summary":"","title":"Golang","type":"tags"},{"content":"官方地址: https://go.dev/ 镜像地址： https://golang.google.cn/dl/\n1，下载Golang # 下载tar包并解压到 /usr/local目录\nwget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz tar xf go1.23.1.linux-amd64.tar.gz -C /usr/local/ 2，配置环境变量 # 在/etc/profile 或者.bashrc中配置Go的环境变量\nexport GO111MODULE=on export GOROOT=/usr/local/go export GOPATH=/home/gopath export PATH=$PATH:$GOROOT/bin:$GOPATH/bin 最后使用 source 命令更新一下环境变量就可以了\n","date":"2024-09-30","externalUrl":null,"permalink":"/archives/linux-install-go.html","section":"文章","summary":"","title":"Linux 安装Go环境","type":"posts"},{"content":"","date":"2024-09-25","externalUrl":null,"permalink":"/tags/mysql/","section":"标签","summary":"","title":"Mysql","type":"tags"},{"content":"在日常的数据库管理中，定期备份是保障数据安全的重要手段。MySQL数据库备份可以通过多种方式实现，其中使用Shell脚本进行自动化备份是一种高效且灵活的方法。本文将介绍如何编写一个Shell脚本来实现MySQL数据库的自动备份，并对脚本进行优化。\n基本备份脚本 # 首先，我们来看一个基本的MySQL数据库备份脚本。这个脚本会将指定的数据库备份到指定目录，并按照日期进行分类存储。以下是基本脚本的代码：\n#!/bin/bash # 备份文件保存路径 backup_dir=/data/mysqlbackup # 当前时间 current_time=`date +%Y-%m-%d-%H-%M-%S` # 当前年份 year=`date +%Y` # 当前月份 month=`date +%m` # 当前日 day=`date +%d` # 备份工具 tool=mysqldump # mysql数据库用户名 username=root # mysql数据库密码 password=\u0026#39;123456\u0026#39; # 对将要备份的多个数据库用数组分开 database_names=(database1 database2 database3) # -d用来判断目录是否存在，存在返回真，不存在则 if [ ! -d $backup_dir/$year/$month/$day ] then #不存在则用mkdir命令来创建目录, -p选项确保指定的整个目录路径都会被创建，创建的目录为/data/mysqlbackup/年份/月份/日 mkdir -p $backup_dir/$year/$month/$day fi # 循环需要备份的数据库数组 for i in ${database_names[@]} do #--single-transaction这个选项保证了通过将导出操作封装在一个事务(隔离级别为REPEATABLE READ)内来使得导出的数据是一个一致性快照 #| gzip意思是利用管道命令连接gzip命令然后把sql文件直接压缩为gz压缩文件 $tool -u $username -p $password --single-transaction $i | gzip \u0026gt; $backup_dir/$year/$month/$day/$i-$current_time.sql.gz # 把备份情况写进日志 echo \u0026#34;成功创建备份：$backup_dir/$year/$month/$day/$i-$current_time.sql.gz\u0026#34; \u0026gt;\u0026gt; $backup_dir/mysqlbackup.log done 这个脚本的基本功能已经可以满足日常的备份需求，但在实际使用中，我们可以对其进行一些优化，以提高脚本的安全性、可读性和可维护性。\n优化后的备份脚本 # 为了使脚本更加完善，我们可以进行以下优化：\n变量引用：在引用变量时加上双引号，避免路径或文件名中包含空格等特殊字符导致的问题。 密码安全：建议不要在脚本中明文存储密码，可以考虑使用环境变量或配置文件来存储密码。 日志记录：增加备份失败的日志记录，以便于排查问题。 代码可读性：使用更具描述性的变量名，并在适当的位置添加注释以提高代码的可读性。 命令替换：使用 $(...) 代替反引号 `...` 进行命令替换，提高可读性和嵌套能力。 下面是优化后的脚本：\n#!/bin/bash ​ # 备份文件保存路径 backup_dir=\u0026#34;/data/mysqlbackup\u0026#34; # 当前时间 current_time=$(date +%Y-%m-%d-%H-%M-%S) # 当前年份 year=$(date +%Y) # 当前月份 month=$(date +%m) # 当前日 day=$(date +%d) # 备份工具 tool=\u0026#34;mysqldump\u0026#34; # MySQL数据库用户名 username=\u0026#34;root\u0026#34; # MySQL数据库密码（建议使用更安全的方式存储密码，如环境变量或配置文件） password=\u0026#34;123456\u0026#34; # 将要备份的多个数据库用数组分开 database_names=(\u0026#34;database1\u0026#34; \u0026#34;database2\u0026#34; \u0026#34;database3\u0026#34;) ​ # 创建备份目录，如果不存在则创建 backup_path=\u0026#34;$backup_dir/$year/$month/$day\u0026#34; if [ ! -d \u0026#34;$backup_path\u0026#34; ]; then mkdir -p \u0026#34;$backup_path\u0026#34; fi ​ # 循环需要备份的数据库数组 for db in \u0026#34;${database_names[@]}\u0026#34;; do # --single-transaction 选项保证了通过将导出操作封装在一个事务(隔离级别为 REPEATABLE READ)内来使得导出的数据是一个一致性快照 # | gzip 意思是利用管道命令连接 gzip 命令然后把 sql 文件直接压缩为 gz 压缩文件 $tool -u \u0026#34;$username\u0026#34; -p\u0026#34;$password\u0026#34; --single-transaction \u0026#34;$db\u0026#34; | gzip \u0026gt; \u0026#34;$backup_path/$db-$current_time.sql.gz\u0026#34; # 检查备份是否成功 if [ $? -eq 0 ]; then echo \u0026#34;成功创建备份：$backup_path/$db-$current_time.sql.gz\u0026#34; \u0026gt;\u0026gt; \u0026#34;$backup_dir/mysqlbackup.log\u0026#34; else echo \u0026#34;备份失败：$backup_path/$db-$current_time.sql.gz\u0026#34; \u0026gt;\u0026gt; \u0026#34;$backup_dir/mysqlbackup.log\u0026#34; fi done ","date":"2024-09-25","externalUrl":null,"permalink":"/archives/use-shell-script-backup-mysql-data.html","section":"文章","summary":"","title":"使用Shell脚本进行MySQL数据库备份","type":"posts"},{"content":"","date":"2024-09-21","externalUrl":null,"permalink":"/tags/github/","section":"标签","summary":"","title":"GitHub","type":"tags"},{"content":" 准备工作 # CloudFlare 账号 GitHub账号 一个域名 PicGO软件,下载地址在文末 搭建图床 # 创建 GitHub仓库 # 在GitHUB上创建一个仓库，专门用于存放图片的，怎么创建就不用多说了吧，不能使用私有仓库！！！\n获取 GitHub Token # 需要获取GitHub Token，后面会用到\n打开：https://github.com/settings/profile\n找到 Developer settings,然后找到 tokens (classic)：https://github.com/settings/tokens\n创建一个新的token\n勾选repo，将Expiration过期时间调整为永不过期\n然后记住你的token，接下来会用到\nPicgo配置 # 下载按照picgo，下载地址：https://molunerfinn.com/PicGo/\n安装完成后，点击图床设置，GitHub\n仓库名：这个就填写你的GitHub仓库名字/仓库。例如我的仓库名称是geeklinux,我创建了一个叫做picgo的仓库，所以我就填为 geeklinux/picgo\n分支名称：这里我们都是单分支，填写main即可。\nToken：这个就是你上一步在GitHub获取到的Token。\n存储路径，这个就是配置上传的图片放到你的仓库中的哪一个子目录里，为空默认放你仓库根目录\n**自定义域名：我们可以采用 jsdelivr.jsdelivr.net 来加速访问你的仓库\n配置方法：https://jsdelivr.jsdelivr.net/gh/github用户名/仓库名/\n配置代理加速 # 方法1：反向代理 # 我们知道，jsdelivr.jsdelivr.net 在国内的访问速度并不理想，我们可以使用香港等延迟比较低比较稳定的服务器通过反向代理来加速访问 jsdelivr.jsdelivr.net\n例如我这里使用的一台日本的服务器，使用nginx反向代理jsdelivr.jsdelivr.net 配置如下；\nserver { listen 80; server_name img.geeklinux.cn; # Redirect all HTTP requests to HTTPS location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name img.geeklinux.cn; ssl_certificate F:/cert/geeklinux.cn/ssl.pem; ssl_certificate_key F:/cert/geeklinux.cn/ssl.key; location / { proxy_pass https://jsdelivr.jsdelivr.net:443; proxy_ssl_server_name on; proxy_set_header Host jsdelivr.jsdelivr.net; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header Upgrade $http_upgrade; } } 访问测试\n方法2：使用 CloudFlare Worker 加速 # 此方法不需要额外服务器，只需要有一个域名且DNS服务器指向了CloudFlare。\n（CloudFlare Worker 会赠送一个测试域名，因为某些原因可能无法访问，这里推荐还是用自己的域名）\n登录CloudFlare账号，在侧边栏找到 Worker and Pages\n然后我们需要创建一个Worker\n定义好你的 Worker 名称 创建 Worker\n创建成功后我们编辑代码\n然后填写如下内容\naddEventListener(\u0026#39;fetch\u0026#39;, event =\u0026gt; { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { let url = new URL(request.url); if (url.pathname.startsWith(\u0026#39;/\u0026#39;)) { // proxy url and port url.hostname = \u0026#34;jsdelivr.jsdelivr.net\u0026#34;; url.port = \u0026#39;443\u0026#39;; url.protocol = \u0026#39;https\u0026#39;; //PROTOCOL let new_request = new Request(url, request); new_request.headers.set(\u0026#39;Host\u0026#39;, \u0026#39;jsdelivr.jsdelivr.net\u0026#39;); // set host header return fetch(new_request); } return fetch(request); } 最终效果如下，使用 Worker 反向代理 jsdelivr.jsdelivr.net\n使用自带的域名访问测试\nemmmm\u0026hellip;\u0026hellip; 还是用自己的域名吧，请接着往下看\n然后我们绑定自定义域名，找到你的Worker \u0026gt; 设置 \u0026gt; 触发器\n添加自定义域即可\n最终效果测试，最稳定的其实还是自己搞服务器反向代理（itdog频率限制了，这里用zhale.me，效果一样）\n最后在picgo里面把 jsdelivr.jsdelivr.net 替换为你的域名即可\n","date":"2024-09-21","externalUrl":null,"permalink":"/archives/github-photobed.html","section":"文章","summary":"","title":"PicGo + GitHub + c+ CloudFlare 搭建高速免费不限量图床","type":"posts"},{"content":"","date":"2024-09-21","externalUrl":null,"permalink":"/tags/%E5%9B%BE%E5%BA%8A/","section":"标签","summary":"","title":"图床","type":"tags"},{"content":"","date":"2024-09-18","externalUrl":null,"permalink":"/tags/terser/","section":"标签","summary":"","title":"Terser","type":"tags"},{"content":"在构建Web 应用时，Webpack 通常会使用 Terser 插件来优化和压缩 JavaScript 文件，当使用 Node.js 17 或更新版本时，构建过程中可能会出现如下错误：\njs/chunk-vendors.57e08fad.js from Terser Error: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:69:19) at Object.createHash (node:crypto:133:10) ... 此错误通常伴随着 Build failed with errors 消息，导致构建失败。\n错误原因 # 这个问题主要源于 Node.js 17+ 加密模块 crypto 发生了变化。这些版本默认使用 OpenSSL 3.x，而某些加密算法或配置在 OpenSSL 3.x 中已经不再受支持。这会导致 Terser 这样的工具在进行哈希操作时遇到问题，\n解决方案 # 解决这个问题有几种方式，具体取决于你的项目环境\n第一种 使用 \u0026ndash;openssl-legacy-provider # 在 Node.js 17 +的版本中 ，通过指定 \u0026ndash;openssl-legacy-provider 可以让 Node.js 使用 OpenSSL 的旧版加密方法\nNODE_OPTIONS=--openssl-legacy-provider yarn build # 使用 Yarn 作为包管理工具 NODE_OPTIONS=--openssl-legacy-provider npm run build # 使用 npm 进行构建 第二种 降级Node.js 版本 # 如果你的项目不需要使用 Node.js 17+，降级到 Node.js 16.x 或 14.x 版本也是一种可行的解决方案。\n使用 Node Version Manager (NVM) 来管理 Node.js 版本：\nnvm install 16 nvm use 16 降级后，再次运行构建命令，错误应该会消失。\n第三种 升级相关依赖 # 如果你想继续使用最新版本的 Node.js，另一种解决方案是升级项目中的 Webpack 和 Terser 插件版本。\n更新 terser-webpack-plugin 和 webpack 到最新版本： yarn add terser-webpack-plugin@latest webpack@latest 再次运行构建命令，检查是否已解决问题。\n总结 # error:0308010C 主要是由于 Node.js 和加密库的版本不兼容。通过使用 --openssl-legacy-provider 选项、降级 Node.js 版本或升级构建工具链即可解决\n","date":"2024-09-18","externalUrl":null,"permalink":"/archives/Resolve-Terser-plugin-build-error-Error-0308010C.html","section":"文章","summary":"","title":"解决 Terser 插件构建错误:Error: error:0308010C","type":"posts"},{"content":"Caddy 2.x 版本引入了一个新的命令行工具 caddy hash-password，它可以用来生成密码的哈希值。这个工具可以用在配置 basicauth 认证时生成密码。\n1. 安装 Caddy # 如果还没有安装，你可以从 Caddy官网 下载并安装。\n2. 使用 caddy hash-password 命令 # 打开你的命令行工具，然后输入以下命令来生成密码哈希：\ncaddy hash-password 执行这个命令后会提示你输入密码，为了安全考虑，系统将不会显示你输入的密码，与passwd一样…\n3. 生成密码 # 在提示符后输入你的密码。例如：\nEnter Password: 输入你的密码\n输入密码后，caddy hash-password 将输出密码的哈希值。这个哈希值是使用 bcrypt 算法生成的，可以直接用于 Caddyfile 中的 basic 认证。\n同时也可以使用OpenSSL生成 MD5 密码 （但是不推荐用于生产环境）\nopenssl passwd -apr1 4. 配置 Caddyfile # 将生成的哈希值复制并粘贴到你的 Caddyfile 中，替换相应的密码字段。例如：\nbasicauth @group { mirrors $1a$13$ZhisIsGeneratedHash } 5.例子 例如你想要为用户名 admin 设置密码 mypassword，你可以参考下面的步骤操作：\n运行 caddy hash-password\nEnter Password: 输入mypassword\n会输出类似这样的哈希值：$2a$14$Wmfsb3jZ6kvMf8G4vj4FhO5NtLgXGJ1y4Hj3iW6OZ/7iU6Ve4i06\n将这个哈希值添加到你的 Caddyfile 中，下面是配置文件例子\n@basicdir { # 定义一个名为 @basicdir 的匹配器，用于匹配路径以 /security/ 开头的请求 path /security/* } basicauth @basicdir { # 对 @basicdir 匹配器匹配的路径应用 Basic 认证 username hashed_password # hashed_password 是 bcrypt 加密后的字符串 } ​\n注意事项 # 注意事项\n使用 caddy hash-password 生成的哈希值是唯一的，每次生成的哈希都会不同，即使是相同的密码。\n哈希值是不可逆的，这意味着你不能从哈希值恢复原始密码。\n","date":"2024-09-15","externalUrl":null,"permalink":"/archives/caddy-basic-auth.html","section":"文章","summary":"","title":"Caddy 2.x 配置Basic认证","type":"posts"},{"content":" 在 Linux环境下安装Docker # Debian/Ubuntu # 首先要安装依赖\napt-get update apt-get install ca-certificates curl gnupg -y 信任 Docker 的 GPG 公钥并添加仓库：\ndebian:\ninstall -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \\ \u0026#34;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian \\ \u0026#34;$(. /etc/os-release \u0026amp;\u0026amp; echo \u0026#34;$VERSION_CODENAME\u0026#34;)\u0026#34; stable\u0026#34; | \\ tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null ubuntu\ninstall -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \\ \u0026#34;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \\ \u0026#34;$(. /etc/os-release \u0026amp;\u0026amp; echo \u0026#34;$VERSION_CODENAME\u0026#34;)\u0026#34; stable\u0026#34; | \\ tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null 最后安装docker\napt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 安装完成之后设置为开机自启\nsystemctl enable --now docker RHEL系列操作系统 # 安装相关依赖，下载repo文件\nyum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo sed -i \u0026#39;s+https://download.docker.com+https://mirrors.tuna.tsinghua.edu.cn/docker-ce+\u0026#39; /etc/yum.repos.d/docker-ce.repo 安装docker\nyum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 同样，安装完成最后不要忘了设置开机自启\nsystemctl enable --now docker 配置镜像加速 # 因为某些不可描述的原因无法使用Docker官方镜像仓库，这里提供几种加速的方法\n1、通过更改镜像源实现加速\n修改 /etc/docker/daemon.json 文件（如果不存在则创建），内容如下\n{ \u0026#34;registry-mirrors\u0026#34;: [\u0026#34;https://docker.1ms.run\u0026#34;] } 然后重启Docker\nsystemctl daemon-reload \u0026amp;\u0026amp; systemctl restart docker 2、自建缓存服务器\n参考公众号文章：https://mp.weixin.qq.com/s/6zjn49zH-z7eBlt6Os1TeQ\n3、使用开源项目，CRProxy自建代理服务器，简单来说与上一个的区别就是不会缓存镜像到你的服务器，仅限代理\n参考：https://zhuanlan.zhihu.com/p/711282751\n","date":"2024-09-12","externalUrl":null,"permalink":"/archives/install-docker.html","section":"文章","summary":"","title":"Docker安装教程","type":"posts"},{"content":"Grafana 是一个流行的开源分析和监控平台，用于可视化时间序列数据，Grafana 提供了多种身份验证方式，其中之一是通过 API 密钥 API密钥 是一种基于令牌的身份验证方法，通常用于系统与系统之间的交互。它是一种有效的方法来实现自动化访问，而无需人工干预，例如，我们要将服务器的状态信息展现给其他用户，我们可以利用 Nginx 作为反向代理，通过在请求中自动添加密钥，实现对 Grafana 的无感登录。 Nginx 是一个高性能的 HTTP 服务器和反向代理服务器，它能够处理大量并发连接。通过配置 Nginx，可以在反向代理请求时注入API密钥，从而绕过传统的登录界面，实现无感登录的效果。\n1. 生成 Grafana API Key # 首先，需要在 Grafana 中生成一个 API Key。 在这里我们可以配置API的名称、权限，以及过期时间 温馨提示:生成的API密钥只会显示一次，请妥善管理\n2. 配置Nginx # 接下来，需要使用Nginx配置反向代理，添加grafana 的api密钥到请求头部\nlocation / { proxy_pass http://10.0.81.1:3000; proxy_set_header Host $host; 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; # 添加 api key 到请求头 proxy_set_header Authorization \u0026#34;Bearer abcdefg123456\u0026#34;; # 替换为你的API Key } } 保存配置文件后，测试 Nginx 配置是否正确并重启\nnginx -t sudo systemctl restart nginx 3. 安全性注意事项 # 虽然这种方法简化了登录过程，但也存在安全风险。在使用 API Key 时，建议使用nginx的 allow 或者deny来限制访问来源IP，如果要暴露在公共网络中建议定期轮转API KEY 防止长期使用导致安全问题。\n限制来源：\nlocation / { allow x.x.x.x/x; # 允许特定子网访问 deny all; # 禁止其他所有IP } ","date":"2024-09-07","externalUrl":null,"permalink":"/archives/use-nginx-and-grafana-key-to-login-grafana.html","section":"文章","summary":"","title":"Grafana 鉴权 使用 Nginx 和 Grafana API Key 实现无感登录","type":"posts"},{"content":" Docker 导出容器导入后无法运行报错：Error response from daemon: No command specified. # 根据报错信息是没有指定 command 加上 command即可运行\n[root@wanghaoyu ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/ubuntu latest fb52e22af1b0 3 weeks ago 72.8 MB ​ [root@wanghaoyu ~]# docker run -itd docker.io/ubuntu:latest 7ce403eba8d89127395e057eb4d6cf7eb2374e40214ce6575a29e755b707ca23 ​ [root@wanghaoyu ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7ce403eba8d8 docker.io/ubuntu:latest \u0026#34;bash\u0026#34; 4 seconds ago Up 3 seconds hungry_babbage [root@wanghaoyu ~]# docker export -o test.tar 7ce403eba8d8 ​ [root@wanghaoyu ~]# ls anaconda-ks.cfg test.tar ​ [root@wanghaoyu ~]# docker import test.tar test/ubuntu:v0.1 sha256:c3d604273769f7a74126b27f29c8cecd055fc18b10c943da0c0528dc28520a60 ​ [root@wanghaoyu ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test/ubuntu v0.1 c3d604273769 4 seconds ago 72.8 MB docker.io/ubuntu latest fb52e22af1b0 3 weeks ago 72.8 MB ​ [root@wanghaoyu ~]# docker run -itd test/ubuntu:v0.1 /usr/bin/docker-current: Error response from daemon: No command specified. See \u0026#39;/usr/bin/docker-current run --help\u0026#39;. ​ # 加上 /bin/bash [root@wanghaoyu ~]# docker run -itd test/ubuntu:v0.1 /bin/bash 5b7dca448a1400716fa931463c2c314e0e59c7888fa77d5b182e72d911122966 ​ [root@wanghaoyu ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5b7dca448a14 test/ubuntu:v0.1 \u0026#34;/bin/bash\u0026#34; 3 seconds ago Up 2 seconds ","date":"2024-08-31","externalUrl":null,"permalink":"/archives/docker-Error-response-from-daemon-No-command-specified.html","section":"文章","summary":"","title":"Docker 导出容器导入后无法运行报错：Error response from daemon: No command specified","type":"posts"},{"content":"随着Windows操作系统的不断更新，远程桌面连接的安全性也在逐步提高。然而，这也带来了一些兼容性问题。特别是当高版本的Windows操作系统尝试连接到旧版本Windows操作系统时，经常会出现“身份验证错误，要求的函数不支持”的提示。这不仅影响了工作效率，还可能导致重要的数据传输中断。\n在接下来的文章中，我们将详细介绍如何进行这些组策略的修改。通过一步步的指导，您将了解如何调整远程桌面连接的身份验证设置，以确保不同版本的Windows操作系统之间能够顺利建立连接。\n希望这篇文章能够帮助您解决远程桌面连接中的身份验证问题。\n方法1 修改组策略（推荐） # 按下 WIn+R 键 打开运行窗口，输入 gpedit.msc 并回车 打开组策略编辑器。\n打开之后，如下图所示，我们找到 计算机配置 \u0026gt; 管理模板 \u0026gt; 系统 \u0026gt; 凭据分配 \u0026gt; 加密数据库修正 \u0026gt; 启用并改为易受攻击\n刷新组策略 切记！！！\n按下Win+R键，打开“运行”对话框。 输入cmd并按下Enter键，打开命令提示符。 在命令提示符中，输入gpupdate /force并按下Enter键刷新组策略就可以了。 如果是Powershell的话 输入Invoke-GPUpdate -Force 并回车就可以了。 方法2 关闭NLA # 关闭NLA可以禁用网络级别身份验证，从而允许远程桌面连接通过。在Windows系统上，可以通过修改注册表、使用系统属性对话框的远程选项卡上的选项，或者使用PowerShell来禁用NLA。\n2.1 使用 PowerShell 关闭 NLA # 禁用NLA\nNew-ItemProperty -Path \u0026#34;HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\u0026#34; -Name \u0026#34;fSingleSessionPerUser\u0026#34; -Value 1 -Type DWord 禁用User Account Control（UAC）\nNew-ItemProperty -Path \u0026#34;HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\u0026#34; -Name \u0026#34;ConsentPromptBehaviorUser\u0026#34; -Value 5 -Type DWord 禁用UAC通知\nNew-ItemProperty -Path \u0026#34;HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System\u0026#34; -Name \u0026#34;PromptOnSecureDesktop\u0026#34; -Value 0 -Type DWord 重启计算机以使更改生效.\n2.2 使用注册表禁用 NLA # 打开注册表编辑器（在Windows上按Win+R键，输入regedit并回车）。\n定位到以下注册表路径\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\WinStations\\RDP-Tcp 在右侧窗格中，找到名为\u0026quot;SecurityLayer\u0026quot;的键。\n双击\u0026quot;SecurityLayer\u0026quot;键，将其值改为\u0026quot;7\u0026quot;即可。\n以上方法适用于Windows操作系统，具体步骤可能因系统版本和设置而略有不同。\n","date":"2024-08-22","externalUrl":null,"permalink":"/archives/Windows-Remote-Desktop-authentication-error.html","section":"文章","summary":"","title":"Windows mstsc远程桌面提示“身份验证错误，要求的函数不支持” 解决方案","type":"posts"},{"content":"CloudFlare Warp KEY 收集工具,是一个从特定群组抓取可用的WARP KEY的工具，每天更新数据\n获取KEY # 可以从以下 URL 直接获取 Warp KEY（每天更新数据）：\nhttps://raw.githubusercontent.com/nas-tool/warpkey/main/data/full https://raw.githubusercontent.com/nas-tool/warpkey/main/data/full 也可以通过我的GitHub Pages中获取：CloudFlare WARP Key (geeklinux-io.github.io)\n私有化部署 # 1. 安装 Go # 请确保你已经安装了 Go。你可以从 Go 官方网站 下载并安装 Go。\n2. 克隆项目 # 将此仓库克隆到你的本地环境中：\ngit clone https://github.com/nas-tool/warpkey.git cd warpkey 3. 配置 # 如有需要，可以编辑 main.go 文件以调整配置。\n4. 运行/构建 # 在配置好 Go 环境之后，可以使用以下命令打包程序：\nchmod a+x build.sh ./build.sh 构建好的应用程序会自动放置到当前所在目录的build目录中。\n该工具将从 Telegram 收集密钥，并将其保存在当前目录下的/data目录中。\n你也可以通过特定的Socket 或者HTTP代理去请求获取 CloudFlare Key 可以使用 \u0026ndash;proxy 参数，当然，你也可以使用 程序 -h 查看具体的使用帮助。\n5. 自动更新 # 您可以使用crontab计划任务执行项目中的update.sh脚本，并将更新提交到您的GitHub存储库中。\n许可 # 该项目使用 MIT 许可。有关详细信息，请参阅 LICENSE 文件。\n","date":"2024-08-15","externalUrl":null,"permalink":"/archives/cloudflare-warp-key.html","section":"文章","summary":"","title":"CloudFlare Warp KEY 收集工具","type":"posts"},{"content":" 前言 # 在云计算和虚拟化技术日益普及的今天，定制化的虚拟机镜像对于满足特定需求显得尤为重要。Virt-Manager是一个强大的工具，可以帮助用户轻松地创建和管理虚拟机镜像。本文将指导您如何使用Virt-Manager定制Windows Server的QCOW2镜像，以满足您的特定需求。通过本文，您将了解如何配置操作系统、安装软件、设置网络参数等，以便在虚拟机上获得所需的系统环境。\n准备工作 # Windows Server iso 镜像 安装有libvirt套件的虚拟机一台 virtio-win iso驱动包 1、上传所需镜像到KVM虚拟机 # 将您的virtio-win iso驱动包以及Windows Server iso 镜像，通过WinSCP或者其他SFTP工具上传到您的KVM虚拟机的中，如下图所示；\n2、创建虚拟机 # 打开您的Virt-Manager软件 如下图所示\n点击创建新的虚拟机\n选择您的ISO镜像\n下面磁盘的话先不要创建新的磁盘，把Enable storage for this vm复选框去掉。\n3、配置虚拟机参数 # 虚拟机创建完成之后，先不要开机，我们需要对虚拟机做出如下更改。\n3.1 添加硬盘 # 因为创建时添加的硬盘默认是SATA协议的硬盘，所以我们需要创建完成虚拟机之后手动添加硬盘，改为VirtIO\n3.2 添加virtio-win iso驱动 # 重复上面类似的步骤，添加virtio-win iso驱动包\n3.3 配置引导项 # 配置引导项，确保可以从系统安装镜像启动即可。\n3.4 配置网卡类型 # 修改网卡类型为virtio\n4、安装操作系统 # 注意：因为我们之前添加的硬盘 VirtIO 类型的，（步骤3.1）默认 Windows 安装镜像包中并不携带此类型的驱动程序，所以我们现在要使用之前添加的 virtio-win iso 驱动包（步骤3.1）来安装 VirtIO 驱动程序。\n5、调整操作系统 # 5.1 安装驱动 # 按 WIN + X 打开系统快捷菜单，打开设备管理器，如下图\n可以看到有2个带感叹号的设备，选择以太网控制器，右击选择更新驱动程序，选择浏览查找驱动程序\n直接选择virtio-win这个光驱就可以了，无需手动选择里面的驱动程序。\n勾选始终信任 进行安装驱动程序。\n其他的以下驱动程序重复上述步骤即可。\n5.2 开启远程桌面 # 使用sysdm.cpl打开系统属性页面\n开启远程桌面服务\n打开控制面板 找到 Windows Defender防火墙 将其关闭。\n5.3 运行Sysprep # 勾选通用，选择完成后关机\n6、压缩QCOW2镜像 # 由下图我们可以看到 当前镜像占用空间为14.8GiB\n我们用如下命令进行压缩操作。\nqemu-img convert -c -O qcow2 win2k22.qcow2 win2k22_new.qcow2 压缩完成后大小\n到此就结束了，Enjoy it ~\n","date":"2024-08-14","externalUrl":null,"permalink":"/archives/winserver-qocw2.html","section":"文章","summary":"","title":"使用Virt-Manager定制 Windows Server QCOW2镜像","type":"posts"},{"content":"","date":"2024-02-02","externalUrl":null,"permalink":"/tags/kolla-ansible/","section":"标签","summary":"","title":"Kolla-Ansible","type":"tags"},{"content":" 系统环境初始化 # 1、安装基础软件包\nyum install -y python-devel libffi-devel gcc openssl-devel libselinux-python git wget vim yum-utils 2、设置主机名 以及hosts\nhostnamectl set-hostname openstack \u0026amp;\u0026amp; bash vim /etc/hosts #设置hosts 先清空hosts文件 然后添加如下 192.168.122.10\topenstack\topenstack 192.168.122.10\tkolla\tkolla #或者使用 tee /etc/hosts \u0026lt;\u0026lt;-\u0026#39;EOF\u0026#39; 192.168.122.10 openstack openstack 192.168.122.10 kolla kolla EOF 3、关闭系统安全设置\n#关闭防火墙 systemctl stop firewalld \u0026amp;\u0026amp; systemctl disable firewalld #关闭网络管理组件 systemctl stop NetworkManager \u0026amp;\u0026amp; systemctl disable NetworkManager #关闭selinux setenforce 0 \u0026amp;\u0026amp; sed -i \u0026#39;s/^SELINUX=.*/SELINUX=disabled/\u0026#39; /etc/selinux/config 安装pip和docker # 1、pip是python的包管理器，ansible是基于python开发的，因此需要安装pip，安装相应的python包，默认系统都不带pip。\n#下载pip wget https://bootstrap.pypa.io/pip/2.7/get-pip.py #安装pip python get-pip.py #更新其他包 pip install -U setuptools 2、安装docker，docker是本次部署的关键，因此需要安装docker。\n#添加docker软件仓库 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum makecache #安装docker-ce不是docker yum -y install docker-ce #启动服务 systemctl enable docker systemctl start docker #修改docker镜像的源 sudo tee /etc/docker/daemon.json \u0026lt;\u0026lt;-\u0026#39;EOF\u0026#39; { \u0026#34;registry-mirrors\u0026#34;: [\u0026#34;http://hub-mirror.c.163.com\u0026#34;] } EOF\tsystemctl daemon-reload service docker restart 3、配置pip加速\ncd ~ mkdir .pip tee .pip/pip.conf \u0026lt;\u0026lt; EOF　[global] [index-url=http://mirrors.aliyun.com/pypi/simple/ [install] trusted-host=mirrors.aliyun.com EOF 安装ansible kolla-ansible # kolla-ansible部署是需要使用ansible，它是一款自动化的工具，是基于python开发。因此ansible是必须部署。\n#获取epel的yum文件，否则ansible可能找不到包 yum install epel-release -y #安装ansible yum install ansible -y #试过很多次都要先安装这个报告 pip install pbr #开始安装kolla-ansible,要带上--ignore-installed ，否则可能会报错 pip install -i https://mirrors.aliyun.com/pypi/simple/ kolla-ansible --ignore-installed #创建kolla的文件夹，后续部署的时候很多openstack的配置文件都会在这 mkdir -p /etc/kolla chown $USER:$USER /etc/kolla #复制ansible的部署配置文件 cp -v /usr/share/kolla-ansible/ansible/inventory/* /etc/kolla/. #负责gloable.yml和password.yml到目录 cp -rv /usr/share/kolla-ansible/etc_examples/kolla/* /etc/kolla/. #检查`etc/kolla`文件夹下的文件 修改配置文件 # 1、修改ansible的配置文件。\nvi /etc/ansible/ansible.cfg [defaults] host_key_checking=False #line 71 pipelining=True #line 401 forks=100 #line 20 2、all-in-one配置节点清单文件 /etc/kolla/all-in-one\n如果配置本地免密登录可以不需要使用\nansible_user=root ansible_password=openstack ansible_become=true\n使用vim进行替换\n1,$s/localhost ansible_connection=local/openstack ansible_user=root ansible_password=openstack ansible_become=true/ [control] openstack ansible_user=root ansible_password=openstack ansible_become=true [network] openstack ansible_user=root ansible_password=openstack ansible_become=true [compute] openstack ansible_user=root ansible_password=openstack ansible_become=true [storage] openstack ansible_user=root ansible_password=openstack ansible_become=true [monitoring] openstack ansible_user=root ansible_password=openstack ansible_become=true [deployment] openstack ansible_user=root ansible_password=openstack ansible_become=true 3、global.yml添加\n#选择下载的基础镜像 kolla_base_distro: \u0026#34;centos\u0026#34; #选择的安装方法，2选1。binary二进制安装，source源码安装 kolla_install_type: \u0026#34;binary\u0026#34; #选择OpenStack的版本标签， openstack_release: \u0026#34;yoga\u0026#34; #OpenStack内部管理网络地址，通过该IP访问OpenStack Web页面进行管理。如果启用了高可用，需要设置为VIP（浮动IP） kolla_internal_vip_address: \u0026#34;192.168.122.10\u0026#34; #OpenStack内部管理网络地址的网卡接口 network_interface: \u0026#34;ens33\u0026#34; #此网卡应该在没有IP地址的情况下处于活动，如果不是，那么OpenStack云平台中的云主机实例将无法访问外部网络。（存在IP时br-ex桥接就不成功） neutron_external_interface: \u0026#34;ens34\u0026#34; #关闭高可用 enable_haproxy: \u0026#34;no\u0026#34; #关闭cinder（块存储） enable_cinder: \u0026#34;yes\u0026#34; enable_cinder_backend_lvm: \u0026#34;yes\u0026#34; #指定nova-compute守护进程使用的虚拟化技术。（kvm好像有点问题，大家可以试试，看看你们能不能过nova下载）nova-compute\u0026gt;是一个非常重要的守护进程，负责创建和终止虚拟机实例，即管理虚拟机实例的生命周期。 nova_compute_virt_type: \u0026#34;qemu\u0026#34; #在物理机上部署时无需设置（默认kvm） ，仅在虚拟机中部署kolla-ansible-openstack时设置qemu 4、生成密码 并修改webui的密码\nkolla-genpwd vim /etc/kolla/passwords.yml /keystone_admin_password 5、cinder的配置准备，官方文档未说明的点，需要提前创建好cinder使用的vgs(如果使用cinder请忽略。。)\npvcreate /dev/sdb vgcreate cinder-volumes /dev/sdb 开始部署 # 环境预检查 没问题直接next\nkolla-ansible -i /etc//kolla/all-in-one prechecks 拉取镜像 时间有点长 大概15Minutes\nkolla-ansible -i /etc/kolla/all-in-one pull 正式部署 大概15Minutes\nkolla-ansible -i /etc/kolla/all-in-one deploy 验证安装\nkolla-ansible -i /etc/kolla/all-in-one post-deploy cat /etc/kolla/admin-openrc.sh /etc/kolla/all-in-one pull 正式部署 大概15分钟左右，具体取决于你的网络环境\nkolla-ansible -i /etc/kolla/all-in-one deploy 验证安装\nkolla-ansible -i /etc/kolla/all-in-one post-deploy cat /etc/kolla/admin-openrc.sh ","date":"2024-02-02","externalUrl":null,"permalink":"/archives/kolla-ansible-openstack-t.html","section":"文章","summary":"","title":"Kolla-Ansible OpenStack 部署","type":"posts"},{"content":"","date":"2024-02-02","externalUrl":null,"permalink":"/tags/openstack/","section":"标签","summary":"","title":"Openstack","type":"tags"},{"content":"在此之前，您需要一个授权使用红帽官方仓库的RHNID，才能够订阅官方仓库。\nsubscription-manager register # 注册当前操作系统 执行此命令时，系统会提示您输入红帽帐户的账号和密码。 输入账号密码后，系统会提示绑定成功，并生成您系统的唯一标识符。 使用订阅池ID激活订阅 # 使用 subscription-manager attach --auto 命令来自动附加订阅。\n这个命令会尝试找到与您的红帽帐户相关联的订阅，并自动附加到您的系统上。\n或者使用subscription-manager attach \u0026ndash;pool=Pool ID命令，其中的Pool ID`应替换为您实际使用的订阅池ID。\n通过以上步骤，您就可以使用subscription-manager完成红帽系统的认证并添加所需的仓库。\nsubscription-manager命令帮助 # subscription-manager是一个用于管理系统订阅和产品信息的命令行工具，它可以帮助用户轻松地管理系统的订阅情况。该工具提供了多个主要模块和其他辅助模块，以满足用户对系统订阅管理的各种需求。\n主要模块包括：\nattach（附加）：将指定的订阅附加到注册系统上，当系统不使用简单内容访问模式时使用。 list（列表）：列出此系统的订阅和产品信息。 refresh（刷新）：从服务器获取最新的订阅数据。 register（注册）：将此系统注册到客户门户或其他订阅管理服务。 release（发布）：配置要使用的操作系统版本。 remove（移除）：从此系统中移除所有或特定的订阅。 status（状态）：显示此系统订阅和产品的状态信息。 unregister（注销）：从客户门户或其他订阅管理服务中注销此系统。 其他模块包括：\naddons（插件）：已弃用，参见syspurpose。 auto-attach（自动附加）：设置是否按计划（默认为每日）附加订阅。 clean（清理）：删除所有本地系统和订阅数据，而不影响服务器。 config（配置）：列出、设置或删除此系统使用的配置参数。 environments（环境）：显示用户可用的环境。 facts（事实）：查看或更新检测到的系统信息。 identity（身份）：显示此系统的身份证书或请求新的身份证书。 import（导入）：导入在工具之外提供的证书。 orgs（组织）：显示用户可以向其注册系统的组织。 plugins（插件）：使用subscription-manager plugins查看和配置插件。 redeem（兑换）：尝试为预配置系统兑换订阅。 repo-override（仓库覆盖）：管理自定义内容仓库设置。 repos（仓库）：列出此系统有权使用的仓库。 role（角色）：已弃用，参见syspurpose。 service-level（服务级别）：已弃用，参见syspurpose。 syspurpose（系统目的）：管理所有系统目的设置的便捷模块。 usage（用法）：已弃用，参见syspurpose。 version（版本）：打印版本信息。 subscription-manager命令的常用示例：\n注册系统：\nsubscription-manager register --username YOUR_USERNAME --password YOUR_PASSWORD 这个命令将系统注册到订阅管理服务，替换YOUR_USERNAME和YOUR_PASSWORD为你的用户名和密码。\n列出系统的订阅信息：\nsubscription-manager list 这个命令将列出系统当前的订阅信息，包括订阅名称、ID、状态等。\n附加订阅：\nsubscription-manager attach --pool=YOUR_POOL_ID 这个命令将指定的订阅附加到系统上，替换YOUR_POOL_ID为你要附加的订阅池的ID。\n刷新订阅数据：\nsubscription-manager refresh 这个命令将从服务器拉取最新的订阅数据，确保系统获取到最新的订阅信息。\n移除订阅：\nsubscription-manager remove --all 这个命令将移除系统上的所有订阅，可以通过指定具体的订阅ID来移除特定的订阅。\n注销系统：\nsubscription-manager unregister 这个命令将系统从订阅管理服务中注销，取消系统的订阅关系。\n","date":"2023-09-01","externalUrl":null,"permalink":"/archives/rhel-subscription.html","section":"文章","summary":"","title":"在RedHat Linux 9 中配置订阅官方存储库","type":"posts"},{"content":"我手上的型号是某OEM厂商的定制盘，主控芯片为PHISON-PS3111-S11-13 经过升级工具升级完美解决硬盘型号变成satafirms11的问题。\n所需工具及文件资源\n– 查看固态硬盘芯片的Phison flash id – SBFMP1W3自动升级文件\n以上所需在群联PHISON S11的官方固态资源库中基本可以找到，有部分型号的固件包可能需要用户自行另找，链接如下：\nhttp://vlo.name:3000/ssdtool/\nSBFMP1W3自动升级文件下载请看文末\n此工具仅限给已经被写保护的SSD（固件名为SBFMP1W3）升级固件，升级成功后SSD的固件将升级为SBFMP1B3； 2 . 程序是打开后直接自动运行的，不用手动开始。 3. 软件如图中2处显示的是Device FW name：SBFMP1W3（更新前检测到的FW版本）。 检测版本可以通过，phison_flash_id这个软件来检测。 v0.83a Drive: 1(ATA) OS: 10.0 build 19044 Model: Thinklife SSD ST800 256G Fw : SBFMP1B3 这个是已经升级成功的fw。 下载地址：\n下载地址 ","date":"2023-02-02","externalUrl":null,"permalink":"/archives/PS3111.html","section":"文章","summary":"","title":"PS3111主控的SSD固态硬盘固件门掉盘通病问题解决，硬盘型号变成satafirms11修复步骤","type":"posts"},{"content":"","date":"2023-02-02","externalUrl":null,"permalink":"/tags/%E7%A1%AC%E7%9B%98/","section":"标签","summary":"","title":"硬盘","type":"tags"},{"content":"前几天心血来潮，在某宝上面买了一块esp32 温湿度模块，空气质量模块，来给我的机柜做温湿度传感器，空气质量监测（烟感报警）\n","date":"1970-01-01","externalUrl":null,"permalink":"/esp32-tmp.html","section":"Haoyu Wang's Blog","summary":"","title":"ESP32实现温湿度监控","type":"page"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]