Вызов rbd (docker) из kubernetes на coreos возвращает неверный аргумент fork/exec

Я использую Kubernetes v1.2.4 (поверх стабильной версии CoreOS 1010.5.0) и хотел бы смонтировать тома rbd/ceph. В основном я следил за https://github.com/kubernetes/kubernetes/tree/master/examples/rbd за исключением того, что я предпочитаю YAML, а не JSON.

Замечено, что должны быть оба:

secretRef:
  name: ceph-secret

и

keyring: /etc/ceph/keyring

еще kubectl пожаловался. Это ожидаемое поведение?

Кажется, kubelet пытается вызвать двоичный файл rbd непосредственно на хосте (что является проблемой для «голой системы», такой как CoreOS). Поскольку копирование двоичного файла и зависимостей было бы немного громоздким, я сделал этот трюк:

$ cat /opt/bin/rbd
#!/bin/sh
docker run -v /etc/ceph:/etc/ceph ceph/rbd $@

Позаботился о конфигурации /etc/ceph, сделал исполняемым скрипт оболочки и так далее - если я делаю "список rbd" на CoreOS, все работает нормально. /opt/bin (помимо PATH в CoreOS по умолчанию) также находится в PATH для процесса kubelet (что я могу подтвердить через /proc/kubelet pid/environ).

Однако, если я запускаю (тестовый) модуль, я получаю эту ошибку (в описании модуля kubectl):

Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason      Message
  --------- --------    -----   ----                -------------   --------    ------      -------
  5s        5s      1   {default-scheduler }                Normal      Scheduled   Successfully assigned busybox4 to some-host
  4s        4s      1   {kubelet some-host}         Warning     FailedMount Unable to mount volumes for pod "busybox4_default(5386c7f3-3959-11e6-a768-aa00009a7832)": rbd: map failed fork/exec /opt/bin/rbd: invalid argument
  4s        4s      1   {kubelet some-host}         Warning     FailedSync  Error syncing pod, skipping: rbd: map failed fork/exec /opt/bin/rbd: invalid argument

так что либо fork(), либо execve() возвращает EINVAL? Прочитав несколько справочных страниц, я обнаружил, что только exec может потерпеть неудачу с EINVAL из-за

An ELF executable had more than one PT_INTERP segment (i.e., tried to name more than one interpreter)

но это кажется довольно неясным.

Любая идея, в чем дело или как я могу исправить/обойти проблему?

Изменить: я попробовал strace -fp pid, и есть много вызовов stat(), которые, как я полагаю, исходят из golang os/exec LookPath. Однако я не вижу ни одного execve() для "rbd", ни какого-либо системного вызова с ошибкой EINVAL. Чтобы убедиться, что это не связано с флотом (systemd), я также попытался запустить kubelet непосредственно на консоли от имени пользователя root. Результаты такие же.


person fiction    schedule 23.06.2016    source источник
comment
Вы можете проверить github.com/ceph/ceph-docker /tree/master/examples/coreos/rbdmap.   -  person Yu-Ju Hong    schedule 24.06.2016
comment
Проблема в том, что существует несколько узлов, и rbd (или, по крайней мере, ext4 поверх него) небезопасно использовать одновременно на нескольких узлах. github.com/kubernetes/kubernetes/issues/23518 было бы неплохо. Но сомневаюсь, что это произойдет (поскольку интеграция с внешним инструментом CLI менее хрупкая)   -  person fiction    schedule 27.06.2016
comment
@Yu-JuHong - спасибо за дополнение к моему скрипту rbdmap.   -  person hookenz    schedule 01.07.2016


Ответы (3)


Я не очень хорошо знаком с тем, как kubernetes запускает этот скрипт rbd, но я думаю, что проблема в том, что это скрипт. Скрипт нельзя запустить напрямую вызовом exec, который делает kubernetes.

Строка #!/bin/sh в начале файла не запустит для вас оболочку автоматически. Это на самом деле интерпретируется другой оболочкой. Итак, что вы действительно хотите вместо того, чтобы вызывать ваш скрипт /opt/bin/rbd непосредственно в вашей конфигурации kubernetes. Вы хотите изменить его на:

/bin/sh -c "/opt/bin/rbd" ...

И тогда это должно работать.

На самом деле, я бы немного изменил сценарий

#!/bin/sh
exec docker run -v /etc/ceph:/etc/ceph ceph/rbd $@

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

Постоянное хранилище для ваших контейнеров с помощью krbd в кубернете

Дела продвинулись.

person hookenz    schedule 30.06.2016
comment
На самом деле я вообще не ссылаюсь на rbd ни в одном файле конфигурации. Похоже, что kubelet автоматически ищет двоичный файл rbd в PATH и пытается выполнить первый найденный файл (тогда с полным путем). › Дело пошло. У них точно есть с 2015 года ;) Возможно, это неясно из моего поста, но я делаю именно так, как указано в этом руководстве, за исключением того, что сначала на вашем хосте установите Ceph: sudo yum install -y ceph-common это то, что я могу' t делать на CoreOS - вот почему у меня все проблемы с rbd в первую очередь. - person fiction; 02.07.2016
comment
Exec в скрипте — хорошее предложение, оно гарантирует, что не будет другого подпроцесса аля kubelet -> shell -> docker, а просто kubelet -> (shell заменен на docker), верно? Я также подумал, что может быть какая-то проблема, потому что rbd — это сценарий оболочки, а execve() не может с этим справиться, но я создал фиктивную программу Go, которая вызывает exec.Command, и если я настрою ее, я увижу: execve(./test , [./test], [/* 16 переменных /]) = 0 [pid 32147] execve(/opt/bin/rbd, [rbd, list], [/ 16 переменных */] ‹незавершенное...›, значит, это не проблема? - person fiction; 02.07.2016
comment
@fiction - Подойдет ли вам этот сценарий? github.com/ceph/ceph-docker/tree/master/examples/coreos/rbdmap. Я написал его для работы на CoreOS. Он был основан на работе Лорана Барбе, но расширен. Это позволит вам отображать устройства rbd непосредственно на хосте CoreOS (а не внутри контейнера). На самом деле это должно работать на любом Linux. - person hookenz; 04.07.2016

Частичный ответ: не имеет значения, что rbd — это шелл-скрипт. Глядя на вывод strace из kubelet при вызове других внешних инструментов, я понял, что использовался clone(). Я написал короткий тестовый код, чтобы проверить, что происходит, когда он терпит неудачу.

#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>

int test(void *p) {
  printf("Hello there!");
  return 0;
}

int main() {
  if (clone(test, NULL, CLONE_THREAD, NULL) == -1) {
    perror("clone");
  }
  return 0;
}

теперь, если я сделаю

strace ./test 2>&1  | grep clone

вывод

write(2, "clone: Invalid argument\n", 24clone: Invalid argument

Что объясняет одну часть тайны. Когда функция clone() не работает с EINVAL, strace вообще не показывает это.

Затем я просмотрел исходный код Kubernetes и

https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/rbd/rbd_util.go#L218

кажется, работает как шарм

[pid 25039] execve("/usr/sbin/modprobe", ["modprobe", "rbd"], [/* 4 vars */] <unfinished ...>

Интересно, почему вызов на https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/rbd/rbd_util.go#L231 или https://github.com/kubernetes/kubernetes/blob/master/pkg/volume/rbd/rbd_util.go#L234 не будет ( или, в частности, что может привести к сбою этого вызова clone())

person fiction    schedule 02.07.2016

Просто продолжение темы.

Тем временем я обновился до стабильной версии CoreOS (1068.9.0) с Kubernetes v1.3.5.

Мой /opt/bin/rbd выглядит так:

#!/bin/sh
exec docker run -v /dev:/dev -v /sys:/sys --net=host --privileged=true -v /etc/ceph:/etc/ceph ceph/rbd $@

(частично на основе ваших предложений). И теперь все работает как шарм. Так что я предполагаю, что это была какая-то ошибка, которая была исправлена ​​(также и secretRef, и keyring больше не требуются от kubectl). Возможно, кто-то может прокомментировать, в чем заключалась настоящая проблема, но в остальном считайте, что дело закрыто.

person fiction    schedule 27.08.2016