Provisioning a complete CoreOS infrastructure on Digital Ocean with Terraform

In this recipe, we'll build from scratch a fully working CoreOS cluster on Digital Ocean in their New York region, using Terraform and cloud-init. We'll add some latency monitoring as well with StatusCake, so we have a good foundation of using Terraform on Digital Ocean.

Getting ready

To step through this recipe, you will need the following:

  • A working Terraform installation
  • A Digital Ocean account
  • A StatusCake account
  • An Internet connection

How to do it…

Let's start by creating the digitalocean provider (it only requires an API token) in a file named providers.tf:

provider "digitalocean" {
  token = "${var.do_token}"
}

Declare the do_token variable in a file named variables.tf:

variable "do_token" {
  description = "Digital Ocean Token"
}

Also, don't forget to set it in a private terraform.tfvars file:

do_token = "a1b2c3d4e5f6"

Handling the SSH key

We know that we'll need an SSH key to log into the cluster members. With Digital Ocean, the resource is named digitalocean_ssh_key. I propose that we name the SSH key file iac_admin_sshkey in the keys directory, but as you might prefer something else, let's use a variable for that as well. Let's write this in a keys.tf file:

resource "digitalocean_ssh_key" "default" {
  name       = "Digital Ocean SSH Key"
  public_key = "${file("${var.ssh_key_file}.pub")}"
}

Create the related variable in variables.tf, with our suggested default:

variable "ssh_key_file" {
  default     = "keys/iac_admin_sshkey"
  description = "Default SSH Key file"
}

It's now time to effectively override the value in the terraform.tfvars file if you feel like it:

ssh_key_file = "./keys/my_own_key"

Creating the CoreOS cluster members

Here's the core of our infrastructure: three nodes running in the New York City data center NYC1, with private networking enabled, no backups activated (set it to true if you feel like it!), the SSH key we previously created, and a cloud-init file to initiate configuration. A virtual machine at Digital Ocean is named a droplet, so the resource to launch a droplet is digitalocean_droplet. All variables' names relate to what we just enumerated:

resource "digitalocean_droplet" "coreos" {
  image              = "${var.coreos_channel}"
  count              = "${var.cluster_nodes}"
  name               = "coreos-${count.index+1}"
  region             = "${var.do_region}"
  size               = "${var.do_droplet_size}"
  ssh_keys           = ["${digitalocean_ssh_key.default.id}"]
  private_networking = true
  backups            = false
  user_data          = "${file("cloud-config.yml")}"
}

Declare all the variables in the variables.tf file, with some good defaults (the smallest 512 MB droplet, a three-node cluster), and some defaults we'll want to override (AMS3 data center or the stable CoreOS channel):

variable "do_region" {
  default     = "ams3"
  description = "Digital Ocean Region"
}

variable "do_droplet_size" {
  default     = "512mb"
  description = "Droplet Size"
}

variable "coreos_channel" {
  default     = "coreos-stable"
  description = "CoreOS Channel"
}

variable "cluster_nodes" {
  default     = "3"
  description = "Number of nodes in the cluster"
}

Here are our overridden values in terraform.tfvars (but feel free to put your own values, such as using another data center or CoreOS release):

do_region = "nyc1"
coreos_channel = "coreos-beta"

Adding useful output

It would be awesome to automatically have a few auto-documented lines on how to connect to our CoreOS cluster. As we can do that with the Terraform outputs, let's use this example for a start, in outputs.tf. This is constructing an SSH command line with dynamic information from Terraform that we'll be able to use easily (it's simply iterating over every digitalocean_droplet.coreos.* available):

output "CoreOS Cluster Members" {
  value = "${formatlist("ssh core@%v -i ${var.ssh_key_file}", digitalocean_droplet.coreos.*.ipv4_address)}"
}

The output will look like this:

CoreOS Cluster Members = [
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey
]

Dynamic DNS Integration

One of the attractive features of Digital Ocean is the easy DNS integration. For example, if our domain is infrastructure-as-code.org and we launch a blog droplet, we'll end up registering it automatically under the public DNS name blog.infrastructure-as-code.org. Pretty easy and dynamic! To give Digital Ocean power on our domain, we need to go to our registrar (where we bought our domain), and configure our domain to be managed by Digital Ocean, using their own nameservers, which are as follows:

  • ns1.digitalocean.com
  • ns2.digitalocean.com
  • ns3.digitalocean.com

This prerequisite being done, let's declare our domain in the dns.tf file using the digitalocean_domain resource, automatically using a cluster_domainname variable for the domain name, and an initial IP address matching, that we can either set to a value you already know or to an arbitrary droplet:

resource "digitalocean_domain" "cluster_domainname" {
  name       = "${var.cluster_domainname}"
  ip_address = "${digitalocean_droplet.coreos.0.ipv4_address}"
}

Add the new variable in variables.tf:

variable "cluster_domainname" {
  default     = "infrastructure-as-code.org"
  description = "Domain to use"
}

Don't forget to override it as necessary in terraform.tfvars.

The next step is to register automatically every droplet in the DNS. By iterating over each droplet, and extracting their name and ipv4_address attributes, we'll add this digitalocean_record resource into the mix:

resource "digitalocean_record" "ipv4" {
  count  = "${var.cluster_nodes}"
  domain = "${digitalocean_domain.cluster_domainname.name}"
  type   = "A"
  name   = "${element(digitalocean_droplet.coreos.*.name, count.index)}"
  value  = "${element(digitalocean_droplet.coreos.*.ipv4_address, count.index)}"
}

This will automatically register every droplet under the name core-[1,2,3].mydomain.com, for easier access and reference.

If you like, you can access the fqdn attribute of this resource right from the outputs (outputs.tf):

output "CoreOS Cluster Members DNS" {
  value = "${formatlist("ssh core@%v -i ${var.ssh_key_file}", digitalocean_record.ipv4.*.fqdn)}"
}

Integrating cloud-init

We need to build a fully working cloud-config.yml file for our CoreOS cluster. Refer to the cloud-init part of this book in Chapter 5, Provisioning the Last Mile with Cloud-Init for more information on the cloud-config.yml file, and especially on configuring CoreOS with it.

What we need for a fully usable CoreOS cluster are the following:

  • A working etcd cluster on the local network interface ($private_ipv4)
  • A working fleet cluster on the local network interface ($private_ipv4)

    Fleet is a distributed init system. You can think of it as systemd for a whole cluster

To configure etcd, we first need to obtain a new token. This token is unique and can be distributed through different channels. It can be easily obtained through the https://coreos.com/os/docs/latest/cluster-discovery.html etcd service. Then we'll start 2 units—etcd and fleet.

$ curl -w "
" 'https://discovery.etcd.io/new?size=3'
https://discovery.etcd.io/b04ddb7ff454503a66ead486b448afb7

Note this URL carefully and copy paste it in the following cloud-config.yml file:

#cloud-config
# https://coreos.com/validate/
coreos:
  etcd2:
    discovery: "https://discovery.etcd.io/b04ddb7ff454503a66ead486b448afb7"
    advertise-client-urls: "http://$private_ipv4:2379"
    initial-advertise-peer-urls: "http://$private_ipv4:2380"
    listen-client-urls: http://0.0.0.0:2379
    listen-peer-urls: http://$private_ipv4:2380
  units:
    - name: etcd2.service
      command: start
    - name: fleet.service
      command: start
  fleet:
    public-ip: "$public_ipv4"
    metadata: "region=ams,provider=digitalocean"

This will be enough to start an etcd + fleet cluster on CoreOS. Chapter 5, Provisioning the Last

Mile with Cloud-Init, for in-depth details on cloud-init.

Integrating dynamic StatusCake monitoring

We can reuse our knowledge from previous chapters to easily integrate full latency monitoring to the hosts of our CoreOS cluster, using a free StatusCake account (https://statuscake.com).

Start by configuring the provider in providers.tf:

provider "statuscake" {
  username = "${var.statuscake_username}"
  apikey   = "${var.statuscake_apikey}"
}

Declare the required variables in variables.tf:

variable "statuscake_username" {
  default     = "changeme"
  description = "StatusCake Account Username"
}

variable "statuscake_apikey" {
  default     = "hackme"
  description = "StatusCake Account API Key"
}

Also, override with your own values in terraform.tfvars.

Now we can use the statuscake_test resource to activate immediate latency (ping) monitoring on every droplet by iterating over each digitalocean_droplet.coreos.* resource value:

resource "statuscake_test" "coreos_cluster" {
  count        = "${var.cluster_nodes}"
  website_name = "${element(digitalocean_droplet.coreos.*.name, count.index)}.${var.cluster_domainname}"
  website_url  = "${element(digitalocean_droplet.coreos.*.ipv4_address, count.index)}"
  test_type    = "PING"
  check_rate   = 300
  paused       = false
}

It's time to terraform apply this:

$ terraform apply
[...]

CoreOS Cluster Members = [
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey
]
CoreOS Cluster Members DNS = [
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey,
    ssh [email protected] -i ./keys/iac_admin_sshkey
]

Confirm that we can connect to a member using the command line from the output:

$ ssh [email protected] -i ./keys/iac_admin_sshkey

Verify the etcd cluster health:

$ core@coreos-1 ~ $ etcdctl cluster-health
member 668f889d5f96b578 is healthy: got healthy result from http://10.136.24.178:2379
member c8e8906e0f3f63be is healthy: got healthy result from http://10.136.24.176:2379
member f3b53735aca3062e is healthy: got healthy result from http://10.136.24.177:2379
cluster is healthy

Check that all fleet members are all right:

core@coreos-1 ~ $ fleetctl list-machines
MACHINE         IP              METADATA
24762c02...     159.203.189.146 provider=digitalocean,region=ams
3b4b0792...     159.203.189.142 provider=digitalocean,region=ams
59e15b88...     159.203.189.131 provider=digitalocean,region=ams

Enjoy, in less than a minute, you're ready to use a CoreOS cluster with basic monitoring, using only fully automated Terraform code!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.234.188