近期改造一个基于 django 3.2 的后端服务,把单点 redis 换为 redis sentinel 集群时发生了一些问题,记录下来:
最终的 settings.py 内关于 redis 的配置如下
# 单点服务配置
REDIS = {
'HOST': 'redis-node-0.redis-headless.infra', # k8s 内的按索引找到 redis 节点 0
'PORT': 6389,
'PASSWORD': '',
'DB': '0', # redis 一个实例有 16 个数据库,分别为:0 ~ 15,默认为 0
'SENTINEL': False, # 是否使用 sentinel 集群
'SENTINEL_SERVICE_NAME': 'mymaster', # 集群下的服务名
}
# sentinel 集群服务配置
REDIS = {
'HOST': 'redis.infra',
'PORT': 26389, # sentinel 集群一般使用 26379 端口
'PASSWORD': 'xxxx',
'DB': '1', # redis 一个实例有 16 个数据库,分别为:0 ~ 15,默认为 0
'SENTINEL': True, # 是否使用 sentinel 集群
'SENTINEL_SERVICE_NAME': 'mymaster', # 集群下的服务名
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{REDIS['HOST']}:{REDIS['PORT']}/{REDIS['DB']}",
"KEY_PREFIX": '',
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 20},
"PASSWORD": REDIS['PASSWORD'],
},
}
}
if REDIS['SENTINEL']:
# @see https://github.com/jazzband/django-redis#use-the-sentinel-connection-factory
DJANGO_REDIS_CONNECTION_FACTORY = 'django_redis.pool.SentinelConnectionFactory'
CACHES['default']['LOCATION'] = f"redis://{REDIS['SENTINEL_SERVICE_NAME']}/{REDIS['DB']}",
CACHES['default']['OPTIONS'].update(
CLIENT_CLASS='django_redis.client.SentinelClient',
SENTINELS=[(REDIS['HOST'], int(REDIS['PORT']))],
SENTINEL_KWARGS=dict(password=REDIS['PASSWORD']),
)
CACHE_SECONDS = 60 * 15
开发环境(requirements.txt)
django==3.2.8
django-redis==5.2.0
...
注意事项
试验密码支持时,找了不少资料,下面附带上如何测试 redis sentinel 集群的简化脚本:
# contest-backend-7d9b77b879-f56vz 是 django 后端服务的 pod 名字
kubectl -n weather-efusion exec -it contest-backend-7d9b77b879-f56vz -- python3
from redis.sentinel import Sentinel
sentinel = Sentinel([('redis.infra', 26379)], sentinel_kwargs=dict(password='xxxx'), socket_timeout=0.2)
print(sentinel.discover_master('mymaster'))
print(sentinel.discover_slaves('mymaster'))
master = sentinel.master_for('mymaster', socket_timeout=0.2, password='xxxx')
master.set('foo', 'bar')
slave = sentinel.slave_for('mymaster', socket_timeout=0.2, password='xxxx')
slave.get('foo')
master.delete('foo')