Accessing K8S pods
2024-05-31
Warning
|
This is some note, nothing serious. DO NOT cite from this article. |
1. Forwarding a Local Network Port to a Port in The Pod
When you want to talk to a specific pod without going through a service (for debugging or other reasons), Kubernetes allows you to configure port forwarding to the pod.
This is done through the kubectl port-forward
command. The following command
will forward your machine’s local port 8888
to port 8080
of your
e.g kubia-manual
pod.
$ kubectl port-forward kubia-manual 8888:8080
... Forwarding from 127.0.0.1:8888 -> 8080 ... Forwarding from [::1]:8888 -> 8080
1.1. Connecting to The Pod Through the Port Forwarder
In a different terminal, you can now use curl to send an HTTP request to your pod
through the kubectl port-forward
proxy running on localhost:8888
.
$ curl localhost:8888
You've hit kubia-manual
2. Service Object
Each pod gets its own IP address, but this address is internal to the cluster
and isn’t accessible from outside of it. To make the pod accessible from the
outside, you’ll expose it through a Service object. You’ll create a special
service of type LoadBalancer
, because if you create a regular service (a
ClusterIP
service), like the pod, it would also only be accessible from
inside the cluster. By creating a LoadBalancer
type service, an external load
balancer will be created and you can connect to the pod through the load
balancer’s public IP.
2.1. Creating a Service Object
To create the service, you’ll tell Kubernetes to expose the ReplicationController you created:
-
You’ll also need to obtain the cluster IP of your service (using
kubectl get svc
, for example)
$ kubectl exec kubia-7nog1 -- curl -s http://10.111.249.153
You've hit kubia-gzwli
2.2. Session Affinity on the Service
If you execute the same command a few more times, you should hit a different pod with every invocation, because the service proxy normally forwards each connection to a randomly selected backing pod, even if the connections are coming from the same client.
If, on the other hand, you want all requests made by a certain client to be
redirected to the same pod every time, you can set the service’s
sessionAffinity
property to ClientIP
(instead of None
, which is the
default), as shown in the following listing.
ClientIP
Session Affinity ManifestapiVersion: v1
kind: Service
spec:
sessionAffinity: ClientIP
...
-
Kubernetes supports only two types of service session affinity:
None
andClientIP
. -
Kubernetes services don’t operate at the HTTP level. Services deal with TCP and UDP packets and don’t care about the payload they carry. Because cookies are a construct of the HTTP protocol, services don’t know about them, which explains why session affinity cannot be based on cookies.
2.3. Exposing Multiple Ports in the Same Service
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: kubia
2.4. Using Named Ports
You can give a name to each pod’s port and refer to it by name in the service spec.
kind: Pod
spec:
containers:
- name: kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
3. Connecting to services living outside the cluster
Instead of having the service redirect connections to pods in the cluster, you want it to redirect to external IP(s) and port(s).
This allows you to take advantage of both service load balancing and service discovery. Client pods running in the cluster can connect to the external service like they connect to internal services.
3.1. Service Endpoints
Services don’t link to pods directly. Instead, a resource sits in between—the
Endpoints resource. You may have already noticed endpoints if you used the
kubectl describe
command on your service.
$ kubectl describe svc kubia
Name: kubia Namespace: default Labels: <none> Selector: app=kubia Type: ClusterIP IP: 10.111.249.153 Port: <unset> 80/TCP Endpoints: 10.108.1.4:8080,10.108.2.5:8080,10.108.2.6:8080 Session Affinity: None No events.
An Endpoints resource (yes, plural) is a list of IP addresses and ports exposing a service. The Endpoints resource is like any other Kubernetes resource, so you can display its basic info with kubectl get.
$ kubectl get endpoints kubia
NAME ENDPOINTS AGE kubia 10.108.1.4:8080,10.108.2.5:8080,10.108.2.6:8080 1h
4. Exposing services to external clients
Few ways to make a service accessible externally.
-
NodePort
Service Type-
Each cluster node opens a port on the node itself (hence the name) and redirects traffic received on that port to the underlying service.
-
The service isn’t accessible only at the internal cluster IP and port, but also through a dedicated port on all nodes.
-
-
LoadBalancer
Service Type, an extention ofNodePort
type-
This makes the service accessible through a dedicated load balancer, provisioned from the cloud infrastructure Kubernetes is running on.
-
The load balancer redirects traffic to the node port across all the nodes. Clients connect to the service through the load balancer’s IP.
-
-
Create
Ingress
Resource, radically different mechanism for exposing multiple services through a single IP address-
It operates at the HTTP level (network layer 7) and can thus offer more features than layer 4 services can
-
4.1. Using a NodePort service
By creating a NodePort
service, you make Kubernetes reserve a port on all its
nodes (the same port number is used across all of them) and forward incoming
connections to the pods that are part of the service.
This is similar to a regular service (their actual type is ClusterIP
), but a
NodePort
service can be accessed not only through the service’s internal
cluster IP, but also through any node’s IP and the reserved node port.
This will make more sense when you try interacting with a NodePort
service.
kubia-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: kubia-nodeport
spec:
type: NodePort # service type
ports:
- port: 80 # service's internal cluster IP port
targetPort: 8080 # target port of the backing pods
nodePort: 30123 # service will listen on port 30123, each cluster nodes
selector:
app: kubia
$ kubectl get svc kubia-nodeport
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubia-nodeport 10.111.254.223 <nodes> 80:30123/TCP 2m
EXTERNAL-IP
column shows <nodes>
, indicating the service is accessible
through the IP address of any cluster node. The PORT(S)
column shows both the
internal port of the cluster IP (80
) and the node port (30123
). The service
is accessible at the following addresses:
-
10.11.254.223:80
-
<1st node’s IP>:30123
-
<2nd node’s IP>:30123
-
and so on
You can find the IP in the JSON or YAML descriptors of the nodes. But instead
of sifting through the relatively large JSON, you can tell kubectl
to print
out only the node IP instead of the whole service definition
$ kubectl get nodes -o \
jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'
130.211.97.55 130.211.99.206
Once you know the IPs of your nodes, you can try accessing your service through them.
$ curl http://130.211.97.55:30123
You've hit kubia-ym8or
$ curl http://130.211.99.206:30123
You've hit kubia-xueq1
4.2. Exposing a service through an external load balancer
Kubernetes clusters running on cloud providers usually support the automatic
provision of a load balancer from the cloud infrastructure. All you need to do
is set the service’s type to LoadBalancer
instead of NodePort
. The load
balancer will have its own unique, publicly accessible IP address and will
redirect all connections to your service. You can thus access your service
through the load balancer’s IP address.
If Kubernetes is running in an environment that doesn’t support LoadBalancer
services, the load balancer will not be provisioned, but the service will still
behave like a NodePort
service. That’s because a LoadBalancer
service is an
extension of a NodePort
service. You’ll run this example on Google Kubernetes
Engine, which supports LoadBalancer
services. Minikube doesn’t, at least not
as of this writing.
4.3. Understanding the peculiarities of external connections
You must be aware of several things related to externally originating connections to services.
When an external client connects to a service through the node port (this also includes cases when it goes through the load balancer first), the randomly chosen pod may or may not be running on the same node that received the connection. An additional network hop is required to reach the pod, but this may not always be desirable.
You can prevent this additional hop by configuring the service to redirect external
traffic only to pods running on the node that received the connection. This is done by
setting the externalTrafficPolicy
field in the service’s spec
section
spec:
externalTrafficPolicy: Local
...
Usually, when clients inside the cluster connect to a service, the pods backing the service can obtain the client’s IP address. But when the connection is received through a node port, the packets' source IP is changed, because Source Network Address Translation (SNAT) is performed on the packets.
The backing pod can’t see the actual client’s IP, which may be a problem for some applications that need to know the client’s IP. In the case of a web server, for example, this means the access log won’t show the browser’s IP.
The Local
external traffic policy described in the previous section affects
the preservation of the client’s IP, because there’s no additional hop between
the node receiving the connection and the node hosting the target pod (SNAT
isn’t performed).
5. Exposing services externally through an Ingress resource
You must be aware of several things related to externally originating connections to services.
-
each LoadBalancer service requires its own load balancer with its own public IP address, whereas an Ingress only requires one, even when providing access to dozens of services
-
When a client sends an HTTP request to the Ingress, the host and path in the request determine which service the request is forwarded to
-
Ingresses operate at the application layer of the network stack (HTTP) and can provide features such as cookie-based session affinity and the like, which services can’t
To make Ingress resources work, an Ingress controller needs to be running in the cluster.
5.1. Creating an Ingress resource
kubia-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kubia
spec:
rules:
- host: kubia.example.com # maps the domain name to your service
http:
paths:
- path: / # All requests will be sent to port 80
backend: # of the kubia-nodeport service.
serviceName: kubia-nodeport #
servicePort: 80 #
This defines an Ingress with a single rule, which makes sure all HTTP requests
received by the Ingress controller, in which the host kubia.example.com
is
requested, will be sent to the kubia-nodeport
service on port 80
.
Note
|
Ingress controllers on cloud providers (in GKE, for example) require the
Ingress to point to a NodePort service. But that’s not a requirement of
Kubernetes itself.
|
5.2. Accessing the service through the Ingress
To access your service through http://kubia.example.com, you’ll need to make sure the domain name resolves to the IP of the Ingress controller
$ kubectl get ingresses
NAME HOSTS ADDRESS PORTS AGE kubia kubia.example.com 192.168.99.100 80 29m
Note
|
When running on cloud providers, the address may take time to appear, because the Ingress controller provisions a load balancer behind the scenes. |
-
The IP is shown in the
ADDRESS
column.
Tip
|
Once you know the IP, you can then either configure your DNS servers to resolve
kubia.example.com to that IP or you can setup hosts: add the following line to
/etc/hosts 192.168.99.100 kubia.example.com So you can access the service at http://kubia.example.com using a browser or
|
5.2.1. Understading how Ingress work
-
The client first performed a DNS lookup of kubia.example.com, and the DNS server (or the local operating system) returned the IP of the Ingress controller
-
The client then sent an HTTP request to the Ingress controller and specified
kubia.example.com
in theHost
header-
From that header, the controller determined which service the client is trying to access, looked up the pod IPs through the Endpoints object associated with the service, and forwarded the client’s request to one of the pods.
-
-
The Ingress controller don’t forward the request to the service
-
It only use it to select a pod
-
Most, if not all, controllers work like this
-
5.3. Exposing multiple services through the same Ingress
Both rules
and paths
are arrays, so they can contain multiple items