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 인스턴스와 통신하기 위해 redis-py 패키지를 사용합니다.

Flask==3.0.3
gunicorn==22.0.0
redis==5.2.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 1918, RFC 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 서비스의 URL이 표시됩니다. 웹브라우저에서 이 URL을 방문(또는 curl과 같은 도구 사용)하고 서비스를 방문할 때마다 Redis 인스턴스의 수가 증가하는지 확인합니다.