Docker Compose部署Web应用连接Kafka失败问题排查
问题
我尝试用Docker Compose将应用容器化,启动ZooKeeper、Kafka和humanresources三个容器。我的docker-compose配置如下:
version: '3' services: zookeeper: image: wurstmeister/zookeeper ports: - "2181:2181" kafka: image: wurstmeister/kafka ports: - "9092:9092" depends_on: - zookeeper healthcheck: test: nc -z localhost 9092 || exit -1 start_period: 15s interval: 5s timeout: 10s retries: 10 environment: KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false' humanresources: image: univer-anu4/humanresources:latest ports: - "8081:8081" depends_on: kafka: condition: service_healthy deploy: restart_policy: condition: on-failure delay: 10s max_attempts: 3 window: 30s
每次执行docker compose up时,都会出现如下错误:
compose-humanresources-1 | 2022-11-19 09:14:59.941 WARN 1 --- [| adminclient-1] org.apache.kafka.clients.NetworkClient : [AdminClient clientId=adminclient-1] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.
但如果从Docker Compose中移除humanresources服务,仅启动ZooKeeper和Kafka容器,然后在IntelliJ IDEA中启动应用却能正常运行。请问这是什么原因?
我的application.properties配置如下:
spring.h2.console.enabled=true spring.datasource.url=jdbc:h2:mem:DB spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.kafka.template.default-topic=pad2 eureka.client.service-url.defaultZone = http://localhost:7080/eureka/ kafka.bootstrapAddress = localhost:9092 spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.generate-ddl=true spring.jpa.hibernate.ddl-auto=update spring.flyway.locations=classpath:db/migration spring.flyway.enabled=true spring.application.name=hr eureka.instance.prefer-ip-address=true eureka.client.register-with-eureka=true eureka.client.fetch-registry=true eureka.instance.lease-renewal-interval-in-seconds=30 spring.kafka.consumer.group-id=${random.uuid}
原因与解决方案
核心原因
- 容器网络隔离限制:Docker Compose的默认网络里,每个容器都是独立节点,
localhost在humanresources容器中指向容器自身,而非宿主机或Kafka容器。本地IDEA启动应用时,localhost指向宿主机,此时Kafka端口已映射到宿主机9092,所以能正常连接。 - Kafka地址配置错误:当前Kafka的
KAFKA_ADVERTISED_HOST_NAME设为127.0.0.1,会导致Kafka向客户端返回自身地址为127.0.0.1,容器内应用拿到地址后会尝试连接自身的9092端口,必然失败。
解决步骤
1. 修改Kafka的环境变量配置
调整docker-compose.yml中Kafka的环境变量,同时支持容器内部和宿主机的连接:
kafka: # 保留原有其他配置 ports: - "9092:9092" - "9093:9093" environment: KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9093,OUTSIDE://localhost:9092 KAFKA_LISTENERS: INSIDE://0.0.0.0:9093,OUTSIDE://0.0.0.0:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
KAFKA_ADVERTISED_LISTENERS:对外宣告的监听地址,INSIDE供容器内部使用(用服务名kafka作为地址),OUTSIDE供宿主机使用。KAFKA_LISTENERS:Kafka实际监听的端口,9093用于容器内部通信,9092映射到宿主机。
2. 更新应用的Kafka连接配置
修改application.properties中的Kafka地址,让容器内应用通过服务名连接Kafka:
kafka.bootstrapAddress = kafka:9093 spring.kafka.bootstrap-servers = ${kafka.bootstrapAddress}
3. 调整Kafka的健康检查规则
因为Kafka现在监听内部端口9093,健康检查需要对应修改:
kafka: # 保留原有其他配置 healthcheck: test: nc -z localhost 9093 || exit -1 start_period: 15s interval: 5s timeout: 10s retries: 10
4. 重新部署容器
执行以下命令重启服务:
docker compose down docker compose up -d
修改完成后,容器内的humanresources应用会通过kafka:9093连接Kafka,宿主机本地应用依然可以用localhost:9092连接,两种场景都能正常运行。
内容的提问来源于stack exchange,提问作者Gumaniuc Alexandru
相关产品推荐
相关产品推荐

