Questo documento fornisce istruzioni passo passo per il deployment di Open WebUI, un'interfaccia di chat self-hosted basata su browser, in ambienti con air gap di Google Distributed Cloud (GDC). Open WebUI si connette ai backend di inferenza di cui è stato eseguito il deployment con le guide dell'utente di Ollama e vLLM tramite le relative API compatibili con Ollama e OpenAI, in modo che gli sviluppatori e gli utenti finali possano interagire con i modelli con pesi aperti senza scrivere chiamate API.
Architettura
Open WebUI viene eseguito come Deployment a replica singola nel proprio spazio dei nomi del cluster standard. Il suo stato (account utente, chat, impostazioni) viene archiviato su un
ReadWriteOnce PersistentVolumeClaim. Un ConfigMap contiene le impostazioni di connessione per i backend di inferenza, che vengono raggiunti tramite i relativi nomi Service nel cluster, mentre un Secret contiene la chiave di firma della sessione. Un Service Kubernetes di tipo LoadBalancer espone l'interfaccia web e gli oggetti NetworkPolicy/ProjectNetworkPolicy proteggono l'accesso.
Prima di iniziare
Assicurati che sia stato eseguito il deployment di almeno un backend di inferenza con la guida dell'utente dei modelli Gemma con Ollama, la guida dell'utente dei modelli Gemma con vLLM o la guida dell'utente dei modelli Mistral con vLLM e di avere accesso alla directory ${HOME}/gdcag-solutions dell'implementazione di riferimento corrispondente sulla workstation.
Identity and Access Management
Assicurati che gli account, i ruoli e le autorizzazioni IAM necessari siano configurati correttamente.
Ruoli Utente GDC nell'organizzazione:
- (Facoltativo) Amministratore cluster standard (visualizza i cluster dalla console)
Ruoli GDC User sul progetto:
- Standard Cluster Admin (
standard-cluster-admin)
Ruolo GDC User nel cluster standard:
StandardClusterRoleBindingalcluster-adminStandardClusterRole(creato da un amministratore di progetto IAM, consulta la sezione Identity and Access Management dell'implementazione di riferimento)
Workstation
Esegui tutti i comandi di questa guida da una workstation con gli strumenti e la connettività necessari per l'ambiente e internet, se non diversamente indicato in un passaggio.
Crea la struttura di directory della guida dell'utente:
mkdir -p ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.d mkdir -p ${HOME}/gdcag-solutions/open-weight-models/open-webui/secrets chmod u=rwx,go= ${HOME}/gdcag-solutions/open-weight-models/open-webui/secretsCrea la chiave di firma della sessione:
openssl rand -hex 32 > ${HOME}/gdcag-solutions/open-weight-models/open-webui/secrets/open_webui_secret_key chmod u=rw,go= ${HOME}/gdcag-solutions/open-weight-models/open-webui/secrets/open_webui_secret_keyCrea il file di configurazione dell'ambiente della guida dell'utente:
cat << 'EOF' > ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.d/open-webui.sh && echo "Successfully created." || echo "Failed to create!" # Open WebUI export GDCS_OPEN_WEBUI_IMAGE_TAG="v0.11.3" export GDCS_OPEN_WEBUI_NAME="open-webui" export GDCS_OPEN_WEBUI_PVC_SIZE="20Gi" export GDCS_OPEN_WEBUI_SECRET_KEY="$(cat ${GDCS_USER_GUIDE_HOME}/secrets/open_webui_secret_key)" # Inference backends (in-cluster Service addresses, several backends separated by ';'). # The user guides name the Service after the model: Ollama uses GDCS_OLLAMA_MODEL_KUBERNETES # (gemma4:e4b -> gemma4-e4b) and vLLM uses GDCS_HF_MODEL_KUBERNETES (such as vllm-gemma or vllm-mistral) # (mistralai/Mistral-7B-Instruct-v0.2 -> mistralai-mistral-7b-instruct-v0-2); kubectl get service # in the workload namespace (ollama-gemma, vllm-gemma or vllm-mistral) lists them. export GDCS_OPEN_WEBUI_ENABLE_OLLAMA_API="OPEN_WEBUI_ENABLE_OLLAMA_API" export GDCS_OPEN_WEBUI_OLLAMA_BASE_URLS="OPEN_WEBUI_OLLAMA_BASE_URLS" export GDCS_OPEN_WEBUI_ENABLE_OPENAI_API="OPEN_WEBUI_ENABLE_OPENAI_API" export GDCS_OPEN_WEBUI_OPENAI_API_BASE_URLS="OPEN_WEBUI_OPENAI_API_BASE_URLS" export GDCS_OPEN_WEBUI_OPENAI_API_KEYS="none" # vLLM does not check API keys unless started with --api-key # Workload export GDCS_WORKLOAD_NAMESPACE="open-webui" # CPU resources export GDCS_WORKLOAD_CPU_LIMIT="2" export GDCS_WORKLOAD_CPU_REQUEST="1" # Memory resources export GDCS_WORKLOAD_MEMORY_LIMIT="4Gi" export GDCS_WORKLOAD_MEMORY_REQUEST="2Gi" EOFSostituisci quanto segue:
OPEN_WEBUI_ENABLE_OLLAMA_API:truese hai eseguito il deployment di un backend Ollama, altrimentifalse.OPEN_WEBUI_OLLAMA_BASE_URLS: gli URL in-cluster dei servizi Ollama, separati da;, ad esempiohttp://gemma4-e4b.ollama-gemma.svc.cluster.local. Lascia il campo vuoto se non hai eseguito il deployment di un backend Ollama.OPEN_WEBUI_ENABLE_OPENAI_API:truese hai eseguito il deployment di un backend vLLM, altrimentifalse.OPEN_WEBUI_OPENAI_API_BASE_URLS: gli URL in-cluster dei servizi vLLM con il percorso/v1, separati da;, ad esempiohttp://mistralai-mistral-7b-instruct-v0-2.vllm-mistral.svc.cluster.local/v1ohttp://google-gemma-4-e2b-it.vllm-gemma.svc.cluster.local/v1. Lascia il campo vuoto se non hai eseguito il deployment di un backend vLLM.
Crea il file del caricatore dell'ambiente della guida dell'utente:
cat << 'EOF' > ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.sh && echo "Successfully created." || echo "Failed to create!" source "${HOME}/gdcag-solutions/open-weight-models/IMPLEMENTATION/env.sh" export GDCS_USER_GUIDE_HOME="${HOME}/gdcag-solutions/open-weight-models/open-webui" echo "GDCS_USER_GUIDE_HOME=${GDCS_USER_GUIDE_HOME}" # Sourced in dependency order source "${GDCS_USER_GUIDE_HOME}/env.d/open-webui.sh" EOFSostituisci quanto segue:
IMPLEMENTATION:ollamaovllm, l'implementazione di riferimento che hai completato su questa workstation.
Modifica e rivedi il file di ambiente con l'editor che preferisci:
${EDITOR:-vi} ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.d/open-webui.shEsegui il sourcing del file di ambiente:
source ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.shL'output è simile al seguente:
GDCS_ROOT_HOME=HOME_DIRECTORY_PATH/gdcag-solutions GDCS_IMPLEMENTATION_HOME=HOME_DIRECTORY_PATH/gdcag-solutions/open-weight-models/IMPLEMENTATION GDCS_USER_GUIDE_HOME=HOME_DIRECTORY_PATH/gdcag-solutions/open-weight-models/open-webui
Cluster
Configura
gdcloud:gdcloud config set core/account "default-user" gdcloud config set core/organization_console_url "${GDC_ZONAL_CONSOLE_URL}" gdcloud config set core/project "${GDC_PROJECT}" gdcloud config set core/zone "${GDC_ZONE}"Esegui l'autenticazione nell'ambiente GDC:
gdcloud auth loginRecupera le credenziali del cluster:
gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \ --project="${GDC_PROJECT}" \ --standard \ --zone="${GDC_ZONE}"Verifica che i backend di inferenza
Servicea cui viene fatto riferimento nel file dell'ambiente esistano:for url in $(echo "${GDCS_OPEN_WEBUI_OLLAMA_BASE_URLS};${GDCS_OPEN_WEBUI_OPENAI_API_BASE_URLS}" | tr ';' ' '); do host=$(echo "${url}" | sed -E 's#https?://([^/:]+).*#\1#') kubectl get service "${host%%.*}" --namespace="$(echo "${host}" | cut -d. -f2)" --output=wide done
Implementazione
Migrazione degli artefatti
Definisci l'elenco delle immagini container richieste per la soluzione:
declare -a GDCS_REGISTRY_IMAGES=( "ghcr.io/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG}" ) export SERIALIZED_IMAGES=$(declare -p GDCS_REGISTRY_IMAGES)Esegui il seeding delle immagini container richieste in Artifact Registry con lo script dell'implementazione di riferimento:
${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
Spazio dei nomi
Crea lo spazio dei nomi:
kubectl create namespace "${GDCS_WORKLOAD_NAMESPACE}"Aggiungi
imagePullSecret:kubectl create secret docker-registry "${GDCS_HARBOR_K8S_PULL_SECRET}" \ --dry-run=client \ --from-file=.dockerconfigjson=${GDCS_HARBOR_K8S_DOCKER_CONFIG}/config.json \ --namespace="${GDCS_WORKLOAD_NAMESPACE}" \ --output=yaml | kubectl apply -f -Crea il
Secretcon la chiave di firma della sessione:kubectl create secret generic "${GDCS_OPEN_WEBUI_NAME}" \ --dry-run=client \ --from-literal=WEBUI_SECRET_KEY="${GDCS_OPEN_WEBUI_SECRET_KEY}" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}" \ --output=yaml | kubectl apply -f -
Archiviazione
Crea il manifest per il
PersistentVolumeClaimper i dati di Open WebUI:cat <<EOF > "${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-rwo-pvc.yaml" && echo "Successfully created." || echo "Failed to create!" apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ${GDCS_OPEN_WEBUI_NAME}-rwo spec: accessModes: - ReadWriteOnce resources: requests: storage: ${GDCS_OPEN_WEBUI_PVC_SIZE} storageClassName: standard-rwo volumeMode: Filesystem EOFApplica il manifest per
PersistentVolumeClaim:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-rwo-pvc.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Interfaccia web
Crea il manifest per Open WebUI
ConfigMap,DeploymenteService. Open WebUI viene eseguita in modalità offline (OFFLINE_MODE), mantiene questaConfigMapcome fonte di attendibilità (ENABLE_PERSISTENT_CONFIG=false) e disattiva le funzionalità che richiedono l'accesso a internet:cat <<EOF > "${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-deployment.yaml" && echo "Successfully created." || echo "Failed to create!" apiVersion: v1 kind: ConfigMap metadata: name: ${GDCS_OPEN_WEBUI_NAME} data: DATA_DIR: "/app/backend/data" ENABLE_COMMUNITY_SHARING: "false" ENABLE_IMAGE_GENERATION: "false" ENABLE_OLLAMA_API: "${GDCS_OPEN_WEBUI_ENABLE_OLLAMA_API}" ENABLE_OPENAI_API: "${GDCS_OPEN_WEBUI_ENABLE_OPENAI_API}" ENABLE_PERSISTENT_CONFIG: "false" ENABLE_SIGNUP: "true" ENABLE_VERSION_UPDATE_CHECK: "false" ENABLE_WEB_SEARCH: "false" OFFLINE_MODE: "true" OLLAMA_BASE_URLS: "${GDCS_OPEN_WEBUI_OLLAMA_BASE_URLS}" OPENAI_API_BASE_URLS: "${GDCS_OPEN_WEBUI_OPENAI_API_BASE_URLS}" OPENAI_API_KEYS: "${GDCS_OPEN_WEBUI_OPENAI_API_KEYS}" PORT: "8080" --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: ${GDCS_OPEN_WEBUI_NAME} name: ${GDCS_OPEN_WEBUI_NAME} spec: replicas: 1 selector: matchLabels: app: ${GDCS_OPEN_WEBUI_NAME} strategy: type: Recreate template: metadata: labels: app: ${GDCS_OPEN_WEBUI_NAME} spec: containers: - envFrom: - configMapRef: name: ${GDCS_OPEN_WEBUI_NAME} - secretRef: name: ${GDCS_OPEN_WEBUI_NAME} image: ${GDCS_HARBOR_PROJECT_URI}/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG} imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 3 httpGet: path: /health port: 8080 periodSeconds: 30 timeoutSeconds: 10 name: open-webui ports: - containerPort: 8080 name: http readinessProbe: failureThreshold: 3 httpGet: path: /health port: 8080 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5 resources: limits: cpu: "${GDCS_WORKLOAD_CPU_LIMIT}" memory: "${GDCS_WORKLOAD_MEMORY_LIMIT}" requests: cpu: "${GDCS_WORKLOAD_CPU_REQUEST}" memory: "${GDCS_WORKLOAD_MEMORY_REQUEST}" securityContext: allowPrivilegeEscalation: false startupProbe: failureThreshold: 60 httpGet: path: /health port: 8080 periodSeconds: 10 timeoutSeconds: 5 volumeMounts: - mountPath: /app/backend/data name: data imagePullSecrets: - name: ${GDCS_HARBOR_K8S_PULL_SECRET} volumes: - name: data persistentVolumeClaim: claimName: ${GDCS_OPEN_WEBUI_NAME}-rwo --- apiVersion: v1 kind: Service metadata: name: ${GDCS_OPEN_WEBUI_NAME} spec: ports: - name: http port: 80 protocol: TCP targetPort: 8080 selector: app: ${GDCS_OPEN_WEBUI_NAME} type: LoadBalancer EOFApplica il manifest per Open WebUI
ConfigMap,DeploymenteService:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-deployment.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"Crea il manifest per Open WebUI
NetworkPolicy:cat <<EOF > "${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-netpol.yaml" && echo "Successfully created." || echo "Failed to create!" apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-${GDCS_OPEN_WEBUI_NAME}-ingress namespace: ${GDCS_WORKLOAD_NAMESPACE} spec: ingress: - from: - ipBlock: cidr: 0.0.0.0/0 - namespaceSelector: {} ports: - port: 8080 protocol: TCP podSelector: {} policyTypes: - Ingress EOFApplica il manifest per Open WebUI
NetworkPolicy:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-netpol.yaml"
Attendi che
Podsia in esecuzione:watch --color --interval 5 --no-title \ "kubectl get pods --selector=app=${GDCS_OPEN_WEBUI_NAME} \ --namespace=${GDCS_WORKLOAD_NAMESPACE} | GREP_COLORS='mt=01;92' grep -E --color=always -e '^' -e 'Running'"Attendi che
Deploymentsia pronto:watch --color --interval 5 --no-title \ "kubectl get deployment/${GDCS_OPEN_WEBUI_NAME} \ --namespace=${GDCS_WORKLOAD_NAMESPACE} | GREP_COLORS='mt=01;92' grep -E --color=always -e '^' -e '1/1 1 1'"(Facoltativo) Trasmetti in streaming i log del container per monitorare l'avanzamento dell'avvio:
kubectl logs deployment/${GDCS_OPEN_WEBUI_NAME} \ --follow \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Convalida
Recupera l'indirizzo IP del bilanciatore del carico per Open WebUI:
export OPEN_WEBUI_LB_IP=$(kubectl get service/${GDCS_OPEN_WEBUI_NAME} --namespace="${GDCS_WORKLOAD_NAMESPACE}" --output=jsonpath='{.status.loadBalancer.ingress[*].ip}') echo "OPEN_WEBUI_LB_IP=${OPEN_WEBUI_LB_IP}"Controlla gli endpoint di integrità e versione utilizzando il bilanciatore del carico:
curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/health && echo curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/api/version && echoL'output è simile al seguente:
{"status":true} {"version":"0.11.3"}Controlla gli endpoint di integrità e versione utilizzando il port forwarding:
kubectl port-forward service/${GDCS_OPEN_WEBUI_NAME} \ --namespace="${GDCS_WORKLOAD_NAMESPACE}" 8080:80 & PF_PID=$! sleep 2 curl --show-error --silent http://127.0.0.1:8080/health && echo curl --show-error --silent http://127.0.0.1:8080/api/version && echo kill ${PF_PID}Verifica che Open WebUI possa raggiungere i backend di inferenza:
kubectl exec deployment/${GDCS_OPEN_WEBUI_NAME} \ --namespace="${GDCS_WORKLOAD_NAMESPACE}" \ -- sh -c 'for url in $(echo "${OLLAMA_BASE_URLS};${OPENAI_API_BASE_URLS}" | tr ";" " "); do case "${url}" in *"/v1") echo "${url}/models"; curl --max-time 10 --show-error --silent "${url}/models" | head -c 300; echo;; *) echo "${url}/api/tags"; curl --max-time 10 --show-error --silent "${url}/api/tags" | head -c 300; echo;; esac; done'Apri
http://${OPEN_WEBUI_LB_IP}in un browser e crea il primo account; il primo account creato diventa l'amministratore. Seleziona un modello nella visualizzazione chat e invia un prompt.
Operazioni
Aggiungi o modifica i backend di inferenza
Aggiorna
GDCS_OPEN_WEBUI_OLLAMA_BASE_URLSoGDCS_OPEN_WEBUI_OPENAI_API_BASE_URLSinenv.d/open-webui.sh, esegui il sourcing del file di ambiente ed esegui di nuovo il passaggio del manifest nell'interfaccia web. Quindi applica il manifest e riavvia Open WebUI, che leggeConfigMapa ogni avvio perché la configurazione persistente è disabilitata:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-deployment.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}" kubectl rollout restart deployment/${GDCS_OPEN_WEBUI_NAME} \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Esegui l'upgrade
Imposta il nuovo
GDCS_OPEN_WEBUI_IMAGE_TAGnel file dell'ambiente, esegui il sourcing, esegui il seeding della nuova immagine e l'implementazione diDeployment:source ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.sh declare -a GDCS_REGISTRY_IMAGES=( "ghcr.io/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG}" ) export SERIALIZED_IMAGES=$(declare -p GDCS_REGISTRY_IMAGES) ${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh kubectl set image deployment/${GDCS_OPEN_WEBUI_NAME} \ open-webui="${GDCS_HARBOR_PROJECT_URI}/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG}" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Backup
Crea un
VolumeSnapshotdel volume di dati (ilstandardVolumeSnapshotClassè disponibile sui cluster standard GDC):cat <<EOF | kubectl apply --namespace="${GDCS_WORKLOAD_NAMESPACE}" -f - apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot metadata: name: ${GDCS_OPEN_WEBUI_NAME}-$(date +%Y%m%d-%H%M) spec: source: persistentVolumeClaimName: ${GDCS_OPEN_WEBUI_NAME}-rwo volumeSnapshotClassName: standard EOF
Risoluzione dei problemi
| Sintomo | Causa probabile | Azione |
|---|---|---|
| Nessun modello offerto nella visualizzazione della chat | Indirizzo del backend Service errato, backend non pronto o backend NetworkPolicy non ammette lo spazio dei nomi Open WebUI |
Esegui il passaggio di verifica del backend in Convalida; controlla il backend Deployment e il relativo NetworkPolicy |
Pod riavvii durante l'avvio |
Primo avvio lento su PersistentVolume |
Controlla kubectl describe pod; startupProbe consente 10 minuti |
| Caricamento di documenti (RAG) non riuscito | La modalità offline blocca il download del modello di embedding predefinito | Configura RAG_EMBEDDING_ENGINE=ollama con un modello di embedding fornito da Ollama o mantieni RAG disattivato |
L'IP LoadBalancer rimane <pending> o non è raggiungibile |
Bilanciatore del carico non disponibile nell'ambiente o ProjectNetworkPolicy mancante |
Utilizza il passaggio di convalida del port forwarding; chiedi all'amministratore Project NetworkPolicy un ProjectNetworkPolicy |
Sessione non valida dopo un riavvio di Pod |
WEBUI_SECRET_KEY cambiato |
Mantieni stabile Secret; ripristinalo da secrets/open_webui_secret_key |
Esegui la pulizia
Elimina le risorse Open WebUI:
kubectl delete namespace "${GDCS_WORKLOAD_NAMESPACE}"Elimina i file locali:
rm -rf ${HOME}/gdcag-solutions/open-weight-models/open-webui
Materiali aggiuntivi
- Apri la documentazione di WebUI
- Riferimento alle variabile di ambiente di Open WebUI
- Compatibilità con OpenAI di Ollama
- Server vLLM compatibile con OpenAI