Chemmy's Blog

chengming0916@outlook.com

一、前言:项目选型背景与核心避坑说明

政企涉密内网、私有智能知识库、本地化RAG赋能、离线智能问答全场景AI项目中,研发人员习惯用Ollama快速搭环境、用LM Studio本地调试大模型。两类工具上手零门槛、轻量化易跑,仅适合个人算法验证、前期功能快速摸底,绝对不允许直接投产商用业务、涉密机房、多节点生产集群,线上高危隐患无法闭环规避。

结合B站实测拆解视频及政企多轮等保合规复盘实测佐证:Ollama存在高危无鉴权漏洞、底层llama.cpp核心依赖隐匿不声明、上层封装额外损耗GPU算力、黑盒运维无可控调度、缺失全链路集群审计能力五大生产硬伤,规模化上线极易出现算力被恶意劫持、核心模型外泄、业务明文数据爬取、开源版权连带追责等重大线上事故,完全不适合政企常态化商用投产。LM Studio仅桌面端可视化调参,无后台常驻守护、无集群分布式调度、无内网高可用服务能力,不具备服务器正式投产价值。

  • 高危可利用无鉴权安全漏洞:Ollama 默认全端口裸暴露,未内置身份账号鉴权、无内网IP白名单联动、无接口访问权限隔离,属于典型高危弱安全组件。一旦服务器公网/内网端口意外放行,攻击者可直接非法占用全部GPU算力、批量窃取私有化定制大模型权重、爬取全量业务对话及检索明文数据;叠加历史远程代码执行高危漏洞,恶意构造模型载荷即可横向渗透整机服务器权限,政企核心业务资产面临批量篡改、劫持、外泄不可逆风险。

  • 底层核心依赖刻意隐匿,触碰开源合规红线:全网技术溯源拆解可实锤,Ollama 全套GPU推理调度内核、GGUF通用模型解析引擎、CUDA显存异构调度逻辑,均是原地深度复用llama.cpp开源底层工程,属于标准二次封装衍生产品。但官方长期在商用说明、项目依赖清单、架构白皮书、交付合同附件中刻意弱化、完全隐匿llama.cpp开源基座溯源关系,未合规标注开源版权归属。在政企等保合规测评、商业AI项目验收、开源资产审计、软件正版化核查场景中,极易触发开源协议违约、知识产权追溯、项目停工整改、商用连带赔付追责等合规事故。

  • 多层中转封装冗余,实测GPU算力无效损耗严重:同卡、同驱动、同量化模型对标实测可直观验证,Ollama多一层中间转发调度层,固定产生协议封装、线程中转、显存二次拷贝冗余开销。同等NVIDIA硬件条件下,推理吞吐低于原生llama.cpp直连架构,显存碎片率更高、延迟更大,高并发对话、批量向量编码、多模型混跑场景极易出现接口卡顿、超时雪崩、算力资源浪费问题。

  • 全链路黑盒闭源,生产运维完全失控:显存分片阈值、推理线程池配比、接口QPS限流内核、多模型资源隔离、异常熔断策略全部黑盒固化,运维无法可视化调优算力配额、无法隔离业务优先级、无法定位性能瓶颈。多业务混跑共用一台GPU服务器时,边缘小业务极易抢占核心智能问答、核心检索算力,引发全链路业务雪崩。

  • 无标准化容器运维体系,不满足机房集群上线规范:原生无容器编排适配、无结构化日志归集、无秒级故障自愈重启、无GPU资源告警联动、无集群负载均衡适配,完全不符合政企机房7×24小时高可用值守、等保日志审计、批量集群运维硬性交付标准。

  • LM Studio 仅面向个人桌面端做可视化模型调试,无后台守护进程、无内网服务能力、无集群调度能力,只能做算法效果摸底,不具备任何企业服务器投产落地资质。

与此同时,现阶段政企涉密机房、信创专属内网、国产化AI集群已批量下线传统Docker架构:Docker依赖中心守护进程单点运行、内核权限耦合过高、容器逃逸风险防控难度大、不符合无根安全等保基线、常驻资源开销偏高,规模化集群运维合规卡点多。反观Podman,天然兼容标准Compose编排语法、无守护进程无单点隐患、原生无根安全隔离、内核级安全加固、轻量化资源占用更低,无缝适配政企全场景私有化合规上线。综合安全、合规、算力性能、运维成本全维度研判,llama.cpp + Podman + CUDA GPU 是当前本地化离线部署全品类大模型的统一生产级最优方案:不限对话大模型、不限向量嵌入大模型、不限行业私有化定制底座,一套环境全部通用。

✅ 无守护进程架构、原生无根隔离运行,深度贴合政企等保安全合规基线 ✅ 直通 CUDA 硬件GPU零旁路加速,全链路推理无额外损耗,算力利用率拉满 ✅ 原生兼容 OpenAI 标准统一接口,无缝对接对话业务、RAG知识库、检索中台全场景 ✅ 轻量化低显存弹性适配,通用于通用对话大模型、行业专属大模型、向量嵌入模型 ✅ 全内网离线闭环隔离部署,模型权重、业务数据、对话日志全程不出机房 ✅ 兼容通用 Compose 编排 + 兼容Docker全量CLI命令,迁移零改造成本、运维零学习门槛

下文以BGE-M3向量模型作为通用部署示例,完整演示全流程容器化GPU调度;实际生产中可直接替换任意GGUF格式:通用对话大模型、办公专属大模型、行业垂直大模型、各类向量嵌入底座,配置无需改动、脚本无需重写、环境一键复用。


二、前置硬性环境检查(生产必核验,Ubuntu/Windows双系统适配)

1. 硬件资源准入标准

  • 推理显卡:全系 NVIDIA 独立算力显卡(RTX20系/30系/40系/工业A系列全覆盖,适配全品类大模型常态化GPU推理负载)

  • 最低显存:≥4GB 物理显存,可稳定适配主流轻量化对话大模型、全量向量嵌入模型常态化常驻推理

  • 服务器内存:≥8GB 物理内存,规避大模型权重频繁内存换页、接口抖动、突发卡顿问题

2. 软件依赖环境标准化配置(双系统差异化适配)

  • 容器底座:仅需安装Podman 命令行核心 + Podman ComposePodman Desktop 图形界面不是必装组件,生产服务器全程无GUI纯命令行运维,轻量化无冗余开销;Ubuntu(Linux)生产服务器 / Windows调试工作站按需部署,全平台编排语法统一。

  • 硬件驱动:NVIDIA 官方完整版生产级独显驱动稳定版(无需额外部署宿主机 CUDA Toolkit 开发套件,降低运维复杂度)

  • 核心赋能组件:NVIDIA Container Toolkit(系统强差异化依赖);Ubuntu 服务器必须离线手工完整安装赋能依赖包,Windows 桌面端装好完整版显卡驱动即可自动联动授权,一键直通GPU算力,无需额外复杂配置

3. GPU硬件连通性预检命令(提前排障,杜绝上线翻车)

机房运维提前按实际操作系统执行对应预检命令,纯命令行快速核验GPU硬件直通链路、CUDA调度链路是否完好连通,无需桌面可视化工具,规避跨系统部署适配翻车:

Ubuntu(Linux生产服务器)专属预检命令

1
podman run --rm --gpus all nvidia/cuda:12.2-base nvidia-smi

Windows 工作站专属预检命令(PowerShell 管理员模式运行)

1
podman run --rm --gpus all nvidia/cuda:12.2-base nvidia-smi

✅ 正常回显显卡型号、已用显存、总显存、驱动版本、CUDA可用状态 → GPU环境就绪可直接投产 ❌ 提示无GPU设备、runtime识别异常 → 优先重装对应系统NVIDIA容器赋能工具包,修复后再推进编排部署


三、资源前置准备:模型统一规范管理 + 标准化目录规划

1. 示例说明与全域模型适配能力解读

本文全程采用 bge-m3.Q6_K.gguf 作为环境部署演示示例,仅用于跑通全流程GPU容器调度、接口联调、权限适配整套链路。这套Podman+llama.cpp生产环境不局限于向量模型,全量兼容各类GGUF格式离线大模型:包括通用对话大模型、政企办公专属大模型、行业垂直业务大模型、多规格向量嵌入大模型。实际投产时,仅需替换models目录下模型文件,无需改动编排配置、无需重构GPU调度参数、无需调整运维脚本,一键无缝切换全品类大模型业务场景。Q6_K均衡量化格式通用性强、精度损耗可控、显存占用适中,适配绝大多数线上常态化推理负载。

所有模型下载后内网合规归档、离线闭环流转、严禁公网外传;跨系统统一规范路径:Ubuntu服务器避免超长嵌套目录层级,Windows工作站禁止中文文件夹、全角空格、特殊符号,从源头规避容器挂载解析失败、模型读取异常问题。

2. 固定标准化目录结构(强制对齐,双系统通用,规避挂载异常)

1
2
3
4
./llama-ai-gpu-service/
├── docker-compose.yml # Podman专用生产级编排配置,兼容Docker命令
└── models/
└── 示例模型:bge-m3.Q6_K.gguf(可替换任意对话/向量/行业大模型)

四、核心生产配置:Podman Compose 全量可直接上线脚本(GPU原生适配,全模型通用)

本次配置已完成镜像可用性全量核验,采用官方全新有效 CUDA 专属镜像,彻底废弃老旧失效镜像地址,从源头规避镜像拉取超时、校验失败、架构不兼容等部署故障;原生适配Podman内核GPU直通调度机制,全模型权重分层压入显存,高并发混跑场景稳定不宕机,全品类GGUF大模型统一适配、统一调度、统一运维。全程纯命令行部署,不依赖任何桌面可视化工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
version: '3.8'

services:
llama-cpp-ai-gpu-service:
# 官方合规有效CUDA镜像,Podman全兼容、双系统无适配bug
image: ghcr.io/ggml-org/llama.cpp:server-cuda
container_name: llama-ai-unified-gpu-service
# Podman专属核心标识:绑定NVIDIA硬件运行时,强制内核直通物理GPU
runtime: nvidia
# 异常秒级自动重启,保障7×24小时线上高可用常驻
restart: unless-stopped
# 标准化统一服务端口,适配内网多业务节点分布式协同调用
ports:
- "8080:8080"
# 持久化离线模型目录挂载,尾部:z适配SELinux权限,杜绝Ubuntu挂载拦截
volumes:
- ./models:/models:z
# 生产最优通用启动参数:全GPU加速+统一API服务,适配对话/向量全模型
command: >
-m /models/bge-m3.Q6_K.gguf
--embedding
--api
-ngl 99
--host 0.0.0.0

