Статьи

Архитектура микросервисов с Java и Docker

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

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

Хотя в микросервисах нет магии, которая позволяла бы вам думать, что реальность — это не то, чем кажется, я могу вам сказать, что волшебники существуют! Я вернусь к этой теме позже в этой серии.

Если вы хорошо знаете Java и читаете эту статью, ожидая, пока ваш партнер завершит покупки, вы можете пропустить следующий раздел (Java), чтобы перейти непосредственно к Microservices , но если вы ждете, пока она закончит работу попробуй это классное платье на Рождество, приятель, у тебя есть все время, чтобы прочитать каждый раздел и получить пиццу с доставкой & # 55357; & # 56841;

Ява

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

Кто-то может сказать, что он все еще далек от достижения своих пяти основных целей [1], но три месяца назад была выпущена версия 9, и это по-прежнему один из самых популярных используемых языков программирования, особенно для клиент-серверных веб-приложений, о котором было сообщено более 9 миллион разработчиков.

Сообщество Java во многом способствовало успеху Java, предоставляя библиотеки и обратную связь, и это помогло сделать Java подходящим для разработки микросервисов.

Архитектура микросервисов

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

Чтобы понять микросервисы, полезно сравнить это с монолитами. Корпоративные приложения часто создаются в соответствии с шаблоном MVC, поэтому пользовательский интерфейс (UI) получает доступ к данным через приложение на стороне сервера.

монолиты

Строго говоря, Монолит сам по себе неплох; Я знаю пример успешных монолитных приложений, а также ужасную реализацию, где буквально все в одном приложении (забудьте о MVC и разделении концернов). Однако приложение Monolith требует больше ресурсов с точки зрения аппаратного обеспечения и человеческой работы, и все большее число людей разочаровывается в приложении Monolithic. Циклы изменения требуют перестройки, повторного тестирования и повторного развертывания всего монолита, а обновление всей серверной фермы легко занимает месяцы (и повышенное разочарование каждой участвующей части), а масштабирование требует масштабирования всего приложения.

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

Для построения микросервисов не требуется ни стандартного протокола, ни обязательного языка программирования, но многие реализации основаны на HTTP / REST и используют JSON для обмена данными. Лично я предпочитаю поддерживать как JSON, так и XML, потому что это дает мне большую гибкость и охватывает более широкий диапазон сценариев. И если вы можете получить и то, и другое бесплатно, единственная причина этого не делать, если в спецификации так сказано. Мы собираемся производить результаты в обоих форматах.

Обладая архитектурой Microservices, вы можете развернуть несколько экземпляров службы на одном хосте (это может быть физическая или виртуальная машина). Запуск нового сервиса обычно занимает несколько секунд, поэтому, когда вам нужно больше от этого сервиса (что бы ни значило больше для этой функциональности), вы просто запускаете новый сервис или несколько экземпляров этого сервиса и, наконец , все готово. Одна проблема, которую вы могли бы поднять, заключается в том, что при запуске нескольких экземпляров одного и того же приложения на одном хосте все они будут работать в одной куче JVM, поэтому не будет никакой изоляции. И ты абсолютно прав. Через минуту мы увидим, почему это не проблема для нас.

Перед этим я хочу добавить еще один важный момент о микросервисах. Создавая микросервисы, мы стараемся уменьшить их размеры до минимума. Я видел Microservices больше чем 3GB, который не соответствует моему определению micro . В одном из таких случаев мне удалось уменьшить примерно до половины исходного размера.

докер

Docker — это программная технология, обеспечивающая уровень абстракции поверх операционной системы с использованием собственных ресурсов, и, действительно, пользователи, которые используют образы контейнеров Docker (обычно называемые просто изображениями ) и контейнеры . Ниже приведено официальное описание образов и контейнеров от Docker: « Образ контейнера — это легкий, автономный исполняемый пакет программного обеспечения, который включает в себя все необходимое для его запуска: код, среду выполнения, системные инструменты, системные библиотеки, настройки. »

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

