Geral, Linux, RHEL, Tools, Virtualização

Integrando o Vagrant com libvirt/KVM no RHEL/CentOS 7

Primeiro o que é Vagrant?

Vagrant é uma ferramenta utilizada para criação e configuração de ambientes de trabalho (desenvolvimento, testes, homologação, produção etc) de forma configurável, reproduzível, portável, isolada  e automatizada. Humm… lembra um pouco algumas características do Docker, não?

Bom, em partes sim! Porém para um ambiente que utiliza virtualização tradicional. Docker e seu ecosistema tem avançado em uma velocidade absurda. Mas creio que ainda levará um tempo para o mercado corporativo começar a adotar Docker em larga escala em seus Data Centers privados. Virtualização tradicional tem vida longa ainda e, na minha opinião, irá coeexistir junto com iniciativas de Cloud baseadas em Containers.

Descrevendo de uma forma um pouco mais clara… Vagrant permite que você:

  • crie imagens (Boxes) baseadas em um Sistema Operacional qualquer
  • instale e configure pacotes/recursos utilizando uma ferramenta de automação (Provisioner) – também chamada de Configuration Management Tool. Vagrant suporta diversas dessas ferramentas: shell, chef, puppet, ansible.
  • provisione essa imagem em um hypervisor (Provider) específico: VirtualBox, VMWare, HyperV, KVM. Vagrant poder ser integrado inclusive com Cloud Providers como AWS, Digital Ocean etc.

Bom, Não vou me estender na descrição da ferramenta neste post. Para mais detalhes veja a Vagrant doc oficial.

Quando você lê o a documentação oficial do site, percebe que os providers oficialmente suportados e disponíveis Out of Box pelo Vagrant são: VirtualBox o Vmware.

Pô! Uso Linux e meu Hypervisor é o KVM! Como fazer com que o Vagrant utilize o KVM como provider?

Graças à API libvirt e o mecanismo de Vagrant plugins, existe um plugin que permite essa integração: vagrant-libvirt

Neste post descrevo os passos para instalar e configurar o Vagrant integrado com libvirt/KVM em um Host RHEL 7/Centos 7.redhat-logo

Infelizmente até o momento o Vagrant não está disponível no reposiório oficial da Red Hat. Entretanto ele está disponível no próprio site do projeto Vagrant, ná página Vagrant downloads.

Após baixar o RPM para seu host instale utilizando o yum.

sudo yum localinstall ~/Downloads/vagrant_1.7.4_x86_64.rpm

Confira a instalação

vagrant --version
Vagrant 1.7.4

Instale os seguintes pacotes necessários para plugin do provider (libvirt):

sudo yum install libxslt-devel libxml2-devel libvirt-devel libguestfs-tools-c

Em seguida instale o plugin do libvirt e o plugin nfs_guest.

vagrant plugin install vagrant-libvirt vagrant-nfs_guest

Installing the ‘vagrant-libvirt’ plugin. This can take a few minutes…
Installed the plugin ‘vagrant-libvirt (0.0.30)’!

Verifique a lista de plugins instalados:

vagrant plugin list

vagrant-kvm (0.1.9)
vagrant-libvirt (0.0.30)
vagrant-nfs_guest (0.1.5)
vagrant-share (1.1.4, system)

Ok! O próximo passo é começar a provisionar VMs. Na terminologia do Vagrant VMs são Boxes. Assim como o Docker hub, existe um repositório público de Boxes chamado Atlas Hashcorp. Basta buscar por um Box usando o nome do sistema operaciona ou o nome do provider. Ex: procure por imagens criadas para libvirt.

Vamos adicionar uma Box em meu host local. Eu escolhi uma imagem baseada no Centos 7 criada pelo usuário uvsmtid.

vagrant vagrant box add --insecure uvsmtid/centos-7.0-minimal
==> box: Loading metadata for box 'uvsmtid/centos-7.0-minimal'
box: URL: https://atlas.hashicorp.com/uvsmtid/centos-7.0-minimal
==> box: Adding box 'uvsmtid/centos-7.0-minimal' (v1.0.0) for provider: libvirt
box: Downloading: https://atlas.hashicorp.com/uvsmtid/boxes/centos-7.0-minimal/versions/1.0.0/providers/libvirt.box
==> box: Successfully added box 'uvsmtid/centos-7.0-minimal' (v1.0.0) for 'libvirt'!

Diferentemente de uma Docker Image os Vagrant Boxes são imagens de VMs tradicionais! Portanto são bem maiores. O download pode demorar um pouco. Aguarde!

Verifique as imagens adicionadas em seu host com o comando abaixo

vagrant box list
uvsmtid/centos-7.0-minimal (libvirt, 1.0.0)

Agora é necessário criar um descritor chamado VagrantFile. Esse cara é uma espécie de Dockerfile e através dele o Vagrant faz toda a sua mágica de provisionamento da VM.
Crie um diretório (como um projeto) para hospedar o descritor e os metadados para as suas Vagrant Boxes.

mkdir -p ~/vagrant/centos7-minimal
cd ~/vagrant/centos7-minimal

vagrant init uvsmtid/centos-7.0-minimal
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Veja que o Vagrant criou um descritor Vagrantfile no diretório atual e um diretório oculto (.vagrant) para armazenar metadados (arquivos criados durante a inicialização da).

➜  tree -a .
.
├── .vagrant
│   └── machines
│       └── default
│           └── libvirt
│               ├── created_networks
│               ├── creator_uid
│               ├── id
│               ├── index_uuid
│               └── private_key
└── Vagrantfile

Agora basta executar o comando:

vagrant up

Bringing machine 'default' up with 'libvirt' provider...
==> default: Creating image (snapshot of base box volume).
==> default: Creating domain with the following settings...
==> default:  -- Name:              centos7-minimal_default
==> default:  -- Domain type:       kvm
==> default:  -- Cpus:              1
==> default:  -- Memory:            512M
==> default:  -- Base box:          uvsmtid/centos-7.0-minimal
==> default:  -- Storage pool:      default
==> default:  -- Image:             /var/lib/libvirt/images/centos7-minimal_default.img
==> default:  -- Volume Cache:      default
==> default:  -- Kernel:
==> default:  -- Initrd:
==> default:  -- Graphics Type:     vnc
==> default:  -- Graphics Port:     5900
==> default:  -- Graphics IP:       127.0.0.1
==> default:  -- Graphics Password: Not defined
==> default:  -- Video Type:        cirrus
==> default:  -- Video VRAM:        9216
==> default:  -- Keymap:            en-us
==> default:  -- Command line :
==> default: Pruning invalid NFS exports. Administrator privileges will be required...
==> default: Creating shared folders metadata...
==> default: Starting domain.
==> default: Waiting for domain to get an IP address...
==> default: Waiting for SSH to become available...
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Configuring and enabling network interfaces...
==> default: Rsyncing folder: /home/rsoares/vagrant/centos7-minimal/ => /vagrant

Voila!
Sua VM Centos7 UP and Running!

NOTA: na época da escrita desse post a última release do plugin vagrant-libvirt era a 0.0.30. Tem um bug nessa versão documentado em: https://github.com/pradels/vagrant-libvirt/issues/412. Nos comentários da issue tem um trecho de código (Ruby) que resolve o problema. Copie o trecho de código e altere o arquivo ‘ ~/.vagrant.d/gems/gems/vagrant-libvirt-0.0.30/lib/vagrant-libvirt/action/prepare_nfs_settings.rb. Salve o arquivo e execute o vagrant up novamente.

Para acessar a VM:

vagrant ssh

Para ver um status das VMs gerenciadas pelo Vagrant no host atual:

vagrant global-status
id       name    provider state   directory
------------------------------------------------------------------------
5b3e766  default libvirt running /home/rsoares/vagrant/centos7-minimal

The above shows information about all known Vagrant environments
on this machine. This data is cached and may not be completely
up-to-date. To interact with any of the machines, you can go to
that directory and run Vagrant, or you can use the ID directly
with Vagrant commands from any directory. For example:
"vagrant destroy 1a2b3c4d"

Para efetuar um shutdown na VM:

vagrant halt
==> default: Halting domain...

Para eliminar a VM:

