diff --git a/.gitignore b/.gitignore index e201ecd0..0b651bca 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,7 @@ __pycache__ /shell/preload/lang_env.sh .deepseek/ -.claude/ \ No newline at end of file +.claude/ + +# local Kubernetes overlays +/deploy/kubernetes/overlays/local/ diff --git a/deploy/kubernetes/README.md b/deploy/kubernetes/README.md new file mode 100644 index 00000000..74b1d445 --- /dev/null +++ b/deploy/kubernetes/README.md @@ -0,0 +1,81 @@ +# Kubernetes deployment + +This deploys Qinglong as a single-replica `StatefulSet` with persistent data at `/ql/data`. + +```bash +kubectl apply -k deploy/kubernetes/overlays/local +kubectl -n qinglong rollout status statefulset/qinglong +``` + +Open the panel locally: + +```bash +kubectl -n qinglong port-forward svc/qinglong 5700:5700 +``` + +Then visit . + +## Image registry overlays + +Use `overlays/example` as the committed template for registry customization: + +```yaml +whyour/qinglong:debian -> registry.example.com/whyour/qinglong:debian +``` + +Create `overlays/local/kustomization.yaml` for the actual cluster image. The `local` overlay is ignored by git so private registry names, digests, and credentials-related references stay local. + +## Storage + +The manifest creates a 5 GiB `ReadWriteOnce` PVC from the cluster's default `StorageClass`. +If your cluster has no default storage class, add `storageClassName` under: + +```yaml +volumeClaimTemplates: + - metadata: + name: data + spec: + storageClassName: your-storage-class +``` + +Keep `replicas: 1`. Qinglong stores state in the persistent data directory, including SQLite files, so multiple replicas should not share the same data volume. + +## Ingress example + +If you expose Qinglong through an Ingress path other than `/`, set `QlBaseUrl` to the same path with leading and trailing slashes. + +```yaml +env: + - name: QlBaseUrl + value: "/qinglong/" +``` + +Example Ingress: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: qinglong + namespace: qinglong +spec: + rules: + - host: qinglong.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: qinglong + port: + number: 5700 +``` + +## Maintenance commands + +```bash +kubectl -n qinglong logs -f statefulset/qinglong +kubectl -n qinglong exec -it statefulset/qinglong -- ql check +kubectl -n qinglong exec -it statefulset/qinglong -- ql update +``` diff --git a/deploy/kubernetes/base/kustomization.yaml b/deploy/kubernetes/base/kustomization.yaml new file mode 100644 index 00000000..2c78f0ff --- /dev/null +++ b/deploy/kubernetes/base/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - qinglong.yaml diff --git a/deploy/kubernetes/base/qinglong.yaml b/deploy/kubernetes/base/qinglong.yaml new file mode 100644 index 00000000..62213799 --- /dev/null +++ b/deploy/kubernetes/base/qinglong.yaml @@ -0,0 +1,95 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: qinglong +--- +apiVersion: v1 +kind: Service +metadata: + name: qinglong + namespace: qinglong + labels: + app.kubernetes.io/name: qinglong +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: qinglong + ports: + - name: http + port: 5700 + targetPort: http +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: qinglong + namespace: qinglong + labels: + app.kubernetes.io/name: qinglong +spec: + serviceName: qinglong + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: qinglong + template: + metadata: + labels: + app.kubernetes.io/name: qinglong + spec: + securityContext: + fsGroup: 5432 + fsGroupChangePolicy: OnRootMismatch + containers: + - name: qinglong + image: whyour/qinglong:debian + imagePullPolicy: IfNotPresent + env: + - name: QlBaseUrl + value: "/" + - name: TZ + value: Asia/Shanghai + ports: + - name: http + containerPort: 5700 + readinessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 6 + livenessProbe: + httpGet: + path: /api/health + port: http + initialDelaySeconds: 60 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 6 + startupProbe: + httpGet: + path: /api/health + port: http + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 60 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + memory: 1Gi + volumeMounts: + - name: data + mountPath: /ql/data + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi diff --git a/deploy/kubernetes/overlays/example/kustomization.yaml b/deploy/kubernetes/overlays/example/kustomization.yaml new file mode 100644 index 00000000..9114c03e --- /dev/null +++ b/deploy/kubernetes/overlays/example/kustomization.yaml @@ -0,0 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - ../../base + +images: + - name: whyour/qinglong + newName: registry.example.com/whyour/qinglong + newTag: debian