Cloud Run functions から Redis インスタンスへの接続

サーバーレス VPC アクセスを使用すると、Cloud Run functions から Redis インスタンスに接続できます。

設定

Google Cloud CLI をインストール済みで Redis インスタンスを作成済みの場合は、次の手順をスキップできます。

  1. gcloud CLI をインストールして初期化します。

    gcloud init
    
  2. クイックスタート ガイドの手順に沿って Redis インスタンスを作成します。Redis インスタンスのゾーン、IP アドレス、ポート番号をメモしておきます。

サーバーレス VPC アクセスの構成

Cloud Run functions から Redis インスタンスの承認済み VPC ネットワークに接続するには、サーバーレス VPC アクセスを設定する必要があります。

  1. 次のコマンドを実行して、Redis インスタンスの承認済みネットワークを確認します。

    gcloud redis instances describe INSTANCE_ID --region REGION
  2. コネクタの作成の手順に従って、サーバーレス VPC アクセス コネクタを作成します。関数をデプロイするリージョンと同じリージョン内にコネクタを作成していることを確認し、コネクタが Redis インスタンスで承認された VPC ネットワークにアタッチされていることを確認してください。コネクタの名前を覚えておいてください。

サンプル関数

Cloud Run functions から Redis インスタンスへの接続を確立するサンプル関数です。

使用するプログラミング言語のリポジトリのクローンを作成し、サンプルコードを含むフォルダに移動します。

Go

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

Node.js

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

Python

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

サンプルコードは、関数がトリガーされるたびに Redis カウンタをインクリメントします。

Go

この関数は github.com/gomodule/redigo/redis クライアントを使用します。


// Package visitcount provides a Cloud Function that connects
// to a managed Redis instance.
package visitcount

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

	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
	"github.com/gomodule/redigo/redis"
)

var redisPool *redis.Pool

func init() {
	// Register the HTTP handler with the Functions Framework
	functions.HTTP("VisitCount", visitCount)
}

// initializeRedis initializes and returns a connection pool
func initializeRedis() (*redis.Pool, error) {
	redisHost := os.Getenv("REDISHOST")
	if redisHost == "" {
		return nil, errors.New("REDISHOST must be set")
	}
	redisPort := os.Getenv("REDISPORT")
	if redisPort == "" {
		return nil, errors.New("REDISPORT must be set")
	}
	redisAddr := fmt.Sprintf("%s:%s", redisHost, redisPort)

	const maxConnections = 10
	return &redis.Pool{
		MaxIdle: maxConnections,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", redisAddr)
			if err != nil {
				return nil, fmt.Errorf("redis.Dial: %w", err)
			}
			return c, err
		},
	}, nil
}

// visitCount increments the visit count on the Redis instance
// and prints the current count in the HTTP response.
func visitCount(w http.ResponseWriter, r *http.Request) {
	// Initialize connection pool on first invocation
	if redisPool == nil {
		// Pre-declare err to avoid shadowing redisPool
		var err error
		redisPool, err = initializeRedis()
		if err != nil {
			log.Printf("initializeRedis: %v", err)
			http.Error(w, "Error initializing connection pool", http.StatusInternalServerError)
			return
		}
	}

	conn := redisPool.Get()
	defer conn.Close()

	counter, err := redis.Int(conn.Do("INCR", "visits"))
	if err != nil {
		log.Printf("redis.Int: %v", err)
		http.Error(w, "Error incrementing visit count", http.StatusInternalServerError)
		return
	}
	fmt.Fprintf(w, "Visit count: %d", counter)
}

Node.js

この関数は redis モジュールを使用します。


const functions = require('@google-cloud/functions-framework');
const redis = require('redis');

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

const redisClient = redis.createClient({
  socket: {
    host: REDISHOST,
    port: REDISPORT,
  },
});
redisClient.on('error', err => console.error('ERR:REDIS:', err));
redisClient.connect();

functions.http('visitCount', async (req, res) => {
  try {
    const response = await redisClient.incr('visits');
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(`Visit count: ${response}`);
  } catch (err) {
    console.log(err);
    res.status(500).send(err.message);
  }
});

Python

この関数は redis-py パッケージを使用します。


import os

import functions_framework
import redis

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)


@functions_framework.http
def visit_count(request):
    value = redis_client.incr("visits", 1)
    return f"Visit count: {value}"

サンプルを Cloud Run functions にデプロイする

Google Cloud CLI を使用して関数をデプロイします。

Go

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=go116 \
--source=. \
--entry-point=VisitCount \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

Node.js

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=nodejs16 \
--source=. \
--entry-point=visitCount \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

Python

gcloud functions deploy visit-count \
--gen2 \
--region=REGION \
--runtime=python310 \
--source=. \
--entry-point=visit_count \
--trigger-http \
--vpc-connector=projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME \
--set-env-vars=REDISHOST=REDIS_IP,REDISPORT=REDIS_PORT

ここで

  • PROJECT_ID は、Google Cloud プロジェクトの ID です。
  • REGION は、関数をデプロイするリージョンです。
  • CONNECTOR_NAME は、コネクタの名前です。
  • REDIS_IPREDIS_PORT は、Redis インスタンスの IP アドレスとポート番号です。

関数のデプロイが完了したら、関数の URL を取得します。

gcloud functions describe visit-count \
--gen2 \
--region=REGION \
--format="value(serviceConfig.uri)"

GET リクエストを URL に送信することで、関数をトリガーするたびにカウンタが増加します。