Loki 日志系统分布式部署实践三 redis

说明

这里支持 redis 主从、哨兵、集群三种模式,我这里选择主从即可,集群模式测试异常,没能解决

安装

# helm repo add bitnami https://charts.bitnami.com/bitnami

# helm search repo redis
NAME                    CHART VERSION   APP VERSION     DESCRIPTION                                       
bitnami/redis           12.1.1          6.0.9           Open source, advanced key-value store. It is of...
bitnami/redis-cluster   4.0.2           6.0.9           Open source, advanced key-value store. It is of...

安装 redis 主从模式:

# helm pull bitnami/redis --version=12.1.1
# helm show values redis-12.1.1.tgz

编写配置文件:

# cat > redis-config.yaml <<EOF
global:
  redis: {}

image:
  registry: ops-harbor.hupu.io/k8s
  repository: redis
  tag: 6.0.9
  pullPolicy: IfNotPresent

# 1. 默认 Master-Slave
# 2. Master-Slave with Sentinel 需要设置 cluster.enabled=true 和 sentinel.enabled=true,出来的 pod 名称不再是 Master-Slave 区分开,都是 node 这样的
cluster:
  enabled: true
  slaveCount: 1

sentinel:
  enabled: false
  usePassword: true
  image:
    registry: ops-harbor.hupu.io/k8s
    repository: redis-sentinel
    tag: 6.0.9
    pullPolicy: IfNotPresent
  masterSet: mymaster
  initialCheckTimeout: 5
  quorum: 2
  downAfterMilliseconds: 60000
  failoverTimeout: 18000
  parallelSyncs: 1
  port: 26379
  configmap:
  staticID: false
  livenessProbe:
    enabled: true
    initialDelaySeconds: 5
    periodSeconds: 5
    timeoutSeconds: 5
    successThreshold: 1
    failureThreshold: 5
  readinessProbe:
    enabled: true
    initialDelaySeconds: 5
    periodSeconds: 5
    timeoutSeconds: 1
    successThreshold: 1
    failureThreshold: 5
  customLivenessProbe: {}
  customReadinessProbe: {}
  service:
    type: ClusterIP
    sentinelPort: 26379
    redisPort: 6379

    annotations: {}
    labels: {}
    loadBalancerIP:

clusterDomain: cluster.local

networkPolicy:
  enabled: false
  ingressNSMatchLabels: {}
  ingressNSPodMatchLabels: {}

serviceAccount:
  create: false
  name:

rbac:
  create: false

  role:
    rules: []

securityContext:
  enabled: true
  fsGroup: 1001
  #runAsNonRoot: false
  #runAsUser: 0

containerSecurityContext:
  enabled: true
  runAsUser: 1001
  #runAsNonRoot: false
  #runAsUser: 0
  #privileged: true
  #capabilities:
  #  add:
  #  - SYS_ADMIN
  #  drop:
  #  - ALL

usePassword: true
password: "kong62123"

usePasswordFile: false

persistence:
  existingClaim:

redisPort: 6379

tls:
  enabled: false
  authClients: true
  certificatesSecret:
  certFilename:
  certKeyFilename:
  certCAFilename:

master:
  command: "/run.sh"
  preExecCmds: ""
  configmap:
  extraFlags: []
  disableCommands:
    - FLUSHDB
    - FLUSHALL

  podLabels: {}
  podAnnotations: {}
  shareProcessNamespace: false
  livenessProbe:
    enabled: true
    initialDelaySeconds: 5
    periodSeconds: 5
    timeoutSeconds: 5
    successThreshold: 1
    failureThreshold: 5
  readinessProbe:
    enabled: true
    initialDelaySeconds: 5
    periodSeconds: 5
    timeoutSeconds: 1
    successThreshold: 1
    failureThreshold: 5

  customLivenessProbe: {}
  customReadinessProbe: {}

  affinity: 
    # Pod 反亲和
    podAntiAffinity:
      # Pod 硬反亲和
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - redis
        topologyKey: "kubernetes.io/hostname"
      # Pod 软反亲和
      #preferredDuringSchedulingIgnoredDuringExecution:
      #- podAffinityTerm:
      #    labelSelector:
      #      matchExpressions:
      #      - key: app
      #        operator: In
      #        values:
      #        - loki
      #    topologyKey: kubernetes.io/hostname
      #  weight: 100

  service:
    type: ClusterIP
    port: 6379


    annotations: {}
    labels: {}
    loadBalancerIP:

  persistence:
    enabled: true
    path: /data
    subPath: ""
    accessModes:
      - ReadWriteOnce
    size: 100Gi
    storageClass: "alicloud-disk-efficiency-cn-hangzhou-g"
    matchLabels: {}
    matchExpressions: {}

  statefulset:
    labels: {}
    updateStrategy: RollingUpdate

  priorityClassName: ''

  extraEnvVars: []

  extraEnvVarsCM: []

  extraEnvVarsSecret: []

slave:
  service:
    type: ClusterIP
    port: 6379

    annotations: {}
    labels: {}
    loadBalancerIP:

  port: 6379
  command: "/run.sh"
  preExecCmds: ""
  configmap:
  extraFlags: []
  disableCommands:
    - FLUSHDB
    - FLUSHALL

  affinity:
    # Pod 反亲和
    podAntiAffinity:
      # Pod 硬反亲和
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - redis
        topologyKey: "kubernetes.io/hostname"
      # Pod 软反亲和
      #preferredDuringSchedulingIgnoredDuringExecution:
      #- podAffinityTerm:
      #    labelSelector:
      #      matchExpressions:
      #      - key: app
      #        operator: In
      #        values:
      #        - loki
      #    topologyKey: kubernetes.io/hostname
      #  weight: 100

  spreadConstraints: {}

  shareProcessNamespace: false
  livenessProbe:
    enabled: true
    initialDelaySeconds: 30
    periodSeconds: 10
    timeoutSeconds: 5
    successThreshold: 1
    failureThreshold: 5
  readinessProbe:
    enabled: true
    initialDelaySeconds: 5
    periodSeconds: 10
    timeoutSeconds: 10
    successThreshold: 1
    failureThreshold: 5
  customLivenessProbe: {}
  customReadinessProbe: {}
  podLabels: {}
  podAnnotations: {}
  persistence:
    enabled: true
    path: /data
    subPath: ""
    accessModes:
      - ReadWriteOnce
    size: 100Gi
    storageClass: "alicloud-disk-efficiency-cn-hangzhou-g"
    matchLabels: {}
    matchExpressions: {}

  statefulset:
    labels: {}
    updateStrategy: RollingUpdate

  extraEnvVars: []

  extraEnvVarsCM: []

  extraEnvVarsSecret: []

metrics:
  enabled: true
  image:
    registry: ops-harbor.hupu.io/k8s
    repository: redis-exporter
    tag: 1.13.1
    pullPolicy: IfNotPresent
  podAnnotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9121"

  serviceMonitor:
    enabled: true
    selector:
      prometheus: kube-prometheus

  prometheusRule:
    enabled: false
    additionalLabels: {}
    namespace: ""
    rules: []

  service:
    type: ClusterIP
    annotations: {}
    labels: {}

volumePermissions:
  enabled: false
  registry: ops-harbor.hupu.io/base
  repository: alpine
  tag: v3.10
  pullPolicy: IfNotPresent
  resources: {}
  securityContext:
    runAsUser: 0

configmap: |-
  # 关闭 aof,文件太大,IO 太高了
  appendonly no
  # 代表当前 AOF 文件空间和上次重写后 AOF 空间的比值
  auto-aof-rewrite-percentage 100
  # AOF 超过 64m 就开始收缩
  auto-aof-rewrite-min-size 8mb

  # RDB 磁盘消耗也不小啊,要彻底 RDB 功能需要注释默认的 3 行任务并添加 save ""
  # Save the DB on disk: 保存数据库到磁盘  
  #   save <秒> <更新>  
  #  
  #   如果指定的秒数和数据库写操作次数都满足了就将数据库保存。  
  #   900 秒(15 分钟)内至少 1 个 key 值改变(则进行数据库保存--持久化)
  #save 900 1
  #   300 秒(5 分钟)内至少 10 个 key 值改变(则进行数据库保存--持久化)
  #save 300 10
  #   60 秒(1 分钟)内至少 10000 个 key 值改变(则进行数据库保存--持久化)
  #save 60 10000
  save ""
  
  # 默认情况下,如果 RDB 快照持久化操作被激活并且持久化操作失败,Redis 则会停止接受更新操作。这样会让用户了解到数据没有被正确的存储到磁盘上
  # 如果后台存储(持久化)操作进程再次工作,Redis 会自动允许更新操作。  
  stop-writes-on-bgsave-error yes  
    
  # 是否在导出 .rdb 数据库文件的时候采用 LZF 压缩字符串和对象?压缩会产生较高的 CPU
  rdbcompression yes
    
  # 从版本 RDB版本 5 开始,一个 CRC64 的校验就被放在了文件末尾。 这会让格式更加耐攻击,但是当存储或者加载 rbd 文件的时候会有一个 10% 左右的性能下降
  # 没有校验的 RDB 文件会有一个 0 校验位,来告诉加载代码跳过校验检查 
  rdbchecksum yes
  
  dbfilename dump.rdb

sysctlImage:
  enabled: true
  command: 
  - /bin/sh
  - -c
  - |
    # 正常运行的 docker 容器中,是不能修改任何 sysctl 内核参数的。因为 /proc/sys 是以只读方式挂载到容器里面的。可以使用 --privileged 参数解决,也可以挂为 rw 解决
    mount -o remount rw /proc/sys
    sysctl -w net.core.somaxconn=65535
    #sysctl -w net.ipv4.tcp_max_syn_backlog=262144
    sysctl -w net.ipv4.ip_local_port_range="1024 65535"
    sysctl -w fs.file-max=26234859
    sysctl -w fs.inotify.max_user_instances=26234859
    sysctl -w fs.inotify.max_user_watches=26234859
    sysctl -w fs.inotify.max_queued_events=26234859
    sysctl -w vm.max_map_count=262144
    echo never > /host-sys/kernel/mm/transparent_hugepage/enabled
    echo never > /host-sys/kernel/mm/transparent_hugepage/defrag
  registry: ops-harbor.hupu.io/base
  repository: alpine
  #repository: minideb
  tag: v3.10
  #tag: buster
  pullPolicy: Always
  mountHostSys: true
  resources: {}

podSecurityPolicy:
  create: false

podDisruptionBudget:
  enabled: false
  minAvailable: 1
EOF

安装:

# helm upgrade --install redis -f redis-config.yaml redis-12.1.1.tgz -n grafana

# kubectl get pod -n grafana |grep redis 
redis-master-0                        2/2     Running   0          73s
redis-slave-0                         2/2     Running   0          73s

# kubectl get svc -n grafana |grep redis
redis-headless                           ClusterIP   None            <none>        6379/TCP         77s
redis-master                             ClusterIP   172.21.0.153    <none>        6379/TCP         77s
redis-metrics                            ClusterIP   172.21.1.60     <none>        9121/TCP         77s
redis-slave                              ClusterIP   172.21.7.185    <none>        6379/TCP         77s

# kubectl get sts -n grafana |grep redis   
redis-master                        1/1     81s
redis-slave                         2/2     81s

查看密码:

# kubectl get secret --namespace grafana redis -o jsonpath="{.data.redis-password}" | base64 --decode
kong62123

连接测试

连接 master:

# kubectl run -n grafana redis-redis-cluster-client --rm \
                                                    --tty -i \
                                                    --restart='Never' \
                                                    --env REDIS_PASSWORD=kong62123 \
                                                    --image ops-harbor.hupu.io/k8s/redis-cluster:6.0.9 \
                                                    -- redis-cli -c -h redis-master -a kong62123 info

连接 slave:

# kubectl run -n grafana redis-redis-cluster-client --rm \
                                                    --tty -i \
                                                    --restart='Never' \
                                                    --env REDIS_PASSWORD=kong62123 \
                                                    --image ops-harbor.hupu.io/k8s/redis-cluster:6.0.9 \
                                                    -- redis-cli -c -h redis-slave -a kong62123 info

读写分离:
读写:

# redis-master.grafana.svc.cluster.local

只读:

redis-slave.grafana.svc.cluster.local

错误处理

错误 1:

# kubectl logs -f -n grafana redis-master-0 -c redis
1:M 30 Nov 2020 15:08:29.257 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.

解决:
参考:https://github.com/helm/charts/issues/10666
参考:https://docs.bitnami.com/kubernetes/infrastructure/redis/administration/kernel-settings/
参考:https://github.com/helm/charts/tree/master/stable/redis/#user-content-host-kernel-settings

sysctlImage:
  enabled: true
  command: 
  - /bin/sh
  - -c
  - |
    # 正常运行的 docker 容器中,是不能修改任何 sysctl 内核参数的。因为 /proc/sys 是以只读方式挂载到容器里面的。可以使用 --privileged 参数解决,也可以挂为 rw 解决
    mount -o remount rw /proc/sys
    sysctl -w net.core.somaxconn=65535
    #sysctl -w net.ipv4.tcp_max_syn_backlog=262144
    sysctl -w net.ipv4.ip_local_port_range="1024 65535"
    sysctl -w fs.file-max=26234859
    sysctl -w fs.inotify.max_user_instances=26234859
    sysctl -w fs.inotify.max_user_watches=26234859
    sysctl -w fs.inotify.max_queued_events=26234859
    sysctl -w vm.max_map_count=262144

注意:Kubernetes 1.12+ 可以使用 securityContext.sysctls 来设置 pod 的 sysctl,而不需要 initContainer 了:

securityContext:
  sysctls:
  - name: net.core.somaxconn
    value: "65535"

错误 2:

# kubectl logs -f -n grafana redis-master-0 -c init-sysctl 
mount: /proc/sys: mount point not mounted or bad option.

解决:
使用 initContainer 去修改 sysctl 的方案在生产环境正常,这里却报错了,应该是 securityContext 或 PSP 的问题,这里没有启用 PSP,那就剩下 securityContext 了:
我这里尝试写入如下权限,最终都被修改掉了:

containerSecurityContext:
  enabled: true
  runAsUser: 1001
  runAsNonRoot: false
  runAsUser: 0
  privileged: true
  capabilities:
    add:
    - SYS_ADMIN
    drop:
    - ALL

# kubectl get sts -n grafana redis-master -o yaml 
...
        securityContext:
          privileged: true
          runAsUser: 0
...
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 1001

这里暂时没有找到解决方案,但是发现虽然报错了,下面 sysctl 修改是生效了的,因为日志不再报错了

错误 3:

# kubectl logs -f -n grafana redis-master-0 -c init-sysctl 
/bin/sh: 3: sysctl: not found
/bin/sh: 5: sysctl: not found
/bin/sh: 6: sysctl: not found
/bin/sh: 7: sysctl: not found
/bin/sh: 8: sysctl: not found
/bin/sh: 9: sysctl: not found
/bin/sh: 10: sysctl: not found

解决:
因为 bitnami/minideb:buster 镜像里没有 sysctl

# docker run -it --rm bitnami/minideb:buster sysctl   
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"sysctl\": executable file not found in $PATH": unknown.

方法一:替换镜像

sysctlImage:
  registry: ops-harbor.hupu.io/base
  repository: alpine
  tag: v3.10

方法二:直接安装包,但是这个比较慢

sysctlImage:
  enabled: true
  mountHostSys: true
  command:
    - /bin/sh
    - -c
    - |-
      install_packages procps

错误 4:

# kubectl logs -f -n grafana redis-master-0 -c redis
1:M 30 Nov 2020 15:08:29.257 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').

解决:
参考:https://github.com/docker-library/redis/issues/55
参考:https://github.com/prometheus/node_exporter/issues/703
注意:它将修改调度了容器的节点的内核设置,从而影响该节点上运行的其他容器。这就是为什么您需要运行特权的 initContainer 或设置 securityContext.sysctls 的原因。

sysctlImage:
  enabled: true
  command: 
  - /bin/sh
  - -c
  - |
    echo never > /host-sys/kernel/mm/transparent_hugepage/enabled
    echo never > /host-sys/kernel/mm/transparent_hugepage/defrag
  mountHostSys: true

错误 5:

# kubectl logs -f -n grafana redis-master-0 -c init-sysctl 
/bin/sh: can't create /sys/kernel/mm/transparent_hugepage/enabled: Read-only file system
/bin/sh: can't create /sys/kernel/mm/transparent_hugepage/defrag: Read-only file system

解决:
因为将宿主机的 /sys 挂载到容器内的路径变成了 /host-sys

# kubectl describe pod -n grafana redis-master-0
    Mounts:
      /host-sys from host-sys (rw)
