Connect to a Redis instance from a Compute Engine VM
Stay organized with collections
Save and categorize content based on your preferences.
You can connect to your Redis instance from Compute Engine VM instances that use the same authorized network as your Redis instance.
Setup
If you have already installed the Google Cloud CLI, created a Redis instance, and created a Cloud Storage bucket you can skip these steps.
Install the gcloud CLI and initialize:
gcloud initFollow the Quickstart Guide to create a Redis instance. Take note of the zone, IP address, and port of the Redis instance.
Create a Cloud Storage bucket where the application artifact for this sample application will be uploaded. For more, see Create buckets.
Configuring the gcloud settings for the sample application
- Set the
gclouddefault project to your sample app project.gcloud config set project [PROJECT_ID]
Sample application
This sample HTTP server application establishes a connection to a Redis instance from a Compute Engine VM instance.
Clone the repository for your desired programming language and navigate to the folder that contains the sample code:
Go
gitclonehttps://github.com/GoogleCloudPlatform/golang-samples
cdgolang-samples/memorystore/redis
Java
gitclonehttps://github.com/GoogleCloudPlatform/java-docs-samples
cdjava-docs-samples/memorystore/redis
Node.js
gitclonehttps://github.com/GoogleCloudPlatform/nodejs-docs-samples
cdnodejs-docs-samples/memorystore/redis
Python
git clone https://github.com/GoogleCloudPlatform/python-docs-samples
cd python-docs-samples/memorystore/redis
This sample application increments a Redis counter every time the / endpoint
is accessed.
Go
This application uses the
github.com/gomodule/redigo/redis
client. Install it by running the following command:
gogetgithub.com/gomodule/redigo/redis
// Command redis is a basic app that connects to a managed Redis instance.
packagemain
import(
"fmt"
"log"
"net/http"
"os"
"github.com/gomodule/redigo/redis"
)
varredisPool*redis.Pool
funcincrementHandler(whttp.ResponseWriter,r*http.Request){
conn:=redisPool.Get()
deferconn.Close()
counter,err:=redis.Int(conn.Do("INCR","visits"))
iferr!=nil{
http.Error(w,"Error incrementing visitor counter",http.StatusInternalServerError)
return
}
fmt.Fprintf(w,"Visitor number: %d",counter)
}
funcmain(){
redisHost:=os.Getenv("REDISHOST")
redisPort:=os.Getenv("REDISPORT")
redisAddr:=fmt.Sprintf("%s:%s",redisHost,redisPort)
constmaxConnections=10
redisPool=&redis.Pool{
MaxIdle:maxConnections,
Dial:func()(redis.Conn,error){returnredis.Dial("tcp",redisAddr)},
}
http.HandleFunc("/",incrementHandler)
port:=os.Getenv("PORT")
ifport==""{
port="8080"
}
log.Printf("Listening on port %s",port)
iferr:=http.ListenAndServe(":"+port,nil);err!=nil{
log.Fatal(err)
}
}
Java
This application is Jetty 3.1 servlet-based.
It uses the Jedis library:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency>The AppServletContextListener class is used to create a long-lived
Redis connection pool:
packagecom.example.redis;
importjava.io.IOException;
importjava.util.Properties;
importjavax.servlet.ServletContextEvent;
importjavax.servlet.ServletContextListener;
importjavax.servlet.annotation.WebListener;
importredis.clients.jedis.JedisPool;
importredis.clients.jedis.JedisPoolConfig;
@WebListener
publicclass AppServletContextListenerimplementsServletContextListener{
privatePropertiesconfig=newProperties();
privateJedisPoolcreateJedisPool()throwsIOException{
Stringhost;
Integerport;
config.load(
Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("application.properties"));
host=config.getProperty("redis.host");
port=Integer.valueOf(config.getProperty("redis.port","6379"));
JedisPoolConfigpoolConfig=newJedisPoolConfig();
// Default : 8, consider how many concurrent connections into Redis you will need under load
poolConfig.setMaxTotal(128);
returnnewJedisPool(poolConfig,host,port);
}
@Override
publicvoidcontextDestroyed(ServletContextEventevent){
JedisPooljedisPool=(JedisPool)event.getServletContext().getAttribute("jedisPool");
if(jedisPool!=null){
jedisPool.destroy();
event.getServletContext().setAttribute("jedisPool",null);
}
}
// Run this before web application is started
@Override
publicvoidcontextInitialized(ServletContextEventevent){
JedisPooljedisPool=(JedisPool)event.getServletContext().getAttribute("jedisPool");
if(jedisPool==null){
try{
jedisPool=createJedisPool();
event.getServletContext().setAttribute("jedisPool",jedisPool);
}catch(IOExceptione){
// handle exception
}
}
}
}The VisitCounterServlet class is a web servlet that increments
a Redis counter:
packagecom.example.redis;
importjava.io.IOException;
importjava.net.SocketException;
importjavax.servlet.annotation.WebServlet;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importredis.clients.jedis.Jedis;
importredis.clients.jedis.JedisPool;
@WebServlet(name="Track visits",value="")
publicclass VisitCounterServletextendsHttpServlet{
@Override
publicvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsIOException{
try{
JedisPooljedisPool=(JedisPool)req.getServletContext().getAttribute("jedisPool");
if(jedisPool==null){
thrownewSocketException("Error connecting to Jedis pool");
}
Longvisits;
try(Jedisjedis=jedisPool.getResource()){
visits=jedis.incr("visits");
}
resp.setStatus(HttpServletResponse.SC_OK);
resp.getWriter().println("Visitor counter: "+String.valueOf(visits));
}catch(Exceptione){
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,e.getMessage());
}
}
}Node.js
This application uses the redis
module.
{
"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';
consthttp=require('http');
constredis=require('redis');
constREDISHOST=process.env.REDISHOST||'localhost';
constREDISPORT=process.env.REDISPORT||6379;
constclient=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
This application uses Flask
for web serving and the redis-py
package to communicate with the Redis instance.
Flask==3.0.3
gunicorn==23.0.0
redis==6.0.0
Werkzeug==3.0.3importlogging
importos
fromflaskimport Flask
importredis
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("/")
defindex():
value = redis_client.incr("counter", 1)
return f"Visitor number: {value}"
@app.errorhandler(500)
defserver_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)Deploying the application to a Compute Engine VM
Navigate to the gce_deployment directory:
cd gce_deployment
The deployment script uploads the artifact to the Cloud Storage path. It then launches a Compute Engine instance, creating a firewall to expose the port 8080. Then the startup script prepares the instance.
Set the REDISHOST and REDISPORT environmental variables:
export REDISHOST=[REDISHOST] export REDISPORT=[REDISPORT]
where:
REDISHOSTis the managed Redis instance IP.REDISPORTis the managed Redis instance port by default 6379.
Set the GCS_BUCKET_NAME environmental variable:
export GCS_BUCKET_NAME=[BUCKET_NAME]/[PATH]
where:
BUCKET_NAMEis the name of your Cloud Storage bucket.PATHis an optional path for the directory where you want to store the application artifact.
Here is a sample deployment script that deploys this application on a new Compute Engine VM instance.
Go
if[-z"$REDISHOST"];then
echo"Must set \$REDISHOST. For example: REDISHOST=127.0.0.1"
exit1
fi
if[-z"$REDISPORT"];then
echo"Must set \$REDISPORT. For example: REDISPORT=6379"
exit1
fi
if[-z"$GCS_BUCKET_NAME"];then
echo"Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"
exit1
fi
if[-z"$ZONE"];then
ZONE=$(gcloudconfigget-valuecompute/zone-q)
echo"$ZONE"
fi
# Cross compile the app for linux/amd64
GOOS=linuxGOARCH=amd64gobuild-v-oapp../main.go
# Add the app binary
tar-cvfapp.tarapp
# Copy to GCS bucket
gsutilcpapp.targs://"$GCS_BUCKET_NAME"/gce/
# Create an instance
gcloudcomputeinstancescreatemy-instance\
--image-family=debian-9\
--image-project=debian-cloud\
--machine-type=g1-small\
--scopescloud-platform\
--metadata-from-filestartup-script=startup-script.sh\
--metadatagcs-bucket="$GCS_BUCKET_NAME",redis-host="$REDISHOST",redis-port="$REDISPORT"\
--zone"$ZONE"\
--tagshttp-server
gcloudcomputefirewall-rulescreateallow-http-server-8080\
--allowtcp:8080\
--source-ranges0.0.0.0/0\
--target-tagshttp-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"
exit1
fi
if[-z"$ZONE"];then
ZONE=$(gcloudconfigget-valuecompute/zone-q)
echo$ZONE
fi
if[-z"$WAR"];then
WAR=visitcounter-1.0-SNAPSHOT.war
fi
#Build the WAR package
cd..
mvncleanpackage
#Copy the WAR artifact to the GCS bucket location
gsutilcp-rtarget/${WAR}gs://"$GCS_BUCKET_NAME"/gce/
cdgce_deployment
# Create an instance
gcloudcomputeinstancescreatemy-instance\
--image-family=debian-9\
--image-project=debian-cloud\
--machine-type=g1-small\
--scopescloud-platform\
--metadata-from-filestartup-script=startup-script.sh\
--metadatagcs-bucket=$GCS_BUCKET_NAME,app-war=$WAR\
--zone$ZONE\
--tagshttp-server
gcloudcomputefirewall-rulescreateallow-http-server-8080\
--allowtcp:8080\
--source-ranges0.0.0.0/0\
--target-tagshttp-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"
exit1
fi
if[-z"$REDISPORT"];then
echo"Must set \$REDISPORT. For example: REDISPORT=6379"
exit1
fi
if[-z"$GCS_BUCKET_NAME"];then
echo"Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"
exit1
fi
if[-z"$ZONE"];then
ZONE=$(gcloudconfigget-valuecompute/zone-q)
echo$ZONE
fi
#Upload the tar to GCS
tar-cvfapp.tar-C..package.jsonserver.js
gsutilcpapp.targs://"$GCS_BUCKET_NAME"/gce/
# Create an instance
gcloudcomputeinstancescreatemy-instance\
--image-family=debian-9\
--image-project=debian-cloud\
--machine-type=g1-small\
--scopescloud-platform\
--metadata-from-filestartup-script=startup-script.sh\
--metadatagcs-bucket=$GCS_BUCKET_NAME,redis-host=$REDISHOST,redis-port=$REDISPORT\
--zone$ZONE\
--tagshttp-server
gcloudcomputefirewall-rulescreateallow-http-server-8080\
--allowtcp:8080\
--source-ranges0.0.0.0/0\
--target-tagshttp-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"
exit1
fi
if[-z"$REDISPORT"];then
echo"Must set \$REDISPORT. For example: REDISPORT=6379"
exit1
fi
if[-z"$GCS_BUCKET_NAME"];then
echo"Must set \$GCS_BUCKET_NAME. For example: GCS_BUCKET_NAME=my-bucket"
exit1
fi
if[-z"$ZONE"];then
ZONE=$(gcloudconfigget-valuecompute/zone-q)
echo$ZONE
fi
#Upload the tar to GCS
tar-cvfapp.tar-C..requirements.txtmain.py
# Copy to GCS bucket
gsutilcpapp.targs://"$GCS_BUCKET_NAME"/gce/
# Create an instance
gcloudcomputeinstancescreatemy-instance\
--image-family=debian-11\
--image-project=debian-cloud\
--machine-type=g1-small\
--scopescloud-platform\
--metadata-from-filestartup-script=startup-script.sh\
--metadatagcs-bucket=$GCS_BUCKET_NAME,redis-host=$REDISHOST,redis-port=$REDISPORT\
--zone$ZONE\
--tagshttp-server
gcloudcomputefirewall-rulescreateallow-http-server-8080\
--allowtcp:8080\
--source-ranges0.0.0.0/0\
--target-tagshttp-server\
--description"Allow port 8080 access to http-server"Run the deployment script:
chmod +x deploy.sh
./deploy.sh
Application startup script
This startup script is used in the sample deployment script to prepare the instance. For more information about start up scripts and viewing startup script execution logs, see Running Startup Scripts.
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")
exportPROJECTID
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-getupdate
apt-getinstall-yqca-certificatessupervisor
# Install logging monitor. The monitor will automatically pickup logs send to
# syslog.
curl"https://storage.googleapis.com/signals-agents/logging/google-fluentd-install.sh"--outputgoogle-fluentd-install.sh
checksum=$(sha256sumgoogle-fluentd-install.sh|awk'{print 1ドル;}')
if["$checksum"!="ec78e9067f45f6653a6749cf922dbc9d79f80027d098c90da02f71532b5cc967"];then
echo"Checksum does not match"
exit1
fi
chmod+xgoogle-fluentd-install.sh && ./google-fluentd-install.sh
servicegoogle-fluentdrestart&
gsutilcpgs://"$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.
getentpasswdgoapp||useradd-m-d/home/goappgoapp
chown-Rgoapp: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
supervisorctlreread
supervisorctlupdateJava
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")
gsutilcpgs://"$GCS_BUCKET_NAME"/gce/"$WAR".
# Install dependencies from apt
apt-getupdate
apt-getinstall-qqopenjdk-8-jdk-headless
# Make Java8 the default
update-alternatives--setjava/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-Lhttps://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.10.v20180503/jetty-distribution-9.4.10.v20180503.tar.gz-ojetty9.tgz
tarxfjetty9.tgz--strip-components=1-C/opt/jetty
# Add a Jetty User
useradd--user-group--shell/bin/false--home-dir/opt/jetty/tempjetty
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--recursivejetty/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
systemctldaemon-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
servicegoogle-fluentdrestart&
servicejettystart
servicejettycheck
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-getupdate
# Install Node.js 9
curl-sLhttps://deb.nodesource.com/setup_9.x|sudo-Ebash-
apt-getinstall-yqca-certificatessupervisornodejsbuild-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
servicegoogle-fluentdrestart&
gsutilcpgs://"$GCS_BUCKET_NAME"/gce/app.tar/app.tar
mkdir-p/app
tar-x-f/app.tar-C/app
cd/app
# Install the app dependencies
npminstall
# Create a nodeapp user. The application will run as this user.
getentpasswdnodeapp||useradd-m-d/home/nodeappnodeapp
chown-Rnodeapp: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=nodeserver.js
autostart=true
autorestart=true
user=nodeapp
stdout_logfile=syslog
stderr_logfile=syslog
EOF
supervisorctlreread
supervisorctlupdatePython
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-getupdate
apt-getinstall-yq\
gitbuild-essentialsupervisorpythonpython-devpython-piplibffi-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
servicegoogle-fluentdrestart&
gsutilcpgs://"$GCS_BUCKET_NAME"/gce/app.tar/app.tar
mkdir-p/app
tar-x-f/app.tar-C/app
cd/app
# Install the app dependencies
pipinstall--upgradepipvirtualenv
virtualenv/app/env
/app/env/bin/pipinstall-r/app/requirements.txt
# Create a pythonapp user. The application will run as this user.
getentpasswdpythonapp||useradd-m-d/home/pythonapppythonapp
chown-Rpythonapp: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/gunicornmain:app--bind0.0.0:8080
autostart=true
autorestart=true
user=pythonapp
stdout_logfile=syslog
stderr_logfile=syslog
EOF
supervisorctlreread
supervisorctlupdateIt may take several minutes for the sample application to deploy on the newly created Compute Engine instance.
Once the instance is ready and the startup script has completed execution, go to the Compute Engine instances page and copy the External Ip address.
To view the sample app you deployed, visit http://[EXTERNAL-IP]:8080
You can use the teardown.sh script to delete the instance and firewall created by the deployment script:
gcloudcomputeinstancesdeletemy-instance
gcloudcomputefirewall-rulesdeleteallow-http-server-8080