Skip to content

OCI – 03 – Build a VCN

If you want to catch up on the project, you can get the last posts’ code here: https://github.com/ocadmin-io/oci.minecraft/tree/02-terraform

Virtual Cloud Networks

The first resource we will build for our Minecraft project is a Virtual Cloud Network (VCN). A VCN is a software-based network we create in our tenancy. You can create multiple VCN’s in your tenancy if you want to separate resources in different networks.

There are a number of resources that are part of a VCN that all work together to create a fully featured network.

  • Subnets are ranges of IP addresses within your VCN. A subnet will have a security list attached to it and you can lock down traffic to the subnet using the security list and network routes. Subnets can be public (servers have a publicly-addressable IP) or private (cannot assign a public IP). You would use a public subnet for your load balancers, bastion servers, or anything you want the wider internet to access. Subnets can also be regional – meaning the subnets can be used by servers that are running in different Availability Domains – or AD specific subnets.
  • Security Lists are basically firewall rules attached to a subnet. You can set rules for ingress (inbound) or egress (outbound) and they apply to all servers inside that subnet.
  • If you want servers on your public subnets to communicate with the Internet, you attach an Internet Gateway to the VCN. This Internet Gateway facilitates internet traffic for public subnets.
  • If you want servers on your private subnets to communicate with the Internet, you attach a NAT Gateway to the VCN. The NAT Gateway facilitates internet traffic for private subnets.
  • If you want servers on your private subnets to communicate with OCI Services (like Object Store), you attach a Service Gateway to facilitate OCI traffic for private subnets.
  • Route Table tells the network where to direct traffic outside of the VCN. If you have an Internet Gateway, NAT Gateway, Service Gateway, multiple VCNS, a VPN connection, you would add routes to the Route Table so your traffic flows correctly.
  • DHCP Options allow you set a DNS resolver for the VCN. OCI VCN’s come with their own built-in DNS system that works for many, but you can also point to an on-prem DNS resolver or you own custom resolver in OCI.
  • Security Group is a set of firewall rules that you can attach directly to a server (specifically a VNIC) and gives you a more specific way control access to servers.

Those are the basic resources for a VCN that we will use for our Minecraft application.

VCN Design

Minecraft is a simple java application and it requires one port to be open for users to connect. We will create two subnets for our VCN: a public subnet for a bastion server (for SSH access) and a load balancer, and a private subnet to host our Minecraft servers that can only be accessed by the load balancer or the bastion server. Below is a visual representation of our VCN (using the OCI Design Toolkit).

  • VCN: minecraft-vcn
  • Gateways: minecraft-igminecraft-natminecraft-sg
  • Subnets: dmz (public), apps (private)
  • Security List: We will use the default security list that comes with VCNs
  • Network Security Group: minecraft-* – allow Minecraft’s port 25565
  • Routing Table: minecraft-*-rt with routes for appropriate gateways
  • DHCP: minecraft-dns – use the default DNS resolver for minecraft.oraclevcn.com

Terraform a VCN

With our VCN designed, we can build our Terraform code to create these resources. Before we get to writing the code, I want to call out that there are different blocks of code we can use Terraform. Resource blocks are used to declare a resource and build it on OCI. Data Source blocks are used to pull information from our OCI tenancy and reference it elsewhere in Terraform. We used the data{} blocks in our first example to get a list of Availability Domains in our tenancy.

To keep our Minecraft application resources organized, we create a new compartment (under the root compartment) for our resources.

compartment.tf

# Minecraft Compartment
resource "oci_identity_compartment" "minecraft" {
    #Required
    compartment_id  = var.tenancy_id     
    description     = "Minecraft Application"
    name            = "minecraft"
    #Optional
    enable_delete   = false
}

A fun advantage of Terraform development is that we can build resources as we declare them, test, and destroy them if we want. Save the file and we will create our new compartment.

We will use terraform plan to get a list of changes that Terraform will make to our tenancy.

$ terraform plan

Terraform will perform the following actions:

  # oci_identity_compartment.minecraft will be created
  + resource "oci_identity_compartment" "minecraft" {
      + compartment_id = "ocid1.tenancy.oc1..aaaaaaaaklxobhadjdgwudmav4ijqja4olnrn6xa3v4rdq5d2ged5ydprvca"
      + defined_tags   = (known after apply)
      + description    = "Minecraft Application"
      ...
    }

Plan: 1 to add, 0 to change, 0 to destroy.

We can see our new compartment will be created, and no other resources will be touched. We will use terraform apply to create the resources. Answer yes to approve the plan.

$ terraform apply

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

oci_identity_compartment.minecraft: Creating...
oci_identity_compartment.minecraft: Still creating... [10s elapsed]
oci_identity_compartment.minecraft: Still creating... [20s elapsed]
oci_identity_compartment.minecraft: Creation complete after 22s [id=ocid1.compartment.oc1..xxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

If we look at the OCI Console we can see the new minecraft compartment in our compartment list. Compartments can be nested, so if you wanted to create a different parent compartment you can do that too. We created our minecraftcompartment at the root level of our tenancy.

Now we will start building the core networking components in the file network.tf

network.tf

Virtual Cloud Network

resource "oci_core_vcn" "minecraft" {
    # Required
    compartment_id  = oci_identity_compartment.minecraft.id
    cidr_blocks     = ["192.168.0.0/16"]
    # Optional
    dns_label       = "minecraft"
    display_name    = "minecraft-vcn"
    is_ipv6enabled  = false
}

Our VCN references the newly created compartment object, and we can get the OCID from it dynamically. We set our networks IP range to the 192.168.0.0/16 CIDR block. The other main setting is the dns_label value. We are using “minecraft”, which means our server DNS will be <server>.<subnet>.minecraft.oraclevcn.com

Within our VCN, we will create the various gateways, security lists, security groups before we create the subnets. If we were given a Terraform file with all of these resources defined, Terraform would figure out the correct order to build resources so all the dependencies are met. But, because we are building out a fresh VCN, we’ll create and test the objects one at a time and that also means we’ll need to build them in the proper order.

Internet Gateway

# Internet Gateway
resource "oci_core_internet_gateway" "minecraft" {
    # Required
    compartment_id = oci_identity_compartment.minecraft.id
    vcn_id         = oci_core_vcn.minecraft.id
    # Optional
    enabled        = true
    display_name   = "minecraft-ig"
}

The Internet Gateway is responsible for handling outbound traffic to the public internet from servers that have a publicly routable IP address. For our VCN, only servers in the dmz subnet will have public IP address. Creating an Internet Gateway is easy – just specify where you want it created and give it a name.

NAT Gateway

# NAT Gateway
resource "oci_core_nat_gateway" "minecraft" {
    # Required
    compartment_id = oci_identity_compartment.minecraft.id
    vcn_id         = oci_core_vcn.minecraft.id
    # Optional
    display_name   = "minecraft-nat"
    block_traffic  = false
}

The NAT Gateway is responsible for handling outbound traffic to the public internet from servers that only have private IP addresses. In our VCN, that will be all servers in the apps subnet. Like the Internet Gateway, tell Terraform where you want it created and give it a name. You can also choose to block internet access and keep the servers from accessing the internet.

Service Gateway

# Get All OCI Services OCID
data "oci_core_services" "all_services" {
    filter {
        name = "cidr_block"
				values = ["all-*"]
        regex = true
    }
}

# Service Gateway
resource "oci_core_service_gateway" "minecraft" {
    # Required
    compartment_id = oci_identity_compartment.minecraft.id
    vcn_id         = oci_core_vcn.minecraft.id
    services {
        service_id = data.oci_core_services.all_services.services[0].id
    }
    # Optional
    display_name   = "minecraft-sg"
}

The Service Gateway is used for servers in a private subnet, and if you don’t want to them to route OCI traffic over the public internet. The Service Gateway will create a route to services like Object Storage inside OCI. For our Service Gateway, we use the oci_core_services data objects to return an OCID that tells the gateway to route all internal OCI requests. You could also specify different Service Gateway for Object Store versus other services.

Route Tables

# Create Public Route Table
resource "oci_core_route_table" "minecraft_public_rt" {
    # Required
    compartment_id = oci_identity_compartment.minecraft.id
    vcn_id         = oci_core_vcn.minecraft.id
    route_rules    {
        destination_type  = "CIDR_BLOCK"
        destination       = "0.0.0.0/0"
        network_entity_id = oci_core_internet_gateway.minecraft.id
    }
    # Optional
    display_name   = "minecraft-ig-rt"
}

# Create Private Route Table
# Update VCN Default Route Table
resource "oci_core_default_route_table" "minecraft_private_rt" {
    # Required
    manage_default_resource_id = oci_core_vcn.minecraft.default_route_table_id
    route_rules    {
        destination_type  = "CIDR_BLOCK"
        destination       = "0.0.0.0/0"
        network_entity_id = oci_core_nat_gateway.minecraft.id
    }
    route_rules    {
        destination_type  = "SERVICE_CIDR_BLOCK"
        destination       = data.oci_core_services.all_services.services[0].cidr_block
        network_entity_id = oci_core_service_gateway.minecraft.id
    }
    # Optional
    display_name   = "minecraft-nat-rt"
}

Because we have a public subnet and a private subnet, we create two route tables. The public route table includes one rule for the Internet Gateway so that outbound traffic can reach the internet.

The second route table is for the private subnet and has two rules. One rule is to the NAT Gateway so servers can get out the internet, and the second rule is to the OCI Service Gateway. The Service Gateway will route private traffic to OCI services without going out on the public internet. The second route table is also set as the default route table for our VCN. So new subnets will use the private route table unless we set otherwise.

DHCP

# DHCP Options
# Update VCN Default DHCP Option
resource "oci_core_default_dhcp_options" "minecraft" {
    # Required
    manage_default_resource_id = oci_core_vcn.minecraft.default_dhcp_options_id
    options    {
        type  = "DomainNameServer"
        server_type = "VcnLocalPlusInternet"
    }
    options    {
        type  = "SearchDomain"
        search_domain_names      = ["minecraft.oraclevcn.com"]
    }
    # Optional
    display_name   = "minecraft-dns"
}

The DHCP options allow us to set the DNS for our VCN. We’ll set ours as minecraft.oraclevcn.com. You can set up your own DNS server, or use an on-prem DNS server if need to, but using the OCI DNS Resolver makes life a lot easier. We also set this DNS to be the default for our VCN.

Subnets

# Apps Private Subnet
resource "oci_core_subnet" "apps" {
    # Required
    compartment_id             = oci_identity_compartment.minecraft.id
    vcn_id                     = oci_core_vcn.minecraft.id
    cidr_block                 = "192.168.1.0/24"
    # Optional 
    display_name               = "apps"
    dns_label                  = "apps"
		route_table_id             = oci_core_route_table.minecraft_private_rt.id
    dhcp_options_id            = oci_core_default_dhcp_options.minecraft.id
    prohibit_public_ip_on_vnic = true
}

# DMZ Public Subnet
resource "oci_core_subnet" "dmz" {
    # Required
    compartment_id             = oci_identity_compartment.minecraft.id
    vcn_id                     = oci_core_vcn.minecraft.id
    cidr_block                 = "192.168.0.0/24"
    # Optional 
    display_name               = "dmz"
    dns_label                  = "dmz"
    route_table_id             = oci_core_default_route_table.minecraft_public_rt.id
    dhcp_options_id            = oci_core_default_dhcp_options.minecraft.id
    prohibit_public_ip_on_vnic = false
}

Now add our two subnets – the public dmz subnet and the private apps subnet. We set the CIDR block range for each subnet (you can use any range inside your VCN CIDR block). I like this CIDR block calculator if you are new to creating networks; it will tell you how many IP addresses are available when you create your ranges.

Our VCN uses the 192.168.0.0/16 private IP range (65,536 IP addresses), and we can split that up into smaller subnets. For simplicity, we’ll use /24 ranges which are normally 256 IP addresses.

We also can set the DNS name for the subnet (if we want it to be different than the Subnet Name), and if the subnet allows public IP addresses.

Network Security Groups

# Network Security Group
resource "oci_core_network_security_group" "minecraft_app" {
    # Required
    compartment_id             = oci_identity_compartment.minecraft.id
    vcn_id                     = oci_core_vcn.minecraft.id
    # Optional
    display_name   = "minecraft-app-nsg"
}

resource "oci_core_network_security_group" "minecraft_dmz" {
    # Required
    compartment_id             = oci_identity_compartment.minecraft.id
    vcn_id                     = oci_core_vcn.minecraft.id
    # Optional
    display_name   = "minecraft-dmz-nsg"
}

# Network Security Group Rules
resource "oci_core_network_security_group_security_rule" "minecraft_from_dmz" {
    # Required
    network_security_group_id = oci_core_network_security_group.minecraft_app.id
    direction         = "INGRESS"
    protocol          = "6"
    # Optional
    description       = "minecraft-from-dmz"
    destination       = "192.168.1.0/24" # App
    destination_type  = "CIDR_BLOCK"
    source            = "192.168.0.0/24" # DMZ
    tcp_options {
        destination_port_range {
        min = "25565"
        max = "25565"
        }
    }
}

resource "oci_core_network_security_group_security_rule" "minecraft_from_public" {
    # Required
    network_security_group_id = oci_core_network_security_group.minecraft_dmz.id
    direction         = "INGRESS"
    protocol          = "6"
    # Optional
    description       = "minecraft-from-public"
    destination       = "192.168.0.0/24" # DMZ
    destination_type  = "CIDR_BLOCK"
    source            = "0.0.0.0/0" # Public
    tcp_options {
        destination_port_range {
        min = "25565"
        max = "25565"
        }
    }
}

We create two network security groups and rules: one that allows internet traffic into the dmz on the Minecraft port, and another that allows traffic from the dmz into our app subnet. We want to split the rules so they can be applied to our application and load balancers without allowing unnecessary traffic in.

Deploy VCN

With these resources defined in your [network.tf](<http://network.tf>) file, you can run terraform plan to check your code and see what Terraform will build.

$ terraform plan

Terraform will perform the following actions:

  # oci_core_default_dhcp_options.minecraft will be created
  # oci_core_default_route_table.minecraft_private_rt will be created
  # oci_core_internet_gateway.minecraft will be created
  # oci_core_nat_gateway.minecraft will be created
  # oci_core_route_table.minecraft_public_rt will be created
  # oci_core_service_gateway.minecraft will be created
  # oci_core_subnet.apps will be created
  # oci_core_subnet.dmz will be created
  # oci_core_network_security_group.minecraft_app will be created
  # oci_core_network_security_group.minecraft_dmz will be created
  # oci_core_network_security_group_security_rule.minecraft_from_dmz will be created
  # oci_core_network_security_group_security_rule.minecraft_from_public will be created
  # oci_core_vcn.minecraft will be created
  # oci_identity_compartment.minecraft will be created

Plan: 14 to add, 0 to change, 0 to destroy.

All of our VCN resources will be created in the correct order. Now it’s time build them in OCI.

$ terrraform apply

Plan: 14 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Enter a value: yes

...

Apply complete! Resources: 14 added, 0 changed, 0 destroyed.

We now have a functioning cloud network that we can use to deploy Minecraft servers.

If you ran into errors, you can view the code for this post here: GitHub – ocadmin-io/oci.minecraft at 03-gamesvcn

Leave a Reply

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