docker 搭建redis集群/单机

文章目录

  • 前言
  • 目录结构
  • redis.conf(单机配置文件)
  • cluster-redis.conf(集群配置文件)
  • docker-compose.yml
  • golang测试代码

前言

  1. 基于docker环境和docker-compose

  2. 当前redis 6.2.6

  3. mac访问集群时可能出现链接超时问题。是因为mac没有docker0桥接,可参考docker-connettor方案

    https://docs.docker.com/desktop/mac/networking/
    https://github.com/wenjunxiao/mac-docker-connector/blob/master/README-ZH.md

      # 配合brew安装的客户端以及路由文件 可以在mac中使用集群
      mac-connector:
        image: wenjunxiao/mac-docker-connector
        container_name: mac-connector
        restart: always
        network_mode: host
        cap_add:
          - NET_ADMIN
    
  4. redis-cli创建集群时不识别hostname,必须使用ip。ip不固定的话,也可以使用dig命令动态获取,安装dig工具

    FROM redis:alpine
    RUN echo -e "https://mirrors.aliyun.com/alpine/v3.15/main" > /etc/apk/repositories
    RUN apk add bind-tools
    CMD ["bash"]
    
    sh -c "redis-cli --cluster create 
    $(dig redis_6380 +short):6380 
    $(dig redis_6381 +short):6381 
    $(dig redis_6382 +short):6382 
    $(dig redis_6383 +short):6383 
    $(dig redis_6384 +short):6384 
    $(dig redis_6385 +short):6385 
    --cluster-replicas 1" 
    
  5. 集群链接时参数 -c

    redis-cli -c [...]
    

目录结构

docker 搭建redis集群/单机_第1张图片

redis 6380,6381,6382,6383,6384,6385 组成集群
redis 6379 单机

RedisDesktopManager可视化管理工具
docker 搭建redis集群/单机_第2张图片

redis.conf(单机配置文件)

#端口
port 6379
#ip 
bind 0.0.0.0
# 关闭本地访问保护模式
protected-mode no

#rdb 策略
# save ""  关闭rdb
save 900 1  # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000
#rdb压缩 默认yes 消耗cpu
rdbcompression yes
# 保存rdb文件的时候,进行错误的检查校验,默认yes
rdbchecksum yes
#指定rdb文件的名称,默认是dump.rdb
dbfilename dump.rdb

#aof 策略
appendonly no
#1.no 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。
#2.always 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全。
#3.everysec 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据。
appendfsync everysec
#指定aof文件的名称,默认为appendonly.aof
appendfilename appendonly.aof

#设置RDB和AOF文件目录
dir /data

cluster-redis.conf(集群配置文件)

#端口
port 6379
#ip
bind 0.0.0.0
# 关闭本地访问保护模式
protected-mode no

#开启集群功能
cluster-enabled yes
#集群节点配置文件名,该文件不是人工编写的,由程序自动生成和修改
cluster-config-file nodes.conf
#集群节点超时时间,配合cluster-replica-validity-factor使用
cluster-node-timeout 15000
#探测集群节点超时不可用的次数,假设cluster-node-timeout设置为1000毫秒,cluster-replica-validity-factor为5,那么1000*5之间内集群节点还不可用的话,会被标记为疑似下线
cluster-replica-validity-factor 5
#主节点下最少的从节点数
cluster-migration-barrier 1
#yes 要求所有主节点正常工作,且所有hash slots被分配到工作的主节点,集群才能提供服务,如果想一部分hash slots即可响应请求,则设置为no
cluster-require-full-coverage yes
#yes 禁止当主节点挂掉时,让从节点不能竞选为主节点
cluster-replica-no-failover no


#rdb 策略
# save ""  关闭rdb
save 900 1  # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000
#rdb压缩 默认yes 消耗cpu
rdbcompression yes
# 保存rdb文件的时候,进行错误的检查校验,默认yes
rdbchecksum yes
#指定rdb文件的名称,默认是dump.rdb
dbfilename dump.rdb

#aof 策略
appendonly no
#1.no 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差。
#2.always 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全。
#3.everysec 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据。
appendfsync everysec
#指定aof文件的名称,默认为appendonly.aof
appendfilename appendonly.aof

#设置RDB和AOF文件目录
dir /data

docker-compose.yml

# cd redis/ 
docker compose up .
version: '3.8'

networks:
  rd_net:
    driver: bridge
    name: rd_net
    ipam:
      config:
        - subnet: 172.18.0.0/16
          gateway: 172.18.0.1

services:
   # 单机  并且 用于创建集群
  redis_6379:
    image: redis:latest
    hostname: redis_6379
    container_name: redis_6379
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-single/redis-6379/data:/data
      - ./docker/redis.conf:/etc/redis/redis.conf
    ports:
      - 6379:6379
    depends_on:
      - redis_6380
      - redis_6381
      - redis_6382
      - redis_6383
      - redis_6384
      - redis_6385
    networks:
      rd_net:
        ipv4_address:  172.18.0.8
    command:
      - sh
      - -c
      - |
        echo yes | redis-cli --cluster create 172.18.0.2:6380 172.18.0.3:6381 172.18.0.4:6382 172.18.0.5:6383 172.18.0.6:6384 172.18.0.7:6385 --cluster-replicas 1
        redis-server /etc/redis/redis.conf
 
  # 集群
  redis_6380:
    image: redis:latest
    hostname: redis_6380
    container_name: redis_6380
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6380/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6380:6380
    networks:
      rd_net:
        ipv4_address:  172.18.0.2
    command: redis-server /etc/redis/redis.conf --port 6380

  redis_6381:
    image: redis:latest
    hostname: redis_6381
    container_name: redis_6381
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6381/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6381:6381
    networks:
      rd_net:
        ipv4_address:  172.18.0.3
    command: redis-server /etc/redis/redis.conf --port 6381

  redis_6382:
    image: redis:latest
    hostname: redis_6382
    container_name: redis_6382
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6382/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6382:6382
    networks:
      rd_net:
        ipv4_address:  172.18.0.4
    command: redis-server /etc/redis/redis.conf --port 6382

  redis_6383:
    image: redis:latest
    hostname: redis_6383
    container_name: redis_6383
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6383/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6383:6383
    networks:
      rd_net:
        ipv4_address:  172.18.0.5
    command: redis-server /etc/redis/redis.conf --port 6383

  redis_6384:
    image: redis:latest
    hostname: redis_6384
    container_name: redis_6384
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6384/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6384:6384
    networks:
      rd_net:
        ipv4_address:  172.18.0.6
    command: redis-server /etc/redis/redis.conf --port 6384

  redis_6385:
    image: redis:latest
    hostname: redis_6385
    container_name: redis_6385
    restart: unless-stopped
    environment:
      TZ: Asia/Shanghai
      LANG: en_US.UTF-8
      REDIS_PASSWORD: 123456
    volumes:
      - ./redis-cluster/redis-6385/data:/data
      - ./docker/cluster_redis.conf:/etc/redis/redis.conf
    ports:
      - 6385:6385
    networks:
      rd_net:
        ipv4_address:  172.18.0.7
    command: redis-server /etc/redis/redis.conf --port 6385

#  mac-docker-connector 服务端
#  mac-connector:
#    image: wenjunxiao/mac-docker-connector
#    container_name: mac-connector
#    restart: always
#    network_mode: host
#    cap_add:
#      - NET_ADMIN

golang测试代码

go-redis

package main

import (
	"context"
	"github.com/davecgh/go-spew/spew"
	"github.com/go-redis/redis/v8"
)

type Model struct {
	Str1    string   `redis:"str1"`
	Str2    string   `redis:"str2"`
	Int     int      `redis:"int"`
	Bool    bool     `redis:"bool"`
	Ignored struct{} `redis:"-"`
}

func main() {
	ctx := context.Background()

    // 单例连接
	//rdb := redis.NewClient(&redis.Options{
	//	Addr: ":6379",
	//})
    
    // 集群链接
	rdb := redis.NewClusterClient(&redis.ClusterOptions{
		Addrs: []string{"127.0.0.1:6380", "127.0.0.1:6381", "127.0.0.1:6382", "127.0.0.1:6383", "127.0.0.1:6384", "127.0.0.1:6385"},
	})

	// Set some fields.
	if _, err := rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error {
		rdb.HSet(ctx, "key", "str1", "hello")
		rdb.HSet(ctx, "key", "str2", "world")
		rdb.HSet(ctx, "key", "int", 123)
		rdb.HSet(ctx, "key", "bool", 1)
		return nil
	}); err != nil {
		panic(err)
	}

	var model1, model2 Model

	// Scan all fields into the model.
	if err := rdb.HGetAll(ctx, "key").Scan(&model1); err != nil {
		panic(err)
	}

	// Or scan a subset of the fields.
	if err := rdb.HMGet(ctx, "key", "str1", "int").Scan(&model2); err != nil {
		panic(err)
	}

	spew.Dump(model1)
	spew.Dump(model2)
}


/*
GOROOT=/Users/zyj/go/go1.16 #gosetup
GOPATH=/Users/zyj/go #gosetup
/Users/zyj/go/go1.16/bin/go build -o /private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___1go_build_lib_demo_redis_goredis lib_demo/redis/goredis #gosetup
/private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___1go_build_lib_demo_redis_goredis #gosetup
(main.Model) {
 Str1: (string) (len=5) "hello",
 Str2: (string) (len=5) "world",
 Int: (int) 123,
 Bool: (bool) true,
 Ignored: (struct {}) {
 }
}
(main.Model) {
 Str1: (string) (len=5) "hello",
 Str2: (string) "",
 Int: (int) 123,
 Bool: (bool) false,
 Ignored: (struct {}) {
 }
}

Process finished with exit code 0

*/

redisgo(不支持集群)

package main

import (
	"github.com/davecgh/go-spew/spew"
	"github.com/gomodule/redigo/redis"
	"log"
	"time"
)

type Model struct {
	Str1    string   `redis:"str1"`
	Str2    string   `redis:"str2"`
	Int     int      `redis:"int"`
	Bool    bool     `redis:"bool"`
	Ignored struct{} `redis:"-"`
}

func main() {
	db := redis.DialDatabase(0) //库
	//passwd := redis.DialPassword("123456")             //密码
	timeout := redis.DialConnectTimeout(5 * time.Second)    //连接超时时间
	readTimeout := redis.DialReadTimeout(5 * time.Second)   //读超时时间
	writeTimeout := redis.DialWriteTimeout(5 * time.Second) //写超时时间

	//c1, err := redis.Dial("tcp", "127.0.0.1:6379", db, passwd, timeout, readTimeout, writeTimeout) 无密码
	c1, err := redis.Dial("tcp", "127.0.0.1:6379", db,timeout, readTimeout, writeTimeout)
	if err != nil {
		log.Fatalln(err)
	}
	defer c1.Close()


	// Set some fields.
	c1.Do("HSET","key", "str1", "hello")
	c1.Do("HSET","key", "str2", "world")
	c1.Do("HSET","key", "int", 123)
	c1.Do("HSET","key", "bool", 1)


	var model1, model2 Model

	// Scan all fields into the model.
	if do, err := c1.Do("HGETALL", "key");err==nil{
		redis.ScanStruct(do.([]interface{}),&model1)
	}


	keys:=[]interface{}{[]byte("str1"),[]byte("int")}
	// Or scan a subset of the fields.
	if do, err := c1.Do("HMGET", "key","str1", "int");err==nil{
		r:=make([]interface{},0)
		for i, data := range do.([]interface{}) {
			r = append(r,keys[i])
			r = append(r, data)
		}
		redis.ScanStruct(r, &model2)
	}

	spew.Dump(model1)
	spew.Dump(model2)

}
/*
GOROOT=/Users/zyj/go/go1.16 #gosetup
GOPATH=/Users/zyj/go #gosetup
/Users/zyj/go/go1.16/bin/go build -o /private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___go_build_lib_demo_redis_redisgo lib_demo/redis/redisgo #gosetup
/private/var/folders/18/c2k36qyx3hl6d3dg8ccs876r0000gn/T/___go_build_lib_demo_redis_redisgo #gosetup
(main.Model) {
 Str1: (string) (len=5) "hello",
 Str2: (string) (len=5) "world",
 Int: (int) 123,
 Bool: (bool) true,
 Ignored: (struct {}) {
 }
}
(main.Model) {
 Str1: (string) (len=5) "hello",
 Str2: (string) "",
 Int: (int) 123,
 Bool: (bool) false,
 Ignored: (struct {}) {
 }
}

Process finished with exit code 0
*/

你可能感兴趣的:(docker,#,docker,compose,#,redis,redis,docker)