How to write your first GNES YAML config

July 29, 2019 ยท View on GitHub

YAML is everywhere. This is pretty much your impression when first trying GNES. Understanding the YAML config is therefore extremely important to use GNES.

Essentially, GNES requires two types of YAML config:

All other YAML files, including the docker-compose YAML config and Kubernetes config generated from the GNES Board or gnes compose command are not a part of this tutorial. Interested readers are welcome to read their YAML specification respectively.

Table of Content

GNES-compose YAML specification

The GNES-compose YAML defines a high-level service topology behind the GNES app. It is designed for simplicity and clarity, allowing the user to quickly get started with GNES.

The code below shows a example GNES-compose YAML:

port: 5566
services:
- name: Preprocessor
  yaml_path: text-prep.yml
- name: Encoder
  yaml_path: gpt2.yml
- name: Indexer
  yaml_path: b-indexer.yml

In this example, our GNES app is composed of three microservices: Preprocessor, Encoder and Indexer as a pipeline. Each component is associated with a component-wise YAML file by yaml_path. All accepted arguments are listed below.

ArgumentTypeDescription
namestrthe name of this GNES composition, default GNES app
portintthe communication port with gRPCFrontend, where the GNES app use for sending/receiving message to/from outside, default 8800
services(nested) listthe list of components, see below for details
volumesmap/dictvolume mapping used in Docker-Compose/Swarm mode, default {}
networksmap/dictnetwork mapping used in Docker-Compose/Swarm mode, default {gnes-net: {driver: overlay, attachable: true}}

services specification

For each service defined in services, it accepts the following arguments.

ArgumentTypeDescription
namestrchoose from [Preprocessor, Encoder, Indexer]
yaml_pathstrthe absolute/relative path of the component-wise YAML config, default None
replicasintthe number of replica services you want to start, default 1
incomestrchoose from [pull, sub]. Only applicable when replicas>1, where pull means competing for the income data and only one replica read the data; sub is like a broadcast, all replicas receive the same data, default pull

Sequential and parallel services

When specifying services, please kindly note the difference between a list and a nested list:

SequentialParallel
services:
- name: Preprocessor
  yaml_path: text-prep.yml
- name: Encoder
  yaml_path: gpt2.yml
- name: Indexer
  yaml_path: faiss-indexer.yml
- name: Indexer
  yaml_path: fulltext-indexer.yml
   
services:
- name: Preprocessor
  yaml_path: text-prep.yml
- name: Encoder
  yaml_path: gpt2.yml
- 
  - name: Indexer
    yaml_path: faiss-indexer.yml
  - name: Indexer
    yaml_path: fulltext-indexer.yml
GNES workflow of example 1 GNES workflow of example 2

In the parallel mode, two heterogeneous indexers receive the incoming data from the last encoder in a simultaneous manner. This is particular useful when you have multiple heterogeneous models (e.g. VGG and inception, or Faiss indexer and key-value database), you don't want to wait for one after another as their executions are essentially independent.

You may also scale up the parallel mode by setting replicas, e.g.

services:
- name: Preprocessor
  yaml_path: text-prep.yml
- name: Encoder
  yaml_path: gpt2.yml
- 
  - name: Indexer
    yaml_path: faiss-indexer.yml
    replicas: 2
  - name: Indexer
    yaml_path: fulltext-indexer.yml
    replicas: 3

which results a topology like the following:

gRPCFrontend and Router, why are they in my graph?

Careful readers may notice that gRPCFrontend and Router components may be added to the workflow graph, even though they are not defined in the YAML file. Here is the explanation:

  • gRPCFrontend serves as the only interface between GNES and the outside. All data must be sent to it and all results will be returned from it, which likes a hole on the black-box. Its data-flow pattern and the role it's playing in GNES is so deterministic that we don't even want to bother users to define it.
  • Put simply, Router forwards messages. It is often required when replicas > 1. However, the behavior of a router depends on the topology and the runtime (i.e. training, indexing and querying). Sometimes it serves as a mapper, other times it serves as a reducer or an aggregator, or even not required. In general, it might not be very straightforward for beginners to choose the right router. Fortunately, the type of the router can often be determined by the two consecutive layers, which is exactly what GNES Board (gnes compose) does.

What's Next?

The GNES-compose YAML describes a high-level picture of the GNES topology. Having it only is not enough. The detailed specification of each component is defined in yaml_path respectively, namely the component-wise YAML config. In the next tutorial, you will learn how to write a component-wise YAML config.