Zero to Dockerized Java, Step-by-Step

Recently I’ve been doing a lot of work with Containers and Container-related technology in AWS, and one conversation that keeps coming up over and over again is, “How do I get started containerizing my application?” and similarly, “How do I get my container running in the cloud?”

There are lots of resources available to answer these questions, but I find usually:

  1. These resources don’t take you all the way to the beginning, or…
  2. They have too many bells and whistles, with lots of “magic” going on behind the scenes.

In this post, I’m going to assume you are a developer who knows his/her craft very well. You know your code, you know how to build it, and how to get it running. (In this example, we’ll be using Java, but the example can be applied to almost any web language or framework.) Instead, I will assume that you need a ground-up example of how to containerize and start your application in the cloud, so we will focus on that.

As far as Amazon Web Services technologies, here are the main ones we’ll be using today:

As for example code, we will be using two projects from my personal Github account:

To get started, download those two sample projects and (if you don’t already have one) sign up for an AWS account: https://aws.amazon.com/getting-started/

Click the “Create an AWS Account” button and follow the directions from there. 🙂

Now, assuming you have successfully signed up for AWS and you’ve downloaded my two Github projects, you can proceed with the first main section.

Setting up your ECS Cluster

After logging into AWS, you’ll first need to double-check your region in the top-right corner of the dashboard.

I’m in Ohio, so us-east-2 is perfect.

Now navigate to the CloudFormation console, either by selecting it from the menu or by typing “CloudFormation” in the search box.

From the CloudFormation welcome page, you’ll want to click on the “Create stack” button.

Click on “Create stack” to, well, create a stack.

In CloudFormation, a Stack is a collection of resources that were created from a Template. We’ll upload our template, and we will allow CloudFormation to create all the resources we ask for.

On the Create stack page, select “Template is ready”, “Upload a template file”, and then click “Choose file”. (For the file, you’ll want the cloudformation.yaml file which you downloaded from the ecs-trivial-cluster project.) From there, you’ll click “Next”.

We will be using “cloudformation.yaml” from the ecs-trivial-cluster project.

On the next page, all you need to do is give your stack a name. You could have multiple instances of any given template, so you can name your stacks accordingly. In this case I just typed “ecs-trivial-cluster” and left the rest of the fields as default. Then I clicked “Next”. Note: Whatever you use as your stack name, you’ll need to remember it for future steps!

Defaults are fine, just name your stack.

On the “Configure stack options”, all of the defaults are fine. Just skip this page and click “Next”.

Just scroll down and click “Next”.

On the next page, scroll all the way down, tick the “I acknowledge” checkbox and then click “Create stack”.

This CloudFormation template creates some IAM resources.

Now you’ll need to wait a few minutes, but eventually you’ll arrive at a screen that shows your ecs-trivial-cluster stack in a green “CREATE_COMPLETE” state. If you select the “Resources” tab and glance through the resources that were created, you’ll notice (among other things) that your ECS Cluster exists now, and you can click the link to jump to the ECS console in a new browser tab.

Your stack in “CREATE_COMPLETE” state.

You’ll notice that your cluster has nothing running in it! We’ll change that here shortly.

The lights are on, but nobody’s home.

One more thing to notice in your CloudFormation stack before we move on. If you navigate back to the CloudFormation stack and click the “Outputs” tab, you’ll see a “LoadBalancerDNS” entry which is clickable.

Your stack includes a load balancer!

Go ahead and right-click on that DNS name and open it in a new tab. You’ll be greeted with something like the following:

Like I said, nobody’s home.

That 503 error is actually a great sign! It means the load balancer is wired up correctly, but there are no services attached. Let’s work on that next.

Bootstrapping the Build

As promised, we’ll be using an automated build and deploy process using AWS CodeBuild and AWS CloudFormation. But how do we configure the AWS CodeBuild project? With more CloudFormation of course!

From your CloudFormation console, click on “Create stack” and then select “With new resources (standard)”.

Create a new stack.

As before, select “Template is ready” and “Upload a template file”. But this time, we’ll select the bootstrap.yaml file from the ecs-trivial-tomcat project. Then we’ll click “Next”.

This time we’ll deploy the bootstrap.yaml template.

For stack name, I typed “ecs-trivial-tomcat-bootstrap”. For ECSStackName it’s important that you enter the name of the first stack you deployed. In my case, this is “ecs-trivial-cluster”, but in your case it may be different depending on what you entered above. The rest of the defaults are fine, so just go ahead and click “Next”.

Make sure the reference to the ECS trivial cluster stack is correct!

As before, we’ll take all the defaults on the next page, scroll down and click “Next”.

Take all the defaults on this page.

On this page, you’ll notice you don’t need to acknowledge that CloudFormation may create IAM resources. This is a design choice for this template example, and we’ll explain it once we start digging into the code. All you need to do here is scroll down and click “Create stack”.

No IAM acknowledgement this time.

Once your stack gets to a green CREATE_COMPLETE state, you’re ready to proceed to the next section!

That beautiful CREATE_COMPLETE.

Triggering Your First Build

Navigate to the CodeBuild console by clicking Services and selecting CodeBuild from the menu, or simply typing “CodeBuild” in the search box and selecting CodeBuild from the populated results.

Navigate to the CodeBuild console.

Here you’ll notice that there is a CodeBuild project that was automatically created for you by our bootstrap process! Go ahead and click on the project name to inspect the project.

You’ll notice a CodeBuild project was automatically created for you.

Go ahead and click “Start build”.

Start build doesn’t start the build. It takes you to the “Start build” page.

On the “Start build” page, go ahead and take all the defaults, clicking “Start build” at the bottom.

This will start the build for real.

You’ll notice the build goes to “In progress”.

In progress.

You can click “Tail log” to watch as the build progresses.

Tail logs will show you the logs roughly as they happen, although there is a short delay.

After some time, you should see the “SUCCEEDED” status appear!

SUCCEEDED

Your build will also receive a green check mark.

Now that your build has succeeded, your application has been built and deployed! Let’s verify…

Verifying the Deployment

Go back to your Load Balancer’s DNS name (it should be something like ecs-t-loadb-blah-blah.us-east-2.elb.amazonaws.com/ and you can find it in the Outputs tab of your ecs-trivial-cluster stack within the CloudFormation console) and reload it in your browser.

Hello world!

As you can see, the container is responding with a “Hello world!” message! Let’s add “HelloWorld-1.0” to the end of the URL and we’ll see a Hello World message emitted from within the WAR.

More hello world!

So as you can see, the container is running, Tomcat is running, and the simple Java app is running.

We can see further details if we navigate back to the ECS console:

Elastic Container Service should be in your recently visited list.

Once you have reached your ECS Clusters page, you can observe your ecs-trivial-cluster, but this time you have a running task! That task was launched by the service which was created by our deployment process. (Don’t worry if this doesn’t make sense yet, we’ll dive into the code in the next section.)

Go ahead and click on the name of the cluster to take a closer look.

One service with one running task!

On the cluster detail page, you can click on the ecs-trivial-tomcat service to inspect it.

This is the service that was created by our deployment process.

On the service detail page, you can click on the “Tasks” tab to see a list of tasks started by this service.

Click on “Tasks” to see a list of tasks started by this service.

Now click on the Task id to inspect the task itself.

This is the task that was started by the service.

Finally arriving at the task detail page, we can observe a number of useful details…

Here are all the details you’ll need about your running container.

You’ll notice that the task is RUNNING, which is important if you want it to take traffic. You’ll be able to see that it was launched in FARGATE, which means it’s a serverless Docker container. You’ll be able to see networking details, links to container logs, and more!

Diving Deeper

With the demo complete, what is going on here?

If you’re anything like me, you are going to be very suspicious of any goofy “magic” going on inside example projects like this one. And frankly, one of the main reasons I designed this tutorial in the way that I did was so that we could walk through the entire project in some logical order and explain everything that is going on behind the scenes.

We’ll start with ecs-trivial-cluster because it provides the necessary foundation. Next we’ll examine ecs-trivial-tomcat, but we’ll work our way from the inside to the outside, so you can more logically connect it to the application development process you’re already used to.

Understanding ecs-trivial-cluster’s cloudformation.yaml

This is our first CloudFormation template. As mentioned above, we are using CloudFormation to automate the creation and management of our cloud resources. This works by passing a template file in either JSON or YAML syntax to the CloudFormation service, which then takes responsibility for calculating the necessary steps to get from your current state to the desired state outlined in the template.

Generally, the choice between YAML or JSON is up to you, and may be influenced by the tools you plan on using, but I generally prefer YAML for two reasons:

  • I find YAML easier to read than JSON.
  • YAML supports adding comments, whereas JSON does not.

Basic CloudFormation template format and syntax is explained in detail in the documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html

However, we will dig in and see what’s going on here…

Description: >
  This is the base template which includes a VPC and some key dependencies.

This may be self-explanatory, but in this section we are simply declaring a human-readable description of the purpose of this CloudFormation template.

Parameters:
  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.168.0.0/16

  PublicSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
    Type: String
    Default: 10.168.0.0/24

  PublicSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
    Type: String
    Default: 10.168.1.0/24

In this section, we are declaring three Parameters with appropriate defaults. They are all three IP ranges in standard CIDR notation. By themselves, they have no function, but you can think of them like variables which will be used later.

Outputs:

In this section, we declare nine Outputs and set them to reference things that will be created by the stack. Like Parameters, these Outputs have no function by themselves, but you can think of them like variables that will be used later.

We won’t examine the Outputs section yet, because it won’t make sense until later. However, I like to put the Outputs section near the top of my template because it’s part of the interface between your CloudFormation stack and everything else in your environment, so it’s good to declare it up front.

Resources:

In this section we declare fifteen Resources for CloudFormation to create on our behalf. It might seem like a lot, but I promise it will make sense once we break them down.

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName} VPC

This is our VPC (Virtual Private Cloud). It will provide the fundamental building block of our network in the cloud. See the documentation for AWS::EC2::VPC for details, but we’ve defined our properties like so:

  • CidrBlock
    • This is the only required property.
    • Using !Ref we are directly passing the VpcCIDR parameter that was declared at the top of the file. So whatever the user supplies will get passed directly here.
  • EnableDnsHostnames
    • This enables DNS hostnames for VPC. I have set this to true just for convenience, although we’re not actually using them in this demo.
  • Tags
    • I have set a Name tag so it will show up in the console with an appropriate name. We’re using !Sub here to interpolate the CloudFormation StackName into the tag, so you can clearly see which CloudFormation stack produced the VPC. For more information on how this works, see the Fn::Sub documentation as well as the Psudo parameters reference.

More deep dive coming soon!

Understanding ecs-trivial-tomcat’s Dockerfile

More deep dive coming soon!

Understanding ecs-trivial-tomcat’s app-deployment.yaml

More deep dive coming soon!

Understanding ecs-trivial-tomcat’s buildspec.yml

More deep dive coming soon!

Understanding ecs-trivial-tomcat’s bootstrap.yaml

More deep dive coming soon!

Next Steps

Next, you should get your own code running!

  • Clone your own copy of ecs-trivial-tomcat and make some modifications, such as updating the various Hello World messages.
  • Update the CodeBuild source repository to point to your own (modified) repository and re-run the build.
    • Notice how the deployment seamlessly rolls from the old version of the container to the new version with the updated code?
  • Insert your own application code into the src/ directory and update the buildspec.yml and the Dockerfile to point to it.
    • Are you able to build your project within CodeBuild?
    • Does the container build successfully?

Let me know if this resource was helpful for you! I would love to hear how your Containers journey turns out!

Credits

In support of this tutorial, I was grateful to receive contributions from Mamata Vaidya, who generously contributed the Tomcat hello world example. — https://www.linkedin.com/in/mamata-vaidya/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s