Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate Envoy stats from Ambassador stats. #14

Merged
merged 2 commits into from
Apr 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
Ambassador
==========

Ambassador is an overseer for Envoy in Kubernetes deployments. You can tell it about new services you're creating and deleting, and it will update Envoy's configuration to match.
Ambassador is a tool for easily and flexibly mapping public URLs to services running inside a Kubernetes cluster. Think of it as a simple way to spin up an API gateway for Kubernetes.

Ambassador is ALPHA SOFTWARE. In particular, at present it does not include an authentication mechanism, and it updates Envoy's configuration only with a specific request to do so -- be aware!
Under the hood, Ambassador uses [Envoy](https://lyft.github.io/envoy/) for the heavy lifting. You needn't understand anything about how Envoy works to use Ambassador, however.

CAVEATS
-------

Ambassador is ALPHA SOFTWARE. In particular, in version 0.3.1:

- There is no authentication mechanism, so anyone can bring services up or down.
- There is no SSL support.
- Ambassador updates Envoy's configuration only with a specific request to do so.

Ambassador is under active development; check frequently for updates, and please file issues for things you'd like to see!

Running Ambassador
==================
Expand Down Expand Up @@ -77,7 +88,7 @@ will do a health check;
curl $AMBASSADORURL/ambassador/services
```

will get a list of all user-defined services Ambassador knows about;
will get a list of all the Ambassador knows how to map;

```
curl -XPOST -H "Content-Type: application/json" \
Expand All @@ -99,3 +110,17 @@ curl -XPUT $AMBASSADORURL/ambassador/services

will update Envoy's configuration to match the currently-defined set of services.

Finally:

```
curl $AMBASSADOR/ambassador/stats
```

will return a JSON dictionary of statistics about resources that Ambassador presently has mapped. Most notably, the `services` dictionary lets you know basic health information about the services to which Ambassador is providing access:

- `services.$service.healthy_members` is the number of healthy back-end systems providing the service;
- `services.$service.upstream_ok` is the number of requests to the service that have succeeded; and
- `services.$service.upstream_bad` is the number of requests to the service that have failed.



38 changes: 25 additions & 13 deletions ambassador/envoy.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,16 @@ def __init__(self):
"last_update": 0,
"last_attempt": 0,
"update_errors": 0,
"services": {},
"envoy": {}
}

def update(self, active_service_names):
self.stats['last_attempt'] = time.time()
# Remember how many update errors we had before...
update_errors = self.stats['update_errors']

# ...and remember when we started.
last_attempt = time.time()

r = requests.get("http://127.0.0.1:8001/stats")

Expand All @@ -155,7 +161,9 @@ def update(self, active_service_names):
self.stats['update_errors'] += 1
return

new_dict = {}
# Parse stats into a hierarchy.

envoy_stats = {}

for line in r.text.split("\n"):
if not line:
Expand All @@ -165,7 +173,7 @@ def update(self, active_service_names):
key, value = line.split(":")
keypath = key.split('.')

node = new_dict
node = envoy_stats

for key in keypath[:-1]:
if key not in node:
Expand All @@ -175,23 +183,18 @@ def update(self, active_service_names):

node[keypath[-1]] = int(value.strip())

new_dict['last_attempt'] = self.stats['last_attempt']
new_dict['update_errors'] = self.stats['update_errors']
new_dict['last_update'] = time.time()

self.stats = new_dict
# Now dig into clusters a bit more.

active_services = {}

# Now dig into clusters a bit more.
if "cluster" in self.stats:
if "cluster" in envoy_stats:
active_service_map = {
x + '_cluster': x
for x in active_service_names
}

for cluster_name in self.stats['cluster']:
cluster = self.stats['cluster'][cluster_name]
for cluster_name in envoy_stats['cluster']:
cluster = envoy_stats['cluster'][cluster_name]

if cluster_name in active_service_map:
service_name = active_service_map[cluster_name]
Expand Down Expand Up @@ -227,4 +230,13 @@ def update(self, active_service_names):
'upstream_bad': upstream_bad
}

self.stats['services'] = active_services
# OK, we're now officially finished with all the hard stuff.
last_update = time.time()

self.stats = {
"last_update": last_update,
"last_attempt": last_attempt,
"update_errors": update_errors,
"services": active_services,
"envoy": envoy_stats
}