docker - Google Kubernetes Engine 到 Cloud SQL
问题描述
我目前正在尝试学习使用 Google Kubernetes Engine 和 Google Cloud SQL。为此,我在我的 Visual Studio 2019 中创建了一个 AspnetCore 3.1 Web Api 项目,用作培训项目。
目前,我目前能够执行以下操作。
- 使用Google Cloud Tools for Visual Studio将 Web Api 部署到 GKE
- 使用 SSMS 和Cloud SQL 代理从自己的 PC 连接到 Cloud SQL
我想要做的如下。
- 使用Google Cloud Tools for Visual Studio将 Web Api 部署到 GKE,附带一个包含Cloud SQL 代理的 sidecar
根据我对 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:连接云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,抱怨“没有这样的文件或目录”。完整错误的图像如下所示。
解决方案
我设法通过在我的项目中创建自己的类来让它工作,给定代理文件的路径将使用所需的参数运行它,我也通过我的类提供给它。为了将代理文件与我自己的代码一起获取,我使用了以下 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; }
...
}
在上面的代码片段中,我删除了方法的摘要,以及检查以确保文件存在并同样检查的代码。还删除了许多与本案例无关的代码。
希望这在将来的某个时候对某人有用。
推荐阅读
- python - 如何在python中获取外部文件的文件大小?
- r - R:创建新列,其中一个与条件匹配的同一列中的值之间的差异
- command-line - 如何使用 git bash 执行静默安装
- scala - UDF 函数中数据帧的长度
- image - 如何将div中的图像与其他元素对齐
- bash - Docker 容器 - “Docker run”附加 bash 命令
- python - 您如何在 base64.b64encode 上使用变量参数,但是当我不使用提示窗口时?
- python - 如何使用正则表达式替换python字典中的字符串
- javascript - ORM在获取14737行时内存不足是否正常
- r - 无法组合形状文件的 ggplot,包括多边形和空间点