wenmo8 发布的文章

重磅推出 Sdcb Chats:一个全新的开源大语言模型前端

在当前大语言模型(LLM)蓬勃发展的时代,各类 LLM 前端层出不穷。那么,
为什么我们还需要另一个 LLM 前端呢?

最初的原因在于
质感的追求
。市面上已有的开源或不开源的大语言模型前端在用户体验上都缺少了一些类似 ChatGPT 那样的质感。因此,我们打造了
Sdcb Chats
——一个基于 Apache 2.0 协议开源的项目,旨在为用户提供更优质的交互体验。

另一个原因是市场上几乎没有基于 .NET 实现的 LLM 前端,这让我个人感到非常遗憾和不满。
Sdcb Chats 旨在填补这个空白
,不仅为 .NET 社区带来一个功能强大的大语言模型前端,同时也展示 .NET 在现代 AI 应用中的潜力和优势。

消息内容即是一棵树

在与大语言模型交互时,传统的对话形式可能会导致用户与模型“争吵”。
Sdcb Chats 引入了树状消息结构
,即在同一位置,用户消息可以有多个回复,LLM 的响应也可以有多个。

这种设计的优势在于:

  • 当您认为 LLM 的回复不正确时,您可以修改提示词(Prompt)或请求 LLM 重新回复,而不是继续争论。
  • 提供了更灵活的对话路径,方便回溯和比较不同的回答。


动图1:Sdcb Chats 的主界面展示

如图,与其在聊完之后再与LLM争吵,不如修改Prompt。

遗憾的是,在我开发Chats之前,并没有一个开源的LLM前端支持这个功能,这也是促成我要重新再做一个的根本原因。


大语言模型功能篇

多语言支持


动图2:通过修改浏览器语言,Chats 支持多语言显示

Sdcb Chats 支持多语言界面
。您只需修改浏览器的语言设置,Chats 就会自动适配您的语言偏好,为全球用户提供便捷。

聊天性能统计


动图3:详细的聊天性能统计

每次与 LLM 的对话,
Chats 都会进行详尽的统计
,包括:

  • Token 数量
  • 延迟时间
  • 返回次数
  • 客户端 IP
  • 用户代理(User Agent)
  • 用户相关信息

这些数据有助于您日后进行分析和优化。

多种大语言模型支持


动图4:支持多种模型提供商,内置超过 100 款模型

Sdcb Chats 内置了对 9 种模型提供商的支持
,包括 OpenAI 兼容协议在内。此外,还预置了超过 100 款模型的信息,方便您快速使用。

支持配置新的大语言模型


动图5:演示如何新增 DeepSeek 和零一万物模型

如果预置的模型不能满足您的需求,您可以
自行添加新的模型配置
。在上述动图中,我们演示了如何添加 DeepSeek 和零一万物模型。

视觉模型支持


动图6:支持多模态视觉模型与图床功能

为了增强用户体验,
Chats 支持视觉模型
。我们为 Chats 添加了一层图床,方便上传和管理图片。支持的图床形式包括:

  • 本地文件
  • AWS S3
  • Minio
  • Azure Blob Storage
  • 阿里云 OSS

API 网关功能


动图7:强大的 API 网关功能

很多开源项目声称兼容 OpenAI API 协议的 API 网关功能,但可能仅仅是 HTTP 协议的转发。
Chats 的 API 网关不仅支持按用户权限配置模型、限制模型量,还支持目的地为非 OpenAI 协议的模型提供商,进行透明代理

API 网关支持参数


动图8:全面支持 OpenAI API 的参数和功能

Chats 的 API 网关全面支持 OpenAI API 的所有参数
,包括最新的
max_response_tokens
(适用于最新的 OpenAI o1 模型)和
response_format=json_schema
等功能。此外,响应的结束原因等信息也会忠实于源 API 的响应,确保兼容性和准确性。


部署与兼容性

简单部署:Docker 支持


图片9:复杂的 GitHub Actions CICD 流程

Sdcb Chats 提供了便捷的 Docker 部署方式
。我们在 GitHub Actions 中编写了复杂的 CICD 流程,会自动编译包括 Windows、Linux 等 4 种不同操作系统的原生支持 Docker 镜像。

动态选择合适的 Docker 镜像


图片10:通过 manifest 部署的 Docker 标签

通过使用
docker.io/sdcb/chats:latest
这样的 Docker 标签,
Docker 会根据您的操作系统和平台架构(如 amd64、arm64 等)动态选择合适的 Docker 镜像下载
,这大大简化了部署流程。Chats 提供了以下几个镜像:

描述 Docker 镜像
Latest docker.io/sdcb/chats:latest
r docker.io/sdcb/chats:r
Linux x64 docker.io/sdcb/chats:r{version}-linux-x64
Linux ARM64 docker.io/sdcb/chats:r{version}-linux-arm64
Windows Nano Server 1809 docker.io/sdcb/chats:r{version}-nanoserver-1809
Windows Nano Server LTSC 2022 docker.io/sdcb/chats:r{version}-nanoserver-ltsc2022

说明:

  • Latest

    r{version}
    镜像中已经包含了以下四个操作系统版本的支持:
    • Linux x64
    • Linux ARM64
    • Windows Nano Server 1809(适用于 Windows Server 2019)
    • Windows Nano Server LTSC 2022(适用于 Windows Server 2022)

因此,用户在使用
docker pull
时,无需指定具体的操作系统版本,Docker 的 manifest 功能会自动选择适合您系统的正确版本。这确保了用户能够轻松获取与其环境兼容的镜像,避免了选择错误镜像的风险。

请注意,
r{version}
中的
{version}
表示具体的版本号,例如
r141
(在编写文档时的最新版本号)。通过这种方式,您可以始终获取到最新或指定版本的最优匹配版本,确保部署的快速和可靠。

简单部署——Linux 容器演示


动图11:在 Linux 上部署 Chats,虽然图片是在Windows pwsh中演示,但我Docker Desktop启用的是Linux容器

在 Linux 上,您可以按照以下步骤轻松部署 Chats:

  1. 创建
    AppData
    文件夹并设置权限:

    mkdir ./AppData && chmod 777 ./AppData
    
  2. 运行 Docker 命令:

    docker run --restart unless-stopped --name sdcb-chats -v ./AppData:/app/AppData -p 8080:8080 sdcb/chats:latest
    

创建之后,默认的用户名为
chats
,默认密码为:
RESET!!!
,请注意,为了您的安全,请登录之后立即修改您的默认密码。

简单部署——Windows 容器演示


动图12:在 Windows 上部署 Chats

许多朋友可能不知道,
Windows 也有原生的 Docker
。Chats 支持在 Windows 上以进程隔离的方式部署 Docker,这与 Linux 的部署方式类似,但映射的目录变为了
C:\app\AppData

简单部署——可运行文件安装


图片13:GitHub Actions 的 Summary 页面

对于不方便使用 Docker 的用户,
Sdcb Chats 提供了可直接运行的二进制文件
。我们通过 GitHub Actions 的 Matrix strategy,自动编译了
8 种不同操作系统或平台的 Native AOT 编译版本
。对于不便使用 Docker 部署的环境,Chats 提供了 8 种操作系统或架构的直接部署选项,您可以从以下链接获取相应的编译包:

平台 Github下载链接 替代下载链接
Windows 64位 chats-win-x64.zip chats-win-x64.zip
Linux 64位 chats-linux-x64.zip chats-linux-x64.zip
Linux ARM64 chats-linux-arm64.zip chats-linux-arm64.zip
Linux musl x64 chats-linux-musl-x64.zip chats-linux-musl-x64.zip
Linux musl ARM64 chats-linux-musl-arm64.zip chats-linux-musl-arm64.zip
macOS ARM64 chats-osx-arm64.zip chats-osx-arm64.zip
macOS x64 chats-osx-x64.zip chats-osx-x64.zip
依赖.NET的通用包 chats.zip chats.zip
纯前端文件 chats-fe.zip chats-fe.zip


