hashicorp / terraform-provider-vsphere

Terraform Provider for VMware vSphere
https://registry.terraform.io/providers/hashicorp/vsphere/
Mozilla Public License 2.0
616 stars 451 forks source link

question about cloud-init #588

Closed MattPumphrey closed 6 years ago

MattPumphrey commented 6 years ago

So, I have been working through a problem, and I have been trying to use cloud-init to bootstrap my vms, while, I can see on the VMs that the cloud-init userdata is being passed into the vmtoolsd guestinfo namespace when I use the extra_config options

 extra_config {
   "guestinfo.cloudinit.userdata" = "${data.template_cloudinit_config.cloud-init-config.rendered}"
 } 

here is my template

data "template_cloudinit_config" "cloud-init-config" {
  gzip          = true
  base64_encode = true

  # lay down rendered userdata script
  part {
    content_type = "text/cloud-config"

    content = <<EOF
write_files:
  - content: |
      ${base64encode(data.template_file.userdata.rendered)}
    encoding: b64
    owner: root:root
    path: /usr/local/bin/userdata.sh
    permissions: '0700'
  - content: |
      ${base64encode(data.template_file.client-rb.rendered)}
    encoding: b64
    owner: root:root
    path: /etc/chef/client.rb
    permissions: '0644'
  - content: |
      ${base64encode(data.template_file.autoscaling-janitor.rendered)}
    encoding: b64
    owner: root:root
    path: /etc/init.d/autoscaling-janitor
    permissions: '0755'
  - content: |
      { "run_list": [${var.chef-run-list}] }
    owner: root:root
    path: /etc/chef/first-boot.json
    permissions: '0644'
runcmd:
 - [ /usr/local/bin/userdata.sh ]
packages:
  - python3
EOF
  }
}

The files do not actually get written to the file system, I have tried creating the userdata.txt and mounting it as an ISO from the datastore and that fails as well. Do I need to have the CDrom added as a client device and then use the extra_config options for this to work? If someone has been successful on doing this and can point me in the right direction that would be much appreciated. To give a little background, we are building our images via Packer, and soon to be Ansible and packer for a base build with Chef doing the final provisioning.

I have even tried to use this, https://github.com/xing/cloudinit-vmware-guestinfo but it also fails for me. Any help would be appreciated.

MnrGreg commented 6 years ago

@MattPumphrey Here is a official VMware version for implementing: https://github.com/akutz/cloud-init-vmware-guestinfo

I haven't tested with Terraforms extra_config as yet but looks like it should work well. Doesn't require OVF template or vApp.

Thanks to @akutz

akutz commented 6 years ago

No problem! Glad someone’s using it. If you check out https://github.com/akutz/yake2e, you’ll see it in use with TF and extra config :)

akutz commented 6 years ago

Here’s a line where it’s injected - https://github.com/akutz/yake2e/blob/master/vsphere.tf#L102. Check out cloud_config.tf to see where it is built. FYI, there is an undocumented size limit to the guestinfo interface as I learned myself recently (I’m still new to VMware). It just starts truncating a little before 100KiB. Make sure you gzip AND base64 encode. The pain is, if cloud config supported URL encoded values, the size could get real small. Gzip’ing inner content and then gzip’ing it again via the cloud config greatly impacts overall compression. I ran some tests, hang on.

akutz commented 6 years ago

Found it. Just going to paste in a Slack chat:

By the way, after some tests here are the results:

-rw-r--r--@  1 akutz  staff    94K Aug 28 11:39 cc.base64.base64
-rw-r--r--@  1 akutz  staff    32K Aug 28 11:40 cc.base64.gzip
-rw-r--r--@  1 akutz  staff    51K Aug 28 11:38 cc.gzip.base64
-rw-r--r--@  1 akutz  staff    31K Aug 28 11:41 cc.gzip.gzip
-rw-r--r--@  1 akutz  staff    22K Aug 28 11:49 cc.plaintext.gzip
-rw-r--r--@  1 akutz  staff    23K Aug 28 11:49 cc.urlencode.gzip

Each file is cloud config data. The first extension indicates how the child documents are encoded. The second extension indicates how the outer document is encoded. As you can see from comparing cc.gzip.gzip and cc.base64.gzip, there is a very minor improvement if child content is compressed prior to being included in the outer content. But it's very minor.

Now, cc.plaintext.gzip is of course the most compressed. Unfortunately it represents the inner-content as plain-text. This is not possible in production thanks to the outer content's rules about valid characters.

However, it occurred to me to consider using URL encoding for the inner content instead of base64 encoding. The URL encoding should make the characters safe for YAML-transport, and look at the results! Only fractionally worse than the inner-content as plain-text.

And yet, unfortunately once more, cloud config doesn't support URL encoding for its file content (https://cloudinit.readthedocs.io/en/latest/topics/examples.html#writing-out-arbitrary-files):

encoding can be given b64 or gzip or (gz+b64).

In the end it looks like gzip+gzip is the best solution for the situational constraints. Without those restrictions in the way, keeping the inner content as plain-text offers the best compression.

bill-rich commented 6 years ago

Thanks @MnrGreg & @akutz. Thats some real interesting info on cloud init.

@MattPumphrey Does using https://github.com/akutz/cloud-init-vmware-guestinfo resolve your problem for you?

MattPumphrey commented 6 years ago

This is alot of really interesting information, I have yet to test this out, however you can close this as it has given me a lot more information than I previously had on how to successfully get my VMs deployed in this manner! Thanks @MnrGreg & @akutz

@bill-rich thanks for all your help!

SerenaLi279 commented 5 years ago

Hello, May I ask a question? For the extra_config https://www.terraform.io/docs/providers/vsphere/r/virtual_machine.html#extra_config How to use cloud-init with terraform, if I use template imported from OVF?