mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2025-12-13 01:45: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