DEV Community

Laura
Laura

Posted on • Edited on

Migrating an Springboot Java API: Part 3 - AWS ElasticBeanstalk

On the previous post I showed how to setup an Azure DevOps pipeline to push Docker images into ECR.
In this guide I will show how to use AWS Elastic Beanstalk to pull and run these images in a fully fledged application (with database, EC2 servers, load balancer; all handled by EB)

Pre-requisites

  • AWS-CLI
  • EB CLI
  • An AWS account with the following permissions:
    • ECR (Elastic Container Registry)
    • ECS (Elastic Container Service)
    • EC2 (Elastic Compute Cloud)
    • RDS (Relational Database Service)
    • EB (Elastic Beanstalk)

IAM role

In order to initialize an EB application, I created IAM role with the following permission policies:

AdministratorAccess-AWSElasticBeanstalk
AmazonEC2ContainerRegistryFullAccess
EC2InstanceProfileForImageBuilderECRContainerBuilds
AmazonSSMFullAccess
AmazonEC2FullAccess
AWSElasticBeanstalkMulticontainerDocker
AWSElasticBeanstalkRoleECS
Enter fullscreen mode Exit fullscreen mode

Optional - push the redis cache to a private ECR repository

It was better for my project to have a locally available version of the dependencies, so I created an powershell script create-local-redis.ps1 to push a new version of the redis image

aws ecr get-login-password --region AWS_REGION | docker login --username AWS --password-stdin AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com

docker pull redis:7.2.1

docker tag redis:7.2.1 AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/local-redis:7.2.1

docker push AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/local-redis:7.2.1
Enter fullscreen mode Exit fullscreen mode

Configuration files for Elastic Beanstalk

I used the configuration for EB multicontainer
So I added a deployment/ folder side-by-side with MyJavaSolution with the following:

deployment/
├── create-local-redis.ps1
├── Dockerrun.aws.json
├── .ebextensions/
│ │── db-instance-properties.config
│ │── network-load-balancer.config
│
MyJavaSolution/
├── ...
Enter fullscreen mode Exit fullscreen mode

Dockerrun.aws.json

{
    "AWSEBDockerrunVersion": 2,
    "containerDefinitions": [
        {
            "name": "local-redis",
            "image": "AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/local-redis:7.2.1",
            "portMappings": [
                {
                    "hostPort": 6379,
                    "containerPort": 6379
                }
            ],
            "essential": true,
            "memory": 1024
        },
        {
            "name": "customer_DOCKER_REPOSITORY_NAME",
            "image": "AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/customer_DOCKER_REPOSITORY_NAME:Build.BuildNumber",
            "portMappings": [
                {
                    "hostPort": 9001,
                    "containerPort": 9001
                }
            ],
            "links": [
                "local-redis"
            ],
            "essential": true,
            "memory": 1024
        },
        {
            "name": "ADMIN_DOCKER_REPOSITORY_NAME",
            "image": "AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/ADMIN_DOCKER_REPOSITORY_NAME:Build.BuildNumber",
            "portMappings": [
                {
                    "hostPort": 9002,
                    "containerPort": 9002
                }
            ],
            "links": [
                "local-redis"
            ],
            "essential": true,
            "memory": 1024
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

db-instance-properties.config

option_settings:  
  # Sets a decoupled database for the connection
  # It can be created from a specific snapshot using DBSnapshotIdentifier
  # If you are happy with the database, you can choose to decouple it via command line later
  aws:rds:dbinstance:
    DBAllocatedStorage: '5'
    DBDeletionPolicy: Retain
    DBInstanceClass: db.t4g.micro    
    DBSnapshotIdentifier: 'DB_INITIAL_SNAPSHOT'
    HasCoupledDatabase: 'true'
    MultiAZDatabase: 'false'
    DBName: DB_NAME
    DBEngine: DB_ENGINE
    DBEngineVersion: DB_ENGINE_VERSION
    DBUser: DB_USERNAME    
    DBPassword: DB_PASSWORD
Enter fullscreen mode Exit fullscreen mode

network-load-balancer.config

option_settings:
  # Set the instance type for EC2 instances to t3.large.
  aws:ec2:instances:
    InstanceTypes: t3.large

  # Set the instance type for Auto Scaling launch configuration to t3.large.
  aws:autoscaling:launchconfiguration:
    InstanceType: t3.large

  # Disable the listener on port 80.
  aws:elbv2:listener:default:
    ListenerEnabled: false

  # Configure Elastic Beanstalk to listen on port 9001.
  aws:elbv2:listener:9001:
      DefaultProcess: default
      ListenerEnabled: 'true'
      Protocol: HTTP
      Rules: null
      SSLCertificateArns: null
      SSLPolicy: null

  # Configure Elastic Beanstalk to listen on port 9002.
  aws:elbv2:listener:9002:
    DefaultProcess: admin
    ListenerEnabled: 'true'
    Protocol: HTTP
    Rules: null
    SSLCertificateArns: null
    SSLPolicy: null

  # Configure default Health endpoint to be Customer health
  aws:elasticbeanstalk:environment:process:default:
    DeregistrationDelay: '20'
    HealthCheckInterval: '15'
    HealthCheckPath: /api/customer/health
    HealthCheckTimeout: '5'
    HealthyThresholdCount: '3'
    MatcherHTTPCode: '200'
    Port: '9001'
    Protocol: HTTP
    StickinessEnabled: 'false'
    StickinessLBCookieDuration: '86400'
    StickinessType: lb_cookie
    UnhealthyThresholdCount: '5'

  # Configure additional Health endpoint to be Admin health
  admin.aws:elasticbeanstalk:environment:process:admin:
    DeregistrationDelay: '20'
    HealthCheckInterval: '15'
    HealthCheckPath: /api/admin/health
    HealthCheckTimeout: '5'
    HealthyThresholdCount: '3'
    MatcherHTTPCode: '200'
    Port: '9002'
    Protocol: HTTP
    StickinessEnabled: 'false'
    StickinessLBCookieDuration: '86400'
    StickinessType: lb_cookie
    UnhealthyThresholdCount: '5'
Enter fullscreen mode Exit fullscreen mode

Inital AWS Elastic Beanstalk setup

Provided the desired images are already in ECR, I first initialized EB after installing AWS and EB CLI with: eb init

Select a default region
1) us-east-1 : US East (N. Virginia)        
2) us-west-1 : US West (N. California)      
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-south-1 : Asia Pacific (Mumbai)       
7) ap-southeast-1 : Asia Pacific (Singapore)
8) ap-southeast-2 : Asia Pacific (Sydney)   
9) ap-northeast-1 : Asia Pacific (Tokyo)    
10) ap-northeast-2 : Asia Pacific (Seoul)   
11) sa-east-1 : South America (Sao Paulo)   
12) cn-north-1 : China (Beijing)
13) cn-northwest-1 : China (Ningxia)        
14) us-east-2 : US East (Ohio)
15) ca-central-1 : Canada (Central)
16) eu-west-2 : EU (London)
17) eu-west-3 : EU (Paris)
18) eu-north-1 : EU (Stockholm)
19) eu-south-1 : EU (Milano)
20) ap-east-1 : Asia Pacific (Hong Kong)    
21) me-south-1 : Middle East (Bahrain)      
22) il-central-1 : Middle East (Israel)     
23) af-south-1 : Africa (Cape Town)
24) ap-southeast-3 : Asia Pacific (Jakarta)
25) ap-northeast-3 : Asia Pacific (Osaka)

(default is 3): 19


Enter Application Name
(default is "eb_deploy"): MyJavaAPI-newEnv
Application MyJavaAPI-newEnv has been created.

It appears you are using Docker. Is this correct?
(Y/n): y
Select a platform branch.
1) Docker running on 64bit Amazon Linux 2023
2) ECS running on 64bit Amazon Linux 2023
3) Docker running on 64bit Amazon Linux 2
4) ECS running on 64bit Amazon Linux 2
(default is 1): 2 

Cannot setup CodeCommit because there is no Source Control setup, continuing with initialization
Do you want to set up SSH for your instances?
(Y/n): y

Select a keypair.
1) existing_pair
2) [ Create new KeyPair ]
(default is 1): 2


Type a keypair name.
(Default is aws-eb): MyJavaSolutionAPI_pair
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in C:\Users\Username\.ssh\MyJavaSolutionAPI_pair.
Your public key has been saved in C:\Users\Username\.ssh\MyJavaSolutionAPI_pair.pub.
The key fingerprint is:
SHA256:.../te34 MyJavaSolutionAPI_pair
The key's randomart image is:
...
Enter passphrase:
WARNING: Uploaded SSH public key for "MyJavaSolutionAPI_pair" into EC2 for region ...
Enter fullscreen mode Exit fullscreen mode

Create

  • After the AWS Beanstalk Environment was initialized locally, run eb create to push the initial changes into the server
Enter Environment Name
(default is MyJavaAPI-newEnv): MyJavaAPI-newEnv
Enter DNS CNAME prefix
(default is MyJavaAPI-newEnv): 

Select a load balancer type
1) classic
2) application
3) network
(default is 2): 2


Would you like to enable Spot Fleet requests for this environment? (y/N): n
Creating application version archive "app-231023_153901209577".
Uploading MyJavaAPI/app-231023_153901209577.zip to S3. This may take a while.
Enter fullscreen mode Exit fullscreen mode

If the configuration is applied correctly, then it will create a new EB application and environment, with a coupled RDS database, a EC2 instance, an S3 bucket containing the files, an ECS cluster and all necessary security groups.

EB instance

Manually deploying a new version on AWS Elastic Beanstalk

Deploy

If the latest build was 20231026.21:
You can change the version in Dockerrun.aws.json and run

eb deploy --label "20231026.21" --message "20231026.21" --process
Enter fullscreen mode Exit fullscreen mode

This will result in a new application version in the server.

Config save

The configuration of a current version can be saved to be reused in different environments.

eb config save --cfg my-api-v2-20231026.21 --tags Environment=newEnv
Enter fullscreen mode Exit fullscreen mode

And with that we have an environment up and running with our 2 dockerized API images.
In the next post of this series I will show how to use Azure DevOps pipelines and release to Continuously Deploy this.

Top comments (0)