How can I dynamicallly start kubernetes pods when the first request arrives? - kubernetes

Let's say I have multiple endpoints in my application that are exposed as different Kubernetes services via an ingress controller.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /service1
backend:
serviceName: service1
servicePort: 80
- path: /service2
backend:
serviceName: service2
servicePort: 80
Let us say service2 endpoint does not receive requests for a long time, so a serverless strategy is appropriate for it. Can I configure a Kubernetes ingress controller to dynamically scale service deployments up when a request arrives after a long time to service2 and shut down the pods for service2 when no request arrives for a long time?

Nginx ingress can not be used for serverless. You can use knative for this use case.

I don't think ingress controller can auto scale pods. Look at kubernetes autoscalar (https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/), this can be used to horizontally scale pods of your deployment (or other resources where replicas can be specified).

Check the custom metrics support added to HorizontalPodAutoscalar.
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics
Your application could expose a custom metric (e.g. a number of HTTP requests) to a collector say Prometheus which then feeds these custom metrics to the Prometheus Adapter (a custom API server).
Not sure if you could start with 0 replicas and scale to 1 (and above) when a first request arrives.

Related

Kubernetes ingress to pod running on same host?

We are just getting started with k8s (bare metal on Ubuntu 20.04). Is it possible for ingress traffic arriving at a host for a load balanced service to go to a pod running on that host (if one is available)?
We have some apps that use client side consistent hashing (using customer ID) to select a service instance to call. The service instances are stateless but maintain in memory ML models for each customer. So it is useful (but not essential) to have repeated requests for a given customer go to the same service. Then we can just use antiAffinity to have one pod per host.
Our existing service discovery mechanism lets the clients find all the instances of the service and the nodes they are running on. All our k8s nodes are running the Nginx ingress controller.
I finally got this figured out. This was way harder than it should be IMO! Update: It's not working. Traffic frequently goes to the wrong pod.
The service needs externalTrafficPolicy: Local (see docs).
apiVersion: v1
kind: Service
metadata:
name: starterservice
spec:
type: LoadBalancer
selector:
app: starterservice
ports:
- port: 8168
externalTrafficPolicy: Local
The Ingress needs nginx.ingress.kubernetes.io/service-upstream: "true" (service-upstream docs).
The nginx.ingress.kubernetes.io/server-alias: "~^starterservice-[a-z0-9]+\\.example\\.com" bit is because our service discovery updates DNS so each instance of the service includes the name of the host it is running on in its DNS name.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: starterservice
namespace: default
annotations:
nginx.ingress.kubernetes.io/server-alias: "~^starterservice-[a-z0-9]+\\.example\\.com"
nginx.ingress.kubernetes.io/service-upstream: "true"
spec:
rules:
- host: starterservice.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: starterservice
port:
number: 8168
So now a call https://starterservice-foo.example.com will go to the instance running on k8s host foo.
I believe Sticky Sessions is what you are looking for. Ingress does not communicate directly with pods, but with services. Sticky sessions try to bind requests from the same client to the same pod by setting an affinity cookie.
This is used for example with SignalR sessions, where the negotiation request has to be on the same host as the following websocket connection.
Example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/affinity: cookie
nginx.ingress.kubernetes.io/affinity-mode: persistent
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Affinity mode "balanced" is the default. If your pod count does not change, part of your clients will lose the session. Use "persistent" to have users connect to the same pod always (unless it dies of course). Further reading: https://github.com/kubernetes/ingress-nginx/issues/5944

kubernetes - route ingress traffic to specific pod for some paths

I have multiple pods, that scale up and down automatically.
I am using an ingress as entry point. I need to route external traffic to a specific pod base on some conditions (lets say path). At the point the request is made I am sure the specific pod is up.
For example lets say I have domain someTest.com, that normally routes traffic to pod 1, 2 and 3 (lets say I identify them by internal ips - 192.168.1.10, 192.168.1.11 and 192.168.1.13).
When I call someTest.com/specialRequest/12, I need to route the traffic to 192.168.1.12, when I call someTest.com/specialRequest/13, I want to route traffic to 192.168.1.13. For normal cases (someTest.com/normalRequest) I just want to do the lb do his epic job normally.
If pods scale up and 192.168.1.14 appears, I need to be able to call someTest.com/specialRequest/14 and be routed to the mentioned pod.
Is there anyway I can achieve this?
Yes, you can easily achieve this using Kubernetes Ingress. Here is a sample code that might help:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: YourHostName.com
http:
paths:
- path: /
backend:
serviceName: Service1
servicePort: 8000
- path: /api
backend:
serviceName: Service2
servicePort: 8080
- path: /admin
backend:
serviceName: Service3
servicePort: 80
Please not that the ingress rules have serviceNames and not pod names, so you will have to create services for your pods. Here is an example for a service which exposes nginx as a service in Kubernetes:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
io.kompose.service: nginx
spec:
ports:
- name: "80"
port: 80
targetPort: 80
selector:
io.kompose.service: nginx
I am not aware of built-in functionality to implement this (if this is what your really want). You can achieve this by building your own operator for Kubernetes. Your operator may provision a Pod+Ingress combo which will do exactly what you want - forward your traffic to a single pod, or you can provision 2 pods and 1 ingress to achive HA setup.
Depending on the Ingress you are using, it also may be possible to group multiple ingress resources under the same load balancer.
Here is a brief diagram of how this could look like.
would it be feasible to create another application
that can get the path and target the pod directly via
a pattern in the naming convention? for example
${podnamePrefix+param}.${service name}.${namespace}.svc.cluster.local

Kubernetes POD versions

Does Kubernetes allow multiple versions of same micro service (not Kubernetes service) to be deployed in a Kubernetes cluster at any given point in time?
So, let's say I have a micro-service "MyCalculator". I create a POD to host this service.
this is v1 of the "MyCalculator".
I need that as new versions of the services are created, Kubernetes cluster allows these services to be deployed in the cluster, like so.
"MyCalculatorKubeService"/v1
"MyCalculatorKubeService"/v2
...
"MyCalculatorKubeService"/vn
The idea here is dynamically allow new versions, and allow access to these versions through a convention specified above. In the example above, I am thinking that the "MyCalculatorKubeService" is a kubernetes service.
The unused versions get decommissioned over a period of time through a manual trigger after reviewing which services are not getting any traffic for say x days.
Thanks,
For this scenario you will have to maintain a separate pod for each versions you want to keep alive. Use ingress to create separate virtual paths to each version of your application
- host: example.com
http:
paths:
- backend:
serviceName: app-v1
servicePort: 80
path: /app/v1/
- backend:
serviceName: app-v2
servicePort: 80
path: /app/v2/
Posting this as Community Wiki as its extension of #Charlie's answer.
As it was mentioned in #Charlie's answer you can do it using Ingress.
There are some Ingress Controllers, however Kubernetes officially supports only two.
Kubernetes as a project currently supports and maintains GCE and nginx controllers.
If you would like to use Nginx Ingress on GKE you need to add annotation to your Ingress manifest.
annotations:
kubernetes.io/ingress.class: nginx
You could also use Nginx Ingress Rewrite annotation.
When you are using Ingress, your services can be NodePort or ClusterIP types.
Depends on your needs you can specify which path should redirect to which service but you can also use default backend if you provide some random path like on example below:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: hello-world
servicePort: 60000
rules:
- http:
paths:
- path: /v1
backend:
serviceName: MyCalculatorKubeService/v1
servicePort: 60000
- path: /v2
backend:
serviceName: MyCalculatorKubeService/v2
servicePort: 80
If you would provide path like MyCalculatorKubeService/vasdsadsa it would redirect to default backend which is hello-world service in this case. It might be used in some workarounds.

One Kubernetes Ingress to front multiple clusters? Scope issues

So I have two clusters at the moment (soon to be a few more, once I get this working), ClusterA and ClusterB.
Is it possible for one ingress to interface with services from both clusters?
ClusterA hosts the front end and the ingress, while ClusterB hosts the back end.
The excerpted ingress is below. Everything bar the back end works.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:{...}
selfLink: /apis/extensions/v1beta1/namespaces/default/ingresses/test-frontend-ingress
uid: //
spec:
backend:
serviceName: idu-frontend-XYZ
servicePort: 80
rules:
- http:
paths:
- backend:
serviceName: test-backend-app-service
servicePort: 8080
path: /api/v2/
- backend:
serviceName: idu-frontend-XYZ
servicePort: 80
path: /
tls:
- secretName: tls-cert-name
status:
loadBalancer:
ingress:
- ip: 123.456.789.012
Back end service URL:
https://console.cloud.google.com/kubernetes/service/asia-southeast1-b/test-backend/default/test-backend-app-service...
URL the ingress tries to point to:
https://console.cloud.google.com/kubernetes/service/asia-southeast1-b/standard-cluster-1/default/test-backend-app-service...
So what I've gathered is the ingress can only interface with things in the same cluster as them? test-backend and standard-cluster-1 are the cluster names, and they are both on the default namespace. Isn't that kind of pointless, as you can only deploy one thing to each cluster? Unless your images contain multiple apps, in which case it isn't really microservices anymore.
Connecting two clusters with Kubernetes is hard I guess.
Instead you can deploy both the services on the same cluster. You can create two deployments and expose them as services. And then ingress can redirect traffic between them.
Why do you need a cluster per service though ?
If there is no other alternative you will have to do something like this:
https://appscode.com/products/voyager/7.1.1/guides/ingress/http/external-svc/

Rewrite all request targets in istio ingress controller

In the process of testing out Istio I'm in need of rewriting all incomming requests on the Istio ingress controller in the same manner as with Kubernetes's own ingress controller, where I use the rewrite-target annotation.
# Existing Kubernetes ingress configuration
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: api
annotations:
ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: some.host.com
http:
paths:
- path: /svc
backend:
serviceName: svc
servicePort: 80
This makes all requests, e.g. http://some.host.com/svc/endpoint hit the service with /endpoint instead of /svc/endpoint
How can I effectively do the same with Istio's ingress controller?
The Route rules object can handle rewrites, but is only available as a per destination manner, i.e. I have to create a route rule for every service.
You are right. You need a route rule per service to do setup the rewrite targets. See https://istio.io/docs/reference/config/traffic-rules/routing-rules.html#httprewrite for an example.