Menghubungkan ke instance Redis dari cluster Google Kubernetes Engine

Anda hanya dapat terhubung ke instance Redis dari cluster Google Kubernetes Engine yang menggunakan jaringan yang sama dengan instance Redis.

Penyiapan

Jika Anda telah menginstal Google Cloud CLI dan membuat instance Redis, Anda dapat melewati langkah-langkah ini.

  1. Instal gcloud CLI dan lakukan inisialisasi:

    gcloud init
    
  2. Ikuti Panduan Memulai Cepat untuk membuat instance Redis. Catat zona, alamat IP, dan port instance Redis.

Menyiapkan cluster GKE

  1. Jika Anda belum membuat cluster GKE, buat cluster menggunakan perintah berikut untuk Google Cloud CLI:

    1. Tentukan project untuk aplikasi contoh ini di gcloud.

      gcloud config set project PROJECT_ID

    2. Tetapkan variabel konfigurasi zona Compute Engine di gcloud.

      gcloud config set compute/zone ZONE

    3. Buat cluster GKE bernama visitcount-cluster.

      gcloud container clusters create visitcount-cluster --num-nodes=3 --enable-ip-alias

  2. Jika Anda tidak membuat cluster menggunakan gcloud, gunakan perintah berikut untuk mengambil kredensial cluster:

    gcloud container clusters get-credentials CLUSTER_NAME --zone CLUSTER_ZONE --project PROJECT_ID
    1. CLUSTER_NAME adalah nama cluster GKE Anda.
    2. CLUSTER_ZONE adalah zona tempat cluster Anda berada.
    3. PROJECT_ID adalah project tempat cluster dan instance Redis Anda berada.
  3. Jika cluster Anda adalah versi 1.8 atau yang lebih tinggi dan mengaktifkan alias IP, lewati langkah ini. Jika cluster Anda adalah versi 1.7 atau yang lebih lama, atau cluster versi 1.8 atau yang lebih baru tidak mengaktifkan alias IP, ikuti langkah-langkah solusi berikut sebelum mencoba terhubung ke instance Anda.

    1. Jalankan perintah ini, dengan mengganti RESERVED_IP_RANGE dengan rentang IP yang dicadangkan untuk instance Anda:

      git clone https://github.com/bowei/k8s-custom-iptables.git
      cd k8s-custom-iptables/
      TARGETS="RESERVED_IP_RANGE" ./install.sh
      cd ..
      
    2. Jika Anda tidak mengetahui rentang IP yang dicadangkan untuk instance Anda, cari tahu dengan menggunakan konsol (opsi lanjutan) atau perintah berikut:

      gcloud redis instances describe INSTANCE_ID --region=REGION
      

    Untuk mengetahui informasi selengkapnya tentang IP alias, termasuk cara membuat cluster dengan setelan ini diaktifkan, lihat dokumentasi IP alias.

Contoh aplikasi

Aplikasi server HTTP contoh ini membuat koneksi ke instance Redis dari cluster Google Kubernetes Engine.

Buat clone repositori untuk bahasa pemrograman yang Anda pilih dan buka folder yang berisi kode contoh:

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

Aplikasi contoh ini menambah penghitung Redis setiap kali endpoint / diakses.

Go

Aplikasi ini menggunakan klien github.com/gomodule/redigo/redis. Instal dengan menjalankan perintah berikut:

go get github.com/gomodule/redigo/redis@latest

Contoh aplikasi:


// 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

Aplikasi ini berbasis servlet Jetty 3.1.

Library ini menggunakan library Jedis:

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

Class AppServletContextListener digunakan untuk membuat kumpulan koneksi Redis yang berjalan lama:


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

Class VisitCounterServlet adalah servlet web yang menambah penghitung 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

Aplikasi ini menggunakan modul 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

Aplikasi ini menggunakan Flask untuk penayangan web dan paket redis-py untuk berkomunikasi dengan 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)

Membuat image container

Bangun dan kirim image container ke Container Registry:

    gcloud artifacts repositories create --location REPO_REGION --repository-format=docker REPO_ID
    cp gke_deployment/Dockerfile .
    docker build -t REPO_REGION-docker.pkg.dev/PROJECT_ID/REPO_ID/visit-counter:v1 .
    gcloud docker -- push REPO_REGION-docker.pkg.dev/PROJECT_ID/REPO_ID/visit-counter:v1

Men-deploy aplikasi Anda ke Google Kubernetes Engine

Untuk menghindari hardcode IP instance Redis, Anda dapat membuat ConfigMap redishost:

    export REDISHOST_IP=REDISHOST_IP
    kubectl create configmap redishost --from-literal=REDISHOST=${REDISHOST_IP}

Verifikasi konfigurasi menggunakan perintah berikut:

kubectl get configmaps redishost -o yaml

Perbarui gke_deployment/visit-counter.yaml, dengan mengganti <REPO_REGION>, <PROJECT_ID>, dan <REPO_ID> dengan nilai dari image container yang dibuat di Membangun image container. File ini berisi konfigurasi untuk deployment dan layanan.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: visit-counter
  labels:
    app: visit-counter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: visit-counter
  template:
    metadata:
      labels:
        app: visit-counter
    spec:
      containers:
      - name: visit-counter
        image: "<REPO_REGION>-docker.pkg.dev/<PROJECT_ID>/<REPO_ID>/visit-counter:v1"
        env:
        - name: REDISHOST
          valueFrom:
            configMapKeyRef:
              name: redishost
              key: REDISHOST
        ports:
        - name: http
          containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: visit-counter
spec:
  type: LoadBalancer
  selector:
    app: visit-counter
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

Terapkan konfigurasi ke cluster Anda:

kubectl apply -f gke_deployment/visit-counter.yaml

Tentukan alamat IP eksternal untuk aplikasi contoh ini dengan menjalankan perintah berikut:

kubectl get service visit-counter

Verifikasi bahwa aplikasi Anda dihosting di IP eksternal menggunakan browser, atau kirim permintaan GET menggunakan curl atau browser Anda:

curl http://EXTERNAL_IP

Menghapus entri tabel IP untuk instance Redis

Jika Anda telah mengikuti langkah ketiga di bagian Menyiapkan cluster GKE dalam panduan ini, berarti Anda telah menginstal rentang IP yang dicadangkan dari instance Redis ke tabel IP instance GKE. Jika Anda ingin menghapus entri rentang IP Redis ini dari tabel IP instance GKE Anda, jalankan perintah berikut dari direktori k8s-custom-iptables/:

./uninstall.sh