Исправление проблем под Docker. Казалось бы, при чём здесь GIT?

  • Tutorial


Докер под Windows — это постоянные приключения. То ему нужно обновить операционку, иначе последние версии не ставятся, то он забывает, как подключаться к сети. В общем, каждый день от него новости. «Поставил и забыл» — это не про Docker Desktop for Windows. Особенно, когда он используется не совсем так, как рекомендуют его разработчики. А они почему-то не одобряют подключение внешних windows сетевых дисков в качестве локальных. И совсем не одобряют доступ к к таким сетевым папкам, которые расположены ещё и на host машине. Пишут, что это ужас-ужас с точки зрения безопасности, требуют всяких ключей типа:

cap_add:
- SYS_ADMIN
- DAC_READ_SEARCH


для работы команды mount в контейнере и прочая, и прочая.

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

Так что я открываю свою инструкцию и начинаю действовать. Перезапускаю контейнеры — не помогает. Перезапускаю через docker-compose с пересозданием инфраструктуры — не помогает. Сбрасываю настройки Docker к заводским, восстанавливаю параметры виртуалки, загружаю заново образы, запускаю через docker-compose — опять всё по старому — не видит сеть. Точнее не подключается к сетевым шарам, хотя пинг из контейнера до SMB сервера проходит нормально. Последний пункт — перезагрузку сервера и переустановку Docker, пока пропускаю, так как перезагружать сервер очень не хочется. На этом инструкция кончилась.

Ок, перехожу на свою домашнюю машину, тут у меня тоже Docker под Windows, но чуть более новой версии. Проверяю на нём. Те же яйца:

Связи нет, но вы держитесь!

Ага. Ну неужели, думаю, Docker накатил обновление с какой-то безопасностью и теперь мои скрипты из-за этого не запускаются? Последняя проверка — начисто удалить Docker с машины, и поставить заново. Это должно быть круче сброса к заводским настройкам. Проделываю весь перечень из предыдущего шага, только в дополнение к этому ещё и перезагружаю свою машину, чтобы уж совсем железно. Ставлю Docker c нуля, заливаю образы, запускаю docker-compose — ёпрст! Все сервисы как не видели сетевых шар, так и продолжают писать при загрузке «mount error(22): Invalid argument»

Пробую запустить скрипт по строкам из командной строки: подключаюсь к контейнеру, запускаю по очереди команды и вижу, что всё подключается и работает как надо:

mkdir -p $SMB_MOUNT_POINT
mount -t cifs $SMB_SHARE $SMB_MOUNT_POINT -o user=$SMB_USER,password=$SMB_PASSWORD,vers=2.0
ls $SMB_MOUNT_POINT

То есть, это что же, какая-то хрень с передачей параметров в скрипт при запуске контейнера?

Ищем ещё идеи. Все варианты с перезагрузкой докера отмели, остались варианты с возможными изменениями в родительском образе. У меня образ собирается на основе openjdk:8-jdk-alpine, конкретной версии не указано, так что какие-нибудь улучшения безопасности могли сломать мои скрипты. Может поменяли что-то в OpenJDK или дочернем Alpine?

Проверяю логи проекта, пробую выбрать более старые openjdk:8-jdk-alpine-3.8, openjdk:8-jdk-alpine-3.7 и т.д. — каждый раз пересобираю контейнер, проверяю — всё по-старому.

Чёрт подери! Может я что-то всё-таки поменял в своей сборке? Выгружаю из GIT'а версию проекта месячной давности, собираю — те же глюки. Трёхмесячной давности — проблема всё ещё тут. Как же так? Что изменилось? Конфигурация докера к настоящему моменту гарантировано рабочая, конфигурация образа — тоже не поменялась, исходники проекта те же самые (GIT всё сохраняет). Чудес не бывает — надо понять, где всё-таки появились изменения. В проде вручную запускаю команды подключения к шарам — так до перезапуска сервисы будут работать нормально и иду спать. Утро вечера мудренее.

Связи нет, но вы держитесь!

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

Сообщение «mount error(22): Invalid argument» — это не сильно информативно. Нахожу, что есть волшебный ключик -х для баша с которой он выводит отладочную инфу при выполнениии скриптов.

