如何使用云运行服务帐户& GOOGLE_APPLICATION_CREDENTIALS在本地运行Docker容器?

r7xajy2e  于 2022-11-28  发布在  Docker
关注(0)|答案(1)|浏览(167)

我正在尝试在本地运行连接到云SQL数据库的Ktor应用程序的Docker容器。
这个容器稍后将被推送到Cloud Run,所以我在我的环境变量中使用了一个IAM服务帐户来进行身份验证。我已经研究这个问题一个星期了,但还没有找到答案。
我使用的步骤如下:
1.创建IAM服务帐户“foo-cloudrun@foo-foo-121212.iam.gserviceaccount.com
1.为Cloud Run和Cloud SQL设置所需的角色:

Cloud Build Service Agent
   Cloud Run Admin
   Cloud Run Service Agent
   Cloud SQL Client
   Cloud SQL Instance User
   Viewer

1.从IAM帐户创建新密钥并将其保存到桌面。
1.通过在CMD上运行以下命令激活并登录到服务帐户:

gcloud auth activate-service-account --key-file="C:\Users\user\Desktop\cloudrun-key.json"

1.将GOOGLE_APPLICATION_CREDENTIALS环境变量设置为密钥文件位置:

set GOOGLE_APPLICATION_CREDENTIALS="C:\Users\user\Desktop\cloudrun-key.json"

1.导航到我的应用程序文件夹并构建Docker映像:

docker build -t my-application .

1.启动映像:

docker run -p 8080:8080 my-application

这就是我得到一个模糊异常的地方:线程“main”中出现异常.
为了了解发生了什么,我尝试使用docker compose来查看是否有更好的想法。因此,我运行gradleshadowJar在build/libs目录中创建一个.jar文件,然后运行**“docker-compose up”**。
这就是我遇到这个异常的地方,这个异常让我很困惑,因为我已经将环境变量设置到了正确的位置:
引起原因:java.io. IO异常:应用程序默认身份证明不可用。如果在Google Compute Engine中运行,则这些身份证明可用。否则,必须定义环境变量GOOGLE_APPLICATION_ CREDENTIALS,使其指向定义身份证明的文件。有关详细信息,请参阅https://developers.google.com/accounts/docs/application-default-credentials
Dockerfile如下所示:

FROM gradle:7-jdk11 AS build
# Copy local code to the container image.
COPY --chown=gradle:gradle . /home/gradle/src
WORKDIR /home/gradle/src
# Build a release artifact.
RUN gradle shadowJar --no-daemon 

