K8S集群中安装postgresql和redis
环境准备
| K8S版本 | v1.33.10 |
本例子中使用CloudNativePG (CNPG)安装
1、添加仓库并更新缓存
# 添加仓库
helm repo add cnpg https://cloudnative-pg.github.io/charts
# 更新本地仓库缓存,确保能搜到最新的 Chart
helm repo update
2、安装cloudnative,命令如下:
helm install cnpg-operator cnpg/cloudnative-pg --namespace cnpg-system --create-namespace
注:如果提示如下内容,说明从gitlab下载包失败:
Error: INSTALLATION FAILED: Get "https://github.com/cloudnative-pg/charts/releases/download/cloudnative-pg-v0.28.2/cloudnative-pg-0.28.2.tgz": unexpected EOF
可将包下载回来后再次执行:
helm install cnpg-operator ./cloudnative-pg-0.28.2.tgz --namespace cnpg-system --create-namespace

3、安装postgresql
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgres-ha
namespace: gitlab-db
spec:
instances: 2 # 1主 + 1备
imageName: ghcr.io/cloudnative-pg/postgresql:16.14 # PG16
# 存储(务必替换为你的 StorageClass)
storage:
size: 10Gi
storageClass: longhorn-new
walStorage:
size: 10Gi
storageClass: longhorn-new
# PostgreSQL 基础参数
postgresql:
parameters:
max_connections: "500"
shared_buffers: "1GB"
effective_cache_size: "3GB"
work_mem: "16MB"
maintenance_work_mem: "256MB"
synchronous:
method: any
number: 1 # 至少1个同步备库
# 自动备份(可选,需要对象存储)
# backup:
# barmanObjectStore:
# destinationPath: s3://pg-backups/
# s3Credentials:
# accessKeyId:
# secretKeyRef:
# name: s3-creds
# key: accesskey
# secretAccessKey:
# secretKeyRef:
# name: s3-creds
# key: secretkey
注:会涉及到一些镜像需要提前下载回来

查看service,应用使用postgres-ha-rw连接,如下:

应用在连接的时候完整地址为:
#<service-name>.<namespace>.svc.cluster.local
postgres-ha-rw.gitlab-db.svc.cluster.local:6379
安装Redis集群
1、创建secret,用于存储redis密码,如下:
apiVersion: v1
kind: Secret
metadata:
name: redis-cluster-secret
namespace: redis-cluster
type: Opaque
stringData:
redis-password: "Abcd@1234"
- Opaque:通用型secret(密码、字符串、文件)
- stringData:把明文自动转换为base64
2、定义configmap,内部主要包含redis的配置文件,如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cluster-config
namespace: redis-cluster
data:
redis.conf: |
bind 0.0.0.0
port 6379
# ===== 集群 =====
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
# ===== 持久化 =====
appendonly yes
appendfsync everysec #每妙刷数据到磁盘
# ===== 内存 =====
maxmemory 2gb
maxmemory-policy allkeys-lru #内存满,所有key淘汰
#=== ACL(Redis 7 必须)===
user default on >${REDIS_PASSWORD} ~* +@all #可访问所有key,执行所有命令
# ===== 密码(重点) =====
requirepass ${REDIS_PASSWORD}
masterauth ${REDIS_PASSWORD} #主从同步密码
# ===== 安全 =====
protected-mode no
rename-command FLUSHALL ""
rename-command FLUSHDB ""
3、创建headless service,如下:
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
namespace: redis-cluster
spec:
clusterIP: None
ports:
- port: 6379
name: client
selector:
app: redis-cluster
- clusterIP: Node 这是一个Headless Service
为什么必须用Headless service?
如果使用普通的Service,那么DNS名就是:
<service-name>.<namespace>.svc.cluster.local #一个service会匹配后面多个pod
redis集群6个节点,每个节点的DNS名称都一样,那么创建集群的命令就是:
redis-cli --cluster create \
redis-cluster.default.svc.cluster.local:6379 \
redis-cluster.default.svc.cluster.local:6379 \
redis-cluster.default.svc.cluster.local:6379 #会失败
如果使用Headless service ,那么DNS名就是:
<pod-name>.<service-name>.<namespace>.svc.cluster.local #一个POD对应一个DNS名
此时集群6个节点,每个节点DNS名称不一样,创建命令就是:
redis-cli --cluster create \
redis-cluster-0.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-1.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-2.redis-cluster.redis-cluster.svc.cluster.local:6379
4、部署redis集群,通过statefulset,如下:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: redis-cluster
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["redis-cluster"]
topologyKey: kubernetes.io/hostname
containers:
- name: redis
image: redis:7.2
command:
- sh
- -c
- |
cp /etc/redis/config/redis.conf /etc/redis/redis.conf
sed -i "s/\${REDIS_PASSWORD}/$REDIS_PASSWORD/g" /etc/redis/redis.conf
redis-server /etc/redis/redis.conf
ports:
- containerPort: 6379
name: client
resources:
requests:
cpu: "500m"
memory: "2Gi"
limits:
cpu: "1"
memory: "4Gi"
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-cluster-secret
key: redis-password
volumeMounts:
- name: redis-conf
mountPath: /etc/redis/config
- name: redis-data
mountPath: /data
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- redis-cli
- -a
- "$(REDIS_PASSWORD)"
- ping
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: redis-conf
configMap:
name: redis-cluster-config
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: longhorn-new # ⚠️ 改成你自己的 StorageClass
resources:
requests:
storage: 10Gi
- volumeClaimTemplates:statefulset专属配置,其余的控制器没有,为每个 Pod 自动创建独立的 PVC
- volumes: 把configmap挂载到/etc/redis/config目录下,上面command中先通过cp 把/etc/redis/config下的redis 复制到/etc/redis目录下,然后使用sed去修改/etc/redis/redis.conf,为什么要先cp过去? 因为configmap和secret的挂载目录默认都是只读的,因此不能直接修改/etc/redis/config目录下的,必须cp到/etc/redis/目录下,然后修改就可以啦
5、创建集群,先进入到容器中,如下:
(1)、kubectl exec -it redis-cluster-0 -n redis-cluster -- sh
(2)、redis-cli -a "$REDIS_PASSWORD" \
--cluster create \
redis-cluster-0.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-1.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-2.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-3.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-4.redis-cluster.redis-cluster.svc.cluster.local:6379 \
redis-cluster-5.redis-cluster.redis-cluster.svc.cluster.local:6379 \
--cluster-replicas 1
集群初始化成功如图:

查看集群信息,如下:

- cluster_state ==> 最核心的指标。只要显示
ok,就代表整个集群没有任何故障,处于可用状态。如果是fail,则说明有主节点宕机或者槽位未分配 - cluster_slots_assigned:16384与 cluster_slots_ok:16384 ==> 16384 是 Redis Cluster 的总槽位数,这两个数字一致,意味着所有槽位都已分配给主节点,并且处于健康状态。没有出现
(error)或未分配的槽位,说明数据可以正常路由 - cluster_known_nodes:6 ==> 3个主节点 + 3个从节点 = 6个节点。这个数字说明集群内所有节点都已经互相发现并建立了连接
- cluster_size:3 ==> 集群中有 3 个主节点在工作,符合你预期的集群规模


