Skip to content

EC2 capacity

Path-owned compute for ${NAME}-ec2. Confirm shared vars from Setup are still in this shell ($NAME, $VPC_ID, $PRIVATE_SUBNET_A, $EXEC_ROLE_ARN is not used here — the instance role is separate).

This page gives the EC2 path its compute. Fargate had none of this — on Fargate, AWS runs the host. Here you create an instance role, launch an ECS-optimized instance in a private subnet, and wait for it to register as capacity on the cluster.

1Instance rolerole + instance profile
2Cluster + SGcreate before boot
3Launch instanceprivate subnet · user data
4Registerscapacity on the cluster
Terminal window
export PATH_NAME="${NAME}-ec2"
export CLUSTER="$PATH_NAME"
export GATEWAY="$PATH_NAME"
echo "path ready: CLUSTER=$CLUSTER GATEWAY=$GATEWAY"
path ready: CLUSTER=ecs-gw-lab-ec2 GATEWAY=ecs-gw-lab-ec2

ECS container instances need an IAM role trusted by ec2.amazonaws.com with AmazonEC2ContainerServiceforEC2Role.

Terminal window
aws iam create-role \
--role-name "${NAME}-ec2" \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}' \
--query 'Role.RoleName' --output text
ecs-gw-lab-ec2
Terminal window
aws iam attach-role-policy \
--role-name "${NAME}-ec2" \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
echo "instance role policy attached"
instance role policy attached
Terminal window
aws iam create-instance-profile --instance-profile-name "${NAME}-ec2" \
--query 'InstanceProfile.InstanceProfileName' --output text
ecs-gw-lab-ec2
Terminal window
aws iam add-role-to-instance-profile \
--instance-profile-name "${NAME}-ec2" \
--role-name "${NAME}-ec2"
echo "role added to instance profile"
role added to instance profile

IAM is eventually consistent — wait before run-instances:

Terminal window
sleep 10
echo "ready to launch"
ready to launch

Private-subnet instance; no inbound rules required for this lab (tasks use their own ENIs).

Terminal window
EC2_SG_ID=$(aws ec2 create-security-group \
--group-name "${NAME}-ec2" \
--description "ECS container instances for ${NAME}-ec2" \
--vpc-id "$VPC_ID" \
--query GroupId --output text)
export EC2_SG_ID
echo "EC2_SG_ID set (sg-<id>)"
EC2_SG_ID set (sg-<id>)

The ECS agent needs the cluster name at boot (ECS_CLUSTER in user data).

Terminal window
aws ecs create-cluster --cluster-name "$CLUSTER" \
--query 'cluster.{name:clusterName,status:status}'
{
"name": "ecs-gw-lab-ec2",
"status": "ACTIVE"
}
Terminal window
AMI_ID=$(aws ssm get-parameters \
--names /aws/service/ecs/optimized-ami/amazon-linux-2023/recommended/image_id \
--query 'Parameters[0].Value' --output text)
export AMI_ID
echo "AMI_ID set (ami-<id>)"
AMI_ID set (ami-<id>)

Pins the agent to this path’s cluster:

Terminal window
cat > /tmp/${PATH_NAME}-userdata.sh <<EOF
#!/bin/bash
echo ECS_CLUSTER=${CLUSTER} >> /etc/ecs/ecs.config
EOF
wc -l /tmp/${PATH_NAME}-userdata.sh
2 /tmp/ecs-gw-lab-ec2-userdata.sh

Place the instance in a private subnet (no public IP). Outbound ECS agent and image traffic uses the NAT gateway. t3.medium leaves ENI headroom for awsvpc tasks.

Terminal window
INSTANCE_ID=$(aws ec2 run-instances \
--image-id "$AMI_ID" \
--instance-type t3.medium \
--iam-instance-profile "Name=${NAME}-ec2" \
--subnet-id "$PRIVATE_SUBNET_A" \
--security-group-ids "$EC2_SG_ID" \
--no-associate-public-ip-address \
--user-data "file:///tmp/${PATH_NAME}-userdata.sh" \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=${PATH_NAME}}]" \
--query 'Instances[0].InstanceId' --output text)
export INSTANCE_ID
echo "INSTANCE_ID set (i-<id>)"
INSTANCE_ID set (i-<id>)
Terminal window
aws ec2 wait instance-running --instance-ids "$INSTANCE_ID"
aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \
--query 'Reservations[0].Instances[0].{state:State.Name,type:InstanceType,az:Placement.AvailabilityZone}'
{
"state": "running",
"type": "t3.medium",
"az": "ap-southeast-6a"
}
Terminal window
for i in $(seq 1 36); do
COUNT=$(aws ecs list-container-instances --cluster "$CLUSTER" \
--query 'length(containerInstanceArns)' --output text)
echo "container instances: $COUNT"
[ "$COUNT" != "0" ] && break
sleep 10
done
container instances: 0
container instances: 0
container instances: 1

Confirm without printing ARNs (they embed the account ID):

Terminal window
aws ecs list-container-instances --cluster "$CLUSTER" \
--query 'length(containerInstanceArns)' --output text
1

EC2 gateway