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.
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
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.