adding custom plugins using docker-compose

Gravitee.io

Gravitee.io – open source gateway product with API showcase. This article is intended for those who are already familiar with the system. General information about the product is well described in the article: What are API Management systems.

Most of the cases for working with queries can be done using existing plugins, but you can write your own if you wish. If you get carried away in this matter, then you come to the conclusion that it is easier to write your own plugin than to deal with existing ones. Sometimes using built-in plugins is quite a tricky art, but if you stumbled across this article, you already know that.

Also, writing your own plugins adds a lot of possibilities when working with Gravitee

About what?

In this article, we will not consider how to write your own plugins, we will rely on the fact that you are learning how to do it and have achieved some success, and now you want to connect, test and use them. As a bonus, we will learn how to log in Graylog.

Let’s get started

I will perform my manipulations on the latest version of Gravitee at the time of publication (3.20.10), but everything described below is also true for previous versions.

First, let’s take docker-compose.yml from the official source and start making changes.

Install the required version of Gravitee

Let’s forward the folder for custom plugins to the volumes section for management_api:
– ./apim-management-api/plugins:/opt/graviteeio-management-api/plugins-ext
Here we set the path of the folder with plugins within our system, as well as within the container

In the environment section for management_api, add the paths to our plugins:
– gravitee_plugins_path_0=/opt/graviteeio-management-api/plugins
– gravitee_plugins_path_1=/opt/graviteeio-management-api/plugins-ext

Similarly, we will forward the folder for custom plugins to the volumes section for gateway:
– ./apim-gateway/plugins:/opt/graviteeio-gateway/plugins-ext

Similarly, in the environment section for gateway, add the paths to our plugins:
– gravitee_plugins_path_0=/opt/graviteeio-gateway/plugins
– gravitee_plugins_path_1=/opt/graviteeio-gateway/plugins-ext

The finished docker-compose.yml file is shown below:

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

The next step is to bring Gravitee up using docker-compose up -d, or manually create plugin paths in the directory with our docker-compose.yml file.
Path for gateway: /apim-gateway/plugins
Path for management: /apim-management-api/plugins

Adding a Plugin

After creating the necessary paths, we need to build our plugin. Go to the target folder and take the zip archive obtained during the build. The plugin based on which I worked will be available on GitHub

Now we place our plugin in both created directories and start/restart Gravitee. An important clarification: after each update / addition of the plugin, you will need to restart Gravitee or separately its gateway and management-api modules

After launch, we need to create an API, go to the Design tab and find our plugin in the right list. How it looks in my case:

Custom plugin display

Custom plugin display

The plugin category depends on which category you put it in with plugin.properties

Your plugin is now ready to use! You can start configuring. Don’t forget to do “deploy your api” after every plugin configuration change.

Bonus

Logging in Graylog cannot be configured separately for a specific plugin, but it can be configured for a separate component such as management, gateway and others.

Each component has a config directory that contains the logback.xml file, which we will be looking at, as well as gravitee.yml (an important config file) and some other files depending on the module that we will not be looking at.

Initially, the logback.xml file looks like this:

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>

We need to add configurations depending on how we will be logging to Graylog. For example, in the case of UDP, we will add:

    <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>
Added 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>

Now it remains only to supplement docker-compose.yml by dropping the config folder inside the container. For example for management-api it would be like this:

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

This configuration can be applied to all Gravitee modules and everyone will write to Graylog together.

The next step is to “enrich” our Gravitee with the biz.paluch.logging.logstash-gelf library. To do this, we will turn to maven-central, where we will get the jar of the library and place it along the path /opt/название компонента/lib (within the container). To do this, we will again “forward” the folder outside, for a single or all components. For example, for the gateway component:

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

It remains only to configure Graylog, which I will leave to the reader’s conscience

Conclusion

In this article, we looked at how to add custom plugins to Gravitee.io using docker-compose.yml. We also taught our Gravitee how to write in Graylog as a bonus. The plugin source code and docker-compose.yml (pre-bonus version) are available at GitHub

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *