Structured logging for the Envoy access logs (ie. JSON access logs) was requested in #624 and implemented in #1511. Envoy supports a more advanced and flexible access logging option: an Access Log Service (ALS).
With this activated, Envoy uses gRPC streams to pass rich and strongly typed protobufs with all details to a sink. This sink is free to do whatever it pleases with the access logs.
Use cases valuable to us:
We've implemented this as a proof of concept with the following strategy:
127.0.0.1 (note: this cannot be done via CDS, we've tried -- https://github.com/envoyproxy/envoy/issues/3660)If Contour supported this, fewer hacks would be needed. I think the minimum would be:
I don't think Contour needs to provide anything related to _implementing_ an ALS receiver, though we'd be happy to contribute this somewhere if there is interest.
This may be too complex for the scope of Contour and too niche of a feature, but I thought I'd file an issue regardless following @youngnick's recommendation.
Sounds like this could be implemented like our discussions with the RateLimiting service. Contour configures the endpoint and then the receiving end is responsible for implementing its side.
ping @m2 for backlog prioritisation.
Now that we've added the ExtensionService CRD, it should be much easier to have a way to configure this - we could conceivably have a config file item that asks for Envoy logs to be sent to a given ExtensionService, with failback to stdout if not available.
In my mind, this feature will be much less usable without full support for #2495 however, as Contour has no way to tell you that the logging that you've configured won't work.
FWIW, for our use case, we don't need this to be configurable at the HTTPProxy or route level -- we'd be satisfied with a global ALS config in Contour's YAML config, for example.
But if we can leverage ExtensionService for a more dynamic experience, that's cool too.
Our current implementation works like this:
HttpConnectionManager is updated on the :80 and :443 listeners through an LDS proxy127.0.0.1, since it's a sidecar) -- no EDS or DNS neededNow, for status propagation all the way to the HTTPProxy.status objects, I'm not sure exactly how that would work. For one thing, it's Envoy that talks to the gRPC ALS service, not Contour. Any errors sending logs to it would be visible only from Envoy logs and/or metrics.
Are you thinking that Contour would initiate its own gRPC checks against the ALS service before handing it off to Envoy? Does it do that just once or continuously? Would the fallback to STDOUT also be dynamic?
If Contour really wants to go down that path...
HTTPProxyIMO, it's a lot of moving parts, and likely out of scope for Contour.
I meant more that, if the ALS service is running inside Kubernetes, it will have a Service object, with Endpoints that will come and go based on healthchecks, so the Endpoints (which Contour watches already) will tell us if the configured service is accepting traffic.
That's a simple way that we could get a Ready indicator for any given Service.
If the ALS is a sidecar, that is interesting. Any design that we propose definitely needs to be able to handle that use case as well, thanks.
I meant more that, if the ALS service is running inside Kubernetes, it will have a Service object, with Endpoints that will come and go based on healthchecks, so the Endpoints (which Contour watches already) will tell us if the configured service is accepting traffic.
That's a simple way that we could get a
Readyindicator for any given Service.If the ALS is a sidecar, that is interesting. Any design that we propose definitely needs to be able to handle that use case as well, thanks.
Maybe. We can consistently support ALS in the ExtensionService model, which lets the services scale independently (and arguably gives better resource management and resource visibility). The cost is increased deployment complexity, and potentially decreased reliability.
One way to support side-car services could be with a new CRD that would cause Contour to inject and configure the sidecar. The difficulty there is that Contour today doesn't manage the Envoy deployment at all, so we would need to deal with that. I guess another way would be a CRD that expresses the operators' commitment to have already configured such a sidecar :)
I don't know that ALS is special WRT being deployed as a sidecar (you could make a similar for deploying ext_authz and rate limit proxies as sidecars).
I don't know that ALS is special WRT being deployed as a sidecar (you could make a similar for deploying ext_authz and rate limit proxies as sidecars).
Seems like the sidecar question is a blocker to this work?
Could you sort of cheat and expose the services running in a sidecar with an ExternalName or headless service? (https://kubernetes.io/docs/concepts/services-networking/service/#externalname or https://kubernetes.io/docs/concepts/services-networking/service/#headless-services) That way we wouldn't have to do anything different from the existing ExtensionService model
(well, it might not work to try to expose 127.0.0.1 this way)
Or instead to add another concept in addition to ExtensionService, something like RawServiceAddress, which could possibly be used to configure Envoy to reach out to services outside k8s or inside a pod with loopback addresses
I don't think we need extensionService in play for this. You create your ALS service which might be contour-als (for example) or localhost (if running as a side-car. Envoy then is configured to use that and you log.
I feel like we're over complicating this problem or possibly I'm over simplifying it. =)
Using a sidecar is not a hard requirement.
ALS would work fine behind a Service and Deployment.
The sidecar pattern was our preference only because it scales linearly with the number of Envoy nodes (a fair balancing), and the convenience of not needing endpoint discovery.
I don't think we need extensionService in play for this. You create your ALS service which might be
contour-als(for example) orlocalhost(if running as a side-car. Envoy then is configured to use that and you log.I feel like we're over complicating this problem or possibly I'm over simplifying it. =)
Yeah, if the ask is to just add GRPC access logging support to the HTTP connection manager this sounds right
Using ExtensionService implies some more granular listener access logging, but we don't support that today so maybe a moot point
If the consensus is to add to the existing global access log configuration, the required pieces potentially look like this (spelling it out since I'm new to validate my understanding):
envoy.access_loggers.http_grpc Access Logger that points to the cluster configured in the bootstrap to each HTTP filter chaintransport_api_version to be configurable between xDS v2 and v3, but other than that seems like we can leave all the defaults for nowLmk if this is something that is involved enough to require a design doc instead
ExtensionService could come into play as in the proposed RLS design: https://github.com/projectcontour/contour/pull/3178/files#diff-20429b7228686401b67018e240efb3c4ff9ff1289328f9922da0ba90ae10d806R143
The list of request/response headers to send to ALS is one of the configurations needed. Simple ones like _Content-type_ are not included by default.
^ https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/access_loggers/grpc/v3/als.proto#envoy-v3-api-field-extensions-access-loggers-grpc-v3-httpgrpcaccesslogconfig-additional-request-headers-to-log
The reason I brought up ExtensionService WRT sidecar is that for all these types of problems you need (1) to express where the API endpoint is and (2) to express the policy for using the endpoint. Although ExtensionService was built for expressing remote API endpoints, I think that it's a logical extrapolation of the concept to be able to use it to say that the endpoint is running locally as a sidecar or something more abstract than a Kubernetes service. Have not thought through what the YAML would look like, but IMHO it's a consistent approach. What we should try to avoid (as much as we can) is having different ways to express similar concepts.
I agree that the point of ExtensionService is to express the location and the policy for a cluster we're going to tell Envoy about. That would fit well with "you're connecting to localhost" at a conceptual level, but we probably need some more general guidance about how we do policy for the cluster (this has come up in the discussion about tracing as well).
For the specific config here, we're going to need something similar to the discussions about Tracing in #399, probably.
Nick, are we ready to begin to pick this up within the next couple releases? Regarding granularity, I absolutely agree that we start at global instance level and move down to route if we really need to. Further, to James’ point on watching the envoy deployment, if we go with that approach can we think about adding this logic to the Contour Operator once again. I know we’re stuffing a lot of stuff into the Operator already
Furthermore, can these Access Logs then be streamed to something like Fluent Bit. To be quite transparent here, fluent is very popular solution that has already been well integrated into a few telemetry platforms in downstream DIY k8s on the market; that are capable of being post processed and plotted in time series charts etc
Envoy has its own gRPC logging protocol, that's ALS. Currently, it seems that Fluent Bit doesn't have support for ingesting ALS. So to fully implement this, we may need to implement a basic ALS sink (similar to how we made contour-authserver), or add support for ALS to other projects (like Fluent Bit).
The work of actually adding the config items to enable sending ALS from Envoy is a reasonably straightforward addition to the contour bootstrap command, but being able to verify that the logs are being received requires us to have a sink that supports the ALS gRPC API.
So we can definitely provide the facility to configure this, but we won't be able to validate that it works in CI until we have a sink we can check.
@youngnick @skriss I am interested in working with someone to add ALS support to Contour.
I have been playing around with Envoy ALS and wrote a simple ALS sink.
Wrote a gist on how this can be configured: https://gist.github.com/abhide/805ea927500a73658ef696f04961a7d9
@youngnick As you said, we will have to add the ALS cluster as part of contour bootstrap configuration and this seems pretty easy. But users can specify whether access logs need to be send as part of listener configuration in HTTPConnectionManager. Take a look at https://github.com/abhide/envoy-getting-started/blob/master/k8s/als.yaml#L17
Trying to wrap my head around Contour's ExtensionService and how RLS achieves it.
Let me know what do you think?
This is great work @abhide!
Yes, what I see is that we have a way to tell the Contour bootstrap to configure ALS by looking at a named ExtensionService object. The ExtensionService object then allows us to configure the cluster that will be generated in the boostrap (timeouts and load balancing policy, mainly). In addition, you can point an ExtensionService object to localhost to send traffic to another container running inside the Envoy. Downside to this approach is that the ExtensionService must exist and be parsed by contour bootstrap, which is a fair bit of extra work.
You make a good point that we also need to decide how to enable this. I had originally thought that we would just have a boolean config item for contour serve (in the config file), that would enable sending logs to the configured ALS server, but given that we don't have any way to know if the ALS server is configured in the bootstrap, it's a little complicated. I think we should look to start from "Contour owner mandates everyone use ALS", and see how we go.
Maybe that looks like: We tell the controller the ExtensionService that controls ALS, and that serves both as a boolean that ALS should be enabled, and a check that the service is working before we configure the connection manager to do it for everything? This would be a global config file item for contour serve. Then, if people ask for it, we can add a "disable ALS for this vhost" to HTTPProxy at a later date. @bgagnon, any thoughts on what you'd like?
However, I think that this feature needs a design document, that walks through what to add to contour boostrap and contour serve, lays out a preferred option, and explains some alternatives and why they were not chosen.
@abhide Do you want to make a start and open a WIP PR? I'll help you with polish, so please feel free to submit early and we'll iterate.
Thanks @youngnick. Will starta WIP design doc PR and we can collaborate on this.
Sent out a design document PR.
Assigning this to myself.
I believe this is WIP in the current v1.15 release, thanks to @abhide for taking this on. Tagging it 1.15 to track it
Seems like this will slip to 1.16, ok to move?
Yep, move this one out to 1.16.
Still in flight, moving to 1.17.,