Ceph 存储集群7

时间:2022-10-17 21:44:27

Ceph 仍在积极开发中,所以你可能碰到一些问题,需要评估 Ceph 配置文件、并修改日志和调试选项来纠正它。

 

 

一、日志记录和调试

般来说,你应该在运行时增加调试选项来调试问题;也可以把调试选项添加到 Ceph 配置文件里来调试启动问题,然后查看 /var/log/ceph (默认位置)下的日志文件。

注意:调试输出会拖慢系统,这种延时有可能掩盖竞争条件

 

日志记录是资源密集任务。如果你碰到的问题在集群的某个特定区域,只启用那个区域对应的日志功能即可。例如,你的 OSD 运行良好、元数据服务器却不行,这时应该先打开那个可疑元数据服务器例程的调试日志;如果不行再打开各子系统的日志。

 

 

注意:详尽的日志每小时可能超过 1GB ,如果你的系统盘满了,这个节点就会停止工作。

 

如果你要打开或增加 Ceph 日志级别,确保系统盘空间足够。滚动日志文件的方法见加快日志更迭。集群稳定运行后,可以关闭不必要的调试选项以更好地运行。在运营中记录调试输出会拖慢系统、且浪费资源。

 

运行时

 

如果你想查看一进程的运行时配置,必须先登录对应主机,然后执行命令:

ceph daemon {daemon-name} config show | less

例如:

ceph daemon osd.0 config show | less

 

要在运行时激活 Ceph 的调试输出(即 dout() ),用 ceph tell 命令把参数注入运行时配置:

 

ceph tell {daemon-type}.{daemon id or *} injectargs --{name} {value} [--{name} {value}]

 

osdmonmds 替代 {daemon-type} 。你可以用星号( * )把配置应用到同类型的所有守护进程,或者指定具体守护进程的 ID 。例如,要给名为 ods.0ceph-osd 守护进程提高调试级别,用下列命令:

ceph tell osd.0 injectargs --debug-osd 0/5

 

ceph tell 命令会贯穿所有监视器。如果你不能绑定监视器,还可以登录你要改的那台主机用 ceph daemon 来更改。例如:

sudo ceph daemon osd.0 config set debug_osd 0/5

 

 

启动时

 

要在启动时激活调试输出( dout() ),你得把选项加入配置文件。各进程共有配置可写在配置文件的 [global] 下,某类进程的配置可写在守护进程段下( [mon][osd][mds] )。例如:

 

[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

 

 

加快日志更迭

 

如果你的系统盘比较满,可以修改 /etc/logrotate.d/ceph 内的日志滚动配置以加快滚动。在滚动频率后增加一个尺寸选项(达到此尺寸就滚动)来加快滚动(通过 cronjob )。例如默认配置大致如此:

rotate 7
weekly
compress
sharedscripts

 

增加一个 size 选项。

rotate 7
weekly
size 500M
compress
sharedscripts

 

然后,打开 crontab 编辑器。

crontab -e

 

最后,增加一条用以检查 /etc/logrorate.d/ceph 文件。

 

30 * * * * /usr/sbin/logrotate /etc/logrotate.d/ceph >/dev/null 2>&1

 

本例中每 30 分钟检查一次 /etc/logrorate.d/ceph 文件。

 

Valgrind

你也许还得追踪内存和线程问题,可以在 Valgrind 中运行一个守护进程、一类进程、或整个集群。 Valgrind 是计算密集型程序,应该只用于开发或调试,否则会拖慢系统。其消息记录到 stderr

 

子系统、日志和调试选项

 

大多数情况下你可以通过子系统打开调试。

 

Ceph 子系统概览

各子系统都有日志级别用于分别控制其输出日志、和暂存日志,你可以分别为这些子系统设置不同的记录级别。 Ceph 的日志级别从 1201 是简洁、 20 是详尽。通常,内存驻留日志不会发送到输出日志,除非:

 

致命信号冒出来了,或者
源码中的 assert 被触发,或者
明确要求发送。

 

调试选项允许用单个数字同时设置日志级别和内存级别,会设置为一样。比如,如果你指定 debug ms = 5 , Ceph 会把日志级别和内存级别都设置为 5 。也可以分别设置,第一个选项是日志级别、后一个是内存级别,二者必须用斜线( / )分隔。假如你想把 ms 子系统的调试日志级别设为 1 、内存级别设为 5 ,可以写为 debug ms = 1/5 ,如下:

debug {subsystem} = {log-level}/{memory-level}
#
for example
debug mds log
= 1/20

下表列出了 Ceph 子系统及其默认日志和内存级别。一旦你完成调试,应该恢复默认值、或一个适合平常运营的级别。

子系统                         日志级别     内存日志级别
default 0 5
lockdep
0 5
context
0 5
crush
1 5
mds
1 5
mds balancer
1 5
mds locker
1 5
mds log
1 5
mds log expire
1 5
mds migrator
1 5
buffer
0 0
timer
0 5
filer
0 5
objecter
0 0
rados
0 5
rbd
0 5
journaler
0 5
objectcacher
0 5
client
0 5
osd
0 5
optracker
0 5
objclass
0 5
filestore
1 5
journal
1 5
ms
0 5
mon
1 5
monc
0 5
paxos
0 5
tp
0 5
auth
1 5
finisher
1 5
heartbeatmap
1 5
perfcounter
1 5
rgw
1 5
javaclient
1 5
asok
1 5
throttle
1 5

 

 

日志记录选项

 

日志和调试选项不是必需配置,但你可以按需覆盖默认值。 Ceph 支持如下配置:

 

log file
描述: 集群日志文件的位置。
类型: String
是否必需: No
默认值:
/var/log/ceph/$cluster-$name.log

log max
new
描述: 新日志文件的最大数量。
类型: Integer
是否必需: No
默认值:
1000

log max recent
描述: 一个日志文件包含的最新事件的最大数量。
类型: Integer
是否必需: No
默认值:
1000000

log to stderr
描述: 设置日志消息是否输出到标准错误( stderr )。
类型: Boolean
是否必需: No
默认值:
true

err to stderr
描述: 设置错误消息是否输出到标准错误( stderr )。
类型: Boolean
是否必需: No
默认值:
true

log to syslog
描述: 设置日志消息是否输出到 syslog 。
类型: Boolean
是否必需: No
默认值:
false

err to syslog
描述: 设置错误消息是否输出到 syslog 。
类型: Boolean
是否必需: No
默认值:
false

log flush on exit
描述: 设置 Ceph 退出后是否回写日志文件。
类型: Boolean
是否必需: No
默认值:
true

clog to monitors
描述: 设置是否把 clog 消息发送给监视器。
类型: Boolean
是否必需: No
默认值:
true

clog to syslog
描述: 设置是否把 clog 输出到 syslog 。
类型: Boolean
是否必需: No
默认值:
false

mon cluster log to syslog
描述: 设置集群日志是否输出到 syslog 。
类型: Boolean
是否必需: No
默认值:
false

mon cluster log file
描述: 集群日志位置。
类型: String
是否必需: No
默认值:
/var/log/ceph/$cluster.log

 

 

 

OSD

 

osd debug drop ping probability
描述:
?
类型: Double
是否必需: No
默认值:
0

osd debug drop ping duration
描述:
类型: Integer
是否必需: No
默认值:
0

osd debug drop pg create probability
描述:
类型: Integer
是否必需: No
默认值:
0

osd debug drop pg create duration
描述:
?
类型: Double
是否必需: No
默认值:
1

osd preserve trimmed log
描述: 裁减后保留剩余日志。
类型: Boolean
是否必需: No
默认值:
false

osd tmapput sets uses tmap
描述: 使用 tmap ,仅用于调试。
类型: Boolean
是否必需: No
默认值:
false

osd min pg log entries
描述: 归置组日志最小条数。
类型:
32-bit Unsigned Integer
是否必需: No
默认值:
1000

osd op log threshold
描述: 一次发送多少操作日志消息。
类型: Integer
是否必需: No
默认值:
5
Filestore

filestore debug omap check
描述: 调试同步检查,这是昂贵的操作。
类型: Boolean
是否必需: No
默认值:
0

 

 

MDS

 

mds debug scatterstat
描述: Ceph 将把各种回归状态常量设置为真(谨为开发者)。
类型: Boolean
是否必需: No
默认值:
false

mds debug frag
描述: Ceph 将在方便时校验目录碎片(谨为开发者)。
类型: Boolean
是否必需: No
默认值:
false

mds debug auth pins
描述: debug auth pin 开关(谨为开发者)。
类型: Boolean
是否必需: No
默认值:
false

mds debug subtrees
描述: debug subtree 开关(谨为开发者)。
类型: Boolean
是否必需: No
默认值:
false

 

 

RADOS 网关

 

rgw log nonexistent bucket
描述: 记录不存在的桶?
类型: Boolean
是否必需: No
默认值:
false

rgw log
object name
描述: 是否记录对象名称。注:关于格式参考 man date ,子集也支持。
类型: String
是否必需: No
默认值:
%Y-%m-%d-%H-%i-%n

rgw log
object name utc
描述: 对象日志名称包含 UTC ?
类型: Boolean
是否必需: No
默认值:
false

rgw enable ops log
描述: 允许记录 RGW 的每一个操作。
类型: Boolean
是否必需: No
默认值:
true

rgw enable usage log
描述: 允许记录 RGW 的带宽使用。
类型: Boolean
是否必需: No
默认值:
true

rgw usage log flush threshold
描述: 回写未决的日志数据阀值。
类型: Integer
是否必需: No
默认值:
1024

rgw usage log tick interval
描述: 每隔 s 回写一次未决日志。
类型: Integer
是否必需: No
默认值:
30

rgw intent log
object name
描述:
类型: String
是否必需: No
默认值:
%Y-%m-%d-%i-%n

rgw intent log
object name utc
描述: 日志对象名字里包含 UTC 时间戳。
类型: Boolean
是否必需: No
默认值:
false

 

二、监视器故障排除

 

当集群遇到与监视器相关的问题时, 会有恐慌的倾向, 有时还有很好的理由。你应该记住, 失去一个显示器, 或一堆, 并不一定意味着你的集群是下降, 只要大多数是, 运行和形成的法定人数。

 

开始排障

 

监视器在运行吗?

首先, 我们需要确保监视器正在运行。你会惊讶于人们经常忘记运行监视器, 或者在升级后重新启动它们。这并不可耻, 但是让我们试着不要失去几个小时去追逐一个不存在的问题。

 

ceph -s 是否能运行并收到集群回复?

如果答案是肯定的,那么你的集群已启动并运行着。你可以想当然地认为如果已经形成法定人数,监视器们就只会响应 status 请求。

 

但是, 如果 ceph 被阻止, 而没有从群集得到答复或显示大量的错误消息, 那么很可能您的监视器要么完全关闭, 要么只是部分上升--这部分不足以形成仲裁 (请记住, 如果 f由大多数显示器 ormed)。

 

 

使用监视器的管理套接字

通过管理套接字,你可以用 Unix 套接字文件直接与指定守护进程交互。这个文件位于你监视器的 run 目录下,默认配置时它位于 /var/run/ceph/ceph-mon.ID.asok ,但你要是改过就不一定在那里了。如果你在那里没找到它,请看看 ceph.conf 里是否配置了其它路径、或者用下面的命令获取:

ceph-conf --name mon.ID --show-config-value admin_socket

 

请牢记,只有在监视器运行时管理套接字才可用。监视器正常关闭时,管理套接字会被删除;如果监视器不运行了、但管理套接字还存在,就说明监视器不是正常关闭的。不管怎样,监视器没在运行,你就不能使用管理套接字, ceph 命令会返回类似 Error 111: Connection Refused 的错误消息。

访问管理套接字很简单,就是让 ceph 工具使用 asok 文件。对于 Dumpling 之前的版本,命令是这样的:

 

ceph --admin-daemon /var/run/ceph/ceph-mon.<id>.asok <command>

 

对于 Dumpling 及后续版本,你可以用另一个(推荐的)命令:

 

ceph daemon mon.<id> <command>

 

ceph 工具的 help 命令会显示管理套接字支持的其它命令。请仔细了解一下 config getconfig showmon_statusquorum_status 命令,在排除监视器故障时它们能给你些启发。

 

 

理解 mon_status

 

mon_status 可以通过 ceph 工具获得, 当您有一个形成的法定人数, 或通过管理插座, 如果你不。此命令将输出有关监视器的大量信息, 包括与 quorum_status 相同的输出。

 

以下 mon_status 为例:

 

 

{ "name": "c",
"rank": 2,
"state": "peon",
"election_epoch": 38,
"quorum": [
1,
2],
"outside_quorum": [],
"extra_probe_peers": [],
"sync_provider": [],
"monmap": { "epoch": 3,
"fsid": "5c4e9d53-e2e1-478a-8061-f543f8be4cf8",
"modified": "2013-10-30 04:12:01.945629",
"created": "2013-10-29 14:14:41.914786",
"mons": [
{
"rank": 0,
"name": "a",
"addr": "127.0.0.1:6789\/0"},
{
"rank": 1,
"name": "b",
"addr": "127.0.0.1:6790\/0"},
{
"rank": 2,
"name": "c",
"addr": "127.0.0.1:6795\/0"}]}}

 

 

最常见的监视器问题

 

有仲裁, 但至少有一个显示器关闭

当这一切发生的时候,取决于运行的ceph版本。你应该看到类似的东西

$ ceph health detail
[snip]
mon.a (rank
0) addr 127.0.0.1:6789/0 is down (out of quorum)

 

 

 

三、OSD 故障排除

进行 OSD 排障前,先检查一下监视器集群和网络。如果 ceph healthceph -s 返回的是健康状态,这意味着监视器们形成了法定人数。如果你还没监视器法定人数、或者监视器状态错误,要先解决监视器问题。核实下你的网络,确保它在正常运行,因为网络对 OSD 的运行和性能有显著影响。

 

收集 OSD 数据

 

开始 OSD 排障的第一步最好先收集信息,另外还有监控 OSD 时收集的,如 ceph  osd tree

 

Ceph 日志

 

如果你没改默认路径,可以在 /var/log/ceph 下找到日志:

 

ls /var/log/ceph

如果看到的日志还不够详细,可以增大日志级别。请参考日志记录和调试,看看如何保证看到大量日志时又不影响集群运行。

 

管理套接字

用管理套接字检索运行时信息。所有 Ceph 套接字列表:

ls /var/run/ceph

 

然后,执行下例命令显示可用选项,把 {daemon-name} 换成实际的守护进程(如 osd.0 ):

ceph daemon osd.0 help

 

另外,你也可以指定一个 {socket-file} (如 /var/run/ceph 下的文件):

 

ceph daemon {socket-file} help

 

和其它手段相比,管理接口允许你:

 

    在运行时列出配置
列出历史操作
列出操作的优先队列状态
列出在进行的操作
列出性能计数器

 

显示剩余空间

 

可能是文件系统问题,用 df 显示文件系统剩余空间。

 

df -h

 

 

I/O 统计信息

 

iostat 定位 I/O 相关问题。

 

iostat -x

 

诊断消息

要查看诊断信息,配合 lessmoregreptail 使用 dmesg ,例如:

dmesg | grep scsi

 

停止自动重均衡

你得周期性地维护集群的子系统、或解决某个失败域的问题(如一机架)。如果你不想在停机维护 OSD 时让 CRUSH 自动重均衡,提前设置 noout

 

ceph osd set noout

 

在集群上设置 noout 后,你就可以停机维护失败域内的 OSD 了。

stop ceph-osd id={num}

 

注意:在定位同一故障域内的问题时,停机 OSD 内的归置组状态会变为 degraded

维护结束后,重启OSD。

 

start ceph-osd id={num}

 

最后,解除 noout 标志。

ceph osd unset noout

 

OSD 没运行

 

通常情况下,简单地重启 ceph-osd 进程就可以重回集群并恢复。

 

OSD 起不来

 

如果你重启了集群,但其中一个 OSD 起不来,依次检查:

 

配置文件: 如果你新装的 OSD 不能启动,检查下配置文件,确保它合爻性(比如 host 而非 hostname ,等等);

检查路径: 检查你配置的路径,以及它们自己的数据和日志路径。如果你分离了 OSD 数据和日志、而配置文件和实际挂载点存在出入,启动 OSD 时就可能遇挫。如果你想把日志存储于一个块设备,应该为日志硬盘分区并为各 OSD 分别分配一分区。

检查最大线程数: 如果你的节点有很多 OSD ,也许就会触碰到默认的最大线程数限制(如通常是 32k 个),尤其是在恢复期间。你可以用 sysctl 增大线程数,把最大线程数更改为支持的最大值(即
4194303 ),看看是否有用。例如:
sysctl -w kernel.pid_max=4194303

如果增大最大线程数解决了这个问题,你可以把此配置 kernel.pid_max 写入配置文件 /etc/sysctl.conf,使之永久生效,例如:

kernel.pid_max = 4194303
内核版本: 确认你在用的内核版本以及所用的发布版。 Ceph 默认依赖一些第三方工具,这些工具可能有缺陷或者与特定发布版和/或内核版本冲突(如 Google perftool )。检查下操作系统推荐确保你已经解决了内核相关的问题。

段错误: 如果有了段错误,提高日志级别(如果还没提高),再试试。如果重现了,联系 ceph-devel 并提供你的配置文件、显示器输出和日志文件内容。

 

如果问题仍未解决,电子邮件也无用,你可以联系 Inktank 寻求帮助

 

OSD 失败

 

ceph-osd 挂掉时,监视器可通过活着的 ceph-osd 了解到此情况,且通过 ceph health 命令报告:

ceph health
HEALTH_WARN
1/3 in osds are down

 

而且,有 ceph-osd 进程标记为 indown 的时候,你会得到警告,你可以用下面的命令得知哪个 ceph-osd 进程挂了:

ceph health detail
HEALTH_WARN
1/3 in osds are down
osd.
0 is down since epoch 23, last address 192.168.106.220:6800/11080

 

如果有个硬盘失败或其它错误使 ceph-osd 不能正常运行或重启,一条错误信息将会出现在日志文件 /var/log/ceph/ 里。

如果守护进程因心跳失败、或者底层文件系统无响应而停止,查看 dmesg 获取硬盘或者内核错误。

如果是软件错误(失败的插入或其它意外错误),就应该回馈到 ceph-devel 邮件列表。

 

 

硬盘没剩余空间

 

Ceph 不允许你向满的 OSD 写入数据,以免丢失数据。在运营着的集群中,你应该能收到集群空间将满的警告。 mon osd full ratio 默认为 0.95 、或达到 95% 时它将阻止客户端写入数据。 mon osd nearfull ratio 默认为 0.85 、也就是说达到容量的 85% 时它会产生健康警告。

满载集群问题一般产生于测试 Ceph 在小型集群上如何处理 OSD 失败时。当某一节点利用率较高时,集群能够很快掩盖将满和占满率。如果你在测试小型集群上的 Ceph 如何应对 OSD 失败,应该保留足够的空间,然后试着临时降低 mon osd full ratiomon osd nearfull ratio 值。

ceph health 会显示将满的 ceph-osds

ceph health
HEALTH_WARN
1 nearfull osds
osd.
2 is near full at 85%

 

或者:

ceph health
HEALTH_ERR
1 nearfull osds, 1 full osds
osd.
2 is near full at 85%
osd.
3 is full at 97%

 

处理这种情况的最好方法就是增加新的 ceph-osd ,这允许集群把数据重分布到新 OSD 里。

如果因满载而导致 OSD 不能启动,你可以试着删除那个 OSD 上的一些归置组数据目录。

 

注意:如果你准备从填满的 OSD 中删除某个归置组,注意不要删除另一个OSD 上的同名归置组,否则你会丢数据必须在多个 OSD 上保留至少一份数据副本。

 

OSD 龟速或无响应

 

一个反复出现的问题是龟速或无响应。在深入性能问题前,你应该先确保不是其他故障。例如,确保你的网络运行正常、且 OSD 在运行,还要检查 OSD 是否被恢复流量拖住了。

 

注意:较新版本的 Ceph 能更好地处理恢复,可防止恢复进程耗尽系统资源而导致 upin 的 OSD 不可用或响应慢。

 

网络问题

 

Ceph 是一个分布式存储系统,所以它依赖于网络来互联 OSD 们、复制对象、恢复错误、和检查心跳。网络问题会导致 OSD 延时和打摆子,详情参见打摆子的 OSD

确保 Ceph 进程和 Ceph 依赖的进程连接了、和/或在监听。

 

netstat -a | grep ceph
netstat
-l | grep ceph
sudo netstat
-p | grep ceph

 

检查网络统计信息。

 

netstat -s

 

 

驱动器配置

一个存储驱动器应该只用于一个 OSD 。如果有其它进程共享驱动器,顺序读和顺序写吞吐量会成为瓶颈,包括日志记录、操作系统、监视器、其它 OSD 和非 Ceph 进程。

Ceph 在日志记录完成之后才会确认写操作,所以使用 ext4 或 XFS 文件系统时高速的 SSD 对降低响应延时很有吸引力。相反, btrfs 文件系统可以同时读写。

注意:给驱动器分区并不能改变总吞吐量或顺序读写限制。把日志分离到单独的分区可能有帮助,但最好是另外一块硬盘的分区。

 

坏扇区和碎片化硬盘

检修下硬盘是否有坏扇区和碎片。这会导致总吞吐量急剧下降。

 

监视器和 OSD 蜗居

监视器是普通的轻量级进程,但它们会频繁调用 fsync() ,这会妨碍其它工作量,特别是监视器和 OSD 共享驱动器时。另外,如果你在 OSD 主机上同时运行监视器,遭遇的性能问题可能和这些相关:

    运行较老的内核(低于3.0
v0.
48 版运行在老的 glibc 之上
运行的内核不支持 syncfs(
2) 系统调用

 

在这些情况下,同一主机上的多个 OSD 会相互拖垮对方。它们经常导致爆炸式写入。

 

 

 

进程蜗居

共存于同一套硬件、并向 Ceph 写入数据的进程(像基于云的解决方案、虚拟机和其他应用程序)会导致 OSD 延时大增。一般来说,我们建议用一主机跑 Ceph 、其它主机跑其它进程,实践证明把 Ceph 和其他应用程序分开可提高性能、并简化故障排除和维护。

日志记录级别

如果你为追踪某问题提高过日志级别、但结束后忘了调回去,这个 OSD 将向硬盘写入大量日志。如果你想始终保持高日志级别,可以考虑给默认日志路径挂载个硬盘,即 /var/log/ceph/$cluster-$name.log

恢复节流

根据你的配置, Ceph 可以降低恢复速度来维持性能,否则它会不顾 OSD 性能而加快恢复速度。检查下 OSD 是否正在恢复。

内核版本

检查下你在用的内核版本。较老的内核也许没有移植能提高 Ceph 性能的功能。

内核与 SyncFS 问题

试试在一主机上只运行一个 OSD ,看看能否提升性能。老内核未必支持有 syncfs(2) 系统调用的 glibc

文件系统问题

当前,我们推荐基于 xfs 或 ext4 部署集群。 btrfs 有很多诱人的功能,但文件系统内的缺陷可能导致性能问题。

内存不足

我们建议为每 OSD 进程规划 1GB 内存。你也许注意到了,通常情况下 OSD 仅会用一小部分(如 100-200MB )。你也许想用这些空闲内存跑一些其他应用,如虚拟机等等,然而当 OSD 进入恢复状态时,其内存利用率激增,如果没有可用内存,此 OSD 的性能将差的多。

old requests 或 slow requests

如果某 ceph-osd 守护进程对一请求响应很慢,它会生成日志消息来抱怨请求耗费的时间过长。默认警告阀值是 30 秒,用 osd op complaint time 选项来配置。这种情况发生时,集群日志系统会收到这些消息。

很老的版本抱怨 “old requests” :

osd.0 192.168.106.220:6800/18813 312 : [WRN] old request osd_op(client.5099.0:790 fatty_26485_object789 [write 0~4096] 2.5e54f643) v4 received at 2012-03-06 15:42:56.054801 currently waiting for sub ops

 

较新版本的 Ceph 抱怨 “slow requests” :

 

{date} {osd.num} [WRN] 1 slow requests, 1 included below; oldest blocked for > 30.005692 secs
{date} {osd.num} [WRN] slow request
30.005692 seconds old, received at {date-time}: osd_op(client.4240.0:8 benchmark_data_ceph-1_39426_object7 [write 0~4194304] 0.69848840) v4 currently waiting for subops from [610]

 

可能起因有:

 

    坏驱动器(查看 dmesg 输出);
内核文件系统缺陷(查看 dmesg 输出);
集群过载(检查系统负载、 iostat 等等);
ceph
-osd 守护进程缺陷。

 

可能的解决方法:

    从 Ceph 主机去除 VM 云解决方案;
升级内核;
升级 Ceph ;
重启 OSD 。

 

打摆子的 OSD

 

我们建议同时部署公网(前端)和集群网(后端),这样能更好地满足对象复制的容量需求。另一个优点是你可以运营一个不连接互联网的集群,以此避免拒绝估计。 OSD 们互联和检查心跳时会优选集群网(后端),详情见监视器与 OSD 的交互

然而,如果集群网(后端)失败、或出现了明显的延时,同时公网(前端)却运行良好, OSD 现在不能很好地处理这种情况。这时 OSD 们会向监视器报告邻居 down 了、同时报告自己是 up 的,我们把这种情形称为打摆子( flapping )。

如果有东西导致 OSD 打摆子(反复地被标记为 down ,然后又 up ),你可以强制监视器停止:

ceph osd set noup      # prevent OSDs from getting marked up
ceph osd
set nodown # prevent OSDs from getting marked down

 

这些标记记录在 osdmap 数据结构里:

ceph osd dump | grep flags
flags no
-up,no-down

 

下列命令可清除标记:

ceph osd unset noup
ceph osd unset nodown

 

mon osd down out interval is). 还支持其它两个标记 noinnoout ,它们分别可防止正在启动的 OSD 被标记为 in 、或被误标记为 out (不管 `` mon osd down out interval`` 的值是什么)。

 

注意:noupnooutnodown 从某种意义上说是临时的,一旦标记清除了,它们被阻塞的动作短时间内就会发生;相反, noin 标记阻止 OSD 启动后进入集群,但其它守护进程都维持原样。

 

四、归置组排障

 

归置组总不整洁

 

当您创建群集并且您的群集处于active, active+remapped or active+degraded状态并且从未达到active+clean状态时, 您的配置可能会有问题

 

一个节点群集

Ceph 不再提供在单个节点上操作的文档, 因为您永远不会在单个节点上部署专为分布式计算设计的系统。

此外, 在包含 Ceph 守护进程的单个节点上安装客户端内核模块可能会由于 Linux 内核本身的问题而导致死锁 (除非您为客户端使用 vm)。

 

您可以在1节点的配置中进行 Ceph 实验,

 

如果您试图在单个节点上创建群集, 则必须在创建监视器和osd之前,在 Ceph 配置文件中选择 "从 1 (意味着主机或节点) 到 0 (意味着 osd)" 中的 "从您的默认值" 中选取 "叶类型" 设置。

 

如果使用单个磁盘创建 OSD, 则必须首先手动创建数据目录。例如:
mkdir /var/local/osd0 /var/local/osd1
ceph
-deploy osd prepare {localhost-name}:/var/local/osd0 {localhost-name}:/var/local/osd1
ceph
-deploy osd activate {localhost-name}:/var/local/osd0 {localhost-name}:/var/local/osd1

 

比复制副本更少的 OSD

 

如果您已经将两个 osd 设置为 in 和 up, 但仍未看到 active + clean放置组, 则可能是 osd 池的默认大小设置为大于2。

有几种方法可以解决这种情况。如果要在具有两个副本的active + degraded状态下操作群集, 可以将 osd pool default min size设置为 2, 以便可以在active + degraded状态下编写对象。

 

您也可以将 osd pool default size 设置为 2, 以便您只有两个存储的副本 (原件和一个副本), 在这种情况下, 群集应该实现一个active + clean的状态

 

注意:您可以在运行时进行更改。如果您在 Ceph 配置文件中进行了更改, 则可能需要重新启动群集。

 

Pool Size = 1

 

如果将 osd pool default size设置为 1, 则只会有一个该对象的副本。osd 依赖于其他 osd 来告诉他们应该拥有哪些对象。

如果第一个 osd 有一个对象的副本, 并且没有第二个拷贝, 那么没有第二个 osd 可以告诉第一个 osd 它应该有该副本。

对于映射到第一个 osd 的每个放置组 (请参见 ceph pg 转储), 您可以强制第一个 osd 通过运行来注意它需要的放置组:

 

ceph pg force_create_pg <pgid>

 

CRUSH 图错误

另一个候选的安置组仍然不洁涉及在你的CRUSH map的错误。

 

卡住的归置组

有失败时归置组会进入“degraded”(降级)或“peering”(连接建立中)状态,这事时有发生,通常这些状态意味着正常的失败恢复正在进行。然而,如果一个归置组长时间处于某个这些状态就意味着有更大的问题,因此监视器在归置组卡 (stuck) 在非最优状态时会警告。我们具体检查:

    inactive (不活跃)——归置组长时间无活跃(即它不能提供读写服务了);
unclean (不干净)——归置组长时间不干净(例如它未能从前面的失败完全恢复);
stale (不新鲜)——归置组状态没有被 ceph
-osd 更新,表明存储这个归置组的所有节点可能都挂了。

 

你可以摆出卡住的归置组:

 

 

ceph pg dump_stuck stale
ceph pg dump_stuck inactive
ceph pg dump_stuck unclean

 

卡在 stale 状态的归置组通过修复 ceph-osd 进程通常可以修复;卡在 inactive 状态的归置组通常是互联问题(参见 归置组挂了——互联失败 );卡在 unclean 状态的归置组通常是由于某些原因阻止了恢复的完成,像未找到的对象(参见 未找到的对象 )。

 

归置组挂了——互联失败

在某些情况下, ceph-osd 连接建立进程会遇到问题,使 PG 不能活跃、可用,例如 ceph health 也许显示:

 

ceph health detail
HEALTH_ERR
7 pgs degraded; 12 pgs down; 12 pgs peering; 1 pgs recovering; 6 pgs stuck unclean; 114/3300 degraded (3.455%); 1/3 in osds are down
...
pg
0.5 is down+peering
pg
1.4 is down+peering
...
osd.
1 is down since epoch 69, last address 192.168.106.220:6801/8651

 

可以查询到 PG 为何被标记为 down

 

ceph pg 0.5 query
{
"state": "down+peering",
...
"recovery_state": [
{
"name": "Started\/Primary\/Peering\/GetInfo",
"enter_time": "2012-03-06 14:40:16.169679",
"requested_info_from": []},
{
"name": "Started\/Primary\/Peering",
"enter_time": "2012-03-06 14:40:16.169659",
"probing_osds": [
0,
1],
"blocked": "peering is blocked due to down osds",
"down_osds_we_would_probe": [
1],
"peering_blocked_by": [
{
"osd": 1,
"current_lost_at": 0,
"comment": "starting or marking this osd lost may let us proceed"}]},
{
"name": "Started",
"enter_time": "2012-03-06 14:40:16.169513"}
]
}

 

recovery_state 段告诉我们连接建立因 ceph-osd 进程挂了而被阻塞,本例是 osd.1 挂了,启动这个进程应该就可以恢复。

另外,如果 osd.1 是灾难性的失败(如硬盘损坏),我们可以告诉集群它丢失( lost )了,让集群尽力完成副本拷贝。

 

注意:集群不能保证其它数据副本是一致且最新就危险了!

 

让 Ceph 无论如何都继续:

ceph osd lost 1

恢复将继续

 

未找到的对象

 

某几种失败相组合可能导致 Ceph 抱怨有找不到( unfound )的对象:

 

ceph health detail
HEALTH_WARN
1 pgs degraded; 78/3778 unfound (2.065%)
pg
2.4 is active+degraded, 78 unfound

 

这意味着存储集群知道一些对象(或者存在对象的较新副本)存在,却没有找到它们的副本。下例展示了这种情况是如何发生的,一个 PG 的数据存储在 ceph-osd 1 和 2 上:

 

    1 挂了;
2 独自处理一些写动作;
1 起来了;
12 重新互联, 1 上面丢失的对象加入队列准备恢复;
新对象还未拷贝完,
2 挂了。

 

 

这时, 1 知道这些对象存在,但是活着的 ceph-osd 都没有副本,这种情况下,读写这些对象的 IO 就会被阻塞,集群只能指望节点早点恢复。这时我们假设用户希望先得到一个 IO 错误。

首先,你应该确认哪些对象找不到了:

ceph pg 2.4 list_missing [starting offset, in json]
{ "offset": { "oid": "",
"key": "",
"snapid": 0,
"hash": 0,
"max": 0},
"num_missing": 0,
"num_unfound": 0,
"objects": [
{ "oid": "object 1",
"key": "",
"hash": 0,
"max": 0 },
...
],
"more": 0}

 

如果在一次查询里列出的对象太多, more 这个字段将为 true ,因此你可以查询更多。(命令行工具可能隐藏了,但这里没有)

其次,你可以找出哪些 OSD 上探测到、或可能包含数据:

 

ceph pg 2.4 query
"recovery_state": [
{ "name": "Started\/Primary\/Active",
"enter_time": "2012-03-06 15:15:46.713212",
"might_have_unfound": [
{ "osd": 1,
"status": "osd is down"}]},

 

 

本例中,集群知道 osd.1 可能有数据,但它挂了( down )。所有可能的状态有:

 

    已经探测到了
在查询
OSD 挂了
尚未查询

 

有时候集群要花一些时间来查询可能的位置。

还有一种可能性,对象存在于其它位置却未被列出,例如,集群里的一个 ceph-osd 停止且被剔出,然后完全恢复了;后来的失败、恢复后仍有未找到的对象,它也不会觉得早已死亡的 ceph-osd 上仍可能包含这些对象。(这种情况几乎不太可能发生)。

如果所有位置都查询过了仍有对象丢失,那就得放弃丢失的对象了。这仍可能是罕见的失败组合导致的,集群在写入完成前,未能得知写入是否已执行。以下命令把未找到的( unfound )对象标记为丢失( lost )。

 

 

ceph pg 2.5 mark_unfound_lost revert|delete

 

上述最后一个参数告诉集群应如何处理丢失的对象。

delete 选项将导致完全删除它们。

revert 选项(纠删码存储池不可用)会回滚到前一个版本或者(如果它是新对象的话)删除它。要慎用,它可能迷惑那些期望对象存在的应用程序。

 

 

无根归置组

拥有归置组拷贝的 OSD 都可以失败,在这种情况下,那一部分的对象存储不可用,监视器就不会收到那些归置组的状态更新了。为检测这种情况,监视器把任何主 OSD 失败的归置组标记为 stale (不新鲜),例如:

 

ceph health
HEALTH_WARN
24 pgs stale; 3/300 in osds are down

 

你能找出哪些归置组 stale 、和存储这些归置组的最新 OSD ,命令如下:

 

ceph health detail
HEALTH_WARN
24 pgs stale; 3/300 in osds are down
...
pg
2.5 is stuck stale+active+remapped, last acting [2,0]
...
osd.
10 is down since epoch 23, last address 192.168.106.220:6800/11080
osd.
11 is down since epoch 13, last address 192.168.106.220:6803/11539
osd.
12 is down since epoch 24, last address 192.168.106.220:6806/11861

 

如果想使归置组 2.5 重新在线,例如,上面的输出告诉我们它最后由 osd.0osd.2 处理,重启这些 ceph-osd 将恢复之(还有其它的很多 PG )。

 

只有几个 OSD 接收数据

如果你的集群有很多节点,但只有其中几个接收数据,检查下存储池里的归置组数量。因为归置组是映射到多个 OSD 的,这样少量的归置组将不能分布于整个集群。试着创建个新存储池,其归置组数量是 OSD 数量的若干倍。详情见归置组,存储池的默认归置组数量没多大用,你可以参考这里更改它。

不能写入数据

如果你的集群已启动,但一些 OSD 没起来,导致不能写入数据,确认下运行的 OSD 数量满足归置组要求的最低 OSD 数。如果不能满足, Ceph 就不会允许你写入数据,因为 Ceph 不能保证复制能如愿进行。详情参见存储池、归置组和 CRUSH 配置参考里的 osd pool default min size

归置组不一致

如果您收到一个active + clean + inconsistent的状态, 这可能发生由于在洗涤过程中的错误。如果不一致的原因是磁盘错误, 请检查磁盘。

 

您可以通过执行以下操作来修复不一致的放置组:

ceph pg repair {placement-group-ID}

 

如果由于时钟歪斜而周期性地收到active + clean + inconsistent的状态, 您可以考虑在监视器主机上配置您的 NTP 守护程序,

 

纠删编码的归置组不是 active+clean

CRUSH 找不到足够多的 OSD 映射到某个 PG 时,它会显示为 2147483647 ,意思是 ITEM_NONE 或 no OSD found ,例如:

[2,1,6,0,5,8,2147483647,7,4]

 

OSD 不够多

 

如果 Ceph 集群仅有 8 个 OSD ,但是纠删码存储池需要 9 个,就会显示上面的错误。这时候,你仍然可以另外创建需要较少 OSD 的纠删码存储池:

ceph osd erasure-code-profile set myprofile k=5 m=3
ceph osd pool create erasurepool
16 16 erasure myprofile

 

或者新增一个 OSD ,这个 PG 会自动用上的。

 

CRUSH 条件不能满足

即使集群拥有足够多的 OSD , CRUSH 规则集的强制要求仍有可能无法满足。假如有 10 个 OSD 分布于两个主机上,且 CRUSH 规则集要求相同归置组不得使用位于同一主机的两个 OSD ,这样映射就会失败,因为只能找到两个 OSD ,你可以从规则集里查看必要条件:

 

$ ceph osd crush rule ls
[
"replicated_ruleset",
"erasurepool"]
$ ceph osd crush rule dump erasurepool
{
"rule_id": 1,
"rule_name": "erasurepool",
"ruleset": 1,
"type": 3,
"min_size": 3,
"max_size": 20,
"steps": [
{
"op": "take",
"item": -1,
"item_name": "default"},
{
"op": "chooseleaf_indep",
"num": 0,
"type": "host"},
{
"op": "emit"}]}

 

可以这样解决此问题,创建新存储池,其内的 PG 允许多个 OSD 位于同一主机,命令如下:

 

ceph osd erasure-code-profile set myprofile ruleset-failure-domain=osd
ceph osd pool create erasurepool
16 16 erasure myprofile

 

CRUSH 过早中止

假设集群拥有的 OSD 足以映射到 PG (比如有 9 个 OSD 和一个纠删码存储池的集群,每个 PG 需要 9 个 OSD ), CRUSH 仍然有可能在找到映射前就中止了。可以这样解决:

    降低纠删存储池内 PG 的要求,让它使用较少的 OSD (需创建另一个存储池,因为纠删码配置不支持动态修改)。
向集群添加更多 OSD (无需修改纠删存储池,它会自动回到清洁状态)。
通过手工打造的 CRUSH 规则集,让它多试几次以找到合适的映射。把 set_choose_tries 设置得高于默认值即可。

 

你从集群中提取出 crushmap 之后,应该先用 crushtool 校验一下是否有问题,这样你的试验就无需触及 Ceph 集群,只要在一个本地文件上测试即可:

 

$ ceph osd crush rule dump erasurepool
{
"rule_name": "erasurepool",
"ruleset": 1,
"type": 3,
"min_size": 3,
"max_size": 20,
"steps": [
{
"op": "take",
"item": -1,
"item_name": "default"},
{
"op": "chooseleaf_indep",
"num": 0,
"type": "host"},
{
"op": "emit"}]}
$ ceph osd getcrushmap
> crush.map
got crush map
from osdmap epoch 13
$ crushtool
-i crush.map --test --show-bad-mappings \
--rule 1 \
--num-rep 9 \
--min-x 1 --max-x $((1024 * 1024))
bad mapping rule
8 x 43 num_rep 9 result [3,2,7,1,2147483647,8,5,6,0]
bad mapping rule
8 x 79 num_rep 9 result [6,0,2,1,4,7,2147483647,5,8]
bad mapping rule
8 x 173 num_rep 9 result [0,4,6,8,2,1,3,7,2147483647]

 

 

其中 --num-rep 是纠删码 crush 规则集所需的 OSD 数量, --ruleceph osd crush rule dump 命令结果中 ruleset 字段的值。此测试会尝试映射一百万个值(即 [--min-x,--max-x] 所指定的范围),且必须至少显示一个坏映射;如果它没有任何输出,说明所有映射都成功了,你可以就此打住:问题的根源不在这里。

反编译 crush 图后,你可以手动编辑其规则集:

 

$ crushtool --decompile crush.map > crush.txt

 

 

并把下面这行加进规则集:

step set_choose_tries 100

 

然后 crush.txt 文件内的这部分大致如此:

rule erasurepool {
ruleset
1
type erasure
min_size
3
max_size
20
step set_chooseleaf_tries
5
step set_choose_tries
100
step take
default
step chooseleaf indep
0 type host
step emit
}

 

然后编译、并再次测试:

$ crushtool --compile crush.txt -o better-crush.map

 

所有映射都成功时,用 crushtool--show-choose-tries 选项能看到成功映射的尝试次数直方图:

$ crushtool -i better-crush.map --test --show-bad-mappings \
--show-choose-tries \
--rule 1 \
--num-rep 9 \
--min-x 1 --max-x $((1024 * 1024))
...
11: 42
12: 44
13: 54
14: 45
15: 35
16: 34
17: 30
18: 25
19: 19
20: 22
21: 20
22: 17
23: 13
24: 16
25: 13
26: 11
27: 11
28: 13
29: 11
30: 10
31: 6
32: 5
33: 10
34: 3
35: 7
36: 5
37: 2
38: 5
39: 5
40: 2
41: 5
42: 4
43: 1
44: 2
45: 2
46: 3
47: 1
48: 0
...
102: 0
103: 1
104: 0
...

 

有 42 个归置组需 11 次重试、 44 个归置组需 12 次重试,以此类推。这样,重试的最高次数就是防止坏映射的最低值,也就是 set_choose_tries 的取值(即上面输出中的 103 ,因为任意归置组成功映射的重试次数都没有超过 103 )。