Deploying an Amazon ECS service using a blue/green deployment - Amazon Elastic Container Service

Deploying an Amazon ECS service using a blue/green deployment

Learn how to create an Amazon ECS service containing a Fargate task that uses the blue/green deployment type with the AWS CLI.

Note

Support for performing a blue/green deployment has been added for AWS CloudFormation. For more information, see Perform Amazon ECS blue/green deployments through CodeDeploy using AWS CloudFormation in the AWS CloudFormation User Guide.

Prerequisites

This tutorial assumes that you have completed the following prerequisites:

Step 1: Create an Application Load Balancer

Amazon ECS services using the blue/green deployment type require the use of either an Application Load Balancer or a Network Load Balancer. This tutorial uses an Application Load Balancer.

To create an Application Load Balancer
  1. Use the create-load-balancer command to create an Application Load Balancer. Specify two subnets that aren't from the same Availability Zone as well as a security group.

    aws elbv2 create-load-balancer \ --name bluegreen-alb \ --subnets subnet-abcd1234 subnet-abcd5678 \ --security-groups sg-abcd1234 \ --region us-east-1

    The output includes the Amazon Resource Name (ARN) of the load balancer, with the following format:

    arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642
  2. Use the create-target-group command to create a target group. This target group will route traffic to the original task set in your service.

    aws elbv2 create-target-group \ --name bluegreentarget1 \ --protocol HTTP \ --port 80 \ --target-type ip \ --vpc-id vpc-abcd1234 \ --region us-east-1

    The output includes the ARN of the target group, with the following format:

    arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4
  3. Use the create-listener command to create a load balancer listener with a default rule that forwards requests to the target group.

    aws elbv2 create-listener \ --load-balancer-arn arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642 \ --protocol HTTP \ --port 80 \ --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4 \ --region us-east-1

    The output includes the ARN of the listener, with the following format:

    arn:aws:elasticloadbalancing:region:aws_account_id:listener/app/bluegreen-alb/e5ba62739c16e642/665750bec1b03bd4

Step 2: Create an Amazon ECS cluster

Use the create-cluster command to create a cluster named tutorial-bluegreen-cluster to use.

aws ecs create-cluster \ --cluster-name tutorial-bluegreen-cluster \ --region us-east-1

The output includes the ARN of the cluster, with the following format:

arn:aws:ecs:region:aws_account_id:cluster/tutorial-bluegreen-cluster

Step 3: Register a task definition

Use the register-task-definition command to register a task definition that is compatible with Fargate. It requires the use of the awsvpc network mode. The following is the example task definition used for this tutorial.

First, create a file named fargate-task.json with the following contents. Ensure that you use the ARN for your task execution role. For more information, see Amazon ECS task execution IAM role.

{ "family": "tutorial-task-def", "networkMode": "awsvpc", "containerDefinitions": [ { "name": "sample-app", "image": "httpd:2.4", "portMappings": [ { "containerPort": 80, "hostPort": 80, "protocol": "tcp" } ], "essential": true, "entryPoint": [ "sh", "-c" ], "command": [ "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #00FFFF;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\"" ] } ], "requiresCompatibilities": [ "FARGATE" ], "cpu": "256", "memory": "512", "executionRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskExecutionRole" }

Then register the task definition using the fargate-task.json file that you created.

aws ecs register-task-definition \ --cli-input-json file://fargate-task.json \ --region us-east-1

Step 4: Create an Amazon ECS service

Use the create-service command to create a service.

First, create a file named service-bluegreen.json with the following contents.

{ "cluster": "tutorial-bluegreen-cluster", "serviceName": "service-bluegreen", "taskDefinition": "tutorial-task-def", "loadBalancers": [ { "targetGroupArn": "arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4", "containerName": "sample-app", "containerPort": 80 } ], "launchType": "FARGATE", "schedulingStrategy": "REPLICA", "deploymentController": { "type": "CODE_DEPLOY" }, "platformVersion": "LATEST", "networkConfiguration": { "awsvpcConfiguration": { "assignPublicIp": "ENABLED", "securityGroups": [ "sg-abcd1234" ], "subnets": [ "subnet-abcd1234", "subnet-abcd5678" ] } }, "desiredCount": 1 }

Then create your service using the service-bluegreen.json file that you created.

aws ecs create-service \ --cli-input-json file://service-bluegreen.json \ --region us-east-1

The output includes the ARN of the service, with the following format:

arn:aws:ecs:region:aws_account_id:service/service-bluegreen

Obtain the DNS name of the load balancer using the following command.

aws elbv2 describe-load-balancers --name bluegreen-alb --query 'LoadBalancers[*].DNSName'

Enter the DNS name in your web browser and you should see a webpage that displays the sample app with a blue background.

Step 5: Create the AWS CodeDeploy resources

Use the following steps to create your CodeDeploy application, the Application Load Balancer target group for the CodeDeploy deployment group, and the CodeDeploy deployment group.

To create CodeDeploy resources
  1. Use the create-application command to create a CodeDeploy application. Specify the ECS compute platform.

    aws deploy create-application \ --application-name tutorial-bluegreen-app \ --compute-platform ECS \ --region us-east-1

    The output includes the application ID, with the following format:

    {
        "applicationId": "b8e9c1ef-3048-424e-9174-885d7dc9dc11"
    }
  2. Use the create-target-group command to create a second Application Load Balancer target group, which will be used when creating your CodeDeploy deployment group.

    aws elbv2 create-target-group \ --name bluegreentarget2 \ --protocol HTTP \ --port 80 \ --target-type ip \ --vpc-id "vpc-0b6dd82c67d8012a1" \ --region us-east-1

    The output includes the ARN for the target group, with the following format:

    arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget2/708d384187a3cfdc
  3. Use the create-deployment-group command to create a CodeDeploy deployment group.

    First, create a file named tutorial-deployment-group.json with the following contents. This example uses the resource that you created. For the serviceRoleArn, specify the ARN of your Amazon ECS CodeDeploy IAM role. For more information, see Amazon ECS CodeDeploy IAM Role.

    { "applicationName": "tutorial-bluegreen-app", "autoRollbackConfiguration": { "enabled": true, "events": [ "DEPLOYMENT_FAILURE" ] }, "blueGreenDeploymentConfiguration": { "deploymentReadyOption": { "actionOnTimeout": "CONTINUE_DEPLOYMENT", "waitTimeInMinutes": 0 }, "terminateBlueInstancesOnDeploymentSuccess": { "action": "TERMINATE", "terminationWaitTimeInMinutes": 5 } }, "deploymentGroupName": "tutorial-bluegreen-dg", "deploymentStyle": { "deploymentOption": "WITH_TRAFFIC_CONTROL", "deploymentType": "BLUE_GREEN" }, "loadBalancerInfo": { "targetGroupPairInfoList": [ { "targetGroups": [ { "name": "bluegreentarget1" }, { "name": "bluegreentarget2" } ], "prodTrafficRoute": { "listenerArns": [ "arn:aws:elasticloadbalancing:region:aws_account_id:listener/app/bluegreen-alb/e5ba62739c16e642/665750bec1b03bd4" ] } } ] }, "serviceRoleArn": "arn:aws:iam::aws_account_id:role/ecsCodeDeployRole", "ecsServices": [ { "serviceName": "service-bluegreen", "clusterName": "tutorial-bluegreen-cluster" } ] }

    Then create the CodeDeploy deployment group.

    aws deploy create-deployment-group \ --cli-input-json file://tutorial-deployment-group.json \ --region us-east-1

    The output includes the deployment group ID, with the following format:

    {
        "deploymentGroupId": "6fd9bdc6-dc51-4af5-ba5a-0a4a72431c88"
    }

Step 6: Create and monitor a CodeDeploy deployment

Before creating a CodeDeploy deployment, update the task definition command in fargate-task.json as follows to change the sample app background color to green.

{ ... "containerDefinitions": [ { ... "command": [ "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #097969;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' > /usr/local/apache2/htdocs/index.html && httpd-foreground\"" ] } ], ... }

Register the updated task definition using the following command.

aws ecs register-task-definition \ --cli-input-json file://fargate-task.json \ --region us-east-1

Now, use the following steps to create and upload an application specification file (AppSpec file) and an CodeDeploy deployment.

To create and monitor a CodeDeploy deployment
  1. Create and upload an AppSpec file using the following steps.

    1. Create a file named appspec.yaml with the contents of the CodeDeploy deployment group. This example uses the updated task definition.

      version: 0.0 Resources: - TargetService: Type: AWS::ECS::Service Properties: TaskDefinition: "arn:aws:ecs:region:aws_account_id:task-definition/tutorial-task-def:2" LoadBalancerInfo: ContainerName: "sample-app" ContainerPort: 80 PlatformVersion: "LATEST"
    2. Use the s3 mb command to create an Amazon S3 bucket for the AppSpec file.

      aws s3 mb s3://tutorial-bluegreen-bucket
    3. Use the s3 cp command to upload the AppSpec file to the Amazon S3 bucket.

      aws s3 cp ./appspec.yaml s3://tutorial-bluegreen-bucket/appspec.yaml
  2. Create the CodeDeploy deployment using the following steps.

    1. Create a file named create-deployment.json with the contents of the CodeDeploy deployment. This example uses the resources that you created earlier in the tutorial.

      { "applicationName": "tutorial-bluegreen-app", "deploymentGroupName": "tutorial-bluegreen-dg", "revision": { "revisionType": "S3", "s3Location": { "bucket": "tutorial-bluegreen-bucket", "key": "appspec.yaml", "bundleType": "YAML" } } }
    2. Use the create-deployment command to create the deployment.

      aws deploy create-deployment \ --cli-input-json file://create-deployment.json \ --region us-east-1

      The output includes the deployment ID, with the following format:

      {
          "deploymentId": "d-RPCR1U3TW"
      }
  3. Use the get-deployment-target command to get the details of the deployment, specifying the deploymentId from the previous output.

    aws deploy get-deployment-target \ --deployment-id "d-IMJU3A8TW" \ --target-id tutorial-bluegreen-cluster:service-bluegreen \ --region us-east-1

    Initially, the deployment status is InProgress. Traffic is directed to the original task set, which has a taskSetLabel of BLUE, a status of PRIMARY, and a trafficWeight of 100.0. The replacement task set has a taskSetLabel of GREEN, a status of ACTIVE, and a trafficWeight of 0.0. The web browser you entered the DNS name in still displays the sample app with a blue background.

    {
    "deploymentTarget": {
    "deploymentTargetType": "ECSTarget",
    "ecsTarget": {
        "deploymentId": "d-RPCR1U3TW",
        "targetId": "tutorial-bluegreen-cluster:service-bluegreen",
        "targetArn": "arn:aws:ecs:region:aws_account_id:service/service-bluegreen",
        "lastUpdatedAt": "2023-08-10T12:07:24.797000-05:00",
        "lifecycleEvents": [
            {
                "lifecycleEventName": "BeforeInstall",
                "startTime": "2023-08-10T12:06:22.493000-05:00",
                "endTime": "2023-08-10T12:06:22.790000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "Install",
                "startTime": "2023-08-10T12:06:22.936000-05:00",
                "status": "InProgress"
            },
            {
                "lifecycleEventName": "AfterInstall",
                "status": "Pending"
            },
            {
                "lifecycleEventName": "BeforeAllowTraffic",
                "status": "Pending"
            },
            {
                "lifecycleEventName": "AllowTraffic",
                "status": "Pending"
            },
            {
                "lifecycleEventName": "AfterAllowTraffic",
                "status": "Pending"
            }
        ],
        "status": "InProgress",
        "taskSetsInfo": [
            {
                "identifer": "ecs-svc/9223370493423413672",
                "desiredCount": 1,
                "pendingCount": 0,
                "runningCount": 1,
                "status": "ACTIVE",
                "trafficWeight": 0.0,
                "targetGroup": {
                    "name": "bluegreentarget2"
                },
                "taskSetLabel": "Green"
            },
            {
                "identifer": "ecs-svc/9223370493425779968",
                "desiredCount": 1,
                "pendingCount": 0,
                "runningCount": 1,
                "status": "PRIMARY",
                "trafficWeight": 100.0,
                "targetGroup": {
                    "name": "bluegreentarget1"
                },
                "taskSetLabel": "Blue"
            }
        ]
    }
    }
    }

    Continue to retrieve the deployment details using the command until the deployment status is Succeeded, as shown in the following output. Traffic is now redirected to the replacement task set, which now has a status of PRIMARY and a trafficWeight of 100.0. Refresh the web browser you entered the load balancer DNS name in, and you should now see the sample app with a green background.

    {
    "deploymentTarget": {
    "deploymentTargetType": "ECSTarget",
    "ecsTarget": {
        "deploymentId": "d-RPCR1U3TW",
        "targetId": "tutorial-bluegreen-cluster:service-bluegreen",
        "targetArn": "arn:aws:ecs:region:aws_account_id:service/service-bluegreen",
        "lastUpdatedAt": "2023-08-10T12:07:24.797000-05:00",
        "lifecycleEvents": [
            {
                "lifecycleEventName": "BeforeInstall",
                "startTime": "2023-08-10T12:06:22.493000-05:00",
                "endTime": "2023-08-10T12:06:22.790000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "Install",
                "startTime": "2023-08-10T12:06:22.936000-05:00",
                "endTime": "2023-08-10T12:08:25.939000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "AfterInstall",
                "startTime": "2023-08-10T12:08:26.089000-05:00",
                "endTime":  "2023-08-10T12:08:26.403000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "BeforeAllowTraffic",
                "startTime": "2023-08-10T12:08:26.926000-05:00",
                "endTime":  "2023-08-10T12:08:27.256000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "AllowTraffic",
                "startTime": "2023-08-10T12:08:27.416000-05:00",
                "endTime": "2023-08-10T12:08:28.195000-05:00",
                "status": "Succeeded"
            },
            {
                "lifecycleEventName": "AfterAllowTraffic",
                "startTime": "2023-08-10T12:08:28.715000-05:00",
                "endTime":  "2023-08-10T12:08:28.994000-05:00",
                "status": "Succeeded"
            }
        ],
        "status": "Succeeded",
        "taskSetsInfo": [
            {
                "identifer": "ecs-svc/9223370493425779968",
                "desiredCount": 1,
                "pendingCount": 0,
                "runningCount": 1,
                "status": "ACTIVE",
                "trafficWeight": 0.0,
                "targetGroup": {
                    "name": "bluegreentarget1"
                },
                "taskSetLabel": "Blue"
            },
            {
                "identifer": "ecs-svc/9223370493423413672",
                "desiredCount": 1,
                "pendingCount": 0,
                "runningCount": 1,
                "status": "PRIMARY",
                "trafficWeight": 100.0,
                "targetGroup": {
                    "name": "bluegreentarget2"
                },
                "taskSetLabel": "Green"
            }
        ]
    }
    }
    }

Step 7: Clean up

When you have finished this tutorial, clean up the resources associated with it to avoid incurring charges for resources that you aren't using.

Cleaning up the tutorial resources
  1. Use the delete-deployment-group command to delete the CodeDeploy deployment group.

    aws deploy delete-deployment-group \ --application-name tutorial-bluegreen-app \ --deployment-group-name tutorial-bluegreen-dg \ --region us-east-1
  2. Use the delete-application command to delete the CodeDeploy application.

    aws deploy delete-application \ --application-name tutorial-bluegreen-app \ --region us-east-1
  3. Use the delete-service command to delete the Amazon ECS service. Using the --force flag allows you to delete a service even if it has not been scaled down to zero tasks.

    aws ecs delete-service \ --service arn:aws:ecs:region:aws_account_id:service/service-bluegreen \ --force \ --region us-east-1
  4. Use the delete-cluster command to delete the Amazon ECS cluster.

    aws ecs delete-cluster \ --cluster tutorial-bluegreen-cluster \ --region us-east-1
  5. Use the s3 rm command to delete the AppSpec file from the Amazon S3 bucket.

    aws s3 rm s3://tutorial-bluegreen-bucket/appspec.yaml
  6. Use the s3 rb command to delete the Amazon S3 bucket.

    aws s3 rb s3://tutorial-bluegreen-bucket
  7. Use the delete-load-balancer command to delete the Application Load Balancer.

    aws elbv2 delete-load-balancer \ --load-balancer-arn arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642 \ --region us-east-1
  8. Use the delete-target-group command to delete the two Application Load Balancer target groups.

    aws elbv2 delete-target-group \ --target-group-arn arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4 \ --region us-east-1
    aws elbv2 delete-target-group \ --target-group-arn arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget2/708d384187a3cfdc \ --region us-east-1