执迷的博客 - Lovisnd BLOG - 📘 开发记录
type: channel
link: https://zhangmingrui.top/index.php/category/%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95
items: 利用 Cloudflare Tunnel 实现内网穿透与免端口访问
items: 从零开始开发 Typecho 微语插件 - Whisper
items: 黄金大涨，我给媳妇手搓了一个“定制版”黄金管理APP
items: 从手动部署到自动化：GitLab Runner 实现 CI/CD 的实战笔记
items: Gemini CLI 使用指南:将 AI 助手集成到你的终端（含网络问题与常见错误解决）
items: 向量数据库Weaviate的踩坑记录
items: Selenium 到 Playwright 的切换记录
items: Ubuntu 上使用 Docker 安装 Weaviate 教程
items: 前端项目一键部署至Windows服务器
items: Docker部署项目指南

利用 Cloudflare Tunnel 实现内网穿透与免端口访问
type: item
date: 2026-03-18T11:40:00+08:00
description: 🤖AI摘要此内容根据文章生成，不代表个人观点，仅用于文章内容的解释与总结昨晚心血来潮写了个页面，准备部署到 VPS 上给媳妇展示一下。考虑到机器配置较低（1H1C），且已经运行了其他服务，不想再额外折腾 Nginx 反向代理和 SSL 证书。经过一番研究，发现 Cloudflare 提供的 Cloudflare Tunnel 完美解决了我的痛点。它本质上是一种内网穿透技术：在 VPS 上安装客户端建立隧道，CF 侧直接配置路由。使用体验：无需公网 IP/开放端口： 甚至连防火墙都不用配置，安全性极高。自带 SSL： 自动分配证书，免去配置 HTTPS 的烦恼。部署丝滑： 尤其适合海外机器，几条指令搞定。核心流程如下：1. 创建隧道登录 Cloudflare 控制台，进入 Networks -> Tunnel，点击创建。2. 设置名称为你的隧道起个好记的名字。3. 环境部署根据 VPS 系统选择对应的安装指令，复制运行即可。4. 确认连接看到隧道状态显示为“已连接（Healthy）”即可进行下一步。5. 配置路由规则选择 Public Hostname。6. 绑定域名输入二级域名，服务地址指向本地端口（注意：通常使用 localhost:端口号）。搞定！ 现在可以直接通过域名访问服务了，配置过程非常省心。

