UNPKG

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