【问题标题】:Google Kubernetes Engine to Cloud SQLGoogle Kubernetes Engine 到 Cloud SQL
【发布时间】:2020-11-11 09:10:28
【问题描述】:

我目前正在尝试学习使用 Google Kubernetes Engine 和 Google Cloud SQL。为此,我在我的 Visual Studio 2019 中创建了一个 AspnetCore 3.1 Web Api 项目,用作培训项目。

目前我能够做到以下几点。

我想要做的如下。

根据我对 Cloud SQL 的理解,最好始终通过代理访问它,因为它更安全,这就是我想要 sidecar 的原因。然而,为了让代理工作,我需要保存在 GKE 中我的秘密中的凭据文件。我还有一些与数据库相关的变量需要作为环境变量传入,同样来自 GKE 中的机密。

目前在我的解决方案中,在我的 api 项目文件旁边,我有一个 Dockerfile,如下所示。

FROM gcr.io/google-appengine/aspnetcore:3.1
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "HelloCloud.Api.dll"]

#FROM gcr.io/cloudsql-docker/gce-proxy
#COPY . /app
#WORKDIR /app
#CMD ["/cloud_sql_proxy -instances=noble-cubist-294511:europe-west2:helloclouddb=tcp:1433 -credential_file=/app/secrets/cloudsql/key.json"]

如您所见,Dockerfile 的第二部分已被注释掉。这样做是因为它使 GKE 上的 Pod 崩溃,因为它缺少需要从 Secret 挂载的凭据文件。

在Dockerfile之外,还有一个名为deployment.yaml的文件,内容如下。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hellocloud
spec:
  selector:
    matchLabels:
      app: hellocloud
  template:
    metadata:
      labels:
        app: hellocloud
    spec:
      containers:
      - name: hellocloud
        image: gcr.io/noble-cubist-294511/hello-cloud-api
        env:
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: helloclouddb-db-credentials
                  key: username
            - name: DB_PASS
              valueFrom:
                secretKeyRef:
                  name: helloclouddb-db-credentials
                  key: password
            - name: DB_NAME
              valueFrom:
                secretKeyRef:
                  name: helloclouddb-db-credentials
                  key: database
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80

      - name: cloudsql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy
        command: ["/cloud_sql_proxy",
                    "-instances=noble-cubist-294511:europe-west2:helloclouddb=tcp:1433",
                    "-credential_file=/secrets/cloudsql/key.json"]
        resources:
            limits:
              memory: "128Mi"
              cpu: "500m"
        volumeMounts:
            - name: credentials-volumn
              mountPath: /secrets/cloudsql
              readOnly: true

    volumes:
        - name: credentials-volumn
          secret:
            secretName: helloclouddb-instance-credentials

我按照本网站上的指南创建了上述 deployment.yaml:Connecting Cloud SQL

通过研究,我发现Google Cloud Tools for Visual Studio 对 Dockerfile 有反应,这也是我尝试注释掉的部分的原因。我一直在试图弄清楚我是否可以通过 Dockerfile 指示 GKE 使用 deployment.yaml 文件,因为据我了解这应该可以解决问题。

我喜欢 DRY(不要重复自己)的开发概念,这是它想要通过 Google Cloud Tools for Visual Studio 做到这一点的另一个原因。我曾尝试直接在 GKE 上创建部署,这花了我大约 10 分钟的时间,结果甚至没有工作。当然,如果我更习惯于在 GKE 上创建部署,它会减少时间,最终也可以工作,但这将是一种 WET(每次写入)方式。

在我的头撞在桌子上两天之后,我没有靠近,这就是我写这个 Stackoverflow 任务的原因,希望对 Docker、GKE 和 Cloud SQL 更有经验的人可以给我一些指示。

如果我可能遗漏了重要的内容,请随时询问更多细节。

[编辑 1]

作为一种解决方法,我试图将文件放在我的驱动器上,Dockerfile 中的副本将从那里获取它,至少根据我的理解。下面是我在 Visual Studio 中的项目图像,后面是我更新的 Dockerfile。

FROM gcr.io/google-appengine/aspnetcore:3.1
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "HelloCloud.Api.dll"]

FROM gcr.io/cloudsql-docker/gce-proxy
COPY . /app
WORKDIR /app/Secrets/CloudSQL
CMD ["/cloud_sql_proxy -instances=noble-cubist-294511:europe-west2:helloclouddb=tcp:1433 -credential_file=key.json"]

在我的计算机上构建 Dockerfile 并使用Dive 命令查看映像的内容,它在指定位置包含“key.json”。即使这样,当部署到 GKE 时,Cloud Build 也可以很好地构建它,但是在启动 pod 时,它会抛出 RunContainerError,抱怨“没有这样的文件或目录”。完整错误的图像如下所示。

