ElasticSearch slowly uses up to 2GB of RAM. Eventually, on instances provisioned with only 4GB of RAM and without swap, if more than one ElasticSearch pod end up on the same instance, it will cause the instance to slow down and ultimately crash. Instead, we now use a tiny Go web server that shows its environment in JSON. It still highlights that multiple backends are serving requests but without the memory usage issue.
5.8 KiB
Exposing containers
-
kubectl exposecreates a service for existing pods -
A service is a stable address for a pod (or a bunch of pods)
-
If we want to connect to our pod(s), we need to create a service
-
Once a service is created, CoreDNS will allow us to resolve it by name
(i.e. after creating service
hello, the namehellowill resolve to something) -
There are different types of services, detailed on the following slides:
ClusterIP,NodePort,LoadBalancer,ExternalName
Basic service types
-
ClusterIP(default type)- a virtual IP address is allocated for the service (in an internal, private range)
- this IP address is reachable only from within the cluster (nodes and pods)
- our code can connect to the service using the original port number
-
NodePort- a port is allocated for the service (by default, in the 30000-32768 range)
- that port is made available on all our nodes and anybody can connect to it
- our code must be changed to connect to that new port number
These service types are always available.
Under the hood: kube-proxy is using a userland proxy and a bunch of iptables rules.
More service types
-
LoadBalancer- an external load balancer is allocated for the service
- the load balancer is configured accordingly
(e.g.: aNodePortservice is created, and the load balancer sends traffic to that port)
-
ExternalName- the DNS entry managed by CoreDNS will just be a
CNAMEto a provided record - no port, no IP address, no nothing else is allocated
- the DNS entry managed by CoreDNS will just be a
The LoadBalancer type is currently only available on AWS, Azure, and GCE.
Running containers with open ports
- Since
pingdoesn't have anything to connect to, we'll have to run something else
.exercise[
-
Start a bunch of HTTP servers:
kubectl run httpenv --image=jpetazzo/httpenv --replicas=10 -
Watch them being started:
kubectl get pods -w
]
The jpetazzo/httpenv image runs an HTTP server on port 8888.
It serves its environment variables in JSON format.
The -w option "watches" events happening on the specified resources.
Exposing our deployment
- We'll create a default
ClusterIPservice
.exercise[
-
Expose the HTTP port of our server:
kubectl expose deploy/httpenv --port 8888 -
Look up which IP address was allocated:
kubectl get svc
]
Services are layer 4 constructs
-
You can assign IP addresses to services, but they are still layer 4
(i.e. a service is not an IP address; it's an IP address + protocol + port)
-
This is caused by the current implementation of
kube-proxy(it relies on mechanisms that don't support layer 3)
-
As a result: you have to indicate the port number for your service
-
Running services with arbitrary port (or port ranges) requires hacks
(e.g. host networking mode)
Testing our service
- We will now send a few HTTP requests to our pods
.exercise[
- Let's obtain the IP address that was allocated for our service, programmatically:
IP=$(kubectl get svc httpenv -o go-template --template '{{ .spec.clusterIP }}')
-
Send a few requests:
curl http://$IP:8888/ -
Too much output? Filter it with
jq:curl -s http://$IP:8888/ | jq .HOSTNAME
]
--
Our requests are load balanced across multiple pods.
class: extra-details
If we don't need a load balancer
-
Sometimes, we want to access our scaled services directly:
-
if we want to save a tiny little bit of latency (typically less than 1ms)
-
if we need to connect over arbitrary ports (instead of a few fixed ones)
-
if we need to communicate over another protocol than UDP or TCP
-
if we want to decide how to balance the requests client-side
-
...
-
-
In that case, we can use a "headless service"
class: extra-details
Headless services
-
A headless service is obtained by setting the
clusterIPfield toNone(Either with
--cluster-ip=None, or by providing a custom YAML) -
As a result, the service doesn't have a virtual IP address
-
Since there is no virtual IP address, there is no load balancer either
-
CoreDNS will return the pods' IP addresses as multiple
Arecords -
This gives us an easy way to discover all the replicas for a deployment
class: extra-details
Services and endpoints
-
A service has a number of "endpoints"
-
Each endpoint is a host + port where the service is available
-
The endpoints are maintained and updated automatically by Kubernetes
.exercise[
- Check the endpoints that Kubernetes has associated with our
httpenvservice:kubectl describe service httpenv
]
In the output, there will be a line starting with Endpoints:.
That line will list a bunch of addresses in host:port format.
class: extra-details
Viewing endpoint details
-
When we have many endpoints, our display commands truncate the list
kubectl get endpoints -
If we want to see the full list, we can use one of the following commands:
kubectl describe endpoints httpenv kubectl get endpoints httpenv -o yaml -
These commands will show us a list of IP addresses
-
These IP addresses should match the addresses of the corresponding pods:
kubectl get pods -l run=httpenv -o wide
class: extra-details
endpoints not endpoint
endpointsis the only resource that cannot be singular
$ kubectl get endpoint
error: the server doesn't have a resource type "endpoint"
-
This is because the type itself is plural (unlike every other resource)
-
There is no
endpointobject:type Endpoints struct -
The type doesn't represent a single endpoint, but a list of endpoints