Skip to content
Open
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
4 changes: 4 additions & 0 deletions update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/utils/utils.sh" "$@"

# Keep each host's superseded-image prune timer installed / up to date. This is
# the path every node setup funnels through, so existing nodes pick it up too.
setup_image_prune

if [ "$REVERSE_PROXY" = true ]; then
kubectl --kubeconfig=$KUBECONFIG delete certificate 5stack-ssl -n 5stack 2>/dev/null
fi
Expand Down
68 changes: 68 additions & 0 deletions utils/5stack-image-prune.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/bash
#
# 5stack image prune
#
# Reclaims disk from superseded 5stack container images. Every 5stack image is
# deployed as ghcr.io/5stackgg/*:latest, so when a node pulls a new build the
# :latest tag moves to the new digest and the previous version is left behind
# untagged (a "dangling" image whose overlayfs snapshot keeps filling
# /var/lib/rancher/k3s/agent). This removes those superseded versions.
#
# The image that currently holds a :latest tag is always kept - including
# game-server / game-streamer, which usually are NOT running when this fires
# but must stay ready so a match start does not wait on a re-pull. Because we
# key off the tag (not "is it running"), a plain `crictl rmi --prune` is not
# used: that would delete the idle-but-current game-server / game-streamer
# images too.
#
# Installed and scheduled by setup_image_prune (utils/setup_image_prune.sh).
# Safe to run by hand.

set -o pipefail

# k3s ships crictl; prefer it on PATH, fall back to `k3s crictl`.
if command -v crictl >/dev/null 2>&1; then
CRICTL=(crictl)
elif command -v k3s >/dev/null 2>&1; then
CRICTL=(k3s crictl)
else
echo "[5stack] image-prune: crictl not found, nothing to do"
exit 0
fi

if ! command -v jq >/dev/null 2>&1; then
echo "[5stack] image-prune: jq not found, skipping"
exit 0
fi

# Superseded = a 5stack image (matched by tag or digest) that no longer carries
# a :latest tag. Pinned images (e.g. the pause sandbox) are never touched.
mapfile -t STALE < <(
"${CRICTL[@]}" images -o json 2>/dev/null | jq -r '
.images[]
| select(.pinned != true)
| select([.repoTags[]?, .repoDigests[]?] | any(contains("ghcr.io/5stackgg/")))
| select((.repoTags // []) | any(endswith(":latest")) | not)
| .id
' | sort -u
)

if [ "${#STALE[@]}" -eq 0 ]; then
echo "[5stack] image-prune: no superseded 5stack images"
exit 0
fi

removed=0
for id in "${STALE[@]}"; do
[ -n "$id" ] || continue
if "${CRICTL[@]}" rmi "$id" >/dev/null 2>&1; then
echo "[5stack] image-prune: removed $id"
removed=$((removed + 1))
else
# Still referenced by a (terminating) container, or busy - leave it for the
# next run. Removing it would not stop a running container anyway.
echo "[5stack] image-prune: skipped $id (in use or busy)"
fi
done

echo "[5stack] image-prune: removed $removed superseded image(s)"
47 changes: 47 additions & 0 deletions utils/setup_image_prune.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash

# Installs the 5stack image-prune script and its systemd timer on the host.
# Idempotent: safe to re-run on every update. The schedule honors the
# IMAGE_PRUNE_ON_CALENDAR env var (a systemd OnCalendar= value, default
# "weekly") so it can be tuned per-deployment without code changes.
setup_image_prune() {
if [ "$EUID" -ne 0 ]; then
warn "skipping image prune timer setup (needs root)"
return 0
fi

step "Installing 5stack image prune timer"

local util_dir
util_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

install -m 0755 "$util_dir/5stack-image-prune.sh" /usr/local/bin/5stack-image-prune.sh

cat >/etc/systemd/system/5stack-image-prune.service <<'UNIT'
[Unit]
Description=5stack prune superseded container images

[Service]
Type=oneshot
ExecStart=/usr/local/bin/5stack-image-prune.sh
NoNewPrivileges=yes
UNIT

cat >/etc/systemd/system/5stack-image-prune.timer <<EOF
[Unit]
Description=Run 5stack image prune

[Timer]
OnCalendar=${IMAGE_PRUNE_ON_CALENDAR:-weekly}
Persistent=true
RandomizedDelaySec=30min
Unit=5stack-image-prune.service

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable --now 5stack-image-prune.timer >/dev/null 2>&1
ok "image prune timer enabled (OnCalendar=${IMAGE_PRUNE_ON_CALENDAR:-weekly})"
}
1 change: 1 addition & 0 deletions utils/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ source "$SCRIPT_DIR/checkout_repos.sh"
source "$SCRIPT_DIR/check_dev_dependencies.sh"
source "$SCRIPT_DIR/watch_ssl_status.sh"
source "$SCRIPT_DIR/setup_kustomize.sh"
source "$SCRIPT_DIR/setup_image_prune.sh"
source "$SCRIPT_DIR/tailscale-api.sh"
source "$SCRIPT_DIR/interactive_select.sh"

Expand Down