I have an idea to implement and i need to create a cache for the pods IPs in a kubernetes cluster and then use an http request to access the cached IPs.
I'm using Golang and as i'm new in this field i would be so grateful if anyone have an idea how to implement that. I searched a lot in internet but i didn't find any simple examples to use as a start.
I started with a piece of code to get the podlist what i need is to put he podlist in a cache, like that each time a request arrives it will use the cache insead of using the kubernetes api o get the IPs.
kubeClient, err := kubernetes.NewForConfig(cfg)
if err != nil {
fmt.Printf("Error building kubernetes clientset: %v\n", err)
os.Exit(2)}
options := metav1.ListOptions{
LabelSelector: "app=hello",}
podList, _ := kubeClient.CoreV1().Pods("namespace").List(options). What i need is to create a cache for the IPs of hello pods for-example and when an http request arrive to my http server, the request will use directly the cached IPs
I appreciate your help.
Thank you in advance,
There's only one correct answer to such question: Leave it. It's a very, very bad idea. Believe me, with such solution you'll only generate more problems that you will need to solve. What about updating such cache when a Pod gets recreated and both its name and IP changes ?
Pod in kubernetes is an object of ephemeral nature and it can be destroyed and recreated in totally normal circumstances e.g. as a result of scaling down the cluster and draining the node Pods are evicted and rescheduled on a different node, with completely different names and IP addresses.
The only stable manner of accessing your Pods is via a Service that exposes them.
To minimize the latency when receiving each time a new request i need
to use the Ips from a cache instead of trying to get the ip of the pod
from the kubernetes API
It's really reinventing the wheel. Every time a Service is created (except Services without selectors), the corresponding Endpoints object is created as well. And in fact it acts exactly like the caching mechanism you need. It keeps track of all IP addresses of Pods and gets updated if a Pod gets recreated and its IP changes. This way you have a guarantee that it is always up to date. When implementing any cache mechanism you would need to call the kubernetes API anyway, to make sure that a Pod with such IP still exists and if it doesn't, what was created instead of it, with what name, with what IP address. Quite bothersome, isn't it ?
And it is not true that each time you access a Pod you need to make a call to kubernetes API to get its IP address. In fact,Service is implemented as a set of iptables rules on each node. When the request hits the virtual IP of the Service it actually falls into specific iptables chain and gets routed by the the kernel to the backend Pod. Kubernetes networking is really wide topic, so I recommend you to read about it e.g. using the resources I attached in the end, but not going into unnecessary details it's worth mentioning that each time cluster configuration changes (e.g. some Pod is recreated and gets a different IP and the respective Endpoints object, that keeps track of Pods IPs for the specific Service, changes), kube-proxy which runs as a container on every node takes care of updating the above mentioned iptables forwarding rules. If running in iptables mode (which is the most common implementation) kube-proxy configures Netfilter chains so the connection is routed directly to the backend container’s endpoint by the node’s kernel.
So API call is made only when the cluster configuration changes so that kube-proxy can update iptables rules. Normally when you're accessing the Pod via a Service, the traffic is routed to backend Pods based on current iptables rules without a need for asking kubenetes API about the IP of such Pod.
In fact, Krishna Chaurasia have already answered your question (shortly but 100% correct) by saying:
You should not access pod by their IPs. They are not persisted across
pod restarts.
and
that's not how K8s work. Requests are forwarded based on the Service
generally and they are redirected towards the matching pods based on
label/selectors. – Krishna Chaurasia 4 hours ago
I can only agree with that. And my reasons for "why" have been explained in detail above.
Additional resources:
Kubernetes Networking Demystified: A Brief Guide
iptables: How Kubernetes Services Direct Traffic to Pods
Kubernetes: Service, load balancing, kube-proxy, and iptables
Network overview on GCP docs or more specifically very nice explanation how kube-proxy works
Related
I have a cluster of Elasticsearch nodes running on different AWS EC2 instances. They internally connect via a network within AWS, so their network and discovery addresses are set up within this internal network. I want to use the python elasticsearch library to connect to these nodes from the outside. The EC2 instances have static public IP addresses attached, and the elastic instances allow https connections from anywhere. The connection works fine, i.e. I can connect to the instances via browser and via the Python elasticsearch library. However, I now want to set up sniffing, so I set up my Python code as follows:
self.es = Elasticsearch([f'https://{elastic_host}:{elastic_port}' for elastic_host in elastic_hosts],
sniff_on_start=True, sniff_on_connection_fail=True, sniffer_timeout=60,
sniff_timeout=10, ca_certs=ca_location, verify_certs=True,
http_auth=(elastic_user, elastic_password))
If I remove the sniffing parameters, I can connect to the instances just fine. However, with sniffing, I immediately get elastic_transport.SniffingError: No viable nodes were discovered on the initial sniff attempt upon startup.
http.publish_host in the elasticsearch.yml configuration is set to the public IP address of my EC2 machines, and the /_nodes/_all/http endpoint returns the public IPs as the publish_address (i.e. x.x.x.x:9200).
We have localized this problem to the elasticsearch-py library after further testing with our other microservices, which could perform sniffing with no problem.
After testing with our other microservices, we found out that this problem was related to the elasticsearch-py library rather than our elasticsearch configuration, as our other microservice, which is golang based, could perform sniffing with no problem.
After further investigation we linked the problem to this open issue on the elasticsearch-py library: https://github.com/elastic/elasticsearch-py/issues/2005.
The problem is that the authorization headers are not properly passed to the request made to Elasticsearch to discover the nodes. To my knowledge, there is currently no fix that does not involve altering the library itself. However, the error message is clearly misleading.
I have searched before writing this ... All i found is at certain point they are using load balancer hardware or software. But the thing i need is without hardware and Software can we do the load balancing ?.
While i was searching for this i came across the below statement.
"Another way to distribute requests is to have a single virtual IP (VIP) that all clients use. And for the computer on that 'virtual' IP to forward the request to the real servers"
Could you please anyone let me know how to do the Virtual IP load balancing?.
I have searched lots of article but i could not find anything related to VIP configuration or setup. All i found is only theoretical materials.
I need to divide the incoming requests into two applications. In this case both application server should be up and running.
Below is the architecture:
Application Node 1 : 10.66.204.10
Application Node 2 : 10.66.204.11
Virtual IP: 10.66.204.104
Run an instance of Nginx and use it as a load balancing Gateway for connections. There's no difference using virtual IPs to actual IPs - although it helps if your cloud setup is on LAN based IPs for both security and ease.
Depending on your setup there's two paths to go:
Dynamically assign connections to a server. This can be done on a split (evenly distributed) or on one instance until it fills up - then overflow.
Each function has it's own IP assigned. For example, you can configure the Gateway to serve static content itself and request dynamic content from other servers.
Configuring Nginx is a large job. However, it's a relatively well documented process and it shouldn't be hard for you to find a guide that suits your needs.
I understand that IP addresses behind the ELB may change in time, new IP addresses can be added and removed depending on the traffic pattern we have at the moment.
My question is - how does this work with long living connections, e.g. websocket? Let's say I have persistent websocket connection to the web service behind the ELB. When AWS changes the ELB's IP address I'm currently connected to, replacing it with some other, what will happen? I cannot find a good answer in AWS docs.
Thanks,
Vovan
When AWS changes the ELB's IP address I'm currently connected to, replacing it with some other, what will happen? I cannot find a good answer in AWS docs.
In general there are two situations where the ELB's IP addresses will change:
Underlying ELB Failure
Think of the ELB as a scalable cluster of Load Balancers all addressable under a single DNS name, each with an IP address. If one node dies (e.g. due to an underlying hardware failure), the IP will be removed from the DNS record and replaced with a new node.
Clients connected to it at the time of failure will lose their connection and should handle a reconnect. It won't automatically be routed to a 'healthy' part of the ELB.
Traffic Variation
If the ELB is scaled up or down - because of modifications in traffic profile - as mentioned in the forum post linked above, the connections will continue to function for some time, but there is no guarantee of that period (min or max). This is especially notable in cases where the LB is scaled up quickly to meet load ("cliff face" style), as the 'old' ELB nodes may be overwhelmed (or become so) and their ability to process traffic impaired.
Subsequently it does mean developers need to handle reconnections in both cases on the client side.
We have a web instance (nginx) behind a ELB which we manually power on when required.
The web app starts up quickly and returns a successful 200 response when we run wget locally.
However the website will not load as the ELB isn't sending healthcheck requests to the instance. I can confirm this by viewing the nginx access logs.
The workaround I've been using is to remove the web instance from the ELB and add it back in.
This seem to activate the healthchecks again and they are visible from our access logs.
I've edited our Healthcheck settings to allow a longer timeout and raise the Unhealthy Threshold to 3 but this has made no difference.
Currently our Health Check Config is:
Ping Target: HTTPS:443/login
Timeout: 10 sec
Interval: 12 sec
Unhealthy: 2
Healthy: 2
Listener:
HTTPS 443 to HTTPS 443 SSL Cert
The ELB and web instance are both on the same public VPC Security Group which has http/https opened to 0.0.0.0/0
Can anyone help me figure out why the ELB Health checks aren't kicking in as soon as the web instance has started? Is this by design or is there a way of automatically initiating the checks? Thank you.
Niall
Does your instance come up with a different IP address each time you start it?
Elastic Load Balancing registers your load balancer with your EC2 instances using the IP addresses that are associated with your instances. When an instance is stopped and then restarted, the IP address associated with your instance changes. Your load balancer cannot recognize the new IP address, which prevents it from routing traffic to your instances.
— http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#registerinstance
It would seem like the appropriate approach to getting the instance re-associated would be for code running on the web server instance to programmatically register itself with the load balancer via the API when the startup process determines that the instance is ready for web traffic.
Update:
Luke#AWS: "You should be de-registering from your ELB during a stop/start."
— https://forums.aws.amazon.com/thread.jspa?messageID=463835
I'm curious what the console shows as the reason why the instance isn't active in the ELB. There does appear to be some kind of interaction between ELB and EC2 where ELB has some kind of awareness of an instance's EC2 state (e.g. "stopped") that goes beyond just the health checks. This isn't well-documented, but I would speculate that ELB, based on that awareness, decides that it isn't worth bothering with the health checks, and the console may provide something useful to at least confirm this.
It's possible that, given sufficient time, ELB might become aware that the instance is running again and start sending health checks, but it's also possible that instances have a hidden global meta-identifier separate from i-xxxxxx and that a stopped and restarted instance is, from the perspective of this identifier, a different instance.
...but the answer seems to be that stopping an instance and restarting it requires re-registration with the ELB.
I've been using fabric and boto to start up new ec2 hosts for some temporary processing but I've always had trouble knowing when I can connect to the host. The problem is that I can ask ec2 when something is ready but it's never really ready.
This is the process that I've noticed works best (though it still sucks):
Poll ec2 until it says that the host it "active"
Poll ec2 until it has a public_dns_name
Try to connect to the new host in a loop until it accepts the connection
But sometimes it accepts the connection seemingly before it knows about the ssh key pair that I've associated it with and then asks for a password.
Is there a better way to decide when I can start connecting to my ec2 hosts after they've started up? Has anyone written a library that does this nicely and efficiently?
I do the same for #1 and #2, but for #3 I have a code loop that attempts to make a simple TCP connection to the ssh port (22) with short timeouts and retry. When it finally succeeds, it waits five more seconds an then run the ssh command.
The timing and order in which sshd is started and the public ssh key is added to .ssh/authorized_keys may vary depending on the AMI you are running.
Note: I mildly recommend using the public IP address directly instead of the DNS name. The IP address is encoded in the DNS name, so there's no benefit to adding DNS lookups into the process.
EC2 itself doesn't have any way of knowing when your instance is ready to accept SSH connections; it operates on a much lower level than that.
The best way to do this is to update your AMI to have some sort of health servlet. It can be very simple -- just a few lines of web.py script -- that runs at the later stages of startup, and which just returns status code 200 to any HTTP request. By the time that servlet is responding to requests, everything else should be up too, so you can check your instance with exponential backoff on that URL.
If you ever put your instances behind a load balancer (which has its own benefits), this health servlet is required anyway, and has the added benefit of telling the load balancer when an instance has gone down, for any reason. It's just a general best-practice on EC2.