How Do I Deploy a Containerized Application on AWS ECS With a PostgreSQL Backend Using Pulumi?
Introduction
Deploying a containerized application on AWS ECS with a PostgreSQL backend involves orchestrating several AWS resources to ensure seamless integration and operation. The primary goal of this deployment is to establish a robust infrastructure that supports running containerized applications while maintaining a reliable database backend. This guide outlines the step-by-step process to achieve this using Pulumi, focusing on setting up networking, security, and compute resources necessary for the application and database.
Implementation Steps
- VPC and Networking: Start by creating a VPC and subnets required for ECS and RDS.
- Security Groups: Define security groups to allow communication between ECS tasks and the RDS instance.
- IAM Roles and Policies: Create the necessary IAM roles for ECS task execution.
- ECS Cluster and Task Definition: Set up the ECS cluster and associate a Task Definition that defines the container resources.
- RDS PostgreSQL: Deploy an RDS PostgreSQL instance with the necessary security and networking configurations.
- ECS Service: Create an ECS service to run the task using the task definition.
Code Example
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// VPC and Subnets
const main = new aws.ec2.Vpc("main", {cidrBlock: "10.0.0.0/16"});
const subnet1 = new aws.ec2.Subnet("subnet1", {
vpcId: main.id,
cidrBlock: "10.0.1.0/24",
availabilityZone: "us-west-2a",
});
const subnet2 = new aws.ec2.Subnet("subnet2", {
vpcId: main.id,
cidrBlock: "10.0.2.0/24",
availabilityZone: "us-west-2b",
});
const ecsSg = new aws.ec2.SecurityGroup("ecs_sg", {
vpcId: main.id,
ingress: [{
fromPort: 80,
toPort: 80,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
}],
egress: [{
fromPort: 0,
toPort: 0,
protocol: "-1",
cidrBlocks: ["0.0.0.0/0"],
}],
});
const rdsSg = new aws.ec2.SecurityGroup("rds_sg", {
vpcId: main.id,
ingress: [{
fromPort: 5432,
toPort: 5432,
protocol: "tcp",
cidrBlocks: ["10.0.0.0/16"],
}],
egress: [{
fromPort: 0,
toPort: 0,
protocol: "-1",
cidrBlocks: ["0.0.0.0/0"],
}],
});
// IAM roles and policies for ECS
const ecsTaskExecutionRole = new aws.iam.Role("ecs_task_execution_role", {
name: "ecsTaskExecutionRole",
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Effect: "Allow",
Principal: {
Service: "ecs-tasks.amazonaws.com",
},
}],
}),
});
const ecsTaskExecutionRolePolicy = new aws.iam.RolePolicyAttachment("ecs_task_execution_role_policy", {
role: ecsTaskExecutionRole.name,
policyArn: "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
});
// ECS Cluster
const mainCluster = new aws.ecs.Cluster("main", {name: "main-ecs-cluster"});
const app = new aws.ecs.TaskDefinition("app", {
family: "app-task",
requiresCompatibilities: ["FARGATE"],
networkMode: "awsvpc",
cpu: "256",
memory: "512",
executionRoleArn: ecsTaskExecutionRole.arn,
containerDefinitions: JSON.stringify([{
name: "app",
image: "nginx:latest",
portMappings: [{
containerPort: 80,
hostPort: 80,
}],
}]),
});
const mainSubnetGroup = new aws.rds.SubnetGroup("main", {
name: "main-subnet-group",
subnetIds: [
subnet1.id,
subnet2.id,
],
});
// RDS PostgreSQL Instance
const postgresql = new aws.rds.Instance("postgresql", {
allocatedStorage: 20,
engine: "postgres",
engineVersion: "12.4",
instanceClass: aws.rds.InstanceType.T3_Micro,
name: "mydatabase",
username: "username",
password: "password",
parameterGroupName: "default.postgres12",
skipFinalSnapshot: true,
dbSubnetGroupName: mainSubnetGroup.name,
vpcSecurityGroupIds: [rdsSg.id],
});
// ECS Service
const appService = new aws.ecs.Service("app", {
name: "app-service",
cluster: mainCluster.id,
taskDefinition: app.arn,
desiredCount: 1,
launchType: "FARGATE",
networkConfiguration: {
subnets: [
subnet1.id,
subnet2.id,
],
securityGroups: [ecsSg.id],
assignPublicIp: true,
},
});
export const vpcId = main.id;
export const ecsClusterId = mainCluster.id;
export const ecsServiceName = appService.name;
export const rdsEndpoint = postgresql.endpoint;
Summary
This deployment process establishes an AWS ECS Fargate cluster to run a containerized application, backed by an RDS PostgreSQL instance for database services. Key components such as VPC, subnets, security groups, IAM roles, and policies are set up to ensure secure and efficient communication between the application and database. The ECS service deploys the application container, connecting it to the database backend while maintaining secure network interactions. The stack outputs provide easy access to essential resource identifiers and endpoints, facilitating further management and monitoring.
Deploy this code
Want to deploy this code? Sign up for a free Pulumi account to deploy in a few clicks.
Sign upNew to Pulumi?
Want to deploy this code? Sign up with Pulumi to deploy in a few clicks.
Sign upThank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.