如何解决Quarkus原生容器连接ElasticSearch的连接拒绝问题
问题
我把集成了hibernate-orm-panache、resteasy-reactive、PostgreSQL的Quarkus应用打包成Docker容器后,能正常连接本地PostgreSQL,但开发模式下正常的ElasticSearch集成,在原生镜像容器中启动时出现「Elasticsearch request failed:Connection refused」错误。配置里quarkus.elasticsearch.hosts设为localhost:9200,先启动ElasticSearch集群再启动Quarkus也没用。
配置文件
quarkus.datasource.db-kind=postgresql quarkus.datasource.username=postgres quarkus.datasource.password=postgrespw quarkus.datasource.jdbc.url=jdbc:postgresql://192.168.5.45:5432/b2data #quarkus.hibernate-orm.log.sql=true quarkus.hibernate-search-orm.elasticsearch.version=7 quarkus.hibernate-search-orm.automatic-indexing.synchronization.strategy=sync quarkus.elasticsearch.hosts = localhost:9200 #quarkus.elasticsearch.port = 9200 #quarkus.elasticsearch.devservices.enabled = false
依赖
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm-panache</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-search-orm-elasticsearch</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-postgresql</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> </dependencies>
错误日志
2022-11-03 15:10:05,879 ERROR [org.hib.sea.eng.rep.spi.RootFailureCollector] (JPA Startup Thread: <default>) HSEARCH000521: Hibernate Search encountered a failure during bootstrap; continuing for now to list all problems, but the process will ultimately be aborted. Context: default backend Failure:: org.hibernate.search.util.common.SearchException: HSEARCH400080: Unable to detect the Elasticsearch version running on the cluster: HSEARCH400007: Elasticsearch request failed: Connection refused Request: GET with parameters {} Response: (no response) at org.hibernate.search.backend.elasticsearch.client.impl.ElasticsearchClientUtils.getElasticsearchVersion(ElasticsearchClientUtils.java:59) at org.hibernate.search.backend.elasticsearch.impl.ElasticsearchLinkImpl.initVersion(ElasticsearchLinkImpl.java:200) at org.hibernate.search.backend.elasticsearch.impl.ElasticsearchLinkImpl.onStart(ElasticsearchLinkImpl.java:142) at org.hibernate.search.backend.elasticsearch.impl.ElasticsearchBackendImpl.start(ElasticsearchBackendImpl.java:148) at org.hibernate.search.engine.common.impl.BackendNonStartedState.start(BackendNonStartedState.java:48) at org.hibernate.search.engine.common.impl.SearchIntegrationPartialBuildStateImpl$SearchIntegrationFinalizerImpl.finalizeIntegration(SearchIntegrationPartialBuildStateImpl.java:174) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationPartialBuildState.doBootSecondPhase(HibernateOrmIntegrationPartialBuildState.java:75) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationBooterImpl.bootNow(HibernateOrmIntegrationBooterImpl.java:176) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateSearchSessionFactoryObserver.sessionFactoryCreated(HibernateSearchSessionFactoryObserver.java:41) at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:402) at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.build(FastBootEntityManagerFactoryBuilder.java:74) at io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.createEntityManagerFactory(FastBootHibernatePersistenceProvider.java:72) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:80) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55) at io.quarkus.hibernate.orm.runtime.JPAConfig$LazyPersistenceUnit.get(JPAConfig.java:165) at io.quarkus.hibernate.orm.runtime.JPAConfig$1.run(JPAConfig.java:66) at java.lang.Thread.run(Thread.java:833) at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:705) at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202) Caused by: org.hibernate.search.util.common.SearchException: HSEARCH400007: Elasticsearch request failed: Connection refused Request: GET with parameters {} Response: (no response) at org.hibernate.search.backend.elasticsearch.client.impl.ElasticsearchClientUtils.tryGetElasticsearchVersion(ElasticsearchClientUtils.java:83) at org.hibernate.search.backend.elasticsearch.client.impl.ElasticsearchClientUtils.getElasticsearchVersion(ElasticsearchClientUtils.java:56) ... 22 more Caused by: org.hibernate.search.util.common.SearchException: Connection refused at org.hibernate.search.util.common.impl.Throwables.toRuntimeException(Throwables.java:33) at org.hibernate.search.util.common.impl.Futures.unwrappedExceptionJoin(Futures.java:196) at org.hibernate.search.backend.elasticsearch.client.impl.ElasticsearchClientUtils.tryGetElasticsearchVersion(ElasticsearchClientUtils.java:67) ... 23 more Caused by: java.net.ConnectException: Connection refused at com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VA_LIST_ConnectException_constructor_026ed3e065cc052585fca43de83265b2d1381f28(JNIJavaCallWrappers.java:0) at sun.nio.ch.Net.pollConnect(Net.java) at sun.nio.ch.Net.pollConnectNow(Net.java:672) at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:946) at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvent(DefaultConnectingIOReactor.java:174) at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvents(DefaultConnectingIOReactor.java:148) at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:351) at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:221) at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64) ... 3 more 2022-11-03 15:10:05,882 ERROR [io.qua.run.Application] (main) Failed to start application (with profile prod): org.hibernate.search.util.common.SearchException: HSEARCH000520: Hibernate Search encountered failures during bootstrap. Failures: default backend: failures: - HSEARCH400080: Unable to detect the Elasticsearch version running on the cluster: HSEARCH400007: Elasticsearch request failed: Connection refused Request: GET with parameters {} Response: (no response) at org.hibernate.search.engine.reporting.spi.RootFailureCollector.checkNoFailure(RootFailureCollector.java:53) at org.hibernate.search.engine.common.impl.SearchIntegrationPartialBuildStateImpl$SearchIntegrationFinalizerImpl.finalizeIntegration(SearchIntegrationPartialBuildStateImpl.java:177) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationPartialBuildState.doBootSecondPhase(HibernateOrmIntegrationPartialBuildState.java:75) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateOrmIntegrationBooterImpl.bootNow(HibernateOrmIntegrationBooterImpl.java:176) at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147) at org.hibernate.search.mapper.orm.bootstrap.impl.HibernateSearchSessionFactoryObserver.sessionFactoryCreated(HibernateSearchSessionFactoryObserver.java:41) at org.hibernate.internal.SessionFactoryObserverChain.sessionFactoryCreated(SessionFactoryObserverChain.java:35) at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:402) at io.quarkus.hibernate.orm.runtime.boot.FastBootEntityManagerFactoryBuilder.build(FastBootEntityManagerFactoryBuilder.java:74) at io.quarkus.hibernate.orm.runtime.FastBootHibernatePersistenceProvider.createEntityManagerFactory(FastBootHibernatePersistenceProvider.java:72) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:80) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55) at io.quarkus.hibernate.orm.runtime.JPAConfig$LazyPersistenceUnit.get(JPAConfig.java:165) at io.quarkus.hibernate.orm.runtime.JPAConfig$1.run(JPAConfig.java:66) at java.lang.Thread.run(Thread.java:833) at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:705) at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
解决方案
核心问题是Docker容器内的localhost指向容器自身网络,而非宿主机,所以你的Quarkus容器在尝试连接localhost:9200时,其实是在容器内部找ElasticSearch,自然会连接失败。以下是几种可行的解决方法:
方法1:替换localhost为宿主机IP
直接把配置中的quarkus.elasticsearch.hosts改成宿主机的IP地址(和你连接PostgreSQL用的192.168.5.45一致):
quarkus.elasticsearch.hosts = 192.168.5.45:9200
注意:要确保ElasticSearch的配置中network.host不是仅绑定localhost,需要允许外部访问(比如设为0.0.0.0),否则宿主机IP也无法访问。
方法2:用Docker Compose统一管理容器网络
如果用Docker Compose部署ElasticSearch和Quarkus,两者会自动加入同一个默认网络,此时可以用服务名作为主机名访问:
- 编写
docker-compose.yml:
version: '3.8' services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 environment: - discovery.type=single-node - ES_JAVA_OPTS=-Xms512m -Xmx512m ports: - "9200:9200" quarkus-app: build: . environment: - QUARKUS_ELASTICSEARCH_HOSTS=elasticsearch:9200 - QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://192.168.5.45:5432/b2data ports: - "8080:8080" depends_on: - elasticsearch
- 修改Quarkus配置,或者通过环境变量覆盖(如上),此时
elasticsearch就是ElasticSearch容器的主机名,无需IP地址。
方法3:使用Docker主机网络模式(快速测试用,不推荐生产)
启动Quarkus容器时加上--network host参数,让容器直接使用宿主机的网络栈,此时容器内的localhost就等同于宿主机的localhost:
docker run --network host your-quarkus-image
这种方法会破坏容器的网络隔离性,生产环境不建议使用,但可以快速验证问题是否来自网络隔离。
内容的提问来源于stack exchange,提问作者ko.alen

