diff --git a/bootstrap/host/README.md b/bootstrap/host/README.md index f4bd99c..faf4d34 100644 --- a/bootstrap/host/README.md +++ b/bootstrap/host/README.md @@ -60,3 +60,19 @@ host, run: ./jeannie ollama-setup ./jeannie ai-check ``` + +Installer downloads prefer IPv4 by default because some networks have broken or +slow IPv6 paths to `ollama.com`. Override with +`LAB_OLLAMA_INSTALL_CURL_IP_VERSION=` if you want curl's automatic behavior. + +If the Debian host cannot reach `ollama.com`, download the matching Linux +tarball from another machine, copy it to Debian, and point `jeannie` at it: + +```bash +curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o /tmp/ollama-linux-amd64.tgz +scp /tmp/ollama-linux-amd64.tgz jv@192.168.100.73:/tmp/ +LAB_OLLAMA_TARBALL_FILE=/tmp/ollama-linux-amd64.tgz ./jeannie ollama-setup +``` + +The same fallback is available to the Ansible bootstrap with +`debian_pc_ollama_tarball_file`. diff --git a/bootstrap/host/group_vars/debian_pc.yml b/bootstrap/host/group_vars/debian_pc.yml index 48ee531..704f64d 100644 --- a/bootstrap/host/group_vars/debian_pc.yml +++ b/bootstrap/host/group_vars/debian_pc.yml @@ -5,6 +5,10 @@ debian_pc_kubernetes_minor_version: v1.36 debian_pc_enable_passwordless_sudo: true debian_pc_install_ollama: true debian_pc_ollama_install_url: https://ollama.com/install.sh +debian_pc_ollama_install_curl_ip_version: "-4" +debian_pc_ollama_install_connect_timeout: 15 +debian_pc_ollama_install_max_time: 120 +debian_pc_ollama_tarball_file: "" debian_pc_ollama_host: 0.0.0.0:11434 debian_pc_ollama_models_dir: /data/ollama/models debian_pc_ollama_default_model: qwen2.5:0.5b diff --git a/bootstrap/host/playbook.yml b/bootstrap/host/playbook.yml index 5355688..ac276be 100644 --- a/bootstrap/host/playbook.yml +++ b/bootstrap/host/playbook.yml @@ -237,18 +237,62 @@ failed_when: false when: debian_pc_install_ollama | bool + - name: Install Ollama from local Linux tarball when provided + ansible.builtin.shell: | + set -euo pipefail + tar -C /usr -xzf "{{ debian_pc_ollama_tarball_file }}" + if ! id ollama >/dev/null 2>&1; then + useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama + fi + if getent group render >/dev/null 2>&1; then + usermod -aG render ollama + fi + if getent group video >/dev/null 2>&1; then + usermod -aG video ollama + fi + cat >/etc/systemd/system/ollama.service <<'EOF' + [Unit] + Description=Ollama Service + After=network-online.target + + [Service] + ExecStart=/usr/bin/ollama serve + User=ollama + Group=ollama + Restart=always + RestartSec=3 + Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + + [Install] + WantedBy=default.target + EOF + args: + executable: /bin/bash + when: + - debian_pc_install_ollama | bool + - debian_pc_ollama_installed.rc | default(1) != 0 + - debian_pc_ollama_tarball_file | length > 0 + - name: Install Ollama from official Linux installer ansible.builtin.shell: | set -euo pipefail tmp_installer="$(mktemp)" trap 'rm -f "${tmp_installer}"' EXIT - curl -fsSL "{{ debian_pc_ollama_install_url }}" -o "${tmp_installer}" + curl -fsSL \ + {% if debian_pc_ollama_install_curl_ip_version | length > 0 %} + "{{ debian_pc_ollama_install_curl_ip_version }}" \ + {% endif %} + --connect-timeout "{{ debian_pc_ollama_install_connect_timeout }}" \ + --max-time "{{ debian_pc_ollama_install_max_time }}" \ + "{{ debian_pc_ollama_install_url }}" \ + -o "${tmp_installer}" sh "${tmp_installer}" args: executable: /bin/bash when: - debian_pc_install_ollama | bool - debian_pc_ollama_installed.rc | default(1) != 0 + - debian_pc_ollama_tarball_file | length == 0 - name: Ensure Ollama model storage exists on data disk ansible.builtin.file: diff --git a/jeannie b/jeannie index 93fb389..28cb384 100755 --- a/jeannie +++ b/jeannie @@ -3420,12 +3420,74 @@ website_ollama_listen() { echo "Ollama API is reachable on the Debian host." } +ollama_linux_arch() { + case "$(uname -m)" in + x86_64 | amd64) + printf 'amd64\n' + ;; + aarch64 | arm64) + printf 'arm64\n' + ;; + *) + echo "Unsupported Ollama Linux architecture: $(uname -m)" >&2 + return 1 + ;; + esac +} + +ollama_write_systemd_service() { + sudo tee /etc/systemd/system/ollama.service >/dev/null <<'EOF' +[Unit] +Description=Ollama Service +After=network-online.target + +[Service] +ExecStart=/usr/bin/ollama serve +User=ollama +Group=ollama +Restart=always +RestartSec=3 +Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +[Install] +WantedBy=default.target +EOF +} + +ollama_install_from_tgz() { + local tarball="$1" + + if [[ ! -r "${tarball}" ]]; then + echo "Ollama tarball is not readable: ${tarball}" >&2 + exit 1 + fi + + echo "Installing Ollama from local tarball ${tarball}..." + sudo tar -C /usr -xzf "${tarball}" + if ! id ollama >/dev/null 2>&1; then + sudo useradd -r -s /bin/false -U -m -d /usr/share/ollama ollama + fi + if getent group render >/dev/null 2>&1; then + sudo usermod -aG render ollama + fi + if getent group video >/dev/null 2>&1; then + sudo usermod -aG video ollama + fi + ollama_write_systemd_service +} + ollama_setup() { local bind_address="${LAB_OLLAMA_BIND_ADDRESS:-0.0.0.0:11434}" local models_dir="${LAB_OLLAMA_MODELS_DIR:-/data/ollama/models}" local model="${LAB_AI_GATEWAY_MODEL:-qwen2.5:0.5b}" local installer_url="${LAB_OLLAMA_INSTALL_URL:-https://ollama.com/install.sh}" + local connect_timeout="${LAB_OLLAMA_INSTALL_CONNECT_TIMEOUT:-15}" + local max_time="${LAB_OLLAMA_INSTALL_MAX_TIME:-120}" + local curl_ip_version="${LAB_OLLAMA_INSTALL_CURL_IP_VERSION:--4}" + local local_tarball="${LAB_OLLAMA_TARBALL_FILE:-}" + local arch local tmp_installer + local curl_ip_args=() require_debian_server "ollama-setup" @@ -3433,13 +3495,37 @@ ollama_setup() { echo "curl is required to install Ollama." >&2 exit 1 fi + if [[ -n "${curl_ip_version}" ]]; then + curl_ip_args+=("${curl_ip_version}") + fi if ! command -v ollama >/dev/null 2>&1; then - echo "Installing Ollama from ${installer_url}..." - tmp_installer="$(mktemp)" - curl -fsSL "${installer_url}" -o "${tmp_installer}" - sudo sh "${tmp_installer}" - rm -f "${tmp_installer}" + if [[ -n "${local_tarball}" ]]; then + ollama_install_from_tgz "${local_tarball}" + else + echo "Installing Ollama from ${installer_url}..." + tmp_installer="$(mktemp)" + if ! curl "${curl_ip_args[@]}" -fsSL --connect-timeout "${connect_timeout}" --max-time "${max_time}" "${installer_url}" -o "${tmp_installer}"; then + rm -f "${tmp_installer}" + arch="$(ollama_linux_arch || true)" + cat >&2 <