UNPKG

38.3 kBMarkdownView Raw
1# Amazon ECS Construct Library
2<!--BEGIN STABILITY BANNER-->
3
4---
5
6![cfn-resources: Stable](https://img.shields.io/badge/cfn--resources-stable-success.svg?style=for-the-badge)
7
8![cdk-constructs: Stable](https://img.shields.io/badge/cdk--constructs-stable-success.svg?style=for-the-badge)
9
10---
11
12<!--END STABILITY BANNER-->
13
14This package contains constructs for working with **Amazon Elastic Container
15Service** (Amazon ECS).
16
17Amazon Elastic Container Service (Amazon ECS) is a fully managed container orchestration service.
18
19For further information on Amazon ECS,
20see the [Amazon ECS documentation](https://docs.aws.amazon.com/ecs)
21
22The following example creates an Amazon ECS cluster, adds capacity to it, and
23runs a service on it:
24
25```ts
26declare const vpc: ec2.Vpc;
27
28// Create an ECS cluster
29const cluster = new ecs.Cluster(this, 'Cluster', {
30 vpc,
31});
32
33// Add capacity to it
34cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
35 instanceType: new ec2.InstanceType("t2.xlarge"),
36 desiredCapacity: 3,
37});
38
39const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
40
41taskDefinition.addContainer('DefaultContainer', {
42 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
43 memoryLimitMiB: 512,
44});
45
46// Instantiate an Amazon ECS Service
47const ecsService = new ecs.Ec2Service(this, 'Service', {
48 cluster,
49 taskDefinition,
50});
51```
52
53For a set of constructs defining common ECS architectural patterns, see the `@aws-cdk/aws-ecs-patterns` package.
54
55## Launch Types: AWS Fargate vs Amazon EC2
56
57There are two sets of constructs in this library; one to run tasks on Amazon EC2 and
58one to run tasks on AWS Fargate.
59
60- Use the `Ec2TaskDefinition` and `Ec2Service` constructs to run tasks on Amazon EC2 instances running in your account.
61- Use the `FargateTaskDefinition` and `FargateService` constructs to run tasks on
62 instances that are managed for you by AWS.
63- Use the `ExternalTaskDefinition` and `ExternalService` constructs to run AWS ECS Anywhere tasks on self-managed infrastructure.
64
65Here are the main differences:
66
67- **Amazon EC2**: instances are under your control. Complete control of task to host
68 allocation. Required to specify at least a memory reservation or limit for
69 every container. Can use Host, Bridge and AwsVpc networking modes. Can attach
70 Classic Load Balancer. Can share volumes between container and host.
71- **AWS Fargate**: tasks run on AWS-managed instances, AWS manages task to host
72 allocation for you. Requires specification of memory and cpu sizes at the
73 taskdefinition level. Only supports AwsVpc networking modes and
74 Application/Network Load Balancers. Only the AWS log driver is supported.
75 Many host features are not supported such as adding kernel capabilities
76 and mounting host devices/volumes inside the container.
77- **AWS ECSAnywhere**: tasks are run and managed by AWS ECS Anywhere on infrastructure owned by the customer. Only Bridge networking mode is supported. Does not support autoscaling, load balancing, cloudmap or attachment of volumes.
78
79For more information on Amazon EC2 vs AWS Fargate, networking and ECS Anywhere see the AWS Documentation:
80[AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html),
81[Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html),
82[ECS Anywhere](https://aws.amazon.com/ecs/anywhere/)
83
84## Clusters
85
86A `Cluster` defines the infrastructure to run your
87tasks on. You can run many tasks on a single cluster.
88
89The following code creates a cluster that can run AWS Fargate tasks:
90
91```ts
92declare const vpc: ec2.Vpc;
93
94const cluster = new ecs.Cluster(this, 'Cluster', {
95 vpc,
96});
97```
98
99The following code imports an existing cluster using the ARN which can be used to
100import an Amazon ECS service either EC2 or Fargate.
101
102```ts
103const clusterArn = 'arn:aws:ecs:us-east-1:012345678910:cluster/clusterName';
104
105const cluster = ecs.Cluster.fromClusterArn(this, 'Cluster', clusterArn);
106```
107
108To use tasks with Amazon EC2 launch-type, you have to add capacity to
109the cluster in order for tasks to be scheduled on your instances. Typically,
110you add an AutoScalingGroup with instances running the latest
111Amazon ECS-optimized AMI to the cluster. There is a method to build and add such an
112AutoScalingGroup automatically, or you can supply a customized AutoScalingGroup
113that you construct yourself. It's possible to add multiple AutoScalingGroups
114with various instance types.
115
116The following example creates an Amazon ECS cluster and adds capacity to it:
117
118```ts
119declare const vpc: ec2.Vpc;
120
121const cluster = new ecs.Cluster(this, 'Cluster', {
122 vpc,
123});
124
125// Either add default capacity
126cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
127 instanceType: new ec2.InstanceType("t2.xlarge"),
128 desiredCapacity: 3,
129});
130
131// Or add customized capacity. Be sure to start the Amazon ECS-optimized AMI.
132const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
133 vpc,
134 instanceType: new ec2.InstanceType('t2.xlarge'),
135 machineImage: ecs.EcsOptimizedImage.amazonLinux(),
136 // Or use Amazon ECS-Optimized Amazon Linux 2 AMI
137 // machineImage: EcsOptimizedImage.amazonLinux2(),
138 desiredCapacity: 3,
139 // ... other options here ...
140});
141
142const capacityProvider = new ecs.AsgCapacityProvider(this, 'AsgCapacityProvider', {
143 autoScalingGroup,
144});
145cluster.addAsgCapacityProvider(capacityProvider);
146```
147
148If you omit the property `vpc`, the construct will create a new VPC with two AZs.
149
150By default, all machine images will auto-update to the latest version
151on each deployment, causing a replacement of the instances in your AutoScalingGroup
152if the AMI has been updated since the last deployment.
153
154If task draining is enabled, ECS will transparently reschedule tasks on to the new
155instances before terminating your old instances. If you have disabled task draining,
156the tasks will be terminated along with the instance. To prevent that, you
157can pick a non-updating AMI by passing `cacheInContext: true`, but be sure
158to periodically update to the latest AMI manually by using the [CDK CLI
159context management commands](https://docs.aws.amazon.com/cdk/latest/guide/context.html):
160
161```ts
162declare const vpc: ec2.Vpc;
163const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
164 machineImage: ecs.EcsOptimizedImage.amazonLinux({ cachedInContext: true }),
165 vpc,
166 instanceType: new ec2.InstanceType('t2.micro'),
167});
168```
169
170### Bottlerocket
171
172[Bottlerocket](https://aws.amazon.com/bottlerocket/) is a Linux-based open source operating system that is
173purpose-built by AWS for running containers. You can launch Amazon ECS container instances with the Bottlerocket AMI.
174
175The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI.
176
177The following example adds Bottlerocket capacity to the cluster:
178
179```ts
180declare const cluster: ecs.Cluster;
181
182cluster.addCapacity('bottlerocket-asg', {
183 minCapacity: 2,
184 instanceType: new ec2.InstanceType('c5.large'),
185 machineImage: new ecs.BottleRocketImage(),
186});
187```
188
189### ARM64 (Graviton) Instances
190
191To launch instances with ARM64 hardware, you can use the Amazon ECS-optimized
192Amazon Linux 2 (arm64) AMI. Based on Amazon Linux 2, this AMI is recommended
193for use when launching your EC2 instances that are powered by Arm-based AWS
194Graviton Processors.
195
196```ts
197declare const cluster: ecs.Cluster;
198
199cluster.addCapacity('graviton-cluster', {
200 minCapacity: 2,
201 instanceType: new ec2.InstanceType('c6g.large'),
202 machineImage: ecs.EcsOptimizedImage.amazonLinux2(ecs.AmiHardwareType.ARM),
203});
204```
205
206Bottlerocket is also supported:
207
208```ts
209declare const cluster: ecs.Cluster;
210
211cluster.addCapacity('graviton-cluster', {
212 minCapacity: 2,
213 instanceType: new ec2.InstanceType('c6g.large'),
214 machineImageType: ecs.MachineImageType.BOTTLEROCKET,
215});
216```
217
218### Spot Instances
219
220To add spot instances into the cluster, you must specify the `spotPrice` in the `ecs.AddCapacityOptions` and optionally enable the `spotInstanceDraining` property.
221
222```ts
223declare const cluster: ecs.Cluster;
224
225// Add an AutoScalingGroup with spot instances to the existing cluster
226cluster.addCapacity('AsgSpot', {
227 maxCapacity: 2,
228 minCapacity: 2,
229 desiredCapacity: 2,
230 instanceType: new ec2.InstanceType('c5.xlarge'),
231 spotPrice: '0.0735',
232 // Enable the Automated Spot Draining support for Amazon ECS
233 spotInstanceDraining: true,
234});
235```
236
237### SNS Topic Encryption
238
239When the `ecs.AddCapacityOptions` that you provide has a non-zero `taskDrainTime` (the default) then an SNS topic and Lambda are created to ensure that the
240cluster's instances have been properly drained of tasks before terminating. The SNS Topic is sent the instance-terminating lifecycle event from the AutoScalingGroup,
241and the Lambda acts on that event. If you wish to engage [server-side encryption](https://docs.aws.amazon.com/sns/latest/dg/sns-data-encryption.html) for this SNS Topic
242then you may do so by providing a KMS key for the `topicEncryptionKey` property of `ecs.AddCapacityOptions`.
243
244```ts
245// Given
246declare const cluster: ecs.Cluster;
247declare const key: kms.Key;
248// Then, use that key to encrypt the lifecycle-event SNS Topic.
249cluster.addCapacity('ASGEncryptedSNS', {
250 instanceType: new ec2.InstanceType("t2.xlarge"),
251 desiredCapacity: 3,
252 topicEncryptionKey: key,
253});
254```
255
256## Task definitions
257
258A task definition describes what a single copy of a **task** should look like.
259A task definition has one or more containers; typically, it has one
260main container (the *default container* is the first one that's added
261to the task definition, and it is marked *essential*) and optionally
262some supporting containers which are used to support the main container,
263doings things like upload logs or metrics to monitoring services.
264
265To run a task or service with Amazon EC2 launch type, use the `Ec2TaskDefinition`. For AWS Fargate tasks/services, use the
266`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes
267provide simplified APIs that only contain properties relevant for each specific launch type.
268
269For a `FargateTaskDefinition`, specify the task size (`memoryLimitMiB` and `cpu`):
270
271```ts
272const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
273 memoryLimitMiB: 512,
274 cpu: 256,
275});
276```
277
278On Fargate Platform Version 1.4.0 or later, you may specify up to 200GiB of
279[ephemeral storage](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-task-storage.html#fargate-task-storage-pv14):
280
281```ts
282const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
283 memoryLimitMiB: 512,
284 cpu: 256,
285 ephemeralStorageGiB: 100,
286});
287```
288
289To add containers to a task definition, call `addContainer()`:
290
291```ts
292const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
293 memoryLimitMiB: 512,
294 cpu: 256,
295});
296const container = fargateTaskDefinition.addContainer("WebContainer", {
297 // Use an image from DockerHub
298 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
299 // ... other options here ...
300});
301```
302
303For a `Ec2TaskDefinition`:
304
305```ts
306const ec2TaskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef', {
307 networkMode: ecs.NetworkMode.BRIDGE,
308});
309
310const container = ec2TaskDefinition.addContainer("WebContainer", {
311 // Use an image from DockerHub
312 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
313 memoryLimitMiB: 1024,
314 // ... other options here ...
315});
316```
317
318For an `ExternalTaskDefinition`:
319
320```ts
321const externalTaskDefinition = new ecs.ExternalTaskDefinition(this, 'TaskDef');
322
323const container = externalTaskDefinition.addContainer("WebContainer", {
324 // Use an image from DockerHub
325 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
326 memoryLimitMiB: 1024,
327 // ... other options here ...
328});
329```
330
331You can specify container properties when you add them to the task definition, or with various methods, e.g.:
332
333To add a port mapping when adding a container to the task definition, specify the `portMappings` option:
334
335```ts
336declare const taskDefinition: ecs.TaskDefinition;
337
338taskDefinition.addContainer("WebContainer", {
339 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
340 memoryLimitMiB: 1024,
341 portMappings: [{ containerPort: 3000 }],
342});
343```
344
345To add port mappings directly to a container definition, call `addPortMappings()`:
346
347```ts
348declare const container: ecs.ContainerDefinition;
349
350container.addPortMappings({
351 containerPort: 3000,
352});
353```
354
355To add data volumes to a task definition, call `addVolume()`:
356
357```ts
358const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
359 memoryLimitMiB: 512,
360 cpu: 256,
361});
362const volume = {
363 // Use an Elastic FileSystem
364 name: "mydatavolume",
365 efsVolumeConfiguration: {
366 fileSystemId: "EFS",
367 // ... other options here ...
368 },
369};
370
371const container = fargateTaskDefinition.addVolume(volume);
372```
373
374> Note: ECS Anywhere doesn't support volume attachments in the task definition.
375
376To use a TaskDefinition that can be used with either Amazon EC2 or
377AWS Fargate launch types, use the `TaskDefinition` construct.
378
379When creating a task definition you have to specify what kind of
380tasks you intend to run: Amazon EC2, AWS Fargate, or both.
381The following example uses both:
382
383```ts
384const taskDefinition = new ecs.TaskDefinition(this, 'TaskDef', {
385 memoryMiB: '512',
386 cpu: '256',
387 networkMode: ecs.NetworkMode.AWS_VPC,
388 compatibility: ecs.Compatibility.EC2_AND_FARGATE,
389});
390```
391
392### Images
393
394Images supply the software that runs inside the container. Images can be
395obtained from either DockerHub or from ECR repositories, built directly from a local Dockerfile, or use an existing tarball.
396
397- `ecs.ContainerImage.fromRegistry(imageName)`: use a public image.
398- `ecs.ContainerImage.fromRegistry(imageName, { credentials: mySecret })`: use a private image that requires credentials.
399- `ecs.ContainerImage.fromEcrRepository(repo, tagOrDigest)`: use the given ECR repository as the image
400 to start. If no tag or digest is provided, "latest" is assumed.
401- `ecs.ContainerImage.fromAsset('./image')`: build and upload an
402 image directly from a `Dockerfile` in your source directory.
403- `ecs.ContainerImage.fromDockerImageAsset(asset)`: uses an existing
404 `@aws-cdk/aws-ecr-assets.DockerImageAsset` as a container image.
405- `ecs.ContainerImage.fromTarball(file)`: use an existing tarball.
406- `new ecs.TagParameterContainerImage(repository)`: use the given ECR repository as the image
407 but a CloudFormation parameter as the tag.
408
409### Environment variables
410
411To pass environment variables to the container, you can use the `environment`, `environmentFiles`, and `secrets` props.
412
413```ts
414declare const secret: secretsmanager.Secret;
415declare const dbSecret: secretsmanager.Secret;
416declare const parameter: ssm.StringParameter;
417declare const taskDefinition: ecs.TaskDefinition;
418declare const s3Bucket: s3.Bucket;
419
420const newContainer = taskDefinition.addContainer('container', {
421 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
422 memoryLimitMiB: 1024,
423 environment: { // clear text, not for sensitive data
424 STAGE: 'prod',
425 },
426 environmentFiles: [ // list of environment files hosted either on local disk or S3
427 ecs.EnvironmentFile.fromAsset('./demo-env-file.env'),
428 ecs.EnvironmentFile.fromBucket(s3Bucket, 'assets/demo-env-file.env'),
429 ],
430 secrets: { // Retrieved from AWS Secrets Manager or AWS Systems Manager Parameter Store at container start-up.
431 SECRET: ecs.Secret.fromSecretsManager(secret),
432 DB_PASSWORD: ecs.Secret.fromSecretsManager(dbSecret, 'password'), // Reference a specific JSON field, (requires platform version 1.4.0 or later for Fargate tasks)
433 API_KEY: ecs.Secret.fromSecretsManagerVersion(secret, { versionId: '12345' }, 'apiKey'), // Reference a specific version of the secret by its version id or version stage (requires platform version 1.4.0 or later for Fargate tasks)
434 PARAMETER: ecs.Secret.fromSsmParameter(parameter),
435 },
436});
437newContainer.addEnvironment('QUEUE_NAME', 'MyQueue');
438```
439
440The task execution role is automatically granted read permissions on the secrets/parameters. Support for environment
441files is restricted to the EC2 launch type for files hosted on S3. Further details provided in the AWS documentation
442about [specifying environment variables](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/taskdef-envfiles.html).
443
444### System controls
445
446To set system controls (kernel parameters) on the container, use the `systemControls` prop:
447
448```ts
449declare const taskDefinition: ecs.TaskDefinition;
450
451taskDefinition.addContainer('container', {
452 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
453 memoryLimitMiB: 1024,
454 systemControls: [
455 {
456 namespace: 'net',
457 value: 'ipv4.tcp_tw_recycle',
458 },
459 ],
460});
461```
462
463### Using Windows containers on Fargate
464
465AWS Fargate supports Amazon ECS Windows containers. For more details, please see this [blog post](https://aws.amazon.com/tw/blogs/containers/running-windows-containers-with-amazon-ecs-on-aws-fargate/)
466
467```ts
468// Create a Task Definition for the Windows container to start
469const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
470 runtimePlatform: {
471 operatingSystemFamily: ecs.OperatingSystemFamily.WINDOWS_SERVER_2019_CORE,
472 cpuArchitecture: ecs.CpuArchitecture.X86_64,
473 },
474 cpu: 1024,
475 memoryLimitMiB: 2048,
476});
477
478taskDefinition.addContainer('windowsservercore', {
479 logging: ecs.LogDriver.awsLogs({ streamPrefix: 'win-iis-on-fargate' }),
480 portMappings: [{ containerPort: 80 }],
481 image: ecs.ContainerImage.fromRegistry('mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019'),
482});
483```
484
485### Using Graviton2 with Fargate
486
487AWS Graviton2 supports AWS Fargate. For more details, please see this [blog post](https://aws.amazon.com/blogs/aws/announcing-aws-graviton2-support-for-aws-fargate-get-up-to-40-better-price-performance-for-your-serverless-containers/)
488
489```ts
490// Create a Task Definition for running container on Graviton Runtime.
491const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
492 runtimePlatform: {
493 operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
494 cpuArchitecture: ecs.CpuArchitecture.ARM64,
495 },
496 cpu: 1024,
497 memoryLimitMiB: 2048,
498});
499
500taskDefinition.addContainer('webarm64', {
501 logging: ecs.LogDriver.awsLogs({ streamPrefix: 'graviton2-on-fargate' }),
502 portMappings: [{ containerPort: 80 }],
503 image: ecs.ContainerImage.fromRegistry('public.ecr.aws/nginx/nginx:latest-arm64v8'),
504});
505```
506
507## Service
508
509A `Service` instantiates a `TaskDefinition` on a `Cluster` a given number of
510times, optionally associating them with a load balancer.
511If a task fails,
512Amazon ECS automatically restarts the task.
513
514```ts
515declare const cluster: ecs.Cluster;
516declare const taskDefinition: ecs.TaskDefinition;
517
518const service = new ecs.FargateService(this, 'Service', {
519 cluster,
520 taskDefinition,
521 desiredCount: 5,
522});
523```
524
525ECS Anywhere service definition looks like:
526
527```ts
528declare const cluster: ecs.Cluster;
529declare const taskDefinition: ecs.TaskDefinition;
530
531const service = new ecs.ExternalService(this, 'Service', {
532 cluster,
533 taskDefinition,
534 desiredCount: 5,
535});
536```
537
538`Services` by default will create a security group if not provided.
539If you'd like to specify which security groups to use you can override the `securityGroups` property.
540
541### Deployment circuit breaker and rollback
542
543Amazon ECS [deployment circuit breaker](https://aws.amazon.com/tw/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/)
544automatically rolls back unhealthy service deployments without the need for manual intervention. Use `circuitBreaker` to enable
545deployment circuit breaker and optionally enable `rollback` for automatic rollback. See [Using the deployment circuit breaker](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-type-ecs.html)
546for more details.
547
548```ts
549declare const cluster: ecs.Cluster;
550declare const taskDefinition: ecs.TaskDefinition;
551const service = new ecs.FargateService(this, 'Service', {
552 cluster,
553 taskDefinition,
554 circuitBreaker: { rollback: true },
555});
556```
557
558> Note: ECS Anywhere doesn't support deployment circuit breakers and rollback.
559
560### Include an application/network load balancer
561
562`Services` are load balancing targets and can be added to a target group, which will be attached to an application/network load balancers:
563
564```ts
565declare const vpc: ec2.Vpc;
566declare const cluster: ecs.Cluster;
567declare const taskDefinition: ecs.TaskDefinition;
568const service = new ecs.FargateService(this, 'Service', { cluster, taskDefinition });
569
570const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
571const listener = lb.addListener('Listener', { port: 80 });
572const targetGroup1 = listener.addTargets('ECS1', {
573 port: 80,
574 targets: [service],
575});
576const targetGroup2 = listener.addTargets('ECS2', {
577 port: 80,
578 targets: [service.loadBalancerTarget({
579 containerName: 'MyContainer',
580 containerPort: 8080
581 })],
582});
583```
584
585> Note: ECS Anywhere doesn't support application/network load balancers.
586
587Note that in the example above, the default `service` only allows you to register the first essential container or the first mapped port on the container as a target and add it to a new target group. To have more control over which container and port to register as targets, you can use `service.loadBalancerTarget()` to return a load balancing target for a specific container and port.
588
589Alternatively, you can also create all load balancer targets to be registered in this service, add them to target groups, and attach target groups to listeners accordingly.
590
591```ts
592declare const cluster: ecs.Cluster;
593declare const taskDefinition: ecs.TaskDefinition;
594declare const vpc: ec2.Vpc;
595const service = new ecs.FargateService(this, 'Service', { cluster, taskDefinition });
596
597const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
598const listener = lb.addListener('Listener', { port: 80 });
599service.registerLoadBalancerTargets(
600 {
601 containerName: 'web',
602 containerPort: 80,
603 newTargetGroupId: 'ECS',
604 listener: ecs.ListenerConfig.applicationListener(listener, {
605 protocol: elbv2.ApplicationProtocol.HTTPS
606 }),
607 },
608);
609```
610
611### Using a Load Balancer from a different Stack
612
613If you want to put your Load Balancer and the Service it is load balancing to in
614different stacks, you may not be able to use the convenience methods
615`loadBalancer.addListener()` and `listener.addTargets()`.
616
617The reason is that these methods will create resources in the same Stack as the
618object they're called on, which may lead to cyclic references between stacks.
619Instead, you will have to create an `ApplicationListener` in the service stack,
620or an empty `TargetGroup` in the load balancer stack that you attach your
621service to.
622
623See the [ecs/cross-stack-load-balancer example](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/ecs/cross-stack-load-balancer/)
624for the alternatives.
625
626### Include a classic load balancer
627
628`Services` can also be directly attached to a classic load balancer as targets:
629
630```ts
631declare const cluster: ecs.Cluster;
632declare const taskDefinition: ecs.TaskDefinition;
633declare const vpc: ec2.Vpc;
634const service = new ecs.Ec2Service(this, 'Service', { cluster, taskDefinition });
635
636const lb = new elb.LoadBalancer(this, 'LB', { vpc });
637lb.addListener({ externalPort: 80 });
638lb.addTarget(service);
639```
640
641Similarly, if you want to have more control over load balancer targeting:
642
643```ts
644declare const cluster: ecs.Cluster;
645declare const taskDefinition: ecs.TaskDefinition;
646declare const vpc: ec2.Vpc;
647const service = new ecs.Ec2Service(this, 'Service', { cluster, taskDefinition });
648
649const lb = new elb.LoadBalancer(this, 'LB', { vpc });
650lb.addListener({ externalPort: 80 });
651lb.addTarget(service.loadBalancerTarget({
652 containerName: 'MyContainer',
653 containerPort: 80,
654}));
655```
656
657There are two higher-level constructs available which include a load balancer for you that can be found in the aws-ecs-patterns module:
658
659- `LoadBalancedFargateService`
660- `LoadBalancedEc2Service`
661
662## Task Auto-Scaling
663
664You can configure the task count of a service to match demand. Task auto-scaling is
665configured by calling `autoScaleTaskCount()`:
666
667```ts
668declare const target: elbv2.ApplicationTargetGroup;
669declare const service: ecs.BaseService;
670const scaling = service.autoScaleTaskCount({ maxCapacity: 10 });
671scaling.scaleOnCpuUtilization('CpuScaling', {
672 targetUtilizationPercent: 50,
673});
674
675scaling.scaleOnRequestCount('RequestScaling', {
676 requestsPerTarget: 10000,
677 targetGroup: target,
678});
679```
680
681Task auto-scaling is powered by *Application Auto-Scaling*.
682See that section for details.
683
684## Integration with CloudWatch Events
685
686To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an
687`@aws-cdk/aws-events-targets.EcsTask` instead of an `Ec2Service`:
688
689```ts
690declare const cluster: ecs.Cluster;
691// Create a Task Definition for the container to start
692const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
693taskDefinition.addContainer('TheContainer', {
694 image: ecs.ContainerImage.fromAsset(path.resolve(__dirname, '..', 'eventhandler-image')),
695 memoryLimitMiB: 256,
696 logging: new ecs.AwsLogDriver({ streamPrefix: 'EventDemo', mode: ecs.AwsLogDriverMode.NON_BLOCKING }),
697});
698
699// An Rule that describes the event trigger (in this case a scheduled run)
700const rule = new events.Rule(this, 'Rule', {
701 schedule: events.Schedule.expression('rate(1 min)'),
702});
703
704// Pass an environment variable to the container 'TheContainer' in the task
705rule.addTarget(new targets.EcsTask({
706 cluster,
707 taskDefinition,
708 taskCount: 1,
709 containerOverrides: [{
710 containerName: 'TheContainer',
711 environment: [{
712 name: 'I_WAS_TRIGGERED',
713 value: 'From CloudWatch Events'
714 }],
715 }],
716}));
717```
718
719## Log Drivers
720
721Currently Supported Log Drivers:
722
723- awslogs
724- fluentd
725- gelf
726- journald
727- json-file
728- splunk
729- syslog
730- awsfirelens
731- Generic
732
733### awslogs Log Driver
734
735```ts
736// Create a Task Definition for the container to start
737const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
738taskDefinition.addContainer('TheContainer', {
739 image: ecs.ContainerImage.fromRegistry('example-image'),
740 memoryLimitMiB: 256,
741 logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'EventDemo' }),
742});
743```
744
745### fluentd Log Driver
746
747```ts
748// Create a Task Definition for the container to start
749const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
750taskDefinition.addContainer('TheContainer', {
751 image: ecs.ContainerImage.fromRegistry('example-image'),
752 memoryLimitMiB: 256,
753 logging: ecs.LogDrivers.fluentd(),
754});
755```
756
757### gelf Log Driver
758
759```ts
760// Create a Task Definition for the container to start
761const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
762taskDefinition.addContainer('TheContainer', {
763 image: ecs.ContainerImage.fromRegistry('example-image'),
764 memoryLimitMiB: 256,
765 logging: ecs.LogDrivers.gelf({ address: 'my-gelf-address' }),
766});
767```
768
769### journald Log Driver
770
771```ts
772// Create a Task Definition for the container to start
773const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
774taskDefinition.addContainer('TheContainer', {
775 image: ecs.ContainerImage.fromRegistry('example-image'),
776 memoryLimitMiB: 256,
777 logging: ecs.LogDrivers.journald(),
778});
779```
780
781### json-file Log Driver
782
783```ts
784// Create a Task Definition for the container to start
785const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
786taskDefinition.addContainer('TheContainer', {
787 image: ecs.ContainerImage.fromRegistry('example-image'),
788 memoryLimitMiB: 256,
789 logging: ecs.LogDrivers.jsonFile(),
790});
791```
792
793### splunk Log Driver
794
795```ts
796// Create a Task Definition for the container to start
797const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
798taskDefinition.addContainer('TheContainer', {
799 image: ecs.ContainerImage.fromRegistry('example-image'),
800 memoryLimitMiB: 256,
801 logging: ecs.LogDrivers.splunk({
802 token: SecretValue.secretsManager('my-splunk-token'),
803 url: 'my-splunk-url',
804 }),
805});
806```
807
808### syslog Log Driver
809
810```ts
811// Create a Task Definition for the container to start
812const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
813taskDefinition.addContainer('TheContainer', {
814 image: ecs.ContainerImage.fromRegistry('example-image'),
815 memoryLimitMiB: 256,
816 logging: ecs.LogDrivers.syslog(),
817});
818```
819
820### firelens Log Driver
821
822```ts
823// Create a Task Definition for the container to start
824const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
825taskDefinition.addContainer('TheContainer', {
826 image: ecs.ContainerImage.fromRegistry('example-image'),
827 memoryLimitMiB: 256,
828 logging: ecs.LogDrivers.firelens({
829 options: {
830 Name: 'firehose',
831 region: 'us-west-2',
832 delivery_stream: 'my-stream',
833 },
834 }),
835});
836```
837
838To pass secrets to the log configuration, use the `secretOptions` property of the log configuration. The task execution role is automatically granted read permissions on the secrets/parameters.
839
840```ts
841declare const secret: secretsmanager.Secret;
842declare const parameter: ssm.StringParameter;
843
844const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
845taskDefinition.addContainer('TheContainer', {
846 image: ecs.ContainerImage.fromRegistry('example-image'),
847 memoryLimitMiB: 256,
848 logging: ecs.LogDrivers.firelens({
849 options: {
850 // ... log driver options here ...
851 },
852 secretOptions: { // Retrieved from AWS Secrets Manager or AWS Systems Manager Parameter Store
853 apikey: ecs.Secret.fromSecretsManager(secret),
854 host: ecs.Secret.fromSsmParameter(parameter),
855 },
856 }),
857});
858```
859
860### Generic Log Driver
861
862A generic log driver object exists to provide a lower level abstraction of the log driver configuration.
863
864```ts
865// Create a Task Definition for the container to start
866const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
867taskDefinition.addContainer('TheContainer', {
868 image: ecs.ContainerImage.fromRegistry('example-image'),
869 memoryLimitMiB: 256,
870 logging: new ecs.GenericLogDriver({
871 logDriver: 'fluentd',
872 options: {
873 tag: 'example-tag',
874 },
875 }),
876});
877```
878
879## CloudMap Service Discovery
880
881To register your ECS service with a CloudMap Service Registry, you may add the
882`cloudMapOptions` property to your service:
883
884```ts
885declare const taskDefinition: ecs.TaskDefinition;
886declare const cluster: ecs.Cluster;
887
888const service = new ecs.Ec2Service(this, 'Service', {
889 cluster,
890 taskDefinition,
891 cloudMapOptions: {
892 // Create A records - useful for AWSVPC network mode.
893 dnsRecordType: cloudmap.DnsRecordType.A,
894 },
895});
896```
897
898With `bridge` or `host` network modes, only `SRV` DNS record types are supported.
899By default, `SRV` DNS record types will target the default container and default
900port. However, you may target a different container and port on the same ECS task:
901
902```ts
903declare const taskDefinition: ecs.TaskDefinition;
904declare const cluster: ecs.Cluster;
905
906// Add a container to the task definition
907const specificContainer = taskDefinition.addContainer('Container', {
908 image: ecs.ContainerImage.fromRegistry('/aws/aws-example-app'),
909 memoryLimitMiB: 2048,
910});
911
912// Add a port mapping
913specificContainer.addPortMappings({
914 containerPort: 7600,
915 protocol: ecs.Protocol.TCP,
916});
917
918new ecs.Ec2Service(this, 'Service', {
919 cluster,
920 taskDefinition,
921 cloudMapOptions: {
922 // Create SRV records - useful for bridge networking
923 dnsRecordType: cloudmap.DnsRecordType.SRV,
924 // Targets port TCP port 7600 `specificContainer`
925 container: specificContainer,
926 containerPort: 7600,
927 },
928});
929```
930
931### Associate With a Specific CloudMap Service
932
933You may associate an ECS service with a specific CloudMap service. To do
934this, use the service's `associateCloudMapService` method:
935
936```ts
937declare const cloudMapService: cloudmap.Service;
938declare const ecsService: ecs.FargateService;
939
940ecsService.associateCloudMapService({
941 service: cloudMapService,
942});
943```
944
945## Capacity Providers
946
947There are two major families of Capacity Providers: [AWS
948Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html)
949(including Fargate Spot) and EC2 [Auto Scaling
950Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html)
951Capacity Providers. Both are supported.
952
953### Fargate Capacity Providers
954
955To enable Fargate capacity providers, you can either set
956`enableFargateCapacityProviders` to `true` when creating your cluster, or by
957invoking the `enableFargateCapacityProviders()` method after creating your
958cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity
959providers on your cluster.
960
961```ts
962declare const vpc: ec2.Vpc;
963
964const cluster = new ecs.Cluster(this, 'FargateCPCluster', {
965 vpc,
966 enableFargateCapacityProviders: true,
967});
968
969const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef');
970
971taskDefinition.addContainer('web', {
972 image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
973});
974
975new ecs.FargateService(this, 'FargateService', {
976 cluster,
977 taskDefinition,
978 capacityProviderStrategies: [
979 {
980 capacityProvider: 'FARGATE_SPOT',
981 weight: 2,
982 },
983 {
984 capacityProvider: 'FARGATE',
985 weight: 1,
986 },
987 ],
988});
989```
990
991### Auto Scaling Group Capacity Providers
992
993To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling
994Group. Then, create an `AsgCapacityProvider` and pass the Auto Scaling Group to
995it in the constructor. Then add the Capacity Provider to the cluster. Finally,
996you can refer to the Provider by its name in your service's or task's Capacity
997Provider strategy.
998
999By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling
1000Group's size for you. It will also enable managed termination protection, in
1001order to prevent EC2 Auto Scaling from terminating EC2 instances that have tasks
1002running on them. If you want to disable this behavior, set both
1003`enableManagedScaling` to and `enableManagedTerminationProtection` to `false`.
1004
1005```ts
1006declare const vpc: ec2.Vpc;
1007
1008const cluster = new ecs.Cluster(this, 'Cluster', {
1009 vpc,
1010});
1011
1012const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
1013 vpc,
1014 instanceType: new ec2.InstanceType('t2.micro'),
1015 machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
1016 minCapacity: 0,
1017 maxCapacity: 100,
1018});
1019
1020const capacityProvider = new ecs.AsgCapacityProvider(this, 'AsgCapacityProvider', {
1021 autoScalingGroup,
1022});
1023cluster.addAsgCapacityProvider(capacityProvider);
1024
1025const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
1026
1027taskDefinition.addContainer('web', {
1028 image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
1029 memoryReservationMiB: 256,
1030});
1031
1032new ecs.Ec2Service(this, 'EC2Service', {
1033 cluster,
1034 taskDefinition,
1035 capacityProviderStrategies: [
1036 {
1037 capacityProvider: capacityProvider.capacityProviderName,
1038 weight: 1,
1039 },
1040 ],
1041});
1042```
1043
1044## Elastic Inference Accelerators
1045
1046Currently, this feature is only supported for services with EC2 launch types.
1047
1048To add elastic inference accelerators to your EC2 instance, first add
1049`inferenceAccelerators` field to the Ec2TaskDefinition and set the `deviceName`
1050and `deviceType` properties.
1051
1052```ts
1053const inferenceAccelerators = [{
1054 deviceName: 'device1',
1055 deviceType: 'eia2.medium',
1056}];
1057
1058const taskDefinition = new ecs.Ec2TaskDefinition(this, 'Ec2TaskDef', {
1059 inferenceAccelerators,
1060});
1061```
1062
1063To enable using the inference accelerators in the containers, add `inferenceAcceleratorResources`
1064field and set it to a list of device names used for the inference accelerators. Each value in the
1065list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition.
1066
1067```ts
1068declare const taskDefinition: ecs.TaskDefinition;
1069const inferenceAcceleratorResources = ['device1'];
1070
1071taskDefinition.addContainer('cont', {
1072 image: ecs.ContainerImage.fromRegistry('test'),
1073 memoryLimitMiB: 1024,
1074 inferenceAcceleratorResources,
1075});
1076```
1077
1078## ECS Exec command
1079
1080Please note, ECS Exec leverages AWS Systems Manager (SSM). So as a prerequisite for the exec command
1081to work, you need to have the SSM plugin for the AWS CLI installed locally. For more information, see
1082[Install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html).
1083
1084To enable the ECS Exec feature for your containers, set the boolean flag `enableExecuteCommand` to `true` in
1085your `Ec2Service` or `FargateService`.
1086
1087```ts
1088declare const cluster: ecs.Cluster;
1089declare const taskDefinition: ecs.TaskDefinition;
1090
1091const service = new ecs.Ec2Service(this, 'Service', {
1092 cluster,
1093 taskDefinition,
1094 enableExecuteCommand: true,
1095});
1096```
1097
1098### Enabling logging
1099
1100You can enable sending logs of your execute session commands to a CloudWatch log group or S3 bucket by configuring
1101the `executeCommandConfiguration` property for your cluster. The default configuration will send the
1102logs to the CloudWatch Logs using the `awslogs` log driver that is configured in your task definition. Please note,
1103when using your own `logConfiguration` the log group or S3 Bucket specified must already be created.
1104
1105To encrypt data using your own KMS Customer Key (CMK), you must create a CMK and provide the key in the `kmsKey` field
1106of the `executeCommandConfiguration`. To use this key for encrypting CloudWatch log data or S3 bucket, make sure to associate the key
1107to these resources on creation.
1108
1109```ts
1110declare const vpc: ec2.Vpc;
1111const kmsKey = new kms.Key(this, 'KmsKey');
1112
1113// Pass the KMS key in the `encryptionKey` field to associate the key to the log group
1114const logGroup = new logs.LogGroup(this, 'LogGroup', {
1115 encryptionKey: kmsKey,
1116});
1117
1118// Pass the KMS key in the `encryptionKey` field to associate the key to the S3 bucket
1119const execBucket = new s3.Bucket(this, 'EcsExecBucket', {
1120 encryptionKey: kmsKey,
1121});
1122
1123const cluster = new ecs.Cluster(this, 'Cluster', {
1124 vpc,
1125 executeCommandConfiguration: {
1126 kmsKey,
1127 logConfiguration: {
1128 cloudWatchLogGroup: logGroup,
1129 cloudWatchEncryptionEnabled: true,
1130 s3Bucket: execBucket,
1131 s3EncryptionEnabled: true,
1132 s3KeyPrefix: 'exec-command-output',
1133 },
1134 logging: ecs.ExecuteCommandLogging.OVERRIDE,
1135 },
1136});
1137```