Arquivo de utilidades - Blog do Fernando Jorge Mota https://fjorgemota.com/tag/utilidades-2/ Dicas e ferramentas para facilitar o seu dia-a-dia como desenvolvedor Sun, 30 Jun 2024 22:42:57 +0000 pt-BR hourly 1 https://wordpress.org/?v=6.9.4 71563333 Como melhorar a velocidade do phpcs e do phpcbf https://fjorgemota.com/2024/06/30/como-melhorar-a-velocidade-do-phpcs-e-do-phpcbf/ https://fjorgemota.com/2024/06/30/como-melhorar-a-velocidade-do-phpcs-e-do-phpcbf/#respond Sun, 30 Jun 2024 22:33:13 +0000 https://fjorgemota.com/?p=6871

Uma tarefa comum quando você está desenvolvendo um projeto é rodar um linter. Linters são ferramentas que analisam o código fonte do seu projeto e buscam detectar diferentes problemas no código, incluindo possíveis falhas na lógica e também questões relacionadas ao estilo do código. Porém, se tem uma coisa que incomoda MUITO é quando você está desenvolvendo, QUASE finalizando o software, e precisa ESPERAR para que ferramentas de verificação assim (que incluem tanto linters quanto testes, que são importantes para qualquer projeto) assim rodem.

Esse é o clássico problema que o XKCD retratou extremamente bem no post "Compiling":

Tirinha do XKCD que traduz livremente para: A desculpa número 1 dos programadores para procrastinar legitimamente: 'meu código está compilando'

E isso incomoda AINDA MAIS quando a ferramenta em questão é single-thread ou mal otimizada, pois o seu computador (que assumo ter um processador com múltiplos núcleos hoje em dia, certo?), fica mais ou menos assim:

Imagem de vários trabalhadores ao redor de uma obra, com só um dos trabalhadores de fato trabalhando em um buraco. Fazendo analogia ao uso de 100% de apenas um núcleo só na maior parte das atividades cotidianas em um computador.

Bom. Agora vamos ao tópico do post: Já que é bem difícil simplesmente paralelizar o trabalho de uma ferramenta de forma eficiente, vamos tentar tornar a sua atuação mais inteligente: Uma forma de deixar o trabalho de qualquer script - ou qualquer coisa, na verdade - mais eficiente, é fazer com que tal script (ou coisa) faça MENOS trabalho.

No caso de um linter, uma forma bem fácil de fazer MENOS trabalho é simplesmente... não ficar rechecando arquivos que não são necessários. Isso é MUITO mais eficiente do que simplesmente paralelizar o algoritmo, uma vez que mesmo o número de núcleos em um computador moderno é limitado.

Para exemplificar isso, considere um projeto que tenha, digamos, 5000 arquivos para serem analisados, cada um levando aí algo em torno de 0.04 segundos. Um linter rodando sequencialmente levaria, portanto, 200 segundos (ou 3 minutos e 20 segundos) para analisar tudo.

Se você tem um processador com 8 núcleos e 16 threads no seu computador, normalmente um algoritmo paralelo tentaria disparar 16 threads para processar cada arquivo. Digamos que essas 16 threads consigam acelerar em 16 vezes a velocidade de processamento (algo que nunca acontece pois há diversas limitações que impedem que algo assim aconteça). Em vez de demorar 200 segundos, ou 3 minutos e 20 segundos, tal algoritmo paralelo levaria cerca de 12 segundos para processar todos os 5000 arquivos.

Levar 12 segundos em vez de 3 minutos e 20 segundos é uma vitória tremenda, certo? Porém, considere que você NÃO vai modificar todos os 5000 arquivos a todo momento. Pelo contrário, você vai modificar ali 1, 3, 5 arquivos por vez, enquanto trabalha no seu projeto. E é aqui que mesmo a paralelização deixa de fazer sentido: Para que checar todos os arquivos, se apenas alguns poucos foram modificados? Se apenas 5 arquivos foram modificados, e o linter leva 0.04 segundos para processar um arquivo, apenas 0.04 * 5 = 0.2 segundos (ou MENOS de um segundo!) deveriam ser necessários para processar tudo, o que já é 60 vezes mais rápido que o algoritmo paralelo ou 1000 vezes mais rápido que o algoritmo sequencial.

E é aqui que entra o script que é o assunto desse post.

Esse é o script, em bash:

#!/bin/bash

set -e

SCRIPT_NAME=$(basename "$0");

find_script() {
    script_name="$1"
    current_dir=$(pwd)
    parent_dir=$(dirname "$current_dir")
    
    # Check if the path exists in the directory exists in the current directory
    script_path="$current_dir/vendor/bin/$script_name"
    if [ -x "$script_path" ]; then
        echo "$script_path"
        return
    fi
    
    # Traverse up the directory tree and check if the script exists in any parent directory
    while [ "$parent_dir" != "$current_dir" ]; do
        script_path="$parent_dir/vendor/bin/$script_name"
        if [ -x "$script_path" ]; then
            echo "$script_path"
            return
        fi
        
        current_dir="$parent_dir"
        parent_dir=$(dirname "$current_dir")
    done
    
    # Script not found
    echo "Script $SCRIPT_NAME not found." > /dev/stderr
    exit 1
}


SCRIPT_PATH=$(find_script "$SCRIPT_NAME");

MODIFIED_FILES=$(git status --porcelain=v2 . | awk '{print $NF}');

if [ -z "$MODIFIED_FILES" ]; then
  echo "No modified files found in the current directory."
  exit 0
fi

echo "Running $SCRIPT_PATH for the following list of files:";
echo "$MODIFIED_FILES" | xargs -i{} echo "- {}";

echo "$MODIFIED_FILES" | xargs "$SCRIPT_PATH";

O que o script faz? É bem simples, vou explicar:

  1. Primeiramente, rodamos set -e para fazer com que o bash imediatamente interrompa a execução caso qualquer comando retorne erro (com código de status de saída diferenet de 0)
  2. Definimos uma variável chamada SCRIPT_NAME contendo o nome do arquivo atual, já explico mais abaixo o motivo disso
  3. Definimos uma função find_script para procurar o script. Essa função busca encontrar o script de nome SCRIPT_NAME dentro da pasta vendor/bin, e busca tanto no diretório atual quanto em qualquer superior. Para exemplificar, se você rodar o arquivo dentro de uma pasta /home/usuario/projetos/MeuProjeto, ele procurará um arquivo executável com o nome do script salvo em SCRIPT_NAME (digamos que tal nome seja phpcs) nas seguintes pastas:
    • /home/usuario/projetos/MeuProjeto/vendor/bin/phpcs
    • /home/usuario/projetos/vendor/bin/phpcs
    • /home/usuario/vendor/bin/phpcs
    • /home/vendor/bin/phpcs
    • /vendor/bin/phpcs
  4. Definimos uma variável SCRIPT_PATH contendo o caminho para o script encontrado pela função find_script
  5. Computamos uma lista de arquivos modificados a partir do git. Apenas arquivos modificados cujas mudanças não foram comittados ainda aparecerão aqui
  6. Verificamos se a lista de arquivos modificados possui, de fato, arquivos modificados. Se não houver arquivos modificados mostramos uma mensagem e encerramos o script
  7. Mostramos a lista de arquivos modificados, e passamos essa lista de arquivos modificados para o script em questão, como parâmetros.

Para usar esse script em uma computador Linux, basta você

  1. Criar uma pasta bin na sua $HOME se você ainda não tiver, assim: mkdir -p $HOME/bin
  2. Adicionar essa pasta bin no seu environment, adicionando no arquivo de configuração do seu shell (digamos, .bashrc) algo como export PATH=$PATH:$HOME/bin
  3. Adicionar esse script nessa pasta bin, com o nome da ferramenta que você quer otimizar. Você pode nomeá-lo como phpcs ou phpcbf, por exemplo
  4. Em um projeto PHP com o phpcbf ou phpcs instalado via Composer, executar o comando correspondente SEM usar vendor/bin como prefixo
  5. Pronto!

O comando só rodará de fato a ferramenta em questão SE houver arquivos modificados e não comittados ainda no repositório Git.

Também não executará se a ferramenta em questão não estiver instalada usando Composer, que já apresentei me outro post.

Finalmente, se você não estiver usando Git, o script não executará, pois ele não tem como rastrear quais arquivos modificados. Se você ainda não usa Git, verifque o meu post sobre, também. ;)

E você, tem alguma outra dica para agilizar tarefas demoradas - mas necessárias - do seu dia-a-dia? Compartilhe nos comentários! E se ficou alguma dúvida, pode madnar nos comentários que vou tentar fazer o meu melhor para ajudar, também. :D

Valeu pela leitura!

]]>
Uma tarefa comum quando você está desenvolvendo um projeto é rodar um linter. Linters são ferramentas que analisam o código fonte do seu projeto e buscam detectar diferentes problemas no código, incluindo possíveis falhas na lógica e também questões relacionadas ao estilo do código. Porém, se tem uma coisa que incomoda MUITO é quando você está desenvolvendo, QUASE finalizando o software, e precisa ESPERAR para que ferramentas de verificação assim (que incluem tanto linters quanto testes, que são importantes para qualquer projeto) assim rodem.

Esse é o clássico problema que o XKCD retratou extremamente bem no post "Compiling":

Tirinha do XKCD que traduz livremente para: A desculpa número 1 dos programadores para procrastinar legitimamente: 'meu código está compilando'

E isso incomoda AINDA MAIS quando a ferramenta em questão é single-thread ou mal otimizada, pois o seu computador (que assumo ter um processador com múltiplos núcleos hoje em dia, certo?), fica mais ou menos assim:

Imagem de vários trabalhadores ao redor de uma obra, com só um dos trabalhadores de fato trabalhando em um buraco. Fazendo analogia ao uso de 100% de apenas um núcleo só na maior parte das atividades cotidianas em um computador.

Bom. Agora vamos ao tópico do post: Já que é bem difícil simplesmente paralelizar o trabalho de uma ferramenta de forma eficiente, vamos tentar tornar a sua atuação mais inteligente: Uma forma de deixar o trabalho de qualquer script - ou qualquer coisa, na verdade - mais eficiente, é fazer com que tal script (ou coisa) faça MENOS trabalho.

No caso de um linter, uma forma bem fácil de fazer MENOS trabalho é simplesmente... não ficar rechecando arquivos que não são necessários. Isso é MUITO mais eficiente do que simplesmente paralelizar o algoritmo, uma vez que mesmo o número de núcleos em um computador moderno é limitado.

Para exemplificar isso, considere um projeto que tenha, digamos, 5000 arquivos para serem analisados, cada um levando aí algo em torno de 0.04 segundos. Um linter rodando sequencialmente levaria, portanto, 200 segundos (ou 3 minutos e 20 segundos) para analisar tudo.

Se você tem um processador com 8 núcleos e 16 threads no seu computador, normalmente um algoritmo paralelo tentaria disparar 16 threads para processar cada arquivo. Digamos que essas 16 threads consigam acelerar em 16 vezes a velocidade de processamento (algo que nunca acontece pois há diversas limitações que impedem que algo assim aconteça). Em vez de demorar 200 segundos, ou 3 minutos e 20 segundos, tal algoritmo paralelo levaria cerca de 12 segundos para processar todos os 5000 arquivos.

Levar 12 segundos em vez de 3 minutos e 20 segundos é uma vitória tremenda, certo? Porém, considere que você NÃO vai modificar todos os 5000 arquivos a todo momento. Pelo contrário, você vai modificar ali 1, 3, 5 arquivos por vez, enquanto trabalha no seu projeto. E é aqui que mesmo a paralelização deixa de fazer sentido: Para que checar todos os arquivos, se apenas alguns poucos foram modificados? Se apenas 5 arquivos foram modificados, e o linter leva 0.04 segundos para processar um arquivo, apenas 0.04 * 5 = 0.2 segundos (ou MENOS de um segundo!) deveriam ser necessários para processar tudo, o que já é 60 vezes mais rápido que o algoritmo paralelo ou 1000 vezes mais rápido que o algoritmo sequencial.

E é aqui que entra o script que é o assunto desse post.

Esse é o script, em bash:

#!/bin/bash

set -e

SCRIPT_NAME=$(basename "$0");

find_script() {
    script_name="$1"
    current_dir=$(pwd)
    parent_dir=$(dirname "$current_dir")
    
    # Check if the path exists in the directory exists in the current directory
    script_path="$current_dir/vendor/bin/$script_name"
    if [ -x "$script_path" ]; then
        echo "$script_path"
        return
    fi
    
    # Traverse up the directory tree and check if the script exists in any parent directory
    while [ "$parent_dir" != "$current_dir" ]; do
        script_path="$parent_dir/vendor/bin/$script_name"
        if [ -x "$script_path" ]; then
            echo "$script_path"
            return
        fi
        
        current_dir="$parent_dir"
        parent_dir=$(dirname "$current_dir")
    done
    
    # Script not found
    echo "Script $SCRIPT_NAME not found." > /dev/stderr
    exit 1
}


SCRIPT_PATH=$(find_script "$SCRIPT_NAME");

MODIFIED_FILES=$(git status --porcelain=v2 . | awk '{print $NF}');

if [ -z "$MODIFIED_FILES" ]; then
  echo "No modified files found in the current directory."
  exit 0
fi

echo "Running $SCRIPT_PATH for the following list of files:";
echo "$MODIFIED_FILES" | xargs -i{} echo "- {}";

echo "$MODIFIED_FILES" | xargs "$SCRIPT_PATH";

O que o script faz? É bem simples, vou explicar:

  1. Primeiramente, rodamos set -e para fazer com que o bash imediatamente interrompa a execução caso qualquer comando retorne erro (com código de status de saída diferenet de 0)
  2. Definimos uma variável chamada SCRIPT_NAME contendo o nome do arquivo atual, já explico mais abaixo o motivo disso
  3. Definimos uma função find_script para procurar o script. Essa função busca encontrar o script de nome SCRIPT_NAME dentro da pasta vendor/bin, e busca tanto no diretório atual quanto em qualquer superior. Para exemplificar, se você rodar o arquivo dentro de uma pasta /home/usuario/projetos/MeuProjeto, ele procurará um arquivo executável com o nome do script salvo em SCRIPT_NAME (digamos que tal nome seja phpcs) nas seguintes pastas:
    • /home/usuario/projetos/MeuProjeto/vendor/bin/phpcs
    • /home/usuario/projetos/vendor/bin/phpcs
    • /home/usuario/vendor/bin/phpcs
    • /home/vendor/bin/phpcs
    • /vendor/bin/phpcs
  4. Definimos uma variável SCRIPT_PATH contendo o caminho para o script encontrado pela função find_script
  5. Computamos uma lista de arquivos modificados a partir do git. Apenas arquivos modificados cujas mudanças não foram comittados ainda aparecerão aqui
  6. Verificamos se a lista de arquivos modificados possui, de fato, arquivos modificados. Se não houver arquivos modificados mostramos uma mensagem e encerramos o script
  7. Mostramos a lista de arquivos modificados, e passamos essa lista de arquivos modificados para o script em questão, como parâmetros.

Para usar esse script em uma computador Linux, basta você

  1. Criar uma pasta bin na sua $HOME se você ainda não tiver, assim: mkdir -p $HOME/bin
  2. Adicionar essa pasta bin no seu environment, adicionando no arquivo de configuração do seu shell (digamos, .bashrc) algo como export PATH=$PATH:$HOME/bin
  3. Adicionar esse script nessa pasta bin, com o nome da ferramenta que você quer otimizar. Você pode nomeá-lo como phpcs ou phpcbf, por exemplo
  4. Em um projeto PHP com o phpcbf ou phpcs instalado via Composer, executar o comando correspondente SEM usar vendor/bin como prefixo
  5. Pronto!

O comando só rodará de fato a ferramenta em questão SE houver arquivos modificados e não comittados ainda no repositório Git.

Também não executará se a ferramenta em questão não estiver instalada usando Composer, que já apresentei me outro post.

Finalmente, se você não estiver usando Git, o script não executará, pois ele não tem como rastrear quais arquivos modificados. Se você ainda não usa Git, verifque o meu post sobre, também. 😉

E você, tem alguma outra dica para agilizar tarefas demoradas - mas necessárias - do seu dia-a-dia? Compartilhe nos comentários! E se ficou alguma dúvida, pode madnar nos comentários que vou tentar fazer o meu melhor para ajudar, também. 😀

Valeu pela leitura!

]]>
https://fjorgemota.com/2024/06/30/como-melhorar-a-velocidade-do-phpcs-e-do-phpcbf/feed/ 0 6871
Uma simples dica para economizar tempo ao atualizar pacotes do AUR no Arch Linux https://fjorgemota.com/2023/12/28/uma-simples-dica-para-economizar-tempo-ao-atualizar-pacotes-do-aur-no-arch-linux/ https://fjorgemota.com/2023/12/28/uma-simples-dica-para-economizar-tempo-ao-atualizar-pacotes-do-aur-no-arch-linux/#respond Thu, 28 Dec 2023 17:34:57 +0000 https://fjorgemota.com/?p=7656
Imagem gerada pelo Dall-E representando um usuário mexendo em um computador com um relógio na parede

Como usuário da excelente distribuição Arch Linux (BTW, I use Arch), eu só gostaria de fazer aqui um breve lembrete para todos vocês: Antes de você gastar bastante tempo compilando um pacote gigante do AUR (ou Arch User Repository), como por exemplo o qt5-webkit, vale checar se você realmente PRECISA desse pacote OU se ele é dependência para algum outro pacote instalado na sua máquina.

No Arch Linux, você pode checar se outro pacote depende do pacote em questão através do pactree, que é um script disponível no pacote pacman-contrib. Primeiro, instale o pacman-contrib:

sudo pacman -S pacman-contrib

Depois, execute o seguinte comando, sendo pacote o pacote que você quer verificar se é usado de alguma forma:

sudo pactree -r pacote

No caso do qt5-webkit, por exemplo, você pode usar:

sudo pactree -r qt5-webkit

Nesse comando, o -r significa reverse, ou seja, ele monta uma árvore ASCII de pacotes que DEPENDEM do pacote que você especificou no parâmetro. Se nenhum outro pacote no seu computador tiver o pacote que você informou como dependência, apenas o nome do pacote que você informou aparecerá na tela (como "raiz" da árvore).

Claro que, se o pacote informado for de algum programa que você use, como o MySQL ou o Google Chrome, por exemplo, é provável que nenhum outro pacote aparecerá para esse comando, afinal, nenhum outro pacote instalado no seu computador depende dele.

Quando encontrar um pacote do AUR que de fato não faz sentido gastar tempo para atualizar, você pode simplesmente...removê-lo. Esse é o comando para fazer isso, e, novamente, pacote é o pacote que você quer remover:

sudo pacman -Rns pacote

No caso do qt5-webkit, o comando fica assim:

sudo pacman -Rns qt5-webkit

Note que esse o parâmetro -Rns aqui vai remover não somente o pacote em questão mas também todas as suas dependências não utilizadas (o que pode muito bem incluir outros pacotes grandes do AUR..).

"Mas, Fernando, por qual motivo tantas menções para o qt5-webkit?!". Bom, nada em especial, é só o fato de que o gênio que vos escreve aqui gastou mais de uma hora essa semana esperando a compilação desse pacote só para finalmente descobrir que.. esse pacote não tava sendo utilizado por nenhuma outra dependência do PC. Ou seja, literalmente desperdicei tempo a toa. 🙃

Um último detalhe...

Agora, para você que leu até aqui (obrigado!), uma dúvida: Qual a sua opinião sobre textos curtos assim? Eu quero muito voltar a escrever mais para esse blog, e inclusive até comecei a preparar uma versão em inglês (!!!), mas para a surpresa de um total de zero pessoas a vida adulta é bem...caótica, e artigos de sucesso como o que escrevi anos atrás sobre o Docker (que até hoje recebe um razoável número de visitas) levam MUITO tempo para serem propriamente escrito, testados e revisados. Eu gosto da ideia de trazer dicas e comentários simples assim em posts concisos, entretanto, mas queria saber: qual a sua opinião sobre isso? Deixe nos comentários! :)

Ah, e feliz ano novo, pessoal! :D

]]>
Imagem gerada pelo Dall-E representando um usuário mexendo em um computador com um relógio na parede

Como usuário da excelente distribuição Arch Linux (BTW, I use Arch), eu só gostaria de fazer aqui um breve lembrete para todos vocês: Antes de você gastar bastante tempo compilando um pacote gigante do AUR (ou Arch User Repository), como por exemplo o qt5-webkit, vale checar se você realmente PRECISA desse pacote OU se ele é dependência para algum outro pacote instalado na sua máquina.

No Arch Linux, você pode checar se outro pacote depende do pacote em questão através do pactree, que é um script disponível no pacote pacman-contrib. Primeiro, instale o pacman-contrib:

sudo pacman -S pacman-contrib

Depois, execute o seguinte comando, sendo pacote o pacote que você quer verificar se é usado de alguma forma:

sudo pactree -r pacote

No caso do qt5-webkit, por exemplo, você pode usar:

sudo pactree -r qt5-webkit

Nesse comando, o -r significa reverse, ou seja, ele monta uma árvore ASCII de pacotes que DEPENDEM do pacote que você especificou no parâmetro. Se nenhum outro pacote no seu computador tiver o pacote que você informou como dependência, apenas o nome do pacote que você informou aparecerá na tela (como "raiz" da árvore).

Claro que, se o pacote informado for de algum programa que você use, como o MySQL ou o Google Chrome, por exemplo, é provável que nenhum outro pacote aparecerá para esse comando, afinal, nenhum outro pacote instalado no seu computador depende dele.

Quando encontrar um pacote do AUR que de fato não faz sentido gastar tempo para atualizar, você pode simplesmente...removê-lo. Esse é o comando para fazer isso, e, novamente, pacote é o pacote que você quer remover:

sudo pacman -Rns pacote

No caso do qt5-webkit, o comando fica assim:

sudo pacman -Rns qt5-webkit

Note que esse o parâmetro -Rns aqui vai remover não somente o pacote em questão mas também todas as suas dependências não utilizadas (o que pode muito bem incluir outros pacotes grandes do AUR..).

"Mas, Fernando, por qual motivo tantas menções para o qt5-webkit?!". Bom, nada em especial, é só o fato de que o gênio que vos escreve aqui gastou mais de uma hora essa semana esperando a compilação desse pacote só para finalmente descobrir que.. esse pacote não tava sendo utilizado por nenhuma outra dependência do PC. Ou seja, literalmente desperdicei tempo a toa. 🙃

Um último detalhe...

Agora, para você que leu até aqui (obrigado!), uma dúvida: Qual a sua opinião sobre textos curtos assim? Eu quero muito voltar a escrever mais para esse blog, e inclusive até comecei a preparar uma versão em inglês (!!!), mas para a surpresa de um total de zero pessoas a vida adulta é bem...caótica, e artigos de sucesso como o que escrevi anos atrás sobre o Docker (que até hoje recebe um razoável número de visitas) levam MUITO tempo para serem propriamente escrito, testados e revisados. Eu gosto da ideia de trazer dicas e comentários simples assim em posts concisos, entretanto, mas queria saber: qual a sua opinião sobre isso? Deixe nos comentários! 🙂

Ah, e feliz ano novo, pessoal! 😀

]]>
https://fjorgemota.com/2023/12/28/uma-simples-dica-para-economizar-tempo-ao-atualizar-pacotes-do-aur-no-arch-linux/feed/ 0 7656
Como reduzir o tamanho de vídeos no Linux com ffmpeg https://fjorgemota.com/2023/04/09/como-reduzir-o-tamanho-de-videos-no-linux-com-ffmpeg/ https://fjorgemota.com/2023/04/09/como-reduzir-o-tamanho-de-videos-no-linux-com-ffmpeg/#respond Mon, 10 Apr 2023 01:03:53 +0000 https://fjorgemota.com/?p=6486

Sabe quando você tem um vídeo e você quer compartilhar na internet? Pois então, hoje, na maior parte dos casos, isso não é um problema em termos de plataforma, visto que muitas plataformas aceitam vídeos sem problema nenhum. O problema, entretanto, é a velocidade de upload da maior parte das conexões de internet aqui no Brasil: Aqui, apesar de assinar uma conexão com centenas de Mbps, por exemplo, eu possuo apenas 25 Mbps de upload. Não que hoje seja MUITO demorado quando você compara com, por exemplo, a conexão discada (famosos 56 Kbps que na verdade se traduziam em apenas 8 Kbps em dias em que a conexão estava boa), mas, no dia a dia moderno, o upload de um vídeo qualquer pode levar...bastante tempo.

Logo, a possibilidade de otimizar o vídeo de forma a reduzir o seu tamanho enquanto mantendo uma boa qualidade acaba sendo bem atraente. É dificil dizer em números exatos, mas, na minha experiência, recodificar um vídeo localmente e depois enviar acaba tomando bem menos tempo do que só..enviar, daí a razão por trás desse post.

Antes de finalmente apresentar o script, um pequeno aviso:

Os vídeo ao qual me refiro nesse post são, majoritariamente, gravações de tela, que são muito úteis para mostrar um recurso em desenvolvimento, ou então documentar um bug. Nesses vídeos, pode haver um pequeno bloco no qual a imagem da sua webcam aparece, mas os comandos aqui apresentados não são necessariamente otimizados para um vídeo contendo SOMENTE isso. Em virtude disso, o framerate do vídeo é reduzido para apenas 30 FPS, o que reforça a intenção do script de ser mais otimizado para uso em vídeos em que muitos quadros acabam por serem estáticos.

Segue o script, que você pode salvar como prepare-video no seu computador:

#!/bin/bash
ffmpeg -i "$1" -crf 27 -preset veryfast -movflags +faststart -r 30 -vcodec libx264 -acodec aac "$2"

Antes de executar, lembre-se de dar permissões de execução (chmod +x prepare-video) para o comando em questão. A partir daí, você pode usar o seguinte comando para converter vídeos:

prepare-video video-de-entrada.avi video-de-saida.mp4

Substituindo video-de-entrada.avi pelo vídeo que você quer converter e video-de-saida.mp4 pelo arquivo para o qual o vídeo convertido deve ser salvo.

Esse é o script que eu tenho usado há anos, só que com o nome convert-to-whatsapp, pois foi o motivador inicial para criar tal script: O WhatsApp é CHATO no suporte a vídeos, e esse script é quem me permitia codificar um vídeo que fosse suportado pelo WhatsApp nativamente.

Note que um grande problema desse script é que ele roda todo o processo de decodificação e codificação no processador. Em um processador moderna, com 8 núcleos, isso normalmente não é um grande problema, mas em processadores mais antigos isso pode ser um problema.

Movendo a carga para a placa de vídeo

Como documentado acima, esse é o script que eu uso já há alguns bons anos. Entretanto, como tudo na vida, sempre há espaço pra melhorias. Recentemente, eu fiz uma pequena melhoria no meu computador: troquei a minha placa de vídeo, que antes era uma AMD RX 580 8GB, por uma NVIDIA RTX 4090. Em virtude disso, a possibilidade de usar a placa de vídeo para fazer todo o processo de decodificação e codificação se tornou mais...real, uma vez que infelizmente a AMD é bem fraca nesse aspecto (mesmo nas últimas gerações de placa de vídeo).

Se você tem uma placa de vídeo da NVIDIA com suporte a NVENC, uma possibilidade é usar esse comando aqui para conseguir resultados similares à versão com processador, conforme gerado pelo ChatGPT (a ideia é que esse comando seja equivalente ao anterior):

#!/bin/bash
ffmpeg -hwaccel cuvid -i "$1" -c:v h264_nvenc -preset:v fast -rc:v vbr -cq:v 19 -profile:v high -level:v 4.2 -b:v 1200k -maxrate:v 2400k -bufsize:v 2400k -c:a aac -b:a 128k -movflags +faststart -r 30 "$2"

A parte curiosa? Apesar de ser um pouco mais rápida do que a versão que roda somente em processador, esta versão gera arquivos MUITO maiores. Segue uma tabela comparando os resultados:

Configuração do CodificadorTamanho final do arquivoTempo para realizar a conversão
Arquivo Original1.1 GB-
Usando somente o processador106 MB1 minuto e 50 segundos
Usando somente a placa de vídeo293 MB1 minuto e 34 segundos

Para fins de documentação, o meu processador é um AMD Ryzen 7 5800X3D, um dos processadores mais poderosos da atualidade...para jogos.

Aí você me pergunta: Tá, mas pra quê eu vou utilizar a versão baseada em GPU, então? Bom, eu não faço ideia, ¯\_(ツ)_/¯. Ao meu ver, a maior vantagem de usar o codificador baseado em GPU é que você acaba movendo para a GPU toda a carga de processamento, o que é bem útil em algumas situações.

Outras alternativa usando somente o processador

De acordo com o ChatGPT, uma outra boa alternativa - ainda usando somente o processador - para codificar os vídeos com o propósito desse post, é o seguinte script:

#!/bin/bash
ffmpeg -y -i "$1" -c:v libx264 -preset medium -crf 28 -r 30 -c:a aac -b:a 192k -movflags +faststart "$2"

Fica a seu critério qual utilizar. Pelos meus testes, esse comando provê muda algumas configurações para ter um pouco mais de qualidade de imagem, MAS naturalmente isso aumenta um pouco o tamanho do arquivo. Além disso, o comando demora mais para executar, veja a tabela abaixo:

Configuração do CodificadorTamanho final do arquivoTempo para realizar a conversão
Arquivo Original1.1 GB-
Meu script106 MB1 minuto e 50 segundos
Script do ChatGPT128 MB2 minutos e 26 segundos

Se você valoriza mais qualidade e não está tão preocupado em ter a execução mais demorada nem o tamanho ligeiramente maior, essa pode ser definitivamente uma opção.

Conclusão

