DEV Community

Cover image for Deploying a Multi-Stage Docker Image to AWS EC2 using Docker Hub
Ansuman Satapathy
Ansuman Satapathy

Posted on

Deploying a Multi-Stage Docker Image to AWS EC2 using Docker Hub

Hello, Guys! Hope you are doing alright. So, in this article I'll be sharing how to dockerize a Spring Boot application, push it to Dockerhub and finally deploy it to an AWS EC2 instance. If you're looking to get a Spring Boot To-Do application up and running in the cloud, you're in the right place. We'll walk through cloning the app from GitHub, creating a Docker image, and deploying it to AWS EC2. Don’t worry, it's actually a lot simpler than it sounds.

Step 1: Clone the GitHub Repository

First things first, you need to get the app's code. I have created a demo springboot todo app so that you can easily get started on creating the Dockerfile. Now go ahead, open up your terminal and run:

git clone https://github.com/ansuman-satapathy/demo-todoapp-for-dockerizing.git
mv demo-todoapp-for-dockerizing todoapp #renaming it to todoapp for easy navigation
cd todoapp #change into that directory
Enter fullscreen mode Exit fullscreen mode

Once you are inside the project root folder, you'll already find a Dockerfile there. Well, that's the one I created. If you want to start from scratch, just delete that file or the content inside the file, as we will be using a file named 'Dockerfile'.

Step 2: Create a Dockerfile

Well, you already have one don't you? So, go ahead and open it with any text editor. A Dockerfile is like a recipe for Docker to build your application’s image. After that add the following content inside it.

Don't worry, I'll explain each line.

# Stage 1: Build Application
FROM maven:3.8.4-openjdk-17-slim AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package

#Stage 2: Extract the built JAR
FROM openjdk:17-jdk-slim AS runner
WORKDIR /app
COPY --from=builder /app/target/todoapp-0.0.1-SNAPSHOT.jar app.jar

#Stage 3: Running the Application
FROM openjdk:17-jdk-alpine AS final
WORKDIR /app
COPY --from=runner /app/app.jar .
EXPOSE 8080
ENTRYPOINT [ "java", "-jar", "app.jar" ]
Enter fullscreen mode Exit fullscreen mode

This Dockerfile has three stages: one for building the app, one for preparing it to run and another for running it. Dividing the Dockerfile into multiple stages helps keep your docker image nice and small.

Exploring the Content of the Dockerfile

Building the Application:

FROM maven:3.8.4-openjdk-17-slim AS builder
Enter fullscreen mode Exit fullscreen mode
  • FROM maven:3.8.4-openjdk-17-slim: This line specifies the base image for this stage. We are using a Maven image with OpenJDK 17 on a slim base to keep the size small. A base image is the starting point of a Dockerfile that provides a operating system and the environment your application will run in.

  • AS builder: This gives a name to this stage, so we can reference it later.

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode
  • WORKDIR /app: Sets the working directory inside the container to /app. All subsequent commands will be run from this directory.
COPY pom.xml .
COPY src ./src
Enter fullscreen mode Exit fullscreen mode
  • COPY pom.xml .: This copies the pom.xml file from your local machine to the working directory in the container.

  • COPY src ./src: This copies the src directory from your local machine to the src directory in the container.

RUN mvn clean package
Enter fullscreen mode Exit fullscreen mode
  • RUN mvn clean package: Runs the Maven command to clean and package the application. This will compile the code and package it into a JAR file.

Extracting the Built JAR and Preparing It To Run:

FROM openjdk:17-jdk-slim AS runner
Enter fullscreen mode Exit fullscreen mode
  • FROM openjdk:17-jdk-slim: This line specifies the base image for this stage. We are using an OpenJDK 17 slim image to keep the size small.

  • AS runner: This names the stage runner for later reference.

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode
  • WORKDIR /app: Again sets the working directory inside the container to /app.
COPY --from=builder /app/target/todoapp-0.0.1-SNAPSHOT.jar app.jar
Enter fullscreen mode Exit fullscreen mode
  • COPY --from=builder /app/target/todoapp-0.0.1-SNAPSHOT.jar app.jar: Copies the JAR file from the builder stage to the current stage. It takes the JAR from /app/target/todoapp-0.0.1-SNAPSHOT.jar in the builder stage and places it in the current /app directory as app.jar.

Running the Application:

FROM openjdk:17-jdk-alpine AS final
Enter fullscreen mode Exit fullscreen mode
  • FROM openjdk:17-jdk-alpine: This specifies the base image for this final stage. We are using an OpenJDK 17 Alpine image, which is even slimmer and suitable for running the application.

  • AS final: This names the final stage final.

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode
  • WORKDIR /app: Sets the working directory inside the container to /app.
COPY --from=runner /app/app.jar .
Enter fullscreen mode Exit fullscreen mode
  • COPY --from=runner /app/app.jar .: Copies the app.jar from the runner stage to the current directory (/app) in the final stage.
EXPOSE 8080
Enter fullscreen mode Exit fullscreen mode
  • EXPOSE 8080: Tells Docker that the container will listen on port 8080 at runtime. This doesn't actually publish the port; it's more of a documentation for users of the image.
ENTRYPOINT [ "java", "-jar", "app.jar" ]
Enter fullscreen mode Exit fullscreen mode
  • ENTRYPOINT [ "java", "-jar", "app.jar" ]: Specifies the command to run when the container starts. Here, it runs the JAR file using the java -jar command.

    Now that we understand how a Dockerfile works, lets move forward.

Step 3: Build the Docker Image

So, now we have our Dockerfile ready. The next step is to build it and create a Docker image out of it. To do this, run this command in your terminal:

NOTE: Before proceeding, go ahead and create an account on Dockerhub. After that replace "your-dockerhub-username" with your actual username.

docker build -t your-dockerhub-username/todoapp:latest .
Enter fullscreen mode Exit fullscreen mode

This command tells Docker to build an image using the Dockerfile we just created. After this command is successfully run, check that if any image was created. Run the below command to find images on your machine.

docker images
Enter fullscreen mode Exit fullscreen mode

If you find an image named todoapp, then we have successfully created our docker image. Next is to upload it to Dockerhub.

Step 4: Push the Image to Docker Hub

To make your image available online, you need to push it to Docker Hub. Assuming you have created an account already, run this command:

Push your image:

docker push your-dockerhub-username/todoapp:latest
Enter fullscreen mode Exit fullscreen mode

Step 5: Set Up an AWS EC2 Instance

  1. Log in to AWS and go to the EC2 Dashboard.

  2. Launch a new instance using an Ubuntu Server AMI.

  3. Set up security groups to allow traffic on port 8080 by editing the inbound rules and adding a custom tcp rule for port 8080 to be open to everyone.

Step 6: Install Docker on Your EC2 Instance

Connect to your EC2 instance using SSH:

ssh -i your-key.pem ubuntu@<your-ec2-public-ip>
Enter fullscreen mode Exit fullscreen mode

Install Docker by running these commands by following this tutorial. After that start Docker and ensure it runs automatically on boot:

sudo systemctl start docker
sudo systemctl enable docker
Enter fullscreen mode Exit fullscreen mode

Step 7: Pull and Run Your Docker Image

  1. Pull the Docker image you pushed earlier. This command basically pulls the image from your docker hub account inside the EC2 instance.

    sudo docker pull your-dockerhub-username/todoapp:latest
    
  2. Run the Docker container: Finally run the below command to run the image on port 8080 of the EC2 instance.

    sudo docker run -d -p 8080:8080 your-dockerhub-username/todoapp:latest
    

Step 8: Check Your Application

Locate your public IP address for the EC2 instance. Then, open your web browser and go to:

http://<your-ec2-public-ip>:8080
Enter fullscreen mode Exit fullscreen mode

Voila! You should see your To-Do application up and running!

Troubleshooting

  • Not reachable? Check that your EC2 security group allows traffic on port 8080.

  • Container not running? Use docker ps to see if the container is active and docker logs <container-id> for any errors.

Conclusion

We’ve now got a Spring Boot To-Do application running in the cloud with Docker and AWS EC2. It might seem like a lot at first, but with these steps, it’s pretty manageable. Enjoy your newly deployed app(terminate the instance if you are not rich), and happy coding!

Credits: (Image by pikisuperstar on Freepik)

Top comments (0)