Dokumen ini memberikan petunjuk langkah demi langkah untuk men-deploy Open WebUI, antarmuka chat berbasis browser yang dihosting sendiri, di lingkungan Google Distributed Cloud (GDC) dengan air gap. Open WebUI terhubung ke backend inferensi yang di-deploy dengan panduan pengguna Ollama dan vLLM melalui API yang kompatibel dengan Ollama dan OpenAI, sehingga developer dan pengguna akhir dapat berinteraksi dengan model open weight tanpa menulis panggilan API.
Arsitektur
Open WebUI berjalan sebagai Deployment replika tunggal di namespace-nya sendiri di cluster standar. Statusnya (akun pengguna, chat, setelan) disimpan di
ReadWriteOnce PersistentVolumeClaim. ConfigMap menyimpan setelan koneksi
untuk backend inferensi, yang dijangkau melalui nama Service
dalam cluster, dan Secret menyimpan kunci penandatanganan sesi. Service Kubernetes
dengan jenis LoadBalancer mengekspos antarmuka web, dan objek NetworkPolicy
/ ProjectNetworkPolicy mengamankan akses ke antarmuka tersebut.
Sebelum memulai
Pastikan setidaknya satu backend inferensi telah di-deploy dengan
panduan pengguna model Gemma dengan Ollama, panduan pengguna model Gemma dengan vLLM, atau panduan pengguna model Mistral dengan vLLM, dan Anda
memiliki akses ke direktori ${HOME}/gdcag-solutions dari implementasi referensi yang sesuai
di workstation.
Identity and Access Management
Pastikan akun, peran, dan izin IAM yang diperlukan telah dikonfigurasi dengan benar.
Peran GDC User di organisasi:
- Opsional: Standard Cluster Admin (Melihat cluster dari konsol)
Peran GDC User di project:
- Standard Cluster Admin (
standard-cluster-admin)
Peran GDC User di cluster standar:
StandardClusterRoleBindingkeStandardClusterRolecluster-admin(dibuat oleh Project IAM Admin, lihat bagian Identity and Access Management pada implementasi referensi)
Workstation
Jalankan semua perintah dalam panduan ini dari workstation dengan alat dan konektivitas yang diperlukan ke lingkungan dan internet, kecuali jika ada langkah yang menyatakan sebaliknya.
Buat struktur direktori panduan pengguna:
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/secretsBuat kunci penandatanganan sesi:
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_keyBuat file konfigurasi lingkungan panduan pengguna:
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" EOFGanti kode berikut:
OPEN_WEBUI_ENABLE_OLLAMA_API:truejika Anda men-deploy backend Ollama, jika tidak,false.OPEN_WEBUI_OLLAMA_BASE_URLS: URL dalam cluster Layanan Ollama, dipisahkan dengan;, misalnyahttp://gemma4-e4b.ollama-gemma.svc.cluster.local. Biarkan kosong jika Anda tidak men-deploy backend Ollama.OPEN_WEBUI_ENABLE_OPENAI_API:truejika Anda men-deploy backend vLLM, jika tidak,false.OPEN_WEBUI_OPENAI_API_BASE_URLS: URL dalam cluster dari Layanan vLLM dengan jalur/v1, dipisahkan dengan;, misalnyahttp://mistralai-mistral-7b-instruct-v0-2.vllm-mistral.svc.cluster.local/v1atauhttp://google-gemma-4-e2b-it.vllm-gemma.svc.cluster.local/v1. Biarkan kosong jika Anda tidak men-deploy backend vLLM.
Buat file loader lingkungan panduan pengguna:
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" EOFGanti kode berikut:
IMPLEMENTATION:ollamaatauvllm, implementasi referensi yang Anda selesaikan di workstation ini.
Edit dan tinjau file lingkungan dengan editor pilihan Anda:
${EDITOR:-vi} ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.d/open-webui.shAmbil file lingkungan:
source ${HOME}/gdcag-solutions/open-weight-models/open-webui/env.shOutputnya mirip dengan hal berikut ini:
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
Konfigurasikan
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}"Lakukan autentikasi ke lingkungan GDC:
gdcloud auth loginAmbil kredensial cluster:
gdcloud clusters get-credentials "${GDC_STANDARD_CLUSTER_NAME}" \ --project="${GDC_PROJECT}" \ --standard \ --zone="${GDC_ZONE}"Pastikan backend inferensi
Serviceyang dirujuk dalam file lingkungan ada: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
Penerapan
Migrasi artefak
Tentukan daftar image container yang diperlukan untuk solusi:
declare -a GDCS_REGISTRY_IMAGES=( "ghcr.io/open-webui/open-webui:${GDCS_OPEN_WEBUI_IMAGE_TAG}" ) export SERIALIZED_IMAGES=$(declare -p GDCS_REGISTRY_IMAGES)Isi image container yang diperlukan ke artifact registry dengan skrip implementasi referensi:
${GDCS_IMPLEMENTATION_HOME}/seed_registry.sh
Namespace
Buat namespace:
kubectl create namespace "${GDCS_WORKLOAD_NAMESPACE}"Tambahkan
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 -Buat
Secretdengan kunci penandatanganan sesi: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 -
Penyimpanan
Buat manifes untuk
PersistentVolumeClaimuntuk data 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 EOFTerapkan manifes untuk
PersistentVolumeClaim:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-rwo-pvc.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Antarmuka web
Buat manifes untuk
ConfigMap,Deployment, danServiceOpen WebUI. Open WebUI berjalan dalam mode offline (OFFLINE_MODE), mempertahankanConfigMapini sebagai sumber kebenaran (ENABLE_PERSISTENT_CONFIG=false), dan menonaktifkan fitur yang memerlukan akses 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 EOFTerapkan manifes untuk
ConfigMap,Deployment, danServiceOpen WebUI:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-deployment.yaml" \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"Buat manifes untuk
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 EOFTerapkan manifes untuk
NetworkPolicyOpen WebUI:kubectl apply \ --filename="${GDCS_USER_GUIDE_HOME}/${GDCS_OPEN_WEBUI_NAME}-netpol.yaml"
Tunggu hingga
Podberjalan: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'"Tunggu hingga
DeploymentSiap: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'"Opsional: Alirkan log container untuk memantau progres startup:
kubectl logs deployment/${GDCS_OPEN_WEBUI_NAME} \ --follow \ --namespace="${GDCS_WORKLOAD_NAMESPACE}"
Validasi
Ambil alamat IP LoadBalancer untuk 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}"Periksa endpoint health dan version menggunakan load balancer:
curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/health && echo curl --show-error --silent http://${OPEN_WEBUI_LB_IP}/api/version && echoOutputnya mirip dengan hal berikut ini:
{"status":true} {"version":"0.11.3"}Periksa endpoint health dan version menggunakan penerusan 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}Pastikan Open WebUI dapat menjangkau backend inferensi:
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'Buka
http://${OPEN_WEBUI_LB_IP}di browser dan buat akun pertama; akun pertama yang dibuat akan menjadi administrator. Pilih model dalam tampilan chat dan kirim perintah.
Operasi
Menambahkan atau mengubah backend inferensi
Perbarui
GDCS_OPEN_WEBUI_OLLAMA_BASE_URLSatauGDCS_OPEN_WEBUI_OPENAI_API_BASE_URLSdienv.d/open-webui.sh, ambil file lingkungan, dan jalankan langkah manifes di Antarmuka web lagi. Kemudian, terapkan manifes dan mulai ulang Open WebUI, yang membacaConfigMappada setiap startup karena konfigurasi persisten dinonaktifkan: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}"
Upgrade
Tetapkan
GDCS_OPEN_WEBUI_IMAGE_TAGbaru di file lingkungan, ambil sumbernya, semai image baru, dan luncurkanDeployment: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}"
Cadangan
Buat
VolumeSnapshotvolume data (standardVolumeSnapshotClasstersedia di cluster standar 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
Pemecahan masalah
| Gejala | Kemungkinan penyebab | Tindakan |
|---|---|---|
| Tidak ada model yang ditawarkan dalam tampilan chat | Alamat Service backend salah, backend tidak Siap, atau NetworkPolicy backend tidak mengizinkan namespace Open WebUI |
Jalankan langkah verifikasi backend di Validation; periksa Deployment backend dan NetworkPolicy-nya |
Pod dimulai ulang saat pengaktifan |
Peluncuran pertama yang lambat di PersistentVolume |
Periksa kubectl describe pod; startupProbe mengizinkan 10 menit |
| Upload dokumen (RAG) gagal | Mode offline memblokir download model embedding default | Konfigurasikan RAG_EMBEDDING_ENGINE=ollama dengan model embedding yang disajikan oleh Ollama, atau tetap nonaktifkan RAG |
IP LoadBalancer tetap <pending> atau tidak dapat dijangkau |
Load balancer tidak tersedia di lingkungan, atau ProjectNetworkPolicy tidak ada |
Gunakan langkah validasi penerusan port; minta ProjectNetworkPolicy kepada Project NetworkPolicy Admin |
Sesi tidak valid setelah memulai ulang Pod |
WEBUI_SECRET_KEY diubah |
Menjaga Secret tetap stabil; memulihkannya dari secrets/open_webui_secret_key |
Pembersihan
Hapus resource Open WebUI:
kubectl delete namespace "${GDCS_WORKLOAD_NAMESPACE}"Hapus file lokal:
rm -rf ${HOME}/gdcag-solutions/open-weight-models/open-webui
Materi tambahan
- Dokumentasi Open WebUI
- Referensi variabel lingkungan Open WebUI
- Kompatibilitas OpenAI Ollama
- Server vLLM yang kompatibel dengan OpenAI