#!/usr/bin/env bash # deepflow/pull_save_scp_image_multi_arch.sh # 远端:multi-arch pull(优先 --all-platforms,回退逐平台) # -> image convert (--oci --all-platforms) 到临时本地引用 # -> save -o /tmp/-.multi.tar (docker-archive) # -> scp 回本地 set -euo pipefail REMOTE_HOST="${REMOTE_HOST:-root@10.1.3.179}" DEST_DIR="${DEST_DIR:-$HOME/Desktop}" REMOTE_TMPDIR="${REMOTE_TMPDIR:-/tmp}" RM_REMOTE="${RM_REMOTE:-0}" REMOTE_NERDCTL="${REMOTE_NERDCTL:-nerdctl}" REMOTE_NERDCTL_NS="${REMOTE_NERDCTL_NS:-}" # 例如 "k8s.io" REMOTE_NC="${REMOTE_NERDCTL} ${REMOTE_NERDCTL_NS:+-n ${REMOTE_NERDCTL_NS}}" PLATFORMS_DEFAULT="linux/amd64,linux/arm64" PLATFORMS="${PLATFORMS:-$PLATFORMS_DEFAULT}" usage() { cat < [image2 ...] [--rm-remote] $0 -f images.txt [--rm-remote] 流程(远端): 1) ${REMOTE_NC} pull --all-platforms # 不支持则逐平台 --platform 2) ${REMOTE_NC} image convert --oci --all-platforms 3) ${REMOTE_NC} save -o ${REMOTE_TMPDIR}/-.multi.tar 4) scp 回本地 ${DEST_DIR} 环境变量: REMOTE_HOST, DEST_DIR, REMOTE_TMPDIR, REMOTE_NERDCTL, REMOTE_NERDCTL_NS, PLATFORMS, RM_REMOTE EOF } # ---------- 参数解析 ---------- IMAGES=() LIST_FILE="" if [[ $# -eq 0 ]]; then usage; exit 1; fi ARGS=() while [[ $# -gt 0 ]]; do case "$1" in -h|--help) usage; exit 0 ;; --rm-remote) RM_REMOTE=1; shift ;; -f) [[ $# -ge 2 ]] || { echo "❌ 缺少镜像清单文件"; exit 1; } LIST_FILE="$2"; shift 2 ;; *) ARGS+=("$1"); shift ;; esac done if [[ -n "$LIST_FILE" ]]; then [[ -f "$LIST_FILE" ]] || { echo "❌ 文件不存在: $LIST_FILE"; exit 1; } while IFS= read -r line; do line="${line%%#*}"; line="$(echo -n "$line" | xargs || true)" [[ -n "$line" ]] || continue IMAGES+=("$line") done < "$LIST_FILE" fi if [[ ${#ARGS[@]} -gt 0 ]]; then IMAGES+=("${ARGS[@]}"); fi [[ ${#IMAGES[@]} -gt 0 ]] || { echo "❌ 没有可处理的镜像"; exit 1; } echo "🖥️ 远端: $REMOTE_HOST" echo "📂 本地目录: $DEST_DIR" echo "🧭 命名空间: ${REMOTE_NERDCTL_NS:-}" echo "🧹 rm-remote: $([[ $RM_REMOTE -eq 1 ]] && echo ON || echo OFF)" echo "🧩 回退平台: $PLATFORMS" mkdir -p "$DEST_DIR" # ---------- 预检查 ---------- ssh -o BatchMode=yes "$REMOTE_HOST" "command -v ${REMOTE_NERDCTL} >/dev/null" \ || { echo "❌ 远端未安装 ${REMOTE_NERDCTL}"; exit 1; } ssh -o BatchMode=yes "$REMOTE_HOST" "test -d ${REMOTE_TMPDIR}" \ || { echo "❌ 远端临时目录不存在: ${REMOTE_TMPDIR}"; exit 1; } REMOTE_SUPPORTS_ALL_PLATFORMS=0 if ssh -o BatchMode=yes "$REMOTE_HOST" "${REMOTE_NC} pull --help 2>/dev/null | grep -q -- '--all-platforms'"; then REMOTE_SUPPORTS_ALL_PLATFORMS=1 fi # ---------- 工具函数 ---------- rand_suffix() { LC_ALL=C tr -dc a-z0-9 /dev/null 2>&1 || true fi' RETURN # 1) 拉取多架构 if [[ $REMOTE_SUPPORTS_ALL_PLATFORMS -eq 1 ]]; then ssh -o BatchMode=yes "$REMOTE_HOST" \ "set -euo pipefail; ${REMOTE_NC} pull --all-platforms $Q_IMAGE" else echo "ℹ️ 远端不支持 --all-platforms,逐平台拉取: $PLATFORMS" IFS=, read -r -a arr <<< "$PLATFORMS" for p in "${arr[@]}"; do local QP; QP=$(printf %q "$p") ssh -o BatchMode=yes "$REMOTE_HOST" \ "set -euo pipefail; ${REMOTE_NC} pull --platform=$QP $Q_IMAGE" done fi # 2) 转为 OCI(到临时本地引用),确保包含所有平台 ssh -o BatchMode=yes "$REMOTE_HOST" \ "set -euo pipefail; ${REMOTE_NC} image convert --oci --all-platforms $Q_IMAGE $Q_TARGET" # 3) 保存为 docker-archive TAR ssh -o BatchMode=yes "$REMOTE_HOST" \ "set -euo pipefail; ${REMOTE_NC} save -o $Q_TAR $Q_TARGET" # 4) 回传 scp -q "$REMOTE_HOST:$REMOTE_TAR" "$DEST_PATH" # 5) 清理 if [[ $RM_REMOTE -eq 1 ]]; then ssh -o BatchMode=yes "$REMOTE_HOST" "rm -f $Q_TAR" fi ssh -o BatchMode=yes "$REMOTE_HOST" "${REMOTE_NC} rmi -f $Q_TARGET" >/dev/null 2>&1 || true CLEAN_ON_FAILURE=0 trap - RETURN echo "✅ OK: $DEST_PATH (docker-archive, multi-arch)" echo " 加载:nerdctl load -i \"$DEST_PATH\"" echo " 基本校验:tar tf \"$DEST_PATH\" | egrep 'manifest.json|repositories' | sed -n '1,5p'" echo " 平台确认(加载后):nerdctl image inspect \"$TARGET_REF\" --mode=native | jq '.[0].Manifest.Manifests[].Platform'" } for img in "${IMAGES[@]}"; do process_image "$img" done echo echo "🎉 全部 multi-arch 导出完成。"