AWS Prescriptive Guidance - Containerizing traditional Java EE applications for the AWS Cloud
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud AWS Prescriptive Guidance: Containerizing traditional Java EE applications for the AWS Cloud Copyright © 2022 Amazon Web Services, Inc. and/or its affiliates. All rights reserved. Amazon's trademarks and trade dress may not be used in connection with any product or service that is not Amazon's, in any manner that is likely to cause confusion among customers, or in any manner that disparages or discredits Amazon. All other trademarks not owned by Amazon are the property of their respective owners, who may or may not be affiliated with, connected to, or sponsored by Amazon.
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Table of Contents Introduction ...................................................................................................................................... 1 Overview ................................................................................................................................... 1 Container-based application design ...................................................................................................... 2 Replatforming challenges ............................................................................................................ 2 Best practices ............................................................................................................................ 2 Migration approach ............................................................................................................................ 3 Discovery and planning ............................................................................................................... 3 Clustering options ...................................................................................................................... 3 Vendor-specific packages ............................................................................................................ 3 Target container platform ........................................................................................................... 3 Automated testing ..................................................................................................................... 3 Technical domains .............................................................................................................................. 5 1. Session state .......................................................................................................................... 6 2. Agents .................................................................................................................................. 6 3. Application servers ................................................................................................................. 6 4. File store ............................................................................................................................... 7 5. Build and deploy process ......................................................................................................... 7 6. Database access ..................................................................................................................... 7 Additional considerations .................................................................................................................... 9 Small base image ....................................................................................................................... 9 Container-aware JDK version ....................................................................................................... 9 Resources ........................................................................................................................................ 10 References ............................................................................................................................... 10 Tools ....................................................................................................................................... 10 Document history ............................................................................................................................. 11 Glossary .......................................................................................................................................... 12 Modernization terms ................................................................................................................. 12 iii
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Overview Containerizing traditional Java EE applications for the AWS Cloud Mayuki Yamabe and Michal Urbaniak, Amazon Web Services (AWS) April 2022 (document history (p. 11)) Overview Although Java Enterprise Edition (EE) is the dominant framework for enterprise applications, it can be challenging to migrate your Java EE applications to the Amazon Web Services (AWS) Cloud without refactoring your application’s business logic and data models. This guide helps you overcome that challenge by using a containerization strategy for migrating your Java EE application to the AWS Cloud, while preserving the application’s server-side business logic and data model. The strategy is based on refactoring your application into microservices and then running the application on a modernized container platform. The “heart” of an application is the business logic and data model, which are tightly coupled with longstanding business rules and requirements. This tight coupling makes applications more difficult to refactor. In this guide, we recommend a strategy to preserve the server-side business logic and data model as much as possible, while modernizing your application’s underlying technologies by using Docker containers and container orchestration platforms, such as Amazon Elastic Container Service (Amazon ECS) and Amazon Elastic Kubernetes Service (Amazon EKS). The following diagram shows a design pattern for refactoring a traditional Java EE application into a containerized application. 1
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Replatforming challenges Container-based application design Java EE replatforming challenges You can face the following challenges when you migrate your Java EE application to a containerized platform in the AWS Cloud: • Disposability – To keep a container "stateless," you might have to store session state in an external database. Container-based applications require a faster, smaller application runtime, and your Java EE application server might not be able to run in the container environment. • Container platform compatibility – You might have to reduce application runtime-specific capabilities, such as clustering, application deployment, and memory replications. • Portability – Container-based applications are deployed by using an application runtime, while traditional Java EE applications are deployed by using application packages (.jar or .war files). Best practices for container-based application design We recommend that you follow these best practices when you design your container-based Java EE applications for the AWS Cloud: • Avoid making changes to your container instance after you created it. If you must make changes, build a new container image and reuse that new image across all environments. • Avoid storing permanent data inside your container. • Design your container so that it addresses a single purpose. For information on designing a container that serves multiple purposes, see the Using sidecar injection on Amazon EKS with AWS App Mesh blog post. • Make sure that your container implements all necessary APIs. • Design your container so that its system requirements are built around CPU usage, system memory, and persistent storage. For more information on best practices, see Principles of Container-based Application Design in the Kubernetes documentation. 2
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Discovery and planning Migration approach This section describes an approach for containerizing traditional Java EE applications in the AWS Cloud. For more general migration guidelines, see Mobilize your organization to accelerate large-scale migrations in the AWS Prescriptive Guidance documentation. Start the discovery and planning process Java EE application migration requires deep application discovery. As part of the discovery and planning process, we recommend that you identify the following in your Java EE application: • Number of CPUs • Memory and disk requirements • Java EE, the Java Development Kit (JDK), and application server versions (such as Oracle WebLogic Server 10) Understand clustering options for high availability and scalability More and more traditional Java EE applications are running on vendor-specific clustering systems that improve application availability and scalability. In a containerized approach, clustering is performed by container orchestration platforms such as Amazon ECS and Amazon EKS. We recommend that you understand the difference between clustering done by container orchestration platforms and clustering done by your current application platforms. Assess the compatibility of vendor-specific packages Application server vendors can offer their own Java EE packages. To ensure compatibility with containerized environments, check if your application uses any Java EE packages from application server vendors. Select a target container platform Choosing the right container platform for Java EE depends on your business needs. Popular choices include container-friendly, open-source (and sometimes light-weight) Java EE platforms that are distributed on Docker Hub, including GlassFish Server, WildFly, and Open Liberty. We recommend that you consider a container platform that offers production-level technical support and licensing. Prepare for automated testing Migrating Java EE applications to a new application server requires code or configuration changes other than business logic. Without an automated test and build process for your current application, you can’t 3
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Automated testing verify that your code and configuration changes won’t break existing business logic. We recommend that you establish an automated build and test pipeline in the first phase of the project, which includes modernizing manual test processes and unmaintained application build settings (such as build.xml in Apache Ant) with mainstream build tools such as Maven (Apache Maven documentation) or Gradle (Gradle documentation). For more information, see Automatically build and deploy a Java application to Amazon EKS using a CI/CD pipeline in the AWS Prescriptive Guidance documentation. 4
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Technical domains This section provides an overview of the main technology domains for containerization. The following diagram shows the architecture of a traditional Java EE application. The following diagram shows the architecture of a containerized Java EE application. 5
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud 1. Session state 1. Session state In most cases, Java EE applications store the session data associated with user requests, such as cookies for servlets and Enterprise Java Beans (EJB) stateful sessions. We recommend that you avoid storing state information in Java virtual machine (JVM) memory because your container must be kept stateless. For more information on the disposability principle, see Principles of container-based application design in the Red Hat documentation. In Java EE, there are two types of session data: HTTP session data and EJB session data. HTTP and EJB session data can be persisted by the application server. Multiple traditional application servers support memory replication to increase the availability of this session data, such as Infinispan on RedHat JBoss and Data Replication Service on IBM WebSphere Application Server. The memory replication mechanism assumes that a particular set of servers always exist in the cluster, or a small number of servers join or leave the cluster. This is incompatible with a container environment, so we recommend that you get rid of your memory replication mechanism. In a container environment, the application servers are redeployed when a new version of the container image is built. That is, all replicated memory data is also erased. 2. Agents There are multiple agent processes running on a single physical or virtual server that typically perform automation and utility tasks, such as the following: 1. Monitoring – Traditional Java EE application environments often use dedicated agents for monitoring. These agents are responsible for monitoring server CPU, memory, disk usage, memory usage inside the JVM, log messages, and more. However, it’s not possible to directly run monitoring agents in a container environment. You must replace monitoring agents with the monitoring mechanism provided by the container platform, such as Amazon CloudWatch and Amazon CloudWatch Logs. 2. Jobs (scheduled tasks) – In traditional Java EE application environments, the job execution environment often resides on the same server as the application server and is responsible for long- running batch processes apart from user requests. For example, the batch process controlled by the job controller accesses the database to retrieve data and create a report. Since these multiple workloads cannot coexist in a container environment, you must build the job and batch execution environment separately from the container environment. 3. File transfer – File transfer agents are typically not as common in Java EE application environments, but they sometimes run on the same operating system with the Java application as an independent process to exchange files to or from external applications. For example, data used by other applications is transferred to a file every day and reflected in the database. File transfer agents can’t be running aside containers, but must be running on another server that has access to the database and files. 3. Application servers The most significant challenge in containerization is changing application servers. Traditional Java EE compliant application servers assume a static compute environment, so they might not be suitable for running in a container environment. That is, the physical or virtual servers are assumed as the entity of the compute environment for Java EE applications. For example, proprietary Java EE application servers, such as a traditional IBM WebSphere Application Server (tWAS) and Oracle WebLogic Server, have their own application deployment mechanism. The situation is different in a container environment. In this environment, containers include an application server and runtime with application build packages (for example, .war and .jar files) and are deployed to container platforms such Amazon ECS or Amazon EKS. We recommend that you use 6
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud 4. File store a container platform mechanism to deploy applications to environments. Application servers are frequently deployed with containers, so they must be small in size (less than 500 MB) and fast to start. To meet this requirement, you might need to change the traditional application server and migrate to a more container-friendly application server. This could require a migration from IBM WebSphere Application Server to IBM WebSphere Liberty or JBoss Enterprise Application Platform (EAP) to WildFly. We recommend that you consider the following impacts that can result from changing an application server: 1. Configuration injection through environment variables (in contrast to traditional Java EE applications that store configurations in a file, such as web.xml) 2. Compatibility with Java EE capabilities 3. JVM versions 4. File store The file store most commonly used for traditional Java EE applications is the local file system. The most common use cases include application log files, application-generated files such as business reports, and user-uploaded content. We recommend that you avoid storing files inside the container because containers are stateless, which means that file stores need to be externalized for containerization. Consider the following containerization options: 1. Amazon Elastic File System (Amazon EFS) – Amazon EFS is a managed NFS service that’s accessible from containers. Amazon EFS is integrated with Amazon ECS and Amazon EKS. If you use Amazon EFS, you don’t need to write custom scripts to mount EFS volumes to your containers. The first step for this option is to list all the file system paths in your application that are used to read or write. After you identify the file system path to be persisted, you can map the file system path to an EFS file system path. For more information, see Tutorial: Using Amazon EFS file systems with Amazon ECS in the Amazon ECS documentation. Not all paths are required to be persisted, especially application log files. Most enterprise applications write log files to a local file system. As part of the containerization process, we recommend that you consider changing the logging destination to use Standard Out and Standard Error. This allows you to capture all output to CloudWatch Logs without managing log storage sizing and performance. For more information about logging in Amazon ECS, see Using the awslogs log driver in the Amazon ECS documentation. 2. Amazon Simple Storage Service (Amazon S3) – Amazon S3 is less expensive than Amazon EFS and supports a wider bandwidth than Amazon EFS, but Amazon S3 requires a broader application code change than Amazon EFS. This is because Amazon S3 is not a file system. 5. Build and deploy process The containerization process involves changing and extending the application delivery process. In traditional environments, the application delivery process primarily involves Java artifacts (for example, .war and .ear files). In a container environment, the container image is the delivery unit. In addition to the process for building existing Java artifacts, you must build a process for building and delivering Docker containers. For more information about the pipeline process, see Automatically build and deploy a Java application to Amazon EKS using a CI/CD pipeline in the AWS Prescriptive Guidance documentation. 6. Database access Traditional application containerizations are often accompanied by database migration. To reduce migration risk, we recommend following the migration strategy for relational databases (AWS 7
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud 6. Database access Prescriptive Guidance). Containerized environments require externalized configuration, including database connection strings. You can use tools such as Spring Cloud Config (GitHub repository) to externalize the Java application configuration in a distributed environment. 8
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Small base image Additional considerations This section covers general considerations on Java containerization that are not specific to Java EE applications. Use a small base image We recommend that you create a small (less than 500 MB) and well-maintained base image. Decreasing the base image size reduces your network and operating costs. A smaller base image can also improve security by reducing the number of components that can be exploited. You can use one of the distroless images based on Debian, which have the minimum number of tools installed in the image and do no contain package managers or shells. These distroless images also reduce the overall surface of attack. Distroless images can be smaller than 150 MB. For more information, see the "Distroless" Container Images GitHub repository. It’s a best practice to follow the disposability principle and develop a fast startup time for your container images. By using techniques such as ahead-of-time-compilation (OpenJDK documentation) or application class data sharing (OpenJDK documentation), you can improve the overall startup time by compiling Java classes to native code prior to launching the virtual machine and by allowing a set of classes to be pre-processed into a shared archive file. You can also use GraalVM to build minimal docker images for Java applications. For more information, see the Using GraalVM to Build Minimal Docker Images for Java Applications AWS blog post. Upgrade to a container-aware JDK version Before JDK 8u131, the JVM did not recognize memory or CPU limits set by the Docker engine using flags. This means that whenever you run your application in a container, JVM “sees” the total number of processors available on the system, or in the case of virtual machines, the virtual system. The same is true for default memory limits: the JVM will look at the host’s overall memory and use that for setting its defaults. Consequently, the JVM can claim more memory than the container platform allows it to, which results in ending the Java process by the container platform (Docker). One solution to this problem is to migrate your Java application to either Java 9 or 8u131+ before containerizing. Java 10 and later versions have full container awareness and support. 9
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud References Resources References • Principles of container-based application design • Mobilize your organization to accelerate large-scale migrations • Automatically build and deploy a Java application to Amazon EKS using a CI/CD pipeline • Migration strategy for relational databases • Using GraalVM to Build Minimal Docker Images for Java Applications Tools • Database Session Persistence 1.0 • AWS App2Container 10
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Document history The following table describes significant changes to this guide. If you want to be notified about future updates, you can subscribe to an RSS feed. Change Description Date Initial publication (p. 11) — April 11, 2022 11
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Modernization terms AWS Prescriptive Guidance glossary The following are commonly used terms in strategies, guides, and patterns provided by AWS Prescriptive Guidance. To suggest entries, please use the Provide feedback link at the end of the glossary. Modernization terms business capability What a business does to generate value (for example, sales, customer service, or marketing). Microservices architectures and development decisions can be driven by business capabilities. For more information, see the Organized around business capabilities section of the Running containerized microservices on AWS whitepaper. domain-driven design An approach to developing a complex software system by connecting its components to evolving domains, or core business goals, that each component serves. This concept was introduced by Eric Evans in his book, Domain-Driven Design: Tackling Complexity in the Heart of Software (Boston: Addison-Wesley Professional, 2003). For information about how you can use domain-driven design with the strangler fig pattern, see Modernizing legacy Microsoft ASP.NET (ASMX) web services incrementally by using containers and Amazon API Gateway. microservice A small, independent service that communicates over well-defined APIs and is typically owned by small, self-contained teams. For example, an insurance system might include microservices that map to business capabilities, such as sales or marketing, or subdomains, such as purchasing, claims, or analytics. The benefits of microservices include agility, flexible scaling, easy deployment, reusable code, and resilience. For more information, see Integrating microservices by using AWS serverless services. microservices architecture An approach to building an application with independent components that run each application process as a microservice. These microservices communicate through a well-defined interface by using lightweight APIs. Each microservice in this architecture can be updated, deployed, and scaled to meet demand for specific functions of an application. For more information, see Implementing microservices on AWS. modernization Transforming an outdated (legacy or monolithic) application and its infrastructure into an agile, elastic, and highly available system in the cloud to reduce costs, gain efficiencies, and take advantage of innovations. For more information, see Strategy for modernizing applications in the AWS Cloud. modernization readiness assessment An evaluation that helps determine the modernization readiness of an organization’s applications; identifies benefits, risks, and dependencies; and determines how well the organization can support the future state of those applications. The outcome of the assessment is a blueprint of the target architecture, a roadmap that details development phases and milestones for the modernization process, and an action plan for addressing identified gaps. For more information, see Evaluating modernization readiness for applications in the AWS Cloud. 12
AWS Prescriptive Guidance Containerizing traditional Java EE applications for the AWS Cloud Modernization terms monolithic applications (monoliths) Applications that run as a single service with tightly coupled processes. Monolithic applications have several drawbacks. If one application feature experiences a spike in demand, the entire architecture must be scaled. Adding or improving a monolithic application’s features also becomes more complex when the code base grows. To address these issues, you can use a microservices architecture. For more information, see Decomposing monoliths into microservices. polyglot persistence Independently choosing a microservice’s data storage technology based on data access patterns and other requirements. If your microservices have the same data storage technology, they can encounter implementation challenges or experience poor performance. Microservices are more easily implemented and achieve better performance and scalability if they use the data store best adapted to their requirements. For more information, see Enabling data persistence in microservices. split-and-seed model A pattern for scaling and accelerating modernization projects. As new features and product releases are defined, the core team splits up to create new product teams. This helps scale your organization’s capabilities and services, improves developer productivity, and supports rapid innovation. For more information, see Phased approach to modernizing applications in the AWS Cloud. strangler fig pattern An approach to modernizing monolithic systems by incrementally rewriting and replacing system functionality until the legacy system can be decommissioned. This pattern uses the analogy of a fig vine that grows into an established tree and eventually overcomes and replaces its host. The pattern was introduced by Martin Fowler as a way to manage risk when rewriting monolithic systems. For an example of how to apply this pattern, see Modernizing legacy Microsoft ASP.NET (ASMX) web services incrementally by using containers and Amazon API Gateway. two-pizza team A small DevOps team that you can feed with two pizzas. A two-pizza team size ensures the best possible opportunity for collaboration in software development. For more information, see the Two- pizza team section of the Introduction to DevOps on AWS whitepaper. 13
You can also read