vagrant destroy
==> default: Removing domain...

Pode destruir sem dó! A ideia do Vagrant é essa! Criar e recriar ambientes de forma rápida, automatizada sem stress! 🙂

Bom, por hoje é só! Há muito mais para falar do Vagrant e suas funcionalidades. Em um próximo post quero abordar a questão da portabilidade entre os diferentes providers (Hypervisors). Tipo, como portar essa mesma imagem que foi provionada em cima do KVM (com o plugin libvirt) para o provider VMWare ou VirtualBox? É possível!


Referências utilizadas neste post:

Dicas, Java, JVM, Tools

Colhendo envidências em caso de erro (crash) na JVM

Sabe aquela situação em que a equipe responsável pelo ambiente de produção chega pra você e diz: “o servidor X está caindo toda hora… já olhamos os recursos da máquina (rede, memória, cpu e disco), inclusive o log do servidor, mas não identificamos nada…”

Isso mesmo! Há casos em que seu processo caiu sem dar explicações e sem deixar pista alguma.

Já tive a oportunidade de passar por essa situação algumas vezes… Trata-se de uma situação complicada, pois quase sempre ocorre em um momento e ambiente crítico onde o tempo para solução é o seu pior inimigo. Bem, após bater cabeça com esse tipo de problema, posso afirmar que a melhor opção é preparar o seu ambiente para colher algumas evidências quando o problema voltar a ocorrer. Como quase tudo na TI esse tipo de situação é inevitável. Softwares e Hardware podem falhar por “n” motivos. Nesse post deixo algumas dicas que podem lhe ajudar na investigação “pós queda” de um processo Java (rodando em cima de uma JVM).

Existem dois casos em que é bastante comum o processo da JVM cair:

  1. insuficiência de memória
  2. algum erro fatal da jvm

O tipo de erro relacionado à insuficiência de memória é o conhecido por OutOfMemoryError (OOM) e ocorre devido à algum memory leak no código da aplicação. Para analisar a causa desse tipo de erro é interessante ter em mãos o estado (snapshot) do Heap da JVM no momento em que o erro ocorre. Para isso é necessário habilitar alguns parâmetros na JVM conforme abaixo:

Geração do Dump (formato hprof) do Heap da JVM.

Adicionar as seguintes propriedades no comando que inicia a JVM. Caso o comando seja parametrizado em um arquivo de configuração, basta usar uma variável JAVA_OPTS como no exemplo abaixo.

# HeapDUMP e Core DUMP
# registra a data e hora de início do processo
DATE_START=`date  +%d-%m-%Y-%k%M`

# Habilita a geração do DUMP de memória (HPROF)
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError "
JAVA_OPTS="$JAVA_OPTS -XX:OnOutOfMemoryError=<execute algum comando shell aqui (ex: 'kill -9%p')> "
JAVA_OPTS="$JAVA_OPTS -XX:HeapDumpPath=/var/log/jvm/heapdump_$DATE_START.hprof "

 

NOTA:

  • Recentemente encontrei uma implementação alternativa à opção ‘-XX:OnOutOfMemoryError’ interessante. Vale a pena testar!
  • o arquivo de dump no formato .hprof pode ser aberto com os utilizatários jVisualvm (fornecido pelo JDK Sun/Oracle Hotspot) ou Eclipse Memory Analizer (MAT). É necessário que a versão e arquitetura do JDK utilizado na análise dump seja idêntica à JVM utilizada pelo servidor de aplicação onde o erro ocorreu.

O segundo caso de erro pode ser gerado por algum problema entre a JVM e SO. Um caso típico é o erro de estouro de pilha conhecido como StackOverflowError. Esse tipo de erro pode ocorrer caso a aplicação utilize algum tipo de loop infinito ou uma recursividade muito profunda. O tamanho da pilha alocada pela JVM durante a criação de uma thread java é de 1024KB (1mb) em um sistema Linux x64. É possível alterar esse valor para mais ou para menos a depender da necessidade da aplicação. O parâmetro da JVM -Xss:<n>k altera o valor da pilha, sendo que <n> é um valor inteiro.

Geração do coredump (threads e memória) da JVM

Adicionar as seguintes propriedades no comando que inicia a JVM.

# Habilita a geração de coredump (BIN)
JAVA_OPTS="$JAVA_OPTS -XX:OnError='gcore -o /var/log/jvm/jboss_PID%p_$DATE_START.coredump %p' "
JAVA_OPTS="$JAVA_OPTS -XX:ErrorFile=/var/log/jvm/hs_err_jboss_PID%p_$DATE_START.log "

NOTAS:

  • A opção ‘-XX:OnError‘ é executada apenas quando  a causa raiz do erro for Nativo devido a alguma falha fora da JVM – JVM Native Crash.
  • o arquivo de dump no formato hprof pode ser aberto com os utilizatários jVisualvm (fornecido pelo JDK) ou Eclipse Memory Analizer (MAT).  É necessário que a versão e arquitetura do JDK utilizado na análise dump seja idêntica à JVM utilizada pelo servidor de aplicação onde o erro ocorreu.
  • Para que o comando gcore (invocado pela JVM após o evento de erro) funcione é necessário que o pacote gdb (A GNU source-level debugger for C, C++, Fortran and other languages) esteja devidamente instalado no Sistema Operacional.

Uma opção mais eficiente para geração de Dumps em JVMs com Heaps grandes (>= 2gb)

Utilizar o jmap em heap muito grandes pode demorar horas para concluir. Em algumas situações é necessário reiniciar o serviço imediatamente após a falha para diminuir o tempo em que ele fica fora do ar. Uma excelente alternativa é descrita nesse artigo disponível no Blog da Atlassian: So you want your JVM’s heap…

Gerando dumps da JVM em tempo de runtime manualmente

O formato binário pode ser aberto com as ferramentas: jstack (JDK), jVisualVM (JDK), Eclipse MAT ou qualquer outro profiler Java que reconheça o formato HPROF.

jmap -dump:live,format=b,file=jvm_heap.bin <PID>

O formato texto pode ser aberto com as ferramentas: jVisualVM (JDK), IBM Thread Analizer ou qualquer outro profiler Java que reconheça dump de threads Java.

jstack -F -l <PID> > /tmp/jvm_thread.dump

NOTA: os procedimentos acima causam o travamento das threads (equivalente ao efeito Stop The World do FullGC). Portanto utilize com cautela em ambiente de produção!

A análise dos dumps citados neste post é assunto para um novo post. Existem várias ferramentas que podem ser utilizadas na análise. Além das ferramentas é importante ter conhecimento da arquitertura da Máquina Virtual Java utilizada. No caso do dump de memória (heap) é importante conhecer a divisão geracional dos pools de memória da JVM , bem como os vários mecanismos de coleta de lixo. Para o dump de threads é importante conhecer um pouco sobre a execução de threads na plataforma Java: sincronização, pilha de execução, thread monitor, thread status (WAINTING, RUNNING, BLOCKED, etc). Com a ferramenta certa e com um pouco de paciência é possível chegar a tão desejada causa raiz do problema: nome da classe, nome do método, nome da biblioteca ou até mesmo a linha de código que causa ou influência o crash do processo Java.

Java, Segurança Digital, Tools

Manutenção em Java Key Stores (JKS) usando o Keytool

Manutenção de Kestores já existentes

Em certas situações é necessário realizar uma manutenção em um keystore já existente no servidor. A manutenção pode ser motivada pela necessidade de importar/exportar uma chave/certitificado de outro ambiente no keystore em questão.

Segue abaixo alguns comandos úteis da ferramenta keytool (fornecida pelo JDK da Sun/Oracle)

Listar o conteúdo de um keystore

Antes de qualquer manutenção no keystore é interessante listar o seu conteúdo.

  • O comando abaixo lista todos os certificados armazenados no keystore (JKS) informado.
keytool -list -keystore server_keystore.jks

