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 个主节点在工作,符合你预期的集群规模

标签