cloud-barista / cb-spider

CB-Spider offers a unified view and interface for multi-cloud management.
https://github.com/cloud-barista/cb-spider/wiki
Apache License 2.0
31 stars 47 forks source link

[GCP:PMKS] after creating a cluster, I got an error with 'SystemId: does not exist' when getting it #1252

Closed sykim-etri closed 2 weeks ago

sykim-etri commented 1 month ago

GCP의 asia-east1를 대상으로 CB-SP의 adminweb을 통해 클러스터 생성 후 ListCluster()/GetCluster 수행시 아래와 같은 에러가 발생하면서 조회가 되지 않습니다. GCP 콘솔을 통해 확인하면 해당 클러스터는 생성된 것으로 확인됩니다.

[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 CommonHandler.go:87, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.GetCallLogScheme() - Call GCP GetCluster()
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:786, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.mappingClusterInfo() - metaSecurityGroupTags : [sg01-cqgqjpkcpuq811oo1ea0]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:897, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.mappingClusterInfo() - Cluster status : PROVISIONINGCreating
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:1104, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertCluster() - nodeGroupList [{{default-pool default-pool} {COS_CONTAINERD COS_CONTAINERD} e2-medium pd-balanced 100 {NameId SystemId} false 3 0 0 Active [] [{InstanceGroup_0 gke-spider-cluster-01-cq-default-pool-a898cdf7-grp}]}]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:1121, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - convertNodeGroup {{default-pool default-pool} {COS_CONTAINERD COS_CONTAINERD} e2-medium pd-balanced 100 {NameId SystemId} false 3 0 0 Active [] [{InstanceGroup_0 gke-spider-cluster-01-cq-default-pool-a898cdf7-grp}]}
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:1125, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - keyValue {InstanceGroup_0 gke-spider-cluster-01-cq-default-pool-a898cdf7-grp}
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:1127, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - HasPrefix
[CB-SPIDER].[INFO]: 2024-07-25 10:45:22 ClusterHandler.go:1134, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - instanceList [gke-spider-cluster-01-cq-default-pool-a898cdf7-141t gke-spider-cluster-01-cq-default-pool-a898cdf7-htld gke-spider-cluster-01-cq-default-pool-a898cdf7-w44h]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:23 ClusterHandler.go:1145, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - instanceInfo.Labels map[cb-spider-pmks-securitygroup-0:sg01-cqgqjpkcpuq811oo1ea0 goog-gke-node: goog-k8s-cluster-location:asia-east1-a goog-k8s-cluster-name:spider-cluster-01-cqgqq74cpuq811oo1ebg goog-k8s-node-pool-name:default-pool]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:23 ClusterHandler.go:1145, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - instanceInfo.Labels map[cb-spider-pmks-securitygroup-0:sg01-cqgqjpkcpuq811oo1ea0 goog-gke-node: goog-k8s-cluster-location:asia-east1-a goog-k8s-cluster-name:spider-cluster-01-cqgqq74cpuq811oo1ebg goog-k8s-node-pool-name:default-pool]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:23 ClusterHandler.go:1145, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - instanceInfo.Labels map[cb-spider-pmks-securitygroup-0:sg01-cqgqjpkcpuq811oo1ea0 goog-gke-node: goog-k8s-cluster-location:asia-east1-a goog-k8s-cluster-name:spider-cluster-01-cqgqq74cpuq811oo1ebg goog-k8s-node-pool-name:default-pool]
[CB-SPIDER].[INFO]: 2024-07-25 10:45:23 ClusterHandler.go:1155, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - nodeList [{gke-spider-cluster-01-cq-default-pool-a898cdf7-141t gke-spider-cluster-01-cq-default-pool-a898cdf7-141t} {gke-spider-cluster-01-cq-default-pool-a898cdf7-htld gke-spider-cluster-01-cq-default-pool-a898cdf7-htld} {gke-spider-cluster-01-cq-default-pool-a898cdf7-w44h gke-spider-cluster-01-cq-default-pool-a898cdf7-w44h}]
[CB-SPIDER].[DEBUG]: 2024-07-25 10:45:23 ClusterHandler.go:1158, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.convertNodeGroup() - {{default-pool default-pool} {COS_CONTAINERD COS_CONTAINERD} e2-medium pd-balanced 100 {NameId SystemId} false 3 0 0 Active [{gke-spider-cluster-01-cq-default-pool-a898cdf7-141t gke-spider-cluster-01-cq-default-pool-a898cdf7-141t} {gke-spider-cluster-01-cq-default-pool-a898cdf7-htld gke-spider-cluster-01-cq-default-pool-a898cdf7-htld} {gke-spider-cluster-01-cq-default-pool-a898cdf7-w44h gke-spider-cluster-01-cq-default-pool-a898cdf7-w44h}] [{InstanceGroup_0 gke-spider-cluster-01-cq-default-pool-a898cdf7-grp}]}
[CB-SPIDER].[ERROR]: 2024-07-25 10:45:23 ClusterManager.go:665, github.com/cloud-barista/cb-spider/api-runtime/common-runtime.setResourcesNameId() - gcp-asia-east1, SystemId: does not exist!
[CB-SPIDER].[ERROR]: 2024-07-25 10:45:23 ClusterManager.go:748, github.com/cloud-barista/cb-spider/api-runtime/common-runtime.ListCluster() - gcp-asia-east1, SystemId: does not exist!

생성 요청부터 에러 발생까지 전체 로그를 첨부하오니 검토 부탁드립니다. gcp-cluster-error.log

powerkimhub commented 1 month ago

@CliffSynn @hippo-an @MZC-CSC (@sykim-etri )


[최초 에러 발생 위치 및 원인]

[증상]

[제안]

[기타]

[CB-SPIDER].[ERROR]: 2024-07-25 10:41:18 CommonHandler.go:557, github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/drivers/gcp/resources.WaitContainerOperationFail() - Forcing termination of Wait because the status of resource [%!s(int=30)] has not completed within [%!d(string=operation-1721871647341-61072b75-a036-47c4-9e6c-bf9de203d40e)] seconds.
sykim-etri commented 1 month ago

[제안]

  • K8S Cluster의 경우 30초 Timeout은 너무 짧은 거 같습니다.
  • 생성 사양을 소/중/대 정도로 실제 소요 시간을 측정 후
  • 충분한 Timeout을 설정해시는 방법을 고려해주시기 바랍니다.

클러스터 생성에 많은 시간이 소요되므로, 비동기 호출만으로 처리가 가능한지도 검토해주시면 좋겠습니다. 일부 CSP의 ClusterHandler의 경우 CB-SP 엔진에서 요구하는 최소 정보만 채울때까지 기다리다가 리턴하기도 합니다. (ex. NHNCloud 드라이버에서는 자체적으로 생성되는 security group 생성까지만 기다리고 리턴)

sykim-etri commented 4 weeks ago

혹시 검토된 사항이 있다면 공유를 부탁드립니다.

hippo-an commented 3 weeks ago

@sykim-etri @powerkimhub

안녕하세요. 해당 이슈에 대해서 파악한 점 말씀드리겠습니다. 먼저 답변이 늦어져 죄송합니다.

1) 최초 에러 발생 위치라고 말씀하신 부분에서는 에러가 발생하지 않습니다.

hippo-an commented 3 weeks ago

2) 제공해주신 에러 로그를 바탕으로 전체 흐름을 따라 파악해 본 결과는 다음과 같습니다.

비동기 호출과 관련된 문제는 아닌것으로 확인했습니다. 문제는 cluster 에 생성된 node 의 keypair 의 시스템 아이디를 적절하게 넘겨주지 못해서 발생합니다.


2.1) 클러스터 생성 요청 시 발생하는 에러 로그에 대한 분석

gcp 의 cluster 생성 흐름은 다음과 같습니다.

  1. gcp cluster 생성 요청
  2. 생성 요청한 operation 을 통해 30 초 동안 해당 생성 요청 조회 (약 6번정도 조회) 후 문제 없을 경우 return nil
  3. clusterHandler 의 GetCluster() 함수를 호출하여 생성 클러스터 정보 조회 및 공통 인터페이스로 변환 후 리턴
    • 여기서 gcp 에 실제 요청을 바탕으로 리소스를 조회합니다.

제공해주신 에러 로그를 바탕으로 생성시 찍힌 에러 로그는 다음과 같이 설명 가능합니다.

결과적으로 생성 시점에서 발생하는 에러 로그는 2건이지만, 두건의 에러 로그 모두 cluster 생성 요청에 에러 응답을 반환하지 않습니다.

클러스터 생성 요청은 문제없이 실행됩니다.


2.2) 생성 직후 클러스터 리스트 조회에서 발생하는 에러에 대한 분석

아래 두 건의 로그는 클러스터 생성 요청 직후 클러스터 리스트 조회 요청에 포함된 로그로 총 한건의 에러 로그가 발생합니다.

이는 앞서 말씀드린 노드풀 생성 이전에 인스턴스 그룹으로 조회 요청시 생성되는 에러 로그입니다. 앞서 설명드린 바와 같이 common handler 레벨에서 발생하는 에러 로그만 남으며, 실제적으로 error 가 응답으로 반환되지 않습니다.

그리고 이렇게 ListCluster 요청이 성공하면 다음과 같이 리스트 화면이 보이게 됩니다.

스크린샷 2024-08-16 152450

노드 그룹이 비어 있는 상태로 클러스터 정보만 채워져 있는 상태입니다.


2.3) 노드 그룹이 생성된 후 클러스터 리스트 조회에서 발생하는 에러에 대한 분석

이후 시간이 흐르고 ListCluster() 함수를 호출하게 되면 error 를 반환하며, 기존에 조회되던 리스트도 더이상 조회가 불가능합니다.

위 에러는 호출 시간만 다를 뿐 동일한 위치에서 발생하는 에러입니다. 위 에러에 대한 원인은 다음과 같습니다.

ListCluster 는 GetCluster() 함수를 반복문으로 호출합니다. ClusterManager:718 이때 GetCluster() 함수로 반환된 cluster info 정보에는 cluster 에 대한 정보와 NodeGroupList 에 대한 정보를 반환합니다. ClusterManager:729 이후 동일한 반복문 내에서 setResourcesNameId() 함수를 호출합니다. ClusterManager:746

해당 함수에는 반환된 NodeGroupList 를 순회하는 반복문이 존재합니다. 여기서 KeyPairIID 의 매핑이 문제가 됩니다.

응답으로 넘어오는 cluster info 의 NodeGroupList 의 예시 응답은 다음과 같습니다. (로그로 찍힌 정보를 그대로 올린 것이라 json 형식이 이상한 부분은 신경쓰지 않으셔도 괜찮습니다.)

[
    {
        IId:{NameId:ng-01-cqvf0bt93pmufaik3rt0 SystemId:ng-01-cqvf0bt93pmufaik3rt0} 
        ImageIID:{NameId:COS_CONTAINERD SystemId:COS_CONTAINERD} 
        VMSpecName:e2-medium 
        RootDiskType:pd-balanced 
        RootDiskSize:10 
        KeyPairIID:{NameId:NameId SystemId:SystemId}  #<-- 이부분의 정보가 이상합니다.
        OnAutoScaling:true 
        DesiredNodeSize:1 
        MinNodeSize:1 
        MaxNodeSize:2 
        Status:Active 
        Nodes:[{NameId:gke-spider-cluster-0-ng-01-cqvf0bt93p-570a55fa-whbt SystemId:gke-spider-cluster-0-ng-01-cqvf0bt93p-570a55fa-whbt}]
        KeyValueList:[{Key:InstanceGroup_0 Value:gke-spider-cluster-0-ng-01-cqvf0bt93p-570a55fa-grp}]
    }
]

KeyPairIID 에 대해서 왜 저렇게 매핑을 하는지 추적했습니다.

GCP Cluster Handler 에서 node group list 의 키페어를 매핑하는 방식은 노드 그룹에 설정된 인스턴스의 반복문을 돌며 마지막 인스턴스에 설정된 keypair 와 관련된 label 을 매핑하여 제공합니다.

func convertNodeGroup(client *compute.Service, credential idrv.CredentialInfo, region idrv.RegionInfo, orgNodeGroupList []irs.NodeGroupInfo) ([]irs.NodeGroupInfo, error) {
    nodeGroupList := []irs.NodeGroupInfo{}
    for _, nodeGroupInfo := range orgNodeGroupList {
        cblogger.Info("convertNodeGroup ", nodeGroupInfo)
        //cblogger.Debug(nodeGroupInfo)
        keyValueList := nodeGroupInfo.KeyValueList
        for _, keyValue := range keyValueList {
            cblogger.Info("keyValue ", keyValue)
            if strings.HasPrefix(keyValue.Key, GCP_PMKS_INSTANCEGROUP_KEY) {
                cblogger.Info("HasPrefix ")
                nodeList := []irs.IID{}
                instanceGroupValue := keyValue.Value
                instanceList, err := GetInstancesOfInstanceGroup(client, credential, region, instanceGroupValue)
                if err != nil {
                    return nodeGroupList, err
                }
                cblogger.Info("instanceList ", instanceList)
                for _, instance := range instanceList {
                    instanceInfo, err := GetInstance(client, credential, region, instance)
                    if err != nil {
                        return nodeGroupList, err
                    }
                    //cblogger.Debug(instanceInfo)
                    // nodeGroup의 Instance ID
                    nodeIID := irs.IID{NameId: instanceInfo.Name, SystemId: instanceInfo.Name}
                    nodeList = append(nodeList, nodeIID)

                    cblogger.Info("instanceInfo.Labels ", instanceInfo.Labels)
                    nodeGroupInfo.KeyPairIID = irs.IID{NameId: "NameId", SystemId: "SystemId"} // empty면 오류나므로 기본값으로 설정후 update하도록  <--- 해당 부분
                    if instanceInfo.Labels != nil {                                                                                             <--- 해당 부분
                        keyPairVal, exists := instanceInfo.Labels[GCP_PMKS_KEYPAIR_KEY]                                                         <--- 해당 부분
                        if exists {                                                                                                             <--- 해당 부분
                            cblogger.Info("nodeGroup set keypair ", keyPairVal)                                                                 <--- 해당 부분
                            nodeGroupInfo.KeyPairIID = irs.IID{NameId: keyPairVal, SystemId: keyPairVal}                                        <--- 해당 부분
                        }
                    }
                }
                cblogger.Info("nodeList ", nodeList)
                nodeGroupInfo.Nodes = nodeList
                //cblogger.Info("nodeGroupInfo ", nodeGroupInfo)
                cblogger.Debug(nodeGroupInfo)
            }
        }
        nodeGroupList = append(nodeGroupList, nodeGroupInfo)
    }
    return nodeGroupList, nil
}

인스턴스에 등록된 label 을 조회하는 과정에서 keypair 와 관련된 label 이 적절하게 등록되어 있지 않음을 확인했습니다.

즉, cluster 생성시 함께 생성되는 인스턴스의 라벨을 통해선 keypair 와 관련된 정보를 조회할 수 없기 때문에 KeyPairIID:{NameId:NameId SystemId:SystemId} 와 같은 형태로 NodeGroupList 의 응답이 반환된 것입니다. setResourcesNameId() 함수에서 해당 id 로 keypair 에 대한 정보를 조회하는 과정에서 에러가 발생해, 리스트 조회 시 에러가 발생하여 클러스터 리스트가 보이지 않는 문제가 발생하는 것입니다.

참고로, cluster 생성시에도 동일한 함수가 호출되지만 비어있는 NodeGroupList 때문에 문제가 되는 코드를 수행하지 않고 넘어가게 됩니다. ClusterManager:558

hippo-an commented 3 weeks ago

3) Node Group 에 대한 convert 함수에서 왜 해당 방식을 사용해서 mapping 을 했을까

convert 하는 로직에서 가장 마지막 인스턴스의 label 을 사용하여 매핑하는 방식을 왜 사용했을까에 대한 이유는 cluster 생성에서 추측해 볼 수 있습니다.

gcp cluster 생성 로직에서는 request 의 NodeGroupList 에 따라 node group 을 생성하는 방식이 다릅니다.

클러스터 생성 요청에 NodeGroupList 를 넘기지 않는 경우

이 경우 gcp 에 클러스터 생성 요청을 보내면 default-pool 이라는 노드 그룹을 생성합니다. 해당 노드 그룹에 속한 각각의 인스턴스는 기본적으로 keypair 가 설정되지 않으며, GCP_PMKS_KEYPAIR_KEY 를 사용하는 label 을 추가하지 않습니다. 그렇기 때문에 keypair 에 대한 label 은 찾을 수 없습니다.

요청 예시)

curl -sX POST http://localhost:1024/spider/cluster -H 'Content-Type: application/json' -d \
'{
    "ConnectionName": "gcp-asia-east1",
    "ReqInfo": {
        "Name": "spider-cluster-02",
        "Version" : "",
        "VPCName" : "vpc-01",
        "SubnetNames" : ["subnet-01"],
        "SecurityGroupNames" : ["sg-01"],
        "NodeGroupList": []
    }
}'

클러스터 생성 요청에 NodeGroupList 를 넘기는 경우

