Creating our ECS hello world service

We have an ECS cluster and a load balancer ready to take on traffic on one side, and an ECR repository containing the image of our application on the other side. We now need to tie the two together. This is done by creating an ECS service resource.

We will create a new file called helloworld-ecs-service-cf-template.py and start as usual with its imports, template variable creation, and template description, shown in the following code:

"""Generating CloudFormation template.""" 
 
from troposphere.ecs import ( 
    TaskDefinition, 
    ContainerDefinition 
)
from troposphere import ecs
from awacs.aws import ( Allow, Statement, Principal, Policy ) from troposphere.iam import Role from troposphere import ( Parameter, Ref, Template, Join, ImportValue, Select, Split, ) from awacs.sts import AssumeRole t = Template() t.add_description("Effective DevOps in AWS: ECS service - Helloworld")

Our template will take one argument—the tag of the image we want to deploy. Currently, our repository only has one image tagged as latest, but in the next section, we will update our deployment pipeline and automatize the deployment of our service to ECS:

t.add_parameter(Parameter( 
    "Tag", 
    Type="String", 
    Default="latest", 
    Description="Tag to deploy" 
)) 

In ECS, applications are defined by their task definitions. This is where we declare which repository to use to get our image, how much CPU and memory the application needs, and all other system properties such as port mapping, environment variables, mount points, and so on. We will keep our task definition minimal; in order to select the proper image, we will utilize the ImportValue function (we previously exported the repository name) combined with a Join to craft the repository URL. We will require 32 MB of RAM and one-quarter of a core to run our application. Finally, we will specify that port 3000 needs to be mapped onto the system:

t.add_resource(TaskDefinition( 
    "task", 
    ContainerDefinitions=[ 
        ContainerDefinition( 
            Image=Join("", [ 
                Ref("AWS::AccountId"), 
                ".dkr.ecr.", 
                Ref("AWS::Region"), 
                ".amazonaws.com", 
                "/", 
                ImportValue("helloworld-repo"), 
                ":", 
                Ref("Tag")]), 
            Memory=32, 
            Cpu=256, 
            Name="helloworld", 
            PortMappings=[ecs.PortMapping( 
                ContainerPort=3000)] 
        ) 
    ], 
)) 

As for most of the AWS managed services, the ECS service needs a certain set of permissions provided by the intermediary of a role. We will create that role and use the vanilla policy for the ECS service role:

t.add_resource(Role( 
    "ServiceRole", 
    AssumeRolePolicyDocument=Policy( 
        Statement=[ 
            Statement( 
                Effect=Allow, 
                Action=[AssumeRole], 
                Principal=Principal("Service", ["ecs.amazonaws.com"]) 
            ) 
        ] 
    ), 
    Path="/", 
    ManagedPolicyArns=[ 
        'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole'] 
)) 

We will complete the creation of our template with the addition of the ECS service resource, which ties the task definition, the ECS cluster, and the ALB together:

t.add_resource(ecs.Service( 
    "service", 
    Cluster=ImportValue( 
        Join( 
            "-", 
            [Select(0, Split("-", Ref("AWS::StackName"))), 
                "cluster-id"] 
        ) 
    ), 
    DesiredCount=1, 
    TaskDefinition=Ref("task"), 
    LoadBalancers=[ecs.LoadBalancer( 
        ContainerName="helloworld", 
        ContainerPort=3000, 
        TargetGroupArn=ImportValue( 
            Join( 
                "-", 
                [Select(0, Split("-", Ref("AWS::StackName"))), 
                    "alb-helloworld-target-group"] 
            ), 
        ), 
    )], 
    Role=Ref("ServiceRole") 
)) 

Finally, as always, we will output the template generated by our code:

print(t.to_json()) 

The script is ready and should look like this: http://bit.ly/2uB5wQn.

We will now generate the template and create our stack:

$ git add helloworld-ecs-service-cf-template.py
$ git commit -m "Adding helloworld ECS service script"
$ git push
$ python helloworld-ecs-service-cf-template.py > helloworld-ecs-service-cf.template
$ aws cloudformation create-stack
--stack-name staging-helloworld-service
--capabilities CAPABILITY_IAM
--template-body file://helloworld-ecs-service-cf.template
--parameters
ParameterKey=Tag,ParameterValue=latest

After a few minutes, the stack should be created. We can circle back to the output of the ALB stack to get the URL of our newly deployed application and test its output:

$ aws cloudformation describe-stacks 
--stack-name staging-alb
--query 'Stacks[0].Outputs'
[
{
"Description": "TargetGroup",
"OutputKey": "TargetGroup",
"OutputValue": "arn:aws:elasticloadbalancing:us-east-1:511912822958:targetgroup/stagi-Targe-1XEVVSL4W45ZU/318c23a83212c5bf"
},
{
"Description": "Helloworld URL",
"OutputKey": "URL",
"OutputValue": "http://stagi-LoadB-1C1GMX1ULESJZ-1294651738.us-east-1.elb.amazonaws.com:3000"
}
]

$ curl http://stagi-LoadB-1C1GMX1ULESJZ-1294651738.us-east-1.elb.amazonaws.com:3000
Hello World

We completed the creation of our staging ECS environment.

At this point, we can easily manually deploy new code to our staging as follows:

  1. Make the changes in the helloworld code locally
  2. Log in to the ecr registry, shown as follows:
$ eval "$(aws ecr get-login --region us-east-1 --no-include-email)"
  1. Build your docker container,as follows:
$ docker build -t helloworld 
  1. Pick a new unique tag and use it to tag your image, that is, let's say your new tag is foobar:
$ docker tag helloworld 511912822958.dkr.ecr.us-east-1.amazonaws.com/hello-repos-1llamst2oolbl:foobar
  1. Push the image to the ecr repository, shown as follows:
$ docker push 511912822958.dkr.ecr.us-east-1.amazonaws.com/hello-repos-1llamst2oolbl:foobar
  1. Update the ECS service CloudFormation stack, as shown in the following code:
$ aws cloudformation update-stack 
--stack-name staging-helloworld-service
--capabilities CAPABILITY_IAM
--template-body file://helloworld-ecs-service-cf.template
--parameters
ParameterKey=Tag,ParameterValue=foobar

Using this sequence of events, we are going to automate the deployment process and create a new continuous integration/continuous deployment pipeline.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset