ollama chat webui kurulumu

This commit is contained in:
Sedat ÖZTÜRK 2026-09-17 15:24:51 +03:00
parent 9e2f928aac
commit 0bc6336715
9 changed files with 228 additions and 19 deletions

3
.gitignore vendored
View file

@ -12,3 +12,6 @@ logs/
configs/change-logs/change-logs.json configs/change-logs/change-logs.json
# Wizard import staging klasoru (gecici; oturum bitince silinir) # Wizard import staging klasoru (gecici; oturum bitince silinir)
configs/seeds/.imports/ configs/seeds/.imports/
# Deployment secret'lari (WEBUI_SECRET_KEY vb.) — sunucuda uretilir, depoya girmez
configs/deployment/.env

View file

@ -183,7 +183,7 @@ sozsoft-platform/
│ │ # host/{crud,custom,wizard,sql,postgres} │ │ # host/{crud,custom,wizard,sql,postgres}
│ │ # tenants/{tenantId}/{crud,custom,wizard,sql,postgres} │ │ # tenants/{tenantId}/{crud,custom,wizard,sql,postgres}
│ │ # {sql|postgres}/{object,execute} │ │ # {sql|postgres}/{object,execute}
│ └── ai/ # AI entegrasyonu (n8n akış tanımları) │ └── ai/ # AI entegrasyonu (n8n akış tanımları, Ollama model kurulum notları)
├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları) ├── .github/instructions/ # ai.instructions.md (bağlayıcı platform kuralları)
│ # lowcode.instructions.md (artefakt üretim referansı: şemalar, örnekler) │ # lowcode.instructions.md (artefakt üretim referansı: şemalar, örnekler)
│ # dotnet.instructions.md (api/ için bağlayıcı .NET/ABP kod standardı) │ # dotnet.instructions.md (api/ için bağlayıcı .NET/ABP kod standardı)
@ -1148,9 +1148,16 @@ kuyruğu arka plan işçisiyle çalıştırır ve sonuçları günlükler.
### 13.5. AI entegrasyonu ### 13.5. AI entegrasyonu
`AiBot` kayıtları tenant bazlıdır; n8n webhook üzerinden LangChain ajanı + bellek penceresi ve `AiBot` kayıtları tenant bazlıdır; n8n webhook üzerinden bir niyet sınıflandırma adımı ve
Gemini sohbet modeli bağlanır (`configs/ai/Chat.json`). UI tarafında `/admin/ai` ve header'daki ardından sohbet ya da SQL dalı çalışır (`configs/ai/Chat.json`). Model, DevOps makinesindeki
asistan bileşeni bu uç noktayı kullanır. Ollama'da yerel olarak koşar (`http://ollama:11434/api/chat`, varsayılan `qwen2.5:3b`) — dışarıya
model çağrısı yapılmaz. UI tarafında `/admin/ai` ve header'daki asistan bileşeni bu uç noktayı
kullanır.
Ollama'ya yüklü modelleri yönetmek için `configs/ai/Kurulum.txt`, sohbet arayüzü (Open WebUI) ve
alan adı yönlendirmesi için §20 kullanılır. Akıştaki model adı değiştirilirse `configs/ai/Chat.json`
içindeki üç varsayılanın (`intentModel`, `chatModel`, `sqlModel`) birlikte güncellenmesi gerekir;
biri kalırsa o dal "model not found" ile düşer.
### 13.6. MCP sunucusu ### 13.6. MCP sunucusu
@ -1254,6 +1261,30 @@ claude mcp add sozsoft-local --transport http https://localhost:44344/mcp \n --
Ters vekilde `/mcp` ayrı bir blokta karşılanır (`configs/deployment/configs/nginx.conf`): Ters vekilde `/mcp` ayrı bir blokta karşılanır (`configs/deployment/configs/nginx.conf`):
Streamable HTTP yanıtları SSE akışıdır, `location /` altındaki tamponlama akışı bekletir. Streamable HTTP yanıtları SSE akışıdır, `location /` altındaki tamponlama akışı bekletir.
**Şema tuzağı.** İstek vekilden HTTP olarak gelir. `issuer` ve `registration_endpoint`
yapılandırmadan sabitlenir (`AuthServer:Authority`), ama `authorization_endpoint` ve
`token_endpoint` adreslerini OpenIddict istekten türetir. Uygulama `X-Forwarded-Proto`
başlığını işlemezse bu iki adres keşif belgesine `http://` olarak yazılır ve **OAuth 2.1
istemcileri (MCP connector'ları) TLS'siz uç kabul etmediği için bağlantı kurulamaz** —
istemci tarafında görülen hata genellikle "please register the OAuth client" olur.
Çözüm iki parçalıdır ve ikisi birlikte olmak zorundadır:
- API konteynerinde `ASPNETCORE_FORWARDEDHEADERS_ENABLED=true` (`docker-compose-app.yml`)
- nginx'te **her** `location` bloğunun `X-Forwarded-Proto $scheme` göndermesi. `location /`
bunu `proxy_params` üzerinden yapar; `/mcp`, `/videoroomhub` ve `/messengerhub` kendi
blokları olduğu için başlığı ayrıca set eder. Bir blok atlanırsa istemcinin gönderdiği
başlık olduğu gibi geçer ve şema taklit edilebilir hale gelir.
Doğrulama:
```bash
curl -s https://api.sozsoft.com/.well-known/oauth-authorization-server \
| tr ',' '\n' | grep -E 'issuer|authorization_endpoint|token_endpoint|registration_endpoint'
```
Dördü de `https://` olmalıdır.
**Yetkiler.** `App.DeveloperKit.McpServers` (ekran), `.Invoke` (araç çağırma) ve `.Write` **Yetkiler.** `App.DeveloperKit.McpServers` (ekran), `.Invoke` (araç çağırma) ve `.Write`
(veri yazma). Son ikisi `MultiTenancySide = Both`, yani kiracı kullanıcılarına da verilebilir. (veri yazma). Son ikisi `MultiTenancySide = Both`, yani kiracı kullanıcılarına da verilebilir.
@ -1419,19 +1450,67 @@ grant_type=password
## 20. Dağıtım (Deployment) ## 20. Dağıtım (Deployment)
`configs/deployment` altında üç ortam için compose dosyaları ve numaralandırılmış scriptler bulunur. `configs/deployment` altında üç ortam için compose dosyaları ve numaralandırılmış scriptler bulunur.
Benzer adlı `configs/docker` ile karıştırılmamalı: `docker/` yalnızca **yerel geliştirmede** veri
katmanını (SQL/PostgreSQL/Redis) ayağa kaldırır, `deployment/` ise **sunucudaki** kurulumdur.
| Dosya | Amaç | | Dosya | Amaç |
| --- | --- | | --- | --- |
| `docker-compose-data.yml` | Redis, SQL Server / PostgreSQL, yedek volume'ları | | `docker-compose-data.yml` | Redis, SQL Server / PostgreSQL, yedek volume'ları |
| `docker-compose-app.yml` / `.dev.yml` / `.production.yml` | API, UI, migrator, nginx | | `docker-compose-app.yml` / `.dev.yml` / `.production.yml` | API, UI, migrator, nginx |
| `docker-compose-devops.yml` | Forgejo + runner (CI/CD) | | `docker-compose-devops.yml` | Forgejo (CI/CD), Rocket.Chat, n8n, Ollama, Open WebUI, coturn |
| `Dockerfile`, `api/*.Dockerfile`, `ui/Sozsoft.Platform.Ui.Dockerfile` | İmaj tanımları | | `Dockerfile`, `api/*.Dockerfile`, `ui/Sozsoft.Platform.Ui.Dockerfile` | İmaj tanımları |
| `configs/nginx.conf`, `redis.conf`, `turnserver.conf`, `elasticsearch.yml` | Servis yapılandırmaları |
Servis yapılandırma dosyaları tek klasörde, `configs/deployment/configs/` altındadır — compose
dosyaları buradan mount eder, ikinci bir kopya tutulmaz:
| Dosya | Kullanan servis |
| --- | --- |
| `nginx.conf` | Ters vekil: tüm `*.sozsoft.com` vhost'ları, SSL sonlandırma, `/mcp` bloğu |
| `redis.conf` / `redis.7.conf` | Redis (dağıtık cache, sürüme göre) |
| `turnserver.conf` | coturn — WebRTC TURN/STUN (bkz. §Messenger) |
| `elasticsearch.yml` | Elasticsearch (opsiyonel log/arama katmanı) |
### Alt alan adı → port eşlemesi
`nginx.conf` içindeki vhost'lar host üzerinde yayınlanan portlara bakar; yeni bir servis
eklerken portun boş olduğu buradan doğrulanır.
| Alan adı | Port | Servis |
| --- | --- | --- |
| `devops.sozsoft.com` | 3000 | Forgejo |
| `chat.sozsoft.com` | 3001 | Rocket.Chat |
| `sozsoft.com` + tenant alt alan adları | 3002 | UI (React) |
| `ollama.sozsoft.com` | 3003 | Open WebUI — sohbet arayüzü |
| `ollama.sozsoft.com/ollama-api/` | 11434 | Ollama ham API'si (basic auth) |
| `ai.sozsoft.com` | 5678 | n8n |
| `api.sozsoft.com` | 8080 | API (`/mcp`, `/videoroomhub`, `/messengerhub` dahil) |
| `cdn.sozsoft.com` | 8081 | CDN |
Open WebUI kendi backend'ini `/api/...` altında sunduğu için Ollama'nın ham API'si `/ollama-api/`
önekine alınmıştır; iki uç aynı önekte yaşayamaz.
Basic auth yalnızca `/ollama-api/` bloğundadır — Ollama'nın kendi kimlik doğrulaması yoktur.
Sohbet arayüzünün önüne **konmaz**: Open WebUI socket.io üzerinden WebSocket açar, tarayıcı WS
upgrade isteğine basic auth kimliğini eklemez ve bağlantı 401 döngüsüne girer. Arayüzün güvenlik
sınırı uygulamanın kendi girişidir; bu yüzden kurulumdan hemen sonra **yeni kayıt alımının
kapatılması bir tercih değil, kurulumun parçasıdır** (`Settings → Admin → General → Enable New
Sign Ups`). Daha sıkı bir sınır gerekiyorsa vhost'a `allow`/`deny` ile IP kısıtı eklenir; basic
auth bu arayüz için bir seçenek değildir.
Compose tarafında `DEFAULT_USER_ROLE=pending` ikinci bir emniyet katmanıdır: ilk kaydolan admin
olur, sonrakiler onay bekleyen role düşer. Bu değer ve `ENABLE_SIGNUP` Open WebUI'nin
*PersistentConfig* değişkenlerindendir — yalnızca **ilk açılışta** env'den okunur, sonrasında
veritabanındaki değer geçerlidir; yani env'i sonradan değiştirmek yerine arayüzdeki ayar kullanılır.
GPU olmayan 8 GB'lık DevOps makinesinde Ollama `OLLAMA_MAX_LOADED_MODELS=1` ile sınırlıdır ve
Open WebUI'nin gömme/erişim hattı kapalıdır (`BYPASS_EMBEDDING_AND_RETRIEVAL`): varsayılan RAG
hattı ayrıca bir gömme modeli indirir, bu makinede bellek bütçesi yoktur. Yüklenen dosyanın metni
doğrudan bağlama girer.
Script sırası (`configs/deployment/scripts`): Script sırası (`configs/deployment/scripts`):
``` ```
1-devops.sh → DevOps makinesi (Forgejo, runner) 1-devops.sh → DevOps makinesi (Forgejo, Rocket.Chat, n8n, Ollama, Open WebUI)
2-data-postgres.sh / 2-data-postgres.sh /
3-data-sql.sh → Veri katmanı 3-data-sql.sh → Veri katmanı
4-production-build.sh → İmaj build 4-production-build.sh → İmaj build

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,6 @@
docker exec -it ollama ollama list docker exec -it ollama ollama list
docker exec -it ollama ollama pull qwen2.5:3b docker exec -it ollama ollama pull qwen2.5:3b
docker exec -it ollama ollama pull gemma3:4b docker exec -it ollama ollama pull gemma3:4b
docker exec -it ollama ollama rm gemma3:4b
docker exec -it ollama ollama stop gemma3:4b

View file

@ -90,6 +90,18 @@
- sudo systemctl restart nginx - sudo systemctl restart nginx
- sudo nginx -t - sudo nginx -t
## Basic auth kullanıcıları (/etc/nginx/.htpasswd)
nginx.conf'ta auth_basic kullanan bloklar bu dosyaya bakar; su an tek kullanan
ollama.sozsoft.com/ollama-api/ (Ollama ham API'si). Dosya her istekte okunur,
reload gerekmez.
- sudo apt install apache2-utils -y
- sudo cut -d: -f1 /etc/nginx/.htpasswd # tanımlı kullanıcılar
- sudo htpasswd /etc/nginx/.htpasswd <kullanici> # ekle / parola değiştir
Dikkat: -c parametresi mevcut dosyayı siler, yalnızca ilk kullanıcıda kullanılır.
## Forgejo kurulumu ayarları yapılacak ## Forgejo kurulumu ayarları yapılacak
- home klasöründeki `cd ~/forgejo/data/gitea/conf` klasörün altındaki app.ini dosyasına aşağıdaki satırlar eklenecek. - home klasöründeki `cd ~/forgejo/data/gitea/conf` klasörün altındaki app.ini dosyasına aşağıdaki satırlar eklenecek.
@ -133,9 +145,18 @@
mkdir configs mkdir configs
- Dosya yapısı - Dosya yapısı
-> sozsoft-platform->configs->deployment->configs->nginx-devops.conf -> sozsoft-platform->configs->deployment->configs->nginx.conf
-> sozsoft-platform->configs->deployment->configs->turnserver.conf
-> sozsoft-platform->configs->deployment->docker-compose-devops.yml -> sozsoft-platform->configs->deployment->docker-compose-devops.yml
-> sozsoft-platform->configs->deployment->Dockerfile -> sozsoft-platform->configs->deployment->Dockerfile
-> sozsoft-platform->configs->deployment->.env
Servis konfigürasyonlarının tamamı `configs/deployment/configs/` altındadır
(nginx.conf, turnserver.conf, redis.conf, redis.7.conf, elasticsearch.yml);
compose dosyaları buradan mount eder, ayrı bir kopya tutulmaz.
- .env içeriği (compose bu değişkeni okur, depoya girmez)
echo "WEBUI_SECRET_KEY=$(openssl rand -hex 32)" >> .env
cd ~/sozsoft-platform/configs/deployment cd ~/sozsoft-platform/configs/deployment
chmod 775 * chmod 775 *
@ -153,5 +174,30 @@
- 192.168.0.2 sozsoft.com - 192.168.0.2 sozsoft.com
- 192.168.0.2 api.sozsoft.com - 192.168.0.2 api.sozsoft.com
- 192.168.0.2 cdn.sozsoft.com - 192.168.0.2 cdn.sozsoft.com
- 192.168.0.2 ollama.sozsoft.com
## Ollama + Open WebUI
`ollama.sozsoft.com` kökü Open WebUI'ye (3003), `/ollama-api/` öneki Ollama'nın ham
API'sine (11434) gider. Open WebUI kendi backend'ini `/api` altında sunduğu için ham
API bu öneğe taşınmıştır: https://ollama.sozsoft.com/ollama-api/tags
- Model: docker exec -it ollama ollama pull qwen2.5:3b
Makinede GPU yok ve 8 GB RAM var; compose `OLLAMA_MAX_LOADED_MODELS=1` ile ikinci bir
modelin belleğe alınmasını engeller. Yeni model çekmeden önce `free -h` bakılır.
- Basic auth yalnızca /ollama-api/ bloğundadır (Ollama'nın kendi kimlik doğrulaması yok).
Sohbet arayüzünün önüne KONMAZ: Open WebUI socket.io ile WebSocket açar, tarayıcı WS
upgrade isteğine basic auth kimliğini eklemediği için bağlantı 401 döngüsüne girer ve
parola ekranı üst üste sorulur.
- Arayüzün güvenlik sınırı uygulamanın kendi girişidir. Open WebUI ilk açılışta kaydolan
kullanıcıyı admin yapar; bu yüzden ilk hesap oluşturulur oluşturulmaz
Settings -> Admin -> General -> Enable New Sign Ups KAPATILIR. Bu adım atlanırsa ekran
internete açık kalır. Daha sıkı bir sınır gerekiyorsa vhost'a allow/deny ile IP kısıtı
eklenir; basic auth bu arayüz için seçenek değildir.
- Gömme/erişim hattı kapalıdır (BYPASS_EMBEDDING_AND_RETRIEVAL); yüklenen dosyanın metni
doğrudan bağlama girer. Modelin num_ctx değeri 8192 yapılır.
`ipconfig /flushdns` `ipconfig /flushdns`

View file

@ -90,6 +90,9 @@ server {
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header __tenant $http___tenant; proxy_set_header __tenant $http___tenant;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600; proxy_read_timeout 3600;
proxy_send_timeout 3600; proxy_send_timeout 3600;
} }
@ -101,6 +104,9 @@ server {
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header __tenant $http___tenant; proxy_set_header __tenant $http___tenant;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600; proxy_read_timeout 3600;
proxy_send_timeout 3600; proxy_send_timeout 3600;
} }
@ -113,6 +119,9 @@ server {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header __tenant $http___tenant; proxy_set_header __tenant $http___tenant;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection ""; proxy_set_header Connection "";
proxy_buffering off; proxy_buffering off;
proxy_cache off; proxy_cache off;
@ -155,13 +164,17 @@ server {
ssl_trusted_certificate /etc/ssl/sozsoft.com/chain1.pem; ssl_trusted_certificate /etc/ssl/sozsoft.com/chain1.pem;
ssl_certificate_key /etc/letsencrypt/live/ollama.sozsoft.com/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/ollama.sozsoft.com/privkey.pem;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
client_max_body_size 512M; client_max_body_size 512M;
location / { # Ollama ham API'sinin kendi kimlik dogrulamasi yoktur, bu yuzden basic auth arkasindadir.
proxy_pass http://127.0.0.1:11434; # Open WebUI kendi backend'ini de /api altinda sunar; iki uc ayni onekte yasayamaz,
# o yuzden ham API /ollama-api/ altina alindi:
# https://ollama.sozsoft.com/ollama-api/tags -> http://127.0.0.1:11434/api/tags
location /ollama-api/ {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://127.0.0.1:11434/api/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host 127.0.0.1:11434; proxy_set_header Host 127.0.0.1:11434;
@ -176,6 +189,29 @@ server {
proxy_connect_timeout 3600; proxy_connect_timeout 3600;
proxy_send_timeout 3600; proxy_send_timeout 3600;
} }
# Sohbet arayuzu (Open WebUI). Buraya basic auth konmaz: uygulama socket.io uzerinden
# WebSocket acar, tarayici WS upgrade istegine basic auth kimligini eklemez ve baglanti
# 401 dongusune girer. Guvenlik siniri uygulamanin kendi girisidir; kurulumdan hemen
# sonra Settings -> Admin -> General -> Enable New Sign Ups kapatilir.
location / {
proxy_pass http://127.0.0.1:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Yanit CPU uzerinde token token uretilir; tamponlama akisi durdurur.
proxy_buffering off;
proxy_read_timeout 3600;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
}
} }
# dashboard.sozsoft.com # dashboard.sozsoft.com

View file

@ -37,6 +37,12 @@ services:
environment: environment:
- ASPNETCORE_ENVIRONMENT=Dev - ASPNETCORE_ENVIRONMENT=Dev
- DOTNET_ENVIRONMENT=Dev - DOTNET_ENVIRONMENT=Dev
# Istek ters vekilden HTTP olarak gelir. Bu anahtar olmadan Request.Scheme "http"
# kalir ve OpenIddict kesif belgesindeki authorization_endpoint/token_endpoint
# adreslerini http:// olarak uretir; OAuth 2.1 istemcileri (MCP connector'lari)
# TLS'siz uc kabul etmedigi icin baglanti kurulamaz. nginx bu basliklari her
# location blogunda set eder, istemciden gelen deger her zaman ezilir.
- ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
ports: ports:
- 8080:8080 - 8080:8080
volumes: volumes:

View file

@ -19,6 +19,7 @@ volumes:
driver: local driver: local
n8n_data: n8n_data:
ollama_data: ollama_data:
open_webui_data:
# netdataconfig: # netdataconfig:
# netdatalib: # netdatalib:
# netdatacache: # netdatacache:
@ -113,10 +114,46 @@ services:
- ollama_data:/root/.ollama - ollama_data:/root/.ollama
environment: environment:
- OLLAMA_HOST=0.0.0.0 - OLLAMA_HOST=0.0.0.0
# 8 GB RAM, GPU yok: ikinci bir model bellege alinirsa kutu takasa duser.
- OLLAMA_MAX_LOADED_MODELS=1
- OLLAMA_NUM_PARALLEL=1
- OLLAMA_KEEP_ALIVE=5m
networks: networks:
- ollama - ollama
- n8n - n8n
# ollama.sozsoft.com sohbet arayuzu. Ters vekilde basic auth ARKASINDA DEGILDIR
# (WebSocket'i kirar, bkz. nginx.conf); guvenlik siniri uygulamanin kendi girisidir.
# Gomme/erisim hatti kapali: varsayilan RAG sentence-transformers indirir, bu kutuda
# ne disk ne bellek butcesi var.
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: always
ports:
- 127.0.0.1:3003:8080
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- WEBUI_URL=https://ollama.sozsoft.com
- WEBUI_SECRET_KEY=${WEBUI_SECRET_KEY}
# Ilk kaydolan kullanici admin olur; sonrakiler onaya duser ve onaylanana kadar hicbir
# sey yapamaz. Ilk hesap olusturuldugunda Settings -> Admin -> General uzerinden yeni
# kayit alimi da kapatilir. Bu iki deger PersistentConfig'tir: yalnizca ILK acilista
# env'den okunur, sonrasinda veritabanindaki deger gecerlidir.
- DEFAULT_USER_ROLE=pending
- BYPASS_EMBEDDING_AND_RETRIEVAL=true
- ENABLE_IMAGE_GENERATION=false
- WHISPER_MODEL_AUTO_UPDATE=false
- HF_HUB_OFFLINE=1
- GENERIC_TIMEZONE=Europe/Istanbul
volumes:
- open_webui_data:/app/backend/data
mem_limit: 2g
depends_on:
- ollama
networks:
- ollama
coturn: coturn:
image: coturn/coturn:latest # ✅ Güncel resmi image image: coturn/coturn:latest # ✅ Güncel resmi image
container_name: coturn container_name: coturn

View file

@ -4,4 +4,3 @@ docker compose -f docker-compose-devops.yml down
docker compose -f docker-compose-devops.yml up -d docker compose -f docker-compose-devops.yml up -d
docker exec -it ollama ollama pull qwen2.5:3b docker exec -it ollama ollama pull qwen2.5:3b
docker exec -it ollama ollama pull qwen2.5-coder:1.5b