Привет, Хабр!


Несколько месяцев назад я выступал на конференции FrontendConf 2019 с докладом Docker для фронтендера и хотел бы сделать небольшую расшифровку доклада для тех, кто больше любит читать, а не слушать.



Приглашаю под кат всех веб-разработчиков, особенно фронтендеров.


Восхождение Docker


Docker инструмент не новый, он впервые был опубликован ещё в марте 2013 года и с тех пор его популярность только растёт.



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



Вот такая игрушка была на конференции РИТ++ 2019 в рамках DevOps-секции. И по моему опыту ни одна конференция по DevOps не проходит без докладов про Docker, так же, как и фронтендерские конференции не проходят без докладов про сравнение фреймворков, настройку вебпака и профессиональное выгорание.


Давайте и мы во фронтенд-тусовке тоже начнём говорить про эту технологию.



Это дорожная карта веб-разработчика. Слева изображены навыки, обязательные для любого пути развития. Действительно, сложно представить хорошего веб-разработчика, который не умеет в Git, терминал или не знает HTTP. 


Docker тут тоже присутствует, но где в недрах ветки развития DevOps'а в блоке Infrastructure as a code -> Containers.


Но мы знаем, что Docker — это ещё и отличный инструмент для разработки, а не только для эксплуатации. И, по моему мнению, он имеет все шансы через некоторое время попасть в секцию Required for any path и стать обязательным требованием во многих вакансиях фронтенд-разработчиков.


Можно сказать, что мы сейчас живём в Эру Docker. Поэтому, если вы фронтенд-разработчик и ещё с ним не познакомились, то я расскажу, почему это стОит сделать.


Зачем?


Кейс 1. Поднимаем backend


Первый и самый полезный кейс, который мы можем рассмотреть — это запуск API на своём уютном макбуке. Да, мы отлично знаем наши технологии, но разворачивать что-то стороннее всегда испытание не из лёгких.


На одном из наших проектов фронтенд-разработчику нужно было поставить на компьютер вот такой набор, чтобы API заработал:


* go1.11
* MySQL
* Redis
* Elasticsearch
* Capistrano
* syslog
* PostgreSQL

К счастью, у нас были инструкции по развёртыванию проекта в README-файлах. Они выглядели примерно так:


