service

Services enables communication with outside to make pods accessible for users or other applications.

service usecase:

  • external communication

To ping from server to pod we need something between outside and pod. This is where a service came to play. It is an object like a replicaset or a pod. One usecase is to listen on a port from a node to a port on a running pod. This service is known as a NodePort service because the service listen on port from a node to a port.

service exports a pod.

service types:

  1. NodePort - which is the service above
  2. ClusterIP - creates a virtual ip inside the clusetr to enable communication as example front- and backend communication inside a node
  3. LoadBalancer - provisioning a load balancer

NodePort

Mapping a port on the node to a port on a pod

3 ports are envolved

  • pod port - targetPort
  • service port - port
    • a virtual server in a node - has its own ip address
  • node port - nodePort
    • can only exported in the default valid range which is 30000 to 327670

create service-definition.yml

---
apiVersion: v1
kind: Service
metadata:
  name: myapp-service

spec:
  type: NodePort
  ports:
    - targetPort: 3000
      port: 3000
      nodePort: 30030

  selector:
    app: myapp
    type: front-end

dont forget to define containerPort in deployment

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-grafana
  labels:
    app: grafana
    type: front-end

spec:
  selector:
    matchLabels:
      app: deployment-grafana
  replicas: 3
  template:
    metadata:
      name: nginx-2
      labels:
        app: grafana

    spec:
      containers:
        - name: grafana
          image: grafana/grafana
          ports:
          - containerPort: 3000 # defines port

spec: is most important part

  • spec:

    • type: NodePort # can be ClusterIP or also LoadBalacer
  • ports:

    • targetPort: 3000
      • port from pod
    • port: 3000
      • port from service
    • nodePort: 30030
      • port from node

If you dont provide a targetPort - service it will use a port from the default valid port range. We have to declare on which pod the service should run.

In this case we defined to use label and selector for this

# aplly service yaml
> kubectl apply -f service-definition.yml

# show services
> kubectl get services
  ---
  myapp-service   NodePort    10.109.132.205   <none>        80:30008/TCP   28s

# get node ip
> minikube service list

# check connection
> nc -zv 192.168.49.2 30030
  Connection to 192.168.49.2 30030 port [tcp/*] succeeded!

> curl http://192.168.49.2:30030
  <a href="/login">Found</a>.
 
> kubectl describe svc myapp-service-grafana
  ---
  Name:                     myapp-service-grafana
  Namespace:                default
  Labels:                   <none>
  Annotations:              <none>
  Selector:                 app=grafana
  Type:                     NodePort
  IP Family Policy:         SingleStack
  IP Families:              IPv4
  IP:                       10.99.172.11
  IPs:                      10.99.172.11
  Port:                     <unset>  8080/TCP
  TargetPort:               3000/TCP
  NodePort:                 <unset>  30030/TCP
  Endpoints:                172.17.0.2:3000,172.17.0.5:3000,172.17.0.3000                                                     
  Events:                   <none>
  Session Affinity:         None
  External Traffic Policy:  Cluster
  ---

# exec printenv
> kubectl exec myapp-deployment-grafana-7d9d9-hjkh -- printenv | grep SERVICE
  ---    
  KUBERNETES_SERVICE_HOST=10.96.0.1
  MYAPP_SERVICE_GRAFANA_SERVICE_PORT=3000
  MYAPP_SERVICE_GRAFANA_PORT_3000_TCP=tcp://10.99.172.11:3000
  MYAPP_SERVICE_GRAFANA_PORT_3000_TCP_PROTO=tcp
  MYAPP_SERVICE_GRAFANA_SERVICE_HOST=10.99.172.11
  MYAPP_SERVICE_GRAFANA_PORT=tcp://10.99.172.11:3000
  KUBERNETES_SERVICE_PORT_HTTPS=443
  MYAPP_SERVICE_GRAFANA_PORT_3000_TCP_PORT=3000
  MYAPP_SERVICE_GRAFANA_PORT_3000_TCP_ADDR=10.99.172.11
  KUBERNETES_SERVICE_PORT=443
  ---

> kubectl get node -o wide
  ---
  NAME       STATUS   ROLES                  AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION                 CONTAINER-RUNTIME
  minikube   Ready    control-plane,master   5h34m   v1.22.1   192.168.49.2   <none>        Ubuntu 20.04.2 LTS   4.18.0-305.12.1.el8_4.x86_64   docker://20.10.8

sometimes minikube needs to be restarted for working correctly - keep an eye on the selector, multi label entrys means that multi labels must also match…

MultiNode NodePort

In multi node pod setup we will using labels instead of ports within selector. k8s automaticly creates a network for using in our service. Only ports are used - no addresses

  • algorythm: Random
  • SessionAffinity: Yes

ClusterIP

The case can be:

  • front-end pod
  • back-end pod
  • database pod

Communication over ip is not really possible cause pod ip’s will change from time to time. We can create a service using a single interface for an application to access the database or back-end connection.

edit clusterip.yml

---
apiVersion: v1
kind: Service 
metadata:
  name: backend

spec:
  type: ClusterIP
  ports:
    - targetPort: 3000
      port: 3000

  selector:
      app: grafana
      type: front-end
# apply service
> kubectl apply -f  clusterip.yml

ClusterIP is the default type


LoadBalancer

Could use a combination of address and port - it will be accessible for all nodes in a cluster.

One way to make an app accessible from a domain is to create a new vm (node) and install a LoadBalancer service like haproxy or nginx on it. Then configure the LB to route traffic to the different nodes which can be a tbs task. We can use also cloud provider integrated services. k8s can handle almost all of this.

edit clusterip.yml

---
apiVersion: v1
kind: Service
metadata:
  name: backend

spec:
  type: LoadBalancer
  ports:
    - targetPort: 3000
      port: 3000
      nodePort: 30030

  selector:
      app: grafana
      type: front-end