Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

Troubleshooting application performance with Red Hat OpenShift metrics, Part 2: The test environment

July 15, 2021
Pavel Macik
Related topics:
KubernetesOperators
Related products:
Developer SandboxRed Hat OpenShift

    This series shows how I solved a real-life performance problem by gathering metrics from Red Hat OpenShift in the Developer Sandbox for Red Hat OpenShift. Part 1 laid out the development environment and requirements. Now, we will set up the test environment and I will introduce two different test scenarios.

    Read the whole series:

    • Part 1: Performance requirements
    • Part 2: The test environment
    • Part 3: Collecting runtime metrics
    • Part 4: Gathering performance metrics
    • Part 5: Test rounds and results

    Provisioning the OpenShift cluster

    The Developer Sandbox for Red Hat OpenShift provides a temporary cloud platform, which is useful for testing an application before deploying it. As I explained in Part 1, the application under test is the Service Binding Operator. I ran the projection using Red Hat OpenShift Container Platform, but you could use any OpenShift cluster to generate metrics. I used the openshift-install tool to set up a cluster in Amazon Web Services (AWS) for a sandbox that meets the prerequisites of an operator in production. The provisions for the cluster were:

    • Three coordinator nodes of m5.4xlarge size (16 vCPU, 64 GiB memory)
    • Three worker nodes of m5.2xlarge size (8 vCPU, 32 GiB memory)

    Installing the Developer Sandbox

    My installation uses the Developer Sandbox setup tool, which I introduced in Part 1. The installation steps are as follows:

    1. Clone the repository containing the CodeReady Toolchain E2E tests:

      git clone git@github.com:codeready-toolchain/toolchain-e2e.git
    2. Use a Makefile to install the Developer Sandbox Operators:

      make dev-deploy-e2e

    Installing the Service Binding Operator

    Use the following installation script to install the Service Binding Operator and Red Hat OpenShift Application Services Operator in OpenShift Container Platform:

    export SBO_INDEX_IMAGE=${SBO_INDEX_IMAGE:-quay.io/redhat-developer/servicebinding-operator:index}
    export SBO_CHANNEL=${SBO_CHANNEL:-beta}
    export SBO_PACKAGE=${SBO_PACKAGE:-service-binding-operator}
    export SBO_CATSRC_NAMESPACE=${SBO_CATSRC_NAMESPACE:-openshift-marketplace}
    export SBO_CATSRC_NAME=${SBO_CATSRC_NAME:-sbo-operators}
    
    export RHOAS_INDEX_IMAGE=${RHOAS_INDEX_IMAGE:-quay.io/rhoas/service-operator-registry:autolatest}
    export RHOAS_CHANNEL=${RHOAS_CHANNEL:-beta}
    export RHOAS_PACKAGE=${RHOAS_PACKAGE:-rhoas-operator}
    export RHOAS_CATSRC_NAMESPACE=${RHOAS_CATSRC_NAMESPACE:-openshift-marketplace}
    export RHOAS_CATSRC_NAME=${RHOAS_CATSRC_NAME:-rhoas-operators}
    export RHOAS_NAMESPACE=${RHOAS_NAMESPACE:-openshift-operators}
    
    DOCKER_CFG=$(mktemp)
    chmod -r $DOCKER_CFG
    
    echo "Installing Service Binding Operator"
    curl -s https://raw.githubusercontent.com/redhat-developer/service-binding-operator/master/install.sh | \
        OPERATOR_INDEX_IMAGE=$SBO_INDEX_IMAGE \
        OPERATOR_CHANNEL=$SBO_CHANNEL \
        OPERATOR_PACKAGE=$SBO_PACKAGE \
        CATSRC_NAMESPACE=$SBO_CATSRC_NAMESPACE \
        CATSRC_NAME=$SBO_CATSRC_NAME \
        SKIP_REGISTRY_LOGIN=true \
        DOCKER_CFG=$DOCKER_CFG \
        /bin/bash -s
    
    rm -f $DOCKER_CFG
    
    echo "Installing RHOAS Operator"
    oc apply -f - << EOD
    ---
    apiVersion: operators.coreos.com/v1alpha1
    kind: CatalogSource
    metadata:
      name: $RHOAS_CATSRC_NAME
      namespace: $RHOAS_CATSRC_NAMESPACE
    spec:
      displayName: RHOAS Operators
      icon:
        base64data: ""
        mediatype: ""
      image: $RHOAS_INDEX_IMAGE
      priority: -400
      publisher: RHOAS
      sourceType: grpc
      updateStrategy:
        registryPoll:
          interval: 260s
    ---
    apiVersion: operators.coreos.com/v1alpha1
    kind: Subscription
    metadata:
      name: $RHOAS_PACKAGE
      namespace: $RHOAS_NAMESPACE
    spec:
      channel: $RHOAS_CHANNEL
      installPlanApproval: Automatic
      name: $RHOAS_PACKAGE
      source: $RHOAS_CATSRC_NAME
      sourceNamespace: $RHOAS_CATSRC_NAMESPACE
    EOD
    
    #Wait for the operator to get up and running
    retries=50
    until [[ $retries == 0 ]]; do
      kubectl get deployment/rhoas-operator -n $RHOAS_NAMESPACE >/dev/null 2>&1 && break
      echo "Waiting for rhoas-operator to be created in $RHOAS_NAMESPACE namespace"
      sleep 5
      retries=$(($retries - 1))
    done
    kubectl rollout status -w deployment/rhoas-operator -n $RHOAS_NAMESPACE
    

    Run the following command to execute the script:

    SBO_INDEX_IMAGE=registry.redhat.io/redhat/redhat-operator-index:v4.7 SBO_CHANNEL=preview SBO_PACKAGE=rh-service-binding-operator ./install-operators.sh

    Simulating active users

    As mentioned in Part 1, the Developer Sandbox setup tool can extend the "activity" of simulated active users in one namespace by adding additional workloads. For the purpose of this test, I used two sets of Deployment + Service + Route resources, one for the backing service and one for the application. I included a single ServiceBinding to bind the backing service route URL to the application.

    The backing service is a BusyBox container with an exposed route (the URL to be bound to the application). The application is Service Binding Operator's generic test application, which is good for testing because it is simple and lightweight.

    The simulation tool "provisions" users in sequence. This means that it registers each simulated user into the sandbox. Then, if the user is defined as active, it creates a workload in one of the user's namespaces to simulate their "activity."

    To run the simulation tool, I used the following command:

    go run setup/main.go --template=<workload-template-file> --operators-limit 0 --users 2000 --active 2000 --username zippy

    I used two slightly different approaches (scenarios) to determine performance. Each scenario is represented by a workload template file referenced in the above command and described in the following sections.

    Scenario 1: With a ServiceBinding resource

    The first scenario ("with SBR") includes a ServiceBinding resource in the users' provisioning. The backing service and application are created in the active user's namespace together with a ServiceBinding, so that the Service Binding Operator needs to perform the binding only intermittently. The load on the Service Binding Operator to process ServiceBinding resources and perform the binding is distributed throughout the duration of the user provisioning, which takes a couple of hours for 2,000 users. The workload template for this scenario is perf-test.with-sbr.yaml:

    kind: Template
    apiVersion: v1
    metadata:
      name: sbo-perf-with-sbr
    objects:
      - apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: sbo-perf-app
          labels:
            app: sbo-perf-app
        spec:
          replicas: 1
          strategy:
            type: RollingUpdate
          selector:
            matchLabels:
              app: sbo-perf-app
          template:
            metadata:
              labels:
                app: sbo-perf-app
            spec:
              containers:
              - name: sbo-generic-test-app
                image: quay.io/redhat-developer/sbo-generic-test-app:20200923
                imagePullPolicy: IfNotPresent
                ports:
                - containerPort: 8080
      - apiVersion: v1
        kind: Service
        metadata:
          labels:
            app: sbo-perf-app
          name: sbo-perf-app
        spec:
          ports:
          - port: 8080
            protocol: TCP
            targetPort: 8080
          selector:
            app: sbo-perf-app
      - apiVersion: route.openshift.io/v1
        kind: Route
        metadata:
          labels:
            app: sbo-perf-app
          name: sbo-perf-app
          annotations:
            service.binding/host: path={.spec.host}
        spec:
          port:
            targetPort: 8080
          to:
            kind: "Service"
            name: sbo-perf-app
      - apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: sbo-perf-svc
          labels:
            app: sbo-perf-svc
        spec:
          replicas: 1
          strategy: 
            type: RollingUpdate
          selector:
            matchLabels:
              app: sbo-perf-svc
          template:
            metadata:
              labels:
                app: sbo-perf-svc
            spec:
              containers:
              - name: busybox
                image: busybox
                imagePullPolicy: IfNotPresent
                command: ['sh', '-c', 'echo Container 1 is Running ; sleep 3600']
                ports:
                - containerPort: 8080
      - apiVersion: v1
        kind: Service
        metadata:
          labels:
            app: sbo-perf-svc
          name: sbo-perf-svc
        spec:
          ports:
          - port: 8080
            protocol: TCP
            targetPort: 8080
          selector:
            app: sbo-perf-svc
      - apiVersion: route.openshift.io/v1
        kind: Route
        metadata:
          labels:
            app: sbo-perf-svc
          name: sbo-perf-svc
          annotations:
            service.binding/host: path={.spec.host}
        spec:
          port:
            targetPort: 8080
          to:
            kind: "Service"
            name: sbo-perf-svc
      - apiVersion: binding.operators.coreos.com/v1alpha1
        kind: ServiceBinding
        metadata:
          name: service-binding
        spec:
          services:
          - group: route.openshift.io
            version: v1
            kind: Route
            name: sbo-perf-svc
          application:
            name: sbo-perf-app
            group: apps
            version: v1
            resource: deployments
    

    Scenario 2: Without the ServiceBinding resource

    The second scenario ("without SBR") provisions users without the ServiceBinding resource. The backing service and the application are created in the active user's namespace without a ServiceBinding. The workload template for this scenario is perf-test.without-sbr.yaml:

    kind: Template
    apiVersion: v1
    metadata:
      name: sbo-perf-without-sbr
    objects:
      - apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: sbo-perf-app
          labels:
            app: sbo-perf-app
        spec:
          replicas: 1
          strategy:
            type: RollingUpdate
          selector:
            matchLabels:
              app: sbo-perf-app
          template:
            metadata:
              labels:
                app: sbo-perf-app
            spec:
              containers:
              - name: sbo-generic-test-app
                image: quay.io/redhat-developer/sbo-generic-test-app:20200923
                imagePullPolicy: IfNotPresent
                ports:
                - containerPort: 8080
      - apiVersion: v1
        kind: Service
        metadata:
          labels:
            app: sbo-perf-app
          name: sbo-perf-app
        spec:
          ports:
          - port: 8080
            protocol: TCP
            targetPort: 8080
          selector:
            app: sbo-perf-app
      - apiVersion: route.openshift.io/v1
        kind: Route
        metadata:
          labels:
            app: sbo-perf-app
          name: sbo-perf-app
          annotations:
            service.binding/host: path={.spec.host}
        spec:
          port:
            targetPort: 8080
          to:
            kind: "Service"
            name: sbo-perf-app
      - apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: sbo-perf-svc
          labels:
            app: sbo-perf-svc
        spec:
          replicas: 1
          strategy: 
            type: RollingUpdate
          selector:
            matchLabels:
              app: sbo-perf-svc
          template:
            metadata:
              labels:
                app: sbo-perf-svc
            spec:
              containers:
              - name: busybox
                image: busybox
                imagePullPolicy: IfNotPresent
                command: ['sh', '-c', 'echo Container 1 is Running ; sleep 3600']
                ports:
                - containerPort: 8080
      - apiVersion: v1
        kind: Service
        metadata:
          labels:
            app: sbo-perf-svc
          name: sbo-perf-svc
        spec:
          ports:
          - port: 8080
            protocol: TCP
            targetPort: 8080
          selector:
            app: sbo-perf-svc
      - apiVersion: route.openshift.io/v1
        kind: Route
        metadata:
          labels:
            app: sbo-perf-svc
          name: sbo-perf-svc
          annotations:
            service.binding/host: path={.spec.host}
        spec:
          port:
            targetPort: 8080
          to:
            kind: "Service"
            name: sbo-perf-svc
    

    Only after all of the users are provisioned and the resources have settled into place are the ServiceBinding resources created. All of this happens in a very short time—in fact, almost simultaneously. The following script creates all the ServiceBinding resources, one for each active users namespace:

    oc get deploy --all-namespaces -o json | jq -rc '.items[] | select(.metadata.name | contains("sbo-perf-app")).metadata.namespace' > workload.namespace.list
    
    split -l 300 workload.namespace.list sbr-segment
    
    for i in sbr-segment*; do
        for j in $(cat $i); do
            oc apply -n $j --server-side=true -f - << EOD
    apiVersion: binding.operators.coreos.com/v1alpha1
    kind: ServiceBinding
    metadata:
      name: service-binding
    spec:
      services:
        - group: route.openshift.io
          version: v1
          kind: Route
          name: sbo-perf-svc
      application:
        name: sbo-perf-app
        group: apps
        version: v1
        resource: deployments
    EOD
            sleep 0.02s
        done &
    done
    
    wait
    
    rm -rf sbr-segment*
    rm -rf workload.namespace.list
    

    Creating the ServiceBinding resources all at once simulates a situation where all of the active users do the binding in their namespace almost simultaneously. This happens, for example, when thousands of Red Hat Summit attendees see an announcement or demonstration at the same time and start playing with the Service Binding Operator in their sandboxes. That's the most extreme stress under which the Service Binding Operator will run, and the test should ensure that nothing crashes.

    Next steps

    In this article, you saw how to set up the test environment for testing the Service Binding Operator's real-life performance under stress. Next, we will look at the infrastructure to collect metrics during the tests.

    Read next: Part 3: Collecting runtime metrics.

    Last updated: January 15, 2025

    Related Posts

    • Troubleshooting application performance with Red Hat OpenShift metrics, Part 1: Requirements

    • Introducing the Service Binding Operator

    • Service Binding Operator: The Operator in action

    Recent Posts

    • A guide to JIT checkpointing with Kubeflow Trainer on OpenShift AI

    • How to manage TLS certificates used by OpenShift GitOps operator

    • Configure a split disk on OpenShift Container Platform

    • Red Hat Enterprise Linux 10.2 and 9.8: Top features for developers

    • What GPU kernels mean for your distributed inference

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.