FROM openjdk:11
EXPOSE 8080:8080
RUN mkdir /app
# Copy the jar to the production image from the builder stage.
COPY --from=build /home/gradle/src/build/libs/*.jar /app/my-application-docker.jar
# Run the web service on container startup.
ENTRYPOINT  [ "java", "-jar", "/app/my-application-docker.jar" ]

docker-compose.yml文件:

services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      INSTANCE_CONNECTION_NAME: ${INSTANCE_CONNECTION_NAME}
      DB_IAM_USER: ${DB_IAM_USER}
      DB_USER: ${DB_USER}
      DB_NAME: ${DB_NAME}
      PORT: ${PORT}
      JDBC_DRIVER: ${JDBC_DRIVER}
      GOOGLE_APPLICATION_CREDENTIALS: ${GOOGLE_APPLICATION_CREDENTIALS}

环境文件:

JDBC_DRIVER=org.postgresql.Driver
DB_IAM_USER=foo-cloudrun@foo-foo-121212.iam
DB_NAME=my-database
INSTANCE_CONNECTION_NAME=foo-foo-121212:us-central1:database-sql-instance
PORT=8080
GOOGLE_APPLICATION_CREDENTIALS=C:\Users\user\Desktop\cloudrun-key.json

数据库工厂.kt文件,如this reference

object DatabaseFactory {

    private val INSTANCE_CONNECTION_NAME = System.getenv("INSTANCE_CONNECTION_NAME");
    private val DB_IAM_USER = System.getenv("DB_IAM_USER")
    private val DB_NAME = System.getenv("DB_NAME")

    fun init() {
        Database.connect(hikari())

        transaction {
            SchemaUtils.create(FirstTable)
            SchemaUtils.create(SecondTable)
        }
    }

    private fun hikari(): HikariDataSource {

        val config = HikariConfig()

        // IAM AUTHENTICATION
        config.driverClassName = System.getenv("JDBC_DRIVER")
        config.jdbcUrl = String.format("jdbc:postgresql:///%s", DB_NAME)
        config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory")
        config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME)
        config.addDataSourceProperty("enableIamAuth", "true")
        config.addDataSourceProperty("user", DB_IAM_USER)
        config.addDataSourceProperty("password", "password")
        config.addDataSourceProperty("sslmode", "disable")

        config.connectionTimeout = 10000 // 10s
        config.maximumPoolSize = 5
        config.minimumIdle = 3
        config.maxLifetime = 1800000; // 30 minutes
        config.isAutoCommit = false
        config.transactionIsolation = "TRANSACTION_REPEATABLE_READ"
        config.validate()

        // Initialize the connection pool using the configuration object.
        return HikariDataSource(config);

    }

    suspend fun <T> dbQuery(block: () -> T): T =
        withContext(Dispatchers.IO) {
            transaction {
                addLogger(StdOutSqlLogger)
                block()
            }
        }

}

我尝试过但失败的事情:

1.正在本地测试云运行服务。正在获取NullPointerException
第8080章:我的应用程序是什么?
如果您有任何问题,请与我们联系。如果您有问题,请与我们联系。
1.使用我的gmail用户帐户运行docker-composite up命令:

  • 取消设置环境变量(设置GOOGLE_APPLICATION_CREDENTIALS=)
  • gcloud验证登录(到gmail帐户)
  • gcloud auth应用程序-默认登录(将默认凭据设置为已知位置)
  • 再次运行docker run命令或docker-compose up

我得到的是:

  • ”从环境变量GOOGLE_APPLICATION_CREDENTIALS读取凭证文件时出错,值为“C:\Users\user\Desktop\cloudrun-key. json”:文件不存在。"*

1.通过系统属性手动设置Windows系统环境变量中的GOOGLE_APPLICATION_CREDENTIALS。再次出现相同错误。
任何帮助都很好,谢谢。

z9smfwbn

z9smfwbn1#

哇,这是一个很大的问题:-)

第一个以下命令是互斥的-ish:

这将使用服务帐户验证gcloud及其命令。当您运行gcloud something时,它将使用服务帐户作为其标识。

gcloud auth activate-service-account \
--key-file="C:\Users\user\Desktop\cloudrun-key.json"

这使得使用Google客户端库构建的代码能够使用应用程序默认凭据(ADC)来获取代码的运行时标识。它不会影响gcloud。它确实为您的代码提供了一个标识。
我假设set是Windows环境变量命令

set GOOGLE_APPLICATION_CREDENTIALS="C:\Users\user\Desktop\cloudrun-key.json"

第二个我承认我仔细考虑了这个问题,您需要在容器运行时将服务帐户装入容器。

使用dockerpodman等:

docker run \
--interactive --tty --rm
--publish=8080:8080 \
--volume=c:\Users\user\Desktop\cloudrun-key.json:/secrets/key.json \
--env=GOOGLE_APPLICATION_CREDENTIALS=/secrets/key.json \
my-application

使用Docker时,路径c:\Users\user\Desktop\cloudrun-key.json可能不正确。您可能需要将\替换为/。您可能需要对c:执行一些操作。我不知道它在Windows上是如何Map的。
--volume标志将您的主机上的密钥位置Map到(:容器中的位置。
在这种情况下,在容器中,密钥将是/secrets/key.json,因此这是用于GOOGLE_APPLICATION_CREDENTIALS的值。
使用docker-compose,它将类似于:

services:
  web:
    build: .
    ports:
      - "8080:8080"
    volumes:
    - "c:\Users\user\Desktop\cloudrun-key.json:/secrets/key.json"
    environment:
      INSTANCE_CONNECTION_NAME: ${INSTANCE_CONNECTION_NAME}
      DB_IAM_USER: ${DB_IAM_USER}
      DB_USER: ${DB_USER}
      DB_NAME: ${DB_NAME}
      PORT: ${PORT}
      JDBC_DRIVER: ${JDBC_DRIVER}
      GOOGLE_APPLICATION_CREDENTIALS: /secrets/key.json

第三当您在Cloud Run上运行容器时,它将利用ADC并自动使用您部署的Cloud Run服务的标识。Cloud Run服务具有默认标识,但您可以在部署时使用gcloud run deploy ... --service-account=${SERVICE_ACCOUNT}指定标识。

多亏了ADC,您不需要担心将密钥装入容器或指定环境变量,它就可以正常工作。

相关问题