Назад | Перейти на главную страницу

соединение kubernetes отклонено во время развертывания

Я пытаюсь добиться развертывания с нулевым временем простоя с помощью кубернетов, и во время моего теста служба плохо балансирует нагрузку.

Мой манифест Kubernetes:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: myapp
        version: "0.2"
    spec:
      containers:
      - name: myapp-container
        image: gcr.io/google-samples/hello-app:1.0
        imagePullPolicy: Always
        ports:
          - containerPort: 8080
            protocol: TCP
        readinessProbe:
          httpGet:
            path: /
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
          successThreshold: 1

---

apiVersion: v1
kind: Service
metadata:
  name: myapp-lb
  labels:
    app: myapp
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: myapp

Если я перейду через сервис с внешним IP-адресом, скажем:

$ kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
kubernetes   ClusterIP      10.35.240.1    <none>           443/TCP        1h
myapp-lb     LoadBalancer   10.35.252.91   35.205.100.174   80:30549/TCP   22m

используя сценарий bash:

while True
    do
        curl 35.205.100.174 
        sleep 0.2s
    done

Я получаю немного connection refused во время развертывания:

curl: (7) Не удалось подключиться к 35.205.100.174 порт 80: В соединении отказано

Приложение по умолчанию привет приложение предоставляется Google Cloud Platform и работает на 8080.

Информация о кластере:

У меня возникла та же проблема, и я попытался немного глубже изучить настройку сети GKE для такого типа LoadBalancing.

Я подозреваю, что правила iptables на узле, на котором запущен контейнер, обновляются раньше времени. Я немного увеличил таймауты в вашем примере, чтобы лучше найти этап, на котором запросы получают тайм-ауты.

Мои изменения в вашем развертывании:

spec:
...
  replicas: 1         # easier to track the state of the system
  minReadySeconds: 30 # give the load-balancer time to pick up the new node
...
  template:
    spec:
      containers:
        command: ["sh", "-c", "./hello-app"] # ignore SIGTERM and keep serving requests for 30s

Все работает до тех пор, пока старый под не перейдет из состояния Running к Terminating. Я тестировал kubectl port-forward на завершающем модуле, и мои запросы обслуживались без тайм-аутов.

Следующие вещи происходят во время изменения с Running к Terminating:

  • Pod-IP удален из службы
  • Проверка работоспособности на узле возвращает 503 с "localEndpoints": 0
  • Правила iptables изменяются на этом узле, и трафик для этой службы отбрасывается (--comment "default/myapp-lb: has no local endpoints" -j KUBE-MARK-DROP

По умолчанию балансировщик нагрузки проверяет каждые 2 секунды и требует 5 сбоев для удаления узла. Это означает, что пакеты отбрасываются не менее 10 секунд. После того, как я изменил интервал на 1 и переключился только после 1 сбоя, количество сброшенных пакетов уменьшилось.

Если вас не интересует исходный IP-адрес клиента, вы можете удалить строку:

externalTrafficPolicy: Local

в определении службы и развертываниях без тайм-аутов подключения.

Протестировано на GKE Cluster с 4 узлами и версией v1.9.7-gke.1.

Глядя на снимок экрана, которым вы поделились в комментариях, это не случай того, что ваш кластер k8s не может принять и правильно ответить на HTTP GET / запрос, а проблема с осадой и тем, как она работает. Я сам сталкивался с этим пару раз.

См. Эту проблему github для справки: https://github.com/JoeDog/siege/issues/127

Проблема в том, что по умолчанию siege закрывает каждое соединение, оставляя порт в состоянии TIME_WAIT, что означает, что его нельзя повторно использовать в течение некоторого времени. На вашем сервере просто заканчиваются доступные порты.

В основном, когда вы использовали все доступные эфемерные порты. Вы можете проверить доступный диапазон портов с помощью:

sysctl net.ipv4.ip_local_port_range

И сколько времени им потребуется, чтобы перейти от TIME_WAIT к CLOSE с помощью:

sysctl net.ipv4.tcp_fin_timeout

На рабочем столе Linux я использую в данный момент следующие значения:

sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768    60999
sysctl net.ipv4.tcp_fin_timeout
net.ipv4.tcp_fin_timeout = 60

Это означает, что он не может использовать более 28231 сокета (диапазон доступен от 32768 до 60999) менее чем за 60 секунд. Через 60 секунд те, которые достигли этого количества времени, система будет ждать с момента завершения TCP-соединения, чтобы фактически освободить сокет, чтобы его можно было использовать для новых подключений:

tcp_fin_timeout

Время в секундах, необходимое для получения окончательного FIN, прежде чем сокет всегда будет закрыт. Это строгое нарушение спецификации TCP, но необходимо для предотвращения атак типа «отказ в обслуживании». http://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html

Вот почему вы видите периодически возникающую ошибку вместо того, чтобы просто добраться до точки, где осада вообще перестает формировать соединения.

Если вас интересует более сложное стресс-тестирование развертывания, и учитывая, что вы запускаете тест из тестового экземпляра, который не будет использоваться в производстве, вы можете просто временно уменьшить это значение до более низкого:

sysctl net.ipv4.tcp_fin_timeout=30

А также максимальное увеличение эфемерного диапазона защиты:

sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"

Это изменит эти значения до конца сеанса и вернется к значениям по умолчанию после перезапуска службы.

Если вы хотите сделать изменение постоянным, вы можете перезаписать соответствующие значения в / proc /:

echo "your new port range" > /proc/sys/net/ipv4/ip_local_port_range
echo "your new timeout" > /proc/sys/net/ipv4/tcp_fin_timeout

На самом деле все это сложнее, но этого должно быть достаточно, чтобы провести ваш тест, по крайней мере, немного дольше.

Кроме того, если вы хотите проверить статистику ваших сокетов и состояния некоторых дистрибутивов, классический netstat больше не будет. В этом случае вы можете использовать сс вот так, чтобы проверить сокеты по TIME-WAIT:

 ss  state time-wait