Giter Club home page Giter Club logo

traefik-cache-nginx-spring-boot's Introduction

traefik-cache-nginx-spring-boot

Build Status License renovateenabled versionspringboot versiontestcontainers

As Traefik is a really gread & modern loadbalancer, but it sadly doesn´t feature caching right now. So we need to put something in front of it, that is able to do caching - like old Nginx.

And as I like full examples, let´s bring in a client application (weatherclient), which want´s to call a server backend (weatherbackend). Both are implemented as simple Spring Boot microservices, as the following ASCII shows:

                  -----------------------------------------------------------------------------    
                 | Docker Network scope                                                        |  
                 |                                                                             |  
                 |                                                                             |   
                 |                                                                             |
 ============    |   ==============     ================     ===============     ============  |
 =  docker- =    |   =            =     =              =     =             =     =          =  |
 = network- = -----> =   weather  = --> =    Nginx     = --> =   Traefik   = --> =  weather =  |
 =  client  =    |   =    client  =     =  (caching)   =     = (loadbalan.)=     =  backend =  |
 ============    |   ==============     ================     ===============     ============  |
                 |                                                                             |
                 |                                                                             |
                 |                                                                             |
                  -----------------------------------------------------------------------------
                 

It also shows, that we simulate the whole scenario with Docker. To have the chance to execute everything within an intergration test, we use docker-compose-rule and the docker-network-client app. Why?

As the weatherclient only has access to the DNS alias weatherbackend, if it itself is part of the Docker (Compose) network, we need another way to run an Integration test inside the Docker network scope. Therefore we use the docker-compose-rule and the docker-network-client that just calls weatherclient inside the Docker network.

HowTo Use

Everything you need to run a full build and complete test (incl. Integrationtest of docker-network-client firing up all microservices that´ll call each other with client certificate support) is this:

mvn clean install

Only if you want to check everything manually - which you for sure want to do :) - fire up all components with:

docker-compose up -d

Now you can have a look at some of the components of our architecture:

Traefik: http://localhost:8080/dashboard/#/

weatherclient: http://localhost:8085/swagger-ui.html

Nginx: http://localhost:8088/

Alternative variant without Docker-Compose for the Dockerized Spring Boot backends

In a real world DevOps scenario, you may want to run Traefik separate from the Dockerized Spring Boot backends - so that each Backend is able to have it's own CI/CD pipeline for example (e.g. GitLab CI).

Now in this case you don't want a central docker-compose.yml, since you want to run and scale all the services independently.

There's a way to run Traefik and the backends separately, as shown in the docker-compose-traefik-only.yml without-compose.sh files:

version: '3.8'

services:

  traefik:
    image: traefik
    command:
      # - "--logLevel=DEBUG"
      - "--api.insecure=true"
      # Enabling docker provider
      - "--providers.docker=true"
      # Do not expose containers unless explicitly told so
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    networks:
      - traefiknet
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    restart:
      always

networks:
  traefiknet:
#!/usr/bin/env bash

echo "[-->] Start Traefik using Compose as usual"
docker-compose -f docker-compose-traefik-only.yml up -d

echo "[-->] Startup weatherbackend without Compose - but connect to Traefik"
docker build ./weatherbackend --tag weatherbackend
docker run \
  --rm \
  -d \
  -p 8095 \
  --label="traefik.enable=true" \
  --label="traefik.http.routers.whoami.entrypoints=web" \
  --label="traefik.http.routers.weatherbackend.rule=Host(\`weatherbackend.server.test\`)" \
  --network="traefik-cache-nginx-spring-boot_traefiknet" \
  --name weatherbackend \
  weatherbackend

echo "[-->] Startup weatherclient without Compose - but connect to Traefik"
docker build ./weatherclient --tag weatherclient
docker run \
  --rm \
  -d \
  -p 8085:8085 \
  --network="traefik-cache-nginx-spring-boot_traefiknet" \
  --name weatherclient \
  weatherclient

The downside of this approach is, that one cannot use the scaling option of Docker-Compose now so easily.

Nginx + Traefik + weatherbackend in logical scope (aka host) with the help of Docker DNS

Additionally, in real world scenarios, Nginx + Traefik + weatherbackend would reside on a separate host with their own DNS configuration. So there´s a second "logical" scope here, which we could have implemented with tools like Vagrant - but this would have been overkill here.

Trying to imitate a machine, where Traefik + weatherbackend + Nginx are running all on one machine with DNS configured, we configure the Docker DNS alias for weatherbackend to the Traefik container, which routes it then to the weatherbackend. This is done with the help of this Docker Compose configuration (see the docs):

  traefik:
    ...
    networks:
      default:
        aliases:
          - weatherbackend.server.test
    ...

Instead of configuring the weatherbackend directly to have the DNS alias weatherbackend.server.test, we use the Traefik Docker Compose service here - which has a similar effect to a scoped machine around Nginx, Traefik & weatherbackend. We should see the call now in Traefik GUI:

first-call-weatherbackend-through-traefik-with-docker-dns-configured

Now installing Nginx as cache

To see what´s happening, we need to activate Nginx´ logging to the Docker log system. As we use the alpine image here, we need to create our own Dockerfile for that:

FROM nginx:alpine

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

CMD ["nginx-debug", "-g", "daemon off;"]

Additionally, we need to imitate the Scope of Nginx + Traefik + weatherbackend again - now that Nginx is at front of all three:

  nginx:
    build: ./nginx
    ...
    ...
    networks:
      default:
        aliases:
          - weatherbackend.server.test
    ...

Now, the WeatherclientController.class needs to access Nginx instead of Traefik directly. This is done with the like the first tutorial steps with Traefik discribe - like the famous curl:

curl -H Host:weatherbackend.server.test http://nginx:80 -v

We therefore set the Host header when using Spring´s RestTemplate:

import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;

@RestController
public class WeatherclientController {

    private RestTemplate restTemplate = new RestTemplate();

    /*
     * Without the System property, we won´t be able to set the Host header, see
     * https://stackoverflow.com/questions/43223261/setting-host-header-for-spring-resttemplate-doesnt-work/43224279
     * and https://stackoverflow.com/a/8172736/4964553
     */
    @PostConstruct
    public void setProperty() {
        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
    }

    @GetMapping("/forecast/{cityname}")
    @ResponseStatus(HttpStatus.OK)
    public String forecast(@PathVariable("cityname") String cityname) {

        HttpHeaders headers = new HttpHeaders();
        headers.set("Host", "weatherbackend.server.test");

        ResponseEntity<String> responseEntity = restTemplate.exchange("http://nginx:80/weather/" + cityname,
                HttpMethod.GET,
                new HttpEntity<String>(null, headers),
                String.class);

        return responseEntity.getBody();
    }
}

As you maybe already noticed, setting the Host header in this way is only possible, if we set System.setProperty("sun.net.http.allowRestrictedHeaders", "true");, like we do it in Spring style:

    /*
     * Without the System property, we won´t be able to set the Host header, see
     * https://stackoverflow.com/questions/43223261/setting-host-header-for-spring-resttemplate-doesnt-work/43224279
     * and https://stackoverflow.com/a/8172736/4964553
     */
    @PostConstruct
    public void setProperty() {
        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
    }

Now our setup works perfectly fine. If you fire many requests with the help of weatherclient, using Springfox Swagger GUI at http://localhost:8085/swagger-ui.html#!/weatherclient45controller/forecastUsingGET and do a docker logs NginxContainerIdHere --follow to see the logs of Nginx and a docker logs weatherbackendContainerIdHere to see the logs of the weatherbackend, you´ll notice only one call in Traefik and one call of the weatherbackend:

one-call-in-traefik

one-call-of-weatherbackend

But you´ll notice many calls inside Nginx:

many-calls-in-nginx

traefik-cache-nginx-spring-boot's People

Contributors

jonashackt avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

traefik-cache-nginx-spring-boot's Issues

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Warning

Renovate failed to look up the following dependencies: Failed to look up maven package de.jonashackt:traefik-cache-nginx-spring-boot.

Files affected: docker-network-client/pom.xml, weatherbackend/pom.xml, weatherclient/pom.xml


Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

docker-compose
docker-compose-traefik-only.yml
docker-compose.yml
dockerfile
nginx/Dockerfile
weatherbackend/Dockerfile
  • openjdk 11.0.16-jdk-slim
weatherclient/Dockerfile
  • openjdk 11.0.16-jdk-slim
github-actions
.github/workflows/maven.yml
  • actions/checkout v2
  • actions/setup-java v2
maven
docker-network-client/pom.xml
  • de.jonashackt:traefik-cache-nginx-spring-boot 0.0.1-SNAPSHOT
  • io.rest-assured:rest-assured 4.5.1
  • org.testcontainers:testcontainers 1.19.8
pom.xml
  • org.springframework.boot:spring-boot-starter-parent 2.7.18
weatherbackend/pom.xml
  • de.jonashackt:traefik-cache-nginx-spring-boot 0.0.1-SNAPSHOT
  • commons-io:commons-io 2.16.1
  • org.springdoc:springdoc-openapi-ui 1.8.0
  • io.rest-assured:rest-assured 4.5.1
  • org.apache.pdfbox:pdfbox 2.0.31
weatherclient/pom.xml
  • de.jonashackt:traefik-cache-nginx-spring-boot 0.0.1-SNAPSHOT
  • org.springdoc:springdoc-openapi-ui 1.8.0
  • io.rest-assured:rest-assured 4.5.1

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.