A Java Microservice Architecture with CI/CD – it’s easier than you think!
A basic Java microservice architecture requires containerizing your Java application and NGINX. Those steps are the most challenging part. Below are simple steps to migrate your Java microservice to a Kubernetes cluster. While this is a super simple example, it will get you started down the right path to a microservice implementation.
Before you get started, it’s important to note that migrating your java application to a java microservices is not that big of a leap. While there are complexities in the process, it’s easier than you might think. Below is a basic roadmap for migrating to microservices using a standard J2EE application from a monolithic to a microservice architecture.
Containerizing Java Applications
To start, we containerize our Java application and end with auto-deploying to a Kubernetes microservice environment. In this example, we will use a standard containerized J2EE application, but you will get an idea of how to get started down the microservice path.
Migrating to java microservices roadmap
Migrating to Java microservices
Here are the steps involved in migrating to your Java microservices
Step 1: Containerize your Java Application and Runtime
Start your migration journey by first containerizing your Java Application (.jar, .war, or .ear). This step will involve building a container that includes the Java runtime and your application. When you do this, remember the following:
- The container should have one purpose (entry point).
- The container should be self-reliant.
Step 2: Learn NGINX
As you begin migrating to microservices, you will need a load balancer for your Kubernetes environment that handles external Ingress from your Cloud Load Balancer. NGINX is a good place to start. Take the time to install and learn NGINX native.
Step 3: Containerize NGINX
Now that you understand NGINX, containerize it. Take your NGINX setup and move it to a container.
Step 4: Build a Kubernetes Cluster
This step is when the fun starts. Your efforts in migrating your java app to Java microservices are nothing without Kubernetes to manage your containers. Learn Kubernetes by building your first Kubernetes Cluster. Start with Google and their free credits. The wizard will help you build your cluster. You will run a local install of the Google Command Line Client. Also, learn Helm.
Step 5: Understand Domains
With your Kubernetes cluster built and waiting for your java microservice, it’s time to think about how to break apart your Application. DeployHub supports a Domain-Driven Design for microservices to organize, catalog, publish and share microservices. It’s critical to define your domain structure early on in the process. Organizing your microservices and reusable components from the beginning will prevent you from suffering later from a disorganized implementation.
Step 6: Add a Binary Repo to your CD workflow.
Start managing your binaries in a repo for the container build step. Every build is stored with a version number and is critical for tracking history.
Step 7: Add a Docker (container) repository to your CI/CD
Start managing your binaries in a repo for the container build step. Every build is stored with a version number, critical for tracking history.
Step 8: Auto Deploy via CI/CD
Use DeployHub Team microservice dashboard to Auto Deploy. DeployHub will track your configurations and version for a single source of truth.
And that is about it. Don’t overthink the process of migrating to microservices. Take the time needed to complete each step. You will need to make minor changes to your CI/CD workflows to support new processes, but it is all very doable. From this example, you can begin building on your microservices strategy and launch your journey into modern architecture.
What we built
You can view our recent webinar on this topic at: https://webinars.devops.com/migrating-to-microservices-its-easier-than-you-think
Questions and Answers on the topic of Migrating a Java app to Java Microservices:
Is there a way to capture the application dependencies when migrating an app to a container for the first time?
The build is the best place to start. The compile and link process will show the dependencies needed, ie JDBC driver, C runtime. If you are a OpenMake Meister user, all this information is displayed in the impact analysis reports. Then, you will start with your Dockerbuild file doing a “yum install” of the packages needed to satisfy your app. A JDBC dependency indicates that you must do a “yum install” of the proper database runtime. If your app uses SSL, “yum install openssh” is needed. Again, the libraries consumed in the build will need to be resolved at execution time. Therefore the build will drive your OS-level package installs.
How does DeployHub integrate with a CD pipeline in DevOps Azure?
First, DeployHub hooks into the CI part of Azure. So when a build happens, DeployHub is notified of what was created and where it came from. The next level is at the application view. The Azure pipelines build images for independent microservices, but it doesn’t have a logical view of the application. DeployHub brings the logical view of the application with the configuration management and application version to component version dependency mapping to Azure.
Azure triggers DeployHub to deploy the logical application version when a deployment is needed. DeployHub looks across all pipeline artifacts deploying only the incremental changes out to Kubernetes.
Can you expand what you mean by DeployHub is a modern CONFIGURE and deploy solution? Does this refer to the microservices configuration only?
DeployHub is a ‘modern’ configure and deploy solution in two ways.
First, for monolithic applications that are being pushed via an agile practice, DeployHub can perform incremental updates of your monolithic with database modifications. DeployHub starts you thinking in a ‘component’ context where individual pieces are deployed in an incremental fashion that fits more with an agile practice that always deploys everything.
For example, it can support version jumping, moving production from Version 2 to Version 10, and only applying what has changed, including your application binaries (easy), environment variables (a bit harder), and database schema changes (much harder). Because of its back-end version control engine, it knows the differences. Think about versioning code and apply it to your software configuration.
Second, DeployHub includes a Domain Structure that allows you to catalog and publish your reusable components like microservices. It also tracks the relationships between ‘components’ (microservices) and their applications. And because it already has the built-in configuration version control, it can easily manage microservices that are independently deployed.
The DeployHub microservice catalog tool supports both a monolithic and microservice architecture based on an independently deployable component design.
With NGINX, is that a cluster or else on prod how to manage the single-point-of-failure issue?
You can run multiple replicas of NGINX in the pod to provide high availability and scale-ability for the Ingress.
To run RDBMS in a container, you need to persist the data external to the container. This setup allows only a single container to have read/write access to that mounted volume. Multiple containers access the volume will corrupt the database due to uncontrolled writes to the underlying data files.
Cloud providers have a managed database solution that your containers can talk to directly without running the RDBMS yourself. For on-perm, connecting to Oracle, DB2, Mysql via a network connect is the way to go.
How do we ensure different versions of a deployed application on a container? Is it through different versions of container images on Docker hub?
Docker containers are immutable, but the tag to the container can be changed. Use the LABEL command in the Dockerfile to embed information about the git commit, git repo, CI build into the container. This information will be immutable and can be queried post docker build no matter what the TAG is set to. As for tagging, we recommend using semantic version numbering v22.214.171.124 plus the git commit that triggered the build, for example, v126.96.36.199-gd4c633fc. This format gives you a quick way to get back the git changes and also has a “human-readable” version to reference.
The image tag is used at deploy time to determine if a new image needs to be brought down. You may rebuild base on a new commit but not change the semantic version. This version format will handle that scenario without the need to force a semantic version number change.
Dockerhub can handle multiple versions of a container you need to manage you TAGs. Do not edit TAGs but create new ones to move from version to version.
Would NGINX interface with the Kubernetes API server or directly with the Kube Pods during the reverse proxy?=
NGINX allows you to intercept the incoming URI and route them to a pod of containers. This routing is done using the NGINX reverse proxy. If you need to do fancier intercepting of the transaction a service mesh like Istio should be used since it can interrogate anything in the HTTP header and route based on that info.
How do we implement a messaging broker like Kafka or Rabbit MQ as part of a microservices-based application architecture?
The message broker would be installed into the cluster as a container/pod. There could be several different containers needed to run the broker. See https://github.com/GoogleCloudPlatform/click-to-deploy/tree/master/k8s/rabbitmq for the setup of Rabbit MQ in Kubernetes.
Once the broker is running, you will be able to reach it based on well-known names defined to the pods in the cluster. One trick is to connect to a container via “bash” in for one of the pods and run ‘set’ to see the well-known names being generated. Also, look at the /etc/hosts file for additional information.
How do you debug mistakes in changing APIs? Unexpected partial breakage in the Death Star network of dependencies.
The first step is to understand the relationships of your microservices and how they make up your application version. DeployHub can help you with this by providing a map of the relationships. The map will give you your starting point and a pathway to the API having the error.
From there, you start at the beginning of the path. Look at the log output to see if the data being passed along is what you expect. The API error may result from “bad” data upstream from where the error is. You can trace the log messages down to a container in the Pods.
Given the data and the container version, you should be able to resolve the issue. Be aware the problem may be the client-side sending bad data (no edit checks) to the back end, and the back end doesn’t handle it or throws an error.
You are describing containerizing a customer-facing application. Do you recommend applying the pattern internally to containerize the build environment itself?
Jenkins-X, Google CloudBuild, Tekton, CircleCI are containerized build systems. Use a containerize build system that already exists instead of building one yourself. Each one has its strengths based on the type of application you are building and what your pipeline looks like.
Most important to understand that when you manage hundreds of java microservices, you will need continuous delivery automation. Nothing should be done manually and get away from scripts.
What is the difference between DeployHub functions and Garden_io ?
DeployHub functions outside the cluster, enabling it to adapt to different types of Kubernetes providers used in the microservice pipeline. For example, developers use minikube, the testing team uses AWS and prodictopm uses on-prem openshift. garden.io is all in a single cluster and is more similar to the Tekton Project.
Would you change something in your migration flow if you were migrating to AWS instead of Google Cloud?
No, the cloud provider does not matter. They all run Kubernetes they just have a different set of commands you run to configure your client (kubectl) to talk to the cluster. Once the config is done, the kubectl commands will the same for all providers.
How does DeployHub compare to AWS Codestar?
Codestar is a CD platform that manages a pipeline. It’s similar to Jenkins and CircleCI. Codestar can be used to orchestrate the push of java microservices like any similar CD solution.
DeployHub would be one of the tools that Codestar would interface with. DeployHub would keep track of the dependency relationship and manage your application’s configuration. Codestar would run the pipeline calling out to DeployHub to grab information about the builds and do deployments at the logical application view. Codestar can do deployments at the individual container level, but has no knowledge of how that container relates to the others and what impact the deployment will have.
Can you please share an URL for a video on an end-to-end configuration setup using DeployHub?
While we don’t have a single video, we have a series of shorter videos covering the steps. Visit our Resource page for the tutorials. We also have a First Steps tutorial that may help.
DeployHub Key Features