UNPKG

32.2 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
26import * as ecs from '@aws-cdk/aws-ecs';
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
92const cluster = new ecs.Cluster(this, 'Cluster', {
93 vpc: vpc
94});
95```
96
97To use tasks with Amazon EC2 launch-type, you have to add capacity to
98the cluster in order for tasks to be scheduled on your instances. Typically,
99you add an AutoScalingGroup with instances running the latest
100Amazon ECS-optimized AMI to the cluster. There is a method to build and add such an
101AutoScalingGroup automatically, or you can supply a customized AutoScalingGroup
102that you construct yourself. It's possible to add multiple AutoScalingGroups
103with various instance types.
104
105The following example creates an Amazon ECS cluster and adds capacity to it:
106
107```ts
108const cluster = new ecs.Cluster(this, 'Cluster', {
109 vpc: vpc
110});
111
112// Either add default capacity
113cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
114 instanceType: new ec2.InstanceType("t2.xlarge"),
115 desiredCapacity: 3,
116});
117
118// Or add customized capacity. Be sure to start the Amazon ECS-optimized AMI.
119const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
120 vpc,
121 instanceType: new ec2.InstanceType('t2.xlarge'),
122 machineImage: EcsOptimizedImage.amazonLinux(),
123 // Or use Amazon ECS-Optimized Amazon Linux 2 AMI
124 // machineImage: EcsOptimizedImage.amazonLinux2(),
125 desiredCapacity: 3,
126 // ... other options here ...
127});
128
129cluster.addAutoScalingGroup(autoScalingGroup);
130```
131
132If you omit the property `vpc`, the construct will create a new VPC with two AZs.
133
134
135### Bottlerocket
136
137[Bottlerocket](https://aws.amazon.com/bottlerocket/) is a Linux-based open source operating system that is
138purpose-built by AWS for running containers. You can launch Amazon ECS container instances with the Bottlerocket AMI.
139
140The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI.
141
142The following example adds Bottlerocket capacity to the cluster:
143
144```ts
145cluster.addCapacity('bottlerocket-asg', {
146 minCapacity: 2,
147 instanceType: new ec2.InstanceType('c5.large'),
148 machineImage: new ecs.BottleRocketImage(),
149});
150```
151
152### ARM64 (Graviton) Instances
153
154To launch instances with ARM64 hardware, you can use the Amazon ECS-optimized
155Amazon Linux 2 (arm64) AMI. Based on Amazon Linux 2, this AMI is recommended
156for use when launching your EC2 instances that are powered by Arm-based AWS
157Graviton Processors.
158
159```ts
160cluster.addCapacity('graviton-cluster', {
161 minCapacity: 2,
162 instanceType: new ec2.InstanceType('c6g.large'),
163 machineImage: ecs.EcsOptimizedImage.amazonLinux2(ecs.AmiHardwareType.ARM),
164});
165```
166
167Bottlerocket is also supported:
168
169```ts
170cluster.addCapacity('graviton-cluster', {
171 minCapacity: 2,
172 instanceType: new ec2.InstanceType('c6g.large'),
173 machineImage: ecs.MachineImageType.BOTTLEROCKET,
174});
175```
176
177### Spot Instances
178
179To add spot instances into the cluster, you must specify the `spotPrice` in the `ecs.AddCapacityOptions` and optionally enable the `spotInstanceDraining` property.
180
181```ts
182// Add an AutoScalingGroup with spot instances to the existing cluster
183cluster.addCapacity('AsgSpot', {
184 maxCapacity: 2,
185 minCapacity: 2,
186 desiredCapacity: 2,
187 instanceType: new ec2.InstanceType('c5.xlarge'),
188 spotPrice: '0.0735',
189 // Enable the Automated Spot Draining support for Amazon ECS
190 spotInstanceDraining: true,
191});
192```
193
194### SNS Topic Encryption
195
196When 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
197cluster's instances have been properly drained of tasks before terminating. The SNS Topic is sent the instance-terminating lifecycle event from the AutoScalingGroup,
198and 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
199then you may do so by providing a KMS key for the `topicEncryptionKey` property of `ecs.AddCapacityOptions`.
200
201```ts
202// Given
203const key = kms.Key(...);
204// Then, use that key to encrypt the lifecycle-event SNS Topic.
205cluster.addCapacity('ASGEncryptedSNS', {
206 instanceType: new ec2.InstanceType("t2.xlarge"),
207 desiredCapacity: 3,
208 topicEncryptionKey: key,
209});
210```
211
212## Task definitions
213
214A task definition describes what a single copy of a **task** should look like.
215A task definition has one or more containers; typically, it has one
216main container (the *default container* is the first one that's added
217to the task definition, and it is marked *essential*) and optionally
218some supporting containers which are used to support the main container,
219doings things like upload logs or metrics to monitoring services.
220
221To run a task or service with Amazon EC2 launch type, use the `Ec2TaskDefinition`. For AWS Fargate tasks/services, use the
222`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes
223provide simplified APIs that only contain properties relevant for each specific launch type.
224
225For a `FargateTaskDefinition`, specify the task size (`memoryLimitMiB` and `cpu`):
226
227```ts
228const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
229 memoryLimitMiB: 512,
230 cpu: 256
231});
232```
233
234To add containers to a task definition, call `addContainer()`:
235
236```ts
237const container = fargateTaskDefinition.addContainer("WebContainer", {
238 // Use an image from DockerHub
239 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
240 // ... other options here ...
241});
242```
243
244For a `Ec2TaskDefinition`:
245
246```ts
247const ec2TaskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef', {
248 networkMode: NetworkMode.BRIDGE
249});
250
251const container = ec2TaskDefinition.addContainer("WebContainer", {
252 // Use an image from DockerHub
253 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
254 memoryLimitMiB: 1024
255 // ... other options here ...
256});
257```
258
259For an `ExternalTaskDefinition`:
260
261```ts
262const externalTaskDefinition = new ecs.ExternalTaskDefinition(this, 'TaskDef');
263
264const container = externalTaskDefinition.addContainer("WebContainer", {
265 // Use an image from DockerHub
266 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
267 memoryLimitMiB: 1024
268 // ... other options here ...
269});
270```
271
272You can specify container properties when you add them to the task definition, or with various methods, e.g.:
273
274To add a port mapping when adding a container to the task definition, specify the `portMappings` option:
275
276```ts
277taskDefinition.addContainer("WebContainer", {
278 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
279 memoryLimitMiB: 1024,
280 portMappings: [{ containerPort: 3000 }]
281});
282```
283
284To add port mappings directly to a container definition, call `addPortMappings()`:
285
286```ts
287container.addPortMappings({
288 containerPort: 3000
289});
290```
291
292To add data volumes to a task definition, call `addVolume()`:
293
294```ts
295const volume = {
296 // Use an Elastic FileSystem
297 name: "mydatavolume",
298 efsVolumeConfiguration: ecs.EfsVolumeConfiguration({
299 fileSystemId: "EFS"
300 // ... other options here ...
301 })
302};
303
304const container = fargateTaskDefinition.addVolume("mydatavolume");
305```
306
307> Note: ECS Anywhere doesn't support volume attachments in the task definition.
308
309To use a TaskDefinition that can be used with either Amazon EC2 or
310AWS Fargate launch types, use the `TaskDefinition` construct.
311
312When creating a task definition you have to specify what kind of
313tasks you intend to run: Amazon EC2, AWS Fargate, or both.
314The following example uses both:
315
316```ts
317const taskDefinition = new ecs.TaskDefinition(this, 'TaskDef', {
318 memoryMiB: '512',
319 cpu: '256',
320 networkMode: NetworkMode.AWS_VPC,
321 compatibility: ecs.Compatibility.EC2_AND_FARGATE,
322});
323```
324
325### Images
326
327Images supply the software that runs inside the container. Images can be
328obtained from either DockerHub or from ECR repositories, built directly from a local Dockerfile, or use an existing tarball.
329
330- `ecs.ContainerImage.fromRegistry(imageName)`: use a public image.
331- `ecs.ContainerImage.fromRegistry(imageName, { credentials: mySecret })`: use a private image that requires credentials.
332- `ecs.ContainerImage.fromEcrRepository(repo, tag)`: use the given ECR repository as the image
333 to start. If no tag is provided, "latest" is assumed.
334- `ecs.ContainerImage.fromAsset('./image')`: build and upload an
335 image directly from a `Dockerfile` in your source directory.
336- `ecs.ContainerImage.fromDockerImageAsset(asset)`: uses an existing
337 `@aws-cdk/aws-ecr-assets.DockerImageAsset` as a container image.
338- `ecs.ContainerImage.fromTarball(file)`: use an existing tarball.
339- `new ecs.TagParameterContainerImage(repository)`: use the given ECR repository as the image
340 but a CloudFormation parameter as the tag.
341
342### Environment variables
343
344To pass environment variables to the container, you can use the `environment`, `environmentFiles`, and `secrets` props.
345
346```ts
347taskDefinition.addContainer('container', {
348 image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
349 memoryLimitMiB: 1024,
350 environment: { // clear text, not for sensitive data
351 STAGE: 'prod',
352 },
353 environmentFiles: [ // list of environment files hosted either on local disk or S3
354 ecs.EnvironmentFile.fromAsset('./demo-env-file.env'),
355 ecs.EnvironmentFile.fromBucket(s3Bucket, 'assets/demo-env-file.env'),
356 ],
357 secrets: { // Retrieved from AWS Secrets Manager or AWS Systems Manager Parameter Store at container start-up.
358 SECRET: ecs.Secret.fromSecretsManager(secret),
359 DB_PASSWORD: ecs.Secret.fromSecretsManager(dbSecret, 'password'), // Reference a specific JSON field, (requires platform version 1.4.0 or later for Fargate tasks)
360 PARAMETER: ecs.Secret.fromSsmParameter(parameter),
361 }
362});
363```
364
365The task execution role is automatically granted read permissions on the secrets/parameters. Support for environment
366files is restricted to the EC2 launch type for files hosted on S3. Further details provided in the AWS documentation
367about [specifying environment variables](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/taskdef-envfiles.html).
368
369## Service
370
371A `Service` instantiates a `TaskDefinition` on a `Cluster` a given number of
372times, optionally associating them with a load balancer.
373If a task fails,
374Amazon ECS automatically restarts the task.
375
376```ts
377const taskDefinition;
378
379const service = new ecs.FargateService(this, 'Service', {
380 cluster,
381 taskDefinition,
382 desiredCount: 5
383});
384```
385
386ECS Anywhere service definition looks like:
387
388```ts
389const taskDefinition;
390
391const service = new ecs.ExternalService(this, 'Service', {
392 cluster,
393 taskDefinition,
394 desiredCount: 5
395});
396```
397
398`Services` by default will create a security group if not provided.
399If you'd like to specify which security groups to use you can override the `securityGroups` property.
400
401### Deployment circuit breaker and rollback
402
403Amazon ECS [deployment circuit breaker](https://aws.amazon.com/tw/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/)
404automatically rolls back unhealthy service deployments without the need for manual intervention. Use `circuitBreaker` to enable
405deployment 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)
406for more details.
407
408```ts
409const service = new ecs.FargateService(stack, 'Service', {
410 cluster,
411 taskDefinition,
412 circuitBreaker: { rollback: true },
413});
414```
415
416> Note: ECS Anywhere doesn't support deployment circuit breakers and rollback.
417
418### Include an application/network load balancer
419
420`Services` are load balancing targets and can be added to a target group, which will be attached to an application/network load balancers:
421
422```ts
423import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
424
425const service = new ecs.FargateService(this, 'Service', { /* ... */ });
426
427const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
428const listener = lb.addListener('Listener', { port: 80 });
429const targetGroup1 = listener.addTargets('ECS1', {
430 port: 80,
431 targets: [service]
432});
433const targetGroup2 = listener.addTargets('ECS2', {
434 port: 80,
435 targets: [service.loadBalancerTarget({
436 containerName: 'MyContainer',
437 containerPort: 8080
438 })]
439});
440```
441
442> Note: ECS Anywhere doesn't support application/network load balancers.
443
444Note 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.
445
446Alternatively, 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.
447
448```ts
449import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2';
450
451const service = new ecs.FargateService(this, 'Service', { /* ... */ });
452
453const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
454const listener = lb.addListener('Listener', { port: 80 });
455service.registerLoadBalancerTargets(
456 {
457 containerName: 'web',
458 containerPort: 80,
459 newTargetGroupId: 'ECS',
460 listener: ecs.ListenerConfig.applicationListener(listener, {
461 protocol: elbv2.ApplicationProtocol.HTTPS
462 }),
463 },
464);
465```
466
467### Using a Load Balancer from a different Stack
468
469If you want to put your Load Balancer and the Service it is load balancing to in
470different stacks, you may not be able to use the convenience methods
471`loadBalancer.addListener()` and `listener.addTargets()`.
472
473The reason is that these methods will create resources in the same Stack as the
474object they're called on, which may lead to cyclic references between stacks.
475Instead, you will have to create an `ApplicationListener` in the service stack,
476or an empty `TargetGroup` in the load balancer stack that you attach your
477service to.
478
479See the [ecs/cross-stack-load-balancer example](https://github.com/aws-samples/aws-cdk-examples/tree/master/typescript/ecs/cross-stack-load-balancer/)
480for the alternatives.
481
482### Include a classic load balancer
483
484`Services` can also be directly attached to a classic load balancer as targets:
485
486```ts
487import * as elb from '@aws-cdk/aws-elasticloadbalancing';
488
489const service = new ecs.Ec2Service(this, 'Service', { /* ... */ });
490
491const lb = new elb.LoadBalancer(stack, 'LB', { vpc });
492lb.addListener({ externalPort: 80 });
493lb.addTarget(service);
494```
495
496Similarly, if you want to have more control over load balancer targeting:
497
498```ts
499import * as elb from '@aws-cdk/aws-elasticloadbalancing';
500
501const service = new ecs.Ec2Service(this, 'Service', { /* ... */ });
502
503const lb = new elb.LoadBalancer(stack, 'LB', { vpc });
504lb.addListener({ externalPort: 80 });
505lb.addTarget(service.loadBalancerTarget({
506 containerName: 'MyContainer',
507 containerPort: 80
508}));
509```
510
511There are two higher-level constructs available which include a load balancer for you that can be found in the aws-ecs-patterns module:
512
513- `LoadBalancedFargateService`
514- `LoadBalancedEc2Service`
515
516## Task Auto-Scaling
517
518You can configure the task count of a service to match demand. Task auto-scaling is
519configured by calling `autoScaleTaskCount()`:
520
521```ts
522const scaling = service.autoScaleTaskCount({ maxCapacity: 10 });
523scaling.scaleOnCpuUtilization('CpuScaling', {
524 targetUtilizationPercent: 50
525});
526
527scaling.scaleOnRequestCount('RequestScaling', {
528 requestsPerTarget: 10000,
529 targetGroup: target
530})
531```
532
533Task auto-scaling is powered by *Application Auto-Scaling*.
534See that section for details.
535
536## Integration with CloudWatch Events
537
538To start an Amazon ECS task on an Amazon EC2-backed Cluster, instantiate an
539`@aws-cdk/aws-events-targets.EcsTask` instead of an `Ec2Service`:
540
541```ts
542import * as targets from '@aws-cdk/aws-events-targets';
543
544// Create a Task Definition for the container to start
545const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
546taskDefinition.addContainer('TheContainer', {
547 image: ecs.ContainerImage.fromAsset(path.resolve(__dirname, '..', 'eventhandler-image')),
548 memoryLimitMiB: 256,
549 logging: new ecs.AwsLogDriver({ streamPrefix: 'EventDemo', mode: AwsLogDriverMode.NON_BLOCKING })
550});
551
552// An Rule that describes the event trigger (in this case a scheduled run)
553const rule = new events.Rule(this, 'Rule', {
554 schedule: events.Schedule.expression('rate(1 min)')
555});
556
557// Pass an environment variable to the container 'TheContainer' in the task
558rule.addTarget(new targets.EcsTask({
559 cluster,
560 taskDefinition,
561 taskCount: 1,
562 containerOverrides: [{
563 containerName: 'TheContainer',
564 environment: [{
565 name: 'I_WAS_TRIGGERED',
566 value: 'From CloudWatch Events'
567 }]
568 }]
569}));
570```
571
572## Log Drivers
573
574Currently Supported Log Drivers:
575
576- awslogs
577- fluentd
578- gelf
579- journald
580- json-file
581- splunk
582- syslog
583- awsfirelens
584
585### awslogs Log Driver
586
587```ts
588// Create a Task Definition for the container to start
589const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
590taskDefinition.addContainer('TheContainer', {
591 image: ecs.ContainerImage.fromRegistry('example-image'),
592 memoryLimitMiB: 256,
593 logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'EventDemo' })
594});
595```
596
597### fluentd Log Driver
598
599```ts
600// Create a Task Definition for the container to start
601const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
602taskDefinition.addContainer('TheContainer', {
603 image: ecs.ContainerImage.fromRegistry('example-image'),
604 memoryLimitMiB: 256,
605 logging: ecs.LogDrivers.fluentd()
606});
607```
608
609### gelf Log Driver
610
611```ts
612// Create a Task Definition for the container to start
613const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
614taskDefinition.addContainer('TheContainer', {
615 image: ecs.ContainerImage.fromRegistry('example-image'),
616 memoryLimitMiB: 256,
617 logging: ecs.LogDrivers.gelf({ address: 'my-gelf-address' })
618});
619```
620
621### journald Log Driver
622
623```ts
624// Create a Task Definition for the container to start
625const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
626taskDefinition.addContainer('TheContainer', {
627 image: ecs.ContainerImage.fromRegistry('example-image'),
628 memoryLimitMiB: 256,
629 logging: ecs.LogDrivers.journald()
630});
631```
632
633### json-file Log Driver
634
635```ts
636// Create a Task Definition for the container to start
637const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
638taskDefinition.addContainer('TheContainer', {
639 image: ecs.ContainerImage.fromRegistry('example-image'),
640 memoryLimitMiB: 256,
641 logging: ecs.LogDrivers.jsonFile()
642});
643```
644
645### splunk Log Driver
646
647```ts
648// Create a Task Definition for the container to start
649const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
650taskDefinition.addContainer('TheContainer', {
651 image: ecs.ContainerImage.fromRegistry('example-image'),
652 memoryLimitMiB: 256,
653 logging: ecs.LogDrivers.splunk({
654 secretToken: cdk.SecretValue.secretsManager('my-splunk-token'),
655 url: 'my-splunk-url'
656 })
657});
658```
659
660### syslog Log Driver
661
662```ts
663// Create a Task Definition for the container to start
664const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
665taskDefinition.addContainer('TheContainer', {
666 image: ecs.ContainerImage.fromRegistry('example-image'),
667 memoryLimitMiB: 256,
668 logging: ecs.LogDrivers.syslog()
669});
670```
671
672### firelens Log Driver
673
674```ts
675// Create a Task Definition for the container to start
676const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
677taskDefinition.addContainer('TheContainer', {
678 image: ecs.ContainerImage.fromRegistry('example-image'),
679 memoryLimitMiB: 256,
680 logging: ecs.LogDrivers.firelens({
681 options: {
682 Name: 'firehose',
683 region: 'us-west-2',
684 delivery_stream: 'my-stream',
685 }
686 })
687});
688```
689
690To 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.
691
692```ts
693const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
694taskDefinition.addContainer('TheContainer', {
695 image: ecs.ContainerImage.fromRegistry('example-image'),
696 memoryLimitMiB: 256,
697 logging: ecs.LogDrivers.firelens({
698 options: {
699 // ... log driver options here ...
700 },
701 secretOptions: { // Retrieved from AWS Secrets Manager or AWS Systems Manager Parameter Store
702 apikey: ecs.Secret.fromSecretsManager(secret),
703 host: ecs.Secret.fromSsmParameter(parameter),
704 },
705 })
706});
707```
708
709### Generic Log Driver
710
711A generic log driver object exists to provide a lower level abstraction of the log driver configuration.
712
713```ts
714// Create a Task Definition for the container to start
715const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
716taskDefinition.addContainer('TheContainer', {
717 image: ecs.ContainerImage.fromRegistry('example-image'),
718 memoryLimitMiB: 256,
719 logging: new ecs.GenericLogDriver({
720 logDriver: 'fluentd',
721 options: {
722 tag: 'example-tag'
723 }
724 })
725});
726```
727
728## CloudMap Service Discovery
729
730To register your ECS service with a CloudMap Service Registry, you may add the
731`cloudMapOptions` property to your service:
732
733```ts
734const service = new ecs.Ec2Service(stack, 'Service', {
735 cluster,
736 taskDefinition,
737 cloudMapOptions: {
738 // Create A records - useful for AWSVPC network mode.
739 dnsRecordType: cloudmap.DnsRecordType.A,
740 },
741});
742```
743
744With `bridge` or `host` network modes, only `SRV` DNS record types are supported.
745By default, `SRV` DNS record types will target the default container and default
746port. However, you may target a different container and port on the same ECS task:
747
748```ts
749// Add a container to the task definition
750const specificContainer = taskDefinition.addContainer(...);
751
752// Add a port mapping
753specificContainer.addPortMappings({
754 containerPort: 7600,
755 protocol: ecs.Protocol.TCP,
756});
757
758new ecs.Ec2Service(stack, 'Service', {
759 cluster,
760 taskDefinition,
761 cloudMapOptions: {
762 // Create SRV records - useful for bridge networking
763 dnsRecordType: cloudmap.DnsRecordType.SRV,
764 // Targets port TCP port 7600 `specificContainer`
765 container: specificContainer,
766 containerPort: 7600,
767 },
768});
769```
770
771### Associate With a Specific CloudMap Service
772
773You may associate an ECS service with a specific CloudMap service. To do
774this, use the service's `associateCloudMapService` method:
775
776```ts
777const cloudMapService = new cloudmap.Service(...);
778const ecsService = new ecs.FargateService(...);
779
780ecsService.associateCloudMapService({
781 service: cloudMapService,
782});
783```
784
785## Capacity Providers
786
787There are two major families of Capacity Providers: [AWS
788Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-capacity-providers.html)
789(including Fargate Spot) and EC2 [Auto Scaling
790Group](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/asg-capacity-providers.html)
791Capacity Providers. Both are supported.
792
793### Fargate Capacity Providers
794
795To enable Fargate capacity providers, you can either set
796`enableFargateCapacityProviders` to `true` when creating your cluster, or by
797invoking the `enableFargateCapacityProviders()` method after creating your
798cluster. This will add both `FARGATE` and `FARGATE_SPOT` as available capacity
799providers on your cluster.
800
801```ts
802const cluster = new ecs.Cluster(stack, 'FargateCPCluster', {
803 vpc,
804 enableFargateCapacityProviders: true,
805});
806
807const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef');
808
809taskDefinition.addContainer('web', {
810 image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
811});
812
813new ecs.FargateService(stack, 'FargateService', {
814 cluster,
815 taskDefinition,
816 capacityProviderStrategies: [
817 {
818 capacityProvider: 'FARGATE_SPOT',
819 weight: 2,
820 },
821 {
822 capacityProvider: 'FARGATE',
823 weight: 1,
824 }
825 ],
826});
827```
828
829### Auto Scaling Group Capacity Providers
830
831To add an Auto Scaling Group Capacity Provider, first create an EC2 Auto Scaling
832Group. Then, create an `AsgCapacityProvider` and pass the Auto Scaling Group to
833it in the constructor. Then add the Capacity Provider to the cluster. Finally,
834you can refer to the Provider by its name in your service's or task's Capacity
835Provider strategy.
836
837By default, an Auto Scaling Group Capacity Provider will manage the Auto Scaling
838Group's size for you. It will also enable managed termination protection, in
839order to prevent EC2 Auto Scaling from terminating EC2 instances that have tasks
840running on them. If you want to disable this behavior, set both
841`enableManagedScaling` to and `enableManagedTerminationProtection` to `false`.
842
843```ts
844const cluster = new ecs.Cluster(stack, 'Cluster', {
845 vpc,
846});
847
848const autoScalingGroup = new autoscaling.AutoScalingGroup(stack, 'ASG', {
849 vpc,
850 instanceType: new ec2.InstanceType('t2.micro'),
851 machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
852 minCapacity: 0,
853 maxCapacity: 100,
854});
855
856const capacityProvider = new ecs.AsgCapacityProvider(stack, 'AsgCapacityProvider', {
857 autoScalingGroup,
858});
859cluster.addAsgCapacityProvider(capacityProvider);
860
861const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'TaskDef');
862
863taskDefinition.addContainer('web', {
864 image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
865 memoryReservationMiB: 256,
866});
867
868new ecs.Ec2Service(stack, 'EC2Service', {
869 cluster,
870 taskDefinition,
871 capacityProviderStrategies: [
872 {
873 capacityProvider: capacityProvider.capacityProviderName,
874 weight: 1,
875 }
876 ],
877});
878```
879
880## Elastic Inference Accelerators
881
882Currently, this feature is only supported for services with EC2 launch types.
883
884To add elastic inference accelerators to your EC2 instance, first add
885`inferenceAccelerators` field to the Ec2TaskDefinition and set the `deviceName`
886and `deviceType` properties.
887
888```ts
889const inferenceAccelerators = [{
890 deviceName: 'device1',
891 deviceType: 'eia2.medium',
892}];
893
894const taskDefinition = new ecs.Ec2TaskDefinition(stack, 'Ec2TaskDef', {
895 inferenceAccelerators,
896});
897```
898
899To enable using the inference accelerators in the containers, add `inferenceAcceleratorResources`
900field and set it to a list of device names used for the inference accelerators. Each value in the
901list should match a `DeviceName` for an `InferenceAccelerator` specified in the task definition.
902
903```ts
904const inferenceAcceleratorResources = ['device1'];
905
906taskDefinition.addContainer('cont', {
907 image: ecs.ContainerImage.fromRegistry('test'),
908 memoryLimitMiB: 1024,
909 inferenceAcceleratorResources,
910});
911```
912
913## ECS Exec command
914
915Please note, ECS Exec leverages AWS Systems Manager (SSM). So as a prerequisite for the exec command
916to work, you need to have the SSM plugin for the AWS CLI installed locally. For more information, see
917[Install Session Manager plugin for AWS CLI](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html).
918
919To enable the ECS Exec feature for your containers, set the boolean flag `enableExecuteCommand` to `true` in
920your `Ec2Service` or `FargateService`.
921
922```ts
923const service = new ecs.Ec2Service(stack, 'Service', {
924 cluster,
925 taskDefinition,
926 enableExecuteCommand: true,
927});
928```
929
930### Enabling logging
931
932You can enable sending logs of your execute session commands to a CloudWatch log group or S3 bucket by configuring
933the `executeCommandConfiguration` property for your cluster. The default configuration will send the
934logs to the CloudWatch Logs using the `awslogs` log driver that is configured in your task definition. Please note,
935when using your own `logConfiguration` the log group or S3 Bucket specified must already be created.
936
937To encrypt data using your own KMS Customer Key (CMK), you must create a CMK and provide the key in the `kmsKey` field
938of the `executeCommandConfiguration`. To use this key for encrypting CloudWatch log data or S3 bucket, make sure to associate the key
939to these resources on creation.
940
941```ts
942const kmsKey = new kms.Key(stack, 'KmsKey');
943
944// Pass the KMS key in the `encryptionKey` field to associate the key to the log group
945const logGroup = new logs.LogGroup(stack, 'LogGroup', {
946 encryptionKey: kmsKey,
947});
948
949// Pass the KMS key in the `encryptionKey` field to associate the key to the S3 bucket
950const execBucket = new s3.Bucket(stack, 'EcsExecBucket', {
951 encryptionKey: kmsKey,
952});
953
954const cluster = new ecs.Cluster(stack, 'Cluster', {
955 vpc,
956 executeCommandConfiguration: {
957 kmsKey,
958 logConfiguration: {
959 cloudWatchLogGroup: logGroup,
960 cloudWatchEncryptionEnabled: true,
961 s3Bucket: execBucket,
962 s3EncryptionEnabled: true,
963 s3KeyPrefix: 'exec-command-output',
964 },
965 logging: ecs.ExecuteCommandLogging.OVERRIDE,
966 },
967});
968```