ECS Deployments with Traefik Enterprise | Traefik Labs

If you are not familiar with Traefik Enterprise, it is a unified cloud native networking solution, ideal for easing the complexity of microservices networking. It is cloud-agnostic and can be configured dynamically by a lot of orchestration platforms.

Lately, at Traefik Labs we have had a lot of requests to deploy Traefik Enterprise in Amazon ECS. At first, the requests left me a bit perplexed — why not choose Kubernetes? Kubernetes has a vast and mature ecosystem, plus you avoid vendor lock-in by not going with the cloud provider’s own orchestrator.

But the truth is, that every organization has different needs. And what Kubernetes cannot provide in certain use cases, ECS can. Kubernetes use cases are very well documented, so why not shift our focus a bit and start providing the tools and sharing the knowledge necessary to explore use cases with different orchestrators? This, after all, is only a very simple first step to building a (very) complex infrastructure.

So, here I am, writing this article which I hope will help everyone who wants to try out Traefik Enterprise in AWS ECS.

Traefik Enterprise is a load balancing and reverse proxy solution which helps you publish your services easily.  And now you’re probably asking yourself, what’s the point in using Traefik Enterprise to publish your services in AWS since you have the Application Load Balancer (ALB)? You could be right, but Traefik Enterprise adds a lot of functionality that you won’t find in ALB:

  • Dynamic configuration by the orchestrator
  • Capacity to modify the request or response behavior of your published services according to your needs
  • Ability to add or remove headers by modifying the URL path
  • Simple process to add authentication that is applied without any loss of requests

Note: If you are not used to interacting with ECS, this blog post could be a bit dense, but I will try to keep it as easy to follow as possible and cover each step, from creating the ECS cluster to the deployment of your first web app.

If you are using ECS daily and want to try Traefik Enterprise on your own, you can find some great material in our documentation.

Here is what I’ll cover in this tutorial:

  • Creating the ECS cluster that will run my workload
  • Creating specific IAM rules needed to run my workload
  • Installing and configuring Traefik Enterprise
  • Deploying my first web application with rate limiting and header modification provided by Traefik Enterprise

This should give you an excellent example of interacting with Traefik Enterprise in ECS and why Traefik Enterprise is such a powerful tool.

Secure, manage, & scale all your APIs.See how Traefik Enterprise simplifies, automates, and centralizes API management and security with one easy-to-use solution.Learn More

Preparing the ECS environment

To run Traefik Enterprise in ECS, you first need to create your ECS environment. There are three kinds of ECS workloads:

  • The EC2 implementation relies on EC2 instances and the scaling group you will need to create.
  • The Fargate implementation is fully managed by AWS but has certain drawbacks, for example, instances IPs will change each time the resources you deployed are restarted.
  • The Anywhere implementation lets you run the workload on your servers (VMs or bare metal). This implementation has some limitations regarding service discovery — it limits Traefik Enterprise and will not let it works correctly.

These are all interesting implementation methods to explore but in order to save you time and confusion, in this article I will focus on the EC2 implementation.

Note: Make sure you add the aws-cli to your workstation and configure the API credential to be able to create resources with the aws-cli command. Take a look at the official AWS documentation to install the aws-cli.

The ECS cluster

As I already mentioned, the ECS implementation based on EC2 will need EC2 instances to run, and AWS had a good idea to map the creation of the cluster with Scaling Group; that way you won’t have to manage EC2 by yourself.

If you are not used to working with the aws-cli, creating the ECS EC2 cluster is easier to do using the AWS WebUI. When connected to the ECS dashboard on the AWS site, go to the Cluster section cluster and click on the Create cluster button. Give a name to the cluster, and select the VPC and subnet you want to get your cluster in.

Then, select the provider — in this case, Amazon EC2 — and configure some information about the EC2 scaling group that you want to use.

For this discovery, you don’t need tagging and monitoring, so feel free to skip this section.

Once you fill out all the necessary info, you have an ECS cluster running 3 EC2 instances that you can use to run Traefik Enterprise.

AWS IAM

In order to run the Traefik Enterprise workloads on the ECS cluster, you need to create specific IAM policies and roles to allow Traefik Enterprise to discover configurations from the ECS API, or access secrets to get its license.

To keep this tutorial as simple as possible, I will use the same IAM roles for the Traefik Enterprise Workload as well as for my web app workload. In an enterprise environment, however, you should have separate roles for the application workload which doesn’t need access to ECS API or Secret Manager.

AWS has already an execution task role that you can use to run your Traefik Enterprise task, but it lacks the capacity to read secrets. To do so, you need to create a secret read policy and map it to the role.

Start by creating a JSON file, containing this rule:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "secretsmanager:GetSecretValue"
            ],
            "Resource": "*"
        }
    ]
}

Then add it to IAM with this command:

aws iam create-policy --policy-name AmazonECSTaskSecretRead --policy-document file://AmazonECSTaskSecretRead.json

And finally, attach this policy to the AWS defined role named ecsTaskExecutionRole with this command:

aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/AmazonECSTaskSecretRead --role-name ecsTaskExecutionRole

Now, you need to add a new role that enables Traefik Enterprise to communicate with the ECS API. First, create the policy containing this info:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "TraefikECSReadAccess",
            "Effect": "Allow",
            "Action": [
                "ecs:ListClusters",
                "ecs:DescribeClusters",
                "ecs:ListTasks",
                "ecs:DescribeTasks",
                "ecs:DescribeContainerInstances",
                "ecs:DescribeTaskDefinition",
                "ec2:DescribeInstances",
                "ssm:DescribeAssociation",
                "ssm:GetDeployablePatchSnapshotForInstance",
                "ssm:GetDocument",
                "ssm:DescribeDocument",
                "ssm:GetManifest",
                "ssm:GetParameter",
                "ssm:GetParameters",
                "ssm:ListAssociations",
                "ssm:ListInstanceAssociations",
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "logs:DescribeLogStreams"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "TraefikECSContainerExec",
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Here you can see two Sid defined. This is because you will have to enable the capacity to interact with the container directly through the aws-cli to get the Traefik Enterprise token to connect proxies to the controller.

Add the policy with this command:

aws iam create-policy --policy-name TraefikECSReadAccessRolePolicy --policy-document file://TraefikECSReadAccessRolePolicy.json

Before you create the role that will map this policy, you need to create a JSON file with a trust policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Then create the role.

aws iam create-role --role-name TraefikECSReadAccessRole --assume-role-policy-document file://trust-policy.json

And finally, attach the previously created policy to the role.

aws iam attach-role-policy --policy-arn arn:aws:iam::aws:policy/TraefikECSReadAccessRolePolicy --role-name TraefikECSReadAccessRole

That’s it, you are ready to roll!

Deploying Traefik Enterprise

Traefik Enterprise needs a license to run. You can get a trial license here.

Secure, manage, & scale all your APIs.Want to simplify API management and security? Start your free 30-day trial of Traefik Enterprise today.Try Traefik Enterprise

If you are already familiar with Traefik Proxy, keep in mind that there is a difference in Traefik Enterprise; a controller will hold the configuration for two proxy instances and an offline custom plugin registry. For more details about the architecture of Traefik Enterprise check the documentation here.

Keep it a secret!

When deploying Traefik Enterprise in ECS you should consider using AWS secret manager to store Traefik licenses and secret tokens — tokens that are needed to connect proxies and the registry with the controller.

Tokens needed to connect proxies and registry will be generated by the controller at the first start. You need to wait till the controller runs in order to deploy the proxies and registry.

The registry is only accessible from the controller and will need a random string to authenticate the controller with the registry.

Now that you know that, you can prepare the secrets to start the controller. This secret will contain the license and the plugin authentication string.

To create the secret use this command:

aws secretsmanager create-secret \\
    --name traefikee-secrets \\
    --description "TraefikEE secret " \\
    --secret-string "{\\"TRAEFIKEE_LICENSE\\":\\"MY_LICENSE_NUMBER\\",\\"TRAEFIKEE_PLUGIN_TOKEN\\":\\"MY_10CHAR_RANDOM_STRING\\"}"

With the secrets prepared and ready, you can go ahead and deploy the controller.

Deploying the controller

When using ECS, each deployment needs a task definition and a service definition. The first thing you need to do is to create a task definition for the controller.

Create a JSON file containing the following definition:

{
    "family": "traefikee-controller",    
    "taskRoleArn": "arn:aws:iam::${account-id}:role/TraefikECSReadAccessRole",
    "executionRoleArn": "arn:aws:iam::${account-id}:role/ecsTaskExecutionRole",
    "cpu": "512",
    "memory": "1024",
    "containerDefinitions": [
        {
            "name": "controller",
            "image": "traefik/traefikee:v2.6.1",
            "cpu": 500,
            "links": [],
            "portMappings": [
                {
                    "containerPort": 55055,
                    "hostPort": 55055,
                    "protocol": "tcp"
                },
                {
                    "containerPort": 4242,
                    "hostPort": 4242,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "command": [
                "controller",
                "--name=controller",
                "--advertise=${controller_instance_ip}:4242",
                "--statedir=/data/state",
                "--jointoken.file.path=/data/tokens",
                "--api.autocerts",
                "--plugin.url=https://${registry_instance_ip}:443"
            ],
            "linuxParameters": {
                "initProcessEnabled": true
            },
            "entryPoint": [],
            "environment": [],
            "environmentFiles": [],
            "secrets": [
                {
                    "name": "TRAEFIKEE_LICENSE",
                    "valueFrom": "arn:aws:secretsmanager:${region}:${account-id}:secret:traefikee-secrets-cWNokO:TRAEFIKEE_LICENSE::"
                },
                {
                    "name": "TRAEFIKEE_PLUGIN_TOKEN",
                    "valueFrom": "arn:aws:secretsmanager:${region}:${account-id}:secret:traefikee-secrets-cWNokO:TRAEFIKEE_PLUGIN_TOKEN::"
                }
            ],
            "volumesFrom": [],
            "dockerLabels": {
                "com.traefik.traefikee.component": "controller"
            },
            "mountPoints": [
                {
                    "sourceVolume": "traefikee-data",
                    "containerPath": "/data",
                    "readOnly": false
                }
            ],
            "dnsServers": [],
            "dnsSearchDomains": [],
            "extraHosts": [],
            "dockerSecurityOptions": [],
            "ulimits": [],
            "systemControls": []
        }
    ],
    "volumes": [
        {
            "name": "traefikee-data",       
            "dockerVolumeConfiguration": {
                "scope": "shared",
                "autoprovision": true,
                "driver": "local"                
            }   
        }
    ],
    "placementConstraints": [],
    "networkMode": "host",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "requiresCompatibilities": [
        "EC2"
    ]
}

As you can see, in the task definition command section there are 2 values that need to be changed to reflect the right IP address for the controller and registry.

Those IPs can be obtained by getting the private IP of the node where you want to run the controller and registry. With constraint placement, you will always get the same IP for both registry and controller every time you recreate the tasks.

This example runs in a testing environment, but in a production environment, you should consider using DNS to cover ECS instance failure and be sure that the controller and registry will be resolvable even if not running on the same host.

You may also observe that I declared a volume that will be used to store the state of the controller and everything needed, like tokens in case of task restart.

Now that you have our definition, go ahead and register it to ECS to use it:

aws ecs register-task-definition --cli-input-json file://controller-task-ec2.json

Ok, you have the task definition but how are you supposed to run it? Just define a service that will start a task based on our definition.

To create the service, create a new JSON file defining this service with the content below:

{
  "serviceName": "traefikee-controller",
  "taskDefinition": "traefikee-controller",
  "desiredCount": 1,
  "launchType": "EC2",    
  "schedulingStrategy": "REPLICA",
  "placementConstraints": [
    {
      "expression": "attribute:controller == true",
      "type": "memberOf"
    }
  ]
}

In this service definition, the placement constraint is based on an attribute named controller. This is a custom attribute that you can add to the instance when selecting the internal IP in the task definition.

This attribute can be applied to the instance using this command:

aws ecs put-attributes --cluster ${cluster_name} --attributes name=controller,value=true,targetId=arn:aws:ecs:${region}:${account-id}:container-instance/${cluster_name}/${ecs_instance_id}

You are finally ready to deploy the controller, and as for the attributes or the registration of the task it is a simple command that does the trick:

aws ecs create-service --cli-input-json file://controller-svc-ec2.json --cluster ${cluster_name} --enable-execute-command

At the end of the command line, you see the option —enable-execute-command, you will need it to run the command to the controller container without connecting to the instance running the controller.

When the controller is running, you should see the task as running in the ECS dashboard.

Before going forward with deploying the other component of Traefik Enterprise, you should get the proxy join token and, again, a simple command will get that for you:

aws ecs execute-command --cluster ${cluster_name} --container controller --task ${task_arn} --interactive --command "/traefikee tokens"

This should give you this type of output:

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-0ffaaf5e6e0c45675
export TRAEFIKEE_CONTROLLER_TOKEN=5531644e5645744f4c5445744e5842684d6d517762576c69646d56694e4752354e326c6a5933687a625452355a58646c4d576c3363326f795932566b637a463561477831634749324f584533596d67744f484e34637a42764f47787162575a30627a457764545678636d4a694f44593259773d3d3a303a842a69fd610af752cc4edd8a655fd1921027624f2d047e3f18cd67a84d14dcb83974672d1ceeb72b5b63cedfa6b70f3a
export TRAEFIKEE_PROXY_TOKEN=5531644e5645744f4c5445744e5842684d6d517762576c69646d56694e4752354e326c6a5933687a625452355a58646c4d576c3363326f795932566b637a463561477831634749324f584533596d67745a57466c6233526e5a6a4e3161486431645856714d6d56766548557a626a513259513d3d3a313a842a69fd610af752cc4edd8a655fd1921027624f2d047e3f18cd67a84d14dcb84f2eea12628f7e60e95a1bcc22debc63
Exiting session with sessionId: ecs-execute-command-0ffaaf5e6e0c45675.

You also need to update the secret with the two tokens given by the command.

Note: The controller token will be used if you want to implement a multi-controller Traefik Enterprise.

To update the secrets you previously created, let's send this command:

aws secretsmanager put-secret-value \\
--secret-id traefikee-secrets \\
--secret-string "{\\"TRAEFIKEE_LICENSE\\":\\"MY_LICENSE_NUMBER\\",\\"TRAEFIKEE_PLUGIN_TOKEN\\":\\"MvXVeX3qDylxJQ==\\",\\"TRAEFIKEE_JOIN_TOKEN\\":\\"${proxy_join_token_value}\\",\\"TRAEFIKEE_CONTROLLER_TOKEN\\":\\"${controller_join_token_value}\\"}"

I know this took a bit of configuration but it will be worth your time, trust me!

Proxy deployment

With the Traefik Enterprise controller deployed, you are ready to deploy the proxies and the registry by simply registering the proxy and registry task and then creating the service associated.

Let’s begin with the proxy by creating the task definition with the info you see below:

{
    "family": "traefikee-proxies",    
    "taskRoleArn": "arn:aws:iam::${account-id}:role/TraefikECSReadAccessRole",
    "executionRoleArn": "arn:aws:iam::${account-id}:role/ecsTaskExecutionRole",
    "cpu": "512",
    "memory": "1024",
    "containerDefinitions": [
        {
            "name": "proxy",
            "image": "traefik/traefikee:v2.6.1",
            "cpu": 500,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp"
                },
                {
                    "containerPort": 443,
                    "hostPort": 443,
                    "protocol": "tcp"
                },
                {
                    "containerPort": 8484,
                    "hostPort": 8484,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "command": [
                "proxy",
                "--role=ingress",
                "--discovery.static.peers=${controller_instance_ip}:4242"
            ],
            "entryPoint": [],
            "environment": [],
            "environmentFiles": [],
            "secrets": [
                {
                    "name": "TRAEFIKEE_JOIN_TOKEN",
                    "valueFrom": "arn:aws:secretsmanager:${region}:${account-id}:secret:traefikee-secrets-cWNokO:TRAEFIKEE_PROXY_TOKEN::"
                }
            ],
            "volumesFrom": [],
            "dockerLabels": {
                "com.traefik.traefikee.component": "proxy"
            },
            "mountPoints": [],
            "dnsServers": [],
            "dnsSearchDomains": [],
            "extraHosts": [],
            "dockerSecurityOptions": [],
            "ulimits": [],
            "systemControls": []
        }
    ],
    "volumes": [],
    "placementConstraints": [],
    "networkMode": "host",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "requiresCompatibilities": [
        "EC2"
    ]
}

You can register the proxy task definition just as you did for the controller, with a slight difference; you don’t have to add the option to connect to the task container directly because there is no need to do so.

aws ecs register-task-definition --cli-input-json file://proxy-task-ec2.json

As for the controller’s task, let's create a service but with a difference regarding the constraint placement.

If you want to really get the best from the proxy placement, you want to be sure they are not running on the same ECS instance, and for that, there is a  built-in constraint named distinctInstance:

{
    "serviceName": "traefikee-proxies",
    "taskDefinition": "traefikee-proxies",
    "desiredCount": 2,
    "launchType": "EC2",    
    "schedulingStrategy": "REPLICA",
    "placementConstraints": [
      {
        "type": "distinctInstance"
      }
    ]
}

The desired count is 2, as expected in a basic Traefik Enterprise deployment.

Let’s deploy the service:

aws ecs create-service --cli-input-json file://proxy-svc-ec2.json --cluster ${cluster_name}

After a few minutes, the proxy should be up and running and can move forward with the registry.

Registry deployment

By now, you must have gotten used to deploying ECS tasks, but since I’m here to make your life easier, I give you the content of the registry task just below.

{
    "family": "traefikee-registry",    
    "taskRoleArn": "arn:aws:iam::${account-id}:role/TraefikECSReadAccessRole",
    "executionRoleArn": "arn:aws:iam::${account-id}:role/ecsTaskExecutionRole",
    "cpu": "512",
    "memory": "1024",
    "containerDefinitions": [
        {
            "name": "registry",
            "image": "traefik/traefikee:v2.6.1",
            "cpu": 500,
            "portMappings": [
                {
                    "containerPort": 443,
                    "hostPort": 443,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "command": [
                "plugin-registry",
                "--name=registry",
                "--plugindir=/var/lib/plugins",
                "--discovery.static.peers=${controller_instance_ip}:4242"
            ],
            "entryPoint": [],
            "environment": [],
            "environmentFiles": [],
            "secrets": [
                {
                    "name": "TRAEFIKEE_JOIN_TOKEN",
                    "valueFrom": "arn:aws:secretsmanager:eu-north-1:${account-id}:secret:traefikee-secret-cWNokO:TRAEFIKEE_PRX_JOIN_TOKEN::"
                },
                {
                    "name": "TRAEFIKEE_PLUGIN_TOKEN",
                    "valueFrom": "arn:aws:secretsmanager:eu-north-1:${account-id}:secret:traefikee-secret-cWNokO:TRAEFIKEE_PLUGIN_TOKEN::"
                }
            ],
            "volumesFrom": [],
            "dockerLabels": {
                "com.traefik.traefikee.component": "registry"
            },
            "mountPoints": [
                {
                    "sourceVolume": "traefikee-plugins",
                    "containerPath": "/var/lib/plugins",
                    "readOnly": false
                }
            ],
            "dnsServers": [],
            "dnsSearchDomains": [],
            "extraHosts": [],
            "dockerSecurityOptions": [],
            "ulimits": [],
            "systemControls": []
        }
    ],
    "volumes": [
        {
            "name": "traefikee-plugins",       
            "dockerVolumeConfiguration": {
                "scope": "shared",
                "autoprovision": true,
                "driver": "local"                
            }   
        }
    ],    
    "placementConstraints": [],
    "networkMode": "host",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "requiresCompatibilities": [
        "EC2"
    ]
}

For the registry, you need a volume to store the custom plugins you create or you get from the community.

So, let’s register the task definition:

aws ecs register-task-definition --cli-input-json file://registry-task-ec2.json

Let’s prepare the service file with the same placement constraint as the controller. If you defined the same IP for the registry and the controller, or if you want to deploy the registry on another ECS instance, don’t forget to add custom attributes to the ECS instance.

Here’s my service content:

{
    "serviceName": "traefikee-registry",
    "taskDefinition": "traefikee-registry",
    "desiredCount": 1,
    "launchType": "EC2",    
    "schedulingStrategy": "REPLICA",
    "placementConstraints": [
        {
          "expression": "attribute:controller == true",
          "type": "memberOf"
        }
      ]
}

Let’s deploy it:

aws ecs create-service --cli-input-json file://registry-svc-ec2.json --cluster ${cluster_name}

And there you have it! Traefik Enterprise is deployed.

But you are not done just yet.

Configuring Traefik Enterprise

If you’re used to working with Traefik products, you know that Traefik proxies will need configuration for the entry points or even the certificate resolvers.

With Traefik Enterprise the static configuration can be pushed as a YAML or TOML file directly from the teectl command line. I will use this method in this example but keep in mind that Traefik Enterprise could load the static configuration from a file in the container file system.

Enable Teectl command line

Teectl command line is a specific command-line tool used to interact with Traefik Enterprise. You can download it here.

To use teectl you will need to get a configuration from the Traefik Enterprise cluster. To do so, you need to generate it from the controller container.

In an earlier step, I showed you to execute a command to the controller container to get the join token. You can now do the same to get the teectl configuration. Here is the command:

aws ecs execute-command --cluster ${cluster_name} --container controller --task ${task-arn} --interactive --command "/traefikee generate credentials --cluster ${cluster_name} --onpremise.hosts ${controller_instance_public_ip}"

This should output the content of the configuration as-is:

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.
Starting session with SessionId: ecs-execute-command-002f01b38de06784b
cluster_name: ${cluster_name}
tls:
  cert: |
    -----BEGIN CERTIFICATE-----
    MIICAjCCAamgAwIBAgIRAOTxE8Wq4P6wRF1lF82IhOMwCgYIKoZIzj0EAwIwLjEV
    MBMGA1UEChMMVHJhZWZpayBMYWJzMRUwEwYDVQQDEwxUcmFlZmlrRUUgQ0EwIBcN
    MjIwNTEwMTc0NTE5WhgPMjEyMjA0MTYxNzQ1MTlaMBkxFzAVBgNVBAMTDmNsaWVu
    dC5kYmwtZWMyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEh8eGA1K4XZqdh0MG
    El8cIf3VDN6Kx8K5jrgqKonGjTnu7bvax7QNxmt1vUe8NfOTbULgwjvvz48Ggiin
    lEGlyKOBujCBtzAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
    DAYDVR0TAQH/BAIwADApBgNVHQ4EIgQg/gnNqkFwSB57+v9Q5tM9gSdogksKhHQI
    WM8TQM93gLgwKwYDVR0jBCQwIoAgSRfYMAziQ6uXen+xZjCoBuKtEqRB2xiR05eR
    6I1zr6EwKgYDVR0RBCMwIYIOY2xpZW50LmRibC1lYzKCCWxvY2FsaG9zdIcEfwAA
    ATAKBggqhkjOPQQDAgNHADBEAiBi6j5jlVXY+DZVDCGRYG/syikHA4mNmYtHrNqO
    rf4QfwIgD+QIvuWYsz35JXa2KbI38bBbEKwYHhoqgIRMffwNjxI=
    -----END CERTIFICATE-----
  key: |
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIDAieMD6nT/sLY6xLSZ+idngR4r64cUQmCm6ed2na05HoAoGCCqGSM49
    AwEHoUQDQgAEh8eGA1K4XZqdh0MGEl8cIf3VDN6Kx8K5jrgqKonGjTnu7bvax7QN
    xmt1vUe8NfOTbULgwjvvz48GgiinlEGlyA==
    -----END EC PRIVATE KEY-----
  ca: |
    -----BEGIN CERTIFICATE-----
    MIIB1zCCAX2gAwIBAgIQa6AezUUITU49suXxO1VufTAKBggqhkjOPQQDAjAuMRUw
    EwYDVQQKEwxUcmFlZmlrIExhYnMxFTATBgNVBAMTDFRyYWVmaWtFRSBDQTAgFw0y
    MjA1MTAxNTE3NTlaGA8yMTIyMDQxNjE1MTc1OVowLjEVMBMGA1UEChMMVHJhZWZp
    ayBMYWJzMRUwEwYDVQQDEwxUcmFlZmlrRUUgQ0EwWTATBgcqhkjOPQIBBggqhkjO
    PQMBBwNCAAT4RGJgpdsibzTV8shst8v8I3GQ5nUSbL1tukIaMn7O4hyVi52CzV+9
    IaHQYz7CcguIDsXjOCdON7RWO9dM3vwTo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYD
    VR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgSRfYMAziQ6uXen+xZjCoBuKtEqRB2xiR
    05eR6I1zr6EwKwYDVR0jBCQwIoAgSRfYMAziQ6uXen+xZjCoBuKtEqRB2xiR05eR
    6I1zr6EwCgYIKoZIzj0EAwIDSAAwRQIhAOZEoqk3Oxo0frHJaVf0653eR/kKX2WQ
    uS1FFooGQqL+AiAN0zT4MCfPWewLZ11XubMGEo+T3gtaba7pHQBgAvkKgA==
    -----END CERTIFICATE-----
onPremise:
  hosts:
  - ${controller_instance_public_ip}
  port: 55055
pluginregistrytoken: ""
Exiting session with sessionId: ecs-execute-command-002f01b38de06784b.

Put the lines from cluster_name to pluginregistrytoken in a YAML file that you’ll load with the teectl command:

teectl cluster import --file="config.yaml"

If you already use teectl and have already a cluster configured, select the new cluster config to use it:

teectl cluster use --name ${cluster_name}

If everything is ok, you should be able to use a teectl command against the cluster like this:

teectl get nodes

And get this output:

ID                         NAME                                             STATUS  ROLE
4344928c8pon5og0xk70fso1d  controller                                       Ready   Controller (Leader)
eedv9yjd5e06ytpoukjpcde6z  i-0541989dedbaed386.eu-north-1.compute.internal  Ready   Proxy / Ingress
iz0z279rkykbwy17ftxl6lm3r  registry                                         Ready   Plugin Registry
wj4vwuk6opz58mizzuderqfvx  i-0daed77eb281e9fcf.eu-north-1.compute.internal  Ready   Proxy / Ingress

It’s now time to deploy the configuration.

Deploying the static configuration

Using baby steps, you are nearly ready to roll out your first web service! Just one more step to go.

Traefik Enterprise will need to get the information of the ECS API to dynamically create the routes and rules for each and every web application you want to publish.

And this should be defined in the static configuration. Below, you will find a simple configuration that should work for this use case:

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: :443
api:
  dashboard: true
providers:
  ecs:
    exposedByDefault: false
    clusters:
      - ${cluster_name}
    region: ${region}
certificatesResolvers:
  default:
    acme:
      email: "${your_mail}"
      tlschallenge: {}

Let’s push the configuration with this teectl command:

teectl apply --file=static.yaml

If you look at the static configuration, you will see that the Traefik Enterprise dashboard is enabled. Let’s create a simple route to get access to it using a simple YAML file which you can push as a dynamic configuration:

http:
  routers:
    dashboard:
      rule: Host(`${dashboard.domain.com}`)
      service: api@internal
      tls:
        certResolver: default

Apply it as you did for the static configuration:

teectl apply --file=dynamic-dashboard.yaml

To get access to the dashboard, you can add a DNS entry to point to the public IP of one of the instances running a proxy, or you can set a Network Load Balancer (NLB) to forward to each proxy.

Brace yourselves, the fun part is about to begin! Traefik Enterprise is ready and you can check out everything you previously configured through the dashboard.

A quick example

With Traefik Enterprise deployed and configured, you are ready to publish every API, Web application, or any other TCP/UDP backend.

To try out the power of the Traefik Enterprise dynamic configuration in ECS, let’s deploy a simple web app.

This example is a simple web server showing some basic information, like the headers and the IPs of the backend.

As for the deployment of the Traefik Enterprise component, create a task definition for your web app.

Let's check the content of the task below:

{
    "family": "webapp",
    "taskRoleArn": "arn:aws:iam::${account-id}:role/TraefikECSReadAccessRole",
    "executionRoleArn": "arn:aws:iam::${account-id}:role/ecsTaskExecutionRole",
    "cpu": "512",
    "memory": "1024",
    "containerDefinitions": [
        {
            "name": "webapp",
            "image": "traefik/traefikee-webapp-demo:v2",
            "cpu": 500,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 0,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "command": [],
            "linuxParameters": {},
            "environment": [],
            "volumesFrom": [],
            "secrets": [],
            "dockerLabels": {
                "com.traefik.traefikee.component": "container",
                "traefik.enable": "true",
                "traefik.http.services.appv1.loadbalancer.server.port": "80",
                "traefik.http.routers.appv1.rule": "Host(`${webapp.domain.com}`)",
                "traefik.http.routers.appv1.entrypoints": "websecure",
                "traefik.http.routers.appv1.middlewares": "hostHeader,rateLimit",
                "traefik.http.middlewares.hostHeader.headers.customRequestHeaders.X-Custom-Header":"this_is_a_custom_header",
                "traefik.http.middlewares.rateLimit.plugin.ratelimit.average":"2",
                "traefik.http.middlewares.rateLimit.plugin.ratelimit.burst":"2",
                "traefik.http.routers.appv1.tls": "true",
                "traefik.http.routers.appv1.tls.certresolver": "default"
            },
            "mountPoints": [],
            "readonlyRootFilesystem": true,
            "privileged": false
        }
    ],
    "volumes": [],
    "networkMode": "bridge",
    "runtimePlatform": {
        "cpuArchitecture": "X86_64",
        "operatingSystemFamily": "LINUX"
    },
    "requiresCompatibilities": [
        "EC2"
    ]
}

If you remember the task definition from the Traefik Enterprise component, there were no Docker labels specific to Traefik.

Here, these labels are used by Traefik Enterprise to create the configuration, as the definition of the router, the middleware, or the use of the certificate resolver.

Did you know that you can use Traefik Proxy Middleware in Traefik Enterprise? Well, you can! If you want to use some other middleware, you could check the Traefik Proxy middleware list here or the list from Traefik Enterprise documentation here.

Let’s register the webapp task:

aws ecs register-task-definition --cli-input-json file://webapp-task-ec2.json

Create the service file with this simple configuration:

{
    "serviceName": "webapp",
    "taskDefinition": "webapp",
    "desiredCount": 3,
    "launchType": "EC2",    
    "schedulingStrategy": "REPLICA"
}

No need for a placement constraint here, but if you want to make sure you run the backend on ECS instances that are not already running the Traefik component, you can check the AWS documentation about the placement constraints.

Finally, let’s apply the service:

aws ecs create-service --cli-input-json file://webapp-svc-ec2.json --cluster ${cluster_name}

And let the magic happen!

The service is detected from ECS and you can now connect to the URL you set. You can now access your web backend through Traefik Enterprise and see the custom header you set previously through the middleware.

Some final words

With this blog post, I wanted to show you how to use Traefik Enterprise in ECS and how to configure it dynamically for each service you want to publish.

If you followed this step-by-step tutorial, you must realize by now how easy it is to deploy a web application and how you can use Traefik Enterprise to publish and improve your applications, just by adding Docker labels.

I want to emphasize once more that this implementation example is for testing purposes only. If you want to use this configuration in a production environment, make sure you make improvemetns to your configuration, including the DNS configuration regarding the controller and registry and the creation of an NLB in front of each proxy.

I hope you enjoyed this hand-on tutorial and I intrigued you enough to try it out yourself!

Webinar: Centralize OIDC Auth at the API Gateway Level Learn why API gateways simplify authentication and how to configure and manage OpenID Connect using Traefik Enterprise.Watch the Webinar


This is a companion discussion topic for the original entry at https://traefik.io/blog/roll-out-ecs-deployments-with-traefik-enterprise/