> For the complete documentation index, see [llms.txt](https://docs-epc.gitbook.io/ncs-documents/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-epc.gitbook.io/ncs-documents/public-api/getting-start-with-ncs-public-api/auto-scaling-openstack-instances-with-senlin-and-prometheus.md).

# Auto-scaling OpenStack Instances with Senlin and Prometheus

### Prerequisite

* openstack cloud
* Prometheus
* Alertmanager
* ทั้ง Prometheus เเละ Alertmanager ควรใช้งานเป็น [version ล่าสุด](https://prometheus.io/download/)
* Installation Openstack client, Senlin client, and Octavia client

  * update packet cache\
    `sudo apt update`
  * Install Python3\
    `sudo apt install python3-dev python3-pip`
  * install the OpenStack client\
    `sudo apt install python3-openstackclient`
  * verify by accessing the help tools\
    `openstack --help`

  <figure><img src="/files/jC2mzKzuTFvhF66zdFDN" alt=""><figcaption></figcaption></figure>

  * build environment python for Senlin client and Octavia client\
    `sudo python3 -m venv /opt/osenv`\
    `sudo chmod -R 777 /opt/osenv`
  * use command `source` to use environment python\
    `source /opt/osenv/bin/activate`
  * install the Senlin client and Octavia client\
    `pip install python-senlinclient python-octaviaclient`
  * verify install Senlin client and Octavia client
    * Senlin\
      `openstack cluster -h`\
      \
      ![](/files/VbM67GGe4KgmtZJraI1v)
    * Octavia\
      `openstack loadbalancer -h`\
      \
      ![](/files/n8ifVsDBdEdyTTisElAj)

### Requirement

* ติดตั้ง Openstack cli ลงในเครื่อง ubuntu
* OpenStack RC File

### Setup Senlin Cluster

สร้าง Instance ขึ้นมา 1 ตัวที่ติดตั้งเพื่อใช้ในการสร้าง Cluster \[OPTIONAL] ด้วย command ดังนี้ :

{% code overflow="wrap" %}

```bash
FLOATING_IP=$(openstack floating ip create -f value -c floating_ip_address {Network_Pool_id})
```

{% endcode %}

```bash
PORT_ID=$(openstack port create \
  --network {network_id} \
  --security-group ALL \
  --security-group default \
  --security-group http \
  --security-group ICMP \
  --security-group HTTP \
  --security-group In-Cluster \
  --security-group Ping \
  --security-group ssh \
  Instance1234-port \
  -f value -c id)
```

```bash
openstack floating ip set --port $PORT_ID $FLOATING_IP  
```

{% code overflow="wrap" %}

```bash
openstack server create \
  --flavor {flavor_id} \
  --image {image_id} \
  --boot-from-volume 20 \
  --availability-zone NCP-NON \
  --port $PORT_ID \
  --key-name gaebolg \
  Instance1234
```

{% endcode %}

หลังจากนั้น remote เข้า instance ด้วย command `ssh -i [path-file-privatekey] username@host-IP` เพื่อ install Openstack Client เเละ Senlin Client ดูได้ที่ **Prerequisite**

#### สร้าง Cluster ด้วย Senlin

1. สร้างไฟล์ node\_exporter\_profile.yaml

```bash
touch node_exporter_profile.yaml
```

<figure><img src="/files/QNxQUHNefCl1xBoaJyaf" alt=""><figcaption></figcaption></figure>

2. ทำการเเก้ไขภายในไฟล์ node\_exporter\_profile.yaml สามารถดู Properties เพิ่มเติมได้ที่ [**Nova Profile**](https://docs.openstack.org/senlin/yoga/user/profile_types/nova.html)

**Note:** ในส่วนของ **image:** เเละ **block\_device\_mapping\_v2:** เลือกใช้อย่างใดอย่างหนึ่งเท่านั้น

**Limitation:** **admin\_pass** ไม่สามารถใช้งานได้&#x20;

{% code overflow="wrap" %}

```yaml
type: os.nova.server
version: 1.0
properties:
  name: UbuntuG
  flavor: {flavor_name} # Ex.csa.large.v2
  availability_zone: {az} # Ex. NCP-BKK,NCP-NON,NCP-BKK2
  image: {image_name} # Ex.ubuntu-24-v240703
####################### 
#  block_device_mapping_v2:    
#    - uuid: {image_id}
#      boot_index: 0
#      source_type: image
#      destination_type: volume
#      volume_size: 30
#######################
  networks:
    - network: default # หาได้จาก vpc network ของ project นั้นๆ
      floating_network: {floating_network_name} # Ex.Standard_Public_IP_Pool_NON
      security_groups:
        - ALL
        - default
        - In-Cluster
        - HTTP
        - Ping
  user_data: |
    #!/bin/bash
    # Update the package index
    sudo apt-get update
    cd /home/ubuntu

    wget \
      https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-amd64.tar.gz

    sudo groupadd -f node_exporter
    sudo useradd -g node_exporter --no-create-home --shell /bin/false node_exporter
    sudo mkdir /etc/node_exporter
    sudo chown node_exporter:node_exporter /etc/node_exporter

    tar -xvf node_exporter-1.0.1.linux-amd64.tar.gz
    mv node_exporter-1.0.1.linux-amd64 node_exporter-files

    sudo cp node_exporter-files/node_exporter /usr/bin/
    sudo chown node_exporter:node_exporter /usr/bin/node_exporter

    echo "[Unit]
    Description=Node Exporter
    Documentation=https://prometheus.io/docs/guides/node-exporter/
    Wants=network-online.target
    After=network-online.target

    [Service]
    User=node_exporter
    Group=node_exporter
    Type=simple
    Restart=on-failure
    ExecStart=/usr/bin/node_exporter --web.listen-address=:9100

    [Install]
    WantedBy=multi-user.target" | sudo tee /usr/lib/systemd/system/node_exporter.service > /dev/null

    sudo chmod 664 /usr/lib/systemd/system/node_exporter.service

    sudo systemctl daemon-reload
    sudo systemctl start node_exporter

    sudo systemctl status node_exporter

    sudo systemctl enable node_exporter.service

    sudo apt-get install -y wget nginx

    systemctl start nginx

    systemctl enable nginx

    # NGINX EXPORTER
    #Setting nginx Configuration

    apt-get install -y wget tar

        sudo sed -i '/^http {/a \
        \n    server {\n\
            listen 8080;\n\
            server_name localhost;\n\
            \n\
            location /stub_status {\n\
                stub_status;\n\
                allow 0.0.0.0/0; # Allow all IPs for testing (restrict in production)\n\
                deny all;\n\
            }\n\
        }\n
    ' /etc/nginx/nginx.conf

    # Restart Nginx to apply changes
    systemctl restart nginx


    # Download and install Nginx Exporter
    
    wget \
      https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v1.2.0/nginx-prometheus-exporter_1.2.0_linux_amd64.tar.gz

    tar -xvzf nginx-prometheus-exporter_1.2.0_linux_amd64.tar.gz

    mv nginx-prometheus-exporter /usr/local/bin/
    
    # Create systemd service for Nginx Exporter
    cat <<EOL > /etc/systemd/system/nginx-exporter.service
    [Unit]
    Description=Nginx Prometheus Exporter
    After=network.target

    [Service]
    ExecStart=/usr/local/bin/nginx-prometheus-exporter -nginx.scrape-uri http://localhost:8080/stub_status
    Restart=always
    User=nobody
    Group=nogroup

    [Install]
    WantedBy=multi-user.target
    EOL

    # Reload systemd and start the Nginx Exporter service
    systemctl daemon-reload
    systemctl start nginx-exporter
    systemctl enable nginx-exporter

```

{% endcode %}

ในส่วนของ userdata จะทำการติดตั้ง Node exporter , Nginx , Nginx Exporter

หากใช้ IIS ให้เเก้ไข node\_exporter\_profile.yaml ดังนี้เเทน

{% code overflow="wrap" %}

```yaml
type: os.nova.server
version: 1.0
properties:
  name: WindowG
  flavor: {flavor_name} # Ex. asa.large.v2
  key_name: gaebolg
  image: {Image_id}    #image ที่มีการติดตั้ง window exporter เเล้ว
  availability_zone: {AZ_name} # Ex. NCP-BKK
  networks:
    - network: {Network_name} # Ex. Network-fortest
      floating_network: {floating_network_name}  # Ex. Standard_Public_IP_Pool_NON
      security_groups:
        - ALL
        - default
        - http
        - HTTP
        - ICMP
        - In-Cluster
        - Ping
        - ssh
```

{% endcode %}

3. สร้าง Cluster Profile จาก ไฟล์ node\_exporter\_profile.yaml

{% code overflow="wrap" %}

```bash
openstack cluster profile create --spec node_exporter_profile.yaml {profile_name}
```

{% endcode %}

Note : {profile\_name} สามารถใส่เป็นชื่ออะไรก็ได้

<figure><img src="/files/Ed6xygeY8mTxth8JKcLk" alt=""><figcaption></figcaption></figure>

4. สร้าง Cluster จาก Profile ที่สร้างไว้ในข้อที่ 2

Note: {Clustername} สามารถใส่เป็นชื่ออะไรก็ได้

{% code overflow="wrap" %}

```bash
openstack cluster create --profile {cluster_profile_name} --desired-capacity 1 --min-size 1 {Clustername}
```

{% endcode %}

หรือ

{% code overflow="wrap" %}

```bash
openstack cluster create --profile {cluster_profile_name} --desired-capacity 1 --min-size 1 --max-size 3 {Clustername}
```

{% endcode %}

5. สร้าง Receiver เพื่อใช้ในการ trigger scale in เเละ scale out

```bash
openstack cluster receiver create --cluster {Clustername} --action CLUSTER_SCALE_OUT {receiver_name}
```

หลังจากสร้าง Receiver ให้ทำการ Copy **alarm\_url** ไว้ทุกครั้ง เพื่อใช้ในส่วนของการ Setup Alertmanager ต่อ

ตัวอย่างเช่น

<figure><img src="/files/eF0I40OFqiVIL3xAMEzz" alt=""><figcaption></figcaption></figure>

alarm\_url คือ <https://cloud-api.nipa.cloud:8778/v1/webhooks/cec91e87-f31d-4471-b451-283a8e382c7d/trigger?V=2>

```bash
openstack cluster receiver create --cluster {Clustername} --action CLUSTER_SCALE_IN {receiver_name}
```

<figure><img src="/files/hhGHzugzUcC8lMzY7Dx5" alt=""><figcaption></figcaption></figure>

alarm\_url คือ <https://cloud-api.nipa.cloud:8778/v1/webhooks/e2dc6ab7-4827-4477-b4f7-90f849dfd47a/trigger?V=2>

หากต้องการเรียกดู **alarm\_url** ภายหลังสามารถใช้คำสั่ง

```bash
openstack cluster receiver show {Receivername} -f value -c channel
```

6. สามารถดู metrics ของ node exporter ได้ที่ http\://\<FloatingIP>:9100/metrics

### Config Prometheus

1. สร้าง Instance สำหรับ ติดตั้ง Prometheus (สามารถนำ command สร้าง Instance จาก Setup Senlin Cluster มาใช้ในการสร้างได้)
2. ติดตั้ง [Prometheus](broken://pages/pMHQurjwj2wBaHzYCe3a) (ควรใช้เวอร์ชั่นล่าสุด)
3. **สร้าง application credential ที่ใช้ใน config ให้ใช้ ซึ่ง application\_credential\_secret จะเเสดงเเค่ครั้งเเรกที่สร้างเท่านั้น**

```bash
openstack application credential create {credentialname}
```

1. เมื่อติดตั้ง Prometheus เรียบร้อยแล้ว ให้แก้ไขไฟล์ config ของ prometheus ที่ path: **/etc/prometheus/prometheus.yml** โดยทำการเเก้ไขไฟล์ config ดังนี้

```bash
global:
 scrape_interval: 15s
scrape_configs:
 - job_name: 'prometheus'
   scrape_interval: 5s
   static_configs:
     - targets: ['localhost:9090']
 # Job, for nginx_exporter
 - job_name: 'NginxExporter'
   openstack_sd_configs:  #ต้องทำการเเก้ไขในส่วนนี้
      - identity_endpoint: https://identity-api.nipa.cloud/
        region: NCP-TH
        application_credential_id: {appcredentialID}   
        application_credential_secret: {appcredentialsecret}
        role: instance
   relabel_configs:
        - source_labels: [__meta_openstack_tag_cluster_id]
          action: keep
          regex: .+
          target_label: cluster_id
        # Update the scraping port if required
        - source_labels:
          - __address__
          action: replace
          regex: ([^:]+)(?::\d+)
          replacement: $1:9113
          target_label: __address__
    # Scrape IIS instances  
 - job_name: 'IIS'
   openstack_sd_configs:
      - identity_endpoint: https://identity-api.nipa.cloud/
        region: NCP-TH
        application_credential_id: {appcredentialID}   
        application_credential_secret: {appcredentialsecret}
        role: instance
   relabel_configs:
        - source_labels: [__meta_openstack_tag_cluster_id]
          action: keep
          regex: .+
          target_label: cluster_id
        # Update the scraping port if required
        - source_labels:
          - __address__
          action: replace
          regex: ([^:]+)(?::\d+)
          replacement: $1:9182
          target_label: __address__
 # Scrape OpenStack instances
 - job_name: 'openstack'
   openstack_sd_configs:
      - identity_endpoint: https://identity-api.nipa.cloud/
        region: NCP-TH
        application_credential_id: {appcredentialID}   
        application_credential_secret: {appcredentialsecret}
        role: instance
   relabel_configs:
      # Keep Senlin instances that have cluster_id
      - source_labels: [__meta_openstack_tag_cluster_id]
        action: keep
        regex: .+
        target_label: cluster_id
      # Update the scraping port if required
      - source_labels:
        - __address__
        action: replace
        regex: ([^:]+)(?::\d+)
        replacement: $1:9100
        target_label: __address__
        
rule_files:
  - alert.rules.yml

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 'localhost:9093'
```

4. ในโฟลเดอร์เดียวกันกับ **/etc/prometheus/** ให้สร้าง **alert\_rules.yml** ด้วย จากนั้นเเก้ไขไฟล์ **alert\_rules.yml** ดังนี้ โดย rule ที่ใช้จะขึ้นกับตัว Exporter ที่ผู้ใช้งานเลือกใช้<br>

4.1 กรณีใช้กับ **Node Exporter**

{% code overflow="wrap" %}

```yaml
groups:
- name: example
  rules:
  # Define minimum cluster size
  - record: min_cluster_size
    expr: 2  # Change this value to your actual minimum cluster size

  # Record the count of instances with idle CPU
  - record: instance_count
    expr: count(count by (instance) (node_cpu_seconds_total{mode="idle"}))

  # Alert for any instance that has average CPU idle < 60% (40% usage) only if instance count is equal or greater than min_cluster_size
  - alert: HighUsage
    expr: |
      (instance_count >= min_cluster_size)
      and
      (
        avg(irate(node_cpu_seconds_total{mode="idle"}[1m])) * 100 < 60
      )
    for: 1m
    annotations:
      summary: "High usage on {{ $labels.instance }}"
      description: "{{ $labels.instance }} has an average CPU idle of less than 60% (current value: {{ $value }}%)"

  # Alert for any instance that has average CPU idle > 75% (25% usage) only if instance count is greater than min_cluster_size
  - alert: LowUsage
    expr: |
      (instance_count > min_cluster_size)
      and
      (
        avg(irate(node_cpu_seconds_total{mode="idle"}[1m])) * 100 > 75
      )
    for: 1m
    annotations:
      summary: "Low usage on {{ $labels.instance }}"
      description: "{{ $labels.instance }} has an average CPU idle of more than 75% (current value: {{ $value }}%)"
```

{% endcode %}

4.2 กรณีใช้กับ **Nginx Exporter**

{% code overflow="wrap" %}

```yaml
groups:
- name: HTTPREQUEST
  rules:
  # Record the minimum cluster size (adjust as needed)
  - record: min_cluster_size
    expr: 1  # Adjust this value to your actual minimum cluster size
  
  # Record the count of instances (using a different metric if available)
  - record: instance_count
    expr: count(count by (instance) (nginx_http_requests_total))  # Adjust based on available metrics

  # Alert to scale out if current HTTP requests are greater than 200 and instance count is >= min_cluster_size
  - alert: HighUsage
    expr: |
      (instance_count >= min_cluster_size)
      and
      (sum(rate(nginx_http_requests_total[1m]))) > 200
    for: 1m
    annotations:
      summary: "Scale out: High number of HTTP requests"
      description: "Number of HTTP requests exceeds 200 and instance count is greater than or equal to minimum cluster size."

  # Alert to scale in if current HTTP requests are less than 200 and instance count is > min_cluster_size
  - alert: LowUsage
    expr: |
      (instance_count > min_cluster_size)
      and
      (sum(rate(nginx_http_requests_total[1m]))) < 200
    for: 1m
    annotations:
      summary: "Scale in: Low number of HTTP requests"
      description: "Number of HTTP requests is less than 200 and instance count is greater than minimum cluster size."
```

{% endcode %}

4.3 กรณีใช้กับ **Window\_Exporter(for IIS)**

{% code overflow="wrap" %}

```yaml
groups:
- name: WINDOWREQUEST
  rules:
  # Record the minimum cluster size (adjust as needed)
  - record: min_cluster_size
    expr: 1  # Adjust this value to your actual minimum cluster size
  
  # Record the count of instances (using a different metric if available)
  - record: instance_count
    expr: count(count by (instance) (windows_iis_requests_total))  # Adjust based on available metrics

  # Alert to scale out if current HTTP requests are greater than 200 and instance count is >= min_cluster_size
  - alert: HighUsage
    expr: |
      (instance_count >= min_cluster_size)
      and
      (sum(rate(windows_iis_requests_total[1m]))) > 200
    for: 1m
    annotations:
      summary: "Scale out: High number of HTTP requests"
      description: "Number of HTTP requests exceeds 200 and instance count is greater than or equal to minimum cluster size."

  # Alert to scale in if current HTTP requests are less than 200 and instance count is > min_cluster_size
  - alert: LowUsage
    expr: |
      (instance_count > min_cluster_size)
      and
      (sum(rate(windows_iis_requests_total[1m]))) < 200
    for: 1m
    annotations:
      summary: "Scale in: Low number of HTTP requests"
      description: "Number of HTTP requests is less than 200 and instance count is greater than minimum cluster size."
```

{% endcode %}

5. Restart prometheus service

```bash
systemctl restart prometheus
```

6. สามารถตรวจสอบได้ที่หน้าเว็บ UI ของ Prometheus http\://{FloatingIP}:9090

### Config Alertmanager

1. ติดตั้ง [Alertmanager](broken://pages/G2hgfQv5kQc7Uy8buCLM) ภายใน instance เดียวกันกับ Prometheus (ควรติดตั้งเวอร์ชั่นล่าสุด)
2. **แก้ไขไฟล์ /etc/alertmanager/alertmanager.yml** เพื่อทำการเเก้ไขในส่วนของ  url: **เป็น alarm\_url** ที่ copy ไว้ตอนสร้าง senlin receiver

{% code overflow="wrap" %}

```yaml
global:
  resolve_timeout: 5m

route:
  group_by: ["alertname"]
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 5m
  receiver: default

  routes:
    - match:
        alertname: HighUsage
      receiver: "scale-out-senlin"
    - match:
        alertname: LowUsage
      receiver: "scale-in-senlin"

receivers:
  - name: "scale-out-senlin"
    webhook_configs:
      - url: "Link จาก alarm_url ที่ copy ไว้ตอนสร้าง Receiver"
        send_resolved: false

  - name: "scale-in-senlin"
    webhook_configs:
      - url: "Link จาก alarm_url ที่ copy ไว้ตอนสร้าง Receiver"
        send_resolved: false

  - name: "default"   #receiver ตัวนี้ใช้ลิงค์ dummy เพื่อให้มี default receivers ไม่มีผลในส่วนของการ autoscaling
    webhook_configs:
      - url: "http://localhost:9999/dummy"  
        send_resolved: false

inhibit_rules:
  - source_match:
      severity: "critical"
    target_match:
      severity: "warning"
    equal: ["alertname", "dev", "instance"]
```

{% endcode %}

**limitation : ควรปรับให้ repeat\_interval เป็น 5-10 นาที (5-10m) เนื่องจาก cluster สามารถ scale in ได้รอบละ 1 node ต่อ 1 trigger หากปรับนานกว่านั้นจะทำให้การ scale in ช้าลง**

3. Restart alertmanager service

```bash
systemctl restart alertmanager
```

4. สามารถตรวจสอบได้จากหน้า web UI ของ Alertmanager &#x20;

http\://{FloatingIP}:9093/#/alerts

### Config Load-Balancing Policy

1. ภายใต้โฟลเดอร์เดียวกันกับที่สร้าง Cluster Profile ให้สร้างไฟล์ ชื่อ **lb\_policy.yaml**

```bash
touch lb_policy.yaml
```

<figure><img src="/files/3w5lke9jgJPZfMywGGFG" alt=""><figcaption></figcaption></figure>

2. เเก้ไขไฟล์ **lb\_policy.yaml** ดังนี้ ซึ่งมี 2 กรณี

2.1 กรณี ใช้ lb\_policy ในการสร้าง loadbalancer ให้อัตโนมัติหลังผูกกับ cluster

```yaml
type: senlin.policy.loadbalance
version: 1.0
properties:
  availability_zone: NCP-BKK
  flavor_id: [loadbalancer flavor_id]
  vip:
    subnet: [subnet_id]  
    protocol: TCP
    protocol_port: 80
  health_monitor:
    type: TCP  # Options can be PING, TCP, HTTP, HTTPS
    delay: 5
    timeout: 3
    max_retries: 3
  pool:
    subnet: [subnet_id]  #ใช้ subnet เดียวกันกับ vip
    lb_method: ROUND_ROBIN
    session_persistence: {}
    protocol: TCP
    protocol_port: 80
```

2.2 กรณีที่ผู้ใช้สร้าง loadbalancer ไว้เองเเล้วไปผูกกับ cluster ทีหลัง โดยค่าต่างๆที่ใช้ในเเต่ละ argument ควรจะตรงกับค่าของ loadbalancer ที่ต้องการผูก

```yaml
type: senlin.policy.loadbalance
version: 1.2
properties:
  loadbalancer: [loadbalancer_id]
  vip:
    address: 192.168.0.5  
    subnet: [subnet_id]
    protocol: TCP
    protocol_port: 80
  health_monitor:
    id: [health_monitor_id]
    type: TCP  # Options can be PING, TCP, HTTP, HTTPS
    delay: 5
    timeout: 3
    max_retries: 3
  pool:
    id: [pool_id]
    subnet: [subnet_id]
    lb_method: ROUND_ROBIN
    session_persistence: {}
    protocol: TCP
    protocol_port: 80
```

3. จากนั้นใช้ command สร้าง policy จากไฟล์ **lb\_policy.yaml**

```bash
openstack cluster policy create --spec-file lb_policy.yaml lb_policy
```

<figure><img src="/files/t5kz5PnLcfRbXpB8JsOn" alt=""><figcaption></figcaption></figure>

4. นำ policy ที่สร้างมาผูกกับ cluster ที่สร้างไว้

```bash
openstack cluster policy attach --policy <POLICY_ID> <CLUSTER_ID>
```
