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

feat: add support for Equinix Metal Load Balancer #470

Merged
merged 48 commits into from
Oct 26, 2023
Merged
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0fb7d72
feat: Iniitial Create of EMLB
cprivitere Sep 25, 2023
f178e49
define an Equinix Metal Load Balancer client and initialize it
ctreatma Sep 26, 2023
42faf22
chore: standardize markdown
cprivitere Sep 26, 2023
2fa2f7f
chore: tabs to spaces
cprivitere Sep 26, 2023
e3af08d
chore: formatting remove line
cprivitere Sep 26, 2023
248a36c
feat: parse config for new emlb lbs
cprivitere Sep 26, 2023
1aef0da
exchange Metal API key for LB OAuth tokenbefore making LB API requests
ctreatma Sep 27, 2023
f2bbcf0
add map of known locations
ctreatma Sep 28, 2023
1182dde
scaffold out the LBaaS integration
ctreatma Sep 28, 2023
15c1de1
add more detailed implementation outline in comments
ctreatma Sep 28, 2023
69fd1a9
pass in API key and project ID for EMLB
ctreatma Sep 28, 2023
9d7d97f
fix: MapKeys() not Keys()
cprivitere Sep 28, 2023
e53c43f
feat: support tilt based development
cprivitere Sep 28, 2023
c08e63c
fix: get off experimental builder
cprivitere Sep 28, 2023
1c80d87
refactor: introduce usesBGP flag for loadbalancers
ctreatma Oct 2, 2023
24b62b0
feat: move example dev files to dev/
cprivitere Oct 2, 2023
978e07b
fix: ignore dev directory for rebuilds
cprivitere Oct 3, 2023
322373c
feat: create an Equinix Metal Load Balancer for a k8s service
ctreatma Oct 3, 2023
716c3ef
return actual service status for non-BGP load balancers
ctreatma Oct 3, 2023
abd52f3
set LoadBalancerIP for backwards compatibility
ctreatma Oct 3, 2023
4456f42
fix: various updates and comments
cprivitere Oct 3, 2023
b44bae8
fix: ensure LoadBalancer is implemented
cprivitere Oct 3, 2023
0fe57f0
fix: use front end port
cprivitere Oct 3, 2023
20e73f5
fix: don't call this controller
cprivitere Oct 3, 2023
e62ed27
fix: don't call this controller
cprivitere Oct 3, 2023
9a9185f
chore: refactor infrastructure management code
ctreatma Oct 4, 2023
5a5df10
fix: handle multiple ports for a service
ctreatma Oct 5, 2023
7e80f12
fix: use a consistent load balancer name
ctreatma Oct 6, 2023
f9ede3e
fix: commit svc updates
cprivitere Oct 6, 2023
b5b86cf
fix: if service already has a load balancer ID, don't create a new one
ctreatma Oct 6, 2023
21328f0
feat: add code for deletes
cprivitere Oct 9, 2023
937350f
feat: add interface function so deletes work
cprivitere Oct 9, 2023
3b137ee
refactor: defer implementation of GetLoadBalancer to EMLB
ctreatma Oct 10, 2023
adcfa00
fix: delete pools when load balancer is deleted
ctreatma Oct 10, 2023
a442432
fix: patch service object instead of updating
ctreatma Oct 10, 2023
590d80b
feat: report status correctly for new LB
cprivitere Oct 11, 2023
d4507a3
Wire up UpdateService for EMLB
ctreatma Oct 11, 2023
d35cae5
change ID properties to strings
ctreatma Oct 12, 2023
279d0fa
fix: actually delete old resources when updating a service
ctreatma Oct 13, 2023
540c751
chore: bump go mods and version
cprivitere Oct 13, 2023
d7eb273
fix: support latest k8s code new flags
cprivitere Oct 13, 2023
bac1e6c
fix: Tiltfile support new cpem controller name
cprivitere Oct 13, 2023
7f2cfe6
fix: revert k8s and metallb updates
cprivitere Oct 13, 2023
69c4c2a
fix: remove cloud-sa.json from root
cprivitere Oct 17, 2023
36cf224
chore: refactor manager to replace add/update functions with reconcile
ctreatma Oct 20, 2023
e81f2c1
feat: add control plane load balancing with LBaaS
ctreatma Oct 23, 2023
2a3dc23
chore: address lint failures
ctreatma Oct 24, 2023
8a048dc
docs: update README with EMLB support and config
ctreatma Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 47 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ This section lists each configuration option, and whether it can be set by each
| Kubernetes Service annotation to set EIP metro | | `METAL_ANNOTATION_EIP_METRO` | `annotationEIPMetro` | `"metal.equinix.com/eip-metro"` |
| Kubernetes Service annotation to set EIP facility | | `METAL_ANNOTATION_EIP_FACILITY` | `annotationEIPFacility` | `"metal.equinix.com/eip-facility"` |
| Tag for control plane Elastic IP | | `METAL_EIP_TAG` | `eipTag` | No control plane Elastic IP |
| ID for control plane Equinix Metal Load Balancer | | `METAL_LOAD_BALANCER_ID` | `loadBalancerID` | No control plane Equinix Metal Load Balancer |
Copy link
Member

@displague displague Oct 24, 2023

Choose a reason for hiding this comment

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

I think this option would fit with how we offer arguments in METAL_LOAD_BALANCER, as used by metallb:

  • METAL_LOAD_BALANCER=emlb://sv
  • METAL_LOAD_BALANCER=emlb://?loadbalancerID=123
  • METAL_LOAD_BALANCER=emlb://?existing-only-tagged=cpem

Perhaps we don't need a separate option for ID.

(This is conversational, not merge blocking)

Copy link
Member

Choose a reason for hiding this comment

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

I know there is no support for tags today.

I'm curious how well CPEM will handle the limitation of working with a single LoadBalancer and whether this is more of a development feature or if this is an argument that users will want in their clusters.

Copy link
Member

@displague displague Oct 24, 2023

Choose a reason for hiding this comment

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

Alternatively, if new options are the preferred approach, perhaps with #472, we may want to expose CLI / environment / config arguments in the future for the metallb options only offered through METAL_LOAD_BALANCER query parameters today.

| Kubernetes API server port for Elastic IP | | `METAL_API_SERVER_PORT` | `apiServerPort` | Same as `kube-apiserver` on control plane nodes, same as `0` |
| Filter for cluster nodes on which to enable BGP | | `METAL_BGP_NODE_SELECTOR` | `bgpNodeSelector` | All nodes |
| Use host IP for Control Plane endpoint health checks | | `METAL_EIP_HEALTH_CHECK_USE_HOST_IP` | `eipHealthCheckUseHostIP` | false |
Expand All @@ -253,50 +254,20 @@ The Kubernetes CCM for Equinix Metal deploys as a `Deployment` into your cluster

### Service Load Balancers

~~Equinix Metal does not offer managed load balancers like [AWS ELB](https://aws.amazon.com/elasticloadbalancing/)
or [GCP Load Balancers](https://cloud.google.com/load-balancing/). Instead, if configured to do so,
Equinix Metal CCM will interface with and configure external bare-metal loadbalancers.~~
Equinix CCM supports two approaches to load balancing:

When a load balancer is enabled, the CCM does the following:
1. If configured to do so, Equinix Metal CCM will interface with and configure external bare-metal load balancers
2. If configured to do so, and if the feature is available on your Equinix Metal account, Equinix Metal CCM will interface with and configure external, managed Equinix Metal Load Balancers (EMLB)

When any load balancer is enabled, the CCM does the following:

1. Enable BGP for the project
1. Enable BGP on each node as it comes up
1. Sets ASNs based on configuration or default
1. Unless you are using EMLB, for each `Service` of `type=LoadBalancer`:
1. If you are using bare-metal load balancers, then for each `Service` of `type=LoadBalancer`:
- If you have specified a load balancer IP on `Service.Spec.LoadBalancerIP` (bring your own IP, or BYOIP), do nothing
- If you have not specified a load balancer IP on `Service.Spec.LoadBalancerIP`, get an Equinix Metal Elastic IP and set it on `Service.Spec.LoadBalancerIP`, see below
1. Then, depending on the load balancer implementation:

1. If MetalLB, set up the CRDs necessary for MetalLB to create the load balancer and configure it on the relevant nodes
1. If EMLB

1. Ensure preferred load balancer metro is configured (make it part of the schema?)
1. Create an origin pool per service
1. Pool Name, based on service
1. Node IP
1. Port (the nodeport) - chris's guess
1. Reuse or Create load balancer
1. Check annotation for load balancer name/metro
1. Already exists?
1. Yes
1. Get the load balancer
1. Make sure public port is free
1. If not, do we enable creating new load balancers? $$$$$
1. Specify Origin Pool created above
1. No
1. Create it
1. Listener Port (public port requested in Service.Spec.Port)
1. Specify Origin Pool created above
1. Delete Load Balancer
1. Remove origin pool
1. Remove listener Port
1. If no more listeners, delete load balancer ?
1. Update Load Balancer
1. Get Origin Pool
1. Diff
1. Update if needed

1. If Kube-vip or empty, do nothing
1. Pass control to the specific load balancer implementation

#### Service Load Balancer IP

Expand Down Expand Up @@ -354,12 +325,6 @@ are created at a system-wide level, ignoring the annotations.

Using these flags and annotations, you can run the CCM on a node in a different metro or facility, or even outside of Equinix Metal entirely.

#### Control Plane LoadBalancer Implementation

For the control plane nodes, the Equinix Metal CCM uses static Elastic IP assignment, via the Equinix Metal API, to tell the
Equinix Metal network which control plane node should receive the traffic. For more details on the control plane
load-balancer, see [this section](#control-plane-load-balancing).

#### Service LoadBalancer Implementations
Copy link
Member

@displague displague Oct 24, 2023

Choose a reason for hiding this comment

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

nit: We should link to this section above in the table where the loadbalancer setting is introduced without any options described.

8a048dc#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R224


Loadbalancing is enabled as follows.
Expand All @@ -375,13 +340,32 @@ The value of the loadbalancing configuration is `<type>:///<detail>` where:

For loadbalancing for Kubernetes `Service` of `type=LoadBalancer`, the following implementations are supported:


- [Equinix Metal Load Balancer](#EquinixMetalLoadBalancer)
- [kube-vip](#kube-vip)
- [MetalLB](#metallb)
- [empty](#empty)

CCM does **not** deploy _any_ load balancers for you. It limits itself to managing the Equinix Metal-specific
API calls to support a load balancer, and providing configuration for supported load balancers.

##### Equinix Metal Load Balancer

Equinix Metal Load Balancer (EMLB) is a beta service that is available to a limited number of Equinix Metal customers that provides managed layer 4 load balancers.
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
Equinix Metal Load Balancer (EMLB) is a beta service that is available to a limited number of Equinix Metal customers that provides managed layer 4 load balancers.
[Equinix Metal Load Balancer](https://github.com/equinix/lbaas-api-docs) (EMLB) is a [service under development](https://feedback.equinixmetal.com/network/p/load-balancer-as-a-service-lbaas), currently in [beta](https://deploy.equinix.com/enroll-beta-lab/), that provides managed layer 4 load balancers.


When the EMLB option is enabled, for user-deployed Kubernetes `Service` of `type=LoadBalancer`, the Equinix Metal CCM:
- creates an Equinix Metal Load Balancer for the service
- creates listener ports on the Equinix Metal Load Balancer for each port on the service
- creates origin pools for each listener port that send traffic to the corresponding NodePorts in your cluster

To enable EMLB, set the configuration `METAL_LOAD_BALANCER` or config `loadbalancer` to:

```
emlb://<metro>
```

Where `<metro>` is the Equinix metro in which you want CCM to deploy your external load balancers. For example, to deploy your load balancers in Silicon Valley, you would set the configuration to `emlb://sv`. Note that EMLB is available in a limited number of Equinix metros (as of this writing, `sv`, `da`, and `ny`).

##### kube-vip

**Supported Versions**:
Expand Down Expand Up @@ -602,8 +586,10 @@ load balancer to work, the IP address needs to be all of: Reserved, Assigned, Ma

## Control Plane Load Balancing

CCM implements an optional control plane load balancer using an Equinix Metal Elastic IP (EIP) and the Equinix Metal API's
ability to assign that EIP to different devices.
CCM implements an optional control plane load balancer using one of two approaches:

1. an Equinix Metal Load Balancer
1. an Equinix Metal Elastic IP (EIP) and the Equinix Metal API's ability to assign that EIP to different devices.

You have several options for control plane load-balancing:

Expand All @@ -613,6 +599,19 @@ You have several options for control plane load-balancing:

### CCM Managed

#### Equinix Metal Load Balancer

If you have configured the CCM to use Equinix Metal Load Balancers (EMLB) for service load balancing, you can also choose to use EMLB for control plane load balancing. To enable control plane load balancing with EMLB:

1. Create a Load Balancer using the Equinix Metal API or Web UI
1. When starting the CCM
- set the [configuration](#Configuration) for load balancing with EMLB, e.g. env var `METAL_LOAD_BALANCER=emlb://<metro>`, where `<metro>` is the metro in which you want the CCM to create your load balancers
- set the [configuration](#Configuration) for the control plane EIP tag, e.g. env var `METAL_LOAD_BALANCER_ID=<id>`, where `<id>` is the ID of the Load Balancer you created earlier

When run with the correct configuration, on startup, CCM will automatically update your Load Balancer to send traffic to your control plane nodes.

#### Elastic IP Load Balancer

It is a common procedure to use Elastic IP as Control Plane endpoint in order to
have a static endpoint that you can use from the outside, or when configuring
the advertise address for the kubelet.
Expand All @@ -622,9 +621,9 @@ To enable CCM to manage the control plane EIP:
1. Create an Elastic IP, using the Equinix Metal API, Web UI or CLI
1. Put an arbitrary but unique tag on the EIP
1. When starting the CCM
- set the [configuration][Configuration] for the control plane EIP tag, e.g. env var `METAL_EIP_TAG=<tag>`, where `<tag>` is whatever tag you set on the EIP
- set the [configuration](#Configuration) for the control plane EIP tag, e.g. env var `METAL_EIP_TAG=<tag>`, where `<tag>` is whatever tag you set on the EIP
- (optional) set the port that the EIP should listen on; by default, or when set to `0`, it will use the same port as the `kube-apiserver` on the control plane nodes. This port can also be specified with `METAL_API_SERVER_PORT=<port>.`
- (optional) set the [configuration][Configuration] for using the host IP for control plane endpoint health checks. This is
- (optional) set the [configuration](#Configuration) for using the host IP for control plane endpoint health checks. This is
needed when the EIP is configured as an loopback IP address, such as the case with [CAPP](https://github.com/kubernetes-sigs/cluster-api-provider-packet)

In [CAPP](https://github.com/kubernetes-sigs/cluster-api-provider-packet) we
Expand Down Expand Up @@ -655,7 +654,7 @@ The logic will circle over all the available control planes looking for an
active api server. As soon as it can find one the Elastic IP will be unassigned
and reassigned to the working node.

#### How the Elastic IP Traffic is Routed
##### How the Elastic IP Traffic is Routed

Of course, even if the router sends traffic for your Elastic IP (EIP) to a given control
plane node, that node needs to know to process the traffic. Rather than require you to
Expand Down
Loading