Spring Cloud Config Server on Kubernetes - Part 1 - briansdevblog
source link: https://www.briansdevblog.com/2021/04/spring-cloud-config-server-on-kubernetes-part-1/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Spring Cloud Config Server on KubernetesSkip to content
This is the first of a two part article where I’ll show you how to use Spring Cloud Config Server to build centralised configuration for your microservices. In this first post we’ll create a Config Service that pulls its data from a Git repo and exposes it to other services. We’ll also create a consumer, a Config Consumer Service that pulls from the Config Service on startup. The diagram below shows how the services will interact.
In part two of this article we’ll take the two services and deploy them to Kubernetes. We’ll initially deploy and test on a local Kubernetes cluster, before deploying on Azures manged Kubernetes Service, AKS.
The diagram below is a sneak peak of what we’ll eventually run on Kubernetes.
Source Code
The source code for this post and part two is available on Github. Feel free to pull it and have a dig around.
Creating the Config Service
Without further ado lets put Spring Cloud Config Server to work and create the first of our two services, the Config Service.
To turn a vanilla Spring Boot app into a Spring Cloud Config Server you’ll need
spring-cloud-dependencies
and spring-cloud-config-server
in your POM.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.briansjavablog.microservices</groupId> <artifactId>config-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>config-server</name> <description>Demo config server provides centralised configuration for micro services</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>11</java.version> <spring-cloud.version>2020.0.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Next add the
@EnableConfigServer
annotation to the main application class.
package com.briansjavablog.microservices.configserver; 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); } }
Service Configuration
The Config Service is going to use Git as its configuration repository. In order for the Config Service to pull the configuration, we’ll need to provide the details of the GIT repo in
application.yml
.
spring: application: name: config-server cloud: config: server: git: # URI of GIT repo containing properties uri: https://github.com/briansjavablog/spring-config-server-on-kubernettes default-label: main search-paths: configuration server: port: 8888 management: endpoint: health: show-details: always endpoints: web: exposure: include: "*"
The most important config here is lines 4 to 11.
- uri: https://github.com/briansjavablog/spring-config-server-on-kubernettes
uri: https://github.com/briansjavablog/spring-config-server-on-kubernettes
is the URI of the repo that Config Service will clone. - default-label: main
default-label: main
tells the Config Service to use themain
branch of the repository - search-paths: configuration
search-paths: configuration
is the path of the properties in the repo.
These three attributes combined, will tell the Config Service to serve up configuration from https://github.com/briansjavablog/spring-config-server-on-kubernettes/tree/main/configuration
Lines 14 to 21 enable the actuator endpoints. We’re only really interested in the health endpoint as we’ll need it later when deploying to the Kubernetes cluster.
Defining a Dockerfile
Now that we’ve defined the Config Service, the next step is to containerise it with Docker. The Dockerfile below is pretty straight forward. It uses a JRE 11 base image, adds the packaged JAR and runs the JAR on startup via the command in
ENRTYPOINT
. I’ve also installed cURL as it can be handy for debugging connectivity issues between containers.
FROM openjdk:11-jre-slim-buster MAINTAINER Brian Hannaway RUN apt-get update RUN apt-get -y install curl WORKDIR /app COPY /target/config-server-0.0.1-SNAPSHOT.jar /app/ ENTRYPOINT ["java", "-jar", "config-server-0.0.1-SNAPSHOT.jar"]
Now that we’ve defined the Config Service, its time to put it to work. Next we’ll create a consumer service that will pull its configuration from Config Service on startup.
Config Consumer Service
The Config Consumer service is a vanilla Spring Boot app with a simple REST endpoint. On startup the Config Consumer Service will call the Config Service to retrieve its configuration. The REST endpoint will return the config values loaded from the Configuration Service, allowing us to do a simple end to end test.
POM Definition
To consume config from the Confg Service we’ll need
spring-cloud-starter-config
in the POM. The full POM definition is shown below.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.briansjavablog.microservices</groupId> <artifactId>config-consumer-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>config-consumer-service</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>11</java.version> <spring-cloud.version>2020.0.0</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
REST Endpoint
The REST endpoint is very simple. It returns a
TimoutConfig
object with 2 values, connectionTimeoutMillis
and readTimeoutMillis
. These values are sourced from the Config Service and injected into the controller via Configuration
.
@Slf4j @RestController @AllArgsConstructor public class ConfigConsumerController { private Configuration configuration; @GetMapping("/timeout-config") public ResponseEntity<TimeoutConfig> getTimeoutConfiguration(){ TimeoutConfig timeoutConfig = new TimeoutConfig(); timeoutConfig.setConnectionTimeoutMillis(configuration.getConnectionTimeoutMillis()); timeoutConfig.setReadTimeoutMillis(configuration.getReadTimeoutMillis()); log.info("retrieving TimeoutConfig [{}]", timeoutConfig); return ResponseEntity.ok(timeoutConfig); } }
Configuration Properties
Configuration
contains the two config values that we exposed on the REST endpoint. The @ConfigurationProperties(prefix="config-consumer-service")
annotation along with the configuration in bootstrap.yml
, tell Spring boot to source connectionTimeoutMillis
and readTimeoutMillis
from the Config Service, on startup. The prefix config-consumer-service
is passed to the Config Service and used as a key to look up the configuration specific to the Config Consumer Service.
@Setter @Getter @Component @EnableRetry @ConfigurationProperties(prefix="config-consumer-service") public class Configuration { private Integer connectionTimeoutMillis; private Integer readTimeoutMillis; }
bootstrap.yml
In order to load configuration from a Spring Cloud Config Server, we need to provide the connection details for the service we’re calling. To make these connection details available to Spring Boot early in the startup sequence, we need add them to
bootstrap.yml
rather than application.yml
. We do this because bootstrap.yml
is loaded by the parent ApplicationContext
, before application.yml
is loaded.
spring: application: name: config-consumer-service cloud: config: profile: uat # target URI for config server corresponds to Kubernetes Service config-service uri: http://config-service:8888 # the label must correspond with the branch name in Github label: main fail-fast: true retry: # retry configuration retrieval up to 100 times max-attempts: 30 # max backoff interval max-interval: 8000
- name: config-consumer-service
name: config-consumer-service
tells the Config Service what application is requesting its configuration - profile: uat
profile: uat
tells the Config Service to return properties associated with the active profileuat
- uri: http://config-service:8888
uri: http://config-service:8888
is the URI of the Config Service - label: main
label: main
is the name of the branch in the Git repo, containing the properties - fail-fast: true
fail-fast: true
tells the Config Consumer Service to fail startup if it cannot retrieve its properties from Config Service - max-attempts: 30
max-attempts: 30
used by Spring Retry to retry the call to Config Service up to 30 times, in the event of a failure. This is useful as the Config Service may not be immediately available when the Config Consumer Service starts up. - max-internal: 8000
max-internal: 8000
is the maximum back off time between retries
Defining a Dockerfile
The Dockerfile definition for Config Consumer Service is almost identical to the one we used for Config Service. The only difference is the name of the JAR that’s copied in and executed.
Wrapping Up
If you’ve made it this far, you’ll have created the Config Service and the Config Consumer Service. The next step is defining the Kubernetes resources required to run our services in a cluster. We’ll look at the Kubernetes manifest in detail, in part two.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK