Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3.1.0] Major Version - PVC Rendering Fix #36

Merged
merged 1 commit into from
Jan 1, 2024

Conversation

lenaxia
Copy link

@lenaxia lenaxia commented Dec 31, 2023

This is a fix for PR #34 , which had broken PVC and deployment rendering due to improper context handling in the helm templates. Also fixed some whitespace issues.

Repeating the original comment here with a few additions that were left out last time:

Specifically:

  • the images tag is now broken down into repository and tag fields to enable better automation for upgrading container
  • persistence is now a top level field, moved out from under models to reflect the ability to define more volumes
  • arbitrary volumes can now be defined under persistence, and will be default mounted to /, but can also be mounted using a globalMount option. This also prevents the recurrence of issues like Image generation issue #32 which cannot be fixed without a helm chart update
  • initContainers and sidecarContainers allow creating or adding additional containers to do more work if desired
  • init and sidecar containers are now more robust to undefined parameters (e.g. if you do not define volumeMounts because you dont need any)
  • download-models initContainer now also ensures that /tmp/generated/images and /tmp/generated/audio (for tts) are created
  • download-models also now provides lock files when downloading so if run with multiple replicas with shared storage, only one replica will download each file at a time. This has the added benefit that downloads become parallelized across replicas.

The values.yaml has been updated, and I highlight the updates below:

deployment:
  image:
    repository: quay.io/go-skynet/local-ai  # Example: "docker.io/myapp"
    tag: latest 
    
# Models to download at runtime
models:
  # Whether to force download models even if they already exist
  forceDownload: false

  # The list of URLs to download models from
  # Note: the name of the file will be the name of the loaded model
  list:
  #  - url: "https://gpt4all.io/models/ggml-gpt4all-j.bin"
      # basicAuth: base64EncodedCredentials

initContainers: []
# Example:
# - name: my-init-container
#   image: my-init-image
#   imagePullPolicy: IfNotPresent
#   command: ["/bin/sh", "-c", "echo init"]
#   volumeMounts:
#     - name: my-volume
#       mountPath: /path/to/mount

sidecarContainers: []
# Example:
# - name: my-sidecar-container
#   image: my-sidecar-image
#   imagePullPolicy: IfNotPresent
#   ports:
#     - containerPort: 1234

# Persistent storage for models and prompt templates.
# PVC and HostPath are mutually exclusive. If both are enabled,
# PVC configuration takes precedence. If neither are enabled, ephemeral
# storage is used.
persistence:
  models: 
    enabled: true
    annotations: {}
    storageClass: hostPath
    accessModes: ReadWriteMany
    size: 10Gi
    globalMount: /models # if not defined, it will mount to /models
  output:
    enabled: true
    annotations: {}
    storageClass: hostPath
    accessModes: ReadWriteMany
    size: 5Gi
    globalMount: /tmp/generated

@lenaxia
Copy link
Author

lenaxia commented Dec 31, 2023

Output from the default values.yaml:

$:~/workspace/localai-helm-charts/charts(⎈|prod:test)$ helm template -f local-ai/values.yaml localai ./local-ai/ --debug
install.go:178: [debug] Original chart version: ""
install.go:195: [debug] CHART PATH: /home/ubuntu/workspace/localai-helm-charts/charts/local-ai

---
# Source: local-ai/templates/pvcs.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: localai-local-ai-models
  labels:
    app.kubernetes.io/instance: localai
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: local-ai
    app.kubernetes.io/version: "1.4"
    helm.sh/chart: local-ai-3.1.0
spec:
  accessModes:
    - "ReadWriteMany"
  resources:
    requests:
      storage: "10Gi"
  storageClassName: "hostPath"
---
# Source: local-ai/templates/pvcs.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: localai-local-ai-output
  labels:
    app.kubernetes.io/instance: localai
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: local-ai
    app.kubernetes.io/version: "1.4"
    helm.sh/chart: local-ai-3.1.0
spec:
  accessModes:
    - "ReadWriteMany"
  resources:
    requests:
      storage: "5Gi"
  storageClassName: "hostPath"
---
# Source: local-ai/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: localai-local-ai
  namespace: "test"
  labels:
    helm.sh/chart: local-ai-3.1.0
    app.kubernetes.io/name: local-ai
    app.kubernetes.io/instance: "localai"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/version: "1.4"
spec:
  selector:
    app.kubernetes.io/name: local-ai
  type: "ClusterIP"
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
      name: http
---
# Source: local-ai/templates/deployment.yaml
# yamllint disable rule:line-length

apiVersion: apps/v1
kind: Deployment
metadata:
  name: localai-local-ai
  namespace: "test"
  labels:
    helm.sh/chart: local-ai-3.1.0
    app.kubernetes.io/name: local-ai
    app.kubernetes.io/instance: "localai"
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/version: "1.4"
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: local-ai
      app.kubernetes.io/instance: localai
  replicas: 1
  template:
    metadata:
      name: localai-local-ai
      labels:
        app.kubernetes.io/name: local-ai
        app.kubernetes.io/instance: localai
      annotations:
    spec:
      initContainers:
        # Additional initContainers from values.yaml
        - name: download-model
          image: busybox
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c"]
          args:
            - |
              MODEL_DIR=/models
              FORCE_DOWNLOAD=false
              URLS=""
              LOCK_DIR=/tmp/model-download-locks

              mkdir -p "$MODEL_DIR"
              mkdir -p "$LOCK_DIR"
              mkdir -p "/tmp/generated/images"
              mkdir -p "/tmp/generated/audio"
              rm -rf "/models/lost+found"

              validate_url() {
                  local url=$1
                  local regex='^(https?|ftp):https://[a-zA-Z0-9.-]+(:[a-zA-Z0-9.-]+)?(/[a-zA-Z0-9.-]*)*$'
                  if [[ $url =~ $regex ]]; then
                      return 0 # URL is valid
                  else
                      return 1 # URL is invalid
                  fi
              }

              echo "List of URLs:"
              echo "$URLS"

              echo "$URLS" | awk -F, '{for (i=1; i<=NF; i++) print $i}' | while read -r line; do
                  url=$(echo "$line" | awk '{print $1}')
                  auth=$(echo "$line" | awk '{print $2}')
                  full_filename=$(basename "$url" .bin)
                  short_filename=$(echo "$full_filename" | cut -c1-20)
                  hash=$(echo "$full_filename" | sha256sum | cut -c1-12)
                  filename="${short_filename}_${hash}"
                  lockfile="$LOCK_DIR/$filename.lock"

                  # Validate URL
                  if ! validate_url "$url"; then
                      echo "Invalid URL: $url. Skipping download."
                      continue
                  fi

                  if [ -e "$MODEL_DIR/$filename" ]; then
                      echo "File $filename already exists. Skipping download."
                      continue
                  fi

                  if [ -e "$lockfile" ]; then
                      echo "Another pod is downloading $filename. Waiting for download to complete."
                      while [ -e "$lockfile" ]; do sleep 1; done
                      continue
                  fi

                  touch "$lockfile"

                  echo "Downloading $filename"
                  if [ -n "$auth" ]; then
                      wget --header "Authorization: Basic $auth" "$url" -O "$MODEL_DIR/$filename"
                  else
                      wget "$url" -O "$MODEL_DIR/$filename"
                  fi

                  if [ "$?" -ne 0 ]; then
                      echo "Download failed."
                      rm -f "$lockfile"
                      exit 1
                  else
                      echo "Download completed."
                      rm -f "$lockfile"
                  fi
              done

          volumeMounts:
            - name: models
              mountPath: /models
            - name: output
              mountPath: /tmp/generated

      containers:
        # Sidecar containers from values.yaml
        - name: localai-local-ai
          image: "quay.io/go-skynet/local-ai:latest"
          imagePullPolicy: IfNotPresent
          resources:
            {}
          env:
            - name: CONTEXT_SIZE
              value: "512"
            - name: THREADS
              value: "4"
            - name: MODELS_PATH
              value: /models
          volumeMounts:
            - name: models
              mountPath: /models
            - name: output
              mountPath: /tmp/generated
      volumes:
        - name: models
          persistentVolumeClaim:
            claimName: localai-local-ai-models
        - name: output
          persistentVolumeClaim:
            claimName: localai-local-ai-output
        - name: prompt-templates
          configMap:
            name: localai-local-ai-prompt-templates

Copy link
Member

@mudler mudler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's great, thanks @lenaxia for keep going with this!

@mudler mudler merged commit 6a3e8b1 into go-skynet:main Jan 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants