Knative Eventing Deep Dive
강연자: Ahmed Abdalla
- Redhat Openshift serverless team 소속. knative eventing 담당
cf.
이 강의를 끝까지 들어보면, github 소스코드를 따로 보여주지 않는다.
강의에서 사용한 github 레포도 따라가보고 강연자 @devguyio 의 깃허브 링크를 확인했지만, 이 실습에 사용한 yaml코드는 없었다.
Knative Eventing이란 무엇인가.
뜯어보려면 꽤 복잡한 구조. 미리 정의하고 출발하자면
Opinionated way of building Event Driven Architeucture / Event Driven Application using K8s.
- Opinionated (framework / software): 해당 프레임워크나 소프트웨어 디자이너가 '개발자가 쉽고 빠르게 사용할 수 있도록 모범지침을 만들어둔 것을 의미함. "Happy Path"
- 프레임워크나 소프트웨어에서 가정하거나 규정한 몇 가지 요건을 충족한다면, 빠르게 개발하고 적용할 수 있다는 뜻.
- knative의 경우 kubernetes에서 구축한 design concept를 토대로 동작한다.
특징
- K8s의 디자인 원칙을 계승함.
- Declarative API 형식, k8s와 유사한 개념 (specs, status), Desired status와 current status의 차이를 reactive하게 수정할 수 있는 controller와 reconciler의 존재.
- EDA 개발에 필요한 building blocks 제공
- 독립적인 형태의 producer / consumer 제공. 선후관계 없이 생성할 수 있고, 생성된 것들을 연결하면 됨.
- CloudEvent 표준을 준수함.
- Eventing / Messaging을 위한 여러 tech를 지원함. kafka, NATS, RabbitMQ, GCP PubSub 등
Event Driven Application을 개발할 때 해결해야 할 세 가지 이슈. Knative Eventing은 어떻게 해결했는가?
How to Get your Event? -> Source
- Source라는 abstract concept 도입. Source: 클러스터 외부에서 이벤트를 받고, 외부로 이벤트를 보낼 수 있는 Concept.
- Ingress events into the eventing mesh.
How do I route my Events? -> Broker / Channel
- Broker / Channel이라는 building block / Logical Concept 을 제공함.
- 이벤트를 필요한 곳에 전달하고, 다양한 방식 (style) 으로 이벤트를 받아서 목적지에 전달할 수 있게 되어 있음.
- broker : content-based Routing
- channel : topology-based Routing
How do I egress My events? -> Sink
- 이벤트를 클러스터로 받아서 external destination으로 보내는 방법.
- Sink 제공. cloudEvent를 받아서 처리하게 될 목적지를 의미하는 Logical Concept. final off-cluster system.
Example Cases
사실 Event Driven Architecture는 문제를 해결하는 여러 방법 중 하나일 뿐이다.
EDA 방식으로 문제를 해결한 몇 가지 좋은 예시를 소개하는 편이 좋을 것 같다.
1. Extensibility Problem
대략 상황을 가정하면
- External System이 존재함. 레거시 시스템이거나 외부 솔루션이거나... External System 자체를 변경하기 위한 비용이 매우 크다.
- 이 External System 자체를 따로 손대거나 변경하지 않은 채 functionality를 확장하고 싶다. 새로운 기능을 추가하려고 한다.
예컨대 github와 연동을 생각한다면, github API에서 보내는 이벤트를 기반으로 Extension 기능을 추가할 수 있다.
- github라는 서비스와 기존 레거시 서비스 자체를 건드리지 않고도 새로운 기능을 제공할 수 있는 셈.
2. Integration
heterogenous solutions를 이벤트 기반으로 연결할 수 있다.
- 예시처럼 github / slack / legacy system 세 개의 서비스를 이벤트 기반으로 연결해서 사용할 수 있음.
예시
해결하고 싶은 Problem: github 기능의 확장
- github Contributer가
- 자신의 github repo에 부정적인 comment가 달렸을 경우
- 이메일을 받을 수 있도록 하고 싶다.
외부 github 요청을 받아들일 knative Source를 정의한다.
- spec: desired State를 의미함.
- 예시의 경우 github에서 어떤 종류의 이벤트를 받을 것인지 정의한 eventTypes
- 이벤트가 발생할 repository를 정의한 ownerAndREpository
- sink uri : 이벤트를 전송할 uri
어떻게 동작하는가?
kubernetes 언어로 표현하면
- Control plane: the way that you are trying to declare "Desired States". 원하는 상태를 정의하기 위한 영역
- a set of controllers that exist in k8s.
- Data plane: set of components in the path of actual data that are live and running in your system.
예시. 만약 새로운 pod을 띄우고 싶다면
Control-Plane
- interact with a set of controllers from k8s
- create the actual container
Data-Plane
- service와 통신하기 위한 ingress / routing traffic to actual container & correct Node 생성
Knative Eventing 동작과정에서 각 plane이 어떻게 적용되는지 체크.
- Controller: install one time inside your cluster.
- 예시의 경우 githubSourceController. interact / reconciles all objects that are kind "GithubSource"
- Adapter: pod. 실제로 github과 통신하는 역할. githubSource에서 정의한 eventType, repo url 등의 정보를 토대로 작동한다.
- github API로 webhook 등록. github에서 제공하는 이벤트를 받아들이는 component.
- 필요한 곳에 맞게 Cloud Event를 emit.
- Sink: adapter에게서 이벤트를 받아 처리하는 Object.
그림의 파란색 배경 (GithubSourceController)은 Control Plane 영역이다.
- source를 생성할 때 controller가 만들어지고, controller는 adapter를 설정함. 어떤 api를 사용할 것인지, 어떤 동작에 반응하도록 할 것인지 세팅함.
초록색 배경 (Adapter)는 Data Plane 영역이다.
- 모든 이벤트의 lifeCycle은 Adapter를 거쳐간다.
- Adapter를 어떻게 배치할 것인지? -> 다양한 디자인 패턴에 의해 결정할 수 있다.
위 설정을 보면, Source와 Sink 간 coupling이 확인된다.
service의 uri가 Source의 sink값과 일치해야 하기 때문. 이런 형태의 디자인은 Eventing에서 권장하지 않는다.
단순히 uri만 저장하는 것보다는 Sink Type을 지정하는 편이 Decoupling에는 유리하다.
knative에는 sink type을 매번 정적으로 지정하는 대신 duck type으로 사용할 수 있다.
위 사진의 경우 Service와 InMemoryChannel이라는 별개의 sink type이지만,
address.url을 입력하는 것만으로도 sink type을 판별해서 적용할 수 있다.
이 방식을 Addressable Duck Type이라고 하며, knative에서 sink decoupling을 위해 지원하고 있다.
- 사용자가 Object 형식보다는 object의 동작과정이나 로직에 집중할 수 있도록.
- sink는 uri값이 아예 정해져 있거나, addressable 형태로 주어진다.
- addressable duck type을 제공하는 객체는 url에 접근할 수 있도록 설계되어 있다.
addressable sink는 ready 상태인지 아닌지 k8s에서 제공하는 healthCheck 기능을 지원한다.
GithubSource 활용한 실습 - github 이벤트를 Service와 연결하기
1. GithubSouce로 github Webhook 받도록 세팅하기.
https://knative.dev/docs/samples/eventing/ 에서도 확인할 수 있다.
- github에서 personal access token 생성하기
- 웹훅 등록하기
실습에 필요한 yaml 오브젝트는
https://github.com/inspirit941/knative-eventing-example 에서 확인할 수 있다.
- githubSource 객체를 사용하기 위해 eventing-github 설정파일을 k8s에 적용해야 한다.
- https://github.com/knative-sandbox/eventing-github/releases 에서 자신의 knative 버전에 맞는 github.yaml 파일을 클러스터에 등록해야 한다.
kubectl apply -f https://github.com/knative-sandbox/eventing-github/releases/download/<RELEASE_VERSION>/github.yaml
- https://github.com/inspirit941/knative-eventing-example 에서 확인할 수 있는 네 개의 yaml파일을 클러스터에 배포한다.
- namespace, service, secret, githubsource 네 개의 yaml 파일이 있다.
- secret에 있는 accessToken, secretToken은 https://github.com/knative/docs/tree/main/code-samples/eventing/github-source 를 참고해서 생성한다.
kubectl apply -f <github 클론 경로>
kubectl get pods -n integration
으로 pod를 확인하면,
githubSource가 배포되어 있다. 이 deployment가 앞에서 설명한 Adapter 역할을 함.
watch kubectl -n dev get pods
stern -n dev --json-pp github-message-dumper-0001-deployment -c user-container
# 내 로컬에서는 --json-pp 옵션을 인식하지 못했다. k9s로 로그 확인이 충분히 가능하므로, 강의의 코드를 가능한 그대로 가져왔다.
- webhook을 생성할 때, 요청을 보낼 url은 githubSource여야 한다.
kn service list -n integration
으로 확인할 수 있는 githubSource의 url (http://endgamesource-7wngz.integration....) 으로 이벤트를 보내도록 webhook을 세팅한다. - github webhook을 생성하면, 연결 확인을 위해 ping 이벤트가 호출된다.
stern으로 확인해보면 webhook을 통해 ping 요청이 githubSource로 전달되고,githubSource는 해당 요청을 Service에 전달한다.
따라서 Service에서 cloudEvent 로그가 만들어지는 걸 확인할 수 있다.