Привет, Хабр! Сегодня мы разберёмся, как добавлять кастомные плагины в Gravitee.io

Gravitee.io - open source продукт-шлюз с витриной API. Эта статья рассчитана на тех, кто уже знаком с системой. Общая информация о продукте хорошо описана в статье: Что такое системы API Management.

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

Также написание собственных плагинов добавляет кучу возможностей при работе с Gravitee

О чём?

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

Приступим

Свои манипуляции я буду производить на последней версии Gravitee на момент публикации (3.20.10), но всё описанное ниже справедливо и для прошлых версий.

Для начала возьмём docker-compose.yml из официального источника и начнём вносить изменения.

Установим необходимую версию Gravitee

В раздел volumes для management_api пробросим папку для кастомных плагинов:
- ./apim-management-api/plugins:/opt/graviteeio-management-api/plugins-ext
Здесь мы задали путь папки с плагинами в рамках нашей системы, а также в рамках контейнера

В раздел environment для management_api добавим пути до наших плагинов:
- gravitee_plugins_path_0=/opt/graviteeio-management-api/plugins
- gravitee_plugins_path_1=/opt/graviteeio-management-api/plugins-ext

Аналогично в раздел volumes для gateway пробросим папку для кастомных плагинов:
- ./apim-gateway/plugins:/opt/graviteeio-gateway/plugins-ext

Аналогично в раздел environment для gateway добавим пути до наших плагинов:
- gravitee_plugins_path_0=/opt/graviteeio-gateway/plugins
- gravitee_plugins_path_1=/opt/graviteeio-gateway/plugins-ext

Готовый файл docker-compose.yml приведён ниже:

docker-compose.yml
version: '3.5'

networks:
  frontend:
    name: frontend
  storage:
    name: storage

volumes:
  data-elasticsearch:
  data-mongo:

services:
  mongodb:
    image: mongo:3.6
    restart: always
    ports:
      - "27017:27017"
    volumes:
      - data-mongo:/data/db
      - ./logs/apim-mongodb:/var/log/mongodb
    networks:
      - storage

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.7.0
    container_name: gio_apim_elasticsearch
    restart: always
    volumes:
      - data-elasticsearch:/usr/share/elasticsearch/data
    environment:
      - http.host=0.0.0.0
      - transport.host=0.0.0.0
      - xpack.security.enabled=false
      - xpack.monitoring.enabled=false
      - cluster.name=elasticsearch
      - bootstrap.memory_lock=true
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile: 65536
    networks:
      - storage

  gateway:
    image: graviteeio/apim-gateway:3.20.10
    container_name: gio_apim_gateway
    restart: always
    ports:
      - "8082:8082"
    depends_on:
      - mongodb
      - elasticsearch
    volumes:
      - ./logs/apim-gateway:/opt/graviteeio-gateway/logs
      - ./apim-gateway/plugins:/opt/graviteeio-gateway/plugins-ext
    environment:
      - gravitee_management_mongodb_uri=mongodb://mongodb:27017/gravitee?serverSelectionTimeoutMS=5000&connectTimeoutMS=5000&socketTimeoutMS=5000
      - gravitee_ratelimit_mongodb_uri=mongodb://mongodb:27017/gravitee?serverSelectionTimeoutMS=5000&connectTimeoutMS=5000&socketTimeoutMS=5000
      - gravitee_reporters_elasticsearch_endpoints_0=http://elasticsearch:9200
      - gravitee_plugins_path_0=/opt/graviteeio-gateway/plugins
      - gravitee_plugins_path_1=/opt/graviteeio-gateway/plugins-ext
    networks:
      - storage
      - frontend

  management_api:
    image: graviteeio/apim-management-api:3.20.10
    container_name: gio_apim_management_api
    restart: always
    ports:
      - "8083:8083"
    links:
      - mongodb
      - elasticsearch
    depends_on:
      - mongodb
      - elasticsearch
    volumes:
      - ./logs/apim-management-api:/opt/graviteeio-management-api/logs
      - ./apim-management-api/plugins:/opt/graviteeio-management-api/plugins-ext
    environment:
      - gravitee_management_mongodb_uri=mongodb://mongodb:27017/gravitee?serverSelectionTimeoutMS=5000&connectTimeoutMS=5000&socketTimeoutMS=5000
      - gravitee_analytics_elasticsearch_endpoints_0=http://elasticsearch:9200
      - gravitee_plugins_path_0=/opt/graviteeio-management-api/plugins
      - gravitee_plugins_path_1=/opt/graviteeio-management-api/plugins-ext
    networks:
      - storage
      - frontend

  management_ui:
    image: graviteeio/apim-management-ui:3.20.10
    container_name: gio_apim_management_ui
    restart: always
    ports:
      - "8084:8080"
    depends_on:
      - management_api
    environment:
      - MGMT_API_URL=http://localhost:8083/management/organizations/DEFAULT/environments/DEFAULT/
    volumes:
      - ./logs/apim-management-ui:/var/log/nginx
    networks:
      - frontend

  portal_ui:
    image: graviteeio/apim-portal-ui:3.20.10
    container_name: gio_apim_portal_ui
    restart: always
    ports:
      - "8085:8080"
    depends_on:
      - management_api
    environment:
      - PORTAL_API_URL=http://localhost:8083/portal/environments/DEFAULT
    volumes:
      - ./logs/apim-portal-ui:/var/log/nginx
    networks:
      - frontend

Следующим шагом мы поднимаем Gravitee, используя docker-compose up -d, либо в ручную создаём пути для плагинов в каталоге с нашим docker-compose.yml файлом.
Путь для gateway: /apim-gateway/plugins
Путь для managment: /apim-management-api/plugins

Добавление плагина

После создания необходимых путей нам необходимо собрать наш плагин. Перейти в папку target и взять zip архив, полученный при сборке. Плагин на основе которого я работал будет доступен на GitHub

Теперь мы помещаем наш плагин в оба созданных каталога и запускаем/перезапускаем Gravitee. Важное уточнение: после каждого обновления\добавления плагина необходимо будет перезагрузить Gravitee либо отдельно его модули gateway и managment-api

После запуска нам необходимо создать API, перейти на вкладку Design и в правом списке найти наш плагин. Как это выглядит в моём случае:

Отображение Custom plugin
Отображение Custom plugin

Категория плагина зависит от того, в какую категорию вы его поместите с помощью plugin.properties

Теперь ваш плагин готов к использованию! Можно приступить к конфигурированию. Не забывайте делать "deploy your api" после каждого изменения конфигурации плагина.

Бонус

Логирование в Graylog нельзя настроить отдельно для какого-то определённого плагина, но можно для отдельного компонента например managment, gateway и другие.

В каждом компоненте присутствует директория для конфигураций, в которой находятся файл logback.xml, который мы и будем рассматривать, а также gravitee.yml (важный конфигурационный файл) и некоторые другие файлы в зависимости от модуля, который мы не будем рассматривать.

Изначально файл logback.xml выглядит вот так:

