Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add K8sEndpointGroup #4497

Closed
minwoox opened this issue Oct 21, 2022 · 13 comments · Fixed by #5001
Closed

Add K8sEndpointGroup #4497

minwoox opened this issue Oct 21, 2022 · 13 comments · Fixed by #5001

Comments

@minwoox
Copy link
Member

minwoox commented Oct 21, 2022

So that the client connects to the pods directly instead of through service proxy.

Of course, we can set up "Headless" Services and use DnsEndpointGroup for CSLB.
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#a-aaaa-records

However, if so, users cannot use the service proxy anymore which means that other components that are not using Armeria have no way to balance the load unless they have the well-made equivalent of DnsEndpointGroup.

Related links:
https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#get-read-the-specified-service
https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#list-list-or-watch-objects-of-kind-pod
https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodStatus

@ta7uw
Copy link
Member

ta7uw commented Dec 23, 2022

May I work on this issue?

@minwoox
Copy link
Member Author

minwoox commented Dec 23, 2022

Of course. It's all yours. 😄
Please, let me know if there's anything I can help with.

@ta7uw
Copy link
Member

ta7uw commented Jan 5, 2023

Is it ok that we create a new Gradle module like eureka (The module name is k8s)?

@minwoox
Copy link
Member Author

minwoox commented Jan 5, 2023

k8s sounds awesome. 😆

@ikhoon
Copy link
Contributor

ikhoon commented Jan 5, 2023

If it does not need third-party dependencies, I'm OK to add K8sEndpointGroup to c.l.a.client.endoint.k8s of core module. @ta7uw Do you have any reason to prefer a new module?

@minwoox
Copy link
Member Author

minwoox commented Jan 5, 2023

I personally prefer adding a dedicated module for K8sEndpointGroup to encapsulate it (such as we do for Eureka even if it doesn't have any third-party dependencies), but I'm fine with adding it to the core module if:

  • it doesn't need any third-party dependencies obviously.
  • it needs a small number of classes.

@ta7uw
Copy link
Member

ta7uw commented Jan 5, 2023

I haven't started implementation yet, but it seems that it doesn't need any third-party dependencies and it needs a small number of classes.
I thought we need to create a new module like eureka, but currently I'm sure it is not necessary.
It would be better to create a new module when k8s-related features will be added and there are many classes in the core module in the future.

@adiseshan
Copy link

I think that there are 2 options.

  1. Use plain http calls to kubernetes api. (OR)
  2. Use k8s sdk viz., https://github.com/kubernetes-client/java

I understand from the above communication that, the team is not planning to use any third-party dependencies. Before the decision is made, I would kindly recommend to consider the above mentioned SDK too.

@minwoox
Copy link
Member Author

minwoox commented Feb 3, 2023

Hi @adiseshan!
Thanks for the opinion. 😄
Could you let us know why you recommend using the library, please?

@adiseshan
Copy link

I think we intended to query kubernetes API and retrieve the list of pods etc.,

The mentioned library contains many prebuilt classes to take care of authentication, watching a pod etc.,
Eg., https://github.com/kubernetes-client/java/blob/master/examples/examples-release-15/src/main/java/io/kubernetes/client/examples/WatchExample.java

Though I have not used the below mentioned packages,
io.kubernetes.client.openapi, contains many prebuild models in java. This can also be used instead of armeria maintaining models of kubernetes request/response/objects.

@minwoox
Copy link
Member Author

minwoox commented Feb 3, 2023

Thanks for the explanation.
So you mean if we use the library, we do not have to reimplement the features that are provided by the library.
There are also advantages by not using the third-party library such as reducing the size, etc.
Of course, if it's too much work, we definitely need to use the library.
You might want to participate our conversation about this in Slack:
https://line-armeria.slack.com/archives/C1NGPBUH2/p1675075239795269

@ikhoon
Copy link
Contributor

ikhoon commented Feb 3, 2023

I agree that it is not practical to reinvent the wheel. But https://github.com/kubernetes-client/java depends on OkHttp client which would be weird since we already have our own HTTP client. Additionally, as @minwoox said, most Armeria users do not expect to see those transitive dependencies from Armeria modules.

If the protocol provided by Kubernetes is straightforward and solid what do you think of implementing the specification without relying on the kubernatetes java client?

@adiseshan
Copy link

I agree. Let us please consider developing with lesser dependencies. If there is too much of orchestration, kindly reconsider.

ikhoon added a commit that referenced this issue Jan 29, 2024
Motivation:

Fabric8 is one of the popular Kubernetes client implementations. It has
an abstract layer for HTTP and WebSocket protocols.

If Armeria provides a Kubernetes client to better support the cloud
infrastructure, other useful functions such as #4497 will be able to be
implemented based on it.
 
Modifications:

- Add `kubernetes` module to provide Fabric `StandardHttpClient`.
  - `ArmeriaHttpClientFactory` is automatically activated via Java SPI.
- Both HTTP and WebSocket clients have been implemented in compliance
with Reactive Streams specification.
- WebSocket is working over HTTP/1. WebSocket over HTTP/2 is disabled
for compatibility.
  - Forked test suites from the upstream repo.
- Miscellaneous) 
  - Allow `ProxyConfig` to configure proxy headers.
- Allow `WebSocketClient` to configure `HttpHeaders` and
`RequestOptions` when starting a WebSocket session.
 
Result:

You can use the Fabric Kubernetes client on top of the Armeria client.
ikhoon added a commit that referenced this issue Apr 9, 2024
Motivation:

It is tricky to send requests to a Kubernetes cluster from outside
servers without ingress.
There is no way to send traffic directly to the pod, but we can send
traffic to the port of nodes (NodePort) where the pods are located.

This PR proposes a new EndpointGroup that can send requests with CSLB
using NodeIP and NodePort to pods in Kubernetes. This way is not an
ideal CSLB where servers and clients communicate directly, but it will
be a safer way to send traffic without going through ingress which can
be SPOF.

Modifications:

- Add `KubernetesEndpointGroup` on top of `KubernetesClient` to
dynamically obtain Kubernetes resources.
-
[Permission](https://kubernetes.io/docs/reference/access-authn-authz/rbac)
to watch `services`, `nodes`, `pods` is required to fetch endpoints.
- `service.ports[*].nodePort` is used to create the port of `Endpoint`.
- [Watch
API](https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes)
is used to track changes in Kubernetes with a minimal delay.
  - `ADDED` and `MODIFIED` events are used to update resouces.
  - `DELETED` is used to remove the resouce.
  - `BOOKMARK` event is not used and `ERROR` may be ignorable.
- Test `KubernetesEndpointGroup` with both a real Kubernetes cluster and
a mock Kubernetes server.

Result:

- You can use `KubernetesEndpointGroup` to perform client-side
load-balancing when sending requests.
- Fixes #4497 
```java
// Create a KubernetesEndpointGroup that fetches the endpoints of the 'my-service' service in the 'default'
// namespace. The Kubernetes client will be created with the default configuration in the $HOME/.kube/config.
KubernetesClient kubernetesClient = new KubernetesClientBuilder().build();
KubernetesEndpointGroup
  .builder(kubernetesClient)
  .namespace("default")
  .serviceName("my-service")
  .build();

// If you want to use a custom configuration, you can create a KubernetesEndpointGroup as follows:
// The custom configuration would be useful when you want to access Kubernetes from outside the cluster.
Config config =
  new ConfigBuilder()
    .withMasterUrl("https://my-k8s-master")
    .withOauthToken("my-token")
    .build();
KubernetesEndpointGroup
  .builder(config)
  .namespace("my-namespace")
  .serviceName("my-service")
  .build();

```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants