Warning: Experimental and still under development. Not meant for production deployment. Note: This component has only been functionally tested with Kourier and Istio as the networking layer. Other networking layers are currently untested but will be added.
This is an add-on component that, when installed, will enable your Knative services to be called asynchronously. You can set a service to be always or conditionally asynchronous. Conditionally asynchronous services will respond when the Prefer: respond-async
header is provided as a part of the request, while always asynchronous services do not need a special header to be called asynchronously.
When Knative Serving creates a service, one of the artifacts created is a KIngress for the service. The yaml for the KIngress will contain an annotation which is read by the networking controller (net-istio, net-countour, etc.). The ingress controller in our asynchronous component looks for KIngresses with the async.ingress.networking.knative.dev
annotation. The ingress controller will then create a KIngress for Istio (annotated with istio.ingress.networking.knative.dev
), which will be picked up by the net component (net-istio in the above case) to create the required istio components, such as a virtual service, to route asynchronous service calls appropriately. If the service is an always asynchronous service, then all requests are routed to the producer component.
The following is the request flow (seen in blue in the architecture diagram above)
- A new request is made to the application url with the header
Prefer: respond-async
. - The gateway has been modified such that requests with this header are routed to a K8s service in the user namespace.
- This K8s service routes the request to the producer component in the knative-serving namespace, while the producer component returns a
202 Accepted
status to the user. - The Producer is responsible for writing to the queue.
- The Redis Source component watches the queue for new requests.
- The Redis Source component sends cloud events to our Consumer service
- The consumer component reads the cloud event and synchronously makes the service call to the Knative Service.
- Apply the following config files:
ko apply -f config/async/100-async-consumer.yaml ko apply -f config/ingress/controller.yaml
To change this edit the prefix of INGRESS_CLASS_NAME
in the config/ingress/controller.yaml file.
For example change the default kourier:
env:
- name: INGRESS_CLASS_NAME
value: kourier.ingress.networking.knative.dev
To istio:
env:
- name: INGRESS_CLASS_NAME
value: istio.ingress.networking.knative.dev
-
Follow the
Getting Started - Install
Instructions for the Redis Source. -
Update the producer .yaml file with the value for the
REDIS_ADDRESS
. -
Update the tls-secret.yaml file with the cert.pem data key from your cloud instance. This will be the same key used in
Getting Started - Install
instructions. You can then apply it to your cluster.kubectl apply -f config/async/tls-secret.yaml
-
There is a .yaml file in the
async-component
describing theRedisStreamSource
. It points to theasync-consumer
as the sink. First, update theaddress
value in this .yaml file. You can then apply it to your cluster.kubectl apply -f config/async/100-async-redis-source.yaml
-
Follow the
Getting Started
Instructions for the Redis Source. For theExample
section, do not install the entiresamples
folder, as you don't need the event-display sink. Only install redis with:kubectl apply -f samples/redis
. -
Update the producer .yaml file with the value for the
REDIS_ADDRESS
. This should beredis.redis.svc.cluster.local:6379
. -
There is a .yaml file in the
async-component
describing theRedisStreamSource
. It points to theasync-consumer
as the sink. First update the address torediss://redis.redis.svc.cluster.local:6379
. You can then apply it to your cluster.kubectl apply -f config/async/100-async-redis-source.yaml
- Apply the producer config file to install the component:
ko apply -f config/async/100-async-producer.yaml
-
This can be any simple hello world application. There is a sample application that sleeps for 10 seconds in the
test/app
folder. To deploy, use thekubectl apply
command:kubectl apply -f test/app/service.yml
Alternatively you can deploy using a ko compiled image:
ko apply -f test/app/koService.yaml
-
Note that your application has an annotation setting the
ingress.class
asasync.ingress.networking.knative.dev
. This enables just this application to respond to thePrefer: respond-async
header.networking.knative.dev/ingress.class: async.ingress.networking.knative.dev
-
Make note of your application URL, which you can get with the following command:
kubectl get kservice helloworld-sleep
Note: If you don't see the service make sure you are using the default namespace
-
(Optional) If you wanted every service newly created by knative to respond to the
Prefer: respond-async
header, you can configure Knative Serving to use the async ingress class for every service. See here for warnings and details on ingress class handling.kubectl patch configmap/config-network \ -n knative-serving \ --type merge \ -p '{"data":{"ingress.class":"async.ingress.networking.knative.dev"}}'
You can remove this setting by updating the ingress.class to null or by updating the ingress.class to the ingress.class you would like to use, for example
kourier
.kubectl patch configmap/config-network \ -n knative-serving --type merge \ -p '{"data":{"ingress.class":null}}'
kubectl patch configmap/config-network \ -n knative-serving \ --type merge \ -p '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'
-
Curl your application. Try both asynchronous and non asynchronous requests.
curl helloworld-sleep.default.11.112.113.14.xip.io curl helloworld-sleep.default.11.112.113.14.xip.io -H "Prefer: respond-async" -v
-
For the synchronous case, you should see that the connection remains open to the client, and does not close until about 10 seconds have passed, which is the amount of time this application sleeps. For the asynchronous case, you should see a
202
response returned immediately.
-
To set a service to always respond asynchronously, rather than conditionally requiring the header, you can add the following annotation in the
.yml
for the service.async.knative.dev/mode: always.async.knative.dev
-
You can find an example of this (commented) in the
test/app/service.yml
file. Uncomment the annotationasync.knative.dev/mode: always.async.knative.dev
. -
Update the application by applying the
.yaml
file:kubectl apply -f test/app/service.yml
-
Curl the application, this time without the
Prefer: respond-async
header. You should see a202
response returned while some pods are spun up to handle your request.curl helloworld-sleep.default.11.112.113.14.xip.io -v
-
You can see the pods with
kubectl get pods.
Performance testing information can be found in the performance test README.
-
Remove the demo application
kubectl delete -f test/app/service.yml
You can verify deletion by checking the output of:
kubectl get all --namespace=default | grep helloworld-sleep
-
If you previously set every service to respond to the
Prefer: respond-async
header, remember to remove this setting by updatingingress.class
to null, the previous value, or by updatingingress.class
to theingress.class
you would like to use, for examplekourier
.kubectl patch configmap/config-network \ -n knative-serving --type merge \ -p '{"data":{"ingress.class":null}}'
kubectl patch configmap/config-network \ -n knative-serving \ --type merge \ -p '{"data":{"ingress.class":"kourier.ingress.networking.knative.dev"}}'
-
Remove the producer component
ko delete -f config/async/100-async-producer.yaml
You can verify the deletion by checking the output of:
kubectl get all --namespace=knative-serving | grep async-producer
-
Remove the
RedisStreamSource
and tls secretkubectl delete -f config/async/100-async-redis-source.yaml kubectl delete -f config/async/tls-secret.yaml
You can verify deletion by checking the output of:
kubectl get all --namespace=knative-serving | grep redis kubectl get secrets --namespace=knative-serving | grep tls
-
Remove the eventing-redis component - make sure you are using this command in the
eventing-redis
projectko delete -f source/config
You can verify the deletion by checking the output of:
kubectl get all --all-namespaces | grep redis
-
Remove the consumer and async controller components - back in the async component project.
ko delete -f config/ingress/controller.yaml ko delete -f config/async/100-async-consumer.yaml
You can verify deletion by checking the output of:
kubectl get all --namespace=knative-serving | grep async-controller kubectl get all --namespace=knative-serving | grep async-consumer