Compare commits
2 Commits
349ffd006e
...
04d5b259c0
| Author | SHA1 | Date |
|---|---|---|
|
|
04d5b259c0 | |
|
|
06941a8454 |
|
|
@ -685,8 +685,8 @@ Deploy or refresh them from the Debian server with:
|
|||
```
|
||||
|
||||
`./jeannie up` also runs this step unless
|
||||
`LAB_RPI_SERVICES_DEPLOY=false` is set. The bootstrap prefers Docker data under
|
||||
`/nvme-storage/docker` only when that path is an active writable mount point. If
|
||||
`LAB_RPI_SERVICES_DEPLOY=false` is set. Docker data for the Raspberry Pi is
|
||||
expected under `/nvme-storage/docker` when that path is an active writable mount point. If
|
||||
the NVMe is missing or resets, it rewrites Docker's `data-root` to
|
||||
`/var/lib/docker` and restarts Docker so DNS can still come up from the
|
||||
SD/root filesystem.
|
||||
|
|
@ -706,7 +706,8 @@ Uptime Kuma monitor seeds live in
|
|||
Kuma user exists.
|
||||
`./jeannie status` and `./jeannie doctor-rpi` test Pi-hole DNS,
|
||||
direct Unbound DNS, public fallback resolver readiness, Uptime Kuma HTTP, and
|
||||
whether Docker is currently using the NVMe path or `/var/lib/docker`.
|
||||
whether Docker is currently using `/nvme-storage/docker` or has fallen back to
|
||||
`/var/lib/docker`.
|
||||
|
||||
## Adding Nodes
|
||||
|
||||
|
|
@ -744,7 +745,7 @@ Worker NIC MAC addresses default to the VMID-derived deterministic format used
|
|||
by the original working Pimox worker path. Set `LAB_PIMOX_WORKER_MAC_MODE=auto`
|
||||
only when you explicitly want Pimox to generate the MAC.
|
||||
Worker creation fails faster than template creation: static guest-network
|
||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=300`,
|
||||
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
||||
and SSH after an IP appears waits up to `LAB_PIMOX_GUEST_SSH_TIMEOUT_SECONDS=600`.
|
||||
Static guest-network validation skips the extra LAN ICMP probe by default
|
||||
|
|
|
|||
|
|
@ -685,8 +685,8 @@ Deploy or refresh them from the Debian server with:
|
|||
```
|
||||
|
||||
`./{{ main_script }} up` also runs this step unless
|
||||
`LAB_RPI_SERVICES_DEPLOY=false` is set. The bootstrap prefers Docker data under
|
||||
`/nvme-storage/docker` only when that path is an active writable mount point. If
|
||||
`LAB_RPI_SERVICES_DEPLOY=false` is set. Docker data for the Raspberry Pi is
|
||||
expected under `/nvme-storage/docker` when that path is an active writable mount point. If
|
||||
the NVMe is missing or resets, it rewrites Docker's `data-root` to
|
||||
`/var/lib/docker` and restarts Docker so DNS can still come up from the
|
||||
SD/root filesystem.
|
||||
|
|
@ -706,7 +706,8 @@ Uptime Kuma monitor seeds live in
|
|||
Kuma user exists.
|
||||
`./{{ main_script }} status` and `./{{ main_script }} doctor-rpi` test Pi-hole DNS,
|
||||
direct Unbound DNS, public fallback resolver readiness, Uptime Kuma HTTP, and
|
||||
whether Docker is currently using the NVMe path or `/var/lib/docker`.
|
||||
whether Docker is currently using `/nvme-storage/docker` or has fallen back to
|
||||
`/var/lib/docker`.
|
||||
|
||||
## Adding Nodes
|
||||
|
||||
|
|
@ -744,7 +745,7 @@ Worker NIC MAC addresses default to the VMID-derived deterministic format used
|
|||
by the original working Pimox worker path. Set `LAB_PIMOX_WORKER_MAC_MODE=auto`
|
||||
only when you explicitly want Pimox to generate the MAC.
|
||||
Worker creation fails faster than template creation: static guest-network
|
||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||
configuration waits up to `LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=300`,
|
||||
worker IP/SSH readiness waits up to `LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`,
|
||||
and SSH after an IP appears waits up to `LAB_PIMOX_GUEST_SSH_TIMEOUT_SECONDS=600`.
|
||||
Static guest-network validation skips the extra LAN ICMP probe by default
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ Worker NIC MAC addresses default to the VMID-derived deterministic format used
|
|||
by the original working Pimox worker path. Set `LAB_PIMOX_WORKER_MAC_MODE=auto`
|
||||
only when Pimox-generated MAC addresses are preferred.
|
||||
Worker clone failures are intentionally faster than template build failures:
|
||||
`LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=120`,
|
||||
`LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=300`,
|
||||
`LAB_PIMOX_WORKER_WAIT_TIMEOUT_SECONDS=600`, and
|
||||
`LAB_PIMOX_GUEST_SSH_TIMEOUT_SECONDS=600` by default. The broader
|
||||
`LAB_PIMOX_GUEST_TIMEOUT_SECONDS` still controls template preparation.
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ hosts:
|
|||
user: jv
|
||||
lan_ip: 192.168.100.89
|
||||
tailscale_ip: 100.77.80.72
|
||||
docker_root: /nvme-storage/docker
|
||||
docker_nvme_root: /nvme-storage/docker
|
||||
docker_fallback_root: /var/lib/docker
|
||||
opi5_pimox:
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ For Pi-hole v6, the stack sets `FTLCONF_dns_interface=ALL` and
|
|||
`PIHOLE_DNS_LISTENING_MODE` so LAN clients such as the Debian control host are
|
||||
not rejected as non-local networks.
|
||||
|
||||
The bootstrap script prefers Docker data under `/nvme-storage/docker` only when
|
||||
Docker data for the Raspberry Pi is expected under `/nvme-storage/docker` when
|
||||
that path is an active mount point and writable. If the NVMe is missing or
|
||||
reset, it rewrites Docker's `data-root` to `/var/lib/docker` and restarts
|
||||
Docker, allowing the services to come up from the SD/root filesystem instead.
|
||||
reset, the bootstrap rewrites Docker's `data-root` to `/var/lib/docker` and
|
||||
restarts Docker, allowing the services to come up from the SD/root filesystem
|
||||
instead.
|
||||
|
||||
Service config lives under `/opt/homelab-rpi-services/data` by default. Backup
|
||||
storage is intentionally not placed on the NVMe until the disk has proven
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
INSTALL_DIR="${LAB_RPI_SERVICES_INSTALL_DIR:-/opt/homelab-rpi-services}"
|
||||
DATA_DIR="${LAB_RPI_SERVICES_DATA_DIR:-${INSTALL_DIR}/data}"
|
||||
DOCKER_NVME_ROOT="${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}"
|
||||
DOCKER_NVME_ROOT="${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}"
|
||||
DOCKER_FALLBACK_ROOT="${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}"
|
||||
STOP_LEGACY_PIHOLE="${LAB_RPI_STOP_LEGACY_PIHOLE:-true}"
|
||||
PIHOLE_WEBPASSWORD="${PIHOLE_WEBPASSWORD:-}"
|
||||
|
|
|
|||
170
jeannie
170
jeannie
|
|
@ -52,6 +52,7 @@ mapping = {
|
|||
"hosts.rpi4.user": "LAB_RPI_USER",
|
||||
"hosts.rpi4.lan_ip": "LAB_RPI_HOST",
|
||||
"hosts.rpi4.tailscale_ip": "LAB_RPI_TAILSCALE_IP",
|
||||
"hosts.rpi4.docker_root": "LAB_RPI_DOCKER_ROOT",
|
||||
"hosts.rpi4.docker_nvme_root": "LAB_RPI_DOCKER_NVME_ROOT",
|
||||
"hosts.rpi4.docker_fallback_root": "LAB_RPI_DOCKER_FALLBACK_ROOT",
|
||||
"hosts.opi5_pimox.user": "LAB_PIMOX_USER",
|
||||
|
|
@ -1076,7 +1077,40 @@ sudo '${qm_bin}' status '${vmid}'
|
|||
echo 'Pimox VM ${vmid} config summary:'
|
||||
sudo '${qm_bin}' config '${vmid}' | grep -E '^(agent|boot|net0|scsi0|virtio0|sata0|ide0|ide2|efidisk0):' || true
|
||||
echo 'Pimox VM ${vmid} guest-agent network-get-interfaces:'
|
||||
sudo '${qm_bin}' guest cmd '${vmid}' network-get-interfaces" >&2 || true
|
||||
sudo '${qm_bin}' guest cmd '${vmid}' network-get-interfaces || true
|
||||
echo 'Pimox VM ${vmid} guest SSH diagnostics:'
|
||||
sudo '${qm_bin}' guest exec '${vmid}' -- bash -lc 'set +e
|
||||
echo "-- units --"
|
||||
systemctl is-active ssh sshd 2>/dev/null || true
|
||||
echo "-- listeners --"
|
||||
ss -ltnp 2>/dev/null | grep :22 || ss -ltn 2>/dev/null | grep :22 || true
|
||||
echo "-- sshd config test --"
|
||||
sudo sshd -t 2>&1 || true
|
||||
echo "-- recent ssh logs --"
|
||||
journalctl -u ssh -u sshd --no-pager -n 40 2>/dev/null || true
|
||||
' || true" >&2 || true
|
||||
}
|
||||
|
||||
pimox_worker_guest_agent_recovery_hint() {
|
||||
local host="$1"
|
||||
local user="$2"
|
||||
local vmid="$3"
|
||||
local qm_bin="$4"
|
||||
|
||||
cat >&2 <<EOF
|
||||
|
||||
QEMU guest agent did not become available for Pimox VM ${vmid}.
|
||||
Fast checks:
|
||||
ssh ${user}@${host} 'sudo ${qm_bin} status ${vmid}; sudo ${qm_bin} config ${vmid}; sudo ${qm_bin} guest cmd ${vmid} network-get-interfaces'
|
||||
|
||||
Recovery options:
|
||||
1. If the clone is still booting slowly, rerun the Pimox stage with a longer agent wait:
|
||||
LAB_RPI_SERVICES_DEPLOY=false LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS=600 ./jeannie up
|
||||
2. If the same VM still reports "QEMU guest agent is not running", recreate the worker clone:
|
||||
LAB_RPI_SERVICES_DEPLOY=false LAB_PIMOX_WORKER_REPLACE_EXISTING=true ./jeannie up
|
||||
3. If a recreated worker still has no guest agent, rebuild the template and worker:
|
||||
LAB_RPI_SERVICES_DEPLOY=false LAB_PIMOX_TEMPLATE_REPLACE_EXISTING=true LAB_PIMOX_WORKER_REPLACE_EXISTING=true ./jeannie up
|
||||
EOF
|
||||
}
|
||||
|
||||
pimox_guest_exec_exitcode() {
|
||||
|
|
@ -1099,6 +1133,73 @@ except Exception:
|
|||
PY
|
||||
}
|
||||
|
||||
pimox_worker_guest_ssh_repair() {
|
||||
local host="$1"
|
||||
local user="$2"
|
||||
local key_path="$3"
|
||||
local vmid="$4"
|
||||
local qm_bin="$5"
|
||||
local script
|
||||
local encoded_script
|
||||
local repair_output
|
||||
local repair_exitcode
|
||||
|
||||
script="$(cat <<'EOF'
|
||||
set -eu
|
||||
ssh_unit=""
|
||||
if systemctl list-unit-files ssh.service 2>/dev/null | grep -q '^ssh[.]service'; then
|
||||
ssh_unit=ssh.service
|
||||
elif systemctl list-unit-files sshd.service 2>/dev/null | grep -q '^sshd[.]service'; then
|
||||
ssh_unit=sshd.service
|
||||
else
|
||||
echo "Neither ssh.service nor sshd.service exists in the guest" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo ssh-keygen -A
|
||||
sudo install -d -m 0755 /run/sshd
|
||||
sudo mkdir -p /etc/ssh/sshd_config.d
|
||||
sudo tee /etc/ssh/sshd_config.d/99-homelab-worker-listen.conf >/dev/null <<'SSHD_CONFIG'
|
||||
Port 22
|
||||
ListenAddress 0.0.0.0
|
||||
PubkeyAuthentication yes
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PermitRootLogin no
|
||||
UsePAM yes
|
||||
SSHD_CONFIG
|
||||
|
||||
sudo sshd -t
|
||||
sudo systemctl unmask ssh.service ssh.socket sshd.service sshd.socket >/dev/null 2>&1 || true
|
||||
sudo systemctl disable --now ssh.socket sshd.socket >/dev/null 2>&1 || true
|
||||
sudo systemctl enable "$ssh_unit" >/dev/null
|
||||
sudo systemctl restart "$ssh_unit"
|
||||
sleep 2
|
||||
sudo systemctl is-active "$ssh_unit"
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -ltnp 2>/dev/null || ss -ltn
|
||||
ss -ltn | awk '$4 == "0.0.0.0:22" { found = 1 } END { exit found ? 0 : 1 }'
|
||||
fi
|
||||
EOF
|
||||
)"
|
||||
encoded_script="$(printf '%s' "${script}" | base64 | tr -d '\n')"
|
||||
if ! repair_output="$(pimox_ssh "${host}" "${user}" "${key_path}" "sudo '${qm_bin}' guest exec '${vmid}' -- bash -lc \"printf '%s' '${encoded_script}' | base64 -d | sudo bash\"" 2>&1)"; then
|
||||
echo "Could not run guest SSH repair through qemu-guest-agent for VM ${vmid}." >&2
|
||||
printf '%s\n' "${repair_output}" | sed 's/^/ guest-ssh-repair: /' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "${repair_output}" | sed 's/^/ guest-ssh-repair: /' >&2
|
||||
if repair_exitcode="$(pimox_guest_exec_exitcode "${repair_output}")" && [[ "${repair_exitcode}" == "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo "Guest SSH repair failed inside Pimox VM ${vmid}." >&2
|
||||
if [[ -n "${repair_exitcode:-}" ]]; then
|
||||
echo "Guest exit code: ${repair_exitcode}" >&2
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_pimox_guest_ssh() {
|
||||
local host="$1"
|
||||
local user="$2"
|
||||
|
|
@ -1124,6 +1225,9 @@ wait_for_pimox_guest_ssh() {
|
|||
local ssh_deadline=0
|
||||
local ssh_output
|
||||
local ssh_timeout_seconds="${LAB_PIMOX_GUEST_SSH_TIMEOUT_SECONDS:-600}"
|
||||
local ssh_refused_since=0
|
||||
local ssh_refused_timeout_seconds="${LAB_PIMOX_GUEST_SSH_REFUSED_TIMEOUT_SECONDS:-120}"
|
||||
local ssh_repair_attempted=false
|
||||
|
||||
ip_filter_description="matching prefix ${ip_prefix}"
|
||||
if [[ -z "${ip_prefix}" ]]; then
|
||||
|
|
@ -1133,6 +1237,10 @@ wait_for_pimox_guest_ssh() {
|
|||
echo "LAB_PIMOX_GUEST_SSH_TIMEOUT_SECONDS must be a positive integer." >&2
|
||||
return 1
|
||||
fi
|
||||
if ! [[ "${ssh_refused_timeout_seconds}" =~ ^[0-9]+$ ]] || ((ssh_refused_timeout_seconds == 0)); then
|
||||
echo "LAB_PIMOX_GUEST_SSH_REFUSED_TIMEOUT_SECONDS must be a positive integer." >&2
|
||||
return 1
|
||||
fi
|
||||
if ! truthy "${require_host_probe}" && ! disabled_value "${require_host_probe}"; then
|
||||
echo "LAB_PIMOX_WORKER_REQUIRE_HOST_PROBE must be true or false." >&2
|
||||
return 1
|
||||
|
|
@ -1181,6 +1289,23 @@ wait_for_pimox_guest_ssh() {
|
|||
return 0
|
||||
fi
|
||||
last_ssh_output="${ssh_output}"
|
||||
if [[ "${ssh_output}" == *"Connection refused"* ]]; then
|
||||
if ((ssh_refused_since == 0)); then
|
||||
ssh_refused_since="${SECONDS}"
|
||||
fi
|
||||
if [[ "${ssh_repair_attempted}" == "false" ]]; then
|
||||
echo "SSH on worker VM ${vmid} at ${guest_ip} is refusing connections; attempting qemu-guest-agent SSH repair..." >&2
|
||||
pimox_worker_guest_ssh_repair "${host}" "${user}" "${key_path}" "${vmid}" "${qm_bin}" || true
|
||||
ssh_repair_attempted=true
|
||||
elif ((SECONDS - ssh_refused_since >= ssh_refused_timeout_seconds)); then
|
||||
echo "Worker VM ${vmid} at ${guest_ip} is reachable but still refuses SSH after ${ssh_refused_timeout_seconds}s." >&2
|
||||
echo "This usually means sshd is missing, broken, or not listening on IPv4 inside the worker template." >&2
|
||||
pimox_worker_vm_debug "${host}" "${user}" "${key_path}" "${vmid}" "${qm_bin}"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
ssh_refused_since=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if ((SECONDS >= next_log)); then
|
||||
|
|
@ -1278,7 +1403,7 @@ configure_pimox_worker_guest_network() {
|
|||
local gateway="$7"
|
||||
local dns_servers="$8"
|
||||
local interface_name="$9"
|
||||
local timeout_seconds="${LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS:-120}"
|
||||
local timeout_seconds="${LAB_PIMOX_GUEST_AGENT_CONFIG_TIMEOUT_SECONDS:-300}"
|
||||
local deadline
|
||||
local script
|
||||
local encoded_script
|
||||
|
|
@ -1360,15 +1485,36 @@ sudo ip addr add ${worker_ip} dev ${interface_name}
|
|||
sudo ip route replace default via ${gateway} dev ${interface_name}
|
||||
printf '%s\n' ${dns_servers} | awk '{ for (i = 1; i <= NF; i++) print "nameserver " \$i }' | sudo tee /etc/resolv.conf >/dev/null
|
||||
sudo ifup --force ${interface_name} 2>/dev/null || true
|
||||
sudo systemctl restart ssh 2>/dev/null || true
|
||||
sudo ssh-keygen -A
|
||||
sudo install -d -m 0755 /run/sshd
|
||||
sudo mkdir -p /etc/ssh/sshd_config.d
|
||||
sudo tee /etc/ssh/sshd_config.d/99-homelab-worker-listen.conf >/dev/null <<'SSHD_CONFIG'
|
||||
Port 22
|
||||
ListenAddress 0.0.0.0
|
||||
PubkeyAuthentication yes
|
||||
PasswordAuthentication no
|
||||
KbdInteractiveAuthentication no
|
||||
PermitRootLogin no
|
||||
UsePAM yes
|
||||
SSHD_CONFIG
|
||||
sudo sshd -t
|
||||
sudo systemctl unmask ssh.service ssh.socket sshd.service sshd.socket >/dev/null 2>&1 || true
|
||||
sudo systemctl disable --now ssh.socket sshd.socket >/dev/null 2>&1 || true
|
||||
ssh_unit=ssh.service
|
||||
if ! systemctl list-unit-files ssh.service 2>/dev/null | grep -q '^ssh[.]service'; then
|
||||
ssh_unit=sshd.service
|
||||
fi
|
||||
sudo systemctl enable "\$ssh_unit" >/dev/null
|
||||
sudo systemctl restart "\$ssh_unit"
|
||||
ip -br addr show ${interface_name}
|
||||
ip route
|
||||
ip -4 addr show dev ${interface_name} | grep -q '${worker_address}/'
|
||||
${lan_probe_block}
|
||||
echo 'guest-check: ssh service'
|
||||
sudo systemctl is-active ssh
|
||||
sudo systemctl is-active "\$ssh_unit"
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -ltn | grep -Eq '(^|[[:space:]])0\.0\.0\.0:22[[:space:]]|(^|[[:space:]])\[::\]:22[[:space:]]|(^|[[:space:]])\*:22[[:space:]]'
|
||||
ss -ltnp 2>/dev/null || ss -ltn
|
||||
ss -ltn | awk '\$4 == "0.0.0.0:22" { found = 1 } END { exit found ? 0 : 1 }'
|
||||
fi
|
||||
EOF
|
||||
)"
|
||||
|
|
@ -1394,6 +1540,7 @@ EOF
|
|||
if [[ -n "${network_output:-}" ]]; then
|
||||
printf '%s\n' "${network_output}" | sed 's/^/ /' >&2
|
||||
fi
|
||||
pimox_worker_guest_agent_recovery_hint "${pimox_host}" "${pimox_user}" "${vmid}" "${qm_bin}"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
|
@ -1974,7 +2121,7 @@ check_rpi_docker_root_state() {
|
|||
local rpi_host="${LAB_RPI_HOST:-${LAB_RASPBERRY_HOST:-192.168.100.89}}"
|
||||
local rpi_user="${LAB_RPI_USER:-${LAB_RASPBERRY_USER:-jv}}"
|
||||
local rpi_key="${LAB_RPI_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}}"
|
||||
local nvme_root="${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}"
|
||||
local nvme_root="${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}"
|
||||
local fallback_root="${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}"
|
||||
|
||||
ssh -i "${rpi_key}" -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "${rpi_user}@${rpi_host}" "set -eu
|
||||
|
|
@ -3538,7 +3685,7 @@ deploy_rpi_services() {
|
|||
local rpi_key="${LAB_RPI_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}}"
|
||||
local install_dir="${LAB_RPI_SERVICES_INSTALL_DIR:-/opt/homelab-rpi-services}"
|
||||
local data_dir="${LAB_RPI_SERVICES_DATA_DIR:-${install_dir}/data}"
|
||||
local docker_nvme_root="${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}"
|
||||
local docker_nvme_root="${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}"
|
||||
local docker_fallback_root="${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}"
|
||||
local stop_legacy_pihole="${LAB_RPI_STOP_LEGACY_PIHOLE:-true}"
|
||||
local install_docker="${LAB_RPI_INSTALL_DOCKER:-false}"
|
||||
|
|
@ -3583,6 +3730,7 @@ deploy_rpi_services() {
|
|||
ssh -i "${rpi_key}" -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "${rpi_user}@${rpi_host}" "
|
||||
LAB_RPI_SERVICES_INSTALL_DIR='${install_dir}' \
|
||||
LAB_RPI_SERVICES_DATA_DIR='${data_dir}' \
|
||||
LAB_RPI_DOCKER_ROOT='${docker_nvme_root}' \
|
||||
LAB_RPI_DOCKER_NVME_ROOT='${docker_nvme_root}' \
|
||||
LAB_RPI_DOCKER_FALLBACK_ROOT='${docker_fallback_root}' \
|
||||
LAB_RPI_STOP_LEGACY_PIHOLE='${stop_legacy_pihole}' \
|
||||
|
|
@ -6024,7 +6172,7 @@ status_rpi_services() {
|
|||
local rpi_user="${LAB_RPI_USER:-${LAB_RASPBERRY_USER:-jv}}"
|
||||
local rpi_key="${LAB_RPI_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-/home/jv/.ssh/id_ed25519}}"
|
||||
local install_dir="${LAB_RPI_SERVICES_INSTALL_DIR:-/opt/homelab-rpi-services}"
|
||||
local docker_nvme_root="${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}"
|
||||
local docker_nvme_root="${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}"
|
||||
local docker_fallback_root="${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}"
|
||||
local pihole_container="${PIHOLE_CONTAINER_NAME:-homelab-pihole}"
|
||||
local unbound_container="${UNBOUND_CONTAINER_NAME:-homelab-unbound}"
|
||||
|
|
@ -6092,7 +6240,7 @@ if [ \"\$docker_root\" = '${docker_nvme_root}' ]; then
|
|||
printf '%-34s warn - configured for nvme but mountpoint is missing\n' 'Docker root class'
|
||||
fi
|
||||
elif [ \"\$docker_root\" = '${docker_fallback_root}' ]; then
|
||||
printf '%-34s ok - fallback root active\n' 'Docker root class'
|
||||
printf '%-34s warn - fallback root active; expected ${docker_nvme_root}\n' 'Docker root class'
|
||||
else
|
||||
printf '%-34s warn - unexpected docker root\n' 'Docker root class'
|
||||
fi
|
||||
|
|
@ -6404,7 +6552,7 @@ doctor_rpi() {
|
|||
cat <<'EOF'
|
||||
|
||||
Next steps:
|
||||
- If Docker root is /var/lib/docker, the NVMe mount is unavailable or failed the writable check.
|
||||
- Docker root should be /nvme-storage/docker; if it is /var/lib/docker, the NVMe mount is unavailable or failed the writable check.
|
||||
- If Pi-hole works but Unbound fails, DNS should still resolve through configured public fallbacks.
|
||||
- If Pi-hole DNS fails, clients using the RPi as DNS will be affected.
|
||||
- Reapply the stack with ./jeannie rpi-services.
|
||||
|
|
@ -6414,7 +6562,7 @@ EOF
|
|||
Doctor command: doctor-rpi
|
||||
RPi LAN host: ${LAB_RPI_HOST:-${LAB_RASPBERRY_HOST:-192.168.100.89}}
|
||||
RPi Tailscale IP: ${LAB_RPI_TAILSCALE_IP:-unknown}
|
||||
Docker NVMe root: ${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}
|
||||
Docker root: ${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}
|
||||
Docker fallback root: ${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}
|
||||
Services: Pi-hole, Unbound, Uptime Kuma
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -158,14 +158,15 @@ rpi_capacity() {
|
|||
section "RPi4"
|
||||
ssh -i "${rpi_key}" -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "${rpi_user}@${rpi_host}" '
|
||||
set +e
|
||||
docker_root="$(sudo docker info --format "{{.DockerRootDir}}" 2>/dev/null || true)"
|
||||
echo "-- memory"
|
||||
free -h
|
||||
echo
|
||||
echo "-- disk"
|
||||
df -h / /nvme-storage /var/lib/docker 2>/dev/null || df -h
|
||||
df -h / /nvme-storage "${docker_root:-/nvme-storage/docker}" 2>/dev/null || df -h
|
||||
echo
|
||||
echo "-- docker root"
|
||||
sudo docker info --format "{{.DockerRootDir}}" 2>/dev/null || true
|
||||
printf "%s\n" "${docker_root:-unknown}"
|
||||
echo
|
||||
echo "-- docker disk usage"
|
||||
sudo docker system df 2>/dev/null || true
|
||||
|
|
@ -345,6 +346,8 @@ compact_rpi_capacity() {
|
|||
local rpi_host="${LAB_RPI_HOST:-${LAB_RASPBERRY_HOST:-192.168.100.89}}"
|
||||
local rpi_user="${LAB_RPI_USER:-${LAB_RASPBERRY_USER:-jv}}"
|
||||
local rpi_key="${LAB_RPI_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-${HOME}/.ssh/id_ed25519}}"
|
||||
local expected_docker_root="${LAB_RPI_DOCKER_ROOT:-${LAB_RPI_DOCKER_NVME_ROOT:-/nvme-storage/docker}}"
|
||||
local fallback_docker_root="${LAB_RPI_DOCKER_FALLBACK_ROOT:-/var/lib/docker}"
|
||||
local output
|
||||
|
||||
output="$(ssh -i "${rpi_key}" -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "${rpi_user}@${rpi_host}" '
|
||||
|
|
@ -352,9 +355,11 @@ set +e
|
|||
docker_root="$(sudo docker info --format "{{.DockerRootDir}}" 2>/dev/null || true)"
|
||||
root_used="$(df -P / 2>/dev/null | awk "NR == 2 { print \$5 }")"
|
||||
nvme_used="$(df -P /nvme-storage 2>/dev/null | awk "NR == 2 { print \$5 }")"
|
||||
docker_root_used="$(df -P "${docker_root:-/nvme-storage/docker}" 2>/dev/null | awk "NR == 2 { print \$5 }")"
|
||||
echo "docker_root=${docker_root:-unknown}"
|
||||
echo "root_used=${root_used:-unknown}"
|
||||
echo "nvme_used=${nvme_used:-unknown}"
|
||||
echo "docker_root_used=${docker_root_used:-unknown}"
|
||||
' 2>/dev/null || true)"
|
||||
|
||||
if [[ -z "${output}" ]]; then
|
||||
|
|
@ -365,10 +370,18 @@ echo "nvme_used=${nvme_used:-unknown}"
|
|||
local docker_root
|
||||
local root_used
|
||||
local nvme_used
|
||||
local docker_root_used
|
||||
docker_root="$(printf '%s\n' "${output}" | awk -F= '/docker_root=/ { print $2 }')"
|
||||
root_used="$(printf '%s\n' "${output}" | awk -F= '/root_used=/ { print $2 }')"
|
||||
nvme_used="$(printf '%s\n' "${output}" | awk -F= '/nvme_used=/ { print $2 }')"
|
||||
emit ok "RPi4" "Docker root" "${docker_root:-unknown}"
|
||||
docker_root_used="$(printf '%s\n' "${output}" | awk -F= '/docker_root_used=/ { print $2 }')"
|
||||
if [[ "${docker_root}" == "${expected_docker_root}" ]]; then
|
||||
emit ok "RPi4" "Docker root" "${docker_root}" "used=${docker_root_used:-unknown}"
|
||||
elif [[ "${docker_root}" == "${fallback_docker_root}" ]]; then
|
||||
emit warn "RPi4" "Docker root" "${docker_root}" "expected ${expected_docker_root}" "./jeannie doctor-rpi"
|
||||
else
|
||||
emit warn "RPi4" "Docker root" "${docker_root:-unknown}" "expected ${expected_docker_root}" "./jeannie doctor-rpi"
|
||||
fi
|
||||
emit ok "RPi4" "Root disk" "${root_used:-unknown}"
|
||||
if [[ "${nvme_used:-unknown}" = "unknown" ]]; then
|
||||
emit warn "RPi4" "NVMe mount" "unknown" "df /nvme-storage failed" "./jeannie doctor-rpi"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ else:
|
|||
"hosts.debian.docker_root",
|
||||
"hosts.rpi4.lan_ip",
|
||||
"hosts.rpi4.tailscale_ip",
|
||||
"hosts.rpi4.docker_root",
|
||||
"hosts.opi5_pimox.lan_ip",
|
||||
"hosts.opi5_pimox.worker_storage",
|
||||
"hosts.oci_edge.public_ip",
|
||||
|
|
@ -155,6 +156,7 @@ required_paths = %w[
|
|||
hosts.debian.docker_root
|
||||
hosts.rpi4.lan_ip
|
||||
hosts.rpi4.tailscale_ip
|
||||
hosts.rpi4.docker_root
|
||||
hosts.opi5_pimox.lan_ip
|
||||
hosts.opi5_pimox.worker_storage
|
||||
hosts.oci_edge.public_ip
|
||||
|
|
|
|||
Loading…
Reference in New Issue