logback.xml
<!--
  ~ Copyright (c) 2015-2016, The Gravitee team (http://www.gravitee.io)
  ~
  ~  Licensed under the Apache License, Version 2.0 (the "License");
  ~  you may not use this file except in compliance with the License.
  ~  You may obtain a copy of the License at
  ~
  ~  http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~  Unless required by applicable law or agreed to in writing, software
  ~  distributed under the License is distributed on an "AS IS" BASIS,
  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~  See the License for the specific language governing permissions and
  ~  limitations under the License.
  -->

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] [%X{api}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${gravitee.home}/logs/gravitee.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${gravitee.home}/logs/gravitee_%d{yyyy-MM-dd}.log</fileNamePattern>

            <!-- keep 30 days' worth of history -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>

        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] [%X{api}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="async-file" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE" />
    </appender>

    <appender name="async-console" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="STDOUT" />
    </appender>

    <logger name="io.gravitee" level="INFO" />
    <logger name="com.graviteesource" level="INFO" />
    <logger name="org.reflections" level="WARN" />
    <logger name="org.springframework" level="WARN" />
    <logger name="org.eclipse.jetty" level="WARN" />

    <!-- Strictly speaking, the level attribute is not necessary since -->
    <!-- the level of the root level is set to DEBUG by default.       -->
    <root level="WARN">
        <appender-ref ref="async-console" />
        <appender-ref ref="async-file" />
    </root>

</configuration>

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

    <appender name="GELF" class="biz.paluch.logging.gelf.logback.GelfLogbackAppender">
        <host>udp:host</host>
        <port>port</port>
        <facility>НАЗВАНИЕ КОМПОНЕНТА</facility>
        <extractStackTrace>true</extractStackTrace>
        <filterStackTrace>true</filterStackTrace>
        <includeFullMdc>true</includeFullMdc>
    </appender>


    <logger name="io.gravitee.rest.api.service.impl.upgrade" level="INFO">
        <appender-ref ref="FILE-UPGRADERS"/>
    </logger>
    <logger name="io.gravitee" level="INFO"/>
    <logger name="org.eclipse.jetty" level="INFO"/>
    <logger name="org.reflections" level="WARN" />
    <logger name="org.springframework" level="WARN" />
    <logger name="org.eclipse.jetty" level="WARN" />

    <!-- Strictly speaking, the level attribute is not necessary since -->
    <!-- the level of the root level is set to DEBUG by default.       -->
    <root level="WARN">
        <appender-ref ref="GELF"/>
        <appender-ref ref="async-console" />
        <appender-ref ref="async-file" />
    </root>
Дополненный logback.xml
<!--
  ~ Copyright (c) 2015-2016, The Gravitee team (http://www.gravitee.io)
  ~
  ~  Licensed under the Apache License, Version 2.0 (the "License");
  ~  you may not use this file except in compliance with the License.
  ~  You may obtain a copy of the License at
  ~
  ~  http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~  Unless required by applicable law or agreed to in writing, software
  ~  distributed under the License is distributed on an "AS IS" BASIS,
  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~  See the License for the specific language governing permissions and
  ~  limitations under the License.
  -->

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] [%X{api}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${gravitee.home}/logs/gravitee.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- daily rollover -->
            <fileNamePattern>${gravitee.home}/logs/gravitee_%d{yyyy-MM-dd}.log</fileNamePattern>

            <!-- keep 30 days' worth of history -->
            <maxHistory>30</maxHistory>
        </rollingPolicy>

        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] [%X{api}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="async-file" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="FILE" />
    </appender>

    <appender name="async-console" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="STDOUT" />
    </appender>

    <appender name="GELF" class="biz.paluch.logging.gelf.logback.GelfLogbackAppender">
        <host>udp:host</host>
        <port>port</port>
        <facility>НАЗВАНИЕ КОМПОНЕНТА</facility>
        <extractStackTrace>true</extractStackTrace>
        <filterStackTrace>true</filterStackTrace>
        <includeFullMdc>true</includeFullMdc>
    </appender>


    <logger name="io.gravitee.rest.api.service.impl.upgrade" level="INFO">
        <appender-ref ref="FILE-UPGRADERS"/>
    </logger>
    <logger name="io.gravitee" level="INFO"/>
    <logger name="org.eclipse.jetty" level="INFO"/>
    <logger name="org.reflections" level="WARN" />
    <logger name="org.springframework" level="WARN" />
    <logger name="org.eclipse.jetty" level="WARN" />

    <!-- Strictly speaking, the level attribute is not necessary since -->
    <!-- the level of the root level is set to DEBUG by default.       -->
    <root level="WARN">
        <appender-ref ref="GELF"/>
        <appender-ref ref="async-console" />
        <appender-ref ref="async-file" />
    </root>

</configuration>

Теперь осталось лишь дополнить docker-compose.yml , пробросив папку config внутрь контейнера. Например для managment-api это будет так:

- .\apim-management-api\config:/opt/apim-management-api/config

Данную конфигурацию можно применить для всех модулей Gravitee и все дружно будут писать в Graylog.

Следующим шагом будет "обогащение" нашего Gravitee библиотекой biz.paluch.logging.logstash-gelf. Для этого мы обратимся на maven-central, где получим jar библиотеки и поместим по пути /opt/название компонента/lib (в рамках контейнера). Для этого мы вновь "пробросим" папку наружу, для отдельного или всех компонентов. Например для компонента gateway:

- .\apim-gateway\lib:/opt/graviteeio-gateway/lib

Осталось лишь сконфигурировать Graylog, что я оставлю на совести читателя

Заключение

В статье мы рассмотрели способ добавления кастомных плагинов в Gravitee.io, используя docker-compose.yml. Также бонусом научили наш Gravitee писать в Graylog. Исходный код плагина и docker-compose.yml (версия до бонуса) доступен на GitHub

Комментарии (0)