是否可以将 Spring Boot 应用程序部署到 App Engine 并连接到数据库?

Posted

技术标签:

【中文标题】是否可以将 Spring Boot 应用程序部署到 App Engine 并连接到数据库?【英文标题】:Is it possible to deploy a Spring Boot app to App Engine and connect to the database? 【发布时间】:2020-02-18 06:00:41 【问题描述】:

我觉得我在这里兜圈子,所以,请多多包涵。我想将我的 Spring Boot 应用程序部署到 App Engine,但与我的 simple sample Google provides 不同,它需要一个数据库,这意味着凭据。我在 Google App Engine 上的 Standard 上运行 Java 11。

我设法通过在application.properties 中使我的应用成功连接:

spring.datasource.url=jdbc:postgresql://google/recruiters_wtf?cloudSqlInstance=recruiters-wtf:europe-west2:recruiters-wtf&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=the_user&password=monkey123

问题是我不想向存储库提交任何凭据,因此这是不可接受的。我可以使用环境变量,但是我必须在app.yaml 文件中定义它们。我要么保留一个部署所需的未提交的app.yaml 文件,这很麻烦,要么我提交它,然后我回到第一方,将凭据提交到存储库。

显然Google App Engine cannot have environment variables defined in any other way (unlike Heroku),这是否意味着在不使用一些不安全/繁琐的做法的情况下,不可能将 Spring Boot 应用程序部署到 App Engine 并让它连接到数据库?我觉得我在这里遗漏了一些东西。

【问题讨论】:

【参考方案1】:

根据我对您所描述内容的理解,您希望实质上将在 Google App Engine 上运行的 Spring Boot 应用程序连接到数据库,而不会暴露敏感信息。如果是这样的话,我发现Cloud KMS 为用户提供了秘密管理的能力。具体来说,在构建或运行时需要小块敏感数据的应用程序称为secrets。这些秘密可以使用对称密钥进行加密和解密。在您的情况下,您可以将数据库凭据存储为机密。您可以找到有关加密/解密秘密here 的过程的更多详细信息。

他们目前是three ways to manage secrets:

    在代码中存储机密,使用来自 Cloud KMS 的密钥进行加密。此解决方案是在应用层实现机密。 将机密存储在 Cloud Storage 的存储桶中,静态加密。您可以使用 Cloud Storage: Bucket 来存储您的数据库 凭据,还可以授予该存储桶特定的服务帐户。 该解决方案允许系统分离。在这种情况下 代码存储库被破坏,您的秘密可能仍然存在 受到保护。 使用第三方秘密管理系统。

就存储机密本身而言,我发现here 概述的以下步骤对此非常有用。本指南将引导用户在 Cloud Storage 存储分区中设置和存储机密。 秘密在应用层使用来自 Cloud KMS 的加密密钥进行加密。 鉴于您的用例,这将是一个很好的选择,因为您的秘密将存储在存储桶中,而不是 app.yaml 文件中.此外,存储在存储桶中的密钥将授予您使用服务帐户角色限制对其的访问权限。

本质上,您的应用需要对 Google Cloud Storage 执行 API 调用才能下载包含密钥的 KMS 加密文件。然后它将使用 KMS 生成的密钥来解密文件,以便能够读出密码并使用它来手动连接到数据库。添加这些额外的步骤将实现更多的安全层,这就是 Cloud SQL 的 Google 示例存储库中的“'Note: Saving credentials in environment variables is convenient, but not secure - consider a more secure solution such as Cloud KMS to help keep secrets safe.'”中提到的整个想法。

我希望这会有所帮助!

【讨论】:

但是如何从 KMS 到连接数据库,再到连接 Spring Boot JPA? 我找到了以下社区教程here,用于连接带有 Cloud SQL+GAE:flex 的 Spring boot。我相信这将为您提供有关使用 Spring Datasource 获取数据库凭据的必要步骤。之后,如果您仍然对使用 KMS 来增加安全性感兴趣,您可以关注此guide,了解如何使用存储桶进行设置。获得密钥后,您可以将其用作凭据。【参考方案2】:

假设您可以使用 KMS 或 GCS 获取凭据,您可以在 Spring Boot 中以编程方式设置它们。看这篇文章

Configure DataSource programmatically in Spring Boot

【讨论】:

【参考方案3】:

正如您所指出的,除了使用 app.yaml 文件之外,没有内置方法可以在 App Engine 中设置环境变量。我不是 Spring Boot 方面的专家,但除非您可以在 application.properties 评估之前设置/覆盖一些挂钩以从 Java 代码初始化 env var,否则您需要在构建时设置这些。

选项 1:使用 Cloud Build

我知道您并不是真的热衷于使用 Cloud Build,但就是这样。

首先,按照说明 here,(在 KMS 中创建 KeyRing 和 CryptoKey 并授予对 Cloud Build 服务帐户的访问权限后)从您的终端使用 KMS 加密您的环境变量并取回其 base64 表示:

echo -n $DB_PASSWORD | gcloud kms encrypt \
  --plaintext-file=- \  # - reads from stdin
  --ciphertext-file=- \  # - writes to stdout
  --location=global \
  --keyring=[KEYRING-NAME] \
  --key=[KEY-NAME] | base64

接下来,假设您有一个像这样的app.yaml 文件:

runtime: java11
instance_class: F1

env_variables:
  USER:  db_user
  PASSWORD: db_passwd

创建一个cloudbuild.yaml 文件来定义您的build steps:

