본문 바로가기
프로그래밍/기타

[기타] CloudEvents v1.0.2 번역 및 정리 - 이벤트 메시지 스키마 스펙

by 사바라다 2023. 4. 19.

안녕하세요. 사바라다입니다 !

클라우드가 보편화되고 서비스를 만들어내는게 쉬워지면서 여러개의 서버로 하나의 서비스를 이루는 MSA(MicroService Architecture)로 개발이 이루어지고 전환도 이루지고 있습니다. 이에 따라 기존의 모놀리식(monolithic) 방식과는 주요하게 고려해야할 부분이 부분이 다릅니다.

오늘 여러분들에게 소개드릴 내용은 CloudEvents Spec입니다. 하나의 서비스를 하나의 서버가 아닌 여러개의 서버로 제공한다는 것은 서버간의 Message 교환이 중요합니다. 따라서 Message의 Schema 또한 중요한데요. CloudEvents Spec은 이러한 스키마의 표준에 대해서 고민하고 논의하여 정의된 문서입니다. 오늘은 CloudEvents Spec의 내용을 요약해서 전달드리고자합니다.

참고로 기존 모놀리식과 MSA의 비교에 대해서는 이전 [MSA] MSA의 개념과 이해_기존 아키텍처와 MSA 포스팅을 작성한적이 있으므로 참고해주시기 바랍니다.

CloudEvents Spec 이란? 그리고 이것은 왜 필요한가?

CloudEvents는 이벤트 메시지의 스키마 포맷의 vendor-neutral한 스펙(Spec)입니다.

이벤트는 서버가 분산되어 서비스를 제공할 때, 특히 MSA 상황에서는 서버간의 이벤트 메시지는 교환은 필수적인 요소중 하나입니다. A Service의 상황을 B Service는 이벤트 메시지를 통해서 알게 되는 것입니다. 이러한 이벤트 메시지는 정의하는 팀, 조직에 따라서 연동되는 형식이 달라질 수 있습니다. 이렇게 되면 서로 다른팀이 같은 스키마를 보고 다르게 해석할 수 있다는 이야기가됩니다. 이러한 것을 막기 위해서는 조직에서는 표준을 정할 필요가 있습니다. 이런 상황에 참고할 수 있는 스펙이 CloudEvents Spec이라고 할 수 있습니다.

CloudEvents는 서버간의 이벤트 메시지의 공통적인 형식(format)을 제공해줍니다.

용어(Terminology)

사건(Occurrence)

사건이라는 것은 소프트웨어 시스템의 작동중 무언가에 의해서 상태가 변하는것을 말합니다. 예를 들어 디바이스에 배터리가 얼마 없다면 알람이 발생한다고 예를 들어보겠습니다. 이 경우 디바이스에 베터리가 있었던 상태에서 없었던 상태가 되는 것을 사건(Occurrence)이 생겼다고 할 수 있습니다.

이벤트(Event)

이벤트는 사건과 그 컨텍스트를 표한하는 데이터 Report Message 입니다. 이벤트는 생성하는 곳을 Producer, 이를 전달 받아 처리하는 곳을 Consumer라고하며 Producer에서 Consumer로 라우팅됩니다. Event에는 사건을 나타내는 Event Data, 컨텍스트 메타데이터라는 두가지의 데이터가 가지고 있습니다. 또한 하나의 사건으로 인해 둘 이상의 이벤트가 발행 될 수 도 있습니다.

생산자(Producer)

이벤트를 만들어 내는 서버(instance) 또는 디바이스 하나를 말합니다.

Source

Source는 사건이 일어나는 컨텍스트를 의미합니다. 분산 시스템의 경우 하나의 컨텍스트에 여러 생산자가 존재할 수 있습니다. 예를 들어, User 라는 컨텍스트에 기본 정보를 관리하는 서비스가 있고 인증 정보를 관리하는 서비스가 따로 있다며 이는 2개의 Producer에서 User라는 하나의 컨텍스트에 대한 Source가 될 수 있습니다.

소비자(Consumer)

소비자는 이벤트를 받아 행동하는 컴포넌트(instance)입니다. 소비자는 컨텍스트를 받아들이고 이를 기반으로 동작하는 로직을 가지고 있습니다.

중개자(Intermediary)

중개자는 이벤트를 받아서 다음 중개자 또는 소비자에게 이벤트를 전달하는 역할을 하는 역할을 하는 컴포넌트(ex. 서버)를 말합니다. 해당 컴포넌트는 라우팅을 주요 역할로 한다고 말할 수 있습니다.

컨텍스트(Context)

컨텍스트 정보는 이벤트들과 시스템 또는 다른 이벤트들간의 관계를 나타냅니다. 즉, 비즈니스를 MSA로 구성하기때문에 MSA 환경에서의 각 이벤트들의 상호 관계성을 나타내고 이러한 환경이라고 할 수 있습니다. 이러한 컨텍스트는 필수적인 속성과 선택적인 속성으로 나타낼 수 있습니다.

데이터(Data)

사건에 대해서 도메인 Specific한 정보를 나타냅니다. 다른 말로는 payload라고도 부르기도합니다. 데이터에는 사건에 대한정보가 포함됩니다. 구체적으로는 변경된 데이터가 어떠한 것인지 등을 나타낼 수 있습니다.

이벤트 형식(Event Format)

이벤트 형식은 CloudEvents를 어떠한 방식으로 serialize 할 것인지를 나타냅니다. 대표적으로 JSON 형식이 있고 최근에는 bytebuffer, avro 등 Schema first로 serialize 하는 방식도 많이 사용합니다.

메시지(Message)

이벤트는 결과적으로 source 에서 특정 목적지로 전송됩니다. 이때 message를 통해서 전송된다고 할 수 있습니다. 이러한 Message에는 Structured-mode message와 binary-mode message로 구분해볼 수 있습니다.

  • structured-mode message : 이벤트가 독립된 형태의 이벤트 형식을 사용해서 전체적으로 인코딩되어 메시지 본문에 저장되는 형식
  • binary-mode message : 메시지 본문은 따로 있고 이중 하나의 속성으로써 사용되는 형식

컨텍스트 속성(Context Attributes)

CloudEvent는 필수적인(REQUIRED) 속성(Attributes)들과 선택적인(OPTIONAL) 속성들로 이루어집니다. 이러한 속성들은 Serialize/Deserialize하지 않고 Schema 단독으로 어떠한 상황에 사건에 대해서 발행된 Event Message 인지 이해할 수 있습니다.

CloudEvents는 어떠한 vendor에도 종속받지 않습니다. 따라서 모든 언어에 대해서 통용되는 컨벤션을 따라야합니다. 따라서 컨벤션은 아래의 3가지를 유의해야합니다.

  • 대소문자를 구분합니다.
  • 속성 이름은 반드시 ASCII 문자 집합의 소문자('a' ~ 'z') 또는 숫자('0' ~ '9')로 구성되어야 합니다.
  • 이름은 설명적이고 간결해야 하며 길이가 20자를 초과해서는 안 됩니다.

타입(Type)

CloudEvents에 사용할 수 있는 타입에 대한 정의입니다. 모든 환경에서 사용할 수 있어야하기 때문에 String encoding을 사용해서 타입을 기술하는 방법을 함께 기술합니다.

  • Boolean : true / false를 나타내는 Type
    • String encoding: "true" 또는 "false"로 대소문자를 구분하여 사용
  • Integer : -2,147,483,648 ~ +2,147,483,647의 값으로 32-bit(4Byte)로 표현할 수 있는 숫자
    • String encoding: 0 ~ 9의 숫자를 입력할 수 있습니다. 추가로 음수의 경우 -로 표현
  • String
    • Unicode로 허용되는 글자
  • Binary
    • Byte Array
    • String encoding: Base64 인코딩하여 String으로 처리
  • URI
    • 절대 주소를 사용하여 표현
    • String encoding: 절대 주소를 사용하여 String으로 표현
  • URI-reference
    • 리소스 식별자를 참조할 수 있는 표현. URI를 사용할 수도 있으며 상대 참조도 사용 가능
    • String encoding : RFC 3986의 규격을 따름
    • 예시
    • 인터넷에서 주로 사용되는 unique URI를 사용가능
    • 글로벌 unique한 URN
      • urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66
    • 어플리에케이션 종속적인 Identifiers
      • /cloudevents/spec/pull/123
      • /sensors/tn-1234567/alerts
      • 1-555-123-4567
  • Timestamp
    • 날짜와 시간 표현
    • String encoding: RFC 3339 규격을 따릅니다.
    • 예시 : 1996-12-19T16:39:57-08:00

필수 속성(REQUIRED Attributes)

아래의 attribute는 CloudEvents에서 반드시 포함해야하는 필수적인 속성(Attribute)들입니다.

id

  • 타입 : String
  • Description : event를 식별하는 값입니다. 생산자는 source + id의 값이 unique함을 보장해야합니다. 소비자는 source + id 값으로 동일한 이벤트라는 것을 확인할 수 있습니다.
  • 제약사항
  • ID는 null이면 안되며 empty여도 안됩니다.
  • producer가 생성해내는 ID는 unique 해야합니다.
  • 예시
    • Auto Incremental Number : 하나씩 증가하는 Number
    • UUID

source

  • 타입: URI-reference
  • 설명 : 이벤트가 발생한 컨택스트를 명시하는 값입니다. 이벤트 소스의 유형, 이벤트를 사출한 조직, 서비스 등이 포함되는 경우가 많습니다. 타입은 URI-reference 형식으로 포함합니다.
  • 예시
  • 인터넷에서 주로 사용되는 unique URI를 사용가능
  • 글로벌 unique한 URN
    • urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66
  • 어플리에케이션 중심적인 Identifiers
    • /cloudevents/spec/pull/123
    • /sensors/tn-1234567/alerts
    • 1-555-123-4567

specversion

  • 타입: String
  • 설명 : 사용하고 있는 CloudEvents 스펙의 version입니다. 이를 통해서 버저닝이 가능하고 컨텍스트를 제대로 해석할 수 있습니다. 1.0 버전부터 시작해야합니다.

현재 이 속성에는 '메이저' 및 '마이너' 버전 번호만 포함됩니다. 이를 통해 직렬화에서 이 속성 값을 변경하지 않고도 사양을 '패치' 변경할 수 있습니다. 참고: '릴리스 후보' 릴리스의 경우 테스트 목적으로 접미사가 사용될 수 있습니다.

  • 제약조건
    • null이면 안되며 empty여도 안됩니다.

type

  • 타입: String
  • 설명: 이 속성은 사건에 대한 타입을 나타냅니다. 이 속성을 이용해서 컨슈머에서는 라우팅, 어떠한 비지니스를 실행하는 정책 등으로 사용할 수 있습니다.
  • 제약 조건
    • null이면 안되며 empty여도 안됩니다.
    • reverse-DNS 이름의 순서로 prefix를 붙여주면 좋습니다. prefix는 java의 package를 지정하는것 처럼 조직에서 부터 evnet type까지 범위를 좁혀가며 기술하는게 좋습니다.
  • 예시
    • com.github.pull_request.opened
    • com.example.object.deleted.v2

선택적 속성(OPTIONAL Attributes)

위에서는 필수 속성(Attributes)들을 알아보았습니다. 이번에는 비지니스와 Producer의 성격에 따라서 생략할 수 있는 속성들을 알아보도록 하겠습니다.

datacontenttype

  • 타입 : String per RFC 2046
  • 설명 : 실제 데이터와 이를 감싸고 있는 wrapper의 데이터 형식이 다를 경우에 사용합니다. 즉, 전체 스키마는 JSON이나 data의 스키마는 xml 이거나 할 때 사용할 수 있습니다. 따라서 만약 wrapper의 데이터 형식과 실제 데이터의 형식이 동일하다면 해당 속성은 스킵이 가능합니다. 따라서 data type을 해석할 때 datacontenttype 속성이 있다면 data와 wrapper의 형식이 다르다는것을 이해해하고 파싱해야합니다.

dataschema

  • 타입 : URI
  • 설명 : data가 준수해야하는 schema의 URI를 기술합니다. 스키마가 변경이 된다면 별도의 URI를 기술해야합니다.
  • 제약 조건
    • 사용한다면 empty String은 불가능하다.

subject

  • 타입 : String
  • 설명 : 해당 속성은 event producer의 컨텍스트에서 그 이벤트의 상세한 이름(제목)을 기술할 수 있습니다. 발행-구독 모델(publish-subscribe model)에서 구독자는 source 속성을 통해서 event의 컨텍스트를 파악하고 type 속성을 이용해서 어떤 사건에 의해서 발행됬는지를 파악할 수 있습니다. 일반적인 경우 위 두 속성을 통해서 확인이 가능합니다만 내부 값이 필요하다면 subejct 속성이 필요할 수 있습니다.

예를 들면 미디어 파일을 생성했다는 이벤트를 발행했다고 해보겠습니다. 이때 source에는 https://example.com/storage/tenant/container와 같은 URI-Reference 값이 들어올 수 있고 이를 통해서 우리는 컨텍스트 파악이 가능합니다. 그리고 type에 created라고 들어온다면 뭔가 생성이 되었다는 것을 알 수 있습니다. 이때 id 값이 UUID로 들어오면 우리는 UUID값을 이용해서 중복 여부를 판단할 수 있습니다. 그런데 여기서 파일의 확장자에 따라서 로직이 다를 수 있습니다. 이런 케이스가 있을 수 있을 경우 subject를 jpg 또는 txt로 두거나 이름까지 포함하여 mynewFile.jpg라는 식으로 구현을 할 수 있습니다.

time

  • 타입 : Timestamp
  • 설명 : time은 사건이 발생한 시간을 나타냅니다.만약 사건이 발생한 시간이 지정되지 않았다면 대신 어느정도 유사한 이벤트 메시지 발행 시각 등을 사용할 수 있습니다. Producer가 여러개라면 사건 발생시간 또는 이벤트 메시지 발생 시작중 통일되게 시간을 설정할 필요가 있습니다.
  • 제약 사항
    • RFC 3339 제약 사항을 지켜야합니다. 이는 ISO 8601 규약과 동일합니다.

이벤트 Data

data 속성은 사건에 대해 domain-specific한 정보를 포함하는 속성입니다. event payload라고도 부를 수 있습니다.

예제

CloudEvents 스펙에 따른 예제로 들어보면 아래와 같이 기술할 수 있습니다.

{
    "specversion" : "1.0",
    "type" : "com.github.pull_request.opened",
    "source" : "https://github.com/cloudevents/spec/pull",
    "subject" : "123",
    "id" : "A234-1234-1234",
    "time" : "2018-04-05T17:31:00Z",
    "data" : {
        "profileName" : "sabarada"
    }
}

마무리

오늘은 이렇게 Vender 중립적인 CloudEvents를 번역하면서 분석해보는 시간을 가져보았습니다.

이번 번역을 하면서 이미 다양한 생각을 해서 만들어 놓은 해당 스펙에 얼마나 많은 고민이 들어갔었는지 알 수 있는 계기가 되었습니다.

감사합니다.

참조

[1] https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md

[2] https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/kafka-protocol-binding.md

[3] https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/bindings/http-protocol-binding.md

[4] https://sabarada.tistory.com/50

댓글