DevOps, 클라우드/Open Sources

[OpenStack] Nova-Compute IncompatibleObjectVersion 해결하기

Floodnut 2023. 3. 29. 19:38

개요

원래 1개의 데스크탑, 3개의 라즈베리파이에 우분투를 설치하고 개인 서버로 활용하고 있었다.

솔직히 데스크탑은 공부용, 개발서버용 정도로 쓰고 있었는데 라즈베리파이가 놀고 있는게 맘에 좀 걸렸다.

그러면서 또 여러 문제가 생겼는데 개인 서버가 있으니 클라우드를 활용하는 빈도가 현저히 적어졌다는 거다.

 

솔직히 돈도 돈 때문에 안쓴거긴 하지만...

그러다가 오픈스택을 함 올려볼까 싶었다.

그래서 고민 끝에 윈도우 데스크탑을 밀고 우분투 서버를 설치했다.

 

결과적으로 컴퓨팅 노드, 컨트롤 노드로 쓰일 데스크탑 서버 2대와 스토리지 노드로 쓰일 라즈베리파이 3대가 생겼다.

이걸 클러스터링해서 어떻게 오픈스택을 잘 올려보려고 했다.

 


문제 발생

설치 과정이나 개념 정리는 다음에 한번에 적어보기로 하고...

Keystone, Glance, Placement, Nova까지 잘 설치하고 Nova-compute를 준비할 차례가 됐다.

구성요소를 작성하고 설정을 적용하기 위해서 nova-compute를 재시작하는데...

 

openstack compute service list --service nova-compute

 

 

공식문서에 따르면 컨트롤 노드에서 컴퓨팅 노드에 설치한 Nova-compute 서비스를 찾아야 한다고 한다.

 

근데 컨트롤 노드에서 컴퓨팅 노드의 서비스를 찾지 못하고 있었다.

그래서 컴퓨팅 노드에서 nova-compute의 서비스 상태를 확인해보니 서비스를 실행하자마자 비활성화 상태로 죽어버리는 상황이었다.

 

/var/log/nova/nova-compute.log

공식문서에 따르면 위 경로에 로그를 저장한다고 해서 로그를 열어봤다.

