The desire to migrate application to cloud has many advantages, so today almost no questions appear on why we should migrate applications to the cloud.
The main benefit of using the cloud is that it takes responsibility (by providing SLAs) for properly running and maintaining low-level infrastructure (networks, servers, etc.), operating systems, and services, so you can focus on things that really matter: your app features. Besides that, modern clouds offer flexible pricing models (no need to buy a server or lease it for a long time, you can pay-as-you-go), centralized control, advanced security tools, automatic backups, and disaster recovery solutions, etc.
Migration procedure
Of course, the migration procedure is unique for each individual case and largely depends on a number of factors, such as your business logic, your understanding of what is cloud migration, the volume of your current environment, migration goals, and so on. In this situation, you need to fully rely on your team of engineers, which will deal with the migration, or trust the provider.
One way or another, the migration process is often divided into several main stages:
Migration scenario
As elsewhere, good thought of further actions gives a better chance of success. Think carefully about why you need to migrate application to cloud, what your goals are, what outcome you expect, and what difficulties you will face in the process. Think about which tools and metrics to choose to measure the effectiveness of your migration. All this will make your cloud migration strategy solid.
Choosing a provider
There are a large number of cloud providers selling cloud migration services, including industry giants like AWS or MS Azure. Each provider has a number of its advantages, cloud models and ready to provide cloud migration consulting. Evaluate which provider's offer best suits your migration requirements and goals.
Migration
After careful planning and selection of a provider, you can begin the migration process. It is worth noting that migration can be carried out both online and offline, with the actual shipping of your servers to the provider.
Assessment of success
Using the previously selected tools and metrics, assess whether you have achieved your goals and passed all cloud migration challenges. Often, the success of the migration can be assessed only after a while, so take your time.
And if how to migrate an application to the cloud is no secret anymore, then the more tricky question that’s often out of scope is how to make apps ready to migrate to a cloud.
Make sure your app can be configured easily
Business benefit: faster deployment to different environments.
This refers to the same concept as the first rule from the twelve-factor app methodology: you should only have one codebase which can be deployed to multiple environments.
In practice, it usually means that all environment-related configuration should be taken from environment variables which, in their turn, are filled from some configuration files (see another 12-factor app principle).
Such files can be stored in different ways (the easiest is to use .env file) and must be kept separately from the code as they usually contain sensitive information like DB credentials, passwords, secret keys, etc.
Take into account that container orchestration systems (that you will likely use in a cloud) usually have their own formats of config files, for example: see Kubernetes ConfigMap and Secrets.
Decouple data from the application
Business benefit: faster cloud application migration.
Your application shouldn’t store any important data either in memory or on a local hard drive while running. The reasoning is that clouds usually assume that it’s OK to stop your application or even the whole virtual machine and relaunch it again, and it shouldn’t break anything - it’s true for all modern container orchestration systems (e.g., AWS ECS, Google App Engine or Kubernetes).
The above recommendations relate to two more twelve-factor app principles: treat backing services as attached resources and execute the app as one or more stateless processes.
Let’s walk through the most popular places where the app can keep data and provide our recommendations.
File system. As the local file system is not permanent, the local hard drive will lose your data when an app will be launched on a different VM. So don’t keep anything important on a local hard drive. If you need to use a file system to keep your data, consider using a network file system (clouds usually offer such solutions, see AWS EFS as an example). The most widespread solution nowadays is using file storage provided by clouds, e.g. AWS S3 or Google Cloud Storage.
Session state. Don’t keep it locally, use Redis/Memcached or their cloud analogs like AWS ElastiCache.
Databases. It may seem obvious, but don’t assume that the database is located on the same host as your application. Always use full URIs (Uniform Resource Identifier) to set the connection to your DB.
Logs. Avoid logging into the file system directly, prefer logging to stdout instead. Stdout will then be used by the cloud’s log collection agents (e.g., AWS CloudWatch or Google Stackdriver Logs).
Also, consider using logging libraries that allow you to flexibly configure logging handlers (see Python’s logging module as a very good example).
By the way, there’s also a bunch of platform-agnostic tools for centrally collecting logs from multiple sources, for example, Fluentd. See the self-titled 12-factor app principle to investigate more on logging recommendations.
Use standard tools and protocols
Business benefit: higher chances to find developers with proper skills; faster application migration to cloud; faster switch between clouds.
Today, application portability is extremely important, so cloud providers, despite having many very specific proprietary instruments, try to also make some of their services compatible with the tools and protocols developers are using.
For example, Google offers their own implementation of relational databases management system (Google CloudSpanner), which is not compatible with standard SQL libraries and ORMs (so devs need to use Google’s proprietary library for working with such DB), but they also provide Google CloudSQL which is fully compatible with MySQL, PostgreSQL, and SQL Server.
Another example: AWS cloud migration offers its own implementation of message queues called AWS SQS, but they also provide AmazonMQ which is fully compatible with the AMQP protocol used, for example, in RabbitMQ and Apache ActiveMQ.
Incapsulate any environment-specific code
Business benefit: less time for code maintenance in the long run; but setting this up, in the beginning, may require a significant time investment
We don’t live in an ideal world, sometimes we have to use some non-standard tools and services, and there’s a high chance that such a tool won’t be available in a cloud; and vice versa: we can use some cloud tools that aren’t available locally (e.g., AWS S3 which can only be used in AWS, and can’t be fully installed locally, we don’t account for emulators here). However, when we will migrate to a cloud (or from one cloud to another, e.g., from AWS to Google), it could happen that we will need to change tons of environment-bound code (e.g., change file storage from AWS S3 to Google Cloud Storage). To avoid that, consider encapsulating all such codes when possible. For more information, see Clean Architecture principles by Robert Martin.
Write platform-agnostic code
Business benefit: application portability between different operating systems
Your code should be fully compatible with both the operating system you’re developing and the OS on the machine where it’s deployed. So try to use platform-agnostic libraries for interacting with OS (nice example: Python’s os module).
Example: directory separators and line endings that are different on Windows, Linux, and macOS. To write platform-agnostic code, use some library that knows which directory separators and line endings to use for the current OS.
Dockerize your app
Business benefit: extreme portability and scalability boost: the dockerized app can be deployed to most modern platforms within days or even hours; after its deployed, it can be easily scaled
Today, wrapping your app to a Docker container(-s) is the best way to achieve its portability, as containers can be used by any orchestration systems, both platform-agnostic (like Docker Swarm and Kubernetes) and the ones offered by specific clouds (like AWS ECS or Google App Engine flexible environment).
Despite portability, containerizing your app allows well-structured app dependencies like databases, message queues, other services (if we’re building microservice applications), etc.
Design your code for scalability
Business benefit: drastic scalability boost in terms of application instances (due to the ability to use modern container orchestration systems)
As all modern clouds provide excellent tools for scaling applications, it’s a crime to not use them. However, some steps need to be taken to get your app ready for that.
The most important step is to make sure your app can be easily stopped and run on other virtual machines, see more details in the “Decouple data from application” paragraph above. In this case, your app will be “stateless” (it won’t store any important info in memory or local filesystem) and it will be easy for the Cloud to launch a new instance (Docker container in most cases) of your app and distribute the load between all app instances by means of cloud built-in load balancers (e.g., AWS Elastic Load Balancing).
Another widespread step is, as described in the section above, dockerizing your app because in most cases Clouds scale your code using container orchestration tools (AWS ECS, Kubernetes, etc.). However, it’s not 100% required and could be also done under the hoods (e.g., Google App Engine Standard Environment or AWS Elastic Beanstalk). But if you do dockerize, you will 100% see that there will be some tool in a cloud that can scale your containers.
Fit your code for DB scaling
Business benefit: the ability to use modern databases scalability features
While scaling instances with app code is widely discussed, the most challenging yet not so widely discussed problem is database scaling.
Unlike app code which can be scaled straightforwardly with modern container orchestration engines like Docker Swarm, Kubernetes, AWS ECS, Google App Engine, etc., database scaling usually needs to be tailored for each specific application. Moreover, each database has its own scaling mechanisms using which sometimes requires narrow technical expertise.
But, even if a project has a highly skilled DBA or DevOps engineer, app developers are also required to make some code changes to fully utilize DB scaling features.
Let’s quickly go through the most popular means for achieving DB replication and provide our recommendations for app developers.
Vertical scaling. That’s the easiest way of scaling: you just buy a new, more powerful server and move your database there. Also, all modern clouds offer DB server instance adjustment which doesn’t require to do cloud data migration. More than that, the cloud can manage the underlying DB instance(-s) by itself, see AWS Aurora as an example.
In this case, devs can literally do nothing with the code, but vertical scaling has its limits: besides usually being very expensive (because of the cost of leasing powerful instances), at some moment you will get to the point when adding few additional gigabytes of RAM won’t really increase the overall performance.
That’s where horizontal scaling comes to the rescue. It’s all about distributing a database between different (virtual or physical) hosts. It’s achieved by replication (duplicating the same data on different hosts) and sharding (keeping different data pieces on different hosts). This article is definitely not about how to setup replication and sharding - it varies a lot for different databases (in most cases, it’s easier for NoSQL and a bit harder for SQL databases, but anyway possible), the purpose is to provide recommendations for the engineers developing apps which utilize sharded and/or replicated database.
So, here’s a list of our recommendations.
First. Your ORM should have the ability to write to/read from different DB hosts. It’s required if the database uses replication with multiple nodes, so you will read and write to one of the primary hosts (in case of the multi-primary scheme, previously known as multi-master, see terminology mapping) or write to one of the primary hosts and read to one of the replica hosts (in case of the primary-replica scheme, previously known as master-slave). In this case, it’s important that your ORM knows about all of them and is able to use host #2 in case host #1 fails.
Second.ORM should be able to manage database connections. Namely, it’s important that ORM should automatically close connections and/or reuse existing ones to avoid opening too many connections. Mature ORMs usually use a connection pool, so they open a limited amount of DB connections and reuse them between request handlers, for example, see how SQLAlchemy (a Python ORM) did that.
Third. If you share your data between multiple hosts (e.g., you’re storing geodata and want to keep, say, data for North America and Europe on different DB hosts), some logic should decide which DB host to use to write or read data. In some cases, it can be automatically determined by DB itself, but, in the general case, your application should determine which app server to use by itself. For that, the concept of shard key comes to play - it determines (in the code or via DB stored procedures) the target DB host to read/write. It’s extremely important to wisely choose a shard key that, ideally, allows distributing data between hosts evenly.
Consider using microservices architecture
Business benefit: a lot of; the main ones, in our opinion, are improved application scalability and code maintainability.
This is a very wide topic and it’s worth making a separate article about how to build the microservices application, but here our point is that microservices architecture has very nice compatibility with modern clouds which offer a lot of powerful tools to effectively deploy and monitor microservices. So it’s worth considering implementing microservices architecture in your application, even such an architecture requires having mature developers onboard and solving a ton of cloud migration problems that such architecture brings.
Resume
Above, we described our recommendations which can help to migrate application to cloud and make it ready to fully unleash its power.
Of course, recommendations for every specific application will be different. Always look for a specialist, who can evaluate your application and define what steps should be taken. If you are currently thinking about building a cloud-ready application, we can help you with all the peculiarities. Feel free to contact our experts.
The migration process is a complex task that must be performed by experienced IT engineers. Ideally, apply for a migration service from an experienced provider who guarantees success, such as AWS or MS Azure.
Expanding apps. Migration to the cloud is carried out most often in cases when the application scope is expanding and it needs more server support, security, and the ability to scale. Cloud hosting is ideal for startups.
This is not about types, but more about strategies. There are 6 separate cloud migration strategies, namely:
Rehosting - a transfer of an exact copy of your environment from on-premise to cloud hosting.
Replatforming - small changes in the environment for better adaptation.
Repurchasing - migrating to cloud native application architectures, for example replacing your CRM with Salesforce.
Refactoring - the complete rebuilding of the application from scratch, specifically for the cloud environment.
Retiring - abandoning some application functions, since they are not needed in the new environment.
Retaining - rethinking the task and refusing to migrate, in favor of the development of a better and more reasoned migration strategy.