Chapter 4. Creating the Environment
4.1. Overview
This reference architecture can be deployed in either a production or a trial environment. In both cases, it is assumed that ocp-master1 refers to one (or the only) OpenShift master host and that the environment includes two other OpenShift schedulable hosts with the host names of ocp-node1 and ocp-node2. Production environments would have at least 3 master hosts to provide High Availability (HA) resource management, and presumably a higher number of working nodes.
It is further assumed that OpenShift Container Platform has been properly installed, and that a Linux user with sudo privileges has access to the host machines. This user can then set up an OpenShift user through its identity providers.
4.2. Project Download
Download the source code and related artifacts for this reference architecture application from its public github repository:
$ git clone https://github.com/RHsyseng/spring-boot-msa-ocp.git LambdaAirChange directory to the root of this project. It is assumed that from this point on, all instructions are executed from inside the LambdaAir directory.
$ cd LambdaAir4.4. OpenShift Configuration
Create an OpenShift user, optionally with the same name, to use for creating the project and deploying the application. Assuming the use of HTPasswd as the authentication provider:
$ sudo htpasswd -c /etc/origin/master/htpasswd ocpAdmin New password: PASSWORD Re-type new password: PASSWORD Adding password for user ocpAdmin
Grant OpenShift admin and cluster admin roles to this user, so it can create persistent volumes:
$ sudo oadm policy add-cluster-role-to-user admin ocpAdmin $ sudo oadm policy add-cluster-role-to-user cluster-admin ocpAdmin
At this point, the new OpenShift user can be used to sign in to the cluster through the master server:
$ oc login -u ocpAdmin -p PASSWORD --server=https://ocp-master1.xxx.example.com:8443
Login successful.Create a new project to deploy this reference architecture application:
$ oc new-project lambdaair --display-name="Lambda Air" --description="Spring Boot Microservices on Red Hat OpenShift Container Platform 3"
Now using project "lambdaair" on server "https://ocp-master1.xxx.example.com:8443".4.5. Zipkin Deployment
Zipkin uses MySQL database for storage, which in turn requires an OpenShift persistent volume to be created. Edit Zipkin/zipkin-mysql-pv.json and provide a valid NFS server and path, before proceeding. Once the file has been corrected, use it to create a persistent volume:
$ oc create -f Zipkin/zipkin-mysql-pv.json
persistentvolume "zipkin-mysql-data" createdValidate that the persistent volume is available:
$ oc get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
zipkin-mysql-data 1Gi RWO Recycle Available 1mOnce available, use the provided zipkin template to deploy both MySQL and Zipkin services:
$ oc new-app -f Zipkin/zipkin-mysql.yml
--> Deploying template "lambdaair/" for "Zipkin/zipkin-mysql.yml" to project lambdaair
---------
MySQL database service, with persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/README.md.
NOTE: Scaling to more than one replica is not supported. You must have persistent volumes available in your cluster to use this template.
The following service(s) have been created in your project: zipkin-mysql.
Username: zipkin
Password: TwnDiEpoMqOGiJNb
Database Name: zipkin
Connection URL: mysql://zipkin-mysql:3306/
For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/README.md.
* With parameters:
* Memory Limit=512Mi
* Namespace=openshift
* Database Service Name=zipkin-mysql
* MySQL Connection Username=zipkin
* MySQL Connection Password=TwnDiEpoMqOGiJNb # generated
* MySQL root user Password=YJmmYOO3BVyX77wL # generated
* MySQL Database Name=zipkin
* Volume Capacity=1Gi
* Version of MySQL Image=5.7
--> Creating resources ...
secret "zipkin-mysql" created
service "zipkin" created
service "zipkin-mysql" created
persistentvolumeclaim "zipkin-mysql" created
configmap "zipkin-mysql-cnf" created
configmap "zipkin-mysql-initdb" created
deploymentconfig "zipkin" created
deploymentconfig "zipkin-mysql" created
route "zipkin" created
--> Success
Run 'oc status' to view your app.The output above includes randomly generated passwords for the database that will be different each time. It is advisable to note down the passwords for your deployed database, in case it is later needed for troubleshooting.
You can use oc status to get a report, but for further details and to view the progress of the deployment, watch the pods as they get created and deployed:
$ watch oc get pods
Every 2.0s: oc get pods Fri Jul 21 02:04:15 2017
NAME READY STATUS RESTARTS AGE
zipkin-1-deploy 1/1 Running 0 2m
zipkin-1-sclgl 0/1 Running 0 2m
zipkin-mysql-1-deploy 1/1 Running 0 2m
zipkin-mysql-1-tv2v1 0/1 ContainerCreating 0 1mIt may take a few minutes for the deployment process to complete, at which point there should be two pods in the Running state:
$ oc get pods
NAME READY STATUS RESTARTS AGE
zipkin-1-k0dv6 1/1 Running 0 5m
zipkin-mysql-1-g44s7 1/1 Running 0 4mOnce the deployment is complete, you will be able to access the Zipkin console. Discover its address by querying the routes:
$ oc get routes
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
zipkin zipkin-lambdaair.ocp.xxx.example.com zipkin 9411 NoneUse the displayed URL to access the console from a browser and verify that it works correctly:
Figure 4.1. Zipkin Console

4.6. Service Deployment
To deploy a Spring Boot service, use Maven to build the project, with the fabric8:deploy target for the openshift profile to deploy the built image to OpenShift. For convenience, an aggregator pom file has been provided at the root of the project that delegates the same Maven build to all 6 configured modules:
$ mvn clean fabric8:deploy -Popenshift
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Lambda Air 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
...
...
[INFO] --- fabric8-maven-plugin:3.5.30:deploy (default-cli) @ aggregation ---
[WARNING] F8: No such generated manifest file /Users/bmozaffa/RedHatDrive/SysEng/Microservices/SpringBoot/SpringBootOCP/LambdaAir/target/classes/META-INF/fabric8/openshift.yml for this project so ignoring
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Lambda Air ......................................... SUCCESS [01:33 min]
[INFO] Lambda Air ......................................... SUCCESS [02:21 min]
[INFO] Lambda Air ......................................... SUCCESS [01:25 min]
[INFO] Lambda Air ......................................... SUCCESS [01:05 min]
[INFO] Lambda Air ......................................... SUCCESS [02:20 min]
[INFO] Lambda Air ......................................... SUCCESS [01:06 min]
[INFO] Lambda Air ......................................... SUCCESS [ 1.659 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 09:55 min
[INFO] Finished at: 2017-12-08T16:03:12-08:00
[INFO] Final Memory: 67M/661M
[INFO] ------------------------------------------------------------------------Once all services have been built and deployed, there should be a total of 8 running pods, including the 2 Zipkin pods from before, and a new pod for each of the 6 services:
$ oc get pods
NAME READY STATUS RESTARTS AGE
airports-1-72kng 1/1 Running 0 18m
airports-s2i-1-build 0/1 Completed 0 21m
flights-1-4xkfv 1/1 Running 0 15m
flights-s2i-1-build 0/1 Completed 0 16m
presentation-1-k2xlz 1/1 Running 0 10m
presentation-s2i-1-build 0/1 Completed 0 11m
sales-1-fqxjd 1/1 Running 0 7m
sales-s2i-1-build 0/1 Completed 0 8m
salesv2-1-s1wq0 1/1 Running 0 5m
salesv2-s2i-1-build 0/1 Completed 0 6m
zipkin-1-k0dv6 1/1 Running 0 1h
zipkin-mysql-1-g44s7 1/1 Running 0 1h
zuul-1-2jkj0 1/1 Running 0 1m
zuul-s2i-1-build 0/1 Completed 0 2m4.7. Flight Search
The presentation service also creates a route. Once again, list the routes in the OpenShift project:
$ oc get routes
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
presentation presentation-lambdaair.ocp.xxx.example.com presentation 8080 None
zipkin zipkin-lambdaair.ocp.xxx.example.com zipkin 9411 NoneUse the URL of the route to access the HTML application from a browser, and verify that it comes up:
Figure 4.2. Lambda Air Landing Page

Search for a flight by entering values for each of the four fields. The first search may take a bit longer, so wait a few seconds for the response:
Figure 4.3. Lambda Air Flight Search

4.8. External Configuration
The Presentation service configures Hystrix with a thread pool size of 20 in its application properties. Confirm this by searching the logs of the presentation pod after a flight search operation and verify that the batch size is the same:
$ oc logs presentation-1-k2xlz | grep batch
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 20 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 13 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 20 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 13 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 20 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 13 ticketsCreate a new application.yml file that assumes a higher number of Sales service pods relative to Presentation pods:
$ vi application.ymlEnter the following values:
hystrix:
threadpool:
SalesThreads:
coreSize: 30
maxQueueSize: 300
queueSizeRejectionThreshold: 300
Create a configmap using the oc utility based on this file:
$ oc create configmap presentation --from-file=application.yml
configmap "presentation" createdEdit the Presentation deployment config and mount this ConfigMap as /deployments/config, where it will automatically be part of the Spring Boot application classpath:
$ oc edit dc presentationAdd a new volume with an arbitrary name, such as config-volume, that references the previously created configmap. The volumes definition is a child of the template spec. Next, create a volume mount under the container to reference this volume and specify where it should be mounted. The final result is as follows, with the new lines highlighted:
...
resources: {}
securityContext:
privileged: false
terminationMessagePath: /dev/termination-log
volumeMounts:
- name: config-volume
mountPath: /deployments/config
volumes:
- name: config-volume
configMap:
name: presentation
dnsPolicy: ClusterFirst
restartPolicy: Always
...
Once the deployment config is modified and saved, OpenShift will deploy a new version of the service that will include the overriding properties. This change is persistent and pods created in the future with this new version of the deployment config will also mount the yaml file.
List the pods and note that a new pod is being created to reflect the change in the deployment config, which is the mounted file:
$ oc get pods
NAME READY STATUS RESTARTS AGE
airports-1-72kng 1/1 Running 0 18m
airports-s2i-1-build 0/1 Completed 0 21m
flights-1-4xkfv 1/1 Running 0 15m
flights-s2i-1-build 0/1 Completed 0 16m
presentation-1-k2xlz 1/1 Running 0 10m
presentation-2-deploy 0/1 ContainerCreating 0 3s
presentation-s2i-1-build 0/1 Completed 0 11m
sales-1-fqxjd 1/1 Running 0 7m
sales-s2i-1-build 0/1 Completed 0 8m
salesv2-1-s1wq0 1/1 Running 0 5m
salesv2-s2i-1-build 0/1 Completed 0 6m
zipkin-1-k0dv6 1/1 Running 0 1h
zipkin-mysql-1-g44s7 1/1 Running 0 1h
zuul-1-2jkj0 1/1 Running 0 1m
zuul-s2i-1-build 0/1 Completed 0 2mWait until the second version of the pod has started in the running state. The first version will be terminated and subsequently removed:
$ oc get pods
NAME READY STATUS RESTARTS AGE
...
presentation-2-pxx85 1/1 Running 0 5m
presentation-s2i-1-build 0/1 Completed 0 1h
...Once this has happened, use the browser to do one or several more flight searches. Then verify the updated thread pool size by searching the logs of the new presentation pod and verify the batch size:
$ oc logs presentation-2-pxx85 | grep batch
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 30 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 3 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 30 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 3 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 30 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 3 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 30 tickets
... c.r.r.o.b.l.p.s.API_GatewayController : Will price a batch of 3 ticketsNotice that with the mounted overriding properties, pricing happens in concurrent batches of 30 instead of 20 items now.
4.9. A/B Testing
Copy the groovy script provided in the Zuul project over to the shared storage for this service:
$ cp Zuul/misc/ABTestingFilterBean.groovy /mnt/zuul/volume/Create a persistent volume for the Zuul service. External groovy scripts placed in this location can provide dynamic routing.
$ oc create -f Zuul/misc/zuul-pv.json
persistentvolume "groovy" createdAlso create a persistent volume claim:
$ oc create -f Zuul/misc/zuul-pvc.json
persistentvolumeclaim "groovy-claim" createdVerify that the claim is bound to the persistent volume:
$ oc get pvc
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
groovy-claim Bound groovy 1Gi RWO 7s
zipkin-mysql Bound zipkin-mysql-data 1Gi RWO 2hAttach the persistent volume claim to the deployment config as a directory called groovy on the root of the filesystem:
$ oc volume dc/zuul --add --name=groovy --type=persistentVolumeClaim --claim-name=groovy-claim --mount-path=/groovy
deploymentconfig "zuul" updated
[bmozaffa@middleware-master LambdaAir]$ oc get pods
NAME READY STATUS RESTARTS AGE
airports-1-72kng 1/1 Running 0 1h
airports-s2i-1-build 0/1 Completed 0 1h
flights-1-4xkfv 1/1 Running 0 1h
flights-s2i-1-build 0/1 Completed 0 1h
presentation-2-pxx85 1/1 Running 0 32m
presentation-s2i-1-build 0/1 Completed 0 1h
sales-1-fqxjd 1/1 Running 0 1h
sales-s2i-1-build 0/1 Completed 0 1h
salesv2-1-s1wq0 1/1 Running 0 1h
salesv2-s2i-1-build 0/1 Completed 0 1h
zipkin-1-k0dv6 1/1 Running 0 2h
zipkin-mysql-1-g44s7 1/1 Running 0 2h
zuul-1-2jkj0 1/1 Running 0 1h
zuul-2-deploy 0/1 ContainerCreating 0 4s
zuul-s2i-1-build 0/1 Completed 0 1hOnce again, the change prompts a new deployment and terminates the original zuul pod, once the new version is started up and running.
Wait until the second version of the pod reaches the running state:
$ oc get pods | grep zuul
zuul-2-gz7hl 1/1 Running 0 7m
zuul-s2i-1-build 0/1 Completed 0 1hReturn to the browser and perform one or more flight searches. Then return to the OpenShift environment and look at the log for the zuul pod.
If the IP address received from your browser ends in an odd number, the groovy script filters pricing calls and sends them to version 2 of the sales service instead. This will be clear in the zuul log:
$ oc logs zuul-2-gz7hl
...
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.79
Running filter
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.79
Running filterIn this case, the logs from salesv2 will show tickets being priced with a modified algorithm:
$ oc logs salesv2-1-s1wq0
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 463 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 425 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 407 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 549 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 509 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 598 with lower hop discount
... c.r.r.o.b.l.sales.service.Controller : Priced ticket at 610 with lower hop discountIf that is not the case and your IP address ends in an even number, it will still be printed but the Running filter statement will not appear:
$ oc logs zuul-2-gz7hl
...
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.78
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.78
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.78
... groovy.ABTestingFilterBean : Caller IP address is 10.3.116.78In this case, you can change the filter criteria to send IP addresses with an even digit to the new version of pricing algorithm, instead of the odd ones:
$ vi /mnt/zuul/volume/ABTestingFilterBean.groovy
...
if( lastDigit % 2 == 0 )
{
//Even IP address will be filtered
true
}
else
{
//Odd IP address won’t be filtered
false
}
...
Deploy a new version of the zuul service to pick up the updated groovy script:
$ oc rollout latest zuul
deploymentconfig "zuul" rolled outOnce the new pod is running, do a flight search again and check the logs. The calls to pricing should go to the salesv2 service now, and logs should appear as previously described.

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.