linux lvm

LVM

对磁盘创建 lvm。

1
2
3
4
5
6
7
8
9
10
11
# 创建
$ pvcreate /dev/sda
$ vgcreate cinder-volumes /dev/sda

# 删除
## 移除卷
$ lvremove cinder--volumes-cinder--volumes--pool_tmeta
## 删除组
$ vgremove cinder-volumes
## 删除物理卷
$ pvremove /dev/sda

如果出现 pvcreate 时出现 execlude by a filter,检查 /etc/lvm/lvm.conf 下的 filters

1
filter = [ "a/sda/", "a/nvme/", "r/.*/" ]

如果想要接受一个块设备,使用类似下面的配置。

1
"a|.*|"

如果想要拒绝一个块设备,使用类似下面的配置。

1
"r|/dev/cdrom|"

openstack multiple backends

在添加前,需要对磁盘分区,并创建 lvm。

描述

一台机器,两块存储盘,一个 HDD,一个SSD。HDD 用来做存储,SSD用来做系统盘。

目标

安装系统时可以指定把卷创建在 SSD 盘中,在 HDD 盘上创建存储盘,并挂载到机器。

操作

初始化硬盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# lsblk
...
nvme1n1 259:0 0 931.5G 0 disk
└─nvme1n1p1 259:1 0 931.5G 0 part /data
...

## 使用 nvme1n1 来创建 lvm 卷,卷组为 cinder-nvme
# umount /dev/nvme1n1p1
# vim /etc/lvm/lvm.conf
## 修改 devices.filter, 包含该设备
filter = ["a|/dev/sda|", "a|/dev/nvme1n1|", "r|.*|"]
## 创建 PV (Physical Volume)
# pvcreate /dev/nvme1n1
## 查看信息
# pvdisplay /dev/nvme1n1
## 创建 volumn group (VG)
# vgcreate cinder-nvme /dev/nvme1n1
## 再次查看信息
# pvdisplay /dev/nvme1n1

配置多存储

openstack cinder 支持一台机器上配置多块存储(multiple backends),编辑 /etc/cinder/cinder.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[DEFAULT]
...
enabled_backends = lvm-nvme,lvm-sda

...

[lvm-nvme]
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
volume_group = cinder-nvme
target_protocol = iscsi
target_helper = tgtadm
volume_backend_name = LVM_NVME

[lvm-sda]
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
volume_group = cinder-volumes
target_protocol = iscsi
target_helper = tgtadm
volume_backend_name = LVM_SDA

这里配置了两个 backends,lvm-nvme 和 lvm-sda,前者为固态硬盘,后者为机械硬盘。

创建卷类型

命令行方式

1
2
3
4
5
6
7
8
9
10
# 创建卷类型
$ openstack --os-username admin --os-tenant-name admin volume type create 存储
# 设置扩展参数,将该类型卷绑定到特定的 backends
$ openstack --os-username admin --os-tenant-name admin volume type set 存储 \
--property volume_backend_name=LVM_SDA

# 同样的方式,绑定另一块盘
$ openstack --os-username admin --os-tenant-name admin volume type create 高性能
$ openstack --os-username admin --os-tenant-name admin volume type set 高性能 \
--property volume_backend_name=LVM_NVME

界面操作

image-20211102234111326

在 管理员 / 卷 / 卷类型 中,创建卷类型 高性能和存储。

image-20211102234211598

点击卷类型的操作菜单,选择「查看扩展规格」。

image-20211102234351115

创建扩展,key 为 volume_backend_name,值为LVM_SDA(存储)和 LVM_NVME(高性能)。

创建卷

在菜单 项目 / 卷 下,创建卷。

image-20211102234617509

指定类型为「高性能」。同理创建其他卷,类型选择「存储」。

效果

image-20211102234740428

最终,在 LVM_SDA 上面创建了 hdd 卷,在 LVM_NVME 上面创建了 ssd 卷。对于卷列表中的 host,其格式为 host@backend-name#pool,比如 srv@lvm-sda#LVM_SDA

使用 lsblk 查看卷信息。

image-20211102235013992

参考

prometheus metric

metric

指标类型 说明 场景举例
Counter 累计计数器,只能增加或置零 请求数、错误数
Gauge 数值指标监控,可增加也可以减小 温度、线程数量
Histogram 直方图采样统计,可设置分位统计 请求耗时、返回数据大小
Summary

Counter

Counter 用于累计指标,表示一个只能递增或置零的单调递增计数器。

Gauge

Gauge 用于数值指标,统计值可以增加也可以减小。

Histogram

Histogram 用于对指标进行采样观察,可以设置需要统计的分位值。在抓取时,Histogram 指标会返回多个时序。

  • 观察桶的累计计数,指标名称为 <basename>_bucket{le="<upper inclusive bound>"}
  • 所有采样数据值的总和,指标名称为 <basename>_sum
  • 所有采样数据的总数,指标名称为 <basename>_count ,和 <basename>_bucket{le="+Inf"} 值一致

Summary

和 Histogram 相似,Summary 对观察指标进行采样,在提供所有采用数据总数和值的总和的同时,在滑动窗口时间内计算可配置的分位数。在抓取时,Summary 指标会返回多个时序。

  • 流式传输观察到的事件的 φ-分位数 (0 ≤ φ ≤ 1),指标名称为 <basename>{quantile="<φ>"}
  • 所有采样数据值的总和,指标名称为 <basename>_sum
  • 所有采样数据的总数,指标名称为 <basename>_count

对比

Histogram & Summary

Histogram 和 Summary 都是采样观察,常用于请求耗时及响应大小统计。两者会统计样本数量以及样本值的总和,以便计算统计值的平均值。原则上,两者可用于观测有负值的指标,这种情况下观测值的总和可能会上下波动,不再使用 rate() 方法。对于这种场景如果想用 rate() ,可以用两个独立的 Summary 指标,一个统计整数,一个统计负数,然后再使用 PromQL 进行组合。

如果想统计最近五分钟的平均请求耗时,指标是 Summary 或者 Histogram 都可,指标名称为 http_request_duration_seconds,那么表达式如下。

1
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])

Apdex score

使用 Histogram 而不是用 Summary 的一个直接场景,是计算一个指标落入特定分桶样本数量的值。比如,有一个 SLO 指标,95% 的请求要在 300ms 内返回。配置一个包含 300ms 以上的分桶,可以直接表示 300ms 以内的相对请求数量,并且很容易地在值小于 0.95 时报警。下面的表达式可以计算 5 分钟内请求耗时在 300ms 以内的比例。

1
2
3
  sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
/
sum(rate(http_request_duration_seconds_count[5m])) by (job)

可以用同样的方式计算 Apdex score。配置分桶,包含目标耗时(比如,0.3s)和最大容忍耗时(一般 4 倍于目标耗时,比如 1.2s),下面表达式可以计算 Apdex Score。

1
2
3
4
5
(
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
+
sum(rate(http_request_duration_seconds_bucket{le="1.2"}[5m])) by (job)
) / 2 / sum(rate(http_request_duration_seconds_count[5m])) by (job)

分位数(Quantiles)

Histogram 和 Summary 都可以计算 φ 分位数,0 ≤ φ ≤ 1。 φ 分位数是 N 个观察值排序后位于 φ * N 位置的数的值。两者计算分位数最重要的不同是,Summary 在客户端流式计算 φ 分位数并直接上传;Histogram 暴露分桶的观测值数量,在使用 histogram_quantile() 方法获取分位值时计算发生在 server 端。

项目 Histogram Summary
请求配置 选择符合观测值的合适分桶 选择想要的分位数,并设置时间窗口;其他分位数和时间窗口不能再通过表达式计算
客户端性能
服务端性能
时序数量 每个分桶一个时序 每个分位一个时序
分桶误差 受限于桶的宽度 受限于 φ 可配置值
指定分位和滑动窗口 PramQL 表达式 客户端配置
聚合 PramQL 表达式 通常不可聚合

注意聚合差异的重要性,重新回到 SLO 的问题,这次不再关注 300ms 以内请求的比例是否达到 95%,而是 95 分位本身(95% 的请求可以在多上时间返回)。为了达到这个目的,可以设置一个 Summary 并配置 95 分位,也可以设置一个 Histogram 并在 300ms 附近设置一部分分桶(比如 {le="0.1"}{le="0.2"}{le="0.3"}, 和 {le="0.45"})。如果服务有多个实例,期望将结果聚合并得到一个整体的 95 分位值。那么,如果是 Summary 使用下面表达式计算平均值是没有意义的。

1
avg(http_request_duration_seconds{quantile="0.95"}) // BAD!

如果是 Histogram,可以使用下面的表达式。

1
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) // GOOD.

通过对原始数据进行聚合,再计算分位值,是比较合理的。

文档

kafka configuration

Common

配置项 单位 说明
bootstrap.servers - brokers 列表,多个服务器 , 分隔
metadata.max.age.ms 毫秒 本地缓存的 meta 信息最大有效时间
send.buffer.bytes 字节(bytes) TCP 发送缓存大小,-1 使用系统默认值
receive.buffer.bytes 字节(bytes) TCP 接受缓存大小,-1 使用系统默认值

Producer

配置项 单位 说明
client.id - 向服务端发请求的客户端标识,主要用于 kafka 服务端落日志及 debug
reconnect.backoff.ms 毫秒 客户端重连 broker 的最小等待时间
reconnect.backoff.max.ms 毫秒 每次重连失败会增加等待时间,直到此最大等待时间
retries - 消息发送失败重试次数;设置大于 0 的值,会触发客户端遇到错误后重新发送消息;设置 retries 且 max.in.flight.requests.per.connection > 1 可能会导致消息重排序
retry.backoff.ms 毫秒 消息重发等待时间
connections.max.idle.ms 毫秒 限制连接最大空闲时间
request.timeout.ms 毫秒 请求超时时间;应大于 replica.lag.time.max.ms (broker配置) 的值
batch.size 字节(bytes) 设置批量发送消息时每条消息最大字节数,设置 0 禁用批量发送消息
acks - 设置 kafka 集群 leader 需要多少 ack 才认为消息发送成功;设置 0,producer 不等待确认,消息写入发送缓存,这种情况下不保证服务端接收到消息,retries 配置不生效,offset 返回值总是 -1;设置 1,kafka leader 消息写入日志文件后发送确认消息,不等待 followers 数据写入确认,如果 leader down 掉可能会出现消息丢失;设置 all 或 -1,kafka leader 等待所有 follower 确认写入后,再发送确认消息。
linger.ms 毫秒 设置徘徊等待时间,消息到达后不是立即发送给消费者,而是等待一段时间,以便聚合更到消息,批量发送;如果达到 batch.size 的限制,则会忽略该配置立即发送;默认是0,不启用消息徘徊等待
max.request.size 字节(bytes) 一个请求的最大字节数;服务器可能用不同的方式来计算 batch size
max.block.ms 毫秒 控制 KafkaProducer.send()KafkaProducer.partitionsFor() 的阻塞时间,这两个方法可能会因为缓存满或元数据不可用阻塞
buffer.memory 字节(bytes) producer 用于发送消息缓存的全部字节数,不仅缓存消息,还有处理中的请求、压缩后的消息
compression.type - 压缩类型;none、gzip、snappy、lz4
max.in.flight.requests.per.connection - 每个连接允许的最大正在处理(未收到确认)请求数,超过之后会阻塞;如果设置的值大于 1,那么存在因为消息发送失败及启用 retries 导致的消息重排序的问题(比如 message1 先发送且失败,message2 后发送且成功,message1 会因为重试机制再次发送且后于 message2 确认)
key.serializer - key 的序列化类,实现 org.apache.kafka.common.serialization.Serializer 接口
value.serializer - value 的序列化类,实现 org.apache.kafka.common.serialization.Serializer 接口
partitioner.class - 分区类,实现 org.apache.kafka.clients.producer.Partitioner 接口
interceptor.classes - 拦截器类,实现 org.apache.kafka.clients.producer.ProducerInterceptor 接口;允许在发送给 cluster 前拦截 producer 的消息;默认没有拦截器
enable.idempotence - 幂等性设置;设置 true 会保证消息只被写入一次,前提配置是 enable.idempotence <= 5,retries > 0,acks = all;如果设为 false,则可能会出现一条消息因为重试写入多次
transaction.timeout.ms 毫秒 事务协作者等待事务状态更新的超时时间
transactional.id - 用于事务投递,如果配置,则必须开启 enable.idempotence,默认是 null,不能使用事务

Consumer

配置项 单位 说明
group.id - 消费组
max.poll.records - 单次调用 poll() 返回的最大消息条数
max.poll.interval.ms 毫秒 使用消费组管理时,触发poll() 的最大间隔时间
session.timeout.ms 毫秒 消费者存活周期;使用组管理设施时,消费者定期发送心跳延长 broker 保存的存活时间,如果超过该项设置的时间没有收到心跳请求,broker 会移除 consumer 并 rebalance;值必须是 broker 允许的值,在 group.min.session.timeout.msgroup.max.session.timeout.ms (broker 配置)之间
heartbeat.interval.ms 毫秒 心跳请求发送周期;该值一般不应大于 session.timeout.ms 的 1/3
enable.auto.commit - 如果为 true,消费者的 offset 会在后台定期提交
auto.commit.interval.ms 毫秒 消费者定期提交 offset 的间隔
partition.assignment.strategy - 类名,消费者使用的分区分配策略
auto.offset.reset - 没有初始的 offset 信息时消息消费策略;earliest,从最早的 offset 消费;latest,从新的消息开始消费;none,如果没有设置消费组 offset 则抛出异常
fetch.min.bytes 字节 拉取请求 server 返回的最小字节数;不足则等待足够的消息再返回,默认为1,只要有消息则立即返回
fetch.max.bytes 字节 拉取请求 server 返回的最大字节数;并不是一个绝对最大值,还受其他配置影响,比如 max.message.bytes,默认 50 MB
fetch.max.wait.ms 毫秒 服务响应拉取请求的最大阻塞时间
max.partition.fetch.bytes 毫秒 拉取请求,每个分区返回的最大字节数,默认 1 MB
client.id - 向服务端发请求的客户端标识,主要用于 kafka 服务端落日志及 debug
check.crcs - 开启 CRC32 检查
key.deserializer - key 反序列化类,实现 org.apache.kafka.common.serialization.Deserializer 接口
value.deserializer - key 反序列化类,实现 org.apache.kafka.common.serialization.Deserializer 接口
default.api.timeout.ms 毫秒 consumer API 的默认超时时间
interceptor.classes - 拦截器类,实现 org.apache.kafka.clients.consumer.ConsumerInterceptor 接口
exclude.internal.topics - 内部 topic 消息是否暴露给消费者;如果为 true,唯一可以从内部 topic 获取消息的方式是订阅它
internal.leave.group.on.close - consumer 关闭时是否退出消费组;如果为 false,则消费者关闭后不会触发 rebalance,知道 session.timeout.ms 过期
isolation.level - 控制读取事务消息的方式;read_committed,consumer.poll() 只返回提交了的事务消息;read_uncommitted,返回所有消息,包括被废弃的事务消息;对于非事务消息,两种方式会无条件返回;默认是 read_uncommitted

其他

幂等性

producer 设置 enable.idempotence 为 true,kafka 会保证消息按顺序且不重复的送达。

原理

每个 producer 被分配一个 PID(producer id),向 broker 发送消息时会带上,且每个消息有一个单调递增的序列号,生产者为主题的每个分区维护一个序列号,broker 同样记录 producer 的最大消息序列号,只接受 +1 的消息。

参考

grpc python3

安装

1
$ pip3 install grpcio

示例

1
2
3
4
5
import grpc
channel = grpc.insecure_channel('localhost:8000')
grpc.channel_ready_future(self.channel).result(10) # 等待 channel ready, 超时 10s
stub = EchoServiceGRPC.EchoServiceStub(self.channel) # 创建 stub
stub.ping('pong') # 远程调用