Vald Agent Standalone on Kubernetes

This article will show you how to deploy a standalone Vald Agent using Helm and run it on your Kubernetes cluster.

Overview

Vald is made up of multiple microservices. In the Get Started, you may use 4 kinds of components to deploy Vald. In this case, you use only 1 component, Vald Agent that is the core component for Vald named vald-agent-ngt, to deploy. The below image shows the architecture image of this case.

Notice: Using only Vald Agent, the auto indexing function is not in use.

The 5 steps to Vald Agent Standalone on Kubernetes with Vald:

  1. Check and Satisfy the Requirements
  2. Prepare Kubernetes Cluster
  3. Deploy Vald Agent Standalone on Kubernetes cluster
  4. Run Example Code
  5. Cleanup

Requirements

  • Kubernetes: v1.19 ~
  • Go: v1.15 ~
  • Helm: v3 ~
  • libhdf5 (only required for get started)

Helm is used to deploying Vald on your Kubernetes and Hdf5 is used to decode the sample data file to run the example.
If Helm or HDF5 is not installed, please install Helm and HDF5.

Installation command for Helm
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
Installation command for HDF5
# yum
yum install -y hdf5-devel

# apt
apt-get install libhdf5-serial-dev

# homebrew
brew install hdf5

Prepare the Kubernetes Cluster

  1. Prepare Kubernetes cluster

    To complete get started, the Kubernetes cluster is required.
    Vald will run on Cloud Service such as GKE, AWS. In the sense of trying to “Get-Started”, k3d or kind are easy Kubernetes tools to use.

Deploy Vald Agent Standalone on Kubernetes Cluster

This chapter will show you how to deploy a standalone Vald Agent using Helm and run it on your Kubernetes cluster.
This chapter uses NGT as Vald Agent to perform vector insertion operation, indexing, and searching operations.

  1. Clone the repository

    To use the deployment yaml for deploy, let’s clone vdaas/vald repository.

    git clone https://github.com/vdaas/vald.git
    cd vald
    
  2. Deploy Vald Agent Standalone using Helm

    There is the values.yaml to deploy standalone Vald Agent. Each component can be disabled by setting the value false to the [component].enabled field. This is useful for deploying standalone Vald Agent NGT pods.

    helm repo add vald https://vald.vdaas.org/charts
    helm install vald-agent-ngt vald/vald --values example/helm/values-standalone-agent-ngt.yaml
    
  3. Verify

    kubectl get pods
    
    Example output
    If the deployment is successful, Vald Agent component should be running.
    NAME               READY   STATUS    RESTARTS   AGE
    vald-agent-ngt-0   1/1     Running   0          20m
    vald-agent-ngt-1   1/1     Running   0          20m
    vald-agent-ngt-2   1/1     Running   0          20m
    vald-agent-ngt-3   1/1     Running   0          20m
    

