Spring Boot 应用在部署到 Google App Engine 时无法连接到 Google Cloud SQL (PostgreSQL)

Posted

技术标签:

【中文标题】Spring Boot 应用在部署到 Google App Engine 时无法连接到 Google Cloud SQL (PostgreSQL)【英文标题】:Spring Boot app can't connect to Google Cloud SQL (PostgreSQL) when deploying to Google App Engine 【发布时间】:2020-02-19 01:43:43 【问题描述】:

我有一个基于 Gradle 的 Spring Boot(版本 2.1.6)应用,我正在部署到 Google App Engine。我创建了一个尝试连接的 Google Cloud SQL (PostgreSQL 11) 数据库实例。

我已按照此示例中的所有步骤操作:https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-postgres-sample。

当我在本地运行应用程序时,一切正常。但是当我将应用程序部署到 App Engine 时,它​​无法连接到数据库。这些是我得到的错误:

ERROR: (gcloud.app.deploy) Error Response: [9] 
Application startup error:
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:417) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1089) ~[google-http-client-1.30.1.jar!/:na]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:515) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:448) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:565) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.cloud.sql.core.CloudSqlInstance.fetchEphemeralCertificate(CloudSqlInstance.java:327) ~[jdbc-socket-factory-core-1.0.14.jar!/:na]
    ... 10 common frames omitted

2019-10-22 20:49:13.206  INFO 1 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
2019-10-22 20:49:13.474  INFO 1 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core 5.3.10.Final
2019-10-22 20:49:13.485  INFO 1 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2019-10-22 20:49:14.420  INFO 1 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations 5.0.4.Final
2019-10-22 20:49:15.392 ERROR 1 --- [           main] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'authenticationTokenFilter': Unsatisfied dependency expressed through field 'apiKeysService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'apiKeysServiceImpl' defined in URL [jar:file:/app.jar!/BOOT-INF/classes!/nl/storegear/sgapi/services/servicesimpl/ApiKeysServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'apiKeyRepository': Cannot create inner bean '(inner bean)#72e34f77' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#72e34f77': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
2019-10-22 20:49:15.466  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2019-10-22 20:49:15.473  WARN 1 --- [           main] o.a.c.loader.WebappClassLoaderBase       : The web application [ROOT] appears to have started a thread named [pool-1-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
 java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 java.lang.Thread.run(Thread.java:748)
2019-10-22 20:49:15.478  WARN 1 --- [           main] o.a.c.loader.WebappClassLoaderBase       : The web application [ROOT] appears to have started a thread named [pool-1-thread-2] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
...

这是我的application.properties 文件的样子:

...

spring.datasource.hikari.connectionTimeout=20000
spring.datasource.hikari.maximumPoolSize=5

spring.datasource.username=REDACTED
spring.datasource.password=REDACTED
spring.cloud.gcp.sql.database-name=REDACTED
spring.cloud.gcp.sql.instance-connection-name=REDACTED
spring.datasource.continue-on-error=true
spring.datasource.initialization-mode=always
spring.cloud.gcp.credentials.location=file:src/main/resources/REDACTED.json
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.jpa.show-sql=false

...

我认为这可能与 gcp 凭据 json 文件有关,但我不知道接下来要尝试什么。有什么想法吗?

【问题讨论】:

您能否详细介绍一下您的 Cloud SQL 配置?公共IP?私人的? SSL 激活? 【参考方案1】:

这里可能有两个问题。

1-

在本地测试应用程序时,它能够连接并获得身份验证,因为“application.properties”文件已正确配置,私钥的路径在“..credentials.location”属性中指定。部署 App Engine 时,尝试连接的不是应用程序,而是为应用程序提供服务的 App Engine instance itself。我怀疑在这种情况下会发生什么,App Engine 没有被告知去哪里获取驻留在“application.properties”Spring Boot 文件中的正确凭据。

In this tutorial,类似的集成是在 App Engine 上运行的 Spring Boot 应用程序连接到 Cloud SQL 实例时完成的。用于将 App Engine 指向凭据的方法是通过 Spring Profiles 持有 the credentials 以及配置“app.yaml”文件完成的,其中配置文件通过“spring_profiles_active”环境变量激活。

或者,可以在 Cloud SDK 中完成身份验证,如step 5 of the example you referenced 中所述。这是用于通过此方法获取凭据的the command,以及来自完整教程的here is an implementation example。

2-

Spring Boot 启动器使用 Tomcat as the default servlet containers。 App Engine 使用Jetty as servlet containers。部署 App Engine 的时候,如果不忽略 Tomcat 的依赖,会和 Jetty 的冲突。这可能是在执行 App Engine 部署时抛出“o.s.b.web.embedded.tomcat.TomcatStarter : Error started Tomcat context”错误时发生的情况。

这是关于如何排除这些依赖项的official Spring documentation,这是集成相同组件的完整教程中的implementation example。

【讨论】:

以上是关于Spring Boot 应用在部署到 Google App Engine 时无法连接到 Google Cloud SQL (PostgreSQL)的主要内容,如果未能解决你的问题,请参考以下文章

在 Google App Engine 中部署 Spring Boot gradle 应用

Google Cloud Spring Boot InvocationTargetException

部署失败:Google App Engine 上的 Kotlin Spring Boot 应用

如何在谷歌云中部署 Spring Boot 应用程序?

部署到 Google App Engine 时 Spring Boot CORS 配置不起作用

通过 Google Compute Shell 部署 Spring Boot 服务和 UI 后出现 CORS 错误