Я пытаюсь добиться развертывания с нулевым временем простоя с помощью кубернетов, и во время моего теста служба плохо балансирует нагрузку.
Мой манифест 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
:
"localEndpoints": 0
--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