DEV Community

Uendi Hoxha
Uendi Hoxha

Posted on

Containerization and Deployment Using Amazon ECS and Fargate

Amazon Elastic Container Service (ECS) is a fully managed container orchestration service that simplifies the deployment and management of containerized applications. AWS Fargate is a serverless compute engine for containers that works with ECS, allowing you to run containers without managing the underlying infrastructure.

In this article, I will explore how to use ECS and Fargate for deploying a sample application while integrating Amazon RDS for database management, using AWS KMS and Secrets Manager to securely handle sensitive information and managing Docker images with Amazon ECR.

Image description

I. Setting Up Your Development Environment

To get started, let’s create a simple application that we will containerize and deploy. For this example, we will use a Node.js application with a PostgreSQL database.
Example of simple app.js:

const express = require('express');
const mysql = require('mysql');
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();

const app = express();
const port = process.env.PORT || 3000;

async function getDatabaseCredentials() {
    const data = await secretsManager.getSecretValue({ SecretId: 'RDSMasterUserSecret' }).promise();
    return JSON.parse(data.SecretString);
}

app.get('/', async (req, res) => {
    const secret = await getDatabaseCredentials();
    const connection = mysql.createConnection({
        host: 'your-rds-endpoint', // Replace this with your actual RDS endpoint after creation
        user: secret.username,
        password: secret.password,
        database: 'mydatabase'
    });

    connection.query('SELECT * FROM mytable', (error, results) => {
        if (error) throw error;
        res.json(results);
    });
});

