Move infra-setup and k3s-setup into dedicated directories
This changes makes it easier to differentiate and understand the different parts of the kubernetes setup. On one hand we have the bare infrastructure (servers, network, etc), on the other hand we have the software (k3s in this case). In the future we'll have a few more parts, like the minimal configuration of the kubernetes cluster, e.g. with a cert-manager. This is easier to manage with helm or terraform than with ansible. Therefore it makes even more sense to split the responsibilities into dedicated directories.
This commit is contained in:
99
infra/.terraform.lock.hcl
generated
Normal file
99
infra/.terraform.lock.hcl
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/external" {
|
||||
version = "2.3.4"
|
||||
constraints = "2.3.4"
|
||||
hashes = [
|
||||
"h1:XWkRZOLKMjci9/JAtE8X8fWOt7A4u+9mgXSUjc4Wuyo=",
|
||||
"zh:037fd82cd86227359bc010672cd174235e2d337601d4686f526d0f53c87447cb",
|
||||
"zh:0ea1db63d6173d01f2fa8eb8989f0809a55135a0d8d424b08ba5dabad73095fa",
|
||||
"zh:17a4d0a306566f2e45778fbac48744b6fd9c958aaa359e79f144c6358cb93af0",
|
||||
"zh:298e5408ab17fd2e90d2cd6d406c6d02344fe610de5b7dae943a58b958e76691",
|
||||
"zh:38ecfd29ee0785fd93164812dcbe0664ebbe5417473f3b2658087ca5a0286ecb",
|
||||
"zh:59f6a6f31acf66f4ea3667a555a70eba5d406c6e6d93c2c641b81d63261eeace",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:ad0279dfd09d713db0c18469f585e58d04748ca72d9ada83883492e0dd13bd58",
|
||||
"zh:c69f66fd21f5e2c8ecf7ca68d9091c40f19ad913aef21e3ce23836e91b8cbb5f",
|
||||
"zh:d4a56f8c48aa86fc8e0c233d56850f5783f322d6336f3bf1916e293246b6b5d4",
|
||||
"zh:f2b394ebd4af33f343835517e80fc876f79361f4688220833bc3c77655dd2202",
|
||||
"zh:f31982f29f12834e5d21e010856eddd19d59cd8f449adf470655bfd19354377e",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/local" {
|
||||
version = "2.5.3"
|
||||
hashes = [
|
||||
"h1:1Nkh16jQJMp0EuDmvP/96f5Unnir0z12WyDuoR6HjMo=",
|
||||
"zh:284d4b5b572eacd456e605e94372f740f6de27b71b4e1fd49b63745d8ecd4927",
|
||||
"zh:40d9dfc9c549e406b5aab73c023aa485633c1b6b730c933d7bcc2fa67fd1ae6e",
|
||||
"zh:6243509bb208656eb9dc17d3c525c89acdd27f08def427a0dce22d5db90a4c8b",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:885d85869f927853b6fe330e235cd03c337ac3b933b0d9ae827ec32fa1fdcdbf",
|
||||
"zh:bab66af51039bdfcccf85b25fe562cbba2f54f6b3812202f4873ade834ec201d",
|
||||
"zh:c505ff1bf9442a889ac7dca3ac05a8ee6f852e0118dd9a61796a2f6ff4837f09",
|
||||
"zh:d36c0b5770841ddb6eaf0499ba3de48e5d4fc99f4829b6ab66b0fab59b1aaf4f",
|
||||
"zh:ddb6a407c7f3ec63efb4dad5f948b54f7f4434ee1a2607a49680d494b1776fe1",
|
||||
"zh:e0dafdd4500bec23d3ff221e3a9b60621c5273e5df867bc59ef6b7e41f5c91f6",
|
||||
"zh:ece8742fd2882a8fc9d6efd20e2590010d43db386b920b2a9c220cfecc18de47",
|
||||
"zh:f4c6b3eb8f39105004cf720e202f04f57e3578441cfb76ca27611139bc116a82",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/random" {
|
||||
version = "3.7.1"
|
||||
constraints = "3.7.1"
|
||||
hashes = [
|
||||
"h1:/qtweZW2sk0kBNiQM02RvBXmlVdI9oYqRMCyBZ8XA98=",
|
||||
"zh:3193b89b43bf5805493e290374cdda5132578de6535f8009547c8b5d7a351585",
|
||||
"zh:3218320de4be943e5812ed3de995946056db86eb8d03aa3f074e0c7316599bef",
|
||||
"zh:419861805a37fa443e7d63b69fb3279926ccf98a79d256c422d5d82f0f387d1d",
|
||||
"zh:4df9bd9d839b8fc11a3b8098a604b9b46e2235eb65ef15f4432bde0e175f9ca6",
|
||||
"zh:5814be3f9c9cc39d2955d6f083bae793050d75c572e70ca11ccceb5517ced6b1",
|
||||
"zh:63c6548a06de1231c8ee5570e42ca09c4b3db336578ded39b938f2156f06dd2e",
|
||||
"zh:697e434c6bdee0502cc3deb098263b8dcd63948e8a96d61722811628dce2eba1",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:a0b8e44927e6327852bbfdc9d408d802569367f1e22a95bcdd7181b1c3b07601",
|
||||
"zh:b7d3af018683ef22794eea9c218bc72d7c35a2b3ede9233b69653b3c782ee436",
|
||||
"zh:d63b911d618a6fe446c65bfc21e793a7663e934b2fef833d42d3ccd38dd8d68d",
|
||||
"zh:fa985cd0b11e6d651f47cff3055f0a9fd085ec190b6dbe99bf5448174434cdea",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hetznercloud/hcloud" {
|
||||
version = "1.50.0"
|
||||
constraints = "1.50.0"
|
||||
hashes = [
|
||||
"h1:z5J9wgkt9xIKlr699hWCjHSS7K4bYKWWnGCg2T/YNmg=",
|
||||
"zh:0bd650fb52e272f74eda5053a7bb62f0fd92182f57ad3ef742abe165cb8cac98",
|
||||
"zh:1c36667aa89b672a96c0df3d3c613e80916a2d0944b1a1f9112065f40630b689",
|
||||
"zh:21f90683890ea7a184b0ac55efd52911694ba86c58898bc8bbe87ee2507bb1eb",
|
||||
"zh:24349d483a6ff97420d847433553fa031f68f99b9ead4ebb3592fc8955ef521f",
|
||||
"zh:3fffd83c450bea2b382a986501ae51a4d3e6530eda48ed9ca74d518e4a909c37",
|
||||
"zh:43d7de1dc4c50fae99d6c4ab4bb394608948091f5b53ddb29bc65deead9dc8a6",
|
||||
"zh:47a37d5fec79dd8bc9cab2c892bc59e135b86cb51eebe2b01cdb40afac7ed777",
|
||||
"zh:6efeb9530b8f57618c43f0b294b983d06cce43e9423bdd737eed81db913edb80",
|
||||
"zh:7511ace4b33baddfc452ef95a634d83b92bfbfaa23cb30403899e95b64727075",
|
||||
"zh:7bade77104ed8788c9b5171c7daae6ab6c011b3c40b152274fda803bf0bf2707",
|
||||
"zh:83bce3ff9a1bd52a340a6ebdd2e2b731ec6fb86811ef0ed8a8264daf9d7beb61",
|
||||
"zh:a09d5fce4c8d33e10b9a19318c965076db2d8ed5f62f5feb3e7502416f66d7bf",
|
||||
"zh:c942832b80270eb982eeb9cc14f30a437db5fd28faf37d6aa32ec2cd345537d6",
|
||||
"zh:e2c1812f2e1f9fac17c7551d4ab0efb713b6d751087c18b84b8acd542f587459",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/timohirt/hetznerdns" {
|
||||
version = "2.2.0"
|
||||
constraints = "2.2.0"
|
||||
hashes = [
|
||||
"h1:HyskQAglrOueur79gSCBgx9MNDOs0tz39aNYQiFgxz8=",
|
||||
"zh:5bb0ab9f62be3ed92070235e507f3c290491d51391ef4edcc70df53b65a83019",
|
||||
"zh:5ccdfac7284f5515ac3cff748336b77f21c64760e429e811a1eeefa8ebb86e12",
|
||||
"zh:687c35665139ae37c291e99085be2e38071f6b355c4e1e8957c5a6a3bcdf9caf",
|
||||
"zh:6de27f0d0d1513b3a4b7e81923b4a8506c52759bd466e2b4f8156997b0478931",
|
||||
"zh:85770a9199a4c2d16ca41538d7a0f7a7bfc060678104a1faac19213e6f0a800c",
|
||||
"zh:a5ff723774a9ccfb27d5766c5e6713537f74dd94496048c89c5d64dba597e59e",
|
||||
"zh:bf9ab76fd37cb8aebb6868d73cbe8c08cee36fc25224cc1ef5949efa3c34b06c",
|
||||
"zh:db998fe3bdcd4902e99fa470bb3f355883170cf4c711c8da0b5f1f4510f1be41",
|
||||
]
|
||||
}
|
||||
21
infra/README.adoc
Normal file
21
infra/README.adoc
Normal file
@@ -0,0 +1,21 @@
|
||||
= infra
|
||||
:icons: font
|
||||
|
||||
This project is responsible for providing the required infra to run a kubernetes-cluster.
|
||||
|
||||
== Setup
|
||||
|
||||
// tag::setup[]
|
||||
[WARNING]
|
||||
Make sure `config.auto.tfvars` with all the needed configuration-secrets is present otherwise the module cannot be applied!
|
||||
The file is savely stored in the password-manager.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
terraform init # <1>
|
||||
terraform apply # <2>
|
||||
----
|
||||
|
||||
<1> Init the terraform modules if necessary
|
||||
<2> Setup infrastructure and create/update inventory.ini (This might take some time, even after it's "ready")
|
||||
// end::setup[]
|
||||
13
infra/inventory.ini.tftpl
Normal file
13
infra/inventory.ini.tftpl
Normal file
@@ -0,0 +1,13 @@
|
||||
[server]
|
||||
%{for ip in server_ips~}
|
||||
${ip}
|
||||
%{endfor~}
|
||||
|
||||
[agent]
|
||||
%{for ip in agent_ips~}
|
||||
${ip}
|
||||
%{endfor~}
|
||||
|
||||
[k3s_cluster:children]
|
||||
server
|
||||
agent
|
||||
163
infra/main.tf
Normal file
163
infra/main.tf
Normal file
@@ -0,0 +1,163 @@
|
||||
resource "hcloud_ssh_key" "this" {
|
||||
for_each = var.ssh_keys
|
||||
name = each.key
|
||||
public_key = each.value
|
||||
}
|
||||
|
||||
resource "hcloud_primary_ip" "k8s_ipv4" {
|
||||
count = var.k8s_server_count < 1 ? 1 : var.k8s_server_count
|
||||
|
||||
name = "k8s_primary_ipv4_${count.index}"
|
||||
datacenter = var.k8s_ip_datacenter
|
||||
type = "ipv4"
|
||||
assignee_type = "server"
|
||||
auto_delete = !var.k8s_test_installation
|
||||
}
|
||||
|
||||
resource "hcloud_primary_ip" "k8s_ipv6" {
|
||||
count = var.k8s_server_count < 1 ? 1 : var.k8s_server_count
|
||||
|
||||
name = "k8s_primary_ipv6_${count.index}"
|
||||
datacenter = var.k8s_ip_datacenter
|
||||
type = "ipv6"
|
||||
assignee_type = "server"
|
||||
auto_delete = !var.k8s_test_installation
|
||||
}
|
||||
|
||||
data "external" "my_ip" {
|
||||
program = [
|
||||
"sh",
|
||||
"-c",
|
||||
"(dig TXT +short -4 o-o.myaddr.l.google.com @ns1.google.com && dig TXT +short -6 o-o.myaddr.l.google.com @ns1.google.com) | jq '{(.): .}' | jq -s add"
|
||||
]
|
||||
}
|
||||
|
||||
module "k8s" {
|
||||
source = "./modules/hetzner/kubernetes"
|
||||
|
||||
name = "cluster1"
|
||||
ssh_keys = [for o in hcloud_ssh_key.this : o.id]
|
||||
servers = [for n in range(var.k8s_server_count) : {
|
||||
ipv4_id = hcloud_primary_ip.k8s_ipv4[n].id
|
||||
ipv6_id = hcloud_primary_ip.k8s_ipv6[n].id
|
||||
type = var.k8s_server_type
|
||||
location = var.k8s_location
|
||||
}]
|
||||
agents = [{
|
||||
type = var.k8s_agent_type
|
||||
location = var.k8s_location
|
||||
count = var.k8s_agent_count
|
||||
}]
|
||||
kubernetes_exposed_ips = var.expose_kubernetes_and_ssh_ports ? values(data.external.my_ip.result) : []
|
||||
ssh_exposed_ips = var.expose_kubernetes_and_ssh_ports ? values(data.external.my_ip.result) : []
|
||||
public_tcp_services = {
|
||||
http = ["80", "443"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "ansible_inventory" {
|
||||
filename = "${path.module}/../k3s/inventory.ini"
|
||||
content = templatefile("./inventory.ini.tftpl", {
|
||||
server_ips = module.k8s.server_ips_v4,
|
||||
agent_ips = module.k8s.agent_ips_v4,
|
||||
})
|
||||
}
|
||||
|
||||
locals {
|
||||
dns_zones = {
|
||||
# costs-table (incl. taxes):
|
||||
# domain | amount | hetzner (einzel) | tecspace (einzel)
|
||||
# --------+--------+------------------+-------------------
|
||||
# de | 3 | 35.70€ (11.90€) | 15.85€ (5.95€)
|
||||
# info | 1 | 30.94€ (30.94€) | 21.95€ (21.95€)
|
||||
# net | 2 | 33.32€ (16.66€) | 35.90€ (17.95€)
|
||||
# org | 1 | 17.85€ (17.85€) | 21.95€ (21.95€)
|
||||
# --------+--------+------------------+-------------------
|
||||
# sum | 7 | 117.21€ | 97.65€
|
||||
#
|
||||
# Note, that Hetzner usually shows prices w/o taxes!
|
||||
#
|
||||
# Reference: https://www.hetzner.com/de/whois/
|
||||
# Reference: https://www.tecspace.de/domain-preisliste
|
||||
"goperte.de" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
]
|
||||
},
|
||||
"nehrke.info" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "@", ttl = 86400, type = "MX", value = "1 smtp.google.com." },
|
||||
{ name = "@", ttl = 86400, type = "TXT", value = "v=spf1 include:_spf.google.com a mx ~all" },
|
||||
{ name = "_dmarc", ttl = 86400, type = "TXT", value = "v=DMARC1; p=none;" },
|
||||
{ name = "google._domainkey", ttl = 86400, type = "TXT", value = var.nehrke_info_dkim },
|
||||
]
|
||||
},
|
||||
"sozpaedil.net" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "@", ttl = 86400, type = "MX", value = "1 smtp.google.com." },
|
||||
{ name = "@", ttl = 86400, type = "TXT", value = "v=spf1 include:_spf.google.com a mx ~all" },
|
||||
{ name = "_dmarc", ttl = 86400, type = "TXT", value = "v=DMARC1; p=none;" },
|
||||
{ name = "google._domainkey", ttl = 86400, type = "TXT", value = var.sozpaedil_net_dkim },
|
||||
]
|
||||
},
|
||||
"tovot.de" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
]
|
||||
},
|
||||
"tovot.net" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
]
|
||||
},
|
||||
"tovot.org" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
]
|
||||
},
|
||||
"xn--alleingnger-r8a.de" = {
|
||||
zone_ttl = 900
|
||||
records = [
|
||||
{ name = "@", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "*", type = "A", value = hcloud_primary_ip.k8s_ipv4[0].ip_address },
|
||||
{ name = "@", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
{ name = "*", type = "AAAA", value = "${hcloud_primary_ip.k8s_ipv6[0].ip_address}1" },
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
module "dns" {
|
||||
source = "./modules/hetzner/dns"
|
||||
for_each = local.dns_zones
|
||||
zone = each.key
|
||||
zone_ttl = lookup(each.value, "zone_ttl")
|
||||
records = lookup(each.value, "records")
|
||||
}
|
||||
|
||||
25
infra/modules/hetzner/dns/main.tf
Normal file
25
infra/modules/hetzner/dns/main.tf
Normal file
@@ -0,0 +1,25 @@
|
||||
resource "hetznerdns_zone" "this" {
|
||||
name = var.zone
|
||||
ttl = var.zone_ttl
|
||||
}
|
||||
|
||||
locals {
|
||||
records = nonsensitive({
|
||||
for record in var.records : "${record.type}#${record.name}#${md5(record.value)}" => {
|
||||
for key, value in record : key => value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resource "hetznerdns_record" "this" {
|
||||
for_each = local.records
|
||||
zone_id = hetznerdns_zone.this.id
|
||||
name = each.value.name
|
||||
type = each.value.type
|
||||
value = (each.value.type == "TXT"
|
||||
? "\"${join("\" \"", [for c in chunklist(split("", each.value.value), 255) : join("", c)])}\""
|
||||
: each.value.value
|
||||
)
|
||||
ttl = each.value.ttl
|
||||
}
|
||||
|
||||
0
infra/modules/hetzner/dns/outputs.tf
Normal file
0
infra/modules/hetzner/dns/outputs.tf
Normal file
19
infra/modules/hetzner/dns/variables.tf
Normal file
19
infra/modules/hetzner/dns/variables.tf
Normal file
@@ -0,0 +1,19 @@
|
||||
variable "zone" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "zone_ttl" {
|
||||
type = number
|
||||
default = 3600
|
||||
}
|
||||
|
||||
variable "records" {
|
||||
type = set(object({
|
||||
name = string
|
||||
value = string
|
||||
type = string
|
||||
ttl = optional(number, null)
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
10
infra/modules/hetzner/dns/versions.tf
Normal file
10
infra/modules/hetzner/dns/versions.tf
Normal file
@@ -0,0 +1,10 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
hetznerdns = {
|
||||
source = "timohirt/hetznerdns"
|
||||
version = "2.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
infra/modules/hetzner/kubernetes/agent-init.yaml.tftpl
Normal file
17
infra/modules/hetzner/kubernetes/agent-init.yaml.tftpl
Normal file
@@ -0,0 +1,17 @@
|
||||
#cloud-config
|
||||
packages:
|
||||
- curl
|
||||
users:
|
||||
- name: cluster
|
||||
shell: /bin/bash
|
||||
runcmd:
|
||||
# configure correct routing via NAT
|
||||
- ip route add default via ${network_gateway}
|
||||
- NIC=$(ifconfig | grep -q enp7s0 && echo enp7s0 || echo ens10)
|
||||
- echo "[Match]" > /etc/systemd/network/10-$NIC.network
|
||||
- echo "Name=$NIC" >> /etc/systemd/network/10-$NIC.network
|
||||
- echo "[Network]" >> /etc/systemd/network/10-$NIC.network
|
||||
- echo "DHCP=yes" >> /etc/systemd/network/10-$NIC.network
|
||||
- echo "Gateway=${network_gateway}" >> /etc/systemd/network/10-$NIC.network
|
||||
- sed -e "s/#DNS=/DNS=${dns_servers}/" -i /etc/systemd/resolved.conf
|
||||
- systemctl restart systemd-resolved
|
||||
133
infra/modules/hetzner/kubernetes/main.tf
Normal file
133
infra/modules/hetzner/kubernetes/main.tf
Normal file
@@ -0,0 +1,133 @@
|
||||
locals {
|
||||
network = "10.0.0.0/16"
|
||||
subnet_eu_central = "10.0.0.0/24"
|
||||
servers = {
|
||||
for idx, config in var.servers : "${var.name}-server-${idx + 1}" => merge(
|
||||
config,
|
||||
{
|
||||
ip = cidrhost(local.subnet_eu_central, idx + 2)
|
||||
first_ip = idx == 0 ? "" : cidrhost(local.subnet_eu_central, 2)
|
||||
}
|
||||
)
|
||||
}
|
||||
agents = merge([
|
||||
for idx, config in var.agents : {
|
||||
for n in range(0, config.count) : "${var.name}-agent-${idx + 1}-${n + 1}" => merge(
|
||||
config,
|
||||
{ ip = cidrhost(local.subnet_eu_central, 255 - (idx * 20) - n - 1) }
|
||||
)
|
||||
}
|
||||
]...)
|
||||
all_ips = ["0.0.0.0/0", "::/0"]
|
||||
ping_firewall = var.ping_enabled ? { "ping" : [{ protocol = "icmp", port = null }] } : {}
|
||||
k8s_firewall = { "kubernetes" : [{ port = "6443", source_ips = concat([local.network], var.kubernetes_exposed_ips) }] }
|
||||
ssh_firewall = length(var.ssh_exposed_ips) > 0 ? { "ssh" : [{ port = 1022, source_ips = var.ssh_exposed_ips }] } : {}
|
||||
service_firewalls = { for service, ports in var.public_tcp_services : service => [for port in ports : { port = port }] }
|
||||
firewalls = merge(
|
||||
local.ping_firewall,
|
||||
local.k8s_firewall,
|
||||
local.ssh_firewall,
|
||||
local.service_firewalls
|
||||
)
|
||||
}
|
||||
|
||||
resource "hcloud_network" "this" {
|
||||
name = var.name
|
||||
ip_range = local.network
|
||||
}
|
||||
|
||||
resource "hcloud_network_subnet" "this" {
|
||||
type = "cloud"
|
||||
network_id = hcloud_network.this.id
|
||||
network_zone = "eu-central"
|
||||
ip_range = local.subnet_eu_central
|
||||
}
|
||||
|
||||
resource "hcloud_network_route" "this" {
|
||||
network_id = hcloud_network.this.id
|
||||
destination = "0.0.0.0/0"
|
||||
gateway = cidrhost(local.subnet_eu_central, 2)
|
||||
}
|
||||
|
||||
resource "random_string" "k3s_token" {
|
||||
length = 100
|
||||
special = false
|
||||
}
|
||||
|
||||
resource "hcloud_firewall" "this" {
|
||||
for_each = local.firewalls
|
||||
|
||||
name = each.key
|
||||
dynamic "rule" {
|
||||
for_each = each.value
|
||||
content {
|
||||
direction = lookup(rule.value, "direction", "in")
|
||||
protocol = lookup(rule.value, "protocol", "tcp")
|
||||
source_ips = lookup(rule.value, "source_ips", local.all_ips)
|
||||
port = lookup(rule.value, "port")
|
||||
}
|
||||
}
|
||||
}
|
||||
resource "hcloud_server" "server" {
|
||||
depends_on = [hcloud_network_subnet.this]
|
||||
for_each = local.servers
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [ user_data ]
|
||||
}
|
||||
|
||||
name = each.key
|
||||
image = "ubuntu-24.04"
|
||||
server_type = each.value.type
|
||||
location = each.value.location
|
||||
ssh_keys = var.ssh_keys
|
||||
public_net {
|
||||
ipv4 = each.value.ipv4_id
|
||||
ipv6 = each.value.ipv6_id
|
||||
}
|
||||
network {
|
||||
network_id = hcloud_network.this.id
|
||||
ip = each.value.ip
|
||||
}
|
||||
user_data = templatefile(
|
||||
"${path.module}/server-init.yaml.tftpl",
|
||||
{
|
||||
network_ip_range = local.network
|
||||
k3s_token = random_string.k3s_token.result
|
||||
first_ip = each.value.first_ip
|
||||
}
|
||||
)
|
||||
firewall_ids = [for firewall in hcloud_firewall.this : firewall.id]
|
||||
}
|
||||
|
||||
resource "hcloud_server" "agent" {
|
||||
depends_on = [hcloud_server.server]
|
||||
for_each = local.agents
|
||||
|
||||
lifecycle {
|
||||
ignore_changes = [ user_data ]
|
||||
}
|
||||
|
||||
name = each.key
|
||||
image = "ubuntu-24.04"
|
||||
server_type = each.value.type
|
||||
location = each.value.location
|
||||
ssh_keys = var.ssh_keys
|
||||
public_net {
|
||||
ipv4_enabled = false
|
||||
ipv6_enabled = false
|
||||
}
|
||||
network {
|
||||
network_id = hcloud_network.this.id
|
||||
ip = each.value.ip
|
||||
}
|
||||
user_data = templatefile(
|
||||
"${path.module}/agent-init.yaml.tftpl",
|
||||
{
|
||||
server_ip = cidrhost(local.subnet_eu_central, 2)
|
||||
network_gateway = cidrhost(local.subnet_eu_central, 1)
|
||||
dns_servers = "8.8.8.8 8.8.4.4"
|
||||
k3s_token = random_string.k3s_token.result
|
||||
}
|
||||
)
|
||||
}
|
||||
11
infra/modules/hetzner/kubernetes/outputs.tf
Normal file
11
infra/modules/hetzner/kubernetes/outputs.tf
Normal file
@@ -0,0 +1,11 @@
|
||||
output "server_ips_v4" {
|
||||
value = [for key, value in hcloud_server.server : value.ipv4_address]
|
||||
}
|
||||
|
||||
output "server_ips_v6" {
|
||||
value = [for key, value in hcloud_server.server : value.ipv6_address]
|
||||
}
|
||||
|
||||
output "agent_ips_v4" {
|
||||
value = flatten([for key, value in hcloud_server.agent : value.network.*.ip])
|
||||
}
|
||||
13
infra/modules/hetzner/kubernetes/server-init.yaml.tftpl
Normal file
13
infra/modules/hetzner/kubernetes/server-init.yaml.tftpl
Normal file
@@ -0,0 +1,13 @@
|
||||
#cloud-config
|
||||
packages:
|
||||
- curl
|
||||
users:
|
||||
- name: cluster
|
||||
shell: /bin/bash
|
||||
runcmd:
|
||||
# configure NAT
|
||||
- echo '#!/bin/bash' > /etc/networkd-dispatcher/routable.d/10-eth0-post-up
|
||||
- echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/networkd-dispatcher/routable.d/10-eth0-post-up
|
||||
- echo 'iptables -t nat -A POSTROUTING -s ${network_ip_range} -o eth0 -j MASQUERADE' >> /etc/networkd-dispatcher/routable.d/10-eth0-post-up
|
||||
- chmod +x /etc/networkd-dispatcher/routable.d/10-eth0-post-up
|
||||
- /etc/networkd-dispatcher/routable.d/10-eth0-post-up
|
||||
44
infra/modules/hetzner/kubernetes/variables.tf
Normal file
44
infra/modules/hetzner/kubernetes/variables.tf
Normal file
@@ -0,0 +1,44 @@
|
||||
variable "name" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "ssh_keys" {
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
variable "servers" {
|
||||
type = list(object({
|
||||
ipv4_id = number
|
||||
ipv6_id = number
|
||||
type = string
|
||||
location = string
|
||||
}))
|
||||
}
|
||||
|
||||
variable "agents" {
|
||||
type = list(object({
|
||||
count = optional(number, 1)
|
||||
type = string
|
||||
location = string
|
||||
}))
|
||||
}
|
||||
|
||||
variable "ping_enabled" {
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "public_tcp_services" {
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "kubernetes_exposed_ips" {
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "ssh_exposed_ips" {
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
14
infra/modules/hetzner/kubernetes/versions.tf
Normal file
14
infra/modules/hetzner/kubernetes/versions.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
hcloud = {
|
||||
source = "hetznercloud/hcloud"
|
||||
version = "1.50.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "3.7.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
74
infra/variables.tf
Normal file
74
infra/variables.tf
Normal file
@@ -0,0 +1,74 @@
|
||||
variable "expose_kubernetes_and_ssh_ports" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "hetzner_dns_apitoken" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "hetzner_cloud_apitoken" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "nehrke_info_dkim" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "sozpaedil_net_dkim" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "ssh_keys" {
|
||||
type = map(string)
|
||||
}
|
||||
|
||||
variable "k8s_test_installation" {
|
||||
default = false
|
||||
description = <<EOF
|
||||
When this is set to true we configure primary-ips to not be deleted automatically!
|
||||
This allows us to reuse the same IP for multiple create/destroy cycles.
|
||||
As soon as the test-phase is over this should be false.
|
||||
EOF
|
||||
}
|
||||
|
||||
# Right now this only supports 1 location, but that's okay for me!
|
||||
variable "k8s_location" {
|
||||
type = string
|
||||
description = <<EOF
|
||||
This is the location where I host the k8s-cluster.
|
||||
An overview of possible locations can be found at: https://docs.hetzner.com/cloud/general/locations/
|
||||
EOF
|
||||
}
|
||||
|
||||
variable "k8s_ip_datacenter" {
|
||||
type = string
|
||||
description = <<EOF
|
||||
This is the datacenter where the public IPs of the k8s-cluster belong to.
|
||||
An overview of possible locations can be found at: https://docs.hetzner.com/cloud/general/locations/
|
||||
Note, that the k8s_ip_datacenter has to match the k8s_location!
|
||||
EOF
|
||||
}
|
||||
|
||||
variable "k8s_server_count" {
|
||||
type = number
|
||||
default = 3
|
||||
description = "Number of k8s-server nodes. This should always be an odd number."
|
||||
}
|
||||
|
||||
variable "k8s_server_type" {
|
||||
type = string
|
||||
default = "cax11"
|
||||
}
|
||||
|
||||
variable "k8s_agent_count" {
|
||||
type = number
|
||||
default = 3
|
||||
}
|
||||
|
||||
variable "k8s_agent_type" {
|
||||
type = string
|
||||
default = "cax11"
|
||||
}
|
||||
31
infra/versions.tf
Normal file
31
infra/versions.tf
Normal file
@@ -0,0 +1,31 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
hetznerdns = {
|
||||
source = "timohirt/hetznerdns"
|
||||
version = "2.2.0"
|
||||
}
|
||||
hcloud = {
|
||||
source = "hetznercloud/hcloud"
|
||||
version = "1.50.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "3.7.1"
|
||||
}
|
||||
external = {
|
||||
source = "hashicorp/external"
|
||||
version = "2.3.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "hetznerdns" {
|
||||
apitoken = var.hetzner_dns_apitoken
|
||||
}
|
||||
|
||||
provider "hcloud" {
|
||||
token = var.hetzner_cloud_apitoken
|
||||
}
|
||||
|
||||
provider "random" {}
|
||||
provider "external" {}
|
||||
Reference in New Issue
Block a user