Статьи

Использование Docker в разработке API-шлюзов и микросервисов

По мере того, как вы будете совершенствоваться в своем образовании в качестве разработчика, вы рано или поздно поймете преимущества, которые может предложить контейнерная система, такая как Docker: вы можете указать свою среду в коде, без всяких сообщений Slack для системных инженеров или головной боли, которая связана с стоящие последовательно настроенные серверы. Точно так же вы, вероятно, поняли привлекательность микросервисов как решение проблем монолитного приложения, погрязшего в своем долгом запахе.

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

Докер для поддержки изолированного микросервиса

Разработка самого микросервиса должна быть довольно простой; С точки зрения окружающей среды, разработка не должна сильно отличаться от разработки более традиционного приложения. Возможно, ваш микросервис должен поддерживать одну или две конечные точки API — он нужен вам для подключения к паре моделей данных, таких как MySQL или Redis, и вы можете довольно быстро приступить к гонкам. Это Docker 101. Вы можете воспользоваться хорошо поддерживаемыми существующими проектами Docker, такими как Laradock или NoDock (для PHP и Node.js соответственно), которые предлагают разработчикам интегрированную среду Docker, которая поддерживает множество общих технологий, объединенных в сеть посредством docker-compose .

Быстрый хедз-ап

Первое, что я хотел бы сказать любому, кто работает с Docker, — это то, что темпы его разработки были довольно быстрыми: даже довольно недавние курсы могут ссылаться на команды или утилиты, которые уже устарели ( например , docker-machine ). Будьте готовы немного стиснуть зубы, почесать голову и ориентироваться в незнакомых сообщениях об ошибках. Однако, как только вы преодолеете вышибалы, вступление в клуб Docker того стоит.

Докер автономный

Конечно, прежде чем мы начнем, убедитесь, что на вашем компьютере есть панель инструментов Docker. Посетите Docker.com, чтобы загрузить клиент для вашей операционной системы (версия CE Community Edition подходит для наших целей).

Если вам нужно запустить определенную технологию, такую ​​как язык сценариев или операционную систему, велика вероятность, что кто-то уже создал для нее образ Docker. DockerHub — ваш друг, когда дело доходит до повторного использования кода, которым так щедро делятся другие. Помните: не изобретайте колеса! Обратите внимание, что по какой-то причине сайт помечен для поиска контейнеров , когда вы на самом деле ищете изображения . Помните: контейнеры — это шаблоны — из одного шаблона изображения можно создать несколько экземпляров контейнера.

Короче говоря, ваши взаимодействия должны вращаться вокруг клонирования контейнера (с помощью команды clone ) и последующего запуска его экземпляра (с помощью команды run ). Например, это все, что вам нужно, чтобы получить рабочую копию PostGres:

1
2
docker pull postgres
docker run --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres

Если вы посмотрите на соответствующий репозиторий Git для любого контейнера, его Dockerfile содержит шаги, необходимые для создания экземпляра контейнера из изображения, например , для контейнера PostGres .

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

Docker Compose

Во многих случаях вам будет полезно соединить отдельные контейнеры Docker вместе. Если вашему приложению требуется определенная версия PHP и конкретная версия PostGres, нет проблем: найдите для каждого изображения изображения Docker и docker-compose.yml них в файле docker-compose.yml .

Для многих вариантов использования docker-compose будет самым важным инструментом для объединения ваших контейнеров. Для каждого микросервиса вы сможете ссылаться на новые и существующие образы Docker и определять их отношения через файл docker-compose.yml .

Например, вот как мы можем определить среду для поддержки PHP 7 и PostGres на веб-сервере NGINX. Давайте предположим, что наш корень репозитория имеет здесь composer.json и папку для публичных веб-файлов с именем public/ .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# PHP + PostGres microservice docker-compose.yml
version: '2'
services:
  nginx:
    image: nginx:1.13-alpine
    ports:
      - 3000:80
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
  web:
    # This build command is what
    # builds the neighboring Dockerfile
    build: .
    ports:
      - 9000:9000
    volumes:
      - .:/var/www
      - /var/www/vendor
    depends_on:
      - postgres
    environment:
      DATABASE_URL: postgres://todoapp@postgres/todos
  postgres:
    image: postgres:9.6.2-alpine
    environment:
      POSTGRES_USER: todoapp
      POSTGRES_DB: todos

Большая часть этого — отображение портов и объемов. Где PHP? Это несколько затенено build: . команда. Говоря более подробно, эта команда запускает docker build . поэтому он ожидает, что рядом с этим файлом docker-compose.yml . PHP Dockerfile может выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM php:7.1-fpm-alpine
 
RUN apk update && apk add build-base
 
RUN apk add postgresql postgresql-dev \
  && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
  && docker-php-ext-install pdo pdo_pgsql pgsql
 
RUN apk add zlib-dev git zip \
  && docker-php-ext-install zip
 
RUN curl -sS https://getcomposer.org/installer | php \
        && mv composer.phar /usr/local/bin/ \
        && ln -s /usr/local/bin/composer.phar /usr/local/bin/composer
 
# References the virtual path
COPY . /var/www
WORKDIR /var/www
 
RUN composer install --prefer-source --no-interaction
 
ENV PATH="~/.composer/vendor/bin:./vendor/bin:${PATH}"

В зависимости от ваших потребностей, вы можете обойтись без Dockerfile вообще. Вместо команды build ваш docker-compose.yml может вместо этого ссылаться на image , но, поскольку PHP используется на стороне сервера, весьма вероятно, что он потребует некоторой настройки. Это проще всего сделать внутри Dockerfile , поэтому, вероятно, лучше всего ссылаться на него через свойство build .

Последнее, что вам нужно, это файл конфигурации NGINX. Что-то вроде этого должно быть достаточно:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
server {
  listen  80;
  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  root /var/www/public/;
 
  location / {
      try_files $uri /index.php$is_args$args;
  }
 
  location ~ ^/.+\.php(/|$) {
      fastcgi_pass web:9000;
      include fastcgi_params;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}

Если вы обращаете внимание на сопоставление, выполняемое в docker-compose.yml где корень вашего хранилища соответствует /var/www/ внутри контейнера, вы можете видеть, что, как написано, он ожидает, что файл nginx.conf будет в корень хранилища.

Может быть легко запутаться в виртуальных путях в вашем файле nginx.conf , поэтому вы должны сравнить его с вашим docker-compose.yml . В частности, это сопоставлено . (корень хранилища) в /var/www на виртуальной машине. Таким образом, NGINX начинает с этого момента и определяет свой веб-корень как /var/www/public/ — который является public/ папкой из вашего репозитория.

Чтобы загрузить эти изображения и встроить их в контейнеры, вы должны запустить docker-compose up . Загрузка и сборка изображений может занять некоторое время, но если все пойдет хорошо, вы сможете установить новое приложение PHP на http://localhost:3000

Данные семян

По мере развития вашего микросервиса, вы захотите писать тесты. При использовании такой технологии, как Docker, которая может так легко и последовательно вызывать связанные сервисы, вы должны осознать хорошую возможность для создания тщательной интеграции и функциональных тестов, основанных на куративной коллекции исходных данных.

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

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

Один из самых простых способов выполнить начальное действие — использовать функцию exec docker-compose , которая выполняет команду в именованном контейнере. Например, если наше приложение PHP является приложением Laravel, мы можем использовать его инструмент командной строки для миграции и заполнения нашей базы данных.

1
docker-compose exec web php artisan migrate

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

Зарегистрируй бесплатный аккаунт Codeship

Докер для шлюза API

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

Образ Docker для вашего приложения API Gateway может не сильно отличаться от того, что мы обсуждали для отдельного микросервиса. Ему потребуется некоторая среда для обработки запросов и ответов через код на стороне сервера (возможно, Go или Elixir), и он часто будет подключен к сервису аутентификации / авторизации, чтобы он мог проверять запросы перед их передачей в микросервис.

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

Однако это не означает сквозного тестирования. Нам нужно сделать немного лучше, если мы хотим быть уверены, что конкретные запросы дают конкретные ответы. И вот тут мы возвращаемся к Docker и Docker Compose.

Если бы мы использовали docker-compose.yml качестве нашего «документа записи», касающегося наших микросервисов, вы можете легко представить, что он потенциально может docker-compose.yml большое количество сервисов ( например , по одному для каждого микросервиса). Если каждый сервис построен как образ Docker, вы можете опубликовать эти образы в Docker Hub как общедоступные (или частные) репозитории, чтобы другие разработчики могли легко клонировать и создавать контейнеры, необходимые для вашего приложения.

Выделенное изображение для тестирования

Одним из решений для решения проблемы заполнения данных и запуска интеграционных тестов является создание выделенного образа Docker для этой задачи. Это изображение Docker, вероятно, будет эффективно использовать depends_on слово depends_on в вашем файле docker-compose.yml . Язык, который вы используете для написания своих тестов, может быть наиболее подходящим для решения поставленной задачи: тестирование. Пока вы можете легко заполнять свои модели данных начальными данными и писать тесты, которые попадают в шлюз API с HTTP-запросами, это будет работать.

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

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

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

Вывод

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

Опубликовано на Java Code Geeks с разрешения Эверетта Гриффитса, партнера нашей программы JCG . См. Оригинальную статью здесь: Использование Docker в разработке API-шлюзов и микросервисов.

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