2023-03-29 18:28:57.796 8600 ERROR oslo_service.service Traceback (most recent call last):
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_service/service.py", line 806, in run_service
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     service.start()
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/nova/service.py", line 162, in start
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     self.manager.init_host(self.service_ref)
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 1638, in init_host
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     evacuated_instances = self._destroy_evacuated_instances(
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/nova/compute/manager.py", line 805, in _destroy_evacuated_instances
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     evacuations = objects.MigrationList.get_by_filters(context,
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_versionedobjects/base.py", line 175, in wrapper
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     result = cls.indirection_api.object_class_action_versions(
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/nova/conductor/rpcapi.py", line 240, in object_class_action_versions
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     return cctxt.call(context, 'object_class_action_versions',
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_messaging/rpc/client.py", line 190, in call
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     result = self.transport._send(
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_messaging/transport.py", line 123, in _send
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     return self._driver.send(target, ctxt, message,
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_messaging/_drivers/amqpdriver.py", line 689, in send
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     return self._send(target, ctxt, message, wait_for_reply, timeout,
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service   File "/usr/lib/python3/dist-packages/oslo_messaging/_drivers/amqpdriver.py", line 681, in _send
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service     raise result
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service oslo_messaging.rpc.client.RemoteError: Remote error: IncompatibleObjectVersion Version 1.5 of MigrationList is not supported, supported version is 1.4
2023-03-29 18:28:57.796 8600 ERROR oslo_service.service ['Traceback (most recent call last):\n', 
'  File "/usr/lib/python3/dist-packages/oslo_messaging/rpc/server.py", line 165, in _process_incoming\n    res = self.dispatcher.dispatch(message)\n', 
'  File "/usr/lib/python3/dist-packages/oslo_messaging/rpc/dispatcher.py", line 276, in dispatch\n    return self._do_dispatch(endpoint, method, ctxt, args)\n', 
'  File "/usr/lib/python3/dist-packages/oslo_messaging/rpc/dispatcher.py", line 196, in _do_dispatch\n    result = func(ctxt, **new_args)\n', 
'  File "/usr/lib/python3/dist-packages/nova/conductor/manager.py", line 145, in object_class_action_versions\n    objclass = nova_object.NovaObject.obj_class_from_name(\n', 
'  File "/usr/lib/python3/dist-packages/oslo_versionedobjects/base.py", line 383, in obj_class_from_name\n    raise exception.IncompatibleObjectVersion(objname=objname,\n', 'oslo_versionedobjects.exception.IncompatibleObjectVersion: Version 1.5 of MigrationList is not supported, supported version is 1.4\n'].

 

로그를 열어보니 크리티컬한 오류는 아닌거 같았다.

대강 보면 MigrationList 라는 오브젝트의 버전이 현재 1.5가 설치되어 있고 1.4 버전이 지원된다는 것이었다.

 


해결 과정

첫 시도로 MigrationList가 파이썬에서 제공하는 패키지인지 살펴봤다.

pip list를 통해서 조회한 파이썬 의존성 패키지에서는 MigrationList를 찾을 수 없었다.

 

 

그래서 간단하게 코드를 살펴보기로 했다.

# oslo_versionedobjects/base.py
# 357라인부터

@classmethod
def obj_class_from_name(cls, objname, objver):
    """Returns a class from the registry based on a name and version."""
    if objname not in VersionedObjectRegistry.obj_classes():
        LOG.error('Unable to instantiate unregistered object type '
                  '%(objtype)s'), dict(objtype=objname)
        raise exception.UnsupportedObjectError(objtype=objname)

    # NOTE(comstud): If there's not an exact match, return the highest
    # compatible version. The objects stored in the class are sorted
    # such that highest version is first, so only set compatible_match
    # once below.
    compatible_match = None

    for objclass in VersionedObjectRegistry.obj_classes()[objname]:
        if objclass.VERSION == objver:
            return objclass
        if (not compatible_match and
                vutils.is_compatible(objver, objclass.VERSION)):
            compatible_match = objclass

    if compatible_match:
        return compatible_match

    # As mentioned above, latest version is always first in the list.
    latest_ver = VersionedObjectRegistry.obj_classes()[objname][0].VERSION
    raise exception.IncompatibleObjectVersion(objname=objname,   ##  <- 요기
                                              objver=objver,
                                              supported=latest_ver)
# oslo_versionedobjects/exception.py

class IncompatibleObjectVersion(VersionedObjectsException):
    msg_fmt = _('Version %(objver)s of %(objname)s is not supported, '
                'supported version is %(supported)s')

 

에러가 발생한 메서드 내부에서 objclassVERSION 프로퍼티나 compatible_match의 조건을 통과하지 못하면 IncompatibleObjectVersion 예외처리를 발생시킨다.

 

그런데 위의 코드에서 보면 오브젝트 클래스의 버전을 VersionedObjectRegistry에서 끌고온다.

물론 이 클래스를 직접 살펴봐도 큰 정보는 얻을 수 없었다.

 

 

어쨌건 oslo라는 레지스트리에서 관리하는 MigrationList라는 객체가 버전 불일치로 오류가 발생했다는 것까지는 알았다.

 

 

GitHub - openstack/nova: OpenStack Compute (Nova). Mirror of code maintained at opendev.org.

OpenStack Compute (Nova). Mirror of code maintained at opendev.org. - GitHub - openstack/nova: OpenStack Compute (Nova). Mirror of code maintained at opendev.org.

github.com

여기서부터 열심히 구글링을 하고 있었는데...

우연히 Nova의 객체 클래스의 코드를 보게 되었다.

...

@base.NovaObjectRegistry.register
class ImageMeta(base.NovaObject):
    # Version 1.0: Initial version
    # Version 1.1: updated ImageMetaProps
    # Version 1.2: ImageMetaProps version 1.2
    # Version 1.3: ImageMetaProps version 1.3
    # Version 1.4: ImageMetaProps version 1.4
    # Version 1.5: ImageMetaProps version 1.5
    # Version 1.6: ImageMetaProps version 1.6
    # Version 1.7: ImageMetaProps version 1.7
    # Version 1.8: ImageMetaProps version 1.8
    VERSION = '1.8'


...

VersionedObjectRegistry와 유사한 객체 레지스트리에서 객체 클래스의 버전을 위와 같이 관리하고 있었다.

 

여기서 힌트를 얻었다.

분명 MigrationList라는 객체 클래스가 의존성 패키지들 어딘가에 파이썬 코드로 존재할 것이고 그 코드에서 버전을 수정할 수 있다는 생각을 했다.

 

그런데 나는 olso 하위의 패키지에서 이 클래스를 찾고 있었다. 하지만 위 코드는 nova 패키지 하위에 존재한다.

nova/objects

nova 패키지 하위에는 objects를 모아놓은 디렉터리가 있었고 그 경로 아래에서 migrate, migration의 키워드를 가진 코드를 찾았다.

 

@base.NovaObjectRegistry.register
class MigrationList(base.ObjectListBase, base.NovaObject):
    # Version 1.0: Initial version
    #              Migration <= 1.1
    # Version 1.1: Added use_slave to get_unconfirmed_by_dest_compute
    # Version 1.2: Migration version 1.2
    # Version 1.3: Added a new function to get in progress migrations
    #              for an instance.
    # Version 1.4: Added sort_keys, sort_dirs, limit, marker kwargs to
    #              get_by_filters for migrations pagination support.
    # Version 1.5: Added a new function to get in progress migrations
    #              and error migrations for a given host + node.
    VERSION = '1.5' ## <--- 요기

    fields = {
        'objects': fields.ListOfObjectsField('Migration'),
        }

    ...

그리고 migration.py 내부에서 MigrationList 클래스를 찾았다!

 

클래스 내부엔 버전이 1.5로 명시되어있었다.

해당 값을 1.4로 변경하고나서...

 

 


결과

컨트롤 노드에서 컴퓨팅 노드를 찾을 수 있게 됐다!

 


결론

사실 근본적으로 적합한 버전을 설치했다면 이런 일이 없었을 것이다.

버전도 강제로 변경했기에 다른 오류가 생길 수도 있지 않을까 싶었다.

하지만 구글링에서도 명확한 해답을 찾지 못했다는데 코드 하나하나 찾아가보면서 직접 오류를 고쳐보니 좀 성취감이 있긴 하다.