Compare commits

2 Commits

Author SHA1 Message Date
Max
9e90135b00 feat: simple hello service 2026-01-01 19:05:44 -05:00
Max
ac8b6d8f9a fix: automatically create dns recods 2026-01-01 18:59:53 -05:00
9 changed files with 288 additions and 233 deletions

View File

@@ -4,7 +4,7 @@
vars_files: vars_files:
- ../vault.yml - ../vault.yml
tasks: tasks:
- name: Destroy Terraform. - name: Destroy
community.general.terraform: community.general.terraform:
project_path: '../terraform' project_path: '../terraform'
state: "absent" state: "absent"

View File

@@ -1,154 +1,140 @@
# - name: Configure compute for the cluster. - name: Configure compute for the cluster.
# hosts: servers hosts: servers
# gather_facts: false gather_facts: false
# vars: vars:
# kubernetes_version: v1.30 kubernetes_version: v1.30
# tasks: tasks:
# - name: Download Kubernetes key. - name: Download Kubernetes key.
# ansible.builtin.apt_key: ansible.builtin.apt_key:
# url: https://pkgs.k8s.io/core:/stable:/{{ kubernetes_version }}/deb/Release.key url: https://pkgs.k8s.io/core:/stable:/{{ kubernetes_version }}/deb/Release.key
# state: present state: present
# - name: Download Kubernetes repository. - name: Download Kubernetes repository.
# ansible.builtin.apt_repository: ansible.builtin.apt_repository:
# repo: "deb https://pkgs.k8s.io/core:/stable:/{{ kubernetes_version }}/deb/ /" repo: "deb https://pkgs.k8s.io/core:/stable:/{{ kubernetes_version }}/deb/ /"
# state: present state: present
# - name: Download CRI-O key. - name: Download CRI-O key.
# ansible.builtin.apt_key: ansible.builtin.apt_key:
# url: https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/Release.key url: https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/Release.key
# state: present state: present
# - name: Download CRI-O repository. - name: Download CRI-O repository.
# ansible.builtin.apt_repository: ansible.builtin.apt_repository:
# repo: "deb https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/ /" repo: "deb https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/ /"
# state: present state: present
# - name: Download Helm key. - name: Download Helm key.
# ansible.builtin.apt_key: ansible.builtin.apt_key:
# url: https://packages.buildkite.com/helm-linux/helm-debian/gpgkey url: https://packages.buildkite.com/helm-linux/helm-debian/gpgkey
# state: present state: present
# - name: Download Helm repository. - name: Download Helm repository.
# ansible.builtin.apt_repository: ansible.builtin.apt_repository:
# repo: "deb https://packages.buildkite.com/helm-linux/helm-debian/any/ any main" repo: "deb https://packages.buildkite.com/helm-linux/helm-debian/any/ any main"
# state: present state: present
# - name: Install packages. - name: Install packages.
# ansible.builtin.apt: ansible.builtin.apt:
# state: present state: present
# update_cache: true update_cache: true
# name: [cri-o, kubelet, kubeadm, kubectl, python3-pip, helm, git] name: [cri-o, kubelet, kubeadm, kubectl, python3-pip, helm, git]
# - name: Install Kubernetes Python packages. - name: Install Kubernetes Python packages.
# ansible.builtin.pip: ansible.builtin.pip:
# name: [kubernetes, pyyaml] name: [kubernetes, pyyaml]
# state: present state: present
# break_system_packages: true break_system_packages: true
# - name: Enable `br_netfilter` module. - name: Enable IPv4 forwarding.
# community.general.modprobe: ansible.posix.sysctl:
# name: br_netfilter name: net.ipv4.ip_forward
# state: present value: '1'
# notify: Reboot the nodes. sysctl_set: true
notify: Reboot the nodes.
# - name: Configure `sysctl` permanently. - name: Enable `br_netfilter` module.
# ansible.posix.sysctl: community.general.modprobe:
# name: '{{ item }}' name: br_netfilter
# value: '1' state: present
# state: present notify: Reboot the nodes.
# reload: true
# loop:
# - net.bridge.bridge-nf-call-iptables
# - net.ipv4.ip_forward
# handlers: handlers:
# - name: Reboot the nodes. - name: Reboot the nodes.
# ansible.builtin.reboot: ansible.builtin.reboot:
# - name: Spawn new cluster on control node. - name: Spawn new cluster on control node.
# hosts: control hosts: control
# gather_facts: false gather_facts: false
# vars: vars:
# config_template: ../templates/InitConfiguration.yml.jinja2 config_template: ../templates/InitConfiguration.yml.jinja2
# config: config:
# bootstrap_token: "{{ secrets.bootstrap_token }}" bootstrap_token: "{{ secrets.bootstrap_token }}"
# node_ip: 10.0.2.11 node_ip: 10.0.2.11
# node_name: control node_name: control
# vars_files: vars_files:
# - ../vault.yml - ../vault.yml
# tasks: tasks:
# - name: Test for cluster. - name: Test for cluster.
# kubernetes.core.k8s_cluster_info: kubernetes.core.k8s_cluster_info:
# register: api_status register: api_status
# ignore_errors: true ignore_errors: true
# - name: Copy configuration over. - name: Copy configuration over.
# ansible.builtin.template: ansible.builtin.template:
# src: "{{ config_template }}" src: "{{ config_template }}"
# dest: InitConfiguration.yml dest: InitConfiguration.yml
# mode: preserve mode: preserve
# when: "api_status.failed" when: "api_status.failed"
# - name: Initialize cluster. - name: Initialize cluster.
# ansible.builtin.command: ansible.builtin.command:
# kubeadm init --config InitConfiguration.yml kubeadm init --config InitConfiguration.yml
# changed_when: true changed_when: true
# when: "api_status.failed" when: "api_status.failed"
# - name: Apply the Kubernetes config to the shell. - name: Apply the Kubernetes config to the shell.
# ansible.builtin.lineinfile: ansible.builtin.lineinfile:
# path: /etc/environment path: /etc/environment
# line: 'KUBECONFIG=/etc/kubernetes/admin.conf' line: 'KUBECONFIG=/etc/kubernetes/admin.conf'
# when: "api_status.failed" when: "api_status.failed"
# - name: Join worker nodes to cluster. - name: Join worker nodes to cluster.
# hosts: [node-a, node-b] hosts: [node-a, node-b]
# vars: vars:
# join_template: ../templates/JoinConfiguration.yml.jinja2 join_template: ../templates/JoinConfiguration.yml.jinja2
# join_control_ip: 10.0.2.11 join_control_ip: 10.0.2.11
# join_bootstrap_token: "{{ secrets.bootstrap_token }}" join_bootstrap_token: "{{ secrets.bootstrap_token }}"
# vars_files: vars_files:
# - ../vault.yml - ../vault.yml
# tasks: tasks:
# - name: Copy join configuration over. - name: Copy join configuration over.
# vars: vars:
# join_worker_ip: "{{ ansible_default_ipv4.address }}" join_worker_ip: "{{ ansible_default_ipv4.address }}"
# join_worker_name: "{{ ansible_hostname }}" join_worker_name: "{{ ansible_hostname }}"
# ansible.builtin.template: ansible.builtin.template:
# src: "{{ join_template }}" src: "{{ join_template }}"
# dest: JoinConfiguration.yml dest: JoinConfiguration.yml
# mode: preserve mode: preserve
# - name: Join the nodes. - name: Join the nodes.
# ansible.builtin.command: ansible.builtin.command:
# kubeadm join --config JoinConfiguration.yml kubeadm join --config JoinConfiguration.yml
# changed_when: true changed_when: true
# - name: Install Helm Diff. - name: Install Helm Diff.
# gather_facts: false gather_facts: false
# hosts: control hosts: control
# tasks: tasks:
# - name: Install it. - name: Install it.
# kubernetes.core.helm_plugin: kubernetes.core.helm_plugin:
# plugin_path: https://github.com/databus23/helm-diff plugin_path: https://github.com/databus23/helm-diff
# state: present state: present
- name: Install CNI. - name: Install CNI.
gather_facts: false gather_facts: false
hosts: control hosts: control
tasks: tasks:
- name: Assign nodes as workers.
kubernetes.core.k8s:
state: patched
kind: Node
name: "{{ item }}"
definition:
metadata:
labels:
node-role.kubernetes.io/worker: worker
loop: [node-a, node-b]
- name: Create Flannel namespace. - name: Create Flannel namespace.
kubernetes.core.k8s: kubernetes.core.k8s:
state: present state: present
@@ -193,20 +179,67 @@
value: "true" value: "true"
effect: NoSchedule effect: NoSchedule
- name: Install `nginx` Controller. - name: Install Hetzner Cloud Controller.
gather_facts: false gather_facts: false
hosts: control hosts: control
vars_files: vars_files:
- ../vault.yml - ../vault.yml
- ../secrets/tf_outputs.yml - ../secrets/tf_outputs.yml
tasks: tasks:
- name: Remove schedule taint to nodes. - name: Create `hcloud` secret.
kubernetes.core.k8s:
name: hcloud
namespace: kube-system
kind: Secret
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: hcloud
namespace: kube-system
type: Opaque
data:
token: "{{ secrets.hcloud_token | b64encode }}"
network: "{{ private_network_id.value | b64encode }}"
- name: Add Cloud Controller repository.
kubernetes.core.helm_repository:
name: hcloud
url: https://charts.hetzner.cloud
state: present
- name: Copy over values file.
vars:
values_template: ../templates/HCCMValues.yml.jinja2
ansible.builtin.template:
src: "{{ values_template }}"
dest: HCCMValues.yml
mode: preserve
- name: Install it.
kubernetes.core.helm:
name: hccm
chart_ref: hcloud/hcloud-cloud-controller-manager
namespace: kube-system
state: present
update_repo_cache: true
force: true
values_files: [HCCMValues.yml]
- name: Install `nginx` Controller.
gather_facts: false
hosts: control
tasks:
# This makes sure Hetzer gives the nodes a proper Provider ID.
- name: Add schedule taint to nodes.
kubernetes.core.k8s_taint: kubernetes.core.k8s_taint:
state: absent state: present
name: "{{ item }}" name: "{{ item }}"
taints: taints:
- key: node.cloudprovider.kubernetes.io/uninitialized - key: node.cloudprovider.kubernetes.io/uninitialized
value: true value: "true"
effect: NoSchedule effect: NoSchedule
loop: [node-a, node-b] loop: [node-a, node-b]
@@ -219,9 +252,6 @@
- name: Copy over values file. - name: Copy over values file.
vars: vars:
values_template: ../templates/IngressValues.yml.jinja2 values_template: ../templates/IngressValues.yml.jinja2
load_balancer_name: "{{ variables.load_balancer_name }}"
network_zone: "{{ variables.network_zone }}"
certificate_name: "test"
ansible.builtin.template: ansible.builtin.template:
src: "{{ values_template }}" src: "{{ values_template }}"
dest: IngressValues.yml dest: IngressValues.yml
@@ -235,3 +265,28 @@
state: present state: present
update_repo_cache: true update_repo_cache: true
values_files: [IngressValues.yml] values_files: [IngressValues.yml]
- name: Connect DNS to the Load Balancer.
gather_facts: false
vars_files:
- ../vault.yml
hosts: localhost
tasks:
- name: Get the IP address of the LB.
hetzner.hcloud.load_balancer_info:
api_token: "{{ secrets.hcloud_token }}"
name: "hetzner-lb"
register: hetzner_lb_info
- name: Connect DNS.
vars:
ip_address: "{{ hetzner_lb_info.hcloud_load_balancer_info[0].ipv4_address }}"
hetzner.hcloud.zone_rrset:
api_token: "{{ secrets.hcloud_token }}"
zone: "{{ secrets.zone_name }}"
name: "{{ item }}"
type: A
records:
- value: "{{ ip_address }}"
state: present
loop: ["*", "@"]

View File

@@ -0,0 +1,51 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-app
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: hashicorp/http-echo
args: ["-text=Hello from Kubernetes!"]
ports:
- containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 5678
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: hello.maximhutz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-service
port:
number: 80

View File

@@ -4,13 +4,14 @@ controller:
kind: DaemonSet kind: DaemonSet
service: service:
annotations: annotations:
load-balancer.hetzner.cloud/name: {{ load_balancer_name }} load-balancer.hetzner.cloud/name: "hetzner-lb"
load-balancer.hetzner.cloud/location: "fsn1"
load-balancer.hetzner.cloud/type: "lb11" load-balancer.hetzner.cloud/type: "lb11"
load-balancer.hetzner.cloud/ipv6-disabled: "true" load-balancer.hetzner.cloud/ipv6-disabled: "true"
load-balancer.hetzner.cloud/use-private-ip: "true" load-balancer.hetzner.cloud/use-private-ip: "true"
load-balancer.hetzner.cloud/protocol: "https" load-balancer.hetzner.cloud/protocol: "https"
load-balancer.hetzner.cloud/network-zone: {{ network_zone }} load-balancer.hetzner.cloud/network-zone: "eu-central"
load-balancer.hetzner.cloud/http-certificates: {{ certificate_name }} load-balancer.hetzner.cloud/http-certificates: "Main Certificate"
load-balancer.hetzner.cloud/http-redirect-http: "true" load-balancer.hetzner.cloud/http-redirect-http: "true"
enableHttp: false enableHttp: false
targetPorts: targetPorts:

View File

@@ -51,18 +51,3 @@ resource "hcloud_server" "server" {
depends_on = [hcloud_network_subnet.subnet] depends_on = [hcloud_network_subnet.subnet]
} }
resource "hcloud_load_balancer" "lb" {
name = "lb-hetzner"
load_balancer_type = "lb11"
network_zone = "eu-central"
}
resource "hcloud_load_balancer_target" "load_balancer_target" {
for_each = hcloud_server.server
type = "server"
load_balancer_id = hcloud_load_balancer.lb.id
use_private_ip = true
server_id = each.value.id
}

View File

@@ -20,9 +20,8 @@ resource "hcloud_network_route" "gateway" {
gateway = local.nat-private-ip gateway = local.nat-private-ip
} }
// Attach the load blaancer to the private network. // A managed certificate for the domain, to be used by the load balancer.
resource "hcloud_load_balancer_network" "attachment" { resource "hcloud_managed_certificate" "managed_cert" {
load_balancer_id = hcloud_load_balancer.lb.id name = "Main Certificate"
subnet_id = hcloud_network_subnet.subnet.id domain_names = ["*.${local.domain}", "${local.domain}"]
ip = local.lb-private-ip
} }

View File

@@ -1,32 +0,0 @@
data "hcloud_zone" "zone" {
name = local.domain
}
// Attach the load balancer to the domain.
resource "hcloud_zone_rrset" "records" {
for_each = toset(["@", "*"])
zone = data.hcloud_zone.zone.name
name = each.value
type = "A"
ttl = 60
records = [{ value = hcloud_load_balancer.lb.ipv4 }]
change_protection = false
}
// A managed certificate for the domain, to be used by the load balancer.
resource "hcloud_managed_certificate" "main" {
name = local.certificate_name
domain_names = ["*.${local.domain}", "${local.domain}"]
}
resource "hcloud_load_balancer_service" "load_balancer_service" {
load_balancer_id = hcloud_load_balancer.lb.id
protocol = "https"
http {
sticky_sessions = true
certificates = [hcloud_managed_certificate.main.id]
redirect_http = true
}
}

View File

@@ -11,8 +11,6 @@ locals {
} }
domain = "maximhutz.com" domain = "maximhutz.com"
certificate_name = "Main Certificate"
} }
variable "public_key_file" { variable "public_key_file" {

View File

@@ -1,40 +1,38 @@
$ANSIBLE_VAULT;1.1;AES256 $ANSIBLE_VAULT;1.1;AES256
62656134326239313562396138346634316530303635353562616163323836666132616565366336 33653833333639353437316264356533353739383838306564656334653238636239633366393665
3035353733653832316165356663303264396439393232390a666465306363356335383734616438 6239383662613562396562326238663733633962303365630a663235633337613137636563353932
37313833663535356163616666343933303363383462353064633538333433373431663161626230 31626533303532613566356638346663643337393839653536613866386339666537643435396133
3962303165346162360a626536313165643466343965633431343833653163656266396535656232 3030373532333061300a323835623262626531663666386130333034316237626635306536303764
35653139613737336431323733616533363531616131613965663534343938396661336331376633 33343338333234383937363064616364623864353338653366623333356336333939656433373663
62306130323131626435303262326261376630616433613363363536663638306261643734363661 65653433386435313634653037623462636333303535373432306463353833663466653236363630
66366631393034653536343163313862623733316465376533313030393761363033376536643861 32646664656634643331616331396230346263363838373133383737396461623434333837633832
63313735343033656332333838343532343236623435303135383033306131313930316137613634 34653333373364396438376130383265373534383936373131646437333535306631386662626439
37386339313530353534343162613733333935333136656134623862323861653739353636366363 31336634343466333732343639353239656632356632663333663464303965656563343737376630
38656565643437663330353366636331316337626438323162393838346534393063386338326336 61343337353561386139653739313562643738306535636264623430396365323531373133663063
64373030336466376432386334653737313461626264396431613330393938316230623235663962 37393330313461613865313633363136383663643331356461633662626232613937393761393561
62323431626261386238363163646662336134373534376632653431396532626438613830396164 32663437393332643334356330386536663965303634646261393137316662653637623064353263
34663434656131336265353336633930666230323131633130373833396230313634646134353464 66366234303664373861343766363563303539646239366637303666323062616339653234306438
30373537623939316565393966376439336465623330353037303536306632646361643437306139 32636237346563646365393636383964663336653833323566316135373463383933623037653563
36393232623236613737336263396138376336396335316465663661613635636232383435666230 32626263323136333330393735363839333062303963316364626363323330363735356266653834
65333361656337653135363239346264613530626231636635303466326331323832383337626534 61303339373965653964383331646664353130363131303664313433303131386138646236663966
64306630306531393461356535336136323833643735353232343336623830656563616663353933 63353463393538303939383566396264623739633331383834323334626635636231353930313838
34656562626238343030383833326333323463306634616333303531633832326532316664383837 32346134643830613365613337613331386533356633386630616562653264326531383632646463
65343463323837376630323663323961636631376535313538646462626130653431306563323137 66373465393336643538373935373738376665656561316162386238393433613465643132373032
35616335333265306366376532353861643935663764326334313035323432343361306639643633 36393665643166336265643730383833343064306432626233356639303832326463613164643361
62643932303161326634656463633166643062363262303665633261303730353438633834326432 65633336613130643330633062373865303763616132643337333261643438323662626261313237
63386439653266333561336432653737316538333330613662356535363162633635663039646430 30663963633364353933623134363062643565393766323465643130613234646535386462333763
31363866396265613639333266636532373438366430663632633061663736366366623061313765 31653737613334653231356634316532626530376530363562363439373831663065623830663739
37313932613339643731616263656636303439633637623935333136353866303361396230393632 37613165626630323262306335306136376639623238633635613430373065303766386131616235
37316566303932336361653335353632353161353864616361326665393065363736363430666464 62323739363564386138336330373930333462306231323238613363623233326164623130383338
62656333393632313664653837393335353662363965313238633131313631373534313336613831 31303733663165346532323839353364643833646633333562303932373161613263323930363963
33313762316330653835616637323134656536626661343833373336363430633836663831643563 34623732613465386563376433656330663039346162643130333866333431313733646263623234
39303364656638306661616537623538663230326639643533306538353435626336383435633836 36646662646630666436633862643161393561646236333437323231646134376338633664356664
35656633313436623733666464346337343664393236336535616135333032363034666333316233 39346434636262636663313232633733396464383666633730363438356638636533633937396236
65363537633630356662353034613935366330643361393631353561643062376239343363646462 39623739383239636365353364373438383334363636613864663932303863326266373666623130
38633335356234396334313265393235636337663365646533343234323634646166623038343266 39643833323637393837363365343363396534326461656433646562323765356339346437383262
32646432653731383366616333633862643531303633613136386331383365376633343935666563 31353636316530393935666266383834376236316633643630306462666531396263643261643833
33363035356365626263646132353631653336383939646538393336393463626632663661663962 64326530316636376230306234336233373361333933613233643937306333306361323938636264
63656238353463356665633964316135646264333262633862643234313035386230666661643733 66306565316663623862616331656532626165363732646137366338653561626261653763356166
65396534636365356130356463393634646136373362343334636138633531383135333637323635 63356336623761613731306464636438346462623535306431646666623061376565353132303765
35366131353261643661373366653838373238343732633430653862613134386565303765326166 64353265396137663264626264333439376538373634636533383338376134306561636339623437
32386465336231666564653361653235646231623065643738613939353439323430656236613633 6466
63333034303863633036613662313238383430373365353637323062363363303461333766373164
393133613238363662663335626561393630