이 경우 미리 생성한 keypair 이름을 사용하는 노드 그룹을 명시적으로 요청에 포함시킵니다. 이때 node config 에 label 을 등록하는 다음과 같은 로직이 들어갑니다.

    keyPair := map[string]string{}
    if reqNodeGroup.KeyPairIID.SystemId != "" {
        //keyPair[GCP_PMKS_KEYPAIR_KEY] = reqNodeGroup.KeyPairIID.NameId
        keyPair[GCP_PMKS_KEYPAIR_KEY] = reqNodeGroup.KeyPairIID.SystemId
        nodeConfig.Labels = keyPair
    }

각 노드에 설정한 라벨을 추가하는 Labels 를 활용하여 각 노드에 명시적으로 선언한 keypair 의 system id 라벨을 할당합니다.

요청 예시)

curl -sX POST http://localhost:1024/spider/cluster -H 'Content-Type: application/json' -d \
'{
    "ConnectionName": "gcp-asia-east1",
    "ReqInfo": {
        "Name": "spider-cluster-02",
        "Version" : "",
        "VPCName" : "vpc-01",
        "SubnetNames" : ["subnet-01"],
        "SecurityGroupNames" : ["sg-01"],
        "NodeGroupList": [
            {
                "Name": "ng-01",
                "VMSpecName": "e2-medium",
                "KeyPairName": "keypair-01",
                "OnAutoScaling": "true",
                "DesiredNodeSize": "1",
                "MinNodeSize": "1",
                "MaxNodeSize": "2",
                "RootDiskSize": "10"
            }
        ]
    }
}'

위 요청을 통해 생성한 노드풀이 가지는 메타데이터는 다음과 같습니다. 스크린샷 2024-08-16 170106


위 내용에 비추어 볼때 convertNodeGroup() 함수에서 의도한 바는 다음과 같이 유추할 수 있습니다.

클러스터 생성 요청에 NodeGroupList 에 넘어온 keypair 정보를 node 의 라벨로 등록한다. 조회 시 해당 라벨 정보를 읽어 keypair id 를 반환한다.

라벨을 설정하는 옵션은 생성된 노드에 적절하게 등록이 되는 것을 콘솔에서 확인했습니다. 다만, 해당 옵션은 노드에 부여하는 k8s 라벨이기 때문에 vm 의 label 에는 포함되어 있지 않습니다.

노드 라벨) 스크린샷 2024-08-16 171046

노드 가상 머신의 라벨) 스크린샷 2024-08-16 171106

cluster 생성 요청 시 node group list 를 추가하는 방식도 vm 에서 라벨을 읽어 keypair 정보를 매핑하기 때문에 적절한 keypair system id 를 찾지 못해 기존과 동일한 에러가 발생합니다.

hippo-an commented 3 weeks ago

@powerkimhub

GCP 클러스터 생성 시 1) 노드 그룹 없이 생성하는 경우, 2) 노드 그룹을 추가해서 생성하는 경우에 대해 처리가 달라져야 할 것 같습니다.

1) 노드 그룹 없이 생성하는 경우

2) 노드 그룹을 추가해서 생성하는 경우

powerkimhub commented 3 weeks ago



[확인 요청]

[그런 옵션이 없다면 제안 드립니다.]


※ @hippo-an, @sykim-etri : 제안 내용 검토 부탁 드립니다.

hippo-an commented 3 weeks ago

[확인 요청]

  • 관련된 GCP API에 default-pool 생성 없이 노드 그룹 없는 Cluster 생성이 가능한 옵션이 있는지 재확인
    • 없었을 것 같은데, 현재도 없는 지 재확인 부탁 드립니다.

현재 사용하는 cluster api v1 sdk ( 최신 정식 버전) 에서 GKE 의 Standard Cluster 를 생성하는 경우 default node pool 없이 클러스터를 생성하는 옵션은 찾지 못했습니다.

추가적으로 gcloud, gcp console, api reference 에서 default node pool 없이 생성하는 옵션을 확인했으나 찾지 못했습니다.

클러스터 생성 시 default node pool 을 사용하거나 사용자 지정 node pool 설정이 반드시 필요합니다.


gcp 의 경우 type 2 를 사용하는 것이 spider 에서 명확하게 자원을 관리할 수 있다고 생각합니다.


참고) 클러스터 생성 시 node pool 의 필수 여부와는 별개로 클러스터가 생성된 후 모든 노드 풀을 삭제할 수 있습니다.

powerkimhub commented 3 weeks ago

@hippo-an (@sykim-etri )



[Type-II로 마무리 부탁 드립니다.]