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

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.

Install Istio on a control plane cluster and a separate data plane cluster. This installation approach provides increased security.

Note

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

  1. 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
    Copy to Clipboard Toggle word wrap
  2. Create a REMOTE_CLUSTER_NAME environment variable that defines the name of the cluster by running the following command:

    $ export REMOTE_CLUSTER_NAME=cluster1
    Copy to Clipboard Toggle word wrap
  3. 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>
    Copy to Clipboard Toggle word wrap
  4. 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>
    Copy to Clipboard Toggle word wrap
  5. Set up the ingress gateway for the control plane:

    1. 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}"
      Copy to Clipboard Toggle word wrap
    2. Create an Istio resource on the control plane cluster to manage the ingress gateway by running the following command:

      $ 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
      EOF
      Copy to Clipboard Toggle word wrap
    3. 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
      Copy to Clipboard Toggle word wrap
    4. 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}'
      Copy to Clipboard Toggle word wrap
    5. 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}')
      Copy to Clipboard Toggle word wrap
  6. Install Istio on the data plane cluster:

    1. 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}"
      Copy to Clipboard Toggle word wrap
    2. Create an Istio resource on the data plane cluster by running the following command:

      $ 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: true 
      1
      
          pilot:
            configMap: true
            istiodRemote:
              injectionPath: /inject/cluster/cluster2/net/network1
      EOF
      Copy to Clipboard Toggle word wrap
      1
      This setting identifies the data plane cluster as the source of the mesh configuration.
  7. 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}"
    Copy to Clipboard Toggle word wrap
    1. Create an IstioCNI resource on the data plane cluster by running the following command:

      $ 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
      Copy to Clipboard Toggle word wrap
  8. Set up the external Istio control plane on the control plane cluster:

    1. 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}"
      Copy to Clipboard Toggle word wrap
    2. 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
      Copy to Clipboard Toggle word wrap
    3. 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
      Copy to Clipboard Toggle word wrap
    4. 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:

      $ 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 -
      Copy to Clipboard Toggle word wrap
    5. Create an Istio resource on the control plane cluster by running the following command:

      $ 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
      EOF
      Copy to Clipboard Toggle word wrap
    6. Create Gateway and VirtualService resources so that the sidecar proxies on the data plane cluster can access the control plane by running the following command:

      $ 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
      EOF
      Copy to Clipboard Toggle word wrap
    7. 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
      Copy to Clipboard Toggle word wrap
    8. 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
      Copy to Clipboard Toggle word wrap
    9. 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
      Copy to Clipboard Toggle word wrap

Verification

  1. Deploy sample applications on the data plane cluster:

    1. 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
      Copy to Clipboard Toggle word wrap
    2. 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
      Copy to Clipboard Toggle word wrap
    3. Deploy the helloworld application:

      1. 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
        Copy to Clipboard Toggle word wrap
      2. 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
        Copy to Clipboard Toggle word wrap
    4. 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
      Copy to Clipboard Toggle word wrap
    5. 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
      Copy to Clipboard Toggle word wrap

      The terminal should return 2/2 for each pod on the sample 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
      Copy to Clipboard Toggle word wrap

  2. Verify that internal traffic can reach the applications on the cluster:

    1. Verify a request can be sent to the helloworld application through the sleep 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
      Copy to Clipboard Toggle word wrap

      The terminal should return a response from the helloworld application:

      Example output

      Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
      Copy to Clipboard Toggle word wrap

  3. Install an ingress gateway to expose the sample application to external clients:

    1. 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 sample
      Copy to Clipboard Toggle word wrap
    2. 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}"
      Copy to Clipboard Toggle word wrap

      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
      Copy to Clipboard Toggle word wrap

    3. 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}"
      Copy to Clipboard Toggle word wrap
    4. 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
      Copy to Clipboard Toggle word wrap
  4. Verify that external traffic can reach the applications on the mesh:

    1. Confirm that the helloworld application is accessible through the gateway by running the following command:

      $ curl -s "http://${GATEWAY_URL}/hello"
      Copy to Clipboard Toggle word wrap

      The helloworld application should return a response.

      Example output

      Hello version: v1, instance: helloworld-v1-6d65866976-jb6qc
      Copy to Clipboard Toggle word wrap

Back to top
Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust. Explore our recent updates.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Theme

© 2025 Red Hat