diff --git a/ansible-playbook.yml b/ansible-playbook.yml index b737fff..8d7ef4e 100644 --- a/ansible-playbook.yml +++ b/ansible-playbook.yml @@ -1,163 +1,88 @@ --- -# ------------------------------------------------------------ -# Ansible Playbook – Deploy Ollama + Open‑WebUI with Podman -# ------------------------------------------------------------ -- name: Deploy Ollama + Open‑WebUI on a remote host using Podman - hosts: target # <-- change to your inventory group / host - become: true # we need root to manage containers/volumes +- name: Deploy Ollama + Open WebUI + hosts: your_gpu_servers + become: yes + vars: - # ----------------------------------------------------------------- - # Port that the Open‑WebUI UI will listen on on the *host*. - # If you set the environment variable OPEN_WEBUI_PORT on the host - # before running the play, that value will be used, otherwise 3000. - # ----------------------------------------------------------------- + ollama_port: 11434 open_webui_port: "{{ lookup('env', 'OPEN_WEBUI_PORT') | default('3000') }}" - - # Secret key for the UI – you can also move this to a vault variable - webui_secret_key: "vk4HCy3FnlF0fqRT8MClJSGVmEdsP2tK" - - # ----------------------------------------------------------------- - # Container images – pinning to tags is recommended for production. - # ----------------------------------------------------------------- - ollama_image: "ollama/ollama:latest" - open_webui_image: "ghcr.io/open-webui/open-webui:main" - # npm_image: "jc21/nginx-proxy-manager:latest" # (commented out) + ollama_volume_name: ollama_ollama + open_webui_volume_name: ollama_open_webui + webui_secret_key: "vk4HCy3FnlF0fqRT8MClJSGVmEdsP2tK" # ← move to ansible-vault in production! tasks: - - # ----------------------------------------------------------------- - # 1️⃣ Install Podman (and the NVIDIA container toolkit if you need it) - # ----------------------------------------------------------------- - - name: Install podman (Debian/Ubuntu) - ansible.builtin.apt: - name: - - podman - - ca-certificates - - curl - - gnupg + - name: Create named volumes (optional but cleaner than host paths) + community.docker.docker_volume: + name: "{{ item }}" state: present - update_cache: true - when: ansible_facts.os_family == "Debian" + loop: + - "{{ ollama_volume_name }}" + - "{{ open_webui_volume_name }}" - - name: Install podman (RHEL/CentOS/Fedora) - ansible.builtin.dnf: - name: podman - state: present - when: ansible_facts.os_family == "RedHat" - - # ----------------------------------------------------------------- - # 2️⃣ Create the named Podman volumes - # ----------------------------------------------------------------- - - name: Create volume for Ollama data - containers.podman.podman_volume: + - name: Deploy Ollama container + community.docker.docker_container: name: ollama - state: present - - - name: Create volume for Open‑WebUI data - containers.podman.podman_volume: - name: open-webui - state: present - - # ----------------------------------------------------------------- - # 3️⃣ Run the Ollama container (GPU‑enabled) - # ----------------------------------------------------------------- - - name: Pull Ollama image - containers.podman.podman_image: - name: "{{ ollama_image }}" - pull: true - - - name: Start Ollama container - containers.podman.podman_container: - name: ollama - image: "{{ ollma_image }}" + image: ollama/ollama:latest state: started restart_policy: unless-stopped + pull: true tty: true - pull: true # same as `pull_policy: always` - ports: - - "11434:11434" + published_ports: + - "{{ ollama_port }}:11434" volumes: - - "ollama:/root/.ollama" - # GPU access – expose *all* NVIDIA devices (the same as the compose file) - devices: - - "/dev/nvidiactl:/dev/nvidiactl" - - "/dev/nvidia-uvm:/dev/nvidia-uvm" - - "/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools" - - "/dev/nvidia0:/dev/nvidia0" - - "/dev/nvidia1:/dev/nvidia1" - - "/dev/nvidia2:/dev/nvidia2" - - "/dev/nvidia3:/dev/nvidia3" - # If you only have a single GPU you can keep just /dev/nvidia0, - # or use a loop with `count: all` in the compose – here we expose - # the typical device nodes. Adjust if your system uses a different - # naming scheme. - env: - # Optional – explicit, matches the commented out env in the compose - NVIDIA_VISIBLE_DEVICES: "all" + - "{{ ollama_volume_name }}:/root/.ollama" + device_requests: + - driver: nvidia + count: -1 # -1 = all GPUs (or use device_ids: ['0', '1'] for specific) + capabilities: + - - gpu # nested list as per docs + comparisons: + image: strict + env: strict + volumes: strict + # container_default_behavior: no_defaults # optional, modern default + register: ollama_deploy - # ----------------------------------------------------------------- - # 4️⃣ Run the Open‑WebUI container (depends on Ollama) - # ----------------------------------------------------------------- - - name: Pull Open‑WebUI image - containers.podman.podman_image: - name: "{{ open_webui_image }}" - pull: true - - - name: Start Open‑WebUI container - containers.podman.podman_container: + - name: Deploy Open WebUI container + community.docker.docker_container: name: open-webui - image: "{{ open_webui_image }}" + image: ghcr.io/open-webui/open-webui:main state: started restart_policy: unless-stopped - ports: + pull: true + published_ports: - "{{ open_webui_port }}:8080" volumes: - - "open-webui:/app/backend/data" + - "{{ open_webui_volume_name }}:/app/backend/data" env: - OLLAMA_BASE_URL: "http://ollama:11434" + OLLAMA_BASE_URL: "http://ollama:11434" # container → container via name WEBUI_SECRET_KEY: "{{ webui_secret_key }}" extra_hosts: - - "host.docker.internal:host-gateway" - depends_on: - - ollama # ensures podman waits for ollama to be up - # No special GPU handling needed here – it talks to Ollama over the network + host.docker.internal: host-gateway + # networks: + # - name: bridge # default anyway + depends_on: [] # no real depends_on in docker_container → use wait or handler if critical + comparisons: + image: strict + env: strict + volumes: strict + when: ollama_deploy.changed or not ollama_deploy.container # optional: only if ollama exists/changed - # ----------------------------------------------------------------- - # 5️⃣ (Optional) Deploy the NPM / Nginx‑Proxy‑Manager service - # ----------------------------------------------------------------- - # Uncomment the block below if you ever need the commented‑out service - # from the original compose file. - # - # - name: Pull NPM image - # containers.podman.podman_image: - # name: "{{ npm_image }}" - # pull: true - # - # - name: Start NPM container - # containers.podman.podman_container: - # name: npm - # image: "{{ npm_image }}" - # state: started - # restart_policy: unless-stopped - # ports: - # - "80:80" - # - "81:81" - # - "443:443" - # volumes: - # - "./data:/data" - # - "./letsencrypt:/etc/letsencrypt" + # Optional: wait for Ollama API to become responsive before starting WebUI + - name: Wait for Ollama to be ready + ansible.builtin.uri: + url: "http://localhost:{{ ollama_port }}/api/tags" + method: GET + status_code: 200 + return_content: no + register: ollama_ready + until: ollama_ready.status == 200 + retries: 30 + delay: 5 + when: ollama_deploy.changed - # ----------------------------------------------------------------- - # 6️⃣ Print a short summary for the operator - # ----------------------------------------------------------------- - - name: Show deployment summary + - name: Debug deployment status ansible.builtin.debug: - msg: | - -------------------------------------------------------------- - ✅ Ollama → http://{{ ansible_host }}:11434 - ✅ Open‑WebUI → http://{{ ansible_host }}:{{ open_webui_port }} - -------------------------------------------------------------- - -# ------------------------------------------------------------ -# End of playbook -# ------------------------------------------------------------ + msg: + - "Ollama container: {{ ollama_deploy.container.State.Status | default('unknown') }}" + - "Open WebUI listening on port {{ open_webui_port }}" \ No newline at end of file