Ce document fournit des instructions détaillées pour déployer Open WebUI, une interface de chat auto-hébergée basée sur un navigateur, dans des environnements Google Distributed Cloud (GDC) air-gapped. Open WebUI se connecte aux backends d'inférence déployés avec les guides de l'utilisateur Ollama et vLLM via leurs API compatibles avec Ollama et OpenAI. Les développeurs et les utilisateurs finaux peuvent ainsi interagir avec les modèles de pondérations ouverts sans écrire d'appels d'API.
Architecture
Open WebUI s'exécute en tant que Deployment à instance répliquée unique dans son propre espace de noms du cluster standard. Son état (comptes utilisateur, discussions, paramètres) est stocké sur un ReadWriteOnce PersistentVolumeClaim. Un ConfigMap contient les paramètres de connexion des backends d'inférence, qui sont accessibles via leurs noms Service dans le cluster, et un Secret contient la clé de signature de session. Un Service Kubernetes de type LoadBalancer expose l'interface Web, et les objets NetworkPolicy/ProjectNetworkPolicy sécurisent l'accès à celle-ci.
Avant de commencer
Assurez-vous qu'au moins un backend d'inférence a été déployé avec le guide de l'utilisateur des modèles Gemma avec Ollama, le guide de l'utilisateur des modèles Gemma avec vLLM ou le guide de l'utilisateur des modèles Mistral avec vLLM, et que vous avez accès au répertoire ${HOME}/gdcag-solutions de l'implémentation de référence correspondante sur le poste de travail.
Identity and Access Management
Assurez-vous que les comptes, rôles et autorisations IAM nécessaires sont correctement configurés.
Rôles Utilisateur GDC sur l'organisation :
- Facultatif : Administrateur de cluster standard (afficher les clusters depuis la console)
Utilisateur GDC sur le projet :
- Administrateur de cluster standard (
standard-cluster-admin)
Utilisateur GDC sur le cluster standard :
StandardClusterRoleBindingauStandardClusterRolecluster-admin(créé par un administrateur IAM du projet, consultez la section Identity and Access Management de l'implémentation de référence)
Station de travail
Sauf indication contraire, exécutez toutes les commandes de ce guide à partir d'un poste de travail disposant des outils requis et d'une connectivité à l'environnement et à Internet.
Créez la structure de répertoires du guide de l'utilisateur :
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/secretsCréez la clé de signature de session :
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_keyCréez le fichier de configuration de l'environnement du guide de l'utilisateur :
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" EOFRemplacez les éléments suivants :
OPEN_WEBUI_ENABLE_OLLAMA_API:truesi vous avez déployé un backend Ollama, sinonfalse.OPEN_WEBUI_OLLAMA_BASE_URLS: URL des services Ollama dans le cluster, séparées par;, par exemplehttp://gemma4-e4b.ollama-gemma.svc.cluster.local. Laissez ce champ vide si vous n'avez pas déployé de backend Ollama.OPEN_WEBUI_ENABLE_OPENAI_API:truesi vous avez déployé un backend vLLM, sinonfalse.OPEN_WEBUI_OPENAI_API_BASE_URLS: URL dans le cluster des services vLLM avec le chemin d'accès/v1, séparées par;, par exemplehttp://mistralai-mistral-7b-instruct-v0-2.vllm-mistral.svc.cluster.local/v1ouhttp://google-gemma-4-e2b-it.vllm-gemma.svc.cluster.local/v1. Laissez ce champ vide si vous n'avez pas déployé de backend vLLM.
Créez le fichier de chargement de l'environnement du guide de l'utilisateur :
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" EOFRemplacez les éléments suivants :
IMPLEMENTATION:ollamaouvllm, l'implémentation de référence que vous avez effectuée sur ce poste de travail.
Modifiez et examinez le fichier d'environnement avec l'éditeur de votre choix :
${EDITOR:-vi} ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.d/open-webui.shSourcez le fichier d'environnement :
source ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.shLe résultat ressemble à ce qui suit :
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
Configurez
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}"Authentifiez-vous auprès de l'environnement GDC :
gdcloud auth loginRécupérez les identifiants du cluster :
gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \ --project="${GDC_PROJECT}" \ --standard \ --zone="${GDC_ZONE}"Vérifiez que les backends d'inférence
Serviceréférencés dans le fichier d'environnement existent :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
Implémentation
Migration d'artefacts
Définissez la liste des images de conteneurs requises pour la solution :
declare -a GDCS_REGISTRY_IMAGES=( "ghcr.io/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG}" ) export SERIALIZED_IMAGES=$(declare -p GDCS_REGISTRY_IMAGES)Amorcez les images de conteneurs requises dans Artifact Registry à l'aide du script de l'implémentation de référence :
${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
Espace de noms
Créez l'espace de noms :
kubectl create namespace "${GDCS_WORKLOAD_NAMESPACE}"Ajoutez le
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 -Créez le
Secretavec la clé de signature de session :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 -
Stockage
Créez le fichier manifeste pour le
PersistentVolumeClaimpour les données 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 EOFAppliquez le fichier manifeste pour
PersistentVolumeClaim:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-rwo-pvc.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Interface Web
Créez le fichier manifeste pour les éléments
ConfigMap,DeploymentetServiced'Open WebUI. Open WebUI s'exécute en mode hors connexion (OFFLINE_MODE), conserve ceConfigMapcomme source de vérité (ENABLE_PERSISTENT_CONFIG=false) et désactive les fonctionnalités qui nécessitent un accès à 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 EOFAppliquez le fichier manifeste pour les éléments
ConfigMap,DeploymentetServiced'Open WebUI :kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-deployment.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"Créez le fichier manifeste pour l'
NetworkPolicyOpen WebUI :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 EOFAppliquez le fichier manifeste pour l'
NetworkPolicyOpen WebUI :kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-netpol.yaml"
Attendez que l'état de
Podpasse à "En cours d'exécution" :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'"Attendez que le
Deploymentsoit prêt :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'"Facultatif : Diffusez les journaux du conteneur pour surveiller la progression du démarrage :
kubectl logs deployment/${GDCS_OPEN_WEBUI_NAME} \ --follow \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Validation
Récupérez l'adresse IP de l'équilibreur de charge pour 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}"Vérifiez les points de terminaison d'état et de version à l'aide de l'équilibreur de charge :
curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/health && echo curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/api/version && echoLe résultat ressemble à ce qui suit :
{"status":true} {"version":"0.11.3"}Vérifiez les points de terminaison d'état et de version à l'aide du transfert de port :
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}Vérifiez qu'Open WebUI peut atteindre les backends d'inférence :
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'Ouvrez
http://${OPEN_WEBUI_LB_IP}dans un navigateur et créez le premier compte. Le premier compte créé devient l'administrateur. Sélectionnez un modèle dans la vue de chat et envoyez une requête.
Opérations
Ajouter ou modifier des backends d'inférence
Mettez à jour
GDCS_OPEN_WEBUI_OLLAMA_BASE_URLSouGDCS_OPEN_WEBUI_OPENAI_API_BASE_URLSdansenv.d/open-webui.sh, sourcez le fichier d'environnement, puis exécutez à nouveau l'étape du fichier manifeste dans l'interface Web. Appliquez ensuite le fichier manifeste et redémarrez Open WebUI, qui lit leConfigMapà chaque démarrage, car la configuration persistante est désactivée :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}"
Mettre à niveau
Définissez la nouvelle valeur
GDCS_OPEN_WEBUI_IMAGE_TAGdans le fichier d'environnement, sourcez-la, amorcez la nouvelle image et effectuez un déploiement progressif deDeployment: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}"
Sauvegarde
Créez un
VolumeSnapshotdu volume de données (lestandardVolumeSnapshotClassest disponible sur les clusters GDC standards) :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
Dépannage
| Problème constaté | Cause probable | Action |
|---|---|---|
| Aucun modèle n'est proposé dans la vue de chat | L'adresse du backend Service est incorrecte, le backend n'est pas prêt ou le backend NetworkPolicy n'accepte pas l'espace de noms Open WebUI. |
Exécutez l'étape de vérification du backend dans Validation ; vérifiez le backend Deployment et son NetworkPolicy. |
Pod redémarrages au démarrage |
Démarrage lent sur le PersistentVolume |
Vérifiez kubectl describe pod ; startupProbe autorise 10 minutes |
| Échec de l'importation de documents (RAG) | Le mode hors connexion bloque le téléchargement du modèle d'embedding par défaut | Configurez RAG_EMBEDDING_ENGINE=ollama avec un modèle d'embedding diffusé par Ollama, ou laissez le RAG désactivé |
L'adresse IP de l'équilibreur de charge reste <pending> ou n'est pas accessible |
Équilibreur de charge non disponible dans l'environnement ou ProjectNetworkPolicy manquant |
Utilisez l'étape de validation du transfert de port. Demandez un ProjectNetworkPolicy à l'administrateur NetworkPolicy du projet. |
Session non valide après un redémarrage de Pod |
WEBUI_SECRET_KEY modifié |
Maintenir la stabilité de Secret ; le restaurer à partir de secrets/open_webui_secret_key |
Effectuer un nettoyage
Supprimez les ressources Open WebUI :
kubectl delete namespace "${GDCS_WORKLOAD_NAMESPACE}"Supprimez les fichiers locaux :
rm -rf ${HOME}/gdcag-solutions/open-weight-models/open-webui
Autres ressources
- Documentation Open WebUI
- Ouvrir la documentation de référence sur les variables d'environnement WebUI
- Compatibilité Ollama OpenAI
- Serveur vLLM compatible avec OpenAI