Привет, Хабр! Сегодня мы разберёмся, как добавлять кастомные плагины в 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 и в правом списке найти наш плагин. Как это выглядит в моём случае:
Категория плагина зависит от того, в какую категорию вы его поместите с помощью 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