블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

Serveless를 위한 오픈소스 KNative #2 Eventing


조대협 (http://bcho.tistory.com)


knative의 다른 모듈로써는 비동기 메세지 처리를 위한 eventing 이라는 모듈이 있다. 카프카나, 구글 클라우드 Pub/Sub, AWS SQS와 같은 큐에서 메시지를 받거나 또는 Cron과 같은 타이머에서 이벤트가 발생하면 이를 받아서 처리할 수 있는 비동기 메커니즘을 제공하는 모듈이라고 보면 된다.


메시지 큐나 cron 과 같이 이벤트를 발생 시키는 자원들은 knative에 event source 라는 Custom Resource로 등록이 되고, 등록된 event source는 이벤트가 발생되면 지정된 knative 서비스로 이벤트 메시지를 HTTP로 전송한다. 이때 이벤트를 받는 knative 서비스는 앞에서 언급한 knative serving의 서비스이다. 이때 이벤트에 대한 스펙은 CNCF Serverless WG 에서 정의한 CloudEvents 스펙에 기반한다.

Hello Eventing

자세하게 Eventing에 대해서 알아보기 전에 간단한 예제를 살펴보자. 예제는 knative.dev의 cronjob  예제이다.  Crontab으로 이벤트를 생성하면, event-display 라는 서비스에서 이 이벤트를 받아서 이벤트의 내용을 간략하게 로그로 출력하는 예제이다.


먼저 이벤트를 읽어드릴 event-display 서비스를 배포하자. 해당 서비스는 HTTP post로 받은 이벤트의 내용을 log로 출력해주는 코드로 이벤트의 포맷은 앞에서 설명한 CloudEvent의 포맷을 따른다.

Go 로 구현된 코드이며, 코드 원본은 여기에 있다.

 해당 컨테이너를 배포하기 위해서 아래와 같이 service.yaml 파일을 만들고, kubectl apply -f service.yaml 을 이용해서 배포하면, crontab 에서 이벤트를 받는 serving 인스턴스가 준비된다.

apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: event-display

spec:

 runLatest:

   configuration:

     revisionTemplate:

       spec:

         container:

           image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display

<그림. Event consumer용 knative 서비스 배포>


다음 Crontab event 소스를 아래와 같이 yaml로 정의한다.


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: CronJobSource

metadata:

 name: test-cronjob-source

spec:

 schedule: "*/2 * * * *"

 data: '{"message": "Hello world!"}'

 sink:

   apiVersion: serving.knative.dev/v1alpha1

   kind: Service

   name: event-display

<그림. Crontab event source 정의>


spec>schedule 부분에 이벤트 주기에 대한 설정을 crontab 포맷을 따라서 하고, data 부분에 cron 이벤트가 발생할때 마다 보낼 데이타를 정의한다.

데이타를 보낼 목적지는 sink 부분에 지정하는데, kind에 타입을 정의하고 (여기서는 knative의 Service로 지정) 그리고 service 의 이름을 name에 정의한다. 앞에서 knative serving 서비스를 event-display로 지정하였기 때문에, 서비스명을 event-display로 정의한다.

yaml 파일 설정이 끝났으면 kubectl apply -f  명령을 이용해서 이벤트 소스를 등록하고, 동작을 하는지 확인해보도록 하자.


%kubectl logs -l serving.knative.dev/service=event-display -c user-container --since=10m


명령을 이용하면 앞에서 배포한 event-display 서비스의 로그를 볼 수 있는데, 결과를 보면 다음과 같다.



Data 부분에서 crontab 이벤트 소스에서 보내온 “message”:”Hello world!” 문자열이 도착한것을 확인할 수 있다.

Eventing detail

이벤트는 앞의 예제에서 본것과 같이 이벤트 소스에서 바로 Knative 서빙에서 받아서 처리하는 가장 기본적인 비동기 이벤트 처리 패턴이다.


Broker & Trigger

이러한 패턴이외에도 좀 더 다양한 패턴 구현이 가능한데, 두번째가 Broker와 Trigger이다. Broker는 이벤트 소스로 부터 메시지를 받아서 저장하는 버킷 역할을 하고, Broker에는 Trigger를 달 수 있는데, Trigger에는 메시지 조건을 넣어서, 특정 메시지 패턴만 서비스로 보낼 수 있다. 위의 패턴에서 필터를 추가한 패턴으로 보면 된다.



이해를 돕기 위해서 예제를 보자. 다음은 knative.dev 공식 사이트에 나와 있는 예제중에, Google Cloud Pub/Sub Source를 Broker로 연동하는 예제이다.


# Replace the following before applying this file:

#   MY_GCP_PROJECT: Replace with the GCP Project's ID.


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: GcpPubSubSource

metadata:

 name: testing-source

spec:

 gcpCredsSecret:  # A secret in the knative-sources namespace

   name: google-cloud-key

   key: key.json

 googleCloudProject: MY_GCP_PROJECT  # Replace this

 topic: testing

 sink:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Broker

   name: default

<그림. github-pubsub-source.yaml>


위의 코드는 GCP Pub/Sub Source를 등록하는 부분인데, sink 부분은 이 소스에서 오는 메시지를 어디로 보낼지를 정하는 부분이다. 위에 보면 Broker로 보내는것을 볼 수 있다. Broker는 Default Broker로 보낸다.


다음은 Broker에서 받은 메시지를 Trigger 조건에 따라서 Knative Serving 서비스로 보내는 설정이다.


apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: event-display

spec:

 template:

   spec:

     containers:

     - # This corresponds to

       # https://github.com/knative/eventing-sources/blob/release-0.5/cmd/event_display/main.go           

       image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display@sha256:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d


---


# The GcpPubSubSource's output goes to the default Broker. This Trigger subscribes to events in the

# default Broker.


apiVersion: eventing.knative.dev/v1alpha1

kind: Trigger

metadata:

 name: gcppubsub-source-sample

spec:

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: event-display


< 그림. Trigger와 이벤트 메시지를 수신하는 Service를 정의한 부분>


서비스는 event-display라는 서비스를 정의하였고, 그 아래 Trigger 부분을 보면 gcppubsub-source-sample 이라는 이름으로 Trigger를 정의하였다. Broker 명을 정의하지 않으면 이 Trigger는 default broker에 적용된다. 별다른 조건이 없기 때문에, Broker의 모든 메시지를 대상 서비스인 event-display로 전달한다.

Channel & subscription

다음 개념은 Channel과 subscription 이라는 개념인데, Channel을 메시지를 저장 후에, Channel에 저장된 메시지는 메시지를 수신하는 Subscription을 통해서 다른 Channel로 포워딩 되거나 또는 Service로 전달 될 수 있다.



<그림. Channel과 Subscription 개념도>


앞에서 Channel에서는 메시지를 저장한다고 했는데, 그러면 저장할 장소가 필요하다. 저장할 장소는 설정으로 다양한 메시지 저장소를 사용할 수 있는데, 현재 메모리, Apache Kafka 또는 NATS Streaming을 지원한다.


간단한 예제를 살펴보자 예제는 이 문서를 참고하였다

먼저 아래 설정을 보자


apiVersion: sources.eventing.knative.dev/v1alpha1

kind: GcpPubSubSource

metadata:

 name: testing-source

spec:

 gcpCredsSecret:  # A secret in the knative-sources namespace

   name: google-cloud-key

   key: key.json

 googleCloudProject: knative-atamel  # Replace this

 topic: testing

 sink:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test



< 그림. GCPPubSub Event Source 정의한 코드>


위 설정은 GCP Pub/Sub을 Event source로 등록하는 부분이다. 이벤트 소스로 등록 한후에, 이벤트를 sink 부분에서 pubsub-test라는 Channel로 전달하도록 하였다.

다음 아래는 Channel을 정의한 부분인데, pubsub-test 라는 이름으로 Channel을 정의하고 "provisioner” 부분에, 메시지 저장소를 "in-memory-channel” 로 지정해서 메모리에 메시지를 저장하도록 하였다.

apiVersion: eventing.knative.dev/v1alpha1

kind: Channel

metadata:

 name: pubsub-test

spec:

 provisioner:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: ClusterChannelProvisioner

   name: in-memory-channel

< 그림. Channel 정의한 코드>



apiVersion: serving.knative.dev/v1alpha1

kind: Service

metadata:

 name: message-dumper-csharp

spec:

 runLatest:

   configuration:

     revisionTemplate:

       spec:

         container:

           # Replace {username} with your actual DockerHub

           image: docker.io/{username}/message-dumper-csharp:v1

---

apiVersion: eventing.knative.dev/v1alpha1

kind: Subscription

metadata:

 name: gcppubsub-source-sample-csharp

spec:

 channel:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: message-dumper-csharp

< 그림. Serving과 subscription을 정의 코드>


Channel에 저장된 메시지를 다른 Channel로 보내거나 또는 Service로 보내려면 Subscription을 거쳐야 한다. 위에서 gcppubsub-source-sample-charp이라는 subscription을 정의하였고, 이 subscription이 연결되는 Channel은 spec > channel 부분에 아래와 같이 정의 하였다.


aspec:

 channel:

   apiVersion: eventing.knative.dev/v1alpha1

   kind: Channel

   name: pubsub-test

< 그림. 위의 Subscription 정의에서 Channel 정의 부분>


그리고 그 채널에서 받은 메시지를 subscriber > ref 부분에서 아래와 같이 message-dumper-charp이라는 서비스로 포워딩 하도록 하였다.

 subscriber:

   ref:

     apiVersion: serving.knative.dev/v1alpha1

     kind: Service

     name: message-dumper-csharp

< 그림.위의 Subscription 정의에서 Service 정의 부분>


전체적으로 Eventing 모듈을 이해하는데 시간이 많이 걸렸는데, Eventing 모듈은 Serving 모듈에 비해서 예제가 적고, 공식 문서에 아직 설명이 부족하다. 예를 들어서 소스 → 서빙으로 메시지를 보낼때 스케일링할 경우 문제가 없는지. Channel → subscription 으로 메시지를 보낼때 Trigger를 사용할 수 있는지 등 정보가 아직 부족해서 자세한 분석이 어려웠다. Knative는 현재 0.5 버전으로 버전이고, Event Source 들도 아직 개발 단계가 아니라 PoC (Proof Of Concept : 기술적으로 가능한지 테스트를 하는 단계) 단계 이기 때문에 제대로 사용하기에는 시간이 더 걸릴 듯 하다.

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요


구글 클라우드의 대용량 메세지 큐 Pub/Sub 소개

조대협 (http://bcho.tistory.com)




구글 클라우드의 Pub/Sub 은 클라우드 기반의 대용량 메세지 큐이다.

흔히들 사용하는 RabbitMQ, JMS나 Kafka의 클라우드 버전으로 보면 된다. Rabbit MQ와 같은 설치형 큐가 작은 메세지에 대해서 세심한 컨트롤을 제공한다고 하면, Kafka나 Pub/Sub은 대용량 스케일의 메세지를 처리하기 위해서 설계 되었고, 자잘한 기능보다는 용량에 목적을 둔다.

그중에서 Pub/Sub은 클라우드 기반의 서비스로 비동기 메세징이 필요한 기능을 매니지드 서비스 형태로 제공함으로써, 별도의 설치나 운영이 필요 없이 손쉽게, 사용이 가능하다.

보통 특정 클라우드 벤더의 매지니드 솔루션은 Lock in 이슈 (한번 개발하면 다른 플랫폼으로 옮기기가 어려운)가 있어서 쉽사리 권하기는 어렵지만, 사용법이 간단하고 Lock in을 감수하고도 기능이 막강한 서비스나, 타 서비스로 전환이 쉬운 서비스일 경우에는 적극적으로 권장하는 편이다.


Pub/Sub의 경우 대용량 큐 서비스이기 때문에 Kafka 처럼 설치나 운영이 필요없음에도 불구하고 대용량 처리를 지원하면서 사용이 매우 쉽고, 코딩 양이 매우 적어서 차후에 다른 솔루션으로 교체가 용이하고 또한 대용량 장점과, 운영 대행의 장점으로 Lock in에 대한 단점을 충분히 커버하리라고 본다.

특징

주요 특징을 살펴보면, 글로벌 스케일 큐로, 전세계 어느 데이타 센터에서 접속하던지 구글 자체 광케이블망을 이용하여 빠른 접근을 제공한다.

메세지 전달 보장을 기능이 있으며, 큐에서 메세지를 PULLING 하는 기능뿐만 아니라, 큐가 메세지를 받는 쪽으로 HTTP를 이용하여 PUSH 해줄 수 있다.

토폴로지

구글 Pub/Sub 은 Message Provider (보내는쪽)과 Message Consumer (받는쪽)이 1:1 관계가 아니라. 1:N 관계이다.


Pub/Sub에는 Topic과 Subscription이라는 개념이 존재하는데,  Topic 을 큐로 생각하면 된다.

Message Provider가 Topic으로 메세지를 보내게 되고, 메세지를 읽으려면 Subscription 이라는 구독 채널을 설정해야 한다. Subscription은 하나의 Topic에 대해서 1..N개가 생성될 수 있다.

클라이언트는 각각의 Subscription에 붙어서 메세지를 받을 수 있다.

예를 들어서 하나의 메세지를 로그 시스템과 데이타 베이스 양쪽에 저장하고 싶을때는 Topic을 만든 후에, 로그 시스템용 Subscription, 데이타 베이스용 Subscription을 각각 만들어서 데이타를 읽으면 된다.


클라이언트 인터페이스

구글 Pub/Sub의 연동은 크게 다음과 같이 3가지 방법으로 접근이 가능하다.

메세지 구조와 생명 주기

Pub/Sub에 넣을 수 있는 메세지는 간단하다. String 형태의 메세지를 넣을 수 있으며, 메세지의 크기는 base64-encoding이 된 기준으로 최대 10M까지 지원이 된다.

메세지는 Message 와, Message Attribute  두가지 블럭으로 구분된다. 비교해서 이해하자면 Message 는 HTTP BODY, Message Attribute는 HTTP Header와 같은 개념으로 생각하면 되는데, Message는 통째로 TEXT가 들어가고, Message Attribute는 Key/Value 형태로 각각의 필드가 들어간다.  

생명주기 및 재처리 정책

메세지 생명 주기가 재미있는데, 먼저 Push로 받거나 Pull로 받은 메세지는 큐에서는 일단은 보이지 않는다. (다시 가지고 올 수 없다는 이야기). 메세지 처리가 끝난 후에는 클라이언트는 Pub/Sub으로 Acknowlege를 보내야 하는데, 만약에 정해진 시간 (이를 message acknowlegement deadline이라고 하고 디폴트는 10초)내에 ack를 주지 않으면, 그 메세지는 다시 Pub/Sub으로 들어간다.  이 acknowlegement를 통해서 메세지 전달 보장이 가능하다.


다시 Pub/Sub으로 돌아간 메세지는 Publishing time으로 부터 최대 7일까지 보관이 되서 클라이언트에서 다시  읽어드릴 수 있다.


https://cloud.google.com/pubsub/subscriber#ack_deadline


순서 보장

Pub/Sub 큐에 들어온 메세지는 Consumer에서 읽어드릴때, Pub/Sub에서 보낸 순서대로 읽을 수 없고, 랜덤한 순서로 전달된다. 즉 전달 순서 보장이 되지 않는다. 이는 Pub/Sub이 기본적으로 분산형 아키텍쳐를 띄고 있기 때문에, 내부에 어떤 노드로 데이타가 전달되는지, 그리고 각 노드중 어느 노드에서 데이타를 읽는지 예측이 불가능하기 때문이다.

메세지 전달 방식 (Message delivery type)

Pub/Sub은 일반적은 큐 시스템과 다르게 메세지를 Subscriber가 읽어오는 Pull 방식 이외에, Pub/Sub이 직접 Subscriber에게 메세지를 쏴주는 Push 방식을 같이 지원한다.

Pub/Sub 테스트 하기

대략적인 개념 이해가 끝났으면, 이제 실제 테스트를 해보자

Topic 생성하기

구글 클라우드 콘솔에서 Pub/Sub을 선택한 후, 아래 그림과 같이 메뉴에 들어오면, Create Topic 메뉴를 선택하여 Pub/Sub Topic을 생성한다.


여기서는 아래 그림과 같이 “mytopic”이라는 이름으로 토픽을 생성하겠다.


토픽명은 “projects/{프로젝트명}/topcis/{토픽명}” 식으로 생성된다. 이 예제에서 사용한 프로젝트명은 terrycho-sandbox이고, 지정한 토픽명은 “mytopic”이기 때문에, topic의 전체 이름은 “projects/terrycho-sandbox/topcis/mytopic”이 된다.

Subscription 생성하기

이제 앞에서 생성한 Topic으로 부터 메세지를 읽어드리기 위해서 Subscription을 생성해보자.

Pub/Sub 메뉴에서 아래와 같이 앞서 생성한 Topic을 확인할 수 있다. 이 메뉴에서 생성한 Topic의 “+New subscrition”이라는 버튼을 선택하면 새로운 Subscription을 생성할 수 있다.


아래 그림과 같이 subscription 생성화면에서 subscription 이름을  mysubscription으로 지정하자.

topic과 마찬가지로 subscription의 full name 역시 “projects/{프로젝트명}/subscriptions/{서브스크립션명}” 이 된다.


그리고 Delivery type (메세지 전달 방식)은 Pull을 선택한다.

아래 그림과 같이 Advanced option에서  Acknowlegement deadline을 설정할 수 있는데, 건들지 말고 디폴트 10초로 놔둔다.



메시지 보내보기

메세지를 보내는 테스트를 하기 위해서는 클라우드 콘솔 Pub/Sub 메뉴에서 앞에서 생성한 Topic을 선택하면 아래 그림과 같이 “Publish” 버튼이 나온다.


Publish 버튼을 누르면 아래와 같이 메세지를 직접 입력할 수 있는 창이 나온다.


위의 그림과 같이 Message 창에 보내고 싶은 메세지를 적고 Publish버튼을 누르면 Pub/Sub에 메세지가 퍼블리슁 된다.

보낸 메세지 읽어드리기

이제 퍼블리슁된 메세지를 읽어보자. 메세지는 gcloud라는 구글 클라우드 클라이언트를 이용해서 할것인데, 설치 방법은 https://cloud.google.com/sdk/gcloud/ 를 참고하면된다.

설치가 귀찮은 경우에는 아래 그림과 같이 구글 클라우드 콘솔의 상단 부분에 우측에 “>.” 이렇게 생긴 아이콘을 누르면 된다.



클라우드 쉘이라는 것인데, 구글 클라우드에 대해서 Command Line으로 명령을 내릴 수 있는 기본 명령어들이 미리 깔려있는 Linux 접속창이다.



pub/sub 은 아직 알파 버전이기 때문에, gcloud를 업그레이드 해서 alpha 버전 명령어가 수행이 가능하도록 해야 한다.

다음 명령어를 실행하자

%gcloud components install alpha

이제 gcloud 명령어가 업데이트 되었다. 이제 Pub/Sub topic에서 데이타를 읽어와보자

다음 명령어를 실행하면 mysubscrition에서 메세지를 읽어올 수 있다.

gcloud alpha pubsub subscriptions pull projects/terrycho-sandbox/subscriptions/mysubscription

다음은 명령을 실행해서 데이타를 읽어온 결과 이다.




10초 정도 후에 같은 명령어 실행해보면 같은 메세지가 리턴되는 것을 볼 수 있는데, 이는 ack를 주지 않았기 때문이다. gcloud에서 자동으로 ack를 보내는 방법은 명령어에 --auto-ack라는 옵션을 추가하면 된다.

옵션을 추가하고 명령을 실행해보자

gcloud alpha pubsub subscriptions pull projects/terrycho-sandbox/subscriptions/mysubscription --auto-ack

아래 결과와 같이, 첫번째 실행에서는 메세지가 도착하지만, 두번째 실행에서는 같은 메세지가 도착 하지 않는 것을 확인할 수 있다.


이 밖에도 gcloud 명령으로 하나의 메세지 뿐 아니라 한번에 여러개의 메세지를 리턴받을 수 도 있고, 여러개의 메세지를  pagination을 통해서 리턴 받을 수 도 있다. 자세한 옵션은 https://cloud.google.com/sdk/gcloud/reference/alpha/pubsub/subscriptions/pull 를 참고하기 바란다.


클라우드 웹 콘솔과, gcloud 명령어를 이용해서, 메세지를 퍼블리슁하고 읽어들이 것을 알아보았다. 다음 글에서는 실제로 SDK를 이용해서 메세지를 퍼블리슁하고 읽어들이는 예제를 소개하도록 하겠다.

본인은 구글 클라우드의 직원이며, 이 블로그에 있는 모든 글은 회사와 관계 없는 개인의 의견임을 알립니다.

댓글을 달아 주세요