diff --git a/.bookignore b/.bookignore new file mode 100644 index 0000000000..1646affdc1 --- /dev/null +++ b/.bookignore @@ -0,0 +1,3 @@ +README.md +package*.json +build-website.sh diff --git a/.markdownlintrc b/.markdownlintrc new file mode 100644 index 0000000000..ce4ea08f4f --- /dev/null +++ b/.markdownlintrc @@ -0,0 +1,6 @@ +{ + "no-trailing-spaces": { "br_spaces": 2 }, + "no-multiple-blanks": false, + "line-length": false, + "ol-prefix": { "style": "ordered" } +} diff --git a/INDEX.md b/INDEX.md new file mode 100644 index 0000000000..fb1919f417 --- /dev/null +++ b/INDEX.md @@ -0,0 +1,3 @@ +# GitBook Index Page + +This page is overwritten by the documentation build process. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..9eb5a88cd4 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Ambassador documentation + +We've switched to GatsbyJS for generating the documentation, which gives us more control and flexibility over the layout. + +## Authoring documentation + +If you're authoring the documentation, just edit the Markdown files. You can use GitHub to preview the Markdown. + +## Documentation infrastructure notes + +* The rendered YAML and markdown files are copied by Travis CI to a separate Gatsby-based toolchain. Still TODO is to provide a local version of this toolchain. +* The `doc-links.yml` file is the new TOC. \ No newline at end of file diff --git a/_layouts/footer.html b/_layouts/footer.html new file mode 100644 index 0000000000..ed081e5647 --- /dev/null +++ b/_layouts/footer.html @@ -0,0 +1,5 @@ + + +
+ +join our Slack chat diff --git a/_layouts/website/page.html b/_layouts/website/page.html new file mode 100644 index 0000000000..2886daf8ea --- /dev/null +++ b/_layouts/website/page.html @@ -0,0 +1,21 @@ +{% extends template.self %} + +{% block book_sidebar %} + + + + +{{ super() }} +{% endblock %} + +{% block page %} + + ← Back to Datawire Open Source Documentation + +{{ super() }} +{% endblock %} + +{% block javascript %} + {{ super() }} + +{% endblock %} \ No newline at end of file diff --git a/_redirects b/_redirects new file mode 100644 index 0000000000..7650ac263f --- /dev/null +++ b/_redirects @@ -0,0 +1,10 @@ +# Redirects; parsed by Netlify +/how-to/grpc /user-guide/grpc +/how-to/tls-termination /user-guide/tls-termination +/how-to/statistics /reference/statistics +/how-to/auth-external /reference/services/auth-service +/user-guide/running /reference/running +/reference/auth-external /reference/services/auth-service +/reference/auth-tls-certs /reference/core/tls +/helm/* https://s3.amazonaws.com/datawire-static-files/ambassador/:splat +/index.yaml https://s3.amazonaws.com/datawire-static-files/ambassador/index.yaml diff --git a/about/alternatives.md b/about/alternatives.md new file mode 100644 index 0000000000..720d8ddb3d --- /dev/null +++ b/about/alternatives.md @@ -0,0 +1,19 @@ +# Alternatives to Ambassador + +Alternatives to Ambassador fall in three basic categories. + +* Hosted API gateways, such as the [Amazon API gateway](https://aws.amazon.com/api-gateway/). +* Traditional API gateways, such as [Kong](https://getkong.org/). +* L7 proxies, such as [Traefik](https://traefik.io/), [NGINX](http://nginx.org/), [HAProxy](http://www.haproxy.org/), or [Envoy](https://www.envoyproxy.io), or Ingress controllers built on these proxies. + +Both hosted API gateways and traditional API gateways are: + +* Not self-service. The management interfaces on traditional API gateways are not designed for developer self-service, and provide limited safety and usability for developers. +* Not Kubernetes-native. They're typically configured using REST APIs, making it challenging to adopt cloud-native patterns such as GitOps and declarative configuration. +* [Designed for API management, versus microservices](/about/microservices-api-gateways). + +A Layer 7 proxy can be used as an API gateway, but typically requires additional bespoke development to support microservices use cases. In fact, many API gateways package the additional features needed for an API gateway on top of a L7 proxy. Ambassador uses Envoy, while Kong uses NGINX. If you're interested in deploying Envoy directly, we've written an [introductory tutorial](https://www.datawire.io/guide/traffic/getting-started-lyft-envoy-microservices-resilience/). + +## Istio + +[Istio](https://istio.io) is an open source service mesh, built on Envoy. A service mesh is designed to manage east/west traffic, while an API gateway manages north/south traffic. Documentation on how to deploy Ambassador with Istio is [here](/user-guide/with-istio). In general, we've found that north/south traffic is quite different from east/west traffic (i.e., you don't control the client in the North/South use case). diff --git a/about/features-and-benefits.md b/about/features-and-benefits.md new file mode 100644 index 0000000000..5885719af2 --- /dev/null +++ b/about/features-and-benefits.md @@ -0,0 +1,39 @@ +# Features and Benefits + +In cloud-native organizations, developers frequently take on responsibility for the full development lifecycle of a service, from development to QA to operations. Ambassador was especially designed for these organizations where developers have operational responsibility for their service(s). + +As such, Ambassador is designed to be used by both developers and operators. + +## Self-Service via Kubernetes Annotations + +Ambassador is built from the start to support _self-service_ deployments -- a developer working on a new service doesn't have to go to Operations to get their service added to the mesh, they can do it themselves in a matter of seconds. Likewise, a developer can remove their service from the mesh, or merge services, or separate services, as needed, at their convenience. All of these operations are performed via Kubernetes annotations, so it can easily integrate with your existing development workflow. + +## Flexible Canary Deployments + +Canary deployments are an essential component of cloud-native development workflows. In a canary deployment, a small percentage of production traffic is routed to a new version of a service to test it under real-world conditions. Ambassador allows developers to easily control and manage the amount of traffic routed to a given service through annotations. [This tutorial](https://www.datawire.io/faster/canary-workflow/) covers a complete canary workflow using Ambassador. + +## Kubernetes-Native Architecture + +Ambassador relies entirely on Kubernetes for reliability, availability, and scalability. For example, Ambassador persists all state in Kubernetes, instead of requiring a separate database. Scaling Ambassador is as simple as changing the replicas in your deployment, or using a [horizontal pod autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/). + +Ambassador uses [Envoy](https://www.envoyproxy.io) for all traffic routing and proxying. Envoy is a modern L7 proxy that is used in production at companies including Lyft, Apple, Google, and Stripe. + +## gRPC and HTTP/2 Support + +Ambassador fully supports gRPC and HTTP/2 routing, thanks to Envoy's extensive capabilities in this area. See [gRPC and Ambassador](/user-guide/grpc) for more information. + +## Istio Integration + +Ambassador integrates with the [Istio](https://istio.io) service mesh as the edge proxy. In this configuration, Ambassador routes external traffic to the internal Istio service mesh. See [Istio and Ambassador](/user-guide/with-istio) for details. + +## Authentication + +Ambassador supports authenticating incoming requests. When configured, Ambassador will check with a third party authentication service prior to routing an incoming request. For more information, see the [authentication tutorial](/user-guide/auth-tutorial). + +## Rate Limiting + +Ambassador supports rate limiting incoming requests. When configured, Ambassador will check with a third party rate limit service prior to routing an incoming request. For more information, see the [rate limiting tutorial](/user-guide/rate-limiting-tutorial). + +## Integrated Diagnostics + +Ambassador includes a diagnostics service so that you can quickly debug issues associated with configuring Ambassador. For more information, see [running Ambassador](https://www.getambassador.io/reference/running). diff --git a/about/microservices-api-gateways.md b/about/microservices-api-gateways.md new file mode 100644 index 0000000000..2b84f5cb9e --- /dev/null +++ b/about/microservices-api-gateways.md @@ -0,0 +1,60 @@ +# Microservices API Gateways vs. Traditional Enterprise API Gateways + +A microservices API gateway is an API gateway designed to accelerate the development workflow of independent services teams. A microservices API gateway provides all the functionality for a team to independently publish, monitor, and update a microservice. + +This focus on accelerating the development workflow is distinct from the purpose of traditional API gateways, which focus on the challenges of managing APIs. Over the past decade, organizations have worked to expose internal systems through well-defined APIs. The challenge of safely exposing hundreds or thousands of APIs to end users (both internal and external) led to the emergence of API gateways. Over time, API gateways have become centralized, mission critical pieces of infrastructure that control access to these APIs. + +In this article, we'll discuss how the difference in business objective (productivity vs management) results in a very different API gateway. + +## Microservices Organization + +In a microservices organization, small teams of developers work independently from each other to rapidly deliver functionality to the customer. In order for each service team to work independently, with a productive workflow, a services team needs to be able to: + +1. Publish their service, so that others can use the service +2. Monitor their service, to see how well it's working +3. Test and update their service, so they can keep on improving the service + +The team needs to all of this *without* requiring assistance from another operations or platform team--as soon as a services team requires another team, they're no longer working independently, and this can lead to bottlenecks. + +For service publication, a microservices API gateway provides a static address for consumers, and dynamically route requests to the appropriate service address. In addition, providing authentication and TLS termination for security are typical considerations in exposing a service to other consumers. + +Understanding the end user experience of a service is crucial to improving the service. For example, a software update could inadvertently impact the latency of certain requests. A microservices API gateway is well situated to collect key observability metrics on end user traffic as it routes traffic to the end service. + +A microservices API gateway also supports dynamically routing user requests to different service versions for canary testing. By routing a small fraction of end user requests to a new version of a service, service teams can safely test the impact of new updates to a small subset of users. + +## Microservices API Gateways vs. Enterprise API Gateways + +At first glance, the use case described above may be fulfilled with a enterprise-focused API gateway. While this may be true, the actual emphasis of enterprise API gateways and microservices API gateways are somewhat different: + +| Use case | API gateway | Microservices API gateway | +|---------------|-------------------|------------------------------| +| Primary Purpose | Expose, compose, and manage internal business APIs | Expose and observe internal business services | +| Publishing Functionality | API management team or service team registers / updates gateway via admin API | Service team registers / updates gateway via declarative code as part of the deployment process | +| Monitoring | Admin and operations focused e.g. meter API calls per consumer, report errors (e.g. internal 5XX). | Developer focused e.g. latency, traffic, errors, saturation | +| Handling and Debugging Issues | L7 error-handling (e.g. custom error page or payload). Run gateway/API with additional logging. Troubleshoot issue in staging environment | Configure more detailed monitoring. Enable traffic shadowing and / or canarying | +| Testing | Operate multiple environments for QA, Staging and Production. Automated integration testing, and gated API deployment. Use client-driven API versioning for compatibility and stability (e.g. semver) | Facilitate canary routing for dynamic testing (taking care with data mutation side effects). Use developer-driven service versioning for upgrade management | +| Local Development | Deploy gateway locally (via installation script, Vagrant or Docker), and attempt to mitigate infrastructure differences with production. Use language-specific gateway mocking and stubbing frameworks | Deploy gateway locally via service orchestration platform (e.g. Kubernetes) | + +## Self-Service Publishing + +A team needs to be able to publish a new service to customers without requiring an operations or API management team. This ability to self-service for deployment and publication enables the team to keep the feature release velocity high. While a traditional enterprise API gateway may provide a simple mechanism (e.g., REST API) for publishing a new service, in practice, the usage is often limited to the use of a dedicated team that is responsible for the gateway. The primary reason for limiting publication to a single team is to provide an additional (human) safety mechanism: an errant API call could have potentially disastrous effects on production. + +Microservices API gateways utilize mechanisms that enable service teams to easily *and* safely publish new services, with the inherent understanding that the producing team are responsible for their service, and will fix an issue if one occurs. A microservices gateway provides configurable monitoring for issue detection, and provides hooks for debugging, such as inspecting traffic or traffic shifting/duplication. + +## Monitoring & Rate Limiting + +A common business model for APIs is metering, where a consumer is charged different fees depending on API usage. Traditional enterprise API gateways excel in this use case: they provide functionality for monitoring per-client usage of an API, and the ability to limit usage when the client exceeds their quota. + +A microservice gateway also requires monitoring and rate limiting, but for different reasons. Monitoring user-visible metrics such as throughput, latency, and availability are important to ensure that new updates don't impact the end user. Robust end user metrics are critical to allowing rapid, incremental updates. Rate limiting is used to improve the overall resilience of a service. When a service is not responding as expected, an API gateway can throttle incoming requests to allow a service to recover and prevent a cascade failure. + +## Testing and Updates + +A microservices application has multiple services, each of which is being independently updated. Automated pre-production testing of a moving target is necessary but not sufficient for microservices. Canary testing, where a small percentage of production traffic is routed to a new service version, is an important tool to help test an update. By limiting a new service version to a small percentage of users, the impact of a service failure is limited. + +In a traditional enterprise API gateway, routing is used to isolate or compose/aggregate changing API versions. Automated pre-production testing and manual post-production verification and exploration is required. + +# Summary + +Traditional enterprise API gateways are designed to solve the challenges of API management. While they may appear to solve some of the challenges of adopting microservices, the reality is that a microservices workflow creates a different set of requirements. Integrating a microservices API gateway into your development workflow empowers service teams to self-publish, monitor, and update their service, quickly and safely. This will enable your organization to ship software more rapidly, and with more reliability than ever before. + +For further reading on how an API Gateway can accelerate continuous delivery, read [this blog post](https://blog.getambassador.io/continuous-delivery-how-can-an-api-gateway-help-or-hinder-1ff15224ec4d). diff --git a/about/quickstart.md b/about/quickstart.md new file mode 100644 index 0000000000..07e67bbc5b --- /dev/null +++ b/about/quickstart.md @@ -0,0 +1,59 @@ +# Five minute quickstart + +In this section, we'll get Ambassador running locally with a demo configuration. In the next section, we'll then walk through how to deploy Ambassador in Kubernetes with a custom configuration. + +## 1. Running the Demo Configuration + +By default, Ambassador uses a demo configuration to show some of its basic features. Get it running with Docker, and expose Ambassador on port 8080: + +```shell +docker run -it -p 8080:80 --name=ambassador --rm quay.io/datawire/ambassador:{VERSION} --demo +``` + +## 2. Ambassador's Diagnostics + +Ambassador provides live diagnostics viewable with a web browser. While this would normally not be exposed to the public network, the Docker demo publishes the diagnostics service at the following URL: + +`http://localhost:8080/ambassador/v0/diag/` + +Some of the most important information - your Ambassador version, how recently Ambassador's configuration was updated, and how recently Envoy last reported status to Ambassador - is right at the top. The diagnostics overview can show you what it sees in your configuration map, and which Envoy objects were created based on your configuration. + +## 3. The Quote of the Moment Service + +Since Ambassador is an API gateway, its primary purpose is to provide access to microservices. The demo is preconfigured with a mapping that connects the `/qotm/` resource to the "Quote of the Moment" service -- a demo service that supplies quotations. You can try it out here: + +```shell +curl http://localhost:8080/qotm/ +``` + +This request will route to the `qotm` service at `demo.getambassador.io`, and return a quote in a JSON object. + +You can also see the mapping by clicking the `mapping-qotm.yaml` link from the diagnostic overview, or by opening + +`http://localhost:8080/ambassador/v0/diag/mapping-qotm.yaml` + +## 4. Authentication + +On the diagnostic overview, you can also see that Ambassador is configured to do authentication -- click the `auth.yaml` link, or open + +`http://localhost:8080/ambassador/v0/diag/auth.yaml` + +for more here. Ambassador uses a demo authentication service at `demo.getambassador.io` to mediate access to the Quote of the Moment: simply getting a random quote is allowed without authentication, but to get a specific quote, you'll have to authenticate: + +```shell +curl -v http://localhost:8080/qotm/quote/5 +``` + +will return a 401, but + +```shell +curl -v -u username:password http://localhost:8080/qotm/quote/5 +``` + +will succeed. (Note that that's literally "username" and "password" -- the demo auth service is deliberately not very secure!) + +Note that it's up to the auth service to decide what needs authentication -- teaming Ambassador with an authentication service can be as flexible or strict as you need it to be. + +## Next steps + +We've just walked through some of the core features of Ambassador in a local configuration. Next, we'll walk through how to configure these features in Kubernetes. diff --git a/about/roadmap.md b/about/roadmap.md new file mode 100644 index 0000000000..bb4ef81a6c --- /dev/null +++ b/about/roadmap.md @@ -0,0 +1,10 @@ +# Roadmap + +Ambassador is an API gateway for microservices built on [Envoy](https://www.envoyproxy.io). Planned features for future releases include the following: + +* Enhanced diagnostics service +* Advanced integration with Istio +* Pushing the authentication filter into upstream Envoy +* Expanded tutorials on monitoring + +Ambassador is under continuous development. If you have any requests or suggestions for the roadmap, please join the [Slack chat](https://d6e.co/slack) or [file an issue](https://github.com/datawire/ambassador/issues). diff --git a/about/support.md b/about/support.md new file mode 100644 index 0000000000..f3a3f3cdc4 --- /dev/null +++ b/about/support.md @@ -0,0 +1,17 @@ +# Need help deploying Ambassador at your organization? +Our support team is here to help! + +## Community Support +[Join our Slack channel](http://d6e.co/slack) to talk with other users in the community and get your questions answered. If you can’t find an answer there, [contact us about a support contract](https://www.getambassador.io/contact). + +## Deployment Support +Ambassador can accelerate your migration to Kubernetes. Deployment support helps you with Ambassador and Kubernetes migration, before you move to production. + +## Production Support +We offer two types of production support contracts for users deploying Ambassador in production. + +We offer both business hour (8am - 5pm EST, M-F) and 24x7 Sev 1 support for Ambassador. 24x7 Sev 1 support includes custom hotfix support for production outages if necessary. + +## Pricing + +[Contact us](https://www.getambassador.io/contact) to learn how we can help, and for detailed pricing information. diff --git a/about/why-ambassador.md b/about/why-ambassador.md new file mode 100644 index 0000000000..199e495535 --- /dev/null +++ b/about/why-ambassador.md @@ -0,0 +1,34 @@ +# Why Ambassador? + +Ambassador is an open source, Kubernetes-native [microservices API gateway](/about/microservices-api-gateways) built on the [Envoy Proxy](https://www.envoyproxy.io). Ambassador is built from the ground up to support multiple, independent teams that need to rapidly publish, monitor, and update services for end users. Ambassador can also be used to handle the functions of a Kubernetes ingress controller and load balancer (for more, see [this blog post](https://blog.getambassador.io/kubernetes-ingress-nodeport-load-balancers-and-ingress-controllers-6e29f1c44f2d)). + +Ambassador is: + +* Self-service. Ambassador is designed so that developers can manage services directly. This requires a system that is not only easy for developers to use, but provides safety and protection against inadvertent operational issues. +* Operations friendly. Ambassador has virtually no moving parts, and delegates all routing and resilience to [Envoy Proxy](https://www.envoyproxy.io) and Kubernetes, respectively. Ambassador stores all state in Kubernetes (no database!). Multiple Ambassadors can be run in the same cluster, making upgrades easy and seamless. +* Designed for microservices. Ambassador integrates the features teams need for microservices, including authentication, rate limiting, observability, routing, TLS termination, and more. +* Open Source. Ambassador is an open source API Gateway. Install it now for free and join the community [Slack Channel](http://d6e.co/slack). + +For more background on the motivations of Ambassador, read [this blog post](https://blog.getambassador.io/building-ambassador-an-open-source-api-gateway-on-kubernetes-and-envoy-ed01ed520844). + + + + diff --git a/ambassador-0.30.1.tgz b/ambassador-0.30.1.tgz new file mode 100644 index 0000000000..ef771480c6 Binary files /dev/null and b/ambassador-0.30.1.tgz differ diff --git a/ambassador-0.30.2.tgz b/ambassador-0.30.2.tgz new file mode 100644 index 0000000000..a2d48c4281 Binary files /dev/null and b/ambassador-0.30.2.tgz differ diff --git a/ambassador-0.31.0.tgz b/ambassador-0.31.0.tgz new file mode 100644 index 0000000000..7dd176cc4d Binary files /dev/null and b/ambassador-0.31.0.tgz differ diff --git a/ambassador-0.32.2.tgz b/ambassador-0.32.2.tgz new file mode 100644 index 0000000000..6af97c2d82 Binary files /dev/null and b/ambassador-0.32.2.tgz differ diff --git a/ambassador-0.33.0.tgz b/ambassador-0.33.0.tgz new file mode 100644 index 0000000000..94e22eb00c Binary files /dev/null and b/ambassador-0.33.0.tgz differ diff --git a/ambassador-0.33.1.tgz b/ambassador-0.33.1.tgz new file mode 100644 index 0000000000..63635aa416 Binary files /dev/null and b/ambassador-0.33.1.tgz differ diff --git a/ambassador-0.34.0.tgz b/ambassador-0.34.0.tgz new file mode 100644 index 0000000000..1db2f14e26 Binary files /dev/null and b/ambassador-0.34.0.tgz differ diff --git a/ambassador-0.34.1.tgz b/ambassador-0.34.1.tgz new file mode 100644 index 0000000000..733d99a2c8 Binary files /dev/null and b/ambassador-0.34.1.tgz differ diff --git a/ambassador-0.34.2.tgz b/ambassador-0.34.2.tgz new file mode 100644 index 0000000000..62e9bc303e Binary files /dev/null and b/ambassador-0.34.2.tgz differ diff --git a/ambassador-0.34.3.tgz b/ambassador-0.34.3.tgz new file mode 100644 index 0000000000..a2c9a49956 Binary files /dev/null and b/ambassador-0.34.3.tgz differ diff --git a/ambassador-0.35.0.tgz b/ambassador-0.35.0.tgz new file mode 100644 index 0000000000..1d03acfbe2 Binary files /dev/null and b/ambassador-0.35.0.tgz differ diff --git a/ambassador-0.35.1.tgz b/ambassador-0.35.1.tgz new file mode 100644 index 0000000000..aab3976e72 Binary files /dev/null and b/ambassador-0.35.1.tgz differ diff --git a/ambassador-0.35.2.tgz b/ambassador-0.35.2.tgz new file mode 100644 index 0000000000..33829eab10 Binary files /dev/null and b/ambassador-0.35.2.tgz differ diff --git a/book.json b/book.json new file mode 100644 index 0000000000..865f914eb6 --- /dev/null +++ b/book.json @@ -0,0 +1,27 @@ +{ + "title": "Ambassador: open source API Gateway for microservices and Kubernetes", + "root": ".", + "structure": { + "readme": "INDEX.md" + }, + "plugins": [ + "gtm", + "anchorjs" + ], + "pluginsConfig": { + "layout": { + "footerPath": "layouts/footer.html" + }, + "gtm": { + "token": "GTM-TPMQ838", + "virtualPageViews": true + }, + "anchorjs": { + "selector": "h2,h3,h4,h5", + "placement": "right", + "visible": "always", + "truncate": 64, + "ariaLabel": "Anchor" + } + } +} diff --git a/build-website.sh b/build-website.sh new file mode 100755 index 0000000000..187565da25 --- /dev/null +++ b/build-website.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' +# http://redsymbol.net/articles/unofficial-bash-strict-mode/ +# because I don't know what I'm doing in Bash + +set -x + +# Build the documentation as usual +cd "$(dirname "$0")" +npm install +npm run build + +# Remove the data-path attributed of every list item linking to index.html, +# which are the ones marked with data-level="1.1". This causes the GitBook +# scripts to redirect to the index page rather fetching and replacing just +# the content area, as they do for proper GitBook-generated pages. + +perl -pi \ + -e "s/{VERSION}/$VERSION/g;" \ + -e 's,
  • ,
  • ,g;' \ + $(find _book -name '*.html') _book/search_index.json + +# Replace index.html with our hand-crafted landing page... +cp index.html _book/ +cp features.html _book/ + +# ...and make sure that the version is correct in the index. +perl -pi -e "s/{VERSION}/$VERSION/g;" _book/index.html + +# Copy YAML into _book/ as well. +cp -prv yaml _book/ + diff --git a/concepts/architecture.md b/concepts/architecture.md new file mode 100644 index 0000000000..29416417a9 --- /dev/null +++ b/concepts/architecture.md @@ -0,0 +1,25 @@ +# Ambassador Architecture + +## Ambassador is a control plane + +Ambassador is a specialized [control plane for Envoy Proxy](https://blog.getambassador.io/the-importance-of-control-planes-with-service-meshes-and-front-proxies-665f90c80b3d). In this architecture, Ambassador translates configuration (in the form of Kubernetes annotations) to Envoy configuration. All actual traffic is directly handled by the high-performance [Envoy Proxy](https://www.envoyproxy.io). + +![Architecture](/images/ambassador-arch.png) + +## Details + +When a user applies a Kubernetes manifest containing Ambassador annotations, the following steps occur: + +1. Ambassador is asynchronously notified by the Kubernetes API of the change. +2. Ambassador translates the configuration into an abstract intermediate representation (IR). +3. An Envoy configuration file is generated from the IR. +4. The Envoy configuration file is validated by Ambassador (using Envoy in validation mode). +5. Assuming the file is valid configuration, Ambassador uses Envoy's [hot restart mechanism](https://blog.envoyproxy.io/envoy-hot-restart-1d16b14555b5) to deploy the new configuration and properly drain connections. + +## Scaling and availability + +Ambassador relies on Kubernetes for scaling, high availability, and persistence. All Ambassador configuration is stored directly in Kubernetes; there is no database. Ambassador is packaged as a single container that contains both the control plane and an Envoy Proxy instance. By default, Ambassador is deployed as a Kubernetes `deployment` and can be scaled and managed like any other Kubernetes deployment. + +## Envoy Proxy + +Ambassador closely tracks Envoy Proxy releases. A stable branch of Envoy Proxy is maintained that enables the team to cherry pick specific fixes into Ambassador. \ No newline at end of file diff --git a/concepts/developers.md b/concepts/developers.md new file mode 100644 index 0000000000..b75777e6eb --- /dev/null +++ b/concepts/developers.md @@ -0,0 +1,96 @@ +# Self-Service Routing and Deployment Control + +Traditionally, API Gateways have focused on operators as the primary user. Ambassador considers both developers and operators to be first-class citizens. + +## Decentralized, declarative configuration + +The Ambassador model is a decentralized, declarative configuration model. What does this mean? + +* Decentralized. Ambassador is designed so that lots of developers can individually configure a specific aspect of Ambassador's configuration (usually a route). Ambassador then aggregates these individual bits of configuration into a master configuration for the gateway. + +* Declarative. In Ambassador, the user declares the desired end state of the configuration. Ambassador then figures out how to achieve that desired end state. If the desired end state is already in effect, no change happens. This is a contrast from an imperative model (most frequently seen as a REST API configuration), which forces the user to understand *how* to configure the gateway. + +## Ambassador configuration in practice + +In a typical Ambassador deployment, each service is owned by a developer or development team. This team writes the code, tests, and deploys the service. In order to deploy this service, a team must create a Kubernetes manifest that specifies the desired end state of the service. For example, the `my-service` service could be defined as below: + +``` +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` + +Because a Kubernetes `service` is the fundamental abstraction by which new services are exposed to other services and end users, Ambassador extends the `service` with custom annotations. For example: + +``` +kind: Service +apiVersion: v1 +metadata: + name: my-service + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: my_service_mapping + prefix: /my-service/ + service: my-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` + +With this approach, there is no centralized Ambassador configuration file -- the routing configuration for Ambassador is associated with each individual service. This offers numerous benefits: + +* Agility: Service owners can change their Ambassador configuration without worrying about other end users or going through a central operations function. +* Organizational scalability: Configuring individual routes in Ambassador is the responsibility of service owners, instead of a centralized team. +* Maintainability: If a service is deleted, the route disappears with the service. All of the machinery used to manage Kubernetes manifests can be used with Ambassador without modification. + +## Centralized configuration + +It's possible as a migration strategy to start with a centralized Ambassador configuration that contains all the necessary mappings (you can create a dummy `service` for this). In general, though, this approach is not recommended. + +## Ingress resources + +Astute Kubernetes observers may note that this decentralized model is not unique to Ambassador. In fact, Ingress controllers and Ingress resources can be used in a similar way. In this approach, a Kubernetes service stays unmodified, and a separate Ingress resource can be added to the Kubernetes manifest. You'd end up with something that looks like: + +``` +kind: Service +apiVersion: v1 +metadata: + name: my-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: my-service-ingress +spec: + backend: + serviceName: my-service + servicePort: 80 +``` + +Ambassador has chosen not to support ingress resources for the following reasons: + +* The ingress API is extremely limited, and supports only a small subset of Ambassador's features +* The ingress API has been in beta for a number of years, and there is the possibility of it being deprecated altogether +* Having a separate object that is associated with the `service` object diff --git a/concepts/overview.md b/concepts/overview.md new file mode 100644 index 0000000000..98be6b254d --- /dev/null +++ b/concepts/overview.md @@ -0,0 +1,9 @@ +# Ambassador Core Concepts + +Ambassador is an API gateway that was designed from the ground up to support independent development teams working with cloud native technologies like Kubernetes. This section of the documentation provides a high-level overview of key concepts for any team looking to use Ambassador. + +Topics covered include: + +* 30,000 ft view of the [architecture](/concepts/architecture) +* The role of a cloud native, [microservices-focused](/about/microservices-api-gateways) API gateway +* Why the Ambassador project choose to use Envoy Proxy diff --git a/concepts/using-ambassador-in-org.md b/concepts/using-ambassador-in-org.md new file mode 100644 index 0000000000..4e549e6ebd --- /dev/null +++ b/concepts/using-ambassador-in-org.md @@ -0,0 +1,11 @@ +# Using Ambassador in your organization + +Ambassador can be used in a variety of situations. We've documented a few common use cases with Ambassador: + +## Transitioning from a monolith to microservices + +The "strangler" pattern is a common approach to incrementally migrating from a monolith to microservices. Ambassador can be deployed as a facade to both the monolith and microservices, enabling a migration to Kubernetes and microservices without impacting end users. [Read more here](https://blog.getambassador.io/using-api-gateways-to-facilitate-your-transition-from-monolith-to-microservices-5e630da24717). + +## Testing + +Ambassador can be used for testing services and accelerating continuous delivery. With features such as canary releases and traffic shadowing, Ambassador enables rapid iteration of services. [Read more here](https://blog.getambassador.io/next-level-testing-with-an-api-gateway-and-continuous-delivery-9cbb9c4564b5). diff --git a/css/mermaid.css b/css/mermaid.css new file mode 100644 index 0000000000..769933f0af --- /dev/null +++ b/css/mermaid.css @@ -0,0 +1,273 @@ +/* Flowchart variables */ +/* Sequence Diagram variables */ +/* Gantt chart variables */ +.mermaid .label { + color: #333; +} +.node rect, +.node circle, +.node ellipse, +.node polygon { + fill: #ECECFF; + stroke: #CCCCFF; + stroke-width: 1px; +} +.edgePath .path { + stroke: #333333; +} +.edgeLabel { + background-color: #e8e8e8; +} +.cluster rect { + fill: #ffffde !important; + rx: 4 !important; + stroke: #aaaa33 !important; + stroke-width: 1px !important; +} +.cluster text { + fill: #333; +} +.actor { + stroke: #CCCCFF; + fill: #ECECFF; +} +text.actor { + fill: black; + stroke: none; +} +.actor-line { + stroke: grey; +} +.messageLine0 { + stroke-width: 1.5; + stroke-dasharray: "2 2"; + marker-end: "url(#arrowhead)"; + stroke: #333; +} +.messageLine1 { + stroke-width: 1.5; + stroke-dasharray: "2 2"; + stroke: #333; +} +#arrowhead { + fill: #333; +} +#crosshead path { + fill: #333 !important; + stroke: #333 !important; +} +.messageText { + fill: #333; + stroke: none; +} +.labelBox { + stroke: #CCCCFF; + fill: #ECECFF; +} +.labelText { + fill: black; + stroke: none; +} +.loopText { + fill: black; + stroke: none; +} +.loopLine { + stroke-width: 2; + stroke-dasharray: "2 2"; + marker-end: "url(#arrowhead)"; + stroke: #CCCCFF; +} +.note { + stroke: #aaaa33; + fill: #fff5ad; +} +.noteText { + fill: black; + stroke: none; + font-family: 'trebuchet ms', verdana, arial; + font-size: 14px; +} +/** Section styling */ +.section { + stroke: none; + opacity: 0.2; +} +.section0 { + fill: rgba(102, 102, 255, 0.49); +} +.section2 { + fill: #fff400; +} +.section1, +.section3 { + fill: white; + opacity: 0.2; +} +.sectionTitle0 { + fill: #333; +} +.sectionTitle1 { + fill: #333; +} +.sectionTitle2 { + fill: #333; +} +.sectionTitle3 { + fill: #333; +} +.sectionTitle { + text-anchor: start; + font-size: 11px; + text-height: 14px; +} +/* Grid and axis */ +.grid .tick { + stroke: lightgrey; + opacity: 0.3; + shape-rendering: crispEdges; +} +.grid path { + stroke-width: 0; +} +/* Today line */ +.today { + fill: none; + stroke: red; + stroke-width: 2px; +} +/* Task styling */ +/* Default task */ +.task { + stroke-width: 2; +} +.taskText { + text-anchor: middle; + font-size: 11px; +} +.taskTextOutsideRight { + fill: black; + text-anchor: start; + font-size: 11px; +} +.taskTextOutsideLeft { + fill: black; + text-anchor: end; + font-size: 11px; +} +/* Specific task settings for the sections*/ +.taskText0, +.taskText1, +.taskText2, +.taskText3 { + fill: white; +} +.task0, +.task1, +.task2, +.task3 { + fill: #8a90dd; + stroke: #534fbc; +} +.taskTextOutside0, +.taskTextOutside2 { + fill: black; +} +.taskTextOutside1, +.taskTextOutside3 { + fill: black; +} +/* Active task */ +.active0, +.active1, +.active2, +.active3 { + fill: #bfc7ff; + stroke: #534fbc; +} +.activeText0, +.activeText1, +.activeText2, +.activeText3 { + fill: black !important; +} +/* Completed task */ +.done0, +.done1, +.done2, +.done3 { + stroke: grey; + fill: lightgrey; + stroke-width: 2; +} +.doneText0, +.doneText1, +.doneText2, +.doneText3 { + fill: black !important; +} +/* Tasks on the critical line */ +.crit0, +.crit1, +.crit2, +.crit3 { + stroke: #ff8888; + fill: red; + stroke-width: 2; +} +.activeCrit0, +.activeCrit1, +.activeCrit2, +.activeCrit3 { + stroke: #ff8888; + fill: #bfc7ff; + stroke-width: 2; +} +.doneCrit0, +.doneCrit1, +.doneCrit2, +.doneCrit3 { + stroke: #ff8888; + fill: lightgrey; + stroke-width: 2; + cursor: pointer; + shape-rendering: crispEdges; +} +.doneCritText0, +.doneCritText1, +.doneCritText2, +.doneCritText3 { + fill: black !important; +} +.activeCritText0, +.activeCritText1, +.activeCritText2, +.activeCritText3 { + fill: black !important; +} +.titleText { + text-anchor: middle; + font-size: 18px; + fill: black; +} +/* + + +*/ +.node text { + font-family: 'trebuchet ms', verdana, arial; + font-size: 14px; +} +div.mermaidTooltip { + position: absolute; + text-align: center; + max-width: 200px; + padding: 2px; + font-family: 'trebuchet ms', verdana, arial; + font-size: 12px; + background: #ffffde; + border: 1px solid #aaaa33; + border-radius: 2px; + pointer-events: none; + z-index: 100; +} diff --git a/css/styles.css b/css/styles.css new file mode 100644 index 0000000000..bef7fd5c72 --- /dev/null +++ b/css/styles.css @@ -0,0 +1,807 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +*, *::before, *::after { + box-sizing: inherit; +} + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + box-sizing: border-box; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +ol, ul { + list-style: none; + padding: 0; + margin: 0; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +.container { + margin-right: auto; + margin-left: auto; + padding-right: 15px; + padding-left: 15px; + width: 100%; +} + + +@media screen and (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media screen and (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media screen and (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media screen and (min-width: 1200px) { + .container { + max-width: 1140px; + } +} + +body { + font-family: 'Source Sans Pro', sans-serif; + font-size: 16px; + line-height: 1.5; + color: #222222; + font-weight: 400; +} + +a { + color: #F9634E; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +h1, h2, h3, h4, h5, h6, strong { + font-weight: bold; +} + +/* +* COLORS +*/ +.text-red { + color: #F9634E; +} +.bg-red { + background-color: #F9634E; +} +.text-blue { + color: #68C2D6; +} +.bg-blue { + background-color: #68C2D6; +} +.text-white { + color: #FFFFFF; +} +.bg-white { + background-color: #FFFFFF; +} +.bg-gray { + background-color: #F4F4F4; +} +.text-black { + color: #222222; +} + +.text-xl { + font-size: 3em; /* 48px */ +} +.text-lg { + font-size: 2.25em; /* 36px */ +} +.text-md { + font-size: 1.5em; /* 24px */ +} +.text-sm { + font-size: 1.125em; /* 18px */ +} +.text-regular { + font-size: 1em; /* 16px */ +} +.text-xs { + font-size: 0.875em; /* 14px */ +} + +.font-light { + font-weight: 300; +} + + +/* +* Utilities +*/ +.text-center { + text-align: center; +} + +.flex-center { + justify-content: center; +} + +.text-uppercase { + text-transform: uppercase; +} + +@media screen and (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} + +.btn { + padding: 15px 30px; + text-transform: uppercase; + font-size: 20px; + color: #fff; + text-decoration: none; + display: inline-block; +} +.btn:hover { + text-decoration: none; + opacity: 0.95; +} +.btn-black { + background-color: #2B2F3E; +} +.btn-red { + background-color: #F9634E; +} + +.border-gray { + border: 1px solid #E2E2E2; +} + +.box-shadow { + box-shadow: 0 12px 14px rgba(0,0,0,0.1); +} + +.padding-top-130 { + padding-top: 130px; +} + +.padding-bottom-130 { + padding-bottom: 130px; +} + +.padding-top-75 { + padding-top: 75px; +} + +.padding-bottom-75 { + padding-bottom: 75px; +} + +header { + display: flex; + flex-direction: row; + justify-content: space-between; +} +.navigation-right { + display: flex; + flex-direction: column; +} +@media screen and (min-width: 768px) { + .navigation-right { + align-items: flex-end; + } +} + +.datawire-link { + display: block; + padding: 15px 0 5px; + width: 110px; + margin: 0 16px; +} +.datawire-link > img { + display: block; + width: 100%; +} + +@media screen and (min-width: 768px) { + .datawire-link { + padding: 15px 0; + width: 130px; + margin: 0 40px; + } +} + +.main-navigation { + display: flex; + padding: 10px 5px; + flex-wrap: wrap; + flex-direction: column; +} + +@media screen and (min-width: 768px) { + .main-navigation { + padding: 0 30px; + margin-bottom: 25px; + flex-direction: row; + } + + .main-navigation.right { + justify-content: flex-end; + margin-right: 10px; + } +} + +.main-navigation li a { + margin: 0 10px; + color: #222222; + text-decoration: none; + text-transform: uppercase; + line-height: 35px; +} + +@media screen and (min-width: 768px) { + .main-navigation li a { + font-size: 1.125em; + font-weight: bold; + line-height: 1; + } +} + +.main-navigation li a:hover { + text-decoration: underline; +} + +.main-navigation li .dropbtn { + color: #222222; + border: none; + cursor: pointer; + text-transform: uppercase; + padding: 0; + font-size: 16px; + display: none; +} +@media screen and (min-width: 768px) { + .main-navigation li .dropbtn { + font-size: 1.125em; + font-weight: bold; + line-height: 1; + display: block; + } +} + +.main-navigation .dropdown { + position: relative; + display: inline-block; + font-size: 15px; +} + +@media screen and (min-width: 768px) { + .main-navigation .dropdown-content { + display: none; + position: absolute; + right: 0; + background-color: white; + min-width: 200px; + box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2); + z-index: 1; + border: 1px solid #E2E2E2; + } +} + +.main-navigation .dropdown-content a { + color: black; + padding: 12px 16px; + line-height: 25px; + text-decoration: none; + display: block; + font-weight: normal; + font-size: 1em; +} + +.main-navigation .dropdown:hover .dropdown-content { + display: block; +} + +#home-page #hero { + background: url('/images/penguin-background.svg') no-repeat right bottom; + max-width: 1120px; + margin: 0 auto -90px; + padding: 60px 30px 250px; +} +@media screen and (max-width: 1120px) { + #home-page #hero { + padding-bottom: 400px; + } +} + +#features-page #hero { + padding: 60px 30px 250px; +} + +#hero h1 { + font-weight: 900; +} +#hero .btn { + margin-top: 70px; +} + +.version-number { + height: 72px; + width: 72px; + display: inline-block; + line-height: 68px; + text-align: center; + border: 2px solid #222; + border-radius: 30px; + font-size: 28px; + font-weight: 300; + margin: 10px 0; +} + +#tabs { + padding: 25px 0; +} + +.about-tabs { + display: flex; + justify-content: center; +} + +.about-tabs .tab { + text-align: center; + color: #fff; + font-weight: 600; + text-transform: uppercase; + display: flex; + flex-direction: column; + justify-content: center; + cursor: pointer; + font-size: 12px; + padding: 10px; +} +@media screen and (min-width: 768px) { + .about-tabs .tab { + width: 185px; + height: 131px; + font-size: 1em; + padding: 0; + } +} +.about-tabs .tab.active { + background: #F9634E; +} +.about-tabs .tab img { + margin-bottom: 10px; + height: 33px; +} +.tab-content { + padding: 20px 0 50px; + max-width: 925px; + margin: 0 auto; + font-size: 1.5em; +} +@media screen and (min-width: 768px) { + .tab-content { + font-size: 2.25em; + } +} + +.tab-content div { + display: none; +} + +.tab-content a { + color: #fff; + text-decoration: underline; +} +.content-box { + padding: 70px 50px; + position: relative; +} + +.github-buttons { + padding: 40px 0px; +} + +.code-box { + margin-top: 25px; + padding: 25px; + background-color: #F4F4F4; + overflow: auto; +} +.code-box code { + font-family: Courier New, serif; +} + +.get-started-steps { + counter-reset: item; + margin-left: 0; + padding-left: 75px; +} +.get-started-steps li { + display: block; + margin-bottom: 50px; + list-style-position: inside; +} +.get-started-steps li::before { + position: absolute; + display: inline-block; + content: counter(item); + counter-increment: item; + width: 48px; + line-height: 48px; + color: #fff; + background: #F9634E; + margin-left: -75px; + text-align: center; + font-size: 1.5em; +} + +.sponsored-by { + padding: 90px 0 0; +} +.sponsored-by img.datawire-logo { + margin-bottom: 25px; +} + +.mailing-list-signup .hbspt-form { + max-width: 680px; + margin: 30px auto 0; +} +.mailing-list-signup .hbspt-form form { + display: flex; + flex-direction: column; +} +@media screen and (min-width: 768px) { + .mailing-list-signup .hbspt-form form { + flex-direction: row; + } +} + +.mailing-list-signup .hbspt-form form > div:first-child{ + width: 100%; +} +.mailing-list-signup .hbspt-form .hs_email { + flex: 1; + margin-bottom: 10px; +} +.mailing-list-signup .hbspt-form .hs_email input { + width: 100%; + border-radius: 0; + border: 1px solid #F9634E; + height: 60px; + padding: 10px; + font-size: 20px; + color: #9B9B9B; + font-weight: 400; +} +.mailing-list-signup .hbspt-form .hs_email > label { + display: none; +} +.mailing-list-signup .hbspt-form .hs-error-msgs { + list-style-type: none; + margin-bottom: 0; +} +.mailing-list-signup .hs_submit .hs-button { + padding: 15px 30px; + text-transform: uppercase; + font-size: 20px; + color: #fff; + text-decoration: none; + display: inline-block; + background-color: #F9634E; + border: none; + height: 60px; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 0; +} +.mailing-list-signup .hs_submit .hs-button:hover { + opacity: 0.95; +} + +footer { + padding: 30px 0; +} + +#announcement-widget { + margin: 0; + padding: 9px; + border-radius: 0; + position: relative; + z-index: 9; + color: #fff; + background-color: #e04e39; + box-shadow: 0 3px 6px -2px hsla(199,7%,45%,.5); +} + +#announcement-widget .close { + float: none; + line-height: 1; + position: absolute; + top: 10px; + right: 10px; + font-size: 21px; + font-weight: bold; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .2; + cursor: pointer; + background: transparent; + border: none; +} + +#announcement-widget .close:hover { + background: transparent; + opacity: 0.6; + color: white; +} + +#announcement-widget .btn.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; + font-weight: 400; + font-size: 14px; +} + +.announcement-widget__content { + line-height: 1.3; + text-align: center; +} + +.announcement-widget__content p a { + color: white; + font-weight: bold; + text-decoration: underline; +} + +.announcement-widget__content p a:hover { + text-decoration: none; +} + +@media (min-width: 768px) { + #announcement-widget .btn.btn-default { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + } + #announcement-widget .close { + -webkit-transform: translateY(-50%); + transform: translateY(-50%); + top: 50%; + } + .announcement-widget__content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + text-align: left; + line-height: 2.2; + } + .announcement-widget__content p { + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + margin: 0 15px 0 0; + padding-right: 15px; + text-align: center; + } +} + +.quotes { + max-width: 100%; +} +.slick-arrow { + border: none; + outline: none; + background: transparent; + padding: 0 10px; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 37px; + width: 20px; + position: absolute; + top: 50%; + text-indent: -999999px; +} +.slick-prev { + background: url('/images/left-arrow.svg'); + left: -50px; +} +.slick-next { + background: url('/images/right-arrow.svg'); + right: -50px; +} +.quotes.content-box { + padding-top: 50px; + padding-bottom: 50px; +} +.quote { + display: flex; + align-items: flex-start; +} + +.quote p { + font-size: 20px; + font-weight: 300; + font-style: italic; +} +@media screen and (min-width: 768px) { + .quote p { + font-size: 30px; + } +} + +.quote p .author { + font-weight: 400; + font-size: 20px; + color: #222222; +} + +.quote img { + margin: 30px 100px 0 40px; +} +.quote img.logo { + margin: 10px 50px 0 30px; + max-width: 120px; +} + +.cards { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; +} + +.card { + flex: 1; + min-width: 340px; + max-width: 340px; + height: 273px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: white; + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.04); + margin: 0 15px 30px; + padding: 20px; + border: 2px solid white; +} + +a.card:hover { + text-decoration: none; + border: 2px solid #F9634E; +} + +.card .icon { + height: 72px; + display: flex; + align-items: center; +} + +.card .icon > img { + max-width: 100%; + max-height: 100%; +} + +.card strong { + color: #111; + font-weight: 800; + font-size: 24px; + letter-spacing: -0.5px; + line-height: 30px; + margin: 15px 0 10px; + display: inline-block; +} + +.card p { + display: inline-block; + color: #4A4A4A; + font-size: 18px; + letter-spacing: -0.82px; + text-align: center; + min-height: 60px; + line-height: 30px; +} +@media (min-width: 768px) { + #global-features { + background-image: url('/images/global-features-bg.svg'); + background-repeat: no-repeat; + background-position: 100% 50px; + padding-top: 50px; + } +} +@media (min-width: 768px) { + #self-service-features { + margin-top: -65px; + background-image: url('/images/self-service-features-bg.svg'); + background-repeat: no-repeat; + padding-top: 175px; + } +} +.toggle { + display: flex; + flex-direction: column; + align-items: center; +} + +@media (min-width: 768px) { + .toggle { + flex-direction: row; + justify-content: center; + } +} + +.toggle a { + padding: 0 25px; + line-height: 50px; + font-size: 18px; + letter-spacing: -.4px; + text-transform: uppercase; +} + +.toggle a:first-child { + background: white; + font-weight: bold; +} + +.toggle a:last-child { + color: #111111; +} \ No newline at end of file diff --git a/doc-links.yml b/doc-links.yml new file mode 100644 index 0000000000..40bfa94ad3 --- /dev/null +++ b/doc-links.yml @@ -0,0 +1,180 @@ +- title: Getting Started + link: /docs + collapsable: false + items: + - title: Why Ambassador? + link: /about/why-ambassador + - title: Features and Benefits + link: /about/features-and-benefits + - title: Using Ambassador in Your Organization + link: /concepts/using-ambassador-in-org + - title: Ambassador vs. Other Software + link: /about/alternatives + - title: Installing Ambassador + link: /user-guide/install + items: + - title: Kubernetes (YAML) + link: /user-guide/getting-started + - title: Kubernetes (Helm) + link: /user-guide/helm + - title: Docker Quickstart + link: /about/quickstart + - title: Docker Compose + link: /user-guide/docker-compose + - title: Bare Metal + link: /user-guide/bare-metal + - title: Ambassador Pro + link: /user-guide/ambassador-pro-install + - title: Early Access Releases + link: /user-guide/early-access + +- title: Concepts + link: /concepts/overview + items: + - title: Ambassador Architecture + link: /concepts/architecture + - title: Microservices API Gateways + link: /about/microservices-api-gateways + - title: Why Ambassador Uses Envoy Proxy (External Link) + link: https://blog.getambassador.io/envoy-vs-nginx-vs-haproxy-why-the-open-source-ambassador-api-gateway-chose-envoy-23826aed79ef + +- title: Developer Guide + link: /user-guide/developers + items: + - title: Self-Service Routing and Deployment Control + link: /concepts/developers + - title: Safely Testing in Production + link: /docs/dev-guide/test-in-prod + items: + - title: Testing in Production with Canary Releases + link: /docs/dev-guide/canary-release-concepts + - title: Detect Issues and Determine Root Cause (Monitoring) + link: /reference/statistics + - title: Support for gRPC, WebSockets etc + link: /user-guide/protocol-support-ambassador + items: + - title: Use gRPC with Ambassador + link: /user-guide/grpc + - title: Use WebSockets with Ambassador + link: /user-guide/websockets-ambassador + - title: Integration Testing with Service Preview + link: /docs/dev-guide/service-preview + +- title: Operator Guide + link: /user-guide/operators + items: + - title: Kubernetes Integration (architecture overview) + link: /user-guide/kubernetes-integration + - title: Continuous Delivery, Declarative Config, and GitOps + link: /user-guide/cd-declarative-gitops + items: + - title: Implementing GitOps with Ambassador + link: /user-guide/gitops-ambassador + - title: Security - Encryption and Authentication + link: /user-guide/security + items: + - title: Enabling Transport Level Security (TLS) + link: /user-guide/tls-termination + - title: Server Name Indication (SNI) + link: /user-guide/sni + - title: Basic Authentication + link: /user-guide/auth-tutorial + - title: Single Sign-On with OAuth & OIDC + link: /user-guide/oauth-oidc-auth + - title: Rate Limiting + link: /user-guide/rate-limiting + items: + - title: Implementing Rate Limiting + link: /user-guide/rate-limiting-tutorial + - title: Advanced Rate Limiting + link: /user-guide/advanced-rate-limiting + - title: Adding Tracing + link: /user-guide/tracing-tutorial + - title: Service Mesh Integration + link: /user-guide/service-mesh-integration + items: + - title: Istio and Ambassador + link: /user-guide/with-istio + - title: Consul Connect + link: /user-guide/consul-connect-ambassador + +- title: Reference + collapsable: false + items: + - title: Configuring Ambassador + link: /reference/configuration + items: + - title: Core Configuration + link: /reference/modules + items: + - title: TLS and X-Forwarded-Proto + link: /reference/core/tls + - title: Configuring Services + link: /reference/mappings + items: + - title: Canary Releases + link: /reference/canary + - title: Cross Origin Resource Sharing + link: /reference/cors + - title: Custom Envoy config + link: /reference/override + - title: Header-based routing + link: /reference/headers + - title: Host Header + link: /reference/host + - title: Rate Limits + link: /reference/rate-limits + - title: Redirects + link: /reference/redirects + - title: Request Headers + link: /reference/add_request_headers + - title: Response Headers + link: /reference/add_response_headers + - title: Rewrites + link: /reference/rewrites + - title: Traffic Shadowing + link: /reference/shadowing + - title: Plugins + link: /reference/services/services + items: + - title: Authentication + link: /reference/services/auth-service + - title: Access Control + link: /reference/services/access-control + - title: Rate Limiting + link: /reference/services/rate-limit-service + - title: Tracing + link: /reference/services/tracing-service + - title: Advanced configuration topics + link: /reference/advanced + - title: Running Ambassador + link: /reference/running + items: + - title: Diagnostics + link: /reference/diagnostics + - title: Ambassador with AWS + link: /reference/ambassador-with-aws + - title: Debugging Ambassador + link: /reference/debugging + - title: Upgrading Ambassador + link: /reference/upgrading + - title: Statistics and Monitoring + link: /reference/statistics + +- title: Contributors + items: + - title: Building Ambassador (GitHub) + link: https://github.com/datawire/ambassador/blob/master/BUILDING.md + - title: Changelog (GitHub) + link: https://github.com/datawire/ambassador/blob/master/CHANGELOG.md + +- title: Need Help? + items: + - title: Support + link: /about/support + - title: Ask on Slack + link: https://d6e.co/slack + - title: File a GitHub Issue + link: https://github.com/datawire/ambassador/issues/new + - title: Visit Datawire.io + link: https://www.datawire.io diff --git a/docs/dev-guide/canary-release-concepts.md b/docs/dev-guide/canary-release-concepts.md new file mode 100644 index 0000000000..8addf3a686 --- /dev/null +++ b/docs/dev-guide/canary-release-concepts.md @@ -0,0 +1,147 @@ +# Kubernetes Canary Releases + +Canary release is a technique to reduce the risk of introducing a new version of software in production by slowly rolling out the change to a small subset of users, before rolling it out to the entire infrastructure and making it available to everybody. + + +## Benefits of Canary Releases + +This technique was rather gruesomely inspired from the fact that canary birds were once used in coal mines to alert miners when toxic gases reached dangerous levels — the gases would kill the canary before killing the miners, which provides a warning to get out of the mine tunnels immediately. As long as the canary kept singing, the miners knew that the air was free of dangerous gases. If a canary died, then this signaled an immediate evacuation. + +This technique is called "canary" releasing because just like canaries that were once used in coal mining to alert miners when toxic gases reached dangerous levels, a small set of end users selected for testing act as the canaries and are used to provide an early warning. Unlike the poor canaries of the past, obviously no users are physically hurt during a software release, but negative results from a canary release can be inferred from telemetry and metrics in relation to key performance indicators (KPIs). + +Canary tests can be automated, and are typically run after testing in a pre-production environment has been completed. The canary release is only visible to a fraction of actual users, and any bugs or negative changes can be reversed quickly by either routing traffic away from the canary or by rolling-back the canary deployment. + + +![Canary release process overview](../../images/canary-release-overview.png) + + +## Basic Kubernetes Canary Releases: Deployments + +The first approach to Kubernetes canary releases that many engineers either discover or read about is creating a Service backed by multiple Deployments with [several common selector labels and one "canary" label](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments). For example, the Kubernetes documentation suggests using multiple "track" labels, such as "stable" and "canary", in addition to the common identifying Service selector labels (e.g. "app: guestbook"). As the track labels are not used as a selector in the Service this allows two different (stable and canary) Deployments to be created that do not overlap. + +Assuming that a round robin load balancing algorithm is being used within the Service, the percentage of traffic directed to the canary can be selected by altering the ratio of "stable" to "canary" Deployments. For example, creating nine replicas of the "track: stable" Deployment and a single replica of the "track: canary" Deployment enables approximately 10% of traffic to flow to the new canary Deployment. + + +``` +apiVersion: v1 +kind: Service +metadata: + name: payment-service +spec: + selector: + app: payment-app +... + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: payment + replicas: 9 + ... + labels: + app: payment-app + track: stable + ... + image: payment-app:v3 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: payment-canary + replicas: 1 + ... + labels: + app: payment-app + track: canary + ... + image: payment-app:v4 +``` + + +The obvious downside to implementing Kubernetes canary releases like this is that if the application contained within the Deployment is large or resource intensive it may not be practical to deploy multiple versions of this. In addition, creating fine-grained canary releases that only route 1% or 2% of traffic is challenging, and this is a standard use case for a canary release with an application that receives a reasonable amount of traffic. + + +## Flexible Kubernetes Canary Releases: Smart Routing with Ambassador + +A more effective approach to Kubernetes canary releases is to use some kind of smart proxy, load balancer or API gateway -- like Ambassador. Dynamically routing traffic at the request level means that only one Deployment is required for each of the "stable" and "canary" versions of the application. Instead of relying on a round robin load balancing implementation, the smart proxy can direct a specified percentage of Service requests to each Deployment. This is exactly how Ambassador can be configured to canary release applications. + +Using the smart routing approach requires two Kubernetes Services to be created -- one for the stable version of the app e.g. "payment", and one for the canary version of the app e.g. "payment-canary"-- and associated Deployments can be created a configured as required (the Deployments are not involved with smart routing). + +Ambassador itself is deployed as a Service, typically of type LoadBalancer (which by default uses the underlying platform implementation of a load balancer e.g. on AWS this is an ELB, on GCP a TCP/UDP Load Balancer etc). With the application Services and the Ambassador Service deployed all that is required to enable a canary release is the creation of appropriate Ambassador Mapping, which are defined as Kubernetes annotations attached to a Service. + +The example below defines the annotations on the Ambassador Service itself, but any Service can be used. Note the "weight: 1" property in the payment-canary mapping, which tells Ambassador to route 1% of traffic to this service for the /payment/ route. By default the remaining amount of traffic, 99% in this case, will be routed to the Mapping without a weight specified. + +Ambassador Service config: + +``` +apiVersion: v1 +kind: Service +metadata: + labels: + service: ambassador + name: ambassador + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: payment + prefix: /payment/ + --- + apiVersion: ambassador/v0 + kind: Mapping + name: payment-canary + prefix: /payment/ + service: payment-canary:8081 + Weight: 1 +spec: + type: LoadBalancer + ports: + - name: ambassador + port: 80 + targetPort: 80 + selector: + service: ambassador +``` +And the payment-service Service definition + +``` +apiVersion: v1 +kind: Service +metadata: + name: payment-service +spec: + selector: + app: payment-app +... + + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: payment + replicas: 9 + ... + labels: + app: payment-app + track: stable + ... + image: payment-app:v3 + + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: payment-canary + replicas: 1 + ... + labels: + app: payment-app + track: canary + ... + image: payment-app:v4 + +``` + + +We've written more about canary releases on the [Ambassador blog](https://blog.getambassador.io). To learn more about this pattern, you can [read more here](https://blog.getambassador.io/cloud-native-patterns-canary-release-1cb8f82d371a). diff --git a/docs/dev-guide/service-preview.md b/docs/dev-guide/service-preview.md new file mode 100644 index 0000000000..e0b5181a9f --- /dev/null +++ b/docs/dev-guide/service-preview.md @@ -0,0 +1,157 @@ +# Service Preview + +How do you verify that the code you've written actually works? Ambassador Pro's *Service Preview* lets developers see exactly how their service works in a realistic enviroment -- without impacting other developers or end users. Service Preview integrates [Telepresence](https://www.telepresence.io), the popular CNCF project for local development and debugging on Kubernetes. + +## Install `apictl` + +`apictl` is the command client for Ambassador Pro. + +Download the latest version of the client: + +Mac 64-bit | +Linux 64-bit + +Make sure the client is somewhere on your PATH. In addition, place your license key in `~/.ambassador.key`. + +## Getting started + +In this quick start, we're going to preview a change we make to the QOTM service, without impacting normal users of the QOTM service. Before getting started, make sure the [QOTM service is installed](https://www.getambassador.io/user-guide/getting-started#5-adding-a-service) on your cluster and you've installed the `apictl` command line tool, as explained above. + +1. We're first going to get the QOTM service running locally. Clone the QOTM repository and build a local Docker image. + + ``` + git clone https://github.com/datawire/qotm + cd qotm + docker build . -t qotm:dev + docker run --rm -it -v $(pwd):/service -p 5000:5000 qotm:dev + ``` + + Note that Preview doesn't depend on a locally running container; you can just run a service locally on your laptop. We're using a container in this tutorial to minimize environmental issues with different Python environments. + + In the `docker run` command above, we mount the local directory into the container, so that any code changes to the QOTM service happen immediately. + +2. Now, in another terminal window, redeploy the QOTM service with the Preview sidecar. The sidecar is special process which will route requests to your local machine or to the production cluster. The `apictl traffic inject` command will automatically create the appropriate YAML to inject the sidecar. In the `qotm` directory, pass the file name of the QOTM deployment: + + ``` + apictl traffic inject kubernetes/qotm-deployment.yaml -d qotm -p 5000 > qotm-sidecar.yaml + ``` + + This will create a YAML file called `qotm-sidecar.yaml`. The file will look like the following: + + ``` + apiVersion: extensions/v1beta1 + kind: Deployment + metadata: + name: qotm + spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: qotm + spec: + containers: + - name: qotm + image: datawire/qotm:1.2 + ports: + - name: http-api + containerPort: 5000 + resources: + limits: + cpu: "0.1" + memory: 100Mi + - env: + - name: APPNAME + value: qotm + - name: APPPORT + value: "5000" + image: quay.io/datawire/ambassador-pro:app-sidecar-0.1.2 + name: traffic-sidecar + ports: + - containerPort: 9900 + imagePullSecrets: + - name: ambassador-pro-registry-credentials + ``` + +3. Update the QOTM service YAML to point to the sidecar on port 9900, instead of the QOTM service directly on port 5000. + + ``` + --- + apiVersion: v1 + kind: Service + metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + service: qotm + spec: + selector: + app: qotm + ports: + - port: 80 + name: http-qotm + targetPort: 9900 + ``` + +4. Redeploy QOTM with the sidecar: + + ``` + kubectl apply -f qotm-sidecar.yaml + kubectl apply -f qotm-service.yaml + ``` + +5. Test to make sure that both your production and development instances of QOTM work: + + ``` + curl $AMBASSADOR_IP/qotm/ # test production + curl localhost:5000 # test development + ``` + +6. Initialize the traffic manager for the cluster. + + ``` + apictl traffic initialize + ``` + +7. We need to create an `intercept` rule that tells Ambassador where to route specific requests. The following command will tell Ambassador to route any traffic for the `qotm` deployment where the header `x-service-preview` is `dev` to go to port 5000 on localhost: + + ``` + apictl traffic intercept qotm -n x-service-preview -m dev -t 5000 + ``` + +8. Requests with the header `x-service-preview: dev` will now get routed locally: + + ``` + curl -H "x-service-preview: dev" $AMBASSADOR_IP/qotm/` # will go to local Docker instance + curl $AMBASSADOR_IP/qotm/ # will go to production instance + ``` + +9. Make a change to the QOTM source code. In `qotm/qotm.py`, uncomment out line 149, and comment out line 148, so it reads like so: + + ``` + return RichStatus.OK(quote="Telepresence rocks!") + # return RichStatus.OK(quote=random.choice(quotes)) + ``` + + This will insure that the QOTM service will return a quote of "Telepresence rocks" every time. + +10. Re-run the `curl` above, which will now route to your (modified) local copy of the QOTM service: + + ``` + curl -H "x-service-preview: dev" $AMBASSADOR_IP/qotm/` # will go to local Docker instance + ``` + + To recap: With Preview, we can now see test and visualize changes to our service that we've mode locally, without impacting other users of the stable version of that service. + +## Using Service Preview + +Service Preview will match HTTP headers based on the headers that are seen by the *sidecar*, and not the edge gateway. Matches are made on the whole header, e.g., a match rule of `dev` will not match in the example above, while `/qotm/dev` will match. + +While any HTTP header will match, in practice, using host-based routing (i.e., the `:authority` header), a custom HTTP header (e.g., the `x-service-preview` header used above), or an authentication header is recommended. \ No newline at end of file diff --git a/docs/dev-guide/test-in-prod.md b/docs/dev-guide/test-in-prod.md new file mode 100644 index 0000000000..8700f0f637 --- /dev/null +++ b/docs/dev-guide/test-in-prod.md @@ -0,0 +1,22 @@ +# Testing Safely with Production Traffic + +No code is ever truly proven or tested until it’s running in production. This is particularly true of today’s cloud-native applications. Applications today are continuously deployed, and have multiple layers of dependencies. Thus, traditional strategies such as mocks, staging environments, and integration testing are no longer sufficient for testing cloud-native applications. + +## Testing in production + +The problem with testing in production is the possibility that real-world users could be impacted by any errors in a given update. Over the past few years, two approaches for testing against production traffic have gained increasing acceptance. These patterns are: + +* Traffic shadowing: this approach “shadows” or mirrors a small amount of real user traffic from production to your application under test: + * Although the shadowed results can be verified (both the downstream response and associated upstream side effects) they are not returned to the user -- the user only sees the results from the currently released application. Typically any side effects are not persisted or are executed as a no-op and verified (much like setting up a mock, and verifying that a method/function was called with the correct parameters). + * This allows verification that the application is not crashing or otherwise behaving badly from an operational perspective when dealing with real user traffic and behaviour (and the larger the percentage of traffic shadowed, the higher the probability of confidence). +* Canary releasing: this approach shifts a small amount of real user traffic from production to your application under test: + * The user will see the direct response from the canary version of application from any traffic that is shifted to the canary release, and they will not trigger or see the response from the current production released version of the application. The canary results can also be verified (both the downstream response and associated upstream side effects), but it is key to understand that any side effects will be persisted. + * In addition to allowing verification that the application is not crashing or otherwise behaving badly from an operational perspective when dealing with real user traffic and behaviour, canary releasing allows user validation. For example, if a business KPI performs worse for all canaried requests, then this most likely indicates that the canaried application should not be fully released in its current form. + +## Observability is a prerequisite for testing in production + +Observability is a critical requirement for testing in production. In any canary or shadow deployment, collecting key metrics around latency, traffic, errors, and saturation (the [“Four Golden Signals of Monitoring”](https://landing.google.com/sre/sre-book/chapters/monitoring-distributed-systems/#xref_monitoring_golden-signals)) provides valuable insight into the stability and performance of a new version of the service. Moreover, application developers can compare the metrics (e.g., latency) between the production version and update version. If the metrics are similar, then updates can proceed with much greater confidence. + +## Benefits of testing in production + +Modern cloud applications are continuously deployed, as different teams rapidly update their respective services. Deploying and testing updates in a pre-production staging environment introduces either a bottleneck to the speed of iteration, or provides little feedback due to the fact that staging is not representative of what will be running in production when the deployment actually occurs. Testing in production addresses both of these challenges: developers evaluate their changes in the real-world environment, enabling rapid iteration. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000000..3a94c8ec33 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,23 @@ +# Welcome + +Ambassador is a production tested, open source API Gateway that exposes the power of [Envoy Proxy](https://www.envoyproxy.io) in Kubernetes. + +* [Install Ambassador](/user-guide/install). Quick start instructions for deploying Ambassador for both development environments (Docker, Docker Compose) and Kubernetes (via Kubernetes YAML or Helm). + +* [Concepts](/concepts/overview). Core concepts that cover how Ambassador is designed to be used. + +* [Developer Guide](/user-guide/developers). These guides focus on the core functionality of Ambassador relevant to application developers. + - [Self-Service Routing and Deployment Control](/concepts/developers) + - [Safely Testing in Production](/docs/dev-guide/test-in-prod) + - [Detect Issues and Determine Root Cause (Monitoring)](/reference/statistics) + - [Support for gRPC, WebSockets, etc.](/user-guide/protocol-support-ambassador) + +* [Operator Guide](/user-guide/operators). These guides focus on the core functionality of Ambassador relevant for operations and sysadmin teams. + - [Kubernetes Integration (Architecture Overview)](/user-guide/kubernetes-integration) + - [Continuous Delivery, Declarative Configuration, and GitOps](/user-guide/cd-declarative-gitops) + - [Security - Encryption and Authentication](/user-guide/security) + - [Rate Limiting](/user-guide/rate-limiting) + - [Adding Tracing](/user-guide/tracing-tutorial) + - [Service Mesh Integration](/user-guide/service-mesh-integration) + +* [Reference](/reference/configuration). Detailed documentation on configuring and managing all aspects of Ambassador. diff --git a/docs/test-in-prod.md b/docs/test-in-prod.md new file mode 100644 index 0000000000..ca3213999c --- /dev/null +++ b/docs/test-in-prod.md @@ -0,0 +1,22 @@ +# Testing Safely with Production Traffic + +There’s a saying that no code is ever truly proven or tested until it’s running in production. This is particularly true of today’s cloud-native applications. Applications today are continuously deployed, and have multiple layers of dependencies. Thus, traditional strategies such as mocks, staging environments, and integration testing are no longer sufficient for testing cloud-native applications. + +## Testing in production + +The problem with testing in production is the possibility that real-world users could be impacted by any errors in a given update. Over the past few years, two approaches for testing against production traffic have gained increasing acceptance. These patterns are: + +* Traffic shadowing: this approach “shadows” or mirrors a small amount of real user traffic from production to your application under test: + * Although the shadowed results can be verified (both the downstream response and associated upstream side effects) they are not returned to the user -- the user only sees the results from the currently released application. Typically any side effects are not persisted or are executed as a no-op and verified (much like setting up a mock, and verifying that a method/function was called with the correct parameters). + * This allows verification that the application is not crashing or otherwise behaving badly from an operational perspective when dealing with real user traffic and behaviour (and the larger the percentage of traffic shadowed, the higher the probability of confidence). +* Canary releasing: this approach shifts a small amount of real user traffic from production to your application under test: + * The user will see the direct response from the canary version of application from any traffic that is shifted to the canary release, and they will not trigger or see the response from the current production released version of the application. The canary results can also be verified (both the downstream response and associated upstream side effects), but it is key to understand that any side effects will be persisted. + * In addition to allowing verification that the application is not crashing or otherwise behaving badly from an operational perspective when dealing with real user traffic and behaviour, canary releasing allows user validation. For example, if a business KPI performs worse for all canaried requests, then this most likely indicates that the canaried application should not be fully released in its current form. + +## Observability and testing in production + +Observability is a critical requirement for testing in production. In any canary or shadow deployment, collecting key metrics around latency, traffic, errors, and saturation (the [“Four Golden Signals of Monitoring”](https://landing.google.com/sre/sre-book/chapters/monitoring-distributed-systems/#xref_monitoring_golden-signals)) provides valuable insight into the stability and performance of a new version of the service. Moreover, application developers can compare the metrics (e.g., latency) between the production version and update version. If the metrics are similar, then updates can proceed with much greater confidence. + +## Benefits of testing in production + +Modern cloud applications are continuously deployed, as different teams rapidly update their respective services. Deploying and testing updates in a pre-production staging environment introduces either a bottleneck to the speed of iteration, or provides little feedback due to the fact that staging is not representative of what will be running in production when the deployment actually occurs. Testing in production addresses both of these challenges: developers evaluate their changes in the real-world environment, enabling rapid iteration. \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000..a1ec3dadf4 Binary files /dev/null and b/favicon.ico differ diff --git a/features.html b/features.html new file mode 100644 index 0000000000..aaa5b156bd --- /dev/null +++ b/features.html @@ -0,0 +1,393 @@ + + + + + + + + + Features | Ambassador + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +

    Stay Updated

    +

    Ambassador is under active development. Subscribe to get updates and announcements:

    + +
    + +
    +
    + + + + + + + diff --git a/images/Auth0_JWT.png b/images/Auth0_JWT.png new file mode 100644 index 0000000000..e18155f50e Binary files /dev/null and b/images/Auth0_JWT.png differ diff --git a/images/Auth0_audience.png b/images/Auth0_audience.png new file mode 100644 index 0000000000..6cb706817c Binary files /dev/null and b/images/Auth0_audience.png differ diff --git a/images/Auth0_domain_clientID.png b/images/Auth0_domain_clientID.png new file mode 100644 index 0000000000..a7f8edf615 Binary files /dev/null and b/images/Auth0_domain_clientID.png differ diff --git a/images/Auth0_method_callback_origins.png b/images/Auth0_method_callback_origins.png new file mode 100644 index 0000000000..8d31138e1d Binary files /dev/null and b/images/Auth0_method_callback_origins.png differ diff --git a/images/Auth0_none.png b/images/Auth0_none.png new file mode 100644 index 0000000000..1e87f6c0e1 Binary files /dev/null and b/images/Auth0_none.png differ diff --git a/images/Auth0_secret.png b/images/Auth0_secret.png new file mode 100644 index 0000000000..d0636a50d6 Binary files /dev/null and b/images/Auth0_secret.png differ diff --git a/images/ambassador-arch.png b/images/ambassador-arch.png new file mode 100644 index 0000000000..86b887752d Binary files /dev/null and b/images/ambassador-arch.png differ diff --git a/images/ambassador-logo.svg b/images/ambassador-logo.svg new file mode 100644 index 0000000000..7e18c4a14c --- /dev/null +++ b/images/ambassador-logo.svg @@ -0,0 +1,49 @@ + + + + ambassador logo@1x + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/images/auth-flow.png b/images/auth-flow.png new file mode 100644 index 0000000000..e1ba438792 Binary files /dev/null and b/images/auth-flow.png differ diff --git a/images/authentication-icon.svg b/images/authentication-icon.svg new file mode 100644 index 0000000000..bc4b39a4ab --- /dev/null +++ b/images/authentication-icon.svg @@ -0,0 +1,18 @@ + + + + noun_897228_cc + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/blackbird.png b/images/blackbird.png new file mode 100644 index 0000000000..1f10e5cc93 Binary files /dev/null and b/images/blackbird.png differ diff --git a/images/canary-release-overview.png b/images/canary-release-overview.png new file mode 100644 index 0000000000..c683a23dcb Binary files /dev/null and b/images/canary-release-overview.png differ diff --git a/images/configure-icon.svg b/images/configure-icon.svg new file mode 100644 index 0000000000..a223071672 --- /dev/null +++ b/images/configure-icon.svg @@ -0,0 +1,14 @@ + + + + noun_858572_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/create-application.png b/images/create-application.png new file mode 100644 index 0000000000..d181be2ed5 Binary files /dev/null and b/images/create-application.png differ diff --git a/images/datawire-logo.svg b/images/datawire-logo.svg new file mode 100644 index 0000000000..78a8f01af1 --- /dev/null +++ b/images/datawire-logo.svg @@ -0,0 +1,27 @@ + + + + Group 3 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/diagnostics.png b/images/diagnostics.png new file mode 100644 index 0000000000..2924870390 Binary files /dev/null and b/images/diagnostics.png differ diff --git a/images/docker-compose.png b/images/docker-compose.png new file mode 100644 index 0000000000..b8829521b9 Binary files /dev/null and b/images/docker-compose.png differ diff --git a/images/docker.png b/images/docker.png new file mode 100644 index 0000000000..1f35e5ea41 Binary files /dev/null and b/images/docker.png differ diff --git a/images/fast-icon.svg b/images/fast-icon.svg new file mode 100644 index 0000000000..cb140b5a98 --- /dev/null +++ b/images/fast-icon.svg @@ -0,0 +1,14 @@ + + + + noun_1187990_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/basic-authentication.svg b/images/features-icons/basic-authentication.svg new file mode 100644 index 0000000000..e973e84d16 --- /dev/null +++ b/images/features-icons/basic-authentication.svg @@ -0,0 +1,20 @@ + + + + noun_897228_cc + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/canary-release.svg b/images/features-icons/canary-release.svg new file mode 100644 index 0000000000..e44b8acfda --- /dev/null +++ b/images/features-icons/canary-release.svg @@ -0,0 +1,27 @@ + + + + Group 25 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/cors.svg b/images/features-icons/cors.svg new file mode 100644 index 0000000000..af04a51b18 --- /dev/null +++ b/images/features-icons/cors.svg @@ -0,0 +1,14 @@ + + + + noun_111967_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/datadog.png b/images/features-icons/datadog.png new file mode 100644 index 0000000000..eea05f8ca1 Binary files /dev/null and b/images/features-icons/datadog.png differ diff --git a/images/features-icons/datadog.svg b/images/features-icons/datadog.svg new file mode 100644 index 0000000000..191082e73a --- /dev/null +++ b/images/features-icons/datadog.svg @@ -0,0 +1,12 @@ + + + + Screen Shot 2018-04-05 at 8.22.25 AM + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/images/features-icons/diagnostics.svg b/images/features-icons/diagnostics.svg new file mode 100644 index 0000000000..05c606b985 --- /dev/null +++ b/images/features-icons/diagnostics.svg @@ -0,0 +1,14 @@ + + + + noun_196445_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/distributed-tracing.png b/images/features-icons/distributed-tracing.png new file mode 100644 index 0000000000..6b69e28ca1 Binary files /dev/null and b/images/features-icons/distributed-tracing.png differ diff --git a/images/features-icons/grpc.png b/images/features-icons/grpc.png new file mode 100644 index 0000000000..b2f5a0d91f Binary files /dev/null and b/images/features-icons/grpc.png differ diff --git a/images/features-icons/prometheus.svg b/images/features-icons/prometheus.svg new file mode 100644 index 0000000000..ab2f808d6e --- /dev/null +++ b/images/features-icons/prometheus.svg @@ -0,0 +1,14 @@ + + + + prometheus_logo_grey + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/rate-limiting.svg b/images/features-icons/rate-limiting.svg new file mode 100644 index 0000000000..f9a6252427 --- /dev/null +++ b/images/features-icons/rate-limiting.svg @@ -0,0 +1,16 @@ + + + + Group 10 + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/regex-routing.svg b/images/features-icons/regex-routing.svg new file mode 100644 index 0000000000..06bcbbc317 --- /dev/null +++ b/images/features-icons/regex-routing.svg @@ -0,0 +1,20 @@ + + + + noun_699774_cc + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/request-transformers.svg b/images/features-icons/request-transformers.svg new file mode 100644 index 0000000000..a8497409ee --- /dev/null +++ b/images/features-icons/request-transformers.svg @@ -0,0 +1,18 @@ + + + + noun_96239_cc + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/shadowing.svg b/images/features-icons/shadowing.svg new file mode 100644 index 0000000000..7a0ebeaa8e --- /dev/null +++ b/images/features-icons/shadowing.svg @@ -0,0 +1,15 @@ + + + + shadow + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/statsd.png b/images/features-icons/statsd.png new file mode 100644 index 0000000000..2837443841 Binary files /dev/null and b/images/features-icons/statsd.png differ diff --git a/images/features-icons/statsd.svg b/images/features-icons/statsd.svg new file mode 100644 index 0000000000..fc5771ecbc --- /dev/null +++ b/images/features-icons/statsd.svg @@ -0,0 +1,20 @@ + + + + 88eb31f74479e422e4e9abfc6c2b00ee + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/third-party-auth.svg b/images/features-icons/third-party-auth.svg new file mode 100644 index 0000000000..4d0070d8f9 --- /dev/null +++ b/images/features-icons/third-party-auth.svg @@ -0,0 +1,14 @@ + + + + noun_511233_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/timeouts.svg b/images/features-icons/timeouts.svg new file mode 100644 index 0000000000..6d69544832 --- /dev/null +++ b/images/features-icons/timeouts.svg @@ -0,0 +1,18 @@ + + + + noun_587034_cc + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/tls-termination.svg b/images/features-icons/tls-termination.svg new file mode 100644 index 0000000000..2c590a34d0 --- /dev/null +++ b/images/features-icons/tls-termination.svg @@ -0,0 +1,17 @@ + + + + noun_63544_cc + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/url-rewrite.svg b/images/features-icons/url-rewrite.svg new file mode 100644 index 0000000000..cb1d1a8f03 --- /dev/null +++ b/images/features-icons/url-rewrite.svg @@ -0,0 +1,14 @@ + + + + noun_1295942_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/features-icons/websockets.svg b/images/features-icons/websockets.svg new file mode 100644 index 0000000000..7ca19e9cbd --- /dev/null +++ b/images/features-icons/websockets.svg @@ -0,0 +1,16 @@ + + + + noun_50814_cc + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/images/gRPC-TLS-Ambassador.png b/images/gRPC-TLS-Ambassador.png new file mode 100644 index 0000000000..0189253e07 Binary files /dev/null and b/images/gRPC-TLS-Ambassador.png differ diff --git a/images/gRPC-TLS-Originate.png b/images/gRPC-TLS-Originate.png new file mode 100644 index 0000000000..1b62010dd1 Binary files /dev/null and b/images/gRPC-TLS-Originate.png differ diff --git a/images/global-features-bg.svg b/images/global-features-bg.svg new file mode 100644 index 0000000000..67e3c039d3 --- /dev/null +++ b/images/global-features-bg.svg @@ -0,0 +1,34 @@ + + + + ambassador_logo@2x + Created with Sketch. + + + + + + + + \ No newline at end of file diff --git a/images/grafana.png b/images/grafana.png new file mode 100644 index 0000000000..befe3377f4 Binary files /dev/null and b/images/grafana.png differ diff --git a/images/grpc-tls.png b/images/grpc-tls.png new file mode 100644 index 0000000000..4d705ff0ca Binary files /dev/null and b/images/grpc-tls.png differ diff --git a/images/helm.png b/images/helm.png new file mode 100644 index 0000000000..1c5af71b84 Binary files /dev/null and b/images/helm.png differ diff --git a/images/highly-available-icon.svg b/images/highly-available-icon.svg new file mode 100644 index 0000000000..31803bfa37 --- /dev/null +++ b/images/highly-available-icon.svg @@ -0,0 +1,14 @@ + + + + noun_1205522_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/kubernetes.png b/images/kubernetes.png new file mode 100644 index 0000000000..a392a886bf Binary files /dev/null and b/images/kubernetes.png differ diff --git a/images/left-arrow.svg b/images/left-arrow.svg new file mode 100644 index 0000000000..e977877268 --- /dev/null +++ b/images/left-arrow.svg @@ -0,0 +1,12 @@ + + + + Path 2 + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/images/logo.png b/images/logo.png new file mode 100644 index 0000000000..701f63ba88 Binary files /dev/null and b/images/logo.png differ diff --git a/images/machine-machine.png b/images/machine-machine.png new file mode 100644 index 0000000000..32a112f9cc Binary files /dev/null and b/images/machine-machine.png differ diff --git a/images/penguin-background.svg b/images/penguin-background.svg new file mode 100644 index 0000000000..d45f7c0d79 --- /dev/null +++ b/images/penguin-background.svg @@ -0,0 +1,102 @@ + + + + @2xambassador_logo + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/images/quote.svg b/images/quote.svg new file mode 100644 index 0000000000..5ad3433912 --- /dev/null +++ b/images/quote.svg @@ -0,0 +1,16 @@ + + + + + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/images/right-arrow.svg b/images/right-arrow.svg new file mode 100644 index 0000000000..9b9c31e91a --- /dev/null +++ b/images/right-arrow.svg @@ -0,0 +1,12 @@ + + + + Path 2 Copy + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/images/routing-icon.svg b/images/routing-icon.svg new file mode 100644 index 0000000000..c4940d2f85 --- /dev/null +++ b/images/routing-icon.svg @@ -0,0 +1,14 @@ + + + + noun_1062254_cc + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/images/scopes.png b/images/scopes.png new file mode 100644 index 0000000000..f78d22a0c5 Binary files /dev/null and b/images/scopes.png differ diff --git a/images/self-service-features-bg.svg b/images/self-service-features-bg.svg new file mode 100644 index 0000000000..e50d3d53c1 --- /dev/null +++ b/images/self-service-features-bg.svg @@ -0,0 +1,93 @@ + + + + Group 8 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELF-SERVICE FEATURES + + + + \ No newline at end of file diff --git a/images/shadowing.png b/images/shadowing.png new file mode 100644 index 0000000000..097ecbd5e1 Binary files /dev/null and b/images/shadowing.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000000..f8a7c9c29b --- /dev/null +++ b/index.html @@ -0,0 +1,379 @@ + + + + + + + + + Kubernetes-native microservices API gateway: Ambassador + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    +

    Ambassador

    +

    Open source, Kubernetes-native API Gateway for Microservices built on Envoy

    + +
    + Star + Fork +
    +
    +
    + 0.50.0-rc3 +
    +
    + +
    + Read the blog post +
    +
    +
    +
    +
      +
    • + + Routing +
    • +
    • + + Authentication +
    • +
    • + + Fast +
    • +
    • + + Highly Available +
    • +
    • + + Self-service configuration +
    • +
    +
    +
    +

    Map services to arbitrary URLs in a single, declarative YAML file. Configure routes with CORS support, circuit breakers, timeouts, and more. Replace your Kubernetes ingress controller. Route gRPC, WebSockets, or HTTP. Load balance between your different services.

    +
    +
    +

    + Easily integrate your own authentication service with Ambassador for + + per-request authentication. +

    +
    +
    +

    + Ambassador uses the high performance + Envoy Proxy, which processes + over 2M requests/second at Lyft. Ambassador runs as a sidecar to Envoy, ensuring that you + get raw Envoy performance. +

    +
    +
    +

    + Ambassador uses the proven resilience capabilities of Kubernetes to ensure high + availability. For example, Kubernetes is responsible for auto-restarting, auto-scaling, + and updating (via a RollingUpdate) Ambassador. Unlike many other API gateways, Ambassador + has no database -- it relies on ConfigMap to store state. +

    +
    +
    +

    + Easily and safely configure Ambassador without going through operations -- Ambassador is configured using Kubernetes annotations. +

    +
    +
    +
    +
    +
    + Learn more about Ambassador features +
    +
    +
    +
    +
    +
    +
    + +
    +

    "Ambassador seemed like the only solution that makes dynamically adding routes + easy and the fact that I can do that just by creating a service and annotating + it is awesome! Without Ambassador I don’t know how we would have easily made + all the different web services comprising Kubeflow available to users."

    +

    + + Jeremy Lewi, Software Engineer, Google Kubeflow + +

    +
    +
    +
    +
    +
    + +
    +

    "Ever since the inception of Kubernetes, we’ve witnessed a community of ideas + coming together and evolving best practices. Ambassador is one of the building + blocks we adopted and are proud to be contributing to."

    +

    + + Alexandre Gervais, Staff Software Engineer at AppDirect + +

    +
    +
    +
    +
    +
    + +
    +

    "Regarding Ambassador, all I hear around our company is pure joy."

    +

    + + Carlos Yakimov, Solutions Architect, Falabella + +

    +
    +
    +
    +
    +
    +
    +
    +
    +

    It's easy to get started:

    +
    +
    +
      +
    1. +

      Download and run Ambassador as a Docker container:

      +
      +
      
      +docker run -it -p 8080:80 --name=ambassador --rm quay.io/datawire/ambassador:{VERSION} --demo
      +
      +
      +
    2. +
    3. +

      That's it! Check out the diagnostics page at http://localhost:8080/ambassador/v0/diag/.

      +
    4. +
    5. +

      See Ambassador route requests live to our Quote of The Moment service at demo.getambassador.io:

      +
      +
      curl localhost:8080/qotm/
      +
      +
    6. +
    + +
    +
    +
    +
    +
    +
    +

    Stay Updated

    +

    Ambassador is under active development. Subscribe to get updates and announcements:

    + +
    + +
    +
    + + + + + + + diff --git a/js/datawire-breadcrumb.js b/js/datawire-breadcrumb.js new file mode 100644 index 0000000000..10d64b6d7c --- /dev/null +++ b/js/datawire-breadcrumb.js @@ -0,0 +1,7 @@ +var url = new URL(window.location.href); +var utmSource = url.searchParams.get('utm_source'); +var datawireBreadcrumb = document.getElementById('datawire-breadcrumb'); + +if (utmSource === 'datawire-docs') { + datawireBreadcrumb.style.display = 'block'; +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000000..f27c5df0ec --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "ambassador-web", + "version": "1.0.0", + "description": "GitBook Documentation for Ambassador", + "main": "index.js", + "dependencies": { + "gitbook-cli": "^2.3.0", + "gitbook-plugin-anchorjs": "^2.0.1", + "gitbook-plugin-footer": "^0.1.0", + "gitbook-plugin-gtm": "0.0.6", + "netlify-cli": "^1.2.2" + }, + "scripts": { + "postinstall": "gitbook install", + "build": "gitbook build", + "start": "gitbook serve" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/datawire/ambassador-web.git" + }, + "author": "Datawire", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/datawire/ambassador-web/issues" + }, + "homepage": "https://github.com/datawire/ambassador-web#readme", + "devDependencies": {} +} diff --git a/reference/add_request_headers.md b/reference/add_request_headers.md new file mode 100644 index 0000000000..4cb9db0e4e --- /dev/null +++ b/reference/add_request_headers.md @@ -0,0 +1,24 @@ +# Add Request Headers + +Ambassador can add a dictionary of HTTP headers that can be added to each request that is passed to a service. + +## The `add_request_headers` annotation + +The `add_request_headers` attribute is a dictionary of `header`: `value` pairs. Envoy dynamic values `%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%` and `%PROTOCOL%` are supported, in addition to static values. + +## A basic example + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +add_request_headers: + x-test-proto: "%PROTOCOL%" + x-test-ip: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" + x-test-static: This is a test header +service: qotm +``` + +will add the protocol, client IP, and a static header to `/qotm/`. diff --git a/reference/add_response_headers.md b/reference/add_response_headers.md new file mode 100644 index 0000000000..3b4db6ddd7 --- /dev/null +++ b/reference/add_response_headers.md @@ -0,0 +1,24 @@ +# Add Response Headers + +Ambassador can add a dictionary of HTTP headers that can be added to each response that is returned to client. + +## The `add_response_headers` annotation + +The `add_response_headers` attribute is a dictionary of `header`: `value` pairs. Envoy dynamic values `%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%` and `%PROTOCOL%` are supported, in addition to static values. + +## A basic example + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +add_response_headers: + x-test-proto: "%PROTOCOL%" + x-test-ip: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" + x-test-static: This is a test header +service: qotm +``` + +will add the protocol, client IP, and a static header to returning response to client. diff --git a/reference/advanced.md b/reference/advanced.md new file mode 100644 index 0000000000..a3cab91198 --- /dev/null +++ b/reference/advanced.md @@ -0,0 +1,29 @@ +# Ambassador configuration sequence + +When you run Ambassador within Kubernetes: + +1. At startup, Ambassador will look for the `ambassador-config` Kubernetes `ConfigMap`. If it exists, its contents will be used as the baseline Ambassador configuration. +2. Ambassador will then scan Kubernetes `service`s in its namespace, looking for `annotation`s named `getambassador.io/config`. YAML from these `annotation`s will be merged into the baseline Ambassador configuration. +3. Whenever any services change, Ambassador will update its `annotation`-based configuration. +4. The baseline configuration, if present, will **never be updated** after Ambassador starts. To effect a change in the baseline configuration, use Kubernetes to force a redeployment of Ambassador. + +**Note:** We recommend using _only_ `annotation`-based configuration, so that Ambassador can respond to updates in its environment. + +## Modifying Ambassador's Underlying Envoy Configuration + +Ambassador uses Envoy for the heavy lifting of proxying. + +If you wish to use Envoy features that aren't (yet) exposed by Ambassador, you can use the [`envoy_override` annotation](/reference/mappings#using-envoy-override). This annotation lets you add additional configuration for [Envoy routes](https://www.envoyproxy.io/docs/envoy/latest/api-v1/route_config/route.html). + +If you need to add additional configuration for Envoy clusters, you will need to use your own custom configuration template. To do this, create a templated `envoy.json` file using the Jinja2 template language, and use this to to replace the [default template](https://github.com/datawire/ambassador/tree/master/ambassador/templates/envoy.j2). This method is not officially supported -- if you need to do this, please open a GitHub issue and [contact us on Slack](https://d6e.co/slack) for more information if this seems necessary so that we can explore direct Ambassador support for your use case (or, better yet, submit a PR!). + +## Configuring Ambassador via a Custom Image + +You can also run Ambassador by building a custom image that contains baked-in configuration: + +1. All the configuration data should be collected within a single directory on the filesystem. +2. At image startup, run `ambassador config $configdir $envoy_json_out` where + - `$configdir` is the path of the directory containing the configuration data, and + - `$envoy_json_out` is the path to the `envoy.json` to be written. + +In this usage, Ambassador will not look for `annotation`-based configuration, and will not update any configuration after startup. diff --git a/reference/ambassador-with-aws.md b/reference/ambassador-with-aws.md new file mode 100644 index 0000000000..78beadf6f9 --- /dev/null +++ b/reference/ambassador-with-aws.md @@ -0,0 +1,237 @@ +# Ambassador on AWS + +## Yaml Configuration + +The following is a sample configuration for deploying Ambassador in AWS: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador + namespace: {{ ambassador namespace }} + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "{{ tls certificate ARN }}" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: ambassador + config: + use_proxy_proto: true + use_remote_address: true +spec: + externalTrafficPolicy: Local + type: LoadBalancer + ports: + - name: ambassador + port: 443 + targetPort: 80 + selector: + service: ambassador +``` + +In this configuration, an ELB is deployed with a multi-domain AWS Certificate Manager certificate. The ELB is configured to route TCP to support both WebSockets and HTTP. Ambassador is configured with `use_remote_address` and `use_proxy_proto` to ensure that remote IP addresses are passed through properly. TLS termination then occurs at the ELB. + +## Helm Values Configuration + +The following in a sample configuration for deploying Ambassador in AWS using Helm. + +Create a values file with the following content: + +`values.aws.yaml` +``` +service: + enableHttp: true + enableHttps: true + + targetPorts: + http: 80 + https: 80 + + httpPort: 80 + httpsPort: 443 + + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "{{ tls certificate arn }}" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: ambassador + config: + use_proxy_proto: false + use_remote_address: false + x_forwarded_proto_redirect: true +``` + +Install with: +``` +helm repo add datawire https://www.getambassador.io/helm +helm install --name ambassador -f values.aws.yaml datawire/ambassador +``` + +In this configuration, an ELB is deployed with a multi-domain AWS Certificate Manager certificate. The ELB is configured to route in L7 mode, which means only HTTP(S) traffic is supported, and not web sockets. TLS termination occurs at the ELB. Automatic redirection of HTTP to HTTPS is enabled. Downstream services can extract the client IP from the `X-FORWARDED-FOR` header + +## Ambassador and AWS load balancer notes + +AWS provides three types of load balancers: + +* "Classic" Load Balancer (abbreviated ELB or CLB, sometimes referred to as ELBv1 or Elastic Load Balancer) + * Supports L4 (TCP, TCP+SSL) and L7 load balancing (HTTP 1.1, HTTPS 1.1) + * Does not support WebSockets unless running in L4 mode + * Does not support HTTP 2 (which is required for GRPC) unless running in L4 mode + * Can perform SSL/TLS offload +* Application Load Balancer (abbreviated ALB, sometimes referred to as ELBv2) + * Supports L7 only + * Supports WebSockets + * Supports a broken implementation of HTTP2 (trailers are not supported and these are needed for GRPC) + * Can perform SSL/TLS offload +* Network Load Balancer (abbreviated NLB) + * Supports L4 only + * Cannot perform SSL/TLS offload + +In Kubernetes, when using the AWS integration and a service of type `LoadBalancer`, the only types of load balancers that can be created are ELBs and NLBs (in Kubernetes 1.9 and later). When `aws-load-balancer-backend-protocol` is set to `tcp`, AWS will create a L4 ELB. When `aws-load-balancer-backend-protocol` is set to `http`, AWS will create a L7 ELB. + +## TLS Termination + +As with any Kubernetes environment, Ambassador can be configured to perform SSL offload by configuring a tls [`Module`](/reference/core/tls) or [`TLSContext`](/user-guide/sni). Refer to the [TLS Termination](/user-guide/tls-termination) documentation for more information. + +In AWS, you can also perform SSL offload with an ELB or ALB. If you choose to terminate TLS at the LB, Ambassador should be configured to listen for cleartext traffic on the default port 80. An example of this using an L4 ELB is shown at the top of this document. + +Enabling HTTP -> HTTPS redirection will depend on if your load balancer is running in L4 or L7 mode. + +### L4 Load Balancer + +When running an ELB in L4 mode, you will need to listen on two ports to redirect all incoming HTTP requests to HTTPS. The first port will listen for HTTP traffic to redirect to HTTPS, while the second port will listen for HTTPS traffic. + +Let's say, +- port 80 on the load balancer forwards requests to port 80 on Ambassador +- port 443 on the load balancer forwards requests to port 443 on Ambassador + + + +First off, configure this forwarding in your load balancer. + +```yaml +spec: + externalTrafficPolicy: Local + type: LoadBalancer + ports: + - name: https + port: 443 + targetPort: 443 + - name: http + port: 80 + targetPort: 80 +``` + +Now, we want every request on port 80 to be redirected to port 443. + +To achieve this, you need to use `redirect_cleartext_from` as follows - + +```yaml +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + server: + enabled: True + redirect_cleartext_from: 80 +``` + +**Note:** Ensure there is no `ambassador-certs` secret in Ambassador's Namespace. If present, the tls `Module` will configure Ambassador to expect HTTPS traffic. + +Editing the example service configuration above will give us: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador + namespace: {{ ambassador namespace }} + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "{{ tls certificate ARN }}" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: ambassador + config: + use_remote_address: true + use_proxy_proto: true + --- + apiVersion: ambassador/v0 + kind: Module + name: tls + config: + server: + enabled: true + redirect_cleartext_from: 80 +spec: + externalTrafficPolicy: Local + type: LoadBalancer + ports: + - name: https + port: 443 + targetPort: 443 + - name: http + port: 80 + targetPort: 80 + selector: + service: ambassador +``` + +This configuration makes Ambassador start a new listener on 80 which redirects all cleartext HTTP traffic to HTTPS. + +**Note:** Ambassador only supports standard ports (80 and 443) on the load balancer for L4 redirection, [yet](https://github.com/datawire/ambassador/issues/702)! For instance, if you configure port 8888 for HTTP and 9999 for HTTPS on the load balancer, then an incoming request to `http://:8888` will be redirected to `https://:8888`. This will fail because HTTPS listener is on port 9999. + +### L7 Load Balancer + +If you are running the load balancer in L7 mode, then you will want to redirect all the incoming HTTP requests without the `X-FORWARDED-PROTO: https` header to HTTPS. Here is an example Ambassador configuration for this scenario: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador + namespace: {{ ambassador namespace }} + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "{{ tls certificate ARN }}" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443" + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp" + service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" + service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: ambassador + config: + use_proxy_proto: true + use_remote_address: true + x_forwarded_proto_redirect: true +spec: + externalTrafficPolicy: Local + type: LoadBalancer + ports: + - name: ambassador + port: 443 + targetPort: 80 + selector: + service: ambassador +``` diff --git a/reference/canary.md b/reference/canary.md new file mode 100644 index 0000000000..1c5cbd8c66 --- /dev/null +++ b/reference/canary.md @@ -0,0 +1,36 @@ +# Canary releases + +Canary releasing is a deployment pattern where a small percentage of traffic is diverted to an early ("canary") release of a particular service. This technique lets you test a release on a small subset of users, mitigating the impact of any given bug. Canary releasing also allows you to quickly roll back to a known good version in the event of an unexpected error. Detailed monitoring of core service metrics is an essential part of canary releasing, as monitoring enables the rapid detection of problems in the canary release. + +## Canary releases in Kubernetes + +Kubernetes supports a basic canary release workflow using its core objects. In this workflow, a service owner can create a Kubernetes [service](https://kubernetes.io/docs/concepts/services-networking/service/). This service can then be pointed to multiple [deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/). Each deployment can be a different version. By specifying the number of `replicas` in a given deployment, you can control how much traffic goes between different versions. For example, you could set `replicas: 3` for `v1`, and `replicas: 1` for `v2`, to ensure that 25% of traffic goes to `v2`. This approach works, but is fairly coarse grained unless you have lots of replicas. Moreover, auto scaling doesn't work well with this strategy. + +## Canary releases in Ambassador + +Ambassador supports fine-grained canary releases. Ambassador uses a weighted round robin scheme to route traffic between multiple services. Full metrics are collected for all services, making it easy to compare the relative performance of the canary and production. + + +### The `weight` annotation + +The `weight` attribute specifies how much traffic for a given resource will be routed using a given mapping. Its value is an integer percentage between 0 and 100. Ambassador will balance weights to make sure that, for every resource, the mappings for that resource will have weights adding to 100%. (In the simplest case, a single mapping is guaranteed to receive 100% of the traffic no matter whether it's assigned a `weight` or not.) + +Specifying a weight only makes sense if you have multiple mappings for the same resource, and typically you would _not_ assign a weight to the "default" mapping (the mapping expected to handle most traffic): letting Ambassador assign that mapping all the traffic not otherwise spoken for tends to make life easier when updating weights. Here's an example, which might appear during a canary deployment: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +service: qotm +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm2_mapping +prefix: /qotm/ +service: qotmv2 +weight: 10 +``` + +In this case, the `qotm2_mapping` will receive 10% of the requests for `/qotm/`, and Ambassador will assign the remaining 90% to the `qotm_mapping`. diff --git a/reference/configuration.md b/reference/configuration.md new file mode 100644 index 0000000000..f19aa20cb3 --- /dev/null +++ b/reference/configuration.md @@ -0,0 +1,41 @@ +# Ambassador Configuration + +Ambassador is configured in a declarative fashion, using YAML manifests to describe the state of the world. As with Kubernetes, Ambassador's manifests are identified with `apiVersion`, `kind`, and `name`. The current `apiVersion` is `ambassador/v0`; currently-supported `kind`s are: + +- [`Module`](/reference/modules) manifests configure things with can apply to Ambassador as a whole. For example, the `ambassador` module can define listener ports, and the `tls` module can configure TLS termination for Ambassador. + +- [`AuthService`](/reference/services/auth-service) manifests configures the external authentication service[s] that Ambassador will use. + +- [`RateLimitService`](/reference/services/rate-limit-service) manifests configures the external rate limiting service that Ambassador will use. + +- [`TracingService`](/reference/services/tracing-service) manifests configures the external tracing service that Ambassador will use. + +- [`Mapping`](/reference/mappings) manifests associate REST _resources_ with Kubernetes _services_. Ambassador _must_ have one or more mappings defined to provide access to any services at all. + +## Configuration sources + +Ambassador assembles its configuration from YAML blocks that may be stored: + +- as `annotations` on Kubernetes `service`s (this is the recommended technique); +- as data in a Kubernetes `ConfigMap`; or +- as files in Ambassador's local filesystem. + +The data contained within each YAML block is the same no matter where the blocks are stored, and multiple YAML documents are likewise supported no matter where the blocks are stored. + +## Best Practices for Configuration + +Ambassador's configuration is assembled from multiple YAML blocks, to help enable self-service routing and make it easier for multiple developers to collaborate on a single larger application. This implies a few things: + +- Ambassador's configuration should be under version control. + + While you can always read back Ambassador's configuration from `annotation`s or its diagnostic service, Ambassador will not do versioning for you. Tools like [Forge](https://forge.sh) can help you maintain proper version control for your services' routing configurations. + +- Be aware that Ambassador tries to not start with a broken configuration, but it's not perfect. + + Gross errors will result in Ambassador refusing to start, in which case `kubectl logs` will be helpful. However, it's always possible to e.g. map a resource to the wrong service, or use the wrong `rewrite` rules. Ambassador can't detect that on its own, although its diagnostic pages can help you figure it out. + +- Be careful of mapping collisions. + + If two different developers try to map `/user/` to something, this can lead to unexpected behavior. Ambassador's canary-deployment logic means that it's more likely that traffic will be split between them than that it will throw an error -- again, the diagnostic service can help you here. + +**Note:** Unless specified, mapping attributes cannot be applied to any other resource type. diff --git a/reference/core/tls.md b/reference/core/tls.md new file mode 100644 index 0000000000..11a09c7e02 --- /dev/null +++ b/reference/core/tls.md @@ -0,0 +1,165 @@ +# Transport Layer Security (TLS) + +Ambassador supports both terminating TLS and originating TLS. By default, Ambassador will enable TLS termination whenever it finds valid TLS certificates stored in the `ambassador-certs` Kubernetes secret. + +## The `tls` module + +The `tls` module defines system-wide configuration for TLS when additional configuration is needed. + +```yaml +--- +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + # The 'server' block configures TLS termination. 'enabled' is the only + # required element. + server: + # If 'enabled' is not True, TLS termination will not happen. + enabled: True + + # If you set 'redirect_cleartext_from' to a port number, HTTP traffic + # to that port will be redirected to HTTPS traffic. Typically you would + # use port 80, of course. + # redirect_cleartext_from: 80 + + # These are optional. They should not be present unless you are using + # a custom Docker build to install certificates onto the container + # filesystem, in which case YOU WILL STILL NEED TO SET enabled: True + # above. + # + # cert_chain_file: /etc/certs/tls.crt # remember to set enabled! + # private_key_file: /etc/certs/tls.key # remember to set enabled! + + # Enable TLS ALPN protocol, typically HTTP2 to negotiate it with + # HTTP2 clients over TLS. + # This must be set to be able to use grpc over TLS. + # alpn_protocols: h2 + + # The 'client' block configures TLS client-certificate authentication. + # 'enabled' is the only required element. + client: + # If 'enabled' is not True, TLS client-certificate authentication will + # not happen. + enabled: False + + # If 'cert_required' is True, TLS client certificates will be required + # for every connection. + # cert_required: False + + # This is optional. It should not be present unless you are using + # a custom Docker build to install certificates onto the container + # filesystem, in which case YOU WILL STILL NEED TO SET enabled: True + # above. + # + # cacert_chain_file: /etc/cacert/tls.crt # remember to set enabled! +``` + +## Redirecting from cleartext to TLS + +The most common case requiring a `tls` module is redirecting cleartext traffic on port 80 to HTTPS on port 443, which can be done with the following configuration: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + server: + enabled: True + redirect_cleartext_from: 80 +``` + +## X-FORWARDED-PROTO Redirect + +In cases when TLS is being terminated at an external layer 7 load balancer, then you would want to redirect only the originating HTTP requests to HTTPS, and let the originating HTTPS requests pass through. + +This distinction between an originating HTTP request and an originating HTTPS request is done based on the `X-FORWARDED-PROTO` header that the external layer 7 load balancer adds to every request it forwards after TLS termination. + +To enable this `X-FORWARDED-PROTO` based HTTP to HTTPS redirection, add a `x_forwarded_proto_redirect: true` field to the Ambassador module configuration, e.g., + +```yaml +apiVersion: ambassador/v0 +kind: Module +name: ambassador +config: + x_forwarded_proto_redirect: true +``` + +Note: Setting `x_forwarded_proto_redirect: true` will impact all your Ambassador mappings. Requests that contain have `X-FORWARDED-PROTO` set to `https` will be passed through. Otherwise, for all other values of `X-FORWARDED-PROTO`, they will be redirected to TLS. + +## Authentication with TLS Client Certificates + +Ambassador also supports TLS client-certificate authentcation. After enabling TLS termination, collect the full CA certificate chain (including all necessary intermediate certificates) into a single file. Store the CA certificate chain used to validate the client certificate into a Kubernetes `secret` named `ambassador-cacert`: + +```shell +kubectl create secret generic ambassador-cacert --from-file=tls.crt=$CACERT_PATH +``` + +where `$CACERT_PATH` is the path to the single file mentioned above. + +If you want to _require_ client-cert authentication for every connection, you can add the `cert_required` key: + +```shell +kubectl create secret generic ambassador-cacert --from-file=tls.crt=$CACERT_PATH --from-literal=cert_required=true +``` + +When Ambassador starts, it will notice the `ambassador-cacert` secret and turn TLS client-certificate auth on (assuming that TLS termination is enabled). + +### Using a user defined secret + +If you do not wish to use a secret named `ambassador-cacert`, then you can specify your own secret. This can be particularly useful if you want to use different secrets for different Ambassador deployments in your cluster. + +Create the secret - +```shell +kubectl create secret generic user-secret --from-file=tls.crt=$CACERT_PATH +``` + +And then, configure Ambassador's TLS module like the following - + +```yaml +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + client: + enabled: True + secret: user-secret +``` + +Note: If `ambassador-cacert` is present in the cluster and the TLS module is configured to load a custom secret, then `ambassador-cacert` will take precedence, and the custom secret will be ignored. + +## TLS Origination +Ambassador is also able to originate a TLS connection with backend services. This can be easily configured by telling Ambassador to route traffic to a service over HTTPS or setting the [tls](/reference/mappings#using-tls) attribute to `true`. + +``` +--- +apiVersion: v1 +kind: Service +metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + service: https://qotm +spec: + selector: + app: qotm + ports: + - port: 443 + name: http-qotm + targetPort: http-api +``` +Ambassador will assume it can trust the services in your cluster so will default to not validating the backend's certificates. This allows for your backend services to use self-signed certificates with ease. + +### Mutual TLS +Ambassador can be configured to do mutual TLS with backend services as well. To accomplish this, you will need to provide certificates for Ambassador to use with the backend. An example of this is given in the [Ambassador with Istio](/user-guide/with-istio#istio-mutual-tls) documentation. + +## More reading + +The [TLS termination guide](/user-guide/tls-termination) provides a tutorial on getting started with TLS in Ambassador. For more informatiom on configuring Ambassador with external L4/L7 load balancers, see the [documentation on AWS](/reference/ambassador-with-aws). Note that this document, while intended for AWS users, has information also applicable to other cloud providers. + diff --git a/reference/cors.md b/reference/cors.md new file mode 100644 index 0000000000..fac268a05e --- /dev/null +++ b/reference/cors.md @@ -0,0 +1,109 @@ +# Cross-Origin Resource Sharing + +Cross-Origin resource sharing lets users request resources (e.g., images, fonts, videos) from domains outside the original domain. + +CORS configuration can be set for all Ambassador mappings in the [ambassador](https://www.getambassador.io/reference/modules#the-ambassador-module) module, or set per [mapping](https://www.getambassador.io/reference/mappings#configuring-mappings). + +## The `cors` attribute + +The `cors` attribute enables the CORS filter. The following settings are supported: + +- `origins`: Specifies a list of allowed domains for the `Access-Control-Allow-Origin` header. To allow all origins, use the wildcard `"*"` value. Format can be either of: + - comma-separated list, e.g. + ```yaml + origins: http://foo.example,http://bar.example + ``` + - YAML array, e.g. + ```yaml + origins: + - http://foo.example + - http://bar.example + ``` +- `methods`: if present, specifies a list of allowed methods for the `Access-Control-Allow-Methods` header. Format can be either of: + - comma-separated list, e.g. + ```yaml + methods: POST, GET, OPTIONS + ``` + - YAML array, e.g. + ```yaml + methods: + - GET + - POST + - OPTIONS + ``` +- `headers`: if present, specifies a list of allowed headers for the `Access-Control-Allow-Headers` header. Format can be either of: + - comma-separated list, e.g. + ```yaml + headers: Content-Type + ``` + - YAML array, e.g. + ```yaml + header: + - Content-Type + ``` +- `credentials`: if present with a true value (boolean), will send a `true` value for the `Access-Control-Allow-Credentials` header. +- `exposed_headers`: if present, specifies a list of allowed headers for the `Access-Control-Expose-Headers` header. Format can be either of: + - comma-separated list, e.g. + ```yaml + exposed_headers: X-Custom-Header + ``` + - YAML array, e.g. + ```yaml + exposed_headers: + - X-Custom-Header + ``` +- `max_age`: if present, indicated how long the results of the preflight request can be cached, in seconds. This value must be a string. + +## Example + +```yaml +apiVersion: ambassador/v0 +kind: Mapping +name: cors_mapping +prefix: /cors/ +service: cors-example +cors: + origins: http://foo.example,http://bar.example + methods: POST, GET, OPTIONS + headers: Content-Type + credentials: true + exposed_headers: X-Custom-Header + max_age: "86400" +``` +## [AuthService](/reference/services/auth-service) and Cross-Origin Resource Sharing + +When you use external authorization, each incoming request is authenticated before routing to its destination, including pre-flight `OPTIONS` requests. +If your `AuthService` implementation wants to deal with CORS itself, by default it will deny these requests, so you have to teach it to accept anything, because you implement CORS on a different level. + +For example, a possible configuration for Spring Boot 2.0.1: +```java +@EnableWebSecurity +class SecurityConfig extends WebSecurityConfigurerAdapter { + + public void configure(final HttpSecurity http) throws Exception { + http + .cors().configurationSource(new PermissiveCorsConfigurationSource()).and() + .csrf().disable() + .authorizeRequests() + .antMatchers("**").permitAll(); + } + + private static class PermissiveCorsConfigurationSource implements CorsConfigurationSource { + /** + * Return a {@link CorsConfiguration} based on the incoming request. + * + * @param request + * @return the associated {@link CorsConfiguration}, or {@code null} if none + */ + @Override + public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) { + final CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowCredentials(true); + configuration.setAllowedHeaders(Collections.singletonList("*")); + configuration.setAllowedMethods(Collections.singletonList("*")); + configuration.setAllowedOrigins(Collections.singletonList("*")); + return configuration; + } + } +} +``` diff --git a/reference/debugging.md b/reference/debugging.md new file mode 100644 index 0000000000..72c841ad46 --- /dev/null +++ b/reference/debugging.md @@ -0,0 +1,365 @@ +# Debugging (Advanced) + +If Ambassador is not starting or is not behaving as you would expect, your first step should be the [Ambassador Diagnostics](/reference/diagnostics) service. This document covers more advanced use cases and approaches, and assumes that you have either looked at the diagnostic console or can't access this page due to an Ambassador initialisation issue. + +## tl;dr Problem? Start here + +* [Example configuration for debug examples](#a-nameexample-configaexample-config-for-debug-demonstrations) +* Ambassador not starting + * [Check Ambassador is running](#a-namecheck-runningachecking-ambassador-is-running) via `kubectl` + * [Check the logs](#a-namelogsagetting-access-to-the-ambassador-logs) +* Ambassador not behaving as expected + * [Check Ambassador is running correctly](#a-namecheck-runningachecking-ambassador-is-running) via `kubectl` + * [Check the logs](#a-namelogsagetting-access-to-the-ambassador-logs) (potentially with "Set Debug On" via the Diagnostic Console) +* Ambassdor/Envoy configuration not as unexpected + * "Set Debug On" (via Diagnostic Console) and [check the (now verbose) logs](#a-namelogsagetting-access-to-the-ambassador-logs) + * Exec into an Ambassador Pod and [manually verify](#a-nameexamining-podaexamining-an-ambassadorenvoy-pod-and-container) the generated Envoy configuration +* Mounted TLS certificates not being detected by Ambassador + * Exec into an Ambassador Pod and [manually verify](#a-nameexamining-podaexamining-an-ambassadorenvoy-pod-and-container) that the mount is as expected (and in the correct file system location) +* You want to manually change and experiment with the generated Envoy configuration + * [Exec into an Ambassador Pod](#a-nameexamining-podaexamining-an-ambassadorenvoy-pod-and-container) and [manually experiment](#a-namemanually-experimentinga-manually-experimenting-with-ambassador--envoy-configuration) with changing the Envoy configuration and sending a SIGHUP to the parent process + + +## Example Config for Debug Demonstrations + +The following debugging instructions assume that Ambassador and the following services from the +[getting started guide](/user-guide/getting-started) have been deployed to a Kubernetes cluster. + +e.g. Create a cluster in GKE with RBAC support enabled and your user account configured correctly: + +```shell +$ gcloud container clusters create ambassador-demo --preemptible +$ kubectl create clusterrolebinding cluster-admin-binding-new \ +--clusterrole cluster-admin --user +``` + +Deploy the latest version of Ambassador: + +```shell +$ kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-rbac.yaml +``` +Next, create an Ambassador Service and deploy a basic `httpbin` Ambassador Mapping +e.g. save this YAML to a file named ```ambassador-services.yaml``` + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador +spec: + type: LoadBalancer + ports: + - port: 80 + selector: + service: ambassador + +--- +apiVersion: v1 +kind: Service +metadata: + name: httpbin + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin_mapping + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org +spec: + ports: + - name: httpbin + port: 80 +``` +And apply this into your cluster, e.g.: + +```shell +$ kubectl apply -f ambassador-services.yaml +``` + +## Checking Ambassador is running + +If you cannot access the [diagnostics console](/reference/diagnostics) via ```kubectl port-forward 8877``` +the first thing to check is that Ambassador is running. This can be achieved via +the standard Kubernetes commands. + +First, check the Deployment + +```shell +$ kubectl get deployments +NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE +ambassador 3 3 3 3 1m +``` + +If after a brief period of time to allow for Ambassador to initialize the "desired" number of replicas does not equal the "current" or "available" number, then you will also want to check the associated Pods: + +```shell +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-85c4cf67b-4pfj2 1/1 Running 0 1m +ambassador-85c4cf67b-fqp9g 1/1 Running 0 1m +ambassador-85c4cf67b-vg6p5 1/1 Running 0 1m +``` + +If any of the Pods have not started you can "Describe" both the Deployment and individual Pods. + +When describing the Deployment, pay particular attention to the "Replicas" (close to the topi of the output) and the "Events" log (close to the bottom of the output). *The "Events" log will often show information like a failed image pull, RBAC issues, or a lack of cluster resources.* + +```shell +$ kubectl describe deployment ambassador +Name: ambassador +Namespace: default +CreationTimestamp: Mon, 15 Oct 2018 13:26:40 +0100 +Labels: service=ambassador +Annotations: deployment.kubernetes.io/revision=1 + kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"extensions/v1beta1","kind":"Deployment","metadata":{"annotations":{},"name":"ambassador","namespace":"default"},"spec":{"replicas":3,"te... +Selector: service=ambassador +Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable +StrategyType: RollingUpdate + +... + +Pod Template: + Labels: service=ambassador + Annotations: sidecar.istio.io/inject=false + Service Account: ambassador + Containers: + ambassador: + Image: quay.io/datawire/ambassador:0.40.0 + Ports: 80/TCP, 443/TCP, 8877/TCP + Host Ports: 0/TCP, 0/TCP, 0/TCP + Limits: + cpu: 1 + memory: 400Mi + Requests: + cpu: 200m + memory: 100Mi + Liveness: http-get http://:8877/ambassador/v0/check_alive delay=30s timeout=1s period=3s #success=1 #failure=3 + Readiness: http-get http://:8877/ambassador/v0/check_ready delay=30s timeout=1s period=3s #success=1 #failure=3 + Environment: + AMBASSADOR_NAMESPACE: (v1:metadata.namespace) + Mounts: + Volumes: + +... + +Conditions: + Type Status Reason + ---- ------ ------ + Available True MinimumReplicasAvailable +OldReplicaSets: +NewReplicaSet: ambassador-85c4cf67b (3/3 replicas created) +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal ScalingReplicaSet 2m deployment-controller Scaled up replica set ambassador-85c4cf67b to 3 +``` + +You can also describe individual Pods, paying particular attention to the "Status" field (at the top of the output) and the "Events" log (at the bottom of the output). *The "Events" log will often show issues such as image pull failures, volume mount issues, and container crash loops,* e.g.: + +```shell +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-85c4cf67b-4pfj2 1/1 Running 0 3m + + +$ kubectl describe pods ambassador-85c4cf67b-4pfj2 +Name: ambassador-85c4cf67b-4pfj2 +Namespace: default +Node: gke-ambassador-demo-default-pool-912378e5-dkxc/10.128.0.2 +Start Time: Mon, 15 Oct 2018 13:26:40 +0100 +Labels: pod-template-hash=417079236 + service=ambassador +Annotations: sidecar.istio.io/inject=false +Status: Running +IP: 10.60.0.5 +Controlled By: ReplicaSet/ambassador-85c4cf67b +Containers: + ambassador: + Container ID: docker://33ab16fe9f02bb425dd03a501b70c67eb41fd5831ff68e064f64965584e7cd43 + Image: quay.io/datawire/ambassador:0.40.0 + +... + +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Scheduled 4m default-scheduler Successfully assigned ambassador-85c4cf67b-4pfj2 to gke-ambassador-demo-default-pool-912378e5-dkxc + Normal SuccessfulMountVolume 4m kubelet, gke-ambassador-demo-default-pool-912378e5-dkxc MountVolume.SetUp succeeded for volume "ambassador-token-tmk94" + Normal Pulling 4m kubelet, gke-ambassador-demo-default-pool-912378e5-dkxc pulling image "quay.io/datawire/ambassador:0.40.0" + Normal Pulled 4m kubelet, gke-ambassador-demo-default-pool-912378e5-dkxc Successfully pulled image "quay.io/datawire/ambassador:0.40.0" + Normal Created 4m kubelet, gke-ambassador-demo-default-pool-912378e5-dkxc Created container + Normal Started 4m kubelet, gke-ambassador-demo-default-pool-912378e5-dkxc Started container +``` + +## Getting Access to the Ambassador Logs + +The Ambassador logs can provide a lot of information if something isn't behaving as expected. There can be a lot of text to parse (especially when running in debug mode), but key information to look out for is the Ambassador process restarting unexpectedly, or malformed Envoy configuration. + +In order to view the logs you will need to target an individual Ambassador Pod. e.g.: + +```shell +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-85c4cf67b-4pfj2 1/1 Running 0 3m +$ +$ kubectl logs ambassador-85c4cf67b-4pfj2 +2018-10-10 12:26:50 kubewatch 0.40.0 INFO: generating config with gencount 1 (0 changes) +/usr/lib/python3.6/site-packages/pkg_resources/__init__.py:1235: UserWarning: /ambassador is writable by group/others and vulnerable to attack when used with get_resource_filename. Consider a more secure location (set with .set_extraction_path or the PYTHON_EGG_CACHE environment variable). + warnings.warn(msg, UserWarning) +2018-10-10 12:26:51 kubewatch 0.40.0 INFO: Scout reports {"latest_version": "0.40.0", "application": "ambassador", "notices": [], "cached": false, "timestamp": 1539606411.061929} + +2018-10-10 12:26:54 diagd 0.40.0 [P15TMainThread] INFO: thread count 3, listening on 0.0.0.0:8877 +[2018-10-10 12:26:54 +0000] [15] [INFO] Starting gunicorn 19.8.1 +[2018-10-10 12:26:54 +0000] [15] [INFO] Listening at: http://0.0.0.0:8877 (15) +[2018-10-10 12:26:54 +0000] [15] [INFO] Using worker: threads +[2018-10-10 12:26:54 +0000] [42] [INFO] Booting worker with pid: 42 +2018-10-10 12:26:54 diagd 0.40.0 [P42TMainThread] INFO: Starting periodic updates +[2018-10-10 12:27:01.977][21][info][main] source/server/drain_manager_impl.cc:63] shutting down parent after drain +``` + +By using the [Ambassador diagnostics console](/reference/diagnostics) you can click a button to "Set Debug On", and this causes Ambassador to generate a lot more logging. This can be useful when tracking down a particularly subtle bug. + +## Examining an Ambassador/Envoy Pod and Container + +It can sometimes be useful to examine the contents of the Ambassador Pod, for example, to check volume mounts are correct (e.g. TLS certificates are present in the required directory), to determine the latest Ambassador configuration has been sent to the Pod, or that the generated Envoy configuration is correct (or as expected). + +You can look into an Ambassador Pod by using ```kube-exec``` and the ```/bin/sh``` shell contained within the Ambassador container. e.g.: + +```shell +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-85c4cf67b-4pfj2 1/1 Running 0 14m +ambassador-85c4cf67b-fqp9g 1/1 Running 0 14m +ambassador-85c4cf67b-vg6p5 1/1 Running 0 14m +$ +$ kubectl exec -it ambassador-85c4cf67b-4pfj2 -- /bin/sh +/ambassador # pwd +/ambassador +/ambassador # ls -lsa +total 84 + 4 drwxrwxr-x 1 root root 4096 Oct 15 12:35 . + 4 drwxr-xr-x 1 root root 4096 Oct 15 12:26 .. + 4 drwxr-xr-x 4 root root 4096 Oct 15 12:26 ambassador-0.40.0-py3.6.egg-tmp + 4 drwxrwxr-x 1 root root 4096 Sep 25 20:29 ambassador-config + 4 drwxr-xr-x 2 root root 4096 Oct 15 12:26 ambassador-config-1 + 4 drwxr-xr-x 2 root root 4096 Oct 15 12:35 ambassador-config-2 + 4 drwxrwxr-x 1 root root 4096 Sep 25 20:29 ambassador-demo-config + 8 -rwxr-xr-x 1 root root 4179 Sep 25 20:28 entrypoint.sh + 4 -rw-r--r-- 1 root root 3322 Oct 15 12:26 envoy-1.json + 8 -rw-r--r-- 1 root root 4101 Oct 15 12:35 envoy-2.json + 8 -rw-rw-r-- 1 root root 5245 Sep 25 20:28 hot-restarter.py + 20 -rw-rw-r-- 1 root root 16952 Sep 25 20:28 kubewatch.py + 4 -rwxrwxr-- 1 root root 175 Sep 25 20:28 requirements.txt + 4 -rwxr-xr-x 1 root root 997 Sep 25 20:28 start-envoy.sh +``` +The above output shows a typical file list from a pre-0.50 Ambassador instance. The `ambassador -config-X` directories contain the Ambassador configuration that was specified during each update of Ambassador via Kubernetes config files, with the higher number indicating the more recent configuration (as verified by the directory timestamps). The easy method to determine the latest configuration is to look for the `ambassador-config-X` directory with the highest number. + +```shell +/ambassador # ls ambassador-config-2 +Httpbin-default.yaml + +/ambassador # cat ambassador-config-2/Httpbin-default.yaml + +--- +apiVersion: v0.1 +kind: Pragma +ambassador_id: default +source: "service httpbin, namespace default" +autogenerated: true +--- +apiVersion: ambassador/v0 +kind: Mapping +name: httpbin_mapping +prefix: /httpbin/ +service: httpbin.org:80 +host_rewrite: httpbin.org +``` + + +The Envoy Proxy configuration that was generated from the Ambassador configuration is found in corresponding `envoy-X.json` file (where the number matches the `ambassador-config-X` directory number). The contents of the Envoy configuration files can be very useful when looking for subtle mapping issues or bugs. + +```shell +/ambassador # cat envoy-2.json + +{ + "listeners": [ + + { + "address": "tcp://0.0.0.0:80", + + "filters": [ + { + "type": "read", + "name": "http_connection_manager", + "config": {"codec_type": "auto", + "stat_prefix": "ingress_http", + "access_log": [ + { +``` + +## Manually Experimenting with Ambassador / Envoy configuration + +If the generated Envoy configuration is not looking as expected, you can manually tweak this and restart the Envoy process. The general approach to this is to scale down the Ambassador Deployment to a single Pod in order to send all Ambassador traffic through this single instance (which is not recommended in production!), exec into the Pod and make the modification, and then restart the Envoy process by sending a ```SIGHUP``` to the ```hot-starter.py``` process. e.g. + +```shell +$ kubectl scale deployment ambassador --replicas=1 +deployment.extensions "ambassador" scaled + tmp $ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-85c4cf67b-4pfj2 1/1 Running 0 30m +ambassador-85c4cf67b-fqp9g 1/1 Terminating 0 30m +ambassador-85c4cf67b-vg6p5 1/1 Terminating 0 30m +``` + +Wait for the scale down to complete, and then modify the Envoy config. e.g: + +```shell +$ kubectl exec -it ambassador-85c4cf67b-4pfj2 -- /bin/sh +/ambassador # ls -lsa +total 84 + 4 drwxrwxr-x 1 root root 4096 Oct 15 12:35 . + 4 drwxr-xr-x 1 root root 4096 Oct 15 12:26 .. + 4 drwxr-xr-x 4 root root 4096 Oct 15 12:26 ambassador-0.40.0-py3.6.egg-tmp + 4 drwxrwxr-x 1 root root 4096 Sep 25 20:29 ambassador-config + 4 drwxr-xr-x 2 root root 4096 Oct 15 12:26 ambassador-config-1 + 4 drwxr-xr-x 2 root root 4096 Oct 15 12:35 ambassador-config-2 + 4 drwxrwxr-x 1 root root 4096 Sep 25 20:29 ambassador-demo-config + 8 -rwxr-xr-x 1 root root 4179 Sep 25 20:28 entrypoint.sh + 4 -rw-r--r-- 1 root root 3322 Oct 15 12:26 envoy-1.json + 8 -rw-r--r-- 1 root root 4101 Oct 15 12:35 envoy-2.json + 8 -rw-rw-r-- 1 root root 5245 Sep 25 20:28 hot-restarter.py + 20 -rw-rw-r-- 1 root root 16952 Sep 25 20:28 kubewatch.py + 4 -rwxrwxr-- 1 root root 175 Sep 25 20:28 requirements.txt + 4 -rwxr-xr-x 1 root root 997 Sep 25 20:28 start-envoy.sh +/ambassador # vi envoy-2.json +``` +Make your changes to the Envoy configuration using `vi` and save the data. Now you can restart the Envoy process by sending a SIGHUP to the `hot-restarter.py` process. + +Be aware that even though you have modified the configuration files, the Ambassador Diagnostic Console may not accurately reflect your updates. In order to determine that the restart was successful with the correct configuration, you can ensure that the "Set Debug On" has been enabled via the Diagnostic Console and you can follow the Ambassador/Envoy logs to see the new configuration has been loaded. + +```shell +/ambassador # ps aux +PID USER TIME COMMAND + 1 root 0:00 {entrypoint.sh} /bin/sh ./entrypoint.sh + 15 root 0:01 {diagd} /usr/bin/python3 /usr/bin/diagd --no-debugging /ambassador/ambassador-config + 16 root 0:00 /usr/bin/python3 /ambassador/hot-restarter.py /ambassador/start-envoy.sh + 17 root 0:02 /usr/bin/python3 /ambassador/kubewatch.py watch /ambassador/ambassador-config /ambassador/envoy.json -p 16 --delay 15 + 42 root 0:03 {diagd} /usr/bin/python3 /usr/bin/diagd --no-debugging /ambassador/ambassador-config + 52 root 0:01 /usr/local/bin/envoy -c /ambassador/envoy-2.json --restart-epoch 1 --drain-time-s 5 --service-cluster ambassador-default --parent-shutdow + 63 root 0:00 /bin/sh + 76 root 0:00 ps aux +/ambassador # +/ambassador # # send a SIGHUP (kill -1) to the "/usr/bin/python3 /ambassador/hot-restarter.py" process +/ambassador # kill -1 16 +``` +In a separate window you can follow the Ambassador logs before you issue the SIGHUP, e.g.: + +```shell +$ kubectl logs -f ambassador-85c4cf67b-4pfj2 +got SIGHUP +forking and execing new child process at epoch 3 +forked new child process with PID=79 +``` diff --git a/reference/diagnostics.md b/reference/diagnostics.md new file mode 100644 index 0000000000..4d810b9880 --- /dev/null +++ b/reference/diagnostics.md @@ -0,0 +1,42 @@ +# Diagnostics + +If Ambassador is not routing your services as you'd expect, your first step should be the Ambassador Diagnostics service. This is exposed on port 8877 by default. You'll need to use `kubectl port-forward` for access, e.g., + +```shell +kubectl port-forward ambassador-xxxx-yyy 8877 +``` + +where you'll have to fill in the actual pod name of one of your Ambassador pods (any will do). Once you have that, you'll be able to point a web browser at + +`http://localhost:8877/ambassador/v0/diag/` + +for the diagnostics overview. + +![Diagnostics](/images/diagnostics.png) + + Some of the most important information - your Ambassador version, how recently Ambassador's configuration was updated, and how recently Envoy last reported status to Ambassador - is right at the top. The diagnostics overview can show you what it sees in your configuration map, and which Envoy objects were created based on your configuration. + +If needed, you can get JSON output from the diagnostic service, instead of HTML: + +`curl http://localhost:8877/ambassador/v0/diag/?json=true` + +## Health status + +Ambassador displays the health of a service in the diagnostics UI. Health is computed as successful requests / total requests and expressed as a percentage. The total requests comes from nvoy `upstream_rq_pending_total` stat. Successful requests is calculated by substracting `upstream_rq_4xx` and `upstream_rq_5xx` from the total. + +Red is used when the success rate ranges from 0% - 70%. +Yellow is used when the success rate ranges from 70% - 90%. +Green is used when the success rate is > 90%. + +## Troubleshooting + +If the diagnostics service does not provide sufficient information, Kubernetes and Envoy provide additional debugging information. + +If Ambassador isn't working at all, start by looking at the data from the following: + +* `kubectl describe pod ` will give you a list of all events on the Ambassador pod +* `kubectl logs ambassador` will give you a log from Ambassador itself + +If you need additional help, feel free to join our [Slack channel](https://d6e.co/slack) with the above information (along with your Kubernetes manifest). + +You can also increase the debug of Envoy through the button in the diagnostics panel. Turn on debug logging, issue a request, and capture the log output from the Ambassador pod using `kubectl logs` as described above. \ No newline at end of file diff --git a/reference/headers.md b/reference/headers.md new file mode 100644 index 0000000000..42908609f8 --- /dev/null +++ b/reference/headers.md @@ -0,0 +1,64 @@ +# Headers + +Ambassador can route to target services based on HTTP headers with the `headers` and `regex_headers` annotations. Multiple mappings with different annotations can be applied to construct more complex routing rules. + +## The `headers` annotation + +The `headers` attribute is a dictionary of `header`: `value` pairs. Ambassador will only allow requests that match the specified `header`: `value` pairs to reach the target service. + +You can also set the `value` of a header to `true` to test for the existence of a header. + +## A basic example + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +headers: + x-qotm-mode: canary + x-random-header: datawire +service: qotm +``` + +will allow requests to `/qotm/` to succeed only if the `x-qotm-mode` header has the value `canary` _and_ the `x-random-header` has the value `datawire`. + +## A conditional example + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mode_mapping +prefix: /qotm/ +headers: + x-qotm-mode: true +service: qotm-mode +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_regular_mapping +prefix: /qotm/ +service: qotm-regular +``` + +will send requests that contain the `x-qotm-mode` header to the `qotm-mode` target, while routing all other requests to the `qotm-regular` target. + +## `regex_headers` + +The following mapping will route mobile requests from Android and iPhones to a mobile service: + +```yaml +name: mobile-ui + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: mobile_ui_mapping + regex_headers: + user-agent: "^(?=.*\\bAndroid\\b)(?=.*\\b(m|M)obile\\b).*|(?=.*\\biPhone\\b)(?=.*\\b(m|M)obile\\b).*$" + prefix: / + service: mobile-ui +``` \ No newline at end of file diff --git a/reference/host.md b/reference/host.md new file mode 100644 index 0000000000..461ca91333 --- /dev/null +++ b/reference/host.md @@ -0,0 +1,68 @@ +# Host Headers + +Ambassador supports several different methods for managing the HTTP `Host` header. + +## Using `host` and `host_regex` + +A mapping that specifies the `host` attribute will take effect _only_ if the HTTP `Host` header matches the value in the `host` attribute. If `host_regex` is `true`, the `host` value is taken to be a regular expression, otherwise an exact string match is required. + +You may have multiple mappings listing the same resource but different `host` attributes to effect `Host`-based routing. An example: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +service: qotm1 +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +host: qotm.datawire.io +service: qotm2 +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +host: "^qotm[2-9]\\.datawire\\.io$" +host_regex: true +service: qotm3 +``` + +will map requests for `/qotm/` to + +- the `qotm2` service if the `Host` header is `qotm.datawire.io`; +- the `qotm3` service if the `Host` header matches `^qotm[2-9]\\.datawire\\.io$`; and to +- the `qotm1` service otherwise. + +Note that enclosing regular expressions in quotes can be important to prevent backslashes from being doubled. + +## Using `host_rewrite` + +By default, the `Host` header is not altered when talking to the service -- whatever `Host` header the client gave to Ambassador will be presented to the service. For many microservices this will be fine, but if you use Ambassador to route to services that use the `Host` header for routing, it's likely to fail (legacy monoliths are particularly susceptible to this, as well as external services). You can use `host_rewrite` to force the `Host` header to whatever value that such target services need. + +An example: the default Ambassador configuration includes the following mapping for `httpbin.org`: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: httpbin_mapping +prefix: /httpbin/ +service: httpbin.org:80 +host_rewrite: httpbin.org +``` + +As it happens, `httpbin.org` is virtually hosted, and it simply _will not_ function without a `Host` header of `httpbin.org`, which means that the `host_rewrite` attribute is necessary here. + +## `host` and `method` + +Internally: + +- the `host` attribute becomes a `header` match on the `:authority` header; and +- the `method` attribute becomes a `header` match on the `:method` header. + +You will see these headers in the diagnostic service if you use the `method` or `host` attributes. diff --git a/reference/mappings.md b/reference/mappings.md new file mode 100644 index 0000000000..526af22362 --- /dev/null +++ b/reference/mappings.md @@ -0,0 +1,210 @@ +# Configuring Services + +Ambassador is designed so that the author of a given Kubernetes service can easily and flexibly configure how traffic gets routed to the service. The core abstraction used to support service authors is a `mapping`. + +## Mappings + +Mappings associate REST [_resources_](#resources) with Kubernetes [_services_](#services). A resource, here, is a group of things defined by a URL prefix; a service is exactly the same as in Kubernetes. Ambassador _must_ have one or more mappings defined to provide access to any services at all. + +Each mapping can also specify, among other things: + +- a [_rewrite rule_](/reference/rewrites) which modifies the URL as it's handed to the Kubernetes service; +- a [_weight_](/reference/canary) specifying how much of the traffic for the resource will be routed using the mapping; +- a [_host_](/reference/host) specifying a required value for the HTTP `Host` header; +- a [_shadow_](/reference/shadowing) marker, specifying that this mapping will get a copy of traffic for the resource; and +- other [_headers_](/reference/headers) which must appear in the HTTP request. + +## Mapping Configuration + +Ambassador supports a number of attributes to configure and customize mappings. + +| Attribute | Description | +| :------------------------ | :------------------------ | +| [`add_request_headers`](/reference/add_request_headers) | specifies a dictionary of other HTTP headers that should be added to each request when talking to the service | +| [`add_response_headers`](/reference/add_response_headers) | specifies a dictionary of other HTTP headers that should be added to each response when returning response to client | +| [`cors`](/reference/cors) | enables Cross-Origin Resource Sharing (CORS) setting on a mapping | +| `enable_ipv4` | if true, enables IPv4 DNS lookups for this mapping's service (the default is set by the [Ambassador module](/reference/modules)) | +| `enable_ipv6` | if true, enables IPv6 DNS lookups for this mapping's service (the default is set by the [Ambassador module](/reference/modules)) | +| [`grpc`](/user-guide/grpc) | if true, tells the system that the service will be handling gRPC calls | +| [`headers`](/reference/headers) | specifies a list of other HTTP headers which _must_ appear in the request for this mapping to be used to route the request | +| [`host`](/reference/host) | specifies the value which _must_ appear in the request's HTTP `Host` header for this mapping to be used to route the request | +| [`host_regex`](/reference/host) | if true, tells the system to interpret the `host` as a [regular expression](http://en.cppreference.com/w/cpp/regex/ecmascript) | +| [`host_rewrite`](/reference/host) | forces the HTTP `Host` header to a specific value when talking to the service | +| [`method`](/reference/method) | defines the HTTP method for this mapping (e.g. GET, PUT, etc. -- must be all uppercase) | +| `method_regex` | if true, tells the system to interpret the `method` as a [regular expression](http://en.cppreference.com/w/cpp/regex/ecmascript) | +| `prefix_regex` | if true, tells the system to interpret the `prefix` as a [regular expression](http://en.cppreference.com/w/cpp/regex/ecmascript) | +| [`rate_limits`](/reference/rate-limits) | specifies a list rate limit rules on a mapping | +| [`regex_headers`](/reference/headers) | specifies a list of HTTP headers and [regular expressions](http://en.cppreference.com/w/cpp/regex/ecmascript) which _must_ match for this mapping to be used to route the request | +| [`rewrite`](/reference/rewrites) | replaces the URL prefix with when talking to the service | +| `timeout_ms` | the timeout, in milliseconds, for requests through this `Mapping`. Defaults to 3000. | +| [`tls`](#using-tls) | if true, tells the system that it should use HTTPS to contact this service. (It's also possible to use `tls` to specify a certificate to present to the service.) | +| `use_websocket` | if true, tells Ambassador that this service will use websockets | + +If both `enable_ipv4` and `enable_ipv6` are set, Ambassador will prefer IPv6 to IPv4. See the [Ambassador module](/reference/modules) documentation for more information. + +Ambassador supports multiple deployment patterns for your services. These patterns are designed to let you safely release new versions of your service, while minimizing its impact on production users. + +| Attribute | Description | +| :------------------------ | :------------------------ | +| [`shadow`](/reference/shadowing) | if true, a copy of the resource's traffic will go the `service` for this `Mapping`, and the reply will be ignored. | +| [`weight`](/reference/canary) | specifies the (integer) percentage of traffic for this resource that will be routed using this mapping | + +These attributes are less commonly used, but can be used to override Ambassador's default behavior in specific cases. + +| Attribute | Description | +| :------------------------ | :------------------------ | +| `auto_host_rewrite` | if true, forces the HTTP `Host` header to the `service` to which Ambassador routes | +| `case_sensitive` | determines whether `prefix` matching is case-sensitive; defaults to True | +| [`envoy_override`](/reference/override) | supplies raw configuration data to be included with the generated Envoy route entry. | +| [`host_redirect`](/reference/redirects) | if true, this `Mapping` performs an HTTP 301 `Redirect`, with the host portion of the URL replaced with the `service` value. | +| [`path_redirect`](/reference/redirects) | if set when `host_redirect` is also true, the path portion of the URL will replaced with the `path_redirect` value in the HTTP 301 `Redirect`. | +| [`precedence`](#a-nameprecedencea-using-precedence) | an integer overriding Ambassador's internal ordering for `Mapping`s. An absent `precedence` is the same as a `precedence` of 0. Higher `precedence` values are matched earlier. | +| `bypass_auth` | if true, tells Ambassador that this service should bypass `ExtAuth` (if configured) | + +The name of the mapping must be unique. If no `method` is given, all methods will be proxied. + +## Example Mappings + +Mapping definitions are fairly straightforward. Here's an example for a REST service which Ambassador will contact using HTTP: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +service: http://qotm +``` + +and a REST service which Ambassador will contact using HTTPS: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: quote_mapping +prefix: /qotm/quote/ +rewrite: /quotation/ +service: https://qotm +``` + +(Note that the 'http://' prefix for an HTTP service is optional.) + +Here's an example for a CQRS service (using HTTP): + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: cqrs_get_mapping +prefix: /cqrs/ +method: GET +service: getcqrs +--- +apiVersion: ambassador/v0 +kind: Mapping +name: cqrs_put_mapping +prefix: /cqrs/ +method: PUT +service: putcqrs +``` + +Required attributes for mappings: + +- `name` is a string identifying the `Mapping` (e.g. in diagnostics) +- `prefix` is the URL prefix identifying your [resource](#resources) +- `service` is the name of the [service](#services) handling the resource; must include the namespace (e.g. `myservice.othernamespace`) if the service is in a different namespace than Ambassador + +## Mapping Evaluation Order + +Ambassador sorts mappings such that those that are more highly constrained are evaluated before those less highly constrained. The prefix length, the request method and the constraint headers are all taken into account. + +If absolutely necessary, you can manually set a `precedence` on the mapping (see below). In general, you should not need to use this feature unless you're using the `regex_headers` or `host_regex` matching features. If there's any question about how Ambassador is ordering rules, the diagnostic service is a good first place to look: the order in which mappings appear in the diagnostic service is the order in which they are evaluated. + +## Optional Fallback Mapping + +Ambassador will respond with a `404 Not Found` to any request for which no mapping exists. If desired, you can define a fallback "catch-all" mapping so all unmatched requests will be sent to an upstream service. + +For example, defining a mapping with only a `/` prefix will catch all requests previously unhandled and forward them to an external service: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: catch-all +prefix: / +service: https://www.getambassador.io +``` + +### Using `precedence` + +Ambassador sorts mappings such that those that are more highly constrained are evaluated before those less highly constrained. The prefix length, the request method and the constraint headers are all taken into account. These mechanisms, however, may not be sufficient to guarantee the correct ordering when regular expressions or highly complex constraints are in play. + +For those situations, a `Mapping` can explicitly specify the `precedence`. A `Mapping` with no `precedence` is assumed to have a `precedence` of 0; the higher the `precedence` value, the earlier the `Mapping` is attempted. + +If multiple `Mapping`s have the same `precedence`, Ambassador's normal sorting determines the ordering within the `precedence`; however, there is no way that Ambassador can ever sort a `Mapping` with a lower `precedence` ahead of one at a higher `precedence`. + +### Using `tls` + +In most cases, you won't need the `tls` attribute: just use a `service` with an `https://` prefix. However, note that if the `tls` attribute is present and `true`, Ambassador will originate TLS even if the `service` does not have the `https://` prefix. + +If `tls` is present with a value that is not `true`, the value is assumed to be the name of a defined TLS context, which will determine the certificate presented to the upstream service. TLS context handling is a beta feature of Ambassador at present; please [contact us on Slack](https://d6e.co/slack) if you need to specify TLS origination certificates. + +## Namespaces and Mappings + +Given that `AMBASSADOR_NAMESPACE` is correctly set, Ambassador can map to services in other namespaces by taking advantage of Kubernetes DNS: + +- `service: servicename` will route to a service in the same namespace as the Ambassador, and +- `service: servicename.namespace` will route to a service in a different namespace. + +## Resources + +To Ambassador, a `resource` is a group of one or more URLs that all share a common prefix in the URL path. For example: + +```shell +https://ambassador.example.com/resource1/foo +https://ambassador.example.com/resource1/bar +https://ambassador.example.com/resource1/baz/zing +https://ambassador.example.com/resource1/baz/zung +``` + +all share the `/resource1/` path prefix, so can be considered a single resource. On the other hand: + +```shell +https://ambassador.example.com/resource1/foo +https://ambassador.example.com/resource2/bar +https://ambassador.example.com/resource3/baz/zing +https://ambassador.example.com/resource4/baz/zung +``` + +share only the prefix `/` -- you _could_ tell Ambassador to treat them as a single resource, but it's probably not terribly useful. + +Note that the length of the prefix doesn't matter: if you want to use prefixes like `/v1/this/is/my/very/long/resource/name/`, go right ahead, Ambassador can handle it. + +Also note that Ambassador does not actually require the prefix to start and end with `/` -- however, in practice, it's a good idea. Specifying a prefix of + +```shell +/man +``` + +would match all of the following: + +```shell +https://ambassador.example.com/man/foo +https://ambassador.example.com/mankind +https://ambassador.example.com/man-it-is/really-hot-today +https://ambassador.example.com/manohmanohman +``` + +which is probably not what was intended. + +## Services + +A `service` is simply a URL to Ambassador. For example: + +- `servicename` assumes that DNS can resolve the bare servicename, and that it's listening on the default HTTP port; +- `servicename.domain` supplies a domain name (for example, you might do this to route across namespaces in Kubernetes); and +- `service:3000` supplies a nonstandard port number. + +At present, Ambassador relies on Kubernetes to do load balancing: it trusts that using the DNS to look up the service by name will do the right thing in terms of spreading the load across all instances of the service. + diff --git a/reference/method.md b/reference/method.md new file mode 100644 index 0000000000..4dddf2e7eb --- /dev/null +++ b/reference/method.md @@ -0,0 +1,23 @@ +# Method-based routing + +Ambassador supports routing based on HTTP method and regular expression. + +## Using `method` + +The `method` annotation specifies the specific HTTP method for a mapping. The value of the `method` annotation must be in all upper case. + +For example: + +```yaml +--- +aapiVersion: ambassador/v0 +kind: Mapping +name: get_mapping +prefix: /get_only/ +method: GET +service: qotm +``` + +## Using `method_regex` + +When `method_regex` is set to `true`, the value of the `method` annotation will be interpreted as a regular expression. \ No newline at end of file diff --git a/reference/modules.md b/reference/modules.md new file mode 100644 index 0000000000..053b74374d --- /dev/null +++ b/reference/modules.md @@ -0,0 +1,123 @@ +# Core Configuration: Modules + +Modules let you enable and configure special behaviors for Ambassador, in ways that may apply to Ambassador as a whole or which may apply only to some mappings. The actual configuration possible for a given module depends on the module. + +## The `ambassador` Module + +If present, the `ambassador` module defines system-wide configuration. **You may very well not need this module.** The defaults in the `ambassador` module are: + +```yaml +--- +apiVersion: ambassador/v1 +kind: Module +name: ambassador +config: + # admin_port is the port where Ambassador's Envoy will listen for + # low-level admin requests. You should almost never need to change + # this. + # admin_port: 8001 + + # default_label_domain and default_labels set a default domain and + # request labels to every request for use by rate limiting. For + # more on how to use these, see the Rate Limit reference. + + # diag_port is the port where Ambassador will listen for requests + # to the diagnostic service. + # diag_port: 8877 + + # The diagnostic service (at /ambassador/v0/diag/) defaults on, but + # you can disable the api route. It will remain accessible on + # diag_port. + # diagnostics: + # enabled: true + + # Should we do IPv4 DNS lookups when contacting services? Defaults to true, + # but can be overridden in a [`Mapping`](/reference/mappings). + # enable_ipv4: true + + # Should we do IPv6 DNS lookups when contacting services? Defaults to false, + # but can be overridden in a [`Mapping`](/reference/mappings). + # enable_ipv6: false + + # liveness probe defaults on, but you can disable the api route. + # It will remain accessible on diag_port. + # liveness_probe: + # enabled: true + + # readiness probe defaults on, but you can disable the api route. + # It will remain accessible on diag_port. + # readiness_probe: + # enabled: true + + # If present, service_port will be the port Ambassador listens + # on for microservice access. If not present, Ambassador will + # use 443 if TLS is configured, 80 otherwise. In future releases + # of Ambassador, this will change to 8080 when we run Ambassador + # as non-root by default. + # service_port: 80 + + # statsd configures Ambassador statistics. These values can be + # set in the Ambassador module or in an environment variable. + # For more information, see the [Statistics reference](/reference/statistics/#exposing-statistics-via-statsd) + + # use_proxy_protocol controls whether Envoy will honor the PROXY + # protocol on incoming requests. + # use_proxy_proto: false + + # use_remote_address controls whether Envoy will trust the remote + # address of incoming connections or rely exclusively on the + # X-Forwarded_For header. + # use_remote_address: true + + # Ambassador lets through only the HTTP requests with + # `X-FORWARDED-PROTO: https` header set, and redirects all the other + # requests to HTTPS if this field is set to true. + # x_forwarded_proto_redirect: false + + # Set default CORS configuration for all mappings in the cluster. See + # CORS syntax at https://www.getambassador.io/reference/cors.html + # cors: + # origins: http://foo.example,http://bar.example + # methods: POST, GET, OPTIONS + # ... + # ... + + # This enables gRPC-Web; a bridge to have Ambassador translate between + # the gRPC-Web protocol and a native gRPC upstream server. + # enable_grpc_web: true +``` + +### `enable_ivp4` and `enable_ipv6` + +If both IPv4 and IPv6 are enabled, Ambassador will prefer IPv6. This can have strange effects if Ambassador receives +`AAAA` records from a DNS lookup, but the underlying network of the pod doesn't actually support IPv6 traffic. For this +reason, the default for 0.50.0 is IPv4 only. + +A `Mapping` can override both `enable_ipv4` and `enable_ipv6`, but if either is not stated explicitly in a `Mapping`, +the values here are used. Most Ambassador installations will probably be able to avoid overridding these setting in `Mapping`s. + +### `use_remote_address` + +In Ambassador 0.50 and later, the default value for `use_remote_address` to `true`. When set to `true`, Ambassador will append to the `X-Forwarded-For` header its IP address so upstream clients of Ambassador can get the full set of IP addresses that have propagated a request. You may also need to set `externalTrafficPolicy: Local` on your `LoadBalancer` as well to propagate the original source IP address.. See the [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http_conn_man/headers.html) and the [Kubernetes documentation](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) for more details. + +### `use_proxy_proto` + +Many load balancers can use the [PROXY protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) to convey information about the connection they are proxying. In order to support this in Ambassador, you'll need to set `use_proxy_protocol` to `true`; this is not the default since the PROXY protocol is not compatible with HTTP. + +### Probes + +The default liveness and readiness probes map `/ambassador/v0/check_alive` and `ambassador/v0/check_ready` internally to check Envoy itself. If you'd like to, you can change these to route requests to some other service. For example, to have the readiness probe map to the Quote of the Moment's health check, you could do + +```yaml +readiness_probe: + service: qotm + rewrite: /health +``` + +The liveness and readiness probe both support `prefix`, `rewrite`, and `service`, with the same meanings as for [mappings](/reference/mappings). Additionally, the `enabled` boolean may be set to `false` (as in the commented-out examples above) to disable support for the probe entirely. + +**Note well** that configuring the probes in the `ambassador` module only means that Ambassador will respond to the probes. You must still configure Kubernetes to perform the checks, as shown in the Datawire-provided YAML files. + +## The `authentication` Module + +The `authentication` module is now deprecated. Use the [AuthService](/reference/services/auth-service) manifest type instead. diff --git a/reference/override.md b/reference/override.md new file mode 100644 index 0000000000..2583b5de5e --- /dev/null +++ b/reference/override.md @@ -0,0 +1,37 @@ +# Customizing Envoy + +You may run into a situation where Ambassador may not yet support a specific Envoy feature. Ambassador supports two different methods for these situations. + +## `envoy_override` + +The `envoy_override` attribute can be used to add specific values to the generated configuration file. Any object given as the value of the attribute will be inserted into the Envoy `Route` for a given mapping. For example, you could enable Envoy's `auto_host_rewrite` by supplying: + +```yaml +envoy_override: + auto_host_rewrite: True +``` + +Here is another example of using `envoy_override` to set Envoy's [connection retries](https://www.envoyproxy.io/docs/envoy/latest/api-v1/route_config/route.html#retry-policy): + +``` +envoy_override: + retry_policy: + retry_on: connect-failure + num_retries: 4 +``` + +Note that `envoy_override` has the following limitations: + +* It is restricted to Envoy v1 configuration only +* It only supports adding information to Envoy routes, and not clusters +* It cannot change any element already synthesized in the mapping + +These limitations will be addressed in future releases of Ambassador. + +## Modifying Ambassador's Underlying Envoy Configuration + +Ambassador ships with a standard configuration template that is used to generate Envoy configuration. If you need to do more extensive modifications, you can create your own custom configuration template to replace the standard template. To do this, create a templated `envoy.json` file using the Jinja2 template language. Then, use this template as the value for the key `envoy.j2` in your ConfigMap. This will then replace the [default template](https://github.com/datawire/ambassador/tree/master/ambassador/templates). + +## File an issue + +If you do need to use one of these options, we'd appreciate if you filed a [GitHub issue](https://github.com/datawire/ambassador/issues/) and/or contacted us on [Slack](https://d6e.co/slack) so we can understand the use case, and add support in the future. Better yet, we'll send you a T-shirt if you open a PR! \ No newline at end of file diff --git a/reference/rate-limits.md b/reference/rate-limits.md new file mode 100644 index 0000000000..1594cfd024 --- /dev/null +++ b/reference/rate-limits.md @@ -0,0 +1,72 @@ +# Rate Limits + +Rate limits are a powerful way to improve availability and scalability for your microservices. With Ambassador, individual requests can be annotated with metadata, called labels. These labels can then be passed to a third party [rate limiting service](/services/rate-limit-service) which can then rate limit based on this data. If you do not want to write your own rate limiting service, [Ambassador Pro](https://www.getambassador.io/pro) includes an integrated, flexible rate limiting service. + +## Request labels + +In Ambassador 0.50 and later, each mapping in Ambassador can have multiple *labels* which annotate a given request. These labels are then passed to a rate limiting service through a gRPC interface. These labels are specified with the `labels` annotation: + +``` +apiVersion: ambassador/v1 +kind: Mapping +name: catalog +prefix: /catalog/ +service: catalog +labels: + ambassador: + - string_request_label: # a specific request label group + - catalog # annotate the request with the string `catalog` + - header_request_label: + - headerkey: # The name of the label + header: ":method" # annotate the request with the specific HTTP method used + omit_if_not_present: true # if the header is not present, omit the label + - multi_request_label_group: + - authorityheader: + header: ":authority" + omit_if_not_present: true + - xuserheader: + header: "x-user" + omit_if_not_present: true +``` + +Let's digest the above example: + +* Request labels must be part of the `ambassador` namespace. This limitation will be removed in future versions of Ambassador. +* Each label must have a name, e.g., `one_request_label` +* The `string_request_label` simply adds the string `catalog` to every incoming request to the given mapping. The string is referenced with the key `generic_key`. +* The `header_request_label` adds a specific HTTP header value to the request, in this case, the method. Note that HTTP/2 request headers must be used here (e.g., the `host` header needs to be specified as the `:authority` header). +* Multiple labels can be part of a single named label, e.g., `multi_request_label` specifies two different headers to be added +* When an HTTP header is not present, the entire named label is omitted. The `omit_if_not_present: true` is an explicit notation to remind end users of this limitation. `false` is *not* a supported value. This limitation will be removed in future versions of Ambassador. + +Ambassador supports several special labels: + +* `remote_address` automatically populates the remote IP address using the trusted IP address from `X-Forwarded-For` +* `request_headers: HEADER` will extract the value from a given HTTP header +* `destination_cluster` populates the name of the Envoy cluster. Typically, there is a 1:1 correspondence between a `service` in a `Mapping` to a `destination_cluster`. You can get the name of the cluster from the diagnostics service. +* `source_cluster` populates the name of the originating cluster (e.g., the Envoy listener). + +Note: In Envoy, labels are referred to as descriptors. + +## The `rate_limits` attribute + +In pre-0.50 versions of Ambassador, a mapping can specify the `rate_limits` list attribute and at least one `rate_limits` rule which will call the external [RateLimitService](/reference/services/rate-limit-service) before proceeding with the request. An example: + +```yaml +apiVersion: ambassador/v0 +kind: Mapping +name: rate_limits_mapping +prefix: /rate-limit/ +service: rate-limit-example +rate_limits: + - {} + - descriptor: a rate-limit descriptor + headers: + - matching-header +``` + +Rate limit rule settings: + +- `descriptor`: if present, specifies a string identifying the triggered rate limit rule. This descriptor will be sent to the `RateLimitService`. +- `headers`: if present, specifies a list of other HTTP headers which **must** appear in the request for the rate limiting rule to apply. These headers will be sent to the `RateLimitService`. + +As with request labels, you must use the internal HTTP/2 request header names in `rate_limits` rules. \ No newline at end of file diff --git a/reference/redirects.md b/reference/redirects.md new file mode 100644 index 0000000000..822c8a98f7 --- /dev/null +++ b/reference/redirects.md @@ -0,0 +1,50 @@ +# Redirects + +### Host Redirect + +To effect an HTTP 301 `Redirect`, the `Mapping` **must** set `host_redirect` to `true`, with `service` set to the host to which the client should be redirected: + +```yaml +apiVersion: ambassador/v0 +kind: Mapping +name: redirect_mapping +prefix: /redirect/ +service: httpbin.org +host_redirect: true +``` + +Using this `Mapping`, a request to `http://$AMBASSADOR_URL/redirect/` will result in an HTTP 301 `Redirect` to `http://httpbin.org/redirect/`. + +The `Mapping` **may** also set `path_redirect` to change the path portion of the URL during the redirect: + +```yaml +apiVersion: ambassador/v0 +kind: Mapping +name: redirect_mapping +prefix: /redirect/ +service: httpbin.org +host_redirect: true +path_redirect: /ip +``` + +Here, a request to `http://$AMBASSADOR_URL/redirect/` will result in an HTTP 301 `Redirect` to `http://httpbin.org/ip`. As always with Ambassador, attention paid to the trailing `/` on a URL is helpful! + +## X-FORWARDED-PROTO Redirect + +In cases when TLS is being terminated at an external layer 7 load balancer, then you would want to redirect only the originating HTTP requests to HTTPS, and let the originating HTTPS requests pass through. + +This distinction between an originating HTTP request and an originating HTTPS request is done based on the `X-FORWARDED-PROTO` header that the external layer 7 load balancer adds to every request it forwards after TLS termination. + +To enable this `X-FORWARDED-PROTO` based HTTP to HTTPS redirection, add a `x_forwarded_proto_redirect: true` field to ambassador module's configuration. + +An example configuration is as follows - + +```yaml +apiVersion: ambassador/v0 +kind: Module +name: ambassador +config: + x_forwarded_proto_redirect: true +``` + +Note: Setting `x_forwarded_proto_redirect: true` will impact all your ambassador mappings. Every HTTP request to ambassador will only be allowed to pass if it has an `X-FORWARDED-PROTO: https` header. \ No newline at end of file diff --git a/reference/rewrites.md b/reference/rewrites.md new file mode 100644 index 0000000000..be3d22e195 --- /dev/null +++ b/reference/rewrites.md @@ -0,0 +1,38 @@ +# Rewrites + +Once Ambassador uses a prefix to identify the service to which a given request should be passed, it can rewrite the URL before handing it off to the service. By default, the `prefix` is rewritten to `/`, so e.g., if we map `/prefix1/` to the service `service1`, then + +```shell +http://ambassador.example.com/prefix1/foo/bar +``` + +would effectively be written to + +```shell +http://service1/foo/bar +``` + +when it was handed to `service1`. + +You can change the rewriting: for example, if you choose to rewrite the prefix as `/v1/` in this example, the final target would be: + +```shell +http://service1/v1/foo/bar +``` + +And, of course, you can choose to rewrite the prefix to the prefix itself, so that + +```shell +http://ambassador.example.com/prefix1/foo/bar +``` + +would be "rewritten" as: + +```shell +http://service1/prefix1/foo/bar +``` + +Ambassador can be configured to not change the prefix as it forwards a request to the upstream service. To do that, specify an empty `rewrite` directive: + +- `rewrite: ""` + diff --git a/reference/running.md b/reference/running.md new file mode 100644 index 0000000000..e9d721327a --- /dev/null +++ b/reference/running.md @@ -0,0 +1,250 @@ +# Running Ambassador + +This section is intended for operators running Ambassador, and covers various aspects of deploying and configuring Ambassador in production. + +## Ambassador and Kubernetes + +Ambassador relies on Kubernetes for reliability, availability, and scalability. This means that features such as Kubernetes readiness and liveness probes, rolling updates, and the Horizontal Pod Autoscaling should be utilized to manage Ambassador. + +## Default configuration + +The default configuration of Ambassador includes default [resource limits](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container), as well as [readiness and liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/). These values should be adjusted for your specific environment. + +## Running as non-root + +Starting with Ambassador 0.35, we support running Ambassador as non-root. This is the recommended configuration, and will be the default configuration in future releases. We recommend you configure Ambassador to run as non-root as follows: + +* Have Kubernetes run Ambassador as non-root. This may happen by default (e.g., OpenShift) or you can set a `securityContext` in your Deployment as shown below in this abbreviated example: + +```yaml +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: ambassador +spec: + replicas: 1 + template: + metadata: + labels: + service: ambassador + spec: + containers: + image: quay.io/datawire/ambassador:0.35.0 + name: ambassador + restartPolicy: Always + securityContext: + runAsUser: 8888 + serviceAccountName: ambassador +``` + +* Set the `service_port` element in the ambassador Module to 8080 (cleartext) or 8443 (TLS). This is the port that Ambassador will use to listen to incoming traffic. Note that any port number above 1024 will work; Ambassador will use 8080/8443 as its defaults in the future. + +* Make sure that incoming traffic to Ambassador is configured to route to the `service_port`. If you're using the default Ambassador configuration, this means configuring the `targetPort` to point to the `service_port` above. + +* If you are using `redirect_cleartext_from`, change the value of this field to point to your cleartext port (e.g., 8080) and set `service_port` to be your TLS port (e.g., 8443). + +## Changing the configuration directory + +While running, Ambassador needs to use a directory within its container for generated configuration data. +Normally this is `/ambassador`, but in some situations - especially if running as non-root - it may be necessary to +change to a different directory. To do so, set the environment variable `AMBASSADOR_CONFIG_BASE_DIR` to the full +pathname of the directory to use, as shown below in this abbreviated example: + +```yaml +env: +- name: AMBASSADOR_CONFIG_BASE_DIR + value: /tmp/ambassador-config +``` + +With `AMBASSADOR_CONFIG_BASE_DIR` set as above, Ambassador will create and use the directory `/tmp/ambassador-config` +for its generated data. (Note that, while the directory will be created if it does not exist, attempts to turn an +existing file into a directory will fail.) + +## Running as daemonset + +Ambassador can be deployed as daemonset to have one pod per node in Kubernetes cluster. This setup up is especially helpful when you have Kubernetes cluster running on bare metal or private cloud. + +* Ideal scenario could be when you are running containers on Kubernetes along side with your non containerized applications running exposed via VIP using BIG-IP or similar products. In such cases, east-west traffic is routed based on iRules to certain set of application pools consisting of application or web servers. In this setup, along side of traditonal application servers, two or more Ambassador pods can also be part of the application pools. In case of failure there is atleast one Ambassdor pod available to BIG-IP and can take care of routing traffic to kubernetes cluster. + +* In manifest files `kind: Deployment` needs to be updated to `kind: DaemonSet` and `replicas` should be removed in `spec`section. + +## Namespaces + +Ambassador supports multiple namespaces within Kubernetes. To make this work correctly, you need to set the `AMBASSADOR_NAMESPACE` environment variable in Ambassador's container. By far the easiest way to do this is using Kubernetes' downward API (this is included in the YAML files from `getambassador.io`): + +```yaml +env: +- name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +``` + +Given that `AMBASSADOR_NAMESPACE` is set, Ambassador [mappings](/reference/mappings) can operate within the same namespace, or across namespaces. **Note well** that mappings will have to explicitly include the namespace with the service to cross namespaces; see the [mapping](/reference/mappings) documentation for more information. + +If you only want Ambassador to only work within a single namespace, set `AMBASSADOR_SINGLE_NAMESPACE` as an environment variable. + +``` +env: +- name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace +- name: AMBASSADOR_SINGLE_NAMESPACE + value: "true" +``` + +## Multiple Ambassadors in One Cluster + +Ambassador supports running multiple Ambassadors in the same cluster, without restricting a given Ambassador to a single namespace. This is done with the `AMBASSADOR_ID` setting. In the Ambassador module, set the `ambassador_id`, e.g., + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: ambassador + namespace: ambassador-1 + labels: + app: ambassador + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: ambassador + ambassador_id: ambassador-1 +``` + +Then, assign each Ambassador pod a unique `AMBASSADOR_ID` with the environment variable as part of your deployment: + +```yaml +env: +- name: AMBASSADOR_ID + value: ambassador-1 +``` + +Ambassador will then only use YAML objects that include an appropriate `ambassador_id` attribute. For example, if Ambassador is given the ID `ambassador-1` as above, then of these YAML objects, only the first two will be used: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: mapping_used_1 +ambassador_id: ambassador-1 +prefix: /demo1/ +service: demo1 +--- +apiVersion: ambassador/v0 +kind: Mapping +name: mapping_used_2 +ambassador_id: [ "ambassador-1", "ambassador-2" ] +prefix: /demo2/ +service: demo2 +--- +apiVersion: ambassador/v0 +kind: Mapping +name: mapping_skipped_1 +prefix: /demo3/ +service: demo3 +--- +apiVersion: ambassador/v0 +kind: Mapping +name: mapping_skipped_2 +ambassador_id: ambassador-2 +prefix: /demo4/ +service: demo4 +``` + +The list syntax (shown in `mapping_used_2` above) permits including a given object in the configuration for multiple Ambassadors. In this case `mapping_used_2` will be included in the configuration for `ambassador-1` and also for `ambassador-2`. + +**Note well that _any_ object can and should have an `ambassador_id` included** so, for example, it is _fully supported_ to use `ambassador_id` to qualify the `ambassador Module`, `TLS`, and `AuthService` objects. You will need to set Ambassador_id in all resources you want to use for Ambassador. + +If no `AMBASSADOR_ID` is assigned to an Ambassador, it will use the ID `default`. If no `ambassador_id` is present in a YAML object, it will also use the ID `default`. + +## `AMBASSADOR_VERIFY_SSL_FALSE` + +By default, Ambassador will verify the TLS certificates provided by the Kubernetes API. In some situations, the cluster may be deployed with self-signed certificates. In this case, set `AMBASSADOR_VERIFY_SSL_FALSE` to `true` to disable verifying the TLS certificates. + +## Reconfiguration Timing Configuration + +Ambassador is constantly watching for changes to the service annotations. When changes are observed, Ambassador generates a new Envoy configuration and restarts the Envoy handling the heavy lifting of routing. Three environment variables provide control over the timing of this reconfiguration: + +- `AMBASSADOR_RESTART_TIME` (default 15) sets the minimum number of seconds between restarts. No matter how often services are changed, Ambassador will never restart Envoy more frequently than this. + +- `AMBASSADOR_DRAIN_TIME` (default 5) sets the number of seconds that the Envoy will wait for open connections to drain on a restart. Connections still open at the end of this time will be summarily dropped. + +- `AMBASSADOR_SHUTDOWN_TIME` (default 10) sets the number of seconds that Ambassador will wait for the old Envoy to clean up and exit on a restart. **If Envoy is not able to shut down in this time, the Ambassador pod will exit.** If this happens, it is generally indicative of issues with restarts being attempted too often. + +These environment variables can be set much like `AMBASSADOR_NAMESPACE`, above. + +## Configuration From the Filesystem + +If desired, Ambassador can be configured from YAML files in the directory `$AMBASSADOR_CONFIG_BASE_DIR/ambassador-config` (by default, `/ambassador/ambassador-config`, which is empty in the images built by Datawire). You could volume mount an external configuration directory here, for example, or use a custom Dockerfile to build configuration directly into a Docker image. + +Note well that while Ambassador will read its initial configuration from this directory, configuration loaded from Kubernetes annotations will _replace_ this initial configuration. If this is not what you want, you will need to set the environment variable `AMBASSADOR_NO_KUBEWATCH` so that Ambassador will not try to update its configuration from Kubernetes resources. + +Also note that the YAML files in the configuration directory must contain Ambassador resources, not Kubernetes resources with annotations. + +## Ambassador Update Checks (Scout) + +Ambassador integrates Scout, a service that periodically checks with Datawire servers to advise of available updates. Scout also sends anonymized usage data and the Ambassador version. This information is important to us as we prioritize test coverage, bug fixes, and feature development. Note that Ambassador will run regardless of the status of Scout (i.e., our uptime has zero impact on your uptime.) + +We do not recommend you disable Scout, since we use this mechanism to notify users of new release (including critical fixes and security issues). This check can be disabled by setting the environment +variable `SCOUT_DISABLE` to `1` in your Ambassador deployment. + +Each Ambassador installation generates a unique cluster ID based on the UID of its Kubernetes namespace and +its Ambassador ID: the resulting cluster ID is a UUID which cannot be used to reveal the namespace name nor +Ambassador ID itself. Ambassador needs RBAC permission to get namespaces for this purpose, as shown in the +default YAML files provided by Datawire; if not granted this permission it will generate a UUID based only on +the Ambassador ID. To disable cluster ID generation entirely, set the environment variable `AMBASSADOR_CLUSTER_ID` +to a UUID that will be used for the cluster ID. + +Unless disabled, Ambassador will also report the following anonymized information back to Datawire: + +| Attribute | Type | Description | +| :------------------------ | :---- | :------------------------ | +| `cluster_count` | int | total count of clusters in use | +| `cluster_grpc_count` | int | count of clusters using GRPC upstream | +| `cluster_http_count` | int | count of clusters using HTTP or HTTPS upstream | +| `cluster_tls_count` | int | count of clusters originating TLS | +| `custom_ambassador_id` | bool | has the `ambassador_id` been changed from 'default'? | +| `custom_diag_port` | bool | has the diag port been changed from 8877? | +| `custom_listener_port` | bool | has the listener port been changed from 80/443? | +| `diagnostics` | bool | is the diagnostics service enabled? | +| `endpoint_grpc_count` | int | count of endpoints to which Ambassador will originate GRPC | +| `endpoint_http_count` | int | count of endpoints to which Ambassador will originate HTTP or HTTPS | +| `endpoint_tls_count` | int | count of endpoints to which Ambassador will originate TLS | +| `extauth` | bool | is extauth enabled? | +| `extauth_allow_body` | bool | will Ambassador send the body to extauth? | +| `extauth_host_count` | int | count of extauth hosts in use | +| `extauth_proto` | str | extauth protocol in use ('http', 'grpc', or `null` if not active) | +| `group_canary_count` | int | count of Mapping groups that include more than one Mapping | +| `group_count` | int | total count of Mapping groups in use (length of the route table) | +| `group_header_match_count` | int | count of groups using header matching (including `host` and `method`) | +| `group_host_redirect_count` | int | count of groups using host_redirect | +| `group_host_rewrite_count` | int | count of groups using host_rewrite | +| `group_precedence_count` | int | count of groups that explicitly set the precedence of the group | +| `group_regex_header_count` | int | count of groups using regex header matching | +| `group_regex_prefix_count` | int | count of groups using regex prefix matching | +| `group_shadow_count` | int | count of groups using shadows | +| `listener_count` | int | count of active listeners (1 unless `redirect_cleartext_from` is in use) | +| `liveness_probe` | bool | are liveness probes enabled? | +| `ratelimit` | bool | is rate limiting in use? | +| `ratelimit_custom_domain` | bool | has the rate limiting domain been changed from 'ambassador'? | +| `ratelimit_data_plane_proto` | bool | is rate limiting using the data plane proto? | +| `readiness_probe` | bool | are readiness probes enabled? | +| `statsd` | bool | is statsd enabled? | +| `tls_origination_count` | int | count of TLS origination contexts | +| `tls_termination_count` | int | count of TLS termination contexts | +| `tls_using_contexts` | bool | is the old TLS module in use? | +| `tls_using_module` | bool | are new TLSContext resources in use? | +| `tracing` | bool | is tracing in use? | +| `tracing_driver` | str | tracing driver in use ('zipkin', 'lightstep', or `null` if not active) | +| `use_proxy_proto` | bool | is the `PROXY` protocol in use? | +| `use_remote_address` | bool | is Ambassador honoring remote addresses? | +| `x_forwarded_proto_redirect` | bool | is Ambassador redirecting based on `X-Forwarded-Proto`? | + +To completely disable feature reporting, set the environment variable `AMBASSADOR_DISABLE_FEATURES` to any non-empty +value. + diff --git a/reference/services/access-control.md b/reference/services/access-control.md new file mode 100644 index 0000000000..15f34050fa --- /dev/null +++ b/reference/services/access-control.md @@ -0,0 +1,94 @@ +# Access Control +--- +Ambassador Pro authentication is managed with the `Policy` custom resource definition (CRD). This resource allows you to specify which routes should and should not be authenticated by the authentication service. By default, all routes require authentication from the IDP with either a JWT or via a login service. + +## Authentication Policy +A `rule` for the `Policy` CRD is a set of hosts, paths, and permission settings that indicate which routes require authentication from Ambassador Pro as well as the access rights that particular API needs. The default rule is to require authentication from all paths on all hosts. + +### Rule Configuration Values +| Value | Example | Description | +| ----- | ------- | ----------- | +| `host` | "*", "foo.com" | the Host that a given rule should match | +| `path` | "/foo/url/" | the URL path that a given rule should match to | +| `public` | true | a boolean that indicates whether or not authentication is required; default false | +| `scopes` | openid | the rights that need to be granted in a given API. Not all APIs will need a scope defined.
    e.g. `scope: openid` is required for OIDC conformant authentication servers | + +### Examples +The following policy is shown in the [OAuth/OIDC Authentication](/user-guide/oauth-oidc-auth#test-the-auth0-application) guide and is used to secure the example `httpbin` service. + +``` +apiVersion: getambassador.io/v1beta1 +kind: Policy +metadata: + name: httpbin-policy +spec: + rules: + - host: "*" + path: /httpbin/ip + public: true + scope: openid + - host: "*" + path: /httpbin/user-agent/* + public: false + scope: openid + - host: "*" + path: /httpbin/headers/* + scope: openid +``` +The `Policy` defines rules based on matching the `host` and `path` to a request and refers to the `public` attribute to decide whether or not it needs to be authenticated. Since both `host` and `path` support wildcards, it is easy to configure an entire mapping to need to be authenticated or not. + +``` +apiVersion: getambassador.io/v1beta1 +kind: Policy +metadata: + name: mappings-policy +spec: + rules: + - host: "*" + path: /httpbin/* + public: true + - host: + path: /qotm/* + public: false + - host: "*" + path: /* + public: false +``` +The above `policy` configures Ambassador Pro authentication to + +1. Not require authentication for the `httpbin` mapping. +2. Require authentication for the `qotm` mapping. +3. Explicitly express the default requiring authentication for all routes. + +#### Mutliple Domains + +``` +apiVersion: getambassador.io/v1beta1 +kind: Policy +metadata: + name: multi-domain-policy +spec: + rules: + - host: foo.bar.com + path: /qotm/ + public: true + - host: example.com + path: /qotm/ + public: false +``` +Imagine you have multiple domains behind Ambassador Pro. A domain `foo.bar.com` and `example.com`. Imagine a service named `qotm` sits behind both of these domains, you want `foo.bar.com` to have public access to `qotm` without authenticating but requests from `example.com` require authentication. The above mapping will accomplish this. + +#### Pass-Through by Default +``` +--- +apiVersion: getambassador.io/v1beta1 +kind: Policy +metadata: + name: default-policy +spec: + rules: + - host: "*" + path: /* + public: true +``` +This policy will change the default to not require authentication for all routes. **Note** Rules applied to higher-level paths, e.g. `/qotm/`, will take precedence over ones applied to lower-level paths, e.g `/`. \ No newline at end of file diff --git a/reference/services/auth-service.md b/reference/services/auth-service.md new file mode 100644 index 0000000000..d9ee7689a3 --- /dev/null +++ b/reference/services/auth-service.md @@ -0,0 +1,148 @@ +# Authentication + +Ambassador supports a highly flexible mechanism for authentication. An `AuthService` manifest configures Ambassador to use an external service to check authentication and authorization for incoming requests. Each incoming request is authenticated before routing to its destination. + +There are currently two supported versions of the `AuthService` manifest: + +### V1 (Ambassador 0.50.0 and higher): + +`AuthService` V1, introduced in Ambassador 0.50, allows you to separately configure the headers that will be sent from the client to the auth service, and from the auth service to the upstream service. You should use `AuthService` V1 for any new deployment of Ambassador 0.50 or higher. + +```yaml +--- +apiVersion: ambassador/v1 +kind: AuthService +name: authentication +auth_service: "example-auth:3000" +path_prefix: "/extauth" +proto: http +allowed_request_headers: +- "x-example-header" +allowed_authorization_headers: +- "x-qotm-session" +allow_request_body: false +``` + +- `proto` (optional) specifies the protocol to use when communicating with the auth service. Valid options are `http` (default) or `grpc`. + +- `allowed_request_headers` (optional) lists headers that will be sent from the client to the auth service. These headers are always included: + * `Authorization` + * `Cookie` + * `From` + * `Proxy-Authorization` + * `User-Agent` + * `X-Forwarded-For` + * `X-Forwarded-Host` + * `X-Forwarded-Proto` + +- `allowed_authorization_headers` (optional) lists headers that will be sent from the auth service to the upstream service when the request is allowed. These headers are always included: + * `Location` + * `Authorization` + * `Proxy-Authenticate` + * `Set-cookie` + * `WWW-Authenticate` + +- `allow_request_body` (optional) will pass the full body of the request to the auth service for use cases such as computing an HMAC or request signature. + +### v0 (Ambassador versions prior to 0.50.0) + +`AuthService` V0 was current prior to Ambassador 0.50.0. It is deprecated and support for V0 will be removed in a future Ambassador release. + +```yaml +--- +apiVersion: ambassador/v0 +kind: AuthService +name: authentication +auth_service: "example-auth:3000" +path_prefix: "/extauth" +allowed_headers: +- "x-qotm-session" +``` + +- `auth_service` gives the URL of the authentication service +- `path_prefix` (optional) gives a prefix prepended to every request going to the auth service +- `allowed_headers` (optional) gives an array of headers that will be incorporated into the upstream request if the auth service supplies them. + +## Multiple AuthService resources + +You may use multiple `AuthService` manifests to round-robin authentication requests among multiple services. **Note well that all services must use the same `path_prefix` and header definitions;** if you try to have different values, you'll see an error in the diagnostics service, telling you which value is being used. + +## Using the AuthService API + +By design, the AuthService interface is highly flexible. The authentication service is the first external service invoked on an incoming request (e.g., it runs before the rate limit filter). Because the logic of authentication is encapsulated in an external service, you can use this to support a wide variety of use cases. For example: + +* Supporting traditional SSO authentication protocols, e.g., OAuth, OpenID Connect, etc. +* Support HTTP basic authentication (sample implementation available at: https://github.com/datawire/ambassador-auth-httpbasic) +* Only authenticating requests that are under a rate limit, and rejecting authentication requests above the rate limit +* Authenticating specific services (URLs), and not others + +## AuthService and TLS + +You can tell Ambassador to use TLS to talk to your service by using an `auth_service` with an `https://` prefix. However, you may also provide a `tls` attribute: if `tls` is present and `true`, Ambassador will originate TLS even if the `service` does not have the `https://` prefix. + +If `tls` is present with a value that is not `true`, the value is assumed to be the name of a defined TLS context, which will determine the certificate presented to the upstream service. TLS context handling is a beta feature of Ambassador at present; please [contact us on Slack](https://d6e.co/slack) if you need to specify TLS origination certificates. + +## The External Authentication Service + +The external auth service receives information about every request through Ambassador, and must indicate whether the request is to be allowed, or not. If not, the external auth service provides the response which is to be handed back to the client. The control flow for Authentication is shown below. + +![Authentication flow](/images/auth-flow.png) + +### The Request + +For every incoming request, the HTTP `method` and headers are forwarded to the auth service. Only two changes are made: + +1. The `Content-Length` header is overwritten with `0`. +2. The body is removed. + +So, for example, if the incoming request is + +``` +PUT /path/to/service HTTP/1.1 +Host: myservice.example.com:80 +User-Agent: curl/7.54.0 +Accept: */* +Content-Type: application/json +Content-Length: 27 + +{ "greeting": "hello world!", "spiders": "OMG no" } +``` + +then the request Ambassador will make of the auth service is: + +``` +PUT /path/to/service HTTP/1.1 +Host: extauth.example.com:80 +User-Agent: curl/7.54.0 +Accept: */* +Content-Type: application/json +Content-Length: 0 +``` + +**ALL** request methods will be proxied, which implies that the auth service must be able to handle any request that any client could make. If desired, Ambassador can add a prefix to the path before forwarding it to the auth service; see the example below. + +### Allowing the Request to Continue (HTTP status code 200) + +To tell Ambassador that the request should be allowed, the external auth service must return an HTTP status of 200. **Note well** that **only** 200 indicates success; other 2yz status codes will prevent the request from continuing, as below. + +The 200 response should not contain any body, but may contain arbitrary headers. Any header present in the response that is also listed in the `allow_headers` attribute of the `AuthService` resource will be copied from the external auth response into the request going upstream. This allows the external auth service to inject tokens or other information into the request, or to modify headers coming from the client. + +### Preventing the Request from Continuing (any HTTP status code other than 200) + +Any HTTP status code other than 200 from the external auth service tells Ambassador **not** to allow the request to continue. In this case, the entire response from the external auth service - including the status code, the headers, and the body - is handed back to the client verbatim. This gives the external auth service **complete** control over the entire response presented to the client. + +Giving the external auth service control over the response on failure allows many different types of auth mechanisms, for example: + +- The external auth service can simply return an error page with an HTTP 401 response. +- The external auth service can choose to include a `WWW-Authenticate` header in the 401 response, to ask the client to perform HTTP Basic Auth. +- The external auth service can issue a 301 `Redirect` to divert the client into an OAuth or OIDC authentication sequence. + +Finally, if Ambassador cannot reach the auth service at all, it will return a HTTP 503 status code to the client. + +## Configuring Public Mappings + +Authentication can be disabled for a mapping by setting `bypass_auth` to `true`. This will tell Ambassador to allow all requests for that mapping through without interacting with the external auth service. + +## Example + +See [the Ambassador Authentication Tutorial](/user-guide/auth-tutorial) for an example. diff --git a/reference/services/rate-limit-service.md b/reference/services/rate-limit-service.md new file mode 100644 index 0000000000..48e33ac2bf --- /dev/null +++ b/reference/services/rate-limit-service.md @@ -0,0 +1,93 @@ +# Rate Limiting with the RateLimitService + +Rate limiting is a powerful technique to improve the [availability and resilience of your services](https://blog.getambassador.io/rate-limiting-a-useful-tool-with-distributed-systems-6be2b1a4f5f4). In Ambassador, each request can have one or more *labels*. These labels are exposed to a third party service via a gRPC API. The third party service can then rate limit requests based on the request labels. + +## Request labels + +Ambassador lets users add one or more labels to a given request. These labels are added as part of a `Mapping` object. For example: + +``` +apiVersion: ambassador/v0 +kind: Mapping +name: catalog +prefix: /catalog/ +service: catalog +request_labels: + - service: catalog +``` + +For more information on request labels, see the [Rate Limit reference](/reference/rate-limits). + +## Domains + +In Ambassador, each engineer (or team) can be assigned its own *domain*. A domain is a separate namespace for labels. By creating individual domains, each team can assign their own labels to a given request, and independently set the rate limits based on their own labels. + +## Default labels + +Ambassador allows setting a default label on every request. A default label is set on the `ambassador` module. For example: + +``` +--- +apiVersion: ambassador/v1 +kind: Module +name: ambassador +config: + default_label_domain: ambassador + default_labels: + ambassador: + defaults: + - remote_address +``` + +## External Rate Limit Service + +In order for Ambassador to rate limit, you need to implement a gRPC service that supports the Envoy [ratelimit.proto](https://github.com/datawire/ambassador/blob/master/ambassador/common/ratelimit/ratelimit.proto) interface. If you do not have the time or resources to implement your own rate limit service, [Ambassador Pro](/pro) integrates a high performance, rate limiting service. + +Ambassador generates a gRPC request to the external rate limit service and provides a list of labels on which the rate limit service can base its decision to accept or reject the request: + +``` +[ + {"source_cluster", ""}, + {"destination_cluster", ""}, + {"remote_address", ""}, + {"generic_key", ""}, + {"", ""} +] +``` + +If Ambassador cannot contact the rate limit service, it will allow the request to be processed as if there were no rate limit service configuration. + +It is the external rate limit service's responsibility to determine whether rate limiting should take place, depending on custom business logic. The rate limit service must simply respond to the request with an `OK` or `OVER_LIMIT` code: +* If Envoy receives an `OK` response from the rate limit service, then Ambassador allows the client request to resume being processed by the normal Ambassador Envoy flow. +* If Ambassador receives an `OVER_LIMIT` response, then Ambassador will return an HTTP 429 response to the client and will end the transaction flow, preventing the request from reaching the backing service. + +The headers injected by the [AuthService](/reference/services/auth-service) can also be passed to the rate limit service since the `AuthService` is invoked before the `RateLimitService`. + +## Configuring the Rate Limit Service + +A `RateLimitService` manifest configures Ambassador to use an external service to check and enforce rate limits for incoming requests: + +```yaml +--- +apiVersion: ambassador/v0 +kind: RateLimitService +name: ratelimit +service: "example-rate-limit:5000" +``` + +- `service` gives the URL of the rate limit service. + +You may only use a single `RateLimitService` manifest. + +## Example + +The [Ambassador Rate Limiting Tutorial](/user-guide/rate-limiting-tutorial) has a simple rate limiting example. For a more advanced example, read the [advanced rate limiting tutorial](/user-guide/advanced-rate-limiting) with Ambassador Pro tutorial. + +## Further reading + +* [Rate limiting: a useful tool with distributed systems](https://blog.getambassador.io/rate-limiting-a-useful-tool-with-distributed-systems-6be2b1a4f5f4) +* [Rate limiting for API Gateways](https://blog.getambassador.io/rate-limiting-for-api-gateways-892310a2da02) +* [Implementing a Java Rate Limiting Service for Ambassador](https://blog.getambassador.io/implementing-a-java-rate-limiting-service-for-the-ambassador-api-gateway-e09d542455da) +* [Designing a Rate Limit Service for Ambassador](https://blog.getambassador.io/designing-a-rate-limiting-service-for-ambassador-f460e9fabedb) + + diff --git a/reference/services/services.md b/reference/services/services.md new file mode 100644 index 0000000000..32a2fbdb60 --- /dev/null +++ b/reference/services/services.md @@ -0,0 +1,9 @@ +# Services + +You may need an API Gateway to enforce policies specific to your organization. Ambassador supports custom policies through external services or plugins. The policy logic specific to your organization is implemented in the external service, and Ambassador is configured to send RPC requests to your service. + +Currently, Ambassador supports plugins for authentication, rate limiting and tracing. + +* [AuthService](/reference/services/auth-service) +* [RateLimitService](/reference/services/rate-limit-service) +* [TracingService](/reference/services/tracing-service) diff --git a/reference/services/tracing-service.md b/reference/services/tracing-service.md new file mode 100644 index 0000000000..4566d02f9d --- /dev/null +++ b/reference/services/tracing-service.md @@ -0,0 +1,45 @@ +# Distributed Tracing + +Applications that consist of multiple services can be difficult to debug, as a single request can span multiple services. Distributed tracing tells the story of your request as it is processed through your system. Distributed tracing is a powerful tool to debug and analyze your system in addition to request logging and [metrics](/reference/statistics). + +## The TracingService + +When enabled, the `TracingService` will instruct Ambassador to initiate a trace on requests by generating and populating an `x-request-id` HTTP header. Services can make use of this `x-request-id` header in logging and forward it in downstream requests for tracing. Ambassador also integrates with external trace visualization services, including [LightStep](https://lightstep.com/) and Zipkin-compatible APIs such as [Zipkin](https://zipkin.io/) and [Jaeger](https://github.com/jaegertracing/) to allow you to store and visualize traces. You can read further on [Envoy's Tracing capabilities](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/tracing). + +A `TracingService` manifest configures Ambassador to use an external trace visualization service: + +```yaml +--- +apiVersion: ambassador/v0 +kind: TracingService +name: tracing +service: "example-zipkin:9411" +driver: zipkin +config: {} +tag_headers: +- ":authority" +- ":path" +``` + +- `service` gives the URL of the external HTTP trace service. +- `driver` provides the driver information that handles communicating with the `service`. Supported values are `lightstep` and `zipkin`. +- `config` provides additional configuration options for the selected `driver`. +- `tag_headers` (optional) if present, specifies a list of other HTTP request headers which will be used as tags in the trace's span. + +Please note that you must use the HTTP/2 preudo-header names. For example: +- the `host` header should be specified as the `:authority` header; and +- the `method` header should be specified as the `:method` header. + +### `lightstep` driver configurations: +- `access_token_file` provides the location of the file containing the access token to the LightStep API. + +### `zipkin` driver configurations: +- `collector_endpoint` gives the API endpoint of the Zipkin service where the spans will be sent. The default value is `/api/v1/spans` +- `trace_id_128bit` whether a 128bit trace id will be used when creating a new trace instance. The default value is `false`, which will result in a 64 bit trace id being used. +- `shared_span_context` whether client and server spans will shared the same span id. The default value is `true`. + +You may only use a single `TracingService` manifest. + +## Example + +The [Ambassador Tracing Tutorial](/user-guide/tracing-tutorial) has a simple Zipkin tracing example. diff --git a/reference/shadowing.md b/reference/shadowing.md new file mode 100644 index 0000000000..6543cbf178 --- /dev/null +++ b/reference/shadowing.md @@ -0,0 +1,55 @@ +# Traffic Shadowing + +Traffic shadowing is a deployment pattern where production traffic is asynchronously copied to a non-production service for testing. Shadowing is a close cousin to two other commonly known deployment patterns, [canary releases](/reference/canary) and blue/green deployments. Shadowing traffic has several important benefits over blue/green and canary testing: + +* Zero production impact. Since traffic is duplicated, any bugs in services that are processing shadow data have no impact on production. + +* Test persistent services. Since there is no production impact, shadowing provides a powerful technique to test persistent services. You can configure your test service to store data in a test database, and shadow traffic to your test service for testing. Both blue/green deployments and canary deployments require more machinery for testing. + +* Test the actual behavior of a service. When used in conjunction with tools such as [Twitter's Diffy](https://github.com/twitter/diffy), shadowing lets you measure the behavior of your service and compare it with an expected output. A typical canary rollout catches exceptions (e.g., HTTP 500 errors), but what happens when your service has a logic error and is not returning an exception? + +## Shadowing and Ambassador + +Ambassador lets you easily shadow traffic to a given endpoint. In Ambassador, only requests are shadowed; responses from a service are dropped. All normal metrics are collected for the shadow services. This makes it easy to compare the performance of the shadow service versus the production service on the same data set. Ambassador also prioritizes the production path, i.e., it will return responses from the production service without waiting for any responses from the shadow service. + +![Shadowing](/images/shadowing.png) + +## The `shadow` annotation + +In Ambassador, you can enable shadowing for a given mapping by setting `shadow: true` in your `Mapping`. One copy proceeds as if the shadowing `Mapping` was not present: the request is handed onward per the `service`(s) defined by the non-shadow `Mapping`s, and the reply from whichever `service` is picked is handed back to the client. + +The second copy is handed to the `service` defined by the `Mapping` with `shadow` set. Any reply from this `service` is ignored, rather than being handed back to the client. Only a single `shadow` per resource can be specified (i.e., you can't shadow the same resource to more than 1 additional destination). In this situation, Ambassador will indicate an error in the diagnostic service, and only one `shadow` will be used. If you need to implement this type of use case, you should shadow traffic to a multicast proxy (or equivalent). + +You can shadow multiple different services. + +During shadowing, the host header is modified such that `-shadow` is appended. + +## Example + +The following example may help illustrate how shadowing can be used. This first annotation sets up a basic mapping between the `myservice` Kubernetes service and the `/myservice/` prefix, as expected. + +``` + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: myservice-mapping + prefix: /myservice/ + service: myservice.default +``` + +What if we want to shadow the traffic to `myservice`, and send that exact same traffic to `myservice-shadow`? We can create a new mapping that does this: + +``` + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: myservice-shadow-mapping + prefix: /myservice/ + service: myservice-shadow.default + shadow: true +``` + +The `prefix` is set to be the same as the first annotation, which tells Ambassador which production traffic to shadow. The destination service, where the shadow traffic is routed, is a *different* Kubernetes service, `myservice-shadow`. Finally, the `shadow: true` annotation actually enables shadowing. + diff --git a/reference/statistics.md b/reference/statistics.md new file mode 100644 index 0000000000..13f561c03e --- /dev/null +++ b/reference/statistics.md @@ -0,0 +1,144 @@ +# Statistics and Monitoring + +Ambassador is an API gateway for microservices built on [Envoy](https://lyft.github.io/envoy/). A key feature of Envoy is the observability it enables by exposing a multitude of statistics about its own operations. Ambassador makes it easy to direct this information to a statistics and monitoring tool of your choice. + +As an example, for a given service `usersvc`, here are some interesting statistics to investigate: + +- `envoy.cluster.usersvc_cluster.upstream_rq_total` is the total number of requests that `usersvc` has received via Ambassador. The rate of change of this value is one basic measure of service utilization, i.e. requests per second. +- `envoy.cluster.usersvc_cluster.upstream_rq_2xx` is the total number of requests to which `usersvc` responded with an HTTP response indicating success. This value divided by the prior one, taken on an rolling window basis, represents the recent success rate of the service. There are corresponding `4xx` and `5xx` counters that can help clarify the nature of unsuccessful requests. +- `envoy.cluster.usersvc_cluster.upstream_rq_time` is a StatsD timer that tracks the latency in milliseconds of `usersvc` from Ambassador's perspective. StatsD timers include information about means, standard deviations, and decile values. + +## Exposing statistics via StatsD + +Statistics are exposed via the ubiquitous and well-tested [StatsD](https://github.com/etsy/statsd) protocol. + +To expose statistics via StatsD, you will need to set an environment variable `STATSD_ENABLED: true` to Ambassador's Kubernetes Deployment YAML. To set this environment variable, run `kubectl edit deployment ambassador` and add the variable to `spec.template.spec.containers[0].env`. + +The YAML snippet will look something like - + +```yaml + + spec: + containers: + - env: + - name: STATSD_ENABLED + value: "true" + - name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + image: + imagePullPolicy: IfNotPresent + +``` + + Ambassador automatically sends statistics information to a Kubernetes service called `statsd-sink` using typical StatsD protocol settings, UDP to port 8125. We have included a few example configurations in the [statsd-sink](https://github.com/datawire/ambassador/tree/master/statsd-sink) subdirectory to help you get started. Clone the repository to get local, editable copies. + + You may also override the StatsD host by setting the `STATSD_HOST` environment variable. This can be useful if you have an existing StatsD sink available in your cluster. + +## Graphite + +[Graphite](http://graphite.readthedocs.org/) is a web-based realtime graphing system. Spin up an example Graphite setup: + + kubectl apply -f statsd-sink/graphite/graphite-statsd-sink.yaml + +This sets up the `statsd-sink` service and a deployment that contains Graphite and its related infrastructure. Graphite's web interface is available at `http://statsd-sink/` from within the cluster. Use port forwarding to access the interface from your local machine: + + SINKPOD=$(kubectl get pod -l service=statsd-sink -o jsonpath="{.items[0].metadata.name}") + kubectl port-forward $SINKPOD 8080:80 + +This sets up Graphite access at `http://localhost:8080/`. + +## Prometheus + +[Prometheus](https://prometheus.io/) is an open-source monitoring and alerting system. If you use Prometheus, you should deploy a StatsD exporter as a sidecar on each of your Ambassador pods as shown in this [example](https://www.getambassador.io/yaml/ambassador/ambassador-rbac-prometheus.yaml). + +The `statsd-sink` service referenced in this example is built on the [Prometheus StatsD Exporter](https://github.com/prometheus/statsd_exporter), and configured in this [Dockerfile](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prom-statsd-exporter/Dockerfile). + +Add a Prometheus target to read from `statsd-sink` on port 9102 to complete the Prometheus configuration. + +### Configuring metrics mappings for Prometheus + +It may be desirable to change how metrics produced by the `statsd-sink` are named, labeled and grouped. + +For example, by default each service that the API Gateway serves will create a new metric using its name. For the service called `usersvc` you will see this metric: `envoy.cluster.usersvc_cluster.upstream_rq_total`. This may lead to problems if you are trying to create a single aggregate that is the sum of all similar metrics from different services. In this case it is common to differentiate the metrics for an individual service with a `label`. This can be done using a mapping. + +[Follow this guide](https://github.com/prometheus/statsd_exporter/tree/v0.6.0#metric-mapping-and-configuration) to learn how to modify your mappings. + +#### Configuring for Helm + +If you deploy using helm the value that you should change is `exporter.configuration`. Set it to something like this: + +```yaml +exporter: + configuration: | + --- + mappings: + - match: 'envoy.cluster.*.upstream_rq_total' + name: "envoy_cluster_upstream_rq_total" + timer_type: 'histogram' + labels: + cluster_name: "$1" +``` + +#### Configuring for kubectl + +In the [ambassador-rbac-prometheus](https://github.com/datawire/ambassador/blob/master/templates/ambassador/ambassador-rbac-prometheus.yaml) example template there is a `ConfigMap` that should be updated. Add your mapping to the `configuration` property. + +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: ambassador-config +data: + exporterConfiguration: | + --- + mappings: + - match: 'envoy.cluster.*.upstream_rq_total' + name: "envoy_cluster_upstream_rq_total" + timer_type: 'histogram' + labels: + cluster_name: "$1" +``` + +### The Prometheus Operator + +If you don't already have a Prometheus setup, the [Prometheus operator](https://github.com/coreos/prometheus-operator) is a powerful way to create and deploy Prometheus instances. Use the following YAML to quickly configure the Prometheus Operator with Ambassador: + +- [`statsd-sink.yaml`](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/statsd-sink-svc.yaml) Creates the statsd-sink service that collects stats date from Ambassador and translates it to Prometheus metrics. It also creates a `ServiceMonitor` that adds `statsd-sink` as a Prometheus target. +- [`prometheus.yaml`](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prometheus.yaml) Deploys the Prometheus Operator and creates a `Prometheus` object that collects data from the location defined by the `ServiceMonitor`. + +Make sure that the `ServiceMonitor` is in the same namespace as Ambassador. A walk-through of the basics of configuring the Prometheus operator with Ambassador and Envoy is available [here](http://www.datawire.io/faster/ambassador-prometheus/). + +Ensure `STATSD_ENABLED` is set to `"true"` and apply the yaml with kubectl. + +``` +kubectl apply -f statsd-sink.yaml +kubectl apply -f prometheus.yaml +``` + +Wait for a minute after the pods spin up and then access the Prometheus dashboard by port-forwarding the prometheus pod and going to http://localhost:9090/ on a web-browser. + +``` +kubectl port-forward prometheus-prometheus-0 9090 +``` + +## StatsD as an Independent Deployment + +If you want to set up the StatsD sink as an independent deployment, [this example](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prom-statsd-sink.yaml) configuration mirrors the Graphite and Datadog configurations. + +## Grafana + +![Grafana dashboard](/images/grafana.png) + +If you're using Grafana, [Alex Gervais](https://twitter.com/alex_gervais) has written a template [Grafana dashboard for Ambassador](https://grafana.com/dashboards/4698). + +## Datadog + +If you are a user of the [Datadog](https://www.datadoghq.com/) monitoring system, pulling in Ambassador statistics is very easy. Replace the sample API key in the YAML file with your own, then launch the DogStatsD agent: + + kubectl apply -f statsd-sink/datadog/dd-statsd-sink.yaml + +This sets up the `statsd-sink` service and a deployment of the DogStatsD agent that automatically forwards Ambassador stats to your Datadog account. diff --git a/reference/upgrading.md b/reference/upgrading.md new file mode 100644 index 0000000000..f2a9d9db2f --- /dev/null +++ b/reference/upgrading.md @@ -0,0 +1,51 @@ +# Upgrading Ambassador + +Since Ambassador's configuration is entirely stored in annotations or a `ConfigMap`, no special process is necessary to upgrade Ambassador. If you're using the YAML files supplied by Datawire, you'll be able to upgrade simply by repeating the following `kubectl apply` commands. + +First determine if Kubernetes has RBAC enabled: + +```shell +kubectl cluster-info dump --namespace kube-system | grep authorization-mode +``` + +If you see something like `--authorization-mode=Node,RBAC` in the output, then RBAC is enabled. + +If RBAC is enabled: +```shell +kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac.yaml +``` + +If RBAC is not enabled: +```shell +kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml +``` + +This will trigger a rolling upgrade of Ambassador. + +If you're using your own YAML, check the Datawire YAML to be sure of other changes, but at minimum, you'll need to change the pulled `image` for the Ambassador container and redeploy. + +## Upgrading to 0.50.0 +Ambassador 0.50.0 adds the v1 API. While all v0 API objects will still work in Ambassador 0.50.0, it is recommended you upgrade them to v1 so you can take advantage of the new features being added to Ambassador. + +### Mapping +The v1 `Mapping` object gives you the ability to configure Ambassador to [add response headers](/reference/add_response_headers) and [bypass the external authorization service](/reference/mappings#mapping-configuration). + +Upgrading from v0 to v1 is as simple as changing the `apiVersion` in your `Mapping` definition. + +**Note:** The `rate_limits` attribute is replaced by `labels` in the v1 API. This change is required by Ambassador 0.50.0 See the [rate limiting tutorial](/user-guide/rate-limiting-tutorial#v1-api) and [rate limits](/reference/rate-limits/) documentation for more information. + +### AuthService +The v1 AuthService API adds the ability to forward the request body to your external authorization service. It also allows you to define which headers are allowed to and from the authorization service. More information on these changes can be found in the [authentication plugin](/reference/services/auth-service) documentation. + +### TracingService +The v1 `TracingService` adds more configuration options of the [Zipkin driver](/reference/services/tracing-service#zipkin-driver-configurations). + +Upgrading from v0 to v1 is as simple as changing the `apiVersion` in your `TracingService` definition. + +### RateLimitService +There is no difference between the v0 and v1 `RateLimitService` API at this time. Upgrading is as simple as changing the `apiVersion` in the `Mapping` definition. + +**Note:** Ambassador 0.50.0 requires the `rate_limits` `Mapping` attribute be replaced by `labels`. See the [rate limiting tutorial](/user-guide/rate-limiting-tutorial#v1-api) and [rate limits](/reference/rate-limits/) documentation for more information. + +### Module +There is no difference between the v0 and v1 `Module` API at this time. Upgrading is as simple as changing the `apiVersion` in the `Mapping` definition. diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000000..6c22e2c49a --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,107 @@ + + + + + + + https://www.getambassador.io/ + 2018-03-02T19:37:03+00:00 + 1.00 + + + https://www.getambassador.io/user-guide/getting-started + 2018-03-02T19:37:03+00:00 + 0.80 + + + https://www.getambassador.io/about/why-ambassador + 2018-03-02T19:37:03+00:00 + 0.80 + + + https://www.getambassador.io/user-guide/auth-tutorial + 2018-03-02T19:37:03+00:00 + 0.80 + + + https://www.getambassador.io/about/features-and-benefits + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/about/quickstart + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/user-guide/grpc + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/user-guide/tls-termination + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/user-guide/with-istio + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/configuration + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/modules + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/mappings + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/advanced + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/running + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/upgrading + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/statistics + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/auth-external + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/reference/auth-tls-certs + 2018-03-02T19:37:03+00:00 + 0.64 + + + https://www.getambassador.io/about/microservices-api-gateways + 2018-03-02T19:37:03+00:00 + 0.64 + + + + \ No newline at end of file diff --git a/styles/website.css b/styles/website.css new file mode 100644 index 0000000000..8ca5051a41 --- /dev/null +++ b/styles/website.css @@ -0,0 +1,13 @@ +.logo { + width: 75%; + padding: 15px; +} + +/** Removes the "Introduction" link from the table of contents **/ +.book-summary ul.summary > li:first-child { + display: none; +} + +#datawire-breadcrumb { + display: none; +} \ No newline at end of file diff --git a/user-guide/advanced-rate-limiting.md b/user-guide/advanced-rate-limiting.md new file mode 100644 index 0000000000..c3abfbc888 --- /dev/null +++ b/user-guide/advanced-rate-limiting.md @@ -0,0 +1,149 @@ +# Advanced Rate Limiting + +Ambassador Pro integrates a flexible, high performance Rate Limit Service (RLS). Similar to Ambassador, the RLS features a decentralized configuration model so that individual teams can manage their own rate limits. For example: + +* A service owner may want to manage load shedding characteristics, and ensuring specific types of requests take precedence over other types of requests +* An operations engineer may want to ensure service availability overall when request volume is high, and limit the total number of requests being passed to upstream services +* A security engineer may want to protect against denial-of-service attacks from a bad actor + +Like Ambassador, the Ambassador RLS is designed so that many different teams, with different requirements, can independently manage and control rate limiting as necessary. + +## Request labels and domains + +In Ambassador Pro, each request can have multiple *labels*. Labels are arbitrary key/value pairs that contain any metadata about a given request, e.g., its IP, a hard-coded value, the path of the request, and so forth. The Rate Limit Service processes these labels and enforces any limits that are set on a label. Labels can be assigned to *domains*, which are separate namespaces. Typically, different teams would be responsible for different domains. + +## Configuring Rate Limiting: The 50,000 foot view + +Logically, configuring rate limiting is straightforward. + +1. Configure a specific mapping to include one or more request labels. +2. Configure a limit for a given request label with the `RateLimit` resource. + +In the examples below, we'll use the QOTM sample service used in the [Getting Started](https://www.getambassador.io/user-guide/getting-started#5-adding-a-service). + +## Example 1: Global rate limiting for availability + +Imagine the `qotm` service is a Rust-y application that can only handle 3 requests per minute before crashing. While the engineering team really wants to rewrite the `qotm` service in Golang (because Rust isn't fast enough), they haven't had a chance to do so. We want to rate limit all requests for this service to 3 requests per minute. (ProTip: Using requests per minute simplifies testing.) + +We update the mapping for the `qotm` service to add a request label `qotm` to the route as part of a `request_label_group`: + +``` +apiVersion: ambassador/v1 +kind: Mapping +name: qotm +prefix: /qotm/ +service: qotm +labels: + ambassador: + - request_label_group: + - qotm +``` + +*Note* If you're modifying an existing mapping, make sure you to update the apiVersion to `v1` as above from `v0`. + +We then need to configure the rate limit for the qotm service. Create a new YAML file, `qotm-ratelimit.yaml`, and put the following configuration into the file. + +``` +apiVersion: getambassador.io/v1beta1 +kind: RateLimit +metadata: + name: qotm-rate-limit +spec: + domain: ambassador + limits: + - pattern: [{generic_key: qotm}] + rate: 3 + unit: minute +``` + +`generic_key` in the example above is a special, hard-coded value that is used when a single string label is added to a request. + +Deploy the rate limit with `kubectl apply -f qotm-ratelimit.yaml`. (Make sure you always `kubectly apply` your original `qotm` mapping as well.) + +## Example 2: Per user rate limiting + +Suppose you've rewritten the `qotm` service in Golang, and it's humming along nicely. You then discover that some users are taking advantage of this speed to sometimes cause a big spike in requests. You want to make sure that your API doesn't get overwhelmed by any single user. We use the `remote_address` special value in our mapping, which will automatically label all requests with the calling IP address: + +``` +apiVersion: ambassador/v1 +kind: Mapping +name: qotm +prefix: /qotm/ +service: qotm +labels: + ambassador: + - request_label_group: + - remote_address +``` + +We then update our rate limits to limit on `remote_address`: + +``` +apiVersion: getambassador.io/v1beta1 +kind: RateLimit +metadata: + name: qotm-rate-limit +spec: + domain: ambassador + limits: + - pattern: [{remote_address: "*"}] + rate: 3 + unit: minute +``` + +Note for this to work, you need to make sure you've properly configured Ambassador to [propagate your original client IP address](https://www.getambassador.io/reference/modules/#use_remote_address). + +## Example 3: Load shedding GET requests + +You've dramatically improved availability of the `qotm` service, thanks to the per-user rate limiting. However, you've realized that on occasion the queries (e.g., the 'GET' requests) cause so much volume that updates to the qotm (e.g., the 'POST' requests) don't get processed. So we're going to add a more sophisticated load shedding strategy: + +* We're going to rate limit per user. +* We're going to implement a global rate limit on `GET` requests, but not `POST` requests. + +``` +apiVersion: ambassador/v1 +kind: Mapping +name: qotm +prefix: /qotm/ +service: qotm +labels: + ambassador: + - request_label_group: + - remote_address + - qotm_http_method: + header: ":method" + omit_if_not_present: true +``` + +When we add multiple criteria to a pattern, the entire pattern matches when ANY of the rules match (i.e., a logical OR). A pattern match then triggers a rate limit event. Our rate limiting configuration becomes: + +``` +apiVersion: getambassador.io/v1beta1 +kind: RateLimit +metadata: + name: qotm-rate-limit +spec: + domain: ambassador + limits: + - pattern: [{remote_address: "*"}, {qotm_http_method: GET}] + rate: 3 + unit: minute +``` + +## Rate limiting matching rules + +The following rules apply to the rate limit patterns: + +* Patterns are order-sensitive, and must respect the order in which a request is labeled. For example, in #3 above, the `remote_address` pattern must come before the `qotm_http_method` pattern. Switching the two will fail to match. +* Every label in a label group must exist in the pattern in order for matching to occur. +* By default, any type of failure will let the request pass through (fail open). +* Ambassador sets a hard timeout of 20ms on the rate limiting service. If the rate limit service does not respond within the timeout period, the request will pass through. +* If a pattern does not match, the request will pass through. + +## Troubleshooting rate limiting + +The most common source of failure of the rate limiting service will occur when the labels generated by Ambassador do not match the rate limiting pattern. By default, the rate limiting service will log all incoming labels from Ambassador. Use a tool such as [Stern](https://github.com/wercker/stern) to watch the rate limiting logs from Ambassador, and ensure the labels match your descriptor. + +## More + +For more on rate limiting, see the [rate limit reference](/reference/rate-limits). \ No newline at end of file diff --git a/user-guide/ambassador-pro-install.md b/user-guide/ambassador-pro-install.md new file mode 100644 index 0000000000..709de81aaa --- /dev/null +++ b/user-guide/ambassador-pro-install.md @@ -0,0 +1,109 @@ +# Installing Ambassador Pro +--- + +Ambassador Pro is a commercial version of Ambassador that includes integrated Single Sign-On, powerful rate limiting, and more. In this tutorial, we'll walk through the process of installing Ambassador Pro in Kubernetes. + +## 1. Create the Ambassador Pro registry credentials secret. +Your credentials to pull the image from the Ambassador Pro registry were given in the sign up email. If you have lost this email, please contact us at support@datawire.io. + +``` +kubectl create secret docker-registry ambassador-pro-registry-credentials --docker-server=quay.io --docker-username= --docker-password= --docker-email= +``` +- ``: Username given in sign up email +- ``: Password given in sign up email +- ``: Your email address + +## 2. Download the Ambassador Pro Deployment File +Ambassador Pro consists of a series of modules that communicate with Ambassador. The core Pro module is typically deployed as a sidecar to Ambassador. This means it is an additional process that runs on the same pod as Ambassador. Ambassador communicates with the Pro sidecar locally. Pro thus scales in parallel with Ambassador. Ambassador Pro also relies on a Redis instance for its rate limit service and several Custom Resource Definitions (CRDs) for configuration. + +The full configuration for Ambassador Pro is available at https://www.getambassador.io/yaml/ambassador/pro/ambassador-pro.yaml. Download this file locally: + +``` +curl -O "https://www.getambassador.io/yaml/ambassador/pro/ambassador-pro.yaml" +``` + +Next, ensure the `namespace` field in the `ClusterRoleBinding` is configured correctly for your particular deployment. If you are not installing Ambassador into the `default` namespace, you will need to update this file accordingly. + +**Note:** Ambassador 0.40.2 and below does not support v1 `AuthService` configurations. If you are using an older version of Ambassador, replace the `AuthService` in the downloaded YAML with: + +``` + --- + apiVersion: ambassador/v0 + kind: AuthService + name: authentication + auth_service: ambassador-pro + allowed_headers: + - "Client-Id" + - "Client-Secret" + - "Authorization" +``` + +## 3. License Key + +In the `ambassador-pro.yaml` file, update the `AMBASSADOR_LICENSE_KEY` environment variable field with the license key that is supplied as part of your trial email. + +**Note:** The Ambassador Pro will not start without your license key. + +## 4. Deploy Ambassador Pro + +Once you have fully configured Ambassador Pro, deploy your updated configuration. Note that the default configuration will also redeploy your current Ambassador configuration, so verify that you have the correct Ambassador version before deploying Pro. + +``` +kubectl apply -f ambassador-pro.yaml +``` + +Verify that Ambassador Pro is running: + +``` +kubectl get pods | grep ambassador +ambassador-79494c799f-vj2dv 2/2 Running 0 1h +ambassador-pro-redis-dff565f78-88bl2 1/1 Running 0 1h +``` + +## 5. Configure Ambassador Pro services + +Ambassador should now be running, along with the Pro modules. To enable rate limiting and authentication, some additional configuration is required. + +### Enabling Rate limiting + +Deploy the Kubernetes service that enables rate limiting. You will then want to review the [Advanced Rate Limiting tutorial ](/user-guide/advanced-rate-limiting) for information on configuring rate limits. + +```bash +kubectl apply -f https://www.getambassador.io/yaml/ambassador/pro/ambassador-pro-ratelimit.yaml +``` + +### Enabling Single Sign-On + +Ambassador Pro's authentication service requires a URL for your authentication provider. This will be the URL Ambassador Pro will direct to for authentication. + +If you are using Auth0, this will be the name of the tenant you created (e.g `datawire-ambassador`). To create an Auth0 tenant, go to auth0.com and sign up for a free account. Once you have created an Auth0 tenant, the full `AUTH_PROVIDER_URL` is `https://.auth0.com`. + +You can also find this as the domain for your application. + +![](/images/Auth0_domain_clientID.png) + +Add this as the `AUTH_PROVIDER_URL` in your Ambassador Pro deployment manifest. + +``` + env: + # Auth provider's absolute url: {scheme}://{host} + - name: AUTH_PROVIDER_URL + value: https://datawire-ambassador.auth0.com +``` + +Redeploy the Ambassador Pro deployment manifest, along with the Ambassador Pro auth service: + +```bash +kubectl apply -f ambassador-pro.yaml +kubectl apply -f https://www.getambassador.io/yaml/ambassador/pro/ambassador-pro-auth.yaml +``` + +Finally, you will need to configure a tenant resource for Ambassador Pro to authenticate against. Details on how to configure this can be found in the [Single Sign-On with OAuth & OIDC](/user-guide/oauth-oidc-auth#configure-your-authentication-tenants) documentation. + +### Enabling Service Preview + +Service Preview requires a command-line client, `apictl`. For instructions on configuring Service Preview, see the [Service Preview tutorial](/docs/dev-guide/service-preview). + +### Enabling Consul Connect integration + +Ambassador Pro's Consul Connect integration is deployed as a separate Kubernetes service. For instructions on deploying Consul Connect, see the [Consul Connect integration guide](/user-guide/consul-connect-ambassador). diff --git a/user-guide/auth-tutorial.md b/user-guide/auth-tutorial.md new file mode 100644 index 0000000000..3456541c05 --- /dev/null +++ b/user-guide/auth-tutorial.md @@ -0,0 +1,197 @@ +# Authentication + +Ambassador can authenticate incoming requests before routing them to a backing service. In this tutorial, we'll configure Ambassador to use an external third party authentication service. + +## Before You Get Started + +This tutorial assumes you have already followed the [Ambassador Getting Started](/user-guide/getting-started) guide. If you haven't done that already, you should do that now. + +After completing [Getting Started](/user-guide/getting-started), you'll have a Kubernetes cluster running Ambassador and the Quote of the Moment service. Let's walk through adding authentication to this setup. + +## 1. Deploy the authentication service + +Ambassador delegates the actual authentication logic to a third party authentication service. We've written a [simple authentication service](https://github.com/datawire/ambassador-auth-service) that: + +- listens for requests on port 3000; +- expects all URLs to begin with `/extauth/`; +- performs HTTP Basic Auth for all URLs starting with `/qotm/quote/` (other URLs are always permitted); +- accepts only user `username`, password `password`; and +- makes sure that the `x-qotm-session` header is present, generating a new one if needed. + +Ambassador routes _all_ requests through the authentication service: it relies on the auth service to distinguish between requests that need authentication and those that do not. If Ambassador cannot contact the auth service, it will return a 503 for the request; as such, **it is very important to have the auth service running before configuring Ambassador to use it.** + +Here's the YAML we'll start with: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: example-auth +spec: + type: ClusterIP + selector: + app: example-auth + ports: + - port: 3000 + name: http-example-auth + targetPort: http-api +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: example-auth +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: example-auth + spec: + containers: + - name: example-auth + image: datawire/ambassador-auth-service:1.1.1 + imagePullPolicy: Always + ports: + - name: http-api + containerPort: 3000 + resources: + limits: + cpu: "0.1" + memory: 100Mi +``` + +Note that the service does _not_ yet contain any Ambassador annotations. This is intentional: we want the service running before we tell Ambassador about it. + +The YAML above is published at getambassador.io, so if you like, you can just do + +```shell +kubectl apply -f https://www.getambassador.io/yaml/demo/demo-auth.yaml +``` + +to spin everything up. (Of course, you can also use a local file, if you prefer.) + +Wait for the pod to be running before continuing. The output of `kubectl get pods` should look something like - +```console +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +example-auth-6c5855b98d-24clp 1/1 Running 0 4m +``` +Note that the `READY` field says `1/1` which means the pod is up and running. + +## 2. Configure Ambassador authentication + +Once the auth service is running, we need to tell Ambassador about it. The easiest way to do that is to annotate the `example-auth` service. While we could use `kubectl patch` for this, it's simpler to just modify the service definition and re-apply. Here's the new YAML: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: example-auth + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: AuthService + name: authentication + auth_service: "example-auth:3000" + path_prefix: "/extauth" + allowed_headers: + - "x-qotm-session" +spec: + type: ClusterIP + selector: + app: example-auth + ports: + - port: 3000 + name: http-example-auth + targetPort: http-api +``` + +This configuration tells Ambassador about the auth service, notably that it +needs the `/extauth` prefix, and that it's OK for it to pass back the +`x-qotm-session` header. Note that `path_prefix` and `allowed_headers` are +optional. + +If the auth service uses a framework like +[Gorilla Toolkit](http://www.gorillatoolkit.org) which enforces strict slashes +as HTTP path separators, it is possible to end up with an infinite redirect +where the auth service's framework redirects any request with non-conformant +slashing. This would arise if the above example had +```path_prefix: "/extauth/"```, the auth service would see a request for +```/extauth//qotm/quote/1``` which would then be redirected to +```/extauth/quotm/quote/1``` rather than actually be handled by the +authentication handler. For this reason, remember that the full path of the +incoming request including the leading slash, will be appended to +```path_prefix``` regardless of non-conformant slashing. + +You can apply this file from getambassador.io with + +```shell +kubectl apply -f https://www.getambassador.io/yaml/demo/demo-auth-enable.yaml +``` + +or, again, apply it from a local file if you prefer. + +Ambassador will see the annotations and reconfigure itself within a few seconds. + +## 3. Test authentication + +If we `curl` to a protected URL: + +```shell +$ curl -v $AMBASSADORURL/qotm/quote/1 +``` + +We get a 401, since we haven't authenticated. + +```shell +HTTP/1.1 401 Unauthorized +x-powered-by: Express +x-request-id: 9793dec9-323c-4edf-bc30-352141b0a5e5 +www-authenticate: Basic realm=\"Ambassador Realm\" +content-type: text/html; charset=utf-8 +content-length: 0 +etag: W/\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\" +date: Fri, 15 Sep 2017 15:22:09 GMT +x-envoy-upstream-service-time: 2 +server: envoy +``` + +If we authenticate to the service, we will get a quote successfully: + +```shell +$ curl -v -u username:password $AMBASSADORURL/qotm/quote/1 + +TCP_NODELAY set +* Connected to 35.196.173.175 (35.196.173.175) port 80 (#0) +* Server auth using Basic with user \'username\' +> GET /qotm/quote/1 HTTP/1.1 +> Host: 35.196.173.175 +> Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< content-type: application/json +< x-qotm-session: 069da5de-5433-46c0-a8de-d266e327d451 +< content-length: 172 +< server: envoy +< date: Wed, 27 Sep 2017 18:53:38 GMT +< x-envoy-upstream-service-time: 25 +< +{ + \"hostname\": \"qotm-1827164760-gf534\", + \"ok\": true, + \"quote\": \"A late night does not make any sense.\", + \"time\": \"2017-09-27T18:53:39.376073\", + \"version\": \"1.1\" +} +``` + +## More + +For more details about configuring authentication, read the documentation on [external authentication](/reference/services/auth-service). diff --git a/user-guide/bare-metal.md b/user-guide/bare-metal.md new file mode 100644 index 0000000000..605c1647be --- /dev/null +++ b/user-guide/bare-metal.md @@ -0,0 +1,82 @@ +# Deploying Ambassador on a Bare Metal Kubernetes Installation +--- +In cloud environments, provisioning a readily available network load balancer with Ambassador is the best option for handling ingress into your Kubernetes cluster. When running kubernetes on a bare-metal setup, where network load balancers are not available by default, we need to consider different options for exposing Ambassador. +##Exposing Ambassador via NodePort +The simplest way to expose an application in Kubernetes is via a `NodePort` service. In this configuration, we create the [Ambassador service](/user-guide/getting-started#2-defining-the-ambassador-service) and identify `type: NodePort` instead of `LoadBalancer`. Kubernetes will then create a service and assign that service a port to be exposed externally and direct traffic to Ambassador via the defined `port`. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador +spec: + type: NodePort + ports: + - name: http + port: 8088 + targetPort: 80 + nodePort: 30036 # Optional: Define the port you would like exposed + protocol: TCP + selector: + service: ambassador +``` +Using a `NodePort` leaves Ambassador isolated from the host network, allowing the Kubernetes service to handle routing to Ambassador pods. You can drop-in this yaml to replace the `LoadBalancer` service in the [YAML installation guide](/user-guide/getting-started) and use `http://:/` as the host for requests. + +## Exposing Ambassador via Host Network +When running Ambassador on a bare-metal install of Kubernetes, you have the option to configure Ambassador pods to use the network of the host they are running on. This method allows you to bind Ambassador directly to port 80 or 443 so you won't need to identify the port in requests. + +i.e `http://:/` becomes `http:///` + +This can be configured by setting `hostNetwork: true` in the Ambassador deployment. `dnsPolicy: ClusterFirstWithHostNet` will also need to set to tell Ambassador to use *KubeDNS* when attempting to resolve mappings. + +```diff +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: ambassador +spec: + replicas: 1 + template: + metadata: + annotations: + sidecar.istio.io/inject: "false" + labels: + service: ambassador + spec: ++ hostNetwork: true ++ dnsPolicy: ClusterFirstWithHostNet + serviceAccountName: ambassador + containers: + - name: ambassador + image: quay.io/datawire/ambassador:0.40.0 + resources: + limits: + cpu: 1 + memory: 400Mi + requests: + cpu: 200m + memory: 100Mi + env: + - name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + livenessProbe: + httpGet: + path: /ambassador/v0/check_alive + port: 8877 + initialDelaySeconds: 30 + periodSeconds: 3 + readinessProbe: + httpGet: + path: /ambassador/v0/check_ready + port: 8877 + initialDelaySeconds: 30 + periodSeconds: 3 + restartPolicy: Always +``` +This configuration does not require an Ambassador service be defined so you can remove that service if you have defined one. + +**Note:** Before configuring Ambassador with this method, consider some of the functionality that is lost by bypassing the Kubernetes service including only having one Ambassador able to bind to port 80 or 443 per node and losing any load balancing that is typically performed by Kubernetes services. Join our [slack channel](https://join.slack.com/t/datawire-oss/shared_invite/enQtMzcwMDEwMTc5ODQ3LTE1NmIzZTFmZWE0OTQ1NDc2MzE2NTkzMDAzZWM0MDIxZTVjOGIxYmRjZjY3N2M2Mjk4NGI5Y2Q4NGY4Njc1Yjg) to ask any questions you have regarding running Ambassador on a bare-metal installation. diff --git a/user-guide/cd-declarative-gitops.md b/user-guide/cd-declarative-gitops.md new file mode 100644 index 0000000000..45f8ccdb2a --- /dev/null +++ b/user-guide/cd-declarative-gitops.md @@ -0,0 +1,36 @@ +# Continuous Delivery with Declarative Config and DVCS as the Source of Truth + +No code provides value to end users until it is running in production. Many of the architectural patterns that are associated with today's cloud-native applications, such as microservices and serverless, promote looser coupling between components, which enables a faster iteration cycle. However, traditional software continuous integration and delivery mechanisms need to adapt to be able to support this increase in speed. + + +## Components of Cloud Native Continuous Delivery + +[Continuous Delivery](https://continuousdelivery.com/) is the ability to get changes of all types -- including new features, configuration changes, bug fixes and experiments -- into production and in front of customers safely and quickly in a sustainable way. In a complex and ever-changing cloud native environment, combining continuous delivery with declarative configuration -- focusing on the what, rather than the how -- is an obvious choice to reduce the burden of describing all of the low-level implementation detail, and also allow orchestration and scheduling frameworks to make optimal second-by-second modifications that wouldn't be practical for a human operator. + +By using a distributed version control system (DVCS), like git, to store all code and configuration organisations get many benefits: there can be a single source of truth; engineers can apply contextual comments to code, and create discussion close to the issue being examined; and repetitive (but vital) review and delivery operations can be automated and changes written back to a repository in the form of an audit log. + +High-performing organisations combine all three of these approaches to drive success within IT and business. This is supported by conclusions from the "State of DevOps 2018" report, and also the emergence of methodologies like "[GitOps](https://www.weave.works/blog/gitops-operations-by-pull-request)" and the "[Software Defined Delivery Manifesto](https://sdd-manifesto.org/)" + + +## How Ambassador Supports Continuous Delivery of Cloud Services + +As engineering teams take advantage of the ability to iterate faster by using loosely coupled architectures, such as microservices, they frequently find that they require a new workflow in order to expose their services to end users. With a monolithic application this was easy: there was typically a single ingress point (via an edge gateway or Application Delivery Controller), and routing additions or updates were simply added into the monolith's codebase. With microservices there may be multiple points of ingress into a system, and the routing is typically decoupled from any single (monolithic) application -- which is often pushed downstream into the edge gateway. This in turn means that the edge gateways becomes integral with your continuous delivery process. + +What is required is a mechanism to decentralise edge configuration and also support the continuous delivery of new changes. Ambassador supports this by allowing routing configuration to be specified in code next to a team's Kubernetes' service definitions. + +A microservice development team's workflow therefore moves from this deployment pattern when using traditional edge technologies: + +1. App developer defines configuration. +1. App developer opens ticket for operations. +1. Operations reviews ticket. +1. Operations initiates infrastructure change management process. +1. Operations executes change using UI or REST API. +1. Operations notifies app dev of the change. +1. App developer tests change, and opens a ticket to give feedback to operations if necessary. + +To this with Ambassador: + +1. App dev opens Pull Request in Git with proposed edge configuration changes. +1. Operations reviews PR, offers comments and ultimately merges PR. +1. Automated CI pipeline applies changes to Ambassador. +1. App dev tests changes. diff --git a/user-guide/config-ambassador.md b/user-guide/config-ambassador.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/user-guide/consul-connect-ambassador.md b/user-guide/consul-connect-ambassador.md new file mode 100644 index 0000000000..6cf7858daa --- /dev/null +++ b/user-guide/consul-connect-ambassador.md @@ -0,0 +1,178 @@ +# Consul Connect Integration with Ambassador + +**In Beta:** Please contact us on [Slack](https://d6e.co/slack) or Email at support@datawire.io if you would like to learn more about how to implement this feature. + + +## Prerequisites + +### Consul Connect +Installation and configuration of Consul Connect is outside of the scope of this document. Please refer to [Consul documentation](https://www.consul.io/docs/platform/k8s/index.html) for information on how to install and configure Consul Connect. + +### Ambassador +Install and configure Ambassador. If you are using a cloud provider such as Amazon, Google, or Azure, you can type: + +``` +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-rbac.yaml +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-service.yaml +``` + +Note: If you are using GKE, you will need additional privileges: + +``` +kubectl create clusterrolebinding my-cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format="value(config.account)") +``` + +For more detailed instructions on installing Ambassador, please see the [Ambassador installation guide](/user-guide/getting-started). + +**Note:** If you have automatic sidecar injection enabled, ensure the `"consul.hashicorp.com/connect-inject":` annotation is set to `"false"` in the Ambassador deployment spec. + +```yaml +spec: + replicas: 1 + selector: + matchLabels: + service: ambassador + strategy: + type: RollingUpdate + template: + metadata: + labels: + service: ambassador + annotations: + "consul.hashicorp.com/connect-inject": "false" +``` + +## 1. Install the Ambassador Pro Consul Connector +Ambassador Pro integrates with Consul Connect via a sidecar service. This service does two things: + +- Talks to Consul and registers Ambassador as a Consul Service +- Retrieves the TLS certificate issued by the Consul CA and stores it as a Kubernetes secret Ambassador will use to authenticate with upstream services. + +Deploy the Ambassador Consul Connector via kubectl: + +``` +kubectl apply -f https://getambassador.io/yaml/ambassador/pro/ambassador-consul-connector.yaml +``` + +## 2. Configure Ambassador + +### Create the TLSContext +You will need to tell Ambassador to use the certificate issued by Consul for `mTLS` with upstream services. This is accomplished by configuring a `TLSContext` to store the secret. + + ```yaml + --- + apiVersion: ambassador/v1 + kind: TLSContext + name: ambassador-consul-connect + hosts: [] + secret: ambassador-consul-connect + ``` + +### Configure Ambassador Mappings to use the TLSContext +Ambassador needs to be configured to originate TLS to upstream services. This is done by providing a `TLSContext` to your service `Mapping`. + + ```yaml + --- + apiVersion: ambassador/v1 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + tls: ambassador-consul + service: https://qotm:443 + ``` + **Note:** All service mappings will need `tls: ambassador-consul` to authenticate with Connect-enabled upstream services. + +## 3. Test the Ambassador Consul Connector +To test that the Ambassador Consul Connector is working, you will need to have a service running with a Connect Sidecar. The following configuration will create the QoTM service with a Connect sidecar. + +```yaml +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: qotm +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: qotm + annotations: + "consul.hashicorp.com/connect-inject": "true" + spec: + containers: + - name: qotm + image: datawire/qotm:1.2 + ports: + - name: http-api + containerPort: 5000 + readinessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 3 + resources: + limits: + cpu: "0.1" + memory: 100Mi +``` +Put this YAML in a file called `qotm-deploy.yaml` and apply it with `kubectl`: + +``` +kubectl apply -f qotm-deploy.yaml +``` + +Now, you will need to configure a service for Ambassador to route requests to. The following service will: + +- Create a `Mapping` to tell Ambassador to originate TLS using the `ambassador-consul` `TLSContext` configured earlier. +- Route requests to Ambassador to the Connect sidecar in the QoTM pod using the statically assigned Consul port: `20000`. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v1 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + tls: ambassador-consul + service: https://qotm:443 +spec: + type: NodePort + selector: + app: qotm + ports: + - port: 443 + name: https-qotm + targetPort: 20000 +``` +Put this YAML in a file named `qotm-service.yaml` and apply it with `kubectl`. + +``` +kubectl apply -f qotm-service.yaml +``` + +Finally, test the service with cURL. + +``` +curl -v https://{AMBASSADOR-EXTERNAL-IP}/qotm/ +``` + +## Additional Configuration +Additional configuration for the Ambassador Consul Connector is done with environment variables. + +| Environment Variable | Description | Default | +| -------------------- | ----------- | ------- | +| AMBASSADOR_ID | Set the Ambassador ID so multiple instances of this integration can run per-Cluster when there are multiple Ambassadors (Required if `AMBASSADOR_ID` is set in your Ambassador deployment) | `""` | +| CONSUL_HOST | Set the IP or DNS name of the target Consul HTTP API server | `127.0.0.1` | +| CONSUL_PORT | Set the port number of the target Consul HTTP API server | `8500` | +| AMBASSADOR_TLS_SECRET_NAME | Set the name of the Kubernetes `v1.Secret` created by this program that contains the Consul-generated TLS certificate. | `$AMBASSADOR_ID-consul-connect` | +| AMBASSADOR_TLS_SECRET_NAMESPACE | Set the namespace of the Kubernetes `v1.Secret` created by this program. | (same Namespace as the Pod running this integration) | diff --git a/user-guide/demo-in-detail.md b/user-guide/demo-in-detail.md new file mode 100644 index 0000000000..a940335261 --- /dev/null +++ b/user-guide/demo-in-detail.md @@ -0,0 +1,237 @@ +# Demo in Detail + +Ambassador is an API gateway for microservices, so to get started, it's helpful to actually have a running service to use it with. We'll use Datawire's "Quote of the Moment" service (`qotm`) for this; you can deploy it into Kubernetes with + +```shell +kubectl apply -f https://www.getambassador.io/yaml/demo/demo-qotm.yaml +``` + +This will create a deployment called `qotm` and a corresponding Kubernetes service entry that's also called `qotm`. Quote of the Moment supports a very simple REST API: + +* `GET /health` performs a simple health check +* `GET /` returns a random Quote of the Moment +* `GET /quote/:quoteid` returns the Quote of the Moment with a given ID +* `POST /quote` adds a new Quote of the Moment and returns its ID + * this requires that the POST body carry the new Quote of the Moment + +We'll use the health check as our first simple test to make sure that Ambassador is relaying requests, but of course we want all of the above to work through Ambassador -- and we want everything using the `/quote` endpoint to require authentication. + +At its heart, Ambassador is controlled by a collection of YAML files that tell it which URLs map to which services. The most straightforward way to run Ambassador is to push these configuration files into a Kubernetes `ConfigMap` named `ambassador-config`, then use Datawire's published Ambassador image to read the published configuration at boot time. When you need to change the configuration, you update the `ConfigMap`, then use Kubernetes' deployment machinery to trigger a rollout. + +To set this up, we'll start by creating the `ConfigMap` from a directory of YAML files. In any real scenario, this would be a directory under revision control, but for now, we'll just create an empty `config` directory: + +``` +mkdir config +``` + +Our configuration will start with a single mapping: the `/qotm/` resource will be mapped to the Quote of the Moment service. Here's the YAML for that, which we'll put into `config/mapping-qotm.yaml`: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +service: qotm +``` + +Once that's done, we can create the `ambassador-config` map from our `config` directory: + +```shell +kubectl create configmap ambassador-config --from-file config +``` + +Now we can start Ambassador running in the Kubernetes cluster. We recommend using [TLS](/user-guide/tls-termination), but for right now we'll just set up an HTTP-only Ambassador to show you how things work: + +```shell +kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador.yaml +``` + +This will create the Ambassador service, listening on port 80, and the Ambassador deployment itself. Again, in the real world you'll definitely want TLS, but this is enough for our purposes here. + +To actually use the QotM service, we need the URL for microservice access through Ambassador. This is, sadly, a little harder than one might like. If you're using AWS, GKE, or Minikube, you may be able to use the commands below -- **note that these will only work since we already know we're using HTTP**: + +```shell +# AWS (for Ambassador using HTTP) +AMBASSADORURL=http://$(kubectl get service ambassador --output jsonpath='{.status.loadBalancer.ingress[0].hostname}') + +# GKE (for Ambassador using HTTP) +AMBASSADORURL=http://$(kubectl get service ambassador --output jsonpath='{.status.loadBalancer.ingress[0].ip}') + +# Minikube (for Ambassador using HTTP) +AMBASSADORURL=$(minikube service --url ambassador) +``` + +If that doesn't work out, look at the `LoadBalancer Ingress` line of `kubectl describe service ambassador` and set `$AMBASSADORURL` based on that. **Do not include a trailing `/`** on it, or our examples below won't work. + +Once `$AMBASSADORURL` is set, you'll be able to use that for a basic health check on the QotM service: + +```shell +curl -v $AMBASSADORURL/qotm/health +``` + +If all goes well you should get an HTTP 200 response with a JSON body, something like: + +```json +{ + "hostname": "qotm-2399866569-9q4pz", + "msg": "QotM health check OK", + "ok": true, + "time": "2017-09-15T04:09:51.897241", + "version": "1.1" +} +``` + +Since the `/qotm/` prefix in the path portion of the URL there matches the prefix we used for the `qotm_map` mapping above, Ambassador knows to route the request to the QotM service. In the process it rewrites `/qotm/` to `/` so that `/qotm/health` becomes `/health`, which is what the QotM service expects. (This rewriting is configurable; `/` is just the default.) + +Suppose we want a quote for this moment? + +```shell +curl $AMBASSADORURL/qotm/ +``` + +(Note that the trailing `/` is mandatory the way we've set things up.) This should return something like + +```json +{ + "hostname": "qotm-2399866569-9q4pz", + "ok": true, + "quote": "Non-locality is the driver of truth. By summoning, we vibrate.", + "time": "2017-09-15T04:18:10.371552", + "version": "1.1" +} +``` + +and repeating that should yield other (kind of surreal) quotes. + +The QotM service also has an endpoint that allows teaching it new quotes, which should be accessible now: + +```shell +curl -XPOST -H"Content-Type: application/json" \ + -d'{ "quote": "The grass is never greener anywhere else." }' \ + $AMBASSADORURL/qotm/quote +``` + +That should return the ID of the new quote: + +```json +{ + "hostname": "qotm-2399866569-9q4pz", + "ok": true, + "quote": "The grass is never greener anywhere else.", + "quoteid": 10, + "time": "2017-09-15T04:19:06.367547", + "version": "1.1" +} +``` + +and we should be able to read that back with + +```shell +curl $AMBASSADORURL/qotm/quote/10 +``` + +But it's probably not a good idea to allow any random person to update our quotations. Ambassador supports an external authentication service for exactly this reason: let's set it up, using a very very simple demo authentication service: + +```shell +kubectl apply -f https://www.getambassador.io/yaml/demo/demo-auth.yaml +``` + +That will start the demo auth service running. The auth service: + +- listens for requests on port 3000; +- expects all URLs to begin with `/extauth/`; +- performs HTTP Basic Auth for all URLs starting with `/qotm/quote/` (other URLs are always permitted); +- accepts only user `username`, password `password`; and +- makes sure that the `x-qotm-session` header is present, generating a new one if needed. + +Once the auth service is running, add `config/module-authentication.yaml`: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Module +name: authentication +config: + auth_service: "example-auth:3000" + path_prefix: "/extauth" + allowed_headers: + - "x-qotm-session" +``` + +which tells Ambassador about the auth service, notably that it needs the `/extauth` prefix, and that it's OK for it to pass back the `x-qotm-session` header. Note that `path_prefix` and `allowed_headers` are optional. + +We don't actually need to change any mappings, since the auth service is responsible for knowing about which things need auth and which don't. However, if we try pull quote 10 again: + +```shell +curl $AMBASSADORURL/qotm/quote/10 +``` + +...then you'll see that it works, even though it seems like it should need authentication! + +The problem is that we only changed the Ambassador config on the local disk -- we need to update the `ConfigMap`, and we need to force a new rollout of Ambassador so that it can reconfigure everything. + +We can update the `ConfigMap` by using `kubectl` to rewrite it in place: + +```shell +kubectl create configmap ambassador-config --from-file config -o yaml --dry-run | \ + kubectl replace -f - +``` + +This neat little trick uses `-o yaml --dry-run` option to make `kubectl` write a file that we can feed into `kubectl replace`, since `kubectl replace` doesn't understand the `--from-file` option. The result is that the `ambassador-config` `ConfigMap` gets updated with all the new config at once. + +Once that's done, we need to trigger a new rollout of Ambassador using the Kubernetes deployment machinery. There are several ways to do this, but the most painless (unless you happen to need to upgrade to a new Ambassador release!) is to update an annotation on the deployment: + +```shell +kubectl patch deployment ambassador -p \ + "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}" +``` + +This simply patches the deployment with an annotation containing a timestamp of when the configuration was last updated. Kubernetes will respond by rolling out new Ambassador pods, which will pick up the new configuration. You can use `kubectl rollout status deployment/ambassador` to keep an eye on this process. + +Once that's all done, if we try to read our quote back: + +```shell +curl $AMBASSADORURL/qotm/quote/10 +``` + +then we should get a 401, since we haven't authenticated. + +```shell +HTTP/1.1 401 Unauthorized +x-powered-by: Express +x-request-id: 9793dec9-323c-4edf-bc30-352141b0a5e5 +www-authenticate: Basic realm="Ambassador Realm" +content-type: text/html; charset=utf-8 +content-length: 0 +etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" +date: Fri, 15 Sep 2017 15:22:09 GMT +x-envoy-upstream-service-time: 2 +server: envoy +``` + +It will work, though, if we authenticate to the QotM service: + +```shell +curl -v -u username:password $AMBASSADORURL/qotm/quote/10 +``` + +which should now return something like the following (including the `x-qotm-session` header!): + +``` +HTTP/1.1 200 OK +content-type: application/json +x-qotm-session: 599969b7-8f00-4663-bb8c-fefd3dfc6174 +content-length: 176 +server: envoy +date: Fri, 15 Sep 2017 15:39:58 GMT +x-envoy-upstream-service-time: 4 + +{ + "hostname": "qotm-2399866569-9q4pz", + "ok": true, + "quote": "The grass is never greener anywhere else.", + "time": "2017-09-15T15:39:59.200961", + "version": "1.1" +} +``` diff --git a/user-guide/developers.md b/user-guide/developers.md new file mode 100644 index 0000000000..61f5b2116b --- /dev/null +++ b/user-guide/developers.md @@ -0,0 +1,16 @@ +# Developer Guide + +Unlike traditional API gateways, Ambassador has been designed to be managed by developers and frontline application engineers that are working within independent product (service) focused teams. This section of the documentation focuses on the core functionality of Ambassador for application developers. + +# Why Should Developers Use Ambassador? + +The decentralized control plane and ability to locate configuration close to each team's Kubernetes service code enables rapid rollout of new APIs and features, and the ability for developers to manage the deployment, testing and monitoring in production. + +In more detail, Ambassador supports developers in the following ways: + +* [Enables publishing a service](/concepts/developers) publicly without a hand-off to operations +* [Fine-grained control of routing](/concepts/developers), with support for regex-based routing, host routing, and more +* Support for [gRPC and HTTP/2](/user-guide/grpc) +* [Testing in production](/docs/dev-guide/test-in-prod) +* Support for [canarying and shadow traffic](/docs/dev-guide/canary-release-concepts) +* [Transparent monitoring](/reference/statistics) of L7 traffic to given services diff --git a/user-guide/docker-compose.md b/user-guide/docker-compose.md new file mode 100644 index 0000000000..86a102d8a0 --- /dev/null +++ b/user-guide/docker-compose.md @@ -0,0 +1,407 @@ +# Deploying Ambassador to Docker Compose for local development + +Docker Compose is useful for local development where Minikube may be undesirable. This guide is not intended for production deployments but it is intended to allow developers to quickly try out Ambassador features in a simple, local environment. + +*It is important to note that any change to Ambassador's configuration using this method requires a restart of the Ambassador container and thus downtime.* + +## Prerequisites + +We assume that you have the latest version of Docker at the time of the writing of this guide. + +## 1. Creating a simple Docker Compose environment + +In this guide we will begin with a basic Ambassador API Gateway and add features over time. Not all features will be covered but by the end of this read you should know how to configure Ambassador to meet your local development needs. + +### Create docker-compose.yaml file + +In a working directory create a file called `docker-compose.yaml` with this content: + +```yaml +version: '3.5' + +services: + ambassador: + image: quay.io/datawire/ambassador:0.50.1 + ports: + # expose port 80 via 8080 on the host machine + - 8080:80 + volumes: + # mount a volume where we can inject configuration files + - ./config:/ambassador/ambassador-config + environment: + # don't try to watch Kubernetes for configuration changes + - AMBASSADOR_NO_KUBEWATCH=no_kubewatch +``` + +Note the mounted volume. When Ambassador bootstraps on container startup it checks the `/ambassador/ambassador-config` directory for configuration files. We will use this behavior to configure ambassador. + +Note also the `AMBASSADOR_NO_KUBEWATCH` environment variable. Without this, Ambassador will try to use the Kubernetes API to watch for service changes, which won't work in Docker. + +### Create the initial configuration + +Ambassador will interpret a total absence of configuration information as meaning that it should wait for dynamic configuration, so we'll give it a bare-bones configuration to get started. + +Create a `config` folder (which must match the mounted volume in the `docker-compose.yaml` file) and add a file called `ambassador.yaml` to the directory. +(Note: Configuration files can have any name or combined into the same yaml file) + +```bash +mkdir config +touch config/ambassador.yaml +``` + +Set the contents of the `config/ambassador.yaml` to this yaml configuration: + +```yaml +--- +apiVersion: ambassador/v1 +kind: Module +name: ambassador +config: {} +``` + +This will allow Ambassador to come up with a completely default configuration. + +### Test using Ambassador's Diagnostics + +Run your compose environment and curl the diagnostics endpoint to ensure the compose file is working as expected. + +```bash +# start your containers in the background +docker-compose up -d + +# curl for the response header from the diagnostics endpoint +curl -I localhost:8080/ambassador/v0/diag/ + +# the response code should be 200 +HTTP/1.1 200 OK +server: envoy +date: Fri, 17 Aug 2018 21:07:37 GMT +content-type: text/html; charset=utf-8 +content-length: 6459 +x-envoy-upstream-service-time: 10 +``` + +## 2. Make a change to the default configuration + +Let's turn off the diagnostics page to demonstrate how we will enable and configure Ambassador. + +Edit the contents of the `config/ambassador.yaml` to this yaml configuration: + +```yaml +--- +apiVersion: ambassador/v1 +kind: Module +name: ambassador +config: + diagnostics: + # Stop the diagnostics endpoint from being publicly available + enabled: false +``` + +Now restart ambassador and test the diagnostics endpoint to ensure our configuration is in use: + +```bash +# restart the container to pick up new configuration settings +docker-compose up -d -V ambassador + +# curl the same diagnostics endpoint as the previous step +curl -I localhost:8080/ambassador/v0/diag/ + +# the response code should be 404 +HTTP/1.1 404 Not Found +date: Fri, 17 Aug 2018 21:18:25 GMT +server: envoy +content-length: 0 +``` + +Feel free to re-enable the diagnostics endpoint. + +## 3. Add a route mapping + +Now that we have demonstrated that we can modify the configuration let's add a mapping to route to `http://demo.getambassador.io/qotm/` service. + +Create a new file `config/mapping-qotm.yaml` with these contents: + +```yaml +--- +apiVersion: ambassador/v1 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +rewrite: /qotm/ +service: demo.getambassador.io +``` + +Once again, restart ambassador and test the new mapping: + +```bash +# restart the container to pick up new configuration settings +docker-compose up -d -V ambassador + +# curl the quote-of-the-moment service +curl localhost:8080/qotm/quote/1 + +# the response body should be a json object with a quote +{ + "hostname": "qotm-3716059461-47tnl", + "ok": true, + "quote": "The light at the end of the tunnel is interdependent on the relatedness of motivation, subcultures, and management.", + "time": "2018-08-17T21:29:24.690950", + "version": "1.3" +} +``` + +## 3. Add a route mapping to an internal service + +While routing to an external service is useful, more often than not our Docker Compose environment will contain a number of services that need routing. + +### Add the qotm service to the docker-compose.yaml file + +Edit the `docker-compose.yaml` file and add a new `qotm` service. It should now look like this: + +```yaml +version: '3.5' + +services: + ambassador: + image: quay.io/datawire/ambassador:0.50.1 + ports: + - 8080:80 + volumes: + # mount a volume where we can inject configuration files + - ./config:/ambassador/ambassador-config + environment: + # don't try to watch Kubernetes for configuration changes + - AMBASSADOR_NO_KUBEWATCH=no_kubewatch + qotm: + image: datawire/qotm:1.3 + ports: + - 5000 +``` + +### Update the mapping-qotm.yaml file to route to our internal qotm service + +Edit the `config/mapping-qotm.yaml` file and modify the `service` and `rewrite` field. It should now look like this: + +```yaml +--- +apiVersion: ambassador/v1 +kind: Mapping +name: qotm_mapping +prefix: /qotm/ +# remove the qotm prefix when talking to the qotm service +rewrite: / +# change the `service` parameter to the name of our service with the port +service: qotm:5000 +``` + +### Restart Ambassador and test + +Re-run the same test as in the previous section to ensure the route works as before. This time we will need to bring up the new service first. + +```bash +# start all new containers (eg. qotm) +docker-compose up -d + +# restart the container to pick up new configuration settings +docker-compose up -d -V ambassador + +# curl the quote-of-the-moment service +curl localhost:8080/qotm/quote/1 + +# the response body should be a json object with a quote +{ + "hostname": "qotm-3716059461-47tnl", + "ok": true, + "quote": "The light at the end of the tunnel is interdependent on the relatedness of motivation, subcultures, and management.", + "time": "2018-08-17T21:29:24.690950", + "version": "1.3" +} +``` + +### Verify that the local qotm container is serving requests + +To ensure that the routing changes worked inspect the docker-compose logs to see the output from the local qotm service. It should look something like this: + +```bash +> docker-compose logs +Attaching to ambassador-compose_qotm_1 +qotm_1 | 2018-08-17 21:53:13 QotM 1.3 INFO: initializing on local-qotm:5000 +qotm_1 | 2018-08-17 21:53:13 QotM 1.3 INFO: * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) +qotm_1 | 2018-08-17 21:53:13 QotM 1.3 INFO: * Restarting with stat +qotm_1 | 2018-08-17 21:53:14 QotM 1.3 INFO: initializing on local-qotm:5000 +qotm_1 | 2018-08-17 21:53:14 QotM 1.3 WARNING: * Debugger is active! +qotm_1 | 2018-08-17 21:53:14 QotM 1.3 INFO: * Debugger PIN: 336-275-311 +qotm_1 | 2018-08-17 21:53:41 QotM 1.3 DEBUG: GET /: session None, username None, handler statement +qotm_1 | 2018-08-17 21:53:41 QotM 1.3 INFO: 172.19.0.3 - - [17/Aug/2018 21:53:41] "GET / HTTP/1.1" 200 - +``` + +## 4. Add Authentication + +The authentication module can be used to verify the identity and other security concerns at the entrypoint to the docker-compose cluster. + +We will use the `datawire/ambassador-auth-service` container as an example. + +### Create docker-compose.yaml service entry + +Update the `docker-compose.yaml` file to include the new `auth` service: + +```yaml +version: '3.5' + +services: + ambassador: + image: quay.io/datawire/ambassador:0.50.1 + ports: + - 8080:80 + volumes: + # mount a volume where we can inject configuration files + - ./config:/ambassador/ambassador-config + environment: + # don't try to watch Kubernetes for configuration changes + - AMBASSADOR_NO_KUBEWATCH=no_kubewatch + qotm: + image: datawire/qotm:1.3 + ports: + - 5000 + auth: + image: datawire/ambassador-auth-service:latest + ports: + - 3000 +``` + +### Create the auth.yaml configuration + +Make a new file called `config/auth.yaml` with an auth definition inside: + +```yaml +--- +apiVersion: ambassador/v1 +kind: AuthService +name: authentication +auth_service: "auth:3000" +path_prefix: "/extauth" +allowed_headers: +- "x-qotm-session" +``` + +This configuration will use the `AuthService` object to ensure that all requests made to ambassador are first sent to the `auth` docker container on port `3000` before being routed to the service that is mapped to the desired route. See the Authentication documentation for more details. + +### Verify that Authentication is working + +This sample authentication service only supports basic auth on a specific route. While the route is hardcoded you can implement your own that covers all routes. We will demonstrate that accessing the authenticated route with an incorrect Authorization header will result in a 401. + +```bash +# start all new containers (eg. auth) +docker-compose up -d + +# restart the api gateway to pick up new configuration settings +docker-compose up -d -V ambassador + +# curl the quote-of-the-moment service without an auth header +curl -I localhost:8080/qotm/quote/1 + +# the response should look like this +HTTP/1.1 401 Unauthorized +x-powered-by: Express +x-request-id: da059f3e-7b9e-4d98-8428-f4f8ca742af7 +www-authenticate: Basic realm="Ambassador Realm" +content-type: text/html; charset=utf-8 +content-length: 0 +etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" +date: Fri, 17 Aug 2018 22:25:38 GMT +x-envoy-upstream-service-time: 1 +server: envoy + +# now try with a specific username and password +curl -I --user username:password localhost:8080/qotm/quote/1 + +# the response should be a 200 +HTTP/1.1 200 OK +content-type: application/json +x-qotm-session: 5b75f31f-1155-4827-ab28-74d0f98573da +content-length: 217 +server: envoy +date: Fri, 17 Aug 2018 22:29:43 GMT +x-envoy-upstream-service-time: 2 +``` + +## 5. Tracing + +As a final example we will configure Ambassador to send Zipkin traces to Jaeger. Integrating Zipkin into your services can be a vital glimpse into the performance bottlenecks of a distributed system. + +### Add the Jaeger container to the docker-compose.yaml file + +Building on our original `docker-compose.yaml` file, we can add a new service called `tracing` to the list: + +```yaml +version: '3.5' + +services: + ambassador: + image: quay.io/datawire/ambassador:0.50.1 + ports: + - 8080:80 + volumes: + # mount a volume where we can inject configuration files + - ./config:/ambassador/ambassador-config + environment: + # don't try to watch Kubernetes for configuration changes + - AMBASSADOR_NO_KUBEWATCH=no_kubewatch + qotm: + image: datawire/qotm:1.3 + ports: + - 5000 + auth: + image: datawire/ambassador-auth-service:latest + ports: + - 3000 + tracing: + image: jaegertracing/all-in-one:latest + environment: + COLLECTOR_ZIPKIN_HTTP_PORT: 9411 + ports: + - 5775:5775/udp + - 6831:6831/udp + - 6832:6832/udp + - 5778:5778 + - 16686:16686 + - 14268:14268 + - 9411:9411 +``` + +### Create a tracing configuration file for Ambassador + +Add a new configuration file `config/tracing.yaml` with these contents: + +```yaml +--- +apiVersion: ambassador/v1 +kind: TracingService +name: tracing +service: tracing:9411 +driver: zipkin +``` + +This will forward all of Ambassador's traces to the `tracing` service. + +### Make requests and observe the traces + +After reloading the Docker containers and configuration we should be able to make requests to the qotm service and see the traces in the Jaeger front-end UI. + +```bash +# start all new containers (eg. tracing) +docker-compose up -d + +# restart the api gateway to pick up new configuration settings +docker-compose up -d -V ambassador + +# curl the quote-of-the-moment service as many times as you would like +curl --user username:password localhost:8080/qotm/quote/1 +``` + +In a browser you can go to [http://localhost:16686/](http://localhost:16686/) and search for traces. To make this demonstration more useful one should implement Zipkin tracing middleware into their webserver. + +## Next Steps + +We have demonstrated that all the configurations that would normally be stored in kubernetes annotations can be saved as a yaml document in a volume mapped to `/ambassador/ambassador-config` within the Ambassador docker container. Hopefully this guide can be used to test new configurations locally before moving to a Kubernetes cluster. Of course, there will be differences between docker-compose and the Kubernetes implementation and one should be sure to test thoroughly in the latter before moving to production. diff --git a/user-guide/early-access.md b/user-guide/early-access.md new file mode 100644 index 0000000000..59f4e2c2ca --- /dev/null +++ b/user-guide/early-access.md @@ -0,0 +1,23 @@ +# Ambassador Early Access Releases + +From time to time, Ambassador may ship early access releases to test major changes. **Early access releases are not supported for production use**, but are intended to gain early feedback from our community prior to shipping a release. + +Early access releases will always have names that include the string "-ea" followed by a build number, for example `0.50.0-ea1` is the first early access build of Ambassador 0.50.0. + +## Ambassador 0.50 Early Access + +Ambassador 0.50 is a major revamp of Ambassador's architecture, with support for Envoy v2 configuration, ADS, and significant internal refactoring. For details on Ambassador 0.50, see the [Ambassador 0.50 Early Access blog post](https://blog.getambassador.io/announcing-ambassador-0-50-early-access-1-with-v2-config-ads-support-cd785276a60e). + +### Installing Ambassador Early Access releases + +We do not recommend Helm for early access releases. Instead, use a Kubernetes deployment as usual, but use image `quay.io/datawire/ambassador:0.50.0-ea5`. + +We recommend testing with shadowing, as documented below, before switching to any new Ambassador release. We also recommend testing with shadowing for all early access releases before deploying in production. + +## Testing with shadowing + +One strategy for testing early access releases involves using Ambassador ID and traffic shadowing. You can do the following: + +1. Install Ambassador Early Access on your cluster with a unique Ambassador ID. +2. Shadow traffic from your production Ambassador instance to the Ambassador Early Access release. +3. Monitor the Early Access release to determine if there are any problems. diff --git a/user-guide/enabling-authentication.md b/user-guide/enabling-authentication.md new file mode 100644 index 0000000000..5fe2f171a9 --- /dev/null +++ b/user-guide/enabling-authentication.md @@ -0,0 +1,5 @@ +# Enabling Authentication + +Ambassador supports a range of authentication implementations, with a simple demonstration service provided within the open source Ambassador, and a more comprehensive OAuth/OIDC Authentication service provided within Ambassador Pro. + +Expand this section of the documentation to learn more. diff --git a/user-guide/getting-started.md b/user-guide/getting-started.md new file mode 100644 index 0000000000..63cc62ba44 --- /dev/null +++ b/user-guide/getting-started.md @@ -0,0 +1,248 @@ +# Deploying Ambassador to Kubernetes + +In this tutorial, we'll walk through the process of deploying Ambassador in Kubernetes for ingress routing. Ambassador provides all the functionality of a traditional ingress controller (i.e., path-based routing) while exposing many additional capabilities such as [authentication](/user-guide/auth-tutorial), URL rewriting, CORS, rate limiting, and automatic metrics collection (the [mappings reference](/reference/mappings) contains a full list of supported options). For more background on Kubernetes ingress, [read this blog post](https://blog.getambassador.io/kubernetes-ingress-nodeport-load-balancers-and-ingress-controllers-6e29f1c44f2d). + +Ambassador is designed to allow service authors to control how their service is published to the Internet. We accomplish this by permitting a wide range of annotations on the *service*, which Ambassador reads to configure its Envoy Proxy. Below, we'll use service annotations to configure Ambassador to map `/httpbin/` to `httpbin.org`. + +## 1. Deploying Ambassador + +To deploy Ambassador in your default namespace, first you need to check if Kubernetes has RBAC enabled: + +```shell +kubectl cluster-info dump --namespace kube-system | grep authorization-mode +``` + +If you see something like `--authorization-mode=Node,RBAC` in the output, then RBAC is enabled. The majority of current hosted Kubernetes providers (such as GKE) create +clusters with RBAC enabled by default, and unfortunately the above command may not return any information indicating this. + +Note: If you're using Google Kubernetes Engine with RBAC, you'll need to grant permissions to the account that will be setting up Ambassador. To do this, get your official GKE username, and then grant `cluster-admin` role privileges to that username: + +``` +$ kubectl create clusterrolebinding my-cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format="value(config.account)") +``` + +If RBAC is enabled: + +```shell +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-rbac.yaml +``` + +Without RBAC, you can use: + +```shell +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml +``` + +We recommend downloading the YAML files and exploring the content. You will see +that an `ambassador-admin` NodePort Service is created (which provides an +Ambassador Diagnostic web UI), along with an ambassador ClusterRole, ServiceAccount and ClusterRoleBinding (if RBAC is enabled). An Ambassador Deployment is also created. + +For production configurations, we recommend you download these YAML files as your starting point, and customize them accordingly (e.g., your namespace). + + +## 2. Defining the Ambassador Service + +Ambassador is deployed as a Kubernetes Service that references the ambassador +Deployment you deployed previously. Create the following YAML and put it in a file called `ambassador-service.yaml`. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: ambassador +spec: + type: LoadBalancer + externalTrafficPolicy: Local + ports: + - port: 80 + selector: + service: ambassador +``` + +Deploy this service with `kubectl`: + +```shell +$ kubectl apply -f ambassador-service.yaml +``` + +The YAML above creates a Kubernetes service for Ambassador of type `LoadBalancer`, and configures the `externalTrafficPolicy` to propagate [the original source IP](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) of the client. All HTTP traffic will be evaluated against the routing rules you create. Note that if you're not deploying in an environment where `LoadBalancer` is a supported type (such as minikube), you'll need to change this to a different type of service, e.g., `NodePort`. + +## 3. Creating your first route + +Create the following YAML and put it in a file called `httpbin.yaml`. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: httpbin + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin_mapping + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org +spec: + ports: + - name: httpbin + port: 80 +``` + +Then, apply it to the Kubernetes with `kubectl`: + +```shell +$ kubectl apply -f httpbin.yaml +``` + +When the service is deployed, Ambassador will notice the `getambassador.io/config` annotation on the service, and use the `Mapping` contained in it to configure the route. (There's no restriction on what kinds of Ambassador configuration can go into the annotation, but it's important to note that Ambassador only looks at annotations on Kubernetes `Service`s.) + +In this case, the mapping creates a route that will route traffic from the `/httpbin/` endpoint to the public `httpbin.org` service. Note that we are using the `host_rewrite` attribute for the `httpbin_mapping` — this forces the HTTP `Host` header, and is often a good idea when mapping to external services. Ambassador supports [many different configuration options](/reference/configuration). + +## 4. Testing the Mapping + +To test things out, we'll need the external IP for Ambassador (it might take some time for this to be available): + +```shell +kubectl get svc -o wide ambassador +``` + +Eventually, this should give you something like: + +``` +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +ambassador 10.11.12.13 35.36.37.38 80:31656/TCP 1m +``` +You should now be able to use `curl` to `httpbin` (don't forget the trailing `/`): + +```shell +$ curl 35.36.37.38/httpbin/ +``` + +or on minikube: + +```shell +$ minikube service list +|-------------|----------------------|-----------------------------| +| NAMESPACE | NAME | URL | +|-------------|----------------------|-----------------------------| +| default | ambassador-admin | http://192.168.99.107:30319 | +| default | ambassador | http://192.168.99.107:31893 | +|-------------|----------------------|-----------------------------| +$ curl http://192.168.99.107:31893/httpbin/ +``` + +or on Docker for Mac/Windows + +```shell +$ kubectl get svc +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +ambassador LoadBalancer 10.106.108.64 localhost 80:32324/TCP 13m +ambassador-admin NodePort 10.107.188.149 8877:30993/TCP 14m +httpbin ClusterIP 10.107.77.153 80/TCP 13m +kubernetes ClusterIP 10.96.0.1 443/TCP 84d +$ curl http://localhost/httpbin/ +``` + +## 5. Adding a Service + +You can add a Service route simply by deploying it with an appropriate Ambassador annotation. For example, we can deploy the QoTM service locally in this cluster, and automatically map it through Ambassador by creating `qotm.yaml` with the following configuration: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + service: qotm +spec: + selector: + app: qotm + ports: + - port: 80 + name: http-qotm + targetPort: http-api +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: qotm +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: qotm + spec: + containers: + - name: qotm + image: datawire/qotm:1.2 + ports: + - name: http-api + containerPort: 5000 + readinessProbe: + httpGet: + path: /health + port: 5000 + initialDelaySeconds: 30 + periodSeconds: 3 + resources: + limits: + cpu: "0.1" + memory: 100Mi +``` + +and then applying it with: + +``` +kubectl apply -f qotm.yaml +``` + +A few seconds after the QoTM service is running, Ambassador should be configured for it. Try it with + +```shell +$ curl http://${AMBASSADOR_IP}/qotm/ +``` + +## 6. The Diagnostics Service in Kubernetes + +Ambassador includes an integrated diagnostics service to help with troubleshooting. By default, this is not exposed to the Internet. To view it, we'll need to get the name of one of the Ambassador pods: + +``` +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-3655608000-43x86 1/1 Running 0 2m +ambassador-3655608000-w63zf 1/1 Running 0 2m +``` + +Forwarding local port 8877 to one of the pods: + +``` +kubectl port-forward ambassador-3655608000-43x86 8877 +``` + +will then let us view the diagnostics at http://localhost:8877/ambassador/v0/diag/. + +## 7. Next + +We've just done a quick tour of some of the core features of Ambassador: diagnostics, routing, configuration, and authentication. + +- Join us on [Slack](https://join.slack.com/t/datawire-oss/shared_invite/enQtMzcwMDEwMTc5ODQ3LTE1NmIzZTFmZWE0OTQ1NDc2MzE2NTkzMDAzZWM0MDIxZTVjOGIxYmRjZjY3N2M2Mjk4NGI5Y2Q4NGY4Njc1Yjg); +- Learn how to [add authentication](/user-guide/auth-tutorial) to existing services; or +- Learn how to [add rate limiting](/user-guide/rate-limiting-tutorial) to existing services; or +- Learn how to [add tracing](/user-guide/tracing-tutorial); or +- Learn how to [use gRPC with Ambassador](/user-guide/grpc); or +- Read about [configuring Ambassador](/reference/configuration). diff --git a/user-guide/gitops-ambassador.md b/user-guide/gitops-ambassador.md new file mode 100644 index 0000000000..0ea83adcc7 --- /dev/null +++ b/user-guide/gitops-ambassador.md @@ -0,0 +1,31 @@ +# Implementing GitOps with Ambassador + +As all of the Ambassador configuration is described via annotations in Kubernetes YAML files, it is very easy to implement a "GitOps" style workflow -- in fact, if a team is already following this way of working for deploying applications and configurations, no additional machinery or set up should be required. + + +# Continuous Delivery with Ambassador and GitOps + +"[GitOps](https://www.weave.works/technologies/gitops/)" is the name given by the Weaveworks team for how they use developer tooling to drive operations and to implement continuous delivery. GitOps is implemented by using the Git distributed version control system (DVCS) as a single source of truth for declarative infrastructure and applications. + + +## How does GitOps work? + +Every developer within a team can issue pull requests against a Git repository, and when merged, a "diff and sync" tool detects a difference between the intended and actual state of the system. Tooling can then be triggered to update and synchronise the infrastructure to the intended state. + +Using the GitOps practices, automated build/delivery pipelines detect and roll out changes to infrastructure when changes are made to Git. This practice does not enforce specific tools or products, and instead only requires certain functionality (such as the "diff and sync") to be provided by the chosen tools. As Ambassador relies on declarative configuration it is fully compatible with integration into a GitOps workflow. + + +## Developer Workflow with GitOps + +The Datawire interpretation of the guidelines for Weaveworks' implementation of GitOps, which uses containers and Kubernetes for deployment, includes: + + + +1. Everything within the software system that can be described as code must be stored in Git: By using Git as the source of truth, it is possible to observe a cluster and compare it with the desired state. The goal is to describe and version control all aspects of a systems: code, configuration, routing, security policies, rate limiting, and monitoring/alerting. +1. The 'kubectl' Kubernetes CLI tool should not be used directly: As a general rule, it is not a good practice to deploy directly to the cluster using kubectl (in the same regard as it is not recommended to manually deploy locally built binaries to production). + 1. The Weaveworks team argue that many people let their CI tool drive deployment, and by doing this they are not practicing good separation of concerns, + 1. Deploying all changes (code and config) via a pipeline allows verification and validation, for example, a pipeline can check for potential route naming collisions, or an invalid security policy +1. Automate the "diff and sync" of codified required state within git and the associated actual state of the system: As soon as the continually executed "diff" process detects that either an automated process merges an engineer's changeset or the cluster state deviates from the current specification, a "sync" should be triggers to converge the actual state to what is specified within the git-based single source of truth. + 1. Weavework use a Kubernetes controller that follows an "[operator pattern](https://coreos.com/blog/introducing-operators.html)": By extending the functionality offered by Kubernetes, using a custom [controller](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) that follows the operator pattern, the cluster can be configured to always stay in sync with the Git-based 'source of truth'. + 1. The Weaveworks team uses "diff" and "sync" tools such as the open source [kubediff,](https://github.com/weaveworks/kubediff) as well as internal tools like "terradiff" and "ansiblediff" (for Terraform and Ansible, respectively), that compare the intended state cluster state with actual state. + 1. The [AppDirect engineering team](https://blog.getambassador.io/fireside-chat-with-alex-gervais-accelerating-appdirect-developer-workflow-with-ambassador-7586597b1c34) write Ambassador configuration within each team's Kubernetes service YAML manifests. These are stored in git and follow the same review/approval process as any other code unit, and the CD pipeline listens on changes to the git repo and applies the diff to Kubernetes diff --git a/user-guide/grpc.md b/user-guide/grpc.md new file mode 100644 index 0000000000..1615d95180 --- /dev/null +++ b/user-guide/grpc.md @@ -0,0 +1,298 @@ +# gRPC and Ambassador + +Ambassador makes it easy to access your services from outside your application. This includes gRPC services, although a little bit of additional configuration is required: by default, Envoy connects to upstream services using HTTP/1.x and then upgrades to HTTP/2 whenever possible. However, gRPC is built on HTTP/2 and most gRPC servers do not speak HTTP/1.x at all. Ambassador must tell its underlying Envoy that your gRPC service only wants to speak that HTTP/2, using the `grpc` attribute of a `Mapping`. + +## Writing a gRPC service for Ambassador + +There are many examples and walkthroughs on how to write gRPC applications so that is not what this article will aim to accomplish. If you do not yet have a service written you can find examples of gRPC services in all supported languages here: [gRPC Quickstart](https://grpc.io/docs/quickstart/) + +This document will use the [gRPC python helloworld example](https://github.com/grpc/grpc/tree/master/examples/python/helloworld) to demonstrate how to configure a gRPC service with Ambassador. + +Follow the example up through [Run a gRPC application](https://grpc.io/docs/quickstart/python.html#run-a-grpc-application) to get started. + +### Dockerize + +After building our gRPC application and testing it locally, we need to package it as a Docker container and deploy it to Kubernetes. + +To run a gRPC application, we need to include the client/server and the protocol buffer definitions. + + +For gRPC with python, we need to install `grpcio` and the common protos. + +```Dockerfile +FROM python:2.7 + +WORKDIR /grpc + +ENV PATH "$PATH:/grpc" + +COPY greeter_server.py /grpc +COPY helloworld_pb2.py /grpc +COPY helloworld_pb2_grpc.py /grpc + +RUN python -m pip install grpcio +RUN python -m pip install grpcio-tools googleapis-common-protos + +CMD ["python", "./greeter_server.py"] + +EXPOSE 50051 +``` + +Create the container and test it: + +```shell +$ docker build -t /grpc_example . +$ docker run -p 50051:50051 /example +``` +Where `` is your Docker profile. + +Switch to another terminal and, from the same directory, run the `greeter_client`. +The output should be the same as running it outside of the container. + +```shell +$ docker run -p 50051:50051 /example +Greeter client received: Hello, you! +``` + +Once you verify the container works, push it to your Docker registry: + +```shell +$ docker push /grpc_example +``` + +### Mapping gRPC Services + +Ambassador `Mapping`s are based on URL prefixes; for gRPC, the URL prefix is the full service name, including the package path (`package.service`). These are defined in the `.proto` definition file. In the example [proto definition file](https://github.com/grpc/grpc/blob/master/examples/protos/helloworld.proto) we see: + +``` +package helloworld; + +// The greeting service definition. +service Greeter { ... } +``` + +so the URL `prefix` is `helloworld.Greeter` and the mapping would be: + +```yaml + --- + apiVersion: ambassador/v0 + kind: Mapping + name: grpc_py_mapping + grpc: True + prefix: /helloworld.Greeter/ + rewrite: /helloworld.Greeter/ + service: grpc-example +``` + +Note the `grpc: true` line —- this is what tells Envoy to use HTTP/2 so the request can communicate with the backend service. Also note that you'll need `prefix` and `rewrite` the same here, since the gRPC service needs the package and service to be in the request to do the right thing. + +### Deploying to Kubernetes +`grpc_example.yaml` + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: grpc-example + name: grpc-example + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: grpc_py_mapping + grpc: True + prefix: /helloworld.Greeter/ + rewrite: /helloworld.Greeter/ + service: grpc-example +spec: + type: ClusterIP + ports: + - name: grpc-greet + port: 80 + targetPort: grpc-api + selector: + service: grpc-example +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: grpc-example +spec: + replicas: 1 + template: + metadata: + labels: + service: grpc-example + spec: + containers: + - name: grpc-example + image: /grpc_example + ports: + - name: grpc-api + containerPort: 50051 + restartPolicy: Always +``` + +After adding the Ambassador mapping to the service, the rest of the Kubernetes deployment YAML file is pretty straightforward. We need to identify the container image to use, expose the `containerPort` to listen on the same port the Docker container is listening on, and map the service port (80) to the container port (50051). + +Once you have the YAML file configured, deploy it to your cluster with kubectl. + +```shell +$ kubectl apply -f grpc_example.yaml +``` + +### Testing the deployment + +Make sure to test your Kubernetes deployment before making more advanced changes (like adding TLS). To test any service with Ambassador, we will need the hostname of the running Ambassador service which you can get with: + +```shell +$ kubectl get service ambassador -o wide +``` +Which should return something similar to: + +``` +NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE +ambassador 10.11.12.13 35.36.37.38 80:31656/TCP 1m +``` +where `EXTERNAL-IP` is the `$AMBASSADORHOST` and 80 is the `$PORT`. + +You will need to open the `greeter_client.py` and change `localhost:50051` to `$AMBASSADORHOST:$PORT` + +```diff +- with grpc.insecure_channel('localhost:50051') as channel: ++ with grpc.insecure_channel(‘$AMBASSADORHOST:$PORT’) as channel: + stub = helloworld_pb2_grpc.GreeterStub(channel) + response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) + print("Greeter client received: " + response.message) +``` + +After making that change, simply run the client again and you will see the gRPC service in your cluster responds! + +```shell +$ python greeter_client.py +Greeter client received: Hello, you! +``` + +### gRPC and TLS + +There is some extra configuration required to connect to a gRPC service through Ambassador over an encrypted channel. Currently, the gRPC call is being sent over cleartext to Ambassador which proxies it to the gRPC application. + +![](/images/gRPC-TLS.png) + +If you want to add TLS encyrption to your gRPC calls, first you need to tell Ambassador to add [ALPN protocols](/reference/core/tls) which are required by HTTP/2 to do TLS. Next, you need to change the client code slightly and tell it to open a secure RPC channel with Ambassador. + +```diff +- with grpc.insecure_channel(‘$AMBASSADORHOST:$PORT’) as channel: ++ with grpc.secure_channel(‘$AMBASSADORHOST:$PORT’, grpc.ssl_channel_credentials()) as channel: + stub = helloworld_pb2_grpc.GreeterStub(channel) + response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) + print("Greeter client received: " + response.message) +``` +`grpc.ssl_channel_credentials(root_certificates=None, private_key=None, certificate_chain=None)`returns the root certificate that will be used to validate the certificate and public key sent by Ambassador. +The default values of `None` tells the gRPC runtime to grab the root certificate from the default location packaged with gRPC and ignore the private key and certificate chain fields. + +Ambassador is now terminating TLS from the gRPC client and proxying the call to the application over cleartext. + +![](/images/gRPC-TLS-Ambassador.png) + +Refer to the Ambassador [TLS termination guide](/user-guide/tls-termination) for more information on the TLS module. + +[gRPC provides examples](https://grpc.io/docs/guides/auth.html) with proper syntax for other languages. Generally, passing no arguments to the method that requests credentials gives the same behavior as above. + +Refer to the languages [API Reference](https://grpc.io/docs/) if this is not the case. + +#### Originating TLS with gRPC Service + +![](/images/gRPC-TLS-Originate.png) + +Ambassador can originate TLS with your gRPC service so the entire RPC channel is encyrpted. To configure this, first get some TLS certificates and configure the server to open a secure channel with them. Using self-signed certs this can be done with openssl and adding a couple of lines to the server code. + +```diff +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) ++ with open('certs/server.key', 'rb') as f: ++ private_key = f.read() ++ with open('certs/server.crt', 'rb') as f: ++ cert_chain = f.read() ++ server_creds = grpc.ssl_server_credentials( ( (private_key, cert_chain), ) ) + helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server) +- server.add_insecure_port('[::]:50052') ++ server.add_secure_port('[::]:50052', server_creds) + server.start() +``` +Rebuild your docker container **making sure the certificates are included** and follow the same steps of testing and deploying to kubernetes. You will need to make a small change the the client code to test locally. + +```diff +- with grpc.insecure_channel(‘localhost:$PORT’) as channel: ++ with grpc.secure_channel(‘localhost:$PORT’, grpc.ssl_channel_credentials(open('certs/server.crt', 'rb').read())) as channel: + stub = helloworld_pb2_grpc.GreeterStub(channel) + response = stub.SayHello(helloworld_pb2.HelloRequest(name='you')) + print("Greeter client received: " + response.message) +``` + +Once deployed we will need to tell Ambassador to originate TLS to the application. + +``` +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: grpc-py + name: grpc-py + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: grpc_py_mapping + grpc: True + tls: upstream + prefix: /hello.Greeter/ + rewrite: /hello.Greeter/ + service: https://grpc-py +spec: + type: ClusterIP + ports: + - name: grpc-greet + port: 443 + targetPort: grpc-api + selector: + service: grpc-py +``` + +``` + --- + apiVersion: ambassador/v0 + kind: Module + name: tls + config: + server: + enabled: true + alpn_protocols: h2 + client: + enabled: false + upstream: + alpn_protocols: h2 +``` +We need to tell Ambassador to route to the `service:` over https and have the service listen on `443`. We also need to give tell Ambassador to use ALPN protocols when originating TLS with the application, the same way we did with TLS termination. This is done by setting `alpn_protocols: ["h2"]` under a tls-context name (like `upstream`) in the TLS module and telling the service to use that tls-context in the mapping by setting `tls: upstream`. + +Refer to the [TLS document](/reference/core/tls) for more information on TLS origination. + + +### gRPC Headers +gRPC services use [HTTP/2 headers](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md). This means that some header-based routing rules will need to be rewritten to support HTTP/2 headers. For example, `host: subdomain.host.com` needs to be rewitten using the `headers: ` attribute with the `:authority` header: + +``` +headers: + :authority: subdomain.host.com +``` + +## Note + +Some [Kubernetes ingress controllers](https://kubernetes.io/docs/concepts/services-networking/ingress/) do not support HTTP/2 fully. As a result, if you are running Ambassador with an ingress controller in front, you may find that gRPC requests fail even with correct Ambassador configuration. + +A simple way around this is to use Ambassador with a `LoadBalancer` service, rather than an Ingress controller. diff --git a/user-guide/helm.md b/user-guide/helm.md new file mode 100644 index 0000000000..c168c76f17 --- /dev/null +++ b/user-guide/helm.md @@ -0,0 +1,23 @@ +# Installing Ambassador with Helm + +[Helm](https://helm.sh) is a package manager for Kubernetes. Ambassador is available as a Helm chart if you use Helm for package management. To install with Helm: + +1. Add the Ambassador Helm repository: + + ``` + helm repo add datawire https://www.getambassador.io + ``` + +2. Install the Ambassador Chart: + + ``` + helm upgrade --install --wait my-release datawire/ambassador + ``` + +3. Jump to [step 3](/user-guide/getting-started#3-creating-your-first-route) of the Ambassador tutorial to create your first route. + + +## Configuring the chart + +For details on how to configure the chart, see the official chart documentation here: +https://github.com/datawire/ambassador/tree/stable/helm/ambassador. \ No newline at end of file diff --git a/user-guide/incremental-migration-ambassador.md b/user-guide/incremental-migration-ambassador.md new file mode 100644 index 0000000000..a161cd61a9 --- /dev/null +++ b/user-guide/incremental-migration-ambassador.md @@ -0,0 +1,8 @@ +# Incremental migration +Many organizations are migrating from monolithic applications to a microservices-based system, and an API gateway like Ambassador can help with this transition. + +## Traffic Shadowing and Canarying +The primary benefit of using an API gateway within the development stage of a project is the ability to deploy your application or service to production and “hide” it — i.e. not expose the endpoints to end-users. A gateway can block traffic to a new endpoint, or simply not expose the endpoints publicly. Some gateways can also be configured to route only permitted traffic to a new endpoint, either via security policies or request header metadata. This allows you to test your walking skeleton application deployed into the real environment. This is more likely to give you results that are highly correlated within an actual live release — you can’t get a more production-like environment than production itself! + +## Test and QA: Shadowing and Shifting +A modern API gateway can help with testing on many levels. As mentioned previously, we can deploy a service — or a new version of a service — into production, hide this deployment via the gateway, and run acceptance and nonfunctional tests here (e.g. load tests and security analysis). This is invaluable in and of itself, but we can also use a gateway to “shadow” (duplicate) real production traffic to the new version of the service and hide the responses from the user. This allows you to learn how this service will perform under realistic use cases and load. diff --git a/user-guide/install.md b/user-guide/install.md new file mode 100644 index 0000000000..d210c4feb3 --- /dev/null +++ b/user-guide/install.md @@ -0,0 +1,25 @@ +# Installing Ambassador + +Ambassador can be installed in a variety of ways. The most common approach to installing Ambassador is directly on Kubernetes with our default, customizable manifest. + +## Kubernetes + + + + + + + +
    + + +Ambassador is designed to run in Kubernetes for production. Deploy to Kubernetes via YAML. +
    + +## Other methods + +You can also install Ambassador using Helm, Docker, or Docker Compose. + +| [![Helm](/images/helm.png)](/user-guide/helm) | [![Docker](/images/docker.png)](/about/quickstart) | [![Docker Compose](/images/docker-compose.png)](/user-guide/docker-compose) +| --- | --- | --- | +| Helm is a package manager for Kubernetes. Ambassador comes pre-packaged as a Helm chart. [Deploy to Kubernetes via Helm.](/user-guide/helm) | The Docker install will let you try Ambassador locally in seconds, but is not supported for production. [Try via Docker.](/about/quickstart) | The Docker Compose setup gives you a local development environment (a good alternative to Minikube), but is not suitable for production. [Set up with Docker Compose.](/user-guide/docker-compose) \ No newline at end of file diff --git a/user-guide/kubernetes-integration.md b/user-guide/kubernetes-integration.md new file mode 100644 index 0000000000..589b17792f --- /dev/null +++ b/user-guide/kubernetes-integration.md @@ -0,0 +1,25 @@ +# Kubernetes Integration: Ambassador Architecture Overview + +## Ambassador is a control plane + +Ambassador is a specialized [control plane for Envoy Proxy](https://blog.getambassador.io/the-importance-of-control-planes-with-service-meshes-and-front-proxies-665f90c80b3d). In this architecture, Ambassador translates configuration (in the form of Kubernetes annotations) to Envoy configuration. All actual traffic is directly handled by the high-performance [Envoy Proxy](https://www.envoyproxy.io). + +![Architecture](/images/ambassador-arch.png) + +## Details + +When a user applies a Kubernetes manifest containing Ambassador annotations, the following steps occur: + +1. Ambassador is asynchronously notified by the Kubernetes API of the change. +2. Ambassador translates the configuration into an abstract intermediate representation (IR). +3. An Envoy configuration file is generated from the IR. +4. The Envoy configuration file is validated by Ambassador (using Envoy in validation mode). +5. Assuming the file is valid configuration, Ambassador uses Envoy's [Aggregated Discovery Service](https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/v2_overview#aggregated-discovery-service) to deploy the new configuration and properly drain connections. + +## Scaling and availability + +Ambassador relies on Kubernetes for scaling, high availability, and persistence. All Ambassador configuration is stored directly in Kubernetes; there is no database. Ambassador is packaged as a single container that contains both the control plane and an Envoy Proxy instance. By default, Ambassador is deployed as a Kubernetes `deployment` and can be scaled and managed like any other Kubernetes deployment. + +## Envoy Proxy + +Ambassador closely tracks Envoy Proxy releases. A stable branch of Envoy Proxy is maintained that enables the team to cherry pick specific fixes into Ambassador. diff --git a/user-guide/monitoring-ambassador.md b/user-guide/monitoring-ambassador.md new file mode 100644 index 0000000000..f96db35825 --- /dev/null +++ b/user-guide/monitoring-ambassador.md @@ -0,0 +1,132 @@ +# Monitoring Ambassador + +Ambassador is an API gateway for microservices built on [Envoy](https://lyft.github.io/envoy/). A key feature of Envoy is the observability it enables by exposing a multitude of statistics about its own operations. Ambassador makes it easy to direct this information to a statistics and monitoring tool of your choice. + +As an example, for a given service `usersvc`, here are some interesting statistics to investigate: + +- `envoy.cluster.usersvc_cluster.upstream_rq_total` is the total number of requests that `usersvc` has received via Ambassador. The rate of change of this value is one basic measure of service utilization, i.e. requests per second. +- `envoy.cluster.usersvc_cluster.upstream_rq_2xx` is the total number of requests to which `usersvc` responded with an HTTP response indicating success. This value divided by the prior one, taken on an rolling window basis, represents the recent success rate of the service. There are corresponding `4xx` and `5xx` counters that can help clarify the nature of unsuccessful requests. +- `envoy.cluster.usersvc_cluster.upstream_rq_time` is a StatsD timer that tracks the latency in milliseconds of `usersvc` from Ambassador's perspective. StatsD timers include information about means, standard deviations, and decile values. + +## Exposing statistics via StatsD + +Statistics are exposed via the ubiquitous and well-tested [StatsD](https://github.com/etsy/statsd) protocol. + +To expose statistics via StatsD, you will need to set an environment variable `STATSD_ENABLED: true` to Ambassador's Kubernetes Deployment YAML. To set this environment variable, run `kubectl edit deployment ambassador` and add the variable to `spec.template.spec.containers[0].env`. + +The YAML snippet will look something like - + +```yaml + + spec: + containers: + - env: + - name: STATSD_ENABLED + value: "true" + - name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + image: + imagePullPolicy: IfNotPresent + +``` + + Ambassador automatically sends statistics information to a Kubernetes service called `statsd-sink` using typical StatsD protocol settings, UDP to port 8125. We have included a few example configurations in the [statsd-sink](https://github.com/datawire/ambassador/tree/master/statsd-sink) subdirectory to help you get started. Clone the repository to get local, editable copies. + + You may also override the StatsD host by setting the `STATSD_HOST` environment variable. This can be useful if you have an existing StatsD sink available in your cluster. + +## Graphite + +[Graphite](http://graphite.readthedocs.org/) is a web-based realtime graphing system. Spin up an example Graphite setup: + + kubectl apply -f statsd-sink/graphite/graphite-statsd-sink.yaml + +This sets up the `statsd-sink` service and a deployment that contains Graphite and its related infrastructure. Graphite's web interface is available at `http://statsd-sink/` from within the cluster. Use port forwarding to access the interface from your local machine: + + SINKPOD=$(kubectl get pod -l service=statsd-sink -o jsonpath="{.items[0].metadata.name}") + kubectl port-forward $SINKPOD 8080:80 + +This sets up Graphite access at `http://localhost:8080/`. + +## Prometheus + +[Prometheus](https://prometheus.io/) is an open-source monitoring and alerting system. If you use Prometheus, you should deploy a StatsD exporter as a sidecar on each of your Ambassador pods as shown in this [example](https://www.getambassador.io/yaml/ambassador/ambassador-rbac-prometheus.yaml). + +The `statsd-sink` service referenced in this example is built on the [Prometheus StatsD Exporter](https://github.com/prometheus/statsd_exporter), and configured in this [Dockerfile](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prom-statsd-exporter/Dockerfile). + +Add a Prometheus target to read from `statsd-sink` on port 9102 to complete the Prometheus configuration. + +### Configuring metrics mappings for Prometheus + +It may be desirable to change how metrics produced by the `statsd-sink` are named, labeled and grouped. + +For example, by default each service that the API Gateway serves will create a new metric using its name. For the service called `usersvc` you will see this metric: `envoy.cluster.usersvc_cluster.upstream_rq_total`. This may lead to problems if you are trying to create a single aggregate that is the sum of all similar metrics from different services. In this case it is common to differentiate the metrics for an individual service with a `label`. This can be done using a mapping. + +[Follow this guide](https://github.com/prometheus/statsd_exporter/tree/v0.6.0#metric-mapping-and-configuration) to learn how to modify your mappings. + +#### Configuring for Helm + +If you deploy using helm the value that you should change is `exporter.configuration`. Set it to something like this: + +```yaml +exporter: + configuration: | + --- + mappings: + - match: 'envoy.cluster.*.upstream_rq_total' + name: "envoy_cluster_upstream_rq_total" + timer_type: 'histogram' + labels: + cluster_name: "$1" +``` + +#### Configuring for kubectl + +In the [ambassador-rbac-prometheus](https://github.com/datawire/ambassador/blob/master/templates/ambassador/ambassador-rbac-prometheus.yaml) example template there is a `ConfigMap` that should be updated. Add your mapping to the `configuration` property. + +```yaml +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: ambassador-config +data: + exporterConfiguration: | + --- + mappings: + - match: 'envoy.cluster.*.upstream_rq_total' + name: "envoy_cluster_upstream_rq_total" + timer_type: 'histogram' + labels: + cluster_name: "$1" +``` + +### The Prometheus Operator + +If you don't already have a Prometheus setup, the [Prometheus operator](https://github.com/coreos/prometheus-operator) is a powerful way to create and deploy Prometheus instances. Once you've installed and configured the Prometheus operator, the following files can be useful: + +- [`statsd-sink-svc.yaml`](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/statsd-sink-svc.yaml) creates a `service` that points to the sidecar `statsd-sink` on the Ambassador pod, and a `ServiceMonitor` that adds the `statsd-sink` as a Prometheus target +- [`prometheus.yaml`](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prometheus.yaml) creates a `Prometheus` object that collects data from the `ServiceMonitor` specified +- [`ambassador-rbac-prometheus.yaml`](https://www.getambassador.io/yaml/ambassador/ambassador-rbac-prometheus.yaml) is an example Ambassador deployment that includes the Prometheus `statsd` exporter. The statsd exporter collects the stats data from Ambassador, translates it to Prometheus metrics, and is picked up by the Operator using the configurations above. + +Make sure that the `ServiceMonitor` is in the same namespace as Ambassador. A walk-through of the basics of configuring the Prometheus operator with Ambassador and Envoy is available [here](http://www.datawire.io/faster/ambassador-prometheus/). + +## StatsD as an Independent Deployment + +If you want to set up the StatsD sink as an independent deployment, [this example](https://github.com/datawire/ambassador/blob/master/statsd-sink/prometheus/prom-statsd-sink.yaml) configuration mirrors the Graphite and Datadog configurations. + +## Grafana + +![Grafana dashboard](images/grafana.png) + +If you're using Grafana, [Alex Gervais](https://twitter.com/alex_gervais) has written a template [Grafana dashboard for Ambassador](https://grafana.com/dashboards/4698). + +## Datadog + +If you are a user of the [Datadog](https://www.datadoghq.com/) monitoring system, pulling in Ambassador statistics is very easy. Replace the sample API key in the YAML file with your own, then launch the DogStatsD agent: + + kubectl apply -f statsd-sink/datadog/dd-statsd-sink.yaml + +This sets up the `statsd-sink` service and a deployment of the DogStatsD agent that automatically forwards Ambassador stats to your Datadog account. diff --git a/user-guide/oauth-oidc-auth.md b/user-guide/oauth-oidc-auth.md new file mode 100644 index 0000000000..3e37562c1d --- /dev/null +++ b/user-guide/oauth-oidc-auth.md @@ -0,0 +1,155 @@ +# Configuring OAuth/OIDC Authentication +--- + +Ambassador Pro adds native support for the OAuth and OIDC authentication schemes for single sign-on with an external identity providers (IDP). This guide will demonstrate configuration using Auth0 as your IDP. + +**Note:** If you need to use an IDP other than Auth0, please [Slack](https://d6e.co/slack) or email us. We are currently testing support for other IDPs, including Keycloak, Okta, and AWS Cognito. + +## Configure your IDP +You will need to configure your IDP to handle authentication requests. The way to do this varies by IDP. + +#### Auth0 +With Auth0 as your IDP, you will need to create an `Application` to handle authentication requests from Ambassador Pro. + +1. Navigate to Applications and Select "CREATE APPLICATION" + + ![](/images/create-application.png) + +2. In the pop-up window, give the application a name and create a "Machine to Machine App" + + ![](/images/machine-machine.png) + +3. Select the Auth0 Management API. Grant any scopes you may require. (You may grant none.) + + ![](/images/scopes.png) + +4. In your newly created application, click on the Settings tab, add the Domain and Callback URLs for your service and ensure the "Token Endpoint Authentication Method" is set to `Post`. The default YAML installation of Ambassador Pro uses `/callback` for the URL, so the values should be the domain name that points to Ambassador, e.g., `example.com/callback` and `example.com`. + + ![](/images/Auth0_none.png) + + +## Configure your Authentication Tenants + +**Note:** Ensure your [authentication provider](/user-guide/ambassador-pro-install/#5-single-sign-on) is set in your Ambassador Pro deployment before configuring authentication tenants. + +Ambassador Pro is integrated with your IDP via the `Tenant` custom resource definition. This is where you will tell Ambassador Pro which hosts to require authentication from and which client to use for authentication. + +To configure your tenant, create the following YAML and put it in a file called `tenants.yaml`. + +``` +--- +apiVersion: getambassador.io/v1beta1 +kind: Tenant +metadata: + name: domain1-tenant +spec: + tenants: + # The URL used to access your app. + - tenantUrl: {scheme}://{hostname or ip} + # The API Audience that is listening for authentication requests + audience: https://datawire-ambassador.auth0.com/api/v2/ + # Client ID from your authentication application + clientId: + # Client Secret from your authentication application + secret: +``` + +If you are using Auth0, get the `Client ID` and `Client Secret` from your application settings: + +![](/images/Auth0_secret.png) + +The `audience` is the API Audience of your Auth0 Management API: + +![](/images/Auth0_audience.png) + +Apply the YAML with `kubectl`. + +``` +kubectl apply -f tenants.yaml +``` + +## Configure Authentication Across Multiple Domains (Optional) +Ambassador Pro supports authentication for multiple domains where each domain is issued its own access token. For example, imagine you're hosting both `domain1.example.com` and `domain2.example.com` on the same cluster. With multi-domain support, users will receive separate authentication tokens for `domain1` and `domain2`. + +To configure multi-domain access, you will need to create another authentication endpoint with your IDP (see [Configure your IDP](/user-guide/oauth-oidc-auth/#configure-your-idp)) and create another `Tenant` for the new domain. + +Example: + +``` +--- +apiVersion: getambassador.io/v1beta1 +kind: Tenant +metadata: + name: domain1-tenant +spec: + tenants: + # Domain 1 + - tenantUrl: http://domain1.example.com + audience: https://example.auth0.com/api/v2/ + clientId: + secret: +``` + +``` +--- +apiVersion: getambassador.io/v1beta1 +kind: Tenant +metadata: + name: domain2-tenant +spec: + tenants: + # Domain 2 + - tenantUrl: http://domain2.example.com + audience: https://example.auth0.com/api/v2/ + clientId: + secret: +``` + +This will tell Ambassador Pro to configure separate access tenants for `http://domain1.example.com` and `http://domain2.example.com`. After a subsequent login to either domain, Ambassador Pro will create a separate SSO token for just that domain. + +## Test Authentication +After applying Ambassador Pro and the `tenants.yaml` file, Ambassador Pro should be configured to authenticate with your IDP. + +You can use any service to test this. From a web browser, attempt to access your service (e.g., `http://domain1.example.com/httpbin/`) and you should be redirected to a login page. Log in using your credentials and you should be redirected to your application. + +Next, test SSO by attempting to access the application from a different tab. You should be sent to your application without being redirected to the login page. + +You can also use a JWT for authentication through Ambassador Pro. To do this, click on APIs, the API you're using for the Ambassador Authentication service, and then the Test tab. Run the curl command given to get the JWT. + +![](/images/Auth0_JWT.png) + +After you have the JWT, use it to send a test `curl` to your app by passing it in the `authorization:` header. + +``` +$ curl --header 'authorization: Bearer eyeJdfasdf...' http://datawire-ambassador.com/httpbin/user-agent +{ + "user-agent": "curl/7.54.0" +} +``` + +## Configure Access Controls +By default, Ambassador Pro will require all requests be authenticated before passing through. If some services or resources do not require authentication, Ambassador Pro allows for you to configure which services you want authenticated. This is done with the `Policy` custom resource definition. + +This is an example policy for the `httpbin` service defined in the [YAML installation guide](/user-guide/getting-started#3-creating-your-first-route). + +``` +--- +apiVersion: getambassador.io/v1beta1 +kind: Policy +metadata: + name: policy +spec: + rules: + - host: example.com + path: /httpbin/ip + public: true + scope: openid + - host: example.com + path: /httpbin/user-agent + public: false + scope: openid +``` +This policy will tell Ambassador Pro to not require authentication for requests to `http://example.com/httpbin/ip`. See [Access Control](/reference/services/access-control) for more information. + +**Note:** `scope: openid` is required if your authentication server is OIDC Conformant. + diff --git a/user-guide/operators.md b/user-guide/operators.md new file mode 100644 index 0000000000..0a78e82510 --- /dev/null +++ b/user-guide/operators.md @@ -0,0 +1,14 @@ +# Operator Guide + +Unlike traditional API gateways, Ambassador has been designed to allow developers and operations to work independently from each other, with an operations team more focused towards the global deployment and configuration of the gateway. This section of the documentation focuses on the core functionality of Ambassador for operations and sysadmin teams. + +# Why Should Operators or Sysadmins Use Ambassador? +Ambassador allows developers to manage individual service/API deployments, and frees time for operations to focus on global, cross-cutting concerns such as authentication, continuous delivery integration, rate limiting and tracing. + +In more detail, Ambassador supports operations in the following ways: + +* Is simple to [deploy and operate](/user-guide/kubernetes-integration), relying entirely on Envoy and Kubernetes for routing and scaling +* Has extensive support for [TLS termination](/user-guide/tls-termination) and redirects +* Integrated [diagnostics](/reference/statistics) and [tracing](/user-guide/tracing-tutorial) for troubleshooting +* Supports running multiple Ambassadors in a cluster, with different versions, simplifying upgrades and testing +* [Integrates with Istio](/user-guide/with-istio), if you need a service mesh diff --git a/user-guide/protocol-support-ambassador.md b/user-guide/protocol-support-ambassador.md new file mode 100644 index 0000000000..8e39710d62 --- /dev/null +++ b/user-guide/protocol-support-ambassador.md @@ -0,0 +1,5 @@ +# Support for gRPC, WebSockets etc. + +Ambassador supports a range of protocols, including HTTP/1 and HTTP/2, and also a variety of Transports, such as gRPC and WebSockets. + +Expand this section of the documentation to learn more. diff --git a/user-guide/rate-limiting-tutorial.md b/user-guide/rate-limiting-tutorial.md new file mode 100644 index 0000000000..abc81892b4 --- /dev/null +++ b/user-guide/rate-limiting-tutorial.md @@ -0,0 +1,198 @@ +# Rate Limiting + +Ambassador can validate incoming requests before routing them to a backing service. In this tutorial, we'll configure Ambassador to use a simple third party rate limit service. If you don't want to implement your own rate limiting service, Ambassador Pro integrates a [powerful, flexible rate-limiting service](/user-guide/advanced-rate-limiting). + +## Before You Get Started + +This tutorial assumes you have already followed the [Ambassador Getting Started](/user-guide/getting-started) guide. If you haven't done that already, you should do that now. + +After completing [Getting Started](/user-guide/getting-started), you'll have a Kubernetes cluster running Ambassador and the Quote of the Moment service. Let's walk through adding rate limiting to this setup. + +## 1. Deploy the rate limit service + +Ambassador delegates the actual rate limit logic to a third party service. We've written a [simple rate limit service](https://github.com/datawire/ambassador/tree/master/end-to-end/ratelimit-service) that: + +- listens for requests on port 5000; +- handles gRPC `shouldRateLimit` requests; +- allows requests with the `x-ambassador-test-allow: "true"` header; and +- marks all other requests as `OVER_LIMIT`; + +Here's the YAML we'll start with: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: example-rate-limit + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: RateLimitService + name: ratelimit + service: "example-rate-limit:5000" +spec: + type: ClusterIP + selector: + app: example-rate-limit + ports: + - port: 5000 + name: http-example-rate-limit + targetPort: http-api +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: example-rate-limit +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: example-rate-limit + spec: + containers: + - name: example-rate-limit + image: agervais/ambassador-ratelimit-service:1.0.0 + imagePullPolicy: Always + ports: + - name: http-api + containerPort: 5000 + resources: + limits: + cpu: "0.1" + memory: 100Mi +``` + +This configuration tells Ambassador about the rate limit service, notably that it is serving requests at `example-rate-limit:5000`. + +Ambassador will see the annotations and reconfigure itself within a few seconds. + +## 2. Configure Ambassador Mappings + +Ambassador only validates requests on Mappings which set rate limiting descriptors. If Ambassador cannot contact the rate limit service, it will allow the request to be processed as if there were no rate limit service configuration. + +We already have the `qotm` service running, so let's apply some rate limits to the service. The easiest way to do that is to annotate the `qotm` service. While we could use `kubectl patch` for this, it's simpler to just modify the service definition and re-apply. + +### v1 API +Ambassador 0.50.0 and later requires the `v1` API Version for rate limiting. The `v1` API uses the `labels` attribute to attach rate limiting descriptors. Review the [Rate Limits configuration documentation](/reference/rate-limits#request-labels) for more information. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v1 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + service: qotm + labels: + ambassador: + - request_label_group: + - x-ambassador-test-allow: + header: "x-ambassador-test-allow" + omit_if_not_present: true +spec: + type: ClusterIP + selector: + app: qotm + ports: + - port: 80 + name: http-qotm + targetPort: http-api +``` + +### v0 API +Ambassador versions 0.40.2 and earlier use the `v0` API version which uses the `rate_limits` attribute to set rate limiting descriptors. Review the [Rate Limits configuration documentation](/reference/rate-limits#the-rate_limits-attribute) for more information. + + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: qotm + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: qotm_mapping + prefix: /qotm/ + service: qotm + rate_limits: + - descriptor: A test case + headers: + - "x-ambassador-test-allow" +spec: + type: ClusterIP + selector: + app: qotm + ports: + - port: 80 + name: http-qotm + targetPort: http-api +``` + +This configuration tells Ambassador about the rate limit rules to apply, notably that it needs the `x-ambassador-test-allow` header, and that it should set "A test case" as the `generic_key` descriptor when performing the gRPC request. + +Note that both `descriptor` and `headers` are optional. However, if `headers` are defined, **they must be part of the request in order to be rate limited**. + +Ambassador would also perform multiple requests to `example-rate-limit:5000` if we had defined multiple `rate_limits` rules on the mapping. + +## 3. Test rate limiting + +If we `curl` to a rate-limited URL: + +```shell +$ curl -v -H "x-ambassador-test-allow: probably" $AMBASSADORURL/qotm/quote/1 +``` + +We get a 429, since we are limited. + +```shell +HTTP/1.1 429 Too Many Requests +content-type: text/html; charset=utf-8 +content-length: 0 +``` + +If we set the correct header value to the service request, we will get a quote successfully: + +```shell +$ curl -v -H "x-ambassador-test-allow: true" $AMBASSADORURL/qotm/quote/1 + +TCP_NODELAY set +* Connected to 35.196.173.175 (35.196.173.175) port 80 (#0) +> GET /qotm/quote/1 HTTP/1.1 +> Host: 35.196.173.175 +> User-Agent: curl/7.54.0 +> Accept: */* +> +< HTTP/1.1 200 OK +< content-type: application/json +< x-qotm-session: 069da5de-5433-46c0-a8de-d266e327d451 +< content-length: 172 +< server: envoy +< date: Wed, 27 Sep 2017 18:53:38 GMT +< x-envoy-upstream-service-time: 25 +< +{ + \"hostname\": \"qotm-1827164760-gf534\", + \"ok\": true, + \"quote\": \"A late night does not make any sense.\", + \"time\": \"2017-09-27T18:53:39.376073\", + \"version\": \"1.1\" +} +``` + +## More + +For more details about configuring the external rate limit service, read the documentation on [external rate limit](/reference/services/rate-limit-service) and [rate_limits mapping](/reference/rate-limits). diff --git a/user-guide/rate-limiting.md b/user-guide/rate-limiting.md new file mode 100644 index 0000000000..8f918b2d2d --- /dev/null +++ b/user-guide/rate-limiting.md @@ -0,0 +1,31 @@ +# Rate Limiting at the Edge + +Rate limiting at the edge is a technique that is used to prevent a sudden or sustained increase in user traffic from breaking an API or underlying service. On the Internet, users can do whatever they want to your APIs, as you have no direct control over these end users. Whether it’s intentional or not, these users can impact the availability, responsiveness, and scalability of your service. + +## Two Approaches: Rate Limiting and Load Shedding + +Rate limiting use cases that fall into this scenario range from implementing functional requirements related to a business scenario -- for example, where requests from paying customers is prioritised over requests from non-paying trial users -- to implementing cross functional requirements, such as resilience from a malicious actor attempting to issue a denial-of-service (DoS) attack. + +A closely related technique to rate limiting is load shedding, and this can be used to selectively prioritise traffic (by dropping requests) based on the state of the entire system. For example, if a backend data store has become overloaded and slow to respond, it may be appropriate to drop (or “shed”) low priority requests or requests that are not time sensitive. + +## Use Cases and Scenarios + +The table below outlines several scenarios where rate limiting and load shedding can provide an effective solution to a range of functional and cross-functional requirements. The “Type of Rate Limiter” column provides a summary of the category of rate limiting that would be most appropriate for the scenario, and the “Specifics” column outlines what business or system properties would be involved in computing rate limiting decisions. + +| Scenario | Type of Rate Limiter | Specifics +| --- | --- | --- | +**Fairness.** One or more users are sending large volumes of requests, and thus impacting other users of the API | **User request rate limiting -** restricts each user to a predetermined number of requests per time unit.

    **Concurrent user request limiting -** limits the number of concurrent user requests that can be inflight at any given point in time. |
    • **User ID rate limiter**
    • **User property rate limiter (IP address, organisation, device etc)**
    • **Geographic rate limiter**
    • **Time-based rate limiter**
    +**Prioritisation.** The business model depends on handling high priority requests over others | **User request rate limiting** |
    • **User ID rate limiter**
    • **User property rate limiter (IP address, organisation, device, free vs non-free user etc)**
    +**Resilience.** The API backend cannot scale rapidly enough to meet request demand due to a technical issue. | **Backend utilisation load shedder -** rate limit based upon utilisation of aggregate backend instances.

    **Node/server utilisation load shedder -** rate limit based upon utilisation of individual or isolated groups of compute nodes/servers. |
    • **User ID rate limiter**
    • **User property rate limiter (IP address, organisation, device etc)**
    +**Security.** Prevent bad actors from using a DoS attack to overwhelm services, fuzzing, or brute force attacks |**User request rate limiting**

    **Node/server utilisation load shedder** |
    • **User ID rate limiter**
    • **User property rate limiter (IP address, organisation, device etc)**
    • **Service identifier load shedder e.g. login service, audit service**
    +**Responsiveness.** As per the Reactive Manifesto, responsive systems focus on providing rapid and consistent response times, establishing reliable upper bounds so they deliver a consistent quality of service | **Concurrent user request limiting**

    **Backend utilisation load shedder**

    **Node/server utilisation load shedder** |
    • **User ID rate limiter**
    • **User property rate limiter (IP address, organisation, device etc)**
    • **Service identifier load shedder e.g. login service, audit service**
    + +## Avoiding Contention with Rate Limiting Configuration: Decoupling Dev and Ops +One of the core features of Ambassador is the decentralization of configuration, allowing operations and development teams to independently control Ambassador, as well as individual application development teams to minimise collaboration when configuring independently deployable services. This same approach applies to rate limiting configuration. + +The Ambassador rate limiting configuration allows centralised operations teams to define and implement global rate limiting and load shedding policies to protect the system, while still allowing individual application teams to define rate limiting policies that enforce business rules, for example, around paying and non-paying customers (perhaps implementing the so called “freemium” model). See [Advanced Rate Limiting](/user-guide/advanced-rate-limiting) documentation for examples. + +## Benefits of Applying a Rate Limiter to the Edge +Modern applications and APIs can experience floods of traffic over a short time period (e.g. from achieving a HackerNews front page link), and increasingly bad actors and cyber criminals are targeting public-facing services. + +By implementing rate limiting and load shedding capabilities at the edge a large amount of scenarios that are bad for business can be mitigated. These capabilities also make the life of the operations and development team that much easier, as the need to constantly firefight ingress traffic is reduced. diff --git a/user-guide/security.md b/user-guide/security.md new file mode 100644 index 0000000000..6eb71979e1 --- /dev/null +++ b/user-guide/security.md @@ -0,0 +1,5 @@ +# Security: Encryption and Authentication + +Encryption like Transport-Level Security (TLS) and authentication at the edge of systems is essential with the current threat landscape, although implementing them can be non-trivial. Ambassador enables easy configuration of TLS termination, certificate rotation and associated Kubernetes configuration. + +Ambassador also supports a range of authentication implementations, with a simple demonstration service provided within the open source Ambassador, and a more comprehensive OAuth/OIDC Authentication service provided within Ambassador Pro. diff --git a/user-guide/service-mesh-integration.md b/user-guide/service-mesh-integration.md new file mode 100644 index 0000000000..c9a729e30a --- /dev/null +++ b/user-guide/service-mesh-integration.md @@ -0,0 +1,5 @@ +# Ambassador Service Mesh Integration + +Ambassador is primarily focused on the north-south (ingress) traffic use case, and many engineers also want similar functionality for east-west (cross-service) traffic. This is typically provided by a "service mesh". + +Ambassador integrates with a number of service meshes, and this section of the documentation provides details of how to configure Ambassador appropriately. diff --git a/user-guide/sni.md b/user-guide/sni.md new file mode 100644 index 0000000000..897c6fbd6f --- /dev/null +++ b/user-guide/sni.md @@ -0,0 +1,160 @@ +# Server Name Indication (SNI) + +Ambassador lets you supply separate TLS certificates for different domains, instead of using a single TLS certificate for all domains. This allows Ambassador to serve multiple secure connections on the same IP address without requiring all websites to use the same certificate. Ambassador supports this use case through its support of Server Name Indication, an extension to the TLS protocol. + +Note: SNI is only available in the [0.50 early access release](/user-guide/early-access). + +## Configuring SNI + +1. Create a TLS certificate, and store the secret in a Kubernetes secret. + ```console + kubectl create secret tls --cert --key + ``` + +2. Create a `TLSContext` resource which points to the certificate, and lists all the different hosts in the certificate. Typically, these would be the Subject Alternative Names you will be using. If you're using a wildcard certificate, you can put in any host values that you wish to use. + + ```yaml + apiVersion: ambassador/v0 + kind: TLSContext + name: + hosts: # list of hosts to match against> + - host1 + - host2 + secret: + ``` + + The `TLSContext` resource is typically added to the main Ambassador `service` where global Ambassador configuration is typically stored. + +3. Create additional `TLSContext` resources pointing to additional certificates as necessary. + +4. Configure the global TLS configuration (e.g., `redirect_cleartext_from`) in the `tls` module. The `tls` configuration applies to all `TLSContext` resources. For more information on global TLS configuration, see the [reference section on TLS](/reference/core/tls). + +## Using SNI + +SNI is designed to be configured on a per-mapping basis. This enables application developers or service owners to individually manage how their service gets exposed over TLS. To use SNI, specify your SNI host in the `mapping` resource, e.g., + +```yaml +apiVersion: ambassador/v0 +kind: Mapping +name: example-mapping +prefix: /example/ +service: example.com:80 +host: +``` +Ambassador will check if any of the `TLSContext` resources have a matching host, and if it finds one, SNI configuration will be applied to that mapping. + +Note that if the mapping does not have the `host` field, all valid SNI configurations will be applied to the given mapping. + +## Examples + +#### Multiple certificates + +In this configuration: + +* Requests with `Host: internal.example.com` header set hitting `/httpbin/` prefix get internal TLS certificates. +* Requests with `Host: external.example.com` header set hitting `/httpbin/` prefix get external TLS certificates. + + +Note that the `TLSContext` and `Mapping` objects are on the same `Service` for illustrative purposes; more typically they would be managed separately as noted above. + +```yaml +apiVersion: v1 +kind: Service +metadata: + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin-internal + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org + host: internal.example.com + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin-external + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org + host: external.example.com + --- + apiVersion: ambassador/v0 + kind: TLSContext + name: internal-context + hosts: + - internal.example.com + secret: internal-secret + --- + apiVersion: ambassador/v0 + kind: TLSContext + name: external-context + hosts: + - external.example.com + secret: external-secret + name: httpbin +spec: + ports: + - port: 80 + targetPort: 80 +``` + + +#### Multiple mappings with a fallback + +In this configuration: + +* Requests with `Host: host.httpbin.org` header set hitting `/httpbin/` prefix get httpbin TLS certificates. +* Requests with `Host: host.mockbin.org` header set hitting `/mockbin/` prefix get mockbin TLS certificates +* The `frontend` mapping will be accessible via both via `host.httpbin.org` and `host.mockbin.org` + +```yaml +apiVersion: v1 +kind: Service +metadata: + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org + host: host.httpbin.org + --- + apiVersion: ambassador/v0 + kind: Mapping + name: mockbin + prefix: /mockbin/ + service: mockbin.org:80 + host_rewrite: mockbin.org + host: host.mockbin.org + --- + apiVersion: ambassador/v0 + kind: TLSContext + name: httpbin + hosts: + - host.httpbin.org + secret: httpbin-secret + --- + apiVersion: ambassador/v0 + kind: TLSContext + name: mockbin + hosts: + - host.mockbin.org + secret: mockbin-secret + --- + # This mapping gets all the available SNI configurations applied to it + apiVersion: ambassador/v0 + kind: Mapping + name: frontend + prefix: / + service: frontend + name: httpbin +spec: + ports: + - port: 80 + targetPort: 80 +``` \ No newline at end of file diff --git a/user-guide/tls-termination.md b/user-guide/tls-termination.md new file mode 100644 index 0000000000..3c5e69a527 --- /dev/null +++ b/user-guide/tls-termination.md @@ -0,0 +1,208 @@ +# TLS Termination + +To enable TLS termination for Ambassador you'll need a few things: + +1. You'll need a TLS certificate. +2. For any production use, you'll need a DNS record that matches your TLS certificate's `Common Name`. +3. You'll need to store the certificate in a Kubernetes `secret`. +4. Configure other Ambassador TLS options using the `tls` module. + +All these requirements mean that it's easiest to decide to enable TLS _before_ you configure Ambassador the first time. It's possible to switch after setting up Ambassador, but it's annoying. + +## 1. You'll need a TLS certificate. + +There are a great many ways to get a certificate; [Let's Encrypt](https://www.letsencrypt.org) is a good option if you're not already working with something else. Check out the "Certificate Manager" section below to get set up with Let's Encrypt on Kubernetes. + +Note that requesting a certificate _requires_ a `Common Name` (`CN`) for your Ambassador. The `CN` becomes very important when you try to use HTTPS in practice: if the `CN` does not match the DNS name you use to reach the Ambassador, most TLS libraries will refuse to make the connection. So use a DNS name for the `CN`, and in step 2 make sure everything matches. + +## 2. You'll need a DNS name. + +As noted above, the DNS name must match the `CN` in the certificate. The simplest way to manage this is to create an `ambassador` Kubernetes service up front, before you do anything else, so that you can point DNS to whatever Kubernetes gives you for it -- then don't delete the `ambassador` service, even if you later need to update it or delete and recreate the `ambassador` deployment. + +```shell +kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-https.yaml +``` + +will create a minimal `ambassador` service for this purpose; you can then use its external appearance to configure either a `CNAME` or an `A` record in DNS. Make sure that there's a matching `PTR` record, too. + +It's OK to include annotations on the `ambassador` service at this point, if you need to configure additional TLS options (see below for more on this). + +## 3. You'll need to store the certificate in a Kubernetes `secret`. + +Create a Kubernetes `secret` named `ambassador-certs`: + +```shell +kubectl create secret tls ambassador-certs --cert=$FULLCHAIN_PATH --key=$PRIVKEY_PATH +``` + +where `$FULLCHAIN_PATH` is the path to a single PEM file containing the certificate chain for your cert (including the certificate for your Ambassador and all relevant intermediate certs -- this is what Let's Encrypt calls `fullchain.pem`), and `$PRIVKEY_PATH` is the path to the corresponding private key. + +When Ambassador starts, it will notice the `ambassador-certs` secret and turn TLS on. + +**Important.** Note that the `ambassador-certs` Secret _must_ be in the same Kubernetes namespace as the Ambassador Service. + +**Important.** If you've already created the `ambassador` deployment in step 2 or you're adding TLS termination to an existing deployment, you **MUST** restart it. Ambassador looks for the `ambassador-certs` when it starts and only watches service changes later on ([#474](https://github.com/datawire/ambassador/issues/474)). If high availability is not an issue, simply delete the `ambassador` pods (after a short downtime, the deployment will start new pods for you): + +```shell +kubectl delete pods -l service=ambassador +``` + +To ensure high availability, you can force a no-op rolling update (https://github.com/kubernetes/kubernetes/issues/27081): + +```shell +kubectl patch deployment ambassador -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}" +``` + +##### Configuring using a user defined secret + +If you do not wish to use a secret named `ambassador-certs`, then you can tell Ambassador to use your own secret. This can be particularly useful if you want to use different secrets for different Ambassador deployments. + +Create the secret - +```shell +kubectl create secret tls user-secret --cert=$FULLCHAIN_PATH --key=$PRIVKEY_PATH +``` + +And then, configure Ambassador's TLS module like the following - + +```yaml +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + server: + enabled: True + secret: user-secret +``` + +This will make Ambassador load a secret called `user-secret` to configure TLS termination. + +Note: If `ambassador-certs` is present in the cluster and the TLS module is configured to load a custom secret, then `ambassador-certs` will take precedence, and the custom secret will be ignored. + +## 4. Configure other Ambassador TLS options using the `tls` module. + +Ambassador will detect the presence of a Secret named `ambassador-certs` and begin serving traffic over HTTPS automatically. However, there are additional options that you may wish to configure via Ambassador's `tls` Module: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + # The 'server' block configures TLS termination. 'enabled' is the only + # required element. + server: + # If 'enabled' is True, TLS termination will be enabled. + enabled: True + + # If you set 'redirect_cleartext_from' to a port number, HTTP traffic + # to that port will be redirected to HTTPS traffic. Typically you would + # use port 80, of course. + # redirect_cleartext_from: 80 + + # These are optional. They should not be present unless you are using + # a custom Docker build to install certificates onto the container + # filesystem, in which case YOU WILL STILL NEED TO SET enabled: True + # above. + # + # cert_chain_file: /etc/certs/tls.crt # remember to set enabled! + # private_key_file: /etc/certs/tls.key # remember to set enabled! + + # Enable TLS ALPN protocol, typically HTTP2 to negotiate it with HTTP2 + # clients over TLS. This must be set to be able to use grpc over TLS. + # alpn_protocols: h2 + + # The 'client' block configures TLS client-certificate authentication. + # 'enabled' is the only required element. + client: + # If 'enabled' is True, TLS client-certificate authentication will occur. + enabled: False + + # If 'cert_required' is True, TLS client certificates will be required + # for every connection. + # cert_required: False + + # This is optional. It should not be present unless you are using + # a custom Docker build to install certificates onto the container + # filesystem, in which case YOU WILL STILL NEED TO SET enabled: True + # above. + # + # cacert_chain_file: /etc/cacert/tls.crt # remember to set enabled! +``` + +Of these, `redirect_cleartext_from` is the most likely to be relevant: to make Ambassador redirect HTTP traffic on port 80 to HTTPS on port 443, you _must_ use the `tls` module: + +```yaml +--- +apiVersion: ambassador/v0 +kind: Module +name: tls +config: + server: + enabled: True + redirect_cleartext_from: 80 +``` + +is the minimal YAML to do this. + +If you need a `tls` module, it's simplest to include it as an `annotation` on the `ambassador` service itself, like so: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: ambassador + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Module + name: tls + config: + server: + enabled: True + redirect_cleartext_from: 80 +spec: + ports: + - name: http + protocol: TCP + port: 80 + - name: https + protocol: TCP + port: 443 + ... +``` + +**Important.** Note that the name of the Module is case-sensitive! It must be `name: tls` as opposed to `name: TLS`. + +## Certificate Manager + +Jetstack's [cert-manager](https://github.com/jetstack/cert-manager) lets you easily provision and manage TLS certificates on Kubernetes. No special configuration is required to use Ambassador with `cert-manager`. + +Once `cert-manager` is running and you have successfully created the Issuer, you can request a Certificate such as the following: + +```yaml +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Certificate +metadata: + name: cloud-foo-com + # cert-manager will put the resulting Secret in the same Kubernetes namespace + # as the Certificate. Therefore you should put this Certificate in the same + # namespace as Ambassador. + namespace: default +spec: + secretName: ambassador-certs + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + commonName: cloud.foo.com + dnsNames: + - cloud.foo.com + acme: + config: + - dns01: + provider: clouddns + domains: + - cloud.foo.com +``` + +Note the `secretName` line above. When the certificate has been stored in the secret, restart Ambassador to pick up the new certificate. diff --git a/user-guide/tracing-tutorial.md b/user-guide/tracing-tutorial.md new file mode 100644 index 0000000000..a62f6f0f19 --- /dev/null +++ b/user-guide/tracing-tutorial.md @@ -0,0 +1,127 @@ +# Tracing + +Ambassador can support distributed tracing, one of the ["3 pillars of observability"](https://medium.com/@copyconstruct/monitoring-in-the-time-of-cloud-native-c87c7a5bfa3e), which allows developers to visualize request flows in microservice and service-oriented architectures. In this tutorial, we'll configure Ambassador to initiate a trace on some sample requests, and use an external tracing service to visualize them. + +## Before You Get Started + +This tutorial assumes you have already followed the [Ambassador Getting Started](/user-guide/getting-started.html) guide. If you haven't done that already, you should do that now. + +After completing the Getting Started guide you will have a Kubernetes cluster running Ambassador and the Quote of the Moment service. Let's walk through adding tracing to this setup. + +## 1. Deploy Zipkin + +In this tutorial you will use a simple deployment of the open source [Zipkin](https://zipkin.io/) distributed tracing system to store and visualize the Ambassador-generated traces. The trace data will be stored in-memory within the +Zipkin container, and you will be able to explore the traces via the Zipkin +web UI. + +First, add the following YAML to a file named `zipkin.yaml`. This configuration +will create a zipkin Deployment that uses the [`openzipkin/zipkin`](https://hub.docker.com/r/openzipkin/zipkin/) container image +and also an associated Service. You will notice that the Service also has an +annotation on it that configures Ambassador to use the zipkin service (running on the +default port of 9411) to provide tracing support. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: zipkin + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: TracingService + name: tracing + service: zipkin:9411 + driver: zipkin +spec: + selector: + app: zipkin + ports: + - port: 9411 + name: http + targetPort: http + type: NodePort +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: zipkin +spec: + replicas: 1 + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: zipkin + spec: + containers: + - name: zipkin + image: openzipkin/zipkin + imagePullPolicy: Always + ports: + - name: http + containerPort: 9411 +``` + +You can deploy this configuration into your Kubernetes cluster like so: + +```shell +$ kubectl apply -f zipkin.yaml +``` + +The Ambassador Service will detect the annotations and reconfigure itself within a few seconds. + +## 2. Generate some requests + +Use `curl` to generate a few requests to an existing Ambassador mapping. You may need to perform many requests since only a subset of random requests are sampled and instrumented with traces. + +```shell +$ curl $AMBASSADOR_IP/httpbin/ip +``` + +## 3. Test traces + +To test things out, we'll need to access the Zipkin UI. If you're on Kubernetes, get the name of the Zipkin pod: + +```shelll +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-5ffcfc798-c25dc 2/2 Running 0 1d +prometheus-prometheus-0 2/2 Running 0 113d +zipkin-868b97667c-58v4r 1/1 Running 0 2h +``` + +And then use `kubectl port-forward` to access the pod: + +```shell +$ kubectl port-forward zipkin-868b97667c-58v4r 9411 +``` + +Open your web browser to `http://localhost:9411` for the Zipkin UI. + +If you're on `minikube` you can access the `NodePort` directly, and this ports +number can be obtained via the `minikube services list` command. +If you are using `Docker for Mac/Windows`, you can use the +`kubectl get svc` command to get the same information. + +```shell +$ minikube service list +|-------------|----------------------|-----------------------------| +| NAMESPACE | NAME | URL | +|-------------|----------------------|-----------------------------| +| default | ambassador-admin | http://192.168.99.107:30319 | +| default | ambassador | http://192.168.99.107:31893 | +| default | zipkin | http://192.168.99.107:31043 | +|-------------|----------------------|-----------------------------| +``` + +Open your web browser to the Zipkin dashboard http://192.168.99.107:31043/zipkin/. + +In the Zipkin UI, click on the "Find Traces" button to get a listing instrumented traces. Each of the traces that are displayed can be clicked on, which provides further information +about each span and associated metadata. + +## More + +For more details about configuring the external tracing service, read the documentation on [external tracing](/reference/services/tracing-service). diff --git a/user-guide/websockets-ambassador.md b/user-guide/websockets-ambassador.md new file mode 100644 index 0000000000..b85e025b2a --- /dev/null +++ b/user-guide/websockets-ambassador.md @@ -0,0 +1,30 @@ +# WebSockets and Ambassador + + +Ambassador makes it easy to access your services from outside your application, and this includes services that use WebSockets. Only a small amount of additional configuration is required, which is as simple as adding the `use_websocket` attribute with a value of `true` on a `Mapping`. + +## Writing a WebSocket service for Ambassador +The example configuration below demonstrates the addition of the `use_websocket` attribute. + +``` +kind: Service +apiVersion: v1 +metadata: + name: my-service + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: my_service_mapping + prefix: /my-service/ + service: my-service + use_websocket: true +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +``` diff --git a/user-guide/with-istio.md b/user-guide/with-istio.md new file mode 100644 index 0000000000..e64d6ccf20 --- /dev/null +++ b/user-guide/with-istio.md @@ -0,0 +1,432 @@ +# Ambassador and Istio: Edge Proxy and Service Mesh + +Ambassador is a Kubernetes-native API gateway for microservices. Ambassador is deployed at the edge of your network, and routes incoming traffic to your internal services (aka "north-south" traffic). [Istio](https://istio.io/) is a service mesh for microservices, and is designed to add application-level Layer (L7) observability, routing, and resilience to service-to-service traffic (aka "east-west" traffic). Both Istio and Ambassador are built using [Envoy](https://www.envoyproxy.io). + +Ambassador and Istio can be deployed together on Kubernetes. In this configuration, incoming traffic from outside the cluster is first routed through Ambassador, which then routes the traffic to Istio-powered services. Ambassador handles authentication, edge routing, TLS termination, and other traditional edge functions. + +This allows the operator to have the best of both worlds: a high performance, modern edge service (Ambassador) combined with a state-of-the-art service mesh (Istio). Istio's basic [ingress controller](https://istio.io/docs/tasks/traffic-management/ingress.html) is very limited, and has no support for authentication or many of the other features of Ambassador. + +## Getting Ambassador Working With Istio + +Getting Ambassador working with Istio is straightforward. In this example, we'll use the `bookinfo` sample application from Istio. + +1. Install Istio on Kubernetes, following [the default instructions](https://istio.io/docs/setup/kubernetes/quick-start.html) (without using mutual TLS auth between sidecars) +2. Next, install the Bookinfo sample application, following the [instructions](https://istio.io/docs/guides/bookinfo.html). +3. Verify that the sample application is working as expected. + +By default, the Bookinfo application uses the Istio ingress. To use Ambassador, we need to: + +1. Install Ambassador. + +First you will need to deploy the Ambassador ambassador-admin service to your cluster: + +It's simplest to use the YAML files we have online for this (though of course you can download them and use them locally if you prefer!). + +First, you need to check if Kubernetes has RBAC enabled: + +```shell +kubectl cluster-info dump --namespace kube-system | grep authorization-mode +``` +If you see something like `--authorization-mode=Node,RBAC` in the output, then RBAC is enabled. + +If RBAC is enabled, you'll need to use: + + +```shell +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-rbac.yaml +``` + +Without RBAC, you can use: + +```shell +kubectl apply -f https://getambassador.io/yaml/ambassador/ambassador-no-rbac.yaml +``` + +(Note that if you are planning to use mutual TLS for communication between Ambassador and Istio/services in the future, then the order in which you deploy the ambassador-admin service and the ambassador LoadBalancer service below may need to be swapped) + +Next you will deploy an ambassador service that acts as a point of ingress into the cluster via the LoadBalancer type. Create the following YAML and put it in a file called `ambassador-service.yaml`. + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: ambassador + name: ambassador + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin_mapping + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org +spec: + type: LoadBalancer + ports: + - name: ambassador + port: 80 + targetPort: 80 + selector: + service: ambassador +``` + +Then, apply it to the Kubernetes with `kubectl`: + +```shell +kubectl apply -f ambassador-service.yaml +``` + +The YAML above does several things: + +* It creates a Kubernetes service for Ambassador, of type `LoadBalancer`. Note that if you're not deploying in an environment where `LoadBalancer` is a supported type (i.e. MiniKube), you'll need to change this to a different type of service, e.g., `NodePort`. +* It creates a test route that will route traffic from `/httpbin/` to the public `httpbin.org` HTTP Request and Response service (which provides useful endpoint that can be used for diagnostic purposes). In Ambassador, Kubernetes annotations (as shown above) are used for configuration. More commonly, you'll want to configure routes as part of your service deployment process, as shown in [this more advanced example](https://www.datawire.io/faster/canary-workflow/). + +You can see if the two Ambassador services are running correctly (and also obtain the LoadBalancer IP address when this is assigned after a few minutes) by executing the following commands: + +```shell +$ kubectl get services +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +ambassador LoadBalancer 10.63.247.1 35.224.41.XX 80:32171/TCP 11m +ambassador-admin NodePort 10.63.250.17 8877:32107/TCP 12m +details ClusterIP 10.63.241.224 9080/TCP 16m +kubernetes ClusterIP 10.63.240.1 443/TCP 24m +productpage ClusterIP 10.63.248.184 9080/TCP 16m +ratings ClusterIP 10.63.255.72 9080/TCP 16m +reviews ClusterIP 10.63.252.192 9080/TCP 16m + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +ambassador-2680035017-092rk 2/2 Running 0 13m +ambassador-2680035017-9mr97 2/2 Running 0 13m +ambassador-2680035017-thcpr 2/2 Running 0 13m +details-v1-3842766915-3bjwx 2/2 Running 0 17m +productpage-v1-449428215-dwf44 2/2 Running 0 16m +ratings-v1-555398331-80zts 2/2 Running 0 17m +reviews-v1-217127373-s3d91 2/2 Running 0 17m +reviews-v2-2104781143-2nxqf 2/2 Running 0 16m +reviews-v3-3240307257-xl1l6 2/2 Running 0 16m +``` + +Above we see that external IP assigned to our LoadBalancer is 35.224.41.XX (XX is used to mask the actual value), and that all ambassador pods are running (Ambassador relies on Kubernetes to provide high availability, and so there should be two small pods running on each node within the cluster). + +You can test if Ambassador has been installed correctly by using the test route to `httpbin.org` to get the external cluster [Origin IP](https://httpbin.org/ip) from which the request was made: + +```shell +$ curl 35.224.41.XX/httpbin/ip +{ + "origin": "35.192.109.XX" +} +``` + +If you're seeing a similar response, then everything is working great! + +(Bonus: If you want to use a little bit of awk magic to export the LoadBalancer IP to a variable AMBASSADOR_IP, then you can type `export AMBASSADOR_IP=$(kubectl get services ambassador | tail -1 | awk '{ print $4 }')` and use `curl $AMBASSADOR_IP/httpbin/ip` + +2. Now you are going to modify the bookinfo demo `bookinfo.yaml` manifest to include the necessary Ambassador annotations. See below. + +``` +apiVersion: v1 +kind: Service +metadata: + name: productpage + labels: + app: productpage + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: productpage_mapping + prefix: /productpage/ + rewrite: /productpage + service: productpage:9080 +spec: + ports: + - port: 9080 + name: http + selector: + app: productpage +``` + +The annotation above implements an Ambassador mapping from the '/productpage/' URI to the Kubernetes productpage service running on port 9080 ('productpage:9080'). The 'prefix' mapping URI is taken from the context of the root of your Ambassador service that is acting as the ingress point (exposed externally via port 80 because it is a LoadBalancer) e.g. '35.224.41.XX/productpage/'. + +You can now apply this manifest from the root of the Istio GitHub repo on your local file system (taking care to wrap the apply with istioctl kube-inject): + +```shell +kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/kube/bookinfo.yaml) +``` + +3. Optionally, delete the Ingress controller from the `bookinfo.yaml` manifest by typing `kubectl delete ingress gateway`. + +4. Test Ambassador by going to the IP of the Ambassador LoadBalancer you configured above e.g. `35.192.109.XX/productpage/`. You can see the actual IP address again for Ambassador by typing `kubectl get services ambassador`. + +## Automatic Sidecar Injection + +Newer versions of Istio support Kubernetes initializers to [automatically inject the Istio sidecar](https://istio.io/docs/setup/kubernetes/sidecar-injection.html#automatic-sidecar-injection). You don't need to inject the Istio sidecar into Ambassador's pods -- Ambassador's Envoy instance will automatically route to the appropriate service(s). Ambassador's pods are configured to skip sidecar injection, using an annotation as [explained in the documentation](https://istio.io/docs/setup/kubernetes/sidecar-injection.html#policy). + +## Istio Mutual TLS + +In case Istio mutual TLS is enabled on the cluster, the mapping outlined above will not function correctly as the Istio sidecar will intercept the connections and the service will only be reachable via `https` using the Istio managed certificates, which are available in each namespace via the `istio.default` secret. To get the proxy working we need to tell Ambassador to use those certificates when communicating with Istio enabled service. To do this we need to modify the Ambassador deployment installed above. + +In case of RBAC: + +``` yaml +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: ambassador +spec: + replicas: 3 + template: + metadata: + annotations: + sidecar.istio.io/inject: "false" + labels: + service: ambassador + spec: + serviceAccountName: ambassador + containers: + - name: ambassador + image: quay.io/datawire/ambassador:0.33.1 + resources: + limits: + cpu: 1 + memory: 400Mi + requests: + cpu: 200m + memory: 100Mi + env: + - name: AMBASSADOR_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + livenessProbe: + httpGet: + path: /ambassador/v0/check_alive + port: 8877 + initialDelaySeconds: 30 + periodSeconds: 3 + readinessProbe: + httpGet: + path: /ambassador/v0/check_ready + port: 8877 + initialDelaySeconds: 30 + periodSeconds: 3 + volumeMounts: + - mountPath: /etc/istiocerts/ + name: istio-certs + readOnly: true + - name: statsd + image: quay.io/datawire/statsd:0.33.1 + restartPolicy: Always + volumes: + - name: istio-certs + secret: + optional: true + secretName: istio.default +``` + +Specifically note the mounting of the Istio secrets. For non RBAC cluster modify accordingly. Next we need to modify the Ambassador configuration to tell it use the new certificates for Istio enabled services: + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + labels: + service: ambassador + name: ambassador + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: httpbin_mapping + prefix: /httpbin/ + service: httpbin.org:80 + host_rewrite: httpbin.org + --- + apiVersion: ambassador/v0 + kind: Module + name: tls + config: + server: + enabled: True + redirect_cleartext_from: 80 + client: + enabled: False + upstream: + cert_chain_file: /etc/istiocerts/cert-chain.pem + private_key_file: /etc/istiocerts/key.pem +spec: + type: LoadBalancer + ports: + - name: ambassador + port: 80 + targetPort: 80 + selector: + service: ambassador +``` + +This will define an `upstream` that uses the Istio certificates. We can now reuse the `upstream` in all Ambassador mappings to enable communication with Istio pods. + +``` yaml +apiVersion: v1 +kind: Service +metadata: + name: productpage + labels: + app: productpage + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: Mapping + name: productpage_mapping + prefix: /productpage/ + rewrite: /productpage + tls: upstream + service: https://productpage:9080 +spec: + ports: + - port: 9080 + name: http + protocol: TCP + selector: + app: productpage +``` +Note the `tls: upstream`, which lets Ambassador know which certificate to use when communicating with that service. + +In the definition above we also have TLS termination enabled; please see [the TLS termination tutorial](https://www.getambassador.io/user-guide/tls-termination) for more details. + +## Tracing Integration + +Istio provides a tracing mechanism based on Zipkin, which is one of the drivers supported by Ambassador. In order to achieve an end-to-end tracing, it is possible to integrate Ambassador with Istio's Zipkin. +First confirm that Istio's Zipkin is up and running in the `istio-system` Namespace: + +```shell +$ kubectl get service zipkin -n istio-system +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +zipkin ClusterIP 10.102.146.104 9411/TCP 7m +``` + +If Istio's Zipkin is up & running on `istio-system` Namespace, add the `TracingService` annotation pointing to it: +```yaml + annotations: + getambassador.io/config: | + --- + apiVersion: ambassador/v0 + kind: TracingService + name: tracing + service: "zipkin.istio-system:9411" + driver: zipkin + config: {} +``` + +*Note:* We are using the DNS entry `zipkin.istio-system` as well as the port that our service is running, in this case `9411`. +Please see [Distributed Tracing](https://www.getambassador.io/reference/services/tracing-service) for more details on Tracing configuration. + +## Monitoring/Statistics Integration + +Istio also provides a Prometheus service that is an open-source monitoring and alerting system which is supported by Ambassador as well. It is possible to integrate Ambassador into Istio's Prometheus to have all statistics and monitoring in a single place. + +First we need to change our Ambassador Deployment to use the [Prometheus StatsD Exporter](https://github.com/prometheus/statsd_exporter) as its sidecar. Do this by applying the [ambassador-rbac-prometheus.yaml](https://www.getambassador.io/yaml/ambassador/ambassador-rbac-prometheus.yaml): +```sh +$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-rbac-prometheus.yaml +``` + +This YAML is changing the StatsD container definition on our Deployment to use the Prometheus StatsD Exporter as a sidecar: +```yaml + - name: statsd-sink + image: datawire/prom-statsd-exporter:0.6.0 + restartPolicy: Always +``` + +Next, a Service needs to be created pointing to our `Prometheus StatsD Exporter` sidecar: +```yaml +apiVersion: v1 +kind: Service +metadata: + name: ambassador-monitor + labels: + app: ambassador + service: ambassador-monitor +spec: + type: ClusterIP + ports: + - port: 9102 + name: prometheus-metrics + selector: + service: ambassador +``` + +Now we need to add a `scrape` configuration to Istio's Prometheus so that it can pool data from our Ambassador. This is done by applying the new ConfigMap: +```sh +$ kubectl apply -f https://www.getambassador.io/yaml/ambassador/ambassador-istio-configmap.yaml +``` + +This ConfigMap YAML changes the `prometheus` ConfigMap that is on `istio-system` Namespace and adds the following: +```yaml + - job_name: 'ambassador' + static_configs: + - targets: ['ambassador-monitor.default:9102'] + labels: {'application': 'ambassador'} +``` + +*Note:* Assuming ambassador-monitor service is runnning in default namespace. + +*Note:* You can also add the scrape by hand by using kubectl edit or dashboard. + +Afer adding the `scrape`, Istio's Prometheus POD needs to be restarted: +```sh +$ export PROMETHEUS_POD=`kubectl get pods -n istio-system | grep prometheus | awk '{print $1}'` +$ kubectl delete pod $PROMETHEUS_POD -n istio-system +``` + +More details can be found in [Statistics and Monitoring](https://www.getambassador.io/reference/statistics). + + +## Grafana Dashboard + +Istio provides a Grafana dashboad service as well, and it is possible to import an Ambassador Dashboard into it, to monitor the Statistics provided by Prometheus. We're going to use [Alex Gervais'](https://twitter.com/alex_gervais) template available on [Grafana's](https://grafana.com/) website under entry [4689](https://grafana.com/dashboards/4698) as a starting point. + +First let's start the port-forwarding for Istio's Grafana service: +```sh +$ kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 3000:3000 & +``` + +Now, open Grafana tool by acessing: `http://localhost:3000/` + +To install Ambassador Dashboard: + +* Click on Create +* Select Import +* Enter number 4698 + +Now we need to adjust the Dashboard Port to reflect our Ambassador configuration: + +* Open the Imported Dashboard +* Click on Settings in the Top Right corner +* Click on Variables +* Change the port to 80 (according to the ambassador service port) + +Next, adjust the Dashboard Registered Services metric: + +* Open the Imported Dashboard +* Find Registered Services +* Click on the down arrow and select Edit +* Change the Metric to: +```yaml +envoy_cluster_manager_active_clusters{job="ambassador"} +``` + +Now lets save the changes: +* Click on Save Dashboard in the Top Right corner + +## Roadmap + +There are a number of roadmap items that we'd like to tackle in improving Istio integration. This includes supporting Istio routing rules in Ambassador and full propagation of request headers (e.g., Zipkin tracing) between Ambassador and Istio. If you're interested in contributing, we'd welcome the help!