Привет, мир с докером

Посмотрим, как выглядит классический пример.
HelloWorld.java

1
2
3
4
5
6
7
package com.daftano.examples.commons;
 
public class helloworld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Учитывая следующий Dockerfile и запустив команду

1
docker build helloworld .

Dockerfile

1
2
3
4
5
6
7
FROM daftano:java
 
LABEL maintainer-twitter="@daftano"
 
ADD build/helloworld-1.0.0.jar /opt/helloworld.jar
 
ENTRYPOINT java -jar /opt/helloworld.jar

Docker создаст новый образ с именем helloworld.

Чтобы запустить наш новый образ, нам нужно просто запустить эту команду:

1
docker run helloworld

И на консоли вы увидите

Привет мир!

Да, это действительно довольно сложный способ запуска JAR-файла, но он все еще имеет некоторые преимущества. Вы можете поделиться этим изображением с кем угодно: им не нужно иметь Java в своей системе, и вы будете уверены, что он всегда будет работать одинаково, независимо от среды, и он будет изолирован от своего окружения.

Давайте подробно разберем Dockerfile:

  • Допустимый файл Docker должен начинаться с инструкции FROM, которая устанавливает базовый образ для последующих инструкций. Изображение может быть любым действительным изображением.
  • Инструкция LABEL добавляет метаданные к изображению, а ключевой сопровождающий является специальным, устанавливающим автора этого изображения.
  • Инструкция ADD копирует банку, которую мы построили, во встроенный образ.
  • ENTRYPOINT настраивает команду, которая должна выполняться при запуске контейнера.

В настоящее время доступно 18 возможных инструкций по адресу https://docs.docker.com/engine/reference/builder , и есть еще одна, часто используемая, которую я не использовал в этом примере специально. Многие реализации основаны на HTTP / REST, и службе необходимо прослушивать один или несколько портов и информировать Docker о том, какие порты контейнер должен прослушивать во время выполнения, мы будем использовать инструкцию EXPOSE .

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

Когда мы создаем изображение, Docker генерирует уникальный идентификатор, и мы обычно помечаем это изображение значимым именем (с именем с именем репозитория), таким как daftano / java. За двоеточием («:«) стоит номер версии (в нашем случае 1.8 — если не указано, версия по умолчанию самая последняя). Имя репозитория должно быть как минимум в одной строчной букве, необязательно разделенной точками, тире или подчеркиванием; фактически оно должно соответствовать этому регулярному выражению: [a-z0-9] + (?: [._-] [a-z0-9] +) *

Образ Java

Существует большой выбор образов Docker для Java, и вы можете спросить, почему я решил создать свой собственный. Основной причиной этого является то, что ни один из них не соответствовал моим потребностям. Мне было очень легко создать новый пользовательский образ, который я забыл по количеству созданных изображений, но если уже есть что-то, что я мог бы использовать повторно, почему бы и нет? Вы знаете, есть много преимуществ в повторном использовании, одно из них заключается в том, что если есть ошибка, которая может быть обнаружена (и, возможно, исправлена), прежде чем она может повлиять на меня, или я смогу исправить ее и поделиться с автором (s) / сообщества.

К сожалению, технический руководитель одного из приложений, которое я переносил на Microservices, считает, что ему не нужно писать какую-либо документацию или спецификацию, поскольку чтение исходного кода — это все, что вам нужно. Это привело к их требованиям предоставить образ, строго основанный на Ubuntu 12.04, с тоннами установленных пакетов (включая все для Xorg). Мне также пришлось использовать файлы политики неограниченной юрисдикции Java Cryptography Extension (JCE), которые недоступны в стандартных двоичных файлах JDK / JRE и накладывают некоторые ограничения на пользователей. Есть много вещей, которые вы должны проверить, хотите ли вы распространять образ на основе Oracle Java, чтобы не выходить за рамки условий соглашения, подписанного с Oracle, при загрузке JDK / JRE. И у нас есть возможность использовать OpenJDK, который выпущен под лицензией GNU General Public License, версия 2, с исключением Classpath.

