Туториалы делятся на две больших категории: либо "как нарисовать сову", либо подробно расписанные тысячи шагов в формате "напиши туториал для дурака - и только дурак захочет его читать". К какой из двух категорий относится эта статья — решать вам. В этой статье вы увидите пошаговое создание cloud-native микросервиса на Amazon AWS, пригодное для "чтения с листа". Чтобы понять, что здесь происходит, не нужно разворачивать проект - достаточно обладать живым воображением и прочитать текст по диагонали. Если же вы всё-таки захотите повторить шаги, вам будут жизненно нужны знания вида, как создавать классы в IDE и что такое Spring. Вначале мы напишем пару простых микросервисов на Spring Boot, докеризуем их, зальём в AWS, настроим красивые доменные имена и HTTPS, прикрутим логирование и мониторинг, Prometheus и Grafana. Это небольшое путешествие по всем кругам ада, из которого вы не вернетесь прежним. Текст написан на основе текстов и демо-проекта microservice-customer за авторством @kamaruzzaman. Если вы потеряли нить повествования, всегда можно зайти на GitHub и найти весь код в пригодном для запуска виде. Если захочется закопаться в тему, то бро Дима Чуйко (@Teapot) написал вам ещё две части статьи "Микросервисы: от CRUD до Native Image" (раз, два). Последняя важная оговорка. В этом гайде будут использоваться технологии Amazon и обычные дистрибутивы OpenJDK. Автор осознает, что мы живём в России, и возможно, вместо Amazon куда лучше подойдет что-то вроде SberCloud или MTS Cloud, а вместо обычного OpenJDK - Axiom JDK с сертификацией по ФСТЭК. Особенности российских технологий - тема для отдельной статьи. Если вы захотите таковую после чтения этого гайда - отметьтесь в комментариях. Какую выбрать Java? Многие всё ещё используют Oracle JDK и не осознали, что он имеет определенные лицензионные проблемы. К счастью, у нас есть множество альтернативных дистрибутивов OpenJDK, которые мы и будем использовать. Все они на 100% совместимы со стандартом, и всё такое прочее, т.е. годятся для серьезного продакшена. Версию Java не имеет смысла брать ниже 11 — новые версии куда лучше годятся для облачного использования. Какие выбрать фреймворки? Сейчас есть куча отличных технологий, которые не стыдно использовать на проде - Spring (Pivotal), MicroProfile (Eclipse), Quarkus (RedHat), Micronaut (Object Computing), и другие. Имхо, рынок сейчас устроен так, что Spring задоминировал совершенно всех. Если Quarkus и Micronaut воспринимаются как смелые эксперименты, то Spring давно зарекомендовал себя как "дефолтная" Java-технология, с тех самых пор как JavaEE начала трещать по швам. Можно считать это Spring-шовинизмом, но писать туториал сразу по всему зоопарку технологий - задача непосильная. Наверное, немного грустно собирать проект из исключительно дефолтных технологий, но хей - именно это обычно и нужно в реальной жизни. Что-то, что действительно будет работать и не сломается на полпути. Очевидно, что разработать свою копию Амазона или Нетфликса было бы эпической задачей, совершенно не подходящей под формат Хабра. Вместо этого мы соберем какой-то небольшой магазинчик на микросервисной архитектуре. Бэкенд будет без фронтенда, как это модно сейчас говорить - headless. Это отличное слово для оправдания своей лени, запомните и никогда не забывайте. В отсутствие интерфейса, тестировать будем через HTTP API, то есть, через curl или Postman. Мы напишем микросервисы Customer и Order, и каждый из них будет делать одну свою маленькую задачу, как и положено в соответствии с канонами микросервисной архитектуры, да и хорошей архитектуры вообще (single responsibility principle). Во-первых, давайте создадим проект Spring Boot с нужными зависимостями. Для этого есть совершенно стандартная для Spring веб-утилита Spring Initializr. Что нам важно: Project: Gradle или Maven (лучше Gradle) Language: Java Spring Boot: 2.4.2 (ну или какая там будет доступна, они меняются) Project Metadata: любые какие понравятся Packaging: JAR Java: 11 Dependencies:  Lombok Spring Web Spring Configuration Processor Spring Boot DevTools Spring Data MongoDB Validation Проект разделен на несколько пакетов, как и положено в соответствии с Domain-Driven Design. Классы домена - это POJO (простые Java-объекты), которые вместе составляют API модели данных. Эти POJO могут пригодиться для хранения данных в MongoDB: их преимущество в том, что не нужно лепить несколько слоёв DTO (data transfer objects) для того, чтобы замапить сущности REST на базу данных. Вот как выглядит POJO для микросервиса Order: Я предлагаю сразу использовать Lombok , чтобы не писать обычный шаблонный код, за который так хейтят Java. Геттеры, сеттеры, toString, hashCode, equals. Мы же хотим, чтобы микросервисы у нас получились модные и молодежные? В любом случае, я решил использовать Lombok, туториал — не место для демократии :-) Автоматический аудит заказов будет производиться с помощью аннотаций @CreatedDate, @LastModifiedDate и @Version. Микросервис Order у нас состоит всего из одной коллекции, и все ее сущности (продукты, адреса, итп) стоит встроить прямо в эту коллекцию, а не хранить отдельно. В качестве базы данных я выбрал MongoDB. Существует поверие, что Mongo - ужасная база данных, которая ни на что не годна. Ирония в том, что она идеально подходит для коротких туториалов. Deal with it. В MongoDB есть два способа работать с базой. Первый - использовать Java Driver, который поддерживает нативный, низкоуровневый API базы. Второй и более простой вариант - использовать Spring Data MongoDB. Spring Data - это проект и инициатива сообщества Spring, целью которой является унификация доступа к различным хранилищам (SQL, NoSQL, и т.п.).  И она тоже поддерживает идею Domain-Driven Design. В этой демке, мы будем использовать Spring Data MongoDB чтобы упростить разработку и понимание текста. Чтобы работать с сохранением заказов в базу, нужно создать всего один интерфейс: Наша микросервисная архитектура выставляет наружу API, которое клиент или какой-то другой микросервис может использовать чтобы общаться с помощью REST. Spring MVC поддерживает паттерн написания контроллеров, которые выставляют наружу REST API, и делается это буквально в несколько строчек. Для большинства приложений, хорошая практика - заводить эндпоинт /health , с помощью которого можно проверить, что приложение действительно запустилось и работает. Кроме того, его можно использовать в Kubernetes, чтобы посылать heartbeat-запросы. Для начала, создадим класс Health: И дополняющий его класс HealthResource: Для микросервиса Order, эндпоинты сервиса определяются в классе  OrderResource , который предоставляет REST API для всех CRUD операций, относящихся к заказам. Для простоты, сейчас сделаем только один метод, createOrder (POST request). Последний штрих — добавить файлы конфигурации (application.yml, index.html) и один класс ApplicationConfiguration. index.html: application.yml в директории src/main/resources: application.yml в директории src/main/local: Во-первых, классы ApplicationConfiguration, Health, HealthStatus и HealthResource совершенно идентичны предыдущему микросервису Order, поэтому их можно и нужно просто скопипастить. А вот список сущностей мы будем использовать другой: Дальше нужно сделать CustomerRepository по аналогии с OrderRepository: Эндпоинты сервиса определяются в классе CustomerResource, и предоставляют REST API для всех CRUD операций, связанных с клиентами. Кроме того, нужно создать CustomerOrderResource, который предоставит REST API для связи между Customer и Order. Ну и традиционно, файлы конфигурации: index.html: application.yml в src/main/local: application.yml в src/main/resources: В Spring принято хранить бизнес-логику внутри сервисного слоя. Логику синхронизации Customer и Order придется положить в микросервис Order в таком виде: Что мы сейчас имеем на руках? Мы примерно осознали, чем будем заниматься (делать микросервис на Spring Boot и Mongo), написали базовые сущности (Controller, Service, Repository, Entity). Код должен компилироваться без ошибок. Прелесть микросервисной архитектуры в том, что микросервисы можно писать сразу на многих языках программирования и нескольких разношерстных технологиях, даже внутри одного проекта. Строго говоря, обсуждая облачную инфраструктуру, нужно всегда задаваться вопросом: а как мы будем писать в ней на нескольких языках сразу?  Тем не менее, многоязычные проекты всё ещё не стали золотым стандартом индустрии (хотя и применяются активно в известных конторах типа Netflix). Поэтому, для простоты будем здесь и дальше считать, что пишем всё на Java. Чтобы развернуть наше приложение в облаке, нужно построить инфраструктуру, в которой участвуют Docker и Kubernetes. Docker позволяет упаковать приложение и всю его уйму зависимостей в контейнер. Этот контейнер запускать в разных средах, поддерживая безопасную и изолированную среду внутри него. Другие преимущества контейнеров: Возможность частичного обновления отдельных участков кода, без необходимости останавливать всё приложение; Многослойные образы, которые можно переиспользовать для создания новых контейнеров; Автоматическое развертывание; Возможность откатываться до предыдущей версии образа. Минутка рекламы закончена. В этот момент вы должны понять, что Docker — это хорошо, а если не поняли — закрыть статью, отформатировать свой жесткий диск и пойти мести крыши. Spring Boot предоставляет Cloud-Native Buildpack , который генерирует докерный образ из проекта Spring Boot. Если вы живете в экосистеме Spring, то ваше приложение уже автоматически на полпути в облако. Один из способов разворачивать приложения Spring Boot или отдельные микросервисы в AWS — создавать докерные контейнеры в формате OCI, использовать Amazon ECR в качестве облачного докерного реестра, Amazon EKS в качестве облачного Kubernetes. Другой способ — использовать AWS Elastic Beanstalk, который предоставляет Platform as a Service (PaaS) для развертывания веб-приложений в облаке AWS, так что это легко масштабировать и настраивать. В этом гайде мы об этом говорить не станем. К сожалению, в короткой демке не затронуть всех аспектов жизни в облаках, типа CI/CD, классического JavaEE подхода с помощью MicroProfile, построения распределенных систем, и тому подобного. В голове всё это нужно держать, но сейчас сконцентрируемся на контейнеризации. Вначале нужно установить Docker на компьютер. Процесс этот сильно отличается на разных операционных системах и состоит из множества шагов, поэтому нужно последовать советам из официальной документации. Микросервисы для упаковки у нас уже есть. Теперь, как построить образы Docker? Теоретически, можно написать свой Dockerfile. Минус в том, что для этого нужно неплохо разбираться в Docker и прочих контейнерных технологиях. Но мы не хотим разбираться, хотим раз-два и в продакшен. Для этого у нас есть Cloud Native Buildpacks , которые умеют превращать исходники в  OCI-совместимые Docker-образы без необходимости писать Dockerfile вручную.(Если вы на самом деле хотите разобраться с вопросом: нормально ли ничего не знать о Docker и генерить всё билдпаками, то обязательно ходите на конференции JUG Ru Group. Например, на JPoint был отличный доклад "Не клади все яйца в один buildpack" Дмитрия Чуйко, запись доступна на YouTube). Чтобы собрать образ микросервиса Customer, достаточно пройти в его корневую директорию и выполнить команду: Если вам больше нравится Gradle, то команда будет другая: Эта команда собирает образ на основе файла конфигурации Spring. В нашей демке, образ микросервиса Customer будет называться как-то так: docker.io/library/microservice-customer:1.0.0 Точно такй же командой собирается и микросервис Order: docker.io/library/microservice-order:1.0.0 Запустить их можно, использовав это имя: Посмотреть, что микросервис запустился, можно на стандартном адресе: http://localhost:8080. Если контейнер успешно запустился, вы увидите идентификатор этого контейнера в консоли. В вашем случае он будет другой, все идентификаторы уникальны: Посмотреть логи контейнера можно с помощью следующей команды: И вы увидите там нечто вроде: Важно отметить, что именно форвардинг портов  (-p 8080) позволяет нам соединяться с нашими сервисами снаружи контейнера, например - с локального компьютера. Можно вписать туда и другую цифру, 8080 просто имеет какое-то ритуальное значение для джавистов. Теперь становится возможно проверить статус контейнера через GET запрос. Сделать запрос можно разными способами, для простоты пусть это будет curl в консоли: Вот такой результат мы ожидаем: Чтобы остановить контейнер, нужно выполнить docker stop  с указанием идентификатора останавливаемого контейнера: Мы создали два микросервиса, научились упаковывать их в контейнеры и запускать на своем локальном компьютере. Следующий вопрос - как запустить их в облаке? Для публикации контейнеров мы воспользуемся Amazon ECR. Это Docker Registry, который позволяет хранить, распространять и развертывать контейнеры в облаке AWS. Вначале, нужно установить и настроить утилиты командной строки AWS CLI на вашем компьютере. Конкретные шаги описаны в официальной инструкции. Я не скопировал их в эту статью исключительно из гуманистических соображений: пара таких инструкций, и Хабр превратится в чертов инсталлятор. Теперь, для каждого микросервиса нужно создать соответствующий репозиторий в ECR. Важно, чтобы имя репозитория в точности соответствовало названию имени контейнерного образа. Эта команда создаст репозиторий для микросервиса Order, и должна распечатать вот такой выхлоп: К нашему локально лежащему образу нужно привязать тэг, в котором описан реестр ECR и репозиторий.  Вначале нужно взять идентификатор докерного образа для микросервиса Order, и посмотреть о нём полную информацию: Ожидаемый выхлоп: Теперь, создаем тэг, указывая ECR и репозиторий: Для публикации образа в ECR, нужно залогиниться. Этот логин продержится 12 часов, после чего придётся перезайти заново. Ожидаемый выхлоп: Теперь можно запушить образ в AWS ECR: Скорость загрузки зависит от того, насколько быстрый у вас интернет. Это может занять несколько минут, за которые можно вскипятить чаю и поесть. После загрузки, неплохо сходить в веб-интерфейс и проверить, что всё получилось: Но самая главная проверка, конечно же, делается не глазами. Давайте попробуем запуллить образ: Если вся предыдущие шаги были выполнены правильно, мы увидим долгожданный ответ: Нашим микросервисам нужна база данных, без них они не заработают. Поэтому, нужно развернуть на Амазоне кластер MongoDB. Зарегистрируйтесь на MongoDB Atlas. После регистрации, создайте бесплатный Shared cluster, выберите cloud provider (AWS) и какой-нибудь регион поближе, и жмите кнопку "Create Cluster". Если не знаете, какой регион выбрать - берите Франкфурт, не ошибётесь. Дальше нужно создать пользователя, указав логин и пароль. Как только кластер создастся, вы увидите такое: Теперь, нужно сгенерировать строчку для соединения с кластером, которую мы пропишем в микросервисы. Нажмите кнопку "Connect", выберите "Connect your application". Важно: строка для подключения зависит от вашего языка программирования и версии MongoDB. Там будут какие-то инструкции, их все нужно выполнить. Kubernetes (или, для краткости, Куб) — стандарт индустрии для инфраструктурной оркестрации. Он изначально разработан в Google, выложен в опенсорс, и сейчас разрабатывается всем миром. Он помогает в разворачивании, масштабировании и управлении контейнеризованными приложениями. Если вас это зачем-то интересует, то Kubernetes отлично работает на Docker Desktop, у него в поставке есть сервер и клиент. Не то чтобы это действительно нужно было делать. К сожалению, Kubernetes требует кучу сисадминской работы. Если хочется только программировать и оставить админскую работу админам, лучше использовать облачные решения, т. е. Managed Kubernetes. Для cloud-native разработки, воспользуемся Amazon Elastic Kubernetes Service (EKS). Он позволяет запускать и масштабировать приложения как в облаке AWS Cloud, так и on-perm на локальном железе. Установите утилиты для командной строки:  eksctl для управления кластером EKS, и kubectl для Kubernetes. Теперь можно создать кластер EKS, используя команду eksctl: Эта команда создаёт два воркера типа "t2.small" в регионе "eu-central-1", с именем "microservice". Где-то там глубоко в кишках, для создания кластера, eksctl использует CloudFormation. Это занимает минут 10-15, поэтому вы снова сможете вскипятить чаю и поесть. Используя облака, вы никогда не умрёте с голоду на рабочем месте. В любом, случае, вот такой в конце концов вас ждет выхлоп: По выхлопу видно, что у нас создалось две ноды и одна группа. Кроме того, конфиг теперь лежит в  ./.kube/config. Если у вас уже есть minikube или microk8s, нужно дописывать этот ./.kube/config в качестве параметра --kubeconfig: После того, как прошла целая вечность и кластер создался, можно проверить его статус командой: Ожидаемый выхлоп: Двигаемся дальше. Создадим конфиг развертывания: Теперь, скормим его kubectl, чтобы развернуть приложение в Kubernetes: Ожидаемый выхлоп: Проверим, что всё запустилось: Ожидаемый выхлоп: Для пущей уверенности, можно проверить лог пода: Ожидаемый выхлоп: По логу видно, что микросервис не только запустился на порту 8080, но и подключился к MongoDB Atlas. Несмотря на то, что микросервис корректно развернулся в EKS, снаружи до него достучаться невозможно. Нужно создать Kubernetes Service Controller, который выставит наружу IP-адрес, по которому к развернутым приложениям можно будет обратиться из внешнего мира. Вот как определить сервис: Заметьте, что targetPort должен совпадать со значением containerPort, которое определено в файле развертывания (в нашем случае, ритуальное 8080). Теперь можно развернуть сервис в AWS: Ожидаемый выхлоп: Сервис привязан к Elastic Load Balancer (ELB) в AWS. Теперь можно проверить, что лежит на внешнем IP-адресе: Судя по выхлопу, балансировщик находится вот на этом внешнем адресе: aa62f80b9596a4fa6835d80a506227d6-1183908486.eu-central-1.elb.amazonaws.com Конечно же, можно сходить и в веб-интерфейс: Заметьте, что DNS имя в ELB — точно такое же, как у внешнего IP-адреса, который мы получили ранее. Если открыть этот адрес в браузере, то мы увидим веб-страничку нашего микросервиса: Аналогично, можно развернуть микросервис Order. Просто повторить шаги, перечисленные выше: создать докерный образ, опубликовать его в ECR, развернуть в EKS. Конечно, вначале, нужно пописать эндпоинт микросервиса Customer в application.yml микросервиса Order: Все остальные шаги совершенно те же самые. Использовать Amazon ECS далеко не дешево. Там и сама инфраструктура EKS (мастер-узел), и воркеры, и балансировщики, и группа узлов. В продакшене они крутились бы круглосуточно, но если вы изучаете вопрос по туториалам, стоит вовремя избавляться от ненужного балласта. К счастью, делается это предельно просто: Burn, mazafaka, burn. Миссия практически выполнена: мы развернули контейнеры в Kubernetes и EKS. Но это ещё не предел мечтаний. У микросервисов уродливые DNS имена, трафик ходит без HTTPS, логирования никакого нет. Это не очень похоже на прод. Amazon Route 53 — это высокоустойчивый, масштабируемый облачный DNS. В дополнение к классическим возможностям DNS-роутинга, он позволяет регистрировать домены и проверять живость сервисов. Конечно же, можно использовать какой-то другой провайдер DNS. В этом гайде мы воспользуемся именно Amazon Route 53, потому что это просто и удобно. Вначале, откройте вкладку "Route 53" и выберите “Register domain": Например, можно выбрать имя “microservicesdemo”. Сразу после этого отображаются другие домены с именем “microservicesdemo”. На момент регистрации, microservicesdemo.net было свободно и стоило около 11 американских USD в год. Дальше вам предлагают вписать разные дополнительные сведения, необходимые для завершения регистрации. Сразу после этого, домен отправляется на регистрацию. После регистрации имени, Route 53 автоматически настраивает DNS для зарегистрированного домена. Кроме того, создается "Hosted zone", с таким же именем, как и у зарегистрированного домена. Нам нужно два субдомена для двух микросервисов, трафик с них нужно отправить на балансировщик. Для этого создадим рекорды внутри "Hosted zone": В поле ”Routing policy” нужно выбрать “Simple routing”. В разделе “Configure record”, нужно создать “Simple record” для микросервиса Customer, сконфигурить его доменное имя, и отправить трафик на балансировщик: В награду за старания, микросервис Customer теперь доступен по новому имени: Route 53 DNS Service перенаправляет трафик с субдоменов на балансировщик. Теперь, нужно повторить все те же самые шаги и сконфигурировать микросервис Order. Для любого более-менее энтерпрайзного приложения подразумевается, что трафик ходит через TLS 1.2+. Весь трафик между браузером пользователя и балансировщиком - терминируется TLS (точнее, шифруется и дешифруется по HTTPS). Заметьте, что соединения между балансировщиком и подами шифровать не нужно: Чтобы управлять сертификатами SSL/TLS у нас есть Amazon Certificate Manager, который мы и заиспользуем на балансировщике. Откройте Amazon Certificate Manager и найдите раздел с публичными сертификатами: Дальше нужно запросить сертификат: На следующей странице указываем домен: AWS хочет удостовериться, что домен принадлежит именно нам. Самое простое — выбрать вариант “DNS validation”, раз уж мы всё равно копаемся в админке DNS. Теперь мы видим DNS-запись CNAME, которую нужно добавить в конфигурацию домена. Можно сделать это автоматически, прожав кнопку “Create record in Route 53”. (Ценители могут мучиться с конфигом DNS самостоятельно). Как только сертификат появится, мы увидим вот такой экран: Что мы здесь видим? Статус сертификата — "issued", то есть он корректно выдан AWS. Интерфейс показывает "no" в поле "in use", потому что мы еще не настраивали использование сертификата. Теперь, нужно терминировать SSL/TLS на балансировщике. Еще раз напомню, что соединение между балансером и подами зашифровано не будет. Вначале нужно сделать Load Balancer Service “eks-service-tls.yaml” в директории “src/main/k8s”: По сравнению с конфигом из предыдущей части, произошли некоторые изменения. Аннотациями мы сконфигурировали сертификаты, только что выданные в ACM: Ещё, мы явно определили, что протоколом на бэкенде будет http. Чтобы получить заголовок “X-Forwarded-Proto” мы установили “service.beta.kubernetes.io/aws-load-balancer-backend-protocol” в значение  "http". Ну и порт поменяли на “443”, потому что https. Теперь можно пройти на страничку микросервиса Customer прямо из браузера: https://customer.microservicesdemo.net/customer На скриншоте можно увидеть значок "замка" рядом с URL, который означает, что мы используем защищенное соединение. Если щелкнуть по "замку", то мы увидим подробную информацию о только что выданном сертификате SSL/TLS (в том числе то, что он выдан Amazon, дата выдачи - сейчас, и тому подобное). Все те же самые шаги нужно сделать для микросервиса Order. Логирование — неотъемлемая часть разработки и эксплуатации. Для продакшена же оно совершенно необходимо. В ходе логирования мы собираем как можно больше полезных данных о том, как работает приложение, и о всех важных событиях. Примеры, зачем это нужно: Если приложение сломалось, мы можем проанализировать логи и найти корень проблемы; Если в приложении закрался баг, по логам мы можем воспроизвести проблему; Если необходимо понять, как работает приложение. В нашем демо-проекте мы тоже пишем логи. Но как их собрать и проанализировать? Есть много способов экспортировать логи наружу. Их можено писать в файлы, собирать с помощью специальных API, выливать в STDOUT на консоль, использовать какие-то коллекторы... Согласно идеям twelve factor apps, которые являются золотым стандартом современной разработки, приложение не должно само писать в файлы или коллекторы. Вместо этого, приложение складывает логи в небуферизованный STDOUT, как в поток ивентов. В случае разработки на локальной машине, программисты имеют возможность видеть этот поток ивентов прямо в своей консоли. На стейджинге или проде, логи собираются средой исполнения. Роутеры и коллекторы логов собирают и отправляют их в какое-то определенное место конечного хранения (например, базу данных или файл), где их можно складировать и анализировать. В нашем демо-проекте из двух микросервисов, мы последуем путями “12 factor apps logging”: сгенерируем лог как поток событий, который поймают поды Kubernetes, отправим его в коллектор и анализатор, а в качестве места последнего упокоения заиспользуем Amazon Cloud Watch. На рынке существует куча разных коллекторов логов. Чаще всего используют вендор-нейтральный Fluentd. Он заслужил определенную славу в последние годы, из-за своих лёгких, но довольно функцинальных фичей. Более того, Fluent Bit, в первую очередь, предназначен именно для работы с контейнерами. Для нашего демо-проекта, мы заиспользуем Fluent Bit, чтобы собрать данные из подов. Для дальнейшего анализа пригодится Amazon CloudWatch Container Insights. Вот примерная схема используемых компонентов: Fluent Bit — это опенсорсный процессор и форвардер логов, который позволяет собирать всевозможные данные (например, метрики и логи) из различных источников, фильтровать и отправлять их по нескольким направлениям, включая CloudWatch. Это очевидное решение для контейнерных технологий, в том числе, для Kubernetes. Архитектура Fluent Bit создана с расчетом на высокую производительность. Для этого она написана на Си и позволяет работать с высокой пропускной способностью, не тратя на это излишние ресурсы процессора и памяти. В нашем кластере EKS есть следующие типы логов: Application logs — создаются приложением и лежат в /var/log/containers/*.log Host logs — системные логи, которые создаются хостовыми нодами EKS и хранятся в файлах /var/log/messages,/var/log/dmesg,/var/log/secure Data Plane logs — создаются компонентами EKS Data Plane В Kubernetes существует концепция DaemonSet: все ноды держат по копии пода. Это полезно для кластерных операций, таких как сбор логов и мониторинга узлов, при которых поды автоматически добавляются к новым узлам. Кстати говоря, DaemonSet — отличный пример, когда стоит вспомнить о размере контейнерных образов. Постоянная загрузка образов жрёт трафик как не в себя. Но к счастью, образы, генерируемые в Spring Boot, собираются на основе Alpine Linux и специальных дистрибутивов OpenJDK, что позволяет им быть очень маленькими. Базовый образ весит на диске меньше 100 мегабайт. Нам нужно настроить Fluent Bit как DaemonSet, чтобы посылать логи в AWS CloudWatch. Вначале нужно предоставить IAM разрешения для Kubernetes-воркеров, чтобы метрики и логи улетали в CloudWatch. Это делается добавлением политики в IAM hоли воркеров. Заметьте, что это задача администрирования кластера, и делается ровно один раз при его создании. Заниматься этим каждый день, к счастью, не придется. Вначале, нужно выделить IAM роли воркеров (инстансов EC2): Далее, приаттачить политику “CloudWatchAgentServerPolicy” в IAM роли: Далее, создать неймспейс “amazon-cloudwatch”: Далее, создать ConfigMap с именем “fluent-bit-cluster-info”: Здесь приведены дефолтные настройки FluentBitHttpServer. В дополнение, Fluent Bit читает логи из tail, и поэтому после развертывания захватывает только свежие логи. Дальше, нужно скачать и развернуть Fluent Bit DaemonSet: После этих шагов, в вашем кластере появятся следующие ресурсы: Сервис-аккаунт с именем Fluent-Bit в неймспейсе amazon-cloudwatch. Он используется для запуска Fluent Bit daemonSet; Кластерная роль с именем Fluent-Bit-role в неймспейсе amazon-cloudwatch. Она позволяет учетке Fluent-Bit смотреть и загружать логи подов; ConfigMap с именем Fluent-Bit-config в неймспейсе amazon-cloudwatch. В нем хранится конфигурация Fluent Bit. Дальше, нужно на практике проверить, что у нас запустилось: В списке должны быть поды с именем, начинающимся на “fluent-bit-”, по штуке на узел. Ожидаемый выхлоп выглядит так: Если зайти в веб-консоль AWS, наш DaemonSet выглядит как-то так: Если пройти в консоль CloudWatch, то можно посмотреть, правильно ли мы настроили Fluent Bit. Важно проверить, что регион CloudWatch Console совпадает с регионом вашего кластера EKS (в этом гайде, чуть выше по тексту мы выбирали eu-central-1). В логе должны появиться три группы: Можно проверить группу /aws/containerinsights/microservices/application, в которой сложены все события от микросервисов. Лог можно фильтровать и смотреть в аггрегированном виде. В нашем демо-проекте, создание заказа с несуществующим customer ID возвращает “500 Internal Server Error”. Все такие события легко ловятся в логах: Мониторинг микросервисов на проде не менее важен, чем логирование. Для мониторинга Kubernetes создали кучу всевозможных инструментов. Amazon CloudWatch — это "родной" способ мониторить кластеры EC2. Amazon CloudWatch предоставляет сервис Container Insights, который позволяет мониторить, траблошутить и следить за AWS Elastic Kubernetes Service (EKS) и AWS Elastic Container Service (ECS). Дашборда Container Insights показывает: Утилизацию процессора и памяти Количество задач и сервисов R/W хранилища Rx/Tx сети Количество контейнеров Чтобы включить CloudWatch Container Insights, необходимо развернуть в кластере EKS агент CloudWatch для FluentBit: Теперь можно зайти в дашборду CloudWatch Container Insights и понаблюдать за нагрузкой на наш кластер EKS: Что мы имеем на руках? Мы написали пару микросервисов, опакетили в контейнер, запустили на AWS, прикрутили красивые доменные имена и логи. Тем не менее, CloudWatch Container Insights — это не предел мечтаний, и наш рассказ был бы неполон без ещё одной, последней части. Когда вы запускаете Java-приложение (или даже, любое JVM-приложение) в контейнере, то на самом деле оно выполняется на JVM. (Капитан Очевидность отдает честь). Поэтому, если мы мониторим только контейнер, то всей картины получить никак не получится. Для лучшего обзервабилити, нужно мониторить и саму JVM. Одно из ограничений Amazon CloudWatch Container Insights заключается в том, что он может собрать метрики и процессить алерты только на уровне контейнера, а на уровне JVM он так не умеет. Есть несколько инструментов, которые можно использовать для мониторинга JVM микросервисов в Kubernetes. Один из лучших инструментов - Prometheus. Prometheus — популярная и широко используемая база данных для time series (временных рядов). Она входит в Cloud Native Computing Foundation (CNCF). На GitHub у нее больше 40 тысяч звезд, и по сути, это самое популярное опенсорсное решение для мониторинга. Prometheus предоставляет очень эффективное хранилище на time series базе данных, кучу интеграций, крутой язык запросов PromQL, отличные визуализации и алерты. В отличие от CloudWatch, Prometheus работает на основе мониторинга, который пуллит и собирает те данные из приложения, которые выставлены наружу через Metrics API. При разработке cloud-native микросервисов, pull может быть лучше, чем push, потому что это генерирует меньше трафика, создает меньше нагрузки на сеть. Есть два способа, которым Prometheus вытягивает метрики. Первый заключается в том, что приложения выставляют Metrics API с помощью клиентских библиотек. Библиотека позволяет разработчикам выставить наружу важные им бизнесовые метрики, которые потом пойдут в дашборду Prometheus. Этот способ используется для тех приложений, которые вы сами разрабатываете. Второй способ — запускать рядом с приложением Exporter, который сам выставляет метрики через API. Этот метод больше подходит для сторонних приложений, например, для баз данных. Мы будем использовать клиентскую библиотеку. Чтобы включить мониторинг Prometheus на AWS, придется немного доделать наши приложения, написанные на Spring Boot. В Java-приложениях есть несколько способов выставить наружу эндпоинты для мониторинга. Эти эндпоинты используются для сбора метрик приложения. Можно использовать данные Prometheus  вместе с JMX бинами. В Spring, есть Spring Boot Actuator, который используется для создания хороших, готовых для продакшена эндпоинтов, совместимых с любым приложением на Spring MVC. Почитать про Spring Boot Actuator, можно в официальной документации. В нашем микросервисе Customer, нужно добавить следующую зависимость: io.micrometer:micrometer-registry-prometheus. Для Gradle это делается в build.gradle, для Maven - pom.xml соответственно. Кроме того, в конфигурацию нужно доложить еще несколько свойств для корректной работы Prometheus Metrics API: На следующем шаге, нужно заставить работать авто-дискавери, чтобы Prometheus смог сам нас найти и стянуть все данные. Для этого в “eks-deployment.yaml” нужно добавить еще парочку аннотаций: Теперь, возвращаемся в EKS, и создаем неймспейс Prometheus в нашем кластере: Теперь нужно создать докерный образ и развернуть его на неймспейсе “eks-prometheus-namespace”. Этот процесс мы уже описывали ранее в этом гайде. (Жестокая реальность в том, что я не буду копипастить сюда третью часть этого гайда, вам придется осознать это самостоятельно). Как только приложение задеплоилось, нужно пройти в Actuator и проверить эндпоинт Prometheus: Этот эндпоинт (https://customer.microservicesdemo.net/customer/actuator/prometheus) отдаёт следующие метрики: Как всегда, нужно повторить все эти процедуры для второго нашего микросервиса, Order. AMP Workspace — место для того, чтобы получать, хранить и анализировать метрики Prometheus. AMP Workspace помогает изолировать мониторинг различных проектов/приложений. Создать AMP Workspace  можно через AWS CLI или через AWS Console. Идём в Amazon Prometheus и создаем его примерно так: Теперь в консоли AWS можно посмотреть подробности о созданном Prometheus Workspace : AMP не стягивает оперативные метрики автоматически. Для этого нужно сконфигурировать Prometheus Metrics Collector. Он стягивает их с контейнеризованных Java-микросервисов, запущенных в кластере, и посылает в AMP Workspace. Есть несколько способов развернуть Prometheus Metrics Collector в AWS: либо сервер Prometheus, либо агент OpenTelemetry. В этом примере, мы будем использовать AWS-овский дистрибутив OpenTelemetry Collector/Prometheus server. Устанавливать новый сервер Prometheus имеет смысл с помощью Helm. Helm — это популярный пакетный менеджер для Kubernetes. С помощью него легко и приятно искать, распространять и использовать софт, сделанный специально для Kubernetes. Вначале, нужно установить Helm на наш локальный компьютер, как рассказано в официальной документацией. Дальше, нужно добавить репозиторий чартов, где лежит Prometheus: Нужно настроить IAM роли сервис-аккаунтов для приема метрик из кластера EKS. Сервер Prometheus посылает данные с по HTTPS. Данные нужно подписать валидными креденшелами AWS и алгоритмом AWS Signature Version 4 для аутентификации и авторизации каждого клиентского запроса, предназначенного для управляемого сервиса. Запросы отправляются инстансу  AWS signing proxy, который перенаправляет эти запросы на управляемый сервер. AWS signing proxy может быть развернут на кластере EKS и может запускаться от имени сервис-аккаунта Kubernetes. С помощью IAM Roles for Service Account (IRSA), можно связать роль IAM с сервис-аккаунтом Kubernetes, и тем самым предоставить полномочия для каждого пода, который использует этот сервис-аккаунт. Это соответствует принципу минимальных привилегий: IRSA используется для безопасной настройки AWS signing proxy, чтобы переливать метрики Prometheus в AMP. Подробное объяснение того, как для AMP настроить IAM роли сервис-аккаунтов можно прочитать в официальном гайде. После создания двух ролей (одной для приема метрик, и второй для запросов), мы можем проверить правильность происходящего, поискав эти роли в консоли: С помощью Helm можно переопределить конфигурацию Kubernetes. Нужно создать файл с именем “prometheus_eks_values.yaml” и содержимым типа такого: В этом файле, мы просим использовать целевой Amazon Managed Service for Prometheus, а сервер Prometheus, запущенный на EKS Kubernetes, будет посылать данные с мониторинга. Теперь, создадим сервер Prometheus следующей командой Helm: Grafana — это опенсорсная веб-консоль для аналитики и визуализации данных. Он предоставляет дашборд с графиками, таблицами, алертами (будучи подключена к источнику данных, который такое поддерживает). Grafana оказывается полезной для логирования, мониторинга, трейсинга, и так далее. Часто она используется в тандеме с Prometheus: можно показать дашборд с данными Prometheus на AWS. Amazon предоставляет полностью управляемую облачную Grafana. Дальше я опишу шаги, которые позволяют настроить амазоновскую Grafana так, чтобы она начала визуализировать данные от Prometheus. Для аутентификации в управляемой Grafana, мы будем использовать AWS SSO. Если у вас включена “AWS Organization”, то вы можете сделать пользователя AWS SSO как-то так: Дальше, нужно создать воркспейс для Grafana.  Grafana Workspace — это некий виртуальный сервер, который используется как общий дашборд для данных из различных источников. Из раздела “Amazon Grafana” > Workspaces, новый воркспейс можно создать, выбрав опцию “Create workspace”: Теперь нужно указать имя воркспейса, в нашем примере это “prometheus-metrics”: Дальше, этот воркспейс нужно настроить. Мы будем использовать AWS SSO как метод аутентификации, и тогда ранее созданный аккаунт WEB SSO можно будет использовать чтобы залогиниться в Grafana. “Permission Type” установим в значение “Service managed”, тогда AWS автоматически будет предоставлять нужные разрешения. На последней странице мастера, нужно установить “IAM permission access settings” в значение “Current Account”. В разделе “Data Sources” можно выбрать разные источники данных, и таким образом, у нас будет один дашборд Grafana, который можно использовать для разных целей и разных инструментов (Prometheus Monitoring, AWS CloudWatch Monitoring, AWS X-Ray Tracing, итп). Сейчас в качестве источника данных выберем “Amazon Managed Service for Prometheus”. Для нотификаций пусть будет “Amazon SNS”. Как только воркспейс создан, становится возможным привязать нашего ранее созданного пользователя AWS SSO к этому воркспейсу: Далее, логинимся в воркспейс Grafana, пройдя на URL воркспейса, через AWS SSO. Сразу после логина, мы можем добавить воркспейс Prometheus в качестве “Data Source”: В этом датасорсе, нужно использовать эндпоинт Prometheus Query URL, но без суффикса  “/api/v1/query”. Ещё, нужно включить “SigV4 auth” в разделе “Auth”. В разделе “SigV4 Auth Details”, нужно выбрать правильный регион (в этом демо-проекте - “eu-central-1”) и “AWS SDK Default” как провайдер аутентификации: Дальше, нужно добавить дашборд для мониторинга наших приложений Spring Boot, запущенных на кластере EKS Kubernetes. На сайте grafana.com уже есть дашборд для мониторинга Spring Boot APM. Можно импортировать его по id = "12900". После успешного импорта, можно увидеть дашборд с данными мониторинга Kubernetes, включая JVM-специфичную информацию типа использования CPU или использования джавной кучи: И конечно, какой же джавист не хочет посмотреть на подробности работы сборщика мусора? В дополнение к мониторингу JVM, мы можем делать продвинутый мониторинг Kubernetes, используя Prometheus и Grafana. Добавив Grafana dashboard for Kubernetes , можно видеть метрики кластера Kubernetes (количество узлов, подов, свойства кластера). Еще, можно искать метрики за какой-то конкретный период времени. Вот снапшот с несколькими репликами, запущенными на кластере в неймспейсе “eks-prometheus-namespace”: Ещё, мы можем смотреть историю реплики с нужными критериями поиска: Диаграмма выше показывает, что все наши реплики (4 для микросервисов и 4 для кластера Prometheus), отлично себе работают и не падают. На проде очень важно установить правильный набор алертов, чтобы вовремя заметить некорректную работу приложения. Алерты позволяют техподдержке быстро отреагировать на ошибку на проде. Алерты просто необходимы, если вам нужно выполнять какие-то понятные SLA/SLO на приложение. Вот пара примеров того, что можно использовать в алертах: CPU используется более чем на 80% RAM используется более чем на 80% Поды перезагружаются каждые 10 минут База данных заполнена на 90% Когда у вас сработал какой-то алерт, активируется триггер, сообщающий об этом команде техподдержки или безопасникам. В зависимости от того, какой алерт сработал, можно использовать разные каналы оповещения: электронную почту, SMS, чаты. В этом гайде мы совершили небольшое путешествие по разработке микросервисов. Оно началось с написания кода в IDE и закончилось наблюдением графиков в Grafana. Надеюсь, этот гайд окажется хорошей отправной точкой для людей, в первый раз погружающихся в страшный волшебный мир микросервисов на Java и всего того DevOps ада, который ждёт вас при встрече с облачными провайдерами. Все сообщения об опечатках, отвратительных англицизмах, битых ссылках и прочих вопросах по форме, а не по сути — прошу пройти с ними в личку, чтобы не мусорить в комментариях. Если хотите пообщаться со мной по существу, лучше использовать не личку Хабра (там миллионы каких-то сообщений, шансов разгрести которые нет), а писать на рабочую почту (по рабочим вопросам), либо в личный Facebook и личную почту (по приватным вопросам). Отдельная просьба к сотрудникам облачных провайдеров. Если вы готовы предоставить помощь в написании подобной статьи не для Amazon, а для ваших облаков (SberCloud, MTS Cloud, Selectel, Yandex.Cloud, VK cloud solutions, итп), свяжитесь со мной любым способом. Помощь может выражаться, например, в экспертизе по использованию ваших аналогов зарубежных сервисов, помощи в вычитке статьи, либо предоставлении демо-доступа до ваших облаков. Приятного Сезона Java на Хабре!  Деврел Axiom JDK, продюсер в Failover Bar