Начинаем отладку внутри sh:

/ # sh -x /entry-point.sh
' echo 'Mount //<private_ip>/Exchange/Pipeline to /pipeline
Mount //<private_ip>/Exchange/Pipeline to /pipeline
' mkdir -p '/pipeline
' mount -t cifs //<private_ip>/Exchange/Pipeline /pipeline -o 'user=<private_user>,password=<private_password>,vers=2.0
mount error(22): Invalid argument
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)
mount: mounting //<private_ip>/Exchange/Pipeline on /pipeline failed: Invalid argument
' ls '/pipeline
+ exec

И тут появляются какие-то непонятные моменты — строка начинается с кавычек, потом кавычки в конце… Откуда кавычки?

Идея — может запуск с помощью настоящего bash будет информативнее?

Инсталлирую в контейнер BASH:

apk add bash

Запускаю отладку:

/ # bash -x /entry-point.sh
' echo 'Mount //<private_ip>/Exchange/Pipeline to /pipeline
Mount //<private_ip>/Exchange/Pipeline to /pipeline
+ mkdir -p $'/pipeline\r'
+ mount -t cifs //<private_ip>/Exchange/Pipeline /pipeline -o $'user=<private_user>,password=<private_password>,vers=2.0\r'
mount error(22): Invalid argument
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)
mount: mounting //<private_ip>/Exchange/Pipeline on /pipeline failed: Invalid argument
+ ls $'/pipeline\r'
+ exec

Блин, тут вроде, когда строка начинается с плюса — это хорошо, но появились какие-то \r и параметры $'...'

Ставим Midnight Commander, чтобы уж экспериментировать с удобствами apk add mc и открываем скрипт на редактирование, а там:



Оппа! ^M в конце каждой строки. Ну-ка, ну-ка, смотрим в локальном проекте — а что у нас с окончаниями строк???? CRLF. Работаем под Windows, однако.

Меняем в этом конкретно файле CRLF на LF (да здравствует Notepad++!), собираем проект — бинго! Работает как надо.

Почему раньше было ок, а сейчас всё полетело? Смотрю по коммитам — не было никаких перемен. И тут вспоминаю, что GIT умеет на лету править символы перевода строк текстовых файлов. А я на днях подключил новый репозитарий, и возможно выгрузил оттуда все файлы с конвертацией в CRLF.

В итоге добавляем в проект файл .gitattributes, с указанием, что в отдельных файлах надо-таки сохранять символы конца строк как в UNIX:

# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Declare files that will always have LF line endings on checkout.
*.sh text eol=lf

Мораль — иногда виновник даже не попадает в круг первоначальных подозреваемых.

Постскриптум


После обновления на последнюю версию Docker Desktop for Windows 2.2.0.3 всё обратно перестало работать. На этот раз я сразу пошёл внутрь контейнеров, и начал отладку. Оказалось, что перестал работать IP адрес 10.0.75.1 для доступа к хост машине, DockerNat теперь удалён из системы, а виртуалка DockerDesktopVM даже не имеет внешнего сетевого адаптера, т.е. связь с ней идёт не через стандартную сеть, а как-то иначе. И для доступа к хосту надо пользоваться host.docker.internal. Товарищи разработчики так и написали, что
DockerNAT has been removed from Docker Desktop 2.2.0.0 as using an IP address to communicate from the host to a container is not a supported feature. To communicate from a container to the host, you must use the special DNS name host.docker.internal.

Ок, поправил конфиги для тестового окружения, база данных подцепилась, пинг из контейнера до host.docker.internal проходит, а вот сетевые диски не подключаются. Пробую запустить mount вручную из шелла, и получаю знакомую ошибку «mount error(22): Invalid argument».

Убираю по очереди аргументы — запускаю просто «mount -t cifs //host.docker.internal/playground /pipeline» — вроде работает, но стучится от пользователя «root».

Добавляю пользователя: mount -t cifs //host.docker.internal/playground /pipeline -o user=smbuser — спрашивает пароль и подключается!

Полный вариант тоже работает:

mount -t cifs //host.docker.internal/playground /pipeline -o user=smbuser,password=smbpassword

а вот "mount -t cifs //host.docker.internal/playground /pipeline -o user=smbuser,password=smbpassword,vers=2.0" не пашет.

Меняю последний параметр на vers=2.1 — ура, работает!

Похоже, что Docker в последней версии сделал свою собственную имплементацию SMB сервера с блекджеком, но без поддержки 2.0. Ерунда, конечно, по сравнению с другими новостями.

Всем добра, сил и упорства!

Иллюстрация для статьи взята с сайта blog.eduonix.com/software-development/learn-debug-docker-containers
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

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

    +1
    По части NAT — наступил на те же грабли. Полез смотреть, что за host.docker.internal — оказывается, оно в файл hosts тупо прописывает адрес хостовой машины. Ну и еще пару алиасов туда же: gateway.docker.internal на тот же хостовой адрес и kubernetes.docker.internal на loopback 127.0.0.1
      0
      Интересно, а как всё-таки они умудрились сделать сеть без сетевого адаптера у виртуалки?
        0

        Сначала нужно убедиться, что это именно так ifconfig -a в помощь. Для поддержки сети вовсе не обязательна эмуляция железа реального сетевого адаптера или виртуальный сетевой адаптер для общения с хост системой можно и в user space весь data link уровень сделать через tun/tap и подобные механизмы. Но конечно существуют и инвазивные методы встраивания в реализацию ipc4/ipv6 в ядре.

          0
          / # traceroute -I host.docker.internal
          traceroute to host.docker.internal (192.168.65.2), 30 hops max, 46 byte packets
           1  172.18.0.1 (172.18.0.1)  0.006 ms  0.006 ms  0.003 ms
           2  192.168.65.2 (192.168.65.2)  0.804 ms  0.868 ms  0.755 ms
          


          Это tun/tap так работает?

          / # ifconfig -a
          eth0      Link encap:Ethernet  HWaddr 02:42:AC:12:00:02  
                    inet addr:172.18.0.2  Bcast:172.18.255.255  Mask:255.255.0.0
                    UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                    RX packets:46 errors:0 dropped:0 overruns:0 frame:0
                    TX packets:49 errors:0 dropped:0 overruns:0 carrier:0
                    collisions:0 txqueuelen:0 
                    RX bytes:3439 (3.3 KiB)  TX bytes:3071 (2.9 KiB)
          
          ip6tnl0   Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
                    NOARP  MTU:1452  Metric:1
                    RX packets:0 errors:0 dropped:0 overruns:0 frame:0
                    TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
                    collisions:0 txqueuelen:1000 
                    RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          
          lo        Link encap:Local Loopback  
                    inet addr:127.0.0.1  Mask:255.0.0.0
                    UP LOOPBACK RUNNING  MTU:65536  Metric:1
                    RX packets:10 errors:0 dropped:0 overruns:0 frame:0
                    TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
                    collisions:0 txqueuelen:1000 
                    RX bytes:698 (698.0 B)  TX bytes:698 (698.0 B)
          
          tunl0     Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
                    NOARP  MTU:1480  Metric:1
                    RX packets:0 errors:0 dropped:0 overruns:0 frame:0
                    TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
                    collisions:0 txqueuelen:1000 
                    RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          
          
      0

      Непонятно зачем такая боль, если можно использовать docker под вменяемыми операционками, просто чтобы показать что умеете готовить кошек?

        0
        Всё гораздо прозаичнее — Docker под Windows я использую потому как разработка изначально была под виндой. И когда пришла необходимость написать пару-тройку микросервисов, которые должны в итоге работать в контейнерах, нужно было где-то отлаживать и контейнерную часть.

        А вот то, что у заказчика контейнеры до сих пор живут под Docker for Windows — это да, артефакт ленивого меня. На время тестирования развернул такую же конфигурацию, как и на разработке, а вот поднять на виртуалке Ubuntu с докером и запустить весь зоопарк оттуда — всё никак не становится главным делом. Да и с IT отделом заказчика надо будет такие вещи согласовывать, а это всегда долго.
        0

        У меня история другая.
        Периодически перестают синхронизироваться тома.
        Причина не известна.
        Даже перезагрузка не помогает.
        Спустя некоторое время все само восстанавливается.


        Docker desctop for Windows

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое