Se connecter à une instance Redis depuis une VM Compute Engine

Vous pouvez vous connecter à une instance Redis à partir d'instances de VM Compute Engine utilisant le même réseau autorisé que votre instance Redis.

Configuration

Si vous avez déjà installé Google Cloud CLI, et créé une instance Redis et un bucket Cloud Storage, vous pouvez ignorer ces étapes.

  1. Installez gcloud CLI et initialisez-le :

    gcloud init 
  2. Suivez le Guide de démarrage rapide pour créer une instance Redis. Retenez la zone, l'adresse IP et le port de l'instance Redis.

  3. Créez un bucket Cloud Storage dans lequel l'artefact d'application de cet exemple d'application sera chargé. Pour en savoir plus, consultez Créer des buckets.

Configurer des paramètres gcloud pour l'exemple d'application

  1. Définissez le projet par défaut gcloud sur votre exemple de projet d'application.
    gcloud config set project [PROJECT_ID]

Exemple d'application

Cet exemple d'application de serveur HTTP établit une connexion à une instance Redis à partir d'une instance de VM Compute Engine.

Clonez le dépôt correspondant au langage de programmation souhaité et accédez au dossier contenant l'exemple de code :

Go

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

Java

git clone https://github.com/GoogleCloudPlatform/java-docs-samples cd java-docs-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 

Cet exemple de code incrémente un compteur Redis à chaque accès au point de terminaison /.

Go

Cette application utilise le client github.com/gomodule/redigo/redis. Installez-le en exécutant la commande suivante :

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) 	} } 

Java

Cette application est basée sur un servlet Jetty 3.1.

Il utilise la bibliothèque Jedis :

<dependency>   <groupId>redis.clients</groupId>   <artifactId>jedis</artifactId>   <version>5.1.0</version> </dependency>

La classe AppServletContextListener permet de créer un pool de connexions Redis longue durée :

 package com.example.redis;  import java.io.IOException; import java.util.Properties; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig;  @WebListener public class AppServletContextListener implements ServletContextListener {    private Properties config = new Properties();    private JedisPool createJedisPool() throws IOException {     String host;     Integer port;     config.load(         Thread.currentThread()             .getContextClassLoader()             .getResourceAsStream("application.properties"));     host = config.getProperty("redis.host");     port = Integer.valueOf(config.getProperty("redis.port", "6379"));      JedisPoolConfig poolConfig = new JedisPoolConfig();     // Default : 8, consider how many concurrent connections into Redis you will need under load     poolConfig.setMaxTotal(128);      return new JedisPool(poolConfig, host, port);   }    @Override   public void contextDestroyed(ServletContextEvent event) {     JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");     if (jedisPool != null) {       jedisPool.destroy();       event.getServletContext().setAttribute("jedisPool", null);     }   }    // Run this before web application is started   @Override   public void contextInitialized(ServletContextEvent event) {     JedisPool jedisPool = (JedisPool) event.getServletContext().getAttribute("jedisPool");     if (jedisPool == null) {       try {         jedisPool = createJedisPool();         event.getServletContext().setAttribute("jedisPool", jedisPool);       } catch (IOException e) {         // handle exception       }     }   } }

La classe VisitCounterServlet est un servlet Web qui incrémente un compteur Redis :

 package com.example.redis;  import java.io.IOException; import java.net.SocketException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool;  @WebServlet(name = "Track visits", value = "") public class VisitCounterServlet extends HttpServlet {    @Override   public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {     try {       JedisPool jedisPool = (JedisPool) req.getServletContext().getAttribute("jedisPool");        if (jedisPool == null) {         throw new SocketException("Error connecting to Jedis pool");       }       Long visits;        try (Jedis jedis = jedisPool.getResource()) {         visits = jedis.incr("visits");       }        resp.setStatus(HttpServletResponse.SC_OK);       resp.getWriter().println("Visitor counter: " + String.valueOf(visits));     } catch (Exception e) {       resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());     }   } }

Node.js

Cette application utilise le module 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

Cette application utilise Flask pour la diffusion Web et le package redis-py pour communiquer avec l'instance Redis.

Flask==3.0.3 gunicorn==23.0.0 redis==6.0.0 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)

Déployer l'application vers une VM Compute Engine

Accédez au répertoire gce_deployment :

cd gce_deployment 

Le script de déploiement importe l'artefact vers le chemin d'accès Cloud Storage. Il lance ensuite une instance Compute Engine, ce qui crée un pare-feu pour exposer le port 8080. Enfin, le script de démarrage prépare l'instance.

