Getting started with Oracle Cloud Infrastructure
2021-09-05 - Oracle Cloud Infrastructure's always free tier is very generous
Tag: terraform
Introduction
Oracle Cloud Infrastructure provides quite a generous always free tier for you to use and test their cloud… or host some light services. But getting started was a little difficult with many pieces missing or incomplete in the examples, especially how to configure ipv6 on your instances.
The documentation is very good and exhaustive but information was scattered : the following should help you get started right after you create your oracle cloud infrastructure’s account.
Create your API access
In order to terraform your infrastructure, you are going to need to generate an api access which is composed of a key and several other things :
- Open the web console, click the top left menu and select
Identity & Security
thenUsers
. - Click your account
- Scroll to bottom left and select
API Keys
- click
Add an api key
- Select
Generic API Key Pair
, download the private key file then clickAdd
- Copy the information displayed for the next phase
Terraform
Provider configuration
Here is the relevant snippet from my providers.tf
file :
variable "oracle_tenancy_ocid" {}
variable "oracle_user_ocid" {}
variable "oracle_fingerprint" {}
provider "oci" {
tenancy_ocid = var.oracle_tenancy_ocid
user_ocid = var.oracle_user_ocid
fingerprint = var.oracle_fingerprint
private_key_path = "../tf-common/oracle_key.pem"
region = "eu-amsterdam-1"
}
variable "oracle_amd64_instances_names" {}
This goes along with a terraform.tfvars
file that you should fill with the api access information you saved up earlier :
oracle_tenancy_ocid = "XXXXX"
oracle_user_ocid = "YYYYY"
oracle_fingerprint = "ZZZZZ"
oracle_amd64_instances_names = ["dalinar", "kaladin"]
The last bit is how I name the two free instances I want to create, pick anything you like.
Networking
Here is how to bootstrap a vcn and the associated objects for direct internet access. For simplicity I will leave the access lists opened, firewall rules really are a pain to write with terraform… I plan to keep on using iptables or shorewall on the hosts for now.
resource "oci_core_vcn" "adyxax" {
compartment_id = var.oracle_tenancy_ocid
cidr_blocks = ["10.0.0.0/16"]
display_name = "adyxax"
dns_label = "adyxax"
is_ipv6enabled = true
}
resource "oci_core_internet_gateway" "gw" {
compartment_id = var.oracle_tenancy_ocid
vcn_id = oci_core_vcn.adyxax.id
enabled = true
display_name = "gw"
}
resource "oci_core_route_table" "default-via-gw" {
compartment_id = var.oracle_tenancy_ocid
vcn_id = oci_core_vcn.adyxax.id
display_name = "default-via-gw"
route_rules {
destination = "0.0.0.0/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.gw.id
}
route_rules {
destination = "::/0"
destination_type = "CIDR_BLOCK"
network_entity_id = oci_core_internet_gateway.gw.id
}
}
# protocol - Specify either all or an IPv4 protocol number : ICMP ("1"), TCP ("6"), UDP ("17"), and ICMPv6 ("58").ยท
resource "oci_core_security_list" "allow-all" {
compartment_id = var.oracle_tenancy_ocid
vcn_id = oci_core_vcn.adyxax.id
display_name = "allow-all"
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
ingress_security_rules {
protocol = "all"
source = "0.0.0.0/0"
}
egress_security_rules {
protocol = "all"
destination = "::/0"
}
ingress_security_rules {
protocol = "all"
source = "::/0"
}
}
resource "oci_core_subnet" "adyxax-production" {
cidr_block = cidrsubnet(oci_core_vcn.adyxax.cidr_blocks[0], 8, 0)
compartment_id = var.oracle_tenancy_ocid
vcn_id = oci_core_vcn.adyxax.id
display_name = "production"
dns_label = "production"
ipv6cidr_block = cidrsubnet(oci_core_vcn.adyxax.ipv6cidr_blocks[0], 8, 0)
security_list_ids = [oci_core_security_list.allow-all.id]
route_table_id = oci_core_route_table.default-via-gw.id
}
Instances
Here is how to create the two always free tier instances, each in a different fault domain. The tricky part was to understand how ipv6 addresses are like second class citizens on oracle cloud :
data "oci_identity_availability_domains" "ads" {
compartment_id = var.oracle_tenancy_ocid
}
data "oci_identity_fault_domains" "fd" {
compartment_id = var.oracle_tenancy_ocid
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
}
# taken from https://docs.oracle.com/en-us/iaas/images/all/?search=Oracle-Linux-8.4
data "oci_core_image" "ol8" {
image_id = "ocid1.image.oc1.eu-amsterdam-1.aaaaaaaaj46eslsa6ivgneyneypomtvzb6dmg22gtewy6opwiniuwgsdv7uq"
}
resource "oci_core_instance" "amd64-vms" {
count = length(var.oracle_amd64_instances_names)
compartment_id = var.oracle_tenancy_ocid
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
fault_domain = data.oci_identity_fault_domains.fd.fault_domains[
count.index % length(data.oci_identity_fault_domains.fd.fault_domains)].name
display_name = var.oracle_amd64_instances_names[
count.index % length(var.oracle_amd64_instances_names)]
shape = "VM.Standard.E2.1.Micro"
preserve_boot_volume = true
create_vnic_details {
subnet_id = oci_core_subnet.adyxax-production.id
hostname_label = var.oracle_amd64_instances_names[count.index]
display_name = var.oracle_amd64_instances_names[count.index]
}
source_details {
boot_volume_size_in_gbs = 50
source_type = "image"
source_id = data.oci_core_image.ol8.id
}
metadata = {
"ssh_authorized_keys" : "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILOJV391WFRYgCVA2plFB8W8sF9LfbzXZOrxqaOrrwco"
}
}
data "oci_core_vnic_attachments" "amd64-vms-vnics" {
count = length(var.oracle_amd64_instances_names)
compartment_id = var.oracle_tenancy_ocid
instance_id = oci_core_instance.amd64-vms[count.index].id
}
resource "oci_core_ipv6" "amd64-vms-ipv6s" {
count = length(var.oracle_amd64_instances_names)
vnic_id = data.oci_core_vnic_attachments.amd64-vms-vnics[count.index].vnic_attachments[0].vnic_id
display_name = var.oracle_amd64_instances_names[count.index]
}
Bonus : Provisionning cloudflare’s dns
If like me you are managing your dns with cloudflare, here is how to provision the relevant records :
resource "cloudflare_record" "adyxax-org-oracle-amd64-vms-ipv4" {
count = length(var.oracle_amd64_instances_names)
zone_id = lookup(data.cloudflare_zones.adyxax-org.zones[0], "id")
name = var.oracle_amd64_instances_names[count.index]
value = oci_core_instance.amd64-vms[count.index].public_ip
type = "A"
proxied = false
}
resource "cloudflare_record" "adyxax-org-oracle-amd64-vms-ipv6" {
count = length(var.oracle_amd64_instances_names)
zone_id = lookup(data.cloudflare_zones.adyxax-org.zones[0], "id")
name = var.oracle_amd64_instances_names[count.index]
value = oci_core_ipv6.amd64-vms-ipv6s[count.index].ip_address
type = "AAAA"
proxied = false
}
Conclusion
Putting all of this together was an interesting experience, and I am satisfied that it works well. In the future I plan to add my own oci image based on alpine linux which is not available natively. I tried oracle linux and it is fine, but consumes way too much ram for my taste. For now I installed alpine linux using the instance’s cloud console and my procedure for that.