{"id":"IL-0196","org":"Palark","date":"2020-03-13","docs":[],"title":{"en":"Adding a second Redis replica and lowering memory limits in one RedisFailover change restarted the only Redis pod that held data, and Sentinel promoted the empty new replica, erasing the data","ko":"Redis Operator 로 레플리카를 늘리면서 메모리 한도를 낮추자 데이터를 가진 유일한 Redis 파드가 재시작됐고, Sentinel 이 빈 레플리카를 마스터로 올려 데이터가 모두 지워졌다."},"domain":["kubernetes"],"impact":{"scope":"The largest Redis database of a customer's website (an .rdb snapshot over 5 GB): downtime and a restore from backup","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Changing the replica count and the pod resources in one rollout turned a seemingly harmless change into a rolling restart of the only pod with data while the new replica was still empty.\n\nA readiness probe that only checks that Redis answers a ping does not tell whether a replica has finished syncing. Operators for critical stateful components need to be evaluated for maturity before production use, and the dump.rdb file should be backed up before any change to the Redis cluster.","ko":"핵심 인프라를 쿠버네티스 오퍼레이터로 관리할 때는 그 오퍼레이터가 얼마나 성숙하고 검증됐는지 따져 봐야 한다. 레플리카 수와 리소스 값을 한 번에 바꾸면 롤링 업데이트가 예상하지 못한 순서로 파드를 재시작할 수 있다. 레디니스 프로브가 ping 만 보면 복제가 끝나지 않은 빈 인스턴스도 준비됐다고 판단한다. Redis 클러스터를 바꾸기 전에는 반드시 dump.rdb 파일을 백업해야 한다."},"related":[],"sources":[{"url":"https://palark.com/blog/failure-with-redis-operator-and-redis-data-analysis-tools/","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Palark runs Redis for a customer's large website on Kubernetes with the Redis Operator by Spotahome, which builds a Redis StatefulSet, a Sentinel Deployment and ConfigMaps from a RedisFailover custom resource. One installation was used as a persistent store without TTLs; its .rdb snapshot was over 5 GB and the single Redis pod used about 16 GB of RAM at peak while saving to disk, so at first only one replica fit on the nodes.\n\nAfter new servers were added, the team raised the replica count to two to make the database more reliable and, in the same change, cut the memory requests and limits from 22 GB to 19 GB so the pod could be rescheduled more easily. The resource change forced a rolling update of the StatefulSet. Because the operator's readiness probe was a plain ping, the new replica counted as ready before it held any data, the rolling update restarted the only pod with data, Sentinel promoted the empty replica, and replication from that empty master erased the original data. The result was downtime and a restore from backup.\n\nThe author names the operator's readiness probe as the principal cause and opened an issue with the operator project; the developers answered with a fix, which Palark tested. The author also points out that with this operator a single-replica setup was safer against data loss than a multi-replica one, because a master restart while a new replica is joining loses the data. A later analysis of the dump with redis-memory-analyzer found 27,402,900 keys, 16,236,021 of them (59.24%) under one key pattern, and the developers found over 18 million excess keys, which they attributed to a wrong architectural decision in the application.","ko":"Palark 는 고객 애플리케이션의 Redis 를 쿠버네티스 안에서 Spotahome 의 Redis Operator 로 운영했다. 이 오퍼레이터는 RedisFailover 커스텀 리소스를 바탕으로 Redis 설정 ConfigMap, Redis StatefulSet, Sentinel Deployment 를 만든다. 한 대형 웹사이트에서 Redis 를 인메모리 저장소, 캐시, 상태 저장소로 함께 쓰면서 가장 큰 데이터베이스의 .rdb 파일이 5 Gb 를 넘었다. 이 인스턴스는 디스크에 저장할 때 최대 약 16 Gb 의 RAM 을 썼고, 처음에는 레플리카를 더 띄울 노드 여유가 없어 레플리카 하나로 돌렸다.\n\n데이터센터에 서버가 늘자 신뢰성을 높이려고 레플리카를 두 개로 늘리기로 했다. 파드가 너무 커서 장애가 나도 다른 노드로 옮겨 가기 어려웠다. 그래서 옮겨 갈 가능성을 높이려고 같은 변경에서 메모리 requests 와 limits 도 22 GB 에서 19 GB 로 낮췄다. 백업을 준비한 뒤 롤아웃했다.\n\n새 인스턴스가 복제를 시작하자마자 ping 뿐인 레디니스 프로브를 통과했다. 리소스 값이 바뀌었으므로 롤링 업데이트는 데이터를 가진 유일한 파드를 재시작했다. Sentinel 은 마스터가 사라졌다고 보고 아직 데이터가 없는 레플리카를 마스터로 올렸다. 재시작한 원래 파드가 이 빈 마스터에 연결해 복제하면서 데이터가 모두 지워졌다. 서비스가 멈췄고 백업에서 복원했다.\n\nPalark 는 오퍼레이터의 레디니스 프로브를 주된 원인으로 보고 이슈를 올렸고, 개발자들에게서 수정을 받아 시험을 마쳤다. 이 오퍼레이터에서는 레플리카가 하나인 구성이 여러 개인 구성보다 오히려 데이터 손실에 강했다. 새 Redis 노드가 생기는 동안 마스터가 어떤 이유로든 재시작되면 데이터가 사라진다. 복원 뒤 데이터를 분석해 보니 키 27402900개 가운데 16236021개가 패턴 하나에 몰려 있었고, 개발자들은 애플리케이션 설계 문제로 생긴 것으로 보이는 불필요한 키를 1800만 개 넘게 찾아냈다."},"trigger":{"en":"A RedisFailover change that raised the number of Redis replicas from one to two and, in the same rollout, lowered the containers' memory requests and limits from 22 GB to 19 GB.","ko":"RedisFailover 리소스에서 Redis 레플리카 수를 1에서 2로 늘리면서, 같은 변경으로 컨테이너의 메모리 requests 와 limits 를 22 GB 에서 19 GB 로 낮췄다."},"severity":"unknown","symptoms":["data-loss","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":null,"components":["database","persistent-volume","memory-limit"],"root_cause":"software-bug","remediation":[{"text":{"en":"restored the Redis data from the backup prepared before the rollout","ko":"미리 준비해 둔 백업에서 Redis 데이터를 복원했다."}}],"action_items":[{"text":{"en":"Report the readiness probe flaw to the Redis Operator project and test the developers' fix","ko":"레디니스 프로브 문제를 Redis Operator 이슈로 올렸고, 개발자들에게서 받은 수정을 시험해 통과를 확인했다."}},{"text":{"en":"Reconsider the use of the Spotahome Redis Operator","ko":"상황 분석을 마친 뒤 Spotahome Redis Operator 를 계속 쓸지 다시 검토한다."}},{"text":{"en":"Clean up the Redis databases and fix the application code that created the excess keys","ko":"Redis 데이터베이스를 정리하고, 불필요한 키를 만든 애플리케이션 코드를 고친다."}},{"text":{"en":"Consider Redis built-in clustering and sharding","ko":"Redis 의 내장 클러스터링과 샤딩을 검토한다."}}],"contributing":["no-staged-rollout","single-point-of-failure","assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"Right after an updated RedisFailover resource was rolled out, the large Redis database lost all its data and the service went down; the data had to be restored from a backup taken just before the change.\n\nThe sequence, as reconstructed afterwards: the StatefulSet grew to two pods and the new pod joined as a slave and began replicating. The operator's readiness probe, which only sends a plain ping, marked the new pod ready almost at once. Because the memory requests and limits had also changed, the StatefulSet rolling update then restarted the one pod that held the data.\n\nThe Sentinels saw the master disappear and promoted the new, still empty replica to master. At that moment the old master had hardly managed the initial BGSAVE needed for a full resynchronisation. When the rolling update finished, the restarted original pod connected as a replica of the empty master and synchronised from it immediately, which wiped its data.","ko":"RedisFailover 리소스에 바뀐 설정을 적용하자 고객 웹사이트의 가장 큰 Redis 데이터베이스에서 데이터가 모두 사라졌다. 이 데이터베이스는 TTL 없이 영속 저장소로도 쓰였고, .rdb 스냅숏이 5 Gb 를 넘었다. 서비스가 멈췄고 백업에서 데이터를 복원해야 했다.\n\n롤아웃 과정은 이랬다. StatefulSet 이 두 개로 늘었고 새 인스턴스가 슬레이브가 되어 복제를 시작했다. 곧바로 새 인스턴스가 레디니스 프로브를 통과했는데, 이 프로브는 ping 하나만 확인했다. 리소스가 바뀌었으므로 롤링 업데이트가 데이터를 가진 유일한 파드를 재시작했다.\n\nSentinel 은 마스터가 없다고 바로 판단하고 빈 레플리카를 마스터로 올렸다. 그때 마스터는 전체 재동기화에 필요한 첫 BGSAVE 도 거의 끝내지 못한 상태였다. 롤링 업데이트가 끝나자 원래 파드는 새로 마스터가 된 빈 인스턴스에 레플리카로 연결됐고, 복제가 곧바로 끝나면서 데이터가 모두 지워졌다."},"updated_at":"2026-11-10T00:00:40.571464Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0196","org_slug":"palark"}