CloudForet에서 기본 지원하지 않는 Samsung Cloud Platform(SCP)의 Virtual Server 정보를 수집하기 위해 Custom Provider, Secret Schema, Plugin, Service Account, Collector를 구성하고, SCP OpenAPI를 호출하는 Custom Inventory Collector를 제작하였다.
최종 목표는 SCP OpenAPI에서 조회한 VM 정보를 CloudForet의 inventory.CloudService 리소스로 변환하여 CloudForet 콘솔에서 SCP 서버 목록을 확인하는 것이다.
CloudForet에서 외부 CSP 리소스를 수집하려면 다음 구성요소가 필요하다.
Provider는 CloudForet에서 CSP 또는 외부 플랫폼을 구분하는 기준이다.
이번 실습에서는 scp Provider를 생성하여 Samsung Cloud Platform을 CloudForet 내 독립 Provider로 인식하게 했다.
핵심 값:
provider: scp
name: Samsung Cloud Platform
Provider가 없으면 Service Account, Secret, Collector가 어느 CSP에 속하는지 구분할 수 없다.
Secret Schema는 해당 Provider에 접속하기 위해 어떤 인증 정보가 필요한지 정의하는 구조다.
이번 SCP Collector에서는 SCP OpenAPI 호출을 위해 다음 값이 필요했다.
access_key
access_secret_key
project_id
client_type
language
resource_type
base_endpoint
region_code
ssl_verify
핵심은 access_key, access_secret_key, project_id가 필수 인증값이라는 점이다.
Secret Schema는 UI 입력 폼과 Secret 저장 구조의 기준이 된다.
Repository Plugin은 CloudForet가 실제로 실행할 Collector Plugin 이미지 정보를 정의한다.
이번 실습에서는 Docker Hub에 Custom Collector 이미지를 push하고 CloudForet Plugin으로 등록했다.
핵심 값:
plugin_id: scp-vm-collector
provider: scp
service_type: inventory.Collector
image: ypjs09/scp-vm-collector
registry_type: DOCKER_HUB
version: 0.1.9
중요한 점은 Docker Hub에 이미지를 push하는 것과 CloudForet Repository에 Plugin 버전이 등록되는 것은 별개라는 것이다.
CloudForet가 모르는 버전으로 Collector를 생성하면 다음과 같은 오류가 발생한다.
version does not exist
Service Account는 특정 Provider의 계정 또는 프로젝트 단위를 CloudForet에 등록하는 객체다.
이번 실습에서는 SCP 프로젝트를 하나의 Service Account로 등록했다.
핵심 값:
provider: scp
service_account_id: sa-d88be3973e9a
scope: PROJECT
project_id: project-2bcfd61ce098
Service Account는 “어떤 SCP 프로젝트를 수집할 것인가”를 나타낸다.
Secret은 Service Account에 연결되는 실제 인증 정보다.
이번 실습에서 중요한 트러블슈팅 포인트는 Service Account는 있었지만 Secret이 없어서 Collector JobTask가 생성되지 않았던 점이다.
즉, CloudForet 수집 흐름에서는 다음 연결이 반드시 필요하다.
Provider
→ Service Account
→ Secret
→ Collector
Secret이 없으면 Collector가 어떤 인증 정보로 수집해야 하는지 알 수 없다.
Collector는 특정 Plugin을 이용해서 실제 수집 작업을 수행하는 객체다.
이번 실습의 최종 Collector는 다음과 같다.
collector_id: collector-71452b876d46
name: SCP VM Collector 019
provider: scp
plugin_id: scp-vm-collector
version: 0.1.9
secret_filter:
service_accounts:
- sa-d88be3973e9a
Collector의 핵심은 plugin_info와 secret_filter다.
plugin_info는 어떤 Collector Plugin 이미지를 실행할지 결정하고, secret_filter는 어떤 Service Account/Secret을 대상으로 수집할지 결정한다.
이번 실습에서 성공한 전체 순서는 다음과 같다.
1. SCP Provider 생성
2. SCP Secret Schema 생성
3. Docker Hub에 Custom Collector 이미지 push
4. Repository Plugin 등록
5. SCP Service Account 생성
6. SCP Secret 생성
7. Collector 생성
8. Collect Data 실행
9. Job / JobTask 확인
10. CloudService 수집 결과 확인
이 순서 중 하나라도 빠지면 수집이 실패한다.
특히 중요한 순서는 다음이다.
Service Account 생성 후 반드시 Secret을 생성해야 함
Collector 생성 시 Secret Filter에 Service Account를 연결해야 함
Collector Plugin 버전은 CloudForet가 인식하는 버전이어야 함
CloudForet Inventory Collector는 단순히 데이터를 반환하는 것이 아니라, CloudForet가 이해할 수 있는 응답 구조로 반환해야 한다.
이번 실습에서 Collector가 반환해야 했던 핵심 구조는 다음이다.
{
"resource_type": "inventory.CloudService",
"resource": resource,
"match_rules": {
"1": ["reference.resource_id"]
}
}
CloudServiceType은 다음 기준으로 매칭했다.
{
"resource_type": "inventory.CloudServiceType",
"resource": resource,
"match_rules": {
"1": ["name", "provider", "group"]
}
}
핵심 포인트는 다음이다.
resource_type은 최상위에 있어야 함
resource 내부에만 resource_type이 있으면 인식하지 못함
match_rules가 없으면 ERROR_MATCH_RULE 발생
match_rules는 list가 아니라 dict 형태여야 함
원인:
SCP Service Account는 있었지만 연결된 Secret이 없었음
해결:
secret.Secret 생성
schema: scp_openapi
service_account_id: SCP Service Account ID
project_id: CloudForet Project ID
에러:
collector can not find resource_type: None
원인:
Collector 응답에서 resource_type이 최상위가 아니라 resource 내부에만 존재했음
해결:
to_primitive()에서 resource_type을 최상위 필드로 이동
에러:
No match rule
원인:
CloudForet가 리소스를 생성/업데이트할 기준을 찾지 못함
해결:
match_rules = {
"1": ["reference.resource_id"]
}
CloudServiceType의 경우:
match_rules = {
"1": ["name", "provider", "group"]
}
에러:
Failed to parse match_rules field
Struct must be in a dict
원인:
match_rules = [["reference.resource_id"]]
처럼 list 형태로 반환했기 때문이다.
해결:
match_rules = {
"1": ["reference.resource_id"]
}
처럼 dict 형태로 수정했다.
문제:
기존 Collector를 update해도 plugin_info.version이 0.1.8에서 0.1.9로 바뀌지 않음
원인:
inventory.Collector update API는 plugin_info 변경을 지원하지 않음
해결:
기존 Collector를 수정하는 대신 새 Collector를 생성
에러:
version:0.1.10 of scp-vm-collector does not exist
원인:
Docker Hub에는 0.1.10 이미지를 push했지만 CloudForet Repository Plugin 버전으로는 등록되지 않았음
해결:
CloudForet가 이미 알고 있는 0.1.9 태그를 재빌드/재푸시하여 사용
에러:
Channel is not ready
Server is unavailable
원인:
동적 Collector Pod가 Ready 되기 전에 Job이 수집을 시도하거나,
Service는 존재하지만 Endpoints가 비어 있었음
해결:
Service selector에 맞는 수동 Deployment를 생성하여 Endpoint를 강제로 유지
확인 포인트:
kubectl get endpoints -n spaceone-plugin scp-vm-collector-wjdnuksxfqujxcoe -o wide
정상 예시:
10.42.0.45:50051
이번 실습의 핵심은 Collector 코드보다 CloudForet 내부 수집 흐름을 이해한 것이다.
CloudForet Inventory 수집은 다음 순서로 동작한다.
Collector 실행
→ 연결된 Service Account 조회
→ 연결된 Secret 조회
→ Plugin Pod 실행
→ gRPC로 Collector Plugin 호출
→ collect() 결과 수신
→ resource_type 확인
→ match_rules 기준으로 기존 리소스 매칭
→ CloudService 생성 또는 업데이트
따라서 장애를 볼 때는 다음 순서로 확인해야 한다.
1. Collector가 올바른 plugin version을 보고 있는가
2. Service Account가 연결되어 있는가
3. Secret이 존재하는가
4. Plugin Pod가 뜨는가
5. Service Endpoint가 있는가
6. gRPC 연결이 되는가
7. collect 응답에 resource_type이 있는가
8. match_rules가 올바른 dict 구조인가
9. CloudService 필드 구조가 CloudForet 스키마와 맞는가
최종적으로 SCP OpenAPI를 통해 조회한 Virtual Server 정보를 CloudForet Inventory 리소스로 변환하여 수집에 성공했다.
수집 대상은 SCP Virtual Server이며, CloudForet에서는 다음 구조로 표현된다.
Provider: scp
Cloud Service Group: VirtualServer
Cloud Service Type: VM
Resource Type: inventory.CloudService
이번 작업을 통해 CloudForet에서 기본 지원하지 않는 CSP도 Custom Provider와 Custom Collector를 통해 확장할 수 있음을 검증했다.
CloudForet Custom Collector 개발의 핵심은 API 호출 자체보다, CloudForet Inventory Worker가 해석할 수 있는 resource_type, resource, match_rules 구조를 정확히 맞추는 것이다.