【问题讨论】:

  • 创建了一个 Api 方法,当调用它时,它只会告诉我 database.json 和 key.json 文件是否存在,它说它们确实存在。那么为什么 GKE 会抛出该错误...
  • 我可以在您的部署文件中看到您使用的是绝对路径,但错误显示 --credential_file 带有相对路径。此外,您的部署文件中的路径是小写的,并且在您的 Dockerfile/项目中它是 CamelCase ......您是否尝试过让所有人都保持相同?我问是因为对于 linux,如果你说 cd Secrets/CloudSQLcd /secret/cloudsql
  • 从 Visual Studio 部署我的项目后,我确实看到了任何关于 deployment.yaml 文件执行任何操作的建议。影响部署的是 Dockerfile。我已经匹配了两个文件中的路径,以防万一,它们现在都说:“app/Secrets/CloudSQL/key.json”。与他们匹配部署仍然会导致错误。我还认为它丢失的是 cloud_sql_proxy 文件,因此最新的 Dockerfile 将 CMD 之前的命令设置为“WORKDIR”。通过 Dive 查看图像,这就是 cloud_sql_proxy 所在的位置。
  • 您解决了这个问题吗?丢失的 cloud_sql_file 解决了问题?
  • @KoopaKiller 看看下面我的回答。

标签: docker asp.net-core google-kubernetes-engine google-cloud-sql


【解决方案1】:

我设法通过在我的项目中创建自己的类来使其工作,给定代理文件的路径将使用所需的参数运行它,我也通过我的类提供给它。为了将代理文件与我自己的代码一起获取,我使用以下 Dockerfile 来构建我的图像。

FROM gcr.io/cloudsql-docker/gce-proxy as proxy
COPY . /app

FROM gcr.io/google-appengine/aspnetcore:3.1
Copy --from=proxy . /app
WORKDIR /app/app
ENTRYPOINT ["dotnet", "HelloCloud.Api.dll"]

给定代理文件的路径以及凭据和数据库文件,我的“CloudSQLInitializer”类将启动代理。

public Startup(IConfiguration configuration)
        {
            ...
            try
            {
                CloudSQLInitializer cloud = null;

                // Currently running the proxy from either a bat or windows service.
                // As such will not have it start the proxy
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                    cloud = new CloudSQLInitializer($"Secrets/CloudSQL/database.json");
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    cloud = new CloudSQLInitializer($"../cloud_sql_proxy", $"Secrets/CloudSQL/database.json", $"Secrets/CloudSQL/key.json");

                ...
            }
            catch (Exception e)
            {
                ...
            }
        }

public class CloudSQLInitializer
    {
        public string ConnectionString { get; private set; }

        ...
        public CloudSQLInitializer(string databaseInformationFilePath)
        {
            ...

            var information =
                JsonConvert.DeserializeObject<CloudDatabase>(File.ReadAllText(databaseInformationFilePath));

            ConstructConnectionString(information);
        }

        ...
        public CloudSQLInitializer(string cloudSQLProxyFilePath, string databaseInformationFilePath, string cloudCredentialsFilePath)
        {
            ...

            var information =
                JsonConvert.DeserializeObject<CloudDatabase>(File.ReadAllText(databaseInformationFilePath));

            ...

            ConstructConnectionString(information);
            RunCloudSQLProxy(information, cloudSQLProxyFilePath, cloudCredentialsFilePath);
        }

        private void ConstructConnectionString(CloudDatabase information)
        {
            var cs = new StringBuilder();

            cs.Append($"Data Source=127.0.0.1;");
            cs.Append($"Initial Catalog={information.Database};");
            cs.Append($"Persist Security Info=True;");
            cs.Append($"User ID={information.Username};");
            cs.Append($"Password={information.Password}");

            ConnectionString = cs.ToString();
        }

        private void RunCloudSQLProxy(CloudDatabase information, string cloudSQLProxyFilePath, string cloudCredentialsFilePath)
        {
            var cmd = new StringBuilder();

            cmd.Append($" -instances={information.InstancesToOneString()}");
            cmd.Append($" -credential_file={cloudCredentialsFilePath}");

            var proxy = new ProcessStartInfo {FileName = cloudSQLProxyFilePath, Arguments = cmd.ToString()};
            Process.Start(proxy);
        }
    }
public class CloudDatabase
    {
        [JsonProperty("database")]
        public string Database { get; set; }

        [JsonProperty("username")]
        public string Username { get; set; }

        [JsonProperty("password")]
        public string Password { get; set; }

        [JsonProperty("instances")]
        public List<string> Instances { get; set; }

        ...
    }

在上面的代码 sn-ps 中,我删除了方法的摘要,以及检查以确保文件存在并同样检查的代码。还删掉了很多与本案例无关的代码。

希望这对将来的某个时候有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2020-06-07
    • 2018-08-15
    • 1970-01-01
    • 2020-11-23
    • 1970-01-01
    相关资源
    最近更新 更多