Язык Go, микросервисы и DevOps – хорошая компания?

Автор оригинала: Agile Maverick
  • Перевод
Привет, Хабр!

Напоминаем, что все желающие по-прежнему могут приобрести отличную книгу Сэма Ньюмена "Создание микросервисов". Поскольку наши ожидания эта тема более чем оправдала, мы продолжаем искать связанную с ней литературу и не так давно обратили внимание на книгу о программировании микросервисов на языке Go



Интересную статью с обоснованием этого подхода мы нашли в блоге Agile Maverick, и ее перевод размещаем под катом.

Приятного чтения!



Сейчас все говорят о микросервисах и DevOps. Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры. Я побывал в Мюнхене на нескольких интересных митапах по микросервисам, и меня наиболее удивило, что эта тема пользуется наибольшим интересом в сообществах Java и Scala. Удивило потому, что Java и Scala – очень насыщенные языки, в которых есть из чего выбирать.

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

Я понимаю микросервисную архитектуру в более целостном ключе. Я бы сказал, что и вся экосистема должна быть проще и компактнее. Реализация – лишь одна сторона медали. Другая сторона – это время исполнения и сопутствующие фреймворки. Здесь мы подходим к теме DevOps – философии, стремящейся увязать друг с другом две эти стороны.

Виртуальная машина Java оптимизирована для работы с долгоиграющими приложениями, в ней действует одна из наиболее выверенных и затейливых систем сборки мусора. Используется в боевых условиях уже более 10 лет. Тем не менее, когда мне доводится видеть современные высокодоступные архитектуры – сразу напрашивается вопрос: а нужны ли долгоиграющие приложения для реализации абсолютного большинства существующих сервисов?

Приведу пример. Я участвовал в разработке приложения для кодировки видео, и это приложение как назло должно было работать круглосуточно с минимальными задержками. Мы думали, остановиться ли на стабильном языке программирования вроде Java или написать приложение на Go, где использовались бы имеющиеся библиотеки на C для кодирования и декодирования, однако такой проект мог обернуться утечками в памяти. Наконец, мы решили разделить приложение на различные процессы; статический бэкенд почти не изменился, поскольку передавал информацию по практически не изменившемуся протоколу, а еще у нас была функционально богатая клиентская часть, где существовал риск утечек. Обе части использовали разделяемую память. Оказалось, что вариант хороший. Поскольку Go стартует быстро, мы перезапускали клиентскую часть раз в десять секунд. Оказалось, что проблема – не в утечках памяти, а в оперативных обновлениях.

За много лет в Java сложилось много нетривиальных решений – например, фреймворк log4j для логирования. На примере контейнерных решений вроде OpenShift можно убедиться, что теперь снова принято работать с stdout и stderr. Нет необходимости внедрять изощренные решения для логирования на уровне языка. Этот пример позволяет судить, как DevOps и новые среды времени выполнения меняют правила игры.

Типичный docker-образ на Go docker имеет размер около 15 MB; сравните его с образом для JVM на Java, размер которого — около 300 MB. Разница 1 к 10. Java JVM оптимизирована под экономный расход памяти, но все равно требует примерно в 10 раз больше памяти, чем Go.

В Go не так много унаследованных фреймворков, поэтому и зависимостей обычно мало, а код зависимостей входит в состав бинарного файла. Поэтому отпадает необходимость в таких сложных инструментах как Maven. В контейнерной среде релиз нового образа необходим всякий раз, когда меняется одна из зависимостей в цепочке. А значит, на Java Java мы должны обновлять такие контейнеры достаточно часто. Хуже того, зависимости обычно запрятаны где-то глубоко.

Java и Scala – это языки для объектно-ориентированного программирования. Но при работе в сравнительно простых предметных областях такие решения кажутся мне довольно затратными. «Гибкий» аспект философии Go позволяет организовать разработку не только не хуже, но и гораздо понятнее.

Java известен своим огромным конвейером и множеством инструментов для непрерывной интеграции вроде Jenkins, которые развились вокруг этого конвейера. В Go конвейеры получаются гораздо короче, проще и быстрее – ведь мы получаем бинарные файлы, уже готовые к исполнению.

