Conectarse a una instancia de Redis desde una VM de Compute Engine

Puedes conectarte a tu instancia de Redis desde las instancias de VM de Compute Engine que usan la misma red autorizada que tu instancia de Redis.

Configuración

Si ya instalaste Google Cloud CLI y creaste una instancia de Redis y un bucket de Cloud Storage, puedes omitir estos pasos.

  1. Instala gcloud CLI y, luego, inicializa:

    gcloud init
    
  2. Sigue la Guía de inicio rápido para crear una instancia de Redis. Anota la zona, la dirección IP y el puerto de la instancia de Redis.

  3. Crea un bucket de Cloud Storage donde se subirá el artefacto de la aplicación para esta aplicación de muestra. Para obtener más información, consulta Crea buckets.

Configurar la configuración de gcloud para la aplicación de muestra

  1. Configura el proyecto predeterminado gcloud como tu proyecto de app de ejemplo.
    gcloud config set project [PROJECT_ID]

Aplicación de muestra

Esta aplicación del servidor HTTP establece una conexión a una instancia de Redis desde una instancia de VM de Compute Engine.

Clona el repositorio de tu lenguaje de programación deseado y navega a la carpeta que contiene el código de muestra:

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

Esta aplicación de muestra aumenta un contador de Redis cada vez que se accede al extremo /.

Go

En esta aplicación, se usa el cliente github.com/gomodule/redigo/redis. Para instalarla, ejecuta el siguiente comando:

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

Esta aplicación se basa en el servlet Jetty 3.1.

Usa la biblioteca de Jedis:

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

La clase AppServletContextListener se usa para crear un grupo de conexiones de Redis de larga duración:


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 clase VisitCounterServlet es un servlet web que incrementa un contador de 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

En esta aplicación, se usa el módulo 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

Esta aplicación usa Flask para la entrega web y el paquete redis-py a fin de comunicarse con la instancia de Redis.

Flask==3.0.0
gunicorn==20.1.0
redis==5.0.1
Werkzeug==3.0.1
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)

Implementar la aplicación en una VM de Compute Engine

Navega al directorio gce_deployment.

cd gce_deployment

La secuencia de comandos de implementación sube el artefacto a la ruta de Cloud Storage. Luego, ejecuta una instancia de Compute Engine y crea un firewall para exponer el puerto 8080. La secuencia de comandos de inicio prepara la instancia.

Esta es una secuencia de comandos de implementación de muestra que implementa esta aplicación en una nueva instancia de VM de Compute Engine.

Go

Configura las variables del entorno para apuntar a la IP y al puerto de la instancia de Redis administrada:

 export REDISHOST=XXX.XXX.XXX.XXX
 export REDISPORT=6379
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

Configura las variables del entorno para apuntar a la IP y al puerto de la instancia de Redis administrada:

 export REDISHOST=XXX.XXX.XXX.XXX
 export REDISPORT=6379
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

Configura las variables del entorno para apuntar a la IP y al puerto de la instancia de Redis administrada:

 export REDISHOST=XXX.XXX.XXX.XXX
 export REDISPORT=6379
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"

Configura la variable de entorno GCS_BUCKET_NAME:

     export GCS_BUCKET_NAME=[BUCKET_NAME]/[PATH]

Donde:

  • BUCKET_NAME es el nombre de tu bucket de Cloud Storage.
  • PATH es una ruta opcional para el directorio donde deseas almacenar el artefacto de la aplicación.

Ejecuta la secuencia de comandos de implementación:

     chmod +x deploy.sh
     ./deploy.sh

Secuencia de comandos de inicio de la aplicación

Esta secuencia de comandos de inicio se usa en la secuencia de comandos de implementación de ejemplo para preparar la instancia. Para obtener más información sobre la secuencia de comandos de inicio y la visualización de los registros de la secuencia de comandos de inicio, consulta Ejecutar secuencias de comandos de inicio.

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

La aplicación de muestra puede demorar varios minutos en implementarse en la instancia de Compute Engine recién creada.

Una vez que la instancia esté lista y que la secuencia de comandos de inicio haya completado la ejecución, ve a la página de instancias de Compute Engine y copia la dirección de la IP externa.

Para ver la app de muestra que implementaste, visita http://[EXTERNAL-IP]:8080

Puedes usar la secuencia de comandos teardown.sh para borrar la instancia y el firewall creado por la secuencia de comandos de implementación:

gcloud compute instances delete my-instance

gcloud compute firewall-rules delete allow-http-server-8080