GOVC
Como explorar o VMWare pela linha de comando
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,)