Knative: Serverless для Kubernetes – огляд можливостей та запуск у Minikube

Автор |  06/04/2023
 

Knative – система, яка дозволяє використовувати Serverless модель розробки у Kubernetes. По суті, Knative можна уявляти собі як ще один рівень абстракції, який дозволяє девелоперам не поринати в деталі деплойменту, скелінгу та нетворкінгу у “vanilla” Kubernetes.

Розробка самого Knative була розпочта у Google за співучастю таких компаній, як IBM, Pivotal, Red Hat, та загалом має близько 50 компаній-контрібьюторів.

What is: Serverless computing

Але спочатку, давайте розглянемо що таке Serverless взагалі.

Отже, Server та Less, це модель розробки, коли вам не потрібно перейматись менеджментом серверів – все це бере на себе cloud-провайдер, який надає вам послугу Serverless computing.

Тобто, зазвичай маємо в клауді:

  • bare-metal сервери десь в дата-центрі
  • на яких запускаються віртуальні машини
  • на яких ми запускаємо контейнери

Serverless computing поверх цих шарів додає ще один, де ви можете запускати вашу функцію, тобто мінімальний deployable-юніт, не займаючись ані металом, ані віртуалками, ані контейнерами. Ви просто маєте код, який в пару кліків можна запустити в клауді, а всі задачі по менеджменту інфрастуктури бере на себе клауд-провайдер. Ви не маєте турбуватись ані про хай авайлабіліті, ні про failt-tolerance, ні про бекапи, ні про секьюріті-патчі, ні про бекапи, ні про моніторинг і логування того, що відбувається на рівні інфрастуктури. Ба більше – на рівні мережі вам не треба думати про лоад-балансінг та те, як розподіляти навантаження по вашому сервісу – ви просто приймаєте запити на API Gateway або налаштовуєте триггер на event, який триггерить вашу Функцію. Тобто, cloud provider предоставляє вам послугу Function-as-a-Service, FaaS.

Все це чудово підходить у випадках, коли проект тільки-но стартує, і у девелоперів нема достатнього досвіду та/або часу, щоб піднімати сервери та кластери, тобто – скорочується Time-to-Market, або коли проект тільки тестує свою модель роботи системи взагалі, щоб швидко та безболісно реалізувати свою архітектуру.

“Запусти, та радуйся!”

Крім того, в FaaS інша модель оплати за сервіс – замість того, щоб платити за запущені сервери не зважаючи на те, чи виконують вони якусь роботу, чи просто знаходяться у idle стейті, при використанні FaaS ви платите тільки за той час, коли ваша функція виконується – Pay-for-Use Services.

Отже, ви:

  • створюєте функцію
  • налаштовуєте івенти, за якими ця функця буде запускатися (все ще нічого не платите)
  • при виникненні івента – він тригерить запуск функції (тут вже платите, допоки вона виконується)
  • після завершення роботи функції – вона “схлопується”, і біллінг за неї зупиняється до наступного запуску

Серед провайдерів FaaS – Amazon Web Services з його AWS Lambda, Microsoft Azure та Azure Functions, Google Cloud та Cloud Functions, і IBM Cloud з його IBM Cloud Functions.

Детальніше про Serverless – у CNCF Serverless Whitepaper.

Serverless use cases

Serverless модель буде ідеальною для рішень, які можуть працювати асінхронно та не потребують збереження стану, тобто являються Stateless системами.

Наприклад, це може бути функція в AWS, котора при створенні нового ЕС2 в аккаунті буде автоматично додавати теги до неї, накшталт AWS: Lambda – копирование тегов EC2 на EBS, часть 2 – создание Lambda-функции, коли ми використовуємо CloudWatch, який при створенні ЕС2 створює event, який триггерить функцю, передаючи їй аргументом ID EC2-інстансу, для дисків якого треба додати теги: функція запускається по цьому триггеру, додає теги, та зупинятється до наступного виклику.

Self-hosted serverless

Для використання Serverless моделі, необов’язково прив’язуватись до FaaS-провайдеру, натомість можна запустити self-hosted сервіс наприклад в своєму Kubernetes-кластері, і таким чином уникнути vendor lock.

