From 9d967e4b1361fe6bccde1dc8b346fcf13ee89081 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 02:49:23 +0000 Subject: [PATCH] deploy: 3206702bd8a2fee870572686045fa2b19d0b1111 --- 404.html | 8 ++++---- .../01-2023/2023-01-12.html | 8 ++++---- .../01-2023/2023-02-09.html | 8 ++++---- .../01-2023/2023-02-23.html | 8 ++++---- .../01-2023/2023-03-09.html | 8 ++++---- .../01-2023/2023-03-23.html | 8 ++++---- .../01-2023/2023-04-13.html | 8 ++++---- .../01-2023/2023-04-27.html | 8 ++++---- .../01-2023/2023-05-18.html | 8 ++++---- .../01-2023/2023-06-01.html | 8 ++++---- .../01-2023/2023-06-15.html | 8 ++++---- .../01-2023/2023-06-29.html | 8 ++++---- .../01-2023/2023-07-13.html | 8 ++++---- .../01-2023/2023-08-03.html | 8 ++++---- .../01-2023/2023-09-14.html | 8 ++++---- .../01-2023/2023-10-12.html | 8 ++++---- .../Double-Week-Meetings/01-2023/README.html | 8 ++++---- Community/community-guideline.html | 8 ++++---- CurveBS/architecture/architecture-intro.html | 8 ++++---- CurveBS/architecture/chunkserver-arch.html | 8 ++++---- CurveBS/architecture/client-arch.html | 8 ++++---- CurveBS/architecture/mds.html | 8 ++++---- CurveBS/architecture/nebd.html | 8 ++++---- CurveBS/architecture/snapshot.html | 8 ++++---- CurveBS/comparison/CurveBS-vs-Ceph-rbd.html | 8 ++++---- CurveBS/deploy/csi.html | 8 ++++---- CurveBS/deploy/dashboard.html | 8 ++++---- CurveBS/deploy/deploy-with-curveadm.html | 8 ++++---- CurveBS/deploy/deploy-with-k8s-operator.html | 8 ++++---- CurveBS/deploy/iscsi.html | 8 ++++---- CurveBS/deploy/offline-deploy.html | 8 ++++---- CurveBS/deploy/openstack.html | 8 ++++---- CurveBS/deploy/quickstart.html | 8 ++++---- CurveBS/deploy/rbd.html | 8 ++++---- CurveBS/deploy/rdma-spdk.html | 8 ++++---- .../localsnapshotclone_restful_api.html | 8 ++++---- .../interface/localsnapshotclone_tools_api.html | 8 ++++---- .../snapshotcloneserver_interface.html | 8 ++++---- CurveBS/maintenance/administrator-guide.html | 8 ++++---- CurveBS/maintenance/command-line-tools.html | 8 ++++---- CurveBS/performance/how-to-benchmark.html | 8 ++++---- "CurveBS/test/ci\344\273\213\347\273\215.html" | 8 ++++---- ...26\347\216\207\346\224\266\351\233\206.html" | 8 ++++---- CurveBS/test/env-setup.html | 8 ++++---- ...50\345\214\226\346\226\271\346\263\225.html" | 8 ++++---- ...13\350\257\225\345\267\245\345\205\267.html" | 8 ++++---- ...15\347\275\256\344\277\241\346\201\257.html" | 8 ++++---- ...25\345\205\203\346\265\213\350\257\225.html" | 8 ++++---- CurveBS/usecase/chuangyun.html | 8 ++++---- CurveBS/usecase/cloudnative-database.html | 8 ++++---- CurveBS/usecase/rdma-spdk.html | 8 ++++---- CurveBS/usecase/scenario.html | 8 ++++---- CurveBS/usecase/wanfang.html | 8 ++++---- CurveFS/architecture/architecture-intro.html | 8 ++++---- CurveFS/architecture/client-arch.html | 8 ++++---- CurveFS/architecture/metaserver-arch.html | 8 ++++---- CurveFS/comparison/CurveFS-vs-Other-FS.html | 8 ++++---- CurveFS/deploy/csi.html | 8 ++++---- CurveFS/deploy/deploy-with-curveadm.html | 8 ++++---- CurveFS/deploy/deploy-with-k8s-operator.html | 8 ++++---- CurveFS/deploy/distributed-cache.html | 8 ++++---- CurveFS/deploy/offline-deploy.html | 8 ++++---- CurveFS/deploy/quickstart.html | 8 ++++---- CurveFS/deploy/static-pv.html | 8 ++++---- CurveFS/maintenance/administrator-guide.html | 8 ++++---- CurveFS/maintenance/command-line-tools.html | 8 ++++---- CurveFS/maintenance/data-migration.html | 8 ++++---- CurveFS/performance/benchmark.html | 8 ++++---- CurveFS/performance/how-to-benchmark.html | 8 ++++---- CurveFS/performance/perf-tune.html | 8 ++++---- "CurveFS/test/ci\344\273\213\347\273\215.html" | 8 ++++---- CurveFS/test/env-setup.html | 8 ++++---- ...50\345\214\226\346\226\271\346\263\225.html" | 8 ++++---- ...13\350\257\225\345\267\245\345\205\267.html" | 8 ++++---- ...25\345\205\203\346\265\213\350\257\225.html" | 8 ++++---- CurveFS/usecase/ai-storage.html | 12 ++++++------ CurveFS/usecase/asr-storage.html | 12 ++++++------ CurveFS/usecase/elasticsearch-cold-data.html | 10 +++++----- CurveFS/usecase/hadoop-on-cloud.html | 10 +++++----- CurveFS/usecase/jiangsu-nongxin-es.html | 10 +++++----- CurveFS/usecase/s3-gateway.html | 10 +++++----- CurveFS/usecase/scenario.html | 10 +++++----- CurveFS/usecase/smb-support.html | 10 +++++----- Develop/build-and-test.html | 8 ++++---- Develop/code-walkthrough.html | 8 ++++---- Develop/how-to-contribute.html | 8 ++++---- Release/release-intro.html | 8 ++++---- Release/release-notes-v1.5.html | 8 ++++---- Release/release-notes-v2.6.html | 8 ++++---- Roadmap/roadmap-2023.html | 8 ++++---- ...rch-v2-04d6725115ee4a33aae09c8ab0af7201.webp | Bin 44598 -> 0 bytes ...e-arch-856d05060fcc8fadbeda09024859409d.webp | Bin 52002 -> 0 bytes ...a-arch-24bc47886dd26fa64f61d477c3e04051.webp | Bin 45078 -> 0 bytes ...e-arch-934896ba5bcfffe7fe92455d8aa251ff.webp | Bin 40980 -> 0 bytes ...warmup-557fa09de4aeb6d75d2d4fd859dec918.webp | Bin 59066 -> 0 bytes assets/js/561d73c1.53e3c897.js | 1 - assets/js/561d73c1.7c837df2.js | 1 + assets/js/935f2afb.07607faf.js | 1 - assets/js/935f2afb.c5a3d2c7.js | 1 + assets/js/afb57922.9930456c.js | 1 - assets/js/afb57922.fe368e64.js | 1 + assets/js/bda932b4.2a104469.js | 1 - assets/js/bda932b4.da7302dc.js | 1 + .../js/{main.79895394.js => main.607578f6.js} | 4 ++-- ...LICENSE.txt => main.607578f6.js.LICENSE.txt} | 0 ...ain.afb1ed5f.js => runtime~main.d917f5c7.js} | 2 +- category/2023.html | 8 ++++---- ...ve\345\235\227\345\255\230\345\202\250.html" | 8 ++++---- ...07\344\273\266\345\255\230\345\202\250.html" | 8 ++++---- category/roadmap.html | 8 ++++---- .../\345\217\214\345\221\250\344\274\232.html" | 8 ++++---- ...\344\273\266\345\257\271\346\257\224-1.html" | 8 ++++---- ...57\344\273\266\345\257\271\346\257\224.html" | 8 ++++---- ...\345\217\212\346\241\210\344\276\213-1.html" | 10 +++++----- ...57\345\217\212\346\241\210\344\276\213.html" | 8 ++++---- ...21\350\200\205\347\233\270\345\205\263.html" | 8 ++++---- "category/\346\200\247\350\203\275-1.html" | 8 ++++---- "category/\346\200\247\350\203\275.html" | 8 ++++---- ...45\345\217\243\346\226\207\346\241\243.html" | 8 ++++---- "category/\346\236\266\346\236\204-1.html" | 8 ++++---- "category/\346\236\266\346\236\204.html" | 8 ++++---- "category/\346\265\213\350\257\225-1.html" | 8 ++++---- "category/\346\265\213\350\257\225.html" | 8 ++++---- ...10\346\234\254\347\233\270\345\205\263.html" | 8 ++++---- ...76\345\214\272\347\233\270\345\205\263.html" | 8 ++++---- "category/\350\277\220\347\273\264-1.html" | 8 ++++---- "category/\350\277\220\347\273\264.html" | 8 ++++---- "category/\351\203\250\347\275\262-1.html" | 8 ++++---- "category/\351\203\250\347\275\262.html" | 8 ++++---- faq.html | 8 ++++---- index.html | 8 ++++---- markdown-page.html | 8 ++++---- search-index.json | 2 +- search.html | 8 ++++---- 134 files changed, 487 insertions(+), 487 deletions(-) delete mode 100644 assets/images/curvefs-arch-v2-04d6725115ee4a33aae09c8ab0af7201.webp delete mode 100644 assets/images/curvefs-data-cache-arch-856d05060fcc8fadbeda09024859409d.webp delete mode 100644 assets/images/curvefs-meta-arch-24bc47886dd26fa64f61d477c3e04051.webp delete mode 100644 assets/images/curvefs-meta-cache-arch-934896ba5bcfffe7fe92455d8aa251ff.webp delete mode 100644 assets/images/curvefs-warmup-557fa09de4aeb6d75d2d4fd859dec918.webp delete mode 100644 assets/js/561d73c1.53e3c897.js create mode 100644 assets/js/561d73c1.7c837df2.js delete mode 100644 assets/js/935f2afb.07607faf.js create mode 100644 assets/js/935f2afb.c5a3d2c7.js delete mode 100644 assets/js/afb57922.9930456c.js create mode 100644 assets/js/afb57922.fe368e64.js delete mode 100644 assets/js/bda932b4.2a104469.js create mode 100644 assets/js/bda932b4.da7302dc.js rename assets/js/{main.79895394.js => main.607578f6.js} (99%) rename assets/js/{main.79895394.js.LICENSE.txt => main.607578f6.js.LICENSE.txt} (100%) rename assets/js/{runtime~main.afb1ed5f.js => runtime~main.d917f5c7.js} (96%) diff --git a/404.html b/404.html index c041b91..63a5468 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ 找不到页面 | Curve Book - - + +
跳到主要内容

找不到页面

我们找不到您要找的页面。

请联系原始链接来源网站的所有者,并告知他们链接已损坏。

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-01-12.html b/Community/Double-Week-Meetings/01-2023/2023-01-12.html index cd310e1..75d6897 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-01-12.html +++ b/Community/Double-Week-Meetings/01-2023/2023-01-12.html @@ -4,13 +4,13 @@ 2023-01-12 | Curve Book - - + +
跳到主要内容

2023-01-12

时间

2023/01/12 19:00-19:30

加入会议

会议主题:curve双周会
会议时间:2023/01/12 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京
重复周期:2022/10/20-2023/07/27 19:00-19:30, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/DckjtgWnOM1o

#腾讯会议:493-3764-3146

会议内容

一、近期工作

主要内容

  1. curvebs/mds: add curvebs logicpool io metric
  2. curvebs/mds: add cluster io metrics
  3. curvebs/common: some mirror fixes and optimizations
  4. curvebs 性能调优指南
  5. avoid memcpy when using RocksDB slice

curve 版本

release2.4 已完成版本测试,预计这周发布

release2.5

详见 ChangeLog v2.5

bug修复

具体查阅开源周报

二、开源社区

curve开源周报

OpenCurve公众号文章:

关注OpenCurve公众号,查看更多Curve文章。

答疑 & 交流

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-02-09.html b/Community/Double-Week-Meetings/01-2023/2023-02-09.html index bfbe443..ad5e76e 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-02-09.html +++ b/Community/Double-Week-Meetings/01-2023/2023-02-09.html @@ -4,13 +4,13 @@ 2023-02-09 | Curve Book - - + +
跳到主要内容

2023-02-09

时间

2023/02/09 19:00 ~ 20:00

加入会议

W 邀请您参加腾讯会议网络研讨会(Webinar)
会议主题:Curve 社区双周会|2023 Roadmap 解读
会议时间:2023/02/09 19:00-20:00 (GMT+08:00) 中国标准时间 - 北京

点击专属链接入会,或添加至会议列表:
https://meeting.tencent.com/dw/8VUPUacQw4iR

#腾讯会议:792-313-318

复制该信息,打开手机腾讯会议即可参与

会议内容

一、Curve 2023 Roadmap

Curve Roadmap 2023

二、近期工作

已完成

  1. CurveFS 2.4 版本基本测试完成,CHANGELOG of v2.4
    • 新增CurveFS数据预热warmup功能
      • 主动通过命令行将指定文件/文件夹/文件列表文件中的内容加载进本地缓存中,以加速后续的访问请求
    • 优化CurveFS元数据均衡功能
      • 优化部分场景下元数据不均衡的问题
    • 升级aws s3 sdk至v1.9.x版本
      • 新的sdk使用eventloop的模型替换老sdk的threadpool模型
  2. curvefs/client:optimize warmup performance
  3. curvefs/tools-v2:fix warmpup single file
  4. docs: add .clangd template and configuration guide
  5. add clang-format and add some info to dev guides
  6. curvefs: should release s3adapter if we donot need compact
  7. (fix)fixcurvefs/client: fix client core dump
  8. curvebs/mds: constrains stripe in one segment
  9. QEMU: curve cbd support -blockdev

正在进行

  1. CurveBS 控制台
  2. CurveFS WarmUp 优化,支持任务查询和取消
  3. CurveFS BS存储后端性能优化

三、开源社区

开源周报

OpenCurve公众号文章

Google Summer of Code 2023

四、答疑 & 交流

Curve小助手微信

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-02-23.html b/Community/Double-Week-Meetings/01-2023/2023-02-23.html index dc04dc8..112dd7a 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-02-23.html +++ b/Community/Double-Week-Meetings/01-2023/2023-02-23.html @@ -4,14 +4,14 @@ 2023-02-23 | Curve Book - - + +
跳到主要内容

2023-02-23

时间

2023/02/23 19:00 ~ 19:30

加入会议

8毛 邀请您参加腾讯会议
会议主题:Curve 社区双周会
会议时间:2023/02/23 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/sI1hGHZ1msxC

#腾讯会议:341-233-112

复制该信息,打开手机腾讯会议即可参与

会议内容

一、近期工作

CurveFS

主要工作

Curve v2.5.0-beta 正式提测,目前正在修复相关测试暴露问题:

新增特性

  1. 支持共享缓存 memcache,提升 CTO 场景下数据写入的性能(CurveAdm 支持部署 memcache 集群)
  2. CurveBS 新工具 tool-v2 新增命令支持 (delete\list dir\list client\support create dir)

相关优化

  1. Braft 版本更新到 v1.1.2
  2. 优化本地缓存的读缓存模式(之前 diskCache.diskCacheType=1,只有读取的数据才得到缓存,写入的数据不做缓存)
  3. CurveBS 的刷盘逻辑优化
  4. 编译脚本合并

BugFix

  1. S3 版本回退,解决新版本的 S3 写入卡住的问题
master 分支

CurveBS

主要工作

内部正在推动 RDMA/SPDK 的开发优化,目前进入自测阶段,即将提测

master 分支

Curve 2023 Roadmap

为了让大家更好参与 Curve 社区,后续 roadmap 中一些方案将在社区中公开讨论。 相关的时间会提前在 roadmap 页面备注, 讨论之前也会在微信群中提示,欢迎大家参与,详见 Curve 2023 Roadmap

CurveBS 控制台

目前进展:

  • 目前正在推到一期功能的迭代开发
  • 一期主要提供信息展示功能,如集群总体概览、磁盘列表、存储池、卷等信息的展示
  • 目前开发已基本完成,即将提测给 QA,预计 3 月上旬会对外发布

CurveAdm v0.2.0

CurveAdm v0.2.0 Release

  • Improve: add CI (build and test action), thanks to Tsong Lew.
  • Improve: update go version since Go 1.18 is required to build rivo/uniseg, thanks to Tsong Lew.
  • Feature(exec): support execute command in specified container, thanks to Wangpan.
  • Feature(target): support specify target block size, thanks to mfordjody.
  • Feature(format): support stop formmating, thanks to DemoLiang.
  • Feature(mount): support setting environment variable for client container.
  • Feature(hosts): support setting SSH address which only for SSH connect.
  • Feature(playbook): add playbook which user can run any scripts in any hosts.
  • Feature(playbook): support deploy memcache by playbook, thanks to SiKu.
  • Feature(playbook): support setting host environment variable.
  • Feature(playbook): support pass arguments to run scripts.
  • Feature(playbook): support exclude and intersection pattern for playbook label.
  • Feature(playground): now we can run playground by specified container image.
  • Feature: add curvefs-fuse-bt bpftrace tool.
  • Fix: set environment variable failed while executing command.
  • Fix: its no need to become user when execute command in local.
  • Fix(map): map a volume which name contain underscore symbol.
  • Fix(format): wrong sed expression in become_user modle.

更新

CURVEADM_VERSION=v0.2.0 curveadm -u

二、开源社区

开源周报

OpenCurve公众号文章

三、答疑 & 交流

Curve小助手微信

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-03-09.html b/Community/Double-Week-Meetings/01-2023/2023-03-09.html index 1d47c44..6ced9ea 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-03-09.html +++ b/Community/Double-Week-Meetings/01-2023/2023-03-09.html @@ -4,14 +4,14 @@ 2023-03-09 | Curve Book - - + +
跳到主要内容

2023-03-09

时间

2023/03/09 19:00 ~ 19:30

加入会议

会议主题:curve双周会
会议时间:2023/03/09 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京
重复周期:2022/10/20-2023/07/27 19:00-19:30, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/DckjtgWnOM1o

#腾讯会议:493-3764-3146

会议内容

一、近期工作

CurveFS

主要工作

Curve v2.5.0-beta1 发布,主要是修复上个版本提测过程中发现的问题:

curvefs/client: bug fix about memcache

  1. When running the vdbech task, the memory usage of the mount point is very high
  2. When running the vdbech task, the mount will coredump or the task will have data inconsistency
  3. The memcache server fails over and then restarts and cannot be used by the client anymore
master 分支

CurveBS

主要工作

内部的 RDMA/SPDK 版本自测完成,已经提测

master 分支

Curve 2023 Roadmap

  • 完成 <CurveBS K8S 部署> 方案的社区讨论,当前的计划是优先完成集群的部署和清理,架构文档详见PR:Add architecture document

为了让大家更好参与 Curve 社区,后续 roadmap 中一些方案将在社区中公开讨论。 相关的时间会提前在 roadmap 页面备注, 讨论之前也会在微信群中提示,欢迎大家参与,详见 Curve 2023 Roadmap

CurveAdm

二、开源社区

开源周报

OpenCurve公众号文章

三、答疑 & 交流

Curve小助手微信

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-03-23.html b/Community/Double-Week-Meetings/01-2023/2023-03-23.html index 7e30883..9f94a6b 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-03-23.html +++ b/Community/Double-Week-Meetings/01-2023/2023-03-23.html @@ -4,15 +4,15 @@ 2023-03-23 | Curve Book - - + +
跳到主要内容

2023-03-23

时间

2023/03/23 19:00 ~ 19:30

加入会议

会议主题:curve双周会
会议时间:2023/03/23 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京
重复周期:2022/10/20-2023/07/27 19:00-19:30, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/DckjtgWnOM1o

#腾讯会议:493-3764-3146

会议内容

一、近期工作

CurveFS

主要工作

Curve v2.5.0-beta1 发布,主要是修复上个版本提测过程中发现的问题:

curvefs/client: bug fix about memcache

  1. When running the vdbech task, the memory usage of the mount point is very high
  2. When running the vdbech task, the mount will coredump or the task will have data inconsistency
  3. The memcache server fails over and then restarts and cannot be used by the client anymore
master 分支

CurveBS

主要工作

内部的 RDMA/SPDK 版本已经提测。 正在定位测试中出现的问题。

master 分支

Curve 2023 Roadmap

  • 完成 <CurveBS K8S 部署> 方案的社区讨论,当前的计划是优先完成集群的部署和清理,架构文档详见PR:Add architecture document
  • 目前正在完成 operator 部署。

为了让大家更好参与 Curve 社区,后续 roadmap 中一些方案将在社区中公开讨论。 相关的时间会提前在 roadmap 页面备注, 讨论之前也会在微信群中提示,欢迎大家参与,详见 Curve 2023 Roadmap

CurveAdm

  • 暂无

二、开源社区

开源周报

OpenCurve公众号文章

Google 编程之夏 2023

三、答疑 & 交流

Curve小助手微信

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-04-13.html b/Community/Double-Week-Meetings/01-2023/2023-04-13.html index 2820366..37ff5ed 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-04-13.html +++ b/Community/Double-Week-Meetings/01-2023/2023-04-13.html @@ -4,15 +4,15 @@ 2023-04-13 | Curve Book - - + +
跳到主要内容

2023-04-13

时间

2023/04/13 19:00 ~ 19:30

加入会议

会议主题:curve双周会
会议时间:2023/04/13 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京
重复周期:2022/10/20-2023/07/27 19:00-19:30, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dw/MF2yU2kTt6pR

#腾讯会议:703-9861-0542

会议内容

一、近期工作

CurveFS

  • CurveFS 使用CurveBs作为存储后端

该工作预计本月底完成代码merge(开发,开发自测)

CurveBS

  • 内部快照

当前快照: s3 预期快照: 1. s3 2. 本地

社区伙伴Zstack童鞋贡献:CurveBS内部快照pr

  • RDMA/SPDK 版本

内部的 RDMA/SPDK 版本已经QA提测,已经完成基本功能测试,性能测试。异常测试过程中遇到几个bug,部分bug已fix,还有两个bug解决

云原生部署

Curve Operator上手初体验

curvebs部署和删除的功能已经实现,目前处于测试和优化阶段,以及curve-csi的测试。同时还有一些新功能需要添加,这些功能和优化点也是开发者活动的题目。

curveFS的部署和删除根预计本月月底完成。

二、开源社区

开源活动预告

开源周报

OpenCurve公众号文章

本期文章
上期文章

三、答疑 & 交流

Curve小助手微信

为了让大家更好参与 Curve 社区,后续 roadmap 中一些方案将在社区中公开讨论。 相关的时间会提前在 roadmap 页面备注, 讨论之前也会在微信群中提示,欢迎大家参与,详见 Curve 2023 Roadmap

四、专题交流: Curve tools开发实践

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-04-27.html b/Community/Double-Week-Meetings/01-2023/2023-04-27.html index 70cfb0c..5c40ca5 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-04-27.html +++ b/Community/Double-Week-Meetings/01-2023/2023-04-27.html @@ -4,13 +4,13 @@ 2023-04-27 | Curve Book - - + +
跳到主要内容

2023-04-27

时间

2023/04/27 19:00~19:30

加入会议

Maggie 邀请您参加腾讯会议
会议主题:Curve社区双周会
会议时间:2023/04/27 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京
重复周期:2023/04/27-2023/12/21 19:00-19:30, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/eDgUO9uhntyu

#腾讯会议:548-7440-7396

复制该信息,打开手机腾讯会议即可参与

会议内容

一、近期工作

Curve文件存储

性能优化

功能支持

运维工具

Curve块存储

可视化

运维工具

  • curveadm 适配 rdma&spdk 版本镜像(初步可用,还未正式发布版本)@Wine93
  • curve_ops_tool重构(进行中,也是curve开发者活动的议题) @数名开发者

功能支持

  • 支持 PoolSet: 一个集群可以支持不同介质的物理池,可以指定卷的类型创建(开发中)@wu-hanqing

  • 支持rbd协议(开发中)@h0hmj

  • 支持本地快照(开发中,发起外部讨论)@xu-chaojie @David Lee

场景支持

  • 混闪场景支持 @zyb521(测试优化中)

    • NVME+HDD场景
    • wal独立部署: nvme分区独立做wal, 剩下的分区做bcache。极限性能会好一些,但性能不是很稳定,分区偏小的情况下容易被打满
    • bcache方案在内核 5.10(性能更低) 和 4.9 上表现不一致

二、开源社区

三、答疑 & 交流

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-05-18.html b/Community/Double-Week-Meetings/01-2023/2023-05-18.html index f1c5a50..7620027 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-05-18.html +++ b/Community/Double-Week-Meetings/01-2023/2023-05-18.html @@ -4,15 +4,15 @@ 2023-05-18 | Curve Book - - + +
跳到主要内容

2023-05-18

时间

2023/05/18 19:00~19:30

加入会议

you! 邀请您参加腾讯会议
会议主题:you!预定的会议
会议时间:2023/05/18 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/SSKvFmc6rRGl

#腾讯会议:911-103-973

复制该信息,打开手机腾讯会议即可参与

会议内容

一、近期工作

Curve文件存储

Curve块存储

功能支持

  • 支持 PoolSet: 一个集群可以支持不同介质的物理池,可以指定卷的类型创建

    • 底层curvebs开发基本完成, 上层openstack适配中
    • 升级兼容性逻辑修改,并已自测完成,发起review
  • rdma & spdk版本修复若干问题

    • 定位并修复一个IO始终未返回导致IO卡住的问题
    • 定位和修复重启chunkserver后,raft日志一直追赶不上的问题
    • 重启chunkserver报日志损坏问题,持续定位中
    • 优化容器化部署,rdma网络参数调整
  • curvebs 适配支持rbd协议

    • 功能基本完成,目前调试中,glance,cinder目前功能都已正常,nova还有部分功能还在调试
  • 支持本地快照

    • ztack外部开发者正在进行快照部分功能实现
    • curve团队目前在进行从快照克隆部分功能的poc,主要关注从快照克隆出卷的性能是否满足设计需求,预期至少对标ceph

场景支持

  • 混闪场景支持

    • NVME+HDD场景
    • wal独立部署: nvme分区独立做wal, 剩下的分区做bcache。极限性能会好一些,但性能不是很稳定,分区偏小的情况下容易被打满. 进展:将raft快照时间调整为2分钟,50G分区可以正常使用,过程中bcache的gc触发频率明显降低。
    • bcache方案在内核 5.10(性能更低) 和 4.9 上表现不一致, 进展:使用回退后的内核,未再发现其他问题。

工具相关

  • curveadm 支持 rdma&spdk 版本镜像,已提交PR,待进一步完善
  • CurveAdm支持部署控制台功能完成,并已自测完成
  • 控制台支持多集群功能完成,并已自测
  • 新运维工具tools-v2进展,添加bs status chunkserver功能完成,
  • 支持云原生部署 目前已实现CurveBS和CurveFS的K8s部署, 本周期完成代码整理和测试

二、开源社区

  • 多个开发者活动

  • 新活动预告 GitLink开源夏令营活动(GLCC),面向全国高校学生的暑期编程活动,目前社区报名火热进行中,curve社区正在报名,计划提交5个选题,欢迎社区的学生开发者踊跃参加. 链接:https://www.gitlink.org.cn/glcc

三、近期公众号文章

  • 《Curve 基于 SPDK target 的 iSCSI 性能优化实践》 链接:https://mp.weixin.qq.com/s/GycKSdjF5JO1sOsLLSeFZg

    • curve团队为游戏部门提供的性能优化解决方案,通过基于SPDK target的iscsi性能优化,使得业务测试场景的在小IO场景下相比ceph iscsi性能又200%以上的提升。
  • 《支持 POSIX 和 S3 统一命名空间—— Curve 文件系统 S3 网关部署实践》 链接:https://mp.weixin.qq.com/s/UeixFLkyQLTLNwfQ9Mo34w

    • AI 训练业务场景下, CurveFS提供S3 协议和 Posix 协议的统一命名空间能力,两种协议上传/写入的文件可以互相访问。
  • 《我的 Google Summer of Code 2023(GSoC) 申请之路》链接:https://mp.weixin.qq.com/s/hvmTQd-ljiifacNoPXikTw

    • 来自申请google开源之夏curve社区唯一入选的选题的学生的文章

四、答疑 & 交流

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-06-01.html b/Community/Double-Week-Meetings/01-2023/2023-06-01.html index 2854bbf..e1f61e5 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-06-01.html +++ b/Community/Double-Week-Meetings/01-2023/2023-06-01.html @@ -4,15 +4,15 @@ 2023-06-01 | Curve Book - - + +
跳到主要内容

2023-06-01

时间

2023 Jun 01, 19:00~19:30

加入会议

majie 邀请您参加腾讯会议
会议主题:majie预定的会议
会议时间:2023/06/01 19:00-20:00 (GMT+08:00) 中国标准时间 - 北京

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/glDOfgkpiwGe

#腾讯会议:467-801-402

复制该信息,打开手机腾讯会议即可参与

会议内容

一、近期工作

Curve文件存储

  1. close-to-open一致性场景下元数据性能优化

  2. 文件存储对接CurveBS后端

Curve块存储

  1. poolset支持 https://github.com/opencurve/curve/pull/1988 一个集群可以支持不同的物理池,可以指定卷的类型创建, review中

  2. rdma&spdk版本已开发完成,后续可用镜像将会上传发布,代码暂时不开源

  3. cbd接口模拟rbd接口用于支持OpenStack(Glance/Nova/Cinder)尝鲜使用 开发完成,在整理文档和代码中

  4. 支持本地快照 调研设计中

  5. web控制台开发完成,前后端联调中

  6. curvebs认证功能调研设计中

周边生态

  1. k8s curve operator积极开发中,现已能够支持部署curvebs和curvefs,正在开发监控相关
  2. curve运维新工具开发,进度18/27

二、开源社区

  • 多个开发者活动

  • 新活动预告 GitLink开源夏令营活动(GLCC),面向全国高校学生的暑期编程活动,目前社区报名火热进行中,curve社区正在报名,计划提交5个选题,欢迎社区的学生开发者踊跃参加. 链接:https://www.gitlink.org.cn/glcc

三、近期公众号文章

四、答疑 & 交流

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-06-15.html b/Community/Double-Week-Meetings/01-2023/2023-06-15.html index f5433f1..8853c96 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-06-15.html +++ b/Community/Double-Week-Meetings/01-2023/2023-06-15.html @@ -4,13 +4,13 @@ 2023-06-15 | Curve Book - - + +
跳到主要内容

2023-06-15

时间

2023 Jun 15, 19:00~19:30

加入会议

Sean 邀请您参加腾讯会议
会议主题:Curve社区例会
会议时间:2023/06/15 19:00-20:00 (GMT+08:00) 中国标准时间 - 北京
重复周期:2023/06/15-2024/05/02 19:00-20:00, 每两周 (周四)

点击链接入会,或添加至会议列表:
https://meeting.tencent.com/dm/jdXoeG8gBWJp

#腾讯会议:718-2692-7827

复制该信息,打开手机腾讯会议即可参与

会议内容

一、近期工作

Curve块存储

  1. v1.2.7 版本,主要包括poolset支持和一个client支持挂载多个集群的卷。

  2. CurveBS 认证功能已完成方案设计,开发中

  3. 支持本地快照 开发中

  4. rdma&spdk版本已开发完成,进行性能和稳定性测试中,后续可用镜像将会上传发布,代码暂时不开源

  5. web控制台开发完成,测试中,预览

Curve文件存储

版本v2.6 已提测,具体内容见changelog

文件存储对接CurveBS后端,功能和性能测试中,代码分支:https://github.com/opencurve/curve/tree/fs_curvebs_storage_support

周边生态

  1. k8s curve operator完成开发。欢迎大家使用,代码仓库&文档

  2. curve运维新工具开发,进度

  3. CurveAdm v0.3.0 版本开发完成,测试中。主要包括支持多用户共享、分布式数据库。

二、开源社区

  • 6月3日,由 KubeSphere 社区、极狐GitLab、Curve 社区联合主办的云原生 Meetup 杭州站取得圆满成功。会议回顾

  • 开发者活动进行中

    GitLink开源夏令营活动(GLCC),面向全国高校学生的暑期编程活动,目前社区报名火热进行中,curve社区正在报名,计划提交5个选题,欢迎社区的学生开发者踊跃参加.

    链接:https://mp.weixin.qq.com/s/AdHQTWxd16gou2eAb7nyWQ

  • 往期活动(已结束报名)

三、近期公众号文章

四、答疑 & 交流

- - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-06-29.html b/Community/Double-Week-Meetings/01-2023/2023-06-29.html index c8a1699..f7463fd 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-06-29.html +++ b/Community/Double-Week-Meetings/01-2023/2023-06-29.html @@ -4,8 +4,8 @@ 2023-06-29 | Curve Book - - + +
@@ -15,7 +15,7 @@ changelog: https://github.com/opencurve/curve/pull/2526/files

主要特性:
* 优化元数据性能,特别是 CTO 场景下
* 支持 warmup 到 memcache
  • 文件存储对接CurveBS后端,功能和性能测试中,代码分支:https://github.com/opencurve/curve/tree/fs_curvebs_storage_support

  • 周边生态

    1. k8s curve operator基本能力完成开发。欢迎大家使用 代码仓库&文档

    2. curve运维新工具开发,剩余在开发/reivew外部开发者pr过程。进度

    3. curveadm v0.3.0 已经发布预览版,欢迎体验 升级详见:https://github.com/opencurve/curveadm/pull/243 changelog: https://github.com/opencurve/curveadm/pull/243/commits

    二、开源社区

    三、近期公众号文章

    四、答疑 & 交流

    - - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-07-13.html b/Community/Double-Week-Meetings/01-2023/2023-07-13.html index b754a21..25a8af5 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-07-13.html +++ b/Community/Double-Week-Meetings/01-2023/2023-07-13.html @@ -4,13 +4,13 @@ 2023-07-13 | Curve Book - - + +
    跳到主要内容

    2023-07-13

    时间

    2023/07/13, 19:00-20:00

    加入会议

    W 邀请您参加腾讯会议
    会议主题:W预定的会议
    会议时间:2023/07/13 19:00-20:00 (GMT+08:00) 中国标准时间 - 北京

    点击链接入会,或添加至会议列表:
    https://meeting.tencent.com/dm/Rvx4NU7xdkz6

    #腾讯会议:653-606-758

    复制该信息,打开手机腾讯会议即可参与

    会议内容

    一、近期工作

    Curve块存储

    1. v1.2.7 版本提测,https://github.com/opencurve/curve/releases/tag/v1.2.7-beta2

      1. 多存储池支持,资源隔离(不同存储介质 HDD/SSD/NVMe、或者不同租户支持)

      2. 支持单客户端挂载不同集群的卷

    2. 支持本地快照和克隆,目前正在 POC 测试中,后续完善后会同步方案和代码

    3. RDMA & SPDK 版本部署文档(基于 CentOS 7.9,5.4 内核版本),https://github.com/opencurve/curve-meetup-slides/blob/9d17e0eb42df2bee0ec8c7a6c6aaf95a340028f5/PrePaper/2023/CurveBS%20RDMA%26SPDK%20%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97.md

    Curve文件存储

    1. 文件存储对接Curve块存储,目前已完成所有测试:fio、vdbench、fstrash、删除功能;重要 bug 已修复

    2. v2.6 版本已提测,QA 正在测试中,预计 7 月中下旬全部测试完毕

      1. 主要特性:

        • 优化元数据性能,特别是 CTO 场景下

        • 支持 warmup 到 memcache

      2. 分支:https://github.com/opencurve/curve/tree/release2.6

    周边生态

    1. CurveAdm 0.3.0 版本提测

      • 主要针对块存储部署进行优化,支持多用户部署

      • 适配块存储 v1.2.7 版本功能

      • 一键体验 CURVEADM_VERSION=v0.3.0-rc4 curveadm -u

    2. k8s curve operator完成开发,欢迎大家使用体验

    3. Curve 新运维工具开发

    二、Curve控制台开源 🎉🎉🎉

    Curve 控制台(curve-manager)是一个相对独立的 Web 服务,提供基于 Web 的集群管理能力,使得存储集群的部署和管理门槛进相对 CLI方式一步降低,提供一种更加清晰直观的视图。

    项目仓库:

    前端代码仓库:https://github.com/opencurve/curve-dashboard

    后端代码仓库:https://github.com/opencurve/curve-manager

    控制台接口:

    前后端交互接口:https://github.com/opencurve/curve-manager/blob/master/api/curvebs/manager/bind.go

    后端与存储集群交互接口:

    1. RPC请求接口,主要与MDS通信获取信息

      https://github.com/SeanHai/curve-go-rpc/tree/ff4a19bed1392415310cd3636c0fee77e487adcd/rpc/curvebs

    2. HTTP请求接口,主要与监控组件Prometheus通信获取信

      https://github.com/opencurve/curve-manager/blob/master/internal/metrics/core/metrics.go

    三、开源社区

    1. Curve在夏季特别活动预热(08-09),目前在准备阶段,本次奖品丰厚,形式新颖,欢迎大家关注参与。
    2. 往期活动(已结束报名)

    四、近期公众号文章

    1. 使用 Curve 云上部署 Hadoop,轻松节约 50% 存储成本 https://mp.weixin.qq.com/s/xuH4O_UU3fNlYJhtrUlh0Q

    2. CurveAdm 离线部署 Curve 集群操作指南,https://mp.weixin.qq.com/s/jJNRIFt3DkKuJc_Zq6IgBw

    五、答疑 & 交流

    - - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-08-03.html b/Community/Double-Week-Meetings/01-2023/2023-08-03.html index 3be17f1..6298ebc 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-08-03.html +++ b/Community/Double-Week-Meetings/01-2023/2023-08-03.html @@ -4,14 +4,14 @@ 2023-08-03 | Curve Book - - + +
    跳到主要内容

    2023-08-03

    时间

    2023/08/03, 19:00-19:40

    加入会议

    W 邀请您参加腾讯会议
    会议主题:W预定的会议
    会议时间:2023/08/03 19:00-19:40 (GMT+08:00) 中国标准时间 - 北京

    点击链接入会,或添加至会议列表:
    https://meeting.tencent.com/dm/Rvx4NU7xdkz6

    #腾讯会议:653-606-758
    (可微信小程序入会)
    复制该信息,打开手机腾讯会议即可参与

    0:会议内容概要

    本次社区双周会主要包括以下几点:

    • 上半年社区roadmap总结与下半年工作展望
    • Curve自动化测试框架开源
    • Curve官方文档网站正式搭建
    • “社区活动 Summer Code Camp”正在持续招募报名

    会议内容

    一. 2023上半年社区Roadmap总结与下半年工作展望

    1. 感恩/致谢

    自开源以来,Curve 项目受到了社区小伙伴们的大量关注和讨论,项目的一些特色得到了部分社区小伙伴的认可,但是还有更多的小伙伴们给Curve 项目提出了更高的要求和期望,这些需求我们都已经收到并且牢记在心,只是限于社区开发者数量和能力,不能立刻满足。

    但我们一直在朝着更优秀的分布式存储项目在努力,也希望更多优秀的小伙伴一起加入社区,为这个年轻的社区添砖加瓦,争取让Curve从开源基础设施领域的新星成长为明星。

    Curve贡献者-2023H1

    2. Curve当前主推场景

    • Curve文件存储已经上线并支持机器学习,ES,NAS等以posix为接口的场景,即将支持以HDFS为接口的大数据场景;
    • Curve块存储已经上线并支持高随机的数据库,云盘等场景

    3. 上半年roadmap及下半年展望

    详见下文:

    Curve 社区上半年 Roadmap 进展及下半年规划

    二、近期主要工作

    1. Curve测试框架开源

    Curve 测试框架fsthrash

    类似与ceph的Teuthology,可以自动部署Curve服务,并自动进行功能测试,io测试,异常测试以及一致性测试等。

    2. Curve官方文档网站上线

    Curve官方文档网站

    Curve贡献者-2023H1

    Curve的文档目前有不少,但是散落于Curve githu主仓库,curveadm以及curve-meetup-slides等子仓库。我们深知文档易读性的重要性,同时也是应Curve社区很多小伙伴的要求,所以我们建立了这个官方文档网站

    框架本周刚搭建完成,内容有待补充,也欢迎社区同学们给文档库提pr,一起来完善这个唯一的入口。

    三、开源社区

    1. “社区活动 Summer Code Camp”正在持续招募报名,本次奖品丰厚,形式新颖,欢迎大家关注参与。

    本次开发者活动主要分为两大专题,如下:

    • Curve项目本身

    准备了非常多有意思且范围涵盖较广的议题,包括Curve web控制台方向,云原生方向,核心代码逻辑方向,Curveadm方向以及Curve的Ci方向。涉及的语言包括C/C++、go、python/shell等

    • 社区联动

    openEuler拥有非常好的开源生态系统。Curve期望在openEuler操作系统上运行,所以我们需要做一些适应和验证工作。

    开发活动详情见: Curve夏季特别活动

    四、近期公众号文章

    1. Curve 社区上半年 Roadmap 进展及下半年规划

    2. Curve 控制台详细部署使用指南

    五、答疑 & 交流

    Curve小助手微信

    - - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-09-14.html b/Community/Double-Week-Meetings/01-2023/2023-09-14.html index 00a2a4a..16aadf5 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-09-14.html +++ b/Community/Double-Week-Meetings/01-2023/2023-09-14.html @@ -4,13 +4,13 @@ 2023-09-14 | Curve Book - - + +
    跳到主要内容

    2023-09-14

    时间

    2023/09/14 19:00~19:30

    加入会议

    majie 邀请您参加腾讯会议
    会议主题:majie预定的会议
    会议时间:2023/09/14 19:00-19:30 (GMT+08:00) 中国标准时间 - 北京

    点击链接入会,或添加至会议列表:
    https://meeting.tencent.com/dm/hhSiD8xDJ5z3

    #腾讯会议:434-723-266

    复制该信息,打开手机腾讯会议即可参与

    会议内容

    一、近期工作

    Curve文件存储

    • CurveFS对接HDFS

      • HDFS基本对接完成
      • HDFS接口权限开发基本完成
      • 已有容器镜像可供测试,使用文档整理中
    • fs级别的quota积极开发中

    • bug修复

    Curve块存储

    二、开源社区活动和外部会议

    三、近期公众号文章

    四、答疑 & 交流

    - - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/2023-10-12.html b/Community/Double-Week-Meetings/01-2023/2023-10-12.html index ff64f39..f4e1be4 100644 --- a/Community/Double-Week-Meetings/01-2023/2023-10-12.html +++ b/Community/Double-Week-Meetings/01-2023/2023-10-12.html @@ -4,13 +4,13 @@ 2023-10-12 | Curve Book - - + +
    跳到主要内容

    2023-10-12

    时间

    2023/10/12 19:00~19:30

    加入会议

    Sean 邀请您参加腾讯会议
    会议主题:Curve开源社区双周会
    会议时间:2023/10/12 19:00-20:00 (GMT+08:00) 中国标准时间 - 北京

    点击链接入会,或添加至会议列表:
    https://meeting.tencent.com/dm/bH17ULhFHaxr

    #腾讯会议:554-339-963

    复制该信息,打开手机腾讯会议即可参与

    会议内容

    一、近期工作

    Curve文件存储

    Curve块存储

    二、开源社区活动和外部会议

    三、近期公众号文章

    四、答疑 & 交流

    - - + + \ No newline at end of file diff --git a/Community/Double-Week-Meetings/01-2023/README.html b/Community/Double-Week-Meetings/01-2023/README.html index 0751f7b..908141f 100644 --- a/Community/Double-Week-Meetings/01-2023/README.html +++ b/Community/Double-Week-Meetings/01-2023/README.html @@ -4,14 +4,14 @@ README | Curve Book - - + +
    跳到主要内容

    README

    Curve 双周会

    内容

    社区同步开发进度、版本规划、方案讨论

    时间

    每两周一次(通常在周四晚19:00~19:40),使用腾讯会议,提前用户群通知

    参与我们

    • 加入用户群,当前为微信群,由于群人数过多,需要先添加以下个人微信,再邀请进群。 Curve小助手微信

    • 周会议题提交:issue

    - - + + \ No newline at end of file diff --git a/Community/community-guideline.html b/Community/community-guideline.html index 759fb7d..8afe1f2 100644 --- a/Community/community-guideline.html +++ b/Community/community-guideline.html @@ -4,13 +4,13 @@ 社区准则 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/architecture-intro.html b/CurveBS/architecture/architecture-intro.html index 6c7ac2c..2f39522 100644 --- a/CurveBS/architecture/architecture-intro.html +++ b/CurveBS/architecture/architecture-intro.html @@ -4,13 +4,13 @@ 架构介绍 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/chunkserver-arch.html b/CurveBS/architecture/chunkserver-arch.html index 64fe32c..1d1d790 100644 --- a/CurveBS/architecture/chunkserver-arch.html +++ b/CurveBS/architecture/chunkserver-arch.html @@ -4,8 +4,8 @@ CurveBS Chunkserver 架构介绍 | Curve Book - - + +
    @@ -15,7 +15,7 @@ 读取时,获取的ChunkserverPersistentData数据首先校验校验码,防止读取到损坏的数据,并确认版本号是支持的版本。

    Chunkserver Copyset重建

    Chunkserver在重启时,需要重建在重启前已经分配过的Copyset,以响应来自Client/MDS端针对这些Copyset的访问。

    由于Copyset被创建时和初始化时,会在数据主目录下创建copyset子目录,因而留下持久化信息,如1.4节所示。所以可以根据copyset目录列表,得到已经分配的Copyset列表;由于braft本身持久化了raft configuration,所以如果使用相同的copyset数据目录,braft可以自动从持久化信息中恢复出raft configuration。因此Copyset的持久化数据,可以全部从Chunkserver的本地存储盘获取。

    Chunkserver启动时,通过扫描数据主目录下的"LogicPoolId/CopysetId"子目录列表,以获得Chunkserver已经创建的的Copyset列表,根据子目录名字的格式,可以解析出来poolId, 和copysetId,然后使用空的raft configuration可以重建Copyset实例,Copyset重建后,由于使用相同的raft各类数据目录,braft可以从snapshot meta中自动恢复出raft configuration出来,加载snapshot和log, 完成初始化。

    2.2.2 HeartBeat

    MDS需要实时的信息来确认Chunkserver的在线状态,并得到Chunkserver和Copyset的状态和统计数据,并根据信息下发相应的命令。 Chunkserver以心跳的方式来完成上述功能,通过周期性的心跳报文,更新Chunkserver和Copyset的信息,反馈上一个心跳周期的命令执行结果,并接受,解析和执行新的心跳响应报文。

    Chunkserver通过心跳消息定期向MDS更新Chunkserver信息,Copyset状态信息,磁盘状态信息,配置变更命令执行状态等信息,MDS收到请求后,根据调度逻辑将新的copyset配置变更命令添加到消息响应中。

    心跳流程详细流程图:

    heartbeat

    2.2.3 CopysetNode

    CopysetNode封装了RaftNode的状态机,是chunkserver的核心模块,其总体架构如下图所示:

    curve-raft-arch-1

    整个curve-raft对用户暴露的接口,主要提供给用户Propose task给raft statemachine处理

    主要是核心处理逻辑的模块

    (1)NodeImpl主要:

    1. 接收来自外部的Request,然后通过 Raft statemachine RawNode的接口提交给状态机处理,而且其会拥有一个独立Propose execqueue负责驱动Raft statemachine运转,并输出中间结果Ready
    2. 而且是Node Interface的实际实现者,除此之外,还包含了处理Tick timeout和InstallSnapshot的逻辑。

    (2)RaftNode

    主要是处理Raft StateMachine的输出的中间结果Ready,包括异步处理Ready的exec queue。包含需要转发给其他Peers的Msg,需要落盘的Op log entries,以前需要Apply的committed entries

    (3)FSMCaller

    负责User StateMachine的具体执行逻辑,也就是State Machine Apply的逻辑,会包含相应的Apply bthread异步batch的处理需要Apply的请求。

    (1)Snapshot Storage:负责snapshot meta和snapshot read/write接口

    (2)LogManager:Op Log日志管理者,包含Op Log的读写接口,还有Log落盘的batch控制,Op Log缓存等。实际的Op Log最基本的读写接口还是依赖LogStorage的实现。

    (3)Raft Meta Storage:raft meta(term,vote,也就是HardState)的持久化

    (4)Transport:负责管理复制组Peers的连接,提供message send和peers链接管理的接口

    2.2.4 ChunkFilePool

    ChunkFilePool位于DataStore层,Chunkserver使用基于ext4实现的本地文件系统,由于写操作存在较大的IO放大,因此在创建chunk文件时会调用fallocate为文件预分配固定大小的空间,但是即便fallocate以后,在写文件未写过的块时仍需要更改元数据,存在一定的IO放大。 curve使用的一种优化方案是直接使用覆盖写过一遍的文件。由于chunkserver上的所有chunk文件和快照文件都是相同固定大小的文件,所以可以预先生成一批被覆盖写过的固定大小文件,创建chunk文件或快照文件时直接从预分配的文件池中获取进行重命名,删除chunk文件或快照文件时再将文件重命名放到预分配池中,这个预分配池就是chunkfile-pool。

    chunkserver目录结构:

    chunkserver/
    copyset_id/
    data/
    log/
    meta/
    snapshot/
    ...

    chunkfilepool/
    chunkfile_xxx_tmp
    chunkfile_xxx_tmp
    ...
    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/client-arch.html b/CurveBS/architecture/client-arch.html index 881d087..0780c3b 100644 --- a/CurveBS/architecture/client-arch.html +++ b/CurveBS/architecture/client-arch.html @@ -4,13 +4,13 @@ CurveBS Client 架构介绍 | Curve Book - - + +
    跳到主要内容

    CurveBS Client 架构介绍

    1 概要

    curve作为中心化的分布式存储系统主要组件分为前端驱动Client模块、底层存储Chunkserver模块、元数据服务器MDS模块,以及快照系统。

    所谓前端驱动client模块即为curve client,它以动态库libcurve的方式向上提供服务,外部服务进程如Qemu、Curve-Nbd通过链接该库来使用curve提供的存储服务。(热升级后,curve client不再直接对接Qemu和Curve-Nbd等上层服务。)

    所以,curve client可以理解为用户行为入口。

    2 curve client功能点

    Curve Client存在的目的就是通过提供接口的方式向上提供用户数据存储服务,curve系统以分布式文件系统作为底层存储向上提供块存储服务。从用户角度来看,curve为其提供的就是一个可以进行随机读写的块设备,这个块设备对应的就是底层curvefs的一个文件。

    这个curvefs文件的底层存储方式是分布在多个底层集群节点上的,举个例子:

    用户创建一个1T的块设备,这个块设备在底层curvefs上就是一个1T大小的文件。如下图:

    注意:这个所谓的1T大小的文件只是一个逻辑概念,这个文件只有curve client和mds两个模块感知,底层存储集群chunksevrer节点不感知这个文件。

    client-file

    用户一侧创建一个1T的块设备,对其而言是一个连续的0-1T的连续地址空间,其可以在这个空间内进行随机读写。但是从curve角度来看,这个1T大小的块设备就是一个由一批chunk组成的地址空间,每个chunk有唯一的ID编号,这些chunk会对应到底层chunkserver上一段真实的物理存储空间。

    curve client与底层存储集群以rpc方式通信,因此curve client的功能点也就呼之欲出。

    2.1 提供接口

    用户创建块设备及读写块设备都是通过curve client提供的接口,这些接口包括创建块设备、打开块设备等等,以及读写该块设备,为了追求极致的性能,curve向上提供异步读写接口。

    2.2 IO拆分

    从上图可以看到,用户的块设备其实在底层物理存储中是以chunk为单位打散在底层多个存储节点上的,这一方面可以提供处理的并发度,同时也能够提高数据的可靠性。

    用户写数据到curve client,client能够拿到用户IO的offset、length、data,这个offset + length在curve的文件上有可能会覆盖多个chunk,因为底层chunk是分布在不同的chunkserver上的,所以curve client需要把用户对块设备的的IO请求,转换为对底层不同chunkserver的chunk请求,并分别下发。

    2.3 IO跟踪

    IO拆分会将用户的IO根据offset和length拆分,比如一个用户的IO offset = 0, length = 2*chunksize,那么这个请求到client后,会被至少拆分成两个请求,因为这个IO正好横跨两个chunk,这两个chunk分布在不同的chunkserver上,因此一个大IO被拆分成了两个小IO,由于client的IO接口是异步的,而且client写chunk也是异步的,所以需要跟踪一个大IO的每个子IO,记录其返回状态,只有所有的子IO都返回这个IO才能算完成,然后才能向上返回给用户。

    2.4 元数据获取及缓存

    curve client本身是一个无状态的组件,不会保存文件的任何元数据信息,上面提到的IO拆分,是需要依据的,client需要知道chunk对应到具体chunkserver上的某一个物理chunk。这些信息都是通过mds获取的,mds会持久化文件的路由信息,当用户写某个块设备时,client需要从mds获取这个文件的元数据信息,然后保存在内存,供后续使用。

    一个用户IO下发需要的元信息:

    1. 上层文件与底层chunk的对应关系,即上层文件的逻辑chunk,与底层物理chunk的对应关系
    2. 底层物理chunk所属的raft group
    3. raft group所在的chunkserver列表

    2.5 mds failover 支持

    mds提供元数据存储服务,为了提高可用性,mds以集群方式服务,同一时刻只有一个mds提供服务,其他mds节点通过etcd进行监听,随时准备替代leader,成为新leader提供服务。

    curve client在IO下发及控制面调用时会与mds进行通信,通信以RPC方式进行,通信时需要指定对应的mds的地址,所以如果mds集群中服务节点切换时还需要client进行地址切换。

    2.6 Chunkserver failover 支持

    与mds高可用设计类似,chunkserver存储节点通过使用raft算法,实现多节点数据复制及高可用保证,在curve client一侧下发请求时需要先获取当前raft group的leader,然后将请求发往leader所在的节点。

    2.7 IO流控

    所谓IO流控就是curve client在感知底层集群负载过高时进行一定的流量控制,防止底层负载恶性循环。从本质上讲,client一侧的IO流控只是一种缓解方式,并非一种根本解决方案。

    3 curve client架构

    上节大致描述了client的功能点,本节分析client的架构,主要从模块架构和线程模型两方面。

    3.1 模块架构

    curve client的基本模块架构如下,其中client qos目前暂未实现

    client-arch

    3.2 线程模型

    client-arch

    这里以异步IO请求为例,client一侧共涉及到4类线程:

    1. 用户线程:用户发起异步IO请求,会调用client的AioWrite接口,AioWrite会将用户请求封装成task放入任务队列中,此时用户调用就返回了。
    2. 任务队列线程:任务队列线程会监听任务队列,只要有任务进来,就从队列中取出,进行IO拆分,拆分成的Sub-IO请求会放入IO分发队列中。
    3. IO分发线程:根据Sub-IO的信息,调用RPC发送异步请求到对应的chunkserver节点。
    4. BRPC线程:RPC的发送以及请求的返回都是在BRPC线程中处理的,rpc返回后还会调用异步请求的Closure回调。如果所有的Sub-IO都返回成功,则会找到对应的上层IO请求,并调用异步IO的回调(回调的操作也是在BRPC线程中执行)。

    4 关键点

    4.1 MetaCache

    IO拆分过程中,需要依赖文件的元数据信息,这些元数据信息在client一侧并不持久化,只是在需要的时候从mds获取。

    为了避免频繁与mds通信,client会对获取到的元数据进行缓存。

    缓存的元数据在2.4节已经提到,主要是以下几点:

    1. file chunk -> chunk id:上层文件与底层chunk的对应关系,即上层文件的逻辑chunk,与底层物理chunk的对应关系
    2. chunk id -> copyset id(raft group):底层物理chunk所属的raft group
    3. copyset id -> serverlist:raft group所在的chunkserver列表

    4.1.1 元数据缓存更新

    通常文件一旦分配了对应的chunkid和copysetid,这些信息就不会变化。

    上述三种元数据会出现更新的就是copysetid到serverlist的映射关系。因为底层如果出现节点宕机、负载不均衡等情况会触发配置变更,这种情况下会导致copyset的chunkserver列表发生变更。

    client触发元数据更新都是通过RPC失败。

    更新的元数据信息包括

    1. raft group的leader信息

      client发送请求是发送到raft group leader节点,在每次发送请求之前都会获取当前raft group的leader信息(metacache进行缓存)。

      请求发送到chunkserver之后,如果这个chunkserver已经不是leader了,那么client会发起GetLeader请求到当前raft group里的其他chunkserver节点。因为如果底层重新选举出了leader,节点肯定知道当前leader是谁,然后会把这个信息返回给client,client更新完metacache中的leader信息之后将请求转发到新leader。

    2. raft group的peer信息

      极端状况下,某个raft group因为底层节点宕机等原因,其group peer全部变更了。这种情况下,通过GetLeader是无法获取真正的leader信息,所以在重试多次之后client会向mds拉取当前raft group的最新信息。

    4.2 IO请求重试

    IO分发线程获取IO任务后会发送异步RPC请求,RPC请求返回后,会调用异步请求的回调。在回调中,会根据RPC请求的返回值,判断请求成功与否。如果请求失败,而且需要进行重试,那么会在回调中再次发起rpc请求。

    对于读写请求,重试次数设置很大,目的在于让IO尽可能成功返回。因为对于块设备而言,如果用户写入返回错误会造成上层认为块设备错误,硬盘损坏。

    RPC请求重试时,会进行预处理,分为两种情况:

    1. chunkserver overload

      这种情况下,对应的RPC Response中返回的错误码是OVERLOAD,说明底层chunkserver目前负载较高。

      此时,如果直接发送重试请求,大概率的情况下还是会返回OVERLOAD。所以这种情况下,应该让client在下发重试请求前,一段时间的sleep。

      在client一侧,针对这种情况,加入了睡眠重试时间指数退避,并加入了睡眠时间随机抖动,避免大量请求同时被返回OVERLOAD后,又同时重试。

    2. RPC Timeout

      出现这种情况的原因比较多,但是常见的原因是,底层chunkserver处理请求较多,压力较大,导致请求的返回时间超过了预期的RPC超时时间。

      这种情况下,如果重试请求的RPC超时时间不发生变化,那么很可能会重复上述流程,导致用户IO请求迟迟未能返回。所以,在这种情况下,重试请求会将RPC超时时间进行增加。

    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/mds.html b/CurveBS/architecture/mds.html index f498266..723bbed 100644 --- a/CurveBS/architecture/mds.html +++ b/CurveBS/architecture/mds.html @@ -4,13 +4,13 @@ CurveBS MDS 架构介绍 | Curve Book - - + +
    跳到主要内容

    CurveBS MDS 架构介绍

    概要

    MDS是中心节点,负责元数据管理、集群状态收集与调度。MDS包含以下几个部分:

    • Topology: 管理集群的 topo 元数据信息。
    • NameServer: 管理文件的元数据信息。
    • CopySet: 副本放置策略。
    • Heartbeat: 心跳模块。跟chunkserver进行交互,收集chunkserver上的负载信息,copyset信息等。
    • Schedule: 调度模块。用于自动容错和负载均衡。

    Topology

    topology用于管理和组织机器,利用底层机器的放置、网络的规划以面向业务提供如下功能和非功能需求。

    1. 故障域的隔离:比如副本的方式分布在不同机器,不同机架,或是不同的交换机下面。
    2. 隔离和共享:不同用户的数据可以实现固定物理资源的隔离和共享。

    curve整体的拓扑结构如下图:

    mds topo

    chunkserver: 用于抽象描述物理服务器上的一块物理磁盘(SSD),chunkserver以一块磁盘作为最小的服务单元。

    server: 用于抽象描述一台物理服务器,chunkserver必须归属于server。

    zone: 故障隔离的基本单元,一般来说属于不同zone的机器至少是部署在不同的机架,再要求严格一点的话,属于不同zone的机器可以部署在不同机架组下面(一个机架组共享一组堆叠 leaf switch),一个server必须归属于一个zone。

    pool: 用于实现对机器资源进行物理隔离,pool中server之间的交互仅限于pool之内的server。运维上,可以在上架一批新的机器的时候,规划一个全新的pool,以pool为单元进行物理资源的扩容(pool内扩容也可以支持,但是不建议pool内扩容,因为会影响每个chunkserver上的copyset的数量)。

    借鉴ceph的设计,curve在如上物理pool之上又引入逻辑pool的概念,以实现统一存储系统的需求,即在单个存储系统中多副本PageFile支持块设备、三副本AppendFile(待开发)支持在线对象存储、AppendECFile(待开发)支持近线对象存储可以共存。

    mds topo

    如上所示LogicalPool与pool为多对一的关系,一个物理pool可以存放各种类型的file。当然由于curve支持多个pool,可以选择一个logicalPool独享一个pool。

    通过结合curve的用户系统,LogicalPool可以通过配置限定特定user使用的方式,实现多个租户数据物理隔离(待开发)。

    logicalPool: 用于在逻辑层面建立不同特性的pool,比如如上AppendECFile pool、AppendEC pool 、PageFile pool;实现user级别的数据隔离和共享。

    NameServer

    NameServer管理namespace元数据信息,包括(更具体的信息可以查看curve/proto/nameserver2.proto):

    FileInfo: 文件的信息。

    PageFileSegment: segment是给文件分配空间的最小单位 。

    PageFileChunkInfo: chunk是数据分片的最小单元。

    segment 和 chunk的关系如下图:

    mds seg

    namespace信息较为直观,即文件的目录层次关系:

    mds ns

    如上所示。在KV中,Key是ParentID + "/"+ BaseName,Value是自身的文件ID;这种方式可以很好地平衡几个需求:

    1. 文件列目录:列出目录下的所有文件和目录
    2. 文件查找:查找一个具体的文件
    3. 目录重命名:对一个目录/文件进行重命名

    当前元数据信息编码之后存储在 etcd 中。

    CopySet

    Curve系统中数据分片的最小单位称之为Chunk,默认的Chunk大小是16MB。在大规模的存储容量下,会产生大量的Chunk,如此众多的Chunk,会对元数据的存储、管理产生一定压力。因此引入CopySet的概念。CopySet可以理解为一组复制组,这组复制组的成员关系完全一样。CopySet的概念在文献「Copysets: Reducing the Frequency of Data Loss in Cloud Storage」提出,本意是为了提高分布式存储系统中的数据持久性,降低数据丢失的概率。

    在 Curve 系统引入 CopySet 有几个目的:

    1. 减少元数据量:如果为每个Chunk去保存复制组成员关系,需要至少 ChunkID+3×NodeID=20 个byte,在1PB的逻辑数据量,4MB的Chunk的场景下,需要5GB的数据量;而如果在Chunk到复制组之间引入一个CopySet,每个Chunk可以用ChunkID+CopySetID=12个byte,数据量减少到3GB
    2. 减少复制组数量:如果一个数据节点存在 256K个复制组,复制组的内存资源占用将会非常恐怖;复制组之间的通信将会非常复杂,例如复制组内Primary给Secondary定期发送心跳进行探活,在256K个复制组的情况下,心跳的流量将会非常大;而引入CopySet的概念之后,可以以CopySet的粒度进行探活、配置变更,降低开销
    3. 提高数据可靠性:在数据复制组过度打散的情况下,在发生多个节点同时故障的情况下,数据的可靠性会受到影响

    ChunkServer,Copyset和Chunk三者之间的关系如下图:

    mds copyset

    Heartbeat

    心跳用于中心节点和数据节点的数据交互,详细功能如下:

    ​ 1. 通过chunkserver的定期心跳,检测chunkserver的在线状态(online,offline)

    ​ 2. 记录chunkserver定期上报的状态信息(磁盘容量,磁盘负载,copyset负载等),以提供运维工具查看上述状态信息。

    ​ 3. 通过上述信息的定期更新,作为schedule 模块进行均衡及配置变更的依据

    ​ 4. 通过chunkserver定期上报copyset的epoch,检测chunkserver的copyset与mds差异,同步两者的copyset信息

    ​ 5. 支持配置变更功能,在心跳回复报文中下发mds发起的配置变更命令,并在后续心跳中获取配置变更进度。

    心跳模块的结构如下:

    mds heartbeat

    MDS端

    mds 端的心跳主要由三个部分组成:

    TopoUpdater: 根据 chunkserver 上报的 copyset 信息更新拓扑中的信息。

    ConfGenerator: 将当前上报的 copyset 信息提交给调度模块,获取该 copyset 上可能需要执行的任务。

    HealthyChecker: 检查集群中的 chunkserver 在当前时间点距离上一次心跳的时间,根据这个时间差更新chunkserver状态。

    Chunkserver端

    chunkserver 端的心跳由两个部分组成:

    ChunkServerInfo/CopySetInfo: 获取当前 chunkserver 上的 copyset 信息上报给 MDS。

    Order ConfigChange: 将 MDS 下发的任务提交给对应的 copyset 复制组。

    Schedule

    系统调度是为了实现系统的自动容错和负载均衡,这两个功能是分布式存储系统的核心问题,也是 curve 是否能上生产环境的决定因素之一。自动容错保证常见异常(如坏盘、机器宕机)导致的数据丢失不依赖人工处理,可以自动修复。负载均衡和资源均衡保证集群中的磁盘、cpu、内存等资源的利用率最大化。

    调度模块的结构如下:

    mds schedule

    Coordinator: 调度模块的对外接口。心跳会将chunkserver上报上来的copyset信息提交给Coordinator,内部根据该信息判断当前copyset是否有配置变更任务执行,如果有任务则下发。

    任务计算: 任务计算模块包含了多个定时任务触发任务定时任务 中,CopySetScheduler 是copyset均衡调度器,根据集群中copyset的分布情况生成copyset迁移任务;LeaderScheduler 是leader均衡调度器,根据集群中leader的分布情况生成leader变更任务;ReplicaScheduler 是副本数量调度器,根据当前copyset的副本数生成副本增删任务;RecoverScheduler 是恢复调度器,根据当前copyset副本的存活状态生成迁移任务。触发任务 中,RapidLeaderScheduler 是快速leader均衡器,由外部触发,一次生成多个leader变更任务,使得集群的leader尽快达到均衡状态。TopoAdapter 用于获取Topology中调度需要使用的数据。Common Strategy 中是通用的副本添加和移除策略。

    任务管理: 任务管理模块用于管理计算模块产生的任务。operatorController 是任务集合,用于存放和获取任务;operatorStateUpdate 根据上报的copyset信息更新状态;Metric用于统计不同任务个数。

    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/nebd.html b/CurveBS/architecture/nebd.html index a3ea44e..47562b9 100644 --- a/CurveBS/architecture/nebd.html +++ b/CurveBS/architecture/nebd.html @@ -4,13 +4,13 @@ 客户端热升级设计介绍 | Curve Book - - + +
    跳到主要内容

    客户端热升级设计介绍

    概要

    Curve Client作为Curve集群的入口,是以链接库的形式提供给Qemu/Curve-NBD等上层使用,所以在Curve Client需要升级的时候,需要对Qemu/Curve-NBD进行重启。

    为了减少升级对上层业务的影响,我们对上层应用与Curve Client的耦合关系进行拆分,在两者之间加入了热升级模块:NEBD

    整体架构

    overview

    上图是当前系统的部署架构。

    • NEBD Client:即part1,对应上层业务,包括Qemu/Curve-NBD;通过Unix Domain Socket的方式连接到指定的NEBD Server。
    • NEBD Server:即part2,负责接收part1的请求,并调用Curve Client进行相应处理;part2可以接收多个不同NEBD Client的请求。

    从图中可以看到,part1取代了Curve Client直接与上层业务对接。这种情况下,part1的升级也会影响上层业务,所以在设计中,尽量减少了part1的处理逻辑,只进行请求转发,以及有限的重试逻辑。

    升级part2/Curve Client时,需要执行以下步骤:

    1. 安装新版part2/Curve Client
    2. 停止当前part2进程
    3. 重新启动part2进程

    在实践中,我们使用daemon监控part2进程,如果进程不存在,则会主动拉起。

    在测试及生产环境中,从停止part2进程,到新版part2启动,耗时在1~5s之间。

    模块架构图

    modules

    part1

    • libnebd:提供给上层业务的API接口,包括open/close、读写等接口。
    • File Client:主要是对libnebd接口的具体实现,向part2发送用户请求。
    • MetaCache Manager: 主要记录当前已经open文件的信息。
    • Heartbeat Client:定期向part2发送心跳,心跳中携带已经open文件的信息。

    part2

    • File Service:接受并处理part1发送过来的文件请求。
    • Heartbeat Service:接受并处理part1发送过来的心跳请求。
    • File Manager:负责part2上open文件的管理。
    • IO Executor:请求具体的执行层,调用curve-client的接口将请求交给后端存储集群处理。
    • Metafile Manager:负责元数据文件的管理,将元数据持久化到文件,或从文件中读取持久化信息。

    关键点

    1. part1重试逻辑

      前面说到,part1只进行了有限的重试逻辑,具体体现在两方面:

      1. RPC请求不设置超时时间
      2. 只针对RPC请求本身的错误进行重试,RPC Response中返回的错误码直接返回给上层

      以Write请求为例,其他请求也类似:

      • 将上层Write请求通过RPC转发给part2,并不设置超时时间,进入等待
        • 如果RPC返回成功,则根据RPC Response成功与否,向上层返回。
        • 如果连接断开或无法连接,则等待一段时间后重试

      write

    2. 心跳管理

      为了避免上层业务退出时没有close文件,part2会定期检查文件的心跳状态(part1会通过心跳,定期上报以open文件的信息),如果上次心跳时间已经超时,则会主动close文件。

      心跳超时close文件与上层业务主动close文件的区别在于,前者不会把文件信息从metafile中删除。

      但是当上层业务出现假死,后续又恢复的情况下,也有可能导致心跳超时,造成文件被close。

      所以,part2在收到part1请求时,会先检测metafile是否有当前文件记录,如果存在,且处于closed状态,则会先主动open对应的文件,然后执行后续请求。

    - - + + \ No newline at end of file diff --git a/CurveBS/architecture/snapshot.html b/CurveBS/architecture/snapshot.html index b9b3dfa..4dc6cb2 100644 --- a/CurveBS/architecture/snapshot.html +++ b/CurveBS/architecture/snapshot.html @@ -4,13 +4,13 @@ CurveBS 快照克隆架构设计 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/comparison/CurveBS-vs-Ceph-rbd.html b/CurveBS/comparison/CurveBS-vs-Ceph-rbd.html index 18202a3..d70f8d9 100644 --- a/CurveBS/comparison/CurveBS-vs-Ceph-rbd.html +++ b/CurveBS/comparison/CurveBS-vs-Ceph-rbd.html @@ -4,8 +4,8 @@ CurveBS对比Ceph RBD | Curve Book - - + +
    @@ -16,7 +16,7 @@ 单卷场景

    多卷场景: 多卷场景

  • 更稳定

  • 易运维

  • 云原生

  • TBD

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/csi.html b/CurveBS/deploy/csi.html index ad5d056..f29e16a 100644 --- a/CurveBS/deploy/csi.html +++ b/CurveBS/deploy/csi.html @@ -4,14 +4,14 @@ CurveBS CSI 驱动程序 | Curve Book - - + +
    跳到主要内容

    CurveBS CSI 驱动程序

    简介

    该插件实现了 Container Orchestrator(CO) 和 Curve 集群之间的容器存储接口(CSI),从而支持动态地配置 Curve 卷并将其附加到工作负载容器。

    Curve 项目信息请参考: https://docs.opencurve.io

    先决条件

    该驱动程序目前使用csi规范v1.5.0开发,并支持kubernetes v1.17+。

    其他启用 csi-v1.0+ 的容器编排器环境可能会正常工作。

    CSI 规范和 Kubernetes 版本兼容性

    请参考兼容性矩阵

    开发者

    请参阅 csi spec 以及 curve interface 了解更多信息。

    配置

    本CSI驱动支持原生k8s部署及helm部署两种方式:

    1. kubernetes直接部署: refer to deploy doc
    2. helm chart部署: helm installation

    测试及用户手册

    请参阅 doc

    您可以通过CSC工具获取更多详细信息并测试驱动程序。

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/dashboard.html b/CurveBS/deploy/dashboard.html index e984cf4..516b918 100644 --- a/CurveBS/deploy/dashboard.html +++ b/CurveBS/deploy/dashboard.html @@ -4,13 +4,13 @@ CurveBS 控制台部署 | Curve Book - - + +
    跳到主要内容

    CurveBS 控制台部署

    背景

    Curve 控制台是 Curve 为了提升系统易用性而开发的一个相对独立的 Web 服务,提供基于 Web 的集群管理能力,使得存储集群的部署和管理门槛进相对 CLI 方式一步降低,提供一种更加清晰直观的视图。上篇文章《Curve 易用性升级之可视化运维——web 控制台介绍》中我们已经介绍了系统架构和核心特性,控制台项目已经开源,为了帮助大家快速体验该功能,这篇文章将介绍控制台项目的编译打包部署使用,同时,我们也欢迎大家参与控制台项目的开发和维护工作,希望通过共同努力,不断改进 Curve 控制台,为用户提供更好的使用体验。

    项目编译&打包

    目前 Curve 控制台代码仓库前后端分开管理:前端仓库后端仓库,项目的编译打包是通过将前端项目作为后端项目的submodule来关联。

    # clone code
    git clone --recursive git@github.com:opencurve/curve-manager.git

    # go version >= v1.19.0
    # build
    make build

    # make image
    make image tag=<DOCKERHUB_USERNAME>/<REPOSITORY_NAME>:<TAG>
    docker push <DOCKERHUB_USERNAME>/<REPOSITORY_NAME>:<TAG>

    项目部署

    环境准备

    部署控制台的机器需要安装Docker,并确保 Docker Daemon 已经运行,你可以在服务器上执行以下命令来检测:

    sudo docker run --rm hello-world

    准备配置文件

    vim website.yaml

    kind: curvebs      # 集群类型,curvebs 或 curvefs (目前仅支持curvebs)
    host: server-host # 部署控制台服务的机器,需是在 curveadm 中导入的主机
    config:
    container_image: opencurvedocker/curve-manager:latest # curve控制台的镜像
    data_dir: /tmp/curve-manager/data # 控制台的数据目录
    log_dir: /tmp/curve-manager/logs # 控制台的日志目录
    listen_port: 443 # 控制台服务的监听端口
    access.api.enable_check: true # 是否开启接口认证,开启则会检测每个请求携带的认证信息
    access.api.expire_seconds: 60 # 接口认证token过期时间
    access.login.expire_seconds: 1800 # 用户登录失效时间
    enable.multiple.write.user.login: false # 是否允许多个具有写权限的用户同时登陆
    system.log.expiration.days: 30 # 审计日志过期时间,过期内容自动删除
    system.alert.expiration.days: 30 # 系统告警信息过期时间,过期内容自动删除
    curveadm.service.address: 127.0.0.1:11000 # curveadm http service地址,对应部署步骤3 "启动curveadm http service"中配置的值
    email.addr: example@163.com # 管理邮箱,用于重置密码,发送告警信息
    email.auth: password or authCode # 管理邮箱授权码或密码

    部署控制台

    Curve 控制台的部署依赖部署工具CurveAdm,需提前安装该工具,目前 CurveAdm release 版本并不支持该功能,可升级到dev版本进行体验(升级前可先备份下当前curveadm(备份curveadm二进制即可),首次安装完也需要进行升级): CURVEADM_VERSION=curve-dashboard curveadm -u

    注意:由于控制台功能依赖 CurveBS 新增部分接口,目前支持的 CurveBS 存储集群版本为v1.2.7,体验时可选择已有镜像:quay.io/opencurve/curve/curvebs:v1.2.7-beta2_872d38c 或自编译镜像。

    Curve 控制台的完整依赖部署路径:

    1. 部署curvebs集群
    2. 部署监控
    3. 启动curveadm http service
    4. 部署控制台
    # 查看控制台相关命令
    curveadm website -h

    # 部署控制台
    curveadm website deploy -c website.yaml

    # 查看控制台服务状态
    curveadm website status

    # 停止控制台服务
    curveadm website stop

    # 启动控制台服务
    curveadm website start

    # 清理控制台服务
    curveadm website clean

    部署成功后,即可以访问控制台服务(https://ip:port ip为部署控制台服务机器的ip,port为配置文件中配置的监听端口)来管理 Curve 集群。

    使用控制台

    登录 & 忘记密码

    系统默认包含一个管理员用户,用户名:admin,密码:curve,登录后可选择进行密码修改和新增用户。如果忘记密码,可通过忘记密码进行重置,前提是部署时在website.yaml中配置了管理邮箱信息:

    email.addr: example@163.com                # 管理邮箱,用于重置密码,发送告警信息
    email.auth: password or authCode # 管理邮箱授权码或密码

    0717-forget-password.png

    用户管理

    admin用户可以对系统用户进行管理,包括创建,权限修改,删除。每个用户可以修改自己的个人信息,目前主要包括邮箱和密码。

    0717-user-management.png

    集群概览

    集群概览主要展示存储集群各服务的状态信息,集群存储容量和总体性能指标。

    0717-overview.png

    集群管理

    集群管理主要包括集群拓扑、服务器管理和磁盘管理,拓扑主要展示了存储集群节点的拓扑结构信息,服务器管理会展示存储集群的服务器详情和性能指标等信息,磁盘管理展示了存储节点上磁盘的基本信息、挂载状态和容量情况。

    0717-cluster-management.png

    存储池管理

    存储池管理主要以存储池为粒度展示服务和资源信息及该池子的整体性能指标。

    0717-pool-management.png

    块存储管理

    主要包括对卷和快照的管理,在这里可以针对卷做创建、限流、扩容、打快照、克隆、删除和恢复等操作。快照管理可以取消正在进行的快照任务,可删除快照。

    0717-volume-management.png

    0717-snapshot-management.png

    集群部署

    目前采用基于配置的集群部署方式,需要用户补充必要的配置信息,最终该请求会转发给 CurveAdm 执行并返回结果。

    0717-cluster-deploy.png

    系统操作日志

    审计日志记录用户在系统中的操作过程,便于对安全事件的发现和追踪溯源。admin用户可查看所以用户的操作记录,普通用户只能查看自己的操作记录。

    0717-syslog.png

    告警配置

    告警配置是针对存储集群配置监控检测信息,系统内置了三类六种告警规则,包括针对服务(etcd、mds、chunkserver、snapshotclone)、容量和集群状态的监控,用户可在此进行修改,也可设置告警联系人,当系统出现异常触发告警时,告警联系人会收到系统发生的告警邮件。

    0717-alert-conf.png

    告警信息

    这里会展示出触发告警规则的告警信息,便于浏览和回溯。

    0717-alert-info.png

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/deploy-with-curveadm.html b/CurveBS/deploy/deploy-with-curveadm.html index 24e8b91..9fc5a57 100644 --- a/CurveBS/deploy/deploy-with-curveadm.html +++ b/CurveBS/deploy/deploy-with-curveadm.html @@ -4,13 +4,13 @@ 物理机部署 CurveBS 集群 | Curve Book - - + +
    跳到主要内容

    物理机部署 CurveBS 集群

    为了提升 Curve 的运维便利性,我们设计开发了 CurveAdm 项目,其主要用于部署和管理 Curve 集群,目前已支持部署CurveBS & CurveFS,相关使用文档请参考 CurveAdm用户手册,并根据手册首先安装CurveAdm工具之后再进行Curve集群的部署。

    部署 CurveBS 集群

    使用 CurveAdm 部署 CurveFS 集群过程中如遇到错误,会在终端打印对应的错误码和解决方案链接,如参考解决方案链接的指导后仍然无法解决错误问题,可以添加终端打印的微信号来与我们取得进一步联系。

    集群部署过程当前主要分为以下几个主要步骤:

    1. 准备节点列表

    详细步骤请参阅文档:主机管理

    2. 准备集群拓扑文件

    详细步骤请参阅文档:集群拓扑

    3. 部署集群

    部署集群命令执行过程中,CurveAdm工具会自动执行部署环境预检工作,以确保所有节点均满足部署条件,只要当一切检测正常才会真正开始部署集群,关于预检的介绍请参阅文档:环境预检

    关于集群部署的详细步骤请参阅文档:部署集群

    4. 部署客户端

    详细步骤请参阅文档:部署客户端

    命令行工具

    • CurveFS 提供了命令行工具以查看集群状态和进行基本集群操作:命令行工具说明
    • 对于集群管理员,可以参阅管理员指导文档来完成对集群的运维管理工作:运维工具说明
    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/deploy-with-k8s-operator.html b/CurveBS/deploy/deploy-with-k8s-operator.html index 9e8705d..3d5efc5 100644 --- a/CurveBS/deploy/deploy-with-k8s-operator.html +++ b/CurveBS/deploy/deploy-with-k8s-operator.html @@ -4,13 +4,13 @@ Kubernetes 上部署 CurveFS 集群 | Curve Book - - + +
    跳到主要内容

    Kubernetes 上部署 CurveFS 集群

    curve-operator代码仓库:https://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md

    简介

    Curve-Operator用于管理 Kubernetes 系统上的 Curve 集群。更进一步地支持Curve实现云原生的分布式存储系统。

    Curve BS deploy architecture

    部署

    0. 先决条件

    • 安装Kubernetes 1.19/1.20版本。

    1. 安装Operator

    首先需要安装Curve Operaotr,然后再部署 Curve 集群。

    $ git clone https://github.com/opencurve/curve-operator.git
    $ cd curve-operator
    $ kubectl apply -f config/deploy/

    安装成功后,默认会创建curve namespace,确认curve-operator处于Running状态。

    $ kubectl get pod -n curve
    NAME READY STATUS RESTARTS AGE
    curve-operator-69bc69c75d-jfsjg 1/1 Running 0 7s

    2. 部署Curve集群

    Operator部署集群基于声明式的API,你可以在这个目录 config/sample 下找到所有的声明式yaml例子。并且可以自定义修改从而能够符合你当前的部署环境。

    CurveBS 单机部署

    CurveBS 三副本部署

    CurveFS 单机部署

    CurveFS 三副本部署

    这里我们以部署三副本的 CurveBS 集群为例进行说明。这里的声明文件是cluster.yaml。你可以根据yaml文件中的注释详细的了解每一个配置项的作用,从而自定义修改。

    输入如下命令创建集群:

    $ kubectl apply -f config/samples/cluster.yaml

    查看当前 curve namespac 下的所有的pod:

    $ kubectl -n curve get pod

    NAME READY STATUS RESTARTS AGE
    curve-chunkserver-curve-operator-node1-vdc-556fc99467-5nx9q 1/1 Running 0 5m45s
    curve-chunkserver-curve-operator-node2-vdc-7cf89768f9-hmcrs 1/1 Running 0 5m45s
    curve-chunkserver-curve-operator-node3-vdc-f77dd85dc-z5bws 1/1 Running 0 5m45s
    curve-etcd-a-d5bbfb755-lzgrm 1/1 Running 0 41m
    curve-etcd-b-66c5b54f75-6nnnt 1/1 Running 0 41m
    curve-etcd-c-86b7964f87-cj8zk 1/1 Running 0 41m
    curve-mds-a-7b5989bddd-ln2sm 1/1 Running 0 40m
    curve-mds-b-56d8f58645-gv6pd 1/1 Running 0 40m
    curve-mds-c-997c7fd-vt5hw 1/1 Running 0 40m
    gen-logical-pool-rzhlz 0/1 Completed 0 5m15s
    gen-physical-pool-chnw8 0/1 Completed 0 5m45s
    prepare-chunkfile-curve-operator-node1-vdc-znb66 0/1 Completed 0 40m
    prepare-chunkfile-curve-operator-node2-vdc-6gf2z 0/1 Completed 0 40m
    prepare-chunkfile-curve-operator-node3-vdc-2bkxm 0/1 Completed 0 40m
    read-config-k272k 0/1 Completed 0 41m

    说明:在执行完apply命令之后,通过get命令你可以不能立刻就能看到chunkserver pods。因为磁盘需要进行格式化,这个格式化的过程是通过prepare-chunkfile jobs去完成的。所以可能会等待一段时间之后才会看到所有的chunkserver pods。

    具体的等待时间需要根据你的磁盘的大小以及你定义的格式化的百分比,这个时间可能会很长。你可以通过日志查看格式化的进程。

    3. 检查集群的健康状态

    为了检查部署的集群是否是健康的,需要进入任何一个curve-chunkserver pod,然后使用curve_ops_tool status 去查看集群的健康状态。

    $ kubectl exec -it <any one chunkserver pod> -- bash
    $ curve_ops_tool status

    Cluster status:
    cluster is healthy
    total copysets: 100, unhealthy copysets: 0, unhealthy_ratio: 0%
    physical pool number: 1, logical pool number: 1
    Space info:
    physical: total = 1178GB, used = 6GB(0.56%), left = 1171GB(99.44%)
    logical: total = 392GB, used = 41GB(10.44%, can be recycled = 0GB(0.00%)), left = 351GB(89.56%), created file size = 60GB(15.28%)

    Client status:
    nebd-server: version-1.2.5+2c4861ca: 1
    ...

    Curve CSI

    在Kubernetes系统中,需要创建PVC从而使用 Curve 作为pod的后端存储。

    你可以部署对接 Curvebs 集群的 curve-csi 或者是对接 Curvefs 集群的 curvefs-csi。如何部署以及具体的实现细节可以参考对应的项目文档。

    删除

    为了删除已经部署的Curve集群并且清楚其中的数据,可能需要经历如下步骤。

    1. 删除集群CR

    $ kubectl -n curve delete curvecluster my-cluster

    为了验证cluster CR已经被删除,你可以通过如下命令查看:

    $ kubectl -n curve get curvecluster

    2. 删除Operator以及相关的资源

    $ kubectl delete -f config/deploy/

    3. 删除持久化在宿主机的数据和日志(慎重)

    为了彻底的清除集群,需要把集群中的数据和日志全部清除。这个目录是在cluster.yaml文件中定义的hostDataDir配置项。注意,在部署新集群之前一定要将这个配置目录下的内容删除。

    对于多副本部分的集群,数据分布在各个集群节点上,所以需要登录各个节点进行删除。例如,如果配置的目录是/curvebs的话,则需要删除这个目录下的所有数据和日志:

    rm -rf /curvebs
    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/iscsi.html b/CurveBS/deploy/iscsi.html index 7e60005..ab73ba1 100644 --- a/CurveBS/deploy/iscsi.html +++ b/CurveBS/deploy/iscsi.html @@ -4,13 +4,13 @@ iSCSI 协议支持 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/offline-deploy.html b/CurveBS/deploy/offline-deploy.html index 9a9af7a..56cab6b 100644 --- a/CurveBS/deploy/offline-deploy.html +++ b/CurveBS/deploy/offline-deploy.html @@ -4,15 +4,15 @@ 离线部署 CurveFS 集群 | Curve Book - - + +
    跳到主要内容

    离线部署 CurveFS 集群

    本文适用于无法从公网docker镜像仓库拉取CurveFS docker镜像场景下的部署。

    离线环境准备

    Curve镜像准备

    1. 拉取镜像 Curve官方镜像(如opencurvedocker/curvebs:v1.2)到本地环境(可访问外网的机器)
    $ sudo docker pull opencurvedocker/curvebs:v1.2
    1. 导出镜像
    # 查看下载到的Curve镜像,镜像版本、id和大小为示例,具体以实际为准,下同
    $ sudo docker image ls
    opencurvedocker/curvebs v1.2 5717f16d4bec 1 months ago 1.84GB

    # 导出镜像
    $ sudo docker save -o curve_v1.2.tar 5717f16d4bec
    1. 拷贝镜像到本地仓库节点
    $ scp curve_v1.2.tar  ${desthost}:/path/to/save/image
    1. 导入镜像
    $ docker load --input curve_v1.2.tar

    # 查看导入的镜像
    $ sudo docker image ls
    opencurvedocker/curvebs v1.2 5717f16d4bec 1 months ago 1.84GB

    本地镜像仓库搭建

    使用docker-registry来进行本地仓库的搭建,搭建完本地仓库后, 把前面步骤中下载的Curve镜像上传到本地仓库。主要有以下几步:

    1. 运行 docker-registry
    $ docker run -d -p 5000:5000 --restart=always --name registry registry
    1. 标记Curve镜像 标记下载到的Curve镜像,比如把下载来的镜像(opencurvedocker/curvebs:v1.2)标记为127.0.0.1:5000/curvebs:v1.2_local(其中127.0.0.1为本地仓库服务IP,5000为本地仓库服务端口号,请根据实际环境修改)
    # 查看下载到的Curve镜像
    $ sudo docker image ls
    opencurvedocker/curvebs v1.2 5717f16d4bec 1 months ago 1.84GB

    # 标记镜像
    $ sudo docker tag opencurvedocker/curvebs:v1.2 127.0.0.1:5000/curvebs:v1.2_local

    # 查看标记完的镜像
    $ sudo docker image ls
    127.0.0.1:5000/curvebs v1.2_local 5717f16d4bec 13 months ago 1.84GB
    1. 上传镜像
    $ docker push 127.0.0.1:5000/curvebs:v1.2_local

    更多详情可参考私有仓库搭建

    修改镜像地址

    修改客户端部署配置文件client.yaml以及服务端集群部署配置文件topology.yaml中的镜像地址配置项(container_image)为本地仓库镜像地址(如:127.0.0.1:5000/curvebs:v1.2_local

    部署

    安装CurveAdm

    CurveAdm是Curve部署工具,有外网的机器可以一键安装,具体安装参见CurveAdm安装

    但由于本文是介绍内网环境的部署,所以需按如下步骤操作:

    • 下载CurveAdm到本地可访问外网机器,下载地址(最新版本可咨询Curve社区成员): CurveAdm
    • 把CurveAdm拷贝到内网安装需部署Curve集群的主控机
    • 解压CurveAdm
    • 拷贝执行程序并设置环境变量
    $ mv curveadm ~/.curveadm

    # 可考虑更新到~/.bash_profile进行持久化
    $ export PATH=~/.curveadm/bin:$PATH

    主机配置

    配置Curve集群要使用的服务器列表,提交列表给CurveAdm管理。主机配置过程比较简单,在hosts.yaml中添加实际主机名和ip,然后提交就可以了。

    具体配置参考文档:主机管理

    Curve服务端部署

    需修改topology.yaml中的镜像为本地镜像地址,示例如下:

    kind: curvebs
    global:
    container_image: 127.0.0.1:5000/curvebs:v1.2_local ## 修改为本地镜像

    其他的配置项请参考文档 CurveFS集群部署CurveBS集群部署

    client端部署

    需修改client.yaml中的镜像为本地镜像地址,示例如下:

    kind: curvebs
    global:
    container_image: 127.0.0.1:5000/curvebs:v1.2_local ## 修改为本地镜像

    其他的配置项请参考文档 部署CurveFS客户端部署CurveBS客户端

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/openstack.html b/CurveBS/deploy/openstack.html index c1ac07a..276648e 100644 --- a/CurveBS/deploy/openstack.html +++ b/CurveBS/deploy/openstack.html @@ -4,13 +4,13 @@ OpenStack 对接 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/quickstart.html b/CurveBS/deploy/quickstart.html index 6b8c2f0..4f1dcdb 100644 --- a/CurveBS/deploy/quickstart.html +++ b/CurveBS/deploy/quickstart.html @@ -4,13 +4,13 @@ CurveBS 快速体验 | Curve Book - - + +
    跳到主要内容

    CurveBS 快速体验

    为了提升 Curve 的运维便利性,我们设计开发了 CurveAdm 项目,其主要用于部署和管理 Curve 集群,目前已支持部署CurveBS & CurveFS,相关使用文档请参考 CurveAdm用户手册,并根据手册首先安装CurveAdm工具之后再进行Curve集群的部署。

    部署All-in-one体验环境

    请参考CurveAdm用户手册中CurveBS集群部署步骤,单机体验环境请使用“集群拓扑文件-单机部署”模板。

    命令行工具

    curve 提供了命令行工具以查看集群状态和进行基本集群操作:命令行工具说明

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/rbd.html b/CurveBS/deploy/rbd.html index 5cc01d1..5a2e4bd 100644 --- a/CurveBS/deploy/rbd.html +++ b/CurveBS/deploy/rbd.html @@ -4,14 +4,14 @@ OpenStack中CurveBS的Ceph RBD兼容层实现 | Curve Book - - + +
    跳到主要内容

    OpenStack中CurveBS的Ceph RBD兼容层实现

    背景与目标

    OpenStack是目前广泛使用的资源管理平台,向用户提供包括镜像、云主机、云硬盘等在内的资源,而Ceph RBD是最为流行的OpenStack块存储后端之一,提供了可伸缩的存储能力。

    CurveBS(后称CBD)是Curve社区推出的高性能块存储软件。OpenStack对于存储的访问是通过各个存储的驱动实现。CBD如果想要对接OpenStack, 那么需要维护对应的CBD存储驱动,改动包括glance, nova, cinder, libvirt和qemu。目前改动暂时并没有推到对应上游的计划,如果用户需要使用,需要自行修改代码并且编译上述的所有软件。

    除了这一种做法,我们可以通过使用CBD的接口来模拟OpenStack中使用的部分RBD接口,来直接使用RBD驱动来调用CBD,这也就是Ceph RBD的兼容层,用户只需要替换RBD对应的librados和librbd动态库以及python3-rbd和python3-rados的实现就可以进行体验。如果后续有长期使用的需求,再将这种使用方式替换为CBD专属驱动。

    RBD和CBD的概念映射

    在配置使用Ceph RBD时,我们往往需要指定几个配置,包括集群配置,逻辑池等。在CBD中,我们需要找到一种映射关系来正确合理地处理他们。

    以下是概念的映射表:

    rbd   -> cbd
    pool -> dir
    user -> owner
    vol -> vol

    其中需要注意的是,CBD中目前没有类似ceph中pool的概念可以配置,但是可以创建dir,用以模拟RBD的pool;CBD在访问卷的时候,需要指定volume的owner,我们这里不传入passwd(sdk不支持),用以对应RBD的user概念。

    兼容层实现

    这里主要分为两块,一块是基于python开发的OpenStack的管控服务,一块是机器虚拟化的基石服务。

    镜像/虚拟主机/虚拟硬盘的管理 Glance/Nova/Cinder

    OpenStack组件使用python开发,对接了python3-rbd和python3-rados,来连接后端ceph集群。

    涉及到rbd驱动的OpenStack主要的核心操作如下:

    • 镜像(Glance):
      • 上传镜像并打快照并设置保护flag
      • 下载镜像
    • 虚拟硬盘(Cinder):
      • 创建(新卷或克隆卷)
      • 删除
      • 调整大小
      • 打快照/克隆
    • 虚拟主机(Nova):
      • 创建(使用cinder卷或是自行创建系统卷,一般来源是镜像快照或硬盘快照)
      • 删除
      • 挂载卷

    兼容层工作

    我们需要做的就是提供一个新的python3 module(rados、rbd)实现来替换现有的。

    我们将用到CBD的python sdk库 https://github.com/opencurve/curve/tree/master/curvefs_python 来进行模拟,文档在 https://github.com/opencurve/curve/blob/master/docs/cn/curve-client-python-api.md

    这里将开发中遇到的问题进行了记录,给后来人避坑。

    1. 在python多进程(涉及到fork)的环境中,如果在父进程已经进行了CBDclient的初始化,由于fork只会拷贝调用线程,而CBDclient初始化时会创建rpc工作线程池,导致子进程异常,无法正常发送rpc和后端通信。这个问题主要出现在Nova中,nova会fork出N个worker,可以通过延迟初始化来绕过。
    2. CBD的python模块的read/write相关接口使用的是str来接受bytes数据。在python2中byte和str的区分还不是很严格不会出问题,但是python3中str的使用是必须需要经过编解码的,接口使用str会导致数据的错误。
    3. CBD现在的快照机制是将源卷的数据转储到S3兼容的对象存储,如果写入的数据很多,将耗时非常久。快照完成后,源卷和快照解耦。快照克隆时,如果不开启COW,将会需要将对象存储的数据再拷贝一次到CBD中,如果启用COW,那么后续IO有可能会需要走对象存储的IO栈,可能会很慢。Glance在镜像上传完成后会自动打一次快照,后续虚拟机的创建也受到这一问题的影响。
    4. 由于CBD的python sdk依赖CBD的共享库,在各个平台需要单独编译和打包,目前并没有完善的打包,可能需要自己编译处理
    5. CBD逻辑卷大小最小为10G,如果创建一个1G的卷,实际在CBD调用size()获得的仍是10G
    6. 除了直接调用python库,可能会需要调用"ceph df", "ceph mon dump", "rbd status", "rbd import" 等命令,需要hook实现
    7. 目前不支持上传COW镜像

    虚拟机的运行 LibVirt/Qemu

    宿主机虚拟机的管理和虚拟化靠libvirt和qemu,libvirt接收来自nova生成的xml,生成对应的qemu命令运行虚拟机。rbd驱动下,libvirt和qemu使用了ceph提供的librados.so和librbd.so共享库来对接后端的IO。

    兼容层工作

    我们需要做的是提供一个新的动态库来模拟librbd和librados的接口,CBD侧我们将会使用libnebd-client来和通过nebd和CBD后端进行IO交互。nebd是一个用于解决client端热升级的组件,server端和CBD后端进行交互,client端和server端通信,升级时只需要升级重启nebd-server即可,不用重启client(在这里就是qemu)

    这里将开发中遇到的问题进行了记录,给后来人避坑。

    1. libnebd-client库的回调不支持传args,无法和rbd的接口兼容。通过修改libnebd-client来适配。
    2. 由于libnebd-client中链接了brpc,对pthread造成了影响,需要在libvirtd的env中设置preload,不然会导致symbol找不到的问题。https://github.com/apache/brpc/issues/1086
    3. 虚拟机中lsblk看到的硬盘大小是调用的后端size接口,由于前面提到的CBD逻辑卷大小最小为10G,小硬盘的显示可能不正确
    4. qemu的rbd driver没有限制io对齐,允许任意大小的io,但是CBD要求IO必须4096/4K字节对齐,这个需要CBD进行后端支持,或者修改qemu的IO限制,默认应该是512字节。

    简易操作指南

    操作系统:debian11, qemu 5.2, libvirt 7.0

    下面所需的文件都可以在这里下载: https://github.com/h0hmj/curve/releases/download/cbd2rbd-beta-v1/openstack-cbd-mod.tar.gz

    1. 部署一个curvebs后端集群
    2. 计算节点上安装qemu和libvirt以及rbd的支持(qemu-block-extra, libvirt-daemon-driver-storage-rbd), 修改qemu的执行用户为root,关闭apparmor
    3. 计算节点上安装nebd软件包,和部署nebd, 用于支持连接后端的curve集群
    4. 替换librados2和librbd1包中的对应librados.solibrbd.so为我们魔改的so(用cbd模拟了rbd的接口,代码https://github.com/h0hmj/curve/commit/d1df07c86c38b5d0d16a025cb560977d3bfc568f)还有 libnebdclientmod.so
    5. 修改/etc/default/libvirtd,添加preload参数LD_PRELOAD=/usr/lib/nebd/libnebdclientmod.so
    6. 使用kolla部署openstack, base镜像选用debian, 为glance、nova和cinder启用rbd支持,为了简单,使用user为admin,pool为curve对应的文件夹名称,如果需要从glance卷clone来创建vm,打开glance的选项show_image_direct_url = True 。在curve后端创建对应的文件夹。
    7. 修改openstack nova-compute配置使用本机的libvirt
    8. 为glance-api/nova-compute/cinder-volume的容器
      • apt install libunwind8
      • 添加/etc/curve/client.conf
      • 安装curvefs的wheel包
      • 删除自带的python-rbd/rados的so实现,替换为我们的魔改版
      • 替换/usr/bin/ceph和/usr/bin/rbd为我们的魔改版本
    9. 重启所有相关服务
    10. 后续可以通过horizon的web界面进行试用,镜像的创建删除,云主机的创建删除,云硬盘的创建删除等

    总结

    我们探索了一种可能的rbd兼容方式,即提供hook了对应接口的共享库,以及适配当中遇到的问题。这会导致失去原有的ceph rbd的支持,如果有需求,建议仍然使用单独的cbd驱动。

    - - + + \ No newline at end of file diff --git a/CurveBS/deploy/rdma-spdk.html b/CurveBS/deploy/rdma-spdk.html index 03b5964..f60a578 100644 --- a/CurveBS/deploy/rdma-spdk.html +++ b/CurveBS/deploy/rdma-spdk.html @@ -4,13 +4,13 @@ CurveBS RDMA+SPDK 版本部署 | Curve Book - - + +
    跳到主要内容

    CurveBS RDMA+SPDK 版本部署

    系统环境

    至少 3 台机器作为存储节点,相关配置参考如下

    • CentOS 7.9
    • Linux 5.4.247-1.el7.elrepo.x86_64 x86_64
    • Mellanox CX4/CX5 网卡,交换机需要支持设置 ECN
    • NVMe 盘(每个盘至少占用 2 个 CPU)

    至少 1 台机器作为客户端节点,相关配置参考如下

    • CentOS 7.9
    • Linux 5.4.247-1.el7.elrepo.x86_64 x86_64
    • Mellanox CX4/CX5 网卡,交换机需要支持设置 ECN

    TIPS:

    • Debian 11 环境同样支持,MLNX-OFED 驱动安装参考 Installation
    • 如果没有单独的客户端节点,可以复用存储节点,但对性能测试可能略有影响

    配置

    开启 IOMMU

    修改 /etc/default/grub,在 GRUB_COMMAND_LINE 最后加入 intel_iommu=on,然后执行 grub2-mkconfig -o /boot/grub2/grub.cfg 并重启机器。

    调整 memlock 限制

    SPDK 和 UCX 都会依赖 memlcok,需要进行调整,在 /etc/security/limits.conf 文件中添加如下内容

    *     hard   memlock           unlimited
    * soft memlock unlimited

    安装 OFED 驱动

    1. OFED 驱动需要编译安装,默认 GCC4.8.5 会出现编译报错,通过 devtoolset 安装 GCC10

      $ yum install centos-release-scl -y
      $ yum install devtoolset-10 -y
      $ scl enable devtoolset-10 bash
    2. 安装依赖包

      $ yum install libibverbs-utils perftest createrepo tcl gcc-gfortran fuse-libs tk
      $ yum --enablerepo=elrepo-kernel install -y kernel-lt-devel.x86_64 # kernel-devel的版本要跟实际的内核版本保持一致,如果已经安装可以省略(该命令跟内核版本有关,实际命令可能有所不同)
    3. 安装 OFED 驱动

      1. 下载包 https://www.mellanox.com/page/mlnx_ofed_eula?mtag=linux_sw_drivers&mrequest=downloads&mtype=ofed&mver=MLNX_OFED-5.8-2.0.3.0&mname=MLNX_OFED_LINUX-5.8-2.0.3.0-rhel7.9-x86_64.tgz

      2. 解压后执行 ./mlnxofedinstall --add-kernel-support

        这个过程会下载一些包和依赖,并且编译安装,中途可能会报错,根据报错安装相应的包。

      3. 完成后执行如下命令

        $ dract -f
        $ /etc/init.d/openibd restart
        $ reboot # 重启机器
      4. 验证网络

        1. IB 测试

          服务端命令 ib_write_bw -F -d mlx5_bond_0 -q 1 --report_gbits -D 60 --cpu_util

          客户端命令 ib_write_bw -F -d mlx5_bond_0 -q 1 --report_gbits -D 60 --cpu_util <Server IP Address>

          命令中 -d 后面的参数需要替换为 ibv_devinfo 命令输出中的 hca_id

          测试输出结果示例(服务端):

          ib-server

          测试输出结果示例(客户端):

          ib-client

        2. 执行 ucx_info -d | grep "Trans",输出中需要包含 rc_mlx5/ud_mlx5 或者 rc_verbs/uc_verbs

        3. 保存如下配置到 ${HOME}/ucx.conf

          [global]
          UCX_TLS=ib
          UCX_USE_MT_MUTEX=y
          UCX_RNDV_THRESH=32K
          UCX_TCP_CM_REUSEADDR=y
          UCX_RDMA_CM_REUSEADDR=y
          UCX_ASYNC_MAX_EVENTS=10000
          UCX_RC_VERBS_MAX_NUM_EPS=10000
          UCX_RC_MLX5_MAX_NUM_EPS=10000
          UCX_RCACHE_STAT_MAX=10M

          UCX_NET_DEVICES=mlx5_bond_0:1
          #UCX_IB_GID_INDEX=3
          UCX_RC_MLX5_TRAFFIC_CLASS=96
          UCX_RC_VERBS_TRAFFIC_CLASS=96

          UCX_RC_VERBS_TX_QUEUE_LEN=512
          UCX_RC_MLX5_TX_QUEUE_LEN=512
          UCX_TCP_MAX_IOV=128
          UCX_LOG_LEVEL=WARN
          #UCX_MAX_RNDV_RAILS=2
          #UCX_MAX_EAGER_RAILS=2

          其中,UCX_NET_DEVICES 还是需要用 ibv_devinfo 的输出替换,分别为 hca_idport

        4. ucx_perftest 测试

          服务端命令 ucx_perftest -c 0

          客户端命令 ucx_perftest <Server IP Address> -t ucp_am_bw -c 0 -s 65536

          测试输出结果示例(服务端):

          ucx-perf-server

          测试输出结果示例(客户端):

          ucx-perf-client

    部署 Curve

    参考 CurveAdm 部署文档 服务端 客户端请提前熟悉相应的操作和流程,特殊步骤或配置在下面备注。

    CurveAdm 版本

    更新到指定版本 CURVEADM_VERSION=vrdma-rc2 curveadm -u

    镜像

    opencurvedocker/curvebs_rdma_spdk:1.2-netease,格式化和部署,都使用这个镜像

    TIPS: 由于镜像较大,需提前在存储节点和客户端节点上把镜像拉下来 docker pull opencurvedocker/curvebs_rdma_spdk:1.2-netease

    格式化磁盘

    由于 SPDK 版本的限制,目前格式化比例最高只能设置到 75%(第一次部署可以先格式化 10% 或者 20%,确认是否能正常部署集群)

    格式化命令:curveadm format --spdk -f format.yaml ,注意其中的 —-spdk 参数

    TIPS:

    • SPDK 格式化的过程会绑定 NVMe 到 VFIO 驱动,导致 lsblk 无法看到 NVMe 盘
    • SPDK 格式化暂时不支持查看进度,需要到存储节点上查看格式化进程是否退出 ps aux | grep format(或格式化的容器是否退出)

    集群拓扑文件

    在 chunkserver services 中添加如下字段

    chunkserver_services:
    config:
    listen.ucp_port: 92${format_replicas_sequence} # 9200,9201,9202
    use_ucp: true
    use_spdk: true

    部署集群

    1. 把配置过程中使用到的 ucx.conf 文件复制到当前目录(执行 curveadm 命令的目录)

    2. curveadm deploy -k,如果不需要快照克隆服务可以加上 --skip snapshotclone

    查看集群状态

    进入其中一个 MDS 容器,curveadm enter ${Id},然后执行 curve_ops_tool copysets-status

    如下输出表示正常

    Copysets are healthy!
    total copysets: 400, unhealthy copysets: 0, unhealthy_ratio: 0%

    TIPS: 如果没有部署快照克隆服务,命令有可能会报错,需要修改容器内的 /etc/curve/tools.conf 配置文件

    修改如下两个配置项:

    snapshotCloneAddr=127.0.0.1:5555

    snapshotCloneDummyPort=5556

    挂载 NBD 盘

    参考客户端部署文档,同样需要把 ucx.conf 放到当前目录

    挂载成功后,登录到客户端机器上,lsblk 可以看到对应的 nbd 设备,可以执行 fio 测试,参考命令

    fio -name=/dev/nbd0 -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=10G -numjobs=1 -time_based -runtime=120

    其他

    性能测试

    • 至少需要 100G 的卷才能测试出比较真实的性能

    • 大 IO 顺序读写测试需要创建条带卷,curveadm map 不支持创建条带卷,需要到其中一个 MDS 容器中执行命令创建

      • 参考命令 curve_ops_tool create -fileName=/test -userName=curve -fileLength=200 -stripeUnit=32768 -stripeCount=32

      • curveadm map 命令去掉自动创建卷的参数

      • 在大 IO 读的测试中,可能会出现性能很低或抖动明显的情况,这种情况需要在物理机和交换机上进行 RDMA 拥塞控制调整(具体步骤略微繁琐,这里不展开)

    使用 fio cbd 引擎测试

    cbd 引擎测试可以避免 nbd 挂载带来的额外开销,在单卷测试中,会有更高的性能。

    为了方便编译,如下命令以在 curveadm map 创建的容器内执行

    $ git clone https://github.com/opencurve/fio.git -b curve
    $ cd fio
    $ ./configure # 输出中,cbd engine 需要是 yes
    $ make -j`nproc`

    # 保存如下文件到 cbd.fio
    [global]
    ioengine=cbd
    direct=1
    bs=4k
    iodepth=128
    rw=randwrite
    numjobs=1
    size=100G
    runtime=120
    time_based

    [filename1]
    cbd=/test_curve_ # /test 为卷名,curve 为用户名

    $ mkdir -p /curvebs/client/logs
    $ ./fio cbd.fio # 执行测试

    重新部署或格式化步骤

    由于版本限制,curveadm clean 命令不能回收 chunk 到 chunkfilepool 中,重新部署需要重新格式化

    重新格式化前,需要解除 NVMe 绑定

    $ curveadm format -f format.test.yaml --spdk --reset

    存储节点重启

    存储节点的 NVMe 盘在重启后没有自动绑定,需要手动绑定,然后拉起 Chunkserver

    $ curveadm format -f format.yaml --spdk --only-binding
    $ curveadm start --role=chunkserver

    参考

    [1][SPDK System Configuration User Guide](https://spdk.io/doc/system_configuration.html)

    [2][MLNX-OFED Installation](https://docs.nvidia.com/networking/display/MLNXOFEDv582030LTS/Installation)

    - - + + \ No newline at end of file diff --git a/CurveBS/interface/localsnapshotclone_restful_api.html b/CurveBS/interface/localsnapshotclone_restful_api.html index 14cbe1a..26142e4 100644 --- a/CurveBS/interface/localsnapshotclone_restful_api.html +++ b/CurveBS/interface/localsnapshotclone_restful_api.html @@ -4,14 +4,14 @@ localsnapshotclone restful api | Curve Book - - + +
    跳到主要内容

    localsnapshotclone restful api

    localsnapshotclone restful api 需兼容原有s3快照api: snapshotcloneserver_interface, 使用两套API均可以调用本地快照和克隆功能, 此处, 列出本地快照和克隆新api

    创建卷:

    创建一个卷。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=CreateFile&Version=1.5&User=curve&File=/test1&Size=100
    请求参数
    名称类型是否必须描述
    ActionstringCreateFile
    VersionstringAPI版本号
    Userstring租户信息
    Filestring卷路径
    Sizeuint64卷大小,单位为GB
    StripeUnituint64条带的宽度, 默认为0,无条带
    StripeCountuint64条带数量, 默认为0, 无条带
    PoolSetstringpoolset name, 默认使用默认存储池
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=CreateFile&Version=1.5&User=curve&File=/test1&Size=100

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }

    错误码

    见最后一节错误码表。

    删除卷:

    删除一个卷。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=DeleteFile&Version=1.5&User=curve&File=/test1
    请求参数
    名称类型是否必须描述
    ActionstringDeleteFile
    VersionstringAPI版本号
    Userstring租户信息
    Filestring卷路径
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=DeleteFile&Version=1.5&User=curve&File=/test1

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }

    错误码

    见最后一节错误码表。

    列出卷

    列出指定目录下的所有卷信息

    若指定卷名,则列出指定卷的信息

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=ListFile&Version=1.5&User=curve&Dir=/&Limit=10&Offset=0
    GET/SnapshotCloneService?Action=ListFile&Version=1.5&User=curve&File=/test1
    请求参数
    名称类型是否必须描述
    ActionstringListFile
    VersionstringAPI版本号
    Userstring租户信息
    Dirstring目录路径
    Filestring卷路径
    Limituint64返回的卷数量
    Offsetuint64偏移量
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    TotalCountint总个数
    FileInfosarray卷信息

    FileInfo类型说明

    名称类型描述
    Filestring卷名
    Userstring租户信息
    Typeenum类型, 0:卷, 1:目录, 2:未知类型
    Timeuint64创建时间
    FileLengthuint64卷大小(单位Byte)
    StripeUnituint64条带的宽度, 默认为0,无条带
    StripeCountuint64条带数量, 默认为0, 无条带
    Statusenum卷的状态, 0:done, 1:flattening, 2:unflattened
    Readonlybool是否只读
    Progressuint32完成百分比, flattening状态时则表示百分比进度
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=ListFile&Version=1.5&User=curve&Dir=/&Limit=10&Offset=0

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {
    "Code" : "0",
    "FileInfos" :
    [
    {
    "File" : "/RecycleBin",
    "FileLength" : 0,
    "Progress" : 100,
    "Status" : 0,
    "StripeCount" : 0,
    "StripeUnit" : 0,
    "Time" : 1696761354857556,
    "Type" : 1,
    "User" : "root"
    },
    {
    "File" : "/clone",
    "FileLength" : 0,
    "Progress" : 100,
    "Status" : 0,
    "StripeCount" : 0,
    "StripeUnit" : 0,
    "Time" : 1696761362745606,
    "Type" : 1,
    "User" : "root"
    },
    {
    "File" : "/test1",
    "FileLength" : 107374182400,
    "Progress" : 100,
    "Status" : 0,
    "StripeCount" : 0,
    "StripeUnit" : 0,
    "Time" : 1696761378914477,
    "Type" : 0,
    "User" : "curve"
    }
    ],
    "Message" : "Exec success.",
    "RequestId" : "26ffe85b-55e8-46c4-8823-0104fcf9b362",
    "TotalCount" : 3
    }
    错误码

    见最后一节错误码表。

    创建快照:

    描述

    创建一个快照。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=CreateSnapshot&Version=1.5&User=curve&File=/test1&Name=snap1
    请求参数
    名称类型是否必须描述
    ActionstringCreateSnapshot
    VersionstringAPI版本号
    Userstring租户名称
    Filestring快照所属的卷路径
    Namestring快照名称
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    UUIDstring快照唯一ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=CreateSnapshot&Version=1.5&User=curve&File=/test1&Name=snap1

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    ​ "UUID" : "xxx"

    }

    错误码

    见最后一节错误码表。

    删除快照

    描述

    删除一个快照。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=DeleteSnapshot&Version=1.5&User=curve&File=/test1&Name=snap1
    请求参数
    名称类型是否必须描述
    ActionstringDeleteSnapshot
    VersionstringAPI版本号
    Userstring租户信息
    Filestring快照所属卷路径
    Namestring快照名
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=DeleteSnapshot&Version=1.5&User=curve&File=/test1&Name=snap1

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    列出快照

    描述

    查询指定卷的所有快照信息,

    若指定快照名,则查询指定快照信息。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=1.5&User=curve&File=/test1&Limit=10&Offset=0
    GET/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=1.5&User=curve&File=/test1&Name=snap1
    请求参数
    名称类型是否必须描述
    ActionstringGetFileSnapinfo
    VersionstringAPI版本号
    Userstring租户信息
    Filestring卷路径
    Limitint最大显示条数,默认为10
    Offsetint偏移值,默认为0
    Namestring快照名
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    TotalCountint快照总个数
    SnapshotsSnapshot快照信息列表

    Snapshot类型说明

    名称类型描述
    UUIDstring快照唯一ID
    Userstring租户信息
    Filestring快照所属卷路径
    SeqNumuint32快照版本号
    Namestring快照名称
    Timeuint64创建时间
    FileLengthuint64卷大小(单位Byte)
    Statusenum快照处理的状态(0:done, 2:deleteing, 5:error)
    Progressuint32快照完成百分比, 创建快照只有0和100两种情况, 删除快照时则表示删除百分比进度
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=1.5&User=curve&File=/test1&Limit=10&Offset=0

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    ​ "TotalCount": 1,

    ​ "Snapshots":

    [

    ​ {

    ​ "File" : "/test1",

    ​ "FileLength" : 10737418240,

    ​ "Name" : "snap1",

    ​ "Progress" : 30,

    ​ "SeqNum" : 1,

    ​ "Status" : 1,

    ​ "Time" : 1564391913582677,

    ​ "UUID" : "de06df66-b9e4-44df-ba3d-ac94ddee0b28",

    ​ "User" : "curve"

    ​ }

    ]

    }
    错误码

    见最后一节错误码表。

    克隆:

    描述

    从快照克隆一个子卷

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=Clone&Version=1.5&User=curve&File=/test1&Name=snap1&Destination=/clone1
    请求参数
    名称类型是否必须描述
    ActionstringClone
    VersionstringAPI版本号
    Userstring租户信息
    Filestring卷路径
    Namestring快照名
    Destinationstring克隆目标路径
    Readonlybool是否克隆成只读卷
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    UUIDstring克隆任务唯一ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=Clone&Version=1.5&User=curve&File=/test1&Name=snap1&Destination=/clone1

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    }
    错误码

    见最后一节错误码表。

    回滚:

    暂不支持

    Flatten

    描述

    补足子卷的数据,使其与父卷和快照解耦

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=Flatten&Version=1.5&User=curve&File=/clone1
    请求参数
    名称类型是否必须描述
    ActionstringFlatten
    VersionstringAPI版本号
    Userstring租户信息
    Filestring目标卷路径
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=Flatten&Version=1.5&User=curve&File=/clone1

    response

    HTTP/1.1 200 OK
    Content-Length: xxx
    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    错误码表:

    CodeMessageHTTP Status Code描述补充说明
    0Exec success.200执行成功
    -1Internal error.500未知内部错误未知错误,任何请求不应出现此错误,已知的错误都应有明确的错误码。如发现此错误,请联系开发人员定位。
    -2Server init fail.500服务器初始化失败任何请求不应出现此错误,初始化阶段错误
    -3Server start fail.500服务器启动失败任何请求不应出现此错误,初始化阶段错误
    -4Service is stop500服务已停止snapshotcloneserver停止服务退出阶段,发送请求会收到此错误
    -5BadRequest:"Invalid request."400非法的请求发送http请求格式非法:缺少字段,字段值非法等
    -6Task already exist.500任务已存在目前不会出现
    -7Invalid user.500非法的用户请求中User字段与操作的卷或快照的owner不匹配
    -8File not exist.500文件或快照不存在打快照时目标卷不存在; 从快照回滚时,目标卷不存在; 从快照克隆/回滚时,快照不存在;
    -9File status invalid.500文件状态异常打快照时,目标卷正在克隆/回滚中或删除中等状态而不是Normal状态,返回文件状态异常; 从快照克隆过程中,快照正在删除中等状态而不是Normal状态,返回文件状态异常
    -10Chunk size not aligned.500chunk大小未按分片对齐一般是配置文件问题,配置的chunk分配大小与chunksize未对其,正常情况下不会出现
    -11FileName not match.500文件名不匹配删除快照接口,快照所属卷与快照不匹配,即该卷没有这个快照
    -12Cannot delete unfinished.500不能删除未完成的快照
    -13Cannot create when has error.500不能对存在错误的卷打快照或克隆/回滚不能对存在错误快照的卷再次打快照
    -14Cannot cancel finished.500待取消的快照已完成或不存在目前不会出现
    -15Invalid snapshot.500不能对未完成或存在错误的快照进行克隆/回滚
    -16Cannot delete when using.500不能删除正在克隆/回滚的快照
    -17Cannot clean task unfinished.500不能清理未完成的克隆/回滚任务目前不会出现
    -18Snapshot count reach the limit.500快照到达上限
    -19File exist.500文件或快照已存在从快照克隆时,目标卷已存在
    -20Task is full.500克隆/回滚任务已满目前不会出现
    -21not support.500不支持的操作
    -22File is under snapshot500删除的卷具有快照
    -23File is occuiped500删除的卷正在使用(挂载状态或者Flatting中)
    -24Invalid argument.500非法的请求参数
    - - + + \ No newline at end of file diff --git a/CurveBS/interface/localsnapshotclone_tools_api.html b/CurveBS/interface/localsnapshotclone_tools_api.html index f1f95e3..f6f07ca 100644 --- a/CurveBS/interface/localsnapshotclone_tools_api.html +++ b/CurveBS/interface/localsnapshotclone_tools_api.html @@ -4,13 +4,13 @@ localsnapshotclone tools api | Curve Book - - + +
    跳到主要内容

    localsnapshotclone tools api

    本地快照和克隆采用tools-v2工具命令行方式,使用方法如下:

    创建快照:

    描述

    创建一个快照。

    使用方法
    Usage:  curve bs create snapshot [flags]

    create snapshot for file in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snap file path, like: {file-path}@{snap-name}[required]
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs create snapshot --snappath /test0@snap0 --user curve

    删除快照

    描述

    删除一个快照。

    使用方法
    Usage:  curve bs delete snapshot [flags]

    delete snapshot for file in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snap file path, like: {file-path}@{snap-name}[required]
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs delete snapshot --snappath /test0@snap0 --user curve

    列出快照

    描述

    列出所有快照。

    使用方法
    Usage:  curve bs list snapshot [flags]

    list snapshot information for volume in curvebs

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --path string file path[required]
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs list snapshot --path /test0 --user curve

    保护快照

    描述

    保护一个快照, 用于克隆

    使用方法

    Usage:  curve bs protect [flags]

    protect snapshot in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snap file path, like: {file-path}@{snap-name}[required]
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs protect --snappath /test0@snap0 --user curve

    取消保护快照

    描述

    取消保护一个快照

    使用方法

    Usage:  curve bs unprotect [flags]

    unprotect snapshot in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snap file path, like: {file-path}@{snap-name}[required]
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs unprotect --snappath /test0@snap0 --user curve

    children

    描述

    列出卷或者快照的所有子卷

    使用方法

    Usage:  curve bs children [flags]

    list children of snapshot/volume in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --path string file or directory path (default "/test")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snapshot file path
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs children --path /test0 --user curve
    $ curve bs children --snappath /test0@snap0 --user curve

    克隆:

    描述

    从快照克隆一个子卷

    使用方法

    Usage:  curve bs clone [flags]

    clone file in curvebs cluster

    Flags:
    --dstpath string destiation file path[required]
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --snappath string snap file path, like: {file-path}@{snap-name}[required]
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs clone --snappath /test0@snap0 --dstpath /test2 --user curve

    Flatten

    描述

    补足子卷的数据,使其与父卷和快照解耦

    使用方法

    Usage:  curve bs flatten [flags]

    flatten clone file in curvebs cluster

    Flags:
    -f, --format string output format (json|plain) (default "plain")
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --password string user password (default "root_password")
    --path string file path[required]
    --rpcretrytimes int32 rpc retry times (default 1)
    --rpctimeout duration rpc timeout (default 10s)
    --user string user name[required]

    Global Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -h, --help print help
    --showerror display all errors in command
    --verbose show some log

    Examples:
    $ curve bs flatten --path /test2 --user curve
    - - + + \ No newline at end of file diff --git a/CurveBS/interface/snapshotcloneserver_interface.html b/CurveBS/interface/snapshotcloneserver_interface.html index c8ff6c5..75298fa 100644 --- a/CurveBS/interface/snapshotcloneserver_interface.html +++ b/CurveBS/interface/snapshotcloneserver_interface.html @@ -4,13 +4,13 @@ snapshotcloneserver interface | Curve Book - - + +
    跳到主要内容

    snapshotcloneserver interface

    创建快照:

    描述

    创建一个快照。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=CreateSnapshot&Version=0.0.6&User=curve&File=/test1&Name=snap1
    请求参数
    名称类型是否必须描述
    ActionstringCreateSnapshot
    VersionstringAPI版本号 0.0.6
    Userstring租户名称
    Filestring快照目标文件
    Namestring快照文件名称
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    UUIDstring快照唯一ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=CreateSnapshot&Version=0.0.6&User=curve&File=/test1&Name=snap1

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    ​ "UUID" : "xxx"

    }
    错误码

    见最后一节错误码表。

    删除快照

    描述

    删除一个快照。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=DeleteSnapshot&Version=0.0.6&User=curve&File=/test1&UUID=uuid1
    请求参数
    名称类型是否必须描述
    ActionstringDeleteSnapshot
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Filestring快照所属文件名
    UUIDstring快照唯一ID
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=DeleteSnapshot&Version=0.0.6&User=curve&File=/test1&UUID=uuid1

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    取消快照

    取消一个正在执行的快照。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=CancelSnapshot&Version=0.0.6&User=curve&File=/test1&UUID=uuid1
    请求参数
    名称类型是否必须描述
    ActionstringCancelSnapshot
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Filestring快照所属文件名
    UUIDstring快照唯一ID
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=CancelSnapshot&Version=0.0.6&User=curve&File=/test1&UUID=uuid1

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    查询文件的快照信息

    描述

    查询指定文件的所有快照信息,

    若指定UUID,则查询该文件的该UUID指定的快照信息

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=0.0.6&User=curve&File=/test1&Limit=10&Offset=0
    GET/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=0.0.6&User=curve&File=/test1&UUID=de06df66-b9e4-44df-ba3d-ac94ddee0b28
    请求参数
    名称类型是否必须描述
    ActionstringGetFileSnapinfo
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Filestring文件名称
    Limitint最大显示条数,默认为10
    Offsetint偏移值,默认为0
    UUIDstring快照的uuid信息
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    TotalCountint快照总个数
    SnapshotsSnapshot快照信息列表

    Snapshot类型说明

    名称类型描述
    UUIDstring快照唯一ID
    Userstring租户信息
    Filestring文件名称
    SeqNumuint32快照版本号
    Namestring快照名称
    Timeuint64创建时间
    FileLengthuint32文件大小(单位Byte)
    Statusenum快照处理的状态(0:done, 1:pending, 2:deleteing, 3:errorDeleting, 4:canceling, 5:error)
    Progressuint32快照完成百分比
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=GetFileSnapshotInfo&Version=0.0.6&User=curve&File=/test1&Limit=10 

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    ​ "TotalCount": 1,

    ​ "Snapshots":

    [

    ​ {

    ​ "File" : "/test1",

    ​ "FileLength" : 10737418240,

    ​ "Name" : "snap1",

    ​ "Progress" : 30,

    ​ "SeqNum" : 1,

    ​ "Status" : 1,

    ​ "Time" : 1564391913582677,

    ​ "UUID" : "de06df66-b9e4-44df-ba3d-ac94ddee0b28",

    ​ "User" : "curve"

    ​ }

    ]

    }
    错误码

    见最后一节错误码表。

    克隆:

    描述

    从快照或镜像克隆一个文件。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=Clone&Version=0.0.6&User=curve&Source=/test1&Destination=/clone1&Lazy=true
    请求参数
    名称类型是否必须描述
    ActionstringClone
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Sourcestring镜像文件名或者快照UUID
    Destinationstring克隆目标文件名
    Lazybool是否Lazy克隆
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    UUIDstring克隆任务唯一ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=Clone&Version=0.0.6&User=curve&Source=/test1&Destination=/clone1&Lazy=true 

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    ​ "UUID" : "xxx"

    }
    错误码

    见最后一节错误码表。

    恢复:

    描述

    从快照恢复一个文件。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=Recover&Version=0.0.6&User=curve&Source=de06df66-b9e4-44df-ba3d-ac94ddee0b28&Destination=/recover1&Lazy=true
    请求参数
    名称类型是否必须描述
    ActionstringRecover
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Sourcestring快照UUID
    Destinationstring克隆目标文件名
    Lazybool是否Lazy克隆
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    UUIDstring恢复任务的唯一ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=Recover&Version=0.0.6&User=curve&Source=de06df66-b9e4-44df-ba3d-ac94ddee0b28&Destination=/recover1&Lazy=true 

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    ​ "UUID" : "xxx"

    }
    错误码

    见最后一节错误码表。

    Flatten

    描述

    对Lazy克隆的文件进行恢复数据操作。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=Flatten&Version=0.0.6&User=curve&UUID=xxx
    请求参数
    名称类型是否必须描述
    ActionstringFlatten
    VersionstringAPI版本号0.0.6
    Userstring租户信息
    UUIDstring任务唯一ID
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=Flatten&Version=0.0.6&User=curve&UUID=de06df66-b9e4-44df-ba3d-ac94ddee0b28

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    查询指定用户的克隆/恢复任务信息:

    描述

    查询指定用户的所有克隆/恢复任务信息,

    若指定UUID,则查询该用户的该UUID的任务信息

    若指定File,则查询该用户的该文件的任务信息

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=GetCloneTasks&Version=0.0.6&User=curve&Limit=10&Offset=0
    GET/SnapshotCloneService?Action=GetCloneTasks&Version=0.0.6&User=curve&UUID=xxx"
    GET/SnapshotCloneService?Action=GetCloneTasks&Version=0.0.6&User=curve&File=/clone1"
    请求参数
    名称类型是否必须描述
    ActionstringGetCloneTasks
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    Limitint最大显示任务数,默认为10
    Offsetint偏移值,默认为0
    UUIDstring克隆/恢复任务唯一ID
    Filestring克隆/恢复任务的目标文件名
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    TotalCountint任务总个数
    TaskInfosTaskInfo任务信息列表

    TaskInfo类型说明

    名称类型描述
    UUIDstring任务唯一ID
    TaskTypeenum任务类型(0:clone, 1:recover)
    Userstring租户信息
    Filestring文件名称
    Timeuint64创建时间
    TaskStatusenum任务的状态(0:done, 1:cloning, 2:recovering, 3:cleaning, 4:errorCleaning, 5:error,6:retrying, 7:metaInstalled)
    示例

    request

    http://127.0.0.1:5555//SnapshotCloneService?Action=GetCloneTasks&Version=0.0.6&User=curve&Limit=10"

    response

    HTTP/1.1 200 OK

    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx",

    ​ "TotalCount": 1,

    ​ "TaskInfos" :

    ​ [

    ​ {

    ​ "File" : "/clone1",

    ​ "UUID" : "78e83875-2b50-438f-8f25-36715380f4f5",

    ​ "TaskStatus" : 5,

    ​ "TaskType" : 0,

    ​ "Time" : 0,

    ​ "User" : "curve"

    ​ }

    ​ ]

    }
    错误码

    见最后一节错误码表。

    清除克隆/恢复任务:

    描述

    清除克隆/恢复任务。若是失败的任务,还会删除curvefs上的临时克隆文件,否则只删除任务。

    语法
    MethodUrl
    GET/SnapshotCloneService?Action=CleanCloneTask&Version=0.0.6&User=curve&UUID=78e83875-2b50-438f-8f25-36715380f4f5
    请求参数
    名称类型是否必须描述
    ActionstringCleanCloneTask
    VersionstringAPI版本号 0.0.6
    Userstring租户信息
    UUIDstring克隆/恢复任务唯一ID
    响应
    名称类型描述
    Codestring错误码
    Messagestring错误信息
    RequestIdstring请求ID
    示例

    request

    http://127.0.0.1:5555/SnapshotCloneService?Action=CleanCloneTask&Version=0.0.6&User=curve&UUID=78e83875-2b50-438f-8f25-36715380f4f5

    response

    HTTP/1.1 200 OK
    Content-Length: xxx

    {

    ​ "Code" : "0",

    ​ "Message" : "Exec success.",

    ​ "RequestId" : "xxx"

    }
    错误码

    见最后一节错误码表。

    错误码表:

    CodeMessageHTTP Status Code描述补充说明
    0Exec success.200执行成功
    -1Internal error.500未知内部错误未知错误,任何请求不应出现此错误,已知的错误都应有明确的错误码。如发现此错误,请联系相关人员定位。
    -2Server init fail.500服务器初始化失败任何请求不应出现此错误,初始化阶段错误
    -3Server start fail.500服务器启动失败任何请求不应出现此错误,初始化阶段错误
    -4Service is stop500服务已停止snapshotcloneserver停止服务退出阶段,发送请求会收到此错误
    -5BadRequest:"Invalid request."400非法的请求发送http请求格式非法:缺少字段,字段值非法等
    -6Task already exist.500任务已存在目前不会出现
    -7Invalid user.500非法的用户请求中User字段与操作的文件、镜像或快照的owner不匹配
    -8File not exist.500文件不存在打快照时目标文件不存在从快照恢复时,目标文件不存在从快照或镜像克隆/恢复时,快照或镜像不存在清除指定clone任务时,clone任务不存在获取指定克隆/恢复任务时,任务不存在获取指定快照时,快照不存在
    -9File status invalid.500文件状态异常打快照时,目标文件正在克隆/恢复中或删除中等状态而不是Normal状态,返回文件状态异常从镜像克隆过程中,源镜像文件正在克隆/恢复中或删除中等状态而不是Normal状态,返回文件状态异常
    -10Chunk size not aligned.500chunk大小未按分片对齐一般是配置文件问题,配置的chunk分配大小与chunksize未对其,正常情况下不会出现
    -11FileName not match.500文件名不匹配删除或取消快照接口,快照所属文件与快照不匹配,即该文件没有这个快照
    -12Cannot delete unfinished.500不能删除未完成的快照
    -13Cannot create when has error.500不能对存在错误的文件打快照或克隆/恢复不能对存在错误快照的文件再次打快照不能对存在错误克隆/恢复任务的目标文件再次克隆/恢复
    -14Cannot cancel finished.500待取消的快照已完成或不存在
    -15Invalid snapshot.500不能对未完成或存在错误的快照进行克隆/恢复
    -16Cannot delete when using.500不能删除正在克隆/恢复的快照
    -17Cannot clean task unfinished.500不能清理未完成的克隆/恢复任务
    -18Snapshot count reach the limit.500快照到达上限
    -19File exist.500文件已存在从快照或镜像克隆时,目标文件已存在
    -20Task is full.500克隆/恢复任务已满
    - - + + \ No newline at end of file diff --git a/CurveBS/maintenance/administrator-guide.html b/CurveBS/maintenance/administrator-guide.html index 271b147..be884b4 100644 --- a/CurveBS/maintenance/administrator-guide.html +++ b/CurveBS/maintenance/administrator-guide.html @@ -4,13 +4,13 @@ 管理员操作指导 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/maintenance/command-line-tools.html b/CurveBS/maintenance/command-line-tools.html index af67d4f..155ab2f 100644 --- a/CurveBS/maintenance/command-line-tools.html +++ b/CurveBS/maintenance/command-line-tools.html @@ -4,15 +4,15 @@ 命令行工具 | Curve Book - - + +
    跳到主要内容

    命令行工具

    Curve 命令行工具代码仓库地址:https://github.com/opencurve/curve/blob/master/tools-v2/README.md

    工具目前还在继续开发完善之中,欢迎提交issue或pr。

    How to use curve tool

    Install

    install curve tool

    wget https://curve-tool.nos-eastchina1.126.net/release/curve-latest
    chmod +x curve-latest
    mv curve-latest /usr/bin/curve

    set configure file

    wget https://raw.githubusercontent.com/opencurve/curve/master/tools-v2/pkg/config/curve.yaml

    or

    wget https://curve-tool.nos-eastchina1.126.net/config/curve.yaml

    Please modify the mdsAddr, mdsDummyAddr, etcdAddr under curvefs/bs in the template.yaml file as required

    mv curve.yaml ~/.curve/curve.yaml

    or

    mv curve.yaml /etc/curve/curve.yaml

    Introduction

    Here's how to use the tool

    curve COMMAND [options]

    When you are not sure how to use a command, --help can give you an example of use:

    curve COMMAND --help

    For example:

    curve fs status mds --help
    Usage: curve fs status mds [flags]

    get the inode usage of curvefs

    Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -f, --format string Output format (json|plain) (default "plain")
    -h, --help Print usage
    --httptimeout duration http timeout (default 500ms)
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --mdsdummyaddr string mds dummy address, should be like 127.0.0.1:7700,127.0.0.1:7701,127.0.0.1:7702
    --showerror display all errors in command

    Examples:
    $ curve fs status mds

    In addition, this tool reads the configuration from $HOME/.curve/curve.yaml or /etc/curve/curve.yaml by default, and can be specified by --conf or -c.

    Command

    version

    show the version of curve tool

    Usage:

    curve --version

    Output:

    curve v1.2

    completion

    generate curve bash/zsh/fish completion script

    Take bash as an example:

    curve completion bash > /etc/bash_completion.d/curve

    If you perform completion you get the following error:

    curve [tab]bash: _get_comp_words_by_ref: command not found

    You need to install bash-completion:

    sudo apt install bash-completion
    source /usr/share/bash-completion/bash_completion

    upgrade

    Upgrade curve to latest version

    Usage:

    curve upgrade

    If you want to upgrade to the specified version(ee500438):

    CURVE_VERSION=ee500438 curve upgrade

    If you are in an offline environment, you can also set up a temporary http server to provide upgrade services for other machines:

    # 192.168.1.1
    echo "123456" > __version
    cp curve curve-123456
    python3 -m http.server 1234

    In this way, it can be upgraded on other machines

    CURVE_BASE_URL=http://192.168.1.1:1234/ curve upgrade

    fs

    check

    check copyset

    check copysets health in curvefs

    Usage:

    curve fs check copyset --copysetid 1 --poolid 1

    Output:

    +------------+-----------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | EXPLAIN |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    +------------+-----------+--------+--------+---------+

    create

    create fs

    create a fs in curvefs

    Usage:

    curve fs create fs --fsname test3

    Output:

    +--------+-------------------------------------------+
    | FSNAME | RESULT |
    +--------+-------------------------------------------+
    | test3 | fs exist, but s3 info is not inconsistent |
    +--------+-------------------------------------------+
    create topology

    create curvefs topology

    Usage:

    curve fs create topology --clustermap topology.json

    Output:

    +-------------------+--------+-----------+--------+
    | NAME | TYPE | OPERATION | PARENT |
    +-------------------+--------+-----------+--------+
    | pool2 | pool | del | |
    +-------------------+--------+ +--------+
    | zone4 | zone | | pool2 |
    +-------------------+--------+ +--------+
    | **.***.***.**_3_0 | server | | zone4 |
    +-------------------+--------+-----------+--------+

    delete

    delete fs

    delete a fs from curvefs

    Usage:

    curve fs delete fs --fsname test1
    WARNING:Are you sure to delete fs test1?
    please input [test1] to confirm: test1

    Output:

    +--------+-------------------------------------+
    | FSNAME | RESULT |
    +--------+-------------------------------------+
    | test1 | delete fs failed!, error is FS_BUSY |
    +--------+-------------------------------------+

    list

    list copyset

    list all copyset info of the curvefs

    Usage:

    curve fs list copyset

    Output:

    +------------+-----------+--------+-------+--------------------------------+------------+
    | KEY | COPYSETID | POOLID | EPOCH | LEADERPEER | PEERNUMBER |
    +------------+-----------+--------+-------+--------------------------------+------------+
    | 4294967302 | 6 | 1 | 2 | id:1 | 3 |
    | | | | | address:"**.***.***.**:6801:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967303 | 7 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967304 | 8 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967307 | 11 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+--------------------------------+------------+
    | 4294967297 | 1 | | 1 | id:2 | 3 |
    | | | | | address:"**.***.***.**:6802:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967301 | 5 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967308 | 12 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+--------------------------------+------------+
    | 4294967298 | 2 | | 1 | id:3 | 3 |
    | | | | | address:"**.***.***.**:6800:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967299 | 3 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967300 | 4 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967305 | 9 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967306 | 10 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+--------+-------+--------------------------------+------------+
    list fs

    list all fs info in the curvefs

    Usage:

    curve fs list fs

    Output:

    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | ID | NAME | STATUS | CAPACITY | BLOCKSIZE | FSTYPE | SUMINDIR | OWNER | MOUNTNUM |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | 2 | test1 | INITED | 107374182400 | 1048576 | TYPE_S3 | false | anonymous | 1 |
    +----+-------+--------+--------------+-----------+ +----------+ +----------+
    | 3 | test3 | INITED | 107374182400 | 1048576 | | false | | 0 |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    list mountpoint

    list all mountpoint of the curvefs

    Usage:

    curve fs list mountpoint

    Output:

    +------+--------+-------------------------------------------------------------------+
    | FSID | FSNAME | MOUNTPOINT |
    +------+--------+-------------------------------------------------------------------+
    | 2 | test1 | siku-QiTianM420-N000:9002:/curvefs/client/mnt/home/siku/temp/mnt1 |
    + + +-------------------------------------------------------------------+
    | | | siku-QiTianM420-N000:9003:/curvefs/client/mnt/home/siku/temp/mnt2 |
    +------+--------+-------------------------------------------------------------------+
    list partition

    list partition in curvefs by fsid

    Usage:

    curve fs list partition

    Output:

    +-------------+------+--------+-----------+----------+----------+-----------+
    | PARTITIONID | FSID | POOLID | COPYSETID | START | END | STATUS |
    +-------------+------+--------+-----------+----------+----------+-----------+
    | 14 | 2 | 1 | 10 | 1048676 | 2097351 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 20 | | | | 7340732 | 8389407 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 13 | | | 11 | 0 | 1048675 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 16 | | | | 3146028 | 4194703 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 22 | | | | 9438084 | 10486759 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 21 | | | 5 | 8389408 | 9438083 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 23 | | | 7 | 10486760 | 11535435 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 24 | | | | 11535436 | 12584111 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 15 | | | 8 | 2097352 | 3146027 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 18 | | | | 5243380 | 6292055 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 17 | | | 9 | 4194704 | 5243379 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 19 | | | | 6292056 | 7340731 | READWRITE |
    +-------------+------+ +-----------+----------+----------+-----------+
    | 26 | 3 | | 2 | 1048676 | 2097351 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 30 | | | | 5243380 | 6292055 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 34 | | | 3 | 9438084 | 10486759 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 29 | | | 4 | 4194704 | 5243379 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 32 | | | | 7340732 | 8389407 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 35 | | | 5 | 10486760 | 11535435 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 27 | | | | 2097352 | 3146027 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 33 | | | | 8389408 | 9438083 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 25 | | | 6 | 0 | 1048675 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 36 | | | | 11535436 | 12584111 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 28 | | | 8 | 3146028 | 4194703 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 31 | | | 9 | 6292056 | 7340731 | READWRITE |
    +-------------+------+--------+-----------+----------+----------+-----------+
    list topology

    list the topology of the curvefs

    Usage:

    curve fs list topology

    Output:

    +----+------------+--------------------+------------+-----------------------+
    | ID | TYPE | NAME | CHILDTYPE | CHILDLIST |
    +----+------------+--------------------+------------+-----------------------+
    | 1 | pool | pool1 | zone | zone3 zone2 zone1 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | zone | zone3 | server | **.***.***.**_2_0 |
    +----+ +--------------------+ +-----------------------+
    | 2 | | zone2 | | **.***.***.**_1_0 |
    +----+ +--------------------+ +-----------------------+
    | 1 | | zone1 | | **.***.***.**_0_0 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | server | **.***.***.**_2_0 | metaserver | curvefs-metaserver.2 |
    +----+ +--------------------+ +-----------------------+
    | 2 | | **.***.***.**_1_0 | | curvefs-metaserver.1 |
    +----+ +--------------------+ +-----------------------+
    | 1 | | **.***.***.**_0_0 | | curvefs-metaserver.3 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | metaserver | curvefs-metaserver | | |
    +----+ +--------------------+------------+-----------------------+
    | 2 | | curvefs-metaserver | | |
    +----+ +--------------------+------------+-----------------------+
    | 1 | | curvefs-metaserver | | |
    +----+------------+--------------------+------------+-----------------------+

    query

    query copyset

    query copysets in curvefs

    Usage:

    curve fs query copyset --copysetid 1 --poolid 1

    Output:

    +------------+-----------+--------+--------------------------------------+-------+
    | copysetKey | copysetId | poolId | leaderPeer | epoch |
    +------------+-----------+--------+--------------------------------------+-------+
    | 4294967297 | 1 | 1 | id:2 address:"**.***.***.**:6802:0" | 1 |
    +------------+-----------+--------+--------------------------------------+-------+
    query fs

    query fs in curvefs by fsname or fsid

    Usage:

    curve fs query fs --fsname test1

    Output:

    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | id | name | status | capacity | blocksize | fsType | sumInDir | owner | mountNum |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | 2 | test1 | INITED | 107374182400 | 1048576 | TYPE_S3 | false | anonymous | 2 |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    query inode

    query the inode of fs

    Usage:

    curve fs query inode --fsid 2 --inodeid 5243380

    Output:

    +-------+----------+-----------+---------+-------+--------+
    | fs id | inode id | length | type | nlink | parent |
    +-------+----------+-----------+---------+-------+--------+
    | 2 | 5243380 | 352321536 | TYPE_S3 | 1 | [1] |
    +-------+----------+-----------+---------+-------+--------+
    query metaserver

    query metaserver in curvefs by metaserverid or metaserveraddr

    Usage:

    curve fs query metaserver --metaserveraddr **.***.***.**:6801,**.***.***.**:6802

    Output:

    +----+--------------------+--------------------+--------------------+-------------+
    | id | hostname | internalAddr | externalAddr | onlineState |
    +----+--------------------+--------------------+--------------------+-------------+
    | 1 | curvefs-metaserver | **.***.***.**:6801 | **.***.***.**:6801 | ONLINE |
    | 2 | curvefs-metaserver | **.***.***.**:6802 | **.***.***.**:6802 | ONLINE |
    +----+--------------------+--------------------+--------------------+-------------+
    query partition

    query the copyset of partition

    Usage:

    curve fs query partition --partitionid 14

    Output:

    +----+--------+-----------+--------+----------------------+
    | id | poolId | copysetId | peerId | peerAddr |
    +----+--------+-----------+--------+----------------------+
    | 14 | 1 | 10 | 1 | **.***.***.**:6801:0 |
    | 14 | 1 | 10 | 2 | **.***.***.**:6802:0 |
    | 14 | 1 | 10 | 3 | **.***.***.**:6800:0 |
    +----+--------+-----------+--------+----------------------+

    status

    status mds

    get status of mds

    Usage:

    curve fs status mds

    Output:

    +--------------------+--------------------+----------------+----------+
    | addr | dummyAddr | version | status |
    +--------------------+--------------------+----------------+----------+
    | **.***.***.**:6700 | **.***.***.**:7700 | 8fc48476+debug | follower |
    | **.***.***.**:6701 | **.***.***.**:7701 | 8fc48476+debug | follower |
    | **.***.***.**:6702 | **.***.***.**:7702 | 8fc48476+debug | leader |
    +--------------------+--------------------+----------------+----------+
    status metaserver

    get status of metaserver

    Usage:

    curve fs status metaserver

    Output:

    +--------------------+--------------------+----------------+--------+
    | externalAddr | internalAddr | version | status |
    +--------------------+--------------------+----------------+--------+
    | **.***.***.**:6800 | **.***.***.**:6800 | 8fc48476+debug | online |
    | **.***.***.**:6802 | **.***.***.**:6802 | 8fc48476+debug | online |
    | **.***.***.**:6801 | **.***.***.**:6801 | 8fc48476+debug | online |
    +--------------------+--------------------+----------------+--------+
    status etcd

    get status of etcd

    Usage:

    curve fs status etcd

    Output:

    +---------------------+---------+----------+
    | addr | version | status |
    +---------------------+---------+----------+
    | **.***.***.**:23790 | 3.4.10 | follower |
    | **.***.***.**:23791 | 3.4.10 | follower |
    | **.***.***.**:23792 | 3.4.10 | leader |
    +---------------------+---------+----------+
    status copyset

    get status of copyset

    Usage:

    curve fs status copyset

    Output:

    +------------+-----------+--------+--------+---------+
    | copysetKey | copysetId | poolId | status | explain |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    | 4294967298 | 2 | 1 | ok | |
    | 4294967299 | 3 | 1 | ok | |
    | 4294967300 | 4 | 1 | ok | |
    | 4294967301 | 5 | 1 | ok | |
    | 4294967302 | 6 | 1 | ok | |
    | 4294967303 | 7 | 1 | ok | |
    | 4294967304 | 8 | 1 | ok | |
    | 4294967305 | 9 | 1 | ok | |
    | 4294967306 | 10 | 1 | ok | |
    | 4294967307 | 11 | 1 | ok | |
    | 4294967308 | 12 | 1 | ok | |
    +------------+-----------+--------+--------+---------+
    status cluster

    get status of cluster

    Usage:

    curve fs status cluster

    Output:

    etcd:
    +---------------------+---------+----------+
    | addr | version | status |
    +---------------------+---------+----------+
    | **.***.***.**:23790 | 3.4.10 | follower |
    | **.***.***.**:23791 | 3.4.10 | follower |
    | **.***.***.**:23792 | 3.4.10 | leader |
    +---------------------+---------+----------+

    mds:
    +--------------------+--------------------+----------------+----------+
    | addr | dummyAddr | version | status |
    +--------------------+--------------------+----------------+----------+
    | **.***.***.**:6700 | **.***.***.**:7700 | 8fc48476+debug | follower |
    | **.***.***.**:6701 | **.***.***.**:7701 | 8fc48476+debug | follower |
    | **.***.***.**:6702 | **.***.***.**:7702 | 8fc48476+debug | leader |
    +--------------------+--------------------+----------------+----------+

    meataserver:
    +--------------------+--------------------+----------------+--------+
    | externalAddr | internalAddr | version | status |
    +--------------------+--------------------+----------------+--------+
    | **.***.***.**:6800 | **.***.***.**:6800 | 8fc48476+debug | online |
    | **.***.***.**:6802 | **.***.***.**:6802 | 8fc48476+debug | online |
    | **.***.***.**:6801 | **.***.***.**:6801 | 8fc48476+debug | online |
    +--------------------+--------------------+----------------+--------+

    copyset:
    +------------+-----------+--------+--------+---------+
    | copysetKey | copysetId | poolId | status | explain |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    | 4294967298 | 2 | 1 | ok | |
    | 4294967299 | 3 | 1 | ok | |
    | 4294967300 | 4 | 1 | ok | |
    | 4294967301 | 5 | 1 | ok | |
    | 4294967302 | 6 | 1 | ok | |
    | 4294967303 | 7 | 1 | ok | |
    | 4294967304 | 8 | 1 | ok | |
    | 4294967305 | 9 | 1 | ok | |
    | 4294967306 | 10 | 1 | ok | |
    | 4294967307 | 11 | 1 | ok | |
    | 4294967308 | 12 | 1 | ok | |
    +------------+-----------+--------+--------+---------+

    Cluster health is: ok

    umount

    umount fs

    umount fs from the curvefs cluster

    Usage:

    curve fs umount fs --fsname test1 --mountpoint siku-QiTianM420-N000:9002:/curvefs/client/mnt/home/siku/temp/mnt1

    Output:

    +--------+-------------------------------------------------------------------+---------+
    | fsName | mountpoint | result |
    +--------+-------------------------------------------------------------------+---------+
    | test1 | siku-QiTianM420-N000:9003:/curvefs/client/mnt/home/siku/temp/mnt2 | success |
    +--------+-------------------------------------------------------------------+---------+

    usage

    usage inode

    get the inode usage of curvefs

    Usage:

    curve fs usage inode

    Output:

    +------+----------------+-----+
    | fsId | fsType | num |
    +------+----------------+-----+
    | 2 | inode_num | 3 |
    | 2 | type_directory | 1 |
    | 2 | type_file | 0 |
    | 2 | type_s3 | 2 |
    | 2 | type_sym_link | 0 |
    | 3 | inode_num | 1 |
    | 3 | type_directory | 1 |
    | 3 | type_file | 0 |
    | 3 | type_s3 | 0 |
    | 3 | type_sym_link | 0 |
    +------+----------------+-----+
    usage metadata

    get the usage of metadata in curvefs

    Usage:

    curve fs usage metadata

    Output:

    +--------------------+---------+---------+---------+
    | metaserverAddr | total | used | left |
    +--------------------+---------+---------+---------+
    | **.***.***.**:6800 | 2.0 TiB | 182 GiB | 1.8 TiB |
    | **.***.***.**:6802 | 2.0 TiB | 182 GiB | 1.8 TiB |
    | **.***.***.**:6801 | 2.0 TiB | 182 GiB | 1.8 TiB |
    +--------------------+---------+---------+---------+

    warmup

    warmup add

    warmup a file(directory), or given a list file contains a list of files(directories) that you want to warmup.

    Usage:

    curve fs warmup add /mnt/curvefs/warmup
    curve fs warmup add --filelist /mnt/curvefs/warmup.list

    curve fs warmup add /mnt/curvefs/warmup will warmup a file(directory). /mnt/curvefs/warmup.list

    bs

    list

    list logical-pool

    list all logical pool information

    Usage:

    curve bs list logical-pool

    Output:

    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    | ID | NAME | PHYPOOLID | TYPE | ALLOC | SCAN | TOTAL | USED | LEFT | RECYCLE |
    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    | 1 | pool1 | 1 | PAGEFILE | ALLOW | true | 44 GiB | 0 B | 44 GiB | 0 B |
    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    list server

    list all server information in curvebs

    Usage:

    curve bs list server

    Output:

    +----+---------------------+------+---------+-------------------+-------------------+
    | ID | HOSTNAME | ZONE | PHYPOOL | INTERNALADDR | EXTERNALADDR |
    +----+---------------------+------+---------+-------------------+-------------------+
    | 1 | ***************_0_0 | 1 | 1 | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+ +-------------------+-------------------+
    | 2 | ***************_1_0 | 2 | | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+ +-------------------+-------------------+
    | 3 | ***************_2_0 | 3 | | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+---------+-------------------+-------------------+
    list client

    list all client information in curvebs

    curve bs list client

    Output:

    +------------+------+
    | IP | PORT |
    +------------+------+
    | 172.17.0.2 | 9000 |
    +------------+------+
    list dir

    list dir information in curvebs

    curve bs list dir --path /

    Output:

    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    | ID | FILENAME | PARENTID | FILETYPE | OWNER | CTIME | ALLOCATEDSIZE | FILESIZE |
    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    | 1 | /RecycleBin | 0 | INODE_DIRECTORY | root | 2022-11-12 16:38:25 | 0 B | 0 B |
    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    list space

    show curvebs all disk type space, include total space and used space

    curve bs list space

    Output:

    +----------+---------+---------+---------+------------+---------+
    | TYPE | TOTAL | USED | LEFT | RECYCLABLE | CREATED |
    +----------+---------+---------+---------+------------+---------+
    | physical | *** GiB | *** GiB | *** GiB | - | - |
    +----------+---------+---------+---------+------------+---------+
    | logical | *** GiB | *** GiB | *** GiB | *** GiB | *** GiB |
    +----------+---------+---------+---------+------------+---------+
    list chunkserver

    list chunkserver information in curvebs

    curve bs list chunkserver

    Output:

    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    | ID | TYPE | IP | PORT | RWSTATUS | DISKSTATE | COPYSETNUM | MOUNTPOINT | DISKCAPACITY | DISKUSED | UNHEALTHYCOPYSET | EXTADDR |
    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    | 1 | nvme | 127.0.0.1 | 8201 | READWRITE | DISKNORMAL | 100 | local:///curvebs/playground/chunkserver1/data | 39 GiB | 42140479488 | 0 % | 127.0.0.1 |
    +----+ + +------+ + +------------+-----------------------------------------------+--------------+-------------+------------------+ +
    | 2 | | | 8202 | | | 100 | local:///curvebs/playground/chunkserver2/data | 39 GiB | 42140479488 | 0 % | |
    +----+ + +------+ + +------------+-----------------------------------------------+--------------+-------------+------------------+ +
    | 3 | | | 8200 | | | 100 | local:///curvebs/playground/chunkserver0/data | 39 GiB | 42140479488 | 0 % | |
    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    list scan-status

    list curvebs all copyset that scanning is false

    curve bs list scan-status

    Output:

    +-------------+-----------+
    | LOGICALPOOL | COPYSETID |
    +-------------+-----------+
    | 1 | 1 |
    +-------------+-----------+
    | 1 | 10 |
    +-------------+-----------+
    | 1 | 100 |
    +-------------+-----------+
    | 1 | 11 |
    +-------------+-----------+
    | 1 | 12 |
    +-------------+-----------+
    | 1 | 13 |
    +-------------+-----------+
    | 1 | 14 |
    +-------------+-----------+
    | 1 | 15 |
    +-------------+-----------+
    | 1 | 16 |
    +-------------+-----------+
    | 1 | 17 |
    +-------------+-----------+
    | 1 | 18 |
    +-------------+-----------+
    | 1 | 19 |
    +-------------+-----------+
    | 1 | 2 |
    +-------------+-----------+
    | 1 | 20 |
    +-------------+-----------+
    | 1 | 21 |
    +-------------+-----------+
    | 1 | 22 |
    +-------------+-----------+
    | 1 | 23 |
    +-------------+-----------+
    | 1 | 24 |
    +-------------+-----------+
    | 1 | 25 |
    +-------------+-----------+
    | 1 | 26 |
    +-------------+-----------+
    | 1 | 27 |
    +-------------+-----------+
    | 1 | 28 |
    +-------------+-----------+
    | 1 | 29 |
    +-------------+-----------+
    | 1 | 3 |
    +-------------+-----------+
    | 1 | 30 |
    +-------------+-----------+
    | 1 | 31 |
    +-------------+-----------+
    | 1 | 32 |
    +-------------+-----------+
    | 1 | 33 |
    +-------------+-----------+
    | 1 | 34 |
    +-------------+-----------+
    | 1 | 35 |
    +-------------+-----------+
    | 1 | 36 |
    +-------------+-----------+
    | 1 | 37 |
    +-------------+-----------+
    | 1 | 38 |
    +-------------+-----------+
    | 1 | 39 |
    +-------------+-----------+
    | 1 | 4 |
    +-------------+-----------+
    | 1 | 40 |
    +-------------+-----------+
    | 1 | 41 |
    +-------------+-----------+
    | 1 | 42 |
    +-------------+-----------+
    | 1 | 43 |
    +-------------+-----------+
    | 1 | 44 |
    +-------------+-----------+
    | 1 | 45 |
    +-------------+-----------+
    | 1 | 46 |
    +-------------+-----------+
    | 1 | 47 |
    +-------------+-----------+
    | 1 | 48 |
    +-------------+-----------+
    | 1 | 49 |
    +-------------+-----------+
    | 1 | 5 |
    +-------------+-----------+
    | 1 | 50 |
    +-------------+-----------+
    | 1 | 51 |
    +-------------+-----------+
    | 1 | 52 |
    +-------------+-----------+
    | 1 | 53 |
    +-------------+-----------+
    | 1 | 54 |
    +-------------+-----------+
    | 1 | 55 |
    +-------------+-----------+
    | 1 | 56 |
    +-------------+-----------+
    | 1 | 57 |
    +-------------+-----------+
    | 1 | 58 |
    +-------------+-----------+
    | 1 | 59 |
    +-------------+-----------+
    | 1 | 6 |
    +-------------+-----------+
    | 1 | 60 |
    +-------------+-----------+
    | 1 | 61 |
    +-------------+-----------+
    | 1 | 62 |
    +-------------+-----------+
    | 1 | 63 |
    +-------------+-----------+
    | 1 | 64 |
    +-------------+-----------+
    | 1 | 65 |
    +-------------+-----------+
    | 1 | 66 |
    +-------------+-----------+
    | 1 | 67 |
    +-------------+-----------+
    | 1 | 68 |
    +-------------+-----------+
    | 1 | 69 |
    +-------------+-----------+
    | 1 | 7 |
    +-------------+-----------+
    | 1 | 70 |
    +-------------+-----------+
    | 1 | 71 |
    +-------------+-----------+
    | 1 | 72 |
    +-------------+-----------+
    | 1 | 73 |
    +-------------+-----------+
    | 1 | 74 |
    +-------------+-----------+
    | 1 | 75 |
    +-------------+-----------+
    | 1 | 76 |
    +-------------+-----------+
    | 1 | 77 |
    +-------------+-----------+
    | 1 | 78 |
    +-------------+-----------+
    | 1 | 79 |
    +-------------+-----------+
    | 1 | 8 |
    +-------------+-----------+
    | 1 | 80 |
    +-------------+-----------+
    | 1 | 81 |
    +-------------+-----------+
    | 1 | 82 |
    +-------------+-----------+
    | 1 | 83 |
    +-------------+-----------+
    | 1 | 84 |
    +-------------+-----------+
    | 1 | 85 |
    +-------------+-----------+
    | 1 | 86 |
    +-------------+-----------+
    | 1 | 87 |
    +-------------+-----------+
    | 1 | 88 |
    +-------------+-----------+
    | 1 | 89 |
    +-------------+-----------+
    | 1 | 9 |
    +-------------+-----------+
    | 1 | 90 |
    +-------------+-----------+
    | 1 | 91 |
    +-------------+-----------+
    | 1 | 92 |
    +-------------+-----------+
    | 1 | 93 |
    +-------------+-----------+
    | 1 | 94 |
    +-------------+-----------+
    | 1 | 95 |
    +-------------+-----------+
    | 1 | 96 |
    +-------------+-----------+
    | 1 | 97 |
    +-------------+-----------+
    | 1 | 98 |
    +-------------+-----------+
    | 1 | 99 |
    +-------------+-----------+
    list may-broken-vol

    list may broken volumes

    Usage:

    curve bs list may-broken-vol

    Output:

    +----------+
    | FILENAME |
    +----------+
    | test |
    +----------+

    clean-recycle

    clean the recycle bin

    Usage:

    curve bs clean-recycle --recycleprefix=/test --expiredtime=1h

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+

    query

    query file

    query the file info and actual space

    Usage:

    curve bs query file --path=/test

    Output:

    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    | ID | NAME | TYPE | OWNER | CHUNK | SEGMENT | LENGTH | SEQ | CTIME | STATUS | STRIPE | THROTTLE | ALLOC |
    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    | 1003 | test | INODE_PAGEFILE | test | 16 MiB | 1.0 GiB | 10 GiB | 1 | 2022-08-29 17:00:55 | kFileCreated | count:0 | type:IOPS_TOTAL | size:0 B |
    | | | | | | | | | | | uint:0 | limit:2000 | |
    | | | | | | | | | | | | type:BPS_TOTAL | |
    | | | | | | | | | | | | limit:125829120 | |
    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    query chunk

    query the location of the chunk corresponding to the offset

    Usage:

    curve bs query chunk --path /test1 --offset 1008600000 

    Output:

    +-------+-------------+---------+------------+----------------------+
    | CHUNK | LOGICALPOOL | COPYSET | GROUP | LOCATION |
    +-------+-------------+---------+------------+----------------------+
    | 61 | 1 | 61 | 4294967357 | ***.***.***.***:**** |
    | | | | | ***.***.***.***:**** |
    | | | | | ***.***.***.***:**** |
    +-------+-------------+---------+------------+----------------------+
    query segment

    query the segments info of the file

    Usage:

    curve bs query seginfo --path /test1 

    Output:

    +-------------+-------------+-----------+------------+---------+-------+
    | LOGICALPOOL | SEGMENTSIZE | CHUNKSIZE | START | COPYSET | CHUNK |
    +-------------+-------------+-----------+------------+---------+-------+
    | 1 | 1073741824 | 16777216 | 0 | 1 | 1 |
    + + + + +---------+-------+
    | ...... |
    + + + +------------+---------+-------+
    | | | | 9663676416 | 1 | 101 |
    + + + + +---------+-------+
    | ...... |
    + + + + +---------+-------+
    | | | | | 99 | 99 |
    +-------------+-------------+-----------+------------+---------+-------+
    query scan-status

    quey ScanStatus Info in bs

    Usage:

    curve bs query scan-satus --copysetid 1 --logicalpoolid 1

    Output:

    +-------------+-----------+-------+-------------+--------------------+
    | LOGICALPOOL | COPYSETID | SCAN | LASTSCANSEC | LASTSCANCONSISTENT |
    +-------------+-----------+-------+-------------+--------------------+
    | 1 | 1 | false | 1684425801 | true |
    +-------------+-----------+-------+-------------+--------------------+

    status

    status etcd

    get the etcd status of curvebs

    Usage:

    curve bs status etcd

    Output:

    +---------------------+---------+----------+
    | ADDR | VERSION | STATUS |
    +---------------------+---------+----------+
    | ***.***.*.***:***** | 3.4.10 | follower |
    +---------------------+ + +
    | ***.***.*.***:***** | | |
    +---------------------+ +----------+
    | ***.***.*.***:***** | | leader |
    +---------------------+---------+----------+
    status mds

    get the mds status of curvebs

    Usage:

    curve bs status mds

    Output:

    +-------------------+-------------------+-------------------+----------+
    | ADDR | DUMMYADDR | VERSION | STATUS |
    +-------------------+-------------------+-------------------+----------+
    | **.***.**.**:**** | **.***.**.**:**** | ci+562296c7+debug | follower |
    +-------------------+-------------------+ + +
    | **.***.**.**:**** | **.***.**.**:**** | | |
    +-------------------+-------------------+ +----------+
    | **.***.**.**:**** | **.***.**.**:**** | | leader |
    +-------------------+-------------------+-------------------+----------+
    status client

    get the client status of curvebs

    Usage:

    curve bs status client

    Output:

    +-------------+----------------+---------------------+-----+
    | TYPE | VERSION | ADDR | NUM |
    +-------------+----------------+---------------------+-----+
    | nebd-server | 9.9.9+2c4861ca | ***.***.**.***:**** | 2 |
    + + +---------------------+ +
    | | | ***.***.**.***:**** | |
    +-------------+----------------+---------------------+-----+
    status snapshotserver

    get the mds status of curvebs

    Usage:

    curve bs status snapshotserver

    Output:

    +---------------------+---------------------+-------------------+----------+
    | ADDR | DUMMYADDR | VERSION | STATUS |
    +---------------------+---------------------+-------------------+----------+
    | ***.***.**.***:**** | ***.***.**.***:**** | ci+562296c7+debug | follower |
    +---------------------+---------------------+ + +
    | ***.***.**.***:**** | ***.***.**.***:**** | | |
    +---------------------+---------------------+ +----------+
    | ***.***.**.***:**** | ***.***.**.***:**** | | leader |
    +---------------------+---------------------+-------------------+----------+
    status chunkserver

    get the chunkserver status of curvebs

    Usage:

    curve bs status chunkserver

    Output:

    +------------------+------------------+----------------+--------+------------+
    | EXTERNALADDR | INTERNALADDR | VERSION | STATUS | RECOVERING |
    +------------------+------------------+----------------+--------+------------+
    | **************** | **************** | d9b6bb98+debug | online | false |
    +------------------+------------------+ + + +
    | **************** | **************** | | | |
    +------------------+------------------+ + + +
    | **************** | **************** | | | |
    +------------------+------------------+----------------+--------+------------+
    status copyset

    get the copyset status of curvebs

    Usage:

    curve bs status copyset

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | LOGGAP | EXPLAIN |
    +------------+-----------+--------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | 0 | |
    +------------+-----------+ + +--------+---------+
    | ...... | ...... | ...... | ...... | ...... | ...... |
    +------------+-----------+ + +--------+---------+
    | 4294967395 | 99 | | | 0 | |
    +------------+-----------+--------+--------+--------+---------+

    delete

    delete peer

    delete the peer from the copyset

    Usage:

    curve bs delete peer

    Output:

    +------------------+------------------+---------+---------+--------+
    | LEADER | PEER | COPYSET | RESULT | REASON |
    +------------------+------------------+---------+---------+--------+
    | 127.0.0.1:8201:0 | 127.0.0.1:8202:0 | (1:29) | success | null |
    +------------------+------------------+---------+---------+--------+
    delete volume
    delete volume clone

    delete volume clone tasks in curvebs cluster

    Usage:

    curve bs delete volume clone

    Output:

    +------+--------------------------------------+--------------------------------------+-------+---------+            
    | USER | SRC | TASK ID | FILE | RESULT |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    | root | a19b5e5e-b306-488f-8e6d-d87282c869cb | d26e27a8-fcbd-4f7a-adf8-53795217cbb0 | /root | success |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    delete volume recover

    delete volume recover tasks in curvebs cluster

    Usage:

    curve bs delete volume recover

    Output:

    +------+--------------------------------------+--------------------------------------+-------+---------+             
    | USER | SRC | TASK ID | FILE | RESULT |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    | root | a19b5e5e-b306-488f-8e6d-d87282c869cb | 9dfa8699-a275-4891-8ec2-e447a0ccc77c | /root | success |
    +------+--------------------------------------+--------------------------------------+-------+---------+

    update

    update peer

    reset peer

    Usage:

    curve bs update peer 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1

    Output:

    +----------------------+---------+---------+--------+
    | PEER | COPYSET | RESULT | REASON |
    +----------------------+---------+---------+--------+
    | 127.0.0.0:8200:0 | (1:1) | success | null |
    +----------------------+---------+---------+--------+
    update leader

    transfer leader

    Usage:

    curve bs update leader 127.0.0.1:8202:0 --logicalpoolid=1 --copysetid=1 --peers=127.0.0.1:8200:0,127.0.0.1:8201:0,127.0.0.1:8202:0

    Output:

    +-----------------------+-----------------------+---------+---------+
    | LEADER | OLDLEADER | COPYSET | RESULT |
    +-----------------------+-----------------------+---------+---------+
    | ***.***.**.***:****:* | ***.***.**.***:****:* | (1:1) | success |
    +-----------------------+-----------------------+---------+---------+
    update file

    expand pagefile

    Usage:

    curve bs update file --path /test2/test1 --size 10

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    update throttle

    update file throttle params

    Usage:

    curve bs update throttle --path /test1 --type=bps_total --limit 20000

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    update scan-state

    enable/disable scan for logical pool

    Usage:

    curve bs update scan-state --logicalpoolid 1 [--scan=true/false]

    Output:

    +----+------+---------+--------+
    | ID | SCAN | RESULT | REASON |
    +----+------+---------+--------+
    | 1 | true | success | null |
    +----+------+---------+--------+
    update copyset availflag

    update copyset availflag

    Usage:

    curve bs update copyset availflag --availflag=true [--dryrun=true/false]

    Output:

    +--------+-----------+---------------+--------+
    | POOLID | COPYSETID | AVAILFLAG | DRYRUN |
    +--------+-----------+---------------+--------+
    | 1 | 1 | false => true | true |
    +--------+-----------+---------------+--------+
    update leader-schedule

    "rapidly transfer leader

    Usage:

    curve bs update leader-schedule --logicalpoolid 1
    curve bs update leader-schedule --all

    Output:

    +---------+--------+
    | RESULT | REASON |
    +---------+--------+
    | success | null |
    +---------+--------+

    create

    create file

    create pagefile

    Usage:

    curve bs create file --path /test2/test4  --size 10

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    create dir

    create directory

    Usage:

    curve bs create dir --path /test2/test5 

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+

    check

    check copyset

    check copysets health in curvebs

    Usage:

    curve bs check copyset --copysetid 1 --logicalpoolid 1

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | LOGGAP | EXPLAIN |
    +------------+-----------+--------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | 0 | |
    +------------+-----------+--------+--------+--------+---------+
    check chunkserver

    check chunkserver health in curvebs

    Usage:

    curve bs check chunkserver --chunkserverid 1

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | CHUNKSERVERID | HELATHYCOUNT | UNHEALTHYCOUNT | UNHEALTHYRATIO |
    +------------+-----------+--------+--------+--------+---------+
    | 1 | 100 | 0 | 0.00% |
    +------------+-----------+--------+--------+--------+---------+
    check server

    check copysets health in server

    Usage:

    curve bs check server --serverid 1
    curve bs check server --ip 127.0.0.1 --port 8200

    Output:

    +--------+-----------+-------+------------------+
    | SERVER | IP | TOTAL | UNHEALTHYCOPYSET |
    +--------+-----------+-------+------------------+
    | 1 | 127.0.0.1 | 100 | 0(0%) |
    +--------+-----------+-------+------------------+

    snapshot

    snapshot copyset

    take snapshot for copyset

    Usage:

    curve bs snapshot copyset 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1

    Output:

    +-----------------------+---------+---------+
    | PEER | COPYSET | RESULT |
    +-----------------------+---------+---------+
    | ***.***.**.***:****:* | (**:**) | success |
    +-----------------------+---------+---------+
    curve bs snapshot copyset --all

    Output:

    +----------------+---------+
    | CHUNKSERVER | RESULT |
    +----------------+---------+
    | **.*.*.**:8200 | failed |
    +----------------+---------+
    | **.*.*.**:8201 | success |
    +----------------+ +
    | **.*.*.**:8202 | |
    +----------------+---------+

    Comparison of old and new commands

    curve fs

    oldnew
    curvefs_tool check-copysetcurve fs check copyset
    curvefs_tool create-fscurve fs create fs
    curvefs_tool create-topologycurve fs create topology
    curvefs_tool delete-fscurve fs delete fs
    curvefs_tool list-copysetcurve fs list copyset
    curvefs_tool list-fscurve fs list fs
    curvefs_tool list-fscurve fs list mountpoint
    curvefs_tool list-partitioncurve fs list partition
    curvefs_tool query-copysetcurve fs query copyset
    curvefs_tool query-fscurve fs query fs
    curvefs_tool query-inodecurve fs query inode
    curvefs_tool query-metaservercurve fs query metaserver
    curvefs_tool query-partitioncurve fs query partition
    curvefs_tool status-mdscurve fs status mds
    curvefs_tool status-metaservercurve fs status metaserver
    curvefs_tool status-etcdcurve fs status etcd
    curvefs_tool status-copysetcurve fs status copyset
    curvefs_tool status-clustercurve fs status cluster
    curvefs_tool umount-fscurve fs umount fs
    curvefs_tool usage-inodecurve fs usage inode
    curvefs_tool usage-metadatacurve fs usage metadata

    curve bs

    oldnew
    curve_ops_tool logical-pool-listcurve bs list logical-pool
    curve_ops_tool get -fileName=curve bs query file -path
    curve_ops_tool etcd-statuscurve bs status etcd
    curve_ops_tool mds-statuscurve bs status mds
    curve_ops_tool server-listcurve bs list server
    curve_ops_tool client-listcurve bs list client
    curve_ops_tool deletecurve bs delete file
    curve_ops_tool listcurve bs list dir
    curve_ops_tool createcurve bs create file/dir
    curve_ops_tool seginfocurve bs query seginfo
    curve_ops_tool chunk-locationcurve bs query chunk
    curve_ops_tool remove-peercurve bs delete peer
    curve_ops_tool reset-peercurve bs update peer
    curve_ops_tool spacecurve bs list space
    curve_ops_tool update-throttlecurve bs update throttle
    curve_ops_tool check-copysetcurve bs check copyset
    curve_ops_tool client-statuscurve bs status client
    curve_ops_tool check-operatorcurve bs check operator
    curve_ops_tool snapshot-clone-statuscurve bs status snapshotserver
    curve_ops_tool transfer-leadercurve bs update leader
    curve_ops_tool do-snapshotcurve bs snapshot copyset
    curve_ops_tool set-scan-statecurve bs update scan-state
    curve_ops_tool chunkserver-statuscurve bs status chunkserver
    curve_ops_tool chunkserver-listcurve bs list chunkserver
    curve_ops_tool set-copyset-availflagcurve bs update copyset availflag
    curve_ops_tool scan-statuscurve bs list/query scan-status
    curve_ops_tool clean-recyclecurve bs clean-recycle
    curve_ops_tool copysets-statuscurve bs status copyset
    curve_ops_tool list-may-broken-volcurve bs list may-broken-vol
    curve_ops_tool rapid-leader-schedulecurve bs update leader-schedule
    curve_ops_tool do-snapshot-allcurve bs snapshot --all
    curve_ops_tool check-chunkservercurbe bs check chunkserver
    curve_ops_tool status
    curve_ops_tool check-consistency
    curve_ops_tool check-servercurve bs check server
    - - + + \ No newline at end of file diff --git a/CurveBS/performance/how-to-benchmark.html b/CurveBS/performance/how-to-benchmark.html index 5820d15..906dcc8 100644 --- a/CurveBS/performance/how-to-benchmark.html +++ b/CurveBS/performance/how-to-benchmark.html @@ -4,13 +4,13 @@ benchmark指南 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git "a/CurveBS/test/ci\344\273\213\347\273\215.html" "b/CurveBS/test/ci\344\273\213\347\273\215.html" index 0d1e278..9478b40 100644 --- "a/CurveBS/test/ci\344\273\213\347\273\215.html" +++ "b/CurveBS/test/ci\344\273\213\347\273\215.html" @@ -4,13 +4,13 @@ 概述 | Curve Book - - + +
    跳到主要内容

    概述

    本文将主要介绍社区人员在参与curve项目开发过程中如何快速提交PR并进行CI测试,以及快速定位CI失败原因。

    如何提交PR

    在完成代码编写后,就可以提交 PR。当然如果开发尚未完成,在某些情况下也可以先提交 PR,比如希望先让社区看一下大致的解决方案,可以在完成代码框架后提价 PR。

    Curve 编译环境快速构建

    Curve 测试用例编译及执行

    对于 PR 我们有如下要求:

    • CURVE编码规范严格按照Google C++开源项目编码指南来进行代码编写,请您也遵循这一指南来提交您的代码。
    • 代码必须有测试,文档除外,单元测试(增量行覆盖80%以上,增量分支覆盖70%以上);集成测试(与单元测试合并统计,满足单元测试覆盖率要求即可)
    • 请尽可能详细的填写 PR 的描述,关联相关问题的 issuse,PR commit message 能清晰看出解决的问题。
    • CI 通过之后可开始进行 review,每个 PR 在合并之前都需要至少得到两个 Committer/Maintainer 的 LGTM。
    • PR 代码需要一定量的注释来使代码容易理解,且所有注释和 review 意见和回复均要求使用英语。

    对于 commit message:

    一条好的 commit message 需要包含以下要素:

    1. What is your change?(必须)
    2. Why this change was made?(必须)
    3. What effect does the commit have? (可选)

    说明提交的 PR 做了那些修改:性能优化?修复bug?增加功能?以及这么做的原因。最后描述以下这样修改带来的影响,包括性能等等。当然对一些简单的修改,修改的原因和影响可以忽略。在 message中尽量遵循以下原则:

    • 总结说明 PR 的功能和作用
    • 使用短句和简单的动词
    • 避免长的复合词和缩写

    在提交时请尽可能的遵循以下格式:

    [type]<scope>: <description>
    <BLANK LINE>
    [body]
    <BLANK LINE>
    [footer]

    type 可以是以下类型之一:

    • build: 影响系统构建以及外部依赖
    • ci: 影响持续继承相关的功能
    • docs: 文档相关的修改
    • feat: 增加新的特性
    • fix: bug 修复
    • perf: 性能提升
    • refactor: 重构相关的代码,不增加功能也不修复错误
    • style: 不影响代码的的含义的修改,仅仅是修改代码风格
    • test: 单元测试相关的修改

    第一行表示标题应尽可能保持 70 个字符以内,阐述修改的模块以及内容,多模块可以使用 * 来表示,并在正文阶段说明修改的模块。

    footer 是可选的,用来记录对应因为这些更改而可以关闭的 issue,如 Close #12345

    CI 测试过程

    PR提交到 Curve master 分支后需要在comment中输入cicheck触发Curve CI,需保证 CI 通过,CI 的 Jenkins 用户名密码为 netease/netease,如遇到 CI 运行失败可以登录 Jenkins 平台查看失败原因。测试内容包括:

    1) cpplint静态检查

    主要进行测试内容为:

    cpplint --linelength=80 --counting=detailed --output=junit --filter=-build/c++11 --quiet $( find . -name .h -or -name .cc -or -name *.cpp ) 2>&1 | tee cpplint-style.xml

    2) 单元测试

    单元测试主要执行的就是仓库中:https://github.com/opencurve/curve/blob/master/ut.sh

    为提升测试效率,所有单元测试用例为并行执行,需要注意增加的单元测试里起的服务不能和其他用例里起的服务使用同一端口,否则会因为端口占用失败

    测试结束后进行覆盖率的卡点校验,当前覆盖率配置为:

    if [ $1 == "curvebs" ];then

    check_repo_branch_coverage 59
    check_repo_line_coverage 76

    ## two arguments are module and expected branch coverage ratio
    check_module_branch_coverage "src/mds" 70
    check_module_branch_coverage "src/client" 78
    check_module_branch_coverage "src/chunkserver" 65
    check_module_branch_coverage "src/snapshotcloneserver" 65
    check_module_branch_coverage "src/tools" 65
    check_module_branch_coverage "src/common" 65
    check_module_branch_coverage "src/fs" 65
    check_module_branch_coverage "src/idgenerator" 79
    check_module_branch_coverage "src/kvstorageclient" 70
    check_module_branch_coverage "src/leader_election" 100
    check_module_branch_coverage "nebd" 75

    elif [ $1 == "curvefs" ];then

    check_module_branch_coverage "mds" 59
    check_module_branch_coverage "client" 59
    check_module_branch_coverage "metaserver" 65
    check_module_branch_coverage "common" 16
    check_module_branch_coverage "tools" 0
    fi

    测试结束后会输出覆盖率报告,例如: http://59.111.91.248:8080/job/curve_untest_job/6149/HTML_20Report/

    如果测试失败,可以打开对应的curve_untest_job的控制台输出日志,直接页面拉到最后会打印出错误的原因,覆盖率不足会打印在最后,如果是单元测试用例失败,可以从下往上翻页,找到失败的地方,比如:

    [  FAILED  ] 1 test, listed below:
    [ FAILED ] EtcdClientTest.GetEtcdClusterStatus

    1 FAILED TEST
    test bazel-bin/test/mds/server/mds-test.log log is --------------------------------------------->>>>>>>>
    Running main() from gmock_main.cc
    [==========] Running 1 test from 1 test case.
    [----------] Global test environment set-up.
    [----------] 1 test from MDSTest
    [ RUN ] MDSTest.common
    2022-12-07 18:02:48.753204 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:49.753500 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:50.753831 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:51.754211 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:52.755814 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:53.756543 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    test/mds/server/mds_test.cpp:76: Failure
    Value of: initSuccess
    Actual: false
    Expected: true

    3) pjdfstest测试

    pjdfstest 主要是用来测试curvefs POSIX上的兼容性,会部署单机版的curvefs环境,然后进行pjdtest的执行,用的工具在 https://github.com/pjd/pjdfstest 。可以使用如下脚本进行自行测试:

    #!/usr/bin/env bash

    set -e

    git clone https://github.com/pjd/pjdfstest.git
    cd pjdfstest
    autoreconf -ifs
    ./configure
    make pjdfstest
    cd ..
    mkdir tmp
    cd tmp
    # must be root!
    sudo prove -r -v --exec 'bash -x' ../pjd*/tests
    cd ..
    rm -rf tmp pjd*

    如果测试失败,也需要进入控制台输出,拉到最后查看具体失败的原因

    3) failover异常自动化

    异常自动化会进行整体真实环境部署和io注入、故障测试,主要流程:

    主要流程:

    初始化集群 → 用户io注入 (数据面) → 并发调用管理接口(管控面) → 触发异常 → sleep a time → 异常恢复(恢复校验) → 数据校验(ioerror、读写一致性、三副本一致性、抖动) → 管理接口校验 &资源回收等校验

    测试完成后会输出整体的测试报告,具体测试过程和测试结果可以点击对应提交的curve_failover_job 下的log.html , 可以看到比较完整的测试流程、步骤、操作和结果输出,比如 log.html

    常见问题

    如何登录jenkins?

    由于内部的一些权限要求,需要用用户名密码登录jenkins ,开源社区的可以使用netease/netease 的账号/密码进行登录

    如果再次触发失败pr的CI 构建?

    可以打开pr ,在最底部comment ''recheck" 字段,进行再次触发构建

    如何过滤CI?

    对于一些文档修改或其他不影响代码流程的内容,我们可以不进行CI构建,可以在创建PR的时候在标题增加[skipci] 字段

    触发构建后立刻返回了失败?

    查看一下失败的job内容,多数是因为测试任务在进行排队等待。如果job是waiting_in_line ,就是在排队

    - - + + \ No newline at end of file diff --git "a/CurveBS/test/curve\350\246\206\347\233\226\347\216\207\346\224\266\351\233\206.html" "b/CurveBS/test/curve\350\246\206\347\233\226\347\216\207\346\224\266\351\233\206.html" index 825daa3..94f0fb7 100644 --- "a/CurveBS/test/curve\350\246\206\347\233\226\347\216\207\346\224\266\351\233\206.html" +++ "b/CurveBS/test/curve\350\246\206\347\233\226\347\216\207\346\224\266\351\233\206.html" @@ -4,13 +4,13 @@ curve覆盖率收集优化 | Curve Book - - + +
    跳到主要内容

    curve覆盖率收集优化

    curve主要从2个维度衡量单元测试覆盖率:行覆盖率和分支覆盖率。

    项目组内规定,所有模块行覆盖率需要达到70%;分支覆盖率mds模块需要达到70%,其他模块需要达到65%。否则ci自动失败,代码无需进入review阶段,更没法进入仓库。

    先看看效果:

    我们在ci配置了单元测试覆盖率收集的job,每次提交代码都会触发编译、单元测试和覆盖率收集。

    一些覆盖率收集的注意事项:

    1.因为我们是用bazel编译的,而bazel自带的coverage(继承自test, test继承自build)参数并不成熟,因此,不考虑使用coverage参数,直接使用build参数;build需要带上--collect_code_coverage参数

    2.bazel编译时,需要注意build文件的放置,因为覆盖率解析的时候需要gcno和gcda文件路径匹配,基本上src每个目录均会放置BUILD,层层依赖

    3.覆盖率收集会收集到很多第三方依赖等代码的覆盖率,影响我们自己的覆盖率,因此lcov需要通过--remove参数去除这些依赖的覆盖率收集

    4.分支覆盖的时候,所有跳转都被计算成分支,而我们真正的关心的是类似那些if...else的分支,因此需要通过脚本解析,去除这些不需要的分支,思路如下:

    (a.按照lcov常规的方法收集覆盖率

    b.解析lcov生成的文件中的每一行, SF:开头的行为源文件的名称 BRDA:开头的行为分支覆盖检测的那一行,其中分a,b,c,d,a代表源文件的代码行,b和c是内部定义的值,大概是代码块等信息;d代表是否覆盖,0表示未覆盖,其他数字代表覆盖次数

    c.去源文件中去除注释,搜索是否是if开头的,如果不是,则将这个BRDA去除 )

    d.我们已经直接提供了脚本filterbr.py gen-coverage.py,直接运行gen-coverage.py即可,所有文件保留在./coverage目录下面

    5.一些很难覆盖到的行或者分支,比如abort()等,可以通过参数过滤,但是不建议使用,参数配置如下,直接在源代码中添加"//LCOV_EXCL_BR_LINE"去除该行分支覆盖率统计或者"//LCOV_EXCL_LINE"去除该行行覆盖率统计。

    lcov_excl_br_line = LCOV_EXCL_BR_LINE|g_return_if_fail|g_return_val_if_fail

    lcov_excl_line = LCOV_EXCL_LINE|g_return_if_reached|g_return_val_if_reached|g_assert_not_reached

    - - + + \ No newline at end of file diff --git a/CurveBS/test/env-setup.html b/CurveBS/test/env-setup.html index daf8fe1..5048418 100644 --- a/CurveBS/test/env-setup.html +++ b/CurveBS/test/env-setup.html @@ -4,13 +4,13 @@ 测试环境配置 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git "a/CurveBS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" "b/CurveBS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" index 4801e71..c2d678e 100644 --- "a/CurveBS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" +++ "b/CurveBS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" @@ -4,15 +4,15 @@ 异常自动化方法 | Curve Book - - + +
    跳到主要内容

    异常自动化方法

    #异常测试方法 ##测试目的

    • Curve是网易云存储产品组推出的新一代分布式存储平台,致力于打造一个支持块、对象、文件等存储形态的统一平台。本次测试对象主要为支持块设备功能。

    • 生产环境中,故障是一种常态,测试环境中需要尽可能的模拟生产环境中的故障,以此来评估系统的可用性、一致性等。

    ##测试方法

    • 异常测试的环境部署要求尽可能与真实环境一致,当前测试环境定义为6台chunkserver节点,1台mds节点(尚无failover),2台client节点,测试场景尽可能模拟用户使用的方式,读写io由云主机内部下发,管理接口直接采用openstack的接口。

    • 异常项主要通过人为注入的方式触发,有些异常为真实的异常,例如机器宕机、拔网线等;有些异常通过软件模拟的方式,如磁盘io卡顿、服务异常等;有些异常则是在代码侧注入,该部分异常测试主要在集成测试中,不在这里讨论。

    • 异常测试主要校验真实用户场景中,在异常触发情况下的系统行为,按影响面可以分为:用户无感知、影响管理面基本功能、引起性能衰减、引起io抖动、系统不可服务但可恢复、数据异常(ioerror、不一致、丢失)、系统无法恢复。

    • 借鉴混沌工程的理论,测试用例设计的时候也会尽量符合下面原则,当然我们目前只是在测试环境批跑,条件允许的情况下,可以在beta云环境批跑,直到稳定后,可以去A级环境批跑。

    • 建立稳定状态的假设;

    • 多样化现实世界事件;

    • 在生产环境运行实验;

    • 持续自动化运行实验;

    • 最小化“爆炸半径”。 ##测试用例

    • 一个最基本的异常测试用例格式如下:

    • 初始化集群 → 用户io注入 → 并发调用管理接口 → 触发异常 → sleep a random time → 异常恢复(恢复校验) → 数据校验(ioerror、读写一致性、三副本一致性、抖动) → 管理接口校验 → 资源回收校验

    - - + + \ No newline at end of file diff --git "a/CurveBS/test/\346\265\213\350\257\225\345\267\245\345\205\267.html" "b/CurveBS/test/\346\265\213\350\257\225\345\267\245\345\205\267.html" index f357cfc..993fdb5 100644 --- "a/CurveBS/test/\346\265\213\350\257\225\345\267\245\345\205\267.html" +++ "b/CurveBS/test/\346\265\213\350\257\225\345\267\245\345\205\267.html" @@ -4,13 +4,13 @@ 性能测试 | Curve Book - - + +
    跳到主要内容

    性能测试

    背景知识

    • 单盘的性能测试基本与传统物理硬盘的测试方法无差异

    • 一般性能指标为:

      iops:每秒可以执行读写的次数

      带宽:每秒最大带宽

      延迟:每个io从发起请求到返回时的延迟时间

    基准测试的主要测试案例是:

    • 线性读写(大块,长队列),单位MB/s

    • IOPS(每秒输入/输出操作数)中的小块(4-8kb,iodepth=16-128)的高度并行随机读写

    • IOPS中的单线程事务性随机写入(4-8kb,iodepth = 1)和读取

    • 一般情况下,我们用4k随机写来测量iops和延迟,用大块(64k, 512k等)顺序写来测量带宽。

    • 云盘的性能测试也是梯度加压的模式,在加大iodepth的时候,当我们观察到iops或者带宽基本不再增长,而延迟还在持续上升,我们可以认为压到极限了。一般情况下,我们的测试深度不会超过128,再高基本就没有意义了,所以简单评测的时候,可以直接把深度压到128。

    • 测试工具一般使用fio

    测试工具

    测试工具适用场景说明下载地址
    fio适用于裸盘情况下的性能测试fio是测试IOPS的非常好的工具,用来对硬件进行压力测试和验证,支持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network, syslet, guasi, solarisaio 等等http://freecode.com/projects/fio
    vdbench适用于文件系统盘情况下的性能测试vdbench是一个 I/O 工作负载生成器,用于验证数据完整性和度量直接附加和网络连接的存储的性能。它是一个免费的工具,容易使用,而且常常用于测试测试大量文件、目录的创建、删除性能,以及对文件的读写性能。https://www.oracle.com/technetwork/cn/server-storage/vdbench-downloads-1901681-zhs.html

    注意事项

    • 在测试之前尝试禁用驱动器缓存:hdparm -W 0 /dev/sdX(SATA驱动器),sdparm --set WCE=0 /dev/sdX(SAS驱动器)。通常,这是服务器固态硬盘(例如 INTEL SSDSC2BB80),因为它使随机写入iops 增加了两个数量级以上*(从288 iops到18000 iops!)。在某些情况下,它可能没有任何改善,因此请同时尝试使用-W0和-W1选项

    • 推荐的fio裸盘基准测试工具:

    • 从VM内部或通过内核RBD驱动程序(krbd)是相同的:

    1. fio -ioengine=libaio -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -runtime=300 -filename=/dev/rbdX

    2. fio -ioengine=libaio -direct=1 -sync=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=300 -filename=/dev/rbdX

    3. fio -ioengine=libaio -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=300 -filename=/dev/rbdX

      然后重复执行rw=read/randread

      这是为了进行如下测试:

      • 可能的最佳延迟
      • 线性带宽
      • 随机访问iops

      从空的卷读取非常快,因此在测试之前预先填满存储卷。

    • 切勿使用dd测试磁盘性能。

    • 不要使用一些存储自带的测试工具,例如ceph的rados bench。它创建少量对象(每个线程1-2个),因此所有对象始终驻留在缓存中,并且改善了结果,超出了应有的范围。

    • 除非绝对确定需要,否则请不要使用RAID。所有驱动器均应使用直通(HBA)模式进行连接

    • RAID和HBA/SATA之间的IOPS差异可能非常明显。损坏或旧的RAID控制器很容易成为瓶颈

    • 有一件事情可以一次减少2-3倍的延迟。它禁用了CPU的所有节能功能:

      • cpupower idle-set -D 1—— 这将禁用C状态(或者您可以将processor.max_cstate = 1 intel_idle.max_cstate = 0传递给内核命令行)
      • for i in $(seq 0 $((nproc-1))); do cpufreq-set -c $i -g performance; done ——这将禁用频率缩放。
    • 禁用节能功能后,CPU会作为GTX发热,但iops会增加2-3倍

    单块裸盘性能测试

    单块云硬盘的性能即将整个存储池的性能都给这块云硬盘使用,是测试client的极限性能。

    我们需要从多个维度去测量单块云硬盘的性能,包括块大小按4k~512k的梯度上升,iodepth从1~128梯度上升,同时测试不同的读写模式。

    可以参考如下测试方法:

    • 线性读取:fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=read -runtime=300 -filename=/dev/sdX
    • 线性写入:fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=write -runtime=300 -filename=/dev/sdX
    • 峰值并行随机读取:fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randread -runtime=300 -filename=/dev/sdX
    • 单线程读取延迟:fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randread -runtime=300 -filename=/dev/sdX
    • 峰值并行随机写入:fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=300 -filename=/dev/sdX
    • 日志写入延迟:fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=write -runtime=300 -filename=/dev/sdX。还可以使用-fsync=1而不是-sync=1进行尝试,并记下最差的结果,因为有时syncfsync会被杂乱的硬件忽略。
    • 单线程随机写入延迟fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=300 -filename=/dev/sdX

    多盘裸盘性能测试

    一般分布式的存储我们会考虑压测集群的性能,即通过增加client的数量,来获取server的极限性能。

    测试前,我们会根据当前集群的容量准备一些铺底数据,一般要达到总容量的20%以上。

    多盘性能测试与单盘在原理上无差异,可以理解为增加了压测client的数量。

    多盘的性能iops和带宽我们一般取所有被测云盘性能的总和,延迟我们取所有云盘延迟的平均值。

    单客户端文件系统盘性能测试

    (1)vdbench是java开发的测试工具,需要java环境。

    ​ apt-get install openjdk-7-jdk -y

    (2)做文件系统

    ​ mkfs.ext4 /dev/vdc (vdc为云盘)

    (3) mount设备

    ​ mount /dev/vdc /mnt

    (4)在vdbench文件目录下执行:./vdbench -t

    ​ 出现如下信息则说明可正常使用

    vdbench测试需要写测试配置文件。在vdbench 目录下创建profile文件。

    ​ 单节点配置可参考我的如下测试配置文件

    hd=default,vdbench=/home/chenyunhui/vdbench50406,user=root,shell=ssh
    fsd=fsd1,anchor=/mnt,depth=1,width=10,files=10,size=1000m,shared=yes,openflags=o_direct
    fwd=fwd1,fsd=fsd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1

    该配置为10 10 1G的文件数据,具体文件数据可以根据底层集群规模来定。

    其中比较重要的参数为:

    anchor= 将在其中创建目录结构的目录

    width= 要在定位符下创建的目录数

    depth= 要在定位符下创建的级别数(目录深度)

    files= 要在最低级别创建的文件数

    sizes= (size,size,...) 将创建的文件大小

    distribution= bottom(如果希望仅在最低级别创建文件)和 all(如果希望在所有目录中创建文件)

    fileio= random 或 sequential,表示文件 I/O 将执行的方式。

    fileselect= random 或 sequential,标识选择文件或目录的方式。

    xfersizes= 数据传输(读取和写入操作)处理的数据大小。

    rdpct= 读取和写入操作的百分比。0为全写

    threads= 此工作负载的并发线程数量。每个线程需要至少 1 个文件

    elapsed= 以秒为单位的运行持续时间。默认设置为 30

    vdbench可以在测试时增加-jn的参与用于测试时同时进行数据校验。vdbench每次写操作都会记录在一个表里,对每个512字节数据 的8字节逻辑字节地址和1字节的校验值进行记录,校验值中会记录为第几次写。第一次创建写为00,后续每次覆盖写加1。再次运行时会读出之前的校验日志并进行数据校验。

    读写数据测试和记录校验数据使用命令:

    ./vdbench -jn -f profile 执行该命令时同时会进行数据校验。

    但是需要注意,使用vdbench进行一致性校验测试更加适宜于稳定性测试和一致性测试,性能测试过程中使用会影响性能

    最后产生的测试结果都是output下面

    errorlog.html

    当为测试启用了数据验证时,它可包含一些数据块中的错误的相关信息:

    • 无效的密钥读取
    • 无效的 lba 读取(一个扇区的逻辑字节地址)
    • 无效的 SD 或 FSD 名称读取
    • 数据损坏,即使在使用错误的 lba 或密钥时
    • 数据损坏
    • 坏扇区

    flatfile.html

    包含 vdbench 生成的一种逐列的 ASCII 格式的信息。

    histogram.html

    一种包含报告柱状图的响应时间、文本格式的文件。

    logfile.html

    包含 Java 代码写入控制台窗口的每行信息的副本。logfile.html 主要用于调试用途

    parmfile.html

    显示已包含用于测试的每项内容的最终结果

    resourceN-M.html、resourceN.html、resourceN.var_adm_msgs.html

    • 摘要报告
    • stdout/stderr 报告
    • 主机 N 的摘要报告
    • 最后 “nn” 行文件 /var/adm/messages 和 /var/adm/messages。每个 M 个 JVM/Slave 的目标主机 N 和主机 N 上为 0。

    sdN.histogram.html、sdN.html

    每个 N 存储定义的柱状图和存储定义 “N” 报告。

    summary.html

    主要报告文件,显示为在每个报告间隔的每次运行生成的总工作负载,以及除第一个间隔外的所有间隔的加权平均值。

    • interval:报告间隔序号
    • I/O rate:每秒观察到的平均 I/O 速率
    • MB sec:传输的数据的平均 MB 数
    • bytes I/O:平均数据传输大小
    • read pct:平均读取百分比
    • resp time:以读/写请求持续时间度量的平均响应时间。所有 vdbench 时间都以毫秒为单位。
    • resp max:在此间隔中观察到的最大响应时间。最后一行包含最大值总数。
    • resp stddev:响应时间的标准偏差
    • cpu% sys+usr:处理器繁忙 = 100(系统 + 用户时间)(Solaris、Windows、Linux)
    • cpu% sys:处理器利用率:系统时间

    多客户端文件系统盘性能测试

    多数情况下我们会进行多客户端的测试。多客户端测试参数可配置如下:

    hd=default,vdbench=/root/vdbench50406,user=root,shell=ssh
    hd=hd1,system=183.136.181.150
    hd=hd2,system=115.238.123.189

    fsd=fsd1,anchor=/mnt1,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fsd=fsd2,anchor=/mnt2,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fwd=fwd1,fsd=fsd1,host=hd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    fwd=fwd2,fsd=fsd2,host=hd2,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50


    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1,warmup=300

    上面的例子是测试两台机器的云盘,每个云盘4个线程对110 1G的文件进行读写,4K的块大小,50%的写,50%随机,热身写300s,然后测试3600s

    相比于多个客户端上跑fio,使用vdbench进行多客户端测试的好处有:

    • 1、能够每秒显示整个测试的io叠加,这样测试整个集群的io的时候,可以把所有虚机启动起来,然后进行io的压测,而不是去压单个rbd的iops,那个没有太大的意义,只能是一个数值,真正的环境大多也不是给一个业务使用的,也可以跑起一个业务以后,再看剩余的机器还能跑多少性能
    • 2、在测试输出报告里面会根据主机统计一次io,这个面向的业务场景就是,比如某台主机上面可能挂载多块云盘,那么可以根据主机进行统计
    • 3、在报告里面还会根据设备显示io个延时的信息,也就是只要是测试设备,每一个的性能指标都能查到,这个的好处就是检测集群里面的io是不是均匀的,如果做了qos,设备的测试性能值是不是跟设置限制一样
    - - + + \ No newline at end of file diff --git "a/CurveBS/test/\346\265\213\350\257\225\347\216\257\345\242\203\351\205\215\347\275\256\344\277\241\346\201\257.html" "b/CurveBS/test/\346\265\213\350\257\225\347\216\257\345\242\203\351\205\215\347\275\256\344\277\241\346\201\257.html" index c3a1f9e..6619047 100644 --- "a/CurveBS/test/\346\265\213\350\257\225\347\216\257\345\242\203\351\205\215\347\275\256\344\277\241\346\201\257.html" +++ "b/CurveBS/test/\346\265\213\350\257\225\347\216\257\345\242\203\351\205\215\347\275\256\344\277\241\346\201\257.html" @@ -4,13 +4,13 @@ Curve 测试环境配置信息 | Curve Book - - + +
    跳到主要内容

    Curve 测试环境配置信息

    Curve 当前测试的性能,如果没有特别说明,都是在基于以下配置的机器上测试的。该配置仅仅表示curve当前性能数据的测试环境,由于部分硬件版本比较旧,该环境的硬件不作为推荐硬件型号,但是对硬件的配置部分可做参考。

    集群topo

    curvebs 块存储测试集群规模

    mds和chunkserver服务混合部署。

    mds : 3台机器

    chunkserver : 9台机器 * 20块盘/每台机器

    机器选型

    计算/存储节点机型
    Dell Inc. PowerEdge R730xd
    Inspur NF5280M4

    mds 节点

    模块版本配置
    机型Dell PowerEdge R730xd
    osdebain9
    内核4.9.65
    cpu设置performance,开启cpu性能最大化
    网卡Bonding Mode: IEEE 802.3ad Dynamic link aggregation Transmit Hash Policy: layer2+3 (2) MII Status: up MII Polling Interval (ms): 100 Up Delay (ms): 200 Down Delay (ms): 200万M网卡双网口组bond
    系统盘1.2T*2 SAS (RAID1模式) 初始化之后进行拷盘测试,提前发现并剔除坏盘、慢盘等异常
    数据盘1.8T*2 SSD (不需要RAID) 初始化之后进行拷盘测试,提前发现并剔除坏盘、慢盘等异常

    chunkserver 节点

    模块版本配置
    机型inspur NF5280M4
    osdebian9
    内核4.9.65
    cpu设置performance,开启cpu性能最大化
    megaraid_sas06.811.02.00-rc4
    数据盘raid卡配置1.RAID FW 版本:24.21.0-0061 2.RAID卡驱动版本:06.811.02.00-rc41. 配置为JBOD模式 2. RAID 卡关闭 consistent check 3.RAID卡缓存策略:WriteThrough
    数据盘磁盘数据盘类型:1.8TB * 20 Intel S45001.数据盘关闭磁盘缓存 2.磁盘调度策略,SSD盘noop,HDD盘deadline // 初始化之后进行拷盘测试,提前发现并剔除坏盘、慢盘等异常 3、 sudo tune2fs -m 0 /dev/sda8 释放文件系统预留的5%空间 4、如果至少两个raid卡,每个raid卡上数据盘尽量均衡
    网卡bond配置( Bonding Mode: IEEE 802.3ad Dynamic link aggregation Transmit Hash Policy: layer2+3 (2) MII Status: up MII Polling Interval (ms): 100 Up Delay (ms): 200 Down Delay (ms): 200 )万M网卡双网口组bond
    系统盘1.2T*2 SASRAID1模式
    fd数量1.系统允许的最大fd数量至少为10000000 2.单个进程允许的最大fd数量不低于250000
    mmap地址数量每一个bthread都需要使用mmap分配stack,mmap数量操作系统是有限制的,建议修改/proc/sys/vm/max_map_count 至少为5642720。

    一些相关的命令

    raid卡查询命令

    1. RAID FW 版本 查询命令:sudo /usr/sbin/megacli -AdpAllInfo -aALL | grep "FW Package Build"
    2. RAID 卡关闭 consistent check 查询命令:sudo /usr/sbin/megacli -AdpCcSched -info -a0
    3. RAID卡缓存策略查询命令:sudo megacli -LDGetProp -Cache -LALL -a0
    4. RAID卡驱动版本查询命令:sudo modinfo megaraid_sas

    磁盘相关命令

    1. 关闭磁盘缓存命令:sudo /sbin/hdparm -W 0 /dev/sdh
    2. 磁盘缓存是否关闭,查询命令:sudo /sbin/hdparm -W /dev/sdh
    3. 磁盘调度策略,查询命令:cat /sys/block/sdxxx/queue/schedule
    4. 释放文件系统预留空空间命令:sudo tune2fs -m 0 /dev/sda8

    修改单个进程的允许的最大fs数量

    1. 查询系统允许的最大fd数量:cat /proc/sys/fs/file-max
    2. 查询单个进程允许的最大fd数量:ulimit -n
    3. 修改单个进程允许的最大fd数量:
    修改/etc/sysctl.conf配置,然后执行sysctl -p 生效。
    echo "fs.nr_open = 2500000" >> /etc/sysctl.conf
    echo "fs.file-max = 40000000" >> /etc/sysctl.conf
    - - + + \ No newline at end of file diff --git "a/CurveBS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" "b/CurveBS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" index db65eb7..48d6751 100644 --- "a/CurveBS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" +++ "b/CurveBS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" @@ -4,15 +4,15 @@ 编译环境搭建 | Curve Book - - + +
    跳到主要内容

    编译环境搭建

    请注意:

    1. 如您只是想体验Curve的部署流程和基本功能,则不需要编译Curve,请参考 部署
    2. 本文档仅用来帮助你搭建Curve代码编译环境,便于您参与Curve的开发调试
    3. 以下镜像和编译过程目前仅支持 x86 系统
    4. 如要编译arm分支,请根据 Dockerfile打包编译镜像
    5. 目前master分支不支持在arm系统上编译运行
    6. 推荐 debian 10及以上版本的操作系统,其他操作系统未经过全面测试

    使用Docker进行编译(推荐方式)

    获取或者构建docker镜像

    方法一:从docker hub镜像库中拉取docker镜像(推荐方式)

    docker pull opencurvedocker/curve-base:build-debian9

    方法二:手动构建docker镜像

    使用工程目录下的 docker/debian9/compile/Dockerfile 进行构建,命令如下:

    docker build -t opencurvedocker/curve-base:build-debian9

    注意: 上述操作不建议在Curve工程目录执行,否则构建镜像时会把当前目录的文件都复制到docker镜像中,建议把Dockerfile拷贝到新建的干净目录下进行docker镜像的构建。

    在docker镜像中编译

    git clone https://github.com/opencurve/curve.git 或者 git clone https://gitee.com/mirrors/curve.git
    cd curve
    # 如果你想在容器内完成编译+制作+上传镜像的操作,可以添加以下参数
    # -v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker
    # --rm 会在容器退出后自动删除容器,如果你想保留容器,可以去掉该参数
    docker run --rm -v $(pwd):/curve -w /curve -v ${HOME}/.cache:${HOME}/.cache -v ${HOME}/go:${HOME}/go --user $(id -u ${USER}):$(id -g ${USER}) -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro --privileged -it opencurvedocker/curve-base:build-debian9 bash
    # (中国大陆可选)将外部依赖替换为国内下载点或镜像仓库,可以加快编译速度: bash replace-curve-repo.sh

    # curve v2.0 之前
    bash mk-tar.sh (编译 curvebs 并打tar包)
    bash mk-deb.sh (编译 curvebs 并打debian包)

    # (当前)curve v2.0 及之后
    # 编译 curvebs:
    make build stor=bs dep=1
    # or
    make dep stor=bs && make build stor=bs
    # 编译 curvefs:
    make build stor=fs dep=1
    # or
    make dep stor=fs && make build stor=fs

    注意: mk-tar.shmk-deb.sh 用于 curve v2.0 之前版本的编译打包,v2.0 版本之后不再维护。

    在物理机上编译

    Curve编译依赖的包括:

    依赖版本
    bazel4.2.2
    gcc支持c++11的兼容版本

    Curve的其他依赖项,均由bazel去管理,不可单独安装。

    注意: 4.* 版本的 bazel 均可以成功编译 Curve 项目,其他版本不兼容。 4.2.2 为推荐版本。

    安装依赖

    编译相关的软件依赖可以参考 dockerfile 中的安装步骤。

    一键编译

    git clone https://github.com/opencurve/curve.git 或者 git clone https://gitee.com/mirrors/curve.git
    # (中国大陆可选)将外部依赖替换为国内下载点或镜像仓库,可以加快下载速度: bash replace-curve-repo.sh
    # curve v2.0 之前
    bash mk-tar.sh (编译 curvebs 并打tar包)
    bash mk-deb.sh (编译 curvebs 并打debian包)

    # (当前)curve v2.0 及之后
    # 编译 curvebs:
    make build stor=bs dep=1
    # or
    make dep stor=bs && make build stor=bs
    # 编译 curvefs:
    make build stor=fs dep=1
    # or
    make dep stor=fs && make build stor=fs

    制作镜像

    该步骤可以在容器内执行也可以在物理机上执行。 注意若是在容器内执行,需要在执行 docker run 命令时添加 -v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker 参数。

    # 编译 curvebs:
    # 后面的tag参数可以自定义,用于上传到镜像仓库
    make image stor=bs tag=test
    # 编译 curvefs:
    make image stor=fs tag=test

    上传镜像

    # test 为上一步中的tag参数
    docker push test

    测试用例编译及执行

    编译全部模块

    仅编译全部模块,不进行打包

    bash ./build.sh

    列出所有测试模块

    # curvebs
    bazel query '//test/...'
    # curvefs
    bazel query '//curvefs/test/...'

    编译对应模块的代码

    编译对应模块,例如test/common目录下的common-test测试:

    bazel build test/common:common-test --copt -DHAVE_ZLIB=1 --define=with_glog=true --compilation_mode=dbg --define=libunwind=true

    执行测试

    执行测试前需要先准备好测试用例运行所需的依赖:

    运行单元测试:

    • 构建对应的模块测试:

      $ bazel build xxx/...//:xxx_test
    • 运行对应的模块测试:

      $ bazel run xxx/...//:xxx_test
      # 或者
      $ ./bazel-bin/xxx/.../xxx_test
    • 编译全部测试及文件

      $ bazel build "..."
    • bazel 默认自带缓存编译, 但有时可能会失效.

      清除项目构建缓存:

      $ bazel clean

      清除项目依赖缓存(bazel 会将WORKSPACE 文件中的指定依赖项自行编译, 这部分同样也会缓存):

      $ bazel clean --expunge
    • debug 模式编译(-c 指定向bazel 传递参数), 该模式会在默认构建文件中加入调试符号, 及减少优化等级.

      $ bazel build xxx//:xxx_test -c dbg
    • 优化模式编译

      $ bazel build xxx//:xxx_test -c opt
      # 优化模式下加入调试符号
      $ bazel build xxx//:xxx_test -c opt --copt -g
    • 更多文档, 详见 bazel docs.

    动态库

    export LD_LIBRARY_PATH=<CURVE-WORKSPACE>/thirdparties/etcdclient:<CURVE-WORKSPACE>/thirdparties/aws-sdk/usr/lib:/usr/local/lib:${LD_LIBRARY_PATH}

    fake-s3

    快照克隆集成测试中,使用了开源的fake-s3模拟真实的s3服务。

    $ apt install ruby -y OR yum install ruby -y
    $ gem install fakes3
    $ fakes3 -r /S3_DATA_DIR -p 9999 --license YOUR_LICENSE_KEY

    备注:

    • -r S3_DATA_DIR:存放数据的目录
    • --license YOUR_LICENSE_KEY:fakes3需要key才能运行,申请地址见fake-s3
    • -p 9999:fake-s3服务启动的端口,不用更改

    etcd

    wget -ct0 https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz
    tar zxvf etcd-v3.4.10-linux-amd64.tar.gz
    cd etcd-v3.4.10-linux-amd64 && cp etcd etcdctl /usr/bin

    执行单个测试模块

    ./bazel-bin/test/common/common-test

    运行单元/集成测试

    bazel 编译后的可执行程序都在 ./bazel-bin 目录下,例如 test/common 目录下的测试代码对应的测试程序为 ./bazel-bin/test/common/common-test,可以直接运行程序进行测试。

    • CurveBS相关单元测试程序目录在 ./bazel-bin/test 目录下
    • CurveFS相关单元测试程序目录在 ./bazel-bin/curvefs/test 目录下
    • 集成测试在 ./bazel-bin/test/integration 目录下
    • NEBD相关单元测试程序在 ./bazel-bin/nebd/test 目录下
    • NBD相关单元测试程序在 ./bazel-bin/nbd/test 目录下

    如果想运行所有的单元测试和集成测试,可以执行工程目录下的ut.sh脚本:

    bash ut.sh
    - - + + \ No newline at end of file diff --git a/CurveBS/usecase/chuangyun.html b/CurveBS/usecase/chuangyun.html index 1083965..de27009 100644 --- a/CurveBS/usecase/chuangyun.html +++ b/CurveBS/usecase/chuangyun.html @@ -4,14 +4,14 @@ 创云融达基于Curve块存储的超融合场景实践 | Curve Book - - + +
    跳到主要内容

    创云融达基于Curve块存储的超融合场景实践

    作者简介:王彦灵(wangyanling@oct-ea.com),曾在XSky等公司从事存储运维开发工作,
    目前担任创云融达存储解决方案专家。2021年开始关注Curve社区,使用、部署、运维Curve集
    群,也在代码上做一些原型实验,对社区有浓厚兴趣。

    1. 创云融达背景

    创云融达是一家以海量数据的存管用为核心,以企业级私有云建设能力为基础,并提供数据资产和数据中台产品和解决方案的高新技术企业。

    2. 超融合业务背景

    近年来,为了优化人们纳税缴费的服务体验,各省市税务系统逐步构建了面向税务办事大厅的各种创新型智慧设备和智慧应用,如办税一体机,智慧税务大屏,语音辅助设备、以及业务流程辅助AI机器人等。极大便利人们税业务体验的同时也对IT集成和IT基础设施提出了更高的要求。

    创云融达承建了多地的智慧税务大厅解决方案,在实践中,我们发现智慧事务大厅的业务有如下特点:

    1. 各类智慧税务应用系统数量较多
    2. 办事大厅的业务场景对服务的实时性和可用性也有较高要求
    3. 应用不仅仅需要数据库等结构化数据,也需要一定规模的非结构化数据的存储需求

    根据以上特点,我们总结出了智慧税务基础设施的需求:稳定、可靠,适应于超融合集群环境(规模3-6节点)的分布式存储解决方案,提供云主机和对象存储,保证数据安全性、和稳定可靠运行。

    3. 方案选型

    对于对象存储,创云融达有自研的OEOS, 有冷热数据自动分层和智能编排机制,实现了海量数据的持久化存储,同时也满足了海量小文件的高IO性能要求。可以为各类智慧应用提供非结构化数据的存管用治理平台。

    对于块存储,非常知名的有ceph,但是开源版本的ceph在使用过程中是有很多稳定性的问题,常规的一些异常都会引起IO抖动, 和 Curve 替换 Ceph 在网易云音乐的实践 中提到的问题也类似。这些问题和Ceph的一致性协议、数据放置算法都相关,基于开源版本去改动复杂度和工作量都很大。同时,我们注意到了开源的Curve。

    通过和Curve社区多次深入的技术交流,了解到Curve分布式存储通过对数据块的数据摆放机制的优化,成功解决了Ceph类存储中CRUSH算法在生产环境中的巨大不确定性,从而具有生产环境所需要的稳定性和高可靠性。因此,基于Curve构建分布式块存储,作为虚拟化环境的数据底座,具有比Ceph产品具有更高的可靠性和数据安全性。

    特别需要提到的是Curve块存储在快照上的设计。Curve块存储的快照支持上传到S3,不限制快照数量并且降低了快照的存储成本。这一设计和我们自研的对象存储OEOS可以完美结合。

    4. 方案落地

    我们对Curve块存储进行了深入的测试,当前已经在项目中成功构建了基于Curve的分布式块存储和创云OEOS对象存储结合的分布式存储解决方案,为智慧税务项目中提供了统一存储基础设施。为智慧税务提供了有力的技术支撑,并在多地市的新建智慧税务大厅项目中得到了成功应用。

    整体的架构如下:

    arch

    • 支持块存储和对象存储,支持ISCSI、S3、NFS、SMB协议
    • 对象存储支持冷热分层,支持EC
    • 块存储提供高性能,快照数据支持转储到对象存储中
    • 所有服务高可用,任一节点故障不影响用户IO

    在项目落地的过程中,结合Curve的特点和业务场景,总结了一些虚拟化场景下的使用经验。

    1. 业务短暂的IO波峰

    在KVM虚拟化场景下,当用KVM镜像生成虚拟机实例的时候,会有短暂的IO波峰(5000-6000 IOPS),之后则消失。而业务场景多数情况下对IO的要求不高。 因此,我们设计了两个逻辑池,一个SSD盘逻辑池,和一个机械盘逻辑池。KVM虚拟机则制作成40G的系统盘。这样,所有的KVM系统盘都分配在SSD逻辑池中,而数据盘分配在机械盘逻辑池中,从而解决了KVM镜像问题。

    2. nebd服务的单点和性能问题

    Curve 社区提供的curve-tgt版本是基于nebd的,nebd是为了解耦上层应用和curve-sdk所加的热升级模块。但在实践中,这个服务有可能存在单点问题,且对IO时延也有15%左右的消耗。

    因此,我们在社区版curve-tgt基础上做了一点改进,让curve-tgt的backend直接调用Curve客户端(libcurve.so),测试证明也很稳定,并且也提高了性能。

    此外,curve-tgt还有一个多节点负载均衡的问题。我们的解决思路是,在每个节点上开两个curve-tgt进程(3260和3261端口),传统的3260端口仅用于iscsi discovery,当收到iscsi login命令的时候,利用iscsi的login redirect机制,调用loadbalancer,取第二个进程(3261端口)做重定向。Loadbalancer进程随机选取所有active节点的一个,重定向到该节点的3261端口上,完成login。最后一点,再通过keepalived为3260端口提供一个虚IP机制,则保证了discovery过程不会因为单节点掉电而失效。

    3. 集群整体掉电

    和社区讨论后,我们解决思路是:在分析出UPS市电故障时,第一步把前端curve-tgt进程停止掉,第二步集群所有的卷做一次快照,第三步做一次fsync,保证所有内存中的数据都刷到盘上,最后停止集群。

    - - + + \ No newline at end of file diff --git a/CurveBS/usecase/cloudnative-database.html b/CurveBS/usecase/cloudnative-database.html index 68c3388..e78cb66 100644 --- a/CurveBS/usecase/cloudnative-database.html +++ b/CurveBS/usecase/cloudnative-database.html @@ -4,8 +4,8 @@ 高性能云原生数据库存储底座 | Curve Book - - + +
    @@ -16,7 +16,7 @@ 上述脚本完成后会自动部署一个基于本地文件系统的实例,需要执行以下命令来停止这个实例:

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/pg_ctl \
    -D $HOME/tmp_master_dir_polardb_pg_1100_bld/ \
    stop

    在节点本地初始化数据目录 $HOME/replica1/

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/initdb -D $HOME/replica1

    编辑只读节点的配置。打开 $HOME/replica1/postgresql.conf,增加配置项:

    port=5433
    polar_hostid=2
    polar_enable_shared_storage_mode=on
    polar_disk_name='pool@@volume_my_'
    polar_datadir='/pool@@volume_my_/shared_data/'
    polar_vfs.localfs_mode=off
    shared_preload_libraries='$libdir/polar_vfs,$libdir/polar_worker'
    polar_storage_cluster_name='curve'
    logging_collector=on
    log_line_prefix='%p\t%r\t%u\t%m\t'
    log_directory='pg_log'
    listen_addresses='*'
    max_connections=1000

    创建 $HOME/replica1/recovery.conf,增加以下配置项:

    注意 请在下面替换读写节点(容器)所在的 IP 地址。

    polar_replica='on'
    recovery_target_timeline='latest'
    primary_slot_name='replica1'
    primary_conninfo='host=[读写节点所在IP] port=5432 user=postgres dbname=postgres application_name=replica1'

    最后,启动只读节点:

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/pg_ctl start -D $HOME/replica1

    检查只读节点能否正常运行:

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/psql \
    -p 5433 \
    -d postgres \
    -c 'select version();'
    # 下面为输出内容
    version
    --------------------------------
    PostgreSQL 11.9 (POLARDB 11.9)
    (1 row)

    集群检查和测试

    部署完成后,需要进行实例检查和测试,确保读写节点可正常写入数据、只读节点可以正常读取。

    登录 读写节点,创建测试表并插入样例数据:

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/psql -q \
    -p 5432 \
    -d postgres \
    -c "create table t(t1 int primary key, t2 int);insert into t values (1, 1),(2, 3),(3, 3);"

    登录 只读节点,查询刚刚插入的样例数据:

    $HOME/tmp_basedir_polardb_pg_1100_bld/bin/psql -q \
    -p 5433 \
    -d postgres \
    -c "select * from t;"
    # 下面为输出内容
    t1 | t2
    ----+----
    1 | 1
    2 | 3
    3 | 3
    (3 rows)

    在读写节点上插入的数据对只读节点可见。

    总结

    通过以上的步骤我们基于 Curve 块存储系统部署了 PFS 系统;然后分别编译部署了 PolarDB 的读写节点和只读节点;最后通过在读写节点写入、读节点读取的方式检查测试了集群是否正常工作。通过这种方式部署避免了直接暴露块设备,并且 PFS 通过 Curve-sdk 直接与 Curve 块存储通信,减少了 IO 的路径。 好了,现在你已经成功部署了一套基于 Curve 块存储的 PFS 和 PolarDB,快去享用吧!

    - - + + \ No newline at end of file diff --git a/CurveBS/usecase/rdma-spdk.html b/CurveBS/usecase/rdma-spdk.html index d08e306..5b328cd 100644 --- a/CurveBS/usecase/rdma-spdk.html +++ b/CurveBS/usecase/rdma-spdk.html @@ -4,13 +4,13 @@ CurveBS RDMA+SPDK 版本在网易数据库场景的落地实践 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveBS/usecase/scenario.html b/CurveBS/usecase/scenario.html index abe4b1a..6213890 100644 --- a/CurveBS/usecase/scenario.html +++ b/CurveBS/usecase/scenario.html @@ -4,13 +4,13 @@ 应用场景 | Curve Book - - + +
    跳到主要内容

    应用场景

    CurveBS的核心应用场景主要包括:

    • 虚拟机/容器的性能型、混合型、容量型云盘或持久化卷,以及物理机的远程存储盘
    • 高性能存算分离架构:基于RDMA+SPDK的高性能低时延架构,支撑MySQL、kafka等各类数据库、中间件的存算分离部署架构,提升实例交付效率和资源利用率

    TBD

    - - + + \ No newline at end of file diff --git a/CurveBS/usecase/wanfang.html b/CurveBS/usecase/wanfang.html index 45cb2c0..f2ebb3e 100644 --- a/CurveBS/usecase/wanfang.html +++ b/CurveBS/usecase/wanfang.html @@ -4,14 +4,14 @@ 扬州万方基于申威平台的Curve块存储在高性能和超融合场景下的实践 | Curve Book - - + +
    跳到主要内容

    扬州万方基于申威平台的Curve块存储在高性能和超融合场景下的实践

    1. 扬州万方背景

    扬州万方科技股份有限公司主要从事通信、计算机和服务器、智能车辆、基础软件等产品的科研生产,是国家高新技术企业、专精特新小巨人企业、国家火炬计划承担单位。

    2. 业务背景

    申威处理器是在国家“核高基”重大专项支持下、由国家高性能集成电路(上海)设计中心自主研发,采用自主指令集,具有完全自主知识产权的处理器系列。当前主流的申威3231处理器是基于第三代“申威 64” 二次优化版核心的国产高性能多核处理器,主要面向高性能计算和高端服务器应用。申威3231采用CC-NUMA多核结构和SoC技术,单芯片集成了32个64位RISC结构的申威处理器核心、8路DDR4存储控制器接口、40lane的PCI-E 4.0标准I/O接口以及3路直连接口,最高工作频率可达2.5GHz。

    2018年至今,万方科技基于申威系列处理器研制了面向海量存储、高密度存储、全闪存储等多种需求的多类型存储系统,大量采用了基于Ceph的分布式统一存储技术。在随后的生产环境使用维护中,Ceph在性能一致性、运行稳定性、故障修复能力等方面的表现差强人意,并且复杂的IO处理流程、数据放置及迁移机制、庞大的代码规模等增加了使用运维成本。同时,我们也持续关注存储的技术生态,有意向另辟蹊径,探索新型的存储技术,改善目前存储产品的不足。在深入调研了Curve的技术架构、应用成熟度、社区背景的基础上,决定在申威硬件平台上适配、试用Curve技术,主要的试用场景包括高性能块存储、超融合等。

    3. 应用实践

    Curve的适配申威平台

    由于申威3231处理器采用自主申威指令集,因此需要使用申威平台的gcc对Curve进行重新编译。

    Curve的移植适配需要解决的核心问题是brpc的编译,brpc采用M:N的线程模型,为了进一步优化性能,在原子操作、用户态上下文切换等部分使用了与处理器平台强关联的汇编语言,我们使用申威的汇编指令重写了这两部分内容,并且优化了申威平台非对齐访问内存的相关代码。

    高性能块存储场景实践

    高性能是Curve的主要特点之一,而在业务层面,高性能块存储是支撑数据库等性能型应用的关键。在Ceph存储技术的实际应用中,我们大量使用了NVMe闪存盘,通过bcache缓存方案提升机械盘的存储性能。

    对于高性能块存储场景,我们采用全NVMe闪存方式构建Curve集群

    为了充分发挥NVMe闪存性能,我们基于SPDK技术重构Chunkserver的Ext4 filepool。 与当前社区中所采用的Polarfs+SPDK的方式不同,我们使用SPDK blobstore实现Chunkserver的底层存储逻辑。

    这种方案需要注意的点是:SPDK blobstore不存在目录的概念,只支持blob读写,不支持目录操作及文件命名等功能。为了尽量减少对于Chunkserver上层逻辑的修改,我们仍然使用Chunkserver既有的目录结构,但filepool中的文件不再用于存储真实用户数据,而是记录blob id,用于将Ext4文件系统中的文件关联到对应的SPDK blob,目录操作、文件命名等功能仍然沿用Ext4文件系统的相关操作接口,从而实现基于SPDK blobstore的数据存储。

    经过相关改造,在3节点*3块NVMe闪存盘、万兆网络互联的申威3231存储服务器集群上,单个NBD盘IOPS达到32K。

    超融合场景实践

    万方科技的超融合产品采用混闪的硬件存储架构,并基于Kubernetes统一管理容器及KVM虚拟机。

    wanfang

    由于超融合产品需要适应大容量、高性能等不同的存储场景,因此需要Curve满足同一物理池中支持不同类型存储介质的需求,为实现这一需求,我们为chunkserver增加了存储介质类型的属性,并在逻辑池创建时通过配置存储介质类型匹配chunkserver并创建copyset,从而使得逻辑池的物理存储空间在指定类型存储介质上进行分配。

    另一方面,为了实现Kubernetes对于传统KVM虚拟机的统一管理,以容器方式运行KVM虚拟机,并使用Curve的CSI接口为KVM虚拟机提供虚拟磁盘,Curve为虚拟机的容器化运行提供了稳定、高性能的存储服务。

    后续规划

    结合Curve及万方科技相关产品的发展规划,后续工作将会集中在两个方面:

    1. CurveFS 在申威平台的适配、验证;
    2. 结合社区的SPDK+RDMA块存储性能优化方案,探索NVMe全闪存储的性能优化。
    - - + + \ No newline at end of file diff --git a/CurveFS/architecture/architecture-intro.html b/CurveFS/architecture/architecture-intro.html index 416005d..3f36267 100644 --- a/CurveFS/architecture/architecture-intro.html +++ b/CurveFS/architecture/architecture-intro.html @@ -4,13 +4,13 @@ CurveFS 架构介绍 | Curve Book - - + +
    跳到主要内容

    CurveFS 架构介绍

    CurveFS 是 POSIX 兼容的分布式文件存储系统,用于更好的支持云原生场景。

    整体架构

    curvefs arch

    CurveFS由三个部分组成:

    1. curve-fuse 是基于 fuse 的文件系统客户端。
      • 接收并处理 fuse 的请求,与fs-meta cluster 交互实现对元数据的增删改查,与 fs-data cluster 交互实现对数据的增删改查;
      • 提供元数据缓存和数据缓存以提高性能;
      • 用户可以通过客户端访问不同的文件系统实例。
    2. fs-meta cluster 是 CurveFS 的元数据服务集群。
      • 它的架构类似 CurveBS,由 MDS 和 Metaserver 两个部分组成,高可扩、高可用;
      • mds 用于管理集群拓扑、集群调度、文件系统实例、文件元数据分片管理;基于 etcd 存储集群拓扑、用户和文件系统信息;基于 etcd 实现 mds 的高可用。
      • metaserver 用于存储文件的元数据( inode 和 dentry ),通过 multi-raft 实现高可用和高可靠。每个 raft 复制组管理多组元数据分片。
    3. fs-data cluster 是 CurveFS 的数据服务集群。
      • 当前支持S3标准接口的对象存储以及 CurveBS;
      • 用户可以根据性能场景灵活配置;
      • 也因为支持多种存储集群,CurveFS 的元数据使用独立的集群统一管理。

    元数据集群

    元数据集群fs-meta cluster如下:

    curvefs meta arch

    1. 管理拓扑结构

    MDS 管理元数据集群的拓扑结构,如下图:

    curvefs topo

    • pool 物理池,对机器资源进行物理隔离。server不能跨pool交互;

    • zone 故障隔离的基本单元,属于不同zone的机器部署在不同机架,server归属于一个 zone

    • server 物理服务器,metaserver归属于zone

    • metaserver 最小服务单元,管理一块物理磁盘。

    2. 管理文件系统实例

    MDS 管理文件系统实例及文件系统的元数据的分布。

    • 一个文件系统实例由多个元数据分片 partition组成
    • 每个partition管理指定范围的 inode,文件系统根目录的 inode 固定为1
      • dentry 存放在父目录所在的元数据分片上;
      • 对于文件 /A/B
        • 首先找到根目录所在的元数据分片,在该元数据分片上查询 dentry(/A);
        • 从 dentry(/A) 中获得 /A 的 inodeid,根据 inodeid 获取对应的元数据分片查询 dentry(/A/B);
        • 从 dentry(/A/B) 中获得 /A/B 的 inodeid,从而定位到元数据分片,获取 /A/B 的 inode 信息。
    • partitioncopyset管理,copyset是raft复制组
      • copysetpartition是一对多的关系;
      • copysetpartition都是动态创建的,可以弹性扩容。当前的创建策略比较简单:在满足copyset的多个副本在不同的server的前提下,按照metaserver的管理磁盘的剩余容量进行选择;partition同样按照copyset所在metaserver的剩余容量进行选择;

    3. MDS 高可用

    MDS 的高可用基于 etcd 实现,允许部分实例异常,如下图所示:

    curvefs mds ha

    MDS 向 etcd 注册,同时只有一个 MDS 提供服务,备 MDS 监听。主 MDS 挂掉后,备 MDS 启动提供服务。

    4. Metaserver 高可用

    Metaserver高可用基于 raft 实现,2N+1 个副本允许 N 个副本异常。

    数据组织形式

    fs-data cluster存放文件的实际数据,可以是支持S3标准协议的对象存储集群,也可以是CurveBS,后续根据场景需求,我们还会对接更多的集群。

    对接 CurveBS

    一个文件系统对应 CurveBS 上的一个卷volume, 但这里的块设备只用于存储文件的数据。文件系统中的一个文件,地址空间和卷中 chunk 的对应关系如下图:

    curvefs + curvebs

    • CurveBS 中卷和 chunk 的关系请参考:Curve设计要点
    • curve-fuse中,一个文件的地址空间由多个非固定大小的 extent 组成;
    • extent 在 inode 中由{ fsOffset, volumOffset, length} 组成;
    • CurveBS 中卷的空间如何分配给文件系统中的文件,请参考:CurveFS基于块的空间分配方案

    对接 S3

    对于文件系统中的一个文件,地址空间和 S3 对象的对应关系如下图所示:

    curvefs + s3

    • curve-fuse端,一个文件的地址空间由多个固定大小的chunk组成,每个chunk由多个长度不固定的datacache组成;
    • datacache按照block的粒度拆分后上传到 S3 中;
    • 每个数据块在 inode 中由{ chunkid, offset, len, size } 表示,每个数据块在 S3 中的 key 由 { fsid, inodeid, chunkid, blockindex } 表示。根据 inode 中数据位置的记录,可以通过chunkblock的大小,计算出这部分数据对应的 S3 中数据块的数量以及每个数据块的 key。

    系统特性

    1. 多存储系统支持。数据既支持存储到公有云,又支持存储到本地存储系统;支持数据在不同存储系统之间自由流转,支持主动/被动数据生命周期管理;
    2. 元数据集群和数据集群高可用、高可扩、高可靠;
    3. 支持高速缓存,客户端有内存和磁盘两级缓存加速;支持多级缓存,BS集群可作为持久化缓存层;
    4. POSIX兼容,像本地文件系统一样使用,业务可以无缝接入;
    5. 易运维,常见的异常可以自愈。
    - - + + \ No newline at end of file diff --git a/CurveFS/architecture/client-arch.html b/CurveFS/architecture/client-arch.html index 652788c..8369cd1 100644 --- a/CurveFS/architecture/client-arch.html +++ b/CurveFS/architecture/client-arch.html @@ -4,15 +4,15 @@ CurveFS Client 架构介绍 | Curve Book - - + +
    跳到主要内容

    CurveFS Client 架构介绍

    1 概要

    CurveFS client 作为CurveFS的客户端,使用rpc接口向后端的元数据集群和数据集群发送请求,调用后端相应接口,实现相应的功能。其通过对接fuse用户态文件系统接口,向上提供文件系统接口功能。CurveFS client支持数据存储在两种数据存储后端,分别是Curve块存储和S3兼容的对象存储,后续还可能支持CurveBS和S3混合存储,让数据根据冷热程度在CurveBS与S3之间流动。

    2 功能介绍

    2.1 提供标准POSIX文件系统接口

    CurveFS client通过对接libfuse 的 lowlevel fuse api,支持fuse用户态文件系统,实现标准POSIX文件系统接口。

    2.2 支持CurveBS 后端存储引擎存储数据与缓存

    CurveFS client 支持将文件系统数据,存储在CurveBS中。CurveBS提供的是块设备的接口,CurveFS client将文件系统数据组织成一定的结构,写入到CurveBS块设备中,从而实现CurveBS的后端数据存储。 CurveFS client支持将CurveBS中存储的数据缓存在内存中,以加速数据的读写性能。

    2.3 支持S3存储引擎存储数据与缓存

    CurveFS client 支持将文件系统数据,通过一定的格式,转换称为对象存储中的对象,并通过S3接口兼容的客户端(使用S3 C++ sdk)将文件数据保存在S3存储引擎中。 CurveFS client支持内存与磁盘缓存的二级的数据缓存,从而加速S3数据的读写性能。

    2.4 元数据获取和缓存

    CurveFS client 将文件系统元数据存储于CurveFS元数据集群,CurveFS client支持将元数据缓存在client端,从而提供更快速地元数据访问。其缓存的元数据信息有:

    • 文件系统全局信息,即FsInfo。
    • 各文件和目录的元数据,包括dentry信息和inode信息两大部分。
    • 各文件和目录的元数据的分布于的Copyset与Partition信息,CurveFS client缓存这些信息,从而向对应的Copyset与Partition请求元数据操作。
    • 元数据集群的拓扑信息,CurveFS client需要知道元数据集群的ip、端口等拓扑信息,从而知道向哪个ip、端口发送rpc。

    2.5 元数据请求的异常处理和重试

    CurveFS mds 和 CurveFS metaserver集群,实现了高可用部署,其中

    • CurveFS mds 同一时刻只有一个mds提供服务,其他mds节点通过etcd监听,当主mds发生异常时,备mds能够随时替代成为leader,此时需要CurveFS client寻找主mds和重试rpc请求以处理该种情况。
    • CurveFS metaserver集群,是以mutiraft集群的方式,对CurveFS client提供服务的,也会存在类似的leader切换等场景,此时也需要CurveFS client 去GetLeader以及在切换leader后重试rpc请求。

    除此之外,

    • CurveFS client与上述组件的通信,也会因为网络繁忙等原因,造成rpc的超时等问题,那么同样需要CurveFS client 实现请求的重试。

    3 架构

    CurveFS client包含几个主要模块:

    curvefs client structure

    • libfuse,对接了其lowlevel fuse api,支持fuse用户态文件系统;
    • 元数据cache,包含fsinfo, inode cache, dentry cache, 实现对元数据的缓存;
    • meta rpc client, 主要对接元数据集群,实现meta op的发送,超时重试等功能;
    • S3 client, 通过对接S3接口,将数据存储在S3中;
    • S3 data cache, 这是S3数据存储的内存缓存层,作为数据缓存,加速S3数据的读写性能;
    • S3 disk cache,这是S3数据存储的本地持久化缓存,通过磁盘缓存,将对S3数据的读写暂时缓存在本地磁盘上,稍后在异步上传到S3,从而有效降低时延,提供吞吐;
    • curvebs client 通过对接Curve块存储SDK,实现将数据存储在Curve块存储集群中;
    • volume data cache,这是当数据存储在Curve块存储中的缓存层,以加速数据读写性能(开发中);

    4 IO流程

    CurveFS Client的IO流程分为两大部分,分别是元数据Meta IO流和数据Data IO流。

    4.1 Meta IO 流

    meta flow

    CurveFS 的元数据IO流,以MkNod为例,包含如下过程:

    • 用户调用文件系统MkNod接口,经过用户态文件系统fuse low level api,到达CurveFS client接口;
    • MkNod需要执行两步操作,分别是CreateInode和CreateDentry。CreateInode过程首先需要决定创建Inode的partition。通常情况下,topo信息和partition信息缓存在CurveFS client端,如果缓存中没有这些信息,那么CurveFS Client首先会去mds获取这些信息;
    • CurveFS client根据缓存中的partition信息,根据一定的策略,决定创建Inode的partiton;
    • 根据缓存中或者获取到的topo信息以及partiton信息,找到需要创建Inode的copyset;
    • 如果该copyset中缓存了leader信息,那么就可以直接发送CreateInode的rpc请求到对应的metaserver,否则,此时还需要向copyset中任一metaserver获取leader信息;
    • 调用CreateInode的rpc创建完Inode后,接下来就要CreateDentry了;
    • 同样的,Create Dentry过程,首先也需要根据一定的策略,决定创建Dentry的partiton;
    • 之后,Create Dentry过程根据缓存中或者获取到的topo信息以及partiton信息,找到需要创建Dentry的copyset;
    • 如果该copyset中缓存了leader信息,那么就可以直接发送CreateDentry的rpc请求到对应的metaserver,否则还需要向copyset中任一metaserver获取leader信息;
    • 创建Dentry完成后,即完成了MkNod的功能。

    4.2 Data IO 流

    数据IO流分为两大部分,分别是数据存储到CurveBS的Data IO流与数据存储到S3的Data IO流,两者有少许区分,分别如下:

    4.2.1 存储于CurveBS Data IO流:

    curvebs dataio flow

    存储到CurveBS的Data IO流包含如下过程:

    • 用户调用文件系统write接口,经过用户态文件系统fuse low level api,到达CurveFS client接口;
    • CurveFS client 的write接口首先会获取Inode,如果Inode不在cache中,那么走类似上一节元数据的流程,从metaserver端获取Inode;
    • 获取到Inode信息后,write接口首先会根据write的offset,len,判断是否需要分配新的空间;
    • 如果需要分配新的空间,那么将调用AllocExtent RPC接口,向space server申请空间(以extent表示);
    • 分配到空间后,CurveFS client将调用CurveBS client的块设备接口,将数据写入CurveBS;
    • 完成数据写入后,CurveFS client将数据写入信息和分配信息更新到Inode,并调用UpdateInode RPC,将Inode更新到metaserver。

    4.2.2 存储与S3 Data IO流:

    s3 dataio flow

    存储到S3的Data IO流包含如下过程:

    • 用户调用文件系统write接口,经过用户态文件系统fuse low level api,到达CurveFS client接口;
    • CurveFS client 的write接口会先将数据写入Data Cache中;
    • 当DataCache数据满或者周期性的刷新时候到了的时候,CurveFS client将开始数据的Sync过程;
    • CurveFS client首先会将数据写入S3,(如果有disk cache,会先将数据写入disk cache,稍后再异步将数据写入S3);
    • 数据写入S3之后,CurveFS client会记录写入S3的数据的元信息,组织成S3ChunkInfo;
    • 如果此时CurveFS client 没有缓存Inode信息,那么将会走前一节流程中的元数据流从metaserver获取Inode;
    • 得到Inode后,CurveFS client会将S3ChunkInfo信息添加到Inode中;
    • 完成本地Inode更新之后,CurveFS client 将会调用AppendS3ChunkInfo RPC接口增量更新metaserver端的Inode信息;

    5 异常处理

    CurveFS client 的异常处理,主要指的是对元数据集群的各种异常,进行幂等的请求返回。主要涉及到对元数据集群mds和metaserver的rpc请求的重试,涉及包括如下功能:

    • 向mds节点请求的rpc,如果发现mds请求超时,那么需要重试,如果多次重试失败,那么可能切换了主mds,此时需要切换mds继续重试;
    • 向metaserver的节点请求rpc,如果收到redirect的回复或者请求超时,则可能是因为切换了leader,此时需要重新获取leader,然后重试请求;

    上述重试过程还需要保证请求的幂等性,CurveFS Client对于请求的幂等性的保证,主要通过以下几种方式:

    • 对于删除类的请求,如果返回NOT EXIST错误,那么CurveFS Client会直接认为已经删除成功,从而幂等地执行成功。
    • 对于向mds请求的,如Mount FS等请求,这些请求对于性能要求不高,CurveFS Client首先会通过Get FsInfo获取当前挂载点的情况,之后再去Mount FS。通过这种方式,保证Mount FS请求发送前没有被挂载,如果仍然返回了EXIST,那么可以肯定是rpc重试请求造成的,因此幂等的返回执行成功。
    • 其他一些请求,如CreateDentry等,则根据CreateDentry请求内容中InodeId唯一性(重试的CreateDentry中请求的inodeId是一致的,非重试的请求InodeId一定不同)来区分是否是重试的请求,从而幂等的返回成功,或者返回EXIST错误。

    6 关键设计

    6.1 Rename

    CurveFS 的rename 接口,为了保证原子性,借鉴了 leveldb 与 etcd(boltdb) 中事务的实现,设计如下图所示:

    rename

    • rename机制设计在 MDS 所有 copyset 中增加一个 txid 字段,保存当前 copyset 已成功的事务 id(该事务 id 顺序递增,事务每成功一次则加一);
    • 每次 rename 开始时,将 srcDentry, dstDentry 所在 copyset 对应的 txid 分别加 1 (copyset_txid+1) 去删除/创建/修改 dentry(其实就是创建副本,不管是删除/创建/更改都是创建相应 copyset_txid+1 为 key 的副本,原始 dentry 不动),并设置 PendingTx 为本次事务;
    • 如果上一步骤成功了,就提交事务,将 srcDentry, dstDentry 所在 copyset 的 txid 都加 1(这一步是通过 etcd 的事务保证的),如果上一步或这一步失败,因为 txid 不变,原始数据版本也在,还是保证原子性(其实就是一个 txid 对应一个版本的数据);
    • 下次访问的时候,带上对应 copyset 的最新 txid (copyset_txid),判断 PendingTx,如果 (copyset_txid >= PendingTxId && rpc_request.key == PendingTxKey),则表明 PendingTx 对应的事务是已经成功了的,并且 PendingTx 对应事务刚好操作的是请求的 dentry,则返回 PendingTxKey + PendingTxId 对应的副本 dentry,否则返回原始 dentry;
    • PendingTx 与 dentry 副本是一一对应的,每个 copyset 只需要一个 PendingTx(即整个 copyset 中最多只会存留一个副本 dentry);

    CurveFS client的rename机制通过在mds端向etcd原子的提交事务的方式,最终实现了整个Rename的原子性。

    对rename操作的进一步优化正在进行中,预计2.7版本发布。

    - - + + \ No newline at end of file diff --git a/CurveFS/architecture/metaserver-arch.html b/CurveFS/architecture/metaserver-arch.html index d3951c9..a129ca0 100644 --- a/CurveFS/architecture/metaserver-arch.html +++ b/CurveFS/architecture/metaserver-arch.html @@ -4,13 +4,13 @@ CurveFS MetaServer 架构介绍 | Curve Book - - + +
    跳到主要内容

    CurveFS MetaServer 架构介绍

    概述

    MetaServer 在 CurveFS 集群中提供高可用、高可靠的元数据服务,并保证文件系统元数据的一致性。同时,在设计之初就以高性能和扩展性作为目标。

    MetaServer 在整体设计上参考了 CurveBS 的 ChunkServer,单个 MetaServer 以用户态进程的形式运行在宿主机上,在 CPU/RAM 等资源充足的情况下,一台宿主机可以运行多个 MetaServer 进程。同时,也引入了 ChunkServer 中 Copyset 的设计,利用 Raft 保证数据的一致性和服务的高可用。

    在元数据管理层面,对文件系统元数据进行分片管理,避免单个 Raft Group 维护一个文件系统元数据是带来的性能瓶颈。元数据的每个分片称为 Partition。Copyset 与 Partition 的对应关系可以是一对一,也可以是一对多。一对多的情况下表示,一个 Copyset 可以维护多个 Partition。在一对多的情况下,文件系统的元数据管理如下图所示:

    copyset-partition

    图中共有两个 Copyset,三个副本放置在三台机器上。P1/P2/P3/P4 表示文件系统的元数据分片,其中 P1/P3 属于一个文件系统,P2/P4 属于一个文件系统。

    整体架构

    MetaServer 的整体架构如下所示,大致可分为三个部分:Service Layer、Core Business Layer 和 MetaStore。三者之间相互协作,高效处理外部组件的各种请求和任务。下面将对各个模块进行详细的介绍。

    metaserver-arch

    Service Layer

    对外提供 RPC 接口,供系统中的其他服务(Curve-Fuse、MDS、MetaServer 等)调用。同时提供 RESTful 接口,可以把当前进程中各组件的状态(Metric)同步给 Prometheus。

    MetaService

    提供元数据服务,是 MetaServer 的核心服务,提供文件系统元数据查询、创建、更新、删除操作的必要接口,例如 CreateInode、CreateDentry、ListDentry,并支持 Partition 的动态创建和删除。

    CopysetService

    提供动态创建Copyset、查询Copyset状态接口。在创建文件系统时,MDS会根据当前集群的负载情况,决定是否创建新的Copyset。

    RaftService

    braft 提供,用于 Raft 一致性协议的交互。

    CliService (Command Line Service)

    提供 Raft 配置变更接口,包括 AddPeer、RemovePeer、ChangePeer、TransferLeader。同时提供了一个额外的 GetLeader 接口,用于获取当前复制组最新的 Leader 信息。

    MetricService

    提供 RESTful 接口,可以获取进程各组件的状态,Prometheus 会调用该接口采集数据,并利用 Grafana 可视化展示。

    Core Business Layer

    MetaServer 核心处理逻辑,包括元数据请求的处理,并保证元数据的一致性、高可用、高可靠;心跳上报及配置变更任务的执行处理;以及注册模块等。

    CopysetNode

    表示 Raft Group 中的一个副本,是 braft raft node 的简单封装,同时实现了 Raft 状态机。

    ApplyQueue

    用于隔离 braft apply 线程,可以 apply 的请求会放入 ApplyQueue 中,同时 ApplyQueue 保证请求的有序执行,在请求执行完成后,返回给客户端响应。

    MetaOperator

    元数据请求到达后,会生成一个对应的 operator,operator 会将请求封装成 task,然后交给元数据请求对应的 CopysetNode 进行处理,完成副本间的数据同步。

    Register

    正常集群的启动流程是,先启动MDS,然后创建逻辑池,最后启动 MetaServer。在创建逻辑池时,需要指定逻辑池的拓扑结构,以及各个 MetaServer 进程的 IP 和 Port。这样做的目的是,阻止非法的 MetaServer 加入集群。

    所以,在 MetaServer 的启动阶段,需要先向 MDS 进行注册,MDS 验证后会返回唯一标识 MetaServerID 及 Token。在后续 MetaServer 与 MDS 通讯时,需要提供此 ID 和 Token 作为身份标识和合法性认证信息。

    Heartbeat

    MDS 需要实时的信息来确认 MetaServer 的在线状态,并获取 MetaServer 和 Copyset 的状态和统计数据,并根据所有 MetaServer 的信息计算当前集群是否需要动态调度以及相应的调度命令。

    MetaServer 以心跳的方式来完成上述功能,通过周期性的心跳,上报 MetaServer 和 Copyset 的信息,同时执行心跳响应中的调度任务。

    Metric

    利用 bvar 导出系统中核心模块的统计信息。

    MetaStore

    高效组织和管理内存元数据,同时配合 Raft 对元数据进行定期 dump,加速重启过程。

    MetaPartition

    文件系统的元数据进行分片管理,每个分片称为 Partition,Partition 通过聚合 InodeStorage 和 DentryStorage,提供了对 Dentry 和 Inode 的增删改查接口,同时 Partition 管理的元数据全部缓存在内存中。

    Inode 对应文件系统中的一个文件或目录,记录相应的元数据信息,比如 atime/ctime/mtime 等。当 Inode 表示一个文件时,还会记录文件的数据寻址信息。每个 Partition 管理固定范围内的 Inode,根据 InodeId 进行划分,比如 InodeId [1-200] 由 Partition 1管理,InodeId [201-400] 由 Partition 2 管理,依次类推。

    Dentry 是文件系统中的目录项,记录文件名到 inode 的映射关系。一个父目录下所有文件/目录的 Dentry 信息由父目录 Inode 所在的 Partition 进行管理。

    MetaSnapshot

    配合 CopysetNode 实现 Raft 快照,定期将 MetaPartition 中记录的元数据信息 dump 到本地磁盘上,起到了启动加速以及元数据去重的功能。

    当 Raft 快照触发时,MetaStore 会 fork 出一个子进程,子进程会把当前 MetaPartition 中记录的所有元数据进行序列化并持久化到本地磁盘上。在进程重启时,会首先加载上次的 Raft 快照到 MetaPartition 中,然后再从 Raft 日志中回放元数据操作记录。

    S3Compaction

    在对接 S3 文件系统(文件系统数据存放到 S3)时,由于大多数 S3 服务不支持对象的覆盖写/追加写,所以在对文件进行上述写入时,Curve-Fuse 会把新写入的数据上传到一个新的 S3 对象,并向 Inode 的 extent 字段中中插入一条相应的记录。

    以下图为例,用户在第一次写入文件后,进行了三次覆盖写,所以 extent 中会记录 4 条记录。在没有 Compaction 的情况下,后续的读操作需要计算出每个范围的最新数据,然后分别从 S3 下载、合并,最终返回给上层应用。这里的性能开销、空间浪费是显而易见的,但是上层应用的写入模式是无法限制的。

    s3compaction

    Compaction 的主要作用就是将 extent 中有重叠或连续的写入进行合并,生成一个新的 S3 对象,以加快后续的读取速度,减少存储空间浪费。

    Trash

    在当前的设计中,Inode 的 nlink 计数减到 0 时,并没有立即对该 Inode 进行清理,而是将 Inode 标记为待清理状态,由 Trash 模块进行定期扫描,当超过预设的阈值时间后,才将 Inode 从 MetaPartition 中删除。

    - - + + \ No newline at end of file diff --git a/CurveFS/comparison/CurveFS-vs-Other-FS.html b/CurveFS/comparison/CurveFS-vs-Other-FS.html index 6be4441..ce51088 100644 --- a/CurveFS/comparison/CurveFS-vs-Other-FS.html +++ b/CurveFS/comparison/CurveFS-vs-Other-FS.html @@ -4,13 +4,13 @@ CurveFS 对比其他FS | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/csi.html b/CurveFS/deploy/csi.html index a68998e..2c1543c 100644 --- a/CurveFS/deploy/csi.html +++ b/CurveFS/deploy/csi.html @@ -4,13 +4,13 @@ CurveFS CSI 驱动程序 | Curve Book - - + +
    跳到主要内容

    CurveFS CSI 驱动程序

    简介

    CurveFS CSI 驱动程序实现容器编排器的 CSI 规范来管理 CurveFS 文件系统。

    代码库:https://github.com/opencurve/curvefs-csi

    先决条件

    • Kubernetes 1.18+

    CSI 已实现接口

    • ControllerServer: CreateVolume, DeleteVolume, ValidateVolumeCapabilities
    • NodeServer: NodePublishVolume, NodeUnpublishVolume, NodeGetInfo, NodeGetCapabilities
    • IdentityServer: GetPluginInfo, Probe, GetPluginCapabilities

    如何使用

    通过 kubectl

    1.给节点添加标签

    kubectl label node <nodename> curvefs-csi-controller=enabled
    # 为 pod 运行的所有节点添加以下标签
    kubectl label node <nodename> curvefs-csi-node=enabled

    2.部署csi驱动

    kubectl apply -f deploy/csi-driver.yaml
    kubectl apply -f deploy/csi-rbac.yaml
    kubectl apply -f deploy/csi-controller-deployment.yaml
    kubectl apply -f deploy/csi-node-daemonset.yaml
    注意:如果您想启用DiskCache,请阅读下面的相关部分

    3.创建storage class和pvc

    # 复制并填写 storageclass-default.yaml 中的待填充项
    kubectl apply -f storageclass.yaml
    # 复制并修改pvc-default.yaml
    kubectl apply -f pvc.yaml
    1. 现在您可以将此 pvc 绑定到 pod

    DiskCache配置相关

    DiskCache 是客户端使用基于磁盘的缓存来提高 io 性能。

    如果client需要开启diskcache并使用宿主机额外的硬盘或云盘,需要在csi driver部署之前先把云盘或者本地盘挂载好。之后格式化硬盘或云盘然后mount到一个path(如/data/curvefs-diskcache),然后通过hostpath的方式映射进csi的daemonset pod里的/curvefs/client/data/cache路径,配置可以参考下面这个yaml:

    https://github.com/opencurve/curvefs-csi/blob/main/deploy/csi-node-daemonset-enable-cache.yaml

    上述配置文件中的 /data/curvefs-diskcache 对应的就是上面配置的缓存盘目录,可以修改为自己自定义的路径名称。

    之后将“diskCache.diskCacheType=2”或“diskCache.diskCacheType=1”添加到storageclass.yaml的mountOptions部分,2表示开启read和write缓存, 1为read缓存,默认为0是无缓存。

    已知问题:

    • 启用discache(类型=2,写入)后,metadatasever 中的元数据将比 s3 存储中的数据更新,如果 csi 节点 pod 崩溃但写入缓存未完全上传到 s3 存储,您可能会丢失这部分数据。 重新挂载会崩溃,因为你只有元数据但没有数据(还没有刷新到 s3)。

    构建状态

    Curvefs CSI 驱动程序版本CurveFS 版本CurveFS CSI 驱动程序映像
    v1.0.0v2.3.0-rc0curvecsi/curvefscsi:v1.0.0
    v1.0.1v2.4.0-beta2curvecsi/curvefscsi:v1.0.1
    v1.0.2v2.5.0-betacurvecsi/curvefscsi:v1.0.2
    v1.0.3v2.6.xcurvecsi/curvefscsi:v1.0.3

    更多版本请参考:https://github.com/opencurve/curvefs-csi/blob/main/README.md#build-status

    在其他k8s发行版上使用

    后续工作

    • 更多创建/安装选项支持(需要未来的 CurveFS 支持)
    • 将 s3 ak/sk 等敏感信息移至secret配置
    • 子路径挂载支持(需要未来的 CurveFS 支持)
    • 将fuse挂载点隔离到一个单独的 Pod 中,灵感来自 juicefs-csi
    • 容量配额支持(需要未来的 CurveFS 支持)
    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/deploy-with-curveadm.html b/CurveFS/deploy/deploy-with-curveadm.html index 00b0b3f..f6a7c4d 100644 --- a/CurveFS/deploy/deploy-with-curveadm.html +++ b/CurveFS/deploy/deploy-with-curveadm.html @@ -4,13 +4,13 @@ 物理机部署 CurveFS 集群 | Curve Book - - + +
    跳到主要内容

    物理机部署 CurveFS 集群

    为了提升 Curve 的运维便利性,我们设计开发了 CurveAdm 项目,其主要用于部署和管理 Curve 集群,目前已支持部署CurveBS & CurveFS,相关使用文档请参考 CurveAdm用户手册,并根据手册首先安装CurveAdm工具之后再进行Curve集群的部署。

    部署 CurveFS 集群

    使用 CurveAdm 部署 CurveFS 集群过程中如遇到错误,会在终端打印对应的错误码和解决方案链接,如参考解决方案链接的指导后仍然无法解决错误问题,可以添加终端打印的微信号来与我们取得进一步联系。

    集群部署过程当前主要分为以下几个主要步骤:

    1. 准备节点列表

    详细步骤请参阅文档:主机管理

    2. 准备集群拓扑文件

    详细步骤请参阅文档:集群拓扑

    3. 部署集群

    部署集群命令执行过程中,CurveAdm工具会自动执行部署环境预检工作,以确保所有节点均满足部署条件,只要当一切检测正常才会真正开始部署集群,关于预检的介绍请参阅文档:环境预检

    关于集群部署的详细步骤请参阅文档:部署集群

    4. 部署客户端

    详细步骤请参阅文档:部署客户端

    5. 部署分布式缓存【可选】

    如需进一步提升性能,可以为 CurveFS 集群部署分布式缓存服务(当前基于memcached集群),CurveFS 客户端缓存当前有内存、本地硬盘、分布式缓存3类,分别为L1、L2、L3级缓存,均支持读写缓存。

    部署分布式缓存的详细步骤请参阅文档:部署分布式缓存

    命令行工具

    • CurveFS 提供了命令行工具以查看集群状态和进行基本集群操作:命令行工具说明
    • 对于集群管理员,可以参阅管理员指导文档来完成对集群的运维管理工作:运维工具说明
    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/deploy-with-k8s-operator.html b/CurveFS/deploy/deploy-with-k8s-operator.html index fd5e42c..1c4632c 100644 --- a/CurveFS/deploy/deploy-with-k8s-operator.html +++ b/CurveFS/deploy/deploy-with-k8s-operator.html @@ -4,13 +4,13 @@ Kubernetes 上部署 CurveFS 集群 | Curve Book - - + +
    跳到主要内容

    Kubernetes 上部署 CurveFS 集群

    curve-operator代码仓库:https://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md

    简介

    Curve-Operator用于管理 Kubernetes 系统上的 Curve 集群。更进一步地支持Curve实现云原生的分布式存储系统。

    Curve BS deploy architecture

    部署

    0. 先决条件

    • 安装Kubernetes 1.19/1.20版本。

    1. 安装Operator

    首先需要安装Curve Operaotr,然后再部署 Curve 集群。

    $ git clone https://github.com/opencurve/curve-operator.git
    $ cd curve-operator
    $ kubectl apply -f config/deploy/

    安装成功后,默认会创建curve namespace,确认curve-operator处于Running状态。

    $ kubectl get pod -n curve
    NAME READY STATUS RESTARTS AGE
    curve-operator-69bc69c75d-jfsjg 1/1 Running 0 7s

    2. 部署Curve集群

    Operator部署集群基于声明式的API,你可以在这个目录 config/sample 下找到所有的声明式yaml例子。并且可以自定义修改从而能够符合你当前的部署环境。

    CurveBS 单机部署

    CurveBS 三副本部署

    CurveFS 单机部署

    CurveFS 三副本部署

    这里我们以部署三副本的 CurveBS 集群为例进行说明。这里的声明文件是cluster.yaml。你可以根据yaml文件中的注释详细的了解每一个配置项的作用,从而自定义修改。

    输入如下命令创建集群:

    $ kubectl apply -f config/samples/cluster.yaml

    查看当前 curve namespac 下的所有的pod:

    $ kubectl -n curve get pod

    NAME READY STATUS RESTARTS AGE
    curve-chunkserver-curve-operator-node1-vdc-556fc99467-5nx9q 1/1 Running 0 5m45s
    curve-chunkserver-curve-operator-node2-vdc-7cf89768f9-hmcrs 1/1 Running 0 5m45s
    curve-chunkserver-curve-operator-node3-vdc-f77dd85dc-z5bws 1/1 Running 0 5m45s
    curve-etcd-a-d5bbfb755-lzgrm 1/1 Running 0 41m
    curve-etcd-b-66c5b54f75-6nnnt 1/1 Running 0 41m
    curve-etcd-c-86b7964f87-cj8zk 1/1 Running 0 41m
    curve-mds-a-7b5989bddd-ln2sm 1/1 Running 0 40m
    curve-mds-b-56d8f58645-gv6pd 1/1 Running 0 40m
    curve-mds-c-997c7fd-vt5hw 1/1 Running 0 40m
    gen-logical-pool-rzhlz 0/1 Completed 0 5m15s
    gen-physical-pool-chnw8 0/1 Completed 0 5m45s
    prepare-chunkfile-curve-operator-node1-vdc-znb66 0/1 Completed 0 40m
    prepare-chunkfile-curve-operator-node2-vdc-6gf2z 0/1 Completed 0 40m
    prepare-chunkfile-curve-operator-node3-vdc-2bkxm 0/1 Completed 0 40m
    read-config-k272k 0/1 Completed 0 41m

    说明:在执行完apply命令之后,通过get命令你可以不能立刻就能看到chunkserver pods。因为磁盘需要进行格式化,这个格式化的过程是通过prepare-chunkfile jobs去完成的。所以可能会等待一段时间之后才会看到所有的chunkserver pods。

    具体的等待时间需要根据你的磁盘的大小以及你定义的格式化的百分比,这个时间可能会很长。你可以通过日志查看格式化的进程。

    3. 检查集群的健康状态

    为了检查部署的集群是否是健康的,需要进入任何一个curve-chunkserver pod,然后使用curve_ops_tool status 去查看集群的健康状态。

    $ kubectl exec -it <any one chunkserver pod> -- bash
    $ curve_ops_tool status

    Cluster status:
    cluster is healthy
    total copysets: 100, unhealthy copysets: 0, unhealthy_ratio: 0%
    physical pool number: 1, logical pool number: 1
    Space info:
    physical: total = 1178GB, used = 6GB(0.56%), left = 1171GB(99.44%)
    logical: total = 392GB, used = 41GB(10.44%, can be recycled = 0GB(0.00%)), left = 351GB(89.56%), created file size = 60GB(15.28%)

    Client status:
    nebd-server: version-1.2.5+2c4861ca: 1
    ...

    Curve CSI

    在Kubernetes系统中,需要创建PVC从而使用 Curve 作为pod的后端存储。

    你可以部署对接 Curvebs 集群的 curve-csi 或者是对接 Curvefs 集群的 curvefs-csi。如何部署以及具体的实现细节可以参考对应的项目文档。

    删除

    为了删除已经部署的Curve集群并且清楚其中的数据,可能需要经历如下步骤。

    1. 删除集群CR

    $ kubectl -n curve delete curvecluster my-cluster

    为了验证cluster CR已经被删除,你可以通过如下命令查看:

    $ kubectl -n curve get curvecluster

    2. 删除Operator以及相关的资源

    $ kubectl delete -f config/deploy/

    3. 删除持久化在宿主机的数据和日志(慎重)

    为了彻底的清除集群,需要把集群中的数据和日志全部清除。这个目录是在cluster.yaml文件中定义的hostDataDir配置项。注意,在部署新集群之前一定要将这个配置目录下的内容删除。

    对于多副本部分的集群,数据分布在各个集群节点上,所以需要登录各个节点进行删除。例如,如果配置的目录是/curvebs的话,则需要删除这个目录下的所有数据和日志:

    rm -rf /curvebs
    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/distributed-cache.html b/CurveFS/deploy/distributed-cache.html index 772b2cf..b96dd89 100644 --- a/CurveFS/deploy/distributed-cache.html +++ b/CurveFS/deploy/distributed-cache.html @@ -4,13 +4,13 @@ 使用 CurveAdm 部署分布式缓存 | Curve Book - - + +
    跳到主要内容

    使用 CurveAdm 部署分布式缓存

    分布式缓存介绍

    分布式缓存集群不是必须要部署的,但如需进一步提升性能,可以为 CurveFS 集群部署分布式缓存服务(当前基于memcached集群),CurveFS 客户端缓存当前有内存、本地硬盘、分布式缓存3类,分别为L1、L2、L3级缓存,均支持读写缓存。

    基于memcached集群的分布式缓存,当前支持通过Extstore来通过硬盘扩展缓存容量,详情可参与官方文档:https://github.com/memcached/memcached/wiki/Extstore

    部署步骤

    部署分布式缓存集群,以及将缓存集群配置到 CurveFS 集群的详细步骤请参阅文档:部署分布式缓存

    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/offline-deploy.html b/CurveFS/deploy/offline-deploy.html index 07ad85a..7e2fe06 100644 --- a/CurveFS/deploy/offline-deploy.html +++ b/CurveFS/deploy/offline-deploy.html @@ -4,15 +4,15 @@ 离线部署 CurveFS 集群 | Curve Book - - + +
    跳到主要内容

    离线部署 CurveFS 集群

    本文适用于无法从公网docker镜像仓库拉取CurveFS docker镜像场景下的部署。

    离线环境准备

    Curve镜像准备

    1. 拉取镜像 Curve官方镜像(如opencurve/curve/curvefs:v2.4)到本地环境(可访问外网的机器)
    $ sudo docker pull opencurve/curve/curvefs:v2.4
    1. 导出镜像
    # 查看下载到的Curve镜像,镜像版本、id和大小为示例,具体以实际为准,下同
    $ sudo docker image ls
    opencurve/curve/curvefs v2.4 5717f16d4bec 1 months ago 1.84GB

    # 导出镜像
    $ sudo docker save -o curve_v2.4.tar 5717f16d4bec
    1. 拷贝镜像到本地仓库节点
    $ scp curve_v2.4.tar  ${desthost}:/path/to/save/image
    1. 导入镜像
    $ docker load --input curve_v2.4.tar

    # 查看导入的镜像
    $ sudo docker image ls
    opencurve/curve/curvefs v2.4 5717f16d4bec 1 months ago 1.84GB

    本地镜像仓库搭建

    使用docker-registry来进行本地仓库的搭建,搭建完本地仓库后, 把前面步骤中下载的Curve镜像上传到本地仓库。主要有以下几步:

    1. 运行 docker-registry
    $ docker run -d -p 5000:5000 --restart=always --name registry registry
    1. 标记Curve镜像 标记下载到的Curve镜像,比如把下载来的镜像(opencurve/curve/curvefs:v2.4)标记为127.0.0.1:5000/curvefs:v2.4_local(其中127.0.0.1为本地仓库服务IP,5000为本地仓库服务端口号,请根据实际环境修改)
    # 查看下载到的Curve镜像
    $ sudo docker image ls
    opencurve/curve/curvefs v2.4 5717f16d4bec 1 months ago 1.84GB

    # 标记镜像
    $ sudo docker tag opencurve/curve/curvefs:v2.4 127.0.0.1:5000/curvefs:v2.4_local

    # 查看标记完的镜像
    $ sudo docker image ls
    127.0.0.1:5000/curvefs v2.4_local 5717f16d4bec 13 months ago 1.84GB
    1. 上传镜像
    $ docker push 127.0.0.1:5000/curvefs:v2.4_local

    更多详情可参考私有仓库搭建

    修改镜像地址

    修改客户端部署配置文件client.yaml以及服务端集群部署配置文件topology.yaml中的镜像地址配置项(container_image)为本地仓库镜像地址(如:127.0.0.1:5000/curvefs:v2.4_local

    部署

    安装CurveAdm

    CurveAdm是Curve部署工具,有外网的机器可以一键安装,具体安装参见CurveAdm安装

    但由于本文是介绍内网环境的部署,所以需按如下步骤操作:

    • 下载CurveAdm到本地可访问外网机器,下载地址(最新版本可咨询Curve社区成员): CurveAdm
    • 把CurveAdm拷贝到内网安装需部署Curve集群的主控机
    • 解压CurveAdm
    • 拷贝执行程序并设置环境变量
    $ mv curveadm ~/.curveadm

    # 可考虑更新到~/.bash_profile进行持久化
    $ export PATH=~/.curveadm/bin:$PATH

    主机配置

    配置Curve集群要使用的服务器列表,提交列表给CurveAdm管理。主机配置过程比较简单,在hosts.yaml中添加实际主机名和ip,然后提交就可以了。

    具体配置参考文档:主机管理

    Curve服务端部署

    需修改topology.yaml中的镜像为本地镜像地址,示例如下:

    kind: curvefs
    global:
    container_image: 127.0.0.1:5000/curvefs:v2.4_local ## 修改为本地镜像

    其他的配置项请参考文档 CurveFS集群部署CurveBS集群部署

    client端部署

    需修改client.yaml中的镜像为本地镜像地址,示例如下:

    kind: curvefs
    global:
    container_image: 127.0.0.1:5000/curvefs:v2.4_local ## 修改为本地镜像

    其他的配置项请参考文档 部署CurveFS客户端部署CurveBS客户端

    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/quickstart.html b/CurveFS/deploy/quickstart.html index 6d05108..d25745c 100644 --- a/CurveFS/deploy/quickstart.html +++ b/CurveFS/deploy/quickstart.html @@ -4,13 +4,13 @@ CurveFS 快速体验 | Curve Book - - + +
    跳到主要内容

    CurveFS 快速体验

    为了提升 Curve 的运维便利性,我们设计开发了 CurveAdm 项目,其主要用于部署和管理 Curve 集群,目前已支持部署CurveBS & CurveFS,相关使用文档请参考 CurveAdm用户手册,并根据手册首先安装CurveAdm工具之后再进行Curve集群的部署。

    部署All-in-one体验环境

    请参考CurveAdm用户手册中CurveFS集群部署步骤,单机体验环境请使用“集群拓扑文件-单机部署”模板。

    命令行工具

    curve 提供了命令行工具以查看集群状态和进行基本集群操作:命令行工具说明

    - - + + \ No newline at end of file diff --git a/CurveFS/deploy/static-pv.html b/CurveFS/deploy/static-pv.html index a3a14a2..381412a 100644 --- a/CurveFS/deploy/static-pv.html +++ b/CurveFS/deploy/static-pv.html @@ -4,13 +4,13 @@ 静态PV配置 | Curve Book - - + +
    跳到主要内容

    静态PV配置

    CurveFS CSI使用node节点上的缓存盘

    如果client需要开启diskcache并使用宿主机额外的硬盘或云盘,需要在csi driver部署之前先把云盘或者本地盘挂载好。之后格式化硬盘或云盘然后mount到一个path(如/data/curvefs-diskcache),然后通过hostpath的方式映射进csi的daemonset pod里的/curvefs/client/data/cache路径,配置可以参考下面这个yaml:

    https://github.com/opencurve/curvefs-csi/blob/main/deploy/csi-node-daemonset-enable-cache.yaml

    上述配置文件中的 /data/curvefs-diskcache 对应的就是上面配置的缓存盘目录,可以修改为自己自定义的路径名称。

    一个开启diskcache的静态pv模板如下:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: static-pv-curvefs
    spec:
    accessModes:
    - ReadWriteMany
    capacity:
    storage: 1000Mi
    csi:
    driver: csi.curvefs.com
    volumeAttributes:
    fsType: s3
    mdsAddr: <mdsaddr>
    volumeHandle: <curvefs>
    mountOptions:
    - diskCache.diskCacheType=2 # 开启read和write缓存, 1为read缓存,默认为0是无缓存
    - fuseClient.supportKVcache: true # 以下配置可按需修改,下同
    - diskCache.forceFlush: false
    - enableSumInDir: false
    - diskCache.fullRatio: 95
    - diskCache.safeRatio: 50
    persistentVolumeReclaimPolicy: Retain # 不要修改该策略
    volumeMode: Filesystem

    静态PV实现跨namespace共享文件系统

    为了实现跨namespace共享文件系统,需要每一个namespace创建一组PVC和PV,其中PV指向的是后端的同一个FS。

    预先创建文件系统

    比如这里的文件系统名为curvefs,安装新工具并且配置配置文件,具体安装参考如下文档:

    https://github.com/opencurve/curve/tree/master/tools-v2#install

    安装完成后使用如下命令创建fs:

    curve fs create fs --fsname curvefs --fstype s3 --s3.ak ${AK} --s3.sk ${SK} --s3.endpoint http://localhost:9000 --s3.bucketname curvefs --s3.blocksize 4MiB --s3.chunksize 4MiB

    按照如下模板声明PV

    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: static-pv-curvefs
    spec:
    accessModes:
    - ReadWriteMany
    capacity:
    storage: 1000Mi
    csi:
    driver: csi.curvefs.com
    volumeAttributes:
    fsType: s3
    mdsAddr: <mdsaddr>
    volumeHandle: <curvefs>
    mountOptions:
    - diskCache.diskCacheType=2 # 开启read和write缓存,1为read缓存,默认为0是无缓存
    persistentVolumeReclaimPolicy: Retain # 不要修改该策略
    volumeMode: Filesystem

    需要自定义修改的字段:

    • mdsaddr:连接的fs集群的mds地址
    • volumeHandle:预先创建的fs的名字,比如curvefs

    其他重点说明:

    • persistentVolumeReclaiPolicy: 策略为Retain,不要试图修改

    声明PVC绑定上述PV

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
    name: static-pvc-curvefs
    namespace: <namespace>
    spec:
    accessModes:
    - ReadWriteMany
    resources:
    requests:
    storage: 100Mi
    storageClassName: ""
    volumeMode: Filesystem
    volumeName: static-pv-curvefs

    需要自定义修改的字段:

    • namespace:指定pvc的namespace,比如ns1。

    对需要共享pv的namespace重复上述2-3步骤

    对于不同的namespace,需要重复上述的2,3步骤,新建的PV指定同一个FS,新建的PVC指定新的namespace即可。

    这样不同namespace的pod挂载各自namespace下PVC即可。

    - - + + \ No newline at end of file diff --git a/CurveFS/maintenance/administrator-guide.html b/CurveFS/maintenance/administrator-guide.html index 50ea4b5..edf76db 100644 --- a/CurveFS/maintenance/administrator-guide.html +++ b/CurveFS/maintenance/administrator-guide.html @@ -4,13 +4,13 @@ 管理员操作指导 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveFS/maintenance/command-line-tools.html b/CurveFS/maintenance/command-line-tools.html index c3c3bc8..35f2c0c 100644 --- a/CurveFS/maintenance/command-line-tools.html +++ b/CurveFS/maintenance/command-line-tools.html @@ -4,15 +4,15 @@ 命令行工具 | Curve Book - - + +
    跳到主要内容

    命令行工具

    Curve 命令行工具代码仓库地址:https://github.com/opencurve/curve/blob/master/tools-v2/README.md

    工具目前还在继续开发完善之中,欢迎提交issue或pr。

    How to use curve tool

    Install

    install curve tool

    wget https://curve-tool.nos-eastchina1.126.net/release/curve-latest
    chmod +x curve-latest
    mv curve-latest /usr/bin/curve

    set configure file

    wget https://raw.githubusercontent.com/opencurve/curve/master/tools-v2/pkg/config/curve.yaml

    or

    wget https://curve-tool.nos-eastchina1.126.net/config/curve.yaml

    Please modify the mdsAddr, mdsDummyAddr, etcdAddr under curvefs/bs in the template.yaml file as required

    mv curve.yaml ~/.curve/curve.yaml

    or

    mv curve.yaml /etc/curve/curve.yaml

    Introduction

    Here's how to use the tool

    curve COMMAND [options]

    When you are not sure how to use a command, --help can give you an example of use:

    curve COMMAND --help

    For example:

    curve fs status mds --help
    Usage: curve fs status mds [flags]

    get the inode usage of curvefs

    Flags:
    -c, --conf string config file (default is $HOME/.curve/curve.yaml or /etc/curve/curve.yaml)
    -f, --format string Output format (json|plain) (default "plain")
    -h, --help Print usage
    --httptimeout duration http timeout (default 500ms)
    --mdsaddr string mds address, should be like 127.0.0.1:6700,127.0.0.1:6701,127.0.0.1:6702
    --mdsdummyaddr string mds dummy address, should be like 127.0.0.1:7700,127.0.0.1:7701,127.0.0.1:7702
    --showerror display all errors in command

    Examples:
    $ curve fs status mds

    In addition, this tool reads the configuration from $HOME/.curve/curve.yaml or /etc/curve/curve.yaml by default, and can be specified by --conf or -c.

    Command

    version

    show the version of curve tool

    Usage:

    curve --version

    Output:

    curve v1.2

    completion

    generate curve bash/zsh/fish completion script

    Take bash as an example:

    curve completion bash > /etc/bash_completion.d/curve

    If you perform completion you get the following error:

    curve [tab]bash: _get_comp_words_by_ref: command not found

    You need to install bash-completion:

    sudo apt install bash-completion
    source /usr/share/bash-completion/bash_completion

    upgrade

    Upgrade curve to latest version

    Usage:

    curve upgrade

    If you want to upgrade to the specified version(ee500438):

    CURVE_VERSION=ee500438 curve upgrade

    If you are in an offline environment, you can also set up a temporary http server to provide upgrade services for other machines:

    # 192.168.1.1
    echo "123456" > __version
    cp curve curve-123456
    python3 -m http.server 1234

    In this way, it can be upgraded on other machines

    CURVE_BASE_URL=http://192.168.1.1:1234/ curve upgrade

    fs

    check

    check copyset

    check copysets health in curvefs

    Usage:

    curve fs check copyset --copysetid 1 --poolid 1

    Output:

    +------------+-----------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | EXPLAIN |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    +------------+-----------+--------+--------+---------+

    create

    create fs

    create a fs in curvefs

    Usage:

    curve fs create fs --fsname test3

    Output:

    +--------+-------------------------------------------+
    | FSNAME | RESULT |
    +--------+-------------------------------------------+
    | test3 | fs exist, but s3 info is not inconsistent |
    +--------+-------------------------------------------+
    create topology

    create curvefs topology

    Usage:

    curve fs create topology --clustermap topology.json

    Output:

    +-------------------+--------+-----------+--------+
    | NAME | TYPE | OPERATION | PARENT |
    +-------------------+--------+-----------+--------+
    | pool2 | pool | del | |
    +-------------------+--------+ +--------+
    | zone4 | zone | | pool2 |
    +-------------------+--------+ +--------+
    | **.***.***.**_3_0 | server | | zone4 |
    +-------------------+--------+-----------+--------+

    delete

    delete fs

    delete a fs from curvefs

    Usage:

    curve fs delete fs --fsname test1
    WARNING:Are you sure to delete fs test1?
    please input [test1] to confirm: test1

    Output:

    +--------+-------------------------------------+
    | FSNAME | RESULT |
    +--------+-------------------------------------+
    | test1 | delete fs failed!, error is FS_BUSY |
    +--------+-------------------------------------+

    list

    list copyset

    list all copyset info of the curvefs

    Usage:

    curve fs list copyset

    Output:

    +------------+-----------+--------+-------+--------------------------------+------------+
    | KEY | COPYSETID | POOLID | EPOCH | LEADERPEER | PEERNUMBER |
    +------------+-----------+--------+-------+--------------------------------+------------+
    | 4294967302 | 6 | 1 | 2 | id:1 | 3 |
    | | | | | address:"**.***.***.**:6801:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967303 | 7 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967304 | 8 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967307 | 11 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+--------------------------------+------------+
    | 4294967297 | 1 | | 1 | id:2 | 3 |
    | | | | | address:"**.***.***.**:6802:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967301 | 5 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967308 | 12 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+--------------------------------+------------+
    | 4294967298 | 2 | | 1 | id:3 | 3 |
    | | | | | address:"**.***.***.**:6800:0" | |
    +------------+-----------+ +-------+ +------------+
    | 4294967299 | 3 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967300 | 4 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967305 | 9 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+ +-------+ +------------+
    | 4294967306 | 10 | | 1 | | 3 |
    | | | | | | |
    +------------+-----------+--------+-------+--------------------------------+------------+
    list fs

    list all fs info in the curvefs

    Usage:

    curve fs list fs

    Output:

    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | ID | NAME | STATUS | CAPACITY | BLOCKSIZE | FSTYPE | SUMINDIR | OWNER | MOUNTNUM |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | 2 | test1 | INITED | 107374182400 | 1048576 | TYPE_S3 | false | anonymous | 1 |
    +----+-------+--------+--------------+-----------+ +----------+ +----------+
    | 3 | test3 | INITED | 107374182400 | 1048576 | | false | | 0 |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    list mountpoint

    list all mountpoint of the curvefs

    Usage:

    curve fs list mountpoint

    Output:

    +------+--------+-------------------------------------------------------------------+
    | FSID | FSNAME | MOUNTPOINT |
    +------+--------+-------------------------------------------------------------------+
    | 2 | test1 | siku-QiTianM420-N000:9002:/curvefs/client/mnt/home/siku/temp/mnt1 |
    + + +-------------------------------------------------------------------+
    | | | siku-QiTianM420-N000:9003:/curvefs/client/mnt/home/siku/temp/mnt2 |
    +------+--------+-------------------------------------------------------------------+
    list partition

    list partition in curvefs by fsid

    Usage:

    curve fs list partition

    Output:

    +-------------+------+--------+-----------+----------+----------+-----------+
    | PARTITIONID | FSID | POOLID | COPYSETID | START | END | STATUS |
    +-------------+------+--------+-----------+----------+----------+-----------+
    | 14 | 2 | 1 | 10 | 1048676 | 2097351 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 20 | | | | 7340732 | 8389407 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 13 | | | 11 | 0 | 1048675 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 16 | | | | 3146028 | 4194703 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 22 | | | | 9438084 | 10486759 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 21 | | | 5 | 8389408 | 9438083 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 23 | | | 7 | 10486760 | 11535435 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 24 | | | | 11535436 | 12584111 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 15 | | | 8 | 2097352 | 3146027 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 18 | | | | 5243380 | 6292055 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 17 | | | 9 | 4194704 | 5243379 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 19 | | | | 6292056 | 7340731 | READWRITE |
    +-------------+------+ +-----------+----------+----------+-----------+
    | 26 | 3 | | 2 | 1048676 | 2097351 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 30 | | | | 5243380 | 6292055 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 34 | | | 3 | 9438084 | 10486759 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 29 | | | 4 | 4194704 | 5243379 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 32 | | | | 7340732 | 8389407 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 35 | | | 5 | 10486760 | 11535435 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 27 | | | | 2097352 | 3146027 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 33 | | | | 8389408 | 9438083 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 25 | | | 6 | 0 | 1048675 | READWRITE |
    +-------------+ + + +----------+----------+-----------+
    | 36 | | | | 11535436 | 12584111 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 28 | | | 8 | 3146028 | 4194703 | READWRITE |
    +-------------+ + +-----------+----------+----------+-----------+
    | 31 | | | 9 | 6292056 | 7340731 | READWRITE |
    +-------------+------+--------+-----------+----------+----------+-----------+
    list topology

    list the topology of the curvefs

    Usage:

    curve fs list topology

    Output:

    +----+------------+--------------------+------------+-----------------------+
    | ID | TYPE | NAME | CHILDTYPE | CHILDLIST |
    +----+------------+--------------------+------------+-----------------------+
    | 1 | pool | pool1 | zone | zone3 zone2 zone1 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | zone | zone3 | server | **.***.***.**_2_0 |
    +----+ +--------------------+ +-----------------------+
    | 2 | | zone2 | | **.***.***.**_1_0 |
    +----+ +--------------------+ +-----------------------+
    | 1 | | zone1 | | **.***.***.**_0_0 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | server | **.***.***.**_2_0 | metaserver | curvefs-metaserver.2 |
    +----+ +--------------------+ +-----------------------+
    | 2 | | **.***.***.**_1_0 | | curvefs-metaserver.1 |
    +----+ +--------------------+ +-----------------------+
    | 1 | | **.***.***.**_0_0 | | curvefs-metaserver.3 |
    +----+------------+--------------------+------------+-----------------------+
    | 3 | metaserver | curvefs-metaserver | | |
    +----+ +--------------------+------------+-----------------------+
    | 2 | | curvefs-metaserver | | |
    +----+ +--------------------+------------+-----------------------+
    | 1 | | curvefs-metaserver | | |
    +----+------------+--------------------+------------+-----------------------+

    query

    query copyset

    query copysets in curvefs

    Usage:

    curve fs query copyset --copysetid 1 --poolid 1

    Output:

    +------------+-----------+--------+--------------------------------------+-------+
    | copysetKey | copysetId | poolId | leaderPeer | epoch |
    +------------+-----------+--------+--------------------------------------+-------+
    | 4294967297 | 1 | 1 | id:2 address:"**.***.***.**:6802:0" | 1 |
    +------------+-----------+--------+--------------------------------------+-------+
    query fs

    query fs in curvefs by fsname or fsid

    Usage:

    curve fs query fs --fsname test1

    Output:

    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | id | name | status | capacity | blocksize | fsType | sumInDir | owner | mountNum |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    | 2 | test1 | INITED | 107374182400 | 1048576 | TYPE_S3 | false | anonymous | 2 |
    +----+-------+--------+--------------+-----------+---------+----------+-----------+----------+
    query inode

    query the inode of fs

    Usage:

    curve fs query inode --fsid 2 --inodeid 5243380

    Output:

    +-------+----------+-----------+---------+-------+--------+
    | fs id | inode id | length | type | nlink | parent |
    +-------+----------+-----------+---------+-------+--------+
    | 2 | 5243380 | 352321536 | TYPE_S3 | 1 | [1] |
    +-------+----------+-----------+---------+-------+--------+
    query metaserver

    query metaserver in curvefs by metaserverid or metaserveraddr

    Usage:

    curve fs query metaserver --metaserveraddr **.***.***.**:6801,**.***.***.**:6802

    Output:

    +----+--------------------+--------------------+--------------------+-------------+
    | id | hostname | internalAddr | externalAddr | onlineState |
    +----+--------------------+--------------------+--------------------+-------------+
    | 1 | curvefs-metaserver | **.***.***.**:6801 | **.***.***.**:6801 | ONLINE |
    | 2 | curvefs-metaserver | **.***.***.**:6802 | **.***.***.**:6802 | ONLINE |
    +----+--------------------+--------------------+--------------------+-------------+
    query partition

    query the copyset of partition

    Usage:

    curve fs query partition --partitionid 14

    Output:

    +----+--------+-----------+--------+----------------------+
    | id | poolId | copysetId | peerId | peerAddr |
    +----+--------+-----------+--------+----------------------+
    | 14 | 1 | 10 | 1 | **.***.***.**:6801:0 |
    | 14 | 1 | 10 | 2 | **.***.***.**:6802:0 |
    | 14 | 1 | 10 | 3 | **.***.***.**:6800:0 |
    +----+--------+-----------+--------+----------------------+

    status

    status mds

    get status of mds

    Usage:

    curve fs status mds

    Output:

    +--------------------+--------------------+----------------+----------+
    | addr | dummyAddr | version | status |
    +--------------------+--------------------+----------------+----------+
    | **.***.***.**:6700 | **.***.***.**:7700 | 8fc48476+debug | follower |
    | **.***.***.**:6701 | **.***.***.**:7701 | 8fc48476+debug | follower |
    | **.***.***.**:6702 | **.***.***.**:7702 | 8fc48476+debug | leader |
    +--------------------+--------------------+----------------+----------+
    status metaserver

    get status of metaserver

    Usage:

    curve fs status metaserver

    Output:

    +--------------------+--------------------+----------------+--------+
    | externalAddr | internalAddr | version | status |
    +--------------------+--------------------+----------------+--------+
    | **.***.***.**:6800 | **.***.***.**:6800 | 8fc48476+debug | online |
    | **.***.***.**:6802 | **.***.***.**:6802 | 8fc48476+debug | online |
    | **.***.***.**:6801 | **.***.***.**:6801 | 8fc48476+debug | online |
    +--------------------+--------------------+----------------+--------+
    status etcd

    get status of etcd

    Usage:

    curve fs status etcd

    Output:

    +---------------------+---------+----------+
    | addr | version | status |
    +---------------------+---------+----------+
    | **.***.***.**:23790 | 3.4.10 | follower |
    | **.***.***.**:23791 | 3.4.10 | follower |
    | **.***.***.**:23792 | 3.4.10 | leader |
    +---------------------+---------+----------+
    status copyset

    get status of copyset

    Usage:

    curve fs status copyset

    Output:

    +------------+-----------+--------+--------+---------+
    | copysetKey | copysetId | poolId | status | explain |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    | 4294967298 | 2 | 1 | ok | |
    | 4294967299 | 3 | 1 | ok | |
    | 4294967300 | 4 | 1 | ok | |
    | 4294967301 | 5 | 1 | ok | |
    | 4294967302 | 6 | 1 | ok | |
    | 4294967303 | 7 | 1 | ok | |
    | 4294967304 | 8 | 1 | ok | |
    | 4294967305 | 9 | 1 | ok | |
    | 4294967306 | 10 | 1 | ok | |
    | 4294967307 | 11 | 1 | ok | |
    | 4294967308 | 12 | 1 | ok | |
    +------------+-----------+--------+--------+---------+
    status cluster

    get status of cluster

    Usage:

    curve fs status cluster

    Output:

    etcd:
    +---------------------+---------+----------+
    | addr | version | status |
    +---------------------+---------+----------+
    | **.***.***.**:23790 | 3.4.10 | follower |
    | **.***.***.**:23791 | 3.4.10 | follower |
    | **.***.***.**:23792 | 3.4.10 | leader |
    +---------------------+---------+----------+

    mds:
    +--------------------+--------------------+----------------+----------+
    | addr | dummyAddr | version | status |
    +--------------------+--------------------+----------------+----------+
    | **.***.***.**:6700 | **.***.***.**:7700 | 8fc48476+debug | follower |
    | **.***.***.**:6701 | **.***.***.**:7701 | 8fc48476+debug | follower |
    | **.***.***.**:6702 | **.***.***.**:7702 | 8fc48476+debug | leader |
    +--------------------+--------------------+----------------+----------+

    meataserver:
    +--------------------+--------------------+----------------+--------+
    | externalAddr | internalAddr | version | status |
    +--------------------+--------------------+----------------+--------+
    | **.***.***.**:6800 | **.***.***.**:6800 | 8fc48476+debug | online |
    | **.***.***.**:6802 | **.***.***.**:6802 | 8fc48476+debug | online |
    | **.***.***.**:6801 | **.***.***.**:6801 | 8fc48476+debug | online |
    +--------------------+--------------------+----------------+--------+

    copyset:
    +------------+-----------+--------+--------+---------+
    | copysetKey | copysetId | poolId | status | explain |
    +------------+-----------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | |
    | 4294967298 | 2 | 1 | ok | |
    | 4294967299 | 3 | 1 | ok | |
    | 4294967300 | 4 | 1 | ok | |
    | 4294967301 | 5 | 1 | ok | |
    | 4294967302 | 6 | 1 | ok | |
    | 4294967303 | 7 | 1 | ok | |
    | 4294967304 | 8 | 1 | ok | |
    | 4294967305 | 9 | 1 | ok | |
    | 4294967306 | 10 | 1 | ok | |
    | 4294967307 | 11 | 1 | ok | |
    | 4294967308 | 12 | 1 | ok | |
    +------------+-----------+--------+--------+---------+

    Cluster health is: ok

    umount

    umount fs

    umount fs from the curvefs cluster

    Usage:

    curve fs umount fs --fsname test1 --mountpoint siku-QiTianM420-N000:9002:/curvefs/client/mnt/home/siku/temp/mnt1

    Output:

    +--------+-------------------------------------------------------------------+---------+
    | fsName | mountpoint | result |
    +--------+-------------------------------------------------------------------+---------+
    | test1 | siku-QiTianM420-N000:9003:/curvefs/client/mnt/home/siku/temp/mnt2 | success |
    +--------+-------------------------------------------------------------------+---------+

    usage

    usage inode

    get the inode usage of curvefs

    Usage:

    curve fs usage inode

    Output:

    +------+----------------+-----+
    | fsId | fsType | num |
    +------+----------------+-----+
    | 2 | inode_num | 3 |
    | 2 | type_directory | 1 |
    | 2 | type_file | 0 |
    | 2 | type_s3 | 2 |
    | 2 | type_sym_link | 0 |
    | 3 | inode_num | 1 |
    | 3 | type_directory | 1 |
    | 3 | type_file | 0 |
    | 3 | type_s3 | 0 |
    | 3 | type_sym_link | 0 |
    +------+----------------+-----+
    usage metadata

    get the usage of metadata in curvefs

    Usage:

    curve fs usage metadata

    Output:

    +--------------------+---------+---------+---------+
    | metaserverAddr | total | used | left |
    +--------------------+---------+---------+---------+
    | **.***.***.**:6800 | 2.0 TiB | 182 GiB | 1.8 TiB |
    | **.***.***.**:6802 | 2.0 TiB | 182 GiB | 1.8 TiB |
    | **.***.***.**:6801 | 2.0 TiB | 182 GiB | 1.8 TiB |
    +--------------------+---------+---------+---------+

    warmup

    warmup add

    warmup a file(directory), or given a list file contains a list of files(directories) that you want to warmup.

    Usage:

    curve fs warmup add /mnt/curvefs/warmup
    curve fs warmup add --filelist /mnt/curvefs/warmup.list

    curve fs warmup add /mnt/curvefs/warmup will warmup a file(directory). /mnt/curvefs/warmup.list

    bs

    list

    list logical-pool

    list all logical pool information

    Usage:

    curve bs list logical-pool

    Output:

    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    | ID | NAME | PHYPOOLID | TYPE | ALLOC | SCAN | TOTAL | USED | LEFT | RECYCLE |
    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    | 1 | pool1 | 1 | PAGEFILE | ALLOW | true | 44 GiB | 0 B | 44 GiB | 0 B |
    +----+-------+-----------+----------+-------+------+--------+------+--------+---------+
    list server

    list all server information in curvebs

    Usage:

    curve bs list server

    Output:

    +----+---------------------+------+---------+-------------------+-------------------+
    | ID | HOSTNAME | ZONE | PHYPOOL | INTERNALADDR | EXTERNALADDR |
    +----+---------------------+------+---------+-------------------+-------------------+
    | 1 | ***************_0_0 | 1 | 1 | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+ +-------------------+-------------------+
    | 2 | ***************_1_0 | 2 | | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+ +-------------------+-------------------+
    | 3 | ***************_2_0 | 3 | | **.***.**.**:**** | **.***.**.**:**** |
    +----+---------------------+------+---------+-------------------+-------------------+
    list client

    list all client information in curvebs

    curve bs list client

    Output:

    +------------+------+
    | IP | PORT |
    +------------+------+
    | 172.17.0.2 | 9000 |
    +------------+------+
    list dir

    list dir information in curvebs

    curve bs list dir --path /

    Output:

    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    | ID | FILENAME | PARENTID | FILETYPE | OWNER | CTIME | ALLOCATEDSIZE | FILESIZE |
    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    | 1 | /RecycleBin | 0 | INODE_DIRECTORY | root | 2022-11-12 16:38:25 | 0 B | 0 B |
    +------+-------------+----------+-----------------+------------+---------------------+---------------+-------------+
    list space

    show curvebs all disk type space, include total space and used space

    curve bs list space

    Output:

    +----------+---------+---------+---------+------------+---------+
    | TYPE | TOTAL | USED | LEFT | RECYCLABLE | CREATED |
    +----------+---------+---------+---------+------------+---------+
    | physical | *** GiB | *** GiB | *** GiB | - | - |
    +----------+---------+---------+---------+------------+---------+
    | logical | *** GiB | *** GiB | *** GiB | *** GiB | *** GiB |
    +----------+---------+---------+---------+------------+---------+
    list chunkserver

    list chunkserver information in curvebs

    curve bs list chunkserver

    Output:

    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    | ID | TYPE | IP | PORT | RWSTATUS | DISKSTATE | COPYSETNUM | MOUNTPOINT | DISKCAPACITY | DISKUSED | UNHEALTHYCOPYSET | EXTADDR |
    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    | 1 | nvme | 127.0.0.1 | 8201 | READWRITE | DISKNORMAL | 100 | local:///curvebs/playground/chunkserver1/data | 39 GiB | 42140479488 | 0 % | 127.0.0.1 |
    +----+ + +------+ + +------------+-----------------------------------------------+--------------+-------------+------------------+ +
    | 2 | | | 8202 | | | 100 | local:///curvebs/playground/chunkserver2/data | 39 GiB | 42140479488 | 0 % | |
    +----+ + +------+ + +------------+-----------------------------------------------+--------------+-------------+------------------+ +
    | 3 | | | 8200 | | | 100 | local:///curvebs/playground/chunkserver0/data | 39 GiB | 42140479488 | 0 % | |
    +----+------+-----------+------+-----------+------------+------------+-----------------------------------------------+--------------+-------------+------------------+-----------+
    list scan-status

    list curvebs all copyset that scanning is false

    curve bs list scan-status

    Output:

    +-------------+-----------+
    | LOGICALPOOL | COPYSETID |
    +-------------+-----------+
    | 1 | 1 |
    +-------------+-----------+
    | 1 | 10 |
    +-------------+-----------+
    | 1 | 100 |
    +-------------+-----------+
    | 1 | 11 |
    +-------------+-----------+
    | 1 | 12 |
    +-------------+-----------+
    | 1 | 13 |
    +-------------+-----------+
    | 1 | 14 |
    +-------------+-----------+
    | 1 | 15 |
    +-------------+-----------+
    | 1 | 16 |
    +-------------+-----------+
    | 1 | 17 |
    +-------------+-----------+
    | 1 | 18 |
    +-------------+-----------+
    | 1 | 19 |
    +-------------+-----------+
    | 1 | 2 |
    +-------------+-----------+
    | 1 | 20 |
    +-------------+-----------+
    | 1 | 21 |
    +-------------+-----------+
    | 1 | 22 |
    +-------------+-----------+
    | 1 | 23 |
    +-------------+-----------+
    | 1 | 24 |
    +-------------+-----------+
    | 1 | 25 |
    +-------------+-----------+
    | 1 | 26 |
    +-------------+-----------+
    | 1 | 27 |
    +-------------+-----------+
    | 1 | 28 |
    +-------------+-----------+
    | 1 | 29 |
    +-------------+-----------+
    | 1 | 3 |
    +-------------+-----------+
    | 1 | 30 |
    +-------------+-----------+
    | 1 | 31 |
    +-------------+-----------+
    | 1 | 32 |
    +-------------+-----------+
    | 1 | 33 |
    +-------------+-----------+
    | 1 | 34 |
    +-------------+-----------+
    | 1 | 35 |
    +-------------+-----------+
    | 1 | 36 |
    +-------------+-----------+
    | 1 | 37 |
    +-------------+-----------+
    | 1 | 38 |
    +-------------+-----------+
    | 1 | 39 |
    +-------------+-----------+
    | 1 | 4 |
    +-------------+-----------+
    | 1 | 40 |
    +-------------+-----------+
    | 1 | 41 |
    +-------------+-----------+
    | 1 | 42 |
    +-------------+-----------+
    | 1 | 43 |
    +-------------+-----------+
    | 1 | 44 |
    +-------------+-----------+
    | 1 | 45 |
    +-------------+-----------+
    | 1 | 46 |
    +-------------+-----------+
    | 1 | 47 |
    +-------------+-----------+
    | 1 | 48 |
    +-------------+-----------+
    | 1 | 49 |
    +-------------+-----------+
    | 1 | 5 |
    +-------------+-----------+
    | 1 | 50 |
    +-------------+-----------+
    | 1 | 51 |
    +-------------+-----------+
    | 1 | 52 |
    +-------------+-----------+
    | 1 | 53 |
    +-------------+-----------+
    | 1 | 54 |
    +-------------+-----------+
    | 1 | 55 |
    +-------------+-----------+
    | 1 | 56 |
    +-------------+-----------+
    | 1 | 57 |
    +-------------+-----------+
    | 1 | 58 |
    +-------------+-----------+
    | 1 | 59 |
    +-------------+-----------+
    | 1 | 6 |
    +-------------+-----------+
    | 1 | 60 |
    +-------------+-----------+
    | 1 | 61 |
    +-------------+-----------+
    | 1 | 62 |
    +-------------+-----------+
    | 1 | 63 |
    +-------------+-----------+
    | 1 | 64 |
    +-------------+-----------+
    | 1 | 65 |
    +-------------+-----------+
    | 1 | 66 |
    +-------------+-----------+
    | 1 | 67 |
    +-------------+-----------+
    | 1 | 68 |
    +-------------+-----------+
    | 1 | 69 |
    +-------------+-----------+
    | 1 | 7 |
    +-------------+-----------+
    | 1 | 70 |
    +-------------+-----------+
    | 1 | 71 |
    +-------------+-----------+
    | 1 | 72 |
    +-------------+-----------+
    | 1 | 73 |
    +-------------+-----------+
    | 1 | 74 |
    +-------------+-----------+
    | 1 | 75 |
    +-------------+-----------+
    | 1 | 76 |
    +-------------+-----------+
    | 1 | 77 |
    +-------------+-----------+
    | 1 | 78 |
    +-------------+-----------+
    | 1 | 79 |
    +-------------+-----------+
    | 1 | 8 |
    +-------------+-----------+
    | 1 | 80 |
    +-------------+-----------+
    | 1 | 81 |
    +-------------+-----------+
    | 1 | 82 |
    +-------------+-----------+
    | 1 | 83 |
    +-------------+-----------+
    | 1 | 84 |
    +-------------+-----------+
    | 1 | 85 |
    +-------------+-----------+
    | 1 | 86 |
    +-------------+-----------+
    | 1 | 87 |
    +-------------+-----------+
    | 1 | 88 |
    +-------------+-----------+
    | 1 | 89 |
    +-------------+-----------+
    | 1 | 9 |
    +-------------+-----------+
    | 1 | 90 |
    +-------------+-----------+
    | 1 | 91 |
    +-------------+-----------+
    | 1 | 92 |
    +-------------+-----------+
    | 1 | 93 |
    +-------------+-----------+
    | 1 | 94 |
    +-------------+-----------+
    | 1 | 95 |
    +-------------+-----------+
    | 1 | 96 |
    +-------------+-----------+
    | 1 | 97 |
    +-------------+-----------+
    | 1 | 98 |
    +-------------+-----------+
    | 1 | 99 |
    +-------------+-----------+
    list may-broken-vol

    list may broken volumes

    Usage:

    curve bs list may-broken-vol

    Output:

    +----------+
    | FILENAME |
    +----------+
    | test |
    +----------+

    clean-recycle

    clean the recycle bin

    Usage:

    curve bs clean-recycle --recycleprefix=/test --expiredtime=1h

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+

    query

    query file

    query the file info and actual space

    Usage:

    curve bs query file --path=/test

    Output:

    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    | ID | NAME | TYPE | OWNER | CHUNK | SEGMENT | LENGTH | SEQ | CTIME | STATUS | STRIPE | THROTTLE | ALLOC |
    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    | 1003 | test | INODE_PAGEFILE | test | 16 MiB | 1.0 GiB | 10 GiB | 1 | 2022-08-29 17:00:55 | kFileCreated | count:0 | type:IOPS_TOTAL | size:0 B |
    | | | | | | | | | | | uint:0 | limit:2000 | |
    | | | | | | | | | | | | type:BPS_TOTAL | |
    | | | | | | | | | | | | limit:125829120 | |
    +------+------+----------------+-------+--------+---------+--------+-----+---------------------+--------------+---------+-----------------+----------+
    query chunk

    query the location of the chunk corresponding to the offset

    Usage:

    curve bs query chunk --path /test1 --offset 1008600000 

    Output:

    +-------+-------------+---------+------------+----------------------+
    | CHUNK | LOGICALPOOL | COPYSET | GROUP | LOCATION |
    +-------+-------------+---------+------------+----------------------+
    | 61 | 1 | 61 | 4294967357 | ***.***.***.***:**** |
    | | | | | ***.***.***.***:**** |
    | | | | | ***.***.***.***:**** |
    +-------+-------------+---------+------------+----------------------+
    query segment

    query the segments info of the file

    Usage:

    curve bs query seginfo --path /test1 

    Output:

    +-------------+-------------+-----------+------------+---------+-------+
    | LOGICALPOOL | SEGMENTSIZE | CHUNKSIZE | START | COPYSET | CHUNK |
    +-------------+-------------+-----------+------------+---------+-------+
    | 1 | 1073741824 | 16777216 | 0 | 1 | 1 |
    + + + + +---------+-------+
    | ...... |
    + + + +------------+---------+-------+
    | | | | 9663676416 | 1 | 101 |
    + + + + +---------+-------+
    | ...... |
    + + + + +---------+-------+
    | | | | | 99 | 99 |
    +-------------+-------------+-----------+------------+---------+-------+
    query scan-status

    quey ScanStatus Info in bs

    Usage:

    curve bs query scan-satus --copysetid 1 --logicalpoolid 1

    Output:

    +-------------+-----------+-------+-------------+--------------------+
    | LOGICALPOOL | COPYSETID | SCAN | LASTSCANSEC | LASTSCANCONSISTENT |
    +-------------+-----------+-------+-------------+--------------------+
    | 1 | 1 | false | 1684425801 | true |
    +-------------+-----------+-------+-------------+--------------------+

    status

    status etcd

    get the etcd status of curvebs

    Usage:

    curve bs status etcd

    Output:

    +---------------------+---------+----------+
    | ADDR | VERSION | STATUS |
    +---------------------+---------+----------+
    | ***.***.*.***:***** | 3.4.10 | follower |
    +---------------------+ + +
    | ***.***.*.***:***** | | |
    +---------------------+ +----------+
    | ***.***.*.***:***** | | leader |
    +---------------------+---------+----------+
    status mds

    get the mds status of curvebs

    Usage:

    curve bs status mds

    Output:

    +-------------------+-------------------+-------------------+----------+
    | ADDR | DUMMYADDR | VERSION | STATUS |
    +-------------------+-------------------+-------------------+----------+
    | **.***.**.**:**** | **.***.**.**:**** | ci+562296c7+debug | follower |
    +-------------------+-------------------+ + +
    | **.***.**.**:**** | **.***.**.**:**** | | |
    +-------------------+-------------------+ +----------+
    | **.***.**.**:**** | **.***.**.**:**** | | leader |
    +-------------------+-------------------+-------------------+----------+
    status client

    get the client status of curvebs

    Usage:

    curve bs status client

    Output:

    +-------------+----------------+---------------------+-----+
    | TYPE | VERSION | ADDR | NUM |
    +-------------+----------------+---------------------+-----+
    | nebd-server | 9.9.9+2c4861ca | ***.***.**.***:**** | 2 |
    + + +---------------------+ +
    | | | ***.***.**.***:**** | |
    +-------------+----------------+---------------------+-----+
    status snapshotserver

    get the mds status of curvebs

    Usage:

    curve bs status snapshotserver

    Output:

    +---------------------+---------------------+-------------------+----------+
    | ADDR | DUMMYADDR | VERSION | STATUS |
    +---------------------+---------------------+-------------------+----------+
    | ***.***.**.***:**** | ***.***.**.***:**** | ci+562296c7+debug | follower |
    +---------------------+---------------------+ + +
    | ***.***.**.***:**** | ***.***.**.***:**** | | |
    +---------------------+---------------------+ +----------+
    | ***.***.**.***:**** | ***.***.**.***:**** | | leader |
    +---------------------+---------------------+-------------------+----------+
    status chunkserver

    get the chunkserver status of curvebs

    Usage:

    curve bs status chunkserver

    Output:

    +------------------+------------------+----------------+--------+------------+
    | EXTERNALADDR | INTERNALADDR | VERSION | STATUS | RECOVERING |
    +------------------+------------------+----------------+--------+------------+
    | **************** | **************** | d9b6bb98+debug | online | false |
    +------------------+------------------+ + + +
    | **************** | **************** | | | |
    +------------------+------------------+ + + +
    | **************** | **************** | | | |
    +------------------+------------------+----------------+--------+------------+
    status copyset

    get the copyset status of curvebs

    Usage:

    curve bs status copyset

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | LOGGAP | EXPLAIN |
    +------------+-----------+--------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | 0 | |
    +------------+-----------+ + +--------+---------+
    | ...... | ...... | ...... | ...... | ...... | ...... |
    +------------+-----------+ + +--------+---------+
    | 4294967395 | 99 | | | 0 | |
    +------------+-----------+--------+--------+--------+---------+

    delete

    delete peer

    delete the peer from the copyset

    Usage:

    curve bs delete peer

    Output:

    +------------------+------------------+---------+---------+--------+
    | LEADER | PEER | COPYSET | RESULT | REASON |
    +------------------+------------------+---------+---------+--------+
    | 127.0.0.1:8201:0 | 127.0.0.1:8202:0 | (1:29) | success | null |
    +------------------+------------------+---------+---------+--------+
    delete volume
    delete volume clone

    delete volume clone tasks in curvebs cluster

    Usage:

    curve bs delete volume clone

    Output:

    +------+--------------------------------------+--------------------------------------+-------+---------+            
    | USER | SRC | TASK ID | FILE | RESULT |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    | root | a19b5e5e-b306-488f-8e6d-d87282c869cb | d26e27a8-fcbd-4f7a-adf8-53795217cbb0 | /root | success |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    delete volume recover

    delete volume recover tasks in curvebs cluster

    Usage:

    curve bs delete volume recover

    Output:

    +------+--------------------------------------+--------------------------------------+-------+---------+             
    | USER | SRC | TASK ID | FILE | RESULT |
    +------+--------------------------------------+--------------------------------------+-------+---------+
    | root | a19b5e5e-b306-488f-8e6d-d87282c869cb | 9dfa8699-a275-4891-8ec2-e447a0ccc77c | /root | success |
    +------+--------------------------------------+--------------------------------------+-------+---------+

    update

    update peer

    reset peer

    Usage:

    curve bs update peer 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1

    Output:

    +----------------------+---------+---------+--------+
    | PEER | COPYSET | RESULT | REASON |
    +----------------------+---------+---------+--------+
    | 127.0.0.0:8200:0 | (1:1) | success | null |
    +----------------------+---------+---------+--------+
    update leader

    transfer leader

    Usage:

    curve bs update leader 127.0.0.1:8202:0 --logicalpoolid=1 --copysetid=1 --peers=127.0.0.1:8200:0,127.0.0.1:8201:0,127.0.0.1:8202:0

    Output:

    +-----------------------+-----------------------+---------+---------+
    | LEADER | OLDLEADER | COPYSET | RESULT |
    +-----------------------+-----------------------+---------+---------+
    | ***.***.**.***:****:* | ***.***.**.***:****:* | (1:1) | success |
    +-----------------------+-----------------------+---------+---------+
    update file

    expand pagefile

    Usage:

    curve bs update file --path /test2/test1 --size 10

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    update throttle

    update file throttle params

    Usage:

    curve bs update throttle --path /test1 --type=bps_total --limit 20000

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    update scan-state

    enable/disable scan for logical pool

    Usage:

    curve bs update scan-state --logicalpoolid 1 [--scan=true/false]

    Output:

    +----+------+---------+--------+
    | ID | SCAN | RESULT | REASON |
    +----+------+---------+--------+
    | 1 | true | success | null |
    +----+------+---------+--------+
    update copyset availflag

    update copyset availflag

    Usage:

    curve bs update copyset availflag --availflag=true [--dryrun=true/false]

    Output:

    +--------+-----------+---------------+--------+
    | POOLID | COPYSETID | AVAILFLAG | DRYRUN |
    +--------+-----------+---------------+--------+
    | 1 | 1 | false => true | true |
    +--------+-----------+---------------+--------+
    update leader-schedule

    "rapidly transfer leader

    Usage:

    curve bs update leader-schedule --logicalpoolid 1
    curve bs update leader-schedule --all

    Output:

    +---------+--------+
    | RESULT | REASON |
    +---------+--------+
    | success | null |
    +---------+--------+

    create

    create file

    create pagefile

    Usage:

    curve bs create file --path /test2/test4  --size 10

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+
    create dir

    create directory

    Usage:

    curve bs create dir --path /test2/test5 

    Output:

    +---------+
    | RESULT |
    +---------+
    | success |
    +---------+

    check

    check copyset

    check copysets health in curvebs

    Usage:

    curve bs check copyset --copysetid 1 --logicalpoolid 1

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | COPYSETKEY | COPYSETID | POOLID | STATUS | LOGGAP | EXPLAIN |
    +------------+-----------+--------+--------+--------+---------+
    | 4294967297 | 1 | 1 | ok | 0 | |
    +------------+-----------+--------+--------+--------+---------+
    check chunkserver

    check chunkserver health in curvebs

    Usage:

    curve bs check chunkserver --chunkserverid 1

    Output:

    +------------+-----------+--------+--------+--------+---------+
    | CHUNKSERVERID | HELATHYCOUNT | UNHEALTHYCOUNT | UNHEALTHYRATIO |
    +------------+-----------+--------+--------+--------+---------+
    | 1 | 100 | 0 | 0.00% |
    +------------+-----------+--------+--------+--------+---------+
    check server

    check copysets health in server

    Usage:

    curve bs check server --serverid 1
    curve bs check server --ip 127.0.0.1 --port 8200

    Output:

    +--------+-----------+-------+------------------+
    | SERVER | IP | TOTAL | UNHEALTHYCOPYSET |
    +--------+-----------+-------+------------------+
    | 1 | 127.0.0.1 | 100 | 0(0%) |
    +--------+-----------+-------+------------------+

    snapshot

    snapshot copyset

    take snapshot for copyset

    Usage:

    curve bs snapshot copyset 127.0.0.0:8200:0 --logicalpoolid=1 --copysetid=1

    Output:

    +-----------------------+---------+---------+
    | PEER | COPYSET | RESULT |
    +-----------------------+---------+---------+
    | ***.***.**.***:****:* | (**:**) | success |
    +-----------------------+---------+---------+
    curve bs snapshot copyset --all

    Output:

    +----------------+---------+
    | CHUNKSERVER | RESULT |
    +----------------+---------+
    | **.*.*.**:8200 | failed |
    +----------------+---------+
    | **.*.*.**:8201 | success |
    +----------------+ +
    | **.*.*.**:8202 | |
    +----------------+---------+

    Comparison of old and new commands

    curve fs

    oldnew
    curvefs_tool check-copysetcurve fs check copyset
    curvefs_tool create-fscurve fs create fs
    curvefs_tool create-topologycurve fs create topology
    curvefs_tool delete-fscurve fs delete fs
    curvefs_tool list-copysetcurve fs list copyset
    curvefs_tool list-fscurve fs list fs
    curvefs_tool list-fscurve fs list mountpoint
    curvefs_tool list-partitioncurve fs list partition
    curvefs_tool query-copysetcurve fs query copyset
    curvefs_tool query-fscurve fs query fs
    curvefs_tool query-inodecurve fs query inode
    curvefs_tool query-metaservercurve fs query metaserver
    curvefs_tool query-partitioncurve fs query partition
    curvefs_tool status-mdscurve fs status mds
    curvefs_tool status-metaservercurve fs status metaserver
    curvefs_tool status-etcdcurve fs status etcd
    curvefs_tool status-copysetcurve fs status copyset
    curvefs_tool status-clustercurve fs status cluster
    curvefs_tool umount-fscurve fs umount fs
    curvefs_tool usage-inodecurve fs usage inode
    curvefs_tool usage-metadatacurve fs usage metadata

    curve bs

    oldnew
    curve_ops_tool logical-pool-listcurve bs list logical-pool
    curve_ops_tool get -fileName=curve bs query file -path
    curve_ops_tool etcd-statuscurve bs status etcd
    curve_ops_tool mds-statuscurve bs status mds
    curve_ops_tool server-listcurve bs list server
    curve_ops_tool client-listcurve bs list client
    curve_ops_tool deletecurve bs delete file
    curve_ops_tool listcurve bs list dir
    curve_ops_tool createcurve bs create file/dir
    curve_ops_tool seginfocurve bs query seginfo
    curve_ops_tool chunk-locationcurve bs query chunk
    curve_ops_tool remove-peercurve bs delete peer
    curve_ops_tool reset-peercurve bs update peer
    curve_ops_tool spacecurve bs list space
    curve_ops_tool update-throttlecurve bs update throttle
    curve_ops_tool check-copysetcurve bs check copyset
    curve_ops_tool client-statuscurve bs status client
    curve_ops_tool check-operatorcurve bs check operator
    curve_ops_tool snapshot-clone-statuscurve bs status snapshotserver
    curve_ops_tool transfer-leadercurve bs update leader
    curve_ops_tool do-snapshotcurve bs snapshot copyset
    curve_ops_tool set-scan-statecurve bs update scan-state
    curve_ops_tool chunkserver-statuscurve bs status chunkserver
    curve_ops_tool chunkserver-listcurve bs list chunkserver
    curve_ops_tool set-copyset-availflagcurve bs update copyset availflag
    curve_ops_tool scan-statuscurve bs list/query scan-status
    curve_ops_tool clean-recyclecurve bs clean-recycle
    curve_ops_tool copysets-statuscurve bs status copyset
    curve_ops_tool list-may-broken-volcurve bs list may-broken-vol
    curve_ops_tool rapid-leader-schedulecurve bs update leader-schedule
    curve_ops_tool do-snapshot-allcurve bs snapshot --all
    curve_ops_tool check-chunkservercurbe bs check chunkserver
    curve_ops_tool status
    curve_ops_tool check-consistency
    curve_ops_tool check-servercurve bs check server
    - - + + \ No newline at end of file diff --git a/CurveFS/maintenance/data-migration.html b/CurveFS/maintenance/data-migration.html index c6d13a5..103fd7d 100644 --- a/CurveFS/maintenance/data-migration.html +++ b/CurveFS/maintenance/data-migration.html @@ -4,13 +4,13 @@ 迁移数据到 CurveFS | Curve Book - - + +
    跳到主要内容

    迁移数据到 CurveFS

    CurveFS在不同应用场景落地使用时,不可避免的涉及到将原系统数据迁移到CurveFS,特别是大数据和AI相关业务,他们数据量巨大(包括海量小文件),如何做到高效、可靠的数据迁移是一切的开始。

    源数据存储介质

    1. 本地磁盘目录
    2. 集中式存储或分布式存储:NAS存储、NFS、ceph、glusterfs、minio等
    3. 云存储:包括各类公有云文件存储和对象存储等

    数据迁移的工具

    我们调研了几种常见的数据迁移工具,同时也在相同场景下测试他们在CurveFS上的表现:

    名称需单独安装核心应用场景增量同步校验并发多机并发
    cp本机文件的拷贝,不支持网络传输和远程同步
    rsync支持通过SSH RSH协议在本地或远程系统之间同步和备份文件
    rclone支持本地和云服务之间的数据同步,支持超过50+中云服务
    juicesync多机分布式数据同步,主要支持本地、对象存储、POSIX文件系统、HDFS间的数据互相同步

    测试数据集使用测试工具mdtest生成,命令为:mdtest -z 10 -b 2 -I 1000 -w 131072 -C -F -d /curvefs/data ,共 2047000 个 128KB 文件:

    名称命令主要请求(文件数n,目录数m)耗时比较
    cp(v8.26)cp -a src dstlookup: n+m
    mkdir: m
    getattr: m
    create: n
    getxattr: size/128k * n
    write: size/128k *n
    flush: n
    release: n
    setattr: n + 2m
    setxattr: n + m
    removexattr: m
    2h10min,带宽33MiB/s
    rsync(v3.1.2)rsync -a --progress src dstlookup: 4n + 3m
    mkdir: m
    getattr: 2*n + m
    create: n
    getxattr: size/128/2 * n
    write: size/128 * n
    flush: n
    release: n
    setattr: 3*n + 2*m
    rename: n
    5h13min,带宽13MiB/s
    rsync(v3.1.2)rsync -a --inplace --progress src dstlookup: 3n + 3m
    mkdir: m
    getattr: n + m
    create: n
    getxattr: size/128/2 * n
    write: size/128 * n
    flush: n
    release: n
    setattr: 3n + 3m
    3h30min带宽22MiB/s
    rclone(v1.63.1)rclone copy --inplace --metadata --create-empty-src-dirs --links --transfers 20 --progress src dstlookup: n + m
    mkdir: m
    getattr: n + m
    create: n
    getxattr: size/32 * n
    write: size/32 * n
    flush: n
    release: n
    setattr: 4n
    readdir: m
    37min,带宽115MiB/s
    juicesync(v1.1.0-rc1)juicefs sync --threads=20 --list-threads=2 --links --dirs --perms src dstlookup: 3n + 3m
    mkdir: m
    getattr: huge
    create: n
    getxattr: size/32 * n
    write: size/32 * n
    flush: n
    release: n
    setattr: 3n + 3m
    readdir: huge
    2h28min带宽28MiB/s

    注:上述命令中src为迁移数据的源目录,dst为迁移的目标目录,这里是 CurveFS 挂载点,下同。

    采用的迁移方案

    这次迁移是将某业务方存储在CephFS中的文件全量迁移至CurveFS中,数据特点是大文件极少,几百字节到数十kB的小文件巨多,单目录文件数量多达5千万个,文件总数量近百亿。

    1. 目前CurveFS rename操作是原子操作,耗时相对较长,所以工具需要支持直写(--inplace)。
    2. 需要支持并发,在海量小文件时效果更加明显。
    3. 支持校验,数据迁移过程保证文件的完整性尤为重要。

    选择迁移工具:rclone v1.63.1

    迁移命令:rclone copy --inplace --metadata --create-empty-src-dirs --links --transfers 500 --progress --fast-list --checkers 16 src dst

    参数含义
    --inplace直写目的文件,不用先写临时文件再rename
    --metadata保留文件元数据(mode uid gid time)
    --create-empty-src-dirs保留空目录,默认源中如果有空目录则不会在目的端创建
    --links保留符号链接
    --transfers上传并发度
    --progress显示同步进度
    --fast-list提高list速度,会增加内存使用
    --checkers校验线程数

    注意事项

    1. rclone copy 不会同步最上层目录,只会同步其内容,例如(src: /A/file,dest: /B) rclone copy /A /B,结果是/B/file,而不是/B/A/file。
    2. 单rclone并发度不能设置太高,自身竞争可能会比较大,但可以分多个目录跑多个rclone进程,我们本次迁移采用100-500,具体需要关注client节点负载和源&目的系统的集群压力。
    3. rclone目前不支持保留目录的元数据,即使指定了--metadata。我们基于rclone最新稳定版(v1.63.1)进行了修改,使得其支持在本地后端同步时对目录元数据进行保留(https://github.com/opencurve/curve-rclone/tree/v1.63.1-dir-metadata)。
    4. rclone默认使用size 和 mod time进行校验,如果对完整性要求更强,可以在命令中指定 --checksum,这样会基于文件size 和 checksum进行校验。

    最后关于迁移时间的预估,除了考虑数据size的大小还要考虑文件数量,如果是大文件居多可根据带宽进行评估,如果小文件居多需要根据qps进行初步评估,同时需要结合源端和目的端系统的系统压力、网络带宽等综合评估迁移所需时间。具体的迁移方案需要根据业务特点综合考虑,保证数据的平滑、高效、可靠的迁移。

    后续规划

    后续我们将会把rclone整合到CurveAdm运维工具中,方便用户一键启动数据迁移操作,欢迎感兴趣的小伙伴参与开发设计。

    - - + + \ No newline at end of file diff --git a/CurveFS/performance/benchmark.html b/CurveFS/performance/benchmark.html index 0e846b6..289cd94 100644 --- a/CurveFS/performance/benchmark.html +++ b/CurveFS/performance/benchmark.html @@ -4,13 +4,13 @@ 性能指标 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/CurveFS/performance/how-to-benchmark.html b/CurveFS/performance/how-to-benchmark.html index 90e013d..6c83a9f 100644 --- a/CurveFS/performance/how-to-benchmark.html +++ b/CurveFS/performance/how-to-benchmark.html @@ -4,13 +4,13 @@ 性能测试指南 | Curve Book - - + +
    跳到主要内容

    性能测试指南

    测试工具

    测试工具适用场景说明下载地址
    fio适用于裸盘情况下的性能测试fio是测试IOPS的非常好的工具,用来对硬件进行压力测试和验证,支持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network, syslet, guasi, solarisaio 等等http://freecode.com/projects/fio
    vdbench适用于文件系统盘情况下的性能测试vdbench是一个 I/O 工作负载生成器,用于验证数据完整性和度量直接附加和网络连接的存储的性能。它是一个免费的工具,容易使用,而且常常用于测试测试大量文件、目录的创建、删除性能,以及对文件的读写性能。https://www.oracle.com/technetwork/cn/server-storage/vdbench-downloads-1901681-zhs.html

    单客户端文件系统盘性能测试

    (1)vdbench是java开发的测试工具,需要java环境。

    ​ apt-get install openjdk-7-jdk -y

    (2)做文件系统

    ​ mkfs.ext4 /dev/vdc (vdc为云盘)

    (3) mount设备

    ​ mount /dev/vdc /mnt

    (4)在vdbench文件目录下执行:./vdbench -t

    ​ 出现如下信息则说明可正常使用

    vdbench测试需要写测试配置文件。在vdbench 目录下创建profile文件。

    ​ 单节点配置可参考我的如下测试配置文件

    hd=default,vdbench=/home/chenyunhui/vdbench50406,user=root,shell=ssh
    fsd=fsd1,anchor=/mnt,depth=1,width=10,files=10,size=1000m,shared=yes,openflags=o_direct
    fwd=fwd1,fsd=fsd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1

    该配置为10 10 1G的文件数据,具体文件数据可以根据底层集群规模来定。

    其中比较重要的参数为:

    anchor= 将在其中创建目录结构的目录

    width= 要在定位符下创建的目录数

    depth= 要在定位符下创建的级别数(目录深度)

    files= 要在最低级别创建的文件数

    sizes= (size,size,...) 将创建的文件大小

    distribution= bottom(如果希望仅在最低级别创建文件)和 all(如果希望在所有目录中创建文件)

    fileio= random 或 sequential,表示文件 I/O 将执行的方式。

    fileselect= random 或 sequential,标识选择文件或目录的方式。

    xfersizes= 数据传输(读取和写入操作)处理的数据大小。

    rdpct= 读取和写入操作的百分比。0为全写

    threads= 此工作负载的并发线程数量。每个线程需要至少 1 个文件

    elapsed= 以秒为单位的运行持续时间。默认设置为 30

    vdbench可以在测试时增加-jn的参与用于测试时同时进行数据校验。vdbench每次写操作都会记录在一个表里,对每个512字节数据 的8字节逻辑字节地址和1字节的校验值进行记录,校验值中会记录为第几次写。第一次创建写为00,后续每次覆盖写加1。再次运行时会读出之前的校验日志并进行数据校验。

    读写数据测试和记录校验数据使用命令:

    ./vdbench -jn -f profile 执行该命令时同时会进行数据校验。

    但是需要注意,使用vdbench进行一致性校验测试更加适宜于稳定性测试和一致性测试,性能测试过程中使用会影响性能

    最后产生的测试结果都是output下面

    errorlog.html

    当为测试启用了数据验证时,它可包含一些数据块中的错误的相关信息:

    • 无效的密钥读取
    • 无效的 lba 读取(一个扇区的逻辑字节地址)
    • 无效的 SD 或 FSD 名称读取
    • 数据损坏,即使在使用错误的 lba 或密钥时
    • 数据损坏
    • 坏扇区

    flatfile.html

    包含 vdbench 生成的一种逐列的 ASCII 格式的信息。

    histogram.html

    一种包含报告柱状图的响应时间、文本格式的文件。

    logfile.html

    包含 Java 代码写入控制台窗口的每行信息的副本。logfile.html 主要用于调试用途

    parmfile.html

    显示已包含用于测试的每项内容的最终结果

    resourceN-M.html、resourceN.html、resourceN.var_adm_msgs.html

    • 摘要报告
    • stdout/stderr 报告
    • 主机 N 的摘要报告
    • 最后 “nn” 行文件 /var/adm/messages 和 /var/adm/messages。每个 M 个 JVM/Slave 的目标主机 N 和主机 N 上为 0。

    sdN.histogram.html、sdN.html

    每个 N 存储定义的柱状图和存储定义 “N” 报告。

    summary.html

    主要报告文件,显示为在每个报告间隔的每次运行生成的总工作负载,以及除第一个间隔外的所有间隔的加权平均值。

    • interval:报告间隔序号
    • I/O rate:每秒观察到的平均 I/O 速率
    • MB sec:传输的数据的平均 MB 数
    • bytes I/O:平均数据传输大小
    • read pct:平均读取百分比
    • resp time:以读/写请求持续时间度量的平均响应时间。所有 vdbench 时间都以毫秒为单位。
    • resp max:在此间隔中观察到的最大响应时间。最后一行包含最大值总数。
    • resp stddev:响应时间的标准偏差
    • cpu% sys+usr:处理器繁忙 = 100(系统 + 用户时间)(Solaris、Windows、Linux)
    • cpu% sys:处理器利用率:系统时间

    多客户端文件系统盘性能测试

    多数情况下我们会进行多客户端的测试。多客户端测试参数可配置如下:

    hd=default,vdbench=/root/vdbench50406,user=root,shell=ssh
    hd=hd1,system=183.136.181.150
    hd=hd2,system=115.238.123.189

    fsd=fsd1,anchor=/mnt1,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fsd=fsd2,anchor=/mnt2,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fwd=fwd1,fsd=fsd1,host=hd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    fwd=fwd2,fsd=fsd2,host=hd2,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50


    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1,warmup=300

    上面的例子是测试两台机器的云盘,每个云盘4个线程对110 1G的文件进行读写,4K的块大小,50%的写,50%随机,热身写300s,然后测试3600s

    相比于多个客户端上跑fio,使用vdbench进行多客户端测试的好处有:

    • 1、能够每秒显示整个测试的io叠加,这样测试整个集群的io的时候,可以把所有虚机启动起来,然后进行io的压测,而不是去压单个rbd的iops,那个没有太大的意义,只能是一个数值,真正的环境大多也不是给一个业务使用的,也可以跑起一个业务以后,再看剩余的机器还能跑多少性能
    • 2、在测试输出报告里面会根据主机统计一次io,这个面向的业务场景就是,比如某台主机上面可能挂载多块云盘,那么可以根据主机进行统计
    • 3、在报告里面还会根据设备显示io个延时的信息,也就是只要是测试设备,每一个的性能指标都能查到,这个的好处就是检测集群里面的io是不是均匀的,如果做了qos,设备的测试性能值是不是跟设置限制一样
    - - + + \ No newline at end of file diff --git a/CurveFS/performance/perf-tune.html b/CurveFS/performance/perf-tune.html index 7032f98..ef01b2e 100644 --- a/CurveFS/performance/perf-tune.html +++ b/CurveFS/performance/perf-tune.html @@ -4,13 +4,13 @@ 性能调优 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git "a/CurveFS/test/ci\344\273\213\347\273\215.html" "b/CurveFS/test/ci\344\273\213\347\273\215.html" index 4f0bf77..757887d 100644 --- "a/CurveFS/test/ci\344\273\213\347\273\215.html" +++ "b/CurveFS/test/ci\344\273\213\347\273\215.html" @@ -4,13 +4,13 @@ 概述 | Curve Book - - + +
    跳到主要内容

    概述

    本文将主要介绍社区人员在参与curve项目开发过程中如何快速提交PR并进行CI测试,以及快速定位CI失败原因。

    如何提交PR

    在完成代码编写后,就可以提交 PR。当然如果开发尚未完成,在某些情况下也可以先提交 PR,比如希望先让社区看一下大致的解决方案,可以在完成代码框架后提价 PR。

    Curve 编译环境快速构建

    Curve 测试用例编译及执行

    对于 PR 我们有如下要求:

    • CURVE编码规范严格按照Google C++开源项目编码指南来进行代码编写,请您也遵循这一指南来提交您的代码。
    • 代码必须有测试,文档除外,单元测试(增量行覆盖80%以上,增量分支覆盖70%以上);集成测试(与单元测试合并统计,满足单元测试覆盖率要求即可)
    • 请尽可能详细的填写 PR 的描述,关联相关问题的 issuse,PR commit message 能清晰看出解决的问题。
    • CI 通过之后可开始进行 review,每个 PR 在合并之前都需要至少得到两个 Committer/Maintainer 的 LGTM。
    • PR 代码需要一定量的注释来使代码容易理解,且所有注释和 review 意见和回复均要求使用英语。

    对于 commit message:

    一条好的 commit message 需要包含以下要素:

    1. What is your change?(必须)
    2. Why this change was made?(必须)
    3. What effect does the commit have? (可选)

    说明提交的 PR 做了那些修改:性能优化?修复bug?增加功能?以及这么做的原因。最后描述以下这样修改带来的影响,包括性能等等。当然对一些简单的修改,修改的原因和影响可以忽略。在 message中尽量遵循以下原则:

    • 总结说明 PR 的功能和作用
    • 使用短句和简单的动词
    • 避免长的复合词和缩写

    在提交时请尽可能的遵循以下格式:

    [type]<scope>: <description>
    <BLANK LINE>
    [body]
    <BLANK LINE>
    [footer]

    type 可以是以下类型之一:

    • build: 影响系统构建以及外部依赖
    • ci: 影响持续继承相关的功能
    • docs: 文档相关的修改
    • feat: 增加新的特性
    • fix: bug 修复
    • perf: 性能提升
    • refactor: 重构相关的代码,不增加功能也不修复错误
    • style: 不影响代码的的含义的修改,仅仅是修改代码风格
    • test: 单元测试相关的修改

    第一行表示标题应尽可能保持 70 个字符以内,阐述修改的模块以及内容,多模块可以使用 * 来表示,并在正文阶段说明修改的模块。

    footer 是可选的,用来记录对应因为这些更改而可以关闭的 issue,如 Close #12345

    CI 测试过程

    PR提交到 Curve master 分支后需要在comment中输入cicheck触发Curve CI,需保证 CI 通过,CI 的 Jenkins 用户名密码为 netease/netease,如遇到 CI 运行失败可以登录 Jenkins 平台查看失败原因。测试内容包括:

    1) cpplint静态检查

    主要进行测试内容为:

    cpplint --linelength=80 --counting=detailed --output=junit --filter=-build/c++11 --quiet $( find . -name .h -or -name .cc -or -name *.cpp ) 2>&1 | tee cpplint-style.xml

    2) 单元测试

    单元测试主要执行的就是仓库中:https://github.com/opencurve/curve/blob/master/ut.sh

    为提升测试效率,所有单元测试用例为并行执行,需要注意增加的单元测试里起的服务不能和其他用例里起的服务使用同一端口,否则会因为端口占用失败

    测试结束后进行覆盖率的卡点校验,当前覆盖率配置为:

    if [ $1 == "curvebs" ];then

    check_repo_branch_coverage 59
    check_repo_line_coverage 76

    ## two arguments are module and expected branch coverage ratio
    check_module_branch_coverage "src/mds" 70
    check_module_branch_coverage "src/client" 78
    check_module_branch_coverage "src/chunkserver" 65
    check_module_branch_coverage "src/snapshotcloneserver" 65
    check_module_branch_coverage "src/tools" 65
    check_module_branch_coverage "src/common" 65
    check_module_branch_coverage "src/fs" 65
    check_module_branch_coverage "src/idgenerator" 79
    check_module_branch_coverage "src/kvstorageclient" 70
    check_module_branch_coverage "src/leader_election" 100
    check_module_branch_coverage "nebd" 75

    elif [ $1 == "curvefs" ];then

    check_module_branch_coverage "mds" 59
    check_module_branch_coverage "client" 59
    check_module_branch_coverage "metaserver" 65
    check_module_branch_coverage "common" 16
    check_module_branch_coverage "tools" 0
    fi

    测试结束后会输出覆盖率报告,例如: http://59.111.91.248:8080/job/curve_untest_job/6149/HTML_20Report/

    如果测试失败,可以打开对应的curve_untest_job的控制台输出日志,直接页面拉到最后会打印出错误的原因,覆盖率不足会打印在最后,如果是单元测试用例失败,可以从下往上翻页,找到失败的地方,比如:

    [  FAILED  ] 1 test, listed below:
    [ FAILED ] EtcdClientTest.GetEtcdClusterStatus

    1 FAILED TEST
    test bazel-bin/test/mds/server/mds-test.log log is --------------------------------------------->>>>>>>>
    Running main() from gmock_main.cc
    [==========] Running 1 test from 1 test case.
    [----------] Global test environment set-up.
    [----------] 1 test from MDSTest
    [ RUN ] MDSTest.common
    2022-12-07 18:02:48.753204 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:49.753500 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:50.753831 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:51.754211 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:52.755814 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    2022-12-07 18:02:53.756543 I | etcd do NewCient get err:context deadline exceeded, errCode:DeadlineExceeded
    test/mds/server/mds_test.cpp:76: Failure
    Value of: initSuccess
    Actual: false
    Expected: true

    3) pjdfstest测试

    pjdfstest 主要是用来测试curvefs POSIX上的兼容性,会部署单机版的curvefs环境,然后进行pjdtest的执行,用的工具在 https://github.com/pjd/pjdfstest 。可以使用如下脚本进行自行测试:

    #!/usr/bin/env bash

    set -e

    git clone https://github.com/pjd/pjdfstest.git
    cd pjdfstest
    autoreconf -ifs
    ./configure
    make pjdfstest
    cd ..
    mkdir tmp
    cd tmp
    # must be root!
    sudo prove -r -v --exec 'bash -x' ../pjd*/tests
    cd ..
    rm -rf tmp pjd*

    如果测试失败,也需要进入控制台输出,拉到最后查看具体失败的原因

    3) failover异常自动化

    异常自动化会进行整体真实环境部署和io注入、故障测试,主要流程:

    主要流程:

    初始化集群 → 用户io注入 (数据面) → 并发调用管理接口(管控面) → 触发异常 → sleep a time → 异常恢复(恢复校验) → 数据校验(ioerror、读写一致性、三副本一致性、抖动) → 管理接口校验 &资源回收等校验

    测试完成后会输出整体的测试报告,具体测试过程和测试结果可以点击对应提交的curve_failover_job 下的log.html , 可以看到比较完整的测试流程、步骤、操作和结果输出,比如 log.html

    常见问题

    如何登录jenkins?

    由于内部的一些权限要求,需要用用户名密码登录jenkins ,开源社区的可以使用netease/netease 的账号/密码进行登录

    如果再次触发失败pr的CI 构建?

    可以打开pr ,在最底部comment ''recheck" 字段,进行再次触发构建

    如何过滤CI?

    对于一些文档修改或其他不影响代码流程的内容,我们可以不进行CI构建,可以在创建PR的时候在标题增加[skipci] 字段

    触发构建后立刻返回了失败?

    查看一下失败的job内容,多数是因为测试任务在进行排队等待。如果job是waiting_in_line ,就是在排队

    - - + + \ No newline at end of file diff --git a/CurveFS/test/env-setup.html b/CurveFS/test/env-setup.html index 56d2f09..69c7dbd 100644 --- a/CurveFS/test/env-setup.html +++ b/CurveFS/test/env-setup.html @@ -4,13 +4,13 @@ 测试环境配置 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git "a/CurveFS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" "b/CurveFS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" index 71111f1..14e2789 100644 --- "a/CurveFS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" +++ "b/CurveFS/test/\345\274\202\345\270\270\350\207\252\345\212\250\345\214\226\346\226\271\346\263\225.html" @@ -4,15 +4,15 @@ 异常自动化方法 | Curve Book - - + +
    跳到主要内容

    异常自动化方法

    #异常测试方法 ##测试目的

    • Curve是网易云存储产品组推出的新一代分布式存储平台,致力于打造一个支持块、对象、文件等存储形态的统一平台。本次测试对象主要为支持块设备功能。

    • 生产环境中,故障是一种常态,测试环境中需要尽可能的模拟生产环境中的故障,以此来评估系统的可用性、一致性等。

    ##测试方法

    • 异常测试的环境部署要求尽可能与真实环境一致,当前测试环境定义为6台chunkserver节点,1台mds节点(尚无failover),2台client节点,测试场景尽可能模拟用户使用的方式,读写io由云主机内部下发,管理接口直接采用openstack的接口。

    • 异常项主要通过人为注入的方式触发,有些异常为真实的异常,例如机器宕机、拔网线等;有些异常通过软件模拟的方式,如磁盘io卡顿、服务异常等;有些异常则是在代码侧注入,该部分异常测试主要在集成测试中,不在这里讨论。

    • 异常测试主要校验真实用户场景中,在异常触发情况下的系统行为,按影响面可以分为:用户无感知、影响管理面基本功能、引起性能衰减、引起io抖动、系统不可服务但可恢复、数据异常(ioerror、不一致、丢失)、系统无法恢复。

    • 借鉴混沌工程的理论,测试用例设计的时候也会尽量符合下面原则,当然我们目前只是在测试环境批跑,条件允许的情况下,可以在beta云环境批跑,直到稳定后,可以去A级环境批跑。

    • 建立稳定状态的假设;

    • 多样化现实世界事件;

    • 在生产环境运行实验;

    • 持续自动化运行实验;

    • 最小化“爆炸半径”。 ##测试用例

    • 一个最基本的异常测试用例格式如下:

    • 初始化集群 → 用户io注入 → 并发调用管理接口 → 触发异常 → sleep a random time → 异常恢复(恢复校验) → 数据校验(ioerror、读写一致性、三副本一致性、抖动) → 管理接口校验 → 资源回收校验

    - - + + \ No newline at end of file diff --git "a/CurveFS/test/\346\200\247\350\203\275\346\265\213\350\257\225\345\267\245\345\205\267.html" "b/CurveFS/test/\346\200\247\350\203\275\346\265\213\350\257\225\345\267\245\345\205\267.html" index c3ddf18..771607f 100644 --- "a/CurveFS/test/\346\200\247\350\203\275\346\265\213\350\257\225\345\267\245\345\205\267.html" +++ "b/CurveFS/test/\346\200\247\350\203\275\346\265\213\350\257\225\345\267\245\345\205\267.html" @@ -4,13 +4,13 @@ 性能测试 | Curve Book - - + +
    跳到主要内容

    性能测试

    测试工具

    测试工具适用场景说明下载地址
    fio适用于裸盘情况下的性能测试fio是测试IOPS的非常好的工具,用来对硬件进行压力测试和验证,支持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network, syslet, guasi, solarisaio 等等http://freecode.com/projects/fio
    vdbench适用于文件系统盘情况下的性能测试vdbench是一个 I/O 工作负载生成器,用于验证数据完整性和度量直接附加和网络连接的存储的性能。它是一个免费的工具,容易使用,而且常常用于测试测试大量文件、目录的创建、删除性能,以及对文件的读写性能。https://www.oracle.com/technetwork/cn/server-storage/vdbench-downloads-1901681-zhs.html

    单客户端文件系统盘性能测试

    (1)vdbench是java开发的测试工具,需要java环境。

    ​ apt-get install openjdk-7-jdk -y

    (2)做文件系统

    ​ mkfs.ext4 /dev/vdc (vdc为云盘)

    (3) mount设备

    ​ mount /dev/vdc /mnt

    (4)在vdbench文件目录下执行:./vdbench -t

    ​ 出现如下信息则说明可正常使用

    vdbench测试需要写测试配置文件。在vdbench 目录下创建profile文件。

    ​ 单节点配置可参考我的如下测试配置文件

    hd=default,vdbench=/home/chenyunhui/vdbench50406,user=root,shell=ssh
    fsd=fsd1,anchor=/mnt,depth=1,width=10,files=10,size=1000m,shared=yes,openflags=o_direct
    fwd=fwd1,fsd=fsd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1

    该配置为10 10 1G的文件数据,具体文件数据可以根据底层集群规模来定。

    其中比较重要的参数为:

    anchor= 将在其中创建目录结构的目录

    width= 要在定位符下创建的目录数

    depth= 要在定位符下创建的级别数(目录深度)

    files= 要在最低级别创建的文件数

    sizes= (size,size,...) 将创建的文件大小

    distribution= bottom(如果希望仅在最低级别创建文件)和 all(如果希望在所有目录中创建文件)

    fileio= random 或 sequential,表示文件 I/O 将执行的方式。

    fileselect= random 或 sequential,标识选择文件或目录的方式。

    xfersizes= 数据传输(读取和写入操作)处理的数据大小。

    rdpct= 读取和写入操作的百分比。0为全写

    threads= 此工作负载的并发线程数量。每个线程需要至少 1 个文件

    elapsed= 以秒为单位的运行持续时间。默认设置为 30

    vdbench可以在测试时增加-jn的参与用于测试时同时进行数据校验。vdbench每次写操作都会记录在一个表里,对每个512字节数据 的8字节逻辑字节地址和1字节的校验值进行记录,校验值中会记录为第几次写。第一次创建写为00,后续每次覆盖写加1。再次运行时会读出之前的校验日志并进行数据校验。

    读写数据测试和记录校验数据使用命令:

    ./vdbench -jn -f profile 执行该命令时同时会进行数据校验。

    但是需要注意,使用vdbench进行一致性校验测试更加适宜于稳定性测试和一致性测试,性能测试过程中使用会影响性能

    最后产生的测试结果都是output下面

    errorlog.html

    当为测试启用了数据验证时,它可包含一些数据块中的错误的相关信息:

    • 无效的密钥读取
    • 无效的 lba 读取(一个扇区的逻辑字节地址)
    • 无效的 SD 或 FSD 名称读取
    • 数据损坏,即使在使用错误的 lba 或密钥时
    • 数据损坏
    • 坏扇区

    flatfile.html

    包含 vdbench 生成的一种逐列的 ASCII 格式的信息。

    histogram.html

    一种包含报告柱状图的响应时间、文本格式的文件。

    logfile.html

    包含 Java 代码写入控制台窗口的每行信息的副本。logfile.html 主要用于调试用途

    parmfile.html

    显示已包含用于测试的每项内容的最终结果

    resourceN-M.html、resourceN.html、resourceN.var_adm_msgs.html

    • 摘要报告
    • stdout/stderr 报告
    • 主机 N 的摘要报告
    • 最后 “nn” 行文件 /var/adm/messages 和 /var/adm/messages。每个 M 个 JVM/Slave 的目标主机 N 和主机 N 上为 0。

    sdN.histogram.html、sdN.html

    每个 N 存储定义的柱状图和存储定义 “N” 报告。

    summary.html

    主要报告文件,显示为在每个报告间隔的每次运行生成的总工作负载,以及除第一个间隔外的所有间隔的加权平均值。

    • interval:报告间隔序号
    • I/O rate:每秒观察到的平均 I/O 速率
    • MB sec:传输的数据的平均 MB 数
    • bytes I/O:平均数据传输大小
    • read pct:平均读取百分比
    • resp time:以读/写请求持续时间度量的平均响应时间。所有 vdbench 时间都以毫秒为单位。
    • resp max:在此间隔中观察到的最大响应时间。最后一行包含最大值总数。
    • resp stddev:响应时间的标准偏差
    • cpu% sys+usr:处理器繁忙 = 100(系统 + 用户时间)(Solaris、Windows、Linux)
    • cpu% sys:处理器利用率:系统时间

    多客户端文件系统盘性能测试

    多数情况下我们会进行多客户端的测试。多客户端测试参数可配置如下:

    hd=default,vdbench=/root/vdbench50406,user=root,shell=ssh
    hd=hd1,system=183.136.181.150
    hd=hd2,system=115.238.123.189

    fsd=fsd1,anchor=/mnt1,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fsd=fsd2,anchor=/mnt2,depth=1,width=10,files=1,size=1000m,openflags=o_direct
    fwd=fwd1,fsd=fsd1,host=hd1,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50
    fwd=fwd2,fsd=fsd2,host=hd2,threads=4,xfersize=4k,fileio=random,fileselect=random,rdpct=50


    rd=rd1,fwd=fwd*,fwdrate=max,format=restart,elapsed=3600,interval=1,warmup=300

    上面的例子是测试两台机器的云盘,每个云盘4个线程对110 1G的文件进行读写,4K的块大小,50%的写,50%随机,热身写300s,然后测试3600s

    相比于多个客户端上跑fio,使用vdbench进行多客户端测试的好处有:

    • 1、能够每秒显示整个测试的io叠加,这样测试整个集群的io的时候,可以把所有虚机启动起来,然后进行io的压测,而不是去压单个rbd的iops,那个没有太大的意义,只能是一个数值,真正的环境大多也不是给一个业务使用的,也可以跑起一个业务以后,再看剩余的机器还能跑多少性能
    • 2、在测试输出报告里面会根据主机统计一次io,这个面向的业务场景就是,比如某台主机上面可能挂载多块云盘,那么可以根据主机进行统计
    • 3、在报告里面还会根据设备显示io个延时的信息,也就是只要是测试设备,每一个的性能指标都能查到,这个的好处就是检测集群里面的io是不是均匀的,如果做了qos,设备的测试性能值是不是跟设置限制一样
    - - + + \ No newline at end of file diff --git "a/CurveFS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" "b/CurveFS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" index 45c3879..0ab59b1 100644 --- "a/CurveFS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" +++ "b/CurveFS/test/\347\274\226\350\257\221\345\222\214\345\215\225\345\205\203\346\265\213\350\257\225.html" @@ -4,15 +4,15 @@ 编译环境搭建 | Curve Book - - + +
    跳到主要内容

    编译环境搭建

    请注意:

    1. 如您只是想体验Curve的部署流程和基本功能,则不需要编译Curve,请参考 部署
    2. 本文档仅用来帮助你搭建Curve代码编译环境,便于您参与Curve的开发调试
    3. 以下镜像和编译过程目前仅支持 x86 系统
    4. 如要编译arm分支,请根据 Dockerfile打包编译镜像
    5. 目前master分支不支持在arm系统上编译运行
    6. 推荐 debian 10及以上版本的操作系统,其他操作系统未经过全面测试

    使用Docker进行编译(推荐方式)

    获取或者构建docker镜像

    方法一:从docker hub镜像库中拉取docker镜像(推荐方式)

    docker pull opencurvedocker/curve-base:build-debian9

    方法二:手动构建docker镜像

    使用工程目录下的 docker/debian9/compile/Dockerfile 进行构建,命令如下:

    docker build -t opencurvedocker/curve-base:build-debian9

    注意: 上述操作不建议在Curve工程目录执行,否则构建镜像时会把当前目录的文件都复制到docker镜像中,建议把Dockerfile拷贝到新建的干净目录下进行docker镜像的构建。

    在docker镜像中编译

    git clone https://github.com/opencurve/curve.git 或者 git clone https://gitee.com/mirrors/curve.git
    cd curve
    # 如果你想在容器内完成编译+制作+上传镜像的操作,可以添加以下参数
    # -v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker
    # --rm 会在容器退出后自动删除容器,如果你想保留容器,可以去掉该参数
    docker run --rm -v $(pwd):/curve -w /curve -v ${HOME}/.cache:${HOME}/.cache -v ${HOME}/go:${HOME}/go --user $(id -u ${USER}):$(id -g ${USER}) -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro --privileged -it opencurvedocker/curve-base:build-debian9 bash
    # (中国大陆可选)将外部依赖替换为国内下载点或镜像仓库,可以加快编译速度: bash replace-curve-repo.sh

    # curve v2.0 之前
    bash mk-tar.sh (编译 curvebs 并打tar包)
    bash mk-deb.sh (编译 curvebs 并打debian包)

    # (当前)curve v2.0 及之后
    # 编译 curvebs:
    make build stor=bs dep=1
    # or
    make dep stor=bs && make build stor=bs
    # 编译 curvefs:
    make build stor=fs dep=1
    # or
    make dep stor=fs && make build stor=fs

    注意: mk-tar.shmk-deb.sh 用于 curve v2.0 之前版本的编译打包,v2.0 版本之后不再维护。

    在物理机上编译

    Curve编译依赖的包括:

    依赖版本
    bazel4.2.2
    gcc支持c++11的兼容版本

    Curve的其他依赖项,均由bazel去管理,不可单独安装。

    注意: 4.* 版本的 bazel 均可以成功编译 Curve 项目,其他版本不兼容。 4.2.2 为推荐版本。

    安装依赖

    编译相关的软件依赖可以参考 dockerfile 中的安装步骤。

    一键编译

    git clone https://github.com/opencurve/curve.git 或者 git clone https://gitee.com/mirrors/curve.git
    # (中国大陆可选)将外部依赖替换为国内下载点或镜像仓库,可以加快下载速度: bash replace-curve-repo.sh
    # curve v2.0 之前
    bash mk-tar.sh (编译 curvebs 并打tar包)
    bash mk-deb.sh (编译 curvebs 并打debian包)

    # (当前)curve v2.0 及之后
    # 编译 curvebs:
    make build stor=bs dep=1
    # or
    make dep stor=bs && make build stor=bs
    # 编译 curvefs:
    make build stor=fs dep=1
    # or
    make dep stor=fs && make build stor=fs

    制作镜像

    该步骤可以在容器内执行也可以在物理机上执行。 注意若是在容器内执行,需要在执行 docker run 命令时添加 -v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker 参数。

    # 编译 curvebs:
    # 后面的tag参数可以自定义,用于上传到镜像仓库
    make image stor=bs tag=test
    # 编译 curvefs:
    make image stor=fs tag=test

    上传镜像

    # test 为上一步中的tag参数
    docker push test

    测试用例编译及执行

    编译全部模块

    仅编译全部模块,不进行打包

    bash ./build.sh

    列出所有测试模块

    # curvebs
    bazel query '//test/...'
    # curvefs
    bazel query '//curvefs/test/...'

    编译对应模块的代码

    编译对应模块,例如test/common目录下的common-test测试:

    bazel build test/common:common-test --copt -DHAVE_ZLIB=1 --define=with_glog=true --compilation_mode=dbg --define=libunwind=true

    执行测试

    执行测试前需要先准备好测试用例运行所需的依赖:

    运行单元测试:

    • 构建对应的模块测试:

      $ bazel build xxx/...//:xxx_test
    • 运行对应的模块测试:

      $ bazel run xxx/...//:xxx_test
      # 或者
      $ ./bazel-bin/xxx/.../xxx_test
    • 编译全部测试及文件

      $ bazel build "..."
    • bazel 默认自带缓存编译, 但有时可能会失效.

      清除项目构建缓存:

      $ bazel clean

      清除项目依赖缓存(bazel 会将WORKSPACE 文件中的指定依赖项自行编译, 这部分同样也会缓存):

      $ bazel clean --expunge
    • debug 模式编译(-c 指定向bazel 传递参数), 该模式会在默认构建文件中加入调试符号, 及减少优化等级.

      $ bazel build xxx//:xxx_test -c dbg
    • 优化模式编译

      $ bazel build xxx//:xxx_test -c opt
      # 优化模式下加入调试符号
      $ bazel build xxx//:xxx_test -c opt --copt -g
    • 更多文档, 详见 bazel docs.

    动态库

    export LD_LIBRARY_PATH=<CURVE-WORKSPACE>/thirdparties/etcdclient:<CURVE-WORKSPACE>/thirdparties/aws-sdk/usr/lib:/usr/local/lib:${LD_LIBRARY_PATH}

    fake-s3

    快照克隆集成测试中,使用了开源的fake-s3模拟真实的s3服务。

    $ apt install ruby -y OR yum install ruby -y
    $ gem install fakes3
    $ fakes3 -r /S3_DATA_DIR -p 9999 --license YOUR_LICENSE_KEY

    备注:

    • -r S3_DATA_DIR:存放数据的目录
    • --license YOUR_LICENSE_KEY:fakes3需要key才能运行,申请地址见fake-s3
    • -p 9999:fake-s3服务启动的端口,不用更改

    etcd

    wget -ct0 https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz
    tar zxvf etcd-v3.4.10-linux-amd64.tar.gz
    cd etcd-v3.4.10-linux-amd64 && cp etcd etcdctl /usr/bin

    执行单个测试模块

    ./bazel-bin/test/common/common-test

    运行单元/集成测试

    bazel 编译后的可执行程序都在 ./bazel-bin 目录下,例如 test/common 目录下的测试代码对应的测试程序为 ./bazel-bin/test/common/common-test,可以直接运行程序进行测试。

    • CurveBS相关单元测试程序目录在 ./bazel-bin/test 目录下
    • CurveFS相关单元测试程序目录在 ./bazel-bin/curvefs/test 目录下
    • 集成测试在 ./bazel-bin/test/integration 目录下
    • NEBD相关单元测试程序在 ./bazel-bin/nebd/test 目录下
    • NBD相关单元测试程序在 ./bazel-bin/nbd/test 目录下

    如果想运行所有的单元测试和集成测试,可以执行工程目录下的ut.sh脚本:

    bash ut.sh
    - - + + \ No newline at end of file diff --git a/CurveFS/usecase/ai-storage.html b/CurveFS/usecase/ai-storage.html index 6daf2f2..2d2b5b0 100644 --- a/CurveFS/usecase/ai-storage.html +++ b/CurveFS/usecase/ai-storage.html @@ -3,14 +3,14 @@ -CurveFS在AI训练场景下的降本提效实践 | Curve Book - - +Curve 文件系统为 AI 业务降本增效 | Curve Book + +
    -
    跳到主要内容

    CurveFS在AI训练场景下的降本提效实践

    一、项目背景

    在当今大数据和人工智能领域的快速发展中,随着数据量的爆炸式增长,对分布式文件系统的存储可扩展性、成本和性能提出了更高的要求。在此大势所趋之下,杭研云计算团队开发了Curve共享文件存储系统来解决这些问题。

    Curve共享文件存储系统旨在解决以下问题:

    可扩展性问题

    随着文件数量的增长,现有的文件系统如CephFS和HDFS等元数据的可扩展性不足,无法满足大规模文件存储的需求。Curve文件系统将提供可扩展的共享文件存储,解决元数据管理的问题。

    性能问题

    随着文件数量的增加,文件元数据的性能会大幅下降。此外,小文件的读写性能也比较差,这会对大数据和AI等业务产生影响。Curve文件系统将优化元数据管理,提高文件检索效率,并改善小文件的读写性能。

    成本问题

    现有的文件系统通常采用两副本或三副本技术,但实际上80%的数据都是冷数据,使用多副本技术存储成本较高。Curve文件系统将采用更高效的数据存储策略,降低存储成本,提高资源利用率。

    通过解决这些问题,Curve文件系统不仅将为公司的核心业务提供稳定、高效的存储支持,同时也会对开源社区做出重要贡献。我们致力于将Curve文件系统打造成为一款广泛使用的开源存储软件,为全球的开发者社区提供可扩展、高性能和低成本的存储解决方案。我们相信,Curve文件系统的研发和应用将促进技术创新,推动整个开源社区的发展。

    二、项目思路和方案

    文件系统的元数据独立存存储

    解决元数据增长带来的扩展性和性能要求。通过创新的设计和实现,使得其中元数据被存储在一个独立的集群中。随着文件数量的不断增加,元数据集群可以持续扩展,确保了元数据的线性扩展能力。这种设计有效地解决了传统文件系统在处理大规模文件时面临的性能和可扩展性挑战。

    curvefs arch

    Curve共享文件存储系统通过对元数据进行合理的分片,使得多个分片可以分布在由多台服务器组成的元数据集群中。当前方案按照inodeid进行分片,按照算法serverid = (inodeid / inode_per_segment) mod metaserver_num进行分片。

    例如,如果按照每个分片管理100个inodeid,有3个metaserver,那么分片信息将如下。如果元数据数量增加,可以通过增加元数据集群的服务器来实现线性扩展的目标。

    curvefs meta arch

    文件系统数据存储的降本增效

    文件数据最终存储在S3上,例如集团内部的NOS、阿里云OSS、AWS S3等。S3通常使用EC技术(一般可以支持1.2副本),相比2副本或3副本,在保证可靠性的同时可以大幅度降低存储成本。

    以网易对象存储这边当前主流的EC 20+4使用为例,该使用方式相当于1.2副本。因此,以需要1PB使用空间为例,使用Curve共享文件存储系统+1.2副本对象存储只需要1.2PB空间,相比本地盘2副本可以节省约800TB的容量,成本优化效果非常显著。

    文件系统数据和元数据都支持多级缓存

    在工程实践中,由于S3和元数据集群都需要通过网络进行访问,每次读写操作都会经过网络,这可能会对业务性能产生负面影响。为了解决这个问题,Curve共享文件存储系统在保证多挂载点一致性的情况下,进行了数据和元数据的性能优化,主要思路是增加缓存。

    数据缓存

    数据支持多级缓存,主要包括:

    • 内存缓存:用于加速当前节点上的读写速度。
    • 本地缓存:同样用于加速当前节点上的读写速度。
    • 全局缓存集群:用于加速当前节点以及多节点数据共享时的速度。

    curvefs data cache arch

    元数据缓存

    元数据缓存支持metaserver端内存缓存、kernel缓存和本地客户端缓存。

    缓存何时加载或失效是元数据缓存的难点。与采用分布式锁的做法相比,Curve共享文件存储系统选择不实现复杂的分布式锁机制,而是基于业务分析不需要完全强一致性的前提,为每种类型的缓存数据制定了一些规则,在满足业务一致性的前提下提供了较好的性能。

    此外,通过结合VFS层的重试机制,Curve共享文件存储系统提供了完善的CTO(close-to-open)一致性,完全满足CTO语义。相对于JuiceFS等依赖用户缓存时长配置来实现CTO的存储系统,Curve共享文件存储系统具有更好的性能,并在任何场景下都能保证CTO一致性。

    curvefs meta cache arch

    文件系统支持数据预读和预热

    支持预读(Prefetch)

    即在数据访问时,可以将文件超过访问长度外的数据提前读入缓存。

    支持预热(warmup)

    AI用户可以在训练前将云端数据按需拷贝到指定的任意缓存层。这样,在后续访问时可以提供更好的性能,大大节省训练时间。

    curvefs warmup

    三、项目影响力和产出价值

    为业务降本提效

    杭研多媒体团队 AI 业务使用三副本 Ceph 内核文件存储来支撑AI场景,包括通用、AI相关的各种流程。AI 业务存储的数据量是巨大的,但其中 80% 都是冷数据,使用三副本存储成本很高。业务期望找一个文件系统替换 Ceph,在保证性能的同时能够降低存储成本。

    同样 Curve共享文件存储系统可以无缝接入业务,目前杭研多媒体 AI 业务已全量迁入 Curve共享文件存储系统,业务使用后的收益包括:

    • 成本下降:Curve共享文件存储系统 后端接入 NOS低频存储,相比3副本存储每年每 PB 数据存储可节约40%成本
    • 性能收益:在通用场景 Curve共享文件存储系统性能和 Ceph 内核文件系统差不多持平,在 AI 存储密集型的特征提取和部分特征训练场景性能提升30%+,计算密集型特征训练场景性能和Ceph内核文件系统持平。尤其是在昂贵的GPU节点上,存储性能提升可以带来更高的GPU利用效率,从而降低训练成本。
    • 提升训练任务并发度:使用Ceph文件系统作为AI训练数据集存储后端时,所有数据需要实时从存储后端读取,一旦业务有多个AI任务需要并发执行,就会导致Ceph文件系统存储后端负载超出集群总能力,最终导致训练任务耗时大大拉长。Curve共享文件存储系统通过利用多级缓存加速能力,大部分训练数据只需要从存储后端读取一次即可缓存到本地或分布式缓存集群,从而降低对存储后端的性能需求,把负载分散到训练节点或分布式缓存集群,极大提升训练任务的并发度,减少多个训练任务之间的互相影响。

    另外云音乐广告算法团队AI业务也已经落地使用Curve共享文件存储系统,稳定运行半年多。

    Curve共享文件存储系统不仅适用于AI业务场景,还适用于ElasticSearch等大数据存储分析业务场景,目前也在多个集团内部业务场景落地使用。

    四、项目未来规划和展望

    Curve 是一个开源项目(包括共享文件存储和块存储两个子项目),不仅服务于网易内部,也服务于外部用户,当前外部仍然存在大量的 AI 业务场景下的存储需求,目前 Curve共享文件存储系统在 AI 场景下的落地,已证明其在该场景下的性价比优势,更有利于后续在该场景下的推广。

    Curve 作为一个年轻的文件系统,仍在快速迭代发展中,后续将继续聚焦在 AI 、大数据存储等场景:

    • 和 AI 框架的融合:做到⾃动预热、训练节点和缓存节点的亲和性调度、与各类算法平台的深度融合等,进一步提升AI训练场景下的易用性和性能表现
    • ⼤数据和 AI 的融合:提供 HDFS 接⼝,使⽤ Curve共享文件存储系统即可以⽤在数据⽣产收集也可以⽤于后续处理和训练
    • 推动在更多业务场景的落地
    - - +
    跳到主要内容

    Curve 文件系统为 AI 业务降本增效

    背景

    如今大数据和人工智能领域的快速发展,随着数据量的爆炸式增长,对底层文件存储的扩展性成本性能提出了更高的要求。特别在 AI 业务场景下,文件系统面临一些新的变化:POSIX 接口兼容、文件共享、海量小文件、数据读多写少等。

    Why Curve?

    Editor

    1. 接口兼容

    Curve 文件系统同时支持 POSIX、HDFS和K8s CSI 接入方式,可以满足 AI 场景下业务存储的无缝替换。

    2. 横向扩展

    随着文件数量的增长,现有的文件系统如 CephFS 和 HDFS 等元数据的可扩展性不足,无法满足大规模文件存储的需求。Curve 文件系统自研的元数据引擎可横向扩展,解决元数据管理的问题。

    Curve 文件系统原数据引擎具有高可用高可靠高可扩的特点,数据的可靠性和可用性通过 Raft 协议保证,元数据经过分片均匀分散在不同的 Raft-Group 中,保证了数据和负载的均衡性,同时支持业务按需进行一键弹性扩缩容。

    3. 高性能

    随着文件数量的增加,传统文件系统元数据的性能会大幅下降,此外,小文件的读写性能也比较差,这会对大数据和AI等业务产生影响。Curve文件系统通过多级缓存机制,提高文件元数据访问性能,并改善小文件的读写性能。

    3.1 元数据缓存机制

    元数据支持内核和本地的多级缓存,并提供灵活的缓存配置,用户可以根据自己业务的特点配置合适的缓存失效时间,以在满足一致性要求的前提下获取更高的操作性能。此外,通过结合VFS层的重试机制,Curve 文件系统提供了完善的 CTO(close-to-open)一致性。

    Kernel Cache -> 通用缓存
    fs.kernelCache.attrTimeoutSec=3600
    fs.kernelCache.dirAttrTimeoutSec=3600
    fs.kernelCache.entryTimeoutSec=3600
    fs.kernelCache.dirEntryTimeoutSec=3600

    Open File Cache -> 文件读写
    fs.openFile.lruSize=65536

    Negative Lookup Cache -> 代码编译/SO查找/所以命令
    fs.lookupCache.negativeTimeoutSec=1
    fs.lookupCache.minUses=3

    Directory Cache -> 大目录/ls、find
    fs.dirCache.lruSize=5000000

    3.2 数据缓存机制

    数据层面 Curve 文件系统同样支持可配的内存缓存、本地磁盘缓存和分布式缓存集群来加速数据的访问。

    1. 内存缓存:用于加速当前节点上的读写速度(sync操作时会刷到持久化存储保证数据可靠性)。
    2. 本地磁盘缓存:用于加速当前节点上的读写速度(开启共享(cto)时,数据会同时刷一份到共享缓存中,如果没配置共享缓存则需要上传到后端数据存储引擎)。
    3. 共享缓存:用于加速跨节点间的数据共享速度。

    为了加速数据的读取速度,Curve 文件系统支持数据的预读和预热。

    预读(prefetch):即在数据访问时,可以将文件超过访问长度外的数据提前读入缓存,提高后续读请求缓存命中率。

    预热(warmup):指用户在使用到某部分数据之前主动的触发该部分数据写到指定缓存层,提高使用时的性能,例如在 AI 训练场景下,可以提前将训练数据集预热到缓存中,来加速整个训练过程。

    降本增效成果

    网易杭研多媒体团队 AI 业务之前使用三副本 Ceph 内核文件存储来支撑AI场景,包括通用、AI相关的各种流程。AI 业务存储的数据量是巨大的,但其中 80% 都是冷数据,使用三副本存储成本很高。业务期望找一个文件系统能无缝替换 Ceph,且在保证性能的同时能够降低存储成本。

    目前网易杭研多媒体 AI 业务已全量迁入 Curve 文件系统,业务使用后的收益包括:

    成本下降:Curve 文件系统后端接入网易对象存储(NetEase Object Storage)低频存储,相比3副本存储每年每 PB 数据存储可节约75%成本。

    性能提升:在通用场景 Curve 文件系统性能和 Ceph 内核文件系统差不多持平,在 AI 存储密集型的特征提取和部分特征训练场景性能提升30%+,计算密集型特征训练场景性能和Ceph内核文件系统持平。尤其是在昂贵的GPU节点上,存储性能提升可以带来更高的GPU利用效率,从而降低训练成本。

    提升训练任务并发度:使用 Ceph 文件系统作为 AI 训练数据集存储后端时,所有数据需要实时从存储后端读取,一旦业务有多个AI任务需要并发执行,就会导致 Ceph 文件系统存储后端负载超出集群总能力,最终导致训练任务耗时大大拉长。Curve 文件系统通过利用多级缓存加速能力,大部分训练数据只需要从存储后端读取一次即可缓存到本地或分布式缓存集群,从而降低对存储后端的性能需求,把负载分散到训练节点或分布式缓存集群,极大提升训练任务的并发度,减少多个训练任务之间的互相影响。

    后续规划与展望

    Curve 作为一个年轻的文件系统,仍在快速迭代发展中,后续将继续聚焦在 AI 、大数据存储等场景:

    1. 进一步优化元数据和数据读写性能,为支持更多应用场景打好基础。
    2. 和 AI 框架的融合,做到⾃动预热、训练节点和缓存节点的亲和性调度、与各类算法平台的深度融合等,进一步提升AI训练场景下的易用性和性能表现。
    + + \ No newline at end of file diff --git a/CurveFS/usecase/asr-storage.html b/CurveFS/usecase/asr-storage.html index 1f13da7..0407020 100644 --- a/CurveFS/usecase/asr-storage.html +++ b/CurveFS/usecase/asr-storage.html @@ -4,12 +4,12 @@ CurveFS 助力网易云商,解决语音识别训练数据增长需求 | Curve Book - - + +
    -
    跳到主要内容

    CurveFS 助力网易云商,解决语音识别训练数据增长需求

    云商 ASR 训练数据快速增长面临困境

    随着语音技术在各行各业的广泛应用,自动语音识别(ASR)正在成为众多云服务商的核心竞争力。 +

    CurveFS 助力网易云商,解决语音识别训练数据增长需求

    云商 ASR 训练数据快速增长面临困境

    随着语音技术在各行各业的广泛应用,自动语音识别(ASR)正在成为众多云服务商的核心竞争力。 但 ASR 模型的持续优化需要大量训练数据的支持,如何高效管理海量训练数据成为云商面临的一个难题。

    网易云商在发展 ASR 业务过程中,存储面临了巨大挑战。

    • 将海量训练数据保存在单个节点上,不仅存储资源利用率低下,也使数据难以被多个训练任务共享访问。
    • 仅存放在本地还带来数据丢失风险。
    • 随着语音数据量呈指数增长,单节点存储早已无法负荷。
    • 存储空间满溢,将严重制约业务进一步扩张。

    针对存储面临的这些困境,网易云商迫切需要找到一个更优的存储解决方案。

    CurveFS

    为解决这一痛点,网易云商决定引入开源存储系统CurveFS。

    CurveFS是什么

    CurveFS是一个基于 Fuse实现的兼容POSIX 接口的分布式文件系统,架构如下图所示:

    curvefs arch

    CurveFS由三个部分组成:

    1. 客户端 curve-fuse,和元数据集群交互处理文件元数据增删改查请求,和数据集群交互处理文件数据的增删改查请求。

    2. 元数据集群 metaserver cluster,用于接收和处理元数据(inode 和 dentry)的增删改查请求。 metaserver cluster的架构具有高可靠、高可用、高可扩的特点:MDS用于管理集群拓扑结构,资源调度。 metaserver 是数据节点,一个 metaserver 对应管理一个物理磁盘。 @@ -20,8 +20,8 @@ CurveFS 兼容两种存储后端的特性,使其可以适应不同业务的存储需求,既保证高性能,也控制存储成本。

      CurveFS 应用价值

      相较于本地存储而言,CurveFS 具有以下优势:

      • 将训练数据存储在云端对象存储,实现资源弹性扩展

      • 统一管理训练数据,支持多节点共享访问

      • 后端接入 NOS 低频存储,相比本地存储能够大幅降低 ASR 训练存储成本

      • 支持数据多级缓存,提升训练效率约 30 %

      与保存在单机上的传统方式相比,CurveFS云端部署方案虽然数据读取速度略低,但考虑到未来训练数据量的增长需求,其扩展性和资源优化能力已大大超越本地存储。

      Curve共享文件存储系统不仅适用于 ASR 业务场景,在 ElasticSearch 等大数据存储分析业务场景也有应用,目前也在多个集团内部业务场景落地使用。

      项目未来规划和展望

      但是,CurveFS也面临一定的挑战。 由于ASR训练过程中需要频繁读取数据,这对云存储的访问速度和稳定性提出了较高要求。 云存储由于网络传输的影响,读取速度难以与本地存储相比。 -这些在 CurveFS 的后续版本迭代中持续优化,包括优化元数据性能和多级缓存等。

      总结

      作为一个年轻的项目,Curve仍在快速迭代。未来Curve会继续优化在AI和大数据分析场景的适配能力:

      • 与各类AI框架深度集成,提供自动化的数据预热、训练优化等功能,提升AI训练的易用性和效率。
      • 支持 HDFS 接口,使其能够统一应用于数据收集、存储与分析处理,实现数据生命周期的无缝管理。
      • 在更多实际业务场景中落地,丰富解决方案经验。

      通过这些努力,Curve正在成长为一个成熟、强大、适用范围广泛的开源存储系统,为用户提供简单高效的存储服务。它的发展前景广阔,必将为开源社区做出重要贡献。

    - - +这些在 CurveFS 的后续版本迭代中持续优化,包括优化元数据性能和多级缓存等。

    总结

    作为一个年轻的项目,Curve仍在快速迭代。未来Curve会继续优化在AI和大数据分析场景的适配能力:

    • 与各类AI框架深度集成,提供自动化的数据预热、训练优化等功能,提升AI训练的易用性和效率。
    • 支持 HDFS 接口,使其能够统一应用于数据收集、存储与分析处理,实现数据生命周期的无缝管理。
    • 在更多实际业务场景中落地,丰富解决方案经验。

    通过这些努力,Curve正在成长为一个成熟、强大、适用范围广泛的开源存储系统,为用户提供简单高效的存储服务。它的发展前景广阔,必将为开源社区做出重要贡献。

    + + \ No newline at end of file diff --git a/CurveFS/usecase/elasticsearch-cold-data.html b/CurveFS/usecase/elasticsearch-cold-data.html index dfe6a5b..e3b07fb 100644 --- a/CurveFS/usecase/elasticsearch-cold-data.html +++ b/CurveFS/usecase/elasticsearch-cold-data.html @@ -4,13 +4,13 @@ CurveFS在Elasticsearch冷热数据存储中的应用 | Curve Book - - + +
    -
    跳到主要内容

    CurveFS在Elasticsearch冷热数据存储中的应用

    ES使用CurveFS的收益

    CurveFS提供的成本优势

    为了高可靠,ES如果使用本地盘的话一般会使用两副本,也就是说存储1PB数据需要2PB的物理空间。但是如果使用CurveFS,由于CurveFS的后端可以对接S3,所以可以利用对象存储提供的EC能力,既保证了可靠性,又可以减少副本数量,从而达到了降低成本的目的。

    以网易对象存储这边当前主流的EC 20+4使用为例,该使用方式就相当于是1.2副本。所以如果以ES需要1PB使用空间为例,那么使用CurveFS+1.2副本对象存储只需要1.2PB空间,相比本地盘2副本可以节省800TB左右的容量,成本优化效果非常显著。

    CurveFS提供的性能优势

    以下文将要介绍的使用场景为例,对比ES原来使用S3插件做snapshot转存储的方式,由于每次操作的时候索引需要进行restore操作,以100G的日志索引为例,另外会有传输时间,如果restore的恢复速度为100M,那么也要300多秒。实际情况是在一个大量写入的集群,这样的操作可能要几个小时。

    而使用CurveFS后的新模式下基本上只要对freeze的索引进行unfreeze,让对应节点的ES将对应的meta数据载入内存就可以执行索引,大概耗时仅需30S左右,相比直接用S3存储冷数据有数量级的下降。

    CurveFS提供的容量优势

    本地盘的容量是有限的,而CurveFS的空间容量可以在线无限扩展。同时减少了本地存储的维护代价。

    CurveFS提供的易运维优势

    ES使用本地盘以及使用S3插件方式,当需要扩容或者节点异常恢复时,需要增加人力运维成本。CurveFS实现之初的一个目标就是易运维,所以CurveFS可以实现数条命令的快速部署以及故障自愈能力。

    另外如果ES使用CurveFS,就实现了存算分离,进一步释放了ES使用者的运维负担。

    ES使用背景

    在生产环境有大量的场景会用到ES做文档、日志存储后端,因为ES优秀的全文检索能力在很多时候可以大大的简化相关系统设计的复杂度。比较常见的为日志存储,链路追踪,甚至是监控指标等场景都可以用ES来做。

    网易ES选用CurveFS的原因

    网易ES底层存储的使用经历了从本地盘到minio,再从minio到CurveFS这一过程。

    本地盘到minio

    为了符合国内的法律约束,线上系统需要按照要求存储6个月到1年不等的系统日志,主要是国内等保、金融合规等场景。按照内部管理的服务器数量,单纯syslog的日志存储空间每天就需要1T,按照当前手头有的5台12盘位4T硬盘的服务器,最多只能存储200多天的日子,无法满足日志存储1年的需求。

    针对ES使用本地盘无法满足存储容量需求这一情况,网易ES底层存储之前单独引入过基于S3的存储方案来降低存储空间的消耗。如下图,ES配合minio做数据存储空间的压缩。举例来说100G的日志,到了ES里面因为可靠性需求,需要双副本,会使用200G的空间。ES针对索引分片时间,定期性转存储到minio仓库。

    es minio

    minio到CurveFS

    这个方案从一定程度上缓解了存储空间的资源问题,但是实际使用的时候还会感觉非常不便利。

    • 运维成本。ES节点升级的时候需要额外卸载安装S3插件,有一定的运维成本。
    • 性能瓶颈。自己私有化搭建的Minio随着bucket里面数据量的增长,数据存储和抽取都会成为一个很大的问题
    • 稳定性问题。在内部搭建的Minio集群在做数据restore的时候,因为文件处理性能等因素,经常遇到访问超时等场景,所以一直在关注是否有相关的系统可以提供更好的读写稳定性。

    由于S3协议经过多年的演化,已经成了对象存储的工业标准。很多人都有想过用fuse的方式使用S3的存储能力。事实上基于S3的文件系统有很多款,例如开源的s3fs-fuse、ossfs、RioFS、CurveFS等。

    在通过实际调研以及大量的测试后,基于Curve的性能(尤其是元数据方面,CurveFS是基于RAFT一致性协议自研的元数据引擎,与其他没有元数据引擎的S3文件系统(比如s3fs,ossfs)相比具备巨大的性能优势),易运维,稳定性,Curve可以同时提供块存储以及文件存储能力等能力以及Curve活跃的开源氛围,最终选用了CurveFS。

    CurveFS结合ES的实践

    CurveFS是什么

    CurveFS是一个基于 Fuse实现的兼容POSIX 接口的分布式文件系统,架构如下图所示:

    curvefs arch

    CurveFS由三个部分组成:

    1. 客户端curve-fuse,和元数据集群交互处理文件元数据增删改查请求,和数据集群交互处理文件数据的增删改查请求。
    2. 元数据集群metaserver cluster,用于接收和处理元数据(inode和dentry)的增删改查请求。metaserver cluster的架构和CurveBS类似,具有高可靠、高可用、高可扩的特点:MDS用于管理集群拓扑结构,资源调度。metaserver是数据节点,一个metaserver对应管理一个物理磁盘。CurveFS使用Raft保证元数据的可靠性和可用性,Raft复制组的基本单元是copyset。一个metaserver上包含多个copyset复制组。
    3. 数据集群data cluster,用于接收和处理文件数据的增删改查。data cluster目前支持两存储类型:支持S3接口的对象存储以及CurveBS(开发中)。

    Curve除了既能支持文件存储,也能支持块存储之外,从上述架构图我们还能看出Curve的一个特点:就是CurveFS后端既可以支持S3,也可以支持Curve块存储。这样的特点可以使得用户可以选择性地把性能要求高的系统的数据存储在Curve块存储后端,而对成本要求较高的系统可以把数据存储在S3后端。

    CurveFS结合ES的实践

    ES使用CurveFS

    CurveFS定位于网易运维的云原生系统,所以其部署是简单快速的,通过CurveAdm工具,只需要几条命令边便可以部署起CurveFS的环境,具体部署见https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment ; https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment , 部署后效果如下图:

    在日志存储场景,改造是完全基于历史的服务器做的在线改造。下图是线上日志的一个存储架构示例,node0到node5可以认为是热存储节点,机器为12 4T,128G的存储机型,每个节点跑3个ES实例,每个实例32G内存,4块独立盘。node6到node8为12 8T的存储机型,3台服务器跑一个Minio集群,每台机器上的ES实例不做数据本地写。

    可以看到主要的改造重点是将node6到node8 3个节点进行ES的配置改造,其中以node6节点的配置为例:

    cluster.name: ops-elk
    node.name: ${HOSTNAME}
    network.host: [_local_,_bond0_]
    http.host: [_local_]
    discovery.zen.minimum_master_nodes: 1
    action.auto_create_index: true
    transport.tcp.compress: true
    indices.fielddata.cache.size: 20%
    path.data: /home/nbs/elk/data1/data
    path.logs: /home/nbs/elk/data1/logs
    - /curvefs/mnt1
    xpack.ml.enabled: false
    xpack.monitoring.enabled: false
    discovery.zen.ping.unicast.hosts: ["ops-elk1:9300","ops-elk7:9300","ops-elk
    7:9300","ops-elk8.jdlt.163.org:9300"]
    node.attr.box_type: cold

    如配置所示,主要的改造为调整ES的数据存储目录到CurveFS的fuse挂载目录,然后新增 node.attr.box_type 的设置。在node6到node8上分别配置为cold,node1到node5配置对应属性为hot,所有节点配置完成后进行一轮滚动重启。

    ES设置

    除了底层配置外,很重要的一点就是调整index索引的设置。这块的设置难度不高,要点是:

    1. 对应索引设置数据分配依赖和aliases
    2. 设置对应的index Lifecycle policy

    其实在新节点开放数据存储后,如果没有亲和性设置,集群马上会启动relocating操作。因此建议对存量的索引新增routing.alloction.require的设置来避免热数据分配到CurveFS存储节点。针对每天新增索引,建议加入以下这样的index template配置。

    {
    "template": {
    "settings": {
    "index": {
    "lifecycle": {
    "name": "syslog",
    "rollover_alias": "syslog"
    },
    "routing": {
    "allocation": {
    "require": {
    "box_type": "hot"
    }
    }
    },
    "number_of_shards": "10",
    "translog": {
    "durability": "async"
    }
    }
    },
    "aliases": {
    "syslog": {}
    },
    "mappings": {}
    }
    }

    这个index template设置的核心要点是:

    1. routing部分要指定新索引写到热数据节点;
    2. lifecycle中的新增rollover_alias设置。index部分的lifecycle是指索引的生命周期策略,需要注意rollover_alias里面的值要和下面的aliases定义对齐。

    冷数据的切换,可以在kibana的index_lifecycle_management管理页面设置。针对上面的syslog场景,hot部分设置如下图,其余基本默认的就可以了。

    es lifecycle

    在索引周期管理配置页面中,除了设置hot phase,还可以设置warm phase,在warm phase可以做一些shrink,force merge等操作,日志存储场景我们直接做hot到cold的处理逻辑。

    es lifecycle

    从技术上讲,日志存储类型的业务,底层索引一旦完成写后基本不做再次的数据更改,设置索引副本数量主要是为了应对分布式系统节点宕机等异常场景的数据恢复。如果存储层面有更可靠的方式,那么自然而然可以将es的副本数量调整为0,可以明显的降低对存储空间的使用需求。

    - - +
    跳到主要内容

    CurveFS在Elasticsearch冷热数据存储中的应用

    ES使用CurveFS的收益

    CurveFS提供的成本优势

    为了高可靠,ES如果使用本地盘的话一般会使用两副本,也就是说存储1PB数据需要2PB的物理空间。但是如果使用CurveFS,由于CurveFS的后端可以对接S3,所以可以利用对象存储提供的EC能力,既保证了可靠性,又可以减少副本数量,从而达到了降低成本的目的。

    以网易对象存储这边当前主流的EC 20+4使用为例,该使用方式就相当于是1.2副本。所以如果以ES需要1PB使用空间为例,那么使用CurveFS+1.2副本对象存储只需要1.2PB空间,相比本地盘2副本可以节省800TB左右的容量,成本优化效果非常显著。

    CurveFS提供的性能优势

    以下文将要介绍的使用场景为例,对比ES原来使用S3插件做snapshot转存储的方式,由于每次操作的时候索引需要进行restore操作,以100G的日志索引为例,另外会有传输时间,如果restore的恢复速度为100M,那么也要300多秒。实际情况是在一个大量写入的集群,这样的操作可能要几个小时。

    而使用CurveFS后的新模式下基本上只要对freeze的索引进行unfreeze,让对应节点的ES将对应的meta数据载入内存就可以执行索引,大概耗时仅需30S左右,相比直接用S3存储冷数据有数量级的下降。

    CurveFS提供的容量优势

    本地盘的容量是有限的,而CurveFS的空间容量可以在线无限扩展。同时减少了本地存储的维护代价。

    CurveFS提供的易运维优势

    ES使用本地盘以及使用S3插件方式,当需要扩容或者节点异常恢复时,需要增加人力运维成本。CurveFS实现之初的一个目标就是易运维,所以CurveFS可以实现数条命令的快速部署以及故障自愈能力。

    另外如果ES使用CurveFS,就实现了存算分离,进一步释放了ES使用者的运维负担。

    ES使用背景

    在生产环境有大量的场景会用到ES做文档、日志存储后端,因为ES优秀的全文检索能力在很多时候可以大大的简化相关系统设计的复杂度。比较常见的为日志存储,链路追踪,甚至是监控指标等场景都可以用ES来做。

    网易ES选用CurveFS的原因

    网易ES底层存储的使用经历了从本地盘到minio,再从minio到CurveFS这一过程。

    本地盘到minio

    为了符合国内的法律约束,线上系统需要按照要求存储6个月到1年不等的系统日志,主要是国内等保、金融合规等场景。按照内部管理的服务器数量,单纯syslog的日志存储空间每天就需要1T,按照当前手头有的5台12盘位4T硬盘的服务器,最多只能存储200多天的日子,无法满足日志存储1年的需求。

    针对ES使用本地盘无法满足存储容量需求这一情况,网易ES底层存储之前单独引入过基于S3的存储方案来降低存储空间的消耗。如下图,ES配合minio做数据存储空间的压缩。举例来说100G的日志,到了ES里面因为可靠性需求,需要双副本,会使用200G的空间。ES针对索引分片时间,定期性转存储到minio仓库。

    es minio

    minio到CurveFS

    这个方案从一定程度上缓解了存储空间的资源问题,但是实际使用的时候还会感觉非常不便利。

    • 运维成本。ES节点升级的时候需要额外卸载安装S3插件,有一定的运维成本。
    • 性能瓶颈。自己私有化搭建的Minio随着bucket里面数据量的增长,数据存储和抽取都会成为一个很大的问题
    • 稳定性问题。在内部搭建的Minio集群在做数据restore的时候,因为文件处理性能等因素,经常遇到访问超时等场景,所以一直在关注是否有相关的系统可以提供更好的读写稳定性。

    由于S3协议经过多年的演化,已经成了对象存储的工业标准。很多人都有想过用fuse的方式使用S3的存储能力。事实上基于S3的文件系统有很多款,例如开源的s3fs-fuse、ossfs、RioFS、CurveFS等。

    在通过实际调研以及大量的测试后,基于Curve的性能(尤其是元数据方面,CurveFS是基于RAFT一致性协议自研的元数据引擎,与其他没有元数据引擎的S3文件系统(比如s3fs,ossfs)相比具备巨大的性能优势),易运维,稳定性,Curve可以同时提供块存储以及文件存储能力等能力以及Curve活跃的开源氛围,最终选用了CurveFS。

    CurveFS结合ES的实践

    CurveFS是什么

    CurveFS是一个基于 Fuse实现的兼容POSIX 接口的分布式文件系统,架构如下图所示:

    curvefs arch

    CurveFS由三个部分组成:

    1. 客户端curve-fuse,和元数据集群交互处理文件元数据增删改查请求,和数据集群交互处理文件数据的增删改查请求。
    2. 元数据集群metaserver cluster,用于接收和处理元数据(inode和dentry)的增删改查请求。metaserver cluster的架构和CurveBS类似,具有高可靠、高可用、高可扩的特点:MDS用于管理集群拓扑结构,资源调度。metaserver是数据节点,一个metaserver对应管理一个物理磁盘。CurveFS使用Raft保证元数据的可靠性和可用性,Raft复制组的基本单元是copyset。一个metaserver上包含多个copyset复制组。
    3. 数据集群data cluster,用于接收和处理文件数据的增删改查。data cluster目前支持两存储类型:支持S3接口的对象存储以及CurveBS(开发中)。

    Curve除了既能支持文件存储,也能支持块存储之外,从上述架构图我们还能看出Curve的一个特点:就是CurveFS后端既可以支持S3,也可以支持Curve块存储。这样的特点可以使得用户可以选择性地把性能要求高的系统的数据存储在Curve块存储后端,而对成本要求较高的系统可以把数据存储在S3后端。

    CurveFS结合ES的实践

    ES使用CurveFS

    CurveFS定位于网易运维的云原生系统,所以其部署是简单快速的,通过CurveAdm工具,只需要几条命令边便可以部署起CurveFS的环境,具体部署见https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment ; https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment , 部署后效果如下图:

    在日志存储场景,改造是完全基于历史的服务器做的在线改造。下图是线上日志的一个存储架构示例,node0到node5可以认为是热存储节点,机器为12 4T,128G的存储机型,每个节点跑3个ES实例,每个实例32G内存,4块独立盘。node6到node8为12 8T的存储机型,3台服务器跑一个Minio集群,每台机器上的ES实例不做数据本地写。

    可以看到主要的改造重点是将node6到node8 3个节点进行ES的配置改造,其中以node6节点的配置为例:

    cluster.name: ops-elk
    node.name: ${HOSTNAME}
    network.host: [_local_,_bond0_]
    http.host: [_local_]
    discovery.zen.minimum_master_nodes: 1
    action.auto_create_index: true
    transport.tcp.compress: true
    indices.fielddata.cache.size: 20%
    path.data: /home/nbs/elk/data1/data
    path.logs: /home/nbs/elk/data1/logs
    - /curvefs/mnt1
    xpack.ml.enabled: false
    xpack.monitoring.enabled: false
    discovery.zen.ping.unicast.hosts: ["ops-elk1:9300","ops-elk7:9300","ops-elk
    7:9300","ops-elk8.jdlt.163.org:9300"]
    node.attr.box_type: cold

    如配置所示,主要的改造为调整ES的数据存储目录到CurveFS的fuse挂载目录,然后新增 node.attr.box_type 的设置。在node6到node8上分别配置为cold,node1到node5配置对应属性为hot,所有节点配置完成后进行一轮滚动重启。

    ES设置

    除了底层配置外,很重要的一点就是调整index索引的设置。这块的设置难度不高,要点是:

    1. 对应索引设置数据分配依赖和aliases
    2. 设置对应的index Lifecycle policy

    其实在新节点开放数据存储后,如果没有亲和性设置,集群马上会启动relocating操作。因此建议对存量的索引新增routing.alloction.require的设置来避免热数据分配到CurveFS存储节点。针对每天新增索引,建议加入以下这样的index template配置。

    {
    "template": {
    "settings": {
    "index": {
    "lifecycle": {
    "name": "syslog",
    "rollover_alias": "syslog"
    },
    "routing": {
    "allocation": {
    "require": {
    "box_type": "hot"
    }
    }
    },
    "number_of_shards": "10",
    "translog": {
    "durability": "async"
    }
    }
    },
    "aliases": {
    "syslog": {}
    },
    "mappings": {}
    }
    }

    这个index template设置的核心要点是:

    1. routing部分要指定新索引写到热数据节点;
    2. lifecycle中的新增rollover_alias设置。index部分的lifecycle是指索引的生命周期策略,需要注意rollover_alias里面的值要和下面的aliases定义对齐。

    冷数据的切换,可以在kibana的index_lifecycle_management管理页面设置。针对上面的syslog场景,hot部分设置如下图,其余基本默认的就可以了。

    es lifecycle

    在索引周期管理配置页面中,除了设置hot phase,还可以设置warm phase,在warm phase可以做一些shrink,force merge等操作,日志存储场景我们直接做hot到cold的处理逻辑。

    es lifecycle

    从技术上讲,日志存储类型的业务,底层索引一旦完成写后基本不做再次的数据更改,设置索引副本数量主要是为了应对分布式系统节点宕机等异常场景的数据恢复。如果存储层面有更可靠的方式,那么自然而然可以将es的副本数量调整为0,可以明显的降低对存储空间的使用需求。

    + + \ No newline at end of file diff --git a/CurveFS/usecase/hadoop-on-cloud.html b/CurveFS/usecase/hadoop-on-cloud.html index ecdad91..a28d59a 100644 --- a/CurveFS/usecase/hadoop-on-cloud.html +++ b/CurveFS/usecase/hadoop-on-cloud.html @@ -4,13 +4,13 @@ 公有云上使用CurveFS替换HDFS冷存储实现降本 | Curve Book - - + +
    -
    跳到主要内容

    公有云上使用CurveFS替换HDFS冷存储实现降本

    本文讨论的部署方式为基于CurveFS挂载点替换公有云EBS等云盘,CurveFS支持HDFS SDK功能将于2.7版本发布,届时将提供完全兼容HDFS API的SDK jar包供上层大数据应用(如Spark、Flink等)使用,将不再需要CurveFS挂载点。

    问题背景

    场景1:由于相关海外政策法规的收紧,国内企业如果在国外有业务运营,则数据也必须在本地处理,而不能传输到国内。因此部署在国内私有IDC的大数据处理集群就不能使用,需要在国外单独部署。出于成本和运营复杂度等问题考量,国外部署环境的优先选择必定是各类公有云平台,而不是自建或者租赁IDC机房。

    场景2:企业大数据业务上云,但是不想被云厂商的EMR等服务锁定,想做到随时可以切换云服务商。此时就需要考虑自己部署维护大数据存储集群。

    因此云上部署Hadoop/HDFS集群就是面临的一个现实问题。

    业界实践

    通常有2个选择:

    1. 使用云厂商提供的兼容HDFS协议的存储服务,甚至连上层的计算服务也都采用云厂商方案,几乎各大云厂商都提供了EMR服务
    2. 基于云厂商提供的裸金属服务器+EBS云盘部署私有的HDFS集群,在EC2实例或者裸金属实例上运行MR服务

    通常来说,使用第二种方案的成本要高于第一种,毕竟EBS云盘已经是多副本(通常是3副本),HDFS自身又是多副本的架构,即使只配置2副本,也有3*2倍的副本数。而且EBS云盘的价格通常比物理盘高很多很多。

    一般来说第一种方案更适合中小型企业,对HDFS或者上层计算平台(如spark、flink等)没有做定制化改造,可以直接拿云厂商的EMR服务替换。第二种方案则是中大型企业(或者对计算平台做了较多定制化改造的企业)上云面临的一个门槛,只能迫于无奈选择第二种方案。

    但第一种方案相比传统的私有IDC内部署大数据处理集群的方式,仍然有比较大的成本负担。云厂商的大数据处理服务价格通常也是比较高的,类似的情况还有RDS等数据库或其他中间件。

    通过上面的分析来看,这两种方案都不是客户所乐意采用的,那么有没有第三种选择呢?既要又要能做到吗?

    Curve的建议

    建议部署架构如下图所示:

    deployment arch

    部署架构解释:

    1. HDFS集群从原来使用裸金属的EBS盘改成使用CurveFS挂载点(单机模式),数据实际存储在S3,元数据在CurveFS的3副本raft集群(也可以改为单机单副本模式)
    2. 如果由HDFS管理数据副本,则对CurveFS高可用、高可靠要求进一步降低,只是会导致存储到S3上的数据量增加2~3倍(根据HDFS副本数),如果由CurveFS及S3来保证数据的可靠性,则可以把HDFS的副本数设置为1
    3. CurveFS支持内存buffer、EBS云盘、以及分布式KV作为数据缓存,再加上元数据的客户端及服务端内存cache,单机部署模式下都是本地访问性能更高
    4. CurveFS因为是单机部署,且不需要与其他节点共享访问文件,所以可以每个CurveFS节点用一个独立的S3桶,这也能利用多桶的能力增加性能(单桶一般会有QoS上限限制)
    5. EBS云盘作为S3数据的缓存,可以大大降低S3文件的访问次数(S3一般会对访问次数收费),在提升性能的基础上进一步节省成本

    根据相关业务方的FIO及Hive实测数据,这个部署架构做到了既不影响HDFS的功能增强又基本不影响性能。最主要的是把价格昂贵的EBS云盘存储替换为了价格低廉的S3对象存储,整体存储成本节约可以达到50%(HDFS设置为3副本的情况)甚至70%以上(HDFS设置1副本情况)。

    未来思考

    1. CurveFS实现更精简、更高性能的单机部署架构
    2. 开发新的服务支持将S3导出为iSCSI卷类型的块设备,并配合Bcache等缓存组件来进一步提升随机读写性能
    3. 推而广之,所有云上有大容量数据存储需求的业务场景(原来用大容量EBS云盘做文件系统),都可以采用类似架构来降本
    - - +
    跳到主要内容

    公有云上使用CurveFS替换HDFS冷存储实现降本

    本文讨论的部署方式为基于CurveFS挂载点替换公有云EBS等云盘,CurveFS支持HDFS SDK功能将于2.7版本发布,届时将提供完全兼容HDFS API的SDK jar包供上层大数据应用(如Spark、Flink等)使用,将不再需要CurveFS挂载点。

    问题背景

    场景1:由于相关海外政策法规的收紧,国内企业如果在国外有业务运营,则数据也必须在本地处理,而不能传输到国内。因此部署在国内私有IDC的大数据处理集群就不能使用,需要在国外单独部署。出于成本和运营复杂度等问题考量,国外部署环境的优先选择必定是各类公有云平台,而不是自建或者租赁IDC机房。

    场景2:企业大数据业务上云,但是不想被云厂商的EMR等服务锁定,想做到随时可以切换云服务商。此时就需要考虑自己部署维护大数据存储集群。

    因此云上部署Hadoop/HDFS集群就是面临的一个现实问题。

    业界实践

    通常有2个选择:

    1. 使用云厂商提供的兼容HDFS协议的存储服务,甚至连上层的计算服务也都采用云厂商方案,几乎各大云厂商都提供了EMR服务
    2. 基于云厂商提供的裸金属服务器+EBS云盘部署私有的HDFS集群,在EC2实例或者裸金属实例上运行MR服务

    通常来说,使用第二种方案的成本要高于第一种,毕竟EBS云盘已经是多副本(通常是3副本),HDFS自身又是多副本的架构,即使只配置2副本,也有3*2倍的副本数。而且EBS云盘的价格通常比物理盘高很多很多。

    一般来说第一种方案更适合中小型企业,对HDFS或者上层计算平台(如spark、flink等)没有做定制化改造,可以直接拿云厂商的EMR服务替换。第二种方案则是中大型企业(或者对计算平台做了较多定制化改造的企业)上云面临的一个门槛,只能迫于无奈选择第二种方案。

    但第一种方案相比传统的私有IDC内部署大数据处理集群的方式,仍然有比较大的成本负担。云厂商的大数据处理服务价格通常也是比较高的,类似的情况还有RDS等数据库或其他中间件。

    通过上面的分析来看,这两种方案都不是客户所乐意采用的,那么有没有第三种选择呢?既要又要能做到吗?

    Curve的建议

    建议部署架构如下图所示:

    deployment arch

    部署架构解释:

    1. HDFS集群从原来使用裸金属的EBS盘改成使用CurveFS挂载点(单机模式),数据实际存储在S3,元数据在CurveFS的3副本raft集群(也可以改为单机单副本模式)
    2. 如果由HDFS管理数据副本,则对CurveFS高可用、高可靠要求进一步降低,只是会导致存储到S3上的数据量增加2~3倍(根据HDFS副本数),如果由CurveFS及S3来保证数据的可靠性,则可以把HDFS的副本数设置为1
    3. CurveFS支持内存buffer、EBS云盘、以及分布式KV作为数据缓存,再加上元数据的客户端及服务端内存cache,单机部署模式下都是本地访问性能更高
    4. CurveFS因为是单机部署,且不需要与其他节点共享访问文件,所以可以每个CurveFS节点用一个独立的S3桶,这也能利用多桶的能力增加性能(单桶一般会有QoS上限限制)
    5. EBS云盘作为S3数据的缓存,可以大大降低S3文件的访问次数(S3一般会对访问次数收费),在提升性能的基础上进一步节省成本

    根据相关业务方的FIO及Hive实测数据,这个部署架构做到了既不影响HDFS的功能增强又基本不影响性能。最主要的是把价格昂贵的EBS云盘存储替换为了价格低廉的S3对象存储,整体存储成本节约可以达到50%(HDFS设置为3副本的情况)甚至70%以上(HDFS设置1副本情况)。

    未来思考

    1. CurveFS实现更精简、更高性能的单机部署架构
    2. 开发新的服务支持将S3导出为iSCSI卷类型的块设备,并配合Bcache等缓存组件来进一步提升随机读写性能
    3. 推而广之,所有云上有大容量数据存储需求的业务场景(原来用大容量EBS云盘做文件系统),都可以采用类似架构来降本
    + + \ No newline at end of file diff --git a/CurveFS/usecase/jiangsu-nongxin-es.html b/CurveFS/usecase/jiangsu-nongxin-es.html index 7d8466f..3163712 100644 --- a/CurveFS/usecase/jiangsu-nongxin-es.html +++ b/CurveFS/usecase/jiangsu-nongxin-es.html @@ -4,13 +4,13 @@ 江苏农信选择CurveFS实现ES冷数据长期存储 | Curve Book - - + +
    -
    跳到主要内容

    江苏农信选择CurveFS实现ES冷数据长期存储

    一、需求背景

    运维大数据平台自构建以来,日志类数据越来越多,目前已超过200TB;日志的种类也越来越丰富,除了最初的业务日志,还有存储ceph日志,中间件nginx、IHS、Tomcat等日志。目前大部分日志的保留周期都在14天或7天左右,无法满足业务日志和运维操作类日志长期保留的需求。各日志条线负责人,对日志的长期存储需求越来越急迫,一个是为了符合监管审计要求,二是对历史日志也能方便的检索分析。从平台角度来看,现有的方式缺乏统一的日志归档管理,各个业务线自我维护,重复建设消耗的成本高。

    二、需求描述

    场景一:业务侧日志

    • 主要需求: 监管审计,历史日志查看和检索
    • 现阶段痛点:
      • 现有的业务日志备份方式,是通过crontab脚本,定期打包put到对象存储 S3中
      • 该备份流程每个业务系统单独自我管理;
      • 同时在需要恢复的时候,需要人为手动操作,下载解压分析备份日志,整个流程耗时比较长。

    场景二:运维侧日志

    • 主要需求: 监管审计,操作留档,历史回溯;例如网络设备相关日志,现要求保留半年以上。

    三、日志数据分层管理方案设计

    实现行内所有日志的统一备份管理,减少分散管理的压力和流程的混乱;同时运维大数据平台可以作为业务日志和对象存储中间的粘合剂,统一扎口,使之能更好的协同工作。 最后,基于数据分层处理,来大大减少索引存储大小以及内存的占用。

    arch

    3.1 方案调研比对

    compare

    选择CurveFS优势:

    1. 索引备份操作变为索引迁移,对用户查询使用均无感知
    2. 索引恢复速度快,直接打开索引即可被查询

    3.2 整体方案设计

    实现方案: 新增Cold层,实现日志长期留存。实现Cold层数据直接对接到对象存储,减少以前日志备份的中间过程,同时能加快对日志的恢复操作。

    arch

    实现通过ITSM工单,自动恢复历史索引数据。

    3.3 日志备份方案设计

    logbackup

    1. ES分层设计
    2. 冷节点层用于索引“备份”
    3. 通过CurveFS实现数据无缝迁移到对象存储

    3.4 日志恢复方案设计

    logrestore

    3.5 日志备份功能实现

    备份执行流程:

    logbackup

    1. 修改集群参数,更好的满足迁移速度
    2. 按照索引模式,循环迁移各个索引,并进行关闭
    3. 修改集群参数,切换回低速迁移模式

    功能实现:

    logbackup

    1. 新建索引备份策略,基于索引模式角度
    2. 新建备份迁移队列
    3. 备份迁移队列编排
    4. 队列定期执行

    四、实际效果

    4.1 日志备份功能

    4.1.1 迁移队列管理

    配置队列每日执行的时间点,关联的接口,以及是否启用;还可以查看历史执行记录:

    queue

    4.1.2 索引生命周期策略管理

    统一的生命周期策略管理,一目了然:

    lifecycle

    配置每个队列中,索引的迁移的顺序;队列之间是并行执行,队列中是串行执行。

    lifecycle

    另外,部分索引不需要长期存储,只需要通过配置迁移到温节点队列即可。

    灵活查看索引的整个生命周期阶段:

    lifecycle

    4.2 日志恢复功能

    4.2.1 用户工单流程配置

    ticket

    4.2.2 用户申请工单表单

    ticket

    五、方案总结

    • 统一管理: 对ES进行上层封装建设,减少对Kibana的依赖,对多集群、多版本ES可以进行统一管理;
    • 自主可控: 通过自研开发,更贴合实际场景;部分日志需要长期留存,转移到冷节点;部分不需要长期留存,在转移到温节点后,静待删除;
    • 更细粒度的控制: 队列配置了索引迁移开始时间,队列中的索引基于编排的顺序,串行开始迁移;
    • 并行控制: 基于队列的机制,实现备份迁移的并行。如果后端存储性能足够,可以多开几个队列,充分利用凌晨的空闲IO;
    • 统一的策略管理: 所有索引备份策略一目了然,方便全局掌控,快速调整;
    • 全生命周期管理: 索引从产生到最后删除,每一个阶段都留有记录,通过时间树的方式快速查看。

    带来的价值:

    • 灵活性,高效性: 都是从S3取数据,这个直接通过打开索引,就能恢复提供查询;
    • 流程数字化,自动化: 通过工单申请,能自动进行接口调用,实现索引恢复和定期关闭;
    • 优化日志数据存储分层,实现降本增效: 对本地SATA盘的依赖减少,依靠云存储的稳定性,ES本地节点存储容量为0,全部存储到S3;
    - - +
    跳到主要内容

    江苏农信选择CurveFS实现ES冷数据长期存储

    一、需求背景

    运维大数据平台自构建以来,日志类数据越来越多,目前已超过200TB;日志的种类也越来越丰富,除了最初的业务日志,还有存储ceph日志,中间件nginx、IHS、Tomcat等日志。目前大部分日志的保留周期都在14天或7天左右,无法满足业务日志和运维操作类日志长期保留的需求。各日志条线负责人,对日志的长期存储需求越来越急迫,一个是为了符合监管审计要求,二是对历史日志也能方便的检索分析。从平台角度来看,现有的方式缺乏统一的日志归档管理,各个业务线自我维护,重复建设消耗的成本高。

    二、需求描述

    场景一:业务侧日志

    • 主要需求: 监管审计,历史日志查看和检索
    • 现阶段痛点:
      • 现有的业务日志备份方式,是通过crontab脚本,定期打包put到对象存储 S3中
      • 该备份流程每个业务系统单独自我管理;
      • 同时在需要恢复的时候,需要人为手动操作,下载解压分析备份日志,整个流程耗时比较长。

    场景二:运维侧日志

    • 主要需求: 监管审计,操作留档,历史回溯;例如网络设备相关日志,现要求保留半年以上。

    三、日志数据分层管理方案设计

    实现行内所有日志的统一备份管理,减少分散管理的压力和流程的混乱;同时运维大数据平台可以作为业务日志和对象存储中间的粘合剂,统一扎口,使之能更好的协同工作。 最后,基于数据分层处理,来大大减少索引存储大小以及内存的占用。

    arch

    3.1 方案调研比对

    compare

    选择CurveFS优势:

    1. 索引备份操作变为索引迁移,对用户查询使用均无感知
    2. 索引恢复速度快,直接打开索引即可被查询

    3.2 整体方案设计

    实现方案: 新增Cold层,实现日志长期留存。实现Cold层数据直接对接到对象存储,减少以前日志备份的中间过程,同时能加快对日志的恢复操作。

    arch

    实现通过ITSM工单,自动恢复历史索引数据。

    3.3 日志备份方案设计

    logbackup

    1. ES分层设计
    2. 冷节点层用于索引“备份”
    3. 通过CurveFS实现数据无缝迁移到对象存储

    3.4 日志恢复方案设计

    logrestore

    3.5 日志备份功能实现

    备份执行流程:

    logbackup

    1. 修改集群参数,更好的满足迁移速度
    2. 按照索引模式,循环迁移各个索引,并进行关闭
    3. 修改集群参数,切换回低速迁移模式

    功能实现:

    logbackup

    1. 新建索引备份策略,基于索引模式角度
    2. 新建备份迁移队列
    3. 备份迁移队列编排
    4. 队列定期执行

    四、实际效果

    4.1 日志备份功能

    4.1.1 迁移队列管理

    配置队列每日执行的时间点,关联的接口,以及是否启用;还可以查看历史执行记录:

    queue

    4.1.2 索引生命周期策略管理

    统一的生命周期策略管理,一目了然:

    lifecycle

    配置每个队列中,索引的迁移的顺序;队列之间是并行执行,队列中是串行执行。

    lifecycle

    另外,部分索引不需要长期存储,只需要通过配置迁移到温节点队列即可。

    灵活查看索引的整个生命周期阶段:

    lifecycle

    4.2 日志恢复功能

    4.2.1 用户工单流程配置

    ticket

    4.2.2 用户申请工单表单

    ticket

    五、方案总结

    • 统一管理: 对ES进行上层封装建设,减少对Kibana的依赖,对多集群、多版本ES可以进行统一管理;
    • 自主可控: 通过自研开发,更贴合实际场景;部分日志需要长期留存,转移到冷节点;部分不需要长期留存,在转移到温节点后,静待删除;
    • 更细粒度的控制: 队列配置了索引迁移开始时间,队列中的索引基于编排的顺序,串行开始迁移;
    • 并行控制: 基于队列的机制,实现备份迁移的并行。如果后端存储性能足够,可以多开几个队列,充分利用凌晨的空闲IO;
    • 统一的策略管理: 所有索引备份策略一目了然,方便全局掌控,快速调整;
    • 全生命周期管理: 索引从产生到最后删除,每一个阶段都留有记录,通过时间树的方式快速查看。

    带来的价值:

    • 灵活性,高效性: 都是从S3取数据,这个直接通过打开索引,就能恢复提供查询;
    • 流程数字化,自动化: 通过工单申请,能自动进行接口调用,实现索引恢复和定期关闭;
    • 优化日志数据存储分层,实现降本增效: 对本地SATA盘的依赖减少,依靠云存储的稳定性,ES本地节点存储容量为0,全部存储到S3;
    + + \ No newline at end of file diff --git a/CurveFS/usecase/s3-gateway.html b/CurveFS/usecase/s3-gateway.html index 159e7e1..3b8253d 100644 --- a/CurveFS/usecase/s3-gateway.html +++ b/CurveFS/usecase/s3-gateway.html @@ -4,13 +4,13 @@ CurveFS基于minio-s3-gateway的S3协议支持 | Curve Book - - + +
    -
    跳到主要内容

    CurveFS基于minio-s3-gateway的S3协议支持

    本文的部署方案是基于CurveFS挂载点+minio-s3-gateway方式,后续会考虑支持minio-s3-gateway+CurveFS SDK方式部署,将不再需要依赖CurveFS挂载点。

    需求背景

    AI训练业务场景下,业务希望通过S3接口上传训练所用到的数据集,实际训练过程中则想要使用CurveFS共享文件系统来实现多节点共享访问训练数据集,以及存储训练过程中产生的临时文件,并利用CurveFS为AI业务专门开发的文件/目录预热、内存缓存、本地盘缓存、分布式KV缓存特性,最终在低存储成本前提下的训练加速。

    简单来说就是想要实现S3协议和Posix协议的统一命名空间能力,两种协议上传/写入的文件可以互相访问。本文简述相关部署过程,供有类似需求的社区小伙伴们参考。

    部署前提

    1. 已经部署了一套CurveFS共享文件存储系统
    2. 已经挂载了一个CurveFS文件系统到服务器的/mnt/minio-data目录上
    3. 已安装docker运行环境

    部署CurveFS和挂载文件系统的操作步骤可以参考Curve官方部署工具CurveAdm的用户手册:

    部署步骤

    这里使用minio-s3-gateway服务作为S3网关,并用docker进行部署,相关参考资料:https://github.com/minio/minio/blob/RELEASE.2022-04-26T01-20-24Z/docs/gateway/nas.md

    $ docker run --privileged -p 9000:9000 \
    --name curvefs-minio-s3-gateway \
    -v /mnt/minio-data:/data \
    -e "MINIO_ROOT_USER=minio-access-key" \
    -e "MINIO_ROOT_PASSWORD=minio-secret-key" \
    -e "MINIO_REGION=us-east-1" \
    --console-address ":9001" \
    docker.io/minio/minio:RELEASE.2022-04-26T01-20-24Z \
    gateway nas /data

    执行上述命令就可以启动一个CurveFS的minio-s3-gateway服务,非常的简单方便。

    如果部署成功,会在屏幕上打印如下内容:

    API: http://10.88.0.18:9000  http://127.0.0.1:9000     

    Console: http://10.88.0.18:9001 http://127.0.0.1:9001

    Documentation: https://docs.min.io
    Finished loading IAM sub-system (took 0.0s of 0.0s to load data).
    ...... // 以下内容省略

    Console地址就是web控制台地址,可以在浏览器里直接打开访问,用户名密码是上面docker命令行里配置的环境变量MINIO_ROOT_USERMINIO_ROOT_PASSWORD对应的值,参考资料:https://min.io/docs/minio/linux/administration/minio-console.html

    功能验证

    接下来我们部署一个minio的客户端,来验证S3网关的可用性,以及S3网关与CurveFS文件系统挂载点是否可以做到统一命名空间下互相访问文件。

    部署minio客户端

    minio客户端我们也是用docker来部署:

    $ docker run -it --entrypoint=/bin/sh minio/mc:RELEASE.2022-04-26T18-00-22Z

    执行上述命令就可以启动一个minio命令行工具mc的运行环境了。

    修改mc命令行的配置文件,默认是/root/.mc/config.json(如果容器内缺少编辑器不方便修改可以在本地修改好之后用docker cp命令复制进去):

    {
    "version": "10",
    "aliases": {
    "curvefs": {
    "url": "http://10.88.0.18:9000",
    "accessKey": "minio-access-key",
    "secretKey": "minio-secret-key",
    "api": "S3v4",
    "path": "auto"
    }
    }
    }

    其中url就是部署minio-s3-gateway服务时屏幕打印的API地址,accessKey就是启动minio-s3-gateway容器的MINIO_ROOT_USERsecretKey就是MINIO_ROOT_PASSWORD,其他两个保持默认即可。curvefs是minio集群的别名,下面会用到。

    统一命名空间功能验证

    目标1:使用S3创建桶并上传文件,在CurveFS挂载点对应目录下访问

    使用mc命令行工具分别进行创建桶、上传文件、列出桶内文件操作:

    $ mc mb curvefs/bucket1
    Bucket created successfully `curvefs/bucket1`.
    $ mc cp anaconda-ks.cfg curvefs/bucket1/
    /root/anaconda-ks.cfg: 7.53 KiB / 7.53 KiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 777.03 KiB/s 0s
    $ mc ls curvefs/bucket1/
    [2023-04-25 09:16:37 UTC] 7.5KiB STANDARD anaconda-ks.cfg

    在CurveFS的挂载点目录下列出目录和文件,并校验文件md5值(校验md5步骤省略,已确认一致):

    $ ls /mnt/minio-data/
    bucket1
    $ ls /mnt/minio-data/bucket1/
    anaconda-ks.cfg

    目标2:使用S3上传包含子目录的文件到桶内,在CurveFS挂载点对应目录下访问

    $ mc cp curvefs/bucket1/dir1/bigfile.500M
    ...t1/dir1/bigfile.500M: 500.00 MiB / 500.00 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 628.02 MiB/s 0s

    在CurveFS的挂载点目录下列出目录和文件,并校验文件md5值(校验md5步骤省略,已确认一致):

    $ ls /mnt/minio-data/bucket1/dir1
    bigfile.500M

    目标3:在CurveFS挂载点创建根目录及目录下的文件,使用S3查看桶及桶内文件

    首先使用dd命令在CurveFS挂载点的bucket1目录下创建一个10M的文件:

    $ cd /mnt/minio-data/bucket1/dir1
    $ dd if=/dev/zero of=./newbigfile.10M bs=1M count=10

    之后用mc命令查看桶内文件并下载后校验md5值(校验md5步骤省略,已确认一致):

    $ mc ls curvefs/bucket1/dir1
    [2023-04-25 09:29:33 UTC] 10MiB STANDARD newbigfile.10M
    $ mc cp curvefs/bucket1/dir1/newbigfile.10M .
    /root/newbigfile.10M: 10.00 MiB / 10.00 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 76.14 MiB/s 0s

    补充说明

    高可用及单节点性能问题

    这里的操作步骤均是单节点模式,如果要做到s3网关的高可用及防止性能瓶颈,可以在多台服务器上重复上述步骤,并给S3网关服务部署负载均衡服务(如Nginx或haproxy等),只需要CurveFS挂载的是同一个文件系统即可。

    CurveAdm部署工具集成问题

    本次实践是一次功能验证,后续Curve社区将把相关功能集成到CurveAdm部署工具中,方便用户使用维护。

    minio gateway的废弃问题

    minio的s3 gateway服务已经于2020年初开始逐步废弃了,但老版本的仍然可以继续使用,只需要指定版本号即可正常部署。如果有特殊需求,也可以fork minio的代码仓库自行定制修改。

    参考资料:https://blog.min.io/deprecation-of-the-minio-gateway/

    - - +
    跳到主要内容

    CurveFS基于minio-s3-gateway的S3协议支持

    本文的部署方案是基于CurveFS挂载点+minio-s3-gateway方式,后续会考虑支持minio-s3-gateway+CurveFS SDK方式部署,将不再需要依赖CurveFS挂载点。

    需求背景

    AI训练业务场景下,业务希望通过S3接口上传训练所用到的数据集,实际训练过程中则想要使用CurveFS共享文件系统来实现多节点共享访问训练数据集,以及存储训练过程中产生的临时文件,并利用CurveFS为AI业务专门开发的文件/目录预热、内存缓存、本地盘缓存、分布式KV缓存特性,最终在低存储成本前提下的训练加速。

    简单来说就是想要实现S3协议和Posix协议的统一命名空间能力,两种协议上传/写入的文件可以互相访问。本文简述相关部署过程,供有类似需求的社区小伙伴们参考。

    部署前提

    1. 已经部署了一套CurveFS共享文件存储系统
    2. 已经挂载了一个CurveFS文件系统到服务器的/mnt/minio-data目录上
    3. 已安装docker运行环境

    部署CurveFS和挂载文件系统的操作步骤可以参考Curve官方部署工具CurveAdm的用户手册:

    部署步骤

    这里使用minio-s3-gateway服务作为S3网关,并用docker进行部署,相关参考资料:https://github.com/minio/minio/blob/RELEASE.2022-04-26T01-20-24Z/docs/gateway/nas.md

    $ docker run --privileged -p 9000:9000 \
    --name curvefs-minio-s3-gateway \
    -v /mnt/minio-data:/data \
    -e "MINIO_ROOT_USER=minio-access-key" \
    -e "MINIO_ROOT_PASSWORD=minio-secret-key" \
    -e "MINIO_REGION=us-east-1" \
    --console-address ":9001" \
    docker.io/minio/minio:RELEASE.2022-04-26T01-20-24Z \
    gateway nas /data

    执行上述命令就可以启动一个CurveFS的minio-s3-gateway服务,非常的简单方便。

    如果部署成功,会在屏幕上打印如下内容:

    API: http://10.88.0.18:9000  http://127.0.0.1:9000     

    Console: http://10.88.0.18:9001 http://127.0.0.1:9001

    Documentation: https://docs.min.io
    Finished loading IAM sub-system (took 0.0s of 0.0s to load data).
    ...... // 以下内容省略

    Console地址就是web控制台地址,可以在浏览器里直接打开访问,用户名密码是上面docker命令行里配置的环境变量MINIO_ROOT_USERMINIO_ROOT_PASSWORD对应的值,参考资料:https://min.io/docs/minio/linux/administration/minio-console.html

    功能验证

    接下来我们部署一个minio的客户端,来验证S3网关的可用性,以及S3网关与CurveFS文件系统挂载点是否可以做到统一命名空间下互相访问文件。

    部署minio客户端

    minio客户端我们也是用docker来部署:

    $ docker run -it --entrypoint=/bin/sh minio/mc:RELEASE.2022-04-26T18-00-22Z

    执行上述命令就可以启动一个minio命令行工具mc的运行环境了。

    修改mc命令行的配置文件,默认是/root/.mc/config.json(如果容器内缺少编辑器不方便修改可以在本地修改好之后用docker cp命令复制进去):

    {
    "version": "10",
    "aliases": {
    "curvefs": {
    "url": "http://10.88.0.18:9000",
    "accessKey": "minio-access-key",
    "secretKey": "minio-secret-key",
    "api": "S3v4",
    "path": "auto"
    }
    }
    }

    其中url就是部署minio-s3-gateway服务时屏幕打印的API地址,accessKey就是启动minio-s3-gateway容器的MINIO_ROOT_USERsecretKey就是MINIO_ROOT_PASSWORD,其他两个保持默认即可。curvefs是minio集群的别名,下面会用到。

    统一命名空间功能验证

    目标1:使用S3创建桶并上传文件,在CurveFS挂载点对应目录下访问

    使用mc命令行工具分别进行创建桶、上传文件、列出桶内文件操作:

    $ mc mb curvefs/bucket1
    Bucket created successfully `curvefs/bucket1`.
    $ mc cp anaconda-ks.cfg curvefs/bucket1/
    /root/anaconda-ks.cfg: 7.53 KiB / 7.53 KiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 777.03 KiB/s 0s
    $ mc ls curvefs/bucket1/
    [2023-04-25 09:16:37 UTC] 7.5KiB STANDARD anaconda-ks.cfg

    在CurveFS的挂载点目录下列出目录和文件,并校验文件md5值(校验md5步骤省略,已确认一致):

    $ ls /mnt/minio-data/
    bucket1
    $ ls /mnt/minio-data/bucket1/
    anaconda-ks.cfg

    目标2:使用S3上传包含子目录的文件到桶内,在CurveFS挂载点对应目录下访问

    $ mc cp curvefs/bucket1/dir1/bigfile.500M
    ...t1/dir1/bigfile.500M: 500.00 MiB / 500.00 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 628.02 MiB/s 0s

    在CurveFS的挂载点目录下列出目录和文件,并校验文件md5值(校验md5步骤省略,已确认一致):

    $ ls /mnt/minio-data/bucket1/dir1
    bigfile.500M

    目标3:在CurveFS挂载点创建根目录及目录下的文件,使用S3查看桶及桶内文件

    首先使用dd命令在CurveFS挂载点的bucket1目录下创建一个10M的文件:

    $ cd /mnt/minio-data/bucket1/dir1
    $ dd if=/dev/zero of=./newbigfile.10M bs=1M count=10

    之后用mc命令查看桶内文件并下载后校验md5值(校验md5步骤省略,已确认一致):

    $ mc ls curvefs/bucket1/dir1
    [2023-04-25 09:29:33 UTC] 10MiB STANDARD newbigfile.10M
    $ mc cp curvefs/bucket1/dir1/newbigfile.10M .
    /root/newbigfile.10M: 10.00 MiB / 10.00 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 76.14 MiB/s 0s

    补充说明

    高可用及单节点性能问题

    这里的操作步骤均是单节点模式,如果要做到s3网关的高可用及防止性能瓶颈,可以在多台服务器上重复上述步骤,并给S3网关服务部署负载均衡服务(如Nginx或haproxy等),只需要CurveFS挂载的是同一个文件系统即可。

    CurveAdm部署工具集成问题

    本次实践是一次功能验证,后续Curve社区将把相关功能集成到CurveAdm部署工具中,方便用户使用维护。

    minio gateway的废弃问题

    minio的s3 gateway服务已经于2020年初开始逐步废弃了,但老版本的仍然可以继续使用,只需要指定版本号即可正常部署。如果有特殊需求,也可以fork minio的代码仓库自行定制修改。

    参考资料:https://blog.min.io/deprecation-of-the-minio-gateway/

    + + \ No newline at end of file diff --git a/CurveFS/usecase/scenario.html b/CurveFS/usecase/scenario.html index 17db461..763f8e6 100644 --- a/CurveFS/usecase/scenario.html +++ b/CurveFS/usecase/scenario.html @@ -4,13 +4,13 @@ 应用场景 | Curve Book - - + +
    -
    跳到主要内容

    应用场景

    CurveFS的核心应用场景主要包括:

    • AI训练(含机器学习等)场景下的高性价比存储
    • 大数据场景下(ElasticSearch、ClickHouse等)冷数据存储实现降本提效
    • 可替换HDFS的大数据高可扩展、低成本的存算分离存储
    • 公有云上高性价比的共享文件存储:可用于AI、大数据、文件共享等业务场景
    • 混合云存储:热数据存储在本地IDC,冷数据存储在公有云
    • S3协议和POSIX文件协议兼容:可通过S3 gateway实现POSIX协议和S3协议的互通,统一命名空间访问
    • 通用共享文件存储:如替换sshfs、s3fs、NFS、SMB、FTP等存储系统或协议,用来存储数据库备份、保存gitlab仓库、实现文件共享、替换本地文件系统等用途
    - - +
    跳到主要内容

    应用场景

    CurveFS的核心应用场景主要包括:

    • AI训练(含机器学习等)场景下的高性价比存储
    • 大数据场景下(ElasticSearch、ClickHouse等)冷数据存储实现降本提效
    • 可替换HDFS的大数据高可扩展、低成本的存算分离存储
    • 公有云上高性价比的共享文件存储:可用于AI、大数据、文件共享等业务场景
    • 混合云存储:热数据存储在本地IDC,冷数据存储在公有云
    • S3协议和POSIX文件协议兼容:可通过S3 gateway实现POSIX协议和S3协议的互通,统一命名空间访问
    • 通用共享文件存储:如替换sshfs、s3fs、NFS、SMB、FTP等存储系统或协议,用来存储数据库备份、保存gitlab仓库、实现文件共享、替换本地文件系统等用途
    + + \ No newline at end of file diff --git a/CurveFS/usecase/smb-support.html b/CurveFS/usecase/smb-support.html index 62a151d..1d8e5e3 100644 --- a/CurveFS/usecase/smb-support.html +++ b/CurveFS/usecase/smb-support.html @@ -4,13 +4,13 @@ 基于CurveFS+Samba支持SMB协议 | Curve Book - - + +
    -
    跳到主要内容

    基于CurveFS+Samba支持SMB协议

    本文将会介绍CurveFS与samba以及如何将他们结合使用, 并且介绍samba的第三方moudle开发知识。

    本文基于CurveFS挂载点+Samba server方式部署,新的Samba+CurveFS SDK方式后续会发布,SDK将于2.7版本开发完成并发布。

    CurveFS介绍

    Curve共享文件存储(简称CurveFS)是Curve社区开发的新一代分布式共享文件存储系统,在网易内部已有PB级规模应用,其核心架构可以参考设计文档:https://github.com/opencurve/curve/blob/master/docs/cn/curvefs_architecture.md

    相比传统的分布式文件系统,CurveFS具备如下几个特色:

    1. 针对AI训练/机器学习场景、elasticsearch/clickhouse/starrocks等大数据场景做了专门优化,支持内存缓存、本地磁盘缓存、分布式内存缓存(基于memcached或其他kv存储)等多级缓存,可对文件读写进行缓存加速并保持数据写入可靠性,支持数据预热到缓存中加速读取过程,大数据场景下使用CurveFS+对象存储相比本地副本存储方案具备更高的性价比优势以及易用性优势
    2. 基于multi-raft支持元数据的分区存储,具备无限扩展性,性能可随节点数线性扩展,不存在性能热点问题
    3. 支持将文件数据存储到对象存储和CurveBS块存储上,可以做到冷热数据分层存储和生命周期管理(roadmap),数据容量可无限扩展

    Samba介绍

    homepage: https://www.samba.org/

    Samba是目前最流行的文件和打印机共享软件之一. 得益于它良好的兼容性, 可以轻松地跨平台(Windows/Linux/MacOS), 以及灵活的可定制性和可配置性, 受到了各个系统的广泛支持.

    Samba的特性非常多, 其中对于第三方开发者来说比较重要的就是Samba VFS Module特性. 其让开发者可以自由地实现一些功能, 接下来我们的话题会围绕着Samba的基础设定以及如何为Samba添加新的VFS Module.

    本文使用的Samba版本为4.10.16, 系统为CentOS 7.

    如何配置Samba使用CurveFS

    配置CurveFS

    部署CurveFS

    通过CurveAdm进行CurveFS后端服务的部署, 参考以下文档

    https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment

    创建一个fs并挂载

    通过CurveAdm进行CurveFS的挂载, 参考以下文档, 如果fs不存在的话, 会自动进行创建

    https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment

    安装配置samba服务

    sudo yum update -y
    sudo yum install samba -y

    修改/etc/samba/smb.conf, 以下是一个example, 更多的配置请参阅man smb.conf

    [global]
    workgroup = SAMBA
    log level = 0
    max log size = 0
    security = user
    passdb backend = tdbsam
    create mask = 0700
    directory mask = 0700
    writable = yes
    public = no
    guest ok = no

    [curvefs]
    path = /mnt/curvefs # 需要确保samba用户有访问这个目录的权限

    配置user的密码, user需要是本地已有的linux用户, 这里以root举例

    sudo smbpasswd -a root

    启动samba server服务

    sudo systemctl enable --now smb

    然后就可以通过samba的client(windows/linux/macos)来连接访问

    Samba VFS Module介绍

    参考文档: https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module

    Samba VFS Module是Samba VFS提供的一种功能扩展机制, 就像Extention之于Chomre, 非常重要.

    1. Samba VFS提供了一系列的接口定义, 具体可查看结构体struct vfs_fn_pointers, 也是使用了c语言比较常见的函数指针的方式来实现调用module的实现.

    2. 每一个module都是一个Shared Library (.so), 它包含了对上面定义的部分或全部接口的实现, 通过修改配置文件指定module来进行调用, Samba会在client连接的时候加载so.

    3. 多个VFS module可以stackable使用, 也就是如果多个module的实现不冲突, 可以修改配置, 按照顺序依次调用多个module的实现.

    module常见类型

    module用于支持CurveFS

    CurveFS的SDK将在2.7版本发布(预计在11月前),敬请期待。

    默认的samba vfs最终会调用vfs system call来对接文件系统.

    比如说你想要对接CurveFS, 那么可能是需要先把CurveFS挂载到本机上, 然后再通过vfs system call来对接samba, 相当于多了一层中间system call的开销。

    如果你想避免中间system call的调用, 只需要开发一个直接对接CurveFS sdk的module, 来当作stackable调用链的最末端, 替代default的实现即可. 就可以实现不挂载CurveFS, 直接将CurveFS通过samba协议分享出去的效果。

    已有的实现:

    module用于扩充已有的fs的功能集

    在已存在一个fs后端实现的情况下, (默认的是调用vfs system call), 对部分接口进行功能的增强.

    module作为stackable调用链的中间一环, 完成本身module的功能后可以继续往后调用.

    比如说fruit模块就提供了对Apple SMB clients更好的兼容性支持.

    https://www.samba.org/samba/docs/current/man-html/vfs_fruit.8.html

    开始开发你的第一个Samba VFS Moudle

    一个module包含什么?

    现有的module都位于此目录下

    https://github.com/samba-team/samba/tree/samba-4.10.16/source3/modules

    一个module由代码实现和对应的wscript构建配置组成, samba使用WAF来构建

    我们将以一个名为demo的vfs module举例, 我们会有如下变动

    • 代码实现: source3/wscript b/source3/wscript/vfs_demo.c

    • 改动source3/wscript b/source3/wscript和source3/modules/wscript_build增加构建配置

    module代码实现

    模块的初始化以及生命周期

    包括几个部分

    • 模块的注册: 名称
    • connect接口: 响应启用了此module的client的连接请求, 模块的初始化应该在此处完成, 包括自定义的context的构造等
    • disconnect接口: 响应启用了此module的client的disconnect请求, 模块的退出清理应该在此处完成
    • 如果connect和disconnect有改动的需求, 那么实现自己自定义的方法即可.

    从connect到disconnect为一个module的生命周期.

    下面的代码是一个简单的范例:

    // vfs_demo.c

    // initialzation
    NTSTATUS vfs_demo_init(TALLOC_CTX *ctx) {
    return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "demo",
    &vfs_demo_fns); // vfs_demo_fns是接口实现函数指针的struct
    }

    /*
    static struct vfs_fn_pointers vfs_cloudnas_quota_fns = {
    .connect_fn = demo_connect,
    .disconnect_fn = demo_disconnect,
    ... 还有别的fn
    }
    */

    // context
    struct demo_ctx {
    uint64_t whoami;
    }

    // connect
    static int demo_connect(vfs_handle_struct *handle,
    const char *service, const char *user) {
    int res = 0;
    struct demo_ctx *ctx = NULL;
    /*
    * Allow the next module to handle connections first
    * If we get an error, don't do any of our initialization.
    */
    res = SMB_VFS_NEXT_CONNECT(handle, service, user);
    if (res) {
    return res;
    }

    ctx = talloc_zero(handle, struct demo_ctx);
    if (!ctx) {
    DEBUG(1, ("talloc_zero() failed\n"));
    errno = ENOMEM;
    return -1;
    }
    ctx->whoami = lp_parm_ulonglong(SNUM(handle->conn),
    "demo",
    "whoami",
    0);
    SMB_VFS_HANDLE_SET_DATA(handle, ctx, NULL,
    struct demo_ctx, return -1);
    return res;
    }

    // disconnect
    static void demo_disconnect(vfs_handle_struct *handle) {
    struct demo_ctx *ctx;

    SMB_VFS_NEXT_DISCONNECT(handle);

    SMB_VFS_HANDLE_GET_DATA(handle, ctx, struct demo_ctx,
    return -1);
    DEBUG(1, ("%llu disconnect...", ctx->whoami));
    }

    编码需要了解的常用知识
    1. 如果你的代码不是整条stackable调用链上的最末端, 记得使用SMB_VFS_NEXT_XXX系列函数来维持调用链, 比如说SMB_VFS_NEXT_CONNECT和SMB_VFS_NEXT_DISCONNECT
    2. conf中的模块自定义参数可以通过lp_parm_xxx系列函数来进行解析
    3. 自定义的ctx结构体可以通过SMB_VFS_HANDLE_SET_DATA和SMB_VFS_HANDLE_GET_DATA设定与解析
    4. share的路径存储在handle->conn->connectpath中

    使用C ABI兼容的代码

    C是一个历史久远的底层语言, 基于C的开发经常会遇到缺少一些数据结构、一些方便的软件库的情况.

    基于更现代的语言的开发往往更具有便利性, 比如go, rust等等.

    为了结合两者的优点, 我们可以使用在代码里include对应头文件和链接对应的so的方式进行开发.

    需要注意的问题

    编译与安装

    参考https://wiki.samba.org/index.php/Build_Samba_from_Source

    简而言之

    ./configure
    make
    sudo make install
    - - +
    跳到主要内容

    基于CurveFS+Samba支持SMB协议

    本文将会介绍CurveFS与samba以及如何将他们结合使用, 并且介绍samba的第三方moudle开发知识。

    本文基于CurveFS挂载点+Samba server方式部署,新的Samba+CurveFS SDK方式后续会发布,SDK将于2.7版本开发完成并发布。

    CurveFS介绍

    Curve共享文件存储(简称CurveFS)是Curve社区开发的新一代分布式共享文件存储系统,在网易内部已有PB级规模应用,其核心架构可以参考设计文档:https://github.com/opencurve/curve/blob/master/docs/cn/curvefs_architecture.md

    相比传统的分布式文件系统,CurveFS具备如下几个特色:

    1. 针对AI训练/机器学习场景、elasticsearch/clickhouse/starrocks等大数据场景做了专门优化,支持内存缓存、本地磁盘缓存、分布式内存缓存(基于memcached或其他kv存储)等多级缓存,可对文件读写进行缓存加速并保持数据写入可靠性,支持数据预热到缓存中加速读取过程,大数据场景下使用CurveFS+对象存储相比本地副本存储方案具备更高的性价比优势以及易用性优势
    2. 基于multi-raft支持元数据的分区存储,具备无限扩展性,性能可随节点数线性扩展,不存在性能热点问题
    3. 支持将文件数据存储到对象存储和CurveBS块存储上,可以做到冷热数据分层存储和生命周期管理(roadmap),数据容量可无限扩展

    Samba介绍

    homepage: https://www.samba.org/

    Samba是目前最流行的文件和打印机共享软件之一. 得益于它良好的兼容性, 可以轻松地跨平台(Windows/Linux/MacOS), 以及灵活的可定制性和可配置性, 受到了各个系统的广泛支持.

    Samba的特性非常多, 其中对于第三方开发者来说比较重要的就是Samba VFS Module特性. 其让开发者可以自由地实现一些功能, 接下来我们的话题会围绕着Samba的基础设定以及如何为Samba添加新的VFS Module.

    本文使用的Samba版本为4.10.16, 系统为CentOS 7.

    如何配置Samba使用CurveFS

    配置CurveFS

    部署CurveFS

    通过CurveAdm进行CurveFS后端服务的部署, 参考以下文档

    https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment

    创建一个fs并挂载

    通过CurveAdm进行CurveFS的挂载, 参考以下文档, 如果fs不存在的话, 会自动进行创建

    https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment

    安装配置samba服务

    sudo yum update -y
    sudo yum install samba -y

    修改/etc/samba/smb.conf, 以下是一个example, 更多的配置请参阅man smb.conf

    [global]
    workgroup = SAMBA
    log level = 0
    max log size = 0
    security = user
    passdb backend = tdbsam
    create mask = 0700
    directory mask = 0700
    writable = yes
    public = no
    guest ok = no

    [curvefs]
    path = /mnt/curvefs # 需要确保samba用户有访问这个目录的权限

    配置user的密码, user需要是本地已有的linux用户, 这里以root举例

    sudo smbpasswd -a root

    启动samba server服务

    sudo systemctl enable --now smb

    然后就可以通过samba的client(windows/linux/macos)来连接访问

    Samba VFS Module介绍

    参考文档: https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module

    Samba VFS Module是Samba VFS提供的一种功能扩展机制, 就像Extention之于Chomre, 非常重要.

    1. Samba VFS提供了一系列的接口定义, 具体可查看结构体struct vfs_fn_pointers, 也是使用了c语言比较常见的函数指针的方式来实现调用module的实现.

    2. 每一个module都是一个Shared Library (.so), 它包含了对上面定义的部分或全部接口的实现, 通过修改配置文件指定module来进行调用, Samba会在client连接的时候加载so.

    3. 多个VFS module可以stackable使用, 也就是如果多个module的实现不冲突, 可以修改配置, 按照顺序依次调用多个module的实现.

    module常见类型

    module用于支持CurveFS

    CurveFS的SDK将在2.7版本发布(预计在11月前),敬请期待。

    默认的samba vfs最终会调用vfs system call来对接文件系统.

    比如说你想要对接CurveFS, 那么可能是需要先把CurveFS挂载到本机上, 然后再通过vfs system call来对接samba, 相当于多了一层中间system call的开销。

    如果你想避免中间system call的调用, 只需要开发一个直接对接CurveFS sdk的module, 来当作stackable调用链的最末端, 替代default的实现即可. 就可以实现不挂载CurveFS, 直接将CurveFS通过samba协议分享出去的效果。

    已有的实现:

    module用于扩充已有的fs的功能集

    在已存在一个fs后端实现的情况下, (默认的是调用vfs system call), 对部分接口进行功能的增强.

    module作为stackable调用链的中间一环, 完成本身module的功能后可以继续往后调用.

    比如说fruit模块就提供了对Apple SMB clients更好的兼容性支持.

    https://www.samba.org/samba/docs/current/man-html/vfs_fruit.8.html

    开始开发你的第一个Samba VFS Moudle

    一个module包含什么?

    现有的module都位于此目录下

    https://github.com/samba-team/samba/tree/samba-4.10.16/source3/modules

    一个module由代码实现和对应的wscript构建配置组成, samba使用WAF来构建

    我们将以一个名为demo的vfs module举例, 我们会有如下变动

    • 代码实现: source3/wscript b/source3/wscript/vfs_demo.c

    • 改动source3/wscript b/source3/wscript和source3/modules/wscript_build增加构建配置

    module代码实现

    模块的初始化以及生命周期

    包括几个部分

    • 模块的注册: 名称
    • connect接口: 响应启用了此module的client的连接请求, 模块的初始化应该在此处完成, 包括自定义的context的构造等
    • disconnect接口: 响应启用了此module的client的disconnect请求, 模块的退出清理应该在此处完成
    • 如果connect和disconnect有改动的需求, 那么实现自己自定义的方法即可.

    从connect到disconnect为一个module的生命周期.

    下面的代码是一个简单的范例:

    // vfs_demo.c

    // initialzation
    NTSTATUS vfs_demo_init(TALLOC_CTX *ctx) {
    return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "demo",
    &vfs_demo_fns); // vfs_demo_fns是接口实现函数指针的struct
    }

    /*
    static struct vfs_fn_pointers vfs_cloudnas_quota_fns = {
    .connect_fn = demo_connect,
    .disconnect_fn = demo_disconnect,
    ... 还有别的fn
    }
    */

    // context
    struct demo_ctx {
    uint64_t whoami;
    }

    // connect
    static int demo_connect(vfs_handle_struct *handle,
    const char *service, const char *user) {
    int res = 0;
    struct demo_ctx *ctx = NULL;
    /*
    * Allow the next module to handle connections first
    * If we get an error, don't do any of our initialization.
    */
    res = SMB_VFS_NEXT_CONNECT(handle, service, user);
    if (res) {
    return res;
    }

    ctx = talloc_zero(handle, struct demo_ctx);
    if (!ctx) {
    DEBUG(1, ("talloc_zero() failed\n"));
    errno = ENOMEM;
    return -1;
    }
    ctx->whoami = lp_parm_ulonglong(SNUM(handle->conn),
    "demo",
    "whoami",
    0);
    SMB_VFS_HANDLE_SET_DATA(handle, ctx, NULL,
    struct demo_ctx, return -1);
    return res;
    }

    // disconnect
    static void demo_disconnect(vfs_handle_struct *handle) {
    struct demo_ctx *ctx;

    SMB_VFS_NEXT_DISCONNECT(handle);

    SMB_VFS_HANDLE_GET_DATA(handle, ctx, struct demo_ctx,
    return -1);
    DEBUG(1, ("%llu disconnect...", ctx->whoami));
    }

    编码需要了解的常用知识
    1. 如果你的代码不是整条stackable调用链上的最末端, 记得使用SMB_VFS_NEXT_XXX系列函数来维持调用链, 比如说SMB_VFS_NEXT_CONNECT和SMB_VFS_NEXT_DISCONNECT
    2. conf中的模块自定义参数可以通过lp_parm_xxx系列函数来进行解析
    3. 自定义的ctx结构体可以通过SMB_VFS_HANDLE_SET_DATA和SMB_VFS_HANDLE_GET_DATA设定与解析
    4. share的路径存储在handle->conn->connectpath中

    使用C ABI兼容的代码

    C是一个历史久远的底层语言, 基于C的开发经常会遇到缺少一些数据结构、一些方便的软件库的情况.

    基于更现代的语言的开发往往更具有便利性, 比如go, rust等等.

    为了结合两者的优点, 我们可以使用在代码里include对应头文件和链接对应的so的方式进行开发.

    需要注意的问题

    编译与安装

    参考https://wiki.samba.org/index.php/Build_Samba_from_Source

    简而言之

    ./configure
    make
    sudo make install
    + + \ No newline at end of file diff --git a/Develop/build-and-test.html b/Develop/build-and-test.html index d9ea807..521806a 100644 --- a/Develop/build-and-test.html +++ b/Develop/build-and-test.html @@ -4,8 +4,8 @@ 编译和测试 | Curve Book - - + +
    @@ -14,7 +14,7 @@ 4.2.2 为推荐版本。

    安装依赖

    编译相关的软件依赖可以参考 dockerfile 中的安装步骤。

    一键编译

    git clone https://github.com/opencurve/curve.git 或者 git clone https://gitee.com/mirrors/curve.git
    # (中国大陆可选)将外部依赖替换为国内下载点或镜像仓库,可以加快下载速度: bash replace-curve-repo.sh
    # curve v2.0 之前
    bash mk-tar.sh (编译 curvebs 并打tar包)
    bash mk-deb.sh (编译 curvebs 并打debian包)

    # (当前)curve v2.0 及之后
    # 编译 tar dep 包
    make tar dep=1 (编译 curvebs 并打tar包)
    make deb dep=1 (编译 curvebs 并打debian包)
    # 编译 curvebs:
    make ci-build stor=bs dep=1
    # or
    make ci-dep stor=bs && make ci-build stor=bs
    # 编译 curvefs:
    make ci-build stor=fs dep=1
    # or
    make ci-dep stor=fs && make ci-build stor=fs

    制作镜像

    该步骤可以在容器内执行也可以在物理机上执行。 注意若是在容器内执行,需要在执行 docker run 命令时添加 -v /var/run/docker.sock:/var/run/docker.sock -v /root/.docker:/root/.docker 参数。

    # 编译 curvebs:
    # 后面的tag参数可以自定义,用于上传到镜像仓库
    make image stor=bs tag=test
    # 编译 curvefs:
    make image stor=fs tag=test

    上传镜像

    # test 为上一步中的tag参数
    docker push test

    测试

    docker 容器中测试

    docker 镜像 opencurvedocker/curve-base:build-debian11 中已经安装了测试中所有的依赖,可以直接在容器中进行测试。

    运行以下命令会拉起一个容器并在容器中执行全部 curvebs 相关的 ci 测试:

    bash ut.sh curvebs

    同样的,你也可以使用命令 make docker 进入到容器中,然后在容器中执行测试相关的命令(比如你对某些测试用例单独进行测试时)。

    与编译一样你也可以使用 make docker 命令进入到容器中,然后在容器中执行测试命令:

    make docker
    bash util/ut_in_image.sh curvebs

    注意 脚本 util/ut_in_image.sh 会运行 minio,如果中途失败,下次运行时需要手动终止 minio 的运行。

    在物理机上测试

    相关的软件可以参考 dockerfile 来安装依赖。 使用以下命令在物理机上运行全部 curvebs 相关的 ci 测试:

    bash util/ut_in_image.sh curvebs

    测试用例编译及执行

    编译全部模块

    仅编译全部模块,不进行打包

    bash ./build.sh

    列出所有测试模块

    # curvebs
    bazel query '//test/...'
    # curvefs
    bazel query '//curvefs/test/...'

    编译对应模块的代码

    编译对应模块,例如test/common目录下的common-test测试:

    bazel build test/common:common-test --copt -DHAVE_ZLIB=1 --define=with_glog=true --compilation_mode=dbg --define=libunwind=true

    执行测试

    执行测试前需要先准备好测试用例运行所需的依赖:

    运行单元测试:

    执行单个测试模块

    ./bazel-bin/test/common/common-test

    运行单元/集成测试

    bazel 编译后的可执行程序都在 ./bazel-bin 目录下,例如 test/common 目录下的测试代码对应的测试程序为 ./bazel-bin/test/common/common-test,可以直接运行程序进行测试。

    如果想运行所有的单元测试和集成测试,可以参考 docker 容器中测试在物理机上测试

    - - + + \ No newline at end of file diff --git a/Develop/code-walkthrough.html b/Develop/code-walkthrough.html index 6307b2f..eda864a 100644 --- a/Develop/code-walkthrough.html +++ b/Develop/code-walkthrough.html @@ -4,13 +4,13 @@ 代码走读 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/Develop/how-to-contribute.html b/Develop/how-to-contribute.html index 87d1414..6097acb 100644 --- a/Develop/how-to-contribute.html +++ b/Develop/how-to-contribute.html @@ -4,13 +4,13 @@ 参与贡献 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/Release/release-intro.html b/Release/release-intro.html index aea8e35..31a4269 100644 --- a/Release/release-intro.html +++ b/Release/release-intro.html @@ -4,13 +4,13 @@ 版本发布周期 | Curve Book - - + +
    跳到主要内容

    版本发布周期

    • CURVE版本发布周期:大版本半年,小版本1~2个月
    • 版本号规则:采用3段式版本号,x.y.z{-后缀},x是大版本,y是小版本,z是bugfix,后缀用来区beta版本(-beta)、rc版本(-rc)、和稳定版本(没有后缀)。每半年的大版本是指x增加1,每1~2个月的小版本是y增加1。正式版本发布之后,如果有bugfix是z增加1。

    分支规则

    所有的开发都在master分支开发,如果需要发布版本,从master拉取新的分支release-x.y。版本发布从release-x.y分支发布。

    - - + + \ No newline at end of file diff --git a/Release/release-notes-v1.5.html b/Release/release-notes-v1.5.html index 24dd8e4..940b4b9 100644 --- a/Release/release-notes-v1.5.html +++ b/Release/release-notes-v1.5.html @@ -4,13 +4,13 @@ CHANGELOG of v1.5 | Curve Book - - + +
    跳到主要内容

    CHANGELOG of v1.5

    Features

    Local Snapshot

    The related functions provided by local snapshots are as follows:

    • Create snapshot
    • Delete snapshot
    • List snapshots
    • Protect snapshot
    • Unprotect snapshot

    Local Clone

    The relevant functions provided by local cloning are as follows:

    • Clone, that is, clone a subvolume from a local snapshot
    • Flatten, which is to complement the data of the child volume and decouple it from the parent volume and snapshot.
    • children, that is, the subvolumes cloned by querying the snapshot of the parent volume or the subvolumes cloned by querying the snapshot.

    Tools-v2 support for local snapshot and local clone

    The tools-v2 tool provides a local snapshot command line tool to execute the above-mentioned local snapshot and clone command lines.

    For specific instructions on how to use the tool commands, see the documentation:

    localsnapshotclone_tools_api

    RESTful API interface support for local snapshot and local clone

    SnapshotCloneServer provides a RESTful API interface, which provides an interface in the form of RESTful API to perform the above-mentioned local snapshot and cloning functions. In addition, RESTful API interfaces for creating volumes, deleting volumes, and listing volumes are provided.

    For specific restfulapi functions, see the documentation:

    localsnapshotclone_restful_api

    The current version's compatibility with the old s3 snapshot-related API functions has not yet been implemented. It is expected to be compatible with the old API in the next version.

    spdk tgt server that supports caching

    In particular, the release 1.5 version also supports exporting iscsi volumes through the spdk tgt function. By docking the spdk tgt module, curveadm can create a curve target node that supports caching and mount it to multiple platforms such as windows through iscsi (this part requires curveadm Specific versions support).

    - - + + \ No newline at end of file diff --git a/Release/release-notes-v2.6.html b/Release/release-notes-v2.6.html index 778181e..9d76b0b 100644 --- a/Release/release-notes-v2.6.html +++ b/Release/release-notes-v2.6.html @@ -4,13 +4,13 @@ CHANGELOG of v2.6 | Curve Book - - + +
    跳到主要内容

    CHANGELOG of v2.6

    Previous change logs can be found at CHANGELOG-2.5

    Features

    Improve

    Bugfix

    Performance

    Hardware

    3 nodes (3mds, 9metaserver), each with:

    • Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
    • 256G RAM
    • disk cache: INTEL SSDSC2BB80 800G (IOPS is about 30000+, bandwidth is about 300 MiB)

    Configure

    fs.cto: true
    fs.lookupCache.negativeTimeoutSec: 1
    fs.lookupCache.minUses: 3
    fuseClient.supportKVcache: true
    client.loglevel: 0

    fio

    [global]
    rw=randread
    direct=1
    size=50G
    iodepth=128
    ioengine=libaio
    bsrange=4k-4k
    ramp_time=10
    runtime=300
    group_reporting

    [disk01]
    filename=/path/to/mountpoint/1.txt
    fioIOPS/bandwidthavg-latency(ms)clat 99.00th (ms)clat 99.99th (ms)
    numjobs=1 / size=50GB / 4k randwrite42430.230.1762
    numjobs=1 / size=50GB / 4k randwrite9081.03.5104
    numjobs=1 / size=50GB / 512k write412 MiB/s2.419566
    numjobs=1 / size=50GB / 512k read333 MiB/s2.920115

    mdtest

    for i in 1 4 8; do mpirun --allow-run-as-root -np $i mdtest -z 2 -b 3 -I 10000 -d /path/to/mountpoint; done
    CaseDir creationDir statDir removalFile creationFile statFile readFile removalTree creationTree removal
    client*13413959912913343838443694309322851
    client*438512326628836115155921505631036316
    client*8415221383144002811416209763473558
    - - + + \ No newline at end of file diff --git a/Roadmap/roadmap-2023.html b/Roadmap/roadmap-2023.html index 47e5bf6..1843ff1 100644 --- a/Roadmap/roadmap-2023.html +++ b/Roadmap/roadmap-2023.html @@ -4,13 +4,13 @@ Curve Roadmap 2023 | Curve Book - - + +
    跳到主要内容
    - - + + \ No newline at end of file diff --git a/assets/images/curvefs-arch-v2-04d6725115ee4a33aae09c8ab0af7201.webp b/assets/images/curvefs-arch-v2-04d6725115ee4a33aae09c8ab0af7201.webp deleted file mode 100644 index 175789cb95cdf02bd9ec23816f04697e5e7fd1ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44598 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%0D;av#?En9pKxO!w7%AMt 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η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 TDotW9rhXPg#Z?in5ZC|!LXnd) diff --git a/assets/images/curvefs-meta-arch-24bc47886dd26fa64f61d477c3e04051.webp b/assets/images/curvefs-meta-arch-24bc47886dd26fa64f61d477c3e04051.webp deleted file mode 100644 index 6461ddd8199e3efadd81c057014cf5e34ac42592..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45078 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 TI$zd6Bk>`d4F24|k+=W=_Ro;M diff --git a/assets/js/561d73c1.53e3c897.js b/assets/js/561d73c1.53e3c897.js deleted file mode 100644 index f1c41c2..0000000 --- a/assets/js/561d73c1.53e3c897.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[8877],{3905:(e,r,t)=>{t.d(r,{Zo:()=>p,kt:()=>f});var n=t(7294);function a(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function l(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function u(e){for(var r=1;r=0||(a[t]=e[t]);return a}(e,r);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var o=n.createContext({}),c=function(e){var r=n.useContext(o),t=r;return e&&(t="function"==typeof e?e(r):u(u({},r),e)),t},p=function(e){var r=c(e.components);return n.createElement(o.Provider,{value:r},e.children)},s="mdxType",v={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},m=n.forwardRef((function(e,r){var t=e.components,a=e.mdxType,l=e.originalType,o=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),s=c(t),m=a,f=s["".concat(o,".").concat(m)]||s[m]||v[m]||l;return t?n.createElement(f,u(u({ref:r},p),{},{components:t})):n.createElement(f,u({ref:r},p))}));function f(e,r){var t=arguments,a=r&&r.mdxType;if("string"==typeof e||a){var l=t.length,u=new Array(l);u[0]=m;var i={};for(var o in r)hasOwnProperty.call(r,o)&&(i[o]=r[o]);i.originalType=e,i[s]="string"==typeof e?e:a,u[1]=i;for(var c=2;c{t.r(r),t.d(r,{assets:()=>o,contentTitle:()=>u,default:()=>v,frontMatter:()=>l,metadata:()=>i,toc:()=>c});var n=t(7462),a=(t(7294),t(3905));const l={},u="CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",i={unversionedId:"CurveFS/usecase/asr-storage",id:"CurveFS/usecase/asr-storage",title:"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",description:"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",source:"@site/docs/03-CurveFS/01-usecase/08-asr-storage.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/asr-storage",permalink:"/CurveFS/usecase/asr-storage",draft:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5",permalink:"/CurveFS/usecase/ai-storage"},next:{title:"\u90e8\u7f72",permalink:"/category/\u90e8\u7f72-1"}},o={},c=[{value:"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",id:"\u4e91\u5546-asr-\u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",level:2},{value:"CurveFS",id:"curvefs",level:2},{value:"CurveFS\u662f\u4ec0\u4e48",id:"curvefs\u662f\u4ec0\u4e48",level:3},{value:"CurveFS \u5e94\u7528\u4ef7\u503c",id:"curvefs-\u5e94\u7528\u4ef7\u503c",level:3},{value:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",id:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",level:3}],p={toc:c},s="wrapper";function v(e){let{components:r,...l}=e;return(0,a.kt)(s,(0,n.Z)({},p,l,{components:r,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"curvefs-\u52a9\u529b\u7f51\u6613\u4e91\u5546\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42"},"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42"),(0,a.kt)("h2",{id:"\u4e91\u5546-asr-\u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883"},"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883"),(0,a.kt)("p",null,"\u968f\u7740\u8bed\u97f3\u6280\u672f\u5728\u5404\u884c\u5404\u4e1a\u7684\u5e7f\u6cdb\u5e94\u7528\uff0c\u81ea\u52a8\u8bed\u97f3\u8bc6\u522b(ASR)\u6b63\u5728\u6210\u4e3a\u4f17\u591a\u4e91\u670d\u52a1\u5546\u7684\u6838\u5fc3\u7ade\u4e89\u529b\u3002\n\u4f46 ASR \u6a21\u578b\u7684\u6301\u7eed\u4f18\u5316\u9700\u8981\u5927\u91cf\u8bad\u7ec3\u6570\u636e\u7684\u652f\u6301,\u5982\u4f55\u9ad8\u6548\u7ba1\u7406\u6d77\u91cf\u8bad\u7ec3\u6570\u636e\u6210\u4e3a\u4e91\u5546\u9762\u4e34\u7684\u4e00\u4e2a\u96be\u9898\u3002"),(0,a.kt)("p",null,"\u7f51\u6613\u4e91\u5546\u5728\u53d1\u5c55 ASR \u4e1a\u52a1\u8fc7\u7a0b\u4e2d\uff0c\u5b58\u50a8\u9762\u4e34\u4e86\u5de8\u5927\u6311\u6218\u3002"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u5c06\u6d77\u91cf\u8bad\u7ec3\u6570\u636e\u4fdd\u5b58\u5728\u5355\u4e2a\u8282\u70b9\u4e0a\uff0c\u4e0d\u4ec5\u5b58\u50a8\u8d44\u6e90\u5229\u7528\u7387\u4f4e\u4e0b\uff0c\u4e5f\u4f7f\u6570\u636e\u96be\u4ee5\u88ab\u591a\u4e2a\u8bad\u7ec3\u4efb\u52a1\u5171\u4eab\u8bbf\u95ee\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u4ec5\u5b58\u653e\u5728\u672c\u5730\u8fd8\u5e26\u6765\u6570\u636e\u4e22\u5931\u98ce\u9669\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u968f\u7740\u8bed\u97f3\u6570\u636e\u91cf\u5448\u6307\u6570\u589e\u957f\uff0c\u5355\u8282\u70b9\u5b58\u50a8\u65e9\u5df2\u65e0\u6cd5\u8d1f\u8377\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5b58\u50a8\u7a7a\u95f4\u6ee1\u6ea2\uff0c\u5c06\u4e25\u91cd\u5236\u7ea6\u4e1a\u52a1\u8fdb\u4e00\u6b65\u6269\u5f20\u3002")),(0,a.kt)("p",null,"\u9488\u5bf9\u5b58\u50a8\u9762\u4e34\u7684\u8fd9\u4e9b\u56f0\u5883\uff0c\u7f51\u6613\u4e91\u5546\u8feb\u5207\u9700\u8981\u627e\u5230\u4e00\u4e2a\u66f4\u4f18\u7684\u5b58\u50a8\u89e3\u51b3\u65b9\u6848\u3002"),(0,a.kt)("h2",{id:"curvefs"},"CurveFS"),(0,a.kt)("p",null,"\u4e3a\u89e3\u51b3\u8fd9\u4e00\u75db\u70b9,\u7f51\u6613\u4e91\u5546\u51b3\u5b9a\u5f15\u5165\u5f00\u6e90\u5b58\u50a8\u7cfb\u7edfCurveFS\u3002"),(0,a.kt)("h3",{id:"curvefs\u662f\u4ec0\u4e48"},"CurveFS\u662f\u4ec0\u4e48"),(0,a.kt)("p",null,"CurveFS\u662f\u4e00\u4e2a\u57fa\u4e8e Fuse\u5b9e\u73b0\u7684\u517c\u5bb9POSIX \u63a5\u53e3\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\uff0c\u67b6\u6784\u5982\u4e0b\u56fe\u6240\u793a:"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs arch",src:t(8418).Z,width:"971",height:"569"})),(0,a.kt)("p",null,"CurveFS\u7531\u4e09\u4e2a\u90e8\u5206\u7ec4\u6210\uff1a"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u5ba2\u6237\u7aef curve-fuse\uff0c\u548c\u5143\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u5143\u6570\u636e\u589e\u5220\u6539\u67e5\u8bf7\u6c42\uff0c\u548c\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u5143\u6570\u636e\u96c6\u7fa4 metaserver cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u5143\u6570\u636e(inode \u548c dentry)\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002\nmetaserver cluster\u7684\u67b6\u6784\u5177\u6709\u9ad8\u53ef\u9760\u3001\u9ad8\u53ef\u7528\u3001\u9ad8\u53ef\u6269\u7684\u7279\u70b9\uff1aMDS\u7528\u4e8e\u7ba1\u7406\u96c6\u7fa4\u62d3\u6251\u7ed3\u6784\uff0c\u8d44\u6e90\u8c03\u5ea6\u3002\nmetaserver \u662f\u6570\u636e\u8282\u70b9\uff0c\u4e00\u4e2a metaserver \u5bf9\u5e94\u7ba1\u7406\u4e00\u4e2a\u7269\u7406\u78c1\u76d8\u3002\nCurveFS \u4f7f\u7528 Raft \u4fdd\u8bc1\u5143\u6570\u636e\u7684\u53ef\u9760\u6027\u548c\u53ef\u7528\u6027\uff0cRaft\u590d\u5236\u7ec4\u7684\u57fa\u672c\u5355\u5143\u662f copyset\u3002\n\u4e00\u4e2a metaserver \u4e0a\u5305\u542b\u591a\u4e2a copyset \u590d\u5236\u7ec4\u3002")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u6570\u636e\u96c6\u7fa4 data cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u3002\ndata cluster \u76ee\u524d\u652f\u6301\u4e24\u5b58\u50a8\u7c7b\u578b\uff1a\u652f\u6301 S3 \u63a5\u53e3\u7684\u5bf9\u8c61\u5b58\u50a8\u4ee5\u53ca CurveBS\uff08\u5f00\u53d1\u4e2d\uff09\u3002"))),(0,a.kt)("p",null,"CurveFS \u4e00\u4e2a\u72ec\u7279\u7684\u8bbe\u8ba1\u662f\u5176\u540e\u7aef\u5b58\u50a8\u65e2\u53ef\u5bf9\u63a5 S3 \u5bf9\u8c61\u5b58\u50a8\uff0c\u4e5f\u53ef\u5bf9\u63a5 Curve \u81ea\u7814\u7684\u5757\u5b58\u50a8\u3002\n\u8fd9\u8d4b\u4e88\u7528\u6237\u9ad8\u5ea6\u7684\u7075\u6d3b\u6027\uff1a\u5bf9\u6027\u80fd\u8981\u6c42\u8f83\u9ad8\u7684\u4e1a\u52a1\u573a\u666f\uff0c\u53ef\u9009\u62e9\u90e8\u7f72\u5728 Curve \u9ad8\u6027\u80fd\u5757\u5b58\u50a8\uff1b\u5bf9\u6210\u672c\u654f\u611f\u7684\u4e1a\u52a1\uff0c\u5219\u53ef\u4ee5\u5229\u7528 S3 \u5b58\u50a8\u7684\u4f18\u52bf\u6765\u964d\u4f4e\u6210\u672c\u3002\nCurveFS \u517c\u5bb9\u4e24\u79cd\u5b58\u50a8\u540e\u7aef\u7684\u7279\u6027\uff0c\u4f7f\u5176\u53ef\u4ee5\u9002\u5e94\u4e0d\u540c\u4e1a\u52a1\u7684\u5b58\u50a8\u9700\u6c42\uff0c\u65e2\u4fdd\u8bc1\u9ad8\u6027\u80fd\uff0c\u4e5f\u63a7\u5236\u5b58\u50a8\u6210\u672c\u3002"),(0,a.kt)("h3",{id:"curvefs-\u5e94\u7528\u4ef7\u503c"},"CurveFS \u5e94\u7528\u4ef7\u503c"),(0,a.kt)("p",null,"\u76f8\u8f83\u4e8e\u672c\u5730\u5b58\u50a8\u800c\u8a00\uff0cCurveFS \u5177\u6709\u4ee5\u4e0b\u4f18\u52bf:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u5c06\u8bad\u7ec3\u6570\u636e\u5b58\u50a8\u5728\u4e91\u7aef\u5bf9\u8c61\u5b58\u50a8\uff0c\u5b9e\u73b0\u8d44\u6e90\u5f39\u6027\u6269\u5c55")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u7edf\u4e00\u7ba1\u7406\u8bad\u7ec3\u6570\u636e,\u652f\u6301\u591a\u8282\u70b9\u5171\u4eab\u8bbf\u95ee")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u540e\u7aef\u63a5\u5165 NOS \u4f4e\u9891\u5b58\u50a8\uff0c\u76f8\u6bd4\u672c\u5730\u5b58\u50a8\u80fd\u591f\u5927\u5e45\u964d\u4f4e ASR \u8bad\u7ec3\u5b58\u50a8\u6210\u672c")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u652f\u6301\u6570\u636e\u591a\u7ea7\u7f13\u5b58\uff0c\u63d0\u5347\u8bad\u7ec3\u6548\u7387\u7ea6 30 %"))),(0,a.kt)("p",null,"\u4e0e\u4fdd\u5b58\u5728\u5355\u673a\u4e0a\u7684\u4f20\u7edf\u65b9\u5f0f\u76f8\u6bd4\uff0cCurveFS\u4e91\u7aef\u90e8\u7f72\u65b9\u6848\u867d\u7136\u6570\u636e\u8bfb\u53d6\u901f\u5ea6\u7565\u4f4e\uff0c\u4f46\u8003\u8651\u5230\u672a\u6765\u8bad\u7ec3\u6570\u636e\u91cf\u7684\u589e\u957f\u9700\u6c42\uff0c\u5176\u6269\u5c55\u6027\u548c\u8d44\u6e90\u4f18\u5316\u80fd\u529b\u5df2\u5927\u5927\u8d85\u8d8a\u672c\u5730\u5b58\u50a8\u3002"),(0,a.kt)("p",null,"Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u4e0d\u4ec5\u9002\u7528\u4e8e ASR \u4e1a\u52a1\u573a\u666f\uff0c\u5728 ElasticSearch \u7b49\u5927\u6570\u636e\u5b58\u50a8\u5206\u6790\u4e1a\u52a1\u573a\u666f\u4e5f\u6709\u5e94\u7528\uff0c\u76ee\u524d\u4e5f\u5728\u591a\u4e2a\u96c6\u56e2\u5185\u90e8\u4e1a\u52a1\u573a\u666f\u843d\u5730\u4f7f\u7528\u3002"),(0,a.kt)("h3",{id:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"},"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"),(0,a.kt)("p",null,"\u4f46\u662f\uff0cCurveFS\u4e5f\u9762\u4e34\u4e00\u5b9a\u7684\u6311\u6218\u3002\n\u7531\u4e8eASR\u8bad\u7ec3\u8fc7\u7a0b\u4e2d\u9700\u8981\u9891\u7e41\u8bfb\u53d6\u6570\u636e\uff0c\u8fd9\u5bf9\u4e91\u5b58\u50a8\u7684\u8bbf\u95ee\u901f\u5ea6\u548c\u7a33\u5b9a\u6027\u63d0\u51fa\u4e86\u8f83\u9ad8\u8981\u6c42\u3002\n\u4e91\u5b58\u50a8\u7531\u4e8e\u7f51\u7edc\u4f20\u8f93\u7684\u5f71\u54cd\uff0c\u8bfb\u53d6\u901f\u5ea6\u96be\u4ee5\u4e0e\u672c\u5730\u5b58\u50a8\u76f8\u6bd4\u3002\n\u8fd9\u4e9b\u5728 CurveFS \u7684\u540e\u7eed\u7248\u672c\u8fed\u4ee3\u4e2d\u6301\u7eed\u4f18\u5316\uff0c\u5305\u62ec\u4f18\u5316\u5143\u6570\u636e\u6027\u80fd\u548c\u591a\u7ea7\u7f13\u5b58\u7b49\u3002"),(0,a.kt)("h1",{id:"\u603b\u7ed3"},"\u603b\u7ed3"),(0,a.kt)("p",null,"\u4f5c\u4e3a\u4e00\u4e2a\u5e74\u8f7b\u7684\u9879\u76ee\uff0cCurve\u4ecd\u5728\u5feb\u901f\u8fed\u4ee3\u3002\u672a\u6765Curve\u4f1a\u7ee7\u7eed\u4f18\u5316\u5728AI\u548c\u5927\u6570\u636e\u5206\u6790\u573a\u666f\u7684\u9002\u914d\u80fd\u529b:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u4e0e\u5404\u7c7bAI\u6846\u67b6\u6df1\u5ea6\u96c6\u6210\uff0c\u63d0\u4f9b\u81ea\u52a8\u5316\u7684\u6570\u636e\u9884\u70ed\u3001\u8bad\u7ec3\u4f18\u5316\u7b49\u529f\u80fd,\u63d0\u5347AI\u8bad\u7ec3\u7684\u6613\u7528\u6027\u548c\u6548\u7387\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u652f\u6301 HDFS \u63a5\u53e3\uff0c\u4f7f\u5176\u80fd\u591f\u7edf\u4e00\u5e94\u7528\u4e8e\u6570\u636e\u6536\u96c6\u3001\u5b58\u50a8\u4e0e\u5206\u6790\u5904\u7406\uff0c\u5b9e\u73b0\u6570\u636e\u751f\u547d\u5468\u671f\u7684\u65e0\u7f1d\u7ba1\u7406\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5728\u66f4\u591a\u5b9e\u9645\u4e1a\u52a1\u573a\u666f\u4e2d\u843d\u5730,\u4e30\u5bcc\u89e3\u51b3\u65b9\u6848\u7ecf\u9a8c\u3002")),(0,a.kt)("p",null,"\u901a\u8fc7\u8fd9\u4e9b\u52aa\u529b\uff0cCurve\u6b63\u5728\u6210\u957f\u4e3a\u4e00\u4e2a\u6210\u719f\u3001\u5f3a\u5927\u3001\u9002\u7528\u8303\u56f4\u5e7f\u6cdb\u7684\u5f00\u6e90\u5b58\u50a8\u7cfb\u7edf,\u4e3a\u7528\u6237\u63d0\u4f9b\u7b80\u5355\u9ad8\u6548\u7684\u5b58\u50a8\u670d\u52a1\u3002\u5b83\u7684\u53d1\u5c55\u524d\u666f\u5e7f\u9614\uff0c\u5fc5\u5c06\u4e3a\u5f00\u6e90\u793e\u533a\u505a\u51fa\u91cd\u8981\u8d21\u732e\u3002"))}v.isMDXComponent=!0},8418:(e,r,t)=>{t.d(r,{Z:()=>n});const n=t.p+"assets/images/curvefs_architecture-3f978bb3235810ab3994ccd36a79bbb8.png"}}]); \ No newline at end of file diff --git a/assets/js/561d73c1.7c837df2.js b/assets/js/561d73c1.7c837df2.js new file mode 100644 index 0000000..ba6505a --- /dev/null +++ b/assets/js/561d73c1.7c837df2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[8877],{3905:(e,r,t)=>{t.d(r,{Zo:()=>p,kt:()=>f});var n=t(7294);function a(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function l(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function u(e){for(var r=1;r=0||(a[t]=e[t]);return a}(e,r);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var o=n.createContext({}),c=function(e){var r=n.useContext(o),t=r;return e&&(t="function"==typeof e?e(r):u(u({},r),e)),t},p=function(e){var r=c(e.components);return n.createElement(o.Provider,{value:r},e.children)},s="mdxType",v={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},m=n.forwardRef((function(e,r){var t=e.components,a=e.mdxType,l=e.originalType,o=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),s=c(t),m=a,f=s["".concat(o,".").concat(m)]||s[m]||v[m]||l;return t?n.createElement(f,u(u({ref:r},p),{},{components:t})):n.createElement(f,u({ref:r},p))}));function f(e,r){var t=arguments,a=r&&r.mdxType;if("string"==typeof e||a){var l=t.length,u=new Array(l);u[0]=m;var i={};for(var o in r)hasOwnProperty.call(r,o)&&(i[o]=r[o]);i.originalType=e,i[s]="string"==typeof e?e:a,u[1]=i;for(var c=2;c{t.r(r),t.d(r,{assets:()=>o,contentTitle:()=>u,default:()=>v,frontMatter:()=>l,metadata:()=>i,toc:()=>c});var n=t(7462),a=(t(7294),t(3905));const l={},u="CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",i={unversionedId:"CurveFS/usecase/asr-storage",id:"CurveFS/usecase/asr-storage",title:"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",description:"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",source:"@site/docs/03-CurveFS/01-usecase/08-asr-storage.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/asr-storage",permalink:"/CurveFS/usecase/asr-storage",draft:!1,tags:[],version:"current",sidebarPosition:8,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548",permalink:"/CurveFS/usecase/ai-storage"},next:{title:"\u90e8\u7f72",permalink:"/category/\u90e8\u7f72-1"}},o={},c=[{value:"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",id:"\u4e91\u5546-asr-\u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883",level:2},{value:"CurveFS",id:"curvefs",level:2},{value:"CurveFS\u662f\u4ec0\u4e48",id:"curvefs\u662f\u4ec0\u4e48",level:3},{value:"CurveFS \u5e94\u7528\u4ef7\u503c",id:"curvefs-\u5e94\u7528\u4ef7\u503c",level:3},{value:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",id:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",level:3}],p={toc:c},s="wrapper";function v(e){let{components:r,...l}=e;return(0,a.kt)(s,(0,n.Z)({},p,l,{components:r,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"curvefs-\u52a9\u529b\u7f51\u6613\u4e91\u5546\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42"},"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42"),(0,a.kt)("h2",{id:"\u4e91\u5546-asr-\u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883"},"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883"),(0,a.kt)("p",null,"\u968f\u7740\u8bed\u97f3\u6280\u672f\u5728\u5404\u884c\u5404\u4e1a\u7684\u5e7f\u6cdb\u5e94\u7528\uff0c\u81ea\u52a8\u8bed\u97f3\u8bc6\u522b(ASR)\u6b63\u5728\u6210\u4e3a\u4f17\u591a\u4e91\u670d\u52a1\u5546\u7684\u6838\u5fc3\u7ade\u4e89\u529b\u3002\n\u4f46 ASR \u6a21\u578b\u7684\u6301\u7eed\u4f18\u5316\u9700\u8981\u5927\u91cf\u8bad\u7ec3\u6570\u636e\u7684\u652f\u6301,\u5982\u4f55\u9ad8\u6548\u7ba1\u7406\u6d77\u91cf\u8bad\u7ec3\u6570\u636e\u6210\u4e3a\u4e91\u5546\u9762\u4e34\u7684\u4e00\u4e2a\u96be\u9898\u3002"),(0,a.kt)("p",null,"\u7f51\u6613\u4e91\u5546\u5728\u53d1\u5c55 ASR \u4e1a\u52a1\u8fc7\u7a0b\u4e2d\uff0c\u5b58\u50a8\u9762\u4e34\u4e86\u5de8\u5927\u6311\u6218\u3002"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u5c06\u6d77\u91cf\u8bad\u7ec3\u6570\u636e\u4fdd\u5b58\u5728\u5355\u4e2a\u8282\u70b9\u4e0a\uff0c\u4e0d\u4ec5\u5b58\u50a8\u8d44\u6e90\u5229\u7528\u7387\u4f4e\u4e0b\uff0c\u4e5f\u4f7f\u6570\u636e\u96be\u4ee5\u88ab\u591a\u4e2a\u8bad\u7ec3\u4efb\u52a1\u5171\u4eab\u8bbf\u95ee\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u4ec5\u5b58\u653e\u5728\u672c\u5730\u8fd8\u5e26\u6765\u6570\u636e\u4e22\u5931\u98ce\u9669\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u968f\u7740\u8bed\u97f3\u6570\u636e\u91cf\u5448\u6307\u6570\u589e\u957f\uff0c\u5355\u8282\u70b9\u5b58\u50a8\u65e9\u5df2\u65e0\u6cd5\u8d1f\u8377\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5b58\u50a8\u7a7a\u95f4\u6ee1\u6ea2\uff0c\u5c06\u4e25\u91cd\u5236\u7ea6\u4e1a\u52a1\u8fdb\u4e00\u6b65\u6269\u5f20\u3002")),(0,a.kt)("p",null,"\u9488\u5bf9\u5b58\u50a8\u9762\u4e34\u7684\u8fd9\u4e9b\u56f0\u5883\uff0c\u7f51\u6613\u4e91\u5546\u8feb\u5207\u9700\u8981\u627e\u5230\u4e00\u4e2a\u66f4\u4f18\u7684\u5b58\u50a8\u89e3\u51b3\u65b9\u6848\u3002"),(0,a.kt)("h2",{id:"curvefs"},"CurveFS"),(0,a.kt)("p",null,"\u4e3a\u89e3\u51b3\u8fd9\u4e00\u75db\u70b9,\u7f51\u6613\u4e91\u5546\u51b3\u5b9a\u5f15\u5165\u5f00\u6e90\u5b58\u50a8\u7cfb\u7edfCurveFS\u3002"),(0,a.kt)("h3",{id:"curvefs\u662f\u4ec0\u4e48"},"CurveFS\u662f\u4ec0\u4e48"),(0,a.kt)("p",null,"CurveFS\u662f\u4e00\u4e2a\u57fa\u4e8e Fuse\u5b9e\u73b0\u7684\u517c\u5bb9POSIX \u63a5\u53e3\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\uff0c\u67b6\u6784\u5982\u4e0b\u56fe\u6240\u793a:"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs arch",src:t(8418).Z,width:"971",height:"569"})),(0,a.kt)("p",null,"CurveFS\u7531\u4e09\u4e2a\u90e8\u5206\u7ec4\u6210\uff1a"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u5ba2\u6237\u7aef curve-fuse\uff0c\u548c\u5143\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u5143\u6570\u636e\u589e\u5220\u6539\u67e5\u8bf7\u6c42\uff0c\u548c\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u5143\u6570\u636e\u96c6\u7fa4 metaserver cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u5143\u6570\u636e(inode \u548c dentry)\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002\nmetaserver cluster\u7684\u67b6\u6784\u5177\u6709\u9ad8\u53ef\u9760\u3001\u9ad8\u53ef\u7528\u3001\u9ad8\u53ef\u6269\u7684\u7279\u70b9\uff1aMDS\u7528\u4e8e\u7ba1\u7406\u96c6\u7fa4\u62d3\u6251\u7ed3\u6784\uff0c\u8d44\u6e90\u8c03\u5ea6\u3002\nmetaserver \u662f\u6570\u636e\u8282\u70b9\uff0c\u4e00\u4e2a metaserver \u5bf9\u5e94\u7ba1\u7406\u4e00\u4e2a\u7269\u7406\u78c1\u76d8\u3002\nCurveFS \u4f7f\u7528 Raft \u4fdd\u8bc1\u5143\u6570\u636e\u7684\u53ef\u9760\u6027\u548c\u53ef\u7528\u6027\uff0cRaft\u590d\u5236\u7ec4\u7684\u57fa\u672c\u5355\u5143\u662f copyset\u3002\n\u4e00\u4e2a metaserver \u4e0a\u5305\u542b\u591a\u4e2a copyset \u590d\u5236\u7ec4\u3002")),(0,a.kt)("li",{parentName:"ol"},(0,a.kt)("p",{parentName:"li"},"\u6570\u636e\u96c6\u7fa4 data cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u3002\ndata cluster \u76ee\u524d\u652f\u6301\u4e24\u5b58\u50a8\u7c7b\u578b\uff1a\u652f\u6301 S3 \u63a5\u53e3\u7684\u5bf9\u8c61\u5b58\u50a8\u4ee5\u53ca CurveBS\uff08\u5f00\u53d1\u4e2d\uff09\u3002"))),(0,a.kt)("p",null,"CurveFS \u4e00\u4e2a\u72ec\u7279\u7684\u8bbe\u8ba1\u662f\u5176\u540e\u7aef\u5b58\u50a8\u65e2\u53ef\u5bf9\u63a5 S3 \u5bf9\u8c61\u5b58\u50a8\uff0c\u4e5f\u53ef\u5bf9\u63a5 Curve \u81ea\u7814\u7684\u5757\u5b58\u50a8\u3002\n\u8fd9\u8d4b\u4e88\u7528\u6237\u9ad8\u5ea6\u7684\u7075\u6d3b\u6027\uff1a\u5bf9\u6027\u80fd\u8981\u6c42\u8f83\u9ad8\u7684\u4e1a\u52a1\u573a\u666f\uff0c\u53ef\u9009\u62e9\u90e8\u7f72\u5728 Curve \u9ad8\u6027\u80fd\u5757\u5b58\u50a8\uff1b\u5bf9\u6210\u672c\u654f\u611f\u7684\u4e1a\u52a1\uff0c\u5219\u53ef\u4ee5\u5229\u7528 S3 \u5b58\u50a8\u7684\u4f18\u52bf\u6765\u964d\u4f4e\u6210\u672c\u3002\nCurveFS \u517c\u5bb9\u4e24\u79cd\u5b58\u50a8\u540e\u7aef\u7684\u7279\u6027\uff0c\u4f7f\u5176\u53ef\u4ee5\u9002\u5e94\u4e0d\u540c\u4e1a\u52a1\u7684\u5b58\u50a8\u9700\u6c42\uff0c\u65e2\u4fdd\u8bc1\u9ad8\u6027\u80fd\uff0c\u4e5f\u63a7\u5236\u5b58\u50a8\u6210\u672c\u3002"),(0,a.kt)("h3",{id:"curvefs-\u5e94\u7528\u4ef7\u503c"},"CurveFS \u5e94\u7528\u4ef7\u503c"),(0,a.kt)("p",null,"\u76f8\u8f83\u4e8e\u672c\u5730\u5b58\u50a8\u800c\u8a00\uff0cCurveFS \u5177\u6709\u4ee5\u4e0b\u4f18\u52bf:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u5c06\u8bad\u7ec3\u6570\u636e\u5b58\u50a8\u5728\u4e91\u7aef\u5bf9\u8c61\u5b58\u50a8\uff0c\u5b9e\u73b0\u8d44\u6e90\u5f39\u6027\u6269\u5c55")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u7edf\u4e00\u7ba1\u7406\u8bad\u7ec3\u6570\u636e,\u652f\u6301\u591a\u8282\u70b9\u5171\u4eab\u8bbf\u95ee")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u540e\u7aef\u63a5\u5165 NOS \u4f4e\u9891\u5b58\u50a8\uff0c\u76f8\u6bd4\u672c\u5730\u5b58\u50a8\u80fd\u591f\u5927\u5e45\u964d\u4f4e ASR \u8bad\u7ec3\u5b58\u50a8\u6210\u672c")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("p",{parentName:"li"},"\u652f\u6301\u6570\u636e\u591a\u7ea7\u7f13\u5b58\uff0c\u63d0\u5347\u8bad\u7ec3\u6548\u7387\u7ea6 30 %"))),(0,a.kt)("p",null,"\u4e0e\u4fdd\u5b58\u5728\u5355\u673a\u4e0a\u7684\u4f20\u7edf\u65b9\u5f0f\u76f8\u6bd4\uff0cCurveFS\u4e91\u7aef\u90e8\u7f72\u65b9\u6848\u867d\u7136\u6570\u636e\u8bfb\u53d6\u901f\u5ea6\u7565\u4f4e\uff0c\u4f46\u8003\u8651\u5230\u672a\u6765\u8bad\u7ec3\u6570\u636e\u91cf\u7684\u589e\u957f\u9700\u6c42\uff0c\u5176\u6269\u5c55\u6027\u548c\u8d44\u6e90\u4f18\u5316\u80fd\u529b\u5df2\u5927\u5927\u8d85\u8d8a\u672c\u5730\u5b58\u50a8\u3002"),(0,a.kt)("p",null,"Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u4e0d\u4ec5\u9002\u7528\u4e8e ASR \u4e1a\u52a1\u573a\u666f\uff0c\u5728 ElasticSearch \u7b49\u5927\u6570\u636e\u5b58\u50a8\u5206\u6790\u4e1a\u52a1\u573a\u666f\u4e5f\u6709\u5e94\u7528\uff0c\u76ee\u524d\u4e5f\u5728\u591a\u4e2a\u96c6\u56e2\u5185\u90e8\u4e1a\u52a1\u573a\u666f\u843d\u5730\u4f7f\u7528\u3002"),(0,a.kt)("h3",{id:"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"},"\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"),(0,a.kt)("p",null,"\u4f46\u662f\uff0cCurveFS\u4e5f\u9762\u4e34\u4e00\u5b9a\u7684\u6311\u6218\u3002\n\u7531\u4e8eASR\u8bad\u7ec3\u8fc7\u7a0b\u4e2d\u9700\u8981\u9891\u7e41\u8bfb\u53d6\u6570\u636e\uff0c\u8fd9\u5bf9\u4e91\u5b58\u50a8\u7684\u8bbf\u95ee\u901f\u5ea6\u548c\u7a33\u5b9a\u6027\u63d0\u51fa\u4e86\u8f83\u9ad8\u8981\u6c42\u3002\n\u4e91\u5b58\u50a8\u7531\u4e8e\u7f51\u7edc\u4f20\u8f93\u7684\u5f71\u54cd\uff0c\u8bfb\u53d6\u901f\u5ea6\u96be\u4ee5\u4e0e\u672c\u5730\u5b58\u50a8\u76f8\u6bd4\u3002\n\u8fd9\u4e9b\u5728 CurveFS \u7684\u540e\u7eed\u7248\u672c\u8fed\u4ee3\u4e2d\u6301\u7eed\u4f18\u5316\uff0c\u5305\u62ec\u4f18\u5316\u5143\u6570\u636e\u6027\u80fd\u548c\u591a\u7ea7\u7f13\u5b58\u7b49\u3002"),(0,a.kt)("h1",{id:"\u603b\u7ed3"},"\u603b\u7ed3"),(0,a.kt)("p",null,"\u4f5c\u4e3a\u4e00\u4e2a\u5e74\u8f7b\u7684\u9879\u76ee\uff0cCurve\u4ecd\u5728\u5feb\u901f\u8fed\u4ee3\u3002\u672a\u6765Curve\u4f1a\u7ee7\u7eed\u4f18\u5316\u5728AI\u548c\u5927\u6570\u636e\u5206\u6790\u573a\u666f\u7684\u9002\u914d\u80fd\u529b:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u4e0e\u5404\u7c7bAI\u6846\u67b6\u6df1\u5ea6\u96c6\u6210\uff0c\u63d0\u4f9b\u81ea\u52a8\u5316\u7684\u6570\u636e\u9884\u70ed\u3001\u8bad\u7ec3\u4f18\u5316\u7b49\u529f\u80fd,\u63d0\u5347AI\u8bad\u7ec3\u7684\u6613\u7528\u6027\u548c\u6548\u7387\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u652f\u6301 HDFS \u63a5\u53e3\uff0c\u4f7f\u5176\u80fd\u591f\u7edf\u4e00\u5e94\u7528\u4e8e\u6570\u636e\u6536\u96c6\u3001\u5b58\u50a8\u4e0e\u5206\u6790\u5904\u7406\uff0c\u5b9e\u73b0\u6570\u636e\u751f\u547d\u5468\u671f\u7684\u65e0\u7f1d\u7ba1\u7406\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5728\u66f4\u591a\u5b9e\u9645\u4e1a\u52a1\u573a\u666f\u4e2d\u843d\u5730,\u4e30\u5bcc\u89e3\u51b3\u65b9\u6848\u7ecf\u9a8c\u3002")),(0,a.kt)("p",null,"\u901a\u8fc7\u8fd9\u4e9b\u52aa\u529b\uff0cCurve\u6b63\u5728\u6210\u957f\u4e3a\u4e00\u4e2a\u6210\u719f\u3001\u5f3a\u5927\u3001\u9002\u7528\u8303\u56f4\u5e7f\u6cdb\u7684\u5f00\u6e90\u5b58\u50a8\u7cfb\u7edf,\u4e3a\u7528\u6237\u63d0\u4f9b\u7b80\u5355\u9ad8\u6548\u7684\u5b58\u50a8\u670d\u52a1\u3002\u5b83\u7684\u53d1\u5c55\u524d\u666f\u5e7f\u9614\uff0c\u5fc5\u5c06\u4e3a\u5f00\u6e90\u793e\u533a\u505a\u51fa\u91cd\u8981\u8d21\u732e\u3002"))}v.isMDXComponent=!0},8418:(e,r,t)=>{t.d(r,{Z:()=>n});const n=t.p+"assets/images/curvefs_architecture-3f978bb3235810ab3994ccd36a79bbb8.png"}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.07607faf.js b/assets/js/935f2afb.07607faf.js deleted file mode 100644 index 5926e17..0000000 --- a/assets/js/935f2afb.07607faf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"\u5173\u4e8e Curve","href":"/","docId":"Intro"},{"type":"category","label":"Curve\u5757\u5b58\u50a8","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u5e94\u7528\u573a\u666f","href":"/CurveBS/usecase/scenario","docId":"CurveBS/usecase/scenario"},{"type":"link","label":"\u9ad8\u6027\u80fd\u4e91\u539f\u751f\u6570\u636e\u5e93\u5b58\u50a8\u5e95\u5ea7","href":"/CurveBS/usecase/cloudnative-database","docId":"CurveBS/usecase/cloudnative-database"},{"type":"link","label":"CurveBS RDMA+SPDK \u7248\u672c\u5728\u7f51\u6613\u6570\u636e\u5e93\u573a\u666f\u7684\u843d\u5730\u5b9e\u8df5","href":"/CurveBS/usecase/rdma-spdk","docId":"CurveBS/usecase/rdma-spdk"},{"type":"link","label":"\u521b\u4e91\u878d\u8fbe\u57fa\u4e8eCurve\u5757\u5b58\u50a8\u7684\u8d85\u878d\u5408\u573a\u666f\u5b9e\u8df5","href":"/CurveBS/usecase/chuangyun","docId":"CurveBS/usecase/chuangyun"},{"type":"link","label":"\u626c\u5dde\u4e07\u65b9\u57fa\u4e8e\u7533\u5a01\u5e73\u53f0\u7684Curve\u5757\u5b58\u50a8\u5728\u9ad8\u6027\u80fd\u548c\u8d85\u878d\u5408\u573a\u666f\u4e0b\u7684\u5b9e\u8df5","href":"/CurveBS/usecase/wanfang","docId":"CurveBS/usecase/wanfang"}],"href":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b"},{"type":"category","label":"\u90e8\u7f72","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveBS \u5feb\u901f\u4f53\u9a8c","href":"/CurveBS/deploy/quickstart","docId":"CurveBS/deploy/quickstart"},{"type":"link","label":"\u7269\u7406\u673a\u90e8\u7f72 CurveBS \u96c6\u7fa4","href":"/CurveBS/deploy/deploy-with-curveadm","docId":"CurveBS/deploy/deploy-with-curveadm"},{"type":"link","label":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveBS/deploy/deploy-with-k8s-operator","docId":"CurveBS/deploy/deploy-with-k8s-operator"},{"type":"link","label":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveBS/deploy/offline-deploy","docId":"CurveBS/deploy/offline-deploy"},{"type":"link","label":"CurveBS CSI \u9a71\u52a8\u7a0b\u5e8f","href":"/CurveBS/deploy/csi","docId":"CurveBS/deploy/csi"},{"type":"link","label":"iSCSI \u534f\u8bae\u652f\u6301","href":"/CurveBS/deploy/iscsi","docId":"CurveBS/deploy/iscsi"},{"type":"link","label":"OpenStack\u4e2dCurveBS\u7684Ceph RBD\u517c\u5bb9\u5c42\u5b9e\u73b0","href":"/CurveBS/deploy/rbd","docId":"CurveBS/deploy/rbd"},{"type":"link","label":"OpenStack \u5bf9\u63a5","href":"/CurveBS/deploy/openstack","docId":"CurveBS/deploy/openstack"},{"type":"link","label":"CurveBS RDMA+SPDK \u7248\u672c\u90e8\u7f72","href":"/CurveBS/deploy/rdma-spdk","docId":"CurveBS/deploy/rdma-spdk"},{"type":"link","label":"CurveBS \u63a7\u5236\u53f0\u90e8\u7f72","href":"/CurveBS/deploy/dashboard","docId":"CurveBS/deploy/dashboard"}],"href":"/category/\u90e8\u7f72"},{"type":"category","label":"\u6d4b\u8bd5","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","href":"/CurveBS/test/env-setup","docId":"CurveBS/test/env-setup"},{"type":"link","label":"\u6982\u8ff0","href":"/CurveBS/test/ci\u4ecb\u7ecd","docId":"CurveBS/test/ci\u4ecb\u7ecd"},{"type":"link","label":"curve\u8986\u76d6\u7387\u6536\u96c6\u4f18\u5316","href":"/CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","docId":"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6"},{"type":"link","label":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","href":"/CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","docId":"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5"},{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5","href":"/CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","docId":"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177"},{"type":"link","label":"Curve \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","href":"/CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","docId":"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f"},{"type":"link","label":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","href":"/CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","docId":"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5"}],"href":"/category/\u6d4b\u8bd5"},{"type":"category","label":"\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveBS\u5bf9\u6bd4Ceph RBD","href":"/CurveBS/comparison/CurveBS-vs-Ceph-rbd","docId":"CurveBS/comparison/CurveBS-vs-Ceph-rbd"}],"href":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4"},{"type":"category","label":"\u67b6\u6784","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/architecture-intro","docId":"CurveBS/architecture/architecture-intro"},{"type":"link","label":"CurveBS Chunkserver \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/chunkserver-arch","docId":"CurveBS/architecture/chunkserver-arch"},{"type":"link","label":"CurveBS Client \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/client-arch","docId":"CurveBS/architecture/client-arch"},{"type":"link","label":"CurveBS MDS \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/mds","docId":"CurveBS/architecture/mds"},{"type":"link","label":"CurveBS \u5feb\u7167\u514b\u9686\u67b6\u6784\u8bbe\u8ba1","href":"/CurveBS/architecture/snapshot","docId":"CurveBS/architecture/snapshot"},{"type":"link","label":"\u5ba2\u6237\u7aef\u70ed\u5347\u7ea7\u8bbe\u8ba1\u4ecb\u7ecd","href":"/CurveBS/architecture/nebd","docId":"CurveBS/architecture/nebd"}],"href":"/category/\u67b6\u6784"},{"type":"category","label":"\u6027\u80fd","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"benchmark\u6307\u5357","href":"/CurveBS/performance/how-to-benchmark","docId":"CurveBS/performance/how-to-benchmark"}],"href":"/category/\u6027\u80fd"},{"type":"category","label":"\u8fd0\u7ef4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u547d\u4ee4\u884c\u5de5\u5177","href":"/CurveBS/maintenance/command-line-tools","docId":"CurveBS/maintenance/command-line-tools"},{"type":"link","label":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","href":"/CurveBS/maintenance/administrator-guide","docId":"CurveBS/maintenance/administrator-guide"}],"href":"/category/\u8fd0\u7ef4"},{"type":"category","label":"\u63a5\u53e3\u6587\u6863","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"snapshotcloneserver interface","href":"/CurveBS/interface/snapshotcloneserver_interface","docId":"CurveBS/interface/snapshotcloneserver_interface"},{"type":"link","label":"localsnapshotclone restful api","href":"/CurveBS/interface/localsnapshotclone_restful_api","docId":"CurveBS/interface/localsnapshotclone_restful_api"},{"type":"link","label":"localsnapshotclone tools api","href":"/CurveBS/interface/localsnapshotclone_tools_api","docId":"CurveBS/interface/localsnapshotclone_tools_api"}],"href":"/category/\u63a5\u53e3\u6587\u6863"}],"href":"/category/curve\u5757\u5b58\u50a8"},{"type":"category","label":"Curve\u6587\u4ef6\u5b58\u50a8","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u5e94\u7528\u573a\u666f","href":"/CurveFS/usecase/scenario","docId":"CurveFS/usecase/scenario"},{"type":"link","label":"\u6c5f\u82cf\u519c\u4fe1\u9009\u62e9CurveFS\u5b9e\u73b0ES\u51b7\u6570\u636e\u957f\u671f\u5b58\u50a8","href":"/CurveFS/usecase/jiangsu-nongxin-es","docId":"CurveFS/usecase/jiangsu-nongxin-es"},{"type":"link","label":"\u57fa\u4e8eCurveFS+Samba\u652f\u6301SMB\u534f\u8bae","href":"/CurveFS/usecase/smb-support","docId":"CurveFS/usecase/smb-support"},{"type":"link","label":"\u516c\u6709\u4e91\u4e0a\u4f7f\u7528CurveFS\u66ff\u6362HDFS\u51b7\u5b58\u50a8\u5b9e\u73b0\u964d\u672c","href":"/CurveFS/usecase/hadoop-on-cloud","docId":"CurveFS/usecase/hadoop-on-cloud"},{"type":"link","label":"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301","href":"/CurveFS/usecase/s3-gateway","docId":"CurveFS/usecase/s3-gateway"},{"type":"link","label":"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528","href":"/CurveFS/usecase/elasticsearch-cold-data","docId":"CurveFS/usecase/elasticsearch-cold-data"},{"type":"link","label":"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5","href":"/CurveFS/usecase/ai-storage","docId":"CurveFS/usecase/ai-storage"},{"type":"link","label":"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42","href":"/CurveFS/usecase/asr-storage","docId":"CurveFS/usecase/asr-storage"}],"href":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1"},{"type":"category","label":"\u90e8\u7f72","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u5feb\u901f\u4f53\u9a8c","href":"/CurveFS/deploy/quickstart","docId":"CurveFS/deploy/quickstart"},{"type":"link","label":"\u7269\u7406\u673a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/deploy-with-curveadm","docId":"CurveFS/deploy/deploy-with-curveadm"},{"type":"link","label":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/deploy-with-k8s-operator","docId":"CurveFS/deploy/deploy-with-k8s-operator"},{"type":"link","label":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/offline-deploy","docId":"CurveFS/deploy/offline-deploy"},{"type":"link","label":"\u4f7f\u7528 CurveAdm \u90e8\u7f72\u5206\u5e03\u5f0f\u7f13\u5b58","href":"/CurveFS/deploy/distributed-cache","docId":"CurveFS/deploy/distributed-cache"},{"type":"link","label":"CurveFS CSI \u9a71\u52a8\u7a0b\u5e8f","href":"/CurveFS/deploy/csi","docId":"CurveFS/deploy/csi"},{"type":"link","label":"\u9759\u6001PV\u914d\u7f6e","href":"/CurveFS/deploy/static-pv","docId":"CurveFS/deploy/static-pv"}],"href":"/category/\u90e8\u7f72-1"},{"type":"category","label":"\u6d4b\u8bd5","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","href":"/CurveFS/test/env-setup","docId":"CurveFS/test/env-setup"},{"type":"link","label":"\u6982\u8ff0","href":"/CurveFS/test/ci\u4ecb\u7ecd","docId":"CurveFS/test/ci\u4ecb\u7ecd"},{"type":"link","label":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","href":"/CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","docId":"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5"},{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5","href":"/CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","docId":"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177"},{"type":"link","label":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","href":"/CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","docId":"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5"}],"href":"/category/\u6d4b\u8bd5-1"},{"type":"category","label":"\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u5bf9\u6bd4\u5176\u4ed6FS","href":"/CurveFS/comparison/CurveFS-vs-Other-FS","docId":"CurveFS/comparison/CurveFS-vs-Other-FS"}],"href":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1"},{"type":"category","label":"\u67b6\u6784","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/architecture-intro","docId":"CurveFS/architecture/architecture-intro"},{"type":"link","label":"CurveFS Client \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/client-arch","docId":"CurveFS/architecture/client-arch"},{"type":"link","label":"CurveFS MetaServer \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/metaserver-arch","docId":"CurveFS/architecture/metaserver-arch"}],"href":"/category/\u67b6\u6784-1"},{"type":"category","label":"\u6027\u80fd","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5\u6307\u5357","href":"/CurveFS/performance/how-to-benchmark","docId":"CurveFS/performance/how-to-benchmark"},{"type":"link","label":"\u6027\u80fd\u6307\u6807","href":"/CurveFS/performance/benchmark","docId":"CurveFS/performance/benchmark"},{"type":"link","label":"\u6027\u80fd\u8c03\u4f18","href":"/CurveFS/performance/perf-tune","docId":"CurveFS/performance/perf-tune"}],"href":"/category/\u6027\u80fd-1"},{"type":"category","label":"\u8fd0\u7ef4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u547d\u4ee4\u884c\u5de5\u5177","href":"/CurveFS/maintenance/command-line-tools","docId":"CurveFS/maintenance/command-line-tools"},{"type":"link","label":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","href":"/CurveFS/maintenance/administrator-guide","docId":"CurveFS/maintenance/administrator-guide"},{"type":"link","label":"\u8fc1\u79fb\u6570\u636e\u5230 CurveFS","href":"/CurveFS/maintenance/data-migration","docId":"CurveFS/maintenance/data-migration"}],"href":"/category/\u8fd0\u7ef4-1"}],"href":"/category/curve\u6587\u4ef6\u5b58\u50a8"},{"type":"category","label":"\u5f00\u53d1\u8005\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u53c2\u4e0e\u8d21\u732e","href":"/Develop/how-to-contribute","docId":"Develop/how-to-contribute"},{"type":"link","label":"\u7f16\u8bd1\u548c\u6d4b\u8bd5","href":"/Develop/build-and-test","docId":"Develop/build-and-test"},{"type":"link","label":"\u4ee3\u7801\u8d70\u8bfb","href":"/Develop/code-walkthrough","docId":"Develop/code-walkthrough"}],"href":"/category/\u5f00\u53d1\u8005\u76f8\u5173"},{"type":"category","label":"\u793e\u533a\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u793e\u533a\u51c6\u5219","href":"/Community/community-guideline","docId":"Community/community-guideline"},{"type":"category","label":"\u53cc\u5468\u4f1a","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"2023","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"README","href":"/Community/Double-Week-Meetings/01-2023/README","docId":"Community/Double-Week-Meetings/01-2023/README"},{"type":"link","label":"2023-01-12","href":"/Community/Double-Week-Meetings/01-2023/2023-01-12","docId":"Community/Double-Week-Meetings/01-2023/2023-01-12"},{"type":"link","label":"2023-02-09","href":"/Community/Double-Week-Meetings/01-2023/2023-02-09","docId":"Community/Double-Week-Meetings/01-2023/2023-02-09"},{"type":"link","label":"2023-02-23","href":"/Community/Double-Week-Meetings/01-2023/2023-02-23","docId":"Community/Double-Week-Meetings/01-2023/2023-02-23"},{"type":"link","label":"2023-03-09","href":"/Community/Double-Week-Meetings/01-2023/2023-03-09","docId":"Community/Double-Week-Meetings/01-2023/2023-03-09"},{"type":"link","label":"2023-03-23","href":"/Community/Double-Week-Meetings/01-2023/2023-03-23","docId":"Community/Double-Week-Meetings/01-2023/2023-03-23"},{"type":"link","label":"2023-04-13","href":"/Community/Double-Week-Meetings/01-2023/2023-04-13","docId":"Community/Double-Week-Meetings/01-2023/2023-04-13"},{"type":"link","label":"2023-04-27","href":"/Community/Double-Week-Meetings/01-2023/2023-04-27","docId":"Community/Double-Week-Meetings/01-2023/2023-04-27"},{"type":"link","label":"2023-05-18","href":"/Community/Double-Week-Meetings/01-2023/2023-05-18","docId":"Community/Double-Week-Meetings/01-2023/2023-05-18"},{"type":"link","label":"2023-06-01","href":"/Community/Double-Week-Meetings/01-2023/2023-06-01","docId":"Community/Double-Week-Meetings/01-2023/2023-06-01"},{"type":"link","label":"2023-06-15","href":"/Community/Double-Week-Meetings/01-2023/2023-06-15","docId":"Community/Double-Week-Meetings/01-2023/2023-06-15"},{"type":"link","label":"2023-06-29","href":"/Community/Double-Week-Meetings/01-2023/2023-06-29","docId":"Community/Double-Week-Meetings/01-2023/2023-06-29"},{"type":"link","label":"2023-07-13","href":"/Community/Double-Week-Meetings/01-2023/2023-07-13","docId":"Community/Double-Week-Meetings/01-2023/2023-07-13"},{"type":"link","label":"2023-08-03","href":"/Community/Double-Week-Meetings/01-2023/2023-08-03","docId":"Community/Double-Week-Meetings/01-2023/2023-08-03"},{"type":"link","label":"2023-09-14","href":"/Community/Double-Week-Meetings/01-2023/2023-09-14","docId":"Community/Double-Week-Meetings/01-2023/2023-09-14"},{"type":"link","label":"2023-10-12","href":"/Community/Double-Week-Meetings/01-2023/2023-10-12","docId":"Community/Double-Week-Meetings/01-2023/2023-10-12"}],"href":"/category/2023"}],"href":"/category/\u53cc\u5468\u4f1a"}],"href":"/category/\u793e\u533a\u76f8\u5173"},{"type":"category","label":"\u7248\u672c\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u7248\u672c\u53d1\u5e03\u5468\u671f","href":"/Release/release-intro","docId":"Release/release-intro"},{"type":"link","label":"CHANGELOG of v2.6","href":"/Release/release-notes-v2.6","docId":"Release/release-notes-v2.6"},{"type":"link","label":"CHANGELOG of v1.5","href":"/Release/release-notes-v1.5","docId":"Release/release-notes-v1.5"}],"href":"/category/\u7248\u672c\u76f8\u5173"},{"type":"link","label":"FAQ","href":"/faq","docId":"FAQ"},{"type":"category","label":"Roadmap","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Curve Roadmap 2023","href":"/Roadmap/roadmap-2023","docId":"Roadmap/roadmap-2023"}],"href":"/category/roadmap"}]},"docs":{"Community/community-guideline":{"id":"Community/community-guideline","title":"\u793e\u533a\u51c6\u5219","description":"\u793e\u533a\u6cbb\u7406","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-01-12":{"id":"Community/Double-Week-Meetings/01-2023/2023-01-12","title":"2023-01-12","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-02-09":{"id":"Community/Double-Week-Meetings/01-2023/2023-02-09","title":"2023-02-09","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-02-23":{"id":"Community/Double-Week-Meetings/01-2023/2023-02-23","title":"2023-02-23","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-03-09":{"id":"Community/Double-Week-Meetings/01-2023/2023-03-09","title":"2023-03-09","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-03-23":{"id":"Community/Double-Week-Meetings/01-2023/2023-03-23","title":"2023-03-23","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-04-13":{"id":"Community/Double-Week-Meetings/01-2023/2023-04-13","title":"2023-04-13","description":"\x3c!--","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-04-27":{"id":"Community/Double-Week-Meetings/01-2023/2023-04-27","title":"2023-04-27","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-05-18":{"id":"Community/Double-Week-Meetings/01-2023/2023-05-18","title":"2023-05-18","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-01":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-01","title":"2023-06-01","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-15":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-15","title":"2023-06-15","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-29":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-29","title":"2023-06-29","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-07-13":{"id":"Community/Double-Week-Meetings/01-2023/2023-07-13","title":"2023-07-13","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-08-03":{"id":"Community/Double-Week-Meetings/01-2023/2023-08-03","title":"2023-08-03","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-09-14":{"id":"Community/Double-Week-Meetings/01-2023/2023-09-14","title":"2023-09-14","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-10-12":{"id":"Community/Double-Week-Meetings/01-2023/2023-10-12","title":"2023-10-12","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/README":{"id":"Community/Double-Week-Meetings/01-2023/README","title":"README","description":"Curve \u53cc\u5468\u4f1a","sidebar":"tutorialSidebar"},"CurveBS/architecture/architecture-intro":{"id":"CurveBS/architecture/architecture-intro","title":"\u67b6\u6784\u4ecb\u7ecd","description":"\u8bf7\u53c2\u8003Curve\u8bbe\u8ba1\u8981\u70b9","sidebar":"tutorialSidebar"},"CurveBS/architecture/chunkserver-arch":{"id":"CurveBS/architecture/chunkserver-arch","title":"CurveBS Chunkserver \u67b6\u6784\u4ecb\u7ecd","description":"1 ChunkServer \u6982\u8ff0","sidebar":"tutorialSidebar"},"CurveBS/architecture/client-arch":{"id":"CurveBS/architecture/client-arch","title":"CurveBS Client \u67b6\u6784\u4ecb\u7ecd","description":"1 \u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/mds":{"id":"CurveBS/architecture/mds","title":"CurveBS MDS \u67b6\u6784\u4ecb\u7ecd","description":"\u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/nebd":{"id":"CurveBS/architecture/nebd","title":"\u5ba2\u6237\u7aef\u70ed\u5347\u7ea7\u8bbe\u8ba1\u4ecb\u7ecd","description":"\u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/snapshot":{"id":"CurveBS/architecture/snapshot","title":"CurveBS \u5feb\u7167\u514b\u9686\u67b6\u6784\u8bbe\u8ba1","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/comparison/CurveBS-vs-Ceph-rbd":{"id":"CurveBS/comparison/CurveBS-vs-Ceph-rbd","title":"CurveBS\u5bf9\u6bd4Ceph RBD","description":"\u9ad8\u6027\u80fd | \u66f4\u7a33\u5b9a | \u6613\u8fd0\u7ef4 | \u4e91\u539f\u751f","sidebar":"tutorialSidebar"},"CurveBS/deploy/csi":{"id":"CurveBS/deploy/csi","title":"CurveBS CSI \u9a71\u52a8\u7a0b\u5e8f","description":"\u7b80\u4ecb","sidebar":"tutorialSidebar"},"CurveBS/deploy/dashboard":{"id":"CurveBS/deploy/dashboard","title":"CurveBS \u63a7\u5236\u53f0\u90e8\u7f72","description":"\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveBS/deploy/deploy-with-curveadm":{"id":"CurveBS/deploy/deploy-with-curveadm","title":"\u7269\u7406\u673a\u90e8\u7f72 CurveBS \u96c6\u7fa4","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/deploy-with-k8s-operator":{"id":"CurveBS/deploy/deploy-with-k8s-operator","title":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"curve-operator\u4ee3\u7801\u4ed3\u5e93\uff1ahttps://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md","sidebar":"tutorialSidebar"},"CurveBS/deploy/iscsi":{"id":"CurveBS/deploy/iscsi","title":"iSCSI \u534f\u8bae\u652f\u6301","description":"\u76ee\u524d CurveBS \u652f\u6301\u4e24\u79cd iscsi target\u670d\u52a1\uff0c\u8bf7\u9009\u62e9\u5408\u9002\u7684\u8fdb\u884c\u90e8\u7f72\u4f7f\u7528\uff1a","sidebar":"tutorialSidebar"},"CurveBS/deploy/offline-deploy":{"id":"CurveBS/deploy/offline-deploy","title":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u672c\u6587\u9002\u7528\u4e8e\u65e0\u6cd5\u4ece\u516c\u7f51docker\u955c\u50cf\u4ed3\u5e93\u62c9\u53d6CurveFS docker\u955c\u50cf\u573a\u666f\u4e0b\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/openstack":{"id":"CurveBS/deploy/openstack","title":"OpenStack \u5bf9\u63a5","description":"1. Cinder\u5bf9\u63a5\u8bf7\u53c2\u8003\uff1ahttps://github.com/opencurve/curve-cinder","sidebar":"tutorialSidebar"},"CurveBS/deploy/quickstart":{"id":"CurveBS/deploy/quickstart","title":"CurveBS \u5feb\u901f\u4f53\u9a8c","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/rbd":{"id":"CurveBS/deploy/rbd","title":"OpenStack\u4e2dCurveBS\u7684Ceph RBD\u517c\u5bb9\u5c42\u5b9e\u73b0","description":"\u80cc\u666f\u4e0e\u76ee\u6807","sidebar":"tutorialSidebar"},"CurveBS/deploy/rdma-spdk":{"id":"CurveBS/deploy/rdma-spdk","title":"CurveBS RDMA+SPDK \u7248\u672c\u90e8\u7f72","description":"\u7cfb\u7edf\u73af\u5883","sidebar":"tutorialSidebar"},"CurveBS/interface/localsnapshotclone_restful_api":{"id":"CurveBS/interface/localsnapshotclone_restful_api","title":"localsnapshotclone restful api","description":"localsnapshotclone restful api \u9700\u517c\u5bb9\u539f\u6709s3\u5feb\u7167api: snapshotcloneserverinterface, \u4f7f\u7528\u4e24\u5957API\u5747\u53ef\u4ee5\u8c03\u7528\u672c\u5730\u5feb\u7167\u548c\u514b\u9686\u529f\u80fd,","sidebar":"tutorialSidebar"},"CurveBS/interface/localsnapshotclone_tools_api":{"id":"CurveBS/interface/localsnapshotclone_tools_api","title":"localsnapshotclone tools api","description":"\u672c\u5730\u5feb\u7167\u548c\u514b\u9686\u91c7\u7528tools-v2\u5de5\u5177\u547d\u4ee4\u884c\u65b9\u5f0f\uff0c\u4f7f\u7528\u65b9\u6cd5\u5982\u4e0b\uff1a","sidebar":"tutorialSidebar"},"CurveBS/interface/snapshotcloneserver_interface":{"id":"CurveBS/interface/snapshotcloneserver_interface","title":"snapshotcloneserver interface","description":"\u521b\u5efa\u5feb\u7167\uff1a","sidebar":"tutorialSidebar"},"CurveBS/maintenance/administrator-guide":{"id":"CurveBS/maintenance/administrator-guide","title":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","description":"\u65e5\u5e38\u8fd0\u7ef4\u64cd\u4f5c","sidebar":"tutorialSidebar"},"CurveBS/maintenance/command-line-tools":{"id":"CurveBS/maintenance/command-line-tools","title":"\u547d\u4ee4\u884c\u5de5\u5177","description":"Curve \u547d\u4ee4\u884c\u5de5\u5177\u4ee3\u7801\u4ed3\u5e93\u5730\u5740\uff1ahttps://github.com/opencurve/curve/blob/master/tools-v2/README.md","sidebar":"tutorialSidebar"},"CurveBS/performance/how-to-benchmark":{"id":"CurveBS/performance/how-to-benchmark","title":"benchmark\u6307\u5357","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/test/ci\u4ecb\u7ecd":{"id":"CurveBS/test/ci\u4ecb\u7ecd","title":"\u6982\u8ff0","description":"\u672c\u6587\u5c06\u4e3b\u8981\u4ecb\u7ecd\u793e\u533a\u4eba\u5458\u5728\u53c2\u4e0ecurve\u9879\u76ee\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u5982\u4f55\u5feb\u901f\u63d0\u4ea4PR\u5e76\u8fdb\u884cCI\u6d4b\u8bd5\uff0c\u4ee5\u53ca\u5feb\u901f\u5b9a\u4f4dCI\u5931\u8d25\u539f\u56e0\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6":{"id":"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","title":"curve\u8986\u76d6\u7387\u6536\u96c6\u4f18\u5316","description":"curve\u4e3b\u8981\u4ece2\u4e2a\u7ef4\u5ea6\u8861\u91cf\u5355\u5143\u6d4b\u8bd5\u8986\u76d6\u7387\uff1a\u884c\u8986\u76d6\u7387\u548c\u5206\u652f\u8986\u76d6\u7387\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/env-setup":{"id":"CurveBS/test/env-setup","title":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","description":"\u8bf7\u53c2\u8003 \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","sidebar":"tutorialSidebar"},"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5":{"id":"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","title":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","description":"\u6d4b\u8bd5\u76ee\u7684","sidebar":"tutorialSidebar"},"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177":{"id":"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","title":"\u6027\u80fd\u6d4b\u8bd5","description":"\u80cc\u666f\u77e5\u8bc6","sidebar":"tutorialSidebar"},"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f":{"id":"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","title":"Curve \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","description":"Curve \u5f53\u524d\u6d4b\u8bd5\u7684\u6027\u80fd\uff0c\u5982\u679c\u6ca1\u6709\u7279\u522b\u8bf4\u660e\uff0c\u90fd\u662f\u5728\u57fa\u4e8e\u4ee5\u4e0b\u914d\u7f6e\u7684\u673a\u5668\u4e0a\u6d4b\u8bd5\u7684\u3002\u8be5\u914d\u7f6e\u4ec5\u4ec5\u8868\u793acurve\u5f53\u524d\u6027\u80fd\u6570\u636e\u7684\u6d4b\u8bd5\u73af\u5883\uff0c\u7531\u4e8e\u90e8\u5206\u786c\u4ef6\u7248\u672c\u6bd4\u8f83\u65e7\uff0c\u8be5\u73af\u5883\u7684\u786c\u4ef6\u4e0d\u4f5c\u4e3a\u63a8\u8350\u786c\u4ef6\u578b\u53f7\uff0c\u4f46\u662f\u5bf9\u786c\u4ef6\u7684\u914d\u7f6e\u90e8\u5206\u53ef\u505a\u53c2\u8003\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5":{"id":"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","title":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","description":"\u8bf7\u6ce8\u610f\uff1a","sidebar":"tutorialSidebar"},"CurveBS/usecase/chuangyun":{"id":"CurveBS/usecase/chuangyun","title":"\u521b\u4e91\u878d\u8fbe\u57fa\u4e8eCurve\u5757\u5b58\u50a8\u7684\u8d85\u878d\u5408\u573a\u666f\u5b9e\u8df5","description":"1. \u521b\u4e91\u878d\u8fbe\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveBS/usecase/cloudnative-database":{"id":"CurveBS/usecase/cloudnative-database","title":"\u9ad8\u6027\u80fd\u4e91\u539f\u751f\u6570\u636e\u5e93\u5b58\u50a8\u5e95\u5ea7","description":"\u524d\u8a00","sidebar":"tutorialSidebar"},"CurveBS/usecase/rdma-spdk":{"id":"CurveBS/usecase/rdma-spdk","title":"CurveBS RDMA+SPDK \u7248\u672c\u5728\u7f51\u6613\u6570\u636e\u5e93\u573a\u666f\u7684\u843d\u5730\u5b9e\u8df5","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/usecase/scenario":{"id":"CurveBS/usecase/scenario","title":"\u5e94\u7528\u573a\u666f","description":"CurveBS\u7684\u6838\u5fc3\u5e94\u7528\u573a\u666f\u4e3b\u8981\u5305\u62ec\uff1a","sidebar":"tutorialSidebar"},"CurveBS/usecase/wanfang":{"id":"CurveBS/usecase/wanfang","title":"\u626c\u5dde\u4e07\u65b9\u57fa\u4e8e\u7533\u5a01\u5e73\u53f0\u7684Curve\u5757\u5b58\u50a8\u5728\u9ad8\u6027\u80fd\u548c\u8d85\u878d\u5408\u573a\u666f\u4e0b\u7684\u5b9e\u8df5","description":"1. \u626c\u5dde\u4e07\u65b9\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/architecture/architecture-intro":{"id":"CurveFS/architecture/architecture-intro","title":"CurveFS \u67b6\u6784\u4ecb\u7ecd","description":"CurveFS \u662f POSIX \u517c\u5bb9\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\uff0c\u7528\u4e8e\u66f4\u597d\u7684\u652f\u6301\u4e91\u539f\u751f\u573a\u666f\u3002","sidebar":"tutorialSidebar"},"CurveFS/architecture/client-arch":{"id":"CurveFS/architecture/client-arch","title":"CurveFS Client \u67b6\u6784\u4ecb\u7ecd","description":"1 \u6982\u8981","sidebar":"tutorialSidebar"},"CurveFS/architecture/metaserver-arch":{"id":"CurveFS/architecture/metaserver-arch","title":"CurveFS MetaServer \u67b6\u6784\u4ecb\u7ecd","description":"\u6982\u8ff0","sidebar":"tutorialSidebar"},"CurveFS/comparison/CurveFS-vs-Other-FS":{"id":"CurveFS/comparison/CurveFS-vs-Other-FS","title":"CurveFS \u5bf9\u6bd4\u5176\u4ed6FS","description":"CurveFS \u4e0e\u5176\u4ed6\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\u7684\u533a\u522b\u53ef\u4ee5\u53c2\u8003 FAQ \u4e2d\u7684\u7b80\u5355\u6bd4\u8f83\uff0c\u5982\u9700\u4e86\u89e3\u8be6\u7ec6\u5dee\u5f02\uff0c\u8bf7\u6309\u7167\u5982\u4e0b \u8054\u7cfb\u65b9\u5f0f \u4e0e Curve \u56e2\u961f\u8fdb\u884c\u4ea4\u6d41\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/csi":{"id":"CurveFS/deploy/csi","title":"CurveFS CSI \u9a71\u52a8\u7a0b\u5e8f","description":"\u7b80\u4ecb","sidebar":"tutorialSidebar"},"CurveFS/deploy/deploy-with-curveadm":{"id":"CurveFS/deploy/deploy-with-curveadm","title":"\u7269\u7406\u673a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/deploy-with-k8s-operator":{"id":"CurveFS/deploy/deploy-with-k8s-operator","title":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"curve-operator\u4ee3\u7801\u4ed3\u5e93\uff1ahttps://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md","sidebar":"tutorialSidebar"},"CurveFS/deploy/distributed-cache":{"id":"CurveFS/deploy/distributed-cache","title":"\u4f7f\u7528 CurveAdm \u90e8\u7f72\u5206\u5e03\u5f0f\u7f13\u5b58","description":"\u5206\u5e03\u5f0f\u7f13\u5b58\u4ecb\u7ecd","sidebar":"tutorialSidebar"},"CurveFS/deploy/offline-deploy":{"id":"CurveFS/deploy/offline-deploy","title":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u672c\u6587\u9002\u7528\u4e8e\u65e0\u6cd5\u4ece\u516c\u7f51docker\u955c\u50cf\u4ed3\u5e93\u62c9\u53d6CurveFS docker\u955c\u50cf\u573a\u666f\u4e0b\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/quickstart":{"id":"CurveFS/deploy/quickstart","title":"CurveFS \u5feb\u901f\u4f53\u9a8c","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/static-pv":{"id":"CurveFS/deploy/static-pv","title":"\u9759\u6001PV\u914d\u7f6e","description":"CurveFS CSI\u4f7f\u7528node\u8282\u70b9\u4e0a\u7684\u7f13\u5b58\u76d8","sidebar":"tutorialSidebar"},"CurveFS/maintenance/administrator-guide":{"id":"CurveFS/maintenance/administrator-guide","title":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","description":"\u65e5\u5e38\u8fd0\u7ef4\u64cd\u4f5c","sidebar":"tutorialSidebar"},"CurveFS/maintenance/command-line-tools":{"id":"CurveFS/maintenance/command-line-tools","title":"\u547d\u4ee4\u884c\u5de5\u5177","description":"Curve \u547d\u4ee4\u884c\u5de5\u5177\u4ee3\u7801\u4ed3\u5e93\u5730\u5740\uff1ahttps://github.com/opencurve/curve/blob/master/tools-v2/README.md","sidebar":"tutorialSidebar"},"CurveFS/maintenance/data-migration":{"id":"CurveFS/maintenance/data-migration","title":"\u8fc1\u79fb\u6570\u636e\u5230 CurveFS","description":"CurveFS\u5728\u4e0d\u540c\u5e94\u7528\u573a\u666f\u843d\u5730\u4f7f\u7528\u65f6\uff0c\u4e0d\u53ef\u907f\u514d\u7684\u6d89\u53ca\u5230\u5c06\u539f\u7cfb\u7edf\u6570\u636e\u8fc1\u79fb\u5230CurveFS\uff0c\u7279\u522b\u662f\u5927\u6570\u636e\u548cAI\u76f8\u5173\u4e1a\u52a1\uff0c\u4ed6\u4eec\u6570\u636e\u91cf\u5de8\u5927\uff08\u5305\u62ec\u6d77\u91cf\u5c0f\u6587\u4ef6\uff09\uff0c\u5982\u4f55\u505a\u5230\u9ad8\u6548\u3001\u53ef\u9760\u7684\u6570\u636e\u8fc1\u79fb\u662f\u4e00\u5207\u7684\u5f00\u59cb\u3002","sidebar":"tutorialSidebar"},"CurveFS/performance/benchmark":{"id":"CurveFS/performance/benchmark","title":"\u6027\u80fd\u6307\u6807","description":"TBD","sidebar":"tutorialSidebar"},"CurveFS/performance/how-to-benchmark":{"id":"CurveFS/performance/how-to-benchmark","title":"\u6027\u80fd\u6d4b\u8bd5\u6307\u5357","description":"\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},"CurveFS/performance/perf-tune":{"id":"CurveFS/performance/perf-tune","title":"\u6027\u80fd\u8c03\u4f18","description":"TBD","sidebar":"tutorialSidebar"},"CurveFS/test/ci\u4ecb\u7ecd":{"id":"CurveFS/test/ci\u4ecb\u7ecd","title":"\u6982\u8ff0","description":"\u672c\u6587\u5c06\u4e3b\u8981\u4ecb\u7ecd\u793e\u533a\u4eba\u5458\u5728\u53c2\u4e0ecurve\u9879\u76ee\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u5982\u4f55\u5feb\u901f\u63d0\u4ea4PR\u5e76\u8fdb\u884cCI\u6d4b\u8bd5\uff0c\u4ee5\u53ca\u5feb\u901f\u5b9a\u4f4dCI\u5931\u8d25\u539f\u56e0\u3002","sidebar":"tutorialSidebar"},"CurveFS/test/env-setup":{"id":"CurveFS/test/env-setup","title":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","description":"\u8bf7\u53c2\u8003 \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","sidebar":"tutorialSidebar"},"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5":{"id":"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","title":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","description":"\u6d4b\u8bd5\u76ee\u7684","sidebar":"tutorialSidebar"},"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177":{"id":"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","title":"\u6027\u80fd\u6d4b\u8bd5","description":"\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5":{"id":"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","title":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","description":"\u8bf7\u6ce8\u610f\uff1a","sidebar":"tutorialSidebar"},"CurveFS/usecase/ai-storage":{"id":"CurveFS/usecase/ai-storage","title":"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5","description":"\u4e00\u3001\u9879\u76ee\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/usecase/asr-storage":{"id":"CurveFS/usecase/asr-storage","title":"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42","description":"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883","sidebar":"tutorialSidebar"},"CurveFS/usecase/elasticsearch-cold-data":{"id":"CurveFS/usecase/elasticsearch-cold-data","title":"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528","description":"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca","sidebar":"tutorialSidebar"},"CurveFS/usecase/hadoop-on-cloud":{"id":"CurveFS/usecase/hadoop-on-cloud","title":"\u516c\u6709\u4e91\u4e0a\u4f7f\u7528CurveFS\u66ff\u6362HDFS\u51b7\u5b58\u50a8\u5b9e\u73b0\u964d\u672c","description":"\u672c\u6587\u8ba8\u8bba\u7684\u90e8\u7f72\u65b9\u5f0f\u4e3a\u57fa\u4e8eCurveFS\u6302\u8f7d\u70b9\u66ff\u6362\u516c\u6709\u4e91EBS\u7b49\u4e91\u76d8\uff0cCurveFS\u652f\u6301HDFS SDK\u529f\u80fd\u5c06\u4e8e2.7\u7248\u672c\u53d1\u5e03\uff0c\u5c4a\u65f6\u5c06\u63d0\u4f9b\u5b8c\u5168\u517c\u5bb9HDFS API\u7684SDK jar\u5305\u4f9b\u4e0a\u5c42\u5927\u6570\u636e\u5e94\u7528\uff08\u5982Spark\u3001Flink\u7b49\uff09\u4f7f\u7528\uff0c\u5c06\u4e0d\u518d\u9700\u8981CurveFS\u6302\u8f7d\u70b9\u3002","sidebar":"tutorialSidebar"},"CurveFS/usecase/jiangsu-nongxin-es":{"id":"CurveFS/usecase/jiangsu-nongxin-es","title":"\u6c5f\u82cf\u519c\u4fe1\u9009\u62e9CurveFS\u5b9e\u73b0ES\u51b7\u6570\u636e\u957f\u671f\u5b58\u50a8","description":"\u4e00\u3001\u9700\u6c42\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/usecase/s3-gateway":{"id":"CurveFS/usecase/s3-gateway","title":"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301","description":"\u672c\u6587\u7684\u90e8\u7f72\u65b9\u6848\u662f\u57fa\u4e8eCurveFS\u6302\u8f7d\u70b9+minio-s3-gateway\u65b9\u5f0f\uff0c\u540e\u7eed\u4f1a\u8003\u8651\u652f\u6301minio-s3-gateway+CurveFS SDK\u65b9\u5f0f\u90e8\u7f72\uff0c\u5c06\u4e0d\u518d\u9700\u8981\u4f9d\u8d56CurveFS\u6302\u8f7d\u70b9\u3002","sidebar":"tutorialSidebar"},"CurveFS/usecase/scenario":{"id":"CurveFS/usecase/scenario","title":"\u5e94\u7528\u573a\u666f","description":"CurveFS\u7684\u6838\u5fc3\u5e94\u7528\u573a\u666f\u4e3b\u8981\u5305\u62ec\uff1a","sidebar":"tutorialSidebar"},"CurveFS/usecase/smb-support":{"id":"CurveFS/usecase/smb-support","title":"\u57fa\u4e8eCurveFS+Samba\u652f\u6301SMB\u534f\u8bae","description":"\u672c\u6587\u5c06\u4f1a\u4ecb\u7ecdCurveFS\u4e0esamba\u4ee5\u53ca\u5982\u4f55\u5c06\u4ed6\u4eec\u7ed3\u5408\u4f7f\u7528, \u5e76\u4e14\u4ecb\u7ecdsamba\u7684\u7b2c\u4e09\u65b9moudle\u5f00\u53d1\u77e5\u8bc6\u3002","sidebar":"tutorialSidebar"},"Develop/build-and-test":{"id":"Develop/build-and-test","title":"\u7f16\u8bd1\u548c\u6d4b\u8bd5","description":"- \u7f16\u8bd1\u548c\u6d4b\u8bd5","sidebar":"tutorialSidebar"},"Develop/code-walkthrough":{"id":"Develop/code-walkthrough","title":"\u4ee3\u7801\u8d70\u8bfb","description":"\u8bf7\u53c2\u8003 Curve\u6e90\u7801\u53ca\u6838\u5fc3\u6d41\u7a0b\u6df1\u5ea6\u89e3\u8bfb","sidebar":"tutorialSidebar"},"Develop/how-to-contribute":{"id":"Develop/how-to-contribute","title":"\u53c2\u4e0e\u8d21\u732e","description":"\u5982\u4f55\u53c2\u4e0e Curve \u9879\u76ee\u5f00\u53d1\u8be6\u89c1 Curve \u5f00\u53d1\u8005\u6307\u5357\uff0c\u5e76\u4e14\u8bf7\u9075\u5faa\u8d21\u732e\u8005\u51c6\u5219\uff0c\u6211\u4eec\u671f\u5f85\u60a8\u7684\u8d21\u732e\uff01","sidebar":"tutorialSidebar"},"FAQ":{"id":"FAQ","title":"FAQ","description":"\u5173\u4e8eCurve","sidebar":"tutorialSidebar"},"Intro":{"id":"Intro","title":"\u5173\u4e8e Curve","description":"Curve \u662f\u7f51\u6613\u4e3b\u5bfc\u81ea\u7814\u7684\u73b0\u4ee3\u5316\u5b58\u50a8\u7cfb\u7edf, \u76ee\u524d\u652f\u6301\u6587\u4ef6\u5b58\u50a8(CurveFS)\u548c\u5757\u5b58\u50a8(CurveBS)\u3002\u73b0\u4f5c\u4e3a\u6c99\u7bb1\u9879\u76ee\u6258\u7ba1\u4e8eCNCF\u3002","sidebar":"tutorialSidebar"},"Release/release-intro":{"id":"Release/release-intro","title":"\u7248\u672c\u53d1\u5e03\u5468\u671f","description":"- CURVE\u7248\u672c\u53d1\u5e03\u5468\u671f\uff1a\u5927\u7248\u672c\u534a\u5e74\uff0c\u5c0f\u7248\u672c1~2\u4e2a\u6708","sidebar":"tutorialSidebar"},"Release/release-notes-v1.5":{"id":"Release/release-notes-v1.5","title":"CHANGELOG of v1.5","description":"Features","sidebar":"tutorialSidebar"},"Release/release-notes-v2.6":{"id":"Release/release-notes-v2.6","title":"CHANGELOG of v2.6","description":"Previous change logs can be found at CHANGELOG-2.5","sidebar":"tutorialSidebar"},"Roadmap/roadmap-2023":{"id":"Roadmap/roadmap-2023","title":"Curve Roadmap 2023","description":"\u8bf7\u53c2\u8003 Curve Roadmap 2023","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.c5a3d2c7.js b/assets/js/935f2afb.c5a3d2c7.js new file mode 100644 index 0000000..524cc3a --- /dev/null +++ b/assets/js/935f2afb.c5a3d2c7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"\u5173\u4e8e Curve","href":"/","docId":"Intro"},{"type":"category","label":"Curve\u5757\u5b58\u50a8","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u5e94\u7528\u573a\u666f","href":"/CurveBS/usecase/scenario","docId":"CurveBS/usecase/scenario"},{"type":"link","label":"\u9ad8\u6027\u80fd\u4e91\u539f\u751f\u6570\u636e\u5e93\u5b58\u50a8\u5e95\u5ea7","href":"/CurveBS/usecase/cloudnative-database","docId":"CurveBS/usecase/cloudnative-database"},{"type":"link","label":"CurveBS RDMA+SPDK \u7248\u672c\u5728\u7f51\u6613\u6570\u636e\u5e93\u573a\u666f\u7684\u843d\u5730\u5b9e\u8df5","href":"/CurveBS/usecase/rdma-spdk","docId":"CurveBS/usecase/rdma-spdk"},{"type":"link","label":"\u521b\u4e91\u878d\u8fbe\u57fa\u4e8eCurve\u5757\u5b58\u50a8\u7684\u8d85\u878d\u5408\u573a\u666f\u5b9e\u8df5","href":"/CurveBS/usecase/chuangyun","docId":"CurveBS/usecase/chuangyun"},{"type":"link","label":"\u626c\u5dde\u4e07\u65b9\u57fa\u4e8e\u7533\u5a01\u5e73\u53f0\u7684Curve\u5757\u5b58\u50a8\u5728\u9ad8\u6027\u80fd\u548c\u8d85\u878d\u5408\u573a\u666f\u4e0b\u7684\u5b9e\u8df5","href":"/CurveBS/usecase/wanfang","docId":"CurveBS/usecase/wanfang"}],"href":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b"},{"type":"category","label":"\u90e8\u7f72","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveBS \u5feb\u901f\u4f53\u9a8c","href":"/CurveBS/deploy/quickstart","docId":"CurveBS/deploy/quickstart"},{"type":"link","label":"\u7269\u7406\u673a\u90e8\u7f72 CurveBS \u96c6\u7fa4","href":"/CurveBS/deploy/deploy-with-curveadm","docId":"CurveBS/deploy/deploy-with-curveadm"},{"type":"link","label":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveBS/deploy/deploy-with-k8s-operator","docId":"CurveBS/deploy/deploy-with-k8s-operator"},{"type":"link","label":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveBS/deploy/offline-deploy","docId":"CurveBS/deploy/offline-deploy"},{"type":"link","label":"CurveBS CSI \u9a71\u52a8\u7a0b\u5e8f","href":"/CurveBS/deploy/csi","docId":"CurveBS/deploy/csi"},{"type":"link","label":"iSCSI \u534f\u8bae\u652f\u6301","href":"/CurveBS/deploy/iscsi","docId":"CurveBS/deploy/iscsi"},{"type":"link","label":"OpenStack\u4e2dCurveBS\u7684Ceph RBD\u517c\u5bb9\u5c42\u5b9e\u73b0","href":"/CurveBS/deploy/rbd","docId":"CurveBS/deploy/rbd"},{"type":"link","label":"OpenStack \u5bf9\u63a5","href":"/CurveBS/deploy/openstack","docId":"CurveBS/deploy/openstack"},{"type":"link","label":"CurveBS RDMA+SPDK \u7248\u672c\u90e8\u7f72","href":"/CurveBS/deploy/rdma-spdk","docId":"CurveBS/deploy/rdma-spdk"},{"type":"link","label":"CurveBS \u63a7\u5236\u53f0\u90e8\u7f72","href":"/CurveBS/deploy/dashboard","docId":"CurveBS/deploy/dashboard"}],"href":"/category/\u90e8\u7f72"},{"type":"category","label":"\u6d4b\u8bd5","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","href":"/CurveBS/test/env-setup","docId":"CurveBS/test/env-setup"},{"type":"link","label":"\u6982\u8ff0","href":"/CurveBS/test/ci\u4ecb\u7ecd","docId":"CurveBS/test/ci\u4ecb\u7ecd"},{"type":"link","label":"curve\u8986\u76d6\u7387\u6536\u96c6\u4f18\u5316","href":"/CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","docId":"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6"},{"type":"link","label":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","href":"/CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","docId":"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5"},{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5","href":"/CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","docId":"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177"},{"type":"link","label":"Curve \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","href":"/CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","docId":"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f"},{"type":"link","label":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","href":"/CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","docId":"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5"}],"href":"/category/\u6d4b\u8bd5"},{"type":"category","label":"\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveBS\u5bf9\u6bd4Ceph RBD","href":"/CurveBS/comparison/CurveBS-vs-Ceph-rbd","docId":"CurveBS/comparison/CurveBS-vs-Ceph-rbd"}],"href":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4"},{"type":"category","label":"\u67b6\u6784","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/architecture-intro","docId":"CurveBS/architecture/architecture-intro"},{"type":"link","label":"CurveBS Chunkserver \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/chunkserver-arch","docId":"CurveBS/architecture/chunkserver-arch"},{"type":"link","label":"CurveBS Client \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/client-arch","docId":"CurveBS/architecture/client-arch"},{"type":"link","label":"CurveBS MDS \u67b6\u6784\u4ecb\u7ecd","href":"/CurveBS/architecture/mds","docId":"CurveBS/architecture/mds"},{"type":"link","label":"CurveBS \u5feb\u7167\u514b\u9686\u67b6\u6784\u8bbe\u8ba1","href":"/CurveBS/architecture/snapshot","docId":"CurveBS/architecture/snapshot"},{"type":"link","label":"\u5ba2\u6237\u7aef\u70ed\u5347\u7ea7\u8bbe\u8ba1\u4ecb\u7ecd","href":"/CurveBS/architecture/nebd","docId":"CurveBS/architecture/nebd"}],"href":"/category/\u67b6\u6784"},{"type":"category","label":"\u6027\u80fd","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"benchmark\u6307\u5357","href":"/CurveBS/performance/how-to-benchmark","docId":"CurveBS/performance/how-to-benchmark"}],"href":"/category/\u6027\u80fd"},{"type":"category","label":"\u8fd0\u7ef4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u547d\u4ee4\u884c\u5de5\u5177","href":"/CurveBS/maintenance/command-line-tools","docId":"CurveBS/maintenance/command-line-tools"},{"type":"link","label":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","href":"/CurveBS/maintenance/administrator-guide","docId":"CurveBS/maintenance/administrator-guide"}],"href":"/category/\u8fd0\u7ef4"},{"type":"category","label":"\u63a5\u53e3\u6587\u6863","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"snapshotcloneserver interface","href":"/CurveBS/interface/snapshotcloneserver_interface","docId":"CurveBS/interface/snapshotcloneserver_interface"},{"type":"link","label":"localsnapshotclone restful api","href":"/CurveBS/interface/localsnapshotclone_restful_api","docId":"CurveBS/interface/localsnapshotclone_restful_api"},{"type":"link","label":"localsnapshotclone tools api","href":"/CurveBS/interface/localsnapshotclone_tools_api","docId":"CurveBS/interface/localsnapshotclone_tools_api"}],"href":"/category/\u63a5\u53e3\u6587\u6863"}],"href":"/category/curve\u5757\u5b58\u50a8"},{"type":"category","label":"Curve\u6587\u4ef6\u5b58\u50a8","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u5e94\u7528\u573a\u666f","href":"/CurveFS/usecase/scenario","docId":"CurveFS/usecase/scenario"},{"type":"link","label":"\u6c5f\u82cf\u519c\u4fe1\u9009\u62e9CurveFS\u5b9e\u73b0ES\u51b7\u6570\u636e\u957f\u671f\u5b58\u50a8","href":"/CurveFS/usecase/jiangsu-nongxin-es","docId":"CurveFS/usecase/jiangsu-nongxin-es"},{"type":"link","label":"\u57fa\u4e8eCurveFS+Samba\u652f\u6301SMB\u534f\u8bae","href":"/CurveFS/usecase/smb-support","docId":"CurveFS/usecase/smb-support"},{"type":"link","label":"\u516c\u6709\u4e91\u4e0a\u4f7f\u7528CurveFS\u66ff\u6362HDFS\u51b7\u5b58\u50a8\u5b9e\u73b0\u964d\u672c","href":"/CurveFS/usecase/hadoop-on-cloud","docId":"CurveFS/usecase/hadoop-on-cloud"},{"type":"link","label":"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301","href":"/CurveFS/usecase/s3-gateway","docId":"CurveFS/usecase/s3-gateway"},{"type":"link","label":"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528","href":"/CurveFS/usecase/elasticsearch-cold-data","docId":"CurveFS/usecase/elasticsearch-cold-data"},{"type":"link","label":"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548","href":"/CurveFS/usecase/ai-storage","docId":"CurveFS/usecase/ai-storage"},{"type":"link","label":"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42","href":"/CurveFS/usecase/asr-storage","docId":"CurveFS/usecase/asr-storage"}],"href":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1"},{"type":"category","label":"\u90e8\u7f72","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u5feb\u901f\u4f53\u9a8c","href":"/CurveFS/deploy/quickstart","docId":"CurveFS/deploy/quickstart"},{"type":"link","label":"\u7269\u7406\u673a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/deploy-with-curveadm","docId":"CurveFS/deploy/deploy-with-curveadm"},{"type":"link","label":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/deploy-with-k8s-operator","docId":"CurveFS/deploy/deploy-with-k8s-operator"},{"type":"link","label":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","href":"/CurveFS/deploy/offline-deploy","docId":"CurveFS/deploy/offline-deploy"},{"type":"link","label":"\u4f7f\u7528 CurveAdm \u90e8\u7f72\u5206\u5e03\u5f0f\u7f13\u5b58","href":"/CurveFS/deploy/distributed-cache","docId":"CurveFS/deploy/distributed-cache"},{"type":"link","label":"CurveFS CSI \u9a71\u52a8\u7a0b\u5e8f","href":"/CurveFS/deploy/csi","docId":"CurveFS/deploy/csi"},{"type":"link","label":"\u9759\u6001PV\u914d\u7f6e","href":"/CurveFS/deploy/static-pv","docId":"CurveFS/deploy/static-pv"}],"href":"/category/\u90e8\u7f72-1"},{"type":"category","label":"\u6d4b\u8bd5","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","href":"/CurveFS/test/env-setup","docId":"CurveFS/test/env-setup"},{"type":"link","label":"\u6982\u8ff0","href":"/CurveFS/test/ci\u4ecb\u7ecd","docId":"CurveFS/test/ci\u4ecb\u7ecd"},{"type":"link","label":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","href":"/CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","docId":"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5"},{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5","href":"/CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","docId":"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177"},{"type":"link","label":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","href":"/CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","docId":"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5"}],"href":"/category/\u6d4b\u8bd5-1"},{"type":"category","label":"\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u5bf9\u6bd4\u5176\u4ed6FS","href":"/CurveFS/comparison/CurveFS-vs-Other-FS","docId":"CurveFS/comparison/CurveFS-vs-Other-FS"}],"href":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1"},{"type":"category","label":"\u67b6\u6784","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"CurveFS \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/architecture-intro","docId":"CurveFS/architecture/architecture-intro"},{"type":"link","label":"CurveFS Client \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/client-arch","docId":"CurveFS/architecture/client-arch"},{"type":"link","label":"CurveFS MetaServer \u67b6\u6784\u4ecb\u7ecd","href":"/CurveFS/architecture/metaserver-arch","docId":"CurveFS/architecture/metaserver-arch"}],"href":"/category/\u67b6\u6784-1"},{"type":"category","label":"\u6027\u80fd","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u6027\u80fd\u6d4b\u8bd5\u6307\u5357","href":"/CurveFS/performance/how-to-benchmark","docId":"CurveFS/performance/how-to-benchmark"},{"type":"link","label":"\u6027\u80fd\u6307\u6807","href":"/CurveFS/performance/benchmark","docId":"CurveFS/performance/benchmark"},{"type":"link","label":"\u6027\u80fd\u8c03\u4f18","href":"/CurveFS/performance/perf-tune","docId":"CurveFS/performance/perf-tune"}],"href":"/category/\u6027\u80fd-1"},{"type":"category","label":"\u8fd0\u7ef4","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u547d\u4ee4\u884c\u5de5\u5177","href":"/CurveFS/maintenance/command-line-tools","docId":"CurveFS/maintenance/command-line-tools"},{"type":"link","label":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","href":"/CurveFS/maintenance/administrator-guide","docId":"CurveFS/maintenance/administrator-guide"},{"type":"link","label":"\u8fc1\u79fb\u6570\u636e\u5230 CurveFS","href":"/CurveFS/maintenance/data-migration","docId":"CurveFS/maintenance/data-migration"}],"href":"/category/\u8fd0\u7ef4-1"}],"href":"/category/curve\u6587\u4ef6\u5b58\u50a8"},{"type":"category","label":"\u5f00\u53d1\u8005\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u53c2\u4e0e\u8d21\u732e","href":"/Develop/how-to-contribute","docId":"Develop/how-to-contribute"},{"type":"link","label":"\u7f16\u8bd1\u548c\u6d4b\u8bd5","href":"/Develop/build-and-test","docId":"Develop/build-and-test"},{"type":"link","label":"\u4ee3\u7801\u8d70\u8bfb","href":"/Develop/code-walkthrough","docId":"Develop/code-walkthrough"}],"href":"/category/\u5f00\u53d1\u8005\u76f8\u5173"},{"type":"category","label":"\u793e\u533a\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u793e\u533a\u51c6\u5219","href":"/Community/community-guideline","docId":"Community/community-guideline"},{"type":"category","label":"\u53cc\u5468\u4f1a","collapsible":true,"collapsed":true,"items":[{"type":"category","label":"2023","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"README","href":"/Community/Double-Week-Meetings/01-2023/README","docId":"Community/Double-Week-Meetings/01-2023/README"},{"type":"link","label":"2023-01-12","href":"/Community/Double-Week-Meetings/01-2023/2023-01-12","docId":"Community/Double-Week-Meetings/01-2023/2023-01-12"},{"type":"link","label":"2023-02-09","href":"/Community/Double-Week-Meetings/01-2023/2023-02-09","docId":"Community/Double-Week-Meetings/01-2023/2023-02-09"},{"type":"link","label":"2023-02-23","href":"/Community/Double-Week-Meetings/01-2023/2023-02-23","docId":"Community/Double-Week-Meetings/01-2023/2023-02-23"},{"type":"link","label":"2023-03-09","href":"/Community/Double-Week-Meetings/01-2023/2023-03-09","docId":"Community/Double-Week-Meetings/01-2023/2023-03-09"},{"type":"link","label":"2023-03-23","href":"/Community/Double-Week-Meetings/01-2023/2023-03-23","docId":"Community/Double-Week-Meetings/01-2023/2023-03-23"},{"type":"link","label":"2023-04-13","href":"/Community/Double-Week-Meetings/01-2023/2023-04-13","docId":"Community/Double-Week-Meetings/01-2023/2023-04-13"},{"type":"link","label":"2023-04-27","href":"/Community/Double-Week-Meetings/01-2023/2023-04-27","docId":"Community/Double-Week-Meetings/01-2023/2023-04-27"},{"type":"link","label":"2023-05-18","href":"/Community/Double-Week-Meetings/01-2023/2023-05-18","docId":"Community/Double-Week-Meetings/01-2023/2023-05-18"},{"type":"link","label":"2023-06-01","href":"/Community/Double-Week-Meetings/01-2023/2023-06-01","docId":"Community/Double-Week-Meetings/01-2023/2023-06-01"},{"type":"link","label":"2023-06-15","href":"/Community/Double-Week-Meetings/01-2023/2023-06-15","docId":"Community/Double-Week-Meetings/01-2023/2023-06-15"},{"type":"link","label":"2023-06-29","href":"/Community/Double-Week-Meetings/01-2023/2023-06-29","docId":"Community/Double-Week-Meetings/01-2023/2023-06-29"},{"type":"link","label":"2023-07-13","href":"/Community/Double-Week-Meetings/01-2023/2023-07-13","docId":"Community/Double-Week-Meetings/01-2023/2023-07-13"},{"type":"link","label":"2023-08-03","href":"/Community/Double-Week-Meetings/01-2023/2023-08-03","docId":"Community/Double-Week-Meetings/01-2023/2023-08-03"},{"type":"link","label":"2023-09-14","href":"/Community/Double-Week-Meetings/01-2023/2023-09-14","docId":"Community/Double-Week-Meetings/01-2023/2023-09-14"},{"type":"link","label":"2023-10-12","href":"/Community/Double-Week-Meetings/01-2023/2023-10-12","docId":"Community/Double-Week-Meetings/01-2023/2023-10-12"}],"href":"/category/2023"}],"href":"/category/\u53cc\u5468\u4f1a"}],"href":"/category/\u793e\u533a\u76f8\u5173"},{"type":"category","label":"\u7248\u672c\u76f8\u5173","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"\u7248\u672c\u53d1\u5e03\u5468\u671f","href":"/Release/release-intro","docId":"Release/release-intro"},{"type":"link","label":"CHANGELOG of v2.6","href":"/Release/release-notes-v2.6","docId":"Release/release-notes-v2.6"},{"type":"link","label":"CHANGELOG of v1.5","href":"/Release/release-notes-v1.5","docId":"Release/release-notes-v1.5"}],"href":"/category/\u7248\u672c\u76f8\u5173"},{"type":"link","label":"FAQ","href":"/faq","docId":"FAQ"},{"type":"category","label":"Roadmap","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Curve Roadmap 2023","href":"/Roadmap/roadmap-2023","docId":"Roadmap/roadmap-2023"}],"href":"/category/roadmap"}]},"docs":{"Community/community-guideline":{"id":"Community/community-guideline","title":"\u793e\u533a\u51c6\u5219","description":"\u793e\u533a\u6cbb\u7406","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-01-12":{"id":"Community/Double-Week-Meetings/01-2023/2023-01-12","title":"2023-01-12","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-02-09":{"id":"Community/Double-Week-Meetings/01-2023/2023-02-09","title":"2023-02-09","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-02-23":{"id":"Community/Double-Week-Meetings/01-2023/2023-02-23","title":"2023-02-23","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-03-09":{"id":"Community/Double-Week-Meetings/01-2023/2023-03-09","title":"2023-03-09","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-03-23":{"id":"Community/Double-Week-Meetings/01-2023/2023-03-23","title":"2023-03-23","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-04-13":{"id":"Community/Double-Week-Meetings/01-2023/2023-04-13","title":"2023-04-13","description":"\x3c!--","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-04-27":{"id":"Community/Double-Week-Meetings/01-2023/2023-04-27","title":"2023-04-27","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-05-18":{"id":"Community/Double-Week-Meetings/01-2023/2023-05-18","title":"2023-05-18","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-01":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-01","title":"2023-06-01","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-15":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-15","title":"2023-06-15","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-06-29":{"id":"Community/Double-Week-Meetings/01-2023/2023-06-29","title":"2023-06-29","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-07-13":{"id":"Community/Double-Week-Meetings/01-2023/2023-07-13","title":"2023-07-13","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-08-03":{"id":"Community/Double-Week-Meetings/01-2023/2023-08-03","title":"2023-08-03","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-09-14":{"id":"Community/Double-Week-Meetings/01-2023/2023-09-14","title":"2023-09-14","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/2023-10-12":{"id":"Community/Double-Week-Meetings/01-2023/2023-10-12","title":"2023-10-12","description":"\u65f6\u95f4","sidebar":"tutorialSidebar"},"Community/Double-Week-Meetings/01-2023/README":{"id":"Community/Double-Week-Meetings/01-2023/README","title":"README","description":"Curve \u53cc\u5468\u4f1a","sidebar":"tutorialSidebar"},"CurveBS/architecture/architecture-intro":{"id":"CurveBS/architecture/architecture-intro","title":"\u67b6\u6784\u4ecb\u7ecd","description":"\u8bf7\u53c2\u8003Curve\u8bbe\u8ba1\u8981\u70b9","sidebar":"tutorialSidebar"},"CurveBS/architecture/chunkserver-arch":{"id":"CurveBS/architecture/chunkserver-arch","title":"CurveBS Chunkserver \u67b6\u6784\u4ecb\u7ecd","description":"1 ChunkServer \u6982\u8ff0","sidebar":"tutorialSidebar"},"CurveBS/architecture/client-arch":{"id":"CurveBS/architecture/client-arch","title":"CurveBS Client \u67b6\u6784\u4ecb\u7ecd","description":"1 \u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/mds":{"id":"CurveBS/architecture/mds","title":"CurveBS MDS \u67b6\u6784\u4ecb\u7ecd","description":"\u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/nebd":{"id":"CurveBS/architecture/nebd","title":"\u5ba2\u6237\u7aef\u70ed\u5347\u7ea7\u8bbe\u8ba1\u4ecb\u7ecd","description":"\u6982\u8981","sidebar":"tutorialSidebar"},"CurveBS/architecture/snapshot":{"id":"CurveBS/architecture/snapshot","title":"CurveBS \u5feb\u7167\u514b\u9686\u67b6\u6784\u8bbe\u8ba1","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/comparison/CurveBS-vs-Ceph-rbd":{"id":"CurveBS/comparison/CurveBS-vs-Ceph-rbd","title":"CurveBS\u5bf9\u6bd4Ceph RBD","description":"\u9ad8\u6027\u80fd | \u66f4\u7a33\u5b9a | \u6613\u8fd0\u7ef4 | \u4e91\u539f\u751f","sidebar":"tutorialSidebar"},"CurveBS/deploy/csi":{"id":"CurveBS/deploy/csi","title":"CurveBS CSI \u9a71\u52a8\u7a0b\u5e8f","description":"\u7b80\u4ecb","sidebar":"tutorialSidebar"},"CurveBS/deploy/dashboard":{"id":"CurveBS/deploy/dashboard","title":"CurveBS \u63a7\u5236\u53f0\u90e8\u7f72","description":"\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveBS/deploy/deploy-with-curveadm":{"id":"CurveBS/deploy/deploy-with-curveadm","title":"\u7269\u7406\u673a\u90e8\u7f72 CurveBS \u96c6\u7fa4","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/deploy-with-k8s-operator":{"id":"CurveBS/deploy/deploy-with-k8s-operator","title":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"curve-operator\u4ee3\u7801\u4ed3\u5e93\uff1ahttps://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md","sidebar":"tutorialSidebar"},"CurveBS/deploy/iscsi":{"id":"CurveBS/deploy/iscsi","title":"iSCSI \u534f\u8bae\u652f\u6301","description":"\u76ee\u524d CurveBS \u652f\u6301\u4e24\u79cd iscsi target\u670d\u52a1\uff0c\u8bf7\u9009\u62e9\u5408\u9002\u7684\u8fdb\u884c\u90e8\u7f72\u4f7f\u7528\uff1a","sidebar":"tutorialSidebar"},"CurveBS/deploy/offline-deploy":{"id":"CurveBS/deploy/offline-deploy","title":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u672c\u6587\u9002\u7528\u4e8e\u65e0\u6cd5\u4ece\u516c\u7f51docker\u955c\u50cf\u4ed3\u5e93\u62c9\u53d6CurveFS docker\u955c\u50cf\u573a\u666f\u4e0b\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/openstack":{"id":"CurveBS/deploy/openstack","title":"OpenStack \u5bf9\u63a5","description":"1. Cinder\u5bf9\u63a5\u8bf7\u53c2\u8003\uff1ahttps://github.com/opencurve/curve-cinder","sidebar":"tutorialSidebar"},"CurveBS/deploy/quickstart":{"id":"CurveBS/deploy/quickstart","title":"CurveBS \u5feb\u901f\u4f53\u9a8c","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveBS/deploy/rbd":{"id":"CurveBS/deploy/rbd","title":"OpenStack\u4e2dCurveBS\u7684Ceph RBD\u517c\u5bb9\u5c42\u5b9e\u73b0","description":"\u80cc\u666f\u4e0e\u76ee\u6807","sidebar":"tutorialSidebar"},"CurveBS/deploy/rdma-spdk":{"id":"CurveBS/deploy/rdma-spdk","title":"CurveBS RDMA+SPDK \u7248\u672c\u90e8\u7f72","description":"\u7cfb\u7edf\u73af\u5883","sidebar":"tutorialSidebar"},"CurveBS/interface/localsnapshotclone_restful_api":{"id":"CurveBS/interface/localsnapshotclone_restful_api","title":"localsnapshotclone restful api","description":"localsnapshotclone restful api \u9700\u517c\u5bb9\u539f\u6709s3\u5feb\u7167api: snapshotcloneserverinterface, \u4f7f\u7528\u4e24\u5957API\u5747\u53ef\u4ee5\u8c03\u7528\u672c\u5730\u5feb\u7167\u548c\u514b\u9686\u529f\u80fd,","sidebar":"tutorialSidebar"},"CurveBS/interface/localsnapshotclone_tools_api":{"id":"CurveBS/interface/localsnapshotclone_tools_api","title":"localsnapshotclone tools api","description":"\u672c\u5730\u5feb\u7167\u548c\u514b\u9686\u91c7\u7528tools-v2\u5de5\u5177\u547d\u4ee4\u884c\u65b9\u5f0f\uff0c\u4f7f\u7528\u65b9\u6cd5\u5982\u4e0b\uff1a","sidebar":"tutorialSidebar"},"CurveBS/interface/snapshotcloneserver_interface":{"id":"CurveBS/interface/snapshotcloneserver_interface","title":"snapshotcloneserver interface","description":"\u521b\u5efa\u5feb\u7167\uff1a","sidebar":"tutorialSidebar"},"CurveBS/maintenance/administrator-guide":{"id":"CurveBS/maintenance/administrator-guide","title":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","description":"\u65e5\u5e38\u8fd0\u7ef4\u64cd\u4f5c","sidebar":"tutorialSidebar"},"CurveBS/maintenance/command-line-tools":{"id":"CurveBS/maintenance/command-line-tools","title":"\u547d\u4ee4\u884c\u5de5\u5177","description":"Curve \u547d\u4ee4\u884c\u5de5\u5177\u4ee3\u7801\u4ed3\u5e93\u5730\u5740\uff1ahttps://github.com/opencurve/curve/blob/master/tools-v2/README.md","sidebar":"tutorialSidebar"},"CurveBS/performance/how-to-benchmark":{"id":"CurveBS/performance/how-to-benchmark","title":"benchmark\u6307\u5357","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/test/ci\u4ecb\u7ecd":{"id":"CurveBS/test/ci\u4ecb\u7ecd","title":"\u6982\u8ff0","description":"\u672c\u6587\u5c06\u4e3b\u8981\u4ecb\u7ecd\u793e\u533a\u4eba\u5458\u5728\u53c2\u4e0ecurve\u9879\u76ee\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u5982\u4f55\u5feb\u901f\u63d0\u4ea4PR\u5e76\u8fdb\u884cCI\u6d4b\u8bd5\uff0c\u4ee5\u53ca\u5feb\u901f\u5b9a\u4f4dCI\u5931\u8d25\u539f\u56e0\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6":{"id":"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","title":"curve\u8986\u76d6\u7387\u6536\u96c6\u4f18\u5316","description":"curve\u4e3b\u8981\u4ece2\u4e2a\u7ef4\u5ea6\u8861\u91cf\u5355\u5143\u6d4b\u8bd5\u8986\u76d6\u7387\uff1a\u884c\u8986\u76d6\u7387\u548c\u5206\u652f\u8986\u76d6\u7387\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/env-setup":{"id":"CurveBS/test/env-setup","title":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","description":"\u8bf7\u53c2\u8003 \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","sidebar":"tutorialSidebar"},"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5":{"id":"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","title":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","description":"\u6d4b\u8bd5\u76ee\u7684","sidebar":"tutorialSidebar"},"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177":{"id":"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","title":"\u6027\u80fd\u6d4b\u8bd5","description":"\u80cc\u666f\u77e5\u8bc6","sidebar":"tutorialSidebar"},"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f":{"id":"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","title":"Curve \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","description":"Curve \u5f53\u524d\u6d4b\u8bd5\u7684\u6027\u80fd\uff0c\u5982\u679c\u6ca1\u6709\u7279\u522b\u8bf4\u660e\uff0c\u90fd\u662f\u5728\u57fa\u4e8e\u4ee5\u4e0b\u914d\u7f6e\u7684\u673a\u5668\u4e0a\u6d4b\u8bd5\u7684\u3002\u8be5\u914d\u7f6e\u4ec5\u4ec5\u8868\u793acurve\u5f53\u524d\u6027\u80fd\u6570\u636e\u7684\u6d4b\u8bd5\u73af\u5883\uff0c\u7531\u4e8e\u90e8\u5206\u786c\u4ef6\u7248\u672c\u6bd4\u8f83\u65e7\uff0c\u8be5\u73af\u5883\u7684\u786c\u4ef6\u4e0d\u4f5c\u4e3a\u63a8\u8350\u786c\u4ef6\u578b\u53f7\uff0c\u4f46\u662f\u5bf9\u786c\u4ef6\u7684\u914d\u7f6e\u90e8\u5206\u53ef\u505a\u53c2\u8003\u3002","sidebar":"tutorialSidebar"},"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5":{"id":"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","title":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","description":"\u8bf7\u6ce8\u610f\uff1a","sidebar":"tutorialSidebar"},"CurveBS/usecase/chuangyun":{"id":"CurveBS/usecase/chuangyun","title":"\u521b\u4e91\u878d\u8fbe\u57fa\u4e8eCurve\u5757\u5b58\u50a8\u7684\u8d85\u878d\u5408\u573a\u666f\u5b9e\u8df5","description":"1. \u521b\u4e91\u878d\u8fbe\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveBS/usecase/cloudnative-database":{"id":"CurveBS/usecase/cloudnative-database","title":"\u9ad8\u6027\u80fd\u4e91\u539f\u751f\u6570\u636e\u5e93\u5b58\u50a8\u5e95\u5ea7","description":"\u524d\u8a00","sidebar":"tutorialSidebar"},"CurveBS/usecase/rdma-spdk":{"id":"CurveBS/usecase/rdma-spdk","title":"CurveBS RDMA+SPDK \u7248\u672c\u5728\u7f51\u6613\u6570\u636e\u5e93\u573a\u666f\u7684\u843d\u5730\u5b9e\u8df5","description":"TBD","sidebar":"tutorialSidebar"},"CurveBS/usecase/scenario":{"id":"CurveBS/usecase/scenario","title":"\u5e94\u7528\u573a\u666f","description":"CurveBS\u7684\u6838\u5fc3\u5e94\u7528\u573a\u666f\u4e3b\u8981\u5305\u62ec\uff1a","sidebar":"tutorialSidebar"},"CurveBS/usecase/wanfang":{"id":"CurveBS/usecase/wanfang","title":"\u626c\u5dde\u4e07\u65b9\u57fa\u4e8e\u7533\u5a01\u5e73\u53f0\u7684Curve\u5757\u5b58\u50a8\u5728\u9ad8\u6027\u80fd\u548c\u8d85\u878d\u5408\u573a\u666f\u4e0b\u7684\u5b9e\u8df5","description":"1. \u626c\u5dde\u4e07\u65b9\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/architecture/architecture-intro":{"id":"CurveFS/architecture/architecture-intro","title":"CurveFS \u67b6\u6784\u4ecb\u7ecd","description":"CurveFS \u662f POSIX \u517c\u5bb9\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\uff0c\u7528\u4e8e\u66f4\u597d\u7684\u652f\u6301\u4e91\u539f\u751f\u573a\u666f\u3002","sidebar":"tutorialSidebar"},"CurveFS/architecture/client-arch":{"id":"CurveFS/architecture/client-arch","title":"CurveFS Client \u67b6\u6784\u4ecb\u7ecd","description":"1 \u6982\u8981","sidebar":"tutorialSidebar"},"CurveFS/architecture/metaserver-arch":{"id":"CurveFS/architecture/metaserver-arch","title":"CurveFS MetaServer \u67b6\u6784\u4ecb\u7ecd","description":"\u6982\u8ff0","sidebar":"tutorialSidebar"},"CurveFS/comparison/CurveFS-vs-Other-FS":{"id":"CurveFS/comparison/CurveFS-vs-Other-FS","title":"CurveFS \u5bf9\u6bd4\u5176\u4ed6FS","description":"CurveFS \u4e0e\u5176\u4ed6\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\u7684\u533a\u522b\u53ef\u4ee5\u53c2\u8003 FAQ \u4e2d\u7684\u7b80\u5355\u6bd4\u8f83\uff0c\u5982\u9700\u4e86\u89e3\u8be6\u7ec6\u5dee\u5f02\uff0c\u8bf7\u6309\u7167\u5982\u4e0b \u8054\u7cfb\u65b9\u5f0f \u4e0e Curve \u56e2\u961f\u8fdb\u884c\u4ea4\u6d41\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/csi":{"id":"CurveFS/deploy/csi","title":"CurveFS CSI \u9a71\u52a8\u7a0b\u5e8f","description":"\u7b80\u4ecb","sidebar":"tutorialSidebar"},"CurveFS/deploy/deploy-with-curveadm":{"id":"CurveFS/deploy/deploy-with-curveadm","title":"\u7269\u7406\u673a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/deploy-with-k8s-operator":{"id":"CurveFS/deploy/deploy-with-k8s-operator","title":"Kubernetes \u4e0a\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"curve-operator\u4ee3\u7801\u4ed3\u5e93\uff1ahttps://github.com/opencurve/curve-operator/blob/master/docs/readme_cn.md","sidebar":"tutorialSidebar"},"CurveFS/deploy/distributed-cache":{"id":"CurveFS/deploy/distributed-cache","title":"\u4f7f\u7528 CurveAdm \u90e8\u7f72\u5206\u5e03\u5f0f\u7f13\u5b58","description":"\u5206\u5e03\u5f0f\u7f13\u5b58\u4ecb\u7ecd","sidebar":"tutorialSidebar"},"CurveFS/deploy/offline-deploy":{"id":"CurveFS/deploy/offline-deploy","title":"\u79bb\u7ebf\u90e8\u7f72 CurveFS \u96c6\u7fa4","description":"\u672c\u6587\u9002\u7528\u4e8e\u65e0\u6cd5\u4ece\u516c\u7f51docker\u955c\u50cf\u4ed3\u5e93\u62c9\u53d6CurveFS docker\u955c\u50cf\u573a\u666f\u4e0b\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/quickstart":{"id":"CurveFS/deploy/quickstart","title":"CurveFS \u5feb\u901f\u4f53\u9a8c","description":"\u4e3a\u4e86\u63d0\u5347 Curve \u7684\u8fd0\u7ef4\u4fbf\u5229\u6027\uff0c\u6211\u4eec\u8bbe\u8ba1\u5f00\u53d1\u4e86 CurveAdm \u9879\u76ee\uff0c\u5176\u4e3b\u8981\u7528\u4e8e\u90e8\u7f72\u548c\u7ba1\u7406 Curve \u96c6\u7fa4\uff0c\u76ee\u524d\u5df2\u652f\u6301\u90e8\u7f72CurveBS & CurveFS\uff0c\u76f8\u5173\u4f7f\u7528\u6587\u6863\u8bf7\u53c2\u8003 CurveAdm\u7528\u6237\u624b\u518c\uff0c\u5e76\u6839\u636e\u624b\u518c\u9996\u5148\u5b89\u88c5CurveAdm\u5de5\u5177\u4e4b\u540e\u518d\u8fdb\u884cCurve\u96c6\u7fa4\u7684\u90e8\u7f72\u3002","sidebar":"tutorialSidebar"},"CurveFS/deploy/static-pv":{"id":"CurveFS/deploy/static-pv","title":"\u9759\u6001PV\u914d\u7f6e","description":"CurveFS CSI\u4f7f\u7528node\u8282\u70b9\u4e0a\u7684\u7f13\u5b58\u76d8","sidebar":"tutorialSidebar"},"CurveFS/maintenance/administrator-guide":{"id":"CurveFS/maintenance/administrator-guide","title":"\u7ba1\u7406\u5458\u64cd\u4f5c\u6307\u5bfc","description":"\u65e5\u5e38\u8fd0\u7ef4\u64cd\u4f5c","sidebar":"tutorialSidebar"},"CurveFS/maintenance/command-line-tools":{"id":"CurveFS/maintenance/command-line-tools","title":"\u547d\u4ee4\u884c\u5de5\u5177","description":"Curve \u547d\u4ee4\u884c\u5de5\u5177\u4ee3\u7801\u4ed3\u5e93\u5730\u5740\uff1ahttps://github.com/opencurve/curve/blob/master/tools-v2/README.md","sidebar":"tutorialSidebar"},"CurveFS/maintenance/data-migration":{"id":"CurveFS/maintenance/data-migration","title":"\u8fc1\u79fb\u6570\u636e\u5230 CurveFS","description":"CurveFS\u5728\u4e0d\u540c\u5e94\u7528\u573a\u666f\u843d\u5730\u4f7f\u7528\u65f6\uff0c\u4e0d\u53ef\u907f\u514d\u7684\u6d89\u53ca\u5230\u5c06\u539f\u7cfb\u7edf\u6570\u636e\u8fc1\u79fb\u5230CurveFS\uff0c\u7279\u522b\u662f\u5927\u6570\u636e\u548cAI\u76f8\u5173\u4e1a\u52a1\uff0c\u4ed6\u4eec\u6570\u636e\u91cf\u5de8\u5927\uff08\u5305\u62ec\u6d77\u91cf\u5c0f\u6587\u4ef6\uff09\uff0c\u5982\u4f55\u505a\u5230\u9ad8\u6548\u3001\u53ef\u9760\u7684\u6570\u636e\u8fc1\u79fb\u662f\u4e00\u5207\u7684\u5f00\u59cb\u3002","sidebar":"tutorialSidebar"},"CurveFS/performance/benchmark":{"id":"CurveFS/performance/benchmark","title":"\u6027\u80fd\u6307\u6807","description":"TBD","sidebar":"tutorialSidebar"},"CurveFS/performance/how-to-benchmark":{"id":"CurveFS/performance/how-to-benchmark","title":"\u6027\u80fd\u6d4b\u8bd5\u6307\u5357","description":"\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},"CurveFS/performance/perf-tune":{"id":"CurveFS/performance/perf-tune","title":"\u6027\u80fd\u8c03\u4f18","description":"TBD","sidebar":"tutorialSidebar"},"CurveFS/test/ci\u4ecb\u7ecd":{"id":"CurveFS/test/ci\u4ecb\u7ecd","title":"\u6982\u8ff0","description":"\u672c\u6587\u5c06\u4e3b\u8981\u4ecb\u7ecd\u793e\u533a\u4eba\u5458\u5728\u53c2\u4e0ecurve\u9879\u76ee\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u5982\u4f55\u5feb\u901f\u63d0\u4ea4PR\u5e76\u8fdb\u884cCI\u6d4b\u8bd5\uff0c\u4ee5\u53ca\u5feb\u901f\u5b9a\u4f4dCI\u5931\u8d25\u539f\u56e0\u3002","sidebar":"tutorialSidebar"},"CurveFS/test/env-setup":{"id":"CurveFS/test/env-setup","title":"\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","description":"\u8bf7\u53c2\u8003 \u6d4b\u8bd5\u73af\u5883\u914d\u7f6e","sidebar":"tutorialSidebar"},"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5":{"id":"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","title":"\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","description":"\u6d4b\u8bd5\u76ee\u7684","sidebar":"tutorialSidebar"},"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177":{"id":"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","title":"\u6027\u80fd\u6d4b\u8bd5","description":"\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5":{"id":"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","title":"\u7f16\u8bd1\u73af\u5883\u642d\u5efa","description":"\u8bf7\u6ce8\u610f\uff1a","sidebar":"tutorialSidebar"},"CurveFS/usecase/ai-storage":{"id":"CurveFS/usecase/ai-storage","title":"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548","description":"\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/usecase/asr-storage":{"id":"CurveFS/usecase/asr-storage","title":"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42","description":"\u4e91\u5546 ASR \u8bad\u7ec3\u6570\u636e\u5feb\u901f\u589e\u957f\u9762\u4e34\u56f0\u5883","sidebar":"tutorialSidebar"},"CurveFS/usecase/elasticsearch-cold-data":{"id":"CurveFS/usecase/elasticsearch-cold-data","title":"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528","description":"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca","sidebar":"tutorialSidebar"},"CurveFS/usecase/hadoop-on-cloud":{"id":"CurveFS/usecase/hadoop-on-cloud","title":"\u516c\u6709\u4e91\u4e0a\u4f7f\u7528CurveFS\u66ff\u6362HDFS\u51b7\u5b58\u50a8\u5b9e\u73b0\u964d\u672c","description":"\u672c\u6587\u8ba8\u8bba\u7684\u90e8\u7f72\u65b9\u5f0f\u4e3a\u57fa\u4e8eCurveFS\u6302\u8f7d\u70b9\u66ff\u6362\u516c\u6709\u4e91EBS\u7b49\u4e91\u76d8\uff0cCurveFS\u652f\u6301HDFS SDK\u529f\u80fd\u5c06\u4e8e2.7\u7248\u672c\u53d1\u5e03\uff0c\u5c4a\u65f6\u5c06\u63d0\u4f9b\u5b8c\u5168\u517c\u5bb9HDFS API\u7684SDK jar\u5305\u4f9b\u4e0a\u5c42\u5927\u6570\u636e\u5e94\u7528\uff08\u5982Spark\u3001Flink\u7b49\uff09\u4f7f\u7528\uff0c\u5c06\u4e0d\u518d\u9700\u8981CurveFS\u6302\u8f7d\u70b9\u3002","sidebar":"tutorialSidebar"},"CurveFS/usecase/jiangsu-nongxin-es":{"id":"CurveFS/usecase/jiangsu-nongxin-es","title":"\u6c5f\u82cf\u519c\u4fe1\u9009\u62e9CurveFS\u5b9e\u73b0ES\u51b7\u6570\u636e\u957f\u671f\u5b58\u50a8","description":"\u4e00\u3001\u9700\u6c42\u80cc\u666f","sidebar":"tutorialSidebar"},"CurveFS/usecase/s3-gateway":{"id":"CurveFS/usecase/s3-gateway","title":"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301","description":"\u672c\u6587\u7684\u90e8\u7f72\u65b9\u6848\u662f\u57fa\u4e8eCurveFS\u6302\u8f7d\u70b9+minio-s3-gateway\u65b9\u5f0f\uff0c\u540e\u7eed\u4f1a\u8003\u8651\u652f\u6301minio-s3-gateway+CurveFS SDK\u65b9\u5f0f\u90e8\u7f72\uff0c\u5c06\u4e0d\u518d\u9700\u8981\u4f9d\u8d56CurveFS\u6302\u8f7d\u70b9\u3002","sidebar":"tutorialSidebar"},"CurveFS/usecase/scenario":{"id":"CurveFS/usecase/scenario","title":"\u5e94\u7528\u573a\u666f","description":"CurveFS\u7684\u6838\u5fc3\u5e94\u7528\u573a\u666f\u4e3b\u8981\u5305\u62ec\uff1a","sidebar":"tutorialSidebar"},"CurveFS/usecase/smb-support":{"id":"CurveFS/usecase/smb-support","title":"\u57fa\u4e8eCurveFS+Samba\u652f\u6301SMB\u534f\u8bae","description":"\u672c\u6587\u5c06\u4f1a\u4ecb\u7ecdCurveFS\u4e0esamba\u4ee5\u53ca\u5982\u4f55\u5c06\u4ed6\u4eec\u7ed3\u5408\u4f7f\u7528, \u5e76\u4e14\u4ecb\u7ecdsamba\u7684\u7b2c\u4e09\u65b9moudle\u5f00\u53d1\u77e5\u8bc6\u3002","sidebar":"tutorialSidebar"},"Develop/build-and-test":{"id":"Develop/build-and-test","title":"\u7f16\u8bd1\u548c\u6d4b\u8bd5","description":"- \u7f16\u8bd1\u548c\u6d4b\u8bd5","sidebar":"tutorialSidebar"},"Develop/code-walkthrough":{"id":"Develop/code-walkthrough","title":"\u4ee3\u7801\u8d70\u8bfb","description":"\u8bf7\u53c2\u8003 Curve\u6e90\u7801\u53ca\u6838\u5fc3\u6d41\u7a0b\u6df1\u5ea6\u89e3\u8bfb","sidebar":"tutorialSidebar"},"Develop/how-to-contribute":{"id":"Develop/how-to-contribute","title":"\u53c2\u4e0e\u8d21\u732e","description":"\u5982\u4f55\u53c2\u4e0e Curve \u9879\u76ee\u5f00\u53d1\u8be6\u89c1 Curve \u5f00\u53d1\u8005\u6307\u5357\uff0c\u5e76\u4e14\u8bf7\u9075\u5faa\u8d21\u732e\u8005\u51c6\u5219\uff0c\u6211\u4eec\u671f\u5f85\u60a8\u7684\u8d21\u732e\uff01","sidebar":"tutorialSidebar"},"FAQ":{"id":"FAQ","title":"FAQ","description":"\u5173\u4e8eCurve","sidebar":"tutorialSidebar"},"Intro":{"id":"Intro","title":"\u5173\u4e8e Curve","description":"Curve \u662f\u7f51\u6613\u4e3b\u5bfc\u81ea\u7814\u7684\u73b0\u4ee3\u5316\u5b58\u50a8\u7cfb\u7edf, \u76ee\u524d\u652f\u6301\u6587\u4ef6\u5b58\u50a8(CurveFS)\u548c\u5757\u5b58\u50a8(CurveBS)\u3002\u73b0\u4f5c\u4e3a\u6c99\u7bb1\u9879\u76ee\u6258\u7ba1\u4e8eCNCF\u3002","sidebar":"tutorialSidebar"},"Release/release-intro":{"id":"Release/release-intro","title":"\u7248\u672c\u53d1\u5e03\u5468\u671f","description":"- CURVE\u7248\u672c\u53d1\u5e03\u5468\u671f\uff1a\u5927\u7248\u672c\u534a\u5e74\uff0c\u5c0f\u7248\u672c1~2\u4e2a\u6708","sidebar":"tutorialSidebar"},"Release/release-notes-v1.5":{"id":"Release/release-notes-v1.5","title":"CHANGELOG of v1.5","description":"Features","sidebar":"tutorialSidebar"},"Release/release-notes-v2.6":{"id":"Release/release-notes-v2.6","title":"CHANGELOG of v2.6","description":"Previous change logs can be found at CHANGELOG-2.5","sidebar":"tutorialSidebar"},"Roadmap/roadmap-2023":{"id":"Roadmap/roadmap-2023","title":"Curve Roadmap 2023","description":"\u8bf7\u53c2\u8003 Curve Roadmap 2023","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/afb57922.9930456c.js b/assets/js/afb57922.9930456c.js deleted file mode 100644 index 46e59a2..0000000 --- a/assets/js/afb57922.9930456c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[2222],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>k});var l=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);t&&(l=l.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,l)}return r}function u(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(l=0;l=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var c=l.createContext({}),p=function(e){var t=l.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},s=function(e){var t=p(e.components);return l.createElement(c.Provider,{value:t},e.children)},o="mdxType",v={inlineCode:"code",wrapper:function(e){var t=e.children;return l.createElement(l.Fragment,{},t)}},d=l.forwardRef((function(e,t){var r=e.components,a=e.mdxType,n=e.originalType,c=e.parentName,s=i(e,["components","mdxType","originalType","parentName"]),o=p(r),d=a,k=o["".concat(c,".").concat(d)]||o[d]||v[d]||n;return r?l.createElement(k,u(u({ref:t},s),{},{components:r})):l.createElement(k,u({ref:t},s))}));function k(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var n=r.length,u=new Array(n);u[0]=d;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i[o]="string"==typeof e?e:a,u[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>u,default:()=>v,frontMatter:()=>n,metadata:()=>i,toc:()=>p});var l=r(7462),a=(r(7294),r(3905));const n={},u="CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5",i={unversionedId:"CurveFS/usecase/ai-storage",id:"CurveFS/usecase/ai-storage",title:"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5",description:"\u4e00\u3001\u9879\u76ee\u80cc\u666f",source:"@site/docs/03-CurveFS/01-usecase/07-ai-storage.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/ai-storage",permalink:"/CurveFS/usecase/ai-storage",draft:!1,tags:[],version:"current",sidebarPosition:7,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",permalink:"/CurveFS/usecase/elasticsearch-cold-data"},next:{title:"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",permalink:"/CurveFS/usecase/asr-storage"}},c={},p=[{value:"\u4e00\u3001\u9879\u76ee\u80cc\u666f",id:"\u4e00\u9879\u76ee\u80cc\u666f",level:2},{value:"\u53ef\u6269\u5c55\u6027\u95ee\u9898",id:"\u53ef\u6269\u5c55\u6027\u95ee\u9898",level:3},{value:"\u6027\u80fd\u95ee\u9898",id:"\u6027\u80fd\u95ee\u9898",level:3},{value:"\u6210\u672c\u95ee\u9898",id:"\u6210\u672c\u95ee\u9898",level:3},{value:"\u4e8c\u3001\u9879\u76ee\u601d\u8def\u548c\u65b9\u6848",id:"\u4e8c\u9879\u76ee\u601d\u8def\u548c\u65b9\u6848",level:2},{value:"\u6587\u4ef6\u7cfb\u7edf\u7684\u5143\u6570\u636e\u72ec\u7acb\u5b58\u5b58\u50a8",id:"\u6587\u4ef6\u7cfb\u7edf\u7684\u5143\u6570\u636e\u72ec\u7acb\u5b58\u5b58\u50a8",level:3},{value:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u5b58\u50a8\u7684\u964d\u672c\u589e\u6548",id:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u5b58\u50a8\u7684\u964d\u672c\u589e\u6548",level:3},{value:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u548c\u5143\u6570\u636e\u90fd\u652f\u6301\u591a\u7ea7\u7f13\u5b58",id:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u548c\u5143\u6570\u636e\u90fd\u652f\u6301\u591a\u7ea7\u7f13\u5b58",level:3},{value:"\u6570\u636e\u7f13\u5b58",id:"\u6570\u636e\u7f13\u5b58",level:4},{value:"\u5143\u6570\u636e\u7f13\u5b58",id:"\u5143\u6570\u636e\u7f13\u5b58",level:4},{value:"\u6587\u4ef6\u7cfb\u7edf\u652f\u6301\u6570\u636e\u9884\u8bfb\u548c\u9884\u70ed",id:"\u6587\u4ef6\u7cfb\u7edf\u652f\u6301\u6570\u636e\u9884\u8bfb\u548c\u9884\u70ed",level:3},{value:"\u652f\u6301\u9884\u8bfb\uff08Prefetch\uff09",id:"\u652f\u6301\u9884\u8bfbprefetch",level:4},{value:"\u652f\u6301\u9884\u70ed\uff08warmup\uff09",id:"\u652f\u6301\u9884\u70edwarmup",level:4},{value:"\u4e09\u3001\u9879\u76ee\u5f71\u54cd\u529b\u548c\u4ea7\u51fa\u4ef7\u503c",id:"\u4e09\u9879\u76ee\u5f71\u54cd\u529b\u548c\u4ea7\u51fa\u4ef7\u503c",level:2},{value:"\u4e3a\u4e1a\u52a1\u964d\u672c\u63d0\u6548",id:"\u4e3a\u4e1a\u52a1\u964d\u672c\u63d0\u6548",level:3},{value:"\u56db\u3001\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",id:"\u56db\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b",level:2}],s={toc:p},o="wrapper";function v(e){let{components:t,...n}=e;return(0,a.kt)(o,(0,l.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"curvefs\u5728ai\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5"},"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5"),(0,a.kt)("h2",{id:"\u4e00\u9879\u76ee\u80cc\u666f"},"\u4e00\u3001\u9879\u76ee\u80cc\u666f"),(0,a.kt)("p",null,"\u5728\u5f53\u4eca\u5927\u6570\u636e\u548c\u4eba\u5de5\u667a\u80fd\u9886\u57df\u7684\u5feb\u901f\u53d1\u5c55\u4e2d\uff0c\u968f\u7740\u6570\u636e\u91cf\u7684\u7206\u70b8\u5f0f\u589e\u957f\uff0c\u5bf9\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\u7684\u5b58\u50a8\u53ef\u6269\u5c55\u6027\u3001\u6210\u672c\u548c\u6027\u80fd\u63d0\u51fa\u4e86\u66f4\u9ad8\u7684\u8981\u6c42\u3002\u5728\u6b64\u5927\u52bf\u6240\u8d8b\u4e4b\u4e0b\uff0c\u676d\u7814\u4e91\u8ba1\u7b97\u56e2\u961f\u5f00\u53d1\u4e86Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u6765\u89e3\u51b3\u8fd9\u4e9b\u95ee\u9898\u3002"),(0,a.kt)("p",null,"Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u65e8\u5728\u89e3\u51b3\u4ee5\u4e0b\u95ee\u9898\uff1a"),(0,a.kt)("h3",{id:"\u53ef\u6269\u5c55\u6027\u95ee\u9898"},"\u53ef\u6269\u5c55\u6027\u95ee\u9898"),(0,a.kt)("p",null,"\u968f\u7740\u6587\u4ef6\u6570\u91cf\u7684\u589e\u957f\uff0c\u73b0\u6709\u7684\u6587\u4ef6\u7cfb\u7edf\u5982CephFS\u548cHDFS\u7b49\u5143\u6570\u636e\u7684\u53ef\u6269\u5c55\u6027\u4e0d\u8db3\uff0c\u65e0\u6cd5\u6ee1\u8db3\u5927\u89c4\u6a21\u6587\u4ef6\u5b58\u50a8\u7684\u9700\u6c42\u3002Curve\u6587\u4ef6\u7cfb\u7edf\u5c06\u63d0\u4f9b\u53ef\u6269\u5c55\u7684\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\uff0c\u89e3\u51b3\u5143\u6570\u636e\u7ba1\u7406\u7684\u95ee\u9898\u3002"),(0,a.kt)("h3",{id:"\u6027\u80fd\u95ee\u9898"},"\u6027\u80fd\u95ee\u9898"),(0,a.kt)("p",null,"\u968f\u7740\u6587\u4ef6\u6570\u91cf\u7684\u589e\u52a0\uff0c\u6587\u4ef6\u5143\u6570\u636e\u7684\u6027\u80fd\u4f1a\u5927\u5e45\u4e0b\u964d\u3002\u6b64\u5916\uff0c\u5c0f\u6587\u4ef6\u7684\u8bfb\u5199\u6027\u80fd\u4e5f\u6bd4\u8f83\u5dee\uff0c\u8fd9\u4f1a\u5bf9\u5927\u6570\u636e\u548cAI\u7b49\u4e1a\u52a1\u4ea7\u751f\u5f71\u54cd\u3002Curve\u6587\u4ef6\u7cfb\u7edf\u5c06\u4f18\u5316\u5143\u6570\u636e\u7ba1\u7406\uff0c\u63d0\u9ad8\u6587\u4ef6\u68c0\u7d22\u6548\u7387\uff0c\u5e76\u6539\u5584\u5c0f\u6587\u4ef6\u7684\u8bfb\u5199\u6027\u80fd\u3002"),(0,a.kt)("h3",{id:"\u6210\u672c\u95ee\u9898"},"\u6210\u672c\u95ee\u9898"),(0,a.kt)("p",null,"\u73b0\u6709\u7684\u6587\u4ef6\u7cfb\u7edf\u901a\u5e38\u91c7\u7528\u4e24\u526f\u672c\u6216\u4e09\u526f\u672c\u6280\u672f\uff0c\u4f46\u5b9e\u9645\u4e0a80%\u7684\u6570\u636e\u90fd\u662f\u51b7\u6570\u636e\uff0c\u4f7f\u7528\u591a\u526f\u672c\u6280\u672f\u5b58\u50a8\u6210\u672c\u8f83\u9ad8\u3002Curve\u6587\u4ef6\u7cfb\u7edf\u5c06\u91c7\u7528\u66f4\u9ad8\u6548\u7684\u6570\u636e\u5b58\u50a8\u7b56\u7565\uff0c\u964d\u4f4e\u5b58\u50a8\u6210\u672c\uff0c\u63d0\u9ad8\u8d44\u6e90\u5229\u7528\u7387\u3002"),(0,a.kt)("p",null,"\u901a\u8fc7\u89e3\u51b3\u8fd9\u4e9b\u95ee\u9898\uff0cCurve\u6587\u4ef6\u7cfb\u7edf\u4e0d\u4ec5\u5c06\u4e3a\u516c\u53f8\u7684\u6838\u5fc3\u4e1a\u52a1\u63d0\u4f9b\u7a33\u5b9a\u3001\u9ad8\u6548\u7684\u5b58\u50a8\u652f\u6301\uff0c\u540c\u65f6\u4e5f\u4f1a\u5bf9\u5f00\u6e90\u793e\u533a\u505a\u51fa\u91cd\u8981\u8d21\u732e\u3002\u6211\u4eec\u81f4\u529b\u4e8e\u5c06Curve\u6587\u4ef6\u7cfb\u7edf\u6253\u9020\u6210\u4e3a\u4e00\u6b3e\u5e7f\u6cdb\u4f7f\u7528\u7684\u5f00\u6e90\u5b58\u50a8\u8f6f\u4ef6\uff0c\u4e3a\u5168\u7403\u7684\u5f00\u53d1\u8005\u793e\u533a\u63d0\u4f9b\u53ef\u6269\u5c55\u3001\u9ad8\u6027\u80fd\u548c\u4f4e\u6210\u672c\u7684\u5b58\u50a8\u89e3\u51b3\u65b9\u6848\u3002\u6211\u4eec\u76f8\u4fe1\uff0cCurve\u6587\u4ef6\u7cfb\u7edf\u7684\u7814\u53d1\u548c\u5e94\u7528\u5c06\u4fc3\u8fdb\u6280\u672f\u521b\u65b0\uff0c\u63a8\u52a8\u6574\u4e2a\u5f00\u6e90\u793e\u533a\u7684\u53d1\u5c55\u3002"),(0,a.kt)("h2",{id:"\u4e8c\u9879\u76ee\u601d\u8def\u548c\u65b9\u6848"},"\u4e8c\u3001\u9879\u76ee\u601d\u8def\u548c\u65b9\u6848"),(0,a.kt)("h3",{id:"\u6587\u4ef6\u7cfb\u7edf\u7684\u5143\u6570\u636e\u72ec\u7acb\u5b58\u5b58\u50a8"},"\u6587\u4ef6\u7cfb\u7edf\u7684\u5143\u6570\u636e\u72ec\u7acb\u5b58\u5b58\u50a8"),(0,a.kt)("p",null,"\u89e3\u51b3\u5143\u6570\u636e\u589e\u957f\u5e26\u6765\u7684\u6269\u5c55\u6027\u548c\u6027\u80fd\u8981\u6c42\u3002\u901a\u8fc7\u521b\u65b0\u7684\u8bbe\u8ba1\u548c\u5b9e\u73b0\uff0c\u4f7f\u5f97\u5176\u4e2d\u5143\u6570\u636e\u88ab\u5b58\u50a8\u5728\u4e00\u4e2a\u72ec\u7acb\u7684\u96c6\u7fa4\u4e2d\u3002\u968f\u7740\u6587\u4ef6\u6570\u91cf\u7684\u4e0d\u65ad\u589e\u52a0\uff0c\u5143\u6570\u636e\u96c6\u7fa4\u53ef\u4ee5\u6301\u7eed\u6269\u5c55\uff0c\u786e\u4fdd\u4e86\u5143\u6570\u636e\u7684\u7ebf\u6027\u6269\u5c55\u80fd\u529b\u3002\u8fd9\u79cd\u8bbe\u8ba1\u6709\u6548\u5730\u89e3\u51b3\u4e86\u4f20\u7edf\u6587\u4ef6\u7cfb\u7edf\u5728\u5904\u7406\u5927\u89c4\u6a21\u6587\u4ef6\u65f6\u9762\u4e34\u7684\u6027\u80fd\u548c\u53ef\u6269\u5c55\u6027\u6311\u6218\u3002"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs arch",src:r(4096).Z,width:"1170",height:"672"})),(0,a.kt)("p",null,"Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u901a\u8fc7\u5bf9\u5143\u6570\u636e\u8fdb\u884c\u5408\u7406\u7684\u5206\u7247\uff0c\u4f7f\u5f97\u591a\u4e2a\u5206\u7247\u53ef\u4ee5\u5206\u5e03\u5728\u7531\u591a\u53f0\u670d\u52a1\u5668\u7ec4\u6210\u7684\u5143\u6570\u636e\u96c6\u7fa4\u4e2d\u3002\u5f53\u524d\u65b9\u6848\u6309\u7167inodeid\u8fdb\u884c\u5206\u7247\uff0c\u6309\u7167\u7b97\u6cd5serverid = (inodeid / inode_per_segment) mod metaserver_num\u8fdb\u884c\u5206\u7247\u3002"),(0,a.kt)("p",null,"\u4f8b\u5982\uff0c\u5982\u679c\u6309\u7167\u6bcf\u4e2a\u5206\u7247\u7ba1\u7406100\u4e2ainodeid\uff0c\u67093\u4e2ametaserver\uff0c\u90a3\u4e48\u5206\u7247\u4fe1\u606f\u5c06\u5982\u4e0b\u3002\u5982\u679c\u5143\u6570\u636e\u6570\u91cf\u589e\u52a0\uff0c\u53ef\u4ee5\u901a\u8fc7\u589e\u52a0\u5143\u6570\u636e\u96c6\u7fa4\u7684\u670d\u52a1\u5668\u6765\u5b9e\u73b0\u7ebf\u6027\u6269\u5c55\u7684\u76ee\u6807\u3002"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs meta arch",src:r(6231).Z,width:"882",height:"506"})),(0,a.kt)("h3",{id:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u5b58\u50a8\u7684\u964d\u672c\u589e\u6548"},"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u5b58\u50a8\u7684\u964d\u672c\u589e\u6548"),(0,a.kt)("p",null,"\u6587\u4ef6\u6570\u636e\u6700\u7ec8\u5b58\u50a8\u5728S3\u4e0a\uff0c\u4f8b\u5982\u96c6\u56e2\u5185\u90e8\u7684NOS\u3001\u963f\u91cc\u4e91OSS\u3001AWS S3\u7b49\u3002S3\u901a\u5e38\u4f7f\u7528EC\u6280\u672f\uff08\u4e00\u822c\u53ef\u4ee5\u652f\u63011.2\u526f\u672c\uff09\uff0c\u76f8\u6bd42\u526f\u672c\u62163\u526f\u672c\uff0c\u5728\u4fdd\u8bc1\u53ef\u9760\u6027\u7684\u540c\u65f6\u53ef\u4ee5\u5927\u5e45\u5ea6\u964d\u4f4e\u5b58\u50a8\u6210\u672c\u3002"),(0,a.kt)("p",null,"\u4ee5\u7f51\u6613\u5bf9\u8c61\u5b58\u50a8\u8fd9\u8fb9\u5f53\u524d\u4e3b\u6d41\u7684EC 20+4\u4f7f\u7528\u4e3a\u4f8b\uff0c\u8be5\u4f7f\u7528\u65b9\u5f0f\u76f8\u5f53\u4e8e1.2\u526f\u672c\u3002\u56e0\u6b64\uff0c\u4ee5\u9700\u89811PB\u4f7f\u7528\u7a7a\u95f4\u4e3a\u4f8b\uff0c\u4f7f\u7528Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf+1.2\u526f\u672c\u5bf9\u8c61\u5b58\u50a8\u53ea\u9700\u89811.2PB\u7a7a\u95f4\uff0c\u76f8\u6bd4\u672c\u5730\u76d82\u526f\u672c\u53ef\u4ee5\u8282\u7701\u7ea6800TB\u7684\u5bb9\u91cf\uff0c\u6210\u672c\u4f18\u5316\u6548\u679c\u975e\u5e38\u663e\u8457\u3002"),(0,a.kt)("h3",{id:"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u548c\u5143\u6570\u636e\u90fd\u652f\u6301\u591a\u7ea7\u7f13\u5b58"},"\u6587\u4ef6\u7cfb\u7edf\u6570\u636e\u548c\u5143\u6570\u636e\u90fd\u652f\u6301\u591a\u7ea7\u7f13\u5b58"),(0,a.kt)("p",null,"\u5728\u5de5\u7a0b\u5b9e\u8df5\u4e2d\uff0c\u7531\u4e8eS3\u548c\u5143\u6570\u636e\u96c6\u7fa4\u90fd\u9700\u8981\u901a\u8fc7\u7f51\u7edc\u8fdb\u884c\u8bbf\u95ee\uff0c\u6bcf\u6b21\u8bfb\u5199\u64cd\u4f5c\u90fd\u4f1a\u7ecf\u8fc7\u7f51\u7edc\uff0c\u8fd9\u53ef\u80fd\u4f1a\u5bf9\u4e1a\u52a1\u6027\u80fd\u4ea7\u751f\u8d1f\u9762\u5f71\u54cd\u3002\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff0cCurve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u5728\u4fdd\u8bc1\u591a\u6302\u8f7d\u70b9\u4e00\u81f4\u6027\u7684\u60c5\u51b5\u4e0b\uff0c\u8fdb\u884c\u4e86\u6570\u636e\u548c\u5143\u6570\u636e\u7684\u6027\u80fd\u4f18\u5316\uff0c\u4e3b\u8981\u601d\u8def\u662f\u589e\u52a0\u7f13\u5b58\u3002"),(0,a.kt)("h4",{id:"\u6570\u636e\u7f13\u5b58"},"\u6570\u636e\u7f13\u5b58"),(0,a.kt)("p",null,"\u6570\u636e\u652f\u6301\u591a\u7ea7\u7f13\u5b58\uff0c\u4e3b\u8981\u5305\u62ec\uff1a"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u5185\u5b58\u7f13\u5b58\uff1a\u7528\u4e8e\u52a0\u901f\u5f53\u524d\u8282\u70b9\u4e0a\u7684\u8bfb\u5199\u901f\u5ea6\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u672c\u5730\u7f13\u5b58\uff1a\u540c\u6837\u7528\u4e8e\u52a0\u901f\u5f53\u524d\u8282\u70b9\u4e0a\u7684\u8bfb\u5199\u901f\u5ea6\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u5168\u5c40\u7f13\u5b58\u96c6\u7fa4\uff1a\u7528\u4e8e\u52a0\u901f\u5f53\u524d\u8282\u70b9\u4ee5\u53ca\u591a\u8282\u70b9\u6570\u636e\u5171\u4eab\u65f6\u7684\u901f\u5ea6\u3002")),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs data cache arch",src:r(3184).Z,width:"899",height:"504"})),(0,a.kt)("h4",{id:"\u5143\u6570\u636e\u7f13\u5b58"},"\u5143\u6570\u636e\u7f13\u5b58"),(0,a.kt)("p",null,"\u5143\u6570\u636e\u7f13\u5b58\u652f\u6301metaserver\u7aef\u5185\u5b58\u7f13\u5b58\u3001kernel\u7f13\u5b58\u548c\u672c\u5730\u5ba2\u6237\u7aef\u7f13\u5b58\u3002"),(0,a.kt)("p",null,"\u7f13\u5b58\u4f55\u65f6\u52a0\u8f7d\u6216\u5931\u6548\u662f\u5143\u6570\u636e\u7f13\u5b58\u7684\u96be\u70b9\u3002\u4e0e\u91c7\u7528\u5206\u5e03\u5f0f\u9501\u7684\u505a\u6cd5\u76f8\u6bd4\uff0cCurve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u9009\u62e9\u4e0d\u5b9e\u73b0\u590d\u6742\u7684\u5206\u5e03\u5f0f\u9501\u673a\u5236\uff0c\u800c\u662f\u57fa\u4e8e\u4e1a\u52a1\u5206\u6790\u4e0d\u9700\u8981\u5b8c\u5168\u5f3a\u4e00\u81f4\u6027\u7684\u524d\u63d0\uff0c\u4e3a\u6bcf\u79cd\u7c7b\u578b\u7684\u7f13\u5b58\u6570\u636e\u5236\u5b9a\u4e86\u4e00\u4e9b\u89c4\u5219\uff0c\u5728\u6ee1\u8db3\u4e1a\u52a1\u4e00\u81f4\u6027\u7684\u524d\u63d0\u4e0b\u63d0\u4f9b\u4e86\u8f83\u597d\u7684\u6027\u80fd\u3002"),(0,a.kt)("p",null,"\u6b64\u5916\uff0c\u901a\u8fc7\u7ed3\u5408VFS\u5c42\u7684\u91cd\u8bd5\u673a\u5236\uff0cCurve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u63d0\u4f9b\u4e86\u5b8c\u5584\u7684CTO\uff08close-to-open\uff09\u4e00\u81f4\u6027\uff0c\u5b8c\u5168\u6ee1\u8db3CTO\u8bed\u4e49\u3002\u76f8\u5bf9\u4e8eJuiceFS\u7b49\u4f9d\u8d56\u7528\u6237\u7f13\u5b58\u65f6\u957f\u914d\u7f6e\u6765\u5b9e\u73b0CTO\u7684\u5b58\u50a8\u7cfb\u7edf\uff0cCurve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u5177\u6709\u66f4\u597d\u7684\u6027\u80fd\uff0c\u5e76\u5728\u4efb\u4f55\u573a\u666f\u4e0b\u90fd\u80fd\u4fdd\u8bc1CTO\u4e00\u81f4\u6027\u3002"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs meta cache arch",src:r(8614).Z,width:"829",height:"560"})),(0,a.kt)("h3",{id:"\u6587\u4ef6\u7cfb\u7edf\u652f\u6301\u6570\u636e\u9884\u8bfb\u548c\u9884\u70ed"},"\u6587\u4ef6\u7cfb\u7edf\u652f\u6301\u6570\u636e\u9884\u8bfb\u548c\u9884\u70ed"),(0,a.kt)("h4",{id:"\u652f\u6301\u9884\u8bfbprefetch"},"\u652f\u6301\u9884\u8bfb\uff08Prefetch\uff09"),(0,a.kt)("p",null,"\u5373\u5728\u6570\u636e\u8bbf\u95ee\u65f6\uff0c\u53ef\u4ee5\u5c06\u6587\u4ef6\u8d85\u8fc7\u8bbf\u95ee\u957f\u5ea6\u5916\u7684\u6570\u636e\u63d0\u524d\u8bfb\u5165\u7f13\u5b58\u3002"),(0,a.kt)("h4",{id:"\u652f\u6301\u9884\u70edwarmup"},"\u652f\u6301\u9884\u70ed\uff08warmup\uff09"),(0,a.kt)("p",null,"AI\u7528\u6237\u53ef\u4ee5\u5728\u8bad\u7ec3\u524d\u5c06\u4e91\u7aef\u6570\u636e\u6309\u9700\u62f7\u8d1d\u5230\u6307\u5b9a\u7684\u4efb\u610f\u7f13\u5b58\u5c42\u3002\u8fd9\u6837\uff0c\u5728\u540e\u7eed\u8bbf\u95ee\u65f6\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u6027\u80fd\uff0c\u5927\u5927\u8282\u7701\u8bad\u7ec3\u65f6\u95f4\u3002"),(0,a.kt)("p",null,(0,a.kt)("img",{alt:"curvefs warmup",src:r(1720).Z,width:"821",height:"459"})),(0,a.kt)("h2",{id:"\u4e09\u9879\u76ee\u5f71\u54cd\u529b\u548c\u4ea7\u51fa\u4ef7\u503c"},"\u4e09\u3001\u9879\u76ee\u5f71\u54cd\u529b\u548c\u4ea7\u51fa\u4ef7\u503c"),(0,a.kt)("h3",{id:"\u4e3a\u4e1a\u52a1\u964d\u672c\u63d0\u6548"},"\u4e3a\u4e1a\u52a1\u964d\u672c\u63d0\u6548"),(0,a.kt)("p",null,"\u676d\u7814\u591a\u5a92\u4f53\u56e2\u961f AI \u4e1a\u52a1\u4f7f\u7528\u4e09\u526f\u672c Ceph \u5185\u6838\u6587\u4ef6\u5b58\u50a8\u6765\u652f\u6491AI\u573a\u666f\uff0c\u5305\u62ec\u901a\u7528\u3001AI\u76f8\u5173\u7684\u5404\u79cd\u6d41\u7a0b\u3002AI \u4e1a\u52a1\u5b58\u50a8\u7684\u6570\u636e\u91cf\u662f\u5de8\u5927\u7684\uff0c\u4f46\u5176\u4e2d 80% \u90fd\u662f\u51b7\u6570\u636e\uff0c\u4f7f\u7528\u4e09\u526f\u672c\u5b58\u50a8\u6210\u672c\u5f88\u9ad8\u3002\u4e1a\u52a1\u671f\u671b\u627e\u4e00\u4e2a\u6587\u4ef6\u7cfb\u7edf\u66ff\u6362 Ceph\uff0c\u5728\u4fdd\u8bc1\u6027\u80fd\u7684\u540c\u65f6\u80fd\u591f\u964d\u4f4e\u5b58\u50a8\u6210\u672c\u3002"),(0,a.kt)("p",null,"\u540c\u6837 Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u53ef\u4ee5\u65e0\u7f1d\u63a5\u5165\u4e1a\u52a1\uff0c\u76ee\u524d\u676d\u7814\u591a\u5a92\u4f53 AI \u4e1a\u52a1\u5df2\u5168\u91cf\u8fc1\u5165 Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\uff0c\u4e1a\u52a1\u4f7f\u7528\u540e\u7684\u6536\u76ca\u5305\u62ec\uff1a"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u6210\u672c\u4e0b\u964d\uff1aCurve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf \u540e\u7aef\u63a5\u5165 NOS\u4f4e\u9891\u5b58\u50a8\uff0c\u76f8\u6bd43\u526f\u672c\u5b58\u50a8\u6bcf\u5e74\u6bcf PB \u6570\u636e\u5b58\u50a8\u53ef\u8282\u7ea640%\u6210\u672c"),(0,a.kt)("li",{parentName:"ul"},"\u6027\u80fd\u6536\u76ca\uff1a\u5728\u901a\u7528\u573a\u666f Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u6027\u80fd\u548c Ceph \u5185\u6838\u6587\u4ef6\u7cfb\u7edf\u5dee\u4e0d\u591a\u6301\u5e73\uff0c\u5728 AI \u5b58\u50a8\u5bc6\u96c6\u578b\u7684\u7279\u5f81\u63d0\u53d6\u548c\u90e8\u5206\u7279\u5f81\u8bad\u7ec3\u573a\u666f\u6027\u80fd\u63d0\u534730%+\uff0c\u8ba1\u7b97\u5bc6\u96c6\u578b\u7279\u5f81\u8bad\u7ec3\u573a\u666f\u6027\u80fd\u548cCeph\u5185\u6838\u6587\u4ef6\u7cfb\u7edf\u6301\u5e73\u3002\u5c24\u5176\u662f\u5728\u6602\u8d35\u7684GPU\u8282\u70b9\u4e0a\uff0c\u5b58\u50a8\u6027\u80fd\u63d0\u5347\u53ef\u4ee5\u5e26\u6765\u66f4\u9ad8\u7684GPU\u5229\u7528\u6548\u7387\uff0c\u4ece\u800c\u964d\u4f4e\u8bad\u7ec3\u6210\u672c\u3002"),(0,a.kt)("li",{parentName:"ul"},"\u63d0\u5347\u8bad\u7ec3\u4efb\u52a1\u5e76\u53d1\u5ea6\uff1a\u4f7f\u7528Ceph\u6587\u4ef6\u7cfb\u7edf\u4f5c\u4e3aAI\u8bad\u7ec3\u6570\u636e\u96c6\u5b58\u50a8\u540e\u7aef\u65f6\uff0c\u6240\u6709\u6570\u636e\u9700\u8981\u5b9e\u65f6\u4ece\u5b58\u50a8\u540e\u7aef\u8bfb\u53d6\uff0c\u4e00\u65e6\u4e1a\u52a1\u6709\u591a\u4e2aAI\u4efb\u52a1\u9700\u8981\u5e76\u53d1\u6267\u884c\uff0c\u5c31\u4f1a\u5bfc\u81f4Ceph\u6587\u4ef6\u7cfb\u7edf\u5b58\u50a8\u540e\u7aef\u8d1f\u8f7d\u8d85\u51fa\u96c6\u7fa4\u603b\u80fd\u529b\uff0c\u6700\u7ec8\u5bfc\u81f4\u8bad\u7ec3\u4efb\u52a1\u8017\u65f6\u5927\u5927\u62c9\u957f\u3002Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u901a\u8fc7\u5229\u7528\u591a\u7ea7\u7f13\u5b58\u52a0\u901f\u80fd\u529b\uff0c\u5927\u90e8\u5206\u8bad\u7ec3\u6570\u636e\u53ea\u9700\u8981\u4ece\u5b58\u50a8\u540e\u7aef\u8bfb\u53d6\u4e00\u6b21\u5373\u53ef\u7f13\u5b58\u5230\u672c\u5730\u6216\u5206\u5e03\u5f0f\u7f13\u5b58\u96c6\u7fa4\uff0c\u4ece\u800c\u964d\u4f4e\u5bf9\u5b58\u50a8\u540e\u7aef\u7684\u6027\u80fd\u9700\u6c42\uff0c\u628a\u8d1f\u8f7d\u5206\u6563\u5230\u8bad\u7ec3\u8282\u70b9\u6216\u5206\u5e03\u5f0f\u7f13\u5b58\u96c6\u7fa4\uff0c\u6781\u5927\u63d0\u5347\u8bad\u7ec3\u4efb\u52a1\u7684\u5e76\u53d1\u5ea6\uff0c\u51cf\u5c11\u591a\u4e2a\u8bad\u7ec3\u4efb\u52a1\u4e4b\u95f4\u7684\u4e92\u76f8\u5f71\u54cd\u3002")),(0,a.kt)("p",null,"\u53e6\u5916\u4e91\u97f3\u4e50\u5e7f\u544a\u7b97\u6cd5\u56e2\u961fAI\u4e1a\u52a1\u4e5f\u5df2\u7ecf\u843d\u5730\u4f7f\u7528Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\uff0c\u7a33\u5b9a\u8fd0\u884c\u534a\u5e74\u591a\u3002"),(0,a.kt)("p",null,"Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u4e0d\u4ec5\u9002\u7528\u4e8eAI\u4e1a\u52a1\u573a\u666f\uff0c\u8fd8\u9002\u7528\u4e8eElasticSearch\u7b49\u5927\u6570\u636e\u5b58\u50a8\u5206\u6790\u4e1a\u52a1\u573a\u666f\uff0c\u76ee\u524d\u4e5f\u5728\u591a\u4e2a\u96c6\u56e2\u5185\u90e8\u4e1a\u52a1\u573a\u666f\u843d\u5730\u4f7f\u7528\u3002"),(0,a.kt)("h2",{id:"\u56db\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"},"\u56db\u3001\u9879\u76ee\u672a\u6765\u89c4\u5212\u548c\u5c55\u671b"),(0,a.kt)("p",null,"Curve \u662f\u4e00\u4e2a\u5f00\u6e90\u9879\u76ee\uff08\u5305\u62ec\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u548c\u5757\u5b58\u50a8\u4e24\u4e2a\u5b50\u9879\u76ee\uff09\uff0c\u4e0d\u4ec5\u670d\u52a1\u4e8e\u7f51\u6613\u5185\u90e8\uff0c\u4e5f\u670d\u52a1\u4e8e\u5916\u90e8\u7528\u6237\uff0c\u5f53\u524d\u5916\u90e8\u4ecd\u7136\u5b58\u5728\u5927\u91cf\u7684 AI \u4e1a\u52a1\u573a\u666f\u4e0b\u7684\u5b58\u50a8\u9700\u6c42\uff0c\u76ee\u524d Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u5728 AI \u573a\u666f\u4e0b\u7684\u843d\u5730\uff0c\u5df2\u8bc1\u660e\u5176\u5728\u8be5\u573a\u666f\u4e0b\u7684\u6027\u4ef7\u6bd4\u4f18\u52bf\uff0c\u66f4\u6709\u5229\u4e8e\u540e\u7eed\u5728\u8be5\u573a\u666f\u4e0b\u7684\u63a8\u5e7f\u3002"),(0,a.kt)("p",null,"Curve \u4f5c\u4e3a\u4e00\u4e2a\u5e74\u8f7b\u7684\u6587\u4ef6\u7cfb\u7edf\uff0c\u4ecd\u5728\u5feb\u901f\u8fed\u4ee3\u53d1\u5c55\u4e2d\uff0c\u540e\u7eed\u5c06\u7ee7\u7eed\u805a\u7126\u5728 AI \u3001\u5927\u6570\u636e\u5b58\u50a8\u7b49\u573a\u666f\uff1a"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u548c AI \u6846\u67b6\u7684\u878d\u5408\uff1a\u505a\u5230\u2f83\u52a8\u9884\u70ed\u3001\u8bad\u7ec3\u8282\u70b9\u548c\u7f13\u5b58\u8282\u70b9\u7684\u4eb2\u548c\u6027\u8c03\u5ea6\u3001\u4e0e\u5404\u7c7b\u7b97\u6cd5\u5e73\u53f0\u7684\u6df1\u5ea6\u878d\u5408\u7b49\uff0c\u8fdb\u4e00\u6b65\u63d0\u5347AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u6613\u7528\u6027\u548c\u6027\u80fd\u8868\u73b0"),(0,a.kt)("li",{parentName:"ul"},"\u2f24\u6570\u636e\u548c AI \u7684\u878d\u5408\uff1a\u63d0\u4f9b HDFS \u63a5\u2f1d\uff0c\u4f7f\u2f64 Curve\u5171\u4eab\u6587\u4ef6\u5b58\u50a8\u7cfb\u7edf\u5373\u53ef\u4ee5\u2f64\u5728\u6570\u636e\u2f63\u4ea7\u6536\u96c6\u4e5f\u53ef\u4ee5\u2f64\u4e8e\u540e\u7eed\u5904\u7406\u548c\u8bad\u7ec3"),(0,a.kt)("li",{parentName:"ul"},"\u63a8\u52a8\u5728\u66f4\u591a\u4e1a\u52a1\u573a\u666f\u7684\u843d\u5730")))}v.isMDXComponent=!0},4096:(e,t,r)=>{r.d(t,{Z:()=>l});const l=r.p+"assets/images/curvefs-arch-v2-04d6725115ee4a33aae09c8ab0af7201.webp"},3184:(e,t,r)=>{r.d(t,{Z:()=>l});const l=r.p+"assets/images/curvefs-data-cache-arch-856d05060fcc8fadbeda09024859409d.webp"},6231:(e,t,r)=>{r.d(t,{Z:()=>l});const l=r.p+"assets/images/curvefs-meta-arch-24bc47886dd26fa64f61d477c3e04051.webp"},8614:(e,t,r)=>{r.d(t,{Z:()=>l});const l=r.p+"assets/images/curvefs-meta-cache-arch-934896ba5bcfffe7fe92455d8aa251ff.webp"},1720:(e,t,r)=>{r.d(t,{Z:()=>l});const l=r.p+"assets/images/curvefs-warmup-557fa09de4aeb6d75d2d4fd859dec918.webp"}}]); \ No newline at end of file diff --git a/assets/js/afb57922.fe368e64.js b/assets/js/afb57922.fe368e64.js new file mode 100644 index 0000000..ca63230 --- /dev/null +++ b/assets/js/afb57922.fe368e64.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[2222],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>m});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var o=n.createContext({}),c=function(e){var t=n.useContext(o),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=c(e.components);return n.createElement(o.Provider,{value:t},e.children)},s="mdxType",v={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},k=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,o=e.parentName,p=u(e,["components","mdxType","originalType","parentName"]),s=c(r),k=a,m=s["".concat(o,".").concat(k)]||s[k]||v[k]||l;return r?n.createElement(m,i(i({ref:t},p),{},{components:r})):n.createElement(m,i({ref:t},p))}));function m(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,i=new Array(l);i[0]=k;var u={};for(var o in t)hasOwnProperty.call(t,o)&&(u[o]=t[o]);u.originalType=e,u[s]="string"==typeof e?e:a,i[1]=u;for(var c=2;c{r.r(t),r.d(t,{assets:()=>o,contentTitle:()=>i,default:()=>v,frontMatter:()=>l,metadata:()=>u,toc:()=>c});var n=r(7462),a=(r(7294),r(3905));const l={},i="Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548",u={unversionedId:"CurveFS/usecase/ai-storage",id:"CurveFS/usecase/ai-storage",title:"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548",description:"\u80cc\u666f",source:"@site/docs/03-CurveFS/01-usecase/07-ai-storage.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/ai-storage",permalink:"/CurveFS/usecase/ai-storage",draft:!1,tags:[],version:"current",sidebarPosition:7,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",permalink:"/CurveFS/usecase/elasticsearch-cold-data"},next:{title:"CurveFS \u52a9\u529b\u7f51\u6613\u4e91\u5546\uff0c\u89e3\u51b3\u8bed\u97f3\u8bc6\u522b\u8bad\u7ec3\u6570\u636e\u589e\u957f\u9700\u6c42",permalink:"/CurveFS/usecase/asr-storage"}},o={},c=[{value:"\u80cc\u666f",id:"\u80cc\u666f",level:2},{value:"Why Curve\uff1f",id:"why-curve",level:2},{value:"1. \u63a5\u53e3\u517c\u5bb9",id:"1-\u63a5\u53e3\u517c\u5bb9",level:3},{value:"2. \u6a2a\u5411\u6269\u5c55",id:"2-\u6a2a\u5411\u6269\u5c55",level:3},{value:"3. \u9ad8\u6027\u80fd",id:"3-\u9ad8\u6027\u80fd",level:3},{value:"3.1 \u5143\u6570\u636e\u7f13\u5b58\u673a\u5236",id:"31-\u5143\u6570\u636e\u7f13\u5b58\u673a\u5236",level:4},{value:"3.2 \u6570\u636e\u7f13\u5b58\u673a\u5236",id:"32-\u6570\u636e\u7f13\u5b58\u673a\u5236",level:4},{value:"\u964d\u672c\u589e\u6548\u6210\u679c",id:"\u964d\u672c\u589e\u6548\u6210\u679c",level:2},{value:"\u540e\u7eed\u89c4\u5212\u4e0e\u5c55\u671b",id:"\u540e\u7eed\u89c4\u5212\u4e0e\u5c55\u671b",level:2}],p={toc:c},s="wrapper";function v(e){let{components:t,...r}=e;return(0,a.kt)(s,(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"curve-\u6587\u4ef6\u7cfb\u7edf\u4e3a-ai-\u4e1a\u52a1\u964d\u672c\u589e\u6548"},"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548"),(0,a.kt)("h2",{id:"\u80cc\u666f"},"\u80cc\u666f"),(0,a.kt)("p",null,"\u5982\u4eca\u5927\u6570\u636e\u548c\u4eba\u5de5\u667a\u80fd\u9886\u57df\u7684\u5feb\u901f\u53d1\u5c55\uff0c\u968f\u7740\u6570\u636e\u91cf\u7684\u7206\u70b8\u5f0f\u589e\u957f\uff0c\u5bf9\u5e95\u5c42\u6587\u4ef6\u5b58\u50a8\u7684",(0,a.kt)("strong",{parentName:"p"},"\u6269\u5c55\u6027"),"\u3001",(0,a.kt)("strong",{parentName:"p"},"\u6210\u672c"),"\u548c",(0,a.kt)("strong",{parentName:"p"},"\u6027\u80fd"),"\u63d0\u51fa\u4e86\u66f4\u9ad8\u7684\u8981\u6c42\u3002\u7279\u522b\u5728 AI \u4e1a\u52a1\u573a\u666f\u4e0b\uff0c\u6587\u4ef6\u7cfb\u7edf\u9762\u4e34\u4e00\u4e9b\u65b0\u7684\u53d8\u5316\uff1aPOSIX \u63a5\u53e3\u517c\u5bb9\u3001\u6587\u4ef6\u5171\u4eab\u3001\u6d77\u91cf\u5c0f\u6587\u4ef6\u3001\u6570\u636e\u8bfb\u591a\u5199\u5c11\u7b49\u3002"),(0,a.kt)("h2",{id:"why-curve"},"Why Curve\uff1f"),(0,a.kt)("div",{align:"center"},(0,a.kt)("img",{src:"../../images/ai_curvefs_architecture.png",alt:"Editor",width:"500"})),(0,a.kt)("h3",{id:"1-\u63a5\u53e3\u517c\u5bb9"},"1. \u63a5\u53e3\u517c\u5bb9"),(0,a.kt)("p",null,"Curve \u6587\u4ef6\u7cfb\u7edf\u540c\u65f6\u652f\u6301 POSIX\u3001HDFS\u548cK8s CSI \u63a5\u5165\u65b9\u5f0f\uff0c\u53ef\u4ee5\u6ee1\u8db3 AI \u573a\u666f\u4e0b\u4e1a\u52a1\u5b58\u50a8\u7684\u65e0\u7f1d\u66ff\u6362\u3002"),(0,a.kt)("h3",{id:"2-\u6a2a\u5411\u6269\u5c55"},"2. \u6a2a\u5411\u6269\u5c55"),(0,a.kt)("p",null,"\u968f\u7740\u6587\u4ef6\u6570\u91cf\u7684\u589e\u957f\uff0c\u73b0\u6709\u7684\u6587\u4ef6\u7cfb\u7edf\u5982 CephFS \u548c HDFS \u7b49\u5143\u6570\u636e\u7684\u53ef\u6269\u5c55\u6027\u4e0d\u8db3\uff0c\u65e0\u6cd5\u6ee1\u8db3\u5927\u89c4\u6a21\u6587\u4ef6\u5b58\u50a8\u7684\u9700\u6c42\u3002Curve \u6587\u4ef6\u7cfb\u7edf\u81ea\u7814\u7684\u5143\u6570\u636e\u5f15\u64ce\u53ef\u6a2a\u5411\u6269\u5c55\uff0c\u89e3\u51b3\u5143\u6570\u636e\u7ba1\u7406\u7684\u95ee\u9898\u3002"),(0,a.kt)("p",null,"Curve \u6587\u4ef6\u7cfb\u7edf\u539f\u6570\u636e\u5f15\u64ce\u5177\u6709",(0,a.kt)("strong",{parentName:"p"},"\u9ad8\u53ef\u7528"),"\u3001",(0,a.kt)("strong",{parentName:"p"},"\u9ad8\u53ef\u9760"),"\u548c",(0,a.kt)("strong",{parentName:"p"},"\u9ad8\u53ef\u6269"),"\u7684\u7279\u70b9\uff0c\u6570\u636e\u7684\u53ef\u9760\u6027\u548c\u53ef\u7528\u6027\u901a\u8fc7 Raft \u534f\u8bae\u4fdd\u8bc1\uff0c\u5143\u6570\u636e\u7ecf\u8fc7\u5206\u7247\u5747\u5300\u5206\u6563\u5728\u4e0d\u540c\u7684 Raft-Group \u4e2d\uff0c\u4fdd\u8bc1\u4e86\u6570\u636e\u548c\u8d1f\u8f7d\u7684\u5747\u8861\u6027\uff0c\u540c\u65f6\u652f\u6301\u4e1a\u52a1\u6309\u9700\u8fdb\u884c\u4e00\u952e\u5f39\u6027\u6269\u7f29\u5bb9\u3002"),(0,a.kt)("div",{align:"center"},(0,a.kt)("img",{src:"../../images/ai_curvefs_metadata_architecture.png",width:"500"})),(0,a.kt)("h3",{id:"3-\u9ad8\u6027\u80fd"},"3. \u9ad8\u6027\u80fd"),(0,a.kt)("p",null,"\u968f\u7740\u6587\u4ef6\u6570\u91cf\u7684\u589e\u52a0\uff0c\u4f20\u7edf\u6587\u4ef6\u7cfb\u7edf\u5143\u6570\u636e\u7684\u6027\u80fd\u4f1a\u5927\u5e45\u4e0b\u964d\uff0c\u6b64\u5916\uff0c\u5c0f\u6587\u4ef6\u7684\u8bfb\u5199\u6027\u80fd\u4e5f\u6bd4\u8f83\u5dee\uff0c\u8fd9\u4f1a\u5bf9\u5927\u6570\u636e\u548cAI\u7b49\u4e1a\u52a1\u4ea7\u751f\u5f71\u54cd\u3002Curve\u6587\u4ef6\u7cfb\u7edf\u901a\u8fc7\u591a\u7ea7\u7f13\u5b58\u673a\u5236\uff0c\u63d0\u9ad8\u6587\u4ef6\u5143\u6570\u636e\u8bbf\u95ee\u6027\u80fd\uff0c\u5e76\u6539\u5584\u5c0f\u6587\u4ef6\u7684\u8bfb\u5199\u6027\u80fd\u3002"),(0,a.kt)("h4",{id:"31-\u5143\u6570\u636e\u7f13\u5b58\u673a\u5236"},"3.1 \u5143\u6570\u636e\u7f13\u5b58\u673a\u5236"),(0,a.kt)("p",null,"\u5143\u6570\u636e\u652f\u6301\u5185\u6838\u548c\u672c\u5730\u7684\u591a\u7ea7\u7f13\u5b58\uff0c\u5e76\u63d0\u4f9b\u7075\u6d3b\u7684\u7f13\u5b58\u914d\u7f6e\uff0c\u7528\u6237\u53ef\u4ee5\u6839\u636e\u81ea\u5df1\u4e1a\u52a1\u7684\u7279\u70b9\u914d\u7f6e\u5408\u9002\u7684\u7f13\u5b58\u5931\u6548\u65f6\u95f4\uff0c\u4ee5\u5728\u6ee1\u8db3\u4e00\u81f4\u6027\u8981\u6c42\u7684\u524d\u63d0\u4e0b\u83b7\u53d6\u66f4\u9ad8\u7684\u64cd\u4f5c\u6027\u80fd\u3002\u6b64\u5916\uff0c\u901a\u8fc7\u7ed3\u5408VFS\u5c42\u7684\u91cd\u8bd5\u673a\u5236\uff0cCurve \u6587\u4ef6\u7cfb\u7edf\u63d0\u4f9b\u4e86\u5b8c\u5584\u7684 CTO\uff08close-to-open\uff09\u4e00\u81f4\u6027\u3002"),(0,a.kt)("div",{align:"center"},(0,a.kt)("img",{src:"../../images/ai_curvefs_metadata_cache.png",width:"700"})),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-shell"},"Kernel Cache -> \u901a\u7528\u7f13\u5b58\nfs.kernelCache.attrTimeoutSec=3600\nfs.kernelCache.dirAttrTimeoutSec=3600\nfs.kernelCache.entryTimeoutSec=3600\nfs.kernelCache.dirEntryTimeoutSec=3600\n\nOpen File Cache -> \u6587\u4ef6\u8bfb\u5199\nfs.openFile.lruSize=65536\n\nNegative Lookup Cache -> \u4ee3\u7801\u7f16\u8bd1/SO\u67e5\u627e/\u6240\u4ee5\u547d\u4ee4\nfs.lookupCache.negativeTimeoutSec=1\nfs.lookupCache.minUses=3\n\nDirectory Cache -> \u5927\u76ee\u5f55/ls\u3001find\nfs.dirCache.lruSize=5000000\n")),(0,a.kt)("h4",{id:"32-\u6570\u636e\u7f13\u5b58\u673a\u5236"},"3.2 \u6570\u636e\u7f13\u5b58\u673a\u5236"),(0,a.kt)("p",null,"\u6570\u636e\u5c42\u9762 Curve \u6587\u4ef6\u7cfb\u7edf\u540c\u6837\u652f\u6301\u53ef\u914d\u7684\u5185\u5b58\u7f13\u5b58\u3001\u672c\u5730\u78c1\u76d8\u7f13\u5b58\u548c\u5206\u5e03\u5f0f\u7f13\u5b58\u96c6\u7fa4\u6765\u52a0\u901f\u6570\u636e\u7684\u8bbf\u95ee\u3002"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"\u5185\u5b58\u7f13\u5b58\uff1a\u7528\u4e8e\u52a0\u901f\u5f53\u524d\u8282\u70b9\u4e0a\u7684\u8bfb\u5199\u901f\u5ea6\uff08sync\u64cd\u4f5c\u65f6\u4f1a\u5237\u5230\u6301\u4e45\u5316\u5b58\u50a8\u4fdd\u8bc1\u6570\u636e\u53ef\u9760\u6027\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u672c\u5730\u78c1\u76d8\u7f13\u5b58\uff1a\u7528\u4e8e\u52a0\u901f\u5f53\u524d\u8282\u70b9\u4e0a\u7684\u8bfb\u5199\u901f\u5ea6\uff08\u5f00\u542f\u5171\u4eab\uff08cto\uff09\u65f6\uff0c\u6570\u636e\u4f1a\u540c\u65f6\u5237\u4e00\u4efd\u5230\u5171\u4eab\u7f13\u5b58\u4e2d\uff0c\u5982\u679c\u6ca1\u914d\u7f6e\u5171\u4eab\u7f13\u5b58\u5219\u9700\u8981\u4e0a\u4f20\u5230\u540e\u7aef\u6570\u636e\u5b58\u50a8\u5f15\u64ce\uff09\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u5171\u4eab\u7f13\u5b58\uff1a\u7528\u4e8e\u52a0\u901f\u8de8\u8282\u70b9\u95f4\u7684\u6570\u636e\u5171\u4eab\u901f\u5ea6\u3002")),(0,a.kt)("div",{align:"center"},(0,a.kt)("img",{src:"../../images/curvefs-data-cache-arch.webp",width:"700"})),(0,a.kt)("p",null,"\u4e3a\u4e86\u52a0\u901f\u6570\u636e\u7684\u8bfb\u53d6\u901f\u5ea6\uff0cCurve \u6587\u4ef6\u7cfb\u7edf\u652f\u6301\u6570\u636e\u7684\u9884\u8bfb\u548c\u9884\u70ed\u3002"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u9884\u8bfb\uff08prefetch\uff09"),"\uff1a\u5373\u5728\u6570\u636e\u8bbf\u95ee\u65f6\uff0c\u53ef\u4ee5\u5c06\u6587\u4ef6\u8d85\u8fc7\u8bbf\u95ee\u957f\u5ea6\u5916\u7684\u6570\u636e\u63d0\u524d\u8bfb\u5165\u7f13\u5b58\uff0c\u63d0\u9ad8\u540e\u7eed\u8bfb\u8bf7\u6c42\u7f13\u5b58\u547d\u4e2d\u7387\u3002"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u9884\u70ed\uff08warmup\uff09"),"\uff1a\u6307\u7528\u6237\u5728\u4f7f\u7528\u5230\u67d0\u90e8\u5206\u6570\u636e\u4e4b\u524d\u4e3b\u52a8\u7684\u89e6\u53d1\u8be5\u90e8\u5206\u6570\u636e\u5199\u5230\u6307\u5b9a\u7f13\u5b58\u5c42\uff0c\u63d0\u9ad8\u4f7f\u7528\u65f6\u7684\u6027\u80fd\uff0c\u4f8b\u5982\u5728 AI \u8bad\u7ec3\u573a\u666f\u4e0b\uff0c\u53ef\u4ee5\u63d0\u524d\u5c06\u8bad\u7ec3\u6570\u636e\u96c6\u9884\u70ed\u5230\u7f13\u5b58\u4e2d\uff0c\u6765\u52a0\u901f\u6574\u4e2a\u8bad\u7ec3\u8fc7\u7a0b\u3002"),(0,a.kt)("div",{align:"center"},(0,a.kt)("img",{src:"../../images/ai_curvefs_warmup.png",width:"700"})),(0,a.kt)("h2",{id:"\u964d\u672c\u589e\u6548\u6210\u679c"},"\u964d\u672c\u589e\u6548\u6210\u679c"),(0,a.kt)("p",null,"\u7f51\u6613\u676d\u7814\u591a\u5a92\u4f53\u56e2\u961f AI \u4e1a\u52a1\u4e4b\u524d\u4f7f\u7528\u4e09\u526f\u672c Ceph \u5185\u6838\u6587\u4ef6\u5b58\u50a8\u6765\u652f\u6491AI\u573a\u666f\uff0c\u5305\u62ec\u901a\u7528\u3001AI\u76f8\u5173\u7684\u5404\u79cd\u6d41\u7a0b\u3002AI \u4e1a\u52a1\u5b58\u50a8\u7684\u6570\u636e\u91cf\u662f\u5de8\u5927\u7684\uff0c\u4f46\u5176\u4e2d 80% \u90fd\u662f\u51b7\u6570\u636e\uff0c\u4f7f\u7528\u4e09\u526f\u672c\u5b58\u50a8\u6210\u672c\u5f88\u9ad8\u3002\u4e1a\u52a1\u671f\u671b\u627e\u4e00\u4e2a\u6587\u4ef6\u7cfb\u7edf\u80fd\u65e0\u7f1d\u66ff\u6362 Ceph\uff0c\u4e14\u5728\u4fdd\u8bc1\u6027\u80fd\u7684\u540c\u65f6\u80fd\u591f\u964d\u4f4e\u5b58\u50a8\u6210\u672c\u3002"),(0,a.kt)("p",null,"\u76ee\u524d\u7f51\u6613\u676d\u7814\u591a\u5a92\u4f53 AI \u4e1a\u52a1\u5df2\u5168\u91cf\u8fc1\u5165 Curve \u6587\u4ef6\u7cfb\u7edf\uff0c\u4e1a\u52a1\u4f7f\u7528\u540e\u7684\u6536\u76ca\u5305\u62ec\uff1a"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u6210\u672c\u4e0b\u964d"),"\uff1aCurve \u6587\u4ef6\u7cfb\u7edf\u540e\u7aef\u63a5\u5165\u7f51\u6613\u5bf9\u8c61\u5b58\u50a8\uff08NetEase Object Storage\uff09\u4f4e\u9891\u5b58\u50a8\uff0c\u76f8\u6bd43\u526f\u672c\u5b58\u50a8\u6bcf\u5e74\u6bcf PB \u6570\u636e\u5b58\u50a8\u53ef\u8282\u7ea6",(0,a.kt)("strong",{parentName:"p"},"75"),"%\u6210\u672c\u3002"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u6027\u80fd\u63d0\u5347"),"\uff1a\u5728\u901a\u7528\u573a\u666f Curve \u6587\u4ef6\u7cfb\u7edf\u6027\u80fd\u548c Ceph \u5185\u6838\u6587\u4ef6\u7cfb\u7edf\u5dee\u4e0d\u591a\u6301\u5e73\uff0c\u5728 AI \u5b58\u50a8\u5bc6\u96c6\u578b\u7684\u7279\u5f81\u63d0\u53d6\u548c\u90e8\u5206\u7279\u5f81\u8bad\u7ec3\u573a\u666f\u6027\u80fd\u63d0\u5347",(0,a.kt)("strong",{parentName:"p"},"30"),"%+\uff0c\u8ba1\u7b97\u5bc6\u96c6\u578b\u7279\u5f81\u8bad\u7ec3\u573a\u666f\u6027\u80fd\u548cCeph\u5185\u6838\u6587\u4ef6\u7cfb\u7edf\u6301\u5e73\u3002\u5c24\u5176\u662f\u5728\u6602\u8d35\u7684GPU\u8282\u70b9\u4e0a\uff0c\u5b58\u50a8\u6027\u80fd\u63d0\u5347\u53ef\u4ee5\u5e26\u6765\u66f4\u9ad8\u7684GPU\u5229\u7528\u6548\u7387\uff0c\u4ece\u800c\u964d\u4f4e\u8bad\u7ec3\u6210\u672c\u3002"),(0,a.kt)("p",null,(0,a.kt)("strong",{parentName:"p"},"\u63d0\u5347\u8bad\u7ec3\u4efb\u52a1\u5e76\u53d1\u5ea6"),"\uff1a\u4f7f\u7528 Ceph \u6587\u4ef6\u7cfb\u7edf\u4f5c\u4e3a AI \u8bad\u7ec3\u6570\u636e\u96c6\u5b58\u50a8\u540e\u7aef\u65f6\uff0c\u6240\u6709\u6570\u636e\u9700\u8981\u5b9e\u65f6\u4ece\u5b58\u50a8\u540e\u7aef\u8bfb\u53d6\uff0c\u4e00\u65e6\u4e1a\u52a1\u6709\u591a\u4e2aAI\u4efb\u52a1\u9700\u8981\u5e76\u53d1\u6267\u884c\uff0c\u5c31\u4f1a\u5bfc\u81f4 Ceph \u6587\u4ef6\u7cfb\u7edf\u5b58\u50a8\u540e\u7aef\u8d1f\u8f7d\u8d85\u51fa\u96c6\u7fa4\u603b\u80fd\u529b\uff0c\u6700\u7ec8\u5bfc\u81f4\u8bad\u7ec3\u4efb\u52a1\u8017\u65f6\u5927\u5927\u62c9\u957f\u3002Curve \u6587\u4ef6\u7cfb\u7edf\u901a\u8fc7\u5229\u7528\u591a\u7ea7\u7f13\u5b58\u52a0\u901f\u80fd\u529b\uff0c\u5927\u90e8\u5206\u8bad\u7ec3\u6570\u636e\u53ea\u9700\u8981\u4ece\u5b58\u50a8\u540e\u7aef\u8bfb\u53d6\u4e00\u6b21\u5373\u53ef\u7f13\u5b58\u5230\u672c\u5730\u6216\u5206\u5e03\u5f0f\u7f13\u5b58\u96c6\u7fa4\uff0c\u4ece\u800c\u964d\u4f4e\u5bf9\u5b58\u50a8\u540e\u7aef\u7684\u6027\u80fd\u9700\u6c42\uff0c\u628a\u8d1f\u8f7d\u5206\u6563\u5230\u8bad\u7ec3\u8282\u70b9\u6216\u5206\u5e03\u5f0f\u7f13\u5b58\u96c6\u7fa4\uff0c\u6781\u5927\u63d0\u5347\u8bad\u7ec3\u4efb\u52a1\u7684\u5e76\u53d1\u5ea6\uff0c\u51cf\u5c11\u591a\u4e2a\u8bad\u7ec3\u4efb\u52a1\u4e4b\u95f4\u7684\u4e92\u76f8\u5f71\u54cd\u3002"),(0,a.kt)("h2",{id:"\u540e\u7eed\u89c4\u5212\u4e0e\u5c55\u671b"},"\u540e\u7eed\u89c4\u5212\u4e0e\u5c55\u671b"),(0,a.kt)("p",null,"Curve \u4f5c\u4e3a\u4e00\u4e2a\u5e74\u8f7b\u7684\u6587\u4ef6\u7cfb\u7edf\uff0c\u4ecd\u5728\u5feb\u901f\u8fed\u4ee3\u53d1\u5c55\u4e2d\uff0c\u540e\u7eed\u5c06\u7ee7\u7eed\u805a\u7126\u5728 AI \u3001\u5927\u6570\u636e\u5b58\u50a8\u7b49\u573a\u666f\uff1a"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"\u8fdb\u4e00\u6b65\u4f18\u5316\u5143\u6570\u636e\u548c\u6570\u636e\u8bfb\u5199\u6027\u80fd\uff0c\u4e3a\u652f\u6301\u66f4\u591a\u5e94\u7528\u573a\u666f\u6253\u597d\u57fa\u7840\u3002"),(0,a.kt)("li",{parentName:"ol"},"\u548c AI \u6846\u67b6\u7684\u878d\u5408\uff0c\u505a\u5230\u2f83\u52a8\u9884\u70ed\u3001\u8bad\u7ec3\u8282\u70b9\u548c\u7f13\u5b58\u8282\u70b9\u7684\u4eb2\u548c\u6027\u8c03\u5ea6\u3001\u4e0e\u5404\u7c7b\u7b97\u6cd5\u5e73\u53f0\u7684\u6df1\u5ea6\u878d\u5408\u7b49\uff0c\u8fdb\u4e00\u6b65\u63d0\u5347AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u6613\u7528\u6027\u548c\u6027\u80fd\u8868\u73b0\u3002")))}v.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/bda932b4.2a104469.js b/assets/js/bda932b4.2a104469.js deleted file mode 100644 index ed2a6e9..0000000 --- a/assets/js/bda932b4.2a104469.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[9880],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>S});var n=r(7294);function l(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t=0||(l[r]=e[r]);return l}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(l[r]=e[r])}return l}var i=n.createContext({}),o=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},c=function(e){var t=o(e.components);return n.createElement(i.Provider,{value:t},e.children)},v="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,l=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),v=o(r),d=l,S=v["".concat(i,".").concat(d)]||v[d]||p[d]||a;return r?n.createElement(S,u(u({ref:t},c),{},{components:r})):n.createElement(S,u({ref:t},c))}));function S(e,t){var r=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var a=r.length,u=new Array(a);u[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[v]="string"==typeof e?e:l,u[1]=s;for(var o=2;o{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>u,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>o});var n=r(7462),l=(r(7294),r(3905));const a={},u="CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",s={unversionedId:"CurveFS/usecase/elasticsearch-cold-data",id:"CurveFS/usecase/elasticsearch-cold-data",title:"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",description:"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca",source:"@site/docs/03-CurveFS/01-usecase/06-elasticsearch-cold-data.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/elasticsearch-cold-data",permalink:"/CurveFS/usecase/elasticsearch-cold-data",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301",permalink:"/CurveFS/usecase/s3-gateway"},next:{title:"CurveFS\u5728AI\u8bad\u7ec3\u573a\u666f\u4e0b\u7684\u964d\u672c\u63d0\u6548\u5b9e\u8df5",permalink:"/CurveFS/usecase/ai-storage"}},i={},o=[{value:"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca",id:"es\u4f7f\u7528curvefs\u7684\u6536\u76ca",level:2},{value:"CurveFS\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf",level:3},{value:"ES\u4f7f\u7528\u80cc\u666f",id:"es\u4f7f\u7528\u80cc\u666f",level:2},{value:"\u7f51\u6613ES\u9009\u7528CurveFS\u7684\u539f\u56e0",id:"\u7f51\u6613es\u9009\u7528curvefs\u7684\u539f\u56e0",level:3},{value:"\u672c\u5730\u76d8\u5230minio",id:"\u672c\u5730\u76d8\u5230minio",level:4},{value:"minio\u5230CurveFS",id:"minio\u5230curvefs",level:4},{value:"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5",id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5",level:2},{value:"CurveFS\u662f\u4ec0\u4e48",id:"curvefs\u662f\u4ec0\u4e48",level:3},{value:"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5",id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5-1",level:3},{value:"ES\u4f7f\u7528CurveFS",id:"es\u4f7f\u7528curvefs",level:4},{value:"ES\u8bbe\u7f6e",id:"es\u8bbe\u7f6e",level:4}],c={toc:o},v="wrapper";function p(e){let{components:t,...a}=e;return(0,l.kt)(v,(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"curvefs\u5728elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528"},"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528"),(0,l.kt)("h2",{id:"es\u4f7f\u7528curvefs\u7684\u6536\u76ca"},"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf"),(0,l.kt)("p",null,"\u4e3a\u4e86\u9ad8\u53ef\u9760\uff0cES\u5982\u679c\u4f7f\u7528\u672c\u5730\u76d8\u7684\u8bdd\u4e00\u822c\u4f1a\u4f7f\u7528\u4e24\u526f\u672c\uff0c\u4e5f\u5c31\u662f\u8bf4\u5b58\u50a81PB\u6570\u636e\u9700\u89812PB\u7684\u7269\u7406\u7a7a\u95f4\u3002\u4f46\u662f\u5982\u679c\u4f7f\u7528CurveFS\uff0c\u7531\u4e8eCurveFS\u7684\u540e\u7aef\u53ef\u4ee5\u5bf9\u63a5S3\uff0c\u6240\u4ee5\u53ef\u4ee5\u5229\u7528\u5bf9\u8c61\u5b58\u50a8\u63d0\u4f9b\u7684EC\u80fd\u529b\uff0c\u65e2\u4fdd\u8bc1\u4e86\u53ef\u9760\u6027\uff0c\u53c8\u53ef\u4ee5\u51cf\u5c11\u526f\u672c\u6570\u91cf\uff0c\u4ece\u800c\u8fbe\u5230\u4e86\u964d\u4f4e\u6210\u672c\u7684\u76ee\u7684\u3002"),(0,l.kt)("p",null,"\u4ee5\u7f51\u6613\u5bf9\u8c61\u5b58\u50a8\u8fd9\u8fb9\u5f53\u524d\u4e3b\u6d41\u7684EC 20+4\u4f7f\u7528\u4e3a\u4f8b\uff0c\u8be5\u4f7f\u7528\u65b9\u5f0f\u5c31\u76f8\u5f53\u4e8e\u662f1.2\u526f\u672c\u3002\u6240\u4ee5\u5982\u679c\u4ee5ES\u9700\u89811PB\u4f7f\u7528\u7a7a\u95f4\u4e3a\u4f8b\uff0c\u90a3\u4e48\u4f7f\u7528CurveFS+1.2\u526f\u672c\u5bf9\u8c61\u5b58\u50a8\u53ea\u9700\u89811.2PB\u7a7a\u95f4\uff0c\u76f8\u6bd4\u672c\u5730\u76d82\u526f\u672c\u53ef\u4ee5\u8282\u7701800TB\u5de6\u53f3\u7684\u5bb9\u91cf\uff0c\u6210\u672c\u4f18\u5316\u6548\u679c\u975e\u5e38\u663e\u8457\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf"),(0,l.kt)("p",null,"\u4ee5\u4e0b\u6587\u5c06\u8981\u4ecb\u7ecd\u7684\u4f7f\u7528\u573a\u666f\u4e3a\u4f8b\uff0c\u5bf9\u6bd4ES\u539f\u6765\u4f7f\u7528S3\u63d2\u4ef6\u505asnapshot\u8f6c\u5b58\u50a8\u7684\u65b9\u5f0f\uff0c\u7531\u4e8e\u6bcf\u6b21\u64cd\u4f5c\u7684\u65f6\u5019\u7d22\u5f15\u9700\u8981\u8fdb\u884crestore\u64cd\u4f5c\uff0c\u4ee5100G\u7684\u65e5\u5fd7\u7d22\u5f15\u4e3a\u4f8b\uff0c\u53e6\u5916\u4f1a\u6709\u4f20\u8f93\u65f6\u95f4\uff0c\u5982\u679crestore\u7684\u6062\u590d\u901f\u5ea6\u4e3a100M\uff0c\u90a3\u4e48\u4e5f\u8981300\u591a\u79d2\u3002\u5b9e\u9645\u60c5\u51b5\u662f\u5728\u4e00\u4e2a\u5927\u91cf\u5199\u5165\u7684\u96c6\u7fa4\uff0c\u8fd9\u6837\u7684\u64cd\u4f5c\u53ef\u80fd\u8981\u51e0\u4e2a\u5c0f\u65f6\u3002"),(0,l.kt)("p",null,"\u800c\u4f7f\u7528CurveFS\u540e\u7684\u65b0\u6a21\u5f0f\u4e0b\u57fa\u672c\u4e0a\u53ea\u8981\u5bf9freeze\u7684\u7d22\u5f15\u8fdb\u884cunfreeze\uff0c\u8ba9\u5bf9\u5e94\u8282\u70b9\u7684ES\u5c06\u5bf9\u5e94\u7684meta\u6570\u636e\u8f7d\u5165\u5185\u5b58\u5c31\u53ef\u4ee5\u6267\u884c\u7d22\u5f15\uff0c\u5927\u6982\u8017\u65f6\u4ec5\u970030S\u5de6\u53f3\uff0c\u76f8\u6bd4\u76f4\u63a5\u7528S3\u5b58\u50a8\u51b7\u6570\u636e\u6709\u6570\u91cf\u7ea7\u7684\u4e0b\u964d\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf"),(0,l.kt)("p",null,"\u672c\u5730\u76d8\u7684\u5bb9\u91cf\u662f\u6709\u9650\u7684\uff0c\u800cCurveFS\u7684\u7a7a\u95f4\u5bb9\u91cf\u53ef\u4ee5\u5728\u7ebf\u65e0\u9650\u6269\u5c55\u3002\u540c\u65f6\u51cf\u5c11\u4e86\u672c\u5730\u5b58\u50a8\u7684\u7ef4\u62a4\u4ee3\u4ef7\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf"),(0,l.kt)("p",null,"ES\u4f7f\u7528\u672c\u5730\u76d8\u4ee5\u53ca\u4f7f\u7528S3\u63d2\u4ef6\u65b9\u5f0f\uff0c\u5f53\u9700\u8981\u6269\u5bb9\u6216\u8005\u8282\u70b9\u5f02\u5e38\u6062\u590d\u65f6\uff0c\u9700\u8981\u589e\u52a0\u4eba\u529b\u8fd0\u7ef4\u6210\u672c\u3002CurveFS\u5b9e\u73b0\u4e4b\u521d\u7684\u4e00\u4e2a\u76ee\u6807\u5c31\u662f\u6613\u8fd0\u7ef4\uff0c\u6240\u4ee5CurveFS\u53ef\u4ee5\u5b9e\u73b0\u6570\u6761\u547d\u4ee4\u7684\u5feb\u901f\u90e8\u7f72\u4ee5\u53ca\u6545\u969c\u81ea\u6108\u80fd\u529b\u3002"),(0,l.kt)("p",null,"\u53e6\u5916\u5982\u679cES\u4f7f\u7528CurveFS\uff0c\u5c31\u5b9e\u73b0\u4e86\u5b58\u7b97\u5206\u79bb\uff0c\u8fdb\u4e00\u6b65\u91ca\u653e\u4e86ES\u4f7f\u7528\u8005\u7684\u8fd0\u7ef4\u8d1f\u62c5\u3002"),(0,l.kt)("h2",{id:"es\u4f7f\u7528\u80cc\u666f"},"ES\u4f7f\u7528\u80cc\u666f"),(0,l.kt)("p",null,"\u5728\u751f\u4ea7\u73af\u5883\u6709\u5927\u91cf\u7684\u573a\u666f\u4f1a\u7528\u5230ES\u505a\u6587\u6863\u3001\u65e5\u5fd7\u5b58\u50a8\u540e\u7aef\uff0c\u56e0\u4e3aES\u4f18\u79c0\u7684\u5168\u6587\u68c0\u7d22\u80fd\u529b\u5728\u5f88\u591a\u65f6\u5019\u53ef\u4ee5\u5927\u5927\u7684\u7b80\u5316\u76f8\u5173\u7cfb\u7edf\u8bbe\u8ba1\u7684\u590d\u6742\u5ea6\u3002\u6bd4\u8f83\u5e38\u89c1\u7684\u4e3a\u65e5\u5fd7\u5b58\u50a8\uff0c\u94fe\u8def\u8ffd\u8e2a\uff0c\u751a\u81f3\u662f\u76d1\u63a7\u6307\u6807\u7b49\u573a\u666f\u90fd\u53ef\u4ee5\u7528ES\u6765\u505a\u3002"),(0,l.kt)("h3",{id:"\u7f51\u6613es\u9009\u7528curvefs\u7684\u539f\u56e0"},"\u7f51\u6613ES\u9009\u7528CurveFS\u7684\u539f\u56e0"),(0,l.kt)("p",null,"\u7f51\u6613ES\u5e95\u5c42\u5b58\u50a8\u7684\u4f7f\u7528\u7ecf\u5386\u4e86\u4ece\u672c\u5730\u76d8\u5230minio\uff0c\u518d\u4eceminio\u5230CurveFS\u8fd9\u4e00\u8fc7\u7a0b\u3002"),(0,l.kt)("h4",{id:"\u672c\u5730\u76d8\u5230minio"},"\u672c\u5730\u76d8\u5230minio"),(0,l.kt)("p",null,"\u4e3a\u4e86\u7b26\u5408\u56fd\u5185\u7684\u6cd5\u5f8b\u7ea6\u675f\uff0c\u7ebf\u4e0a\u7cfb\u7edf\u9700\u8981\u6309\u7167\u8981\u6c42\u5b58\u50a86\u4e2a\u6708\u52301\u5e74\u4e0d\u7b49\u7684\u7cfb\u7edf\u65e5\u5fd7\uff0c\u4e3b\u8981\u662f\u56fd\u5185\u7b49\u4fdd\u3001\u91d1\u878d\u5408\u89c4\u7b49\u573a\u666f\u3002\u6309\u7167\u5185\u90e8\u7ba1\u7406\u7684\u670d\u52a1\u5668\u6570\u91cf\uff0c\u5355\u7eafsyslog\u7684\u65e5\u5fd7\u5b58\u50a8\u7a7a\u95f4\u6bcf\u5929\u5c31\u9700\u89811T\uff0c\u6309\u7167\u5f53\u524d\u624b\u5934\u6709\u76845\u53f012\u76d8\u4f4d4T\u786c\u76d8\u7684\u670d\u52a1\u5668\uff0c\u6700\u591a\u53ea\u80fd\u5b58\u50a8200\u591a\u5929\u7684\u65e5\u5b50\uff0c\u65e0\u6cd5\u6ee1\u8db3\u65e5\u5fd7\u5b58\u50a81\u5e74\u7684\u9700\u6c42\u3002"),(0,l.kt)("p",null,"\u9488\u5bf9ES\u4f7f\u7528\u672c\u5730\u76d8\u65e0\u6cd5\u6ee1\u8db3\u5b58\u50a8\u5bb9\u91cf\u9700\u6c42\u8fd9\u4e00\u60c5\u51b5\uff0c\u7f51\u6613ES\u5e95\u5c42\u5b58\u50a8\u4e4b\u524d\u5355\u72ec\u5f15\u5165\u8fc7\u57fa\u4e8eS3\u7684\u5b58\u50a8\u65b9\u6848\u6765\u964d\u4f4e\u5b58\u50a8\u7a7a\u95f4\u7684\u6d88\u8017\u3002\u5982\u4e0b\u56fe\uff0cES\u914d\u5408minio\u505a\u6570\u636e\u5b58\u50a8\u7a7a\u95f4\u7684\u538b\u7f29\u3002\u4e3e\u4f8b\u6765\u8bf4100G\u7684\u65e5\u5fd7\uff0c\u5230\u4e86ES\u91cc\u9762\u56e0\u4e3a\u53ef\u9760\u6027\u9700\u6c42\uff0c\u9700\u8981\u53cc\u526f\u672c\uff0c\u4f1a\u4f7f\u7528200G\u7684\u7a7a\u95f4\u3002ES\u9488\u5bf9\u7d22\u5f15\u5206\u7247\u65f6\u95f4\uff0c\u5b9a\u671f\u6027\u8f6c\u5b58\u50a8\u5230minio\u4ed3\u5e93\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es minio",src:r(2654).Z,width:"863",height:"85"})),(0,l.kt)("h4",{id:"minio\u5230curvefs"},"minio\u5230CurveFS"),(0,l.kt)("p",null,"\u8fd9\u4e2a\u65b9\u6848\u4ece\u4e00\u5b9a\u7a0b\u5ea6\u4e0a\u7f13\u89e3\u4e86\u5b58\u50a8\u7a7a\u95f4\u7684\u8d44\u6e90\u95ee\u9898\uff0c\u4f46\u662f\u5b9e\u9645\u4f7f\u7528\u7684\u65f6\u5019\u8fd8\u4f1a\u611f\u89c9\u975e\u5e38\u4e0d\u4fbf\u5229\u3002"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"\u8fd0\u7ef4\u6210\u672c\u3002ES\u8282\u70b9\u5347\u7ea7\u7684\u65f6\u5019\u9700\u8981\u989d\u5916\u5378\u8f7d\u5b89\u88c5S3\u63d2\u4ef6\uff0c\u6709\u4e00\u5b9a\u7684\u8fd0\u7ef4\u6210\u672c\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u6027\u80fd\u74f6\u9888\u3002\u81ea\u5df1\u79c1\u6709\u5316\u642d\u5efa\u7684Minio\u968f\u7740bucket\u91cc\u9762\u6570\u636e\u91cf\u7684\u589e\u957f\uff0c\u6570\u636e\u5b58\u50a8\u548c\u62bd\u53d6\u90fd\u4f1a\u6210\u4e3a\u4e00\u4e2a\u5f88\u5927\u7684\u95ee\u9898"),(0,l.kt)("li",{parentName:"ul"},"\u7a33\u5b9a\u6027\u95ee\u9898\u3002\u5728\u5185\u90e8\u642d\u5efa\u7684Minio\u96c6\u7fa4\u5728\u505a\u6570\u636erestore\u7684\u65f6\u5019\uff0c\u56e0\u4e3a\u6587\u4ef6\u5904\u7406\u6027\u80fd\u7b49\u56e0\u7d20\uff0c\u7ecf\u5e38\u9047\u5230\u8bbf\u95ee\u8d85\u65f6\u7b49\u573a\u666f\uff0c\u6240\u4ee5\u4e00\u76f4\u5728\u5173\u6ce8\u662f\u5426\u6709\u76f8\u5173\u7684\u7cfb\u7edf\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u8bfb\u5199\u7a33\u5b9a\u6027\u3002")),(0,l.kt)("p",null,"\u7531\u4e8eS3\u534f\u8bae\u7ecf\u8fc7\u591a\u5e74\u7684\u6f14\u5316\uff0c\u5df2\u7ecf\u6210\u4e86\u5bf9\u8c61\u5b58\u50a8\u7684\u5de5\u4e1a\u6807\u51c6\u3002\u5f88\u591a\u4eba\u90fd\u6709\u60f3\u8fc7\u7528fuse\u7684\u65b9\u5f0f\u4f7f\u7528S3\u7684\u5b58\u50a8\u80fd\u529b\u3002\u4e8b\u5b9e\u4e0a\u57fa\u4e8eS3\u7684\u6587\u4ef6\u7cfb\u7edf\u6709\u5f88\u591a\u6b3e\uff0c\u4f8b\u5982\u5f00\u6e90\u7684s3fs-fuse\u3001ossfs\u3001RioFS\u3001CurveFS\u7b49\u3002"),(0,l.kt)("p",null,"\u5728\u901a\u8fc7\u5b9e\u9645\u8c03\u7814\u4ee5\u53ca\u5927\u91cf\u7684\u6d4b\u8bd5\u540e\uff0c\u57fa\u4e8eCurve\u7684\u6027\u80fd\uff08\u5c24\u5176\u662f\u5143\u6570\u636e\u65b9\u9762\uff0cCurveFS\u662f\u57fa\u4e8eRAFT\u4e00\u81f4\u6027\u534f\u8bae\u81ea\u7814\u7684\u5143\u6570\u636e\u5f15\u64ce\uff0c\u4e0e\u5176\u4ed6\u6ca1\u6709\u5143\u6570\u636e\u5f15\u64ce\u7684S3\u6587\u4ef6\u7cfb\u7edf(\u6bd4\u5982s3fs,ossfs)\u76f8\u6bd4\u5177\u5907\u5de8\u5927\u7684\u6027\u80fd\u4f18\u52bf\uff09\uff0c\u6613\u8fd0\u7ef4\uff0c\u7a33\u5b9a\u6027\uff0cCurve\u53ef\u4ee5\u540c\u65f6\u63d0\u4f9b\u5757\u5b58\u50a8\u4ee5\u53ca\u6587\u4ef6\u5b58\u50a8\u80fd\u529b\u7b49\u80fd\u529b\u4ee5\u53caCurve\u6d3b\u8dc3\u7684\u5f00\u6e90\u6c1b\u56f4\uff0c\u6700\u7ec8\u9009\u7528\u4e86CurveFS\u3002"),(0,l.kt)("h2",{id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5"},"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5"),(0,l.kt)("h3",{id:"curvefs\u662f\u4ec0\u4e48"},"CurveFS\u662f\u4ec0\u4e48"),(0,l.kt)("p",null,"CurveFS\u662f\u4e00\u4e2a\u57fa\u4e8e Fuse\u5b9e\u73b0\u7684\u517c\u5bb9POSIX \u63a5\u53e3\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\uff0c\u67b6\u6784\u5982\u4e0b\u56fe\u6240\u793a:"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"curvefs arch",src:r(8418).Z,width:"971",height:"569"})),(0,l.kt)("p",null,"CurveFS\u7531\u4e09\u4e2a\u90e8\u5206\u7ec4\u6210\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u5ba2\u6237\u7aefcurve-fuse\uff0c\u548c\u5143\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u5143\u6570\u636e\u589e\u5220\u6539\u67e5\u8bf7\u6c42\uff0c\u548c\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5143\u6570\u636e\u96c6\u7fa4metaserver cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u5143\u6570\u636e(inode\u548cdentry)\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002metaserver cluster\u7684\u67b6\u6784\u548cCurveBS\u7c7b\u4f3c\uff0c\u5177\u6709\u9ad8\u53ef\u9760\u3001\u9ad8\u53ef\u7528\u3001\u9ad8\u53ef\u6269\u7684\u7279\u70b9\uff1aMDS\u7528\u4e8e\u7ba1\u7406\u96c6\u7fa4\u62d3\u6251\u7ed3\u6784\uff0c\u8d44\u6e90\u8c03\u5ea6\u3002metaserver\u662f\u6570\u636e\u8282\u70b9\uff0c\u4e00\u4e2ametaserver\u5bf9\u5e94\u7ba1\u7406\u4e00\u4e2a\u7269\u7406\u78c1\u76d8\u3002CurveFS\u4f7f\u7528Raft\u4fdd\u8bc1\u5143\u6570\u636e\u7684\u53ef\u9760\u6027\u548c\u53ef\u7528\u6027\uff0cRaft\u590d\u5236\u7ec4\u7684\u57fa\u672c\u5355\u5143\u662fcopyset\u3002\u4e00\u4e2ametaserver\u4e0a\u5305\u542b\u591a\u4e2acopyset\u590d\u5236\u7ec4\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u6570\u636e\u96c6\u7fa4data cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u3002data cluster\u76ee\u524d\u652f\u6301\u4e24\u5b58\u50a8\u7c7b\u578b\uff1a\u652f\u6301S3\u63a5\u53e3\u7684\u5bf9\u8c61\u5b58\u50a8\u4ee5\u53caCurveBS\uff08\u5f00\u53d1\u4e2d\uff09\u3002")),(0,l.kt)("p",null,"Curve\u9664\u4e86\u65e2\u80fd\u652f\u6301\u6587\u4ef6\u5b58\u50a8\uff0c\u4e5f\u80fd\u652f\u6301\u5757\u5b58\u50a8\u4e4b\u5916\uff0c\u4ece\u4e0a\u8ff0\u67b6\u6784\u56fe\u6211\u4eec\u8fd8\u80fd\u770b\u51faCurve\u7684\u4e00\u4e2a\u7279\u70b9\uff1a\u5c31\u662fCurveFS\u540e\u7aef\u65e2\u53ef\u4ee5\u652f\u6301S3\uff0c\u4e5f\u53ef\u4ee5\u652f\u6301Curve\u5757\u5b58\u50a8\u3002\u8fd9\u6837\u7684\u7279\u70b9\u53ef\u4ee5\u4f7f\u5f97\u7528\u6237\u53ef\u4ee5\u9009\u62e9\u6027\u5730\u628a\u6027\u80fd\u8981\u6c42\u9ad8\u7684\u7cfb\u7edf\u7684\u6570\u636e\u5b58\u50a8\u5728Curve\u5757\u5b58\u50a8\u540e\u7aef\uff0c\u800c\u5bf9\u6210\u672c\u8981\u6c42\u8f83\u9ad8\u7684\u7cfb\u7edf\u53ef\u4ee5\u628a\u6570\u636e\u5b58\u50a8\u5728S3\u540e\u7aef\u3002"),(0,l.kt)("h3",{id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5-1"},"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5"),(0,l.kt)("h4",{id:"es\u4f7f\u7528curvefs"},"ES\u4f7f\u7528CurveFS"),(0,l.kt)("p",null,"CurveFS\u5b9a\u4f4d\u4e8e\u7f51\u6613\u8fd0\u7ef4\u7684\u4e91\u539f\u751f\u7cfb\u7edf\uff0c\u6240\u4ee5\u5176\u90e8\u7f72\u662f\u7b80\u5355\u5feb\u901f\u7684\uff0c\u901a\u8fc7CurveAdm\u5de5\u5177\uff0c\u53ea\u9700\u8981\u51e0\u6761\u547d\u4ee4\u8fb9\u4fbf\u53ef\u4ee5\u90e8\u7f72\u8d77CurveFS\u7684\u73af\u5883\uff0c\u5177\u4f53\u90e8\u7f72\u89c1",(0,l.kt)("a",{parentName:"p",href:"https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment"},"https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment")," ; ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment"},"https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment")," , \u90e8\u7f72\u540e\u6548\u679c\u5982\u4e0b\u56fe:"),(0,l.kt)("p",null,"\u5728\u65e5\u5fd7\u5b58\u50a8\u573a\u666f\uff0c\u6539\u9020\u662f\u5b8c\u5168\u57fa\u4e8e\u5386\u53f2\u7684\u670d\u52a1\u5668\u505a\u7684\u5728\u7ebf\u6539\u9020\u3002\u4e0b\u56fe\u662f\u7ebf\u4e0a\u65e5\u5fd7\u7684\u4e00\u4e2a\u5b58\u50a8\u67b6\u6784\u793a\u4f8b\uff0cnode0\u5230node5\u53ef\u4ee5\u8ba4\u4e3a\u662f\u70ed\u5b58\u50a8\u8282\u70b9\uff0c\u673a\u5668\u4e3a12 ",(0,l.kt)("em",{parentName:"p"}," 4T\uff0c128G\u7684\u5b58\u50a8\u673a\u578b\uff0c\u6bcf\u4e2a\u8282\u70b9\u8dd13\u4e2aES\u5b9e\u4f8b\uff0c\u6bcf\u4e2a\u5b9e\u4f8b32G\u5185\u5b58\uff0c4\u5757\u72ec\u7acb\u76d8\u3002node6\u5230node8\u4e3a12 ")," 8T\u7684\u5b58\u50a8\u673a\u578b\uff0c3\u53f0\u670d\u52a1\u5668\u8dd1\u4e00\u4e2aMinio\u96c6\u7fa4\uff0c\u6bcf\u53f0\u673a\u5668\u4e0a\u7684ES\u5b9e\u4f8b\u4e0d\u505a\u6570\u636e\u672c\u5730\u5199\u3002"),(0,l.kt)("p",null,"\u53ef\u4ee5\u770b\u5230\u4e3b\u8981\u7684\u6539\u9020\u91cd\u70b9\u662f\u5c06node6\u5230node8 3\u4e2a\u8282\u70b9\u8fdb\u884cES\u7684\u914d\u7f6e\u6539\u9020\uff0c\u5176\u4e2d\u4ee5node6\u8282\u70b9\u7684\u914d\u7f6e\u4e3a\u4f8b\uff1a"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'cluster.name: ops-elk\nnode.name: ${HOSTNAME}\nnetwork.host: [_local_,_bond0_]\nhttp.host: [_local_]\ndiscovery.zen.minimum_master_nodes: 1\naction.auto_create_index: true\ntransport.tcp.compress: true\nindices.fielddata.cache.size: 20%\npath.data: /home/nbs/elk/data1/data\npath.logs: /home/nbs/elk/data1/logs\n- /curvefs/mnt1\nxpack.ml.enabled: false\nxpack.monitoring.enabled: false\ndiscovery.zen.ping.unicast.hosts: ["ops-elk1:9300","ops-elk7:9300","ops-elk\n7:9300","ops-elk8.jdlt.163.org:9300"]\nnode.attr.box_type: cold\n')),(0,l.kt)("p",null,"\u5982\u914d\u7f6e\u6240\u793a\uff0c\u4e3b\u8981\u7684\u6539\u9020\u4e3a\u8c03\u6574ES\u7684\u6570\u636e\u5b58\u50a8\u76ee\u5f55\u5230CurveFS\u7684fuse\u6302\u8f7d\u76ee\u5f55\uff0c\u7136\u540e\u65b0\u589e node.attr.box_type \u7684\u8bbe\u7f6e\u3002\u5728node6\u5230node8\u4e0a\u5206\u522b\u914d\u7f6e\u4e3acold\uff0cnode1\u5230node5\u914d\u7f6e\u5bf9\u5e94\u5c5e\u6027\u4e3ahot\uff0c\u6240\u6709\u8282\u70b9\u914d\u7f6e\u5b8c\u6210\u540e\u8fdb\u884c\u4e00\u8f6e\u6eda\u52a8\u91cd\u542f\u3002"),(0,l.kt)("h4",{id:"es\u8bbe\u7f6e"},"ES\u8bbe\u7f6e"),(0,l.kt)("p",null,"\u9664\u4e86\u5e95\u5c42\u914d\u7f6e\u5916\uff0c\u5f88\u91cd\u8981\u7684\u4e00\u70b9\u5c31\u662f\u8c03\u6574index\u7d22\u5f15\u7684\u8bbe\u7f6e\u3002\u8fd9\u5757\u7684\u8bbe\u7f6e\u96be\u5ea6\u4e0d\u9ad8\uff0c\u8981\u70b9\u662f\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u5bf9\u5e94\u7d22\u5f15\u8bbe\u7f6e\u6570\u636e\u5206\u914d\u4f9d\u8d56\u548caliases"),(0,l.kt)("li",{parentName:"ol"},"\u8bbe\u7f6e\u5bf9\u5e94\u7684index Lifecycle policy")),(0,l.kt)("p",null,"\u5176\u5b9e\u5728\u65b0\u8282\u70b9\u5f00\u653e\u6570\u636e\u5b58\u50a8\u540e\uff0c\u5982\u679c\u6ca1\u6709\u4eb2\u548c\u6027\u8bbe\u7f6e\uff0c\u96c6\u7fa4\u9a6c\u4e0a\u4f1a\u542f\u52a8relocating\u64cd\u4f5c\u3002\u56e0\u6b64\u5efa\u8bae\u5bf9\u5b58\u91cf\u7684\u7d22\u5f15\u65b0\u589erouting.alloction.require\u7684\u8bbe\u7f6e\u6765\u907f\u514d\u70ed\u6570\u636e\u5206\u914d\u5230CurveFS\u5b58\u50a8\u8282\u70b9\u3002\u9488\u5bf9\u6bcf\u5929\u65b0\u589e\u7d22\u5f15\uff0c\u5efa\u8bae\u52a0\u5165\u4ee5\u4e0b\u8fd9\u6837\u7684index template\u914d\u7f6e\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'{\n "template": {\n "settings": {\n "index": {\n "lifecycle": {\n "name": "syslog",\n "rollover_alias": "syslog"\n },\n "routing": {\n "allocation": {\n "require": {\n "box_type": "hot"\n }\n }\n },\n "number_of_shards": "10",\n "translog": {\n "durability": "async"\n }\n }\n },\n "aliases": {\n "syslog": {}\n },\n "mappings": {}\n }\n}\n')),(0,l.kt)("p",null,"\u8fd9\u4e2aindex template\u8bbe\u7f6e\u7684\u6838\u5fc3\u8981\u70b9\u662f\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"routing\u90e8\u5206\u8981\u6307\u5b9a\u65b0\u7d22\u5f15\u5199\u5230\u70ed\u6570\u636e\u8282\u70b9\uff1b"),(0,l.kt)("li",{parentName:"ol"},"lifecycle\u4e2d\u7684\u65b0\u589erollover_alias\u8bbe\u7f6e\u3002index\u90e8\u5206\u7684lifecycle\u662f\u6307\u7d22\u5f15\u7684\u751f\u547d\u5468\u671f\u7b56\u7565\uff0c\u9700\u8981\u6ce8\u610frollover_alias\u91cc\u9762\u7684\u503c\u8981\u548c\u4e0b\u9762\u7684aliases\u5b9a\u4e49\u5bf9\u9f50\u3002")),(0,l.kt)("p",null,"\u51b7\u6570\u636e\u7684\u5207\u6362\uff0c\u53ef\u4ee5\u5728kibana\u7684index_lifecycle_management\u7ba1\u7406\u9875\u9762\u8bbe\u7f6e\u3002\u9488\u5bf9\u4e0a\u9762\u7684syslog\u573a\u666f\uff0chot\u90e8\u5206\u8bbe\u7f6e\u5982\u4e0b\u56fe\uff0c\u5176\u4f59\u57fa\u672c\u9ed8\u8ba4\u7684\u5c31\u53ef\u4ee5\u4e86\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es lifecycle",src:r(3531).Z,width:"1187",height:"958"})),(0,l.kt)("p",null,"\u5728\u7d22\u5f15\u5468\u671f\u7ba1\u7406\u914d\u7f6e\u9875\u9762\u4e2d\uff0c\u9664\u4e86\u8bbe\u7f6ehot phase\uff0c\u8fd8\u53ef\u4ee5\u8bbe\u7f6ewarm phase\uff0c\u5728warm phase\u53ef\u4ee5\u505a\u4e00\u4e9bshrink\uff0cforce merge\u7b49\u64cd\u4f5c\uff0c\u65e5\u5fd7\u5b58\u50a8\u573a\u666f\u6211\u4eec\u76f4\u63a5\u505ahot\u5230cold\u7684\u5904\u7406\u903b\u8f91\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es lifecycle",src:r(2353).Z,width:"1051",height:"605"})),(0,l.kt)("p",null,"\u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u65e5\u5fd7\u5b58\u50a8\u7c7b\u578b\u7684\u4e1a\u52a1\uff0c\u5e95\u5c42\u7d22\u5f15\u4e00\u65e6\u5b8c\u6210\u5199\u540e\u57fa\u672c\u4e0d\u505a\u518d\u6b21\u7684\u6570\u636e\u66f4\u6539\uff0c\u8bbe\u7f6e\u7d22\u5f15\u526f\u672c\u6570\u91cf\u4e3b\u8981\u662f\u4e3a\u4e86\u5e94\u5bf9\u5206\u5e03\u5f0f\u7cfb\u7edf\u8282\u70b9\u5b95\u673a\u7b49\u5f02\u5e38\u573a\u666f\u7684\u6570\u636e\u6062\u590d\u3002\u5982\u679c\u5b58\u50a8\u5c42\u9762\u6709\u66f4\u53ef\u9760\u7684\u65b9\u5f0f\uff0c\u90a3\u4e48\u81ea\u7136\u800c\u7136\u53ef\u4ee5\u5c06es\u7684\u526f\u672c\u6570\u91cf\u8c03\u6574\u4e3a0\uff0c\u53ef\u4ee5\u660e\u663e\u7684\u964d\u4f4e\u5bf9\u5b58\u50a8\u7a7a\u95f4\u7684\u4f7f\u7528\u9700\u6c42\u3002"))}p.isMDXComponent=!0},8418:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/curvefs_architecture-3f978bb3235810ab3994ccd36a79bbb8.png"},3531:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-lifecycle-d898155a236f4c986fc3b3c661b4f52d.png"},2353:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-lifecycle2-7d0dd139046b5927089ac7b60f757cf5.png"},2654:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-minio-a2f62f6d85d72301905b4437d8caaac1.png"}}]); \ No newline at end of file diff --git a/assets/js/bda932b4.da7302dc.js b/assets/js/bda932b4.da7302dc.js new file mode 100644 index 0000000..6281be0 --- /dev/null +++ b/assets/js/bda932b4.da7302dc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[9880],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>m});var n=r(7294);function l(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function u(e){for(var t=1;t=0||(l[r]=e[r]);return l}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(l[r]=e[r])}return l}var i=n.createContext({}),o=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):u(u({},t),e)),r},c=function(e){var t=o(e.components);return n.createElement(i.Provider,{value:t},e.children)},v="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,l=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),v=o(r),d=l,m=v["".concat(i,".").concat(d)]||v[d]||p[d]||a;return r?n.createElement(m,u(u({ref:t},c),{},{components:r})):n.createElement(m,u({ref:t},c))}));function m(e,t){var r=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var a=r.length,u=new Array(a);u[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[v]="string"==typeof e?e:l,u[1]=s;for(var o=2;o{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>u,default:()=>p,frontMatter:()=>a,metadata:()=>s,toc:()=>o});var n=r(7462),l=(r(7294),r(3905));const a={},u="CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",s={unversionedId:"CurveFS/usecase/elasticsearch-cold-data",id:"CurveFS/usecase/elasticsearch-cold-data",title:"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528",description:"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca",source:"@site/docs/03-CurveFS/01-usecase/06-elasticsearch-cold-data.md",sourceDirName:"03-CurveFS/01-usecase",slug:"/CurveFS/usecase/elasticsearch-cold-data",permalink:"/CurveFS/usecase/elasticsearch-cold-data",draft:!1,tags:[],version:"current",sidebarPosition:6,frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"CurveFS\u57fa\u4e8eminio-s3-gateway\u7684S3\u534f\u8bae\u652f\u6301",permalink:"/CurveFS/usecase/s3-gateway"},next:{title:"Curve \u6587\u4ef6\u7cfb\u7edf\u4e3a AI \u4e1a\u52a1\u964d\u672c\u589e\u6548",permalink:"/CurveFS/usecase/ai-storage"}},i={},o=[{value:"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca",id:"es\u4f7f\u7528curvefs\u7684\u6536\u76ca",level:2},{value:"CurveFS\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf",level:3},{value:"CurveFS\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf",id:"curvefs\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf",level:3},{value:"ES\u4f7f\u7528\u80cc\u666f",id:"es\u4f7f\u7528\u80cc\u666f",level:2},{value:"\u7f51\u6613ES\u9009\u7528CurveFS\u7684\u539f\u56e0",id:"\u7f51\u6613es\u9009\u7528curvefs\u7684\u539f\u56e0",level:3},{value:"\u672c\u5730\u76d8\u5230minio",id:"\u672c\u5730\u76d8\u5230minio",level:4},{value:"minio\u5230CurveFS",id:"minio\u5230curvefs",level:4},{value:"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5",id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5",level:2},{value:"CurveFS\u662f\u4ec0\u4e48",id:"curvefs\u662f\u4ec0\u4e48",level:3},{value:"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5",id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5-1",level:3},{value:"ES\u4f7f\u7528CurveFS",id:"es\u4f7f\u7528curvefs",level:4},{value:"ES\u8bbe\u7f6e",id:"es\u8bbe\u7f6e",level:4}],c={toc:o},v="wrapper";function p(e){let{components:t,...a}=e;return(0,l.kt)(v,(0,n.Z)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("h1",{id:"curvefs\u5728elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528"},"CurveFS\u5728Elasticsearch\u51b7\u70ed\u6570\u636e\u5b58\u50a8\u4e2d\u7684\u5e94\u7528"),(0,l.kt)("h2",{id:"es\u4f7f\u7528curvefs\u7684\u6536\u76ca"},"ES\u4f7f\u7528CurveFS\u7684\u6536\u76ca"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6210\u672c\u4f18\u52bf"),(0,l.kt)("p",null,"\u4e3a\u4e86\u9ad8\u53ef\u9760\uff0cES\u5982\u679c\u4f7f\u7528\u672c\u5730\u76d8\u7684\u8bdd\u4e00\u822c\u4f1a\u4f7f\u7528\u4e24\u526f\u672c\uff0c\u4e5f\u5c31\u662f\u8bf4\u5b58\u50a81PB\u6570\u636e\u9700\u89812PB\u7684\u7269\u7406\u7a7a\u95f4\u3002\u4f46\u662f\u5982\u679c\u4f7f\u7528CurveFS\uff0c\u7531\u4e8eCurveFS\u7684\u540e\u7aef\u53ef\u4ee5\u5bf9\u63a5S3\uff0c\u6240\u4ee5\u53ef\u4ee5\u5229\u7528\u5bf9\u8c61\u5b58\u50a8\u63d0\u4f9b\u7684EC\u80fd\u529b\uff0c\u65e2\u4fdd\u8bc1\u4e86\u53ef\u9760\u6027\uff0c\u53c8\u53ef\u4ee5\u51cf\u5c11\u526f\u672c\u6570\u91cf\uff0c\u4ece\u800c\u8fbe\u5230\u4e86\u964d\u4f4e\u6210\u672c\u7684\u76ee\u7684\u3002"),(0,l.kt)("p",null,"\u4ee5\u7f51\u6613\u5bf9\u8c61\u5b58\u50a8\u8fd9\u8fb9\u5f53\u524d\u4e3b\u6d41\u7684EC 20+4\u4f7f\u7528\u4e3a\u4f8b\uff0c\u8be5\u4f7f\u7528\u65b9\u5f0f\u5c31\u76f8\u5f53\u4e8e\u662f1.2\u526f\u672c\u3002\u6240\u4ee5\u5982\u679c\u4ee5ES\u9700\u89811PB\u4f7f\u7528\u7a7a\u95f4\u4e3a\u4f8b\uff0c\u90a3\u4e48\u4f7f\u7528CurveFS+1.2\u526f\u672c\u5bf9\u8c61\u5b58\u50a8\u53ea\u9700\u89811.2PB\u7a7a\u95f4\uff0c\u76f8\u6bd4\u672c\u5730\u76d82\u526f\u672c\u53ef\u4ee5\u8282\u7701800TB\u5de6\u53f3\u7684\u5bb9\u91cf\uff0c\u6210\u672c\u4f18\u5316\u6548\u679c\u975e\u5e38\u663e\u8457\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6027\u80fd\u4f18\u52bf"),(0,l.kt)("p",null,"\u4ee5\u4e0b\u6587\u5c06\u8981\u4ecb\u7ecd\u7684\u4f7f\u7528\u573a\u666f\u4e3a\u4f8b\uff0c\u5bf9\u6bd4ES\u539f\u6765\u4f7f\u7528S3\u63d2\u4ef6\u505asnapshot\u8f6c\u5b58\u50a8\u7684\u65b9\u5f0f\uff0c\u7531\u4e8e\u6bcf\u6b21\u64cd\u4f5c\u7684\u65f6\u5019\u7d22\u5f15\u9700\u8981\u8fdb\u884crestore\u64cd\u4f5c\uff0c\u4ee5100G\u7684\u65e5\u5fd7\u7d22\u5f15\u4e3a\u4f8b\uff0c\u53e6\u5916\u4f1a\u6709\u4f20\u8f93\u65f6\u95f4\uff0c\u5982\u679crestore\u7684\u6062\u590d\u901f\u5ea6\u4e3a100M\uff0c\u90a3\u4e48\u4e5f\u8981300\u591a\u79d2\u3002\u5b9e\u9645\u60c5\u51b5\u662f\u5728\u4e00\u4e2a\u5927\u91cf\u5199\u5165\u7684\u96c6\u7fa4\uff0c\u8fd9\u6837\u7684\u64cd\u4f5c\u53ef\u80fd\u8981\u51e0\u4e2a\u5c0f\u65f6\u3002"),(0,l.kt)("p",null,"\u800c\u4f7f\u7528CurveFS\u540e\u7684\u65b0\u6a21\u5f0f\u4e0b\u57fa\u672c\u4e0a\u53ea\u8981\u5bf9freeze\u7684\u7d22\u5f15\u8fdb\u884cunfreeze\uff0c\u8ba9\u5bf9\u5e94\u8282\u70b9\u7684ES\u5c06\u5bf9\u5e94\u7684meta\u6570\u636e\u8f7d\u5165\u5185\u5b58\u5c31\u53ef\u4ee5\u6267\u884c\u7d22\u5f15\uff0c\u5927\u6982\u8017\u65f6\u4ec5\u970030S\u5de6\u53f3\uff0c\u76f8\u6bd4\u76f4\u63a5\u7528S3\u5b58\u50a8\u51b7\u6570\u636e\u6709\u6570\u91cf\u7ea7\u7684\u4e0b\u964d\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u5bb9\u91cf\u4f18\u52bf"),(0,l.kt)("p",null,"\u672c\u5730\u76d8\u7684\u5bb9\u91cf\u662f\u6709\u9650\u7684\uff0c\u800cCurveFS\u7684\u7a7a\u95f4\u5bb9\u91cf\u53ef\u4ee5\u5728\u7ebf\u65e0\u9650\u6269\u5c55\u3002\u540c\u65f6\u51cf\u5c11\u4e86\u672c\u5730\u5b58\u50a8\u7684\u7ef4\u62a4\u4ee3\u4ef7\u3002"),(0,l.kt)("h3",{id:"curvefs\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf"},"CurveFS\u63d0\u4f9b\u7684\u6613\u8fd0\u7ef4\u4f18\u52bf"),(0,l.kt)("p",null,"ES\u4f7f\u7528\u672c\u5730\u76d8\u4ee5\u53ca\u4f7f\u7528S3\u63d2\u4ef6\u65b9\u5f0f\uff0c\u5f53\u9700\u8981\u6269\u5bb9\u6216\u8005\u8282\u70b9\u5f02\u5e38\u6062\u590d\u65f6\uff0c\u9700\u8981\u589e\u52a0\u4eba\u529b\u8fd0\u7ef4\u6210\u672c\u3002CurveFS\u5b9e\u73b0\u4e4b\u521d\u7684\u4e00\u4e2a\u76ee\u6807\u5c31\u662f\u6613\u8fd0\u7ef4\uff0c\u6240\u4ee5CurveFS\u53ef\u4ee5\u5b9e\u73b0\u6570\u6761\u547d\u4ee4\u7684\u5feb\u901f\u90e8\u7f72\u4ee5\u53ca\u6545\u969c\u81ea\u6108\u80fd\u529b\u3002"),(0,l.kt)("p",null,"\u53e6\u5916\u5982\u679cES\u4f7f\u7528CurveFS\uff0c\u5c31\u5b9e\u73b0\u4e86\u5b58\u7b97\u5206\u79bb\uff0c\u8fdb\u4e00\u6b65\u91ca\u653e\u4e86ES\u4f7f\u7528\u8005\u7684\u8fd0\u7ef4\u8d1f\u62c5\u3002"),(0,l.kt)("h2",{id:"es\u4f7f\u7528\u80cc\u666f"},"ES\u4f7f\u7528\u80cc\u666f"),(0,l.kt)("p",null,"\u5728\u751f\u4ea7\u73af\u5883\u6709\u5927\u91cf\u7684\u573a\u666f\u4f1a\u7528\u5230ES\u505a\u6587\u6863\u3001\u65e5\u5fd7\u5b58\u50a8\u540e\u7aef\uff0c\u56e0\u4e3aES\u4f18\u79c0\u7684\u5168\u6587\u68c0\u7d22\u80fd\u529b\u5728\u5f88\u591a\u65f6\u5019\u53ef\u4ee5\u5927\u5927\u7684\u7b80\u5316\u76f8\u5173\u7cfb\u7edf\u8bbe\u8ba1\u7684\u590d\u6742\u5ea6\u3002\u6bd4\u8f83\u5e38\u89c1\u7684\u4e3a\u65e5\u5fd7\u5b58\u50a8\uff0c\u94fe\u8def\u8ffd\u8e2a\uff0c\u751a\u81f3\u662f\u76d1\u63a7\u6307\u6807\u7b49\u573a\u666f\u90fd\u53ef\u4ee5\u7528ES\u6765\u505a\u3002"),(0,l.kt)("h3",{id:"\u7f51\u6613es\u9009\u7528curvefs\u7684\u539f\u56e0"},"\u7f51\u6613ES\u9009\u7528CurveFS\u7684\u539f\u56e0"),(0,l.kt)("p",null,"\u7f51\u6613ES\u5e95\u5c42\u5b58\u50a8\u7684\u4f7f\u7528\u7ecf\u5386\u4e86\u4ece\u672c\u5730\u76d8\u5230minio\uff0c\u518d\u4eceminio\u5230CurveFS\u8fd9\u4e00\u8fc7\u7a0b\u3002"),(0,l.kt)("h4",{id:"\u672c\u5730\u76d8\u5230minio"},"\u672c\u5730\u76d8\u5230minio"),(0,l.kt)("p",null,"\u4e3a\u4e86\u7b26\u5408\u56fd\u5185\u7684\u6cd5\u5f8b\u7ea6\u675f\uff0c\u7ebf\u4e0a\u7cfb\u7edf\u9700\u8981\u6309\u7167\u8981\u6c42\u5b58\u50a86\u4e2a\u6708\u52301\u5e74\u4e0d\u7b49\u7684\u7cfb\u7edf\u65e5\u5fd7\uff0c\u4e3b\u8981\u662f\u56fd\u5185\u7b49\u4fdd\u3001\u91d1\u878d\u5408\u89c4\u7b49\u573a\u666f\u3002\u6309\u7167\u5185\u90e8\u7ba1\u7406\u7684\u670d\u52a1\u5668\u6570\u91cf\uff0c\u5355\u7eafsyslog\u7684\u65e5\u5fd7\u5b58\u50a8\u7a7a\u95f4\u6bcf\u5929\u5c31\u9700\u89811T\uff0c\u6309\u7167\u5f53\u524d\u624b\u5934\u6709\u76845\u53f012\u76d8\u4f4d4T\u786c\u76d8\u7684\u670d\u52a1\u5668\uff0c\u6700\u591a\u53ea\u80fd\u5b58\u50a8200\u591a\u5929\u7684\u65e5\u5b50\uff0c\u65e0\u6cd5\u6ee1\u8db3\u65e5\u5fd7\u5b58\u50a81\u5e74\u7684\u9700\u6c42\u3002"),(0,l.kt)("p",null,"\u9488\u5bf9ES\u4f7f\u7528\u672c\u5730\u76d8\u65e0\u6cd5\u6ee1\u8db3\u5b58\u50a8\u5bb9\u91cf\u9700\u6c42\u8fd9\u4e00\u60c5\u51b5\uff0c\u7f51\u6613ES\u5e95\u5c42\u5b58\u50a8\u4e4b\u524d\u5355\u72ec\u5f15\u5165\u8fc7\u57fa\u4e8eS3\u7684\u5b58\u50a8\u65b9\u6848\u6765\u964d\u4f4e\u5b58\u50a8\u7a7a\u95f4\u7684\u6d88\u8017\u3002\u5982\u4e0b\u56fe\uff0cES\u914d\u5408minio\u505a\u6570\u636e\u5b58\u50a8\u7a7a\u95f4\u7684\u538b\u7f29\u3002\u4e3e\u4f8b\u6765\u8bf4100G\u7684\u65e5\u5fd7\uff0c\u5230\u4e86ES\u91cc\u9762\u56e0\u4e3a\u53ef\u9760\u6027\u9700\u6c42\uff0c\u9700\u8981\u53cc\u526f\u672c\uff0c\u4f1a\u4f7f\u7528200G\u7684\u7a7a\u95f4\u3002ES\u9488\u5bf9\u7d22\u5f15\u5206\u7247\u65f6\u95f4\uff0c\u5b9a\u671f\u6027\u8f6c\u5b58\u50a8\u5230minio\u4ed3\u5e93\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es minio",src:r(2654).Z,width:"863",height:"85"})),(0,l.kt)("h4",{id:"minio\u5230curvefs"},"minio\u5230CurveFS"),(0,l.kt)("p",null,"\u8fd9\u4e2a\u65b9\u6848\u4ece\u4e00\u5b9a\u7a0b\u5ea6\u4e0a\u7f13\u89e3\u4e86\u5b58\u50a8\u7a7a\u95f4\u7684\u8d44\u6e90\u95ee\u9898\uff0c\u4f46\u662f\u5b9e\u9645\u4f7f\u7528\u7684\u65f6\u5019\u8fd8\u4f1a\u611f\u89c9\u975e\u5e38\u4e0d\u4fbf\u5229\u3002"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},"\u8fd0\u7ef4\u6210\u672c\u3002ES\u8282\u70b9\u5347\u7ea7\u7684\u65f6\u5019\u9700\u8981\u989d\u5916\u5378\u8f7d\u5b89\u88c5S3\u63d2\u4ef6\uff0c\u6709\u4e00\u5b9a\u7684\u8fd0\u7ef4\u6210\u672c\u3002"),(0,l.kt)("li",{parentName:"ul"},"\u6027\u80fd\u74f6\u9888\u3002\u81ea\u5df1\u79c1\u6709\u5316\u642d\u5efa\u7684Minio\u968f\u7740bucket\u91cc\u9762\u6570\u636e\u91cf\u7684\u589e\u957f\uff0c\u6570\u636e\u5b58\u50a8\u548c\u62bd\u53d6\u90fd\u4f1a\u6210\u4e3a\u4e00\u4e2a\u5f88\u5927\u7684\u95ee\u9898"),(0,l.kt)("li",{parentName:"ul"},"\u7a33\u5b9a\u6027\u95ee\u9898\u3002\u5728\u5185\u90e8\u642d\u5efa\u7684Minio\u96c6\u7fa4\u5728\u505a\u6570\u636erestore\u7684\u65f6\u5019\uff0c\u56e0\u4e3a\u6587\u4ef6\u5904\u7406\u6027\u80fd\u7b49\u56e0\u7d20\uff0c\u7ecf\u5e38\u9047\u5230\u8bbf\u95ee\u8d85\u65f6\u7b49\u573a\u666f\uff0c\u6240\u4ee5\u4e00\u76f4\u5728\u5173\u6ce8\u662f\u5426\u6709\u76f8\u5173\u7684\u7cfb\u7edf\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u8bfb\u5199\u7a33\u5b9a\u6027\u3002")),(0,l.kt)("p",null,"\u7531\u4e8eS3\u534f\u8bae\u7ecf\u8fc7\u591a\u5e74\u7684\u6f14\u5316\uff0c\u5df2\u7ecf\u6210\u4e86\u5bf9\u8c61\u5b58\u50a8\u7684\u5de5\u4e1a\u6807\u51c6\u3002\u5f88\u591a\u4eba\u90fd\u6709\u60f3\u8fc7\u7528fuse\u7684\u65b9\u5f0f\u4f7f\u7528S3\u7684\u5b58\u50a8\u80fd\u529b\u3002\u4e8b\u5b9e\u4e0a\u57fa\u4e8eS3\u7684\u6587\u4ef6\u7cfb\u7edf\u6709\u5f88\u591a\u6b3e\uff0c\u4f8b\u5982\u5f00\u6e90\u7684s3fs-fuse\u3001ossfs\u3001RioFS\u3001CurveFS\u7b49\u3002"),(0,l.kt)("p",null,"\u5728\u901a\u8fc7\u5b9e\u9645\u8c03\u7814\u4ee5\u53ca\u5927\u91cf\u7684\u6d4b\u8bd5\u540e\uff0c\u57fa\u4e8eCurve\u7684\u6027\u80fd\uff08\u5c24\u5176\u662f\u5143\u6570\u636e\u65b9\u9762\uff0cCurveFS\u662f\u57fa\u4e8eRAFT\u4e00\u81f4\u6027\u534f\u8bae\u81ea\u7814\u7684\u5143\u6570\u636e\u5f15\u64ce\uff0c\u4e0e\u5176\u4ed6\u6ca1\u6709\u5143\u6570\u636e\u5f15\u64ce\u7684S3\u6587\u4ef6\u7cfb\u7edf(\u6bd4\u5982s3fs,ossfs)\u76f8\u6bd4\u5177\u5907\u5de8\u5927\u7684\u6027\u80fd\u4f18\u52bf\uff09\uff0c\u6613\u8fd0\u7ef4\uff0c\u7a33\u5b9a\u6027\uff0cCurve\u53ef\u4ee5\u540c\u65f6\u63d0\u4f9b\u5757\u5b58\u50a8\u4ee5\u53ca\u6587\u4ef6\u5b58\u50a8\u80fd\u529b\u7b49\u80fd\u529b\u4ee5\u53caCurve\u6d3b\u8dc3\u7684\u5f00\u6e90\u6c1b\u56f4\uff0c\u6700\u7ec8\u9009\u7528\u4e86CurveFS\u3002"),(0,l.kt)("h2",{id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5"},"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5"),(0,l.kt)("h3",{id:"curvefs\u662f\u4ec0\u4e48"},"CurveFS\u662f\u4ec0\u4e48"),(0,l.kt)("p",null,"CurveFS\u662f\u4e00\u4e2a\u57fa\u4e8e Fuse\u5b9e\u73b0\u7684\u517c\u5bb9POSIX \u63a5\u53e3\u7684\u5206\u5e03\u5f0f\u6587\u4ef6\u7cfb\u7edf\uff0c\u67b6\u6784\u5982\u4e0b\u56fe\u6240\u793a:"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"curvefs arch",src:r(8418).Z,width:"971",height:"569"})),(0,l.kt)("p",null,"CurveFS\u7531\u4e09\u4e2a\u90e8\u5206\u7ec4\u6210\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u5ba2\u6237\u7aefcurve-fuse\uff0c\u548c\u5143\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u5143\u6570\u636e\u589e\u5220\u6539\u67e5\u8bf7\u6c42\uff0c\u548c\u6570\u636e\u96c6\u7fa4\u4ea4\u4e92\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u5143\u6570\u636e\u96c6\u7fa4metaserver cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u5143\u6570\u636e(inode\u548cdentry)\u7684\u589e\u5220\u6539\u67e5\u8bf7\u6c42\u3002metaserver cluster\u7684\u67b6\u6784\u548cCurveBS\u7c7b\u4f3c\uff0c\u5177\u6709\u9ad8\u53ef\u9760\u3001\u9ad8\u53ef\u7528\u3001\u9ad8\u53ef\u6269\u7684\u7279\u70b9\uff1aMDS\u7528\u4e8e\u7ba1\u7406\u96c6\u7fa4\u62d3\u6251\u7ed3\u6784\uff0c\u8d44\u6e90\u8c03\u5ea6\u3002metaserver\u662f\u6570\u636e\u8282\u70b9\uff0c\u4e00\u4e2ametaserver\u5bf9\u5e94\u7ba1\u7406\u4e00\u4e2a\u7269\u7406\u78c1\u76d8\u3002CurveFS\u4f7f\u7528Raft\u4fdd\u8bc1\u5143\u6570\u636e\u7684\u53ef\u9760\u6027\u548c\u53ef\u7528\u6027\uff0cRaft\u590d\u5236\u7ec4\u7684\u57fa\u672c\u5355\u5143\u662fcopyset\u3002\u4e00\u4e2ametaserver\u4e0a\u5305\u542b\u591a\u4e2acopyset\u590d\u5236\u7ec4\u3002"),(0,l.kt)("li",{parentName:"ol"},"\u6570\u636e\u96c6\u7fa4data cluster\uff0c\u7528\u4e8e\u63a5\u6536\u548c\u5904\u7406\u6587\u4ef6\u6570\u636e\u7684\u589e\u5220\u6539\u67e5\u3002data cluster\u76ee\u524d\u652f\u6301\u4e24\u5b58\u50a8\u7c7b\u578b\uff1a\u652f\u6301S3\u63a5\u53e3\u7684\u5bf9\u8c61\u5b58\u50a8\u4ee5\u53caCurveBS\uff08\u5f00\u53d1\u4e2d\uff09\u3002")),(0,l.kt)("p",null,"Curve\u9664\u4e86\u65e2\u80fd\u652f\u6301\u6587\u4ef6\u5b58\u50a8\uff0c\u4e5f\u80fd\u652f\u6301\u5757\u5b58\u50a8\u4e4b\u5916\uff0c\u4ece\u4e0a\u8ff0\u67b6\u6784\u56fe\u6211\u4eec\u8fd8\u80fd\u770b\u51faCurve\u7684\u4e00\u4e2a\u7279\u70b9\uff1a\u5c31\u662fCurveFS\u540e\u7aef\u65e2\u53ef\u4ee5\u652f\u6301S3\uff0c\u4e5f\u53ef\u4ee5\u652f\u6301Curve\u5757\u5b58\u50a8\u3002\u8fd9\u6837\u7684\u7279\u70b9\u53ef\u4ee5\u4f7f\u5f97\u7528\u6237\u53ef\u4ee5\u9009\u62e9\u6027\u5730\u628a\u6027\u80fd\u8981\u6c42\u9ad8\u7684\u7cfb\u7edf\u7684\u6570\u636e\u5b58\u50a8\u5728Curve\u5757\u5b58\u50a8\u540e\u7aef\uff0c\u800c\u5bf9\u6210\u672c\u8981\u6c42\u8f83\u9ad8\u7684\u7cfb\u7edf\u53ef\u4ee5\u628a\u6570\u636e\u5b58\u50a8\u5728S3\u540e\u7aef\u3002"),(0,l.kt)("h3",{id:"curvefs\u7ed3\u5408es\u7684\u5b9e\u8df5-1"},"CurveFS\u7ed3\u5408ES\u7684\u5b9e\u8df5"),(0,l.kt)("h4",{id:"es\u4f7f\u7528curvefs"},"ES\u4f7f\u7528CurveFS"),(0,l.kt)("p",null,"CurveFS\u5b9a\u4f4d\u4e8e\u7f51\u6613\u8fd0\u7ef4\u7684\u4e91\u539f\u751f\u7cfb\u7edf\uff0c\u6240\u4ee5\u5176\u90e8\u7f72\u662f\u7b80\u5355\u5feb\u901f\u7684\uff0c\u901a\u8fc7CurveAdm\u5de5\u5177\uff0c\u53ea\u9700\u8981\u51e0\u6761\u547d\u4ee4\u8fb9\u4fbf\u53ef\u4ee5\u90e8\u7f72\u8d77CurveFS\u7684\u73af\u5883\uff0c\u5177\u4f53\u90e8\u7f72\u89c1",(0,l.kt)("a",{parentName:"p",href:"https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment"},"https://github.com/opencurve/curveadm/wiki/curvefs-cluster-deployment")," ; ",(0,l.kt)("a",{parentName:"p",href:"https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment"},"https://github.com/opencurve/curveadm/wiki/curvefs-client-deployment")," , \u90e8\u7f72\u540e\u6548\u679c\u5982\u4e0b\u56fe:"),(0,l.kt)("p",null,"\u5728\u65e5\u5fd7\u5b58\u50a8\u573a\u666f\uff0c\u6539\u9020\u662f\u5b8c\u5168\u57fa\u4e8e\u5386\u53f2\u7684\u670d\u52a1\u5668\u505a\u7684\u5728\u7ebf\u6539\u9020\u3002\u4e0b\u56fe\u662f\u7ebf\u4e0a\u65e5\u5fd7\u7684\u4e00\u4e2a\u5b58\u50a8\u67b6\u6784\u793a\u4f8b\uff0cnode0\u5230node5\u53ef\u4ee5\u8ba4\u4e3a\u662f\u70ed\u5b58\u50a8\u8282\u70b9\uff0c\u673a\u5668\u4e3a12 ",(0,l.kt)("em",{parentName:"p"}," 4T\uff0c128G\u7684\u5b58\u50a8\u673a\u578b\uff0c\u6bcf\u4e2a\u8282\u70b9\u8dd13\u4e2aES\u5b9e\u4f8b\uff0c\u6bcf\u4e2a\u5b9e\u4f8b32G\u5185\u5b58\uff0c4\u5757\u72ec\u7acb\u76d8\u3002node6\u5230node8\u4e3a12 ")," 8T\u7684\u5b58\u50a8\u673a\u578b\uff0c3\u53f0\u670d\u52a1\u5668\u8dd1\u4e00\u4e2aMinio\u96c6\u7fa4\uff0c\u6bcf\u53f0\u673a\u5668\u4e0a\u7684ES\u5b9e\u4f8b\u4e0d\u505a\u6570\u636e\u672c\u5730\u5199\u3002"),(0,l.kt)("p",null,"\u53ef\u4ee5\u770b\u5230\u4e3b\u8981\u7684\u6539\u9020\u91cd\u70b9\u662f\u5c06node6\u5230node8 3\u4e2a\u8282\u70b9\u8fdb\u884cES\u7684\u914d\u7f6e\u6539\u9020\uff0c\u5176\u4e2d\u4ee5node6\u8282\u70b9\u7684\u914d\u7f6e\u4e3a\u4f8b\uff1a"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'cluster.name: ops-elk\nnode.name: ${HOSTNAME}\nnetwork.host: [_local_,_bond0_]\nhttp.host: [_local_]\ndiscovery.zen.minimum_master_nodes: 1\naction.auto_create_index: true\ntransport.tcp.compress: true\nindices.fielddata.cache.size: 20%\npath.data: /home/nbs/elk/data1/data\npath.logs: /home/nbs/elk/data1/logs\n- /curvefs/mnt1\nxpack.ml.enabled: false\nxpack.monitoring.enabled: false\ndiscovery.zen.ping.unicast.hosts: ["ops-elk1:9300","ops-elk7:9300","ops-elk\n7:9300","ops-elk8.jdlt.163.org:9300"]\nnode.attr.box_type: cold\n')),(0,l.kt)("p",null,"\u5982\u914d\u7f6e\u6240\u793a\uff0c\u4e3b\u8981\u7684\u6539\u9020\u4e3a\u8c03\u6574ES\u7684\u6570\u636e\u5b58\u50a8\u76ee\u5f55\u5230CurveFS\u7684fuse\u6302\u8f7d\u76ee\u5f55\uff0c\u7136\u540e\u65b0\u589e node.attr.box_type \u7684\u8bbe\u7f6e\u3002\u5728node6\u5230node8\u4e0a\u5206\u522b\u914d\u7f6e\u4e3acold\uff0cnode1\u5230node5\u914d\u7f6e\u5bf9\u5e94\u5c5e\u6027\u4e3ahot\uff0c\u6240\u6709\u8282\u70b9\u914d\u7f6e\u5b8c\u6210\u540e\u8fdb\u884c\u4e00\u8f6e\u6eda\u52a8\u91cd\u542f\u3002"),(0,l.kt)("h4",{id:"es\u8bbe\u7f6e"},"ES\u8bbe\u7f6e"),(0,l.kt)("p",null,"\u9664\u4e86\u5e95\u5c42\u914d\u7f6e\u5916\uff0c\u5f88\u91cd\u8981\u7684\u4e00\u70b9\u5c31\u662f\u8c03\u6574index\u7d22\u5f15\u7684\u8bbe\u7f6e\u3002\u8fd9\u5757\u7684\u8bbe\u7f6e\u96be\u5ea6\u4e0d\u9ad8\uff0c\u8981\u70b9\u662f\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"\u5bf9\u5e94\u7d22\u5f15\u8bbe\u7f6e\u6570\u636e\u5206\u914d\u4f9d\u8d56\u548caliases"),(0,l.kt)("li",{parentName:"ol"},"\u8bbe\u7f6e\u5bf9\u5e94\u7684index Lifecycle policy")),(0,l.kt)("p",null,"\u5176\u5b9e\u5728\u65b0\u8282\u70b9\u5f00\u653e\u6570\u636e\u5b58\u50a8\u540e\uff0c\u5982\u679c\u6ca1\u6709\u4eb2\u548c\u6027\u8bbe\u7f6e\uff0c\u96c6\u7fa4\u9a6c\u4e0a\u4f1a\u542f\u52a8relocating\u64cd\u4f5c\u3002\u56e0\u6b64\u5efa\u8bae\u5bf9\u5b58\u91cf\u7684\u7d22\u5f15\u65b0\u589erouting.alloction.require\u7684\u8bbe\u7f6e\u6765\u907f\u514d\u70ed\u6570\u636e\u5206\u914d\u5230CurveFS\u5b58\u50a8\u8282\u70b9\u3002\u9488\u5bf9\u6bcf\u5929\u65b0\u589e\u7d22\u5f15\uff0c\u5efa\u8bae\u52a0\u5165\u4ee5\u4e0b\u8fd9\u6837\u7684index template\u914d\u7f6e\u3002"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre"},'{\n "template": {\n "settings": {\n "index": {\n "lifecycle": {\n "name": "syslog",\n "rollover_alias": "syslog"\n },\n "routing": {\n "allocation": {\n "require": {\n "box_type": "hot"\n }\n }\n },\n "number_of_shards": "10",\n "translog": {\n "durability": "async"\n }\n }\n },\n "aliases": {\n "syslog": {}\n },\n "mappings": {}\n }\n}\n')),(0,l.kt)("p",null,"\u8fd9\u4e2aindex template\u8bbe\u7f6e\u7684\u6838\u5fc3\u8981\u70b9\u662f\uff1a"),(0,l.kt)("ol",null,(0,l.kt)("li",{parentName:"ol"},"routing\u90e8\u5206\u8981\u6307\u5b9a\u65b0\u7d22\u5f15\u5199\u5230\u70ed\u6570\u636e\u8282\u70b9\uff1b"),(0,l.kt)("li",{parentName:"ol"},"lifecycle\u4e2d\u7684\u65b0\u589erollover_alias\u8bbe\u7f6e\u3002index\u90e8\u5206\u7684lifecycle\u662f\u6307\u7d22\u5f15\u7684\u751f\u547d\u5468\u671f\u7b56\u7565\uff0c\u9700\u8981\u6ce8\u610frollover_alias\u91cc\u9762\u7684\u503c\u8981\u548c\u4e0b\u9762\u7684aliases\u5b9a\u4e49\u5bf9\u9f50\u3002")),(0,l.kt)("p",null,"\u51b7\u6570\u636e\u7684\u5207\u6362\uff0c\u53ef\u4ee5\u5728kibana\u7684index_lifecycle_management\u7ba1\u7406\u9875\u9762\u8bbe\u7f6e\u3002\u9488\u5bf9\u4e0a\u9762\u7684syslog\u573a\u666f\uff0chot\u90e8\u5206\u8bbe\u7f6e\u5982\u4e0b\u56fe\uff0c\u5176\u4f59\u57fa\u672c\u9ed8\u8ba4\u7684\u5c31\u53ef\u4ee5\u4e86\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es lifecycle",src:r(3531).Z,width:"1187",height:"958"})),(0,l.kt)("p",null,"\u5728\u7d22\u5f15\u5468\u671f\u7ba1\u7406\u914d\u7f6e\u9875\u9762\u4e2d\uff0c\u9664\u4e86\u8bbe\u7f6ehot phase\uff0c\u8fd8\u53ef\u4ee5\u8bbe\u7f6ewarm phase\uff0c\u5728warm phase\u53ef\u4ee5\u505a\u4e00\u4e9bshrink\uff0cforce merge\u7b49\u64cd\u4f5c\uff0c\u65e5\u5fd7\u5b58\u50a8\u573a\u666f\u6211\u4eec\u76f4\u63a5\u505ahot\u5230cold\u7684\u5904\u7406\u903b\u8f91\u3002"),(0,l.kt)("p",null,(0,l.kt)("img",{alt:"es lifecycle",src:r(2353).Z,width:"1051",height:"605"})),(0,l.kt)("p",null,"\u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u65e5\u5fd7\u5b58\u50a8\u7c7b\u578b\u7684\u4e1a\u52a1\uff0c\u5e95\u5c42\u7d22\u5f15\u4e00\u65e6\u5b8c\u6210\u5199\u540e\u57fa\u672c\u4e0d\u505a\u518d\u6b21\u7684\u6570\u636e\u66f4\u6539\uff0c\u8bbe\u7f6e\u7d22\u5f15\u526f\u672c\u6570\u91cf\u4e3b\u8981\u662f\u4e3a\u4e86\u5e94\u5bf9\u5206\u5e03\u5f0f\u7cfb\u7edf\u8282\u70b9\u5b95\u673a\u7b49\u5f02\u5e38\u573a\u666f\u7684\u6570\u636e\u6062\u590d\u3002\u5982\u679c\u5b58\u50a8\u5c42\u9762\u6709\u66f4\u53ef\u9760\u7684\u65b9\u5f0f\uff0c\u90a3\u4e48\u81ea\u7136\u800c\u7136\u53ef\u4ee5\u5c06es\u7684\u526f\u672c\u6570\u91cf\u8c03\u6574\u4e3a0\uff0c\u53ef\u4ee5\u660e\u663e\u7684\u964d\u4f4e\u5bf9\u5b58\u50a8\u7a7a\u95f4\u7684\u4f7f\u7528\u9700\u6c42\u3002"))}p.isMDXComponent=!0},8418:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/curvefs_architecture-3f978bb3235810ab3994ccd36a79bbb8.png"},3531:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-lifecycle-d898155a236f4c986fc3b3c661b4f52d.png"},2353:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-lifecycle2-7d0dd139046b5927089ac7b60f757cf5.png"},2654:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/es-minio-a2f62f6d85d72301905b4437d8caaac1.png"}}]); \ No newline at end of file diff --git a/assets/js/main.79895394.js b/assets/js/main.607578f6.js similarity index 99% rename from assets/js/main.79895394.js rename to assets/js/main.607578f6.js index d563163..75db3b6 100644 --- a/assets/js/main.79895394.js +++ b/assets/js/main.607578f6.js @@ -1,2 +1,2 @@ -/*! For license information please see main.79895394.js.LICENSE.txt */ -(self.webpackChunkcurve_book=self.webpackChunkcurve_book||[]).push([[179],{723:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7294),a=n(7462),o=n(8356),i=n.n(o),l=n(6887);const s={"01bc1984":[()=>n.e(7511).then(n.bind(n,4159)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-03-09.md",4159],"048b1048":[()=>n.e(2247).then(n.bind(n,6970)),"@site/docs/05-Community/01-community-guideline.md",6970],"06990c35":[()=>n.e(7519).then(n.bind(n,3798)),"@site/docs/02-CurveBS/03-test/01-env-setup.md",3798],"06c400de":[()=>n.e(2757).then(n.bind(n,1375)),"@site/docs/02-CurveBS/05-architecture/06-nebd.md",1375],"078d08d5":[()=>n.e(9993).then(n.t.bind(n,2692,19)),"~docs/default/category-tutorialsidebar-category-\u5f00\u53d1\u8005\u76f8\u5173-179.json",2692],"07acec2c":[()=>n.e(3418).then(n.bind(n,5853)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-06-01.md",5853],"09ba7f07":[()=>n.e(2036).then(n.t.bind(n,9396,19)),"~docs/default/category-tutorialsidebar-category-\u8fd0\u7ef4-1-fed.json",9396],"09e24f21":[()=>n.e(16).then(n.bind(n,8562)),"@site/docs/02-CurveBS/01-usecase/04-chuangyun.md",8562],"11a935e6":[()=>n.e(1934).then(n.bind(n,9147)),"@site/docs/08-Roadmap/01-roadmap-2023.md",9147],"12eab49d":[()=>n.e(9680).then(n.t.bind(n,30,19)),"~docs/default/category-tutorialsidebar-category-\u6027\u80fd-1-748.json",30],"138a8c27":[()=>n.e(9412).then(n.bind(n,3163)),"@site/docs/06-Release/03-release-notes-v1.5.md",3163],"14eb3368":[()=>Promise.all([n.e(532),n.e(9817)]).then(n.bind(n,4228)),"@theme/DocCategoryGeneratedIndexPage",4228],17896441:[()=>Promise.all([n.e(532),n.e(1690),n.e(7918)]).then(n.bind(n,230)),"@theme/DocItem",230],"1a4e3797":[()=>Promise.all([n.e(532),n.e(7920)]).then(n.bind(n,2027)),"@theme/SearchPage",2027],"1b4db4d5":[()=>n.e(4986).then(n.t.bind(n,7818,19)),"~docs/default/category-tutorialsidebar-category-\u90e8\u7f72-1-28a.json",7818],"1b7dffe9":[()=>n.e(683).then(n.bind(n,8714)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-04-13.md",8714],"1be78505":[()=>Promise.all([n.e(532),n.e(9514)]).then(n.bind(n,9963)),"@theme/DocPage",9963],"1bff94c1":[()=>n.e(555).then(n.bind(n,6198)),"@site/docs/03-CurveFS/03-test/01-env-setup.md",6198],"1dc14eca":[()=>n.e(9486).then(n.bind(n,6482)),"@site/docs/03-CurveFS/02-deploy/05-distributed-cache.md",6482],"1f391b9e":[()=>Promise.all([n.e(532),n.e(1690),n.e(3085)]).then(n.bind(n,4247)),"@theme/MDXPage",4247],"23675ca2":[()=>n.e(6885).then(n.t.bind(n,6390,19)),"~docs/default/category-tutorialsidebar-category-\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-b52.json",6390],"2776a4dd":[()=>n.e(1578).then(n.bind(n,5302)),"@site/docs/02-CurveBS/08-interface/01-snapshotcloneserver_interface.md",5302],"29d09019":[()=>n.e(8563).then(n.bind(n,6533)),"@site/docs/03-CurveFS/01-usecase/05-s3-gateway.md",6533],"2a048c8f":[()=>n.e(8950).then(n.bind(n,3322)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-09-14.md",3322],"2a6ceb49":[()=>n.e(5536).then(n.bind(n,6653)),"@site/docs/06-Release/01-release-intro.md",6653],"2ae1e598":[()=>n.e(1586).then(n.bind(n,9629)),"@site/docs/07-FAQ.md",9629],"2cda8809":[()=>n.e(6776).then(n.bind(n,1948)),"@site/docs/03-CurveFS/03-test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177.md",1948],"2d7b26d0":[()=>n.e(8150).then(n.bind(n,5503)),"@site/docs/03-CurveFS/07-maintenance/02-administrator-guide.md",5503],"2e551f96":[()=>n.e(2027).then(n.bind(n,4348)),"@site/docs/02-CurveBS/03-test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f.md",4348],"382473a8":[()=>n.e(480).then(n.bind(n,349)),"@site/docs/02-CurveBS/02-deploy/02-deploy-with-curveadm.md",349],"393be207":[()=>n.e(7414).then(n.bind(n,9286)),"@site/src/pages/markdown-page.md",9286],"3ab80725":[()=>n.e(58).then(n.bind(n,8403)),"@site/docs/03-CurveFS/07-maintenance/03-data-migration.md",8403],"3de2262a":[()=>n.e(6730).then(n.bind(n,3194)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-03-23.md",3194],"3fd36018":[()=>n.e(2701).then(n.bind(n,6341)),"@site/docs/04-Develop/02-build-and-test.md",6341],"42f8a9a4":[()=>n.e(6136).then(n.t.bind(n,4616,19)),"~docs/default/category-tutorialsidebar-category-\u6d4b\u8bd5-d30.json",4616],"43bf57b2":[()=>n.e(8825).then(n.t.bind(n,8440,19)),"~docs/default/category-tutorialsidebar-category-\u63a5\u53e3\u6587\u6863-0bd.json",8440],"441f2faf":[()=>n.e(4130).then(n.bind(n,9947)),"@site/docs/02-CurveBS/03-test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5.md",9947],"45ba0cb4":[()=>n.e(5733).then(n.bind(n,5314)),"@site/docs/03-CurveFS/01-usecase/04-hadoop-on-cloud.md",5314],"46f8cdc4":[()=>n.e(8514).then(n.bind(n,2346)),"@site/docs/02-CurveBS/02-deploy/01-quickstart.md",2346],"493bc0dd":[()=>n.e(8099).then(n.t.bind(n,4485,19)),"~docs/default/category-tutorialsidebar-category-roadmap-866.json",4485],"498ce1c4":[()=>n.e(4995).then(n.bind(n,2215)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-07-13.md",2215],"4de6d914":[()=>n.e(1099).then(n.bind(n,6369)),"@site/docs/02-CurveBS/06-performance/01-how-to-benchmark.md",6369],"5051a974":[()=>n.e(5619).then(n.bind(n,3683)),"@site/docs/03-CurveFS/01-usecase/03-smb-support.md",3683],"52985ac1":[()=>n.e(5908).then(n.t.bind(n,2776,19)),"/home/runner/work/curve-book/curve-book/.docusaurus/@easyops-cn/docusaurus-search-local/default/plugin-route-context-module-100.json",2776],"561d73c1":[()=>n.e(8877).then(n.bind(n,6319)),"@site/docs/03-CurveFS/01-usecase/08-asr-storage.md",6319],"599204fa":[()=>n.e(4886).then(n.bind(n,150)),"@site/docs/03-CurveFS/02-deploy/04-offline-deploy.md",150],"59a66e8b":[()=>n.e(8804).then(n.bind(n,2900)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-06-15.md",2900],"5afac02b":[()=>n.e(5498).then(n.bind(n,3223)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-08-03.md",3223],"5f525944":[()=>n.e(3030).then(n.bind(n,3153)),"@site/docs/02-CurveBS/02-deploy/04-offline-deploy.md",3153],"6095e94e":[()=>n.e(4845).then(n.t.bind(n,2316,19)),"~docs/default/category-tutorialsidebar-category-\u793e\u533a\u76f8\u5173-7bb.json",2316],"60ef353b":[()=>n.e(5042).then(n.bind(n,1741)),"@site/docs/02-CurveBS/01-usecase/05-wanfang.md",1741],"62578a01":[()=>n.e(1239).then(n.bind(n,2857)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-02-23.md",2857],"68fc0323":[()=>n.e(8325).then(n.t.bind(n,1919,19)),"~docs/default/category-tutorialsidebar-category-\u67b6\u6784-0c6.json",1919],"6acc8b0c":[()=>n.e(3315).then(n.bind(n,989)),"@site/docs/03-CurveFS/01-usecase/01-scenario.md",989],"6dc77e3b":[()=>n.e(8448).then(n.bind(n,2825)),"@site/docs/03-CurveFS/01-usecase/02-jiangsu-nongxin-es.md",2825],"6e16c946":[()=>n.e(2913).then(n.bind(n,4745)),"@site/docs/02-CurveBS/05-architecture/03-client-arch.md",4745],"74a75e9e":[()=>n.e(396).then(n.t.bind(n,3517,19)),"~docs/default/category-tutorialsidebar-category-\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1-4c2.json",3517],"783daffc":[()=>n.e(4930).then(n.t.bind(n,871,19)),"~docs/default/category-tutorialsidebar-category-\u67b6\u6784-1-eec.json",871],"7cf27860":[()=>n.e(4460).then(n.bind(n,2336)),"@site/docs/02-CurveBS/02-deploy/05-csi.md",2336],"7e989825":[()=>n.e(6647).then(n.bind(n,1840)),"@site/docs/03-CurveFS/03-test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5.md",1840],"80aaa2f6":[()=>n.e(4688).then(n.bind(n,4016)),"@site/docs/04-Develop/01-how-to-contribute.md",4016],"845835fb":[()=>n.e(6473).then(n.bind(n,9741)),"@site/docs/06-Release/02-release-notes-v2.6.md",9741],"86c5a5b3":[()=>n.e(2493).then(n.bind(n,4413)),"@site/docs/02-CurveBS/03-test/\u6d4b\u8bd5\u5de5\u5177.md",4413],"8ccfadd4":[()=>n.e(7853).then(n.t.bind(n,922,19)),"~docs/default/category-tutorialsidebar-category-\u6027\u80fd-3a6.json",922],"8ce7ab14":[()=>n.e(3051).then(n.t.bind(n,2559,19)),"~docs/default/category-tutorialsidebar-category-\u90e8\u7f72-2f8.json",2559],"8d7a4e76":[()=>n.e(1122).then(n.t.bind(n,7782,19)),"~docs/default/category-tutorialsidebar-category-\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-98b.json",7782],"8dcab980":[()=>n.e(3510).then(n.t.bind(n,31,19)),"~docs/default/category-tutorialsidebar-category-\u6d4b\u8bd5-1-e5b.json",31],"8fa8ac7a":[()=>n.e(7154).then(n.t.bind(n,2982,19)),"~docs/default/category-tutorialsidebar-category-curve\u6587\u4ef6\u5b58\u50a8-b49.json",2982],"9099f67d":[()=>n.e(1616).then(n.bind(n,9732)),"@site/docs/03-CurveFS/04-comparison/01-CurveFS-vs-Other-FS.md",9732],"935f2afb":[()=>n.e(53).then(n.t.bind(n,1109,19)),"~docs/default/version-current-metadata-prop-751.json",1109],"93753ec6":[()=>n.e(6942).then(n.bind(n,2898)),"@site/docs/02-CurveBS/01-usecase/03-rdma-spdk.md",2898],"94ed7e21":[()=>n.e(1563).then(n.bind(n,7471)),"@site/docs/02-CurveBS/04-comparison/01-CurveBS-vs-Ceph-rbd.md",7471],"965581a4":[()=>n.e(3609).then(n.bind(n,3011)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-01-12.md",3011],"9c7a26e0":[()=>n.e(446).then(n.bind(n,4801)),"@site/docs/03-CurveFS/06-performance/01-how-to-benchmark.md",4801],"9d2a34a4":[()=>n.e(2053).then(n.bind(n,8809)),"@site/docs/02-CurveBS/05-architecture/01-architecture-intro.md",8809],"9fdc18a9":[()=>n.e(985).then(n.bind(n,6420)),"@site/docs/02-CurveBS/02-deploy/03-deploy-with-k8s-operator.md",6420],a23aff59:[()=>n.e(9118).then(n.bind(n,1950)),"@site/docs/02-CurveBS/08-interface/03-localsnapshotclone_tools_api.md",1950],a2f19940:[()=>n.e(7865).then(n.bind(n,2753)),"@site/docs/02-CurveBS/01-usecase/01-scenario.md",2753],a509eed6:[()=>n.e(7510).then(n.t.bind(n,9143,19)),"~docs/default/category-tutorialsidebar-category-2023-555.json",9143],a639ec47:[()=>n.e(3380).then(n.bind(n,3489)),"@site/docs/02-CurveBS/07-maintenance/02-administrator-guide.md",3489],a9b5322f:[()=>n.e(6681).then(n.bind(n,5618)),"@site/docs/02-CurveBS/03-test/ci\u4ecb\u7ecd.md",5618],adec6730:[()=>n.e(1920).then(n.bind(n,5190)),"@site/docs/03-CurveFS/03-test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5.md",5190],ae6dce9f:[()=>n.e(3001).then(n.bind(n,2069)),"@site/docs/03-CurveFS/02-deploy/07-static-pv.md",2069],ae817aac:[()=>n.e(9795).then(n.t.bind(n,5745,19)),"/home/runner/work/curve-book/curve-book/.docusaurus/docusaurus-plugin-content-pages/default/plugin-route-context-module-100.json",5745],af95274e:[()=>n.e(3727).then(n.bind(n,6467)),"@site/docs/02-CurveBS/05-architecture/04-mds.md",6467],afabcd0e:[()=>n.e(6893).then(n.bind(n,1213)),"@site/docs/03-CurveFS/02-deploy/03-deploy-with-k8s-operator.md",1213],afb57922:[()=>n.e(2222).then(n.bind(n,1763)),"@site/docs/03-CurveFS/01-usecase/07-ai-storage.md",1763],b02e08b4:[()=>n.e(6758).then(n.t.bind(n,2972,19)),"~docs/default/category-tutorialsidebar-category-\u53cc\u5468\u4f1a-2c8.json",2972],b1373c07:[()=>n.e(2458).then(n.bind(n,840)),"@site/docs/03-CurveFS/02-deploy/06-csi.md",840],b45e84ef:[()=>n.e(5196).then(n.bind(n,4945)),"@site/docs/02-CurveBS/02-deploy/06-iscsi.md",4945],bd477228:[()=>n.e(167).then(n.bind(n,9813)),"@site/docs/03-CurveFS/06-performance/03-perf-tune.md",9813],bda932b4:[()=>n.e(9880).then(n.bind(n,7279)),"@site/docs/03-CurveFS/01-usecase/06-elasticsearch-cold-data.md",7279],bed49b90:[()=>n.e(3072).then(n.bind(n,3451)),"@site/docs/02-CurveBS/03-test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5.md",3451],c1b372e7:[()=>n.e(638).then(n.t.bind(n,3197,19)),"~docs/default/category-tutorialsidebar-category-\u7248\u672c\u76f8\u5173-11f.json",3197],c2009c07:[()=>n.e(1641).then(n.bind(n,7907)),"@site/docs/02-CurveBS/02-deploy/10-dashboard.md",7907],c8770ea0:[()=>n.e(7548).then(n.bind(n,4216)),"@site/docs/03-CurveFS/05-architecture/02-client-arch.md",4216],c8964f95:[()=>n.e(2645).then(n.bind(n,7085)),"@site/docs/02-CurveBS/01-usecase/02-cloudnative-database.md",7085],c949413d:[()=>n.e(6937).then(n.bind(n,8904)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-04-27.md",8904],cac80de7:[()=>n.e(2286).then(n.bind(n,4659)),"@site/docs/02-CurveBS/02-deploy/08-openstack.md",4659],cb08b7d8:[()=>n.e(2906).then(n.t.bind(n,4447,19)),"~docs/default/category-tutorialsidebar-category-\u8fd0\u7ef4-3e4.json",4447],ce5f1a59:[()=>n.e(7249).then(n.bind(n,2708)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-10-12.md",2708],d26f610f:[()=>n.e(8506).then(n.bind(n,2549)),"@site/docs/02-CurveBS/07-maintenance/01-command-line-tools.md",2549],d311f897:[()=>n.e(5898).then(n.bind(n,2887)),"@site/docs/04-Develop/03-code-walkthrough.md",2887],d3299682:[()=>n.e(8001).then(n.bind(n,3231)),"@site/docs/03-CurveFS/03-test/ci\u4ecb\u7ecd.md",3231],d385b444:[()=>n.e(2089).then(n.bind(n,3294)),"@site/docs/02-CurveBS/08-interface/02-localsnapshotclone_restful_api.md",3294],d9cf25ad:[()=>n.e(5999).then(n.bind(n,46)),"@site/docs/02-CurveBS/03-test/curve\u8986\u76d6\u7387\u6536\u96c6.md",46],da3ce109:[()=>n.e(9815).then(n.bind(n,2874)),"@site/docs/03-CurveFS/07-maintenance/01-command-line-tools.md",2874],dcb01ba1:[()=>n.e(3337).then(n.bind(n,9964)),"@site/docs/03-CurveFS/05-architecture/03-metaserver-arch.md",9964],de05f377:[()=>n.e(746).then(n.bind(n,6642)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-05-18.md",6642],dffd5b2b:[()=>n.e(9294).then(n.bind(n,2832)),"@site/docs/03-CurveFS/05-architecture/01-architecture-intro.md",2832],e095c50a:[()=>n.e(7770).then(n.bind(n,4521)),"@site/docs/03-CurveFS/02-deploy/01-quickstart.md",4521],e0ebe412:[()=>n.e(6621).then(n.bind(n,6654)),"@site/docs/02-CurveBS/02-deploy/07-rbd.md",6654],e176d7a0:[()=>n.e(7400).then(n.bind(n,8036)),"@site/docs/01-Intro.md",8036],e2a39381:[()=>n.e(5748).then(n.bind(n,4664)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-06-29.md",4664],e56a7fe5:[()=>n.e(6976).then(n.bind(n,9733)),"@site/docs/03-CurveFS/02-deploy/02-deploy-with-curveadm.md",9733],e68ea24b:[()=>n.e(4617).then(n.t.bind(n,4436,19)),"~docs/default/category-tutorialsidebar-category-curve\u5757\u5b58\u50a8-206.json",4436],eaf56d49:[()=>n.e(5058).then(n.bind(n,9826)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/2023-02-09.md",9826],f41bc988:[()=>n.e(6376).then(n.bind(n,3286)),"@site/docs/05-Community/02-Double-Week-Meetings/01-2023/01-README.md",3286],f6a35de5:[()=>n.e(7809).then(n.bind(n,4425)),"@site/docs/03-CurveFS/06-performance/02-benchmark.md",4425],f7d183ad:[()=>n.e(82).then(n.bind(n,5958)),"@site/docs/02-CurveBS/05-architecture/05-snapshot.md",5958],f82b6cfe:[()=>n.e(9932).then(n.bind(n,8683)),"@site/docs/02-CurveBS/02-deploy/09-rdma-spdk.md",8683],fb3a7270:[()=>n.e(2680).then(n.bind(n,6840)),"@site/docs/02-CurveBS/05-architecture/02-chunkserver-arch.md",6840],fdcd7759:[()=>n.e(1118).then(n.t.bind(n,3769,19)),"/home/runner/work/curve-book/curve-book/.docusaurus/docusaurus-plugin-content-docs/default/plugin-route-context-module-100.json",3769],fe1b9c8e:[()=>n.e(1147).then(n.t.bind(n,5981,19)),"~docs/default/category-tutorialsidebar-category-\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1-345.json",5981]};function u(e){let{error:t,retry:n,pastDelay:a}=e;return t?r.createElement("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"}},r.createElement("p",null,String(t)),r.createElement("div",null,r.createElement("button",{type:"button",onClick:n},"Retry"))):a?r.createElement("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"}},r.createElement("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb"},r.createElement("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2"},r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0"},r.createElement("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),r.createElement("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})),r.createElement("circle",{cx:"22",cy:"22",r:"8"},r.createElement("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"}))))):null}var c=n(9670),d=n(226);function p(e,t){if("*"===e)return i()({loading:u,loader:()=>n.e(4972).then(n.bind(n,4972)),modules:["@theme/NotFound"],webpack:()=>[4972],render(e,t){const n=e.default;return r.createElement(d.z,{value:{plugin:{name:"native",id:"default"}}},r.createElement(n,t))}});const o=l[`${e}-${t}`],p={},f=[],m=[],h=(0,c.Z)(o);return Object.entries(h).forEach((e=>{let[t,n]=e;const r=s[n];r&&(p[t]=r[0],f.push(r[1]),m.push(r[2]))})),i().Map({loading:u,loader:p,modules:f,webpack:()=>m,render(t,n){const i=JSON.parse(JSON.stringify(o));Object.entries(t).forEach((t=>{let[n,r]=t;const a=r.default;if(!a)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof a&&"function"!=typeof a||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{a[e]=r[e]}));let o=i;const l=n.split(".");l.slice(0,-1).forEach((e=>{o=o[e]})),o[l[l.length-1]]=a}));const l=i.__comp;delete i.__comp;const s=i.__context;return delete i.__context,r.createElement(d.z,{value:s},r.createElement(l,(0,a.Z)({},i,n)))}})}const f=[{path:"/markdown-page",component:p("/markdown-page","d81"),exact:!0},{path:"/search",component:p("/search","59b"),exact:!0},{path:"/",component:p("/","74d"),routes:[{path:"/",component:p("/","ade"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/2023",component:p("/category/2023","1cd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/curve\u5757\u5b58\u50a8",component:p("/category/curve\u5757\u5b58\u50a8","458"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/curve\u6587\u4ef6\u5b58\u50a8",component:p("/category/curve\u6587\u4ef6\u5b58\u50a8","9f2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/roadmap",component:p("/category/roadmap","232"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u53cc\u5468\u4f1a",component:p("/category/\u53cc\u5468\u4f1a","0a1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4",component:p("/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","dae"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1",component:p("/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1","261"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b",component:p("/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","c19"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1",component:p("/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1","1d6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u5f00\u53d1\u8005\u76f8\u5173",component:p("/category/\u5f00\u53d1\u8005\u76f8\u5173","bb3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u6027\u80fd",component:p("/category/\u6027\u80fd","ca5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u6027\u80fd-1",component:p("/category/\u6027\u80fd-1","252"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u63a5\u53e3\u6587\u6863",component:p("/category/\u63a5\u53e3\u6587\u6863","386"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u67b6\u6784",component:p("/category/\u67b6\u6784","164"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u67b6\u6784-1",component:p("/category/\u67b6\u6784-1","7a0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u6d4b\u8bd5",component:p("/category/\u6d4b\u8bd5","473"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u6d4b\u8bd5-1",component:p("/category/\u6d4b\u8bd5-1","12e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u7248\u672c\u76f8\u5173",component:p("/category/\u7248\u672c\u76f8\u5173","816"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u793e\u533a\u76f8\u5173",component:p("/category/\u793e\u533a\u76f8\u5173","5a5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u8fd0\u7ef4",component:p("/category/\u8fd0\u7ef4","1e7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u8fd0\u7ef4-1",component:p("/category/\u8fd0\u7ef4-1","eb7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u90e8\u7f72",component:p("/category/\u90e8\u7f72","8b8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/category/\u90e8\u7f72-1",component:p("/category/\u90e8\u7f72-1","3b2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/community-guideline",component:p("/Community/community-guideline","3d0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-01-12",component:p("/Community/Double-Week-Meetings/01-2023/2023-01-12","f33"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-02-09",component:p("/Community/Double-Week-Meetings/01-2023/2023-02-09","670"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-02-23",component:p("/Community/Double-Week-Meetings/01-2023/2023-02-23","a85"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-03-09",component:p("/Community/Double-Week-Meetings/01-2023/2023-03-09","940"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-03-23",component:p("/Community/Double-Week-Meetings/01-2023/2023-03-23","8c0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-04-13",component:p("/Community/Double-Week-Meetings/01-2023/2023-04-13","224"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-04-27",component:p("/Community/Double-Week-Meetings/01-2023/2023-04-27","548"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-05-18",component:p("/Community/Double-Week-Meetings/01-2023/2023-05-18","3df"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-06-01",component:p("/Community/Double-Week-Meetings/01-2023/2023-06-01","c14"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-06-15",component:p("/Community/Double-Week-Meetings/01-2023/2023-06-15","0e7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-06-29",component:p("/Community/Double-Week-Meetings/01-2023/2023-06-29","546"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-07-13",component:p("/Community/Double-Week-Meetings/01-2023/2023-07-13","ae8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-08-03",component:p("/Community/Double-Week-Meetings/01-2023/2023-08-03","d8b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-09-14",component:p("/Community/Double-Week-Meetings/01-2023/2023-09-14","137"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/2023-10-12",component:p("/Community/Double-Week-Meetings/01-2023/2023-10-12","8ff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Community/Double-Week-Meetings/01-2023/README",component:p("/Community/Double-Week-Meetings/01-2023/README","ce9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/architecture-intro",component:p("/CurveBS/architecture/architecture-intro","7b4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/chunkserver-arch",component:p("/CurveBS/architecture/chunkserver-arch","7fa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/client-arch",component:p("/CurveBS/architecture/client-arch","10a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/mds",component:p("/CurveBS/architecture/mds","344"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/nebd",component:p("/CurveBS/architecture/nebd","87b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/architecture/snapshot",component:p("/CurveBS/architecture/snapshot","111"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/comparison/CurveBS-vs-Ceph-rbd",component:p("/CurveBS/comparison/CurveBS-vs-Ceph-rbd","215"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/csi",component:p("/CurveBS/deploy/csi","a18"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/dashboard",component:p("/CurveBS/deploy/dashboard","d00"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/deploy-with-curveadm",component:p("/CurveBS/deploy/deploy-with-curveadm","305"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/deploy-with-k8s-operator",component:p("/CurveBS/deploy/deploy-with-k8s-operator","14f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/iscsi",component:p("/CurveBS/deploy/iscsi","8d2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/offline-deploy",component:p("/CurveBS/deploy/offline-deploy","967"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/openstack",component:p("/CurveBS/deploy/openstack","a9b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/quickstart",component:p("/CurveBS/deploy/quickstart","209"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/rbd",component:p("/CurveBS/deploy/rbd","217"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/deploy/rdma-spdk",component:p("/CurveBS/deploy/rdma-spdk","a9f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/interface/localsnapshotclone_restful_api",component:p("/CurveBS/interface/localsnapshotclone_restful_api","017"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/interface/localsnapshotclone_tools_api",component:p("/CurveBS/interface/localsnapshotclone_tools_api","5af"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/interface/snapshotcloneserver_interface",component:p("/CurveBS/interface/snapshotcloneserver_interface","af2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/maintenance/administrator-guide",component:p("/CurveBS/maintenance/administrator-guide","514"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/maintenance/command-line-tools",component:p("/CurveBS/maintenance/command-line-tools","2cc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/performance/how-to-benchmark",component:p("/CurveBS/performance/how-to-benchmark","5f3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/ci\u4ecb\u7ecd",component:p("/CurveBS/test/ci\u4ecb\u7ecd","390"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6",component:p("/CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","d4c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/env-setup",component:p("/CurveBS/test/env-setup","ca5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5",component:p("/CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","96d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/\u6d4b\u8bd5\u5de5\u5177",component:p("/CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","d56"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f",component:p("/CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","ec2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5",component:p("/CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","932"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/usecase/chuangyun",component:p("/CurveBS/usecase/chuangyun","36c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/usecase/cloudnative-database",component:p("/CurveBS/usecase/cloudnative-database","5eb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/usecase/rdma-spdk",component:p("/CurveBS/usecase/rdma-spdk","0c5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/usecase/scenario",component:p("/CurveBS/usecase/scenario","6c0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveBS/usecase/wanfang",component:p("/CurveBS/usecase/wanfang","411"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/architecture/architecture-intro",component:p("/CurveFS/architecture/architecture-intro","7ad"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/architecture/client-arch",component:p("/CurveFS/architecture/client-arch","a56"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/architecture/metaserver-arch",component:p("/CurveFS/architecture/metaserver-arch","3b2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/comparison/CurveFS-vs-Other-FS",component:p("/CurveFS/comparison/CurveFS-vs-Other-FS","abc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/csi",component:p("/CurveFS/deploy/csi","a2a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/deploy-with-curveadm",component:p("/CurveFS/deploy/deploy-with-curveadm","dc4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/deploy-with-k8s-operator",component:p("/CurveFS/deploy/deploy-with-k8s-operator","8ff"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/distributed-cache",component:p("/CurveFS/deploy/distributed-cache","08c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/offline-deploy",component:p("/CurveFS/deploy/offline-deploy","cc6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/quickstart",component:p("/CurveFS/deploy/quickstart","99c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/deploy/static-pv",component:p("/CurveFS/deploy/static-pv","4df"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/maintenance/administrator-guide",component:p("/CurveFS/maintenance/administrator-guide","eaa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/maintenance/command-line-tools",component:p("/CurveFS/maintenance/command-line-tools","405"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/maintenance/data-migration",component:p("/CurveFS/maintenance/data-migration","440"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/performance/benchmark",component:p("/CurveFS/performance/benchmark","9ed"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/performance/how-to-benchmark",component:p("/CurveFS/performance/how-to-benchmark","945"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/performance/perf-tune",component:p("/CurveFS/performance/perf-tune","b62"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/test/ci\u4ecb\u7ecd",component:p("/CurveFS/test/ci\u4ecb\u7ecd","98c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/test/env-setup",component:p("/CurveFS/test/env-setup","04f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5",component:p("/CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","6a8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177",component:p("/CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","da9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5",component:p("/CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","ac6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/ai-storage",component:p("/CurveFS/usecase/ai-storage","c5f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/asr-storage",component:p("/CurveFS/usecase/asr-storage","882"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/elasticsearch-cold-data",component:p("/CurveFS/usecase/elasticsearch-cold-data","9f7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/hadoop-on-cloud",component:p("/CurveFS/usecase/hadoop-on-cloud","3f8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/jiangsu-nongxin-es",component:p("/CurveFS/usecase/jiangsu-nongxin-es","246"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/s3-gateway",component:p("/CurveFS/usecase/s3-gateway","ec5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/scenario",component:p("/CurveFS/usecase/scenario","99d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/CurveFS/usecase/smb-support",component:p("/CurveFS/usecase/smb-support","304"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Develop/build-and-test",component:p("/Develop/build-and-test","e51"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Develop/code-walkthrough",component:p("/Develop/code-walkthrough","4a8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Develop/how-to-contribute",component:p("/Develop/how-to-contribute","f1c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/faq",component:p("/faq","90c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Release/release-intro",component:p("/Release/release-intro","6d0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Release/release-notes-v1.5",component:p("/Release/release-notes-v1.5","a29"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Release/release-notes-v2.6",component:p("/Release/release-notes-v2.6","81e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/Roadmap/roadmap-2023",component:p("/Roadmap/roadmap-2023","37c"),exact:!0,sidebar:"tutorialSidebar"}]},{path:"*",component:p("*")}]},8934:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,t:()=>o});var r=n(7294);const a=r.createContext(!1);function o(e){let{children:t}=e;const[n,o]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{o(!0)}),[]),r.createElement(a.Provider,{value:n},t)}},9383:(e,t,n)=>{"use strict";var r=n(7294),a=n(3935),o=n(3727),i=n(405),l=n(412);const s=[n(2497),n(3310),n(8320),n(2295)];var u=n(723),c=n(6550),d=n(8790);function p(e){let{children:t}=e;return r.createElement(r.Fragment,null,t)}var f=n(7462),m=n(5742),h=n(2263),g=n(4996),b=n(6668),v=n(1944),y=n(4711),w=n(9727),S=n(3320),k=n(197);function E(){const{i18n:{defaultLocale:e,localeConfigs:t}}=(0,h.Z)(),n=(0,y.l)();return r.createElement(m.Z,null,Object.entries(t).map((e=>{let[t,{htmlLang:a}]=e;return r.createElement("link",{key:t,rel:"alternate",href:n.createUrl({locale:t,fullyQualified:!0}),hrefLang:a})})),r.createElement("link",{rel:"alternate",href:n.createUrl({locale:e,fullyQualified:!0}),hrefLang:"x-default"}))}function C(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.Z)(),a=function(){const{siteConfig:{url:e}}=(0,h.Z)(),{pathname:t}=(0,c.TH)();return e+(0,g.Z)(t)}(),o=t?`${n}${t}`:a;return r.createElement(m.Z,null,r.createElement("meta",{property:"og:url",content:o}),r.createElement("link",{rel:"canonical",href:o}))}function x(){const{i18n:{currentLocale:e}}=(0,h.Z)(),{metadata:t,image:n}=(0,b.L)();return r.createElement(r.Fragment,null,r.createElement(m.Z,null,r.createElement("meta",{name:"twitter:card",content:"summary_large_image"}),r.createElement("body",{className:w.h})),n&&r.createElement(v.d,{image:n}),r.createElement(C,null),r.createElement(E,null),r.createElement(k.Z,{tag:S.HX,locale:e}),r.createElement(m.Z,null,t.map(((e,t)=>r.createElement("meta",(0,f.Z)({key:t},e))))))}const _=new Map;function T(e){if(_.has(e.pathname))return{...e,pathname:_.get(e.pathname)};if((0,d.f)(u.Z,e.pathname).some((e=>{let{route:t}=e;return!0===t.exact})))return _.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return _.set(e.pathname,t),{...e,pathname:t}}var L=n(8934),A=n(8940);function R(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r{const r=t.default?.[e]??t[e];return r?.(...n)}));return()=>a.forEach((e=>e?.()))}const P=function(e){let{children:t,location:n,previousLocation:a}=e;return(0,r.useLayoutEffect)((()=>{a!==n&&(!function(e){let{location:t,previousLocation:n}=e;if(!n)return;const r=t.pathname===n.pathname,a=t.hash===n.hash,o=t.search===n.search;if(r&&a&&!o)return;const{hash:i}=t;if(i){const e=decodeURIComponent(i.substring(1)),t=document.getElementById(e);t?.scrollIntoView()}else window.scrollTo(0,0)}({location:n,previousLocation:a}),R("onRouteDidUpdate",{previousLocation:a,location:n}))}),[a,n]),t};function D(e){const t=Array.from(new Set([e,decodeURI(e)])).map((e=>(0,d.f)(u.Z,e))).flat();return Promise.all(t.map((e=>e.route.component.preload?.())))}class O extends r.Component{previousLocation;routeUpdateCleanupCb;constructor(e){super(e),this.previousLocation=null,this.routeUpdateCleanupCb=l.Z.canUseDOM?R("onRouteUpdate",{previousLocation:null,location:this.props.location}):()=>{},this.state={nextRouteHasLoaded:!0}}shouldComponentUpdate(e,t){if(e.location===this.props.location)return t.nextRouteHasLoaded;const n=e.location;return this.previousLocation=this.props.location,this.setState({nextRouteHasLoaded:!1}),this.routeUpdateCleanupCb=R("onRouteUpdate",{previousLocation:this.previousLocation,location:n}),D(n.pathname).then((()=>{this.routeUpdateCleanupCb(),this.setState({nextRouteHasLoaded:!0})})).catch((e=>{console.warn(e),window.location.reload()})),!1}render(){const{children:e,location:t}=this.props;return r.createElement(P,{previousLocation:this.previousLocation,location:t},r.createElement(c.AW,{location:t,render:()=>e}))}}const N=O,F="__docusaurus-base-url-issue-banner-container",I="__docusaurus-base-url-issue-banner",B="__docusaurus-base-url-issue-banner-suggestion-container",M="__DOCUSAURUS_INSERT_BASEURL_BANNER";function j(e){return`\nwindow['${M}'] = true;\n\ndocument.addEventListener('DOMContentLoaded', maybeInsertBanner);\n\nfunction maybeInsertBanner() {\n var shouldInsert = window['${M}'];\n shouldInsert && insertBanner();\n}\n\nfunction insertBanner() {\n var bannerContainer = document.getElementById('${F}');\n if (!bannerContainer) {\n return;\n }\n var bannerHtml = ${JSON.stringify(function(e){return`\n\n`}(e)).replace(/{window[M]=!1}),[]),r.createElement(r.Fragment,null,!l.Z.canUseDOM&&r.createElement(m.Z,null,r.createElement("script",null,j(e))),r.createElement("div",{id:F}))}function $(){const{siteConfig:{baseUrl:e,baseUrlIssueBanner:t}}=(0,h.Z)(),{pathname:n}=(0,c.TH)();return t&&n===e?r.createElement(z,null):null}function U(){const{siteConfig:{favicon:e,title:t,noIndex:n},i18n:{currentLocale:a,localeConfigs:o}}=(0,h.Z)(),i=(0,g.Z)(e),{htmlLang:l,direction:s}=o[a];return r.createElement(m.Z,null,r.createElement("html",{lang:l,dir:s}),r.createElement("title",null,t),r.createElement("meta",{property:"og:title",content:t}),r.createElement("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),n&&r.createElement("meta",{name:"robots",content:"noindex, nofollow"}),e&&r.createElement("link",{rel:"icon",href:i}))}var q=n(4763);function W(){const e=(0,d.H)(u.Z),t=(0,c.TH)();return r.createElement(q.Z,null,r.createElement(A.M,null,r.createElement(L.t,null,r.createElement(p,null,r.createElement(U,null),r.createElement(x,null),r.createElement($,null),r.createElement(N,{location:T(t)},e)))))}var H=n(6887);const G=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise(((t,n)=>{if("undefined"==typeof document)return void n();const r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",e),r.onload=()=>t(),r.onerror=()=>n();const a=document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode;a?.appendChild(r)}))}:function(e){return new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)}))};var Q=n(9670);const Z=new Set,V=new Set,K=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,Y={prefetch(e){if(!(e=>!K()&&!V.has(e)&&!Z.has(e))(e))return!1;Z.add(e);const t=(0,d.f)(u.Z,e).flatMap((e=>{return t=e.route.path,Object.entries(H).filter((e=>{let[n]=e;return n.replace(/-[^-]+$/,"")===t})).flatMap((e=>{let[,t]=e;return Object.values((0,Q.Z)(t))}));var t}));return Promise.all(t.map((e=>{const t=n.gca(e);return t&&!t.includes("undefined")?G(t).catch((()=>{})):Promise.resolve()})))},preload:e=>!!(e=>!K()&&!V.has(e))(e)&&(V.add(e),D(e))},X=Object.freeze(Y);if(l.Z.canUseDOM){window.docusaurus=X;const e=a.hydrate;D(window.location.pathname).then((()=>{e(r.createElement(i.B6,null,r.createElement(o.VK,null,r.createElement(W,null))),document.getElementById("__docusaurus"))}))}},8940:(e,t,n)=>{"use strict";n.d(t,{_:()=>c,M:()=>d});var r=n(7294),a=n(6809);const o=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/","versions":[{"name":"current","label":"Next","isLast":true,"path":"/","mainDocId":"Intro","docs":[{"id":"Community/community-guideline","path":"/Community/community-guideline","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-01-12","path":"/Community/Double-Week-Meetings/01-2023/2023-01-12","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-02-09","path":"/Community/Double-Week-Meetings/01-2023/2023-02-09","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-02-23","path":"/Community/Double-Week-Meetings/01-2023/2023-02-23","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-03-09","path":"/Community/Double-Week-Meetings/01-2023/2023-03-09","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-03-23","path":"/Community/Double-Week-Meetings/01-2023/2023-03-23","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-04-13","path":"/Community/Double-Week-Meetings/01-2023/2023-04-13","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-04-27","path":"/Community/Double-Week-Meetings/01-2023/2023-04-27","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-05-18","path":"/Community/Double-Week-Meetings/01-2023/2023-05-18","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-06-01","path":"/Community/Double-Week-Meetings/01-2023/2023-06-01","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-06-15","path":"/Community/Double-Week-Meetings/01-2023/2023-06-15","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-06-29","path":"/Community/Double-Week-Meetings/01-2023/2023-06-29","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-07-13","path":"/Community/Double-Week-Meetings/01-2023/2023-07-13","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-08-03","path":"/Community/Double-Week-Meetings/01-2023/2023-08-03","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-09-14","path":"/Community/Double-Week-Meetings/01-2023/2023-09-14","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/2023-10-12","path":"/Community/Double-Week-Meetings/01-2023/2023-10-12","sidebar":"tutorialSidebar"},{"id":"Community/Double-Week-Meetings/01-2023/README","path":"/Community/Double-Week-Meetings/01-2023/README","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/architecture-intro","path":"/CurveBS/architecture/architecture-intro","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/chunkserver-arch","path":"/CurveBS/architecture/chunkserver-arch","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/client-arch","path":"/CurveBS/architecture/client-arch","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/mds","path":"/CurveBS/architecture/mds","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/nebd","path":"/CurveBS/architecture/nebd","sidebar":"tutorialSidebar"},{"id":"CurveBS/architecture/snapshot","path":"/CurveBS/architecture/snapshot","sidebar":"tutorialSidebar"},{"id":"CurveBS/comparison/CurveBS-vs-Ceph-rbd","path":"/CurveBS/comparison/CurveBS-vs-Ceph-rbd","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/csi","path":"/CurveBS/deploy/csi","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/dashboard","path":"/CurveBS/deploy/dashboard","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/deploy-with-curveadm","path":"/CurveBS/deploy/deploy-with-curveadm","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/deploy-with-k8s-operator","path":"/CurveBS/deploy/deploy-with-k8s-operator","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/iscsi","path":"/CurveBS/deploy/iscsi","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/offline-deploy","path":"/CurveBS/deploy/offline-deploy","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/openstack","path":"/CurveBS/deploy/openstack","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/quickstart","path":"/CurveBS/deploy/quickstart","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/rbd","path":"/CurveBS/deploy/rbd","sidebar":"tutorialSidebar"},{"id":"CurveBS/deploy/rdma-spdk","path":"/CurveBS/deploy/rdma-spdk","sidebar":"tutorialSidebar"},{"id":"CurveBS/interface/localsnapshotclone_restful_api","path":"/CurveBS/interface/localsnapshotclone_restful_api","sidebar":"tutorialSidebar"},{"id":"CurveBS/interface/localsnapshotclone_tools_api","path":"/CurveBS/interface/localsnapshotclone_tools_api","sidebar":"tutorialSidebar"},{"id":"CurveBS/interface/snapshotcloneserver_interface","path":"/CurveBS/interface/snapshotcloneserver_interface","sidebar":"tutorialSidebar"},{"id":"CurveBS/maintenance/administrator-guide","path":"/CurveBS/maintenance/administrator-guide","sidebar":"tutorialSidebar"},{"id":"CurveBS/maintenance/command-line-tools","path":"/CurveBS/maintenance/command-line-tools","sidebar":"tutorialSidebar"},{"id":"CurveBS/performance/how-to-benchmark","path":"/CurveBS/performance/how-to-benchmark","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/ci\u4ecb\u7ecd","path":"/CurveBS/test/ci\u4ecb\u7ecd","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","path":"/CurveBS/test/curve\u8986\u76d6\u7387\u6536\u96c6","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/env-setup","path":"/CurveBS/test/env-setup","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","path":"/CurveBS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","path":"/CurveBS/test/\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","path":"/CurveBS/test/\u6d4b\u8bd5\u73af\u5883\u914d\u7f6e\u4fe1\u606f","sidebar":"tutorialSidebar"},{"id":"CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","path":"/CurveBS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","sidebar":"tutorialSidebar"},{"id":"CurveBS/usecase/chuangyun","path":"/CurveBS/usecase/chuangyun","sidebar":"tutorialSidebar"},{"id":"CurveBS/usecase/cloudnative-database","path":"/CurveBS/usecase/cloudnative-database","sidebar":"tutorialSidebar"},{"id":"CurveBS/usecase/rdma-spdk","path":"/CurveBS/usecase/rdma-spdk","sidebar":"tutorialSidebar"},{"id":"CurveBS/usecase/scenario","path":"/CurveBS/usecase/scenario","sidebar":"tutorialSidebar"},{"id":"CurveBS/usecase/wanfang","path":"/CurveBS/usecase/wanfang","sidebar":"tutorialSidebar"},{"id":"CurveFS/architecture/architecture-intro","path":"/CurveFS/architecture/architecture-intro","sidebar":"tutorialSidebar"},{"id":"CurveFS/architecture/client-arch","path":"/CurveFS/architecture/client-arch","sidebar":"tutorialSidebar"},{"id":"CurveFS/architecture/metaserver-arch","path":"/CurveFS/architecture/metaserver-arch","sidebar":"tutorialSidebar"},{"id":"CurveFS/comparison/CurveFS-vs-Other-FS","path":"/CurveFS/comparison/CurveFS-vs-Other-FS","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/csi","path":"/CurveFS/deploy/csi","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/deploy-with-curveadm","path":"/CurveFS/deploy/deploy-with-curveadm","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/deploy-with-k8s-operator","path":"/CurveFS/deploy/deploy-with-k8s-operator","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/distributed-cache","path":"/CurveFS/deploy/distributed-cache","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/offline-deploy","path":"/CurveFS/deploy/offline-deploy","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/quickstart","path":"/CurveFS/deploy/quickstart","sidebar":"tutorialSidebar"},{"id":"CurveFS/deploy/static-pv","path":"/CurveFS/deploy/static-pv","sidebar":"tutorialSidebar"},{"id":"CurveFS/maintenance/administrator-guide","path":"/CurveFS/maintenance/administrator-guide","sidebar":"tutorialSidebar"},{"id":"CurveFS/maintenance/command-line-tools","path":"/CurveFS/maintenance/command-line-tools","sidebar":"tutorialSidebar"},{"id":"CurveFS/maintenance/data-migration","path":"/CurveFS/maintenance/data-migration","sidebar":"tutorialSidebar"},{"id":"CurveFS/performance/benchmark","path":"/CurveFS/performance/benchmark","sidebar":"tutorialSidebar"},{"id":"CurveFS/performance/how-to-benchmark","path":"/CurveFS/performance/how-to-benchmark","sidebar":"tutorialSidebar"},{"id":"CurveFS/performance/perf-tune","path":"/CurveFS/performance/perf-tune","sidebar":"tutorialSidebar"},{"id":"CurveFS/test/ci\u4ecb\u7ecd","path":"/CurveFS/test/ci\u4ecb\u7ecd","sidebar":"tutorialSidebar"},{"id":"CurveFS/test/env-setup","path":"/CurveFS/test/env-setup","sidebar":"tutorialSidebar"},{"id":"CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","path":"/CurveFS/test/\u5f02\u5e38\u81ea\u52a8\u5316\u65b9\u6cd5","sidebar":"tutorialSidebar"},{"id":"CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","path":"/CurveFS/test/\u6027\u80fd\u6d4b\u8bd5\u5de5\u5177","sidebar":"tutorialSidebar"},{"id":"CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","path":"/CurveFS/test/\u7f16\u8bd1\u548c\u5355\u5143\u6d4b\u8bd5","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/ai-storage","path":"/CurveFS/usecase/ai-storage","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/asr-storage","path":"/CurveFS/usecase/asr-storage","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/elasticsearch-cold-data","path":"/CurveFS/usecase/elasticsearch-cold-data","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/hadoop-on-cloud","path":"/CurveFS/usecase/hadoop-on-cloud","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/jiangsu-nongxin-es","path":"/CurveFS/usecase/jiangsu-nongxin-es","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/s3-gateway","path":"/CurveFS/usecase/s3-gateway","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/scenario","path":"/CurveFS/usecase/scenario","sidebar":"tutorialSidebar"},{"id":"CurveFS/usecase/smb-support","path":"/CurveFS/usecase/smb-support","sidebar":"tutorialSidebar"},{"id":"Develop/build-and-test","path":"/Develop/build-and-test","sidebar":"tutorialSidebar"},{"id":"Develop/code-walkthrough","path":"/Develop/code-walkthrough","sidebar":"tutorialSidebar"},{"id":"Develop/how-to-contribute","path":"/Develop/how-to-contribute","sidebar":"tutorialSidebar"},{"id":"FAQ","path":"/faq","sidebar":"tutorialSidebar"},{"id":"Intro","path":"/","sidebar":"tutorialSidebar"},{"id":"Release/release-intro","path":"/Release/release-intro","sidebar":"tutorialSidebar"},{"id":"Release/release-notes-v1.5","path":"/Release/release-notes-v1.5","sidebar":"tutorialSidebar"},{"id":"Release/release-notes-v2.6","path":"/Release/release-notes-v2.6","sidebar":"tutorialSidebar"},{"id":"Roadmap/roadmap-2023","path":"/Roadmap/roadmap-2023","sidebar":"tutorialSidebar"},{"id":"/category/curve\u5757\u5b58\u50a8","path":"/category/curve\u5757\u5b58\u50a8","sidebar":"tutorialSidebar"},{"id":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","path":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b","sidebar":"tutorialSidebar"},{"id":"/category/\u90e8\u7f72","path":"/category/\u90e8\u7f72","sidebar":"tutorialSidebar"},{"id":"/category/\u6d4b\u8bd5","path":"/category/\u6d4b\u8bd5","sidebar":"tutorialSidebar"},{"id":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","path":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4","sidebar":"tutorialSidebar"},{"id":"/category/\u67b6\u6784","path":"/category/\u67b6\u6784","sidebar":"tutorialSidebar"},{"id":"/category/\u6027\u80fd","path":"/category/\u6027\u80fd","sidebar":"tutorialSidebar"},{"id":"/category/\u8fd0\u7ef4","path":"/category/\u8fd0\u7ef4","sidebar":"tutorialSidebar"},{"id":"/category/\u63a5\u53e3\u6587\u6863","path":"/category/\u63a5\u53e3\u6587\u6863","sidebar":"tutorialSidebar"},{"id":"/category/curve\u6587\u4ef6\u5b58\u50a8","path":"/category/curve\u6587\u4ef6\u5b58\u50a8","sidebar":"tutorialSidebar"},{"id":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1","path":"/category/\u5e94\u7528\u573a\u666f\u53ca\u6848\u4f8b-1","sidebar":"tutorialSidebar"},{"id":"/category/\u90e8\u7f72-1","path":"/category/\u90e8\u7f72-1","sidebar":"tutorialSidebar"},{"id":"/category/\u6d4b\u8bd5-1","path":"/category/\u6d4b\u8bd5-1","sidebar":"tutorialSidebar"},{"id":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1","path":"/category/\u540c\u7c7b\u8f6f\u4ef6\u5bf9\u6bd4-1","sidebar":"tutorialSidebar"},{"id":"/category/\u67b6\u6784-1","path":"/category/\u67b6\u6784-1","sidebar":"tutorialSidebar"},{"id":"/category/\u6027\u80fd-1","path":"/category/\u6027\u80fd-1","sidebar":"tutorialSidebar"},{"id":"/category/\u8fd0\u7ef4-1","path":"/category/\u8fd0\u7ef4-1","sidebar":"tutorialSidebar"},{"id":"/category/\u5f00\u53d1\u8005\u76f8\u5173","path":"/category/\u5f00\u53d1\u8005\u76f8\u5173","sidebar":"tutorialSidebar"},{"id":"/category/\u793e\u533a\u76f8\u5173","path":"/category/\u793e\u533a\u76f8\u5173","sidebar":"tutorialSidebar"},{"id":"/category/\u53cc\u5468\u4f1a","path":"/category/\u53cc\u5468\u4f1a","sidebar":"tutorialSidebar"},{"id":"/category/2023","path":"/category/2023","sidebar":"tutorialSidebar"},{"id":"/category/\u7248\u672c\u76f8\u5173","path":"/category/\u7248\u672c\u76f8\u5173","sidebar":"tutorialSidebar"},{"id":"/category/roadmap","path":"/category/roadmap","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/","label":"Intro"}}}}],"breadcrumbs":true}}}'),i=JSON.parse('{"defaultLocale":"zh","locales":["zh"],"path":"i18n","currentLocale":"zh","localeConfigs":{"zh":{"label":"\u4e2d\u6587","direction":"ltr","htmlLang":"zh","calendar":"gregory","path":"zh"}}}');var l=n(7529);const s=JSON.parse('{"docusaurusVersion":"2.4.1","siteVersion":"1.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"2.4.1"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"2.4.1"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"2.4.1"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"2.4.1"},"@easyops-cn/docusaurus-search-local":{"type":"package","name":"@easyops-cn/docusaurus-search-local","version":"0.35.0"}}}'),u={siteConfig:a.default,siteMetadata:s,globalData:o,i18n:i,codeTranslations:l},c=r.createContext(u);function d(e){let{children:t}=e;return r.createElement(c.Provider,{value:u},t)}},4763:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var r=n(7294),a=n(412),o=n(5742),i=n(8780),l=n(179);function s(e){let{error:t,tryAgain:n}=e;return r.createElement("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"}},r.createElement("h1",{style:{fontSize:"3rem"}},"This page crashed"),r.createElement("button",{type:"button",onClick:n,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"}},"Try again"),r.createElement(u,{error:t}))}function u(e){let{error:t}=e;const n=(0,i.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{style:{whiteSpace:"pre-wrap"}},n)}function c(e){let{error:t,tryAgain:n}=e;return r.createElement(p,{fallback:()=>r.createElement(s,{error:t,tryAgain:n})},r.createElement(o.Z,null,r.createElement("title",null,"Page Error")),r.createElement(l.Z,null,r.createElement(s,{error:t,tryAgain:n})))}const d=e=>r.createElement(c,e);class p extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){a.Z.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??d)(e)}return e??null}}},412:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,a={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},5742:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(405);function o(e){return r.createElement(a.ql,e)}},9960:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var r=n(7462),a=n(7294),o=n(3727),i=n(8780),l=n(2263),s=n(3919),u=n(412);const c=a.createContext({collectLink:()=>{}});var d=n(4996);function p(e,t){let{isNavLink:n,to:p,href:f,activeClassName:m,isActive:h,"data-noBrokenLinkCheck":g,autoAddBaseUrl:b=!0,...v}=e;const{siteConfig:{trailingSlash:y,baseUrl:w}}=(0,l.Z)(),{withBaseUrl:S}=(0,d.C)(),k=(0,a.useContext)(c),E=(0,a.useRef)(null);(0,a.useImperativeHandle)(t,(()=>E.current));const C=p||f;const x=(0,s.Z)(C),_=C?.replace("pathname://","");let T=void 0!==_?(L=_,b&&(e=>e.startsWith("/"))(L)?S(L):L):void 0;var L;T&&x&&(T=(0,i.applyTrailingSlash)(T,{trailingSlash:y,baseUrl:w}));const A=(0,a.useRef)(!1),R=n?o.OL:o.rU,P=u.Z.canUseIntersectionObserver,D=(0,a.useRef)(),O=()=>{A.current||null==T||(window.docusaurus.preload(T),A.current=!0)};(0,a.useEffect)((()=>(!P&&x&&null!=T&&window.docusaurus.prefetch(T),()=>{P&&D.current&&D.current.disconnect()})),[D,T,P,x]);const N=T?.startsWith("#")??!1,F=!T||!x||N;return F||g||k.collectLink(T),F?a.createElement("a",(0,r.Z)({ref:E,href:T},C&&!x&&{target:"_blank",rel:"noopener noreferrer"},v)):a.createElement(R,(0,r.Z)({},v,{onMouseEnter:O,onTouchStart:O,innerRef:e=>{E.current=e,P&&e&&x&&(D.current=new window.IntersectionObserver((t=>{t.forEach((t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(D.current.unobserve(e),D.current.disconnect(),null!=T&&window.docusaurus.prefetch(T))}))})),D.current.observe(e))},to:T},n&&{isActive:h,activeClassName:m}))}const f=a.forwardRef(p)},5999:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s,I:()=>l});var r=n(7294);function a(e,t){const n=e.split(/(\{\w+\})/).map(((e,n)=>{if(n%2==1){const n=t?.[e.slice(1,-1)];if(void 0!==n)return n}return e}));return n.some((e=>(0,r.isValidElement)(e)))?n.map(((e,t)=>(0,r.isValidElement)(e)?r.cloneElement(e,{key:t}):e)).filter((e=>""!==e)):n.join("")}var o=n(7529);function i(e){let{id:t,message:n}=e;if(void 0===t&&void 0===n)throw new Error("Docusaurus translation declarations must have at least a translation id or a default translation message");return o[t??n]??n??t}function l(e,t){let{message:n,id:r}=e;return a(i({message:n,id:r}),t)}function s(e){let{children:t,id:n,values:o}=e;if(t&&"string"!=typeof t)throw console.warn("Illegal children",t),new Error("The Docusaurus component only accept simple string values");const l=i({message:t,id:n});return r.createElement(r.Fragment,null,a(l,o))}},9935:(e,t,n)=>{"use strict";n.d(t,{m:()=>r});const r="default"},3919:(e,t,n)=>{"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function a(e){return void 0!==e&&!r(e)}n.d(t,{Z:()=>a,b:()=>r})},4996:(e,t,n)=>{"use strict";n.d(t,{C:()=>i,Z:()=>l});var r=n(7294),a=n(2263),o=n(3919);function i(){const{siteConfig:{baseUrl:e,url:t}}=(0,a.Z)(),n=(0,r.useCallback)(((n,r)=>function(e,t,n,r){let{forcePrependBaseUrl:a=!1,absolute:i=!1}=void 0===r?{}:r;if(!n||n.startsWith("#")||(0,o.b)(n))return n;if(a)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return i?e+l:l}(t,e,n,r)),[t,e]);return{withBaseUrl:n}}function l(e,t){void 0===t&&(t={});const{withBaseUrl:n}=i();return n(e,t)}},2263:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8940);function o(){return(0,r.useContext)(a._)}},2389:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(8934);function o(){return(0,r.useContext)(a._)}},9670:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});const r=e=>"object"==typeof e&&!!e&&Object.keys(e).length>0;function a(e){const t={};return function e(n,a){Object.entries(n).forEach((n=>{let[o,i]=n;const l=a?`${a}.${o}`:o;r(i)?e(i,l):t[l]=i}))}(e),t}},226:(e,t,n)=>{"use strict";n.d(t,{_:()=>a,z:()=>o});var r=n(7294);const a=r.createContext(null);function o(e){let{children:t,value:n}=e;const o=r.useContext(a),i=(0,r.useMemo)((()=>function(e){let{parent:t,value:n}=e;if(!t){if(!n)throw new Error("Unexpected: no Docusaurus route context found");if(!("plugin"in n))throw new Error("Unexpected: Docusaurus topmost route context has no `plugin` attribute");return n}const r={...t.data,...n?.data};return{plugin:t.plugin,data:r}}({parent:o,value:n})),[o,n]);return r.createElement(a.Provider,{value:i},t)}},143:(e,t,n)=>{"use strict";n.d(t,{Iw:()=>h,gA:()=>p,_r:()=>c,Jo:()=>g,zh:()=>d,yW:()=>m,gB:()=>f});var r=n(6550),a=n(2263),o=n(9935);function i(e,t){void 0===t&&(t={});const n=function(){const{globalData:e}=(0,a.Z)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}const l=e=>e.versions.find((e=>e.isLast));function s(e,t){const n=function(e,t){const n=l(e);return[...e.versions.filter((e=>e!==n)),n].find((e=>!!(0,r.LX)(t,{path:e.path,exact:!1,strict:!1})))}(e,t),a=n?.docs.find((e=>!!(0,r.LX)(t,{path:e.path,exact:!0,strict:!1})));return{activeVersion:n,activeDoc:a,alternateDocVersions:a?function(t){const n={};return e.versions.forEach((e=>{e.docs.forEach((r=>{r.id===t&&(n[e.name]=r)}))})),n}(a.id):{}}}const u={},c=()=>i("docusaurus-plugin-content-docs")??u,d=e=>function(e,t,n){void 0===t&&(t=o.m),void 0===n&&(n={});const r=i(e),a=r?.[t];if(!a&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return a}("docusaurus-plugin-content-docs",e,{failfast:!0});function p(e){void 0===e&&(e={});const t=c(),{pathname:n}=(0,r.TH)();return function(e,t,n){void 0===n&&(n={});const a=Object.entries(e).sort(((e,t)=>t[1].path.localeCompare(e[1].path))).find((e=>{let[,n]=e;return!!(0,r.LX)(t,{path:n.path,exact:!1,strict:!1})})),o=a?{pluginId:a[0],pluginData:a[1]}:void 0;if(!o&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map((e=>e.path)).join(", ")}`);return o}(t,n,e)}function f(e){return d(e).versions}function m(e){const t=d(e);return l(t)}function h(e){const t=d(e),{pathname:n}=(0,r.TH)();return s(t,n)}function g(e){const t=d(e),{pathname:n}=(0,r.TH)();return function(e,t){const n=l(e);return{latestDocSuggestion:s(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},8320:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4865),a=n.n(r);a().configure({showSpinner:!1});const o={onRouteUpdate(e){let{location:t,previousLocation:n}=e;if(n&&t.pathname!==n.pathname){const e=window.setTimeout((()=>{a().start()}),200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){a().done()}}},3310:(e,t,n)=>{"use strict";n.r(t);var r=n(7410),a=n(6809);!function(e){const{themeConfig:{prism:t}}=a.default,{additionalLanguages:r}=t;globalThis.Prism=e,r.forEach((e=>{n(6726)(`./prism-${e}`)})),delete globalThis.Prism}(r.Z)},9471:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294);const a={iconExternalLink:"iconExternalLink_nPIU"};function o(e){let{width:t=13.5,height:n=13.5}=e;return r.createElement("svg",{width:t,height:n,"aria-hidden":"true",viewBox:"0 0 24 24",className:a.iconExternalLink},r.createElement("path",{fill:"currentColor",d:"M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"}))}},179:(e,t,n)=>{"use strict";n.d(t,{Z:()=>Dt});var r=n(7294),a=n(6010),o=n(4763),i=n(1944),l=n(7462),s=n(6550),u=n(5999),c=n(5936);const d="__docusaurus_skipToContent_fallback";function p(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function f(){const e=(0,r.useRef)(null),{action:t}=(0,s.k6)(),n=(0,r.useCallback)((e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&p(t)}),[]);return(0,c.S)((n=>{let{location:r}=n;e.current&&!r.hash&&"PUSH"===t&&p(e.current)})),{containerRef:e,onClick:n}}const m=(0,u.I)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function h(e){const t=e.children??m,{containerRef:n,onClick:a}=f();return r.createElement("div",{ref:n,role:"region","aria-label":m},r.createElement("a",(0,l.Z)({},e,{href:`#${d}`,onClick:a}),t))}var g=n(5281),b=n(9727);const v={skipToContent:"skipToContent_fXgn"};function y(){return r.createElement(h,{className:v.skipToContent})}var w=n(6668),S=n(9689);function k(e){let{width:t=21,height:n=21,color:a="currentColor",strokeWidth:o=1.2,className:i,...s}=e;return r.createElement("svg",(0,l.Z)({viewBox:"0 0 15 15",width:t,height:n},s),r.createElement("g",{stroke:a,strokeWidth:o},r.createElement("path",{d:"M.75.75l13.5 13.5M14.25.75L.75 14.25"})))}const E={closeButton:"closeButton_CVFx"};function C(e){return r.createElement("button",(0,l.Z)({type:"button","aria-label":(0,u.I)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"})},e,{className:(0,a.Z)("clean-btn close",E.closeButton,e.className)}),r.createElement(k,{width:14,height:14,strokeWidth:3.1}))}const x={content:"content_knG7"};function _(e){const{announcementBar:t}=(0,w.L)(),{content:n}=t;return r.createElement("div",(0,l.Z)({},e,{className:(0,a.Z)(x.content,e.className),dangerouslySetInnerHTML:{__html:n}}))}const T={announcementBar:"announcementBar_mb4j",announcementBarPlaceholder:"announcementBarPlaceholder_vyr4",announcementBarClose:"announcementBarClose_gvF7",announcementBarContent:"announcementBarContent_xLdY"};function L(){const{announcementBar:e}=(0,w.L)(),{isActive:t,close:n}=(0,S.nT)();if(!t)return null;const{backgroundColor:a,textColor:o,isCloseable:i}=e;return r.createElement("div",{className:T.announcementBar,style:{backgroundColor:a,color:o},role:"banner"},i&&r.createElement("div",{className:T.announcementBarPlaceholder}),r.createElement(_,{className:T.announcementBarContent}),i&&r.createElement(C,{onClick:n,className:T.announcementBarClose}))}var A=n(2961),R=n(2466);var P=n(902),D=n(3102);const O=r.createContext(null);function N(e){let{children:t}=e;const n=function(){const e=(0,A.e)(),t=(0,D.HY)(),[n,a]=(0,r.useState)(!1),o=null!==t.component,i=(0,P.D9)(o);return(0,r.useEffect)((()=>{o&&!i&&a(!0)}),[o,i]),(0,r.useEffect)((()=>{o?e.shown||a(!0):a(!1)}),[e.shown,o]),(0,r.useMemo)((()=>[n,a]),[n])}();return r.createElement(O.Provider,{value:n},t)}function F(e){if(e.component){const t=e.component;return r.createElement(t,e.props)}}function I(){const e=(0,r.useContext)(O);if(!e)throw new P.i6("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,a=(0,r.useCallback)((()=>n(!1)),[n]),o=(0,D.HY)();return(0,r.useMemo)((()=>({shown:t,hide:a,content:F(o)})),[a,o,t])}function B(e){let{header:t,primaryMenu:n,secondaryMenu:o}=e;const{shown:i}=I();return r.createElement("div",{className:"navbar-sidebar"},t,r.createElement("div",{className:(0,a.Z)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":i})},r.createElement("div",{className:"navbar-sidebar__item menu"},n),r.createElement("div",{className:"navbar-sidebar__item menu"},o)))}var M=n(2949),j=n(2389);function z(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"}))}function $(e){return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:24,height:24},e),r.createElement("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"}))}const U={toggle:"toggle_vylO",toggleButton:"toggleButton_gllP",darkToggleIcon:"darkToggleIcon_wfgR",lightToggleIcon:"lightToggleIcon_pyhR",toggleButtonDisabled:"toggleButtonDisabled_aARS"};function q(e){let{className:t,buttonClassName:n,value:o,onChange:i}=e;const l=(0,j.Z)(),s=(0,u.I)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the navbar color mode toggle"},{mode:"dark"===o?(0,u.I)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"}):(0,u.I)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"})});return r.createElement("div",{className:(0,a.Z)(U.toggle,t)},r.createElement("button",{className:(0,a.Z)("clean-btn",U.toggleButton,!l&&U.toggleButtonDisabled,n),type:"button",onClick:()=>i("dark"===o?"light":"dark"),disabled:!l,title:s,"aria-label":s,"aria-live":"polite"},r.createElement(z,{className:(0,a.Z)(U.toggleIcon,U.lightToggleIcon)}),r.createElement($,{className:(0,a.Z)(U.toggleIcon,U.darkToggleIcon)})))}const W=r.memo(q),H={darkNavbarColorModeToggle:"darkNavbarColorModeToggle_X3D1"};function G(e){let{className:t}=e;const n=(0,w.L)().navbar.style,a=(0,w.L)().colorMode.disableSwitch,{colorMode:o,setColorMode:i}=(0,M.I)();return a?null:r.createElement(W,{className:t,buttonClassName:"dark"===n?H.darkNavbarColorModeToggle:void 0,value:o,onChange:i})}var Q=n(1327);function Z(){return r.createElement(Q.Z,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function V(){const e=(0,A.e)();return r.createElement("button",{type:"button","aria-label":(0,u.I)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle()},r.createElement(k,{color:"var(--ifm-color-emphasis-600)"}))}function K(){return r.createElement("div",{className:"navbar-sidebar__brand"},r.createElement(Z,null),r.createElement(G,{className:"margin-right--md"}),r.createElement(V,null))}var Y=n(9960),X=n(4996),J=n(3919);function ee(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var te=n(9471);function ne(e){let{activeBasePath:t,activeBaseRegex:n,to:a,href:o,label:i,html:s,isDropdownLink:u,prependBaseUrlToHref:c,...d}=e;const p=(0,X.Z)(a),f=(0,X.Z)(t),m=(0,X.Z)(o,{forcePrependBaseUrl:!0}),h=i&&o&&!(0,J.Z)(o),g=s?{dangerouslySetInnerHTML:{__html:s}}:{children:r.createElement(r.Fragment,null,i,h&&r.createElement(te.Z,u&&{width:12,height:12}))};return o?r.createElement(Y.Z,(0,l.Z)({href:c?m:o},d,g)):r.createElement(Y.Z,(0,l.Z)({to:p,isNavLink:!0},(t||n)&&{isActive:(e,t)=>n?ee(n,t.pathname):t.pathname.startsWith(f)},d,g))}function re(e){let{className:t,isDropdownItem:n=!1,...o}=e;const i=r.createElement(ne,(0,l.Z)({className:(0,a.Z)(n?"dropdown__link":"navbar__item navbar__link",t),isDropdownLink:n},o));return n?r.createElement("li",null,i):i}function ae(e){let{className:t,isDropdownItem:n,...o}=e;return r.createElement("li",{className:"menu__list-item"},r.createElement(ne,(0,l.Z)({className:(0,a.Z)("menu__link",t)},o)))}function oe(e){let{mobile:t=!1,position:n,...a}=e;const o=t?ae:re;return r.createElement(o,(0,l.Z)({},a,{activeClassName:a.activeClassName??(t?"menu__link--active":"navbar__link--active")}))}var ie=n(6043),le=n(8596),se=n(2263);function ue(e,t){return e.some((e=>function(e,t){return!!(0,le.Mg)(e.to,t)||!!ee(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t)))}function ce(e){let{items:t,position:n,className:o,onClick:i,...s}=e;const u=(0,r.useRef)(null),[c,d]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{const e=e=>{u.current&&!u.current.contains(e.target)&&d(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}}),[u]),r.createElement("div",{ref:u,className:(0,a.Z)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===n,"dropdown--show":c})},r.createElement(ne,(0,l.Z)({"aria-haspopup":"true","aria-expanded":c,role:"button",href:s.to?void 0:"#",className:(0,a.Z)("navbar__link",o)},s,{onClick:s.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),d(!c))}}),s.children??s.label),r.createElement("ul",{className:"dropdown__menu"},t.map(((e,t)=>r.createElement(He,(0,l.Z)({isDropdownItem:!0,activeClassName:"dropdown__link--active"},e,{key:t}))))))}function de(e){let{items:t,className:n,position:o,onClick:i,...u}=e;const c=function(){const{siteConfig:{baseUrl:e}}=(0,se.Z)(),{pathname:t}=(0,s.TH)();return t.replace(e,"/")}(),d=ue(t,c),{collapsed:p,toggleCollapsed:f,setCollapsed:m}=(0,ie.u)({initialState:()=>!d});return(0,r.useEffect)((()=>{d&&m(!d)}),[c,d,m]),r.createElement("li",{className:(0,a.Z)("menu__list-item",{"menu__list-item--collapsed":p})},r.createElement(ne,(0,l.Z)({role:"button",className:(0,a.Z)("menu__link menu__link--sublist menu__link--sublist-caret",n)},u,{onClick:e=>{e.preventDefault(),f()}}),u.children??u.label),r.createElement(ie.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:p},t.map(((e,t)=>r.createElement(He,(0,l.Z)({mobile:!0,isDropdownItem:!0,onClick:i,activeClassName:"menu__link--active"},e,{key:t}))))))}function pe(e){let{mobile:t=!1,...n}=e;const a=t?de:ce;return r.createElement(a,n)}var fe=n(4711);function me(e){let{width:t=20,height:n=20,...a}=e;return r.createElement("svg",(0,l.Z)({viewBox:"0 0 24 24",width:t,height:n,"aria-hidden":!0},a),r.createElement("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"}))}const he="iconLanguage_nlXk";var ge=n(1029),be=n(412),ve=n(373),ye=n(143),we=n(22),Se=n(8202),ke=n(3926),Ee=n(1073),Ce=n(2539),xe=n(726);const _e='',Te='',Le='',Ae='',Re='',Pe='',De='',Oe={searchBar:"searchBar_RVTs",dropdownMenu:"dropdownMenu_qbY6",searchBarLeft:"searchBarLeft_MXDe",suggestion:"suggestion_fB_2",cursor:"cursor_eG29",hitTree:"hitTree_kk6K",hitIcon:"hitIcon_a7Zy",hitPath:"hitPath_ieM4",noResultsIcon:"noResultsIcon_EBY5",hitFooter:"hitFooter_E9YW",hitWrapper:"hitWrapper_sAK8",hitTitle:"hitTitle_vyVt",hitAction:"hitAction_NqkB",hideAction:"hideAction_vcyE",noResults:"noResults_l6Q3",searchBarContainer:"searchBarContainer_NW3z",searchBarLoadingRing:"searchBarLoadingRing_YnHq",searchClearButton:"searchClearButton_qk4g",searchIndexLoading:"searchIndexLoading_EJ1f",searchHintContainer:"searchHintContainer_Pkmr",searchHint:"searchHint_iIMx",focused:"focused_OWtg",input:"input_FOTf",hint:"hint_URu1",suggestions:"suggestions_X8XU",dataset:"dataset_QiCy",empty:"empty_eITn"};function Ne(e){let{document:t,type:n,page:r,metadata:a,tokens:o,isInterOfTree:i,isLastOfTree:l}=e;const s=0===n,u=1===n,c=[];i?c.push(Pe):l&&c.push(De);const d=c.map((e=>`${e}`)),p=`${s?_e:u?Te:Le}`,f=[`${(0,xe.o)(t.t,(0,Ee.m)(a,"t"),o)}`];if(!i&&!l&&ge.H6){const e=r?(r.b??[]).concat(r.t).concat(t.s&&t.s!==r.t?t.s:[]):t.b;f.push(`${(0,ke.e)(e??[])}`)}else s||f.push(`${(0,Ce.C)(r.t||(t.u.startsWith("/docs/api-reference/")?"API Reference":""),o)}`);const m=`${Ae}`;return[...d,p,``,...f,"",m].join("")}function Fe(){return`${Re}${(0,u.I)({id:"theme.SearchBar.noResultsText",message:"No results"})}`}var Ie=n(311);async function Be(){const e=await Promise.all([n.e(8443),n.e(5525)]).then(n.t.bind(n,8443,23)),t=e.default;return t.noConflict?t.noConflict():e.noConflict&&e.noConflict(),t}const Me="_highlight";const je=function(e){let{handleSearchBarToggle:t}=e;const{siteConfig:{baseUrl:n}}=(0,se.Z)(),o=(0,ye.gA)();let i=n;try{const{preferredVersion:e}=(0,ve.J)(o?.pluginId??ge.gQ);e&&!e.isLast&&(i=e.path+"/")}catch(F){if(ge.l9&&!(F instanceof P.i6))throw F}const l=(0,s.k6)(),c=(0,s.TH)(),d=(0,r.useRef)(null),p=(0,r.useRef)(new Map),f=(0,r.useRef)(!1),[m,h]=(0,r.useState)(!1),[g,b]=(0,r.useState)(!1),[v,y]=(0,r.useState)(""),w=(0,r.useRef)(null),S=(0,r.useRef)(""),[k,E]=(0,r.useState)("");(0,r.useEffect)((()=>{if(!Array.isArray(ge.Kc))return;let e="";if(c.pathname.startsWith(i)){const t=c.pathname.substring(i.length),n=ge.Kc.find((e=>t===e||t.startsWith(`${e}/`)));n&&(e=n)}S.current!==e&&(p.current.delete(e),S.current=e),E(e)}),[c.pathname,i]);const C=!!ge.hG&&Array.isArray(ge.Kc)&&""===k,x=(0,r.useCallback)((async()=>{if(C||p.current.get(k))return;p.current.set(k,"loading"),w.current?.autocomplete.destroy(),h(!0);const[{wrappedIndexes:e,zhDictionary:t},r]=await Promise.all([(0,we.w)(i,k),Be()]);if(w.current=r(d.current,{hint:!1,autoselect:!0,openOnFocus:!0,cssClasses:{root:(0,a.Z)(Oe.searchBar,{[Oe.searchBarLeft]:"left"===ge.pu}),noPrefix:!0,dropdownMenu:Oe.dropdownMenu,input:Oe.input,hint:Oe.hint,suggestions:Oe.suggestions,suggestion:Oe.suggestion,cursor:Oe.cursor,dataset:Oe.dataset,empty:Oe.empty}},[{source:(0,Se.v)(e,t,ge.qo),templates:{suggestion:Ne,empty:Fe,footer:e=>{let{query:t,isEmpty:r}=e;if(r&&!k)return;const a=(e=>{let{query:t,isEmpty:r}=e;const a=document.createElement("a"),o=new URLSearchParams,s=(0,u.I)({id:"theme.SearchBar.seeAll",message:"See all results"}),c=(0,u.I)({id:"theme.SearchBar.seeAllOutsideContext",message:"See results outside {context}"},{context:k}),d=(0,u.I)({id:"theme.SearchBar.searchInContext",message:"See all results in {context}"},{context:k});let p;if(o.set("q",t),p=k&&r?c:k?d:s,Array.isArray(ge.Kc)&&!r&&o.set("ctx",k),i!==n){if(!i.startsWith(n))throw new Error(`Version url '${i}' does not start with base url '${n}', this is a bug of \`@easyops-cn/docusaurus-search-local\`, please report it.`);o.set("version",i.substring(n.length))}const f=`${n}search?${o.toString()}`;return a.href=f,a.textContent=p,a.addEventListener("click",(e=>{e.ctrlKey||e.metaKey||(e.preventDefault(),w.current?.autocomplete.close(),l.push(f))})),a})({query:t,isEmpty:r}),o=document.createElement("div");return o.className=Oe.hitFooter,o.appendChild(a),o}}}]).on("autocomplete:selected",(function(e,t){let{document:{u:n,h:r},tokens:a}=t;d.current?.blur();let o=n;if(ge.vc&&a.length>0){const e=new URLSearchParams;for(const t of a)e.append(Me,t);o+=`?${e.toString()}`}r&&(o+=r),l.push(o)})).on("autocomplete:closed",(()=>{d.current?.blur()})),p.current.set(k,"done"),h(!1),f.current){const e=d.current;e.value&&w.current?.autocomplete.open(),e.focus()}}),[C,k,i,n,l]);(0,r.useEffect)((()=>{if(!ge.vc)return;const e=be.Z.canUseDOM?new URLSearchParams(c.search).getAll(Me):[];setTimeout((()=>{const t=document.querySelector("article");if(!t)return;const n=new ge.vc(t);n.unmark(),0!==e.length&&n.mark(e),y(e.join(" ")),w.current?.autocomplete.setVal(e.join(" "))}))}),[c.search,c.pathname]);const[_,T]=(0,r.useState)(!1),L=(0,r.useCallback)((()=>{f.current=!0,x(),T(!0),t?.(!0)}),[t,x]),A=(0,r.useCallback)((()=>{T(!1),t?.(!1)}),[t]),R=(0,r.useCallback)((()=>{x()}),[x]),D=(0,r.useCallback)((e=>{y(e.target.value),e.target.value&&b(!0)}),[]),O=!!be.Z.canUseDOM&&/mac/i.test(navigator.userAgentData?.platform??navigator.platform);(0,r.useEffect)((()=>{if(!ge.AY)return;const e=e=>{!(O?e.metaKey:e.ctrlKey)||"k"!==e.key&&"K"!==e.key||(e.preventDefault(),d.current?.focus(),L())};return document.addEventListener("keydown",e),()=>{document.removeEventListener("keydown",e)}}),[O,L]);const N=(0,r.useCallback)((()=>{const e=new URLSearchParams(c.search);e.delete(Me);const t=e.toString(),n=c.pathname+(""!=t?`?${t}`:"")+c.hash;n!=c.pathname+c.search+c.hash&&l.push(n),y(""),w.current?.autocomplete.setVal("")}),[c.pathname,c.search,c.hash,l]);return r.createElement("div",{className:(0,a.Z)("navbar__search",Oe.searchBarContainer,{[Oe.searchIndexLoading]:m&&g,[Oe.focused]:_}),hidden:C},r.createElement("input",{placeholder:(0,u.I)({id:"theme.SearchBar.label",message:"Search",description:"The ARIA label and placeholder for search button"}),"aria-label":"Search",className:"navbar__search-input",onMouseEnter:R,onFocus:L,onBlur:A,onChange:D,ref:d,value:v}),r.createElement(Ie.Z,{className:Oe.searchBarLoadingRing}),ge.AY&&ge.t_&&(""!==v?r.createElement("button",{className:Oe.searchClearButton,onClick:N},"\u2715"):be.Z.canUseDOM&&r.createElement("div",{className:Oe.searchHintContainer},r.createElement("kbd",{className:Oe.searchHint},O?"\u2318":"ctrl"),r.createElement("kbd",{className:Oe.searchHint},"K"))))},ze={searchBox:"searchBox_ZlJk"};function $e(e){let{children:t,className:n}=e;return r.createElement("div",{className:(0,a.Z)(n,ze.searchBox)},t)}var Ue=n(2802);const qe=e=>e.docs.find((t=>t.id===e.mainDocId));const We={default:oe,localeDropdown:function(e){let{mobile:t,dropdownItemsBefore:n,dropdownItemsAfter:a,...o}=e;const{i18n:{currentLocale:i,locales:c,localeConfigs:d}}=(0,se.Z)(),p=(0,fe.l)(),{search:f,hash:m}=(0,s.TH)(),h=[...n,...c.map((e=>{const n=`${`pathname://${p.createUrl({locale:e,fullyQualified:!1})}`}${f}${m}`;return{label:d[e].label,lang:d[e].htmlLang,to:n,target:"_self",autoAddBaseUrl:!1,className:e===i?t?"menu__link--active":"dropdown__link--active":""}})),...a],g=t?(0,u.I)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):d[i].label;return r.createElement(pe,(0,l.Z)({},o,{mobile:t,label:r.createElement(r.Fragment,null,r.createElement(me,{className:he}),g),items:h}))},search:function(e){let{mobile:t,className:n}=e;return t?null:r.createElement($e,{className:n},r.createElement(je,null))},dropdown:pe,html:function(e){let{value:t,className:n,mobile:o=!1,isDropdownItem:i=!1}=e;const l=i?"li":"div";return r.createElement(l,{className:(0,a.Z)({navbar__item:!o&&!i,"menu__list-item":o},n),dangerouslySetInnerHTML:{__html:t}})},doc:function(e){let{docId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,ye.Iw)(a),s=(0,Ue.vY)(t,a);return null===s?null:r.createElement(oe,(0,l.Z)({exact:!0},o,{isActive:()=>i?.path===s.path||!!i?.sidebar&&i.sidebar===s.sidebar,label:n??s.id,to:s.path}))},docSidebar:function(e){let{sidebarId:t,label:n,docsPluginId:a,...o}=e;const{activeDoc:i}=(0,ye.Iw)(a),s=(0,Ue.oz)(t,a).link;if(!s)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${t}" doesn't have anything to be linked to.`);return r.createElement(oe,(0,l.Z)({exact:!0},o,{isActive:()=>i?.sidebar===t,label:n??s.label,to:s.path}))},docsVersion:function(e){let{label:t,to:n,docsPluginId:a,...o}=e;const i=(0,Ue.lO)(a)[0],s=t??i.label,u=n??(e=>e.docs.find((t=>t.id===e.mainDocId)))(i).path;return r.createElement(oe,(0,l.Z)({},o,{label:s,to:u}))},docsVersionDropdown:function(e){let{mobile:t,docsPluginId:n,dropdownActiveClassDisabled:a,dropdownItemsBefore:o,dropdownItemsAfter:i,...c}=e;const{search:d,hash:p}=(0,s.TH)(),f=(0,ye.Iw)(n),m=(0,ye.gB)(n),{savePreferredVersionName:h}=(0,ve.J)(n),g=[...o,...m.map((e=>{const t=f.alternateDocVersions[e.name]??qe(e);return{label:e.label,to:`${t.path}${d}${p}`,isActive:()=>e===f.activeVersion,onClick:()=>h(e.name)}})),...i],b=(0,Ue.lO)(n)[0],v=t&&g.length>1?(0,u.I)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):b.label,y=t&&g.length>1?void 0:qe(b).path;return g.length<=1?r.createElement(oe,(0,l.Z)({},c,{mobile:t,label:v,to:y,isActive:a?()=>!1:void 0})):r.createElement(pe,(0,l.Z)({},c,{mobile:t,label:v,to:y,items:g,isActive:a?()=>!1:void 0}))}};function He(e){let{type:t,...n}=e;const a=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(t,n),o=We[a];if(!o)throw new Error(`No NavbarItem component found for type "${t}".`);return r.createElement(o,n)}function Ge(){const e=(0,A.e)(),t=(0,w.L)().navbar.items;return r.createElement("ul",{className:"menu__list"},t.map(((t,n)=>r.createElement(He,(0,l.Z)({mobile:!0},t,{onClick:()=>e.toggle(),key:n})))))}function Qe(e){return r.createElement("button",(0,l.Z)({},e,{type:"button",className:"clean-btn navbar-sidebar__back"}),r.createElement(u.Z,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"},"\u2190 Back to main menu"))}function Ze(){const e=0===(0,w.L)().navbar.items.length,t=I();return r.createElement(r.Fragment,null,!e&&r.createElement(Qe,{onClick:()=>t.hide()}),t.content)}function Ve(){const e=(0,A.e)();var t;return void 0===(t=e.shown)&&(t=!0),(0,r.useEffect)((()=>(document.body.style.overflow=t?"hidden":"visible",()=>{document.body.style.overflow="visible"})),[t]),e.shouldRender?r.createElement(B,{header:r.createElement(K,null),primaryMenu:r.createElement(Ge,null),secondaryMenu:r.createElement(Ze,null)}):null}const Ke={navbarHideable:"navbarHideable_m1mJ",navbarHidden:"navbarHidden_jGov"};function Ye(e){return r.createElement("div",(0,l.Z)({role:"presentation"},e,{className:(0,a.Z)("navbar-sidebar__backdrop",e.className)}))}function Xe(e){let{children:t}=e;const{navbar:{hideOnScroll:n,style:o}}=(0,w.L)(),i=(0,A.e)(),{navbarRef:l,isNavbarVisible:s}=function(e){const[t,n]=(0,r.useState)(e),a=(0,r.useRef)(!1),o=(0,r.useRef)(0),i=(0,r.useCallback)((e=>{null!==e&&(o.current=e.getBoundingClientRect().height)}),[]);return(0,R.RF)(((t,r)=>{let{scrollY:i}=t;if(!e)return;if(i=l?n(!1):i+u{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return a.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return r.createElement("nav",{ref:l,"aria-label":(0,u.I)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,a.Z)("navbar","navbar--fixed-top",n&&[Ke.navbarHideable,!s&&Ke.navbarHidden],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown})},t,r.createElement(Ye,{onClick:i.toggle}),r.createElement(Ve,null))}var Je=n(8780);const et={errorBoundaryError:"errorBoundaryError_a6uf"};function tt(e){return r.createElement("button",(0,l.Z)({type:"button"},e),r.createElement(u.Z,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error"},"Try again"))}function nt(e){let{error:t}=e;const n=(0,Je.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return r.createElement("p",{className:et.errorBoundaryError},n)}class rt extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}const at="right";function ot(e){let{width:t=30,height:n=30,className:a,...o}=e;return r.createElement("svg",(0,l.Z)({className:a,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true"},o),r.createElement("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"}))}function it(){const{toggle:e,shown:t}=(0,A.e)();return r.createElement("button",{onClick:e,"aria-label":(0,u.I)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button"},r.createElement(ot,null))}const lt={colorModeToggle:"colorModeToggle_DEke"};function st(e){let{items:t}=e;return r.createElement(r.Fragment,null,t.map(((e,t)=>r.createElement(rt,{key:t,onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t})},r.createElement(He,e)))))}function ut(e){let{left:t,right:n}=e;return r.createElement("div",{className:"navbar__inner"},r.createElement("div",{className:"navbar__items"},t),r.createElement("div",{className:"navbar__items navbar__items--right"},n))}function ct(){const e=(0,A.e)(),t=(0,w.L)().navbar.items,[n,a]=function(e){function t(e){return"left"===(e.position??at)}return[e.filter(t),e.filter((e=>!t(e)))]}(t),o=t.find((e=>"search"===e.type));return r.createElement(ut,{left:r.createElement(r.Fragment,null,!e.disabled&&r.createElement(it,null),r.createElement(Z,null),r.createElement(st,{items:n})),right:r.createElement(r.Fragment,null,r.createElement(st,{items:a}),r.createElement(G,{className:lt.colorModeToggle}),!o&&r.createElement($e,null,r.createElement(je,null)))})}function dt(){return r.createElement(Xe,null,r.createElement(ct,null))}function pt(e){let{item:t}=e;const{to:n,href:a,label:o,prependBaseUrlToHref:i,...s}=t,u=(0,X.Z)(n),c=(0,X.Z)(a,{forcePrependBaseUrl:!0});return r.createElement(Y.Z,(0,l.Z)({className:"footer__link-item"},a?{href:i?c:a}:{to:u},s),o,a&&!(0,J.Z)(a)&&r.createElement(te.Z,null))}function ft(e){let{item:t}=e;return t.html?r.createElement("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement("li",{key:t.href??t.to,className:"footer__item"},r.createElement(pt,{item:t}))}function mt(e){let{column:t}=e;return r.createElement("div",{className:"col footer__col"},r.createElement("div",{className:"footer__title"},t.title),r.createElement("ul",{className:"footer__items clean-list"},t.items.map(((e,t)=>r.createElement(ft,{key:t,item:e})))))}function ht(e){let{columns:t}=e;return r.createElement("div",{className:"row footer__links"},t.map(((e,t)=>r.createElement(mt,{key:t,column:e}))))}function gt(){return r.createElement("span",{className:"footer__link-separator"},"\xb7")}function bt(e){let{item:t}=e;return t.html?r.createElement("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):r.createElement(pt,{item:t})}function vt(e){let{links:t}=e;return r.createElement("div",{className:"footer__links text--center"},r.createElement("div",{className:"footer__links"},t.map(((e,n)=>r.createElement(r.Fragment,{key:n},r.createElement(bt,{item:e}),t.length!==n+1&&r.createElement(gt,null))))))}function yt(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?r.createElement(ht,{columns:t}):r.createElement(vt,{links:t})}var wt=n(941);const St={footerLogoLink:"footerLogoLink_BH7S"};function kt(e){let{logo:t}=e;const{withBaseUrl:n}=(0,X.C)(),o={light:n(t.src),dark:n(t.srcDark??t.src)};return r.createElement(wt.Z,{className:(0,a.Z)("footer__logo",t.className),alt:t.alt,sources:o,width:t.width,height:t.height,style:t.style})}function Et(e){let{logo:t}=e;return t.href?r.createElement(Y.Z,{href:t.href,className:St.footerLogoLink,target:t.target},r.createElement(kt,{logo:t})):r.createElement(kt,{logo:t})}function Ct(e){let{copyright:t}=e;return r.createElement("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function xt(e){let{style:t,links:n,logo:o,copyright:i}=e;return r.createElement("footer",{className:(0,a.Z)("footer",{"footer--dark":"dark"===t})},r.createElement("div",{className:"container container-fluid"},n,(o||i)&&r.createElement("div",{className:"footer__bottom text--center"},o&&r.createElement("div",{className:"margin-bottom--sm"},o),i)))}function _t(){const{footer:e}=(0,w.L)();if(!e)return null;const{copyright:t,links:n,logo:a,style:o}=e;return r.createElement(xt,{style:o,links:n&&n.length>0&&r.createElement(yt,{links:n}),logo:a&&r.createElement(Et,{logo:a}),copyright:t&&r.createElement(Ct,{copyright:t})})}const Tt=r.memo(_t),Lt=(0,P.Qc)([M.S,S.pl,R.OC,ve.L5,i.VC,function(e){let{children:t}=e;return r.createElement(D.n2,null,r.createElement(A.M,null,r.createElement(N,null,t)))}]);function At(e){let{children:t}=e;return r.createElement(Lt,null,t)}function Rt(e){let{error:t,tryAgain:n}=e;return r.createElement("main",{className:"container margin-vert--xl"},r.createElement("div",{className:"row"},r.createElement("div",{className:"col col--6 col--offset-3"},r.createElement("h1",{className:"hero__title"},r.createElement(u.Z,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed"},"This page crashed.")),r.createElement("div",{className:"margin-vert--lg"},r.createElement(tt,{onClick:n,className:"button button--primary shadow--lw"})),r.createElement("hr",null),r.createElement("div",{className:"margin-vert--md"},r.createElement(nt,{error:t})))))}const Pt={mainWrapper:"mainWrapper_z2l0"};function Dt(e){const{children:t,noFooter:n,wrapperClassName:l,title:s,description:u}=e;return(0,b.t)(),r.createElement(At,null,r.createElement(i.d,{title:s,description:u}),r.createElement(y,null),r.createElement(L,null),r.createElement(dt,null),r.createElement("div",{id:d,className:(0,a.Z)(g.k.wrapper.main,Pt.mainWrapper,l)},r.createElement(o.Z,{fallback:e=>r.createElement(Rt,e)},t)),!n&&r.createElement(Tt,null))}},1327:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var r=n(7462),a=n(7294),o=n(9960),i=n(4996),l=n(2263),s=n(6668),u=n(941);function c(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,i.Z)(t.src),dark:(0,i.Z)(t.srcDark||t.src)},l=a.createElement(u.Z,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?a.createElement("div",{className:r},l):l}function d(e){const{siteConfig:{title:t}}=(0,l.Z)(),{navbar:{title:n,logo:u}}=(0,s.L)(),{imageClassName:d,titleClassName:p,...f}=e,m=(0,i.Z)(u?.href||"/"),h=n?"":t,g=u?.alt??h;return a.createElement(o.Z,(0,r.Z)({to:m},f,u?.target&&{target:u.target}),u&&a.createElement(c,{logo:u,alt:g,imageClassName:d}),null!=n&&a.createElement("b",{className:p},n))}},197:(e,t,n)=>{"use strict";n.d(t,{Z:()=>o});var r=n(7294),a=n(5742);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return r.createElement(a.Z,null,t&&r.createElement("meta",{name:"docusaurus_locale",content:t}),n&&r.createElement("meta",{name:"docusaurus_version",content:n}),o&&r.createElement("meta",{name:"docusaurus_tag",content:o}),i&&r.createElement("meta",{name:"docsearch:language",content:i}),n&&r.createElement("meta",{name:"docsearch:version",content:n}),o&&r.createElement("meta",{name:"docsearch:docusaurus_tag",content:o}))}},941:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(7462),a=n(7294),o=n(6010),i=n(2389),l=n(2949);const s={themedImage:"themedImage_ToTc","themedImage--light":"themedImage--light_HNdA","themedImage--dark":"themedImage--dark_i4oU"};function u(e){const t=(0,i.Z)(),{colorMode:n}=(0,l.I)(),{sources:u,className:c,alt:d,...p}=e,f=t?"dark"===n?["dark"]:["light"]:["light","dark"];return a.createElement(a.Fragment,null,f.map((e=>a.createElement("img",(0,r.Z)({key:e,src:u[e],alt:d,className:(0,o.Z)(s.themedImage,s[`themedImage--${e}`],c)},p)))))}},6043:(e,t,n)=>{"use strict";n.d(t,{u:()=>s,z:()=>g});var r=n(7462),a=n(7294),o=n(412),i=n(1442);const l="ease-in-out";function s(e){let{initialState:t}=e;const[n,r]=(0,a.useState)(t??!1),o=(0,a.useCallback)((()=>{r((e=>!e))}),[]);return{collapsed:n,setCollapsed:r,toggleCollapsed:o}}const u={display:"none",overflow:"hidden",height:"0px"},c={display:"block",overflow:"visible",height:"auto"};function d(e,t){const n=t?u:c;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function p(e){let{collapsibleRef:t,collapsed:n,animation:r}=e;const o=(0,a.useRef)(!1);(0,a.useEffect)((()=>{const e=t.current;function a(){const t=e.scrollHeight,n=r?.duration??function(e){if((0,i.n)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${r?.easing??l}`,height:`${t}px`}}function s(){const t=a();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return d(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(s(),requestAnimationFrame((()=>{e.style.height=u.height,e.style.overflow=u.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{s()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,r])}function f(e){if(!o.Z.canUseDOM)return e?u:c}function m(e){let{as:t="div",collapsed:n,children:r,animation:o,onCollapseTransitionEnd:i,className:l,disableSSRStyle:s}=e;const u=(0,a.useRef)(null);return p({collapsibleRef:u,collapsed:n,animation:o}),a.createElement(t,{ref:u,style:s?void 0:f(n),onTransitionEnd:e=>{"height"===e.propertyName&&(d(u.current,n),i?.(n))},className:l},r)}function h(e){let{collapsed:t,...n}=e;const[o,i]=(0,a.useState)(!t),[l,s]=(0,a.useState)(t);return(0,a.useLayoutEffect)((()=>{t||i(!0)}),[t]),(0,a.useLayoutEffect)((()=>{o&&s(t)}),[o,t]),o?a.createElement(m,(0,r.Z)({},n,{collapsed:l})):null}function g(e){let{lazy:t,...n}=e;const r=t?h:m;return a.createElement(r,n)}},9689:(e,t,n)=>{"use strict";n.d(t,{nT:()=>m,pl:()=>f});var r=n(7294),a=n(2389),o=n(12),i=n(902),l=n(6668);const s=(0,o.WA)("docusaurus.announcement.dismiss"),u=(0,o.WA)("docusaurus.announcement.id"),c=()=>"true"===s.get(),d=e=>s.set(String(e)),p=r.createContext(null);function f(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.L)(),t=(0,a.Z)(),[n,o]=(0,r.useState)((()=>!!t&&c()));(0,r.useEffect)((()=>{o(c())}),[]);const i=(0,r.useCallback)((()=>{d(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=u.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;u.set(t),r&&d(!1),!r&&c()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return r.createElement(p.Provider,{value:n},t)}function m(){const e=(0,r.useContext)(p);if(!e)throw new i.i6("AnnouncementBarProvider");return e}},2949:(e,t,n)=>{"use strict";n.d(t,{I:()=>g,S:()=>h});var r=n(7294),a=n(412),o=n(902),i=n(12),l=n(6668);const s=r.createContext(void 0),u="theme",c=(0,i.WA)(u),d={light:"light",dark:"dark"},p=e=>e===d.dark?d.dark:d.light,f=e=>a.Z.canUseDOM?p(document.documentElement.getAttribute("data-theme")):p(e),m=e=>{c.set(p(e))};function h(e){let{children:t}=e;const n=function(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.L)(),[a,o]=(0,r.useState)(f(e));(0,r.useEffect)((()=>{t&&c.del()}),[t]);const i=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(o(t),a&&m(t)):(o(n?window.matchMedia("(prefers-color-scheme: dark)").matches?d.dark:d.light:e),c.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",p(a))}),[a]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==u)return;const t=c.get();null!==t&&i(p(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,i]);const s=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||s.current?s.current=window.matchMedia("print").matches:i(null)};return e.addListener(r),()=>e.removeListener(r)}),[i,t,n]),(0,r.useMemo)((()=>({colorMode:a,setColorMode:i,get isDarkTheme(){return a===d.dark},setLightTheme(){i(d.light)},setDarkTheme(){i(d.dark)}})),[a,i])}();return r.createElement(s.Provider,{value:n},t)}function g(){const e=(0,r.useContext)(s);if(null==e)throw new o.i6("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},373:(e,t,n)=>{"use strict";n.d(t,{J:()=>v,L5:()=>g});var r=n(7294),a=n(143),o=n(9935),i=n(6668),l=n(2802),s=n(902),u=n(12);const c=e=>`docs-preferred-version-${e}`,d={save:(e,t,n)=>{(0,u.WA)(c(e),{persistence:t}).set(n)},read:(e,t)=>(0,u.WA)(c(e),{persistence:t}).get(),clear:(e,t)=>{(0,u.WA)(c(e),{persistence:t}).del()}},p=e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}])));const f=r.createContext(null);function m(){const e=(0,a._r)(),t=(0,i.L)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>p(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=d.read(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(d.clear(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){d.save(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function h(e){let{children:t}=e;const n=m();return r.createElement(f.Provider,{value:n},t)}function g(e){let{children:t}=e;return l.cE?r.createElement(h,null,t):r.createElement(r.Fragment,null,t)}function b(){const e=(0,r.useContext)(f);if(!e)throw new s.i6("DocsPreferredVersionContextProvider");return e}function v(e){void 0===e&&(e=o.m);const t=(0,a.zh)(e),[n,i]=b(),{preferredVersionName:l}=n[e];return{preferredVersion:t.versions.find((e=>e.name===l))??null,savePreferredVersionName:(0,r.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}},1116:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,b:()=>l});var r=n(7294),a=n(902);const o=Symbol("EmptyContext"),i=r.createContext(o);function l(e){let{children:t,name:n,items:a}=e;const o=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return r.createElement(i.Provider,{value:o},t)}function s(){const e=(0,r.useContext)(i);if(e===o)throw new a.i6("DocsSidebarProvider");return e}},4477:(e,t,n)=>{"use strict";n.d(t,{E:()=>l,q:()=>i});var r=n(7294),a=n(902);const o=r.createContext(null);function i(e){let{children:t,version:n}=e;return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(null===e)throw new a.i6("DocsVersionProvider");return e}},2961:(e,t,n)=>{"use strict";n.d(t,{M:()=>p,e:()=>f});var r=n(7294),a=n(3102),o=n(7524),i=n(6550),l=(n(1688),n(902));function s(e){!function(e){const t=(0,i.k6)(),n=(0,l.zX)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}var u=n(6668);const c=r.createContext(void 0);function d(){const e=function(){const e=(0,a.HY)(),{items:t}=(0,u.L)().navbar;return 0===t.length&&!e.component}(),t=(0,o.i)(),n=!e&&"mobile"===t,[i,l]=(0,r.useState)(!1);s((()=>{if(i)return l(!1),!1}));const c=(0,r.useCallback)((()=>{l((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&l(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:c,shown:i})),[e,n,c,i])}function p(e){let{children:t}=e;const n=d();return r.createElement(c.Provider,{value:n},t)}function f(){const e=r.useContext(c);if(void 0===e)throw new l.i6("NavbarMobileSidebarProvider");return e}},3102:(e,t,n)=>{"use strict";n.d(t,{HY:()=>l,Zo:()=>s,n2:()=>i});var r=n(7294),a=n(902);const o=r.createContext(null);function i(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return r.createElement(o.Provider,{value:n},t)}function l(){const e=(0,r.useContext)(o);if(!e)throw new a.i6("NavbarSecondaryMenuContentProvider");return e[0]}function s(e){let{component:t,props:n}=e;const i=(0,r.useContext)(o);if(!i)throw new a.i6("NavbarSecondaryMenuContentProvider");const[,l]=i,s=(0,a.Ql)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},9727:(e,t,n)=>{"use strict";n.d(t,{h:()=>a,t:()=>o});var r=n(7294);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},7524:(e,t,n)=>{"use strict";n.d(t,{i:()=>u});var r=n(7294),a=n(412);const o={desktop:"desktop",mobile:"mobile",ssr:"ssr"},i=996;function l(){return a.Z.canUseDOM?window.innerWidth>i?o.desktop:o.mobile:o.ssr}const s=!1;function u(){const[e,t]=(0,r.useState)((()=>s?"ssr":l()));return(0,r.useEffect)((()=>{function e(){t(l())}const n=s?window.setTimeout(e,1e3):void 0;return window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e),clearTimeout(n)}}),[]),e}},5281:(e,t,n)=>{"use strict";n.d(t,{k:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{}}},1442:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{n:()=>r})},2802:(e,t,n)=>{"use strict";n.d(t,{MN:()=>x,Wl:()=>m,_F:()=>v,cE:()=>p,jA:()=>h,xz:()=>f,hI:()=>C,lO:()=>S,vY:()=>E,oz:()=>k,s1:()=>w});var r=n(7294),a=n(6550),o=n(8790),i=n(143),l=n(373),s=n(4477),u=n(1116);function c(e){return Array.from(new Set(e))}var d=n(8596);const p=!!i._r;function f(e){const t=(0,s.E)();if(!e)return;const n=t.docs[e];if(!n)throw new Error(`no version doc found by id=${e}`);return n}function m(e){if(e.href)return e.href;for(const t of e.items){if("link"===t.type)return t.href;if("category"===t.type){const e=m(t);if(e)return e}}}function h(){const{pathname:e}=(0,a.TH)(),t=(0,u.V)();if(!t)throw new Error("Unexpected: cant find current sidebar in context");const n=y({sidebarItems:t.items,pathname:e,onlyCategories:!0}).slice(-1)[0];if(!n)throw new Error(`${e} is not associated with a category. useCurrentSidebarCategory() should only be used on category index pages.`);return n}const g=(e,t)=>void 0!==e&&(0,d.Mg)(e,t),b=(e,t)=>e.some((e=>v(e,t)));function v(e,t){return"link"===e.type?g(e.href,t):"category"===e.type&&(g(e.href,t)||b(e.items,t))}function y(e){let{sidebarItems:t,pathname:n,onlyCategories:r=!1}=e;const a=[];return function e(t){for(const o of t)if("category"===o.type&&((0,d.Mg)(o.href,n)||e(o.items))||"link"===o.type&&(0,d.Mg)(o.href,n)){return r&&"category"!==o.type||a.unshift(o),!0}return!1}(t),a}function w(){const e=(0,u.V)(),{pathname:t}=(0,a.TH)(),n=(0,i.gA)()?.pluginData.breadcrumbs;return!1!==n&&e?y({sidebarItems:e.items,pathname:t}):null}function S(e){const{activeVersion:t}=(0,i.Iw)(e),{preferredVersion:n}=(0,l.J)(e),a=(0,i.yW)(e);return(0,r.useMemo)((()=>c([t,n,a].filter(Boolean))),[t,n,a])}function k(e,t){const n=S(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return r[1]}),[e,n])}function E(e,t){const n=S(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${n.length>1?"s":""} "${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${c(t.map((e=>e.id))).join("\n- ")}`)}return r}),[e,n])}function C(e){let{route:t,versionMetadata:n}=e;const r=(0,a.TH)(),i=t.routes,l=i.find((e=>(0,a.LX)(r.pathname,e)));if(!l)return null;const s=l.sidebar,u=s?n.docsSidebars[s]:void 0;return{docElement:(0,o.H)(i),sidebarName:s,sidebarItems:u}}function x(e){return e.filter((e=>"category"!==e.type||!!m(e)))}},1944:(e,t,n)=>{"use strict";n.d(t,{FG:()=>p,d:()=>c,VC:()=>f});var r=n(7294),a=n(6010),o=n(5742),i=n(226);function l(){const e=r.useContext(i._);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}var s=n(4996),u=n(2263);function c(e){let{title:t,description:n,keywords:a,image:i,children:l}=e;const c=function(e){const{siteConfig:t}=(0,u.Z)(),{title:n,titleDelimiter:r}=t;return e?.trim().length?`${e.trim()} ${r} ${n}`:n}(t),{withBaseUrl:d}=(0,s.C)(),p=i?d(i,{absolute:!0}):void 0;return r.createElement(o.Z,null,t&&r.createElement("title",null,c),t&&r.createElement("meta",{property:"og:title",content:c}),n&&r.createElement("meta",{name:"description",content:n}),n&&r.createElement("meta",{property:"og:description",content:n}),a&&r.createElement("meta",{name:"keywords",content:Array.isArray(a)?a.join(","):a}),p&&r.createElement("meta",{property:"og:image",content:p}),p&&r.createElement("meta",{name:"twitter:image",content:p}),l)}const d=r.createContext(void 0);function p(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.Z)(i,t);return r.createElement(d.Provider,{value:l},r.createElement(o.Z,null,r.createElement("html",{className:l})),n)}function f(e){let{children:t}=e;const n=l(),o=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const i=`plugin-id-${n.plugin.id}`;return r.createElement(p,{className:(0,a.Z)(o,i)},t)}},902:(e,t,n)=>{"use strict";n.d(t,{D9:()=>i,Qc:()=>u,Ql:()=>s,i6:()=>l,zX:()=>o});var r=n(7294);const a=n(412).Z.canUseDOM?r.useLayoutEffect:r.useEffect;function o(e){const t=(0,r.useRef)(e);return a((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function i(e){const t=(0,r.useRef)();return a((()=>{t.current=e})),t.current}class l extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function s(e){const t=Object.entries(e);return t.sort(((e,t)=>e[0].localeCompare(t[0]))),(0,r.useMemo)((()=>e),t.flat())}function u(e){return t=>{let{children:n}=t;return r.createElement(r.Fragment,null,e.reduceRight(((e,t)=>r.createElement(t,null,e)),n))}}},8596:(e,t,n)=>{"use strict";n.d(t,{Mg:()=>i,Ns:()=>l});var r=n(7294),a=n(723),o=n(2263);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function l(){const{baseUrl:e}=(0,o.Z)().siteConfig;return(0,r.useMemo)((()=>function(e){let{baseUrl:t,routes:n}=e;function r(e){return e.path===t&&!0===e.exact}function a(e){return e.path===t&&!e.exact}return function e(t){if(0===t.length)return;return t.find(r)||e(t.filter(a).flatMap((e=>e.routes??[])))}(n)}({routes:a.Z,baseUrl:e})),[e])}},2466:(e,t,n)=>{"use strict";n.d(t,{Ct:()=>p,OC:()=>s,RF:()=>d});var r=n(7294),a=n(412),o=n(2389),i=n(902);const l=r.createContext(void 0);function s(e){let{children:t}=e;const n=function(){const e=(0,r.useRef)(!0);return(0,r.useMemo)((()=>({scrollEventsEnabledRef:e,enableScrollEvents:()=>{e.current=!0},disableScrollEvents:()=>{e.current=!1}})),[])}();return r.createElement(l.Provider,{value:n},t)}function u(){const e=(0,r.useContext)(l);if(null==e)throw new i.i6("ScrollControllerProvider");return e}const c=()=>a.Z.canUseDOM?{scrollX:window.pageXOffset,scrollY:window.pageYOffset}:null;function d(e,t){void 0===t&&(t=[]);const{scrollEventsEnabledRef:n}=u(),a=(0,r.useRef)(c()),o=(0,i.zX)(e);(0,r.useEffect)((()=>{const e=()=>{if(!n.current)return;const e=c();o(e,a.current),a.current=e},t={passive:!0};return e(),window.addEventListener("scroll",e,t),()=>window.removeEventListener("scroll",e,t)}),[o,n,...t])}function p(){const e=(0,r.useRef)(null),t=(0,o.Z)()&&"smooth"===getComputedStyle(document.documentElement).scrollBehavior;return{startScroll:n=>{e.current=t?function(e){return window.scrollTo({top:e,behavior:"smooth"}),()=>{}}(n):function(e){let t=null;const n=document.documentElement.scrollTop>e;return function r(){const a=document.documentElement.scrollTop;(n&&a>e||!n&&at&&cancelAnimationFrame(t)}(n)},cancelScroll:()=>e.current?.()}}},3320:(e,t,n)=>{"use strict";n.d(t,{HX:()=>r,os:()=>a});n(2263);const r="default";function a(e,t){return`docs-${e}-${t}`}},12:(e,t,n)=>{"use strict";n.d(t,{WA:()=>s});n(7294),n(1688);const r="localStorage";function a(e){let{key:t,oldValue:n,newValue:r,storage:a}=e;if(n===r)return;const o=document.createEvent("StorageEvent");o.initStorageEvent("storage",!1,!1,t,n,r,window.location.href,a),window.dispatchEvent(o)}function o(e){if(void 0===e&&(e=r),"undefined"==typeof window)throw new Error("Browser storage is not available on Node.js/Docusaurus SSR process.");if("none"===e)return null;try{return window[e]}catch(n){return t=n,i||(console.warn("Docusaurus browser storage is not available.\nPossible reasons: running Docusaurus in an iframe, in an incognito browser session, or using too strict browser privacy settings.",t),i=!0),null}var t}let i=!1;const l={get:()=>null,set:()=>{},del:()=>{},listen:()=>()=>{}};function s(e,t){if("undefined"==typeof window)return function(e){function t(){throw new Error(`Illegal storage API usage for storage key "${e}".\nDocusaurus storage APIs are not supposed to be called on the server-rendering process.\nPlease only call storage APIs in effects and event handlers.`)}return{get:t,set:t,del:t,listen:t}}(e);const n=o(t?.persistence);return null===n?l:{get:()=>{try{return n.getItem(e)}catch(t){return console.error(`Docusaurus storage error, can't get key=${e}`,t),null}},set:t=>{try{const r=n.getItem(e);n.setItem(e,t),a({key:e,oldValue:r,newValue:t,storage:n})}catch(r){console.error(`Docusaurus storage error, can't set ${e}=${t}`,r)}},del:()=>{try{const t=n.getItem(e);n.removeItem(e),a({key:e,oldValue:t,newValue:null,storage:n})}catch(t){console.error(`Docusaurus storage error, can't delete key=${e}`,t)}},listen:t=>{try{const r=r=>{r.storageArea===n&&r.key===e&&t(r)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}catch(r){return console.error(`Docusaurus storage error, can't listen for changes of key=${e}`,r),()=>{}}}}}},4711:(e,t,n)=>{"use strict";n.d(t,{l:()=>o});var r=n(2263),a=n(6550);function o(){const{siteConfig:{baseUrl:e,url:t},i18n:{defaultLocale:n,currentLocale:o}}=(0,r.Z)(),{pathname:i}=(0,a.TH)(),l=o===n?e:e.replace(`/${o}/`,"/"),s=i.replace(e,"");return{createUrl:function(e){let{locale:r,fullyQualified:a}=e;return`${a?t:""}${function(e){return e===n?`${l}`:`${l}${e}/`}(r)}${s}`}}}},5936:(e,t,n)=>{"use strict";n.d(t,{S:()=>i});var r=n(7294),a=n(6550),o=n(902);function i(e){const t=(0,a.TH)(),n=(0,o.D9)(t),i=(0,o.zX)(e);(0,r.useEffect)((()=>{n&&t!==n&&i({location:t,previousLocation:n})}),[i,t,n])}},6668:(e,t,n)=>{"use strict";n.d(t,{L:()=>a});var r=n(2263);function a(){return(0,r.Z)().siteConfig.themeConfig}},8802:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){const{trailingSlash:n,baseUrl:r}=t;if(e.startsWith("#"))return e;if(void 0===n)return e;const[a]=e.split(/[#?]/),o="/"===a||a===r?a:(i=a,n?function(e){return e.endsWith("/")?e:`${e}/`}(i):function(e){return e.endsWith("/")?e.slice(0,-1):e}(i));var i;return e.replace(a,o)}},4143:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=void 0,t.getErrorCausalChain=function e(t){return t.cause?[t,...e(t.cause)]:[t]}},8780:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getErrorCausalChain=t.applyTrailingSlash=t.blogPostContainerID=void 0,t.blogPostContainerID="__blog-post-container";var a=n(8802);Object.defineProperty(t,"applyTrailingSlash",{enumerable:!0,get:function(){return r(a).default}});var o=n(4143);Object.defineProperty(t,"getErrorCausalChain",{enumerable:!0,get:function(){return o.getErrorCausalChain}})},311:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var r=n(7294),a=n(6010);const o={loadingRing:"loadingRing_RJI3","loading-ring":"loading-ring_FB5o"};function i(e){let{className:t}=e;return r.createElement("div",{className:(0,a.Z)(o.loadingRing,t)},r.createElement("div",null),r.createElement("div",null),r.createElement("div",null),r.createElement("div",null))}},22:(e,t,n)=>{"use strict";n.d(t,{w:()=>l});var r=n(1336),a=n.n(r),o=n(1029);const i=new Map;function l(e,t){const n=`${e}${t}`;let r=i.get(n);return r||(r=async function(e,t){{const n=`${e}${o.J.replace("{dir}",t?`-${t.replace(/\//g,"-")}`:"")}`;if(new URL(n,location.origin).origin!==location.origin)throw new Error("Unexpected version url");const r=await(await fetch(n)).json(),i=r.map(((e,t)=>{let{documents:n,index:r}=e;return{type:t,documents:n,index:a().Index.load(r)}})),l=r.reduce(((e,t)=>{for(const n of t.index.invertedIndex)/\p{Unified_Ideograph}/u.test(n[0][0])&&e.add(n[0]);return e}),new Set);return{wrappedIndexes:i,zhDictionary:Array.from(l)}}return{wrappedIndexes:[],zhDictionary:[]}}(e,t),i.set(n,r)),r}},8202:(e,t,n)=>{"use strict";n.d(t,{v:()=>s});var r=n(1336),a=n.n(r);var o=n(1029);function i(e){return l(e).concat(l(e.filter((e=>{const t=e[e.length-1];return!t.trailing&&t.maybeTyping})),!0))}function l(e,t){return e.map((e=>({tokens:e.map((e=>e.value)),term:e.map((e=>({value:e.value,presence:a().Query.presence.REQUIRED,wildcard:(t?e.trailing||e.maybeTyping:e.trailing)?a().Query.wildcard.TRAILING:a().Query.wildcard.NONE})))})))}function s(e,t,n){return function(r,l){const s=function(e,t){if(1===t.length&&["ja","jp","th"].includes(t[0]))return a()[t[0]].tokenizer(e).map((e=>e.toString()));let n=/[^-\s]+/g;return t.includes("zh")&&(n=/\w+|\p{Unified_Ideograph}+/gu),e.toLowerCase().match(n)||[]}(r,o.dK);if(0===s.length)return void l([]);const u=function(e,t){const n=function(e,t){const n=[];return function e(r,a){if(0===r.length)return void n.push(a);const o=r[0];if(/\p{Unified_Ideograph}/u.test(o)){const n=function(e,t){const n=[];return function e(r,a){let o=0,i=!1;for(const l of t)if(r.substr(0,l.length)===l){const t={missed:a.missed,term:a.term.concat({value:l})};r.length>l.length?e(r.substr(l.length),t):n.push(t),i=!0}else for(let t=l.length-1;t>o;t-=1){const s=l.substr(0,t);if(r.substr(0,t)===s){o=t;const l={missed:a.missed,term:a.term.concat({value:s,trailing:!0})};r.length>t?e(r.substr(t),l):n.push(l),i=!0;break}}i||(r.length>0?e(r.substr(1),{missed:a.missed+1,term:a.term}):a.term.length>0&&n.push(a))}(e,{missed:0,term:[]}),n.sort(((e,t)=>{const n=e.missed>0?1:0,r=t.missed>0?1:0;return n!==r?n-r:e.term.length-t.term.length})).map((e=>e.term))}(o,t);for(const t of n){const n=a.concat(...t);e(r.slice(1),n)}}else{const t=a.concat({value:o});e(r.slice(1),t)}}(e,[]),n}(e,t);if(0===n.length)return[{tokens:e,term:e.map((e=>({value:e,presence:a().Query.presence.REQUIRED,wildcard:a().Query.wildcard.LEADING|a().Query.wildcard.TRAILING})))}];for(const a of n)a[a.length-1].maybeTyping=!0;const r=[];for(const i of o.dK)if("en"===i)o._k||r.unshift(a().stopWordFilter);else{const e=a()[i];e.stopWordFilter&&r.unshift(e.stopWordFilter)}let l;if(r.length>0){const e=e=>r.reduce(((e,t)=>e.filter((e=>t(e.value)))),e);l=[];const t=[];for(const r of n){const n=e(r);l.push(n),n.length0&&t.push(n)}n.push(...t)}else l=n.slice();const s=[];for(const a of l)if(a.length>2)for(let e=a.length-1;e>=0;e-=1)s.push(a.slice(0,e).concat(a.slice(e+1)));return i(n).concat(i(s))}(s,t),c=[];e:for(const{term:t,tokens:a}of u)for(const{documents:r,index:o,type:i}of e)if(c.push(...o.query((e=>{for(const n of t)e.term(n.value,{wildcard:n.wildcard,presence:n.presence})})).slice(0,n).filter((e=>!c.some((t=>t.document.i.toString()===e.ref)))).slice(0,n-c.length).map((t=>{const n=r.find((e=>e.i.toString()===t.ref));return{document:n,type:i,page:0!==i&&e[0].documents.find((e=>e.i===n.p)),metadata:t.matchData.metadata,tokens:a,score:t.score}}))),c.length>=n)break e;!function(e){e.forEach(((e,t)=>{e.index=t})),e.sort(((t,n)=>{let r=t.type>0&&t.page?e.findIndex((e=>e.document===t.page)):t.index,a=n.type>0&&n.page?e.findIndex((e=>e.document===n.page)):n.index;return-1===r&&(r=t.index),-1===a&&(a=n.index),r===a?0===t.type?-1:0===n.type?1:t.index-n.index:r-a}))}(c),function(e){e.forEach(((t,n)=>{n>0&&t.page&&e.some((e=>e.document===t.page))&&(n{"use strict";function r(e){return e.join(" \u203a ")}n.d(t,{e:()=>r})},1690:(e,t,n)=>{"use strict";function r(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}n.d(t,{X:()=>r})},1073:(e,t,n)=>{"use strict";function r(e,t){const n=[];for(const r of Object.values(e))r[t]&&n.push(...r[t].position);return n.sort(((e,t)=>e[0]-t[0]||t[1]-e[1]))}n.d(t,{m:()=>r})},2539:(e,t,n)=>{"use strict";n.d(t,{C:()=>a});var r=n(1690);function a(e,t,n){const o=[];for(const i of t){const n=e.toLowerCase().indexOf(i);if(n>=0){n>0&&o.push(a(e.substr(0,n),t)),o.push(`${(0,r.X)(e.substr(n,i.length))}`);const l=n+i.length;l${(0,r.X)(e)}`:(0,r.X)(e):o.join("")}},726:(e,t,n)=>{"use strict";n.d(t,{o:()=>s});var r=n(1690),a=n(2539);const o=/\w+|\p{Unified_Ideograph}/u;function i(e){const t=[];let n=0,r=e;for(;r.length>0;){const a=r.match(o);if(!a){t.push(r);break}a.index>0&&t.push(r.substring(0,a.index)),t.push(a[0]),n+=a.index+a[0].length,r=e.substring(n)}return t}var l=n(1029);function s(e,t,n,o){void 0===o&&(o=l.Hk);const{chunkIndex:s,chunks:u}=function(e,t,n){const o=[];let l=0,s=0,u=-1;for(;ls){const t=i(e.substring(s,c)).map((e=>({html:(0,r.X)(e),textLength:e.length})));for(const e of t)o.push(e)}-1===u&&(u=o.length),s=c+d,o.push({html:(0,a.C)(e.substring(c,s),n,!0),textLength:d})}}if(s({html:(0,r.X)(e),textLength:e.length})));for(const e of t)o.push(e)}return{chunkIndex:u,chunks:o}}(e,t,n),c=u.slice(0,s),d=u[s],p=[d.html],f=u.slice(s+1);let m=d.textLength,h=0,g=0,b=!1,v=!1;for(;m0){const e=c.pop();m+e.textLength<=o?(p.unshift(e.html),h+=e.textLength,m+=e.textLength):(b=!0,c.length=0)}else{if(!(f.length>0))break;{const e=f.shift();m+e.textLength<=o?(p.push(e.html),g+=e.textLength,m+=e.textLength):(v=!0,f.length=0)}}return(b||c.length>0)&&p.unshift("\u2026"),(v||f.length>0)&&p.push("\u2026"),p.join("")}},1029:(e,t,n)=>{"use strict";n.d(t,{vc:()=>l,gQ:()=>h,H6:()=>d,hG:()=>v,l9:()=>g,dK:()=>o,_k:()=>i,pu:()=>m,AY:()=>p,t_:()=>f,Kc:()=>b,J:()=>s,Hk:()=>c,qo:()=>u,pQ:()=>y});var r=n(1336),a=n.n(r);n(892)(a()),n(1728).w(a()),n(4182)(a());const o=["en","zh"],i=!1,l=null,s="search-index{dir}.json?_=22a0856a",u=8,c=50,d=!1,p=!0,f=!0,m="right",h=void 0,g=!0,b=null,v=!1,y=!1},1728:(e,t,n)=>{"use strict";function r(e){const t=new RegExp("^[^"+e+"]+","u"),n=new RegExp("[^"+e+"]+$","u");return function(e){return e.update((function(e){return e.replace(t,"").replace(n,"")}))}}function a(e,t){e.trimmerSupport.generateTrimmer=r,e.zh=function(){this.pipeline.reset(),this.pipeline.add(e.zh.trimmer,e.zh.stopWordFilter),t&&(this.tokenizer=t)},t&&(e.zh.tokenizer=t),e.zh.wordCharacters="\\u3400-\\u4DBF\\u4E00-\\u9FFC\\uFA0E\\uFA0F\\uFA11\\uFA13\\uFA14\\uFA1F\\uFA21\\uFA23\\uFA24\\uFA27-\\uFA29\\u{20000}-\\u{2A6DD}\\u{2A700}-\\u{2B734}\\u{2B740}-\\u{2B81D}\\u{2B820}-\\u{2CEA1}\\u{2CEB0}-\\u{2EBE0}\\u{30000}-\\u{3134A}",e.zh.trimmer=e.trimmerSupport.generateTrimmer(e.zh.wordCharacters),e.Pipeline.registerFunction(e.zh.trimmer,"trimmer-zh"),e.zh.stopWordFilter=e.generateStopWordFilter("\u7684 \u4e00 \u4e0d \u5728 \u4eba \u6709 \u662f \u4e3a \u4ee5 \u4e8e \u4e0a \u4ed6 \u800c \u540e \u4e4b \u6765 \u53ca \u4e86 \u56e0 \u4e0b \u53ef \u5230 \u7531 \u8fd9 \u4e0e \u4e5f \u6b64 \u4f46 \u5e76 \u4e2a \u5176 \u5df2 \u65e0 \u5c0f \u6211 \u4eec \u8d77 \u6700 \u518d \u4eca \u53bb \u597d \u53ea \u53c8 \u6216 \u5f88 \u4ea6 \u67d0 \u628a \u90a3 \u4f60 \u4e43 \u5b83 \u5427 \u88ab \u6bd4 \u522b \u8d81 \u5f53 \u4ece \u5230 \u5f97 \u6253 \u51e1 \u513f \u5c14 \u8be5 \u5404 \u7ed9 \u8ddf \u548c \u4f55 \u8fd8 \u5373 \u51e0 \u65e2 \u770b \u636e \u8ddd \u9760 \u5566 \u4e86 \u53e6 \u4e48 \u6bcf \u4eec \u561b \u62ff \u54ea \u90a3 \u60a8 \u51ed \u4e14 \u5374 \u8ba9 \u4ecd \u5565 \u5982 \u82e5 \u4f7f \u8c01 \u867d \u968f \u540c \u6240 \u5979 \u54c7 \u55e1 \u5f80 \u54ea \u4e9b \u5411 \u6cbf \u54df \u7528 \u4e8e \u54b1 \u5219 \u600e \u66fe \u81f3 \u81f4 \u7740 \u8bf8 \u81ea".split(" ")),e.Pipeline.registerFunction(e.zh.stopWordFilter,"stopWordFilter-zh")}n.d(t,{w:()=>a})},6010:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;ta});const a=function(){for(var e,t,n=0,a="";n{"use strict";n.d(t,{lX:()=>w,q_:()=>_,ob:()=>f,PP:()=>L,Ep:()=>p});var r=n(7462);function a(e){return"/"===e.charAt(0)}function o(e,t){for(var n=t,r=n+1,a=e.length;r=0;p--){var f=i[p];"."===f?o(i,p):".."===f?(o(i,p),d++):d&&(o(i,p),d--)}if(!u)for(;d--;d)i.unshift("..");!u||""===i[0]||i[0]&&a(i[0])||i.unshift("");var m=i.join("/");return n&&"/"!==m.substr(-1)&&(m+="/"),m};var l=n(8776);function s(e){return"/"===e.charAt(0)?e:"/"+e}function u(e){return"/"===e.charAt(0)?e.substr(1):e}function c(e,t){return function(e,t){return 0===e.toLowerCase().indexOf(t.toLowerCase())&&-1!=="/?#".indexOf(e.charAt(t.length))}(e,t)?e.substr(t.length):e}function d(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e}function p(e){var t=e.pathname,n=e.search,r=e.hash,a=t||"/";return n&&"?"!==n&&(a+="?"===n.charAt(0)?n:"?"+n),r&&"#"!==r&&(a+="#"===r.charAt(0)?r:"#"+r),a}function f(e,t,n,a){var o;"string"==typeof e?(o=function(e){var t=e||"/",n="",r="",a=t.indexOf("#");-1!==a&&(r=t.substr(a),t=t.substr(0,a));var o=t.indexOf("?");return-1!==o&&(n=t.substr(o),t=t.substr(0,o)),{pathname:t,search:"?"===n?"":n,hash:"#"===r?"":r}}(e),o.state=t):(void 0===(o=(0,r.Z)({},e)).pathname&&(o.pathname=""),o.search?"?"!==o.search.charAt(0)&&(o.search="?"+o.search):o.search="",o.hash?"#"!==o.hash.charAt(0)&&(o.hash="#"+o.hash):o.hash="",void 0!==t&&void 0===o.state&&(o.state=t));try{o.pathname=decodeURI(o.pathname)}catch(l){throw l instanceof URIError?new URIError('Pathname "'+o.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):l}return n&&(o.key=n),a?o.pathname?"/"!==o.pathname.charAt(0)&&(o.pathname=i(o.pathname,a.pathname)):o.pathname=a.pathname:o.pathname||(o.pathname="/"),o}function m(){var e=null;var t=[];return{setPrompt:function(t){return e=t,function(){e===t&&(e=null)}},confirmTransitionTo:function(t,n,r,a){if(null!=e){var o="function"==typeof e?e(t,n):e;"string"==typeof o?"function"==typeof r?r(o,a):a(!0):a(!1!==o)}else a(!0)},appendListener:function(e){var n=!0;function r(){n&&e.apply(void 0,arguments)}return t.push(r),function(){n=!1,t=t.filter((function(e){return e!==r}))}},notifyListeners:function(){for(var e=arguments.length,n=new Array(e),r=0;rt?n.splice(t,n.length-t,a):n.push(a),d({action:r,location:a,index:t,entries:n})}}))},replace:function(e,t){var r="REPLACE",a=f(e,t,h(),w.location);c.confirmTransitionTo(a,r,n,(function(e){e&&(w.entries[w.index]=a,d({action:r,location:a}))}))},go:y,goBack:function(){y(-1)},goForward:function(){y(1)},canGo:function(e){var t=w.index+e;return t>=0&&t{"use strict";var r=n(9864),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},o={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function s(e){return r.isMemo(e)?i:l[e.$$typeof]||a}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},l[r.Memo]=i;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,p=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,m=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(m){var a=f(n);a&&a!==m&&e(t,a,r)}var i=c(n);d&&(i=i.concat(d(n)));for(var l=s(t),h=s(n),g=0;g{"use strict";e.exports=function(e,t,n,r,a,o,i,l){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[n,r,a,o,i,l],c=0;(s=new Error(t.replace(/%s/g,(function(){return u[c++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},5826:e=>{e.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},4182:function(e,t,n){var r,a;r=function(){return function(e){e.multiLanguage=function(){for(var t=Array.prototype.slice.call(arguments),n=t.join("-"),r="",a=[],o=[],i=0;i=n&&t[(a-=n)>>3]&1<<(7&a))return this.cursor++,!0}return!1},in_grouping_b:function(t,n,r){if(this.cursor>this.limit_backward){var a=e.charCodeAt(this.cursor-1);if(a<=r&&a>=n&&t[(a-=n)>>3]&1<<(7&a))return this.cursor--,!0}return!1},out_grouping:function(t,n,r){if(this.cursorr||a>3]&1<<(7&a)))return this.cursor++,!0}return!1},out_grouping_b:function(t,n,r){if(this.cursor>this.limit_backward){var a=e.charCodeAt(this.cursor-1);if(a>r||a>3]&1<<(7&a)))return this.cursor--,!0}return!1},eq_s:function(t,n){if(this.limit-this.cursor>1),d=0,p=l0||a==r||u)break;u=!0}}for(;;){if(l>=(f=t[r]).s_size){if(this.cursor=o+f.s_size,!f.method)return f.result;var h=f.method();if(this.cursor=o+f.s_size,h)return f.result}if((r=f.substring_i)<0)return 0}},find_among_b:function(t,n){for(var r=0,a=n,o=this.cursor,i=this.limit_backward,l=0,s=0,u=!1;;){for(var c=r+(a-r>>1),d=0,p=l=0;f--){if(o-p==i){d=-1;break}if(d=e.charCodeAt(o-1-p)-m.s[f])break;p++}if(d<0?(a=c,s=p):(r=c,l=p),a-r<=1){if(r>0||a==r||u)break;u=!0}}for(;;){var m;if(l>=(m=t[r]).s_size){if(this.cursor=o-m.s_size,!m.method)return m.result;var h=m.method();if(this.cursor=o-m.s_size,h)return m.result}if((r=m.substring_i)<0)return 0}},replace_s:function(t,n,r){var a=r.length-(n-t),o=e.substring(0,t),i=e.substring(n);return e=o+r+i,this.limit+=a,this.cursor>=n?this.cursor+=a:this.cursor>t&&(this.cursor=t),a},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>e.length)throw"faulty slice operation"},slice_from:function(e){this.slice_check(),this.replace_s(this.bra,this.ket,e)},slice_del:function(){this.slice_from("")},insert:function(e,t,n){var r=this.replace_s(e,t,n);e<=this.bra&&(this.bra+=r),e<=this.ket&&(this.ket+=r)},slice_to:function(){return this.slice_check(),e.substring(this.bra,this.ket)},eq_v_b:function(e){return this.eq_s_b(e.length,e)}}}},e.trimmerSupport={generateTrimmer:function(e){var t=new RegExp("^[^"+e+"]+"),n=new RegExp("[^"+e+"]+$");return function(e){return"function"==typeof e.update?e.update((function(e){return e.replace(t,"").replace(n,"")})):e.replace(t,"").replace(n,"")}}}}})?r.call(t,n,t,e):r)||(e.exports=a)},1336:(e,t,n)=>{var r,a;!function(){var o,i,l,s,u,c,d,p,f,m,h,g,b,v,y,w,S,k,E,C,x,_,T,L,A,R,P,D,O,N,F=function(e){var t=new F.Builder;return t.pipeline.add(F.trimmer,F.stopWordFilter,F.stemmer),t.searchPipeline.add(F.stemmer),e.call(t,t),t.build()};F.version="2.3.9",F.utils={},F.utils.warn=(o=this,function(e){o.console&&console.warn&&console.warn(e)}),F.utils.asString=function(e){return null==e?"":e.toString()},F.utils.clone=function(e){if(null==e)return e;for(var t=Object.create(null),n=Object.keys(e),r=0;r0){var s=F.utils.clone(t)||{};s.position=[i,l],s.index=a.length,a.push(new F.Token(n.slice(i,o),s))}i=o+1}}return a},F.tokenizer.separator=/[\s\-]+/,F.Pipeline=function(){this._stack=[]},F.Pipeline.registeredFunctions=Object.create(null),F.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&F.utils.warn("Overwriting existing registered function: "+t),e.label=t,F.Pipeline.registeredFunctions[e.label]=e},F.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||F.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},F.Pipeline.load=function(e){var t=new F.Pipeline;return e.forEach((function(e){var n=F.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)})),t},F.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach((function(e){F.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)}),this)},F.Pipeline.prototype.after=function(e,t){F.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},F.Pipeline.prototype.before=function(e,t){F.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},F.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},F.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n1&&(oe&&(n=a),o!=e);)r=n-t,a=t+Math.floor(r/2),o=this.elements[2*a];return o==e||o>e?2*a:ol?u+=2:i==l&&(t+=n[s+1]*r[u+1],s+=2,u+=2);return t},F.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},F.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var o,i=a.str.charAt(0);i in a.node.edges?o=a.node.edges[i]:(o=new F.TokenSet,a.node.edges[i]=o),1==a.str.length&&(o.final=!0),r.push({node:o,editsRemaining:a.editsRemaining,str:a.str.slice(1)})}if(0!=a.editsRemaining){if("*"in a.node.edges)var l=a.node.edges["*"];else{l=new F.TokenSet;a.node.edges["*"]=l}if(0==a.str.length&&(l.final=!0),r.push({node:l,editsRemaining:a.editsRemaining-1,str:a.str}),a.str.length>1&&r.push({node:a.node,editsRemaining:a.editsRemaining-1,str:a.str.slice(1)}),1==a.str.length&&(a.node.final=!0),a.str.length>=1){if("*"in a.node.edges)var s=a.node.edges["*"];else{s=new F.TokenSet;a.node.edges["*"]=s}1==a.str.length&&(s.final=!0),r.push({node:s,editsRemaining:a.editsRemaining-1,str:a.str.slice(1)})}if(a.str.length>1){var u,c=a.str.charAt(0),d=a.str.charAt(1);d in a.node.edges?u=a.node.edges[d]:(u=new F.TokenSet,a.node.edges[d]=u),1==a.str.length&&(u.final=!0),r.push({node:u,editsRemaining:a.editsRemaining-1,str:c+a.str.slice(2)})}}}return n},F.TokenSet.fromString=function(e){for(var t=new F.TokenSet,n=t,r=0,a=e.length;r=e;t--){var n=this.uncheckedNodes[t],r=n.child.toString();r in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[r]:(n.child._str=r,this.minimizedNodes[r]=n.child),this.uncheckedNodes.pop()}},F.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},F.Index.prototype.search=function(e){return this.query((function(t){new F.QueryParser(e,t).parse()}))},F.Index.prototype.query=function(e){for(var t=new F.Query(this.fields),n=Object.create(null),r=Object.create(null),a=Object.create(null),o=Object.create(null),i=Object.create(null),l=0;l1?1:e},F.Builder.prototype.k1=function(e){this._k1=e},F.Builder.prototype.add=function(e,t){var n=e[this._ref],r=Object.keys(this._fields);this._documents[n]=t||{},this.documentCount+=1;for(var a=0;a=this.length)return F.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},F.QueryLexer.prototype.width=function(){return this.pos-this.start},F.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},F.QueryLexer.prototype.backup=function(){this.pos-=1},F.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{t=(e=this.next()).charCodeAt(0)}while(t>47&&t<58);e!=F.QueryLexer.EOS&&this.backup()},F.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(F.QueryLexer.TERM)),e.ignore(),e.more())return F.QueryLexer.lexText},F.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(F.QueryLexer.EDIT_DISTANCE),F.QueryLexer.lexText},F.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(F.QueryLexer.BOOST),F.QueryLexer.lexText},F.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(F.QueryLexer.TERM)},F.QueryLexer.termSeparator=F.tokenizer.separator,F.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==F.QueryLexer.EOS)return F.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return F.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(F.QueryLexer.TERM),F.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(F.QueryLexer.TERM),F.QueryLexer.lexBoost;if("+"==t&&1===e.width())return e.emit(F.QueryLexer.PRESENCE),F.QueryLexer.lexText;if("-"==t&&1===e.width())return e.emit(F.QueryLexer.PRESENCE),F.QueryLexer.lexText;if(t.match(F.QueryLexer.termSeparator))return F.QueryLexer.lexTerm}else e.escapeCharacter()}},F.QueryParser=function(e,t){this.lexer=new F.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},F.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=F.QueryParser.parseClause;e;)e=e(this);return this.query},F.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},F.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},F.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},F.QueryParser.parseClause=function(e){var t=e.peekLexeme();if(null!=t)switch(t.type){case F.QueryLexer.PRESENCE:return F.QueryParser.parsePresence;case F.QueryLexer.FIELD:return F.QueryParser.parseField;case F.QueryLexer.TERM:return F.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(n+=" with value '"+t.str+"'"),new F.QueryParseError(n,t.start,t.end)}},F.QueryParser.parsePresence=function(e){var t=e.consumeLexeme();if(null!=t){switch(t.str){case"-":e.currentClause.presence=F.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=F.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+t.str+"'";throw new F.QueryParseError(n,t.start,t.end)}var r=e.peekLexeme();if(null==r){n="expecting term or field, found nothing";throw new F.QueryParseError(n,t.start,t.end)}switch(r.type){case F.QueryLexer.FIELD:return F.QueryParser.parseField;case F.QueryLexer.TERM:return F.QueryParser.parseTerm;default:n="expecting term or field, found '"+r.type+"'";throw new F.QueryParseError(n,r.start,r.end)}}},F.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(null!=t){if(-1==e.query.allFields.indexOf(t.str)){var n=e.query.allFields.map((function(e){return"'"+e+"'"})).join(", "),r="unrecognised field '"+t.str+"', possible fields: "+n;throw new F.QueryParseError(r,t.start,t.end)}e.currentClause.fields=[t.str];var a=e.peekLexeme();if(null==a){r="expecting term, found nothing";throw new F.QueryParseError(r,t.start,t.end)}if(a.type===F.QueryLexer.TERM)return F.QueryParser.parseTerm;r="expecting term, found '"+a.type+"'";throw new F.QueryParseError(r,a.start,a.end)}},F.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(null!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(null!=n)switch(n.type){case F.QueryLexer.TERM:return e.nextClause(),F.QueryParser.parseTerm;case F.QueryLexer.FIELD:return e.nextClause(),F.QueryParser.parseField;case F.QueryLexer.EDIT_DISTANCE:return F.QueryParser.parseEditDistance;case F.QueryLexer.BOOST:return F.QueryParser.parseBoost;case F.QueryLexer.PRESENCE:return e.nextClause(),F.QueryParser.parsePresence;default:var r="Unexpected lexeme type '"+n.type+"'";throw new F.QueryParseError(r,n.start,n.end)}else e.nextClause()}},F.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(null!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="edit distance must be numeric";throw new F.QueryParseError(r,t.start,t.end)}e.currentClause.editDistance=n;var a=e.peekLexeme();if(null!=a)switch(a.type){case F.QueryLexer.TERM:return e.nextClause(),F.QueryParser.parseTerm;case F.QueryLexer.FIELD:return e.nextClause(),F.QueryParser.parseField;case F.QueryLexer.EDIT_DISTANCE:return F.QueryParser.parseEditDistance;case F.QueryLexer.BOOST:return F.QueryParser.parseBoost;case F.QueryLexer.PRESENCE:return e.nextClause(),F.QueryParser.parsePresence;default:r="Unexpected lexeme type '"+a.type+"'";throw new F.QueryParseError(r,a.start,a.end)}else e.nextClause()}},F.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(null!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="boost must be numeric";throw new F.QueryParseError(r,t.start,t.end)}e.currentClause.boost=n;var a=e.peekLexeme();if(null!=a)switch(a.type){case F.QueryLexer.TERM:return e.nextClause(),F.QueryParser.parseTerm;case F.QueryLexer.FIELD:return e.nextClause(),F.QueryParser.parseField;case F.QueryLexer.EDIT_DISTANCE:return F.QueryParser.parseEditDistance;case F.QueryLexer.BOOST:return F.QueryParser.parseBoost;case F.QueryLexer.PRESENCE:return e.nextClause(),F.QueryParser.parsePresence;default:r="Unexpected lexeme type '"+a.type+"'";throw new F.QueryParseError(r,a.start,a.end)}else e.nextClause()}},void 0===(a="function"==typeof(r=function(){return F})?r.call(t,n,t,e):r)||(e.exports=a)}()},2497:(e,t,n)=>{"use strict";n.r(t)},2295:(e,t,n)=>{"use strict";n.r(t)},4865:function(e,t,n){var r,a;r=function(){var e,t,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
    '};function a(e,t,n){return en?n:e}function o(e){return 100*(-1+e)}function i(e,t,n){var a;return(a="translate3d"===r.positionUsing?{transform:"translate3d("+o(e)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+o(e)+"%,0)"}:{"margin-left":o(e)+"%"}).transition="all "+t+"ms "+n,a}n.configure=function(e){var t,n;for(t in e)void 0!==(n=e[t])&&e.hasOwnProperty(t)&&(r[t]=n);return this},n.status=null,n.set=function(e){var t=n.isStarted();e=a(e,r.minimum,1),n.status=1===e?null:e;var o=n.render(!t),u=o.querySelector(r.barSelector),c=r.speed,d=r.easing;return o.offsetWidth,l((function(t){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(u,i(e,c,d)),1===e?(s(o,{transition:"none",opacity:1}),o.offsetWidth,setTimeout((function(){s(o,{transition:"all "+c+"ms linear",opacity:0}),setTimeout((function(){n.remove(),t()}),c)}),c)):setTimeout(t,c)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var e=function(){setTimeout((function(){n.status&&(n.trickle(),e())}),r.trickleSpeed)};return r.trickle&&e(),this},n.done=function(e){return e||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(e){var t=n.status;return t?("number"!=typeof e&&(e=(1-t)*a(Math.random()*t,.1,.95)),t=a(t+e,0,.994),n.set(t)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},e=0,t=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===t&&n.start(),e++,t++,r.always((function(){0==--t?(e=0,n.done()):n.set((e-t)/e)})),this):this},n.render=function(e){if(n.isRendered())return document.getElementById("nprogress");c(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=r.template;var a,i=t.querySelector(r.barSelector),l=e?"-100":o(n.status||0),u=document.querySelector(r.parent);return s(i,{transition:"all 0 linear",transform:"translate3d("+l+"%,0,0)"}),r.showSpinner||(a=t.querySelector(r.spinnerSelector))&&f(a),u!=document.body&&c(u,"nprogress-custom-parent"),u.appendChild(t),t},n.remove=function(){d(document.documentElement,"nprogress-busy"),d(document.querySelector(r.parent),"nprogress-custom-parent");var e=document.getElementById("nprogress");e&&f(e)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var e=document.body.style,t="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":"";return t+"Perspective"in e?"translate3d":t+"Transform"in e?"translate":"margin"};var l=function(){var e=[];function t(){var n=e.shift();n&&n(t)}return function(n){e.push(n),1==e.length&&t()}}(),s=function(){var e=["Webkit","O","Moz","ms"],t={};function n(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(e,t){return t.toUpperCase()}))}function r(t){var n=document.body.style;if(t in n)return t;for(var r,a=e.length,o=t.charAt(0).toUpperCase()+t.slice(1);a--;)if((r=e[a]+o)in n)return r;return t}function a(e){return e=n(e),t[e]||(t[e]=r(e))}function o(e,t,n){t=a(t),e.style[t]=n}return function(e,t){var n,r,a=arguments;if(2==a.length)for(n in t)void 0!==(r=t[n])&&t.hasOwnProperty(n)&&o(e,n,r);else o(e,a[1],a[2])}}();function u(e,t){return("string"==typeof e?e:p(e)).indexOf(" "+t+" ")>=0}function c(e,t){var n=p(e),r=n+t;u(n,t)||(e.className=r.substring(1))}function d(e,t){var n,r=p(e);u(e,t)&&(n=r.replace(" "+t+" "," "),e.className=n.substring(1,n.length-1))}function p(e){return(" "+(e.className||"")+" ").replace(/\s+/gi," ")}function f(e){e&&e.parentNode&&e.parentNode.removeChild(e)}return n},void 0===(a="function"==typeof r?r.call(t,n,t,e):r)||(e.exports=a)},7418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(a){return!1}}()?Object.assign:function(e,a){for(var o,i,l=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),s=1;s{var r=n(5826);e.exports=f,e.exports.parse=o,e.exports.compile=function(e,t){return l(o(e,t),t)},e.exports.tokensToFunction=l,e.exports.tokensToRegExp=p;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function o(e,t){for(var n,r=[],o=0,i=0,l="",c=t&&t.delimiter||"/";null!=(n=a.exec(e));){var d=n[0],p=n[1],f=n.index;if(l+=e.slice(i,f),i=f+d.length,p)l+=p[1];else{var m=e[i],h=n[2],g=n[3],b=n[4],v=n[5],y=n[6],w=n[7];l&&(r.push(l),l="");var S=null!=h&&null!=m&&m!==h,k="+"===y||"*"===y,E="?"===y||"*"===y,C=n[2]||c,x=b||v;r.push({name:g||o++,prefix:h||"",delimiter:C,optional:E,repeat:k,partial:S,asterisk:!!w,pattern:x?u(x):w?".*":"[^"+s(C)+"]+?"})}}return i{"use strict";n.d(t,{Z:()=>o});var r=function(){var e=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,n={},r={util:{encode:function e(t){return t instanceof a?new a(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);E+=k.value.length,k=k.next){var C=k.value;if(t.length>e.length)return;if(!(C instanceof a)){var x,_=1;if(v){if(!(x=o(S,E,e,b))||x.index>=e.length)break;var T=x.index,L=x.index+x[0].length,A=E;for(A+=k.value.length;T>=A;)A+=(k=k.next).value.length;if(E=A-=k.value.length,k.value instanceof a)continue;for(var R=k;R!==t.tail&&(Ad.reach&&(d.reach=N);var F=k.prev;if(D&&(F=s(t,F,D),E+=D.length),u(t,F,_),k=s(t,F,new a(p,g?r.tokenize(P,g):P,y,P)),O&&s(t,k,O),_>1){var I={cause:p+","+m,reach:N};i(e,t,n,k.prev,E,I),d&&I.reach>d.reach&&(d.reach=I.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function s(e,t,n){var r=t.next,a={value:n,prev:t,next:r};return t.next=a,r.prev=a,e.length++,a}function u(e,t,n){for(var r=t.next,a=0;a"+o.content+""},r}(),a=r;r.default=r,a.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var o={};o[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",o)}}),Object.defineProperty(a.languages.markup.tag,"addAttribute",{value:function(e,t){a.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:a.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t="\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b",n={pattern:/(^(["']?)\w+\2)[ \t]+\S.*/,lookbehind:!0,alias:"punctuation",inside:null},r={bash:n,environment:{pattern:RegExp("\\$"+t),alias:"constant"},variable:[{pattern:/\$?\(\([\s\S]+?\)\)/,greedy:!0,inside:{variable:[{pattern:/(^\$\(\([\s\S]+)\)\)/,lookbehind:!0},/^\$\(\(/],number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee]-?\d+)?/,operator:/--|\+\+|\*\*=?|<<=?|>>=?|&&|\|\||[=!+\-*/%<>^&|]=?|[?~:]/,punctuation:/\(\(?|\)\)?|,|;/}},{pattern:/\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,greedy:!0,inside:{variable:/^\$\(|^`|\)$|`$/}},{pattern:/\$\{[^}]+\}/,greedy:!0,inside:{operator:/:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,punctuation:/[\[\]]/,environment:{pattern:RegExp("(\\{)"+t),lookbehind:!0,alias:"constant"}}},/\$(?:\w+|[#?*!@$])/],entity:/\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|U[0-9a-fA-F]{8}|u[0-9a-fA-F]{4}|x[0-9a-fA-F]{1,2})/};e.languages.bash={shebang:{pattern:/^#!\s*\/.*/,alias:"important"},comment:{pattern:/(^|[^"{\\$])#.*/,lookbehind:!0},"function-name":[{pattern:/(\bfunction\s+)[\w-]+(?=(?:\s*\(?:\s*\))?\s*\{)/,lookbehind:!0,alias:"function"},{pattern:/\b[\w-]+(?=\s*\(\s*\)\s*\{)/,alias:"function"}],"for-or-select":{pattern:/(\b(?:for|select)\s+)\w+(?=\s+in\s)/,alias:"variable",lookbehind:!0},"assign-left":{pattern:/(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,inside:{environment:{pattern:RegExp("(^|[\\s;|&]|[<>]\\()"+t),lookbehind:!0,alias:"constant"}},alias:"variable",lookbehind:!0},string:[{pattern:/((?:^|[^<])<<-?\s*)(\w+)\s[\s\S]*?(?:\r?\n|\r)\2/,lookbehind:!0,greedy:!0,inside:r},{pattern:/((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s[\s\S]*?(?:\r?\n|\r)\3/,lookbehind:!0,greedy:!0,inside:{bash:n}},{pattern:/(^|[^\\](?:\\\\)*)"(?:\\[\s\S]|\$\([^)]+\)|\$(?!\()|`[^`]+`|[^"\\`$])*"/,lookbehind:!0,greedy:!0,inside:r},{pattern:/(^|[^$\\])'[^']*'/,lookbehind:!0,greedy:!0},{pattern:/\$'(?:[^'\\]|\\[\s\S])*'/,greedy:!0,inside:{entity:r.entity}}],environment:{pattern:RegExp("\\$?"+t),alias:"constant"},variable:r.variable,function:{pattern:/(^|[\s;|&]|[<>]\()(?:add|apropos|apt|apt-cache|apt-get|aptitude|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|composer|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|docker|docker-compose|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|node|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|podman|podman-compose|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vcpkg|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,lookbehind:!0},keyword:{pattern:/(^|[\s;|&]|[<>]\()(?:case|do|done|elif|else|esac|fi|for|function|if|in|select|then|until|while)(?=$|[)\s;|&])/,lookbehind:!0},builtin:{pattern:/(^|[\s;|&]|[<>]\()(?:\.|:|alias|bind|break|builtin|caller|cd|command|continue|declare|echo|enable|eval|exec|exit|export|getopts|hash|help|let|local|logout|mapfile|printf|pwd|read|readarray|readonly|return|set|shift|shopt|source|test|times|trap|type|typeset|ulimit|umask|unalias|unset)(?=$|[)\s;|&])/,lookbehind:!0,alias:"class-name"},boolean:{pattern:/(^|[\s;|&]|[<>]\()(?:false|true)(?=$|[)\s;|&])/,lookbehind:!0},"file-descriptor":{pattern:/\B&\d\b/,alias:"important"},operator:{pattern:/\d?<>|>\||\+=|=[=~]?|!=?|<<[<-]?|[&\d]?>>|\d[<>]&?|[<>][&=]?|&[>&]?|\|[&|]?/,inside:{"file-descriptor":{pattern:/^\d/,alias:"important"}}},punctuation:/\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,number:{pattern:/(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,lookbehind:!0}},n.inside=e.languages.bash;for(var a=["comment","function-name","for-or-select","assign-left","string","environment","function","keyword","builtin","boolean","file-descriptor","operator","punctuation","number"],o=r.variable[1].inside,i=0;i]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.c=a.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),a.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),a.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},a.languages.c.string],char:a.languages.c.char,comment:a.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:a.languages.c}}}}),a.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete a.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(a),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-](?:[^;{\s]|\s+(?![\s{]))*(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(a),function(e){var t,n=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css.selector={pattern:e.languages.css.selector.pattern,lookbehind:!0,inside:t={"pseudo-element":/:(?:after|before|first-letter|first-line|selection)|::[-\w]+/,"pseudo-class":/:[-\w]+/,class:/\.[-\w]+/,id:/#[-\w]+/,attribute:{pattern:RegExp("\\[(?:[^[\\]\"']|"+n.source+")*\\]"),greedy:!0,inside:{punctuation:/^\[|\]$/,"case-sensitivity":{pattern:/(\s)[si]$/i,lookbehind:!0,alias:"keyword"},namespace:{pattern:/^(\s*)(?:(?!\s)[-*\w\xA0-\uFFFF])*\|(?!=)/,lookbehind:!0,inside:{punctuation:/\|$/}},"attr-name":{pattern:/^(\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+/,lookbehind:!0},"attr-value":[n,{pattern:/(=\s*)(?:(?!\s)[-\w\xA0-\uFFFF])+(?=\s*$)/,lookbehind:!0}],operator:/[|~*^$]?=/}},"n-th":[{pattern:/(\(\s*)[+-]?\d*[\dn](?:\s*[+-]\s*\d+)?(?=\s*\))/,lookbehind:!0,inside:{number:/[\dn]+/,operator:/[+-]/}},{pattern:/(\(\s*)(?:even|odd)(?=\s*\))/i,lookbehind:!0}],combinator:/>|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}});var r={pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0},a={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0};e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:r,number:a,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:r,number:a})}(a),a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:a.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),a.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),a.languages.markup&&(a.languages.markup.tag.addInlined("script","javascript"),a.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),a.languages.js=a.languages.javascript,function(e){var t=/#(?!\{).+/,n={pattern:/#\{[^}]+\}/,alias:"variable"};e.languages.coffeescript=e.languages.extend("javascript",{comment:t,string:[{pattern:/'(?:\\[\s\S]|[^\\'])*'/,greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0,inside:{interpolation:n}}],keyword:/\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/,"class-member":{pattern:/@(?!\d)\w+/,alias:"variable"}}),e.languages.insertBefore("coffeescript","comment",{"multiline-comment":{pattern:/###[\s\S]+?###/,alias:"comment"},"block-regex":{pattern:/\/{3}[\s\S]*?\/{3}/,alias:"regex",inside:{comment:t,interpolation:n}}}),e.languages.insertBefore("coffeescript","string",{"inline-javascript":{pattern:/`(?:\\[\s\S]|[^\\`])*`/,inside:{delimiter:{pattern:/^`|`$/,alias:"punctuation"},script:{pattern:/[\s\S]+/,alias:"language-javascript",inside:e.languages.javascript}}},"multiline-string":[{pattern:/'''[\s\S]*?'''/,greedy:!0,alias:"string"},{pattern:/"""[\s\S]*?"""/,greedy:!0,alias:"string",inside:{interpolation:n}}]}),e.languages.insertBefore("coffeescript","keyword",{property:/(?!\d)\w+(?=\s*:(?!:))/}),delete e.languages.coffeescript["template-string"],e.languages.coffee=e.languages.coffeescript}(a),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*/.source.replace(//g,(function(){return/[^\s\x00-\x08\x0e-\x1f,[\]{}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]/.source})),o=/"(?:[^"\\\r\n]|\\.)*"|'(?:[^'\\\r\n]|\\.)*'/.source;function i(e,t){t=(t||"").replace(/m/g,"")+"m";var n=/([:\-,[{]\s*(?:\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\]|\}|(?:[\r\n]\s*)?#))/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return e}));return RegExp(n,t)}e.languages.yaml={scalar:{pattern:RegExp(/([\-:]\s*(?:\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\S[^\r\n]*(?:\2[^\r\n]+)*)/.source.replace(/<>/g,(function(){return r}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp(/((?:^|[:\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\s*:\s)/.source.replace(/<>/g,(function(){return r})).replace(/<>/g,(function(){return"(?:"+a+"|"+o+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:i(/\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?(?:[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?))?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?/.source),lookbehind:!0,alias:"number"},boolean:{pattern:i(/false|true/.source,"i"),lookbehind:!0,alias:"important"},null:{pattern:i(/null|~/.source,"i"),lookbehind:!0,alias:"important"},string:{pattern:i(o),lookbehind:!0,greedy:!0},number:{pattern:i(/[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?|\.inf|\.nan)/.source,"i"),lookbehind:!0},tag:n,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(a),function(e){var t=/(?:\\.|[^\\\n\r]|(?:\n|\r\n?)(?![\r\n]))/.source;function n(e){return e=e.replace(//g,(function(){return t})),RegExp(/((?:^|[^\\])(?:\\{2})*)/.source+"(?:"+e+")")}var r=/(?:\\.|``(?:[^`\r\n]|`(?!`))+``|`[^`\r\n]+`|[^\\|\r\n`])+/.source,a=/\|?__(?:\|__)+\|?(?:(?:\n|\r\n?)|(?![\s\S]))/.source.replace(/__/g,(function(){return r})),o=/\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?(?:\n|\r\n?)/.source;e.languages.markdown=e.languages.extend("markup",{}),e.languages.insertBefore("markdown","prolog",{"front-matter-block":{pattern:/(^(?:\s*[\r\n])?)---(?!.)[\s\S]*?[\r\n]---(?!.)/,lookbehind:!0,greedy:!0,inside:{punctuation:/^---|---$/,"front-matter":{pattern:/\S+(?:\s+\S+)*/,alias:["yaml","language-yaml"],inside:e.languages.yaml}}},blockquote:{pattern:/^>(?:[\t ]*>)*/m,alias:"punctuation"},table:{pattern:RegExp("^"+a+o+"(?:"+a+")*","m"),inside:{"table-data-rows":{pattern:RegExp("^("+a+o+")(?:"+a+")*$"),lookbehind:!0,inside:{"table-data":{pattern:RegExp(r),inside:e.languages.markdown},punctuation:/\|/}},"table-line":{pattern:RegExp("^("+a+")"+o+"$"),lookbehind:!0,inside:{punctuation:/\||:?-{3,}:?/}},"table-header-row":{pattern:RegExp("^"+a+"$"),inside:{"table-header":{pattern:RegExp(r),alias:"important",inside:e.languages.markdown},punctuation:/\|/}}}},code:[{pattern:/((?:^|\n)[ \t]*\n|(?:^|\r\n?)[ \t]*\r\n?)(?: {4}|\t).+(?:(?:\n|\r\n?)(?: {4}|\t).+)*/,lookbehind:!0,alias:"keyword"},{pattern:/^```[\s\S]*?^```$/m,greedy:!0,inside:{"code-block":{pattern:/^(```.*(?:\n|\r\n?))[\s\S]+?(?=(?:\n|\r\n?)^```$)/m,lookbehind:!0},"code-language":{pattern:/^(```).+/,lookbehind:!0},punctuation:/```/}}],title:[{pattern:/\S.*(?:\n|\r\n?)(?:==+|--+)(?=[ \t]*$)/m,alias:"important",inside:{punctuation:/==+$|--+$/}},{pattern:/(^\s*)#.+/m,lookbehind:!0,alias:"important",inside:{punctuation:/^#+|#+$/}}],hr:{pattern:/(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,lookbehind:!0,alias:"punctuation"},list:{pattern:/(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,lookbehind:!0,alias:"punctuation"},"url-reference":{pattern:/!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,inside:{variable:{pattern:/^(!?\[)[^\]]+/,lookbehind:!0},string:/(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,punctuation:/^[\[\]!:]|[<>]/},alias:"url"},bold:{pattern:n(/\b__(?:(?!_)|_(?:(?!_))+_)+__\b|\*\*(?:(?!\*)|\*(?:(?!\*))+\*)+\*\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^..)[\s\S]+(?=..$)/,lookbehind:!0,inside:{}},punctuation:/\*\*|__/}},italic:{pattern:n(/\b_(?:(?!_)|__(?:(?!_))+__)+_\b|\*(?:(?!\*)|\*\*(?:(?!\*))+\*\*)+\*/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^.)[\s\S]+(?=.$)/,lookbehind:!0,inside:{}},punctuation:/[*_]/}},strike:{pattern:n(/(~~?)(?:(?!~))+\2/.source),lookbehind:!0,greedy:!0,inside:{content:{pattern:/(^~~?)[\s\S]+(?=\1$)/,lookbehind:!0,inside:{}},punctuation:/~~?/}},"code-snippet":{pattern:/(^|[^\\`])(?:``[^`\r\n]+(?:`[^`\r\n]+)*``(?!`)|`[^`\r\n]+`(?!`))/,lookbehind:!0,greedy:!0,alias:["code","keyword"]},url:{pattern:n(/!?\[(?:(?!\]))+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)|[ \t]?\[(?:(?!\]))+\])/.source),lookbehind:!0,greedy:!0,inside:{operator:/^!/,content:{pattern:/(^\[)[^\]]+(?=\])/,lookbehind:!0,inside:{}},variable:{pattern:/(^\][ \t]?\[)[^\]]+(?=\]$)/,lookbehind:!0},url:{pattern:/(^\]\()[^\s)]+/,lookbehind:!0},string:{pattern:/(^[ \t]+)"(?:\\.|[^"\\])*"(?=\)$)/,lookbehind:!0}}}}),["url","bold","italic","strike"].forEach((function(t){["url","bold","italic","strike","code-snippet"].forEach((function(n){t!==n&&(e.languages.markdown[t].inside.content.inside[n]=e.languages.markdown[n])}))})),e.hooks.add("after-tokenize",(function(e){"markdown"!==e.language&&"md"!==e.language||function e(t){if(t&&"string"!=typeof t)for(var n=0,r=t.length;n",quot:'"'},s=String.fromCodePoint||String.fromCharCode;e.languages.md=e.languages.markdown}(a),a.languages.graphql={comment:/#.*/,description:{pattern:/(?:"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*")(?=\s*[a-z_])/i,greedy:!0,alias:"string",inside:{"language-markdown":{pattern:/(^"(?:"")?)(?!\1)[\s\S]+(?=\1$)/,lookbehind:!0,inside:a.languages.markdown}}},string:{pattern:/"""(?:[^"]|(?!""")")*"""|"(?:\\.|[^\\"\r\n])*"/,greedy:!0},number:/(?:\B-|\b)\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,boolean:/\b(?:false|true)\b/,variable:/\$[a-z_]\w*/i,directive:{pattern:/@[a-z_]\w*/i,alias:"function"},"attr-name":{pattern:/\b[a-z_]\w*(?=\s*(?:\((?:[^()"]|"(?:\\.|[^\\"\r\n])*")*\))?:)/i,greedy:!0},"atom-input":{pattern:/\b[A-Z]\w*Input\b/,alias:"class-name"},scalar:/\b(?:Boolean|Float|ID|Int|String)\b/,constant:/\b[A-Z][A-Z_\d]*\b/,"class-name":{pattern:/(\b(?:enum|implements|interface|on|scalar|type|union)\s+|&\s*|:\s*|\[)[A-Z_]\w*/,lookbehind:!0},fragment:{pattern:/(\bfragment\s+|\.{3}\s*(?!on\b))[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-mutation":{pattern:/(\bmutation\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},"definition-query":{pattern:/(\bquery\s+)[a-zA-Z_]\w*/,lookbehind:!0,alias:"function"},keyword:/\b(?:directive|enum|extend|fragment|implements|input|interface|mutation|on|query|repeatable|scalar|schema|subscription|type|union)\b/,operator:/[!=|&]|\.{3}/,"property-query":/\w+(?=\s*\()/,object:/\w+(?=\s*\{)/,punctuation:/[!(){}\[\]:=,]/,property:/\w+/},a.hooks.add("after-tokenize",(function(e){if("graphql"===e.language)for(var t=e.tokens.filter((function(e){return"string"!=typeof e&&"comment"!==e.type&&"scalar"!==e.type})),n=0;n0)){var l=p(/^\{$/,/^\}$/);if(-1===l)continue;for(var s=n;s=0&&f(u,"variable-input")}}}}function c(e){return t[n+e]}function d(e,t){t=t||0;for(var n=0;n?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|DIV|ILIKE|IN|IS|LIKE|NOT|OR|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,punctuation:/[;[\]()`,.]/},function(e){var t=e.languages.javascript["template-string"],n=t.pattern.source,r=t.inside.interpolation,a=r.inside["interpolation-punctuation"],o=r.pattern.source;function i(t,r){if(e.languages[t])return{pattern:RegExp("((?:"+r+")\\s*)"+n),lookbehind:!0,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},"embedded-code":{pattern:/[\s\S]+/,alias:t}}}}function l(e,t){return"___"+t.toUpperCase()+"_"+e+"___"}function s(t,n,r){var a={code:t,grammar:n,language:r};return e.hooks.run("before-tokenize",a),a.tokens=e.tokenize(a.code,a.grammar),e.hooks.run("after-tokenize",a),a.tokens}function u(t){var n={};n["interpolation-punctuation"]=a;var o=e.tokenize(t,n);if(3===o.length){var i=[1,1];i.push.apply(i,s(o[1],e.languages.javascript,"javascript")),o.splice.apply(o,i)}return new e.Token("interpolation",o,r.alias,t)}function c(t,n,r){var a=e.tokenize(t,{interpolation:{pattern:RegExp(o),lookbehind:!0}}),i=0,c={},d=s(a.map((function(e){if("string"==typeof e)return e;for(var n,a=e.content;-1!==t.indexOf(n=l(i++,r)););return c[n]=a,n})).join(""),n,r),p=Object.keys(c);return i=0,function e(t){for(var n=0;n=p.length)return;var r=t[n];if("string"==typeof r||"string"==typeof r.content){var a=p[i],o="string"==typeof r?r:r.content,l=o.indexOf(a);if(-1!==l){++i;var s=o.substring(0,l),d=u(c[a]),f=o.substring(l+a.length),m=[];if(s&&m.push(s),m.push(d),f){var h=[f];e(h),m.push.apply(m,h)}"string"==typeof r?(t.splice.apply(t,[n,1].concat(m)),n+=m.length-1):r.content=m}}else{var g=r.content;Array.isArray(g)?e(g):e([g])}}}(d),new e.Token(r,d,"language-"+r,t)}e.languages.javascript["template-string"]=[i("css",/\b(?:styled(?:\([^)]*\))?(?:\s*\.\s*\w+(?:\([^)]*\))*)*|css(?:\s*\.\s*(?:global|resolve))?|createGlobalStyle|keyframes)/.source),i("html",/\bhtml|\.\s*(?:inner|outer)HTML\s*\+?=/.source),i("svg",/\bsvg/.source),i("markdown",/\b(?:markdown|md)/.source),i("graphql",/\b(?:gql|graphql(?:\s*\.\s*experimental)?)/.source),i("sql",/\bsql/.source),t].filter(Boolean);var d={javascript:!0,js:!0,typescript:!0,ts:!0,jsx:!0,tsx:!0};function p(e){return"string"==typeof e?e:Array.isArray(e)?e.map(p).join(""):p(e.content)}e.hooks.add("after-tokenize",(function(t){t.language in d&&function t(n){for(var r=0,a=n.length;r]|<(?:[^<>]|<[^<>]*>)*>)*>)?/,lookbehind:!0,greedy:!0,inside:null},builtin:/\b(?:Array|Function|Promise|any|boolean|console|never|number|string|symbol|unknown)\b/}),e.languages.typescript.keyword.push(/\b(?:abstract|declare|is|keyof|readonly|require)\b/,/\b(?:asserts|infer|interface|module|namespace|type)\b(?=\s*(?:[{_$a-zA-Z\xA0-\uFFFF]|$))/,/\btype\b(?=\s*(?:[\{*]|$))/),delete e.languages.typescript.parameter,delete e.languages.typescript["literal-property"];var t=e.languages.extend("typescript",{});delete t["class-name"],e.languages.typescript["class-name"].inside=t,e.languages.insertBefore("typescript","function",{decorator:{pattern:/@[$\w\xA0-\uFFFF]+/,inside:{at:{pattern:/^@/,alias:"operator"},function:/^[\s\S]+/}},"generic-function":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>(?=\s*\()/,greedy:!0,inside:{function:/^#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:t}}}}),e.languages.ts=e.languages.typescript}(a),function(e){function t(e,t){return RegExp(e.replace(//g,(function(){return/(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*/.source})),t)}e.languages.insertBefore("javascript","function-variable",{"method-variable":{pattern:RegExp("(\\.\\s*)"+e.languages.javascript["function-variable"].pattern.source),lookbehind:!0,alias:["function-variable","method","function","property-access"]}}),e.languages.insertBefore("javascript","function",{method:{pattern:RegExp("(\\.\\s*)"+e.languages.javascript.function.source),lookbehind:!0,alias:["function","property-access"]}}),e.languages.insertBefore("javascript","constant",{"known-class-name":[{pattern:/\b(?:(?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)?Array|ArrayBuffer|BigInt|Boolean|DataView|Date|Error|Function|Intl|JSON|(?:Weak)?(?:Map|Set)|Math|Number|Object|Promise|Proxy|Reflect|RegExp|String|Symbol|WebAssembly)\b/,alias:"class-name"},{pattern:/\b(?:[A-Z]\w*)Error\b/,alias:"class-name"}]}),e.languages.insertBefore("javascript","keyword",{imports:{pattern:t(/(\bimport\b\s*)(?:(?:\s*,\s*(?:\*\s*as\s+|\{[^{}]*\}))?|\*\s*as\s+|\{[^{}]*\})(?=\s*\bfrom\b)/.source),lookbehind:!0,inside:e.languages.javascript},exports:{pattern:t(/(\bexport\b\s*)(?:\*(?:\s*as\s+)?(?=\s*\bfrom\b)|\{[^{}]*\})/.source),lookbehind:!0,inside:e.languages.javascript}}),e.languages.javascript.keyword.unshift({pattern:/\b(?:as|default|export|from|import)\b/,alias:"module"},{pattern:/\b(?:await|break|catch|continue|do|else|finally|for|if|return|switch|throw|try|while|yield)\b/,alias:"control-flow"},{pattern:/\bnull\b/,alias:["null","nil"]},{pattern:/\bundefined\b/,alias:"nil"}),e.languages.insertBefore("javascript","operator",{spread:{pattern:/\.{3}/,alias:"operator"},arrow:{pattern:/=>/,alias:"operator"}}),e.languages.insertBefore("javascript","punctuation",{"property-access":{pattern:t(/(\.\s*)#?/.source),lookbehind:!0},"maybe-class-name":{pattern:/(^|[^$\w\xA0-\uFFFF])[A-Z][$\w\xA0-\uFFFF]+/,lookbehind:!0},dom:{pattern:/\b(?:document|(?:local|session)Storage|location|navigator|performance|window)\b/,alias:"variable"},console:{pattern:/\bconsole(?=\s*\.)/,alias:"class-name"}});for(var n=["function","function-variable","method","method-variable","property-access"],r=0;r*\.{3}(?:[^{}]|)*\})/.source;function o(e,t){return e=e.replace(//g,(function(){return n})).replace(//g,(function(){return r})).replace(//g,(function(){return a})),RegExp(e,t)}a=o(a).source,e.languages.jsx=e.languages.extend("markup",t),e.languages.jsx.tag.pattern=o(/<\/?(?:[\w.:-]+(?:+(?:[\w.:$-]+(?:=(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s{'"/>=]+|))?|))**\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(//.source),inside:e.languages.jsx}},e.languages.jsx.tag),e.languages.insertBefore("inside","special-attr",{script:{pattern:o(/=/.source),alias:"language-javascript",inside:{"script-punctuation":{pattern:/^=(?=\{)/,alias:"punctuation"},rest:e.languages.jsx}}},e.languages.jsx.tag);var i=function(e){return e?"string"==typeof e?e:"string"==typeof e.content?e.content:e.content.map(i).join(""):""},l=function(t){for(var n=[],r=0;r0&&n[n.length-1].tagName===i(a.content[0].content[1])&&n.pop():"/>"===a.content[a.content.length-1].content||n.push({tagName:i(a.content[0].content[1]),openedBraces:0}):n.length>0&&"punctuation"===a.type&&"{"===a.content?n[n.length-1].openedBraces++:n.length>0&&n[n.length-1].openedBraces>0&&"punctuation"===a.type&&"}"===a.content?n[n.length-1].openedBraces--:o=!0),(o||"string"==typeof a)&&n.length>0&&0===n[n.length-1].openedBraces){var s=i(a);r0&&("string"==typeof t[r-1]||"plain-text"===t[r-1].type)&&(s=i(t[r-1])+s,t.splice(r-1,1),r--),t[r]=new e.Token("plain-text",s,null,s)}a.content&&"string"!=typeof a.content&&l(a.content)}};e.hooks.add("after-tokenize",(function(e){"jsx"!==e.language&&"tsx"!==e.language||l(e.tokens)}))}(a),function(e){e.languages.diff={coord:[/^(?:\*{3}|-{3}|\+{3}).*$/m,/^@@.*@@$/m,/^\d.*$/m]};var t={"deleted-sign":"-","deleted-arrow":"<","inserted-sign":"+","inserted-arrow":">",unchanged:" ",diff:"!"};Object.keys(t).forEach((function(n){var r=t[n],a=[];/^\w+$/.test(n)||a.push(/\w+/.exec(n)[0]),"diff"===n&&a.push("bold"),e.languages.diff[n]={pattern:RegExp("^(?:["+r+"].*(?:\r\n?|\n|(?![\\s\\S])))+","m"),alias:a,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(n)[0]}}}})),Object.defineProperty(e.languages.diff,"PREFIXES",{value:t})}(a),a.languages.git={comment:/^#.*/m,deleted:/^[-\u2013].*/m,inserted:/^\+.*/m,string:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,command:{pattern:/^.*\$ git .*$/m,inside:{parameter:/\s--?\w+/}},coord:/^@@.*@@$/m,"commit-sha1":/^commit \w{40}$/m},a.languages.go=a.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"|`[^`]*`/,lookbehind:!0,greedy:!0},keyword:/\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/,boolean:/\b(?:_|false|iota|nil|true)\b/,number:[/\b0(?:b[01_]+|o[0-7_]+)i?\b/i,/\b0x(?:[a-f\d_]+(?:\.[a-f\d_]*)?|\.[a-f\d_]+)(?:p[+-]?\d+(?:_\d+)*)?i?(?!\w)/i,/(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?[\d_]+)?i?(?!\w)/i],operator:/[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,builtin:/\b(?:append|bool|byte|cap|close|complex|complex(?:64|128)|copy|delete|error|float(?:32|64)|u?int(?:8|16|32|64)?|imag|len|make|new|panic|print(?:ln)?|real|recover|rune|string|uintptr)\b/}),a.languages.insertBefore("go","string",{char:{pattern:/'(?:\\.|[^'\\\r\n]){0,10}'/,greedy:!0}}),delete a.languages.go["class-name"],function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,r,a,o){if(n.language===r){var i=n.tokenStack=[];n.code=n.code.replace(a,(function(e){if("function"==typeof o&&!o(e))return e;for(var a,l=i.length;-1!==n.code.indexOf(a=t(r,l));)++l;return i[l]=e,a})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,r){if(n.language===r&&n.tokenStack){n.grammar=e.languages[r];var a=0,o=Object.keys(n.tokenStack);!function i(l){for(var s=0;s=o.length);s++){var u=l[s];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=o[a],d=n.tokenStack[c],p="string"==typeof u?u:u.content,f=t(r,c),m=p.indexOf(f);if(m>-1){++a;var h=p.substring(0,m),g=new e.Token(r,e.tokenize(d,n.grammar),"language-"+r,d),b=p.substring(m+f.length),v=[];h&&v.push.apply(v,i([h])),v.push(g),b&&v.push.apply(v,i([b])),"string"==typeof u?l.splice.apply(l,[s,1].concat(v)):u.content=v}}else u.content&&i(u.content)}return l}(n.tokens)}}}})}(a),function(e){e.languages.handlebars={comment:/\{\{![\s\S]*?\}\}/,delimiter:{pattern:/^\{\{\{?|\}\}\}?$/,alias:"punctuation"},string:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,number:/\b0x[\dA-Fa-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[Ee][+-]?\d+)?/,boolean:/\b(?:false|true)\b/,block:{pattern:/^(\s*(?:~\s*)?)[#\/]\S+?(?=\s*(?:~\s*)?$|\s)/,lookbehind:!0,alias:"keyword"},brackets:{pattern:/\[[^\]]+\]/,inside:{punctuation:/\[|\]/,variable:/[\s\S]+/}},punctuation:/[!"#%&':()*+,.\/;<=>@\[\\\]^`{|}~]/,variable:/[^!"#%&'()*+,\/;<=>@\[\\\]^`{|}~\s]+/},e.hooks.add("before-tokenize",(function(t){e.languages["markup-templating"].buildPlaceholders(t,"handlebars",/\{\{\{[\s\S]+?\}\}\}|\{\{[\s\S]+?\}\}/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"handlebars")})),e.languages.hbs=e.languages.handlebars}(a),a.languages.json={property:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,lookbehind:!0,greedy:!0},comment:{pattern:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},number:/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,punctuation:/[{}[\],]/,operator:/:/,boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"}},a.languages.webmanifest=a.languages.json,a.languages.less=a.languages.extend("css",{comment:[/\/\*[\s\S]*?\*\//,{pattern:/(^|[^\\])\/\/.*/,lookbehind:!0}],atrule:{pattern:/@[\w-](?:\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{punctuation:/[:()]/}},selector:{pattern:/(?:@\{[\w-]+\}|[^{};\s@])(?:@\{[\w-]+\}|\((?:[^(){}]|\([^(){}]*\))*\)|[^(){};@\s]|\s+(?!\s))*?(?=\s*\{)/,inside:{variable:/@+[\w-]+/}},property:/(?:@\{[\w-]+\}|[\w-])+(?:\+_?)?(?=\s*:)/,operator:/[+\-*\/]/}),a.languages.insertBefore("less","property",{variable:[{pattern:/@[\w-]+\s*:/,inside:{punctuation:/:/}},/@@?[\w-]+/],"mixin-usage":{pattern:/([{;]\s*)[.#](?!\d)[\w-].*?(?=[(;])/,lookbehind:!0,alias:"function"}}),a.languages.makefile={comment:{pattern:/(^|[^\\])#(?:\\(?:\r\n|[\s\S])|[^\\\r\n])*/,lookbehind:!0},string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"builtin-target":{pattern:/\.[A-Z][^:#=\s]+(?=\s*:(?!=))/,alias:"builtin"},target:{pattern:/^(?:[^:=\s]|[ \t]+(?![\s:]))+(?=\s*:(?!=))/m,alias:"symbol",inside:{variable:/\$+(?:(?!\$)[^(){}:#=\s]+|(?=[({]))/}},variable:/\$+(?:(?!\$)[^(){}:#=\s]+|\([@*%<^+?][DF]\)|(?=[({]))/,keyword:/-include\b|\b(?:define|else|endef|endif|export|ifn?def|ifn?eq|include|override|private|sinclude|undefine|unexport|vpath)\b/,function:{pattern:/(\()(?:abspath|addsuffix|and|basename|call|dir|error|eval|file|filter(?:-out)?|findstring|firstword|flavor|foreach|guile|if|info|join|lastword|load|notdir|or|origin|patsubst|realpath|shell|sort|strip|subst|suffix|value|warning|wildcard|word(?:list|s)?)(?=[ \t])/,lookbehind:!0},operator:/(?:::|[?:+!])?=|[|@]/,punctuation:/[:;(){}]/},a.languages.objectivec=a.languages.extend("c",{string:{pattern:/@?"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},keyword:/\b(?:asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|in|inline|int|long|register|return|self|short|signed|sizeof|static|struct|super|switch|typedef|typeof|union|unsigned|void|volatile|while)\b|(?:@interface|@end|@implementation|@protocol|@class|@public|@protected|@private|@property|@try|@catch|@finally|@throw|@synthesize|@dynamic|@selector)\b/,operator:/-[->]?|\+\+?|!=?|<>?=?|==?|&&?|\|\|?|[~^%?*\/@]/}),delete a.languages.objectivec["class-name"],a.languages.objc=a.languages.objectivec,a.languages.ocaml={comment:{pattern:/\(\*[\s\S]*?\*\)/,greedy:!0},char:{pattern:/'(?:[^\\\r\n']|\\(?:.|[ox]?[0-9a-f]{1,3}))'/i,greedy:!0},string:[{pattern:/"(?:\\(?:[\s\S]|\r\n)|[^\\\r\n"])*"/,greedy:!0},{pattern:/\{([a-z_]*)\|[\s\S]*?\|\1\}/,greedy:!0}],number:[/\b(?:0b[01][01_]*|0o[0-7][0-7_]*)\b/i,/\b0x[a-f0-9][a-f0-9_]*(?:\.[a-f0-9_]*)?(?:p[+-]?\d[\d_]*)?(?!\w)/i,/\b\d[\d_]*(?:\.[\d_]*)?(?:e[+-]?\d[\d_]*)?(?!\w)/i],directive:{pattern:/\B#\w+/,alias:"property"},label:{pattern:/\B~\w+/,alias:"property"},"type-variable":{pattern:/\B'\w+/,alias:"function"},variant:{pattern:/`\w+/,alias:"symbol"},keyword:/\b(?:as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|match|method|module|mutable|new|nonrec|object|of|open|private|rec|sig|struct|then|to|try|type|val|value|virtual|when|where|while|with)\b/,boolean:/\b(?:false|true)\b/,"operator-like-punctuation":{pattern:/\[[<>|]|[>|]\]|\{<|>\}/,alias:"punctuation"},operator:/\.[.~]|:[=>]|[=<>@^|&+\-*\/$%!?~][!$%&*+\-.\/:<=>?@^|~]*|\b(?:and|asr|land|lor|lsl|lsr|lxor|mod|or)\b/,punctuation:/;;|::|[(){}\[\].,:;#]|\b_\b/},a.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},a.languages.python["string-interpolation"].inside.interpolation.inside.rest=a.languages.python,a.languages.py=a.languages.python,a.languages.reason=a.languages.extend("clike",{string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},"class-name":/\b[A-Z]\w*/,keyword:/\b(?:and|as|assert|begin|class|constraint|do|done|downto|else|end|exception|external|for|fun|function|functor|if|in|include|inherit|initializer|lazy|let|method|module|mutable|new|nonrec|object|of|open|or|private|rec|sig|struct|switch|then|to|try|type|val|virtual|when|while|with)\b/,operator:/\.{3}|:[:=]|\|>|->|=(?:==?|>)?|<=?|>=?|[|^?'#!~`]|[+\-*\/]\.?|\b(?:asr|land|lor|lsl|lsr|lxor|mod)\b/}),a.languages.insertBefore("reason","class-name",{char:{pattern:/'(?:\\x[\da-f]{2}|\\o[0-3][0-7][0-7]|\\\d{3}|\\.|[^'\\\r\n])'/,greedy:!0},constructor:/\b[A-Z]\w*\b(?!\s*\.)/,label:{pattern:/\b[a-z]\w*(?=::)/,alias:"symbol"}}),delete a.languages.reason.function,function(e){e.languages.sass=e.languages.extend("css",{comment:{pattern:/^([ \t]*)\/[\/*].*(?:(?:\r?\n|\r)\1[ \t].+)*/m,lookbehind:!0,greedy:!0}}),e.languages.insertBefore("sass","atrule",{"atrule-line":{pattern:/^(?:[ \t]*)[@+=].+/m,greedy:!0,inside:{atrule:/(?:@[\w-]+|[+=])/}}}),delete e.languages.sass.atrule;var t=/\$[-\w]+|#\{\$[-\w]+\}/,n=[/[+*\/%]|[=!]=|<=?|>=?|\b(?:and|not|or)\b/,{pattern:/(\s)-(?=\s)/,lookbehind:!0}];e.languages.insertBefore("sass","property",{"variable-line":{pattern:/^[ \t]*\$.+/m,greedy:!0,inside:{punctuation:/:/,variable:t,operator:n}},"property-line":{pattern:/^[ \t]*(?:[^:\s]+ *:.*|:[^:\s].*)/m,greedy:!0,inside:{property:[/[^:\s]+(?=\s*:)/,{pattern:/(:)[^:\s]+/,lookbehind:!0}],punctuation:/:/,variable:t,operator:n,important:e.languages.sass.important}}}),delete e.languages.sass.property,delete e.languages.sass.important,e.languages.insertBefore("sass","punctuation",{selector:{pattern:/^([ \t]*)\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*(?:,(?:\r?\n|\r)\1[ \t]+\S(?:,[^,\r\n]+|[^,\r\n]*)(?:,[^,\r\n]+)*)*/m,lookbehind:!0,greedy:!0}})}(a),a.languages.scss=a.languages.extend("css",{comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},atrule:{pattern:/@[\w-](?:\([^()]+\)|[^()\s]|\s+(?!\s))*?(?=\s+[{;])/,inside:{rule:/@[\w-]+/}},url:/(?:[-a-z]+-)?url(?=\()/i,selector:{pattern:/(?=\S)[^@;{}()]?(?:[^@;{}()\s]|\s+(?!\s)|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}][^:{}]*[:{][^}]))/,inside:{parent:{pattern:/&/,alias:"important"},placeholder:/%[-\w]+/,variable:/\$[-\w]+|#\{\$[-\w]+\}/}},property:{pattern:/(?:[-\w]|\$[-\w]|#\{\$[-\w]+\})+(?=\s*:)/,inside:{variable:/\$[-\w]+|#\{\$[-\w]+\}/}}}),a.languages.insertBefore("scss","atrule",{keyword:[/@(?:content|debug|each|else(?: if)?|extend|for|forward|function|if|import|include|mixin|return|use|warn|while)\b/i,{pattern:/( )(?:from|through)(?= )/,lookbehind:!0}]}),a.languages.insertBefore("scss","important",{variable:/\$[-\w]+|#\{\$[-\w]+\}/}),a.languages.insertBefore("scss","function",{"module-modifier":{pattern:/\b(?:as|hide|show|with)\b/i,alias:"keyword"},placeholder:{pattern:/%[-\w]+/,alias:"selector"},statement:{pattern:/\B!(?:default|optional)\b/i,alias:"keyword"},boolean:/\b(?:false|true)\b/,null:{pattern:/\bnull\b/,alias:"keyword"},operator:{pattern:/(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|not|or)(?=\s)/,lookbehind:!0}}),a.languages.scss.atrule.inside.rest=a.languages.scss,function(e){var t={pattern:/(\b\d+)(?:%|[a-z]+)/,lookbehind:!0},n={pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0},r={comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0},url:{pattern:/\burl\((["']?).*?\1\)/i,greedy:!0},string:{pattern:/("|')(?:(?!\1)[^\\\r\n]|\\(?:\r\n|[\s\S]))*\1/,greedy:!0},interpolation:null,func:null,important:/\B!(?:important|optional)\b/i,keyword:{pattern:/(^|\s+)(?:(?:else|for|if|return|unless)(?=\s|$)|@[\w-]+)/,lookbehind:!0},hexcode:/#[\da-f]{3,6}/i,color:[/\b(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)\b/i,{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,boolean:/\b(?:false|true)\b/,operator:[/~|[+!\/%<>?=]=?|[-:]=|\*[*=]?|\.{2,3}|&&|\|\||\B-\B|\b(?:and|in|is(?: a| defined| not|nt)?|not|or)\b/],number:n,punctuation:/[{}()\[\];:,]/};r.interpolation={pattern:/\{[^\r\n}:]+\}/,alias:"variable",inside:{delimiter:{pattern:/^\{|\}$/,alias:"punctuation"},rest:r}},r.func={pattern:/[\w-]+\([^)]*\).*/,inside:{function:/^[^(]+/,rest:r}},e.languages.stylus={"atrule-declaration":{pattern:/(^[ \t]*)@.+/m,lookbehind:!0,inside:{atrule:/^@[\w-]+/,rest:r}},"variable-declaration":{pattern:/(^[ \t]*)[\w$-]+\s*.?=[ \t]*(?:\{[^{}]*\}|\S.*|$)/m,lookbehind:!0,inside:{variable:/^\S+/,rest:r}},statement:{pattern:/(^[ \t]*)(?:else|for|if|return|unless)[ \t].+/m,lookbehind:!0,inside:{keyword:/^\S+/,rest:r}},"property-declaration":{pattern:/((?:^|\{)([ \t]*))(?:[\w-]|\{[^}\r\n]+\})+(?:\s*:\s*|[ \t]+)(?!\s)[^{\r\n]*(?:;|[^{\r\n,]$(?!(?:\r?\n|\r)(?:\{|\2[ \t])))/m,lookbehind:!0,inside:{property:{pattern:/^[^\s:]+/,inside:{interpolation:r.interpolation}},rest:r}},selector:{pattern:/(^[ \t]*)(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)(?:(?:\r?\n|\r)(?:\1(?:(?=\S)(?:[^{}\r\n:()]|::?[\w-]+(?:\([^)\r\n]*\)|(?![\w-]))|\{[^}\r\n]+\})+)))*(?:,$|\{|(?=(?:\r?\n|\r)(?:\{|\1[ \t])))/m,lookbehind:!0,inside:{interpolation:r.interpolation,comment:r.comment,punctuation:/[{},]/}},func:r.func,string:r.string,comment:{pattern:/(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,lookbehind:!0,greedy:!0},interpolation:r.interpolation,punctuation:/[{}()\[\];:.]/}}(a),function(e){var t=e.util.clone(e.languages.typescript);e.languages.tsx=e.languages.extend("jsx",t),delete e.languages.tsx.parameter,delete e.languages.tsx["literal-property"];var n=e.languages.tsx.tag;n.pattern=RegExp(/(^|[^\w$]|(?=<\/))/.source+"(?:"+n.pattern.source+")",n.pattern.flags),n.lookbehind=!0}(a),a.languages.wasm={comment:[/\(;[\s\S]*?;\)/,{pattern:/;;.*/,greedy:!0}],string:{pattern:/"(?:\\[\s\S]|[^"\\])*"/,greedy:!0},keyword:[{pattern:/\b(?:align|offset)=/,inside:{operator:/=/}},{pattern:/\b(?:(?:f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|neg?|nearest|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|sqrt|store(?:8|16|32)?|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))?|memory\.(?:grow|size))\b/,inside:{punctuation:/\./}},/\b(?:anyfunc|block|br(?:_if|_table)?|call(?:_indirect)?|data|drop|elem|else|end|export|func|get_(?:global|local)|global|if|import|local|loop|memory|module|mut|nop|offset|param|result|return|select|set_(?:global|local)|start|table|tee_local|then|type|unreachable)\b/],variable:/\$[\w!#$%&'*+\-./:<=>?@\\^`|~]+/,number:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/,punctuation:/[()]/};const o=a},9901:e=>{e.exports&&(e.exports={core:{meta:{path:"components/prism-core.js",option:"mandatory"},core:"Core"},themes:{meta:{path:"themes/{id}.css",link:"index.html?theme={id}",exclusive:!0},prism:{title:"Default",option:"default"},"prism-dark":"Dark","prism-funky":"Funky","prism-okaidia":{title:"Okaidia",owner:"ocodia"},"prism-twilight":{title:"Twilight",owner:"remybach"},"prism-coy":{title:"Coy",owner:"tshedor"},"prism-solarizedlight":{title:"Solarized Light",owner:"hectormatos2011 "},"prism-tomorrow":{title:"Tomorrow Night",owner:"Rosey"}},languages:{meta:{path:"components/prism-{id}",noCSS:!0,examplesPath:"examples/prism-{id}",addCheckAll:!0},markup:{title:"Markup",alias:["html","xml","svg","mathml","ssml","atom","rss"],aliasTitles:{html:"HTML",xml:"XML",svg:"SVG",mathml:"MathML",ssml:"SSML",atom:"Atom",rss:"RSS"},option:"default"},css:{title:"CSS",option:"default",modify:"markup"},clike:{title:"C-like",option:"default"},javascript:{title:"JavaScript",require:"clike",modify:"markup",optional:"regex",alias:"js",option:"default"},abap:{title:"ABAP",owner:"dellagustin"},abnf:{title:"ABNF",owner:"RunDevelopment"},actionscript:{title:"ActionScript",require:"javascript",modify:"markup",owner:"Golmote"},ada:{title:"Ada",owner:"Lucretia"},agda:{title:"Agda",owner:"xy-ren"},al:{title:"AL",owner:"RunDevelopment"},antlr4:{title:"ANTLR4",alias:"g4",owner:"RunDevelopment"},apacheconf:{title:"Apache Configuration",owner:"GuiTeK"},apex:{title:"Apex",require:["clike","sql"],owner:"RunDevelopment"},apl:{title:"APL",owner:"ngn"},applescript:{title:"AppleScript",owner:"Golmote"},aql:{title:"AQL",owner:"RunDevelopment"},arduino:{title:"Arduino",require:"cpp",alias:"ino",owner:"dkern"},arff:{title:"ARFF",owner:"Golmote"},armasm:{title:"ARM Assembly",alias:"arm-asm",owner:"RunDevelopment"},arturo:{title:"Arturo",alias:"art",optional:["bash","css","javascript","markup","markdown","sql"],owner:"drkameleon"},asciidoc:{alias:"adoc",title:"AsciiDoc",owner:"Golmote"},aspnet:{title:"ASP.NET (C#)",require:["markup","csharp"],owner:"nauzilus"},asm6502:{title:"6502 Assembly",owner:"kzurawel"},asmatmel:{title:"Atmel AVR Assembly",owner:"cerkit"},autohotkey:{title:"AutoHotkey",owner:"aviaryan"},autoit:{title:"AutoIt",owner:"Golmote"},avisynth:{title:"AviSynth",alias:"avs",owner:"Zinfidel"},"avro-idl":{title:"Avro IDL",alias:"avdl",owner:"RunDevelopment"},awk:{title:"AWK",alias:"gawk",aliasTitles:{gawk:"GAWK"},owner:"RunDevelopment"},bash:{title:"Bash",alias:["sh","shell"],aliasTitles:{sh:"Shell",shell:"Shell"},owner:"zeitgeist87"},basic:{title:"BASIC",owner:"Golmote"},batch:{title:"Batch",owner:"Golmote"},bbcode:{title:"BBcode",alias:"shortcode",aliasTitles:{shortcode:"Shortcode"},owner:"RunDevelopment"},bbj:{title:"BBj",owner:"hyyan"},bicep:{title:"Bicep",owner:"johnnyreilly"},birb:{title:"Birb",require:"clike",owner:"Calamity210"},bison:{title:"Bison",require:"c",owner:"Golmote"},bnf:{title:"BNF",alias:"rbnf",aliasTitles:{rbnf:"RBNF"},owner:"RunDevelopment"},bqn:{title:"BQN",owner:"yewscion"},brainfuck:{title:"Brainfuck",owner:"Golmote"},brightscript:{title:"BrightScript",owner:"RunDevelopment"},bro:{title:"Bro",owner:"wayward710"},bsl:{title:"BSL (1C:Enterprise)",alias:"oscript",aliasTitles:{oscript:"OneScript"},owner:"Diversus23"},c:{title:"C",require:"clike",owner:"zeitgeist87"},csharp:{title:"C#",require:"clike",alias:["cs","dotnet"],owner:"mvalipour"},cpp:{title:"C++",require:"c",owner:"zeitgeist87"},cfscript:{title:"CFScript",require:"clike",alias:"cfc",owner:"mjclemente"},chaiscript:{title:"ChaiScript",require:["clike","cpp"],owner:"RunDevelopment"},cil:{title:"CIL",owner:"sbrl"},cilkc:{title:"Cilk/C",require:"c",alias:"cilk-c",owner:"OpenCilk"},cilkcpp:{title:"Cilk/C++",require:"cpp",alias:["cilk-cpp","cilk"],owner:"OpenCilk"},clojure:{title:"Clojure",owner:"troglotit"},cmake:{title:"CMake",owner:"mjrogozinski"},cobol:{title:"COBOL",owner:"RunDevelopment"},coffeescript:{title:"CoffeeScript",require:"javascript",alias:"coffee",owner:"R-osey"},concurnas:{title:"Concurnas",alias:"conc",owner:"jasontatton"},csp:{title:"Content-Security-Policy",owner:"ScottHelme"},cooklang:{title:"Cooklang",owner:"ahue"},coq:{title:"Coq",owner:"RunDevelopment"},crystal:{title:"Crystal",require:"ruby",owner:"MakeNowJust"},"css-extras":{title:"CSS Extras",require:"css",modify:"css",owner:"milesj"},csv:{title:"CSV",owner:"RunDevelopment"},cue:{title:"CUE",owner:"RunDevelopment"},cypher:{title:"Cypher",owner:"RunDevelopment"},d:{title:"D",require:"clike",owner:"Golmote"},dart:{title:"Dart",require:"clike",owner:"Golmote"},dataweave:{title:"DataWeave",owner:"machaval"},dax:{title:"DAX",owner:"peterbud"},dhall:{title:"Dhall",owner:"RunDevelopment"},diff:{title:"Diff",owner:"uranusjr"},django:{title:"Django/Jinja2",require:"markup-templating",alias:"jinja2",owner:"romanvm"},"dns-zone-file":{title:"DNS zone file",owner:"RunDevelopment",alias:"dns-zone"},docker:{title:"Docker",alias:"dockerfile",owner:"JustinBeckwith"},dot:{title:"DOT (Graphviz)",alias:"gv",optional:"markup",owner:"RunDevelopment"},ebnf:{title:"EBNF",owner:"RunDevelopment"},editorconfig:{title:"EditorConfig",owner:"osipxd"},eiffel:{title:"Eiffel",owner:"Conaclos"},ejs:{title:"EJS",require:["javascript","markup-templating"],owner:"RunDevelopment",alias:"eta",aliasTitles:{eta:"Eta"}},elixir:{title:"Elixir",owner:"Golmote"},elm:{title:"Elm",owner:"zwilias"},etlua:{title:"Embedded Lua templating",require:["lua","markup-templating"],owner:"RunDevelopment"},erb:{title:"ERB",require:["ruby","markup-templating"],owner:"Golmote"},erlang:{title:"Erlang",owner:"Golmote"},"excel-formula":{title:"Excel Formula",alias:["xlsx","xls"],owner:"RunDevelopment"},fsharp:{title:"F#",require:"clike",owner:"simonreynolds7"},factor:{title:"Factor",owner:"catb0t"},false:{title:"False",owner:"edukisto"},"firestore-security-rules":{title:"Firestore security rules",require:"clike",owner:"RunDevelopment"},flow:{title:"Flow",require:"javascript",owner:"Golmote"},fortran:{title:"Fortran",owner:"Golmote"},ftl:{title:"FreeMarker Template Language",require:"markup-templating",owner:"RunDevelopment"},gml:{title:"GameMaker Language",alias:"gamemakerlanguage",require:"clike",owner:"LiarOnce"},gap:{title:"GAP (CAS)",owner:"RunDevelopment"},gcode:{title:"G-code",owner:"RunDevelopment"},gdscript:{title:"GDScript",owner:"RunDevelopment"},gedcom:{title:"GEDCOM",owner:"Golmote"},gettext:{title:"gettext",alias:"po",owner:"RunDevelopment"},gherkin:{title:"Gherkin",owner:"hason"},git:{title:"Git",owner:"lgiraudel"},glsl:{title:"GLSL",require:"c",owner:"Golmote"},gn:{title:"GN",alias:"gni",owner:"RunDevelopment"},"linker-script":{title:"GNU Linker Script",alias:"ld",owner:"RunDevelopment"},go:{title:"Go",require:"clike",owner:"arnehormann"},"go-module":{title:"Go module",alias:"go-mod",owner:"RunDevelopment"},gradle:{title:"Gradle",require:"clike",owner:"zeabdelkhalek-badido18"},graphql:{title:"GraphQL",optional:"markdown",owner:"Golmote"},groovy:{title:"Groovy",require:"clike",owner:"robfletcher"},haml:{title:"Haml",require:"ruby",optional:["css","css-extras","coffeescript","erb","javascript","less","markdown","scss","textile"],owner:"Golmote"},handlebars:{title:"Handlebars",require:"markup-templating",alias:["hbs","mustache"],aliasTitles:{mustache:"Mustache"},owner:"Golmote"},haskell:{title:"Haskell",alias:"hs",owner:"bholst"},haxe:{title:"Haxe",require:"clike",optional:"regex",owner:"Golmote"},hcl:{title:"HCL",owner:"outsideris"},hlsl:{title:"HLSL",require:"c",owner:"RunDevelopment"},hoon:{title:"Hoon",owner:"matildepark"},http:{title:"HTTP",optional:["csp","css","hpkp","hsts","javascript","json","markup","uri"],owner:"danielgtaylor"},hpkp:{title:"HTTP Public-Key-Pins",owner:"ScottHelme"},hsts:{title:"HTTP Strict-Transport-Security",owner:"ScottHelme"},ichigojam:{title:"IchigoJam",owner:"BlueCocoa"},icon:{title:"Icon",owner:"Golmote"},"icu-message-format":{title:"ICU Message Format",owner:"RunDevelopment"},idris:{title:"Idris",alias:"idr",owner:"KeenS",require:"haskell"},ignore:{title:".ignore",owner:"osipxd",alias:["gitignore","hgignore","npmignore"],aliasTitles:{gitignore:".gitignore",hgignore:".hgignore",npmignore:".npmignore"}},inform7:{title:"Inform 7",owner:"Golmote"},ini:{title:"Ini",owner:"aviaryan"},io:{title:"Io",owner:"AlesTsurko"},j:{title:"J",owner:"Golmote"},java:{title:"Java",require:"clike",owner:"sherblot"},javadoc:{title:"JavaDoc",require:["markup","java","javadoclike"],modify:"java",optional:"scala",owner:"RunDevelopment"},javadoclike:{title:"JavaDoc-like",modify:["java","javascript","php"],owner:"RunDevelopment"},javastacktrace:{title:"Java stack trace",owner:"RunDevelopment"},jexl:{title:"Jexl",owner:"czosel"},jolie:{title:"Jolie",require:"clike",owner:"thesave"},jq:{title:"JQ",owner:"RunDevelopment"},jsdoc:{title:"JSDoc",require:["javascript","javadoclike","typescript"],modify:"javascript",optional:["actionscript","coffeescript"],owner:"RunDevelopment"},"js-extras":{title:"JS Extras",require:"javascript",modify:"javascript",optional:["actionscript","coffeescript","flow","n4js","typescript"],owner:"RunDevelopment"},json:{title:"JSON",alias:"webmanifest",aliasTitles:{webmanifest:"Web App Manifest"},owner:"CupOfTea696"},json5:{title:"JSON5",require:"json",owner:"RunDevelopment"},jsonp:{title:"JSONP",require:"json",owner:"RunDevelopment"},jsstacktrace:{title:"JS stack trace",owner:"sbrl"},"js-templates":{title:"JS Templates",require:"javascript",modify:"javascript",optional:["css","css-extras","graphql","markdown","markup","sql"],owner:"RunDevelopment"},julia:{title:"Julia",owner:"cdagnino"},keepalived:{title:"Keepalived Configure",owner:"dev-itsheng"},keyman:{title:"Keyman",owner:"mcdurdin"},kotlin:{title:"Kotlin",alias:["kt","kts"],aliasTitles:{kts:"Kotlin Script"},require:"clike",owner:"Golmote"},kumir:{title:"KuMir (\u041a\u0443\u041c\u0438\u0440)",alias:"kum",owner:"edukisto"},kusto:{title:"Kusto",owner:"RunDevelopment"},latex:{title:"LaTeX",alias:["tex","context"],aliasTitles:{tex:"TeX",context:"ConTeXt"},owner:"japborst"},latte:{title:"Latte",require:["clike","markup-templating","php"],owner:"nette"},less:{title:"Less",require:"css",optional:"css-extras",owner:"Golmote"},lilypond:{title:"LilyPond",require:"scheme",alias:"ly",owner:"RunDevelopment"},liquid:{title:"Liquid",require:"markup-templating",owner:"cinhtau"},lisp:{title:"Lisp",alias:["emacs","elisp","emacs-lisp"],owner:"JuanCaicedo"},livescript:{title:"LiveScript",owner:"Golmote"},llvm:{title:"LLVM IR",owner:"porglezomp"},log:{title:"Log file",optional:"javastacktrace",owner:"RunDevelopment"},lolcode:{title:"LOLCODE",owner:"Golmote"},lua:{title:"Lua",owner:"Golmote"},magma:{title:"Magma (CAS)",owner:"RunDevelopment"},makefile:{title:"Makefile",owner:"Golmote"},markdown:{title:"Markdown",require:"markup",optional:"yaml",alias:"md",owner:"Golmote"},"markup-templating":{title:"Markup templating",require:"markup",owner:"Golmote"},mata:{title:"Mata",owner:"RunDevelopment"},matlab:{title:"MATLAB",owner:"Golmote"},maxscript:{title:"MAXScript",owner:"RunDevelopment"},mel:{title:"MEL",owner:"Golmote"},mermaid:{title:"Mermaid",owner:"RunDevelopment"},metafont:{title:"METAFONT",owner:"LaeriExNihilo"},mizar:{title:"Mizar",owner:"Golmote"},mongodb:{title:"MongoDB",owner:"airs0urce",require:"javascript"},monkey:{title:"Monkey",owner:"Golmote"},moonscript:{title:"MoonScript",alias:"moon",owner:"RunDevelopment"},n1ql:{title:"N1QL",owner:"TMWilds"},n4js:{title:"N4JS",require:"javascript",optional:"jsdoc",alias:"n4jsd",owner:"bsmith-n4"},"nand2tetris-hdl":{title:"Nand To Tetris HDL",owner:"stephanmax"},naniscript:{title:"Naninovel Script",owner:"Elringus",alias:"nani"},nasm:{title:"NASM",owner:"rbmj"},neon:{title:"NEON",owner:"nette"},nevod:{title:"Nevod",owner:"nezaboodka"},nginx:{title:"nginx",owner:"volado"},nim:{title:"Nim",owner:"Golmote"},nix:{title:"Nix",owner:"Golmote"},nsis:{title:"NSIS",owner:"idleberg"},objectivec:{title:"Objective-C",require:"c",alias:"objc",owner:"uranusjr"},ocaml:{title:"OCaml",owner:"Golmote"},odin:{title:"Odin",owner:"edukisto"},opencl:{title:"OpenCL",require:"c",modify:["c","cpp"],owner:"Milania1"},openqasm:{title:"OpenQasm",alias:"qasm",owner:"RunDevelopment"},oz:{title:"Oz",owner:"Golmote"},parigp:{title:"PARI/GP",owner:"Golmote"},parser:{title:"Parser",require:"markup",owner:"Golmote"},pascal:{title:"Pascal",alias:"objectpascal",aliasTitles:{objectpascal:"Object Pascal"},owner:"Golmote"},pascaligo:{title:"Pascaligo",owner:"DefinitelyNotAGoat"},psl:{title:"PATROL Scripting Language",owner:"bertysentry"},pcaxis:{title:"PC-Axis",alias:"px",owner:"RunDevelopment"},peoplecode:{title:"PeopleCode",alias:"pcode",owner:"RunDevelopment"},perl:{title:"Perl",owner:"Golmote"},php:{title:"PHP",require:"markup-templating",owner:"milesj"},phpdoc:{title:"PHPDoc",require:["php","javadoclike"],modify:"php",owner:"RunDevelopment"},"php-extras":{title:"PHP Extras",require:"php",modify:"php",owner:"milesj"},"plant-uml":{title:"PlantUML",alias:"plantuml",owner:"RunDevelopment"},plsql:{title:"PL/SQL",require:"sql",owner:"Golmote"},powerquery:{title:"PowerQuery",alias:["pq","mscript"],owner:"peterbud"},powershell:{title:"PowerShell",owner:"nauzilus"},processing:{title:"Processing",require:"clike",owner:"Golmote"},prolog:{title:"Prolog",owner:"Golmote"},promql:{title:"PromQL",owner:"arendjr"},properties:{title:".properties",owner:"Golmote"},protobuf:{title:"Protocol Buffers",require:"clike",owner:"just-boris"},pug:{title:"Pug",require:["markup","javascript"],optional:["coffeescript","ejs","handlebars","less","livescript","markdown","scss","stylus","twig"],owner:"Golmote"},puppet:{title:"Puppet",owner:"Golmote"},pure:{title:"Pure",optional:["c","cpp","fortran"],owner:"Golmote"},purebasic:{title:"PureBasic",require:"clike",alias:"pbfasm",owner:"HeX0R101"},purescript:{title:"PureScript",require:"haskell",alias:"purs",owner:"sriharshachilakapati"},python:{title:"Python",alias:"py",owner:"multipetros"},qsharp:{title:"Q#",require:"clike",alias:"qs",owner:"fedonman"},q:{title:"Q (kdb+ database)",owner:"Golmote"},qml:{title:"QML",require:"javascript",owner:"RunDevelopment"},qore:{title:"Qore",require:"clike",owner:"temnroegg"},r:{title:"R",owner:"Golmote"},racket:{title:"Racket",require:"scheme",alias:"rkt",owner:"RunDevelopment"},cshtml:{title:"Razor C#",alias:"razor",require:["markup","csharp"],optional:["css","css-extras","javascript","js-extras"],owner:"RunDevelopment"},jsx:{title:"React JSX",require:["markup","javascript"],optional:["jsdoc","js-extras","js-templates"],owner:"vkbansal"},tsx:{title:"React TSX",require:["jsx","typescript"]},reason:{title:"Reason",require:"clike",owner:"Golmote"},regex:{title:"Regex",owner:"RunDevelopment"},rego:{title:"Rego",owner:"JordanSh"},renpy:{title:"Ren'py",alias:"rpy",owner:"HyuchiaDiego"},rescript:{title:"ReScript",alias:"res",owner:"vmarcosp"},rest:{title:"reST (reStructuredText)",owner:"Golmote"},rip:{title:"Rip",owner:"ravinggenius"},roboconf:{title:"Roboconf",owner:"Golmote"},robotframework:{title:"Robot Framework",alias:"robot",owner:"RunDevelopment"},ruby:{title:"Ruby",require:"clike",alias:"rb",owner:"samflores"},rust:{title:"Rust",owner:"Golmote"},sas:{title:"SAS",optional:["groovy","lua","sql"],owner:"Golmote"},sass:{title:"Sass (Sass)",require:"css",optional:"css-extras",owner:"Golmote"},scss:{title:"Sass (SCSS)",require:"css",optional:"css-extras",owner:"MoOx"},scala:{title:"Scala",require:"java",owner:"jozic"},scheme:{title:"Scheme",owner:"bacchus123"},"shell-session":{title:"Shell session",require:"bash",alias:["sh-session","shellsession"],owner:"RunDevelopment"},smali:{title:"Smali",owner:"RunDevelopment"},smalltalk:{title:"Smalltalk",owner:"Golmote"},smarty:{title:"Smarty",require:"markup-templating",optional:"php",owner:"Golmote"},sml:{title:"SML",alias:"smlnj",aliasTitles:{smlnj:"SML/NJ"},owner:"RunDevelopment"},solidity:{title:"Solidity (Ethereum)",alias:"sol",require:"clike",owner:"glachaud"},"solution-file":{title:"Solution file",alias:"sln",owner:"RunDevelopment"},soy:{title:"Soy (Closure Template)",require:"markup-templating",owner:"Golmote"},sparql:{title:"SPARQL",require:"turtle",owner:"Triply-Dev",alias:"rq"},"splunk-spl":{title:"Splunk SPL",owner:"RunDevelopment"},sqf:{title:"SQF: Status Quo Function (Arma 3)",require:"clike",owner:"RunDevelopment"},sql:{title:"SQL",owner:"multipetros"},squirrel:{title:"Squirrel",require:"clike",owner:"RunDevelopment"},stan:{title:"Stan",owner:"RunDevelopment"},stata:{title:"Stata Ado",require:["mata","java","python"],owner:"RunDevelopment"},iecst:{title:"Structured Text (IEC 61131-3)",owner:"serhioromano"},stylus:{title:"Stylus",owner:"vkbansal"},supercollider:{title:"SuperCollider",alias:"sclang",owner:"RunDevelopment"},swift:{title:"Swift",owner:"chrischares"},systemd:{title:"Systemd configuration file",owner:"RunDevelopment"},"t4-templating":{title:"T4 templating",owner:"RunDevelopment"},"t4-cs":{title:"T4 Text Templates (C#)",require:["t4-templating","csharp"],alias:"t4",owner:"RunDevelopment"},"t4-vb":{title:"T4 Text Templates (VB)",require:["t4-templating","vbnet"],owner:"RunDevelopment"},tap:{title:"TAP",owner:"isaacs",require:"yaml"},tcl:{title:"Tcl",owner:"PeterChaplin"},tt2:{title:"Template Toolkit 2",require:["clike","markup-templating"],owner:"gflohr"},textile:{title:"Textile",require:"markup",optional:"css",owner:"Golmote"},toml:{title:"TOML",owner:"RunDevelopment"},tremor:{title:"Tremor",alias:["trickle","troy"],owner:"darach",aliasTitles:{trickle:"trickle",troy:"troy"}},turtle:{title:"Turtle",alias:"trig",aliasTitles:{trig:"TriG"},owner:"jakubklimek"},twig:{title:"Twig",require:"markup-templating",owner:"brandonkelly"},typescript:{title:"TypeScript",require:"javascript",optional:"js-templates",alias:"ts",owner:"vkbansal"},typoscript:{title:"TypoScript",alias:"tsconfig",aliasTitles:{tsconfig:"TSConfig"},owner:"dkern"},unrealscript:{title:"UnrealScript",alias:["uscript","uc"],owner:"RunDevelopment"},uorazor:{title:"UO Razor Script",owner:"jaseowns"},uri:{title:"URI",alias:"url",aliasTitles:{url:"URL"},owner:"RunDevelopment"},v:{title:"V",require:"clike",owner:"taggon"},vala:{title:"Vala",require:"clike",optional:"regex",owner:"TemplarVolk"},vbnet:{title:"VB.Net",require:"basic",owner:"Bigsby"},velocity:{title:"Velocity",require:"markup",owner:"Golmote"},verilog:{title:"Verilog",owner:"a-rey"},vhdl:{title:"VHDL",owner:"a-rey"},vim:{title:"vim",owner:"westonganger"},"visual-basic":{title:"Visual Basic",alias:["vb","vba"],aliasTitles:{vba:"VBA"},owner:"Golmote"},warpscript:{title:"WarpScript",owner:"RunDevelopment"},wasm:{title:"WebAssembly",owner:"Golmote"},"web-idl":{title:"Web IDL",alias:"webidl",owner:"RunDevelopment"},wgsl:{title:"WGSL",owner:"Dr4gonthree"},wiki:{title:"Wiki markup",require:"markup",owner:"Golmote"},wolfram:{title:"Wolfram language",alias:["mathematica","nb","wl"],aliasTitles:{mathematica:"Mathematica",nb:"Mathematica Notebook"},owner:"msollami"},wren:{title:"Wren",owner:"clsource"},xeora:{title:"Xeora",require:"markup",alias:"xeoracube",aliasTitles:{xeoracube:"XeoraCube"},owner:"freakmaxi"},"xml-doc":{title:"XML doc (.net)",require:"markup",modify:["csharp","fsharp","vbnet"],owner:"RunDevelopment"},xojo:{title:"Xojo (REALbasic)",owner:"Golmote"},xquery:{title:"XQuery",require:"markup",owner:"Golmote"},yaml:{title:"YAML",alias:"yml",owner:"hason"},yang:{title:"YANG",owner:"RunDevelopment"},zig:{title:"Zig",owner:"RunDevelopment"}},plugins:{meta:{path:"plugins/{id}/prism-{id}",link:"plugins/{id}/"},"line-highlight":{title:"Line Highlight",description:"Highlights specific lines and/or line ranges."},"line-numbers":{title:"Line Numbers",description:"Line number at the beginning of code lines.",owner:"kuba-kubula"},"show-invisibles":{title:"Show Invisibles",description:"Show hidden characters such as tabs and line breaks.",optional:["autolinker","data-uri-highlight"]},autolinker:{title:"Autolinker",description:"Converts URLs and emails in code to clickable links. Parses Markdown links in comments."},wpd:{title:"WebPlatform Docs",description:'Makes tokens link to WebPlatform.org documentation. The links open in a new tab.'},"custom-class":{title:"Custom Class",description:"This plugin allows you to prefix Prism's default classes (.comment can become .namespace--comment) or replace them with your defined ones (like .editor__comment). You can even add new classes.",owner:"dvkndn",noCSS:!0},"file-highlight":{title:"File Highlight",description:"Fetch external files and highlight them with Prism. Used on the Prism website itself.",noCSS:!0},"show-language":{title:"Show Language",description:"Display the highlighted language in code blocks (inline code does not show the label).",owner:"nauzilus",noCSS:!0,require:"toolbar"},"jsonp-highlight":{title:"JSONP Highlight",description:"Fetch content with JSONP and highlight some interesting content (e.g. GitHub/Gists or Bitbucket API).",noCSS:!0,owner:"nauzilus"},"highlight-keywords":{title:"Highlight Keywords",description:"Adds special CSS classes for each keyword for fine-grained highlighting.",owner:"vkbansal",noCSS:!0},"remove-initial-line-feed":{title:"Remove initial line feed",description:"Removes the initial line feed in code blocks.",owner:"Golmote",noCSS:!0},"inline-color":{title:"Inline color",description:"Adds a small inline preview for colors in style sheets.",require:"css-extras",owner:"RunDevelopment"},previewers:{title:"Previewers",description:"Previewers for angles, colors, gradients, easing and time.",require:"css-extras",owner:"Golmote"},autoloader:{title:"Autoloader",description:"Automatically loads the needed languages to highlight the code blocks.",owner:"Golmote",noCSS:!0},"keep-markup":{title:"Keep Markup",description:"Prevents custom markup from being dropped out during highlighting.",owner:"Golmote",optional:"normalize-whitespace",noCSS:!0},"command-line":{title:"Command Line",description:"Display a command line with a prompt and, optionally, the output/response from the commands.",owner:"chriswells0"},"unescaped-markup":{title:"Unescaped Markup",description:"Write markup without having to escape anything."},"normalize-whitespace":{title:"Normalize Whitespace",description:"Supports multiple operations to normalize whitespace in code blocks.",owner:"zeitgeist87",optional:"unescaped-markup",noCSS:!0},"data-uri-highlight":{title:"Data-URI Highlight",description:"Highlights data-URI contents.",owner:"Golmote",noCSS:!0},toolbar:{title:"Toolbar",description:"Attach a toolbar for plugins to easily register buttons on the top of a code block.",owner:"mAAdhaTTah"},"copy-to-clipboard":{title:"Copy to Clipboard Button",description:"Add a button that copies the code block to the clipboard when clicked.",owner:"mAAdhaTTah",require:"toolbar",noCSS:!0},"download-button":{title:"Download Button",description:"A button in the toolbar of a code block adding a convenient way to download a code file.",owner:"Golmote",require:"toolbar",noCSS:!0},"match-braces":{title:"Match braces",description:"Highlights matching braces.",owner:"RunDevelopment"},"diff-highlight":{title:"Diff Highlight",description:"Highlights the code inside diff blocks.",owner:"RunDevelopment",require:"diff"},"filter-highlight-all":{title:"Filter highlightAll",description:"Filters the elements the highlightAll and highlightAllUnder methods actually highlight.",owner:"RunDevelopment",noCSS:!0},treeview:{title:"Treeview",description:"A language with special styles to highlight file system tree structures.",owner:"Golmote"}}})},2885:(e,t,n)=>{const r=n(9901),a=n(9642),o=new Set;function i(e){void 0===e?e=Object.keys(r.languages).filter((e=>"meta"!=e)):Array.isArray(e)||(e=[e]);const t=[...o,...Object.keys(Prism.languages)];a(r,e,t).load((e=>{if(!(e in r.languages))return void(i.silent||console.warn("Language does not exist: "+e));const t="./prism-"+e;delete n.c[n(6500).resolve(t)],delete Prism.languages[e],n(6500)(t),o.add(e)}))}i.silent=!1,e.exports=i},6726:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6726},6500:(e,t,n)=>{var r={"./":2885};function a(e){var t=o(e);return n(t)}function o(e){if(!n.o(r,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return r[e]}a.keys=function(){return Object.keys(r)},a.resolve=o,e.exports=a,a.id=6500},9642:e=>{"use strict";var t=function(){var e=function(){};function t(e,t){Array.isArray(e)?e.forEach(t):null!=e&&t(e,0)}function n(e){for(var t={},n=0,r=e.length;n "));var l={},s=e[r];if(s){function u(t){if(!(t in e))throw new Error(r+" depends on an unknown component "+t);if(!(t in l))for(var i in a(t,o),l[t]=!0,n[t])l[i]=!0}t(s.require,u),t(s.optional,u),t(s.modify,u)}n[r]=l,o.pop()}}return function(e){var t=n[e];return t||(a(e,r),t=n[e]),t}}function a(e){for(var t in e)return!0;return!1}return function(o,i,l){var s=function(e){var t={};for(var n in e){var r=e[n];for(var a in r)if("meta"!=a){var o=r[a];t[a]="string"==typeof o?{title:o}:o}}return t}(o),u=function(e){var n;return function(r){if(r in e)return r;if(!n)for(var a in n={},e){var o=e[a];t(o&&o.alias,(function(t){if(t in n)throw new Error(t+" cannot be alias for both "+a+" and "+n[t]);if(t in e)throw new Error(t+" cannot be alias of "+a+" because it is a component.");n[t]=a}))}return n[r]||r}}(s);i=i.map(u),l=(l||[]).map(u);var c=n(i),d=n(l);i.forEach((function e(n){var r=s[n];t(r&&r.require,(function(t){t in d||(c[t]=!0,e(t))}))}));for(var p,f=r(s),m=c;a(m);){for(var h in p={},m){var g=s[h];t(g&&g.modify,(function(e){e in d&&(p[e]=!0)}))}for(var b in d)if(!(b in c))for(var v in f(b))if(v in c){p[b]=!0;break}for(var y in m=p)c[y]=!0}var w={getIds:function(){var e=[];return w.load((function(t){e.push(t)})),e},load:function(t,n){return function(t,n,r,a){var o=a?a.series:void 0,i=a?a.parallel:e,l={},s={};function u(e){if(e in l)return l[e];s[e]=!0;var a,c=[];for(var d in t(e))d in n&&c.push(d);if(0===c.length)a=r(e);else{var p=i(c.map((function(e){var t=u(e);return delete s[e],t})));o?a=o(p,(function(){return r(e)})):r(e)}return l[e]=a}for(var c in n)u(c);var d=[];for(var p in s)d.push(l[p]);return i(d)}(f,c,t,n)}};return w}}();e.exports=t},2703:(e,t,n)=>{"use strict";var r=n(414);function a(){}function o(){}o.resetWarningCache=a,e.exports=function(){function e(e,t,n,a,o,i){if(i!==r){var l=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw l.name="Invariant Violation",l}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:o,resetWarningCache:a};return n.PropTypes=n,n}},5697:(e,t,n)=>{e.exports=n(2703)()},414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},4448:(e,t,n)=>{"use strict";var r=n(7294),a=n(7418),o=n(3840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n
    \n

    Your Docusaurus site did not load properly.

    \n

    A very common reason is a wrong site baseUrl configuration.

    \n

    Current configured baseUrl = ${e} ${"/"===e?" (default value)":""}

    \n

    We suggest trying baseUrl =

    \n