When you want to deploy a containerized Spring Boot application to Kubernetes and provide environment specific configuration during deployment time (e.g. with a Build Pipeline), this is very easy: you just have to set operating system environment variables in the context where your container process runs and these environment variables have to match your Spring Boot configuration properties in order to provide the configuration you want to use (https://docs.spring.io/spring-boot/docs/3.1.1/reference/htmlsingle/#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables).
But this approach does not work with Web Applications which for example are build with Angular and run in a Browser. For example, you can use the official Nginx Docker Image to deploy an Angular app to Kubernetes and you could set operating system environment variables which then can be accessed by the Nginx process in the container . So far, no problem. But how do you want to access such environment variables in a Browser, which runs on a completely different machine than Nginx? The only way to communicate from the Browser with Nginx is via HTTP requests. Furthermore, the default Angular environment.ts configuration can only be used to provide configuration at build time, which means that this configuration will be part of the Docker Image.
Therefore, you have to "inject" a file into your Nginx container, which holds the environment specific configuration. Such a file can then be loaded from the Browser via a HTTP GET request and processed by your Angular app. It makes much sense, that such a configuration file contains JSON data, because this is easy to process with Angular. One important aspect when you are building "cloud native" apps is that you want to build your Docker Image only once and then use it for all environments (e.g. local, dev, prod). To make this work, you have to separate configuration from code, which should automatically be the case when you have a clean and modular design. To separate configuration from code is also one factor in the Twelve-Factor App (https://12factor.net/config). One generic way to "inject" a file into a container at Deployment time is by using Kubernetes Volume Mounts in combination with a Kubernetes ConfigMap (if you're using Kubernetes; see https://kubernetes.io/docs/concepts/storage/volumes/#configmap).
The recommended way to load the configuration into your Angular app is by providing an APP_INITIALIZER which will load the configuration when your Angular App bootstraps (https://angular.io/api/core/APP_INITIALIZER). Because of the asynchronous nature of Angular and the underlying Observer Pattern, the configuration is only accessible through an Observable (https://angular.io/guide/observables). This means that when you make a GET request with the Angular HttpClient, you will get an Observable that holds the configuration as a response. As a consequence, you have to transform this Observable in all areas of your code where you need to access your configuration. Let's say we have a RemoteConfigService which will load the configuration when the APP_INITIALIZER is invoked. The RemoteConfigService can now provide access to the configuration by declaring a getter named getAppConfig with the following signature:
getAppConfig: Observable<AppConfig>
When you want to access a certain property of your JSON configuration, you can use the pipe/switchMap pattern (from rxjs) to transform the Observable. The following method demonstrates how to do that:
return this.remoteConfigService.getAppConfig().pipe(
switchMap(config => of(config.myPropertyName)));
}
No comments:
Post a Comment