[{"data":1,"prerenderedAt":2661},["ShallowReactive",2],{"navigation":3,"posts-undefined-HomeLab-0-999":20},[4,8,12,16],{"title":5,"path":6,"stem":7},"首页","\u002F","00.index",{"title":9,"path":10,"stem":11},"文章","\u002Fposts","01.posts",{"title":13,"path":14,"stem":15},"动态","\u002Fmoments","02.moments",{"title":17,"path":18,"stem":19},"关于","\u002Fabout","09.about",[21,82,318,2237,2313,2620],{"id":22,"title":23,"body":24,"class":61,"cover":62,"coverSize":61,"date":63,"description":30,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":66,"navigation":67,"path":68,"readingTime":69,"seo":74,"sitemap":75,"stem":76,"tags":77,"time":61,"weather":61,"__hash__":81},"posts\u002Fposts\u002F2025\u002F20250708.delete-service-of-synology.md","删除群晖 Synology 证书设置中自定义的服务",{"type":25,"value":26,"toc":58},"minimark",[27,31,34,38,41,52,55],[28,29,30],"p",{},"今天在给群晖增加一个自带的 DDNS 服务以实现在自建的 DDNS 挂掉的情况下还有备用方案。",[28,32,33],{},"在配置证书的时候，发现设置列表中有一个我之前自定义的服务，也不记得是在哪里设置的了，但是找不到地方删除。",[35,36],"post-image",{"filename":37},"01.png",[28,39,40],{},"经过一番探寻，发现在这个文件中：",[42,43,48],"pre",{"className":44,"code":46,"language":47},[45],"language-text","\u002Fusr\u002Fsyno\u002Fetc\u002Fcertificate\u002F_archive\u002FINFO\n","text",[49,50,46],"code",{"__ignoreMap":51},"",[28,53,54],{},"可以看到所有的服务列表，找到对应的服务的 JSON 对象，删掉保存即可。",[35,56],{"filename":57},"02.png",{"title":51,"searchDepth":59,"depth":59,"links":60},2,[],null,"png","2025-07-08",false,"md",{},true,"\u002Fposts\u002F2025\u002Fdelete-service-of-synology",{"text":70,"minutes":71,"time":72,"words":73},"1 min read",0.635,38100,127,{"title":23,"description":30},{"loc":68,"lastmod":63},"posts\u002F2025\u002F20250708.delete-service-of-synology",[78,79,80],"NAS","群晖","HomeLab","0LG2kNgCF7bxyd30Qp42-GKe7SgerIL1bxhbGiAuqBQ",{"id":83,"title":84,"body":85,"class":61,"cover":301,"coverSize":61,"date":302,"description":51,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":303,"navigation":67,"path":304,"readingTime":305,"seo":310,"sitemap":311,"stem":312,"tags":313,"time":61,"weather":61,"__hash__":317},"posts\u002Fposts\u002F2025\u002F20250707.homelab-disaster-postmortem.md","一次 HomeLab 灾难级事故的复盘",{"type":25,"value":86,"toc":295},[87,91,209,212,258,261,281,284],[88,89,90],"h2",{"id":90},"时间线",[92,93,94,102,108,114,120,126,132,138,144,150,156,161,167,173,179,185,191,197,203],"ul",{},[95,96,97,101],"li",{},[98,99,100],"strong",{},"2025-07-07 09:33",": TP-LINK 主路由设备上线告警（上次离线原因：设备重启）",[95,103,104,107],{},[98,105,106],{},"2025-07-07 09:34",": 收到群晖异常关机的邮件通知（收到该通知说明群晖已经重启过了，实际重启时间会更早一点）",[95,109,110,113],{},[98,111,112],{},"2025-07-07 09:36",": 尝试登录群晖 DSM，发现域名解析有问题，无法登录；尝试 ToDesk 远程连接家里的 PC，发现不在线（未开机）",[95,115,116,119],{},[98,117,118],{},"2025-07-07 09:40",": 收到 Uptime Kuma 监控服务的各种告警通知，多项服务不可用",[95,121,122,125],{},[98,123,124],{},"2025-07-07 09:52",": 通过 TP-LINK 商用云平台远程查看主路由，发现可连接，但由于之前为了 IPTV 改为了光猫的子路由（非桥接），无法查看到公网 IP；尝试通过电信的小翼管家查看公网 IP，发现没有入口可查",[95,127,128,131],{},[98,129,130],{},"2025-07-07 09:54",": 尝试通过群晖的 QuickConnect 远程访问，发现之前被我关闭了",[95,133,134,137],{},[98,135,136],{},"2025-07-07 10:30",": 查看自己写的 bots 服务代码（含 ddns 功能），请求失败时，有 backoff 策略，首次失败休眠 1 分钟，然后再失败休眠 10 分钟，再失败休眠 1 小时，决定再等一小时看看",[95,139,140,143],{},[98,141,142],{},"2025-07-07 11:00",": 在 TP-LINK 主路由管理页面尝试通过网络唤醒服务唤醒家里的 PC，发现无法唤醒（事后发现之前记录的网卡 MAC 不对）",[95,145,146,149],{},[98,147,148],{},"2025-07-07 11:30",": 通过米家控制办公桌的智能插座电源重启，尝试唤醒 PC，未成功；打算通过控制机柜的智能插座重启，实现所有服务的重启，但还打算再等等 bots 的 ddns 能否生效",[95,151,152,155],{},[98,153,154],{},"2025-07-07 11:44",": 等了 2 个多小时了，感觉 bots 服务可能已经不在运行，再等下去也没用了，经过深思熟虑决定重启整个机柜电源",[95,157,158,160],{},[98,159,154],{},": 通过米家控制智能插座关闭电源，发现状态未更新，再次点击发现操作失败，此时发现智能插座设备已离线，意识到机柜一旦断电，所有米家设备也无法控制了，再也无法打开",[95,162,163,166],{},[98,164,165],{},"2025-07-07 11:50",": 出发回家，准备手动重启机柜电源",[95,168,169,172],{},[98,170,171],{},"2025-07-07 12:49",": 到家，手动开启机柜智能插座电源",[95,174,175,178],{},[98,176,177],{},"2025-07-07 12:50",": 打开 PC，发现 主板 PCI-E 设备唤醒是 Enabled",[95,180,181,184],{},[98,182,183],{},"2025-07-07 12:51",": 进入 PC 系统，发现网卡的允许设备唤醒也是启用的，但网卡 MAC 地址和之前配置的不一样，原因后面详述",[95,186,187,190],{},[98,188,189],{},"2025-07-07 12:53",": 通过 PC 内网登录 portainer，发现 bots 容器处于 stopped 状态（Stopped for 3 hours with exit code 127），finished 时间为 09:33:52",[95,192,193,196],{},[98,194,195],{},"2025-07-07 12:54",": 手动重新启动 bots 容器，正常启动",[95,198,199,202],{},[98,200,201],{},"2025-07-07 12:55",": bots 服务已正常更新域名解析，手机切换到蜂窝测试，已经可正常访问",[95,204,205,208],{},[98,206,207],{},"2025-07-07 13:01",": 出门赶回公司",[88,210,211],{"id":211},"原因分析",[92,213,214,220,226,232,252],{},[95,215,216,219],{},[98,217,218],{},"导火索","：家里异常断电（TP-LINK 和群晖都在机柜里，他俩同时重启，可断定机柜掉电了；光猫在弱电箱里，查看光猫的启动时间，也在同一时间重启过，可判断是全屋断电了）",[95,221,222,225],{},[98,223,224],{},"直接原因","：自建的 DDNS 服务在光猫重启后公网 IP 发生变化的情况下未更新解析，导致所有服务无法远程访问",[95,227,228,231],{},[98,229,230],{},"根本原因","：包含了 DDNS 服务的 bots 容器在宿主机重启后未能重启成功，经过分析发现因为 bots 容器启动过程中挂载了群晖中的一个目录，用来更新 clash 的配置文件，但是群晖启动会比 bots 容器所在的宿主机慢，可能导致了启动失败",[95,233,234,237,238],{},[98,235,236],{},"处理慢的原因（多种补救措施失效）","：\n",[92,239,240,243,246,249],{},[95,241,242],{},"家里的 PC 未开机，无法通过 ToDesk 远程连接处理（之前几次类似问题都是通过 ToDesk 远程修复）",[95,244,245],{},"家里没人，无法帮忙手动启动 PC",[95,247,248],{},"PC 的远程唤醒功能失效，原因是网卡 MAC 地址记录不正确，这是因为之前记录的是一个虚拟网卡的 MAC，上次去掉了虚拟网卡，直接走的物理网卡，但是忘记记录 MAC 地址",[95,250,251],{},"群晖的 QuickConnect 远程访问服务失效，之前感觉用不到被我手动关闭了",[95,253,254,257],{},[98,255,256],{},"故障升级原因","：由于多个补救方案失效，尝试通过机柜断电重启的方式补救，结果所有设备断电，断绝了任何远程补救的可能",[88,259,260],{"id":260},"改进措施",[92,262,263,266,269,272,275,278],{},[95,264,265],{},"✅ 购买 UPS，确保机柜设备在短暂断电时能够继续供电，避免意外断电导致的服务中断（07-08 更新: 已购买山特 SANTAK TG-BOX850 UPS）",[95,267,268],{},"✅ 提升 DDNS 服务的核心程度，从 bots 项目中独立出来，减少其他依赖（07-08 更新: 已完成）",[95,270,271],{},"✅ 启用群晖的 QuickConnect 服务， DDNS 失效后可连接到群晖上进行一些处理",[95,273,274],{},"✅ 确保 PC 的网络远程唤醒功能正常，可通过远程连接到 PC 解决问题",[95,276,277],{},"✅ 部署一个 Cloudflare Tunnel 容器，作为 DDNS 失效后的备用方案",[95,279,280],{},"✅ 把机柜的米家插座从米家 APP 首页移除，避免误操作关闭电源，吸取教训，以后不要再给机柜断电了",[88,282,283],{"id":283},"经验教训",[92,285,286,289,292],{},[95,287,288],{},"之前出现过一次机柜断电后 DDNS 服务不可用导致无法访问的问题，当时通过 ToDesk 远程连接到 PC，然后通过内网重启了 bots 服务解决了问题，但应该更进一步，看看为什么 bots 服务没有自动重启成功，从而可以避免这次的事故",[95,290,291],{},"核心的服务需要保障高可用，例如公网访问这件事，除了自建的 DDNS 之外，还需要通过 QuickConnect、Cloudflare Tunnel 等多种手段保证可用性",[95,293,294],{},"任何情况下都不要尝试给整个机柜断电这种操作，应该优先考虑其他补救措施",{"title":51,"searchDepth":59,"depth":59,"links":296},[297,298,299,300],{"id":90,"depth":59,"text":90},{"id":211,"depth":59,"text":211},{"id":260,"depth":59,"text":260},{"id":283,"depth":59,"text":283},"jpg","2025-07-07",{},"\u002Fposts\u002F2025\u002Fhomelab-disaster-postmortem",{"text":306,"minutes":307,"time":308,"words":309},"8 min read",7.38,442800,1476,{"title":84,"description":51},{"loc":304,"lastmod":302},"posts\u002F2025\u002F20250707.homelab-disaster-postmortem",[314,80,315,316],"技术","运维","复盘","ICyfDes8hfks9c7nlNUEMy8kuWktGhB5Iu1TriECazU",{"id":319,"title":320,"body":321,"class":61,"cover":61,"coverSize":61,"date":2222,"description":325,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":2223,"navigation":67,"path":2224,"readingTime":2225,"seo":2230,"sitemap":2231,"stem":2232,"tags":2233,"time":61,"weather":61,"__hash__":2236},"posts\u002Fposts\u002F2024\u002F20241212.managing-containers-in-my-homelab.md","如何管理并自动更新 HomeLab 中的容器",{"type":25,"value":322,"toc":2218},[323,326,329,332,335,338,347,350,353,356,359,362,369,375,381,406,412,830,841,846,2177,2187,2208,2211,2214],[28,324,325],{},"经过一段时间的探索和优化，我已经找到一个特别适合我的方法，来自动化管理我 HomeLab 中的众多容器。一直想写一篇文章来记录一下，拖到今天才开始动笔。",[28,327,328],{},"首先交代一下背景，我的 HomeLab 中目前总共管理着 50 多个容器，分布在群晖 NAS 以及另外两台 NUC 的虚拟机中。从最初的直接通过 compose 文件手动管理，到后来使用了 Portainer，再到现在的基于 GitLab CI 的自动化管理方式，逐渐变得更自动化、更方便、也更不容易出错。",[88,330,331],{"id":331},"为什么要自动化管理",[28,333,334],{},"一开始上 Portainer，是为了解决手动管理多个设备中的容器，频繁 SSH 到不同设备中比较麻烦的问题。用 Portainer 确实可以方便快捷地管理多个设备中的容器，当容器数量比较少的时候，还是非常推荐的。",[28,336,337],{},"随着容器数量的变多，Portainer 上我遇到两个不太好解决的问题：",[339,340,341,344],"ol",{},[95,342,343],{},"容器的自动更新，Portainer 的 Business 版是提供了自动更新的功能的，但可惜 CE 版没有",[95,345,346],{},"容器的配置文件管理、配置和 compose 文件的备份、版本记录等",[28,348,349],{},"第二个问题比较好解决，通过 git 管理 compose 文件和配置文件，结合 GitLab CI 在文件变更的时候自动 SSH 到对应宿主机上执行容器的更新操作。",[28,351,352],{},"第一个问题，是我在本博客项目上使用 renovate 来更新前端依赖的时候，从他们的文章中看到，renovate 也可以检测 Docker 镜像的更新，于是灵光一现，基于上一步所有 compose 文件都已经在 GitLab 中管理了，那再结合 renovate，就可以实现容器的自动更新了。",[88,354,355],{"id":355},"最终形态",[28,357,358],{},"中间摸索的步骤由于已经过了差不多几个月了，不太容易复盘了，就把最终的形态分享一下。",[35,360],{":dark-supported":361,"filename":37},"true",[28,363,364,365,368],{},"如上图，所有容器的配置文件、compose 文件都放在 ",[49,366,367],{},"config-files"," 这个项目中，该项目通过 GitLab 管理，renovate 在定期检测到镜像更新的时候，会自动提交一个 MR，可以通过一定规则配置成自动合并或手动合并，当然也支持手动修改配置文件或 compose 文件。MR 合并或配置文件修改后触发 GitLab CI，通过 SSH 登录对应宿主机上，执行对应的配置文件更新或容器更新的操作。",[28,370,371,372,374],{},"我的 ",[49,373,367],{}," 目录结构：",[42,376,379],{"className":377,"code":378,"language":47},[45],".\n├── aliyun\n│   ├── gateway\n│   └── hk\n├── homelab\n│   ├── scripts\n│   ├── synology\n│   ├── vm-rocky-01\n│   └── vm-rocky-02\n├── .gitlab-ci.yml\n└── renovate.json\n",[49,380,378],{"__ignoreMap":51},[28,382,383,386,387,390,391,394,395,398,399,402,403,405],{},[49,384,385],{},"aliyun"," 目录中的 ",[49,388,389],{},"gateway"," 和 ",[49,392,393],{},"hk"," 是我在杭州和香港的两台服务器，部署一些外网项目，也是通过上述的方式管理。",[49,396,397],{},"homelab"," 中的 ",[49,400,401],{},"scripts"," 是一些脚本，与本文无关，但也是通过 ",[49,404,367],{}," 项目统一管理的。",[28,407,371,408,411],{},[49,409,410],{},"renovate.json"," 如下：",[42,413,417],{"className":414,"code":415,"language":416,"meta":51,"style":51},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"$schema\": \"https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json\",\n  \"extends\": [\"config:recommended\", \"group:allNonMajor\", \":semanticCommitTypeAll(chore)\"],\n  \"packageRules\": [\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"sonatype\u002Fnexus3\", \"clickhouse\u002Fclickhouse-server\"],\n      \"enabled\": false\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"gitlab\u002Fgitlab-runner\"],\n      \"versionCompatibility\": \"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$\"\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchUpdateTypes\": [\"minor\", \"patch\"],\n      \"automerge\": true,\n      \"schedule\": [\"before 11am on Monday\"]\n    }\n  ],\n  \"rangeStrategy\": \"bump\",\n  \"timezone\": \"Asia\u002FShanghai\"\n}\n","json",[49,418,419,428,457,501,516,522,547,579,594,600,605,626,648,668,673,678,699,731,748,772,778,784,805,824],{"__ignoreMap":51},[420,421,424],"span",{"class":422,"line":423},"line",1,[420,425,427],{"class":426},"sP7_E","{\n",[420,429,430,434,438,441,444,448,452,454],{"class":422,"line":59},[420,431,433],{"class":432},"s39Yj","  \"",[420,435,437],{"class":436},"sseR_","$schema",[420,439,440],{"class":432},"\"",[420,442,443],{"class":426},":",[420,445,447],{"class":446},"sjJ54"," \"",[420,449,451],{"class":450},"s_sjI","https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json",[420,453,440],{"class":446},[420,455,456],{"class":426},",\n",[420,458,460,462,465,467,469,472,474,477,479,482,484,487,489,491,493,496,498],{"class":422,"line":459},3,[420,461,433],{"class":432},[420,463,464],{"class":436},"extends",[420,466,440],{"class":432},[420,468,443],{"class":426},[420,470,471],{"class":426}," [",[420,473,440],{"class":446},[420,475,476],{"class":450},"config:recommended",[420,478,440],{"class":446},[420,480,481],{"class":426},",",[420,483,447],{"class":446},[420,485,486],{"class":450},"group:allNonMajor",[420,488,440],{"class":446},[420,490,481],{"class":426},[420,492,447],{"class":446},[420,494,495],{"class":450},":semanticCommitTypeAll(chore)",[420,497,440],{"class":446},[420,499,500],{"class":426},"],\n",[420,502,504,506,509,511,513],{"class":422,"line":503},4,[420,505,433],{"class":432},[420,507,508],{"class":436},"packageRules",[420,510,440],{"class":432},[420,512,443],{"class":426},[420,514,515],{"class":426}," [\n",[420,517,519],{"class":422,"line":518},5,[420,520,521],{"class":426},"    {\n",[420,523,525,528,532,534,536,538,540,543,545],{"class":422,"line":524},6,[420,526,527],{"class":432},"      \"",[420,529,531],{"class":530},"sZMiF","matchManagers",[420,533,440],{"class":432},[420,535,443],{"class":426},[420,537,471],{"class":426},[420,539,440],{"class":446},[420,541,542],{"class":450},"docker-compose",[420,544,440],{"class":446},[420,546,500],{"class":426},[420,548,550,552,555,557,559,561,563,566,568,570,572,575,577],{"class":422,"line":549},7,[420,551,527],{"class":432},[420,553,554],{"class":530},"matchPackageNames",[420,556,440],{"class":432},[420,558,443],{"class":426},[420,560,471],{"class":426},[420,562,440],{"class":446},[420,564,565],{"class":450},"sonatype\u002Fnexus3",[420,567,440],{"class":446},[420,569,481],{"class":426},[420,571,447],{"class":446},[420,573,574],{"class":450},"clickhouse\u002Fclickhouse-server",[420,576,440],{"class":446},[420,578,500],{"class":426},[420,580,582,584,587,589,591],{"class":422,"line":581},8,[420,583,527],{"class":432},[420,585,586],{"class":530},"enabled",[420,588,440],{"class":432},[420,590,443],{"class":426},[420,592,593],{"class":432}," false\n",[420,595,597],{"class":422,"line":596},9,[420,598,599],{"class":426},"    },\n",[420,601,603],{"class":422,"line":602},10,[420,604,521],{"class":426},[420,606,608,610,612,614,616,618,620,622,624],{"class":422,"line":607},11,[420,609,527],{"class":432},[420,611,531],{"class":530},[420,613,440],{"class":432},[420,615,443],{"class":426},[420,617,471],{"class":426},[420,619,440],{"class":446},[420,621,542],{"class":450},[420,623,440],{"class":446},[420,625,500],{"class":426},[420,627,629,631,633,635,637,639,641,644,646],{"class":422,"line":628},12,[420,630,527],{"class":432},[420,632,554],{"class":530},[420,634,440],{"class":432},[420,636,443],{"class":426},[420,638,471],{"class":426},[420,640,440],{"class":446},[420,642,643],{"class":450},"gitlab\u002Fgitlab-runner",[420,645,440],{"class":446},[420,647,500],{"class":426},[420,649,651,653,656,658,660,662,665],{"class":422,"line":650},13,[420,652,527],{"class":432},[420,654,655],{"class":530},"versionCompatibility",[420,657,440],{"class":432},[420,659,443],{"class":426},[420,661,447],{"class":446},[420,663,664],{"class":450},"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$",[420,666,667],{"class":446},"\"\n",[420,669,671],{"class":422,"line":670},14,[420,672,599],{"class":426},[420,674,676],{"class":422,"line":675},15,[420,677,521],{"class":426},[420,679,681,683,685,687,689,691,693,695,697],{"class":422,"line":680},16,[420,682,527],{"class":432},[420,684,531],{"class":530},[420,686,440],{"class":432},[420,688,443],{"class":426},[420,690,471],{"class":426},[420,692,440],{"class":446},[420,694,542],{"class":450},[420,696,440],{"class":446},[420,698,500],{"class":426},[420,700,702,704,707,709,711,713,715,718,720,722,724,727,729],{"class":422,"line":701},17,[420,703,527],{"class":432},[420,705,706],{"class":530},"matchUpdateTypes",[420,708,440],{"class":432},[420,710,443],{"class":426},[420,712,471],{"class":426},[420,714,440],{"class":446},[420,716,717],{"class":450},"minor",[420,719,440],{"class":446},[420,721,481],{"class":426},[420,723,447],{"class":446},[420,725,726],{"class":450},"patch",[420,728,440],{"class":446},[420,730,500],{"class":426},[420,732,734,736,739,741,743,746],{"class":422,"line":733},18,[420,735,527],{"class":432},[420,737,738],{"class":530},"automerge",[420,740,440],{"class":432},[420,742,443],{"class":426},[420,744,745],{"class":432}," true",[420,747,456],{"class":426},[420,749,751,753,756,758,760,762,764,767,769],{"class":422,"line":750},19,[420,752,527],{"class":432},[420,754,755],{"class":530},"schedule",[420,757,440],{"class":432},[420,759,443],{"class":426},[420,761,471],{"class":426},[420,763,440],{"class":446},[420,765,766],{"class":450},"before 11am on Monday",[420,768,440],{"class":446},[420,770,771],{"class":426},"]\n",[420,773,775],{"class":422,"line":774},20,[420,776,777],{"class":426},"    }\n",[420,779,781],{"class":422,"line":780},21,[420,782,783],{"class":426},"  ],\n",[420,785,787,789,792,794,796,798,801,803],{"class":422,"line":786},22,[420,788,433],{"class":432},[420,790,791],{"class":436},"rangeStrategy",[420,793,440],{"class":432},[420,795,443],{"class":426},[420,797,447],{"class":446},[420,799,800],{"class":450},"bump",[420,802,440],{"class":446},[420,804,456],{"class":426},[420,806,808,810,813,815,817,819,822],{"class":422,"line":807},23,[420,809,433],{"class":432},[420,811,812],{"class":436},"timezone",[420,814,440],{"class":432},[420,816,443],{"class":426},[420,818,447],{"class":446},[420,820,821],{"class":450},"Asia\u002FShanghai",[420,823,667],{"class":446},[420,825,827],{"class":422,"line":826},24,[420,828,829],{"class":426},"}\n",[28,831,832,833,390,835,837,838,840],{},"里面有一些自定义的规则，比如禁用了 ",[49,834,565],{},[49,836,574],{}," 的自动更新，以及适配了 ",[49,839,643],{}," 的不规则的版本号。",[28,842,843,411],{},[49,844,845],{},".gitlab-ci.yml",[42,847,851],{"className":848,"code":849,"language":850,"meta":51,"style":51},"language-yaml shiki shiki-themes material-theme-lighter github-light github-dark","stages:\n  - pass\n  - deploy\n\npass:\n  stage: pass\n  script:\n    - echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n  except:\n    - main\n\ndeploy:\n  stage: deploy\n  before_script:\n    - eval $(ssh-agent -s)\n    - echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n    - mkdir -p ~\u002F.ssh\n    - echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n\n  script:\n    - MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n    - DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n    - |\n      for FILE in $DELETED_FILES; do\n        echo \"文件移除 $FILE\"\n        # 如果文件是 compose.yaml，执行 docker compose down\n        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n          # fi\n        fi\n\n        # 删除对应文件\n        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n          echo \"从 Synology 删除文件: $FILE_PATH\"\n          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        echo \"文件变更 $FILE\"\n        if [ -e \"$FILE\" ]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n            scp -O $FILE root@hk.aliyun:$DEST_DIR\n          fi\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        if [ -e \"$FILE\" ]; then\n          # 如果文件是 compose.yaml，执行 docker compose up -d\n          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n              echo \"跳过部署 vm-rocky-01:renovate\"\n              continue\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n            # fi\n\n            echo \"容器重新部署完成\"\n            continue\n          fi\n\n          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 synology:nginx 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 gateway.aliyun:nginx\"\n            ssh root@gateway.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 hk.aliyun:nginx\"\n            ssh root@hk.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 rinetd.conf, 重新启动 rinetd\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n            echo \"重新启动 synology:rinetd 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n          fi\n\n          # 如果文件是 gitlab.rb, 重新配置 gitlab\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n            echo \"重新配置 vm-rocky-01:gitlab\"\n            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n          fi\n\n          # 如果文件是 prometheus.yml, 重新启动 prometheus\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n            echo \"重新启动 vm-rocky-01:prometheus\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n          fi\n\n          # 如果文件是 telegraf.conf, 重新启动 telegraf\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 synology:telegraf\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-01:telegraf\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-02:telegraf\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n            echo \"重新启动 vm-rocky-02:bots\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n            echo \"等待 10 秒\"\n            sleep 10\n            echo \"重新启动 synology:clash-premium\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n          fi\n\n          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n            echo \"重新启动 vm-rocky-02:artalk\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n          fi\n\n        fi\n      done\n    - echo \"部署完成\"\n  only:\n    - main\n","yaml",[49,852,853,862,870,877,882,889,898,905,913,920,927,931,938,946,953,960,967,974,981,985,991,998,1005,1013,1018,1024,1030,1036,1042,1048,1054,1060,1066,1071,1077,1083,1089,1095,1100,1105,1111,1117,1123,1129,1134,1139,1145,1151,1157,1163,1169,1175,1180,1186,1192,1198,1204,1210,1215,1220,1226,1232,1238,1244,1249,1254,1260,1266,1272,1278,1283,1288,1294,1300,1306,1312,1317,1323,1328,1334,1340,1346,1351,1356,1362,1368,1374,1379,1384,1389,1394,1400,1406,1412,1417,1422,1427,1432,1438,1444,1450,1455,1460,1466,1472,1478,1484,1490,1495,1500,1506,1512,1518,1524,1530,1535,1540,1545,1550,1555,1560,1566,1572,1578,1584,1590,1596,1600,1606,1612,1618,1624,1629,1634,1640,1646,1652,1658,1663,1668,1674,1680,1686,1692,1697,1702,1708,1714,1720,1726,1732,1737,1743,1749,1754,1759,1765,1771,1777,1783,1788,1793,1799,1805,1811,1817,1822,1827,1833,1838,1844,1850,1855,1860,1866,1872,1878,1884,1889,1894,1900,1906,1912,1918,1923,1928,1934,1940,1946,1952,1957,1962,1968,1974,1980,1986,1991,1996,2002,2008,2014,2019,2024,2030,2036,2042,2047,2052,2058,2064,2070,2076,2082,2088,2094,2100,2105,2110,2116,2122,2128,2134,2139,2144,2149,2154,2162,2170],{"__ignoreMap":51},[420,854,855,859],{"class":422,"line":423},[420,856,858],{"class":857},"sQzsp","stages",[420,860,861],{"class":426},":\n",[420,863,864,867],{"class":422,"line":59},[420,865,866],{"class":426},"  -",[420,868,869],{"class":450}," pass\n",[420,871,872,874],{"class":422,"line":459},[420,873,866],{"class":426},[420,875,876],{"class":450}," deploy\n",[420,878,879],{"class":422,"line":503},[420,880,881],{"emptyLinePlaceholder":67},"\n",[420,883,884,887],{"class":422,"line":518},[420,885,886],{"class":857},"pass",[420,888,861],{"class":426},[420,890,891,894,896],{"class":422,"line":524},[420,892,893],{"class":857},"  stage",[420,895,443],{"class":426},[420,897,869],{"class":450},[420,899,900,903],{"class":422,"line":549},[420,901,902],{"class":857},"  script",[420,904,861],{"class":426},[420,906,907,910],{"class":422,"line":581},[420,908,909],{"class":426},"    -",[420,911,912],{"class":450}," echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n",[420,914,915,918],{"class":422,"line":596},[420,916,917],{"class":857},"  except",[420,919,861],{"class":426},[420,921,922,924],{"class":422,"line":602},[420,923,909],{"class":426},[420,925,926],{"class":450}," main\n",[420,928,929],{"class":422,"line":607},[420,930,881],{"emptyLinePlaceholder":67},[420,932,933,936],{"class":422,"line":628},[420,934,935],{"class":857},"deploy",[420,937,861],{"class":426},[420,939,940,942,944],{"class":422,"line":650},[420,941,893],{"class":857},[420,943,443],{"class":426},[420,945,876],{"class":450},[420,947,948,951],{"class":422,"line":670},[420,949,950],{"class":857},"  before_script",[420,952,861],{"class":426},[420,954,955,957],{"class":422,"line":675},[420,956,909],{"class":426},[420,958,959],{"class":450}," eval $(ssh-agent -s)\n",[420,961,962,964],{"class":422,"line":680},[420,963,909],{"class":426},[420,965,966],{"class":450}," echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n",[420,968,969,971],{"class":422,"line":701},[420,970,909],{"class":426},[420,972,973],{"class":450}," mkdir -p ~\u002F.ssh\n",[420,975,976,978],{"class":422,"line":733},[420,977,909],{"class":426},[420,979,980],{"class":450}," echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n",[420,982,983],{"class":422,"line":750},[420,984,881],{"emptyLinePlaceholder":67},[420,986,987,989],{"class":422,"line":774},[420,988,902],{"class":857},[420,990,861],{"class":426},[420,992,993,995],{"class":422,"line":780},[420,994,909],{"class":426},[420,996,997],{"class":450}," MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n",[420,999,1000,1002],{"class":422,"line":786},[420,1001,909],{"class":426},[420,1003,1004],{"class":450}," DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n",[420,1006,1007,1009],{"class":422,"line":807},[420,1008,909],{"class":426},[420,1010,1012],{"class":1011},"sVHd0"," |\n",[420,1014,1015],{"class":422,"line":826},[420,1016,1017],{"class":450},"      for FILE in $DELETED_FILES; do\n",[420,1019,1021],{"class":422,"line":1020},25,[420,1022,1023],{"class":450},"        echo \"文件移除 $FILE\"\n",[420,1025,1027],{"class":422,"line":1026},26,[420,1028,1029],{"class":450},"        # 如果文件是 compose.yaml，执行 docker compose down\n",[420,1031,1033],{"class":422,"line":1032},27,[420,1034,1035],{"class":450},"        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[420,1037,1039],{"class":422,"line":1038},28,[420,1040,1041],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[420,1043,1045],{"class":422,"line":1044},29,[420,1046,1047],{"class":450},"            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[420,1049,1051],{"class":422,"line":1050},30,[420,1052,1053],{"class":450},"            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[420,1055,1057],{"class":422,"line":1056},31,[420,1058,1059],{"class":450},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n",[420,1061,1063],{"class":422,"line":1062},32,[420,1064,1065],{"class":450},"          fi\n",[420,1067,1069],{"class":422,"line":1068},33,[420,1070,881],{"emptyLinePlaceholder":67},[420,1072,1074],{"class":422,"line":1073},34,[420,1075,1076],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[420,1078,1080],{"class":422,"line":1079},35,[420,1081,1082],{"class":450},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[420,1084,1086],{"class":422,"line":1085},36,[420,1087,1088],{"class":450},"            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[420,1090,1092],{"class":422,"line":1091},37,[420,1093,1094],{"class":450},"            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n",[420,1096,1098],{"class":422,"line":1097},38,[420,1099,1065],{"class":450},[420,1101,1103],{"class":422,"line":1102},39,[420,1104,881],{"emptyLinePlaceholder":67},[420,1106,1108],{"class":422,"line":1107},40,[420,1109,1110],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[420,1112,1114],{"class":422,"line":1113},41,[420,1115,1116],{"class":450},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[420,1118,1120],{"class":422,"line":1119},42,[420,1121,1122],{"class":450},"            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[420,1124,1126],{"class":422,"line":1125},43,[420,1127,1128],{"class":450},"            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n",[420,1130,1132],{"class":422,"line":1131},44,[420,1133,1065],{"class":450},[420,1135,1137],{"class":422,"line":1136},45,[420,1138,881],{"emptyLinePlaceholder":67},[420,1140,1142],{"class":422,"line":1141},46,[420,1143,1144],{"class":450},"          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[420,1146,1148],{"class":422,"line":1147},47,[420,1149,1150],{"class":450},"          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[420,1152,1154],{"class":422,"line":1153},48,[420,1155,1156],{"class":450},"          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[420,1158,1160],{"class":422,"line":1159},49,[420,1161,1162],{"class":450},"          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n",[420,1164,1166],{"class":422,"line":1165},50,[420,1167,1168],{"class":450},"          # fi\n",[420,1170,1172],{"class":422,"line":1171},51,[420,1173,1174],{"class":450},"        fi\n",[420,1176,1178],{"class":422,"line":1177},52,[420,1179,881],{"emptyLinePlaceholder":67},[420,1181,1183],{"class":422,"line":1182},53,[420,1184,1185],{"class":450},"        # 删除对应文件\n",[420,1187,1189],{"class":422,"line":1188},54,[420,1190,1191],{"class":450},"        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[420,1193,1195],{"class":422,"line":1194},55,[420,1196,1197],{"class":450},"          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n",[420,1199,1201],{"class":422,"line":1200},56,[420,1202,1203],{"class":450},"          echo \"从 Synology 删除文件: $FILE_PATH\"\n",[420,1205,1207],{"class":422,"line":1206},57,[420,1208,1209],{"class":450},"          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n",[420,1211,1213],{"class":422,"line":1212},58,[420,1214,1174],{"class":450},[420,1216,1218],{"class":422,"line":1217},59,[420,1219,881],{"emptyLinePlaceholder":67},[420,1221,1223],{"class":422,"line":1222},60,[420,1224,1225],{"class":450},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[420,1227,1229],{"class":422,"line":1228},61,[420,1230,1231],{"class":450},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n",[420,1233,1235],{"class":422,"line":1234},62,[420,1236,1237],{"class":450},"          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n",[420,1239,1241],{"class":422,"line":1240},63,[420,1242,1243],{"class":450},"          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n",[420,1245,1247],{"class":422,"line":1246},64,[420,1248,1174],{"class":450},[420,1250,1252],{"class":422,"line":1251},65,[420,1253,881],{"emptyLinePlaceholder":67},[420,1255,1257],{"class":422,"line":1256},66,[420,1258,1259],{"class":450},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[420,1261,1263],{"class":422,"line":1262},67,[420,1264,1265],{"class":450},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n",[420,1267,1269],{"class":422,"line":1268},68,[420,1270,1271],{"class":450},"          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n",[420,1273,1275],{"class":422,"line":1274},69,[420,1276,1277],{"class":450},"          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n",[420,1279,1281],{"class":422,"line":1280},70,[420,1282,1174],{"class":450},[420,1284,1286],{"class":422,"line":1285},71,[420,1287,881],{"emptyLinePlaceholder":67},[420,1289,1291],{"class":422,"line":1290},72,[420,1292,1293],{"class":450},"        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[420,1295,1297],{"class":422,"line":1296},73,[420,1298,1299],{"class":450},"          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n",[420,1301,1303],{"class":422,"line":1302},74,[420,1304,1305],{"class":450},"          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n",[420,1307,1309],{"class":422,"line":1308},75,[420,1310,1311],{"class":450},"          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n",[420,1313,1315],{"class":422,"line":1314},76,[420,1316,1174],{"class":450},[420,1318,1320],{"class":422,"line":1319},77,[420,1321,1322],{"class":450},"      done\n",[420,1324,1326],{"class":422,"line":1325},78,[420,1327,881],{"emptyLinePlaceholder":67},[420,1329,1331],{"class":422,"line":1330},79,[420,1332,1333],{"class":450},"      for FILE in $MODIFIED_FILES; do\n",[420,1335,1337],{"class":422,"line":1336},80,[420,1338,1339],{"class":450},"        echo \"文件变更 $FILE\"\n",[420,1341,1343],{"class":422,"line":1342},81,[420,1344,1345],{"class":450},"        if [ -e \"$FILE\" ]; then\n",[420,1347,1349],{"class":422,"line":1348},82,[420,1350,1041],{"class":450},[420,1352,1354],{"class":422,"line":1353},83,[420,1355,1047],{"class":450},[420,1357,1359],{"class":422,"line":1358},84,[420,1360,1361],{"class":450},"            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n",[420,1363,1365],{"class":422,"line":1364},85,[420,1366,1367],{"class":450},"            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n",[420,1369,1371],{"class":422,"line":1370},86,[420,1372,1373],{"class":450},"            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n",[420,1375,1377],{"class":422,"line":1376},87,[420,1378,1065],{"class":450},[420,1380,1382],{"class":422,"line":1381},88,[420,1383,881],{"emptyLinePlaceholder":67},[420,1385,1387],{"class":422,"line":1386},89,[420,1388,1076],{"class":450},[420,1390,1392],{"class":422,"line":1391},90,[420,1393,1082],{"class":450},[420,1395,1397],{"class":422,"line":1396},91,[420,1398,1399],{"class":450},"            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n",[420,1401,1403],{"class":422,"line":1402},92,[420,1404,1405],{"class":450},"            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n",[420,1407,1409],{"class":422,"line":1408},93,[420,1410,1411],{"class":450},"            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n",[420,1413,1415],{"class":422,"line":1414},94,[420,1416,1065],{"class":450},[420,1418,1420],{"class":422,"line":1419},95,[420,1421,881],{"emptyLinePlaceholder":67},[420,1423,1425],{"class":422,"line":1424},96,[420,1426,1110],{"class":450},[420,1428,1430],{"class":422,"line":1429},97,[420,1431,1116],{"class":450},[420,1433,1435],{"class":422,"line":1434},98,[420,1436,1437],{"class":450},"            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n",[420,1439,1441],{"class":422,"line":1440},99,[420,1442,1443],{"class":450},"            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n",[420,1445,1447],{"class":422,"line":1446},100,[420,1448,1449],{"class":450},"            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n",[420,1451,1453],{"class":422,"line":1452},101,[420,1454,1065],{"class":450},[420,1456,1458],{"class":422,"line":1457},102,[420,1459,881],{"emptyLinePlaceholder":67},[420,1461,1463],{"class":422,"line":1462},103,[420,1464,1465],{"class":450},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[420,1467,1469],{"class":422,"line":1468},104,[420,1470,1471],{"class":450},"            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[420,1473,1475],{"class":422,"line":1474},105,[420,1476,1477],{"class":450},"            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n",[420,1479,1481],{"class":422,"line":1480},106,[420,1482,1483],{"class":450},"            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n",[420,1485,1487],{"class":422,"line":1486},107,[420,1488,1489],{"class":450},"            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n",[420,1491,1493],{"class":422,"line":1492},108,[420,1494,1065],{"class":450},[420,1496,1498],{"class":422,"line":1497},109,[420,1499,881],{"emptyLinePlaceholder":67},[420,1501,1503],{"class":422,"line":1502},110,[420,1504,1505],{"class":450},"          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n",[420,1507,1509],{"class":422,"line":1508},111,[420,1510,1511],{"class":450},"            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n",[420,1513,1515],{"class":422,"line":1514},112,[420,1516,1517],{"class":450},"            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n",[420,1519,1521],{"class":422,"line":1520},113,[420,1522,1523],{"class":450},"            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n",[420,1525,1527],{"class":422,"line":1526},114,[420,1528,1529],{"class":450},"            scp -O $FILE root@hk.aliyun:$DEST_DIR\n",[420,1531,1533],{"class":422,"line":1532},115,[420,1534,1065],{"class":450},[420,1536,1538],{"class":422,"line":1537},116,[420,1539,1174],{"class":450},[420,1541,1543],{"class":422,"line":1542},117,[420,1544,1322],{"class":450},[420,1546,1548],{"class":422,"line":1547},118,[420,1549,881],{"emptyLinePlaceholder":67},[420,1551,1553],{"class":422,"line":1552},119,[420,1554,1333],{"class":450},[420,1556,1558],{"class":422,"line":1557},120,[420,1559,1345],{"class":450},[420,1561,1563],{"class":422,"line":1562},121,[420,1564,1565],{"class":450},"          # 如果文件是 compose.yaml，执行 docker compose up -d\n",[420,1567,1569],{"class":422,"line":1568},122,[420,1570,1571],{"class":450},"          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[420,1573,1575],{"class":422,"line":1574},123,[420,1576,1577],{"class":450},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n",[420,1579,1581],{"class":422,"line":1580},124,[420,1582,1583],{"class":450},"              echo \"跳过部署 vm-rocky-01:renovate\"\n",[420,1585,1587],{"class":422,"line":1586},125,[420,1588,1589],{"class":450},"              continue\n",[420,1591,1593],{"class":422,"line":1592},126,[420,1594,1595],{"class":450},"            fi\n",[420,1597,1598],{"class":422,"line":73},[420,1599,881],{"emptyLinePlaceholder":67},[420,1601,1603],{"class":422,"line":1602},128,[420,1604,1605],{"class":450},"            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[420,1607,1609],{"class":422,"line":1608},129,[420,1610,1611],{"class":450},"              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[420,1613,1615],{"class":422,"line":1614},130,[420,1616,1617],{"class":450},"              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[420,1619,1621],{"class":422,"line":1620},131,[420,1622,1623],{"class":450},"              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n",[420,1625,1627],{"class":422,"line":1626},132,[420,1628,1595],{"class":450},[420,1630,1632],{"class":422,"line":1631},133,[420,1633,881],{"emptyLinePlaceholder":67},[420,1635,1637],{"class":422,"line":1636},134,[420,1638,1639],{"class":450},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[420,1641,1643],{"class":422,"line":1642},135,[420,1644,1645],{"class":450},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[420,1647,1649],{"class":422,"line":1648},136,[420,1650,1651],{"class":450},"              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[420,1653,1655],{"class":422,"line":1654},137,[420,1656,1657],{"class":450},"              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n",[420,1659,1661],{"class":422,"line":1660},138,[420,1662,1595],{"class":450},[420,1664,1666],{"class":422,"line":1665},139,[420,1667,881],{"emptyLinePlaceholder":67},[420,1669,1671],{"class":422,"line":1670},140,[420,1672,1673],{"class":450},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[420,1675,1677],{"class":422,"line":1676},141,[420,1678,1679],{"class":450},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[420,1681,1683],{"class":422,"line":1682},142,[420,1684,1685],{"class":450},"              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[420,1687,1689],{"class":422,"line":1688},143,[420,1690,1691],{"class":450},"              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n",[420,1693,1695],{"class":422,"line":1694},144,[420,1696,1595],{"class":450},[420,1698,1700],{"class":422,"line":1699},145,[420,1701,881],{"emptyLinePlaceholder":67},[420,1703,1705],{"class":422,"line":1704},146,[420,1706,1707],{"class":450},"            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[420,1709,1711],{"class":422,"line":1710},147,[420,1712,1713],{"class":450},"            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[420,1715,1717],{"class":422,"line":1716},148,[420,1718,1719],{"class":450},"            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[420,1721,1723],{"class":422,"line":1722},149,[420,1724,1725],{"class":450},"            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n",[420,1727,1729],{"class":422,"line":1728},150,[420,1730,1731],{"class":450},"            # fi\n",[420,1733,1735],{"class":422,"line":1734},151,[420,1736,881],{"emptyLinePlaceholder":67},[420,1738,1740],{"class":422,"line":1739},152,[420,1741,1742],{"class":450},"            echo \"容器重新部署完成\"\n",[420,1744,1746],{"class":422,"line":1745},153,[420,1747,1748],{"class":450},"            continue\n",[420,1750,1752],{"class":422,"line":1751},154,[420,1753,1065],{"class":450},[420,1755,1757],{"class":422,"line":1756},155,[420,1758,881],{"emptyLinePlaceholder":67},[420,1760,1762],{"class":422,"line":1761},156,[420,1763,1764],{"class":450},"          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n",[420,1766,1768],{"class":422,"line":1767},157,[420,1769,1770],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n",[420,1772,1774],{"class":422,"line":1773},158,[420,1775,1776],{"class":450},"            echo \"重新启动 synology:nginx 服务\"\n",[420,1778,1780],{"class":422,"line":1779},159,[420,1781,1782],{"class":450},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n",[420,1784,1786],{"class":422,"line":1785},160,[420,1787,1065],{"class":450},[420,1789,1791],{"class":422,"line":1790},161,[420,1792,881],{"emptyLinePlaceholder":67},[420,1794,1796],{"class":422,"line":1795},162,[420,1797,1798],{"class":450},"          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n",[420,1800,1802],{"class":422,"line":1801},163,[420,1803,1804],{"class":450},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n",[420,1806,1808],{"class":422,"line":1807},164,[420,1809,1810],{"class":450},"            echo \"重新启动 gateway.aliyun:nginx\"\n",[420,1812,1814],{"class":422,"line":1813},165,[420,1815,1816],{"class":450},"            ssh root@gateway.aliyun \"nginx -s reload\"\n",[420,1818,1820],{"class":422,"line":1819},166,[420,1821,1065],{"class":450},[420,1823,1825],{"class":422,"line":1824},167,[420,1826,881],{"emptyLinePlaceholder":67},[420,1828,1830],{"class":422,"line":1829},168,[420,1831,1832],{"class":450},"          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n",[420,1834,1836],{"class":422,"line":1835},169,[420,1837,1505],{"class":450},[420,1839,1841],{"class":422,"line":1840},170,[420,1842,1843],{"class":450},"            echo \"重新启动 hk.aliyun:nginx\"\n",[420,1845,1847],{"class":422,"line":1846},171,[420,1848,1849],{"class":450},"            ssh root@hk.aliyun \"nginx -s reload\"\n",[420,1851,1853],{"class":422,"line":1852},172,[420,1854,1065],{"class":450},[420,1856,1858],{"class":422,"line":1857},173,[420,1859,881],{"emptyLinePlaceholder":67},[420,1861,1863],{"class":422,"line":1862},174,[420,1864,1865],{"class":450},"          # 如果文件是 rinetd.conf, 重新启动 rinetd\n",[420,1867,1869],{"class":422,"line":1868},175,[420,1870,1871],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n",[420,1873,1875],{"class":422,"line":1874},176,[420,1876,1877],{"class":450},"            echo \"重新启动 synology:rinetd 服务\"\n",[420,1879,1881],{"class":422,"line":1880},177,[420,1882,1883],{"class":450},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n",[420,1885,1887],{"class":422,"line":1886},178,[420,1888,1065],{"class":450},[420,1890,1892],{"class":422,"line":1891},179,[420,1893,881],{"emptyLinePlaceholder":67},[420,1895,1897],{"class":422,"line":1896},180,[420,1898,1899],{"class":450},"          # 如果文件是 gitlab.rb, 重新配置 gitlab\n",[420,1901,1903],{"class":422,"line":1902},181,[420,1904,1905],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n",[420,1907,1909],{"class":422,"line":1908},182,[420,1910,1911],{"class":450},"            echo \"重新配置 vm-rocky-01:gitlab\"\n",[420,1913,1915],{"class":422,"line":1914},183,[420,1916,1917],{"class":450},"            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n",[420,1919,1921],{"class":422,"line":1920},184,[420,1922,1065],{"class":450},[420,1924,1926],{"class":422,"line":1925},185,[420,1927,881],{"emptyLinePlaceholder":67},[420,1929,1931],{"class":422,"line":1930},186,[420,1932,1933],{"class":450},"          # 如果文件是 prometheus.yml, 重新启动 prometheus\n",[420,1935,1937],{"class":422,"line":1936},187,[420,1938,1939],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n",[420,1941,1943],{"class":422,"line":1942},188,[420,1944,1945],{"class":450},"            echo \"重新启动 vm-rocky-01:prometheus\"\n",[420,1947,1949],{"class":422,"line":1948},189,[420,1950,1951],{"class":450},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n",[420,1953,1955],{"class":422,"line":1954},190,[420,1956,1065],{"class":450},[420,1958,1960],{"class":422,"line":1959},191,[420,1961,881],{"emptyLinePlaceholder":67},[420,1963,1965],{"class":422,"line":1964},192,[420,1966,1967],{"class":450},"          # 如果文件是 telegraf.conf, 重新启动 telegraf\n",[420,1969,1971],{"class":422,"line":1970},193,[420,1972,1973],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[420,1975,1977],{"class":422,"line":1976},194,[420,1978,1979],{"class":450},"            echo \"重新启动 synology:telegraf\"\n",[420,1981,1983],{"class":422,"line":1982},195,[420,1984,1985],{"class":450},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n",[420,1987,1989],{"class":422,"line":1988},196,[420,1990,1065],{"class":450},[420,1992,1994],{"class":422,"line":1993},197,[420,1995,881],{"emptyLinePlaceholder":67},[420,1997,1999],{"class":422,"line":1998},198,[420,2000,2001],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[420,2003,2005],{"class":422,"line":2004},199,[420,2006,2007],{"class":450},"            echo \"重新启动 vm-rocky-01:telegraf\"\n",[420,2009,2011],{"class":422,"line":2010},200,[420,2012,2013],{"class":450},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[420,2015,2017],{"class":422,"line":2016},201,[420,2018,1065],{"class":450},[420,2020,2022],{"class":422,"line":2021},202,[420,2023,881],{"emptyLinePlaceholder":67},[420,2025,2027],{"class":422,"line":2026},203,[420,2028,2029],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[420,2031,2033],{"class":422,"line":2032},204,[420,2034,2035],{"class":450},"            echo \"重新启动 vm-rocky-02:telegraf\"\n",[420,2037,2039],{"class":422,"line":2038},205,[420,2040,2041],{"class":450},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[420,2043,2045],{"class":422,"line":2044},206,[420,2046,1065],{"class":450},[420,2048,2050],{"class":422,"line":2049},207,[420,2051,881],{"emptyLinePlaceholder":67},[420,2053,2055],{"class":422,"line":2054},208,[420,2056,2057],{"class":450},"          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n",[420,2059,2061],{"class":422,"line":2060},209,[420,2062,2063],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n",[420,2065,2067],{"class":422,"line":2066},210,[420,2068,2069],{"class":450},"            echo \"重新启动 vm-rocky-02:bots\"\n",[420,2071,2073],{"class":422,"line":2072},211,[420,2074,2075],{"class":450},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n",[420,2077,2079],{"class":422,"line":2078},212,[420,2080,2081],{"class":450},"            echo \"等待 10 秒\"\n",[420,2083,2085],{"class":422,"line":2084},213,[420,2086,2087],{"class":450},"            sleep 10\n",[420,2089,2091],{"class":422,"line":2090},214,[420,2092,2093],{"class":450},"            echo \"重新启动 synology:clash-premium\"\n",[420,2095,2097],{"class":422,"line":2096},215,[420,2098,2099],{"class":450},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n",[420,2101,2103],{"class":422,"line":2102},216,[420,2104,1065],{"class":450},[420,2106,2108],{"class":422,"line":2107},217,[420,2109,881],{"emptyLinePlaceholder":67},[420,2111,2113],{"class":422,"line":2112},218,[420,2114,2115],{"class":450},"          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n",[420,2117,2119],{"class":422,"line":2118},219,[420,2120,2121],{"class":450},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n",[420,2123,2125],{"class":422,"line":2124},220,[420,2126,2127],{"class":450},"            echo \"重新启动 vm-rocky-02:artalk\"\n",[420,2129,2131],{"class":422,"line":2130},221,[420,2132,2133],{"class":450},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n",[420,2135,2137],{"class":422,"line":2136},222,[420,2138,1065],{"class":450},[420,2140,2142],{"class":422,"line":2141},223,[420,2143,881],{"emptyLinePlaceholder":67},[420,2145,2147],{"class":422,"line":2146},224,[420,2148,1174],{"class":450},[420,2150,2152],{"class":422,"line":2151},225,[420,2153,1322],{"class":450},[420,2155,2157,2159],{"class":422,"line":2156},226,[420,2158,909],{"class":426},[420,2160,2161],{"class":450}," echo \"部署完成\"\n",[420,2163,2165,2168],{"class":422,"line":2164},227,[420,2166,2167],{"class":857},"  only",[420,2169,861],{"class":426},[420,2171,2173,2175],{"class":422,"line":2172},228,[420,2174,909],{"class":426},[420,2176,926],{"class":450},[28,2178,2179,2180,390,2183,2186],{},"其中，",[49,2181,2182],{},"SSH_KNOWN_HOSTS",[49,2184,2185],{},"SSH_PRIVATE_KEY"," 存储在 GitLab CI\u002FCD 的变量中，用于 SSH 登录到对应的宿主机上。",[28,2188,2189,2190,2193,2194,2197,2198,2200,2201,2204,2205,2207],{},"当我需要改某个容器的配置文件时，例如当我需要修改 ",[49,2191,2192],{},"synology"," 上的 ",[49,2195,2196],{},"nginx"," 配置，我只需要去 ",[49,2199,367],{}," 项目中修改 ",[49,2202,2203],{},"homelab\u002Fsynology\u002Fnginx\u002Fnginx.conf"," 文件，然后 commit，push，等待几秒钟，GitLab CI 就会自动帮我完成配置文件的更新以及 ",[49,2206,2196],{}," 的重启。",[28,2209,2210],{},"每周一，renovate 会自动发起并合并更新容器镜像版本的 MR，然后 GitLab CI 会自动帮我更新容器。",[28,2212,2213],{},"对了，GitLab 、GitLab Runner、Renovate 也都是通过容器私有化部署在 HomeLab 中的，这些就不赘述。",[2215,2216,2217],"style",{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":51,"searchDepth":59,"depth":59,"links":2219},[2220,2221],{"id":331,"depth":59,"text":331},{"id":355,"depth":59,"text":355},"2024-12-12",{},"\u002Fposts\u002F2024\u002Fmanaging-containers-in-my-homelab",{"text":2226,"minutes":2227,"time":2228,"words":2229},"11 min read",10.24,614400,2048,{"title":320,"description":325},{"loc":2224},"posts\u002F2024\u002F20241212.managing-containers-in-my-homelab",[314,2234,80,2235],"DevOps","Docker","sSWijwbQSH5iLEufQ0ZZuj2tXgOjghNQbswIr9u7b4A",{"id":2238,"title":2239,"body":2240,"class":61,"cover":62,"coverSize":61,"date":2301,"description":2302,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":2303,"navigation":67,"path":2304,"readingTime":2305,"seo":2308,"sitemap":2309,"stem":2310,"tags":2311,"time":61,"weather":61,"__hash__":2312},"posts\u002Fposts\u002F2024\u002F20240314.how-to-delete-web-service-of-synology-dsm-7-2.md","群晖 DSM7.2 Web Station 网页服务重复无法删除的问题",{"type":25,"value":2241,"toc":2299},[2242,2252,2255,2258,2296],[28,2243,2244,2245],{},"今天遇到一个问题，记录一下，解决方案参考：",[2246,2247,2251],"a",{"href":2248,"rel":2249},"https:\u002F\u002Fcommunity.synology.com\u002Fenu\u002Fforum\u002F1\u002Fpost\u002F161835?reply=502868",[2250],"nofollow","Cannot modify \u002F delete Web Station Web Service created by Container Manager",[28,2253,2254],{},"问题截图如下（当时忘记截图了，该截图摘自上述文档）：",[28,2256,2257],{},"解决方法：",[339,2259,2260,2263,2270,2277,2283,2290],{},[95,2261,2262],{},"ssh 登录群晖",[95,2264,2265,2266,2269],{},"执行 ",[49,2267,2268],{},"sudo synopkg stop WebStation","，停止 Web Station 服务",[95,2271,2272,2273,2276],{},"删除 ",[49,2274,2275],{},"\u002Fusr\u002Fsyno\u002Fetc\u002Fpackages\u002FWebStation\u002FService.json"," 中需要删除的服务（注意记录一下 service_id，下一步需要用到）",[95,2278,2272,2279,2282],{},[49,2280,2281],{},"\u002Fusr\u002Fsyno\u002Fetc\u002Fpackages\u002FWebStation\u002FWSResource.json"," 中上述 id 的 service（注意记录一下 mustache 的路径，下一步需要用到）",[95,2284,2285,2286,2289],{},"删除上述路径的 ",[49,2287,2288],{},".mustache"," 文件",[95,2291,2265,2292,2295],{},[49,2293,2294],{},"sudo synopkg start WebStation","，重新启动 Web Station 服务",[28,2297,2298],{},"去 DSM 中检查一下，任务完成！",{"title":51,"searchDepth":59,"depth":59,"links":2300},[],"2024-03-14","今天遇到一个问题，记录一下，解决方案参考：Cannot modify \u002F delete Web Station Web Service created by Container Manager",{},"\u002Fposts\u002F2024\u002Fhow-to-delete-web-service-of-synology-dsm-7-2",{"text":70,"minutes":2306,"time":2307,"words":1716},0.74,44400,{"title":2239,"description":2302},{"loc":2304},"posts\u002F2024\u002F20240314.how-to-delete-web-service-of-synology-dsm-7-2",[78,79,80],"j3Uobti_GjqbmL459aIyYDUQgE8rCCAdZR6XBtThmio",{"id":2314,"title":2315,"body":2316,"class":61,"cover":301,"coverSize":61,"date":2606,"description":2607,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":2608,"navigation":67,"path":2609,"readingTime":2610,"seo":2615,"sitemap":2616,"stem":2617,"tags":2618,"time":61,"weather":61,"__hash__":2619},"posts\u002Fposts\u002F2021\u002F20210108.synology-letsencrypt-multiple-domain-cert-configuration.md","群晖 Let's Encrypt 配置多个泛域名 SSL 证书自动更新",{"type":25,"value":2317,"toc":2604},[2318,2330,2357,2360,2366,2407,2413,2588,2598,2601],[28,2319,2320,2321,2326,2327,2329],{},"之前一直用的 ",[2246,2322,2325],{"href":2323,"rel":2324},"https:\u002F\u002Fgithub.com\u002Fandyzhshg\u002Fsyno-acme",[2250],"syno-acme"," 配合群晖的计划任务实现泛域名 SSL 证书的更新，但是最近想切换域名，但是又要保持原有域名一段时间可用。",[49,2328,2325],{}," 的方案只支持默认证书的配置，群晖上多个证书的配置确实比较麻烦，几年前也折腾过。",[28,2331,2332,2333,2335,2336,2341,2342,2344,2345,2350,2351,2356],{},"不过调研了下发现，Let's Encrypt 支持将多个域名绑定到同一个证书里，于是找了下解决方案，果然有位兄弟基于 ",[49,2334,2325],{}," 做了些",[2246,2337,2340],{"href":2338,"rel":2339},"https:\u002F\u002F10001blog.xslinc.com\u002F?p=89",[2250],"修改","，支持多个域名。不过这位兄弟是 Hard Code 的，不够通用化，于是对 ",[49,2343,2325],{}," 做了些改进，并提交了 ",[2246,2346,2349],{"href":2347,"rel":2348},"https:\u002F\u002Fgithub.com\u002Fandyzhshg\u002Fsyno-acme\u002Fpull\u002F58",[2250],"Pull request","，希望对大家有帮助，",[2246,2352,2355],{"href":2353,"rel":2354},"https:\u002F\u002Fgithub.com\u002FHADB\u002Fsyno-acme",[2250],"Fork 仓库","。",[28,2358,2359],{},"主要修改内容：",[28,2361,2362,2363,411],{},"配置时可通过逗号分隔多个域名，",[49,2364,2365],{},"config",[42,2367,2371],{"className":2368,"code":2369,"language":2370,"meta":51,"style":51},"language-shell shiki shiki-themes material-theme-lighter github-light github-dark","# 你域名，如 baidu.com sina.com.cn 等，多个域名之间逗号分隔，支持泛域名\nexport DOMAIN=your_domain1,*.your_domain1,your_domain2,*.your_domain2\n","shell",[49,2372,2373,2379],{"__ignoreMap":51},[420,2374,2375],{"class":422,"line":423},[420,2376,2378],{"class":2377},"sutJx","# 你域名，如 baidu.com sina.com.cn 等，多个域名之间逗号分隔，支持泛域名\n",[420,2380,2381,2385,2389,2393,2396,2399,2402,2404],{"class":422,"line":59},[420,2382,2384],{"class":2383},"sbsja","export",[420,2386,2388],{"class":2387},"su5hD"," DOMAIN",[420,2390,2392],{"class":2391},"smGrS","=",[420,2394,2395],{"class":2387},"your_domain1,",[420,2397,2398],{"class":2391},"*",[420,2400,2401],{"class":2387},".your_domain1,your_domain2,",[420,2403,2398],{"class":2391},[420,2405,2406],{"class":2387},".your_domain2\n",[28,2408,2409,2412],{},[49,2410,2411],{},"cert-up.sh"," 主要修改了如下的地方：",[42,2414,2416],{"className":2368,"code":2415,"language":2370,"meta":51,"style":51},"for d in ${DOMAIN\u002F\u002F,\u002F }\ndo\n  domain_params=\"${domain_params} -d ${d}\"\ndone\n${ACME_BIN_PATH}\u002Facme.sh --force --log --issue --dns ${DNS} --dnssleep ${DNS_SLEEP} ${domain_params}\n${ACME_BIN_PATH}\u002Facme.sh --force --installcert ${domain_params} \\\n  --certpath ${CRT_PATH}\u002Fcert.pem \\\n  --key-file ${CRT_PATH}\u002Fprivkey.pem \\\n  --fullchain-file ${CRT_PATH}\u002Ffullchain.pem\n",[49,2417,2418,2445,2450,2478,2483,2518,2539,2557,2574],{"__ignoreMap":51},[420,2419,2420,2423,2426,2429,2432,2435,2438,2440,2442],{"class":422,"line":423},[420,2421,2422],{"class":1011},"for",[420,2424,2425],{"class":2387}," d ",[420,2427,2428],{"class":1011},"in",[420,2430,2431],{"class":426}," ${",[420,2433,2434],{"class":2387},"DOMAIN",[420,2436,2437],{"class":2391},"\u002F\u002F",[420,2439,481],{"class":2387},[420,2441,6],{"class":2391},[420,2443,2444],{"class":426}," }\n",[420,2446,2447],{"class":422,"line":59},[420,2448,2449],{"class":1011},"do\n",[420,2451,2452,2455,2457,2460,2463,2466,2469,2472,2475],{"class":422,"line":459},[420,2453,2454],{"class":2387},"  domain_params",[420,2456,2392],{"class":2391},[420,2458,2459],{"class":446},"\"${",[420,2461,2462],{"class":2387},"domain_params",[420,2464,2465],{"class":446},"}",[420,2467,2468],{"class":450}," -d ",[420,2470,2471],{"class":446},"${",[420,2473,2474],{"class":2387},"d",[420,2476,2477],{"class":446},"}\"\n",[420,2479,2480],{"class":422,"line":503},[420,2481,2482],{"class":1011},"done\n",[420,2484,2485,2487,2490,2492,2495,2497,2500,2502,2505,2507,2510,2512,2514,2516],{"class":422,"line":518},[420,2486,2471],{"class":426},[420,2488,2489],{"class":2387},"ACME_BIN_PATH",[420,2491,2465],{"class":426},[420,2493,2494],{"class":2387},"\u002Facme.sh --force --log --issue --dns ",[420,2496,2471],{"class":426},[420,2498,2499],{"class":2387},"DNS",[420,2501,2465],{"class":426},[420,2503,2504],{"class":2387}," --dnssleep ",[420,2506,2471],{"class":426},[420,2508,2509],{"class":2387},"DNS_SLEEP",[420,2511,2465],{"class":426},[420,2513,2431],{"class":426},[420,2515,2462],{"class":2387},[420,2517,829],{"class":426},[420,2519,2520,2522,2524,2526,2529,2531,2533,2535],{"class":422,"line":524},[420,2521,2471],{"class":426},[420,2523,2489],{"class":2387},[420,2525,2465],{"class":426},[420,2527,2528],{"class":2387},"\u002Facme.sh --force --installcert ",[420,2530,2471],{"class":426},[420,2532,2462],{"class":2387},[420,2534,2465],{"class":426},[420,2536,2538],{"class":2537},"s_hVV"," \\\n",[420,2540,2541,2545,2547,2550,2552,2555],{"class":422,"line":549},[420,2542,2544],{"class":2543},"sbgvK","  --certpath",[420,2546,2431],{"class":426},[420,2548,2549],{"class":2387},"CRT_PATH",[420,2551,2465],{"class":426},[420,2553,2554],{"class":450},"\u002Fcert.pem",[420,2556,2538],{"class":2537},[420,2558,2559,2563,2565,2567,2569,2572],{"class":422,"line":581},[420,2560,2562],{"class":2561},"stzsN","  --key-file",[420,2564,2431],{"class":426},[420,2566,2549],{"class":2387},[420,2568,2465],{"class":426},[420,2570,2571],{"class":450},"\u002Fprivkey.pem",[420,2573,2538],{"class":2537},[420,2575,2576,2579,2581,2583,2585],{"class":422,"line":596},[420,2577,2578],{"class":2561},"  --fullchain-file",[420,2580,2431],{"class":426},[420,2582,2549],{"class":2387},[420,2584,2465],{"class":426},[420,2586,2587],{"class":450},"\u002Ffullchain.pem\n",[28,2589,2590,2591,2593,2594,2597],{},"通过逗号分隔 ",[49,2592,2434],{}," 中的多个域名，并循环拼接多个 ",[49,2595,2596],{},"-d"," 参数即可。",[28,2599,2600],{},"这么修改后，群晖就可以愉快的支持多个主域名的 SSL 证书啦，爽！",[2215,2602,2603],{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":51,"searchDepth":59,"depth":59,"links":2605},[],"2021-01-08","之前一直用的 syno-acme 配合群晖的计划任务实现泛域名 SSL 证书的更新，但是最近想切换域名，但是又要保持原有域名一段时间可用。syno-acme 的方案只支持默认证书的配置，群晖上多个证书的配置确实比较麻烦，几年前也折腾过。",{},"\u002Fposts\u002F2021\u002Fsynology-letsencrypt-multiple-domain-cert-configuration",{"text":2611,"minutes":2612,"time":2613,"words":2614},"2 min read",1.595,95700,319,{"title":2315,"description":2607},{"loc":2609},"posts\u002F2021\u002F20210108.synology-letsencrypt-multiple-domain-cert-configuration",[314,79,78,80],"9T15Ab9v8M2cX6qAsb3w1Oqj78Mu9pxorqz3fCUWoKo",{"id":2621,"title":2622,"body":2623,"class":61,"cover":61,"coverSize":61,"date":2648,"description":2627,"draft":64,"extension":65,"hideComments":64,"location":61,"meta":2649,"navigation":67,"path":2650,"readingTime":2651,"seo":2655,"sitemap":2656,"stem":2657,"tags":2658,"time":61,"weather":61,"__hash__":2660},"posts\u002Fposts\u002F2016\u002F20161121.nas.md","自建 NAS 及 DDNS",{"type":25,"value":2624,"toc":2646},[2625,2628,2631],[28,2626,2627],{},"众所周知的原因，前段时间 360 云盘也倒下了，之前大部分照片、电影资源都放在 360 云盘上。由于国内的环境，感觉第三方云盘的可靠程度还不如自己建个 NAS。前端时间研究了硬件方案，今天研究了下外网访问的方案。",[28,2629,2630],{},"固定 IP 肯定是拉不起，太贵了，国内运营商太黑心。只能通过 DDNS，但花生壳这种我也不想用，以前试用过，速度太慢。既然是程序猿，还是自己来吧。具体方案如下：",[339,2632,2633,2636,2643],{},[95,2634,2635],{},"阿里云的云解析 DNS，升级付费版，将最低 TTL 值拉到 1 秒，其余都拉成最低配置，一年 40.8 块钱，完全可以接受。",[95,2637,2638,2639,2356],{},"在自己的阿里云服务器上搭建一个小站点，用于返回来访请求的公网 IP 地址。没有外网服务器的，可以利用 ip138 的服务来做，",[2246,2640,2641],{"href":2641,"rel":2642},"http:\u002F\u002Fcity.ip138.com\u002Fip2city.asp",[2250],[95,2644,2645],{},"做一个小应用，跑在 NAS 上，每秒向步骤 2 中的站点请求获取 NAS 的外网 IP，并通过阿里云云解析 DNS 的 api 接口，更新域名的 IP 地址，并记录，如果下次请求 IP 不变则跳过，IP 变化了则更新。做好日志，运行一段时间之后看下电信的动态 ip 更换有没有规律，可以适当调整获取外网 IP 的频率。",{"title":51,"searchDepth":59,"depth":59,"links":2647},[],"2016-11-21",{},"\u002Fposts\u002F2016\u002Fnas",{"text":2611,"minutes":2652,"time":2653,"words":2654},1.785,107100,357,{"title":2622,"description":2627},{"loc":2650},"posts\u002F2016\u002F20161121.nas",[2659,80,78],"日记","o89oM2G7aaUqgSCvlsy1UKOAhrlWTSzwd823xUlPC2Q",1777580269318]