...
Volumes:
  host-sys:
    Type:          HostPath (bare host directory volume)
    Path:          /sys
    HostPathType:  

所以要修改路径:

sysctlImage:
  enabled: true
  command: 
  - /bin/sh
  - -c
  - |
    #echo never > /sys/kernel/mm/transparent_hugepage/enabled
    #echo never > /sys/kernel/mm/transparent_hugepage/defrag
    echo never > /host-sys/kernel/mm/transparent_hugepage/enabled
    echo never > /host-sys/kernel/mm/transparent_hugepage/defrag
  mountHostSys: true

错误 6:

# kubectl logs -f -n grafana redis-master-0 -c redis 
useradd: Permission denied.
useradd: cannot lock /etc/passwd; try again later.
chown: invalid user: 'redis'

解决:
这里暂时看不受影响

错误 7:

# kubectl logs -f -n grafana --tail=20 redis-slave-0 -c redis 
1:S 30 Nov 2020 13:51:27.769 * Connecting to MASTER redis-master-0.redis-headless.grafana.svc.cluster.local:6379
1:S 30 Nov 2020 13:51:27.776 # Unable to connect to MASTER: Invalid argument
1:S 30 Nov 2020 13:51:28.778 * Connecting to MASTER redis-master-0.redis-headless.grafana.svc.cluster.local:6379
1:S 30 Nov 2020 13:51:28.785 # Unable to connect to MASTER: Invalid argument
1:signal-handler (1606744289) Received SIGTERM scheduling shutdown...
1:S 30 Nov 2020 13:51:29.086 # User requested shutdown...
1:S 30 Nov 2020 13:51:29.086 * Calling fsync() on the AOF file.
1:S 30 Nov 2020 13:51:29.086 # Redis is now ready to exit, bye bye...

解决:
因为 master 挂掉了

错误 8:

# kubectl logs -f -n grafana redis-master-0 -c redis 
1:M 01 Dec 2020 02:39:33.588 # Connection with replica 10.41.182.170:6379 lost.
1:M 01 Dec 2020 02:39:33.590 # Connection with replica 10.41.176.75:6379 lost.
1:M 01 Dec 2020 02:39:50.092 * Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
1:M 01 Dec 2020 02:40:05.010 * Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
1:M 01 Dec 2020 02:40:20.053 * Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
1:M 01 Dec 2020 02:40:34.111 * Replica 10.41.176.75:6379 asks for synchronization
1:M 01 Dec 2020 02:40:34.111 * Full resync requested by replica 10.41.176.75:6379
1:M 01 Dec 2020 02:40:34.111 * Can't attach the replica to the current BGSAVE. Waiting for next BGSAVE for SYNC
1:M 01 Dec 2020 02:40:35.075 * Replica 10.41.182.170:6379 asks for synchronization
1:M 01 Dec 2020 02:40:35.075 * Full resync requested by replica 10.41.182.170:6379
1:M 01 Dec 2020 02:40:35.075 * Can't attach the replica to the current BGSAVE. Waiting for next BGSAVE for SYNC

解决:
Redis 提供两种相对有效的备份方法:

  1. RDB
    RDB 是在某个时间点将内存中的所有数据的快照保存到磁盘上,在数据恢复时,可以恢复备份时间以前的所有数据,但无法恢复备份时间点后面的数据。
    默认 redis 在磁盘上创建二进制格式的命名为 dump.rdb 的数据快照。可以通过配置文件配置每隔 N 秒且数据集上至少有 M 个变化时创建快照、是否对数据进行压缩、快照名称、存放快照的工作目录。

  2. AOF
    AOF 是以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。
    优点是基本可以实现数据无丢失(缓存的数据有可能丢失),缺点是随着数据量的持续增加,AOF 文件也会越来越大。
    在保证数据安全的情况下,尽量避免因备份数据消耗过多的 Redis 资源,采用如下备份策略:
    Master 实例: 不采用任何备份机制。
    Slave 实例: 采用 AOF(严格数据要求时可同时开启 RDB),每天将 AOF 文件备份至备份服务器。
    为了最大限度减少主实例的资源干扰,将备份相关全部迁移至 Slave 端完成。同时这样也有缺点,当主实例挂掉后,应用服务切换至 Slave 端,此时的 Slave 端的负载将会很大。

