Continuous delivery of Microservices with XebiaLabs — a.k.a DevOps as Code

Deepu K Sasidharan
XebiaLabs
Published in
17 min readApr 2, 2019

--

Are you on the DevOps bandwagon yet? If not it's a great time to get on board. You would have heard the terminology at least once in your career and if you are wondering what it really means then let me tell you, it's all about CI/CD (continuous integration or continuous delivery) of your application, at least that is what matters the most, everything else is just noise.

Are you a developer who is annoyed with all the different tools and processes that are needed to set up your CI/CD setup, struggling with GUI tools and drowning in custom scripts and endless Jenkins Jobs? Then this post is for you. Are you a DevOps engineer who is more comfortable with a declarative approach to DevOps? Then this post is also for you. Are you an enterprise developer/architect looking for a right CI/CD setup that complies to your organization's compliance standards and security requirements? Then this post is for you as well.

Proper Continuous Integration and delivery setup can do great wonders for your application and your team, especially when you are doing complex microservice architectures. Manual deployments and the delivery cycle just can't cut it anymore. Imagine having a Kubernetes cluster with hundreds of microservices (or even a handful for that matter) and having to rollback them manually when one of the dependent services fail.

If you have been following my blog you would have noticed that I’m writing a lot about Microservices, Kubernetes, and JHipster and since I work for a great DevOps company, it's time that I combine all these into a blog post focusing on DevOps for microservices. So, today we will create a full-fledged microservice stack using JHipster and then deploy it to Google Kubernetes Engine. The Google Cloud Platform (GCP) infrastructure will be provisioned using Terraform. All this will be orchestrated using the XebiaLabs DevOps Platform and we will scaffold the provisioning scripts, deployment manifest, and release manifest using XebiaLabs Command line tool (XL CLI). We will also be using Jenkins as our CI tool.

Why XebiaLabs

XebiaLabs is a leading provider of enterprise DevOps software. The XebiaLabs DevOps Platform includes XL Deploy for deployment automation and XL Release for release orchestration, along with few other products focusing on DevOps intelligence and analytics. You can learn more about specific products and offerings here.

So why XebiaLabs? XebiaLabs has one of the most sophisticated release orchestration and deployment automation tools that I have seen, I’m not saying it because I work for XebiaLabs, I’m saying it as a developer who has some exposure to DevOps tools and who has done his fair share of DevOps. The XebiaLabs DevOps Platform is really powerful, especially when it comes to complex enterprise applications with a lot of processes, compliance, and security requirements. It may not be groundbreaking for small applications but you really start seeing the benefit as soon as you start adding complex steps into your CI/CD process. On top of that, it provides useful features like reporting, a nice and interactive UI and so on.

Now for the techies (like me), who would like even their wedding invitation probably to be in code, XebiaLabs also provides something we call DevOps as Code, a comprehensive YAML-based schema for defining deployments and release process as declarative code, and a nice CLI along with starter templates a.k.a blueprints. We shall see more about this later.

For this demo, we will be using both XL Release and XL Deploy along with the DevOps as Code features.

Why JHipster

If you have been following my blog then JHipster needs no introduction. JHipster is the leading rapid application development platform for the JVM ecosystem. It can help you to create monolith and microservice web applications with Spring Boot and Angular/React/VueJS in minutes. You can learn more about JHipster here. JHipster also provides a nice DSL to define our applications, entities, and deployment options. Check out my previous post about creating microservices with JHipster for more details.

We will be using JHipster to scaffold our microservice applications.

Why Terraform

HashiCorp Terraform enables you to safely and predictably create, change, and improve infrastructure. It is an open source tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.

Terraform offers what is commonly known as Infrastructure-as-Code. It is a powerful tool with a nice CLI to provision and teardown infrastructure in a variety of cloud and native environments. It's a truly cross-platform provisioning solution and best of its breed.

We will use Terraform to provision our Google Cloud infrastructure.

XebiaLabs DevOps as Code

Now that the introductions are done let us take a closer look at the XebiaLabs DevOps as Code.

The DevOps as Code offering is made of three main components — Blueprints, XL YAML files, and XL CLI.

Blueprints are templates written using our in-house scaffolding mechanism built on top of Go templating language. We use blueprints to provide starter templates for the DevOps as Code feature. Blueprints are quite similar to JHipster as it helps to scaffold configurations for provisioning and DevOps so that you can focus on getting work done rather than writing boilerplate code.

The XL YAML files provide a comprehensive schema to define release pipelines for XL Release and deployment configurations for XL Deploy. It can be used to completely replace the UI based configurations of both the products. This lets us iterate faster and have a more natural way of defining our DevOps pipeline right from our favorite IDE/Editor

Finally, the XL CLI helps to put everything together, soit can be used to run blueprints and to apply the XL YAML files to the respective products and more.

Prerequisites

Before we start you would need to set up a few tools in order to proceed.

First, you would need to install JHipster if you haven't already. Follow the official instructions here to set it up.

Second, let us install XL CLI using the official instructions here. Use the 8.6.1 version.

Creating the microservice stack using JHipster

Now let us create our microservice applications. We will be using JDL (JHipster Domain Language) to model our applications.

In one of my previous posts, I showcased how to create a full stack microservice architecture using JHipster and JDL. Read the post here if you want to learn more details about it. For this exercise, we will use the same application.

Let us take a quick look at the application architecture

Microservice application architecture

Let us recap the steps required.

Create a JDL file, let’s say app.jdl, and copy the below content into it.

application {
config {
baseName store,
applicationType gateway,
packageName com.jhipster.demo.store,
serviceDiscoveryType eureka,
authenticationType jwt,
prodDatabaseType mysql,
cacheProvider hazelcast,
buildTool gradle,
clientFramework react,
useSass true,
testFrameworks [protractor]
}
entities *
}
application {
config {
baseName invoice,
applicationType microservice,
packageName com.jhipster.demo.invoice,
serviceDiscoveryType eureka,
authenticationType jwt,
prodDatabaseType mysql,
buildTool gradle,
serverPort 8081,
skipUserManagement true
}
entities Invoice, Shipment
}
application {
config {
baseName notification,
applicationType microservice,
packageName com.jhipster.demo.notification,
serviceDiscoveryType eureka,
authenticationType jwt,
databaseType mongodb,
prodDatabaseType mongodb,
devDatabaseType mongodb,
cacheProvider no,
enableHibernateCache false,
buildTool gradle,
serverPort 8082,
skipUserManagement true
}
entities Notification
}
/* Entities for Store Gateway *//** Product sold by the Online store */
entity Product {
name String required
description String
price BigDecimal required min(0)
size Size required
image ImageBlob
}
enum Size {
S, M, L, XL, XXL
}
entity ProductCategory {
name String required
description String
}
entity Customer {
firstName String required
lastName String required
gender Gender required
email String required pattern(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)
phone String required
addressLine1 String required
addressLine2 String
city String required
country String required
}
enum Gender {
MALE, FEMALE, OTHER
}
entity ProductOrder {
placedDate Instant required
status OrderStatus required
code String required
invoiceId Long
}
enum OrderStatus {
COMPLETED, PENDING, CANCELLED
}
entity OrderItem {
quantity Integer required min(0)
totalPrice BigDecimal required min(0)
status OrderItemStatus required
}
enum OrderItemStatus {
AVAILABLE, OUT_OF_STOCK, BACK_ORDER
}
relationship OneToOne {
Customer{user(login) required} to User
}
relationship ManyToOne {
OrderItem{product(name) required} to Product
}
relationship OneToMany {
Customer{order} to ProductOrder{customer(email) required},
ProductOrder{orderItem} to OrderItem{order(code) required} ,
ProductCategory{product} to Product{productCategory(name)}
}
service Product, ProductCategory, Customer, ProductOrder, OrderItem with serviceClass
paginate Product, Customer, ProductOrder, OrderItem with pagination
/* Entities for Invoice microservice */
entity Invoice {
code String required
date Instant required
details String
status InvoiceStatus required
paymentMethod PaymentMethod required
paymentDate Instant required
paymentAmount BigDecimal required
}
enum InvoiceStatus {
PAID, ISSUED, CANCELLED
}
entity Shipment {
trackingCode String
date Instant required
details String
}
enum PaymentMethod {
CREDIT_CARD, CASH_ON_DELIVERY, PAYPAL
}
relationship OneToMany {
Invoice{shipment} to Shipment{invoice(code) required}
}
service Invoice, Shipment with serviceClass
paginate Invoice, Shipment with pagination
microservice Invoice, Shipment with invoice
/* Entities for notification microservice */entity Notification {
date Instant required
details String
sentDate Instant required
format NotificationType required
userId Long required
productId Long required
}
enum NotificationType {
EMAIL, SMS, PARCEL
}
microservice Notification with notification

Now let us also add the Kubernetes deployment description to the JDL so we can generate Kubernetes manifests for all our applications as well. Make sure to use your own dockerRepositoryName

deployment {
deploymentType kubernetes
appsFolders [store, invoice, notification]
dockerRepositoryName "deepu105"
kubernetesNamespace xl-demo
}

Now create a directory called ecommerce in your preferred location in the file system and navigate into it. Save the above JDL as app.jdl in the directory. Run the JHipster import-jdl command. It could take a few minutes, especially the NPM install step.

$ mkdir ecommerce && cd ecommerce
$ jhipster import-jdl app.jdl --skip-git

Once the JHipster process is complete, you will see that we have our store gateway, invoice service and notification service created and ready for us. We also have the Kubernetes manifests created alongside.

Microservice folder structure

The process until this is explained in more detail in my previous post here and you can deploy the application locally using Docker. If you haven’t done that before I strongly suggest that step so that you get an idea of the application and you also can make sure it works locally on your machine.

Now that our application is ready, let us set up a Jenkins CI build for the applications. In the spirit of true microservice, we will add a Jenkins job to each of the services.

Navigate into each of the store, invoice and notification folders and run the jhipster ci-cd command and follow the instructions. Choose the answers below for all the services. Use your own docker login name for “Organization Name for the Docker registry”.

$ cd store$ jhipster ci-cd

🚀 Welcome to the JHipster CI/CD Sub-Generator 🚀
? What CI/CD pipeline do you want to generate? Jenkins pipeline
? Would you like to perform the build in a Docker container ? No
? Would you like to send build status to GitLab ? No
? What tasks/integrations do you want to include ? Build and publish a *Docker* image
? *Docker*: what is the URL of the Docker registry ? https://registry.hub.docker.com
? *Docker*: what is the Jenkins Credentials ID for the Docker registry ? docker-login
? *Docker*: what is the Organization Name for the Docker registry ? deepu105
create Jenkinsfile
create src/main/docker/jenkins.yml
create src/main/resources/idea.gdsl
create src/main/docker/docker-registry.yml
INFO! Congratulations, JHipster execution is complete!

JHipster expects each project to live in its own Git repository, but for this demo, we will follow a monorepo approach, hence we need to adjust the generated Jenkinsfile to work with this. The change is simple, in each of the generated Jenkinsfile, we need to do a cd command before any command execution and adjust paths in copy commands. For example, below is the modified Jenkinsfile for store service with changes highlighted. Some unrelated code is truncated for brevity.

...node {
...
stage('clean') {
sh "cd store && chmod +x gradlew"
sh "cd store && ./gradlew clean --no-daemon"
}
stage('npm install') {
sh "cd store && ./gradlew npm_install ..."
}
stage('backend tests') {
try {
sh "cd store && ./gradlew test ..."
} ...
}
stage('frontend tests') {
try {
sh "cd store && ./gradlew npm_run_test-ci ..."
} ...
}
stage('packaging') {
sh "cd store && ./gradlew bootWar ..."
...
}
def dockerImage
stage('build docker') {
sh "cp -R store/src/main/docker store/build/"
sh "cp store/build/libs/*.war store/build/docker/"
dockerImage = docker.build('docker-login/store', 'store/build/docker')
}
...
}

Creating the CI/CD pipeline with XebiaLabs Blueprints

Now that our application is ready, let us set up a CI/CD pipeline for the applications. We will use the XebiaLabs blueprint feature to scaffold this. At the root of our applications, that is the ecommerce directory, run the following command

$ xl blueprint -b gcp/microservice-ecommerce

This will prompt you with a bunch of questions. Each answer as highlighted below - for most of them you can use the default values provided.

? What is the name of the application? e-commerce-ms
? What is the GCP project ID? e-commerce-devops-playground
? What is the username for Kubernetes cluster? admin
? What is the password for Kubernetes cluster? (minimum 16 characters) *************************
? Select the GCP region: europe-west1
? Do you want to provision a new GCP GKE cluster? Yes
? What is the name of the cluster? e-commerce-ms
? What is the name of the namespace? This should be same as the one used in Kubernetes files. xl-demo
? Do you want to enable CI/CD integration with Jenkins? Yes
? XL Deploy URL for XL Release (This will only be used in the XL Release template) http://xl-deploy:4516
? Confirm to generate blueprint files? Yes

