1. Introducción a Atlas

atlas

Atlas es una herramienta independiente del lenguaje diseñada específicamente para gestionar y migrar esquemas de bases de datos utilizando principios modernos de DevOps. Proporciona dos opciones de flujo de trabajo:

  • Declarativo: Similar a Terraform, Atlas compara el estado actual de la base de datos con el estado deseado definido usando HCL, SQL, u ORM schema. Con base en esta comparación, genera y ejecuta un plan de migración para llevar la base de datos a su estado deseado.
  • Versionado: A diferencia de otras herramientas, Atlas planifica automáticamente las migraciones de esquema para ti. Los usuarios pueden describir su esquema de base de datos deseado usando HCL, SQL o su ORM elegido, y con Atlas, planificar, revisar y aplicar las migraciones necesarias a la base de datos.

Una ventaja clave de Atlas es su simplificación de la complejidad de la gestión de bases de datos, facilitando el control de versiones, la colaboración y la implementación.

2. Instalación de Atlas

Antes de usar Atlas para gestionar las versiones de la base de datos, necesitamos instalarlo. A continuación se detallan los pasos de instalación para diferentes plataformas.

2.1 Instalación en macOS + Linux

En sistemas macOS o Linux, puedes descargar e instalar la última versión de Atlas utilizando el siguiente comando de línea de comandos:

curl -sSf https://atlasgo.sh | sh

Este comando detectará automáticamente la versión del sistema operativo, descargará la versión de compilación adecuada, y colocará el archivo binario de Atlas en la ruta ejecutable.

2.2 Instalación a través de Homebrew

Si estás utilizando macOS y ya has instalado el gestor de paquetes Homebrew, instalar Atlas es tan simple como ejecutar el comando:

brew install ariga/tap/atlas

Esto descargará la última versión de Atlas desde el repositorio de Homebrew y la instalará.

2.3 Instalación con Docker

Instalar y ejecutar Atlas a través de Docker es un método rápido y conveniente, especialmente para pruebas temporales o para usuarios que prefieren no instalar software adicional en su sistema host.

Primero, descarga la imagen de Atlas para Docker:

docker pull arigaio/atlas

Luego, puedes ejecutar un comando de ayuda para confirmar una instalación exitosa:

docker run --rm arigaio/atlas --help

Si el contenedor necesita acceder a la red del host o a directorios locales, utiliza la bandera --net=host y monta los directorios requeridos:

docker run --rm --net=host \
  -v $(pwd)/migrations:/migrations \
  arigaio/atlas migrate apply \
  --url "mysql://root:pass@:3306/test"

2.4 Instalación en Windows

Descarga el archivo binario, https://release.ariga.io/atlas/atlas-windows-amd64-latest.exe, y añade la ruta de instalación a la variable de entorno PATH del sistema.

3. Exportar la Estructura de la Tabla de la Base de Datos Existente Usando Atlas

Atlas proporciona el comando atlas schema inspect, que se puede utilizar para exportar la estructura de una base de datos existente. Este comando admite la lectura de descripciones de la base de datos desde la URL de la base de datos y las muestra en tres formatos diferentes: el formato predeterminado Atlas HCL, formato SQL y formato JSON. En esta guía, mostraremos cómo utilizar los formatos Atlas HCL y SQL, ya que el formato JSON se utiliza típicamente para procesar la salida con jq.

Para inspeccionar una instancia local en ejecución de MySQL y escribir la salida en un archivo llamado schema.hcl, utilice el siguiente comando:

atlas schema inspect -u "mysql://root:pass@localhost:3306/example" > schema.hcl

Abra el archivo schema.hcl para ver la definición de la estructura de la tabla de Atlas que describe la base de datos (HCL es un formato de configuración de estructura de tabla independiente de la base de datos definido por Atlas). Por ejemplo, este archivo contendrá el siguiente contenido:

table "users" {
  schema = schema.example
  column "id" {
    null = false
    type = int
  }
  column "name" {
    null = true
    type = varchar(100)
  }
  primary_key {
    columns = [column.id]
  }
}

Este bloque de código representa una estructura de tabla con columnas id y name. El campo schema hace referencia a la definición del modelo example definido en otro lugar en este documento. Además, el subbloque primary_key especifica la columna id como la clave primaria para la tabla. Atlas se esfuerza por imitar la sintaxis de la base de datos en la que se está operando. En este ejemplo, la columna id tiene un tipo de int, mientras que la columna name tiene un tipo de varchar(100).

De manera similar, para exportar una definición de modelo SQL de la base de datos, puede utilizar el siguiente comando:

atlas schema inspect -u "mysql://root:pass@localhost:3306/example" --format '{{ sql . }}' > schema.sql

Abra el archivo schema.sql para ver la descripción SQL de la base de datos, que típicamente contiene algunas declaraciones CREATE TABLE.

Este enfoque facilita entender los detalles de la estructura de la tabla de la base de datos existente y proporciona una referencia precisa para migraciones versionadas subsiguientes.

4.1 Flujo de Trabajo Declarativo

El Flujo de Trabajo Declarativo de Atlas permite a los usuarios definir de forma declarativa el estado final deseado de la base de datos. Los usuarios solo necesitan describir cuál debería ser el estado final del esquema de la base de datos, y la herramienta Atlas generará y ejecutará automáticamente planes de migración para realizar la transición de manera segura del estado actual del esquema de la base de datos a este estado esperado.

A continuación, se muestra cómo utilizar el Flujo de Trabajo Declarativo para definir y lograr el estado deseado de la base de datos:

4.1.1 Definir la Estructura de la Tabla de Destino

Primero, necesitas crear un archivo que defina la estructura esperada de la base de datos, que puede estar en formato HCL, SQL o ORM (Mapeo Objeto-Relacional).

Tomando como ejemplo el formato HCL, define una nueva tabla blog_posts:

table "blog_posts" {
  schema = schema.example
  column "id" {
    null = false
    type = int
  }
  column "title" {
    null = true
    type = varchar(100)
  }
  column "body" {
    null = true
    type = text
  }
  column "author_id" {
    null = true
    type = int
  }
  primary_key {
    columns = [column.id]
  }
  foreign_key "author_fk" {
    columns     = [column.author_id]
    ref_columns = [table.users.column.id]
  }
}

Nota: El formato de sintaxis de HCL se cubrirá en una sección posterior.

4.1.2 Realizar la Migración Utilizando la Herramienta Atlas

Una vez completada la definición de la estructura de la tabla de la base de datos, puedes utilizar el comando schema apply de Atlas para realizar la migración.

atlas schema apply \
  -u "mysql://root:pass@localhost:3306/example" \
  --to file://schema.hcl

Después de ejecutar el comando anterior, Atlas comparará el esquema de la base de datos existente con el esquema definido en el archivo, generará un plan de migración y pedirá al usuario la confirmación para ejecutarlo. Una vez que el usuario confirme el plan de ejecución, Atlas realizará la migración en la base de datos y la actualizará al estado deseado.

4.2 Flujo de Trabajo Versionado

El flujo de trabajo versionado es otro modo de uso compatible con Atlas, a veces llamado "migración basada en cambios". Es adecuado para escenarios en los que los cambios en el esquema de la base de datos deben estar versionados y revisados en el proceso de revisión de código.

Los pasos del flujo de trabajo versionado incluyen:

4.2.1 Calcular diferencias

Antes de comenzar la migración, es necesario comparar la estructura actual de la base de datos con el estado deseado y determinar las diferencias entre ambos. Esto se puede lograr ejecutando el comando atlas migrate diff.

atlas migrate diff create_blog_posts \
  --dir "file://migrations" \
  --to "file://schema.hcl" \
  --dev-url "docker://mysql/8/example"

El parámetro --dir especifica la URL de la carpeta de migración, con un valor predeterminado de file://migrations. El parámetro --to especifica la URL del estado deseado (por ejemplo, la base de datos del entorno de desarrollo), y --dev-url proporciona una URL para una base de datos de desarrollo utilizada para calcular las diferencias (ten en cuenta que este --dev-url necesita especificar una base de datos vacía que Atlas utiliza para calcular las diferencias).

Consejo: Si deseas generar archivos SQL, consulta la sección anterior para configurar el formato usando el parámetro --format.

4.2.2 Aplicar cambios de migración

Después de que se complete el cálculo de diferencias, Atlas generará dos archivos de migración guardados en la carpeta migrations. Por ejemplo, si el formato seleccionado es SQL, los archivos generados por el comando diff, como el siguiente archivo SQL, contendrán comandos de migración para crear una nueva tabla:

-- crear tabla "blog_posts"
CREATE TABLE `blog_posts` (
  `id` int NOT NULL,
  `title` varchar(100) DEFAULT NULL,
  `body` text DEFAULT NULL,
  `author_id` int NULL REFERENCES `users`(id),
  PRIMARY KEY (`id`)
);

Una vez generados los archivos de migración, puedes utilizar herramientas de control de versiones (por ejemplo, Git) para gestionar estos cambios. Este enfoque permite realizar numerosas modificaciones en la estructura de tabla de la base de datos en el entorno de desarrollo y, cuando llegue el momento de la publicación, comparar el entorno de desarrollo y el entorno UAT utilizando comandos de Atlas para generar archivos de migración de estructura de tabla de base de datos. Estos archivos de migración luego se pueden aplicar a los entornos UAT y producción para actualizar la base de datos.

Estos dos flujos de trabajo, Declarativo y Versionado, proporcionan flexibilidad para diferentes modos de desarrollo e implementación, permitiendo a los equipos elegir el método que mejor se adapte a las necesidades de su proyecto para gestionar los cambios en el esquema de la base de datos.

5. Descripción del formato HCL

5.1 Introducción

HCL es un lenguaje declarativo utilizado para describir las definiciones de la estructura de las tablas de la base de datos. Atlas utiliza el formato HCL para escribir esquemas de base de datos, proporcionando una estructura sólida para describir diferentes aspectos de la base de datos. La ventaja de HCL radica en su legibilidad, facilidad de mantenimiento y soporte para funciones como la inyección de variables y anotaciones adicionales.

Consejo: Si tu proyecto necesita adaptarse a múltiples bases de datos, describir las estructuras de tabla de forma independiente de la base de datos utilizando HCL puede ser muy conveniente.

5.2 Objeto schema

El objeto schema se utiliza para describir un esquema de base de datos. En MySQL y SQLite, representa la BASE DE DATOS, mientras que en PostgreSQL, representa el ESQUEMA. Un archivo HCL puede contener uno o más objetos schema.

schema "public" {
  comment = "Un comentario de esquema"
}

schema "private" {}

5.3 Objeto table

El objeto table se utiliza para describir una tabla en una base de datos SQL, incluyendo columnas, índices, restricciones y diversas propiedades adicionales admitidas por distintos controladores de base de datos.

table "users" {
  schema = schema.public
  column "id" {
    type = int
  }
  column "name" {
    type = varchar(255)
  }
  column "manager_id" {
    type = int
  }
  primary_key {
    columns = [
      column.id
    ]
  }
  index "idx_name" {
    columns = [
      column.name
    ]
    unique = true
  }
  foreign_key "manager_fk" {
    columns     = [column.manager_id]
    ref_columns = [table.users.column.id]
    on_delete   = CASCADE
    on_update   = NO_ACTION
  }
}

5.4 Objeto column

El objeto column es un subrecurso de table utilizado para definir las columnas en la tabla.

column "name" {
  type = text
  null = false
}

column "age" {
  type = integer
  default = 42
}

column "active" {
  type = tinyint(1)
  default = true
}

5.5 Objeto primary_key

El objeto primary_key es un subrecurso de table que define la clave primaria de la tabla.

primary_key {
  columns = [column.id]
}

5.6 Objeto foreign_key

El objeto foreign_key es un subrecurso de table que define columnas que hacen referencia a columnas en otras tablas.

table "orders" {
  schema = schema.market
  // ...
  column "owner_id" {
    type = integer
  }
  foreign_key "owner_id" {
    columns     = [column.owner_id]
    ref_columns = [table.users.column.id]
    on_update   = NO_ACTION
    on_delete   = NO_ACTION
  }
}

5.7 Objeto index

El objeto index representa un índice en la tabla.

index "idx_name" {
  columns = [
    column.name
  ]
  unique = true
}