Chapter 8. External control plane topology
You can use the external control plane topology to isolate the control plane from the data plane on separate clusters.
8.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.
8.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 installed on the laptop you will use to run these instructions.
istioctl
Procedure
Create an
environment variable that defines the Istio version to install on all the clusters by running the following command:ISTIO_VERSION$ export ISTIO_VERSION=1.24.3Create a
environment variable that defines the name of the cluster by running the following command:REMOTE_CLUSTER_NAME$ export REMOTE_CLUSTER_NAME=cluster1Set up the environment variable that contains the
command context for the control plane cluster by running the following command:oc$ export CTX_CONTROL_PLANE_CLUSTER=<context_name_of_the_control_plane_cluster>Set up the environment variable that contains the
command context for the data plane cluster by running the following command:oc$ export CTX_DATA_PLANE_CLUSTER=<context_name_of_the_data_plane_cluster>Set up the ingress gateway for the control plane:
Create a project called
by running the following command:istio-system$ oc get project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project istio-system --context "${CTX_CONTROL_PLANE_CLUSTER}"Create an
resource on the control plane cluster to manage the ingress gateway by running the following command:Istio$ cat <<EOF | oc --context "${CTX_CONTROL_PLANE_CLUSTER}" apply -f - apiVersion: sailoperator.io/v1 kind: Istio metadata: name: default spec: version: v${ISTIO_VERSION} namespace: istio-system value: global: network: network1 EOFCreate 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.yamlGet 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}'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}')
Install Istio on the data plane cluster:
Create a project called
on the data plane cluster by running the following command:external-istiod$ oc get project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_DATA_PLANE_CLUSTER}"Create an
resource on the data plane cluster by running the following command:Istio$ cat <<EOF | oc --context "${CTX_DATA_PLANE_CLUSTER}" apply -f - apiVersion: sailoperator.io/v1 kind: Istio metadata: name: external-istiod spec: version: v${ISTIO_VERSION} namespace: external-istiod profile: remote values: defaultRevision: external-istiod global: remotePilotAddress: ${EXTERNAL_ISTIOD_ADDR} configCluster: true1 pilot: configMap: true istiodRemote: injectionPath: /inject/cluster/cluster2/net/network1 EOF- 1
- This setting identifies the data plane cluster as the source of the mesh configuration.
Create a project called
on the data plane cluster by running the following command:istio-cni$ oc get project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}" || oc new-project istio-cni --context "${CTX_DATA_PLANE_CLUSTER}"Create an
resource on the data plane cluster by running the following command:IstioCNI$ cat <<EOF | oc --context "${CTX_DATA_PLANE_CLUSTER}" apply -f - apiVersion: sailoperator.io/v1 kind: IstioCNI metadata: name: default spec: version: v${ISTIO_VERSION} namespace: istio-cni EOF
Set up the external Istio control plane on the control plane cluster:
Create a project called
on the control plane cluster by running the following command:external-istiod$ oc get project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}" || oc new-project external-istiod --context "${CTX_CONTROL_PLANE_CLUSTER}"Create a
resource on the control plane cluster by running the following command:ServiceAccount$ oc --context="${CTX_CONTROL_PLANE_CLUSTER}" create serviceaccount istiod-service-account -n external-istiodStore 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>:6443Install 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:
$ istioctl create-remote-secret \ --context="${CTX_DATA_PLANE_CLUSTER}" \ --type=config \ --namespace=external-istiod \ --service-account=istiod-external-istiod \ --create-service-account=false \ --server="${DATA_PLANE_API_SERVER}" | \ oc --context="${CTX_CONTROL_PLANE_CLUSTER}" apply -f -Create an
resource on the control plane cluster by running the following command:Istio$ cat <<EOF | oc --context "${CTX_CONTROL_PLANE_CLUSTER}" apply -f - apiVersion: sailoperator.io/v1 kind: Istio metadata: name: external-istiod spec: version: v${ISTIO_VERSION} namespace: external-istiod profile: empty values: meshConfig: rootNamespace: external-istiod defaultConfig: discoveryAddress: $EXTERNAL_ISTIOD_ADDR:15012 pilot: enabled: true volumes: - name: config-volume configMap: name: istio-external-istiod - name: inject-volume configMap: name: istio-sidecar-injector-external-istiod volumeMounts: - name: config-volume mountPath: /etc/istio/config - name: inject-volume mountPath: /var/lib/istio/inject env: INJECTION_WEBHOOK_CONFIG_NAME: "istio-sidecar-injector-external-istiod-external-istiod" VALIDATION_WEBHOOK_CONFIG_NAME: "istio-validator-external-istiod-external-istiod" EXTERNAL_ISTIOD: "true" LOCAL_CLUSTER_SECRET_WATCHER: "true" CLUSTER_ID: cluster2 SHARED_MESH_CONFIG: istio global: caAddress: $EXTERNAL_ISTIOD_ADDR:15012 configValidation: false meshID: mesh1 multiCluster: clusterName: cluster2 network: network1 EOFCreate
andGatewayresources so that the sidecar proxies on the data plane cluster can access the control plane by running the following command:VirtualService$ oc --context "${CTX_CONTROL_PLANE_CLUSTER}" apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: Gateway metadata: name: external-istiod-gw namespace: external-istiod spec: selector: istio: ingressgateway servers: - port: number: 15012 protocol: tls name: tls-XDS tls: mode: PASSTHROUGH hosts: - "*" - port: number: 15017 protocol: tls name: tls-WEBHOOK tls: mode: PASSTHROUGH hosts: - "*" --- apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: external-istiod-vs namespace: external-istiod spec: hosts: - "*" gateways: - external-istiod-gw tls: - match: - port: 15012 sniHosts: - "*" route: - destination: host: istiod-external-istiod.external-istiod.svc.cluster.local port: number: 15012 - match: - port: 15017 sniHosts: - "*" route: - destination: host: istiod-external-istiod.external-istiod.svc.cluster.local port: number: 443 EOFWait for the
external-istiodresource on the control plane cluster to return the "Ready" status condition by running the following command:Istio$ oc --context "${CTX_CONTROL_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3mWait for the
resource on the data plane cluster to return the "Ready" status condition by running the following command:Istio$ oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istio/external-istiod --timeout=3mWait for the
resource on the data plane cluster to return the "Ready" status condition by running the following command:IstioCNI$ oc --context "${CTX_DATA_PLANE_CLUSTER}" wait --for condition=Ready istiocni/default --timeout=3m
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 sampleLabel 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-istiodDeploy the
application:helloworldCreate the
service by running the following command:helloworld$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/helloworld/helloworld.yaml \ -l service=helloworld -n sampleCreate the
deployment by running the following command:helloworld-v1$ 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
Deploy the
application by running the following command:sleep$ oc --context="${CTX_DATA_PLANE_CLUSTER}" apply \ -f https://raw.githubusercontent.com/istio/istio/${ISTIO_VERSION}/samples/sleep/sleep.yaml -n sampleVerify that the pods on the
namespace have a sidecar injected by running the following command:sample$ oc --context="${CTX_DATA_PLANE_CLUSTER}" get pods -n sampleThe terminal should return
for each pod on the2/2namespace by running the following command:sampleExample output
NAME READY STATUS RESTARTS AGE helloworld-v1-6d65866976-jb6qc 2/2 Running 0 1m sleep-5fcd8fd6c8-mg8n2 2/2 Running 0 1m
Verify that internal traffic can reach the applications on the cluster:
Verify a request can be sent to the
application through thehelloworldapplication by running the following command:sleep$ oc exec --context="${CTX_DATA_PLANE_CLUSTER}" -n sample -c sleep deploy/sleep -- curl -sS helloworld.sample:5000/helloThe terminal should return a response from the
application:helloworldExample output
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
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 -f https://raw.githubusercontent.com/istio-ecosystem/sail-operator/refs/heads/main/chart/samples/ingress-gateway.yaml -n sampleConfirm 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}"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 8m4sExpose the
application through the ingress gateway by running the following command:helloworld$ oc apply -f https://raw.githubusercontent.com/istio/istio/refs/heads/master/samples/helloworld/helloworld-gateway.yaml -n sample --context="${CTX_DATA_PLANE_CLUSTER}"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
Verify that external traffic can reach the applications on the mesh:
Confirm that the
application is accessible through the gateway by running the following command:helloworld$ curl -s "http://${GATEWAY_URL}/hello"The
application should return a response.helloworldExample output
Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc