Perhaps I'm not searching correctly or I'm not understanding a fundamental piece of the puzzle.
I have an application which is an ASP.NET web api serving http requests. It will have let's say 3 replicas. In addition to the three replicas, I want a 4th replica (or instance) of the exact same image, but an environment variable changed. This fourth instance will be using all the same code and processing background requests from an AWS Kinesis stream.
To prevent handling the messages more than once, we want the 4th instance to be the only instance handling the kinesis messages.
We're using helm.
I've got this part working pretty well with 2 separate deployments, one has 3 replicas, and the other has 1, easy.
The part I cannot figure out how to make the 4th instance ALSO eligible to serve HTTP requests. Since the kinesis messages are low volume, there's little to no concern with using the resources for the listener pod to also serve http requests.
How can I make a kubernetes service that can load balance between the two deployments?
I've tried something like this
service.yaml
spec:
type: ClusterIP
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
- port: 80
targetPort: http-observer
protocol: TCP
name: http-observer
deployment-api.yaml
ports:
- name: http
containerPort: 8800
protocol: TCP
deployment-observer.yaml
ports:
- name: http-observer
containerPort: 8800
protocol: TCP
This yields the following error though:
Error: Service "my-service-name-here" is invalid: spec.ports[1]: Duplicate value: core.ServicePort{Name:"", Protocol:"TCP", AppProtocol:(*string)(nil), Port:80, TargetPort:intstr.IntOrString{Type:0, IntVal:0, StrVal:""}
Presumeably because I can't have 2 port 80s on the same service.
I want a service exposing port 80 to equally load balance according to whatever k8s HTTP traffic load balancing rules exist naturally between port 8800 on the three api pods and port 8800 on the observer pod. Is this possible? Am I going about this all wrong?
Any guidance would be appreciated.
NOTE: Due to build pipeline, and dependency management, it's important that the two pods remain one code base, so we're not entertaining splitting the kinesis listener code and the API code into two separate apps with a shared library.
CodePudding user response:
What you're trying to achieve is to basically split traffic between different versions of the same application.
The easiest option is to use kubernetes internals for that. You can use one service that serves both deployments by setting the same label/selector on all three.
kind: Service
[...]
spec:
selector:
app.kubernetes.io/name: my-service
ports:
- port: 80
targetPort: http
protocol: TCP
name: http
# Deployment 1
kind: Deployment
[...]
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: my-service
template:
metadata:
labels:
app.kubernetes.io/name: my-service
---
# Deployment 2
kind: Deployment
[...]
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: my-service
template:
metadata:
labels:
app.kubernetes.io/name: my-service
By that kubernetes will split traffic between those 4 pods, where the second deployment is the one with a different environment variable.
If you need more sophisticated traffic splitting you could use something like istio to setup different versions and let istio handle the split, see docs (it's for canary deployments, but you could use it for your use case as well).
CodePudding user response:
The issue you are facing is that you are trying to use the same port number (80
) for both target ports (http
and http-observer
) in the service definition.
One approach to solve this problem is to use a different port number for the second target port. For example, you could use port 8800
for the http
target port and port 8801
for the http-observer
target port. Then, in your deployment-observer.yaml
, you would need to configure the container to listen on port 8801
instead of 8800
.
Another approach is to use a headless service to directly target pods instead of Services. A headless service does not have a ClusterIP and it returns a set of endpoints (Pod IPs and ports) instead of a single virtual IP. This way, you can target both deployments directly, with different ports, and bypass the need to create two services
A third approach is to use a Kubernetes Ingress resource that routes traffic based on the path of the request. This would allow you to expose both deployments on the same port, but route traffic to the appropriate deployment based on the URL path.
The choice of the best approach depends on the specific requirements of your use case and the infrastructure you have in place.