steps:
# replace env vars in app.yaml by their values from KMS
- name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args: ['-c', 'sed -i "s/TEST/$$PASSWORD/g" src/main/appengine/app.yaml']
  secretEnv: ['PASSWORD']
- name: 'gcr.io/cloud-builders/mvn'
  args: ['clean']
- name: 'gcr.io/cloud-builders/mvn'
  args: ['package']
- name: 'gcr.io/cloud-builders/mvn'
  args: ['appengine:deploy']
  timeout: '1600s'
secrets:
- kmsKeyName: projects/<PROJECT-ID>/locations/global/keyRings/<KEYRING_NAME>/cryptoKeys/<KEY_NAME>
  secretEnv:
    PASSWORD: <base64-encoded encrypted password>
timeout: '1600s'

然后您可以通过运行以下命令来部署您的应用程序:

gcloud builds submit .

这种方法的优点是你本地的app.yaml文件只包含占位符值,可以安全地提交。或者,您甚至可以将此构建设置为每次提交到远程存储库时自动触发。

选项 2:在本地使用 bash 脚本

您可以创建一个 bash 脚本来替换 app.yaml 中的值、部署应用程序并立即删除这些值,而不是运行 mvn appengine:deploy 来部署您的应用程序。类似于:

#!/bin/bash
sed -i "s/db_passwd/$PASSWORD/g" src/main/appengine/app.yaml'
mvn appengine:deploy
sed -i "s/$PASSWORD/db_passwd/g" src/main/appengine/app.yaml'

并执行该 bash 脚本,而不是运行 maven 命令。

【讨论】:

【参考方案4】:

我可能会建议将Spring Cloud ConfigGoogle Runtime Configuration API 与您的Spring Boot 应用程序结合使用。

Spring Cloud Config 是负责从远程位置检索配置并在初始化/启动期间将该配置提供给 Spring Boot 的组件。远程位置可以是任何东西。例如,GIT 存储库被广泛使用,但对于您的用例,您可以将配置存储在 Google Runtime Configuration API 中。

所以示例流程将是这样的。

Your Spring Boot App(with Config Client) --> Spring Cloud Config Server --> Google Runtime Configuration API

这要求您将 Spring Cloud Config Server 作为 GCP 中的另一个应用程序启动,并启用从您的许多 Spring Boot 应用程序到与 Google Runtime API 交互的集中式配置服务器的通信。

一些文档链接。

https://cloud.spring.io/spring-cloud-config/reference/html/

https://docs.spring.io/spring-cloud-gcp/docs/1.1.0.M1/reference/html/_spring_cloud_config.html

https://cloud.google.com/deployment-manager/runtime-configurator/reference/rest/

Spring Cloud GCP 配置示例示例。

https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-config-sample

【讨论】:

【参考方案5】:

您可以尝试在应用程序属性中加密密码。看看http://mbcoder.com/spring-boot-how-to-encrypt-properties-in-application-properties/

【讨论】:

【参考方案6】:

您应该使用 GCP 的密钥管理服务:https://cloud.google.com/kms/

【讨论】:

【参考方案7】:

我们使用了几个选项:

1 - 没有 Docker

您可以通过环境或控制台使用这种方法吗?

我们使用(Spring Boot 定义的)环境变量。这是执行此操作的默认方式:

SPRING_DATASOURCE_USERNAME=myusername
SPRING_DATASOURCE_PASSWORD=mypassword

根据 Spring Boot 规范,这将否决 application.properties 变量的任何值。因此,您可以为开发指定默认用户名和密码,并在(测试或)生产部署时将其否决。

另一种方式记录在this post:

spring.datasource.url = $OPENSHIFT_mysql_DB_HOST:$OPENSHIFT_MYSQL_DB_PORT/"nameofDB"
spring.datasource.username = $OPENSHIFT_MYSQL_DB_USERNAME
spring.datasource.password = $OPENSHIFT_MYSQL_DB_PASSWORD

2 - 通过控制台实现类似 Docker 的方法

this post 中描述了您的问题。默认解决方案是使用“秘密”。它们是专门为此而设计的。您可以在构建和部署应用程序的过程中将任何机密(作为文件)转换为环境变量。这是一个在许多帖子中描述的简单操作。寻找更新的方法。

【讨论】:

【参考方案8】:

我觉得这很容易做到。我在我的 Spring Boot 应用程序中使用了以下属性,这些属性连接到公共的 google mysql 实例。

spring.datasource.url=jdbc:postgresql://IP:5432/yourdatabasename
spring.datasource.platform = postgres
spring.datasource.username = youruser
spring.datasource.password = yourpass

其余的将由 JpaRepository 处理。我希望这会对你有所帮助。

如果需要任何帮助,请告诉我。

【讨论】:

作者提到他不想在代码中存储密码。 这不是代码,而是应用程序属性文件,始终仅用于此目的。

以上是关于是否可以将 Spring Boot 应用程序部署到 App Engine 并连接到数据库?的主要内容,如果未能解决你的问题,请参考以下文章

将Spring-boot Backbone应用程序部署到Pivotal Cloud Foundry中

未能将 Spring Boot 应用程序部署到 WildFly

将 Spring Boot 部署到 Wildfly 10

将 Spring Boot 应用程序部署到 AWS beanstalk

将 WAR 部署到 Tomcat(Spring Boot + Angular)

将 Spring Boot 应用程序部署到 AWS Beanstalk