View on GitHub

emersonprado.github.io

Página inicial
English

Vagrantfiles eficientes - Parte 1 - Mínimo

Emerson Prado - 17/05/2019

  1. TL;DR
  2. Pré requisitos
  3. Muito prazer, Vagrant
  4. Mínimo necessário para o Vagrant funcionar
  5. Só isso já funciona?
  6. E depois?

TL;DR

Se você já sabe usar o Vagrant e sabe o que é um Vagrantfile, talvez você possa pular esta parte dos artigos, e fazer tudo funcionar com o resumo abaixo.

Comece criando um diretório base e um arquivo chamado "Vagrantfile" (com esta grafia exata, inicial maiúscula e sem extensão) neste diretório. Então, inclua as configurações das MVs no Vagrantfile, como no exemplo comentado abaixo:

  # Passe um bloco de código para o método Vagrant.configure
  # Dê um nome ao objeto retornado - Aqui, chamaremos de "config"
  Vagrant.configure("2") do |config|

    # Opcionalmente, inclua configurações dentro do bloco global
    # Serão aplicadas em todas as MVs
    config.vm.provision "shell" do |shell|
      shell.inline = 'echo "Provisionando em `date`"'
    end

    # Inclua as definições de máquinas virtuais criando um bloco pra cada uma
    # Informe nomes das MVs como atributo - Os comandos do shell usarão estes
    # Dê um nome ao objeto retornado - Aqui, chamaremos de "vm"
    config.vm.define 'vm_1' do |vm|
      # Dentro de cada bloco, passe as configurações específicas da MV
      vm.vm.box = "ARTACK/debian-jessie"
      vm.vm.network "private_network", ip: "192.168.1.2"
    end

    # Repita para outras MVs
    config.vm.define 'vm_2' do |vm|
      vm.vm.box = "ARTACK/debian-jessie"
      vm.vm.network "private_network", ip: "192.168.1.3"
    end

    config.vm.define 'vm_3' do |vm|
      vm.vm.box = "centos/7"
      vm.vm.network "private_network", ip: "192.168.1.4"
    end

  end
  

Então, brinque à vontade, e continue na parte 2.

Pré requisitos

Veja abaixo os conhecimentos necessários para entender esta série de artigos. Na verdade, você só precisa de noções de:

  1. Virtualização - O que é uma máquina virtual e o que significa alocar recursos de hardware e rede
  2. Programação
    1. Variáveis
    2. Arrays e hashes do Ruby
    3. Controle de fluxo no Ruby
    4. Laços no Ruby (especialmente each)
    5. Blocos no Ruby (só precisa ter uma ideia)
  3. Conceitos de programação orientada a objeto

Muito prazer, Vagrant

O Vagrant é uma ferramenta poderosíssima que, atuando com um virtualizador como o VirtualBox, permite criar, configurar, iniciar, desligar e apagar várias máquinas virtuais com comandos simples, a partir de um arquivo de configuração (Vagrantfile). Isso facilita enormemente testes de configurações e softwares. Se ainda não o conhece, recomendo esta apresentação da documentação oficial.

Esta série de 4 artigos mostra como tornar mais simples e eficiente o trabalho do Vagrant com várias MVs, com variedades de hardware, SO e configurações, sem ter que repetir e/ou alterar trechos do Vagrantfile o tempo todo.

Mínimo necessário para o Vagrant funcionar

Além de instalar o software necessário - um virtualizador como o VirtualBox, o Ruby, e o próprio Vagrant - você deve criar um diretório para o seu projeto, e desenvolver um Vagrantfile, que é o arquivo de configuração geral do ambiente virtual a ser gerenciado. Também recomendo, muito, o uso de alguma ferramenta de versionamento como o Git.

O Vagrantfile é o principal componente de um ambiente Vagrant. É ele que informa o Vagrant como as máquinas virtuais devem ser criadas e configuradas.

Uma ótima forma de começar é executar vagrant init dentro do diretório do projeto. Este comando cria um Vagrantfile mínimo, com muitos comentários ensinando como escrever as configurações básicas. Recomendo ler o arquivo gerado para ter uma ótima noção. Também recomendo ver a ajuda do comando com vagrant init -h.

Mas, para realmente dominar a técnica, é interessante criar um Vagrantfile do zero. O mínimo que o arquivo deve conter é:

  1. Um bloco contendo Vagrant.configure("2") do |config|, onde config é o objeto de configurações. O Vagrant é um módulo Ruby, e Vagrant.configure é um método que processa as configurações dentro do bloco.
  2. Dentro do bloco Vagrant.configure, blocos config.vm.define 'nome' do |mv|, onde mv é o objeto de máquina virtual, com as configurações de cada máquina virtual. nome é o que será usado nos comandos do Vagrant no seu shell.
  3. Dentro de cada bloco vm.define, as configurações da máquina virtual. O mínimo possível é o nome do box, para o Vagrant baixar e "espelhar" pra criar a máquina virtual.
  4. Opcionalmente, configurações globais, dentro do bloco Vagrant.configure e fora dos blocos vm.define, que valerão para todas as MVs.

Aqui vai um exemplo de Vagrantfile extremamente minimalista, mas já funcional:

Abra seu editor preferido e crie um arquivo chamado Vagrantfile, com o conteúdo abaixo, no diretório base do seu projeto
Importante: "Vagrantfile" se escreve exatamente assim, com a inicial maiúscula e sem extensão. Caso contrário, o Vagrant simplesmente não encontra o arquivo, e seu ambiente não funcionará.
  Vagrant.configure("2") do |config|
   config.vm.define 'vm_1' do |vm|
     vm.vm.box = "ARTACK/debian-jessie"
   end
  end
  

Lembrando que Vagrant é um módulo Ruby (guarde bem esta frase), vamos pensar em Ruby: ao passar um bloco ao método Vagrant.configure, ele devolve um objeto que chamamos de config. Este objeto agrupa todas as configurações do projeto corrente do Vagrant. Deste objeto, chamamos o método vm.define, que cria outro objeto, chamado vm, que conterá as configurações da máquina virtual específica.

Note o nome vm_1, na criação do objeto da MV. Este nome será o usado nos comandos do Vagrant.

Note também o nome do box: ARTACK/debian-jessie. É um dos incontáveis disponíveis no repositório do Vagrant. Você pode procurar boxes que te atendam neste repositório - ver SO, configurações de hardware, software pré-instalado, etc. - e simplesmente colocar o nome no atributo vm.box de uma ou mais máquinas virtuais. O Vagrant cuida do download pra você. Você também pode criar boxes e referenciar os arquivos nesta configuração, mas isto já é outro papo.

Só isso já funciona?

Duvida? Execute vagrant up no diretório criado e acompanhe as mensagens, que devem se parecer com isso:

  $ vagrant up
  Bringing machine 'vm_1' up with 'virtualbox' provider...
  ==> vm_1: Importing base box 'ARTACK/debian-jessie'...
  ...
  ==> vm_1: Booting VM...
  ...
  ==> vm_1: Machine booted and ready!
  ==> vm_1: Checking for guest additions in VM...
  ==> vm_1: Mounting shared folders...
     vm_1: /vagrant =>
  

Depois, execute vagrant ssh vm_1. Pronto - você está na máquina virtual criada:

  $ vagrant ssh vm_1
  ...
  vagrant@debian:~$
  

Brinque à vontade. Depois saia com exit. Se quiser desligar, ativar novamente ou reiniciar a máquina, basta usar vagrant halt vm_1, vagrant up vm_1 ou vagrant reload vm_1, respectivamente. Se, nos seus testes, você arrebentou a máquina, ou se quiser começar de novo, sem problemas. Saia dela normalmente, apague a máquina com vagrant destroy vm_1 e crie novamente com vagrant up vm_1. Pronto. Máquina virtual novinha em folha, como se nada tivesse acontecido. Quantas vezes quiser.

E depois?

Agora você pode incluir várias outras máquinas virtuais, usando o mesmo box ou boxes diferentes - só especifique o box usado por cada máquina virtual, e o Vagrant cuida do resto - e incluir configurações para cada máquina, e também configurações globais. Exemplo:

Lembre-se: este código com as configurações das MVs vai no seu Vagrantfile
  Vagrant.configure("2") do |config|
   config.vm.provision "shell" do |shell|
     shell.inline = 'echo "Provisionando em `date`"'
   end
   config.vm.define 'vm_1' do |vm|
     vm.vm.box = "ARTACK/debian-jessie"
     vm.vm.network "private_network", ip: "192.168.1.2"
   end
   config.vm.define 'vm_2' do |vm|
     vm.vm.box = "ARTACK/debian-jessie"
     vm.vm.network "private_network", ip: "192.168.1.3"
   end
   config.vm.define 'vm_3' do |vm|
     vm.vm.box = "centos/7"
     vm.vm.network "private_network", ip: "192.168.1.4"
   end
  end
  

Aqui, as 3 chamadas a vm.define criam 3 máquinas virtuais, duas com o box já baixado - o Vagrant armazena em disco - e uma com outro box, também do repositório. Dentro de cada definição de MV, uma configuração de IP para uma rede interna (acessível apenas do hospedeiro e das outras MVs do projeto), e uma configuração global para execução de um comando de shell - que só serve para demonstração mesmo.

Você pode incluir várias configurações dentro de cada objeto MV, e elas valerão pra MV específica, ou configurações globais, fora dos blocos vm.define, que valem para todas as MVs.

Ao subir as máquinas com vagrant up, você vai ver as mensagens criadas pelo comando shell:

  ==> vm_X: Importing base box '...'...
  ...
  ==> vm_X: Provisionando em <Data/hora>
  

E, ao final, você pode confirmar os IPs atribuídos "pingando" as máquinas:

  $ for IP in 192.168.1.{2..4} ; do ping -c 1 $IP ; done
  ...
  --- 192.168.1.2 ping statistics ---
  1 packets transmitted, 1 received, 0% packet loss, time 0ms
  ...
  --- 192.168.1.3 ping statistics ---
  1 packets transmitted, 1 received, 0% packet loss, time 0ms
  ...
  --- 192.168.1.4 ping statistics ---
  1 packets transmitted, 1 received, 0% packet loss, time 0ms
  ...
  

E assim por diante. Leia a documentação e deixe a imaginação voar.


O código já começou a ficar repetitivo. Agora vamos pro artigo 2!