O ffmpeg é uma ferramenta extremamente poderosa para esse tipo de manipulação de vídeo, e esse tipo de script, em torno de ferramentas assim, ajuda MUITO a facilitar o uso de tarefas cotidianas no computador. Hoje em dia, eu não gravo tantos vídeos quanto gostaria, mas ainda assim esse script se torna muito útil para compartilhar vídeos na internet, em especial considerando os limites da velocidade de upload do plano de internet aqui de casa.

E você, grava vídeos para a internet? Tem algum script maneiro para compartilhar? Deixe nos comentários! :D

]]>
Sabe quando você tem um vídeo e você quer compartilhar na internet? Pois então, hoje, na maior parte dos casos, isso não é um problema em termos de plataforma, visto que muitas plataformas aceitam vídeos sem problema nenhum. O problema, entretanto, é a velocidade de upload da maior parte das conexões de internet aqui no Brasil: Aqui, apesar de assinar uma conexão com centenas de Mbps, por exemplo, eu possuo apenas 25 Mbps de upload. Não que hoje seja MUITO demorado quando você compara com, por exemplo, a conexão discada (famosos 56 Kbps que na verdade se traduziam em apenas 8 Kbps em dias em que a conexão estava boa), mas, no dia a dia moderno, o upload de um vídeo qualquer pode levar...bastante tempo.

Logo, a possibilidade de otimizar o vídeo de forma a reduzir o seu tamanho enquanto mantendo uma boa qualidade acaba sendo bem atraente. É dificil dizer em números exatos, mas, na minha experiência, recodificar um vídeo localmente e depois enviar acaba tomando bem menos tempo do que só..enviar, daí a razão por trás desse post.

Antes de finalmente apresentar o script, um pequeno aviso:

Os vídeo ao qual me refiro nesse post são, majoritariamente, gravações de tela, que são muito úteis para mostrar um recurso em desenvolvimento, ou então documentar um bug. Nesses vídeos, pode haver um pequeno bloco no qual a imagem da sua webcam aparece, mas os comandos aqui apresentados não são necessariamente otimizados para um vídeo contendo SOMENTE isso. Em virtude disso, o framerate do vídeo é reduzido para apenas 30 FPS, o que reforça a intenção do script de ser mais otimizado para uso em vídeos em que muitos quadros acabam por serem estáticos.

Segue o script, que você pode salvar como prepare-video no seu computador:

#!/bin/bash
ffmpeg -i "$1" -crf 27 -preset veryfast -movflags +faststart -r 30 -vcodec libx264 -acodec aac "$2"

Antes de executar, lembre-se de dar permissões de execução (chmod +x prepare-video) para o comando em questão. A partir daí, você pode usar o seguinte comando para converter vídeos:

prepare-video video-de-entrada.avi video-de-saida.mp4

Substituindo video-de-entrada.avi pelo vídeo que você quer converter e video-de-saida.mp4 pelo arquivo para o qual o vídeo convertido deve ser salvo.

Esse é o script que eu tenho usado há anos, só que com o nome convert-to-whatsapp, pois foi o motivador inicial para criar tal script: O WhatsApp é CHATO no suporte a vídeos, e esse script é quem me permitia codificar um vídeo que fosse suportado pelo WhatsApp nativamente.

Note que um grande problema desse script é que ele roda todo o processo de decodificação e codificação no processador. Em um processador moderna, com 8 núcleos, isso normalmente não é um grande problema, mas em processadores mais antigos isso pode ser um problema.

Movendo a carga para a placa de vídeo

Como documentado acima, esse é o script que eu uso já há alguns bons anos. Entretanto, como tudo na vida, sempre há espaço pra melhorias. Recentemente, eu fiz uma pequena melhoria no meu computador: troquei a minha placa de vídeo, que antes era uma AMD RX 580 8GB, por uma NVIDIA RTX 4090. Em virtude disso, a possibilidade de usar a placa de vídeo para fazer todo o processo de decodificação e codificação se tornou mais...real, uma vez que infelizmente a AMD é bem fraca nesse aspecto (mesmo nas últimas gerações de placa de vídeo).

Se você tem uma placa de vídeo da NVIDIA com suporte a NVENC, uma possibilidade é usar esse comando aqui para conseguir resultados similares à versão com processador, conforme gerado pelo ChatGPT (a ideia é que esse comando seja equivalente ao anterior):

#!/bin/bash
ffmpeg -hwaccel cuvid -i "$1" -c:v h264_nvenc -preset:v fast -rc:v vbr -cq:v 19 -profile:v high -level:v 4.2 -b:v 1200k -maxrate:v 2400k -bufsize:v 2400k -c:a aac -b:a 128k -movflags +faststart -r 30 "$2"

A parte curiosa? Apesar de ser um pouco mais rápida do que a versão que roda somente em processador, esta versão gera arquivos MUITO maiores. Segue uma tabela comparando os resultados:

Configuração do CodificadorTamanho final do arquivoTempo para realizar a conversão
Arquivo Original1.1 GB-
Usando somente o processador106 MB1 minuto e 50 segundos
Usando somente a placa de vídeo293 MB1 minuto e 34 segundos

Para fins de documentação, o meu processador é um AMD Ryzen 7 5800X3D, um dos processadores mais poderosos da atualidade...para jogos.

Aí você me pergunta: Tá, mas pra quê eu vou utilizar a versão baseada em GPU, então? Bom, eu não faço ideia, ¯\_(ツ)_/¯. Ao meu ver, a maior vantagem de usar o codificador baseado em GPU é que você acaba movendo para a GPU toda a carga de processamento, o que é bem útil em algumas situações.

Outras alternativa usando somente o processador

De acordo com o ChatGPT, uma outra boa alternativa - ainda usando somente o processador - para codificar os vídeos com o propósito desse post, é o seguinte script:

#!/bin/bash
ffmpeg -y -i "$1" -c:v libx264 -preset medium -crf 28 -r 30 -c:a aac -b:a 192k -movflags +faststart "$2"

Fica a seu critério qual utilizar. Pelos meus testes, esse comando provê muda algumas configurações para ter um pouco mais de qualidade de imagem, MAS naturalmente isso aumenta um pouco o tamanho do arquivo. Além disso, o comando demora mais para executar, veja a tabela abaixo:

Configuração do CodificadorTamanho final do arquivoTempo para realizar a conversão
Arquivo Original1.1 GB-
Meu script106 MB1 minuto e 50 segundos
Script do ChatGPT128 MB2 minutos e 26 segundos

Se você valoriza mais qualidade e não está tão preocupado em ter a execução mais demorada nem o tamanho ligeiramente maior, essa pode ser definitivamente uma opção.

Conclusão

O ffmpeg é uma ferramenta extremamente poderosa para esse tipo de manipulação de vídeo, e esse tipo de script, em torno de ferramentas assim, ajuda MUITO a facilitar o uso de tarefas cotidianas no computador. Hoje em dia, eu não gravo tantos vídeos quanto gostaria, mas ainda assim esse script se torna muito útil para compartilhar vídeos na internet, em especial considerando os limites da velocidade de upload do plano de internet aqui de casa.

E você, grava vídeos para a internet? Tem algum script maneiro para compartilhar? Deixe nos comentários! 😀

]]>
https://fjorgemota.com/2023/04/09/como-reduzir-o-tamanho-de-videos-no-linux-com-ffmpeg/feed/ 0 6486
SQLite - Um mecanismo de banco de dados incrivelmente versátil https://fjorgemota.com/2023/03/23/sqlite-um-mecanismo-de-banco-de-dados-incrivelmente-versatil/ https://fjorgemota.com/2023/03/23/sqlite-um-mecanismo-de-banco-de-dados-incrivelmente-versatil/#respond Fri, 24 Mar 2023 01:00:00 +0000 https://fjorgemota.com/?p=3951
Logo do SQLite
Logo do SQLite

Durante o desenvolvimento de uma aplicação, seja ela web, desktop ou mesmo mobile, sempre aparece algumas perguntas...complexas. Que linguagem de programação usar? Framework? Quais plataformas focar? E mais importante ainda: Onde armazenar os dados da aplicação?

Naturalmente, as resposta vão variar bastante de aplicação para aplicação. Não existe sequer uma aplicação em que eu trabalhei na vida em que a resposta foi simplesmente "evidentemente vai ser X, pois sim", embora com o passar dos anos a experiência e o conhecimento sobre diferentes soluções ajude na decisão por escolhas melhores e mais apropriadas para cada contexto.

E hoje, depois de anos sem escrever nada por aqui, eu vou falar um pouco sobre uma forma de armazenar e consultar dados que é bem interessante, mas quase nunca considerada no desenvolvimento de uma aplicação moderna, embora seja literalmente o mecanismo de banco de dados mais usado do mundo (sim, eu sei que isso é bastante contra intuitivo, mas eu já vou explicar mais sobre): o SQLite.

O que é

O SQLite é uma biblioteca desenvolvida na linguagem C por D. Richard Hipp lançada em 17 de agosto de 2000 enquanto Richard trabalhava para a General Dynamics em um contrato com a marinha dos Estados Unidos.

A ideia principal da biblioteca é bem simples: Prover uma forma de armazenar e consultar dados de forma confiável, com o uso de SQL, mas sem precisar dos complicados processos de instalação e manutenção que um sistema de gerenciamento de banco de dados (SGBDs) necessita, mas sem deixar a desejar muito em recursos, ao mesmo tempo que evita a necessidade de um administrador de banco de dados (ou DBA, como também é conhecido).

Apesar disso, e de conter uma série de recursos que normalmente são encontrados em SGBDs - como suporte a múltiplas tabelas, views, índices (até mesmo full-text!) e até mesmo triggers - o SQLite não compete contra esses sistemas gerenciadores de bancos de dados. Em vez disso, o SQLite compete com o....fopen(). Sim, a mesma chamada padrão que permite que você abra e crie arquivos.

Onde é usado

O SQLite é usado em praticamente...todo lugar. Seu navegador, esse que você provavelmente está usando agora para ler este post, usa SQLite. O seu sistema operacional, seja ele o Windows, Linux (nesse caso, muitas vezes, a nível de distribuição) ou Mac, usa SQLite.

Seu smartphone - e muitos dos aplicativos que você tem instalado nele - usam SQLite (o Android e o iOS chegam até mesmo a oferecer o SQLite de forma nativa). Além disso, muitos dispositivos inteligentes hoje, incluindo as Smart TVs e até muitas geladeiras inteligentes (daquelas super caras), chegam a usar SQLite.

Fora da sua casa, existem sistemas multimídias de carros e até mesmo aviões que usam SQLite, e muitas vezes ele é incluído nos ambientes nativos de linguagens de programação....populares, como PHP e Python. Legal, né?

Casos de Uso Úteis

Como você já pode perceber, o SQLite é um mecanismo de bancos de dados versátil, e na prática, você pode usá-lo em praticamente qualquer situação onde você deseja persistir dados.

Claro, isso não significa que seria uma decisão super inteligente fazer um mecanismo de busca com o objetivo de rastrear quase toda a internet como o Google faz apenas em cima de um banco de dados salvo pelo SQLite, ou então, criar um site que vá receber milhares de visitas por segundo, mas...veja: Em que momentos você precisa fazer isso, mesmo?

Por exemplo, quais aplicações precisam trabalhar com uma quantidade tão absurda de dados, enquanto as consulta tão frequentemente por dia? Pois é, não são muitas. Claro, não vou dizer que dá pra contar nos dedos, pois existem bastante situações em que a quantidade de dados é realmente grande, e a necessidade por fazer consultas frequentes também, mas, dá para dizer com tranquilidade que, para uma grande maior parte dos projetos por aí, o SQLite é sim uma opção bastante viável.

Com isso dito, na minha experiência, o SQLite se mostra especialmente valioso na criação de scripts, desses que são criados para processamento de dados. Isso porquê, com o SQLite, você consegue não só organizar os dados em uma estrutura conhecida, como também consultá-los de maneira eficiente. Chega num ponto em que fica tão interessante que você simplesmente consegue parar o script em dado ponto e rodar consultas para verificar se os dados estão sendo salvos da maneira como você esperava. É algo com MUITO potencial.

Uma outra situação em que o SQLite se destaca é no desenvolvimento de protótipos. Afinal, para quê usar um SGBD complexo rodando 24/7 somente para o desenvolvimento de um simples MVP que a principio vai rodar só na sua máquina, por exemplo? Para esses casos, a simplicidade do SQLite ajuda a economizar muito tempo que em outra situação seria gasto instalando e configurando um SGBD como o MySQL ou o PostgreSQL, por exemplo.

Outra situação no qual o SQLite se destaca bastante é para análise de dados, graças ao uso de ferramentas incríveis como o Datasette, que possui um ecossistema rico de plugins afim de ajudar na análise de diferentes tipos de dados e na criação de diferentes visualizações, como dashboards, só para citar um exemplo.

Por fim, uma outra situação no qual o SQLite é normalmente considerado é para o desenvolvimento de testes de integração - onde muitas aplicações acabam precisando se comunicar com o banco de dados afim de poder rodar suas consultas. Honestamente, eu não sugeriria fazer isso a menos que o seu SGBD principal já seja o SQLite, visto que muitas consultas podem acabar funcionando de forma diferente no SQLite vs. no seu SBGD principal. Apesar disso, é algo que ainda é bastante considerado e difundido na internet, logo fica aqui o aviso.

Para entender melhor em que situações vale considerar seu uso, vamos falar um pouco sobre suas vantagens e desvantagens.

Vantagens e Desvantagens

Apesar do fato do SQLite tentar competir mais com o fopen(), sua lista de vantagens e desvantagens na realidade é bem..interessante, em especial se você considerar quão pequeno o SQLite de fato é (a biblioteca compilada pesa menos de 1MB). Nessa seção, vou abordar alguns pontos que podem tanto motivar quanto desmotivar a escolha pelo SQLite.

Vantagens

Existem várias vantagens em usar o SQLite. Naturalmente, mesmo que alguma vantagem lhe pareça extremamente conveniente, isso não significa que ele será a solução absolutamente PERFEITA para o seu caso de uso. Afinal, vale dar uma olhada na lista de desvantagens também, afim de evitar eventuais problemas futuros. =)

Dito isso, vamos às vantagens:

Serverless (não precisa de servidor)

O SQLite é um SGBD que não necessita de configuração e inicialização de servidores. Basicamente, você importa/linka o SQLite no código do seu projeto, chama as funções da API pública para criar/abrir o banco de dados, rodar consultas e afins, e...é isso.

É basicamente como se, de repente, o driver que você usa para conectar ao banco de dados fosse de fato o banco de dados por si só, com latência basicamente zero, e sem a necessidade de fazer mais nada além do que você já está acostumado a fazer, que é chamar a API pública para abrir o banco de dados, rodar as migrações da sua aplicação para preparar a estrutura do banco de dados, e executar consultas.

Naturalmente, isso pode levar a alguns erros que não acho que aconteceriam tão facilmente no caso de um servidor de banco de dados. Se você passar a string ":memory:" como nome do banco de dados que você quer abrir, por exemplo, o SQLite vai abrir um banco de dados somente em memória, que portanto vai ser destruído no momento que a aplicação for encerrada. Da mesma forma, se você passar uma string vazia, o SQLite vai abrir um banco de dados em uma pasta temporária, que também será apagada ao final da execução do programa, mas que não é mais limitada pela quantidade de memória RAM disponível.

Portátil

Como é possível perceber, o SQLite opera manipulando diretamente arquivos locais de banco de dados. Isso significa que ele não exige nenhuma configuração e gerenciamento de portas nem nada do tipo, sendo, portanto, portátil. Se você desenvolve uma aplicação que usa SQLite e que salva o arquivo em um diretório na mesma pasta, por exemplo, você pode fazer operações no banco de dados mesmo de um pendrive, o que mostra o quão portátil é o SQLite enquanto banco de dados.

Confiável

O SQLite é extremamente confiável, ao ponto que consegue ser o mais resiliente possível mesmo para erros de sistema ou de memória. Além disso, também é resistente contra arquivos maliciosamente alterados, e é recomendado pela biblitoeca do congresso dos Estados Unidos para armazenamento de dados a longo prazo.

Inclusive, falando em longo prazo, o time do SQLite planeja suportar o projeto PELO MENOS até 2050. Dado a quantidade enorme e variada de casos de uso do projeto, eu pessoalmente acredito que irá facilmente além dessa marca, mas isso mostra o quão confiável é o SQLite no geral.

Possui amplo suporte à muitos dos recursos encontrados em outros SGBDs relacionais

O SQLite possui suporte à praticamente todos os recursos que você espera de um SGBD convencional:

  • Transações;
  • JOINs;
  • Window Functions - excelentes para analytics;
  • Suporte a variados tipos de Índices, incluindo aí índices geográficos e full-text;
  • Uma grande quantidade de funções para utilização nas consultas;
  • Tabelas virtuais;
  • Sub-Queries;
  • Um planejador de consultas (query planner) bastante avançado e inteligente;

E vários outros recursos dignos de SGBDs avançados, como o PostgreSQL e o MySQL.

Possui um extenso ecossistema

O SQLite possui uma comunidade muito grande, pelo fato de ser um projeto extremamente usado. Isso faz com que o projeto tenha, também, um rico ecossistema, capaz de suprir as mais variadas necessidades dos seus usuários, a partir do uso de projetos que foram criados justamente para...atender problemas de outros usuários.

Uma lista bastante completa dos projetos ao qual me refiro está presente no repositório awesome-sqlite, mas aqui segue alguns que definitivamente chamam minha atenção:

  • RQLite - Para usar o SQLIte com replicação entre diversos servidores, e também de forma mais tradicional, como um servidor à parte;
  • LiteStream - Também para replicação de diferentes bancos de dados, só que agora sem necessidade do uso de um servidor à parte;
  • Spatialite - Para estender o SQLite com habilidades geoespaciais, permitindo consultas capazes de lidar com dados geográficos de maneira muito eficiente;

Desvantagens

Como todo projeto e coisa nesse universo, o SQLite não é perfeito, e também tem sua lista de desvantagens própria, ou seja, não é uma solução que atende a todos os projetos de maneira absolutamente perfeita (se é que isso existe), e portanto é importante estar ciente de todos os possíveis problemas ao usar o projeto.

É mais dificil de escalar horizontalmente se usado em aplicações web

O SQLite, como já explicado nesse post, é basicamente uma biblioteca que se integra diretamente na aplicação, e que trabalha alterando diretamente um arquivo especificado, que é, nesse caso, o próprio arquivo do banco de dados (onde os dados e estruturas são efetivamente salvos).

Como pode-se imaginar, essa característica reduz significantemente a latência envolvida, pois a aplicação não precisa criar a consulta, mandar para um servidor, esperar o servidor processar e então receber a resposta para retornar para o usuário. Entretanto, essa abordagem também limita significantemente a capacidade de escalar horizontalmente de maneira fácil.

O que seria escalar horizontalmente, nesse caso? Simples! Imagine uma aplicação web, como um...blog, por exemplo. Numa arquitetura em que o SGBD é acessado usando o tradicional cliente-servidor, você pode ter vários clientes e um servidor. Ou seja, é possível ter 5, 10 máquinas distintas rodando o Apache/NGINX+PHP/Python/Go/qualquer outra linguagem, e apenas um banco de dados. Isso trás benefícios significativos quando os dados são apenas lidos do SGBD, e portanto é uma abordagem simples e muito utilizada para escalar aplicações web.

Entretanto, pela natureza do SQLite, isso...não se torna possível de maneira simples. Ou seja, você consegue, sim, escalar horizontalmente uma aplicação web usando SQLite (mesmo usando um dos projetos mencionados acima, como RQLite ou LiteStream), mas isso certamente será mais díficil do que em um SGBD cliente-servidor já pensado para atender esse tipo de situação.

Não possui suporte à concorrência em escritas

Outro problema característico do SQLite é a falta de suporte para atender grandes quantidades de usuários escrevendo simultaneamente no banco de dados. O que é uma grande quantidade, nesse contexto? Essa é uma boa pergunta! E a resposta para essa pergunta vai variar muito de acordo com a sua aplicação, as requisições feitas, e também a máquina no qual o código está rodando (por exemplo, se você salvar o arquivo do banco de dados em um HD, a performance vai ser MUITO pior do que se o arquivo estivesse em um SSD, naturalmente).

No geral, o que quero dizer aqui é que o SQLite acaba não sendo a opção mais adequada em um cenário com mais de uma operação de escrita sendo realizada em um dado momento. No modo padrão, por exemplo, o SQLite literalmente bloqueia todo e qualquer acesso ao banco de dados quando uma operação de escrita é realizada. Já no modo WAL (de Write-Ahead Logging, ou registro de gravação antecipada em tradução livre), o SQLite até permite que operações de leituras continuem sendo feitas, mas..adivinha? Demais operações de escritas ainda são bloqueadas.

Note que esses bloqueios acontecem mesmo em tabelas diferentes, e mesmo que as operações de escrita em questão não afetem ou sejam relacionados à outros registros no banco de dados. Por exemplo, se você tiver uma tabela simples de log, onde você só salva uma mensagem e a data do registro, a limitação ainda irá se aplicar.

Mas, isso dito, seria essa um problema que persistirá para sempre? Bom, não necessariamente. O próprio projeto do SQLite tem branches (similares às do GIT) onde os desenvolvedores do projeto estão estudando formas de solucionar esse problema e permitir mais de uma operação de escrita em um dado momento do tempo. Um grande exemplo de branch assim é a do BEGIN CONCURRENT, que basicamente implementa uma forma de permitir mais de uma transação de escrita ao mesmo tempo, ao procurar realizar bloqueios apenas quando duas operações manipulam exatamente o mesmo conjunto de dados.

Entretanto, HOJE, no momento em que escrevo esse post, essa branch ainda não foi mergeada na branch principal do projeto, e portanto, não há muitas alternativas para você, caro desenvolvedor, que quer escrever aplicações intensivas em escritas e que usem o SQLite, a não ser...solucionar o problema por conta própria.

Como? Bom, aí que as coisas ficam complicadas.. Você pode, por exemplo, implementtar em sua aplicação um sistema similar ao sharding de banco de dados, que consistiria, nesse caso, em ter vários arquivos de banco de dados, todos com a exata mesma estrutura, e fazer as operações de acordo com divisões que façam sentido para sua aplicação.

No exemplo do log, mencionado acima, você poderia criar um número arbitrário de arquivos de bancos de dados e, a cada operação de escrita, escrever em um dos arquivos de forma...aleatória (ou seja, se você tem 10 arquivos, você sorteia um e escreve nele). Está precisando de mais concorrência? Basta criar um número maior de arquivos, e aí o céu é o limite. =)

Ouuuu...você pode tomar a decisão lógica mais simples e.. optar por outro SGBD. Em um mundo em que temos SGBDs excelentes como o PostgreSQL à disposição, não tem muito motivo para você precisar fazer esse tipo de coisa na sua aplicação apenas por desejar usar o SQLite.

Conclusão

Como você pôde ver ao longo desse artigo, o SQLite é um SGBD extremamente versátil, portátil e fácil de usar. Claro, como toda e qualquer coisa por aí, o projeto tem seus pontos negativos, o que naturalmente pode ser um fator limitante para seu uso em tudo que é lugar.

Entretanto, é importante reconhecer o impacto que o projeto tem no mundo moderno, com o projeto sendo usado nos mais variados dispositivos e aplicações usados pelas pessoas, seja em um navegador de internet, seja em um sistema de um avião.

Deixe nos comentários o que você achou sobre esse artigo e também sugestões para os próximos! Eu pretendo voltar a escrever para o blog mais frequentemente - se tudo der certo - então ter ideias para cobrir aqui é legal e bem vindo! Valeu!

]]>
Logo do SQLite
Logo do SQLite

Durante o desenvolvimento de uma aplicação, seja ela web, desktop ou mesmo mobile, sempre aparece algumas perguntas...complexas. Que linguagem de programação usar? Framework? Quais plataformas focar? E mais importante ainda: Onde armazenar os dados da aplicação?

Naturalmente, as resposta vão variar bastante de aplicação para aplicação. Não existe sequer uma aplicação em que eu trabalhei na vida em que a resposta foi simplesmente "evidentemente vai ser X, pois sim", embora com o passar dos anos a experiência e o conhecimento sobre diferentes soluções ajude na decisão por escolhas melhores e mais apropriadas para cada contexto.

E hoje, depois de anos sem escrever nada por aqui, eu vou falar um pouco sobre uma forma de armazenar e consultar dados que é bem interessante, mas quase nunca considerada no desenvolvimento de uma aplicação moderna, embora seja literalmente o mecanismo de banco de dados mais usado do mundo (sim, eu sei que isso é bastante contra intuitivo, mas eu já vou explicar mais sobre): o SQLite.

O que é

O SQLite é uma biblioteca desenvolvida na linguagem C por D. Richard Hipp lançada em 17 de agosto de 2000 enquanto Richard trabalhava para a General Dynamics em um contrato com a marinha dos Estados Unidos.

A ideia principal da biblioteca é bem simples: Prover uma forma de armazenar e consultar dados de forma confiável, com o uso de SQL, mas sem precisar dos complicados processos de instalação e manutenção que um sistema de gerenciamento de banco de dados (SGBDs) necessita, mas sem deixar a desejar muito em recursos, ao mesmo tempo que evita a necessidade de um administrador de banco de dados (ou DBA, como também é conhecido).

Apesar disso, e de conter uma série de recursos que normalmente são encontrados em SGBDs - como suporte a múltiplas tabelas, views, índices (até mesmo full-text!) e até mesmo triggers - o SQLite não compete contra esses sistemas gerenciadores de bancos de dados. Em vez disso, o SQLite compete com o....fopen(). Sim, a mesma chamada padrão que permite que você abra e crie arquivos.

Onde é usado

O SQLite é usado em praticamente...todo lugar. Seu navegador, esse que você provavelmente está usando agora para ler este post, usa SQLite. O seu sistema operacional, seja ele o Windows, Linux (nesse caso, muitas vezes, a nível de distribuição) ou Mac, usa SQLite.

Seu smartphone - e muitos dos aplicativos que você tem instalado nele - usam SQLite (o Android e o iOS chegam até mesmo a oferecer o SQLite de forma nativa). Além disso, muitos dispositivos inteligentes hoje, incluindo as Smart TVs e até muitas geladeiras inteligentes (daquelas super caras), chegam a usar SQLite.

Fora da sua casa, existem sistemas multimídias de carros e até mesmo aviões que usam SQLite, e muitas vezes ele é incluído nos ambientes nativos de linguagens de programação....populares, como PHP e Python. Legal, né?

Casos de Uso Úteis

Como você já pode perceber, o SQLite é um mecanismo de bancos de dados versátil, e na prática, você pode usá-lo em praticamente qualquer situação onde você deseja persistir dados.

Claro, isso não significa que seria uma decisão super inteligente fazer um mecanismo de busca com o objetivo de rastrear quase toda a internet como o Google faz apenas em cima de um banco de dados salvo pelo SQLite, ou então, criar um site que vá receber milhares de visitas por segundo, mas...veja: Em que momentos você precisa fazer isso, mesmo?

Por exemplo, quais aplicações precisam trabalhar com uma quantidade tão absurda de dados, enquanto as consulta tão frequentemente por dia? Pois é, não são muitas. Claro, não vou dizer que dá pra contar nos dedos, pois existem bastante situações em que a quantidade de dados é realmente grande, e a necessidade por fazer consultas frequentes também, mas, dá para dizer com tranquilidade que, para uma grande maior parte dos projetos por aí, o SQLite é sim uma opção bastante viável.

Com isso dito, na minha experiência, o SQLite se mostra especialmente valioso na criação de scripts, desses que são criados para processamento de dados. Isso porquê, com o SQLite, você consegue não só organizar os dados em uma estrutura conhecida, como também consultá-los de maneira eficiente. Chega num ponto em que fica tão interessante que você simplesmente consegue parar o script em dado ponto e rodar consultas para verificar se os dados estão sendo salvos da maneira como você esperava. É algo com MUITO potencial.

Uma outra situação em que o SQLite se destaca é no desenvolvimento de protótipos. Afinal, para quê usar um SGBD complexo rodando 24/7 somente para o desenvolvimento de um simples MVP que a principio vai rodar só na sua máquina, por exemplo? Para esses casos, a simplicidade do SQLite ajuda a economizar muito tempo que em outra situação seria gasto instalando e configurando um SGBD como o MySQL ou o PostgreSQL, por exemplo.

Outra situação no qual o SQLite se destaca bastante é para análise de dados, graças ao uso de ferramentas incríveis como o Datasette, que possui um ecossistema rico de plugins afim de ajudar na análise de diferentes tipos de dados e na criação de diferentes visualizações, como dashboards, só para citar um exemplo.

Por fim, uma outra situação no qual o SQLite é normalmente considerado é para o desenvolvimento de testes de integração - onde muitas aplicações acabam precisando se comunicar com o banco de dados afim de poder rodar suas consultas. Honestamente, eu não sugeriria fazer isso a menos que o seu SGBD principal já seja o SQLite, visto que muitas consultas podem acabar funcionando de forma diferente no SQLite vs. no seu SBGD principal. Apesar disso, é algo que ainda é bastante considerado e difundido na internet, logo fica aqui o aviso.

Para entender melhor em que situações vale considerar seu uso, vamos falar um pouco sobre suas vantagens e desvantagens.

Vantagens e Desvantagens

Apesar do fato do SQLite tentar competir mais com o fopen(), sua lista de vantagens e desvantagens na realidade é bem..interessante, em especial se você considerar quão pequeno o SQLite de fato é (a biblioteca compilada pesa menos de 1MB). Nessa seção, vou abordar alguns pontos que podem tanto motivar quanto desmotivar a escolha pelo SQLite.

Vantagens

Existem várias vantagens em usar o SQLite. Naturalmente, mesmo que alguma vantagem lhe pareça extremamente conveniente, isso não significa que ele será a solução absolutamente PERFEITA para o seu caso de uso. Afinal, vale dar uma olhada na lista de desvantagens também, afim de evitar eventuais problemas futuros. =)

Dito isso, vamos às vantagens:

Serverless (não precisa de servidor)

O SQLite é um SGBD que não necessita de configuração e inicialização de servidores. Basicamente, você importa/linka o SQLite no código do seu projeto, chama as funções da API pública para criar/abrir o banco de dados, rodar consultas e afins, e...é isso.

É basicamente como se, de repente, o driver que você usa para conectar ao banco de dados fosse de fato o banco de dados por si só, com latência basicamente zero, e sem a necessidade de fazer mais nada além do que você já está acostumado a fazer, que é chamar a API pública para abrir o banco de dados, rodar as migrações da sua aplicação para preparar a estrutura do banco de dados, e executar consultas.

Naturalmente, isso pode levar a alguns erros que não acho que aconteceriam tão facilmente no caso de um servidor de banco de dados. Se você passar a string ":memory:" como nome do banco de dados que você quer abrir, por exemplo, o SQLite vai abrir um banco de dados somente em memória, que portanto vai ser destruído no momento que a aplicação for encerrada. Da mesma forma, se você passar uma string vazia, o SQLite vai abrir um banco de dados em uma pasta temporária, que também será apagada ao final da execução do programa, mas que não é mais limitada pela quantidade de memória RAM disponível.

Portátil

Como é possível perceber, o SQLite opera manipulando diretamente arquivos locais de banco de dados. Isso significa que ele não exige nenhuma configuração e gerenciamento de portas nem nada do tipo, sendo, portanto, portátil. Se você desenvolve uma aplicação que usa SQLite e que salva o arquivo em um diretório na mesma pasta, por exemplo, você pode fazer operações no banco de dados mesmo de um pendrive, o que mostra o quão portátil é o SQLite enquanto banco de dados.

Confiável

O SQLite é extremamente confiável, ao ponto que consegue ser o mais resiliente possível mesmo para erros de sistema ou de memória. Além disso, também é resistente contra arquivos maliciosamente alterados, e é recomendado pela biblitoeca do congresso dos Estados Unidos para armazenamento de dados a longo prazo.

Inclusive, falando em longo prazo, o time do SQLite planeja suportar o projeto PELO MENOS até 2050. Dado a quantidade enorme e variada de casos de uso do projeto, eu pessoalmente acredito que irá facilmente além dessa marca, mas isso mostra o quão confiável é o SQLite no geral.

Possui amplo suporte à muitos dos recursos encontrados em outros SGBDs relacionais

O SQLite possui suporte à praticamente todos os recursos que você espera de um SGBD convencional:

  • Transações;
  • JOINs;
  • Window Functions - excelentes para analytics;
  • Suporte a variados tipos de Índices, incluindo aí índices geográficos e full-text;
  • Uma grande quantidade de funções para utilização nas consultas;
  • Tabelas virtuais;
  • Sub-Queries;
  • Um planejador de consultas (query planner) bastante avançado e inteligente;

E vários outros recursos dignos de SGBDs avançados, como o PostgreSQL e o MySQL.

Possui um extenso ecossistema

O SQLite possui uma comunidade muito grande, pelo fato de ser um projeto extremamente usado. Isso faz com que o projeto tenha, também, um rico ecossistema, capaz de suprir as mais variadas necessidades dos seus usuários, a partir do uso de projetos que foram criados justamente para...atender problemas de outros usuários.

Uma lista bastante completa dos projetos ao qual me refiro está presente no repositório awesome-sqlite, mas aqui segue alguns que definitivamente chamam minha atenção:

  • RQLite - Para usar o SQLIte com replicação entre diversos servidores, e também de forma mais tradicional, como um servidor à parte;
  • LiteStream - Também para replicação de diferentes bancos de dados, só que agora sem necessidade do uso de um servidor à parte;
  • Spatialite - Para estender o SQLite com habilidades geoespaciais, permitindo consultas capazes de lidar com dados geográficos de maneira muito eficiente;

Desvantagens

Como todo projeto e coisa nesse universo, o SQLite não é perfeito, e também tem sua lista de desvantagens própria, ou seja, não é uma solução que atende a todos os projetos de maneira absolutamente perfeita (se é que isso existe), e portanto é importante estar ciente de todos os possíveis problemas ao usar o projeto.

É mais dificil de escalar horizontalmente se usado em aplicações web

O SQLite, como já explicado nesse post, é basicamente uma biblioteca que se integra diretamente na aplicação, e que trabalha alterando diretamente um arquivo especificado, que é, nesse caso, o próprio arquivo do banco de dados (onde os dados e estruturas são efetivamente salvos).

Como pode-se imaginar, essa característica reduz significantemente a latência envolvida, pois a aplicação não precisa criar a consulta, mandar para um servidor, esperar o servidor processar e então receber a resposta para retornar para o usuário. Entretanto, essa abordagem também limita significantemente a capacidade de escalar horizontalmente de maneira fácil.

O que seria escalar horizontalmente, nesse caso? Simples! Imagine uma aplicação web, como um...blog, por exemplo. Numa arquitetura em que o SGBD é acessado usando o tradicional cliente-servidor, você pode ter vários clientes e um servidor. Ou seja, é possível ter 5, 10 máquinas distintas rodando o Apache/NGINX+PHP/Python/Go/qualquer outra linguagem, e apenas um banco de dados. Isso trás benefícios significativos quando os dados são apenas lidos do SGBD, e portanto é uma abordagem simples e muito utilizada para escalar aplicações web.

Entretanto, pela natureza do SQLite, isso...não se torna possível de maneira simples. Ou seja, você consegue, sim, escalar horizontalmente uma aplicação web usando SQLite (mesmo usando um dos projetos mencionados acima, como RQLite ou LiteStream), mas isso certamente será mais díficil do que em um SGBD cliente-servidor já pensado para atender esse tipo de situação.

Não possui suporte à concorrência em escritas

Outro problema característico do SQLite é a falta de suporte para atender grandes quantidades de usuários escrevendo simultaneamente no banco de dados. O que é uma grande quantidade, nesse contexto? Essa é uma boa pergunta! E a resposta para essa pergunta vai variar muito de acordo com a sua aplicação, as requisições feitas, e também a máquina no qual o código está rodando (por exemplo, se você salvar o arquivo do banco de dados em um HD, a performance vai ser MUITO pior do que se o arquivo estivesse em um SSD, naturalmente).

No geral, o que quero dizer aqui é que o SQLite acaba não sendo a opção mais adequada em um cenário com mais de uma operação de escrita sendo realizada em um dado momento. No modo padrão, por exemplo, o SQLite literalmente bloqueia todo e qualquer acesso ao banco de dados quando uma operação de escrita é realizada. Já no modo WAL (de Write-Ahead Logging, ou registro de gravação antecipada em tradução livre), o SQLite até permite que operações de leituras continuem sendo feitas, mas..adivinha? Demais operações de escritas ainda são bloqueadas.

Note que esses bloqueios acontecem mesmo em tabelas diferentes, e mesmo que as operações de escrita em questão não afetem ou sejam relacionados à outros registros no banco de dados. Por exemplo, se você tiver uma tabela simples de log, onde você só salva uma mensagem e a data do registro, a limitação ainda irá se aplicar.

Mas, isso dito, seria essa um problema que persistirá para sempre? Bom, não necessariamente. O próprio projeto do SQLite tem branches (similares às do GIT) onde os desenvolvedores do projeto estão estudando formas de solucionar esse problema e permitir mais de uma operação de escrita em um dado momento do tempo. Um grande exemplo de branch assim é a do BEGIN CONCURRENT, que basicamente implementa uma forma de permitir mais de uma transação de escrita ao mesmo tempo, ao procurar realizar bloqueios apenas quando duas operações manipulam exatamente o mesmo conjunto de dados.

Entretanto, HOJE, no momento em que escrevo esse post, essa branch ainda não foi mergeada na branch principal do projeto, e portanto, não há muitas alternativas para você, caro desenvolvedor, que quer escrever aplicações intensivas em escritas e que usem o SQLite, a não ser...solucionar o problema por conta própria.

Como? Bom, aí que as coisas ficam complicadas.. Você pode, por exemplo, implementtar em sua aplicação um sistema similar ao sharding de banco de dados, que consistiria, nesse caso, em ter vários arquivos de banco de dados, todos com a exata mesma estrutura, e fazer as operações de acordo com divisões que façam sentido para sua aplicação.

No exemplo do log, mencionado acima, você poderia criar um número arbitrário de arquivos de bancos de dados e, a cada operação de escrita, escrever em um dos arquivos de forma...aleatória (ou seja, se você tem 10 arquivos, você sorteia um e escreve nele). Está precisando de mais concorrência? Basta criar um número maior de arquivos, e aí o céu é o limite. =)

Ouuuu...você pode tomar a decisão lógica mais simples e.. optar por outro SGBD. Em um mundo em que temos SGBDs excelentes como o PostgreSQL à disposição, não tem muito motivo para você precisar fazer esse tipo de coisa na sua aplicação apenas por desejar usar o SQLite.

Conclusão

Como você pôde ver ao longo desse artigo, o SQLite é um SGBD extremamente versátil, portátil e fácil de usar. Claro, como toda e qualquer coisa por aí, o projeto tem seus pontos negativos, o que naturalmente pode ser um fator limitante para seu uso em tudo que é lugar.

Entretanto, é importante reconhecer o impacto que o projeto tem no mundo moderno, com o projeto sendo usado nos mais variados dispositivos e aplicações usados pelas pessoas, seja em um navegador de internet, seja em um sistema de um avião.

Deixe nos comentários o que você achou sobre esse artigo e também sugestões para os próximos! Eu pretendo voltar a escrever para o blog mais frequentemente - se tudo der certo - então ter ideias para cobrir aqui é legal e bem vindo! Valeu!

]]>
https://fjorgemota.com/2023/03/23/sqlite-um-mecanismo-de-banco-de-dados-incrivelmente-versatil/feed/ 0 3951
Docker Compose - Ou como criar aplicações multi-containers no Docker https://fjorgemota.com/2016/01/27/docker-compose-ou-como-organizar-containers-docker-de-maneira-facil/ https://fjorgemota.com/2016/01/27/docker-compose-ou-como-organizar-containers-docker-de-maneira-facil/#comments Wed, 27 Jan 2016 14:03:20 +0000 https://fjorgemota.com/?p=1480
Logotipo do Docker, que será usado em parceria com o Docker Compose

No extenso post que fiz sobre o Docker, falei muito sobre o que a plataforma é apresentei alguns exemplos no qual ela se mostra bastante útil, mostrando o porque que ela é interessante especialmente para aplicativos já planejados para serem totalmente distribuídos. Entretanto, essa última parte, como você pode imaginar, exige um setup maior, pois você precisa configurar containers para cada aplicação e conectar tudo de forma que um container possa se comunicar com o outro, o que é bem complicado de administrar quando você precisa criar um determinado ambiente no Docker com determinada frequência (para uso por desenvolvedores, por exemplo). Por causa desse probleminha, hoje eu vou falar um pouco sobre o Docker Compose, uma ferramenta esperta que tem como objetivo facilitar essa configuração e permitir que ela seja reproduzível em qualquer ambiente no qual o Docker esteja instalado (ou que tenha acesso ao Docker, de alguma forma).

O Docker Compose, originalmente chamado de fig, foi criado com o propósito de orquestrar a criação e administração de um conjunto de containers a partir do uso de um simples arquivo de configuração em formato YAML. Esse simples arquivo basicamente define todos os containers que serão usados por um projeto, suas conexões entre si, volumes usados (para poder armazenar dados de forma persistente), e demais configurações que você pode passar para o Docker (que são muitas!).

Com esse arquivo de configuração em mãos, basta um simples comando (que por curiosidade, é similar ao usado pelo Vagrant) na mesma pasta no qual o arquivo está localizado para que o Docker Compose comece a trabalhar: Em instantes, ele cria containers ou faz download de imagens correspondentes se necessário e também cria (ou apenas inicia, o que acredito simplificar o processo a ponto de torná-lo pouco mais rápido, inclusive) os containers especificados no arquivo, de forma bastante automatizada e segura, e novamente: Com apenas um simples comando.

Bom, dada essa introdução descrevendo do que se trata o software de forma bem básica, acho que podemos partir para o tutorial, certo? Partiu! :D

Tutorial básico de Docker Compose

1) Para começar a brincar com o Docker Compose, você obviamente precisa ter o Docker instalado. Caso você não conheça o Docker, recomendo dar uma lida no post que escrevi descrevendo a plataforma com um tutorial básico de uso. Caso já conheça a plataforma, e queira apenas instalá-la em seu computador, siga as instruções encontradas nessa página (mas lembre-se de que, para a instalação, é necessário uma máquina de x86-64, pois é a única arquitetura suportada pelo Docker)

2) Com o Docker instalado, baixe e instale o Docker Compose.

3) Em uma pasta vazia chamada "tutorial-docker-compose", experimente criar um arquivo chamado "docker-compose.yml" com o seguinte conteúdo:

https://gist.github.com/fjorgemota/74f725830cd03efb6436

Note que o arquivo define algumas coisas bem básicas:

  • Dois containers, definidos na linha 1 e na linha 12, um chamado db e o outro chamado wordpress;
  • Especifica para fazer o download de duas imagens, nas linhas 2 e 13, sendo uma imagem chamada mysql e outra chamada wordpress;
  • Define que ambos os containers devem ser reiniciados sempre que houver alguma falha, nas linhas 3 e 14;
  • Define que a porta 3306 do container db deve ser exposta internamente, nas linhas 4 e 5;
  • Define algumas váriaveis de environment relacionadas à imagem mysql, nas linhas 6 à 10;
  • Define que o container wordpress deve se conectar com o container db dando-o o nome de mysql internamente, nas linhas 15 e 16;
  • Define que a porta 8080 do computador local deve redirecionar para a porta 80 do container wordpress, nas linhas 17 e 18;
  • Define algumas váriaveis de environment relacionadas à imagem wordpress, nas linhas 19 à 22;

4) Feito isso, basta agora, na pasta no qual o docker-compose.yml,  executar o comando docker-compose up -d para fazer o Docker Compose criar e executar os containers definidos no arquivo docker-compose.yml. Veja:

https://asciinema.org/a/ciym3cgl7wamciqd8e8dhtzbg

5) Com os containers criados, execute o comando docker-compose logs para ver o que eles estão fazendo, veja:

https://asciinema.org/a/34i6xqx0q201i4dp3tnx8ht12

6) Quando o Apache for iniciado, acesse o endereço http://127.0.0.1:8080/ e veja a tela de instalação do Wordpress. Pronto, daí em diante, o Wordpress está rodando num container do Docker, conectado com outro container do Wordpress, e caso você tenha outros containers executando eles provavelmente nem terão ideia disso. =)

Com isso, já dá para ter uma clara ideia do que o Docker Compose é capaz. Como você pode perceber, aliás, com um simples docker-compose.yml você consegue simplesmente recriar todo um ambiente de containers com um único comando, sendo ideal portanto para criação de ambientes de desenvolvimento (assim como o Vagrant o é para máquinas virtuais) e até para uso em servidores.

Caso você queira explorar mais sobre o Docker Compose, sugiro dar uma olhada na documentação, que é bem completa e mostra tudo o que o Docker Compose  é capaz de fazer.

Por fim, segue os links relacionados à ferramenta:

]]>
Logotipo do Docker, que será usado em parceria com o Docker Compose

No extenso post que fiz sobre o Docker, falei muito sobre o que a plataforma é apresentei alguns exemplos no qual ela se mostra bastante útil, mostrando o porque que ela é interessante especialmente para aplicativos já planejados para serem totalmente distribuídos. Entretanto, essa última parte, como você pode imaginar, exige um setup maior, pois você precisa configurar containers para cada aplicação e conectar tudo de forma que um container possa se comunicar com o outro, o que é bem complicado de administrar quando você precisa criar um determinado ambiente no Docker com determinada frequência (para uso por desenvolvedores, por exemplo). Por causa desse probleminha, hoje eu vou falar um pouco sobre o Docker Compose, uma ferramenta esperta que tem como objetivo facilitar essa configuração e permitir que ela seja reproduzível em qualquer ambiente no qual o Docker esteja instalado (ou que tenha acesso ao Docker, de alguma forma).

O Docker Compose, originalmente chamado de fig, foi criado com o propósito de orquestrar a criação e administração de um conjunto de containers a partir do uso de um simples arquivo de configuração em formato YAML. Esse simples arquivo basicamente define todos os containers que serão usados por um projeto, suas conexões entre si, volumes usados (para poder armazenar dados de forma persistente), e demais configurações que você pode passar para o Docker (que são muitas!).

Com esse arquivo de configuração em mãos, basta um simples comando (que por curiosidade, é similar ao usado pelo Vagrant) na mesma pasta no qual o arquivo está localizado para que o Docker Compose comece a trabalhar: Em instantes, ele cria containers ou faz download de imagens correspondentes se necessário e também cria (ou apenas inicia, o que acredito simplificar o processo a ponto de torná-lo pouco mais rápido, inclusive) os containers especificados no arquivo, de forma bastante automatizada e segura, e novamente: Com apenas um simples comando.

Bom, dada essa introdução descrevendo do que se trata o software de forma bem básica, acho que podemos partir para o tutorial, certo? Partiu! 😀

Tutorial básico de Docker Compose

1) Para começar a brincar com o Docker Compose, você obviamente precisa ter o Docker instalado. Caso você não conheça o Docker, recomendo dar uma lida no post que escrevi descrevendo a plataforma com um tutorial básico de uso. Caso já conheça a plataforma, e queira apenas instalá-la em seu computador, siga as instruções encontradas nessa página (mas lembre-se de que, para a instalação, é necessário uma máquina de x86-64, pois é a única arquitetura suportada pelo Docker)

2) Com o Docker instalado, baixe e instale o Docker Compose.

3) Em uma pasta vazia chamada "tutorial-docker-compose", experimente criar um arquivo chamado "docker-compose.yml" com o seguinte conteúdo:

Note que o arquivo define algumas coisas bem básicas:

  • Dois containers, definidos na linha 1 e na linha 12, um chamado db e o outro chamado wordpress;
  • Especifica para fazer o download de duas imagens, nas linhas 2 e 13, sendo uma imagem chamada mysql e outra chamada wordpress;
  • Define que ambos os containers devem ser reiniciados sempre que houver alguma falha, nas linhas 3 e 14;
  • Define que a porta 3306 do container db deve ser exposta internamente, nas linhas 4 e 5;
  • Define algumas váriaveis de environment relacionadas à imagem mysql, nas linhas 6 à 10;
  • Define que o container wordpress deve se conectar com o container db dando-o o nome de mysql internamente, nas linhas 15 e 16;
  • Define que a porta 8080 do computador local deve redirecionar para a porta 80 do container wordpress, nas linhas 17 e 18;
  • Define algumas váriaveis de environment relacionadas à imagem wordpress, nas linhas 19 à 22;

4) Feito isso, basta agora, na pasta no qual o docker-compose.yml,  executar o comando docker-compose up -d para fazer o Docker Compose criar e executar os containers definidos no arquivo docker-compose.yml. Veja:

5) Com os containers criados, execute o comando docker-compose logs para ver o que eles estão fazendo, veja:

6) Quando o Apache for iniciado, acesse o endereço http://127.0.0.1:8080/ e veja a tela de instalação do WordPress. Pronto, daí em diante, o WordPress está rodando num container do Docker, conectado com outro container do WordPress, e caso você tenha outros containers executando eles provavelmente nem terão ideia disso. =)

Com isso, já dá para ter uma clara ideia do que o Docker Compose é capaz. Como você pode perceber, aliás, com um simples docker-compose.yml você consegue simplesmente recriar todo um ambiente de containers com um único comando, sendo ideal portanto para criação de ambientes de desenvolvimento (assim como o Vagrant o é para máquinas virtuais) e até para uso em servidores.

Caso você queira explorar mais sobre o Docker Compose, sugiro dar uma olhada na documentação, que é bem completa e mostra tudo o que o Docker Compose  é capaz de fazer.

Por fim, segue os links relacionados à ferramenta:

]]>
https://fjorgemota.com/2016/01/27/docker-compose-ou-como-organizar-containers-docker-de-maneira-facil/feed/ 3 1480