创建带有 Sidecar 的 Cloud Run 服务

1. 概览

简介

在此 Codelab 中,您将学习如何部署使用多个容器Cloud Run 服务。您将创建一个将用作 Cloud Run Ingress 容器的 node.js 应用,以及一个将用作辅助信息文件的额外 node.js 应用。

技术概览

在一个 Cloud Run 实例中使用多个容器时,一个容器用作 Web 入站流量的主容器。一个或多个其他容器称为 Sidecar。

多个容器相互通信的方式有两种:

  1. 这些容器共享 localhost 网络接口,因此所有容器都可以监听端口,例如localhost:port.
  2. 您还可以使用内存中的卷并将其装载到容器上以共享文件。

使用场景

由于 Cloud Run 实例中的所有容器都共用 localhost 网络接口,因此您可以在主容器前面使用辅助信息文件来代理请求。这种代理可以提供一个额外的抽象层,通过拦截请求并将其转发到适当的端点,在客户端和服务器之间更高效地流向应用。例如,您可以使用 DockerHub 中的官方 Nginx 映像(如此处所示)。

由于多个容器可以通过共享卷共享文件来进行通信,因此您可以向服务添加各种 Sidecar 应用。例如,您可以对 Cloud Run 服务进行插桩,以使用 OpenTelemetry 等自定义代理导出日志、指标和跟踪记录(OpenTelemetry 示例)。另一个示例是使用 Sidecar 连接到 Cloud Spanner PostgreSQL 数据库(Cloud Spanner Postgress 示例)。

此 Codelab 中的示例

在此 Codelab 中,您将首先部署一项 Cloud Run 服务,该服务的入站流量容器通过 localhost 端口与 Sidecar 通信。然后,您将更新 Ingress 容器和 Sidecar,以通过卷装载共享文件。

学习内容

  • 如何创建使用 Sidecar 的容器
  • 入站流量容器如何使用 localhost 与 Sidecar 通信
  • 入站流量容器和 Sidecar 如何通过装载的卷共享文件

2. 设置和要求

前提条件

激活 Cloud Shell

  1. 在 Cloud Console 中,点击激活 Cloud Shelld1264ca30785e435.png

cb81e7c8e34bc8d.png

如果这是您第一次启动 Cloud Shell,系统会显示一个中间屏幕,说明它是什么。如果您看到中间屏幕,请点击继续

d95252b003979716.png

预配和连接到 Cloud Shell 只需花几分钟时间。

7833d5e1c5d18f54

这个虚拟机装有所需的所有开发工具。它提供了一个持久的 5 GB 主目录,并在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的大部分(即使不是全部)工作都可以通过浏览器完成。

在连接到 Cloud Shell 后,您应该会看到自己已通过身份验证,并且相关项目已设为您的项目 ID。

  1. 在 Cloud Shell 中运行以下命令以确认您已通过身份验证:
gcloud auth list

命令输出

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. 在 Cloud Shell 中运行以下命令,以确认 gcloud 命令了解您的项目:
gcloud config list project

命令输出

[core]
project = <PROJECT_ID>

如果不是上述结果,您可以使用以下命令进行设置:

gcloud config set project <PROJECT_ID>

命令输出

Updated property [core/project].

3. 创建 Ingress 应用

设置环境变量

在此 Codelab 中,您将创建一些环境变量,以提高此 Codelab 中使用的 gcloud 命令的可读性。

REGION=<YOUR-REGION>
PROJECT_ID=<YOUR-PROJECT-ID>

SERVICE_NAME=sidecar-codelab
REPO_NAME=sidecar-codelab

创建 ArtifactRegistry 代码库以保存您的容器映像

您可以在 Artifact Registry 中创建代码库,用于存储此 Codelab 的容器映像。

gcloud artifacts repositories create $REPO_NAME --repository-format=docker \
--location=$REGION --description="sidecar codelab"

然后,创建一个包含以下内容的 package.json 文件:

{
  "name": "sidecar-codelab",
  "version": "1.0.0",
  "private": true,
  "description": "demonstrates how to use sidecars in cloud run",
  "main": "index.js",
  "author": "Google LLC",
  "license": "Apache-2.0",
  "scripts": {
    "start": "node ingress.js"
  },
  "dependencies": {
    "axios": "^1.6.2",
    "express": "^4.18.2"
  }
}

现在,创建一个名为 ingress.js 且包含以下内容的文件:

const express = require('express');
const app = express();
const axios = require("axios");

app.get('/', async (req, res) => {

    let response = await axios.get("http://localhost:5000");

    res.send("The sidecar says: " + response.data);
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () => {
    console.log(`Ingress container listening on port ${port}`);
});

为 Ingress 容器创建 dockerfile

FROM node:20.10.0-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --production

# Copy local code to the container image.
COPY . .

# Run the web service on container startup.
ENV PORT=8080
CMD [ "npm", "start" ]

并为 Ingress 容器创建一个 ``.dockerignore` 文件。

# Exclude locally installed dependencies
node_modules/

# Exclude "build-time" ignore files.
.dockerignore
.gcloudignore

# Exclude git history and configuration.
.gitignore

现在,您可以通过运行以下命令为 Ingress 容器构建映像:

gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/ingress:latest

4. 创建 Sidecar 应用

在本部分中,您将创建第二个 node.js 应用,该应用将在 Cloud Run 服务中用作辅助信息文件。

前往 Sidecar 目录。

cd ../sidecar

创建一个包含以下内容的 package.json 文件:

{
  "name": "sidecar-codelab",
  "version": "1.0.0",
  "private": true,
  "description": "demonstrates how to use sidecars in cloud run",
  "main": "index.js",
  "author": "Google LLC",
  "license": "Apache-2.0",
  "scripts": {
    "start": "node sidecar.js"
  },
  "dependencies": {
    "axios": "^1.6.2",
    "express": "^4.18.2"
  }
}

现在,创建一个名为 sidecar.js 且包含以下内容的文件:

const express = require('express');
const app = express();

app.get('/', async (req, res) => {
    res.send("Hello ingress container! I'm the sidecar.");
});

const port = parseInt(process.env.PORT || 5000);
app.listen(port, () => {
    console.log(`Sidecar container listening on port ${port}`);
});

为 Sidecar 容器创建 Dockerfile

FROM node:20.10.0-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --production

# Copy local code to the container image.
COPY . .

# Run the web service on container startup.
ENV PORT=5000
CMD [ "npm", "start" ]

然后为 Sidecar 容器创建一个 ``.dockerignore` 文件。

# Exclude locally installed dependencies
node_modules/

# Exclude "build-time" ignore files.
.dockerignore
.gcloudignore

# Exclude git history and configuration.
.gitignore

现在,您可以通过运行以下命令为 Ingress 容器构建映像:

gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/sidecar:latest

部署 Cloud Run 服务

您将使用 yaml 文件部署 Cloud Run 服务。

导航到父级目录。

cd ..

创建一个名为 sidecar-codelab.yaml 且包含以下内容的文件:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  annotations:
  name: sidecar-codelab
  labels:
    cloud.googleapis.com/location: "<YOUR_REGION>"
spec:
  template:
    spec:
      containers:
        - image: "<YOUR_REGION>-docker.pkg.dev/<YOUR_PROJECT_ID>/sidecar-codelab/ingress:latest"
          ports:
            - containerPort: 8080
        - image: "<YOUR_REGION>-docker.pkg.dev/<YOUR_PROJECT_ID>/sidecar-codelab/sidecar:latest"
          env:
            - name: PORT
              value: "5000"

然后,使用以下命令部署该服务。您需要使用 gcloud beta,因为卷装载处于公开预览版阶段。

gcloud beta run services replace sidecar-codelab.yaml

部署后,将服务网址保存在环境变量中。

SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --platform managed --region $REGION --format 'value(status.url)') 

5. 调用 Cloud Run 服务

现在,您可以通过提供身份令牌来调用服务。

curl -X GET -H "Authorization: Bearer $(gcloud auth print-identity-token)" ${SERVICE_URL}

结果应类似于以下示例输出:

The sidecar says: Hello ingress container! I'm the sidecar.

6. 通过卷装载共享文件

在本部分中,您将更新容器,以通过卷装载共享文件。在此示例中,入站流量容器将写入共享卷上的文件。Sidecar 将读取文件,并将其内容返回给 Ingress 容器。

首先,您将更新入站流量容器代码。导航到 Ingress 目录。

cd ../ingress

然后将 ingress.js 文件的内容替换为以下代码:

const express = require('express');
const app = express();
const fs = require('fs');
const axios = require("axios");

const filename = "test.txt"

let path = "/my-volume-mount";
app.use(path, express.static(path));

try {
    fs.writeFileSync(`${path}/${filename}`, "The ingress container created this file.");
} catch (err) {
    console.error(err);
}

app.get('/', async (req, res) => {

    let response = await axios.get("http://localhost:5000");

    res.send("The sidecar says: " + response.data);
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () => {
    console.log(`Ingress container listening on port ${port}`);
});

运行以下命令,为您的入站流量容器构建新映像:

gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/ingress:latest

现在,导航到 Sidecar 目录:

cd ../sidecar

并使用以下内容更新 sidecar.js

const express = require('express');
const app = express();
const fs = require('fs');

const filename = "test.txt"

let path = "/my-volume-mount";
app.use(path, express.static(path));

async function readFile() {
    try {
        return await fs.readFileSync(`${path}/${filename}`, { encoding: 'utf8' });
    } catch (err) {
        console.log(err);
    }
}

app.get('/', async (req, res) => {
    let contents = await readFile();
    res.send(contents);
});

const port = parseInt(process.env.PORT || 5000);
app.listen(port, () => {
    console.log(`Sidecar container listening on port ${port}`);
});

运行以下命令,为您的 Sidecar 容器构建新映像:

gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/sidecar:latest

使用以下代码更新 sidecar-codelab.yaml 以共享卷:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  annotations:
  name: sidecar-codelab
  labels:
    cloud.googleapis.com/location: "<YOUR_REGION>"
spec:
  template:
    spec:
      containers:
        - image: "<YOUR_REGION>-docker.pkg.dev/<YOUR_PROJECT_ID>/sidecar-codelab/ingress:latest"
          ports:
            - containerPort: 8080
          volumeMounts:
            - mountPath: /my-volume-mount
              name: in-memory-1
        - image: "<YOUR_REGION>-docker.pkg.dev/<YOUR_PROJECT_ID>/sidecar-codelab/sidecar:latest"
          env:
            - name: PORT
              value: "5000"
          volumeMounts:
            - mountPath: /my-volume-mount
              name: in-memory-1
      volumes:
        - emptyDir:
            medium: Memory
          name: in-memory-1

部署更新后的 sidecar-codelab.yaml 文件

gcloud beta run services replace sidecar-codelab.yaml

现在,您可以通过提供身份令牌来调用服务。

curl -X GET -H "Authorization: Bearer $(gcloud auth print-identity-token)" ${SERVICE_URL}

结果应类似于以下示例输出:

The sidecar says: the ingress container created this file.

7. 恭喜!

恭喜您完成此 Codelab!

建议您查看有关 Cloud Run 的文档,特别是部署多容器和使用内存中卷装载

所学内容

  • 如何创建使用 Sidecar 的容器
  • 入站流量容器如何使用 localhost 与 Sidecar 通信
  • 入站流量容器和 Sidecar 如何共享装载的卷

8. 清理

为避免产生意外费用(例如,如果此 Cloud Functions 函数被意外调用的次数超过免费层级中的每月 Cloud Run 调用次数),您可以删除 Cloud Run 服务或删除您在第 2 步中创建的项目。

如需删除此 Cloud Functions 函数,请前往 https://console.cloud.google.com/run/ 前往 Cloud Functions 函数 Cloud 控制台,然后删除 sidecar-codelab 服务(如果您使用其他名称,则删除 $SERVICE_NAME)。

如果您选择删除整个项目,可以前往 https://console.cloud.google.com/cloud-resource-manager,选择您在第 2 步中创建的项目,然后选择“删除”。如果删除项目,则需要在 Cloud SDK 中更改项目。您可以通过运行 gcloud projects list 来查看所有可用项目的列表。