1. Установить GVM (https://github.com/moovweb/gvm)
2. `gvm install go1.11.13 --binary`
3. `gvm use go1.11.13 --default`
4. В папке с проектом создать ссылку на golang (`gvm linkthis`)
5. Установить `gb` `go get github.com/constabulary/gb/...`
6. Настроить авторизацию `git config --global url."git@git.example.com:".insteadOf "https://git.example.com/"`
7. Установить зависимости `gb vendor restore`
8. Поднять БД
9. Собрать ассеты `npm run build` (`npm run build:dev` для разработки)
10. Запустить сервер `npm start`

И вот так:


## Elasticsearch
1. Скачать и установить Elasticsearch `brew install elasticsearch` - macOS (предложит установить дистрибутив java)
2. Установка плагина
* выбери в https://github.com/imotov/elasticsearch-analysis-morphology из списка подходящий к твоей версии плагин, и запусти
`/usr/local/Cellar/elasticsearch/2.3.3/libexec/bin/plugin install http://dl.bintray.com/content/imotov/elasticsearch-plugins/org/elasticsearch/elasticsearch-analysis-morphology/2.3.3/elasticsearch-analysis-morphology-2.3.3.zip` - пример
* потом перезапусти эластик `brew services restart elasticsearch`
3. в проекте админки
`rake environment elasticsearch:import:model CLASS='Tag' FORCE=y `
`rake environment elasticsearch:import:model CLASS='Post' FORCE=y`
индексировать будет долго, но пользоваться можно сразу

## Postgres comments db
1. `psql -U postgres -h localhost`
2. `create database comments_dev;`

## Redis install and start
1. `brew install redis`
2. Перед запуском `brew services start redis`

Как вы думаете, сколько времени требовалось начинающему разработчику, чтобы развернуть у себя проект и начать делать задачи?


Около недели.


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


Это считалось нормальным и всех устраивало. То есть, чтобы сделать фичу в 1 час работы, требовалось сначала потратить 40 часов на разворачивание всех компонент локально.


Сейчас разворачивание проекта со всеми сервисами для разработки выглядит вот так и при холодном старте занимает около 10 минут.


$ docker-compose up api
...
Listening localhost:8080

Все команды разворачивания сервисов выполняются во время сборки Docker образов. Они автоматизированы и не могут устареть.


Кейс 2. Устойчивость


Второй кейс — устойчивость системы нашего рабочего компьютера.


Кому нравится ставить на свой любимый компьютер какой-то сторонний софт? Кому нравится устанавливать несколько разных баз данных, новые компиляторы, интерпретаторы?


А это приходится делать, когда мы разворачивает локально сторонний API.


Более того, вы можете сломать вашу систему и потратить несколько часов на её восстановление.



Использование Docker для разработки достаточно хорошо справляет с этой проблемой. Весь сторонний софт крутится в изолированных контейнерах и при необходимости легко удаляется.


$ docker rm --volumes api
$ docker system prune --all

Кейс 3. Контролируем эксплуатацию


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


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


Фронт: Ребят, я всё сделал. Код в репе. Выкатите, плиз!
Админ: Как оно выкатывается?
Фронт: Собираешь нодой и раздаёшь статику веб-сервером из папки /build
Админ: Какой версией ноды собирать? Какая команда сборки? Каким веб-сервером раздавать?


В итоге для админа развёртывание вашего проекта превращается в не менее увлекательный квест, как для вас развёртывание API на локальной машине из предыдущих кейсов.


Даже если проект будет работать у вас на компьютере, совсем не обязательно, что всё так же будет работать на продакшене. И мы получаем классическую проблему "это работает на моей машине".



Docker помогает нам и с этим. Решение простое, если мы запаковали наш проект в Docker, тем самым автоматизировав его сборку и настроив запуск, то он будет одинаково работать на всех серверах, которые поддерживают Docker.



А инструкция для админов сведётся к:


Фронт: Ребят, я всё сделал. Собрал для вас Docker-образ. Выкатите, плиз!
Админ: Ок


Уж с чем, а с Docker-образами админы точно должны уметь работать. Не то, что с нашей нодой.


Что же такое Docker?


Надеюсь, я смог объяснить, почему стоит присмотреться к этой технологии, если вы фронтенд-разработчик и до сих пор её не знаете. Но я так и не рассказал, что это такое.



Планирую написать про это в следующих частях статьи и дать немного полезных рецептов для фронтенд-разработчиков.


Содержание


  1. Docker для фронтендера. Часть 1. Зачем?
  2. Docker для фронтендера. Часть 2. Что ты такое?
  3. Docker для фронтендера. Часть 3. Немного рецептов

Комментарии (25)


  1. musuk
    05.12.2019 19:32

    docker-compose up api


    Как просто!

    Кроме API нужно ещё поднять конейнер с elastic, mongo запихать данные в mongo и проиндексировать их эластиком. Причём пихать данные нужно только когда у вас подымутся Mongo и эластик. А их ещё надо и сконфигурить.


    1. usego
      05.12.2019 19:37

      Делается преднастроеный докерок эластика с данными и всё вместе линкуется одним компоузом. И индексировать эластиком монго… Это как?


      1. musuk
        05.12.2019 20:59

        docker-compose не ждёт когда подымется mongo или elastic, он знает только запущен конейнер или нет. docker-compose для такой цели можно написать и решить всё через, но он будет довольно сложной штукой с кучей логики в entrypoint.sh
        Для таких целей должно быть что-то более умное, чем docker-compose.


        1. usego
          05.12.2019 22:10

          ну так и апликуха не должна насмерть валиться от кратковременного офлайна эластика


          1. musuk
            05.12.2019 22:46

            Это да, но чтобы заполнить эластик вам нужны данные в монге, те дождаться, когда отработают все mongorestore.
            Чтобы начать заполнять монгу через mongorstore вам нужно знать, что mongo поднялась (ей на это время нужно). Чтобы заполнять эластик вам нужно тоже дождаться, пока подымется эластик. Сам docker-compose всё это не умеет. Те поднять всё одной командой docker-compose up api без вороха sh-скриптов не выйдет.

            И индексировать эластиком монго… Это как?

            Это использовать эластик как полнотекстовый поиск, а документы хранить в mongo/postgresql.


            1. usego
              06.12.2019 00:50

              Зачем весь этот рестор, если можно иметь докера с уже проиндексированными данными? Оно ведь для разработки. Достаточно иметь плюс минус актуальный снэпшот.


              1. musuk
                06.12.2019 10:36

                Так для разработки и нужно, чтобы каждое добавление поля в базу бэкендером сразу же было доступно фронтам. Причём чем интенсивнее разработка, тем быстрее должно обновляться API и база.
                Иметь образа с данными можно, но строить такие образы намного сложнее. По мне пока самый простой способ, это делать образ уже запущенного и модифицированного контейнера. Те я не знаю, как описать рестор данных в рамках Dockerfile.
                Потом для автоматического построения всех этих образов нужна инфраструктура, причём она должна куда-то публиковать образа. А образа должны быть специфичны для веток в которых идёт разработка. Ведь в разных ветках могут быть разные поля в базе. Ну и операция переключения между ветками для фронта будет тоже не такой тривиальной.


                1. usego
                  06.12.2019 10:42

                  Мы используем локальный гитлаб, там вся необходимая инфраструктура встроена. Да, время вложить в «поразбираться» придётся, но где это сейчас не требуется? Я это к тому, что проблемы как таковой нет, при наличии правильного инструментария и процессов.


    1. Avdeev Автор
      06.12.2019 12:14

      Отличное замечание, я про это не рассказал.

      Действительно, полностью избавиться от инструкций и команд не получается.

      Мы делаем вот так:

      1. docker-compose up api собирает связанные через depends_on образы и запускает их.
      2. Все команды на запущенных контейнерах стараемся убирать в Makefile.

      Что-то вроде:

      # Makefile
      migrate:
        docker-compose run api bundle exec rake db:migrate
      


      1. Stormwalker
        06.12.2019 13:48
        +3

        depends_on не делает liveness-readiness проверок, просто проверяет состояние контейнера. Лучше уж сказать, чтобы сделал

        docker-compose up -d backend frontend elastic redis postgres
        и увидел заветную запись в логах, если надо что-то сделать. Еще есть костыли типа wait-for-it.sh.

        Команды в Makefile поддерживаю, главное чтобы они не расползались по другим местам :)


  1. inoyakaigor
    05.12.2019 20:54
    +3

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


    1. Avdeev Автор
      06.12.2019 12:02
      +1

      После доклада меня спрашивали несколько раз про этот подход, когда фронтенд-разработчик всё запускает не локально, а использует отдельный staging- или dev-сервер для разработки.

      Этот способ работает и имеет свои плюсы, но не лишен и недостатков.

      Вот некоторые из них:

      1. Невозможность работать офлайн или при плохом Интернете. Многие любят работать из дома, деревни или в дороге.
      2. Необходимость делить один и тот же ресурс с другими разработчиками и тестировщиками. У нас были случаи, когда приходилось создавать необходимую для разработки сущность новости с названием «НЕ УДАЛЯТЬ!», чтобы кто-то её случайно не грохнул. Но, всё равно это периодически случалось.
      3. Такой сервер достаточно хрупкий. Было несколько ситуаций, когда бекенд-разработчики или эскплуатация начинали шатать сервак. Или, к примеру, накатывали туда какие-нибудь ломающие изменения. Это останавливало разработку у всей команды.


      1. musuk
        06.12.2019 13:31
        +1

        Работаем в распредёлнной команде по такой схеме.

        Невозможность работать офлайн или при плохом Интернете.

        Хороший интернет в наше время must have. Без него вообще плохо.

        Необходимость делить один и тот же ресурс с другими разработчиками и тестировщиками.

        Почти никогда у нас это не проблема, но зависит от проекта, конечно.

        Такой сервер достаточно хрупкий.

        Да, он хрупкий (хотя перед выкладкой прогоняются тесты), но понять почему у фронта упало бекендерам сильно проще, потому что обший сервер пишет логи.

        Есть более важная проблема при таком подходе: очень сложно иметь по серверу на фиче-ветку. Можно сделать front-ветку на каждого фронта и если фронту нужно переключиться на какую-то фиче ветку, то то эту ветку мерджат в front-ветку и CI/CD её строит. Однако это запарано, потому народ всё льёт в master.


        1. Avdeev Автор
          06.12.2019 14:41

          Да, кстати, об это я не подумал. Хороший кейс.

          Локальное разворачивание позволяет развернуть у себя любую ветку API.

          Например, вот так:

          cd api
          git checkout feature/branch
          docker-compose build api
          make migrate
          docker-compose up api
          


          1. musuk
            06.12.2019 15:02

            Мы в итоге, к тому же и пришли. Только у нас не make migrate, а большой скрипт, который инициализирует окружение. Только вот для запуска этого скрипта надо локально установить кучу всего, например, чтобы сделать mongorestore нужно поставить mongo-tools, чтобы проиндексировать эластик, надо собрать соответствующий кусок приложухи, для сборки которого нужно поставить SDK. Так что да, вынести сервера бд в отдельные контейнеры можно, но SDK всё равно надо будет ставить и настраивать.


            1. Avdeev Автор
              06.12.2019 15:32

              Не уверен, но, думаю, можно попробовать сделать образ с SDK, который при старте инициализирует окружение.


  1. vasyapivo
    05.12.2019 21:02

    * go1.11
    * MySQL
    * Redis
    * Elasticsearch
    * Capistrano
    * syslog
    * PostgreSQL


    Сколько памяти отъест виртуалка на MacOS/windows чтобы гонять это всё в контейнерах?


    1. lasc
      06.12.2019 06:17

      на 16 можно жить.


      1. TheRaven
        06.12.2019 11:23
        +1

        Нельзя. Буквально вчера развернул себе бекенд на 12 (!) контейнерах, запустил фронт, IDE, Skype, Firefox, AIMP… и память всё, кончилась. Хорошо что вся эта требуха докерная сама спрятана в виртуалку, сегодня буду её на отдельную тачку выносить.


    1. Avdeev Автор
      06.12.2019 11:47

      Оно всё достаточно легковесное, в этом и прелесть Docker. Вот пример вывода docker stats.

      CONTAINER ID        NAME                                   MEM USAGE
      e4941ea92ce7        nginx_1                                3.16MiB
      1b023bfff38f        api_1                                  351.5MiB
      e07c6958e378        pg_1                                   18.64MiB
      1fa783f5fdbc        terminal-front_1                       14.89MiB
      72e9dfa0805a        adminer_1                              11.19MiB
      e9ce9f965867        admin-front_1                          1.312MiB
      3edacc59a77b        certbot_1                              1.547MiB
      

      Видим, что БД заняла 19 МБ, а API на Java — 352 МБ.

      У нас у разработчиков от 8 до 16 ГБ оперативной памяти. Обычно, этого хватает.

      По личному опыту, большую часть оперативной памяти во время разработки съедает IDE, Chrome, Telegram, Slack, а не запущенные docker-контейнеры.


      1. vasyapivo
        06.12.2019 11:53

        Но ведь вопрос не в том, сколько памяти потребляет контейнер, вопрос в том, сколько ест виртуалка без которой вы на MacOS или Win ничего не сможете сделать. Docker build выполняется именно на виртуалке, и если сама апа ест 14 мегабайт, то вопрос в том, сколько будет жрать ресурсов docker build.


        1. Avdeev Автор
          06.12.2019 12:44

          Да, Вы правы. Плюс ещё примерно 4 ГБ на виртуалку на MacOS.


  1. Tim152
    06.12.2019 12:18
    +1

    Респект автору!


  1. ShamilFrontend
    06.12.2019 15:34
    +1

    Крутой доклад, спасибо :-)


  1. Esvalirion
    09.12.2019 11:03
    +1

    Надо попробовать, жду второй доклад :)