Note: If you have an existing Google Project ID you can use that and select a GCP region that is best suited for you. If you do not have a project yet create one through the Google Cloud GUI or using the command below.

$ gcloud projects create e-commerce-devops-playground \
— organization [YOUR_ORG_ID] \
— set-as-default
$gcloud beta billing projects link e-commerce-devops-playground \
— billing-account [YOUR_BILLING_ACCOUNT_ID]

The value of YOUR_ORG_ID and YOUR_BILLING_ACCOUNT_ID can be found by running below commands

$ gcloud organizations list
$ gcloud beta billing accounts list

This will generate the XebiaLabs YAML files under the xebialabs folder and Terraform provisioning scripts under a terraform folder and a docker-compose file under the docker folder. It will also create a xebialabs.yaml file at the root for the convenience of execution.

Folder structure with DevOps as Code

DevOps as Code

Let's have a quick look at the XebiaLabs YAML files generated.

xld-infra-env.yaml: This defines the GCP infrastructure and environment to be used by XL Deploy to provision the terraform infrastructure and to deploy our application

xld-kubernetes-*-app.yaml: These files define the XL Deploy deployment packages for each of our microservice applications.

xld-terraform-apps.yaml: This file defines the XL Deploy deployment packages for the Terraform provisioning scripts that creates our Kubernetes cluster and the namespace

xlr-pipeline-ci-cd.yaml: This file defines the entire provisioning and release orchestration pipeline for XL Release. You could also use separate XL Release pipelines for provisioning and release if you like. XL Release will skip the provisioning steps if the infrastructure doesn't have any changes.

xlr-pipeline-destroy.yaml: This file defines a pipeline to de-provision and un-deploy everything. This is kind of a cleanup pipeline.

Infrastructure as code using Terraform

We also generated some Terraform scripts to provision our infrastructure:

gke: This module defines the GKE cluster.

vpc: This module defines the VPC, Subnets, and Firewall rules for the cluster.

main.tf: The main module ties everything together.

backend.tf: The backend module defines a Google Cloud Storage backend to store the Terraform state.

Deploying to Google Cloud Platform using XL Release CI/CD pipeline

Now, let's start our CI/CD journey.

Google Cloud Environment preparation

Before we jump in we need to set up a Google Cloud project. Detailed steps to do this are described in the Prerequisites section of xebialabs/USAGE.md which is generated along with the blueprint in the previous step.

Make sure you have the gcloud CLI setup and authenticated and then refer the xebialabs/USAGE.md file to set up the Google Cloud Project, a service account, and the back-end storage to store the tfstate file in Cloud Storage.

You would have downloaded an account.json file from this step, copy that into the terraform folder that was generated when you executed the blueprint in the previous step.

Setting up XL Release, XL Deploy, and Jenkins with Docker

We would also need to run an instance of XL Release, XL Deploy, Jenkins and Terraform, let us use the preconfigured docker-compose file generated by the blueprint to quickly run these tools.

Navigate to the docker folder generated by the blueprint

$ cd ecommerce/docker

Before we start the containers we need to specify a few environment variables for our Jenkins setup as below

$ export GITHUB_USER=<your GitHub user name>
$ export DOCKER_USER=<your Docker user name>
$ export DOCKER_PASS=<your Docker password>

We also need to ensure that Jenkins can build our applications. In the provided docker-compose setup there are preconfigured Jenkins jobs that will build the application. It looks for a GitHub repository https://github.com/${GITHUB_USER}/e-commerce-microservice.git with a gke-blueprint branch to check out the application. So please create the e-commerce-microservice repository under your GitHub user and commit everything we generated so far and push it to that repository. If you can’t use that name then update the generated docker/jenkins/jenkins.yaml file to reflect the new repository/branch name you want to use. Follow the below steps to init and push to the Git repository.

$ cd ecommerce
$ git init
$ git remote add upstream https://github.com/$GITHUB_USER/e-commerce-microservice.git
$ git add --all
$ git commit -am "your message here"
$ git checkout -b gke-blueprint
$ git push upstream gke-blueprint

Now we can start the Docker containers by running the below command.

$ docker-compose up -d

This will spin up all our containers in daemon mode so that we don’t accidentally kill them by ctrl+c

Visit the following URLs to ensure all the services are running correctly, you should be able to see the UI for the services. It could take a few minutes for all services to be up and running.

