Servicio sin cabeza de Kubernetes: ¿Qué pasa si el pod se ha ido?

Encontramos un comportamiento bastante interesante al trabajar con el servicio Headless en Kubernetes. En nuestro caso, el problema ocurrió con mongos, pero es relevante para cualquier servicio Headless. Los invito a leer nuestra historia y tratar de jugar con este problema localmente.





En uno de los proyectos, usamos MongoDB y Kubernetes. MongoDB tiene un componente: mongos. A través de él, las consultas se ejecutan en un clúster MongoDB fragmentado (puede asumir que esto es solo un proxy complicado). Antes de pasar a Kubernetes, los servicios de mongos se instalaron directamente en cada host.





Al mover los servicios a Kubernetes, establecimos el grupo de mongos en un servicio Headless con escalado automático de implementación a través de HPA (Horizontal Pod Autoscaler).





Después de un tiempo, resultó que la aplicación no estaba funcionando muy bien con una disminución en la cantidad de Pods de mongos.





A través de la depuración, resultó que la aplicación se cuelga exactamente cuando intenta establecer una conexión con mongos ( net.Dial



 en términos de Go) y coincide en el tiempo con detener cualquier Pod.





, Headless-: , IP- (ClusterIP: None



). DNS- IP Pod, .





Headless- , , Pod , :





  • mongodb- IP , , , ( «» mongos).  ClusterIP



      «» .





  • gRPC- , .  ClusterIP



      Pod .





, Pod , , IP- Pod. :





  • Pod DNS, DNS ;





  • DNS .





, Pod?





. , .





, Pod Out of Memory, , “connection refused” . , .





, .





  •  SIGTERM



      Pod mongos. 45 DNS ( Pod ). mongos 15 ( IP “connection refused”, ).





  •  terminationGracePeriodSeconds



      , Pod .





minReadySeconds

Pod .





, , IP- ( Pod , ).





 minReadySeconds



. , : IP Pod.





 minReadySeconds



  - , Pod  Terminating



. x2 Pod.





, IP- , DNS  minReadySeconds



.





,  minReadySeconds



  gRPC-: , .





?

MiniKube nginx.





headless Service (service.yml



):





---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

      
      



(dialer.go



):





package main

import (
	"fmt"
	"net"
	"os"
	"time"
)

const timeFormat = "15:04:05.999"

func main() {
	address := os.Args[1]
	last := ""
	ticker := time.NewTicker(time.Millisecond * 100)
	t := time.Now()
	fmt.Printf("%s: === %s\n", t.Format(timeFormat), address)
	for {
		conn, err := net.DialTimeout("tcp", address, time.Millisecond*100)
		var msg string
		if conn != nil {
			msg = fmt.Sprintf("connected (%s)", conn.RemoteAddr())
			_ = conn.Close()
		}
		if err != nil {
			msg = err.Error()
		}
		if last != msg {
			now := time.Now()
			if last != "" {
				fmt.Printf("%s: --- %s: %v\n", now.Format(timeFormat), last, now.Sub(t))
			}
			last = msg
			fmt.Printf("%s: +++ %s\n", now.Format(timeFormat), last)
			t = now
		}
		<-ticker.C
	}
}
      
      



nginx 80- . ( , ):





#!/bin/bash
echo "
tee dialer.go << EEOF
$(cat dialer.go)
EEOF

go run dialer.go nginx:80
" | kubectl --context=minikube run -i --rm "debug-$(date +'%s')" \
            --image=golang:1.16 --restart=Never --
      
      



- :





16:57:19.986: === nginx:80
16:57:19.988: +++ dial tcp: lookup nginx on 10.96.0.10:53: server misbehaving
      
      



.





Deployment

Deployment (nginx.yml



):





---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.14.2
          ports:
            - containerPort: 80

      
      



 replicas



  , IP-.





Deployment  livenessProbe



  readinessProbe



. .





«» Deployment:





#!/bin/bash
kubectl --context minikube rollout restart deployment/nginx
      
      



Deployment. , : Pod Pod. Pod.





( ):





#      Deployment    
#   
17:04:08.288: +++ connected (172.17.0.10:80)
17:07:32.187: --- connected (172.17.0.10:80): 3m23.899438044s
#   nginx   Pod,      
#  IP.
#   Pod ,     "connection refused"
17:07:32.187: +++ dial tcp 172.17.0.10:80: connect: connection refused
17:07:32.488: --- dial tcp 172.17.0.10:80: connect: connection refused: 301.155902ms
#  Pod  ,         IP.
#    IP-    ,   .
17:07:32.488: +++ dial tcp 172.17.0.10:80: i/o timeout
17:07:38.448: --- dial tcp 172.17.0.10:80: i/o timeout: 5.960150161s
#  IP        Pod.
17:07:38.448: +++ connected (172.17.0.7:80)
      
      



Pod

Deployment , “connection refused”:





#!/bin/bash
kubectl --context minikube patch deployment nginx --output yaml --patch '
---
spec:
  template:
    spec:
      containers:
        - name: nginx
          command: [ "sh" ]
          #     nginx
          args:
            - "-c"
            - "nginx -g \"daemon off;\" && sleep 60"
          #  , sh   SIGTERM   
          lifecycle:
            preStop:
              exec:
                command: ["sh", "-c", "nginx -s stop"]
      #  ,     Pod- 
      #   
      terminationGracePeriodSeconds: 180
'
      
      



Pod (  SIGTERM



). , , Out Of Memory Segmentation fault, .





«» Deployment:





#!/bin/bash
kubectl --context minikube rollout restart deployment/nginx
      
      



( ):





#      Deployment    
#   
17:58:10.389: +++ connected (172.17.0.7:80)
18:00:53.687: --- connected (172.17.0.7:80): 2m43.29763747s
#   nginx   Pod,      
#  IP.
#   Pod ,     "connection refused".
#  Pod        sleep  nginx.
18:00:53.687: +++ dial tcp 172.17.0.7:80: connect: connection refused
18:01:10.491: --- dial tcp 172.17.0.7:80: connect: connection refused: 16.804114254s
#  IP        Pod.
18:01:10.491: +++ connected (172.17.0.10:80)
      
      



Pod

Deployment , , Pod :





#!/bin/bash
kubectl --context minikube patch deployment nginx --output yaml --patch '
---
spec:
  template:
    spec:
      containers:
        - name: nginx
          #     nginx
          lifecycle:
            preStop:
              exec:
                command: ["sh", "-c", "sleep 60 && nginx -s stop"]
      #  ,     Pod 
      #   
      terminationGracePeriodSeconds: 180
'
      
      



«» Deployment:





#!/bin/bash
kubectl --context minikube rollout restart deployment/nginx
      
      



( ):





#      Deployment    
#   
18:05:10.589: +++ connected (172.17.0.7:80)
18:07:10.689: --- connected (172.17.0.7:80): 2m0.099149168s
#  IP        Pod.
#  Pod    -    .
18:07:10.689: +++ connected (172.17.0.10:80)
      
      



?

: .





  •  SIGTERM



      — DNS- Pod .





    , DNS-.





    , DNS- .





    IP- ,  SIGTERM



       minReadySeconds



    .





  • Pod, / Pod “connection refused”, .





    ,  SIGTERM



      Pod DNS .





    , , .





.








All Articles