图片14:上传到 GitHub Release 和个人 Minio 服务器的编译文件

考虑到国内用户访问 GitHub 速度较慢,我们将编译文件同时上传到了个人的 Minio 服务器,方便快速下载。


动图15:演示如何下载和部署 Windows 版 Chats

直接下载适合您操作系统的压缩包,解压后双击
Chats.BE.exe
即可运行(对于 Linux 也同样,直接运行里面的
Chats.BE
即可),无需安装 .NET SDK 或 Runtime。

版本和下载说明

  1. 指定版本下载地址

    • 若需下载特定版本的 Chats,将链接中的
      release/latest/download
      替换为
      releases/download/r-{version}
      。例如,版本
      141
      的 Linux ARM64 文件链接为:
      https://github.com/sdcb/chats/releases/download/r-141/chats-linux-arm64.zip
      

执行文件目录结构和运行说明

解压AOT可执行文件后的目录结构如下:

C:\Users\ZhouJie\Downloads\chats-win-x64>dir
 2024/12/06  16:35    <DIR>          .
 2024/12/06  16:35    <DIR>          ..
 2024/12/06  16:35               119 appsettings.Development.json
 2024/12/06  16:35               417 appsettings.json
 2024/12/06  16:35           367,144 aspnetcorev2_inprocess.dll
 2024/12/06  16:35        84,012,075 Chats.BE.exe
 2024/12/06  16:35           200,296 Chats.BE.pdb
 2024/12/06  16:35         1,759,232 e_sqlite3.dll
 2024/12/06  16:35           504,872 Microsoft.Data.SqlClient.SNI.dll
 2024/12/06  16:35               465 web.config
 2024/12/06  16:35    <DIR>          wwwroot
  • 启动应用
    :运行
    Chats.BE.exe
    即可启动 Chats 应用,该文件名虽指“后端”,但实际同时包含前端和后端组件。
  • 数据库配置
    :默认情况下,应用将在当前目录创建名为
    AppData
    的目录,并以 SQLite 作为数据库。命令行参数可用于指定不同的数据库类型:
    .\Chats.BE.exe --DBType=mssql --ConnectionStrings:ChatsDB="Data Source=(localdb)\mssqllocaldb; Initial Catalog=ChatsDB; Integrated Security=True"
    

    • 参数
      DBType
      :可选
      sqlite

      mssql

      pgsql
    • 参数
      --ConnectionStrings:ChatsDB
      :用于指定数据库的ADO.NET连接字符串。

特殊说明

  • 对于下载的
    chats.zip
    ,将需要.NET SDK支持。安装 .NET 运行时后,使用
    dotnet Chats.BE.dll
    启动程序。

多数据库支持(感谢 EF Core)


图片16:支持多种数据库类型

Chats 的后端使用 Entity Framework Core 访问数据库
,支持:

  • SQLite:
    DBType=sqlite
  • SQL Server:
    DBType=mssql
  • PostgreSQL:
    DBType=pgsql

在程序首次运行且数据库不存在时,Chats 会尝试自动创建数据库、相关表并插入基础数据。

多文件服务支持


图片17:支持多种图床形式

Chats 支持本地文件、Minio/AWS S3、阿里云 OSS 和 Azure Blob Storage 五种图床形式
,您可以根据需要进行配置。

我们创新性地使用了云服务厂商提供的
Presigned URL 或 SAS URL
,这样可以节省 Chats 服务器的带宽,并提高图片的访问速度,增强用户体验,比如:

此外,我们引入了
PartialBufferedStream
,在读取用户上传图片的元信息(如宽高)时,不需要完全读入整个文件,节省内存。实际运行中,
Chats 的内存占用仅约 200MB
,具体代码可以从这个链接中参考:
https://github.com/sdcb/chats/blob/main/src/BE/Services/ImageInfo/PartialBufferedStream.cs


用户管理与安全

完善的用户管理功能


图片19:管理员的用户管理界面

作为一款面向企业应用的软件,
Sdcb Chats 提供了强大的用户管理功能
。管理员可以:

  • 限制用户能使用的模型
  • 限制 token 数量
  • 限制聊天次数
  • 设置模型过期时间

用户有余额概念,模型的价格可配置。余额通过
记账表
实现,每一笔开销或收入都会记录,账户余额通过记账表统计,确保并发请求下余额计算的准确性。

支持 Keycloak SSO 登录


动图20:使用 Keycloak SSO 登录

企业通常有自己的身份认证系统
,如 Keycloak SSO。Chats 支持集成这些系统,登录时如果用户不存在,会自动创建。只需配置相应的
well-known URL

client ID

client secret

手机号码验证码登录


图片21:手机验证码登录界面

为方便国内用户,
Chats 支持手机号码验证码登录
,提升用户体验和安全性。

邀请码与注册


图片22:邀请码配置界面

如果您不想开放注册,
可以使用邀请码机制
。邀请码可以限制邀请次数,初始赠送金额,以及模型、token 数、使用次数和过期时间等。


图片23:通过邀请码注册的新用户初始权限设置

当 token 数和聊天次数都为 0 时,将使用余额,按照模型配置的价格扣费。


Sdcb Chats 现已开源!

项目地址:
https://github.com/sdcb/chats

  • 项目最初由我的好朋友文旺发起
  • 历经超过 1000 次签入打磨
  • 我们公司同事已经内部使用(dog food)一年

Sdcb 的含义

Sdcb:Serving dotNet, Constructing Brilliance(服务 .NET,构建卓越)

我们致力于为 .NET 社区和广大开发者提供高质量的开源项目。

开源协议

Apache 2.0 开源协议(可免费商用)

您可以自由地使用、修改和分发 Sdcb Chats,无需商用授权。

欢迎 Star ⭐

如果您觉得这个项目对您有帮助,
请在 GitHub 上给我们一个 Star
!您的支持是我们前进的最大动力。


感谢您的阅读和支持!让我们一起用 Sdcb Chats 探索大语言模型的无限可能!

11月14日,YashanDB在“2024国产数据库创新生态大会”上正式发布YashanDB V23.3版本,定位为面向企业核心的通用数据库,具备1:1替代Oracle的能力。

当前关键行业数字化转型已进入深水区和攻坚阶段,规模化和核心系统替代是这一阶段的显著特征,业界亟需更经济、更普适的规模化替代方案。YashanDB V23.3面向1:1替代的关键需求,在兼容性、性能、可用性、安全性、迁移能力、运维能力六大方面进一步优化增强和突破,并全方位支持基于共享集群架构数据库的应用,
为金融核心场景提供从功能、高可用到性能的端到端1:1替代解决方案
。该版本已在官网
https://download.yashandb.com/download
全面开放下载,包含单机主备、共享集群、分布式多种产品形态,所有用户均可体验。

本文将重点解析YashanDB V23.3在兼容性、性能、可用性、安全性、迁移能力、运维能力六大方面的最新进展
。我们将坚定“专为核心场景打造的1:1替代方案”战略,以三不变(应用、架构、运维不变)、两对等(性能、可用性可靠性对等)、一更优(安全性更优),高效支撑全行业国产化规模替代及数字化转型升级。

产品能力

详细说明

高兼容

Oracle兼容性由90%提升至99%、新增MySQL兼容性形态

高性能

算子性能增强;共享集群性能提升,4节点集群的TPCC达到520万tpmC

高可用

支持逻辑备库、主备共享集群,推出基于共享集群的两地三中心方案

强安全

实现表级、列级加密,实现行级访问控制,实现数据动态脱敏,全面支持国密算法

易迁移