利用 RDB 快照的持久化方式不是非常可靠,当运行 Redis 的计算机停止工作、意外掉电、意外杀掉了 Redis 进程那么最近写入 Redis 的数据将会丢。对于某些应用这或许不成问题,但对于持久化要求非常高的应用场景快照方式不是理想的选择。
利用 AOF 文件是一个替代方案,用以最大限度的持久化数据。同样,可以通过配置文件来开闭 AOF。

打开 AOF 持久化功能后,Redis 处理完每个事件后会调用 write(2) 将变化写入 kernel 的 buffer,如果此时 write(2) 被阻塞,Redis 就不能处理下一个事件。
Linux 规定执行 write(2) 时,如果对同一个文件正在执行 fdatasync(2) 将 kernel buffer 写入物理磁盘,或者有 system wide sync 在执行,write(2) 会被 Block 住,整个 Redis 被 Block 住。
如果系统 IO 繁忙,比如有别的应用在写盘,或者 Redis 自己在 AOF rewrite 或 RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致 fdatasync(2) 迟迟未能完成从而 Block 住 write(2),Block 住整个 Redis。
为了更清晰的看到 fdatasync(2) 的执行时长,可以使用下面命令跟踪,但会影响系统性能:

strace -p (pid of redis server) -T -e -f trace=fdatasync

Redis 提供了一个自救的方式,当发现文件有在执行 fdatasync(2) 时,就先不调用 write(2),只存在 cache 里,免得被 Block。但如果已经超过两秒都还是这个样子,则会硬着头皮执行 write(2),即使 redis 会被 Block 住。
此时那句要命的 log 会打印:Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
之后用 redis-cli INFO 可以看到 aof_delayed_fsync 的值被加 1。

因此,对于 fsync 设为 everysec 时丢失数据的可能性的最严谨说法是:
如果有 fdatasync 在长时间的执行,此时 redis 意外关闭会造成文件里不多于两秒的数据丢失。
如果 fdatasync 运行正常,redis 意外关闭没有影响,只有当操作系统 crash 时才会造成少于 1 秒的数据丢失。

方法一:关闭 AOF
如果采用 redis 主从 + sentinel 方式的话,主节点挂了从节点会自己提升为主点,主节点恢复后全量同步一次数据就可以了,关系也不是太大

方法二:修改系统配置
原来是 AOF rewrite 时一直埋头的调用 write(2),由系统自己去触发 sync。默认配置 vm.dirty_background_ratio=10,也就是占用了 10% 的可用内存才会开始后台 flush
而我的服务器有 8G 内存,很明显一次 flush 太多数据会造成阻塞,所以最后果断设置了sysctl vm.dirty_bytes=33554432(32M) 问题解决

# sysctl -a | grep dirty_background_ratio
vm.dirty_background_ratio = 10

# sysctl -a | grep vm.dirty_bytes
vm.dirty_bytes = 0

# echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf
# sysctl -p

错误 9:

# kubectl logs -f -n grafana redis-master-0 -c redis --previous
1:M 01 Dec 2020 03:24:37.728 * Reading RDB preamble from AOF file...
1:M 01 Dec 2020 03:24:37.728 * Loading RDB produced by version 6.0.9
1:M 01 Dec 2020 03:24:37.728 * RDB age 3622 seconds
1:M 01 Dec 2020 03:24:37.728 * RDB memory usage when created 1024.94 Mb
1:M 01 Dec 2020 03:24:37.728 * RDB has an AOF tail
1:M 01 Dec 2020 03:24:44.534 * Reading the remaining AOF tail...

解决:
看着是启动的时候加载 AOF 文件到内存,然后被 liveness 杀掉了

# kubectl exec -it -n grafana redis-master-0 -- df -Th|grep /data
/dev/vdd       ext4      99G   80G   20G  81% /data

随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,redis 引入了 AOF 重写机制压缩文件。文件能缩小的原因是:

  1. 旧文件中的无效命令不会保留,如 del,key1,sort
  2. 多条合并成一条,如 lplush list a 和 lplush list b 转换为 lplush a b,也可以合并重复项

AOF 重写可以手动触发和自动触发:

  1. 手动触发可以调用 bgrewriteaof
# redis-cli -p 6379 -h 127.0.0.1
127.0.0.1:6379> BGREWRITEAOF
  1. 根据如下两个参数自动触发

