【Kafka】线上集群部署

294 阅读9分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

一、前言

建议总结如下:

因素考量点建议
操作系统操作系统 I/O 模型Linux 系统上:
1. epoll
2. 零拷贝
磁盘磁盘 I/O 性能机械磁盘,不需要搭建 RAID。
生产可以上 SSD。
磁盘容量根据消息数、留存时间预估磁盘容量实际使用中建议预留 20%~30% 空间
带宽根据实际带宽资源和业务 SLA 预估服务器数量对于千兆网络,建议每台服务器按照
700 Mbps 来计算,避免大流量下的丢包

下面来详细解释下。

(1)操作系统

主要从下面三个方面比较,Linux 表现更胜一筹:

  • I/O 模型的使用
  • 数据网络传输效率
  • 社区支持度
  1. 主流的 I/O 模型通常有 5 种类型:阻塞式 I/O、非阻塞式 I/O、I/O 多路复用、信号驱动 I/O 和异步 I/O。
  • 实际上 Kafka 客户端底层使用了 JavaselectorselectorLinux 上的实现机制是 epoll
  • Windows 平台上的实现机制是 select
  1. 零拷贝(Zero Copy)技术:就是当数据在磁盘和网络进行传输时避免昂贵的内核态数据拷贝从而实现快速的数据传输。

Linux 平台实现了这样的零拷贝机制,但有些令人遗憾的是在 Windows 平台上必须要等到 Java 8 的 60 更新版本才能“享受”到这个福利。

  1. 社区的支持度:
  • Linux 上迭代快
  • 社区目前对 Windows 平台上发现的 Kafka Bug 不做任何承诺

(2)磁盘

磁盘使用 RAID 的两个主要优势在于:

  1. 提供冗余的磁盘存储空间

  2. 提供负载均衡


(3)磁盘空间

问题:Kafka 集群到底需要多大的存储空间?

  • Kafka 需要将消息保存在底层的磁盘上,这些消息默认会被保存一段时间然后自动被删除。

  • 虽然这段时间是可以配置的,但你应该如何结合自身业务场景和存储需求来规划 Kafka 集群的存储容量呢?

举个栗子:所在公司有个业务每天需要向 Kafka 集群发送 1 亿条消息,每条消息保存两份以防止数据丢失,另外消息默认保存两周时间。

  • 假设消息的平均大小是 1KB,那么这个 Kafka 集群需要为这个业务预留多少磁盘空间?

计算如下:

  • 每天 1 亿条 1KB 大小的消息,保存两份且留存两周的时间:那么总的空间大小就等于 1 亿 * 1KB * 2 / 1000 / 1000 = 200GB。

  • 一般情况下 Kafka 集群除了消息数据还有其他类型的数据,比如索引数据等,故我们再为这些数据预留出 10% 的磁盘空间,因此总的存储容量就是 220GB。

  • 既然要保存两周,那么整体容量即为 220GB * 14,大约 3TB 左右。

  • Kafka 支持数据的压缩,假设压缩比是 0.75,那么最后你需要规划的存储空间就是 0.75 * 3 = 2.25TB。

总之在规划磁盘容量时,需要考虑下面这几个元素:

  1. 新增消息数
  2. 消息留存时间
  3. 平均消息大小
  4. 备份数
  5. 是否启用压缩

(4)带宽

与其说是带宽资源的规划,其实真正要规划的是所需的 Kafka 服务器的数量。

带宽也主要有两种:

  • 1Gbps 的千兆网络
  • 10Gbps 的万兆网络

举个栗子:假设你公司的机房环境是千兆网络,即 1Gbps,现在你有个业务,其业务目标或 SLA 是在 1 小时内处理 1TB 的业务数据。

那么问题来了,你到底需要多少台 Kafka 服务器来完成这个业务呢?

计算一下:

  • 带宽是 1Gbps,即每秒处理 1Gb 的数据。

  • 假设 Kafka 会用到 70% 的带宽资源,因为总要为其他应用或进程留一些资源。

    根据实际使用经验,超过 70% 的阈值就有网络丢包的可能性了,故 70% 的设定是一个比较合理的值。 也就是说单台 Kafka 服务器最多也就能使用大约 700Mb 的带宽资源。

  • 700Mb 是最大带宽资源,但 Kafka 服务器常规性使用只用 1/3,通常会额外预留出 2/3 的资源,即:单台服务器使用带宽 700Mb / 3 ≈ 240Mbps。

    为什么常规下要预留 2/3? 因为:为 follower 拉取留一些带宽。

目前为止,我们知道 Kafka 单台服务器使用 240Mbps,计算 1 小时内处理 1TB 数据所需的服务器数量了。

  • 每秒需要处理多少数据:1(TB) * 8(b) * 1024(G) / 3600(s) = 2336Mb

  • 需要多少台服务器:2336Mb / 240 = 10 台

  • 因为需要额外复制 2 份,那么总的服务器台数 × 3,即 30 台。

那问题来了,为什么不说 CPUCPU 应该配几核的?

  1. 因为:因为通常情况下 Kafka 不太占用 CPU,所以没有这方面的最佳实践出来。

  2. 有些情况下 Kafkabroker 很耗 CPU

    1. serverclient 使用了不同的压缩算法
    2. serverclient 版本不一致造成消息格式转换
    3. broker 端解压缩校验
  3. 相比带宽资源,CPU 通常都不是瓶颈。



二、重要参数

(1)Broker 端参数

Broker 存储信息:

  • log.dirs:指定 Broker 需要使用的若干个文件目录路径。

    在生产环境一定要为 log.dirs 配置多个路径,逗号分割:/home/kafka1,/home/kafka2,/home/kafka3 优点:

    1. 提升读写性能:比起单块磁盘,多块物理磁盘同时读写数据有更高的吞吐量。
    2. 能够实现故障转移:即 Failover。这是 Kafka 1.1 版本新引入的强大功能。要知道在以前,只要 Kafka Broker 使用的任何一块磁盘挂掉了,整个 Broker 进程都会关闭。但是自 1.1 开始,这种情况被修正了,坏掉的磁盘上的数据会自动地转移到其他正常的磁盘上,而且 Broker 还能正常工作。若没有这种 Failover 的话,我们只能依靠 RAID 来提供保障。
  • log.dir:表示单个路径,补充上一个参数用的。

ZooKeeper 相关的设置:

负责协调管理并保存 Kafka 集群的所有元数据信息,比如集群都有哪些 Broker 在运行、创建了哪些 Topic,每个 Topic 都有多少分区以及这些分区的 Leader 副本都在哪些机器上等信息。

  • zookeeper.connect:例如,zk1:2181,zk2:2181,zk3:2181

    如果有两套 Kafka 集群使用同一套 ZooKeeper 集群,就可以使用 chroot。 两套集群的 zookeeper.connect 参数可以指定:zk1:2181,zk2:2181,zk3:2181/kafka1zk1:2181,zk2:2181,zk3:2181/kafka2

Broker 连接相关的参数:

客户端程序或其他 Broker 如何与该 Broker 进行通信的设置。

  • listeners:监听器,用于内网,其实就是告诉外部连接者要通过什么协议访问指定主机名和端口开放的 Kafka 服务。

  • advertised.listeners:用于外网,advertised 的含义表示宣称的、公布的,就是说这组监听器是 Broker 用于对外发布的。

  • host.name/port:不用在乎,忘掉。

Topic 管理的参数:

  • auto.create.topics.enable:是否允许自动创建 Topic,默认 false

建议最好设置成 false,即不允许自动创建 Topic

  • unclean.leader.election.enable:是否允许 Unclean Leader 选举,默认 false

设置成 false,坚决不能让那些落后太多的副本竞选 Leader

  • auto.leader.rebalance.enable:是否允许定期进行 Leader 选举,设置为 false

    是否允许 Kafka 定期对一些 Topic 分区进行 Leader 重选举。

数据留存的参数:

  • log.retention.{hours|minutes|ms}:控制一条消息数据被保存多长时间。

比如 log.retention.hours=168 表示默认保存 7 天的数据,自动删除 7 天前的数据。

  • log.retention.bytes:指定 Broker 为消息保存的总磁盘容量大小。

默认是 -1,表明你想在这台 Broker 上保存多少数据都可以。

  • message.max.bytes:控制 Broker 能够接收的最大消息大小。

    默认的 1000012 太少了,即 976KB,还不到 1MB

(2)Topic 级别参数

如果同时设置了 Topic 级别参数和全局 Broker 参数,到底听谁的呢?

Topic 级别参数会覆盖全局 Broker 参数的值,而每个 Topic 都能设置自己的参数值,这就是所谓的 Topic 级别参数。

  • retention.ms:规定了该 Topic 消息被保存的时长。

    默认是 7 天,即该 Topic 只保存最近 7 天的消息。一旦设置了这个值,它会覆盖掉 Broker 端的全局参数值。

  • retention.bytes:规定了要为该 Topic 预留多大的磁盘空间。

    和全局参数作用相似,这个值通常在多租户的 Kafka 集群中会有用武之地。当前默认值是 -1,表示可以无限使用磁盘空间。

# 创建 Topic 时设置:
# 一般不会超过 5MB
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic transaction --partitions 1 --replication-factor 1 --config retention.ms=15552000000 --config max.message.bytes=5242880


# 修改 Topic 参数:
# 修改参数:发送最大值是 10MB 消息
bin/kafka-configs.sh --zookeeper localhost:2181 --entity-type topics --entity-name transaction --alter --add-config max.message.bytes=10485760

(3)JVM 参数

无脑给出一个通用的建议:将你的 JVM 堆大小设置成 6GB 吧,这是目前业界比较公认的一个合理值。

  • KAFKA_HEAP_OPTS:指定堆大小

  • KAFKA_JVM_PERFORMANCE_OPTS:指定 GC 参数

# 在启动 Kafka Broker 之前,先设置上这两个环境变量:
$ export KAFKA_HEAP_OPTS=--Xms6g  --Xmx6g
$ export KAFKA_JVM_PERFORMANCE_OPTS= -server -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true
$ bin/kafka-server-start.sh config/server.properties

如果使用 Java8,垃圾回收器设置为:G1 收集器

  • 在没有任何调优的情况下,G1 表现得要比 CMS 出色,主要体现在更少的 Full GC

  • 需要调整的参数更少。


(4)操作系统参数

  1. 文件描述符限制:ulimit -n 1000000

不设置,会常看到 “Too many open files”的错误。

# Kafka 会频繁创建和修改文件
# 计算公式:分区数量 × (分区总大小 / 日志段大小) × 3

# 举个栗子:
# 一个 broker 上有 100 个分区,每个分区大概 10G 的数据,日志段大小是默认 1G
# 计算:100 × 10(分区里的日志段文件数量) × 3 = 3000 个文件
  1. 文件系统类型:指 ext3ext4XFS 日志文件系统

    根据官网的测试报告,XFS 的性能要强于 ext4,所以生产环境最好还是使用 XFS。 最近有个 Kafka 使用 ZFS 的数据报告

  2. Swappiness: 建议将 swappniess 配置成一个接近 0 但不为 0 的值,比如 1

    因为一旦设置成 0,当物理内存耗尽时,操作系统会触发 OOM killer 这个组件,它会随机挑选一个进程然后 kill 掉,即根本不给用户任何的预警。 如果设置成一个比较小的值,当开始使用 swap 空间时,你至少能够观测到 Broker 性能开始出现急剧下降,从而给你进一步调优和诊断问题的时间。

  3. 提交时间:flush 落盘时间,默认 5 秒。

# 如何设置呢?

$ sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000     # os cache 里的数据在 3s 后会被刷入磁盘
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500   # 每隔 5s 唤醒一次刷盘的线程
vm.dirtytime_expire_seconds = 43200 

# 可以设置大一些:1分钟 ~ 2分钟
# 特别大规模的企业和公司,如果可以接受机器宕机的时候,数据适当可以丢失一些,kafka 里的数据可以适当丢失一些,但是为了提升集群的吞吐量的话。