Java App deployment to AWS EKS with Terraform

Selma Mesic
7 min readSep 11, 2021

--

In this tutorial, I will explain how to deploy Java applications using Kubernetes, AWS services ECR, and EKS, and Terraform. Java application will be implemented as a microservice using Kubernetes and the environment for deployment will be built with Terraform.

I will start with definitions of technologies and tools used in this tutorial so that we are all on the same page.

Kubernetes is an open-source container orchestration engine for automating deployment, scaling, and management of containerized applications. Terraform is an Infrastructure as Code tool and can be used for building, changing, and versioning infrastructure safely and efficiently. Amazon Elastic Kubernetes Service (EKS) is the AWS service for deploying, managing and scaling containerized applications with Kubernetes. Amazon Elastic Container Registry (ECR) is a fully-managed Docker container registry that makes it easy for developers to store, manage and deploy Docker container images.

Before going into complex details about this solution, below is the summary of tasks that will be performed:

· Clone GitHub repository containing a simple Java application

· Create a docker image of the Java app

· Create ECR repository and push docker image to the ECR

· Define and create VPC and EKS Cluster using Terraform

· Define and create Kubernetes deployment and service for the Java app

Prerequisites

Before starting this tutorial, it is necessary to install and configure the following tools and resources:

Clone GitHub repository containing a simple Java application

After installing tools from the prerequisites part, the next step is to download the Java application that will be used throughout the tutorial. A simple Java app that will be used is Spring PetClinic. To clone the GitHub repository run the following command:

git clone https://github.com/spring-projects/spring-petclinic

Create a docker image of the Java app

The next step is to dockerize the Java app by creating a Dockerfile and issuing docker build command.

FROM openjdk:16-alpine3.13WORKDIR /appCOPY .mvn/ .mvnCOPY mvnw pom.xml ./RUN ./mvnw dependency:go-offlineCOPY src ./srcCMD ["./mvnw", "spring-boot:run"]docker build -t java-app .

Using the docker images command, we can see that the java-app image is created.

Create ECR repository and push docker image to the ECR

In order to push the docker image, we need to create a repository on AWS ECR. This step can be done on the ECR dashboard, we choose the private repository option and type repository name, other settings stay as default.

After creating the ECR repository, we issue the following command to authenticate AWS CLI to push images to the ECR repository.

aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 378612673110.dkr.ecr.eu-central-1.amazonaws.com/java-demo

Next, the docker image is tagged with the repository name and pushed to the ECR repository:

docker tag java-app:latest 378612673110.dkr.ecr.eu-central-1.amazonaws.com/java-demodocker push 378612673110.dkr.ecr.eu-central-1.amazonaws.com/java-demo:latest

If you get any permission issues make sure your AWS CLI role has permission AmazonEC2ContainerRegistryFullAccess.

Define and create VPC and EKS Cluster using Terraform

In the next step Terraform VPC and EKS modules will be used for creating VPC and Kubernetes clusters.

First of all, we need to define the provider that will be used in this tutorial, provider.tf file:

provider "aws" {
region = "eu-central-1"
}

For VPC we define the name, CIDR, availability zones, public and private subnets, enable gateways, and tag resources.

Terraform file for creating VPC is following, vpc.tf:

module "vpc" {source = "terraform-aws-modules/vpc/aws"name = "java_vpc"cidr = "10.0.0.0/16"azs             = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]enable_nat_gateway = trueenable_vpn_gateway = truetags = {Terraform = "true"Environment = "dev"}public_subnet_tags = {"kubernetes.io/cluster/java-cluster" = "shared""kubernetes.io/role/elb" = 1}private_subnet_tags = {"kubernetes.io/cluster/java-cluster" = "shared""kubernetes.io/role/internal-elb"             = "1"}}

Next, we will create an EKS cluster using Terraform aws/eks module. This module is customized with resources that fit our needs, cluster node group, and its capacity is defined, also the instance type which is used is t3.small and we use VPC from the previous step. Besides that “local-exec” provisioner is defined in order to update kubeconfig file. Terraform file for creating EKS cluster, eks.tf:

data "aws_eks_cluster" "cluster" {
name = module.eks.cluster_id
}
data "aws_eks_cluster_auth" "cluster" {
name = module.eks.cluster_id
}
provider "kubernetes" {
host = data.aws_eks_cluster.cluster.endpoint
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)
token = data.aws_eks_cluster_auth.cluster.token
}
module "eks" {
source = "terraform-aws-modules/eks/aws"
cluster_name = "java-cluster"
cluster_version = "1.20"
subnets = module.vpc.private_subnets
vpc_id = module.vpc.vpc_id
kubeconfig_output_path = "~/.kube/"
node_groups = {
first = {
desired_capacity = 2
max_capacity = 3
min_capacity = 1
instanace_type = "t3.small"
}
}
}
resource "null_resource" "java"{
depends_on = [module.eks]
provisioner "local-exec" {
command = "aws eks --region eu-central-1 update-kubeconfig --name $AWS_CLUSTER_NAME"
environment = {
AWS_CLUSTER_NAME = "java-cluster"
}
}
}

Define and create Kubernetes deployment and service for Java app

The final step to deploy the Java app to AWS EKS is to define Kubernetes deployment and service using Terraform files Kubernetes.tf.

In this step for container definition, we are using the docker image which is located on the ECR repository. The type of the Kubernetes service is set to LoadBalancer in order for the app to be publicly accessible.

resource "kubernetes_deployment" "java" {
metadata {
name = "microservice-deployment"
labels = {
app = "java-microservice"
}
}
spec {
replicas = 2
selector {
match_labels = {
app = "java-microservice"
}
}
template {
metadata {
labels = {
app = "java-microservice"
}
}
spec {
container {
image = "378612673110.dkr.ecr.eu-central-1.amazonaws.com/java-demo:latest"
name = "java-microservice-container"
port {
container_port = 8080
}
}
}
}
}
}
resource "kubernetes_service" "java" {
depends_on = [kubernetes_deployment.java]
metadata {
name = "java-microservice-service"
}
spec {
selector = {
app = "java-microservice"
}
port {
port = 80
target_port = 8080
}
type = "LoadBalancer"}
}

In the output.tf file we define which data will show in output after successful creation of resources. In this tutorial to access the Java app, we need Load Balancer hostname which is defined in the output.tf file.

output "load_balancer_hostname" {
value = kubernetes_service.java.status.0.load_balancer.0.ingress.0.hostname
}

Once we have completed Terraform files, we initialize our Terraform workspace, which will download modules and configure the providers. This is done running terraform init command. To check whether syntax errors exist we run terraform validate command. Terraform plan command gives us output with summarized resources which will be created after terraform apply command.

The order of Terraform commands execution is following:

terraform init
terraform validate
terraform plan
terraform apply

After running terraform apply command we are prompted to confirm the changes, and we can see which resources will be created.

The creation of the EKS cluster takes approximately about 20 minutes. Upon successful application, your terminal prints the outputs defined in outputs.tf.

In the output, we get Load Balancer hostname, which we use to access the Java app through a web browser.

After the creation of the EKS cluster and Kubernetes deployment and service, we can check the status of those resources with the following commands. To check created EKS cluster nodes, Kubernetes services, and deployment, respectively:

kubectl get nodes
kubectl get deployments
kubectl get service -o wide

What’s next?

The next steps that could be taken for the improvement of this app and its deployment are to set a DNS alias record using Route 53 AWS service, for that we need a registered domain. Besides that, we could set SSL/TLS certificate and change the port for accessing the app to 443. Also, it would be good to have a CI/CD pipeline, so that any change to the code of the Java app is automated and applied to the app.

--

--