I have changed my CDK deployment code to make it more modular. And so I have moved the Task definition and FargateService code into a separate class EcsService. After making these changes, the stack deployment is stuck due to ECS. And the reason is that the taskdef is not able to fetch the image due to some permission or network issue. The error is shown below. And my old and new code are below the error message.
Error
Task stopped at: 2023-08-31T05:55:55.882Z
ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve ecr registry auth: service call has been retried 3 time(s): RequestError: send request failed caused by: Post "https://api.ecr.us-east-1.amazonaws.com/": dial tcp 44.213.79.50:443: i/o timeout. Please check your task network configuration.
Old Code
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(3000));
// Validation
if (!envJSON.ssdDockerImageTag ) {
throw new Error('Missing ssd-fe image tag.');
}
const cluster = new ecs.Cluster(this, "ssdCluster", { vpc });
// Define the task definition with a container using an image from ECR
const taskDefinition = new ecs.FargateTaskDefinition(this, 'ssdTaskDef');
const container = taskDefinition.addContainer('ssdContainer', {
image: ecs.ContainerImage.fromEcrRepository(
ecr.Repository.fromRepositoryName(this, 'ssdRepo', 'ssd-fe'),
envJSON.ssdDockerImageTag),
memoryLimitMiB: 512,
cpu: 256,
portMappings: [{
containerPort: 3000
}],
environment: {
NODE_ENV: "production",
API_BASE_URL: api.url
}
});
// Create the Fargate Service
const service = new ecs.FargateService(this, 'ssdService', {
cluster,
taskDefinition,
desiredCount: 1,
vpcSubnets: {
subnetType: ec2.SubnetType.PUBLIC,
},
securityGroups: [securityGroup],
assignPublicIp: true,
});
LoadBalancer.getInstance(this, 'LoadBalancer', {
vpc,
ecsService: service,
});
New code
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(3000));
// Validation
if (!envJSON.ssdDockerImageTag ) {
throw new Error('Missing ssd-fe image tag.');
}
const cluster = new ecs.Cluster(this, "ssdCluster", { vpc });
// Create ECS Service
const ecsService = new EcsService(this, 'ssdService', {
vpc,
securityGroup: securityGroup,
cluster: cluster,
repoName: 'ssd-fe',
imageTag: envJSON.ssdDockerImageTag,
environment: {
NODE_ENV: "production",
API_BASE_URL: api.url
}
});
LoadBalancer.getInstance(this, 'LoadBalancer', {
vpc,
ecsService: ecsService.service,
});
// EcsService class
import * as cdk from 'aws-cdk-lib';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
interface EcsServiceProps {
vpc: ec2.IVpc;
securityGroup: ec2.ISecurityGroup;
cluster: ecs.ICluster;
repoName: string;
imageTag: string;
environment?: { [key: string]: string };
}
export class EcsService extends Construct {
public readonly service: ecs.FargateService;
constructor(scope: Construct, id: string, props: EcsServiceProps) {
super(scope, id);
const ecrRepository = ecr.Repository.fromRepositoryName(this, `${id}Repo`, props.repoName);
const taskDefinition = new ecs.FargateTaskDefinition(this, `${id}TaskDef`);
taskDefinition.addContainer(`${id}Container`, {
image: ecs.ContainerImage.fromEcrRepository(ecrRepository, props.imageTag),
memoryLimitMiB: 512,
cpu: 256,
portMappings: [{ containerPort: 3000 }],
environment: props.environment,
});
this.service = new ecs.FargateService(this, id, {
cluster: props.cluster,
taskDefinition,
desiredCount: 1,
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
securityGroups: [props.securityGroup],
});
}
}
IAM Statement Changes
┌───┬───────────────────────────────────────┬────────┬───────────────────────────────────────┬───────────────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ - │ * │ Allow │ ecr:GetAuthorizationToken │ AWS:${ssdTaskDefExecutionRole469C7625 │ │
│ │ │ │ │ } │ │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ - │ arn:aws:ecr:us-east-1:533732470418:re │ Allow │ ecr:BatchCheckLayerAvailability │ AWS:${ssdTaskDefExecutionRole469C7625 │ │
│ │ pository/ssd-fe │ │ ecr:BatchGetImage │ } │ │
│ │ │ │ ecr:GetDownloadUrlForLayer │ │ │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ + │ ${ssdService/ssdServiceTaskDef/Execut │ Allow │ sts:AssumeRole │ Service:ecs-tasks.amazonaws.com │ │
│ │ ionRole.Arn} │ │ │ │ │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ + │ ${ssdService/ssdServiceTaskDef/TaskRo │ Allow │ sts:AssumeRole │ Service:ecs-tasks.amazonaws.com │ │
│ │ le.Arn} │ │ │ │ │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ + │ * │ Allow │ ecr:GetAuthorizationToken │ AWS:${ssdService/ssdServiceTaskDef/Ex │ │
│ │ │ │ │ ecutionRole} │ │
├───┼───────────────────────────────────────┼────────┼───────────────────────────────────────┼───────────────────────────────────────┼───────────┤
│ + │ arn:aws:ecr:us-east-1:533732470418:re │ Allow │ ecr:BatchCheckLayerAvailability │ AWS:${ssdService/ssdServiceTaskDef/Ex │ │
│ │ pository/ssd-fe │ │ ecr:BatchGetImage │ ecutionRole} │ │
│ │ │ │ ecr:GetDownloadUrlForLayer │ │ │
└───┴───────────────────────────────────────┴────────┴───────────────────────────────────────┴───────────────────────────────────────┴───────────┘
ChatGPT suggested that I explicitly, add permissions in the EcsService class so I made the following changes. But even after these changes the error remains the same.
// Create an execution role
const executionRole = new iam.Role(this, 'ExecutionRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});
// Grant permissions to the execution role to pull from ECR
executionRole.addToPolicy(new iam.PolicyStatement({
actions: [
'ecr:GetAuthorizationToken'
],
resources: ['*'],
}));
const ecrRepository = ecr.Repository.fromRepositoryName(this, `${id}Repo`, props.repoName);
const taskDefinition = new ecs.FargateTaskDefinition(this, `${id}TaskDef`, {
executionRole: executionRole
});
How can I fix this issue?