新增增量迁移组件,实现异构数据实时增量同步

易运维

新增支持管控YashanDB共享集群,增强数据库审计及监控可视化能力

高兼容:Oracle、MySQL两大兼容性提升

过去一年,YashanDB通过在各种项目实践过程中,不断增加对Oracle的兼容性,将Oracle的兼容性从90%提升到99%;同时将兼容开源生态也纳入新的战略方向,首次实现对MySQL开源生态的兼容,V23.3版本正式兼容MySQL 5.7版本。

Oracle兼容性提升至99%,代码“零”修改

YashanDB V23.3版本广泛吸纳了在银行、证券、基金、能源、政务等多个业务场景中的实际需求,持续提升产品的Oracle兼容性,让更多的业务场景可以利用V23.3进行平滑应用迁移。具体而言,从SQL语言、PL语言、系统包、函数四大维度增强Oracle兼容性,具体新增主要特性见下图:

image.png

新兼容MySQL,实现核心能力兼容

近年来,开源软件漏洞和开源软件供应风险事件频发,例如Apache日志严重漏洞、Linux xz压缩工具恶意植入以及最近Linux的内核除名俄罗斯的开发者。
今年崖山将兼容MySQL开源生态作为新的战略方向,旨在为MySQL用户提供更安全可靠且高性价比的数据库解决方案
。YashanDB V23.3通过协议兼容、运维管理、语法语义以及数据类型四大特性支持,实现MySQL核心能力兼容。

协议兼容
。目前YashanDB通过两种方式实现MySQL协议兼容。一种是使用MySQL驱动,通过协议兼容方式实现连接;另一种模式是使用YashanDB JDBC驱动。当前YashanDB已实现两类协议的兼容,100%兼容MySQL协议5.7的常用命令字,通过MySQL客户端直连YashanDB,同时也支持通过YashanDB的协议连接数据库,这种方式在提供MySQL语法兼容的基础上,还可以提供YashanDB高可用等更多能力。

运维管理兼容
。支持information schema、MySQL模式下的系统视图以及MySQL特有的DCL、变量类型。

语法语义兼容
。YashanDB采用重载的方式,实现MySQL语法的兼容。针对标准SQL语法,直接继承YashanDB现有的语法体系;针对MySQL特有的语法和语义,通过覆盖的方式,严格按MySQL模式解析和执行。

数据类型兼容
。兼容MySQL常用数据类型,支持100种以上的MySQL常用内置函数。

高性能:算子性能、共享集群性能提升

执行算子批量加速提升5倍以上

YashanDB V23.3版本新增表扫描、聚集、JOIN、GROUP等执行算子批量加速能力。

YashanDB的SQL执行引擎采用经典火山模型,使用递归算法进行算子树的迭代。为了减少算子树的迭代次数,YashanDB引入批量执行模式,单次迭代按批量数据进行加速。

我们模拟了
200多种
业务场景进行了对比测试,批量加速对比单行模式有
了5到10倍
的性能提升,算子性能显著提升,主流场景持平Oracle,部分场景优于Oracle。

崖山共享集群首发4节点,TPCC达520W

在最新版本中,崖山共享集群YAC已突破
四个节点

线性扩展比达0.7
,在X86架构下集群4节点的TPCC达到
520万 tpmC
。YashanDB持续突破和优化共享集群核心技术,保持产品竞争优势:

  • 通过去中心化事务管理、跨实例事务托管、事务资源动态扩展等核心技术,实现了高性能、多实例对等的事务处理能力。

  • 基于全局资源管理组件,通过聚合内存技术,提供数据缓存、锁等全局资源的跨实例高效协同和控制能力。

  • 针对国产硬件平台,从数据库机制(如锁本地化、资源分区等)和硬件平台(如网络调优、NUMA配置优化等)两方面协同优化,充分发挥硬件能力,提升数据库系统性能。

图片

高可用:新增逻辑备库、集群两地三中心方案

新支持逻辑备库,保障主库故障快速恢复

YashanDBV23.3版本对高可用能力持续进行优化,除了物理备库之外,新版本支持了逻辑备库,在同步、容灾、升级等场景有重要的应用价值:

数据容灾
:主数据库出现故障时,逻辑备库可用于迅速恢复数据,继续提供业务。

滚动升级
:基于逻辑备库的滚动升级可以大幅降低业务停机时间,降低升级风险。

数据同步
:异构表的复制(例如从行表到列表)、细粒度过滤(复制特定的表或数据操作)。

备机可写
:逻辑备库具有写能力,可以用于业务演练和分析测试。

支持主备集群,推出两地三中心方案

YashanDB基于V23.3版本推出共享集群形态的两地三中心容灾方案,通过同城双活双中心以及异地灾备中心部署,在保障集群级高可用基础上实现机房级故障、区域级故障的无损容灾,故障之后自动切换,全方位保障企业数据安全和业务连续性。

对于
同城双活双中心
,两个数据中心之间采用同步复制策略,每个数据中心部署多节点的共享集群数据库。通常情况下,主生产环境由主数据中心的多个对等节点提供业务负载均衡能力,单个节点故障可以在
10S以内
完成数据中心内部的故障切换;在出现整个主中心完全不可用时,如存储故障、机房级故障等情况,业务可以快速的切换到同城的备数据中心,切换时间
小于30S

如果对数据安全有更高要求,比如区域级容灾能力,那么
异地容灾中心
就是数据安全的终极防线。该中心采用异步复制的策略,减少对主数据中心的性能影响,同时可以通过非对等部署(异地容灾部署更少节点)来降低容灾成本。当其他数据中心都不可用时,业务可以在
60S以内
切换到异地的数据中心。

图片

强安全:增强数据安全特性和策略

YashanDB在内核层面构建纵深防御体系,从访问管理、威胁检测和数据保护三个层面形成多层的防护和安全闭环,由内向外解决安全问题。

在最新版本中,YashanDB加强了认证、授权、审计、加密、冗余等多方面安全能力,
全面支持国密算法
,包括存储加密、通讯加密、数据校验等全部加密相关场景,相关安全能力
已通过商用密码产品认证、IT产品信息安全认证EAL4增强级(EAL4+)证书、网络关键设备和网络安全专用产品认证以及网络系统安全等级保护三级认证等证书。

图片

易迁移:新增增量迁移能力、一致性校验能力

崖山在去年发布了崖山迁移平台YMP,在新版本中,崖山从元数据评估与迁移、数据迁移、数据校验、平台能力四方面大幅提升YashanDB 迁移能力。此外,
YMP新增增量迁移组件,支持在线实时迁移增量数据
,支持多种数据库间迁移(目前开放Oracle、YashanDB,后续会陆续集成其他数据库),具备异构高度自适应、低侵入部署、全增衔接、“精确一次”断点续传、DDL实时同步等功能,为企业数据迁移提供新方案基础组件,减少数据库迁移业务割接时间至秒级。

元数据评估与迁移

  • 新增支持多种源端数据库。在Oracle、MySQL基础上,新支持DM、YashanDB;

  • 支持离线评估SQL文件、Mybatis配置等多种文件格式;

  • 实现更细粒度的权限要求,优化使用体验和安全性。

数据迁移

  • 在全量迁移的基础上,首次支持增量数据同步,具备全增衔接、断点续传等能力;

  • 支持多种迁移方式,满足不同业务、硬件的性能要求;

  • 全流程采用并行架构,性能优异。

数据校验

  • 首次支持一致性校验能力;

  • 提供统计校验、部分校验和全量校验能力等多种校验方案;

  • 支持自定义校验规则和阈值等;

  • 支持校验差异数据的展示和过滤,便于数据对比。

平台能力

  • 优化评估、迁移、校验等报告展示,支持历史报告管理,提高用户数据分析能力;

  • 优化任务调度和细粒度管理,支持评估、元数据迁移、数据迁移和校验任务的自定义组合。

易运维:新支持YAC管控,优化异地管理

崖山监控运维平台YCM致力于实现数据库运维的自动化、可视化,减少数据库管理人员的日常冗余工作。在最新版本中,YCM从以下几方面新增大量特性,实现更全面且实时的监控管理和增强高可用性:

  • YCM新版本
    支持YashanDB共享集群
    ,可以一站式管控崖山数据库的生命周期和运维工作。

  • 在崖山共享集群两地三中心模式下,实现YCM
    互联互通
    ,简化异地高可用管理。

  • YCM提高自身高可用能力,
    支持主备模式、自动备份等能力。

  • 在易用性上,YCM
    加强了数据库审计、AWR、TOP SQL等监控可视化能力
    ,便于企业对数据库的实时管控。

未来YashanDB将围绕多租户、软硬协同、应对未来数据挑战三大目标继续优化。多租户方面
,持续完善资源共享与隔离方案解决企业成本和业务发展需求,降低企业数据库运维成本,提供弹性扩展能力;
软硬协同方面
,通过对硬件的深度整合,进一步挖掘网络、内存等硬件资源效能,继续提升数据库系统性能;
面向未来数据挑战
,YashanDB计划通过多机并行计算技术,进一步提升超大集群的扩展能力,在保持联机交易低延时,高并行的基础上,同时提供海量数据分析能力,解决客户多样化的数据使用需求。

YashanDB企业版、个人版及相应配套工具的最新版本均已在官网开放下载,没有任何限制跟门槛,欢迎所有的用户、开发者下载体验。

下载链接
https://download.yashandb.com/download

在此,衷心感谢每一位用户对崖山数据库YashanDB的支持。用户的声音是我们未来努力的方向,我们诚挚地邀请大家继续向我们提出更多建议,也欢迎大家加入YashanDB官方技术交流群,共同探讨数据库相关的最新技术、趋势和挑战。

图片

本文使用
文章同步助手
同步

在上一篇博客中,探讨了如何使用 Python 和
hailo_model_zoo
中预编译的模型来实现目标检测。本篇博客将深入介绍如何将用户自定义训练的模型转换并优化为能够在 Hailo NPU 上高效运行的
.hef
模型。

Python 环境配置

为了将自定义模型编译为
.hef
模型,需要安装
Hailo Dataflow Compiler
(DFC) 工具。登录 Hailo 的网站
https://hailo.ai/developer-zone/software-downloads
,找到对应 Python 版本的
.whl
文件,并按照下面的步骤创建虚拟环境并安装必要的软件包:

conda create -n hailo-model python=3.10  # 创建虚拟环境
conda activate hailo-model  # 激活虚拟环境
sudo apt install libgraphviz-dev
pip install hailo_dataflow_compiler-3.29.0-py3-none-linux_x86_64.whl  # 安装 Hailo Dataflow Compiler Python 包

将自定义模型转换为
.hef
模型需要三步:

  1. 将 Tensorflow 或 ONNX 模型转换成 Hailo Archive 模型(
    .har
    )。

  2. .har
    模型进行量化。
  3. 编译为 Hailo Executable File 模型(
    .hef
    )。

转换

Tensorflow 与 ONNX 模型都可以进行转换,这里以
yolov8n
的 ONNX 模型为例,首先引入软件包并定义相关变量。

from hailo_sdk_client import ClientRunner
import os
import cv2
import numpy as np

input_size = 640  # 模型输入的尺寸
chosen_hw_arch = "hailo8l"  # 要使用的 Hailo 硬件架构,这里是 Hailo-8L
onnx_model_name = "yolov8n"  # 模型的名字
onnx_path = "yolov8n.onnx"  # 模型的路径
hailo_model_har_path = f"{onnx_model_name}_hailo_model.har"  # 转换后模型的保存路径
hailo_quantized_har_path = f"{onnx_model_name}_hailo_quantized_model.har"  # 量化后模型的保存路径
hailo_model_hef_path = f"{onnx_model_name}.hef"  # 编译后模型的保存路径

接着实例化
ClientRunner
类,并调用
translate_onnx_model()
方法进行转换。

runner = ClientRunner(hw_arch=chosen_hw_arch)
hn, npz = runner.translate_onnx_model(model=onnx_path, net_name=onnx_model_name)  # 将 onnx 模型转为 har
runner.save_har(hailo_model_har_path)  # 保存转换后的模型

在模型结构较为简单时,通常不会报错。当模型结构较为复杂时,会存在 Hailo NPU 不支持的算子,从而报错导致转换失败。NPU 支持的算子可以查询官网的数据手册,或者查看下文参考中的链接。例如在转换 YOLOv8 模型时会提示以下错误信息:

hailo_sdk_client.model_translator.exceptions.ParsingWithRecommendationException: Parsing failed. The errors found in the graph are:
 UnsupportedShuffleLayerError in op /model.22/dfl/Reshape: Failed to determine type of layer to create in node /model.22/dfl/Reshape
Please try to parse the model again, using these end node names: /model.22/Concat_3

出现错误时有两种解决方案。一是根据报错信息,使用 Netron
https://netron.app
查看模型结构,并修改原始模型,移除或替换 Hailo NPU 不支持的算子。二是报错信息中会推荐解决方法,在转换时绕过不支持的算子,那么
translate_onnx_model()
方法则需要传递额外的参数:

  • start_node_names
    :原始模型中开始转换的节点(对应新模型的输入)的名称。
  • end_node_names
    :原始模型中停止转换的节点(对应新模型的输出)的名称。
  • net_input_shapes

    start_node_names
    输入的尺寸,如常见的
    [b, c, h, w]

节点的名称可以使用 Netron 查看,或者使用下面的程序遍历打印节点的名称。

import onnx

onnx_path = "yolov8n.onnx"
model = onnx.load(onnx_path)

print("Input Nodes:")
for input in model.graph.input:
    print(input.name)
print("Output Nodes:")
for output in model.graph.output:
    print(output.name)
print("Nodes:")
for node in model.graph.node:
    print(node.name)

根据上面的错误信息提示,要将停止转换的节点修改为
/model.22/Concat_3
,修改后的程序如下。

hn, npz = runner.translate_onnx_model(model=onnx_path, net_name=onnx_model_name, start_node_names=["images"], end_node_names=["/model.22/Concat_3"], net_input_shapes={"images": [1, 3, input_size, input_size]})

程序执行后并未报错,但在最后一步编译时会出现 Hailo NPU 内存不够的情况,我们再观察一下转换时输出的日志:

[info] Translation started on ONNX model yolov8n
[info] Restored ONNX model yolov8n (completion time: 00:00:00.06)
[info] Extracted ONNXRuntime meta-data for Hailo model (completion time: 00:00:00.21)
[info] NMS structure of yolov8 (or equivalent architecture) was detected.
[info] In order to use HailoRT post-processing capabilities, these end node names should be used: /model.22/cv2.0/cv2.0.2/Conv /model.22/cv3.0/cv3.0.2/Conv /model.22/cv2.1/cv2.1.2/Conv /model.22/cv3.1/cv3.1.2/Conv /model.22/cv2.2/cv2.2.2/Conv /model.22/cv3.2/cv3.2.2/Conv.
...

日志建议将停止转换的节点修改为
/model.22/cv2.0/cv2.0.2/Conv /model.22/cv3.0/cv3.0.2/Conv /model.22/cv2.1/cv2.1.2/Conv /model.22/cv3.1/cv3.1.2/Conv /model.22/cv2.2/cv2.2.2/Conv /model.22/cv3
。即在 NMS 处理前将模型切割,查阅 Hailo 开发者论坛得知,Hailo NPU 不具备进行 NMS 运算的能力,这一部分将在 CPU 上运行。Hailo 的 GitHub 仓库提供了主流模型转换时结束节点的名称,具体请查看下文参考中的链接。最终,程序修改为:

