No matter what architecture you’re planning to build: an API Gateway is mostly part of a modern setup. So why not connect your Spring Boot apps with Kong API Gateway using a standard like OpenAPI and configuration-as-code?!
Finding a good way to integrate
I have to admit that I’am a little late to the party. Many of my colleagues have already written posts on Kong API Gateway on our codecentric blog . If you want to read more about Kong as an API Gateway , I can recommend having a look. Getting involved has been on my list for a long time. So here we go. 🙂
Logo sources: Kong logo , Spring Boot logo
I always want to know how I can integrate new technologies into my tool belt. So why not try to integrate Kong as an API Gateway for Spring Boot Apps? Maybe that’s also a problem you have, since you landed here on this article. And it shouldn’t be a big problem, right? Hmm, googling around, I wasn’t convinced about the solutions I found.
Many of the implementations needed code changes or the usage of custom annotations on the Spring side in order to do the integration. A lot of them relying on libraries with a suspiciously small user base. Or I found many articles describing manual steps, where I would need to click on this and that in order to connect my app with Kong. None of these approaches really seem to work out in the long run. So I began to dive a bit deeper into the topic. And I developed some requirements for the solution I was trying to find:
1. There shouldn’t be any need to change the code of my Spring app! Since there’s no silver bullet, I always want to be able to use other tools with minimal effort if my requirements change. And I also want to integrate my existing Spring Boot apps into the API Gateway seamlessly.
2. I had no clue about Kong, but I wanted to use it really as any other tool that is able to remain in my toolbelt: It should be configurable 100% by code – so that no clicks will be needed at all.
3. Many posts about Kong involve a huge Docker Compose file with 3 to 4 services solely needed to fire up a API Gateway instance. I like full-blown setups for production use, but I wanted to have a setup where Kong only needs one service – like other solutions I saw with for example Spring Cloud Gateway or Traefik .
4. The integration of Spring Boot and Kong should be fully automated. Since code is here to change, every change affecting my app’s REST interface should be reflected automatically in the API Gateway. And I want to be able to automatically verify that in my CI/CD pipeline.
The setup
Finally I found a setup that should work for many use cases and would also meet my requirements. Let’s take a look onto the following diagram:
Logo sources: OpenAPI logo , Kong logo , Insomnia logo , Inso logo
So the idea is to use OpenAPI as the integration glue here. I think that’s a great specification – even without using an API Gateway. Maybe it’s even already there in your project?! In order to simplify the usage of OpenAPI with Spring, there’s the great springdoc-openapi project which we’ll use in our setup.
In addition, we’ll also focus on Kong’s Declarative Configuration option . This will have a bunch of benefits. First, this approach enables us to use Kong as fully configurable by code. It also enables a really elegant setup, where we only need one single Kong service – since there’s also a DB-less mode, where Kong doesn’t need any database. And finally using the openapi-2-kong functionality of the Insomnia CLI (“Inso”) we can directly generate the Kong Declarative Configuration file from our OpenAPI specification that is derived from our Spring Boot REST API. As we don’t want to get a headache by using too many buzzwords, we should get our hands dirty and simply build this setup from the ground up! To really be able to comprehend every single step, you may also want to look into the example project on GitHub .
Creating a Spring Boot app or choose an existing one
This is the easy part. We all know where to start if we want to kick off a new Spring Boot project. Go to start.spring.io and generate a Spring REST app skeleton matching your needs. You may also choose one of your existing apps to start with. I simply took the weatherbackend
app from this Spring Cloud-based project , which was part of a blog post I wrote in 2017 about a Spring Cloud Netflix-stack-based setup . Additionally I also wanted to get a feeling for the differences between the setup back then compared to the usage of Kong API Gateway today.
The weatherbackend
Spring Boot app is using standard dependencies like spring-boot-starter-web to implement some Spring MVC based REST endpoints. The class WeatherBackendAPI.java looks like something you would expect:
1package io.jonashackt.weatherbackend.api;
2
3import io.jonashackt.weatherbackend.model.*;
4import org.slf4j.Logger;
5import org.slf4j.LoggerFactory;
6import org.springframework.http.HttpStatus;
7import org.springframework.web.bind.annotation.*;
8
9@RestController
10@RequestMapping("/weather")
11public class WeatherBackendAPI {
12
13 @PostMapping(value = "/general/outlook", produces = "application/json")
14 public @ResponseBody GeneralOutlook generateGeneralOutlook(@RequestBody Weather weather) throws JsonProcessingException {
15 ...
16 return outlook;
17 }
18
19 @GetMapping(value = "/general/outlook", produces = "application/json")
20 public @ResponseBody String infoAboutGeneralOutlook() throws JsonProcessingException {
21 ...
22 return "Try a POST also against this URL! Just send some body with it like: '" + weatherJson + "'";
23 }
24
25 @GetMapping(value = "/{name}", produces = "text/plain")
26 public String whatsTheSenseInThat(@PathVariable("name") String name) {
27 return "Hello " + name + "! This is a RESTful HttpService written in Spring. :)";
28 }
29}
Generating the OpenAPI spec with the springdoc-openapi-maven-plugin
Now that we have a running Spring Boot app in place, we need to take a look at the OpenAPI spec generation. As already stated, there is the springdoc-openapi-maven-plugin waiting to help us:
The aim of springdoc-openapi-maven-plugin is to generate JSON and yaml OpenAPI description during build time. The plugin works during integration-tests phase and generates the OpenAPI description. The plugin works in conjunction with spring-boot-maven plugin.
In order to successfully use the springdoc-openapi-maven-plugin, we also need to add the springdoc-openapi-ui plugin (for Tomcat / Spring MVC based apps) or the springdoc-openapi-webflux-ui plugin (for Reactive WebFlux / Netty based apps) as a dependency in our pom.xml :
1<dependency> 2 <groupId>org.springdoc</groupId> 3 <artifactId>springdoc-openapi-ui</artifactId> 4 <version>1.4.8</version> 5</dependency>
Without using this additional dependency, the springdoc-openapi-maven-plugin
will run into errors like [ERROR] An error has occured: Response code 404
. As described in this stackoverflow answer , we need to use both plugins to successfully generate our OpenAPI spec file. And because we added the springdoc-openapi-ui
dependency, we are also able to access the live API documentation already at localhost:8080/swagger-ui.html .
Now we can add the springdoc-openapi-maven-plugin
to our pom.xml :
1<plugin> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-maven-plugin</artifactId> 4 <executions> 5 <execution> 6 <id>pre-integration-test</id> 7 <goals> 8 <goal>start</goal> 9 </goals> 10 </execution> 11 <execution> 12 <id>post-integration-test</id> 13 <goals> 14 <goal>stop</goal> 15 </goals> 16 </execution> 17 </executions> 18</plugin> 19<plugin> 20 <groupId>org.springdoc</groupId> 21 <artifactId>springdoc-openapi-maven-plugin</artifactId> 22 <version>1.1</version> 23 <executions> 24 <execution> 25 <phase>integration-test</phase> 26 <goals> 27 <goal>generate</goal> 28 </goals> 29 </execution> 30 </executions> 31</plugin>
As you see, we also need to tell the spring-boot-maven-plugin
to start and stop the integration test phases, since the springdoc-openapi-maven-plugin
will use the live documentation of a running Spring Boot app to generate the OpenAPI spec file. In order to finally generate the file, simply execute Maven with: mvn verify
. The output should look like this:
1... 2[INFO] --- springdoc-openapi-maven-plugin:1.1:generate (default) @ hellobackend --- 32020-11-04 10:26:09.579 INFO 42143 --- [ctor-http-nio-2] o.springdoc.api.AbstractOpenApiResource : Init duration for springdoc-openapi is: 29 ms 4...
This indicates that the OpenAPI spec generation was successful. Therefore, we need to take a look into the weatherbackend/target
directory, where a file called openapi.json
should be present now. And this is great news! Without changing any code, we generated our OpenAPI spec file. 🙂
Tweak the API information in the generated OpenAPI spec
We could just go on with that openapi.json
file, but we may want to tweak it slightly. Since if we move forward, we will notice that some information should be changed to achieve a better integration into Kong. Especially the element "title": "OpenAPI definition"
is later used as the Kong service name. Also the element "url": "http://localhost:8080"
is used to configure the upstream service endpoint protocol
, host
and port
inside the Kong service definition.
I know that “this is code”. But again we don’t need to change any existing code and we don’t need to introduce new classes/annotations into our normal Spring code. We simply create a new separate class that uses the @OpenAPIDefinition annotation to configure the needed service info. So let’s create a class like OpenAPIConfig.java that looks as follows:
1package io.jonashackt.weatherbackend.api;
2
3import io.swagger.v3.oas.annotations.OpenAPIDefinition;
4import io.swagger.v3.oas.annotations.info.Info;
5import io.swagger.v3.oas.annotations.servers.Server;
6
7@OpenAPIDefinition(
8 info = @Info(
9 title = "weatherbackend",
10 version = "v2.0"
11 ),
12 servers = @Server(url = "http://weatherbackend:8080")
13)
14public class OpenAPIConfig {
15}
Using the @Info
annotation’s field title
we can specify the Kong service name. And with the url
field of the @Server
annotation we define how Kong internally accesses our service later.
Having that class in place, we can do another generation of our openapi.json
by running mvn verify -DskipTests=true
. This should have the new information propagated (you may need to reformat the code inside you IDE to not just see a one-liner):
1{ 2 "openapi": "3.0.1", 3 "info": { 4 "title": "weatherbackend", 5 "version": "v2.0" 6 }, 7 "servers": [ 8 { 9 "url": "http://weatherbackend:8080", 10 "variables": {} 11 } 12 ], 13 "paths": { 14 "/weather/general/outlook": { 15 "get": { 16 "tags": [ 17 "weather-backend-api" 18 ], 19 "operationId": "infoAboutGeneralOutlook", 20 "responses": { 21 "200": { 22 "description": "OK", 23 "content": { 24 "application/json": { 25 "schema": { 26 "type": "string" 27 } 28 } 29 } 30 } 31 } 32 },
Generating Kong Declarative Configiguration from OpenAPI spec
We could start generating the Kong Declarative Configuration file from our OpenAPI spec using Insomnia Designer and the Kong bundle plugin. But as we need to do this generation every time our Spring API code changes, this wouldn’t match our requirements. Because otherwise the configuration in Kong would differ more and more with every change! Additionally we want to be able to run the generation process on our CI servers as well.
And there’s another cool tool to help us here: Insomnia Inso CLI . Because Inso CLI
encorporates a OpenAPI to Kong Configuration converting functionality using the npm library openapi-2-kong . In order to use Inso CLI
, we need to install it using npm
:
1npm i -g insomnia-inso
If you’re a MacOS user like me and run into problems like clang: error: no such file or directory: '/usr/include'
, you may want to look at this stackoverflow answer .
Now with Inso CLI
readily installed, we can finally go from openapi.json
to kong.yml
. All we have to do is use the inso generate config
command as described in the docs . We should add the option --type declarative
, since the output should result in a Kong declarative configuration file. Additionally, we need to tell Inso CLI
where to find our OpenAPI spec file at weatherbackend/target/openapi.json
. The last part is to define where the Kong declarative configuration file should be located using the --output kong/kong.yml
parameter. So here’s the fully working Inso CLI
command:
1inso generate config weatherbackend/target/openapi.json --output kong/kong.yml --type declarative --verbose
If you want to see a bit more of an info what the inso CLI is doing, you can also add --verbose
to the command. After executing the command, we should find a new file inside our project at kong/kong.yml
, which contains our desired Kong Declarative Configuration:
1_format_version: "1.1" 2services: 3 - name: weatherbackend 4 url: http://weatherbackend:8080 5 plugins: [] 6 routes: 7 - tags: 8 - OAS3_import 9 name: weatherbackend-path-get 10 methods: 11 - GET 12 paths: 13 - /weather/general/outlook 14 strip_path: false 15 - tags: 16 - OAS3_import 17 name: weatherbackend-path_1-post 18 methods: 19 - POST 20 paths: 21 - /weather/general/outlook 22 strip_path: false 23 - tags: 24 - OAS3_import 25 name: weatherbackend-path_2-get 26 methods: 27 - GET 28 paths: 29 - /weather/(?<name>\S+)$ 30 strip_path: false 31 tags: 32 - OAS3_import 33upstreams: 34 - name: weatherbackend 35 targets: 36 - target: weatherbackend:8080 37 tags: 38 - OAS3_import
Really cool! We found an automatable way on how to generate Kong Declarative Configuration from our OpenAPI spec 🙂
Running the Kong Declarative Configuration generation inside the Maven build
Because our API code inside our Spring Boot app evolves and changes, we should initialize a re-generation of our Kong Declarative Configuration file everytime we change our Spring Boot app’s code. Playing with different possibilities from where to trigger the automatic re-generation (Docker, Compose, CI server, …), I found a really simple solution to bind this crucial step to our standard build process.
I simply used the exec-maven-plugin to integrate the inso CLI
execution into our standard build process (I’am sure you can do this using different build tools also). Although the exec-maven-plugin
‘s XML syntax may look a bit strange at first sight, it makes totally sense to have the generation of our kong.yml
also directly coupled to our build process. Therefore let’s have a look at the needed addition to the pom.xml :
1<plugin> 2 <groupId>org.codehaus.mojo</groupId> 3 <artifactId>exec-maven-plugin</artifactId> 4 <version>3.0.0</version> 5 <executions> 6 <execution> 7 <id>execute-inso-cli</id> 8 <phase>verify</phase> 9 <goals> 10 <goal>exec</goal> 11 </goals> 12 </execution> 13 </executions> 14 <configuration> 15 <arguments> 16 <argument>generate</argument> 17 <argument>config</argument> 18 <argument>target/openapi.json</argument> 19 <argument>--output</argument> 20 <argument>../kong/kong.yml</argument> 21 <argument>--type</argument> 22 <argument>declarative</argument> 23 <argument>--verbose</argument> 24 </arguments> 25 </configuration> 26</plugin>
Using mvn exec:exec
, we are now able to execute inso CLI
through Maven:
1$ mvn exec:exec 2[INFO] Scanning for projects... 3[INFO] 4[INFO] ------------< io.jonashackt.weatherbackend:weatherbackend >------------- 5[INFO] Building weatherbackend 2.3.5.RELEASE 6[INFO] --------------------------------[ jar ]--------------------------------- 7[INFO] 8[INFO] --- exec-maven-plugin:3.0.0:exec (default-cli) @ weatherbackend --- 9› Data store configured from app data directory at /Users/jonashecht/Library/Application Support/Insomnia Designer 10› Load api specification with identifier target/openapi.json from data store 11› Found 0. 12› Generating config from file target/openapi.json 13Configuration generated to "../kong/kong.yml". 14[INFO] ------------------------------------------------------------------------ 15[INFO] BUILD SUCCESS 16[INFO] ------------------------------------------------------------------------ 17[INFO] Total time: 1.671 s 18[INFO] Finished at: 2020-11-05T14:05:04+01:00 19[INFO] ------------------------------------------------------------------------
As you can see, the inso CLI
logging Configuration generated to "kong/kong.yml".
is part of the output.
In addition, we can push the integration into our build process even further: as mentioned by Pascal , we can even bind the execution of the exec-maven-plugin
to the standard Maven build. All we have to do is use the tag to bind the execution to the
verify
phase:
1<executions> 2 <execution> 3 <id>execute-inso-cli</id> 4 <phase>verify</phase> 5 <goals> 6 <goal>exec</goal> 7 </goals> 8 </execution> 9</executions>
And that’s pretty cool, since that’s exactly Maven phase where the generation of the OpenAPI spec also takes place. Now with this addition, a normal mvn verify
does every needed step for us to generate a Kong Declarative Configuration file from our Spring Boot REST endpoints! Just have a look at the build log (I’ve shortened that one a bit):
1$ mvn verify 2... 3[INFO] --- spring-boot-maven-plugin:2.3.5.RELEASE:start (pre-integration-test) @ weatherbackend --- 4[INFO] Attaching agents: [] 5 6 . ____ _ __ _ _ 7 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 8( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 9 \\/ ___)| |_)| | | | | || (_| | ) ) ) ) 10 ' |____| .__|_| |_|_| |_\__, | / / / / 11 =========|_|==============|___/=/_/_/_/ 12 :: Spring Boot :: (v2.3.5.RELEASE) 13... 142020-11-12 08:33:23.006 INFO 23312 --- [ main] i.j.w.WeatherBackendApplication : Started WeatherBackendApplication in 1.867 seconds (JVM running for 2.371) 15[INFO] 16[INFO] --- springdoc-openapi-maven-plugin:1.1:generate (default) @ weatherbackend --- 172020-11-12 08:33:23.581 INFO 23312 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 182020-11-12 08:33:23.581 INFO 23312 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 192020-11-12 08:33:23.585 INFO 23312 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms 202020-11-12 08:33:23.815 INFO 23312 --- [nio-8080-exec-1] o.springdoc.api.AbstractOpenApiResource : Init duration for springdoc-openapi is: 207 ms 21... 22[INFO] 23[INFO] --- exec-maven-plugin:3.0.0:exec (execute-inso-cli) @ weatherbackend --- 24› Data store configured from app data directory at /Users/jonashecht/Library/Application Support/Insomnia Designer 25› Load api specification with identifier target/openapi.json from data store 26› Found 0. 27› Generating config from file target/openapi.json 28Configuration generated to "../kong/kong.yml". 29[INFO] ------------------------------------------------------------------------ 30[INFO] BUILD SUCCESS 31[INFO] ------------------------------------------------------------------------ 32[INFO] Total time: 11.185 s 33[INFO] Finished at: 2020-11-05T14:07:54+01:00 34[INFO] ------------------------------------------------------------------------
This means that a standard Maven build command like mvn package
will build & test our Spring Boot app, then generate the openapi.json
using the springdoc-openapi-maven-plugin
and finally generate the kong.yml
via Inso CLI
executed by the exec-maven-plugin
!
Docker Compose with Kong DB-less deployment & declarative configuration
Up to this point, the setup pretty much met every requirement I had set myself at the beginning. But there’s one thing missing: A streamlined Kong deployment. For the course of this post I wanted to stay with the simplest possible infrastructure for the moment: a Docker Compose setup.
Looking at the official Docker Compose file , I found two (!!) database migration services, one database service and one service for Kong. I was really overwhelmed at first sight. But digging into the topic of how to deploy Kong, I found the DB-less deployment based on Kong Declarative Configuration in the official docs. Hey! Didn’t we generate Declarative Configuration already? Would it be possible to simply deploy Kong based on this kong.yml
?
To my surprise, I found out: yes, it’s possible! And as the docs state the DB-less deployment has some advantages over a deployment using a database:
1. a reduced number of dependencies: no need to manage a database installation if the entire setup for your use cases fits in memory
2. it is a good fit for automation in CI/CD scenarios: configuration for entities can be kept in a single source of truth managed via a Git repository
3. it enables more deployment options for Kong
Of course there are some drawbacks also (as always). Not all plugins support this mode and there is no central configuration database if you want to run multiple Kong nodes. But I guess for our setup here we can live very well with that. So let’s just create a docker-compose.yml :
1version: '3.7' 2 3services: 4 kong: 5 image: kong:2.2.0 6 environment: 7 KONG_ADMIN_ACCESS_LOG: /dev/stdout 8 KONG_ADMIN_ERROR_LOG: /dev/stderr 9 KONG_ADMIN_LISTEN: '0.0.0.0:8001' 10 KONG_DATABASE: "off" 11 KONG_DECLARATIVE_CONFIG: /usr/local/kong/declarative/kong.yml 12 KONG_PROXY_ACCESS_LOG: /dev/stdout 13 KONG_PROXY_ERROR_LOG: /dev/stderr 14 volumes: 15 - ./kong/:/usr/local/kong/declarative 16 networks: 17 - kong-net 18 ports: 19 - "8000:8000/tcp" 20 - "127.0.0.1:8001:8001/tcp" 21 - "8443:8443/tcp" 22 - "127.0.0.1:8444:8444/tcp" 23 healthcheck: 24 test: ["CMD", "kong", "health"] 25 interval: 10s 26 timeout: 10s 27 retries: 10 28 restart: on-failure 29 deploy: 30 restart_policy: 31 condition: on-failure 32 33 # no portbinding here - the actual services should be accessible through Kong 34 weatherbackend: 35 build: ./weatherbackend 36 ports: 37 - "8080" 38 networks: 39 - kong-net 40 tty: 41 true 42 restart: 43 unless-stopped 44 45networks: 46 kong-net: 47 external: false
I litterally threw everything out we don’t really really need in a DB-less scenario. No kong-migrations
, kong-migrations-up
, kong-db
services – and no extra Dockerfile
as stated in this blog post . With that we only have a single kong
service for the API gateway – and a weatherbackend
Spring Boot service.
In order to make the DB-less deployment work, we need to use some special Kong environment variables . First we switch to DB-less mode using KONG_DATABASE: "off"
. Then we tell Kong where to get its Declarative Configuration file via the variable KONG_DECLARATIVE_CONFIG: /usr/local/kong/declarative/kong.yml
.
The final thing is to make our generated kong.yml
available inside the Kong container at /usr/local/kong/declarative/kong.yml
. Therefore I used a simple volume mount like this: ./kong/:/usr/local/kong/declarative
. With this solution there’s also no need to manually create the Volume as described in the docs. Or to create another Dockerfile solely to load the config file into the Kong container, as stated in some posts. And we don’t even need to use decK here, since this tool is only needed to sync Declarative Configuration with the Kong database. 🙂 Now this thing started to be fun! So let’s fire up our Kong setup by running a well-known docker-compose up
:
How cool is that? We just should have an eye on one thing: the crucial part here is that Kong successfully loads the declarative configuration file and logs something like [kong] init.lua:354 declarative config loaded from /usr/local/kong/declarative/kong.yml
.
Having fired up our whole setup, we could now have a look at Kong’s admin API by opening localhost:8001 in our Browser. We can also double-check localhost:8001/status , where we have a good overview of Kong’s current availability.
Accessing our Spring Boot app through Kong
Now let’s look if our Spring Boot app is ready to be accessed through our API Gateway. Specifically, we need to examine the configured Kong services and find out if the OpenAPI spec import worked in the way we’d expected it in the first place. Therefore let’s look at the list of all currently registered Kong services at localhost:8001/services :
An important point here is the host
(name) and port
of our Spring Boot service. The service access through Kong relies on this configuration and it is defined as target
inside the upstreams
section of the Kong Declarative Configuration kong.yml :
1... 2upstreams: 3 - name: weatherbackend 4 targets: 5 - target: weatherbackend:8080 6 tags: 7 - OAS3_import
We already tweaked the API information in the generated OpenAPI spec to make it suitable for our Docker-Compose setup, because here Docker generates a DNS name called weatherbackend
for us, which is based on the Docker-Compose service name. If you choose another setup, take a closer look at this configuration! Kong doesn’t ship with DNS resolver or anything. Be aware that this is a thing you need to take care of yourself. Especially if you run into errors like connect() failed (111: Connection refused) while connecting to upstream
.
Now we should have everything in place to access our Spring Boot service through Kong! We can now use Postman , Insomnia Core or another HTTP client to access our Spring Boot app with a GET on localhost:8000/weather/MaxTheKongUser
Looking into our Docker Compose log, we should also see the successful responses from our weatherbackend
service:
1weatherbackend_1 | 2020-11-05 07:54:48.381 INFO 7 --- [nio-8080-exec-1] i.j.controller.WeatherBackendController : Request for /{name} with GET 2kong_1 | 172.19.0.1 - - [05/Nov/2020:07:54:48 +0000] "GET /weather/MaxTheKongUser HTTP/1.1" 200 133 "-" "PostmanRuntime/7.26.1" 3weatherbackend_1 | 2020-11-05 07:54:59.951 INFO 7 --- [nio-8080-exec-2] i.j.controller.WeatherBackendController : Request for /{name} with GET 4kong_1 | 172.19.0.1 - - [05/Nov/2020:07:54:59 +0000] "GET /weather/MonicaTheKongUser HTTP/1.1" 200 136 "-" "PostmanRuntime/7.26.1" 5weatherbackend_1 | 2020-11-05 07:55:06.573 INFO 7 --- [nio-8080-exec-3] i.j.controller.WeatherBackendController : Request for /{name} with GET 6kong_1 | 172.19.0.1 - - [05/Nov/2020:07:55:06 +0000] "GET /weather/MartinTheKongUser HTTP/1.1" 200 136 "-" "PostmanRuntime/7.26.1"
If you want to know more about the URI paths that are configured in Kong, simply take a look at the /servicename/routes
api at localhost:8001/services/weatherbackend/routes .
Automatically re-generating OpenAPI spec & Kong Declarative Configuration using GitHub Actions
As we want to make sure everything works as expected every time code changes, we need to include the whole process into our CI/CD pipeline! For this post , I went with GitHub Actions since I needed quite a flexible solution that is capable of running a full Maven build, the npm
based Inso CLI
and fire up a Docker-Compose setup at the same time. But this should be reproducible on every other CI platform that is able to handle all those things also. The example project on GitHub ships with a fully working openapi-to-kong-config-full-setup.yml . Let’s look at the first part of it:
1name: openapi-to-kong-config-full-setup 2 3on: [push] 4 5jobs: 6 build: 7 8 runs-on: ubuntu-latest 9 10 steps: 11 - uses: actions/checkout@v2 12 13 - name: Install Node/npm for Inso 14 uses: actions/setup-node@v2 15 with: 16 node-version: '14' 17 18 - name: Install Java & Maven 19 uses: actions/setup-java@v1 20 with: 21 java-version: 15 22 23 - name: Install Inso and run Maven build, that'll generate OpenAPI spec and Kong declarative config later needed for Docker Compose 24 run: | 25 echo "Install insomnia-inso (Inso CLI) which is needed by our Maven build process later" 26 npm install insomnia-inso 27 28 echo "Show Inso version" 29 node_modules/insomnia-inso/bin/inso --version 30 31 echo "Build Spring Boot app with Maven" 32 echo "This also generates OpenAPI spec file at weatherbackend/target/openapi.json and the Kong declarative config at kong/kong.yml from the OpenAPI spec with Inso CLI" 33 mvn clean verify --file weatherbackend/pom.xml --no-transfer-progress -Dinso.executable.path=node_modules/insomnia-inso/bin/inso 34 35 echo "Show kong.yml" 36 cat kong/kong.yml 37...
There are some things to note about the pipeline here. A source of error might be the inso
executable itself since GitHub Actions wasn’t able to find it:
1ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:3.0.0:exec (execute-inso-cli) on project weatherbackend: Command execution failed.: Cannot run program "inso" (in directory "/home/build/jonashackt/spring-boot-openapi-kong/weatherbackend"): error=2, No such file or directory -> [Help 1]
I found a solution for that problem while having a look at this stackoverflow answer . We simply need to override the inso
executable path on GitHub Actions. Therefore need to alter our pom.xml slightly to use a new property called ${inso.executable.path}
:
1<properties> 2 ... 3 <inso.executable.path>inso</inso.executable.path> 4</properties> 5... 6<plugin> 7 <groupId>org.codehaus.mojo</groupId> 8 <artifactId>exec-maven-plugin</artifactId> 9 <version>3.0.0</version> 10... 11 <configuration> 12 <executable>${inso.executable.path}</executable> 13 <arguments> 14...
With this change we should be able to run our normal mvn verify
locally – and a special mvn verify -Dinso.executable.path=inso-special-path
on GitHub Actions like this:
1mvn clean verify --file weatherbackend/pom.xml --no-transfer-progress -Dinso.executable.path=node_modules/insomnia-inso/bin/inso
Right after the Maven build finished, we also sneak peak into the generated kong.yml
via cat kong/kong.yml
– since this is the prerequisite for Kong to start up correctly configured later.
Continuously test-drive the Spring Boot service access through Kong
As we want our Kong instance to always use the latest generated Declarative Configuration file, it is crucial to fire up the infrastructure only after a fresh Maven build. Now that the build is done, we can finally start Kong with the latest API definition. This is also reflected in the second part of the openapi-to-kong-config-full-setup.yml :
1... 2 - name: Fire up Docker Compose setup with Kong & do some checks 3 run: | 4 docker-compose up -d 5 6 echo "Let's wait until Kong is available (we need to improve this)" 7 sleep 10 8 9 echo "Also have a look into the Kong & Spring Boot app logs" 10 docker ps -a 11 docker-compose logs kong 12 docker-compose logs weatherbackend 13 14 echo "Have a look at the /services endpoint of Kong's admin API" 15 curl http://localhost:8001/services 16 17 echo "Verify that we can call our Spring Boot service through Kong" 18 curl http://localhost:8000/weather/MaxTheKongUser 19 20 echo "Again look into Kong logs to see the service call" 21 docker-compose logs kong
Right after our Docker-Compose setup with docker-compose up -d
we need to wait for the containers to spin up. On GitHub Actions we can simply use sleep
here . Thereafter, the containers should both be ready and we can take a look into the Kong & Spring Boot app logs with docker-compose logs kong
& docker-compose logs weatherbackend
.
After checking the service admin API with curl http://localhost:8001/services
we finally curl for our service through Kong with curl http://localhost:8000/weather/MaxTheKongUser
– just as we would do it on our local machine.
Integrating Spring Boot & Kong is fun
Wow! That was quite a journey connecting our Spring Boot apps with Kong. Using springdoc-openapi
, we found a way to elegantly generate OpenAPI
spec files from our Spring code – without touching it. So existing apps should also be able to use the setup. Leveraging Insomnia Inso CLI
, we managed to generate Kong Declarative Configuration from our OpenAPI file, and all of this completely inside our build process. No manual steps required! Together with the DB-less Kong infrastructure setup, we directly choose the Declarative Configuration as the way to elegantly configure Kong. And as icing on the cake, all this is 100% automated inside our CI/CD pipeline so that every code change triggers a re-generation of the Kong configuration. I guess it is safe to say that we met the initial requirements of this article 🙂
Hopefully this article inspires you to get your hands on API Gateway. In some future posts we may take a look at how to integrate different API gateways with Spring Boot. Stay tuned!
More articles
fromJonas Hecht
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Jonas Hecht
Senior Solution Architect
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.