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.
- Passing bash script to cloud-init will likely work, since most computes' OS releases have bash installed.
- Passing script written is another language may fail, if compute's OS doesn't have it installed.
- Cloud-config also has its own syntax, based on yaml (file starts with #cloud-config, number sign (#) and cloud-config)
A cloud compute initialization steps
- Boot compute for the first time.
- Cloud-init identify cloud (like OCI) and read "cloud metadata"
- Cloud-init process user_data (what we provide)
- Cloud-init process vendor data (ol, ubuntu, windows)
Service
- systemctl status cloud-init-local.service (Initial cloud-init job, pre-networking)
- systemctl status cloud-init.service (Initial cloud-init job, metadata service crawler)
- system ctl status cloud-config.service ( Apply the settings specified in cloud-config )
- systemctl status cloud-final.service (Execute cloud user/final scripts)
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