app.listen(port, () => {
    console.log(`Server running at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Initialize package.json and Install Dependencies

Command to initialize package.json: npm init -y
Command to install dependencies: npm install express mysql

II. Writing Dockerfile

Next step is creating a Dockerfile to containerize our application:

# Use the official Node.js image
FROM node:14

# Set the working directory
WORKDIR /usr/src/app

# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install

# Copy the application code
COPY . .

# Expose the application port
EXPOSE 3000

# Command to run the application
CMD ["node", "app.js"]
Enter fullscreen mode Exit fullscreen mode

III. Configuring AWS KMS and Secrets Manager

To securely manage your database credentials, we will be using AWS Secrets Manager and KMS.

  • Create a KMS key (as shown in the CloudFormation template below).
  • Store RDS credentials in Secrets Manager (as included in the template).

IV. Setting Up Amazon RDS

Create a template.yaml file for your CloudFormation setup, which includes RDS configuration.

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  MyKMSKey:
    Type: AWS::KMS::Key
    Properties:
      KeyPolicy:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: arn:aws:iam::<your-account-id>:root
            Action: "kms:*"
            Resource: "*"

  MySecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: RDSMasterUserSecret
      Description: RDS Master User Credentials
      SecretString: !Sub |
        {
          "username": "${MasterUsername}",
          "password": "${MasterUserPassword}"
        }
      KmsKeyId: !Ref MyKMSKey

  MyDBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: mydbinstance
      AllocatedStorage: 20
      DBInstanceClass: db.t2.micro
      Engine: mysql
      MasterUsername: !Join [ "", [ !GetAtt MySecret.SecretString, "username" ] ]
      MasterUserPassword: !Join [ "", [ !GetAtt MySecret.SecretString, "password" ] ]
      DBName: mydatabase
      VPCSecurityGroups:
        - !GetAtt MyDBSecurityGroup.GroupId
Enter fullscreen mode Exit fullscreen mode

Run the following command in your terminal to deploy the stack, ensuring you have the AWS CLI configured. The template.yaml file includes parameters such as MasterUsername and MasterUserPassword, which you must define in the command when deploying the stack. Here’s how to pass these parameters during deployment:

aws cloudformation create-stack --stack-name my-stack --template-body file://template.yaml --parameters ParameterKey=MasterUsername,ParameterValue=admin ParameterKey=MasterUserPassword,ParameterValue=mypassword --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

These parameters will be used to create the RDS instance and store the credentials securely in AWS Secrets Manager.

V. Building and Pushing Docker Images

Now that we have our Dockerfile ready, let’s build and push our Docker image to ECR.

Step 1: Create an ECR Repository
First, log in to your AWS Management Console and navigate to ECR. Create a new repository named my-ecs-app.

Step 2: Authenticate Docker to ECR
Run the following command to authenticate Docker with your ECR registry (replace REGION with your AWS region):

aws ecr get-login-password --region REGION | docker login --username AWS --password-stdin <your_account_id>.dkr.ecr.<REGION>.amazonaws.com
Enter fullscreen mode Exit fullscreen mode

Step 3: Build the Docker Image
Run the following command to build your Docker image:

docker build -t my-ecs-app .
Enter fullscreen mode Exit fullscreen mode

Step 4: Tag and Push the Image
Tag the image for your ECR repository:

docker tag my-ecs-app:latest <your_account_id>.dkr.ecr.<REGION>.amazonaws.com/my-ecs-app:latest
Enter fullscreen mode Exit fullscreen mode

Step 5: Push the image to ECR

docker push <your_account_id>.dkr.ecr.<REGION>.amazonaws.com/my-ecs-app:latest
Enter fullscreen mode Exit fullscreen mode

VI. Deploying with Amazon ECS and Fargate

Define a task in ECS that references your Docker image stored in ECR. Make sure the image parameter matches the name of the repository you created in ECR:

{
  "family": "my-node-app",
  "containerDefinitions": [
    {
      "name": "my-node-app",
      "image": "<your-account-id>.dkr.ecr.<region>.amazonaws.com/my-node-app:latest",
      "essential": true,
      "memory": 512,
      "cpu": 256,
      "portMappings": [
        {
          "containerPort": 3000,
          "hostPort": 3000
        }
      ],
      "environment": [
        {
          "name": "PORT",
          "value": "3000"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

You can now run your ECS task on Fargate, which manages the compute resources for you.

aws ecs create-service --cluster my-cluster --service-name my-node-app --task-definition my-node-app --desired-count 1 --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[<subnet-id>],securityGroups=[<security-group-id>],assignPublicIp='ENABLED'}"
Enter fullscreen mode Exit fullscreen mode

VII. Permissions and IAM Roles

Last, but not least we have permissions. For the successful deployment of your application using Amazon ECS, RDS and Secrets Manager we must ensure the following IAM roles and permissions are configured:

a. IAM Role for ECS Task Execution
Role Name: ECS-Task-Execution-Role
Permissions:

  • AmazonECSTaskExecutionRolePolicy (Allows ECS to pull images from ECR)
  • SecretsManagerReadWrite (Allows access to AWS Secrets Manager)

b. IAM Role for RDS Access
Role Name: RDS-Access-Role
Custom Permissions:

  • rds:DescribeDBInstances
  • rds:CreateDBInstance (To create a new RDS instance)
  • rds:DeleteDBInstance (To delete an existing RDS instance)
  • rds:ModifyDBInstance (To modify settings of an RDS instance)

c. Amazon ECR Permissions

  • ecr:GetAuthorizationToken (Required to authenticate Docker with Amazon ECR)
  • ecr:BatchCheckLayerAvailability (To check layers for Docker images)
  • ecr:GetDownloadUrlForLayer (To download layers of Docker images)
  • ecr:BatchGetImage (To retrieve Docker images

d. VPC Permissions (RDS and ECS should be within a VPC)

  • ec2:DescribeVpcs (To describe VPCs)
  • ec2:DescribeSubnets (To describe subnets)
  • ec2:DescribeSecurityGroups (To describe security groups)
  • ec2:CreateNetworkInterface (If using awsvpc network mode)

e. CloudFormation Permissions

  • cloudformation:CreateStack
  • cloudformation:UpdateStack
  • cloudformation:DescribeStacks
  • cloudformation:DeleteStack

Adhere to the Principle of Least Privilege! Ensure that you grant only the permissions that are absolutely necessary for users or services to perform their required tasks.

Top comments (0)