В 1990-е был настоящий бум серверов приложений Java – считалось, что они обеспечат независимость разработки от операционной системы и аппаратного обеспечения. Читая спецификацию JEE, мы также рассчитывали на простоту удаленных взаимодействий и компонент-ориентированную разработку. Когда я вижу контейнер docker, на котором работают Java-приложения, всегда вспоминаю о новой версии EJB. В принципе, стек Java не упростился, но теперь он упакован в контейнер. Такая упаковка даром не дается, поскольку добавляется еще один уровень сложности; вы с ним познакомитесь, как только попробуете отладить сеть такого docker-контейнера.

Go docker – вариант для масштабирования сервисов, но сложную среду времени исполнения он не спасает. Если у вас всего один простой сервис, то простые бинарные файлы Go можно выполнять прямо на хосте. Если же речь идет о более сложном приложении, то сервисы можно положить, например, в контейнер и запускать их в PaaS-среде вроде OpenShift. Чтобы протестировать сервис на ноутбуке разработчика, контейнер не нужен, всяческая связанная с ним магия – тоже.

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

Go, где вариантов не так много, помогает быстрее освоиться с решением тем разработчикам, которые сами его не писали. Не требуется углубляться в философию разработки. Разумеется, всегда можно реализовывать сервисы и в более простом стиле на более насыщенных языках, например, на Java или Scala, но в данном случае нужно научиться самоограничению, обсуждать все детали с командой – соответственно, микросервисная архитектура обрастает огромной документацией.

Мне кажется, Go идеально подходит для реализации микросервисов. Почему же этот язык так медленно усваивается в сообществе разработчиков?

Думаю, просто никто не любит резких изменений. Мы попробовали изменить всего одно измерение в многомерном мире программирования. Это измерение – размер сервиса. По моему опыту, изменить одно измерение еще недостаточно, чтобы пошла эволюция. Поскольку все измерения взаимосвязаны, они влияют друг на друга. Решив упростить приложение, переделав его в виде микросервисов, мы должны также упростить и исполняющую среду, и язык программирования, с которым работаем. Иначе получим лишь новую головную боль – например, придется управлять сразу множеством JVM, которые занимают кучу места в памяти и запускаются довольно медленно. Либо получим множество мелких объектно-ориентированных решений, которые будут распределенными, а значит – более сложными. Наконец, мы просто запутаемся со множеством сервисов, образующих огромное дерево зависимостей.

По-моему, не меняя всех измерений сразу, мы словно пересаживаемся с лошади на машину, но берем с собой седло и шпоры.

Давайте не просто писать микросервисы, но и адаптировать под них всю архитектуру. Go отлично для этого подходит. Если предметная область сервиса расширится, вы всегда сможете дополнительно воспользоваться Java или Scala.

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

Актуальность книги

  • 81,9%Да, книга должна быть хороша172
  • 7,1%Узкая тема, решаем эти задачи иначе15
  • 21,0%Нужна более универсальная книга по Go44

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

    +6
    Напилить микросервисы это не самая сложная задача.

    Потому что буквально после двух простых рест сервисов возникнут следующие вопросы

    1) Service discovery.
    client side? server side? Как у Go с готовыми библиотеками под это дело? А такими которые уже опробованы в продакешене? А такими, которые опробованы в продакшене крупными компаниями аля нетфликс?
    Понятное дело что можно использовать клиентов для Consul или Eureka.

    2) Circuit breaker
    Вот тот вот сервис подтупливает и отвечает по 1000 секунд вместо 5. Из-за этого все сервисы, которые на него завязаны тоже начинают тупить. Есть ли готовый продакшен фреймворк для go в этом случае? (Hystrix с страницей отчета)

    3) Нужно сделать CRUD over REST, с пагинацией, с базовыми селектами.
    Есть ли удобные и проверенные временем фреймворки для Go под это дело?

    4) OAuth и сотоварищи. Насколько просто повесить на Go endpoint-ы авторизацию и аутентификацию?

    5) Tracebility. Есть ли возможность *просто* отследить все сервисы через которые прошел запрос? Так, чтобы без руками пробрасывать UUID correlationID в заголовках или теле запроса?

    6) Логирование. Stdout, конечно круто, а что насчет простой интеграции с ELK?

    В Java мире эти вопросы закрыты более менее нормально.
    Проблема не в создании микросервисов, а в оркестровке и отладке зоопарка из 20 хотя бы сервисов, каждый из которых имеет по 3-4 инстанса.
    И понятное дело что в ИТ все вопросы решаемы. Но количество приседаний, которые необходимо для этого сделать очень отличатся.

    И раз мы уже пытаемся минимизировать размер docker образа, почему бы не воспользоваться
    Erlang docker 17MB?

    В котором есть let it fail, supervision trees, и отличный веб-серве cowboy.

      +4
      Потому что то Гоу, смузи, Гугл и бородатые дровосеки, а то Эрланг, скучная функциональность и практичность.
        +2
        Ну если серьезно, то ответ есть в начале статьи:

        >> Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры
          +1
          Поставьте эти словечки себе в профиль – и вас сразу начнут осаждать рекрутеры


          C глупыми и бесполезными предложениями.
          DevOps/Микросервисы — это удел запада, а наши потуги внедрять всё это — довольно посредственны.
          Пока не решится вопрос с контролем качества и общей организацией работ, а в 80% случаев их нет, до тех пор и думать про какой-либо DevOps и Микросервисы не то что бесполезно, а просто вредно, и для проектов, и для их руководства.
            0
            Ну микросервисы архитектурно неплохи (хотя и не без своих особенностей и проблем), и способствуют повышению качества за счет уменьшения регрессии. А девопсы — это часто вынужденная мера. Участвовал в проекте (система документооборота и автоматизации деятельности госслужб), для установки которого на оборудование заказчика существовала специальная команда из восьми человек.
              0
              Если что, я имел ввиду, что крайне странно сравнивать гигантские фреймворки для всего и солянку из маленьких библиотек
          0

          0) Ещё до написания первых двух сервисов стоит глянуть https://github.com/golang/go/wiki/Projects и присмотреть то, что вам надобно. А ещё убедиться, что golang именно то, что вам нужно для вашей задачи.


          1) circuit
          3) gin + gorm например
          4) https://github.com/golang/go/wiki/Projects#authentication
          5) А почему пробрасывать руками это сложно? Лично я так бы и сделал. Один раз написал бы middleware, который за пару-тройку строчек добавляет заголовок и плюётся в какой-либо message queue.
          6) https://github.com/golang/go/wiki/Projects#logging-and-monitoring


          Но я могу быть не прав в контексте ваших задач. Мои задачи go решает. Например я больше занимаюсь кодом, который вместо масштабирования вверх и вширь должен использовать существующие ресурсы без фанатизма (и диск и память и cpu). Вплоть до того, что docker это кривой оверхед для меня. С другой стороны я не побоюсь использовать няшный Си и ассембрер, если в этом возникнет крайняя необходимость.

          +1
          Почему в статье сравнивают Java EE стек и программы на го, которые состоят из кучи маленьких решений?

          Ну и фраза про «go потребляет в 10 раз меньше памяти чем Java» шикарна, конечно.
            0

            Особенно учитывая, что при использовании docker слой с jre/jdk размером в 300 MiB будет скачиваться и храниться один раз на хост, что, в общем, копейки.

              –1
              Потому, что они выполняют в итоге одни и те же функции?
              0
              > Типичный docker-образ на Go docker имеет размер около 15 MB; сравните его с образом для JVM на Java, размер которого — около 300 MB. Разница 1 к 10.

              Если речь идёт о разнизе в размере, то разница никак не в 10 раз…

              > Чтобы протестировать сервис на ноутбуке разработчика, контейнер не нужен, всяческая связанная с ним магия – тоже.

              Docker даёт возможность унифицировать дев- и прод- окружения, запуская сервис в «одинаковом» (простите) окружении. Хотя, конечно, смотря что понимать под «протестировать».
                0
                Подскажите пожалуйста, можно ли заказать бумажный вариант книги, с Вашего сайта, с доставкой почтой в Казахстан (г.Алматы) по предоплате картой?
                  0
                  Да, возможно, написали в личку.

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

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