hn, npz = runner.translate_onnx_model(model=onnx_path, net_name=onnx_model_name, start_node_names=["images"], end_node_names=["/model.22/cv2.0/cv2.0.2/Conv", "/model.22/cv3.0/cv3.0.2/Conv", "/model.22/cv2.1/cv2.1.2/Conv", "/model.22/cv3.1/cv3.1.2/Conv", "/model.22/cv2.2/cv2.2.2/Conv", "/model.22/cv3.2/cv3.2.2/Conv"], net_input_shapes={"images": [1, 3, input_size, input_size]})

量化

模型量化(Quantization)是将深度学习模型中的权重和激活值(输出)从高精度的浮点数(如 float32)转换为低精度的数据类型(如 int8),以减少模型的存储需求、加快推理速度并降低功耗,这一过程对于将深度学习模型部署到边缘设备中特别重要。这里使用的是训练后量化,即在已经训练好的模型上直接进行量化,无需重新训练或微调,但可能会导致一些准确性的损失。

首先需要准备好量化时使用的校准数据集。校准数据集主要用于帮助确定量化参数,以尽量减少量化过程对模型性能的影响。校准数据集的质量直接影响到量化模型的最终性能,应该尽可能涵盖所有的数据变化,以确保量化后的模型在不同条件下都能有良好的泛化能力。校准数据集不需要标签,其主要用于收集每一层激活值的统计数据,例如最小值、最大值、平均值和标准差等。这些统计信息用于确定如何最佳地映射浮点数到整数,从而保持模型性能,这个过程不需要知道输入数据对应的标签,只需要了解数据的分布特性。

本篇博客用到的 YOLOv8 模型是使用 COCO 数据集训练的,下面就以此为例进行校准数据集的准备。

images_path = "data/images"  # 数据集图像路径
dataset_output_path = "calib_set.npy"  # 处理完成后的保存路径

images_list = [img_name for img_name in os.listdir(images_path) if os.path.splitext(img_name)[1] in [".jpg", ".png", "bmp"]][:1500]  # 获取图像名称列表
calib_dataset = np.zeros((len(images_list), input_size, input_size, 3))  # 初始化 numpy 数组

for idx, img_name in enumerate(sorted(images_list)):
    img = cv2.imread(os.path.join(images_path, img_name))
    resized = cv2.resize(img, (input_size, input_size))  # 调整原始图像的尺寸为模型输入的尺寸
    calib_dataset[idx,:,:,:]=np.array(resized)
np.save(dataset_output_path, calib_dataset)

接着实例化
ClientRunner
类,并调用
optimize()
方法进行量化。

calib_dataset = np.load(dataset_output_path)
runner = ClientRunner(har=hailo_model_har_path)
runner.optimize(calib_dataset)  # 量化模型
runner.save_har(hailo_quantized_har_path)  # 保存量化后的模型

在量化过程中还可以添加一些脚本对参数进行设置,例如
model_optimization_flavor()
设置量化的级别、
resources_param()
设置模型能够使用的资源量等。
hailo_model_zoo
仓库提供了主流模型的参数设置脚本,具体请查看下文参考中的链接。程序示例如下。

alls_lines = [
    'model_optimization_flavor(optimization_level=1, compression_level=2)',
    'resources_param(max_control_utilization=0.6, max_compute_utilization=0.6, max_memory_utilization=0.6)',
    'performance_param(fps=5)'
]
runner.load_model_script('\n'.join(alls_lines))
runner.optimize(calib_dataset)

编译

最后使用
compile()
方法完成模型的编译。

runner = ClientRunner(har=hailo_quantized_har_path)
compiled_hef = runner.compile()
with open(hailo_model_hef_path, "wb") as f:
    f.write(compiled_hef)

完整程序如下。

from hailo_sdk_client import ClientRunner
import os
import cv2
import numpy as np

input_size = 640  # 模型输入的尺寸
chosen_hw_arch = "hailo8l"  # 要使用的 Hailo 硬件架构,这里是 Hailo-8L
onnx_model_name = "yolov8n"  # 模型的名字
onnx_path = "yolov8n.onnx"  # 模型的路径
hailo_model_har_path = f"{onnx_model_name}_hailo_model.har"  # 转换后模型的保存路径
hailo_quantized_har_path = f"{onnx_model_name}_hailo_quantized_model.har"  # 量化后模型的保存路径
hailo_model_hef_path = f"{onnx_model_name}.hef"  # 编译后模型的保存路径
images_path = "data/images"  # 数据集图像路径

# 将 onnx 模型转为 har
runner = ClientRunner(hw_arch=chosen_hw_arch)
hn, npz = runner.translate_onnx_model(model=onnx_path, net_name=onnx_model_name, start_node_names=["images"], end_node_names=["/model.22/cv2.0/cv2.0.2/Conv", "/model.22/cv3.0/cv3.0.2/Conv", "/model.22/cv2.1/cv2.1.2/Conv", "/model.22/cv3.1/cv3.1.2/Conv", "/model.22/cv2.2/cv2.2.2/Conv", "/model.22/cv3.2/cv3.2.2/Conv"], net_input_shapes={"images": [1, 3, input_size, input_size]})
runner.save_har(hailo_model_har_path)

# 校准数据集准备
images_list = [img_name for img_name in os.listdir(images_path) if os.path.splitext(img_name)[1] in [".jpg", ".png", "bmp"]][:1500]  # 获取图像名称列表
calib_dataset = np.zeros((len(images_list), input_size, input_size, 3))  # 初始化 numpy 数组
for idx, img_name in enumerate(sorted(images_list)):
    img = cv2.imread(os.path.join(images_path, img_name))
    resized = cv2.resize(img, (input_size, input_size))  # 调整原始图像的尺寸为模型输入的尺寸
    calib_dataset[idx,:,:,:]=np.array(resized)

# 量化模型
runner = ClientRunner(har=hailo_model_har_path)
alls_lines = [
    'model_optimization_flavor(optimization_level=1, compression_level=2)',
    'resources_param(max_control_utilization=0.6, max_compute_utilization=0.6, max_memory_utilization=0.6)',
    'performance_param(fps=5)'
]
runner.load_model_script('\n'.join(alls_lines))
runner.optimize(calib_dataset)
runner.save_har(hailo_quantized_har_path)

# 编译为 hef
runner = ClientRunner(har=hailo_quantized_har_path)
compiled_hef = runner.compile()
with open(hailo_model_hef_path, "wb") as f:
    f.write(compiled_hef)

参考

  1. Supported operators - Hailo Community:
    https://community.hailo.ai/t/supported-operators/5046/2
  2. hailo_model_zoo - GitHub:
    https://github.com/hailo-ai/hailo_model_zoo/tree/master/hailo_model_zoo/cfg/networks
  3. Dataflow Compiler v3.29.0:
    https://hailo.ai/developer-zone/documentation/dataflow-compiler-v3-29-0

闲聊

考虑了很久,打算写一篇保姆级部署从0-1构建企业级cicd流水线,把工作上面所用到的技术点分享给大家。从最k8s,harbor,jenkins,gitlab,docker的详细部署到集成。前后端流水线的构建,发布等...如果以下内容有不足的地方,请指出,我会第一时间更正。谢谢大家。

先上一下手绘导图,大致的流程图如下:
大致的部署流程是这样的:开发人员把做好的asp.net core项目代码通过git推送到gitlab,然后Jenkins通过 gitlab webhook (前提是配置好),自动从拉取gitlab上面拉取代码下来,然后进行build,编译、生成镜像、然后把镜像推送到Harbor仓库;然后在部署的时候通过k8s拉取Harbor上面的代码进行创建容器和服务,最终发布完成,然后可以用外网访问。

