Terraform + Cloud-Init via extra_config e DataSourceVMware

Terraform + Cloud-Init via extra_config e DataSourceVMware

Pergunta:

Alguém usou com sucesso imagens de nuvem terraform + extra_config + Ubuntu para fazer interface com Cloud Init, fornecendo metadados/dados de usuário? Espero que isso interaja com o DataSourceVMware, mas não posso ter certeza neste estágio.

O que tenho feito:

Estou usando o Terraform para implantar imagens de nuvem Ubuntu no VMware vSphere 7. Tem sido fácil usar as propriedades do vApp:

... below code snipped from resource "vsphere_virtual_machine" "vm" { }

vapp {
    properties = {
        hostname = var.vm_Name_Lower
        instance-id = var.vm_Name_Lower
        user-data = base64encode(file("${path.module}/userdata.yml"))
    }
}

Mas qualquer tentativa de usar extra_config falhou. Eu gostaria de poder fornecer guestinfo.metadata e guestinfo.userdata. Mas atualmente, meus testes com metadados (vistos abaixo) parecem falhar porque o nome do host não está definido na VM:

data "cloudinit_config" "metadata" {
    gzip = true
    base64_encode = true
    part {
        content_type = "text/cloud-config"
        content = <<-EOF
            local-hostname: testvm
            instance-id: testvm
        EOF
    }
}

... below code snipped from resource "vsphere_virtual_machine" "vm" { }

extra_config = {
    "guestinfo.metadata" = data.cloudinit_config.metadata.rendered
    "guestinfo.metadata.encoding" = "gzip+base64"
}

Posso ver a entrada de log do vSphere para provar que o extra_config foi enviado:

config.extraConfig("guestinfo.metadata"): (key = "guestinfo.metadata", value = "H4sIAAAAAAAA/2SOTUvGMBCE74H8h/De11dPQsSDHz14qIK ... snipped

Referências:

Detalhes da versão:

Client system (on which terraform is run): Ubuntu 20.04.3 LTS
ESXi: 7.0.2 / Build: 18538813
vCenter Server: 7.0.2 / Build: 18455184
Cloud Image: https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-amd64.ova
Terraform v1.0.7
on linux_amd64
provider registry.terraform.io/hashicorp/template v2.2.0
provider registry.terraform.io/hashicorp/vsphere v1.24.3

Responder1

O problema é que o cloud-init tem, por padrão, o provedor de fonte de dados OVF invocado antes da nova fonte de dados VMware (a partir do cloud-init 21.3). O Terraform fornece dados que o provedor de fonte de dados OVF gosta e, portanto, processa as informações. Isso explica por que os "dados do usuário" das propriedades do vApp aceitam a configuração da nuvem.

A solução é remover o provedor de fonte de dados OVF do cloud-init:

  1. [Navegador da Web[Baixar OVA: https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-amd64.ova
  2. [VC UI] Implante a partir do OVF, aceite padrões (exceto provisionamento de disco, use Thin Provisioning).
  3. [VC UI] Editar configurações / Opções de VM / Opções de inicialização / Atraso de inicialização = 2.000 ms.
  4. [VC UI] Abra o console da VM.
  5. [Console VM] Ligue a VM.
  6. [VM Console] Segure Shift na tela do BIOS (para forçar o GRUB a exibir o menu).
  7. [Console VM] Selecione Opções Avançadas para Ubuntu.
  8. [VM Console] Selecione a versão mais recente do kernel com "(modo de recuperação)" no final.
  9. [Console VM] Selecione "root / Drop to root shell prompt"
  10. [VM Console] Pressione Enter para manutenção
  11. [Console VM] # dpkg-reconfigure cloud-init
  12. [VM Console] Desmarque tudo, exceto VMware e Nenhum
  13. [Console VM] # limpeza de inicialização na nuvem
  14. [Console VM] # shutdown -h agora
  15. [VC UI] Editar configurações / Opções de VM / Opções de inicialização / Atraso de inicialização = 0 ms.
  16. [VC UI] Converter em modelo

Responder2

Fazendo o mesmo com os OVAs da nuvem do Ubuntu e encontrei uma pequena solução alternativa para isso. Não é o ideal, pois envolve uma reinicialização, o que significa que você precisa de um local-execprovisionador para detectar quando a instância realmente foi concluída, mas funciona.

Digamos que você tenha dois arquivos yaml que está usando para userdata - o primeiro que você está passando com as propriedades do vApp nomeadas vapp-userdata.yamle o segundo que você está passando para a fonte de dados VMware chamadaguest-userdata.yaml

Algo assim no Terraform

  vapp {
    properties = {
      user-data = base64encode(data.template_file.vapp_userdata[count.index].rendered)
    }
  }

  extra_config = {
    "guestinfo.metadata"          = base64encode(data.template_file.guest-metadata[count.index].rendered)
    "guestinfo.metadata.encoding" = "base64"
    "guestinfo.userdata"          = base64encode(data.template_file.userdata.rendered)
    "guestinfo.userdata.encoding" = "base64"
  }

Você vapp-userdata.yamltem uma write_fileação que substitui as definições atuais da fonte de dados e as remove OVFquando você já atingiu esse ponto.

write_files:
  - path: /etc/cloud/cloud.cfg.d/90_dpkg.cfg
    owner: root:root
    permissions: "0644"
    content: |
      datasource_list: [ VMware, None ]

Então, na parte inferior, termine com uma reinicialização

power_state:
  timeout: 600
  mode: reboot

Quando a VM for reinicializada após o último comando, ela lerá a VMwarefonte de dados conforme definido na nova configuração do cloud-init e processará sua guest-userdata.yamle metadata.yamlse você também tiver definido isso.

Quanto a detectar quando terminar, ainda estou tentando descobrir a melhor maneira de fazer isso. A maneira mais fácil é começar nc -l 12345no final guest-userdata.yamle ter um provisionador local que prossiga quando puder se conectar a tcp/12345, mas isso deixa você com um ouvinte netcat aberto, tcp/12345o que não é o ideal.

Se você encontrar uma maneira melhor, responda :)

Editar

Com certeza haverá uma maneira melhor de fazer isso, mas ...

No fundo doguest-userdata.yaml

runcmd:
  - mkdir -p /mnt/sharedfolder
  - sysctl -w vm.overcommit_memory=1
  - sysctl -w kernel.panic=10
  - sysctl -w kernel.panic_on_oops=1
  - curl https://releases.rancher.com/install-docker/${docker_version}.sh | sh
  - usermod -aG docker ubuntu
  - nc -l 1234 & ncpid=$!  #start nc and get PID
  - sleep 20
  - kill $ncpid #kill PID once Terraform has had time to connect

Então, como provisionador no final do seu .tfarquivo

  provisioner "local-exec" {
    # Wait for cloud-init userdata cmds
    # Netcat: z (scan port only), w1 (wait 1 second)
    command = "count=0; until $(nc -zw1 ${self.default_ip_address} 1234); do sleep 1; count=`expr $count + 1`; done"
  }

informação relacionada