This post is also in its original language, English.

Introducción

Estos últimos memes he estado migrando servicios desde la Raspberry Pi a mi nuevo servidor HP y el que me faltaba era MongoDB.

MongoDB ha sido importante en mi stack últimamente, siendo la base de datos utilizada en mis bots de Discord, webhooks-ui y otros proyectos de los que no me acuerdo ahora mismo.

Probando el plan

La base de datos está en Docker con una replica set de 1 nodo (él mismo) para que Prisma funcione.

La idea es añadir el servidor HP como una replica secundaria y promocionarla para que sea la primera.

Primero he creado 2 contenedores de Docker en mi ordenador principal con este compose.yml:

version: "3.8"
services:
  mongo1:
    image: mongo:4.4.17-rc0-focal
    container_name: mongo1
    restart: always
    ports:
      - 27017:27017
    volumes:
      - ./mongo1:/data/db
    command: mongod --replSet mongoset
    networks:
      - mongo
  mongo2:
    image: mongo:4.4.17-rc0-focal
    container_name: mongo2
    restart: always
    ports:
      - 27018:27017
    volumes:
      - ./mongo2:/data/db
    command: mongod --replSet mongoset
    networks:
      - mongo
networks:
  mongo:

y lo ejecuté con docker compose up -d.

Fui a conectarme con MongoDB Compass y no funcionó por alguna razón. Pregunté a GPT y nada. Parece que aceptó la conexión, pero no se conectó, así que instalé mongosh y traté de conectarme con eso.

$ mongosh mongodb://localhost:27017

…¡y funcionó! No tenía mucho sentido, pero bueno, podemos trabajar con eso.

Luego me conecté a la instancia mongo1 y ejecuté el siguiente comando:

> rs.initiate()

y funcionó, pero solo se conectó esa misma base de datos. Antes de añadir la segunda base de datos a la réplica, decidí hacerle un ping desde el primer contenedor (solo para verificar si la configuración de red funcionaba):

docker exec mongo1 sh -c "rm /bin/ping;apt update;apt install inetutils-ping -y;ping mongo2"

Eliminé /bin/ping porque intenté transferir el binario desde WSL al contenedor, pero aún necesitaba algunas bibliotecas y no quería complicarme, así que simplemente instalé el paquete.

Funcionó, así que procedí a agregar la segunda base de datos al conjunto de réplicas:

> rs.add("mongo2")

Después de esperar un poco, la segunda base de datos se conectó y todo funcionaba bien. Vamos a crear una colección y algunos documentos en la réplica primaria (mongo1):

> use test
> db.createCollection("test")
> db.test.insertOne({ name: "test" })

y luego, vamos a comprobar si está en la segunda réplica (mongo2):

$ mongosh mongodb://localhost:27018
> use test
> db.getMongo().setReadPref("secondaryPreferred")
> db.test.find()

y, sí, funcionó.

No sé realmente si los ORMs leerán cuando se conecten a la segunda réplica, pero por ahora está bien, ya que el plan principal está en marcha. Entonces, para promover a mongo2 me conecté a la réplica primaria (mongo1) y ejecuté el siguiente comando:

> rs.stepDown()

¡Y funcionó! ¡Woo! La segunda réplica ahora es la primaria. Ahora podemos empezar redoble de tambores, por favor:

La migración

Esto es. Allá vamos.

Fui y creé un nuevo archivo docker-compose en mi servidor con el siguiente contenido:

version: "3.8"
services:
    mongo:
        image: mongo:4.4.17-rc0-focal
        container_name: mongodb
        restart: unless-stopped
        ports:
            - 27017:27017
        volumes:
            - ./mongo:/data/db
        command: mongod --replSet rs0

Después de desplegar los contenedores, me conecté usando mongosh a la base de datos primaria (RPi) y ejecuté el siguiente comando:

> rs.add("ip")

y después de esperar un tiempo, parece que funcionó. Luego me conecté a la nueva base de datos y ejecuté el siguiente comando para verificar si la réplica se clonó correctamente:

> db.getMongo().setReadPref("secondaryPreferred")

y dejemos que los resultados hablen por sí mismos:

rs0 [direct: secondary] test> show dbs
# nota del autor: algunas bases de datos están redactadas por privacidad 
admin         80.00 KiB
api           80.00 KiB
ava           40.00 KiB
bask         168.00 KiB
config       144.00 KiB
local        348.00 KiB
vinci        428.00 KiB
rs0 [direct: secondary] test> use vinci
switched to db vinci
rs0 [direct: secondary] vinci> show tables
afk
birthdays
chatgpt
giveaways-enters
giveaways-message
padyama
suggestions
twitter
warns
youtube
rs0 [direct: secondary] vinci> db.afk.find()
[
  {
    _id: ObjectId("sadfsad fsadfsdf"),
    id: 'redacted',
    reason: 'redacted',
    __v: 0
  },
  {
    _id: ObjectId("asdfsadfadf"),
    id: 'redacted',
    reason: 'readacted',
    __v: 0
  }
]
rs0 [direct: secondary] vinci>

Genial. Ahora intentemos escribir algo en la base de datos desde Vinci: Eso funcionó y podemos verlo duplicado en la réplica secundaria:

rs0 [direct: secondary] vinci> db.afk.find({ id: '703974042700611634' })
[
  {
    _id: ObjectId("6550eccc6154a8c9030fe76a"),
    id: '703974042700611634',
    reason: 'test',
    __v: 0
  }
]

Ahora editemos todos los archivos .env y cambiemos la URL de la base de datos a la nueva secundaria. Para esto, revisé todas las bases de datos que tengo y luego fui de arriba hacia abajo editando los secretos.

Después de ejecutar todos los comandos para reiniciar los servicios, ejecuté rs.stepDown() en la réplica primaria Raspberry Pi y, como se esperaba, el servidor HP tomó el control.

El último comando del día:

> rs.remove("ip")

y eso fue todo.