当然啦,上面只是粗略的,请看下图才更加形象。

一、前言

K8s 集群部署有多种方式,kubeadm 是 K8s 官方提供的集群部署工具,这种方法最为常用,简单快速,适合初学者。本文就使用 kubeadm 搭建集群演示。

二、主机准备

本次我们搭建一套 3 个节点的 K8s 集群,操作系统使用Ubuntu 22.04.4 LTS,配置2核4G,ip规划如下

主机名 ip地址 主机配置
master231 10.0.0.231 2核,4GiB,系统盘 20GiB
worker232 10.0.0.232 2核,4GiB,系统盘 20GiB
worker233 10.0.0.233 2核,4GiB,系统盘 20GiB

三、系统配置

关闭swap分区
swapoff -a && sysctl -w vm.swappiness=0  # 临时关闭
sed -ri '/^[^#]*swap/s@^@#@' /etc/fstab  # 基于配置文件关闭
确保各个节点MAC地址或product_uuid唯一
ifconfig  ens33  | grep ether | awk '{print $2}'
cat /sys/class/dmi/id/product_uuid 

温馨提示:
   一般来讲,硬件设备会拥有唯一的地址,但是有些虚拟机的地址可能会重复。 
   Kubernetes使用这些值来唯一确定集群中的节点。 如果这些值在每个节点上不唯一,可能会导致安装失败。
检查网络节点是否互通
简而言之,就是检查你的k8s集群各节点是否互通,可以使用ping命令来测试。
ping www.baidu.com -c 10
ping master231 -c 10
允许iptable检查桥接流量
cat <<EOF | tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

sysctl --system
修改cgroup的管理进程
所有节点修改cgroup的管理进程为systemd==乌班图默认不用修改
[root@master231 ~]# docker info  | grep "Cgroup Driver:"
 Cgroup Driver: systemd
[root@master231 ~]# 
	
[root@worker232 ~]# docker info  | grep "Cgroup Driver:"
 Cgroup Driver: systemd
[root@worker232 ~]# 

[root@worker233 ~]# docker info  | grep "Cgroup Driver:"
 Cgroup Driver: systemd
[root@worker233 ~]# 
温馨提示:
	如果不修改cgroup的管理驱动为systemd,则默认值为cgroupfs,在初始化master节点时会失败

四、安装k8s管理工具

kubeadm:用来初始化K8S集群的工具。
kubelet:在集群中的每个节点上用来启动Pod和容器等。
kubectl:用来与K8S集群通信的命令行工具。

所有节点操作
1.K8S所有节点配置软件源
apt-get update && apt-get install -y apt-transport-https
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - 
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF

2.获取最新软件包信息
apt-get update

3.查看一下当前环境支持的k8s版本
[root@master231 ~]# apt-cache madison kubeadm
   kubeadm |  1.28.2-00 | https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial/main amd64 Packages
   kubeadm |  1.28.1-00 | https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial/main amd64 Packages
   kubeadm |  1.28.0-00 | https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial/main amd64 Packages
   
4.安装 kubelet kubeadm kubectl
apt-get -y install kubelet=1.23.17-00 kubeadm=1.23.17-00 kubectl=1.23.17-00


5.所有节点都要检查各组件版本 
kubeadm version
kubectl version
kubelet --version
安装docker
1.编写docker安装脚本
[root@master231 docker]# cat install-docker.sh 
#!/bin/bash
# auther: cherry

# 加载操作系统的变量,主要是ID变量。
. /etc/os-release

# DOCKER_VERSION=26.1.1
DOCKER_VERSION=20.10.24
# DOCKER_COMPOSE_VERSION=2.27.0
DOCKER_COMPOSE_VERSION=2.23.0
FILENAME=docker-${DOCKER_VERSION}.tgz
DOCKER_COMPOSE_FILE=docker-compose-v${DOCKER_COMPOSE_VERSION}
URL=https://download.docker.com/linux/static/stable/x86_64
DOCKER_COMPOSE_URL=https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64
DOWNLOAD=./download
BASE_DIR=/softwares
OS_VERSION=$ID




# 判断是否下载了docker-compose
function prepare(){
   # 判断是否下载docker-compose文件
   if [ ! -f ${DOWNLOAD}/${DOCKER_COMPOSE_FILE} ]; then
      wget -T 3  -t 2 ${DOCKER_COMPOSE_URL} -O ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}
   fi
   
   if [ $? != 0 ];then
     rm -f ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}
     echo "不好意思,由于网络波动原因,无法下载${DOCKER_COMPOSE_URL}软件包,程序已退出!请稍后再试......"
     exit 100
   fi

   # 给脚本添加执行权限
   chmod +x ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}
}


# 定义安装函数
function InstallDocker(){

	if [ $OS_VERSION == "centos" ];then
	  [ -f /usr/bin/wget ] || yum -y install wget
          rpm -qa |grep bash-completion || yum -y install bash-completion
	fi

	if [ $OS_VERSION == "ubuntu" ];then
	  [ -f /usr/bin/wget ] || apt -y install wget
	fi

    # 判断文件是否存在,若不存在则下载软件包
    if [ ! -f ${DOWNLOAD}/${FILENAME} ]; then
       wget ${URL}/${FILENAME} -O ${DOWNLOAD}/${FILENAME}
    fi
    
    # 判断安装路径是否存在
    if [ ! -d ${BASE_DIR} ]; then
      install -d ${BASE_DIR}
    fi
    
    # 解压软件包到安装目录
    tar xf ${DOWNLOAD}/${FILENAME} -C ${BASE_DIR}
 
    # 安装docker-compose
    prepare
    cp $DOWNLOAD/${DOCKER_COMPOSE_FILE} ${BASE_DIR}/docker/docker-compose
   
    # 创建软连接
    ln -svf ${BASE_DIR}/docker/* /usr/bin/
    
    # 自动补全功能
    cp $DOWNLOAD/docker /usr/share/bash-completion/completions/docker
    source /usr/share/bash-completion/completions/docker
    
    # 配置镜像加速
    install -d /etc/docker
    cp $DOWNLOAD/daemon.json /etc/docker/daemon.json
    
    # 开机自启动脚本
    cp download/docker.service /usr/lib/systemd/system/docker.service
    systemctl daemon-reload
    systemctl enable --now docker
    docker version
    docker-compose version
    tput setaf 3
    echo "安装成功,欢迎使用cherry二进制docker安装脚本,欢迎下次使用!"
    tput setaf 2
}


# 卸载docker
function UninstallDocker(){
  # 停止docker服务
  systemctl disable --now docker

  # 卸载启动脚本
  rm -f /usr/lib/systemd/system/docker.service

  # 清空程序目录
  rm -rf ${BASE_DIR}/docker

  # 清空数据目录
  rm -rf /var/lib/{docker,containerd} 

  # 清除符号链接
  rm -f /usr/bin/{containerd,containerd-shim,containerd-shim-runc-v2,ctr,docker,dockerd,docker-init,docker-proxy,runc}

  # 使得终端变粉色
  tput setaf 5
  echo "卸载成功,欢迎再次使用cherry二进制docker安装脚本哟~"
  tput setaf 7
}


# 程序的入口函数
function main(){
   # 判断传递的参数
   case $1 in
     install|i)
      InstallDocker
      ;;
      remove|r)
      UninstallDocker
      ;;
     *)
       echo "Invalid parameter, Usage: $0 install|remove"
       ;;
   esac
}

# 向入口函数传参
main $1 

[root@master231 docker]# ll
total 16
drwxr-xr-x 3 root root 4096 Dec 10 05:49 ./
drwx------ 6 root root 4096 Dec 10 05:49 ../
drwxr-xr-x 2 root root 4096 May  9  2024 download/
-rwxr-xr-x 1 root root 3497 Dec 10 05:49 install-docker.sh*

2.安装docker
[root@master231 docker]# ./install-docker.sh install
'/usr/bin/containerd' -> '/softwares/docker/containerd'
'/usr/bin/containerd-shim' -> '/softwares/docker/containerd-shim'
'/usr/bin/containerd-shim-runc-v2' -> '/softwares/docker/containerd-shim-runc-v2'
'/usr/bin/ctr' -> '/softwares/docker/ctr'
'/usr/bin/docker' -> '/softwares/docker/docker'
'/usr/bin/docker-compose' -> '/softwares/docker/docker-compose'
'/usr/bin/dockerd' -> '/softwares/docker/dockerd'
'/usr/bin/docker-init' -> '/softwares/docker/docker-init'
'/usr/bin/docker-proxy' -> '/softwares/docker/docker-proxy'
'/usr/bin/runc' -> '/softwares/docker/runc'
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /lib/systemd/system/docker.service.
Client:
 Version:           20.10.24
 API version:       1.41
 Go version:        go1.19.7
 Git commit:        297e128
 Built:             Tue Apr  4 18:17:06 2023
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.24
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.19.7
  Git commit:       5d6db84
  Built:            Tue Apr  4 18:23:02 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.6.20
  GitCommit:        2806fc1057397dbaeefbea0e4e17bddfbd388f38
 runc:
  Version:          1.1.5
  GitCommit:        v1.1.5-0-gf19387a6
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
Docker Compose version v2.23.0
安装成功,欢迎使用cherry二进制docker安装脚本,欢迎下次使用!
初始化master组件
1.导入镜像
[root@master231 ~]# docker load -i master-1.23.17.tar.gz

2.使用kubeadm初始化master节点
[root@master231 ~]# kubeadm init --kubernetes-version=v1.23.17 --image-repository registry.aliyuncs.com/google_containers  --pod-network-cidr=10.100.0.0/16 --service-cidr=10.200.0.0/16  --service-dns-domain=cherry.com
#这个要记住,添加节点需要用
kubeadm join 10.0.0.231:6443 --token lzphw7.kc4iu4k0mswnpy7h \
	--discovery-token-ca-cert-hash sha256:298393d4dc931d6d13ec2ec1aedd4295bcd143a84e78dfc5a82ec7e53210d511
--------------------------------------------------

3.拷贝授权文件,用于管理K8S集群
[root@master231 ~]# mkdir -p $HOME/.kube
[root@master231 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master231 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

4.查看集群节点
[root@master231 ~]# kubectl get cs
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME                 STATUS    MESSAGE                         ERROR
scheduler            Healthy   ok                              
controller-manager   Healthy   ok                              
etcd-0               Healthy   {"health":"true","reason":""} 
[root@master231 ~]# 
[root@master231 ~]# 
[root@master231 ~]# kubectl get nodes
NAME        STATUS     ROLES                  AGE    VERSION
master231   NotReady   control-plane,master   117s   v1.23.1
部署worler组件,添加节点
1.导入镜像
[root@worker232 ~]# docker load  -i slave-1.23.17.tar.gz
[root@worker233 ~]# docker load  -i slave-1.23.17.tar.gz

2.在worker节点输入刚刚的token
[root@worker232 ~]# kubeadm join 10.0.0.231:6443 --token lzphw7.kc4iu4k0mswnpy7h \
	--discovery-token-ca-cert-hash sha256:298393d4dc931d6d13ec2ec1aedd4295bcd143a84e78dfc5a82ec7e53210d511
	
[root@worker232 ~]# kubeadm join 10.0.0.231:6443 --token lzphw7.kc4iu4k0mswnpy7h \
	--discovery-token-ca-cert-hash sha256:298393d4dc931d6d13ec2ec1aedd4295bcd143a84e78dfc5a82ec7e53210d511
	
3.master节点检查集群的worker节点列表
[root@master231 ~]# kubectl get nodes
NAME        STATUS     ROLES                  AGE     VERSION
master231   NotReady   control-plane,master   13m     v1.23.17
worker232   NotReady   <none>                 3m19s   v1.23.17
worker233   NotReady   <none>                 2m3s    v1.23.17
部署CNI插件,打通网络
1.导入镜像
[root@master231 ~]# docker load  -i flannel-cni-plugin-v1.5.1.tar.gz
[root@master232 ~]# docker load  -i flannel-cni-plugin-v1.5.1.tar.gz
[root@master233 ~]# docker load  -i flannel-cni-plugin-v1.5.1.tar.gz

[root@worker231 ~]# docker load -i flannel.tar.gz
[root@worker232 ~]# docker load -i flannel.tar.gz
[root@worker233 ~]# docker load -i flannel.tar.gz


2.下载Flannel组件
[root@master231 ~]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

3.安装Flannel组件
[root@master231 ~]# kubectl apply -f kube-flannel.yml 

4.查看版本,版本要一致不然会启动失败
[root@master231 ~]# grep image kube-flannel.yml

5.检查falnnel各组件是否安装成功
[root@master231 ~]# kubectl get pods -o wide -n kube-flannel 
NAME                    READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
kube-flannel-ds-ckkbk   1/1     Running   0          35s   10.0.0.233   worker233   <none>           <none>
kube-flannel-ds-kst7g   1/1     Running   0          35s   10.0.0.232   worker232   <none>           <none>
kube-flannel-ds-ljktm   1/1     Running   0          35s   10.0.0.231   master231   <none>           <none>

6.测试各节点组件
[root@master231 ~]# kubectl get nodes
NAME        STATUS   ROLES                  AGE   VERSION
master231   Ready    control-plane,master   37m   v1.23.17
worker232   Ready    <none>                 27m   v1.23.17
worker233   Ready    <none>                 26m   v1.23.17

五、安装kubectl工具自动补全功能

  1.临时补全生效
  apt -y install bash-completion
  source /usr/share/bash-completion/bash_completion
  source <(kubectl completion bash)
  
  2.永久补全生效需要写入环境变量
  [root@master231 ~]# vim .bashrc 
  ...
  source <(kubectl completion bash)
  

六、修改时区

1.修改时区
[root@master231 ~]# ln -svf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 

2.验证
[root@master231 ~]# date -R

七、k8s基础巡检

1.检查K8S集群的worker节点列表
[root@master231 ~]# kubectl get nodes 

2.检查master组件
[root@master231 ~]# kubectl get cs

3.检查flannel网卡是否正常
[root@master231 ~]# kubectl get pods -o wide -n kube-flannel

4.检查各节点网卡
ifconfig 

- 如果有节点没有cni0网卡,建议大家手动创建相应的网桥设备,但是注意网段要一致:
	1.假设 master231的flannel.1是10.100.0.0网段。
ip link add cni0 type bridge
ip link set dev cni0 up
ip addr add 10.100.0.1/24 dev cni0

	2.假设 worker232的flannel.1是10.100.1.0网段。
ip link add cni0 type bridge
ip link set dev cni0 up
ip addr add 10.100.1.1/24 dev cni0

	3.假设 worker233的flannel.1是10.100.2.0网段。
ip link add cni0 type bridge
ip link set dev cni0 up
ip addr add 10.100.2.1/24 dev cni0

副本集是什么?
我们在前文中讲过什么是pod,简单来说pod就是k8s直接操作的基本单位。
不了解的同学可以参考前文:

k8s 实战 1 ---- 初识 (https://www.cnblogs.com/jilodream/p/18245222)
k8s 实战 2 ---- pod 基础 (https://www.cnblogs.com/jilodream/p/18284282)
k8s 实战 3 ---- 标签(https://www.cnblogs.com/jilodream/p/18293278 )

如果只是有pod,基本是可以满足我们普通开发学习的,但是这和我们直接使用docker容器区别就不是很大了,要知道k8s要做的事情就是提供一整套的管理部署系统,如果仅仅就是操作几个pod,就有点大材小用了。
因此我们必须要学习一些更高阶的资源,才能利用到k8s提供的各种能力。比如副本集 ReplicaSet,部署Deployment等。
今天我们来学习和使用下副本集 ReplicaSet
replica 美[ˈreplɪkə] 复制品;仿制品;

ReplcaSet字面意思就是复制品的集合,我们一般译作副本集。
通常来说,为了高可用,我们会对每个微服务创建多个实例,
为了管理方便,每个实例通常对应一个容器,(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )每个容器又常常对应一个pod,多个实例实际上对应的就是多个pod。在此基础上,k8s定义了一种更高级的资源称之为ReplicaSet,为的就是管理这个pod集合(副本集合)。
比如我们要创建3个kuard服务的副本集,我们应该如何通过副本集创建呢?
和直接定义pod一样,我们可以直接定义一个yaml模板,通过模板来声明我们要定义的副本集的各项特性
如下:

1 apiVersion: apps/v12 kind: ReplicaSet3 metadata:4 name: kuardrs5 spec:6 replicas: 27 selector:8 matchLabels:9 appRs: kuardrs10 template:11 metadata:12 labels:13 appRs: kuardrs14 version: "2"15 spec:16 containers:17 - ports:18 - containerPort: 8019 name: kuardpod120           image: "docker.io/library/kuard-amd64:blue"

这就是一个标准的ReplicaSet 模板。

和pod模板一样,我们依次来看每个键的含义:
首先是
apiVersion
表示当前选定的版本
kind
表示类型,我们的类型是ReplicaSet,也就是副本集
metadata
表示元数据,在这里我们定义新建的副本集名称叫:kuardrs
spec
表示特性,在这里我们定义副本集的一些内部特性信息:
spec.replicas
表示副本集中副本的个数
spec.selector
表示副本集匹配的标签,也称之为标签选择器。这个需要详细说说。在前一节中,我们已经知道了什么是标签,它可以理解为定义到资源上边的一系列kv值,我们可以用标签进行匹配和筛选资源。
而我们创建的副本集和pod,或者说未来的Deployment和副本集之间,都是松耦合的,并不是直接依赖的关系。换句话说创建好副本集之后,你可以随便增删pod,副本集本身是不会受影响的。但是副本集会通过标签,感知到当前所管理的pod是否符合自己的要求,
当它发现多出来了匹配的pod,就会尝试删除pod。当它发现匹配到的pod,少于自己要求的pod时,就会尝试新增pod。因此直接说副本集是pod的一个集合其实并不贴切,更准确的说法应该是,副本集是用来定义和规范pod集合的一个更高维度的资源。
而我们平常发现某一个pod运行的有一些问题时,又不想直接删除pod,又不想生产环境继续持有一个有问题的pod,我们就可以直接修改pod的标签,将pod从副本集管理范围中,剥离出来。然后再围绕有问题的pod进一步研究。
而这些pod对于副本集来说,都是无状态的,替换谁都可以,只要符合副本集的标签匹配要求就可以。就像下边这个样子:

在本示例中,selector所匹配的资源就是符合标签:
appRs: kuardrs
的pod。注意这里用matchLabels进行了匹配。
spec.template
是pod的模板定义,其中我们分别有pod元数据以及pod特性等节点。
我们在pod的metadata节点中定义了pod要持有的两个标签(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ ):
appRs: kuardrs
version: "2"

在pod的spec特性节点中定义了pod中容器的镜像、名称、端口等。
template中pod的定义和前文中声明pod基本类似,有兴趣的同学一定要看下前文,这里就不过多的赘述了。
总而言之定义好的pod,包含容器、标签等信息,而我们定好的副本集又指定了要筛选的标签的信息,两者就通过标签很松散的连接到了一起。

定义好了副本集的yaml文件之后,我们来看看副本集的常规操作
创建副本集

kubectl apply -f xxx.yaml              xxx.yaml文件是副本集的声明文件
执行效果如下:

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl apply -f kuard-rs.yaml 
replicaset.apps/kuardrs created

查看副本集

kubectl get replicaset ,注意replicaset也可以是replicasets/rs (以下不再重复),
执行效果如下,这里查到的副本集就是刚刚创建的副本集:

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get replicaset
NAME DESIRED CURRENT READY AGE
kuardrs 2 2 2 57s

查看副本集详情

k describe replicasets  xxx    xxx 是副本集名称
执行效果如下:

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl describe replicaset kuardrs
Name: kuardrs
Namespace: default
Selector: appRs=kuardrs
Labels:
<none>Annotations:<none>Replicas: 2 current / 2 desired
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: appRs=kuardrs
version=2
Containers:
kuardpod1:
Image: docker.io/library/kuard-amd64:blue
Port: 80/TCP
Host Port: 0/TCP
Environment:
<none>Mounts:<none>Volumes:<none>Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 3m30s replicaset-controller Created pod: kuardrs-q8ghw
Normal SuccessfulCreate 3m30s replicaset-controller Created pod: kuardrs-wd29m

我们可以从详情中看到副本集定义的各种属性和模板。以及关联出的pod的一些相关信息。

有时我们还需要根据
pod反向的找到它
属于哪个副本集

,此时可以通过pod 中的yaml 文件查找到对应创建的副本集

在键ownerReferences下边可以看到,如下:

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get pod
NAME READY STATUS RESTARTS AGE
kuardrs-q8ghw 1/1 Running 0 4m23s
kuardrs-wd29m 1/1 Running 0 4m23s
[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get pod kuardrs-q8ghw -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
cni.projectcalico.org/containerID: 619b5de35bedd683352ce58bab8e6efd03e79fa4495a8ae89bae85f93ac7ed3f
cni.projectcalico.org/podIP: 192.168.240.165/32
cni.projectcalico.org/podIPs: 192.168.240.165/32
creationTimestamp: "2024-12-10T07:13:15Z"
generateName: kuardrs-
labels:
appRs: kuardrs
version: "2"
name: kuardrs-q8ghw
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: kuardrs
uid: 8a91c42a-864a-4172-8653-373210a59eaa
resourceVersion: "28299737"
uid: 587be9bd-2d0a-402b-a3a4-72edbc3170db
spec:
containers:
- image: docker.io/library/kuard-amd64:blue
imagePullPolicy: IfNotPresent
....省略....

如果我们
想调整副本的数量或者其他属性

我们可以通过
(1)k edit rs xxx  ,   xxx是副本集的名称
(2)k apply -f xxx.yaml ,xxx.yaml文件是副本集的声明文件
(3)k scale rs xxx --replicas=新副本数量  ,xxx是副本集名称

我们直接展示最后一种(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )执行效果:

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get pod 
NAME READY STATUS RESTARTS AGE
kuardrs-q8ghw 1/1 Running 0 9m19s
kuardrs-wd29m 1/1 Running 0 9m19s
[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl scale rs kuardrs --replicas=3
replicaset.apps/kuardrs scaled
[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get pod
NAME READY STATUS RESTARTS AGE
kuardrs-lhgtc 1/1 Running 0 8s
kuardrs-q8ghw 1/1 Running 0 10m
kuardrs-wd29m 1/1 Running 0 10m

删除副本集

k delete rs xxx  ,xxx是副本集的名称

[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get rs
NAME DESIRED CURRENT READY AGE
kuardrs 3 3 3 29m
[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl delete rs kuardrs
replicaset.apps "kuardrs" deleted
[root@iZ2ze3bpa0o5cw6gp42ry2Z learnRs]# kubectl get rs
No resources found in default namespace.