Définissez les variables d'environnement REDISHOST et REDISPORT :

    export REDISHOST=[REDISHOST]    export REDISPORT=[REDISPORT] 

où :

  • REDISHOST est l'adresse IP de l'instance Redis gérée.
  • REDISPORT correspond au port de l'instance Redis gérée, qui est défini sur 6379 par défaut.

Définissez la variable d'environnement GCS_BUCKET_NAME :

     export GCS_BUCKET_NAME=[BUCKET_NAME]/[PATH]

où :

  • BUCKET_NAME est le nom du bucket Cloud Storage.
  • PATH est un chemin d'accès facultatif vers le répertoire dans lequel vous souhaitez stocker l'artefact de l'application.

Voici un exemple de script de déploiement qui déploie cette application sur une nouvelle instance de VM Compute Engine.

Go

if [ -z "$REDISHOST" ]; then   echo "Must set \$REDISHOST. For example: REDISHOST=127.0.0.1"   exit 1 fi  if [ -z "$REDISPORT" ]; then   echo "Must set \$REDISPORT. For example: REDISPORT=6379"   exit 1 fi  if [ -z "$GCS_BUCKET_NAME" ]; then   echo "Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"   exit 1 fi  if [ -z "$ZONE" ]; then   ZONE=$(gcloud config get-value compute/zone -q)   echo "$ZONE" fi   # Cross compile the app for linux/amd64 GOOS=linux GOARCH=amd64 go build -v -o app ../main.go # Add the app binary tar -cvf app.tar app # Copy to GCS bucket gsutil cp app.tar gs://"$GCS_BUCKET_NAME"/gce/  # Create an instance gcloud compute instances create my-instance \     --image-family=debian-9 \     --image-project=debian-cloud \     --machine-type=g1-small \     --scopes cloud-platform \     --metadata-from-file startup-script=startup-script.sh \     --metadata gcs-bucket="$GCS_BUCKET_NAME",redis-host="$REDISHOST",redis-port="$REDISPORT" \     --zone "$ZONE" \     --tags http-server  gcloud compute firewall-rules create allow-http-server-8080 \     --allow tcp:8080 \     --source-ranges 0.0.0.0/0 \     --target-tags http-server \     --description "Allow port 8080 access to http-server"

Java

if [ -z "$GCS_BUCKET_NAME" ]; then     echo "Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"     exit 1 fi  if [ -z "$ZONE" ]; then   ZONE=$(gcloud config get-value compute/zone -q)   echo $ZONE fi  if [ -z "$WAR" ]; then   WAR=visitcounter-1.0-SNAPSHOT.war fi  #Build the WAR package cd .. mvn clean package  #Copy the WAR artifact to the GCS bucket location gsutil cp -r target/${WAR} gs://"$GCS_BUCKET_NAME"/gce/  cd gce_deployment  # Create an instance gcloud compute instances create my-instance \     --image-family=debian-9 \     --image-project=debian-cloud \     --machine-type=g1-small \     --scopes cloud-platform \     --metadata-from-file startup-script=startup-script.sh \     --metadata gcs-bucket=$GCS_BUCKET_NAME,app-war=$WAR \     --zone $ZONE \     --tags http-server  gcloud compute firewall-rules create allow-http-server-8080 \     --allow tcp:8080 \     --source-ranges 0.0.0.0/0 \     --target-tags http-server \     --description "Allow port 8080 access to http-server"

Node.js

if [ -z "$REDISHOST" ]; then   echo "Must set \$REDISHOST. For example: REDISHOST=127.0.0.1"   exit 1 fi  if [ -z "$REDISPORT" ]; then   echo "Must set \$REDISPORT. For example: REDISPORT=6379"   exit 1 fi  if [ -z "$GCS_BUCKET_NAME" ]; then   echo "Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"   exit 1 fi  if [ -z "$ZONE" ]; then   ZONE=$(gcloud config get-value compute/zone -q)   echo $ZONE fi  #Upload the tar to GCS tar -cvf app.tar -C .. package.json server.js gsutil cp app.tar gs://"$GCS_BUCKET_NAME"/gce/  # Create an instance gcloud compute instances create my-instance \     --image-family=debian-9 \     --image-project=debian-cloud \     --machine-type=g1-small \     --scopes cloud-platform \     --metadata-from-file startup-script=startup-script.sh \     --metadata gcs-bucket=$GCS_BUCKET_NAME,redis-host=$REDISHOST,redis-port=$REDISPORT \     --zone $ZONE \     --tags http-server  gcloud compute firewall-rules create allow-http-server-8080 \     --allow tcp:8080 \     --source-ranges 0.0.0.0/0 \     --target-tags http-server \     --description "Allow port 8080 access to http-server"

Python

if [ -z "$REDISHOST" ]; then   echo "Must set \$REDISHOST. For example: REDISHOST=127.0.0.1"   exit 1 fi  if [ -z "$REDISPORT" ]; then   echo "Must set \$REDISPORT. For example: REDISPORT=6379"   exit 1 fi  if [ -z "$GCS_BUCKET_NAME" ]; then   echo "Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"   exit 1 fi  if [ -z "$ZONE" ]; then   ZONE=$(gcloud config get-value compute/zone -q)   echo $ZONE fi  #Upload the tar to GCS tar -cvf app.tar -C .. requirements.txt main.py # Copy to GCS bucket gsutil cp app.tar gs://"$GCS_BUCKET_NAME"/gce/  # Create an instance gcloud compute instances create my-instance \     --image-family=debian-11 \     --image-project=debian-cloud \     --machine-type=g1-small \     --scopes cloud-platform \     --metadata-from-file startup-script=startup-script.sh \     --metadata gcs-bucket=$GCS_BUCKET_NAME,redis-host=$REDISHOST,redis-port=$REDISPORT \     --zone $ZONE \     --tags http-server  gcloud compute firewall-rules create allow-http-server-8080 \     --allow tcp:8080 \     --source-ranges 0.0.0.0/0 \     --target-tags http-server \     --description "Allow port 8080 access to http-server"

Exécutez le script de déploiement :

     chmod +x deploy.sh      ./deploy.sh 

Script de démarrage de l'application

Ce script de démarrage est utilisé dans l'exemple de script de déploiement pour préparer l'instance. Pour en savoir plus sur les scripts de démarrage et l'affichage des journaux d'exécution du script de démarrage, consultez la section Exécuter des scripts de démarrage.

Go

set -ex  # Talk to the metadata server to get the project id and location of application binary. PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google") export PROJECTID GCS_BUCKET_NAME=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/gcs-bucket" -H "Metadata-Flavor: Google") REDISHOST=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-host" -H "Metadata-Flavor: Google") REDISPORT=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-port" -H "Metadata-Flavor: Google")  # Install dependencies from apt apt-get update apt-get install -yq ca-certificates supervisor  # Install logging monitor. The monitor will automatically pickup logs send to # syslog. curl "https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh" --output google-fluentd-install.sh checksum=$(sha256sum google-fluentd-install.sh | awk '{print $1;}') if [ "$checksum" != "ec78e9067f45f6653a6749cf922dbc9d79f80027d098c90da02f71532b5cc967" ]; then     echo "Checksum does not match"     exit 1 fi chmod +x google-fluentd-install.sh && ./google-fluentd-install.sh service google-fluentd restart &  gsutil cp gs://"$GCS_BUCKET_NAME"/gce/app.tar /app.tar mkdir -p /app tar -x -f /app.tar -C /app chmod +x /app/app  # Create a goapp user. The application will run as this user. getent passwd goapp || useradd -m -d /home/goapp goapp chown -R goapp:goapp /app  # Configure supervisor to run the Go app. cat >/etc/supervisor/conf.d/goapp.conf << EOF [program:goapp] directory=/app environment=HOME="/home/goapp",USER="goapp",REDISHOST=$REDISHOST,REDISPORT=$REDISPORT command=/app/app autostart=true autorestart=true user=goapp stdout_logfile=syslog stderr_logfile=syslog EOF  supervisorctl reread supervisorctl update

Java

set -ex  # Talk to the metadata server to get the project id and location of application binary. PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google") GCS_BUCKET_NAME=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/gcs-bucket" -H "Metadata-Flavor: Google") WAR=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/app-war" -H "Metadata-Flavor: Google")  gsutil cp gs://"$GCS_BUCKET_NAME"/gce/"$WAR" .  # Install dependencies from apt apt-get update apt-get install -qq openjdk-8-jdk-headless  # Make Java8 the default update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java  # Jetty Setup mkdir -p /opt/jetty/temp mkdir -p /var/log/jetty  # Get Jetty curl -L https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.10.v20180503/jetty-distribution-9.4.10.v20180503.tar.gz -o jetty9.tgz tar xf jetty9.tgz  --strip-components=1 -C /opt/jetty  # Add a Jetty User useradd --user-group --shell /bin/false --home-dir /opt/jetty/temp jetty  cd /opt/jetty # Add running as "jetty" java -jar /opt/jetty/start.jar --add-to-startd=setuid cd /  # very important - by renaming the war to root.war, it will run as the root servlet. mv $WAR /opt/jetty/webapps/root.war  # Make sure "jetty" owns everything. chown --recursive jetty /opt/jetty  # Configure the default paths for the Jetty service cp /opt/jetty/bin/jetty.sh /etc/init.d/jetty echo "JETTY_HOME=/opt/jetty" > /etc/default/jetty {   echo "JETTY_BASE=/opt/jetty"   echo "TMPDIR=/opt/jetty/temp"   echo "JAVA_OPTIONS=-Djetty.http.port=8080"   echo "JETTY_LOGS=/var/log/jetty" } >> /etc/default/jetty   # Reload daemon to pick up new service systemctl daemon-reload  # Install logging monitor. The monitor will automatically pickup logs sent to syslog. curl -s "https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh" | bash service google-fluentd restart &  service jetty start service jetty check  echo "Startup Complete"

