kafka3.7.2集群搭建及SASL认证

279 阅读4分钟

==阅读前说明==

  • 下面用到的confi和bin目录都是相对于kafka目录来进行的
  • ./xxx.sh都是相对kafka/bin目录执行的
  • 请自行将我的kafka地址替换为你的正确地址包括执行命令时所使用到的地址.

1. 修改config/kraft/server.properties

############################# Server Basics #############################

# The role of this server. Setting this puts us in KRaft mode
#角色
process.roles=broker,controller

# The node id associated with this instance's roles
#id 这个id必须要和controller.quorum.voters的对应上
#比如这个controller.quorum.voters配到了3,就必须有三台分别配置了node.id=1,node.id=2,node.id=3
node.id=1

# The connect string for the controller quorum
controller.quorum.voters=1@192.168.1.11:19093,2@192.168.1.12:19093,3@192.168.1.13:19093

############################# Socket Server Settings #############################

# 这个地方要改成当前节点的IP地址
listeners=SASL_PLAINTEXT://192.168.1.11:19092,CONTROLLER://192.168.1.11:19093
# Name of listener used for communication between brokers.
inter.broker.listener.name=SASL_PLAINTEXT


# 这个地方要改成当前节点的IP地址
advertised.listeners=SASL_PLAINTEXT://192.168.1.11:19092

controller.listener.names=CONTROLLER

# Maps listener names to security protocols, the default is for them to be the same. See the config documentation for more details
#CONTROLLER:SASL_PLAINTEXT需要修改
listener.security.protocol.map=CONTROLLER:SASL_PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
# 设置必须授权才能用
allow.everyone.if.no.acl.found=false
# The number of threads that the server uses for receiving requests from the network and sending responses to the network
num.network.threads=3

# The number of threads that the server uses for processing requests, which may include disk I/O
num.io.threads=8

# The send buffer (SO_SNDBUF) used by the socket server
socket.send.buffer.bytes=102400

# The receive buffer (SO_RCVBUF) used by the socket server
socket.receive.buffer.bytes=102400

# The maximum size of a request that the socket server will accept (protection against OOM)
socket.request.max.bytes=104857600


############################# Log Basics #############################

# A comma separated list of directories under which to store log files
# 这个路径很重要
log.dirs=/data/kafka_3.7.2.sasl/logs


num.partitions=1

num.recovery.threads.per.data.dir=1


offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

############################# Log Flush Policy #############################


log.retention.hours=168

# A size-based retention policy for logs. Segments are pruned from the log unless the remaining
# segments drop below log.retention.bytes. Functions independently of log.retention.hours.
#log.retention.bytes=1073741824

# The maximum size of a log segment file. When this size is reached a new log segment will be created.
log.segment.bytes=1073741824

# The interval at which log segments are checked to see if they can be deleted according
# to the retention policies
log.retention.check.interval.ms=300000
# 认证方式,用了最简单的PLAIN,缺点是不能动态添加用户
sasl.mechanism.inter.broker.protocol=PLAIN
sasl.enabled.mechanisms=PLAIN
sasl.mechanism=PLAIN
# 禁用了自动创建topic
auto.create.topics.enable = false
# 设置必须授权才能用
allow.everyone.if.no.acl.found=false
# 设置超级管理员
super.users=User:admin
# 这个是3.2.0版本新引入的认证方式,可以参考 https://cwiki.apache.org/confluence/display/KAFKA/KIP-801%3A+Implement+an+Authorizer+that+stores+metadata+in+__cluster_metadata
# 这个脱离了使用zookeeper进行认证
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer
# 集群间认证时用的认证方式
sasl.mechanism.controller.protocol=PLAIN

1.2 注意事项

下面这三个配置每一个节点都要改,其他的不用改

# 这个每一个节点都要不一样,要注意
node.id=1

############################# Socket Server Settings #############################

# 这个地方要改成当前节点的IP地址
listeners=SASL_PLAINTEXT://192.168.1.11:19092,CONTROLLER://192.168.1.11:19093



# 这个地方要改成当前节点的IP地址
advertised.listeners=SASL_PLAINTEXT://192.168.1.11:19092

2. 在config下新增两个文件

KafkaServer {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="password"
# 这个是必须有的,哪怕是要创建多个账号来做读写账号分离这个也要
# user_是固定前缀, admin是账号名,后面的是密码
# 如果需要多个账号,和权限分离看最下面
user_admin="password";

};

3.kafka_client_jaas.properties 客户端用来登录用的

# 指定 SASL 认证机制和协议
security.protocol=SASL_PLAINTEXT
sasl.mechanism=PLAIN

# 配置用户名和密码(替换为你的实际用户名和密码)
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
    username="admin" \
    password="password";

3. 修改bin/kafka-server-start.sh 在29行左右

# 所有节点都要改
export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G -Djava.security.auth.login.config=/data/kafka_3.7.2.sasl/config/kafka_server_jaas.conf"

4. 初始化数据目录 数据目录为log.dirs的值

bin/kafka-storage.sh random-uuid
# 会输出a1HxK892T6iV3wOHuHm1BA类似的一个随机ID,直接用这个也可以
# 在所有节点都执行一遍下面这句,会创建log.dirs指定的目录并初始化内容
bin/kafka-storage.sh format -t a1HxK892T6iV3wOHuHm1BA -c /data/kafka_3.7.2.sasl/config/kraft/server.properties

5. 依次启动节点

nohup /data/kafka_3.7.2.sasl/bin/kafka-server-start-sasl.sh /data/kafka_3.7.2.sasl/config/kraft/server.properties >/data/kafka_3.7.2.sasl/kfk.out 2>&1 &

6. 校验

# 创建topic
./kafka-topics.sh --bootstrap-server 192.168.1.11:19092 --create --topic test1 --partitions 3 --replication-factor 2 --command-config ../config/kafka_client_jaas.properties

# 查看所有topic
./kafka-topics.sh --bootstrap-server 192.168.1.11:19092 --list --command-config ../config/kafka_client_jaas.properties

# 生产者
./kafka-console-producer.sh broker-list --bootstrap-server 192.168.1.11:19092   --topic test1  --producer.config ../config/kafka_client_jaas.properties

# 消费者
./kafka-console-consumer.sh broker-list --bootstrap-server 192.168.1.11:19092   --topic test1  --consumer.config ../config/kafka_client_jaas.properties

补充

1. 多账号及权限设置

KafkaServer {
    org.apache.kafka.common.security.plain.PlainLoginModule required
    username="admin"
    password="admin的密码"
    user_admin="admin的密码"
    user_a="a的密码" # 读
    user_b="b的密码"; # 写
};

# 给指定主题指定用户设置读写权限
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --add --allow-principal User:a --operation Read --topic test1 --command-config ../config/kafka_client_jaas.properties
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --add --allow-principal User:b --operation Read --operation Write --topic test1 --command-config ../config/kafka_client_jaas.properties

# 删除指定主题指定用户的读权限
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --remove --allow-principal User:a --operation Read --topic test1 --command-config ../config//data/kafka_3.7.2.sasl

# 查看已有的 ACL 配置
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --list --command-config ../config/kafka_client_jaas.properties

# 查看特定主题的 ACL 配置
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --list --topic test1 --command-config /data/kafka_3.7.2.sasl/config/kafka_client_jaas.properties

# 查看特定用户的 ACL 配置:
./kafka-acls.sh --bootstrap-server 192.168.1.11:19092 --list --principal User:a --command-config /data/kafka_3.7.2.sasl/config/kafka_client_jaas.properties

2. java8生产消费

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;

import java.time.Duration;
import java.util.Collections;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {
        producer();	# 生产要确认主题是否存在
//      consumer();
    }

    private static void producer(){
        KafkaProducer<String, String> producer = createProducer();
        // 发送消息到 Kafka
        producer.send(new ProducerRecord<>("test1", "key", "value"), (metadata, exception) -> {
            if (exception != null) {
                exception.printStackTrace();
            } else {
                System.out.println("Message sent to topic " + metadata.topic() + " with offset " + metadata.offset());
            }
        });
        producer.flush();
        producer.close();
    }

    private static void consumer(){
        KafkaConsumer<String, String> consumer = createConsumer();

        consumer.subscribe(Collections.singletonList("test1"));

        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
            for (ConsumerRecord<String, String> record : records) {
                System.out.println("Consumed message: " + record.value() + " from topic: " + record.topic());
            }
        }
    }


    public static KafkaProducer<String, String> createProducer() {
        Properties props = new Properties();

        // Kafka broker地址
        props.put("bootstrap.servers", "192.168.1.11:19092");

        // 使用SASL认证
        props.put("security.protocol", "SASL_PLAINTEXT"); // 或者 SASL_SSL 如果你启用了SSL
        props.put("sasl.mechanism", "PLAIN"); // 或 SCRAM-SHA-256, SCRAM-SHA-512

        // 指定JAAS配置文件路径
        props.put("sasl.jaas.config",
                "org.apache.kafka.common.security.plain.PlainLoginModule required " +
                        "username=\"admin\" password=\"password\";");

        // 生产者设置
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        return new KafkaProducer<>(props);
    }

    public static KafkaConsumer<String, String> createConsumer() {
        Properties props = new Properties();

        // Kafka broker地址
        props.put("bootstrap.servers", "192.168.1.11:19092");

        // 使用SASL认证
        props.put("security.protocol", "SASL_PLAINTEXT"); // 或者 SASL_SSL 如果你启用了SSL
        props.put("sasl.mechanism", "PLAIN"); // 或 SCRAM-SHA-256, SCRAM-SHA-512

        // 指定JAAS配置文件路径
        props.put("sasl.jaas.config",
                "org.apache.kafka.common.security.plain.PlainLoginModule required " +
                        "username=\"admin\" password=\"password\";");

        // 消费者设置
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "test-consumer-group");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        return new KafkaConsumer<>(props);
    }
}