Service Foundry
Young Gyu Kim <credemol@gmail.com>

Service Foundry for Backend - Building Scalable and Resilient Cloud-Native Applications

considerations for microservice

Introduction

Service Foundry for Backend is a comprehensive solution that provides a suite of tools and services for building and managing cloud-native applications. It is designed to help developers and architects create scalable, resilient, and secure backend services by leveraging modern technologies and industry best practices.

Microservices developed using Service Foundry for Backend are expected to adhere to key cloud-native architecture principles, including:

Core Cloud-Native Principles

  • Containerization: Each microservice is packaged as a container image, enabling independent deployment and scaling.

  • Centralized Configuration: Configuration settings are managed centrally, allowing updates without requiring redeployment.

  • Service Discovery: Microservices can dynamically locate and communicate with each other through service discovery mechanisms.

  • Load Balancing: Traffic is intelligently distributed across multiple instances to ensure high availability and optimal performance.

  • Centralized Logging: Logs from all microservices are aggregated in a central repository for efficient monitoring and troubleshooting.

  • Distributed Tracing: End-to-end tracing provides visibility into requests flowing through multiple microservices, helping identify performance bottlenecks and failures.

  • Metrics Collection: Key performance indicators (KPIs) are collected and monitored to assess the health and efficiency of microservices.

  • Security: Robust authentication, authorization, and encryption mechanisms ensure secure communication and protect sensitive data.

  • API Documentation: APIs are documented using OpenAPI specifications, making it easy for other services and developers to integrate.

  • Scalability: Microservices can scale horizontally, adapting to increased traffic and workload demands without downtime.

Observability Enablement

Service Foundry for Backend seamlessly integrates with Service Foundry for Observability, providing a complete observability solution for microservices. This integration enables:

  • Real-time monitoring of microservice performance and health

  • Comprehensive logging for centralized visibility and debugging

  • Distributed tracing to analyze request flows across microservices

For more details on observability enablement, refer to:

Runtime Environment of Service Foundry for Backend

The Service Foundry for Backend provides a fully integrated runtime environment designed to support scalable, secure, and observable microservices. This environment consists of several key components that ensure smooth operation, dynamic configuration, and seamless service discovery.

backend foundry runtime
Figure 1. Runtime Environment

The runtime environment provided by 'Service Foundry for Backend' consists of the following components:

  • Observability Enablement: A comprehensive suite of tools for monitoring, logging, and distributed tracing, ensuring the health, performance, and reliability of microservices.

  • Security Server: A centralized authentication and authorization service that manages user identities, access control policies, and security tokens, ensuring secure communication between microservices.

  • API Gateway: A single entry point for all incoming requests, responsible for routing, load balancing, authentication, and authorization across microservices.

  • Spring Config Watcher: A configuration management service that continuously monitors changes in centralized configurations and dynamically updates microservices without requiring redeployment.

  • Service Discovery: A service registry that enables microservices to dynamically locate and communicate with each other, ensuring seamless interaction and scalability.

  • Kubernetes Gateway and HttpRoute: A high-performance load balancer that efficiently distributes incoming HTTP requests across multiple microservice instances to ensure high availability and fault tolerance.

This runtime environment provides a fully automated and scalable foundation for deploying and managing cloud-native microservices. Would you like to include a diagram or additional details on any specific component?

Getting Started with Service Foundry for Backend

In this section, we will create a new Spring Boot project using the Service Foundry for Backend template. This template provides a well-structured project setup with all the necessary components and configurations required to build microservices following cloud-native architecture principles.

As part of the initialization process, the following three projects will be generated: * {domain-name}-auth-server – A centralized authentication and authorization server managing user identities, access control policies, and security tokens for microservices. * {domain-name}-gateway – A centralized API gateway responsible for routing, load balancing, authentication, and authorization across microservices. * {domain-name}-resource-server-example – A sample microservice exposing REST APIs to manage resources.

Project Setup Procedure

Following the same steps as we did for Observability Enablement, we will now set up a new Spring Boot project using the Service Foundry for Backend template.

Steps to Create and Deploy Microservices

  1. Create a directory (used as the project domain) for Spring Boot projects.

  2. Initialize the project using the Service Foundry for Backend template.

  3. Generate the project structure with pre-configured components.

  4. Build the microservices locally.

  5. Deploy the microservices to Kubernetes.

Project Initialization

We use Yeoman as the scaffolding tool to generate the project structure. Yeoman takes user inputs and creates the required components based on predefined templates.

Step 1: Create the Project Directory
$ mkdir raincity
Step 2: Initialize the Project Using the Template
$ yo nsa2:backend-foundry init
User inputs during initialization
? Project Domain:  raincity
? Base Domain:  nsalexamy.com  <-- your domain name
? Maven Group ID:  com.company.raincity
? Base Java Package:  com.company.raincity
? Azure Container Registry Name:  my-acr <- your ACR name for storing Docker images

This command generates the nsa2-backend-foundry-build-config.yaml file, which stores the configuration settings provided during initialization.

For example, if you specify the values above, the following Kubernetes Gateway routes will be created: * raincity-auth-server.nsalexamy.com * raincity-gateway.nsalexamy.com

These routes allow access to the microservices using their respective domain names.

Configuration Files

The initialization process also generates: * configmap.properties – Stores non-sensitive configuration settings. * secret.properties – Stores sensitive credentials and secrets.

Both files are mounted as ConfigMaps and Secrets in the Kubernetes cluster and used to create: * raincity-configmap – Shared across all microservices for dynamic configuration. * raincity-secret – Stores sensitive data securely.

These configurations will be discussed further in the section on **Dynamic Configuration using Spring.

Generating the Project Structure

After reviewing the configurations, we generate the complete project structure using:

$ yo nsa2:backend-foundry generate

To verify the generated files:

# to see what is generated
$ tree -L 1
Generated Project Structure
.
├── build-backend-foundry.sh
├── configmap.properties
├── deploy-backend-foundry.sh
├── helm-charts
├── install-postgresql.sh
├── k8s
├── nsa2-backend-foundry-build-config.yaml
├── raincity-auth-server
├── raincity-gateway
├── raincity-resource-server-example
├── secret.properties
├── undeploy-backend-foundry.sh
└── uninstall-postgresql.sh

Key Components

  • build-backend-foundry.sh – Script to build microservices and Docker images.

  • deploy-backend-foundry.sh – Script to deploy microservices to Kubernetes.

  • undeploy-backend-foundry.sh – Script to remove deployed microservices from Kubernetes.

  • uninstall-postgresql.sh – Script to uninstall PostgreSQL and clean up Persistent Volumes (PVC/PV).

  • raincity-auth-server/ – Spring Boot project for the centralized authentication server.

  • raincity-gateway/ – Spring Boot project for the configuration and API gateway.

  • raincity-resource-server-example/ – Sample Spring Boot microservice.

Build Microservices locally

To build the microservices and generate the necessary Kubernetes Helm charts, run:

$ ./build-backend-foundry.sh

Deploying Microservices to Kubernetes

I deploy the microservices to Kubernetes using the following command:

$ ./deploy-backend-foundry.sh

Deployment process

Deployment Process * The script builds Docker images and pushes them to the Azure Container Registry (ACR). * It then deploys the microservices to the Kubernetes cluster using Helm charts. * The deployment process may take a few minutes until all resources are up and running. * All Kubernetes resources will be created under the raincity namespace.

Load Balancing with Kubernetes Gateway and HttpRoute

Kubernetes Gateway, combined with HttpRoute, provides seamless load balancing and routing for microservices. During deployment, all microservices are automatically registered with the Kubernetes Gateway using HttpRoute resources, ensuring efficient traffic distribution and high availability.

For more details, refer to:

load balancing
Figure 2. Load Balancing

Routing and Load Balancing

During deployment, the HttpRoute resource defines routing rules for handling incoming HTTP requests. You can list the registered routes using:

$ kubectl -n raincity get httproutes
Example Output
# output
NAME                                     HOSTNAMES                                            AGE
raincity-auth-server-route               ["raincity-auth-server.nsalexamy.com"]               12h
raincity-gateway-route                   ["raincity-gateway.nsalexamy.com"]                   12h
raincity-resource-server-example-route   ["raincity-resource-server-example.nsalexamy.com"]   12h

DNS Configuration for Domain Resolution

To enable domain name resolution, add the following entries to your DNS server or /etc/hosts file:

your-k8s-gateway-ip 		raincity-gateway.nsalexamy.com
your-k8s-gateway-ip 		raincity-auth-server.nsalexamy.com

You can retrieve the Kubernetes Gateway IP address using the following command:

$ fqdn$(kubectl get gateway your-k8s-ateway -n your-gateway-namespace -o jsonpath='{.status.addresses[0].value}')

$ dig $fqdn +short

Alternatively, use:

$ nslookup $fqdn | grep Address | tail -n 1 | awk '{print $2}'

Accessing Microservices via Domain Names

Once DNS resolution is configured, you can access microservices using their domain names:

$ curl http://raincity-gateway.nsalexamy.com/actuator/health | jq

Example Response

# output
{
  "status": "UP",
  "groups": [
    "liveness",
    "readiness"
  ]
}

With Kubernetes Gateway and HttpRoute, microservices are efficiently load-balanced, ensuring scalability, high availability, and seamless traffic routing

OAuth 2.0 Authentication and Authorization

Authentication and authorization in Service Foundry for Backend are managed by a centralized authentication and authorization server, raincity-auth-server. This server is responsible for issuing OAuth 2.0 security tokens to authenticate and authorize requests from both microservices and clients.

How it Works

  1. To access a microservice, clients must first obtain an access token from the raincity-auth-server.

  2. The access token is then included in the HTTP request headers to authenticate and authorize the request.

  3. If a client attempts to access a protected microservice (e.g., raincity-resource-server-example) without a valid token, they are automatically redirected to the authentication server to log in.

  4. Once authenticated, an access token is issued, and the client can use it to access microservices securely.

Example Access Flow

Test URL(Accessing a Protected Resource)

http://raincity-gateway.nsalexamy.com/resource-server/hello

Redirected Login Page

http://raincity-auth-server.nsalexamy.com/login
security login
Figure 3. Redirected URL

Test Credentials

Use the following credentials to authenticate and obtain an access token:

  • Username: raincityadmin

  • Password: password

resource server example 1
Figure 4. Resource Server Example

Backend For Frontend (BFF) Pattern

The Backend For Frontend (BFF) pattern is used in the microservices architecture to improve security and streamline authentication.

  • Unlike traditional approaches, the JWT token is not visible in browser developer tools (e.g., Chrome DevTools).

  • Instead, the token is securely stored in the session store of the raincity-gateway microservice, ensuring better security and session management.

By leveraging OAuth 2.0 with the BFF pattern, the Service Foundry for Backend ensures secure, scalable, and seamless authentication across microservices

Distributed Tracing with Jaeger

All microservices in Service Foundry for Backend are instrumented with Jaeger for distributed tracing. Jaeger, an open-source tracing system, helps monitor, troubleshoot, and analyze the performance of microservices-based applications.

Since we have already invoked the microservices in the previous section, their traces can be viewed in the Jaeger UI.

Analyzing Traces in Jaeger

  • Use raincity-gateway as the service name to search for traces.

  • View detailed information, including spans, logs, and tags, to analyze request flows and identify bottlenecks.

Example: Searching for Traces

jaeger 1
Figure 5. Searching Traces in Jaeger

Example: Viewing Request Flows

Clicking on a trace reveals in-depth details about the request lifecycle, including spans, logs, and execution timelines.

jaeger 2
Figure 6. Viewing Traces, Spans, and Logs

Centralized Logging with OpenSearch

For centralized log management, all microservices are integrated with OpenSearch, an open-source search and analytics engine designed for collecting, storing, and analyzing logs at scale.

Viewing Logs in OpenSearch

To inspect logs for the raincity-gateway microservice, enter the following query in the DQL search bar within the OpenSearch Dashboard:

serviceName:raincity-gateway
opensearch dashboards
Figure 7. Logs Dashboard in OpenSearch

With Jaeger for tracing and OpenSearch for centralized logging, the Service Foundry for Backend provides deep observability, enabling efficient monitoring, troubleshooting, and performance optimization for cloud-native applications.

Dynamic Configuration with Spring Config Watcher

Spring Config Watcher enables dynamic configuration updates for microservices without requiring a restart. It continuously monitors changes in Kubernetes ConfigMaps and Secrets and automatically reloads updated configurations in the microservices.

For detailed information, see:

Configuration Structure

Each microservice has its own ConfigMap and Secret, in addition to shared configurations:

Shared Across All Microservices

  • ConfigMap: raincity-configmap

  • Secret: raincity-secret

Specific to Each Microservice (Example: raincity-gateway)

  • ConfigMap: raincity-gateway

  • Secret: raincity-gateway

Mounted Configuration Directories

Configuration settings are mounted as files in microservices under the following directories:

  • /etc/configs/common/ - common ConfigMap

  • /etc/secrets/common/ - common Secret

  • /etc/configs/app/ - microservice-specific ConfigMap

  • /etc/secrets/app/ - microservice-specific Secret

config watcher
Figure 8. Spring Config Watcher

Each microservice loads configuration settings from ConfigMaps and Secrets via bootstrap.yaml:

bootstrap.yaml of raincity-gateway
# omitted for brevity

---
spring.config.activate.on-profile: kubernetes

spring:
  config:
    import:
      - configtree:/etc/configs/app/
      - configtree:/etc/configs/common/
      - configtree:/etc/secrets/app/
      - configtree:/etc/secrets/common/

spring.cloud.kubernetes:

  reload:
    enabled: true
    monitoring-config-maps: true
    monitoring-secrets: true
    mode: event

  config:
    enabled: true
    namespace: raincity
    sources:
      - name:  raincity-configmap
      - name:  raincity-gateway

  secrets:
    enabled: true
    namespace: raincity
    sources:
      - name:  raincity-secret
      - name:  raincity-gateway

This configuration enables automatic reload of ConfigMap and Secret changes in the raincity namespace.

Testing Dynamic Configuration Updates

ConfigWatcherController

A REST controller (ConfigWatcherController) is provided to test dynamic configuration reloading.

@RefreshScope  // Enables dynamic configuration reload
@RestController
@RequestMapping("/config-watcher")
@Slf4j
public class ConfigWatcherController {
    @Value("${COMMON_PROPERTY:unknown}")
    private String commonProperty;

    @Value("${APP_PROPERTY:unknown}")
    private String appProperty;

    private final Environment env;

    public ConfigWatcherController(Environment env) {
        this.env = env;
    }

    @GetMapping("/")
    public Map<String, String> getConfig() {
        var config = Map.of("commonProperty", commonProperty,
                "appProperty", appProperty
        );

        log.info("config: {}", config);
        return config;
    }

    @GetMapping("/{name}")
    public String getProperty(@PathVariable String name) {
        var value = env.getProperty(name, "unknown");

        log.info("property - name: {}, value: {}", name, value);
        return value;
    }
}

The @RefreshScope annotation ensures that the microservice automatically reloads configurations when they change.

Test the Dynamic Configuration Reload

In the raincity-configmap ConfigMap, there is a property named 'COMMON_PROPERTY' with the value 'raincity'. And each microservice’s ConfigMap, there is a property named 'APP_PROPERTY' with the value 'raincity-gateway'.

I used a web browser to access the ConfigWatcherController endpoint, because it is easy to handle JWT tokens in the browser.

Test URL:

http://raincity-gateway.nsalexamy.com/config-watcher/COMMON_PROPERTY

Before updating, fetch the COMMON_PROPERTY value from raincity-configmap:

$ kubectl -n raincity get configmap raincity-configmap -o yaml | yq .data.COMMON_PROPERTY

raincity

If calling the ConfigWatcherController endpoint, the response will be 'raincity'.

config watcher test 1
Figure 9. COMMON_PROPERTY before updating

I updated the COMMON_PROPERTY value in the raincity-configmap to 'raincity-updated'.

Retrieve the updated value:
$ kubectl -n raincity get configmap raincity-configmap -o yaml | yq .data.COMMON_PROPERTY

raincity-updated

If calling the ConfigWatcherController endpoint again, now the response will be 'raincity-updated'.

config watcher test 2
Figure 10. Updated COMMON_PROPERTY

Consolidated API Documentation with Swagger

Spring Gateway provides centralized API documentation using Swagger, an open-source tool for generating interactive documentation for RESTful APIs. By aggregating API specifications from multiple microservices, the Gateway allows developers to explore and test endpoints from a single interface.

springdoc in application.yaml
springdoc:

  swagger-ui:
    # omitted for brevity
    enabled: true
    path: /swagger-ui.html
    config-url: /v3/api-docs/swagger-config
    urls:
      - name: gateway-service
        url: /v3/api-docs
      - name: resource-server
        url: /resource-server/v3/api-docs

