<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Ceph</title>
	<atom:link href="https://blog.gmem.cc/tag/ceph/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Tue, 21 Apr 2026 10:40:56 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Ceph学习笔记</title>
		<link>https://blog.gmem.cc/ceph-study-note</link>
		<comments>https://blog.gmem.cc/ceph-study-note#comments</comments>
		<pubDate>Sat, 21 Apr 2018 09:01:57 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[IaaS]]></category>
		<category><![CDATA[Ceph]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=20465</guid>
		<description><![CDATA[<p>简介 Ceph是一个高性能、可扩容的分布式存储系统，它提供三大功能： 对象存储：提供RESTful接口，也提供多种编程语言绑定。兼容S3、Swift 块存储：由RBD提供，可以直接作为磁盘挂载，内置了容灾机制 文件系统：提供POSIX兼容的网络文件系统CephFS，专注于高性能、大容量存储 Ceph集群由一系列节点（机器）组成，在这些节点上运行以下组件： Ceph OSDs：OSD即对象存储守护程序，但是它并非针对对象存储。OSD负责存储数据、处理数据复制、恢复、回填（Backfilling）、再平衡。此外OSD还对其它OSD进行心跳检测，检测结果汇报给Monitor Monitors：监视器，维护集群状态的多种映射，同时提供认证和日志记录服务 MDSs：元数据服务器，存储CephFS的元数据信息 Ceph将客户端的数据作为对象存储在它的存储池中，基于CRUSH算法，Ceph计算出每个对象应该位于那个PG，计算哪个OSD负责存储PG 架构 组件层次 数据读写流程 术语 术语 说明 RADOS 可靠的、自动化的分布式对象存储（Reliable, Autonomic Distributed Object <a class="read-more" href="https://blog.gmem.cc/ceph-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/ceph-study-note">Ceph学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Ceph是一个高性能、可扩容的分布式存储系统，它提供三大功能：</p>
<ol>
<li>对象存储：提供RESTful接口，也提供多种编程语言绑定。兼容S3、Swift</li>
<li>块存储：由RBD提供，可以直接作为磁盘挂载，内置了容灾机制</li>
<li>文件系统：提供POSIX兼容的网络文件系统CephFS，专注于高性能、大容量存储</li>
</ol>
<p>Ceph集群由一系列节点（机器）组成，在这些节点上运行以下组件：</p>
<ol>
<li>Ceph OSDs：OSD即对象存储守护程序，但是它并非针对对象存储。OSD负责<span style="background-color: #c0c0c0;">存储数据、处理数据复制、恢复、回填（B</span><span style="color: #3e4349;"><span style="background-color: #c0c0c0;">ackfilling）、再平衡</span>。此外OSD还对其它OSD进行心跳检测，检测结果汇报给Monitor</span></li>
<li>Monitors：监视器，维护集群状态的多种映射<span style="color: #3e4349;">，同时提供认证和日志记录服务</span></li>
<li>MDSs：元数据服务器，存储CephFS的元数据信息</li>
</ol>
<p>Ceph将客户端的数据作为对象存储在它的存储池中，基于CRUSH算法，Ceph<span style="background-color: #c0c0c0;">计算出每个对象应该位于那个PG，计算哪个OSD负责存储PG</span></p>
<div class="blog_h2"><span class="graybg">架构</span></div>
<div class="blog_h3"><span class="graybg">组件层次</span></div>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2018/04/ceph_all_component.png"><img class="aligncenter size-full wp-image-21209" src="https://blog.gmem.cc/wp-content/uploads/2018/04/ceph_all_component.png" alt="ceph_all_component" width="477" height="629" /></a></p>
<div class="blog_h3"><span class="graybg">数据读写流程</span></div>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2018/04/rados_erasure_coding.png"><img class="aligncenter  wp-image-21141" src="https://blog.gmem.cc/wp-content/uploads/2018/04/rados_erasure_coding.png" alt="rados_erasure_coding" width="897" height="533" /></a></p>
<div class="blog_h2"><span class="graybg">术语</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">术语</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>RADOS</td>
<td>
<p>可靠的、自动化的分布式对象存储（Reliable, Autonomic Distributed Object Store）是Ceph的核心之一</p>
<p>librados是RADOS提供的库，上层的RBD、RGW和CephFS都是通过librados访问RADOS的</p>
</td>
</tr>
<tr>
<td>RGW</td>
<td>即RADOS Gateway，指Ceph的对象存储API或者RGW守护进程</td>
</tr>
<tr>
<td>RBD</td>
<td>即RADOS Block Device，指Ceph提供的基于复制性的分布式的块设备。类似于LVM中的逻辑卷，<span style="background-color: #c0c0c0;">RBD只能属于一个Pool</span></td>
</tr>
<tr>
<td>MDS</td>
<td>即Ceph元数据服务器，是CephFS服务依赖的元数据服务</td>
</tr>
<tr>
<td>CephFS</td>
<td>Ceph File System，是Ceph对外提供的文件系统服务</td>
</tr>
<tr>
<td>Pool</td>
<td>
<p>存储池是Ceph中一些<span style="background-color: #c0c0c0;">对象的逻辑分组</span>。它不是一个连续的分区，而是一个逻辑概念，类似LVM中的卷组（Volume Group）</p>
<p>存储池分为两个类型：</p>
<ol>
<li>Replicated 复制型，<span style="background-color: #c0c0c0;">对象具有多份拷贝，确保部分OSD丢失时数据不丢失</span>，需要更多的磁盘空间。复制份数可以动态调整，可以置为1</li>
<li>Erasure-coded 纠错码型，节约空间，但是速度慢，不支持所有对象操作（例如局部写）</li>
</ol>
</td>
</tr>
<tr>
<td>PG</td>
<td>
<p>归置组（Placement Group），PG是Pool组织对象的方式，便于<span style="background-color: #c0c0c0;">更好的分配数据和定位数据</span>，<span style="background-color: #c0c0c0;">Pool由若干PG组成</span></p>
<p>PG 的数量会影响Ceph集群的行为和数据的持久性。<span style="background-color: #c0c0c0;">集群扩容后可以增大PG数量：5个以下OSD设置为128即可</span></p>
<p>PG的特点：同一个PG中所有的对象，<span style="background-color: #c0c0c0;">在相同一组OSDs上被复制</span>。复制型Pool中PG可以有<span style="background-color: #c0c0c0;">一个作为主（Primary）OSD</span>，其它作为从OSD。一个对象仅仅属于一个PG，也就是说对象存储在固定的一组OSDs上</p>
<p>PG在OSD的/var/lib/ceph/osd/ceph-2/current目录下，表现为目录</p>
</td>
</tr>
<tr>
<td>CRUSH</td>
<td>
<p>CRUSH即基于可扩容哈希的受控复制（Controlled Replication Under Scalable Hashing），是一种<span style="background-color: #c0c0c0;">数据分发算法</span>，类似于哈希和一致性哈希。哈希的问题在于数据增长时<span style="background-color: #c0c0c0;">不能动态添加Bucket</span>，一致性哈希的问题在于<span style="background-color: #c0c0c0;">添加Bucket时数据迁移量比较大</span>，其他<span style="background-color: #c0c0c0;">数据分发算法依赖中心的Metadata服务器来存储元数据因而效率较低</span>，CRUSH则是通过计算、接受多维参数的来解决动态数据分发的场景</p>
<p>CRUSH算法接受的参数包括：</p>
<ol>
<li>Cluster map，也就是硬盘分布的逻辑位置，例如这有<span style="background-color: #c0c0c0;">多少个机房、多少个机柜、硬盘是如何分布的</span>等等。<span style="background-color: #c0c0c0;">Cluster map是类似树的结构</span>，<span style="background-color: #c0c0c0;">子节点是真正存储数据的device</span>，每个d<span style="background-color: #c0c0c0;">evice都有id和权重</span>，<span style="background-color: #c0c0c0;">中间节点是bucket</span>，bucket有多种类型用于不同的查询算法，例如一个机柜一个机架一个机房就是bucket</li>
<li>Placement rules，它指定了一份数据有<span style="background-color: #c0c0c0;">多少备份</span>，数据的分布有什么<span style="background-color: #c0c0c0;">限制条件</span>（例如同一份数据不能放在同一个机柜里）。每个Rule对应一系列操作：
<ol>
<li>take，选取一个bucket</li>
<li>select，选择n个类型为t的项</li>
<li>emit，提交</li>
</ol>
</li>
</ol>
<p>CRUSH与一致性哈希最大的区别在于接受的参数多了Cluster map和Placement rules，这样就可以<span style="background-color: #c0c0c0;">根据目前Cluster的状态动态调整数据位置</span>，同时通过算法<span style="background-color: #c0c0c0;">得到一致的结果</span></p>
<p>基于此算法，Ceph存储集群能够动态的<span style="background-color: #c0c0c0;">扩容、再平衡、恢复</span></p>
</td>
</tr>
<tr>
<td>Object</td>
<td>
<p>Ceph最底层的存储单元是Object，每个<span style="background-color: #c0c0c0;">Object包含元数据和原始数据</span></p>
<p>一个RBD会<span style="background-color: #c0c0c0;">包含很多个Object</span></p>
</td>
</tr>
<tr>
<td>OSD</td>
<td>
<p>对象存储守护进程（Object Storage Daemon），负责响应客户端请求返回具体数据的进程。Ceph集群中有大量OSD</p>
<p>一个节点上通常只运行一个OSD守护进程，此守护进程在<span style="background-color: #c0c0c0;">一个存储驱动器上只运行一个 filestore</span></p>
</td>
</tr>
<tr>
<td>EC</td>
<td>
<p>Erasure Code（EC），即纠删码，是一种<span style="background-color: #c0c0c0;">前向错误纠正技术</span>（Forward Error Correction，FEC），主要应用在网络传输中避免包的丢失， 存储系统利用它来<span style="background-color: #c0c0c0;">提高可靠性</span>。相比多副本复制而言， 纠删码能够<span style="background-color: #c0c0c0;">以更小的数据冗余度获得更高数据可靠性</span>， 但<span style="background-color: #c0c0c0;">编码方式较复杂，需要大量计算</span> 。纠删码只能容忍数据丢失，无法容忍数据篡改，纠删码正是得名与此</p>
<p>EC将n份原始数据，增加m份数据，并能通过n+m份中的任意n份数据，还原为原始数据。即如果有<span style="background-color: #c0c0c0;">任意小于等于m份的数据失效，仍然能通过剩下的数据还原出来</span></p>
<p>纠删码技术在分布式存储系统中的应用主要有三类：</p>
<ol>
<li>阵列纠删码（Array Code: RAID5、RAID6等）：RAID是EC的特例，RAID5只支持一个盘失效，RAID6支持两个盘失效，而EC支持多个盘失效</li>
<li>RS(Reed-Solomon)里德-所罗门类纠删码</li>
<li>LDPC(LowDensity Parity Check Code)低密度奇偶校验纠删码：目前主要用于通信、视频和音频编码等领域，与RS编码相比，LDPC<span style="background-color: #c0c0c0;">编码效率要略低，但编码和解码性能要优于RS码以及其他的纠删码</span></li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">组件</span></div>
<div class="blog_h2"><span class="graybg">MON</span></div>
<p>监视器<span style="background-color: #c0c0c0;">维护集群状态的多种映射</span>—— 包monmap<span style="color: #3e4349;">、OSD map、PG map、CRUSH map、MDS map，同时<span style="background-color: #c0c0c0;">提供认证和日志记录服</span>务。Ceph会记录Monitor、OSD、PG的每次状态变更历史（此历史称作epoch）。客户端连到单个监视器并获取当前映射就能确定所有监视器、 OSD 和元数据服务器的位置。依赖于CRUSH算法和当前集群状态映射，客户端就能计算出任何对象的位置，直连OSD读写数据。</span></p>
<p>Ceph<span style="background-color: #c0c0c0;">客户端、其它守护进程通过配置文件发现mon</span>，但是mon之间的<span style="background-color: #c0c0c0;">相互发现却依赖于monmap的本地副本</span>。所有mon会基于分布式一致性算法Paxos，确保各自本地的monmap是一致的，当新增一个mon后，所有现有mon的monmap都自动更新为最新版本。</p>
<div class="blog_h3"><span class="graybg">监视器同步</span></div>
<p>使用多个mon时，每个mon都会检查其它mon是否具有更新的集群状态映射版本 —— 存在一个或多个epoch大于当前mon的最高epoch。太过落后的mon可能会离开quorum，同步后再加入quorum。执行同步时，mon分为三类角色：</p>
<ol>
<li>Leader：具有最新版本状态映射的mon</li>
<li>Provider：同上，但是它的最新状态是从Leader同步获得</li>
<li>Requester：落后于Leader，必须获取最新集群状态映射才能重回quorum</li>
</ol>
<div class="blog_h3"><span class="graybg">时钟偏移</span></div>
<p>如果mon的时钟不同步，可能会导致：</p>
<ol>
<li>守护进程忽略收到的消息（时间戳过时）</li>
<li>消息未及时收到时，超时触发得太快或太晚</li>
</ol>
<div class="blog_h2"><span class="graybg">OSD</span></div>
<div class="blog_h3"><span class="graybg">日志</span></div>
<p>OSD使用日志的原因有两个：</p>
<ol>
<li>速度： 日志使得 OSD 可以<span style="background-color: #c0c0c0;">快速地提交小块数据的写</span>入， <span style="background-color: #c0c0c0;">Ceph 把小片、随机 IO 依次写入日志</span>，这样，后端文件系统就有可能<span style="background-color: #c0c0c0;">归并写入动作</span>，并最终提升并发承载力。因此，使用 OSD 日志能<span style="background-color: #c0c0c0;">展现出优秀的突发写性能</span>，实际上数据还没有写入 OSD ，因为文件系统把它们捕捉到了日志</li>
<li>一致性：OSD需要一个能<span style="background-color: #c0c0c0;">保证原子化复合操作的文件系统接口</span>。 OSD 把一个<span style="background-color: #c0c0c0;">操作的描述写入日志，并把操作应用到文件系统</span>。这确保了对象（例如归置组元数据）的原子更新。每隔一段时间（由filestore max sync interval 和 filestore min sync interval控制 ）， OSD 会停止写入，把日志同步到文件系统，这样允许 OSD 修整日志里的操作并重用空间。若失败， OSD 从上个同步点开始重放日志。<span style="background-color: #c0c0c0;">日志的原子性表现在，它不使用操作系统的文件缓存（基于内存）</span>，避免断电丢数据的问题</li>
</ol>
<p>注意：OSD进程在<span style="background-color: #c0c0c0;">往数据盘上刷日志数据的过程中，是停止写操作的</span>。 </p>
<p>通常使用独立SSD来存储日志，原因是：</p>
<ol>
<li>避免针对单块磁盘的双重写入 —— 先写日志，再写filestore</li>
<li>SSD性能好，可以降低延迟提升IOPS</li>
</ol>
<div class="blog_h3"><span class="graybg">OSD状态矩阵</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 70px;"> </td>
<td style="text-align: center;">IN</td>
<td style="text-align: center;">OUT</td>
</tr>
</thead>
<tbody>
<tr>
<td><strong>UP</strong></td>
<td>正常状态，OSD位于集群中，且接收数据</td>
<td>
<p>OSD虽然在运行，但是<span style="background-color: #c0c0c0;">被踢出集群 —— CRUSH不会再分配归置组给它</span></p>
</td>
</tr>
<tr>
<td><strong>DOWN</strong></td>
<td>这种状态<span style="background-color: #c0c0c0;">不正常</span>，集群处于非健康状态</td>
<td>正常状态</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Bluestore</span></div>
<p>在Luminous中，Bluestore已经代替Filestore作为默认的存储引擎。<span style="background-color: #c0c0c0;">Bluestore直接管理裸设备，不使用OS提供的文件系统接口，因此它不会收到OS缓存影响</span>。</p>
<p><span style="background-color: #c0c0c0;">使用Bluestore时，你不需要配备SSD作为独立的日志存储</span>，Bluestore不存在双重写入问题，它直接把数据落盘到块上，然后在RockDB中更新元数据（指定数据块的位置）。</p>
<p>一个基于Bluestore的OSD最多可以利用到三块磁盘，例如下面的最优化性能组合：</p>
<ol>
<li>使用HDD作为数据盘</li>
<li>使用SSD作为RockDB元数据盘</li>
<li>使用NVRAM作为RockDB WAL</li>
</ol>
<div class="blog_h2"><span class="graybg">PG</span></div>
<p>一些概念：</p>
<ol>
<li>Acting Set：牵涉到PG副本的OSD集合</li>
<li>Up Set：指Acting Set中排除掉Down掉的OSD的子集</li>
</ol>
<p>Ceph依赖于Up Set来处理客户端请求。如果 Up Set 和 Acting Set 不一致，这可能表明集群内部在重均衡或者有潜在问题。</p>
<p>写入数据前，<span style="background-color: #c0c0c0;">归置组必须处于 active 、而且应该是 clean 状态</span>。假设一存储池的归置组有 3 个副本，为让 Ceph 确定归置组的当前状态，一归置组的主 OSD （即 acting set 内的第一个 OSD ）会与第二和第三 OSD 建立连接，并就归置组的当前状态达成一致意见。</p>
<p>由于以下原因，集群状态可能显示为HEALTH WARN：</p>
<ol>
<li>刚刚创建了一个存储池，归置组还没互联好</li>
<li>归置组正在恢复</li>
<li>刚刚增加或删除了一个 OSD</li>
<li>刚刚修改了 CRUSH 图，并且归置组正在迁移</li>
<li>某一归置组的副本间的数据不一致</li>
<li>Ceph 正在洗刷一个归置组的副本</li>
<li>Ceph 没有足够空余容量来完成回填操作</li>
</ol>
<p>这些情况下，集群会自行恢复，并返回 HEALTH OK 状态，归置组全部变为active+clean。</p>
<div class="blog_h3"><span class="graybg">归置组状态表</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">状态</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;"><strong>Creating </strong></td>
<td>
<p>在你创建存储池时，Ceph会创建指定数量的PG，对应此状态</p>
<p><span style="background-color: #c0c0c0;">创建PG完毕后，Acting Set中的OSD将进行互联</span>，互联完毕后，PG变为Active+Clean状态，PG可以接受数据写入</p>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Peering</strong></td>
<td>
<p>Acting Set中的OSD正在进行互联，它们需要就PG中对象、元数据的状态达成一致。互联完成后，所有OSD达成一致意见，但是<span style="background-color: #c0c0c0;">不代表所有副本的内容都是最新</span>的</p>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Active</strong></td>
<td>互联完成后归置组状态会变为Active</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Clean</strong></td>
<td>主OSD和副本OSD已成功互联，并且没有偏离的归置组。 Ceph 已把归置组中的对象<span style="background-color: #c0c0c0;">复制了规定次数</span></td>
</tr>
<tr>
<td style="text-align: center;"><strong>Degraded</strong></td>
<td>
<p>当客户端向主 OSD 写入数据时，由<span style="background-color: #c0c0c0;">主 OSD 负责把数据副本写入其余副本 OSD</span> 。主 OSD 把对象写入存储器后，在<span style="background-color: #c0c0c0;">副本 OSD 创建完对象副本并报告给主 OSD 之前，主 OSD 会一直停留在 degraded 状态</span></p>
<p>如果OSD挂了， Ceph 会把分配到<span style="background-color: #c0c0c0;">此 OSD 的归置组都标记为 degraded</span>。只要它归置组仍然处于active 状态，客户端<span style="background-color: #c0c0c0;">仍可以degraded归置组写入新对象</span></p>
<p>如果OSD挂了（down）长期（ mon osd down out interval ，默认300秒）不恢复，Ceph会将其标记为out，并将其上的PG重新映射到其它OSD</p>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Recovering</strong></td>
<td>当挂掉的OSD重启（up）后，其内的PG中的对象<span style="background-color: #c0c0c0;">副本可能是落后的，副本更新期间</span>OSD处于此状态</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Backfilling</strong></td>
<td>
<p>新 OSD 加入集群时， CRUSH 会把现有集群内的部分归置组重分配给它。强制新 OSD 立即接受重分配的归置组会使之过载，<span style="background-color: #c0c0c0;">用归置组回填可使这个过程在后台开始</span></p>
<p>回填执行期间，你可能看到以下状态之一：</p>
<ol>
<li>backfill_wait，等待时机，回填尚未开始</li>
<li>backfill_too_full，需要进行回填，但是因存储空间不足而不能完成</li>
</ol>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Remapped</strong></td>
<td>负责某个PG的Acting Set发生变更时，数据需要从久集合迁移到新集合。此期间老的主OSD仍然需要提供服务，直到数据迁移完成 </td>
</tr>
<tr>
<td style="text-align: center;"><strong>Stale</strong></td>
<td>
<p>默认情况下，OSD每0.5秒会一次报告其归置组、出流量、引导和失败统计状态，此频率高于心跳</p>
<p>如果：</p>
<ol>
<li>归置组的主 OSD 所在的 Acting Set 没能向MON报告</li>
<li>或者其它MON已经报告，说主 OSD 已 down了</li>
</ol>
<p>则MONs就会把此归置组标记为 stale</p>
<p>集群运行期间，出现此状态，所有PG的<span style="background-color: #c0c0c0;">主OSD</span>挂了</p>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Inactive</strong></td>
<td>归置组不能处理读写请求，因为它们在<span style="background-color: #c0c0c0;">等着一个持有最新数据的 OSD 回到 up 状态</span></td>
</tr>
<tr>
<td style="text-align: center;"><strong>Unclean</strong></td>
<td>归置组里有些对象的<span style="background-color: #c0c0c0;">副本数未达到期望次数</span>，它们应该在恢复中</td>
</tr>
<tr>
<td style="text-align: center;"><strong>Down</strong></td>
<td>归置组的权威副本OSD宕机，必须等待其开机，或者被标记为lost才能继续</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">CRUSH</span></div>
<div class="blog_h3"><span class="graybg">简介</span></div>
<p>CRUSH 算法通过计算数据存储位置来确定如何存储和检索。 CRUSH授权Ceph 客户端直接连接 OSD ，而非通过一个中央服务器或代理。数据存储、检索算法的使用，使 Ceph 避免了单点故障、性能瓶颈、和伸缩的物理限制。</p>
<p>CRUSH 需要一张集群的 Map，利用该Map中的信息，将数据伪随机地、尽量平均地分布到整个集群的 OSD 里。此Map中包含：</p>
<ol>
<li>OSD 列表</li>
<li>把设备汇聚为物理位置的<span style="background-color: #c0c0c0;">“桶”（Bucket，也叫失败域，Failure Domain）</span>列表</li>
<li>指示 CRUSH 如何复制存储池中的数据的规则列表</li>
</ol>
<p>通过CRUSH map来建模存储设备的物理位置，Ceph能够避免潜在的关联性故障 —— 例如一个机柜中的设备可能共享电源、网络供应，它们更加可能因为断电而同时出现故障，Ceph会刻意的避免把数据副本放在同一机柜。</p>
<p>新部署的OSD自动被放置到CRUSH map中，位于一个host节点（OSD所在主机名）。在默认的CRUSH失败域（Failure Domain） 设置中，副本/EC分片会自动分配在不同的host节点上，避免单主机的单点故障。在大型集群中，管理员需要更加仔细的考虑失败域设置，将副本分散到不同的Rack、Row。</p>
<div class="blog_h3"><span class="graybg">crush location</span></div>
<p>OSD在CRUSH map中的位置，称为CRUSH location。此Location以如下形式来描述：</p>
<pre class="crayon-plain-tag"># 一系列键值对，虽然是有层次结构的，但是列出的顺序无所谓
# 键必须是有效的CRUSH type。默认支持root,regin, datacenter, room, row, pod, pdu, rack, chassis, host
# 你不需要声明所有键，默认情况下Ceph自动把新OSD放在root=default host=hostname下，因此这两个键你可以不声明
root=default row=a rack=a2 chassis=a2a host=a2a1</pre>
<p>你可以在Ceph配置文件中，用crush location选项来声明。每当OSD启动时，它会验证当前CRUSH map是否匹配crush location设置，如果不匹配会更新CRUSH map。设置下面的选项可以禁用此行为：</p>
<pre class="crayon-plain-tag">osd crush update on start = false</pre>
<div class="blog_h3"><span class="graybg">crush结构</span></div>
<p>CRUSH map是一个树状的层次结构，它是对存储设备物理位置松散的建模。 </p>
<p>在这个层次结构中，<span style="background-color: #c0c0c0;">叶子节点是Device，对应了OSD守护程序</span>（通常管理一块或几块磁盘）。设备的以name.id来识别，通常是osd.N。设备可以<span style="background-color: #c0c0c0;">关联一个设备类别（Device Class）</span>，取值例如hdd、ssd，CRUSH rule可以使用到设备类别。</p>
<p>除了<span style="background-color: #c0c0c0;">叶子节点之外的，都称为桶（Bucket）</span>，每个桶都具有类型，默认支持的类型包括root,regin, datacenter, room, row, pod, pdu, rack, chassis, host。大部分集群仅仅使用一部分类型的桶。</p>
<p>每个<span style="background-color: #c0c0c0;">节点都具有一个权重（Weight）字段</span>，指示子树负责存储的数据的比例。权重应该仅仅在叶子节点上设置，由Ceph自动向上类加。<span style="background-color: #c0c0c0;">权重的单位通常是TB</span>。</p>
<p>执行命令<pre class="crayon-plain-tag">ceph osd crush tree</pre>可以查看CRUSH的层次，包括节点权重。</p>
<div class="blog_h3"><span class="graybg">规则</span></div>
<p>CRUSH rule定义了数据如何跨越设备分布的规则。大部分情况下你可以通过命令行来创建CRUSH rule，少数情况下需要手工便捷CRUSH map。</p>
<div class="blog_h3"><span class="graybg">TUNABLES</span></div>
<p>随着Ceph的发展，CRUSH算法被不断的优化。Ceph允许你自由选择新或旧的算法变体，这依赖Tunable实现。</p>
<p>要使用新的Tunable，客户端、服务器<span style="background-color: #c0c0c0;">必须同时支持</span>。Tunable的命名就是最初支持对应算法变体的那个Ceph版本的名称（例如jewel）。</p>
<div class="blog_h2"><span class="graybg">RBD</span></div>
<div class="blog_h3"><span class="graybg">缓存</span></div>
<p>用户空间的Ceph块设备实现（librbd）不能使用Linux的页面缓存，因此它自己实现了一套基于内存的LRU缓存——RBD Cacheing。</p>
<p>此缓存的行为类似于页面缓存，当OS发送屏障/Flush请求时，内存中的脏数据被刷出到OSD。</p>
<div class="blog_h2"><span class="graybg">CephFS</span></div>
<p>这是一个POSIX兼容的文件系统，它使用Ceph的存储集群来保存数据。</p>
<p>一个Ceph集群可以有0-N个CephFS文件系统，每个CephFS具有可读名称和一个集群文件系统ID（FSCID）。每个CephFS可以指定多个处于standby状态的MDS进程。 </p>
<p>每个CephFS包含若干Rank，默认是1个。Rank可以看作是元数据分片。CephFS的每个守护进程（ceph-mds）默认情况下无Rank启动，Mon会自动为其分配Rank。每个守护进程最多持有一个Rank。</p>
<p>如果Rank没有关联到ceph-mds，则其状态为failed，否则其状态为up。</p>
<p>每个ceph-mds都有一个关联的名称，典型情况下设置为所在的节点的主机名。每当ceph-mds启动时，会获得一个GID，在进程生命周期中，它都使用此GID。</p>
<p>如果MDS进程超过 mds_beacon_grace seconds没有和MON联系，则它被标记为laggy。</p>
<div class="blog_h2"><span class="graybg">RGW</span></div>
<p>Ceph对象存储网关是基于librados构建的一套RESTful服务，提供对Ceph存储集群的访问。此服务提供两套接口：</p>
<ol>
<li>S3兼容接口：Amazon S3的子集</li>
<li>Swift兼容接口： OpenStack Swift的子集</li>
</ol>
<p>这两套接口可以<span style="background-color: #c0c0c0;">混合使用</span>。</p>
<p>对象存储网关由守护程序radosgw负责，它作为客户端和Ceph存储集群之间的媒介。radosgw<span style="background-color: #c0c0c0;">具有自己的用户管理系统</span>。</p>
<p>从firefly版本开始，对象存储网关在Civetweb上运行，Civetweb内嵌在ceph-radosw这个Daemon中。在老版本中，对象网关基于Apache+FastCGI。</p>
<div class="blog_h2"><span class="graybg">Dashboard</span></div>
<p>Ceph仪表盘是一个内置的、基于Web的管理/监控工具。通过它你能够管理集群中各种资源。仪表盘作为Ceph Manager的模块实现。</p>
<div class="blog_h1"><span class="graybg">命令</span></div>
<div class="blog_h2"><span class="graybg">ceph orch</span></div>
<p>包含一系列集群编排有关的命令。</p>
<div class="blog_h3"><span class="graybg">orch ls</span></div>
<p>列出对编排器可见的服务：</p>
<pre class="crayon-plain-tag">ceph orch ls [&lt;service_type&gt;] [&lt;service_name&gt;] [--export] [plain|json|json-pretty|yaml] [--refresh]

ceph orch ls
# 守护进程类型              数量                     归置规则               使用的镜像
NAME                       RUNNING  REFRESHED  AGE  PLACEMENT             IMAGE NAME                            IMAGE ID      
alertmanager                   1/1  77s ago    2w   count:1               docker.io/prom/alertmanager:v0.20.0   0881eb8f169f  
crash                          2/3  79s ago    2w   *                     docker.io/ceph/ceph:v15               mix           
grafana                        1/1  77s ago    2w   count:1               docker.io/ceph/ceph-grafana:6.7.4     80728b29ad3f  
mds.cephfs                     2/3  79s ago    2w   ceph-1;ceph-2;ceph-3  docker.io/ceph/ceph:v15               mix           
mgr                            1/1  77s ago    2w   ceph-1                docker.io/ceph/ceph:v15               5b724076c58f  
mon                            2/3  79s ago    40m  count:3               docker.io/ceph/ceph:v15               mix           
nfs.ganesha                    1/1  78s ago    7d   count:1               docker.io/ceph/ceph:v15               5b724076c58f  
node-exporter                  2/3  79s ago    2w   *                     docker.io/prom/node-exporter:v0.18.1  mix           
osd.all-available-devices      2/3  79s ago    2w   *                     docker.io/ceph/ceph:v15               mix           
prometheus                     1/1  77s ago    2w   count:1               docker.io/prom/prometheus:v2.18.1     de242295e225  
rgw.china.zircon               2/3  79s ago    2w   count:3               docker.io/ceph/ceph:v15               mix</pre>
<div class="blog_h3"><span class="graybg">orch ps</span></div>
<p>列出对编排器可见的守护进程，守护进程是服务的实例：</p>
<pre class="crayon-plain-tag">orch ps [&lt;hostname&gt;] [&lt;service_name&gt;] [&lt;daemon_type&gt;] [&lt;daemon_id&gt;] [plain|json|json-pretty|yaml] [--refresh] 

ceph orch ps
NAME                            HOST    STATUS         REFRESHED  AGE  VERSION    IMAGE NAME                            IMAGE ID      CONTAINER ID  
alertmanager.ceph-1             ceph-1  running (69m)  3m ago     2w   0.20.0     docker.io/prom/alertmanager:v0.20.0   0881eb8f169f  bef9ab4dcc98  
crash.ceph-1                    ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  3bb0c129d4d4  
crash.ceph-2                    ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/ceph/ceph:v15               &lt;unknown&gt;     &lt;unknown&gt;     
crash.ceph-3                    ceph-3  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  f5c22d2c854b  
grafana.ceph-1                  ceph-1  running (69m)  3m ago     2w   6.7.4      docker.io/ceph/ceph-grafana:6.7.4     80728b29ad3f  17d84abdd9e6  
mds.cephfs.ceph-1.nivqqf        ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  4be1504a4c6f  
mds.cephfs.ceph-2.djnipz        ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/ceph/ceph:v15               &lt;unknown&gt;     &lt;unknown&gt;     
mds.cephfs.ceph-3.cgngbk        ceph-3  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  7e514989bc6c  
mgr.ceph-1.adpioc               ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  a66dd815c2b1  
mon.ceph-1                      ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  0c87ed6da097  
mon.ceph-2                      ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/ceph/ceph:v15               &lt;unknown&gt;     &lt;unknown&gt;     
mon.ceph-3                      ceph-3  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  836ec2a7c34d  
nfs.ganesha.ceph-3              ceph-3  running (68m)  3m ago     7d   3.3        docker.io/ceph/ceph:v15               5b724076c58f  440a1bcef7c5  
node-exporter.ceph-1            ceph-1  running (69m)  3m ago     2w   0.18.1     docker.io/prom/node-exporter:v0.18.1  e5a616e4b9cf  26bf34b93188  
node-exporter.ceph-2            ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/prom/node-exporter:v0.18.1  &lt;unknown&gt;     &lt;unknown&gt;     
node-exporter.ceph-3            ceph-3  running (69m)  3m ago     2w   0.18.1     docker.io/prom/node-exporter:v0.18.1  e5a616e4b9cf  fb60a5b31bfd  
osd.0                           ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/ceph/ceph:v15               &lt;unknown&gt;     &lt;unknown&gt;     
osd.1                           ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  49cad5daf8f8  
osd.2                           ceph-3  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  17ef075e16a4  
prometheus.ceph-1               ceph-1  running (69m)  3m ago     2w   2.18.1     docker.io/prom/prometheus:v2.18.1     de242295e225  7b61f27c6a0e  
rgw.china.zircon.ceph-1.dsctvb  ceph-1  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  b7d6166aae36  
rgw.china.zircon.ceph-2.ulzfto  ceph-2  error          3m ago     2w   &lt;unknown&gt;  docker.io/ceph/ceph:v15               &lt;unknown&gt;     &lt;unknown&gt;     
rgw.china.zircon.ceph-3.qjhszd  ceph-3  running (69m)  3m ago     2w   15.2.10    docker.io/ceph/ceph:v15               5b724076c58f  5d1d6d6e6899</pre>
<div class="blog_h3"><span class="graybg">orch apply</span></div>
<p>设置某种组件（服务/守护进程）的数量或者归置规则，格式：</p>
<pre class="crayon-plain-tag"># 更新守护程序副本数量、归置规则，或者apply一段YAML格式的配置
orch apply [mon|mgr|rbd-mirror|crash|alertmanager|grafana|node-exporter|prometheus] [&lt;placement&gt;]  
 [--dry-run] [plain|json|json-pretty|yaml] [--unmanaged] 

# 扩缩容iSCSI服务
orch apply iscsi &lt;pool&gt; &lt;api_user&gt; &lt;api_password&gt; [&lt;trusted_ip_list&gt;] [&lt;placement&gt;] [--dry-run]
 [plain|json|json-pretty|yaml] [--unmanaged]  

# 更新指定fs_name的MDS实例数
orch apply mds &lt;fs_name&gt; [&lt;placement&gt;] [--dry-run] [--unmanaged] [plain|json|json-pretty|yaml]

# 扩缩容NFS服务
orch apply nfs &lt;svc_id&gt; &lt;pool&gt; [&lt;namespace&gt;] [&lt;placement&gt;] [--dry-run] [plain|json|json-pretty|
 yaml] [--unmanaged] 

# 创建OSD守护进程
orch apply osd [--all-available-devices] [--dry-run] [--unmanaged] [plain|json|json-pretty|yaml]

# 为指定的Zone更新RGW实例的数量
orch apply rgw &lt;realm_name&gt; &lt;zone_name&gt; [&lt;subcluster&gt;] [&lt;port:int&gt;] [--ssl] [&lt;placement&gt;] 
[--dry-run] [plain|json|json-pretty|yaml] [--unmanaged]</pre>
<p>下面是一些简单的例子：</p>
<pre class="crayon-plain-tag"># 指定副本数
ceph orch apply mon 3
# 制定归置规则
ceph orch apply mon ceph-1 ceph-2 ceph-3

# 为所有空闲设备创建OSD
ceph orch apply osd --all-available-devices</pre>
<div class="blog_h3"><span class="graybg">orch daemon<br /></span></div>
<p>管理守护进程。</p>
<p>add子命令，添加一个守护进程：</p>
<pre class="crayon-plain-tag"># 添加守护进程
ceph orch daemon add [mon|mgr|rbd-mirror|crash|alertmanager|grafana|node-exporter|prometheus [&lt;placement&gt;]

ceph orch daemon add iscsi &lt;pool&gt; &lt;api_user&gt; &lt;api_password&gt; [&lt;trusted_ip_list&gt;] [&lt;placement&gt;]
ceph orch daemon add mds &lt;fs_name&gt; [&lt;placement&gt;] 
ceph orch daemon add nfs &lt;svc_id&gt; &lt;pool&gt; [&lt;namespace&gt;] [&lt;placement&gt;]
ceph orch daemon add osd [&lt;svc_arg&gt;]
ceph orch daemon add rgw &lt;realm_name&gt; &lt;zone_name&gt; [&lt;subcluster&gt;] [&lt;port:int&gt;] [--ssl] [&lt;placement&gt;]</pre>
<p>redeploy子命令，重新部署某个守护进程，可以指定使用的镜像：</p>
<pre class="crayon-plain-tag">ceph orch daemon redeploy &lt;name&gt; [&lt;image&gt;]</pre>
<p>如果节点上的守护进程容器被意外删除，也就是<pre class="crayon-plain-tag">podman ps</pre>看不到对应容器，可以使用redeploy命令重新部署。</p>
<p>rm子命令，删除某个守护进程：</p>
<pre class="crayon-plain-tag">ceph orch daemon rm &lt;names&gt;... [--force]</pre>
<p>你也可以启动、停止、重启、重新配置某个守护进程：</p>
<pre class="crayon-plain-tag">ceph orch daemon start|stop|restart|reconfig &lt;name&gt;</pre>
<p>如果需要启动、停止、重启、重新配置<span style="background-color: #c0c0c0;">某种服务的所有守护进程</span>：</p>
<pre class="crayon-plain-tag">ceph orch start|stop|restart|redeploy|reconfig &lt;service_name&gt; </pre>
<div class="blog_h3"><span class="graybg">orch device </span></div>
<p>管理块设备。</p>
<p>显示某些主机上的块设备：</p>
<pre class="crayon-plain-tag">ceph orch device ls [&lt;hostname&gt;...] [plain|json|json-pretty|yaml] [--refresh] [--wide]</pre>
<p>清除块设备上的内容：</p>
<pre class="crayon-plain-tag">ceph orch device zap &lt;hostname&gt; &lt;path&gt; [--force]</pre>
<div class="blog_h3"><span class="graybg">orch host</span></div>
<p>管理主机。</p>
<p>添加主机，可选的，添加标签：</p>
<pre class="crayon-plain-tag">ceph orch host add &lt;hostname&gt; [&lt;addr&gt;] [&lt;labels&gt;...]</pre>
<p>为主机添加/移除标签：</p>
<pre class="crayon-plain-tag">ceph orch host label add &lt;hostname&gt; &lt;label&gt;
ceph orch host label rm &lt;hostname&gt; &lt;label&gt;</pre>
<p>列出主机：</p>
<pre class="crayon-plain-tag">ceph orch host ls [plain|json|json-pretty|yaml]</pre>
<p>检查是否可以在不损害可用性的前提下，停止主机：</p>
<pre class="crayon-plain-tag">ceph orch host ok-to-stop &lt;hostname&gt;</pre>
<p>删除主机：</p>
<pre class="crayon-plain-tag">ceph orch host rm &lt;hostname&gt;</pre>
<p>修改主机地址：</p>
<pre class="crayon-plain-tag">ceph orch host set-addr &lt;hostname&gt; &lt;addr&gt;</pre>
<div class="blog_h3"><span class="graybg">orch osd rm</span></div>
<p>删除OSD实例：</p>
<pre class="crayon-plain-tag">ceph orch osd rm &lt;svc_id&gt;... [--replace] [--force]</pre>
<p>检查删除OSD操作的进度：</p>
<pre class="crayon-plain-tag">ceph orch osd rm status [plain|json|json-pretty|yaml]</pre>
<div class="blog_h3"><span class="graybg"> orch pause</span></div>
<p>暂停编排器的后台任务</p>
<div class="blog_h3"><span class="graybg">orch resume</span></div>
<p>恢复暂停的编排器后台任务</p>
<div class="blog_h3"><span class="graybg">orch set backend</span></div>
<p>选择编排器后端：<pre class="crayon-plain-tag">ceph orch set backend &lt;module_name&gt;</pre></p>
<div class="blog_h3"><span class="graybg">orch status</span></div>
<p>显示使用的编排器后端，以及它的状态：</p>
<pre class="crayon-plain-tag">ceph orch status [plain|json|json-pretty|yaml]

ceph orch status
Backend: cephadm
Available: True</pre>
<p>cephadm就是一个编排器后端，下文会有介绍。</p>
<div class="blog_h3"><span class="graybg">orch upgrade</span></div>
<p>升级相关操作：</p>
<pre class="crayon-plain-tag">orch upgrade check [&lt;image&gt;] [&lt;ceph_version&gt;]    # 检查镜像可用版本
orch upgrade pause                               # 暂停升级
orch upgrade resume                              # 恢复暂停的省级
orch upgrade start [&lt;image&gt;] [&lt;ceph_version&gt;]    # 触发升级
orch upgrade status                              # 升级状态
orch upgrade stop                                # 停止进行中的升级</pre>
<div class="blog_h2"><span class="graybg">ceph log </span></div>
<p>查看日志：</p>
<pre class="crayon-plain-tag">ceph log last [&lt;num:int&gt;] [debug|info|sec|warn|error] [*|cluster|audit|cephadm]

# 查看cephadm的最新日志
ceph log last cephadm</pre>
<div class="blog_h1"><span class="graybg">通过cephadm部署</span></div>
<p>Cephadm是最新的Ceph部署工具，他利用容器和Systemd，仅仅支持Octopus或者更新的版本。</p>
<p>Cephadm的外部依赖包括容器运行时（Docker或者Podman），以及Python3。</p>
<div class="blog_h2"><span class="graybg">安装Cephadm</span></div>
<pre class="crayon-plain-tag">cd /tmp
curl --silent --remote-name --location https://github.com/ceph/ceph/raw/octopus/src/cephadm/cephadm
chmod +x cephadm</pre>
<p>安装支持cephadm命令及其依赖：</p>
<pre class="crayon-plain-tag">./cephadm add-repo --release octopus
./cephadm install</pre>
<div class="blog_h2"><span class="graybg">单节点自举</span></div>
<pre class="crayon-plain-tag">cephadm bootstrap --mon-ip 10.0.2.1
cephadm shell -- ceph -s</pre>
<p>上述命令会安装一个单MON节点的Ceph集群。改命令会：</p>
<ol>
<li>创建MON和MGR守护进程到本机</li>
<li>为Ceph集群生成SSH密钥，并添加到roo用户的/root/.ssh/authorized_keys</li>
<li>生成最小化配置的 /etc/ceph/ceph.conf，用于和新集群通信</li>
<li>生成Ceph管理密钥 /etc/ceph/ceph.client.admin.keyring</li>
<li>复制公钥副本到/etc/ceph/ceph.pub</li>
</ol>
<div class="blog_h2"><span class="graybg">安装Ceph Common</span></div>
<pre class="crayon-plain-tag">cephadm install ceph-common</pre>
<p>这样你就可以直接使用ceph命令了：</p>
<pre class="crayon-plain-tag">ceph status</pre>
<div class="blog_h2"><span class="graybg">添加节点</span></div>
<p>你需要提前为节点安装好Python3</p>
<p>列出现有节点：</p>
<pre class="crayon-plain-tag">ceph orch host ls</pre>
<p>orch子命令用于Ceph集群相关的编排。</p>
<p>将集群的公钥安装到新节点的authorized_keys：</p>
<pre class="crayon-plain-tag">ssh-copy-id -f -i /etc/ceph/ceph.pub root@10.0.2.2
ssh-copy-id -f -i /etc/ceph/ceph.pub root@10.0.2.3</pre>
<p>添加节点，注意要提供主机名：</p>
<pre class="crayon-plain-tag">ceph orch host add ceph-2
ceph orch host add ceph-3</pre>
<div class="blog_h3"><span class="graybg">修改节点地址</span></div>
<pre class="crayon-plain-tag">ceph orch host set-addr ceph-1 ceph-1.gmem.cc </pre>
<div class="blog_h2"><span class="graybg">添加MON</span></div>
<pre class="crayon-plain-tag"># 设置哪些子网中的主机可以作为MON
ceph config set mon public_network 10.0.2.0/24

# 确保三个MON
ceph orch apply mon 3

# 你也可以强制指定在哪些主机上部署MON
ceph orch apply mon ceph-1 ceph-2 ceph-3</pre>
<div class="blog_h2"><span class="graybg">添加OSD</span></div>
<p>使用下面的命令，可以将集群主机所有空闲设备作为OSD：</p>
<pre class="crayon-plain-tag">ceph orch apply osd --all-available-devices</pre>
<p>将特定主机的特定磁盘作为OSD：</p>
<pre class="crayon-plain-tag">ceph orch daemon add osd ceph-1:/dev/vdb</pre>
<div class="blog_h2"><span class="graybg">部署CephFS</span></div>
<pre class="crayon-plain-tag">ceph fs volume create cephfs --placement="ceph-1 ceph-2 ceph-3"</pre>
<div class="blog_h2"><span class="graybg">部署RGW</span></div>
<pre class="crayon-plain-tag">ceph orch apply rgw china beijing '--placement=3' --port=80</pre>
<div class="blog_h2"><span class="graybg">部署NFS</span></div>
<p>NFS Ganesha是一个用户模式的NFS，支持v3  4.0 4.1 4.2，可以同时运行这些协议。</p>
<p>使用下面的命令来部署NFS Ganesha网关。</p>
<pre class="crayon-plain-tag"># 为NFS创建存储池
ceph osd pool create nfs-ganesha 64 replicated
#                   服务ID  存储池      命名空间
ceph orch apply nfs ganesha nfs-ganesha china</pre>
<div class="blog_h1"><span class="graybg">通过ceph-deploy部署</span></div>
<p>&nbsp;</p>
<p><span class="graybg">ceph-deploy</span>是一个ceph部署工具，服务器只需要提供SSH、sudo、一些Python包即可完成ceph的安装部署。</p>
<p>准备好服务器集群后，选取一台作为管理主机，在其上执行：</p>
<pre class="crayon-plain-tag">wget -q -O- 'https://download.ceph.com/keys/release.asc' | sudo apt-key add -
#                                  debian-luminous
echo deb https://download.ceph.com/debian-jewel/ $(lsb_release -sc) main | sudo tee /etc/apt/sources.list.d/ceph.list
sudo apt-get update
sudo apt-get install ceph-deploy


# 也可以通过pip安装
apt install python-pip
pip install ceph-deploy


# BUG太多，直接Git最新源码安装吧
git clone https://github.com/ceph/ceph-deploy.git
cd ceph-deploy
chmod +x setup.py 
python setup.py install</pre>
<div class="blog_h2"><span class="graybg">安装NTP客户端</span></div>
<p>为了防止时钟不同步导致问题，建议<a href="/ntp-under-ubuntu">安装NTP客户端</a>，并保持和NTP服务器的同步。</p>
<div class="blog_h2"><span class="graybg">创建部署用户</span></div>
<pre class="crayon-plain-tag"># 在所有节点上执行
ansible k8s -m raw -a 'useradd -d /home/ceph-ops -m ceph-ops'
ansible k8s -m raw -a 'echo "ceph-ops ALL = (root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ceph-ops'
ansible k8s -m raw -a 'chmod 0440 /etc/sudoers.d/ceph-ops'
ansible k8s -m raw -a 'echo "ceph-ops:password" | chpasswd'

# 在管理节点上执行
# 生成密钥对，默认生成~/.ssh下的id_rsa和id_rsa.pub
ssh-keygen
# 将公钥拷贝到被管理机，便于免密码登陆
ansible k8s -m raw -a "mkdir /home/ceph-ops/.ssh"
ansible k8s -m raw -a "scp -oStrictHostKeyChecking=no root@master-node:/root/.ssh/id_rsa.pub /home/ceph-ops/.ssh/authorized_keys"
ansible k8s -m raw -a "chown -R ceph-ops:ceph-ops /home/ceph-ops/.ssh"</pre>
<div class="blog_h2"><span class="graybg">安装ceph</span></div>
<p>在集群中的管理主机上，执行：</p>
<pre class="crayon-plain-tag">ceph-deploy install {hostname [hostname] ...} --release {code-name}
# 示例：
ceph-deploy --username ceph-ops install Carbon Radon Neon Boron Xenon --release jewel
ceph-deploy --username ceph-ops install Carbon Radon Neon Boron Xenon  --release luminous


# 如果网络速度太慢，ceph-deploy会提前退出。这种情况下手工、通过代理安装为好
export http_proxy=http://10.0.0.1:8087
export https_proxy=http://10.0.0.1:8087

# 实际上就是安装这些软件
apt install ceph ceph-osd ceph-mds ceph-mon radosgw</pre>
<div class="blog_h2"><span class="graybg">卸载ceph</span></div>
<pre class="crayon-plain-tag"># 卸载软件
ceph-deploy uninstall {hostname [hostname] ...}
# 示例：
ceph-deploy --username ceph-ops uninstall  Carbon Radon Neon

# 下面的命令可以在Ubuntu上执行，清除配置文件
ceph-deploy purge {hostname [hostname] ...}
# 示例：
ceph-deploy --username ceph-ops purge  Carbon Radon Neon</pre>
<div class="blog_h2"><span class="graybg">创建集群</span></div>
<div class="blog_h3"><span class="graybg">临时配置目录</span></div>
<pre class="crayon-plain-tag"># 在管理节点上执行：
# 创建一个目录，存放ceph-deploy生成的配置文件
mkdir /tmp/ceph
cd /tmp/ceph</pre>
<div class="blog_h3"><span class="graybg">新建集群</span></div>
<pre class="crayon-plain-tag"># 创建一个新集群，host为mon节点
ceph-deploy --cluster {cluster-name} new {host [host], ...}

# 示例
ceph-deploy --username ceph-ops new Xenon</pre>
<div class="blog_h3"><span class="graybg">分发配置文件</span></div>
<pre class="crayon-plain-tag"># 修改好当前目录的ceph.conf，执行下面的命令，分发到所有节点的/etc/ceph目录
# ceph.conf至少要提供网络配置
# public network = 10.0.0.0/16
# cluster network = 10.0.0.0/16

# 示例
ceph-deploy --username ceph-ops --overwrite-conf config push Carbon Radon Neon Boron Xenon</pre>
<div class="blog_h3"><span class="graybg">配置文件示例</span></div>
<pre class="crayon-plain-tag">[client]
rbd_cache = true
rbd_cache_max_dirty = 25165824
rbd_cache_max_dirty_age = 5
rbd_cache_size = 268435456

[global]
fsid = 9b92d057-a4bc-473e-b6ab-462092fcf205
max_open_files = 131072
mon_initial_members = Carbon, Radon, Neon
mon_host = 10.0.0.100,10.0.1.1,10.0.2.1
osd pool default min size = 1
osd pool default pg num = 384
osd pool default pgp num = 384
osd pool default size = 2
mon_max_pg_per_osd = 256
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx

[mon]
mon_allow_pool_delete = true

[osd]
public network = 10.0.0.0/16
cluster network = 10.0.0.0/16
filestore max sync interval = 15
filestore min sync interval = 10
filestore op thread = 32
journal max write bytes = 1073714824
journal max write entries = 10000
journal queue max bytes = 10485760000
journal queue max ops = 50000
ms_bind_port_max = 7100
osd_client_message_size_cap = 2147483648
osd_crush_update_on_start = true
osd_deep_scrub_stride = 131072
osd_disk_threads = 4
osd_journal_size = 10240
osd_map_cache_bl_size = 128
osd_max_backfills = 4
osd_max_object_name_len = 256
osd_max_object_namespace_len = 64
osd_max_write_size = 512
osd_op_threads = 8
osd_recovery_op_priority = 4</pre>
<div class="blog_h2"><span class="graybg">初始mon</span></div>
<p>执行下面的命令，部署初始mon节点，并收集key：</p>
<pre class="crayon-plain-tag">ceph-deploy --username ceph-ops mon create-initial

# 将在当前目录生成以下文件：
# ceph.client.admin.keyring
# ceph.bootstrap-mgr.keyring
# ceph.bootstrap-osd.keyring
# ceph.bootstrap-mds.keyring
# ceph.bootstrap-rgw.keyring
# ceph.bootstrap-rbd.keyring

# 目标主机的/etc/ceph/ceph.conf被创建，如果此文件已经存在，你必须用--overwrite-conf选项重新运行上述命令</pre>
<div class="blog_h2"><span class="graybg">增减mon</span></div>
<pre class="crayon-plain-tag"># 增加
ceph-deploy mon create {host-name [host-name]...}
# 删除
ceph-deploy mon destroy {host-name [host-name]...}

# 示例：
ceph-deploy --username ceph-ops mon create Radon
ceph-deploy --username ceph-ops mon create Carbon</pre>
<div class="blog_h2"><span class="graybg">密钥收集</span></div>
<p>以ceph-deploy作为工具，将一台主机作为OSD或MDS时，需要收集MON、OSD、MDS的初始keyring：</p>
<pre class="crayon-plain-tag">ceph-deploy gatherkeys {monitor-host}

# 示例：
ceph-deploy --username ceph-ops gatherkeys Carbon Radon Neon Boron Xenon</pre>
<p>不再使用ceph-deploy或者另外建立一个新集群时，需要删除管理主机、本地目录的密钥：</p>
<pre class="crayon-plain-tag">ceph-deploy forgetkeys</pre>
<div class="blog_h2"><span class="graybg">增加OSD </span></div>
<div class="blog_h3"><span class="graybg">列出磁盘</span></div>
<pre class="crayon-plain-tag">ceph-deploy disk list {node-name [node-name]...}
# 示例：
ceph-deploy --username ceph-ops disk list Carbon Radon Neon Boron Xenon</pre>
<div class="blog_h3"><span class="graybg">擦净分区</span></div>
<p>下面的命令可以擦净（删除分区表）磁盘，以供Ceph使用 ： </p>
<pre class="crayon-plain-tag">ceph-deploy disk zap {osd-server-name} {disk-name}
# 示例（luminous）：
ceph-deploy --username ceph-ops disk zap xenial-100 /dev/vdb
ceph-deploy --username ceph-ops disk zap xenial-100 /dev/vdc
ceph-deploy --username ceph-ops disk zap Carbon     /dev/sdb
ceph-deploy --username ceph-ops disk zap Radon      /dev/sdb
ceph-deploy --username ceph-ops disk zap Neon       /dev/sdb
# 示例（jewel）：
ceph-deploy --username ceph-ops disk zap xenial-100:vdb
ceph-deploy --username ceph-ops disk zap xenial-100:vdc
ceph-deploy --username ceph-ops disk zap Carbon:sdb
ceph-deploy --username ceph-ops disk zap Radon:sdb
ceph-deploy --username ceph-ops disk zap Neon:sdb</pre>
<p>警告：所有数据会被删除。</p>
<div class="blog_h3"><span class="graybg">准备OSD</span></div>
<p>此命令在ceph-deploy 2.0.0中已经废除</p>
<p>使用prepare命令来准备磁盘，会自动创建分区。在大部分OS中，<span style="background-color: #c0c0c0;">activate会随后自动执行</span></p>
<pre class="crayon-plain-tag">ceph-deploy osd prepare {node-name}:{data-disk}[:{journal-disk}]
# 示例：
ceph-deploy --username ceph-ops osd prepare --fs-type xfs xenial-100:vdb
ceph-deploy --username ceph-ops osd prepare --fs-type xfs xenial-100:vdc
ceph-deploy --username ceph-ops osd prepare --fs-type xfs Carbon:sdb
ceph-deploy --username ceph-ops osd prepare --fs-type xfs Radon:sdb</pre>
<p>建议：将日志存储在独立磁盘中以最优化性能，如果将日志和数据存储在一起，会有损性能。</p>
<div class="blog_h3"><span class="graybg">激活OSD</span></div>
<p>此命令在ceph-deploy 2.0.0中已经废除</p>
<p>很多操作系统上，不需要手工激活OSD。</p>
<pre class="crayon-plain-tag">ceph-deploy osd activate {node-name}:{data-disk-partition}[:{journal-disk-partition}]
# 示例
ceph-deploy --username ceph-ops osd activate xenial-100:vdb
ceph-deploy --username ceph-ops osd activate xenial-100:vdc
ceph-deploy --username ceph-ops osd activate Carbon:sdb
ceph-deploy --username ceph-ops osd activate Radon:sdb</pre>
<p>激活之后，系统运行ceph-osd进程，OSD进入up+in状态。</p>
<div class="blog_h3"><span class="graybg">创建OSD </span></div>
<p>即prepare + activate：</p>
<pre class="crayon-plain-tag">ceph-deploy osd create {node-name}:{disk}[:{path/to/journal}]
# 示例
ceph-deploy osd create osdserver1:sdb:/dev/ssd1

# 示例（luminous）：
ceph-deploy --username ceph-ops osd create --data /dev/vdb --bluestore xenial-100  
ceph-deploy --username ceph-ops osd create --data /dev/vdc --bluestore xenial-100  
ceph-deploy --username ceph-ops osd create --data /dev/sdb --bluestore Carbon
ceph-deploy --username ceph-ops osd create --data /dev/sdb --bluestore Radon
# 指定分区也可以
ceph-deploy --username ceph-ops osd create --data /dev/sda3 --bluestore Carbon</pre>
<div class="blog_h2"><span class="graybg">增加MDS</span></div>
<pre class="crayon-plain-tag">ceph-deploy --username ceph-ops mds create Carbon</pre>
<div class="blog_h2"><span class="graybg">部署MGR</span></div>
<p>仅仅luminous支持，否则报错Error EACCES: access denied could not create mgr</p>
<pre class="crayon-plain-tag">ceph-deploy --username ceph-ops mgr create Carbon</pre>
<div class="blog_h2"><span class="graybg">部署RGW</span></div>
<pre class="crayon-plain-tag">ceph-deploy --username ceph-ops rgw create Radon</pre>
<p>默认情况下RGW监听7480端口，你可以验证RGW是否正常工作：</p>
<pre class="crayon-plain-tag">curl http://Carbon:7480</pre>
<div class="blog_h2"><span class="graybg">清除主机</span></div>
<p>如果只想清除 /var/lib/ceph 下的数据、并保留 Ceph 安装包，可以：</p>
<pre class="crayon-plain-tag">ceph-deploy purgedata {hostname} [{hostname} ...]
# 示例：
ceph-deploy --username ceph-ops purgedata Carbon Radon</pre>
<p>如果向同时清除数据、Ceph安装包：</p>
<pre class="crayon-plain-tag">ceph-deploy purge {hostname} [{hostname} ...]</pre>
<div class="blog_h2"><span class="graybg">管理和分发</span></div>
<div class="blog_h3"><span class="graybg">管理主机</span></div>
<p>要允许某些主机以管理员权限执行 Ceph 命令，可以：</p>
<pre class="crayon-plain-tag">ceph-deploy admin {host-name [host-name]...}

# 示例：
ceph-deploy --username ceph-ops admin Carbon Radon Neon Boron Xenon</pre>
<p>上述命令执行后，当前目录中的ceph.client.admin.keyring被分发到所有指定的主机，在这些主机上可以执行ceph -s命令了。</p>
<div class="blog_h3"><span class="graybg">分发配置文件</span></div>
<p>要将修改过的、当前目录下的配置文件分发给集群内其它主机，可以：</p>
<pre class="crayon-plain-tag">ceph-deploy config push {host-name [host-name]...}</pre>
<div class="blog_h3"><span class="graybg">拉取配置文件 </span></div>
<p>要获取某台主机的配置文件，可以：</p>
<pre class="crayon-plain-tag">ceph-deploy config pull {host-name [host-name]...} </pre>
<div class="blog_h1"><span class="graybg">配置</span></div>
<p>启动Ceph服务时，初始化进程会启动一系列守护进程，这些进程至少包含两类：</p>
<ol>
<li>ceph-mon 监控进程</li>
<li>ceph-osd  Ceph OSD的守护进程</li>
</ol>
<p>要使用Ceph文件系统功能，则需要额外运行：</p>
<ol>
<li>ceph-mds Ceph元数据服务</li>
</ol>
<p>要使用Ceph对象存储功能，则需要额外运行：</p>
<ol>
<li>ceph-rgw RADOS网关守护进程 </li>
</ol>
<div class="blog_h2"><span class="graybg">运行时配置</span></div>
<p>要列出当前使用的所有配置值，可以访问守护进程的管理套接字。</p>
<p>在节点上执行下面的命令，获得管理套接字的位置：</p>
<pre class="crayon-plain-tag">ceph-conf --name mon.$(hostname -s) --show-config-value admin_socket
# /var/run/ceph/ceph-mon.a.asok</pre>
<p>调用下面的命令列出所有配置：</p>
<pre class="crayon-plain-tag">ceph daemon /var/run/ceph/ceph-mon.a.asok config show </pre>
<p>对于osd或者其它守护进程，也可以使用上述方式获取运行时配置。</p>
<div class="blog_h2"><span class="graybg">配置文件</span></div>
<p>所有守护进程从同一个配置文件ceph.conf中检索自己感兴趣的信息。该配置文件中包含了集群身份、认证配置、集群成员、主机名、主机 IP 地址、Keyring路径、日志路径、数据路径，以及其它运行时选项。</p>
<p>按照以下顺序来搜索，后面的可以覆盖前面的：</p>
<ol>
<li>编译进二进制文件的默认值</li>
<li>ceph-mon的集群中心配置数据库</li>
<li>本机上的配置文件：
<ol>
<li>/etc/ceph/ceph.conf</li>
<li>~/.ceph/config</li>
<li>./ceph.conf</li>
</ol>
</li>
<li>环境变量：$CEPH_CONF </li>
<li>命令行参数：-c path/path</li>
<li>管理员在运行时设置的选项</li>
</ol>
<p>一个Ceph进程启动时，它会先从命令行、环境变量、本地配置文件收集配置项，然后连接ceph-mon读取集群中心配置信息，然后启动。</p>
<div class="blog_h2"><span class="graybg">配置项格式</span></div>
<p>配置项的名称唯一，标准格式是小写字母 + 下划线</p>
<p>在命令行中指定配置项时，下划线_可以替换为<span style="background-color: #c0c0c0;">短横线</span></p>
<p>在配置文件中指定配置项时，下划线可以替换为<span style="background-color: #c0c0c0;">空格或短横线</span></p>
<div class="blog_h2"><span class="graybg" style="background-color: #c0c0c0;">配置项列表</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 30%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2"><em>公共选项 [global]</em></td>
</tr>
<tr>
<td>host</td>
<td>用于指定节点的主机名</td>
</tr>
<tr>
<td>mon host</td>
<td>指定mon节点的地址，逗号分隔</td>
</tr>
<tr>
<td>auth cluster required</td>
<td>集群身份验证设置，默认值cephx。如果启用，集群守护进程之间必须相互验证身份</td>
</tr>
<tr>
<td>auth service required</td>
<td>服务身份验证设置，默认值cephx。如果启用，服务端需要验证客户端身份</td>
</tr>
<tr>
<td>auth client required</td>
<td>客户端身份验证设置，默认值cephx。如果启用，客户端需要验证服务端身份</td>
</tr>
<tr>
<td>keyring</td>
<td>
<p>钥匙串的位置</p>
<p>默认值：/etc/ceph/$cluster.$name.keyring,/etc/ceph/$cluster.keyring,/etc/ceph/keyring,/etc/ceph/keyring.bin</p>
</td>
</tr>
<tr>
<td>public network</td>
<td>公共网络配置，CIDR格式，多个则用逗号分隔</td>
</tr>
<tr>
<td><strong>cluster network</strong></td>
<td>
<p>集群网络配置，CIDR格式，多个则用逗号分隔</p>
<p>如果配置了集群网， OSD 将把<span style="background-color: #c0c0c0;">心跳、对象复制和恢复流量</span>路由到集群网</p>
</td>
</tr>
<tr>
<td>fsid</td>
<td>存储集群的唯一标识，便于允许在同一套硬件上部署多个集群</td>
</tr>
<tr>
<td><strong>max open files</strong></td>
<td>
<p>设置操作系统级的 max open fds</p>
<p>建议值：131072</p>
</td>
</tr>
<tr>
<td>fatal signal handlers</td>
<td>如果设置为true，则安装 SEGV 、 ABRT 、 BUS 、 ILL 、 FPE 、 XCPU 、 XFSZ 、 SYS 信号处理器，用于产生有用的日志信息</td>
</tr>
<tr>
<td>chdir</td>
<td>进程一旦启动、运行就进入这个目录。默认 /</td>
</tr>
<tr>
<td colspan="2"><em>mon选项  [mon]</em></td>
</tr>
<tr>
<td>mon addr</td>
<td>
<p>监听地址:端口，可以针对每个mon.$id段分别配置，或者在mon段下配置：</p>
<p style="padding-left: 30px;">10.0.0.10:6789,10.0.0.11:6789,10.0.0.12:6789</p>
</td>
</tr>
<tr>
<td>mon data</td>
<td>mon存储数据的路径，默认/var/lib/ceph/mon/$cluster-$id</td>
</tr>
<tr>
<td>mon initial members</td>
<td>
<p>集群初始化监视器ID，逗号分隔。这些MON必须在线以建立quorum，正确设置此参数可能让集群更快的可用</p>
</td>
</tr>
<tr>
<td>
<p>mon osd full ratio</p>
</td>
<td>
<p>磁盘利用率总计多少认为满了，默认 .95</p>
<p>当Ceph集群利用率达到此比率时，作为<span style="background-color: #c0c0c0;">防止数据丢失的安全措施，它会阻止你读写 OSD</span></p>
<p>注意：</p>
<ol>
<li>删除OSD、OSD out都会导致利用率增加，甚至超过full ratio导致锁死</li>
<li>如果一些 OSD 快满了，但其他的仍有足够空间，你可能配错了CRUSH权重</li>
</ol>
</td>
</tr>
<tr>
<td>mon osd nearfull ratio</td>
<td>磁盘利用率总计多少认为快满了，默认.85</td>
</tr>
<tr>
<td>mon sync timeout</td>
<td>mon从其provider获取下一个更新消息的超时</td>
</tr>
<tr>
<td>mon tick interval</td>
<td>监视器的心跳间隔，单位为秒。默认5</td>
</tr>
<tr>
<td>mon clock drift allowed</td>
<td>监视器间允许的时钟漂移量。默认.050</td>
</tr>
<tr>
<td>mon timecheck interval</td>
<td>和 leader 的时间偏移检查间隔。默认300秒</td>
</tr>
<tr>
<td>mon osd min down reports</td>
<td>OSD连续多少次向mon报告某个OSD宕掉，mon才采纳，默认3</td>
</tr>
<tr>
<td>mon osd min down reporters</td>
<td>类似上面，mon要求多少个OSD都报告某个OSD宕掉，才采纳，默认1</td>
</tr>
<tr>
<td>mon osd min up ratio</td>
<td>把 OSD 标记为 down 前，保持处于 up 状态的 OSD 最小比例。默认.3</td>
</tr>
<tr>
<td>mon osd min in ratio</td>
<td>把 OSD 标记为 out 前，保持处于 in 状态的 OSD 最小比例。默认.3</td>
</tr>
<tr>
<td>mon osd auto mark in</td>
<td>是否把任何启动中的 OSD 标记为在集群中。默认false</td>
</tr>
<tr>
<td>mon osd auto mark auto out in</td>
<td>是否把正在启动、且被自动标记为 out 状态的 OSD 标记为 in 。默认true</td>
</tr>
<tr>
<td>mon osd auto mark new in</td>
<td>是否把正在启动的新 OSD 标记为 in 。默认true</td>
</tr>
<tr>
<td>mon osd down out interval</td>
<td>在 OSD 停止响应多少秒后把它标记为 down 且 out。默认300</td>
</tr>
<tr>
<td>mon osd downout subtree limit</td>
<td>最大可以把什么级别的CRUSH单元标记为out，默认rack</td>
</tr>
<tr>
<td colspan="2"><em>osd选项 [osd]</em></td>
</tr>
<tr>
<td>osd data</td>
<td>osd存储数据的路径，默认/var/lib/ceph/osd/$cluster-$id</td>
</tr>
<tr>
<td><strong>osd map cache size</strong></td>
<td>OSD map缓存大小，默认500M，建议1024</td>
</tr>
<tr>
<td><strong>osd map cache bl size</strong></td>
<td>OSD进程的In-Memory OSD map缓存大小，默认50，建议128</td>
</tr>
<tr>
<td>osd heartbeat interval</td>
<td>和其它OSD进行心跳检查的间隔，默认6秒</td>
</tr>
<tr>
<td>osd heartbeat grace</td>
<td>多久没有心跳，认为其它OSD宕掉</td>
</tr>
<tr>
<td><strong>osd max write size</strong></td>
<td>OSD一次写入的最大尺寸，默认90MB，建议512</td>
</tr>
<tr>
<td>osd mkfs options {fs-type} </td>
<td>为OSD新建文件系统时的选项</td>
</tr>
<tr>
<td>osd mount options {fs-type}</td>
<td>为OSD挂载文件系统时的选项</td>
</tr>
<tr>
<td><strong>osd journal</strong></td>
<td>
<p>OSD 日志路径，可以指向文件或块设备（例如SSD分区）</p>
<p>默认 /var/lib/ceph/osd/$cluster-$id/journal</p>
</td>
</tr>
<tr>
<td><strong>osd journal size</strong></td>
<td>
<p>日志文件的尺寸（MB），如果为0，且日志路径为块设备，则自动使用整个设备</p>
<p>推荐最少2G，有的用户则以 10GB 日志尺寸起步。合理的值是：</p>
<p style="padding-left: 30px;">osd journal size = {2 * (期望吞吐量* filestore max sync interval)}</p>
<p>期望吞吐量应考虑两个参数：<span style="background-color: #c0c0c0;">硬盘吞吐量（即持续数据传输速率）、网络吞吐量</span>，例如一个 7200 转硬盘的速度大致是 100MB/s 。硬盘和网络吞吐量中较小的一个是相对合理的吞吐量</p>
</td>
</tr>
<tr>
<td><strong>osd client message size cap</strong></td>
<td>客户端允许在内存中的最大数据量。默认524288000，建议2147483648</td>
</tr>
<tr>
<td><strong>crush location</strong></td>
<td>此OSD的CRUSH location设置</td>
</tr>
<tr>
<td>crush location hook</td>
<td>用于生成crush location的钩子 </td>
</tr>
<tr>
<td>osd max scrubs</td>
<td>
<p>OSD 的最大并发洗刷操作数</p>
<p>除了为对象复制多个副本外， Ceph 还要洗刷归置组以确保数据完整性。这种洗刷类似对象存储层的 fsck ，对于每个归置组， Ceph 生成一个所有对象的目录，并<span style="background-color: #c0c0c0;">比对每个主对象及其副本以确保没有对象丢失或错配</span>。轻微洗刷（每天）检查对象尺寸和属性，深层洗刷（每周）会读出数据并用校验和方法确认数据完整性</p>
</td>
</tr>
<tr>
<td>osd scrub begin hour</td>
<td>被调度的洗刷操作，允许的运行区间起点，默认0</td>
</tr>
<tr>
<td>osd scrub end hour</td>
<td>被调度的洗刷操作，允许的运行区间终点，默认24</td>
</tr>
<tr>
<td>osd scrub load threshold</td>
<td>系统负载高于该值，不进行洗刷操作，默认0.5</td>
</tr>
<tr>
<td>osd scrub min interval</td>
<td>系统负载不高的前提下，多久进行一次洗刷，默认每天一次，60*60*24秒</td>
</tr>
<tr>
<td>osd scrub max interval</td>
<td>不论系统负载如何，最大多久进行一次洗刷，默认每周</td>
</tr>
<tr>
<td>osd deep scrub interval</td>
<td>深度洗刷的间隔，默认每周</td>
</tr>
<tr>
<td><strong>osd deep scrub stride</strong></td>
<td><strong>深度洗刷允许读取的字节数。默认524288，建议131072</strong></td>
</tr>
<tr>
<td><strong>osd op threads</strong></td>
<td>
<p>OSD 操作线程数， 0 禁用多线程。增大数量可以增加请求处理速度，默认2，建议8</p>
<p>增加此线程数会增大CPU开销</p>
</td>
</tr>
<tr>
<td><strong>osd disk threads</strong></td>
<td>
<p>硬盘线程数，用于在后台执行磁盘密集型操作，像数据洗刷和快照修复。默认1，建议4</p>
<p>增加此线程数会增大CPU开销</p>
</td>
</tr>
<tr>
<td><strong>osd max backfills</strong></td>
<td>
<p>当集群新增或移除 OSD 时，按照 CRUSH 算法应该<span style="background-color: #c0c0c0;">重新均衡集群</span>，它会<span style="background-color: #c0c0c0;">把一些归置组移出或移入多个 OSD</span> 以回到均衡状态。<span style="background-color: #c0c0c0;">归置组和对象的迁移会导致集群运营性能显著降低</span>，为维持运营性能， Ceph 用 backfilling 来执行此迁移，它可以使得 Ceph 的<span style="background-color: #c0c0c0;">回填操作优先级低于用户读写请求</span></p>
<p>单个 OSD 允许的最大回填操作数。默认10，建议4</p>
</td>
</tr>
<tr>
<td>osd backfill full ratio</td>
<td>OSD 的占满率达到多少时拒绝接受回填请求，默认85%</td>
</tr>
<tr>
<td><strong>osd recovery op priority</strong></td>
<td>恢复操作优先级，取值1-63，值越高占用资源越高。默认10，建议4</td>
</tr>
<tr>
<td>osd recovery delay start</td>
<td>
<p>当集群启动、或某 OSD 守护进程崩溃后重启时，此 OSD 开始与其它 OSD 们建立互联（Peering），这样才能正常工作</p>
<p>如果某 OSD 崩溃并重启，通常会<span style="background-color: #c0c0c0;">落后于其他 OSD ，也就是没有同归置组内最新版本的对象</span>。这时， OSD 守护进程<span style="background-color: #c0c0c0;">进入恢复模式并检索最新数据副本</span>，并<span style="background-color: #c0c0c0;">更新运行图</span>。根据 OSD 宕掉的时间长短， OSD 的对象和归置组可能落后得厉害，另外，如果挂的是一个失效域（如一个机柜），多个 OSD 会同时重启，这样恢复时间更长、更耗资源</p>
<p>为保持性能， Ceph<span style="background-color: #c0c0c0;"> 进行恢复时会限制恢复请求数、线程数、对象块尺寸</span>，这样在降级状态下也能保持良好的性能</p>
<p>对等关系建立完毕后， Ceph 开始对象恢复前等待的时间。默认0秒</p>
</td>
</tr>
<tr>
<td><strong>osd recovery max active</strong></td>
<td>每个OSD同时处理的活跃恢复请求最大数，增大此值能加速恢复，但它们会增加OSD的负担，甚至导致其无法提供服务</td>
</tr>
<tr>
<td>osd recovery max chunk</td>
<td>恢复时一次推送的数据块的最大尺寸，可以用于防止网络拥塞</td>
</tr>
<tr>
<td>osd recovery threads</td>
<td>数据恢复时的线程数，默认1</td>
</tr>
<tr>
<td><strong>osd mount options xfs</strong></td>
<td>OSD的xfs挂载选项，默认rw,noatime,inode64，建议rw,noexec,nodev,noatime,nodiratime,nobarrier</td>
</tr>
<tr>
<td colspan="2"><em>rbd客户端调优  [client]</em></td>
</tr>
<tr>
<td><strong>rbd cache</strong></td>
<td>
<p>是否启用RBD缓存，默认true</p>
<p>用于用户空间块设备实现——librbd</p>
</td>
</tr>
<tr>
<td><strong>rbd cache size</strong></td>
<td>RBD缓存大小，默认33554432，建议268435456</td>
</tr>
<tr>
<td><strong>rbd cache max dirty</strong></td>
<td>缓存为write-back时允许的最大dirty字节数，如果为0，使用write-through。默认25165824，建议25165824</td>
</tr>
<tr>
<td><strong>rbd cache max dirty age</strong></td>
<td>在被刷新到存储盘前dirty数据存在缓存的时间，默认1秒，建议5</td>
</tr>
<tr>
<td colspan="2"><em>filestore选项 <em> [osd]</em></em></td>
</tr>
<tr>
<td>filestore max inline xattr size</td>
<td>每个对象在文件系统中存储XATTR（扩展属性）的最大尺寸，不得超过文件系统限制。默认值根据底层文件系统自动设置</td>
</tr>
<tr>
<td>filestore max inline xattrs</td>
<td>每个对象在文件系统中存储XATTR（扩展属性）的最大数量</td>
</tr>
<tr>
<td><strong>filestore max sync interval</strong></td>
<td>
<p>filestore 需要周期性地静默（暂停）写入、同步文件系统 —— <span style="background-color: #c0c0c0;">创建了一个提交点，然后就能释放相应的日志条目了</span>。</p>
<p><span style="background-color: #c0c0c0;">较高的同步频率可减小执行同步的时间及保存在日志里的数据量，但是日志利用率较低</span></p>
<p>较低的频率使得后端的文件系统能优化<span style="background-color: #c0c0c0;">归并较小的数据和元数据写入</span>，因此可能使同步更有效</p>
<p>默认5秒。建议15</p>
</td>
</tr>
<tr>
<td><strong>filestore min sync interval</strong></td>
<td>从日志到数据盘最小同步间隔。默认.01秒，建议10</td>
</tr>
<tr>
<td><strong>filestore op threads</strong></td>
<td>并发文件系统操作数。默认2，建议32</td>
</tr>
<tr>
<td>filestore flusher</td>
<td>
<p>是否启用回写器（Flusher）。回写器强制使用sync file range 来写出大块数据，这样处理可能减小最终同步的代价，禁用回写器有时可能提高性能</p>
<p>默认false</p>
</td>
</tr>
<tr>
<td>filestore flusher max fds</td>
<td>回写器的最大文件描述符数量。默认512</td>
</tr>
<tr>
<td>filestore fsync flushes journal data</td>
<td>在fsync时是否也回写日志数据</td>
</tr>
<tr>
<td><strong>filestore queue max ops</strong></td>
<td>
<p>文件存储在阻止新操作加入队列之前，可以接受的最大操作数。取值示例25000</p>
</td>
</tr>
<tr>
<td><strong>filestore queue max bytes</strong></td>
<td>文件存储单个操作的最大字节数。取值示例10485760</td>
</tr>
<tr>
<td><strong>filestore queue committing max ops</strong></td>
<td>文件存储单次可以提交的最大操作数</td>
</tr>
<tr>
<td><strong>filestore queue committing max bytes</strong></td>
<td>文件存储单次可以提交的最大字节数</td>
</tr>
<tr>
<td>filestore journal parallel</td>
<td>允许并行记日志，对 btrfs 默认开</td>
</tr>
<tr>
<td>filestore journal writeahead</td>
<td>允许预写日志，对 xfs 默认开</td>
</tr>
<tr>
<td colspan="2"><em>journal选项 <em> [osd]</em></em></td>
</tr>
<tr>
<td>journal dio</td>
<td>启用日志的Direct I/O，要求journal block align=true。默认true</td>
</tr>
<tr>
<td>journal aio</td>
<td>使用libaio库进行日志的异步写，要求journal dio =true。0.61+默认true</td>
</tr>
<tr>
<td>journal block align</td>
<td>日志按块对齐。默认true</td>
</tr>
<tr>
<td><strong>journal max write bytes</strong></td>
<td>日志写操作单次最大字节数。建议1073714824</td>
</tr>
<tr>
<td><strong>journal max write entries </strong></td>
<td>日志写操作单次最大条目数。建议10000</td>
</tr>
<tr>
<td><strong>journal queue max ops </strong></td>
<td>排队等候日志写的操作最大数。建议50000</td>
</tr>
<tr>
<td><strong>journal queue max bytes</strong></td>
<td>排队等候日志写的最大字节数。建议10485760000</td>
</tr>
<tr>
<td>journal align min size</td>
<td>对于大于此尺寸的数据，进行对齐操作 </td>
</tr>
<tr>
<td>journal zero on create</td>
<td>在创建文件系统（ mkfs ）期间用 0 填充整个日志 </td>
</tr>
<tr>
<td colspan="2"><em>pool/pg/crush相关 [global]</em></td>
</tr>
<tr>
<td><strong>osd pool default size</strong></td>
<td>对象默认副本份数</td>
</tr>
<tr>
<td><strong>osd pool default min size</strong></td>
<td>降级情况下，默认允许写操作的最小可用副本份数</td>
</tr>
<tr>
<td><strong>osd pool default pg num</strong></td>
<td>归置组的默认数量</td>
</tr>
<tr>
<td><strong>osd pool default pgp num</strong></td>
<td>为归置使用的归置组数量，默认值等同于 mkpool 的 pgp_num 参数。当前 PG 和 PGP 应该相同</td>
</tr>
<tr>
<td>mon max pool pg num</td>
<td>每个存储的最大归置组数量。默认65536</td>
</tr>
<tr>
<td>mon pg create interval</td>
<td>在同一个 OSD 里创建 PG 的间隔秒数。默认30</td>
</tr>
<tr>
<td>mon pg stuck threshold</td>
<td>多长时间无响应，则认为PG卡住了</td>
</tr>
<tr>
<td>mon pg min inactive</td>
<td>如果大于此数量的PG处于inactive状态超过mon_pg_stuck_threshold，则显示集群为HEALTH_ERR。默认1</td>
</tr>
<tr>
<td>mon pg warn min per osd</td>
<td>如果每OSD平均可用PG低于此数量，则显示集群为HEALTH_WARN。默认30</td>
</tr>
<tr>
<td>mon pg warn max per osd</td>
<td>如果每OSD平均可用PG高于此数量，则显示集群为HEALTH_WARN。默认300</td>
</tr>
<tr>
<td>osd crush chooseleaf type</td>
<td>在CRUSH 规则内用于 chooseleaf 的桶类型。用序列号而不是名字，默认1</td>
</tr>
<tr>
<td>osd crush initial weight</td>
<td>
<p>新加入到CRUSH map中的OSD的权重</p>
<p><span style="background-color: #c0c0c0;">默认情况下，权重是OSD的磁盘容量，单位TB</span></p>
</td>
</tr>
<tr>
<td>osd pool default crush rule</td>
<td>
<p>创建复制型池时，使用的默认CRUSH规则。默认-1，表示使用ID最低的规则</p>
</td>
</tr>
<tr>
<td>osd pool erasure code stripe unit</td>
<td>EC池中的对象条带尺寸</td>
</tr>
<tr>
<td>osd pool default flags</td>
<td>新存储池的默认标志</td>
</tr>
<tr>
<td colspan="2"><em>ms选项</em></td>
</tr>
<tr>
<td>ms tcp nodelay</td>
<td>禁用 nagle 算法，默认true</td>
</tr>
<tr>
<td>ms initial backoff</td>
<td>出错时重连的初始等待时间</td>
</tr>
<tr>
<td>
<p>ms max backoff</p>
</td>
<td>出错重连时等待的最大时间</td>
</tr>
<tr>
<td>ms nocrc</td>
<td>禁用网络消息的 crc 校验， CPU 不足时可提升性能</td>
</tr>
<tr>
<td colspan="2"><em>mds选项</em></td>
</tr>
<tr>
<td>mds cache memory limit</td>
<td>MDS缓存最大使用多少内存</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg" style="background-color: #c0c0c0;">配置变量</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">变量</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>$cluster</td>
<td>展开为存储集群的名称，在相同硬件上运行多个集群时有用</td>
</tr>
<tr>
<td>$type</td>
<td>展开为守护进程类型，例如mds, osd, or mon</td>
</tr>
<tr>
<td>$id</td>
<td>展开为守护进程或者客户端的标识符，对于osd.0其标识符为0</td>
</tr>
<tr>
<td>$host</td>
<td>展开为主机名</td>
</tr>
<tr>
<td>$name</td>
<td>展开为$type.$id</td>
</tr>
<tr>
<td>$pid</td>
<td>展开为守护进程的PID</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg" style="background-color: #c0c0c0;">配置段落<br /></span></div>
<p>配置文件是INI格式的，可以分为以下段落：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">段落</td>
<td style="text-align: center;">用途</td>
</tr>
</thead>
<tbody>
<tr>
<td>global</td>
<td>这里的配置影响 Ceph 集群里的所有守护进程</td>
</tr>
<tr>
<td>osd</td>
<td>影响存储集群里的所有 ceph-osd 进程，覆盖global相同选项</td>
</tr>
<tr>
<td>mon</td>
<td>影响集群里的所有 ceph-mon 进程，覆盖global相同选项</td>
</tr>
<tr>
<td>mds</td>
<td>影响集群里的所有 ceph-mds 进程，覆盖global相同选项</td>
</tr>
<tr>
<td>client</td>
<td>影响所有客户端（<span style="background-color: #c0c0c0;">如挂载的 Ceph 文件系统、挂载的块设备等等</span>）</td>
</tr>
</tbody>
</table>
<p>你还可以针对特定的实例配置段落：</p>
<ol>
<li>[osd.1]，针对ID为1的OSD的配置</li>
<li>[mon.HOSTNAME]，针对名称为HOSTNAME的MON的配置</li>
</ol>
<div class="blog_h2"><span class="graybg">启动选项</span></div>
<p>进程连接ceph-mon、进行身份验证、抓取集群中心配置信息时需要一些配置，这些配置必须存放在本地：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 50%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>mon_host</td>
<td>ceph-mon的主机列表</td>
</tr>
<tr>
<td>mon_dns_serv_name</td>
<td>ceph-mon的DNS名称，默认 ceph-mon</td>
</tr>
<tr>
<td>mon_data, osd_data, mds_data, mgr_data</td>
<td>守护进程在本地存放数据的路径</td>
</tr>
<tr>
<td>keyring, keyfile,key</td>
<td>连接ceph-mon进行身份验证时，使用的凭证</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">集群中心配置</span></div>
<p>ceph-mon集群管理了配置配置选项的数据库，用于供整个集群来消费。为了简化管理，<span style="background-color: #c0c0c0;">大部分的Ceph选项应该在此数据库中管理</span>。</p>
<p>集群中心配置的分段情况，和上文的配置段落一致。</p>
<div class="blog_h3"><span class="graybg">不使用集群配置</span></div>
<p>传入命令行选项<pre class="crayon-plain-tag">--no-mon-config</pre>，可以让进程不去读取集群中心配置，使用场景：</p>
<ol>
<li>希望所有配置信息在本地文件中管理</li>
<li>ceph-mon目前宕机，但是需要进行一些维护工作</li>
</ol>
<div class="blog_h3"><span class="graybg">掩码</span></div>
<p>集群中心配置的配置项，可以关联一个掩码，用于限定选项应用到哪种守护进程、哪种客户端。例如host:foo，限制foo选项仅仅应用到运行在host上的进程或客户端。</p>
<div class="blog_h3"><span class="graybg">命令</span></div>
<p>以下命令可以用于修改集群中心配置：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 35%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>ceph config dump</td>
<td>Dump出整个集群中心配置</td>
</tr>
<tr>
<td>ceph config get [who]</td>
<td>获取指定的客户端/守护进程存放在集群中心的配置，例如mds.a </td>
</tr>
<tr>
<td>ceph config set [who] [opt] [val]</td>
<td>
<p>设置指定客户端/守护进程的配置项，示例：</p>
<p><pre class="crayon-plain-tag"># 启用特定OSD的调试日志
ceph config set osd.123 debug_osd 20</pre>
</td>
</tr>
<tr>
<td>ceph tell [who] config set  [opt] [val]</td>
<td>临时设置配置项，目标重启后失效</td>
</tr>
<tr>
<td>ceph config show [who]</td>
<td>显示指定客户端/守护进程的当前使用的配置信息，可能和集群中心配置不一样</td>
</tr>
<tr>
<td>ceph config assimilate-conf -i -o</td>
<td>从-i选项读取所有配置信息，注入到集群中心配置。任何无法识别、无效的配置项存放到-o </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">存储池选项</span></div>
<p>在运行时，你可以使用<pre class="crayon-plain-tag">ceph osd pool set</pre>命令来修改这些选项：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>size</td>
<td>对象副本数</td>
</tr>
<tr>
<td>min_size</td>
<td>I/O 需要的最小副本数</td>
</tr>
<tr>
<td>crush_rule</td>
<td>此存储池使用的CRUSH规则</td>
</tr>
<tr>
<td>compression_algorithm</td>
<td>BlueStore使用的压缩算法，可选值lz4, snappy, zlib, zstd</td>
</tr>
<tr>
<td>compression_mode</td>
<td>BlueStore使用的压缩模式，可选值none, passive, aggressive, force</td>
</tr>
<tr>
<td>compression_min_blob_size<br />compression_max_blob_size</td>
<td>BlueStore启用压缩的阈值</td>
</tr>
<tr>
<td>hashpspool</td>
<td>设置或者取消HASHPSPOOL标记。1设置0取消</td>
</tr>
<tr>
<td>nodelete</td>
<td>设置或取消NODELETE标记。1设置0取消</td>
</tr>
<tr>
<td>nopgchange</td>
<td>设置或取消NOPGCHANGE标记</td>
</tr>
<tr>
<td>nosizechange</td>
<td>设置或取消NOSIZECHANGE标记</td>
</tr>
<tr>
<td>write_fadvise_dontneed</td>
<td>设置或取消WRITE_FADVISE_DONTNEED标记</td>
</tr>
<tr>
<td>noscrub</td>
<td>设置或取消NOSCRUB标记</td>
</tr>
<tr>
<td>nodeep-scrub</td>
<td>设置或取消NODEEP_SCRUB标记</td>
</tr>
<tr>
<td>hit_set_type</td>
<td>启用缓存存储池的命中集跟踪，设置命中集类型，生产环境仅仅支持bloom</td>
</tr>
<tr>
<td>hit_set_count</td>
<td>为缓存存储池保留的命中集数量。此值越高， OSD消耗的内存越多</td>
</tr>
<tr>
<td>hit_set_fpp</td>
<td>bloom 命中集类型的误检率（false positive probability）</td>
</tr>
<tr>
<td>cache_target_dirty_ratio</td>
<td><span style="background-color: #c0c0c0;">缓存存储池</span>包含的修改（脏）对象达到多少比例时就把它们<span style="background-color: #c0c0c0;">回写到后端的存储池</span></td>
</tr>
<tr>
<td>cache_target_dirty_high_ratio</td>
<td>缓存存储池内包含的已修改（脏）对象达到什么比例时，缓存层代理就会更快地把脏对象刷回到后端存储池</td>
</tr>
<tr>
<td>cache_target_full_ratio</td>
<td>缓存存储池包含的干净对象达到多少比例时，缓存代理就把它们清除出缓存存储池</td>
</tr>
<tr>
<td>target_max_bytes</td>
<td>回写（Flushing）或清除（Evicting）对象的阈值，按字节数</td>
</tr>
<tr>
<td>target_max_objects</td>
<td>回写（Flushing）或清除（Evicting）对象的阈值，按对象数</td>
</tr>
<tr>
<td>scrub_min_interval</td>
<td>在负载低时，洗刷存储池的最大间隔秒数</td>
</tr>
<tr>
<td>scrub_max_interval</td>
<td>不管集群负载如何，都要洗刷存储池的最大间隔秒数</td>
</tr>
<tr>
<td>deep_scrub_interval</td>
<td>深度洗刷存储池的间隔秒数</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">运维</span></div>
<div class="blog_h2"><span class="graybg">操控集群</span></div>
<div class="blog_h3"><span class="graybg">UPSTART</span></div>
<p>Ubuntu系统下，基于ceph-deploy部署集群后，可以用这种方法来操控集群。</p>
<p>列出节点上所有Ceph进程：</p>
<pre class="crayon-plain-tag">initctl list | grep ceph</pre>
<p>启动节点上所有Ceph进程：</p>
<pre class="crayon-plain-tag">start ceph-all</pre>
<p>启动节点上特定类型的Ceph进程： </p>
<pre class="crayon-plain-tag">sudo start ceph-osd-all
sudo start ceph-mon-all
sudo start ceph-mds-all</pre>
<p>启动特定类型的Ceph进程的某个实例：</p>
<pre class="crayon-plain-tag">sudo start ceph-osd id={id}
sudo start ceph-mon id={hostname}
sudo start ceph-mds id={hostname}</pre>
<p>停止特定类型的Ceph进程的某个实例：</p>
<pre class="crayon-plain-tag">sudo stop ceph-osd id={id}
sudo stop ceph-mon id={hostname}
sudo stop ceph-mds id={hostname}</pre>
<div class="blog_h3"><span class="graybg">SYSVINIT</span></div>
<p>在 CentOS 、 Redhat 、 Fedora 和 SLES 发行版上可以通过传统的 sysvinit 运行 Ceph ， Debian/Ubuntu 的较老的版本也可以用此方法。</p>
<p>命令格式：</p>
<pre class="crayon-plain-tag"># 启动、重启或停止
sudo /etc/init.d/ceph [options] [start|restart|stop] [daemonType|daemonID]

# 示例：

# -a 表示在所有节点执行
sudo /etc/init.d/ceph -a start
sudo /etc/init.d/ceph -a stop
sudo /etc/init.d/ceph start osd
sudo /etc/init.d/ceph -a stop osd
sudo /etc/init.d/ceph start osd.0
sudo /etc/init.d/ceph stop osd.0 </pre>
<div class="blog_h2"><span class="graybg">监控集群</span></div>
<div class="blog_h3"><span class="graybg">检查集群状态</span></div>
<p>执行<pre class="crayon-plain-tag">ceph status</pre>或则<pre class="crayon-plain-tag">ceph -s</pre>可以查看集群的状态：</p>
<ol>
<li>active+clean：说明集群健康运行</li>
<li>undersized+degraded：如果有OSD节点宕机，可能进入此状态。<span style="background-color: #c0c0c0;">降级后还是可以正常读写数据</span></li>
<li>undersized+degraded+peered：如果超过min size要求的OSD宕机，则不可读写，显示为此状态。min size默认2，副本份数默认3。执行下面的命令可以修改min size：<br />
<pre class="crayon-plain-tag">ceph osd pool set rbd min_size 1</pre></p>
<p>peered相当于已经配对（PG - OSDs），但是正在等待OSD上线</p>
</li>
<li>
<p>remapped+backfilling：默认情况下，OSD宕机5分钟后会被标记为out状态，Ceph认为它已经不属于集群了。Ceph会按照一定的规则，将已经out的OSD上的PG重映射到其它OSD，并且从现存的副本来回填（Backfilling）数据到新OSD</p>
</li>
</ol>
<p>执行<pre class="crayon-plain-tag">ceph health</pre>可以查看简短的健康状态。</p>
<p>执行<pre class="crayon-plain-tag">ceph -w</pre>可以持续的监控发生在集群中的各种事件。</p>
<div class="blog_h3"><span class="graybg">检查存储用量</span></div>
<p>执行命令<pre class="crayon-plain-tag">ceph df</pre>可以查看<span style="background-color: #c0c0c0;">集群的数据用量及其在存储池内的分布情况</span>：</p>
<pre class="crayon-plain-tag">GLOBAL:
                       # 已用存储空间总量（包括所有副本）
    SIZE     AVAIL     RAW USED     %RAW USED 
    323G      318G        4966M          1.50 

# 这一段显示的数值，不包含副本、克隆、快照的用量
POOLS:
                             # 大概使用率              # 大概对象数
    NAME     ID     USED      %USED     MAX AVAIL     OBJECTS 
    rbd      1      3539M      1.14          300G        1018</pre>
<div class="blog_h3"><span class="graybg">检查MON状态</span></div>
<pre class="crayon-plain-tag"># 基本信息
ceph mon stat
# 详细信息
ceph mon dump
# 法定人数状态、monmap内容
ceph quorum_status -f json-pretty</pre>
<div class="blog_h3"><span class="graybg">检查MDS状态 </span></div>
<pre class="crayon-plain-tag">ceph mds stat
ceph mds dump</pre>
<div class="blog_h3"><span class="graybg">检查OSD状态</span></div>
<p>通过PG这个中间层，Ceph确保了数据不会被绑死在某个特定的OSD。要追踪错误根源，你需要检查归置组、以及底层的OSD。</p>
<p>执行下面的命令，获取最简短的OSD状态：</p>
<pre class="crayon-plain-tag">ceph osd stat
# 输出
12 osds: 12 up, 12 in</pre>
<p>执行<pre class="crayon-plain-tag">ceph osd dump</pre>则可以获得详细信息，包括在CRUSH map中的权重、UUID、是in还是out：</p>
<pre class="crayon-plain-tag">osd.0 up   out weight 0 up_from 70 up_thru 172 down_at 65 last_clean_interval [51,60) 10.5.39.13:6800/48 10.5.39.13:6801/48 10.5.39.13:6802/48 10.5.39.13:6803/48 exists,up 354a6547-3437-46d6-a928-f5633eb7f059
osd.1 up   in  weight 1 up_from 74 up_thru 327 down_at 63 last_clean_interval [55,60) 10.5.39.42:6800/48 10.5.39.42:6801/48 10.5.39.42:6802/48 10.5.39.42:6803/48 exists,up 0fb4bb77-7c84-45ac-919a-2cc350fc62b9</pre>
<p>执行<pre class="crayon-plain-tag">ceph osd tree</pre>可以在OSD树中打印各OSD的位置、状态、权重。如果OSD的in数量大于up数量，可以通过此命令快速定位：</p>
<pre class="crayon-plain-tag"># 仅仅包含out的OSD
ceph osd tree out
# ID CLASS WEIGHT  TYPE NAME               STATUS REWEIGHT PRI-AFF 
# -1       4.89999 root default                                    
# -2             0     host k8s-10-5-38-25                         
#  2   hdd       0         osd.2              DNE        0</pre>
<div class="blog_h3"><span class="graybg">检查PG状态</span></div>
<p>执行命令<pre class="crayon-plain-tag">ceph pg stat</pre>可以查看全局性的PG统计信息。</p>
<p>可以获取PG列表：</p>
<pre class="crayon-plain-tag"># 输出的第一列为PG ID
ceph pg dump
# 导出为JSON
ceph pg dump -o {filename} --format=json</pre>
<p>执行下面的命令可以查看PG到OSD的映射关系：</p>
<pre class="crayon-plain-tag"># PG ID 格式为 存储池号.归置组ID，归置组ID为一个十六进制数字
ceph pg map 1.13d
# 输出
osdmap e790 pg 1.13d (1.13d) -&gt; up [4,6,5] acting [4,6,5]</pre>
<p>执行<pre class="crayon-plain-tag">ceph status</pre>也可以看到PG的统计性信息。</p>
<p>执行<pre class="crayon-plain-tag">ceph pg 1.13d query</pre>可以查看某个PG的非常细节的信息。</p>
<div class="blog_h2"><span class="graybg">操控MON</span></div>
<div class="blog_h3"><span class="graybg">增加</span></div>
<pre class="crayon-plain-tag">MONID=Neon
MONADDR=10.0.3.1:6789

# 创建目录
mkdir /var/lib/ceph/mon/ceph-$MONID

# 获取密钥和monmap
ceph auth get mon. -o /tmp/keyring
ceph mon getmap -o /tmp/monmap

# 初始化Mon
sudo ceph-mon -i $MONID --mkfs --monmap /tmp/monmap --keyring /tmp/keyring

# 启动Mon
ceph-mon -i $MONID --public-addr $MONADDR</pre>
<div class="blog_h3"><span class="graybg">删除</span></div>
<pre class="crayon-plain-tag">ceph mon rm Xenon </pre>
<div class="blog_h3"><span class="graybg">monmap</span></div>
<p>导出monmap：</p>
<pre class="crayon-plain-tag">ceph mon getmap -o monmap</pre>
<p>打印monmap的内容：</p>
<pre class="crayon-plain-tag">monmaptool --print monmap</pre>
<p>从monmap中删除一个MON：</p>
<pre class="crayon-plain-tag">monmaptool  monmap --rm xenon</pre>
<p>添加一个MON到monmap中：</p>
<pre class="crayon-plain-tag">monmaptool  monmap --add Xenon 10.0.5.1:6789</pre>
<p>导入monmap到MON节点：</p>
<pre class="crayon-plain-tag">ceph-mon -i Xenon --inject-monmap monmap </pre>
<div class="blog_h2"><span class="graybg">操控OSD</span></div>
<div class="blog_h3"><span class="graybg">增加</span></div>
<pre class="crayon-plain-tag"># 空间使用率达到 near full 比率后， OSD 失败可能导致集群空间占满。因此，你需要提前扩容
# 执行下面的命令创建一个新的OSD，其OSD号会输出到控制台：

# uuid、id可选，如果不指定则自动生成。不能和现有OSD的uuid、id重复。不建议手工指定id
ceph osd create [{uuid} [{id}]]


# 如果希望OSD使用独立磁盘或者分区，可以先创建好文件系统，再挂载到适当位置
sudo mkfs -t {fstype} /dev/{drive}
# 示例
mkfs -t xfs -f /dev/sda3
# 挂载点
mkdir /var/lib/ceph/osd/ceph-{osd-num}
# 挂载
mount /dev/sda3 /var/lib/ceph/osd/ceph-14

# 初始化OSD数据目录：
ceph-osd -i {osd-num} --mkfs --mkkey

# 注册OSD认证密钥：
ceph auth add osd.{osd-num} osd 'allow *' mon 'allow rwx' -i /var/lib/ceph/osd/${cluster-name}-{osd-num}/keyring
# 示例
ceph auth add osd.14 osd 'allow *' mon 'allow rwx' -i /var/lib/ceph/osd/ceph-14/keyring

# 你需要把OSD加入到CRUSH map，这样数据才会分配到此OSD上： 
# 把OSD加入到CRUSH树的适当位置（桶）
# 如果指定了不止一个桶，则将OSD加入到最靠近叶子节点的桶中，并把此桶移动到你指定的其它桶中
# 如果你指定了root桶，则此OSD直接挂在root下，则是不建议的，CRUSH规则期望OSD位于主机这种桶类型的下级节点
ceph osd crush add {id-or-name} {weight}  [{bucket-type}={bucket-name} ...]
# 示例
ceph osd crush add 14 0.11589
# 如果设置osd_crush_update_on_start=true，则可以OSD启动后自动加入到CRUSH树并更新权重
# 警告，如果上述参数设置为false，且你没有将osd添加到适当位置，则osd可能无法承载PG</pre>
<div class="blog_h3"><span class="graybg">查询</span></div>
<pre class="crayon-plain-tag">ceph osd find 14
{
    "osd": 14,
    "ip": "10.0.1.1:6804/3146",
    "crush_location": {
        "host": "Carbon",
        "root": "default"
    }
} </pre>
<div class="blog_h3"><span class="graybg">启动</span></div>
<p>一旦启动了 OSD ，其状态就变成了 up+in ，此时可以通过ceph -w来观察数据迁移。归置组状态会变为active, some degraded objects，最终变回active+clean</p>
<pre class="crayon-plain-tag"># Debian/Ubuntu 上用 Upstart：
start ceph-osd id={osd-num}
# CentOS/RHEL 上用 sysvinit：
/etc/init.d/ceph start osd.{osd-num}
# 基于systemd的系统
systemctl start ceph-osd@14.service </pre>
<div class="blog_h3"><span class="graybg">删除</span></div>
<p>删除OSD之前，应该评估集群容量，保证操作之后，集群不会到达 near full 比率</p>
<pre class="crayon-plain-tag"># 首先从CRUSH map中移除
ceph osd crush remove {name}
# 删除其认证密钥
ceph auth del osd.{osd-num}
# 删除OSD
ceph osd rm {osd-num}</pre>
<div class="blog_h3"><span class="graybg">标记为宕机</span></div>
<pre class="crayon-plain-tag">ceph osd down {osd-num}</pre>
<div class="blog_h3"><span class="graybg">标记为踢出</span></div>
<p>踢出OSD后，Ceph会进行数据迁移，达到再平衡。归置组状态会变为active, some degraded objects，最终变回active+clean。</p>
<pre class="crayon-plain-tag">ceph osd out {osd-num}</pre>
<p>对于某些小型测试集群，踢出一个OSD即导致CRUSH进入临界状态，某些归置组一直卡在active+remapped状态。如果遇到这种情况，你可以：</p>
<pre class="crayon-plain-tag"># 把被踢出的集群重新加进来
ceph osd in {osd-num}
# 将其权重标记为0，而非踢出
ceph osd crush reweight osd.{osd-num} 0 </pre>
<p>等待数据迁移完毕后，再将OSD踢出。 </p>
<div class="blog_h3"><span class="graybg">标记为进入</span></div>
<p>你可能需要更新CRUSH map才能让新进入的OSD接受数据：</p>
<pre class="crayon-plain-tag">ceph osd in {osd-num}</pre>
<div class="blog_h3"><span class="graybg">标记为丢失</span></div>
<p>标记OSD为lost，可能导致数据丢失，谨慎：</p>
<pre class="crayon-plain-tag">ceph osd lost {id} [--yes-i-really-mean-it]</pre>
<div class="blog_h3"><span class="graybg">设置权重</span></div>
<pre class="crayon-plain-tag"># 权重默认是以TB为单位
ceph osd reweight {osd-num} {weight} </pre>
<div class="blog_h3"><span class="graybg">清理OSD</span></div>
<pre class="crayon-plain-tag">ceph osd scrub {osd-num}
# 清理所有
ceph osd scrub all</pre>
<div class="blog_h3"><span class="graybg">深度清理OSD</span></div>
<pre class="crayon-plain-tag">ceph osd deep-scrub all </pre>
<div class="blog_h3"><span class="graybg">修复OSD</span></div>
<pre class="crayon-plain-tag">ceph osd repair N</pre>
<div class="blog_h3"><span class="graybg">测试OSD性能 </span></div>
<pre class="crayon-plain-tag">ceph tell osd.N bench [TOTAL_DATA_BYTES] [BYTES_PER_WRITE]</pre>
<div class="blog_h3"><span class="graybg">空间不足处理</span></div>
<p><span style="background-color: #c0c0c0;">Ceph不允许向满的 OSD 写入数据，以免丢失数据</span>。在运营着的集群中，你应该能收到集群空间将满的警告。mon osd full ratio 默认为 0.95 ，也就是说<span style="background-color: #c0c0c0;">达到 95% 时它将阻止客户端写入数据</span>； mon osd backfillfull ratio 默认为 0.90 ，也就是说<span style="background-color: #c0c0c0;">达到容量的 90% 时它会阻塞，防止回填启动</span>； OSD 将满比率默认为 0.85 ，也就是说<span style="background-color: #c0c0c0;">达到容量的 85% 时它会产生健康警告</span>。</p>
<p>使用下面的命令临时修改设置，否则你可能没有机会清理不需要的RBD以腾出空间：</p>
<pre class="crayon-plain-tag">ceph osd set-nearfull-ratio 0.95
ceph osd set-full-ratio 0.99
ceph osd set-backfillfull-ratio 0.99</pre>
<div class="blog_h2"><span class="graybg">操控MDS</span></div>
<div class="blog_h3"><span class="graybg">增加</span></div>
<p>首先，在/var/lib/ceph/mds/mds.N创建一个数据挂载点。N是MDS的ID，通常就是主机名。</p>
<p>然后，修改Ceph配置，添加一个mds段。修改完毕后进行配置分发：</p>
<pre class="crayon-plain-tag">[mds.N]
host = {hostname}</pre>
<p> 如果启用了CephX，需要创建认证密钥：</p>
<pre class="crayon-plain-tag">sudo ceph auth get-or-create mds.N mon 'profile mds' mgr 'profile mds' mds 'allow *' osd 'allow *' &gt; \
    /var/lib/ceph/mds/ceph-N/keyring</pre>
<div class="blog_h3"><span class="graybg">移除</span></div>
<p>执行下面的命令将目标mds标记为宕机：</p>
<pre class="crayon-plain-tag">ceph mds fail &lt;mds name&gt;</pre>
<p>移除MDS的/var/lib/ceph/mds/ceph-NAME下对应目录，然后，删除/etc/systemd/system/ceph-mds.target.wants/下的对应项目：</p>
<pre class="crayon-plain-tag">systemctl stop ceph-mds@Neon.service
systemctl disable ceph-mds@Neon.service
rm -rf /var/lib/ceph/mds/ceph-Neon </pre>
<p>如果服务是通过/etc/init.d/ceph加载的，则：</p>
<pre class="crayon-plain-tag">service ceph stop
update-rc.d ceph disable</pre>
<div class="blog_h3"><span class="graybg">状态</span></div>
<p>查看守护进程的简短状态：</p>
<pre class="crayon-plain-tag">ceph mds stat</pre>
<div class="blog_h3"><span class="graybg">启动</span></div>
<pre class="crayon-plain-tag">service ceph start mds.NAME</pre>
<div class="blog_h3"><span class="graybg">引用守护进程</span></div>
<p>你可以使用多种方式来引用一个MDS守护进程：</p>
<pre class="crayon-plain-tag">ceph mds fail 5446     # 基于GID
ceph mds fail myhost   # 基于名称
ceph mds fail 3:0      # 基于FSCID:rank
ceph mds fail myfs:0   # 基于文件系统名称:rank</pre>
<div class="blog_h3"><span class="graybg">管理故障转移</span></div>
<p>和MDS进程的Standby行为相关的配置项包括：</p>
<pre class="crayon-plain-tag"># 如果设置为true则standby会持续的从Rank中读取元数据日志，从而维持一个有效的元数据缓存，这可以加速Failover
mds_standby_replay = true
# 仅仅作为具有指定名称的MDS的Standby
mds_standby_for_name = Carbon
# 仅仅作为指定Rank的Standby
mds_standby_for_rank
# 仅仅作为指定文件系统的Standby
mds_standby_for_fscid</pre>
<p>如果不进行任何配置，没有持有Rank的那些MDS进程，可以作为任何Rank的Standby。</p>
<p>配置示例：</p>
<pre class="crayon-plain-tag"># a、b两个MDS互备，负责Rank 0
[mds.a]
mds standby replay = true
mds standby for rank = 0

[mds.b]
mds standby replay = true
mds standby for rank = 0</pre>
<div class="blog_h3"><span class="graybg">修改配置</span></div>
<pre class="crayon-plain-tag">ceph tell mds.{mds-id} config set {setting} {value}
# 示例
ceph tell mds.0 config set debug_ms 1</pre>
<div class="blog_h3"><span class="graybg">启用诊断信息</span></div>
<pre class="crayon-plain-tag">ceph mds stat</pre>
<div class="blog_h3"><span class="graybg">手工故障转移</span></div>
<p>标记当前活动MDS为失败，触发故障转移：</p>
<pre class="crayon-plain-tag">ceph mds fail 0 </pre>
<div class="blog_h2"><span class="graybg">操控CephFS</span></div>
<div class="blog_h3"><span class="graybg">创建</span></div>
<p>要创建一个文件系统，你<span style="background-color: #c0c0c0;">至少需要两个存储池，一个存放数据，另外一个存放元数据</span>。注意：</p>
<ol>
<li>元数据池的副本份数要设置的高，因为任何元数据的丢失都会导致整个文件系统不可用</li>
<li>元数据池应该使用高速存储，例如SSD，因为这对客户端操作的延迟有直接影响</li>
</ol>
<p>示例：</p>
<pre class="crayon-plain-tag"># ceph fs new &lt;fs_name&gt; &lt;metadata&gt; &lt;data&gt;
# 示例，可以使用现有的存储池
ceph fs new cephfs rbd-ssd rbd-hdd

# Error EINVAL: pool 'rbd-ssd' already contains some objects. Use an empty pool instead.
# 出现上述错误，可以：
ceph fs new cephfs rbd-ssd rbd-hdd --force</pre>
<p>创建了文件系统之后，在Luminous版本中，集群状态中显示：</p>
<pre class="crayon-plain-tag">mds: cephfs-1/1/1 up  {0=Carbon=up:active}</pre>
<div class="blog_h3"><span class="graybg">列出</span></div>
<pre class="crayon-plain-tag">ceph fs ls</pre>
<div class="blog_h3"><span class="graybg">状态</span></div>
<p>查看CephFS的详细状态，包括MDS列表、Rank列表等：</p>
<pre class="crayon-plain-tag">ceph fs status</pre>
<div class="blog_h3"><span class="graybg">删除</span></div>
<pre class="crayon-plain-tag">ceph fs rm &lt;filesystem name&gt; [--yes-i-really-mean-it]</pre>
<div class="blog_h3"><span class="graybg">关闭</span></div>
<pre class="crayon-plain-tag">mds set &lt;fs_name&gt; down true</pre>
<div class="blog_h3"><span class="graybg">查看选项</span></div>
<p>要获取某个文件系统的信息，可以：</p>
<pre class="crayon-plain-tag">ceph fs get cephfs</pre>
<div class="blog_h3"><span class="graybg">设置选项</span></div>
<pre class="crayon-plain-tag">fs set &lt;filesystem name&gt; &lt;var&gt; &lt;val&gt;

# 示例
# 设置单个文件的大小，默认1TB
fs set cephfs max_file_size 1099511627776</pre>
<div class="blog_h3"><span class="graybg">增减数据池</span></div>
<pre class="crayon-plain-tag">fs add_data_pool &lt;filesystem name&gt; &lt;pool name/id&gt;
fs rm_data_pool &lt;filesystem name&gt; &lt;pool name/id&gt;</pre>
<div class="blog_h3"><span class="graybg">设为默认 </span></div>
<p>如果集群中有多个文件系统，而客户端在挂载时没有明确指定使用哪个，则使用默认文件系统：</p>
<pre class="crayon-plain-tag">ceph fs set-default cephfs</pre>
<div class="blog_h3"><span class="graybg">使用EC池 </span></div>
<p>EC池可以作为Ceph的数据池，但是需要启用overwirte：</p>
<pre class="crayon-plain-tag">ceph osd pool set my_ec_pool allow_ec_overwrites true</pre>
<p>注意：EC池不能用来存储元数据。</p>
<div class="blog_h3"><span class="graybg">配额</span></div>
<p>CephFS支持对<span style="background-color: #c0c0c0;">任何一个子目录进行配额</span>。但是，需要注意以下限制：</p>
<ol>
<li>需要客户端协作，因此被篡改过的客户端可以突破配额</li>
<li>配额不是非常精确的</li>
<li>内核客户端，仅仅在4.17+才支持配额。用户空间客户端fuse、libcephfs都支持配额</li>
</ol>
<p>设置配额（设置为0则移除配额）：</p>
<pre class="crayon-plain-tag">setfattr -n ceph.quota.max_bytes -v 100000000 /some/dir     # 按字节数
setfattr -n ceph.quota.max_files -v 10000 /some/dir         # 按文件数</pre>
<p>查看配额：</p>
<pre class="crayon-plain-tag">getfattr -n ceph.quota.max_bytes /some/dir
getfattr -n ceph.quota.max_files /some/dir </pre>
<div class="blog_h2"><span class="graybg">挂载CephFS</span></div>
<div class="blog_h3"><span class="graybg">内核驱动</span></div>
<p>你可以直接使用Linux内核提供的驱动来挂载CephFS：</p>
<pre class="crayon-plain-tag">mkdir /mnt/cephfs
mount -t ceph 10.0.1.1:6789:/ /mnt/cephfs</pre>
<p>如何启用了CephX，需要指定访问密钥，否则会报22错误：</p>
<pre class="crayon-plain-tag">mount -t ceph 10.0.1.1:6789:/ /mnt/cephfs -o name=admin,secret=AQDRNBZbCp3WMBAAynSCCFPtILwHeI3RLDADKA==
# 或者指定包含密钥的文件
mount -t ceph 10.0.1.1:6789:/ /mnt/cephfs -o name=admin,secretfile=/etc/ceph/admin.secret</pre>
<p>如果报can't read superblock，说明客户端内核不支持。</p>
<p>要实现自动挂载，你需要修改fstab：</p>
<pre class="crayon-plain-tag">{ipaddress}:{port}:/ {mountpoint} {fs-type} [name=username,secret=key|secretfile=file],[{mount.options}]

# 示例
10.0.1.1:6789:/ /mnt/cephfs  ceph name=admin,secretfile=/etc/ceph/cephfs.key,noatime,_netdev    0       2</pre>
<div class="blog_h3"><span class="graybg">FUSE</span></div>
<p>要在用户空间挂载CephFS，你需要：</p>
<ol>
<li>将Ceph配置文件拷贝到客户端，命名为/etc/ceph/ceph.conf</li>
<li>将Keyring拷贝到客户端，命名为/etc/ceph/ceph.keyring：<br />
<pre class="crayon-plain-tag">sudo scp -i ~/Documents/puTTY/gmem.key root@xenon.gmem.cc:/etc/ceph/ceph.client.admin.keyring /etc/ceph/ceph.keyring </pre>
</li>
<li>执行挂载：<br />
<pre class="crayon-plain-tag">sudo ceph-fuse -m 10.0.1.1:6789 /mnt/cephfs
# ceph-fuse[847]: starting ceph client                                                                                                                                                     
# 2018-06-07 19:18:25.503086 7fa5c44e1000 -1 init, newargv = 0x7fa5cd643b40 newargc=9                                                                                                      
# ceph-fuse[847]: starting fuse</pre>
</li>
</ol>
<p>如果有多个CephFS，你可以为ceph-fuse指定命令行选项--client_mds_namespace，或者在客户端的ceph.conf中添加client_mds_namespace配置。</p>
<p>要实现自动挂载，你需要修改fstab：</p>
<pre class="crayon-plain-tag">none    /mnt/ceph  fuse.ceph ceph.id={user-ID}[,ceph.conf={path/to/conf.conf}],_netdev,defaults  0 0

# 示例
none    /mnt/ceph  fuse.ceph ceph.id=admin,_netdev,defaults  0 0
none    /mnt/ceph  fuse.ceph ceph.id=admin,ceph.conf=/etc/ceph/ceph.conf,_netdev,defaults  0 0 </pre>
<div class="blog_h2"><span class="graybg">操控存储池</span></div>
<div class="blog_h3"><span class="graybg">设置默认参数</span></div>
<pre class="crayon-plain-tag"># 设置新建存储池时使用的默认参数
osd pool default pg num = 128
osd pool default pgp num = 128</pre>
<div class="blog_h3"><span class="graybg">运行时修改参数</span></div>
<pre class="crayon-plain-tag">ceph osd pool set {pool-name} option-name num
# 示例
ceph osd pool set  .rgw.root pg_num 128
ceph osd pool set  .rgw.root pgp_num 128 </pre>
<div class="blog_h3"><span class="graybg">创建存储池</span></div>
<pre class="crayon-plain-tag"># 创建存储池
# crush-ruleset-name：使用的默认CRUSH规则集名称
# 复制型的默认规则集由选项osd pool default crush replicated ruleset控制
# 
ceph osd pool create {pool-name} {pg-num} [{pgp-num}] [replicated] \
        [crush-ruleset-name] [expected-num-objects]
ceph osd pool create {pool-name} {pg-num}  {pgp-num}   erasure \
        [erasure-code-profile] [crush-ruleset-name] [expected_num_objects]


# 示例
ceph osd pool create rbd-ssd 384 replicated replicated_rule_ssd</pre>
<div class="blog_h3"><span class="graybg">初始化存储池</span></div>
<p>创建存储池之后，在管理节点上，使用rbd工具来初始化池：</p>
<pre class="crayon-plain-tag">rbd pool init &lt;pool-name&gt;</pre>
<div class="blog_h3"><span class="graybg">读写存储池配置</span></div>
<pre class="crayon-plain-tag"># 修改存储池配置
ceph osd pool set {pool-name} {key} {value}
# 读取存储池配置
ceph osd pool get {pool-name} {key}</pre>
<div class="blog_h3"><span class="graybg">列出存储池</span></div>
<pre class="crayon-plain-tag">ceph osd lspools
# 输出
# 1 rbd,3 rbd-ssd,4 rbd-hdd,</pre>
<div class="blog_h3"><span class="graybg">列出池中对象</span></div>
<pre class="crayon-plain-tag"># 列出存储池中的对象
rados -p rbd ls</pre>
<div class="blog_h3"><span class="graybg">显示池用量</span></div>
<pre class="crayon-plain-tag"># 显示所有存储池的使用情况
rados df

# 或者
ceph df

# 更多细节
ceph df detail
# USED       %USED       MAX AVAIL     OBJECTS     DIRTY     READ      WRITE      RAW USED 
# 用量       用量百分比                对象数量              读速度    写数量     用量x副本份数</pre>
<div class="blog_h3"><span class="graybg">存储池配额</span></div>
<pre class="crayon-plain-tag"># 设置最大对象数量、最大字节数
ceph osd pool set-quota {pool-name} [max_objects {obj-count}] [max_bytes {bytes}]
# 示例：
ceph osd pool set-quota data max_objects 10000</pre>
<p>要取消配额，设置为0即可。 </p>
<div class="blog_h3"><span class="graybg">存储池快照</span></div>
<pre class="crayon-plain-tag"># 制作存储池快照
ceph osd pool mksnap {pool-name} {snap-name}
# 删除存储池快照
ceph osd pool rmsnap {pool-name} {snap-name}</pre>
<div class="blog_h3"><span class="graybg">删除存储池</span></div>
<pre class="crayon-plain-tag"># 删除存储池
ceph osd pool delete {pool-name} [{pool-name} --yes-i-really-really-mean-it]

# 示例
ceph osd pool rm rbd-ssd rbd-ssd --yes-i-really-really-mean-it
ceph osd pool rm rbd-hdd rbd-hdd --yes-i-really-really-mean-it</pre>
<div class="blog_h3"><span class="graybg">清空存储池</span></div>
<pre class="crayon-plain-tag"># 列出池中对象，逐个删除
for i in `rados -p rbd-ssd ls`; do echo $i; rados -p rbd-ssd rm $i; done</pre>
<div class="blog_h2"><span class="graybg">操控镜像</span></div>
<p>镜像就是块设备，所谓块是一系列连续的字节序列（例如512KB）。基于块的存储接口，是磁盘、CD、软盘、甚至磁带都使用的，是存储对象最广泛使用的方式。</p>
<p>Ceph的块设备具有以下特点：thin-provisioned（精简配备）、可改变大小、跨越多OSD存储。</p>
<div class="blog_h3"><span class="graybg">列出镜像</span></div>
<pre class="crayon-plain-tag">rbd ls {poolname}</pre>
<p>如果不指定池名称，则列出默认池中的镜像。</p>
<p>下面的命令可以列出池中延迟删除的镜像：</p>
<pre class="crayon-plain-tag">rbd trash ls {poolname} </pre>
<div class="blog_h3"><span class="graybg">查看镜像磁盘占用</span></div>
<pre class="crayon-plain-tag">rbd du --pool rbd-ssd</pre>
<p>注意：rbd info输出的是thin provisioning的大小，不是实际磁盘空间占用。</p>
<p>除了上面的命令，还可以：</p>
<pre class="crayon-plain-tag">rbd diff k8s/kubernetes-dynamic-pvc | awk '{ SUM += $2 } END { print SUM/1024/1024 " MB" }' </pre>
<div class="blog_h3"><span class="graybg">查看镜像信息</span></div>
<pre class="crayon-plain-tag">rbd info {pool-name}/{image-name}
rbd info {image-name}

# 输出示例：
rbd image 'kubernetes-dynamic-pvc-0783b011-6a04-11e8-a266-3e299ab03dc6':
        # 总大小（thin-provisioning的大小，不是实际占用磁盘大小），分布在多少个对象中
        size 2048 MB in 512 objects
        order 22 (4096 kB objects)
        block_name_prefix: rbd_data.7655b643c9869
        format: 2
        features: layering
        flags:
        create_timestamp: Thu Jun  7 11:36:58 2018</pre>
<div class="blog_h3"><span class="graybg">查看镜像状态</span></div>
<p>可以看到什么客户端在使用（watch）镜像：</p>
<pre class="crayon-plain-tag">rbd status k8s/kubernetes-dynamic-pvc-ca081cd3-01a0-11eb-99eb-ce0c4cdcd662
# Watchers:
#         watcher=192.168.106.18:0/756489925 client.254697953 cookie=18446462598732840981 </pre>
<div class="blog_h3"><span class="graybg">创建镜像</span></div>
<pre class="crayon-plain-tag">rbd create --size {megabytes} {pool-name}/{image-name}
# 示例
# 创建大小为1G的镜像
rbd create test --size 1G</pre>
<p>如果不指定存储池，则在默认池中创建镜像。</p>
<div class="blog_h3"><span class="graybg">修改镜像大小</span></div>
<pre class="crayon-plain-tag"># 修改镜像大小
rbd --image test resize --size 2G
# 不但可以扩大，还可以缩小
rbd --image test resize --size 1G --allow-shrink</pre>
<div class="blog_h3"><span class="graybg">映射为块设备</span></div>
<pre class="crayon-plain-tag"># 将镜像映射为本地块设备，可以进行格式化、挂载
rbd map test
# 格式化
mkfs.xfs -f /dev/rbd0
# 挂载
mount /dev/rbd0 /test

# 显示映射到本地块设备的镜像
rbd showmapped

# 卸载
umount /dev/rbd0
# 解除映射
rbd unmap /dev/rbd0</pre>
<div class="blog_h3"><span class="graybg">删除镜像</span></div>
<pre class="crayon-plain-tag">rbd rm {pool-name}/{image-name}

rbd --image test rm</pre>
<div class="blog_h3"><span class="graybg">延迟删除</span></div>
<pre class="crayon-plain-tag"># 放入回收站
rbd trash mv {pool-name}/{image-name}
# 彻底删除
rbd trash rm {pool-name}/{image-id}
# 还原
rbd trash restore {image-id}</pre>
<div class="blog_h3"><span class="graybg">快照管理</span></div>
<pre class="crayon-plain-tag"># 创建快照
rbd snap create --image test --snap test_snap
# 列出镜像的所有快照
rbd snap ls --image test

# 回滚到指定快照
rbd snap rollback --image test --snap test_snap
# 另一种写法
rbd snap rollback rbd/test@test_snap

# 删除快照，注意删除是异步进行的，空间不会立刻释放
rbd snap rm --image test --snap test_snap
rbd snap purge --image test

# 保护快照
rbd snap protect --image test --snap test_snap
# 取消保护
rbd snap unprotect --image test --snap test_snap

# 清除指定镜像的所有快照
rbd snap purge {pool-name}/{image-name}</pre>
<div class="blog_h3"><span class="graybg">镜像克隆</span></div>
<pre class="crayon-plain-tag"># 克隆镜像，注意只有镜像格式2才支持克隆
# 从快照创建克隆
rbd clone --image test --snap test_snap test_clone
# 列出快照的所有克隆
rbd children --image test --snap test_snap
# 将父镜像（被克隆的镜像的快照）的数据扁平化到子镜像，从而解除父子关联
rbd flatten --image test_clone</pre>
<div class="blog_h2"><span class="graybg">镜像镜像</span></div>
<p>从Jewel开始，RBD镜像可以异步的跨越两个集群进行镜像（Mirroring）。通过配置，你可以镜像池中的所有、或者一部分镜像。</p>
<div class="blog_h3"><span class="graybg">启用镜像复制</span></div>
<pre class="crayon-plain-tag">rbd mirror pool enable {pool-name} {mode}

# 启用名为local的集群的镜像复制，默认为pool
rbd --cluster local mirror pool enable image-pool pool
rbd --cluster remote mirror pool enable image-pool pool</pre>
<p>mode取值：</p>
<ol>
<li>pool，池中所有启用了journaling特性的镜像都被复制</li>
<li>image，只有明确配置的镜像才进行复制 </li>
</ol>
<div class="blog_h3"><span class="graybg">禁用镜像复制</span></div>
<pre class="crayon-plain-tag">rbd mirror pool disable {pool-name}

rbd --cluster local mirror pool disable image-pool
rbd --cluster remote mirror pool disable image-pool</pre>
<div class="blog_h2"><span class="graybg">操控对象</span></div>
<div class="blog_h3"><span class="graybg">创建对象</span></div>
<pre class="crayon-plain-tag"># 在池中创建一个对象，其内容来自文件
echo "Hello World" &gt; /tmp/file
rados -p rbd put helloworld /tmp/file</pre>
<div class="blog_h3"><span class="graybg">查看对象</span></div>
<pre class="crayon-plain-tag"># 查看对象
rados -p rbd ls | grep helloworld</pre>
<div class="blog_h2"><span class="graybg">操控CRUSH</span></div>
<pre class="crayon-plain-tag"># 根据CRUSH Map，列出OSD树
ceph osd tree
#                 缩进显示树层次
# ID  CLASS WEIGHT  TYPE NAME               STATUS REWEIGHT PRI-AFF 
#  -1       5.73999 root default                                    
#  -2       0.84000     host k8s-10-5-38-25                         
#   0   hdd 0.84000         osd.0               up  1.00000 1.00000 
#  -5       0.45000     host k8s-10-5-38-70                         
#   1   hdd 0.45000         osd.1               up  1.00000 1.00000 

# 移动桶的位置
# 将rack01移动到{root=default} 
ceph osd crush move rack01 root=default</pre>
<div class="blog_h2"><span class="graybg">操控PG</span></div>
<div class="blog_h3"><span class="graybg">镜像和PG对应关系</span></div>
<pre class="crayon-plain-tag"># 显示镜像和PG的关系
ceph osd map rbd  test
#                                                 此镜像存放在1.b5这个PG中
#                                                        此PG位于 osd.3 osd.1 osd.6中
#                                                                      主副本 位于osd.3中
# osdmap e26 pool 'rbd' (1) object 'test' -&gt; pg 1.40e8aab5 (1.b5) -&gt; up ([3,1,6], p3) acting ([3,1,6], p3)

# 显示PG和镜像的关系
ceph pg map 1.c0
# osdmap e1885 pg 1.c0 (1.c0) -&gt; up [9,8] acting [9,8]</pre>
<div class="blog_h3"><span class="graybg">Dump出PG统计信息</span></div>
<p>Dump出所有PG：</p>
<pre class="crayon-plain-tag">pg dump {all|summary|sum|delta|pools|osds|pgs|pgs_brief [all|summary|sum|delta|pools|osds|pgs|pgs_brief...]}
# 示例
ceph pg dump [--format {format}]  # format取值plain或json</pre>
<p>Dump出卡在指定状态中的PG的统计信息：</p>
<pre class="crayon-plain-tag"># threshold默认30秒
ceph pg dump_stuck inactive|unclean|stale|undersized|degraded [--format {format}] [-t|--threshold {seconds}]</pre>
<div class="blog_h3"><span class="graybg">修复PG</span></div>
<pre class="crayon-plain-tag">ceph pg repair 1.c0
# instructing pg 1.c0 on osd.9 to repair</pre>
<div class="blog_h3"><span class="graybg">优先回填或修复</span></div>
<pre class="crayon-plain-tag">ceph pg force-backfill &lt;pgid&gt; [&lt;pgid&gt;...]  
ceph pg force-recovery &lt;pgid&gt; [&lt;pgid&gt;...] 
# 取消
ceph pg cancel-force-backfill &lt;pgid&gt; [&lt;pgid&gt;...]
ceph pg cancel-force-recovery &lt;pgid&gt; [&lt;pgid&gt;...]</pre>
<div class="blog_h2"><span class="graybg">调整PG数量</span></div>
<div class="blog_h3"><span class="graybg">计算PG合理值</span></div>
<p>参考<a href="http://ceph.com/pgcalc/">官网的算法</a>进行计算。</p>
<div class="blog_h3"><span class="graybg">确保集群健康</span></div>
<p>执行调整之前，必须保证集群处于健康状态。</p>
<div class="blog_h3"><span class="graybg">调整数据同步参数</span></div>
<p>为避免调整PG数量导致业务性能受到严重影响，应该调整一些参数：</p>
<pre class="crayon-plain-tag">ceph tell osd.* injectargs '--osd-max-backfills 1'
ceph tell osd.* injectargs '--osd-recovery-max-active 1'</pre>
<p>其它相关的参数还包括：</p>
<pre class="crayon-plain-tag">osd_backfill_scan_min = 4 
osd_backfill_scan_max = 32 
osd recovery threads = 1 
osd recovery op priority = 1</pre>
<div class="blog_h3"><span class="graybg">调整PG数量 </span></div>
<p>按照2的幂进行翻倍增长，例如原来32个，可以先调整为64个。</p>
<p>注意：不要一下子把PG设置为太大的值，这会导致大规模的rebalance，影响系统性能。</p>
<div class="blog_h3"><span class="graybg">调整PGP数量</span></div>
<p>等到上一步操作后，集群变为Active+Clean状态后，再将pgp_num设置的和pg_num一致。 </p>
<div class="blog_h2"><span class="graybg">操控RGW</span></div>
<div class="blog_h3"><span class="graybg">手工安装</span></div>
<pre class="crayon-plain-tag"># 在RGW节点安装软件
# yum install ceph-radosgw

RGW_HOST=$(hostname -s)

# 在RGW节点，配置ceph.conf
cat &lt;&lt; EOF &gt;&gt; /etc/ceph/ceph.conf
[client.rgw.$RGW_HOST]
rgw_frontends = "civetweb port=7480"
EOF

# 拷贝配置到所有Ceph节点

# 在RGW节点，创建数据目录
mkdir -p /var/lib/ceph/radosgw/ceph-rgw.$RGW_HOST

# 在RGW节点，创建用户，输出Keyring
ceph auth get-or-create client.rgw.$RGW_HOST osd 'allow rwx' mon 'allow rw' \
    -o /var/lib/ceph/radosgw/ceph-rgw.$RGW_HOST/keyring
chown -R ceph:ceph /var/lib/ceph/radosgw

# 在RGW节点，启用Systemd服务
systemctl enable ceph-radosgw.target
systemctl enable ceph-radosgw@rgw.$RGW_HOST
systemctl start ceph-radosgw@rgw.$RGW_HOST</pre>
<div class="blog_h3"><span class="graybg">修改端口</span></div>
<pre class="crayon-plain-tag">[client.rgw.Carbon]
rgw_frontends = "civetweb port=80"</pre>
<p>推送修改后的配置文件后，重启RGW服务：</p>
<pre class="crayon-plain-tag">systemctl restart ceph-radosgw.service</pre>
<div class="blog_h3"><span class="graybg">启用SSL </span></div>
<pre class="crayon-plain-tag">[client.rgw.Carbon]
# 指定包含了私钥和证书的PEM
rgw_frontends = civetweb port=443s ssl_certificate=/etc/ceph/private/keyandcert.pem
# Luminous开始，可以同时绑定SSL和非SSL端口
rgw_frontends = civetweb port=80+443s ssl_certificate=/etc/ceph/private/keyandcert.pem</pre>
<div class="blog_h3"><span class="graybg">配置桶分片</span></div>
<p>RGW在index_pool池中存放桶（Bucket）索引数据，此池默认名为.rgw.buckets.index。</p>
<p>从0.94版本开始，支持对桶索引进行分片，避免<span style="background-color: #c0c0c0;">单个桶中对象数量过多时出现性能瓶颈</span>：</p>
<pre class="crayon-plain-tag"># 每个桶的最大索引分片数，默认0，表示不支持分片
rgw_override_bucket_index_max_shards = 0</pre>
<p>你可以在global段配置上面的选项。 </p>
<div class="blog_h3"><span class="graybg">启用用户</span></div>
<p>要使用RGW的RESTful接口，你需要：</p>
<ol>
<li>创建初始的S3接口的用户</li>
<li>创建Swift接口的子用户</li>
<li>验证用户可以访问网关</li>
</ol>
<p>要创建S3接口用户，需要在网关机上执行：</p>
<pre class="crayon-plain-tag">radosgw-admin user create --uid="rgw" --display-name="rgw"</pre>
<p>access_key、secret_key会打印在屏幕上，要访问网关，客户端必须提供这两个key：</p>
<pre class="crayon-plain-tag">{
    "user_id": "rgw",
    "display_name": "rgw",
    "email": "",
    "suspended": 0,
    "max_buckets": 1000,
    "auid": 0,
    "subusers": [],
    "keys": [
        {
            "user": "rgw",
            "access_key": "IN01UCU1M1996LK6OM88",
            "secret_key": "AuuAbroSUlWLykbQHCbFLVO6RU2ozUEjIFkYeoqc"
        }
    ],
    "swift_keys": [],
    "caps": [],
    "op_mask": "read, write, delete",
    "default_placement": "",
    "placement_tags": [],
    "bucket_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "user_quota": {
        "enabled": false,
        "check_on_raw": false,
        "max_size": -1,
        "max_size_kb": 0,
        "max_objects": -1
    },
    "temp_url_keys": [],
    "type": "rgw"
}</pre>
<p>要创建Swift子用户，需要在网关机上执行：</p>
<pre class="crayon-plain-tag">radosgw-admin subuser create --uid=alex --subuser=alex:swift --access=full</pre>
<p>你需要为Swift子用户创建secret key：</p>
<pre class="crayon-plain-tag">radosgw-admin key create --subuser=alex:swift --key-type=swift --gen-secret</pre>
<p>现在，你可以用自己熟悉的语言的S3、Swift客户端来验证用户是否可用。 </p>
<div class="blog_h3"><span class="graybg">操控桶</span></div>
<pre class="crayon-plain-tag">radosgw-admin bucket list                # 列出桶
radosgw-admin bucket limit check         # 显示桶的分片情况
radosgw-admin bucket link                # 将桶链接到用户
radosgw-admin bucket unlink              # 取消桶到用户的链接
radosgw-admin bucket stats               # 显示桶的统计信息
radosgw-admin bucket rm                  # 删除桶
radosgw-admin bucket check               # 检查桶索引
radosgw-admin bucket reshard             # 对桶进行重分片
radosgw-admin bucket sync disable        # 禁止桶同步
radosgw-admin bucket sync enable         # 启用桶同步</pre>
<p>要创建桶，你需要使用合法的User ID + AWS Access Key发起请求，Ceph没有提供对应的命令行。需要注意以下约束：</p>
<ol>
<li>桶名称必须唯一</li>
<li>桶名称不能格式化为IP地址</li>
<li>桶名称在3-63字符之间</li>
<li>桶名称不得包含大写字母、下划线，但是可以包含短横线</li>
<li>桶名称必须以小写字母或数字开头</li>
<li>桶名称必须由一系列的标签组成，每个标签用点号.分隔</li>
</ol>
<p>我们可以使用MinIO客户端创建桶：</p>
<pre class="crayon-plain-tag"># 添加配置
#                                               access_key           secret_key
mc config host add rgw https://rgw.gmem.cc:7480 IN01UCU1M1996LK6OM88 AuuAbroSUlWLykbQHCbFLVO6RU2ozUEjIFkYeoqc

# 创建桶
mc mb rgw/test</pre>
<p>现在通过Rgw命令行可以看到这个桶：</p>
<pre class="crayon-plain-tag">radosgw-admin buckets list
# [
#     "test"
# ]</pre>
<div class="blog_h2"><span class="graybg">管理身份验证</span></div>
<p>Ceph默认开启了cephx协议，加密认证需要消耗少量的资源。</p>
<p>启用cephx后，Cephe会自动在包括/etc/ceph/ceph.$name.keyring在内的位置寻找钥匙串，你可以指定keyring选项来修改默认路径，但是不推荐。</p>
<div class="blog_h3"><span class="graybg">手工启用</span></div>
<p>在禁用了cephx的集群上，启用它的步骤为：</p>
<ol>
<li>创建 client.admin 密钥：<br />
<pre class="crayon-plain-tag"># 如果你使用的自动部署工具已经生成此文件，切勿执行此命令，会覆盖
ceph auth get-or-create client.admin mon 'allow *' mds 'allow *' osd 'allow *' -o /etc/ceph/ceph.client.admin.keyring</pre>
</li>
<li>创建mon集群所需的钥匙串、密钥：<br />
<pre class="crayon-plain-tag">ceph-authtool --create-keyring /tmp/ceph.mon.keyring --gen-key -n mon. --cap mon 'allow *'</pre>
</li>
<li>将上述钥匙串复制到所有mon的mon data目录，例如：<br />
<pre class="crayon-plain-tag">cp /tmp/ceph.mon.keyring /var/lib/ceph/mon/ceph-a/keyring</pre>
</li>
<li>为每个OSD生成密钥：<br />
<pre class="crayon-plain-tag">ceph auth get-or-create osd.{$id} mon 'allow rwx' osd 'allow *' -o /var/lib/ceph/osd/ceph-{$id}/keyring</pre>
</li>
<li>
<p>为每个 MDS 生成密钥：</p>
<pre class="crayon-plain-tag">ceph auth get-or-create mds.{$id} mon 'allow rwx' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mds/ceph-{$id}/keyring</pre>
</li>
<li>
<p>添加以下内容到配置文件的global段：
<pre class="crayon-plain-tag">auth cluster required = cephx
auth service required = cephx
auth client required = cephx</pre>
</li>
<li>
<p>启动或重启Ceph集群：  
<pre class="crayon-plain-tag"># 停止当前节点上的所有Ceph守护进程
sudo stop ceph-all
sudo start ceph-all</pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">禁用认证</span></div>
<p>修改配置文件global段：
<pre class="crayon-plain-tag">auth cluster required = none
auth service required = none
auth client required = none</pre>
<p>然后重启Ceph集群。 </p>
<div class="blog_h3"><span class="graybg">身份验证命令</span></div>
<pre class="crayon-plain-tag"># 列出keyring
ceph auth ls
# 添加OSD的keyring
ceph auth add {osd} {--in-file|-i} {path-to-osd-keyring}</pre>
<div class="blog_h2"><span class="graybg">管理CRUSH map</span> </div>
<div class="blog_h3"><span class="graybg">手工管理</span></div>
<p>任何时后你都可以Dump、反编译、修改、编译、注入CURSH map。如果要完全基于手工方式管理，不使用自动生成的CRUSH map，可以设置：</p>
<pre class="crayon-plain-tag">osd crush update on start = false</pre>
<div class="blog_h3"><span class="graybg">查看Dump</span></div>
<p>执行命令<pre class="crayon-plain-tag">ceph osd crush dump</pre>，可以将整个CRUSH导出为可读形式：</p>
<pre class="crayon-plain-tag"># 下面的输出时安装后最初的状态，没有任何OSD

{   
    # 设备列表，最初为空                                                                                                                                                                       
    "devices": [],   
    # 桶类型定义列表                                                                                                                                                               
    "types": [                                                                                                                                                                           
        {                                                                                                                                                                                
            "type_id": 0,                                                                                                                                                                
            "name": "osd"                                                                                                                                                                
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 1,                                                                                                                                                                
            "name": "host"                                                                                                                                                               
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 2,                                                                                                                                                                
            "name": "chassis"                                                                                                                                                            
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 3,                                                                                                                                                                
            "name": "rack"                                                                                                                                                               
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 4,                                                                                                                                                                
            "name": "row"                                                                                                                                                                
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 5,                                                                                                                                                                
            "name": "pdu"                                                                                                                                                                
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 6,                                                                                                                                                                
            "name": "pod"                                                                                                                                                                
        },                                                                                                                                                                               
        {                                                                                                                                                                                
            "type_id": 7,                                                                                                                                                                
            "name": "room"                                                                                                                                                               
        },
        {
            "type_id": 8,
            "name": "datacenter"
        },
        {
            "type_id": 9,
            "name": "region"
        },
        {
            "type_id": 10,
            "name": "root"
        }
    ],
    # 桶列表，可以形成树状结构
    "buckets": [
        {
            "id": -1,
            "name": "default",
            "type_id": 10,
            "type_name": "root",
            "weight": 0,
            "alg": "straw2",
            "hash": "rjenkins1",
            "items": []
        }
        # 加入一个OSD节点（基于目录），自动生成如下两个Bucket：
        {                                                                                                                                                                          
            "id": -2,                                                                                                                                                              
            "name": "k8s-10-5-38-25",                                                                                                                                              
            "type_id": 1,                                                                                                                                                          
            "type_name": "host",                                                                                                                                                   
            "weight": 55050,                                                                                                                                                       
            "alg": "straw2",                                                                                                                                                       
            "hash": "rjenkins1",                                                                                                                                                   
            "items": [                                                                                                                                                             
                {                                                                                                                                                                  
                    "id": 0,                                                                                                                                                       
                    "weight": 55050,                                                                                                                                               
                    "pos": 0                                                                                                                                                       
                }                                                                                                                                                                  
            ]                                                                                                                                                                      
        },                                                                                                                                                                         
        {                                                                                                                                                                          
            "id": -3,                                                                                                                                                              
            "name": "k8s-10-5-38-25~hdd",                                                                                                                                          
            "type_id": 1,                                                                                                                                                          
            "type_name": "host",                                                                                                                                                   
            "weight": 55050,                                                                                                                                                       
            "alg": "straw2",                                                                                                                                                       
            "hash": "rjenkins1",                                                                                                                                                   
            "items": [                                                                                                                                                             
                {
                    "id": 0,
                    "weight": 55050,
                    "pos": 0
                }
            ]
        },

    ],
    # 规则列表
    "rules": [
        {
            "rule_id": 0,
            "rule_name": "replicated_rule",
            # 所属规则集
            "ruleset": 0,
            # 此规则是否用于RAID，取值replicated 或 raid4
            "type": 1,
            # 如果Pool的副本份数不在此范围内，则CRUSH不会使用当前规则
            "min_size": 1,
            "max_size": 10,
            "steps": [
                {
                    # 选择一个桶，并迭代其子树
                    "op": "take",
                    "item": -1,
                    "item_name": "default"
                },
                {
                    # 在上一步的基础上，确定每个副本如何放置
                    "op": "chooseleaf_firstn",
                    # 取值0，此Step适用pool-num-replicas个副本（所有）
                    # 取值&gt;0 &amp; &lt; pool-num-replicas，适用num个副本
                    # 取值&lt;0，适用pool-num-replicas -num个副本
                    "num": 0,
                    "type": "host"
                },
                {
                    "op": "emit"
                }
            ]
        }
    ],
    # 可微调参数，以及一些状态信息
    "tunables": {                                                                                                                                                                        
        "choose_local_tries": 0,                                                                                                                                                         
        "choose_local_fallback_tries": 0,                                                                                                                                                
        "choose_total_tries": 50,                                                                                                                                                        
        "chooseleaf_descend_once": 1,                                                                                                                                                    
        "chooseleaf_vary_r": 1,                                                                                                                                                          
        "chooseleaf_stable": 1,                                                                                                                                                          
        "straw_calc_version": 1,                                                                                                                                                         
        "allowed_bucket_algs": 54,    
        # 使用的Profile，执行ceph osd crush tunables hammer后此字段改变，连带其它tunables字段自动改变                                                                                                                                                   
        "profile": "jewel",                                                                                                                                                              
        "optimal_tunables": 1,                                                                                                                                                           
        "legacy_tunables": 0,                                                                                                                                                            
        "minimum_required_version": "jewel",                                                                                                                                             
        "require_feature_tunables": 1,                                                                                                                                                   
        "require_feature_tunables2": 1,                                                                                                                                                  
        "has_v2_rules": 0,                                                                                                                                                               
        "require_feature_tunables3": 1,                                                                                                                                                  
        "has_v3_rules": 0,                                                                                                                                                               
        "has_v4_buckets": 1,                                                                                                                                                             
        "require_feature_tunables5": 1,
        "has_v5_rules": 0
    },
    "choose_args": {}
}</pre>
<div class="blog_h3"><span class="graybg">编辑Dump</span></div>
<p>执行下面的命令，导出当前Map：</p>
<pre class="crayon-plain-tag">ceph osd getcrushmap -o curshmap</pre>
<p>然后，需要反编译为文本：</p>
<pre class="crayon-plain-tag">crushtool -d curshmap -o curshmap.src</pre>
<p>源文件内容示例：</p>
<pre class="crayon-plain-tag"># begin crush map
tunable choose_local_tries 0
tunable choose_local_fallback_tries 0
tunable choose_total_tries 50
tunable chooseleaf_descend_once 1
tunable chooseleaf_vary_r 1
tunable straw_calc_version 1
tunable allowed_bucket_algs 54

# devices

# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 region.Values.storageclass.fsType
type 10 root

# buckets
root default {
        id -1           # do not change unnecessarily
        # weight 0.000
        alg straw2           
        hash 0  # rjenkins1
}
# rules
rule replicated_rule {
        id 0
        type replicated
        min_size 1
        max_size 10
        step take default
        step chooseleaf firstn 0 type host
        step emit
}

# end crush map</pre>
<p>我们可以根据实际需要，对源文件进行修改，例如将算法改为straw，解决CentOS 7上CEPH_FEATURE_CRUSH_V4 1000000000000特性不满足的问题：</p>
<pre class="crayon-plain-tag">sed -i 's/straw2/straw/g' curshmap.src</pre>
<p>修改源文件完毕后，执行下面的命令编译：</p>
<pre class="crayon-plain-tag">crushtool -c curshmap.src -o curshmap</pre>
<p>最后，注入最新编译的Map：</p>
<pre class="crayon-plain-tag">ceph osd setcrushmap -i curshmap
# 会输出修订版号 </pre>
<div class="blog_h3"><span class="graybg">修改设备类型</span></div>
<p>默认情况下，Ceph自动根据硬件类型，设置OSD的设备类型为hdd, ssd或nvme。你可以手工进行设置：</p>
<pre class="crayon-plain-tag"># 你需要移除当前设置的设备类型，才能重新设置
ceph osd crush rm-device-class &lt;osd-name&gt; [...]
# 示例
ceph osd crush rm-device-class osd.3 osd.4 osd.5 osd.6 osd.7 osd.8 osd.0 osd.10 osd.1 osd.12 osd.2 osd.13 


ceph osd crush set-device-class &lt;class&gt; &lt;osd-name&gt; [...]
# 示例
ceph osd crush set-device-class ssd osd.3 osd.4 osd.5 osd.6 osd.7 osd.8 osd.0 osd.10 osd.1 osd.12 osd.2 osd.13</pre>
<div class="blog_h3"><span class="graybg">查看规则</span></div>
<p>列出集群中的CRUSH rule：</p>
<pre class="crayon-plain-tag">ceph osd crush rule ls</pre>
<p>Dump出规则的内容：</p>
<pre class="crayon-plain-tag">ceph osd crush rule dump</pre>
<div class="blog_h3"><span class="graybg">删除规则</span></div>
<pre class="crayon-plain-tag">ceph osd crush rule rm replicated_rule_ssd</pre>
<div class="blog_h3"><span class="graybg">创建规则 </span></div>
<p>创建一个规则，仅仅使用指定类型的设备：</p>
<pre class="crayon-plain-tag">ceph osd crush rule create-replicated &lt;rule-name&gt; &lt;root&gt; &lt;failure-domain&gt; &lt;class&gt;
# 示例：仅仅使用ssd类型的设备，失败域为host，也就是数据副本必须位于不同的主机上
ceph osd crush rule create-replicated replicated_rule_ssd default host ssd
ceph osd crush rule create-replicated replicated_rule_hdd default host hdd</pre>
<div class="blog_h3"><span class="graybg">应用规则</span></div>
<p>为存储池指定所使用的规则：</p>
<pre class="crayon-plain-tag">ceph osd pool set &lt;pool-name&gt; crush_rule &lt;rule-name&gt;
# 修改规则
ceph osd pool set rbd-ssd crush_rule replicated_rule_ssd
# 创建存储池时指定规则
ceph osd pool create rbd-ssd 384 replicated replicated_rule_ssd</pre>
<div class="blog_h3"><span class="graybg">编辑规则</span></div>
<p>CRUSH rule的语法如下：</p>
<pre class="crayon-plain-tag">rule &lt;rulename&gt; {
        ruleset &lt;ruleset&gt;
        type [ replicated | erasure ]
        min_size &lt;min-size&gt;
        max_size &lt;max-size&gt;
        # 根据桶名称来选取CRUSH子树，并迭代，可限定设备类型
        step take &lt;bucket-name&gt; [class &lt;device-class&gt;]
        # choose：选择指定数量、类型的桶
        # chooseleaf：选择指定数量、类型的桶，并选择每个这些桶的一个叶子节点
        step [choose|chooseleaf] [firstn|indep] &lt;N&gt; &lt;bucket-type&gt;
        step emit
}</pre>
<p>示例一，将主副本存放在SSD中，第二副本存放在HDD中：</p>
<pre class="crayon-plain-tag">rule ssd-primary-affinity {
    ruleset 0
    type replicated
    min_size 2
    max_size 3
    # 选择名为SSD的桶
    step take ssd
    # 在上述桶中的host类型的子树中选择叶子节点，存储1个副本（第一个）
    step chooseleaf firstn 1 type host
    # 执行
    step emit
    # 选择名为HDD的桶
    step take hdd
    # 在上述桶中的host类型的子树中选择叶子节点，存储N-1个副本（所有其它副本）
    step chooseleaf firstn -1 type host
    step emit
}</pre>
<p> 示意二，在第一个机架上存储两个副本，第二个机架上存储一个副本：</p>
<pre class="crayon-plain-tag">rule 3_rep_2_racks {
   ruleset 1
   type replicated
   min_size 2
   max_size 3
   step take default
   # 选择一个Rack，存储2个副本
   step choose firstn 2 type rack
   # 在上述选定的Rack中选择Host
   step chooseleaf firstn 2 type host
   step emit
}</pre>
<div class="blog_h3"><span class="graybg">增加OSD</span></div>
<p>如果要添加OSD到CRUSH map中，执行：</p>
<pre class="crayon-plain-tag">ceph osd crush set {name} {weight} root={root} [{bucket-type}={bucket-name} ...]
# 示例
ceph osd crush set osd.14 0 host=xenial-100
ceph osd crush set osd.0 1.0 root=default datacenter=dc1 room=room1 row=foo rack=bar host=foo-bar-1</pre>
<div class="blog_h3"><span class="graybg">调整OSD权重</span></div>
<pre class="crayon-plain-tag">ceph osd crush reweight {name} {weight}</pre>
<div class="blog_h3"><span class="graybg">移除OSD</span></div>
<pre class="crayon-plain-tag">ceph osd crush remove {name}</pre>
<div class="blog_h3"><span class="graybg">增加Bucket</span></div>
<pre class="crayon-plain-tag">ceph osd crush add-bucket {bucket-name} {bucket-type}</pre>
<div class="blog_h3"><span class="graybg">移动Bucket </span></div>
<pre class="crayon-plain-tag">ceph osd crush move {bucket-name} {bucket-type}={bucket-name}, [...]</pre>
<div class="blog_h3"><span class="graybg">移除Bucket</span></div>
<pre class="crayon-plain-tag">ceph osd crush remove {bucket-name} </pre>
<div class="blog_h3"><span class="graybg">调整Tunable</span></div>
<pre class="crayon-plain-tag"># 自动优化
ceph osd crush tunables optimal
# 最大兼容性，存在老旧内核的cephfs/rbd客户端时
ceph osd crush tunables legacy


# 选择一个PROFILE，例如jewel
ceph osd crush tunables {PROFILE} </pre>
<div class="blog_h3"><span class="graybg">避免作为主OSD</span></div>
<pre class="crayon-plain-tag"># weight在0-1之间，默认1，值越小，CRUSH 越避免将目标OSD作为主
ceph osd primary-affinity &lt;osd-id&gt; &lt;weight&gt;</pre>
<div class="blog_h3"><span class="graybg">分配Pool到特定OSD</span></div>
<p>使用CRUSH rule，可以限定某个Pool仅仅使用一部分OSD：</p>
<pre class="crayon-plain-tag"># SSD主机
host ceph-osd-ssd-server-1 {
      id -1
      alg straw
      hash 0
      item osd.0 weight 1.00
      item osd.1 weight 1.00
}

# HDD主机
host ceph-osd-hdd-server-1 {
      id -3
      alg straw
      hash 0
      item osd.4 weight 1.00
      item osd.5 weight 1.00
}

# HDD的根桶
root hdd {
      id -5
      alg straw
      hash 0
      item ceph-osd-hdd-server-1 weight 2.00
}

# SSD的根桶
root ssd {
      id -6
      alg straw
      hash 0
      item ceph-osd-ssd-server-1 weight 2.00
}


# 仅仅使用HDD的规则
rule hdd {
      ruleset 3
      type replicated
      min_size 0
      max_size 10
      step take hdd
      # 选择
      step chooseleaf firstn 0 type host
      step emit
}

# 仅仅使用SSD的规则
rule ssd {
      ruleset 4
      type replicated
      min_size 0
      max_size 4
      step take ssd
      step chooseleaf firstn 0 type host
      step emit
}

# 在SSD上存储主副本，其它副本存放在HDD
rule ssd-primary {
      ruleset 5
      type replicated
      min_size 5
      max_size 10
      step take ssd
      step chooseleaf firstn 1 type host
      step emit
      step take hdd
      step chooseleaf firstn -1 type host
      step emit
}</pre>
<div class="blog_h2"><span class="graybg">修改日志尺寸</span></div>
<p>前提条件：</p>
<ol>
<li>集群处于OK状态</li>
<li>所有PG处于active+clean状态</li>
</ol>
<p>步骤，针对每个需要改变尺寸的OSD，一个个的处理：</p>
<ol>
<li>修改Cephe配置，设置<pre class="crayon-plain-tag">osd_journal_size = NEWSIZE</pre></li>
<li>禁止数据迁移（防止OSD进入out状态）：<pre class="crayon-plain-tag">ceph osd set noout</pre></li>
<li>停止目标OSD实例</li>
<li>刷出缓存：<pre class="crayon-plain-tag">ceph-osd -i  OSDID --flush-journal</pre></li>
<li>删除日志：<br />
<pre class="crayon-plain-tag"># 基于Helm部署时，需要到宿主机上的osd_directory下寻找对应目录
cd /var/lib/ceph/osd/ceph-osd.OSDID
rm journal </pre>
</li>
<li>创建一个新的日志文件：<pre class="crayon-plain-tag">ceph-osd --mkjournal -i OSDID</pre></li>
<li>启动OSD</li>
<li>验证新的日志尺寸被使用：<br />
<pre class="crayon-plain-tag"># Helm安装的情况下，需要在OSD容器中执行
ceph --admin-daemon /var/run/ceph/ceph-osd.OSDID.asok config get osd_journal_size</pre>
</li>
<li>
<p>确保集群处于OK状态，所有PG处于active+clean状态 </p>
</li>
</ol>
<p>处理完所有OSD后，执行： <pre class="crayon-plain-tag">ceph osd unset noout</pre>，清除noout标记</p>
<div class="blog_h2"><span class="graybg">性能测试</span></div>
<div class="blog_h3"><span class="graybg">rbd bench</span></div>
<pre class="crayon-plain-tag"># 默认4K，可以--io-size定制
# 默认16线程，可以--io-threads定制

# 随机读
rbd bench -p rbd-hdd --image benchmark --io-total 128M --io-type read --io-pattern rand
# elapsed: 25 ops:  32768 ops/sec:  1284.01  bytes/sec: 5259316.53
# elapsed: 15 ops: 327680 ops/sec: 20891.46  bytes/sec: 85571410.91
# HDD差20倍

# 顺序读
rbd bench -p rbd-hdd --image benchmark --io-total 64M --io-type read --io-pattern seq
# elapsed: 46 ops:  163840 ops/sec:   3528.06  bytes/sec: 14450938.87
# elapsed: 45 ops: 1638400 ops/sec:   35672.87 bytes/sec: 146116057.32
# HDD差10倍

# 随机写
rbd bench -p rbd-hdd --image benchmark --io-total 128M --io-type write --io-pattern rand
# elapsed: 85  ops:  32768 ops/sec: 383.24  bytes/sec: 1569743.22
# elapsed: 111 ops: 327680 ops/sec: 2936.78 bytes/sec: 12029055.24
# HDD差7倍

# 顺序写
rbd bench -p rbd-hdd --image benchmark --io-total 128M --io-type write --io-pattern seq
# elapsed: 3  ops: 32768  ops/sec:  9382.16 bytes/sec: 38429334.91
# elapsed: 17 ops: 327680 ops/sec: 18374.69 bytes/sec: 75262749.05
# HDD差1倍</pre>
<div class="blog_h2"><span class="graybg">运行时修改参数</span></div>
<p>要动态、临时（重启后消失）的修改组件的参数，可以使用tell命令。</p>
<div class="blog_h3"><span class="graybg">控制恢复进程</span></div>
<pre class="crayon-plain-tag"># 临时修改所有OSD和恢复相关的选项
ceph tell osd.* injectargs '--osd-max-backfills 1'             # 并发回填操作数
ceph tell osd.* injectargs '--osd-recovery-threads 1'          # 恢复线程数量
ceph tell osd.* injectargs '--osd-recovery-op-priority 1'      # 恢复线程优先级  
ceph tell osd.* injectargs '--osd-client-op-priority 63'       # 客户端线程优先级
ceph tell osd.* injectargs '--osd-recovery-max-active 1'       # 最大活跃的恢复请求数</pre>
<div class="blog_h2"><span class="graybg">管理Watcher</span></div>
<div class="blog_h3"><span class="graybg">加入黑名单</span></div>
<p>可以将RBD上的Watcher加入黑名单，这样可以解除RBD的Watcher，再其它机器上挂载RBD：</p>
<pre class="crayon-plain-tag">rbd status  kubernetes-dynamic-pvc-22d9e659-6e31-11e8-92e5-c6b9f35768f0                                                                                                     
# Watchers:                                                                                                                                                                                
#         watcher=10.0.3.1:0/158685765 client.3524447 cookie=18446462598732840965

# 添加到黑名单
ceph osd blacklist add 10.0.3.1:0/158685765
# blacklisting 10.0.3.1:0/158685765 until 2018-08-21 18:04:31.855791 (3600 sec)

rbd status  kubernetes-dynamic-pvc-22d9e659-6e31-11e8-92e5-c6b9f35768f0
# Watchers: none</pre>
<div class="blog_h3"><span class="graybg">显示黑名单</span></div>
<pre class="crayon-plain-tag">ceph osd blacklist ls
# listed 1 entries
# 10.0.3.1:0/158685765 2018-08-21 18:04:31.855791</pre>
<div class="blog_h3"><span class="graybg">移除黑名单</span></div>
<pre class="crayon-plain-tag">ceph osd blacklist rm 10.0.3.1:0/158685765</pre>
<div class="blog_h3"><span class="graybg">清空黑名单 </span></div>
<pre class="crayon-plain-tag">ceph osd blacklist clear</pre>
<div class="blog_h2"><span class="graybg">RADOS管理</span></div>
<div class="blog_h3"><span class="graybg">管理池</span></div>
<pre class="crayon-plain-tag"># 列出池
rados lspools
.rgw.root
default.rgw.control
default.rgw.meta
default.rgw.log
rbd
rbd-ssd
rbd-hdd

# 创建池pool-name，使用auid 123，使用crush规则4
mkpool pool-name [123[ 4]] 

# 复制池的内容
cppool pool-name dest-pool

# 移除池
rmpool pool-name pool-name --yes-i-really-really-mean-it

# 清空池中对象
purge pool-name --yes-i-really-really-mean-it

# 显示每个池的对象数量、空间占用情况
rados df

# 列出池中对象
rados ls -p rbd

# 将池的所有者设置为auid 123
rados chown 123  -p rbd</pre>
<div class="blog_h3"><span class="graybg">管理快照</span> </div>
<pre class="crayon-plain-tag"># 列出池快照
rados lssnap -p rbd

# 创建池快照
rados mksnap snap-name -p rbd

# 删除池快照
rados rmsnap mksnap snap-name -p rbd

# 从快照中恢复对象
rados rollback &lt;obj-name&gt; &lt;snap-name&gt;

# 列出对象的快照
rados listsnaps &lt;obj-name&gt;</pre>
<div class="blog_h3"><span class="graybg">读写对象</span></div>
<pre class="crayon-plain-tag"># 读对象
rados get object-name /tmp/obj -p rbd

# 使用指定的偏移量写对象
rados put object-name /tmp/obj --offset offset

# 附加内容到对象
rados append &lt;obj-name&gt; [infile

# 截断对象为指定的长度
rados truncate &lt;obj-name&gt; length

# 创建对象
rados create &lt;obj-name&gt;

# 移除对象
rados rm &lt;obj-name&gt; ...[--force-full]

# 复制对象
rados cp &lt;obj-name&gt; [target-obj]</pre>
<div class="blog_h3"><span class="graybg">读写对象属性</span></div>
<pre class="crayon-plain-tag"># 列出扩展属性
rados listxattr &lt;obj-name&gt;
# 获取扩展属性
rados getxattr &lt;obj-name&gt; attr
# 设置扩展属性
rados setxattr &lt;obj-name&gt; attr val
# 移除扩展属性
rados rmxattr &lt;obj-name&gt; attr

# 显示属性
rados stat &lt;obj-name&gt;</pre>
<div class="blog_h3"><span class="graybg">列出不一致PG</span></div>
<pre class="crayon-plain-tag">rados list-inconsistent-pg pool-name</pre>
<div class="blog_h3"><span class="graybg">列出不一致对象</span></div>
<pre class="crayon-plain-tag">rados list-inconsistent-obj  40.14  --format=json-pretty</pre>
<div class="blog_h3"><span class="graybg">列出不一致快照</span></div>
<pre class="crayon-plain-tag">rados list-inconsistent-snapset 40.14 </pre>
<div class="blog_h2"><span class="graybg">配置仪表盘</span></div>
<div class="blog_h3"><span class="graybg">启用仪表盘</span></div>
<pre class="crayon-plain-tag">ceph mgr module enable dashboard</pre>
<div class="blog_h3"><span class="graybg">SSL支持配置</span></div>
<pre class="crayon-plain-tag"># 使用自签名证书
ceph dashboard create-self-signed-cert

# 使用外部提供的证书
ceph dashboard set-ssl-certificate -i dashboard.crt
ceph dashboard set-ssl-certificate-key -i dashboard.key

# 禁用SSL
ceph config set mgr mgr/dashboard/ssl false</pre>
<div class="blog_h3"><span class="graybg">设置用户</span></div>
<pre class="crayon-plain-tag">ceph dashboard ac-user-create admin  administrator -i - &lt;&lt;&lt;"pswd"</pre>
<div class="blog_h3"><span class="graybg">管理RGW</span></div>
<pre class="crayon-plain-tag"># 创建用户
radosgw-admin user create --uid=rgw --display-name=rgw --system

# 设置access_key和secret_key
ceph dashboard set-rgw-api-access-key -i - &lt;&lt;&lt; "$(radosgw-admin user info --uid=rgw | jq -r .keys[0].access_key)"
ceph dashboard set-rgw-api-secret-key -i - &lt;&lt;&lt; "$(radosgw-admin user info --uid=rgw | jq -r .keys[0].secret_key)"

# 禁用SSL校验
ceph dashboard set-rgw-api-ssl-verify False</pre>
<div class="blog_h1"><span class="graybg">诊断</span></div>
<div class="blog_h2"><span class="graybg">调试日志</span></div>
<p>注意：详尽的日志每小时可能超过 1GB ，如果你的系统盘满了，这个节点就会停止工作。</p>
<div class="blog_h3"><span class="graybg">临时启用调试日志</span></div>
<pre class="crayon-plain-tag"># 通过中心化配置下发
ceph tell osd.0 config set debug_osd 0/5

# 到目标主机上，针对OSD进程设置
ceph daemon osd.0 config set debug_osd 0/5</pre>
<div class="blog_h3"><span class="graybg">配置日志级别</span></div>
<p>可以为各子系统定制日志级别：</p>
<pre class="crayon-plain-tag"># debug {subsystem} = {log-level}/{memory-level}

[global]
        debug ms = 1/5
[mon]
        debug mon = 20
        debug paxos = 1/5
        debug auth = 2
[osd]
        debug osd = 1/5
        debug filestore = 1/5
        debug journal = 1
        debug monc = 5/20
[mds]
        debug mds = 1
        debug mds balancer = 1
        debug mds log = 1
        debug mds migrator = 1</pre>
<p>子系统列表：</p>
<table class=" full-width">
<thead>
<tr>
<td class="head" style="text-align: center;">
<p><strong>子系统</strong></p>
</td>
<td class="head" style="text-align: center;">
<p><strong>日志级别</strong></p>
</td>
<td class="head" style="text-align: center;">
<p><strong>内存日志级别</strong></p>
</td>
</tr>
</thead>
<tbody>
<tr>
<td>default</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>lockdep</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>context</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>crush</td>
<td>
<p>1</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>mds</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mds <span class="pre">balancer</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mds <span class="pre">locker</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mds <span class="pre">log</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mds <span class="pre">log</span> <span class="pre">expire</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mds <span class="pre">migrator</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>buffer</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>timer</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>filer</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>striper</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>objecter</td>
<td>
<p>0</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>rados</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rbd</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rbd <span class="pre">mirror</span></td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rbd <span class="pre">replay</span></td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>journaler</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>objectcacher</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>client</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>osd</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>optracker</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>objclass</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>filestore</td>
<td>
<p>1</p>
</td>
<td>
<p>3</p>
</td>
</tr>
<tr>
<td>journal</td>
<td>
<p>1</p>
</td>
<td>
<p>3</p>
</td>
</tr>
<tr>
<td>ms</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mon</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>monc</td>
<td>
<p>0</p>
</td>
<td>
<p>10</p>
</td>
</tr>
<tr>
<td>paxos</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>tp</td>
<td>
<p>0</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>auth</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>crypto</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>finisher</td>
<td>
<p>1</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>reserver</td>
<td>
<p>1</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>heartbeatmap</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>perfcounter</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rgw</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rgw <span class="pre">sync</span></td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>civetweb</td>
<td>
<p>1</p>
</td>
<td>
<p>10</p>
</td>
</tr>
<tr>
<td>javaclient</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>asok</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>throttle</td>
<td>
<p>1</p>
</td>
<td>
<p>1</p>
</td>
</tr>
<tr>
<td>refs</td>
<td>
<p>0</p>
</td>
<td>
<p>0</p>
</td>
</tr>
<tr>
<td>compressor</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>bluestore</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>bluefs</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>bdev</td>
<td>
<p>1</p>
</td>
<td>
<p>3</p>
</td>
</tr>
<tr>
<td>kstore</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>rocksdb</td>
<td>
<p>4</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>leveldb</td>
<td>
<p>4</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>memdb</td>
<td>
<p>4</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>fuse</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mgr</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>mgrc</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>dpdk</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
<tr>
<td>eventtrace</td>
<td>
<p>1</p>
</td>
<td>
<p>5</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">加快日志滚动</span></div>
<p>如果磁盘空间有限，可以配置/etc/logrotate.d/ceph，加快日志滚动：</p>
<pre class="crayon-plain-tag">rotate 7
weekly
size 500M
compress
sharedscripts</pre>
<p>然后设置定时任务，定期检查并清理： </p>
<pre class="crayon-plain-tag">30 * * * * /usr/sbin/logrotate /etc/logrotate.d/ceph &gt;/dev/null 2&gt;&amp;1</pre>
<p>&nbsp;</p>
<div class="blog_h1"><span class="graybg">集成K8S</span></div>
<div class="blog_h2"><span class="graybg">基于Helm</span></div>
<div class="blog_h3"><span class="graybg">简介</span></div>
<p>为了将Ceph部署到K8S集群中，可以利用<a href="https://github.com/ceph/ceph-helm/">ceph-helm项目</a>。 目前此项目存在一些限制：</p>
<ol>
<li>public和cluster网络必须一样</li>
<li>如果Storage的用户不是admin，你需要在Ceph集群中手工创建用户，并在K8S中创建对应的Secrets</li>
<li>ceph-mgr只能运行单副本</li>
</ol>
<div class="blog_h3"><span class="graybg">仓库</span></div>
<p>执行下面的命令把ceph-helm添加到本地Helm仓库：</p>
<pre class="crayon-plain-tag"># 此项目使用Helm本地仓库保存Chart，如果没有启动本地存储，请启动
nohup /usr/local/bin/helm serve  --address 0.0.0.0:8879 &gt; /dev/null 2&gt;&amp;1 &amp;

git clone https://github.com/ceph/ceph-helm
pushd ceph-helm/ceph
make
popd
# 构建成功后Chart归档文件位于 ./ceph-0.1.0.tgz</pre>
<div class="blog_h3"><span class="graybg">覆盖值</span></div>
<p>可用值的说明如下：</p>
<pre class="crayon-plain-tag"># 部署哪些组件
deployment:
  ceph: true
  storage_secrets: true
  client_secrets: true
  rbd_provisioner: true
  rgw_keystone_user_and_endpoints: false

# 修改这些值可以指定其它镜像
images:
  ks_user: docker.io/kolla/ubuntu-source-heat-engine:3.0.3
  ks_service: docker.io/kolla/ubuntu-source-heat-engine:3.0.3
  ks_endpoints: docker.io/kolla/ubuntu-source-heat-engine:3.0.3
  bootstrap: docker.io/ceph/daemon:tag-build-master-luminous-ubuntu-16.04
  dep_check: docker.io/kolla/ubuntu-source-kubernetes-entrypoint:4.0.0
  daemon: docker.io/ceph/daemon:tag-build-master-luminous-ubuntu-16.04
  ceph_config_helper: docker.io/port/ceph-config-helper:v1.7.5
  # 如果使用官方提供的StorageClass，你需要扩展kube-controller镜像，否则报executable file not found in $PATH
  rbd_provisioner: quay.io/external_storage/rbd-provisioner:v0.1.1
  minimal: docker.io/alpine:latest
  pull_policy: "IfNotPresent"

# 不同Ceph组件使用什么节点选择器
labels:
  jobs:
    node_selector_key: ceph-mon
    node_selector_value: enabled
  mon:
    node_selector_key: ceph-mon
    node_selector_value: enabled
  mds:
    node_selector_key: ceph-mds
    node_selector_value: enabled
  osd:
    node_selector_key: ceph-osd
    node_selector_value: enabled
  rgw:
    node_selector_key: ceph-rgw
    node_selector_value: enabled
  mgr:
    node_selector_key: ceph-mgr
    node_selector_value: enabled

pod:
  dns_policy: "ClusterFirstWithHostNet"
  replicas:
    rgw: 1
    mon_check: 1
    rbd_provisioner: 2
    mgr: 1
  affinity:
      anti:
        type:
          default: preferredDuringSchedulingIgnoredDuringExecution
        topologyKey:
          default: kubernetes.io/hostname
  # 如果集群资源匮乏，可以调整下面的资源请求
  resources:
    enabled: false
    osd:
      requests:
        memory: "256Mi"
        cpu: "100m"
      limits:
        memory: "1024Mi"
        cpu: "1000m"
    mds:
      requests:
        memory: "10Mi"
        cpu: "100m"
      limits:
        memory: "50Mi"
        cpu: "500m"
    mon:
      requests:
        memory: "50Mi"
        cpu: "100m"
      limits:
        memory: "100Mi"
        cpu: "500m"
    mon_check:
      requests:
        memory: "5Mi"
        cpu: "100m"
      limits:
        memory: "50Mi"
        cpu: "500m"
    rgw:
      requests:
        memory: "5Mi"
        cpu: "100m"
      limits:
        memory: "50Mi"
        cpu: "500m"
    rbd_provisioner:
      requests:
        memory: "5Mi"
        cpu: "100m"
      limits:
        memory: "50Mi"
        cpu: "500m"
    mgr:
      requests:
        memory: "5Mi"
        cpu: "100m"
      limits:
        memory: "50Mi"
        cpu: "500m"
    jobs:
      bootstrap:
        limits:
          memory: "1024Mi"
          cpu: "2000m"
        requests:
          memory: "128Mi"
          cpu: "100m"
      secret_provisioning:
        limits:
          memory: "1024Mi"
          cpu: "2000m"
        requests:
          memory: "128Mi"
          cpu: "100m"
      ks_endpoints:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "1024Mi"
          cpu: "2000m"
      ks_service:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "1024Mi"
          cpu: "2000m"
      ks_user:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "1024Mi"
          cpu: "2000m"

secrets:
  keyrings:
    mon: ceph-mon-keyring
    mds: ceph-bootstrap-mds-keyring
    osd: ceph-bootstrap-osd-keyring
    rgw: ceph-bootstrap-rgw-keyring
    mgr: ceph-bootstrap-mgr-keyring
    admin: ceph-client-admin-keyring
  identity:
    admin: ceph-keystone-admin
    user: ceph-keystone-user
    user_rgw: ceph-keystone-user-rgw

# !! 根据实际情况网络配置
network:
  public:   10.0.0.0/16
  cluster:  10.0.0.0/16
  port:
    mon: 6789
    rgw: 8088

# !! 在此添加需要的Ceph配置项
conf:
  # 对象存储网关服务相关
  rgw_ks:
    config:
      rgw_keystone_api_version: 3
      rgw_keystone_accepted_roles: "admin, _member_"
      rgw_keystone_implicit_tenants: true
      rgw_s3_auth_use_keystone: true
  ceph:
    override:
    append:
    config:
      global:
        mon_host: null
      osd:
        ms_bind_port_max: 7100

ceph:
  rgw_keystone_auth: false
  enabled:
    mds: true
    rgw: true
    mgr: true
  storage:
    # 基于目录的OSD，在宿主机上存储的路径
    # /var/lib/ceph-helm/osd会挂载到容器的/var/lib/ceph/osd目录
    osd_directory: /var/lib/ceph-helm
    mon_directory: /var/lib/ceph-helm
    # 将日志收集到/var/log，便于fluentd来采集
    mon_log: /var/log/ceph/mon
    osd_log: /var/log/ceph/osd

# !! 是否启用基于目录的OSD，需要配合节点标签ceph-osd=enabled
# 存储的位置由上面的storage.osd_directory确定，沿用现有的文件系统
osd_directory:
  enabled: false

# 如果设置为1，则允许Ceph格式化磁盘，这会导致数据丢失
enable_zap_and_potentially_lose_data: true
# !! 基于块设备的OSD，需要配合节点标签ceph-osd-device-dev-***=enabled
osd_devices:
  - name: dev-vdb
    # 使用的块设备
    device: /dev/vdb
    # 日志可以存储到独立块设备上，提升性能，如果不指定，存放在device
    journal: /dev/vdc
    # 是否删除其分区表
    zap: "1"

bootstrap:
  enabled: false
  script: |
    ceph -s
    function ensure_pool () {
      ceph osd pool stats $1 || ceph osd pool create $1 $2
    }
    ensure_pool volumes 8

# 启用的mgr模块
ceph_mgr_enabled_modules:
  - restful
  - status

# 配置mgr模块
ceph_mgr_modules_config:
  dashboard:
    port: 7000
  localpool:
    failure_domain: host
    subtree: rack
    pg_num: "128"
    num_rep: "3"
    min_size: "2"

# 在部署/升级后，执行下面的命令
# 这些命令通过kubectl来执行
ceph_commands:
- ceph osd pool create  pg_num
- ceph osd crush tunables 

# Kubernetes 存储类配置
storageclass:
  provision_storage_class: true
  provisioner: ceph.com/rbd
  # 存储类名称
  name: ceph-rbd
  monitors: nullcurshmap.src
  # 使用的RBD存储池的名称
  pool: rbd
  admin_id: admin
  admin_secret_name: pvc-ceph-conf-combined-storageclass
  admin_secret_namespace: ceph
  user_id: admin
  user_secret_name: pvc-ceph-client-key
  # RBD设备的镜像格式和特性
  image_format: "2"
  image_features: layering

endpoints:
  # 集群域名后缀
  cluster_domain_suffix: k8s.gmem.cc
  identity:
    name: keystone
    namespace: null
    auth:
      admin:
        region_name: RegionOne
        username: admin
        password: password
        project_name: admin
        user_domain_name: default
        project_domain_name: default
      user:
        role: admin
        region_name: RegionOne
        username: swift
        password: password
        project_name: service
        user_domain_name: default
        project_domain_name: default
    hosts:
      default: keystone-api
      public: keystone
    host_fqdn_override:
      default: null
    path:
      default: /v3
    scheme:
      default: http
    port:
      admin:
        default: 35357
      api:
        default: 80
  object_store:
    name: swift
    namespace: null
    hosts:
      default: ceph-rgw
    host_fqdn_override:
      default: null
    path:
      default: /swift/v1
    scheme:
      default: http
    port:
      api:
        default: 8088
  ceph_mon:
    namespace: null
    hosts:
      default: ceph-mon
    host_fqdn_override:
      default: null
    port:
      mon:
        default: 6789</pre>
<p>Ext4文件系统上基于目录的OSD配置，覆盖值示例：</p>
<pre class="crayon-plain-tag">network:
  public: 10.0.0.0/8
  cluster: 10.0.0.0/8

conf:
  ceph:
    config:
      global:
        # Ext4文件系统
        filestore_xattr_use_omap: true
      osd:
        ms_bind_port_max: 7100
        # Ext4文件系统
        osd_max_object_name_len: 256
        osd_max_object_namespace_len: 64
        osd_crush_update_on_start : false

ceph:
  storage:
    osd_directory: /var/lib/ceph-helm
    mon_directory: /var/lib/ceph-helm
    mon_log: /var/log/ceph/mon
    osd_log: /var/log/ceph/osd

# 和操作系统共享一个分区，基于目录的OSD
osd_directory:
  enabled: true

storageclass:
  name: ceph-rbd
  pool: rbd</pre>
<div class="blog_h3"><span class="graybg">创建K8S资源</span></div>
<p>为Ceph创建名字空间：</p>
<pre class="crayon-plain-tag">kubectl create namespace ceph</pre>
<p>创建RBAC资源：</p>
<pre class="crayon-plain-tag">kubectl create -f ceph-helm/ceph/rbac.yaml</pre>
<p>为了部署Ceph集群，需要为K8S集群中，不同角色（参与到Ceph集群中的角色）的节点添加标签： </p>
<ol>
<li>ceph-mon=enabled，部署mon的节点上添加</li>
<li>ceph-mgr=enabled，部署mgr的节点上添加</li>
<li>ceph-osd=enabled，部署基于设备、基于目录的OSD的节点上添加</li>
<li>ceph-osd-device-NAME=enabled。部署基于设备的OSD的节点上添加，其中NAME需要替换为上面 ceph-overrides.yaml中的OSD设备名，即：
<ol>
<li>ceph-osd-device-dev-vdb=enabled</li>
<li>ceph-osd-device-dev-vdc=enabled</li>
</ol>
</li>
</ol>
<p>对应的K8S命令：</p>
<pre class="crayon-plain-tag"># 部署Ceph Monitor的节点
kubectl label node xenial-100 ceph-mon=enabled ceph-mgr=enabled
# 对于每个OSD节点
kubectl label node xenial-100 ceph-osd=enabled ceph-osd-device-dev-vdb=enabled ceph-osd-device-dev-vdc=enabled
kubectl label node xenial-101 ceph-osd=enabled ceph-osd-device-dev-vdb=enabled ceph-osd-device-dev-vdc=enabled</pre>
<div class="blog_h3"><span class="graybg">Release</span></div>
<pre class="crayon-plain-tag">helm install --name=ceph local/ceph --namespace=ceph -f ceph-overrides.yaml</pre>
<div class="blog_h3"><span class="graybg">检查状态</span></div>
<p>确保所有Pod正常运行：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph get pods
NAME                                    READY     STATUS    RESTARTS   AGE
ceph-mds-7cb7c647c7-7w6pc               0/1       Pending   0          18h
ceph-mgr-66cb85cbc6-hsm65               1/1       Running   3          1h
ceph-mon-check-758b88d88b-2r975         1/1       Running   1          1h
ceph-mon-gvtq6                          3/3       Running   3          1h
ceph-osd-dev-vdb-clj5f                  1/1       Running   15         1h
ceph-osd-dev-vdb-hldw5                  1/1       Running   15         1h
ceph-osd-dev-vdb-l4v6t                  1/1       Running   15         1h
ceph-osd-dev-vdb-v5jmd                  1/1       Running   15         1h
ceph-osd-dev-vdb-wm4v4                  1/1       Running   15         1h
ceph-osd-dev-vdb-zwr65                  1/1       Running   15         1h
ceph-osd-dev-vdc-27wfk                  1/1       Running   15         1h
ceph-osd-dev-vdc-4w4fn                  1/1       Running   15         1h
ceph-osd-dev-vdc-cpkxh                  1/1       Running   15         1h
ceph-osd-dev-vdc-twmwq                  1/1       Running   15         1h
ceph-osd-dev-vdc-x8tpb                  1/1       Running   15         1h
ceph-osd-dev-vdc-zfrll                  1/1       Running   15         1h
ceph-rbd-provisioner-5544dcbcf5-n846s   1/1       Running   4          18h
ceph-rbd-provisioner-5544dcbcf5-t84bz   1/1       Running   3          18h
ceph-rgw-7f97b5b85d-nc5fq               0/1       Pending   0          18h</pre>
<p>其中MDS、RGW的Pod处于Pending状态，这是由于没有给任何节点添加标签：</p>
<pre class="crayon-plain-tag"># rgw即RADOS Gateway，是Ceph的对象存储网关服务，它是基于librados接口封装的FastCGI服务
# 提供存储和管理对象数据的REST API。对象存储适用于图片、视频等各类文件
# rgw兼容常见的对象存储API，例如绝大部分Amazon S3 API、OpenStack Swift API
ceph-rgw=enabled
# mds即Metadata Server，用于支持文件系统
ceph-mds=enabled</pre>
<p>现在从监控节点，检查一下Ceph集群的状态：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph exec -ti ceph-mon-gvtq6 -c ceph-mon -- ceph -s
  cluster:
    # 集群标识符
    id:     08adecc5-72b1-4c57-b5b7-a543cd8295e7
    health: HEALTH_OK
 
  services:
    # 监控节点
    mon: 1 daemons, quorum xenial-100
    # 管理节点
    mgr: xenial-100(active)
    # OSD（Ceph Data Storage Daemon）
    osd: 12 osds: 12 up, 12 in
  
  data:
    # 存储池、PG数量
    pools:   0 pools, 0 pgs
    # 对象数量
    objects: 0 objects, 0 bytes
    # 磁盘的用量，如果是基于文件系统的OSD，则操作系统用量也计算在其中
    usage:   1292 MB used, 322 GB / 323 GB avail

    # 所有PG都未激活，不可用
    pgs:     100.000% pgs not active
             # undersize是由于OSD数量不足（复制份数3，此时仅仅一个OSD），peerd表示128个PG配对到OSD
             128 undersized+peered
    # 将复制份数设置为1后，输出变为
    pgs:     100.000% pgs not active
             128 creating+peering
    # 过了一小段时间后，输出变为
    pgs: 128 active+clean
    # 到这里，PVC才能被提供，否则PVC状态显示 Provisioning，Provisioner日志中出现类似下面的：
    # attempting to acquire leader lease...
    # successfully acquired lease to provision for pvc ceph/ceph-pvc
    # stopped trying to renew lease to provision for pvc ceph/ceph-pvc, timeout reached</pre>
<p>如果K8S集群没有默认StorageClass，可以设置：</p>
<pre class="crayon-plain-tag">kubectl patch storageclass ceph-rbd -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'</pre>
<p>这样没有显式声明StorageClass的PVC将自动通过ceph-rbd进行卷提供。 </p>
<div class="blog_h3"><span class="graybg">创建存储池</span></div>
<pre class="crayon-plain-tag"># 创建具有384个PG的名为rbd的复制存储池
ceph osd pool create rbd 384 replicated
ceph osd pool set rbd min_size 1

# 开发环境下，可以把Replica份数设置为1
ceph osd pool set rbd size 1
# min_size 会自动被设置的比size小
# 减小size后，可以立即看到ceph osd status的used变小

# 初始化池，最好在所有节点加入后，调整好CURSH Map后执行
rbd pool init rbd

# 可以创建额外的用户，例如下面的，配合Value storageclass.user_id=k8s使用
ceph auth get-or-create-key client.k8s mon 'allow r' osd 'allow rwx pool=rbd' | base64
# 如果使用默认用户admin，则不需要生成上面这步。admin权限也是足够的


# 其它命令
# 查看块设备使用情况（需要MGR）
ceph osd status
+----+------------+-------+-------+--------+---------+--------+---------+-----------+
| id |    host    |  used | avail | wr ops | wr data | rd ops | rd data |   state   |
+----+------------+-------+-------+--------+---------+--------+---------+-----------+
| 0  | xenial-100 |  231M | 26.7G |    0   |  3276   |    0   |     0   | exists,up |
| 1  | xenial-103 |  216M | 26.7G |    0   |   819   |    0   |     0   | exists,up |
| 2  | xenial-101 |  253M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
| 3  | xenial-103 |  286M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
| 4  | xenial-101 |  224M | 26.7G |    0   |  1638   |    0   |     0   | exists,up |
| 5  | xenial-105 |  211M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
| 6  | xenial-100 |  243M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
| 7  | xenial-102 |  224M | 26.7G |    0   |  2457   |    0   |     0   | exists,up |
| 8  | xenial-102 |  269M | 26.7G |    0   |  1638   |    0   |     0   | exists,up |
| 9  | xenial-104 |  252M | 26.7G |    0   |  2457   |    0   |     0   | exists,up |
| 10 | xenial-104 |  231M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
| 11 | xenial-105 |  206M | 26.7G |    0   |     0   |    0   |     0   | exists,up |
+----+------------+-------+-------+--------+---------+--------+---------+-----------+</pre>
<div class="blog_h3"><span class="graybg">使用存储池</span></div>
<p>可以先使用ceph命令尝试创建RBD并挂载：</p>
<pre class="crayon-plain-tag"># 镜像格式默认2
# format 1 - 此格式兼容所有版本的 librbd 和内核模块，但是不支持较新的功能，像克隆。此格式目前已经废弃
# 2 - librbd 和 3.11 版以上内核模块才支持。此格式增加了克隆支持，未来扩展更容易
rbd create  test --size 1G --image-format 2 --image-feature layering

# 映射为本地块设备，如果卡住，可能有问题，一段时间后会有提示
rbd map test
# CentOS 7 下可能出现如下问题：
#   rbd: sysfs write failed
#   In some cases useful info is found in syslog - try "dmesg | tail".
#   rbd: map failed: (5) Input/output error
# dmesg | tail
#   [1180891.928386] libceph: mon0 10.5.39.41:6789 feature set mismatch, 
#     my 2b84a042a42 &lt; server's 40102b84a042a42, missing 401000000000000                                            
#   [1180891.934804] libceph: mon0 10.5.39.41:6789 socket error on read
# 解决办法是把Bucket算法从straw2改为straw

# 挂载为目录
fdisk /dev/rbd0
mkfs.ext4 /dev/rbd0
mkdir /test
mount /dev/rbd0 /test

# 测试性能
# 1MB块写入
sync; dd if=/dev/zero of=/test/data bs=1M count=512; sync
# 512+0 records in
# 512+0 records out
# 536870912 bytes (537 MB) copied, 4.44723 s, 121 MB/s
# 16K随机写
fio -filename=/dev/rbd0 -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -name=test
# WRITE: bw=35.7MiB/s (37.5MB/s), 35.7MiB/s-35.7MiB/s (37.5MB/s-37.5MB/s), io=2148MiB (2252MB), run=60111-60111msec 
# 16K随机读
fio -filename=/dev/rbd0 -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=512M -numjobs=30 -runtime=60 -name=test
# READ: bw=110MiB/s (116MB/s), 110MiB/s-110MiB/s (116MB/s-116MB/s), io=6622MiB (6943MB), run=60037-60037msec

# 删除测试镜像
umount /test
rbd unmap test
rbd remove test</pre>
<p>确认Ceph RBD可以挂载、读写后，创建一个PVC：</p>
<pre class="crayon-plain-tag">kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: ceph-pvc
  namespace: ceph
spec:
  accessModes:
   - ReadWriteOnce
  resources:
    requests:
       storage: 1Gi
  storageClassName: ceph-rbd</pre>
<p>查看PVC是否绑定到PV：</p>
<pre class="crayon-plain-tag">kubectl -n ceph create -f ceph-pvc.yaml

kubectl -n ceph get pvc

# NAME       STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# ceph-pvc   Bound     pvc-43caef06-46b4-11e8-bed8-deadbeef00a0   1Gi        RWO            ceph-rbd       3s

# 在Monitor节点上确认RBD设备已经创建
rbd ls
# kubernetes-dynamic-pvc-fbddb77d-46b5-11e8-9204-8a12961e4b47
rbd info kubernetes-dynamic-pvc-fbddb77d-46b5-11e8-9204-8a12961e4b47
# rbd image 'kubernetes-dynamic-pvc-fbddb77d-46b5-11e8-9204-8a12961e4b47':
#         size 128 MB in 32 objects
#         order 22 (4096 kB objects)
#         block_name_prefix: rbd_data.11412ae8944a
#         format: 2
#         features: layering
#         flags: 
#         create_timestamp: Mon Apr 23 05:20:07 2018</pre>
<p>需要在其它命名空间中使用此存储池时，拷贝一下Secret：</p>
<pre class="crayon-plain-tag">kubectl -n ceph get secrets/pvc-ceph-client-key -o json --export | jq '.metadata.namespace = "default"' | kubectl create -f - </pre>
<div class="blog_h3"><span class="graybg">卸载</span></div>
<pre class="crayon-plain-tag">helm delete ceph --purge
kubectl delete namespace ceph</pre>
<p>此外，如果要<span style="background-color: #c0c0c0;">重新安装，一定要把所有节点的一下目录清除掉</span>：</p>
<pre class="crayon-plain-tag">rm -rf /var/lib/ceph-helm
rm -rf /var/lib/ceph</pre>
<div class="blog_h2"><span class="graybg">既有Ceph集群</span></div>
<p>只需要安装相应的Provisioner，配置适当的StorageClass即可。示例：</p>
<ol>
<li>Provisioner：<a href="https://git.gmem.cc/alex/helm-charts/src/branch/master/ceph-provisioners">https://git.gmem.cc/alex/helm-charts/src/branch/master/ceph-provisioners</a></li>
<li>安装脚本：<a href="https://git.gmem.cc/alex/k8s-init/src/branch/master/4.infrastructure/0.ceph-external.sh">https://git.gmem.cc/alex/k8s-init/src/branch/master/4.infrastructure/0.ceph-external.sh</a></li>
</ol>
<div class="blog_h3"><span class="graybg">基于CephFS的卷</span></div>
<p>Kubernetes卷的动态Provisioning，目前需要依赖于external-storage项目，K8S没有提供内置的Provisioner。此项目存在不少问题，生产环境下可以考虑<a href="/kubernetes-study-note#static-provisioning-ceph">静态提供</a>。</p>
<p>Provisioner会自动在Ceph集群的默认CephFS中创建“卷”，Ceph支持基于libcephfs+librados来<span style="background-color: #c0c0c0;">实现一个基于CephFS目录的虚拟卷</span>。</p>
<p>你可以在默认CephFS中看到volumes/kubernetes目录。kubernetes目录对应一个虚拟卷组。每个PV对应了它的一个子目录。</p>
<div class="blog_h1"><span class="graybg">性能调优</span></div>
<div class="blog_h2"><span class="graybg">最佳实践</span></div>
<div class="blog_h3"><span class="graybg">基本</span></div>
<ol>
<li>监控节点对于集群的正确运行非常重要，应当为其分配独立的硬件资源。如果跨数据中心部署，监控节点应该分散在不同数据中心或者可用性区域</li>
<li><span style="background-color: #c0c0c0;">日志可能会让集群的吞吐量减半</span>。理想情况下，应该<span style="background-color: #c0c0c0;">在不同磁盘上运行操作系统、OSD数据、OSD日志</span>。对于高吞吐量工作负载，<span style="background-color: #c0c0c0;">考虑使用SSD进行日志存储</span></li>
<li>纠删编码（Erasure coding）可以用于存储大容量的<span style="background-color: #c0c0c0;">一次性写、非频繁读、性能要求不高</span>的数据。纠删编码存储消耗小，但是IOPS也降低</li>
<li>目录项（Dentry）和inode缓存可以提升性能，特别是存在很多小对象的情况下</li>
<li>使用<span style="background-color: #c0c0c0;">缓存分层（Cache Tiering）可以大大提升集群性能</span>。此技术可以在热、冷Tier之间自动的进行数据迁移。为了最大化性能，请<span style="background-color: #c0c0c0;">使用SSD作为缓存池、并且在低延迟节点上部署缓存池</span></li>
<li>部署奇数个数的监控节点，以便仲裁投票（Quorum Voting），建议使用3-5个节点。更多的节点可以增强集群的健壮性，但是mon之间需要保持数据同步，这会影响性能</li>
<li>诊断性能问题的时候，总是从最底层（磁盘、网络）开始，然后再检查块设备、对象网关等高层接口</li>
<li>在大型集群里用<span style="background-color: #c0c0c0;">单独的集群网络（Cluster Newwork）可显著地提升性能和安全性</span></li>
</ol>
<div class="blog_h3"><span class="graybg">文件系统</span></div>
<ol>
<li>限制最大文件大小，创建极大的文件会导致删除过程很缓慢</li>
<li>避免在生产环境下使用试验特性。你应该使用<span style="background-color: #c0c0c0;">单个活动MDS、不使用快照（默认如此）</span></li>
<li>避免增大max_mds，可能导致大于1个的MDS处于Active</li>
<li>客户端选择：
<ol>
<li>FUSE，容易使用、容易升级和服务器集群保持一致</li>
<li>内核，性能好</li>
<li>不同客户端的功能并不完全一致，例如FUSE支持客户端配额，内核不支持</li>
</ol>
</li>
<li>对于Ceph 10.x，最好使用4.x内核。如果必须使用老内核，你应该使用FUSE作为客户端</li>
</ol>
<div class="blog_h2"><span class="graybg">基础设施要求</span></div>
<div class="blog_h3"><span class="graybg">处理器</span></div>
<p>OSD需要消耗CPU资源，可以将其绑定到一个核心上。如果使用纠删码，则需要更多的CPU资源。此外，集群处于Recovery状态时，OSD的CPU消耗显著增加</p>
<p>MON不怎么消耗CPU资源，几个G内存的单核心物理机即可。</p>
<p>MDS相当消耗CPU资源，考虑4核心或更多CPU。如果依赖于CephFS处理大量工作，应当分配专用物理机</p>
<div class="blog_h3"><span class="graybg">内存</span></div>
<p>MON/MDS需要不少于2G内存。OSD通常需要1G内存（和存储容量有关）。此外，集群处于Recovery状态时，OSD的内存消耗显著增加，因此配备2G内存更好</p>
<div class="blog_h3"><span class="graybg">网络</span></div>
<p>最好具有万兆网络，公共网络、集群网络需要物理隔离（双网卡连接到独立交换机）。对于数百TB规模的集群，千兆网络也能够正常工作</p>
<p>集群网络往往消耗更多的带宽，此外，高性能的集群网络对于Recovery的效率很重要。</p>
<p>如果交换机支持，应当启用Jumbo帧，可以提升网络吞吐量。</p>
<div class="blog_h3"><span class="graybg">磁盘</span></div>
<p>在生产环境下，最好让OSD使用独立的驱动器，如果和OS共享驱动，最好使用独立的分区。</p>
<p>通常使用SATA SSD作为日志存储，预算足够可以考虑PCIE SSD。<a href="http://www.sebastien-han.fr/blog/2014/10/10/ceph-how-to-test-if-your-ssd-is-suitable-as-a-journal-device/">Intel S3500</a>的4K随机写 IOPS可达10K+</p>
<p>关于RAID：</p>
<ol>
<li>最好不要使用RAID</li>
<li>如果有RAID卡，并且磁盘数量太多，而对应的内存数量不足（每个OSD大概需要2G内存），可以RAID0</li>
<li>不要使用RAID5，因为随机IO的性能降低</li>
</ol>
<p>关于filestore：</p>
<ol>
<li>建议使用SSD存储日志，以减少访问时间、读取延迟，实现吞吐量的显著提升</li>
<li>可以为创建SSD分区，每个分区作为一个OSD的日志存储，但是最好不要超过4个</li>
</ol>
<div class="blog_h3"><span class="graybg">BIOS设置</span></div>
<ol>
<li>启用超线程Hyper-Threading技术</li>
<li>关闭节能</li>
<li>关闭NUMA</li>
</ol>
<div class="blog_h3"><span class="graybg">内核参数</span></div>
<pre class="crayon-plain-tag"># 修改pid max
# 执行命令
echo 4194303 &gt; /proc/sys/kernel/pid_max
# 或者
sysctl -w kernel.pid_max=4194303

# read_ahead, 数据预读到内存，提升磁盘读操作能力
echo "8192" &gt; /sys/block/sda/queue/read_ahead_kb

# 禁用交换文件
echo "vm.swappiness = 0" | tee -a /etc/sysctl.conf

# I/O Scheduler：SSD使用用noop，SATA/SAS使用deadline
echo "deadline" &gt; /sys/block/sda/queue/scheduler
echo "noop" &gt; /sys/block/sda/queue/scheduler</pre>
<div class="blog_h3"><span class="graybg">文件系统</span></div>
<p>底层文件系统的稳定性和性能对于Ceph很重要。在开发、非关键部署时可以使用btrfs，这也是未来的方向。关键的生产环境下应该使用XFS。</p>
<p>在高可扩容性的存储环境下，XFS和btrfs相比起ext3/4有很大<span style="background-color: #c0c0c0;">优势</span>。<span style="background-color: #c0c0c0;">XFS和btrfs都是日志式文件系统，更健壮，容易从崩溃、断电中恢复</span>。日志文件系统会在执行写操作之前，把需要进行的变更记录到日志。</p>
<p>OSD依赖于底层文件系统的扩展属性（Extended Attributes，XATTRs），来存储各种内部对象状态和属性。XFS支持64KB的XATTRs，但是ext4就太小了，你应该为运行在ext4上的OSD配置：</p>
<pre class="crayon-plain-tag"># 新版本Ceph此配置项已经没了
filestore xattr use omap = true</pre>
<p>关于文件系统的一些知识：</p>
<ol>
<li>XFS 、 btrfs 和 ext4 都是日志文件系统</li>
<li>XFS很成熟</li>
<li>btrfs相对年轻，他是一个写时复制（COW）文件系统，因而支持可写文件系统快照。此外它还支持透明压缩、完整性校验</li>
</ol>
<div class="blog_h2"><span class="graybg">归置组</span></div>
<div class="blog_h3"><span class="graybg">PG数量</span></div>
<p>PG的数量应当总是和PGP相同。PGP是为了实现定位而创建的PG。再平衡仅仅再pgp_num被修改后才会触发，仅仅修改pg_num不会触发。</p>
<p>随着OSD数量的变化，选取适当的PG数量很重要。因为PG数量对<span style="background-color: #c0c0c0;">集群行为、数据持久性</span>（Durability，灾难性事件发生时保证数据堡丢失）有很大影响。此外，归置组很耗计算资源，所以<span style="background-color: #c0c0c0;">很多存储池x很多归置组会导致性能下降</span>。建议的取值：</p>
<ol>
<li>对于小于5个OSD的集群：设置为128</li>
<li>5-10个OSD的集群：设置为512</li>
<li>10-50个OSD的集群：设置为1024</li>
<li>超过50个OSD的集群，需要自己权衡，利用pgcalc来计算适合的PG数量</li>
</ol>
<p>《Ceph分布式存储学习指南》一书中建议的PG数量算法：</p>
<p style="padding-left: 30px;">每个池的PG数量 = OSD总数 * 100 / 最大副本数 / 池数</p>
<p>计算结果需要向上舍入到2的N次幂。此外该书倾向于让所有池具有相同的PG数量。</p>
<div class="blog_h2"><span class="graybg">再平衡</span></div>
<ol>
<li>加入新的OSD后，考虑设置权重为0，然后逐渐增加权重，这样可以避免性能下降</li>
</ol>
<div class="blog_h1"><span class="graybg">监控</span></div>
<div class="blog_h2"><span class="graybg">ceph-exporter</span></div>
<p>DigitalOcean开源了Ceph的Exporter，本文使用<a href="https://github.com/gmemcc/ceph_exporter">gmemcc的fork</a>版本。Ceph Exporter和MON节点通信，所有信息都通过rados_mon_command()调用获得。</p>
<p>此Exporter可以在任意Ceph客户端节点上运行，和任何形式的Ceph客户端一样，你需要提供ceph.conf、ceph.USER.keyring两个配置文件。</p>
<div class="blog_h3"><span class="graybg">常用选项</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 150px; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>telemetry.addr</td>
<td>监听的地址和端口，示例：*:9100</td>
</tr>
<tr>
<td>telemetry.path</td>
<td>查询指标的URL路径，示例：/metrics</td>
</tr>
<tr>
<td>ceph.config</td>
<td>Ceph配置文件路径</td>
</tr>
<tr>
<td>ceph.user</td>
<td>使用的Ceph用户，示例：admin</td>
</tr>
<tr>
<td>exporter.config</td>
<td>Ceph Exporter配置文件的位置，示例：/etc/ceph/exporter.yml</td>
</tr>
<tr>
<td>rgw.mode</td>
<td>是否收集RGW的指标：0禁用，1启用，2后台</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">构建镜像</span></div>
<p>你可以直接使用预构建好的镜像：<pre class="crayon-plain-tag">digitalocean/ceph_exporter:2.0.1-luminous</pre></p>
<p>或者，从源码构建：</p>
<pre class="crayon-plain-tag">sudo apt install librados-dev

git clone https://github.com/gmemcc/ceph_exporter.git
cd ceph_exporter
go install
make
docker build -t docker.gmem.cc/digitalocean/ceph_exporter .</pre>
<div class="blog_h2"><span class="graybg">内置Prometheus支持</span></div>
<p>对于Luminous12.2或者Mimic13.2版本，MGR已经内置了Prometheus模块，不再需要ceph_exporter了。</p>
<p>执行下面的命令启用Prometheus模块：</p>
<pre class="crayon-plain-tag">ceph mgr module enable prometheus</pre>
<p>然后，你就可以访问任意MGR节点的http://MGR_HOST:9283/metrics，抓取指标了。Prometheus配置示例：</p>
<pre class="crayon-plain-tag">scrape_configs:               
- job_name: ceph                           
  static_configs:                          
  - targets:
    # 列出所有MGR节点，防止故障转移时数据丢失                      
    - 10.0.1.1:9283                        
    labels:                                
      cluster: ceph</pre>
<p>Grafana仪表盘可以参考这个示例：<a href="https://grafana.com/dashboards/7056">https://grafana.com/dashboards/7056</a></p>
<div class="blog_h1"><span class="graybg">附录</span></div>
<div class="blog_h2"><span class="graybg">CEPH_FEATURE</span></div>
<p>R表示必须支持的特性，S表示该版本内核可以支持，-*-表示从这个版本开始支持。</p>
<table class="full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 20%; text-align: center;" align="left"><strong>Feature</strong></td>
<td style="width: 20%; text-align: center;" align="right"><strong>OCT</strong></td>
<td align="center"><strong>3.8</strong></td>
<td align="center"><strong>3.9</strong></td>
<td align="center"><strong>3.10</strong></td>
<td align="center"><strong>3.14</strong></td>
<td align="center"><strong>3.15</strong></td>
<td align="center"><strong>3.18</strong></td>
<td align="center"><strong>4.1</strong></td>
<td align="center"><strong>4.5</strong></td>
<td align="center"><strong>4.6</strong></td>
</tr>
</tbody>
<tbody>
<tr>
<td align="left">CEPH_FEATURE_NOSRCADDR</td>
<td align="right">2</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_SUBSCRIBE2</td>
<td align="right">10</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-R-</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_RECONNECT_SEQ</td>
<td align="right">40</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-R-</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_PGID64</td>
<td align="right">200</td>
<td align="center"> </td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_PGPOOL3</td>
<td align="right">800</td>
<td align="center"> </td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSDENC</td>
<td align="right">2000</td>
<td align="center"> </td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
<td align="center">R</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_TUNABLES</td>
<td align="right">40000</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_MSG_AUTH</td>
<td align="right">800000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_TUNABLES2</td>
<td align="right">2000000</td>
<td align="center"> </td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_REPLY_CREATE_INODE</td>
<td align="right">8000000</td>
<td align="center"> </td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSDHASHPSPOOL</td>
<td align="right">40000000</td>
<td align="center"> </td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSD_CACHEPOOL</td>
<td align="right">800000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_V2</td>
<td align="right">1000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_EXPORT_PEER</td>
<td align="right">2000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSD_ERASURE_CODES***</td>
<td align="right">4000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSDMAP_ENC</td>
<td align="right">8000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_TUNABLES3</td>
<td align="right">20000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_OSD_PRIMARY_AFFINITY</td>
<td align="right">20000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_V4 ****</td>
<td align="right">1000000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_CRUSH_TUNABLES5</td>
<td align="right">200000000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
</tr>
<tr>
<td align="left">CEPH_FEATURE_NEW_OSDOPREPLY_ENCODING</td>
<td align="right">400000000000000</td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center"> </td>
<td align="center">-S-</td>
<td align="center">S</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">版本代码</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">代码</td>
<td style="text-align: center;">版本</td>
</tr>
</thead>
<tbody>
<tr>
<td>Luminous</td>
<td>12</td>
</tr>
<tr>
<td>Jewel</td>
<td>10</td>
</tr>
<tr>
<td colspan="2"><em>已归档版本</em></td>
</tr>
<tr>
<td>argonaut</td>
<td>0.48</td>
</tr>
<tr>
<td>bobtail</td>
<td>0.56</td>
</tr>
<tr>
<td>Cuttlefish</td>
<td>0.61</td>
</tr>
<tr>
<td>Dumpling</td>
<td>0.67 </td>
</tr>
<tr>
<td>Emperor</td>
<td>0.72 </td>
</tr>
<tr>
<td>Firefly</td>
<td>0.80 </td>
</tr>
<tr>
<td>Giant</td>
<td>0.87 </td>
</tr>
<tr>
<td>Hammer</td>
<td>0.94 </td>
</tr>
<tr>
<td>Infernalis</td>
<td>9.2.0 </td>
</tr>
<tr>
<td>Kraken</td>
<td>11.0.2 </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">参考资源</span></div>
<ol>
<li><a href="https://tobegit3hub1.gitbooks.io/ceph_from_scratch/content/">Ceph From Scratch</a></li>
<li><a href="http://docs.ceph.com/docs/giant/rados/operations/control/">CEPH CONTROL COMMANDS</a></li>
</ol>
<div class="blog_h1"><span class="graybg">新特性</span></div>
<div class="blog_h2"><span class="graybg">Firefly</span></div>
<ol>
<li><span style="background-color: #c0c0c0;">纠删码</span>支持</li>
<li><span style="background-color: #c0c0c0;">缓存分层</span></li>
<li>键/值 OSD后端</li>
<li>独立的RadosGW（使用civetweb）</li>
</ol>
<div class="blog_h2"><span class="graybg">Giant</span></div>
<ol>
<li>LRC纠删码</li>
<li>CephFS日志恢复，诊断工具</li>
</ol>
<div class="blog_h2"><span class="graybg">Hammer</span></div>
<ol>
<li>RGW对象版本化</li>
<li>对象桶分片</li>
<li><span style="background-color: #c0c0c0;">Crush straw2算法</span></li>
</ol>
<div class="blog_h2"><span class="graybg">Infernalis</span></div>
<ol>
<li>纠删码到达稳定，支持很多新特性</li>
<li>支持Swift API新特性，例如对象过期设置</li>
</ol>
<div class="blog_h2"><span class="graybg">Jewel</span></div>
<ol>
<li><span style="background-color: #c0c0c0;">CephFS到达稳定</span></li>
<li>RGW多站点可达（支持主/主配置）</li>
<li>AWS4兼容</li>
<li><span style="background-color: #c0c0c0;">RBD镜像（mirroring）</span></li>
<li>引入BlueStore</li>
</ol>
<div class="blog_h2"><span class="graybg">Kraken</span></div>
<ol>
<li>BlueStore到达稳定</li>
<li>AsyncMessenger</li>
<li>RGW：通过ES来索引元数据</li>
<li>S3桶生命周期API支持</li>
<li>RGW：支持导出为NFS v3接口</li>
<li>Rados支持在基于纠删码的池上进行overwrite操作</li>
<li>基于纠删码池的RBD卷</li>
</ol>
<div class="blog_h2"><span class="graybg">Luminous</span></div>
<ol>
<li>集成Web仪表盘Ceph Dashboard</li>
<li>直接管理裸设备的<span style="background-color: #c0c0c0;">BlueStore到达稳定并且作为默认选项</span></li>
<li>纠删码池完全支持overwirte，可以和CephFS/RDB一起使用</li>
<li>引入<span style="background-color: #c0c0c0;">组件ceph-mgr</span>，如果该组件停止，则指标不会更新，某些依赖于指标的请求，例如ceph df，无法工作</li>
<li>可扩容能力提升，可部署10000OSD的集群</li>
<li>每个<span style="background-color: #c0c0c0;">OSD支持关联一个设备类</span>（例如hdd/ssd），允许CRUSH规则将数据简单地映射到系统中的设备的子集。通常不需要手动编写CRUSH规则或手动编辑CRUSH</li>
<li>支持优化CRUSH权重，以保持OSD之间数据的近乎完美的分布</li>
<li>OSD可以根据后端设备是HDD还是SSD来调整其默认配置</li>
<li><span style="background-color: #c0c0c0;">RGW引入了上传对象的服务器端加密</span>，用于管理加密密钥的三个选项有：自动加密（仅推荐用于测试设置），客户提供的类似于Amazon SSE-C规范的密钥，以及通过使用外部密钥管理服务</li>
<li>RGW具有初步的类似AWS的存储桶策略API支持。现在，策略是一种表达一系列新授权概念的方式。未来，这将成为附加身份验证功能的基础，例如STS和组策略等</li>
<li>RGW通过使用rados命名空间合并了几个元数据索引池</li>
<li>引入组件<span style="background-color: #c0c0c0;">rbd-mirror，负责RBD卷的镜像复制</span></li>
<li>rbd trash命令支持延迟的镜像删除</li>
<li>镜像可以通过rbd mirroringreplay delay配置选项支持可配置的复制延迟</li>
<li><span style="background-color: #c0c0c0;">多Active MDS到达稳定状态</span></li>
</ol>
<div class="blog_h2"><span class="graybg"> Mimic</span></div>
<ol>
<li>引入一个新的、全功能和美观的<span style="background-color: #c0c0c0;">仪表盘 Dashboard V2</span></li>
<li>RADOS：配置选项可被mon中心化存储和管理</li>
<li>RADOS：在恢复和再平衡时，mon使用的磁盘大大减小</li>
<li>RADOS：引入一个异步恢复特性，减少在OSD从错误恢复期间，请求的tail latency</li>
<li>RGW：支持将一个Zone（或者Buckets的子集）复制到外部云服务，例如S3</li>
<li>RGW：支持S3多因子身份验证</li>
<li>RGW：前端Beast到达stable</li>
<li>CephFS：使用多MDS时快照功能到达stable</li>
<li>RBD：镜像克隆不需要显式的protect/unprotect步骤</li>
<li>RBD：<span style="background-color: #c0c0c0;">镜像支持深克隆</span>（包含parent镜像、关联快照的数据的克隆）到新池，支持修改数据布局</li>
</ol>
<div class="blog_h2"><span class="graybg">Nautilus</span></div>
<ol>
<li><span style="background-color: #c0c0c0;">仪表盘：增加很多新功能</span>，全方位的查看各种指标，对Ceph进行管理</li>
<li>RADOS：每个<span style="background-color: #c0c0c0;">池的PG数量 ，现在可以随时减小</span>了。集群可以根据用量和管理员的提示，自动优化PG数量</li>
<li>RADOS：v2 wire protocol支持传输加密</li>
<li>RADOS：mon/osd使用的物理设备的健康指标（例如SMART）可以得到跟踪，并且在预期出现磁盘失败前进行警告</li>
<li>RADOS：在recovery/backfill时，OSD更有效的优先恢复重要的PG、对象</li>
<li>RADOS：例如磁盘失败后的恢复，这样长期运行的后台操作的进度，在ceph status中报告</li>
<li>RGW：<span style="background-color: #c0c0c0;">Beast替换civetweb作为默认Web前端</span></li>
<li>CephFS：MDS稳定性得到很大优化，特别是针对大缓存、长时间运行的具有大内存的客户端</li>
<li>CephFS：在Rook管理的环境下，<span style="background-color: #c0c0c0;">CephFS可以通过NFS-Ganesha集群暴露出去</span></li>
<li>CephFS：支持查询进行中的<span style="background-color: #c0c0c0;">针对MDS的scrub的进度</span></li>
<li>RBD：镜像可以在最小宕机时间内进行迁移，这可以支持在池之间迁移镜像、修改镜像布局</li>
<li>RBD：rbd perf image iotop 以及 rbd perf image iostat命令可以<span style="background-color: #c0c0c0;">提供iostat风格的、针对所有RBD镜像的监控</span></li>
<li>RBD：ceph-mgr的Prometheus Exporter支持暴露所有RBD镜像的IO监控指标</li>
<li>RBD：在一个池中，可以<span style="background-color: #c0c0c0;">使用命名空间来隔离RBD</span>，实现多租户</li>
</ol>
<div class="blog_h2"><span class="graybg">Octopus</span></div>
<ol>
<li>新的工具cephadm，用于支持容器化部署</li>
<li>Health警告可以被临时/永久的静默</li>
<li>Dashboard：UI增强、安全性增强</li>
<li>管理功能的增强</li>
<li>RADOS：从N版引入的PG括缩，默认启用</li>
<li>RADOS：Bluestore包含了多项改进和性能增强</li>
<li>RBD：镜像支持一种新的，基于快照的模式，不再依赖于journaling特性</li>
<li>RBD：克隆操作保持源镜像的稀疏性（sparseness）</li>
<li>RBD：改进了rbd-nbd 工具，可以使用新的内核特性</li>
<li>RBD：缓存性能增强</li>
</ol>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">CephFS问题诊断</span></div>
<div class="blog_h3"><span class="graybg">无法创建</span></div>
<p>创建新CephFS报错Error EINVAL: pool 'rbd-ssd' already contains some objects. Use an empty pool instead，解决办法：</p>
<pre class="crayon-plain-tag">ceph fs new cephfs rbd-ssd rbd-hdd --force</pre>
<div class="blog_h3"><span class="graybg">mds.0 is damaged</span></div>
<p>断电后出现此问题。MDS进程报错： Error recovering journal 0x200: (5) Input/output error。诊断过程：</p>
<pre class="crayon-plain-tag"># 健康状况
ceph health detail
# HEALTH_ERR mds rank 0 is damaged; mds cluster is degraded
# mds.0 is damaged

# 文件系统详细信息，可以看到唯一的MDS Boron启动不了
ceph fs status
# cephfs - 0 clients
# ======
# +------+--------+-----+----------+-----+------+
# | Rank | State  | MDS | Activity | dns | inos |
# +------+--------+-----+----------+-----+------+
# |  0   | failed |     |          |     |      |
# +------+--------+-----+----------+-----+------+
# +---------+----------+-------+-------+
# |   Pool  |   type   |  used | avail |
# +---------+----------+-------+-------+
# | rbd-ssd | metadata |  138k |  106G |
# | rbd-hdd |   data   | 4903M | 2192G |
# +---------+----------+-------+-------+

# +-------------+
# | Standby MDS |
# +-------------+
# |    Boron    |
# +-------------+

# 显示错误原因
ceph tell mds.0 damage
# terminate called after throwing an instance of 'std::out_of_range'
#   what():  map::at
# Aborted

# 尝试修复，无效
ceph mds repaired 0

# 尝试导出CephFS日志，无效
cephfs-journal-tool journal export backup.bin
# 2019-10-17 16:21:34.179043 7f0670f41fc0 -1 Header 200.00000000 is unreadable
# 2019-10-17 16:21:34.179062 7f0670f41fc0 -1 journal_export: Journal not readable, attempt object-by-object dump with `rados`Error ((5) Input/output error)

# 尝试重日志修复，无效
# 尝试将journal中所有可回收的 inodes/dentries 写到后端存储（如果版本比后端更高）
cephfs-journal-tool event recover_dentries summary
# Events by type:
# Errors: 0
# 2019-10-17 16:22:00.836521 7f2312a86fc0 -1 Header 200.00000000 is unreadable

# 尝试截断日志，无效
cephfs-journal-tool journal reset 
# got error -5from Journaler, failing
# 2019-10-17 16:22:14.263610 7fe6717b1700  0 client.6494353.journaler.resetter(ro) error getting journal off disk
# Error ((5) Input/output error)


# 删除重建，数据丢失
ceph fs rm cephfs  --yes-i-really-mean-it



## 又一次遇到此问题

# 深度清理，发现200.00000000存在数据不一致
ceph osd deep-scrub all
40.14 shard 14: soid 40:292cf221:::200.00000000:head data_digest
  0x6ebfd975 != data_digest 0x9e943993 from auth oi 40:292cf221:::200.00000000:head
  (22366'34 mds.0.902:1 dirty|data_digest|omap_digest s 90 uv 34 dd 9e943993 od ffffffff alloc_hint [0 0 0])                                                                                  
40.14 deep-scrub 0 missing, 1 inconsistent objects
40.14 deep-scrub 1 errors

# 查看RADOS不一致对象详细信息
rados list-inconsistent-obj  40.14  --format=json-pretty
{
    "epoch": 23060,
    "inconsistents": [
        {
            "object": {
                "name": "200.00000000",
            },
            "errors": [],
            "union_shard_errors": [
                # 错误原因，校验信息不一致
                "data_digest_mismatch_info"
            ],
            "selected_object_info": {
                "oid": {
                    "oid": "200.00000000",
                },
            },
            "shards": [
                {
                    "osd": 7,
                    "primary": true,
                    "errors": [],
                    "size": 90,
                    "omap_digest": "0xffffffff"
                },
                {
                    "osd": 14,
                    "primary": false,

# errors：分片之间存在不一致，而且无法确定哪个分片坏掉了，原因：
#    data_digest_mismatch 此副本的摘要信息和主副本不一样
#    size_mismatch 此副本的数据长度和主副本不一致
#    read_error 可能存在磁盘错误
                    "errors": [
                        # 这里的原因是两个副本的摘要不一致
                        "data_digest_mismatch_info"
                    ],
                    "size": 90,
                    "omap_digest": "0xffffffff",
                    "data_digest": "0x6ebfd975"
                }
            ]
        }
    ]
}
# 转为处理inconsistent问题，停止OSD.14，Flush 日志，启动OSD.14，执行PG修复
# 无效…… 执行PG修复后Ceph会自动以权威副本覆盖不一致的副本，但是并非总能生效，
# 例如，这里的情况，主副本的数据摘要信息丢失

# 删除故障对象
rados -p rbd-ssd  rm 200.00000000</pre>
<div class="blog_h2"><span class="graybg">OSD问题诊断</span></div>
<div class="blog_h3"><span class="graybg">启动后立即崩溃</span></div>
<p>通常可以认为属于Ceph的Bug。这些Bug可能因为数据状态引发，有些时候将崩溃OSD的权重清零，可以恢复：</p>
<pre class="crayon-plain-tag"># 尝试解决osd.17启动后立即宕机
ceph osd reweight 17 0</pre>
<div class="blog_h2"><span class="graybg">PG问题诊断</span></div>
<div class="blog_h3"><span class="graybg">所有PG卡在unkown</span></div>
<p>如果创建一个存储池后，其所有PG都卡在此状态，可能原因是CRUSH map不正常。你可以配置osd_crush_update_on_start为true让集群自动调整CRUSH map。</p>
<div class="blog_h3"><span class="graybg">卡在peering</span></div>
<p><span class="graybg">ceph -s显示如下状态，长期不恢复：</span></p>
<pre class="crayon-plain-tag">cluster:                 
    health: HEALTH_WARN                                                    
            Reduced data availability: 2 pgs inactive, 2 pgs peering
            19 slow requests are blocked &gt; 32 sec
  data:
    pgs:     0.391% pgs not active
             510 active+clean
             2   peering</pre>
<p>此案例中，使用此PG的Pod呈Known状态。</p>
<p>检查卡在inactive状态的PG：</p>
<pre class="crayon-plain-tag">ceph pg dump_stuck inactive

PG_STAT STATE   UP     UP_PRIMARY ACTING ACTING_PRIMARY  
17.68   peering [3,12]          3 [3,12]              3
16.32   peering [4,12]          4 [4,12]              4</pre>
<p>输出其中一个PG的诊断信息，片断如下：</p>
<pre class="crayon-plain-tag">// ceph pg 17.68 query
{                                                   
    "info": {                                                
        "stats": {
            "state": "peering",
            "stat_sum": {
                "num_objects_dirty": 5
            },
            "up": [
                3,
                12
            ],
            "acting": [
                3,
                12
            ],
            // 因为哪个OSD而阻塞
            "blocked_by": [
                12
            ],
            "up_primary": 3,
            "acting_primary": 3
        }
    },
    "recovery_state": [
        // 如果顺利，第一个元素应该是 "name": "Started/Primary/Active"
        {
            "name": "Started/Primary/Peering/GetInfo",
            "enter_time": "2018-06-11 18:32:39.594296",
            // 但是，卡在向OSD 12 请求信息这一步上
            "requested_info_from": [
                {
                    "osd": "12"
                }
            ]
        },
        {
            "name": "Started/Primary/Peering",
        },
        {
            "name": "Started",
        }
    ]
}</pre>
<p>没有获得osd-12阻塞Peering的明确原因。 </p>
<p>查看日志，osd-12位于10.0.0.104，osd-3位于10.0.0.100，后者为Primary OSD。</p>
<p>osd-3日志，在18:26开始出现，和所有其它OSD之间心跳检测失败。此时10.0.0.100负载很高，卡死。</p>
<p>osd-12日志，在18:26左右大量出现：</p>
<pre class="crayon-plain-tag">osd.12 466 heartbeat_check: no reply from 10.0.0.100:6803 osd.4 since back 2018-06-11 18:26:44.973982 ...</pre>
<p>直到18:44分仍然无法进行心跳检测，重启osd-12后一切恢复正常。 </p>
<div class="blog_h3"><span class="graybg">incomplete</span></div>
<p>检查无法完成的PG：</p>
<pre class="crayon-plain-tag">ceph pg dump_stuck

# PG_STAT STATE      UP     UP_PRIMARY ACTING ACTING_PRIMARY
# 17.79   incomplete [9,17]          9 [9,17]              9
# 32.1c   incomplete [16,9]         16 [16,9]             16
# 17.30   incomplete [16,9]         16 [16,9]             16
# 31.35   incomplete [9,17]          9 [9,17]              9</pre>
<p>查询PG 17.30的诊断信息：</p>
<pre class="crayon-plain-tag">// ceph pg  17.30 query
{
  "state": "incomplete",
  "info": {
    "pgid": "17.30",
    "stats": {
      // 被osd.11阻塞而无法完成，此osd已经不存在
      "blocked_by": [
        11
      ],
      "up_primary": 16,
      "acting_primary": 16
    }
  },
  // 恢复的历史记录
  "recovery_state": [
    {
      "name": "Started/Primary/Peering/Incomplete",
      "enter_time": "2018-06-17 04:48:45.185352",
      // 最终状态，此PG没有完整的副本
      "comment": "not enough complete instances of this PG"
    },
    {
      "name": "Started/Primary/Peering",
      "enter_time": "2018-06-17 04:48:45.131904",
      "probing_osds": [
        "9",
        "16",
        "17"
      ],
      // 期望检查已经不存在的OSD
      "down_osds_we_would_probe": [
        11
      ],
      "peering_blocked_by_detail": [
        {
          "detail": "peering_blocked_by_history_les_bound"
        }
      ]
    }
  ]
}</pre>
<p>可以看到17.30期望到osd.11寻找权威数据，而osd.11已经永久丢失了。这种情况下，可以尝试强制标记PG为complete。</p>
<p>首先，停止PG的主OSD：<pre class="crayon-plain-tag">service ceph-osd@16 stop</pre>  </p>
<p>然后，运行下面的工具：</p>
<pre class="crayon-plain-tag">ceph-objectstore-tool --data-path /var/lib/ceph/osd/ceph-16  --pgid 17.30 --op mark-complete
# Marking complete 
# Marking complete succeeded</pre>
<p>最后，重启PG的主OSD：<pre class="crayon-plain-tag">service ceph-osd@16 start</pre>  </p>
<div class="blog_h3"><span class="graybg">单副本导致的stale</span></div>
<p>不做副本的情况下，单个OSD宕机即导致数据不可用：</p>
<pre class="crayon-plain-tag">ceph health detail 
# 注意Acting Set仅仅有一个成员
# pg 2.21 is stuck stale for 688.372740, current state stale+active+clean, last acting [7]
# 但是其它PG的Acting Set则不是
# pg 3.4f is active+recovering+degraded, acting [9,1]</pre>
<p>如果OSD的确出现硬件故障，则数据丢失。此外，你也无法对这种PG进行查询操作。</p>
<div class="blog_h3"><span class="graybg">inconsistent</span></div>
<p>定位出问题PG的主OSD，停止它，刷出日志，然后修复PG：</p>
<pre class="crayon-plain-tag">ceph health detail
# HEALTH_ERR 2 scrub errors; Possible data damage: 2 pgs inconsistent
# OSD_SCRUB_ERRORS 2 scrub errors
# PG_DAMAGED Possible data damage: 2 pgs inconsistent
#     pg 15.33 is active+clean+inconsistent, acting [8,9]
#     pg 15.61 is active+clean+inconsistent, acting [8,16]

# 查找OSD所在机器
ceph osd find 8
 
# 登陆到osd.8所在机器
systemctl stop ceph-osd@8.service
ceph-osd -i 8 --flush-journal
systemctl start ceph-osd@8.service
ceph pg repair 15.61</pre>
<div class="blog_h2"><span class="graybg">对象问题诊断</span></div>
<div class="blog_h3"><span class="graybg">unfound</span></div>
<p>持有对象权威副本的OSD宕机或被剔除，会导致该问题出现。例如两个配对的OSD（共同处理某个PG）：</p>
<ol>
<li>osd.1宕机</li>
<li>osd.2独自处理了一些写操作</li>
<li>osd1开机</li>
<li>osd.1+osd2配对，由于osd.2独自的写操作，缺失的对象排队等候在osd.1上恢复</li>
<li>恢复完成之前，osd.2宕机，或者被移除</li>
</ol>
<p>在上面这个事件序列中，osd.1知道权威副本存在，但是却找不到，这种情况下<span style="background-color: #c0c0c0;">针对目标对象的请求会被阻塞</span>，直到权威副本的持有者osd上线。</p>
<p>执行下面的命令，定位存在问题的PG：</p>
<pre class="crayon-plain-tag">ceph health detail | grep unfound
# OBJECT_UNFOUND 1/90055 objects unfound (0.001%)
#     pg 33.3e has 1 unfound objects
#    pg 33.3e is active+recovery_wait+degraded, acting [17,6], 1 unfound</pre>
<p>进一步，定位存在问题的对象：</p>
<pre class="crayon-plain-tag">// ceph pg 33.3e list_missing
{
    "offset": {
        "oid": "",
        "key": "",
        "snapid": 0,
        "hash": 0,
        "max": 0,
        "pool": -9223372036854775808,
        "namespace": ""
    },
    "num_missing": 1,
    "num_unfound": 1,
    "objects": [
        {
            "oid": {
                // 丢失的对象
                "oid": "obj_delete_at_hint.0000000066",
                "key": "",
                "snapid": -2,
                "hash": 2846662078,
                "max": 0,
                "pool": 33,
                "namespace": ""
            },
            "need": "1723'1412",
            "have": "0'0",
            "flags": "none",
            "locations": []
        }
    ],
    "more": false
}</pre>
<p>如果丢失的对象太多，more会显示为true。</p>
<p>执行下面的命令，可以查看PG的诊断信息：</p>
<pre class="crayon-plain-tag">// ceph pg 33.3e query
{
  "state": "active+recovery_wait+degraded",
  "recovery_state": [
    {
      "name": "Started/Primary/Active",
      "enter_time": "2018-06-16 15:03:32.873855",
      // 丢失的对象所在的OSD
      "might_have_unfound": [
        {
          "osd": "6",
          "status": "already probed"
        },
        {
          "osd": "11",
          "status": "osd is down"
        }
      ],
    } 
  ]
}</pre>
<p>上面输出中的osd.11，先前已经出现硬件故障，被移除了。这意味着unfound的对象已经不可恢复。你可以标记：</p>
<pre class="crayon-plain-tag"># 回滚到前一个版本，如果是新创建对象则忘记其存在。不支持EC池
ceph pg 33.3e mark_unfound_lost revert
# 让Ceph忘记unfound对象的存在
ceph pg 33.3e mark_unfound_lost delete </pre>
<div class="blog_h2"><span class="graybg">ceph-deploy</span></div>
<div class="blog_h3"><span class="graybg">TypeError: 'Logger' object is not callable</span></div>
<p>/usr/lib/python2.7/dist-packages/ceph_deploy/osd.py第376行，替换为：</p>
<pre class="crayon-plain-tag">LOG.info(line.decode('utf-8')) </pre>
<div class="blog_h3"><span class="graybg">Could not locate executable 'ceph-volume' make sure it is installed and available</span></div>
<p>应该安装ceph-deploy的1.5.39版本，2.0.0版本仅仅支持luminous：</p>
<pre class="crayon-plain-tag">apt remove ceph-deploy
apt install ceph-deploy=1.5.39 -y</pre>
<div class="blog_h3"><span class="graybg">部署MON后ceph-s卡死</span></div>
<p>在我的环境下，是因为MON节点识别的public addr为LVS的虚拟网卡的IP地址导致。修改配置，显式指定MON的IP地址即可：</p>
<pre class="crayon-plain-tag">[mon.master01-10-5-38-24]
public addr = 10.5.38.24 
cluster addr = 10.5.38.24

[mon.master02-10-5-38-39]
public addr = 10.5.38.39
cluster addr = 10.5.38.39

[mon.master03-10-5-39-41]
public addr = 10.5.39.41
cluster addr = 10.5.39.41</pre>
<div class="blog_h2"><span class="graybg">ceph-helm</span></div>
<p>在我的环境下部署，出现一系列和权限有关的问题，如果你遇到相同问题且不关心安全性，可以修改配置：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph edit configmap ceph-etc
apiVersion: v1
data:
  ceph.conf: |
    [global]
    fsid = 08adecc5-72b1-4c57-b5b7-a543cd8295e7
    mon_host = ceph-mon.ceph.svc.k8s.gmem.cc
    # 添加以下三行
    auth client required = none
    auth cluster required = none
    auth service required = none
    [osd]
    # 在大型集群里用单独的“集群”网可显著地提升性能
    cluster_network = 10.0.0.0/16
    ms_bind_port_max = 7100
    public_network = 10.0.0.0/16
kind: ConfigMap</pre>
<p>如果需要保证集群安全，请参考下面几个案例。</p>
<div class="blog_h3"><span class="graybg">ceph-mgr报Operation not permitted</span></div>
<p>问题现象：</p>
<p>此Pod一直无法启动，查看容器日志，发现：</p>
<p>timeout 10 ceph --cluster ceph auth get-or-create mgr.xenial-100 mon 'allow profile mgr' osd 'allow *' mds 'allow *' -o /var/lib/ceph/mgr/ceph-xenial-100/keyring</p>
<p>0 librados: client.admin authentication error (1) Operation not permitted</p>
<p>问题分析：</p>
<p>连接到可以访问的ceph-mon，执行命令：</p>
<pre class="crayon-plain-tag">kubectl -n ceph exec -it ceph-mon-nhx52 -c ceph-mon -- ceph</pre>
<p>发现报同样的错误。这说明client.admin的Keyring有问题。登陆到ceph-mon，获取Keyring列表：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph exec -it ceph-mon-nhx52 -c ceph-mon bash
# ceph --cluster=ceph  --name mon. --keyring=/var/lib/ceph/mon/ceph-xenial-100/keyring auth list   
 
installed auth entries:

client.admin
        key: AQAXPdtaAAAAABAA6wd1kCog/XtV9bSaiDHNhw==
        auid: 0
        caps: [mds] allow
        caps: [mgr] allow *
        caps: [mon] allow *
        caps: [osd] allow *

client.bootstrap-mds
        key: AQAgPdtaAAAAABAAFPgqn4/zM5mh8NhccPWKcw==
        caps: [mon] allow profile bootstrap-mds
client.bootstrap-osd
        key: AQAUPdtaAAAAABAASbfGQ/B/PY4Imoa4Gxsa2Q==
        caps: [mon] allow profile bootstrap-osd
client.bootstrap-rgw
        key: AQAJPdtaAAAAABAAswtFjgQWahHsuy08Egygrw==
        caps: [mon] allow profile bootstrap-rgw</pre>
<p>而当前使用的client.admin的Keyring内容为：</p>
<pre class="crayon-plain-tag">[client.admin]
  key = AQAda9taAAAAABAAgWIsgbEiEsFRJQq28hFgTQ==
  auid = 0
  caps mds = "allow"
  caps mon = "allow *"
  caps osd = "allow *"
  caps mgr = "allow *"</pre>
<p>内容不一致。使用auth list获得的client.admin的Keyring，可以发现是有效的：</p>
<pre class="crayon-plain-tag">ceph --cluster=ceph --name mon. --keyring=/var/lib/ceph/mon/ceph-xenial-100/keyring auth get client.admin &gt; client.admin.keyyring
ceph --name client.admin --keyring client.admin.keyyring # OKskydns_skydns_dns_cachemiss_count_total{instance="172.27.100.134:10055"}</pre>
<p>检查一下各Pod的/etc/ceph/ceph.client.admin.keyring，可以发现都是从Secret ceph-client-admin-keyring挂载的。那么这个Secret是如何生成的呢？执行命令：</p>
<pre class="crayon-plain-tag">kubectl -n ceph get job --output=yaml --export | grep ceph-client-admin-keyring -B 50</pre>
<p>可以发现Job ceph-storage-keys-generator负责生成该Secret。 查看其Pod日志可以生成Keyring、创建Secret的记录。进一步查看Pod的资源定义，可以看到负责创建的脚本/opt/ceph/ceph-storage-key.sh挂载自ConfigMap ceph-bin中的ceph-storage-key.sh。</p>
<p>解决此问题<span style="background-color: #c0c0c0;">最简单的办法</span>就是修改Secret，将其修改为集群中实际有效的Keyring：</p>
<pre class="crayon-plain-tag"># 导出Secret定义
kubectl -n ceph get  secret ceph-client-admin-keyring --output=yaml --export &gt; ceph-client-admin-keyring
# 获得有效Keyring的Base64编码
cat client.admin.keyyring | base64
# 将Secret中的编码替换为上述Base64，然后重新创建Secret
kubectl -n ceph apply -f ceph-client-admin-keyring</pre>
<p>此外Secret<span style="background-color: #c0c0c0;"> pvc-ceph-client-key中存放的也是admin用户的Key</span>，其内容也需要替换到有效的：</p>
<pre class="crayon-plain-tag">kubectl -n ceph edit secret  pvc-ceph-client-key</pre>
<div class="blog_h3"><span class="graybg">pvc无法提供</span></div>
<p>原因和上一个问题类似，还是权限问题。</p>
<p>查看无法绑定的PVC日志：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph describe pvc
 Normal   Provisioning        53s   ceph.com/rbd ceph-rbd-provisioner-5544dcbcf5-n846s 708edb2c-4619-11e8-abf2-e672650d97a2  External provisioner is provisioning volume for claim
"ceph/ceph-pvc"
  Warning  ProvisioningFailed  53s   ceph.com/rbd ceph-rbd-provisioner-5544dcbcf5-n846s 708edb2c-4619-11e8-abf2-e672650d97a2  Failed to provision volume with StorageClass "general"
: failed to create rbd image: exit status 1, command output: 2018-04-22 13:44:35.269967 7fb3e3e3ad80 -1 did not load config file, using default settings.
2018-04-22 13:44:35.297828 7fb3e3e3ad80 -1 auth: unable to find a keyring on /etc/ceph/ceph.client.admin.keyring,/etc/ceph/ceph.keyring,/etc/ceph/keyring,/etc/ceph/keyring.bin: (2)
 No such file or directoryConnection to localhost closed by remote host.
Connection to localhost closed.e3e3ad80  0 librados: client.admin authentication error (1) Operation not permitted</pre>
<p>rbd-provisioner需要读取StorageClass定义，获取需要的凭证信息：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph get storageclass --output=yaml
apiVersion: v1                                                                                                                                                                      
items:                                                                                                                                                                              
- apiVersion: storage.k8s.io/v1                                                                                                                                                     
  kind: StorageClass                                                                                                                                                                
  metadata:                                                                                                                                 
    name: general
  parameters:
    adminId: admin
    adminSecretName: pvc-ceph-conf-combined-storageclass
    adminSecretNamespace: ceph
    imageFeatures: layering
    imageFormat: "2"
    monitors: ceph-mon.ceph.svc.k8s.gmem.cc:6789
    pool: rbd
    userId: admin
    userSecretName: pvc-ceph-client-key
  provisioner: ceph.com/rbd
  reclaimPolicy: Delete</pre>
<p>可以看到牵涉到两个Secret：pvc-ceph-conf-combined-storageclass、pvc-ceph-client-key，你需要把正确的Keyring内容写入其中。</p>
<div class="blog_h3"><span class="graybg">pvc无法Attach</span></div>
<p>症状：PVC可以Provision，RBD可以通过Ceph命令挂载，但是Pod无法启动，Describe之显示：</p>
<p style="padding-left: 30px;">auth: unable to find a keyring on /etc/ceph/keyring: (2) No such file or directory<br />monclient(hunting): authenticate NOTE: no keyring found; disabled cephx authentication<br />librados: client.admin authentication error (95) Operation not supported</p>
<p>解决办法：把ceph.client.admin.keyring拷贝一份为 /etc/ceph/keyring即可。</p>
<div class="blog_h3"><span class="graybg">ceph-osd报Operation not permitted</span></div>
<p>原因和上一个问题一样。查看无法启动的容器日志：</p>
<pre class="crayon-plain-tag">kubectl -n ceph logs ceph-osd-dev-vdb-bjnbm -c osd-prepare-pod
# ceph --cluster ceph --name client.bootstrap-osd --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring health                                                             
# 0 librados: client.bootstrap-osd authentication error (1) Operation not permitted                                                         
# [errno 1] error connecting to the cluster</pre>
<p>进一步查看，可以发现/var/lib/ceph/bootstrap-osd/ceph.keyring挂载自ceph-bootstrap-osd-keyring下的ceph.keyring：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph get secret ceph-bootstrap-osd-keyring --output=yaml --export
apiVersion: v1
data:
  ceph.keyring: W2NsaWVudC5ib290c3RyYXAtb3NkXQogIGtleSA9IEFRQVlhOXRhQUFBQUFCQUFSQ2l1bVY1NFpOU2JGVWwwSDZnYlJ3PT0KICBjYXBzIG1vbiA9ICJhbGxvdyBwcm9maWxlIGJvb3RzdHJhcC1vc2QiCgo=
kind: Secret
metadata:
  creationTimestamp: null
  name: ceph-bootstrap-osd-keyring
  selfLink: /api/v1/namespaces/ceph/secrets/ceph-bootstrap-osd-keyring
type: Opaque

# BASE64解码后：
[client.bootstrap-osd]
  key = AQAYa9taAAAAABAARCiumV54ZNSbFUl0H6gbRw==
  caps mon = "allow profile bootstrap-osd"</pre>
<p>获得实际有效的Keyring：</p>
<pre class="crayon-plain-tag">kubectl -n ceph exec -it ceph-mon-nhx52 -c ceph-mon -- ceph --cluster=ceph --name mon. --keyring=/var/lib/ceph/mon/ceph-xenial-100/keyring auth get client.bootstrap-osd
# 注意上述命令的输出的第一行exported keyring for client.bootstrap-osd不属于Keyring
[client.bootstrap-osd]
        key = AQAUPdtaAAAAABAASbfGQ/B/PY4Imoa4Gxsa2Q==
        caps mon = "allow profile bootstrap-osd"</pre>
<p>修改Secret：<pre class="crayon-plain-tag">kubectl -n ceph edit secret ceph-bootstrap-osd-keyring</pre> 替换为上述Keyring。</p>
<div class="blog_h3"><span class="graybg">ceph-osd报No cluster conf with fsid</span></div>
<p>报错信息：</p>
<pre class="crayon-plain-tag"># kubectl -n ceph logs  ceph-osd-dev-vdc-cpkxh -c osd-activate-pod
ceph_disk.main.Error: Error: No cluster conf found in /etc/ceph with fsid 08adecc5-72b1-4c57-b5b7-a543cd8295e7
# 每个OSD都包同样的错误</pre>
<p>对应的配置文件内容： </p>
<pre class="crayon-plain-tag">kubectl -n ceph get configmap ceph-etc --output=yaml
apiVersion: v1
data:
  ceph.conf: |
    [global]
    fsid = a4426e8a-c46d-4407-95f1-911a23a0dd6e
    mon_host = ceph-mon.ceph.svc.k8s.gmem.cc
    [osd]
    cluster_network = 10.0.0.0/16
    ms_bind_port_max = 7100
    public_network = 10.0.0.0/16
kind: ConfigMap
metadata:
  name: ceph-etc
  namespace: ceph</pre>
<p>可以看到，fsid不一致。修改一下ConfigMap中的fsid即可解决此问题。  </p>
<div class="blog_h3"><span class="graybg">容器无法Attach PV</span></div>
<p>报错信息：</p>
<p style="padding-left: 30px;">describe pod报错：timeout expired waiting for volumes to attach/mount for pod<br />kubelet报错：executable file not found in $PATH, rbd output</p>
<p>原因分析：动态提供的持久卷，包含两个阶段：</p>
<ol>
<li>卷提供，原本由控制平面负责，controller-manager中需要包含rbd命令，才能在Ceph集群中创建供K8S使用的镜像。目前这个职责由<a href="https://github.com/kubernetes-incubator/external-storage">external_storage</a>项目的rbd-provisioner完成</li>
<li>卷依附/分离，由使用卷的Pod所在的Node的kubelet负责完成。这些Node需要安装rbd命令，并提供有效的配置文件</li>
</ol>
<p>解决方案：</p>
<pre class="crayon-plain-tag"># 安装软件
apt install -y ceph-common
# 从ceph-mon拷贝以下文件：
# /etc/ceph/ceph.client.admin.keyring
# /etc/ceph/ceph.conf</pre>
<p>应用上述方案后，如果继续报错：rbd: map failed exit status 110, rbd output: rbd: sysfs write failed In some cases useful info is found in syslog。则查看一下系统日志：</p>
<pre class="crayon-plain-tag">dmesg | tail

# [ 3004.833252] libceph: mon0 10.0.0.100:6789 feature set mismatch, my 106b84a842a42 
#     &lt; server's 40106b84a842a42, missing 400000000000000
# [ 3004.840980] libceph: mon0 10.0.0.100:6789 missing required protocol features</pre>
<p>对照本文前面的特性表，可以发现内核版本必须4.5+才可以（CEPH_FEATURE_NEW_OSDOPREPLY_ENCODING）。 </p>
<p>最简单的办法就是升级一下内核：</p>
<pre class="crayon-plain-tag"># Desktop
apt install --install-recommends linux-generic-hwe-16.04 xserver-xorg-hwe-16.04 -y
# Server
apt install --install-recommends linux-generic-hwe-16.04 -y

sudo apt-get remove linux-headers-4.4.* -y &amp;&amp; \
sudo apt-get remove linux-image-4.4.* -y &amp;&amp; \
sudo apt-get autoremove -y &amp;&amp; \
sudo update-grub</pre>
<p>或者，将tunables profile调整到hammer版本的Ceph：</p>
<pre class="crayon-plain-tag">ceph osd crush tunables hammer
# adjusted tunables profile to hammer</pre>
<div class="blog_h3"><span class="graybg">OSD启动失败报文件名太长</span></div>
<p>报错信息：ERROR: osd init failed: (36) File name too long</p>
<p>报错原因：使用的文件系统为EXT4，存储的xattrs大小有限制，有条件的话最好使用XFS</p>
<p>解决办法：修改配置文件，如下：</p>
<pre class="crayon-plain-tag">osd_max_object_name_len = 256
osd_max_object_namespace_len = 64</pre>
<div class="blog_h3"><span class="graybg">无法打开/proc/0/cmdline</span></div>
<p>报错信息：Fail to open '/proc/0/cmdline' error No such file or directory</p>
<p>报错原因：在CentOS 7上，将ceph-mon和ceph-osd（基于目录）部署在同一节点（基于Helm）报此错误，分离后问题消失。此外部署mon的那些节点还设置了虚IP，其子网和Ceph的Cluster/Public网络相同，这导致了某些OSD监听的地址不正确。</p>
<p>再次遇到此问题，原因是一个虚拟网卡lo:ngress使用和eth0相同的网段，导致OSD使用了错误的网络。</p>
<p>解决办法是写死OSD监听地址：</p>
<pre class="crayon-plain-tag">[osd.2]                                                                                                                                                                                    
public addr = 10.0.4.1                                                                                                                                                                     
cluster addr = 10.0.4.1</pre>
<div class="blog_h3"><span class="graybg">无法挂载RBD</span></div>
<p>报错信息：Input/output error，结合dmesg | tail可以看到更细节的报错</p>
<p>报错原因，可能情况：</p>
<ol>
<li>CentOS7下报错，提示客户端不满足特性CEPH_FEATURE_CRUSH_V4（1000000000000）。解决办法，将Bucket算法改为straw。注意，之后加入的OSD仍然默认使用straw2，使用的镜像的标签为tag-build-master-luminous-ubuntu-16.04。</li>
</ol>
<div class="blog_h3"><span class="graybg">write error: File name too long</span></div>
<p><a href="https://github.com/kubernetes-incubator/external-storage/tree/master/ceph/cephfs">external storage中的CephFS</a>可以正常Provisioning，但是尝试读写数据时报此错误。原因是文件路径过长，和底层文件系统有关，为了兼容部分Ext文件系统的机器，我们限制了osd_max_object_name_len。</p>
<p>解决办法，不使用UUID，而使用namespace + pvcname来命名目录。修改cephfs-provisioner.go，118行：</p>
<pre class="crayon-plain-tag">// create random share name
share := fmt.Sprintf("%s-%s", options.PVC.Namespace,options.PVC.Name)
// create random user id
user := fmt.Sprintf("%s-%s", options.PVC.Namespace,options.PVC.Name)</pre>
<p>重新编译即可。 </p>
<div class="blog_h2"><span class="graybg">K8S相关</span></div>
<div class="blog_h3"><span class="graybg">rbd image *** is still being used</span></div>
<p>describe pod发现：</p>
<pre class="crayon-plain-tag">rbd image rbd-unsafe/kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 is still being used</pre>
<p>说明有其它客户端正在占用此镜像。如果尝试删除镜像，你会发现无法成功：</p>
<pre class="crayon-plain-tag">rbd rm rbd-unsafe/kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 

librbd::image::RemoveRequest: 0x560e39df9af0 check_image_watchers: image has watchers - not removing
Removing image: 0% complete...failed.
rbd: error: image still has watchers
This means the image is still open or the client using it crashed. Try again after closing/unmapping it or waiting 30s for the crashed client to timeout. </pre>
<p>要知道watcher是谁，可以执行：</p>
<pre class="crayon-plain-tag">rbd status rbd-unsafe/kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 
Watchers:
        watcher=10.5.39.12:0/1652752791 client.94563 cookie=18446462598732840961</pre>
<p>可以发现10.5.39.12正在占用镜像。</p>
<p>另一种获取watcher的方法是，使用rbd的header对象。执行下面的命令获取rbd的诊断信息：</p>
<pre class="crayon-plain-tag">rbd info rbd-unsafe/kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 

rbd image 'kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6':
        size 8192 MB in 2048 objects
        order 22 (4096 kB objects)
        block_name_prefix: rbd_data.134474b0dc51
        format: 2
        features: layering
        flags: 
        create_timestamp: Wed Jul 11 17:49:51 2018</pre>
<p>字段block_name_prefix的值rbd_data.134474b0dc51，将data换为header即为header对象。然后使用命令：</p>
<pre class="crayon-plain-tag">rados listwatchers -p rbd-unsafe rbd_header.134474b0dc51

watcher=10.5.39.12:0/1652752791 client.94563 cookie=18446462598732840961</pre>
<p>既然知道10.5.39.12占用镜像，断开连接即可。 在此机器上执行下面的命令，显示当前映射的rbd镜像列表：</p>
<pre class="crayon-plain-tag">rbd showmapped

id pool       image                                                       snap device    
0  rbd-unsafe kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 -    /dev/rbd0 
1  rbd-unsafe kubernetes-dynamic-pvc-0729f9a6-84f0-11e8-9b75-5a3f858854b1 -    /dev/rbd1</pre>
<p>此机器上的rbd0虽然映射，但是没有挂载。解除映射：</p>
<pre class="crayon-plain-tag">rbd unmap /dev/rbd0</pre>
<p>再次检查rbd镜像状态，发现没有watcher了：</p>
<pre class="crayon-plain-tag">rbd status rbd-unsafe/kubernetes-dynamic-pvc-c0ac2cff-84ef-11e8-9a2a-566b651a72d6 

Watchers: none</pre>
<div class="blog_h3"><span class="graybg">rbd: map failed signal: aborted (core dumped)</span></div>
<p>kubectl describe报错Unable to mount volumes for pod... timeout expired waiting for volumes to attach or mount for pod...</p>
<p>检查发现目标rbd没有Watcher，Pod所在机器的Kubectl报错rbd: map failed signal: aborted (core dumped)。此前曾经在该机器上执行过rbd unmap操作。</p>
<p>手工 rbd map后问题消失。</p>
<div class="blog_h2"><span class="graybg">断电后无法启动OSD</span></div>
<div class="blog_h3"><span class="graybg"> journal do_read_entry: bad header magic</span></div>
<p>报错信息：journal do_read_entry(156389376): bad header magic......FAILED assert(interval.last &gt; last)</p>
<p>这是12.2版本<a href="http://tracker.ceph.com/issues/21142">已知的BUG</a>，断电后可能出现OSD无法启动，可能导致数据丢失。</p>
<div class="blog_h2"><span class="graybg">Couldn't init storage provider (RADOS)</span></div>
<p>RGW实例无法启动，通过journalctl看到上述信息。</p>
<p>要查看更多信息，需要查看RGW日志：</p>
<pre class="crayon-plain-tag">2020-10-22 16:51:55.771035 7fb1b0f20e80  0 ceph version 12.2.5 (cad919881333ac92274171586c827e01f554a70a) luminous (stable), process (unknown), pid 2546439
2020-10-22 16:51:55.792872 7fb1b0f20e80  0 librados: client.rgw.ceph02 authentication error (22) Invalid argument
2020-10-22 16:51:55.793450 7fb1b0f20e80 -1 Couldn't init storage provider (RADOS)</pre>
<p>可以发现是和身份验证有关的问题。</p>
<p>通过<pre class="crayon-plain-tag">systemctl status ceph-radosgw@rgw.$RGW_HOST</pre>得到命令行，手工运行：</p>
<pre class="crayon-plain-tag">radosgw -f --cluster ceph  --name client.rgw.ceph02 --setuser ceph --setgroup ceph -d --debug_ms 1</pre>
<p>发现报错和上面一样。尝试增加--keyring参数，问题解决：</p>
<pre class="crayon-plain-tag">radosgw -f --cluster ceph  --name client.rgw.ceph02        \
  --setuser ceph --setgroup ceph -d --debug_ms 1           \
  --keyring=/var/lib/ceph/radosgw/ceph-rgw.ceph02/keyring</pre>
<p>看来是Systemd服务没有找到keyring导致。 </p>
<div class="blog_h2"><span class="graybg">零散问题</span></div>
<div class="blog_h3"><span class="graybg">禁用IPv6的机器上无法开启Prometheus模块</span></div>
<p>报错信息：Unhandled exception from module 'prometheus' while running on mgr.master01-10-5-38-24: error('No socket could<br /> be created',)  </p>
<p>解决办法：<pre class="crayon-plain-tag">ceph config-key set mgr/prometheus/server_addr 0.0.0.0</pre></p>
<div class="blog_h3"><span class="graybg">反复警告mon... clock skew</span></div>
<p>原因是时钟不同步警告阈值太低，在global段增加配置并重启MON：</p>
<pre class="crayon-plain-tag">mon clock drift allowed = 2
mon clock drift warn backoff = 30</pre>
<p>或者执行下面的命令即时生效：</p>
<pre class="crayon-plain-tag">ceph tell mon.* injectargs '--mon_clock_drift_allowed=2'
ceph tell mon.* injectargs '--mon_clock_drift_warn_backoff=30'</pre>
<p>或者检查ntp相关配置，保证时钟同步精度。</p>
<div class="blog_h3"><span class="graybg">深度清理导致高IO</span></div>
<p>深度清理很消耗IO，如果长时间无法完成，可以禁用：</p>
<pre class="crayon-plain-tag">ceph osd set noscrub
ceph osd set nodeep-scrub</pre>
<p>问题解决后，可以再启用： </p>
<pre class="crayon-plain-tag">ceph osd unset noscrub
ceph osd unset nodeep-scrub </pre>
<p>使用CFQ作为IO调度器时，可以调整OSD IO线程的优先级：</p>
<pre class="crayon-plain-tag"># 设置调度器
echo cfq &gt; /sys/block/sda/queue/scheduler

# 检查当前某个OSD的磁盘线程优先级类型
ceph daemon osd.4 config get osd_disk_thread_ioprio_class

# 修改IO优先级
ceph tell osd.* injectargs '--osd_disk_thread_ioprio_priority 7'
# IOPRIO_CLASS_RT最高 IOPRIO_CLASS_IDLE最低
ceph tell osd.* injectargs '--osd_disk_thread_ioprio_class idle'</pre>
<p>如果上述措施没有问题时，可以考虑配置以下参数：</p>
<pre class="crayon-plain-tag">osd_deep_scrub_stride = 131072                                                                                                                                                           
# 每次Scrub的块数量范围
osd_scrub_chunk_min = 1                                                                                                                                                                  
osd_scrub_chunk_max = 5                                                                                                                                                                  
osd scrub during recovery = false                                                                                                                                                        
osd deep scrub interval = 2592000                                                                                                                                                        
osd scrub max interval = 2592000                                                                                                                                                         
# 单个OSD并发进行的Scrub个数
osd max scrubs = 1       
# Scrub起止时间                                                                                                                                                                
osd max begin hour = 2                                                                                                                                                                   
osd max end hour = 6                                                                                                                                                                     
# 系统负载超过多少则禁止Scrub
osd scrub load threshold = 4                                                                                                                                                             
# 每次Scrub后强制休眠0.1秒
osd scrub sleep = 0.1                                                                                                                                                                      
# 线程优先级
osd disk thread ioprio priority = 7
osd disk thread ioprio class = idle</pre>
<div class="blog_h3"><span class="graybg">强制unmap</span></div>
<p>如果Watcher被黑名单，则尝试Unmap镜像时会报错：rbd: sysfs write failed rbd: unmap failed: (16) Device or resource busy</p>
<p>可以使用下面的命令强制unmap：<pre class="crayon-plain-tag">rbd unmap -o force ...</pre> </p>
<div class="blog_h3"><span class="graybg">增加pg_num和pgp_num后无法A+C</span></div>
<p>部分PG状态卡死，可能原因是OSD允许的PG数量受限，修改全局配置项mon_max_pg_per_osd并重启MON即可。</p>
<p>此外注意：调整PG数量后，<span style="background-color: #c0c0c0;">一定要进入A+C状态后，再进行下一次调整</span>。</p>
<div class="blog_h3"><span class="graybg">无法删除RBD镜像</span></div>
<p>下面第二个镜像对应的K8S PV已经删除：</p>
<pre class="crayon-plain-tag">rbd ls
# kubernetes-dynamic-pvc-35350b13-46b8-11e8-bde0-a2c14c93573f
# kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4</pre>
<p>但是对应的RBD没有删除，手工删除：</p>
<pre class="crayon-plain-tag">rbd remove kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4</pre>
<p>报错：</p>
<p>2018-04-23 13:37:25.559444 7f919affd700 -1 librbd::image::RemoveRequest: 0x5598e77831d0 check_image_watchers: image has watchers - not removing<br />Removing image: 0% complete...failed.<br />rbd: error: image still has watchers<br />This means the image is still open or the client using it crashed. Try again after closing/unmapping it or waiting 30s for the crashed client to timeout.</p>
<p>查看RBD状态：</p>
<pre class="crayon-plain-tag"># rbd info kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4
rbd image 'kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4':
        size 8192 MB in 2048 objects
        order 22 (4096 kB objects)
        block_name_prefix: rbd_data.1003e238e1f29
        format: 2
        features: layering
        flags: 
        create_timestamp: Mon Apr 23 11:42:59 2018

#rbd status kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4
Watchers:
        watcher=10.0.0.101:0/4275384344 client.65597 cookie=18446462598732840963</pre>
<p>到10.0.0.101这台机器上查看：</p>
<pre class="crayon-plain-tag"># df | grep e6e3339859d4
/dev/rbd2        8125880  251560   7438508   4% /var/lib/kubelet/plugins/kubernetes.io/rbd/rbd/rbd-image-kubernetes-dynamic-pvc-78740b26-46eb-11e8-8349-e6e3339859d4</pre>
<p>重启Kubelet后可以删除RBD。</p>
<div class="blog_h3"><span class="graybg">Error EEXIST: entity osd.9 exists but key does not match</span></div>
<pre class="crayon-plain-tag"># 删除密钥
ceph auth del osd.9
# 重新收集目标主机的密钥
ceph-deploy --username ceph-ops gatherkeys Carbon</pre>
<div class="blog_h3"><span class="graybg">创建新Pool后无法Active+Clean</span></div>
<pre class="crayon-plain-tag">pgs:     12.413% pgs unknown                                                                                                                                                         
             20.920% pgs not active                                                                                                                                                      
             768 active+clean                                                                                                                                                            
             241 creating+activating                                                                                                                                                     
             143 unknown</pre>
<p>可能是由于PG总数太大导致，降低PG数量后很快Active+Clean </p>
<div class="blog_h3"><span class="graybg">Orphaned pod无法清理</span></div>
<p>报错信息：Orphaned pod "a9621c0e-41ee-11e8-9407-deadbeef00a0" found, but volume paths are still present on disk : There were a total of 1 errors similar to this. Turn up verbosity to see them</p>
<p>临时解决办法：</p>
<p>rm -rf /var/lib/kubelet/pods/a9621c0e-41ee-11e8-9407-deadbeef00a0/volumes/rook.io~rook/</p>
<div class="blog_h3"><span class="graybg">osd启动报错：ERROR: osd init failed: (1) Operation not permitted</span></div>
<p>可能原因是OSD使用的keyring和MON不一致。对于ID为14的OSD，将宿主机/var/lib/ceph/osd/ceph-14/keyring的内容替换为<pre class="crayon-plain-tag">ceph auth get osd.14</pre>的输出前两行即可。</p>
<div class="blog_h3"><span class="graybg">Mount failed with '(11) Resource temporarily unavailable'</span></div>
<p>在没有停止OSD的情况下执行ceph-objectstore-tool命令，会出现此错误。</p>
<div class="blog_h3"><span class="graybg">neither `public_addr` nor `public_network` keys are defined for monitors</span></div>
<p>通过ceph-deploy添加MON节点时出现此错误，将public_network配置添加到配置文件的global段即可。</p>
<div class="blog_h3"><span class="graybg">journalctl删除pv后卡在Terminating</span></div>
<p>可能原因：</p>
<ol>
<li>对应的PVC没有删除，还在引用此PV。先删除PV即可</li>
</ol>
<div class="blog_h3"><span class="graybg">chown: cannot access '/var/log/ceph': No such file or directory</span></div>
<p>OSD无法启动，报上面的错误，可以配置：</p>
<pre class="crayon-plain-tag">ceph:
  storage:
    osd_log: /var/log </pre>
<div class="blog_h3"><span class="graybg">HEALTH_WARN application not enabled on</span></div>
<pre class="crayon-plain-tag">#池 # 功能
ceph osd pool application enable rbd block-devices</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/ceph-study-note">Ceph学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/ceph-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于Rook的Kubernetes存储方案</title>
		<link>https://blog.gmem.cc/rook-based-k8s-storage-solution</link>
		<comments>https://blog.gmem.cc/rook-based-k8s-storage-solution#comments</comments>
		<pubDate>Wed, 28 Feb 2018 12:06:09 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[PaaS]]></category>
		<category><![CDATA[Ceph]]></category>
		<category><![CDATA[K8S]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=19163</guid>
		<description><![CDATA[<p>简介 Rook是专用于Cloud-Native环境的文件、块、对象存储服务。它实现了一个自我管理的、自我扩容的、自我修复的分布式存储服务。 Rook支持自动部署、启动、配置、分配（provisioning）、扩容/缩容、升级、迁移、灾难恢复、监控，以及资源管理。 为了实现所有这些功能，Rook依赖底层的容器编排平台。 Rook在初期专注于Kubernetes+Ceph。Ceph是一个分布式存储系统，支持文件、块、对象存储，在生产环境中被广泛应用。 Rook架构 Rook和K8S的交互关系如下图： &#160; 说明如下： Kubelet是K8S节点上运行的代理程序，负责挂载当前节点的Pod所需要的卷 Rook提供了一种卷插件，扩展了K8S的存储系统。Pod可以挂载Rook管理的块设备或者文件系统 Operator启动并监控：监控Ceph的Pods、OSD（提供基本的RADOS存储）的Daemonset。Operator还管理CRD、对象存储（S3/Swift）、文件系统。Operator通过监控存储守护程序，来保障集群的健康运行。Ceph监控Pod会自动启动和Failover。集群扩缩容时Operator会进行相应的调整 Operator还负责创建Rook Agent。这些代理是部署在所有K8S节点上的Pod。每个代理都配置一个Flexvolume驱动，此驱动和K8S的卷控制框架集成。节点上的所有存储相关操作（例如添加网络存储设备、挂载卷、格式化文件系统）都Agent负责 Rook守护程序（Mons, OSDs, MGR, RGW, MDS）被编译到单体的程序rook中，并打包到一个很小的容器中。该容器还包含Ceph守护程序，以及管理、存储数据所需的工具。 Rook隐藏了Ceph的很多细节，而向它的用户暴露物理资源、池、卷、文件系统、Bucket等概念。 Ceph 前提条件 <a class="read-more" href="https://blog.gmem.cc/rook-based-k8s-storage-solution">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/rook-based-k8s-storage-solution">基于Rook的Kubernetes存储方案</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Rook是专用于Cloud-Native环境的文件、块、对象存储服务。它实现了一个自我管理的、自我扩容的、自我修复的分布式存储服务。</p>
<p>Rook支持自动部署、启动、配置、分配（provisioning）、扩容/缩容、升级、迁移、灾难恢复、监控，以及资源管理。 为了实现所有这些功能，Rook依赖底层的容器编排平台。</p>
<p>Rook在初期专注于Kubernetes+Ceph。Ceph是一个分布式存储系统，支持文件、块、对象存储，在生产环境中被广泛应用。</p>
<div class="blog_h2"><span class="graybg">Rook架构</span></div>
<p>Rook和K8S的交互关系如下图：<a href="https://blog.gmem.cc/wp-content/uploads/2018/02/rook-architecture.png"><br /></a></p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2018/02/rook-architecture-0.png"><img class="aligncenter  wp-image-19287" src="https://blog.gmem.cc/wp-content/uploads/2018/02/rook-architecture-0.png" alt="rook-architecture-0" width="860" height="392" /></a></p>
<p>&nbsp;</p>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2018/02/rook-architecture-2.png"><img class="aligncenter  wp-image-19187" src="https://blog.gmem.cc/wp-content/uploads/2018/02/rook-architecture-2.png" alt="rook-architecture-2" width="918" height="464" /></a></p>
<p>说明如下：</p>
<ol>
<li>Kubelet是K8S节点上运行的代理程序，负责挂载当前节点的Pod所需要的卷</li>
<li>Rook提供了一种卷插件，扩展了K8S的存储系统。Pod可以挂载Rook管理的块设备或者文件系统</li>
<li>Operator启动并监控：监控Ceph的Pods、OSD（提供基本的RADOS存储）的Daemonset。Operator还管理CRD、对象存储（S3/Swift）、文件系统。Operator通过监控存储守护程序，来保障集群的健康运行。Ceph监控Pod会自动启动和Failover。集群扩缩容时Operator会进行相应的调整</li>
<li>Operator还负责创建<span style="background-color: #c0c0c0;">Rook Agent</span>。这些代理是<span style="background-color: #c0c0c0;">部署在所有K8S节点上的Pod</span>。每个代理都配置一个Flexvolume驱动，此驱动和K8S的卷控制框架集成。节点上的所有存储相关操作（例如添加网络存储设备、挂载卷、格式化文件系统）都Agent负责</li>
</ol>
<p>Rook守护程序（Mons, OSDs, MGR, RGW, MDS）被编译到单体的程序rook中，并打包到一个很小的容器中。该容器还包含Ceph守护程序，以及管理、存储数据所需的工具。</p>
<p>Rook隐藏了Ceph的很多细节，而向它的用户暴露<span style="background-color: #c0c0c0;">物理资源、池、卷、文件系统、Bucket</span>等概念。</p>
<div class="blog_h1"><span class="graybg">Ceph</span></div>
<div class="blog_h2"><span class="graybg">前提条件</span></div>
<p>要使用rook-ceph，必须满足：</p>
<ol>
<li>Kubernetes 1.11+版本</li>
<li>节点提供以下本地存储之一：
<ol>
<li>裸设备（没有分区、文件系统）</li>
<li>裸分区（没有文件系统），可以<pre class="crayon-plain-tag">lsblk -f</pre>查看，如果<pre class="crayon-plain-tag">FSTYPE</pre>不为空说明有文件系统</li>
<li>来自block模式的StorageClass提供的PV</li>
</ol>
</li>
</ol>
<div class="blog_h2"><span class="graybg">安装</span></div>
<div class="blog_h3"><span class="graybg">快速起步</span></div>
<p>使用下面的命令，创建一个简单的Rook集群：</p>
<pre class="crayon-plain-tag">git clone --single-branch --branch release-1.4 https://github.com/rook/rook.git
cd rook/cluster/examples/kubernetes/ceph
kubectl create -f common.yaml
kubectl create -f operator.yaml
kubectl create -f cluster.yaml</pre>
<p>集群启动后，你可以创建块/对象/文件系统存储。</p>
<div class="blog_h3"><span class="graybg">Operator</span></div>
<p>一般情况下，使用Rook Operator来管理Rook集群，下面的命令安装此Operator： </p>
<pre class="crayon-plain-tag">cd cluster/examples/kubernetes/ceph
kubectl create -f common.yaml
kubectl create -f operator.yaml

kubectl -n rook-ceph get pod</pre>
<p>你也可以通过Helm Chart安装：</p>
<pre class="crayon-plain-tag">helm repo add rook-release https://charts.rook.io/release

# Helm 3
helm install --namespace rook-ceph rook-ceph rook-release/rook-ceph

# Helm 2
helm install --namespace rook-ceph --name rook-ceph rook-release/rook-ceph


# 参数示例
helm install --namespace rook-ceph rook-ceph rook-release/rook-ceph \
  --set image.repository=docker.gmem.cc/rook/ceph,logLevel=DEBUG  \
  --set csi.cephcsi.image=docker.gmem.cc/cephcsi/cephcsi:v3.1.0  \
  --set csi.registrar.image=docker.gmem.cc/k8scsi/csi-node-driver-registrar:v1.2.0  \
  --set csi.resizer.image=docker.gmem.cc/k8scsi/csi-resizer:v0.4.0  \
  --set csi.provisioner.image=docker.gmem.cc/k8scsi/csi-provisioner:v1.6.0 \
  --set csi.snapshotter.image=docker.gmem.cc/k8scsi/csi-snapshotter:v2.1.1  \
  --set csi.attacher.image=docker.gmem.cc/k8scsi/csi-attacher:v2.1.0</pre>
<div class="blog_h2"><span class="graybg">创建集群</span></div>
<div class="blog_h3"><span class="graybg">示例</span></div>
<p>使用自定义资源CephCluster来创建Rook Ceph集群： </p>
<pre class="crayon-plain-tag"># 使用宿主机上的块设备
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: ceph/ceph:v15.2.4
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: true
  storage:
    useAllNodes: true
    useAllDevices: true


# 使用PVC创建块设备
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: ceph/ceph:v15.2.4
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    volumeClaimTemplate:
      spec:
        storageClassName: local-storage
        resources:
          requests:
            storage: 10Gi
  storage:
   storageClassDeviceSets:
    - name: set1
      count: 3
      portable: false
      tuneDeviceClass: false
      encrypted: false
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 10Gi
          storageClassName: local-storage
          volumeMode: Block
          accessModes:
            - ReadWriteOnce

# 使用特定设备
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: ceph/ceph:v15.2.4
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: true
  dashboard:
    enabled: true
  # cluster level storage configuration and selection
  storage:
    useAllNodes: false
    useAllDevices: false
    deviceFilter:
    config:
      metadataDevice:
      databaseSizeMB: "1024" # this value can be removed for environments with normal sized disks (100 GB or larger)
    nodes:
    - name: "172.17.4.201"
      devices:             # specific devices to use for storage can be specified for each node
      - name: "sdb" # Whole storage device
      - name: "sdc1" # One specific partition. Should not have a file system on it.
      - name: "/dev/disk/by-id/ata-ST4000DM004-XXXX" # both device name and explicit udev links are supported
      config:         # configuration can be specified at the node level which overrides the cluster level config
        storeType: bluestore
    - name: "172.17.4.301"
      deviceFilter: "^sd."</pre>
<div class="blog_h3"><span class="graybg">CephCluster</span></div>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  # 内部名称，一般使用命名空间的名字，因为同一个命名空间仅仅支持一个集群
  name: rook-ceph
  namespace: rook-ceph
spec:
  # Rook可以连接到外部Ceph集群，而不是在K8S集群中创建自己管理的Ceph集群
  external:
    # 使用外部集群（最低luminous 12.2.x），仅仅作为集群消费者。这种情况下，Rook仍然能够在K8S中创建
    # 对象网关、MDS、NFS组件
    # 除了cephVersion.image、dataDirHostPath之外的其它选项，都被忽略
    #
    # Rook特性要求的Ceph版本
    #   动态分配RBD：12.2.X
    #   配置额外的CR（object,file,nfs）：13.2.3
    #   动态分配CephFS：14.2.3
    enabled: true
  # Ceph守护程序的版本信息
  cephVersion:
    # 用于运行Ceph守护进程的镜像
    image: ceph/ceph:v15.2.4
    # 如果true则允许使用不支持的major Ceph版本
    # 目前支持nautilus( 14.2.0 )、octopus( 15.2.0 )
    allowUnsupported: true
  # 宿主机上的路径，在此路径下存储各Ceph服务的配置和数据
  dataDirHostPath: /var/lib/rook
  # 如果设置为true，则升级时不会检查Ceph守护程序的新版本
  skipUpgradeChecks: false
  # Ceph仪表盘设置
  dashboard:
    # 启用仪表盘，以查看集群状态
    enabled: true
    # URL前缀，反向代理场景下有用
    urlPrefix:
    # 仪表盘端口
    port:
    # 使用使用SSL，13.2.2+
    ssl: true
  # 基于Prometheus的监控配置
  monitoring:
    enabled: true
    # 部署PrometheusRule的命名空间，默认此CR所在命名空间
    rulesNamespace:
  # 集群网络设置，如果不指定，使用默认的SDN
  network:
    # 可选 host multus（为Pod添加多个接口的开源项目，试验性支持）
    provider: host
    # 网络选择器，选择器的值必须匹配Multus的NetworkAttachmentDefinition对象
    selectors
      # 客户端通信（读写）使用的网络
      public:
      # 集群内部流量使用的网络
      cluster:
  mon:
    # mon pod的数量
    count: 3
    # 允许在单个节点上部署多个mon pod
    allowMultiplePerNode: true
    # 用于创建mon存储的PersistentVolumeSpec，如果不支持，使用HostPath卷
    volumeClaimTemplate:
  mgr:
    # 启用的mgr模块列表
    modules:
  crashCollector:
    # 禁用crash collector，不再每个运行了Ceph守护进程的节点上创建crash collector pod
    disable: true
  # 针对所有节点的存储设备选择和配置，各节点可以覆盖
  storage:
    # 是否将集群中所有节点作为Ceph节点。如果你使用nodes字段，必须设置为false
    useAllNodes: true
    # 按节点配置存储
    nodes:
      # 匹配kubernetes.io/hostname的节点名
      - name: xenial-100
        devices:
         - name: sdb
         - name: sdc1
        config:
          # 用于存放OSD元数据的设备名，使用低延迟设备例如SSD/NVMe可以提升性能
          metadataDevice:
          # OSD的底层存储格式
          storeType: bluestore
          # Bluestore WAL日志大小
          walSizeMB:
          # Bluestore 数据库的大小
          databaseSizeMB:
          # CRUSH设备类，如果不指定，Ceph会自动设置为hdd ssd nvme之一
          deviceClass:
          # 设置各设备运行OSD的数量，NVMe这样的高性能设备可以运行多个OSD
          osdsPerDevice**: 
          # 设置各设备是否使用dmcrypt加密
          encryptedDevice**: 
    # 是否自动将所有发现的裸设备/分区作为OSD
    useAllDevices: true
    # 参与OSD的设备，过滤表达式：
    # sdb      仅仅sdb
    # ^sd.     任何sd开头的设备
    # ^[^r]    任何不以r开头的设备
    # ^sd[a-d] sda sdb sdc sdd
    # ^s       任何s开头的设备
    deviceFilter: sdb
    # 参与OSD的设备
    devices:
        # sdb这样的设备名，或者udev的完整路径，例如/dev/disk/by-id/ata-ST4000DM004-XXXX
      - name:
        # 参见上面的config
        config:
    # 参见上面的config
    config:
  # 为各种Ceph组件添加的注解
  annotations:
    all:
    mgr:
    mon:
    osd:
  # 为各种Ceph组件添加的亲和性设置
  placement:
    mgr:
      nodeAffinity:
      podAffinity:
      podAntiAffinity:
      tolerations:
      topologySpreadConstraints:
    mon:
    osd:
    cleanup:
    all:
  # 为各种Ceph组件进行资源配额
  resources:
    mon: 
      requests:
        cpu:
        memory:
      limits:
    mgr: 
    osd: 
    mds: 
    prepareosd: 
    crashcollector:
  # 健康检查配置
  healthCheck:
    daemonHealth:
      mon:
        disabled: false
        interval: 45s
        timeout: 600s
      osd:
        disabled: false
        interval: 60s
      status:
        disabled: false
    livenessProbe:
      mon:
        disabled: false
      mgr:
        disabled: false
      osd:
        disabled: false

  # 各组件的优先级类别名
  priorityClassNames:
  # 删除CephCluster对象是，如何清理数据
  cleanupPolicy:
    # kubectl -n rook-ceph patch cephcluster rook-ceph --type merge \
    #   -p '{"spec":{"cleanupPolicy":{"confirmation":"yes-really-destroy-data"}}}'
    # 仅仅当你要删除CephCluster时，才设置该字段。可选空值或者yes-really-destroy-data
    #   空值：仅仅删除Ceph元数据，除非你手工清理主机，否则无法重新安装CephCluster
    #   yes-really-destroy-data：RookOperator回自动删除宿主机上的目录，清理宿主机上的块设备
    confirmation: yes-really-destroy-data
    # yes-really-destroy-data时如何清理磁盘
    sanitizeDisks:
      # 仅仅清理元数据，还是消毒整个磁盘
      method: quick | complete
      # 消毒磁盘使用的随机字节来源
      dataSource: zero | random
      # 反复消毒多次
      iteration: 1</pre>
<div class="blog_h3"><span class="graybg">使用外部Ceph集群</span></div>
<p>为了使用外部Ceph集群，Rook需要知道如何连接到Ceph。你可以通过脚本导入Rook需要的信息：</p>
<pre class="crayon-plain-tag"># 信息导入到什么命名空间
export NAMESPACE=rook-ceph-external
# 外部集群的FSID
export ROOK_EXTERNAL_FSID=3240b4aa-ddbc-42ee-98ba-4ea7b2a61514
# 外部集群的MON信息
export ROOK_EXTERNAL_CEPH_MON_DATA=a=172.17.0.4:6789
# 外部集群的管理Keyring
export ROOK_EXTERNAL_ADMIN_SECRET=AQC6Ylxdja+NDBAAB7qy9MEAr4VLLq4dCIvxtg==

# 执行导入
bash cluster/examples/kubernetes/ceph/import-external-cluster.sh</pre>
<p>然后，创建使用外部集群的CephCluster对象：</p>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph-external
  # 命名空间和上面对应
  namespace: rook-ceph-external
spec:
  external:
    enable: true
  crashCollector:
    disable: true
  # 可选的，使用外部ceph-mgr暴露的Prometheus Exporter
  monitoring:
    enabled: true
    rulesNamespace: rook-ceph
    externalMgrEndpoints:
      - ip: 192.168.39.182 </pre>
<div class="blog_h2"><span class="graybg">使用块存储</span></div>
<div class="blog_h3"><span class="graybg">示例 </span></div>
<pre class="crayon-plain-tag"># 定义一个块存储池
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  # 池的名字
  name: replicapool
  namespace: rook-ceph
spec:
  failureDomain: host
  replicated:
    size: 3

---

# 定义一个StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: rook-ceph-block
# 该SC的Provisioner标识，rook-ceph前缀即当前命名空间
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
    # clusterID 就是集群所在的命名空间名
    clusterID: rook-ceph
    # RBD镜像在哪个池中创建
    pool: replicapool
    # RBD镜像格式，默认2
    imageFormat: "2"
    # RBD镜像格式为2时，指定镜像特性，CSI RBD目前仅仅支持layering
    imageFeatures: layering
    # Ceph管理凭证配置
    csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
    csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
    csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
    csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
    csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
    csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph

    # 卷的文件系统类型，默认ext4，不建议xfs，因为存在潜在的死锁问题（超融合设置下卷被挂载到相同节点作为OSD时）
    # will set default as `ext4`. Note that `xfs` is not recommended due to potential deadlock
    csi.storage.k8s.io/fstype: ext4
# PVC被删除后，删除对应的RBD卷
reclaimPolicy: Delete</pre>
<div class="blog_h3"><span class="graybg">CephBlockPool</span></div>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  # 存储池的名称，会反映到底层Ceph
  name: replicapool
  namespace: rook-ceph
spec:
  # 每个数据副本必须跨越不同的故障域分布，如果设置为host，则保证每个副本在不同机器上
  failureDomain: host
  # 复制/纠删码，二选一
  replicated:
    # 副本数量
    size: 3
    # 如果需要设置副本数量为1，则必须将该字段置为false
    requireSafeReplicaSize: false
  erasureCoded:
    dataChunks: 2
    codingChunks: 1
  # CRUSH设备类别，说明该池中的数据可以存放在哪些设备上
  deviceClass: hdd
  # 该池使用的CRUSH map中的根节点
  crushRoot:
  # 通过启用动态OSD性能计数器，从而实现per-rbd的IO统计
  enableRBDStats:
  # 为存储池传递参数
  # 可用参数参考 https://docs.ceph.com/docs/master/rados/operations/pools/#set-pool-values
  parameters:
    # 给Ceph一个提示，说明该池期望消耗集群的最大多少容量
    target_size_ratio:
    # Bluestore压缩模式
    compression_mode: </pre>
<div class="blog_h2"><span class="graybg">使用文件系统</span></div>
<p>目前，对多文件系统的支持仍然处于试验阶段，需要通过Rook Operator的环境变量<pre class="crayon-plain-tag">ROOK_ALLOW_MULTIPLE_FILESYSTEMS</pre>开启。默认情况下，一个Ceph集群仅仅支持单个文件系统。</p>
<div class="blog_h3"><span class="graybg">示例</span></div>
<pre class="crayon-plain-tag"># 定义文件系统
apiVersion: ceph.rook.io/v1
kind: CephFilesystem
metadata:
  name: myfs
  namespace: rook-ceph
spec:
  # 元数据池配置
  metadataPool:
    replicated:
      size: 3
  # 数据池配置
  dataPools:
    - replicated:
        size: 3
  # 该对象删除后，Ceph存储池怎么办
  preservePoolsOnDelete: true
  # 元数据服务器一主一备
  metadataServer:
    activeCount: 1
    activeStandby: true

---

# 定义一个StorageClass

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rook-cephfs
#            注意前缀匹配Operator的命名空间
provisioner: rook-ceph.cephfs.csi.ceph.com
parameters:
  # Operator所在命名空间，就是Ceph集群的ID
  clusterID: rook-ceph

  # CephFS 文件系统名
  fsName: myfs

  # CephFS卷在哪个存储池中创建，如果provisionVolume: "true" 则此字段必须
  pool: myfs-data0

  # 可以使用既有的CephFS卷，需要指定卷的根路径
  # 如果 provisionVolume: "false" 则此字段必须
  rootPath: /absolute/path

  # 这些Ceph管理凭证由Operator自动生成
  csi.storage.k8s.io/provisioner-secret-name: rook-csi-cephfs-provisioner
  csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
  csi.storage.k8s.io/controller-expand-secret-name: rook-csi-cephfs-provisioner
  csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
  csi.storage.k8s.io/node-stage-secret-name: rook-csi-cephfs-node
  csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph

reclaimPolicy: Delete</pre>
<div class="blog_h3"><span class="graybg">关于PV配额</span></div>
<p>CephFS的CSI驱动使用Quotas来强制应用PVC声明的大小，仅仅4.17+内核才能支持CephFS quotas。</p>
<p>如果内核不支持，而且你需要配额管理，配置Operator环境变量<pre class="crayon-plain-tag">CSI_FORCE_CEPHFS_KERNEL_CLIENT: false</pre>来启用FUSE客户端。</p>
<p>使用FUSE客户端时，升级Ceph集群时应用Pod会断开mount，需要重启才能再次使用PV。</p>
<div class="blog_h3"><span class="graybg">CephFilesystem</span></div>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephFilesystem
metadata:
  name: myfs
  namespace: rook-ceph
spec:
  # 存放元数据的池的配置
  metadataPool:
    failureDomain: host
    replicated:
      size: 3
  # 存放数据的池的配置
  dataPools:
    - failureDomain: host
      replicated:
        size: 3
  # 删除该对象后，底层的存储池是否保留
  preservePoolsOnDelete: true
  # 元数据服务器配置
  metadataServer:
    # 主备元数据服务器数量
    activeCount: 1
    activeStandby: true
    # 注解
    annotations:
      key: value
    # 亲和性
    placement:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: role
              operator: In
              values:
              - mds-node
      tolerations:
      - key: mds-node
        operator: Exists
      podAffinity:
      podAntiAffinity:
      topologySpreadConstraints:
    # 配额
    resources:
      limits:
        cpu: "500m"
        memory: "1024Mi"
      requests:
        cpu: "500m"
        memory: "1024Mi" </pre>
<div class="blog_h2"><span class="graybg">使用对象存储 </span></div>
<div class="blog_h3"><span class="graybg">集群内对象存储</span></div>
<p>Rook支持在集群内部创建一个支持S3接口的RGW网关：</p>
<pre class="crayon-plain-tag"># 这个示例需要3个位于不同节点的Bluestore OSD
apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
  name: my-store
  namespace: rook-ceph
spec:
  metadataPool:
    failureDomain: host
    replicated:
      size: 3
  dataPool:
    # 故障域设置为host，则意味着三个副本必须位于不同节点，也就是说需要3个节点
    failureDomain: host
    # 使用纠删码方式，三副本中2个用作dataChunks，一个用于codingChunks
    erasureCoded:
      dataChunks: 2
      codingChunks: 1
  preservePoolsOnDelete: true
  gateway:
    type: s3
    sslCertificateRef:
    port: 80
    securePort:
    instances: 1
  healthCheck:
    bucket:
      disabled: false
      interval: 60s</pre>
<div class="blog_h3"><span class="graybg">使用外部Ceph集群</span></div>
<p>Rook也可以直接连接到外部的RGW网关：</p>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
  name: external-store
  namespace: rook-ceph
spec:
  gateway:
    port: 8080
    externalRgwEndpoints:
      - ip: 192.168.39.182
  healthCheck:
    bucket:
      enabled: true
      interval: 60s</pre>
<div class="blog_h3"><span class="graybg">创建Bucket</span></div>
<p>有了对象存储后，还需要创建Bucket，以便客户端读写数据。</p>
<p>你需要定义一个StorageClass，定义了基于此StorageClass创建的桶的回收策略、所依赖的对象网关：</p>
<pre class="crayon-plain-tag">apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
   name: rook-ceph-bucket
# rook-ceph前缀必须和命名空间对应
provisioner: rook-ceph.ceph.rook.io/bucket
# ObjectBucketClaim被删除后，删除RGW中的桶
reclaimPolicy: Delete
parameters:
  # 底层对象存储的信息
  objectStoreName: my-store
  objectStoreNamespace: rook-ceph
  region: us-east-1</pre>
<p>有了上述StorageClass后，就可以使用下面的CR来创建Bucket： </p>
<pre class="crayon-plain-tag">apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
  name: ceph-bucket
  namespace: default
spec:
  # 生成的桶的名称
  generateBucketName: ceph-bkt
  # 基于的StorageClass
  storageClassName: rook-ceph-bucket</pre>
<p>Rook Operator会创建名为ceph-bkt的桶，还会在ObjectBucketClaim所在命名空间下，创建和ObjectBucketClaim名字相同的：</p>
<ol>
<li>Configmap：包含Bucket的地址</li>
<li>Secret：包含访问Bucket所需要的凭证信息</li>
</ol>
<p>其中，Configmap内容如下：</p>
<pre class="crayon-plain-tag">apiVersion: v1
data:
  BUCKET_HOST: rook-ceph-rgw-rgw.rook-ceph
  BUCKET_NAME: tcnp-5aca27d8-f74f-4687-b979-e336f4203459
  BUCKET_PORT: "9000"
  BUCKET_REGION: ""
  BUCKET_SUBREGION: ""
kind: ConfigMap
metadata:
  name: ceph-bucket
  namespace: default</pre>
<p>Secret的内容如下：</p>
<pre class="crayon-plain-tag">apiVersion: v1
data:
  AWS_ACCESS_KEY_ID: RjZFRVUyTkUwWEZCWDRJVFgwWkE=
  AWS_SECRET_ACCESS_KEY: TUVPVU1panBFekM5emNQaDR2UkE3bHRwcFlaSnEzeGxFMmRmUFBQbw==
kind: Secret
metadata:
  name: ceph-bucket
  namespace: default
type: Opaque </pre>
<div class="blog_h3"><span class="graybg">创建用户</span></div>
<p>如果你希望创建独立的访问S3端点的用户，可以使用下面的CR：</p>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephObjectStoreUser
metadata:
  name: my-user
  namespace: rook-ceph
spec:
  # 对象存储的名字
  store: my-store
  displayName: "my display name"</pre>
<p>Access Key和Secret Key 会创建到相同命名空间。</p>
<div class="blog_h3"><span class="graybg">从集群外访问</span></div>
<p>将rook-ceph-rgw-**服务暴露出去即可。</p>
<div class="blog_h3"><span class="graybg">CephObjectStore</span></div>
<pre class="crayon-plain-tag">apiVersion: ceph.rook.io/v1
kind: CephObjectStore
metadata:
  # 对象存储的名字，影响相关Pod、其它资源的命名
  name: rgw
  namespace: rook-ceph
spec:
  # 用于对象存储元数据的池，必须使用replicated方式
  metadataPool:
    failureDomain: host
    replicated:
      size: 3
  # 用于对象存储数据的池，可以使用纠删码方式
  dataPool:
    failureDomain: host
    erasureCoded:
      dataChunks: 2
      codingChunks: 1
  # 当前对象删除时，用于支持对象存储的池，是否被删除
  preservePoolsOnDelete: true
  gateway:
    # 目前仅仅支持s3
    type: s3
    # 必须指定证书才能配置SSL，包含SSL证书的Secret名称
    sslCertificateRef:
    # RGW Pod的HTTP端口
    port: 80
    # RGW Pod的HTTPS端口
    securePort:
    # RGW Pod的数量
    instances: 1
    # 添加到RGW Pod的注解
    annotations:
      key: value
    # 亲和性配置
    placement:
      nodeAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
          nodeSelectorTerms:
          - matchExpressions:
            - key: role
              operator: In
              values:
              - rgw-node
      tolerations:
      - key: rgw-node
        operator: Exists
      podAffinity:
      podAntiAffinity:
      topologySpreadConstraints:
    # 资源配额
    resources:
      limits:
        cpu: "500m"
        memory: "1024Mi"
      requests:
        cpu: "500m"
        memory: "1024Mi"</pre>
<p>&nbsp;</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">rook-ceph-osd无法启动</span></div>
<div class="blog_h3"><span class="graybg">failed to configure devices</span></div>
<p>报错信息：failed to configure devices. failed to config osd 65. failed format/partition of osd 65. failed to partion device vdc. failed to partition /dev/vdc. Failed to complete partition vdc: exit status 4 </p>
<p>报错原因：在我的环境下是节点（本身是KVM虚拟机）的虚拟磁盘太小导致，分配32G的磁盘没有问题，20G则出现上述报错</p>
<div class="blog_h3"><span class="graybg">activate容器失败</span></div>
<p>重启后出现，原因是逻辑卷没有激活，lsblk看到的块设备列表中没有Ceph管理的逻辑卷。执行：</p>
<pre class="crayon-plain-tag"># yum install lvm2

modprobe dm-mod
# 扫描可用的卷组
vgscan
# 激活逻辑卷
vgchange -ay</pre>
<p>进行激活，然后可以在/dev/mapper、/dev/VGNAME下看到块设备了，OSD也可以启动了。</p>
<div class="blog_h3"><span class="graybg">已存在文件系统</span></div>
<p>如果块设备上已经有了文件系统，则无法prepare OSD。这个在重装Rook的情况下容易出现。需要手工清除磁盘：</p>
<pre class="crayon-plain-tag">wipefs --all  /dev/vdb
dmsetup remove  ceph--164d13ee--d820--4da3--ac66--d1b2d83d8a28-osd--data--c29cedc6--f8ce--4e4b--b88b--4c7272866180

# 或者更优雅的
vgremove ceph-1fd08944-b4e0-4839-82a9-62ecc6f5861e
wipefs --all /dev/vdb</pre>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/rook-based-k8s-storage-solution">基于Rook的Kubernetes存储方案</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/rook-based-k8s-storage-solution/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
