Architecture as Code with SolSA
SolSA makes it possible to specify the software architecture of Kubernetes-managed cloud applications as JavaScript or TypeScript programs.
Overview
Kubernetes is becoming the de facto standard for managing applications in the cloud. Thanks to operators, Kubernetes can be extended to manage virtually any kind of application resources: not only containers and microservices, but also virtual machines, cloud functions and events, managed cloud services, policies, meshes, etc. Each resource can be declared and configured with a few lines of YAML. However, while YAML is a fine language to perform some basic configuration, SolSA is designed to enable application developers to reason about applications at a higher-level of abstraction using familiar languages and development tools.
The SolSA library for Node.js —a shorthand for Solution Service Architecture — makes it possible to specify the architecture of cloud applications as programs. SolSA enables developers to configure Kubernetes-managed resources by writing JavaScript or TypeScript code instead of YAML. SolSA automatically translates the developer written code to the required lower-level YAML. SolSA integrates with IDEs such as Visual Studio Code to provide online validation, code completion, and documentation of the various resources and configuration parameters.
Getting Started
The solsa
CLI is included in the Tools Image and is pre-configured as a plugin to the ArgoCD instance installed in the tools
namespace of your cluster. No further setup is needed for basic usage of SolSA. To enable full IDE support, you will need to install the solsa
package using npm in the same manner in which you would install any npm package you want to use in your IDE. Some typical installation options are discussed in the SolSA setup instructions.
Usage Scenarios
SolSA can be applied in any scenario where it is desirable to use a higher-level of abstraction to programmatically generate YAML to be applied to a Kubernetes or OpenShift cluster. The use of SolSA is particularly compelling to:
- Specify the architecture of complex applications composed of multiple Kubernetes resources. SolSA augments the base Kubernetes resources with additional abstractions that simplify the specification of common patterns and reduce the amount of code written.
- Enable full IDE-support for your software architecture, including validation, code assist, and integrated documentation of Kubernetes resources and operators.
- Allow a single architecture specification to be programmatically specialized to different contexts (eg. dev vs. test vs. prod).
Specification of Kubernetes Resources
Deploying just a single microservice that is accessible outside of a Kubernetes cluster requires specifying three related Kubernetes resources: a Deployment
, a Service
, and an Ingress
. SolSA simplifies this task by reducing the amount of repetitive code a developer needs to write and maintain.
To illustrate how to use SolSA to specify the architecture of a simple cloud native application, we use the Bookinfo sample application from the Istio project. Bookinfo consists of four containerized microservices: an externally exposed productpage service that is implemented using three internal backend microservices.
The tabs below contain two variants of the SolSA specification of Bookinfo’s architecture and the (identical) Kubernetes YAML that is generated from either one of these specifications by the solsa
CLI.
A very common Kubernetes pattern is the combination of a Deployment
and a Service
to implement a single logical microservice. SolSA’s ContainerizedService
provides a higher level abstraction for this pattern that allows the developer to specify the essential elements while eliding virtually all of the Kubernetes-specific details. As shown below, simple microservices can be specified very concisely. By providing additional arguments to the ContainerizedService
constructor it is possible to control many aspects of the generated YAML. Specifying that the productpage
service should be exposed outside the cluster is declared by invoking getIngress
passing the desired virtual host name bookinfo
.
let solsa = require('solsa')let details = new solsa.ContainerizedService({ name: 'details', image: 'istio/examples-bookinfo-details-v1:1.15.0', port: 9080 })let ratings = new solsa.ContainerizedService({ name: 'ratings', image: 'istio/examples-bookinfo-ratings-v1:1.15.0', port: 9080 })let reviews = new solsa.ContainerizedService({ name: 'reviews', image: 'istio/examples-bookinfo-reviews-v1:1.15.0', port: 9080 })let productpage = new solsa.ContainerizedService({ name: 'productpage', image: 'istio/examples-bookinfo-productpage-v1:1.15.0', port: 9080 })productpage.env = {DETAILS_HOSTNAME: details.name,RATINGS_HOSTNAME: ratings.name,
The SolSA library includes fully integrated support for all Kubernetes resource types. The (much larger) code block below shows how Bookinfo would be specified in SolSA directly using the Kubernetes native concept of Deployment
. Working at this lower level of abstraction requires that the developer be more familiar with Kubernetes concepts, but enables full control over all aspects of the generated YAML. Notice that even when working directly with Deployment
, the developer is still able to elide the details of the Service
and Ingress
resources by using SolSA’s getService
and getIngress
methods which derive all the necessary information the underlying Deployment
. Thus even when working at a Kubernetes-native level of abstraction, SolSA can eliminate some of the repetitive and error prone coding present when working purely at the YAML level.
let solsa = require('solsa')let details = new solsa.apps.v1.Deployment({metadata: { name: 'details' },spec: {selector: { matchLabels: { 'solsa.ibm.com/pod': 'details' } },replicas: 1,template: {spec: {
Shown below is the output from solsa yaml
when applied to either of the two Bookinfo specifications.
apiVersion: v1kind: Servicemetadata:name: detailsspec:ports:- port: 9080targetPort: 9080selector:
Using SolSA with Kubernetes Operators
The SolSA library includes JavaScript/TypeScript bindings for every Kubernetes Operator available on OperatorHub.io. These enables full IDE-support for defining applications that utilize operators to manage the life-cycles of some portions of their resources.
As an example, the tabs below show the SolSA specification and the generated YAML of a simple language translation application in which a microservice defined using SolSA’s ContainerzedService
abstraction is combined with an instance of the Watson Translator service on the IBM Public Cloud. The IBM Cloud Operator is used to instantiate the Watson Translator Service instance and make the credentials for accessing it available to the dependent microservice via a Kubernetes secret.
const path = require('path')const solsa = require('solsa')function translator ({ name, language }) {let watson = new solsa.LanguageTranslator({ name: 'watson-translator-for-' + name })let translator = new solsa.ContainerizedService({ name, image: 'solsa-translator', build: path.join(__dirname, 'solsa-translator'), port: 8080 })translator.env = { LANGUAGE: { value: language }, WATSON_URL: watson.getSecret('url'), WATSON_APIKEY: watson.getSecret('apikey') }let ingress = translator.getIngress()
apiVersion: ibmcloud.ibm.com/v1alpha1kind: Servicemetadata:name: watson-translator-for-my-translatorspec:plan: liteserviceClass: language-translator---apiVersion: v1
Using SolSA with ArgoCD
The usage of ArgoCD is described in more detail in the continuous delivery section of this guide. A SolSA-specified application can be used in exactly the same workflows as an application specified by a Helm chart or Kustomize tree. The only additional step is to inform ArgoCD that it should use its SolSA plugin to generate the YAML for the application. In the instructions below, we will continue using the Bookinfo sample from the solsa-examples git repo. The main SolSA file for this application is instance.js
Log into ArgoCD
Click
New Application
and provide the following values:application name
- dev-bookinfoproject
- defaultsync-policy
-Automatic with pruning
repository url
- https://github.com/IBM/solsa-examples.gitrevision
- HEADpath
- examples/bookinfodestination cluster
- https://kubernetes.default.svcdestination namespace
- dev
Click the
EDIT AS YAML
button and add the plugin stanza shown below to thesource:
block
source:plugin:name: solsaenv:- name: SOLSA_APP_MAINvalue: instance.js
Finally deploy Bookinfo by clicking the Create button.
ArgoCD Applications can also be specified without using the ArgoCD UI by defining an argoproj.io.Application
Custom Resource. In this style of management you
create a bookinfoApp.yaml
file containing the YAML shown below and then either check it into a git repo that ArgoCD is monitoring
or directly apply it to your cluster via kubectl apply -f bookinfoApp.yaml -n tools
. Within a few seconds, the Bookinfo application should be deployed to your dev
namespace.
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata:name: bookinfospec:project: defaultsource:repoURL: 'https://github.com/IBM/solsa-examples.git'path: examples/bookinfo
More Information
Additional information on SolSA, including a tutorial and sample applications, is available from the SolSA open source project.