Серед таких рішень:

  • KubelessIron FunctionsSpace CloudOpenLambdaFunktion, OpenWhisk та Fn Project: наразі, більше мертві, ніж живі, хоча деякі досі мають оновлення
  • Fission: має релізи, тобто розвивається, але займає тільки 2%
  • OpenFaaS:  використовується у 10% випадків
  • Knative: наразі являється найбільш популярним (27%) та найбільше активно розвиваючимся проектом

Knative vs AWS Lambda

Але навіщо взагалі морочитись із запуском власного Serverless?

  • маєте можливість обійти ліміти, задані провайдером – див. AWS Lambda quotas
  • уникаєте vendor lock, тобто маєте можливість швидко переїхати до іншого провайдера або використовувати multi-cloud архітектуру для більшої надійності
  • маєте більше можливостей для моніторингу, трейсінгу та роботи з логами, бо в Kubernetes ми можемо все, на відміну від AWS Lambda та її прив’язки до CloudWatch

Доречі, Knative має змогу запускати функції, розроблені для AWS Labmda завдяки Knative Lambda Runtimes.

Компоненти та архітектура Knative

Knative має два основні компоненти – Knative Serving та Knative Eventing.

Для контролю мережи, роутів та ревізій Knative використовує Istio (хоча може бути і інший, див. Configuring the ingress gateway).

Knative Serving

Knative Serving відповідає за деплоймент контейнеру, оновлення, мережу та автоскейлінг.

Роботу Knative Serving можна відобразити наступним чином:

Трафик від клієнта приходить на Ingress, та в залежності від запиту відправляється на конкретний Knative Service, який являє собою Kubernetes Service та Kubernetes Deployment.

В залежності від конфігурації конкретного Knative Service, трафік може бути розподіленний між різними ревізіями, тобто версіями самого application.

KPA – Knative Pod Autoscaler, перевіряє кількість запитів та при необхідності додає нові поди в Deployment. Якщо трафіка нема – то KPA скейлить поди в нуль, а коли з’являються нові запити від клієнтів – запускає поди та скейлить їх в залежності від кількості запитів, які треба обробити.

Основні типи ресурсів:

  • Service: service.serving.knative.dev відповідає за весь цикл життя вашого workload (тобто, деплойменту та зв’язанних з ним ресурсів) – контролює створення залежних ресурсів, таких як роути, конфігурації, ревізії
  • Routes: route.serving.knative.dev відповідає за зв’язок між ендпоінтом та ревізіями
  • Configurations: configuration.serving.knative.dev відповідає за необхідний стан (desired state) деплойменту та створює revisions, на які буде потрапляти трафік
  • Revisions: revision.serving.knative.dev являє собою point-in-time snapshot коду та конфігурацї для кожної зміни у workload

Див. Resource Types.

Knative Eventing

Knative Eventing відповідає за асинхронний зв’язок розподіленних частин вашої системи. Замість того, щоб робити їх залежними друг від друга, вони можуть створювати events (event producers), які будуть отримані іншим компонентом системи (event consumers або subcsribers, або в термінології Knative – sinks), тобто реалізувати event-driven architecture.

Knative Eventing використовує стандартні запроси HTTP POST для відправлення та отримання таких івентів, які мають відповідати специфікації CloudEvents.

Knative Eventing дозволяє створити:

  • Source to Sink: Source (event producers) відправляє івент (чи таки евент?) до Sink, який його обробляє. В ролі Source може бути PingSource, APIServerSource (Kubernetes API івенти), Apache Kafka, GitHub, тощо
  • Channel and Subscription: створює event pipe, коли івент потрапивши до Channel відразу відправляється до subscriber
  • Broker and Trigger: являє собою event mesh – івент, потрапляє до Broker, який має одного чи більше Trigger, котрі має фільтри, в залежності від яких отримється від Брокера ці events

Install Knative in Minikube