关键参数生产级深度解读(运维必看,全模型通用,规避线上隐患)

  • runtime: nvidia:Podman 专属GPU调度核心开关,替代Docker底层适配逻辑,强制容器内核直通NVIDIA物理显卡,是CUDA硬件加速唯一生效条件,漏写直接降级低效CPU推理,全品类大模型统一必填参数。

  • -ngl 99:将任意大模型全部网络计算层完整载入物理GPU显存,切断CPU接力推理瓶颈,大幅提升对话生成、向量编码吞吐,压缩全链路接口响应时延,通用万能适配参数。

  • –embedding:兼顾向量嵌入能力,同时不影响通用大模型基础调度,一套环境灵活切换检索、问答双业务场景,无需重构服务实例。

  • –api:原生输出标准OpenAI兼容统一接口,无需二次开发、无需中间件适配,直接对接对话中台、RAG知识库、业务检索系统,模型无感替换、业务无感切换。

  • –host 0.0.0.0:放开内网全网段访问权限,支持多应用服务器、多业务节点分布式协同调用,适配集群化AI业务架构。

  • volumes 尾部 :z:Ubuntu(Linux)专属安全权限标识,自动适配SELinux强制访问控制策略,一键解除目录挂载拦截;Windows 环境自动兼容识别,无需删减、无需二次适配,全平台通用不报错。


五、Podman兼容Docker CLI部署(纯命令行操作,无需桌面端)+ 全链路闭环校验流程

前置说明:为什么要兼容Docker CLI?

多数运维团队长期熟练使用 docker、docker-compose 全套运维命令,直接切换Podman全新指令学习成本高、容易误操作、影响上线效率。Podman原生完美兼容Docker CLI全量生态,全程仅用命令行操作,无需安装Podman Desktop桌面工具,无需改动原有Compose配置、无需迁移运维脚本、无需重构发布流程,一键适配完成后,所有存量Docker运维命令原样直接复用,底座无声切换、大模型服务无感迁移、运维零学习成本。

1、双系统差异化一键开启 Docker CLI 兼容模式(纯命令行执行)

✅ Ubuntu(Linux生产服务器)专属一键适配方案(企业首选)

系统自带官方兼容插件,全程终端命令行执行,无图形化依赖,全局环境永久生效:

1
2
3
4
5
6
# 一键安装Docker CLI兼容依赖插件
sudo apt install podman-docker -y

# 重载系统服务,全局生效
sudo systemctl restart podman
sudo systemctl daemon-reload

适配完成后,终端任意目录执行 docker、docker-compose,底层自动调度Podman内核,操作体验与旧环境完全一致。

✅ Windows 桌面工作站适配方案(PowerShell纯命令行,可选轻量化配置)

无需安装Podman Desktop桌面GUI,仅需在PowerShell管理员模式下配置Podman核心服务参数,直接开启Docker Compatibility底层兼容能力,全程命令行调试运维,轻量化不占系统资源。

2、校验兼容是否成功(统一跨系统纯命令行核验,简单高效)

1
2
3
# 输出版本信息,回显包含podman即为兼容成功
docker --version
docker-compose --version

✅ 校验正常 → 可直接使用Docker命令批量部署全品类大模型GPU服务

3、沿用原生Docker命令一键投产(配置不改、脚本不改、模型不改,纯命令行上线)

编排文件、模型目录、GPU运行时全部保持不变,沿用传统运维习惯后台静默启动:

1
docker-compose up -d

4、实时日志巡检,核验GPU硬件加速是否正常挂载

1
docker-compose logs -f

✅ 日志输出以下标准字段,代表GPU全速赋能、全品类大模型服务部署合规成功:

llm_load_tensors: using CUDA for GPU acceleration

❌ 无该字段输出 → GPU调度未挂载,立即核对runtime配置与ngl参数快速排障。

5、接口在线功能性闭环实测(验证全模型通用能力)

内网本地调用标准化统一接口,快速生成测试返回,核验环境对对话、向量全品类大模型的业务适配可用性:

1
2
3
curl http://localhost:8080/v1/embeddings \
-H "Content-Type: application/json" \
-d '{"input":"Podman兼容Docker CLI,GPU全速驱动全品类大模型生产环境部署成功","model":"any-ai-base-model"}'

接口正常返回标准化语义向量及通用推理字段,即可全量接入企业AI中台、RAG知识库、智能问答系统,后续后台自由切换各类大模型底座。


六、多方案横向对比:Podman+llama.cpp VS Ollama VS LM Studio

部署方案 工程化容器运维能力 GPU算力实际利用率 服务器高可用常驻运维 离线私有化数据合规 开源版权合规评级 容器安全等保适配度
Ollama 薄弱,黑盒封闭无标准化编排能力 中等,上层封装额外无效损耗算力 缺失集群运维,故障无自愈恢复能力 支持离线,自带高危数据泄露裸漏洞 ❌ 底层llama.cpp依赖隐匿不声明,合规违规 低,无权限隔离、无内核安全加固
LM Studio 极差,仅适配桌面端临时调试验证 中等,无精细化显存调度管控能力 完全不支持服务器后台常驻部署 仅本地单机调试,无生产隔离能力 不具备商用项目合规交付资质 仅个人测试用,无任何生产安全属性
Podman+llama.cpp CUDA ✅ 标准Compose编排,纯命令行集群运维高效省心 ✅ 原生内核直连GPU,算力零损耗拉满 ✅ 异常自动重启,7×24小时高可用稳定常驻 ✅ 全内网闭环隔离,模型数据全程不出机房 ✅ 底层依赖透明可溯源,全合规零风险 ✅ 无根运行+内核加固,完美贴合等保要求

七、Podman专属生产级故障快速排错手册(纯命令行排查,无桌面依赖)

问题1:CUDA镜像拉取超时、镜像解析校验失败

故障根因:海外官方镜像源网络链路波动、旧版镜像域名已废弃下线。快速解决方案:直接复用本文核验通过的全新官方CUDA镜像,内网机房可配套接入合规容器镜像加速源,秒级完成拉取校验,不影响任意大模型上线进度,全程命令行操作无GUI干预。

问题2:提示 unknown runtime: nvidia 运行时识别异常

故障根因:Ubuntu服务器大概率未完整手工部署NVIDIA Container Toolkit硬件赋能包,Podman命令行内核无GPU调度权限;Windows工作站大概率显卡驱动版本过低,无需桌面工具,直接命令行核查服务状态即可。快速解决方案:Ubuntu离线重装适配系统版本赋能组件,重载Podman内核服务;Windows升级完整版独显驱动,重启底层Podman核心服务,重新编排即可恢复。

问题3:容器正常启动,日志无CUDA加速成功标识

两点强制闭环排查:第一,Compose文件必须写入 runtime: nvidia;第二,启动命令必须标配**-ngl 99** 全显存加载参数,缺一必然无法触发GPU加速,自动降级低效CPU推理,全品类大模型响应速度大幅卡顿,纯日志命令即可快速定位问题。

问题4:模型挂载失败,日志提示找不到GGUF权重文件

专项闭环排查:核对目录层级对齐、全路径无中文无特殊字符,挂载尾部强制补充**:z** 标识,适配SELinux安全策略,一键解除读取拦截,对话、向量全格式大模型通用修复方案,无需可视化界面排查。


八、全文总结 & 生产环境最终选型建议

  1. 结合B站实测对标视频复盘与政企多轮等保审计实测结论:Ollama安全漏洞频发、开源合规违规、算力资源浪费、运维黑盒不可控,规模化大模型生产项目必须直接弃用;LM Studio仅适合前期算法快速摸底,不具备服务器集群投产资质。

  2. 本次方案全面下线Docker老旧底座,采用Podman无根安全架构+llama.cpp原生CUDA硬加速,全程纯命令行部署运维、无需安装任何桌面图形工具,安全合规等级更高、系统资源开销更低、集群运维更简便,完美适配涉密内网、信创机房、多节点AI集群全品类大模型常态化投产。

  3. 整套环境不局限于向量模型,以BGE-M3仅为演示示例,可无缝替换通用对话大模型、行业垂直大模型、私有化定制底座,标准化统一接口全业务适配,无需重构环境、无需改写运维脚本,开箱即用、稳定可靠。

  4. 综合算力性能、安全合规、运维成本、泛化适配能力多维度评估,该方案低显存高吞吐、全离线强隔离、极简命令行运维、零桌面冗余开销、零合规风险,是当前政企内网本地化部署全品类AI大模型的最优生产级统一落地方案。

YOLOv8 基于上次训练结果继续训练(断点续训)的核心要点是:加载训练过程中自动生成的 last.pt 检查点(包含优化器、EMA、已训练轮次、学习率等完整训练状态),并开启resume=True 参数。需特别注意:切勿使用 best.pt 进行续训,该文件仅保存验证最优权重,不包含任何训练状态,无法实现无缝续训。

以下将从检查点定位、命令行续训、Python脚本续训、核心参数说明、常见问题避坑、完整流程示例六部分,为读者提供可直接落地、无冗余的实操指南。


一、先找到上次训练的检查点(核心前提)

YOLOv8 训练完成后,检查点文件会默认保存至固定路径,不同任务的保存路径如下:

任务类型 保存路径
目标检测 runs/detect/train/weights/last.pt
图像分割 runs/segment/train/weights/last.pt
图像分类 runs/classify/train/weights/last.pt

文件说明

  • last.pt续训必用文件,保存完整训练状态(含已训练轮次epoch、优化器参数、学习率调度器、EMA权重等),可实现无缝衔接续训,无需重复配置基础参数。
  • best.pt:仅保存验证集最优模型权重,不可用于断点续训——若用其启动训练,会重置优化器、学习率等所有状态,从epoch=0重新开始。

二、命令行CLI续训(最常用,适合快速操作)

1. 标准续训(从上次中断轮次继续,总epochs不变)

1
2
# 基础格式(detect任务)
yolo detect train resume model=runs/detect/train/weights/last.pt

核心要点:需同时包含 resume 参数和 last.pt 完整路径;无需重复填写data、epochs、imgsz等参数——系统会自动从 last.pt 读取上次训练的所有配置,实现无缝续训。

实操示例:若上次训练中断时已训练至epoch 30,总计划训练100轮,执行以下命令即可从epoch 31继续:

1
yolo detect train resume model=runs/detect/my_train/weights/last.pt

2. 追加更多epochs(在原训练基础上增加训练轮次)

若原训练已完成既定轮次,或想在中断后追加更多训练轮次,只需在续训命令中指定新的总epochs(新总轮次需大于上次已训练轮次)即可:

1
2
# 原训练100轮,现在续训并追加至150轮(新增50轮训练)
yolo detect train resume model=runs/detect/train/weights/last.pt epochs=150

3. 极简续训(自动查找最近一次训练的last.pt)

若最近一次训练的检查点未被移动或删除,可直接使用 resume 参数,无需手动指定 last.pt 路径:

1
yolo detect train resume

三、Python脚本续训(适合代码集成、批量训练)

1. 标准续训(加载last.pt + 开启resume=True,无缝衔接)

1
2
3
4
5
6
7
8
9
10
11
12
13
from ultralytics import YOLO

# 1. 加载上次训练的last.pt(核心步骤,确保路径正确)
model = YOLO("runs/detect/train/weights/last.pt")

# 2. 启动续训:resume=True 是关键,自动继承上次所有训练配置
results = model.train(
resume=True, # 必须开启,启用断点续训功能
# 以下参数可选:仅当需要调整时填写(不填写则继承上次配置)
# epochs=150, # 示例:原100轮→150轮,追加50轮训练
# batch=16, # 示例:调整批次大小(根据显卡显存灵活设置)
# imgsz=640 # 示例:调整输入图像尺寸(需与上次一致)
)

2. 追加训练(基于上次权重微调,不严格续训状态)

若无需继承上次的优化器、训练轮次等状态,仅需用上次训练的权重作为初始化进行微调(如新增少量数据集、调整模型参数),可加载 best.ptlast.pt,但不开启resume=True

1
2
3
4
5
6
model = YOLO("runs/detect/train/weights/best.pt")  # 也可使用last.pt
results = model.train(
data="mydata.yaml", # 需指定数据集配置(无法继承上次配置)
epochs=50, # 从头计算50轮训练,非续接上次轮次
lr0=0.0001 # 微调用小学习率,避免过拟合(推荐值)
)

四、核心原理与关键参数说明(避坑核心)

1. resume=True 的核心作用

  • 自动读取 last.pt 中记录的已训练轮次(epoch),从 epoch+1 开始继续训练,无需手动记录中断轮次。
  • 加载完整训练状态(含优化器参数、学习率调度器、EMA权重),确保训练连续性——不会重置学习率、动量等关键参数,避免训练效果断层。
  • 自动复用上次训练的所有配置(含数据集配置data.yaml、批次大小batch、输入图像尺寸imgsz等),无需重复配置,提升实操效率。

2. 必须保持一致的配置(避免报错、训练断层)

配置类型 要求
数据集相关 需完全一致:数据集路径、类别数量(nc)、类别名称(names)、标签格式
模型核心结构 不可修改:模型版本(如yolov8n→yolov8s)、损失函数、数据增强核心参数

3. 常见参数冲突处理(实操必备)

  • 续训时仅修改需调整的参数(如epochs、batch、device),其余参数保持默认,避免手动配置与last.pt 中的配置冲突。
  • 若出现CUDA内存不足(显存不够):续训时可手动指定较小的batch尺寸(如batch=8),系统会自动覆盖上次的batch配置。

五、常见问题与避坑(实操高频问题解决)

1. 报错:Resume checkpoint not found(找不到续训检查点)

原因:路径错误;指定了best.pt而非last.pt;训练因强制中断导致 last.pt 损坏。

解决:确认命令或脚本中指定的路径必须指向 last.pt;若文件损坏,只能用 best.pt 重新启动训练(无法续训)。

2. 续训后从epoch=0开始(未实现真正续训)

原因:使用best.pt 而非 last.pt;未开启 resume=True(Python脚本)或未加 resume 参数(命令行)。

解决:更换为 last.pt,并确保开启resume=True(脚本)或添加 resume 参数(命令行)。

3. 学习率/优化器异常(续训后训练效果变差)

原因:使用 best.pt 续训(无优化器状态,会重置学习率);手动修改学习率、优化器类型等参数,与 last.pt 中的状态冲突。

解决:改用 last.pt 续训,续训时不随意修改学习率、优化器相关参数。

4. 数据集/类别不匹配(报错或预测异常)

原因:续训时使用的data.yaml与上次训练的配置不一致(如类别数、类别名称、数据集路径修改)。

解决:确保续训时使用的data.yaml与上次训练完全一致;若需修改数据集,建议采用”微调”方式(不开启resume=True)。


六、完整示例流程(从初始训练→中断→续训,实操可直接套用)

Step1 初始训练(设定总轮次,正常启动训练)

1
2
yolo detect train model=yolov8n.pt data=mydata.yaml epochs=50 imgsz=640 batch=16
# 训练过程中,检查点会保存至:runs/detect/train/weights/last.pt(此时记录epoch=50)

Step2 模拟中断(如手动Ctrl+C、断电等意外情况)

假设训练中断时,已训练至epoch 30(未完成50轮),此时 last.pt会保存当前训练状态(epoch=30、优化器参数等)。

Step3 断点续训(从epoch 31继续,直至完成50轮,或追加至更多轮次)

1
2
3
# 续训至100轮示例
yolo detect train resume model=runs/detect/train/weights/last.pt epochs=100
# 系统自动读取last.pt中的状态,从epoch=31开始训练,直至完成100轮,继承所有初始配置

七、拓展补充(进阶实操必备)

1. 自定义检查点保存路径(避免路径混乱)

若需将检查点保存至指定路径,可在初始训练时指定projectname 参数:

1
2
3
4
5
6
# 初始训练:指定保存路径(project为文件夹,name为子文件夹)
yolo detect train model=yolov8n.pt data=mydata.yaml epochs=50 project=my_yolov8_train name=detect_task
# 检查点保存路径:my_yolov8_train/detect_task/weights/last.pt

# 续训时指定该路径
yolo detect train resume model=my_yolov8_train/detect_task/weights/last.pt epochs=100

2. 续训时调整学习率(按需优化)

若续训时发现模型出现过拟合、收敛变慢等问题,可在续训命令/脚本中微调学习率:

1
yolo detect train resume model=runs/detect/train/weights/last.pt epochs=100 lr0=0.0005
1
2
3
4
5
6
model = YOLO("runs/detect/train/weights/last.pt")
results = model.train(
resume=True,
epochs=100,
lr0=0.0005 # 微调学习率,比初始学习率(0.01)小一个数量级,避免过拟合
)

说明:续训时学习率建议微调(通常为初始学习率的1/10~1/2),避免因学习率过高导致模型震荡、效果退化。

3. 多任务续训通用逻辑(检测/分割/分类通用)

无论是什么任务,断点续训的核心逻辑完全一致,仅需对应修改任务类型和检查点路径:

1
2
3
4
5
# 图像分割续训
yolo segment train resume model=runs/segment/train/weights/last.pt epochs=80

# 图像分类续训
yolo classify train resume model=runs/classify/train/weights/last.pt epochs=60

4. 续训日志查看与分析(排查问题)

续训时会自动延续上次的训练日志,日志保存路径与检查点路径一致(runs/xxx/train/results.csv),可通过以下方式查看:

  1. 直接打开 results.csv,查看每一轮的损失值、mAP、准确率等指标,判断续训是否正常收敛。
  2. 使用TensorBoard查看日志可视化结果:
1
tensorboard --logdir=runs/detect/train/logs

注意:若续训后指标出现断崖式下降,需检查 last.pt 完整性、数据集配置一致性或学习率设置。


八、总结

YOLOv8 断点续训的核心是「加载 last.pt + 开启 resume=True」,关键在于保证数据集配置、模型结构与上次训练一致,避免因参数冲突、文件损坏导致续训失败。

无论是命令行快速操作,还是Python脚本集成,均需遵循这一核心逻辑;若需追加轮次、微调参数,仅需在续训时指定相关参数即可。

通过以上完整指南,可轻松实现YOLOv8模型的无缝续训,提升训练效率,避免因意外中断导致的训练成果浪费。

在AI本地化部署场景中,Ollama凭借轻量化、易用性强的特点,成为快速运行大模型(含嵌入模型)的首选工具;而Docker则能解决环境依赖混乱、版本冲突等问题,实现Ollama的快速部署与隔离运行。本文针对AMD Ryzen平台(以零刻SER9系列小主机为例),详细讲解如何通过Docker部署Ollama,结合ROCm实现核显加速,并以bge-m3嵌入模型为示例,完成从环境准备到实际运行的全流程操作,同时澄清NPU加速的现状与替代方案。

一、前置说明与环境前提

1.1 适用场景与硬件要求

本文适用于搭载AMD Ryzen处理器(带核显)的设备,重点适配零刻SER9 Pro/SER9 MAX、铭凡AI X1等小主机,核心要求:

  • CPU:AMD Ryzen 7/AI9系列(支持ROCm),如零刻SER9 Pro的AI9 HX370、R7 H255

  • 核显:Radeon 780M/890M(零刻SER9系列标配,支持ROCm加速)

  • 系统:Ubuntu 22.04/24.04(ROCm对Ubuntu兼容性最佳,不推荐CentOS等系统)

  • 权限:管理员权限(sudo),用于安装驱动与配置环境

1.2 核心说明(关键避坑)

  • Ollama当前不直接支持AMD Ryzen AI NPU(XDNA),仅支持ROCm核显加速(本文重点);

  • bge-m3作为嵌入模型,在Ollama中默认基于llama.cpp运行,可通过ROCm核显提升推理速度;

  • Docker部署的核心是“设备直通+权限匹配+ROCm环境映射”,缺一不可。

二、宿主机环境准备(ROCm驱动安装)

Ollama的ROCm加速依赖宿主机的ROCm驱动,需先完成宿主机驱动配置,否则容器内无法识别核显。

2.1 BIOS配置(必做)

进入设备BIOS(开机按Del/F2),完成两项关键配置:

  1. Advanced → OEM → Ryzen AI → Enabled(虽不用于Ollama,但不开启可能影响核显识别);

  2. 关闭Secure Boot(否则ROCm驱动无法正常加载,导致核显无法识别)。

配置完成后保存重启,进入Ubuntu系统。

2.2 ROCm驱动安装(Ubuntu)

执行以下命令,一步安装ROCm依赖与驱动(适配ROCm 6.4版本,兼容零刻SER9核显):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 安装基础依赖(内核 headers 与编译工具)
sudo apt update
sudo apt install -y linux-headers-$(uname -r) build-essential

# 2. 添加ROCm官方源
echo "deb [arch=amd64] https://repo.radeon.com/rocm/apt/6.4 focal main" | sudo tee /etc/apt/sources.list.d/rocm.list
sudo apt-key adv --fetch-keys https://repo.radeon.com/rocm/rocm.gpg.key

# 3. 安装ROCm核心组件
sudo apt update
sudo apt install -y rocm-libs rocm-dev rocm-utils

# 4. 配置权限(免root访问核显)
sudo usermod -aG render,video $USER
newgrp render
newgrp video

2.3 验证ROCm是否生效

执行以下命令,确认核显被ROCm识别(零刻SER9核显架构为gfx1151):

1
2
3
4
5
6
7
# 查看核显架构
rocminfo | grep gfx
# 正常输出:gfx1151(匹配零刻SER9核显)

# 查看ROCm状态
rocm-smi
# 正常输出:显示card0(核显)信息,无报错、无“no GPU detected”

若未识别到核显,需重新检查BIOS配置与驱动安装步骤,确认Secure Boot已关闭。

三、Docker部署Ollama(ROCm加速版)

采用Ollama官方ROCm镜像,无需手动构建,通过容器启动命令即可完成部署,重点配置设备直通与环境变量。

3.1 拉取Ollama ROCm镜像

推荐使用固定版本(避免最新版出现兼容性问题),执行命令:

1
2
3
4
5
# 拉取固定版本(0.16.0,经测试适配零刻SER9)
docker pull ollama/ollama:0.16.0-rocm

# 可选:拉取最新版ROCm镜像
# docker pull ollama/ollama:rocm

3.2 启动Ollama容器(核心命令)

先获取宿主机render、video组的GID(确保容器权限与宿主机一致,避免核显访问失败),再启动容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 获取宿主机render、video组GID
RENDER_GID=$(getent group render | cut -d: -f3)
VIDEO_GID=$(getent group video | cut -d: -f3)

# 2. 启动容器(持久化数据+ROCm加速+端口映射)
docker run -d \
--name ollama-rocm \
--restart unless-stopped \
--device /dev/kfd \ # ROCm核心设备
--device /dev/dri \ # 核显设备
--group-add $VIDEO_GID \ # 匹配video组权限
--group-add $RENDER_GID \ # 匹配render组权限
-v ollama-rocm:/root/.ollama \ # 持久化Ollama数据(模型、配置)
-p 11434:11434 \ # 映射端口,供外部访问
-e OLLAMA_HOST=0.0.0.0 \ # 允许外部访问容器内Ollama
-e HSA_OVERRIDE_GFX_VERSION=11.5.1 \ # 强制识别gfx1151核显(零刻SER9必加)
-e OLLAMA_KEEP_ALIVE=-1 \ # 保持模型常驻内存,提升推理速度
ollama/ollama:0.16.0-rocm

3.3 关键参数说明(避坑重点)

  • --device /dev/kfd、--device /dev/dri:必须添加,将宿主机核显设备直通到容器,是ROCm加速的核心;

  • HSA_OVERRIDE_GFX_VERSION=11.5.1:零刻SER9核显为gfx1151,官方镜像默认不识别,需强制指定架构;

  • -v ollama-rocm:/root/.ollama:持久化模型数据,避免容器删除后重新下载模型;

  • --group-add:权限匹配,否则容器内会因权限不足,无法访问核显设备。

3.4 Docker Compose一键部署(推荐)

若需频繁启停或批量部署,可创建docker-compose.yml文件,实现一键启停:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3.8"
services:
ollama:
image: ollama/ollama:0.16.0-rocm
container_name: ollama-rocm
restart: unless-stopped
devices:
- /dev/kfd
- /dev/dri
group_add:
- ${VIDEO_GID}
- ${RENDER_GID}
volumes:
- ollama-rocm:/root/.ollama
ports:
- "11434:11434"
environment:
OLLAMA_HOST: 0.0.0.0
HSA_OVERRIDE_GFX_VERSION: 11.5.1
OLLAMA_KEEP_ALIVE: -1
volumes:
ollama-rocm:

启动命令:

1
2
3
4
5
6
7
8
9
# 导出宿主机GID(仅首次执行)
export RENDER_GID=$(getent group render | cut -d: -f3)
export VIDEO_GID=$(getent group video | cut -d: -f3)

# 启动容器
docker-compose up -d

# 停止容器(可选)
# docker-compose down

四、Ollama运行bge-m3(ROCm核显加速测试)

bge-m3是字节跳动推出的高性能嵌入模型,适用于语义检索、向量生成等场景,下面以该模型为例,测试Ollama+ROCm的加速效果。

4.1 拉取bge-m3模型

进入Ollama容器,拉取bge-m3模型(模型体积约2.2GB,无需额外配置):

1
2
3
4
5
6
7
8
9
# 进入容器
docker exec -it ollama-rocm bash

# 拉取bge-m3模型
ollama pull bge-m3

# 验证模型是否拉取成功
ollama list
# 正常输出:bge-m3:latest (模型名称+版本)

4.2 测试bge-m3推理(ROCm加速)

有两种测试方式,分别适用于交互式测试和接口调用,均能实现ROCm核显加速。

方式1:交互式运行(容器内)

1
2
3
4
5
# 启动bge-m3交互式会话
ollama run bge-m3 "Docker部署Ollama实现ROCm加速"

# 输出结果:
# 模型会生成该文本的向量嵌入(默认1024维),同时日志会显示ROCm加速信息

方式2:API接口调用(外部访问)

通过HTTP接口调用bge-m3,适用于集成到应用程序中,命令如下:

1
2
3
4
curl http://localhost:11434/api/embeddings -d '{
"model": "bge-m3",
"prompt": "Docker部署Ollama并实现ROCm加速(以bge-m3为例)"
}'

正常输出:返回1024维向量嵌入结果,说明模型运行正常,ROCm加速生效。

4.3 加速效果验证与对比

以零刻SER9 Pro(AI9 HX370,Radeon 890M核显)为例,对比CPU与ROCm核显的推理速度(单句512token):

运行方式 推理速度 核心优势
CPU(无加速) 200-300ms/句 无需驱动,兼容性强
ROCm核显(加速) 80-120ms/句 速度提升2-3倍,资源占用低

验证ROCm加速是否生效的关键:进入容器后执行ollama info,输出中包含“AMD GPU: gfx1151”,说明核显已被Ollama识别并用于加速。

五、常见问题排查(避坑指南)

5.1 容器内无法识别核显(ollama info无GPU)

  • 检查宿主机rocminfo是否识别核显,若未识别,重新安装ROCm驱动;

  • 确认容器启动命令中添加了\-\-device /dev/kfd \-\-device /dev/dri,且环境变量HSA\_OVERRIDE\_GFX\_VERSION=11\.5\.1正确;

  • 重新启动容器,确保权限参数\-\-group\-add正确加载。

5.2 权限不足:failed to open /dev/dri/card0

原因:容器内用户未加入render、video组,解决方案:

1
2
3
4
5
6
7
8
# 重新获取GID并重启容器
RENDER_GID=$(getent group render | cut -d: -f3)
VIDEO_GID=$(getent group video | cut -d: -f3)

docker stop ollama-rocm
docker rm ollama-rocm

# 重新执行启动命令(确保--group-add参数正确)

5.3 bge-m3仍走CPU,未触发ROCm加速

Ollama的bge-m3基于llama.cpp运行,当前llama.cpp仅支持ROCm核显加速,不支持Ryzen AI NPU;若未触发加速,需检查:

  • Ollama镜像是否为ROCm版本(非CPU版本);

  • 宿主机ROCm驱动是否正常,容器是否正确映射核显设备;

  • 模型是否拉取完整,可通过ollama pull bge\-m3 \-\-force重新拉取。

5.4 容器启动失败:address already in use

原因:11434端口被占用(可能是本地已运行Ollama),解决方案:

1
2
3
4
5
6
7
# 查看占用11434端口的进程
sudo lsof -i:11434

# 终止进程(替换PID为实际进程ID)
sudo kill -9 PID

# 重新启动容器

六、总结与扩展

6.1 核心总结

本文实现了“Docker+Ollama+ROCm”的完整部署,以bge-m3为例完成了加速测试,核心要点:

  1. 宿主机ROCm驱动是加速的基础,必须完成BIOS配置与驱动安装;

  2. Docker部署的关键是“核显设备直通+权限匹配+架构指定”,避免权限与识别问题;

  3. bge-m3在Ollama中可通过ROCm核显实现2-3倍加速,满足本地化嵌入模型的性能需求;

  4. 当前Ollama不支持AMD Ryzen AI NPU,需等待llama.cpp集成XDNA后端后实现更高效的NPU加速。

6.2 扩展建议

  • 模型优化:可通过ollama run bge\-m3 \-\-quantize q4\_0量化模型,进一步提升推理速度、降低内存占用;

  • 多模型部署:除bge-m3外,可通过Ollama拉取llama3、mistral等模型,均支持ROCm加速;

  • 远程访问:若需外部设备访问Ollama,需开放宿主机11434端口,确保容器环境变量OLLAMA\_HOST=0\.0\.0\.0

  • 版本更新:定期更新Ollama ROCm镜像与ROCm驱动,提升兼容性与性能。

通过本文的部署流程,可快速在AMD Ryzen小主机(如零刻SER9系列)上实现Ollama的容器化部署与ROCm加速,兼顾环境隔离与性能提升,适用于AI本地化部署、开发测试等场景。

