[{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/ci/cd/","section":"Tags","summary":"","title":"CI/CD","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/devops/","section":"Tags","summary":"","title":"DevOps","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/categories/github/","section":"Categories","summary":"","title":"Github","type":"categories"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/github/","section":"Tags","summary":"","title":"Github","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/github-actions/","section":"Tags","summary":"","title":"Github Actions","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/categories/vps-%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97/","section":"Categories","summary":"","title":"VPS 实战指南","type":"categories"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/posts/vps-combat/","section":"系列","summary":"","title":"VPS 实战系列","type":"posts"},{"content":"在经历了前几个阶段的“毛坯房装修”和环境搭建后，终于迎来了工程化实践的重头戏：打通 CI/CD 流水线 。\n过去写完一篇笔记，我们需要手动执行编译指令，打开 FTP 软件，再把文件生硬地拖拽到服务器上。而今天，我们将通过 GitHub Actions，实现一套极致的极客工作流：只要在本地执行 git push 把代码推送到 GitHub 的主分支，云端就会自动完成编译，并通过 SSH 悄无声息地将最新博客更新到 VPS 上 。这不仅让我们彻底告别了手动上传的繁琐，更是掌握现代互联网大厂 DevOps 核心工作流的关键一步 。\n针对咱们这台小内存 VPS，我们采用了最轻量级的方案：利用 GitHub 免费的高配置服务器进行“重体力”编译，然后直接通过 SCP 协议将纯静态网页传输到 Nginx 目录下，全程不占用 VPS 的一丁点算力。\n第一步：将服务器“钥匙”托管给 GitHub # 为了让 GitHub 机器人能够免密登录 VPS 传文件，必须将安全凭证存入仓库的密码箱（Secrets）中。\n在私有仓库的 Settings -\u0026gt; Secrets and variables -\u0026gt; Actions 中，依次添加独立变量：\nSERVER_IP：VPS 的公网 IP。 SERVER_USERNAME：登录用户名（通常为 root）。 SERVER_PORT：修改过后的高位 SSH 端口。 SERVER_SSH_KEY：本地电脑的 SSH 私钥（包含头尾的完整多行文本）。 SERVER_SSH_PASSPHRASE：（高频踩坑点！） 如果生成密钥时设置了保护密码，必须填入此项，否则机器人握手会被无情拒绝。 当然，可以为机器人专门生成一个“无密码”密钥，这样也更符合 DevOps 规范，同时下方的 deploy.yml 要将 passphrase: ${{ secrets.SERVER_SSH_PASSPHRASE }} 这一行删掉\n第二步：编写云端流水线剧本 (GitHub Actions) # 在项目根目录新建 .github/workflows/deploy.yml，为 GitHub 的临时服务器下达指令集：\nname: Deploy Hugo Blog to VPS on: push: branches: - main # 监听 main 分支的代码推送 jobs: build-and-deploy: runs-on: ubuntu-latest steps: # 1. 拉取最新代码 (必须开启 submodules 以拉取主题) - name: Checkout Code uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 # 2. 配置编译环境 - name: Setup Hugo uses: peaceiris/actions-hugo@v3 with: hugo-version: \u0026#34;latest\u0026#34; extended: true # 3. 执行编译并压缩静态资源 - name: Build Site run: hugo --minify # 4. SCP 传输到 VPS - name: Deploy to VPS uses: appleboy/scp-action@master with: host: ${{ secrets.SERVER_IP }} username: ${{ secrets.SERVER_USERNAME }} key: ${{ secrets.SERVER_SSH_KEY }} passphrase: ${{ secrets.SERVER_SSH_PASSPHRASE }} # 密钥保护密码 port: ${{ secrets.SERVER_PORT }} source: \u0026#34;public/\u0026#34; # 编译产物目录 target: \u0026#34;/opt/blog/public\u0026#34; # VPS 上的目标存放路径 strip_components: 1 # 剥离 public 外壳，直接倾倒内部文件 第三步：Nginx 容器的最终接应 # 文件顺利抵达 VPS 后，还需要让前端的 Nginx 准确找到它们。\n在部署 Nginx 的 docker-compose.yml 中，将宿主机的接收目录挂载到容器内部：\nvolumes: # 将 GitHub Actions 传过来的静态文件，挂载到 Nginx 默认读取目录 - /opt/blog/public:/usr/share/nginx/html 并在映射的 nginx.conf 中，将根目录精确指向容器内的挂载点：\nlocation / { root /usr/share/nginx/html; # 必须是容器内的绝对路径 index index.html index.htm; } 执行 docker compose down 与 docker compose up -d 重启容器，大功告成！\n🛠️ 避坑指南 (Troubleshooting) # 在打通这条流水线的过程中，我踩过了几个极其经典的工程化“大坑”，特此记录：\n私钥的密码保护门槛：报错 ssh: this private key is passphrase protected。就是生成密钥时设置了 Passphrase，机器人将无法输入。必须在流水线的 scp 插件参数中显式传递 passphrase 字段。 连字符问题：在 VPS 终端执行重启时报错 Command 'docker-compose' not found。现代 Docker 环境（V2+）已将 compose 集成为内置插件，必须去掉中间的连字符，改用 docker compose。 Nginx 强拦截指令的遮挡：部署成功后依旧显示旧版测试页面。原因在于 nginx.conf 中遗留了 return 200 '...'; 拦截指令。Nginx 识别到该指令会直接返回文本，不再读取本地 HTML，必须将其替换为标准的 root 指令。 至此，整套自动化流程已彻底打通。把重复的劳动外包给机器，把宝贵的精力留给创作！\n","date":"2026-05-31","externalUrl":null,"permalink":"/posts/vps-combat/task-10-cicd/","section":"系列","summary":"在经历了前几个阶段的“毛坯房装修”和环境搭建后，终于迎来了工程化实践的重头戏：打通 CI/CD 流水线 。\n","title":"任务 10：打通 CI\u0026CD（持续集成与部署），全自动上云实战","type":"posts"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/posts/","section":"系列","summary":"","title":"系列","type":"posts"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"这是一次非常典型、硬核且充满戏剧性的“开发者实战之旅”。虽然在这个具体的电商爬虫项目上你选择了战略性放弃，但在短短的折腾过程中，你几乎把 Linux 运维、网络通信、Python 环境配置和爬虫反风控的经典“大坑”全踩了一遍，并且解决了其中 90% 的问题。\n为你整理的本次硬核探索总结如下：\n阶段一：目标确立与轻量级探索 (API 直连方案) # 初始目标：完成 VPS 支线任务 9，写一个自动化脚本，每天早上 8 点监控京东上一款“宏碁掠夺者 32G DDR5”内存条的价格，并邮件推送到室友邮箱。 行动与遇阻： 我们最开始采用了最轻量级的 requests + smtplib 方案。 第一只拦路虎（基础环境）：VPS 默认不带 pip3，你通过 apt install python3-pip 成功解决，掌握了基础的 Linux 包管理。 第二只拦路虎（网络风控）：代码运行时报 ConnectTimeoutError。排查发现，VPS 端是因为京东防火墙直接拦截了海外（RackNerd）IP；而在本地电脑运行报错，则是被本地的全局代理软件拦截了请求。 阶段二：重装上阵与本地捷报 (真实浏览器方案) # 策略转移：为了绕过反爬，你切换到了更高级的 DrissionPage 库，通过控制真实浏览器内核来抓取数据。 高光时刻：在本地 Windows 环境中成功跑通了完整逻辑，脚本成功打开了京东、抓到了 价格，并顺利发送出了测试邮件！ 阶段三：云端迁移与环境部分 # 行动计划：决定将本地跑通的代码、以及本地生成的京东 Cookie 登录缓存（jd_browser_data）一起打包上传到 VPS，实现免扫码登录。 接连迎战 Linux 系统限制： 报错 1 (externally-managed-environment)：Debian/Ubuntu 系统锁死了全局 pip 权限。 报错 2 (ensurepip not available)：尝试创建虚拟环境 (venv) 时，发现系统阉割了 Python 环境包。 破局：最终通过引入 --break-system-packages 参数强制安装，成功把 DrissionPage 部署到了服务器上。同时，你也掌握了如何为服务器配置无头模式 (headless) 和沙盒绕过 (--no-sandbox)。 阶段四：结束 # 最后一道防线：虽然代码在 VPS 上成功运行，但最终卡在了 未能找到价格。 根本原因：京东有完善的风控检测，我在使用本地运行脚本成功发送邮件，但将jd的登录数据简单移动到VPS的文件夹上后，VPS运行该脚本（修改为无头版）仍然失败，回到本地后，运行脚本发现原本保存的登录数据失效了，而我使用京东APP进行登录发现京东提示我账号触发风控，被迫进行人脸识别，结论：此方法行不通。 止损：使用 purge 卸载了庞大的 Chromium 内核，并用带强制参数的命令彻底清理了环境残留。 ","date":"2026-05-31","externalUrl":null,"permalink":"/posts/vps-combat/task-9-crawler-script/","section":"系列","summary":"这是一次非常典型、硬核且充满戏剧性的“开发者实战之旅”。虽然在这个具体的电商爬虫项目上你选择了战略性放弃，但在短短的折腾过程中，你几乎把 Linux 运维、网络通信、Python 环境配置和爬虫反风控的经典“大坑”全踩了一遍，并且解决了其中 90% 的问题。\n","title":"任务 9：编写并运行定时爬虫-自动化脚本（被京东拒绝）","type":"posts"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/3x-ui/","section":"Tags","summary":"","title":"3X-UI","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/github-gist/","section":"Tags","summary":"","title":"GitHub Gist","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/posts/tech-extras/","section":"系列","summary":"","title":"技术番外","type":"posts"},{"content":"在使用 3X-UI 搭建代理节点时，很多朋友可能会遇到这样一个痛点： 因为 3X-UI 的 Vision 流控（xtls-rprx-vision）是需要在具体客户端上设置的，这就导致开启了 Vision 和未开启 Vision 的节点，被强制拆分成了不同的订阅链接。 当把它们导入到代理软件（比如 V2rayN）时，软件里会显示出多个不同的订阅分组，不仅观感杂乱，后期维护起来也极其不便。\n为了解决这个问题，实现“所有节点汇聚成一个订阅链接，一次导入即可”的完美体验，这里为大家提供一套终极解决方案！无论你使用的是 V2rayN（直连版）还是 Clash（YAML 配置版），都能轻松搞定。\n方法一：适用于 V2rayN 等直连版软件 # V2rayN 等软件可以直接识别一行一个的单节点链接（如 vless://... 或 vmess://...）。我们可以借助 GitHub 的 Gist 功能来做一个属于自己的“云端订阅池”。\n🛠️ 操作步骤： # 获取所有节点链接：在你的 3X-UI 面板中，把你需要的所有节点（无论有没有 Vision 流控）的单独分享链接全部复制下来。\n创建 GitHub Gist：\n访问并登录 GitHub Gist。 在巨大的文本框中，把你刚刚复制的节点链接全部粘贴进去，保证一行一个链接。 给文件随便起个名字（比如 nodes.txt）。 保存并获取订阅链接：\n点击右下角的 Create secret gist（千万别点 Public，详见下方避坑指南）。 创建成功后，在文件内容的右上角找到并点击 Raw 按钮。 此时浏览器会跳转到一个纯文本页面，复制当前浏览器地址栏里的网址。 导入软件：打开 V2rayN，选择“订阅分组” -\u0026gt; “添加订阅”，把刚刚复制的 Raw 网址填入 URL 中即可。\n方法二：适用于 Clash 等读取 YAML 的软件 # Clash 系列软件（如 Clash Verge, Clash for Windows）不支持直接导入 vless:// 这类链接，它们需要的是结构化的 .yaml 配置文件。这时候我们就可以请出 AI 来帮我们打工了。\n🛠️ 操作步骤： # 收集节点链接：同样从 3X-UI 中复制出想要导入到Clash的单节点链接。\n让 AI 写一段YAML代码。 下面是提示词参考：\n“我这里有一些代理节点链接，请帮我把它们转换成完整的 Clash YAML 订阅配置文件格式（包含 proxies, proxy-groups, rules 等基础结构，请确保能作为一份完整的配置文件直接在 Clash 中使用）。以下是我的节点链接： [在这里粘贴你的所有节点链接]”\n使用 GitHub Gist 托管配置：\n同样前往 GitHub Gist。 将 AI 生成的那一大段 YAML 代码粘贴到文本框中。 将文件名命名为 config.yaml。 点击 Create secret gist 保存。 点击 Raw 按钮，并复制浏览器地址栏的链接。 然后就可以当作购买的梯子那般直接导入订阅了。\n避坑指南 # 操作时容易遇到各种奇怪的报错，请对照以下几点进行检查：\n1. 绝对不要把 Gist 设置为公开 (Public) # 这是最容易犯的致命错误。 你的节点链接中包含你的服务器 IP 和密码（UUID），如果点击了 Create public gist，全世界的人（包括各种爬虫脚本）都能直接连上你的服务器，你的流量很快就会被耗尽，甚至 IP 被封锁！请务必确保点击的是 Create secret gist（秘密 Gist）。\n2. 复制的是错的网址？注意要点击 Raw # 新手经常直接复制 Gist 创建完成后的那个带有代码高亮界面的网页地址。代理软件是不认识那个网页的！必须点击文件右上角的 Raw 按钮，跳转到全是纯文本（黑白字）的页面，再复制地址栏的 URL。正确的 Raw 链接通常是以 https://gist.githubusercontent.com/... 开头的。\n3. GitHub 打不开 # 国内访问 GitHub 是需要代理的。如果正在设置第一个节点，手上又没有现成的梯子，可能会陷入连 Gist 网页都打不开的尴尬局面，这时先导入单个节点用此节点访问就可以了。\n4. 订阅更新失败 # 有些地方的网络环境即使挂了代理，代理软件本身更新订阅时也可能连不上 raw.githubusercontent.com。如果软件在更新订阅时一直提示超时，可以在网上搜索“GitHub Raw 加速代理”，在 Raw 链接前面加上加速前缀即可。\n","date":"2026-05-31","externalUrl":null,"permalink":"/posts/tech-extras/3x-ui-optimize/","section":"系列","summary":"在使用 3X-UI 搭建代理节点时，很多朋友可能会遇到这样一个痛点： 因为 3X-UI 的 Vision 流控（xtls-rprx-vision）是需要在具体客户端上设置的，这就导致开启了 Vision 和未开启 Vision 的节点，被强制拆分成了不同的订阅链接。 当把它们导入到代理软件（比如 V2rayN）时，软件里会显示出多个不同的订阅分组，不仅观感杂乱，后期维护起来也极其不便。\n","title":"番外：3X-UI 节点订阅合并与优化","type":"posts"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/tags/%E7%BD%91%E7%BB%9C%E4%BB%A3%E7%90%86/","section":"Tags","summary":"","title":"网络代理","type":"tags"},{"content":"","date":"2026-05-31","externalUrl":null,"permalink":"/categories/%E7%BD%91%E7%BB%9C%E6%8A%80%E6%9C%AF/","section":"Categories","summary":"","title":"网络技术","type":"categories"},{"content":"","date":"2026-05-30","externalUrl":null,"permalink":"/categories/%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%90%E7%BB%B4/","section":"Categories","summary":"","title":"服务器运维","type":"categories"},{"content":" 🌐 核心架构设计：为什么这么做？ # 过去使用一键翻墙脚本，脚本作者通常采用“霰弹枪策略”：在一个黑盒环境里强行塞入 10 多个协议节点，将其全部打包在一个 Sing-box 内核里。这种方式看似全面，但在 1GB 内存的入门级 VPS（虚拟专用服务器）上会产生严重的弊端：大量闲置进程白白吃掉宝贵的系统资源，且黑盒脚本会死锁底层网络逻辑，导致后续更新极易崩溃或丢失配置。\n本教程采用更加优雅的“微服务解耦”架构，在服务器上并行部署两套独立运行的顶级代理核心，各司其职，互不干扰：\nXray 核心（通过 3X-UI 图形面板管理）： 运行在 TCP 协议上，部署目前最隐蔽、最抗封锁的 VLESS-Reality-Vision 方案，作为日常高频代码开发、拉取 GitHub 仓库、查阅文档的主力节点。 Native Hysteria2 核心（原生独立进程运行）： 运行在 UDP 协议上，利用极度激进的 Brutal（暴力拥塞控制算法），强行在长距离、高延迟、多丢包的劣质公网上跑满物理带宽，专门作为 Gemini 等 AI 服务流式输出、4K 视频、打游戏的高速爆发引擎。 🛠️ 第一部分：VPS 基础环境修复（排查 DNS 故障） # 在某些精简版的 Linux 镜像中，或者在频繁重装 Docker 容器后，VPS 的底层域名解析系统可能会瘫痪。表现为运行 curl 下载命令时，系统报错：curl: (6) Could not resolve host。这意味着你的服务器找不到“电话本”，无法将网址翻译为 IP 地址。\n1. 强制注入公共 DNS 电话本 # 在终端（如 FinalShell）中，以 Root 权限直接运行以下命令，强行将 Google 和 Cloudflare 的顶级公共 DNS 服务器写入系统的解析配置文件中：\necho -e \u0026#34;nameserver 8.8.8.8\\nnameserver 1.1.1.1\u0026#34; \u0026gt; /etc/resolv.conf 2. 验证网络层连通性 # 通过向 GitHub 发送 3 个测试数据包，如果能看到类似 64 bytes from... 且带有延迟数字的回显，说明服务器的网络域名解析已经完美恢复：\nping -c 3 github.com 🖥️ 第二部分：3X-UI 面板的安装与基础安全配置 # 3X-UI 面板目前是全球 GitHub 上维护最勤快、最轻量化的可视化代理控制台项目。\n1. 一键安装面板与 Xray 内核 # 在终端中执行以下官方纯净脚本（它会自动下载最新稳定的二进制文件并注册为系统服务）：\nbash \u0026lt;(curl -fsSL https://app.hysteria.network/get.sh) 注：由于官方版本更迭，原旧短链接已废弃，当前必须使用最新的 get.hy2.sh 或官方发布库。\n2. 绑定域名与 DNS 解析（以 Spaceship 为例） # 登录你购买域名的供应商后台（如 Spaceship），进入高级 DNS 设置（Advanced DNS），添加以下 A 记录：\nA 记录 1： 主机名（Host）填 @，值（Value）填你的 VPS 真实真实 IP，生存时间（TTL）设为 30分钟。 A 记录 2： 主机名 填 panel，值 填你的 VPS 真实真实 IP。 A 记录 3： 主机名 填 www，值 填你的 VPS 真实真实 IP。 3. 一键申请 SSL 域名安全证书 # 在终端中输入 x-ui 调出主菜单，选择 19 (SSL 证书管理)，再选择 1 (Get SSL)。输入你的二级域名 panel.你的域名.top，脚本会自动调用 Let\u0026rsquo;s Encrypt 在后台完成证书的签发。证书的绝对存放路径为：\n公钥证书（Cert）： /root/cert/panel.你的域名.top/fullchain.pem 私钥钥匙（Key）： /root/cert/panel.你的域名.top/privkey.pem 4. 强行“上锁”：开启 HTTPS 安全加密访问 # 为了防止面板的登录页面被审查机构直接明文探测，我们需要把刚刚申请下来的证书直接绑定给面板：\n终端输入 x-ui。 选择 19 进入证书管理，再选择 5 (Set Cert paths for the panel)。 终端会提示输入域名，手动完整打一遍：panel.你的域名.top 并回车。 脚本会自动将证书路径写入 3X-UI 底层配置，并强制重启网页服务。 🔒 第三部分：3X-UI 入站节点保姆级创建流程 # 🎯 节点一：VLESS-Reality-Vision（日常主力） # 基本原理： 白嫖大厂网站（如 Intel 或 Mozilla）的合法身份作掩护。在网络审查看来你是在访问英特尔官网，但实际上在 TLS 握手阶段，服务器利用密码学对流量完成了安全的私密放行。 面板后台配置步骤： # 添加入站： 登录 3X-UI 网页端 -\u0026gt; 点击 入站列表 -\u0026gt; 点击 + 添加入站。\n基础设置： 备注填 US-VLESS-Reality，协议选 vless，端口随机填一个五位数（如 42991）。\n添加客户端： 必须在“客户端 (Client)”区域，点击蓝色的 “+” 号。系统会自动生成一行用户记录和一串长 UUID。如果不加用户，节点内没有钥匙，导出链接时弹出的窗口就会是一片空白！\n流控选择（涡轮增压）： 在新生成的客户端那一行，找到 flow 下拉菜单，必须选择 xtls-rprx-vision。它能在不降低安全性的前提下大幅省去加解密算力，直接消除跨洋大流量时的握手延迟。\n传输安全性： 网络选 tcp，安全性选 reality。\nReality 特殊配置（新版3x-ui面板似乎有默认设置，有默认就不用填了）：\nTarget 填 www.intel.com:443 SNI 填 www.intel.com uTLS 选 chrome 公钥/私钥： 点击蓝色的 Get New Cert 按钮，让系统自动刷出一对崭新的非对称加密密钥对。 保存： 点击添加。回到列表点击“操作” -\u0026gt; “导出链接” -\u0026gt; 点击 Copy，直接去 V2rayN 中按 Ctrl + V 导入即可。\n🛡️ 节点二：VLESS-WS-TLS（IP 被墙时的备用方案） # 核心价值： WebSocket 协议能将数据伪装成网页上的实时聊天流。这个节点平时不作主力，但它的最大作用是：一旦未来某天的 VPS 真实 IP 被墙，只要点亮域名在 Cloudflare 后台的“橙色小云（开启 CDN 代理）”，这个节点就能穿过全球 CDN 节点满血复活！ 面板后台配置步骤： # 基础设置： 协议选 vless，端口必须填 8443 或 2053（因为 Cloudflare 的全球 CDN 网络的 HTTPS 流量只放行这几个特定端口）。\n添加客户端： 点击 “+” 号 生成专属用户 UUID。\n传输设置： 网络（Network）必须下拉选择 ws（WebSocket），路径填 /gyq-cdn。\n安全设置： 安全性选择 tls。\nSNI 填：panel.你的域名.top 数字证书路径： 勾选“文件路径”，将之前申请的证书绝对路径完整复制进来： 公钥路径：/root/cert/panel.你的域名.top/fullchain.pem 私钥路径：/root/cert/panel.你的域名.top/privkey.pem 保存与启用： 点击添加。修改完后，必须点击面板左上角红色的“重启 Xray”按钮，配置才能真正被底层核心接管！\n客户端排错： 导入 V2rayN 后若发现不通，双击打开该节点，确保 Flow 这一栏完全保持空白！ 因为 Vision 流控是纯 TCP 独占的技术，绝对不能和 WebSocket 混用。\n⚡ 第四部分：高性能 Native Hysteria2 独立加装与“端口跳跃” # 1. 攻克 Linux 权限隔离禁区 # 由于 3X-UI 将证书保存在最高权限的 /root/ 目录下，而独立运行的 Hysteria2 服务在后台是以普通用户 hysteria 的身份运行的，它没有权限跨级读取 root 的文件，这会导致服务启动时发生 FATAL failed to load server config (tls.cert file not found) 崩溃。\n需要把证书复制到公共目录，并将所有权无缝移交给 Hysteria 服务：\n# 复制证书到独立进程地盘 cp /root/cert/panel.你的域名.top/fullchain.pem /etc/hysteria/fullchain.pem cp /root/cert/panel.你的域名.top/privkey.pem /etc/hysteria/privkey.pem # 将证书所有权改写给 hysteria 用户组 chown hysteria:hysteria /etc/hysteria/fullchain.pem chown hysteria:hysteria /etc/hysteria/privkey.pem 2. 编写“防封锁端口跳跃”版配置文件 # 国内运营商的 QoS 机制一旦发现某个固定的 UDP 端口在持续进行超大流量的数据传输，就会直接判定为翻墙并实施限速或拦截封锁 24-72 小时。\n我们利用 Hysteria2 原生的端口跳跃（Port Hopping）功能来彻底降维打击这种检测：让服务器同时打开 10000 个高位端口，客户端每次发包时，端口都在这个范围内随机高频跳动，运营商的检测设备根本无法锁定特征。\n运行 nano /etc/hysteria/config.yaml（清空默认内容），将以下规范代码整体粘贴进去：\nlisten: :50000-60000 # 开启一万个高位端口的监听矩阵，安全避开系统常用端口冲突 tls: cert: /etc/hysteria/fullchain.pem # 读取已移交权限的公共证书 key: /etc/hysteria/privkey.pem auth: type: password password: your_secret_password # 设置一个你自己的专属连接密码 fastOpen: true masquerade: type: proxy proxy: url: https://www.bing.com # 极其逼真的伪装，当遭遇主动探测时返回微软必应官网 rewriteHost: true 提示：按 Ctrl+O 存盘，按 Ctrl+X 退出。\n3. 放行底层高级防火墙矩阵并启动 # # 在底层网络防火墙中强行插入一万个高频 UDP 端口的放行链 iptables -I INPUT -p udp --dport 50000:60000 -j ACCEPT ufw allow 50000:60000/udp ufw reload # 重启并激活极速服务 systemctl restart hysteria-server.service systemctl enable hysteria-server.service 4. 手工拼装极速分享链接 # 由于是独立加装，不需要依赖任何第三方魔改脚本，我们自己就可以按照标准格式拼装出高能节点代码：\nhysteria2://your_secret_password@panel.你的域名.top:50000/?mport=50000-60000\u0026amp;sni=panel.你的域名.top\u0026amp;insecure=0#US-Hys2-防封跳跃 复制这串代码，在电脑端的 V2rayN 里直接 Ctrl + V 导入即可！\n🛠️ 第五部分：高频运维与故障排查 # 当未来忘记了登录网址，或者系统发生未知瘫痪时，不要慌张，连接 FinalShell，这四行极客命令是你的终极安全底线：\n1. 免重置、免修改，明文捞出面板的所有登录数据 # 3X-UI 的所有配置都持久化存储在本地的 SQLite 数据库中。无需重启面板，直接在终端运行以下高级 SQL 查询，当前活着的明文密码、用户、端口和那个长长难记的随机根路径会被直接打印在屏幕上：\nsqlite3 /etc/x-ui/x-ui.db \u0026#34;SELECT key, value FROM settings WHERE key IN (\u0026#39;webPort\u0026#39;, \u0026#39;webBasePath\u0026#39;);\u0026#34; (提示：若提示未找到命令，随手运行 apt install sqlite3 -y 即可)\n2. 检查特定端口被谁占用了（网络层捉虫） # 如果你想查看 8443 或者面板端口到底有没有在正常运行：\nss -nltp | grep 8443 若能看到右侧清晰打印着 users:((\u0026quot;xray-linux-amd6\u0026quot;,pid=...))，说明服务完好，直接去排查客户端配置或本地运营商干扰即可。\n3. 查看双引擎服务的健康状态 # systemctl status x-ui # 检查3X-UI图形控制台状态 systemctl status hysteria-server # 检查独立极速进程状态 4. 实时追踪最近 20 行报错日志（精准定位错在哪一行） # journalctl -u hysteria-server -n 20 --no-pager ","date":"2026-05-30","externalUrl":null,"permalink":"/posts/tech-extras/3x-ui--hysteria2/","section":"系列","summary":"🌐 核心架构设计：为什么这么做？ # 过去使用一键翻墙脚本，脚本作者通常采用“霰弹枪策略”：在一个黑盒环境里强行塞入 10 多个协议节点，将其全部打包在一个 Sing-box 内核里。这种方式看似全面，但在 1GB 内存的入门级 VPS（虚拟专用服务器）上会产生严重的弊端：大量闲置进程白白吃掉宝贵的系统资源，且黑盒脚本会死锁底层网络逻辑，导致后续更新极易崩溃或丢失配置。\n","title":"番外：网络代理使用 3X-UI 面板与 Hysteria2 独立加装教程","type":"posts"},{"content":"","date":"2026-05-30","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"2026-05-30","externalUrl":null,"permalink":"/tags/mysql/","section":"Tags","summary":"","title":"MySQL","type":"tags"},{"content":"在经历了前面的基础环境搭建后，本任务我们将真正把后端服务暴露给公网。\n由于这个任务涉及到的细节非常多（尤其是针对 1G 内存的极限压榨），我已经将我部署 Java JSP 项目（游戏论坛管理系统） 的完整踩坑过程，单独整理长文实战。\n本任务已完成！详情及完整操作步骤，请直接跳转阅读专栏文章：\n👉 番外：1G 小内存 VPS 部署 Java JSP 项目实战（Docker 本地构建 + 远程运行方案）\n","date":"2026-05-30","externalUrl":null,"permalink":"/posts/vps-combat/task-8-nginx-proxy/","section":"系列","summary":"在经历了前面的基础环境搭建后，本任务我们将真正把后端服务暴露给公网。\n","title":"任务 8：后端接口上线与 Nginx 代理","type":"posts"},{"content":"","date":"2026-05-30","externalUrl":null,"permalink":"/tags/%E5%89%8D%E7%AB%AF%E9%83%A8%E7%BD%B2/","section":"Tags","summary":"","title":"前端部署","type":"tags"},{"content":"在服务器部署的进阶之路上，部署限制内存的数据库是一个必经的关卡 。对于这种小内存的 VPS（比如 RackNerd），如果直接裸跑默认配置的 MySQL 8.0，它会一口气吞掉 400MB 甚至更多的内存，极易导致服务器 OOM（内存溢出）死机。\n这篇文章记录了如何通过 Docker 配合自定义 my.cnf 文件，将 MySQL 的内存占用极限压榨到 200MB 以内 ，同时理清了“环境容器化”的核心思想 。\n💡 核心认知：MySQL 是“发电厂”，不是“随用随插的 U 盘” # 在刚接触全栈部署时，很容易产生一个误区：以为“只要 JSP 或 Vue 项目跑起来了，才需要把 MySQL 拉起来”。\n真实情况是：\nMySQL 是一个需要 24 小时常驻后台独立运行的基础设施。它就像市里的“发电厂”，而我们的各种 Web 项目（无论是以前写的 JSP，还是以后的 Spring Boot/C#）都只是“用电器”。不管用电器开没开，发电厂都必须在那运转，守着 3306 端口等待连接。\n一台服务器上，通常只需要运行这一个 MySQL 容器实例。未来不管开发多少个新项目，只需要在这个实例里 CREATE DATABASE 创建新的库即可，数据完全隔离。\n🛠️ 实战步骤：打造精简版 MySQL 容器 # 为了方便日后在 VS Code 里通过 Remote-SSH 直接修改配置，我们将配置文件和数据目录挂载到宿主机。\n1. 创建宿主机目录 # mkdir -p ~/mysql/conf ~/mysql/data cd ~/mysql 2. 编写极限压缩内存的 my.cnf (INI 格式) # 这是限制 MySQL 内存消耗的核心图纸。在 ~/mysql/conf/my.cnf 中写入：\n[mysqld] # 核心杀手锏：关闭性能监控，省下 100MB+ 内存 performance_schema = OFF # 核心参数：限制 InnoDB 缓冲池大小（默认通常 128M，压缩到 64M） innodb_buffer_pool_size = 64M # 限制最大连接数（默认 151，压缩到 50 足够个人全栈项目使用） max_connections = 50 # 跳过 DNS 解析，提升连接速度 skip-name-resolve # 统一字符集，防止前后端中文乱码 character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci 注意：将其用作个人博客这个配置还算不错，但要注意“skip-name-resolve”功能，MySQL将不再解析 localhost，连接地址必须硬编码写成 120.0.0.1。\n3. 编写 docker-compose.yml # 在 ~/mysql/ 目录下创建 docker-compose.yml，把我们的图纸挂载进去：\nservices: mysql-lite: image: mysql:8.0 container_name: mysql-lite restart: always environment: MYSQL_ROOT_PASSWORD: \u0026#34;这里填你的强密码\u0026#34; ports: - \u0026#34;3306:3306\u0026#34; volumes: - ./conf/my.cnf:/etc/mysql/conf.d/my.cnf - ./data:/var/lib/mysql command: --default-time-zone=\u0026#39;+08:00\u0026#39; 4. 一键启动与内存验证 # # 后台启动容器 docker compose up -d # 查看内存占用情况（检验压榨成果） docker stats 正常情况下，内存占用会被稳稳控制在 150MB - 180MB 左右。\n🔧 常见运维操作回顾 # 1. 修改配置后如何生效？ # MySQL 只有在启动的那一刻才会读取 my.cnf 文件。所以如果后续修改了配置文件，必须重启容器强迫它重新加载规则：\ndocker restart mysql-lite 2. 如何优雅清理废弃的旧数据库容器？ # 为了防止旧容器占用端口和内存，必须将其彻底清理：\n# 1. 停止运行 docker stop 旧容器名称或ID # 2. 删除容器 docker rm 旧容器名称或ID # 3. (可选) 清理未被使用的无用数据卷，释放硬盘空间 docker volume prune 总结： 这套组合拳打完，不仅跳出了以前“只懂写增删改查”的代码舒适区 ，更重要的是在服务器部署层面建立起了“容器化隔离”和“底层参数调优”的思维 。以后无论换什么云服务器，这套方案都可以几分钟内快速复刻。\n","date":"2026-05-17","externalUrl":null,"permalink":"/posts/vps-combat/task-7-database/","section":"系列","summary":"在服务器部署的进阶之路上，部署限制内存的数据库是一个必经的关卡 。对于这种小内存的 VPS（比如 RackNerd），如果直接裸跑默认配置的 MySQL 8.0，它会一口气吞掉 400MB 甚至更多的内存，极易导致服务器 OOM（内存溢出）死机。\n","title":"任务 7：部署限制内存的数据库","type":"posts"},{"content":"","date":"2026-05-17","externalUrl":null,"permalink":"/tags/ssh/","section":"Tags","summary":"","title":"SSH","type":"tags"},{"content":"","date":"2026-05-17","externalUrl":null,"permalink":"/tags/vscode/","section":"Tags","summary":"","title":"VSCode","type":"tags"},{"content":"对于拥有海外小性能 VPS 的开发者来说，高物理延迟往往是绕不开的痛。如果还在使用传统的终端工具（如 FinalShell），忍受着 nano 或 vim 编辑长文本时吞换行、卡顿的折磨，那么 VS Code + Remote-SSH 绝对是救星。\n它能将本地强悍的编辑体验直接“嫁接”到远端服务器上，实现“本地丝滑敲代码，远端零感存文件”的现代运维工作流。\n一、 核心优势：为什么放弃传统终端？ # 降维打击的编辑体验： 告别终端编辑器，直接在 VS Code 侧边栏双击打开服务器文件。支持完美复制粘贴、全局搜索、快捷键撤销，所有修改在按下 Ctrl + S 的瞬间自动同步到远端。 极简的文件传输： 放弃繁琐的 SFTP 客户端，直接把本地文件拖拽进 VS Code 左侧目录即可完成上传。 内网穿透神技（端口转发）： 无需修改防火墙，VS Code 可直接将远端 Docker 容器跑的测试端口映射到本地，直接用 localhost:端口号 在本地浏览器预览。 二、 环境说明与准备工作 # 本教程基于以下环境配置（进行了基本的安全加固）：\n本地环境： Windows 11，已安装 VS Code 远端服务器： Ubuntu 24.04 (位于美国芝加哥机房) 安全策略： 已禁用密码登录，修改了默认 22 端口，采用 Ed25519 密钥认证，且私钥设置了保护密码（Passphrase）。 三、 核心配置步骤 # Step 1: 安装核心插件 # 打开本地 VS Code，在扩展商店搜索并安装由 Microsoft 官方提供的 Remote - SSH 插件。\nStep 2: 放置私钥并配置 config 文件 # 找到本地的 Ed25519 私钥文件（例如 id_ed25519），将其复制到 Windows 的默认 SSH 目录下：C:\\Users\\你的用户名\\.ssh\\。 在 VS Code 中按 F1 呼出命令面板，输入 SSH: Open，选择 Remote-SSH: Open SSH Configuration File...，打开 ~/.ssh/config 文件。 按照以下格式填入服务器信息并保存： Host RN_VPS-Chicago HostName 198.46.159.139 # 你的VPS真实公网IP User root # 登录用户名 Port 2222 # 你修改后的自定义SSH端口 IdentityFile ~/.ssh/id_ed25519 # 指向你的私钥文件路径 Step 3: 解决频繁索要密钥密码的问题（配置 ssh-agent） # 由于我的 Ed25519 密钥设置了保护密码，这个模式下保护密码的询问高频，即使是打开文件都会需要输入保护密码，相当的麻烦。我的解决方式是把密码交给 Windows 自带的 ssh-agent 管家管理。\n以管理员身份运行 Windows 的 PowerShell。 依次执行以下命令，启动并设置代理服务为自启： # 将 ssh-agent 服务设置为开机自动启动 Get-Service ssh-agent | Set-Service -StartupType Automatic # 启动 ssh-agent 服务 Start-Service ssh-agent 将私钥添加进系统保险箱： # 添加默认路径下的密钥 ssh-add 此时系统会提示 Enter passphrase for...，在此输入当初设置的密钥保护密码并回车。看到 Identity added 即代表成功。\n重启 VS Code，再次点击左下角连接 RN_VPS-Chicago，此时应该已经实现了丝滑的无感秒连！\nPS：可以尝试修改文件测试一下 # 以修改 Docker 部署的 Nginx 网关提示语为例：\n加载工作区： 点击左侧蓝色的“打开文件夹”，在顶部弹出的输入框中直接回车（默认打开 /root/ 家目录），信任该作者后，左侧即可展现 Linux 系统的文件树。\n修改文件： 在左侧目录中随便找一个文件做修改测试，我使用的是 nginx.conf（例如位于 /root/NGINX-PROXY/ 下），双击打开。像编辑本地文本一样加上修改：\n\u0026lt;h1\u0026gt;网关配置成功...\u0026lt;/h1\u0026gt; \u0026lt;h1\u0026gt;\u0026lt;i\u0026gt;网关配置成功...\u0026lt;/i\u0026gt;\u0026lt;/h1\u0026gt; 这是加了一个 i 标签让字体倾斜\n保存修改： 按下 Ctrl + S，文件瞬间同步到美国机房的硬盘上。\n终端重启服务： 按下快捷键 Ctrl + `（Tab 键上方）呼出集成终端。确保当前路径位于项目目录下，执行重启命令：\n# 重启对应的 Docker 容器 docker compose restart 验证： 浏览器刷新网页，即可看到斜体标签生效。 ","date":"2026-05-17","externalUrl":null,"permalink":"/posts/tech-extras/vscode-remote/","section":"系列","summary":"对于拥有海外小性能 VPS 的开发者来说，高物理延迟往往是绕不开的痛。如果还在使用传统的终端工具（如 FinalShell），忍受着 nano 或 vim 编辑长文本时吞换行、卡顿的折磨，那么 VS Code + Remote-SSH 绝对是救星。\n","title":"番外：VS Code + Remote-SSH 模式","type":"posts"},{"content":"","date":"2026-05-15","externalUrl":null,"permalink":"/tags/dns/","section":"Tags","summary":"","title":"DNS","type":"tags"},{"content":"","date":"2026-05-15","externalUrl":null,"permalink":"/tags/ssl/","section":"Tags","summary":"","title":"SSL","type":"tags"},{"content":" 📝 1. 背景与目标 # 在云服务器（VPS）上部署项目时，直接暴露端口既不安全也不优雅。本次实战的目标是：\n将在 Spaceship 购买的域名 (gyqblog.top) 解析到 VPS 公网 IP。 停用并清理早期的测试项目（如 JSP 容器），释放资源。 利用 Docker 部署一个全局 Nginx 作为统一网关（反向代理）。 使用 acme.sh 自动申请并部署 Let\u0026rsquo;s Encrypt 免费 SSL 证书，实现全站 HTTPS 加密。 🌐 2. DNS 解析：让域名找到服务器 # 在域名注册商（如 Spaceship）的后台，找到 Advanced DNS（高级 DNS） 设置。我们需要添加两条 A 记录（A 记录的作用是将域名直接指向一个 IPv4 地址）：\n记录 1：主机记录 (Host) 填 @，记录值 (Value) 填 VPS 公网 IP。代表访问主域名。 记录 2：主机记录 (Host) 填 www，记录值 (Value) 填 VPS 公网 IP。代表访问 www 子域名。 验证方法：在本地终端执行 ping gyqblog.top，如果返回服务器 IP，说明解析已生效。\n🔐 3. SSL 证书申请与“端口保卫战” # 3.1 安装 acme.sh 脚本 # 这是一个轻量级的自动证书申请工具：\ncurl https://get.acme.sh | sh -s email=你的邮箱@example.com source ~/.bashrc 3.2 ⚠️ 避坑指南：80 端口占用问题 # 在使用 --standalone（独立模式）申请证书时，acme.sh 会临时创建一个 Web 服务来验证域名所有权，这要求服务器的 80 端口必须处于空闲状态。\n如果遇到报错：tcp port 80 is already used by ((\u0026quot;docker-proxy\u0026quot;...))，说明之前的容器没有彻底关闭。 排查与解决：\n# 1. 揪出占用端口的“内鬼”容器 docker ps # 2. 找到占用 80 端口的 CONTAINER ID，强行停止它 docker stop \u0026lt;容器ID\u0026gt; # 3. 再次确认端口已空出 lsof -i:80 3.3 申请并提取证书 # 端口清空后，执行申请：\nacme.sh --issue -d gyqblog.top -d www.gyqblog.top --standalone 申请成功后，将证书安装到宿主机的指定目录（此目录稍后会挂载给 Nginx）：\nmkdir -p /opt/nginx/ssl acme.sh --install-cert -d gyqblog.top \\ --key-file /opt/nginx/ssl/gyqblog.top.key \\ --fullchain-file /opt/nginx/ssl/gyqblog.top.pem \\ --reloadcmd \u0026#34;cd /root/nginx-proxy \u0026amp;\u0026amp; docker compose restart nginx\u0026#34; (注：--reloadcmd 保证了未来证书自动续期后，Nginx 能自动重启加载新证书。 而cd /root/nginx-proxy是存放地点，必须写明)\n🏗️ 4. 核心网关搭建（Docker + Nginx） # 为了将来的博客和 API 接口做准备，我建立一个专属的网关目录：\nmkdir -p /root/nginx-proxy cd /root/nginx-proxy 4.1 写入 Nginx 配置文件 (解决终端粘贴换行错乱) # 踩坑记录：在某些 SSH 终端中使用 nano 右键粘贴代码时，会导致换行符丢失、格式错乱。 优雅的极客解法：使用 cat EOF 直接注入代码，绝对不会乱码。\n在终端直接执行以下整段命令：\ncat \u0026gt; nginx.conf \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; # HTTP 强制跳转 HTTPS server { listen 80; server_name gyqblog.top www.gyqblog.top; return 301 https://$host$request_uri; } # HTTPS 核心网关 server { listen 443 ssl; server_name gyqblog.top www.gyqblog.top; # 容器内的证书路径（稍后在 compose 中映射） ssl_certificate /etc/nginx/ssl/gyqblog.top.pem; ssl_certificate_key /etc/nginx/ssl/gyqblog.top.key; # 临时欢迎页面 location / { default_type text/html; return 200 \u0026#39;\u0026lt;!DOCTYPE html\u0026gt;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt;\u0026lt;title\u0026gt;网关成功\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;网关配置成功！博客即将上线...\u0026lt;/h1\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#39;; } } EOF 4.2 编写 docker-compose.yml # 同样使用 cat EOF 写入容器编排配置。注意我们需要挂载配置文件和刚刚提取的 SSL 证书目录：\ncat \u0026gt; docker-compose.yml \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; services: nginx: image: nginx:latest container_name: nginx-gateway restart: always ports: - \u0026#34;80:80\u0026#34; - \u0026#34;443:443\u0026#34; volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - /opt/nginx/ssl:/etc/nginx/ssl EOF 4.3 启动网关 # docker compose up -d 启动后，在浏览器访问 http://gyqblog.top，网页会自动跳转到带有安全锁的 HTTPS 页面，并显示“网关配置成功！”。\n💡 5. 总结与架构演进 # 完成这一步后，服务器架构发生了质的改变： 过去是单体直连（用户直接通过 IP:端口 访问项目），现在演变成了网关代理（Nginx 独占 80 和 443 端口，处理完安全加密后，在内网安全地将请求转发给后端的真实项目）。\n","date":"2026-05-15","externalUrl":null,"permalink":"/posts/vps-combat/task-6-gateway/","section":"系列","summary":"📝 1. 背景与目标 # 在云服务器（VPS）上部署项目时，直接暴露端口既不安全也不优雅。本次实战的目标是：\n","title":"任务 6：搭建全网关架构 —— 域名解析、SSL 证书与 Docker Nginx 部署","type":"posts"},{"content":"","date":"2026-05-07","externalUrl":null,"permalink":"/tags/nginx/","section":"Tags","summary":"","title":"Nginx","type":"tags"},{"content":"折腾服务器，从“在宿主机上乱装一气”到“全面拥抱容器化” 。如何使用 Docker 和 Nginx，将纯前端项目托管到云服务器上，并实现公网访问 。\n💡 核心原理解析：为什么我们需要 Nginx？ # 在本地电脑上，我们双击写好的 index.html 就能在浏览器里看到网页。此时浏览器的地址栏是 file:/// 开头的，这只是浏览器在充当本地文件读取器。\n但如果要把网页放到远端服务器上让全世界访问，别人是无法直接“读取”你服务器硬盘的。这时候就需要引入 Web 服务器（如 Nginx）。\nNginx 的角色：它就像一个 24 小时守在网络端口的接待员。 工作机制：当用户的浏览器通过 http:// 访问服务器公网 IP 时，Nginx 会接收请求，去指定的文件夹里找到网页文件，并打包发送给用户的浏览器进行渲染 。 🛠️ Docker 部署实战 # 使用 Docker 部署服务，核心在于编写 docker-compose.yml 配置文件 。这能让我们的宿主机保持极其干净，日后迁移也只需几行命令 。\n1. 准备挂载目录 # 首先在服务器上创建一个目录，用于存放网页文件：\nmkdir -p ~/nginx-web/html 注意：不要用 mkdir index.html 去创建文件！mkdir 命令永远只会创建文件夹。如果你想要创建一个空的纯文本文件，请使用 touch index.html 或者用 echo 命令重定向写入。\n2. 编写 Docker 配置文件 # 在 ~/nginx-web 目录下创建 docker-compose.yml。\n高延迟 SSH 避坑指南：如果你使用的是海外 VPS，SSH 连接延迟较高，直接用 nano 编辑并粘贴 YAML 代码极易导致缩进错乱。建议使用 SSH 客户端自带的可视化文件管理器双击编辑，或者使用 cat 重定向无损写入： cat \u0026gt; docker-compose.yml \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; services: nginx: image: nginx:latest container_name: nginx-web ports: - \u0026#34;80:80\u0026#34; volumes: - ./html:/usr/share/nginx/html restart: always EOF 3. YAML 核心参数秒懂 # 这段简短的代码包含了 Docker 容器化的两大精髓：\nports: - \u0026quot;80:80\u0026quot;（端口映射）：左边是宿主机对外开放的“大门”，右边是 Nginx 容器内部出厂默认死守的端口。这告诉 Docker 将外部敲击 80 端口的请求，精准送进容器内部的 80 端口。 volumes: - ./html:/usr/share/nginx/html（数据卷挂载）：把宿主机上的 ./html 真实文件夹，像插 U 盘一样，映射到了 Nginx 容器内部的默认网页读取路径 /usr/share/nginx/html 。这样不仅能让 Nginx 读到我们的代码，还能保证即便容器被删除，网页资产依然安全留在宿主机硬盘上。 4. 启动服务 # 在终端执行以下命令，让它在后台默默运行：\ndocker compose up -d 最后，打开本地浏览器，输入 http://你的公网IP，就能看到属于你的页面了！\n💣 进阶避坑：Bash 终端的历史命令解析错误 # 在用命令行快速生成测试网页时，你可能会写出这样的命令：\necho \u0026#34;\u0026lt;h1\u0026gt;Hello World!\u0026lt;/h1\u0026gt;\u0026#34; \u0026gt; html/index.html 如果你的文字里带有感叹号 ! 且使用了双引号 \u0026quot;\u0026quot;，Linux 终端会报错 event not found。\n原因：在 Bash 中，! 代表历史命令扩展。 解法：养成好习惯，使用单引号 '' 包裹纯文本字符串，它会让终端把里面的内容当作纯文本对待，不做任何特殊解析。 ","date":"2026-05-07","externalUrl":null,"permalink":"/posts/vps-combat/task-5-nginx/","section":"系列","summary":"折腾服务器，从“在宿主机上乱装一气”到“全面拥抱容器化” 。如何使用 Docker 和 Nginx，将纯前端项目托管到云服务器上，并实现公网访问 。\n","title":"任务 5：配置 Nginx 与静态网站的前端部署","type":"posts"},{"content":"","date":"2026-04-29","externalUrl":null,"permalink":"/categories/docker/","section":"Categories","summary":"","title":"Docker","type":"categories"},{"content":"在拥有了一台属于自己的 VPS（如 1核 1G内存，配置了 2G Swap）后，很多新手在尝试部署 Java 项目时，往往会选择直接在服务器上安装 Maven 或运行 docker build。但现实很残酷：Java 编译极其消耗内存，1G 的内存在构建瞬间就会被挤爆，导致系统卡死或触发 OOM (Out Of Memory) 杀掉进程。\n破局思路：本地构建，远程运行 利用性能强大的本地 Windows 电脑（作为宿主机）进行代码编译、打包和 Docker 镜像构建。然后将纯净的“镜像成品”传输到 VPS 上，VPS 只需要负责简单的“运行”即可。\n以下是完整的实战记录，带你一步步避坑，完成低配服务器的完美部署。\n阶段一：VPS 端数据库准备与数据导入 # 由于资源有限，我们通过 Docker 启动一个轻量级、限制内存的 MySQL 容器。\n1. 启动 MySQL 容器并限制内存 # 在 VPS 上执行以下命令启动 MySQL 8.0。关键点在于 -m 400m，这能防止 MySQL 随着运行时间增长吞噬掉所有内存。\nsudo docker run -d \\ --name mysql-db \\ --restart unless-stopped \\ -p 3306:3306 \\ -e MYSQL_ROOT_PASSWORD=你的数据库密码 \\ -e MYSQL_DATABASE=你的数据库名 \\ -m 400m \\ mysql:8.0 \\ --default-authentication-plugin=mysql_native_password 2. 导入本地数据到 VPS 数据库 # 不要尝试在服务器上安装笨重的可视化工具，直接通过命令行搞定：\n第一步（Windows）： 将本地导出的 xxx.sql 文件传到 VPS。如果你的 VPS 修改过默认 SSH 端口，记得加上 -P 参数。 scp -P 你的SSH端口号 xxx.sql root@你的VPS_IP:/root/ 第二步（VPS）： 将 sql 文件从系统拷贝到 MySQL 容器内部。 sudo docker cp /root/xxx.sql mysql-db:/xxx.sql 注意：这里的“mysql-db”是启动的MySQL容器的名字，上面的第一步的“\u0026ndash;name”\n第三步（VPS）： 让 MySQL 容器执行该文件导入数据。 sudo docker exec -it mysql-db mysql -uroot -p你的数据库密码 你的数据库名 -e \u0026#34;source /xxx.sql\u0026#34; (验证：此时可通过本地电脑的 Navicat 或 IDEA 直连 VPS 的 3306 端口，确认数据导入成功。)\n阶段二：本地 Java 项目调整与打包 # 为了适应 1G 内存的限制，我们需要对 Java 环境做向下兼容的优化。\n1. 修改代码中的数据库连接 # 极易踩坑点： 之前代码里写的是 localhost:3306，现在必须修改为 你的VPS公网IP:3306，并且核对用户名、密码以及对应的数据库名称。\n2. 将 Java 版本降级为 1.8 (Java 8) # Java 8 内存占用更小，且与传统的 JSP 技术（依赖 javax.servlet）兼容性最好。\n在 IDEA 的 Project Structure 中，将 SDK 和语言级别修改为 1.8 和 8。\n打开 Maven 面板，依次双击 clean 和 package，在 target 目录下生成全新的 .war 包。\n3. 编写 Dockerfile # 新建一个名为 vps-deploy 的文件夹，将 .war 文件拷贝进去，并在同级目录新建 Dockerfile（无后缀）：\n# 选用 Tomcat 9 搭配轻量级 Java 8 环境 (slim版本体积更小) FROM tomcat:9.0-jre8-slim # 清理 Tomcat 默认页面，保持环境纯净 RUN rm -rf /usr/local/tomcat/webapps/* # 将项目 war 包复制并重命名为 ROOT.war，实现根目录直接访问（无需在 IP 后加项目名） COPY 你的项目名.war /usr/local/tomcat/webapps/ROOT.war EXPOSE 8080 CMD [\u0026#34;catalina.sh\u0026#34;, \u0026#34;run\u0026#34;] 阶段三：构建镜像与解决网络代理问题 # 1. 本地构建 Docker 镜像 # 在 vps-deploy 目录下打开终端，执行：\ndocker build -t my-jsp-app:v1 . 避坑指南（网络拉取失败）： 如果在构建时遇到 failed to fetch oauth token 报错，说明国内直连 Docker Hub 被墙。\n解决方案： 启动你的代理客户端（如 v2rayN / Clash 等），开启 TUN 模式（虚拟网卡模式），让终端和 Docker Desktop 强制走代理网络，重新执行 build 即可顺利拉取基础镜像。 2. 导出镜像包 # 网络畅通完成构建后，将镜像打包为一个离线的 .tar 文件：\ndocker save -o my-jsp-app.tar my-jsp-app:v1 阶段四：VPS 最终部署与内存压榨 # 1. 传输镜像到 VPS # 再次使用 scp 命令，把“集装箱”运上服务器：\nscp -P 你的SSH端口号 my-jsp-app.tar root@你的VPS_IP:/root/ 2. 加载并运行容器（核心优化点） # 在 VPS 终端执行 sudo docker load -i my-jsp-app.tar 加载镜像。随后，使用以下命令运行你的 JSP 项目：\nsudo docker run -d \\ --name my-web-app \\ --restart unless-stopped \\ -p 80:8080 \\ -e JAVA_OPTS=\u0026#34;-Xms128m -Xmx256m\u0026#34; \\ my-jsp-app:v1 关键参数解析：\n-p 80:8080：将服务器的 80 端口映射到容器的 8080，实现在浏览器直接输入 IP 访问网站。\n-e JAVA_OPTS=\u0026quot;-Xms128m -Xmx256m\u0026quot;：绝对不能省的一句！ 限制 Tomcat 的 JVM 初始内存为 128M，最大 256M。结合前面限制了 400M 的 MySQL，系统整体占用被严格控制在 700M 左右，完美适配 1G 内存机器！\n3. 常见报错排查：端口冲突 # 如果运行上述命令报错 Bind for 0.0.0.0:80 failed: port is already allocated，说明 80 端口被占用了（可能是之前测试用的 Nginx，或者自带的 Web 服务）。\n排查命令： sudo ss -tulnp | grep :80 或 sudo docker ps 查找占用 80 端口的进程或容器。 解决： 使用 kill -9 PID 杀掉进程，或 docker rm -f 容器名 删掉占位容器。然后重新运行启动命令即可。 部署总结： 通过“本地编译打包 -\u0026gt; 生成纯净镜像 -\u0026gt; 导出导入 -\u0026gt; 云端限制内存运行”这一套标准的工业级工作流，不仅保护了小内存 VPS 脆弱的性能，还极大提高了部署成功率。即使未来更换服务器，只需要把 tar 包拷走，你的项目也能在 1 分钟内在新服务器上满血复活！\n","date":"2026-04-29","externalUrl":null,"permalink":"/posts/tech-extras/jsp-deployment/","section":"系列","summary":"在拥有了一台属于自己的 VPS（如 1核 1G内存，配置了 2G Swap）后，很多新手在尝试部署 Java 项目时，往往会选择直接在服务器上安装 Maven 或运行 docker build。但现实很残酷：Java 编译极其消耗内存，1G 的内存在构建瞬间就会被挤爆，导致系统卡死或触发 OOM (Out Of Memory) 杀掉进程。\n","title":"番外：1G 小内存 VPS 部署 Java JSP 项目实战：Docker 本地构建 + 远程运行完美方案","type":"posts"},{"content":"","date":"2026-04-29","externalUrl":null,"permalink":"/tags/dockerfile/","section":"Tags","summary":"","title":"Dockerfile","type":"tags"},{"content":" 基础环境设置 # FROM：指定基础镜像 功能：这是每个 Dockerfile 的第一条指令（除注释外）。它决定了你的应用运行在什么环境之上。 示例：FROM ubuntu:24.04 （基于 Ubuntu 24.04 系统）或 FROM nginx:alpine （基于轻量级的 Nginx 镜像）。 WORKDIR：设置工作目录 功能：相当于 Linux 里的 cd 命令。后续的 RUN、CMD、COPY 等指令都会在这个目录下执行。如果目录不存在，Docker 会自动帮你创建。 示例：WORKDIR /app （将后续操作的默认路径设为容器内的 /app 目录）。 2. 文件复制 # COPY：复制文件/目录到容器中 功能：将宿主机（你的电脑或服务器）上的文件或目录，原封不动地拷贝到容器的指定路径下。 示例：COPY . /app （将当前宿主机目录下的所有文件，复制到容器的 /app 目录下）。 ADD：高级复制 功能：和 COPY 类似，但带有额外功能。如果复制的是一个本地的 .tar.gz 压缩包，ADD 会自动解压到目标路径；它也支持填入一个网络 URL 来下载文件。（新手推荐优先使用 COPY，语义更清晰）。 3. 执行命令与配置 # RUN：在构建镜像时执行命令 功能：这是构建阶段的主力军。通常用来安装软件包、创建文件夹、配置环境等。注意：每次 RUN 都会生成一个新的镜像层，所以通常会把多条命令用 \u0026amp;\u0026amp; 连起来写。 示例：RUN apt-get update \u0026amp;\u0026amp; apt-get install -y curl。 ENV：设置环境变量 功能：定义环境变量，后续的 RUN 指令可以使用，并且这些变量也会一直保留到容器运行阶段供你的程序读取。 示例：ENV MYSQL_ROOT_PASSWORD=my-secret-pw 或 ENV PORT=8080。 EXPOSE：声明监听端口 功能：这只是一个声明，告诉使用这个镜像的人，该容器内部的程序会使用哪个端口。它不会自动将端口映射到宿主机，映射依然需要在运行 docker run 时加上 -p 参数或在 docker-compose.yml 中配置。 示例：EXPOSE 80 （声明容器内的 Web 服务使用 80 端口）。 4. 容器启动指令 # CMD：容器启动时的默认命令 功能：指定容器跑起来之后要做的第一件事（例如启动 Nginx、运行 Java 后端应用等）。如果用户在 docker run 时手动指定了其他命令，CMD 的内容会被覆盖。 示例：CMD [\u0026quot;nginx\u0026quot;, \u0026quot;-g\u0026quot;, \u0026quot;daemon off;\u0026quot;]。 ENTRYPOINT：容器启动的主入口 功能：和 CMD 类似，但它不会被轻易覆盖。通常用于让容器表现得像一个独立的可执行程序。如果同时存在 ENTRYPOINT 和 CMD，CMD 的内容会作为参数传递给 ENTRYPOINT。 综合示例 # 为了方便理解，这里提供一个部署简单前端/静态网页的通用 Dockerfile 模板：\n# 1. 声明基础镜像为 Nginx FROM nginx:latest # 2. 声明维护者信息（可选） LABEL maintainer=\u0026#34;yourname@example.com\u0026#34; # 3. 设置工作目录为 Nginx 存放网页的默认路径 WORKDIR /usr/share/nginx/html # 4. 把当前目录下所有的前端静态文件（如 html/css/js）拷贝到容器的工作目录 COPY ./dist/ . # 5. 声明容器将提供 80 端口的服务 EXPOSE 80 # 6. 启动 Nginx，并保持在前台运行（防止容器启动后立刻退出） CMD [\u0026#34;nginx\u0026#34;, \u0026#34;-g\u0026#34;, \u0026#34;daemon off;\u0026#34;] ","date":"2026-04-29","externalUrl":null,"permalink":"/posts/tech-extras/dockerfile-tips/","section":"系列","summary":"基础环境设置 # FROM：指定基础镜像 功能：这是每个 Dockerfile 的第一条指令（除注释外）。它决定了你的应用运行在什么环境之上。 示例：FROM ubuntu:24.04 （基于 Ubuntu 24.04 系统）或 FROM nginx:alpine （基于轻量级的 Nginx 镜像）。 WORKDIR：设置工作目录 功能：相当于 Linux 里的 cd 命令。后续的 RUN、CMD、COPY 等指令都会在这个目录下执行。如果目录不存在，Docker 会自动帮你创建。 示例：WORKDIR /app （将后续操作的默认路径设为容器内的 /app 目录）。 2. 文件复制 # COPY：复制文件/目录到容器中 功能：将宿主机（你的电脑或服务器）上的文件或目录，原封不动地拷贝到容器的指定路径下。 示例：COPY . /app （将当前宿主机目录下的所有文件，复制到容器的 /app 目录下）。 ADD：高级复制 功能：和 COPY 类似，但带有额外功能。如果复制的是一个本地的 .tar.gz 压缩包，ADD 会自动解压到目标路径；它也支持填入一个网络 URL 来下载文件。（新手推荐优先使用 COPY，语义更清晰）。 3. 执行命令与配置 # RUN：在构建镜像时执行命令 功能：这是构建阶段的主力军。通常用来安装软件包、创建文件夹、配置环境等。注意：每次 RUN 都会生成一个新的镜像层，所以通常会把多条命令用 \u0026\u0026 连起来写。 示例：RUN apt-get update \u0026\u0026 apt-get install -y curl。 ENV：设置环境变量 功能：定义环境变量，后续的 RUN 指令可以使用，并且这些变量也会一直保留到容器运行阶段供你的程序读取。 示例：ENV MYSQL_ROOT_PASSWORD=my-secret-pw 或 ENV PORT=8080。 EXPOSE：声明监听端口 功能：这只是一个声明，告诉使用这个镜像的人，该容器内部的程序会使用哪个端口。它不会自动将端口映射到宿主机，映射依然需要在运行 docker run 时加上 -p 参数或在 docker-compose.yml 中配置。 示例：EXPOSE 80 （声明容器内的 Web 服务使用 80 端口）。 4. 容器启动指令 # CMD：容器启动时的默认命令 功能：指定容器跑起来之后要做的第一件事（例如启动 Nginx、运行 Java 后端应用等）。如果用户在 docker run 时手动指定了其他命令，CMD 的内容会被覆盖。 示例：CMD [\"nginx\", \"-g\", \"daemon off;\"]。 ENTRYPOINT：容器启动的主入口 功能：和 CMD 类似，但它不会被轻易覆盖。通常用于让容器表现得像一个独立的可执行程序。如果同时存在 ENTRYPOINT 和 CMD，CMD 的内容会作为参数传递给 ENTRYPOINT。 综合示例 # 为了方便理解，这里提供一个部署简单前端/静态网页的通用 Dockerfile 模板：\n","title":"番外：Dockerfile 常用指令详解","type":"posts"},{"content":"","date":"2026-04-29","externalUrl":null,"permalink":"/categories/%E7%AC%94%E8%AE%B0/","section":"Categories","summary":"","title":"笔记","type":"categories"},{"content":" 方案一：Windows 不装 Docker # 这种方式下，你的 Windows 只负责写代码和生成基础的打包文件，镜像的构建全在服务器上。\nWindows 端： 使用 Maven 将 Java 项目打包成 .jar 或 .war 文件。 传输： 将这个包和编写好的 Dockerfile 一起上传到你的 Linux VPS。 VPS 端： 在服务器上执行 docker build 将包构建成镜像，然后 docker run 启动容器。 致命缺点： 构建 Docker 镜像是一个非常消耗 CPU 和内存的操作。在小性能 VPS 上执行构建，极易导致服务器内存溢出（OOM）、死机或卡顿，整个过程也会非常缓慢。 方案二：Windows 端安装 Docker # 这种方式被称为**“本地构建，云端运行”**，也是目前企业级开发（CI/CD）的主流思路。\nWindows 端： 将 Java 项目打包，并在本地使用 Docker Desktop 执行 docker build，利用你强大的电脑硬件瞬间把项目打包成一个标准的 Docker 镜像。 推送： 将这个镜像推送到云端的镜像仓库（比如阿里云的免费容器镜像服务，或者 Docker Hub）。 VPS 端： 服务器只需要执行 docker pull 拉取现成的镜像，然后直接 docker run 跑起来。 核心优势： * 保护 VPS： VPS 只负责最轻松的“运行”环节，完全不需要消耗资源去编译和构建，最大化利用了你小服务器的有限性能。 效率极高： 把构建镜像的压力全部转移到本地电脑，速度极快。 版本控制： 你的云端镜像仓库里会有项目不同版本的镜像，如果新版本在 VPS 上跑出问题，随时可以一键回滚到旧版本。 ","date":"2026-04-27","externalUrl":null,"permalink":"/posts/tech-extras/cs-choose/","section":"系列","summary":"方案一：Windows 不装 Docker # 这种方式下，你的 Windows 只负责写代码和生成基础的打包文件，镜像的构建全在服务器上。\n","title":"番外：客户端和服务器谁为工作方的建议","type":"posts"},{"content":" 背景与目标 # 在完成了服务器基础的安全加固与虚拟内存分配后，环境搭建正式进入容器化阶段。本次实践的核心目标是全面拥抱 Docker。摒弃传统的“直接在宿主机装一切”的做法，转而通过编写 docker-compose.yml 文件来统一管理和启动服务 。\n这样做最大的收获在于掌握了现代化的容器化技术：无论未来如何更换服务器，只需要几行命令就能瞬间恢复所有服务，并且能保持宿主机环境极其干净 。这不仅是云服务器管理的最佳实践，也是计算机专业学生向 DevOps 迈进的关键一步。\n核心原理解析：为什么选择 Docker？ # 在本次 RackNerd VPS 的实践中，我深刻理解了**“基础设施即代码”（Infrastructure as Code）**的概念。\n传统的服务器部署，应用与宿主机的操作系统、环境变量深度绑定，迁移成本极高。而 Docker 通过将应用程序及其依赖打包成标准的“镜像（Image）”，彻底改变了这一现状。\n隔离与干净： 所有的服务都在各自的容器内运行，互不干扰，宿主机仅仅提供 Docker 运行的引擎 。 迁移逻辑重塑： 服务器搬家不再需要重新配置复杂的运行环境。只需要备份“架构图纸”（docker-compose.yml 文件）以及“核心数据”（映射到宿主机的目录）。在新服务器上，只需拉取镜像并根据图纸重新启动，即可实现分钟级的服务无损复原。 实践操作记录 (Ubuntu 24.04 环境) # 1. 规范化安装 Docker 引擎 # 为了获取最新且稳定的功能，通过添加官方 HTTPS 软件源的方式进行安装 ：\n# 1. 安装必要的依赖 sudo apt-get update sudo apt-get install ca-certificates curl # 2. 添加 Docker 官方 GPG 密钥 sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # 3. 将 Docker 仓库添加到软件源列表 (自动感知 CPU 架构与系统版本) echo \\ \u0026#34;deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) \\ $(. /etc/os-release \u0026amp;\u0026amp; echo \u0026#34;$VERSION_CODENAME\u0026#34;) stable\u0026#34; | \\ sudo tee /etc/apt/sources.list.d/docker.list \u0026gt; /dev/null # 4. 安装 Docker 及其 Compose 插件 sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 2. 编写并启动第一个容器 (Nginx) # 创建一个独立的测试目录，编写 docker-compose.yml 来映射端口并启动 Nginx 服务 ：\nservices: my-web-server: image: nginx:latest ports: - \u0026#34;8080:80\u0026#34; 启动命令：sudo docker compose up -d（-d 参数表示在后台运行，断开 SSH 后服务依然在线）。\n避坑指南（Troubleshooting） # 在首次启动容器时，遇到了典型的 YAML 解析报错：\nyaml: line 1, column 24: mapping values are not allowed in this context\n错误原因与解决方案： YAML 是一种对缩进和格式要求极为严苛的配置语言。该报错通常是因为：\n复制粘贴时带入了多余的不可见字符或说明文字。 错误地使用了 Tab 键进行缩进（YAML 规范要求必须使用空格对齐层级）。 清空文件内容，确保只有纯净的代码和正确的空格缩进后，服务成功启动。可以通过 curl http://localhost:8080 在终端验证 Nginx 的欢迎页面。\n","date":"2026-04-25","externalUrl":null,"permalink":"/posts/vps-combat/task-4-docker/","section":"系列","summary":"背景与目标 # 在完成了服务器基础的安全加固与虚拟内存分配后，环境搭建正式进入容器化阶段。本次实践的核心目标是全面拥抱 Docker。摒弃传统的“直接在宿主机装一切”的做法，转而通过编写 docker-compose.yml 文件来统一管理和启动服务 。\n","title":"任务 4：全面拥抱 Docker 与容器化部署初体验","type":"posts"},{"content":"","date":"2026-04-22","externalUrl":null,"permalink":"/tags/tmux/","section":"Tags","summary":"","title":"Tmux","type":"tags"},{"content":" 核心目标 # 操作：安装并学习使用 tmux。 收获：掌握在服务器上跑长耗时任务的能力。即使本地 SSH 突然断开，任务依然会在后台运行，不会前功尽弃。 核心工具：tmux # tmux (Terminal Multiplexer) 是现代服务端开发的必备工具，解决了远程连接中途掉线导致任务中断的痛点。\n1. 基础管理命令 # 命令 功能 sudo apt install tmux -y 安装 tmux tmux new -s \u0026lt;name\u0026gt; 创建一个名为 “\u0026lt;name\u0026gt;” 的新会话 tmux ls 查看后台正在运行的所有会话 tmux attach -t \u0026lt;name\u0026gt; 重新连回（接管）名为 “\u0026lt;name\u0026gt;” 的会话 tmux kill-session -t \u0026lt;name\u0026gt; 彻底销毁名为 “\u0026lt;name\u0026gt;” 的会话 2. 常用快捷键 (Prefix: Ctrl + b) # 在 tmux 内部，所有快捷键都需要先按前缀键 Ctrl + b，松开后再按具体的功能键：\n会话管理 d (Detach)：将当前会话挂起至后台（最常用的命令）。 窗格分屏 (Pane) %：左右分屏。 \u0026quot;：上下分屏。 方向键：在不同窗格间移动光标。 x：关闭当前窗格。 实战复盘记录 # 意外断连测试 在运行 top 监控时，经历了一次 FinalShell 意外断连。\n重新登录服务器后运行 tmux ls，发现会话仍显示为 (attached) 状态。 通过 tmux attach -t test 成功恢复现场，top 进程完全没有中断。 验证了 tmux 作为“防断线神器”的可靠性。 ","date":"2026-04-22","externalUrl":null,"permalink":"/posts/vps-combat/task-3-tmux/","section":"系列","summary":"核心目标 # 操作：安装并学习使用 tmux。 收获：掌握在服务器上跑长耗时任务的能力。即使本地 SSH 突然断开，任务依然会在后台运行，不会前功尽弃。 核心工具：tmux # tmux (Terminal Multiplexer) 是现代服务端开发的必备工具，解决了远程连接中途掉线导致任务中断的痛点。\n","title":"任务 3：熟练使用终端复用工具","type":"posts"},{"content":"","date":"2026-04-21","externalUrl":null,"permalink":"/tags/swap/","section":"Tags","summary":"","title":"Swap","type":"tags"},{"content":" Linux 服务器运维笔记：分配虚拟内存 (Swap) 避坑与实操 # 0. 背景与目标 # 在小内存（如 1GB RAM）的云服务器上运行 Java、MySQL 或进行前端构建时，物理内存极易耗尽导致进程被系统杀掉（OOM）。Swap（交换空间） 充当了“虚拟内存”的角色，是服务器在高负载下的“救命支撑”。\n本次任务目标：\n处理系统预设的 Swap 环境。 手动分配 2GiB 的 Swap 空间。 实现开机自动挂载与内核参数优化。 1. 环境预处理：清理旧空间 # 由于某些供应商（如 RackNerd）预装镜像自带了 1GiB 的 Swap，为了扩容并统一管理，先将其关闭：\n# 查看当前内存与 Swap 状态 free -h # 关闭所有当前的 Swap 空间 sudo swapoff -a 2. 核心操作步骤 # 第一步：分配空间 # 在根目录下创建一个 2GiB 的空文件。建议使用 fallocate，它比传统的 dd 命令更快。\nsudo fallocate -l 2G /swapfile 注：若提示不支持 fallocate，可改用以下命令： sudo dd if=/dev/zero of=/swapfile bs=1M count=2048\n第二步：安全权限加固 # Swap 文件涉及内存数据，必须限制仅 root 用户可读写，防止敏感信息泄露。\nsudo chmod 600 /swapfile 第三步：格式化与激活 # 将该文件转化为 Swap 格式并通知系统立即启用。\nsudo mkswap /swapfile sudo swapon /swapfile 第四步：持久化配置（开机自启） # 编辑 /etc/fstab 文件，确保服务器重启后 Swap 依然有效。\necho \u0026#39;/swapfile none swap sw 0 0\u0026#39; | sudo tee -a /etc/fstab 3. 极客优化：Swappiness 调优 # swappiness 参数（0-100）控制系统使用 Swap 的积极程度。\n默认值：60（对于小内存 VPS 来说太频繁，会导致磁盘 I/O 过高）。 建议值：10（尽可能使用物理内存，物理内存不足时才使用 Swap）。 # 临时生效 sudo sysctl vm.swappiness=10 # 永久生效 echo \u0026#39;vm.swappiness=10\u0026#39; | sudo tee -a /etc/sysctl.conf 4. 验证结果 # 最后通过以下命令确认成果：\nfree -h 预期输出： Swap 栏位的 total 应显示为 2.0Gi。\n","date":"2026-04-21","externalUrl":null,"permalink":"/posts/vps-combat/task-2-swap/","section":"系列","summary":"Linux 服务器运维笔记：分配虚拟内存 (Swap) 避坑与实操 # 0. 背景与目标 # 在小内存（如 1GB RAM）的云服务器上运行 Java、MySQL 或进行前端构建时，物理内存极易耗尽导致进程被系统杀掉（OOM）。Swap（交换空间） 充当了“虚拟内存”的角色，是服务器在高负载下的“救命支撑”。\n","title":"任务 2：分配虚拟内存 (Swap)","type":"posts"},{"content":" 0. 前言 # 拿到一台全新的 Ubuntu 24.04 服务器（RackNerd 1核/1GB）后，第一步永远不是装软件，而是“关门”。本文记录了如何通过非对称加密实现免密登录，并完成初期的安全加固。\n1. 核心原理：非对称密钥 (Asymmetric Encryption) # SSH 免密登录的核心在于**“公钥锁门，私钥开锁”**。\n公钥 (Public Key)：存放在服务器的 authorized_keys 中。它像一把打开的挂锁，谁都能看，但只能用来“锁住”信息。 私钥 (Private Key)：保存在本地电脑（如我的 ThinkBook 16+）。它是唯一的钥匙，只有它能解开公钥锁住的信息。 2. 操作指南 # 第一步：本地生成密钥对 # 在本地终端执行：\n# 推荐使用性能更优、更安全的 ed25519 算法 ssh-keygen -t ed25519 注意：如果设置了 passphrase（私钥口令），每次使用私钥时都需要输入该口令以解锁私钥。\n第二步：将公钥上传至服务器 # 通过 FinalShell 等工具，将本地 ~/.ssh/id_ed25519.pub 中的内容完整复制到服务器的 /root/.ssh/authorized_keys 文件中。\n第三步：修改 SSH 核心配置 # 编辑 /etc/ssh/sshd_config：\n修改端口：Port 22334 （避开默认端口，防御暴力扫描）。 开启密钥登录：PubkeyAuthentication yes。 禁用密码登录：PasswordAuthentication no。 3. 踩过的坑（Ubuntu 24.04 特辑） # 坑 1：修改端口后无法连接 # 原因：Ubuntu 24.04 引入了 ssh.socket 机制，默认接管了端口管理，导致直接修改 sshd_config 不生效。 对策： systemctl disable --now ssh.socket systemctl enable --now ssh.service systemctl restart ssh 坑 2：权限过于开放导致钥匙失效 # 原因：SSH 有严格的“权限强迫症”，如果文件夹权限太高，它会认为不安全而拒绝工作。 对策： chmod 700 /root/.ssh chmod 600 /root/.ssh/authorized_keys ","date":"2026-04-20","externalUrl":null,"permalink":"/posts/vps-combat/task-1-ssh/","section":"系列","summary":"0. 前言 # 拿到一台全新的 Ubuntu 24.04 服务器（RackNerd 1核/1GB）后，第一步永远不是装软件，而是“关门”。本文记录了如何通过非对称加密实现免密登录，并完成初期的安全加固。\n","title":"任务 1：配置免密登录与安全加固","type":"posts"},{"content":"","externalUrl":null,"permalink":"/en/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/en/","section":"Blowfish","summary":"","title":"Blowfish","type":"page"},{"content":"","externalUrl":null,"permalink":"/en/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/en/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/en/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]