~egtann/terrafirma

Composable alternative to Terraform
3060412c — Evan Tann 18 days ago
add static ip note to readme
ef05efaf — Evan Tann 18 days ago
fix readme issues
73a67d10 — Evan Tann 18 days ago
add version flag

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~egtann/terrafirma
read/write
git@git.sr.ht:~egtann/terrafirma

You can also use your local clone with git send-email.

#Terrafirma

Terrafirma is a composable alternative to Terraform. Like Terraform, Terrafirma deploys infrastructure. It abstracts away the cloud provider and provides a CLI that reliably creates and destroys servers.

Unlike Terraform, Terrafirma is designed to work alongside and with other tools. It is very much designed in the UNIX philosphy of doing one thing well.

  • Composable with other tools, so it easily works with binp to handle binpacking and cup for zero-downtime, high-speed, parallel, rolling deploys. Mix-and-match to find your perfect deployment strategy.
  • State is discovered, not stored, and thus is always in sync.
  • Standard config language rather than tf. Works alongside everything else using json. jq to your heart's content.

Note that this is not a "batteries-included" tool. Terrafirma provides the bare minimum to interface with cloud providers and manage infrastructure.

Currently only Google Cloud (GCP) is supported. Patches for additional features and cross-cloud support are welcome.

#How it works

Terrafirma deploys infrastructure in 3 steps:

  1. Plan: Terrafirma retrieves the current state from your cloud provider, compares that against your desired state, and generates a minimal diff of machines that need to be created or destroyed to reach your desired state.
  2. Create: Create each of the machines listed in the plan.
  3. Destroy: Delete each of the machines listed in the plan.

Unlike Terraform which executes a plan in a single command, Terrafirma separates the create and destroy steps.

This is because Terrafirma is designed to compose well with other tools and doesn't make any assumptions around how you provision servers, start your services, check their health, or notify your reverse proxy of new IPs.

In a high-availability production environment, all of that must happen after create but before destroy. A single step that both creates and destroys would require Terrafirma to handle that logic, which would greatly complicate the tool's code, configuration, and use.

Instead, ensure your new servers are provisioned, services are running, and your reverse proxy is directing traffic to the new instances prior to deleting old instances. Usually this can be accomplished with a simple bash script gluing everything together.

#Usage example

Terrafirma requires a services.json file describing the services you want to deploy and the types of boxes you'll use. Here's an example:

{
	"requirements": {
		"app-1": {
			"cpu": 2,
			"ram": "12 GB",
			"disk": "20 GB",
			"net": {
				"ports": [5000, 5001],
				"mbps": 300
			}
		},
		"app-2": {
			"cpu": 3,
			"ram": "4 GB",
			"disk": "30 GB"
		},
		"redis": {
			"cpu": 1,
			"ram": "2 GB",
			"disk": "10 GB"
		}
	},
	"counts": {
		"openbsd": {
			"app-1": 2,
			"app-2": 4
		},
		"debian": {
			"redis": 1
		}
	},
	"providers": {
		"gcp:my-project:us-central1:us-central1-b": {
			"openbsd": {
				"machineType": "n1-standard-4",
				"disk": "100 GB",
				"image": "projects/{your-project}/global/images/openbsd-67"
			},
			"debian": {
				"machineType": "n1-highmem-4",
				"disk": "10 GB",
				"image": "projects/gce-uefi-images/global/images/family/debian-11"
			}
		},
	}
}

Since using Terrafirma requires several steps, you'll probably use a short bash script to automate your particular workflow. An example script might look like this:

#!/usr/bin/env bash
set -efux

GCP_TOKEN=$(gcloud auth print-access-token)
export GCP_TOKEN

# Plan the number of servers to be created and destroyed. This creates a
# tf_plan.json file which the remaining steps use.
#
# binp is a binpacking tool that minimizes the number of servers for our box
# type.
terrafirma -b "$(binp)" plan > tf_plan.json

# Create any new servers, wait for them to boot, then use terrafirma to build
# an inventory file mapping the new IPs to each of the services running on
# them. Other tools can convert this inventory.json file to an SSH config file,
# etc.
#
# Terrafirma will automatically create and assign static IP addresses as
# needed.
if terrafirma create; then
	echo "waiting 60s for servers to boot..." && sleep 60
fi

# Get our new inventory only containing our future state
terrafirma -i inventory > inventory.json

# Provision our servers using our usual deployment tools (cup, ansible, etc.).
up -t 100.0.0.1 -c provision_openbsd
up -t 100.0.0.2 -c provision_debian

# Now we know our reverse proxy is sending traffic to these new boxes, so we
# can bring down our old ones and clean up any temporary files when we're done.
#
# Note that Terrafirma will not delete static IPs, so you don't need to worry
# about affecting IP whitelists. Static IPs must be manually deleted.
terrafirma destroy
rm tf_plan.json

If you wanted to wipe out your Terrafirma-managed infrastructure (except for any static IPs):

GCP_TOKEN=... terrafirma -b '{}' plan > tf_plan.json
GCP_TOKEN=... terrafirma destroy

#Future work

Patches for the following would be greatly appreciated:

  • Across a larger team, you'll likely need a locking mechanism. We need a locking design, ideally using the primitives we already use (VMs+tags). Any lock should expire with a timeout in the terrafirma client.
  • Right now it can only deploy Compute Engine VMs (Google's EC2 equivalent). Ideally we'd be able to manage more than that through this tool, but we have no need for that yet.
  • Other clouds would be great to support.