Service
  • 21 Mar 2024
  • 7 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Service

  • Dark
    Light
  • PDF

Article summary

A service represents a single data plane proxy. Often this is attached to a deployed application in a sidecar pattern, but it can also standalone, as in the case for edges. Services are denoted by the #Service definition.

Service File

Services are stored in a “service file”. Although services can be written entirely by hand, we highly recommend generating a service with the CLI init command.

Example Service

Here is an example of a service configured for mTLS ingress and mTLS health checks.

// declare the package -- typically the project/namespace name
package tenant

// Import the GSL schemas as alias 'gsl' and import the globals package from the current project
import (
	gsl "greymatter.io/gsl/spec/v1"
	"tenant.module/greymatter:globals"
)

Myapi: gsl.#Service & {
    // Boilerplate used for internal processing
	context: Myapi.#NewContext & globals

	name:          "myApi"
	display_name:  "My API"
	version:       "v2.0.2"
	description:   "An API that is mine"
	api_endpoint:              "https://\(context.globals.edge_host)/services/\(context.globals.namespace)/\(name)/"
	api_spec_endpoint:         "https://\(context.globals.edge_host)/services/\(context.globals.namespace)/\(name)/"
	business_impact:           "low"
	owner: "Tenant"

	health_options: {
		spire: gsl.#SpireUpstream
	}

	ingress: {
		"service-info": gsl.#ServiceInfo
        
        // (name) means replace this parenthesis with the value of name, in this case "myApi".
        // this is the main listener.
		(name): {
            
            // Using two mixins, one to set the protocol and the other to set security.
			gsl.#HTTPListener
			gsl.#MTLSListener
			
			routes: {
                // route everything matching the prefix "/"
				"/": {
					upstreams: {
                        // arbitrary upstream name since we provide a static IP and port.
						"local": {
							gsl.#Upstream
							
							instances: [
								{
									host: "127.0.0.1"
									port: 443
								},
							]
						}
					}
				}
			}
		}
	}

    // Connects the tenant edge to this service
	edge: {
		edge_name: "edge"
        // match services/<tenant namespace>/<service name>
        // Note that \(variable) syntax means substitute the variable into the string.
		routes: "/services/\(context.globals.namespace)/\(name)": {
			prefix_rewrite: "/"
            // This upstream name is NOT arbitrary. It is set to the service name which means
            // the IP and port will be dynamically discovered.
			upstreams: (name): {
				gsl.#Upstream
				namespace: context.globals.namespace
                // set mTLS on the upstream. 
				gsl.#MTLSUpstream
			}
		}
	}
}

// All services must be "exported". This syntax means: append a new key-value to the exports struct, 
// where the key is "myApi" (arbitrary name) and the value is the service definition defined above.
exports: "myApi": Myapi

Exports

For Sync to register a service file, they must be “exported”. To export a service, simply add it to the exports struct:

exports: "myApi": Myapi

Typically this line exists at the bottom of the file and is auto-generated. The keys of the struct do not matter only the values.

To temporarily delete a service (for debugging or other purposes), you can comment out that line. Sync will treat it as deleted.

Main listener

The main listener is a special listener on a service denoted by the listener name matching the service name. Every service must have a main listener. Main listeners are unique from other listeners because:

  1. It runs the metrics filter on its traffic

  2. It handles all service discovery traffic

Since the main listener runs the metrics filter by default, only routes on that listener are reported.

When using service discovery between two data plane proxies, traffic can only flow from main listener to main listener. Statically defined endpoints do not have this limitation.

Main listeners also listen on the defaulted proxy port 10908. You cannot override this port without overriding all ports for all services. Thus, you should seek an alternative to changing the default port if conflicts arise.

Schema

#Service: {
	name:                      string
	namespace:                 string
	mesh_id:                   string
	service_id:                string
	api_endpoint?:             string
	api_spec_endpoint?:        string
	description?:              string
	enable_instance_metrics:   bool 
	enable_historical_metrics: bool
	business_impact:           string
	version?:                  string
	owner?:                    string
	owner_url?:                string
	capability?:               string
	runtime?:                  string
	documentation?:            string
	prometheus_job:            string 
	external_links?:           [...]
	display_name?:             string
	upgrades?:                 string
	health_options:            {...}
	ingress?:                  {...}
	egress?:                   {...}
	raw_upstreams:             {...}
	edge?:                     {...}
}

Schema Definitions

Field

Type

Description

name

string

Required

The name of the service. Must match the name of the Kubernetes workload, otherwise the configuration will not propagate to the injected data plane proxy. Must also be unique within a GSL project (namespace).

namespace

string

Preset

The GSL project name. This should map to the namespace the service gets deployed into. Populated from the project globals file.

mesh_id

string

Preset

The name of the mesh the service will get deployed into. Populated from the project globals file.

service_id

string

Preset

The id of the service used by Catalog for health check associations. Must match the name of the service.

api_endpoint

string

Optional

Catalog metadata field containing a URL to the service. HTTP-only. This value is displayed on the Dashboard card as a link.

api_spec_endpoint

string

Optional

Catalog metadata field containing a URL to the service’s OpenAPI specification. HTTP-only. Will also allow the Dashboard to display the API specification.

description

string

Optional

A description of the service to display on the Dashboard service card.

enable_instance_metrics

bool

Deprecated

Enables the metrics to be displayed by the Dashboard in the Instance View.

enable_historical_metrics

bool

Deprecated

enables the aggregate metrics view in the Dashboard. Only available for HTTP services.

business_impact

string

Optional

Catalog metadata field representing the impact to the business the service has.

version

string

Optional

Catalog metadata field containing the version of the deployed service.

owner

string

Optional

Catalog metadata field containing the owner (the one responsible for) of the service.

owner_url

string

Optional

Catalog metadata field. Contains the URL to the owner’s page.

capability

string

Optional

Catalog metadata field containing the capability of the service.

runtime

string

Optional

Catalog metadata field containing the runtime of the service.

documentation

string

Optional

Catalog metadata field.

prometheus_job

string

Preset

The job id used by Prometheus metrics scrapping. This value should never be changed.

external_links

array

Optional

An arbitrary list of links relevant to the service, used to link out to Grafana, Tracing tools, Kibana and other 3rd party capabilities for DevOps Administrators.

display_name

string

Optional

A Catalog metadata field containing a human-friendly name for the service. This value is used for the Dashboard card.

upgrades

string

Optional

Controls proxy-wide websocket support. Possible values: websocket

health_options

struct

Optional

Controls the options related to the service’s health checking subsystem.

ingress

map

Required

A map of ingress names to ingresses. An ingress is a listener which accepts traffic originating from outside the proxy’s host machine. The ingress names should be descriptive but are ultimately arbitrary with one exception. The ingress named after the service name is reserved for the main listener.

egress

map

Optional

A map of egress names to egresses. An egress is a listener which accepts traffic originating from inside the proxy’s host machine. Egress names should be descriptive but are ultimately arbitrary.

raw_upstreams

map

Optional

A map of upstream names to upstreams. A “raw” upstream is divorced from a route and listener. It exists within the proxy’s upstream registry typically to provide filters with upstream connection information. For example, the ExternalAuthzFilter connects to the external authorization service directly through an upstream rather than tunneling through a defined listener.

edge

struct

Optional

The routing configurations lining the tenant edge with the service.

ExternalLink

Field

Type

Description

title

string

Required

Name of the link to display.

url

string

Required

URL of the link.

{
  title: string
  url: string
}

HealthOptions

Health checks are collected by Catalog and used to display health information in the dashboard. TLS configurations that match the overall mesh’s security policy must be configured here otherwise the service will fail to connect to the health subsystem.

Below are the TLS configurations for connecting to the health checking subsystem. These are required if the mesh is configured for TLS. Mix-ins conforming to the UpstreamTLSSchema definition are:

Field

Type

Description

tls

struct

Possible inputs include: TLSUpstream & MTLSUpstream

spire

Spire configurations for connecting to the health checking subsystem. Required if the mesh is configured for Spire based TLS.

secrets

Secrets (if any) used by the metrics filter. MetricsSecrets.

health_options: {
		tls: gsl.#MTLSUpstream
}

Ingress

Ingresses are listeners specifically configured for incoming traffic. Mix-ins that implement the ingress schema include:

  • #HTTPListener

  • #TCPListener

  • #UDPListener

Each listener mix-in enables that protocol for the ingress. They are mutually exclusive. Available options change depending on the features of the protocol’s listener.

Egress

Egresses are listeners specifically configured for outgoing traffic. Otherwise, they follow the same schemas as ingresses.

Edge

Each service contains an block for the route between an edge and the service. Although this configuration is applied to the edge and can be written at the edge, for convenience and coordination of configuration, it also exists at the service level.

Non-HTTP Edge Routing

The service’s edge configuration is setup to centralize all traffic through a single listener. HTTP routing allows for requests to get directed appropriately. This behavior cannot be replicated with non-HTTP traffic. TCP and UDP services that are accessible from the edge must operator on a unique listener. The configuration for edge-to-service routing must also occur on the edge service file, not using the service definitions edge field. 

{
	edge_ingress: string
	edge_name:    string
	routes:       {...}
}

edge_ingress

(String, Preset). The name of the ingress listener that the route should handle traffic from. This is set by default to be the name of the edge. As a result, the default listener for the edge-to-service link is the main listener of the edge.

edge_name

(String, Mandatory). The service name of the edge.

routes

(map[string]Route, Mandatory). A map of route matches to upstreams. Follows the same format of an #HTTPListener routes map.


Was this article helpful?