# Customization overview

PerfectScale allows easy cluster management through profiles, increasing productivity with minimal effort. PerfectScale provides various customizations to help you create your own K8s optimization ecosystem, enhancing observability across your environment and simplifying alerting, ticketing, pricing management, etc., to ensure you achieve the best possible results.

To make your optimization process seamless and personalized, PerfectScale provides various out-of-the-box integrations, helping you to build your optimization ecosystem tailored to your business needs.&#x20;

{% hint style="success" %}
You can configure integrations using either UI profiles or CRD-based inline profiles. However, **we strongly recommend you configure the integrations via PerfectScale UI**.

**Profiles referenced via CRD take precedence over profiles assigned in UI.**
{% endhint %}

You can quickly configure integrations using **Customization Profiles** (YAML files) via UI, or customize your environment by configuring profiles with **CRDs**.

{% hint style="info" %}
You can apply multiple profiles to a single cluster, but only one profile of each type (Slack, Jira, etc.).
{% endhint %}

## :sparkles: Recommended path&#x20;

1. Create and test profiles in the UI first.
2. Use refByID to reference the profile by its ID (case-sensitive).
3. Ensure the profile type you are referencing matches the existing profile type.

```yaml
apiVersion: perfectscale.io/v1
kind: ClusterSettings
metadata:
  name: cluster-settings-main
  namespace: perfectscale
spec:
  profiles:
    pricing:
      # Reference an existing pricing profile by ID
      # No assigned field needed - refByID is always assigned
      - type: custom
        refByID: "pricing-1"
```

## Configuring integration profile via UI

You can seamlessly create an integration profile directly from the PerfectScale UI. Simply go to **Settings**, select the **integration type**, and click **Add profile**. This approach allows you to set up and manage integrations quickly and accurately. Detailed step-by-step instructions for each integration type are provided on their dedicated integration pages.

<figure><img src="https://3591580169-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FzCh9aABpk7yLeToPr6vk%2Fuploads%2F5378wiIh8tfAqEz6Dljx%2Fimage.png?alt=media&#x26;token=27cb8b17-a2c8-445f-b5e4-14c404851332" alt=""><figcaption><p>Settings</p></figcaption></figure>

{% hint style="info" %}
When a profile is created in the UI, its ID has the following format: `{type|class}-N`. Underscores in type/class names are converted to hyphens in IDs.\
\
**Examples**: `jira-1`, `slack-2`, `resiliency-alerts-1`, etc.
{% endhint %}

Created profiles in the UI can be referenced in a CRD using `refByID`.&#x20;

**Example:**

```
- type: jira
        refByID: "jira-1"
```

{% hint style="warning" %}
Profiles created via CRD are cluster-specific and cannot be referenced by other clusters.
{% endhint %}

### Prerequisites for configuring the integration profile via CRD

{% hint style="info" %}
The feature is supported starting from the exporter **v1.0.80**.
{% endhint %}

If **psc-exporter (v1.0.80+)** is newly installed, the required **CRDs are installed automatically**, allowing you to manage cluster settings using custom resources (CRs). However, CRDs are **not installed automatically** when running a `helm upgrade`. If you are upgrading, you must install the CRDs manually by running:

```
kubectl apply -f https://github.com/perfectscale-io/perfectscale-io.github.io/blob/main/charts/perfectscale-agent/crds/clustersettings.yaml
```

## Combining refByID and inline integrations

{% hint style="success" %}
We highly recommend creating profiles via UI and referencing them using `refByID` rather than inline profiles. If you prefer using inline profiles, you can test them in UI before activation, even when `assigned: false`.
{% endhint %}

You have the option to configure a CR that combines multiple integrations, allowing you to manage them with a single resource. Additionally, you can include existing profiles that were previously configured via the PerfectScale UI, giving you flexibility to mix and match inline and UI-based configurations in one setup.

You can combine both approaches by referencing profiles created in the PerfectScale UI directly in your CR using <mark style="color:$warning;">**refByID (case-sensitive)**</mark>. However, refByID cannot reference profiles created via CR, and a CR cannot have both `refByID` and an inline profile with `assigned: true` for the same type.

**Example:**

* ✅ **Valid:** `refByID` + inline profiles with `assigned: false` (for testing)
* ❌ **Invalid:** `refByID` + inline profile with `assigned: true`

{% hint style="info" %}
When using **refByID**, ensure the profile type matches the existing profile's type: jira, slack, teams, etc.
{% endhint %}

#### Example

```yaml
apiVersion: perfectscale.io/v1
kind: ClusterSettings
metadata:
  name: cluster-settings-main
  namespace: perfectscale
spec:
  profiles:
    # Pricing: Create inline custom pricing profile and assign it
    pricing:
      - type: custom
        name: production-pricing
        assigned: true
        value:
          nodeTypes:
            - instanceType: c5.large
              pricing:
                cpuCoreHourPrice: 0.085
                memGBHourPrice: 0.0095
            - instanceType: c5.xlarge
              pricing:
                cpuCoreHourPrice: 0.085
                memGBHourPrice: 0.0095
            - instanceType: m5.large
              pricing:
                cpuCoreHourPrice: 0.096
                memGBHourPrice: 0.0096

      # Create AWS CUR profile but don't assign it yet (for testing)
      - type: aws_cur
        name: aws-cur-backup
        assigned: false
        value:
          role_arn: "arn:aws:iam::your-account-id:role/your-role-name"
          aws_external_id: "your-unique-external-id-here"
          athena_database: athenacurcfn_perfectscale_cur
          athena_region: us-east-1
          athena_result_bucket: s3://perfectscale-cur-results/
          athena_table: perfectscale_cur_hourly
          aws_account_id: "your-aws-account-id-here"

    # Integrations: Mix inline Slack and referenced Jira
    integrations:
      # Create and assign inline Slack integration
      - type: slack
        name: team-alerts
        assigned: true
        value:
          channel: "perfectscale-production"
          routings:
            - label_alert
            - cost_waste_alert
          slack_token_from:
            secretKeyRef:
              name: slack-credentials
              key: bot-token

      # Reference existing Jira integration (created in UI)
      - type: jira
        refByID: "jira-1"

    # Resiliency Alerts: Create inline profile
    resiliency_alerts:
      - name: production-alerts
        assigned: true
        value:
          min_risk_level: high
          ignore_workload: "^(test-.*|dev-.*)"
          ignore_namespace: "^(kube-system|kube-public)$"
          ignore_container: "^(istio-proxy|envoy)$"
          ignore_indicator: ""

    # PodFit Labels: Reference existing profile
    podfit_labels:
      - refByID: "label-1"

    # Customization: Create inline profile
    customization:
      - name: app-labels
        assigned: true
        value:
          workload_labels:
            - app.kubernetes.io/name
            - app.kubernetes.io/component
            - team
            - cost-center
          node:
            spot_labels:
              label_cloud_google_com_gke_nodepool: pool-2
              label_spotinst_io_node_lifecycle: spot
            node_group_labels:
              - eks.amazonaws.com/nodegroup
              - kops.k8s.io/instancegroup
            architecture_labels:
              - kubernetes.io/arch
            os_labels:
              - kubernetes.io/os
            instance_type_labels:
              - node.kubernetes.io/instance-type
            region_labels:
              - topology.kubernetes.io/region
            availability_zone_labels:
              - topology.kubernetes.io/zone
          ignored_pod_labels:
            - uid
            - namespace
          ignored_node_labels:
            - uid
```

{% hint style="info" %}
CRD profiles (both refByID and inline) take precedence over UI-assigned profiles and will override them.
{% endhint %}

## Deleting a profile

{% hint style="danger" %}
Profiles assigned to clusters cannot be deleted.
{% endhint %}

To delete a profile, it **must first be unassigned** from all clusters.\
If the profile is still assigned to one or more clusters, deletion is blocked, and a pop-up message will inform you of the reason with the following instructions.

<figure><img src="https://3591580169-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FzCh9aABpk7yLeToPr6vk%2Fuploads%2FPC1yJzgNlkpdZp6xYarX%2Fimage.png?alt=media&#x26;token=f83e8998-a77e-4cd2-a371-d503c477e65c" alt="" width="563"><figcaption><p>Unassign a cluster befor deleting</p></figcaption></figure>

To unassign a profile from all clusters, navigate to **Manage Assignments** and deselect the relevant profile.

{% hint style="warning" %}
Profiles created via CRD cannot be deleted in the UI, so the delete option is disabled.
{% endhint %}

## Troubleshooting

#### CRD creation failed

* Check if ClusterSettings already exists: `kubectl get clustersettings -n perfectscale`
* Review logs: `kubectl logs -n perfectscale -l app=perfectscale-exporter`

#### Profile not applied

* Check logs for sync errors
* Verify secrets exist: `kubectl get secrets -n perfectscale`
* Check profile format matches the type

#### Secret not found

* Verify secret exists: `kubectl get secret <secret-name> -n perfectscale`
* Ensure the secret is in `perfectscale` namespace

#### Profile deletion failed

* Ensure the profile is unassigned from all clusters
* If it is still assigned, navigate to **Manage Assignments** and deselect the relevant profile.
