Installing Traefik Enterprise ECS¶
This page guides you through the installation of Traefik Enterprise on AWS Elastic Container Service.
AWS Knowledge
Assistance with configuring or setting up AWS services is not included in this guide. If you need more information about any of the components referenced in this guide, start with the following resources:
Requirements¶
- An ECS cluster
- The
aws
CLI tool properly configured to communicate with the cluster - Enough permissions on the ECS instance role to access any integrated AWS service
- All required ports are open on the associated security group of the container instances
- SSH access to your EC2 container instances
- The
- Controller containers can reach
https://v4.license.containous.cloud
- The
teectl
CLI tool, for cluster management
Installation¶
This guide only focuses on the EC2 launch type, it may be possible to run with the Fargate with some customization.
Altough some external AWS resources are referenced in the logConfiguration
and secrets
sections, they are optional and you can just remove or customize them in the task definition, otherwise you must create those resources manually with the AWS Console or CLI tool which is not covered in this guide as well.
Since teectl setup gen
does not provide support for ECS manifest files, this guide demonstrates how to write your own task and service definitions, using JSON syntax, to define the necessary resources and deploy a Traefik Enteprise cluster of one controller and two proxies.
Plugin Registry Token¶
The plugin registry needs a token to secure its communications with the controller. This token is set on the controller and plugin registry task definition.
Here is an example of how to create it:
openssl rand -base64 10
MvXVeX3qDylxJQ==
Controllers¶
Create a file named controller-task.json
with the following task definition:
{
"family": "traefikee-controller",
"taskRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskTraefikee",
"executionRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskTraefikee",
"networkMode": "host",
"containerDefinitions": [
{
"name": "controller-0",
"image": "traefik/traefikee:v2.12.0",
"cpu": 500,
"memory": 1024,
"memoryReservation": 256,
"essential": true,
"hostname": "traefikee-controller-0",
"disableNetworking": false,
"privileged": false,
"readonlyRootFilesystem": true,
"interactive": false,
"pseudoTerminal": false,
"secrets": [
{
"name": "TRAEFIKEE_LICENSE",
"valueFrom": "arn:aws:secretsmanager:aws_region:aws_account_id:secret:traefikee-license-03vFNy:key::"
}
],
"portMappings": [
{
"containerPort": 55055,
"hostPort": 55055,
"protocol": "tcp"
}
],
"command": [
"controller",
"--name=controller-0",
"--advertise=controller-0:4242",
"--discovery.static.peers=",
"--license=$TRAEFIKEE_LICENSE",
"--log.level=",
"--log.filepath=",
"--log.format=",
"--statedir=/data/state",
"--jointoken.file.path=/data/tokens",
"--api.socket=/data/run/teectl-controller-0.sock",
"--socket=/data/run/controller-0.sock",
"--api.autocerts",
"--plugin.url=https://[my-plugin-registry-ip]:443",
"--plugin.token=[my-plugin-registry-token]"
],
"mountPoints": [
{
"sourceVolume": "traefikee-data",
"containerPath": "/data",
"readOnly": false
}
],
"linuxParameters": {
"capabilities": {
"add": [
"NET_BIND_SERVICE"
],
"drop": [
"ALL"
]
}
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "traefikee",
"awslogs-region": "aws_region",
"awslogs-stream-prefix": "controller-0"
}
},
"dockerLabels": {
"com.traefik.traefikee.component": "controllers"
}
}
],
"volumes": [
{
"name": "traefikee-data",
"dockerVolumeConfiguration": {
"scope": "shared",
"autoprovision": true,
"driver": "local"
}
}
],
"requiresCompatibilities": [
"EC2"
]
}
Controller Storage
To keep things simple this guide is using a docker volume configuration with a local
driver. This is fine for a single controller cluster but not suited for a multi controller cluster, because tasks would be spread across different ECS instances. Refer to the official ECS docs for all supported volume drivers.
Customize the file, then register the task definition on ECS:
aws ecs register-task-definition --cli-input-json file://controller-task.json --region $AWS_REGION
Now create a file named controller-svc.json
with the service definition:
{
"serviceName": "traefikee-controllers",
"taskDefinition": "traefikee-controller",
"desiredCount": 1,
"launchType": "EC2",
"enableECSManagedTags": true,
"propagateTags": "SERVICE",
"placementConstraints": [
{
"type": "distinctInstance"
}
],
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "ECS"
}
}
Customize the file and deploy it to the ECS cluster:
aws ecs create-service --cli-input-json file://controller-svc.json --cluster mycluster --region $AWS_REGION
Check the service status to ensure the service was started:
aws ecs describe-services --services traefikee-controllers --cluster mycluster --region $AWS_REGION
{
"services": [
{
"status": "ACTIVE",
"serviceRegistries": [],
"pendingCount": 0,
"launchType": "EC2",
"enableECSManagedTags": true,
"schedulingStrategy": "REPLICA",
"loadBalancers": [],
"placementConstraints": [
{
"type": "distinctInstance"
}
],
"createdAt": 1605815767.081,
"desiredCount": 1,
"serviceName": "traefikee-controllers",
"clusterArn": "arn:aws:ecs:aws_region:aws_account_id:cluster/traefikee-ecs",
"createdBy": "arn:aws:iam::aws_account_id:user/aws_user",
"taskDefinition": "arn:aws:ecs:aws_region:aws_account_id:task-definition/traefikee-controller:7",
"serviceArn": "arn:aws:ecs:aws_region:aws_account_id:service/traefikee-ecs/traefikee-controllers",
"propagateTags": "SERVICE",
"deploymentConfiguration": {
"maximumPercent": 200,
"minimumHealthyPercent": 100
},
"deployments": [
{
"status": "PRIMARY",
"pendingCount": 0,
"launchType": "EC2",
"createdAt": 1605815767.081,
"desiredCount": 1,
"taskDefinition": "arn:aws:ecs:aws_region:aws_account_id:task-definition/traefikee-controller:7",
"updatedAt": 1605815785.903,
"id": "ecs-svc/4291651818662396701",
"runningCount": 1
}
],
"events": [
{
"message": "(service traefikee-controllers) has reached a steady state.",
"id": "2ae7ae80-11bc-48fa-bd1c-ec6235521928",
"createdAt": 1605815785.907
},
{
"message": "(service traefikee-controllers) has started 1 tasks: (task fe932a1e5690403eacd5d3b4f8221b18).",
"id": "901615c4-8e67-4ed2-91f6-b52fcc88d147",
"createdAt": 1605815774.564
}
],
"runningCount": 1,
"placementStrategy": []
}
],
"failures": []
}
Wait until the controllers are up and running before proceeding to the next steps.
Get the Proxy Join Token¶
Fetch the proxy join token by connecting to the container instance and executing the following commands:
# Get the controller container id
CONTAINER_ID=$(docker ps | grep traefikee | cut -f 1 -d " ")
# Get the tokens
docker exec -it $CONTAINER_ID /traefikee tokens --socket /data/run/controller-0.sock
export TRAEFIKEE_CONTROLLER_TOKEN=5531644e5645744f4c5445744e47706f4e7a5a3261574a765a485274616a55345a57526f4d7a566f65444e6f4e544e70616d4a6d615459314e5464684f48526f4d6e6c735933466e4e33686a6348417459544e6c4e5451314e475a70616d4e304e484e796432466f4e445a796447707859673d3d3a303a97b42afa416f0df94d7c453bd4c55fcddd339ec4570068eac2c5cc5504c158d42521e9852615ab95049e2b3296b67c4a
export TRAEFIKEE_PROXY_TOKEN=5531644e5645744f4c5445744e47706f4e7a5a3261574a765a485274616a55345a57526f4d7a566f65444e6f4e544e70616d4a6d615459314e5464684f48526f4d6e6c735933466e4e33686a634841744e47397563484d31636a4d354d7a42354e5463785933557762546b774e7a55794f413d3d3a313a97b42afa416f0df94d7c453bd4c55fcddd339ec4570068eac2c5cc5504c158d42194b3059e6861761121966a44f964d3
Write down the proxy token as it is required for the next step, the proxies task definition.
Proxies¶
Create a file named proxies-task.json
with the following task definition:
{
"family": "traefikee-proxies",
"taskRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskTraefikee",
"networkMode": "host",
"containerDefinitions": [
{
"name": "proxy",
"image": "traefik/traefikee:v2.12.0",
"disableNetworking": false,
"privileged": false,
"readonlyRootFilesystem": true,
"interactive": true,
"pseudoTerminal": true,
"essential": true,
"cpu": 500,
"memory": 1024,
"memoryReservation": 256,
"portMappings": [
{
"containerPort": 80,
"hostPort": 80,
"protocol": "tcp"
},
{
"containerPort": 443,
"hostPort": 443,
"protocol": "tcp"
},
{
"containerPort": 8484,
"hostPort": 8484,
"protocol": "tcp"
}
],
"command": [
"traefikee",
"proxy",
"--role=ingress",
"--discovery.static.peers=[my-controller-ip]:4242",
"--jointoken.value=[my-proxy-join-token]",
"--log.level=",
"--log.filepath=",
"--log.format="
],
"linuxParameters": {
"capabilities": {
"add": [
"NET_BIND_SERVICE"
],
"drop": [
"ALL"
]
}
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "traefikee",
"awslogs-region": "aws_region",
"awslogs-stream-prefix": "proxies"
}
},
"dockerLabels": {
"com.containous.traefikee.component": "proxies"
}
}
],
"requiresCompatibilities": [
"EC2"
]
}
Customize the file then register the task definition on ECS:
aws ecs register-task-definition --cli-input-json file://proxies-task.json --region $AWS_REGION
Now create a file named proxies-svc.json
with the service definition:
{
"serviceName": "traefikee-proxies",
"taskDefinition": "traefikee-proxies",
"desiredCount": 2,
"launchType": "EC2",
"enableECSManagedTags": true,
"propagateTags": "SERVICE",
"placementConstraints": [
{
"type": "distinctInstance"
}
],
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "ECS"
}
}
Customize the file and deploy it to the ECS cluster:
aws ecs create-service --cli-input-json file://proxies-svc.json --cluster mycluster --region $AWS_REGION
Check the service status to ensure the service was started:
aws ecs describe-services --services traefikee-proxies --cluster mycluster --region $AWS_REGION
{
"services": [
{
"status": "ACTIVE",
"serviceRegistries": [],
"pendingCount": 0,
"launchType": "EC2",
"enableECSManagedTags": true,
"schedulingStrategy": "REPLICA",
"loadBalancers": [],
"placementConstraints": [
{
"type": "distinctInstance"
}
],
"createdAt": 1605879947.392,
"desiredCount": 2,
"serviceName": "traefikee-proxies",
"clusterArn": "arn:aws:ecs:aws_region:aws_account_id:cluster/traefikee-ecs",
"createdBy": "arn:aws:iam::aws_account_id:user/aws_user",
"taskDefinition": "arn:aws:ecs:aws_region:aws_account_id:task-definition/traefikee-proxies:5",
"serviceArn": "arn:aws:ecs:aws_region:aws_account_id:service/traefikee-ecs/traefikee-proxies",
"propagateTags": "SERVICE",
"deploymentConfiguration": {
"maximumPercent": 200,
"minimumHealthyPercent": 100
},
"deployments": [
{
"status": "PRIMARY",
"pendingCount": 0,
"launchType": "EC2",
"createdAt": 1605880984.365,
"desiredCount": 2,
"taskDefinition": "arn:aws:ecs:aws_region:aws_account_id:task-definition/traefikee-proxies:5",
"updatedAt": 1605881208.064,
"id": "ecs-svc/4529007424825088897",
"runningCount": 2
}
],
"events": [
{
"message": "(service traefikee-proxies) has reached a steady state.",
"id": "51e07037-3595-40fa-a394-c489561fcd11",
"createdAt": 1605881208.068
},
{
"message": "(service traefikee-proxies) has started 1 tasks: (task 11ad2458e5624330955dc97f6ce41d41).",
"id": "7e74652c-f88c-4e0f-b922-446ac8a7a114",
"createdAt": 1605881197.123
},
{
"message": "(service traefikee-proxies) has reached a steady state.",
"id": "2d857d41-658a-452b-b7d6-c1c3bb4a2944",
"createdAt": 1605881165.664
},
{
"message": "(service traefikee-proxies) has started 1 tasks: (task b94de86d3664413081c045ed25f9b326).",
"id": "74bb63a2-bbf8-45a2-bc22-a5a266b4289e",
"createdAt": 1605881153.114
},
],
"runningCount": 2,
"placementStrategy": []
}
],
"failures": []
}
Plugin Registry¶
Create a file named registry-task.json
with the following task definition:
{
"executionRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"portMappings": [
{
"hostPort": 443,
"protocol": "tcp",
"containerPort": 443
}
],
"command": [
"plugin-registry",
"--name=registry",
"--plugindir=/var/lib/plugins",
"--token=[my-plugin-registry-token]",
"--log.level=debug",
"--log.filepath=",
"--log.format=",
"--discovery.static.peers=[my-controller-ip]:4242",
"--jointoken.file.path=/data/tokens"
],
"cpu": 500,
"mountPoints": [
{
"readOnly": false,
"containerPath": "/data",
"sourceVolume": "traefikee-data"
}
],
"memory": 128,
"image": "traefik/traefikee:v2.12.0",
"name": "registry"
}
],
"taskRoleArn": "arn:aws:iam::aws_account_id:role/ecsTaskExecutionRole",
"family": "registry",
"requiresCompatibilities": [
"EC2"
],
"volumes": [
{
"name": "traefikee-data",
"dockerVolumeConfiguration": {
"autoprovision": false,
"scope": "shared",
"driver": "local"
}
}
]
}
Customize the file then register the task definition on ECS:
aws ecs register-task-definition --cli-input-json file://registry-task.json --region $AWS_REGION
Now create a file named registry-svc.json
with the service definition:
{
"serviceName": "registry",
"taskDefinition": "registry",
"desiredCount": 2,
"launchType": "EC2",
"enableECSManagedTags": true,
"propagateTags": "SERVICE",
"placementConstraints": [
{
"type": "distinctInstance"
}
],
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "ECS"
}
}
Customize the file and deploy it to the ECS cluster:
aws ecs create-service --cli-input-json file://registry-svc.json --cluster mycluster --region $AWS_REGION
Check the service status to ensure the service was started:
aws ecs describe-services --services registry --cluster mycluster --region $AWS_REGION
{
"services": [
{
"serviceArn": "arn:aws:ecs:eu-north-1:114072598128:service/rc2/registry",
"serviceName": "registry",
"clusterArn": "arn:aws:ecs:eu-north-1:114072598128:cluster/rc2",
"loadBalancers": [],
"serviceRegistries": [],
"status": "ACTIVE",
"desiredCount": 1,
"runningCount": 1,
"pendingCount": 0,
"launchType": "EC2",
"taskDefinition": "arn:aws:ecs:eu-north-1:114072598128:task-definition/registry:2",
"deploymentConfiguration": {
"deploymentCircuitBreaker": {
"enable": false,
"rollback": false
},
"maximumPercent": 200,
"minimumHealthyPercent": 100
},
"deployments": [
{
"id": "ecs-svc/5244500742569760246",
"status": "PRIMARY",
"taskDefinition": "arn:aws:ecs:eu-north-1:114072598128:task-definition/registry:2",
"desiredCount": 1,
"pendingCount": 0,
"runningCount": 1,
"failedTasks": 0,
"createdAt": 1610097942.755,
"updatedAt": 1610097977.375,
"launchType": "EC2",
"rolloutState": "COMPLETED",
"rolloutStateReason": "ECS deployment ecs-svc/5244500742569760246 completed."
}
],
"events": [
{
"id": "9b8f4549-e972-4506-848a-8050084f57f0",
"createdAt": 1610097977.381,
"message": "(service registry) has reached a steady state."
},
{
"id": "adee0f69-5ceb-4f11-8fd9-47effc1b39c2",
"createdAt": 1610097977.38,
"message": "(service registry) (deployment ecs-svc/5244500742569760246) deployment completed."
},
{
"id": "6b070094-8686-4ab7-9886-82c248203698",
"createdAt": 1610097966.485,
"message": "(service registry) has started 1 tasks: (task 65e57cd8504346da8a8d654917919c7b)."
},
],
"createdAt": 1610096466.659,
"placementConstraints": [],
"placementStrategy": [
{
"type": "spread",
"field": "attribute:ecs.availability-zone"
},
{
"type": "spread",
"field": "instanceId"
}
],
"schedulingStrategy": "REPLICA",
"createdBy": "arn:aws:iam::114072598128:user/xxx",
"enableECSManagedTags": true,
"propagateTags": "NONE"
}
],
"failures": []
}
Remote Access Through teectl
¶
Once your cluster is ready, if you want to operate the cluster remotely using the teectl
tool, you will need to
generate credentials from your cluster using traefikee generate credentials
on one of your controllers and use teectl
to import the cluster credentials.
First connect to a container instance running a controller task, then run:
# Get the controller container id
CONTAINER_ID=$(docker ps | grep traefikee | cut -f 1 -d " ")
# Get a teectl config with credentials
docker exec -it $CONTAINER_ID /traefikee generate c --onpremise.hosts="[Container-Instance-Public-IP]" --cluster ecs --socket /data/run/teectl-controller-0.sock
cluster_name: default
tls:
cert: |
-----BEGIN CERTIFICATE-----
MIIB+jCCAaCgAwIBAgIQKHgQX14LGetAsD8wSEta5jAKBggqhkjOPQQDAjAuMRUw
EwYDVQQKEwxUcmFlZmlrIExhYnMxFTATBgNVBAMTDFRyYWVmaWtFRSBDQTAgFw0y
MDExMTkyMDAwMDNaGA8yMTIwMTAyNjIwMDAwM1owFTETMBEGA1UEAxMKY2xpZW50
LmVjczBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKcFJkSGRxbfAnQx2JtYpEnA
knHdqukFWF8Sbvht30Ge/EnBrTe37ilzJ0KlY11UQK/KlGPMoqVSrjAnqZz10nGj
gbYwgbMwDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1Ud
EwEB/wQCMAAwKQYDVR0OBCIEIKh1ygHajrADFcxpfv4p/ff7FH4lDULi0uqcDp7j
qaylMCsGA1UdIwQkMCKAILoCK8uf89Yveb/uZN+Vs+gfvz05Y6NHB2/hd3T6ix3T
MCYGA1UdEQQfMB2CCmNsaWVudC5lY3OCCWxvY2FsaG9zdIcEfwAAATAKBggqhkjO
PQQDAgNIADBFAiEAiXT09co8egGQ0Y6hJ/lgIzCGC0I9GiFfvrxCGKl15l8CICYY
E6ig/k2D4coprOclVF5QVkqHCcx2EL0HA/zK05eI
-----END CERTIFICATE-----
key: |
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49
AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu
KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ==
-----END EC PRIVATE KEY-----
ca: |
-----BEGIN CERTIFICATE-----
MIIB2DCCAX2gAwIBAgIQDEHgiwDfaIX3wS1EHF800DAKBggqhkjOPQQDAjAuMRUw
EwYDVQQKEwxUcmFlZmlrIExhYnMxFTATBgNVBAMTDFRyYWVmaWtFRSBDQTAgFw0y
MDExMTkxOTU2MjBaGA8yMTIwMTAyNjE5NTYyMFowLjEVMBMGA1UEChMMVHJhZWZp
ayBMYWJzMRUwEwYDVQQDEwxUcmFlZmlrRUUgQ0EwWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAATe+gM99l/nAAeNIy/kn8vbrcSNORhbWGBUp+j1CtL2ADqLYIel/acB
B3ssLPnbAZLoKJefrQS/CNJOdZpRZBnfo3sweTAOBgNVHQ8BAf8EBAMCAYYwDwYD
VR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgugIry5/z1i95v+5k35Wz6B+/PTljo0cH
b+F3dPqLHdMwKwYDVR0jBCQwIoAgugIry5/z1i95v+5k35Wz6B+/PTljo0cHb+F3
dPqLHdMwCgYIKoZIzj0EAwIDSQAwRgIhAMRjpKzzAZr5wT5IXMaBWv0OUvy6wxFn
kyZApTzhcbr9AiEAtaJZ7DN+xLeA0/RUljK0uDKuZALix30VSx4U2aaYdc4=
-----END CERTIFICATE-----
onPremise:
hosts:
- ec2-18-223-125-157.us-east-1.compute.amazonaws.com
port: 55055
Save the output of the last command above to a file named teectl-config.yaml
, then from your machine run:
teectl cluster import --file="teectl-config.yaml"
You can now use teectl
to operate your cluster.
teectl get nodes
ID NAME STATUS ROLE
hlx1b3gu8bb5n1lg8qtiy5nvv controller-0 Ready Controller (Leader)
jm5wv9kdmp9imspqx39n300b3 ip-10-0-0-130.us-east-1.compute.internal Ready Proxy / Ingress
y0l2me7zjalidnzqf3fqanuxk ip-10-0-0-178.us-east-1.compute.internal Ready Proxy / Ingress
What's Next?
Learn more about the features that Traefik Enterprise provides.
License Monitoring¶
When a Traefik Enterprise controller starts for the first time, it checks the license validity.
If the license is valid, another check is done once every 24 hours.
If the controller can't communicate with the license server, a 72-day grace period starts to recover from this situation.
Once the grace period is over, the controller stops updating the proxies configuration.
Please look at the FAQ to know how to implement the license monitoring.