Компоненти Knative можна запустити за допомогою YAML-маніфестів (див. Install Knative Serving by using YAML та Install Knative Eventing by using YAML), використовуючи Knative Operator, або ж за допомогою Knative Quickstart plugin до Knative CLI.

Для знайомства нам підійде і quickstart плагін, тож використаємо його, а запускати будемо у Minikube.

Install Knative CLI

Див. Installing kn.

Для Arch-based дистрибутивів – за допомогою pacman із репозиторію:

[simterm]

$ sudo pacman -S knative-client

[/simterm]

Перевіряємо:

[simterm]

$ kn version
Version:      
Build Date:   
Git Revision: 
Supported APIs:
* Serving
  - serving.knative.dev/v1 (knative-serving v0.34.0)
* Eventing
  - sources.knative.dev/v1 (knative-eventing v0.34.1)
  - eventing.knative.dev/v1 (knative-eventing v0.34.1)

[/simterm]

Запуск Knative з Quickstart plugin

Див. kn-plugin-quickstart.

Знаходимо останню версію на сторінці релізів, вибираємо потрібну версію, в моєму випадку це буде kn-quickstart-linux-amd64, та завантажуємо у каталог, який є в $PATH:

[simterm]

$ wget -O /usr/local/bin/kn-quickstart https://github.com/knative-sandbox/kn-plugin-quickstart/releases/download/knative-v1.8.2/kn-quickstart-linux-amd64
$ chmod +x /usr/local/bin/kn-quickstart

[/simterm]

Перевіряємо (у CLI навіть є автокомпліт):

[simterm]

$ kn plugin list
- kn-quickstart : /usr/local/bin/kn-quickstart

[/simterm]

Перевіряємо:

[simterm]

$ kn quickstart --help
Get up and running with a local Knative environment

Usage:
  kn-quickstart [command]
  ...

[/simterm]

Quickstart плагін замість Istio встановить Kourier, а також створить Minikube кластер та встановить Knative Serving з sslip.io в ролі DNS і Knative Eventing.

Запускаємо:

[simterm]

$ kn quickstart minikube
Running Knative Quickstart using Minikube
Minikube version is: v1.29.0
...
🏄  Done! kubectl is now configured to use "knative" cluster and "default" namespace by default


To finish setting up networking for minikube, run the following command in a separate terminal window:
    minikube tunnel --profile knative
The tunnel command must be running in a terminal window any time when using the knative quickstart environment.

Press the Enter key to continue
...

[/simterm]

В іншому терміналі створюємо minikube-тунель для доступу з хост-машини:

[simterm]

$ minikube tunnel --profile knative &

[/simterm]

Повертаємось до попереднього вікна:

[simterm]

...
🍿 Installing Knative Serving v1.8.5 ...
...
🕸 Installing Kourier networking layer v1.8.3 ...
...
🕸 Configuring Kourier for Minikube...
...
🔥 Installing Knative Eventing v1.8.8 ...
...
🚀 Knative install took: 3m23s 
🎉 Now have some fun with Serverless and Event Driven Apps!

[/simterm]

Перевіряємо кластера Minikube:

[simterm]

$ minikube profile list
|---------|-----------|---------|--------------|------|---------|---------|-------|--------|
| Profile | VM Driver | Runtime |      IP      | Port | Version | Status  | Nodes | Active |
|---------|-----------|---------|--------------|------|---------|---------|-------|--------|
| knative | docker    | docker  | 192.168.49.2 | 8443 | v1.24.3 | Running |     1 |        |
|---------|-----------|---------|--------------|------|---------|---------|-------|--------|

[/simterm]

Неймспейси в Kubernetes:

[simterm]

$ kubectl get ns
NAME               STATUS   AGE
default            Active   7m30s
knative-eventing   Active   5m54s
knative-serving    Active   6m43s
kourier-system     Active   6m23s
...

[/simterm]

Поди в kourier-system:

[simterm]

$ kk -n kourier-system get pod
NAME                                      READY   STATUS    RESTARTS   AGE
3scale-kourier-gateway-7b89ff5c79-hwmpr   1/1     Running   0          7m26s

[/simterm]

Та в knative-eventing:

[simterm]

$ kk -n knative-eventing get pod
NAME                                    READY   STATUS    RESTARTS   AGE
eventing-controller-5c94cfc645-8pxgk    1/1     Running   0          7m10s
eventing-webhook-5554dc76bc-656p4       1/1     Running   0          7m10s
imc-controller-86dbd688db-px5z4         1/1     Running   0          6m39s
imc-dispatcher-5bfbdfcd85-wvpc7         1/1     Running   0          6m39s
mt-broker-controller-78dcfd9768-ttn7t   1/1     Running   0          6m28s
mt-broker-filter-687c575bd4-rpzk7       1/1     Running   0          6m28s
mt-broker-ingress-59d566b54-8t4x7       1/1     Running   0          6m28s

[/simterm]

І в knative-serving:

[simterm]

$ kk -n knative-serving get pod
NAME                                      READY   STATUS      RESTARTS   AGE
activator-66b65c899d-5kqsm                1/1     Running     0          8m17s
autoscaler-54cb7cd8c6-xltb9               1/1     Running     0          8m17s
controller-8686fd49f-976dk                1/1     Running     0          8m17s
default-domain-mk8tf                      0/1     Completed   0          7m34s
domain-mapping-559c8cdcbb-fs7q4           1/1     Running     0          8m17s
domainmapping-webhook-cbfc99f99-kqptw     1/1     Running     0          8m17s
net-kourier-controller-54f9c959c6-xsgp8   1/1     Running     0          7m57s
webhook-5c5c86fb8b-l24sh                  1/1     Running     0          8m17s

[/simterm]

Тепер давайте подвимось, що Knative вміє.

Knative Functions

Functions дозволяє швидко розробляти функції без необхідності писати Dockerfile та взагалі знати Knative та Kubernetes.

Див. About Knative Functions.

Додаємо ще один плагін – kn-func:

[simterm]

$ wget -O /usr/local/bin/kn-func https://github.com/knative/func/releases/download/knative-v1.9.3/func_linux_amd64
$ chmod +x /usr/local/bin/kn-func

[/simterm]

Перевіряємо:

[simterm]

$ kn plugin list
- kn-func : /usr/local/bin/kn-func
- kn-quickstart : /usr/local/bin/kn-quickstart

[/simterm]

Логінимось до Docker Registry (хоча якщо не зробити зараз – то Knative запросить логін під час деплою):

[simterm]

$ docker login

[/simterm]

func create

За допомогою func create створимо функцію на Python, див. Creating a function:

[simterm]

$ kn func create -l python hello-world
Created python function in /home/setevoy/Scripts/Knative/hello-world

[/simterm]

На робочій машині буде створено директорію з її кодом:

[simterm]

$ ll hello-world/
total 28
-rw-r--r-- 1 setevoy setevoy   28 Apr  5 12:36 Procfile
-rw-r--r-- 1 setevoy setevoy  862 Apr  5 12:36 README.md
-rwxr-xr-x 1 setevoy setevoy   55 Apr  5 12:36 app.sh
-rw-r--r-- 1 setevoy setevoy 1763 Apr  5 12:36 func.py
-rw-r--r-- 1 setevoy setevoy  390 Apr  5 12:36 func.yaml
-rw-r--r-- 1 setevoy setevoy   28 Apr  5 12:36 requirements.txt
-rw-r--r-- 1 setevoy setevoy  258 Apr  5 12:36 test_func.py

[/simterm]

У файлі func.yaml описується її конфіг:

[simterm]

$ cat hello-world/func.yaml 
specVersion: 0.35.0
name: hello-world
runtime: python
registry: ""
image: ""
imageDigest: ""
created: 2023-04-05T12:36:46.022291382+03:00
build:
  buildpacks: []
  builder: ""
  buildEnvs: []
run:
  volumes: []
  envs: []
deploy:
  namespace: ""
  remote: false
  annotations: {}
  options: {}
  labels: []
  healthEndpoints:
    liveness: /health/liveness
    readiness: /health/readiness

[/simterm]

А в func.py – сам код, який буде запускатись:

[simterm]

$ cat hello-world/func.py   
from parliament import Context
from flask import Request
import json


# parse request body, json data or URL query parameters
def payload_print(req: Request) -> str:
...

[/simterm]

func run

func run дозволяє перевірити, як функція буде працювати без необхідності її деплою до Knative. Для цього Knative CLI створить образ за допомогою вашого container runtime, та запустить його. Див. Running a function.

Запускаємо, вказуючи шлях до коду:

[simterm]

$ kn func run --path hello-world/
   🙌 Function image built: docker.io/setevoy/hello-world:latest
Function already built.  Use --build to force a rebuild.
Function started on port 8080

[/simterm]

Перевіряємо Docker-контейнери:

[simterm]

$ docker ps
CONTAINER ID   IMAGE                      COMMAND            CREATED          STATUS          PORTS                     NAMES
3869fa00ac51   setevoy/hello-world:latest "/cnb/process/web" 52 seconds ago   Up 50 seconds   127.0.0.1:8080->8080/tcp  sweet_vaughan

[/simterm]

Та пробуємо curl:

[simterm]

$ curl localhost:8080
{}

[/simterm]

В аутпуті func run:

[simterm]

Received request
GET http://localhost:8080/ localhost:8080
  Host: localhost:8080
  User-Agent: curl/8.0.1
  Accept: */*
URL Query String:
  {}

[/simterm]

Або можна перевірити за допомогою func invoke:

[simterm]

$ kn func invoke --path hello-world/
Received response
{"message": "Hello World"}

[/simterm]

І в самій функцї:

[simterm]

Received request
POST http://localhost:8080/ localhost:8080
  Host: localhost:8080
  User-Agent: Go-http-client/1.1
  Content-Length: 25
  Content-Type: application/json
  Accept-Encoding: gzip
Request body:
  {"message": "Hello World"}

[/simterm]

func deploy

func deploy створить образ, запушить його до registry, та задеплоїть у Kubernetes як Knative Service.

Якшо в коді зробити зміни, або просто виконати func build – то в Knative для функції буде створено нову ревізію, на яку буде переключений відповідний route:

[simterm]

$ kn func deploy --build --path hello-world/ --registry docker.io/setevoy
   🙌 Function image built: docker.io/setevoy/hello-world:latest
   ✅ Function updated in namespace "default" and exposed at URL: 
   http://hello-world.default.10.106.17.160.sslip.io

[/simterm]

Перевіряємо под в default namespace:

[simterm]

$ kk get pod 
NAME                                           READY   STATUS    RESTARTS   AGE
hello-world-00003-deployment-74dc7fcdd-7p6ql   2/2     Running   0          9s

[/simterm]

Kubernetes Service:

[simterm]

$ kk get svc 
NAME                                    TYPE           CLUSTER-IP       EXTERNAL-IP                                         PORT(S)                                              AGE
example-broker-kne-trigger-kn-channel   ExternalName   <none>           imc-dispatcher.knative-eventing.svc.cluster.local   80/TCP                                               33m
hello-world                             ExternalName   <none>           kourier-internal.kourier-system.svc.cluster.local   80/TCP                                               3m50s
hello-world-00001                       ClusterIP      10.109.117.53    <none>                                              80/TCP,443/TCP                                       4m1s
hello-world-00001-private               ClusterIP      10.105.142.206   <none>                                              80/TCP,443/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP   4m1s
hello-world-00002                       ClusterIP      10.107.71.130    <none>                                              80/TCP,443/TCP                                       2m58s
hello-world-00002-private               ClusterIP      10.108.40.73     <none>                                              80/TCP,443/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP   2m58s
hello-world-00003                       ClusterIP      10.103.142.53    <none>                                              80/TCP,443/TCP                                       2m37s
hello-world-00003-private               ClusterIP      10.110.95.58     <none>                                              80/TCP,443/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP   2m37s

[/simterm]

Або за допомогою Knative CLI:

[simterm]

$ kn service list
NAME          URL                                                 LATEST              AGE     CONDITIONS   READY   REASON
hello-world   http://hello-world.default.10.106.17.160.sslip.io   hello-world-00003   4m41s   3 OK / 3     True    

[/simterm]

Або можна за kubectl отримати ресурс kvcs:

[simterm]

$ kubectl get ksvc
NAME          URL                                                 LATESTCREATED       LATESTREADY         READY   REASON
hello         http://hello.default.10.106.17.160.sslip.io         hello-00001         hello-00001         True    
hello-world   http://hello-world.default.10.106.17.160.sslip.io   hello-world-00003   hello-world-00003   True

[/simterm]

Визиваємо нашу функцію:

[simterm]

$ kn func invoke --path hello-world/
Received response
{"message": "Hello World"}

[/simterm]

І через хвилину відповідний Pod буде вбито, тобто Deployment заскейлиться в нуль:

[simterm]

$ kk get pod 
NAME                                           READY   STATUS        RESTARTS   AGE
hello-world-00003-deployment-74dc7fcdd-7p6ql   2/2     Terminating   0          79s

[/simterm]

Knative Serving

Functions – чудово, але ж ми начебто інженери, і нам цікаво щось більш наближене до Kubernetes, тож давайте глянемо на Knative Service.

Як вже говорилось, Knative Service – це “повний workload”, включаючи Kubernetes Service, Kubernetes Deployment, Knative Pod Autoscaler та відповідні роути і конфіги. Див. Deploying a Knative Service.

Тут використаємо YAML маніфест (хоча можно і kn service create), в якому описано образ, з якого буде створено под:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: hello
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-samples/helloworld-go
          ports:
            - containerPort: 8080
          env:
            - name: TARGET
              value: "World"

Деплоїмо до Kubernetes:

[simterm]

$ kubectl apply -f knative-hello-go-svc.yaml 
service.serving.knative.dev/hello created

[/simterm]

Перевіряємо Knative Service:

[simterm]

$ kn service list
NAME          URL                                                 LATEST              AGE     CONDITIONS   READY   REASON
hello         http://hello.default.10.106.17.160.sslip.io         hello-00001         70s     3 OK / 3     True    

[/simterm]

Пробуємо з curl:

[simterm]

$ curl http://hello.default.10.106.17.160.sslip.io
Hello World!

[/simterm]

Глянемо поди:

[simterm]

$ kk get pod
NAME                                      READY   STATUS    RESTARTS   AGE
hello-00001-deployment-5897f54974-wdhq8   2/2     Running   0          10s

[/simterm]

Та Kubernetes Deployments:

[simterm]

$ kk get deploy
NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
hello-00001-deployment         1/1     1            1           19m
hello-world-00001-deployment   0/0     0            0           26m
hello-world-00002-deployment   0/0     0            0           25m
hello-world-00003-deployment   0/0     0            0           24m

[/simterm]

Autoscaling

Knative Serving використовує власний Knative Pod Autoscaler (KPA), хоча можна замість нього створити “класичний” Horizontal Pod Autoscaler (HPA).

KPA підтримує scale to zero, але не вміє в CPU-based autoscaling. Крім того, у KPA можемо використовувати concurrency або requests per second метрики для скейлінгу.

HPA ж навпаки – вміє CPU-based autoscaling (і багато іншого), але не знає як скейлити в нуль, а для concurrency або RPS йому потрібні додаткові налаштування (див. Kubernetes: HorizontalPodAutoscaler – обзор и примеры).

Ми вже маємо KPA, які були створені при func deploy та під час створення нашого Knative Service:

[simterm]

$ kk get kpa
NAME                DESIREDSCALE   ACTUALSCALE   READY   REASON
hello-00001         0              0             False   NoTraffic
hello-world-00001   0              0             False   NoTraffic
hello-world-00002   0              0             False   NoTraffic
hello-world-00003   0              0             False   NoTraffic

[/simterm]

Запустимо curl в 10 потоків:

[simterm]

$ seq 1 10 | xargs -n1 -P 10 curl http://hello.default.10.106.17.160.sslip.io

[/simterm]

А в іншому вікні термінала глянемо на kubectl get pod --watch:

[simterm]

$ kk get pod -w
NAME                                      READY   STATUS              RESTARTS   AGE
hello-00001-deployment-5897f54974-9lxwl   0/2     ContainerCreating   0          1s
hello-00001-deployment-5897f54974-9lxwl   1/2     Running             0          2s
hello-00001-deployment-5897f54974-9lxwl   2/2     Running             0          2s

[/simterm]

“Воу, воно скейлиться!” 🙂

Knative Revisions

Knative завдяки Istio (або Kourier чи Kong) вміє розподіляти трафік між різними версіями (revisions) системи, що дозволяє виконувати blue/green або canary deployments.

Див. Traffic splitting.

В нашому Knative Service ми задавали змінну оточення $TARGET зі значенням World – давайте замінимо його на Knative:

[simterm]

$ kn service update hello --env TARGET=Knative
Updating Service 'hello' in namespace 'default':

  0.025s The Configuration is still working to reflect the latest desired specification.
  2.397s Traffic is not yet migrated to the latest revision.
  2.441s Ingress has not yet been reconciled.
  2.456s Waiting for load balancer to be ready
  2.631s Ready to serve.

Service 'hello' updated to latest revision 'hello-00002' is available at URL:
http://hello.default.10.106.17.160.sslip.io

[/simterm]

Перевіряємо ревізії:

[simterm]

$ kn revision list
NAME                SERVICE       TRAFFIC   TAGS   GENERATION   AGE   CONDITIONS   READY   REASON
hello-00002         hello         100%             2            20s   4 OK / 4     True    
hello-00001         hello                          1            24m   3 OK / 4     True    

[/simterm]

Та пробуємо звернутись до ендпоінту:

[simterm]

$ curl http://hello.default.10.106.17.160.sslip.io
Hello Knative!

[/simterm]

Тепер спробуємо поділити трафік – 50 відсотків відправити на попередню ревізію, а 50% – на нову:

[simterm]

$ kn service update hello --traffic hello-00001=50 --traffic @latest=50

[/simterm]

Глянемо revisions зараз:

[simterm]

$ kn revision list
NAME                SERVICE       TRAFFIC   TAGS   GENERATION   AGE    CONDITIONS   READY   REASON
hello-00002         hello         50%              2            115s   4 OK / 4     True    
hello-00001         hello         50%              1            26m    4 OK / 4     True    

[/simterm]

Та знов пробуємо curl, повторюємо кілька раз:

[simterm]

$ curl http://hello.default.10.106.17.160.sslip.io
Hello Knative!
$ curl http://hello.default.10.106.17.160.sslip.io
Hello World!
$ curl http://hello.default.10.106.17.160.sslip.io
Hello Knative!
$ curl http://hello.default.10.106.17.160.sslip.io
Hello World!

[/simterm]

Nice!

Knative Eventing

Knative Eventing – це набор сервісів та ресурсів, які дозволяють нам побудувати event-driven applications, коли Функції викликаються якимось евентом. Для цього можемо підключити sources, які ці події будуть генерувати, та sinks, тобто “споживачі”, які на ці події реагують.

У подальшій документації по Quickstart є приклад роботи з Broker та Trigger за допомогою cloudevents-player, але як на мене, там не дуже-то демонструються можливості Knative Eventing, тож виберемо трошки the hard way.

Як вже писалось, Knative підтримує три типи реалізації event-driven системи – Source to Sink, Channel and Subscription та Broker and Trigger.

Source to Sink

Source to Sink – сама базова модель, створюється за допомогою ресурсів Source та Sink, де Source – це Event Producer, а Sink – ресурс, який можна визвати або адресувати йому повідомлення.

Серед прикладів Sources – APIServerSource (Kubernetes API server), GitHub та GitLab, RabbitMQ та інші, див. Event sources.

В ролі Sink може бути Knative Service, Channel або Broker (тобто, “раковина”, куди ми “зливаємо” наші івенти). Хоча при побудові саме Source to Sink моделі, в ролі sink має бути саме Knative Service – про Channel та Broker поговоримо далі.

Channel and Subscription

Channel та Subscription являє собой even pipe (як pipe в bash, коли ми через | перенаправляємо stdout одніє програми у stdin іншої).

Тут Channel – це інтерфейс між event source та subscriber цього каналу. При цьому channel являє собою тип sink, так як він може зберігати івенти, щоб потім віддати їх своїм subscribers.

Knative підтримує три типи каналів:

  • In-memory Channel
  • Apache Kafka Channel
  • Google Cloud Platform Pub-sub Channel

In-memory Channel – тип за-замовченням, і не має змоги відновити івенти або зберігати їх постійно – для цього використовуйте типи накшталт Apache Kafka Channel.

Далі, Subscription – відповідає за з’єднання Channel з відповідним Knative Service – Сервіси підписуються на Канал через Subscription, та починають отримувати з Каналу повідомлення.

Broker and Trigger

Broker та Trigger являють собою event mesh модель, дозволяючи передавати події необхідним сервісам.

Тут Broker збирає події з різних sources предоставляючи вхідний шлюз дня них, а Trigger по суті є Subscription, але з можливістью фільтрування того, які саме івенти він буде отримувати.

Приклад створення Source to Sink

Створимо Knative Service, який буде нашим sink, тобто отримувачем:

[simterm]

$ kn service create knative-hello --concurrency-target=1 --image=quay.io/redhattraining/kbe-knative-hello:0.0.1
...
Service 'knative-hello' created to latest revision 'knative-hello-00001' is available at URL:
http://knative-hello.default.10.106.17.160.sslip.io

[/simterm]

concurrency-target тут вказує, що наш Service може обробляти тільки один запит одночасно. Якщо їх буде більше – то відповідний KPA створить додаткові поди.

Створюємо Event Source, наприклад – PingSource, який кожну хвилину буде слати повідомлення в вигляді JSON до нашого knative-hello Service:

[simterm]

$ kn source ping create knative-hello-ping-source --schedule "* * * * *" --data '{"message": "Hello from KBE!"}' --sink ksvc:knative-hello
Ping source 'knative-hello-ping-source' created in namespace 'default'.

[/simterm]

Перевіряємо:

[simterm]

$ kn source list
NAME                        TYPE         RESOURCE                          SINK                 READY
knative-hello-ping-source   PingSource   pingsources.sources.knative.dev   ksvc:knative-hello   True

[/simterm]

І глянемо логи поду з Service knative-hello:

[simterm]

$ kubectl logs -f knative-hello-00001-deployment-864756c67d-sk76m
...
2023-04-06 10:23:00,329 INFO  [eventing-hello] (executor-thread-1) ce-id=bd1093a9-9ab7-4b79-8aef-8f238c29c764
2023-04-06 10:23:00,331 INFO  [eventing-hello] (executor-thread-1) ce-source=/apis/v1/namespaces/default/pingsources/knative-hello-ping-source
2023-04-06 10:23:00,331 INFO  [eventing-hello] (executor-thread-1) ce-specversion=1.0
2023-04-06 10:23:00,331 INFO  [eventing-hello] (executor-thread-1) ce-time=2023-04-06T10:23:00.320053265Z
2023-04-06 10:23:00,332 INFO  [eventing-hello] (executor-thread-1) ce-type=dev.knative.sources.ping
2023-04-06 10:23:00,332 INFO  [eventing-hello] (executor-thread-1) content-type=null
2023-04-06 10:23:00,332 INFO  [eventing-hello] (executor-thread-1) content-length=30
2023-04-06 10:23:00,333 INFO  [eventing-hello] (executor-thread-1) POST:{"message": "Hello from KBE!"}

[/simterm]

Наразі це все, що хотілося дізнатись про Knative.

Виглядає в цілому досить цікаво, але можуть бути з автоскейлінгом та Istio, бо Istio сам по собі може бути тим ще гемороєм. Хоча на поточному проекті Knative у нас вже в production, та особливих проблем з ним поки не бачили.

Посилання по темі