/dev/sre

Marcelo Andrade

Sysop 2 SRE

GOVC

Como explorar o VMWare pela linha de comando

Marcelo Andrade

9 Minutos De Leitura

If all you have is a hammer, everything looks like a nail.

O projeto govmomi oferece uma biblioteca em Go Para interação com as APIs VMWare Vsphere (ESXi ou vCenter). O programa govc é o binário que te permite executar coisas na linha de comando.

Eu nunca implantei VMWare do zero; conheço um emaranhado de conceitos usando a técnica que todo ultrageneralista usa: sair aprendendo uma série de coisas desconexas sobre o objeto de estudo que são o suficiente para desempenhar determinada tarefa, torcendo para que, algum dia, eu possa voltar àquele assunto e completar o que falta.

Ironicamente, tem funcionado.

O objetivo de hoje? Descobrir tudo que existe no ambiente e onde estão minhas coisas já criadas.

Por que isso? Para implementar a automação com Terraform. Eu preciso de um monte de informações, e me recuso a usar interfaces gráficas coloridas. Isso é contra os princípios de todo bom #Ops.

Abrindo a caixa de ferramentas

A minha caixa de ferramentas, na verdade, vem com um único martelo. Portanto, daqui para frente, tudo é prego.

Existem diversas opções viáveis para administrar VMWare a partir da linha de comando. Não é o que eu estou procurando. Eu quero algo que seja capaz de me trazer as informações necessárias sobre o meu ambiente para que eu possa viabilizar o uso do Terraform, já que as estruturas de dados (objetos ‘data') que ele pode importar precisam que eu especifique nomes e IDs.

Para usar o govc, é necessário configurar algumas variáveis de ambiente e um conjunto de credenciais:

# # Deixe de ser preguiçoso e importe a CA auto assinada na sua máquina em vez de usar GOVC_INSECURE
# export GOVC_INSECURE=true

# export GOVC_URL=https://endereco-ip-do-seu-vcenter:443

# # Credenciais. Não preciso dizer que você não deve fazer isso em uma máquina com root compartilhado...
# export GOVC_USERNAME=$USER
# export GOVC_PASSWORD='Mnh$nh@2020@69'

Peça para os administradores VMWare criarem um usuário com ‘readonly’ no cluster. Não precisamos de mais nada além disso para fazer a exploração.

Existem maneiras mais inteligentes de fazer a autenticação, como, por exemplo, com um token. Mas eu ainda estou esperando isso aqui acontecer:

# https://github.com/vmware/govmomi/issues/1824
6 Feb 2020
Some more info on the auth methods here: kubernetes/kubernetes#63209
We should put some of that in the govmomi docs.

Apenas para referência, é possível também usar algumas das variáveis abaixo:

  • GOVC_TLS_CA_CERTS
  • GOVC_TLS_KNOWN_HOSTS
  • GOVC_TLS_HANDSHAKE_TIMEOUT
  • GOVC_DATACENTER
  • GOVC_DATASTORE
  • GOVC_NETWORK
  • GOVC_RESOURCE_POOL
  • GOVC_HOST

GOVC_DATACENTER, por exemplo, me economizaria todas as vezes em que eu precisei passar o atributo -dc nos comandos abaixo.

Mas vamos ao que importa.

govc find

Como descobrir TUDO que eu preciso sobre minha infraestrutura contando apenas com minhas credenciais e sabendo que, “lá dentro”, tem máquinas minhas?

Basicamente usando o comando find do govc.

Este comando conta com um parâmetro -type que será a chave para as descobertas.

# govc find 
...
The '-type' flag value can be a managed entity type or one of the following aliases:

  a    VirtualApp
  c    ClusterComputeResource
  d    Datacenter
  f    Folder
  g    DistributedVirtualPortgroup
  h    HostSystem
  m    VirtualMachine
  n    Network
  o    OpaqueNetwork
  p    ResourcePool
  r    ComputeResource
  s    Datastore
  w    DistributedVirtualSwitch
...

Então vamos lá.

Fase 1: o mínimo necessário

Quantos datacenters estão disponíveis?

# govc find . -type Datacenter
/DATACENTER-PE

Um. Ótimo. Menos tapetes para procurar sujeira embaixo.

Quer informações sobre o que tem pela frente?

# govc datacenter.info -dc DATACENTER-PE
Name:                DATACENTER-PE
  Path:              /DATACENTER-PE
  Hosts:             9
  Clusters:          3
  Virtual Machines:  90
  Networks:          6
  Datastores:        9

Quantos clusters de processamento?

Direto e reto:

# govc find -dc DATACENTER-PE -type ClusterComputeResource
/DATACENTER-PE/host/cc_pool01
/DATACENTER-PE/host/cc_pool02
/DATACENTER-PE/host/cc_pool03

Quantas redes diferentes?

Mais um direto:

# govc find -dc DATACENTER-PE -type  Network
./network/kubernetes_cluster_producao
./network/kubernetes_cluster_homologacao
./network/gerencia
./network/internet
./network/backup
./network/kubernetes_cluster_lab

Quantos datastore clusters?

Nós não usamos os Datastores diretamente; fazemos uso do conceito de Datastore Clusters.

Aqui confesso que vou ficar devendo um find melhor: o melhor que consegui foi listar todos os Datastores; o nome do DatastoreCluster é o nome da pasta em que o Datastore está, como visto abaixo:

# govc find -dc DATACENTER-PE -type Datastore
./datastore/datastorecluster01/dsc1_1
./datastore/datastorecluster01/dsc1_2
./datastore/datastorecluster01/dsc1_3
./datastore/datastorecluster02/dsc2_1
./datastore/datastorecluster02/dsc2_2
./datastore/datastorecluster02/dsc2_3
./datastore/datastorecluster03/dsc3_1
./datastore/datastorecluster03/dsc3_2
./datastore/datastorecluster03/dsc3_3

# # mas eu só quero os datastoreclusters!
# govc find -dc DATACENTER-PE -type Datastore | rev | cut -f2 -d'/' | rev | sort | uniq -c
  3 datastorecluster01
  3 datastorecluster02
  3 datastorecluster03

# # uma alternativa é usar o comando govc ls
# govc ls -dc DATACENTER-PE'
/DATACENTER-PE/vm
/DATACENTER-PE/network
/DATACENTER-PE/host
/DATACENTER-PE/datastore

# govc ls /DATACENTER-HARDCORE/datastore
/DATACENTER-PE/datastore/datastorecluster01
/DATACENTER-PE/datastore/datastorecluster02
/DATACENTER-PE/datastore/datastorecluster03

Não há uma maneira trivial de usar o find para esta finalidade, pois o comando ‘govc datacenter.info’ mostra apenas o número de Datastores, enquanto para os clusters de computação, ele mostra adequadamente que tenho 3 clusters e 9 máquinas.

Mas não tome minha palavra como final. Eu estou muito longe de ser um especialista em VMWare.

Notas adicionais: mostrando os exemplos acima enquanto explicava a outras pessoas, percebi um problema: do jeito que está listado acima, fica parecendo que, necessariamente, o que vier entre o nome /datastore/ e os Datastores em si (dsc3_1,dsc3_2,dsc3_3, etc.) é necessáriamente o nome do DataStoreCluster.

Isso não é verdade! Não é que o exemplo acima está errado (ele é inspirado em uma estrutura real), mas ele não representa 100% dos casos. O VMware é um emaranhado de conceitos que se misturam tanto na representação gráfica quanto na representação via linha de comando. Vou representar uma outra estrutura que representa um cluster bem mais complexo:

# # usando o govc ls:
# govc ls /DATACENTER-HARDCORE/datastore
/DATACENTER-HARDCORE/datastore/nomealeatorio1
/DATACENTER-HARDCORE/datastore/nomealeatorio2
/DATACENTER-HARDCORE/datastore/discosSSD
/DATACENTER-HARDCORE/datastore/discosHDD

E aí, temos quatro DatastoreClusters, certo?

Errado!

Você só pode concluir isso se conhecer a infraestrutura previamente. Dei exemplos simples para facilitar o entendimento do programa, mas não dá para simplificar demais o que é absurdamente complexo.

A ideia é complementar este ‘artigo’ com uma versão mais avançada, mas acredito que seja melhor já adiantar isso aqui logo.

# #  usando govc ls com -i e -l para entender o que é o que:
# govc ls -i -l /DATACENTER-HARDCORE/datastore
StoragePod:group-p34927 /DATACENTER-HARDCORE/datastore/nomealeatorio1
Folder:group-s111 /DATACENTER-HARDCORE/datastore/nomealeatorio2
Folder:group-s113 /DATACENTER-HARDCORE/datastore/discosSSD
Folder:group-s49 /DATACENTER-HARDCORE/datastore/discosHDD

Resultado: Temos três Folders simples que servem de container lógico para agrupar coisas, e um StoragePod, que é o “codinome” para Datastore!

Então, como é que faz para efetivamente descobrirmos os malditos DatastoreClusters?

Se você usa Folders, primeira coisa a saber é que os StoragePods também são FolderS. Sabendo disso, fica “fácil”, mesmo em um cluster complexo:

# govc find -type Folder -dc DATACENTER-HARDCORE -i -l datastore
Folder:group-s5           datastore
StoragePod:group-p349     datastore/nomealeatorio1
Folder:group-s111         datastore/nomealeatorio2
Folder:group-s113         datastore/discosSSD
Folder:group-s49          datastore/discosHDD
StoragePod:group-p85069   datastore/discosHDD/dsc_hdd_01
StoragePod:group-p85068   datastore/discosHDD/dsc_hdd_02
StoragePod:group-p85067   datastore/discosHDD/dsc_hdd_03
StoragePod:group-p11286   datastore/discosSSD/dsc_ssd_01
StoragePod:group-p11274   datastore/discosSSD/dsc_ssd_02
StoragePod:group-p11234   datastore/discosSSD/dsc_ssd_03

Espero que tenha esclarecido aqui qualquer dúvida que o exemplo simplório mais em cima possa ter deixado!

Tenho tudo que preciso?

O meu principal uso de Terraform, no momento, é o de criar ‘cascas’ vazias de máquinas virtuais que irão ser instaladas usando PXE.

Se você achou a ideia idiota, eu tenho minhas razões! Mas tolere essa particularidade e acompanhe abaixo o que é necessário para criar um Terraform resource do tipo vsphere_virtual_machine dessa forma:

Nota: Não entendeu nada dos Terraform listados abaixo? Entre em contato por qualquer canal - Twitter, Linkedin, Facebook, Instagram… E me deixe saber que você quer saber mais sobre Terraform!

Recuperação do objeto datacenter

Preciso:

  • do nome do datacenter;
data "vsphere_datacenter" "cluster_datacenter" {
  name          = lookup(local.cluster_vsphere, "vsphere_datacenter")
}

Recuperação do objeto datacenter

Preciso:

  • do nome do datastore cluster;
  • do id do datacenter (recuperado com a estrutura data.vsphere_datacenter);
data "vsphere_datastore_cluster" "cluster_datastore" {
  name          = lookup(local.cluster_vsphere, "vsphere_datastore_cluster")
  datacenter_id = data.vsphere_datacenter.cluster_datacenter.id
}

Recuperação do cluster de computação

Preciso:

  • do nome do cluster de computação;
    • do id do datacenter (data.vsphere_datacenter.nome.id);
data "vsphere_compute_cluster" "compute_cluster" {
  name          = lookup(local.cluster_vsphere, "vsphere_compute_cluster")
  datacenter_id = data.vsphere_datacenter.cluster_datacenter.id
}

Recuperação das redes

Preciso:

  • do nome da rede;
  • do id do datacenter.
data "vsphere_network" "networks" {
  for_each = local.cluster_network_profiles

  name = each.value
  datacenter_id = data.vsphere_datacenter.cluster_datacenter.id
}

Resource VM

Aqui está o resource que uso para criar as VMs.

Eu posso trocar metade dos lookups por referências diretas. Mas, enquanto fazia, por incrível que pareça, ficava mais fácil para poder acertar a profundidade da estrutura de dados que eu optei usar como ‘input’ no terraform.tfvars.

Cicatrizes de batalha: não faça coisas complexas no Terraform, ainda que você consiga apenas porque você pode; você não trabalha sozinho, e alguém vai precisar entender algum dia.

De qualquer forma, de novo, esqueça o objeto Terraform ilegível por causa da minha tendência natural de fazer coisas absurdas. Observe que criei as máquinas com os três recursos de data listados acima.

resource "vsphere_virtual_machine" "vm" {
  for_each  = local.vms
  name      = each.value.hostname

  # Pode pular tudo até o (1).  
  cpu_hot_add_enabled = true
  cpu_hot_remove_enabled = true
  memory_hot_add_enabled = true
  guest_id = "coreos64Guest"

  num_cpus = lookup(lookup(local.cluster_profiles,each.value.profile), "num_cpus")
  memory   = lookup(lookup(local.cluster_profiles,each.value.profile), "memory")
  disk {
    label = "${each.value.hostname}-disk"
    size  = lookup(lookup(local.cluster_profiles,each.value.profile), "disk_size")
    unit_number = 0
    eagerly_scrub = false
    thin_provisioned = true
  }

  # Truque sujos necessários para falar para o Terraform não esperar as máquinas.  
  wait_for_guest_net_timeout = 0

  # (1): cluster de computação
  resource_pool_id = data.vsphere_compute_cluster.compute_cluster.resource_pool_id

  # (2): datastore cluster
  datastore_cluster_id = data.vsphere_datastore_cluster.cluster_datastore.id
  folder = vsphere_folder.folder.path
  
  # Nós gostamos de máquinas virtuais com muitas interfaces, e cada uma com uma quantidade variável delas.
  dynamic "network_interface" {

    for_each = each.value.network

    content {
      # (3): redes.
      network_id = lookup(lookup(data.vsphere_network.networks, network_interface.key), "id")
    }

  }

  # O backup adiciona uma tag; precisamos ignorá-la ou o Terraform irá tentar apagá-la.
  lifecycle {
    ignore_changes= [
      annotation,
      # "vapp.0.properties",
    ]
  }
}

Resumindo

Para a minha necessidade inicial (criação de cascas para instalação via PXE), a resposta é sim, os comandos govc listados acima são suficientes para que eu recupere todas as informações por meio de objetos data do Terraform.

Por mais ‘tosco’ que possa parecer, sim, o Terraform só precisa dos nomes dos objetos, e não de ids obscuros e esquisitos.

Cicatrizes de batalha: Se você prefere deixar para lá esse papo de linha de comando e quer usar a interface gráfica para ver todos os nomes e transcrever na mão, se prepare para surpresas desagradáveis se os seus administradores de VMWare forem tão trolls quanto os meus.

O uso de nomes demanda um cuidado especial por causa da tolerância do VMware com caracteres especiais. O VMWare deixa você criar a rede da seguinte maneira na interface gráfica:

  • rede_cluster_kubernetes_1_192.168.69.0/24

Legal, né? Mas olha como é o nome dela de verdade:

# govc find -dc DATACENTER-PE -type  Network
...
./network/kubernetes_cluster_producao_192.168.69.0%2f24
...

O nome que precisa ir no Terraform é o listado pelo Govc.

Isso é tudo?

Infelizmente, não.

Meu problema atual é que preciso ‘particionar’ um host VMWare em um determinado número de máquinas virtuais, usando o disco local daquela máquina como datastore.

Isso quer dizer que precisarei fazer uma parte 2 tentando levantar todas essas informações adicionais!

Alguém tem interesse?

(Na verdade a demanda era para criar máquinas virtuais e entregar o disco como por meio de Raw Device Mapping, mas aparentemente isso não é possível com o Terraform e talvez nunca seja,)

comments powered by Disqus

Posts Recentes

Categorias

Sobre

SRE e Cloud Native solutions engineer.
Respirando software livre desde 1997