diff --git a/13-ECS/Practice-13.1/commands.txt b/13-ECS/Practice-13.1/commands.txt new file mode 100644 index 00000000..8f1a27b2 --- /dev/null +++ b/13-ECS/Practice-13.1/commands.txt @@ -0,0 +1,14 @@ +#LOGIN USING get-login-password +aws ecr get-login-password --region us-east-1 --profile labs | docker login --username AWS --password-stdin 324320755747.dkr.ecr.us-east-1.amazonaws.com + +docker tag nginx:latest 324320755747.dkr.ecr.us-east-1.amazonaws.com/ecr:latest + +docker push 324320755747.dkr.ecr.us-east-1.amazonaws.com/ecr:latest + +# LOGIN USING get-authorization-token +TOKEN=$(aws ecr get-authorization-token --profile labs --output text --query 'authorizationData[].authorizationToken') + +curl -i -H "Authorization: Basic $TOKEN" https://324320755747.dkr.ecr.us-east-1.amazonaws.com/v2/ecr/tags/list + +# Run the image +docker run --rm -it 324320755747.dkr.ecr.us-east-1.amazonaws.com/ecr:latest diff --git a/13-ECS/Practice-13.1/ecr.yml b/13-ECS/Practice-13.1/ecr.yml new file mode 100644 index 00000000..3d6f5557 --- /dev/null +++ b/13-ECS/Practice-13.1/ecr.yml @@ -0,0 +1,63 @@ +Description: An ECR Repository + +Parameters: + repositoryName: + Description: Name for the ECR Repository + Type: String + +Resources: + MyRepository: + Type: AWS::ECR::Repository + Properties: + RepositoryName: !Sub ${repositoryName} + ImageScanningConfiguration: + ScanOnPush: true + RepositoryPolicyText: + Version: "2012-10-17" + Statement: + - + Sid: AllowPushPull + Effect: Allow + Principal: + AWS: + - !Sub "arn:aws:iam::${AWS::AccountId}:user/desmond.ndambi.labs" + Action: + - "ecr:GetDownloadUrlForLayer" + - "ecr:BatchGetImage" + - "ecr:BatchCheckLayerAvailability" + - "ecr:PutImage" + - "ecr:InitiateLayerUpload" + - "ecr:UploadLayerPart" + - "ecr:CompleteLayerUpload" + LifecyclePolicy: + LifecyclePolicyText: | + { + "rules": [ + { + "rulePriority": 2, + "description": "Expire images after a month", + "selection": { + "tagStatus": "any", + "countType": "sinceImagePushed", + "countUnit": "days", + "countNumber": 30 + }, + "action": { "type": "expire" } + }, + { + "rulePriority": 1, + "description": "Keeps only one untagged image", + "selection": { + "tagStatus": "untagged", + "countType": "imageCountMoreThan", + "countNumber": 1 + }, + "action": { "type": "expire" } + } + ] + } + +Outputs: + MyRepository: + Description: The URI of the Repository + Value: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${repositoryName}" diff --git a/13-ECS/Practice-13.1/parameter.json b/13-ECS/Practice-13.1/parameter.json new file mode 100644 index 00000000..f7b9d52f --- /dev/null +++ b/13-ECS/Practice-13.1/parameter.json @@ -0,0 +1,6 @@ +[ + { + "ParameterKey": "repositoryName", + "ParameterValue": "ecr" + } +] \ No newline at end of file diff --git a/13-ECS/Practice-13.2/ecs-ec2.yml b/13-ECS/Practice-13.2/ecs-ec2.yml new file mode 100644 index 00000000..7769a129 --- /dev/null +++ b/13-ECS/Practice-13.2/ecs-ec2.yml @@ -0,0 +1,356 @@ +Description: An ECS EC2 Cluster + +Parameters: + Name: + Description: Name of the Repository + Type: String + LatestAmiId: + Description: Region specific image from the Parameter Store + Type: 'AWS::SSM::Parameter::Value' + Default: '/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id' + InstanceType: + Description: Amazon EC2 instance type for the instances + Type: String + AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, t2.xlarge, t3.micro, t3.small, + t3.medium, t3.large, t3.xlarge, m4.large, m4.xlarge, m5.large, m5.xlarge, c4.large, + c4.xlarge, c5.large, c5.xlarge, r4.large, r4.xlarge, r5.large, r5.xlarge, i3.large, + i3.xlarge] + Default: t2.micro + DesiredCapacity: + Type: Number + Default: '1' + Description: Number of instances to launch in your ECS cluster. + MaxSize: + Type: Number + Default: '2' + Description: Maximum number of instances that can be launched in your ECS cluster. + ImageName: + Description: The name of the image in AWS ECR + Type: String + Default: ecr + ImageTag: + Type: String + Description: The image tag (e.g, 2.xx, latest) + Default: latest + +Mappings: + SubnetConfig: + VPC: + CIDR: '10.0.0.0/16' + PublicSubnet1: + CIDR: '10.0.0.0/24' + PublicSubnet2: + CIDR: '10.0.1.0/24' + +Resources: + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] + EnableDnsHostnames: true + EnableDnsSupport: true + PublicSubnetOne: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: {Ref: 'AWS::Region'} + VpcId: !Ref 'VPC' + CidrBlock: !FindInMap ['SubnetConfig', 'PublicSubnet1', 'CIDR'] + MapPublicIpOnLaunch: true + PublicSubnetTwo: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: {Ref: 'AWS::Region'} + VpcId: !Ref 'VPC' + CidrBlock: !FindInMap ['SubnetConfig', 'PublicSubnet2', 'CIDR'] + MapPublicIpOnLaunch: true + 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 + + # ECS Cluster Resources + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: !Sub "${Name}-arnoldrx" + LBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS Security Group + VpcId: !Ref 'VPC' + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + LBSecurityGroupEgress: + Type: AWS::EC2::SecurityGroupEgress + Properties: + CidrIp: 0.0.0.0/0 + Description: Allow all outbound traffic by default + IpProtocol: "-1" + GroupId: + Fn::GetAtt: + - LBSecurityGroup + - GroupId + ServiceSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS/MyApp/Service/SecurityGroup + VpcId: !Ref VPC + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + Description: Allow all outbound traffic by default + IpProtocol: "-1" + ServiceSecurityGroupIngress: + Type: AWS::EC2::SecurityGroupIngress + Properties: + IpProtocol: tcp + Description: Load balancer to target + FromPort: 80 + GroupId: + Fn::GetAtt: + - ServiceSecurityGroup + - GroupId + SourceSecurityGroupId: + Fn::GetAtt: + - LBSecurityGroup + - GroupId + ToPort: 80 + ECSALB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Scheme: internet-facing + Type: application + Subnets: + - !Ref PublicSubnetOne + - !Ref PublicSubnetTwo + SecurityGroups: + - Fn::GetAtt: + - LBSecurityGroup + - GroupId + DependsOn: + - PublicRoute + LBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - TargetGroupArn: !Ref LBTargetGroup + Type: forward + LoadBalancerArn: !Ref ECSALB + Port: 80 + Protocol: HTTP + LBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + Port: 80 + Protocol: HTTP + TargetType: instance + VpcId: !Ref VPC + TaskDefTaskRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Version: "2012-10-17" + TaskDefExecutionRole: + Type: AWS::IAM::Role + Properties: + RoleName: "TaskDefExecutionRole" + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Version: "2012-10-17" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" + EC2Role: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [ec2.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + Policies: + - PolicyName: ecs-service + PolicyDocument: + Statement: + - Effect: Allow + Action: ['ecs:CreateCluster', 'ecs:DeregisterContainerInstance', 'ecs:DiscoverPollEndpoint', + 'ecs:Poll', 'ecs:RegisterContainerInstance', 'ecs:StartTelemetrySession', + 'ecs:Submit*', 'logs:CreateLogStream', 'logs:PutLogEvents', 'ecr:*'] + Resource: '*' + EC2InstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Path: / + Roles: [!Ref 'EC2Role'] + EcsServiceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: [ecs.amazonaws.com] + Action: ['sts:AssumeRole'] + Path: / + Policies: + - PolicyName: "ecs-service-policy" + PolicyDocument: + Statement: + - Effect: Allow + Action: [ + 'elasticloadbalancing:Describe*', + 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer', + 'elasticloadbalancing:RegisterInstancesWithLoadBalancer', + 'elasticloadbalancing:RegisterTargets', + 'ec2:Describe*', + 'ec2:AuthorizeSecurityGroupIngress' + ] + Resource: '*' + ContainerInstances: + Type: AWS::EC2::LaunchTemplate + Properties: + LaunchTemplateData: + ImageId: !Ref LatestAmiId + SecurityGroupIds: + - !GetAtt LBSecurityGroup.GroupId + InstanceType: !Ref InstanceType + IamInstanceProfile: + Arn: !GetAtt EC2InstanceProfile.Arn + UserData: + Fn::Base64: !Sub | + #!/bin/bash + echo ECS_CLUSTER=${Name}-desmond >> /etc/ecs/ecs.config; + ECSAutoScalingGroup: + Type: AWS::AutoScaling::AutoScalingGroup + Properties: + AutoScalingGroupName: "ASG" + VPCZoneIdentifier: + - Ref: PublicSubnetOne + - Ref: PublicSubnetTwo + LaunchTemplate: + LaunchTemplateId: !Ref ContainerInstances + Version: !GetAtt ContainerInstances.LatestVersionNumber + MinSize: '0' + MaxSize: !Ref MaxSize + DesiredCapacity: !Ref DesiredCapacity + ECSCluster: + Type: AWS::ECS::Cluster + DependsOn: + - CapacityProvider + Properties: + ClusterName: !Sub "${Name}-desmond" + CapacityProviders: + - !Ref CapacityProvider + Tags: + - Key: "Name" + Value: "desmond.ndambi.labs" + CapacityProvider: + Type: AWS::ECS::CapacityProvider + Properties: + Name: "CapacityProvider" + AutoScalingGroupProvider: + AutoScalingGroupArn: !Ref ECSAutoScalingGroup + ManagedScaling: + MaximumScalingStepSize: 5 + MinimumScalingStepSize: 1 + Status: ENABLED + TargetCapacity: 100 + TaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + RequiresCompatibilities: + - "EC2" + ContainerDefinitions: + - Name: "nginx" + Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ImageName}:${ImageTag}" + Cpu: "128" + PortMappings: + - ContainerPort: "80" + Protocol: "tcp" + HostPort: "80" + Memory: "256" + Essential: "true" + ExecutionRoleArn: !Ref TaskDefExecutionRole + NetworkMode: "bridge" + Cpu: "256" + Memory: "512" + RuntimePlatform: + { + "OperatingSystemFamily": "LINUX", + "CpuArchitecture": "X86_64" + } + ECSService: + Type: AWS::ECS::Service + DependsOn: + - ECSALB + - LBListener + - ECSCluster + Properties: + ServiceName: "ECSService" + Cluster: !Ref ECSCluster + HealthCheckGracePeriodSeconds: 200 + DeploymentConfiguration: + DeploymentCircuitBreaker: + { + "Enable" : "true", + "Rollback" : "true" + } + MaximumPercent: "200" + MinimumHealthyPercent: "100" + DesiredCount: "1" + LoadBalancers: + - ContainerName: "nginx" + ContainerPort: "80" + TargetGroupArn: !Ref LBTargetGroup + TaskDefinition: !Ref TaskDefinition + Role: !Ref EcsServiceRole + +Outputs: + WebAppLB: + Description: Web Application Load Balancer + Value: !Join [ "", [ 'http://', !GetAtt ECSALB.DNSName ] ] + Export: + Name: !Sub ${Name}-LB + diff --git a/13-ECS/Practice-13.2/parameter.json b/13-ECS/Practice-13.2/parameter.json new file mode 100644 index 00000000..98c83e13 --- /dev/null +++ b/13-ECS/Practice-13.2/parameter.json @@ -0,0 +1,6 @@ +[ + { + "ParameterKey": "Name", + "ParameterValue": "ecs" + } +] diff --git a/13-ECS/Practice-13.3/ecs-fargate.yml b/13-ECS/Practice-13.3/ecs-fargate.yml new file mode 100644 index 00000000..0ec2138e --- /dev/null +++ b/13-ECS/Practice-13.3/ecs-fargate.yml @@ -0,0 +1,252 @@ +Description: An ECS Fargate Cluster + +Parameters: + Name: + Description: Name of the Repository + Type: String + ECRRepositoryName: + Description: The name of the repository on Github + Type: String + Default: ecr + ImageTag: + Description: The Image tag in the repoitory (e.g 2.xx, latest) + Type: String + Default: latest + +Mappings: + SubnetConfig: + VPC: + CIDR: '10.0.0.0/16' + PublicSubnet1: + CIDR: '10.0.0.0/24' + PublicSubnet2: + CIDR: '10.0.1.0/24' + +Resources: + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR'] + EnableDnsHostnames: true + EnableDnsSupport: true + PublicSubnetOne: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: {Ref: 'AWS::Region'} + VpcId: !Ref 'VPC' + CidrBlock: !FindInMap ['SubnetConfig', 'PublicSubnet1', 'CIDR'] + MapPublicIpOnLaunch: true + PublicSubnetTwo: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: {Ref: 'AWS::Region'} + VpcId: !Ref 'VPC' + CidrBlock: !FindInMap ['SubnetConfig', 'PublicSubnet2', 'CIDR'] + MapPublicIpOnLaunch: true + 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 + + # ECS Cluster Resources + ECSCluster: + Type: AWS::ECS::Cluster + Properties: + ClusterName: !Sub "${Name}-desmond" + Tags: + - Key: "Name" + Value: "desmond.ndambi.labs" + LBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS Security Group + VpcId: !Ref 'VPC' + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + LBSecurityGroupEgress: + Type: AWS::EC2::SecurityGroupEgress + Properties: + GroupId: + Fn::GetAtt: + - LBSecurityGroup + - GroupId + IpProtocol: tcp + Description: Load balancer to target + DestinationSecurityGroupId: + Fn::GetAtt: + - ServiceSecurityGroup + - GroupId + FromPort: 80 + ToPort: 80 + ServiceSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: ECS/MyApp/Service/SecurityGroup + VpcId: !Ref VPC + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + Description: Allow all outbound traffic by default + IpProtocol: "-1" + ServiceSecurityGroupIngress: + Type: AWS::EC2::SecurityGroupIngress + Properties: + IpProtocol: tcp + Description: Load balancer to target + FromPort: 80 + GroupId: + Fn::GetAtt: + - ServiceSecurityGroup + - GroupId + SourceSecurityGroupId: + Fn::GetAtt: + - LBSecurityGroup + - GroupId + ToPort: 80 + ECSALB: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Scheme: internet-facing + Type: application + Subnets: + - !Ref PublicSubnetOne + - !Ref PublicSubnetTwo + SecurityGroups: + - Fn::GetAtt: + - LBSecurityGroup + - GroupId + DependsOn: + - PublicRoute + LBListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - TargetGroupArn: !Ref LBTargetGroup + Type: forward + LoadBalancerArn: !Ref ECSALB + Port: 80 + Protocol: HTTP + LBTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + Port: 80 + Protocol: HTTP + TargetType: ip + VpcId: !Ref VPC + TaskDefTaskRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Version: "2012-10-17" + TaskDefExecutionRole: + Type: AWS::IAM::Role + Properties: + RoleName: "TaskDefExecutionRole" + AssumeRolePolicyDocument: + Statement: + - Action: sts:AssumeRole + Effect: Allow + Principal: + Service: ecs-tasks.amazonaws.com + Version: "2012-10-17" + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" + TaskDefinition: + Type: AWS::ECS::TaskDefinition + Properties: + RequiresCompatibilities: + - "FARGATE" + ContainerDefinitions: + - Name: "nginx" + Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryName}:${ImageTag}" + Cpu: "128" + PortMappings: + - ContainerPort: "80" + Protocol: "tcp" + HostPort: "80" + Memory: "256" + Essential: "true" + ExecutionRoleArn: !Ref TaskDefExecutionRole + NetworkMode: "awsvpc" + Cpu: "256" + Memory: "512" + RuntimePlatform: + { + "OperatingSystemFamily": "LINUX", + "CpuArchitecture": "X86_64" + } + ECSService: + Type: AWS::ECS::Service + DependsOn: + - ECSALB + Properties: + ServiceName: "ECSService" + Cluster: !Ref ECSCluster + DeploymentConfiguration: + DeploymentCircuitBreaker: + { + "Enable" : "true", + "Rollback" : "true" + } + MaximumPercent: "200" + MinimumHealthyPercent: "100" + DesiredCount: "1" + HealthCheckGracePeriodSeconds: "200" + LaunchType: "FARGATE" + LoadBalancers: + - ContainerName: "nginx" + ContainerPort: "80" + TargetGroupArn: !Ref LBTargetGroup + NetworkConfiguration: + AwsvpcConfiguration: + AssignPublicIp: "ENABLED" + SecurityGroups: + - !Ref ServiceSecurityGroup + Subnets: + - Ref: PublicSubnetOne + - Ref: PublicSubnetTwo + TaskDefinition: !Ref TaskDefinition + +Outputs: + WebAppLB: + Description: Web Application Load Balancer + Value: !Join [ "", [ 'http://', !GetAtt ECSALB.DNSName ] ] + Export: + Name: !Sub ${Name}-LB diff --git a/13-ECS/Practice-13.3/parameter.json b/13-ECS/Practice-13.3/parameter.json new file mode 100644 index 00000000..36e2328c --- /dev/null +++ b/13-ECS/Practice-13.3/parameter.json @@ -0,0 +1,6 @@ +[ + { + "ParameterKey": "Name", + "ParameterValue": "desmond-cluster" + } +]