-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathREADME.md
1868 lines (1491 loc) · 64.2 KB
/
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Amazon ECS Construct Library
This package contains constructs for working with **Amazon Elastic Container
Service** (Amazon ECS).
Amazon Elastic Container Service (Amazon ECS) is a fully managed container orchestration service.
For further information on Amazon ECS,
see the [Amazon ECS documentation](https://docs.aws.amazon.com/ecs)
The following example creates an Amazon ECS cluster, adds capacity to it, and
runs a service on it:
```ts
declare const vpc: ec2.Vpc;
// Create an ECS cluster
const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
// Add capacity to it
cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
instanceType: new ec2.InstanceType("t2.xlarge"),
desiredCapacity: 3,
});
const taskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef');
taskDefinition.addContainer('DefaultContainer', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 512,
});
// Instantiate an Amazon ECS Service
const ecsService = new ecs.Ec2Service(this, 'Service', {
cluster,
taskDefinition,
});
```
For a set of constructs defining common ECS architectural patterns, see the `aws-cdk-lib/aws-ecs-patterns` package.
## Launch Types: AWS Fargate vs Amazon EC2 vs AWS ECS Anywhere
There are three sets of constructs in this library:
- Use the `Ec2TaskDefinition` and `Ec2Service` constructs to run tasks on Amazon EC2 instances running in your account.
- Use the `FargateTaskDefinition` and `FargateService` constructs to run tasks on
instances that are managed for you by AWS.
- Use the `ExternalTaskDefinition` and `ExternalService` constructs to run AWS ECS Anywhere tasks on self-managed infrastructure.
Here are the main differences:
- **Amazon EC2**: instances are under your control. Complete control of task to host
allocation. Required to specify at least a memory reservation or limit for
every container. Can use Host, Bridge and AwsVpc networking modes. Can attach
Classic Load Balancer. Can share volumes between container and host.
- **AWS Fargate**: tasks run on AWS-managed instances, AWS manages task to host
allocation for you. Requires specification of memory and cpu sizes at the
taskdefinition level. Only supports AwsVpc networking modes and
Application/Network Load Balancers. Only the AWS log driver is supported.
Many host features are not supported such as adding kernel capabilities
and mounting host devices/volumes inside the container.
- **AWS ECS Anywhere**: tasks are run and managed by AWS ECS Anywhere on infrastructure
owned by the customer. Bridge, Host and None networking modes are supported. Does not
support autoscaling, load balancing, cloudmap or attachment of volumes.
For more information on Amazon EC2 vs AWS Fargate, networking and ECS Anywhere see the AWS Documentation:
[AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html),
[Task Networking](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-networking.html),
[ECS Anywhere](https://aws.amazon.com/ecs/anywhere/)
## Clusters
A `Cluster` defines the infrastructure to run your
tasks on. You can run many tasks on a single cluster.
The following code creates a cluster that can run AWS Fargate tasks:
```ts
declare const vpc: ec2.Vpc;
const cluster = new ecs.Cluster(this, 'Cluster', {
vpc,
});
```
The following code imports an existing cluster using the ARN which can be used to
import an Amazon ECS service either EC2 or Fargate.
```ts
const clusterArn = 'arn:aws:ecs:us-east-1:012345678910:cluster/clusterName';
const cluster = ecs.Cluster.fromClusterArn(this, 'Cluster', clusterArn);
```
To use tasks with Amazon EC2 launch-type, you have to add capacity to
the cluster in order for tasks to be scheduled on your instances. Typically,
you add an AutoScalingGroup with instances running the latest
Amazon ECS-optimized AMI to the cluster. There is a method to build and add such an
AutoScalingGroup automatically, or you can supply a customized AutoScalingGroup
that you construct yourself. It's possible to add multiple AutoScalingGroups
with various instance types.
The following example creates an Amazon ECS cluster and adds capacity to it:
```ts
declare const vpc: ec2.Vpc;
const cluster = new ecs.Cluster(this, 'Cluster', {
vpc,
});
// Either add default capacity
cluster.addCapacity('DefaultAutoScalingGroupCapacity', {
instanceType: new ec2.InstanceType("t2.xlarge"),
desiredCapacity: 3,
});
// Or add customized capacity. Be sure to start the Amazon ECS-optimized AMI.
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
vpc,
instanceType: new ec2.InstanceType('t2.xlarge'),
machineImage: ecs.EcsOptimizedImage.amazonLinux(),
// Or use Amazon ECS-Optimized Amazon Linux 2 AMI
// machineImage: EcsOptimizedImage.amazonLinux2(),
desiredCapacity: 3,
// ... other options here ...
});
const capacityProvider = new ecs.AsgCapacityProvider(this, 'AsgCapacityProvider', {
autoScalingGroup,
});
cluster.addAsgCapacityProvider(capacityProvider);
```
If you omit the property `vpc`, the construct will create a new VPC with two AZs.
By default, all machine images will auto-update to the latest version
on each deployment, causing a replacement of the instances in your AutoScalingGroup
if the AMI has been updated since the last deployment.
If task draining is enabled, ECS will transparently reschedule tasks on to the new
instances before terminating your old instances. If you have disabled task draining,
the tasks will be terminated along with the instance. To prevent that, you
can pick a non-updating AMI by passing `cacheInContext: true`, but be sure
to periodically update to the latest AMI manually by using the [CDK CLI
context management commands](https://docs.aws.amazon.com/cdk/latest/guide/context.html):
```ts
declare const vpc: ec2.Vpc;
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
machineImage: ecs.EcsOptimizedImage.amazonLinux({ cachedInContext: true }),
vpc,
instanceType: new ec2.InstanceType('t2.micro'),
});
```
To use `LaunchTemplate` with `AsgCapacityProvider`, make sure to specify the `userData` in the `LaunchTemplate`:
```ts
declare const vpc: ec2.Vpc;
const launchTemplate = new ec2.LaunchTemplate(this, 'ASG-LaunchTemplate', {
instanceType: new ec2.InstanceType('t3.medium'),
machineImage: ecs.EcsOptimizedImage.amazonLinux2(),
userData: ec2.UserData.forLinux(),
});
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
vpc,
mixedInstancesPolicy: {
instancesDistribution: {
onDemandPercentageAboveBaseCapacity: 50,
},
launchTemplate: launchTemplate,
},
});
const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
const capacityProvider = new ecs.AsgCapacityProvider(this, 'AsgCapacityProvider', {
autoScalingGroup,
machineImageType: ecs.MachineImageType.AMAZON_LINUX_2,
});
cluster.addAsgCapacityProvider(capacityProvider);
```
The following code retrieve the Amazon Resource Names (ARNs) of tasks that are a part of a specified ECS cluster.
It's useful when you want to grant permissions to a task to access other AWS resources.
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const taskARNs = cluster.arnForTasks('*'); // arn:aws:ecs:<region>:<regionId>:task/<clusterName>/*
// Grant the task permission to access other AWS resources
taskDefinition.addToTaskRolePolicy(
new iam.PolicyStatement({
actions: ['ecs:UpdateTaskProtection'],
resources: [taskARNs],
})
)
```
To manage task protection settings in an ECS cluster, you can use the `grantTaskProtection` method.
This method grants the `ecs:UpdateTaskProtection` permission to a specified IAM entity.
```ts
// Assume 'cluster' is an instance of ecs.Cluster
declare const cluster: ecs.Cluster;
declare const taskRole: iam.Role;
// Grant ECS Task Protection permissions to the role
// Now 'taskRole' has the 'ecs:UpdateTaskProtection' permission on all tasks in the cluster
cluster.grantTaskProtection(taskRole);
```
### Bottlerocket
[Bottlerocket](https://aws.amazon.com/bottlerocket/) is a Linux-based open source operating system that is
purpose-built by AWS for running containers. You can launch Amazon ECS container instances with the Bottlerocket AMI.
The following example will create a capacity with self-managed Amazon EC2 capacity of 2 `c5.large` Linux instances running with `Bottlerocket` AMI.
The following example adds Bottlerocket capacity to the cluster:
```ts
declare const cluster: ecs.Cluster;
cluster.addCapacity('bottlerocket-asg', {
minCapacity: 2,
instanceType: new ec2.InstanceType('c5.large'),
machineImage: new ecs.BottleRocketImage(),
});
```
You can also specify an NVIDIA-compatible AMI such as in this example:
```ts
declare const cluster: ecs.Cluster;
cluster.addCapacity('bottlerocket-asg', {
instanceType: new ec2.InstanceType('p3.2xlarge'),
machineImage: new ecs.BottleRocketImage({
variant: ecs.BottlerocketEcsVariant.AWS_ECS_2_NVIDIA,
}),
});
```
### ARM64 (Graviton) Instances
To launch instances with ARM64 hardware, you can use the Amazon ECS-optimized
Amazon Linux 2 (arm64) AMI. Based on Amazon Linux 2, this AMI is recommended
for use when launching your EC2 instances that are powered by Arm-based AWS
Graviton Processors.
```ts
declare const cluster: ecs.Cluster;
cluster.addCapacity('graviton-cluster', {
minCapacity: 2,
instanceType: new ec2.InstanceType('c6g.large'),
machineImage: ecs.EcsOptimizedImage.amazonLinux2(ecs.AmiHardwareType.ARM),
});
```
Bottlerocket is also supported:
```ts
declare const cluster: ecs.Cluster;
cluster.addCapacity('graviton-cluster', {
minCapacity: 2,
instanceType: new ec2.InstanceType('c6g.large'),
machineImageType: ecs.MachineImageType.BOTTLEROCKET,
});
```
### Amazon Linux 2 (Neuron) Instances
To launch Amazon EC2 Inf1, Trn1 or Inf2 instances, you can use the Amazon ECS optimized Amazon Linux 2 (Neuron) AMI. It comes pre-configured with AWS Inferentia and AWS Trainium drivers and the AWS Neuron runtime for Docker which makes running machine learning inference workloads easier on Amazon ECS.
```ts
declare const cluster: ecs.Cluster;
cluster.addCapacity('neuron-cluster', {
minCapacity: 2,
instanceType: new ec2.InstanceType('inf1.xlarge'),
machineImage: ecs.EcsOptimizedImage.amazonLinux2(ecs.AmiHardwareType.NEURON),
});
```
### Spot Instances
To add spot instances into the cluster, you must specify the `spotPrice` in the `ecs.AddCapacityOptions` and optionally enable the `spotInstanceDraining` property.
```ts
declare const cluster: ecs.Cluster;
// Add an AutoScalingGroup with spot instances to the existing cluster
cluster.addCapacity('AsgSpot', {
maxCapacity: 2,
minCapacity: 2,
desiredCapacity: 2,
instanceType: new ec2.InstanceType('c5.xlarge'),
spotPrice: '0.0735',
// Enable the Automated Spot Draining support for Amazon ECS
spotInstanceDraining: true,
});
```
### SNS Topic Encryption
When 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
cluster's instances have been properly drained of tasks before terminating. The SNS Topic is sent the instance-terminating lifecycle event from the AutoScalingGroup,
and 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
then you may do so by providing a KMS key for the `topicEncryptionKey` property of `ecs.AddCapacityOptions`.
```ts
// Given
declare const cluster: ecs.Cluster;
declare const key: kms.Key;
// Then, use that key to encrypt the lifecycle-event SNS Topic.
cluster.addCapacity('ASGEncryptedSNS', {
instanceType: new ec2.InstanceType("t2.xlarge"),
desiredCapacity: 3,
topicEncryptionKey: key,
});
```
## Task definitions
A task definition describes what a single copy of a **task** should look like.
A task definition has one or more containers; typically, it has one
main container (the *default container* is the first one that's added
to the task definition, and it is marked *essential*) and optionally
some supporting containers which are used to support the main container,
doings things like upload logs or metrics to monitoring services.
To run a task or service with Amazon EC2 launch type, use the `Ec2TaskDefinition`. For AWS Fargate tasks/services, use the
`FargateTaskDefinition`. For AWS ECS Anywhere use the `ExternalTaskDefinition`. These classes
provide simplified APIs that only contain properties relevant for each specific launch type.
For a `FargateTaskDefinition`, specify the task size (`memoryLimitMiB` and `cpu`):
```ts
const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
memoryLimitMiB: 512,
cpu: 256,
});
```
On Fargate Platform Version 1.4.0 or later, you may specify up to 200GiB of
[ephemeral storage](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/fargate-task-storage.html#fargate-task-storage-pv14):
```ts
const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
memoryLimitMiB: 512,
cpu: 256,
ephemeralStorageGiB: 100,
});
```
To specify the process namespace to use for the containers in the task, use the `pidMode` property:
```ts
const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: ecs.CpuArchitecture.ARM64,
},
memoryLimitMiB: 512,
cpu: 256,
pidMode: ecs.PidMode.TASK,
});
```
**Note:** `pidMode` is only supported for tasks that are hosted on AWS Fargate if the tasks are using platform version 1.4.0
or later (Linux). Only the `task` option is supported for Linux containers. `pidMode` isn't supported for Windows containers on Fargate.
If `pidMode` is specified for a Fargate task, then `runtimePlatform.operatingSystemFamily` must also be specified.
To add containers to a task definition, call `addContainer()`:
```ts
const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
memoryLimitMiB: 512,
cpu: 256,
});
const container = fargateTaskDefinition.addContainer("WebContainer", {
// Use an image from DockerHub
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
// ... other options here ...
});
```
For an `Ec2TaskDefinition`:
```ts
const ec2TaskDefinition = new ecs.Ec2TaskDefinition(this, 'TaskDef', {
networkMode: ecs.NetworkMode.BRIDGE,
});
const container = ec2TaskDefinition.addContainer("WebContainer", {
// Use an image from DockerHub
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
// ... other options here ...
});
```
For an `ExternalTaskDefinition`:
```ts
const externalTaskDefinition = new ecs.ExternalTaskDefinition(this, 'TaskDef');
const container = externalTaskDefinition.addContainer("WebContainer", {
// Use an image from DockerHub
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
// ... other options here ...
});
```
You can specify container properties when you add them to the task definition, or with various methods, e.g.:
To add a port mapping when adding a container to the task definition, specify the `portMappings` option:
```ts
declare const taskDefinition: ecs.TaskDefinition;
taskDefinition.addContainer("WebContainer", {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
portMappings: [{ containerPort: 3000 }],
});
```
To add port mappings directly to a container definition, call `addPortMappings()`:
```ts
declare const container: ecs.ContainerDefinition;
container.addPortMappings({
containerPort: 3000,
});
```
Sometimes it is useful to be able to configure port ranges for a container, e.g. to run applications such as game servers
and real-time streaming which typically require multiple ports to be opened simultaneously. This feature is supported on
both Linux and Windows operating systems for both the EC2 and AWS Fargate launch types. There is a maximum limit of 100
port ranges per container, and you cannot specify overlapping port ranges.
Docker recommends that you turn off the `docker-proxy` in the Docker daemon config file when you have a large number of ports.
For more information, see [Issue #11185](https://github.com/moby/moby/issues/11185) on the GitHub website.
```ts
declare const container: ecs.ContainerDefinition;
container.addPortMappings({
containerPort: ecs.ContainerDefinition.CONTAINER_PORT_USE_RANGE,
containerPortRange: '8080-8081',
});
```
To add data volumes to a task definition, call `addVolume()`:
```ts
const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
memoryLimitMiB: 512,
cpu: 256,
});
const volume = {
// Use an Elastic FileSystem
name: "mydatavolume",
efsVolumeConfiguration: {
fileSystemId: "EFS",
// ... other options here ...
},
};
const container = fargateTaskDefinition.addVolume(volume);
```
> Note: ECS Anywhere doesn't support volume attachments in the task definition.
To use a TaskDefinition that can be used with either Amazon EC2 or
AWS Fargate launch types, use the `TaskDefinition` construct.
When creating a task definition you have to specify what kind of
tasks you intend to run: Amazon EC2, AWS Fargate, or both.
The following example uses both:
```ts
const taskDefinition = new ecs.TaskDefinition(this, 'TaskDef', {
memoryMiB: '512',
cpu: '256',
networkMode: ecs.NetworkMode.AWS_VPC,
compatibility: ecs.Compatibility.EC2_AND_FARGATE,
});
```
To grant a principal permission to run your `TaskDefinition`, you can use the `TaskDefinition.grantRun()` method:
```ts
declare const role: iam.IGrantable;
const taskDef = new ecs.TaskDefinition(this, 'TaskDef', {
cpu: '512',
memoryMiB: '512',
compatibility: ecs.Compatibility.EC2_AND_FARGATE,
});
// Gives role required permissions to run taskDef
taskDef.grantRun(role);
```
To deploy containerized applications that require the allocation of standard input (stdin) or a terminal (tty), use the `interactive` property.
This parameter corresponds to `OpenStdin` in the [Create a container](https://docs.docker.com/engine/api/v1.35/#tag/Container/operation/ContainerCreate) section of the [Docker Remote API](https://docs.docker.com/engine/api/v1.35/)
and the `--interactive` option to [docker run](https://docs.docker.com/engine/reference/run/#security-configuration).
```ts
declare const taskDefinition: ecs.TaskDefinition;
taskDefinition.addContainer("Container", {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
interactive: true,
});
```
### Images
Images supply the software that runs inside the container. Images can be
obtained from either DockerHub or from ECR repositories, built directly from a local Dockerfile, or use an existing tarball.
- `ecs.ContainerImage.fromRegistry(imageName)`: use a public image.
- `ecs.ContainerImage.fromRegistry(imageName, { credentials: mySecret })`: use a private image that requires credentials.
- `ecs.ContainerImage.fromEcrRepository(repo, tagOrDigest)`: use the given ECR repository as the image
to start. If no tag or digest is provided, "latest" is assumed.
- `ecs.ContainerImage.fromAsset('./image')`: build and upload an
image directly from a `Dockerfile` in your source directory.
- `ecs.ContainerImage.fromDockerImageAsset(asset)`: uses an existing
`aws-cdk-lib/aws-ecr-assets.DockerImageAsset` as a container image.
- `ecs.ContainerImage.fromTarball(file)`: use an existing tarball.
- `new ecs.TagParameterContainerImage(repository)`: use the given ECR repository as the image
but a CloudFormation parameter as the tag.
### Environment variables
To pass environment variables to the container, you can use the `environment`, `environmentFiles`, and `secrets` props.
```ts
declare const secret: secretsmanager.Secret;
declare const dbSecret: secretsmanager.Secret;
declare const parameter: ssm.StringParameter;
declare const taskDefinition: ecs.TaskDefinition;
declare const s3Bucket: s3.Bucket;
const newContainer = taskDefinition.addContainer('container', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
environment: { // clear text, not for sensitive data
STAGE: 'prod',
},
environmentFiles: [ // list of environment files hosted either on local disk or S3
ecs.EnvironmentFile.fromAsset('./demo-env-file.env'),
ecs.EnvironmentFile.fromBucket(s3Bucket, 'assets/demo-env-file.env'),
],
secrets: { // Retrieved from AWS Secrets Manager or AWS Systems Manager Parameter Store at container start-up.
SECRET: ecs.Secret.fromSecretsManager(secret),
DB_PASSWORD: ecs.Secret.fromSecretsManager(dbSecret, 'password'), // Reference a specific JSON field, (requires platform version 1.4.0 or later for Fargate tasks)
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)
PARAMETER: ecs.Secret.fromSsmParameter(parameter),
},
});
newContainer.addEnvironment('QUEUE_NAME', 'MyQueue');
newContainer.addSecret('API_KEY', ecs.Secret.fromSecretsManager(secret));
newContainer.addSecret('DB_PASSWORD', ecs.Secret.fromSecretsManager(secret, 'password'));
```
The task execution role is automatically granted read permissions on the secrets/parameters. Further details provided in the AWS documentation
about [specifying environment variables](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/taskdef-envfiles.html).
### Linux parameters
To apply additional linux-specific options related to init process and memory management to the container, use the `linuxParameters` property:
```ts
declare const taskDefinition: ecs.TaskDefinition;
taskDefinition.addContainer('container', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
linuxParameters: new ecs.LinuxParameters(this, 'LinuxParameters', {
initProcessEnabled: true,
sharedMemorySize: 1024,
maxSwap: Size.mebibytes(5000),
swappiness: 90,
}),
});
```
### System controls
To set system controls (kernel parameters) on the container, use the `systemControls` prop:
```ts
declare const taskDefinition: ecs.TaskDefinition;
taskDefinition.addContainer('container', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
systemControls: [
{
namespace: 'net.ipv6.conf.all.default.disable_ipv6',
value: '1',
},
],
});
```
## Docker labels
You can add labels to the container with the `dockerLabels` property or with the `addDockerLabel` method:
```ts
declare const taskDefinition: ecs.TaskDefinition;
const container = taskDefinition.addContainer('cont', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
memoryLimitMiB: 1024,
dockerLabels: {
foo: 'bar',
},
});
container.addDockerLabel('label', 'value');
```
### Using Windows containers on Fargate
AWS 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/)
```ts
// Create a Task Definition for the Windows container to start
const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.WINDOWS_SERVER_2019_CORE,
cpuArchitecture: ecs.CpuArchitecture.X86_64,
},
cpu: 1024,
memoryLimitMiB: 2048,
});
taskDefinition.addContainer('windowsservercore', {
logging: ecs.LogDriver.awsLogs({ streamPrefix: 'win-iis-on-fargate' }),
portMappings: [{ containerPort: 80 }],
image: ecs.ContainerImage.fromRegistry('mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019'),
});
```
### Using Windows authentication with gMSA
Amazon ECS supports Active Directory authentication for Linux containers through a special kind of service account called a group Managed Service Account (gMSA). For more details, please see the [product documentation on how to implement on Windows containers](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/windows-gmsa.html), or this [blog post on how to implement on Linux containers](https://aws.amazon.com/blogs/containers/using-windows-authentication-with-gmsa-on-linux-containers-on-amazon-ecs/).
There are two types of CredentialSpecs, domained-join or domainless. Both types support creation from a S3 bucket, a SSM parameter, or by directly specifying a location for the file in the constructor.
A domian-joined gMSA container looks like:
```ts
// Make sure the task definition's execution role has permissions to read from the S3 bucket or SSM parameter where the CredSpec file is stored.
declare const parameter: ssm.IParameter;
declare const taskDefinition: ecs.TaskDefinition;
// Domain-joined gMSA container from a SSM parameter
taskDefinition.addContainer('gmsa-domain-joined-container', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
cpu: 128,
memoryLimitMiB: 256,
credentialSpecs: [ecs.DomainJoinedCredentialSpec.fromSsmParameter(parameter)],
});
```
A domianless gMSA container looks like:
```ts
// Make sure the task definition's execution role has permissions to read from the S3 bucket or SSM parameter where the CredSpec file is stored.
declare const bucket: s3.Bucket;
declare const taskDefinition: ecs.TaskDefinition;
// Domainless gMSA container from a S3 bucket object.
taskDefinition.addContainer('gmsa-domainless-container', {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
cpu: 128,
memoryLimitMiB: 256,
credentialSpecs: [ecs.DomainlessCredentialSpec.fromS3Bucket(bucket, 'credSpec')],
});
```
### Using Graviton2 with Fargate
AWS 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/)
```ts
// Create a Task Definition for running container on Graviton Runtime.
const taskDefinition = new ecs.FargateTaskDefinition(this, 'TaskDef', {
runtimePlatform: {
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: ecs.CpuArchitecture.ARM64,
},
cpu: 1024,
memoryLimitMiB: 2048,
});
taskDefinition.addContainer('webarm64', {
logging: ecs.LogDriver.awsLogs({ streamPrefix: 'graviton2-on-fargate' }),
portMappings: [{ containerPort: 80 }],
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/nginx/nginx:latest-arm64v8'),
});
```
## Service
A `Service` instantiates a `TaskDefinition` on a `Cluster` a given number of
times, optionally associating them with a load balancer.
If a task fails,
Amazon ECS automatically restarts the task.
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const service = new ecs.FargateService(this, 'Service', {
cluster,
taskDefinition,
desiredCount: 5,
});
```
ECS Anywhere service definition looks like:
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const service = new ecs.ExternalService(this, 'Service', {
cluster,
taskDefinition,
desiredCount: 5,
});
```
`Services` by default will create a security group if not provided.
If you'd like to specify which security groups to use you can override the `securityGroups` property.
By default, the service will use the revision of the passed task definition generated when the `TaskDefinition`
is deployed by CloudFormation. However, this may not be desired if the revision is externally managed,
for example through CodeDeploy.
To set a specific revision number or the special `latest` revision, use the `taskDefinitionRevision` parameter:
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
new ecs.ExternalService(this, 'Service', {
cluster,
taskDefinition,
desiredCount: 5,
taskDefinitionRevision: ecs.TaskDefinitionRevision.of(1)
});
new ecs.ExternalService(this, 'Service', {
cluster,
taskDefinition,
desiredCount: 5,
taskDefinitionRevision: ecs.TaskDefinitionRevision.LATEST
});
```
### Deployment circuit breaker and rollback
Amazon ECS [deployment circuit breaker](https://aws.amazon.com/tw/blogs/containers/announcing-amazon-ecs-deployment-circuit-breaker/)
automatically rolls back unhealthy service deployments, eliminating the need for manual intervention.
Use `circuitBreaker` to enable the deployment circuit breaker which determines whether a service deployment
will fail if the service can't reach a steady state.
You can optionally enable `rollback` for automatic rollback.
See [Using the deployment circuit breaker](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-type-ecs.html) for more details.
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const service = new ecs.FargateService(this, 'Service', {
cluster,
taskDefinition,
circuitBreaker: {
enable: true,
rollback: true
},
});
```
> Note: ECS Anywhere doesn't support deployment circuit breakers and rollback.
### Deployment alarms
Amazon ECS [deployment alarms]
(https://aws.amazon.com/blogs/containers/automate-rollbacks-for-amazon-ecs-rolling-deployments-with-cloudwatch-alarms/)
allow monitoring and automatically reacting to changes during a rolling update
by using Amazon CloudWatch metric alarms.
Amazon ECS starts monitoring the configured deployment alarms as soon as one or
more tasks of the updated service are in a running state. The deployment process
continues until the primary deployment is healthy and has reached the desired
count and the active deployment has been scaled down to 0. Then, the deployment
remains in the IN_PROGRESS state for an additional "bake time." The length the
bake time is calculated based on the evaluation periods and period of the alarms.
After the bake time, if none of the alarms have been activated, then Amazon ECS
considers this to be a successful update and deletes the active deployment and
changes the status of the primary deployment to COMPLETED.
```ts
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
declare const elbAlarm: cw.Alarm;
const service = new ecs.FargateService(this, 'Service', {
cluster,
taskDefinition,
deploymentAlarms: {
alarmNames: [elbAlarm.alarmName],
behavior: ecs.AlarmBehavior.ROLLBACK_ON_ALARM,
},
});
// Defining a deployment alarm after the service has been created
const cpuAlarmName = 'MyCpuMetricAlarm';
new cw.Alarm(this, 'CPUAlarm', {
alarmName: cpuAlarmName,
metric: service.metricCpuUtilization(),
evaluationPeriods: 2,
threshold: 80,
});
service.enableDeploymentAlarms([cpuAlarmName], {
behavior: ecs.AlarmBehavior.FAIL_ON_ALARM,
});
```
> Note: Deployment alarms are only available when `deploymentController` is set
> to `DeploymentControllerType.ECS`, which is the default.
#### Troubleshooting circular dependencies
I saw this info message during synth time. What do I do?
```text
Deployment alarm ({"Ref":"MyAlarmABC1234"}) enabled on MyEcsService may cause a
circular dependency error when this stack deploys. The alarm name references the
alarm's logical id, or another resource. See the 'Deployment alarms' section in
the module README for more details.
```
If your app deploys successfully with this message, you can disregard it. But it
indicates that you could encounter a circular dependency error when you try to
deploy. If you want to alarm on metrics produced by the service, there will be a
circular dependency between the service and its deployment alarms. In this case,
there are two options to avoid the circular dependency.
1. Define the physical name for the alarm. Use a defined physical name that is
unique within the deployment environment for the alarm name when creating the
alarm, and re-use the defined name. This name could be a hardcoded string, a
string generated based on the environment, or could reference another
resource that does not depend on the service.
2. Define the physical name for the service. Then, don't use
`metricCpuUtilization()` or similar methods. Create the metric object
separately by referencing the service metrics using this name.
Option 1, defining a physical name for the alarm:
```ts
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const service = new ecs.FargateService(this, 'Service', {
cluster,
taskDefinition,
});
const cpuAlarmName = 'MyCpuMetricAlarm';
const myAlarm = new cw.Alarm(this, 'CPUAlarm', {
alarmName: cpuAlarmName,
metric: service.metricCpuUtilization(),
evaluationPeriods: 2,
threshold: 80,
});
// Using `myAlarm.alarmName` here will cause a circular dependency
service.enableDeploymentAlarms([cpuAlarmName], {
behavior: ecs.AlarmBehavior.FAIL_ON_ALARM,
});
```
Option 2, defining a physical name for the service:
```ts
import * as cw from 'aws-cdk-lib/aws-cloudwatch';
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const serviceName = 'MyFargateService';
const service = new ecs.FargateService(this, 'Service', {
serviceName,
cluster,
taskDefinition,
});
const cpuMetric = new cw.Metric({
metricName: 'CPUUtilization',
namespace: 'AWS/ECS',
period: Duration.minutes(5),
statistic: 'Average',
dimensionsMap: {
ClusterName: cluster.clusterName,
// Using `service.serviceName` here will cause a circular dependency
ServiceName: serviceName,
},
});
const myAlarm = new cw.Alarm(this, 'CPUAlarm', {
alarmName: 'cpuAlarmName',
metric: cpuMetric,
evaluationPeriods: 2,
threshold: 80,
});
service.enableDeploymentAlarms([myAlarm.alarmName], {
behavior: ecs.AlarmBehavior.FAIL_ON_ALARM,
});
```
This issue only applies if the metrics to alarm on are emitted by the service
itself. If the metrics are emitted by a different resource, that does not depend
on the service, there will be no restrictions on the alarm name.
### Include an application/network load balancer
`Services` are load balancing targets and can be added to a target group, which will be attached to an application/network load balancers:
```ts
declare const vpc: ec2.Vpc;
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
const service = new ecs.FargateService(this, 'Service', { cluster, taskDefinition });
const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
const listener = lb.addListener('Listener', { port: 80 });
const targetGroup1 = listener.addTargets('ECS1', {
port: 80,
targets: [service],
});
const targetGroup2 = listener.addTargets('ECS2', {
port: 80,
targets: [service.loadBalancerTarget({
containerName: 'MyContainer',
containerPort: 8080
})],
});
```
> Note: ECS Anywhere doesn't support application/network load balancers.
Note 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.
Alternatively, 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.
```ts
declare const cluster: ecs.Cluster;
declare const taskDefinition: ecs.TaskDefinition;
declare const vpc: ec2.Vpc;
const service = new ecs.FargateService(this, 'Service', { cluster, taskDefinition });
const lb = new elbv2.ApplicationLoadBalancer(this, 'LB', { vpc, internetFacing: true });
const listener = lb.addListener('Listener', { port: 80 });
service.registerLoadBalancerTargets(
{
containerName: 'web',
containerPort: 80,
newTargetGroupId: 'ECS',
listener: ecs.ListenerConfig.applicationListener(listener, {
protocol: elbv2.ApplicationProtocol.HTTPS
}),
},
);
```
### Using a Load Balancer from a different Stack