Production Deployment and Best Practices
Learning Objectives: By the end of this lesson, you will be able to:
- Deploy Redis in production environments with high availability and security
- Configure Redis for optimal performance and reliability
- Implement monitoring, alerting, and maintenance procedures
- Design Redis architectures that scale to millions of operations per second
- Handle Redis upgrades, backups, and disaster recovery scenarios
1. Welcome and Introduction (5 minutes)
Icebreaker Activity
You’ve learned to build amazing applications with Redis, but now comes the real challenge: running them in production where millions of users depend on your system being fast, secure, and always available. Today we’ll learn how companies like Netflix, Twitter, and GitHub deploy Redis at massive scale!
2. Learning Objectives and Session Outline (5 minutes)
What We’ll Cover Today:
- Production Architecture - High availability, clustering, and security design
- Configuration Tuning - Memory management, persistence, and performance optimization
- Monitoring & Alerting - Key metrics, dashboards, and automated responses
- Backup & Recovery - Data protection and disaster recovery strategies
- Security Hardening - Authentication, encryption, and network security
- Scaling Strategies - Horizontal scaling with Redis Cluster and Sentinel
3. Foundation Knowledge (15-20 minutes)
Production Requirements
The Five Pillars of Production Redis
- Availability - System uptime and failover capabilities
- Performance - Consistent low latency under load
- Security - Data protection and access control
- Scalability - Ability to handle growth
- Observability - Monitoring and troubleshooting capabilities
Production Architecture Patterns
1. Master-Replica with Sentinel (High Availability)
[App Servers] --> [Redis Sentinel] --> [Redis Master]
| |
| [Redis Replica 1]
| [Redis Replica 2]
[Health Checks] [Automatic Failover]
2. Redis Cluster (Horizontal Scaling)
[App Servers] --> [Redis Cluster]
|
[Master 1] [Master 2] [Master 3]
| | |
[Replica 1] [Replica 2] [Replica 3]
4. Production Configuration (25-30 minutes)
Redis Configuration File (redis.conf)
# === PRODUCTION REDIS CONFIGURATION ===
# Basic settings
bind 127.0.0.1 10.0.1.100 # Bind to specific interfaces
port 6379
protected-mode yes
requirepass your_secure_password_here
# Memory management
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 10
# Persistence configuration
save 900 1 # Save if at least 1 key changed in 900 seconds
save 300 10 # Save if at least 10 keys changed in 300 seconds
save 60 10000 # Save if at least 10000 keys changed in 60 seconds
# AOF configuration for durability
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# Replication settings
replica-read-only yes
replica-serve-stale-data yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# Security settings
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command SHUTDOWN SHUTDOWN_8f2a9d3e
# Network and connection settings
tcp-keepalive 300
timeout 0
tcp-backlog 511
maxclients 10000
# Logging
loglevel notice
logfile /var/log/redis/redis-server.log
syslog-enabled yes
syslog-ident redis
# Slow log
slowlog-log-slower-than 10000 # 10ms
slowlog-max-len 128
# Latency monitoring
latency-monitor-threshold 100 # 100ms
Go Production Client Configuration
package main
import (
"context"
"crypto/tls"
"time"
"github.com/redis/go-redis/v9"
)
type ProductionRedisConfig struct {
Addresses []string
Password string
DB int
PoolSize int
MinIdleConns int
MaxRetries int
EnableTLS bool
}
func NewProductionRedisClient(config ProductionRedisConfig) *redis.Client {
options := &redis.Options{
Addr: config.Addresses[0], // Primary address
Password: config.Password,
DB: config.DB,
PoolSize: config.PoolSize,
MinIdleConns: config.MinIdleConns,
MaxRetries: config.MaxRetries,
// Timeouts
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
IdleTimeout: 5 * time.Minute,
// Connection pool settings
PoolTimeout: 4 * time.Second,
IdleCheckFrequency: time.Minute,
MaxConnAge: 30 * time.Minute,
}
// TLS configuration for production
if config.EnableTLS {
options.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: "redis.yourdomain.com",
}
}
return redis.NewClient(options)
}
// Redis Cluster client for horizontal scaling
func NewProductionRedisCluster(config ProductionRedisConfig) *redis.ClusterClient {
return redis.NewClusterClient(&redis.ClusterOptions{
Addrs: config.Addresses,
Password: config.Password,
PoolSize: config.PoolSize,
MinIdleConns: config.MinIdleConns,
MaxRetries: config.MaxRetries,
// Cluster-specific settings
MaxRedirects: 8,
ReadOnly: false,
RouteByLatency: true,
RouteRandomly: false,
// Timeouts
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
PoolTimeout: 4 * time.Second,
IdleTimeout: 5 * time.Minute,
})
}
// Connection health checking
type HealthChecker struct {
client *redis.Client
}
func (hc *HealthChecker) CheckHealth(ctx context.Context) error {
// Basic ping test
if err := hc.client.Ping(ctx).Err(); err != nil {
return fmt.Errorf("ping failed: %w", err)
}
// Memory usage check
info, err := hc.client.Info(ctx, "memory").Result()
if err != nil {
return fmt.Errorf("info command failed: %w", err)
}
// Parse memory usage (simplified)
if strings.Contains(info, "maxmemory_policy:noeviction") {
memoryUsage, _ := hc.client.Info(ctx, "memory").Result()
if strings.Contains(memoryUsage, "used_memory_rss:") {
// Add memory usage validation logic
}
}
return nil
}
func main() {
config := ProductionRedisConfig{
Addresses: []string{"redis-master:6379", "redis-replica1:6379", "redis-replica2:6379"},
Password: "your_production_password",
DB: 0,
PoolSize: 20,
MinIdleConns: 10,
MaxRetries: 3,
EnableTLS: true,
}
client := NewProductionRedisClient(config)
defer client.Close()
// Health checking
healthChecker := &HealthChecker{client: client}
ctx := context.Background()
if err := healthChecker.CheckHealth(ctx); err != nil {
log.Fatalf("Redis health check failed: %v", err)
}
log.Println("Redis client configured and healthy")
}
5. Monitoring and Alerting (20-25 minutes)
Essential Redis Metrics
package monitoring
import (
"context"
"log"
"strconv"
"strings"
"time"
"github.com/redis/go-redis/v9"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// Prometheus metrics
var (
redisConnectedClients = promauto.NewGauge(prometheus.GaugeOpts{
Name: "redis_connected_clients",
Help: "Number of connected clients",
})
redisUsedMemory = promauto.NewGauge(prometheus.GaugeOpts{
Name: "redis_used_memory_bytes",
Help: "Used memory in bytes",
})
redisOperations = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "redis_operations_total",
Help: "Total number of Redis operations",
}, []string{"operation"})
redisLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "redis_operation_duration_seconds",
Help: "Redis operation latency",
Buckets: prometheus.DefBuckets,
}, []string{"operation"})
redisErrors = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "redis_errors_total",
Help: "Total number of Redis errors",
}, []string{"type"})
)
type RedisMonitor struct {
client *redis.Client
ctx context.Context
interval time.Duration
}
func NewRedisMonitor(client *redis.Client) *RedisMonitor {
return &RedisMonitor{
client: client,
ctx: context.Background(),
interval: 30 * time.Second,
}
}
func (rm *RedisMonitor) Start() {
ticker := time.NewTicker(rm.interval)
go func() {
for range ticker.C {
rm.collectMetrics()
}
}()
}
func (rm *RedisMonitor) collectMetrics() {
info, err := rm.client.Info(rm.ctx).Result()
if err != nil {
redisErrors.WithLabelValues("info_command").Inc()
log.Printf("Failed to get Redis info: %v", err)
return
}
rm.parseAndSetMetrics(info)
}
func (rm *RedisMonitor) parseAndSetMetrics(info string) {
lines := strings.Split(info, "\r\n")
for _, line := range lines {
if strings.Contains(line, ":") {
parts := strings.Split(line, ":")
if len(parts) != 2 {
continue
}
key, value := parts[0], parts[1]
switch key {
case "connected_clients":
if val, err := strconv.ParseFloat(value, 64); err == nil {
redisConnectedClients.Set(val)
}
case "used_memory":
if val, err := strconv.ParseFloat(value, 64); err == nil {
redisUsedMemory.Set(val)
}
case "total_commands_processed":
if val, err := strconv.ParseFloat(value, 64); err == nil {
redisOperations.WithLabelValues("total").Add(val)
}
}
}
}
}
// Custom Redis client with metrics
type MonitoredRedisClient struct {
*redis.Client
}
func (mrc *MonitoredRedisClient) Get(ctx context.Context, key string) *redis.StringCmd {
start := time.Now()
result := mrc.Client.Get(ctx, key)
duration := time.Since(start).Seconds()
redisLatency.WithLabelValues("get").Observe(duration)
if result.Err() != nil {
redisErrors.WithLabelValues("get").Inc()
} else {
redisOperations.WithLabelValues("get").Inc()
}
return result
}
func (mrc *MonitoredRedisClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.StatusCmd {
start := time.Now()
result := mrc.Client.Set(ctx, key, value, expiration)
duration := time.Since(start).Seconds()
redisLatency.WithLabelValues("set").Observe(duration)
if result.Err() != nil {
redisErrors.WithLabelValues("set").Inc()
} else {
redisOperations.WithLabelValues("set").Inc()
}
return result
}
Alerting Rules (Prometheus/AlertManager)
# redis-alerts.yml
groups:
- name: redis
rules:
- alert: RedisDown
expr: up{job="redis"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Redis instance is down"
description: "Redis instance {{ $labels.instance }} has been down for more than 1 minute"
- alert: RedisHighMemoryUsage
expr: redis_used_memory_bytes / redis_maxmemory_bytes > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Redis memory usage is high"
description: "Redis instance {{ $labels.instance }} memory usage is above 90%"
- alert: RedisHighLatency
expr: rate(redis_operation_duration_seconds_sum[5m]) / rate(redis_operation_duration_seconds_count[5m]) > 0.1
for: 2m
labels:
severity: warning
annotations:
summary: "Redis high latency detected"
description: "Redis instance {{ $labels.instance }} average latency is above 100ms"
- alert: RedisConnectionLimitReached
expr: redis_connected_clients / redis_maxclients > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Redis connection limit nearly reached"
description: "Redis instance {{ $labels.instance }} is using more than 80% of available connections"
- alert: RedisSlowlogGrowing
expr: increase(redis_slowlog_length[5m]) > 10
for: 1m
labels:
severity: warning
annotations:
summary: "Redis slowlog is growing"
description: "Redis instance {{ $labels.instance }} slowlog has grown by more than 10 entries in 5 minutes"
6. Backup and Recovery (15 minutes)
Automated Backup Strategy
#!/bin/bash
# redis-backup.sh - Production Redis backup script
set -euo pipefail
# Configuration
REDIS_HOST="localhost"
REDIS_PORT="6379"
REDIS_PASSWORD="your_password"
BACKUP_DIR="/backups/redis"
RETENTION_DAYS=30
S3_BUCKET="your-backup-bucket"
# Create backup directory
mkdir -p "$BACKUP_DIR"
# Generate backup filename with timestamp
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="redis_backup_${TIMESTAMP}.rdb"
BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILE}"
# Function to log with timestamp
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Function to cleanup old backups
cleanup_old_backups() {
find "$BACKUP_DIR" -name "redis_backup_*.rdb" -mtime +$RETENTION_DAYS -delete
log "Cleaned up backups older than $RETENTION_DAYS days"
}
# Function to create backup
create_backup() {
log "Starting Redis backup..."
# Use redis-cli to create backup
redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" \
--rdb "$BACKUP_PATH" --no-auth-warning
if [ $? -eq 0 ]; then
log "Backup created successfully: $BACKUP_PATH"
# Compress backup
gzip "$BACKUP_PATH"
BACKUP_PATH="${BACKUP_PATH}.gz"
log "Backup compressed: $BACKUP_PATH"
# Upload to S3
if command -v aws &> /dev/null; then
aws s3 cp "$BACKUP_PATH" "s3://${S3_BUCKET}/redis-backups/"
log "Backup uploaded to S3: s3://${S3_BUCKET}/redis-backups/$(basename $BACKUP_PATH)"
fi
# Verify backup integrity
verify_backup "$BACKUP_PATH"
else
log "ERROR: Backup failed"
exit 1
fi
}
# Function to verify backup
verify_backup() {
local backup_file="$1"
local file_size=$(stat -f%z "$backup_file" 2>/dev/null || stat -c%s "$backup_file" 2>/dev/null)
if [ "$file_size" -gt 0 ]; then
log "Backup verification passed - Size: ${file_size} bytes"
else
log "ERROR: Backup verification failed - File is empty or missing"
exit 1
fi
}
# Main execution
main() {
log "Starting Redis backup process"
# Check Redis connectivity
if ! redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -a "$REDIS_PASSWORD" ping --no-auth-warning > /dev/null; then
log "ERROR: Cannot connect to Redis server"
exit 1
fi
create_backup
cleanup_old_backups
log "Redis backup process completed successfully"
}
# Execute main function
main "$@"
Recovery Procedures
// Redis recovery utilities
package recovery
import (
"bufio"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
)
type RecoveryManager struct {
RedisDataDir string
BackupDir string
RedisConfig string
}
func NewRecoveryManager(dataDir, backupDir, configFile string) *RecoveryManager {
return &RecoveryManager{
RedisDataDir: dataDir,
BackupDir: backupDir,
RedisConfig: configFile,
}
}
func (rm *RecoveryManager) ListBackups() ([]string, error) {
pattern := filepath.Join(rm.BackupDir, "redis_backup_*.rdb*")
matches, err := filepath.Glob(pattern)
if err != nil {
return nil, fmt.Errorf("failed to list backups: %w", err)
}
return matches, nil
}
func (rm *RecoveryManager) RestoreFromBackup(backupFile string) error {
fmt.Printf("WARNING: This will replace current Redis data with backup from %s\n", backupFile)
fmt.Print("Do you want to continue? (yes/no): ")
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Text() != "yes" {
return fmt.Errorf("restore cancelled by user")
}
// Stop Redis service
if err := rm.stopRedisService(); err != nil {
return fmt.Errorf("failed to stop Redis service: %w", err)
}
// Backup current data
if err := rm.backupCurrentData(); err != nil {
return fmt.Errorf("failed to backup current data: %w", err)
}
// Restore from backup
if err := rm.restoreData(backupFile); err != nil {
return fmt.Errorf("failed to restore data: %w", err)
}
// Start Redis service
if err := rm.startRedisService(); err != nil {
return fmt.Errorf("failed to start Redis service: %w", err)
}
fmt.Println("Recovery completed successfully")
return nil
}
func (rm *RecoveryManager) stopRedisService() error {
cmd := exec.Command("systemctl", "stop", "redis-server")
return cmd.Run()
}
func (rm *RecoveryManager) startRedisService() error {
cmd := exec.Command("systemctl", "start", "redis-server")
return cmd.Run()
}
func (rm *RecoveryManager) backupCurrentData() error {
timestamp := time.Now().Format("20060102_150405")
backupName := fmt.Sprintf("pre_restore_backup_%s", timestamp)
currentRDB := filepath.Join(rm.RedisDataDir, "dump.rdb")
backupPath := filepath.Join(rm.BackupDir, backupName+".rdb")
if _, err := os.Stat(currentRDB); err == nil {
cmd := exec.Command("cp", currentRDB, backupPath)
return cmd.Run()
}
return nil // No current data to backup
}
func (rm *RecoveryManager) restoreData(backupFile string) error {
targetFile := filepath.Join(rm.RedisDataDir, "dump.rdb")
// Handle compressed backups
if filepath.Ext(backupFile) == ".gz" {
cmd := exec.Command("gunzip", "-c", backupFile)
output, err := cmd.Output()
if err != nil {
return err
}
return os.WriteFile(targetFile, output, 0644)
}
// Copy uncompressed backup
cmd := exec.Command("cp", backupFile, targetFile)
return cmd.Run()
}
// Point-in-time recovery for AOF
func (rm *RecoveryManager) RecoverToTimestamp(targetTime time.Time) error {
aofFile := filepath.Join(rm.RedisDataDir, "appendonly.aof")
// This is a simplified example - production implementation would be more complex
fmt.Printf("Recovering to timestamp: %s\n", targetTime.Format(time.RFC3339))
fmt.Printf("AOF file: %s\n", aofFile)
// In practice, you would:
// 1. Parse AOF file
// 2. Find commands up to target timestamp
// 3. Create new AOF with commands up to that point
// 4. Restart Redis with the truncated AOF
return fmt.Errorf("point-in-time recovery not implemented in this example")
}
7. Security Hardening (15 minutes)
Security Checklist
# Security configuration checklist
# 1. Network Security
# - Bind to specific interfaces only
bind 127.0.0.1 10.0.1.100
# - Use firewalls to restrict access
# iptables -A INPUT -p tcp --dport 6379 -s 10.0.1.0/24 -j ACCEPT
# iptables -A INPUT -p tcp --dport 6379 -j DROP
# 2. Authentication
requirepass your_very_secure_password_here
# Use ACL for fine-grained access control (Redis 6+)
# 3. Command Security
# Rename dangerous commands
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command CONFIG "CONFIG_a8b7c6d5e4f3"
# 4. TLS Encryption
port 0 # Disable non-TLS port
tls-port 6380 # Enable TLS port
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt
tls-protocols "TLSv1.2 TLSv1.3"
Go TLS Client Configuration
package security
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"github.com/redis/go-redis/v9"
)
func NewSecureRedisClient(addr, password, certFile, keyFile, caFile string) (*redis.Client, error) {
// Load client certificate
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("failed to load client certificate: %w", err)
}
// Load CA certificate
caCert, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, fmt.Errorf("failed to load CA certificate: %w", err)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
return nil, fmt.Errorf("failed to parse CA certificate")
}
// Configure TLS
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
ServerName: "redis.yourdomain.com",
}
// Create Redis client with TLS
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
TLSConfig: tlsConfig,
})
return client, nil
}
// ACL management (Redis 6+)
type ACLManager struct {
client *redis.Client
}
func NewACLManager(client *redis.Client) *ACLManager {
return &ACLManager{client: client}
}
func (am *ACLManager) CreateUser(username, password string, commands []string, keys []string) error {
// Build ACL command
args := []interface{}{"ACL", "SETUSER", username, "on", ">" + password}
// Add command permissions
for _, cmd := range commands {
args = append(args, "+"+cmd)
}
// Add key permissions
for _, key := range keys {
args = append(args, "~"+key)
}
return am.client.Do(context.Background(), args...).Err()
}
func (am *ACLManager) ListUsers() ([]string, error) {
result, err := am.client.Do(context.Background(), "ACL", "LIST").Result()
if err != nil {
return nil, err
}
// Parse result (simplified)
users := make([]string, 0)
if userList, ok := result.([]interface{}); ok {
for _, user := range userList {
if userStr, ok := user.(string); ok {
users = append(users, userStr)
}
}
}
return users, nil
}
func (am *ACLManager) DeleteUser(username string) error {
return am.client.Do(context.Background(), "ACL", "DELUSER", username).Err()
}
8. Production Deployment Scripts (10 minutes)
Docker Compose for Production
# docker-compose.prod.yml
version: '3.8'
services:
redis-master:
image: redis:7-alpine
container_name: redis-master
restart: unless-stopped
command: redis-server /etc/redis/redis.conf
volumes:
- ./redis-master.conf:/etc/redis/redis.conf:ro
- redis-master-data:/data
- ./logs:/var/log/redis
ports:
- "6379:6379"
networks:
- redis-network
environment:
- REDIS_REPLICATION_MODE=master
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
redis-replica1:
image: redis:7-alpine
container_name: redis-replica1
restart: unless-stopped
command: redis-server /etc/redis/redis.conf --replicaof redis-master 6379
volumes:
- ./redis-replica.conf:/etc/redis/redis.conf:ro
- redis-replica1-data:/data
- ./logs:/var/log/redis
networks:
- redis-network
depends_on:
- redis-master
environment:
- REDIS_REPLICATION_MODE=slave
redis-replica2:
image: redis:7-alpine
container_name: redis-replica2
restart: unless-stopped
command: redis-server /etc/redis/redis.conf --replicaof redis-master 6379
volumes:
- ./redis-replica.conf:/etc/redis/redis.conf:ro
- redis-replica2-data:/data
- ./logs:/var/log/redis
networks:
- redis-network
depends_on:
- redis-master
environment:
- REDIS_REPLICATION_MODE=slave
redis-sentinel1:
image: redis:7-alpine
container_name: redis-sentinel1
restart: unless-stopped
command: redis-sentinel /etc/redis/sentinel.conf
volumes:
- ./sentinel.conf:/etc/redis/sentinel.conf:ro
- ./logs:/var/log/redis
networks:
- redis-network
ports:
- "26379:26379"
depends_on:
- redis-master
redis-exporter:
image: oliver006/redis_exporter:latest
container_name: redis-exporter
restart: unless-stopped
environment:
- REDIS_ADDR=redis://redis-master:6379
- REDIS_PASSWORD=your_password
ports:
- "9121:9121"
networks:
- redis-network
depends_on:
- redis-master
volumes:
redis-master-data:
driver: local
redis-replica1-data:
driver: local
redis-replica2-data:
driver: local
networks:
redis-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
Kubernetes Deployment
# redis-cluster.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: production
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
name: client
- containerPort: 16379
name: gossip
command:
- redis-server
args:
- /etc/redis/redis.conf
- --cluster-enabled
- yes
- --cluster-config-file
- /data/nodes.conf
- --cluster-node-timeout
- "5000"
- --appendonly
- yes
- --protected-mode
- no
volumeMounts:
- name: redis-data
mountPath: /data
- name: redis-config
mountPath: /etc/redis
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: redis-config
configMap:
name: redis-config
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
storageClassName: fast-ssd
---
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
namespace: production
spec:
clusterIP: None
ports:
- port: 6379
targetPort: 6379
name: client
- port: 16379
targetPort: 16379
name: gossip
selector:
app: redis-cluster
9. Summary and Review (5 minutes)
Production Readiness Checklist
- ✅ High Availability: Master-replica setup with Sentinel or Redis Cluster
- ✅ Security: Authentication, TLS encryption, command renaming, network restrictions
- ✅ Monitoring: Comprehensive metrics, alerting, and dashboards
- ✅ Backups: Automated daily backups with off-site storage and recovery procedures
- ✅ Performance: Optimized configuration, connection pooling, memory management
- ✅ Scalability: Horizontal scaling strategy with Redis Cluster
- ✅ Compliance: Data encryption, access controls, audit logging
- ✅ Documentation: Runbooks, recovery procedures, configuration management
Key Production Metrics to Monitor
-
Performance Metrics
- Operations per second
- Average latency (p50, p95, p99)
- Memory usage and fragmentation
- Connection count
-
Availability Metrics
- Uptime percentage
- Failover frequency
- Replication lag
- Network partitions
-
Security Metrics
- Failed authentication attempts
- Suspicious command patterns
- Network connection sources
- Configuration changes
Final Architecture Example
Internet --> [Load Balancer] --> [App Servers] --> [Redis Sentinel] --> [Redis Cluster]
| | | |
[SSL Term] [Monitoring] [Health Checks] [Replication]
| | | |
[WAF] [Metrics] [Auto Failover] [Persistence]
Congratulations! You now have the knowledge to deploy and operate Redis at production scale. Remember: start simple, monitor everything, and scale incrementally based on actual needs.
For Instructors
Teaching Tips
- Use real production scenarios and war stories
- Demonstrate actual monitoring dashboards and alerts
- Show the impact of different configuration options
- Practice disaster recovery scenarios
- Emphasize the importance of gradual rollouts
Assessment Ideas
- Design a complete production Redis architecture
- Create monitoring and alerting configurations
- Develop backup and recovery procedures
- Security assessment of Redis deployment
- Performance tuning exercise with load testing
This completes our comprehensive Redis course - you’re now ready to master Redis in production! 🚀