Skip to content

Commit

Permalink
[website] Service sharing concept documentation (#469)
Browse files Browse the repository at this point in the history
* adjust concept weights
* description and instructions for working with services

---------

Signed-off-by: Etai Lev Ran <[email protected]>
  • Loading branch information
elevran authored Apr 1, 2024
1 parent 806e60f commit e0dc317
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 16 deletions.
4 changes: 2 additions & 2 deletions website/content/en/docs/concepts/fabric.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Fabric
description: Defining a ClusterLink fabric
weight: 32
weight: 10
---

The concept of a `Fabric` encapsulates a set of cooperating [peers]({{< ref "peers" >}}/).
Expand Down Expand Up @@ -42,4 +42,4 @@ This command will create the CA files `cert.pem` and `key.pem` in a folder named

Once a `Fabric` has been created and initialized, you can proceed with configuring
[peers]({{< ref "peers" >}}). For a complete, end to end, use case please refer to the
[iperf toturial]({{< ref "iperf" >}}).
[iperf tutorial]({{< ref iperf >}}).
2 changes: 1 addition & 1 deletion website/content/en/docs/concepts/peers.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Peers
description: Defining ClusterLink Peers as part of Fabric
weight: 34
weight: 20
---

A `Peer` represents a location, such as a Kubernetes cluster, participating in a
Expand Down
2 changes: 1 addition & 1 deletion website/content/en/docs/concepts/policies.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Policies
description: Controlling Service access across peers
weight: 38
weight: 40
---

<!--
Expand Down
194 changes: 182 additions & 12 deletions website/content/en/docs/concepts/services.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,196 @@
---
title: Services
description: Sharing Services
weight: 36
weight: 30
---

<!--
Each task should give the user
ClusterLink uses services as the unit of sharing between peers.
One or more peers can expose an (internal) Kubernetes Service to
be consumed by other [peers]({{% ref "peers" %}}) in the [fabric]({{% ref "fabric" %}}).
A service is exposed by creating an `Export` CRD instance referencing it in the
source cluster. Similarly, the exported service can be made accessible to workloads
in a peer by defining an `Import` CRD instance in the destination cluster[^KEP-1645].
Thus, service sharing is an explicit operation. Services are not automatically
shared by peers in the fabric. Note that the exporting cluster must be
[configured as a peer]({{% ref "peers#add-or-remove-peers" %}}) of the importing
cluster.

* The prerequisites for this task, if any (this can be specified at the top of a multi-task page if they're the same for all the page's tasks. "All these tasks assume that you understand....and that you have already....").
* What this task accomplishes.
* Instructions for the task. If it involves editing a file, running a command, or writing code, provide code-formatted example snippets to show the user what to do! If there are multiple steps, provide them as a numbered list.
* If appropriate, links to related concept, tutorial, or example pages.
{{< notice info >}}
Services sharing is done on a per namespace basis and does not require cluster wide privileges.
It is intended to be used by application owners having access to their own namespaces only.
{{< /notice >}}

A service is shared using a logical name. The logical name does not have to match
the actual Kubernetes Service name in the exporting cluster. Exporting a service
does not expose cluster Pods or their IP addresses to the importing clusters.
Any load balancing and scaling decisions are kept local in the exporting cluster.
This reduces the amount, frequency and sensitivity of information shared between
clusters. Similarly, the imported service can have any arbitrary name in the
destination cluster, allowing independent choice of naming.

Orchestration of service sharing is the responsibility of users wishing to
export or import it, and any relevant information (e.g, the exported service
name and namespace) must be communicated out of band. In the future, this could
be done by a centralized management plane.

<!-- TODO: image showing export/import (from >2 clusters?) -->

<!--
TODO centralized management may apply simplification of sharing via policies
(e.g., auto-expose)
can also simplify some via clusterlink cli (e.g., allow-all policy default)
Ask for inputs through the documentation?
Put all of the above in footnote?
-->

## What is it
## Prerequisites

The following assume that you have `kubectl` access to two or more clusters where ClusterLink
has already been [deployed and configured]({{% ref "users#setup" %}}).

### Exporting a service

In order to make a service potentially accessible by other clusters, it must be
explicitly configured for remote access via ClusterLink. Exporting is
accomplished by creating an `Export` CRD instance in the **same** namespace
as the service being exposed. The CRD instance acts as a marker for enabling
remote access to the service via ClusterLink.

{{% expand summary="Export Custom Resource" %}}

```go
type Export struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ExportSpec `json:"spec,omitempty"`
Status ExportStatus `json:"status,omitempty"`
}

type ExportSpec struct {
Host string `json:"host,omitempty"`
Port uint16 `json:"port,omitempty"`
}

type ExportStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
```

{{% /expand %}}

The ExportSpec defines the following fields:

## Detailed instructions
- **Host** (string, optional): the name of the service being exported. The service
must be defined in the same namespace as the Export CRD instance. If empty,
the export shall refer to a Kubernetes Service with the same name as the instance's
`metadata.name`. It is an error to refer to a non-existent service or one that is
not present in the local namespace. The error will be reflected in the CRD's status.
- **Port** (integer, required): the port number being exposed. If you wish to export
a multi-port service[^multiport], you will need to define multiple Exports using
the same `Host` value and a different `Port` each. This is aligned with ClusterLink's
principle of being explicit in sharing and limiting exposure whenever possible.

### Exporting a local Kubernetes Service
Note that exporting a Service does not automatically make is accessible to other
peers, but only enables *potential* access. To complete service sharing, you must
define at least one [access control policy]({{% ref "policies" %}}) that allows
access in the exporting cluster.
In addition, users in consuming clusters must still explicitly configure
[service imports](#importing-a-service) and [policies]({{% ref "policies" %}})
in their respective namespaces.

### Exporting an external service endpoint
### Importing a service

### Importing and binding a Service
Exposing remote services to a peer is accomplished by creating an `Import` CRD
instance to a namespace. The CRD representing the imported service and its
available backends across all peers. In response to an Import CRD, ClusterLink
control plane will create a local Kubernetes Service selecting the ClusterLink
data plane Pods. The use of native Kubernetes constructs, allows ClusterLink
to work with any compliant cluster and CNI, transparently.

The Import instance creates the service endpoint in the same namespace as it is
defined in. The created service will have the Import's `metadata.Name`. This
allows maintaining independent names for services between peers. Alternately,
you may use the same name for the import and related source exports.
You can define multiple Import CRDs for the same set of Exports in different
namespaces. These are independent of each other.

{{% expand summary="Import Custom Resource" %}}

```go
type Import struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec ImportSpec `json:"spec"`
Status ImportStatus `json:"status,omitempty"`
}

type ImportSpec struct {
Port uint16 `json:"port"`
TargetPort uint16 `json:"targetPort,omitempty"`
Sources []ImportSource `json:"sources"`
LBScheme string `json:"lbScheme"`
}

type ImportSource struct {
Peer string `json:"peer"`
ExportName string `json:"exportName"`
ExportNamespace string `json:"exportNamespace"`
}

type ImportStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty"`
}
```

{{% /expand %}}

The ImportSpec defines the following fields:

- **Port** (integer, required): the imported, user facing, port number define
on the created service object.
- **TargetPort** (integer, optional): this is the internal listening port
used by the ClusterLink data plane pods to represent the remote services. Typically the
choice of TargetPort should be left to the ClusterLink control plane, allowing
it to select a random and non-conflicting port, but there may be cases where
you wish to assume responsibility for port selection (e.g., a-priori define
local cluster Kubernetes NetworkPolicy object instances). This may result in
[port conflicts](https://kubernetes.io/docs/concepts/services-networking/service/#avoid-nodeport-collisions)
as is done for NodePort services.
- **Sources** (source array, required): references to remote exports providing backends
for the Import. Each reference names a different export through the combination of
- *Peer* (string, required): name of ClusterLink peer where the export is defined.
- *ExportNamespace* (string, required): name of the namespace on the remote peer where
the export is defined.
- *ExportName* (string, required): name of the remote export.
- **LBScheme** (string, optional): load balancing method to select between different
Sources defined. The default policy is `round-robin`, but you could override it to use
`random` or `static` (fixed) assignment.

<!-- Importing multiport? It is not possible... Could use merge in future?
perhaps, but might requires explicit service name so can merge correctly
or use port set instead of individual port per export/import -->

As with exports, importing a Service does not automatically make it accessible by
workloads, but only enables *potential* access. To complete service sharing,
you must define at least one [access control policy]({{% ref "policies" %}}) that
allows access in the importing cluster. To grant access, a connection must be
evaluated to "allow" by both egress (importing cluster) and ingress (exporting
cluster) policies.

## Related tasks

Once a service is exported and imported by one or more clusters, you should
configure [polices]({{% ref "policies" %}}) governing its access.
For a complete end to end use case, refer to [iperf toturial]({{< ref "iperf" >}}).

[^KEP-1645]: While using similar terminology as the Kubernetes Multicluster Service
enhancement proposal ([MCS KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api)),
the ClusterLink implementation intentionally differs from and is not compliant with the
KEP (e.g., there is no `ClusterSet` and "name sameness" assumption).

[^multiport]: ClusterLink intentionally does not expose all service ports, as
typically only a small subset in a multi-port service is meant to be user
accessible, and other ports are service internal (e.g., ports used for internal
service coordination and replication).

0 comments on commit e0dc317

Please sign in to comment.