Chapter 7. External control plane topology
Use the external control plane topology to isolate the control plane from the data plane on separate clusters.
7.1. About external control plane topology Copy linkLink copied to clipboard!
The external control plane topology improves security and allows the Service Mesh to be hosted as a service. In this installation configuration one cluster hosts and manages the Istio control plane, and applications are hosted on other clusters.
7.1.1. Installing the control plane and data plane on separate clusters Copy linkLink copied to clipboard!
Install Istio on a control plane cluster and a separate data plane cluster. This installation approach provides increased security.
You can adapt these instructions for a mesh spanning more than one data plane cluster. You can also adapt these instructions for multiple meshes with multiple control planes on the same control plane cluster.
Prerequisites
- You have installed the OpenShift Service Mesh Operator on the control plane cluster and the data plane cluster.
-
You have
istioctl
installed on the laptop you will use to run these instructions.
Procedure
Create an
ISTIO_VERSION
environment variable that defines the Istio version to install on all the clusters by running the following command:export ISTIO_VERSION=1.24.3
$ export ISTIO_VERSION=1.24.3
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create a
REMOTE_CLUSTER_NAME
environment variable that defines the name of the cluster by running the following command:export REMOTE_CLUSTER_NAME=cluster1
$ export REMOTE_CLUSTER_NAME=cluster1
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Set up the environment variable that contains the
oc
command context for the control plane cluster by running the following command:export CTX_CONTROL_PLANE_CLUSTER=<context_name_of_the_control_plane_cluster>
$ export CTX_CONTROL_PLANE_CLUSTER=<context_name_of_the_control_plane_cluster>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Set up the environment variable that contains the
oc
command context for the data plane cluster by running the following command:export CTX_DATA_PLANE_CLUSTER=<context_name_of_the_data_plane_cluster>
$ export CTX_DATA_PLANE_CLUSTER=<context_name_of_the_data_plane_cluster>
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Set up the ingress gateway for the control plane:
Create a project called
istio-system
by running the following command:oc get project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}"
$ oc get project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create an
Istio
resource on the control plane cluster to manage the ingress gateway by running the following command:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the ingress gateway for the control plane by running the following command:
oc --context "${CTX_CONTROL_PLANE_CLUSTER}" apply -f https://raw.githubusercontent.com/istio-ecosystem/sail-operator/main/docs/deployment-models/resources/controlplane-gateway.yaml
$ oc --context "${CTX_CONTROL_PLANE_CLUSTER}" apply -f https://raw.githubusercontent.com/istio-ecosystem/sail-operator/main/docs/deployment-models/resources/controlplane-gateway.yaml
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Get the assigned IP address for the ingress gateway by running the following command:
oc --context "${CTX_CONTROL_PLANE_CLUSTER}" get svc istio-ingressgateway -n istio-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
$ oc --context "${CTX_CONTROL_PLANE_CLUSTER}" get svc istio-ingressgateway -n istio-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Store the IP address of the ingress gateway in an environment variable by running the following command:
export EXTERNAL_ISTIOD_ADDR=$(oc -n istio-system --context="${CTX_CONTROL_PLANE_CLUSTER}" get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ export EXTERNAL_ISTIOD_ADDR=$(oc -n istio-system --context="${CTX_CONTROL_PLANE_CLUSTER}" get svc istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Install Istio on the data plane cluster:
Create a project called
external-istiod
on the data plane cluster by running the following command:oc get project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}"
$ oc get project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create an
Istio
resource on the data plane cluster by running the following command:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - 1
- This setting identifies the data plane cluster as the source of the mesh configuration.
Create a project called
istio-cni
on the data plane cluster by running the following command:oc get project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}"
$ oc get project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create an
IstioCNI
resource on the data plane cluster by running the following command:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Set up the external Istio control plane on the control plane cluster:
Create a project called
external-istiod
on the control plane cluster by running the following command:oc get project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}"
$ oc get project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create a
ServiceAccount
resource on the control plane cluster by running the following command:oc --context="${CTX_CONTROL_PLANE_CLUSTER}" create serviceaccount istiod-service-account -n external-istiod
$ oc --context="${CTX_CONTROL_PLANE_CLUSTER}" create serviceaccount istiod-service-account -n external-istiod
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Store the API server address for the data plane cluster in an environment variable by running the following command:
DATA_PLANE_API_SERVER=https://<hostname_or_IP_address_of_the_API_server_for_the_data_plane_cluster>:6443
$ DATA_PLANE_API_SERVER=https://<hostname_or_IP_address_of_the_API_server_for_the_data_plane_cluster>:6443
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Install a remote secret on the control plane cluster that provides access to the API server on the data plane cluster by running the following command:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create an
Istio
resource on the control plane cluster by running the following command:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create
Gateway
andVirtualService
resources so that the sidecar proxies on the data plane cluster can access the control plane by running the following command:Copy to Clipboard Copied! Toggle word wrap Toggle overflow Wait for the
external-istiod
Istio
resource on the control plane cluster to return the "Ready" status condition by running the following command:oc --context "${CTX_CONTROL_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3m
$ oc --context "${CTX_CONTROL_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3m
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Wait for the
Istio
resource on the data plane cluster to return the "Ready" status condition by running the following command:oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3m
$ oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3m
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Wait for the
IstioCNI
resource on the data plane cluster to return the "Ready" status condition by running the following command:oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istiocni/default --timeout=3m
$ oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istiocni/default --timeout=3m
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Verification
Deploy sample applications on the data plane cluster:
Create a namespace for sample applications on the data plane cluster by running the following command:
oc --context "${CTX_DATA_PLANE_CLUSTER}" get project sample || oc --context="${CTX_DATA_PLANE_CLUSTER}" new-project sample
$ oc --context "${CTX_DATA_PLANE_CLUSTER}" get project sample || oc --context="${CTX_DATA_PLANE_CLUSTER}" new-project sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Label the namespace for the sample applications to support sidecar injection by running the following command:
oc --context="${CTX_DATA_PLANE_CLUSTER}" label namespace sample istio.io/rev=external-istiod
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" label namespace sample istio.io/rev=external-istiod
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Deploy the
helloworld
application:Create the
helloworld
service by running the following command:oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/helloworld/helloworld.yaml \ -l service=helloworld -n sample
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/helloworld/helloworld.yaml \ -l service=helloworld -n sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Create the
helloworld-v1
deployment by running the following command:oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/helloworld/helloworld.yaml \ -l version=v1 -n sample
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/helloworld/helloworld.yaml \ -l version=v1 -n sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Deploy the
sleep
application by running the following command:oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/sleep/sleep.yaml -n sample
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/sleep/sleep.yaml -n sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Verify that the pods on the
sample
namespace have a sidecar injected by running the following command:oc --context="${CTX_DATA_PLANE_CLUSTER}" get pods -n sample
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" get pods -n sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The terminal should return
2/2
for each pod on thesample
namespace by running the following command:Example output
NAME READY STATUS RESTARTS AGE helloworld-v1-6d65866976-jb6qc 2/2 Running 0 1m sleep-5fcd8fd6c8-mg8n2 2/2 Running 0 1m
NAME READY STATUS RESTARTS AGE helloworld-v1-6d65866976-jb6qc 2/2 Running 0 1m sleep-5fcd8fd6c8-mg8n2 2/2 Running 0 1m
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Verify that internal traffic can reach the applications on the cluster:
Verify a request can be sent to the
helloworld
application through thesleep
application by running the following command:oc exec --context="${CTX_DATA_PLANE_CLUSTER}" -n sample -c sleep deploy/sleep -- curl -sS helloworld.sample:5000/hello
$ oc exec --context="${CTX_DATA_PLANE_CLUSTER}" -n sample -c sleep deploy/sleep -- curl -sS helloworld.sample:5000/hello
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The terminal should return a response from the
helloworld
application:Example output
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Install an ingress gateway to expose the sample application to external clients:
Create the ingress gateway by running the following command:
oc --context="${CTX_DATA_PLANE_CLUSTER}" apply
$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply -f https://raw.githubusercontent.com/istio-ecosystem/sail-operator/refs/heads/main/chart/samples/ingress-gateway.yaml -n sample
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Confirm that the ingress gateway is running by running the following command:
oc get pod -l app=istio-ingressgateway -n sample --context="${CTX_DATA_PLANE_CLUSTER}"
$ oc get pod -l app=istio-ingressgateway -n sample --context="${CTX_DATA_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The terminal should return output confirming that the gateway is running:
Example output
NAME READY STATUS RESTARTS AGE istio-ingressgateway-7bcd5c6bbd-kmtl4 1/1 Running 0 8m4s
NAME READY STATUS RESTARTS AGE istio-ingressgateway-7bcd5c6bbd-kmtl4 1/1 Running 0 8m4s
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Expose the
helloworld
application through the ingress gateway by running the following command:oc apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/helloworld/helloworld-gateway.yaml -n sample --context="${CTX_DATA_PLANE_CLUSTER}"
$ oc apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/helloworld/helloworld-gateway.yaml -n sample --context="${CTX_DATA_PLANE_CLUSTER}"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Set the gateway URL environment variable by running the following command:
export INGRESS_HOST=$(oc -n sample --context="${CTX_DATA_PLANE_CLUSTER}" get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'); \ export INGRESS_PORT=$(oc -n sample --context="${CTX_DATA_PLANE_CLUSTER}" get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}'); \ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
$ export INGRESS_HOST=$(oc -n sample --context="${CTX_DATA_PLANE_CLUSTER}" get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}'); \ export INGRESS_PORT=$(oc -n sample --context="${CTX_DATA_PLANE_CLUSTER}" get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].port}'); \ export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Verify that external traffic can reach the applications on the mesh:
Confirm that the
helloworld
application is accessible through the gateway by running the following command:curl -s "http://${GATEWAY_URL}/hello"
$ curl -s "http://${GATEWAY_URL}/hello"
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The
helloworld
application should return a response.Example output
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
Copy to Clipboard Copied! Toggle word wrap Toggle overflow