Key Configuration Points:

  • enabled: true → Activates Swagger UI

  • path: /swagger-ui.html → Defines the Swagger UI endpoint

  • urls → Lists API documentation for multiple microservices

    • /v3/api-docs → Gateway API documentation

    • /resource-server/v3/api-docs → Resource Server API documentation

With this setup, each microservice’s API documentation is accessible through the Gateway, making it easier to explore and test APIs in a centralized way.

Why Consolidate API Documentation in the Gateway?

  • Unified Access → Since resource servers are typically not directly accessible, hosting their API documentation in the Gateway provides a single entry point.

  • Improved Security → Ensures API documentation follows the same security policies as API access through the Gateway.

  • Simplified Development & Testing → Developers can explore APIs from multiple microservices in one place.

Swagger UI Examples

Gateway API Documentation

swagger gateway service
Figure 11. Swagger UI - Gateway

Resource Server API Documentation

swagger resource server
Figure 12. Swagger UI - Resource Server

This consolidated approach enhances API discoverability, usability, and security, making it an essential feature for microservice-based architectures.

Scalability

Scaling Microservices in Kubernetes

Microservices can be scaled dynamically using Kubernetes’ built-in scaling mechanisms, either through kubectl commands or the Kubernetes Dashboard. This ensures high availability and efficient resource utilization.

Manual Scaling with kubectl

To scale the Resource Server Example to three replicas, run the following command:

$ kubectl -n raincity scale deployment raincity-resource-server-example --replicas=3

Verifying the Number of Replicas

To confirm that the deployment has been scaled, use:

kubectl -n raincity get pods -l app.kubernetes.io/name=raincity-resource-server-example
NAME                                                READY   STATUS    RESTARTS   AGE
raincity-resource-server-example-75596f4d6b-jv6qr   1/1     Running   0          1m
raincity-resource-server-example-75596f4d6b-sxv9p   1/1     Running   0          15h
raincity-resource-server-example-75596f4d6b-z6m6x   1/1     Running   0          1m

Auto-Scaling with Kubernetes Horizontal Pod Autoscaler (HPA)

For automated scaling, Kubernetes provides the Horizontal Pod Autoscaler (HPA), which dynamically adjusts the number of replicas based on CPU utilization. HPA helps optimize resource usage and ensures microservices scale automatically under varying workloads.

We will cover HPA in detail in a separate article.

Service Discovery with Kubernetes Service

In Service Foundry for Backend, Kubernetes Services enable service discovery, providing stable network endpoints for microservices to communicate seamlessly. Instead of relying on dynamic pod IPs, microservices can interact using host-based service names, ensuring reliable connectivity regardless of scaling.

Within the Kubernetes cluster, microservices communicate using their service names. For example, the raincity-gateway microservice can interact with raincity-resource-server-example simply by referencing its service name, raincity-resource-server-example, irrespective of the number of running replicas.

Metrics Collection with Prometheus

Prometheus is an open-source monitoring and alerting toolkit designed to collect, store, and query metrics from microservices. It offers a powerful query language that allows you to analyze and visualize these metrics effectively.

For example, to retrieve the recent CPU utilization ratio of the raincity-resource-server-example microservice, you can use the following query:

Example Query
jvm_cpu_recent_utilization_ratio{job='raincity-resource-server-example'}

You can execute this query within the Prometheus UI to visualize and analyze the collected data.

prometheus
Figure 13. Prometheus UI

Metrics Collection with Grafana

Grafana is an open-source platform for analytics and monitoring, designed to visualize and analyze metrics from microservices. It offers a wide range of visualization options for creating dashboards and setting up alerts.

Similar to Prometheus, Grafana allows us to collect and display metrics. You can create custom dashboards to visualize key metrics and set up alerts to notify you of any issues or anomalies.

grafana
Figure 14. Grafana Dashboard

Helm Charts for Microservices

Helm charts are used to package, version, and deploy microservices on Kubernetes. Helm, a package manager for Kubernetes, streamlines the management of applications running within a Kubernetes cluster. All microservices created with ‘Service Foundry for Backend’ are packaged as Helm charts, enabling smooth and efficient deployment.