从零开始开发 Typecho 微语插件 - Whisper
type: item
date: 2026-02-04T15:44:00+08:00
description: 🤖AI摘要此内容根据文章生成，不代表个人观点，仅用于文章内容的解释与总结一个简洁优雅的 Typecho 微语（说说/动态）插件，记录生活中的点滴想法。前言最近快年底了，公司不是很忙，终于有时间折腾自己的博客了。前几天收到 @流情 的留言，于是去他的博客回访了一下，发现了很多有趣的创意。其中最吸引我的就是微语这个模块，类似朋友圈或者说说，可以随时记录一些简短的想法和心情。看着流情博客上那些简洁优雅的微语卡片，我心想：这个功能太棒了！虽然市面上可能有类似的插件，但作为一个爱折腾的开发者，我决定自己动手实现一个，顺便加入一些自己的想法。经过几天的开发和调试（以及无数次的样式调整😂），终于完成了这个插件。从最初的基础功能，到后来加入的图片上传、表情选择、点赞功能，每一个细节都经过了反复打磨。现在已经开源到 GitHub 了，欢迎大家使用和 Star！项目地址： GitHub - Whisper 在线演示： 我的微语页面功能规划在开始开发之前，我先梳理了一下想要实现的功能：核心功能✅ 发表微语（支持多行文本）✅ 图片上传（支持多图）✅ 表情选择✅ 点赞功能✅ 删除管理界面设计✅ 清新简洁的卡片式设计✅ 暗黑模式适配✅ 响应式布局✅ 流畅的动画效果用户体验✅ 图片灯箱预览✅ 智能时间显示✅ 设备信息识别✅ 分页浏览开发过程第一步：搭建基础框架作为一个 Typecho 插件，首先要遵循它的开发规范。Typecho 插件需要实现 Typecho_Plugin_Interface 接口，这是最基本的要求：class Whisper_Plugin implements Typecho_Plugin_Interface { public static function activate() { // 插件激活时执行：创建数据表、注册路由等 } public static function deactivate() { // 插件禁用时执行：清理路由等 } public static function config(Typecho_Widget_Helper_Form $form) { // 插件配置界面：添加各种配置项 } }这个接口定义了插件的生命周期，让我们可以在激活、禁用时执行特定的操作。第二步：设计数据库微语需要存储的信息包括：内容、图片、点赞数、发表时间等。经过思考，我设计了这样的表结构：CREATE TABLE `typecho_whispers` ( `id` int(11) NOT NULL AUTO_INCREMENT, `content` text NOT NULL, -- 微语内容 `images` text, -- 图片地址（逗号分隔） `os_info` varchar(100), -- 操作系统信息 `likes` int(11) DEFAULT 0, -- 点赞数 `created_at` int(11) NOT NULL, -- 发表时间戳 `user_id` int(11) NOT NULL, -- 用户ID PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;在插件激活时自动创建表，并且做了兼容处理：如果表已存在，会检查是否缺少 likes 字段（这是后来加的功能），如果缺少就自动添加。这样即使用户升级插件，也不会出现字段缺失的问题。public static function activate() { $db = Typecho_Db::get(); $prefix = $db->getPrefix(); // 创建表 $sql = "CREATE TABLE IF NOT EXISTS `{$prefix}whispers` (...)"; $db->query($sql); // 检查并添加 likes 字段（兼容旧版本） $hasLikes = false; $columns = $db->fetchAll($db->query("SHOW COLUMNS FROM `{$prefix}whispers`")); foreach ($columns as $column) { if ($column['Field'] == 'likes') { $hasLikes = true; break; } } if (!$hasLikes) { $db->query("ALTER TABLE `{$prefix}whispers` ADD `likes` int(11) DEFAULT 0"); } // 注册 Action 路由 Helper::addAction('whisper', 'Whisper_Action'); }这里有个小插曲：最初…

黄金大涨，我给媳妇手搓了一个“定制版”黄金管理APP
type: item
date: 2026-01-12T16:35:00+08:00
description: 🤖AI摘要此内容根据文章生成，不代表个人观点，仅用于文章内容的解释与总结最近这金价，真是涨得让人心惊肉跳，稍微关注点财经的朋友应该都感受到了。我媳妇也不例外，天天抱着她那些宝贝首饰在那算。前两天我发现她一直盯着一个小程序看（名字叫“金XX”，懂的都懂），是个记录黄金资产和看走势的软件。我看她在那戳了半天，最后在那吐槽：“这软件记个东西还得买会员？好多功能都得收费，真够黑的。我当时正好手头工作忙得差不多了，心血来潮凑过去瞅了一眼。嘿，这逻辑也没多复杂嘛！我直接回了她一句：“别充钱了，这玩意儿还用收费？你等着，我这两天给你复刻一个，功能全免费，还只给你一个人用。”说干就干，程序员的浪漫（或者说强迫症）说来就来。1. 搞定底层：数据爬虫首先得解决最核心的问题——实时金价。这种数据网上到处都是，但也得找个稳定的。我花了点时间研究了一下几个网页的接口，用 Python 写了个小爬虫直接抓取。稍微破解了一下它的接口参数，金价和历史走势图的数据就源源不断地进来了。2. 复刻UI：AI 助攻既然是给媳妇用的，颜值肯定不能太差。这次我偷了个懒，把最近用得贼顺手的 AI 给派上用场了。我把原版软件的截图往里一扔，让它帮我搭建基础框架和前端代码。不得不说，现在的 AI 辅助确实猛，省掉了我大量写琐碎 UI 的时间。我再根据媳妇的审美细节微调了一下，整个页面的还原度直接拉满：首页： 实时金价（Au99.99）置顶，涨跌幅一目了然。走势： 7天、15天、30天的曲线图，该有的都有。记录： 攒金明细、平均克价、预估收益……原版收费的功能，我全给安排上了。3. 快速上线：先打个安卓包本来想搞小程序，但一想现在小程序上架还得各种备案、审核，流程太繁琐，我这心血来潮的火苗等不及。于是直接打包成一个 安卓 APK。装到媳妇手机上的那一刻，成就感简直比做成一个大项目还强。媳妇试了一下，直夸好用，看我的眼神都带光了，哈哈！一点感悟其实这个项目技术含量不算特别高，但在工作之余，能用自己擅长的技术给家人解决一点小烦恼，这种感觉真的挺棒的。这也算是一种程序员式的“解压”方式吧——不为了什么KPI，纯粹是为了爱好和家庭，折腾点有意思的东西。下一步打算：媳妇已经开始提新需求了，比如加入不同金店的金价，看来这个“定制版”还得持续更新啊。

从手动部署到自动化：GitLab Runner 实现 CI/CD 的实战笔记
type: item
date: 2025-09-29T16:23:00+08:00
description: 🤖AI摘要此内容根据文章生成，不代表个人观点，仅用于文章内容的解释与总结之前我们公司的项目都是靠手动部署：打包、拷贝、登录服务器、重启服务……一套流程下来经常要二三十分钟，而且每次发版都容易手忙脚乱。为了减少这种重复劳动，我决定在项目里接入 GitLab CI/CD，把部署自动化。下面是我在折腾 GitLab Runner 的完整过程，包含安装、配置、免密登录，以及 .gitlab-ci.yml 的实际案例，还有一些踩坑记录。算是一次实战笔记，写下来给自己留个档，也希望能帮到有类似需求的人。一、GitLab Runner 安装与配置1. 在 Linux 服务器上安装 GitLab Runner安装 Runner 其实很简单，官方提供了一键脚本：# 添加 GitLab Runner 官方仓库 curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash # 安装最新版本的 GitLab Runner sudo yum install gitlab-runner -y # 如果是 Ubuntu/Debian，用 apt 安装 curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash sudo apt-get install gitlab-runner -y我个人习惯直接用官方源，省事儿，不用再去找 rpm 或 deb 包。2. 注册 GitLab Runner装好 Runner 后，要让它和 GitLab 绑定。这里用 gitlab-runner register：sudo gitlab-runner register一路输入配置信息：GitLab instance URL: http://10.0.5.169/Registration token: 在项目设置 -> CI/CD -> Runners 里获取Description: 比如 Production Deployment RunnerTags: 我用了 deploy,production,warExecutor: 我选了 shell，本地执行脚本最简单第一次填的时候有点紧张，生怕输错 😂，不过输错了也没事，重新来一次就好。（~后面发现gitlab runner里面包含了指令 直接复制就好）2.1 创建部署用户（deploy）为了安全起见，我没有直接用 root，而是单独创建了一个 deploy 用户：sudo useradd -m -s /bin/bash deploy sudo passwd deploy给它加 sudo 权限：# Ubuntu/Debian sudo usermod -aG sudo deploy # CentOS/RHEL sudo usermod -aG wheel deploy第一次忘了加 sudo，结果 Runner 脚本执行的时候各种权限错误，只能回头补上。2.2 配置 sudo 免密码（可选）有些命令（比如重启 Tomcat）如果每次都要输入密码，会很麻烦。于是我给 deploy 用户加了免密码配置：sudo visudo在文件末尾添加：deploy ALL=(ALL) NOPASSWD: /opt/tomcat9/bin/*, \ /usr/bin/systemctl restart tomcat*, \ /usr/bin/systemctl stop tomcat*, \ /usr/bin/systemctl start tomcat*这里千万不要图省事直接给 ALL，只开放必要命令就行。3. 配置 SSH 免密登录Runner 需要免密登录到部署服务器。先在 Runner 机器上切换到 gitlab-runner 用户并生成 SSH 密钥：sudo -u gitlab-runner -H bash ssh-keygen -t rsa -b 2048 -N "" -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub exit然后把公钥加到部署服务器的 authorized_keys：ssh deploy@10.0.5.169 mkdir -p ~/.ssh && chmod 700 ~/.ssh echo "粘贴上面的公钥" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys更省事的方式：sudo -u gitlab-runner ssh-copy…

Gemini CLI 使用指南:将 AI 助手集成到你的终端（含网络问题与常见错误解决）
type: item
date: 2025-06-26T19:35:00+08:00
description: Gemini CLI 使用指南：将 AI 助手集成到你的终端（含网络问题与常见错误解决）对于开发者而言，命令行终端是我们最常打交道的工具之一。如果在终端中能直接调用强大的 AI 模型，无疑会极大地提升工作效率。Google 推出的 Gemini CLI 就是这样一款工具，它能将 Gemini 的能力无缝集成到你的命令行工作流中。本文将详细介绍 Gemini CLI 的安装、配置过程，并分享我在使用中遇到的关键问题及其解决方案。一、准备工作：Node.js 环境在安装 Gemini CLI 之前，请确保你的系统中已经安装了 Node.js。这是其运行的基础环境。你可以通过在终端执行以下命令来检查：node -v如果未安装，请前往 Node.js 官网 下载并安装 LTS (长期支持) 版本。二、安装 Gemini CLI安装 Gemini CLI 有两种方式：临时使用 (npx)：这种方式无需全局安装，适合快速体验。每次使用时都会从网络获取最新版本。npx @google/gemini-cli全局安装 (npm)：（推荐） 对于长期使用者，建议采用全局安装。这样，你就可以在系统的任何路径下通过 gemini 命令直接启动它。npm install -g @google/gemini-cli三、关键配置：网络代理重要提示： Gemini CLI 在首次启动和后续使用中需要验证 Google 账户，相关服务在国内无法直接访问。因此，必须为你的终端配置代理。这是初次配置时最容易遇到的障碍。我的解决方案如下：使用 Clash 等代理工具，并开启 TUN 模式。该模式可以接管系统级的网络请求，从而覆盖到终端环境。启动 TUN 模式后，在终端（如 Windows Terminal 内的 PowerShell）中执行后续命令。当 gemini 命令成功启动并弹出浏览器进行 Google 账户授权时，说明网络配置正确。四、初始化设置首次成功授权后，Gemini CLI 会引导你完成几个简单的初始化配置。选择主题：根据个人偏好选择一个命令行界面的颜色主题。选择认证方式：Sign in with your Google Account: 我选择此项。通过浏览器登录 Google 账户完成认证，流程简单直观。Use an API Key: 你也可以前往 Google AI Studio 生成一个 API 密钥，并在此处粘贴。这种方式更适合自动化脚本或无浏览器环境。完成这些步骤后，你就可以正式开始使用了。五、核心用法在终端输入 gemini 并回车，即可进入交互模式。其输入方式非常灵活：直接输入问题：进行常规的问答对话。@ 符号：分析文件与目录：这是 Gemini CLI 的一个核心功能，可以直接引用本地文件或文件夹作为上下文。示例：@./src/utils.js 解释这段代码的功能。示例：@./project_docs 总结这个目录下的所有文档。/ 命令：使用内置功能：输入 / 可以查看并使用内置命令，如：/theme: 切换主题。/editor: 开启多行编辑模式，方便粘贴和编辑大段代码或文本。/help: 查看帮助文档。! 符号：结合 Shell 命令：将标准 Shell 命令的输出结果作为输入，直接传递给 Gemini 进行处理。示例：!git diff | gemini 为这些变更生成一个合适的 commit message。示例：!ls -l | gemini 解释这些文件权限的含义。六、常见问题排查：解决自动退出在使用过程中，你可能会遇到 Gemini CLI 运行一段时间后自动退出的问题。原因分析：该问题是由于 Gemini CLI 默认会定时发送匿名的使用情况统计（Telemetry）到 Google 服务器。在网络不稳定的情况下，该请求失败会导致程序异常终止。解决方案：关闭该统计功能即可。找到配置文件 settings.json。Windows: C:\Users\<你的用户名>\.gemini\settings.jsonmacOS/Linux: ~/.gemini/settings.json使用文本编辑器打开该文件，添加以下字段并保存："usageStatisticsEnabled": false修改配置后，重新启动 gemini，程序即可稳定运行。总结通过以上步骤，你就可以将 Gemini CLI 顺利地集成到日常的开发工作中。它能够有效减少在不同应用间切换的频率，无论是代码解读、文档生成还是命令查询，都能在终端内一站式完成。希望这篇指南能帮助你顺利地使用 Gemini CLI。如果你有其他使用技巧，也欢迎交流。

向量数据库Weaviate的踩坑记录
type: item
date: 2025-06-17T09:39:02+08:00
description: 向量数据库迁移实录：从 FAISS 到 Weaviate 的那些坑最近因为需要支持多用户知识库检索，我开始琢磨向量数据库的替换方案。原来系统里用的是本地的 FAISS，搭配 LangChain 用起来还挺顺，但随着需求复杂起来，比如想加 hit\_count 记录、分页、精确查某个文件的所有块，FAISS 显然就有点捉襟见肘了。🥊 为什么考虑换掉 FAISS？简单说两点：FAISS 不支持原生 filter 查询，像我要查某个 kb_id 的内容，或模糊查某个 filename 的所有内容，只能自己手动过滤；没有内置分页。每次都得查全量，再手动分页，这在数据量大时非常不友好。于是我决定换掉 FAISS，试试 Weaviate。🔁 切换 Weaviate 的过程✅ 基本查询Weaviate 的向量检索写法和 FAISS 有点类似，只是接口风格更“声明式”一点。最开始试了下这样一段代码：results = collection.query.near_vector( near_vector=query_vector, limit=5, filters=Filter.by_property("kb_id").equal(kb_id) )这一段是查询向量最相近的内容，且只筛选 kb_id 为指定值的记录。比起 FAISS 自己 filter 一堆，Weaviate 这点真的太方便了。📈 查询命中的内容并计数接下来加了一个需求：一旦某个块被召回了，就把它的 hit_count 字段 +1。这其实也很方便：for item in results.objects: collection.data.update( uuid=item.uuid, properties={"hit_count": item.properties.get("hit_count", 0) + 1} )注意这里 item.uuid 是默认带的，不需要你在 return_metadata 里特地加上（我一开始就踩了这个坑）。🧩 模糊查询 + 分页后来又要实现：用户选择一个知识库、一个文件名，分页查看里面所有块，还能模糊查内容。一开始我尝试构造 filters：filters = { "operator": "And", "operands": [ {"path": ["kb_id"], "operator": "Equal", "valueText": kb_id}, {"path": ["filename"], "operator": "Equal", "valueText": filename}, {"path": ["text"], "operator": "Like", "valueText": f"*{query}*"} # 模糊 ] }但后来我发现 Weaviate 新版 SDK 提供了更优雅的 Filter 方式：from weaviate.classes.query import Filter filters = ( Filter.by_property("kb_id").equal(kb_id) & Filter.by_property("filename").equal(filename) & Filter.by_property("text").like(f"*{query}*") # 支持模糊 )这个组合可以直接传给 collection.query.fetch_objects(...) 方法，分页也只需设置 limit 和 offset：offset = (page - 1) * pageSize result = collection.query.fetch_objects( filters=filters, limit=pageSize, offset=offset, return_properties=["text", "hit_count", "upload_time", "filename", "kb_id"] )❌ 踩坑记录不能插入 id 和 vector 到 properties批量插入时我傻傻地把 id 和 vector 放进了 properties 字典里，Weaviate 直接报错：It is forbidden to insert id or vector inside properties...后来我改成使用 DataObject 的方式解决了。return\_metadata=["uuid"] 会报错我以为想取 UUID 要写 return_metadata=["uuid"]，结果报了个特别诡异的错误：Extra inputs are not permitted [type=extra\_forbidden, input…

Selenium 到 Playwright 的切换记录
type: item
date: 2025-06-17T09:18:00+08:00
description: 从 Selenium 到 Playwright：我的浏览器控制切换之旅最近在折腾一个远程浏览器自动化的项目，最开始是用 Selenium 控制浏览器执行一系列开锁流程，后来因为种种问题，最终还是换成了 Playwright。这里简单记录一下整个过程中遇到的一些坑、思考和切换过程。初始方案：Selenium 控制浏览器一开始整个系统的架构是：多线程管理浏览器池（初始化多个浏览器放进浏览器池）每个浏览器执行任务：打开页面 → 登录 → 操作页面 DOM控制流程主要使用 find_element, click, send_keys 等操作Selenium 用着也没啥大问题，配合 Chrome + chromedriver（chromedriver版本很重要），流程基本能跑通。但随着任务量增加、并发变多、页面交互越来越复杂，问题也逐渐暴露出来：有时候页面切换 iframe 很慢，Selenium 报错找不到元素弹窗不稳定，处理逻辑需要各种 try/except多线程下的浏览器状态管理很混乱，有时线程没结束浏览器还开着于是我开始思考，有没有更现代、更轻量的替代方案？Playwright 更香？Playwright 这个名字其实早听说过，一直没仔细研究。这次真碰到实际问题了，就开始试着改一下逻辑，把 Selenium 整体替换成 Playwright。我用了它的 同步模式（sync_playwright()），主要是因为现有代码结构基于同步逻辑，异步改起来太麻烦。改造的重点包括：启动浏览器流程：从 webdriver.Chrome() 换成 p.chromium.launch(...)页面控制逻辑：换成 Playwright 的 locator, click, fill, frame() 等 API最关键：不再直接返回 page 对象，而是封装成 LockIndex.enter() 返回 browser，保持浏览器常驻最大的坑：Playwright 不能跨进程传 browser 对象这点一开始没注意。在 Selenium 模式下，浏览器 driver 可以传来传去（虽然也不推荐）。但 Playwright 的 browser 对象不能跨进程传递，哪怕你 pickle 了也会报错。我原本在主进程调度任务，然后把 browser 对象传过去执行操作，结果直接报错。于是我重构了架构：新方案：多进程 + Playwright 控制最终的架构大致如下：主进程 管理多个任务队列（一个队列对应一个浏览器进程）每个子进程 启动自己的浏览器，进入锁控页面，接收任务 → 执行 → 回传结果调度器 轮询分发任务，比如当前是第 0 个浏览器空闲，就发给它def dispatch_task(task): task_queues[current_index].put(task) current_index = (current_index + 1) % len(task_queues)任务本质上是“给某个 code 和 id 做一次 DOM 操作”。浏览器常驻，执行完一个任务继续待命。这个结构的好处：每个浏览器独立管理，状态更清晰多进程隔离，互不干扰Playwright 控制页面的效率、容错能力都明显强于 Selenium最后一点感慨其实如果不是这次遇到实际需求，我也不会下定决心从 Selenium 迁移。现在的架构逻辑更清晰了，浏览器也更稳定了，维护起来也更方便。小结如果你还在用 Selenium 控制浏览器，建议试试 Playwright。使用多进程时，不要跨进程传 page、browser 对象。合理设计任务调度和进程架构，能让系统更稳定。希望这篇文章能帮到也在做浏览器自动化的朋友们。如果你也在做类似项目，欢迎留言交流。

Ubuntu 上使用 Docker 安装 Weaviate 教程
type: item
date: 2025-06-06T10:02:13+08:00
description: Ubuntu 上使用 Docker 安装 Weaviate 教程在搭建自己的 RAG 知识库系统时，最初我是用 FAISS 来做向量检索的。虽然运行起来很快，但数据是存在本地磁盘文件中，维护和迁移都不太方便。特别是当系统稍微复杂一些，比如要做远程调用、版本升级或者支持多端访问时，这种方式就显得有些力不从心了。后来我了解到了 Weaviate，一个功能比较完善的开源向量数据库，支持 REST 和 gRPC 接口，数据结构更像真正的数据库，有 schema 管理，也适合做多类数据的组织，于是就决定切换过来。这篇文章就是记录一下我在 Ubuntu 上用 Docker 安装 Weaviate 的过程，并用 Python 客户端初始化 schema、插入数据做了基本验证。希望对你有帮助。一、安装 Docker首先确保你的系统中安装了最新版本的 Docker。1. 清理旧的 Docker 源（如有）sudo rm /etc/apt/sources.list.d/docker.list添加新的 GPG 密钥和阿里云源curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list安装 Docker 组件sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io验证安装是否成功sudo docker version二、配置并运行 WeaviateWeaviate 可以直接通过 Docker 启动运行。你可以选择使用 docker run 命令或编写 docker-compose.yml 文件进行管理。这里先介绍更灵活的方式：直接运行容器。拉取镜像（或在有网络环境下打包传输）docker pull semitechnologies/weaviate:1.27.15如服务器无外网，可在本地执行：docker save -o weaviate.tar semitechnologies/weaviate:1.27.15然后将 weaviate.tar 文件复制到目标服务器，执行：docker load -i weaviate.tar启动容器docker run -d --name weaviate \ -p 8003:8080 -p 50052:50052 \ -e QUERY_DEFAULTS_LIMIT=25 \ -e AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true \ -e PERSISTENCE_DATA_PATH=/var/lib/weaviate \ -e DEFAULT_VECTORIZER_MODULE=none \ -e ENABLE_MODULES= \ -e GRPC_ENABLED=true \ -e GRPC_PORT=50052 \ semitechnologies/weaviate:1.27.15✅ 提示：端口可根据需求自行调整。启动成功后，可通过浏览器访问 http://localhost:8003/v1/meta 查看服务是否正常。三、创建 Schema（重点）Weaviate 的 Schema（类定义）是系统的核心，直接关系到你后续向量数据的组织与查询方式。⚠️ 注意：Weaviate 服务端版本与 Python 客户端版本必须匹配，否则可能报错或方法不可用。当前版本：Weaviate Server：1.27.15Python SDK：weaviate-client==4.15.0安装 Python 客户端pip install weaviate-client示例代码：连接 Weaviate 并创建 Schemaimport weaviate from weaviate.classes.config import Configure, Property, DataType from datetime import datetime连接本地 Weaviate 实例client = weaviate.connect_to_local( port=800…

前端项目一键部署至Windows服务器
type: item
date: 2024-09-18T15:57:00+08:00
description: 🤖AI摘要此内容根据文章生成，不代表个人观点，仅用于文章内容的解释与总结在公司中，我负责管理前后端代码的部署。随着项目数量的增加，我发现手动部署过程不仅耗时，而且容易出错。为了解决这个问题，我决定寻找一个自动化部署工具。经过一番搜索，我发现了 deploy-cli-service，但它主要支持Linux服务器。由于我们公司使用的是Windows服务器，我决定自行修改源码以适应Windows环境。以下是我修改和部署的详细步骤：步骤1：评估现有工具我发现 deploy-cli-service 工具不支持Windows服务器，因此决定对其进行修改。步骤2：修改源码主要问题集中在SFTP操作和文件路径处理上，因为Windows和Linux在这些方面存在差异。步骤3：编写备份和部署脚本我编写了一个函数 backupRemoteFile 来处理备份、删除和解压的过程。const backupRemoteFile = async (config, index) => { try { const { windowsPath, bakDir } = config; const dirName = 'dist'; const zipFileName = `${dirName}_${dayjs().format('YYYY-MM-DD_HH-mm-ss')}.zip`; console.log(`(${index}) 备份远程文件 ${underline(windowsPath)}`); const path = 'D:\\soft\\nginx-1.23.4\\nginx-1.23.4\\html'; const destination = `${bakDir}\\${zipFileName}`; await ssh.execCommand(`cmd /c "cd /d ${windowsPath} && powershell -command "Compress-Archive -Path '${windowsPath}\\*' -DestinationPath '${destination}'; Get-ChildItem -Path . -Exclude 'dist.zip' | Remove-Item -Recurse -Force; Expand-Archive -Path 'dist.zip' -DestinationPath .; Remove-Item -Path 'dist.zip' -Force""`); succeed('备份成功'); console.log(`备份成功，备份至 ${underline(`${bakDir}\\${zipFileName}`)}，且原文件已被删除`); } catch (e) { console.error(e); process.exit(1); } };步骤4：测试修改在本地环境和测试服务器上测试修改后的脚本，确保其按预期工作。步骤5：打包和上传项目将整个项目打包，并上传到npm，以便其他开发者可以使用。资源链接npm包地址：deploy-cli-win-serviceGitHub仓库：lovisnd/deploy-cli-win-service结语通过这些步骤，我不仅提高了部署效率，还学习到了如何跨平台修改和优化工具。我希望我的经验能够帮助其他面临类似挑战的开发者。如果你对这个过程有任何疑问或建议，请随时联系我。

Docker部署项目指南
type: item
date: 2024-07-15T11:41:00+08:00
description: 最近，客户要求将项目部署在Docker上，因此我开始学习并了解Docker的部署流程。下面是我整理的部署步骤：1. 安装Docker首先，在Ubuntu系统上安装Docker：sudo snap install docker2. 创建Dockerfile新建一个Dockerfile文件，并添加以下内容：# 使用官方Ubuntu镜像作为基础镜像 FROM ubuntu # 设置时区 RUN echo "Asia/Shanghai" > /etc/timezone ENV LANG C.UTF-8 ENV LC_ALL C.UTF-8 # 更新软件包列表并安装所需软件包 RUN apt-get update \ && apt-get install -y --no-install-recommends \ openjdk-8-jdk \ python3 \ python3-pip \ && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y libgomp1 # 使用pip3安装所需的Python库，并指定镜像源为阿里云 RUN pip3 install -i https://mirrors.aliyun.com/pypi/simple/ numpy==1.21.2 pandas==1.3.3 joblib==1.1.1 lightgbm==2.2.3 # 设置Java版本环境变量 ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 # 设置工作目录 WORKDIR /app # 将Java可执行Jar文件复制到容器中 COPY test.jar app.jar COPY code/ /app # 设置容器启动时执行的命令 CMD ["java", "-jar", "app.jar"]3. 传输文件将打包好的jar文件和Dockerfile一起传输到Ubuntu系统上。4. 创建Docker镜像进入包含Dockerfile的文件夹，并执行以下命令来创建Docker镜像：docker build -t nhgl .5. 查看镜像使用以下命令查看是否成功创建了镜像：docker images6. 运行镜像运行镜像，并将内部端口9900映射到宿主机的10000端口上：docker run -d -p 10000:9900 test7. 查看运行情况查看镜像运行情况：docker ps -a8. 进入镜像使用以下命令进入镜像：docker exec -it [容器ID或容器名] /bin/bash9. 镜像保存根据ID将镜像保存成一个文件：docker save 镜像ID > test.tar以上就是使用Docker部署项目的详细步骤。希望对同样在学习Docker部署的你有所帮助。