Add fallback: scripts/save_droplet_image.sh
Browse filesInsurance for the case where the public-base Dockerfile diverges
from the bootstrap droplet's ABI in a way that breaks specialists.
Commits the running container as an image, saves to a compressed
tarball (zstd if available, gzip fallback), and scp's it
off-droplet to a backup dir.
Not run by default — tarball is ~15 GB compressed and the scp takes
the better part of an hour. Use only if the Dockerfile build can't
reproduce the working env (e.g. AMD changes the public ROCm release
in a way that drops MI300X kernels we depend on). For 99% of
redeploys, scripts/deploy_droplet.sh + the Dockerfile are the right
path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/save_droplet_image.sh
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Riprap droplet image fallback — save the bootstrap droplet's
|
| 3 |
+
# riprap-models image to a portable tarball.
|
| 4 |
+
#
|
| 5 |
+
# Use this when the public-base Dockerfile (services/riprap-models/Dockerfile)
|
| 6 |
+
# can't be reproduced — e.g. when AMD's public ROCm release diverges from
|
| 7 |
+
# the bootstrap droplet's ABI in a way that breaks our specialists. The
|
| 8 |
+
# saved tarball is byte-identical to the running container's image, so
|
| 9 |
+
# `docker load` on a fresh droplet gives you the exact env that worked.
|
| 10 |
+
#
|
| 11 |
+
# Caveats:
|
| 12 |
+
# - Tarballs are huge (~15 GB compressed, ~35 GB raw)
|
| 13 |
+
# - Needs the droplet to still be alive (run BEFORE you destroy it)
|
| 14 |
+
# - Can't be uploaded to a public registry without redacting any
|
| 15 |
+
# embedded auth tokens; recommend you scp it to backup storage
|
| 16 |
+
#
|
| 17 |
+
# Usage:
|
| 18 |
+
# scripts/save_droplet_image.sh <droplet-ip> [out-dir]
|
| 19 |
+
#
|
| 20 |
+
# Default out-dir: ~/riprap-backups/
|
| 21 |
+
set -euo pipefail
|
| 22 |
+
|
| 23 |
+
if [ "$#" -lt 1 ]; then
|
| 24 |
+
echo "Usage: $0 <droplet-ip> [out-dir]" >&2
|
| 25 |
+
exit 64
|
| 26 |
+
fi
|
| 27 |
+
|
| 28 |
+
DROPLET_IP="$1"
|
| 29 |
+
OUT_DIR="${2:-$HOME/riprap-backups}"
|
| 30 |
+
SSH_USER="${SSH_USER:-root}"
|
| 31 |
+
SSH_KEY_FLAG=""
|
| 32 |
+
if [ -n "${SSH_KEY:-}" ]; then SSH_KEY_FLAG="-i $SSH_KEY"; fi
|
| 33 |
+
SSH="ssh $SSH_KEY_FLAG -o StrictHostKeyChecking=accept-new ${SSH_USER}@${DROPLET_IP}"
|
| 34 |
+
SCP="scp $SSH_KEY_FLAG -o StrictHostKeyChecking=accept-new"
|
| 35 |
+
|
| 36 |
+
mkdir -p "$OUT_DIR"
|
| 37 |
+
STAMP=$(date -u +%Y%m%d-%H%M%S)
|
| 38 |
+
TAR="$OUT_DIR/riprap-droplet-base-${STAMP}.tar"
|
| 39 |
+
|
| 40 |
+
echo "==> 1. Commit running terramind container as image"
|
| 41 |
+
$SSH 'docker commit terramind riprap-droplet-base:latest'
|
| 42 |
+
|
| 43 |
+
echo "==> 2. Save image to droplet-local tarball"
|
| 44 |
+
$SSH "docker save riprap-droplet-base:latest -o /workspace/riprap-droplet-base.tar"
|
| 45 |
+
SIZE=$($SSH 'stat -c %s /workspace/riprap-droplet-base.tar')
|
| 46 |
+
echo " droplet-local tarball: $SIZE bytes"
|
| 47 |
+
|
| 48 |
+
echo "==> 3. Compress (zstd preferred; gzip fallback)"
|
| 49 |
+
if $SSH 'command -v zstd > /dev/null'; then
|
| 50 |
+
$SSH 'zstd -3 --rm /workspace/riprap-droplet-base.tar'
|
| 51 |
+
REMOTE="/workspace/riprap-droplet-base.tar.zst"
|
| 52 |
+
else
|
| 53 |
+
$SSH 'gzip /workspace/riprap-droplet-base.tar'
|
| 54 |
+
REMOTE="/workspace/riprap-droplet-base.tar.gz"
|
| 55 |
+
fi
|
| 56 |
+
CSIZE=$($SSH "stat -c %s $REMOTE")
|
| 57 |
+
echo " compressed: $CSIZE bytes ($(awk "BEGIN { printf \"%.1f\", $CSIZE/$SIZE*100 }")% of raw)"
|
| 58 |
+
|
| 59 |
+
echo "==> 4. scp to local: $TAR$(basename $REMOTE | sed 's|riprap-droplet-base.tar||')"
|
| 60 |
+
$SCP "${SSH_USER}@${DROPLET_IP}:${REMOTE}" "${TAR}$(echo $REMOTE | sed 's|.*\.tar||')"
|
| 61 |
+
|
| 62 |
+
echo "==> 5. Cleanup droplet tarball"
|
| 63 |
+
$SSH "rm -f $REMOTE"
|
| 64 |
+
|
| 65 |
+
ls -lh "${TAR}"*
|
| 66 |
+
echo
|
| 67 |
+
echo "Restore on a fresh droplet:"
|
| 68 |
+
echo " scp ${TAR}* root@<new-ip>:/workspace/"
|
| 69 |
+
echo " ssh root@<new-ip> 'zstd -d /workspace/riprap-droplet-base.tar.zst -o /tmp/img.tar && docker load -i /tmp/img.tar'"
|
| 70 |
+
echo " Then docker run with the original device flags (see CLAUDE.md)."
|