mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2025-12-16 03:15:59 +00:00
Add redis-to-valkey migratior
This commit is contained in:
8
data/Dockerfiles/valkeymigrator/Dockerfile
Normal file
8
data/Dockerfiles/valkeymigrator/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM python:3.13.2-alpine3.21
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY migrate.py /app/migrate.py
|
||||||
|
RUN pip install --no-cache-dir redis
|
||||||
|
|
||||||
|
CMD ["python", "/app/migrate.py"]
|
||||||
78
data/Dockerfiles/valkeymigrator/migrate.py
Normal file
78
data/Dockerfiles/valkeymigrator/migrate.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import subprocess
|
||||||
|
import redis
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Container names
|
||||||
|
SOURCE_CONTAINER = "redis-old-mailcow"
|
||||||
|
DEST_CONTAINER = "valkey-mailcow"
|
||||||
|
VALKEYPASS = os.getenv("VALKEYPASS")
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_redis():
|
||||||
|
src_redis = redis.StrictRedis(host=SOURCE_CONTAINER, port=6379, db=0, password=VALKEYPASS, decode_responses=False)
|
||||||
|
dest_redis = redis.StrictRedis(host=DEST_CONTAINER, port=6379, db=0, password=VALKEYPASS, decode_responses=False)
|
||||||
|
|
||||||
|
cursor = 0
|
||||||
|
batch_size = 100
|
||||||
|
migrated_count = 0
|
||||||
|
|
||||||
|
print("Starting migration...")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
cursor, keys = src_redis.scan(cursor=cursor, match="*", count=batch_size)
|
||||||
|
keys_to_migrate = [key for key in keys if not key.startswith(b"PHPREDIS_SESSION:")]
|
||||||
|
|
||||||
|
for key in keys_to_migrate:
|
||||||
|
key_type = src_redis.type(key)
|
||||||
|
print(f"Import {key} of type {key_type}")
|
||||||
|
|
||||||
|
if key_type == b"string":
|
||||||
|
value = src_redis.get(key)
|
||||||
|
dest_redis.set(key, value)
|
||||||
|
|
||||||
|
elif key_type == b"hash":
|
||||||
|
value = src_redis.hgetall(key)
|
||||||
|
dest_redis.hset(key, mapping=value)
|
||||||
|
|
||||||
|
elif key_type == b"list":
|
||||||
|
value = src_redis.lrange(key, 0, -1)
|
||||||
|
for v in value:
|
||||||
|
dest_redis.rpush(key, v)
|
||||||
|
|
||||||
|
elif key_type == b"set":
|
||||||
|
value = src_redis.smembers(key)
|
||||||
|
for v in value:
|
||||||
|
dest_redis.sadd(key, v)
|
||||||
|
|
||||||
|
elif key_type == b"zset":
|
||||||
|
value = src_redis.zrange(key, 0, -1, withscores=True)
|
||||||
|
for v, score in value:
|
||||||
|
dest_redis.zadd(key, {v: score})
|
||||||
|
|
||||||
|
# Preserve TTL if exists
|
||||||
|
ttl = src_redis.ttl(key)
|
||||||
|
if ttl > 0:
|
||||||
|
dest_redis.expire(key, ttl)
|
||||||
|
|
||||||
|
migrated_count += 1
|
||||||
|
|
||||||
|
if cursor == 0:
|
||||||
|
break # No more keys to scan
|
||||||
|
|
||||||
|
print(f"Migration completed! {migrated_count} keys migrated.")
|
||||||
|
|
||||||
|
print("Forcing Valkey to save data...")
|
||||||
|
try:
|
||||||
|
dest_redis.save() # Immediate RDB save (blocking)
|
||||||
|
dest_redis.bgrewriteaof() # Rewrites the AOF file in the background
|
||||||
|
print("Data successfully saved to disk.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to save data: {e}")
|
||||||
|
|
||||||
|
# Main script execution
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
migrate_redis()
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
52
helper-scripts/redis-to-valkey.sh
Executable file
52
helper-scripts/redis-to-valkey.sh
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||||
|
source "${SCRIPT_DIR}/../mailcow.conf"
|
||||||
|
|
||||||
|
VOLUME="${COMPOSE_PROJECT_NAME}_redis-vol-1"
|
||||||
|
if ! docker volume inspect "$VOLUME" &>/dev/null; then
|
||||||
|
echo "Error: Docker volume '$VOLUME' does not exist. Nothing to migrate."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Do you want to proceed with the migration of your old redis data to valkey? (y/n) " CONFIRM
|
||||||
|
if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
echo "Migration aborted."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the old Redis container
|
||||||
|
docker run -d --name redis-old-mailcow \
|
||||||
|
--restart always \
|
||||||
|
--network ${COMPOSE_PROJECT_NAME}_mailcow-network \
|
||||||
|
--hostname redis-old \
|
||||||
|
--volume ${VOLUME}:/data/ \
|
||||||
|
--volume ${SCRIPT_DIR}/../data/conf/valkey/valkey-conf.sh:/valkey-conf.sh:z \
|
||||||
|
--entrypoint "/bin/sh" \
|
||||||
|
-e VALKEYPASS="${VALKEYPASS}" \
|
||||||
|
redis:7.4.2-alpine -c "/valkey-conf.sh && redis-server /valkey.conf"
|
||||||
|
|
||||||
|
|
||||||
|
# Wait for old Redis to be ready
|
||||||
|
echo "Waiting for redis-old-mailcow to be ready..."
|
||||||
|
until docker exec redis-old-mailcow redis-cli -a "$VALKEYPASS" ping | grep -q "PONG"; do
|
||||||
|
echo "Redis not ready yet..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "redis-old-mailcow is ready!"
|
||||||
|
|
||||||
|
# Run the migrate container
|
||||||
|
docker run --rm --name valkeymigrator-mailcow \
|
||||||
|
--network ${COMPOSE_PROJECT_NAME}_mailcow-network \
|
||||||
|
-e VALKEYPASS="${VALKEYPASS}" \
|
||||||
|
mailcow/valkeymigrator:0.1
|
||||||
|
|
||||||
|
echo "Migration completed!"
|
||||||
|
docker stop redis-old-mailcow
|
||||||
|
docker rm redis-old-mailcow
|
||||||
|
|
||||||
|
read -p "Do you want to delete the old Redis volume? (y/n) " CONFIRM
|
||||||
|
if [[ "$CONFIRM" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
|
||||||
|
docker volume rm "$VOLUME"
|
||||||
|
echo "Docker volume '$VOLUME' has been deleted."
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user