Создание API для бедняков с использованием PostgREST

Создание API для бедняков с использованием PostgREST

24 ноября 2022 г.

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

Решение

Основным требованием решения является использование базы данных PostgreSQL. Это хорошо зарекомендовавшая себя база данных SQL с открытым исходным кодом.

Вместо написания нашего REST API мы используем компонент PostgREST:

<цитата>

PostgREST – это автономный веб-сервер, который превращает вашу базу данных PostgreSQL непосредственно в RESTful API. Структурные ограничения и разрешения в базе данных определяют конечные точки и операции API.

-- PostgREST

Давайте применим его к простому варианту использования. Вот таблица product, которую я хочу предоставить через CRUD API:

Обратите внимание, что вы можете найти весь исходный код на GitHub.

Руководство по началу работы от PostgREST довольно полное и готово к работе. Однако я не нашел готового образа Docker, поэтому создал свой:

FROM debian:bookworm-slim                                                   #1

ARG POSTGREST_VERSION=v10.1.1                                               #2
ARG POSTGREST_FILE=postgrest-$POSTGREST_VERSION-linux-static-x64.tar.xz     #2

RUN mkdir postgrest

WORKDIR postgrest

ADD https://github.com/PostgREST/postgrest/releases/download/$POSTGREST_VERSION/$POSTGREST_FILE 
    .                                                                       #3

RUN apt-get update && 
    apt-get install -y libpq-dev xz-utils && 
    tar xvf $POSTGREST_FILE && 
    rm $POSTGREST_FILE                                                      #4
  1. Начните с последней версии Debian
  2. Параметры сборки
  3. Получить архив
  4. Установить зависимости и разархивировать

Образ Docker содержит исполняемый файл postgrest в папке /postgrest. Мы можем «развернуть» архитектуру через Docker Compose:

version: "3"
services:
  postgrest:
    build: ./postgrest                                   #1
    volumes:
      - ./postgrest/product.conf:/etc/product.conf:ro    #2
    ports:
      - "3000:3000"
    entrypoint: ["/postgrest/postgrest"]                 #3
    command: ["/etc/product.conf"]                       #4
    depends_on:
      - postgres
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: "root"
    volumes:
      - ./postgres:/docker-entrypoint-initdb.d:ro       #5
  1. Создайте указанный выше Dockerfile
  2. .
  3. Поделитесь файлом конфигурации
  4. Запустите исполняемый файл postgrest
  5. С файлом конфигурации
  6. Инициализировать схему, разрешения и данные

Теперь мы можем запросить таблицу product:

curl localhost:3000/product

Сразу получаем результаты:

[{"id":1,"name":"Stickers pack","description":"A pack of rad stickers to display on your laptop or wherever you feel like. Show your love for Apache APISIX","price":0.49,"hero":false}, 
 {"id":2,"name":"Lapel pin","description":"With this "Powered by Apache APISIX" lapel pin, support your favorite API Gateway and let everybody know about it.","price":1.49,"hero":false}, 
 {"id":3,"name":"Tee-Shirt","description":"The classic geek product! At a conference, at home, at work, this tee-shirt will be your best friend.","price":9.99,"hero":true}]

Это была быстрая победа!

Улучшение решения

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

Документация PostgREST знает об этом и явно рекомендует использовать обратный прокси-сервер:

<цитата>

PostgREST — это быстрый способ создания RESTful API. Его поведение по умолчанию отлично подходит для формирования шаблонов в процессе разработки. Когда пришло время перейти к производству, это тоже отлично работает, если вы принимаете меры предосторожности. PostgREST — это небольшой инструмент, предназначенный для сопоставления API с базой данных. Мы полагаемся на обратный прокси-сервер, такой как Nginx, для дополнительной защиты.

-- Усиление безопасности PostgREST

Вместо nginx нам бы пригодился полноценный шлюз API: входит Apache APISIX. Мы добавим его в наш Docker Compose:

version: "3"
services:
  apisix:
    image: apache/apisix:2.15.0-alpine                              #1
    volumes:
      - ./apisix/config.yml:/usr/local/apisix/conf/config.yaml:ro
    ports:
      - "9080:9080"
    restart: always
    depends_on:
      - etcd
      - postgrest
  etcd:
    image: bitnami/etcd:3.5.2                                       #2
    environment:
      ETCD_ENABLE_V2: "true"
      ALLOW_NONE_AUTHENTICATION: "yes"
      ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2397"
      ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2397"
  1. Использовать Apache APISIX
  2. APISIX сохраняет свою конфигурацию в etcd

Сначала мы настроим Apache APISIX для прокси-вызовов postgrest:

curl http://apisix:9080/apisix/admin/upstreams/1 -H 'X-API-KEY: 123xyz' -X PUT -d ' #1-2
{
  "type": "roundrobin",
  "nodes": {
    "postgrest:3000": 1                                                             #1-3
  }
}'

curl http://apisix:9080/apisix/admin/routes/1 -H 'X-API-KEY: 123xyz' -X PUT -d '    #4
{
  "uri": "/*",
  "upstream_id": 1
}'
  1. Должен запускаться на одном из узлов Docker, поэтому используйте имя образа Docker. В качестве альтернативы используйте localhost, но убедитесь, что открыты порты
  2. Создайте повторно используемый upstream
  3. Укажите на узел PostgREST
  4. Создайте маршрут к созданному вверх по течению

Теперь мы можем запросить конечную точку через APISIX:

curl localhost:9080/product

Он возвращает тот же результат, что и выше.

Защита от DDoS-атак

Мы ничего не добавляли, но готовы начать работу. Давайте сначала защитим наш API от распределенных атак типа «отказ в обслуживании». Apache APISIX разработан на основе архитектуры плагинов. Для защиты от DDoS воспользуемся плагином. Мы можем установить плагины для определенного маршрута при его создании или для каждого маршрута; в последнем случае это глобальное правило. Мы хотим защитить каждый маршрут по умолчанию, поэтому будем использовать один.

curl http://apisix:9080/apisix/admin/global_rules/1 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "plugins": {
    "limit-count": {                 #1
      "count": 1,                    #2
      "time_window": 5,              #2
      "rejected_code": 429           #3
    }
  }
}'
  1. limit-count ограничивает количество вызовов во временном окне
  2. Ограничение до 1 звонка в 5 секунд; это для демонстрационных целей
  3. Возврат 429 Слишком много запросов; по умолчанию 503

Теперь, если мы выполняем слишком много запросов, Apache APISIX защищает восходящий поток:

curl localhost:9080/product
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>openresty</center>
</body>
</html>

Авторизация для каждого маршрута

PostgREST также предлагает конечную точку Open API в корневом каталоге. Таким образом, у нас есть два маршрута: / для спецификации Open API и /product для продуктов. Предположим, мы хотим запретить неавторизованным пользователям доступ к нашим данным: обычные пользователи могут получить доступ к продуктам, а пользователи с правами администратора могут получить доступ к спецификациям Open API и продуктам.

APISIX предлагает несколько методов аутентификации. Мы будем использовать самый простой из возможных, key-auth. Он основан на абстракции Consumer. Для key-auth требуется определенный заголовок: подключаемый модуль выполняет обратный поиск значения и находит потребителя, чей ключ соответствует.

Вот как создать потребителя:

curl http://apisix:9080/apisix/admin/consumers -H 'X-API-KEY: 123xyz' -X PUT -d '    #1
{
  "username": "admin",                                                               #2
  "plugins": {
    "key-auth": {
      "key": "admin"                                                                 #3
    }
  }
}'
  1. Создать нового потребителя
  2. Имя потребителя
  3. Ключевое значение потребителя

Мы делаем то же самое с потребителем user и ключом user. Теперь мы можем создать выделенный маршрут и настроить его так, чтобы через него проходили только запросы от admin:

curl http://apisix:9080/apisix/admin/routes -H 'X-API-KEY: 123xyz' -X POST -d ' #1
{
  "uri": "/",
  "upstream_id": 1,
  "plugins": {
    "key-auth": {},                                                             #2
    "consumer-restriction": {                                                   #2
      "whitelist": [ "admin" ]                                                  #3
    }
  }
}'
  1. Создать новый маршрут
  2. Используйте плагины key-auth и consumer-restriction
  3. .
  4. Только запросы, прошедшие проверку подлинности admin, могут вызывать маршрут

Давайте попробуем следующее:

curl localhost:9080

Это не работает, так как мы не аутентифицируемся через заголовок ключа API.

{"message":"Missing API key found in request"}
curl -H "apikey: user" localhost:9080

Это не работает, так как мы аутентифицированы как user, но маршрут авторизован не для user, а для admin.

{"message":"The consumer_name is forbidden."}
curl -H "apikey: admin" localhost:9080

На этот раз он возвращает спецификацию Open API, как и ожидалось.

Мониторинг

Очень недооцененной функцией любой программной системы является мониторинг. Как только вы развертываете какой-либо компонент в рабочей среде, вы должны следить за его работоспособностью. В настоящее время многие сервисы доступны для мониторинга. Мы будем использовать Prometheus, так как он имеет открытый исходный код, проверен в боях и широко распространен. Для отображения данных мы будем полагаться на Grafana по тем же причинам. Давайте добавим компоненты в файл Docker Compose:

version: "3"
services:
  prometheus:
    image: prom/prometheus:v2.40.1                                    #1
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml    #2
    depends_on:
      - apisix
  grafana:
    image: grafana/grafana:8.5.15                                     #3
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning              #4
      - ./grafana/dashboards:/var/lib/grafana/dashboards              #4
      - ./grafana/config/grafana.ini:/etc/grafana/grafana.ini         #4-5
    ports:
      - "3001:3001"
    depends_on:
      - prometheus
  1. Изображение Прометея
  2. Конфигурация Prometheus для очистки Apache APISIX. Полный файл смотрите здесь
  3. Изображение Grafana
  4. Конфигурация Grafana. Большинство из них исходит из конфигурации. APISIX.
  5. Измените порт по умолчанию с 3000 на 3001, чтобы избежать конфликта со службой PostgREST

Как только инфраструктура мониторинга будет создана, нам нужно только указать APISIX предоставлять данные в формате, который ожидает Prometheus. Мы можем добиться этого с помощью конфигурации и нового глобального правила:

plugin_attr:
  prometheus:
    export_addr:
      ip: "0.0.0.0"             #1
      port: 9091                #2
  1. Привязать к любому адресу
  2. Привязать к порту 9091. Метрики Prometheus доступны на странице http://apisix:9091/apisix/prometheus/metrics в сети Docker
  3. .

Мы можем создать глобальное правило:

curl http://apisix:9080/apisix/admin/global_rules/2 -H 'X-API-KEY: 123xyz' -X PUT -d '
{
  "plugins": {
    "prometheus": {}
  }
}'

Отправьте пару запросов и откройте панель управления Grafana. Это должно выглядеть примерно так:

Заключение

Создание полноценного REST(ful) API требует огромных инвестиций. Можно быстро протестировать простой API, предоставив свою базу данных в CRUD API через PostgREST. Однако такая архитектура не подходит для промышленного использования.

Чтобы исправить это, вам нужно установить фасад перед PostgREST, обратный прокси или, что еще лучше, шлюз API. Apache APISIX предлагает широкий спектр функций, от авторизации до мониторинга. С его помощью вы можете быстро и недорого проверить требования к API.

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

Исходный код доступен на GitHub.

Дальше:

Первоначально опубликовано на странице A Java Geek 20 ноября 2022 г.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE