From 77861027f49519ef5dcb388bfacf28ade914bbca Mon Sep 17 00:00:00 2001 From: Dennis Marttinen Date: Thu, 22 Aug 2024 18:56:07 +0300 Subject: [PATCH] feat: add support for dynamic DNS cPouta does not provide a DNS service for the dynamically changing public IPs. As such, pull in `linuxserver/ddclient` in the cluster to perform dynamic DNS (DDNS) updates if configured by the user. Should support all DDNS providers supported by `ddclient`, configured to use [dy.fi](https://www.dy.fi/) by default. Signed-off-by: Dennis Marttinen --- .gitignore | 1 + README.md | 2 + work/cpouta | 92 +++++++++++++++++++++++++++++++++---- work/ddclient.template.conf | 13 ++++++ 4 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 work/ddclient.template.conf diff --git a/.gitignore b/.gitignore index 0727668..675b105 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /work/** !/work/**/ !/work/cpouta +!/work/ddclient.template.conf !/work/supernetes-cluster.yaml !/work/manifests/flux.yaml !/work/manifests/kustomization.yaml diff --git a/README.md b/README.md index cf3aaf6..3be7440 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Start by logging into cPouta and selecting the right project from the top left. Then, click on your username from the top right and select `OpenStack RC File`. This will give you a file named `project_1234567-openrc.sh` which will be used by the scripts for API access. Save it into the `work` directory, which is used as the working directory of the container. +Next, since cPouta only hands out ephemeral IPv4 addresses, we need to set up [Dynamic DNS (DDNS)](https://en.wikipedia.org/wiki/Dynamic_DNS) for the cluster. This can be done using external services, such as [dy.fi](https://www.dy.fi/) (Finland-only), or any other service supported by [ddclient](https://ddclient.net/). A configuration template is provided in [`ddclient.template.conf`](work/ddclient.template.conf). Copying this file to `ddclient.conf` and filling it out will set up automatic dynamic DNS reconciliation in the cluster during bringup. + Finally, inside the [container](#usage), run ```shell diff --git a/work/cpouta b/work/cpouta index 80ebe62..583516b 100755 --- a/work/cpouta +++ b/work/cpouta @@ -13,7 +13,9 @@ # - https://www.clouditlab.com/deploying-a-single-node-kubernetes-cluster-in-aws-using-talos/ _p() { (set -x; "$@"); } +alias kubectl="_p kubectl" alias openstack="_p openstack" +alias talosctl="_p talosctl" shopt -s expand_aliases set -o pipefail @@ -56,6 +58,19 @@ set -o pipefail _usage 1 fi + # Extract DDNS FQDN if configuration is present + if [ -f ddclient.conf ]; then + _fqdn=$(tail -1 ddclient.conf) + if [ -z "$_fqdn" ] || [[ "$_fqdn" == *=* ]]; then + cat <<- EOF + Error: invalid DDNS FQDN: ${_fqdn:-(none)} + The last line in ddclient.conf must be the FQDN + EOF + + exit 1 + fi + fi + _delete() { openstack "${@:3}" list -f json | jq --arg key "$1" --arg value "$2" -rc 'map(select(.[$key] == $value)) | .[].ID' | while read -r id; do openstack "${@:3}" delete "$id"; done } @@ -103,13 +118,18 @@ set -o pipefail _public_ip=$(openstack floating ip list --port "$_machine" -f json | jq -r '.[0] | ."Floating IP Address"') # Create baseline Talos configuration, override this with talos-bootstrap - talosctl gen config -f cpouta "https://$_private_ip:6443" --with-secrets secrets.yaml --config-patch @<(cat <<- EOF - machine: - type: init # Bootstrap automatically - cluster: - allowSchedulingOnControlPlanes: true # Single-node cluster - EOF - ) + talosctl gen config -f cpouta "https://$_private_ip:6443" \ + --with-secrets secrets.yaml \ + --config-patch @<(cat <<- EOF + machine: + type: init # Bootstrap automatically + cluster: + allowSchedulingOnControlPlanes: true # Single-node cluster + EOF + ) \ + ${_fqdn+--additional-sans "$_fqdn"} + + # Update generated talosconfig talosctl --talosconfig talosconfig config endpoint "$_public_ip" talosctl --talosconfig talosconfig config node "$_private_ip" talosctl config merge talosconfig @@ -122,9 +142,61 @@ set -o pipefail talosctl kubeconfig -fm sed -i "s/$_private_ip/$_public_ip/g" ~/.kube/config - # Print IPs for convenient access - echo "Private IP: $_private_ip" - echo "Public IP: $_public_ip" + if [ -n "$_fqdn" ]; then + # Deploy linuxserver/ddclient to perform periodic Dynamic DNS (DDNS) updates + kubectl create namespace ddns + kubectl -n ddns create secret generic ddns-config \ + --from-file=ddclient.conf=ddclient.conf + kubectl apply -f <(cat <<- EOF + apiVersion: apps/v1 + kind: Deployment + metadata: + name: ddns + namespace: ddns + spec: + selector: + matchLabels: + app: ddns + template: + metadata: + labels: + app: ddns + spec: + containers: + - name: ddclient + image: linuxserver/ddclient:3.11.2 + securityContext: + privileged: false + volumeMounts: + - mountPath: /defaults + name: ddns-config + volumes: + - name: ddns-config + secret: + secretName: ddns-config + EOF + ) + + # Use the FQDN instead of the public IP + talosctl --talosconfig talosconfig config endpoint "$_fqdn" + talosctl config merge talosconfig + + # Wait for the cluster to become healthy + while ! talosctl health; do sleep 1; done + talosctl kubeconfig -fm + sed -i "s/$_private_ip/$_fqdn/g" ~/.kube/config + fi + + # Print IPs for convenient access + printf "%-12s%s\n" "Private IP:" "$_private_ip" "Public IP:" "$_public_ip" + + # Print FQDN if configured + if [ -n "$_fqdn" ]; then + printf "%-12s%s\n" "FQDN:" "$_fqdn" + fi + + # Confirm readiness + echo "Cluster ready." exit } diff --git a/work/ddclient.template.conf b/work/ddclient.template.conf new file mode 100644 index 0000000..578c109 --- /dev/null +++ b/work/ddclient.template.conf @@ -0,0 +1,13 @@ +# Example ddclient configuration for dynamic DNS updates to dy.fi (Finnish-only DDNS service), +# Change the file name to "ddclient.conf" to enable. Edit the , and +# entries to match your setup, or change the configuration entirely to target your preferred +# DDNS service. The last line MUST match the DDNS FQDN. + +daemon=60 +use=web +web=checkip.dy.fi +server=www.dy.fi +protocol=dyndns2 +login= +password= +.dy.fi