XL Release: localhost:5516

XL Deploy: localhost:4516

Jenkins: localhost:8080

Note: The username and password is “admin for all the above services. You can change them in the docker-compose files if you like.

Applying the XebiaLabs DevOps as Code YAML

Now that everything is ready, let’s use the generated XL YAML files. Apply them using the below command from the project root.

$ xl apply -f xebialabs.yaml

This will apply all the XL Release and XL Deploy configurations and will print out information regarding what was done.

XL Deploy deployments

Let us take a look at what was created in XL Deploy. Log in and visit the explorer tab in XL Deploy. Expand the entries in Applications, Environments, and Infrastructure and play around to see how the Terraform and Kubernetes deployments are mapped. Visit the XL Deploy documentation to understand the concepts and the GUI.

XL Deploy configuration items in explorer GUI

XL Release pipeline

Let us take a look at what was created in XL Release. Log in and visit the design tab in XL Release and click on the “e-commerce-ms” folder (The name will be different if you used a different name during blueprint generation). Now click on “e-commerce-ms-ci-cd” from the list to see the pipeline template. Visit the XL Release documentation to understand the concepts and the GUI.

XL Release pipeline template

Running the Release pipeline

Let us go ahead and run our release. In the XL Release pipeline template that we saw earlier, click on the “new release” button. Fill in a name for the release and click “create”. In the resulting pipeline click “start release” and then “start”.

Running release from XL Release pipeline

The release will start provisioning a GKE cluster first and then will build our applications using Jenkins which will build and push docker images to docker registry. XL Release will then orchestrate the Kubernetes deployment to the cluster that we created to do a smoke test and present the URL for you to verify. You can click on each task to see more details and to see log outputs. The release should take approximately 15 to 20 minutes when everything goes well.

Release with the final confirmation step
Confirmation step

Microservice Gateway

When you click on the URL presented after the release you should see the dapper looking guy below greeting you. Explore application and play with different services. You can mark the final task of the release as complete once you verify everything. This will complete the release.

Microservice gateway application

Google Cloud Console

Let us take a look at the Google Cloud Console to see our GKE cluster. Let us look at the node and workloads respectively.

Google Kubernetes Engine nodes
Google Kubernetes Engine workloads

We can see that all the services are deployed and are running successfully.

Teardown and de-provisioning with XL Release

The blueprint also generated a handy teardown template so that we can undeploy and deprovision everything we created. You can visit the design tab in XL Release and click on the “e-commerce-ms” folder. Now click on “e-commerce-ms-destroy” from the list to see the pipeline template.

XL Release template for teardown

This template will undeploy the applications with Kubernetes and will deprovision the GKE infrastructure with Terraform. To run this, click on the “new release” button. Fill in a name for the release and click “create”. In the resulting pipeline click “start release” and then “start”.

Completed release for teardown

Conclusion

Microservices are not easy, DevOps for microservices makes it even harder and simple orchestration tools fall short when it comes to orchestrating complex microservice deployment pipelines. Of course, we can write our own orchestration scripts using any script runner like Jenkins, but it doesn't scale when it comes to bigger teams and especially enterprises. This is where the XebiaLabs DevOps Platform shines as it provides all those annoying bits and pieces like process, quality and security assurances, which is not the fun part of DevOps. As I highlighted earlier, if you have simple apps then this may not be for you. But if you are in a process-oriented team which has to care about security, quality, and all the other boring stuff that big companies and enterprises care about, then you might need something like this.

You can learn all about XebiaLabs on the official website here and the product docs here.

JHipster provides a great platform for microservice development. To learn more about JHipster and Full stack development, check out my book “Full Stack Development with JHipster” on Amazon and Packt.

If you like JHipster don’t forget to give it a star on Github.

If you like this article, please leave some claps (Did you know that you can clap multiple times in Medium? 😁) I hope to write more about microservices and DevOps in the near future.

You can follow me on Twitter and LinkedIn.

My other related posts:

  1. Create full Microservice stack using JHipster Domain Language under 30 minutes
  2. Deploying JHipster Microservices on Azure Kubernetes Service (AKS)
  3. How to set up JHipster microservices with Istio service mesh on Kubernetes

--

--

Deepu K Sasidharan
XebiaLabs

Developer Advocate @Adyen, @java_hipster co-lead, nerd, robotics, and astronomy enthusiast. http://deepu.tech https://dev.to/deepu105