Let’s look at some of the high-level methods required to migrate your website from a monolithic platform to a container-based microservices platform on GCP. The goal is to migrate your application one feature at a time, avoiding a single large-scale migration methodology.
Our goal here is to make the website more agile and scalable for each of these individual features. Each of these features can now be independently managed and updated, leading to faster improvements for each migrated feature.
Let’s look at some of the biggest advantages of microserviced applications. Most of these advantages now stem from the fact that each feature or microservice is loosely coupled to one another.
- Microservices can be tested and deployed independently to one another. Typically, the smaller each deployment, the easier each deployment.
- Each microservice can be written in its own language or framework. Because microservices communicate over a network via API calls, they do not all need to be written in the same language.
- Microservices can also be tasked to different teams, making it easier to have a team dedicated to one or any related microservices.
- Microservices teams have loosened dependencies on one another. Each needs to focus on their on making sure their APIs made available to the other services stay consistent, but beyond that do not need to worry about release cycles, how services are implemented, etc.
- You can design more cleanly for failure. With clearer boundaries between your services, it can be easier to have a backup in place for that particular service.
Some of the disadvantages of microservices include:
- The complexity of the design can increase as your app is not an interconnect of microservices over a network.
- Security concerns can arise as your services now talk over a network. Products like Istio were developed to try and address these issues.
- Performance can take an impact as data usually has to traverse a more complex route over the program’s microservices network.
- System design can get more complicated, making understanding your application more difficult.
Our goal is to get to a microservices environment from a single monolithic application. Let’s first take a look at the beginning and end of our journey.
First, the beginning of our journey is a monolithic website that runs on prem with dependencies on traditional database communications and the app servers running the application code. The important thing to note about the application is its design. Even if this were lift-and-shifted into the cloud on a GCP VM Instance, it would still be considered a monolithic application and the same migration principles would still apply.
The end result would look something like this after fully migrated to a microservices framework. You can see that each service is now running as an independent microservice container image on Google Kubernetes Engine (GKE) and traditional databases have moved to a Cloud SQL model and content in a Cloud Storage model. Note the application can still work with your datacenter via a Cloud Interconnect or VPN for backend services like CRM. The Cloud CDN is just there to help you distribute cached content more efficiently to your customers and the Cloud Load Balancing is there to help distribute the workload across resources in GCP. Apigee is a managed API gateway. Apigee is not necessary in this migration, but it’s recommended that all of your site’s content be served by public APIs. An API gateway like Apigee provides many features for API management, such as quotas, versioning, and authentication.
Preparing your Google Cloud Environment
Before you begin your migration journey, it is important to have your GCP environment setup, with a strategy defined on how services will be accessed, deployed, etc. Here are some of the major steps involved to get your cloud environment setup:
- Setup your Google Cloud Organization, the environment that will host your cloud resources. During this process you’ll setup your Google Workspace and Cloud Identity.
- Design your Google Cloud policies for control of the cloud resources. This is setting thigs like Network configuration and security controls, and Organizational security controls to meet requirements of your application.
- Design a method to deploy cloud resources, such as using Infrastructure as Code (IaC) to deploy to your GKE Clusters which will host your new microservices. Cloud Deployment Manager is a perfect tool for this and will give you standardized, reproducible, and auditable environments.
- Prepare your GKE environment for production and harden your cluster security. This is things like thinking how your clusters will be load balanced across regions and disabling public endpoint access.
- Build your continuous integration/continuous delivery (CI/CD) tooling for Kubernetes. You can use Cloud Build to build your container images, and Container Registry to store them and to detect vulnerabilities.
Migration Step-by-step Approach
Each feature of the website should be migrated one by one to the new environment, created microservices where it makes sense. These new microservices can call back to the legacy system when needed. The idea is to transform one major migration and refactoring project into several smaller projects. The advantages of migrating this way are:
- The smaller projects have a more definable bound and will be easier to get movement on than one grand migration project. If we do it all at once, we’d need to get all the teams involved and have everyone understand all the interactions between systems, 3rd party dependencies, etc.
- These smaller projects give us lots of flexibility. Smaller projects mean smaller teams, and this way they can be tackled one by one without anyone getting overwhelmed. You could also parallelize some of the work leading to a faster migration.
Before you start migrating any particular feature to a microservice, it’s most important that you take into account dependencies between features and what relies on what. This will help you formulate a chronology of events as some features make more sense to migrate before others.
Let’s look at a shopping cart example and what the journey currently looks like within the monolithic application and the dependencies this makes us aware of:
- A user browses your site and clicks “Add to cart” on an item they like. This triggers an API call from their browser to the shopping-cart feature. This is a first dependency you need to pay attention to: the front-end is acting on the shopping cart
- When the shopping-cart receives the API call, the shopping cart does an API call to the system that handles stock. This is the second dependency: the shopping cart depends on the stock system.
- If it’s in stock, now this is stored in a database such as “user A has 1 instance of X in cart.” This is the third dependency: the shopping cart needs the database to store this information
- When the user finally checks out and pays, the shopping cart is queried by the payment subsystem to compute the total. This is the fourth dependency: the shopping cart is queried by the payment subsystem.
Taking these dependencies into consideration, our migration to a microservice would look like this:
- Create a new microservice that implements your shopping-cart API. Use Firestore to store the shopping cart data. Make sure this new microservice can call the stock subsystem (see dependency 1 and 2 above).
- Create a script that can be rerun as needed that copies shopping carts from the legacy shopping-cart system and writes them to Firestore.
- Create a similar script that does the same thing, but the other way around: it copies Firestone carts back to your legacy system. This is just in case you need to roll-back.
- Expose the shopping cart API with Apigee
- Modify the frontend and payment subsystem so they call this new shopping cart microservice rather than the legacy one.
- Run the script from step 2.
You may also want to test this in a non-production website environment first and then replicate the results to the production system when you have it working correctly. Your shopping cart feature is now a microservice hosted on GCP.
For a deeper breakdown of this process, see https://cloud.google.com/architecture/migrating-a-monolithic-app-to-microservices-gke