External services

Use the Gloo Mesh API to create an entry within a service mesh for a service that is external to the mesh.

Before you begin

To illustrate these concepts, we will assume that:

Be sure to review the assumptions and satisfy the pre-requisites from the Guides top-level document.

Declaring External Services

Motivation

Sometimes within a mesh, an application needs the ability to communicate with services which are “external” to that mesh. For instance a public API, or other external API providers. Istio exposes this functionality via their ServiceEntry API. Gloo Mesh exposes the same functionality, in a way that feels natural within the rest of the Gloo Mesh API ecosystem.

Destination CRD

The Destination CRD can be thought of as a host. In other words an addressable host which may be backed by any number of endpoints. Typically these destinations are discovered by Gloo Mesh and are backed by Kubernetes workloads. However, an external service represents a destination external to the service mesh, and perhaps the Kubernetes cluster. The Destination CRD provides a way to represent these external services manually.

The External Destination is not a seperate CRD in itself, but rather a way to configure the Destination CRD to accomplish our goal of routing to services which exist outside of our mesh.

Demo

Enough explaining, let's get to a quick example.

To start let's create our external service. Simply apply the following yaml to the cluster:

apiVersion: discovery.mesh.gloo.solo.io/v1
kind: Destination
metadata:
  name: my-ext-service-tt
spec:
  externalService:
    endpoints:
    - address: solo.io
      ports:
        http: 80
    hosts:
    - foo.bar.global
    name: my-external-service
    ports:
    - name: http
      number: 80
      protocol: HTTP

This is a very simple example, but it should help us understand the value, as well as how to use External Destinations. The core of the External Destination exists in the spec block:

  externalService:
    endpoints:
    - address: solo.io
      ports:
        http: 80
    hosts:
    - foo.bar.global
    name: my-external-service
    ports:
    - name: http
      number: 80
      protocol: HTTP

For those who have used Istio's ServiceEntry API this should look very familiar.

For reference, let's take a look at what ServiceEntry Gloo Mesh generates from this resource.

kubectl get se -n istio-system my-external-service -oyaml
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  annotations:
    parents.networking.mesh.gloo.solo.io: '{"discovery.mesh.gloo.solo.io/v1, Kind=Destination":[{"name":"my-ext-service-tt","namespace":"default"}]}'
  labels:
    cluster.multicluster.solo.io: ""
    owner.networking.mesh.gloo.solo.io: gloo-mesh
    relay-agent: mgmt.cluster
  name: my-external-service
  namespace: istio-system
spec:
  addresses:
  - 248.6.56.91
  endpoints:
  - address: solo.io
    ports:
      http: 80
  hosts:
  - foo.bar.global
  ports:
  - name: http
    number: 80
    protocol: HTTP
  resolution: DNS

As you can see we have translated this directly to the underlying ServiceEntry primitive, but now we can also add policies to this destination using other Gloo Mesh primitives such as the TrafficPolicy and/or AccessPolicy.

Scoping

By default, an external service is exported to all namespaces in all clusters managed by Gloo Mesh. If you have a large number of external services, you may not want to export every service to all namespaces and all clusters. Instead, you can use the exportTo field in the external service API to narrow down which clusters and namespaces the serviceEntry is exported to. See the example below:

apiVersion: discovery.mesh.gloo.solo.io/v1
kind: Destination
metadata:
  name: my-ext-service-tt
spec:
  externalService:
    endpoints:
    - address: solo.io
      ports:
        http: 80
    hosts:
    - foo.bar.global
    name: my-external-service
    ports:
    - name: http
      number: 80
      protocol: HTTP
    exportTo:
    # Export to bookinfo and default on both cluster1 and cluster2
    - clusters:
      - cluster1
      - cluster2
      namespaces:
      - bookinfo
      - default
    # Export to just default namespace in cluster3
    - clusters:
      - cluster3
      namespaces:
      - default
    # Export to all namespaces in cluster4
    - clusters:
      - cluster4
    # Export to the apps namespace in all clusters
    - namespaces:
      - apps

In this example, the external service is visible in only the bookinfo and default namespaces of cluster1 and cluster2. It is also visible in the default namespace of cluster3. Note: If exportTo is omitted, the external service is exported to all clusters and namespaces. If you specify clusters but omit namespaces within the list defined by the exportTo field, the external service is exported to all namespaces in the given clusters. If you specify namespaces but omit clusters, the external service is exported to the given namespaces in all clusters.

Testing

Just to make sure it's working let's quickly curl this host.

kubectl label namespace default istio-injection=enabled
kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm

Once the previous command finishes initializting, it will leave you with a shell running in an injected Istio pod. This pod will also have curl so we can test our service.

Run the following curl from inside of our injected shell:

curl foo.bar.global

The output should show:

<!DOCTYPE html>
<html>
...
</body>
</html>

If everything worked, the output should be the solo.io homepage! I cut out most of it above for brevity.