从 Cloud Run 服务连接到 Redis 实例

您可以使用直接 VPC 出站流量无服务器 VPC 访问通道从 Cloud Run 连接到 Redis 实例。

设置

如果您已安装 Google Cloud CLI 且已创建 Redis 实例,则可以跳过这些步骤。

  1. 安装 gcloud CLI 并初始化:

    gcloud init
    
  2. 按照快速入门指南创建一个 Redis 实例。记下该 Redis 实例的地区、IP 地址和端口。

为配置 VPC 网络出站流量做好准备

要连接到 Redis 实例,您的 Cloud Run 服务需要访问 Redis 实例的已获授权的 VPC 网络。接收者 您需要直接 VPC 出站流量或 无服务器 VPC 访问通道连接器。 比较这两种网络出站流量方法

  1. 通过运行以下命令查找 Redis 实例的已获授权的网络的名称:

    gcloud redis instances describe INSTANCE_ID --region REGION --format "value(authorizedNetwork)"
    

    记下网络名称。

  2. 如果您使用的是无服务器 VPC 访问通道 创建连接器。 请务必使用与 Redis 实例相同的区域和 VPC 网络。记下连接器的名称。

示例应用

此示例 HTTP 服务器应用通过 Cloud Run 服务建立与 Redis 实例之间的连接。

克隆所选编程语言的代码库并浏览 复制到包含示例代码的文件夹:

Go

git clone https://github.com/GoogleCloudPlatform/golang-samples
cd golang-samples/memorystore/redis

Node.js

git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples
cd nodejs-docs-samples/memorystore/redis

Python

git clone https://github.com/GoogleCloudPlatform/python-docs-samples
cd python-docs-samples/memorystore/redis

每次访问 / 端点时,此示例应用都会增加一个 Redis 计数器。

Go

此应用使用 github.com/gomodule/redigo/redis 客户端。通过运行以下命令进行安装:

go get github.com/gomodule/redigo/redis

// Command redis is a basic app that connects to a managed Redis instance.
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func incrementHandler(w http.ResponseWriter, r *http.Request) {
	conn := redisPool.Get()
	defer conn.Close()

	counter, err := redis.Int(conn.Do("INCR", "visits"))
	if err != nil {
		http.Error(w, "Error incrementing visitor counter", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Visitor number: %d", counter)
}

func main() {
	redisHost := os.Getenv("REDISHOST")
	redisPort := os.Getenv("REDISPORT")
	redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

	const maxConnections = 10
	redisPool = &redis.Pool{
		MaxIdle: maxConnections,
		Dial:    func() (redis.Conn, error) { return redis.Dial("tcp", redisAddr) },
	}

	http.HandleFunc("/", incrementHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

Node.js

此应用使用 redis 模块。

{
  "name": "memorystore-redis",
  "description": "An example of using Memorystore(Redis) with Node.js",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": ">=16.0.0"
  },
  "dependencies": {
    "redis": "^4.0.0"
  }
}

'use strict';
const http = require('http');
const redis = require('redis');

const REDISHOST = process.env.REDISHOST || 'localhost';
const REDISPORT = process.env.REDISPORT || 6379;

const client = redis.createClient(REDISPORT, REDISHOST);
client.on('error', err => console.error('ERR:REDIS:', err));

// create a server
http
  .createServer((req, res) => {
    // increment the visit counter
    client.incr('visits', (err, reply) => {
      if (err) {
        console.log(err);
        res.status(500).send(err.message);
        return;
      }
      res.writeHead(200, {'Content-Type': 'text/plain'});
      res.end(`Visitor number: ${reply}\n`);
    });
  })
  .listen(8080);

Python

此应用使用 Flask 进行网络服务,并使用 redis-py 软件包与 Redis 实例进行通信。

Flask==3.0.3
gunicorn==22.0.0
redis==5.0.1
Werkzeug==3.0.3
import logging
import os

from flask import Flask
import redis

app = Flask(__name__)

redis_host = os.environ.get("REDISHOST", "localhost")
redis_port = int(os.environ.get("REDISPORT", 6379))
redis_client = redis.StrictRedis(host=redis_host, port=redis_port)


@app.route("/")
def index():
    value = redis_client.incr("counter", 1)
    return f"Visitor number: {value}"


@app.errorhandler(500)
def server_error(e):
    logging.exception("An error occurred during a request.")
    return (
        """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(
            e
        ),
        500,
    )


if __name__ == "__main__":
    # This is used when running locally. Gunicorn is used to run the
    # application on Google App Engine and Cloud Run.
    # See entrypoint in app.yaml or Dockerfile.
    app.run(host="127.0.0.1", port=8080, debug=True)

将应用部署到 Cloud Run

如需部署应用,请执行以下操作:

  1. Dockerfile 复制到源目录中:

    cp cloud_run_deployment/Dockerfile .
    
  2. 借助以下命令使用 Cloud Build 构建容器映像:

    gcloud builds submit --tag gcr.io/PROJECT_ID/visit-count
    
  3. 将容器部署到 Cloud Run。

    • 如果您使用的是直接 VPC 出站流量,请运行以下命令:

      gcloud run deploy \
      --image gcr.io/PROJECT_ID/visit-count \
      --platform managed \
      --allow-unauthenticated \
      --region REGION \
      --network NETWORK \
      --subnet SUBNET \
      --set-env-vars REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT
      

      其中:

      • PROJECT_ID 是 Google Cloud 项目 ID。
      • REGION 是您的 Redis 实例所在的区域。
      • NETWORK 是 Redis 实例所附加的已获授权 VPC 网络的名称。
      • SUBNET 是您的子网的名称。子网必须 不小于 /26。直接 VPC 出站流量支持 IPv4 范围 RFC 1918RFC 6598 和 E 类。
      • REDIS_IPREDIS_PORT 是 Redis 实例的 IP 地址和端口号。
    • 如果您使用的是无服务器 VPC 访问通道连接器,请运行以下命令:

      gcloud run deploy \
      --image gcr.io/PROJECT_ID/visit-count \
      --platform managed \
      --allow-unauthenticated \
      --region REGION \
      --vpc-connector CONNECTOR_NAME \
      --set-env-vars REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT
      

      其中:

      • PROJECT_ID 是 Google Cloud 项目 ID。
      • REGION 是无服务器 VPC 访问通道连接器和 Redis 实例所在的地区。
      • CONNECTOR_NAME 是连接器的名称。
      • REDIS_IPREDIS_PORT 是 Redis 实例的 IP 地址和端口号。

部署成功完成后,该命令行会显示 Cloud Run 服务的网址。在网络浏览器中(或使用 curl 等工具)访问此网址,就会看到每次访问服务时 Redis 实例上的计数都会增加。