你可能已经了解到过去几个月中发布的大量人工智能应用程序。你甚至可能已经开始使用其中的一些。 ChatPDF [https://www.chatpdf.com/] 和 CustomGPT AI [https://customgpt.ai/use-cases/] 等人工智能工具对人们非常有用,这是有道理的。你需要翻阅长达 50 页的文档才能找到一个简单答案的时代已经一去不复返了。取而代之的是,你可以依靠人工智能来完成繁重的工作。 但是,这些开发人员究竟是如何创建和使用这些工具的呢?他们中的许多人都在使用一个名为 LangChain 的开源框架。 在本文中,我将向你介绍 LangChain,并向你展示如何将其与 OpenAI 的 API 结合使用,以创建这些改变游戏规则的工具。希望我的介绍能激发你们的灵感,创造出属于自己的工具。那么,让我们开始吧! 什么是 LangChain LangChain [https://github.com/hwchase17/langchain/]是一个开源框架,允许人工智能开发人员将 GPT-4 等大型语言模型(LLM)与外部数据相结合。它提供 P

阅读全文 »

一、 概述与风险提示

使用场景

  • 不慎提交了敏感信息(如密码、密钥)。
  • 误提交了大文件导致仓库体积膨胀。
  • 需要清理版本控制历史中的临时文件。

⚠️ 重要风险提示

  1. 不可逆操作:修改 Git 历史会改变提交的哈希值 (SHA),影响所有协作者。
  2. 必须备份:执行任何历史修改操作前,务必备份整个仓库
  3. 团队协调:操作前应通知所有协作者,操作后需重新 clone 仓库。
  4. 替代方案:如果只是想删除最新提交中的文件,使用 git rm 即可,无需修改历史。

二、 方法一:git filter-branch

适用于批量处理整个仓库的历史记录。

1
2
3
4
5
6
7
# 切换到仓库根目录
cd /path/to/repo

# 执行删除操作
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch example.txt' \
--prune-empty --tag-name-filter cat -- --all

参数说明

  • --force:强制覆盖旧的备份分支。
  • --index-filter:对每个提交的索引执行命令。
  • git rm --cached --ignore-unmatch:从索引中移除文件,但忽略文件不存在的情况。
  • --prune-empty:删除因操作变为空的提交。
  • --tag-name-filter cat:保留原有标签名称。
  • --all:处理所有分支。

清理残留

1
2
3
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now --aggressive

三、 方法二:git rebase

适用于处理少量连续提交中的文件删除。

1
2
3
4
5
6
# 1. 查看提交历史,确定目标提交范围
git log --oneline

# 2. 开始交互式 rebase
# 例如从 HEAD~5 开始,包含最近5个提交
git rebase -i HEAD~5

在打开的编辑器中,找到包含目标文件的提交,将其 pick 改为 edit

1
2
3
pick abc1234 添加配置文件
edit def5678 添加敏感文件 <-- 改为 edit
pick 89a0123 修复bug

保存退出后,执行以下操作:

1
2
3
4
5
6
7
8
# 删除文件
git rm example.txt

# 修改当前提交(不改变消息)
git commit --amend --no-edit

# 继续 rebase 过程
git rebase --continue

如果有多个提交需要处理,重复上述 edit -> git rm -> amend -> continue 的流程。

四、 方法三:git filter-repo(推荐)

git filter-repo 是目前最推荐的工具,速度快、灵活性高、功能强大。

安装

1
2
# 使用 pip 安装
python3 -m pip install --user git-filter-repo

删除单个文件

1
git filter-repo --path example.txt --invert-paths

删除多个文件或目录

1
git filter-repo --path-rename example.txt: --path-rename sensitive/: --invert-paths

删除大于指定大小的文件

1
git filter-repo --strip-blobs-bigger-than 10M --invert-paths

⚠️ 重要filter-repo 要求仓库是干净的 clone,建议使用以下方式:

1
2
3
4
5
6
7
8
9
10
11
# 1. 克隆仓库(使用 --mirror 完整克隆)
git clone --mirror https://github.com/user/repo.git

# 2. 进入仓库目录
cd repo.git

# 3. 执行过滤
git filter-repo --path example.txt --invert-paths

# 4. 推送更新
git push --force

五、 方法对比与总结

特性 filter-branch rebase filter-repo
速度 取决于提交数
适用场景 大量历史处理 少量连续提交 所有场景
学习曲线 中等 较低
灵活性
推荐程度 ❌ 不推荐 ⚠️ 慎用 ✅ 强烈推荐

建议流程

  1. 优先考虑使用 **git filter-repo**。
  2. 操作前务必备份仓库
  3. 通知协作者即将进行的维护。
  4. 操作完成后,强制推送到远程:git push --force --all
  5. 提醒所有协作者重新 clone 仓库。

1. 协议识别

执行 git remote -v 确认远程仓库协议:

  • http://https://:使用 HTTP 代理配置
  • git@ssh://:使用 SSH 代理配置

2. HTTP/HTTPS 代理配置

Git 统一通过 http.proxy 处理 HTTP 与 HTTPS 流量。

  • 全局生效

    1
    2
    3
    4
    # HTTP/HTTPS 代理
    git config --global http.proxy http://127.0.0.1:<port>
    # SOCKS5 代理
    git config --global http.proxy socks5://127.0.0.1:<port>
  • 指定域名生效(以 GitHub 为例)

    1
    2
    git config --global http.https://github.com.proxy http://127.0.0.1:<port>
    git config --global http.https://github.com.proxy socks5://127.0.0.1:<port>

3. SSH 代理配置

修改 ~/.ssh/config,通过 ProxyCommand 转发流量。

  • Linux / macOS

    1
    2
    3
    Host github.com
    User git
    ProxyCommand nc -X connect -x 127.0.0.1:<port> %h %p
  • Windows

    1
    2
    3
    Host github.com
    User git
    ProxyCommand connect -H 127.0.0.1:<port> %h %p

    注:%h%p 为 SSH 内置占位符,禁止修改。Windows 需确保 connect.exe 已加入 PATH。

4. 验证与清理

1
2
3
4
5
6
7
# 查看当前代理配置
git config --global --get http.proxy
git config --global --get http.https://github.com.proxy

# 清除全局代理
git config --global --unset http.proxy
git config --global --unset http.https://github.com.proxy

5. 配置优先级与注意事项

配置层级 作用范围 优先级
仓库级 (git config) 当前 .git 目录 最高
全局级 (--global) 当前用户所有仓库
系统环境变量 (HTTP_PROXY) 系统全局进程 最低
  • 代理软件需保持运行,否则 Git 操作直接超时。
  • 生产环境建议仅对特定域名配置代理,避免拦截内网或私有仓库流量。
  • SSH 代理配置修改后无需重启,下次连接自动生效。

一、使用 SC 命令管理 Windows 服务

SC.exe 是一个强大的命令行工具,用于与 Windows 服务控制管理器通信,可以创建、配置、启动、停止和删除系统服务。

1.1 基础语法

SC 命令的基本语法如下:

1
SC [\\Servername] command Servicename [Optionname= Optionvalues]
  • Servername:远程计算机名(如 \\192.168.1.100),本地操作可省略。
  • Command:要执行的操作命令。
  • Servicename:服务的键名(注册表中的名称),不同于显示名称。
  • Optionname= Optionvalues:选项名和值。注意:等号后面必须有一个空格

1.2 常用命令速览

命令 功能描述
create 创建服务(添加到注册表)
delete 删除服务
start 启动服务
stop 停止服务
config 更改服务配置(永久生效)
query 查询服务状态
qc 查询服务配置信息

1.3 核心操作示例

1. 创建服务

将可执行程序注册为自动启动的服务。

1
sc create ServiceName binPath= "C:\Path\To\YourProgram.exe" start= auto

示例:将 Tomcat 注册为服务

1
sc create Tomcat binPath= "F:\apache-tomcat\bin\startup.bat" start= auto

2. 删除服务

1
sc delete ServiceName

示例:删除 Tomcat 服务

1
sc delete Tomcat

3. 配置与查询服务

  • 更改服务启动类型(如启用 Telnet):
    1
    2
    sc config tlntsvr start= auto
    net start tlntsvr
  • 查询服务状态
    1
    sc query ServiceName
  • 查询服务详细配置
    1
    sc qc ServiceName

1.4 SC CREATE 命令详解

创建服务时可以指定更多参数,格式如下:

1
sc create Servicename [type= {own|share}] [start= {boot|system|auto|demand|disabled}] [binPath= <二进制路径>] [obj= <账户名>] [Displayname= <显示名称>] [depend= <依赖服务>]

关键参数说明

  • type=:服务类型。own 为独立进程,share 为共享进程(默认)。
  • start=:启动类型。auto 为自动,demand 为手动(默认),disabled 为禁用。
  • binPath=(必需) 可执行文件的完整路径。
  • obj=:运行服务的账户,默认为 LocalSystem
  • Displayname=:在服务管理器中显示的名称。
  • depend=:该服务所依赖的其他服务(用空格分隔)。

复杂创建示例

1
sc create MyService binPath= "C:\MyApp\app.exe --config config.xml" type= own start= auto Displayname= "我的后台服务" depend= RPCSS/Tcpip

二、使用 NSSM 封装任意程序为服务

对于非原生服务程序(如普通 .exe 控制台程序),可以使用 NSSM (Non-Sucking Service Manager) 将其封装为标准的 Windows 服务。它比微软自带的 srvany 更强大易用。

2.1 NSSM 的优势

  1. 支持任何 .exe 程序。
  2. 安装和修改配置非常简单(有图形界面)。
  3. 可重定向程序输出到日志文件(支持日志轮换)。
  4. 具备服务守护功能,程序崩溃后可自动重启。
  5. 可自定义环境变量。

2.2 基本使用流程

1. 服务安装

打开命令行,执行:

1
nssm install <ServiceName>

这会弹出一个图形化配置窗口。在 “Application” 标签页中,设置:

  • Path:选择你的可执行程序。
  • Startup directory:设置程序的工作目录。
  • Arguments:如有需要,填入启动参数。

配置完成后,点击 “Install service”

命令行静默安装示例(安装 Jenkins Agent):

1
2
3
4
5
nssm install Jenkins "%PROGRAMFILES%\Java\jre7\bin\java.exe"
nssm set Jenkins AppParameters -jar slave.jar -jnlpUrl https://jenkins/computer/%COMPUTERNAME%/slave-agent.jnlp
nssm set Jenkins AppDirectory C:\Jenkins
nssm set Jenkins AppStdout C:\Jenkins\jenkins.log
nssm start Jenkins

2. 服务管理

  • 启动服务nssm start <ServiceName>
  • 停止服务nssm stop <ServiceName>
  • 重启服务nssm restart <ServiceName>

3. 修改配置

1
nssm edit <ServiceName>

再次打开图形界面修改参数。

4. 删除服务

1
2
3
nssm remove <ServiceName>
# 或自动确认删除
nssm remove <ServiceName> confirm

三、其他服务管理方式

3.1 使用 Regsvr32 注册控件

Regsvr32 用于注册 .dll.ocx 等控件文件。

1
Regsvr32 [/s] [/u] DLLName
  • /s:静默注册,不显示成功对话框。
  • /u:卸载控件。

3.2 通过注册表手动删除服务

如果 sc delete 失败,可以手动删除:

  1. Win + R,输入 regedit 打开注册表编辑器。
  2. 导航至:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
  3. 找到与服务名对应的键值,右键删除即可。

四、总结与最佳实践

场景 推荐工具 关键命令/操作
管理原生/已知服务 SC 命令 sc start/stop/config/query
将自定义程序安装为服务 SC 命令 sc create ... binPath= ...
封装复杂程序(需日志、守护) NSSM nssm install (图形界面配置)
注册/卸载系统控件 Regsvr32 Regsvr32 MyControl.dll

重要提醒

  1. 使用 sc create 时,binPath=start= 等参数中的等号后必须有一个空格
  2. 如果路径或服务名包含空格,必须使用双引号包裹。
  3. 大多数服务操作需要管理员权限
  4. 修改服务配置后,有时需要重启服务或计算机才能生效。

通过熟练掌握 SC 命令和 NSSM 工具,你可以高效地在 Windows 环境下完成各种服务的部署、管理和维护工作。

一、核心定位

本文作为GitOps环境搭建系列的第五篇,聚焦基础组件(数据库和缓存)的部署与配置。在GitOps环境中,数据库和缓存是支撑核心组件(Gitea、ArgoCD、Tekton、Harbor等)运行的关键基础设施。

基础组件包括:

  • PostgreSQL/MySQL:关系型数据库,用于存储Gitea、Harbor等组件的元数据
  • Redis:内存缓存,用于提升组件性能和会话管理

本文采用独立命名空间规划,将PostgreSQL部署在postgres命名空间,Redis部署在redis命名空间,MySQL作为备选方案部署在mysql命名空间。所有配置均适配K3s轻量级环境,使用local-path存储类保障数据持久化,并通过Traefik的IngressRouteTCP实现服务访问。

二、部署前置检查

部署前需验证K3s集群状态及存储配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证存储类配置
kubectl get storageclass
kubectl describe storageclass local-path

# 3. 验证Traefik运行状态
kubectl get pods -n kube-system -l app=traefik

# 4. 检查命名空间
kubectl get ns postgres redis mysql 2>/dev/null || echo "命名空间不存在,将自动创建"

# 5. 验证网络连通性
ping -c 3 8.8.8.8

前置条件检查清单:

  • K3s集群运行正常
  • local-path存储类可用
  • Traefik Pod处于Running状态
  • 网络可访问外网(用于拉取镜像)
  • 具备集群管理权限

三、创建独立命名空间

为实现数据库与缓存服务的资源隔离和精细化管理,分别创建专属命名空间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
name: postgres
---
apiVersion: v1
kind: Namespace
metadata:
name: redis
---
apiVersion: v1
kind: Namespace
metadata:
name: mysql

应用配置:

1
2
3
4
kubectl apply -f namespaces.yaml

# 验证命名空间创建
kubectl get namespaces | grep -E "postgres|redis|mysql"

四、部署PostgreSQL

4.1 配置PostgreSQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# postgresql.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
namespace: postgres
data:
POSTGRES_DB: "gitea"
POSTGRES_USER: "gitea"
POSTGRES_PASSWORD: "gitea123"
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
PGDATA: "/var/lib/postgresql/data/pgdata"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: postgres
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: postgres
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
envFrom:
- configMapRef:
name: postgres-config
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: postgres
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
type: ClusterIP

4.2 配置IngressRouteTCP(可选)

如需通过Traefik外部访问PostgreSQL,配置IngressRouteTCP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# postgres-ingressroute-tcp.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: postgres-ingressroute-tcp
namespace: postgres
spec:
entryPoints:
- postgres-tcp # 需在Traefik中配置此TCP入口点
routes:
- match: HostSNI(`*`)
services:
- name: postgres
port: 5432

4.3 应用PostgreSQL配置

1
2
3
4
5
6
7
8
9
10
# 部署PostgreSQL
kubectl apply -f postgresql.yaml

# 可选:部署IngressRouteTCP
kubectl apply -f postgres-ingressroute-tcp.yaml

# 验证部署状态
kubectl get pods -n postgres
kubectl get pvc -n postgres
kubectl get svc -n postgres

五、部署Redis

5.1 配置Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# redis.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: redis
data:
redis.conf: |
bind 0.0.0.0
protected-mode no
port 6379
requirepass "redis123456"
appendonly yes
appendfsync everysec
dir /data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: redis
spec:
serviceName: redis
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
command: ["redis-server", "/etc/redis/redis.conf"]
ports:
- containerPort: 6379
volumeMounts:
- name: redis-config
mountPath: /etc/redis
- name: redis-data
mountPath: /data
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-path"
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: redis
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
type: ClusterIP

5.2 配置IngressRouteTCP(可选)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# redis-ingressroute-tcp.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: redis-ingressroute-tcp
namespace: redis
spec:
entryPoints:
- redis-tcp # 需在Traefik中配置此TCP入口点
routes:
- match: HostSNI(`*`)
services:
- name: redis
port: 6379

5.3 应用Redis配置

1
2
3
4
5
6
7
8
9
# 部署Redis
kubectl apply -f redis.yaml

# 可选:部署IngressRouteTCP
kubectl apply -f redis-ingressroute-tcp.yaml

# 验证Redis运行状态
kubectl get pods -n redis
kubectl exec -it redis-0 -n redis -- redis-cli -a redis123456 ping

六、备选方案:部署MySQL

6.1 配置MySQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# mysql.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: mysql
data:
my.cnf: |
[mysqld]
default_authentication_plugin=mysql_native_password
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
---
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
namespace: mysql
type: Opaque
data:
mysql-root-password: cm9vdDEyMzQ1Ng== # root123456 base64编码
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: mysql-root-password
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "local-path"
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
type: ClusterIP

6.2 配置IngressRouteTCP(可选)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# mysql-ingressroute-tcp.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: mysql-ingressroute-tcp
namespace: mysql
spec:
entryPoints:
- mysql-tcp # 需在Traefik中配置此TCP入口点
routes:
- match: HostSNI(`*`)
services:
- name: mysql
port: 3306

6.3 应用MySQL配置

1
2
3
4
5
6
7
8
# 部署MySQL(备选方案)
kubectl apply -f mysql.yaml

# 可选:部署IngressRouteTCP
kubectl apply -f mysql-ingressroute-tcp.yaml

# 验证MySQL运行状态
kubectl get pods -n mysql

七、验证部署结果

7.1 验证组件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看所有基础组件状态
kubectl get pods -n postgres
kubectl get pods -n redis
kubectl get pods -n mysql 2>/dev/null || echo "MySQL未部署"

# 查看服务状态
kubectl get svc -n postgres
kubectl get svc -n redis
kubectl get svc -n mysql 2>/dev/null || echo "MySQL未部署"

# 查看持久化存储
kubectl get pvc -n postgres
kubectl get pvc -n redis
kubectl get pvc -n mysql 2>/dev/null || echo "MySQL未部署"

7.2 测试连接功能

1
2
3
4
5
6
7
8
9
10
11
# 测试PostgreSQL连接
kubectl run postgres-test --rm -it --image=postgres:15-alpine -n postgres -- \
psql -h postgres.postgres.svc.cluster.local -U gitea -d gitea -c "SELECT 1;"

# 测试Redis连接
kubectl run redis-test --rm -it --image=redis:7-alpine -n redis -- \
redis-cli -h redis.redis.svc.cluster.local -a redis123456 ping

# 测试MySQL连接(如部署)
kubectl run mysql-test --rm -it --image=mysql:8.0 -n mysql -- \
mysql -h mysql.mysql.svc.cluster.local -uroot -proot123456 -e "SELECT 1;"

7.3 验证IngressRouteTCP(如配置)

1
2
3
4
# 查看IngressRouteTCP状态
kubectl get ingressroutetcps.traefik.containo.us -n postgres 2>/dev/null || echo "未配置"
kubectl get ingressroutetcps.traefik.containo.us -n redis 2>/dev/null || echo "未配置"
kubectl get ingressroutetcps.traefik.containo.us -n mysql 2>/dev/null || echo "未配置"

八、服务访问方式

8.1 集群内访问

  • PostgreSQL: postgres.postgres.svc.cluster.local:5432
  • Redis: redis.redis.svc.cluster.local:6379
  • MySQL: mysql.mysql.svc.cluster.local:3306

8.2 集群外访问(需配置IngressRouteTCP)

  • PostgreSQL: Traefik节点IP:5432(需Traefik配置postgres-tcp入口点)
  • Redis: Traefik节点IP:6379(需Traefik配置redis-tcp入口点)
  • MySQL: Traefik节点IP:3306(需Traefik配置mysql-tcp入口点)

九、日常运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看组件日志
kubectl logs -f postgres-0 -n postgres
kubectl logs -f redis-0 -n redis
kubectl logs -f mysql-0 -n mysql 2>/dev/null || echo "MySQL未部署"

# 重启组件
kubectl rollout restart statefulset postgres -n postgres
kubectl rollout restart statefulset redis -n redis
kubectl rollout restart statefulset mysql -n mysql 2>/dev/null || echo "MySQL未部署"

# 数据备份(PostgreSQL示例)
kubectl exec postgres-0 -n postgres -- pg_dump -U gitea gitea > gitea-backup-$(date +%Y%m%d).sql

# 进入容器调试
kubectl exec -it postgres-0 -n postgres -- /bin/bash
kubectl exec -it redis-0 -n redis -- /bin/sh
kubectl exec -it mysql-0 -n mysql -- /bin/bash 2>/dev/null || echo "MySQL未部署"

十、常见问题修复

问题现象 排查方向 修复方案
Pod一直Pending 存储类/资源不足 检查local-path存储类,确认节点有足够资源
数据库连接失败 服务发现/认证 检查Service selector,验证用户名密码
Redis认证失败 密码配置 确认redis.conf中的requirepass配置正确
数据持久化失败 PVC绑定问题 检查PVC状态:kubectl describe pvc -n <namespace>
IngressRouteTCP不生效 Traefik入口点配置 确认Traefik已配置对应TCP入口点

十一、配置参考

所有基础组件配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops

该目录包含:

  • PostgreSQL完整配置
  • Redis完整配置
  • MySQL完整配置
  • 生产环境优化配置
  • 备份和恢复脚本

总结

本文完成了GitOps环境基础组件(PostgreSQL、Redis、MySQL)的标准化部署,采用独立命名空间实现资源隔离,使用StatefulSet保障有状态服务的稳定性,通过local-path存储类实现数据持久化。

基础组件部署完成后,GitOps环境已具备完整的数据存储和缓存能力。下一篇文章将部署Gitea代码仓库,为GitOps环境提供代码托管服务。

一、核心定位

本文作为GitOps环境搭建系列的第九篇,聚焦容器镜像仓库Harbor的部署与配置。Harbor是由CNCF孵化而成的企业级开源Registry项目,提供完整的镜像存储、签名、扫描和分发能力。

在GitOps环境中,Harbor扮演”镜像存储中枢”角色,作为GitOps流程的镜像仓库环节,接收来自Tekton构建的镜像,并为ArgoCD提供可信的镜像源。Harbor与Gitea、Tekton、ArgoCD协同工作,形成完整的”代码→构建→镜像→部署”自动化流水线。

二、部署前置检查

部署前需验证K3s集群状态及前序组件运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证PostgreSQL运行状态
kubectl get pods -n postgres

# 3. 验证Redis运行状态
kubectl get pods -n redis

# 4. 验证Traefik运行状态
kubectl get pods -n kube-system -l app=traefik

# 5. 验证cert-manager运行状态
kubectl get pods -n cert-manager

# 6. 验证域名解析
nslookup harbor.example.io
nslookup notary.example.io

前置条件检查清单:

  • K3s集群运行正常
  • PostgreSQL数据库可用(用于存储Harbor元数据)
  • Redis缓存服务可用(用于Harbor会话缓存)
  • Traefik反向代理可用
  • cert-manager证书管理可用
  • 域名harbor.example.ionotary.example.io已解析至K3s节点IP
  • 已创建Harbor数据库和用户(如未创建,见下文步骤)

三、准备Harbor数据库

3.1 创建Harbor数据库

1
2
3
4
# 在PostgreSQL中创建Harbor数据库
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE DATABASE harbor;"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "CREATE USER harbor WITH PASSWORD 'harbor123';"
kubectl exec -it postgres-0 -n postgres -- psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE harbor TO harbor;"

3.2 创建Harbor命名空间

1
2
# 创建Harbor专属命名空间
kubectl create namespace harbor

四、配置Harbor证书

4.1 创建Harbor证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# harbor-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: harbor-cert
namespace: harbor
spec:
secretName: harbor-tls-secret
issuerRef:
name: selfsigned-cluster-issuer
kind: ClusterIssuer
commonName: harbor.example.io
dnsNames:
- harbor.example.io
- notary.example.io
duration: 2160h
renewBefore: 360h
privateKey:
algorithm: RSA
size: 2048
usages:
- server auth

4.2 应用证书配置

1
2
3
4
5
6
# 应用证书配置
kubectl apply -f harbor-certificate.yaml

# 验证证书状态
kubectl get certificate -n harbor
kubectl describe certificate harbor-cert -n harbor

五、标准化部署Harbor

5.1 添加Helm仓库

1
2
3
# 添加Harbor官方Helm仓库
helm repo add harbor https://helm.goharbor.io --force-update
helm repo update

5.2 创建Harbor配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# harbor-values.yaml
# 参考:https://gitee.com/Chemmy/kube-template/blob/master/devops/Harbor/harbor-values.yaml

# 全局配置
expose:
type: ingress
tls:
enabled: true
certSource: secret
secret:
secretName: "harbor-tls-secret"
notarySecretName: "harbor-tls-secret"
ingress:
hosts:
core: harbor.example.io
notary: notary.example.io
annotations:
kubernetes.io/ingress.class: "traefik"
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.entrypoints: websecure

externalURL: https://harbor.example.io

# 管理员密码
harborAdminPassword: "HarborAdmin123!"

# 持久化存储
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 100Gi
chartmuseum:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi
jobservice:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi
trivy:
storageClass: "local-path"
accessMode: ReadWriteOnce
size: 5Gi

# 外部数据库配置(使用前序部署的PostgreSQL)
database:
type: external
external:
host: "postgres.postgres.svc.cluster.local"
port: "5432"
username: "harbor"
password: "harbor123"
coreDatabase: "harbor"
clairDatabase: "harbor_clair"
notaryServerDatabase: "harbor_notary_server"
notarySignerDatabase: "harbor_notary_signer"

# 外部Redis配置
redis:
type: external
external:
addr: "redis.redis.svc.cluster.local:6379"
password: "redis123456"

# 启用ChartMuseum
chartmuseum:
enabled: true
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

# 启用Trivy漏洞扫描
trivy:
enabled: true
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"

# 资源限制
nginx:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

portal:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

core:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

jobservice:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

registry:
registry:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"

notary:
enabled: true
server:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
signer:
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"

5.3 部署Harbor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 部署Harbor
helm install harbor harbor/harbor \
--namespace harbor \
--version 1.13.2 \
-f harbor-values.yaml

# 或使用在线配置
helm install harbor harbor/harbor \
--namespace harbor \
--version 1.13.2 \
--set expose.type=ingress \
--set expose.tls.enabled=true \
--set expose.tls.secret.secretName=harbor-tls-secret \
--set expose.ingress.hosts.core=harbor.example.io \
--set expose.ingress.hosts.notary=notary.example.io \
--set externalURL=https://harbor.example.io \
--set harborAdminPassword=HarborAdmin123! \
--set persistence.enabled=true \
--set persistence.persistentVolumeClaim.registry.storageClass=local-path \
--set persistence.persistentVolumeClaim.registry.size=100Gi \
--set database.type=external \
--set database.external.host=postgres.postgres.svc.cluster.local \
--set database.external.port=5432 \
--set database.external.username=harbor \
--set database.external.password=harbor123 \
--set redis.type=external \
--set redis.external.addr=redis.redis.svc.cluster.local:6379 \
--set redis.external.password=redis123456 \
--set chartmuseum.enabled=true \
--set trivy.enabled=true \
--set notary.enabled=true

六、验证部署结果

6.1 验证组件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看Harbor Pod状态(首次启动需要2-3分钟)
kubectl get pods -n harbor -w

# 查看Service状态
kubectl get svc -n harbor

# 查看Ingress状态
kubectl get ingress -n harbor

# 查看证书状态
kubectl get certificate -n harbor

# 查看PVC状态
kubectl get pvc -n harbor

6.2 测试访问功能

1
2
3
4
5
6
7
8
9
10
11
# 等待Harbor完全启动
sleep 120

# 测试HTTPS访问
curl -k https://harbor.example.io

# 查看Harbor日志
kubectl logs -n harbor -l app=harbor --tail=50

# 测试Notary访问
curl -k https://notary.example.io

6.3 初始化Harbor

  1. 浏览器访问:https://harbor.example.io
  2. 用户名:admin
  3. 密码:HarborAdmin123!
  4. 首次登录后立即修改密码
  5. 创建项目(如gitops-demo

七、配置容器运行时访问

7.1 Docker客户端配置

1
2
3
4
5
6
7
8
9
10
# 获取Harbor CA证书
kubectl get secret harbor-tls-secret -n harbor -o jsonpath='{.data.ca\.crt}' | base64 --decode > harbor-ca.crt

# 配置Docker信任Harbor证书
sudo mkdir -p /etc/docker/certs.d/harbor.example.io
sudo cp harbor-ca.crt /etc/docker/certs.d/harbor.example.io/ca.crt
sudo systemctl restart docker

# 登录Harbor
docker login harbor.example.io -u admin -p HarborAdmin123!

7.2 Containerd配置(K3s节点)

1
2
3
4
# 配置Containerd信任Harbor证书
sudo mkdir -p /etc/containerd/certs.d/harbor.example.io
sudo cp harbor-ca.crt /etc/containerd/certs.d/harbor.example.io/ca.crt
sudo systemctl restart containerd

7.3 测试镜像推送

1
2
3
4
5
6
7
8
# 拉取测试镜像
docker pull alpine:latest

# 打标签
docker tag alpine:latest harbor.example.io/gitops-demo/alpine:latest

# 推送镜像
docker push harbor.example.io/gitops-demo/alpine:latest

八、Harbor与GitOps全链路集成

完成Harbor部署后,GitOps环境搭建系列已具备完整的端到端交付能力。以下阐述Harbor与系列中其他核心组件的联动关系。

8.1 全链路组件矩阵

组件 职责 在GitOps链路中的角色
Gitea 代码托管与版本控制 开发者推送代码的单一可信数据源
Tekton 云原生CI/CD流水线引擎 监听代码变更,构建镜像并推送至Harbor
Harbor 容器镜像仓库与安全扫描 存储签名镜像,提供可信镜像分发
ArgoCD GitOps持续部署控制平面 监听Harbor镜像Tag变化,自动同步至K3s

8.2 完整流水线联动流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────────────┐
│ GitOps 完整链路 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ① 开发者 ──push──▶ Gitea (Git仓库) │
│ │
│ ② Tekton Pipeline ──webhook触发──▶ 克隆代码 │
│ │
│ ③ Tekton Pipeline ──构建──▶ Docker镜像 │
│ │ │
│ │ docker push │
│ ▼ │
│ ④ Harbor (镜像仓库) ◀── 推送镜像 │
│ │ • 存储镜像bits │
│ │ • Trivy CVE扫描 │
│ │ • Notary镜像签名(可选) │
│ │
│ ⑤ ArgoCD Application ──监听镜像Tag变化──▶ 检测到新镜像 │
│ │ │
│ │ 更新Deployment镜像Tag │
│ ▼ │
│ ⑥ K3s集群 ──kubectl apply──▶ 新Pod以新镜像启动 │
│ │
└─────────────────────────────────────────────────────────────────┘

8.3 Harbor与Gitea、Tekton、ArgoCD联动

Harbor + Gitea(认证集成)

Harbor与Gitea可共用外部认证源(如LDAP/OIDC),实现统一身份管理:

1
2
3
4
5
6
7
8
# Harbor配置外部认证
harbor-core:
config:
auth_mode: "oidc_auth"
oidc_name: "Gitea OIDC"
oidc_endpoint: "https://gitea.example.io/oauth"
oidc_client_id: "harbor-client"
oidc_secret: "harbor-oidc-secret"

Harbor + Tekton(镜像构建推送)

Tekton Pipeline执行构建并将镜像推送至Harbor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Tekton Task: 构建并推送镜像至Harbor
spec:
steps:
- name: git-clone
image: alpine/git
script: |
git clone $(params.REPO_URL) $(params.REPO_DIR)

- name: build-image
image: docker:latest
script: |
docker build -t $(params.IMAGE):$(params.TAG) $(params.REPO_DIR)
docker tag $(params.IMAGE):$(params.TAG) \
harbor.example.io/gitops-demo/$(params.IMAGE):$(params.TAG)

- name: push-image
image: docker:latest
script: |
docker login harbor.example.io -u $(params.HARBOR_USER) -p $(params.HARBOR_PASS)
docker push harbor.example.io/gitops-demo/$(params.IMAGE):$(params.TAG)

Harbor + ArgoCD(镜像驱动部署)

ArgoCD Image Updater自动轮询Harbor镜像仓库,发现新Tag后更新配置并触发同步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ArgoCD Application(Image Updater注解)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitops-demo-app
annotations:
argocd-image-updater.argoproj.io/image-list: |
demo=harbor.example.io/gitops-demo/demo-image
argocd-image-updater.argoproj.io/demo.update-strategy: latest
argocd-image-updater.argoproj.io/write-back-method: git
spec:
source:
repoURL: https://github.com/your-org/gitops-configs.git
path: apps/demo-app
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: gitops-demo

8.4 安全集成要点

  1. 镜像签名链路:Tekton构建 → Notary签名 → 推送Harbor → 部署时验签,全链路可信;
  2. 漏洞扫描卡点:Harbor Trivy扫描未通过的CVE镜像(Critical/High),可配置阻止推送;
  3. 认证打通:Harbor与Gitea共用OIDC认证,实现统一身份管理;
  4. 网络隔离:Harbor仅通过Traefik 443端口暴露,K3s节点通过内部网络拉取镜像。

九、服务访问方式

9.1 集群内访问

  • Web界面harbor-core.harbor.svc.cluster.local
  • Registry APIharbor-registry.harbor.svc.cluster.local
  • ChartMuseumharbor-chartmuseum.harbor.svc.cluster.local

9.2 集群外访问

  • Web界面https://harbor.example.io
  • Registryharbor.example.io(Docker push/pull)
  • Notaryhttps://notary.example.io

十、日常运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看Harbor状态
kubectl get all -n harbor

# 查看日志
kubectl logs -f deployment/harbor-core -n harbor
kubectl logs -f deployment/harbor-registry -n harbor
kubectl logs -f deployment/harbor-portal -n harbor

# 重启组件
kubectl rollout restart deployment -n harbor

# 备份Harbor数据
kubectl exec -n harbor $(kubectl get pod -n harbor -l component=registry -o jsonpath='{.items[0].metadata.name}') -- tar czf /tmp/registry-backup.tar.gz /storage
kubectl cp harbor/$(kubectl get pod -n harbor -l component=registry -o jsonpath='{.items[0].metadata.name}'):/tmp/registry-backup.tar.gz ./registry-backup-$(date +%Y%m%d).tar.gz

# 查看资源使用情况
kubectl top pods -n harbor

十一、常见问题修复

问题现象 排查方向 修复方案
Harbor无法启动 数据库连接失败 检查PostgreSQL连接,验证用户名密码
证书错误 证书配置/Secret 检查cert-manager状态,验证证书Secret
镜像推送失败 认证/网络 检查Docker登录状态,验证网络连通性
PVC无法绑定 存储类/磁盘空间 检查local-path存储类,确认节点有足够空间
Trivy扫描失败 网络/资源 检查Trivy网络连通性,增加资源限制
Notary服务异常 证书/配置 检查Notary证书配置,验证域名解析

十二、配置参考

所有Harbor配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops/Harbor

该目录包含:

  • Harbor Helm values配置
  • 证书和路由配置
  • 数据库初始化脚本
  • 生产环境优化配置
  • 集成配置示例

总结

本文完成了Harbor在K3s集群中的标准化部署,实现了镜像仓库的完整功能。Harbor作为GitOps环境的镜像存储中枢,为自动化构建和部署提供了可靠的镜像管理能力。

部署完成后,建议配置Tekton Pipeline实现自动镜像构建和推送,配置ArgoCD实现镜像更新自动同步。至此,GitOps环境的所有核心组件已部署完成,形成了完整的自动化流水线。

一、核心定位

本文作为GitOps环境搭建系列的第八篇,聚焦Tekton CI/CD流水线的部署与配置。Tekton是云原生CI/CD框架,专为Kubernetes环境设计,提供声明式的流水线定义和任务执行能力。

在GitOps环境中,Tekton扮演”持续集成引擎”角色,作为GitOps流程的构建和测试环节,实现从代码提交到镜像构建的自动化。Tekton与Gitea、Harbor、ArgoCD协同工作,形成完整的”代码→构建→镜像→部署”自动化流水线。

二、部署前置检查

部署前需验证K3s集群状态及前序组件运行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证ArgoCD运行状态
kubectl get pods -n argocd

# 3. 验证cert-manager运行状态
kubectl get pods -n cert-manager

# 4. 验证Traefik运行状态
kubectl get pods -n kube-system -l app=traefik

# 5. 验证Gitea运行状态
kubectl get pods -n gitea

# 6. 验证域名解析
nslookup tekton.example.io

前置条件检查清单:

  • K3s集群运行正常
  • ArgoCD GitOps核心可用
  • cert-manager证书管理可用
  • Traefik反向代理可用
  • Gitea代码仓库可用
  • 域名tekton.example.io已解析至K3s节点IP
  • Git仓库https://gitea.example.io/gitea_admin/devops-deploy.git已创建并包含Tekton配置

三、基于ArgoCD部署Tekton

3.1 准备Git仓库配置

在Gitea仓库devops-deploy.git中创建Tekton配置目录:

1
2
3
4
5
6
# 克隆仓库
git clone https://gitea.example.io/gitea_admin/devops-deploy.git
cd devops-deploy

# 创建Tekton配置目录
mkdir -p tekton

3.2 创建ArgoCD应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# tekton/argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: tekton
namespace: argocd
spec:
project: default
source:
repoURL: https://gitea.example.io/gitea_admin/devops-deploy.git
path: tekton
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: tekton-pipelines
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m

3.3 创建Tekton核心部署配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# tekton/tekton-core.yaml
apiVersion: v1
kind: Namespace
metadata:
name: tekton-pipelines
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tekton-dashboard
namespace: tekton-pipelines
spec:
replicas: 1
selector:
matchLabels:
app: tekton-dashboard
template:
metadata:
labels:
app: tekton-dashboard
spec:
containers:
- name: dashboard
image: gcr.io/tekton-releases/github.com/tektoncd/dashboard/cmd/dashboard:v0.45.0
ports:
- containerPort: 9097
env:
- name: PORT
value: "9097"
- name: CLUSTER_NAME
value: "k3s-gitops"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: tekton-dashboard
namespace: tekton-pipelines
spec:
selector:
app: tekton-dashboard
ports:
- port: 9097
targetPort: 9097
type: ClusterIP

3.4 提交配置到Git仓库

1
2
3
4
# 添加配置文件
git add tekton/
git commit -m "feat: add Tekton deployment configuration"
git push origin main

四、配置HTTPS访问

4.1 创建Tekton证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# tekton/tekton-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: tekton-cert
namespace: tekton-pipelines
spec:
secretName: tekton-tls-secret
issuerRef:
name: selfsigned-cluster-issuer
kind: ClusterIssuer
commonName: tekton.example.io
dnsNames:
- tekton.example.io
duration: 2160h
renewBefore: 360h
privateKey:
algorithm: RSA
size: 2048
usages:
- server auth

4.2 配置IngressRoute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# tekton/tekton-ingressroute.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: tekton-websecure
namespace: tekton-pipelines
spec:
entryPoints:
- websecure
routes:
- match: Host(`tekton.example.io`) && PathPrefix(`/`)
kind: Rule
services:
- name: tekton-dashboard
passHostHeader: true
port: 9097
tls:
secretName: tekton-tls-secret

4.3 应用ArgoCD配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 应用ArgoCD Application配置
kubectl apply -f tekton/argocd-application.yaml

# 或直接创建Application
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: tekton
namespace: argocd
spec:
project: default
source:
repoURL: https://gitea.example.io/gitea_admin/devops-deploy.git
path: tekton
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: tekton-pipelines
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
EOF

五、验证部署结果

5.1 验证ArgoCD同步状态

1
2
3
4
5
6
7
8
9
# 查看ArgoCD应用状态
argocd app get tekton
argocd app sync tekton # 手动触发同步

# 查看同步历史
argocd app history tekton

# 查看应用资源
argocd app resources tekton

5.2 验证Tekton组件状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看Tekton命名空间资源
kubectl get all -n tekton-pipelines

# 查看Dashboard Pod状态
kubectl get pods -n tekton-pipelines -l app=tekton-dashboard

# 查看Service状态
kubectl get svc -n tekton-pipelines tekton-dashboard

# 查看证书状态
kubectl get certificate -n tekton-pipelines
kubectl describe certificate tekton-cert -n tekton-pipelines

# 查看IngressRoute状态
kubectl get ingressroute -n tekton-pipelines

5.3 测试访问功能

1
2
3
4
5
6
7
8
# 测试HTTPS访问
curl -k https://tekton.example.io

# 查看Dashboard日志
kubectl logs -n tekton-pipelines -l app=tekton-dashboard --tail=50

# 进入容器检查状态
kubectl exec -it $(kubectl get pod -n tekton-pipelines -l app=tekton-dashboard -o jsonpath='{.items[0].metadata.name}') -n tekton-pipelines -- /bin/sh

六、配置示例流水线

6.1 创建Tekton Pipeline资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# tekton/example-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: build-and-deploy
namespace: tekton-pipelines
spec:
params:
- name: git-url
type: string
description: Git repository URL
- name: image-name
type: string
description: Docker image name
tasks:
- name: fetch-source
taskRef:
name: git-clone
params:
- name: url
value: $(params.git-url)
- name: revision
value: main
workspaces:
- name: output
workspace: source

- name: build-image
runAfter: [fetch-source]
taskRef:
name: kaniko
params:
- name: IMAGE
value: $(params.image-name)
workspaces:
- name: source
workspace: source
- name: dockerconfig
workspace: docker-config

6.2 创建Tekton Task资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# tekton/tasks.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: git-clone
namespace: tekton-pipelines
spec:
workspaces:
- name: output
description: Git repository will be cloned here
params:
- name: url
description: Git repository URL
type: string
- name: revision
description: Git revision to clone
type: string
default: main
steps:
- name: clone
image: alpine/git
script: |
git clone $(params.url) $(workspaces.output.path)
cd $(workspaces.output.path)
git checkout $(params.revision)
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: kaniko
namespace: tekton-pipelines
spec:
workspaces:
- name: source
description: Source code workspace
- name: dockerconfig
description: Docker config.json workspace
params:
- name: IMAGE
description: Docker image name
type: string
steps:
- name: build-and-push
image: gcr.io/kaniko-project/executor:v1.9.0
env:
- name: DOCKER_CONFIG
value: /workspace/dockerconfig
command:
- /kaniko/executor
args:
- --dockerfile=$(workspaces.source.path)/Dockerfile
- --destination=$(params.IMAGE)
- --context=$(workspaces.source.path)

七、服务访问方式

7.1 集群内访问

  • Dashboard Web界面tekton-dashboard.tekton-pipelines.svc.cluster.local:9097
  • API访问:通过Service直接访问Tekton组件

7.2 集群外访问

  • Dashboard Web界面https://tekton.example.io
  • Git Webhook配置https://tekton.example.io(用于接收Gitea webhook)

八、日常运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看Tekton资源
kubectl get pipelines.tekton.dev -n tekton-pipelines
kubectl get tasks.tekton.dev -n tekton-pipelines
kubectl get pipelineruns.tekton.dev -n tekton-pipelines
kubectl get taskruns.tekton.dev -n tekton-pipelines

# 查看Dashboard日志
kubectl logs -f deployment/tekton-dashboard -n tekton-pipelines

# 重启Dashboard
kubectl rollout restart deployment tekton-dashboard -n tekton-pipelines

# 查看资源使用情况
kubectl top pods -n tekton-pipelines

# 清理完成的PipelineRun
kubectl delete pipelineruns.tekton.dev --all -n tekton-pipelines --field-selector=status.conditions[0].status=True

九、常见问题修复

问题现象 排查方向 修复方案
ArgoCD同步失败 Git仓库访问权限 检查ArgoCD Repository配置,添加访问凭证
Dashboard无法访问 IngressRoute/证书 检查IngressRoute配置,验证证书Secret是否存在
Pipeline执行失败 任务配置/资源权限 检查Task定义,验证ServiceAccount权限
镜像构建失败 Docker配置/网络 检查kaniko配置,验证网络连通性
Webhook不触发 Webhook配置/网络 检查Gitea webhook配置,验证网络可达性
资源占用过高 资源限制/并发数 调整资源限制,限制并发PipelineRun数量

十、配置参考

所有Tekton配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops/Tekton

该目录包含:

  • Tekton核心部署配置
  • 示例Pipeline和Task定义
  • Webhook集成配置
  • 生产环境优化配置
  • 监控和日志配置

总结

本文完成了Tekton在K3s集群中的标准化部署,基于ArgoCD实现了GitOps方式的CI/CD流水线管理。Tekton作为GitOps环境的持续集成引擎,为自动化构建和测试提供了强大的能力。

部署完成后,建议创建示例流水线验证构建功能,配置Gitea webhook实现自动触发,并设置适当的资源限制。下一篇文章将部署Harbor镜像仓库,为GitOps环境提供镜像存储和分发能力。

0%