To list the Helm charts installed in the ‘raincity’ namespace, use the following command:

Helm Chart list installed in the 'raincity' namespace
$ helm -n raincity list --short
raincity-auth-server
raincity-gateway
raincity-resource-server-example

Each Helm chart contains all the necessary Kubernetes resources for deploying the microservices. The charts manage the entire deployment lifecycle, simplifying the process of getting microservices up and running on Kubernetes.

Here’s an example of the Helm chart structure for the ‘raincity-gateway’ microservice:

$ tree raincity-gateway/src/main/k8s
raincity-gateway/src/main/k8s
├── Dockerfile
└── helm-chart
    └── raincity-gateway
        ├── Chart.yaml
        ├── templates
        │   ├── NOTES.txt
        │   ├── _helpers.tpl
        │   ├── config-watcher-configmap-kubernetes.yaml
        │   ├── config-watcher-configmap.yaml
        │   ├── config-watcher-secret-kubernetes.yaml
        │   ├── config-watcher-secret.yaml
        │   ├── deployment.yaml
        │   ├── hpa.yaml
        │   ├── httproute.yaml
        │   ├── ingress.yaml
        │   ├── role.yaml
        │   ├── rolebinding.yaml
        │   ├── service.yaml
        │   ├── serviceaccount.yaml
        │   ├── servicemonitor.yaml
        │   └── tests
        │       └── test-connection.yaml
        └── values.yaml

This structure defines the various resources for deploying and managing the raincity-gateway service in a Kubernetes environment.

Clean up Kubernetes Resources

To remove all deployed microservices and associated resources from the Kubernetes cluster, execute the following script:

$ ./undeploy-backend-foundry.sh

This script uninstalls all Helm releases and deletes ConfigMaps, Secrets, and other related resources:

configmap "raincity-configmap" deleted
configmap "raincity-configmap-kubernetes" deleted
secret "raincity-secret" deleted
secret "raincity-secret-kubernetes" deleted
configmap "raincity-observability-configmap" deleted
Uninstalling Spring K8s Config Watcher
service "spring-cloud-kubernetes-configuration-watcher" deleted
serviceaccount "spring-cloud-kubernetes-configuration-watcher" deleted
rolebinding.rbac.authorization.k8s.io "spring-cloud-kubernetes-configuration-watcher:view" deleted
role.rbac.authorization.k8s.io "namespace-reader" deleted
deployment.apps "spring-cloud-kubernetes-configuration-watcher-deployment" deleted
Uninstalling previous auth-server
release "raincity-auth-server" uninstalled
Uninstalling previous gateway
release "raincity-gateway" uninstalled
Uninstalling previous raincity-resource-server-example
release "raincity-resource-server-example" uninstalled

The script removes all microservices and their dependencies; however, it does not delete Persistent Volume Claims (PVCs) and Persistent Volumes (PVs) used by PostgreSQL. To remove PostgreSQL resources, run:

$ uninstall-postgresql.sh

Additionally, the raincity namespace must be deleted manually. Before proceeding, ensure that you have reviewed and backed up any necessary data. To delete the namespace, use the following command:

$ kubectl delete namespace raincity

Conclusion

In conclusion, Service Foundry for Backend is a comprehensive, cloud-native solution designed to streamline the development, deployment, and management of microservices. By leveraging Kubernetes and a set of powerful tools, Service Foundry enables organizations to build scalable, resilient, and highly observable applications. Through integrations with essential technologies such as OpenTelemetry, Jaeger, Prometheus, Grafana, and Spring Config Watcher, Service Foundry offers a robust infrastructure for real-time monitoring, centralized logging, and dynamic configuration management.

With the flexibility of Helm charts and Kubernetes’ native scaling features, developers can ensure the seamless operation of microservices in production environments, while the centralized OAuth 2.0 authentication and API Gateway provide enhanced security and streamlined API management. By adopting Service Foundry for Backend, organizations can accelerate development cycles, improve operational efficiency, and provide a consistent and reliable experience for users.

In an ever-evolving technological landscape, Service Foundry for Backend represents a solid foundation for teams looking to implement cloud-native applications that are scalable, secure, and efficient.

All my LinkedIn articles are available at My LinkedIn Article Library.

Internal links: docs/service-foundry/03.backend-foundry/index.adoc