Skip to content

Commit

Permalink
Support creating node groups on hetzner cloud
Browse files Browse the repository at this point in the history
  • Loading branch information
Debakel Orakel committed Aug 31, 2024
1 parent e937994 commit 005533a
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test/*
43 changes: 43 additions & 0 deletions hetzner_node_group/files/setup.ign
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"ignition": {
"version": "3.4.0"
},
"passwd": {
"users": [
{
"name": "core",
"sshAuthorizedKeys": [
"PUBLIC_SSH_KEY"
]
}
]
},
"storage": {
"files": [
{
"overwrite": true,
"path": "/etc/selinux/config",
"contents": {
"compression": "",
"source": "data:,SELINUX%3Dpermissive%0ASELINUXTYPE%3Dtargeted%0A"
}
},
{
"overwrite": true,
"path": "/etc/sysctl.d/60-rke2-cis.conf",
"contents": {
"compression": "",
"source": "data:,vm.panic_on_oom%3D0%0Avm.overcommit_memory%3D1%0Akernel.panic%3D10%0Akernel.panic_on_oops%3D1%0A"
}
},
{
"overwrite": true,
"path": "/etc/zincati/config.d/55-updates-strategy.toml",
"contents": {
"compression": "",
"source": "data:;base64,W3VwZGF0ZXNdCnN0cmF0ZWd5ID0gImZsZWV0X2xvY2siClt1cGRhdGVzLmZsZWV0X2xvY2tdCmJhc2VfdXJsID0gImh0dHA6Ly8xMC40My4wLjExLyIKW2lkZW50aXR5XQpyb2xsb3V0X3dhcmluZXNzID0gMC41Cg=="
}
}
]
}
}
33 changes: 33 additions & 0 deletions hetzner_node_group/files/setup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
variant: fcos
version: 1.5.0
passwd:
users:
- name: core
ssh_authorized_keys:
- PUBLIC_SSH_KEY
storage:
files:
- path: /etc/selinux/config
overwrite: true
contents:
inline: |
SELINUX=permissive
SELINUXTYPE=targeted
- path: /etc/sysctl.d/60-rke2-cis.conf
overwrite: true
contents:
inline: |
vm.panic_on_oom=0
vm.overcommit_memory=1
kernel.panic=10
kernel.panic_on_oops=1
- path: /etc/zincati/config.d/55-updates-strategy.toml
overwrite: true
contents:
inline: |
[updates]
strategy = "fleet_lock"
[updates.fleet_lock]
base_url = "http://10.43.0.11/"
[identity]
rollout_wariness = 0.5
106 changes: 106 additions & 0 deletions hetzner_node_group/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
## --- public ssh key ----------------------------------------------------------

data "hcloud_ssh_key" "nodes" {
id = var.ssh_public_key
}

## --- placement group ---------------------------------------------------------

resource "hcloud_placement_group" "node" {
name = format("%s_%s", var.cluster_name, var.node_group)
type = "spread"
}

## --- nodes -------------------------------------------------------------------

resource "random_id" "node" {
count = var.node_count
prefix = format("%s-", var.node_prefix)
byte_length = 2
}

resource "hcloud_server" "node" {
count = var.node_count

labels = {
"cluster" = var.cluster_name
"group" = var.node_group
"instance" = random_id.node[count.index].hex
}

name = random_id.node[count.index].hex
image = "fedora-40"
rescue = "linux64"
server_type = var.node_type
location = var.cluster_location
ssh_keys = [data.hcloud_ssh_key.nodes.id]
placement_group_id = hcloud_placement_group.node.id

public_net {
ipv4_enabled = var.public_ipv4
ipv6_enabled = var.public_ipv6
}

network {
network_id = var.cluster_network
}

lifecycle {
ignore_changes = [
# Ignore changes to tags, e.g. because a management agent
# updates these based on some ruleset managed elsewhere.
ssh_keys,
rescue,
]
}

connection {
host = var.public_ipv4 ? self.ipv4_address : self.ipv6_address
timeout = "5m"
private_key = file(var.ssh_private_key)
# Root is the available user in rescue mode
user = "root"
}

# Wait for the server to be available
provisioner "local-exec" {
command = "until nc -zv ${var.public_ipv4 ? self.ipv4_address : self.ipv6_address} 22; do sleep 5; done"
}

# Copy setup.ign and replace PUBLIC_SSH_KEY variable
provisioner "file" {
content = replace(file("${path.module}/files/setup.ign"), "PUBLIC_SSH_KEY", trimspace(data.hcloud_ssh_key.nodes.public_key))
destination = "/root/setup.ign"
}

# Get coreos-installer binary
provisioner "remote-exec" {
inline = [
"wget https://s3.eu-central-2.amazonaws.com/tegridy-assets/coreos-installer -O /usr/local/bin/coreos-installer",
"chmod +x /usr/local/bin/coreos-installer",
"coreos-installer install /dev/sda -i /root/setup.ign",
"reboot"
]
}

# Wait for the server to be available
provisioner "local-exec" {
command = "until nc -zv ${var.public_ipv4 ? self.ipv4_address : self.ipv6_address} 22; do sleep 15; done"
}

# Configure CoreOS after installation
provisioner "remote-exec" {
connection {
host = var.public_ipv4 ? self.ipv4_address : self.ipv6_address
timeout = "1m"
private_key = file(var.ssh_private_key)
# This user is configured in setup.yaml
user = "core"
}

inline = [
"sudo hostnamectl set-hostname ${self.name}"
# Add additional commands if needed
]
}
}
3 changes: 3 additions & 0 deletions hetzner_node_group/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "node_id" {
value = hcloud_server.node[*].id
}
12 changes: 12 additions & 0 deletions hetzner_node_group/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
terraform {
required_version = ">= 1.9.5"
required_providers {
hcloud = {
source = "hetznercloud/hcloud"
version = ">= 1.48.0"
}
random = {
source = "hashicorp/random"
}
}
}
78 changes: 78 additions & 0 deletions hetzner_node_group/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
## ----- general configuration -------------------------------------------------

variable "ssh_public_key" {
description = "ID of the public SSH key to configure on the nodes."
type = string
}

variable "ssh_private_key" {
description = "Path to the private SSH key to setup the nodes."
type = string
}

## ----- cluster configuration -------------------------------------------------

variable "cluster_name" {
description = "Name of the cluster."
type = string
}

variable "cluster_network" {
description = "Network ID of the cluster."
type = string
}

variable "cluster_location" {
description = "Hetzner Cloud Location."
type = string
default = "nbg1"

validation {
condition = contains(["nbg1", "fsn1", "hel1", "ash", "hil"], var.cluster_location)
error_message = "Location must be either \"nbg1\", \"fsn1\", \"hel1\", \"ash\" or \"hil\"."
}
}

## ----- node configuration ----------------------------------------------------

variable "node_group" {
description = "Name of the node group."
type = string
}

variable "node_prefix" {
description = "Prefix for the node names."
type = string
default = "node"
}

variable "node_count" {
description = "Number of nodes in the group."
type = number
default = 3
}

variable "node_type" {
description = "Machine type of the nodes."
type = string
default = "cx22"

validation {
condition = contains(["cx22", "cx32", "cx42", "cx52", "cpx11", "cpx21", "cpx31", "cpx41", "cpx51"], var.node_type)
error_message = "Type must be either \"cx(2|3|4|5)2\" or \"cpx(2|3|4|5)1\"."
}
}

## ----- public network --------------------------------------------------------

variable "public_ipv4" {
description = "Enable public IPv4."
type = bool
default = false
}

variable "public_ipv6" {
description = "Enable public IPv6."
type = bool
default = false
}

0 comments on commit 005533a

Please sign in to comment.