Deploying operator resource mapping (ORM)

To optimize the performance of your containerized workloads, Turbonomic for Government Standard can recommend actions such as resizing container specs vertically and scaling replicas horizontally. The Kubeturbo agent executes these actions by updating workload specifications programmatically through workload controllers.

If an operator manages the lifecycle of microservice-based applications and workloads in your environment to maintain their intended configurations, it is possible for the operator to revert any Kubeturbo updates because the updates were not applied through the operator's Custom Resource (CR). To prevent this issue, deploy operator resource mapping (ORM) that defines how to update specifications through the CR. The operator then rolls out the updates.

When you deploy ORM, you define the operand for managing resources and maintaining their intended configurations. Specifically, you configure parameters that control how other assets can make changes to the CR programmatically.

ORM schema

For ORM samples, see this GitHub page.

apiVersion: devops.turbonomic.io/v1alpha1 # version of the api that manifest is using
kind: OperatorResourceMapping # type of kubernetes resource
metadata:
  name: orm # name of the resource
  namespace: # namespace where this resource is located
...
spec:
  mappings:
    patterns:
      - owned:
          apiVersion: # api version for owned resource
          kind: # type of owned resource
          path: # The JSON path to the resource property to be mapped, targeting specific containers within an owned resource.
          selector: # reference to the selector
        ownerPath: #JSON path to the resource location in the owner object, where the values should be reflected
    selectors: # Defines label selectors for identifying resources.
      xyz: # A named selector.
        matchLabels: # Label selectors used to identify resources
         ...
  owner:
    apiVersion: # The API version of the owner resource
    kind: # The kind of the owner resource
    name: # The name of the owner resource
status:
  lastTransitionTime: # The timestamp of the last status change
  owner:
    apiVersion: # Details of owner resource specified in 'spec.owner'
    kind:
    name:
    namespace:
  ownerValues: # The values from the owner resource that correspond to the mappings.
    - owned: # Details of the owned resource specified in 'spec.mappings.patterns.owned'
        apiVersion:
        kind:
        name:
        namespace:
        path: # The JSON path to the resource property to be mapped, targeting specific containers within an owned resource.
      ownerPath: #JSON path to the resource location in the owner object, where the values should be reflected
      value:
        resources: {}
  state: # The status of the resource mapping indicates whether it is 'ok' or if there have been any errors with a reason during the discovery of ORM's and building the ORM mapping registry .

The schema includes the following properties:

Property Description
owner An operator resource that manages owned (see the next item). Any change to owner triggers the operator to update owned.
Note:

An advisor is a resource that provides recommendations to workloads. It gets the mapping from an ORM and modifies owner according to the recommendations. Examples of advisor are Turbonomic for Government Standard, Kubernetes Horizontal Pod Autoscaler, and Vertical Pod AutoScaler. The use cases in the sections that follow include links to GitHub pages that provide sample advisor resources.

owned A resource deployed by an operator. This resource responds to changes in owner (see the previous item).
mappings A pair of paths in owner and owned
patterns A pair of paths in owner and owned

You can define parameters to generate multiple mappings from one pattern. A predefined parameter starts with a period symbol.

.owned.name refers to the name of the owned resource. Together with the label selector of owned, a pattern can generate multiple mappings if the naming is correct.

selectors Predefined label selectors that are used in patterns

Use case 1: One ORM for one owned resource controlled by one owner

For file samples in support of this use case, see this GitHub page.

  • owner

    kind: Deployment
    metadata:
      name: ormowner-solo
  • owned

    kind: Deployment
    metadata:
      name: ormowned-solo
  • ORM

    kind: OperatorResourceMapping
    metadata:
      name: solo
    
    owner:
        kind: Deployment
        name: ormowner-solo
    mappings:
        patterns:
          owned:
            kind: Deployment
            name: ormowned-solo

Use case 2: One ORM for two owned resources controlled by one owner

For file samples in support of this use case, see this GitHub page.

  • owner

    kind: Deployment
    metadata:
      name: ormowner-patterns
  • owned-1

    kind: Deployment
    metadata:
      name: ormowned-0001
      labels:
         app: ormowned-patterns
         id: "00001"
  • owned-2

    kind: Deployment
    metadata:
      name: ormowned-0002
      labels:
         app: ormowned-patterns
         id: "00002"
  • ORM

    kind: OperatorResourceMapping
    metadata:
      name: patterns
    
    owner:
      kind: Deployment
      name: ormowner-patterns
    
    owned:
      kind: Deployment
      matchLabels:
        app: ormowned-patterns
        id: "00001"
    
    owned:
      kind: Deployment
      matchLabels:
        app: ormowned-patterns
        id: "00002"

Use case 3: Multiple ORMs for one owned resource with a hierarchy of owners

For file samples in support of this use case, see this GitHub page.

  • owner-1

    kind: Deployment
    metadata:
      name: ormowner-1
  • owner-2

    kind: Deployment
    metadata:
      name: ormowner-2
  • owned

    kind: Deployment
    metadata:
      name: ormowned
  • ORM 1

    kind: OperatorResourceMapping
    metadata:
      name: hierarchy-1
    
    owner:
      kind: Deployment
      name: ormowner-1
    
    owned:
      kind: Deployment
      name: ormowned
  • ORM 2

    kind: OperatorResourceMapping
    metadata:
      name: hierarchy-2
    
    owner:
      kind: Deployment
      name: ormowner-2
    
    owned:
      kind: Deployment
      name: ormowner-1

Testing your ORM CR

  1. Clone the turbonomic-container-platform repository.

    mkdir turbonomic
    cd turbonomic
    git clone https://github.com/IBM/turbonomic-container-platform.git
    cd orm
  2. Create the CRD for the ORM in the cluster where you deployed Kubeturbo.

    kubectl apply -f install/devops.turbonomic.io_operatorresourcemappings.yaml
    Note:

    The CRD supports Kubernetes 1.16 and later.

  3. Deploy the ORM CR to the namespace of the application where actions for containerized workloads are executed.

    For sample CRs, see this GitHub page.

    In the following example, the CR deploys to the namespace where Turbonomic for Government Standard runs to enable resizing of Turbonomic for Government Standard services.

    kubectl -n turbonomic apply -f examples/ibm/turbo_operator_resource_mapping_sample_cr.yaml
  4. In the Turbonomic for Government Standard user interface, navigate to Settings > Target configuration, find and select the Kubeturbo target, and then click Rediscover.

    You do not need to restart the corresponding Kubeturbo pod in the cluster. Kubeturbo automatically discovers the ORM CR when you see a message similar to the following example.

    I0118 22:34:08.013144       1 k8s_discovery_client.go:327] Discovered 1 v2 ORM Resources.
    Note:

    For Kubeturbo to access the operator-managed CRs from the CRD and map the resources using ORM, Kubeturbo should run with the cluster-admin role. For more information about roles, see this topic.

  5. Try the Redis Standalone example to see the relationship between the operator and the resource it manages.

    1. Create Redis Standalone by following the instructions in this GitHub page.

    2. Run the following command:

      helm list -A

      The command returns results similar to the following example.

      NAME            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                      APP VERSION
      redis           ot-operators    1               2023-05-04 12:27:31.051399 -0400 EDT    deployed        redis-0.14.2               0.14.0     
      redis-operator  ot-operators    1               2023-03-13 12:31:40.264923 -0400 EDT    deployed        redis-operator-0.14.3      0.14.0
    3. Get the Redis Standalone controller resource.

      kubectl get statefulset -n ot-operators -oyaml
      apiVersion: v1
      items:
      - apiVersion: apps/v1
        kind: StatefulSet
        metadata:
          annotations:
            redis.opstreelabs.in: "true"
            redis.opstreelabs.instance: redis-standalone
          creationTimestamp: "2024-01-25T21:48:58Z"
          generation: 1
          labels:
            app: redis-standalone
            redis_setup_type: standalone
            role: standalone
          name: redis-standalone
          namespace: ot-operators
          ownerReferences:
          - apiVersion: redis.redis.opstreelabs.in/v1beta1
            controller: true
            kind: Redis
            name: redis-standalone
            uid: 6dd0759d-0c67-48e7-903b-bf74b9c8e4a1
          resourceVersion: "41508623"
          uid: 256b404c-1961-471e-9d1c-5085d44e2de2
        spec:
          podManagementPolicy: OrderedReady
          replicas: 1
          revisionHistoryLimit: 10
          selector:
            matchLabels:
              app: redis-standalone
              redis_setup_type: standalone
              role: standalone
          serviceName: redis-standalone-headless
          template:
            metadata:
              annotations:
                redis.opstreelabs.in: "true"
                redis.opstreelabs.instance: redis-standalone
              creationTimestamp: null
              labels:
                app: redis-standalone
                redis_setup_type: standalone
                role: standalone
            spec:
              containers:
              - env:
                - name: REDIS_ADDR
                  value: redis://localhost:6379
                - name: SERVER_MODE
                  value: standalone
                - name: SETUP_MODE
                  value: standalone
                image: quay.io/opstree/redis:v7.0.5
                imagePullPolicy: IfNotPresent
                livenessProbe:
                  exec:
                    command:
                    - bash
                    - /usr/bin/healthcheck.sh
                  failureThreshold: 3
                  initialDelaySeconds: 1
                  periodSeconds: 10
                  successThreshold: 1
                  timeoutSeconds: 1
                name: redis-standalone
                readinessProbe:
                  exec:
                    command:
                    - bash
                    - /usr/bin/healthcheck.sh
                  failureThreshold: 3
                  initialDelaySeconds: 1
                  periodSeconds: 10
                  successThreshold: 1
                  timeoutSeconds: 1
                resources:
                  limits:
                    cpu: 100m
                    memory: 128Mi
                  requests:
                    cpu: 20m
                    memory: 24Mi
                terminationMessagePath: /dev/termination-log
                terminationMessagePolicy: File
              dnsPolicy: ClusterFirst
              restartPolicy: Always
              schedulerName: default-scheduler
              securityContext: {}
              terminationGracePeriodSeconds: 30
          updateStrategy:
            rollingUpdate:
              partition: 0
            type: RollingUpdate
        status:
          availableReplicas: 1
          collisionCount: 0
          currentReplicas: 1
          currentRevision: redis-standalone-799b86b4b5
          observedGeneration: 1
          readyReplicas: 1
          replicas: 1
          updateRevision: redis-standalone-799b86b4b5
          updatedReplicas: 1
      kind: List
      metadata:
        resourceVersion: ""
      
    4. Get the Redis operator CR.

      kubectl get redis redis-standalone -n ot-operators -oyaml
      apiVersion: redis.redis.opstreelabs.in/v1beta1
      kind: Redis
      metadata:
        annotations:
          kubectl.kubernetes.io/last-applied-configuration:
        creationTimestamp: "2024-01-25T21:48:58Z"
        finalizers:
          - redisFinalizer
        generation: 2
        name: redis-standalone
        namespace: ot-operators
        resourceVersion: "41508397"
        uid: 6dd0759d-0c67-48e7-903b-bf74b9c8e4a1
      spec:
        kubernetesConfig:
          image: quay.io/opstree/redis:v7.0.5
          imagePullPolicy: IfNotPresent
          resources:
            limits:
              cpu: 100m
              memory: 128Mi
            requests:
              cpu: 20m
              memory: 24Mi
        livenessProbe:
          failureThreshold: 3
          initialDelaySeconds: 1
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        readinessProbe:
          failureThreshold: 3
          initialDelaySeconds: 1
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 1
        redisExporter:
          image: quay.io/opstree/redis-exporter:v1.44.0
        securityContext: {}
      
      
    5. Apply the redis standalone orm from library.

      kubectl get orm -n ot-operators redis-orm -o yaml

      The patterns defined in spec are found in the cluster. Details are shown in the status section.

      apiVersion: devops.turbonomic.io/v1alpha1
      kind: OperatorResourceMapping
      metadata:
        name: redis-orm
        namespace: ot-operators
      ...
      spec:
        mappings:
          patterns:
          - owned:
              apiVersion: apps/v1
              kind: StatefulSet
              path: .spec.template.spec.containers[?(@.name=="redis")].resources
              selector: my_redis_sts
            ownerPath: .spec.kubernetesConfig.resources
          selectors:
            my_redis_sts:
              matchLabels:
                app: redis
        owner:
          apiVersion: redis.redis.opstreelabs.in/v1beta1
          kind: Redis
          name: redis
      status:
        lastTransitionTime: "2023-05-04T17:27:05Z"
        owner:
          apiVersion: redis.redis.opstreelabs.in/v1beta1
          kind: Redis
          name: redis
          namespace: ot-operators
        ownerValues:
        - owned:
            apiVersion: apps/v1
            kind: StatefulSet
            name: redis
            namespace: ot-operators
            path: .spec.template.spec.containers[?(@.name=="redis")].resources
          ownerPath: .spec.kubernetesConfig.resources
          value:
            resources: {}
        state: ok