云原生存储高可用容器编排【免费下载链接】longhornCloud-Native distributed storage built on and for Kubernetes项目地址https://gitcode.com/gh_mirrors/lo/longhorn点击查看免费下载导读本文围绕 Longhorn 加密卷的一个经典容量缺口问题展开启用加密后用户申请10 GiB的卷实际在 Pod 内看到的块设备却是10 GiB - 16 MiB这 16 MiB 被 LUKS2 加密头部metadata header占用。本文以增强提案 enhancements/20260413-luks2-header-space-preservation-for-encrypted-volumes.md 为核心骨架从 LUKS2 头部结构讲起深入分析 Longhorn 如何在后端层透明追加Requested Size 16 MiB的物理容量并完整覆盖重建Rebuild、备份恢复Backup/Restore、扩容Expansion、引擎在线/离线升级、虚拟机在线迁移Live Migration等全部受影响操作的实现与验证方案。读完本文你将掌握16 MiB 开销的精确构成、Longhorn 后端尺寸计算的源码级实现思路、新旧引擎镜像并存时的行为差异以及一套可直接照做的加密卷容量验证测试清单。问题背景加密卷为何凭空少 16 MiB当用户在 Kubernetes 中创建一个指定大小例如10 GiB且启用了加密的 Longhorn 卷时Longhorn 需要在该卷的前部预留一部分空间存放 LUKS2 加密头部LUKS2 metadata header。在增强方案落地之前卷创建后lsblk输出与 Pod 内文件系统可用大小均表现为10 GiB - 16 MiB用户感知到的容量与申请容量存在固定偏差容易触发部分工作负载或数据库应用对块设备尺寸的严格校验strict size validation checks而部署失败。从用户视角看这 16 MiB 属于凭空消失的容量。本增强方案关联 Issuelonghorn/longhorn#9205的目标正是把这一开销透明化让用户在加密卷上获得恰好等于申请值的可用容量而 16 MiB 的额外空间由 Longhorn 在后端层自动追加并全程管理。LUKS2 头部结构16 MiB 是怎么算出来的LUKS2 头部并非单一结构而是由三部分组成二进制/元数据binary/metadata、Keyslot 区keyslot area与对齐填充alignment padding。--------------------------------------------------------------- |Primary |First | Secondary| Second | KeySlot| Alignment| Encrypted| |binary |metadata| binary | metadata| area | padding | data | |header | | header | | | | | --------------------------------------------------------------- |---------------------- LUKS2 Header ----------------------其总量满足如下关系Header Size (Primary Metadata) (Secondary Metadata) (Keyslot Area) (Alignment Padding)三部分的默认规模如下Keyslot AreaKeyslot 区默认每个 keyslot 使用 4000 条 stripe每条 stripe 占用 64 字节用于存放 master key 数据因此单个 keyslot 的空间恰好为 8,000 KiB约 7.8 MiB。Binary JSON Metadata二进制与 JSON 元数据二进制头部固定占用 4 KiB标准使用场景下默认元数据分配为 12 KiB两者合计含 Primary/Secondary 双份布局共 32 KiB。Alignment Padding对齐填充用于把加密数据的起始位置对齐到块block边界。块按块加密一个块通常为 512 字节该填充保证 LUKS 与加密扇区正确协作。cryptsetup会将剩余约 8 MiB 作为空白填充以确保文件系统与 SSD 的硬件块对齐。三者相加即得到 16 MiB 的默认头部规模。值得注意的是cryptsetup2.1.0 是默认 LUKS2 头部大小变为 16 MiB的确切最低版本——这是本方案中16 MiB这一数值的版本学依据参见 Cryptsetup 2.1.0 Release Notes。影响头部大小的cryptsetup luksFormat参数cryptsetup luksFormat执行时以下参数决定最终头部大小--type指定格式类型。Longhorn 默认使用luks2若改用luks类型头部将缩减为 2 MiB。--luks2-metadata-size与--luks2-keyslots-size显式指定为 JSON 文本与二进制 keyslot 数据预留的空间覆盖默认值。Longhorn 执行格式化命令时使用默认值。--offset显式强制 payload 偏移到指定大小。该参数可用于确保头部大小保持固定。补充说明Longhorn 的加密卷运行依赖宿主机具备dm_crypt内核模块并安装cryptsetup详见加密方案文档 enhancements/20221024-pv-encryption.md。这也解释了为何扩容与升级流程中需要先检查宿主机cryptsetup版本。目标与非目标Goals目标将 16 MiB LUKS2 头部开销从用户视角中抽象掉让计算得到的后端尺寸Requested Size 16 MiB在所有操作中正确传递。Non-goals非目标在 Longhorn 升级时为所有既有加密卷提供无需额外操作的自动透明迁移提供可配置的 LUKS2 头部/开销大小。设计核心后端尺寸的统一计算方案的核心思路非常简洁启动引擎engine与副本replica进程时使用Engine.Spec.VolumeSize/Replica.Spec.VolumeSize的值并在Volume.Spec.Encrypted为true时额外追加 16 MiB。统一计算入口getBackendSize为把后端尺寸计算集中化提案在go-common-libs/types/crypto.go中引入统一辅助函数并定义常量LUKS2HeaderSize 16 * 1024 * 102416 MiBconst ( CryptoKeyProvider CRYPTO_KEY_PROVIDER CryptoKeyValue CRYPTO_KEY_VALUE ... LUKS2HeaderSize 16 * 1024 * 1024 // 16 MiB ) func getBackendSize(requestedVolumeSize int64, encrypted bool, cliAPIVersion int, migrating bool) int64 { // The default LUKS2 header size is 16 MiB, so it must be added to the replica size when the volume is encrypted. Otherwise, the device // presented to the user will be 16 MiB smaller than the requested size. if encrypted (cliAPIVersion 12 || migrating) { return requestedVolumeSize LUKS2HeaderSize } return requestedVolumeSize }该函数有两个关键闸门cliAPIVersion 12用 CLI API 版本区分新建的加密卷与旧引擎镜像下的存量加密卷——只有新引擎CLI API v12 及以上才追加 16 MiBmigrating在线迁移路径上的兜底开关保证迁移场景也按新尺寸计算。API 版本升级longhorn-engine/pkg/meta/version.go为引入这一新的 API 层级CLIAPIVersion需要递增 1const ( // CLIAPIVersion used to communicate with user e.g. longhorn-manager CLIAPIVersion 12 CLIAPIMinVersion 8 ... )CLIAPIMinVersion保持为 8意味着新引擎仍可与旧版 longhorn-manager 通信但只有双方都达到 v12 语义时才会启用 16 MiB 追加逻辑。实例进程创建engineapi/instance_manager.go引擎与副本实例创建请求结构体分别新增Encrypted字段并在发起 gRPCInstanceCreate时按数据引擎类型计算后端尺寸type EngineInstanceCreateRequest struct { Engine *longhorn.Engine Encrypted bool ... } type ReplicaInstanceCreateRequest struct { Replica *longhorn.Replica Encrypted bool ... } // EngineInstanceCreate creates a new engine instance func (c *InstanceManagerClient) EngineInstanceCreate(req *EngineInstanceCreateRequest) (*longhorn.InstanceProcess, error) { ... requestBackendSize : uint64(req.Engine.Spec.VolumeSize) case longhorn.DataEngineTypeV1: binary, args, err getBinaryAndArgsForEngineProcessCreation(req.Replica, ..., req.Encrypted) case longhorn.DataEngineTypeV2: requestBackendSize lhcrypto.getBackendSize(requestBackendSize, req.Encrypted, cliAPIVersion, false) ... instance, err : c.instanceServiceGrpcClient.InstanceCreate(imclient.InstanceCreateRequest{ ... Size: requestBackendSize, Binary: binary, }) ... } // ReplicaInstanceCreate creates a new replica instance func (c *InstanceManagerClient) ReplicaInstanceCreate(req *ReplicaInstanceCreateRequest) (*longhorn.InstanceProcess, error) { ... if types.IsDataEngineV1(req.Replica.Spec.DataEngine) { binary, args getBinaryAndArgsForReplicaProcessCreation(req.Replica, ..., req.Encrypted) } requestBackendSize : uint64(req.Replica.Spec.VolumeSize) if types.IsDataEngineV2(req.Replica.Spec.DataEngine) { requestBackendSize lhcrypto.getBackendSize(requestBackendSize, req.Encrypted, cliAPIVersion, false) ... } ... instance, err : c.instanceServiceGrpcClient.InstanceCreate(imclient.InstanceCreateRequest{ ... Size: requestBackendSize, Binary: binary, }) ... }从实现结构可以看出v1基于 longhorn-engine 进程与 v2基于 SPDK数据引擎的接入方式不同——v1 通过启动参数--encrypted传递加密标记v2 则在创建请求阶段直接把Size换成含 16 MiB 的后端尺寸。副本代理engineapi/proxy_replica.go副本添加ReplicaAdd、重建Rebuild、恢复Restore都经由引擎代理发起。代理必须把正确的卷尺寸传给实例才能让物理磁盘分配量正确func (p *Proxy) ReplicaAdd(e *longhorn.Engine, replicaName, replicaAddress string, restore, fastSync bool, ...) (err error) { cliAPIVersion, err : ec.ds.GetDataEngineImageCLIAPIVersion(e.Spec.Image, e.Spec.DataEngine) volumeSize : lhcrypto.getBackendSize(e.Spec.VolumeSize, e.Spec.VolumeEncrypted, cliAPIVersion, false) volumeCurrentSize : lhcrypto.getBackendSize(e.Status.CurrentSize, e.Spec.VolumeEncrypted, cliAPIVersion, false) return p.grpcClient.ReplicaAdd(string(e.Spec.DataEngine), e.Name, e.Spec.VolumeName, p.DirectToURL(e), replicaName, replicaAddress, restore, volumeSize, volumeCurrentSize, int(replicaFileSyncHTTPClientTimeout), fastSync, localSync, grpcTimeoutSeconds) }注意这里同时处理了Spec.VolumeSize目标尺寸与Status.CurrentSize当前实际尺寸两个维度确保重建过程中目标副本从一开始就按VolumeSize 16 MiB预分配。引擎侧加密标记longhorn-engine/app/cmd/replica.go引擎侧新增一个--encrypted布尔参数用于标记卷已加密并在副本启动命令中透传func ReplicaCmd() cli.Command { ... Flags: []cli.Flag{ ... cli.BoolFlag{ Name: encrypted, Hidden: false, Usage: Volume is encrypted, }, }, Action: func(c *cli.Context) { if err : startReplica(c); err ! nil { logrus.WithError(err).Fatalf(Error running start replica command) } }, ... }副本打开时的按需扩容longhorn-engine/pkg/replica/server.goOpen流程判断副本镜像文件是否需要在打开时扩容。这是旧卷在升级后获得正确后端尺寸的关键钩子func (s *Server) Open(isUpgrade bool, expectedBackendSize int64) error { ... state, info : s.Status() sectorSize : s.getSectorSize() logrus.Infof(Opening replica: dir %s, size %d, sector size %d, state: %v, upgrading: %v, s.dir, info.Size, sectorSize, state, isUpgrade) expandingEncryptedDevSize : int64(0) // 0 means it doesnt need to expand the size for the encrypted volume. if isExpandingEncryptedDevRequired(state, s.encrypted, info.Size, expectedBackendSize, isUpgrade) { expandingEncryptedDevSize expectedBackendSize } r, err : New(s.ctx, info.Size, sectorSize, s.dir, s.backing, s.revisionCounterDisabled, s.unmapMarkDiskChainRemoved, s.snapshotMaxCount, r.snapshotMaxSize, s.encrypted, expandingEncryptedDevSize) ... }在longhorn-engine/pkg/replica/replica.go的construct中当副本镜像已存在且expectedBackendSize 0时会将副本镜像文件扩展到期望的后端尺寸func construct(ctx context.Context, readonly bool, size, sectorSize int64, dir, head string, backingFile *backingfile.BackingFile, disableRevCounter, unmapMarkDiskChainRemoved bool, snapshotMaxCount int, snapshotMaxSize int64, encrypted bool, expectedBackendSize int64) (*Replica, error) { ... if exists { if err : r.openLiveChain(); err ! nil { return nil, err } if expectedBackendSize 0 { // expand the replica image file to expectedBackendSize size. } } ... }受影响操作的行为变化卷引擎升级Volume Upgrade离线升级Offline Upgrade沿用现有离线引擎升级流程。引擎升级完成后卷重新挂载attach时打开副本的过程会检查副本镜像文件大小是否需要扩展即触发上文Open中的按需扩容逻辑。在线升级Live Upgrade必须先扩展后端尺寸再进行在线升级。controller/engine_controller.go的syncEngine在检测到升级副本地址映射非空、且当前镜像与目标镜像不一致时若卷已加密会先调用工具函数判断宿主cryptsetup是否属于默认 16 MiB 头部的版本再通过expandEncryptedVolumeBeforeLiveUpgrade预先扩容func (ec *EngineController) syncEngine(key string) error { ... syncReplicaAddressMap : false if len(engine.Spec.UpgradedReplicaAddressMap) ! 0 engine.Status.CurrentImage ! engine.Spec.Image { if volume.Spec.Encrypted { is16MiBHeaderPkgVersion, err : util.IsCryptsetupVerWithFixed16MiBHeaderSize() ... if is16MiBHeaderPkgVersion { if isExpanding, err : ec.expandEncryptedVolumeBeforeLiveUpgrade(volume, engine); err ! nil || isExpanding { // Wait for the expected volume size to be updated before engine live upgrade for encrypted volume return err } } } if err : ec.Upgrade(engine, log); err ! nil { // Engine live upgrade failure shouldnt block the following engine state update. log.WithError(err).Error(Failed to run engine live upgrade) // Sync replica address map as usual when the upgrade fails. syncReplicaAddressMap true } } ... }副本重建Rebuilding重建期间引擎控制器指示新副本按正确尺寸供给空间数据同步重建目标块由于底层块设备文件容纳了VolumeSize 16 MiB快照树与原始同步raw sync可以安全地同时承载 LUKS 元数据与加密后的负载数据。新旧引擎镜像的差异对于仍使用旧引擎镜像的存量加密卷重建继续沿用Replica.Spec.VolumeSize不加 16 MiB行为与当前实现保持一致。备份与恢复Backup and Restore用户从备份恢复时应为恢复出的新卷正确设置Volume.Spec.Encrypted。卷控制器依据该字段判断备份是否源自加密卷并据此计算用户申请的Volume.Spec.Size以正确尺寸创建引擎与副本实例进程。因此加密卷备份 → 恢复为加密卷才能还原出完整容量反之恢复为未加密卷会因设备不匹配而无法正常挂载详见下文测试计划。扩容Expanding新引擎镜像下的加密卷扩容收到扩容请求更新Volume.Spec.Size扩容前先检查宿主机cryptsetup版本卷控制器将Engine.Spec.VolumeSize、Replica.Spec.VolumeSize更新为Volume.Spec.Size引擎与副本控制器计算新的后端尺寸NewBackendSize NewVolumeSize 16 MiB并以正确后端尺寸启动扩容过程。旧引擎镜像下的存量加密卷扩容收到扩容请求更新Volume.Spec.Size卷控制器更新Engine.Spec.VolumeSize、Replica.Spec.VolumeSize为Volume.Spec.Size引擎控制器直接用Engine.Spec.VolumeSize启动扩容过程不加 16 MiB。对应的代理层实现位于engineapi/proxy_volume.gofunc (p *Proxy) VolumeExpand(e *longhorn.Engine) (err error) { v, err : p.ds.GetVolumeRO(e.Spec.VolumeName) cliAPIVersion, err : p.ds.GetDataEngineImageCLIAPIVersion(e.Spec.Image, e.Spec.DataEngine) return p.grpcClient.VolumeExpand(string(e.Spec.DataEngine), e.Name, e.Spec.VolumeName, p.DirectToURL(e), lhcrypto.getBackendSize(e.Spec.VolumeSize, v.Spec.Encrypted, cliAPIVersion, false)) }虚拟机在线迁移Live Migration当用户尝试用旧引擎镜像对加密卷做在线迁移时收到在线迁移请求校验器validators检查引擎镜像是否为旧版本是旧版本直接返回错误提示必须先完成引擎升级再做在线迁移新版本正常开始在线迁移。校验逻辑分布在两处 webhookwebhook/resources/volume/validator.go的Update在原有的迁移过程中禁止修改MigrationNodeID校验之外追加对newVolume.Spec.MigrationNodeID已设置且引擎镜像CLIAPIVersion为 11即旧版的拒绝逻辑webhook/resources/volumeattachment/validator.go的verifyTicketCountForMigratableVolume当 migratable 卷出现第二个 CSI ticketnumCSITickets 2且卷状态为VolumeStateAttached时同样检查引擎镜像CLIAPIVersion是否为 11是则拒绝。func (v *volumeValidator) Update(request *admission.Request, oldObj runtime.Object, newObj runtime.Object) error { ... // prevent the changing v.Spec.MigrationNodeID to different node when the volume is doing live migration (when v.Status.CurrentMigrationNodeID ! ) if newVolume.Status.CurrentMigrationNodeID ! newVolume.Spec.MigrationNodeID ! oldVolume.Spec.MigrationNodeID newVolume.Spec.MigrationNodeID ! newVolume.Status.CurrentMigrationNodeID newVolume.Spec.MigrationNodeID ! { err : fmt.Errorf(cannot change v.Spec.MigrationNodeID to node %v when the volume is doing live migration to node %v , newVolume.Spec.MigrationNodeID, newVolume.Status.CurrentMigrationNodeID) return werror.NewInvalidError(err.Error(), ) } // Check if the newVolume.Spec.MigrationNodeID is set and // Check if the engine image CLIAPIVersion is 11 // If yes, reject the request. ... }func (v *volumeAttachmentValidator) verifyTicketCountForMigratableVolume(va *longhorn.VolumeAttachment, vol *longhorn.Volume) error { ... switch { ... case numCSITickets 2: if vol.Status.State ! longhorn.VolumeStateAttached { msg : fmt.Sprintf(cannot have second CSI ticket for migratable volume %v while it is in state %v, vol.Name, vol.Status.State) return werror.NewInvalidError(msg, spec.attachmentTickets) } // Check if the engine image CLIAPIVersion is 11 // If yes, reject the request. return nil ... } ... }升级计划Upgrade Plan存量加密卷在升级到新引擎镜像之前不做任何额外处理。新建卷将CLIAPIVersion递增 1引入新的 API 层级以区分存量加密卷与新建加密卷当以CLIAPIVersion 12创建/重建/扩容加密卷时为引擎与副本追加额外的 16 MiB 空间。存量加密卷升级过程CLIAPIVersion 12时沿用当前实现不追加 16 MiB直到完成引擎升级引擎升级期间先在宿主机检查cryptsetup版本触发一次隐式卷扩容implicit volume expansion使加密卷后端尺寸变为正确值扩容过程中的任何错误都应返回错误或通过事件记录且不影响其他卷操作升级完成后宿主机上的副本镜像文件被扩展为卷大小 16 MiB工作负载内的设备大小与申请值一致。下图展示了加密卷引擎升级过程的完整流程用户视角完全透明无需改配置从最终用户角度看该功能完全透明不需要任何配置变更用户创建标准 PVCStorageClass 指向加密 Longhorn StorageClass并请求1Gi存储例如spec.resources.requests.storage: 1GiLonghorn 自动创建Size: 10737418241 GiB的卷同时内部将后端存储分配调整为1 GiB 16 MiB工作负载挂载卷后运行lsblk或df -h报告的分区大小恰好为1G而非此前的1008M备份恢复、快照、卷扩容都隐式遵循这一计算。例如用户把卷扩容到2 GiBLonghorn UI 与 Kubernetes PVC 都显示2 GiB而后端加密副本自动扩展为2 GiB 16 MiB。存量旧卷默认保持原有尺寸行为只有按上文升级计划显式升级后才采用新尺寸计算。加密卷的标准创建配置结合仓库中的实际示例examples/crypto/storageclass-crypto-global.yaml、examples/crypto/secret-crypto-global.yaml一个采用全局密钥的加密 StorageClass 与 Secret 配置如下# StorageClass加密卷入口 kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: longhorn-crypto-global provisioner: driver.longhorn.io allowVolumeExpansion: true parameters: numberOfReplicas: 3 staleReplicaTimeout: 2880 # 48 hours in minutes fromBackup: encrypted: true # Do not set provisioner-secret-* parameters: the CSI provisioner cannot read Secrets. # Use node-side Secret references for encrypted volumes. # Ensure the referenced Secret exists before starting a workload. csi.storage.k8s.io/node-publish-secret-name: longhorn-crypto csi.storage.k8s.io/node-publish-secret-namespace: longhorn-system csi.storage.k8s.io/node-stage-secret-name: longhorn-crypto csi.storage.k8s.io/node-stage-secret-namespace: longhorn-system # These two are for online expansion of encrypted volumes. csi.storage.k8s.io/node-expand-secret-name: longhorn-crypto csi.storage.k8s.io/node-expand-secret-namespace: longhorn-system# Secret全局加密密钥存储于 longhorn-system 命名空间 --- apiVersion: v1 kind: Secret metadata: name: longhorn-crypto namespace: longhorn-system stringData: CRYPTO_KEY_VALUE: Simple passphrase CRYPTO_KEY_PROVIDER: secret # this is optional we currently only support direct keys via secrets创建该 StorageClass 与 Secret 后即可通过引用longhorn-crypto-global的 PVC 获得容量精确的加密卷。关于加密卷的完整背景dm_crypt内核模块依赖、密钥参数CRYPTO_KEY_CIPHER/CRYPTO_KEY_HASH/CRYPTO_KEY_SIZE、CSI 加解密流程等可继续阅读 enhancements/20221024-pv-encryption.md。测试计划如何验证 16 MiB 开销已透明化增强提案给出了六组可直接执行的验证场景覆盖新建、扩容、重建、备份恢复、引擎在线/离线升级与虚拟机迁移1. 新建卷New Volume Creation创建一个1 GiB加密卷挂载到 Pod 后在 Pod 内运行lsblk或fdisk -l预期结果块设备显示恰好为1G、1073741824 bytes而不是1008M、1056964608 bytes在卷所挂载的节点上验证后端副本文件大小为1 GiB 16 MiB。2. 卷扩容Volume Expansion新引擎镜像 新建加密卷创建新的1 GiB加密卷并扩容到2 GiB等待扩容完成预期结果Pod 内lsblk/fdisk -l显示2G工作节点上副本镜像文件为2 GiB 16 MiB。旧引擎镜像 存量加密卷将旧引擎下的1 GiB加密卷扩容到2 GiB等待扩容完成预期结果Pod 内显示1.9G工作节点上副本镜像文件为2 GiB不带 16 MiB。3. 副本重建Replica Rebuild新建加密卷删除已挂载1 GiB加密卷的一个副本等待 Longhorn 自动重建降级副本预期结果重建成功新副本文件大小与其他副本一致1 GiB 16 MiB且数据完整性例如存储文件的md5sum保持一致。旧引擎镜像存量卷删除已挂载1 GiB加密卷的一个副本等待自动重建预期结果重建成功新副本文件大小与其他副本一致1 GiB数据完整性md5sum保持一致。4. 备份恢复Backup Restore创建1 GiB加密卷写入 100 MiB 负载并计算校验和备份到远端备份服务器以Volume.Spec.Encrypted为 true 恢复到新卷并挂载到工作负载预期结果恢复卷在工作负载内呈现恰好1.0G负载校验和匹配再以Volume.Spec.Encrypted为 false 恢复同一备份预期结果该卷无法挂载到工作负载设备与加密元数据不匹配。5. 引擎在线升级Engine Live Upgrade用旧引擎镜像创建1 GiB加密卷并挂载到工作负载确认工作节点上副本镜像文件为1 GiB、工作负载内设备小于1 GiB写入 100 MiB 负载并计算校验和将卷引擎镜像在线升级到新版本预期结果副本镜像文件变为1 GiB 16 MiB工作负载内设备为1 GiB负载校验和匹配。6. 引擎离线升级Engine Offline Upgrade用旧引擎镜像创建1 GiB加密卷并挂载确认副本镜像文件为1 GiB、设备小于1 GiB写入 100 MiB 负载并计算校验和缩容工作负载、确保卷已分离升级卷引擎镜像到新版本后重新扩容工作负载预期结果副本镜像文件为1 GiB 16 MiB设备为1 GiB校验和匹配。7. 虚拟机在线迁移VM Live Migration安装带 Longhorn v1.11.x 与 KubeVirt 的 k3s 集群创建带1 GiBLonghorn 加密卷的 VM确认 VM 内卷呈现1.0G - 16 MiB在 VM 内写入 100 MiB 负载并计算校验和升级 Longhorn 至 v1.12在升级卷引擎之前发起在线迁移预期结果收到 Longhorn 校验器返回的错误升级卷引擎后再次发起在线迁移预期结果迁移完成迁移后卷在 VM 内呈现1.0G负载校验和匹配。小结本增强方案通过后端追加 16 MiB、用户侧完全透明的设计彻底解决了 Longhorn 加密卷容量少 16 MiB 的体验问题。其实现要点可概括为三句话统一计算函数getBackendSize以CLIAPIVersion 12与migrating为闸门决定是否追加开销引擎/副本实例创建、副本代理、扩容代理等所有尺寸入口统一改用该函数升级与迁移路径通过校验器、隐式扩容与副本打开时按需扩展完成存量卷的平滑过渡。对于使用加密卷且对块设备尺寸有严格校验的工作负载如部分数据库应用该方案上线后即可直接获得与 PVC 申请值完全一致的可用容量。延伸阅读本增强提案原文见 enhancements/20260413-luks2-header-space-preservation-for-encrypted-volumes.md加密卷功能的完整背景见 enhancements/20221024-pv-encryption.md可运行的 StorageClass/Secret 示例见 examples/crypto/storageclass-crypto-global.yaml 与 examples/crypto/secret-crypto-global.yaml。赞分享云原生存储高可用容器编排【免费下载链接】longhornCloud-Native distributed storage built on and for Kubernetes项目地址https://gitcode.com/gh_mirrors/lo/longhorn点击查看免费下载相关推荐Talos Linux 用户卷 UserVolumeConfig 配置完全指南directory/disk/partition 三种卷类型与 LUKS2 加密实战Talos Linux 用户卷 UserVolumeConfig 配置完全指南directory/disk/partition 三种卷类型与 LUKS2 加密云原生操作系统容器编排PyHessian核心原理深度解析从数学理论到代码实现PyHessian核心原理深度解析从数学理论到代码实现 PyHessian是一个基于PyTorch的神经网络二阶优化分析库它能够帮助开发者深入理解神经网络的如何在MonoGame中高效生成字体图集从基础到高级优化如何在MonoGame中高效生成字体图集从基础到高级优化 在跨平台游戏开发中字体渲染性能直接影响游戏体验。MonoGame作为强大的游戏开发框架提供了完整游戏开发图形学上一篇揭秘IndicatorFastScroll核心组件FastScrollerView与ThumbView工作原理解析下一篇HttpCanary Android网络抓包工具全面使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
M1芯片MacBook运行Keil C51:虚拟机与SDCC原生方案全解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 3:01:08
上海靠谱庭院设计服务商有哪些?实景样板基地供参考 上海青丽花园林设计中心(个人独资),简称青丽花园设计,是一家拥有500㎡实景庭院样板基地的一体化庭院服务商,专注私宅庭院全案设计与落地施工,其精准定位为以实景落地为基础,以透明化全流程服务为核心,为私宅… · 2026/9/28 3:01:02
建站老手揭秘:什么网站吸引流量速查手册 建站老手揭秘:什么网站吸引流量速查手册 改个需求建站公司拖一周,这种憋屈事儿你是不是也经历过?刚上线的官网,想让按钮换个颜色,客服说排期得下周,气得你想砸键盘。其实,很多老板觉得网站没流量是玄学,或者是钱没花够。别天真了,大部分烂站没流量,… · 2026/9/28 3:01:02
顺义本土正骨名医——苏荫来的故事 在顺义本地骨伤正骨领域,有这样一位深耕三十余年的实力派老医者,一身古法正骨手艺,一手传承绝活,专治各类筋骨伤痛,凭借精准的手法、扎实的疗效、踏实的行医作风,深得邻里百姓信赖与认可。他就是杏园金方首… · 2026/9/28 3:29:27
2026实测百度网盘直链助手脚本,速度超越PanDownload工具 随着我们手头的各种文档和视频资料越来越大,网盘在数据流转中扮演的角色也越来越重要。不管是工作交接还是备份生活点滴,它都帮了我们不少忙。
不过在日常使用中,偶尔遇到下载变慢也确实会让人感到有些苦恼。面对这种现象我们除了可以配合Pa… · 2026/9/28 3:29:08
剪映操作|输入文字后,能不能自动生成虚拟主播、配音和字幕 适用对象:AI视频生成任务的创作者。本文只处理“输入文字后,能不能自动生成虚拟主播、配音和字幕?”这一件事。先确定这一条要解决什么先给结论:处理“输入文字后,能不能自动生成虚拟主播、配音和字幕?”&a… · 2026/9/28 3:28:21
运算符 文件操作 6 运算符:算数运算: - * / % ////:整除%:求余比较运算:> < > < !赋值运算 : - *a21
b2
a,bb,a#只适合python
print(a)#2
print(b)#21逻辑运算:and or not当and,or… · 2026/9/28 3:27:47
字符集和编码 bytes 5 字符集和编码ascii——编排了128个文字字符,只需要7个0和1就可以表示了——1 byte8 bitANSI——每个字符 16 bit,2byteGBK编码Unicode:万国码utf-8:最短的字节长度8 英文:8bit,1 byte总结:as… · 2026/9/28 3:27:28
高效获取STM32开发参考方案:摆脱资料海洋,聚焦可落地项目 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/28 3:19:17
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
制作网页比较方便的软件怎么选?一文搞懂避坑指南 制作网页比较方便的软件怎么选?一文搞懂避坑指南 很多老板一上来就问:做个网站多少钱?但我反问他:你的域名买了吗?服务器租了吗?他一脸懵。这就是典型的“域名服务器搞不懂”。别急,今天咱们不聊虚的,直接 一文搞懂 那些让你头秃的技术名词。… · 2026/9/28 0:00:06
婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 婚恋网站实战案例:避开3个高价坑,省钱50%还能跑赢流量 找婚恋网站建站公司,最怕的就是被坑高价。很多同行跟我吐槽,报价单上写得模棱两可,功能栏里全是“高级定制”、“专属UI”,结果落地全是套壳。今天不聊虚的,直接甩几个我经手的 实战案例… · 2026/9/28 0:00:19
济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 济南做网站多少钱:3个案例拆解,防黑源码下载全攻略 上周济南一个做建材的老板找我,脸都绿了。他的官网首页弹出了赌博广告,后台被植入了挖矿脚本。他慌得问我:“网站被黑挂马不知道怎么办?能不能直接找之前的外包公司要源码下载,看看哪里被动了手脚?… · 2026/9/28 0:00:25