Back to Emissary

README

pkg/ambex/README.md

4.0.15.6 KB
Original Source

Ambex: Ambassador Experimental^H^H^H^H^H^H^H^H^H^H^H^H ADS service

Ambassador prior to v0.50.0 worked 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-plane1 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 (Flynn) 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 on disk:

    • it ignores files that start with a . (hidden files)
    • it interprets *.json files as JSON-encoded protobuf
    • it interprets *.pb files as text-encoded protobuf
    • all other files are ignored As for when it loads those files:
    • 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. Be careful about updating files atomically if you use this!

Running Ambex

You'll need the Go toolchain, and will want to have a functioning envoy.

Then, you can run the ambex CLI using busyambassador:

shell
go run github.com/datawire/ambassador/cmd/busyambassador ambex ARGs...

If you're on a platform other than GNU/Linux, in order to have a functioning envoy, you may want to run all of this in the builder shell: make shell.

Try it out

You'll want to run both ambex and an instance of envoy with a boostrap config pointing at that `ambex.

  1. First, start the ambex:

    shell
    go run github.com/datawire/ambassador/cmd/busyambassador ambex ./example/ambex/
    

    or

    shell
    go run github.com/datawire/ambassador/cmd/busyambassador ambex --watch ./example/ambex/
    
  2. Second, in another shell, start the envoy:

    shell
    envoy -l debug -c ./example/envoy/bootstrap-ads.yaml
    

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

console
$ 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/ambex/ directory in order to play with more configurations and see them reload instantaneously (if you used the --watch flag), or when-triggered (if you didn't use the --watch flag; trigger a relead by signaling the process with killall -HUP ambex).

Clean up

Kill Ambex and Envoy with Ctrl-C.

Footnotes

  1. The Envoy go-control-plane usually refers to github.com/envoyproxy/go-control-plane, but we've "forked" it as github.com/datawire/ambassador/pkg/envoy-control-plane in order to build it against the protobufs for our patched Envoy.