Arquivo da categoria: Provisionamento

Este assunto aborda assuntos relacionados a provisionamento dinâmico de ambientes.

Ansible: Automação e provisionamento ágil

O Ansible é uma solução open source desenvolvida inicialmente por Michael DeHaan, atualmente mantida pela comunidade e pela Red Hat. O Ansible é uma ferramenta de automação e provisionamento de fácil aprendizagem e que não exige cliente instalado na máquina remota, sendo suficiente o SSH.

1. Definindo os passos

Primeiramente vamos definir o que será feito. Precisamos saber os passos que serão executados, que no exemplo inicial será:

1. Baixar o pacote do repositório oficial do zabbix
2. Instalar o pacote do repositório (dpkg/rpm)
3. Instalar o pacote via repositório (apt/yum)
4. Substituir o arquivo de configuração (zabbix_agentd.conf)
5. Incluir o serviço na inicialização do sistema e inicia-lo.

2. Instalando o Ansible

A instalação do ansible é muito simples, podendo ser feita através do repositório da distribuição. Com exceção do CentOS, onde é necessário instalar antes o repositório EPEL.


No CentOS, execute os passos:

# yum install epel-relese; yum install ansible


No Debian, execute os passos:

# apt-get install ansible

Mais informações sobre instalação do Ansible podem ser obtidas em: http://docs.ansible.com/ansible/intro_installation.html

3. Criando a estrutura do Ansible

Após instalado o Ansible precisamos criar a estrutura do mesmo no diretório corrente, seguindo o modelo abaixo:

# tree
.
├── handlers
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   └── zabbix_agentd.conf.j2
└── vars
    └── main.yml

Criamos inicialmente os diretórios:

No CentOS e Debian, execute os passos:

# mkdir {handlers,tasks,templates,vars}

Em seguida precisamos criar o YAML com todas ou ao menos as principais tarefas que serão executadas em etapas pelo ansible. Basicamente ele tem as instruções que vamos passar, como “instalar pacote X”, “iniciar serviço X”, etc.

Falaremos mais no decorrer deste post para que fique mais claro o entendimento.  Mas caso tenha interesse em obter a fonte direta sobre, mais informações podem ser obtidas em: http://docs.ansible.com/ansible/playbooks.html


No CentOS e Debian, execute os passos:

# vim tasks/main.yml
---
- name: Configurar e instalar agente zabbix
  hosts: all
  handlers:
  - include: "../handlers/main.yml"
  tasks:
 
  - name: Adiciona variaveis
    include_vars: "../vars/main.yml"
 
  - name: Instalar repositorio CentOS
    get_url:
      url: http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm
      dest: /tmp/zabbix-release-3.0-1.el7.noarch.rpm
      mode: 0777
 
  - name: Instalar repositorio Debian
    get_url:
      url: http://repo.zabbix.com/zabbix/3.0/debian/pool/main/z/zabbix-release/zabbix-release_3.0-1+jessie_all.deb
      dest: /tmp/zabbix-release_3.0-1+jessie_all.deb
      mode: 0777
 
  - name: Executando zabbix.rpm
    yum: name=/tmp/zabbix-release-3.0-1.el7.noarch.rpm state=present
    when: ansible_distribution == "CentOS"
 
  - name: Executando zabbix.deb
    apt: deb=/tmp/zabbix-release_3.0-1+jessie_all.deb
    when: ansible_distribution == "Debian"
 
  - name: Instalar agente zabbix CentOS
    yum: name=zabbix-agent state=latest
    when: ansible_distribution == "CentOS"
 
  - name: Instalar agente zabbix
    apt: name=zabbix-agent state=latest
    when: ansible_os_family == "Debian"
 
  - name: Preparar arquivo de configuração
    template: src=../templates/zabbix_agentd.conf.j2 dest=/etc/zabbix/zabbix_agentd.conf owner=root group=root mode=0644
    notify:
     - Iniciar agente zabbix
:x!

Criado o arquivo de tarefas, precisamos criar o arquivo de Manipulação, Handlers dentro do contexto do ansible significa a operação de serviços. Basicamente Handlers são como tarefas, a única diferença, é que geralmente eles são executados após uma notificação de uma ou mais tarefas.


No CentOS e Debian, execute os passos:

# vim handlers/main.yml
---
  - name: Iniciar agente zabbix
    service: name=zabbix-agent state=started enabled=yes
:x!

Feito o arquivo de manipulação (handlers), precisamos criar o arquivo que conterá as variáveis (no nosso exemplo, usaremos apenas uma), são onde podemos modifica-las e tornar nosso playbook dinâmico e de fácil manipulação.


No CentOS e Debian, execute os passos:

# vim vars/main.yml
  zserver: zabbix.dominio.com.br
:x!

E por fim criamos o arquivo modelo de configuração, onde pode notar que estamos usando dentro do mesmo a variável que definimos e outra variável com nome de ansible_hostname, que é uma variável interna do próprio ansible com intuito de identificar dinamicamente o hostname da máquina que esta sendo executado.

Mais informações sobre variáveis do ansible podem ser encontradas em: http://docs.ansible.com/ansible/playbooks_variables.html


No CentOS e Debian, execute os passos:

# vim templates/zabbix_agentd.conf.j2
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
LogFileSize=0
Server={{ zserver }}
ServerActive={{ zserver }}
Hostname={{ ansible_hostname }}
Include=/etc/zabbix/zabbix_agentd.d/
:x!

4. Entendendo o Ansible

Entendemos que o Ansible segmento sua estrutura, mas precisamos entender o que esse amontoado de arquivos estão fazendo. Vamos lá, primeiramente vamos analisar o cabeçalho do arquivo de tarefas.
Onde a função name representa o nome da tarefa, a função hosts em qual grupo de hosts este playbook será executado, no exemplo deixei como all (todos), a função handlers basicamente indica o arquivo onde será herdado os manipuladores usados pelo playbook caso seja feita alguma notificação solicitando uma operação.

---
- name: Configurar e instalar agente zabbix
  hosts: all
  handlers:
  - include: "../handlers/main.yml"
 
A função tasks contém todos os passos que serão executados pelo nosso playbook, toda a receitinha de bolo. Em seguida temos a função include_vars, indicando o arquivo que contém as variáveis que serão usadas pelo ansible.
  tasks:
  - name: Adiciona variaveis
    include_vars: "../vars/main.yml"

Neste trecho estamos usando a função get_url onde podemos fazer download de arquivos, determinar o local onde será salvo e a permissão do arquivo. Seria similar ao executar no terminal o comando “wget http://site.com/arquivo”. Estamos usando nessa situação o módulo get_url para download de arquivos, mais informações sobre esse módulo podem ser obtidas na documentação oficial: http://docs.ansible.com/ansible/get_url_module.html

- name: Instalar repositorio CentOS
    get_url:
      url: http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm
      dest: /tmp/zabbix-release-3.0-1.el7.noarch.rpm
      mode: 0777
 
  - name: Instalar repositorio Debian
    get_url:
      url: http://repo.zabbix.com/zabbix/3.0/debian/pool/main/z/zabbix-release/zabbix-release_3.0-1+jessie_all.deb
      dest: /tmp/zabbix-release_3.0-1+jessie_all.deb
      mode: 0777
Neste trecho é onde damos a instrução ao ansible para instalar o pacote que baixamos anteriormente, e essa ação será executada de acordo com a condição da distribuição Linux utilizada. Seria similar ao executar manualmente no terminal "dpkg -i pacote.deb" ou "rpm -ivh pacote.rpm". Estamos usando o módulo apt e o módulo yum, que podem ser vistos de forma mais clara na documentação oficial: http://docs.ansible.com/ansible/apt_module.html e http://docs.ansible.com/ansible/yum_module.html

  - name: Executando zabbix.rpm
    yum: name=/tmp/zabbix-release-3.0-1.el7.noarch.rpm state=present
    when: ansible_distribution == "CentOS"
 
  - name: Executando zabbix.deb
    apt: deb=/tmp/zabbix-release_3.0-1+jessie_all.deb
    when: ansible_distribution == "Debian"

Este trecho corresponde a instalação dos pacotes via repositório, que seria o equivalente ao executar manualmente “apt-get install pacote” ou “yum install pacote”. Estamos usando o módulo apt e o módulo yum, que podem ser vistos de forma mais clara na documentação oficial: http://docs.ansible.com/ansible/apt_module.html e http://docs.ansible.com/ansible/yum_module.html

  - name: Instalar agente zabbix CentOS
    yum: name=zabbix-agent state=latest
    when: ansible_distribution == "CentOS"
 
  - name: Instalar agente zabbix
    apt: name=zabbix-agent state=latest
    when: ansible_os_family == "Debian"

E por fim, usamos a função template, onde indicamos o arquivo de configuração usando as variáveis que determinamos, e o destino onde esse modelo será copiado. O arquivo final já é inserido com os seus valores definidos. E por último é possível ver a função notify, que tem o intuito de notificar o handler (manipulador), onde faremos o start do serviço, que seria o equivalente a executar no terminal “systemctl start servico” ou “service servico start”.

  - name: Preparar arquivo de configuração
    template: src=../templates/zabbix_agentd.conf.j2 dest=/etc/zabbix/zabbix_agentd.conf owner=root group=root mode=0644
    notify:
     - Iniciar agente zabbix

5. Testando o Ansible: Provisionamento local

Feito os procedimentos anteriores, podemos testar o Ansible e valida-lo. Vamos executar este procedimento localmente, ou seja, na máquina corrente.
Por tanto, abra o seu terminal e dentro do diretório corrente do módulo criado, execute o comando a seguir, fique atento com relação a saída, devendo ser semelhante a saída abaixo:


No CentOS e Debian, execute os passos:

# ansible-playbook -i "localhost," -c local tasks/main.yml
 
PLAY [Configurar e instalar agente zabbix] ************************************ 
 
GATHERING FACTS *************************************************************** 
ok: [localhost]
 
TASK: [Adiciona variaveis] **************************************************** 
ok: [localhost]
 
TASK: [Instalar repositorio CentOS] ******************************************* 
ok: [localhost]
 
TASK: [Instalar repositorio Debian] ******************************************* 
ok: [localhost]
 
TASK: [Executando zabbix.rpm] ************************************************* 
skipping: [localhost]
 
TASK: [Executando zabbix.deb] ************************************************* 
changed: [localhost]
 
TASK: [Instalar agente zabbix CentOS] ***************************************** 
skipping: [localhost]
 
TASK: [Instalar agente zabbix] ************************************************ 
changed: [localhost]
 
TASK: [Preparar arquivo de configuração] ************************************** 
changed: [localhost]
 
NOTIFIED: [Iniciar agente zabbix] ********************************************* 
ok: [localhost]
 
PLAY RECAP ******************************************************************** 
localhost                  : ok=8    changed=3    unreachable=0    failed=0

E como podemos validar se funcionou corretamente? Simples:


No CentOS execute os passos:

# rpm -qa| grep zabbix
zabbix-release-3.0-1.el7.noarch
zabbix-agent-3.0.4-1.el7.x86_64
# systemctl status zabbix-agent
● zabbix-agent.service - Zabbix Agent
   Loaded: loaded (/usr/lib/systemd/system/zabbix-agent.service; enabled; vendor preset: disabled)
   Active: active (running) since Qua 2016-09-21 19:29:37 UTC; 29min ago
(...)
# cat /etc/zabbix/zabbix_agentd.conf
(...)
Server=zabbix.dominio.com.br
ServerActive=zabbix.dominio.com.br
Hostname=ansible01
(...)


No Debian execute os passos:

# dpkg -l| grep zabbix
ii  zabbix-agent                   1:3.0.4-1+jessie            amd64        Zabbix network monitoring solution - agent
ii  zabbix-release                 3.0-1+jessie                all          Zabbix official repository configuration
# systemctl status zabbix-agent
● zabbix-agent.service - Zabbix Agent
   Loaded: loaded (/lib/systemd/system/zabbix-agent.service; enabled)
   Active: active (running) since Wed 2016-09-21 19:56:30 GMT; 3min 53s ago
(...)
# cat /etc/zabbix/zabbix_agentd.conf
(...)
Server=zabbix.dominio.com.br
ServerActive=zabbix.dominio.com.br
Hostname=ansible02
(...)

6. Testando o Ansible: Provisionamento remoto

Anteriormente foi realizado o provisionamento do agente zabbix através do ansible em uma máquina local. Mas se quisermos provisionar este mesmo ambiente em diversos servidores remotamente via SSH? Lembre-se que o ansible não tem agente ou cliente, o único pré-requisito a existência do SSH na máquina de destino a ser executado.

Vamos-lá! Primeiro precisamos editar o arquivo hosts do Ansible, onde determinamos os nossos grupos de hosts. Edite o arquivo /etc/ansible/hosts, e inclua no final da linha os endereços e o nome do seu grupo de hosts, como no meu exemplo abaixo:


No CentOS e Debian, execute os passos:

# vim /etc/ansible/hosts
[minhasmaquinas]
192.168.150.10
192.168.150.11
:x!

Criado o nosso grupo de hosts, primeiramente devemos incluir a nossa chave pública do ssh nas máquinas remotas seguindo os passos a seguir. Primeiramente geramos a chave e em seguida incluímos as mesmas dentro dos servidores.

Gerando a chave pública e privada do SSH com o comando abaixo. Aperte enter em todas opções, deixando a chave sem passphrase:


No CentOS e Debian, execute os passos:

# ssh-keygen

Gerada a chave é preciso adicionar a chave pública nas máquinas remotas da seguinte forma, e fornece a senha correta de root:


No CentOS e Debian, execute os passos:

# ssh-copy-id root@192.168.150.11
# ssh-copy-id root@192.168.150.10

Podemos validar se a comunicação entre nossa máquina e os servidores remotos estão perfeitas executando o comando abaixo. A saída deve ser parecida com essa:


No CentOS e Debian, execute os passos:

# ansible minhasmaquinas -m ping
192.168.150.11 | success >> {
    "changed": false, 
    "ping": "pong"
}
 
192.168.150.10 | success >> {
    "changed": false, 
    "ping": "pong"
}

E finalmente podemos testar o uso do nosso módulo nas máquinas remotas, onde ele deverá instalar o agente zabbix e configura-lo corretamente. Execute o comando abaixo e confira a saída:


No CentOS e Debian, execute os passos:

# ansible-playbook -l minhasmaquinas  tasks/main.yml
PLAY [Configurar e instalar agente zabbix] ************************************ 
 
GATHERING FACTS *************************************************************** 
ok: [192.168.150.10]
ok: [192.168.150.11]
 
TASK: [Adiciona variaveis] **************************************************** 
ok: [192.168.150.10]
ok: [192.168.150.11]
 
TASK: [Instalar repositorio CentOS] ******************************************* 
ok: [192.168.150.11]
ok: [192.168.150.10]
 
TASK: [Instalar repositorio Debian] ******************************************* 
ok: [192.168.150.11]
ok: [192.168.150.10]
 
TASK: [Executando zabbix.rpm] ************************************************* 
skipping: [192.168.150.11]
changed: [192.168.150.10]
 
TASK: [Executando zabbix.deb] ************************************************* 
skipping: [192.168.150.10]
changed: [192.168.150.11]
 
TASK: [Instalar agente zabbix CentOS] ***************************************** 
skipping: [192.168.150.11]
changed: [192.168.150.10]
 
TASK: [Instalar agente zabbix] ************************************************ 
skipping: [192.168.150.10]
changed: [192.168.150.11]
 
TASK: [Preparar arquivo de configuração] ************************************** 
changed: [192.168.150.11]
changed: [192.168.150.10]
 
NOTIFIED: [Iniciar agente zabbix] ********************************************* 
ok: [192.168.150.11]
changed: [192.168.150.10]
 
PLAY RECAP ******************************************************************** 
192.168.150.10             : ok=8    changed=4    unreachable=0    failed=0   
192.168.150.11             : ok=8    changed=3    unreachable=0    failed=0

7. Conhecendo outros módulos do Ansible

O Ansible possui uma variedade enorme de módulos que já vem nativos e também disponibiliza módulos extras que podem se obtidos e ter mais informações através de sua documentação oficial: http://docs.ansible.com/ansible/modules_by_category.html

Neste exemplo vamos instalar o MySQL e criar uma base de dados chamada “mydatabase”. No caso, no Debian iremos instalar o MySQL e no CentOS o MariaDB, que é o fork versão open do MySQL. Importante se atentar também aos pré-requisitos de cada módulo ou função usada pelo ansible, por exemplo, para o uso do MySQl precisamos instalar a biblioteca MySQLdb do python para que o ansible possa se conectar ao SGBD.

Todos os módulos oficiais disponíveis do ansible estão em sua documentação oficial, assim como esta do MySQL: http://docs.ansible.com/ansible/mysql_db_module.html

Então vamos editar novamente o nosso playbook e vamos adicionar o seguinte:


No CentOS e Debian, execute os passos:

# vim tasks/main.yml
(...)
- name: Instalar MySQLdb CentOS
    yum: name=MySQL-python state=latest
    when: ansible_distribution == "CentOS"
 
  - name: Instalar MySQLdb Debian
    apt: name=python-mysqldb state=latest
    when: ansible_os_family == "Debian"
 
  - name: Instalar MariaDB
    yum: name=mariadb-server state=latest
    when: ansible_distribution == "CentOS"
    notify:
     - Iniciar mariadb CentOS
 
  - name: Instalar MySQL
    apt: name=mysql-server state=latest
    when: ansible_os_family == "Debian"
    notify:
     - Iniciar mysql Debian
(...)
:x!

Em seguida vamos editar o handlers indicando o start do serviço MySQL nos sistemas e também acionar ele para criar a base de dados:


No CentOS e Debian, execute os passos:

# vim handlers/main.yml
(...)
  - name: Iniciar mariadb CentOS
    service: name=mariadb state=started enabled=yes
    notify:
     - Criar base de dados
 
  - name: Iniciar mysql Debian
    service: name=mysql state=started enabled=yes
    notify:
     - Criar base de dados
 
  - name: Criar base de dados
    mysql_db: name=mydatabase state=present
(...)
:x!

Após a edição, precisamos executar o playbook para validar as tarefas que incluímos.


No CentOS e Debian, execute os passos:

# ansible-playbook -l minhasmaquinas  tasks/main.yml
 
PLAY [Configurar e instalar agente zabbix] ************************************ 
 
GATHERING FACTS *************************************************************** 
ok: [192.168.150.10]
ok: [192.168.150.11]
 
TASK: [Adiciona variaveis] **************************************************** 
ok: [192.168.150.10]
ok: [192.168.150.11]
 
TASK: [Instalar repositorio CentOS] ******************************************* 
ok: [192.168.150.11]
ok: [192.168.150.10]
 
TASK: [Instalar repositorio Debian] ******************************************* 
ok: [192.168.150.11]
ok: [192.168.150.10]
 
TASK: [Executando zabbix.rpm] ************************************************* 
skipping: [192.168.150.11]
changed: [192.168.150.10]
 
TASK: [Executando zabbix.deb] ************************************************* 
skipping: [192.168.150.10]
changed: [192.168.150.11]
 
TASK: [Instalar agente zabbix CentOS] ***************************************** 
skipping: [192.168.150.11]
changed: [192.168.150.10]
 
TASK: [Instalar agente zabbix] ************************************************ 
skipping: [192.168.150.10]
changed: [192.168.150.11]
 
TASK: [Instalar MySQLdb CentOS] *********************************************** 
skipping: [192.168.150.11]
ok: [192.168.150.10]
 
TASK: [Instalar MySQLdb Debian] *********************************************** 
skipping: [192.168.150.10]
ok: [192.168.150.11]
 
TASK: [Instalar MariaDB] ****************************************************** 
skipping: [192.168.150.11]
changed: [192.168.150.10]
 
TASK: [Instalar MySQL] ******************************************************** 
skipping: [192.168.150.10]
changed: [192.168.150.11]
 
TASK: [Preparar arquivo de configuração] ************************************** 
changed: [192.168.150.11]
changed: [192.168.150.10]
 
NOTIFIED: [Iniciar agente zabbix] ********************************************* 
ok: [192.168.150.11]
changed: [192.168.150.10]
 
NOTIFIED: [Iniciar mariadb CentOS] ******************************************** 
changed: [192.168.150.10]
 
NOTIFIED: [Iniciar mysql Debian] ********************************************** 
ok: [192.168.150.11]
 
NOTIFIED: [Criar base de dados] *********************************************** 
changed: [192.168.150.10]
 
PLAY RECAP ******************************************************************** 
192.168.150.10             : ok=12   changed=7    unreachable=0    failed=0   
192.168.150.11             : ok=11   changed=4    unreachable=0    failed=0

E por fim, podemos conferir se a base de dados foi criada com sucesso.


No CentOS e Debian, execute os passos:

# mysql
(...)
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydatabase         |
| mysql              |
| performance_schema |
| test               |
+--------------------+
5 rows in set (0.00 sec)
 
MariaDB [(none)]>

8. Conclusão

Como podemos ver o Ansible é uma ótima solução de provisionamento de ambientes, bastante dinâmica e eficiente. Existe alguns contras, claro, como a exigente sintaxe e indentação do seu playbook. No entanto sua lógica é bastante simples. Além de possuir vantagens em relação a sua expansão, já que a própria documentação fornece ideais e sugestões para você desenvolver seus próprios módulos para serem usados em seus playbooks. Enfim, fico por aqui, e espero que essa dica seja útil para todos aqueles empenhados em aplicar essa solução em seu dia a dia.

Vagrant: Provisionamento ágil de Máquinas Virtuais

Como a própria HashiCorp, dona do projeto Vagrant descreve a ferramenta:

“Vagrant é uma ferramenta para a construção de ambientes de desenvolvimento completo. Com um fluxo de trabalho fácil de usar e se concentrar em automação, o Vagrant reduz o tempo de configuração do ambiente de desenvolvimento, aumenta o foco em desenvolvimento/produção, e faz com que o “preparação do ambiente de desenvolvimento” seja uma desculpa do passado.

Se você é um engenheiro de operações (Infraestrutura), Vagrant lhe da um ambiente descartável e um fluxo de trabalho consistente para desenvolver e testar scripts de gerenciamento de infra-estrutura. Você pode rapidamente testar coisas como shell scripts, cookbooks do chef, módulos do puppet, e muito mais, usando virtualização local, como o VirtualBox ou VMware. Então, com a mesma configuração, você pode testar esses scripts em nuvens remotas, como AWS ou RackSpace com o mesmo fluxo de trabalho. Vale seus scripts personalizados para reciclar instâncias EC2. Pare com o malabarismo com SSH entre várias máquinas, e comece a usar Vagrant, levando solidez à sua vida.

Se você é um Desenvolvedor, Vagrant irá isolar dependências e sua configuração dentro de um único ambiente descartável, consistente, sem sacrificar nenhuma das ferramentas que você está acostumado a trabalhar como (editores, navegadores, depuradores, etc.). Uma vez que você ou alguém crie um único Vagrantfile, você só precisa do Vagrant e tudo está instalado e configurado para você trabalhar. Outros membros de sua equipe podem criar seus ambientes de desenvolvimento a partir da mesma configuração, por isso se você estiver trabalhando em Linux, Mac OS X ou Windows, todos os membros da equipe estão executando o código no mesmo ambiente, contra as mesmas dependências, tudo configurado o mesmo caminho. Diga adeus aos erros ou “funciona na minha máquina”.”

1. Introdução ao Vagrant

Espero que você fique maravilhado com esta solução de provisionamento ágil de máquinas virtuais. Ideal para quem pratica bastante laboratórios e/ou faz uso de ambientes de desenvolvimento constantemente, e perde muito tempo instalando SO, preparando o ambiente, fazendo ajustes de configuração, etc.

O Vagrant permite que você possa automatizar tudo isso durante o provisionamento da máquina virtual, e o melhor de tudo, tudo com um único arquivo e um único comando! Além de facilitar em fazer o deploy deste mesmo ambiente para outro com Vagrant, com um pequeno arquivo com pouco mais de 4,0KBs!

Antes de começarmos a parte prática, precisamos entender que o Vagrant é apenas a solução de provisionamento de máquina virtual, e não o virtualizador, por isso precisamos do hypervisor já instalado na máquina. Dentro do conceito do Vagrant, o hypervisor é o que eles chamam de provider, o provider default (padrão) do Vagrant é o VirtualBox, no entanto o mesmo possui suporte a diversos outros fornecedores como VMware, Hyper-V, Docker, etc. Porém neste how-ho será feito com VirtualBox, a forma de instalação do VirtualBox não é coberta nesse how-to, porém garanto que é MUITO simples.

Qual o link disponível do Vagrant para Download? https://www.vagrantup.com/downloads.html

O Vagrant possui suporte a algumas plataformas, dentre elas:

  • CentOS
  • Debian
  • Windows
  • MacOS X

Neste mini how-to vamos abordar tanto em CentOS quanto em Debian. Caso deseja aprender em Windows e/ou MacOS X, segue alguns links de apoio:

  1. Usando o Vagrant como ambiente de desenvolvimento no Windows
  2. Getting Started with Vagrant on OSX 10.10 Yosemite

2. Nossa Primeira Máquina Virtual Auto Provisionada

Então chega de enrolação, e vamos lá! =D

Faça o download do pacote no link https://www.vagrantup.com/downloads.html e baixe o correspondente a sua distribuição.

O modo de instalação é básico, usando o gerenciador de pacotes de baixo nível da sua distribuição. E o melhor que o Vagrant não possui dependências.


No CentOS, execute:

# rpm -ivh vagrant_1.8.5_x86_64.rpm


No Debian, execute:

# dpkg -i vagrant_1.8.5_x86_64.deb

Feita a instalação, já podemos começar a brincar com o Vagrant.

O uso do Vagrant é feito com usuário “comum” do sistema, no meu caso, estou logado no meu notebook com o usuário “elvis”. Não precisa ser feito como root!

Crie um diretório chamado MeuAmbiente (Estes procedimentos abaixo são executados em ambas as distribuições):

$ mkdir MeuAmbiente

Entre no diretório recém criado, e crie o arquivo pilar deste projeto, o vagrantfile. Abaixo segue o conteúdo do mesmo e a explicação dele como comentário no arquivo.

$ cd MeuAmbiente
$ vim vagrantfile
Vagrant.configure(2) do |config|
        config.vm.box = "ubuntu/trusty64" # Distribuição e Versão do SO
        config.vm.hostname = "linuxsrv01" # Hostname da VM
        config.vm.network "forwarded_port", guest: 80, host: 8081 # Encaminhamento de Porta de 80 para 8081
        config.vm.network "public_network", ip: "192.168.0.18" # Configuro IP público estático.
 
        config.vm.provider "virtualbox" do |vb|
                vb.memory = "1024" # 1GB de Memória RAM
                vb.cpus = "1" # Quantidade Core de CPU
                vb.name = "linuxsrv01" # Nome da máquina Virtual no VirtualBox
        end
 
        # Provisionar um servidor web (httpd) Apache com suporte a PHP5.
        config.vm.provision "shell", inline: <

O próprio arquivo é bem claro, ele tem um começo (Vagrant.configure) e um fim (end), dentro desta cláusula passamos os parâmetros de provisionamento da máquina virtual a ser criada.

  • config.vm.box: é a imagem (Box) usada na máquina virtual, por exemplo centos/7, instala a máquina virtual com CentOS 7. Essas imagens são baixadas somente no primeiro provisionamento, em outras instalações que façam uso desta mesma imagem, não será mais necessário download. Mais informações podem ser obtidas em: https://www.vagrantup.com/docs/boxes.html
  • config.vm.hostname: Hostname da máquina virtual. Mais informações podem ser obtidas em: https://www.vagrantup.com/docs/vagrantfile/machine_settings.html
  • config.vm.network: Configurações de rede, encaminhamento de portas, etc. Mais informações podem ser obtidas em: https://www.vagrantup.com/docs/networking/basic_usage.html
  • config.vm.provider: Configurações passadas para o Hypervisor provisionar a máquina com a seguintes configurações especificadas. Mais informações podem ser obtidas em: https://www.vagrantup.com/docs/providers/basic_usage.html
  • config.vm.provision: Configura os passos de provisionamento da máquina virtual durante o primeiro boot. Este aceita diversos provisionadores como: Shell Script, Ansible, Chef e Puppet. Mais informações podem ser obtidas em: https://www.vagrantup.com/docs/provisioning/basic_usage.html

Agora que já entenderam os principais parâmetros do vagrantfile, precisamos apenas criar o arquivo index.php, com a função phpinfo, para coletar as evidências de que o módulo PHP5 já está disponível no Apache para iniciar o desenvolvimento e/ou testes de aplicação (por exemplo).

$ cd MeuAmbiente
$ vim index.php

O conteúdo do arquivo PHP:

<!--?php phpinfo(); ?-->

Com esta etapa pronta, podemos provisionar nossa primeira máquina. =D
O provisionamento é muito simples, dentro do diretório com o arquivo vagrantfile, execute o milagroso comando e aguarde alguns instantes:

