OpenShift iSCSI on Everpure FlashArray - Quick Start Guide

Red Hat

Audience
Public
Technology Integrations
Linux
Source Type
Documentation

Prerequisites

  • OpenShift cluster: OpenShift 4.x already deployed

  • Nodes: Worker nodes healthy and schedulable

  • FlashArray: Configured and reachable for management + data path

  • Connectivity: IP reachability between worker nodes and FlashArray

  • Access: Admin/cluster‑admin rights on OpenShift and FlashArray

Pre‑Flight Validation Commands

Run these checks before applying any MachineConfigs or deploying Portworx. These commands confirm prerequisites — they do not configure anything.

Cluster and Node Health (from any oc client)
# The below command check overall cluster operators & all operators should show Available=True, Degraded=False

oc get co

# The below command check overall and their status & all participating worker nodes should show Ready

oc get nodes -o wide
Management Connectivity to FlashArray (from a worker or bastion)
# Enter a node through debug mode to check connectivity
oc get nodes
# Start a debug session on a worker node
oc debug node/<worker-node-name>


# enter host binaries
chroot /host


# Basic reachability (if ICMP is permitted)
ping <flasharray-mgmt-ip>


# TCP reachability on management port 443
socat connects and hangs on success; errors indicate unreachable


socat - TCP:<flasharray-mgmt-ip>:443
iSCSI Pre-Flight
oc get nodes
# Start a debug session on a worker node
oc debug node/<worker-node-name>


# enter host binaries
chroot /host


# Confirm iSCSI initiator name exists and is unique per node
cat /etc/iscsi/initiatorname.iscsi


# Confirm iSCSI service is available
systemctl status iscsid


# Confirm multipathd is running
systemctl status multipathd


# Optional: TCP connectivity to FlashArray iSCSI port
# Hangs on success; error indicates routing or firewall issue


socat - TCP:<flasharray-iscsi-ip>:3260
  1. Connect OpenShift Workers to FlashArray (Per Transport):

    Each applicable worker node must have:

    1. Unique iSCSI Initiator IQN: Run the following command on each worker node:
      cat /etc/iscsi/initiatorname.iscsi

      The output must contain a non-empty and unique InitiatorName value for that node. For example:

      InitiatorName=iqn.1994-05.com.redhat:2357875188ed
    2. Device Mapper Multipath running: Multipath support must be enabled on each worker node. Verify the service is activesystemctl status multipathd.
      Note:

      DM-Multipath consolidates multiple iSCSI paths to the same LUN into a single logical device. If multipathd is not yet configured, run the following to enable it before proceeding:

      mpathconf --enable
      modprobe dm_multipath
      systemctl start multipathd

      These are one-time setup steps. Once configured, multipathd should start automatically on subsequent boots.

    3. Optional Verification: Basic connectivity verification can be performed from any worker node once the FlashArray has been configured with host objects and volumes are connected to the host group.
      1. Discover iSCSI targets

        iscsiadm -m discovery -t sendtargets -p <flasharray-iscsi-ip>

        Example output:

        10.x.x.x:3260,2137 iqn.2010-06.com.purestorage:flasharray.64724aac22257212
        10.x.x.x:3260,2137 iqn.2010-06.com.purestorage:flasharray.64724aac22257212
      2. Log in to discovered targets:

        iscsiadm -m node --loginall=automatic

        This logs the initiator in to all discovered targets. After login, SCSI block devices will appear on the worker node corresponding to each mapped volume path.

      3. Verify multipath devices:

        multipath -ll

        Example output:

        mpatha (3624a93708eabcb40cc4241b209e0d22b) dm-0 PURE,FlashArray
        size=500G features='0' hwhandler='1 alua' wp=rw
        `-+- policy='service-time 0' prio=50 status=active
          |- 33:0:0:254 sdb 8:16 active ready running
          |- 34:0:0:254 sdc 8:32 active ready running
  2. Configure Transport Protocol (iSCSI MachineConfig):

    This ensures the iscsid service is enabled and multipathing is configured for iSCSI.

    apiVersion: machineconfiguration.openshift.io/v1
    kind: MachineConfig
    metadata:
      name: 99-worker-pure-storage
      labels:
        machineconfiguration.openshift.io/role: worker
    spec:
      config:
        ignition:
          version: 3.2.0
        storage:
          files:
            # Create /etc/multipath.conf
            - path: /etc/multipath.conf
              mode: 420
              overwrite: true
              contents:
                source: data:,defaults%20%7B%0A%20%20%20%20user_friendly_names%20no%0A%20%20%20%20enable_foreign%20%22%5E%24%22%0A%20%20%20%20polling_interval%2010%0A%20%20%20%20find_multipaths%20yes%0A%7D%0Adevices%20%7B%0A%20%20%20%20device%20%7B%0A%20%20%20%20%20%20%20%20vendor%20%22NVME%22%0A%20%20%20%20%20%20%20%20product%20%22Everpure%20FlashArray%22%0A%20%20%20%20%20%20%20%20path_selector%20%22queue-length%200%22%0A%20%20%20%20%20%20%20%20path_grouping_policy%20group_by_prio%0A%20%20%20%20%20%20%20%20prio%20ana%0A%20%20%20%20%20%20%20%20failback%20immediate%0A%20%20%20%20%20%20%20%20fast_io_fail_tmo%2010%0A%20%20%20%20%20%20%20%20user_friendly_names%20no%0A%20%20%20%20%20%20%20%20no_path_retry%200%0A%20%20%20%20%20%20%20%20features%200%0A%20%20%20%20%20%20%20%20dev_loss_tmo%2060%0A%20%20%20%20%7D%0A%20%20%20%20device%20%7B%0A%20%20%20%20%20%20%20%20vendor%20%22PURE%22%0A%20%20%20%20%20%20%20%20product%20%22FlashArray%22%0A%20%20%20%20%20%20%20%20path_selector%20%22service-time%200%22%0A%20%20%20%20%20%20%20%20hardware_handler%20%221%20alua%22%0A%20%20%20%20%20%20%20%20path_grouping_policy%20group_by_prio%0A%20%20%20%20%20%20%20%20prio%20alua%0A%20%20%20%20%20%20%20%20failback%20immediate%0A%20%20%20%20%20%20%20%20path_checker%20tur%0A%20%20%20%20%20%20%20%20fast_io_fail_tmo%2010%0A%20%20%20%20%20%20%20%20user_friendly_names%20no%0A%20%20%20%20%20%20%20%20no_path_retry%200%0A%20%20%20%20%20%20%20%20features%200%0A%20%20%20%20%20%20%20%20dev_loss_tmo%20600%0A%20%20%20%20%7D%0A%7D%0Ablacklist_exceptions%20%7B%0A%20%20%20%20property%20%22(SCSI_IDENT_%7CID_WWN)%22%0A%7D%0Ablacklist%20%7B%0A%20%20%20%20devnode%20%22%5Epxd%5B0-9%5D*%22%0A%20%20%20%20devnode%20%22%5Epxd*%22%0A%20%20%20%20device%20%7B%0A%20%20%20%20%20%20%20%20vendor%20%22VMware%22%0A%20%20%20%20%20%20%20%20product%20%22Virtual%20disk%22%0A%20%20%20%20%7D%0A%7D
            # Create /etc/udev/rules.d/99-pure-storage.rules
            - path: /etc/udev/rules.d/99-pure-storage.rules
              mode: 420
              overwrite: true
              contents:
                source: data:,ACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22sd*%5B!0-9%5D%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BID_VENDOR%7D%3D%3D%22PURE%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Fscheduler%7D%3D%22none%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22dm-%5B0-9%5D*%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BDM_NAME%7D%3D%3D%223624a937*%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Fscheduler%7D%3D%22none%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22sd*%5B!0-9%5D%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BID_VENDOR%7D%3D%3D%22PURE%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Fadd_random%7D%3D%220%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22dm-%5B0-9%5D*%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BDM_NAME%7D%3D%3D%223624a937*%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Fadd_random%7D%3D%220%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22sd*%5B!0-9%5D%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BID_VENDOR%7D%3D%3D%22PURE%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Frq_affinity%7D%3D%222%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22dm-%5B0-9%5D*%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BDM_NAME%7D%3D%3D%223624a937*%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bqueue%2Frq_affinity%7D%3D%222%22%0AACTION%3D%3D%22add%7Cchange%22%2C%20KERNEL%3D%3D%22sd*%5B!0-9%5D%22%2C%20SUBSYSTEM%3D%3D%22block%22%2C%20ENV%7BID_VENDOR%7D%3D%3D%22PURE%22%2C%20OPTIONS%3D%22nowatch%22%2C%20ATTR%7Bdevice%2Ftimeout%7D%3D%2260%22
    

    Validate Configuration:

    C:\oc>oc get mcp worker
    NAME     CONFIG  UPDATED   UPDATING   DEGRADED   MACHINECOUNT   READYMACHINECOUNT   UPDATEDMACHINECOUNT   DEGRADEDMACHINECOUNT   AGE
    worker   rendered-worker-9bff717bea674e806f07303ab142f402   True      False      False      3              3                   3 
  3. Install the Portworx Operator:
    1. Create a dedicated namespace to host the Portworx Operator and cluster components:
      oc create namespace portworx
      
      
      # Verify
      oc get namespace portworx
      

      Expected Output:

      NAME       STATUS   AGE
      portworx   Active   <duration>
      
    2. Install the Portworx Certified Operator into the cluster-wide openshift-operators namespace via OLM:
      apiVersion: operators.coreos.com/v1alpha1
      kind: Subscription
      metadata:
        name: portworx-operator
        namespace: openshift-operators
      spec:
        channel: stable
        name: portworx-certified
        source: certified-operators
        sourceNamespace: openshift-marketplace
        installPlanApproval: Automatic
      

      Apply the Subscription:

      oc apply -f portworx-operator-subscription.yaml
    3. After a short time, verify that the Operator is up and that the necessary CRDs have been registered:
      Check Operator Pod Status
      # List Portworx operator pods in the cluster-wide operator namespace
      oc get pods -n openshift-operators | grep portworx
      

      You should see something similar to:

      portworx-operator-xxxxxxxxxx-xxxxx   1/1   Running   0   <duration>
      

      If the pod is not Running, inspect the logs:

      oc logs -n openshift-operators deploy/portworx-operator
      

      Resolve any issues before you continue.

      Verify CRDs (StorageCluster and Related Types): Confirm the StorageCluster CRD is present.

      oc get crd | grep storagecluster
      
      purestorageclusters.core.libopenstorage.org   	   2026-03-22T12:58:39Z
      storageclusters.core.libopenstorage.org           2026-03-22T12:58:18Z
      
  4. Integrate Portworx with FlashArray.
    1. Create FlashArray API User: Create a dedicated user with storage admin privileges and generate an API token.
      1. Navigate to Settings > Access > Users, create a user (e.g. px-portworx) and assign the Storage Admin role:

      2. Copy and store the API token:

    2. Create a pure.json file containing:
      • FlashArray management endpoint(s) (IP/FQDN)

      • The API token for the Portworx user

      {
        "FlashArrays": [
          {
            "MgmtEndPoint": "https://<flasharray-mgmt-ip-or-fqdn>",
            "APIToken": "<FLASHARRAY-API-TOKEN>"
          }
        ]
      }
    3. Create px-pure-secret and store the pure.json file in a Kubernetes secret that Portworx can read. This secret must be created in the same namespace where your StorageCluster will live (for example, portworx). From your workstation run the following:
      oc create secret generic px-pure-secret -n portworx --from-file=pure.json=./pure.json
  5. Deploy StorageCluster for Everpure FlashArray.

    The spec can be generated from https://central.portworx.com/specGen/px-csi-specgen:

    kind: StorageCluster
    apiVersion: core.libopenstorage.org/v1
    metadata:
      name: px-csi-fa-ocp-6b75a628-bda0-4db9-a226-48393159aac4
      namespace: portworx
      annotations:
        portworx.io/install-source: "https://install.portworx.com/25.6?oem=px-csi&operator=true&ce=pure&csi=true&stork=false&mon=true&promop=true&kbver=1.34.2&ns=portworx&osft=true&c=px-csi-fa-ocp-6b75a628-bda0-4db9-a226-48393159aac4&r=17001&pureSanType=ISCSI&tel=true"
        portworx.io/is-openshift: "true"
        portworx.io/misc-args: "--oem px-csi"
    spec:
      image: portworx/oci-monitor:25.6.0
      imagePullPolicy: Always
      kvdb:
        internal: true
      cloudStorage:
        kvdbDeviceSpec: size=32
      startPort: 17001
      stork:
        enabled: false
      csi:
        enabled: true
      monitoring:
        telemetry:
          enabled: true
        prometheus:
          enabled: true
          exportMetrics: true
      env:
      - name: PURE_FLASHARRAY_SAN_TYPE
        value: "ISCSI"
    1. Save your chosen spec as a YAML file (for example, px-flasharray-iscsi.yaml), then apply it:
      oc apply -f portworx-csi.yaml
      
    2. Check the StorageCluster status:
      oc get storagecluster -n portworx
    3. Monitor Portworx pods:
      oc get pods -n portworx

      If pods are not progressing to Running:

      • Check Operator logs:

        oc logs -n openshift-operators deploy/portworx-operator
      • Check an individual Portworx pod's logs:

        oc logs -n portworx <portworx-pod-name>
      Important:

      Do not proceed to creating StorageClasses or PVCs until the StorageCluster is healthy and all Portworx pods in portworx namespace are in Running state.

  6. Validate with OpenShift Virtualization:
    1. Create a StorageClass that uses Portworx as the provisioner and identifies FlashArray as the backend.
      For iSCSI, an example might look like:
      apiVersion: storage.k8s.io/v1
      kind: StorageClass
      metadata:
        name: px-pure-iscsi-sc
        annotations:
          storageclass.kubernetes.io/is-default-class: "true"
      provisioner: pxd.portworx.com
      parameters:
        backend: pure_block
      allowVolumeExpansion: true
      
      Verify the configuration:
      oc get storageclass
      
    2. Deploy a VM Using the FlashArray StorageClass:
      1. Create a small test namespace:

        oc create namespace px-test
      2. Create a PVC using the Portworx + FlashArray StorageClass:

        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: px-test-pvc
          namespace: px-test
        spec:
          accessModes:
            - ReadWriteOnce
          storageClassName: px-pure-iscsi-sc
          resources:
            requests:
              storage: 5Gi
        oc apply -f px-test-pvc.yaml
        oc get pvc px-test-pvc -n px-test -w

        Validation:

        C:\oc>oc apply -f px-test-pvc.yaml
        persistentvolumeclaim/px-test-pvc created
        
        C:\oc>oc get pvc px-test-pvc -n px-test -w
        
        NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS       VOLUMEATTRIBUTESCLASS   AGE
        px-test-pvc   Bound    pvc-08a9597a-d1db-49a5-930a-670ac895cc95   5Gi  RWO px-pure-iscsi-sc   <unset>  27m
      3. Create a simple pod that mounts it and runs I/O:

        apiVersion: v1
        kind: Pod
        metadata:
          name: px-test-pod
          namespace: px-test
        spec:
          containers:
            - name: app
              image: registry.access.redhat.com/ubi9/ubi-minimal
              command: ["sleep", "3600"]
              volumeMounts:
                - name: data
                  mountPath: /data
          volumes:
            - name: data
              persistentVolumeClaim:
                claimName: px-test-pvc
        
        
        
        oc apply -f px-test-pod.yaml 
        oc get pod px-test-pod -n px-test

        Validation:

        C:\oc>oc get pod px-test-pod -n px-test
        NAME          READY   STATUS    RESTARTS   AGE
        px-test-pod   1/1     Running   0          14m
        
        C:\oc>oc exec -it px-test-pod -n px-test -- /bin/sh
        sh-5.1# df -h /data
        Filesystem Size  Used Avail Use% Mounted on
        /dev/mapper/3624a93708eabcb40cc4241b20a3ed51a  4.9G  129M  4.7G   3% /data
        
        sh-5.1# ls -ld /data
        drwxr-xr-x. 3 root root 4096 Mar 31 13:55 /data