Эта статья пытается предоставить один из возможных подходов к созданию микросервисов . Мы будем использовать Scala в качестве языка программирования. API будет RESTful JSON, предоставленным Spray и Akka . MongoDB будет использоваться в качестве базы данных. Как только все будет сделано, мы упакуем все в контейнер Docker. Vagrant с Ansible позаботится о нашей среде и потребностях в управлении конфигурацией.
Мы сделаем сервис книг. Это должно быть в состоянии сделать следующее:
- Список всех книг
- Получить всю информацию, связанную с книгой
- Обновить существующую книгу
- Удалить существующую книгу
Эта статья не будет пытаться научить всему, что нужно знать о Scala, Spray, Akka, MongoDB, Docker, Vagrant, Ansible, TDD и т. Д. Нет единой статьи, которая могла бы сделать это. Цель состоит в том, чтобы показать поток и настройку, которую можно использовать при разработке сервисов. На самом деле, большая часть этой статьи одинаково актуальна для других типов разработок. Docker имеет гораздо более широкое использование, чем микросервисы, Ansible и CM в целом могут использоваться для любого типа предоставления, а Vagrant очень полезен для быстрого создания виртуальных машин.
Среда
Мы будем использовать Ubuntu в качестве сервера разработки. Самый простой способ настроить сервер — это Vagrant . Если у вас его еще нет, скачайте и установите его. Вам также понадобится Git для клонирования хранилища с исходным кодом. Остальная часть статьи не потребует дополнительных ручных установок.
Давайте начнем с клонирования репо.
|
1
2
|
git clone https://github.com/vfarcic/books-service.gitcd books-service |
Далее мы создадим сервер Ubuntu, используя Vagrant. Определение следующее:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
# -*- mode: ruby -*-# vi: set ft=ruby :VAGRANTFILE_API_VERSION = "2"Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "ubuntu/trusty64" config.vm.synced_folder ".", "/vagrant" config.vm.provision "shell", path: "bootstrap.sh" config.vm.provider "virtualbox" do |v| v.memory = 2048 end config.vm.define :dev do |dev| dev.vm.provision :shell, inline: 'ansible-playbook /vagrant/ansible/dev.yml -c local' end config.vm.define :prod do |prod| prod.vm.provision :shell, inline: 'ansible-playbook /vagrant/ansible/prod.yml -c local' endend |
Мы определили окно (ОС) как Ubuntu. Папка синхронизации — / vagrant, что означает, что все содержимое текущего каталога на хосте будет доступно как каталог / vagrant внутри виртуальной машины. Остальные вещи, которые нам понадобятся, будут установлены с использованием Ansible, поэтому мы предоставляем нашу виртуальную машину с помощью скрипта bootstrap.sh. Наконец, в этом Vagrantfile определены две виртуальные машины: dev и prod . Каждый из них запустит Ansible, который убедится, что все установлено правильно.
Предпочтительным способом работы с Ansible является разделение конфигураций на роли. В нашем случае в каталоге ansible / role есть четыре роли. Один будет убедиться, что Scala и SBT установлены, другой — что Docker запущен и работает, а другой запустит контейнер MongoDB. Последняя роль (книги) будет использована позже для развертывания службы, которую мы создаем, на производственную виртуальную машину.
Как пример, определение роли mongodb следующее.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
- name: Directory is present file: path=/data/db state=directory tags: [mongodb]- name: Container is running docker: name=mongodb image=dockerfile/mongodb ports=27017:27017 volumes=/data/db:/data/db tags: [mongodb] |
Это должно быть самоочевидно для тех, кто работал с Docker. Роль гарантирует, что каталог присутствует и что контейнер mongodb запущен. Playbook ansible / dev.yml — это то, где мы все связываем.
|
1
2
3
4
5
6
7
|
- hosts: localhost remote_user: vagrant sudo: yes roles: - scala - docker - mongodb |
Как и в предыдущем примере, этот также должен быть самоочевидным. Каждый раз, когда мы запускаем эту пьесу, все задачи из ролей scala, docker и mongodb будут выполняться.
Хорошая особенность Ansible и Configuration Management в целом заключается в том, что они не запускают сценарии вслепую, а действуют только при необходимости. Если вы запустите инициализацию во второй раз, Ansible обнаружит, что все в порядке, и ничего не сделает. С другой стороны, если, например, вы удалите каталог / data / db , Ansible обнаружит, что он отсутствует, и создаст его заново.
Давайте поднимем виртуальную машину разработчика! В первый раз это может занять некоторое время, так как Vagrant потребуется загрузить весь дистрибутив Ubuntu, установить несколько пакетов и загрузить образы Docker для MongoDB. Каждый следующий пробег будет намного быстрее.
|
1
2
3
|
vagrant up devvagrant ssh devll /vagrant |
vagrant up создает новую виртуальную машину или оживляет существующую. С помощью vagrant ssh мы можем войти во вновь созданную коробку. Наконец, ll / vagrant перечисляет все файлы в этом каталоге в качестве доказательства того, что все наши локальные файлы доступны внутри виртуальной машины.
Вот и все. Наша среда разработки с контейнерами Scala, SBT и MongoDB готова. Теперь пришло время развивать наш книжный сервис.
Книжный Сервис
Я люблю Скалу и Акку . Scala — очень мощный язык, а Akka — мой любимый фреймворк для создания приложений JVM, управляемых сообщениями. Несмотря на то, что Akka была рождена в Scala, она также может использоваться с Java.
Spray — это простой, но очень мощный инструментарий для создания приложений на основе REST / HTTP. Он асинхронный, использует акторы Akka и имеет отличный (хотя и странный в начале) DSL для определения HTTP-маршрутов.
В стиле TDD мы проводим тесты перед внедрением. Вот пример тестов для маршрута, который извлекает список всех книг.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
"GET /api/v1/books" should { "return OK" in { Get("/api/v1/books") ~> route ~> check { response.status must equalTo(OK) } } "return all books" in { val expected = insertBooks(3).map { book => BookReduced(book._id, book.title, book.author) } Get("/api/v1/books") ~> route ~> check { response.entity must not equalTo None val books = responseAs[List[BookReduced]] books must haveSize(expected.size) books must equalTo(expected) } }} |
Это очень простые тесты, которые, как мы надеемся, показывают направление, в котором нужно следовать для тестирования API на основе Spray. Сначала мы убедимся, что наш маршрут возвращает код 200 (ОК). Вторая спецификация, после вставки нескольких примеров книг в БД, подтверждает, что они правильно получены. Полный исходный код со всеми тестами можно найти в ServiceSpec.scala .
Как бы мы реализовали эти тесты? Вот код, который обеспечивает реализацию на основе приведенных выше тестов.
|
1
2
3
4
5
6
7
|
val route = pathPrefix("api" / "v1" / "books") { get { complete( collection.find().toList.map(grater[BookReduced].asObject(_)) ) }} |
Это было просто. Мы определяем маршрут / api / v1 / books , метод GET и ответ внутри полного оператора. В этом конкретном случае мы извлекаем все книги из БД и преобразуем их в класс дела BookReduced . Полный исходный код со всеми методами (GET, PUT, DELETE) можно найти в ServiceActor.scala .
И тесты, и реализация, представленные здесь, являются упрощенными, и в реальных сценариях было бы больше работы. На самом деле, сложные маршруты и сценарии — это то, где Spray действительно сияет.
При разработке вы можете запускать тесты в быстром режиме.
[Внутри ВМ]
|
1
2
|
cd /vagrantsbt ~test-quick |
При изменении исходного кода все затронутые тесты будут автоматически перезапущены. Я имею тенденцию иметь окно терминала с результатами теста, отображаемыми постоянно, и получаю постоянную обратную связь о качестве кода, над которым я работаю.
Тестирование, сборка и развертывание
Как и любое другое приложение, оно должно быть протестировано, построено и развернуто.
Давайте создадим контейнер Docker с сервисом. Определение, необходимое для создания контейнера, можно найти в Dockerfile .
[Внутри ВМ]
|
1
2
3
4
|
cd /vagrantsbt assemblysudo docker build -t vfarcic/books-service .sudo docker push vfarcic/books-service |
Мы собираем JAR (тесты являются частью задачи сборки), собираем Docker-контейнер и помещаем его в Hub. Если вы планируете воспроизвести эти шаги, создайте учетную запись на hub.docker.com и измените vfarcic на свое имя пользователя.
Контейнер, который мы создали, содержит все необходимое для запуска этого сервиса. Он основан на Ubuntu, имеет JDK7, содержит экземпляр MongoDB и имеет JAR, который мы собрали. Отныне этот контейнер можно запускать на любой машине, на которой установлен Docker. Нет необходимости устанавливать JDK, MongoDB или любую другую зависимость на сервер. Контейнер самодостаточен и может работать где угодно.
Давайте развернем (запустим) контейнер, который мы только что создали в другой виртуальной машине. Таким образом, мы смоделируем развертывание в производство.
Чтобы создать производственную виртуальную машину с развернутым сервисом книг, выполните следующее
[из исходного каталога]
|
1
2
|
vagrant halt devvagrant up prod |
Первая команда останавливает разработку ВМ. Каждый требует 2 ГБ. Если у вас достаточно оперативной памяти, вам не нужно останавливать ее, и вы можете пропустить эту команду. Второй выводит производственную виртуальную машину с развернутым сервисом books.
После небольшого ожидания создается новая виртуальная машина, устанавливается Ansible и запускается playbook prod.yml . Он устанавливает Docker и запускает vfarcic / books-service, который был ранее собран и отправлен в Docker Hub. Во время работы у него будет открыт порт 8080, и он будет использовать каталог / data / db с хостом.
Давайте попробуем это. Сначала мы должны отправить запросы PUT, чтобы вставить некоторые тестовые данные.
|
1
2
3
|
curl -H 'Content-Type: application/json' -X PUT -d '{"_id": 1, "title": "My First Book", "author": "John Doe", "description": "Not a very good book"}' http://localhost:8080/api/v1/bookscurl -H 'Content-Type: application/json' -X PUT -d '{"_id": 2, "title": "My Second Book", "author": "John Doe", "description": "Not a bad as the first book"}' http://localhost:8080/api/v1/bookscurl -H 'Content-Type: application/json' -X PUT -d '{"_id": 3, "title": "My Third Book", "author": "John Doe", "description": "Failed writers club"}' http://localhost:8080/api/v1/books |
Давайте проверим, возвращает ли сервис правильные данные.
|
1
|
curl -H 'Content-Type: application/json' http://localhost:8080/api/v1/books |
Мы можем удалить книгу.
|
1
|
curl -H 'Content-Type: application/json' -X DELETE http://localhost:8080/api/v1/books/_id/3 |
Мы можем проверить, что удаленной книги больше нет.
|
1
|
curl -H 'Content-Type: application/json' http://localhost:8080/api/v1/books |
Наконец, мы можем запросить конкретную книгу.
|
1
|
curl -H 'Content-Type: application/json' http://localhost:8080/api/v1/books/_id/1 |
Это был очень быстрый способ разработки, создания и развертывания микросервиса. Одним из преимуществ Docker является то, что он упрощает развертывание, сводя к минимуму необходимые зависимости. Хотя для службы, которую мы создали, требуются JDK и MongoDB, их не нужно устанавливать на конечном сервере. Все является частью контейнера, который будет запускаться как процесс Docker.
Резюме
Микросервисы существуют в течение длительного времени, но до недавнего времени им не уделялось достаточного внимания из-за проблем, возникающих при подготовке сред, способных работать с сотнями, если не тысячами микросервисов. Преимущества, которые были получены с помощью микросервисов (разделение, более быстрая разработка, масштабируемость и т. Д.), Были не такими большими, как проблемы, возникающие при увеличении усилий, которые необходимо было внедрить и внедрить. Инструменты Docker и CM, такие как Ansible, могут уменьшить это усилие, почти ничтожно мало. В связи с тем, что проблемы с развертыванием и предоставлением ресурсов стали чем-то необычным, микросервисы возвращаются в моду благодаря предоставляемым ими преимуществам. Время разработки, сборки и развертывания быстрее по сравнению с монолитными приложениями .
Спрей — очень хороший выбор для микросервисов. Контейнеры Docker светятся, когда они содержат все, что нужно приложению, но не больше. Использование больших веб-серверов, таких как JBoss и WebSphere, было бы излишним для одной (небольшой) службы. Даже веб-серверы меньшего размера, такие как Tomcat, не нужны. Играть в! отлично подходит для создания RESTful API. Тем не менее, он по-прежнему содержит много вещей, которые нам не нужны. Спрей, с другой стороны, делает только одно и делает это хорошо. Он обеспечивает возможности асинхронной маршрутизации для API RESTful.
Мы могли бы продолжить добавление дополнительных функций к этому сервису. Например, мы могли бы добавить модуль регистрации и аутентификации. Однако это приблизило бы нас на один шаг к монолитным приложениям. В мире микросервисов новые сервисы будут новыми приложениями, а в случае новых контейнеров Docker каждый из них будет слушать другой порт и с радостью отвечать на наши HTTP-запросы.
При создании микросервисов старайтесь создавать их так, чтобы они делали одно или очень мало вещей. Сложность решается путем их объединения, а не создания одного большого монолитного приложения.
| Ссылка: | Разработка микросервисов с помощью Scala, Spray, MongoDB, Docker и Ansible от нашего партнера по JCG Виктора Фарчича в блоге по технологиям . |