健康检查
当系统出现问题或要对系统做出任何改动时,都需要首先检查系统的整体健康状态。 本指南列出了用于判断系统是否健康的部分关键信息。
指标和仪表盘
不同配置和流量下,指标统计值和错误阈值可能有很大差异。 判断的基本思路是检查关键指标的天同比或周同比的情况,观察是否存在显著差异。
活跃性
对于 worker 而言,alluxio_data_access_bytes_count
指标将计算 worker 接收的读写请求。 在 Prometheus 中,我们可以通过查询 irate(alluxio_data_access_bytes_count[5m])
来计算每秒请求数(RPS)。 RPS 应保持稳定。如果 RPS 快速增加,说明 worker 可能将面临容量不足的风险。
对于 FUSE 进程而言,alluxio_fuse_result
指标可以用来计算每个 fuse 操作的 RPS。 alluxio_fuse_concurrency
也可以反映 fuse 进程的负载情况。
UFS 数据流
alluxio_ufs_data_access
指标记录在 worker 上访问 UFS 的读/写数据流量,如果启用了 UFS 回退功能,也会记录 FUSE 进程的相关数据流量。
alluxio_ufs_error
指标记录每种 UFS 的 API 访问错误代码。如果该指标增加,说明访问 UFS 出现问题。 可使用error_code
标签来过滤掉可预期错误,如 "no such file(无此类文件)"。 在 S3 中,该错误代码是404
。不同的 UFS 可能会有不同的错误代码。
缓存命中率
对于 worker 而言,alluxio_cached_data_read_bytes_total
和 alluxio_ufs_data_access_bytes_total
指标可以计算缓存命中率。 要计算每秒的缓存命中率,应在 Prometheus 中使用 irate
函数,然后使用 sum
来移除未使用的标签。
单个 worker 的缓存命中率为:
Copy sum by (instance) (irate(alluxio_cached_data_read_bytes_total{job="worker"}[5m])) / (sum by (instance) (irate(alluxio_cached_data_read_bytes_total{job="worker"}[5m])) + sum by (instance) (irate(alluxio_ufs_data_access_bytes_total{job="worker"}[5m])))
整体缓存命中率(已集成到仪表盘中):
Copy sum(irate(alluxio_cached_data_read_bytes_total{job="worker"}[5m])) / (sum(irate(alluxio_cached_data_read_bytes_total{job="worker"}[5m])) + sum(irate(alluxio_ufs_data_access_bytes_total{job="worker"}[5m])))
整体状态
Alluxio Process Readiness
Copy # 检查 kubernetes 中 worker 的就绪性
# 确保 READY 的值为 100%。即使状态显示为 "Running(运行中)",也不一定表示 worker 是健康的。
$ kubectl get pod -l app.kubernetes.io/component=worker
NAME READY STATUS RESTARTS AGE
alluxio-worker-59476bf8c5-lg4sc 1/1 Running 0 46h
alluxio-worker-59476bf8c5-vg6lc 1/1 Running 0 46h
# 检查 fuse 进程的就绪性
# selector 将同时选择 csi fuse 和 daemonSet fuse
$ kubectl get pod -l 'app.kubernetes.io/component in (fuse, csi-fuse)'
NAME READY STATUS RESTARTS AGE
alluxio-fuse-acee53e8f0a9-3gjbrdekk0 1/1 Running 0 57m
# 或使用以下一行命令来获取进程就绪的百分比
# 如果有多个 Alluxio 集群,请使用 `app.kubernetes.io/instance=alluxio` 来指定集群
$ kubectl get pod -l app.kubernetes.io/component=worker -o jsonpath='{range .items[*]}{.status.containerStatuses[0].ready}{"\n"}{end}' | awk 'BEGIN{t=0}{s+=1;if($1=="true")t+=1}END{print t,"ready /",s,"expected =",t/s*100,"%"}'
2 ready / 2 expected = 100 %
ETCD 就绪性
Copy # 检查 etcd 集群的就绪性
# 使用 `app.kubernetes.io/instance=alluxio` 来选择与 Alluxio 集成的 etcd 集群
$ kubectl get pod -l 'app.kubernetes.io/component=etcd,app.kubernetes.io/instance=alluxio'
NAME READY STATUS RESTARTS AGE
alluxio-etcd-0 1/1 Running 0 46h
alluxio-etcd-1 1/1 Running 0 46h
alluxio-etcd-2 1/1 Running 0 46h
# 使用以下一行命令获取集群就绪的百分比
$ kubectl get pod -l 'app.kubernetes.io/component=etcd,app.kubernetes.io/instance=alluxio' -o jsonpath='{range .items[*]}{.status.containerStatuses[0].ready}{"\n"}{end}' | awk 'BEGIN{t=0}{s+=1;if($1=="true")t+=1}END{print t,"ready /",s,"expected =",t/s*100,"%"}'
3 ready / 3 expected = 100 %
# 检查 etcd 是否能够处理正常的 I/O 操作
# 输出为 Alluxio worker 的注册信息。请勿以这种方式更改或写入值
$ kubectl exec -it alluxio-etcd-0 -c etcd -- bash -c 'ETCDCTL_API=3 etcdctl get --keys-only --prefix /ServiceDiscovery'
/ServiceDiscovery/default-alluxio/worker-1b4bad64-f195-46a8-be5f-27825a8100a4
/ServiceDiscovery/default-alluxio/worker-49ca0d6f-7a0a-482f-bb17-5550bd602a02
UFS 就绪性
Copy $ ./bin/alluxio exec ufsTest --path s3://your_bucket/test_path
Running test: createAtomicTest...
Passed the test! time: 5205ms
Running test: createEmptyTest...
Passed the test! time: 4076ms
Running test: createNoParentTest...
Passed the test! time: 0ms
Running test: createParentTest...
Passed the test! time: 4082ms
...
Running test: listStatusS3RootTest...
Passed the test! time: 543ms
Running test: createFileLessThanOnePartTest...
Passed the test! time: 3551ms
Running test: createAndAbortMultipartFileTest...
Passed the test! time: 6227ms
Tests completed with 0 failed.
# 该示例显示 client 将启动两个线程来向 UFS 写入并读取一个 512MB 的文件,并打印测试结果
$ ./bin/alluxio exec ufsIOTest --path s3://test_bucket/test_path --io-size 512m --threads 2
{
"readSpeedStat" : {
"mTotalDurationSeconds" : 483.992,
"mTotalSizeBytes" : 1073741824,
"mMaxSpeedMbps" : 2.0614405926641703,
"mMinSpeedMbps" : 1.0578687251028942,
"mAvgSpeedMbps" : 1.5596546588835323,
"mClusterAvgSpeedMbps" : 2.1157374502057884,
"mStdDev" : 0.7096324729606261,
"className" : "alluxio.stress.worker.IOTaskSummary$SpeedStat"
},
"writeSpeedStat" : {
"mTotalDurationSeconds" : 172.136,
"mTotalSizeBytes" : 1073741824,
"mMaxSpeedMbps" : 3.5236227246137433,
"mMinSpeedMbps" : 2.974392340939722,
"mAvgSpeedMbps" : 3.2490075327767327,
"mClusterAvgSpeedMbps" : 5.948784681879444,
"mStdDev" : 0.3883645287295896,
"className" : "alluxio.stress.worker.IOTaskSummary$SpeedStat"
},
"errors" : [ ],
"baseParameters" : {
"mCluster" : false ,
"mClusterLimit" : 0,
"mClusterStartDelay" : "10s" ,
"mJavaOpts" : [ ],
"mProfileAgent" : "" ,
"mBenchTimeout" : "20m" ,
"mId" : "local-task-0" ,
"mIndex" : "local-task-0" ,
"mDistributed" : false ,
"mStartMs" : -1,
"mInProcess" : true ,
"mHelp" : false
},
"points" : [ {
"mMode" : "WRITE" ,
"mDurationSeconds" : 145.305,
"mDataSizeBytes" : 536870912,
"className" : "alluxio.stress.worker.IOTaskResult$Point"
}, {
"mMode" : "WRITE" ,
"mDurationSeconds" : 172.136,
"mDataSizeBytes" : 536870912,
"className" : "alluxio.stress.worker.IOTaskResult$Point"
}, {
"mMode" : "READ" ,
"mDurationSeconds" : 248.37,
"mDataSizeBytes" : 536870912,
"className" : "alluxio.stress.worker.IOTaskResult$Point"
}, {
"mMode" : "READ" ,
"mDurationSeconds" : 483.992,
"mDataSizeBytes" : 536870912,
"className" : "alluxio.stress.worker.IOTaskResult$Point"
} ],
"parameters" : {
"className" : "alluxio.stress.worker.UfsIOParameters" ,
"mThreads" : 2,
"mDataSize" : "512m" ,
"mPath" : "s3://test_bucket/test_path" ,
"mUseUfsConf" : false ,
"mConf" : { }
},
"className" : "alluxio.stress.worker.IOTaskSummary"
}
日志
Alluxio 进程
Copy # 使用 `kubectl logs <pod-name>` 来获取单个 pod 的所有日志
# 在裸机上,日志位于`logs/<component>.log`,例如:`logs/worker.log`
# 使用 `grep` 命令来过滤特定的日志级别
# 当捕获到异常时,在 WARN/ERROR 日志之后会有附加信息,
# 使用 `-A` 打印更多行
$ kubectl logs alluxio-fuse-acee53e8f0a9-3gjbrdekk0 | grep -A 1 'WARN\|ERROR'
2024-07-04 17:29:53,499 ERROR HdfsUfsStatusIterator - Failed to list the path hdfs://localhost:9000/
java.net.ConnectException: Call From myhost/192.168.1.10 to localhost:9000 failed on connection exception: java.net.ConnectException: Connection refused; For more details see: http://wiki.apache.org/hadoop/ConnectionRefused
# 使用 `kubectl logs -p` 获取之前失败容器的日志
$ kubectl logs -p alluxio-worker-59476bf8c5-lg4sc | grep -A 1 'WARN\|ERROR'
2024-07-05 14:22:28,290 [QuotaCoordinatorTimerThread] ERROR quota.QuotaCoordinator (QuotaCoordinator.java:lambda$new$0) - Failed to run quota janitor job
java.io.IOException: Failed to poll quota usage from worker WorkerInfo{id=7128818659502632567, identity=worker-7128818659502632567, address=WorkerNetAddress{host=sunbowen, containerHost=, rpcPort=64750, dataPort=64751, webPort=64753, domainSocketPath=, secureRpcPort=0, httpServerPort=0}, lastContactSec=283, state=LIVE, capacityBytes=1073741824, usedBytes=0, startTimeMs=1720160253072, capacityBytesOnTiers={MEM=1073741824}, usedBytesOnTiers={MEM=0}, version=3.x-7.0.0-SNAPSHOT, revision=fca83a4688187055d7abfd3a7d710b83a6b62ac6}
Kubernetes CSI 驱动程序
Copy # 检查 csi 节点日志
# csi 节点将管理本地节点上的 fuse pod 和挂载点,因此我们需要检查与fuse pod和用户pod部署在同一节点上的csi节点
# 1. 获取 FUSE Pod /用户 Pod 的节点名称
$ kubectl get pod alluxio-fuse-acee53e8f0a9-3gjbrdekk0 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
alluxio-fuse-acee53e8f0a9-3gjbrdekk0 1/1 Running 0 66m 10.0.5.97 ip-10-0-5-202.ec2.internal <none> <none>
# 2. 根据节点名称获取 CSI 节点
$ kubectl get pod -n alluxio-operator -l app.kubernetes.io/component=csi-nodeplugin --field-selector spec.nodeName=ip-10-0-5-202.ec2.internal
NAME READY STATUS RESTARTS AGE
alluxio-csi-nodeplugin-5vvdg 2/2 Running 0 47h
# 3. 从 csi 节点获取日志
$ kubectl logs alluxio-csi-nodeplugin-5vvdg -n alluxio-operator -c csi-nodeserver
# 或者可以使用以下一行命令获取 csi 节点日志
# 需要确保环境变量 `PODNS`和 `POD` 设置正确
$ PODNS=default POD=alluxio-fuse-acee53e8f0a9-3gjbrdekk0
$ kubectl logs -n alluxio-operator -c csi-nodeserver $(kubectl get pod -n alluxio-operator -l app.kubernetes.io/component=csi-nodeplugin --field-selector spec.nodeName=$(kubectl get pod -o jsonpath='{.spec.nodeName}' -n ${PODNS} ${POD}) -o jsonpath='{..metadata.name}')
...
{"level":"info","time":"2024-08-09T04:06:19.986Z","caller":"csi/driver.go:94","msg":"GRPC respond","response":{"capabilities":[{"Type":{"Rpc":{"type":1}}}]}}
{"level":"info","time":"2024-08-09T04:06:19.986Z","caller":"csi/driver.go:89","msg":"GRPC call","method":"/csi.v1.Node/NodeGetCapabilities","request":{}}
快速恢复
ETCD 故障
Alluxio 针对 ETCD 故障已经实现了自动应急处理。 在 ETCD 出现故障后,Alluxio 会在一段(一般这个周期为24小时)时间内容忍 ETCD 故障以保证 IO 的正常进行。 同时管理员应当在这段时间内解决ETCD 故障。
当 ETCD 出现故障时,一般可以通过重启 ETCD pod来解决。 在特殊情况下,如果 ETCD 节点无法通过K8s重启解决问题,可以参考如下步骤重建和恢复 ETCD 服务。
关闭集群:kubectl delete -f alluxio-cluster.yaml
对原来的 ETCD pv 目录进行备份,防止之后需要恢复数据
检查所有物理机上的 ETCD 文件夹并将里面的内容都删掉
创建三个 ETCD PV。如果 PV 是由管理员手动创建,则建议在 PV 当中增加nodeAffinity
section 保证 PV 分别对应一个 ETCD 节点和一个物理机。
Copy apiVersion : v1
kind : PersistentVolumeClaim
spec :
nodeAffinity :
required :
nodeSelectorTerms :
- matchExpressions :
- key : kubernetes.io/hostname
operator : In
values :
- ip-10-0-4-212.ec2.internal
重启集群:kubectl create -f alluxio-cluster.yaml
如果 UnderFileSystem
资源中未定义 UFS 挂载点,则需要重新执行alluxio mount add
命令以重新挂载 UFS。
Worker 故障
Alluxio 通过优化功能可以在保证 I/O 不中断的情况下处理 Worker 故障。 一般来说,少量 Worker 故障不会影响 Alluxio 的功能。
当 Worker 出现故障时,K8s 会通过重启 Worker pod 来解决,不会影响已缓存数据。
FUSE 故障
Fuse 容器如果崩溃,会由集群内部的机制自动重启并恢复。 容器内的挂载点也会在 Fuse 容器启动完毕后自动恢复。
如果遇到 Fuse 容器因为某些原因停止响应,希望恢复到正常状态,可以直接执行kubectl delete pod alluxio-fuse-xxx
命令删除Fuse容器,并等待自动恢复机制完成工作。
Coordinator 故障
Coordinator 负责处理负载作业和管理服务。 它将提交的作业的元数据持久化到元存储(metastore),并在崩溃时进行恢复。 如果持久化的数据出现损坏,未完成的作业将丢失,并需要重新提交。
当 coordinator 出现故障时,K8s 会自动重启 coordinator pod。