GrabDuck

Docker контейнеры для web-разработчика под OS X

:

Всю свою текущую разработку я веду, используя docker контейнеры, и если под Linux такой подход не вызывает никаких проблем, то под OS X некоторые моменты могут отнять невероятное количество сил и времени.

Об одном из таких моментов я и хочу рассказать.

В моем случае разработка с иcпользованием docker выглядит так: на контейнере поднят весь необходимый мне софт. Папки с рабочим проектом смаплены с хост системы в контейнер. В контейнере для автобилда настроен либо grunt с grunt-contrib-watch, либо watchify, либо порой просто висит скриптик, использующий inotify-tools.

В случае веб-разработки цикл простой: я правлю на маке файлы проекта, watch утилитки стартуют билд, livereload обновляет web страничку. Но при таком подходе сразу возникает проблема — файловая система vboxsf входящая в Virtualbox Guest Additions невероятно медленная, отсюда:

  • watch утилитки начинают грузить систему процентов на 200;
  • даже небольшая сборка — например простая конкатенация файлов начинает занимать десятки секунд.

Это — неприемлемо.

Быстрое гугление показало, что nfs работает в разы быстрее, чем vboxfs. Настроить nfs сервер под OSX быстро и легко.

#включить nfs сервер
sudo nfsd enable
#отредактировать файл с папками для шаринга
sudo vi /etc/exports
# добавить папки которые надо сделать доступными например  /Users/user/my_web_project -mapall=ice
#проверить что нет ошибок
sudo nfsd checkexports
#проверить что папки подцпились
showmount -e

Дальше надо передать в docker ip адрес OSX хоста, чтобы можно было подцепить файловую систему:

#получить ip хоста висящего на интерфейсе virtual box
HOST_IP=`ifconfig vboxnet0 | tail -1 | awk '{print $2}'`
#передать в контейнер
docker run --name smap -p 3080:3080 -e HOST_IP="$HOST_IP" -d -t sentimeta/node_scikit_image

В контейнере надо накатить nfs клиента и замаунтить нужные папки:

#накатить клиента
sudo apt-get update
sudo apt-get install nfs-common
#замаунтить nfs osx папку
sudo mount -o nolock "$HOST_IP":/Users/user/my_web_project ~/my_web_project

И действительно, две проблемы уходят:

  • загрузка процессора около ноля;
  • билд теперь идет очень быстро.

Но появляется новая проблема — inotify события файловой системы nfs, на которых сидят все watch утилитки, проходят, но с задержками по 10-20 секунд.

Это — неприемлемо.

Поэтому было принято решение начать ловить inotify события на host машине и передавать информацию в docker контейнер.

Для решения проблемы я использовал следующие утилитки:

Сторона докера
Вешаем http веб сервер на 3080 порт:

watch -n0 'printf "HTTP/1.1 200 OK\n\n$(date && touch /home/user/my_web_project/watchhelper.tmp)\n" | nc -l -p 3080 >/dev/null && date'

Это самый настоящий веб сервер на bash висящий на 3080 порту, на каждый запрос он выполняет команду date и touch файла watchhelper.tmp, который надо положить вне исходников вашего проекта, который надо добавить в watch. Ниже я поясню, почему вне исходников проекта.
  • touch команда «трогая» файл вызывает inotify событие приводяещее к сборке;
  • date команда удобна для тестирования, ее вывод и есть ответ этого сервера.

Проверить, что работает, можно так:
  • На OSX получаем адрес boot2docker виртуальной машины:
    boot2docker ip 2>&1 | sed -n 2,2p | awk -F' ' '{print $9}'
    


И, ура — в браузере видим время, а в контейнере — мгновенное срабатывание watch утилиток.

Сторона OSX
Устанавливаем fswatch:

brew install fswatch 

Запускаем:

fswatch-run /Users/user/my_web_project/src "curl http://`boot2docker ip 2>&1 | sed -n 2,2p | awk -F' ' '{print $9}'`:3080"

Теперь при изменении любого файла в папке /Users/user/my_web_project/src будет вызван поднятый в контейнере веб сервер, который «потрогает» файл, что в свою очередь вызовет билд.

Файл watchhelper.tmp надо расположить вне исходников проекта по причине, что nfs все-таки пропускает inotify события и лежащий в исходниках файл вызовет вечный цикл curl touch touch событий.

Различные вопросы настройки докер контейнеров под макось я нерегулярно добавляю в проект на гитхабе istarkov/docker.

Если тема вызовет отклик — напишу еще несколько заслуживающих внимания вещей при использовании docker контейнеров при разработке на OS X.

PS:
Мне сообщили о небольшой проблеме с этим подходом.
Если редактор sublime text 3 и длина файла после внесения изменений осталась прежней, то изменения не попадают в nfs клиента, т.е. в docker.

Вылечить можно так — в настройках sublime, в файле Preferences.sublime-settings сменить крыжик {«atomic_save»: true} на {«atomic_save»: false} и все будет работать как надо.

PPS:
В комментариях descentspb дал более простое решение этой проблемы, рекомендую использовать его подход.