opensuse CT
This commit is contained in:
1 parent
cd56e73e36
commit
996884aefe
1 file changed
+234
Executable
+234
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# docker-opensuse-ct.sh — interactive (whiptail) installer that provisions an
|
||||
# openSUSE LXC container on Proxmox.
|
||||
#
|
||||
# Same UX pattern as opensuse-ct.sh: Default/Advanced menu, whiptail
|
||||
# prompts, then a scripted pct create + pct exec install flow.
|
||||
#
|
||||
# Run this ON the Proxmox host as root:
|
||||
# ./opensuse-ct.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
APP="opensuse"
|
||||
|
||||
# ---------- helpers ----------
|
||||
msg_info() { echo -e "\e[1;34m[*]\e[0m $1"; }
|
||||
msg_ok() { echo -e "\e[1;32m[OK]\e[0m $1"; }
|
||||
msg_err() { echo -e "\e[1;31m[ERR]\e[0m $1" >&2; }
|
||||
|
||||
die() { msg_err "$1"; exit 1; }
|
||||
|
||||
command -v pct >/dev/null 2>&1 || die "pct not found — run this on a Proxmox VE host."
|
||||
command -v whiptail >/dev/null 2>&1 || { msg_info "whiptail not found, installing..."; apt-get update -qq && apt-get install -y whiptail; }
|
||||
|
||||
# ---------- welcome + mode ----------
|
||||
whiptail --title "$APP LXC Installer" --msgbox \
|
||||
"This will create an openSUSE LXC container on this Proxmox host." \
|
||||
10 65
|
||||
|
||||
MODE=$(whiptail --title "$APP LXC Installer" --menu "Choose setup mode" 12 65 2 \
|
||||
"default" "Quick install — sane defaults, minimal prompts" \
|
||||
"advanced" "Advanced — customize every setting" \
|
||||
3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
# ---------- gather settings ----------
|
||||
NEXTID=$(pvesh get /cluster/nextid)
|
||||
|
||||
if [[ "$MODE" == "default" ]]; then
|
||||
CTID="$NEXTID"
|
||||
CT_HOSTNAME="opensuse"
|
||||
CT_STORAGE=$(pvesm status -content rootdir | awk 'NR==2{print $1}')
|
||||
[[ -n "$CT_STORAGE" ]] || die "No rootdir-capable storage found."
|
||||
DISK_GB=4
|
||||
CORES=2
|
||||
MEMORY=1024
|
||||
SWAP=512
|
||||
BRIDGE="vmbr0"
|
||||
VLAN=""
|
||||
NET_MODE="dhcp"
|
||||
UNPRIVILEGED=1
|
||||
ROOT_PASSWORD=$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20)
|
||||
SSH_PUBKEY_FILE=""
|
||||
else
|
||||
CTID=$(whiptail --inputbox "Container ID" 8 58 "$NEXTID" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
CT_HOSTNAME=$(whiptail --inputbox "Hostname" 8 58 "opensuse" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
mapfile -t STORAGES < <(pvesm status -content rootdir | awk 'NR>1{print $1}')
|
||||
[[ ${#STORAGES[@]} -gt 0 ]] || die "No rootdir-capable storage found."
|
||||
RADIO_ARGS=(); first=1
|
||||
for s in "${STORAGES[@]}"; do
|
||||
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$s" "" ON); first=0; else RADIO_ARGS+=("$s" "" OFF); fi
|
||||
done
|
||||
CT_STORAGE=$(whiptail --radiolist "Storage for rootfs" 15 60 6 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
DISK_GB=$(whiptail --inputbox "Disk size (GB)" 8 70 "16" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
CORES=$(whiptail --inputbox "CPU cores" 8 58 "2" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
MEMORY=$(whiptail --inputbox "Memory (MB)" 8 58 "2048" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
SWAP=$(whiptail --inputbox "Swap (MB)" 8 58 "512" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
mapfile -t BRIDGES < <(ip -o link show type bridge | awk -F': ' '{print $2}')
|
||||
[[ ${#BRIDGES[@]} -gt 0 ]] || die "No bridges found on this host."
|
||||
RADIO_ARGS=(); first=1
|
||||
for b in "${BRIDGES[@]}"; do
|
||||
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$b" "" ON); first=0; else RADIO_ARGS+=("$b" "" OFF); fi
|
||||
done
|
||||
BRIDGE=$(whiptail --radiolist "Network bridge" 15 60 6 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
VLAN=$(whiptail --inputbox "VLAN tag (blank = untagged)" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
NET_MODE=$(whiptail --menu "Network mode" 12 58 2 \
|
||||
"dhcp" "DHCP" \
|
||||
"static" "Static IP" \
|
||||
--title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
|
||||
if [[ "$NET_MODE" == "static" ]]; then
|
||||
STATIC_IP_CIDR=$(whiptail --inputbox "IP/CIDR, e.g. 192.168.10.60/24" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
STATIC_GW=$(whiptail --inputbox "Gateway" 8 58 "" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
fi
|
||||
|
||||
if whiptail --title "$APP LXC" --yesno \
|
||||
"Create as unprivileged container?" 12 70; then
|
||||
UNPRIVILEGED=1
|
||||
else
|
||||
UNPRIVILEGED=0
|
||||
fi
|
||||
|
||||
ROOT_PASSWORD=$(whiptail --passwordbox "Root password for the CT" 8 58 --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
SSH_PUBKEY_FILE=$(whiptail --inputbox "SSH public key file to inject (blank = skip)" 8 58 "$HOME" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
fi
|
||||
|
||||
pct status "$CTID" >/dev/null 2>&1 && die "CT $CTID already exists."
|
||||
|
||||
# ---------- template ----------
|
||||
mapfile -t TEMPLATES < <(pveam available -section system | awk '{print $2}' | grep -i opensuse || true)
|
||||
if [[ ${#TEMPLATES[@]} -eq 0 ]]; then
|
||||
msg_info "Refreshing template index..."
|
||||
pveam update
|
||||
mapfile -t TEMPLATES < <(pveam available -section system | awk '{print $2}' | grep -i opensuse || true)
|
||||
fi
|
||||
[[ ${#TEMPLATES[@]} -gt 0 ]] || die "No openSUSE templates found in the Proxmox appliance list."
|
||||
|
||||
if [[ "$MODE" == "default" ]]; then
|
||||
TEMPLATE="${TEMPLATES[-1]}" # newest listed
|
||||
else
|
||||
RADIO_ARGS=(); first=1
|
||||
for t in "${TEMPLATES[@]}"; do
|
||||
if [[ $first -eq 1 ]]; then RADIO_ARGS+=("$t" "" ON); first=0; else RADIO_ARGS+=("$t" "" OFF); fi
|
||||
done
|
||||
TEMPLATE=$(whiptail --radiolist "Choose openSUSE template" 20 72 10 "${RADIO_ARGS[@]}" --title "$APP LXC" 3>&1 1>&2 2>&3) || exit 1
|
||||
fi
|
||||
|
||||
if ! pveam list local | grep -q "$TEMPLATE"; then
|
||||
msg_info "Downloading template $TEMPLATE..."
|
||||
pveam download local "$TEMPLATE"
|
||||
msg_ok "Template downloaded."
|
||||
fi
|
||||
|
||||
# ---------- confirm ----------
|
||||
SUMMARY="CTID: $CTID
|
||||
Hostname: $CT_HOSTNAME
|
||||
Template: $TEMPLATE
|
||||
Storage: $CT_STORAGE (${DISK_GB}G)
|
||||
CPU/RAM: ${CORES} cores / ${MEMORY}MB (+${SWAP}MB swap)
|
||||
Bridge: $BRIDGE${VLAN:+ (vlan $VLAN)}
|
||||
Network: ${NET_MODE}${STATIC_IP_CIDR:+ $STATIC_IP_CIDR via $STATIC_GW}
|
||||
Priv: $([[ $UNPRIVILEGED -eq 1 ]] && echo unprivileged || echo privileged)
|
||||
Features: nesting=1,keyctl=1"
|
||||
|
||||
whiptail --title "Confirm" --yesno "$SUMMARY
|
||||
|
||||
Proceed?" 20 65 || exit 1
|
||||
|
||||
# ---------- build net string ----------
|
||||
if [[ "$NET_MODE" == "static" ]]; then
|
||||
NET_STR="name=eth0,bridge=${BRIDGE},ip=${STATIC_IP_CIDR},gw=${STATIC_GW}"
|
||||
else
|
||||
NET_STR="name=eth0,bridge=${BRIDGE},ip=dhcp"
|
||||
fi
|
||||
[[ -n "$VLAN" ]] && NET_STR="${NET_STR},tag=${VLAN}"
|
||||
|
||||
# ---------- create + start ----------
|
||||
CREATE_ARGS=(
|
||||
"$CTID" "local:vztmpl/${TEMPLATE}"
|
||||
--hostname "$CT_HOSTNAME"
|
||||
--cores "$CORES"
|
||||
--memory "$MEMORY"
|
||||
--swap "$SWAP"
|
||||
--rootfs "${CT_STORAGE}:${DISK_GB}"
|
||||
--net0 "$NET_STR"
|
||||
--unprivileged "$UNPRIVILEGED"
|
||||
--onboot 1
|
||||
--features "nesting=1,keyctl=1"
|
||||
--password "$ROOT_PASSWORD"
|
||||
)
|
||||
[[ -n "${SSH_PUBKEY_FILE:-}" && -f "${SSH_PUBKEY_FILE:-}" ]] && CREATE_ARGS+=(--ssh-public-keys "$SSH_PUBKEY_FILE")
|
||||
|
||||
msg_info "Creating CT $CTID ($CT_HOSTNAME)..."
|
||||
pct create "${CREATE_ARGS[@]}" >/dev/null
|
||||
msg_ok "Container created."
|
||||
|
||||
msg_info "Starting CT $CTID..."
|
||||
pct start "$CTID"
|
||||
|
||||
msg_info "Waiting for network..."
|
||||
for i in {1..30}; do
|
||||
pct exec "$CTID" -- sh -c "true" >/dev/null 2>&1 && pct exec "$CTID" -- ping -c1 -W2 1.1.1.1 >/dev/null 2>&1 && break
|
||||
sleep 2
|
||||
[[ $i -eq 30 ]] && die "CT never came up with network access."
|
||||
done
|
||||
msg_ok "Network is up."
|
||||
|
||||
# ---------- install docker inside the CT ----------
|
||||
run() { pct exec "$CTID" -- bash -c "$1"; }
|
||||
|
||||
msg_info "Refreshing zypper repos..."
|
||||
run "zypper --non-interactive refresh" >/dev/null
|
||||
msg_ok "Repos refreshed."
|
||||
|
||||
msg_info "Installing prerequisites (curl)..."
|
||||
run "zypper --non-interactive install curl" >/dev/null
|
||||
msg_ok "Prerequisites installed."
|
||||
|
||||
msg_info "updating os..."
|
||||
run "zypper --non-interactive up" >/dev/null
|
||||
msg_ok "OS upgraded."
|
||||
|
||||
msg_info "Install nano and ssh utils..."
|
||||
run "zypper in openssh-clients nano" >/dev/null
|
||||
msg_ok "Tools installed."
|
||||
|
||||
|
||||
# ---------- summary ----------
|
||||
CT_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')
|
||||
|
||||
whiptail --title "$APP LXC Installer — Done" --msgbox \
|
||||
"LXC is running.
|
||||
|
||||
CT ID: $CTID
|
||||
IP: ${CT_IP}
|
||||
Root pass: ${ROOT_PASSWORD}
|
||||
|
||||
Enter with: pct enter $CTID
|
||||
(also printed below in the terminal)" 18 65
|
||||
|
||||
cat <<EOF
|
||||
|
||||
========================================
|
||||
$APP LXC ready
|
||||
========================================
|
||||
CT ID: $CTID
|
||||
IP: ${CT_IP}
|
||||
$DOCKER_VERSION
|
||||
$COMPOSE_VERSION_INSTALLED
|
||||
Root pass: ${ROOT_PASSWORD}
|
||||
|
||||
Enter with: pct enter $CTID
|
||||
|
||||
NOTE: if 'docker run hello-world' failed above, this LXC/kernel combo
|
||||
doesn't support unprivileged Docker cleanly — re-run and choose
|
||||
privileged mode in Advanced settings, or set storage-driver=vfs in
|
||||
/etc/docker/daemon.json as a slower fallback.
|
||||
========================================
|
||||
EOF
|
||||
Reference in new issue
Block a user