代表当前 AOF 文件空间和上次重写后 AOF 空间的比值。

auto-aof-rewrite-percentage 100

AOF 超过 16m 就开始收缩

auto-aof-rewrite-min-size 16mb

所以这里处理下,控制 AOF 文件大小:

configmap: |-
  appendonly yes
  save ""
  # 代表当前 AOF 文件空间和上次重写后 AOF 空间的比值
  auto-aof-rewrite-percentage 100
  # AOF 超过 16m 就开始收缩
  auto-aof-rewrite-min-size 16mb

注意:这里依旧没能解决问题,文件依旧很大,而且会造成大量的磁盘 IO,最终导致 redis 失去响应

# top
top - 12:05:03 up 19 days, 21:17,  1 user,  load average: 120.02, 85.39, 38.98
Tasks: 340 total,   2 running, 338 sleeping,   0 stopped,   0 zombie
%Cpu(s): 28.8 us,  6.3 sy,  0.0 ni, 59.4 id,  3.2 wa,  0.0 hi,  2.3 si,  0.0 st
KiB Mem : 65806668 total, 17644992 free, 44516804 used,  3644872 buff/cache
KiB Swap:        0 total,        0 free,        0 used. 20453980 avail Mem 

# iostat -x 1
...
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          32.59    0.06    8.53    6.87    0.00   51.94

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
vda               0.00    40.00   34.00   12.00   140.00   240.00    16.52     0.10    2.24    2.76    0.75   1.50   6.90
vdb               0.00     0.00    0.00    4.00     0.00     8.00     4.00     0.00    0.75    0.00    0.75   0.75   0.30
vdc               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
vdd               0.00     0.00  918.00    0.00 117504.00     0.00   256.00     1.69    1.85    1.85    0.00   1.04  95.10
vde               0.00     0.00  918.00    0.00 116992.00     0.00   254.88     1.81    1.97    1.97    0.00   1.05  96.60

彻底解决:

configmap: |-
  # 关闭 aof,文件太大,IO 太高了
  appendonly no
  # 代表当前 AOF 文件空间和上次重写后 AOF 空间的比值
  auto-aof-rewrite-percentage 100
  # AOF 超过 64m 就开始收缩
  auto-aof-rewrite-min-size 8mb

  # RDB 磁盘消耗也不小啊,要彻底 RDB 功能需要注释默认的 3 行任务并添加 save ""
  # Save the DB on disk: 保存数据库到磁盘  
  #   save <秒> <更新>  
  #  
  #   如果指定的秒数和数据库写操作次数都满足了就将数据库保存。  
  #   900 秒(15 分钟)内至少 1 个 key 值改变(则进行数据库保存--持久化)
  #save 900 1
  #   300 秒(5 分钟)内至少 10 个 key 值改变(则进行数据库保存--持久化)
  #save 300 10
  #   60 秒(1 分钟)内至少 10000 个 key 值改变(则进行数据库保存--持久化)
  #save 60 10000
  save ""
  
  # 默认情况下,如果 RDB 快照持久化操作被激活并且持久化操作失败,Redis 则会停止接受更新操作。这样会让用户了解到数据没有被正确的存储到磁盘上
  # 如果后台存储(持久化)操作进程再次工作,Redis 会自动允许更新操作。  
  stop-writes-on-bgsave-error yes  
    
  # 是否在导出 .rdb 数据库文件的时候采用 LZF 压缩字符串和对象?压缩会产生较高的 CPU
  rdbcompression yes
    
  # 从版本 RDB版本 5 开始,一个 CRC64 的校验就被放在了文件末尾。 这会让格式更加耐攻击,但是当存储或者加载 rbd 文件的时候会有一个 10% 左右的性能下降
  # 没有校验的 RDB 文件会有一个 0 校验位,来告诉加载代码跳过校验检查 
  rdbchecksum yes
  
  dbfilename dump.rdb

之后 dump.rdb 文件一直稳定在 255M

# kubectl exec -it -n grafana redis-master-0 -c redis -- ls -lht /data
total 255M
-rw-r--r-- 1 1001 1001 255M Dec  1 05:38 dump.rdb
drwxrws--- 2 root 1001  16K Dec  1 05:38 lost+found
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容