Deploying Microservice to AWS

Microservice Deployment

A continuous delivery (CD) pipeline is an automated system that streamlines software moving from version control to end users and customers. A Jenkins Pipeline is defined in a text file known as a Jenkinsfile, which can be committed to a project’s source control repository. This practice, known as “Pipeline-as-Code,” integrates the CD pipeline into the application itself, allowing it to be versioned and reviewed just like any other code.

Jenkins Pipeline

The below is pipeline for our Microservice to deploy service to ECS with Load Balancer

The below pipeline will (1)checkout the changes from the repository, (2)build the image and (3)pushes the image to Docker hub, the next step is to (4)deploy the changes to ECS.

pipeline {
    agent any
    environment {
        IMAGE_REPO = "jonesjalapatgithub"
        NAME = "hireapro-admin-service"
        VERSION = "${env.BUILD_ID}-${env.GIT_COMMIT}"
        registryCredential = 'dockerhub_id'
    }
    stages {
        stage('Build') { 
            steps {
                echo "Running ${VERSION} on ${env.JENKINS_URL}"
                sh 'mvn -B -DskipTests clean package' 
            }
        }
        stage('Test') {
            steps {
                sh "chmod +x -R ${env.WORKSPACE}"
                sh 'mvn test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }
        stage('Docker Push') { 
            steps {
            echo " Branch ${env.BRANCH_NAME}"
            sh "docker login -u=${DOCKER_USERNAME} -p=${DOCKER_PASSWORD}"
            sh "docker build -t ${NAME} ."
            sh "docker tag ${NAME}:latest ${IMAGE_REPO}/${NAME}:latest"
            sh "docker push ${IMAGE_REPO}/${NAME}:latest"
            }
        }
        stage('Deploy') { 
            steps {
                 sh "delete-stack --stack-name hireapro-admin"
                 sh "aws cloudformation create-stack --capabilities CAPABILITY_NAMED_IAM --stack-name hireapro-admin --template-body file://./ecs.yml"
            }
        }
    }
}

ECS deployment using CloudFormation

CloudFormation is AWS’s service for automating the deployment of AWS resources.

  • The cloudFormation script does the following things
    • Creates VPC
    • Create two public Subnet
    • Create two private Subnet
    • Create Internet Gateway
    • Create Nat Gateway
    • Create Route Table
    • Create Public Loadbalancer
    • Create ECS Cluster

AWSTemplateFormatVersion: '2010-09-09'
Description: AWS core resources to create an ECS cluster spanning public and private subnets. Supports
             public facing load balancers.
Mappings:
  SubnetConfig:
    VPC:
      CIDR: '10.0.0.0/16'
    PublicOne:
      CIDR: '10.0.0.0/24'
    PublicTwo:
      CIDR: '10.0.1.0/24'
    PrivateOne:
      CIDR: '10.0.2.0/24'
    PrivateTwo:
      CIDR: '10.0.3.0/24'
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      EnableDnsSupport: true
      EnableDnsHostnames: true
      CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']

  PublicSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref 'AWS::Region'
      VpcId: !Ref 'VPC'
      CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
      MapPublicIpOnLaunch: true
  PublicSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 1
        - Fn::GetAZs: !Ref 'AWS::Region'
      VpcId: !Ref 'VPC'
      CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
      MapPublicIpOnLaunch: true

  PrivateSubnetOne:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref 'AWS::Region'
      VpcId: !Ref 'VPC'
      CidrBlock: !FindInMap ['SubnetConfig', 'PrivateOne', 'CIDR']
  PrivateSubnetTwo:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select
        - 1
        - Fn::GetAZs: !Ref 'AWS::Region'
      VpcId: !Ref 'VPC'
      CidrBlock: !FindInMap ['SubnetConfig', 'PrivateTwo', 'CIDR']

  InternetGateway:
    Type: AWS::EC2::InternetGateway
  GatewayAttachement:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref 'VPC'
      InternetGatewayId: !Ref 'InternetGateway'
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: GatewayAttachement
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'InternetGateway'
  PublicSubnetOneRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetOne
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetTwoRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetTwo
      RouteTableId: !Ref PublicRouteTable

  NatGatewayOneAttachment:
    Type: AWS::EC2::EIP
    DependsOn: GatewayAttachement
    Properties:
        Domain: vpc
  NatGatewayTwoAttachment:
    Type: AWS::EC2::EIP
    DependsOn: GatewayAttachement
    Properties:
        Domain: vpc
  NatGatewayOne:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayOneAttachment.AllocationId
      SubnetId: !Ref PublicSubnetOne
  NatGatewayTwo:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayTwoAttachment.AllocationId
      SubnetId: !Ref PublicSubnetTwo
  PrivateRouteTableOne:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
  PrivateRouteOne:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableOne
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayOne
  PrivateRouteTableOneAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableOne
      SubnetId: !Ref PrivateSubnetOne
  PrivateRouteTableTwo:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
  PrivateRouteTwo:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTableTwo
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayTwo
  PrivateRouteTableTwoAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTableTwo
      SubnetId: !Ref PrivateSubnetTwo

  ####
  # ALB related resources
  ####
  PublicLoadBalancerSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Access to the public facing load balancer
      VpcId: !Ref 'VPC'
      SecurityGroupIngress:
          - CidrIp: 0.0.0.0/0
            IpProtocol: -1
  PublicLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      LoadBalancerAttributes:
      - Key: idle_timeout.timeout_seconds
        Value: '30'
      Subnets:
        - !Ref 'PublicSubnetOne'
        - !Ref 'PublicSubnetTwo'
      SecurityGroups: [!Ref 'PublicLoadBalancerSG']
  TargetGroupPublic:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 6
      HealthCheckPath: /hello
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      Port: 80
      Protocol: HTTP
      TargetType: ip
      UnhealthyThresholdCount: 2
      VpcId: !Ref 'VPC'
  PublicLoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    DependsOn:
      - PublicLoadBalancer
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref 'TargetGroupPublic'
          Type: 'forward'
      LoadBalancerArn: !Ref 'PublicLoadBalancer'
      Port: 80
      Protocol: HTTP
  ####
  # ECS related resources
  ####
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: 'hireapro-admin-service'
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: hireapro-admin-service-log-group
  ExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: hireapro-admin-service-role
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
  ContainerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: ContainerSecurityGroup
      GroupDescription: Security group for NGINX container
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          CidrIp: 0.0.0.0/0
      VpcId: !Ref 'VPC'
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: hireapro-admin-service-task
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      ExecutionRoleArn: !Ref ExecutionRole
      ContainerDefinitions:
        - Name: hireapro-admin-service-container
          Image: jonesjalapatgithub/hireapro-admin-service:latest
          PortMappings:
            - ContainerPort: 8080
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-region: !Ref AWS::Region
              awslogs-group: !Ref LogGroup
              awslogs-stream-prefix: ecs
      RequiresCompatibilities:
        - EC2
        - FARGATE
  Service:
    Type: AWS::ECS::Service
    DependsOn: PublicLoadBalancerListener
    Properties:
      ServiceName: hireapro-admin-service
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref TaskDefinition
      DesiredCount: 1
      LaunchType: FARGATE
      LoadBalancers:
        - ContainerName: hireapro-admin-service-container
          ContainerPort: 8080
          TargetGroupArn: !Ref TargetGroupPublic
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: ENABLED
          Subnets:
            - !Ref 'PrivateSubnetOne'
            - !Ref 'PrivateSubnetTwo'
          SecurityGroups:
            - !GetAtt ContainerSecurityGroup.GroupId

API Gateway deployment

Jenkins File

This pipeline will checkout the code, Delete the previous stack, create a new stack.

pipeline {
    agent any
    stages {
        stage('Delete Previous-Stack') { 
            steps {
                    sh "aws cloudformation delete-stack --stack-name hireaproapigateway5"
                }
            }
        stage('Release new-Stack') {
            steps {
                    sh "aws cloudformation create-stack --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND --stack-name hireaproapigateway --template-body file://./apigateway.yml"
                }
            }
        }
}

Cloudformation

  • Creates API Gateway
  • Create Method and add API Configuration
  • Create a Stage and deploy
Transform: AWS::Serverless-2016-10-31
Resources:
  ApiGatewayRestApi0010nid2q3na00BIR7p:
    UpdateReplacePolicy: Delete
    Type: AWS::ApiGateway::RestApi
    DeletionPolicy: Delete
    Properties:
      ApiKeySourceType: HEADER
      EndpointConfiguration:
        Types:
          - REGIONAL
      DisableExecuteApiEndpoint: false
      Name: hireapro1

  ResourcePath:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !GetAtt ApiGatewayRestApi0010nid2q3na00BIR7p.RootResourceId
      RestApiId: !Ref ApiGatewayRestApi0010nid2q3na00BIR7p
      PathPart: admin
  Resource:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !GetAtt ResourcePath.ResourceId
      RestApiId: !Ref ApiGatewayRestApi0010nid2q3na00BIR7p
      PathPart: '{proxy+}'

  ApiGatewayMethod:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref ApiGatewayRestApi0010nid2q3na00BIR7p
      ResourceId: !Ref Resource
      HttpMethod: GET
      AuthorizationType: NONE
      RequestParameters:
        method.request.path.proxy: true
      Integration:
        CacheKeyParameters:
          - method.request.path.proxy
        RequestParameters:
          integration.request.path.proxy: method.request.path.proxy
        IntegrationHttpMethod: ANY
        Type: HTTP_PROXY
        Uri: http://hireap-publi-zqxhkhoasadr-1284408898.us-east-2.elb.amazonaws.com/{proxy}
        PassthroughBehavior: WHEN_NO_MATCH
        IntegrationResponses:
          - StatusCode: 200

  ApiGatewayStage00prod00Dbe25:
    UpdateReplacePolicy: Delete
    Type: AWS::ApiGateway::Stage
    DeletionPolicy: Delete
    Properties:
      RestApiId: !Ref ApiGatewayRestApi0010nid2q3na00BIR7p
      DeploymentId: !GetAtt ApiGatewayDeployment00pga0oi00ACNil.DeploymentId
      StageName: prod
      CacheClusterSize: '0.5'
      TracingEnabled: false
      CacheClusterEnabled: false
  ApiGatewayDeployment00pga0oi00ACNil:
    UpdateReplacePolicy: Delete
    DependsOn:
      - ApiGatewayMethod
    Type: AWS::ApiGateway::Deployment
    DeletionPolicy: Delete
    Properties:
      RestApiId: !Ref ApiGatewayRestApi0010nid2q3na00BIR7p

Testing

Get the API gateway URL and hit the url on the browser

Github

  • https://github.com/jonesjalapatgithub/hireapro-admin-service
  • https://github.com/jonesjalapatgithub/hireaprogateway/





error: Content is protected !!
Scroll to Top