Files
oam/knowledge base/cloud-init.md
2024-07-22 23:17:23 +02:00

7.7 KiB

Cloud init

  1. TL;DR
  2. Merge 2 or more files or parts
  3. Further readings
    1. Sources

TL;DR

# Get the current status.
cloud-init status
cloud-init status --wait

# Verify that cloud-init received the expected user data.
sudo cloud-init query userdata
sudo cat /var/lib/cloud/instance/user-data.txt | gunzip

# Assert the user data we provided is a valid cloud-config.
# A.K.A. validate files.
# Versions <22.2 require 'devel schema' instead of only 'schema'.
sudo cloud-init schema -c '/var/lib/cloud/instance/user-data.txt'
sudo cloud-init schema --system --annotate

# Check the user scripts.
ls '/var/lib/cloud/instance/scripts'

# Check the raw logs.
cat '/var/log/cloud-init-output.log'
tail -f '/var/log/cloud-init.log' '/var/log/cloud-init-output.log'

# Parse and organize the events in the log file by stage.
cloud-init analyze show

# Clean up everything so `cloud-init` can run again.
sudo cloud-init clean

# Re-run everything.
# 1. Clean the existing configuration.
# 2. Detect local data sources.
# 3. Detect any data source requiring the network and run the 'initialization' modules.
# 4. Run the 'configuration' modules.
# 5. Run the 'final' modules.
sudo cloud-init clean --logs \
&& sudo cloud-init init --local \
&& sudo cloud-init init \
&& sudo cloud-init modules --mode='config' \
&& sudo cloud-init modules -m 'final'

# Manually run a single cloud-config module once after the instance has booted.
# Requires one to delete the semaphores in /var/lib/cloud/instances/hostname/sem. Cloud-init will not re-run if these
# files are present.
sudo rm -fv '/var/lib/cloud/instances'/*/'sem/config_ssh' && sudo cloud-init single --name 'cc_ssh' --frequency 'always'
sudo rm -fv '/var/lib/cloud/instances'/*/'sem/config_write_files_deferred' \
&& sudo cloud-init single -n 'write_files_deferred' --frequency 'once'
#cloud-config

# Add the Docker repository
yum_repos:
  docker-ce:
    name: Docker CE Stable - $basearch
    enabled: true
    baseurl: https://download.docker.com/linux/rhel/$releasever/$basearch/stable
    priority: 1
    gpgcheck: true
    gpgkey: https://download.docker.com/linux/rhel/gpg

# Upgrade the instance
package_upgrade: true
package_reboot_if_required: true

# Install required packages
# docker-ce already depends on docker-ce-cli and containerd.io
packages:
  - docker-ce
  - jq
  - unzip

# Enable and start the service after installation
runcmd:
  - systemctl daemon-reload
  - systemctl enable --now docker.service

Merge 2 or more files or parts

See Merging User-Data sections for details.

#cloud-config
packages:
  - jq
  - unzip

---
merge_how:
 - name: list
   settings: [append]
 - name: dict
   settings: [no_replace, recurse_list]

packages:
  - parallel

---
packages:
  - vim

merge_type: 'list(append)+dict(recurse_array)+str()'
In Pulumi
  1. Create a data resource containing the files in order, one per part:

    new cloudinit.Config("example", {
      gzip: true,
      base64Encode: true,
      parts: [
        {
          contentType: "text/cloud-config",
          content: fs.readFileSync("cloud-init/aws.ssm.yaml", "utf8"),
          filename: "cloud-config.ssm.yml",
        },
        {
          contentType: "text/cloud-config",
          content: YAML.stringify({
            number: 3,
            plain: 'string',
            block: 'two\nlines\n'
          }),
          filename: "cloud-config.inline.yml",
          mergeType: "dict(recurse_array,no_replace)+list(append)",
        },
       {
         contentType: "text/cloud-config",
         content: pulumi.all([
           certificate.certificateDomain.apply(v => v),
           certificate.certificatePem.apply(v => v),
           certificate.privateKeyPem.apply(v => v),
         ]).apply(([domain, certificate, privateKey]) => yaml.stringify({
           write_files: [
             {
               path: `/etc/gitlab/ssl/${domain}.crt`,
               content: certificate,
               permissions: "0o600",
               defer: true,
             },
             {
               path: `/etc/gitlab/ssl/${domain}.key`,
               content: btoa(privateKey),
               permissions: "0o600",
               encoding: "base64",
               defer: true,
             },
           ],
         })),
         filename: "letsencrypt.certificate.yml",
         mergeType: "dict(recurse_array,no_replace)+list(append)",
       },
      ],
    });
    
  2. Give its rendered form as input to a virtual machine's userdata attribute, or export it:

    let userData = new cloudinit.Config("userData", {  });
    new aws.ec2.Instance("instance", {
      userData: userData.rendered,
      
    })
    
In Terraform
  1. Create a data resource containing the files in order, one per part:

    # https://registry.terraform.io/providers/hashicorp/cloudinit/latest/docs
    # https://github.com/chrusty/terraform-multipart-userdata/blob/master/example/cloudinit.tf
    data "cloudinit_config" "vm" {
      gzip = true
      base64_encode = true
    
      part {
        content      = file("${path.module}/files/cloud-init/first.yaml")
        content_type = "text/cloud-config"
        filename     = "first.yaml"
      }
      
      part {
        content      = templatefile(
          "${path.module}/templates/cloud-init/n-th.yaml.tftpl",
          {
            key = value
          }
        )
        content_type = "text/cloud-config"
        merge_type   = "dict(recurse_array,no_replace)+list(append)"
        filename     = "n-th.yaml"
      }
    }
    
  2. Give its rendered form as input to a virtual machine's userdata attribute or an output resource:

    resource "azurerm_linux_virtual_machine" "vm" {
      user_data = data.cloudinit_config.vm.rendered
      
    }
    
    output "cloudinit_config" {
      value = data.cloudinit_config.vm.rendered
    }
    

Further readings

Sources