AWS Prescriptive Guidance - Containerizing traditional Java EE applications for the AWS Cloud

 
CONTINUE READING
AWS Prescriptive Guidance - Containerizing traditional Java EE applications for the AWS Cloud
AWS Prescriptive Guidance
Containerizing traditional Java EE
 applications for the AWS Cloud
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