DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Component Tests for Spring Cloud Microservices
  • A Robust Distributed Payment Network With Enchanted Audit Functionality - Part 2: Spring Boot, Axon, and Implementation
  • 7 Microservices Best Practices for Developers

Trending

  • Building a Real-Time Change Data Capture Pipeline With Debezium, Kafka, and PostgreSQL
  • Resolving Parameter Sensitivity With Parameter Sensitive Plan Optimization in SQL Server 2022
  • How GitHub Copilot Helps You Write More Secure Code
  • Security by Design: Building Full-Stack Applications With DevSecOps
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Microservices - Part 2: Configuration Management With Spring Cloud Config and Vault

Microservices - Part 2: Configuration Management With Spring Cloud Config and Vault

Continue your microservices journey by learning about configuration with Spring Cloud Config Server and storing sensitive data with Vault.

By 
Siva Prasad Reddy Katamreddy user avatar
Siva Prasad Reddy Katamreddy
·
Mar. 09, 18 · Tutorial
Likes (20)
Comment
Save
Tweet
Share
21.7K Views

Join the DZone community and get the full member experience.

Join For Free

In Microservices Using Spring Boot & Spring Cloud - Part 1: Overview, we took a brief look at what microservices are and how we can use SpringBoot and SpringCloud to build microservices.

In this post, we are going to learn:

  • What is the need for Spring Cloud Config and Vault?
  • How to create our first microservice: catalog-service.
  • How to create Spring Cloud Config Server.
  • How to use Vault for storing sensitive data.

Microservices Using Spring Boot & Spring Cloud

Spring Boot already provides a lot of options to externalize configuration properties. However, once the application is started, you can't change those property values at runtime. You need to update the properties and restart the application to take those changes into effect.

In the microservices world, there could be a large number of microservices and multiple instances of those microservices are running. Updating configuration properties and restarting all those instances manually or even with automated scripts may not be feasible. Spring Cloud Config addresses this problem.

We can create a Spring Cloud Config Server which provides the configuration values for all of our microservices. We can use git, svn, database or Consul as a backend to store the configuration parameters. Then we can configure the location of Spring Cloud Config server in our microservice so that it will load all the properties when we start the application. In addition to that, whenever we update the properties we can invoke /refresh REST endpoint in our microservice so that it will reload the configuration changes without requiring to restart the application.

In our applications, we also need to configure various sensitive data like database credentials, keys, tokens etc. Obviously, we don't want to store them in plain text. A better approach would be to store them in an encrypted format and Spring Cloud Config Server provides the ability to encrypt and decrypt the data. Even better we should use secure data storage tools like Vault. Spring Cloud also provides the integration with Vault so that we can store any sensitive configuration properties in Vault.

Let us start with our first microservice ie, catalog-service. Create a SpringBoot app with Web, JPA, MySQL, Actuator, DevTools, Lombok starters. Nothing fancy here so far, a typical SpringBoot application.

You can find the source code for this article here.

First, let's implement a REST endpoint to give products data and later refactor it to use Cloud Config Server. We are going to use Docker and run MySQL as a Docker container.

docker-compose.yml

version: '3'
services:
  mysqldb:
     image: mysql:5.7
     container_name: mysqldb
     ports:
       - "3306:3306"
     environment:
       MYSQL_ROOT_PASSWORD: admin
       MYSQL_DATABASE: catalog

Configure datasource properties in application.properties as follows:

server.port=8181
logging.level.com.sivalabs=debug

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/catalog?useSSL=false
spring.datasource.username=root
spring.datasource.password=admin

spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

//expose all the Actuator endpoints
management.endpoints.web.exposure.include=*

Create JPA entity Product.java as follows:

import lombok.Data;
import javax.persistence.*;

@Data
@Entity
@Table(name = "products")
public class Product {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable = false, unique = true)
    private String code;
    @Column(nullable = false)
    private String name;
    private String description;
    private double price;
}

Create Spring Data JPA repository ProductRepository.java as follows:

import com.sivalabs.catalogservice.entities.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface ProductRepository extends JpaRepository<Product, Long> {
    Optional<Product> findByCode(String code);
}

Create ProductService which just delegate to ProductRepository for now. We can directly inject Repository into our web layer components (Controllers), but going forward, there could be business logic which I don't like to put in either Controller or in Repository.

import com.sivalabs.catalogservice.entities.Product;
import com.sivalabs.catalogservice.repositories.ProductRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;

@Service
@Transactional
@Slf4j
public class ProductService {
    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public List<Product> findAllProducts() {
        return productRepository.findAll();
    }

    public Optional<Product> findProductByCode(String code) {
        return productRepository.findByCode(code);
    }
}

Finally, create our REST controller, ProductController.java:

import com.sivalabs.catalogservice.entities.Product;
import com.sivalabs.catalogservice.exceptions.ProductNotFoundException;
import com.sivalabs.catalogservice.services.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/products")
@Slf4j
public class ProductController {

    private final ProductService productService;

    @Autowired
    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping("")
    public List<Product> allProducts() {
        return productService.findAllProducts();
    }

    @GetMapping("/{code}")
    public Product productByCode(@PathVariable String code) {
        return productService.findProductByCode(code)
                .orElseThrow(() -> new ProductNotFoundException("Product with code ["+code+"] doesn't exist"));
    }
}

Create ProductNotFoundException extending RuntimeException and annotate with @ResponseStatus(HttpStatus.NOT_FOUND).

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ProductNotFoundException extends RuntimeException {
    public ProductNotFoundException() {
    }

    public ProductNotFoundException(String message) {
        super(message);
    }

    public ProductNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public ProductNotFoundException(Throwable cause) {
        super(cause);
    }
}

Let's insert some sample products into our database.

src/main/resources/data.sql

DELETE FROM products;

insert into products(id, code, name, description, price) VALUES
(1, 'P001', 'Product 1', 'Product 1 description', 25),
(2, 'P002', 'Product 2', 'Product 2 description', 32),
(3, 'P003', 'Product 3', 'Product 3 description', 50)
;

Okay, now we can start our Spring Boot application and hit http://localhost:8181/api/products. You should be able to see the JSON response with the products' info.

We are going to create Spring Cloud Config Server using a Git backend. Spring Cloud Config Server is nothing but a Spring Boot project. Create a Spring Boot project with Config Server starter.

Configure the location of the Git repository where we are going to store all our configuration files in the application.properties file.

spring.config.name=configserver
server.port=8888

spring.cloud.config.server.git.uri=https://github.com/sivaprasadreddy/microservices-config-repo
spring.cloud.config.server.git.clone-on-start=true

management.endpoints.web.exposure.include=*

Now annotate the entry point class with @EnableConfigServer.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

That's it. This is all you need to do to create Spring Cloud Config Server and you just need to add application-specific config files in the git repository.

If you were mentally prepared to write a bunch of code to create Spring Cloud Config Server, sorry to disappoint you!

Refactor Catalog-Service to Use Config Server

Our catalog-service will become a client for Config Server. So, let us add Config Client starter to catalog-service which will add the following dependency.

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

Make sure you also add the spring-cloud-dependencies BOM and in <properties> section.

While using Spring Cloud Config Server the properties loading process happens at multiple stages, first loading bootstrap.properties/ YAML and then from config server.

Let's rename application.properties to bootstrap.properties and update it to have the following properties:

spring.application.name=catalog-service
server.port=8181
management.endpoints.web.exposure.include=*
spring.cloud.config.uri=http://localhost:8888

Here, we have configured the location of our Config Server and given the name catalog-service to our application using spring.application.name.

Now we need to add all the properties of our catalog-service in catalog-service.properties and commit/push it to our git repo microservices-config-repo.

microservices-config-repo/catalog-service.properties

logging.level.com.sivalabs=debug

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/catalog?useSSL=false
spring.datasource.username=root
spring.datasource.password=admin

spring.datasource.initialization-mode=always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

You can also add separate config files for different files like catalog-service-qa.properties, catalog-service-prod.properties, etc.

First, start Config Server application and then catalog-service application. This should work fine. You can check the console logs that catalog-service is fetching the properties from config server http://localhost:8888/ at startup.

Now, we are getting a little closer to our goal, but we are still storing the credentials in plain text. Let's move the sensitive config properties to Vault.

Vault is a tool for securely storing and accessing secrets. You can read more about Vault here. Vault comes as a single binary which you can download here.

Now start Vault in dev mode using the following command:

 $ vault server -dev 

In the console, you can see the information about how to use Vault and Root token.
Open a new terminal window and set the VAULT_ADDR environment variable.

 $ export VAULT_ADDR='http://127.0.0.1:8200′ 

NOTE: The Vault dev mode is only for development purpose and is not meant for production usage.

We can write secrets to Vault using vault write secret/somename key1=value1 key2=value2.

We can also put all our secrets in a JSON file and write from the file as well. Let us create a JSON file with MySQL database credentials and write to Vault.

catalog-service-credentials.json

{ 
    "spring.datasource.username": "root", 
    "spring.datasource.password": "admin"
}

 $ vault write secret/catalog-service @catalog-service-credentials.json 

You can verify the values by running vault read secret/catalog-service.

We can automate this whole process of setting up Vault and initializing with secrets using Docker. Please look at the source repository on GitHub to see how to do it - well, one way of doing it.

Now that Vault is configured and initialized with secrets, let us refactor catalog-service to use Vault.

Add Vault Configuration starter to catalog-service which will add the following dependency.

<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>

Remove the following credentials from microservices-config-repo/catalog-service.properties and commit it.

spring.datasource.username=root
spring.datasource.password=admin

Add Vault configuration properties in bootstrap.properties.

spring.cloud.vault.host=localhost
spring.cloud.vault.port=8200
spring.cloud.vault.scheme=http
spring.cloud.vault.authentication=token
spring.cloud.vault.token=934f9eae-31ff-a8ef-e1ca-4bea9e07aa09

We have configured the Vault properties, using token-based authentication and configured the Root Taken that is printed in the console log when you started the vault server.

We are all set. We moved the service properties into external config server and sensitive data into Vault.

Now start the Config Server and catalog-service and it should work just fine.

In this post, we learned how to use Spring Cloud Config to externalize the configuration properties and Vault to store secrets. You can use Spring Cloud Bus to Auto Refresh Config Changes as described in Spring Cloud Tutorials - Auto Refresh Config Changes using Spring Cloud Bus.

You can find the source code for this article here.

In the next article, we will take a look at how to use Netflix Eureka for Service Registry and Service Discovery.

Spring Framework Spring Cloud microservice Configuration management

Published at DZone with permission of Siva Prasad Reddy Katamreddy, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Distributed Tracing System (Spring Cloud Sleuth + OpenZipkin)
  • Component Tests for Spring Cloud Microservices
  • A Robust Distributed Payment Network With Enchanted Audit Functionality - Part 2: Spring Boot, Axon, and Implementation
  • 7 Microservices Best Practices for Developers

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!