Node.js

set -ex  # Talk to the metadata server to get the project id and location of application binary. PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google") GCS_BUCKET_NAME=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/gcs-bucket" -H "Metadata-Flavor: Google") REDISHOST=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-host" -H "Metadata-Flavor: Google") REDISPORT=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-port" -H "Metadata-Flavor: Google")  # Install dependencies from apt apt-get update # Install Node.js 9 curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - apt-get install -yq ca-certificates supervisor nodejs build-essential   # Install logging monitor. The monitor will automatically pickup logs send to # syslog. curl -s "https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh" | bash service google-fluentd restart &  gsutil cp gs://"$GCS_BUCKET_NAME"/gce/app.tar /app.tar mkdir -p /app tar -x -f /app.tar -C /app cd /app # Install the app dependencies npm install  # Create a nodeapp user. The application will run as this user. getent passwd nodeapp || useradd -m -d /home/nodeapp nodeapp chown -R nodeapp:nodeapp /app  # Configure supervisor to run the Go app. cat >/etc/supervisor/conf.d/nodeapp.conf << EOF [program:nodeapp] directory=/app environment=HOME="/home/nodeapp",USER="nodeapp",REDISHOST=$REDISHOST,REDISPORT=$REDISPORT command=node server.js autostart=true autorestart=true user=nodeapp stdout_logfile=syslog stderr_logfile=syslog EOF  supervisorctl reread supervisorctl update

Python

set -v  # Talk to the metadata server to get the project id and location of application binary. PROJECTID=$(curl -s "http://metadata.google.internal/computeMetadata/v1/project/project-id" -H "Metadata-Flavor: Google") GCS_BUCKET_NAME=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/gcs-bucket" -H "Metadata-Flavor: Google") REDISHOST=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-host" -H "Metadata-Flavor: Google") REDISPORT=$(curl -s "http://metadata.google.internal/computeMetadata/v1/instance/attributes/redis-port" -H "Metadata-Flavor: Google")  # Install dependencies from apt apt-get update apt-get install -yq \     git build-essential supervisor python python-dev python-pip libffi-dev \     libssl-dev  # Install logging monitor. The monitor will automatically pickup logs send to # syslog. curl -s "https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh" | bash service google-fluentd restart &  gsutil cp gs://"$GCS_BUCKET_NAME"/gce/app.tar /app.tar mkdir -p /app tar -x -f /app.tar -C /app cd /app  # Install the app dependencies pip install --upgrade pip virtualenv virtualenv /app/env /app/env/bin/pip install -r /app/requirements.txt  # Create a pythonapp user. The application will run as this user. getent passwd pythonapp || useradd -m -d /home/pythonapp pythonapp chown -R pythonapp:pythonapp /app  # Configure supervisor to run the app. cat >/etc/supervisor/conf.d/pythonapp.conf << EOF [program:pythonapp] directory=/app environment=HOME="/home/pythonapp",USER="pythonapp",REDISHOST=$REDISHOST,REDISPORT=$REDISPORT command=/app/env/bin/gunicorn main:app --bind 0.0.0:8080 autostart=true autorestart=true user=pythonapp stdout_logfile=syslog stderr_logfile=syslog EOF  supervisorctl reread supervisorctl update

Le déploiement de l'exemple d'application sur la nouvelle instance Compute Engine peut prendre plusieurs minutes.

Une fois l'instance prête et l'exécution du script de démarrage terminée, accédez à la page Instances de VM de Compute Engine, puis copiez l'adresse IP externe.

Pour afficher l'exemple d'application que vous avez déployé, accédez à http://[EXTERNAL-IP]:8080.

Vous pouvez utiliser le script teardown.sh pour supprimer l'instance et le pare-feu créés par le script de déploiement :

gcloud compute instances delete my-instance  gcloud compute firewall-rules delete allow-http-server-8080