Ооочень хочется прям сразу приступить к теме, но правильнее будет немного рассказать про мою историю:
Вступление
Я программист с опытом разработки frontend одностраничных приложений, scala/java и nodejs на сервере.
Довольно долго (уже точно пару — тройку лет), я придерживался мнения, что docker это манна небесная и вообще очень крутой инструмент и абсолютно каждый разработчик должен уметь пользоваться им. А отсюда вытекает, что и у каждого разработчика должен стоять docker на локальной машине. Да что там про моё мнение, вы полистайте вакансии, которые размещаются на том же hh. В каждой второй есть упоминание про docker и если вы им владеете — это будет вашим конкурентным преимуществом 😉
На своем пути я встечался с многими людьми, с их разным отношением к docker и к его экосистеме. Одни говорили, что это удобная вещь, гарантирующая кроссплатформенность. Вторые не понимали зачем им запускаться в контейнерах и какой профит от этого, третьим было вообще пофиг и они не парились (просто писали код и уходили домой — завидую, кстати, им 🙂 )
Причины использования
Почему я использовал docker? Наверное по следующим причинам:
запуск базы данных, 99% приложений пользуются ими
запуск nginx для раздачи frontend и проксирования на backend
можно упаковать приложение в docker образ, таким образом мое приложение будет работать везде, где есть docker, проблема дистрибутации решена уже сразу
service discovery из коробки, можно делать микросервисы, каждый контейнер (подключеный к общей сети) легко достучится до другого по алиасу, очень удобно
прикольно создать контейнер и «поиграться» в нем.
Что мне всегда НЕ нравилось в docker:
для того чтобы мое приложение работало, нужен сам docker на сервере. А зачем мне это, если мои приложения работают на jre или на nodejs и окружение для них уже есть на сервере?
если я хочу запустить свой (приватный) локально собранный образ на удаленном сервере, то мне нужен свой docker репозиторий, нужно чтобы где-то работал registry и еще нужно настроить https, потому что docker cli работает только по https. Ох блин… есть варианты, конечно, сохранить образ локально через docker save и через scp просто скинуть образ… Но это столько телодвижений. И к тому же выглядит «костыльным» решением, пока не появится свой репозиторий
docker-compose. Он нужен только для запуска контейнеров. И все. Больше ничего он не может. Docker-compose имеет кучу версий своих файлов, свой синтаксис. Каким декларативным он бы ни был, я не хочу читать их документацию. Мне она больше нигде не понадобится.
при работе в команде, в большинстве своем люди пишут Dockerfile очень криво, не понимают как это кешируется, добавляют в образ все что надо и не надо, наследуются от образов которых нету в dockerhub или приватном репозитории, создают какие-то docker-compose файлы с базами данных и ничего не персистируют. При этом разработчики гордо заявляют, что docker крут, у них все работает локально и HR важно пишет в вакансии: «Мы используем docker и нам нужен кандидат с таким опытом работы»
постоянно преследуют мысли о поднятии в docker всего и вся: postgresql, kafka, redis. Жаль, что не всё работает в контейнерах, не все легко сконфигурировать и запустить. Поддерживаются это сторонними разработчиками, а не самими вендорами. И кстати сразу возникает вопрос, вендора не парятся насчет поддержания своих продуктов в docker, почему же это, может они что то знают?
всегда возникает вопрос про персистенцию данных контейнера. и тут думаешь, мне просто примонтировать хостовую директорию или создать docker volume или сделать data container который теперь deprecated? Если я монтирую директорию то мне нужно убедиться что uid и gid пользователя в контейнере соответствует id пользователя запустившего контейнер, иначе файлы созданные контейнером будут созданы с правами владельца root. Если использую volume то данные просто буду созданы в каком нибудь /usr/* и будет такая же история с uid и gid как в первом случае. Если запускаешь сторонний компонент то нужно вчитываться в документацию и искать ответ на вопрос: «а в какие директории контейнера компонент пишет файлы?»
Мне всегда не нравилось, что приходится слишком долго возиться с docker-ом на начальном этапе: я придумывал как запускать контейнеры, из каких образов запускаться, делал Makefile, которые содержали алиасы к длинным docker командам. Терпеть не мог docker-compose, потому что не хотел учить еще один инструмент экосистемы docker. И docker-compose up меня напрягала, особенно, если там еще встречались build конструкции, а не уже собранные образы. Все, что я реально хотел — это просто делать продукт эффективно и быстро. Но я никак не мог разложить по полочкам использование docker.
Знакомство с Ansible
Недавно (тройку месяцев назад), я поработал с DevOps командой, почти каждый участник которой, негативно относился к docker. По причинам:
docker правит iptables (хотя можно отключить в daemon.json)
docker бажный и в проде запускать его не будем
если docker daemon падает, то соответственно, падают все контейнеры с инфрастуктурой
в docker нет необходимости
зачем docker если, есть Ansible и виртуальные машины
На той же работе я и познакомился с еще одним инструментом — Ansible. Когда-то я слышал о нем, но не пробовал писать свои плейбуки. А теперь я начал писать свои таски и тут мое видение поменялось окончательно! Потому что я понял: у Ansible есть модули для запуска тех же docker контейнеров, сборок образов, сетей и пр., при этом контейнеры можно запустить не только локально, но и на удаленных серверах! Моему восторгу не было предела — я нашел НОРМАЛЬНЫЙ инструмент и выбросил свои Makefile и docker-compose файлы, они были заменены на yaml таски. Был уменьшен код за счет использования конструкций типа loop, when, etc.
Docker для запуска сторонних компонентов типа бд
Недавно я познакомился с ssh тунелями. Оказалось, что очень просто «пробросить» порт удаленного сервера на локальный порт. Удаленный сервер может быть как машиной в облаке, так и виртуальной машиной, запущенной в VirtualBox. Если мне или моему коллеге нужна бд (или какой нибудь другой сторонний компонент), можно просто запустить сервер с этим компонентом и загасить, когда сервер не нужен. Проброс портов дает такой же эффект, как и бд, запущенная в docker контейнере.
Эта команда пробрасывает мой локальный порт на удаленный сервер с postgresql:
Использование удаленного сервера решает проблему с разработкой в команде. Таким сервером могут пользоваться сразу несколько разработчиков, им не нужно уметь настраивать postgresql, разбираться c docker и с прочими изощрениями. На удаленном сервере можно установить ту же бд в самом docker, если поставить спецефичную версию трудно. Все что будет нужно разработчикам — это выдать ssh доступ!
Недавно прочитал что SSH тунели это ограниченая функиональность обычного VPN! Можно просто настоить OpenVPN или другие реализации VPN, настроить инфраструктуру и дать ее в пользование разработчикам. Это ведь так круто!
Благо, AWS, GoogleCloud и прочие, дают год бесплатного использования, так используйте их! Они копеечные, если их гасить, когда не используются. Я всегда задумывался, в каких целях мне бы понадобился удаленный сервер типа gcloud, кажется что я нашел их.
В качестве виртуальной машины на локали можно использовать тот же Alpine который активно используется в docker контейнерах. Ну или какие нибудь другие облегченные дистрибутивы чтобы побыстрее загружалась машина.
Итог: запускать бд и прочие инфрастуктурные плюшки можно и нужно на удаленных серверах или в virtualbox. мне не нужен docker для этих целей.
Немного про docker образы и дистрибутацию
Я уже писал статью в которой хотел донести, что использование docker образов не дает никакой гарантии. Docker образы нужны только для того чтобы создать docker контейнер. Если вы зашиваетесь на docker образ значит вы зашиваетесь на использование docker контейнеров и вы будете только с ними.
Вы видели где нибудь чтобы разработчики ПО портировали свои продукты только в docker образе?
Результат большинства продуктов это бинарные файлы под определенную платформу, именно их просто добавляют в docker образ который наследуется от нужной платформы. Вы не задумывались, почему в dockerhub так много похожих образов? Вбейте например nginx, вы увидите 100500 образов от разных людей. Эти люди не разрабатывали сам nginx, они просто в свой docker образ добавили официальный nginx и приправили своими конфигами для удобства запуска контейнеров.
Вообщем хранить можно просто в tgz, если кому то понадобится запускать это в docker то пусть в Dockerfile добавляют tgz, наследуются от нужного окружения и создают дополнительные плюшки которые не меняют самого приложения в tgz. Тот, кто будет создавать docker образ, будет знать, что это за tgz и что ему нужно для работы. Именно так я использую docker тут
Итог: мне не нужен docker registry, воспользуюсь каким нибудь S3 или просто файловым хранилищем типа google drive/dropbox
Docker в CI
Все компании, в которых я работал, похожи друг на друга. Они, как правило, продуктовые. То есть у них есть какое то одно приложение, один стек технологий (ну может пара — тройка языков программирования).
Эти компании используют docker на своих серверах где запускается CI процесс. Вопрос — зачем нужно собирать проекты в docker контейнере на своих серверах? Почему просто не подготовить окружение для сборки, например написать Ansible плейбук который будет ставить нужные версии nodejs, php, jdk, копировать ssh ключи и пр. на сервер, в котором будет происходить сборка?
Сейчас я понимаю, что это стрельба себе по ногам, потому что docker не приносит никакого профита со своей изоляцией. Проблемы с CI в docker с которыми я столкнулся:
снова нужнен docker образ для сборки. нужно искать образ или писать свой dockerfile.
90% что нужно пробросить какие нибудь ssh ключи, секретные данные, которые не хочется писать в docker образ.
контейнер создается и умирает, теряются все кеши вместе с ним. следующая сборка будет заново скачивать все зависимости проекта, а это долго и не эффективно, а время — деньги.
Разработчики не собирают проекты в docker контейнерах ( я когда то был таким фанатом правда, жалко себя в прошлом xD ). В java есть возможность иметь несколько версий и менять одной командой на ту которая нужна сейчас. В nodejs тоже самое, есть nvm.
Вывод
Я считаю что docker очень мощщный и гибкий инструмент, в этом его недостаток (звучит странно, да). С помощью него компании легко «подсаживаются» на него, используют где нужно и не нужно. Разработчики запускают свои контейнеры, какое то свое окружение, потом это все плавно перетекает в CI, продакшн. DevOps команда пишет какие то велосипеды чтобы запустить эти контейнеры.
Используйте docker только на самом последнем этапе в вашем рабочем процессе, не тащите его в проект в начале. Он не решит ваших бизнес проблем. Он только сдвинет проблемы на ДРУГОЙ уровень и будет предлагать свои варианты решения, вы будете делать двойную работу.
Когда docker нужен: пришел к мысли что docker очень хорош в оптимизации поставленного процесса но не в построении базового функционала
Если вы все-таки решили использовать docker, то:
будьте предельно осторожны
не навязывайте использование docker разработчикам
локализуйте его использование в одном месте, не размазывайте по всем репозиториям Dockefile и docker-compose
PS:
Недавно наткнулся на packer и говорят он очень хорошо работает с Ansible и позволяет унифицировать процесс построения образов (в т.ч. docker image)