基于Kubernetes在AWS上部署Kafka时遇到的一些问题

作者:Jack47

转载请保留作者和原文出处

欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源

交代一下背景:我们的后台系统是一套使用Kafka消息队列的数据处理管线:Kafka->Logstash->Elasticsearch。这些组件都跑在Docker的容器环境里,我们是基于Kubernetes来编排整个后端的数据处理管线上的容器。Kafka需要暴露在外网里,接收Kafka Producer(filebeat, collectd)发过来的消息。本文是记录基于Kubernetes在AWS上部署Kafka 0.9.x版本时遇到的问题和排查思路。

为了能够在外网也能访问Kafka,Kafka组件对应的Kubernetes ServiceType选用的是NodePort,Kafka集群有三个节点,即Kafka Server有三个Broker。对外暴露的端口是39092,三个Broker对应的外网IP分别是 59.64.11.2159.64.11.2259.64.11.23。Kafka组件部署完成后,使用Kafka producer连接Server,配置的kafka bootstrap_servers59.64.11.22:39092。这里之所以端口使用的是 39092,而非9092,是因为Kubernetes对外暴露的端口,分配的是39092。接着往下看,你会发现这样会有问题。

connection refused

Kafka client的日志里报错:

2016-11-22T07:23:33.312102145Z 2016-11-22T07:23:33Z WARN Failed to connect to broker 59.64.11.21:30791: dial tcp 52.198.148.31:30791: getsockopt: connection refused
2016-11-22T07:23:33.312102145Z
2016-11-22T07:23:33.312102145Z 2016-11-22T07:23:33Z WARN kafka message: client/metadata got error from broker while fetching metadata:%!(EXTRA *net.OpError=dial tcp 52.198.148.31:30791: getsockopt: connection refused)

第一反应是去看AWS实例的安全组(Security Group),发现忘了允许39092这个端口的接入。配置完成后,重启Kafka client。

No available broker

接下来遇到了这个错误:

2016-11-22T07:23:33.312102145Z 2016-11-22T07:23:33Z WARN kafka message: client/metadata no available broker to send metadata request to
2016-11-22T07:23:33.312102145Z 2016-11-22T07:23:33Z WARN client/brokers resurrecting 1 dead seed brokers

Google一番,然后通过阅读Kafka的文档(一定要注意查看对应版本的文档),发现原因了。
当Kafka broker启动时,它会在ZK上注册自己的IP和端口号,客户端就通过这个IP和端口号来连接。在AWS这种IaaS环境下,由于java.net.InetAddress.getCanonicalHostName调用拿到的HostName是类似ip-172-31-10-199这样的只有内网才能访问到的主机名,所以默认注册到ZK上的IP是内网才能访问的内网IP。此时就需要显示指定 advertised.host.name, advertised.listeners参数,让注册到ZK上的IP是外网IP。
例如对于 59.64.11.22 IP对应的broker,需要在 server.properties 配置文件里增加如下三个配置:

advertised.listeners=PLAINTEXT://59.64.11.22:9092
advertised.host.name=59.64.11.22
advertised.port=9092

估计读者们也会跟我一样犯迷糊,为什么需要三个参数来配置IP和端口号呢,用一个advertised.listeners不就搞定了吗?后来发现最新版本0.10.x broker配置弃用了advertised.host.nameadvertised.port 这两个个配置项,就配置advertised.listeners就可以了。:joy

found some partitions to be leaderless

2016-11-22T02:58:36Z WARN kafka message: client/metadata found some partitions to be leaderless

又是 Read The Fucking Manual,发现Kafka生产者会先连接 bootstrap_servers列表里的某个节点,这台机器只是用来获取集群的元数据(meta data):拿到topics,partitions和 replicas的信息。查到写入的topic对应的leader节点(可能不是刚才的那个节点)后,与之建立连接,然后发送实际的数据给leader。有了这个背景知识,上面的这个错误信息就不难理解了,说明获取元数据没问题,但是连不到对应的leader节点。经过跟同事探讨,发现了问题所在:Kafka使用的Kubernetes ServiceType是NodePort,而NodePort这种服务类型是有负载均衡的,所以Kafka producer连接 leader节点时,由于有负载均衡,所以实际会连到其他节点上。把Kafka从Kubernetes的普通服务改为了无头服务(Headless),然后端口要从NodePort改为HostPort这种类型。这样就没有负载均衡,同时又能从外网访问Kafka节点。

还是不行

上述改动改完后,发现还是报 found some partitions to be leaderless 的错。实在不行,那就只能去zk上去看看到底broker是什么状态了。

root@kafka-0:/usr/share/easemon/kafka/bin# ./zookeeper-shell.sh 10.10.18.3
ls /brokers/ids
[1012, 1013, 1014]

可以看到 有三个broker,id分别是 1012,1013,1014。然后接着看看这些broker注册的IP地址(接着在 zookeeper-shell里执行命令):

get /brokers/ids/1012
{"jmx_port":-1,"timestamp":"1480404348685","endpoints":["PLAINTEXT://59.64.11.22:9092"],"host":"59.64.11.22","version":2,"port":9092}

看起来也符合预期,注册到 59.64.11.22的9092端口上了。
那就看看filebeat这个topic相关的信息吧:

/usr/share/easemon/kafka/bin/kafka-topics.sh --zookeeper localhost:2181 --describe --topic filebeat

Topic:filebeat    PartitionCount:3    ReplicationFactor:1    Configs:
    Topic: filebeat    Partition: 0    Leader: 1005    Replicas: 1005    Isr: 1005
    Topic: filebeat    Partition: 1    Leader: 1006    Replicas: 1006    Isr: 1006
    Topic: filebeat    Partition: 2    Leader: 1007    Replicas: 1007    Isr: 1007

等等,这里三个分区的leader怎么是1005,1006,1007呢,应该是1012,1013,1014才对。然后想到应该是Kafka的数据(通过配置项log.dirs指定位置)没有放到持久存储上,而是放到了Docker容器内,导致每次重新部署后,原来的数据都丢了,节点都注册到新的brokerID上去了。修改完成后,重新部署,发现Kafka上终于有数据了。

关于 Broker id

多说两句,我们环境中Kafka的Broker id是使用默认自动生成的id,所以都是1000以上的。而如果你的环境中如果是手工指定的,必须在1000以下,否则根据这篇文章,Kafka不报错,会直接退出。

自动创建Topic

auto.create.topics.enable 参数可以用来配置Kafka Server是否自动创建topic,但这个是针对Producer而言的,如果Consumer消费某个不存在的topic时,是不会触发自动创建的逻辑的。所以当Consumer消费某个不存在的topic时,由于具体的实现不一样,可能会出现报错的情况。

参考资料:

Kafka in a nutshell

posted on 2016-11-30 09:39 Jack47 阅读(...) 评论(...) 编辑 收藏

导航

统计

  • 随笔 - 60
  • 文章 - 0
  • 评论 - 90
  • 引用 - 0