Run Example Code

  1. Port Forward

    At first, port-forward the vald-lb-gateway is required to make request from your local environment possible.

    kubectl port-forward service/vald-agent-ngt 8081:8081
    
  2. Download dataset

    Download fashion-mnist that is used as a dataset for indexing and search query.

    # move to working directory
    cd example/client/agent
    
    # download fashion-mnist testing dataset
    wget http://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5
    
  3. Run Example

    We use example/client/agent/main.go to run the example.
    This example will insert and index 400 vectors into the Vald from the fashion-mnist dataset via gRPC. And then after waiting for indexing, it will request for searching the nearest vector 10 times. You will get the 10 nearest neighbor vectors for each search query.
    Run example codes by executing the below command.

    # run example
    go run main.go
    
    The detailed explanation of example code is here
    This will execute 6 steps.
    1. init

      • Import packages

        example code
        package main
        
        import (
            "context"
            "encoding/json"
            "flag"
            "time"
        
            "github.com/kpango/fuid"
            "github.com/kpango/glg"
            agent "github.com/vdaas/vald-client-go/v1/agent/core"
            "github.com/vdaas/vald-client-go/v1/vald"
            "github.com/vdaas/vald-client-go/v1/payload"
        
            "gonum.org/v1/hdf5"
            "google.golang.org/grpc"
        )
        
      • Set variables

        • The constant number of training datasets and test datasets.

          example code
          const (
              insertCount = 400
              testCount = 20
          )
          
        • The variables for configuration.

          example code
          const (
              datasetPath         string
              grpcServerAddr      string
              indexingWaitSeconds uint
          )
          
      • Recognition parameters.

        example code
        func init() {
            flag.StringVar(&datasetPath, "path", "fashion-mnist-784-euclidean.hdf5", "set dataset path")
            flag.StringVar(&grpcServerAddr, "addr", "127.0.0.1:8081", "set gRPC server address")
            flag.UintVar(&indexingWaitSeconds, "wait", 60, "set indexing wait seconds")
            flag.Parse()
        }
        
    2. load

      • Loading from fashion-mnist dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success.

        example code
        ids, train, test, err := load(datasetPath)
        if err != nil {
            glg.Fatal(err)
        }
        
    3. Create the gRPC connection and Vald client with gRPC connection.

      example code
      ctx := context.Background()
      
      conn, err := grpc.DialContext(ctx, grpcServerAddr, grpc.WithInsecure())
      if err != nil {
          glg.Fatal(err)
      }
      
      client := agent.NewAgentClient(conn)
      
    4. Insert and Index

      • Insert and Indexing 400 training datasets to the Vald agent.

        example code
        for i := range ids [:insertCount] {
            if i%10 == 0 {
                glg.Infof("Inserted %d", i)
            }
            _, err := client.Insert(ctx, &payload.Insert_Request{
                Vector: &payload.Object_Vector{
                    Id: ids[i],
                    Vector: train[i],
                },
                Config: &payload.Insert_Config{
                    SkipStrictExistCheck: true,
                },
            })
            if err != nil {
                glg.Fatal(err)
            }
        }
        
      • Wait until indexing finish.

        example code
        wt := time.Duration(indexingWaitSeconds) * time.Second
        glg.Infof("Wait %s for indexing to finish", wt)
        time.Sleep(wt)
        
      • [Optional] Indexing manually instead of waiting for auto indexing You can set Agent NGT configuration auto_index_duration_limit and auto_index_check_duration for auto indexing. In this example, you can create index manually using CreateAndSaveIndex() method in the client library

        example code
        _, err = client.CreateAndSaveIndex(ctx, &payload.Control_CreateIndexRequest{
            PoolSize: uint32(insertCount),
        })
        if err != nil {
            glg.Fatal(err)
        }
        
    5. Search

      • Search 10 neighbor vectors for each 20 test datasets and return a list of neighbor vectors.

      • When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC.

        example code
        glg.Infof("Start search %d times", testCount)
        for i, vec := range test[:testCount] {
            res, err := client.Search(ctx, &payload.Search_Request){
                Vector: vec,
                Config: &payload.Search_Config{
                    Num: 10,
                    Radius: -1,
                    Epsilon: 0.1,
                }
            }
            if err != nil {
                glg.Fatal(err)
            }
        
            b, _ := json.MarshalIndent(res.GetResults(), "", " ")
            glg.Infof("%d - Results : %s\n\n", i+1, string(b))
            time.Sleep(1 * time.Second)
        }
        
    6. Remove

      • Remove indexed 400 training datasets from the Vald agent.

        example code
        for i := range ids [:insertCount] {
            _, err := client.Remove(ctx, &payload.Remove_Request{
                Id: &payload.Object_ID{
                    Id: ids[i],
                },
            })
            if err != nil {
                glg.Fatal(err)
            }
            if i%10 == 0 {
                glg.Infof("Removed %d", i)
            }
        }
        
      • Remove from the index manually instead of waiting for auto indexing. The removed vectors still exist in the NGT graph index before the SaveIndex (or CreateAndSaveIndex) API is called. If you run the below code, the indexes will be removed completely from the Vald Agent NGT graph and the Backup file.

        example code
        _, err = client.SaveIndex(ctx, &payload.Empty{})
        if err != nil {
            glg.Fatal(err)
        }
        

Cleanup

In the last, you can remove all deployed Vald pods by executing the below command.

helm uninstall vald-agent-ngt

Congratulation! You achieved this tutorial!

For more information, we recommend you to check: