Перехват жизненного цикла Kubernetes после запуска блокирует CNI

#kubernetes #cni

#kubernetes #cni

Вопрос:

Мой объема работы требуется подключение к сети, чтобы начать должным образом, и я хочу использовать postStart жизненный цикл крюк , что ждет, пока он не будет готов, а затем что-то делает. Однако перехваты жизненного цикла, похоже, блокируют CNI; следующей рабочей нагрузке никогда не будет назначен IP:

 kubectl apply -f <(cat <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command:
              - "/bin/sh"
              - "-c"
              - |
                while true; do
                  sleep
                done
EOF
)
kubectl get pods -o wide
  

Это означает, что моя рабочая нагрузка никогда не запускается (зависает при попытке подключения), и мой перехват жизненного цикла вечно зацикливается. Есть ли способ обойти это?

РЕДАКТИРОВАТЬ: я использовал sidecar вместо перехвата жизненного цикла для достижения той же цели — все еще не уверен, почему перехват жизненного цикла не работает, хотя выполнение CNI является частью создания контейнера IMO, поэтому я ожидаю, что перехваты жизненного цикла сработают после настройки сети

Комментарии:

1. В документах для перехватов жизненного цикла вы можете видеть if the hook takes too long to run or hangs, the Container cannot reach a running state. я обычно даю ему задержку в 10 секунд, в моем случае она выполняет свою работу. Вы также могли бы взглянуть на jobs , на случай, если у вас нет приблизительного представления о том, сколько времени потребуется для подключения.

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

Ответ №1:

Это интересный вопрос 🙂 Это не очень хороший ответ, но я провел некоторое расследование и подумал, что поделюсь им — возможно, это принесет какую-то пользу.

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

 $ kubectl get pod -o wide
NAME                    READY   STATUS              RESTARTS   AGE   IP       NODE
nginx-8f59d655b-ds7x2   0/1     ContainerCreating   0          3m    <none>   node-x

$ ssh node-x
node-x$ docker ps | grep nginx-8f59d655b-ds7x2
2064320d1562        881bd08c0b08                                                                                                   "nginx -g 'daemon off"   3 minutes ago       Up 3 minutes                                              k8s_nginx_nginx-8f59d655b-ds7x2_default_14d1e071-4cd4-11e9-8104-42010af00004_0
2f09063ed20b        k8s.gcr.io/pause-amd64:3.1                                                                                     "/pause"                 3 minutes ago       Up 3 minutes                                              k8s_POD_nginx-8f59d655b-ds7x2_default_14d1e071-4cd4-11e9-8104-42010af00004_0
  

Второй запущенный контейнер /pause — это контейнер инфраструктуры. Другой — это контейнер Pod nginx. Обратите внимание, что обычно эта информация также была бы доступна через kubectl get pod , но в данном случае это не так. Странно.

Я ожидаю, что в контейнере настроена сеть и запущен nginx. Давайте убедимся, что:

 node-x$ docker exec -it 2064320d1562 bash
root@nginx-8f59d655b-ds7x2:/# apt update amp;amp; apt install -y iproute2 procps
...installs correctly...
root@nginx-8f59d655b-ds7x2:/# ip a s eth0
3: eth0@if2136: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410 qdisc noqueue state UP group default
    link/ether 0a:58:0a:f4:00:a9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.0.169/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::da:d3ff:feda:1cbe/64 scope link
       valid_lft forever preferred_lft forever
  

Итак, сеть настроена, маршруты установлены, и IP-адрес на eth0 фактически находится в оверлейной сети, как и должно быть. Теперь посмотрим на список процессов:

 root@nginx-8f59d655b-ds7x2:/# ps auwx
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  32652  4900 ?        Ss   18:56   0:00 nginx: master process nginx -g daemon off;
root         5  5.9  0.0   4276  1332 ?        Ss   18:56   0:46 /bin/sh -c while true; do   sleep done
nginx       94  0.0  0.0  33108  2520 ?        S    18:56   0:00 nginx: worker process
root     13154  0.0  0.0  36632  2824 ?        R    19:09   0:00 ps auwx
root     24399  0.0  0.0  18176  3212 ?        Ss   19:02   0:00 bash
  

Хах, значит, nginx запущен, как и команда предварительной остановки. Обратите внимание, однако, на большие PID. В файле развертывания допущена опечатка, и он выполняется sleep без параметров, что является ошибкой.

 root@nginx-8f59d655b-ds7x2:/# sleep
sleep: missing operand
Try 'sleep --help' for more information.
  

Это выполняется из цикла, отсюда и множество разветвлений, приводящих к большим PID.

В качестве другого теста, с узла я также пытаюсь свернуть сервер:

 node-x$ curl http://10.244.0.169
...
<p><em>Thank you for using nginx.</em></p>
...
  

Что очень ожидаемо. Итак, наконец, я хотел бы принудительно завершить команду предварительной остановки, чтобы изнутри контейнера я отключил содержащую оболочку:

 root@nginx-8f59d655b-ds7x2:/# kill -9 5
...container is terminated in a second, result of the preStop hook failure...

$ kubectl get pod
NAME                    READY     STATUS                                                                                                                          RESTARTS   AGE
nginx-8f59d655b-ds7x2   0/1       PostStartHookError: rpc error: code = ResourceExhausted desc = grpc: received message larger than max (53423560 vs. 16777216)   0          21m
  

Хм, итак, я полагаю, что сообщения объемом в 50 МБ (!) были вызваны сбоями из-за отсутствия параметра для перехода в спящий режим. На самом деле, что еще более жутко, так это то, что развертывание не восстанавливается после этого сбоя. Этот модуль продолжает зависать вечно, вместо того, что вы ожидали (создайте другой модуль и повторите попытку).

На этом этапе я удалил развертывание и воссоздал его с режимом ожидания, исправленным в перехвате предварительной остановки ( sleep 1 ). Результаты во многом те же, и в этом случае развертывание также не приведет к появлению другого модуля (так что дело было не только в том, что он захлебнулся журналами).

Теперь я сказал вверху, что на самом деле это не ответ. Но, возможно, некоторый вывод: перехваты жизненного цикла нуждаются в некоторой доработке, прежде чем их можно будет считать полезными и безопасными.

Комментарии:

1. Спасибо за все расследование — да, это была опечатка, не включая секунды с командой sleep, реальный перехват жизненного цикла находится в кластере хранилища, пытающемся его распечатать, но видя ошибки из PID 1, такие как: 2019-03-23T08:33:39.684Z [WARN] storage migration check error: error="Get https://consul:8501/v1/kv/vault/core/migration: dial tcp: i/o timeout" предотвращающий распечатку

2. Как вы говорите, CNI, похоже, настолько запутался в том, почему кажется, что сеть «работает» только после завершения перехвата жизненного цикла — единственное, что я могу думать, это то, что kubelet не публикует назначенный IP, а my overlay (weave) должен сделать что-то дополнительное после его публикации на сервере apiserver — странный

3. Кластер в моем исследовании использует flannel