Como utilizar o Flyway?
Objetivos do post:
- Mostrar como usar o Flway usando o Docker
- Apresentar o fluxo de trabalho com o Flyway
Por que usar o Docker?
A primeira pergunta que você deve se fazer é:
Se eu posso instalar o Flyway na minha máquina, por que utilizar o Docker?
Com a utilização do Docker, fica mais fácil você "colocar a mão na massa", pois não precisa entrar em sites para buscar os programas que deseja testar, não precisa configurá-los, e caso queira parar o teste é muito simples fazer a remoção dos containers e das imagens, sem deixar "rastros" no seu computador.
Além disso, se você usar Docker para testar os programas, terá sempre um fluxo de trabalho muito similar ao que acabei de descrever. Por isso, #use_docker. E se você não conhece Docker, ou não tem ele instalado, leia o post Instalando o Docker no Ubuntu.
Qual imagem do Flyway utilizar?
Agora que já definimos a utilização do Docker, devemos definir qual imagem Docker vamos utilizar. Se você buscar Flyway no Docker Hub, vai encontrar muitas imagens diferentes. Eu recomendo nesses casos, sempre que possível, verificar se tem a imagem oficial da própria equipe que desenvolve a ferramenta. No caso do Flyway, felizmente temos a imagem da própria equipe.
Sendo assim, é essa que nós vamos utilizar!
Qual banco utilizar?
A princípio, você pode testar com qualquer tipo de banco relacional. Por praticidade, vamos aproveitar os conhecimentos que adquirimos no post Rodando o Postgres em Docker e vamos utilizar o Postgres como base de teste.
Docker Compose?
O Docker Compose vai nos ajudar a realizar o teste. Com ajuda dele, vamos subir dois containers Docker, um contendo o Postgres e o outro com o Flyway. De certo modo, é parecido com o que teremos em produção, onde normalmente o banco roda em um servidor, e a aplicação roda em outro. Desse modo, a rotina de migração do banco pode ser executada a partir do servidor onde está a aplicação.
Se você não conhece nada de Docker Compose, não se desespere, que apresentarei o básico necessário para você conseguir seguir esse post. E se você não tem ele instalado, acesse o post Instalando o Docker Compose no Ubuntu.
Subindo o banco
Inicialmente vamos usar o Docker Compose somente para subir o banco de dados. Se você não tem ele instalado, siga esses passos para instalá-lo:
Com o Docker Compose pronto para uso, vamos então criar o arquivo docker-compose.yml
conforme o código a seguir:
version: "3.5"
services:
db:
image: "postgres:12.2-alpine"
environment:
- POSTGRES_USER="flyway"
- POSTGRES_PASSWORD="flyway"
- POSTGRES_DB="flyway_teste"
Inicialmente estamos definindo qual versão de arquivo Docker Compose nós vamos utilizar.
A versão deve ser compatível com o Docker Engine que você estiver utilizando, seguindo essa matriz.
No meu caso, utilizarei a versão 3.5 (version: "3.5"
).
Seguindo o código, temos a definição dos serviços (services:
). Nesse momento usaremos apenas um serviço (db
), que
nada mais é que o Postgres.
Na sequencia definimos qual imagem vamos utilizar para a criação do container ("postgres:12.2-alpine") e as variáveis de ambiente que serão definidas no container:
environment:
- POSTGRES_USER="flyway"
- POSTGRES_PASSWORD="flyway"
- POSTGRES_DB="flyway_teste"
Com elas estamos definindo que o nome do usuário será flyway
(POSTGRES_USER="flyway"
), assim como a
senha (POSTGRES_PASSWORD="flyway"
), e o nome da base será flyway_teste
(POSTGRES_DB="flyway_teste"
).
Vamos executar o comando a seguir para que o container seja instanciado:
docker-compose up -d
Esse comando espera que exista um arquivo docker-compose.yml no diretório onde ele é executado. Se você estiver em outro diretório ou o nome do arquivo que vocẽ criar for diferente, use o parâmetro -f para especificar o caminho completo do arquivo.
Podemos conferir que nosso container está rodando:
docker-compose ps
Name Command State Ports
-------------------------------------------------------
db docker-entrypoint.sh postgres Up 5432/tcp
O estado do serviço está Up
, ou seja, nosso container está rodando. Vamos agora rodar um comando no container para
listar os dados da tabela flyway_version
, do schema flyway
. É necessário informar a senha que definimos no arquivo
docker-compose.yml
.
docker-compose exec db psql -U flyway -d flyway_teste -W -c "select * from flyway.flyway_schema_history"
Password:
ERROR: relation "flyway.flyway_schema_history" does not exist
LINE 1: select * from flyway.flyway_schema_history
^
Obviamente que não criamos esse schema
e muito menos a tabela, por isso é informado o erro.
Vamos agora adicionar o seguinte trecho ao nosso arquivo docker-compose.yml
:
flyway:
image: flyway/flyway:6.4.1-alpine
environment:
- FLYWAY_EDITION=community
command: -url=jdbc:postgresql://db:5432/flyway_teste -schemas=flyway -user=flyway -password=flyway -connectRetries=10 migrate
Cuidado com a sintaxe do arquivo! Você deve colocar o serviço flyway
com a mesma identação do serviço db
para não ter problemas.
Nesse novo serviço, nós definimos a imagem que vamos utilizar flyway/flyway:6.4.1-alpine
. Observe que a parte
6.4.1-alpine
é chamada de tag. Em geral, as tags são utilizadas para especificar qual é a versão da ferramenta.
Nessa tag temos duas informações, a versão do Flyway (6.4.1
) e o alpine
, que indica que a imagem base utilizada para
geração dessa imagem é a da distribuição Alpine do Linux, que é uma versão bem enxuta do Linux, utilizada para gerar
imagens menores.
Usando a variável de ambiente FLYWAY_EDITION
, definimos que vamos usar a versão commnunity (open source) do Flyway.
Por último, definimos os parâmetros para o comando principal da imagem do Flyway. Você encontra essas informações na
documentação da imagem do Flyway, mas vamos entender melhor o que está acontecendo, usando novamente os comandos
docker-compose up -d
e o comando docker-compose ps
. Esse último nos revela o seguinte:
Name Command State Ports
---------------------------------------------------------------------------
flyway-docker_db_1 docker-entrypoint.sh postgres Up 5432/tcp
flyway-docker_flyway_1 /flyway/flyway -url=jdbc:p ... Exit 0
Observe que o container do Flyway apresenta como comando /flyway/flyway
seguindo dos parâmetros
que informamos no docker-compose. Essa primeira parte do comando é definida no Dockerfile da imagem, e é chamada de
entrypoint (ou ponto de entrada, em português). Esse comando é executado por padrão, e podemos passar
parâmetros para ele, por meio do command
.
Ou seja, na verdade o container executa o comando principal flyway
, e aí portanto podemos usar qualquer um dos comandos
do Flyway. No nosso caso, estamos utilizando o comando migrate
,
portanto podemos passar os vários parâmetros.
O comando migrate
serve para aplicar as migrações na nossa base de dados. O que fizemos basicamente foi definir a
URL da nossa base de dados, o usuário, a senha, e o número de tentativas de conexão (no caso de falhas).
Observe que o estado do nosso serviço está marcado como Exit 0
, ou seja o comando foi executado e finalizado com
sucesso. Para ver o log do nosso container, podemos usar o seguinte comando:
docker-compose logs flyway
Ele nos mostra o seguinte:
Attaching to flyway-docker_flyway_1
flyway_1 | Flyway Community Edition 6.4.1 by Redgate
flyway_1 | Database: jdbc:postgresql://db:5432/flyway_teste (PostgreSQL 12.2)
flyway_1 | Creating schema "flyway" ...
flyway_1 | Creating Schema History table "flyway"."flyway_schema_history" ...
flyway_1 | Current version of schema "flyway": null
flyway_1 | Schema "flyway" is up to date. No migration necessary.
O Flyway foi executado (extamente a versão 6.4.1) e acessou a base de dados que informamos (observe que ele mostra a
versão do Postgres utilizado). Como definimos que usaríamos o schema flyway
, e ele ainda não existia, o comando criou o
schema
para nós, e depois a tabela de migração flyway_schema_history
(veja detalhes no post Flyway e versionamento de bases de dados ).
Para confirmar que a tabela foi criada no Postgres, vamos novamente rodar esse comando:
docker-compose exec db psql -U flyway -d flyway_teste -W -c "select * from flyway.flyway_schema_history"
Password:
installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success
----------------+---------+------------------------------+--------+----------+----------+--------------+----------------------------+----------------+---------
0 | | << Flyway Schema Creation >> | SCHEMA | "flyway" | | flyway | 2020-05-07 20:22:43.823796 | 0 | t
(1 row)
Observe que como não definimos nenhuma migração, o próprio flyway criou uma migração, que indica a criação do schema do Flyway, e a da própria tabela. Ou seja, já temos gravado na base de dados a informação de quando o Flyway foi executado pela primeira vez.
Para ficar mais interessante, vamos criar nossa primeira migração, com o seguinte SQL:
CREATE TABLE pessoa (cpf character varying(14), nome character varying(50), ano_nascimento character varying(9));
Esse SQL deve ser salvo em um novo diretório migracoes
(no mesmo diretório do arquivo docker-compose.yml
), com o nome
do arquivo sendo V1__Criar_tabela_pessoa.sql
. Esse nome de arquivo segue o padrão original do Flyway:
V$VERSAO_DA_BASE_DE_DADOS__$DESCRICAO_DA_MIGRACAO.sql
Sendo que o V
inicial é chamado de prefixo, a $VERSAO_DA_BASE_DE_DADOS
é uma versão arbitrária da base de dados que
será atingida ao aplicar a migração, o __
é o separador e a $DESCRICAO_DA_MIGRACAO
é um nome que faça sentido para você e descreva a
migração realizada, e o .sql
é o sufixo.
É possível personalizar o sufixo, o separador e o sufixo por meio de parâmetros.
Precisamos agora fazer com que a migração seja mapeada para dentro de nosso container, para que o Flyway que roda lá
consiga executá-la. Para isso, devemos alterar o docker-compose.yml
:
flyway:
image: flyway/flyway:6.4.1-alpine
environment:
- FLYWAY_EDITION=community
command: -url=jdbc:postgresql://db:5432/flyway_teste -schemas=flyway -user=flyway -password=flyway -connectRetries=10 migrate
volumes:
- ./migracoes:/flyway/sql/
Observe que estamos mapeando o subdiretório recém-criado migracoes, para o diretório do container /flyway/sql, que é o diretório padrão que o Flyway do container espera que estejam as imagens.
Usemos novamente o comando para subir os serviços:
sudo docker-compose up -d
Se olharmos o log do Flyway, vamos ver o seguinte:
docker-compose logs flyway
Attaching to flyway-docker_flyway_1
flyway_1 | Flyway Community Edition 6.4.1 by Redgate
flyway_1 | Database: jdbc:postgresql://db:5432/flyway_teste (PostgreSQL 12.2)
flyway_1 | Successfully validated 2 migrations (execution time 00:00.047s)
flyway_1 | Current version of schema "flyway": null
flyway_1 | Migrating schema "flyway" to version 1 - Criar tabela pessoa
flyway_1 | Successfully applied 1 migration to schema "flyway" (execution time 00:00.062s)
Perceba que agora o Flyway validou 2 migrações, a própria que ele tinha executado anteriormente, a nossa, que foi
adicionada agora. Ele informa que a versão do schema flyway
era null
, e que agora está migrando para a versão 1,
por meio da migração Criar tabela pessoa
. Observe que é exatamente a parte da descrição no nome do nosso arquivo, sem
o caracter _
. A migração foi aplicada com sucesso.
Para confirmar isso, vamos ver a tabela do Flyway:
docker-compose exec db psql -U flyway -d flyway_teste -W -c "select * from flyway.flyway_schema_history"
Password:
installed_rank | version | description | type | script | checksum | installed_by | installed_on | execution_time | success
----------------+---------+------------------------------+--------+-----------------------------+------------+--------------+----------------------------+----------------+---------
0 | | << Flyway Schema Creation >> | SCHEMA | "flyway" | | flyway | 2020-05-07 20:22:43.823796 | 0 | t
1 | 1 | Criar tabela pessoa | SQL | V1__Criar_tabela_pessoa.sql | -332859306 | flyway | 2020-05-07 20:50:04.907069 | 36 | t
(2 rows)
Que confirma que a migração foi aplicada. Para checar a tabela pessoa
podemos executar:
docker-compose exec db psql -U flyway -d flyway_teste -W -c "\d+ pessoa"
Password:
Table "flyway.pessoa"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
----------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
cpf | character varying(14) | | | | extended | |
nome | character varying(50) | | | | extended | |
ano_nascimento | character varying(9) | | | | extended | |
Access method: heap
E como diria Galvão Bueno:
Olha o que ele fez! Olha o que ele fez! Olha o que ele fez!
O Flyway efetivamente aplicou nossa migração. Mas podemos melhorar um pouco essa configuração. Note que estamos passando
os parâmetros, inclusive senha, diretamente. Podemos criar um arquivo flyway.conf
no diretório atual e colocar essas informações nele:
flyway.url=jdbc:postgresql://db:5432/flyway_teste
flyway.schemas=flyway
flyway.user=flyway
flyway.password=flyway
flyway.connectRetries=5
E agora mapeá-lo para o container, como fizemos com as migrações. Desse modo, podemos tirar os parâmetros do
docker-compose.yml
:
flyway:
image: flyway/flyway:6.4.1-alpine
environment:
- FLYWAY_EDITION=community
command: migrate
volumes:
- ./migracoes:/flyway/sql/
- ./flyway.conf:/flyway/conf/flyway.conf
Note como ficou mais enxuto o arquivo, e como estamos fazendo o mapeamento do arquivo de configurações
exatamente na localizaçao esperada pelo container (/flyway/conf/flyway.conf
).
Lembra dos comandos do Flyway? Se quiser testá-los, como por exemplo o repair, basta trocar migrate
por repair
, e
subir novamente os serviços. Prático não!?
Caso não queira mais fazer testes, pode executar:
docker-compose down
Stopping flyway-docker_db_1 ... done
Removing flyway-docker_flyway_1 ... done
Removing flyway-docker_db_1 ... done
Removing network flyway-docker_default
E depois remover as imagens:
docker rmi flyway/flyway:6.4.1-alpine postgres:12.2-alpine
Untagged: flyway/flyway:6.4.1-alpine
Untagged: flyway/flyway@sha256:632fdca5dff4b8ef297cf8381b717b0d3c4b5881414f54ebced2b81fb4be8304
Deleted: sha256:5443b89d1162f4c49271f6d45603b7c40df00e0e8aaa62cbf419eadf1ec886ed
Deleted: sha256:c3a94578881cbc4e65b77ea83271d0b3cb28988cdcb190bbef2fbd996d507285
Deleted: sha256:492547d97d7a1b7e88c1992c88e6ff018945954b2cd5f87c0ee005bacbc29650
Deleted: sha256:8056eede62cc4fa8fecf337eef88706c3cbf0066cdca210f58bff5ee3bd23204
Untagged: postgres:12.2-alpine
Untagged: postgres@sha256:9ea72265275674225b1eaa2ae897dd244028af4ee7ef6e4e89fe474938e0992e
Deleted: sha256:ae192c4d3adaebbbf2f023e1e50eaadfabccb6b08c855ac13d6ce2232381a58a
Deleted: sha256:277b07301deac6997065ac915276044234c358692824f418b28d70c575b84eb1
Deleted: sha256:26c98973517469b8378488028632ae00a11841f5707f61da821d65fd1a043071
Deleted: sha256:8b1bd4464a5ed100dc9b3cd95704cb7fe0f43c46eec45f4cf511a019dfdfb6f6
Deleted: sha256:8fa290d788b68999c7c28f80b18e3f352ef1a4bd2806d53a58f2334e003f127d
Deleted: sha256:41cb65edf4be3a0e9d154f680cd6ef358bcaf31b0fa6bdc22b87ae3bd2b9a10b
Deleted: sha256:39989b6cd98a37ae66277ab0ec1650fba54aa36ed70c1bcc5bb97d1c93d4d717
Deleted: sha256:a43798ee281777a400d2913deca328eb551f32983d9a8876bae84a0689db7840
Deleted: sha256:3e207b409db364b595ba862cdc12be96dcdad8e36c59a03b7b3b61c946a5741a
Viu só como é fácil fazer testes com o Docker?
Agora se você gostou do Flyway, fica como exercício criar um nova migração, adicionando um campo na tabela
pessoa
e fazer o Flyway aplicá-la.
Qualquer dúvida posta aqui. Mas você deve estar se perguntando:
Esse processo ainda é muito manual. Não tem nada mais automático?
E a resposta é SIM. É possível integrar o Flyway no seu código, para ser executado sempre que sua aplicação sobe. No próximo post vamos ver como fazer isso com o Spring Boot.
Por fim, o código utilizado nesse post está disponível em https://gitlab.com/blog-do-bruno/flyway-docker.
Abraços e até a próxima!