A saída do comando deve ser semelhante a apresentada pela listagem abaixo:

 *****************  WARNING WARNING WARNING  *****************                                                                                                                   
 * The integrity of the information stored in your keystore  *
 * has NOT been verified!  In order to verify its integrity, *
 * you must provide your keystore password.                  *
 *****************  WARNING WARNING WARNING  *****************

 Keystore type: JKS
 Keystore provider: SUN

 Your keystore contains 4 entries

 server1.domain.com, Mar 26, 2012, PrivateKeyEntry, 
 Certificate fingerprint (MD5): A7:8B:B7:9A:11:50:CC:98:4C:B5:89:BA:04:3B:CD:AD
 server2.domain.com, Mar 26, 2012, PrivateKeyEntry, 
 Certificate fingerprint (MD5): 2F:A2:50:65:C9:B2:2B:16:C0:2E:10:8A:8C:AB:50:E0
 server3.domain.com, Mar 26, 2012, PrivateKeyEntry, 
 Certificate fingerprint (MD5): 98:CC:AF:88:82:85:07:9A:23:FA:3C:FB:AD:1C:14:62
 server4.domain.com, Mar 26, 2012, PrivateKeyEntry, 
 Certificate fingerprint (MD5): 84:57:8C:40:03:12:F1:56:0B:89:9D:1E:20:E6:48:B5

Exportar um certificado específico

  • No exemplo abaixo o certificado (identificado pelo alias server3.domain.com) armazenado no keystore server_keystore.jks será exportado para o arquivo server3.cer
keytool -export -alias server3.domain.com -keystore server_keystore.jks -file server3.cer

Importar um certificado específico

De posse do certificado exportado no passo anterior basta usar o comando keytool –import para importá-lo em um keystore (JKS) diferente.

  • No exemplo abaixo o certificado (identificado pelo alias server1.domain.com) armazenado no arquivo server1.cer será importado dentro do keystore client_keystore.jks
keytool -import -alias server1.domain.com -file server1.cer -keystore client_keystore.jks

Clonar um certificado específico

Caso seja necessário clonar uma determinada chave/certificado armazenado no keystore basta utilizar o comando keytool -keyclone.

  • No exemplo abaixo o certificado identificado pelo alias server3.domain.com será clonado com um novo alias (clone_server3.domain.com) dentro do keystore server_keystore.jks
keytool -keyclone -alias server3.domain.com -dest clone_server3 -keystore server_keystore.jks

obs:
1) você será questionado quanto a senha que deseja definir para a chave clonada. Caso queira manter a senha da chave original (clonada) confirme com ‘<Enter>’.
2) a opção ‘-keyclone’ funciona apenas para certificados contendo o par de chaves pública/privada. Não é possível clonar (duplciar) chaves públicas.

Excluir um certificado específico

Caso seja necessário excluir uma determinada chave/certificado armazenado no keystore basta utilizar o comando keytool -delete.

  • No exemplo abaixo o certificado identificado pelo alias server1.domain.com será excluído do keystore server_keystore.jks
keytool -delete -alias server1.domain.com -keystore server_keystore.jks

Renomear (altear o alias) um certificado específico

Caso seja necessário renomear (alterar o alias) uma determinada chave/certificado armazenado no keystore basta utilizar o comando keytool -changealias.

  • No exemplo abaixo o certificado identificado pelo alias server1.domain.com será renomeado para o alias serverX no keystore server_keystore.jks
keytool -changealias -alias server1.domain.com -destalias serverX.domain.com -keystore server_keystore.jks
  • para mais detalhes sobre a ferramenta keytool consulte a documentação oficial do JDK disponível em [1]
Curiosidades, Tools

Os grandes também usam software proprietário…

Esses dias navegando de bobeira encontrei no rodapé de uma página da RedHat Support o link de uma empresa que trabalha com ferramentas de colaboração e de apoio ao desenvolvimento de software. Essa empresa é a Atlassian.com.

Com certeza você já caiu em uma aplicação deles buscando um código fonte ou informações sobre um bug de alguma ferramenta pela web…

Já ouviu falar em JIRA, Confluence, FishEye, etc? Pois é! São ferramentas desenvolvidas e comercializadas pela Atlassian. O interessante é que olhando o Customer list dos caras, podemos ver grandes playes, inclusive do mundo OSS, utilizando tais softwares. Provavelmente esses players pensaram: “Desenvolver ou Adquirir?” lá do Gerência de Aquisição do PMBOK…