The purpose of this demo is to showcase how you can use Component CRD
and a Kubernetes operator
deployed on OpenShift to help you to install your Microservices Spring Boot
application, instantiate a database using a Kubernetes Service Catalog and inject the required information to the different Microservices to let a Spring Boot application to access/consume a service (http endpoint, database, ...).
The demo's project consists, as depicted within the following diagram, of two Spring Boot applications and a PostgreSQL Database.
The application to be deployed can be described using a Fluent DSL syntax as :
(from:componentA).(to:componentB).(to:serviceA)
where the ComponentA
and ComponentB
correspond respectively to a Spring Boot application fruit-client-sb
and fruit-backend-sb
.
The relation from -> to
indicates that we will reference
the ComponentA
with the ComponentB
using a Link
.
The link
's purpose is to inject as Env var(s)
the information required to by example configure the HTTP client
of the ComponentA
to access the
ComponentB
which exposes the logic of the backend's system as CRUD REST operations.
To let the ComponentB
to access the database, we will also setup a link
in order to pass using the Secret
of the service instance created the parameters which are needed to configure a Spring Boot Datasource's bean.
The deployment or installation of the application in a namespace will consist in to create the resources on the platform using some Component
yaml resource files defined according to the
Component API spec.
When they will be created, then the Component operator
which is a Kubernetes controller will execute different operations to create :
- For the
component-runtime
a development's pod running asupervisord's daemon
able to start/stop the application [1] and where we can push theuber jar
file compiled locally, - A Service using the OpenShift Automation Broker and the Kubernetes Service Catalog [2],
EnvVar
section for the development's pod [3].
oc login https://195.201.87.126:8443 --token=TOKEN_PROVIDED_BY_SNOWDROP_TEAM
oc project <user_project>
- Minishift (>= v1.26.1) with Service Catalog feature enabled
- Launch Minishift VM
# if you don't have a minishift VM, start as follows
minishift addons enable xpaas
minishift addons enable admin-user
minishift start
minishift openshift component add service-catalog
minishift openshift component add automation-service-broker
- Login to MiniShift using
admin
's user
oc login https://$(minishift ip):8443 -u admin -p admin
- Deploy the resources within the namespace
component-operator
oc new-project component-operator
oc create -f resources/sa.yaml
oc create -f resources/cluster-rbac.yaml
oc create -f resources/user-rbac.yaml
oc create -f resources/crd.yaml
oc create -f resources/operator.yaml
- Login to the OpenShift's cluster using your favorite user
oc login https://ip_address_or_hostname_fqdn>:8443 -u <user> -p <password
- Git clone the project locally to play with a Spring Boot composite application
git clone https://github.com/snowdrop/component-operator-demo.git && cd component-operator-demo
- Create a new project
my-spring-app
oc new-project my-spring-app
- Build the
Client
and theBackend
usingmvn tool
to generate their respective Spring Boot uber jar file
cd fruit-client
mvn package
cd ..
cd fruit-backend-sb
mvn package
cd ..
- Deploy for each microservice, their Component CRs on the cluster and wait till they will be processed by the controller to create the corresponding kubernetes resources such as DeploymentConfig, Pod, Service, Route, ...
oc apply -f fruit-backend-sb/component.yml
oc apply -f fruit-client-sb/component.yml
- Verify that we have 2 components installed
oc get components
NAME RUNTIME VERSION SERVICE TYPE CONSUMED BY AGE
fruit-backend-sb spring-boot 1.5.16 34s
fruit-client spring-boot 1.5.16 32s
- Create the
PostgreSQL database
using thedb-service.yml
Component CR
oc apply -f service/database.yml
WARNING As this process is performed asynchrounously and is managed by the Kubernetes Service Catalog controller in combination with the Service Broker, then this process can take time !
Remark Use the following command to check the status of the instance which, at the end of the installation process, should be equal to ready
oc get serviceinstance/postgresql-db
NAME CLASS PLAN STATUS AGE
postgresql-db ClusterServiceClass/dh-postgresql-apb dev Ready 3m
- Control as we did before that we have 3 components installed: 2 Spring Boot runtimes and 1 service
oc get components
NAME RUNTIME VERSION SERVICE TYPE CONSUMED BY AGE
fruit-backend-sb spring-boot 1.5.16 2m
fruit-client spring-boot 1.5.16 2m
fruit-database postgresql-db 6s
- Inject as ENV variables the parameters of the database to let Spring Boot to create a Datasource's bean to connect to the database using the secret created
oc apply -f fruit-backend-sb/env-secret-service.yml
- Inject the endpoint's address of the
fruit backend
application as an ENV Var. This ENV Var will be used as parameter by the Spring Boot application to configure the HTTP client to access the backend
oc apply -f fruit-client-sb/env-backend-endpoint.yml
- As we have finished to compose our application
from Spring Boot Http Client
-> toSpring Boot REST Backend
-> toPostgreSQL
database, we will now copy the uber jar files, and next start theclient
,backend
Spring Boot applications. Execute the following commands :
./scripts/push_start.sh fruit-client sb
./scripts/push_start.sh fruit-backend sb
- Edit the pom.xml file of the
fruit-client-sb
andfruit-backend-sb
maven modules and add theap4k
gavs (responsible to scan the annotated class and to generate the yaml resource files)
<!-- To generate CRD -->
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>ap4k-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>component-annotations</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Only needed for the backend component -->
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>servicecatalog-annotations</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>ap4k-spring-boot</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
-
Edit the
Application
class to specify the Component's definition, service or relations (= links).The
@CompositeApplication
is used by theap4k
lib to generate aComponent CRD
resource, containing the definition of the runtime, the name of the component, if a route is needed.A
@Link
represents additional information or metadata to be injected within theComnponent
(aka DeploymentConfig resource) in order to configure the application to access anothercomponent
using its service address, a service deployed from the k8s catalog.client
@CompositeApplication( name = "fruit-client-sb", exposeService = true, links = @Link( name = "Env var to be injected within the target component -> fruit-backend", targetcomponentname = "fruit-client-sb", kind = "Env", ref = "", envVars = @Env( name = "OPENSHIFT_ENDPOINT_BACKEND", value = "http://fruit-backend-sb:8080/api/fruits" ) ))
To express the creation of a service on the Cloud platform, we are using the
@ServiceCatalog
annotation where we define theService's class
, its plan and parameters.Like for the Client's component, we will also define a
@Link
annotation to inject from the secret created during the creation of the service, the parameters that the application will use to configure, in this example, theDataSource
's object able to call thePostgreSQL
instance.Backend
@CompositeApplication( name = "fruit-backend-sb", exposeService = true, envVars = @Env( name = "SPRING_PROFILES_ACTIVE", value = "openshift-catalog"), links = @Link( name = "Secret to be injected as EnvVar using Service's secret", targetcomponentname = "fruit-backend-sb", kind = "Secret", ref = "postgresql-db")) @ServiceCatalog( instances = @ServiceCatalogInstance( name = "postgresql-db", serviceClass = "dh-postgresql-apb", servicePlan = "dev", bindingSecret = "postgresql-db", parameters = { @Parameter(key = "postgresql_user", value = "luke"), @Parameter(key = "postgresql_password", value = "secret"), @Parameter(key = "postgresql_database", value = "my_data"), @Parameter(key = "postgresql_version", value = "9.6") } ) )
-
Compile the modules at the root of the project
mvn clean install
- Deploy the generated
component.yaml
resource files
oc apply -f fruit-client-sb/target/classes/META-INF/ap4k/component.yml
oc apply -f fruit-backend-sb/target/classes/META-INF/ap4k/component.yml
- Push the code and launch the java application
./scripts/push_start.sh fruit-client sb
./scripts/push_start.sh fruit-backend sb
- Call the HTTP Endpoint exposed by the
Spring Boot Fruit Client
in order to fetch data from the database
route_address=$(oc get route/fruit-client-sb -o jsonpath='{.spec.host}')
curl http://$route_address/api/client
or
using httpie client
http -s solarized http://$route_address/api/client
http -s solarized http://$route_address/api/client/1
http -s solarized http://$route_address/api/client/2
http -s solarized http://$route_address/api/client/3
-
Decorate the Component with the following values in order to specify the git info needed to perform a Build, like the name of the component to be selected to switch from the dev loop to the publish loop
annotations: app.openshift.io/git-uri: https://github.com/snowdrop/component-operator-demo.git app.openshift.io/git-ref: master app.openshift.io/git-dir: fruit-backend-sb app.openshift.io/artifact-copy-args: "*.jar" app.openshift.io/runtime-image: "fruit-backend-sb" app.openshift.io/component-name: "fruit-backend-sb" app.openshift.io/java-app-jar: "fruit-backend-sb-0.0.1-SNAPSHOT.jar"
Remark : When the maven project does not contain multi modules, then replace the name of the folder / module with
.
using the annotationapp.openshift.io/git-dir
-
Patch the component when it has been deployed to switch fromm
inner
toouter
oc patch cp fruit-backend-sb -p '{"spec":{"deploymentMode":"outerloop"}}' --type=merge
- Build node project locally
cd fruit-client-nodejs
nvm use v10.1.0
npm audit fix
npm install -s --only=production
- Run locally
export OPENSHIFT_ENDPOINT_BACKEND=http://fruit-backend-sb.my-spring-app.195.201.87.126.nip.io/api/fruits
npm run -d start
- Deploy the node's component and link it to the Spring Boot fruit backend
oc apply -f fruit-client-nodejs/component.yml
oc apply -f fruit-client-nodejs/env-backend-endpoint.yml
- Push the code and start the nodejs application
./scripts/push_start.sh fruit-client nodejs
- Test it locally or remotely
# locally
http :8080/api/client
http :8080/api/client/1
#Remotely
route_address=$(oc get route/fruit-client-nodejs -o jsonpath='{.spec.host}')
curl http://$route_address/api/client
or
using httpie client
http http://$route_address/api/client
http http://$route_address/api/client/1
http http://$route_address/api/client/2
http http://$route_address/api/client/3
git clone [email protected]:snowdrop/scaffold-command.git && cd scaffold-command
go build -o scaffold cmd/scaffold.go
export PATH=$PATH:/Users/dabou/Code/snowdrop/scaffold-command
scaffold
? Create from template Yes
? Available templates rest
? Group Id me.snowdrop
? Artifact Id myproject
? Version 1.0.0-SNAPSHOT
? Package name me.snowdrop.myproject
? Spring Boot version 2.1.2.RELEASE
? Use supported version No
? Where should we create your new project ./fruit-demo
cd demo
add to the pom.xml file
<properties>
...
<ap4k.version>0.3.2</ap4k.version>
</properties>
and
<dependencies>
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>component-annotations</artifactId>
<version>${ap4k.version}</version>
</dependency>
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>kubernetes-annotations</artifactId>
<version>${ap4k.version}</version>
</dependency>
<dependency>
<groupId>io.ap4k</groupId>
<artifactId>ap4k-spring-boot</artifactId>
<version>${ap4k.version}</version>
</dependency>
<!-- spring Boot -->
oc delete cp/fruit-backend-sb
oc delete cp/fruit-database
oc delete cp/fruit-database-config
oc delete cp/fruit-client
oc delete cp/fruit-endpoint
oc delete -f resources/sa.yaml -n component-operator
oc delete -f resources/cluster-rbac.yaml
oc delete -f resources/crd.yaml
oc delete -f resources/operator.yaml -n component-operator
brew install [email protected]
echo 'export PATH="/usr/local/opt/[email protected]/bin:$PATH"' >> ~/.zshrc
pg_ctl -D /usr/local/var/[email protected] start
createuser -s postgres
psql -U postgres -h localhost
createdb -h localhost -p 5432 -U postgres springbootdb