k8s

Kubernetes + UIPath - 3

k8s에서 uipath linux robot HPA 적용

by HOON


1

Last updated on April 1, 2025, 1:20 p.m.


random_image

안녕하세요, 오늘은 저번 포스트에 이어서 uipath 로봇이 hpa 적용 되는 부분을 확인해보겠습니다.

우선 기대하는 부분은 저희가 저번 포스트에서는 단순하게 로봇 한대로 Hi “yyyy-MM-dd hh:mm:ss” 형식을 출력하는걸로 끝냈습니다.
이번에는 Queue를 생성해서 큐의 개수가 여러개일경우, 자동으로 로봇이 생성되어 HPA를 유지하는 형식으로 진행해보겠습니다.


UIPath Orchestrator

이번에는 UIPath에서 Queue를 하나 생성하고, 19개의 트랜잭션을 업로드하여 Orchestrator에서 확인 하겠습니다.
Queue를 생성하고 트랜잭션을 생성하는 부분은 이 부분에서 중요하지않기때문에 따로 설명드리진 않겠습니다.
저는 저번 포스트에서 진행한 VirtualBox 테스트 환경이 있기때문에 바로 큐를 적재할게요.

그림과 같이 backtestQ 라는 이름의 Queue에 숫자 1~19 라는 이름의 reference 트랜잭션을 적재했습니다.
저희는 api를 통해서 데이터를 가져오기위해서 실제로 swagger에 접속해서 큐의 정보를 가져오겠습니다.
아래 경로로 이동하면 각자의 Swagger로 이동 할 수 있습니다.

(https://cloud.uipath.com/[OrcheURL]/[Tenant]/orchestrator_/swagger/index.html#/)

이동 후 아래 그림처럼 Get QueueItem 섹션을 볼게요

현재 저희는 큐가 테스트용 한개밖에 없기때문에 아무런 파라미터를 넣지않고, 그냥 실행해볼게요
아래와 같이 저희가 작성한 큐에 대한 정보가 나오는걸 볼 수 있습니다.
특히 Reference의 값이 19로 정확히 들어와있네요.

"value": [
    {
      "OrganizationUnitFullyQualifiedName": "test",
      "AncestorUniqueKey": null,
      "ManualAncestorUniqueKey": null,
      "CreatorJobKey": null,
      "ExecutorJobKey": null,
      "RobotId": null,
      "ParentOperationId": null,
      "OperationId": null,
      "QueueDefinitionId": 1054257,

      "Reference": "19",
      "ProcessingExceptionType": null,

      "CreationTime": "2025-03-27T03:11:55.037Z",
      "Progress": null,
      "RowVersion": "AAAAAFnc4ao=",
      "OrganizationUnitId": 6534338,
      "Id": 589010679,
      "ProcessingException": null,
      "Error": null,
      "SpecificContent": {},
      "Output": null,
      "Analytics": null
    },

특히 curl로 요청한 내역을 보면 아래 토큰값이 나와있습니다. 이 토큰값도 활용해야하니 잘 기억해두세요.

curl -X 'GET' \
  'https://cloud.uipath.com/<OrcheURL>/<Tenant>/orchestrator_/odata/QueueItems' \
  -H 'accept: application/json' \
  -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZC...'

Prometheus, Grafana, k8s

이제 제일 중요한 부분입니다.
저희는 큐의 개수를 기준으로 hpa를 적용 할 예정이기 떄문에, k8s의 hpa가 큐의 개수 메트릭을 알아야합니다.
우선 k8s 클러스터 내부에 Prometheus를 설치해주세요. 저는 helm Operator를 사용해서 설치했습니다.

추가로 Prometheus-Adapter도 설치해야하는데 이부분은 조금 이따 설명드리겠습니다.

Prometheus를 설치하면 Grafana까지 자동으로 설치될거에요. (bitnami/prometheus를 사용했습니다.)
처음 프로메테우스를 설치했다면, 오퍼레이터에서 자동으로 제공되는 cpu, mem, k8s 클러스터 내부 정보를 알 수 있을거에요. 하지만 저희가 가져오고자 하는 uipath queue의 트랜잭션 개수는 못가져오죠. 따라서 Custom Metric을 작성해야합니다.

1. 메트릭 코드 작성

저희는 fastapi를 사용해서 uipath의 큐 데이터를 가져오는 코드를 작성해서 docker hub에 이미지로 올리겠습니다. 그럼 그 이미지를 k8s에서 받아서 pod의 형태로 유지하고있으면 되겠죠??
fastapi 코드는 아래와 같습니다.

#uipath_queue_exporter_fastapi.py
from fastapi import FastAPI, Response
import requests
import os

app = FastAPI()

@app.get("/metrics")
def read_metrics():
    """
    1) Orchestrator API를 호출하여, 특정 Queue에서
       Status='New'인 아이템 개수를 구한다.
    2) Prometheus 텍스트 포맷으로 변환하여 반환한다.
    """
    orchestrator_url = os.environ.get("UIPATH_ORCHESTRATOR_URL")
    token = os.environ.get("UIPATH_TOKEN")
    queue_id = os.environ.get("UIPATH_QUEUE_ID")
    folder_id = os.environ.get("UIPATH_FOLDER_ID")

    if not all([orchestrator_url, token, queue_id, folder_id]):
        return Response(
            content="Missing environment variables",
            status_code=500
        )

    headers = {
        "Authorization": f"Bearer {token}",
        "X-UIPATH-OrganizationUnitId": folder_id
    }
    url = f"{orchestrator_url}/odata/QueueItems?$filter=QueueDefinitionId eq 1054257 and Status eq 'New'"

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status()
    except Exception as e:
        return Response(
            content=f"Error calling Orchestrator API: {str(e)}",
            status_code=500
        )

    data = response.json()
    print(data)
    new_count = len(data.get('value', []))

    # Prometheus 텍스트 포맷 생성
    metric_text = (
        "# HELP uipath_queue_new_count Number of QueueItems with Status=New.\n"
        "# TYPE uipath_queue_new_count gauge\n"
        f"uipath_queue_new_count {new_count}\n"
    )

    return Response(content=metric_text, media_type="text/plain")

코드를 보면 필요한 값이 4개가 있어요. 이는 아래에서 설명 드리겠습니다. 이 변수명 그대로 k8s 클러스터에서 Configmap을 구현할거라 이름은 통일 시키면 좋습니다!
이 코드를 build하고 docker hub에 이미지로 올려주세요.

2. k8s resource 생성

자, 이제 필요한 리소스를 생성해볼게요, 우선 deployment, service는 당연히 필요하겠죠??
그리고 4개 정보에 대해서 configmap으로 작성해야할거같아요.
(테스트 단계라 token을 Configmap으로 관리했습니다.)

k8s-master@k8s-master:~/uipath-robot$ cat uipath-queue-exporter-dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: uipath-queue-exporter-dep
  labels:
    app: uipath-queue-exporter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: uipath-queue-exporter
  template:
    metadata:
      labels:
        app: uipath-queue-exporter
    spec:
      containers:
      - name: uipath-queue-exporter
        image: besthong2/uipath-queue-exporter1:latest
        env:
        - name: UIPATH_ORCHESTRATOR_URL
          valueFrom:
            configMapKeyRef:
              name: uipath-config
              key: UIPATH_ORCHESTRATOR_URL
        - name: UIPATH_TOKEN
          valueFrom:
            configMapKeyRef:
              name: uipath-config
              key: UIPATH_TOKEN
        - name: UIPATH_QUEUE_ID
          valueFrom:
            configMapKeyRef:
              name: uipath-config
              key: UIPATH_QUEUE_ID
        - name: UIPATH_FOLDER_ID
          valueFrom:
            configMapKeyRef:
              name: uipath-config
              key: UIPATH_FOLDER_ID
        ports:
        - containerPort: 8000
          name: metrics
---
apiVersion: v1
kind: Service
metadata:
  name: uipath-queue-exporter-svc
  labels:
    app: uipath-queue-exporter
spec:
  selector:
    app: uipath-queue-exporter
  ports:
  - port: 8000
    targetPort: 8000
    protocol: TCP
    name: metrics
  type: ClusterIP

k8s-master@k8s-master:~/uipath-robot$ cat uipath-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: uipath-config
  namespace: uipath-ns
  labels:
    app: uipath-robots
data:
  LICENSE_AGREEMENT: "accept"
  MACHINE_KEY: "<ROBOT MACHINE KEY>"
  UIPATH_ORCHESTRATOR_URL: "https://cloud.uipath.com/<OrcheURL>/<Tenant>/orchestrator_" // Orchestrator 주소
  UIPATH_TOKEN: "<TOKEN Value> // 토큰 아이디 

  UIPATH_QUEUE_ID: "<QUEUE_ID>" //각자의 큐 아이디
  UIPATH_FOLDER_ID: "<FOLDER_ID>" // 폴더 아이디

저희가 생성한 큐 개수 api를 가져오려면 위 swagger에서 확인한 내용들이 필요해요.
가져와야하는 값은 다음과 같습니다.

  • UIPATH_ORCHESTRATOR_URL : 오케스트레이터 URL
  • UIPATH_QUEUE_ID : GET QueueItems에서의 "Id"
  • UIPATH_FOLDER_ID : GET QueueItems에서의 “OrganizationUnitId”
  • UIPATH_TOKEN: Curl 명령어에서 볼 수 있는 JWT 토큰 value

우선 이렇게 deployment, service, configmap을 생성했다면 kubectl apply -f 으로 적용해주세요.
자, 그럼 실제로 pod가 생성되면 해당 파드에 접속해서 데이터를 잘 가져오나 확인해볼게요.

pod가 아래처럼 생성되었습니다.

실제로 pod에 접속해서 /metrics를 호출해볼까요?

k8s-master@k8s-master:~/uipath-robot$ k exec -it uipath-queue-exporter-dep-5fd5fff7db-7whtw -- /bin/sh
# curl -v http://localhost:8000/metrics
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /metrics HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 200 OK
< date: Tue, 01 Apr 2025 03:24:07 GMT
< server: uvicorn
< content-length: 130
< content-type: text/plain; charset=utf-8
<
# HELP uipath_queue_new_count Number of QueueItems with Status=New.
# TYPE uipath_queue_new_count gauge
uipath_queue_new_count 19
* Connection #0 to host localhost left intact

정상적으로 19개를 가져옵니다.

3. Custom Metric 테스트

그럼 이제 Custom Metric을 생성해서 k8s의 클러스터가 이를 캐치 할 수 있게 하면 될 것 같아요.
그 전에 필요한게 두 가지 있습니다.
ServiceMonitor와 Adapter인데요. 둘은 각각 역할이 다릅니다.
ServiceMonitor은 어떤 서비스의 메트릭을 찾아서 수집 할 지 정하는 역할이고,
Adapter는 Prometheus에서 수집한 메트릭을 Kubernetes 내부에서 활용 할 수 있게 하는 역할입니다.

그럼 각각 메니페스트 파일을 작성하고 적용해볼게요.

k8s-master@k8s-master:~/monitoring-yaml$ cat prometheus-servicemonitor.yaml adapter-values.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: uipath-queue-exporter-sm
  labels:
    release: my-kube-prom-stack   # Prometheus Operator 설치 시 사용한 release 이름과 일치시켜주세요.
spec:
  namespaceSelector:
    matchNames:
      - uipath-ns #저는 로봇과 exporter는 둘 다 uipath-ns 네임스페이스에 존재합니다.
  selector:
    matchLabels:
      app: uipath-queue-exporter  # Exporter Service의 라벨과 일치해야 함
  endpoints:
  - port: metrics                 # Service에서 노출한 포트 이름 (예: metrics)
    path: /metrics
    interval: 15s

rules:
  external:
    - seriesQuery: 'uipath_queue_new_count{namespace!="",pod!=""}'
      resources:
        overrides:
          namespace:
            resource: "namespace"
          pod:
            resource: "pod"
      name:
        as: "uipath_queue_new_count"
      metricsQuery: 'sum(<<.Series>>) by (namespace)'

prometheus:
  url: http://my-kube-prom-stack-kube-pr-prometheus.monitoring.svc.cluster.local
  #특히 operator로 adapter를 설치하면 기본 dns가 default로 잡히기때문에 메트릭이 인식이 안됩니다. 
  #따라서 꼭 FQDN을 확인해서 작성해주세요!!!

이 리소스 두개를 적용하고 나면 아래처럼 메트릭이 제대로 잡혀있어야 합니다.

k8s-master@k8s-master:~/monitoring-yaml$ kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1" | jq
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "external.metrics.k8s.io/v1beta1",
  "resources": [
    {
      "name": "uipath_queue_new_count",
      "singularName": "",
      "namespaced": true,
      "kind": "ExternalMetricValueList",
      "verbs": [
        "get"
      ]
    }
  ]
}

실제로 Grafana 에서도 19가 잘 보이는지 확인해볼까요?

19개로 잘 나오네요!
그럼 uipath api를 가져와서 k8s 클러스터 내부에서 Prometheus Custom Metric이 제대로 뿌려지는것까지 확인이 되었습니다.
이제 마지막으로 HPA를 작성해서 자동으로 로봇이 늘어나기만 하면 성공입니다.

4. 고가용성 테스트

여기까지 됐다면, hpa는 상대적으로 간단해요.

k8s-master@k8s-master:~/uipath-robot$ cat uipath-hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: uipath-robot-hpa
  namespace: uipath-ns
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: uipath-robots # 늘리고자 하는 로봇 deployment 이름
  minReplicas: 1 # 최소 1개는 유지
  maxReplicas: 5 # 최대 5개까지 확장
  metrics:
    - type: External
      external:
        metric:
          name: uipath_queue_new_count
          selector:
            matchLabels:
              namespace: uipath-ns
        target:
          type: Value
          value: "5" # 5개를 기준으로 확장 여부 설정

이렇게 작성하고 생성해주세요. 그리고 저희가 설정한 metric 가져오는 시간 15s를 기다리면?
아래와같이 hpa가 적용되어 robot이 늘어난걸 볼 수 있습니다.

k8s-master@k8s-master:~/uipath-robot$ k get pods
NAME                                         READY   STATUS    RESTARTS       AGE
uipath-queue-exporter-dep-5fd5fff7db-7whtw   1/1     Running   0              50m
uipath-robots-8575f498-8dlz4                 1/1     Running   1 (131m ago)   3d18h
uipath-robots-8575f498-8jc2h                 1/1     Running   0              17s
uipath-robots-8575f498-dtczj                 1/1     Running   0              49m
uipath-robots-8575f498-h95n5                 1/1     Running   0              49m
uipath-robots-8575f498-hls84                 1/1     Running   0              49m

k8s-master@k8s-master:~/monitoring-yaml$ k get hpa
NAME               REFERENCE                  TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
uipath-robot-hpa   Deployment/uipath-robots   19/5      1         5         5          4d21h

실제로 UIPath Orchestrator 에서도 늘어났을까요??

자, 이렇게 k8s 클러스터에서 linux robot을 실행 및 자동 확장 하는것까지 확인해봤습니다.
이번 포스트는 여기서 마치겠습니다.

감사합니다.

×
linux k8s UIPath


Leave a Comment: