Deploying Advanced Cluster Management and OpenShift Data Foundation for ARO Disaster Recovery

This article was originally published at https://cloud.redhat.com/experts/aro/acm-odf-aro/

A guide to deploying Advanced Cluster Management (ACM) and OpenShift Data Foundation (ODF) for Azure Red hat OpenShift (ARO) Disaster Recovery

Overview

VolSync is not supported for ARO in ACM: https://access.redhat.com/articles/7006295 so if you run into issues and file a support ticket, you will receive the information that ARO is not supported.

In today’s fast-paced and data-driven world, ensuring the resilience and availability of your applications and data has never been more critical. The unexpected can happen at any moment, and the ability to recover quickly and efficiently is paramount. That’s where OpenShift Advanced Cluster Management (ACM) and OpenShift Data Foundation (ODF) come into play. In this guide, we will explore the deployment of ACM and ODF for disaster recovery (DR) purposes, empowering you to safeguard your applications and data across multiple clusters.

Sample Architecture Sample architecture Download a Visio file of this architecture

Hub Cluster (East US Region):

  • This is the central control and management cluster of your multi-cluster environment.
  • It hosts Red Hat Advanced Cluster Management (ACM), which is a powerful tool for managing and orchestrating multiple OpenShift clusters.
  • Within the Hub Cluster, you have MultiClusterHub, which is a component of ACM that facilitates the management of multiple OpenShift clusters from a single control point.
  • Additionally, you have OpenShift Data Foundation (ODF) Multicluster Orchestrator in the Hub Cluster. ODF provides data storage, management, and services across clusters.
  • The Hub Cluster shares the same Virtual Network (VNET) with the Primary Cluster, but they use different subnets within that VNET.
  • VNET peering is established between the Hub Cluster’s VNET and the Secondary Cluster’s dedicated VNET in the Central US region. This allows communication between the clusters.

Primary Cluster (East US Region):

  • This cluster serves as the primary application deployment cluster.
  • It has the Submariner Add-On, which is a component that enables network connectivity and service discovery between clusters.
  • ODF is also deployed in the Primary Cluster, providing storage and data services to applications running in this cluster.
  • By using Submariner and ODF in the Primary Cluster, you enhance the availability and data management capabilities of your applications.

Secondary Cluster (Central US Region):

  • This cluster functions as a secondary or backup cluster for disaster recovery (DR) purposes.
  • Similar to the Primary Cluster, it has the Submariner Add-On to establish network connectivity.
  • ODF is deployed here as well, ensuring that data can be replicated and managed across clusters.
  • The Secondary Cluster resides in its own dedicated VNET in the Central US region.

In summary, this multi-cluster topology is designed for high availability and disaster recovery. The Hub Cluster with ACM and ODF Multicluster Orchestrator serves as the central control point for managing and orchestrating the Primary and Secondary Clusters. The use of Submariner and ODF in both the Primary and Secondary Clusters ensures that applications can seamlessly failover to the Secondary Cluster in the event of a disaster, while data remains accessible and consistent across all clusters. The VNET peering between clusters enables secure communication and data replication between regions.

Prerequisites

Azure Account

1. Log into the Azure CLI by running the following and then authorizing through your Web Browser

    Bash
    az login

    2. Make sure you have enough Quota (change the location if you’re not using East US)

    Bash
    az vm list-usage --location "East US" -o table

    See Addendum – Adding Quota to ARO account if you have less than 36 Quota left for Total Regional vCPUs.

    3. Register resource providers

    Bash
    az provider register -n Microsoft.RedHatOpenShift --wait
    az provider register -n Microsoft.Compute --wait
    az provider register -n Microsoft.Storage --wait
    az provider register -n Microsoft.Authorization --wait

    Red Hat pull secret

    1. Log into https://cloud.redhat.com

    2. Browse to https://cloud.redhat.com/openshift/install/azure/aro-provisioned

    3. Click the Download pull secret button and remember where you saved it, you’ll reference it later.

      Manage Multiple Logins

      1. In order to manage several clusters, we will add a new Kubeconfig file to manage the logins and change quickly from one context to another
        Bash
        rm -rf /var/tmp/acm-odf-aro-kubeconfig
        touch /var/tmp/acm-odf-aro-kubeconfig
        export KUBECONFIG=/var/tmp/acm-odf-aro-kubeconfig

        Create clusters

        1. Set environment variables

        Bash
        export AZR_PULL_SECRET=~/Downloads/pull-secret.txt 
        export EAST_RESOURCE_LOCATION=eastus 
        export EAST_RESOURCE_GROUP=rg-eastus 
        export CENTRAL_RESOURCE_LOCATION=centralus 
        export CENTRAL_RESOURCE_GROUP=rg-centralus

        2. Create environment variables for hub cluster

        Bash
        export HUB_VIRTUAL_NETWORK=10.0.0.0/20
        export HUB_CLUSTER=hub-cluster
        export HUB_CONTROL_SUBNET=10.0.0.0/24
        export HUB_WORKER_SUBNET=10.0.1.0/24
        export HUB_JUMPHOST_SUBNET=10.0.10.0/24

        3. Set environment variables for primary cluster

        Bash
        export PRIMARY_CLUSTER=primary-cluster 
        export PRIMARY_CONTROL_SUBNET=10.0.2.0/24 
        export PRIMARY_WORKER_SUBNET=10.0.3.0/24 
        export PRIMARY_POD_CIDR=10.128.0.0/18 
        export PRIMARY_SERVICE_CIDR=172.30.0.0/18

        4. Set environment variables for secondary cluster

        Note: Pod and Service CIDRs CANNOT overlap between primary and secondary clusters (because we are using Submariner). So we will use the parameters “–pod-cidr” and “–service-cidr” to avoid using the default ranges. Details about POD and Service CIDRs are available here.

          Bash
          export SECONDARY_CLUSTER=secondary-cluster 
          export SECONDARY_VIRTUAL_NETWORK=192.168.0.0/20 
          export SECONDARY_CONTROL_SUBNET=192.168.0.0/24 
          export SECONDARY_WORKER_SUBNET=192.168.1.0/24 
          export SECONDARY_JUMPHOST_SUBNET=192.168.10.0/24 
          export SECONDARY_POD_CIDR=10.130.0.0/18 
          export SECONDARY_SERVICE_CIDR=172.30.128.0/18

          Deploying the Hub Cluster

          1. Create an Azure resource group\

          Bash
          az group create  \
            --name $EAST_RESOURCE_GROUP  \
            --location $EAST_RESOURCE_LOCATION

          2. Create virtual network

          Bash
          az network vnet create  \
            --address-prefixes $HUB_VIRTUAL_NETWORK  \
            --name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --resource-group $EAST_RESOURCE_GROUP

          3. Create control plane subnet

          Bash
          az network vnet subnet create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --name "$HUB_CLUSTER-aro-control-subnet-$EAST_RESOURCE_LOCATION"  \
            --address-prefixes $HUB_CONTROL_SUBNET 

          4. Create worker subnet

          Bash
          az network vnet subnet create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --name "$HUB_CLUSTER-aro-worker-subnet-$EAST_RESOURCE_LOCATION"  \
            --address-prefixes $HUB_WORKER_SUBNET   

          5. Create the cluster

          This will take between 30 and 45 minutes

          Bash
          az aro create  \
              --resource-group $EAST_RESOURCE_GROUP  \
              --name $HUB_CLUSTER  \
              --vnet "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
              --master-subnet "$HUB_CLUSTER-aro-control-subnet-$EAST_RESOURCE_LOCATION"  \
              --worker-subnet "$HUB_CLUSTER-aro-worker-subnet-$EAST_RESOURCE_LOCATION"  \
              --version 4.12.25  \
              --apiserver-visibility Private  \
              --ingress-visibility Private  \
              --pull-secret @$AZR_PULL_SECRET

          Deploying the Primary cluster

          1. Create control plane subnet

          Bash
          az network vnet subnet create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --name "$PRIMARY_CLUSTER-aro-control-subnet-$EAST_RESOURCE_LOCATION"  \
            --address-prefixes $PRIMARY_CONTROL_SUBNET 

          2. Create worker subnet

          Bash
          az network vnet subnet create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --name "$PRIMARY_CLUSTER-aro-worker-subnet-$EAST_RESOURCE_LOCATION"  \
            --address-prefixes $PRIMARY_WORKER_SUBNET

          3. Create the cluster

          This will take between 30 and 45 minutes

          Bash
          az aro create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --name $PRIMARY_CLUSTER  \
            --vnet "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --master-subnet "$PRIMARY_CLUSTER-aro-control-subnet-$EAST_RESOURCE_LOCATION"  \
            --worker-subnet "$PRIMARY_CLUSTER-aro-worker-subnet-$EAST_RESOURCE_LOCATION"  \
            --version 4.12.25  \
            --apiserver-visibility Private  \
            --ingress-visibility Private  \
            --pull-secret @$AZR_PULL_SECRET  \
            --pod-cidr $PRIMARY_POD_CIDR  \
            --service-cidr $PRIMARY_SERVICE_CIDR

          Connect to Hub and Primary Clusters

          With the cluster in a private network, we can create a jump host in order to connect to it.

          1. Create the jump subnet

          Bash
          az network vnet subnet create  \
            --resource-group $EAST_RESOURCE_GROUP  \
            --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"  \
            --name jump-subnet  \
            --address-prefixes $HUB_JUMPHOST_SUBNET    

          2. Create a jump host

          Bash
          az vm create --name jumphost  \
              --resource-group $EAST_RESOURCE_GROUP  \
              --ssh-key-values $HOME/.ssh/id_rsa.pub  \
              --admin-username aro  \
              --image "RedHat:RHEL:9_1:9.1.2022112113"  \
              --subnet jump-subnet  \
              --public-ip-address jumphost-ip  \
              --public-ip-sku Standard  \
              --vnet-name "$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION"

          3. Save the jump host public IP address

          Run this command in a second terminal

          Bash
          EAST_JUMP_IP=$(az vm list-ip-addresses -g $EAST_RESOURCE_GROUP -n jumphost -o tsv  \
          --query '[].virtualMachine.network.publicIpAddresses[0].ipAddress')
          
          echo $EAST_JUMP_IP

          4. Use sshuttle to create a SSH VPN via the jump host (use a separate terminal session)

          Run this command in a second terminal

          Replace the IP with the IP of the jump box from the previous step

          Bash
          sshuttle --dns -NHr "aro@${EAST_JUMP_IP}" $HUB_VIRTUAL_NETWORK

          5. Get OpenShift API routes

          Bash
          HUB_APISERVER=$(az aro show  \
          --name $HUB_CLUSTER  \
          --resource-group $EAST_RESOURCE_GROUP  \
          -o tsv --query apiserverProfile.url)
          
          PRIMARY_APISERVER=$(az aro show  \
          --name $PRIMARY_CLUSTER  \
          --resource-group $EAST_RESOURCE_GROUP  \
          -o tsv --query apiserverProfile.url)

          6. Get OpenShift credentials

          Bash
          HUB_ADMINPW=$(az aro list-credentials  \
          --name $HUB_CLUSTER  \
          --resource-group $EAST_RESOURCE_GROUP  \
          --query kubeadminPassword  \
          -o tsv)
          
          PRIMARY_ADMINPW=$(az aro list-credentials  \
          --name $PRIMARY_CLUSTER  \
          --resource-group $EAST_RESOURCE_GROUP  \
          --query kubeadminPassword  \
          -o tsv)

          7. Log into Hub and configure context

          Bash
          oc login $HUB_APISERVER --username kubeadmin --password ${HUB_ADMINPW}
          oc config rename-context $(oc config current-context) hub
          oc config use hub

          8. Log into Primary and configure context

          Bash
          oc login $PRIMARY_APISERVER --username kubeadmin --password ${PRIMARY_ADMINPW}
          oc config rename-context $(oc config current-context) primary
          oc config use primary

          9. You can now switch between the hub and primary clusters with oc config

            Deploying the Secondary Cluster

            1. Create an Azure resource group

            Bash
            az group create  \
              --name $CENTRAL_RESOURCE_GROUP  \
              --location $CENTRAL_RESOURCE_LOCATION

            2. Create virtual network

            Bash
            az network vnet create  \
              --address-prefixes $SECONDARY_VIRTUAL_NETWORK  \
              --name "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"  \
              --resource-group $CENTRAL_RESOURCE_GROUP

            3. Create control plane subnet

            Bash
            az network vnet subnet create  \
              --resource-group $CENTRAL_RESOURCE_GROUP  \
              --vnet-name "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"  \
              --name "$SECONDARY_CLUSTER-aro-control-subnet-$CENTRAL_RESOURCE_LOCATION"  \
              --address-prefixes $SECONDARY_CONTROL_SUBNET 

            4. Create worker subnet

            Bash
            az network vnet subnet create  \
              --resource-group $CENTRAL_RESOURCE_GROUP  \
              --vnet-name "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"  \
              --name "$SECONDARY_CLUSTER-aro-worker-subnet-$CENTRAL_RESOURCE_LOCATION"  \
              --address-prefixes $SECONDARY_WORKER_SUBNET   

            5. Create the cluster

            This will take between 30 and 45 minutes

            Bash
            az aro create  \
                --resource-group $CENTRAL_RESOURCE_GROUP  \
                --name $SECONDARY_CLUSTER  \
                --vnet "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"  \
                --master-subnet "$SECONDARY_CLUSTER-aro-control-subnet-$CENTRAL_RESOURCE_LOCATION"  \
                --worker-subnet "$SECONDARY_CLUSTER-aro-worker-subnet-$CENTRAL_RESOURCE_LOCATION"  \
                --version 4.12.25  \
                --apiserver-visibility Private  \
                --ingress-visibility Private  \
                --pull-secret @$AZR_PULL_SECRET \
                --pod-cidr $SECONDARY_POD_CIDR \
                --service-cidr $SECONDARY_SERVICE_CIDR

            VNet Peering

            1. Create a peering between both VNETs (Hub Cluster in EastUS and Secondary Cluster in Central US)

            Bash
            export RG_EASTUS=$EAST_RESOURCE_GROUP
            export RG_CENTRALUS=$CENTRAL_RESOURCE_GROUP
            export VNET_EASTUS=$HUB_CLUSTER-aro-vnet-$EAST_RESOURCE_LOCATION
            export VNET_CENTRALUS=$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION
            
            # Get the id for $VNET_EASTUS.
            echo "Getting the id for $VNET_EASTUS"
            VNET_EASTUS_ID=$(az network vnet show --resource-group $RG_EASTUS --name $VNET_EASTUS --query id --out tsv)
            
            # Get the id for $VNET_CENTRALUS.
            echo "Getting the id for $VNET_CENTRALUS"
            VNET_CENTRALUS_ID=$(az network vnet show --resource-group $RG_CENTRALUS --name $VNET_CENTRALUS --query id --out tsv)
            
            # Peer $VNET_EASTUS to $VNET_CENTRALUS.
            echo "Peering $VNET_EASTUS to $VNET_CENTRALUS"
            az network vnet peering create --name "Link"-$VNET_EASTUS-"To"-$VNET_CENTRALUS  \
              --resource-group $RG_EASTUS  \
              --vnet-name $VNET_EASTUS  \
              --remote-vnet $VNET_CENTRALUS_ID  \
              --allow-vnet-access=True  \
              --allow-forwarded-traffic=True  \
              --allow-gateway-transit=True
            
            # Peer$VNET_CENTRALUS to $VNET_EASTUS.
            echo "Peering $VNET_CENTRALUS to $VNET_EASTUS"
            az network vnet peering create --name "Link"-$VNET_CENTRALUS-"To"-$VNET_EASTUS  \
              --resource-group $RG_CENTRALUS  \
              --vnet-name $VNET_CENTRALUS  \
              --remote-vnet $VNET_EASTUS_ID  \
              --allow-vnet-access  \
              --allow-forwarded-traffic=True  \
              --allow-gateway-transit=True

            Connect to Secondary cluster

            Since this cluster will reside in a different virtual network, we should create another jump host.

            1. Create the jump subnet

            Bash
            az network vnet subnet create  \
              --resource-group $CENTRAL_RESOURCE_GROUP  \
              --vnet-name "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"  \
              --name jump-subnet  \
              --address-prefixes $SECONDARY_JUMPHOST_SUBNET                  

            2. Create a jump host

            Bash
             az vm create --name jumphost  \
                --resource-group $CENTRAL_RESOURCE_GROUP  \
                --ssh-key-values $HOME/.ssh/id_rsa.pub  \
                --admin-username aro  \
                --image "RedHat:RHEL:9_1:9.1.2022112113"  \
                --subnet jump-subnet  \
                --public-ip-address jumphost-ip  \
                --public-ip-sku Standard  \
                --vnet-name "$SECONDARY_CLUSTER-aro-vnet-$CENTRAL_RESOURCE_LOCATION"

            3. Save the jump host public IP address

            Run this command in a second terminal

            Bash
            CENTRAL_JUMP_IP=$(az vm list-ip-addresses -g $CENTRAL_RESOURCE_GROUP -n jumphost -o tsv  \
            --query '[].virtualMachine.network.publicIpAddresses[0].ipAddress')
            
            echo $CENTRAL_JUMP_IP

            4. Use sshuttle to create a SSH VPN via the jump host

            Run this command in a second terminal

            Replace the IP with the IP of the jump box from the previous step

            Bash
            sshuttle --dns -NHr "aro@${CENTRAL_JUMP_IP}" $SECONDARY_VIRTUAL_NETWORK

            5. Get OpenShift API routes

            Bash
            SECONDARY_APISERVER=$(az aro show  \
            --name $SECONDARY_CLUSTER  \
            --resource-group $CENTRAL_RESOURCE_GROUP  \
            -o tsv --query apiserverProfile.url)

            6. Get OpenShift credentials

            Bash
            SECONDARY_ADMINPW=$(az aro list-credentials  \
            --name $SECONDARY_CLUSTER  \
            --resource-group $CENTRAL_RESOURCE_GROUP  \
            --query kubeadminPassword  \
            -o tsv)

            7. Log into Secondary and configure context

            Bash
            oc login $SECONDARY_APISERVER --username kubeadmin --password ${SECONDARY_ADMINPW}
            oc config rename-context $(oc config current-context) secondary
            oc config use secondary

            You can switch to the secondary cluster with oc config

            Setup Hub Cluster

            Ensure you are in the right context

            Bash
            oc config use hub

            Configure ACM

            1. Create ACM namespace

            Bash
            cat << EOF | oc apply -f -
            apiVersion: v1
            kind: Namespace
            metadata:
              name: open-cluster-management
              labels:
                openshift.io/cluster-monitoring: "true"
            EOF

            2. Create ACM Operator Group

            Bash
            cat << EOF | oc apply -f -
            apiVersion: operators.coreos.com/v1
            kind: OperatorGroup
            metadata:
              name: open-cluster-management
              namespace: open-cluster-management
            spec:
              targetNamespaces:
                - open-cluster-management
            EOF

            3. Install ACM version 2.8

            Bash
            cat << EOF | oc apply -f -
            apiVersion: operators.coreos.com/v1alpha1
            kind: Subscription
            metadata:
              name: advanced-cluster-management
              namespace: open-cluster-management
            spec:
              channel: release-2.8
              installPlanApproval: Automatic
              name: advanced-cluster-management
              source: redhat-operators
              sourceNamespace: openshift-marketplace
            EOF

            4. Check if installation succeeded

            Bash
            oc wait --for=jsonpath='{.status.phase}'='Succeeded' csv -n open-cluster-management \
              -l operators.coreos.com/advanced-cluster-management.open-cluster-management=''

            If you get the following error, it means that the installation wasn’t completed yet. Wait 3-5 minutes and run the last command again.

            Bash
            error: no matching resources found

            A successful output should be similar to:

            Bash
            clusterserviceversion.operators.coreos.com/advanced-cluster-management.v2.8.2 condit
            ion met

            5. Install MultiClusterHub instance in the ACM namespace

            Bash
            cat << EOF | oc apply -f -
            apiVersion: operator.open-cluster-management.io/v1
            kind: MultiClusterHub
            metadata:
              namespace: open-cluster-management
              name: multiclusterhub
            spec: {}
            EOF

            6. Check that the MultiClusterHub is installed and running properly

            Bash
            oc wait --for=jsonpath='{.status.phase}'='Running' multiclusterhub multiclusterhub -n open-cluster-management \
              --timeout=600s

            Configure ODF Multicluster Orchestrator

            1. Install the ODF Multicluster Orchestrator version 4.12

            Bash
            cat << EOF | oc apply -f -
            apiVersion: operators.coreos.com/v1alpha1
            kind: Subscription
            metadata:
              labels:
                operators.coreos.com/odf-multicluster-orchestrator.openshift-operators: ""
              name: odf-multicluster-orchestrator
              namespace: openshift-operators
            spec:
              channel: stable-4.12
              installPlanApproval: Automatic
              name: odf-multicluster-orchestrator
              source: redhat-operators
              sourceNamespace: openshift-marketplace
            EOF

            2. Check if installation succeeded

            Bash
            oc wait --for=jsonpath='{.status.phase}'='Succeeded' csv -n openshift-operators \
              -l operators.coreos.com/odf-multicluster-orchestrator.openshift-operators=''

            If you get the following error, it means that the installation wasn’t completed yet. Wait 3-5 minutes and run the last command again.

            Bash
            error: no matching resources found

            A successful output should be similar to:

            Bash
            clusterserviceversion.operators.coreos.com/advanced-cluster-management.v2.8.2 condit
            ion met

            Import Clusters into ACM

            1. Create a Managed Cluster Set

            Make sure you are running sshuttle –dns -NHr “aro@${EAST_JUMP_IP}” $HUB_VIRTUAL_NETWORK in second terminal

            Bash
            oc config use hub
            
            export MANAGED_CLUSTER_SET_NAME=aro-clusters
            
            cat << EOF | oc apply -f -
            apiVersion: cluster.open-cluster-management.io/v1beta2
            kind: ManagedClusterSet
            metadata:
              name: $MANAGED_CLUSTER_SET_NAME
            
            EOF

            2. Retrive token and server from primary cluster

            Bash
            oc config use primary
            
            PRIMARY_API=$(oc whoami --show-server)
            PRIMARY_TOKEN=$(oc whoami -t)

            3. Retrieve token and server from secondary cluster

            Make sure you are running sshuttle –dns -NHr “aro@${CENTRAL_JUMP_IP}” $SECONDARY_VIRTUAL_NETWORK in second terminal

            Bash
            oc config use secondary
            
            SECONDARY_API=$(oc whoami --show-server)
            SECONDARY_TOKEN=$(oc whoami -t)

            Import Primary Cluster

            1. Ensure you are in the right context

            Make sure you are running sshuttle --dns -NHr "aro@${EAST_JUMP_IP}" $HUB_VIRTUAL_NETWORK in second terminal

            Bash
            oc config use hub

            2. Create Managed Cluster

            Bash
            cat << EOF | oc apply -f - 
            apiVersion: cluster.open-cluster-management.io/v1
            kind: ManagedCluster
            metadata:
              name: $PRIMARY_CLUSTER
              labels:
                cluster.open-cluster-management.io/clusterset: $MANAGED_CLUSTER_SET_NAME
                cloud: auto-detect
                vendor: auto-detect
            spec:
              hubAcceptsClient: true
            EOF

            3. Create auto-import-secret.yaml secret

            Bash
            cat << EOF | oc apply -f -
            apiVersion: v1
            kind: Secret
            metadata:
              name: auto-import-secret
              namespace: $PRIMARY_CLUSTER
            stringData:
              autoImportRetry: "2"
              token: "${PRIMARY_TOKEN}"
              server: "${PRIMARY_API}"
            type: Opaque
            EOF

            4. Create addon config for cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: agent.open-cluster-management.io/v1
            kind: KlusterletAddonConfig
            metadata:
              name: $PRIMARY_CLUSTER
              namespace: $PRIMARY_CLUSTER
            spec:
              clusterName: $PRIMARY_CLUSTER
              clusterNamespace: $PRIMARY_CLUSTER
              clusterLabels:
                cloud: auto-detect
                vendor: auto-detect
                cluster.open-cluster-management.io/clusterset: $MANAGED_CLUSTER_SET_NAME
              applicationManager:
                enabled: true
              policyController:
                enabled: true
              searchCollector:
                enabled: true
              certPolicyController:
                enabled: true
              iamPolicyController:
                enabled: true
            EOF

            5. Check if cluster imported

            Bash
            oc get managedclusters

            Import Secondary Cluster

            1. Create Managed Cluster

            Bash
            cat << EOF | oc apply -f - 
            apiVersion: cluster.open-cluster-management.io/v1
            kind: ManagedCluster
            metadata:
              name: $SECONDARY_CLUSTER
              labels:
                cluster.open-cluster-management.io/clusterset: $MANAGED_CLUSTER_SET_NAME
                cloud: auto-detect
                vendor: auto-detect
            spec:
              hubAcceptsClient: true
            EOF

            2. Create auto-import-secret.yaml secret

            Bash
            cat << EOF | oc apply -f -
            apiVersion: v1
            kind: Secret
            metadata:
              name: auto-import-secret
              namespace: $SECONDARY_CLUSTER
            stringData:
              autoImportRetry: "2"
              token: "${SECONDARY_TOKEN}"
              server: "${SECONDARY_API}"
            type: Opaque
            EOF

            3. Create addon config for cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: agent.open-cluster-management.io/v1
            kind: KlusterletAddonConfig
            metadata:
              name: $SECONDARY_CLUSTER
              namespace: $SECONDARY_CLUSTER
            spec:
              clusterName: $SECONDARY_CLUSTER
              clusterNamespace: $SECONDARY_CLUSTER
              clusterLabels:
                cloud: auto-detect
                vendor: auto-detect
                cluster.open-cluster-management.io/clusterset: $MANAGED_CLUSTER_SET_NAME
              applicationManager:
                enabled: true
              policyController:
                enabled: true
              searchCollector:
                enabled: true
              certPolicyController:
                enabled: true
              iamPolicyController:
                enabled: true
            EOF

            4. Check if cluster imported

            Bash
            oc get managedclusters

            Configure Submariner Add-On

            1. Create Broker configuration

            Bash
            cat << EOF | oc apply -f -
            apiVersion: submariner.io/v1alpha1
            kind: Broker
            metadata:
              name: submariner-broker
              namespace: $MANAGED_CLUSTER_SET_NAME-broker
              labels:
                cluster.open-cluster-management.io/backup: submariner
            spec:
              globalnetEnabled: false
            EOF

            2. Deploy Submariner config to Primary cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: submarineraddon.open-cluster-management.io/v1alpha1
            kind: SubmarinerConfig
            metadata:
              name: submariner
              namespace: $PRIMARY_CLUSTER
            spec:
              IPSecNATTPort: 4500
              NATTEnable: true
              cableDriver: libreswan
              loadBalancerEnable: true
              gatewayConfig:
                gateways: 1
            EOF

            3. Deploy Submariner to Primary cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: addon.open-cluster-management.io/v1alpha1
            kind: ManagedClusterAddOn
            metadata:
                 name: submariner
                 namespace: $PRIMARY_CLUSTER
            spec:
                 installNamespace: submariner-operator
            EOF

            4. Deploy Submariner config to Secondary cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: submarineraddon.open-cluster-management.io/v1alpha1
            kind: SubmarinerConfig
            metadata:
              name: submariner
              namespace: $SECONDARY_CLUSTER
            spec:
              IPSecNATTPort: 4500
              NATTEnable: true
              cableDriver: libreswan
              loadBalancerEnable: true
              gatewayConfig:
                gateways: 1
            EOF

            5. Deploy Submariner to Secondary cluster

            Bash
            cat << EOF | oc apply -f -
            apiVersion: addon.open-cluster-management.io/v1alpha1
            kind: ManagedClusterAddOn
            metadata:
                 name: submariner
                 namespace: $SECONDARY_CLUSTER
            spec:
                 installNamespace: submariner-operator
            EOF

            6. Check connection status for primary cluster (wait a few minutes)

            Bash
            oc -n $PRIMARY_CLUSTER get managedclusteraddons submariner -o yaml

            Look for the connection established status. The status indicates the connection is not degraded and healthy.

            Bash
            message: The connection between clusters "primary-cluster" and "secondary-cluster"
              is established
            reason: ConnectionsEstablished
            status: "False"
            type: SubmarinerConnectionDegraded

            7. Check connection status for secondary cluster

            Look for the connection established status. The status indicates the connection is not degraded and healthy.

            Bash
            message: The connection between clusters "primary-cluster" and "secondary-cluster"
              is established
            reason: ConnectionsEstablished
            status: "False"
            type: SubmarinerConnectionDegraded

            Install ODF

            Please note that when you subscribe to the ocs-operator and to odf-operator, you should change the channel from channel: stable-4.11 to channel:stable-4.12 since we are using the version 4.12 in this example.

            Primary Cluster

            1. Switch the context to the primary cluster

            Bash
            oc config use primary

            2. Follow these steps to deploy ODF into the Primary Cluster: https://cloud.redhat.com/experts/aro/odf/

              Secondary Cluster

              1. Switch the context to the secondary cluster

              Bash
              oc config use secondary

              2. Follow these steps to deploy ODF into the Secondary Cluster: https://cloud.redhat.com/experts/aro/odf/

              Finishing the setup of the disaster recovery solution

              Creating Disaster Recovery Policy on Hub cluster

              1. Switch the context to the hub cluster

              Bash
              oc config use hub

              2. Create a DR policy to enable replication between primary and secondary cluster

              Bash
              cat << EOF | oc apply -f -
              apiVersion: ramendr.openshift.io/v1alpha1
              kind: DRPolicy
              metadata:
                name: drpolicy
              spec:
                drClusters:
                  - primary-cluster
                  - secondary-cluster
                schedulingInterval: 5m
              EOF

              3. Wait for DR policy to be validated

              This can take up to 10 minutes

              Bash
              oc get drpolicy drpolicy -o yaml

              You should see

              Bash
              status:
                conditions:
                - lastTransitionTime: "2023-10-06T22:54:48Z"
                  message: drpolicy validated
                  observedGeneration: 2
                  reason: Succeeded
                  status: "True"
                  type: Validated

              4. Two DRClusters are also created

              Bash
              oc get drclusters

              You should see

              Bash
              NAME                AGE
              primary-cluster     16m
              secondary-cluster   16m

              Creating the Namespace, the Custom Resource Definition, and the PlacementRule

              1. First, log into the Hub Cluster and create a namespace for the application:

              Bash
              cat <<EOF | oc apply -f -
              apiVersion: v1
              kind: Namespace
              metadata:
               name: busybox-sample
              EOF

              Now, still logged into the Hub Cluster create a Custom Resource Definition (CRD) for the PlacementRule installed in the busybox-sample namespace. You can do this by applying the CRD YAML file before creating the PlacementRule. Here are the steps:

              2. Install the CRD for PlacementRule

              Bash
              cat <<EOF | oc apply -f -
              apiVersion: apiextensions.k8s.io/v1
              kind: CustomResourceDefinition
              metadata:
                name: placerules.apps.open-cluster-management.io
              spec:
                group: apps.open-cluster-management.io
                names:
                  kind: PlacementRule
                  listKind: PlacementRuleList
                  plural: placerules
                  singular: placerule
                scope: Namespaced
                versions:
                  - name: v1
                    served: true
                    storage: true
                    schema:
                      openAPIV3Schema:
                        type: object
              EOF

              3. Create the PlacementRule

              Bash
              cat <<EOF | oc apply -f -
              apiVersion: apps.open-cluster-management.io/v1
              kind: PlacementRule
              metadata:
                name: busybox-placementrule
                namespace: busybox-sample
              spec:
                clusterSelector:
                  matchLabels:
                    name: primary-cluster
                schedulerName: ramen
              EOF

              Create application and failover

              1. Create an application with ACM

              Bash
              cat << EOF | oc apply -f -
              apiVersion: app.k8s.io/v1beta1
              kind: Application
              metadata:
                name: busybox-sample
                namespace: busybox-sample
              spec:
                componentKinds:
                - group: apps.open-cluster-management.io
                  kind: Subscription
                descriptor: {}
                selector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values: 
                        - busybox-sample
              ---
              apiVersion: apps.open-cluster-management.io/v1
              kind: Channel
              metadata:
                annotations:
                  apps.open-cluster-management.io/reconcile-rate: medium
                name: busybox-sample
                namespace: busybox-sample
              spec:
                type: Git
                pathname: 'https://github.com/RamenDR/ocm-ramen-samples'
              ---
              apiVersion: apps.open-cluster-management.io/v1
              kind: Subscription
              metadata:
                annotations:
                  apps.open-cluster-management.io/git-branch: main
                  apps.open-cluster-management.io/git-path: busybox-odr
                  apps.open-cluster-management.io/reconcile-option: merge
                labels:
                  app: busybox-sample
                name: busybox-sample-subscription-1
                namespace: busybox-sample
              spec:
                channel: busybox-sample/busybox-sample
                placement:
                  placementRef:
                    kind: PlacementRule
                    name: busybox-placementrule
              EOF 

              2. Associate the DR policy to the application

              Bash
              cat <<EOF | oc apply -f -
              apiVersion: ramendr.openshift.io/v1alpha1
              kind: DRPlacementControl
              metadata:
                labels:
                  cluster.open-cluster-management.io/backup: resource
                name: busybox-placementrule-drpc
                namespace: busybox-sample
              spec:
                drPolicyRef:
                  name: drpolicy
                placementRef:
                  kind: PlacementRule
                  name: busybox-placementrule
                  namespace: busybox-sample
                preferredCluster: $PRIMARY_CLUSTER
                pvcSelector:
                  matchLabels:
                    appname: busybox-sample
              EOF

              3. Failover sample application to secondary cluster

              Bash
              cat <<EOF | oc apply -f -
              apiVersion: ramendr.openshift.io/v1alpha1
              kind: DRPlacementControl
              metadata:
                labels:
                  cluster.open-cluster-management.io/backup: resource
                name: busybox-placementrule-drpc
                namespace: busybox-sample
              spec:
                action: Failover
                failoverCluster: $SECONDARY_CLUSTER
                drPolicyRef:
                  name: drpolicy
                placementRef:
                  kind: PlacementRule
                  name: busybox-placementrule
                  namespace: busybox-sample
                pvcSelector:
                  matchLabels:
                    appname: busybox-sample
              EOF

              4. Verify application runs in secondary cluster

              Make sure you are running sshuttle --dns -NHr "aro@${CENTRAL_JUMP_IP}" $SECONDARY_VIRTUAL_NETWORK in second terminal

              Bash
              oc config use secondary
              
              oc get pods -n busybox-sample

              Cleanup

              Once you’re done it’s a good idea to delete the cluster to ensure that you don’t get a surprise bill.

              Delete the clusters and resources

              Bash
              az aro delete -y  \
                --resource-group rg-eastus  \
                --name hub-cluster
              
              az aro delete -y  \
                --resource-group rg-eastus  \
                --name primary-cluster
              
              az group delete --name rg-eastus
              
              az aro delete -y  \
                --resource-group rg-centralus  \ 
                --name secondary-cluster
              
              az group delete --name rg-centralus

              Additional reference resources:

              Leave a Reply

              Your email address will not be published. Required fields are marked *