Flask Docker Lab – Lesson 3

Lesson 3 – Deploying the Infrastructure

Lesson Overview

  • In this lesson we’ll use terraform.io to build the AWS infrastructure required to host a docker application.
  • We will tag our changes in git and commit them to github.
  • If things work correctly, travis-ci will be notified of the commit and will start a build of the environment. If the tests pass, it will push a brand new docker image to AWS ECR artifact repository.

Warning:

This lab will not work with multiple users running the lab concurrently
in the same region. This is primarily due to limits on the number of
Elastic IPs allowed by default per region. Life will be much simpler
if each user uses a separate AWS account. If you insist on multiple users
running in the same account concurrently, please make the following
adjustments.

  • Open a case with AWS to increase the limit on EIPs per region. You will need 3 EIPs per user running the lab in a singe region.
  • Each user must have a unique stageName in the infrastructure/terraform/variables.tf file (currently set to “dev”). Use a 3 character name with all lower case alpha chars, maybe your initials (like: “bob”, “prd”, “tst” or “foo”). This is because all AWS resources are named using this string. If 2 or more users use the same names, there will be naming conflicts.
  • Additionally the conatinerName defined in infrastructure/terraform/variables.tf and docker-compose.yml (currently set to “flask_docker_lab”) must be changed to something unique. It is suggested that you change it to flaskdockerbob where bob is the same 3 chars you used for the stageName. This is because one image name can only be used once in an AWS account/region ECR repository
  • Finally, the image: & conatiner_name: parameters in the docker-compose.yml file must match the conatinerName in the variables.tf file (flaskdockerbob). You’ll need to stop the local container docker-compose down, edit the docker-compose.yml file, then re-start the docker container docker-compose up -d. This is because the docker image name must match the ECR repository defined above.

Lesson

  1. Initialize Terraform.
    Terraform is a very powerful infrastructure automation engine. First we need to terraform init the terraform scripts and download any required modules (aws module in this case).

    cd ~/flask_docker_lab/infrastructure/terraform/ 
    terraform init
  2. Plan with Terraform
    Next we need to run a terraform plan to let terraform tell us what its going to do when we run apply. In this case, we are planning to create 50+ new resources including, but not limited to: (1) vpc, (1) internet gateway, (4) subnets, (4) routing tables, (1) application load balancer, (1) target group, (1) IAM user, (2) IAM roles, (1) auto scaling group, (1) launch configuration, (2) security groups, (1) ecr artifact repositories & (1) ecr cluster.

    terraform plan
  3. Apply (deploy) with Terraform
    Finally, we need to run terraform apply to actually build the infrastructure in AWS. This will take a few minutes to complete.

    terraform apply

    Because we haven’t yet pushed a docker container to the AWS ECR repository, the build of the AWS ECR Service definition will fail. Terraform doesn’t roll back when a deployment fails. This is important to know about terraform and works great in this circumstance.

    Error: Error applying plan:
    1 error(s) occurred:
    aws_ecs_service.ecs_service: 1 error(s) occurred:
    aws_ecs_service.ecs_service: InvalidParameterException: The target group with targetGroupArn arn:aws:elasticloadbalancing:us-west-2:357849880876:targetgroup/fdldev20180514180259459500000003/a9063a8553981aaf does not have an associated load balancer.
    status code: 400, request id: 136531a0-57a1-11e8-ba1e-8db92705beda “flask_docker_lab_svc”
    Terraform does not automatically rollback in the face of errors.
    Instead, your Terraform state file has been partially updated with
    any resources that successfully completed. Please address the error
    above and apply again to incrementally change your infrastructure.

    Navigate to: AWS Console > EC2 Container Services > Clusters > flask_docker_lab_dev_ecs_cluster > ECS Instances tab and you should have 2 running t2.micro EC2 servers.

  4. Configure the .travis.yml
    Now that the AWS infrastructure is ready to go, lets configure travis-ci to automatically test, build and upload docker images (if the code passes a battery of tests).

    First thing we’ll do is put a starter .travis.yml in place

    cd ~/flask_docker_lab 
    cp infrastructure/travis.yml.example .travis.yml
  5. Login into travis cli
    We’ll use our github credentials to authenticate the travis cli

    travis login
  6. Encrypt the AWS ECR artifact repository login information
    If travis-ci has a successful build, we want it to push the docker file to the AWS docker repository we created in AWS Console > EC2 Container Services > Repositories > flask_docker_lab. Since this is a private repository, we need to put the login credentials in the .travis.yml file. But that file is going to be stored in our public repository, so it must be encrypted. The following script does the work for us.

    ./create_ecr_secrets.sh
  7. Tag and push our code
    Now that AWS and Travis are both ready for a new docker image, lets tag and push the current version. You’ll have to authenticate with github twice, once for the code push and once for the tag push

    git add . 
    git commit -am "the first version of our application" 
    git tag -a v1.0 -m "the first version of our application" 
    git push && git push --tags
  8. Go check travis-ci.org > Build History
    It should be building, or have already built our application. The .travis.ci file runs a build and test on all commits, but only pushes tagged versions as image:latest and image:$version

    Check AWS > ECS Container > ECR repository.
    We should have 1 image with 2 tags (latest and v1.0)

    Re-run terraform apply to deploy the ECR service definition
    cd ~/flask_docker_lab/infrastructure/terraform/

    terraform apply
  9. Get the Loadbalancer DNS name from terraform output

    Check AWS > ECS Container Service > Cluster > flask_docker_lab_dev_ecs_cluster > Tasks. Should have 2 tasks running terraform output

    Put the DNS name in browser and tada!

  10. Update the code

    modify SITE_TITLE again in atom flask_docker_lab/app/config.py

    Then check to make sure the local version looks the way you want

    cd ~/flask_docker_lab 
    git add . 
    git commit -am "the 2nd version of our application" 
    git tag -a v1.1 -m "the 2nd version of our application" 
    git push && git push --tags
  11. Deploy the new code

    When the travis build / test / push is complete, update the ECS task
    definition and service definition with the new docker `containerTag`

    cd infrastructure/terraform 
    nano variables.tf
  12. Now redeploy terraform
    terraform plan 
    terraform apply

    In a few minutes, the web site on the load balancer should have your new changes! Look at AWS > EC2 > target groups > flask-docker-lab-dev-alb-tgt-grp > targets. Observe that one of the old v1.0 instances will drain and be replaced by the new v1.1 instances. Then the second one will trade places too. This ensures a zero outage migration.

    Also watch Clusters > flask_docker_lab_dev_ecs_cluster > flask_docker_lab_svc > Tasks tab. You can see what version of Tasks definition is specified in the service and which are running in the Task tab. Your migration is complete when both tasks are running the new code.

  13. See Update Site
    Get “DNS name:” from AWS Console > EC2 > Load Balancers > flask-docker-lab-dev-alb and paste that URL into your browser

Lesson 3 Summary

In this lesson we

  • Created ssh keys
  • Initialized Terraform
  • ran Terraform plan to see what would happen
  • ran terraform apply to make the changes
  • configured travis.yml for the ci build
  • logged into travis CLI
  • Encrypted the AWS ECR repository password file
  • Tagged our code as v1.0 and pushed it out
  • Watched the build, test and push to AWS happen in travis-ci
  • Deployed out containers to a running cluster in AWS
  • Updated our web site again as v1.1 and commit / pushed it
  • Let Travis-ci build the updated code and we deployed it
  • Learned how to earn $50!

In Lesson 4 we’ll clean up our mess.