Skip to content

Conversation

@jkotalik
Copy link
Contributor

Fixes #200

This is a basic implementation of a Kubernetes ingress and ingress controller.

Relevant subfolders are mostly in src/ReverseProxy.KuberenetesController and src/ReverseProxy.WebApp.

I left most of the OperatorFramework subfolder structure unchanged as we want to reorganize it.

Key follow ups:

@jkotalik jkotalik requested a review from Tratcher March 17, 2021 00:32
@jkotalik
Copy link
Contributor Author

@Tratcher
Copy link
Member

What's the content in /samples/KuberenetesIngress for? Is that apps that you would publish to k8 behind the new ingress?

General structure suggestions:
/src/ReverseProxy.KubernetesController -> /src/ReverseProxy.Kubernetes.Controller
/src/ReverseProxy.KubernetesProtocol -> /src/ReverseProxy.Kubernetes.Protocol
/src/ReverseProxy.WebApp -> Split this up:

  • The LoadFromMessages implementation moves to /src/ReverseProxy.Kubernetes.Protocol
  • Program and Startup move to something like /samples/KuberenetesIngress/ReverseProxy.Kubernetes.Ingress (some day we'll ship a pre-built exe for this, but we'll start with a sample you clone.)

@jkotalik
Copy link
Contributor Author

What's the content in /samples/KuberenetesIngress for? Is that apps that you would publish to k8 behind the new ingress?

Yes

Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to talk to you about the object model mapping in YarpParser. Everything else is code cleanup suggestions. I don't expect to review OperatorFramework in this PR, we can follow up on that seperately.


namespace Yarp.ReverseProxy.Kubernetes.Ingress.Services
{
public class Receiver : BackgroundHostedService
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Receiver looks like standard equipment, people wouldn't be customizing this right? Can it move over to the Protocol package and be included in LoadFromMessages too?

Startup is the main content I expect in the Ingress app, that's where people would do most customization.


try
{
using var client = new HttpClient();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the client inside the outer while loop? Move up to line ~49?

Comment on lines 59 to 63
var request = new HttpRequestMessage();
request.RequestUri = new Uri(_options.ControllerUrl);
request.Method = HttpMethod.Get;
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
using var stream = await response.Content.ReadAsStreamAsync();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var request = new HttpRequestMessage();
request.RequestUri = new Uri(_options.ControllerUrl);
request.Method = HttpMethod.Get;
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
using var stream = await response.Content.ReadAsStreamAsync();
using var stream = await client.GetStreamAsync(_options.ControllerUrl, cancellationToken);

services.Configure<ReceiverOptions>(_configuration.Bind);
services.AddHostedService<Receiver>();
services.AddReverseProxy()
.LoadFromMessages();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.LoadFromMessages();
.LoadFromMessages(receiverOptions => _configuration.Bind(receiverOptions));


namespace Yarp.ReverseProxy.Kubernetes.Protocol
{
public class MessageConfigProvider : IProxyConfigProvider, IUpdateConfig
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Receiver and MessageConfigProvider can be combine, and IUpdateConfig removed.

Comment on lines +18 to +21
_config = new MessageConfig(null, null);
}

public IProxyConfig GetConfig() => _config;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's recommended that GetConfig block if it's called before the first config becomes available. That will prevent the ingress from fully starting and accepting requests until the config is available. Otherwise you may 404 the first requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, what if there is no k8s ingress available? Are you saying that we should still wait on an empty config before accepting requests?

Otherwise I think that makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean if the controller isn't available? At that point it might make sense for the ingress to fail in some way. Throwing from here would be the most blunt solution.

}

var protocol = context.Options.Https ? "https" : "http";
var uri = $"{protocol}://{address.Ip}:{port.Port}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IPv6? it will need square brackets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a follow up I have, but very low priority. K8s doens't use IPv6 unless you opt in and it's extremely uncommon.


var ip = address.Ip;
var cluster = clusters[service.Name];
cluster.ClusterId = ip;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cluster id is the destination ip?

In theory there should be many routes -> few clusters -> many destinations.

This looks more like one route -> one cluster -> one destination?


var protocol = context.Options.Https ? "https" : "http";
var uri = $"{protocol}://{address.Ip}:{port.Port}";
cluster.Destinations[uri] = new Destination()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't recreate destination each time.


foreach (var port in subset.Ports ?? Enumerable.Empty<V1EndpointPort>())
{
if (!MatchesPort(port, service?.Port))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different ports for a route to the same IP need to be in different clusters (all destinations in a cluster are equal).

{
foreach (var address in subset.Addresses ?? Enumerable.Empty<V1EndpointAddress>())
{
if (!clusters.ContainsKey(service?.Name))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name should be cluster:port

@Tratcher
Copy link
Member

Looking pretty good, and only one minor build error left. Let me know when you're ready for a final review.

@jkotalik jkotalik marked this pull request as ready for review March 22, 2021 22:29
@jkotalik
Copy link
Contributor Author

Go ahead.

@jkotalik jkotalik merged commit 293dbce into main Mar 24, 2021
@jkotalik jkotalik deleted the jkotalik/ingressController branch March 24, 2021 17:29
@galvesribeiro
Copy link
Member

@jkotalik awesome work!

Quick question... Is the Operator Framework something that is planned to live as a standalone repo published as a Nuget so people can create operators based on it? Or is it something built purposefuly for Yarp?

I'm building an operator for Orleans right now and would love to use this if that is reusable.

Thanks!

@jkotalik
Copy link
Contributor Author

Quick question... Is the Operator Framework something that is planned to live as a standalone repo published as a Nuget so people can create operators based on it? Or is it something built purposefuly for Yarp?

Long term the goal is to make it standalone in some capacity (separate repo, package, etc). It isn't specific to YARP.

We don't have a timeline on when it would be available on nuget, but we aim to do so.

@galvesribeiro
Copy link
Member

Ok understood. Hopefuly it won't be long! 😄

For now will use it as a submodule.

Thanks!

@galvesribeiro
Copy link
Member

galvesribeiro commented Mar 26, 2021

@jkotalik Just to give you a feedback.

I was able to create an operator for Microsoft Orleans and it worked just fine for what we wanted. Unfortunately, we will not be able to release it until we have the Operator Framework out as a NuGet (at least in preview).

Will keep an eye on it, thanks!

@StevenTCramer
Copy link

StevenTCramer commented Jul 17, 2023

@jkotalik @Tratcher Is there any documentation on using Yarp as a K8s Ingress Controller?

I found the following README.

https://github.com/microsoft/reverse-proxy/blob/main/samples/KubernetesIngress.Sample/README.md

But I found nothing here https://microsoft.github.io/reverse-proxy/index.html

@Tratcher
Copy link
Member

@StevenTCramer it's not in the official docs yet because we've never released a package for it. It will be added to the docs when released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proxy can act as a Kubernetes Ingress Controller

5 participants