Другая причина, по которой вы можете захотеть использовать OpenJDK, заключается в том, что образ на основе Alpine составляет 101 МБ, в то время как Oracle JDK добавляет 399 МБ к вашему образу. Мне было забавно обнаружить, что мой образ Java на 101 МБ легче текущего openjdk: последняя . Я мог бы облегчить работу моего мага, удалив ненужные мне файлы, но при этом нарушил бы условия соглашения с Oracle, если бы поделился этим изображением с кем-либо еще. Это не место, чтобы говорить об этом в глубине, но если есть достаточно интереса к предмету, я мог бы написать статью (сообщите мне, комментируя эту статью или через твиттер).

Для этой серии статей я использую базовый образ Java, доступный в моей учетной записи GitHub по адресу https://github.com/daftano/docker-images/blob/java/Dockerfile, но вам не нужно это изображение для запуска примеров. , если ваш образ Java содержит Java 8 или 9. Если вы решите построить мой образ, обратите внимание, что вам нужно либо загрузить политику JCE, доступную по адресу http://www.oracle.com/technetwork/java/javase/ downloads / jce8-download-2133166.html и скопируйте файлы jar в каталог jce-policy / 8 .

Java Dockerfile

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
1.  FROM daftano/ubuntu:16.04
2.  LABEL maintainer="Davide Fiorentino lo Regio"
3.  LABEL maintainer-twitter"@daftano"
4.
5.  ARG JAVA_VERSION=8
6.  ENV JAVA_HOME=/usr/lib/jvm/java-${JAVA_VERSION}-oracle
7.
8.  ENTRYPOINT ["/usr/bin/java"]
9.  CMD ["-version"]
10.
11. RUN \
12.   echo oracle-java${JAVA_VERSION}-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections \
13.   && add-apt-repository -y ppa:webupd8team/java \
14.   && apt-get update \
15.   && apt-get install -qqy --no-install-recommends oracle-java$ {JAVA_VERSION}-installer oracle-java${JAVA_VERSION}-set-default \
16.   && rm -rf /var/lib/apt/lists/* \
17.   && rm -rf /var/cache/oracle-jdk${JAVA_VERSION}-installer
18.
19. ADD ["./jce-policy/${JAVA_VERSION}/*.jar", "/usr/lib/jvm/java-${JAVA_VERSION}-oracle/jre/lib/security/"]

Я хотел гибкое изображение для основных версий Java без необходимости поддерживать несколько идентификаторов Dockerfile практически идентичными, за единственным исключением для версии Java. И в строке 5 я использовал аргумент сборки со значением по умолчанию версии 8. Это означает, что если вы запустите обычную команду сборки, Docker создаст образ с использованием Java 8. Но если вы захотите собрать его для Java 7, вы должен предоставить аргумент команде build следующим образом: –build-arg JAVA_VERSION = 7 .

Этот образ не предназначен для запуска в одиночку; но в качестве базового слоя для другого изображения я решил вывести версию java, если бы вы запустили ее самостоятельно (строки 8 и 9).

Когда вы устанавливаете Java в Ubuntu, установщик попросит вас подтвердить свое согласие с условиями лицензии и больше не будет запрашивать ту же версию, но когда вы создаете образ, вы не можете взаимодействовать с установщиками и вашей сборкой. не удастся. Поскольку вы уже используете Java и приняли эти условия, в строках 12 отмечается ваше согласие, а в строках с 13 по 15 устанавливается Java для вас и устанавливается JVM по умолчанию.

Чтобы размер моего изображения был наименьшим, в строках 16 и 17 удаляется информация о кеше и установщик Java.

Строки 19 добавляют вышеупомянутые файлы политики JCE. Если они вам не нужны, вы можете безопасно удалить эту строку.

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

См. Оригинальную статью здесь: Архитектура микросервисов с Java и Docker.

Мнения, высказанные участниками Java Code Geeks, являются их собственными.