Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for dynamic DNS #4

Merged
merged 1 commit into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/work/**
!/work/**/
!/work/cpouta
!/work/ddclient.template.conf
!/work/supernetes-cluster.yaml
!/work/manifests/flux.yaml
!/work/manifests/kustomization.yaml
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
92 changes: 82 additions & 10 deletions work/cpouta
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
13 changes: 13 additions & 0 deletions work/ddclient.template.conf
Original file line number Diff line number Diff line change
@@ -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 <email>, <password> and <host>
# 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=<email>
password=<password>
<host>.dy.fi