$ vagrant up

Observe a saída do comando “vagrant up”, e nota que cada etapa que determinamos no arquivo vagrantfile estão sendo executadas uma a uma:

==&gt; default: Preparing to unpack .../php5-readline_5.5.9+dfsg-1ubuntu4.17_amd64.deb ...
==&gt; default: Unpacking php5-readline (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Selecting previously unselected package libaprutil1-dbd-sqlite3:amd64.
==&gt; default: Preparing to unpack .../libaprutil1-dbd-sqlite3_1.5.3-1_amd64.deb ...
==&gt; default: Unpacking libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
==&gt; default: Selecting previously unselected package libaprutil1-ldap:amd64.
==&gt; default: Preparing to unpack .../libaprutil1-ldap_1.5.3-1_amd64.deb ...
==&gt; default: Unpacking libaprutil1-ldap:amd64 (1.5.3-1) ...
==&gt; default: Selecting previously unselected package apache2-bin.
==&gt; default: Preparing to unpack .../apache2-bin_2.4.7-1ubuntu4.13_amd64.deb ...
==&gt; default: Unpacking apache2-bin (2.4.7-1ubuntu4.13) ...
==&gt; default: Selecting previously unselected package apache2-data.
==&gt; default: Preparing to unpack .../apache2-data_2.4.7-1ubuntu4.13_all.deb ...
==&gt; default: Unpacking apache2-data (2.4.7-1ubuntu4.13) ...
==&gt; default: Selecting previously unselected package apache2.
==&gt; default: Preparing to unpack .../apache2_2.4.7-1ubuntu4.13_amd64.deb ...
==&gt; default: Unpacking apache2 (2.4.7-1ubuntu4.13) ...
==&gt; default: Selecting previously unselected package libapache2-mod-php5.
==&gt; default: Preparing to unpack .../libapache2-mod-php5_5.5.9+dfsg-1ubuntu4.17_amd64.deb ...
==&gt; default: Unpacking libapache2-mod-php5 (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Selecting previously unselected package php5.
==&gt; default: Preparing to unpack .../php5_5.5.9+dfsg-1ubuntu4.17_all.deb ...
==&gt; default: Unpacking php5 (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Selecting previously unselected package ssl-cert.
==&gt; default: Preparing to unpack .../ssl-cert_1.0.33_all.deb ...
==&gt; default: Unpacking ssl-cert (1.0.33) ...
==&gt; default: Processing triggers for man-db (2.6.7.1-1ubuntu1) ...
==&gt; default: Processing triggers for ureadahead (0.100.0-16) ...
==&gt; default: Processing triggers for ufw (0.34~rc-0ubuntu2) ...
==&gt; default: Setting up libapr1:amd64 (1.5.0-1) ...
==&gt; default: Setting up libaprutil1:amd64 (1.5.3-1) ...
==&gt; default: Setting up libaprutil1-dbd-sqlite3:amd64 (1.5.3-1) ...
==&gt; default: Setting up libaprutil1-ldap:amd64 (1.5.3-1) ...
==&gt; default: Setting up apache2-bin (2.4.7-1ubuntu4.13) ...
==&gt; default: Setting up apache2-data (2.4.7-1ubuntu4.13) ...
==&gt; default: Setting up apache2 (2.4.7-1ubuntu4.13) ...
==&gt; default: Enabling module mpm_event.
==&gt; default: Enabling module authz_core.
==&gt; default: Enabling module authz_host.
==&gt; default: Enabling module authn_core.
==&gt; default: Enabling module auth_basic.
==&gt; default: Enabling module access_compat.
==&gt; default: Enabling module authn_file.
==&gt; default: Enabling module authz_user.
==&gt; default: Enabling module alias.
==&gt; default: Enabling module dir.
==&gt; default: Enabling module autoindex.
==&gt; default: Enabling module env.
==&gt; default: Enabling module mime.
==&gt; default: Enabling module negotiation.
==&gt; default: Enabling module setenvif.
==&gt; default: Enabling module filter.
==&gt; default: Enabling module deflate.
==&gt; default: Enabling module status.
==&gt; default: Enabling conf charset.
==&gt; default: Enabling conf localized-error-pages.
==&gt; default: Enabling conf other-vhosts-access-log.
==&gt; default: Enabling conf security.
==&gt; default: Enabling conf serve-cgi-bin.
==&gt; default: Enabling site 000-default.
==&gt; default:  * Starting web server apache2
==&gt; default: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
==&gt; default:  *
==&gt; default: Setting up ssl-cert (1.0.33) ...
==&gt; default: Setting up php5-json (1.3.2-2build1) ...
==&gt; default: php5_invoke: Enable module json for cli SAPI
==&gt; default: php5_invoke: Enable module json for apache2 SAPI
==&gt; default: Setting up php5-common (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Creating config file /etc/php5/mods-available/pdo.ini with new version
==&gt; default: php5_invoke: Enable module pdo for cli SAPI
==&gt; default: php5_invoke: Enable module pdo for apache2 SAPI
==&gt; default: Creating config file /etc/php5/mods-available/opcache.ini with new version
==&gt; default: php5_invoke: Enable module opcache for cli SAPI
==&gt; default: php5_invoke: Enable module opcache for apache2 SAPI
==&gt; default: Setting up php5-cli (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: update-alternatives:
==&gt; default: using /usr/bin/php5 to provide /usr/bin/php (php) in auto mode
==&gt; default: Creating config file /etc/php5/cli/php.ini with new version
==&gt; default: php5_invoke json: already enabled for cli SAPI
==&gt; default: php5_invoke pdo: already enabled for cli SAPI
==&gt; default: php5_invoke opcache: already enabled for cli SAPI
==&gt; default: Setting up php5-readline (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Creating config file /etc/php5/mods-available/readline.ini with new version
==&gt; default: php5_invoke: Enable module readline for cli SAPI
==&gt; default: php5_invoke: Enable module readline for apache2 SAPI
==&gt; default: Processing triggers for ureadahead (0.100.0-16) ...
==&gt; default: Processing triggers for ufw (0.34~rc-0ubuntu2) ...
==&gt; default: Setting up libapache2-mod-php5 (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Creating config file /etc/php5/apache2/php.ini with new version
==&gt; default: php5_invoke readline: already enabled for apache2 SAPI
==&gt; default: php5_invoke json: already enabled for apache2 SAPI
==&gt; default: php5_invoke pdo: already enabled for apache2 SAPI
==&gt; default: php5_invoke opcache: already enabled for apache2 SAPI
==&gt; default: Module mpm_event disabled.
==&gt; default: Enabling module mpm_prefork.
==&gt; default: apache2_switch_mpm Switch to prefork
==&gt; default:  * Restarting web server apache2
==&gt; default: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
==&gt; default:    ...done.
==&gt; default: apache2_invoke: Enable module php5
==&gt; default:  * Restarting web server apache2
==&gt; default: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName' directive globally to suppress this message
==&gt; default:    ...done.
==&gt; default: Setting up php5 (5.5.9+dfsg-1ubuntu4.17) ...
==&gt; default: Processing triggers for libc-bin (2.19-0ubuntu6.9) ...
==&gt; default: ‘/vagrant/index.php’ -&gt; ‘/var/www/html/index.php’

Após alguns instantes, podemos testar se o Apache esta no ar com o PHP 5 habilitado na máquina Virtual, como? Simples, abra seu navegador e acesse http://127.0.0.1:8081, lembre-se que fizemos o forwarding da máquina 8081 da máquina física para 80 da máquina virtual.


Nota-se que o arquivo index.php foi criado no mesmo diretório que continha o arquivo vagrantfile, durante o provisionamento, o vagrant copia tudo que tem dentro deste diretório da máquina física para dentro do diretório /vagrant, dentro da máquina virtual. Caso seja máquina CentOS em algumas versões antigas, ele sincroniza no diretório /home/vagrant/sync.

Além disso, é possível visualizar a máquina criada, diretamente no VirtualBox:

3. Outros comandos úteis do Vagrant

É possível acessar a máquina virtual provisionada via SSH pelo próprio vagrant, através do comando vagrant ssh (dentro do diretório da vm correspondente)

$ vagrant ssh
Welcome to Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-92-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com/
 
 System information disabled due to load higher than 1.0
 
  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud
 
vagrant@linuxsrv01:~$ sudo su
root@linuxsrv01:/home/vagrant#

Este mesmo comando pode ser usado para mais de uma máquina, quando se quer acessar um host em específico execute vagrant ssh nomedamaquina:

$ vagrant ssh linuxsrv01
Welcome to Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-92-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com/
 
 System information disabled due to load higher than 1.0
 
  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud
 
vagrant@linuxsrv01:~$

Caso deseje desligar a máquina, é possível desligar a mesma diretamente pelo próprio Vagrant, através do comando “vagrant halt”.

$ vagrant halt
==&gt; default: Attempting graceful shutdown of VM...

Em um cenário com multiplas máquinas virtuais, é possível desligar uma única máquina em específica:

$ vagrant halt linuxsrv01
==&gt; linuxsrv01: Attempting graceful shutdown of VM...

Além disso, é possível também remover a máquina virtual:

$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==&gt; default: Destroying VM and associated drives...

Em um cenário com multiplas máquinas virtuais, é possível remover uma única máquina em específica:

$ vagrant destroy linuxsrv01
    linuxsrv01: Are you sure you want to destroy the 'linuxsrv01' VM? [y/N] y
==&gt; linuxsrv01: Destroying VM and associated drives...

4. Adicionando novas Boxes (Imagens) no Vagrant.

Como explicado acima, quando feito o provisionamento da máquina virtual pela primeira vez, com aquela respectiva imagem da versão da distribuição ou sistema, o vagrant faz o download direto do repositório oficial (https://atlas.hashicorp.com/boxes/search), outras máquinas virtuais que forem fazer uso da mesma imagem da versão da distribuição ou sistema, não precisara fazer novamente o download, poupando esforço.

Porém existe também forma de adicionar imagens customizadas pela comunidade direto no seu Vagrant, por exemplo:

$ vagrant box add "OpenBSD 5.3 64-bit" https://dl.dropboxusercontent.com/u/12089300/VirtualBox/openbsd53_amd64.box

No link a seguir http://www.vagrantbox.es/ possui diversas Boxes (imagens) prontas para uso com Vagrant. Sinta-se a vontade para brincar com elas. \o/

5. Trabalhando como Multiplas Máquinas.

É possível construir cenários complexos, com mais de uma máquina virtual como no exemplo feito. Abaixo faremos um cenário onde teremos um balanceador de carga com Nginx e dois nós atrás com Apache (httpd) 2.4 trabalhando integrados.

O Vagrantfile multi-machine, fica configurado conforme abaixo, primeiramente crie o arquivo Vagrantfile e adicione o seguinte conteúdo:

$ vim Vagrantfile
Vagrant.configure("2") do |config|
 
        # Balanceador de Carga
        config.vm.define "nginx01" do |nginx01|
        nginx01.vm.box = "centos/7" # Distribuição e Versão do SO
        nginx01.vm.hostname = "nginx01" # Hostname da VM
        nginx01.vm.network "private_network", ip: "192.168.50.2"
        nginx01.vm.network "forwarded_port", guest: 80, host: 8080 # Encaminhamento de Porta de 8080 (Física) para 80 (VM)
 
 
        nginx01.vm.provider "virtualbox" do |vb|
                vb.memory = "512" # 512MB de Memória RAM
                vb.cpus = "1" # Quantidade Core de CPU
                vb.name = "nginx01" # Nome da máquina Virtual no VirtualBox
        end
 
                # Provisiona Nginx como Balanceador de Carga
                nginx01.vm.provision "shell", :path =&gt; "scripts/nginx.sh"
        end
 
        # Node app01
        config.vm.define "app01" do |app01|
                app01.vm.box = "centos/7" # Distribuição e Versão do SO
                app01.vm.hostname = "app01" # Hostname da VM
                app01.vm.network "private_network", ip: "192.168.50.3"
 
                app01.vm.provider "virtualbox" do |vb|
                        vb.memory = "512" # 512MB de Memória RAM
                        vb.cpus = "1" # Quantidade Core de CPU
                        vb.name = "app01" # Nome da máquina Virtual no VirtualBox
                end
 
                 # Provisiona Apache em Node 01
                app01.vm.provision "shell", :path =&gt; "scripts/apache2.sh"
        end
 
 
        # Node app02
        config.vm.define "app02" do |app02|
                app02.vm.box = "centos/7" # Distribuição e Versão do SO
                app02.vm.hostname = "app02" # Hostname da VM
                app02.vm.network "private_network", ip: "192.168.50.4"
 
                app02.vm.provider "virtualbox" do |vb|
                        vb.memory = "512" # 512MB de Memória RAM
                        vb.cpus = "1" # Quantidade Core de CPU
                        vb.name = "app02" # Nome da máquina Virtual no VirtualBox
                end
 
                 # Provisiona Apache em Node 02
                app02.vm.provision "shell", :path =&gt; "scripts/apache2.sh"
        end
 
end

Note que estamos criando tês maquinas Virtuais, uma chamada nginx01 e outras duas chamadas app01 e app02. No Nginx estamos instalando e configurando o balanceador de carga e nos apps estamos apenas instalando o apache e adicionando uma index simples.

Mas mesmo assim não fica evidente onde esta sendo executado estes procedimentos, na verdade eles estão sendo feito nos scripts, assim como a configuração também esta dentro dos scripts que estão sendo chamados pelo provisionador. Para preparar o cenário, crie dois diretórios:

  • conf
  • scripts

Execute:

$ mkdir conf
$ scripts

Adicione os dois arquivos abaixo dentro conf:

  • nginx.repo
  • balancer.conf

Execute:

$ vim conf/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
$ vim conf/balancer.conf
# Pool do Load balancer
upstream pool.linuxsysadmin.com.br {
        server 192.168.50.3;
        server 192.168.50.4;
}
 
# Informações de proxy passadas no cabeçalho HTTP
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Queue-Start “t=${msec}000;
 
# Proxy Pass do balanceador
server {
    listen       80;
    server_name  localhost;
 
        location / {
                proxy_pass http://pool.linuxsysadmin.com.br;
        }
}

Em seguida copie os dois scripts abaixo para dentro do diretório scripts.

$ vim scripts/nginx.sh
#!/bin/bash
 
if [ -e "/vagrant" ]; then
 
        VAGRANT_HOME="/vagrant"
 
elif [ -e "/home/vagrant/sync" ]; then
 
        VAGRANT_HOME="/home/vagrant/sync"
 
else
 
        echo "Not sync folder"
        exit 1
 
fi
 
# Desabilita o SELinux.
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
 
# Copia o repositório oficial do Nginx para a VM.
cp -v /vagrant/conf/nginx.repo /etc/yum.repos.d
 
# Instalar Nginx.
yum -y install nginx
 
# Configura o Balanceador de Cagra.
mv -fv ${VAGRANT_HOME}/conf/balancer.conf /etc/nginx/conf.d/
 
# Subindo o serviço Nginx
systemctl restart nginx
systemctl enable nginx

$ vim scripts/apache2.sh
#!/bin/bash
 
# Determina qual o Sync Folder do Vagrant
if [ -e "/vagrant" ]; then
 
        VAGRANT_HOME="/vagrant"
 
elif [ -e "/home/vagrant/sync" ]; then
 
        VAGRANT_HOME="/home/vagrant/sync"
 
else
 
        echo "Not sync folder"
        exit 1
 
fi
 
# Desabilita o SELinux.
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0
 
# Instala o apache
yum -y install httpd
 
# Cria a pagina index.html
echo "$(hostname)" &gt; /var/www/html/index.html
 
# Subindo o serviço apache
systemctl restart httpd
systemctl enable httpd

Desta forma organizamos o nosso Vagrantfile, ficando menos poluído, limpo. O conceito continua sendo o mesmo, sendo a única diferença que ao invés de passarmos vários comandos para o vagrant, apenas fazemos ele chamar um único script.

Com essa estrutura montada, basta executar “vagrant up”, a saída deverá ser parecida com a tela abaixo:

$ vagrant up
Bringing machine 'nginx01' up with 'virtualbox' provider...
Bringing machine 'app01' up with 'virtualbox' provider...
Bringing machine 'app02' up with 'virtualbox' provider...
(...)

Em meu notebook este ambiente foi criado em menos 8 minutos, levando em consideração que meu notebook possui uma configuração básica de mercado, em máquinas mais “parrudas” este ambiente seria construindo muito mais rápido. Outro fator é que eu não precisei baixar a imagem do CentOS 7, já que eu havia instalado este SO, se fosse a primeira vez provavelmente seria feito mais rápido.

Após o ambiente ser criado, basta adicionar em seu /etc/hosts na última linha:

$ sudo vim /etc/hosts
(...)
127.0.0.1       pool.linuxsysadmin.com.br

E acesse seu navegador:

Pode executar F5 para atualizar a página e verificar nginx balanceia as requisições entre os dois nodes app01 e app02. =)

Para mais informações sobre como trabalhar com o modo “Multi-machines”: https://www.vagrantup.com/docs/multi-machine/

6. Conclusão.

Bem, espero que você tenha gostado do post, e principalmente que lhe seja útil em sua jornada e carreira profissional para agilizar seus ambientes, focando no que realmente interessa, sem perder tempo com aquela dor de cabeça de criar máquina virtual, instalar o sistema operacional, preparar o ambiente, instalar e configurar dependências, etc. rs

Forte abraço e sucesso!