https://github.com/knative-extensions/eventing-github
knative eventing 활용해서
- 다양한 외부 이벤트 (webhook)를
- knative broker 또는 knative service로 전달하는 작업을 하기 위해
- knative eventing 진영의 stable 컴포넌트인 eventing-github 로직을 분석한 내용.
여기 코드를 분석하고, knative sample-source 레포를 활용하면 다양한 외부 시스템을 knative eventing과 연동할 수 있다.
https://github.com/knative-extensions/eventing-github/blob/main/samples/githubsource.yaml 예시
apiVersion: sources.knative.dev/v1alpha1
kind: GitHubSource
metadata:
name: githubsource-sample
spec:
eventTypes:
- pull_request
ownerAndRepository: "<your GitHub org>/<your GitHub repo>"
accessToken:
secretKeyRef:
name: githubsecret
key: accessToken
secretToken:
secretKeyRef:
name: githubsecret
key: secretToken
sink:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: destinationServer
---
apiVersion: v1
kind: Secret
metadata:
name: githubsecret
type: Opaque
stringData:
accessToken: "<your GitHub access token>"
secretToken: "<your secret token>"
Custom Resource 기본 구조
knative EventSource는 기본적으로 위와 같은 구성으로 되어 있다.
- Custom Resource 상태를 보고 로직을 수행하는 Reconciler. eventing-github의 경우 사용자가 원하는 특정 github Repo에 webhook을 등록하는 역할을 한다.
- 외부 시스템에서 webhook으로 이벤트가 들어오면, cloudevent 형식으로 변환한 뒤 적절한 목적지로 전달해주는 Adapter.
- Webhook 컴포넌트도 있지만, MutatingWebhook / ValidationWebhook에 특별한 로직은 없었다.
Adapter 로직부터 파악해야 reconcile 쪽 이해가 빨라지기 때문에 adapter부터 설명.
Adapter
- k8s Deployment 또는 knative service로 구성된 컴포넌트. 외부에서 http 요청을 받을 수 있는 URL이 주어진다.
- 외부에서 들어온 이벤트를
- target service (knative의 sink)가 이해할 수 있는 포맷으로 변환.
- GithubSource: github webhook (json format) -> cloudevent
- KafkaSource: kafka message -> cloudevent
- RedisSource: redisStream -> cloudevent
- 이벤트가 전달되어야 하는 target service로 전송
- target service (knative의 sink)가 이해할 수 있는 포맷으로 변환.
adapter를 보면, http Server를 실행하는 단순한 코드로 되어 있다.
외부 이벤트를 받아서, 의도한 곳으로 이벤트를 전달해주는 컴포넌트가 a.router
- router 정보를 업데이트하는 로직은 RegisterHandlerFor()라는 메소드를 보면 된다.
router를 보니 common.NewHandler() 라는 메소드가 있다.
- github webhook에서 들어오는 이벤트 (pull_request 같은 것) 를 받아서
- GithubSource 라는 Custom Resource의 정보를 토대로
- Knative Sink에 정의된 리소스 (example.yaml의 destinationServer) 로 전달한다.
handler 메소드 내부를 보면
adapter url에서 payload로 들어온 이벤트 타입을 파싱한다 (h.Hook.Parse())
- 잘못된 타입이면 BadRequest
- context 정보에 이벤트를 전달할 목적지인 h.SinkURL 정보를 포함한다.
h.handleEvent()로 이벤트 핸들링 로직 수행
- github event payload 파싱하고
- cloudevent의 SetType / SetSource / SetSubject / SetData 등의 메소드로 cloudevent를 생성한다
- h.Client.Send() 로 cloudevent를 h.SinkURL에 전달한다.
mt-adapter?
default adapter의 경우 Custom Resource 하나 만들어질 때마다 adapter 역할을 하는 서버도 하나씩 만들어지는 구조.
- Custom Resource가 많아질수록 리소스 점유가 불필요하게 늘어난다
- multi-tenant 구조를 사용하면, adapter URL 하나만 관리하고 path 기반 route를 사용해서 리소스 효율적으로 adapter를 관리할 수 있다.
k8s Resource의 API Path와 마찬가지로
GithubSource 이름과, 배포된 namespace를 사용해서 path 기반 route를 설정한다.
다시말해
- github에서 이벤트 보내는 webhook (= adapter) URL: https://adapter-url.example.com/default/githubsource-sample
- router 로직: default/githubsource-sample 이라는 key값에 매핑된 handler 로직을 실행한다.
adapter URL의 경우 eventing-github는 knative service를 사용하는데
- knative service는 scale to zero 옵션까지 있으므로,
- 이벤트가 들어오지 않을 경우 pod가 0으로 내려간다.
- 자주 발생하는 이벤트가 아니라면, idle 리소스에서 발생하는 낭비를 줄일 수 있음.
Reconciler
두 개의 메소드가 중요하다.
- 사용자가 Custom Resource를 배포했을 때, github repo에 webhook을 등록하는 ReconcileKind()
- 사용자가 Custom Resource를 삭제했을 때, github repo에서 webhook을 삭제하는 FinalizeKind()
ReconcileKind()의 경우
- github repo에 접근하기 위한 access Token 정보를 k8s secret에서 조회
- reconcileReceiveAdapter() 메소드로 adapter에서 새로운 종류의 이벤트 핸들러 등록
- 원하는 종류의 이벤트와, 이벤트 전달받을 adapter URL 정보를 github webhook에 등록
- webhookID 결과를 Status 필드에 기록
- Custom Resource가 수정되면, 수정사항을 github webhook에도 반영
- 새로운 이벤트 타입 등록 or 기존 이벤트 타입 삭제 -> github API로 변경사항 반영
FinalizedKind()
- Custom Resource에 등록된 정보가 삭제되므로, 외부 리소스에도 삭제 사항을 반영한다
- status의 webhookID 정보를 github API로 삭제