Back to the main page

cloud-init

Intro

Cloud-init is service that's installed inside OCI compute. Cloud-config is set of scripts that are executed when compute is initialized.

A cloud compute initialization steps

Service

Logs

/var/log/cloud-init.log
2021-01-27 17:02:38,473 - __init__.py[DEBUG]: Adding user zarko
util.py[DEBUG]: Running command ['yum', '-t', '-y', 'install', 'zsh', 'samba', 'tigervnc'] with allowed return codes [0]

Analyze

$  cloud-init analyze boot 
-- Most Recent Boot Record -- 
    Kernel Started at: 2020-12-07 18:43:21.045001
    Kernel ended boot at: 2020-12-07 18:43:26.792327
    Kernel time to boot (seconds): 5.74732589722
    Cloud-init activated by systemd at: 2020-12-07 18:43:28.171624
    Time between Kernel end boot and Cloud-init activation (seconds): 1.37929701805
    Cloud-init start: 2020-12-07 18:43:27.316000
successful

Ansible example

In file cloud-config.yml we declare installing new local user and list of rpms to be installed:
#cloud-config
# Users: https://cloudinit.readthedocs.io/en/latest/topics/modules.html#users-and-groups
users:
  # preserver default opc user
  - default
  # add new local users
  - name: zarko
    gecos: "Zarko Lastname"
    shell: /bin/bash
    lock_passwd: true
    ssh_authorized_keys:
       - "ssh-rsa AAAAB3OBrxxxxxxxxxxxxxxxxG8LHTZumo"
    sudo: [' ALL=(ALL)  NOPASSWD: ALL']

packages:
  # packages to install
  - zsh
  - samba
  - tigervnc
Ansible uses encoded cloud config init file, use the command:
$ base64 --wrap=0 cloud-config.yml
This creates one line output, that can be used as user_data . Our Ansible variables are in file vars.yml:
compartment_id: "ocid1.compartment.oc1..aaa-shortened-uiyrrwa" 
ad: "DSdu:US-ASHBURN-AD-1"
shape: "VM.Standard2.2"
compute_name: "ansible-delete-me"
subnet: "ocid1.subnet.oc1.iad.aaaaaaaak-short-ao7ygo55pdca"  
image: "ocid1.image.oc1.iad.aaaaaaaaf-short-dyq" #ol7.9
opc_user_public_key: "ssh-rsa AAAAB3NzaC1yc2-short-LHTZumoH2tVhW+BK6ZSyCoWosR"
cloud_config: "I2Nsb3VkNv-short-FtYmEKICAtIHRpZ2Vydm5jCgo="  # created with "base64 --wrap=0 cloud-config.yml"
Finally, Ansible playbook (to create OCI compute) main.yml file reads:
---
- name: Provision OCI-IAD Compute
  connection: local
  hosts: localhost
  vars_files:
    - vars.yml

  tasks:
    - name: Create task
      oracle.oci.oci_compute_instance:    # must start with oracle.oci
        #config_profile_name: "{{ profile }}"  # can be omitted for default profile, which is IAD
        availability_domain: "{{ ad }}"
        compartment_id: "{{ compartment_id }}"
        shape: "{{ shape }}"
        source_details:
          source_type: image
          image_id: "{{ image }}"
        display_name: "{{ compute_name }}"
        create_vnic_details:
          hostname_label: "{{ compute_name }}"
          #private_ip: "{{ ip }}"
          subnet_id: "{{ subnet }}"
        # adding ssh public key
        metadata: {
          "ssh_authorized_keys": "{{ opc_user_public_key }}"
          ,  # need comma between
          "user_data": "{{ cloud_config  }}"
                }
        freeform_tags: {"What": "delete me later"}
...

Terraform example

In terraform example, cloud-config.yml is same as for Ansible, but it doesn't need ot be encoded.
Terraform OCI provider oci-provider.tf reads:
# https://docs.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm
# provider for default (home) region, us-ashburn-1
provider "oci" {
  region           = "us-ashburn-1"
  alias            = "iad1"
  tenancy_ocid     = "var.tenancy"
  user_ocid        = "var.user"
  fingerprint      = "var.fingerprint"
  private_key_path = "var.private_key"
}

# provider for us-phoenix-1
provider "oci" {
  region           = "us-phoenix-1"
  alias            = "phx1"
  tenancy_ocid     = "var.tenancy"
  user_ocid        = "var.user"
  fingerprint      = "var.fingerprint"
  private_key_path = "var.private_key"
}

# provider for uk-london-1
provider "oci" {
  region           = "uk-london-1"
  alias            = "lhr1"
  tenancy_ocid     = "var.tenancy"
  user_ocid        = "var.user"
  fingerprint      = "var.fingerprint"
  private_key_path = "var.private_key"
}
Terraform variable file variable.tf reads:
# https://www.terraform.io/docs/configuration/variables.html
# Common variables
variable "opc_user_public_key" {
  default     = "ssh-rsa AAAAB3N-short-+BK6ZSyCoWo"
  validation {
    condition = substr(var.opc_user_public_key, 0, 7) == "ssh-rsa"
    error_message = "Public key must start with ssh-rsa."
  }
  description = "opc user public key"
}

variable "compartment" {
  type = map(any)
  default = {
    "test.zd"    = "ocid1.compartment.oc1..aaaaaaaacqfsr375eohiastat2sysrhd7ms76minp57jwf5wsulg472m6a5q"
    "sysadm.zd" = "ocid1.compartment.oc1..aaaaaaaahob5f2d6sqf6znwcc3hpkrroa75tteahhvzisetd3pcjxuiyrrwa"
  }
  description = "Working Compartment"
}
# --------- Availability Domains 
# ---- IAD
variable "iad_ad" {
  type = map(any)
  default = {
    1 = "DSdu:US-ASHBURN-AD-1"
    2 = "DSdu:US-ASHBURN-AD-2"
    3 = "DSdu:US-ASHBURN-AD-3"
  }
  description = "IAD Availability Domain"
}
# -------------- Subnets 
# --- IAD
variable "iad_sub" {
  type = map(any)
  default = {
    common = "ocid1.subnet.oc1.iad.aaaaaaaakxbbuouivaig3oqrnm7cvpmpvaxd65lvygg2iu5rao7ygo55pdca"
    labops = "ocid1.subnet.oc1.iad.aaaaaaaafsd4ks5s37f6friaveqg4en4zfofeuowfw23nwuz6txrwq4p74va"
  }
  description = "IAD Subnets"
}

# ----------------- Images
variable "image-ol79" {
  default     = "ocid1.image.oc1.iad.aaaaaaaaf2wxqc6ee5axabpbandk6ji27oyxyicatqw5iwkrk76kecqrrdyq"
  description = "OL7.9"
}
variable "image-ol610" {
  default     = "ocid1.image.oc1.iad.aaaaaaaa2jjfyhytvqmouqwc2wlqq567nrfpzg5wcwqe5oislwio35fiuqxa"
  description = "OL6.10"
}
# --------------------- Shapes
variable "shape_vm_standard_2_2" {
  default = "VM.Standard2.2"
}
Terraform file compute.tf to create OCI compute reads:
resource "oci_core_instance" "computes" {
  count = 3  # create N computes, index 0 to N-1

  # Required
  availability_domain = lookup(var.iad_ad, 1) # lookup for AD3 in Ashburn
  compartment_id      = lookup(var.compartment, "sysadm.zd")
  shape               = var.shape_vm_standard_2_2

  # Required
  create_vnic_details {
    assign_public_ip = false
    subnet_id        = lookup(var.iad_sub, "common") # lookup for common.sub in IAD
  }

  # Required
  source_details {
    source_id   = var.image-ol610
    source_type = "image"
  }

  metadata = {
    ssh_authorized_keys = var.opc_user_public_key
    user_data = base64encode(file("cloud-config.yml"))
  }
  lifecycle { # ignoring!
    ignore_changes = [metadata]
  }
  # Optional
  display_name  = "tf-delete-compute-${count.index}"
  freeform_tags = { "Created" = " via Terraform : delete me, ${count.index}" }
}
Finally:
$ terraform plan
$ terraform apply

Back to the main page