Tekton Pipelines blog serisinin 3. yazısından selamlar.
Bu blog, serinin son halkası olmakla birlikte; önceki yazılarda adım adım inşa ettiğimiz yapıların üzerine artık gerçek bir otomasyon katmanı ekliyoruz.
İlk yazımızda Tekton’ın temel kavramlarını incelemiş, ikinci yazıda ise bu temelleri kullanarak bir uygulamanın container imajını build edip, image registry’ye pushlayan bir pipeline kurgulamıştık.
Şimdi bu pipeline’ı bir adım ileri taşıyacak, manuel müdahale gerektirmeden; bir olay gerçekleştiğinde (örneğin bir Git push) otomatik olarak tetiklenmesini sağlayacağız.
Yani artık olaylara tepki veren (event-driven
) bir CI/CD mimarisi kurma zamanı.
Burada bu yazıyı okumadan önce serinin diğer yazılarına bir göz atmak veya sırasıyla takip etmek isterseniz aşağıdaki yönergeyi kullanabilirsiniz ☺️
1. Tekton Pipelines Nedir? Tekton ile CI/CD Süreçlerine Giriş
2. Tekton Pipelines ile Docker Image Build ve Registry Push
3. Tekton Pipelines ile Event Driven CI/CD Süreci Kurgulama (okumakta olduğunuz blog)
Tekton Triggers nedir?
Tekton Triggers
dış kaynaklarda (örneğin bir Git push veya webhook çağrısı gibi) oluşan event’leri tanımlayıp yakalayarak, ilgili Pipeline veya Task’lerin otomatik olarak tetiklenmesini sağlayan bir Tekton bileşenidir.
Dış kaynakta oluşan ve Trigger tarafından yakalanan event’teki veriler, tetiklenecek olan objeye parametre olarak aktarılabilir. Bu sayede pipeline ya da task’ler dinamik şekilde parameterize edilebilir.
Tekton Triggers nasıl çalışır ve hangi bileşenlerden oluşur?
Triggers, bir dizi custom Tekton tabanlı K8S CRD (Custom Resource Definition) objesinden oluşur.
🎧 EventListener
Tekton Triggers yapısının dış dünya ile iletişim kuran bileşenidir. Belirli bir port üzerinden HTTP isteklerini dinler ve gelen event’leri yakalar. Cluster içinde birden fazla EventListener
tanımlanabilir. Event’i gönderen dış sistem, bu EventListener
bileşenini hedef alacak şekilde yapılandırılır (örneğin bir Git webhook’u).
⚙️ Trigger
EventListener tarafından yakalanan event’e nasıl tepki verileceğini belirler. Yani hangi pipeline ya da task’in, hangi parametrelerle tetikleneceğini tanımlar. Bir Trigger objesi mutlaka TriggerTemplate
ve TriggerBinding
objesi refere etmelidir.
🏛️ TriggerTemplate
Event listener’ objesinin yakaladığı event sonucu yürütümü sağlanacak kaynakların tanımını içerir. Event sonucu pipeline veya task objelerinin yürütümü için PipelineRun
ve TaskRun
objelerini tanımlandığı şekilde oluşturmakla yükümlüdür.
💿 TriggerBinding
Dış kaynaktan listenere gelen event payloadından veriyi çıkartmak için kullanılır. Payload üzerinden çıkarılan veri TriggerTemplate
üzerine aktarılır.
🚔 Interceptor
Interceptor, bir event gerçekleştiğinde, bu event henüz Trigger’a ulaşmadan önce devreye girerek çeşitli kontrollerin ve işlemlerin yapılmasına olanak sağlar. Bu sayede event’in içeriği doğrulanabilir, belirli koşullara göre filtrelenebilir veya payload üzerinde dönüşümler uygulanabilir. Uygun eventler Trigger
objesine iletilirken, uygun olmayanlar bu aşamada elenir. Tanımlanması ve kullanılması ise zorunlu değildir.
Yukarıda bahsettiğimiz yapının görselleştirilmiş bir çıktısını aşağıdaki çizimden inceleyebilirsiniz.
Triggers’i yükleyelim ve EventListener ile event driven yapıyı oluşturalım.
Triggers’i ve arından interceptorları aşağıdaki şekilde hızlıca clusterınıza kurabilirsiniz.
$ kubectl apply --filename \
https://storage.googleapis.com/tekton-releases/triggers/latest/release.yaml
$ kubectl apply --filename \
https://storage.googleapis.com/tekton-releases/triggers/latest/interceptors.yaml
Aşağıdaki komutun çıktısındaki tüm podlar Running statüsüne geçmisse kurulum başarılı şekilde tamamlanmıştır.
$ kubectl get pods -n tekton-pipelines --watch
Bir önceki blog yazısında oluşturmuş olduğumuz build/push pipeline’ini kullanacak ve bu pipeline’i event-driven
hale getireceğiz.
Öncelikle pipeline tanımımızı burada tekrar edelim;
# pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: image-build-and-push
spec:
params:
- name: repo-url
type: string
- name: revision
type: string
- name: IMAGE
type: string
- name: DOCKERFILE
type: string
- name: CONTEXT
type: string
- name: APP_VERSION
type: string
workspaces:
- name: pipeline-workspace
- name: docker-credentials
tasks:
- name: fetch-repository
params:
- name: url
value: "$(params.repo-url)"
- name: revision
value: "$(params.revision)"
taskRef:
name: git-clone
workspaces:
- name: output
workspace: pipeline-workspace
- name: build-push
taskRef:
name: kaniko
params:
- name: IMAGE
value: "$(params.IMAGE):$(params.APP_VERSION)"
- name: DOCKERFILE
value: "$(params.DOCKERFILE)"
- name: CONTEXT
value: "$(params.CONTEXT)"
- name: EXTRA_ARGS
value:
- "--build-arg=APP_VERSION=$(params.APP_VERSION)"
runAfter:
- fetch-repository
workspaces:
- name: source
workspace: pipeline-workspace
- name: dockerconfig
workspace: docker-credentials
TriggerBinding oluşturulması.
Öncelikle veriyi dışarıdan karşılayacak olan TriggerBinding
objesini aşağıdaki gibi oluşturup üstüne konuşalım.
# trigger-binding.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: image-build-and-push-trigger-binding
spec:
params:
- name: repository_url
value: $(body.repository_url)
- name: git_revision
value: $(body.git_revision)
- name: app_version
value: $(body.app_version)
- name: image_push_addr
value: $(body.image_push_addr)
- name: image_build_context
value: $(body.image_build_context)
- name: dockerfile_path
value: $(body.dockerfile_path)
Parametre tanımında, $body context’inin kullanıldığına dikkat edelim. Gelen HTTP isteğinin taşıdığı body, Tekton tarafında kullanılmak istenen değişken isimleriyle maplenerek payload karşılanmış oluyor.
Burada tanımlamış olduğumuz parametreler interceptor ile bir kontrolde yapmadığımız için doğrudan bu binding ile bağlı olan TriggerTemplate
objesine aktarılacaktır.
⚠️ Bizim örnek senaryomuz için şu anda verinin validate edilmemesi pek fazla problem teşkil etmiyor fakat dış kaynaklara tamamiyle açık bir EventListener
kullanımında binding üstünden geçebilecek potansiyel zararlı bir verinin interceptor
‘lar yardımıyla validate edilmesi iyi bir yaklaşım olacaktır.
TriggerTemplate oluşturulması.
Hatırlarsanız, bir Pipeline veya Task yürüteceğimizde objeye uygun örnegin PipelineRun, TaskRun tanımını da gerçekleştirip bunu cluster üzerinde apply ediyorduk. TriggerTemplate’de aslında event handle edileceğinde nasıl bir objenin hangi parametreleri kullanılarak yaratılacağının bir kalıbı. Tanımı üzerindeki kalıpla işlemi başlatacak olan nesneyi oluşturuyor.
# trigger-template.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: build-push-trigger-template
spec:
params:
- name: repository_url
- name: git_revision
- name: image_push_addr
- name: app_version
- name: dockerfile_path
- name: image_build_context
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: image-build-and-push-pipeline-run-
spec:
pipelineRef:
name: image-build-and-push
podTemplate:
securityContext:
fsGroup: 65532
workspaces:
- name: pipeline-workspace
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: docker-credentials
secret:
secretName: docker-credentials
params:
- name: repo-url
value: "$(tt.params.repository_url)"
- name: revision
value: "$(tt.params.git_revision)"
- name: IMAGE
value: "$(tt.params.image_push_addr)"
- name: DOCKERFILE
value: "$(tt.params.dockerfile_path)"
- name: CONTEXT
value: "$(tt.params.image_build_context)"
- name: APP_VERSION
value: "$(tt.params.app_version)"
Fark edeceğiniz üzere aslında bir önceki blog serisinde manuel olarak oluşturup apply etmiş olduğumu PipelineRun objesiyle pek bir fark barındırmamakta.
Dikkat edilmesi ilk nokta TriggerTemplate nesnesinin, kendisine TriggerBinding üzerinden geçilmiş olan değişken isimleriyle uyuşan bir parametre listesi kabul etmesi ve resourceTemplates
keyi altında PipelineRun
tanımının barındırılıyor olması.
Bir diğer önemli nokta ise, bu parametrelerin PipelineRun objesine aktarılırken $(tt.params.X)
şeklinde kullanılması. Bunun nedeni de tahmin edebileceğiniz üzere, TriggerTemplate
nesnesinin farklı senaryolarda yeniden kullanılabilir olmasını sağlamak. Bu context ayrımı sayesinde template daha modüler hale gelir ve başka trigger’larda da tekrar kullanılabilir.
⚠️
docker-credentials
adlı bir önceki yazıda tanımlanan secretin cluster üstünde hala tanımlı olduğundan emin olalım.Eğer tanımlı değilse bir önceki yazıya ufak bir dönüş yapabilirsiniz.
EventListener oluşturulması.
Buraya kadar listener tanımında bulunması gereken en önemli iki bileşeni oluşturduk ve artık dışarıdan gelecek istekleri karşılayacak olan EventListener
tanımını yapabiliriz.
Öncelikle tanımını yapalım ve üzerine konuşalım.
# event-listener.yaml
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: image-build-and-push-event-listener
spec:
serviceAccountName: tekton-triggers-admin-sa
triggers:
- name: image-build-and-push-trigger
bindings:
- ref: image-build-and-push-trigger-binding
template:
ref: image-build-and-push-trigger-template
Önce aşina olduklarımızla başlayalım.
Triggers listesinde, biraz önce oluşturduğumuz TriggerTemplate ve TriggerBinding isimlerini referans gösteriyoruz ki Trigger’in tam tanımlı hale gelsin.
Burada son bir kez daha tekrar etmenin kavramları daha da oturtacağını düşünüyorum. Bir başka deyişle diyoruz ki bu event listener dışarıdan bir HTTP isteği alıp bir eventi handle edeceğinde hangi veri taşıyıcısını kullanarak hangi kalıpta bir işlem yürütsün!? 🚀
Son olarak, EventListener
‘da her handle ettiği evente karşılık, cluster üstünde objeler oluşturması, listelemsi, gözlemesi vs gerektiğini biliyoruz.
Dolayısıyla -insan olmayan 😄- nesnelerin yetkilendirmesinde kullandığımız bir ServiceAccount
‘a ihtiyacı var. O tanımı da gerçekleştiriyoruz.
Artık kurguladığımız yapıyı test etme zamanı!
⚠️ Not.
EventListener’in kullanacağı serviceaccountı kısaca aşağıdaki şekilde oluşturabilirsiniz. Cluster seviyesinde admin yetkisini vermek gerçek hayat senaryolarında pek önerilmesede blogu takip eden herkesle ortak bir taban sağlama adına burada bunu es geçeceğiz.
EventListener
‘i doğrudan production seviyesinde bir clusterda kullanacaksanız mutlaka ihtiyaçlarınıza yönelik kısıtlamalar yapmalısınız.# service-account.yaml apiVersion: v1 kind: ServiceAccount metadata: name: tekton-triggers-admin-sa --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: tekton-triggers-admin-binding roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: tekton-triggers-admin-sa namespace: default
Test edelim.
Tanımalarını yapmış olduğumuz ServiceAccount, TriggerTemplate, TriggerBinding ve EventListener
objelerini cluster üstüne apply edelim.
Bunu en kısa yoldan yapmak isterseniz Github reposundaki post3 klasörü altında
kubectl apply -f .
# or
kubectl apply -f trigger-template.yaml
kubectl apply -f trigger-binding.yaml
kubectl apply -f service-account.yaml
kubectl apply -f event-listener.yaml
Default namespaceindeki event listener podu Running statüye geçtiyse, artık event listener podunu veya service’ini host makine üzerine yönlendirdikten sonra test edebiliriz.
EventListener
varsayılan olarak ClusterIP
tipinde bir servisi önüne konumlandırır. Fakat farklı servis tipleri ve Ingress objeleriyle kullanımda pek tabi ki mümkündür. Biz şu anda sadece test gerçekleştirmek adında service’in yönlendirme yapmış olduğu 8080 portunu port-forward ederek host üstünde kullanıma açalım.
$ kubectl port-forward services/el-image-build-and-push-event-listener 8080:8080
Dilerseniz gerçek zamanlı loglarını da gözlemek ve pipeline’imizin dispatch edildiğini veya olası hataları takip etmek isterseniz;
$ kubectl logs -f services/el-image-build-and-push-event-listener
Listener 8080 portundan artık istekleri kabul etmeye hazır.
Aşağıdaki gibi bir payload ile süreci test edebiliriz. Image adresini kendi Dockerhub repository’iniz ile değiştirmeyi unutmayın ☺️
Örnek request.
$ curl --location 'http://localhost:8080/' \
--header 'Content-Type: application/json' \
--data '{
"repository_url": "https://github.com/EgehanGundogdu/tekton-pipeline-blog-series",
"git_revision": "main",
"image_push_addr": "docker.io/egundogdu/tekton-blog-series-flask-app",
"dockerfile_path": "Dockerfile",
"image_build_context": ".",
"app_version": "0.48"
}'
Örnek response.
{
"eventListener": "image-build-and-push-event-listener",
"namespace": "default",
"eventListenerUID": "1edcccb6-67e4-4c15-9892-505c4fe6cf16",
"eventID": "aa6f4961-b61c-4d25-bbb0-5ed490977c82"
}
Pushlamıs oldugum 0.48 tag’li Docker imajına aşağıdaki adresten göz atabilir, pullayıp run edebilirsiniz 🚀☺️
https://hub.docker.com/repository/docker/egundogdu/tekton-blog-series-flask-app/
🏁 Sonuç.
Böylece Tekton pipelines serisinin sonuna geldik 🎉 Buraya kadar sabırla takip etmişseniz çok teşekkürler!
İlk yazıda Tekton’ın temel yapı taşlarına göz attık, ikinci yazıda image build & push sürecini kurguladık ve bu son yazıyla birlikte artık dış olaylara tepki veren bir CI/CD sistemine sahip olduk.
Manuel müdahaleye gerek duymayan, olay bazlı (event-driven
) ve esnek bir yapı kurduk.
Buradan sonra yapabilecekleriniz ihtiyaçlarınıza ve hayal gücünüze bağlı;
✅ Interceptor’larla güvenliği artırabilir,
✅ Otomatik test/deploy adımları ekleyebilir,
✅ Farklı pipeline’ları bu yapı üzerinden tetikleyebilirsiniz.
Pek tabii ki Tekton’un yetenekleri bu kadarla sınırlı değil, dolayısıyla dökümanın içine dalıp daha da derinlemesine bilgi sahibi olmak isteyebilirsiniz.
Dökümanın linki: https://tekton.dev/docs/
Eğer bu seri size fayda sağladıysa ve sizin gibi Tekton’a adım atmak isteyen başkaları da varsa, blog serisini paylaşarak daha fazla kişiye ulaşamasını sağlayabilirsiniz.
Her türlü geri bildirim, öneri ve sohbet için yorumlara veya iletişim adresime beklerim.
Görüşmek üzere 👋
İlk Yorumu Siz Yapın