Docker Compose 快速搭建 Kafka + AKHQ 同时支持 SSL 和非 SSL 访问

使用 Docker Compose 配置 Kafka 同时支持 SSL 和非 SSL 访问

在生产环境中,Kafka 安全性是非常重要的。通常,我们希望 Kafka 支持 SSL/TLS 加密访问,同时为了兼容一些测试或老客户端,也保留 非加密访问(PLAINTEXT)。本文将演示如何使用 Docker Compose 部署 Kafka,并配置 SSL。


Docker Compose 文件概览

下面是一个示例 docker-compose.yml

version: "3.9"

services:
  kafka:
    image: apache/kafka:latest
    container_name: kafka
    ports:
      - "9092:9092"   # PLAINTEXT(可选,可删)
      - "9094:9094"   # SSL 客户端访问
      - "9093:9093"   # KRaft Controller 内部通信(可选映射)
    environment:
      # === KRaft 配置 ===
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093

      # === Listeners 配置 ===
      KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9094,CONTROLLER://0.0.0.0:9093"
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://10.100.100.110:9092,SSL://10.100.100.110:9094"
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,SSL:SSL,CONTROLLER:PLAINTEXT"

      # === Broker 间通信 ===
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM: "none"

      # 单节点必须把所有内部 topic 副本数改成 1
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1

      # === SSL 配置 ===
      KAFKA_SSL_KEYSTORE_FILENAME: kafka.keystore.jks
      KAFKA_SSL_KEYSTORE_CREDENTIALS: keystore.pass
      KAFKA_SSL_KEY_CREDENTIALS: key.pass
      KAFKA_SSL_TRUSTSTORE_FILENAME: kafka.truststore.jks
      KAFKA_SSL_TRUSTSTORE_CREDENTIALS: truststore.pass
      KAFKA_SSL_CLIENT_AUTH: none

      # === 数据目录 ===
      KAFKA_LOG_DIRS: /var/lib/kafka/data

    volumes:
      - kafka-data:/var/lib/kafka/data
      - ./kafka-certs:/etc/kafka/secrets

  akhq:
    image: tchiotludo/akhq
    container_name: akhq
    ports:
      - "9000:8080"
    environment:
      AKHQ_CONFIGURATION: |
        akhq:
          connections:
            kafka:
              properties:
                bootstrap.servers: "10.100.100.110:9092"
    volumes:
      - ./kafka-certs:/etc/kafka/secrets
    depends_on:
      - kafka

volumes:
  kafka-data:

配置说明

Kafka Listener 配置

  • PLAINTEXT (9092):非加密访问,方便测试或老客户端使用。
  • SSL (9094):启用 SSL/TLS 访问,保证数据传输安全。
  • CONTROLLER (9093):KRaft 内部通信端口,不建议直接暴露给客户端。

配置关键参数:

KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9094,CONTROLLER://0.0.0.0:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://10.100.100.110:9092,SSL://10.100.100.110:9094"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "PLAINTEXT:PLAINTEXT,SSL:SSL,CONTROLLER:PLAINTEXT"
  • PLAINTEXT:// 对应非加密访问
  • SSL:// 对应加密访问
  • KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT 表示 broker 间通信使用 PLAINTEXT(可改成 SSL 以加密内部通信)

SSL 配置文件

  • kafka.keystore.jks:包含 Kafka broker 私钥和证书
  • kafka.truststore.jks:包含 CA 根证书
  • keystore.pass / key.pass / truststore.pass:存储密码

注意:确保 /etc/kafka/secrets 下存在这些文件,并且 Docker Compose volume 挂载正确。


AKHQ 配置

AKHQ 用于 Kafka Web UI 管理。此示例只连接 PLAINTEXT,你也可以改成 SSL:

bootstrap.servers: "10.100.100.110:9092"

要使用 SSL,需要增加:

security.protocol: SSL
ssl.truststore.location: /etc/kafka/secrets/kafka.truststore.jks
ssl.truststore.password: truststore.pass

启动命令

docker-compose up -d
  • Kafka broker 会监听 9092(PLAINTEXT)和 9094(SSL)
  • AKHQ 会监听 9000 端口

注意事项

  1. SSL 证书 SAN/IP

    • CNsubjectAltName 必须包含 Kafka 的实际 IP 或主机名,否则客户端会报 PKIX path building failed
  2. Broker 间通信

    • 默认 KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT 可以兼容 PLAINTEXT 客户端
    • 如果你希望 broker 间通信也加密,改成 SSL 并确保 KAFKA_ADVERTISED_LISTENERS 包含 SSL
  3. 文件挂载权限

    • Docker volume 权限必须正确,Kafka 容器需要读取 keystore/truststore

这样,你就能在 Docker Compose 下运行 既支持 SSL 又支持非 SSL 客户端访问的 Kafka 了,同时使用 AKHQ 监控管理 Kafka。


准备Kafka证书

本地有Java环境可以跳过(如果也是在docker需要进入其中 keytool 需要用)

docker run -it --name java-environment   -v "./kafka-certs:/certs"   eclipse-temurin:17-jdk   bash 

生成 CA(根证书)

openssl genrsa -out ca.key 4096 openssl req -x509 -new   -key ca.key   -days 3650   -out ca.crt   -subj "/CN=Kafka-CA"

生成 Kafka keystore

keytool -genkeypair -alias kafka -keyalg RSA -keystore kafka.keystore.jks -storepass changeit -keypass changeit -validity 3650 -dname "CN=10.100.100.110"

生成 CSR

keytool -certreq -alias kafka -keystore kafka.keystore.jks -file kafka.csr -storepass changeit

创建 SAN 扩展文件

cat > ext.cnf <<EOF
[v3_req]
subjectAltName = IP:10.100.100.110
EOF

用 CA 签发证书

openssl x509 -req -in kafka.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kafka.crt -days 3650 -extensions v3_req -extfile ext.cnf

导入 CA → keystore

keytool -importcert -alias CARoot -keystore kafka.keystore.jks -file ca.crt -storepass changeit -noprompt

导入 Kafka 证书

keytool -importcert -alias kafka -keystore kafka.keystore.jks -file kafka.crt -storepass changeit

创建 truststore

keytool -importcert -alias CARoot -keystore kafka.truststore.jks -file ca.crt  -storepass changeit -noprompt

创建 Kafka 需要的密码文件

echo changeit > keystore.pass
echo changeit > truststore.pass
echo changeit > key.pass

最终证书检查

keytool -list -v -keystore kafka.keystore.jks -storepass changeit

看到

Alias name: kafka
Entry type: PrivateKeyEntry
Certificate chain length: 2
Owner: CN=10.100.100.110
Issuer: CN=Kafka-CA

执行完docker,用 openssl 测试 Kafka SSL 端口

在 Kafka 宿主机或任意客户端

openssl s_client -connect 10.100.100.110:9094 -CAfile kafka-certs/ca.crt

成功标志

在输出的内容中看到Verify return code: 0 (ok)

为代码客户端准备证书

将 JKS 转换为 PKCS12 格式

keytool -importkeystore -srckeystore kafka.keystore.jks -destkeystore kafka.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass changeit -deststorepass changeit -srcalias kafka -destalias kafka

导出私钥(PEM 格式)

openssl pkcs12 -in kafka.p12 -nocerts -nodes -passin pass:changeit -out kafka.key

-nocerts → 只导出私钥
-nodes → 不加密输出(可选,如果你想用密码保护可以去掉)
输出 kafka.key

导出证书(PEM格式)

openssl pkcs12 -in kafka.p12 -nocerts -nodes -passin pass:changeit -out kafka.key

-clcerts → 只导出客户端证书,不包含 CA
-nokeys → 不导出私钥
输出 kafka.crt 上面其实已经有了

posted @ 2025-12-22 18:52  Windchase  阅读(23)  评论(0)    收藏  举报