OCI #9 – Custom Image for Compute

This recipe shows how to create a new Custom Image for Compute instances.

Before you start, you must install and configure Oracle Cloud Infrastructure CLI ( see: CLI Setup recipe, if needed) and Terraform ( see: Terraform Setup recipe, if needed).

An Image is a template that specifies the instance’s preinstalled software including the Operating System. It is used to initiate a boot volume that gets associated with an instance during provisioning. Oracle Cloud Infrastructure provides default images with various Operating Systems. You can create your own custom images to install additional software on top of an existing image.

You create a new Custom Image from an existing compute instance. This explains the recommended steps you should take, namely:

  1. provision a new compute instance (I like to call it an image builder instance)
  2. install software you would like your new custom image to include
  3. create a new custom image from the instance
  4. terminate the compute instance

If you use scripts, the entire process can be automated and parametrized.

Create Custom Image

First, let’s launch a new compute instance to use it as a base for our image. If you would like your new custom image to include an installation of Node.js on top of CentOS 7.5, you have to provision the instance using Oracle-provided CentOS 7.5 image, install Node.js and use the instance to create a new custom image.

You can use a simple stack I’ve prepared. It will launch a single virtual machine with CentOS 7.5 in a newly created VCN. To do so:

git clone https://github.com/mtjakobczyk/blog-code.git
cd blog-code/oci-09/
# Create a SSH Keypar for the image builder instance
mkdir keys
ssh-keygen -t rsa -b 2048 -C "yourName@example.com" -f keys/oci_id_rsa

Now you should see this:

tree git/blog-code/oci-09
├── build_image.sh
├── infrastructure
│   ├── basevm.tf
│   └── provider.tf
├── keys
│   ├── oci_id_rsa ⇽ 
│   └── oci_id_rsa.pub ⇽ 
└── scripts
    ├── image_building_simple.sh
    └── wait_for_ssh_on_vm.sh

Terraform will need to know the compartment OCID. You can pass it using environment variable. Replace the placeholder and execute the code snippet below:

# oci-09/build_image.sh 1/5
export TF_VAR_compartment_ocid={put-here-your-compartment}

Now you can provision the instance by executing the code snippet. Please use the same console tab as before to reuse the shell variables:

# oci-09/build_image.sh 2/5
export TF_VAR_custom_image_shape="$custom_image_shape"
export TF_VAR_custom_image_base="$custom_image_base"
cd infrastructure
terraform init
echo "IB: Provisioning image building infrastructure"
terraform apply -auto-approve 2>&1 | tee ../terraform.out
vm_ip=`cat ../terraform.out | grep image_builder_vm_public_ip | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+'`
vm_ocid=`cat ../terraform.out | grep image_builder_vm_ocid | awk '{print $3}'`
echo "IB ip: $vm_ip ocid: $vm_ocid"
cd ..
echo "IB: Waiting for SSH on VM"

Next, install the software you would like your custom image to include. As you can see I am using an external shell script that is executed on the instance to perform the installation of Node.js. If you would like to install any other software, this is the script you should customise (scripts/image_building_simple.sh). The installation script is executed synchronously. In this way we can be sure that the image would never get created too early. Please use the same console tab as before to reuse the shell variables:

# oci-09/build_image.sh 3/5
echo "IB: Executing image building script"
ssh -i keys/oci_id_rsa -o StrictHostKeyChecking=no -l opc $vm_ip "bash -s" < scripts/image_building_simple.sh
echo "IB: Ready"

We use OCI CLI to create the custom image:

# oci-09/build_image.sh 4/5
oci compute image create --display-name $custom_image_name --instance-id "$vm_ocid" --wait-for-state AVAILABLE

Finally, we use Terraform to destroy the image builder instance

# oci-09/build_image.sh 5/5
cd infrastructure
terraform destroy -auto-approve
unset TF_VAR_custom_image_shape
unset TF_VAR_custom_image_base
unset TF_VAR_compartment_ocid

You can find the entire script here.

Now you should be able to see the new custom image:

oci compute image list --operating-system CentOS --operating-system-version 7 --output table --query "data [*].{Image:\"display-name\"}"
| Image                                 |
| CentOS-7-2018.11.16-0                 |
| CentOS-7-2018.10.12-0                 |
| CentOS-7-2018.09.19-0                 |
| CentOS-7-2018.10.12-0-Node.js-v10.0.0 |