ambex

package
v1.7.3-rc.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2020 License: Apache-2.0 Imports: 28 Imported by: 0

README

Ambex: Ambassador Experimental ADS service

Ambassador v1 works by writing out Envoy v1 JSON configuration files, then triggering a hot restart of Envoy. This works, but it has some unpleasant limitations:

  • Restarts take awhile, and as a result you can't change the configuration very quickly.
  • Restarts can drop connections (cf Envoy #2776; more on this later).
  • Envoy itself is deprecating the v1 configuration, and will only support v2 in a bit.

To get around these limitations, and generally go for a better experience all 'round, we want to switch to the so-called xDS model, in which Envoy's configuration is supplied by its various "Discovery Services": e.g. the CDS is the Cluster Discovery Service; the EDS is the Endpoint Discovery Service. For Ambassador, the Aggregated Discovery Service or ADS is the one we want to use -- basically, it brings the other services together under one aegis and lets you just tell Envoy "get everything dynamically."

However, the whole ADS thing is a bit of a pain:

  • Envoy makes a bidirectional gRPC stream to the ADS server.
  • The ADS then makes gRPC calls to the Envoy to feed the Envoy configuration elements, but:
  • The ADS has to carefully order things such that the configuration elements match what Envoy expects for consistency.

Rather than do all that logic by hand, we'll use the Envoy go-control-plane for the heavy lifting. This is also something of a pain, given that it's not well documented, but here's the deal:

  • The root of the world is a SnapshotCache:
    • import github.com/datawire/ambassador/pkg/envoy-control-plane/cache, then refer to cache.SnapshotCache.
    • A collection of internally consistent configuration objects is a Snapshot (cache.Snapshot).
    • Snapshots are collected in the SnapshotCache.
    • A given SnapshotCache can hold configurations for multiple Envoys, identified by the Envoy nodeID, which must be configured for the Envoy.
  • The SnapshotCache can only hold go-control-plane configuration objects, so you have to build these up to hand to the SnapshotCache.
  • The gRPC stuff is handled by a Server:
    • import github.com/datawire/ambassador/pkg/envoy-control-plane/server, then refer to server.Server.
    • Our runManagementServer function (largely ripped off from the go-control-plane tests) gets this running. It takes a SnapshotCache (cleverly called config for no reason I understand) and a standard Go gRPCServer as arguments.
    • ALL the gRPC madness is handled by the Server, with the assistance of the methods in its callback object.
  • Once the Server is running, Envoy can open a gRPC stream to it.
    • On connection, Envoy will get handed the most recent Snapshot that the Server's SnapshotCache knows about.
    • Whenever a newer Snapshot is added to the SnapshotCache, that Snapshot will get sent to the Envoy.
  • We manage the SnapshotCache by loading envoy configuration files from json or protobuf files on disk.
    • By default when we get a SIGHUP we reload the configuration.
    • When passed the -watch argument we reload whenever any file in the directory changes.

Running Ambex

You'll need the Go toolchain and Glide.

Linux

Run make build to build the ambex binary. Then in one window run

./bin_$(go env GOOS)_$(go env GOARCH)/ambex bootstrap_image/example

or

./bin_$(go env GOOS)_$(go env GOARCH)/ambex -watch bootstrap_image/example

to start the ADS server.

And in another window run

make run

to launch envoy with a bootstrap configuration that points to the locally running ambex. This uses Docker --net=host networking, which does not work on MacOS or Windows.

Everything in Docker

You can run everything in Docker. This works on MacOS too. In the first shell run Envoy:

make run_envoy

In a second shell run Ambex:

make run_ambex

Note that the run_ambex recipe assumes that make run_envoy has already been executed. It uses docker exec to launch Ambex in the same container.

Try it out

You should now be able to run some curls in (yet) another shell:

$ curl localhost:8080/hello
Hello ADS!!!

$ curl localhost:8080/get
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.54.0", 
    "X-Envoy-Expected-Rq-Timeout-Ms": "15000"
  }, 
  "origin": "72.74.69.156", 
  "url": "http://httpbin.org/get"
}

Edit and/or add more files to the example directory in order to play with more configurations and see them reload instantaneously.

Note that instantaneous reloads require the -watch flag; otherwise you can force a reload by signaling the process (killall -HUP ambex).

If you're running everything in Docker, you will need to edit/add files in /application/example in the container. You can do this directly in the container:

$ curl localhost:8080/hello
Hello ADS!!!

$ docker exec -it ambex-envoy sed -i "s/ADS/ADS World/" /application/example/listener.json

$ curl localhost:8080/hello
Hello ADS World!!!

For more serious in-container editing, you may want to start with something like this:

$ docker exec -it ambex-envoy bash
root@04d063adb905:/application# apt-get -qq update
root@04d063adb905:/application# apt-get -qq install vim > /dev/null
root@04d063adb905:/application# vim example/listener.json
...

You can also edit on the host machine and then copy:

$ curl localhost:8080/hello
Hello ADS World!!!

$ docker cp bootstrap_image/example ambex-envoy:/application/

$ curl localhost:8080/hello
Hello ADS!!!

Clean up

Kill Ambex and Envoy with Ctrl-C. Use make clean to clean up the filesystem and Docker images.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// Version is inserted at build using --ldflags -X
	Version = "-no-version-"
)

Functions

func Clone

func Clone(src proto.Message) proto.Message

func Main added in v1.3.0

func Main()

func MainContext added in v1.7.0

func MainContext(parent context.Context)

func Merge

func Merge(to, from proto.Message)

Types

type Hasher

type Hasher struct {
}

Hasher returns node ID as an ID

func (Hasher) ID

func (h Hasher) ID(node *core.Node) string

ID function

type Validatable

type Validatable interface {
	proto.Message
	Validate() error
}

Not sure if there is a better way to do this, but we cast to this so we can call the generated Validate method.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL