On the part 1 we looked on how to create new block volume on another OCI Region using the new Block Volume Replication feature. While it was really straightforward, in a real Disaster Recovery situation you might want to automate the whole process.

So let’s look on that! I plan on using Terraform and OCI Resource Manager. In case I need new instance which has latest copy of my Block Volume replica, I would just run my stack up and it would create my instance and attach newly created block volume in it.

You could cover multiple different approaches, if you have instance already running, your Terraform script could consist only of block volume creation and attachment.

Or if you’d be syncing also boot volume then you would create boot volume also from replica!

Terraform part with Resource Manager

What I would normally do, is using data sources or other state files to get necessary values. But for this Demo I’ve simply just copied the hardcoded values (I know, lazy!) to one main.tf file. Usually, I also separate variables in different file but for purpose of this simple Demo this should be enough.

I have also my ssh keys in the Resource Manager zip file, perhaps not the best idea for real deployment!

Source Block Volume, Replica and all the network components are already created in my destination Region outside of this Resource Manager Stack.

provider "oci" {
  tenancy_ocid     = var.tenancy_ocid
}

variable "tenancy_ocid" {}

// SUBNET

variable "subnet_id" {
  default = "ocid1.subnet.oc1.ca-montreal-1.aaaaaaaafeagrv5u7uaur3jjffj75vmd6icun4seumcp5sigqvko4l66dgda"
}

// INSTANCE VARIABLES

variable "compartment_id" {
  default = "ocid1.compartment.oc1..aaaaaaaardyyoymj2c2mkrbaydmwyvy45tasnzzj3fkflwkiobnwuzcgn3eq"
}

variable "operating_system" {
  default = "Oracle Linux"
} // Name for the OS

variable "operating_system_version" {
  default = "7.9"
} // OS Version

variable "instance_shape" {
  default = "VM.Standard.E4.Flex"
} // Shape what to be used. Smallest shape selected by default

variable "source_type" {
  default = "image"
} // What type the image source is

variable "instance_create_vnic_details_assign_public_ip" {
  default = "true"
} // This is server in public subnet it will have a public IP

variable "instance_create_vnic_details_hostname_label" {
  default = "instance"
}

variable "instance_shape_config_memory_in_gbs" {
  default="1"
}

variable "instance_display_name" {
  default="replica-instance"
}

// BLOCK VOLUME

variable "bv_display_name" {
  default = "bv_replica_001"
}

variable "bv_replica_id" {
  default = "ocid1.blockvolumereplica.oc1.ca-montreal-1.ab4xkljrqmedzb4sce7f32lbklw4l5yg4s44ag32qeu32xkq7xoeukptaouq"
}

// DATA SOURCES

data "oci_core_images" "oraclelinux" {

  compartment_id           = var.tenancy_ocid
  operating_system         = var.operating_system
  operating_system_version = var.operating_system_version

  # exclude GPU specific images
  filter {
    name   = "display_name"
    values = ["^([a-zA-z]+)-([a-zA-z]+)-([\\.0-9]+)-([\\.0-9-]+)$"]
    regex  = true
  }
}

data "oci_identity_availability_domains" "GetAds" {
  compartment_id = var.tenancy_ocid
}

// CREATE INSTANCE

resource "oci_core_instance" "createInstance" {

    availability_domain = lookup(data.oci_identity_availability_domains.GetAds.availability_domains[0], "name")
    compartment_id = var.compartment_id
    shape = var.instance_shape

 
    create_vnic_details {

        assign_public_ip = var.instance_create_vnic_details_assign_public_ip
        hostname_label = var.instance_create_vnic_details_hostname_label
 
        subnet_id = var.subnet_id
    }
    display_name = var.instance_display_name

metadata = {
  ssh_authorized_keys = file("id_rsa.pub") 
      }      

    shape_config {
       memory_in_gbs = "1"
       ocpus = "1"
    }
    source_details {
        source_id = lookup(data.oci_core_images.oraclelinux.images[0], "id")
        source_type = var.source_type
    }
    preserve_boot_volume = false
}

// CREATE VOLUME FROM REPLICA

resource "oci_core_volume" "replicaVolume" {
    availability_domain = lookup(data.oci_identity_availability_domains.GetAds.availability_domains[0], "name")
    compartment_id = var.compartment_id
    size_in_gbs = "50"
    source_details {
        id = var.bv_replica_id
        type = "blockVolumeReplica"
    }
}

// ATTACH AND MOUNT VOLUME

resource "oci_core_volume_attachment" "createAttachment" {
    attachment_type = "paravirtualized"
    instance_id = oci_core_instance.createInstance.id
    volume_id = oci_core_volume.replicaVolume.id

    device = "/dev/oracleoci/oraclevdb"
    display_name = "bvattachment"

     connection {
    type        = "ssh"
    host        = oci_core_instance.createInstance.public_ip
    user        = "opc"
    private_key = file("id_rsa")
  }
  # Mount
  provisioner "remote-exec" {
    inline = [
      "set -x",
      "sudo mkdir -p /opt/replica",
      "echo '/dev/oracleoci/oraclevdb1 /opt/replica ext4 defaults 0 0' | sudo tee -a /etc/fstab",
      "sudo mount -a",
    ]
  }
}

So really simple setup, I’ve highlighted few key parts from the .tf file.

First highlight is when I define the hardcoded replica id, you could use data source to filter correct replica. Second highlight is when I create new Block Volume, I need to define source details and define the source is replica.

In the end I use remote-exec just to show you can mount your replica to be on same mount point as it exists in the source. This way once stack job completes you are ready to start using the instance!

Summary

This is just to give you idea on automation and what you could do, obviously this would be part of bigger stack where you need to spin up more instances / change some DNS settings etc. And it’s not necessarily only Terraform!

I was playing with idea of doing this with CLI but Terraform handles dependencies better so you don’t need to create artificial checkpoints if resource is created yet.

Terraform code is just to provide idea how things work, I’d never use similar code for real implementations.

2 thoughts on “OCI Block Volume Replication Part 2 – Automation”

  1. hello Simo,

    Need you guidance when trying to create multiple block volume unable to map the persistence device getting error. Please share us your guidance how to achieve this

    1. Are you doing this with terraform? Also can you give the device path and use that one as identifier? Coming from a list or map.

Leave a Reply

Your email address will not be published. Required fields are marked *