

# Amazon ECS 教程
<a name="ecs-tutorials"></a>

以下教程说明在使用 Amazon ECS 时如何执行常见任务。

您可以使用以下任一教程来详细了解 Amazon ECS 入门。


| 教程概述 | 了解详情 | 
| --- | --- | 
|  在 Fargate 上开始使用 Amazon ECS。  |  [了解如何为 Fargate 创建 Amazon ECS Linux 任务](getting-started-fargate.md)  | 
|  在 Fargate 上开始使用 Windows 容器。  |  [了解如何为 Fargate 创建 Amazon ECS Windows 任务](Windows_fargate-getting_started.md)  | 
|  开始使用适用于 EC2 的 Windows 容器。  |  [了解如何创建 EC2 的 Amazon ECS Windows 任务](getting-started-ecs-ec2-v2.md)  | 

您可以借助以下任一教程，使用 AWS CLI 在 Amazon ECS 上部署任务


| 教程概述 | 了解详情 | 
| --- | --- | 
|  为 Fargate 创建一个 Linux 任务。  |  [使用 AWS CLI 为 Fargate 创建 Amazon ECS Linux 任务](ECS_AWSCLI_Fargate.md)  | 
|  为 Fargate 创建一个 Windows 任务。  |  [使用 AWS CLI 为 Fargate 创建 Amazon ECS Windows 任务](ECS_AWSCLI_Fargate_windows.md)  | 
|  为 EC2 创建一个 Linux 任务。  |  [使用 AWS CLI 为 EC2 创建 Amazon ECS 任务](ECS_AWSCLI_EC2.md)  | 

您可以借助以下任一教程来详细了解监控和日志记录。


| 教程概述 | 了解详情 | 
| --- | --- | 
|  设置一个简单的 Lambda 函数，用于侦听任务事件并将其写出到 CloudWatch Logs 日志流。  |  [配置 Amazon ECS 以侦听 CloudWatch Events 事件](ecs_cwet.md)  | 
|  配置一个 Amazon EventBridge 事件规则，用于仅捕获任务因某个主要容器终止而停止的任务事件。  |  [针对 Amazon ECS 任务停止事件发送 Amazon Simple Notification Service 警报](ecs_cwet2.md)  | 
|  连接原属于一个上下文但被分割为多个记录或日志行的日志消息。  |  [连接多行或堆栈跟踪 Amazon ECS 日志消息](firelens-concatanate-multiline.md)  | 
|  在 Amazon ECS 中运行的 Windows 实例上部署 Fluent Bit 容器，以便将 Windows 任务生成的日志流式传输到 Amazon CloudWatch 进行集中日志记录。  |  [在 Amazon ECS Windows 容器上部署 Fluent Bit](tutorial-deploy-fluentbit-on-windows.md)  | 

您可以借助以下任一教程来详细了解如何在 Amazon ECS 上将 Active Directory 身份验证与组托管服务账户结合使用。


| 教程概述 | 了解详情 | 
| --- | --- | 
|  在 EC2 上将组托管服务账户与 Linux 容器结合使用。  |  [将 gMSA 用于 Amazon ECS 上的 EC2 Linux 容器](linux-gmsa.md)  | 
|  在 EC2 上将组托管服务账户与 Windows 容器结合使用。  |  [了解了解如何将 gMSA 用于适用于 Amazon ECS 的 EC2 Windows 容器](windows-gmsa.md)  | 
|  在 Fargate 上将组托管服务账户与 Linux 容器结合使用。  |  [将 gMSA 用于 Fargate 上的 Linux 容器](fargate-linux-gmsa.md)  | 
|  创建一个运行 Windows 容器的任务，该容器含有使用无域组托管服务账户访问 Active Directory 所需的凭证。  |  [通过 AWS CLI 将 Amazon ECS Windows 容器与无域 gMSA 结合使用](tutorial-gmsa-windows.md)  | 

# 使用 AWS CLI 为 Fargate 创建 Amazon ECS Linux 任务
<a name="ECS_AWSCLI_Fargate"></a>

以下步骤帮助您在 Amazon ECS 中使用 AWS CLI 设置集群、注册任务定义、运行 Linux 任务和执行其他常见方案。使用最新版本的 AWS CLI。有关如何升级到最新版本的更多信息，请参阅[安装或更新到最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

**注意**  
您可以使用双堆栈服务端点通过 IPv4 和 IPv6 从 AWS CLI、SDK 和 Amazon ECS API 与 Amazon ECS 进行交互。有关更多信息，请参阅 [使用 Amazon ECS 双堆栈端点](dual-stack-endpoint.md)。

**Topics**
+ [

## 先决条件
](#ECS_AWSCLI_Fargate_prereq)
+ [

## 步骤 1：创建集群
](#ECS_AWSCLI_Fargate_create_cluster)
+ [

## 第 2 步：注册 Linux 任务定义
](#ECS_AWSCLI_Fargate_register_task_definition)
+ [

## 第 3 部：列出任务定义
](#ECS_AWSCLI_Fargate_list_task_definitions)
+ [

## 步骤 4：创建服务
](#ECS_AWSCLI_Fargate_create_service)
+ [

## 步骤 5：列出服务
](#ECS_AWSCLI_Fargate_list_services)
+ [

## 步骤 6：描述正在运行的服务
](#ECS_AWSCLI_Fargate_describe_service)
+ [

## 步骤 7：测试
](#ECS_AWSCLI_Fargate_test)
+ [

## 步骤 8：清除
](#ECS_AWSCLI_Fargate_clean_up)

## 先决条件
<a name="ECS_AWSCLI_Fargate_prereq"></a>

本教程假设以下先决条件已完成。
+ 安装并配置了最新版本的 AWS CLI。有关安装或升级 AWS CLI 的更多信息，请参阅 [Installing or updating to the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您的 IAM 用户具有 [AmazonECS\$1FullAccess](security-iam-awsmanpol.md#security-iam-awsmanpol-AmazonECS_FullAccess) IAM 策略示例中指定的必需权限。
+ 您已创建要使用的 VPC 和安全组。本教程使用的是托管在 Amazon ECR Public 上的容器映像，因此，您的任务必须具有互联网访问权限。要让您的任务连接到互联网，请使用下列选项之一。
  + 将私有子网与具有弹性 IP 地址的 NAT 网关结合使用。
  + 使用公有子网并向任务分配公有 IP 地址。

  有关更多信息，请参阅 [创建虚拟私有云](get-set-up-for-amazon-ecs.md#create-a-vpc)。

  有关安全组的信息，请参阅**《Amazon Virtual Private Cloud 用户指南》中的 [VPC 的默认安全组](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html#DefaultSecurityGroup)和[示例规则](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html#security-group-rule-examples)。
+  如果您使用私有子网学习本教程，则可以使用 Amazon ECS Exec 直接与您的容器交互并测试部署。您需要创建一个任务 IAM 角色才能使用 ECS Exec。有关任务 IAM 角色和其他先决条件的更多信息，请参阅[使用 Amazon ECS Exec 监控 Amazon ECS 容器](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-exec.html)。
+ （可选）AWS CloudShell 是一种为客户提供命令行的工具，而无需创建自己的 EC2 实例。有关更多信息，请参阅《AWS CloudShell 用户指南》**中的[什么是 AWS CloudShell？](https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html)。

## 步骤 1：创建集群
<a name="ECS_AWSCLI_Fargate_create_cluster"></a>

默认情况下，您的账户会收到一个 `default` 集群。

**注意**  
使用为您提供的 `default` 集群的好处是您不必在后续命令中指定 `--cluster cluster_name` 选项。如果您自行创建非默认集群，您必须为您打算用于该集群的每个命令指定 `--cluster cluster_name`。

使用以下命令自行创建具有唯一名称的集群：

```
aws ecs create-cluster --cluster-name fargate-cluster
```

输出：

```
{
    "cluster": {
        "status": "ACTIVE", 
        "defaultCapacityProviderStrategy": [], 
        "statistics": [], 
        "capacityProviders": [], 
        "tags": [], 
        "clusterName": "fargate-cluster", 
        "settings": [
            {
                "name": "containerInsights", 
                "value": "disabled"
            }
        ], 
        "registeredContainerInstancesCount": 0, 
        "pendingTasksCount": 0, 
        "runningTasksCount": 0, 
        "activeServicesCount": 0, 
        "clusterArn": "arn:aws:ecs:region:aws_account_id:cluster/fargate-cluster"
    }
}
```

## 第 2 步：注册 Linux 任务定义
<a name="ECS_AWSCLI_Fargate_register_task_definition"></a>

您必须先注册任务定义才能在 ECS 集群上运行任务。任务定义是分组在一起的一系列容器。以下示例是一个简单的任务定义，它使用托管在 Docker Hub 上的 httpd 容器映像创建 PHP Web 应用程序。有关可用任务定义参数的更多信息，请参阅 [Amazon ECS 任务定义](task_definitions.md)。在本教程中，`taskRoleArn` 只有当您再私有子网中部署任务并希望测试部署时才需要。将 `taskRoleArn` 替换为您创建的 IAM 任务角色，以便使用 ECS Exec，如 [先决条件](#ECS_AWSCLI_Fargate_prereq) 中所述。

```
 {
        "family": "sample-fargate",
        "networkMode": "awsvpc",
        "taskRoleArn": "arn:aws:iam::aws_account_id:role/execCommandRole", 
        "containerDefinitions": [
            {
                "name": "fargate-app",
                "image": "public.ecr.aws/docker/library/httpd:latest",
                "portMappings": [
                    {
                        "containerPort": 80,
                        "hostPort": 80,
                        "protocol": "tcp"
                    }
                ],
                "essential": true,
                "entryPoint": [
                    "sh",
                    "-c"
                ],
                "command": [
                    "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' >  /usr/local/apache2/htdocs/index.html && httpd-foreground\""
                ]
            }
        ],
        "requiresCompatibilities": [
            "FARGATE"
        ],
        "cpu": "256",
        "memory": "512"
}
```

将任务定义 JSON 保存为文件并使用 `--cli-input-json file://path_to_file.json` 选项传递它。

将 JSON 文件用于容器定义：

```
aws ecs register-task-definition --cli-input-json file://$HOME/tasks/fargate-task.json
```

**register-task-definition** 命令在完成其注册后会返回任务定义的描述。

## 第 3 部：列出任务定义
<a name="ECS_AWSCLI_Fargate_list_task_definitions"></a>

您可以随时使用 **list-task-definitions** 命令列出您的账户的任务定义。此命令的输出将显示 `family` 和 `revision` 值，您可以在调用 **run-task** 或 **start-task** 时将这些值一起使用。

```
aws ecs list-task-definitions
```

输出：

```
{
    "taskDefinitionArns": [
        "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate:1"
    ]
}
```

## 步骤 4：创建服务
<a name="ECS_AWSCLI_Fargate_create_service"></a>

为账户注册任务后，您可以为集群中已注册的任务创建服务。在此示例中，您将使用集群中运行的一个 `sample-fargate:1` 任务定义实例来创建服务。此任务需要连接到互联网，您可以通过两种方法实现这一点。一种方法是将使用 NAT 网关配置的私有子网与公有子网中的弹性 IP 地址结合使用。另一种方法是使用公有子网并向任务分配公有 IP 地址。下面提供了这两种方法的示例。

使用私有子网的示例。需要 ` enable-execute-command ` 选项才能使用 Amazon ECS Exec。

```
aws ecs create-service --cluster fargate-cluster --service-name fargate-service --task-definition sample-fargate:1 --desired-count 1 --launch-type "FARGATE" --network-configuration "awsvpcConfiguration={subnets=[subnet-abcd1234],securityGroups=[sg-abcd1234]}" --enable-execute-command
```

使用公有子网的示例。

```
aws ecs create-service --cluster fargate-cluster --service-name fargate-service --task-definition sample-fargate:1 --desired-count 1 --launch-type "FARGATE" --network-configuration "awsvpcConfiguration={subnets=[subnet-abcd1234],securityGroups=[sg-abcd1234],assignPublicIp=ENABLED}"
```

**create-service** 命令在完成其注册后会返回任务定义的描述。

## 步骤 5：列出服务
<a name="ECS_AWSCLI_Fargate_list_services"></a>

列出您的集群的服务。您应看到您在上一部分中创建的服务。您可以选取从此命令返回的服务名称或完整 ARN 并在稍后将其用于描述服务。

```
aws ecs list-services --cluster fargate-cluster
```

输出：

```
{
    "serviceArns": [
        "arn:aws:ecs:region:aws_account_id:service/fargate-cluster/fargate-service"
    ]
}
```

## 步骤 6：描述正在运行的服务
<a name="ECS_AWSCLI_Fargate_describe_service"></a>

使用之前检索到的服务名称描述服务，以获取有关任务的更多信息。

```
aws ecs describe-services --cluster fargate-cluster --services fargate-service
```

如果成功，这将返回服务失败和服务的描述。例如，在 ` services ` 部分中，您将找到有关部署的信息，例如任务的正在运行或待处理状态。也可以找到有关任务定义、网络配置和带时间戳的事件的信息。在失败部分中，您将找到与调用关联的失败的信息（如果有）。对于问题排查，请参阅[服务事件消息](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-event-messages.html)。有关服务描述的更多信息，请参阅[描述服务](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeServices)。

```
{
    "services": [
        {
            "networkConfiguration": {
                "awsvpcConfiguration": {
                    "subnets": [
                        "subnet-abcd1234"
                    ], 
                    "securityGroups": [
                        "sg-abcd1234"
                    ], 
                    "assignPublicIp": "ENABLED"
                }
            }, 
            "launchType": "FARGATE", 
            "enableECSManagedTags": false, 
            "loadBalancers": [], 
            "deploymentController": {
                "type": "ECS"
            }, 
            "desiredCount": 1, 
            "clusterArn": "arn:aws:ecs:region:aws_account_id:cluster/fargate-cluster", 
            "serviceArn": "arn:aws:ecs:region:aws_account_id:service/fargate-service", 
            "deploymentConfiguration": {
                "maximumPercent": 200, 
                "minimumHealthyPercent": 100
            }, 
            "createdAt": 1692283199.771, 
            "schedulingStrategy": "REPLICA", 
            "placementConstraints": [], 
            "deployments": [
                {
                    "status": "PRIMARY", 
                    "networkConfiguration": {
                        "awsvpcConfiguration": {
                            "subnets": [
                                "subnet-abcd1234"
                            ], 
                            "securityGroups": [
                                "sg-abcd1234"
                            ], 
                            "assignPublicIp": "ENABLED"
                        }
                    }, 
                    "pendingCount": 0, 
                    "launchType": "FARGATE", 
                    "createdAt": 1692283199.771, 
                    "desiredCount": 1, 
                    "taskDefinition": "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate:1", 
                    "updatedAt": 1692283199.771, 
                    "platformVersion": "1.4.0", 
                    "id": "ecs-svc/9223370526043414679", 
                    "runningCount": 0
                }
            ], 
            "serviceName": "fargate-service", 
            "events": [
                {
                    "message": "(service fargate-service) has started 2 tasks: (task 53c0de40-ea3b-489f-a352-623bf1235f08) (task d0aec985-901b-488f-9fb4-61b991b332a3).", 
                    "id": "92b8443e-67fb-4886-880c-07e73383ea83", 
                    "createdAt": 1510811841.408
                }, 
                {
                    "message": "(service fargate-service) has started 2 tasks: (task b4911bee-7203-4113-99d4-e89ba457c626) (task cc5853e3-6e2d-4678-8312-74f8a7d76474).", 
                    "id": "d85c6ec6-a693-43b3-904a-a997e1fc844d", 
                    "createdAt": 1510811601.938
                }, 
                {
                    "message": "(service fargate-service) has started 2 tasks: (task cba86182-52bf-42d7-9df8-b744699e6cfc) (task f4c1ad74-a5c6-4620-90cf-2aff118df5fc).", 
                    "id": "095703e1-0ca3-4379-a7c8-c0f1b8b95ace", 
                    "createdAt": 1510811364.691
                }
            ], 
            "runningCount": 0, 
            "status": "ACTIVE", 
            "serviceRegistries": [], 
            "pendingCount": 0, 
            "createdBy": "arn:aws:iam::aws_account_id:user/user_name", 
            "platformVersion": "LATEST", 
            "placementStrategy": [], 
            "propagateTags": "NONE", 
            "roleArn": "arn:aws:iam::aws_account_id:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS", 
            "taskDefinition": "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate:1"
        }
    ], 
    "failures": []
}
```

## 步骤 7：测试
<a name="ECS_AWSCLI_Fargate_test"></a>

### 使用公有子网部署的测试任务
<a name="ECS_AWSCLI_Fargate_test_public"></a>

描述服务中的任务，以便您可以获得该任务的弹性网络接口（ENI）。

首先，获取任务 ARN。

```
aws ecs list-tasks --cluster fargate-cluster --service fargate-service
```

输出包含任务 ARN。

```
{
    "taskArns": [
        "arn:aws:ecs:us-east-1:123456789012:task/fargate-service/EXAMPLE
    ]
}
```

描述任务并找到 ENI ID。将任务 ARN 用于 `tasks` 参数。

```
aws ecs describe-tasks --cluster fargate-cluster --tasks arn:aws:ecs:us-east-1:123456789012:task/service/EXAMPLE
```

输出中列出了附件信息。

```
{
    "tasks": [
        {
            "attachments": [
                {
                    "id": "d9e7735a-16aa-4128-bc7a-b2d5115029e9",
                    "type": "ElasticNetworkInterface",
                    "status": "ATTACHED",
                    "details": [
                        {
                            "name": "subnetId",
                            "value": "subnetabcd1234"
                        },
                        {
                            "name": "networkInterfaceId",
                            "value": "eni-0fa40520aeEXAMPLE"
                        },
                    ]
                }
…
}
```

请描述 ENI 以获取公有 IP 地址。

```
aws ec2 describe-network-interfaces --network-interface-id  eni-0fa40520aeEXAMPLE
```

公有 IP 地址在输出中。

```
{
    "NetworkInterfaces": [
        {
            "Association": {
                "IpOwnerId": "amazon",
                "PublicDnsName": "ec2-34-229-42-222.compute-1.amazonaws.com",
                "PublicIp": "198.51.100.2"
            },
…
}
```

在 Web 浏览器中，输入公有 IP 地址，您应该可以看到显示 **Amazon ECS **样本应用程序的网页。

### 使用私有子网部署的测试任务
<a name="ECS_AWSCLI_Fargate_test_private.title"></a>

 描述任务并找到 `managedAgents` 以验证 `ExecuteCommandAgent` 是否正在运行。记下 `privateIPv4Address` 以供将来使用。

```
aws ecs describe-tasks --cluster fargate-cluster --tasks arn:aws:ecs:us-east-1:123456789012:task/fargate-service/EXAMPLE
```

 输出中列出了托管代理信息。

```
{
     "tasks": [
        {
            "attachments": [
                {
                    "id": "d9e7735a-16aa-4128-bc7a-b2d5115029e9",
                    "type": "ElasticNetworkInterface",
                    "status": "ATTACHED",
                    "details": [
                        {
                            "name": "subnetId",
                            "value": "subnetabcd1234"
                        },
                        {
                            "name": "networkInterfaceId",
                            "value": "eni-0fa40520aeEXAMPLE"
                        },
                        {
                            "name": "privateIPv4Address",
                            "value": "10.0.143.156"
                        }
                    ]
                }
            ],
     ...  
     "containers": [
         {
         ...
        "managedAgents": [
                        {
                            "lastStartedAt": "2023-08-01T16:10:13.002000+00:00",
                            "name": "ExecuteCommandAgent",
                            "lastStatus": "RUNNING"
                        } 
                ],
        ...
    }
```

 验证 ` ExecuteCommandAgent` 是否正在运行后，可以运行以下命令，以在任务中的容器上运行交互式 shell。

```
  aws ecs execute-command --cluster fargate-cluster \
      --task  arn:aws:ecs:us-east-1:123456789012:task/fargate-service/EXAMPLE  \
      --container  fargate-app \
      --interactive \
      --command "/bin/sh"
```

 交互式 shell 运行后，运行以下命令来安装 cURL。

```
apt update 
```

```
apt install curl 
```

 安装 cURL 后，使用之前获得的私有 IP 地址运行以下命令。

```
 curl 10.0.143.156 
```

 您应该会看到与 **Amazon ECS** 示例应用程序网页等效的 HTML。

```
<html>
    <head> 
     <title>Amazon ECS Sample App</title> 
     <style>body {margin-top: 40px; background-color: #333;} </style>
    </head>
      <body> 
      <div style=color:white;text-align:center> 
      <h1>Amazon ECS Sample App</h1> 
      <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> 
      </div>
      </body>
</html>
```

## 步骤 8：清除
<a name="ECS_AWSCLI_Fargate_clean_up"></a>

完成本教程后，您应清除相关资源，以避免产生与未使用的资源相关的费用。

删除服务。

```
aws ecs delete-service --cluster fargate-cluster --service fargate-service --force
```

请删除集群。

```
aws ecs delete-cluster --cluster fargate-cluster
```

# 使用 AWS CLI 为 Fargate 创建 Amazon ECS Windows 任务
<a name="ECS_AWSCLI_Fargate_windows"></a>

以下步骤帮助您在 Amazon ECS 中使用 AWS CLI 设置集群、注册任务定义、运行 Windows 任务和执行其他常见方案。请确保您使用的是最新版本的 AWS CLI。有关如何升级到最新版本的更多信息，请参阅[安装或更新到最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

**注意**  
您可以使用双堆栈服务端点通过 IPv4 和 IPv6 从 AWS CLI、SDK 和 Amazon ECS API 与 Amazon ECS 进行交互。有关更多信息，请参阅 [使用 Amazon ECS 双堆栈端点](dual-stack-endpoint.md)。

**Topics**
+ [

## 先决条件
](#ECS_AWSCLI_Fargate_windows_prereq)
+ [

## 步骤 1：创建集群
](#ECS_AWSCLI_Fargate_windows_create_cluster)
+ [

## 步骤 2：注册 Windows 任务定义
](#ECS_AWSCLI_Fargate_windows_register_task_definition)
+ [

## 步骤 3：列出任务定义
](#ECS_AWSCLI_Fargate_windows__list_task_definitions)
+ [

## 步骤 4：创建服务
](#ECS_AWSCLI_Fargate_windows_create_service)
+ [

## 步骤 5：列出服务
](#ECS_AWSCLI_Fargate_windows_list_services)
+ [

## 步骤 6：描述正在运行的服务
](#ECS_AWSCLI_Fargate_windows_describe_service)
+ [

## 步骤 7：清理
](#ECS_AWSCLI_Fargate_windows_clean_up)

## 先决条件
<a name="ECS_AWSCLI_Fargate_windows_prereq"></a>

本教程假设以下先决条件已完成。
+ 安装并配置了最新版本的 AWS CLI。有关安装或升级 AWS CLI 的更多信息，请参阅 [Installing or updating to the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您的 IAM 用户具有 [AmazonECS\$1FullAccess](security-iam-awsmanpol.md#security-iam-awsmanpol-AmazonECS_FullAccess) IAM 策略示例中指定的必需权限。
+ 您已创建要使用的 VPC 和安全组。本教程使用的是托管在 Docker Hub 上的容器映像，因此，您的任务必须具有互联网访问权限。要让您的任务连接到互联网，请使用下列选项之一。
  + 将私有子网与具有弹性 IP 地址的 NAT 网关结合使用。
  + 使用公有子网并向任务分配公有 IP 地址。

  有关更多信息，请参阅 [创建虚拟私有云](get-set-up-for-amazon-ecs.md#create-a-vpc)。

  有关安全组的信息，请参阅**《Amazon Virtual Private Cloud 用户指南》中的 [VPC 的默认安全组](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html#DefaultSecurityGroup)和[示例规则](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html#security-group-rule-examples)。
+ （可选）AWS CloudShell 是一种为客户提供命令行的工具，而无需创建自己的 EC2 实例。有关更多信息，请参阅《AWS CloudShell 用户指南》**中的[什么是 AWS CloudShell？](https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html)。

## 步骤 1：创建集群
<a name="ECS_AWSCLI_Fargate_windows_create_cluster"></a>

默认情况下，您的账户会收到一个 `default` 集群。

**注意**  
使用为您提供的 `default` 集群的好处是您不必在后续命令中指定 `--cluster cluster_name` 选项。如果您自行创建非默认集群，您必须为您打算用于该集群的每个命令指定 `--cluster cluster_name`。

使用以下命令自行创建具有唯一名称的集群：

```
aws ecs create-cluster --cluster-name fargate-cluster
```

输出：

```
{
    "cluster": {
        "status": "ACTIVE", 
        "statistics": [], 
        "clusterName": "fargate-cluster", 
        "registeredContainerInstancesCount": 0, 
        "pendingTasksCount": 0, 
        "runningTasksCount": 0, 
        "activeServicesCount": 0, 
        "clusterArn": "arn:aws:ecs:region:aws_account_id:cluster/fargate-cluster"
    }
}
```

## 步骤 2：注册 Windows 任务定义
<a name="ECS_AWSCLI_Fargate_windows_register_task_definition"></a>

您必须先注册任务定义才能在 Amazon ECS 集群上运行 Windows 任务。任务定义是分组在一起的一系列容器。下面的示例是创建 Web 应用程序的一个简单任务定义。有关可用任务定义参数的更多信息，请参阅 [Amazon ECS 任务定义](task_definitions.md)。

```
{
    "containerDefinitions": [
        {
            "command": ["New-Item -Path C:\\inetpub\\wwwroot\\index.html -Type file -Value '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p>'; C:\\ServiceMonitor.exe w3svc"],
            "entryPoint": [
                "powershell",
                "-Command"
            ],
            "essential": true,
            "cpu": 2048,
            "memory": 4096,
            "image": "mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019",
            "name": "sample_windows_app",
            "portMappings": [
                {
                    "hostPort": 80,
                    "containerPort": 80,
                    "protocol": "tcp"
                }
            ]
        }
    ],
    "memory": "4096",
    "cpu": "2048",
    "networkMode": "awsvpc",
    "family": "windows-simple-iis-2019-core",
    "executionRoleArn": "arn:aws:iam::012345678910:role/ecsTaskExecutionRole",
    "runtimePlatform": {"operatingSystemFamily": "WINDOWS_SERVER_2019_CORE"},
    "requiresCompatibilities": ["FARGATE"]
}
```

上述示例 JSON 可通过两种方式传递到 AWS CLI：您可以将任务定义 JSON 保存为文件并使用 `--cli-input-json file://path_to_file.json` 选项传递它。

将 JSON 文件用于容器定义：

```
aws ecs register-task-definition --cli-input-json file://$HOME/tasks/fargate-task.json
```

**register-task-definition** 命令在完成其注册后会返回任务定义的描述。

## 步骤 3：列出任务定义
<a name="ECS_AWSCLI_Fargate_windows__list_task_definitions"></a>

您可以随时使用 **list-task-definitions** 命令列出您的账户的任务定义。此命令的输出将显示 `family` 和 `revision` 值，您可以在调用 **run-task** 或 **start-task** 时将这些值一起使用。

```
aws ecs list-task-definitions
```

输出：

```
{
    "taskDefinitionArns": [
        "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate-windows:1"
    ]
}
```

## 步骤 4：创建服务
<a name="ECS_AWSCLI_Fargate_windows_create_service"></a>

为账户注册任务后，您可以为集群中已注册的任务创建服务。在此示例中，您将使用集群中运行的一个 `sample-fargate:1` 任务定义实例来创建服务。此任务需要连接到互联网，您可以通过两种方法实现这一点。一种方法是将使用 NAT 网关配置的私有子网与公有子网中的弹性 IP 地址结合使用。另一种方法是使用公有子网并向任务分配公有 IP 地址。下面提供了这两种方法的示例。

使用私有子网的示例。

```
aws ecs create-service --cluster fargate-cluster --service-name fargate-service --task-definition sample-fargate-windows:1 --desired-count 1 --launch-type "FARGATE" --network-configuration "awsvpcConfiguration={subnets=[subnet-abcd1234],securityGroups=[sg-abcd1234]}"
```

使用公有子网的示例。

```
aws ecs create-service --cluster fargate-cluster --service-name fargate-service --task-definition sample-fargate-windows:1 --desired-count 1 --launch-type "FARGATE" --network-configuration "awsvpcConfiguration={subnets=[subnet-abcd1234],securityGroups=[sg-abcd1234],assignPublicIp=ENABLED}"
```

**create-service** 命令在完成其注册后会返回任务定义的描述。

## 步骤 5：列出服务
<a name="ECS_AWSCLI_Fargate_windows_list_services"></a>

列出您的集群的服务。您应看到您在上一部分中创建的服务。您可以选取从此命令返回的服务名称或完整 ARN 并在稍后将其用于描述服务。

```
aws ecs list-services --cluster fargate-cluster
```

输出：

```
{
    "serviceArns": [
        "arn:aws:ecs:region:aws_account_id:service/fargate-service"
    ]
}
```

## 步骤 6：描述正在运行的服务
<a name="ECS_AWSCLI_Fargate_windows_describe_service"></a>

使用之前检索到的服务名称描述服务，以获取有关任务的更多信息。

```
aws ecs describe-services --cluster fargate-cluster --services fargate-service
```

如果成功，这将返回服务失败和服务的描述。例如，在服务部分中，您将找到有关部署的信息，例如任务的正在运行或待处理状态。也可以找到有关任务定义、网络配置和带时间戳的事件的信息。在失败部分中，您将找到与调用关联的失败的信息（如果有）。对于问题排查，请参阅[服务事件消息](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-event-messages.html)。有关服务描述的更多信息，请参阅[描述服务](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_DescribeServices)。

```
{
    "services": [
        {
            "status": "ACTIVE", 
            "taskDefinition": "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate-windows:1", 
            "pendingCount": 2, 
            "launchType": "FARGATE", 
            "loadBalancers": [], 
            "roleArn": "arn:aws:iam::aws_account_id:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS", 
            "placementConstraints": [], 
            "createdAt": 1510811361.128, 
            "desiredCount": 2, 
            "networkConfiguration": {
                "awsvpcConfiguration": {
                    "subnets": [
                        "subnet-abcd1234"
                    ], 
                    "securityGroups": [
                        "sg-abcd1234"
                    ], 
                    "assignPublicIp": "DISABLED"
                }
            }, 
            "platformVersion": "LATEST", 
            "serviceName": "fargate-service", 
            "clusterArn": "arn:aws:ecs:region:aws_account_id:cluster/fargate-cluster", 
            "serviceArn": "arn:aws:ecs:region:aws_account_id:service/fargate-service", 
            "deploymentConfiguration": {
                "maximumPercent": 200, 
                "minimumHealthyPercent": 100
            }, 
            "deployments": [
                {
                    "status": "PRIMARY", 
                    "networkConfiguration": {
                        "awsvpcConfiguration": {
                            "subnets": [
                                "subnet-abcd1234"
                            ], 
                            "securityGroups": [
                                "sg-abcd1234"
                            ], 
                            "assignPublicIp": "DISABLED"
                        }
                    }, 
                    "pendingCount": 2, 
                    "launchType": "FARGATE", 
                    "createdAt": 1510811361.128, 
                    "desiredCount": 2, 
                    "taskDefinition": "arn:aws:ecs:region:aws_account_id:task-definition/sample-fargate-windows:1", 
                    "updatedAt": 1510811361.128, 
                    "platformVersion": "0.0.1", 
                    "id": "ecs-svc/9223370526043414679", 
                    "runningCount": 0
                }
            ], 
            "events": [
                {
                    "message": "(service fargate-service) has started 2 tasks: (task 53c0de40-ea3b-489f-a352-623bf1235f08) (task d0aec985-901b-488f-9fb4-61b991b332a3).", 
                    "id": "92b8443e-67fb-4886-880c-07e73383ea83", 
                    "createdAt": 1510811841.408
                }, 
                {
                    "message": "(service fargate-service) has started 2 tasks: (task b4911bee-7203-4113-99d4-e89ba457c626) (task cc5853e3-6e2d-4678-8312-74f8a7d76474).", 
                    "id": "d85c6ec6-a693-43b3-904a-a997e1fc844d", 
                    "createdAt": 1510811601.938
                }, 
                {
                    "message": "(service fargate-service) has started 2 tasks: (task cba86182-52bf-42d7-9df8-b744699e6cfc) (task f4c1ad74-a5c6-4620-90cf-2aff118df5fc).", 
                    "id": "095703e1-0ca3-4379-a7c8-c0f1b8b95ace", 
                    "createdAt": 1510811364.691
                }
            ], 
            "runningCount": 0, 
            "placementStrategy": []
        }
    ], 
    "failures": []
}
```

## 步骤 7：清理
<a name="ECS_AWSCLI_Fargate_windows_clean_up"></a>

完成本教程后，您应清除相关资源，以避免产生与未使用的资源相关的费用。

删除服务。

```
aws ecs delete-service --cluster fargate-cluster --service fargate-service --force
```

请删除集群。

```
aws ecs delete-cluster --cluster fargate-cluster
```

# 使用 AWS CLI 为 EC2 创建 Amazon ECS 任务
<a name="ECS_AWSCLI_EC2"></a>

以下步骤帮助您在 Amazon ECS 中使用 AWS CLI 设置集群、注册任务定义、运行任务和执行其他常见方案。使用最新版本的 AWS CLI。有关如何升级到最新版本的更多信息，请参阅[安装或更新到最新版本的 AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。

**注意**  
您可以使用双堆栈服务端点通过 IPv4 和 IPv6 从 AWS CLI、SDK 和 Amazon ECS API 与 Amazon ECS 进行交互。有关更多信息，请参阅 [使用 Amazon ECS 双堆栈端点](dual-stack-endpoint.md)。

**Topics**
+ [

## 先决条件
](#AWSCLI_EC2_prereq)
+ [

## 创建集群
](#AWSCLI_EC2_create_cluster)
+ [

## 使用 Amazon ECS AMI 启动容器实例
](#AWSCLI_EC2_launch_container_instance)
+ [

## 列出容器实例
](#AWSCLI_EC2_list_container_instances)
+ [

## 描述容器实例
](#AWSCLI_EC2_describe_container_instance)
+ [

## 注册任务定义
](#AWSCLI_EC2_register_task_definition)
+ [

## 列出任务定义
](#AWSCLI_EC2_list_task_definitions)
+ [

## 创建服务
](#AWSCLI_EC2_run_task)
+ [

## 列出服务
](#AWSCLI_EC2_list_tasks)
+ [

## 描述服务
](#AWSCLI_EC2_describe_service)
+ [

## 描述正在运行的任务
](#AWSCLI_EC2_describe_task)
+ [

## 测试 Web 服务器
](#AWSCLI_EC2_test_web_server)
+ [

## 清理 资源
](#AWSCLI_EC2_clean_up_resources)

## 先决条件
<a name="AWSCLI_EC2_prereq"></a>

本教程假设以下先决条件已完成：
+ 安装并配置了最新版本的 AWS CLI。有关安装或升级 AWS CLI 的更多信息，请参阅 [Installing or updating to the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您的 IAM 用户具有 [AmazonECS\$1FullAccess](security-iam-awsmanpol.md#security-iam-awsmanpol-AmazonECS_FullAccess) IAM 策略示例中指定的必需权限。
+ 您已创建了一个可供使用的容器实例 IAM 角色。有关更多信息，请参阅[Amazon ECS 容器实例 IAM 角色](instance_IAM_role.md)。
+ 您已创建了一个可供使用的 VPC。有关更多信息，请参阅 [创建虚拟私有云](get-set-up-for-amazon-ecs.md#create-a-vpc)。
+ （可选）AWS CloudShell 是一种为客户提供命令行的工具，而无需创建自己的 EC2 实例。有关更多信息，请参阅《AWS CloudShell 用户指南》**中的[什么是 AWS CloudShell？](https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html)。

## 创建集群
<a name="AWSCLI_EC2_create_cluster"></a>

默认情况下，当您启动第一个容器实例时，您的账户将收到一个 `default` 集群。

**注意**  
使用为您提供的 `default` 集群的好处是您不必在后续命令中指定 `--cluster cluster_name` 选项。如果您自行创建非默认集群，您必须为您打算用于该集群的每个命令指定 `--cluster cluster_name`。

使用以下命令自行创建具有唯一名称的集群：

```
aws ecs create-cluster --cluster-name MyCluster
```

输出：

```
{
    "cluster": {
        "clusterName": "MyCluster",
        "status": "ACTIVE",
        "clusterArn": "arn:aws:ecs:region:aws_account_id:cluster/MyCluster"
    }
}
```

## 使用 Amazon ECS AMI 启动容器实例
<a name="AWSCLI_EC2_launch_container_instance"></a>

容器实例是已注册到集群并且将会运行 Amazon ECS 容器代理的 EC2 实例。在这一部分中，您将使用 ECS 优化版 AMI 启动一个 EC2 实例。

**使用 AWS CLI 启动容器实例**

1. 使用以下命令检索您所在 AWS 区域的最新 ECS 优化版 Amazon Linux 2 AMI ID。此命令使用 AWS Systems Manager Parameter Store 来获取最新 ECS 优化版 AMI ID。该 AMI 包括预装的 Amazon ECS 容器代理和 Docker 运行时。

   ```
   aws ssm get-parameters --names /aws/service/ecs/optimized-ami/amazon-linux-2/recommended --query 'Parameters[0].Value' --output text | jq -r '.image_id'
   ```

   输出：

   ```
   ami-abcd1234
   ```

1. 创建一个允许 SSH 访问（用来管理容器实例）和 HTTP 访问（用于 Web 服务器）的安全组。

   ```
   aws ec2 create-security-group --group-name ecs-tutorial-sg --description "ECS tutorial security group"
   ```

   输出：

   ```
   {
       "GroupId": "sg-abcd1234"
   }
   ```

1. 通过运行以下命令，将一条入站规则添加到该安全组。

   ```
   aws ec2 authorize-security-group-ingress --group-id sg-abcd1234 --protocol tcp --port 80 --cidr 0.0.0.0/0
   ```

   输出：

   ```
   {
       "Return": true,
       "SecurityGroupRules": [
           {
               "SecurityGroupRuleId": "sgr-efgh5678",
               "GroupId": "sg-abcd1234",
               "GroupOwnerId": "123456789012",
               "IsEgress": false,
               "IpProtocol": "tcp",
               "FromPort": 80,
               "ToPort": 80,
               "CidrIpv4": "0.0.0.0/0"
           }
       ]
   }
   ```

   现在，该安全组将允许从指定 IP 范围进行 SSH 访问以及从任何位置进行 HTTP 访问。在生产环境中，您应仅允许从您的特定 IP 地址进行 SSH 访问，并在需要时考虑限制 HTTP 访问。

1. 创建一个 EC2 密钥对，用于对容器实例的 SSH 访问。

   ```
   aws ec2 create-key-pair --key-name ecs-tutorial-key --query 'KeyMaterial' --output text > ecs-tutorial-key.pem
   chmod 400 ecs-tutorial-key.pem
   ```

   该私有密钥将保存到具有恰当 SSH 访问权限的本地计算机中。

1. 使用 ECS 优化版 AMI 启动一个 EC2 实例，并进行配置以加入您的集群。

   ```
   aws ec2 run-instances --image-id ami-abcd1234 --instance-type t3.micro --key-name ecs-tutorial-key --security-group-ids sg-abcd1234 --iam-instance-profile Name=ecsInstanceRole --user-data '#!/bin/bash
   echo ECS_CLUSTER=MyCluster >> /etc/ecs/ecs.config'
   {
       "Instances": [
           {
               "InstanceId": "i-abcd1234",
               "ImageId": "ami-abcd1234",
               "State": {
                   "Code": 0,
                   "Name": "pending"
               },
               "PrivateDnsName": "",
               "PublicDnsName": "",
               "StateReason": {
                   "Code": "pending",
                   "Message": "pending"
               },
               "InstanceType": "t3.micro",
               "KeyName": "ecs-tutorial-key",
               "LaunchTime": "2025-01-13T10:30:00.000Z"
           }
       ]
   }
   ```

   此用户数据脚本会配置 Amazon ECS 代理，以将该实例注册到您的 `MyCluster`。该实例使用 `ecsInstanceRole` IAM 角色，从而为该代理提供所需的权限。

## 列出容器实例
<a name="AWSCLI_EC2_list_container_instances"></a>

在启动您的容器实例后的几分钟内，Amazon ECS 代理将向您的 MyCluster 集群注册该实例。您可以通过运行以下命令列出集群中的容器实例：

```
aws ecs list-container-instances --cluster MyCluster
```

输出：

```
{
    "containerInstanceArns": [
        "arn:aws:ecs:us-east-1:aws_account_id:container-instance/container_instance_ID"
    ]
}
```

## 描述容器实例
<a name="AWSCLI_EC2_describe_container_instance"></a>

在拥有某个容器实例的 ARN 或 ID 后，您就可以使用 **describe-container-instances** 命令获取有关该实例的有价值的信息，例如剩余的和已注册的 CPU 和内存资源。

```
aws ecs describe-container-instances --cluster MyCluster --container-instances container_instance_ID
```

输出：

```
{
    "failures": [],
    "containerInstances": [
        {
            "status": "ACTIVE",
            "registeredResources": [
                {
                    "integerValue": 1024,
                    "longValue": 0,
                    "type": "INTEGER",
                    "name": "CPU",
                    "doubleValue": 0.0
                },
                {
                    "integerValue": 995,
                    "longValue": 0,
                    "type": "INTEGER",
                    "name": "MEMORY",
                    "doubleValue": 0.0
                },
                {
                    "name": "PORTS",
                    "longValue": 0,
                    "doubleValue": 0.0,
                    "stringSetValue": [
                        "22",
                        "2376",
                        "2375",
                        "51678"
                    ],
                    "type": "STRINGSET",
                    "integerValue": 0
                },
                {
                    "name": "PORTS_UDP",
                    "longValue": 0,
                    "doubleValue": 0.0,
                    "stringSetValue": [],
                    "type": "STRINGSET",
                    "integerValue": 0
                }
            ],
            "ec2InstanceId": "instance_id",
            "agentConnected": true,
            "containerInstanceArn": "arn:aws:ecs:us-west-2:aws_account_id:container-instance/container_instance_ID",
            "pendingTasksCount": 0,
            "remainingResources": [
                {
                    "integerValue": 1024,
                    "longValue": 0,
                    "type": "INTEGER",
                    "name": "CPU",
                    "doubleValue": 0.0
                },
                {
                    "integerValue": 995,
                    "longValue": 0,
                    "type": "INTEGER",
                    "name": "MEMORY",
                    "doubleValue": 0.0
                },
                {
                    "name": "PORTS",
                    "longValue": 0,
                    "doubleValue": 0.0,
                    "stringSetValue": [
                        "22",
                        "2376",
                        "2375",
                        "51678"
                    ],
                    "type": "STRINGSET",
                    "integerValue": 0
                },
                {
                    "name": "PORTS_UDP",
                    "longValue": 0,
                    "doubleValue": 0.0,
                    "stringSetValue": [],
                    "type": "STRINGSET",
                    "integerValue": 0
                }
            ],
            "runningTasksCount": 0,
            "attributes": [
                {
                    "name": "com.amazonaws.ecs.capability.privileged-container"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
                },
                {
                    "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.json-file"
                },
                {
                    "name": "com.amazonaws.ecs.capability.logging-driver.syslog"
                }
            ],
            "versionInfo": {
                "agentVersion": "1.5.0",
                "agentHash": "b197edd",
                "dockerVersion": "DockerVersion: 1.7.1"
            }
        }
    ]
}
```

您也可以在 Amazon EC2 控制台中或使用 **aws ec2 describe-instances --instance-id *instance\$1id*** 命令查找可用于监控实例的 Amazon EC2 实例 ID。

## 注册任务定义
<a name="AWSCLI_EC2_register_task_definition"></a>

您必须首先注册一个任务定义，然后才能在 Amazon ECS 集群上运行任务。任务定义是分组在一起的一系列容器。以下示例是一个使用 `nginx` 映像的简单任务定义。有关可用任务定义参数的更多信息，请参阅 [Amazon ECS 任务定义](task_definitions.md)。

```
{
    "family": "nginx-task",
    "containerDefinitions": [
        {
            "name": "nginx",
            "image": "public.ecr.aws/ecs-sample-image/amazon-ecs-sample:latest",
            "cpu": 256,
            "memory": 512,
            "essential": true,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp"
                }
            ]
        }
    ],
    "requiresCompatibilities": ["EC2"],
    "networkMode": "bridge"
}
```

上述示例 JSON 可通过两种方式传递到 AWS CLI：您可以将任务定义 JSON 保存为文件并使用 `--cli-input-json file://path_to_file.json` 选项传递它。您也可以对 JSON 中的引号进行转义并在命令行上传递 JSON 容器定义。如果您选择在命令行上传递容器定义，您的命令还需要一个 `--family` 参数，该参数用于使任务定义的多个版本保持互相关联。

将 JSON 文件用于容器定义：

```
aws ecs register-task-definition --cli-input-json file://$HOME/tasks/nginx.json
```

**register-task-definition** 将在其完成注册后返回任务定义的说明。

```
{
    "taskDefinition": {
        "taskDefinitionArn": "arn:aws:ecs:us-east-1:123456789012:task-definition/nginx-task:1",
        "family": "nginx-task",
        "revision": 1,
        "status": "ACTIVE",
        "containerDefinitions": [
            {
                "name": "nginx",
                "image": "public.ecr.aws/docker/library/nginx:latest",
                "cpu": 256,
                "memory": 512,
                "essential": true,
                "portMappings": [
                    {
                        "containerPort": 80,
                        "hostPort": 80,
                        "protocol": "tcp"
                    }
                ],
                "environment": [],
                "mountPoints": [],
                "volumesFrom": []
            }
        ],
        "volumes": [],
        "networkMode": "bridge",
        "compatibilities": [
            "EC2"
        ],
        "requiresCompatibilities": [
            "EC2"
        ]
    }
}
```

## 列出任务定义
<a name="AWSCLI_EC2_list_task_definitions"></a>

您可以随时使用 **list-task-definitions** 命令列出您的账户的任务定义。此命令的输出将显示 `family` 和 `revision` 值，您可以在调用 **create-service** 时将这些值一起使用。

```
aws ecs list-task-definitions
```

输出：

```
{
    "taskDefinitionArns": [
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/sleep360:1",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/sleep360:2",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/nginx-task:1",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/wordpress:3",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/wordpress:4",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/wordpress:5",
        "arn:aws:ec2:us-east-1:aws_account_id:task-definition/wordpress:6"
    ]
}
```

## 创建服务
<a name="AWSCLI_EC2_run_task"></a>

在为账户注册任务并启动注册到集群的容器实例后，您可以创建一个 Amazon ECS 服务，从而使用所注册的任务定义同时运行和维持所需数量的任务。在本实例中，您将 `nginx:1` 任务定义的单个实例放置在 MyCluster 集群中。

```
aws ecs create-service --cluster MyCluster --service-name nginx-service --task-definition nginx-task:1 --desired-count 1
```

输出：

```
{
    "service": {
        "serviceArn": "arn:aws:ecs:us-east-1:aws_account_id:service/MyCluster/nginx-service",
        "serviceName": "nginx-service",
        "clusterArn": "arn:aws:ecs:us-east-1:aws_account_id:cluster/MyCluster",
        "taskDefinition": "arn:aws:ecs:us-east-1:aws_account_id:task-definition/nginx-task:1",
        "desiredCount": 1,
        "runningCount": 0,
        "pendingCount": 0,
        "launchType": "EC2",
        "status": "ACTIVE",
        "createdAt": "2025-01-13T10:45:00.000Z"
    }
}
```

## 列出服务
<a name="AWSCLI_EC2_list_tasks"></a>

列出您的集群的服务。您应看到您在上一部分中创建的服务。您可以记下此命令返回的服务 ID 或完整 ARN，并在稍后将其用于描述服务。

```
aws ecs list-services --cluster MyCluster
```

输出：

```
{
    "taskArns": [
        "arn:aws:ecs:us-east-1:aws_account_id:task/task_ID"
    ]
}
```

## 描述服务
<a name="AWSCLI_EC2_describe_service"></a>

使用以下命令描述服务，以获取有关该服务的更多信息。

```
aws ecs describe-services --cluster MyCluster --services nginx-service
```

输出：

```
{
    "services": [
        {
            "serviceArn": "arn:aws:ecs:us-east-1:aws_account_id:service/MyCluster/nginx-service",
            "serviceName": "nginx-service",
            "clusterArn": "arn:aws:ecs:us-east-1:aws_account_id:cluster/MyCluster",
            "taskDefinition": "arn:aws:ecs:us-east-1:aws_account_id:task-definition/nginx-task:1",
            "desiredCount": 1,
            "runningCount": 1,
            "pendingCount": 0,
            "launchType": "EC2",
            "status": "ACTIVE",
            "createdAt": "2025-01-13T10:45:00.000Z",
            "events": [
                {
                    "id": "abcd1234-5678-90ab-cdef-1234567890ab",
                    "createdAt": "2025-01-13T10:45:30.000Z",
                    "message": "(service nginx-service) has started 1 tasks: (task abcd1234-5678-90ab-cdef-1234567890ab)."
                }
            ]
        }
    ]
}
```

## 描述正在运行的任务
<a name="AWSCLI_EC2_describe_task"></a>

在描述服务后，运行以下命令可获取有关该服务中正在运行的任务的更多信息。

```
aws ecs list-tasks --cluster MyCluster --service-name nginx-service
```

 输出：

```
{
    "tasks": [
        {
            "taskArn": "arn:aws:ecs:us-east-1:aws_account_id:task/MyCluster/abcd1234-5678-90ab-cdef-1234567890ab",
            "clusterArn": "arn:aws:ecs:us-east-1:aws_account_id:cluster/MyCluster",
            "taskDefinitionArn": "arn:aws:ecs:us-east-1:aws_account_id:task-definition/nginx-task:1",
            "containerInstanceArn": "arn:aws:ecs:us-east-1:aws_account_id:container-instance/MyCluster/abcd1234-5678-90ab-cdef-1234567890ab",
            "lastStatus": "RUNNING",
            "desiredStatus": "RUNNING",
            "containers": [
                {
                    "containerArn": "arn:aws:ecs:us-east-1:aws_account_id:container/MyCluster/abcd1234-5678-90ab-cdef-1234567890ab/abcd1234-5678-90ab-cdef-1234567890ab",
                    "taskArn": "arn:aws:ecs:us-east-1:aws_account_id:task/MyCluster/abcd1234-5678-90ab-cdef-1234567890ab",
                    "name": "nginx",
                    "lastStatus": "RUNNING",
                    "networkBindings": [
                        {
                            "bindIP": "0.0.0.0",
                            "containerPort": 80,
                            "hostPort": 80,
                            "protocol": "tcp"
                        }
                    ]
                }
            ],
            "createdAt": "2025-01-13T10:45:00.000Z",
            "startedAt": "2025-01-13T10:45:30.000Z"
        }
    ]
}
```

## 测试 Web 服务器
<a name="AWSCLI_EC2_test_web_server"></a>

**测试 Web 服务器**

1. 通过运行以下命令，检索容器实例的公有 IP 地址。

   ```
   aws ec2 describe-instances --instance-ids i-abcd1234 --query 'Reservations[0].Instances[0].PublicIpAddress' --output text
   ```

   输出：

   ```
   203.0.113.25
   ```

1. 检索到 IP 地址后，使用该 IP 地址运行以下 `curl` 命令。

   ```
   curl http://203.0.113.25
   ```

   输出：

   ```
   <!DOCTYPE html>
   <html>
   <head>
   <title>Welcome to nginx!</title>
   ...
   </head>
   <body>
   <h1>Welcome to nginx!</h1>
   <p>If you can see this page, the nginx web server is successfully installed and working.</p>
   ...
   </body>
   </html>
   ```

   nginx 欢迎页面会确认您的服务已成功运行并且可以从互联网访问。

## 清理 资源
<a name="AWSCLI_EC2_clean_up_resources"></a>

要避免产生费用，请清理您在本教程中创建的资源。

**清理资源**

1. 更新服务，使需要的任务数降为零，然后删除服务。

   ```
   aws ecs update-service --cluster MyCluster --service nginx-service --desired-count 0
   {
       "service": {
           "serviceArn": "arn:aws:ecs:us-east-1:123456789012:service/MyCluster/nginx-service",
           "serviceName": "nginx-service",
           "desiredCount": 0,
           "runningCount": 1,
           "pendingCount": 0,
           "status": "ACTIVE"
       }
   }
   ```

1. 等待正在运行的任务停止，然后再删除服务。

   ```
   aws ecs delete-service --cluster MyCluster --service nginx-service
   {
       "service": {
           "serviceArn": "arn:aws:ecs:us-east-1:123456789012:service/MyCluster/nginx-service",
           "serviceName": "nginx-service",
           "status": "DRAINING"
       }
   }
   ```

1. 终止您创建的容器实例。

   ```
   aws ec2 terminate-instances --instance-ids i-abcd1234
   {
       "TerminatingInstances": [
           {
               "InstanceId": "i-abcd1234",
               "CurrentState": {
                   "Code": 32,
                   "Name": "shutting-down"
               },
               "PreviousState": {
                   "Code": 16,
                   "Name": "running"
               }
           }
       ]
   }
   ```

1. 清理您创建的安全组和密钥对。

   ```
   aws ec2 delete-security-group --group-id sg-abcd1234
   aws ec2 delete-key-pair --key-name ecs-tutorial-key
   rm ecs-tutorial-key.pem
   ```

1. 删除 Amazon ECS 集群。

   ```
   aws ecs delete-cluster --cluster MyCluster
   {
       "cluster": {
           "clusterArn": "arn:aws:ecs:us-east-1:123456789012:cluster/MyCluster",
           "clusterName": "MyCluster",
           "status": "INACTIVE"
       }
   }
   ```

# 配置 Amazon ECS 以侦听 CloudWatch Events 事件
<a name="ecs_cwet"></a>

了解如何设置一个简单的 Lambda 函数，用于侦听任务事件并将其写出到 CloudWatch Logs 日志流。

## 先决条件：设置测试集群
<a name="cwet_step_1"></a>

如果您没有要从中捕获事件的正在运行的集群，请执行 [为 Fargate 工作负载创建 Amazon ECS 集群](create-cluster-console-v2.md) 中的步骤来创建一个集群。在本教程结束时，您可在此集群上运行任务来测试您是否已正确配置 Lambda 函数。

## 步骤 1：创建 Lambda 函数
<a name="cwet_step_2"></a>

在此过程中，您将创建一个简单的 Lambda 函数来充当 Amazon ECS 事件流消息的目标。

1. 通过 [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 打开 AWS Lambda 控制台。

1. 选择**创建函数**。

1. 在 **Author from scratch** 屏幕上，执行以下操作：

   1. 对于**名称**，输入一个值。

   1. 对于 **Runtime**（运行时），选择 Python 的版本，例如 **Python 3.9**。

   1. 对于**角色**，选择**创建具有基本 Lambda 权限的新角色**。

1. 选择**创建函数**。

1. 在 **Function code** 部分中，编辑示例代码以匹配以下示例：

   ```
   import json
   
   def lambda_handler(event, context):
       if event["source"] != "aws.ecs":
          raise ValueError("Function only supports input from events with a source type of: aws.ecs")
          
       print('Here is the event:')
       print(json.dumps(event))
   ```

   这是一个简单的 Python 3.9 函数，可输出由 Amazon ECS 发送的事件。如果一切配置正确，则在本教程结束时，您将在与此 Lambda 函数关联的 CloudWatch Logs 日志流中看到事件详细信息。

1. 选择**保存**。

## 步骤 2：注册事件规则
<a name="cwet_step_3"></a>

 接下来，您创建一个 CloudWatch Events 事件规则，该规则可捕获来自 Amazon ECS 集群的任务事件。该规则捕获来自定义该规则的账户中的所有集群的所有事件。任务消息本身包含有关事件源的信息（包括事件源所在的集群），可使用这些信息以编程方式对事件进行筛选和排序。

**注意**  
在使用 AWS 管理控制台 创建事件规则时，控制台会自动添加所需的 IAM 权限以便向 CloudWatch Events 授予调用 Lambda 函数所需的权限。如果您使用 AWS CLI 创建事件规则，则需要明确授予此权限。有关更多信息，请参阅《Amazon EventBridge User Guide》**中的 [Events in Amazon EventBridge](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-events.html) 和 [Amazon EventBridge event patterns](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html)。

**将事件路由到 Lambda 函数**

1. 通过 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 打开 CloudWatch 控制台。

1. 在导航窗格上，依次选择**事件**、**规则**、**创建规则**。

1. 对于**事件源**，选择 **ECS** 作为事件源。预设情况下，规则将应用于您的所有 Amazon ECS 组的所有 Amazon ECS 事件。或者，您可以选择特定的事件或特定的 Amazon ECS 组。

1. 对于**目标**，选择**添加目标**，对于**目标类型**，选择 **Lambda 函数**，然后选择您的 Lambda 函数。

1. 选择 **Configure details（配置详细信息）**。

1. 对于 **Rule definition**，键入规则的名称和说明，然后选择 **Create rule**。

## 步骤 3：创建任务定义
<a name="cwet_step_task-def"></a>

创建任务定义。

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 在导航窗格中，选择 **Task Definitions**。

1. 选择 **Create new Task Definition**（创建新的任务定义）、**Create new revision with JSON**（使用 JSON 创建新的修订）。

1. 将以下示例任务定义复制并粘贴到框中，然后选择**保存**。

   ```
   {
      "containerDefinitions": [ 
         { 
            "command": [
               "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' >  /usr/local/apache2/htdocs/index.html && httpd-foreground\""
            ],
            "entryPoint": [
               "sh",
               "-c"
            ],
            "essential": true,
            "image": "public.ecr.aws/docker/library/httpd:2.4",
            "logConfiguration": { 
               "logDriver": "awslogs",
               "options": { 
                  "awslogs-group" : "/ecs/fargate-task-definition",
                  "awslogs-region": "us-east-1",
                  "awslogs-stream-prefix": "ecs"
               }
            },
            "name": "sample-fargate-app",
            "portMappings": [ 
               { 
                  "containerPort": 80,
                  "hostPort": 80,
                  "protocol": "tcp"
               }
            ]
         }
      ],
      "cpu": "256",
      "executionRoleArn": "arn:aws:iam::012345678910:role/ecsTaskExecutionRole",
      "family": "fargate-task-definition",
      "memory": "512",
      "networkMode": "awsvpc",
      "runtimePlatform": {
           "operatingSystemFamily": "LINUX"
       },
      "requiresCompatibilities": [ 
          "FARGATE" 
       ]
   }
   ```

1. 选择**创建**。

## 步骤 4：测试您的规则
<a name="cwet_step_4"></a>

 最后，您创建一个 CloudWatch Events 事件规则，该规则可捕获来自 Amazon ECS 集群的任务事件。该规则捕获来自定义该规则的账户中的所有集群的所有事件。任务消息本身包含有关事件源的信息（包括事件源所在的集群），可使用这些信息以编程方式对事件进行筛选和排序。

**测试您的规则**

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 选择 **Task definitions**（任务定义）。

1. 选择 **console-sample-app-static**，然后选择 **Deploy**（部署），**Run new task**（运行新任务）。

1. 对于 **Cluster**（集群），选择默认值，然后选择 **Deploy**（部署）。

1. 通过 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 打开 CloudWatch 控制台。

1. 在导航窗格中，选择**日志**，然后选择 Lambda 函数的日志组（例如，**/aws/lambda/***my-function*）。

1. 选择日志流以查看事件数据。

# 针对 Amazon ECS 任务停止事件发送 Amazon Simple Notification Service 警报
<a name="ecs_cwet2"></a>

配置一个 Amazon EventBridge 事件规则，用于仅捕获任务因某个主要容器终止而停止的任务事件。事件仅将具有特定 `stoppedReason` 属性的任务事件发送到指定的 Amazon SNS 主题。

## 先决条件：设置测试集群
<a name="cwet2_step_1"></a>

 如果您没有要从中捕获事件的正在运行的集群，请执行[使用 AWS Fargate 上的 Linux 容器开始使用控制台](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/getting-started-fargate.html#get-started-fargate-cluster)中的步骤来创建一个集群。在本教程结束时，您在此集群上运行一个任务来测试您是否已正确配置 Amazon SNS 主题和 EventBridge 规则。

## 先决条件：为 Amazon SNS 配置权限
<a name="cwet2_step_1a"></a>

如要允许 EventBridge 发布到 Amazon SNS 主题，请使用 aws sns get-topic-attributes 和 aws sns set-topic-attributes 命令。

有关如何添加权限的信息，请参阅**《Amazon Simple Notification Service 开发人员指南》中的 [Amazon SNS 权限](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-use-resource-based.html#eb-sns-permissions)。

添加以下权限：

```
{
  "Sid": "PublishEventsToMyTopic",
  "Effect": "Allow",
  "Principal": {
     "Service": "events.amazonaws.com"
  },
  "Action": "sns: Publish",
  "Resource": "arn:aws:sns:region:account-id:TaskStoppedAlert",
}
```

## 步骤 1：创建并订阅 Amazon SNS 主题
<a name="cwet2_step_2"></a>

 在本教程中，您配置一个 Amazon SNS 主题来充当新事件规则的事件目标。

有关如何创建和订阅 Amazon SNS 主题的信息，请参阅《Amazon Simple Notification Service 开发人员指南》**中的 [Amazon SNS 入门](https://docs.aws.amazon.com/sns/latest/dg/sns-getting-started.html#step-create-queue)，并使用下表确定选择哪些选项。


| Option | 值 | 
| --- | --- | 
|  Type  | 标准 | 
| 名称 |  TaskStoppedAlert  | 
| 协议 | 电子邮件 | 
| 端点 |  您当前有权访问的电子邮件地址  | 

## 步骤 2：注册事件规则
<a name="cwet2_step_3"></a>

 接下来，您注册一个事件规则，此规则仅捕获具有已停止容器的任务的任务已停止事件。

有关如何创建和订阅 Amazon SNS 主题的信息，请参阅《Amazon EventBridge 用户指南》**中的[在 Amazon EventBridge 中创建规则](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-get-started.html)，并使用以下表格确定要选择哪些选项。


| Option | 值 | 
| --- | --- | 
|  规则类型  |  具有事件模式的规则  | 
| 事件源 | AWS 事件或 EventBridge 合作伙伴事件 | 
| 事件模式 |  自定义模式（JSON 编辑器）  | 
| 事件模式 |  <pre>{<br />   "source":[<br />      "aws.ecs"<br />   ],<br />   "detail-type":[<br />      "ECS Task State Change"<br />   ],<br />   "detail":{<br />      "lastStatus":[<br />         "STOPPED"<br />      ],<br />      "stoppedReason":[<br />         "Essential container in task exited"<br />      ]<br />   }<br />}</pre> | 
| Target type |  AWS 服务  | 
| Target | SNS 主题 | 
| Topic |  TaskStoppedAlert（您在步骤 1 中创建的主题）  | 

## 步骤 3：测试您的规则
<a name="cwet2_step_4"></a>

通过运行在启动后不久退出的任务来验证规则是否有效。如果您的事件规则配置正确，您将在几分钟内收到包含事件文本的电子邮件。如果您具有可满足规则要求的现有任务定义，请使用该定义运行任务。如果您不具有该定义，以下步骤将引导您注册 Fargate 任务定义并运行它。

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 在导航窗格中，选择 **Task definitions**（任务定义）。

1. 选择 **Create new task definition**（创建新的任务定义）、**Create new task definition with JSON**（使用 JSON 创建新的任务定义）。

1. 在 JSON 编辑器框中，编辑您的 JSON 文件，将以下内容复制到编辑器中。

   ```
   {
      "containerDefinitions":[
         {
            "command":[
               "sh",
               "-c",
               "sleep 5"
            ],
            "essential":true,
            "image":"public.ecr.aws/amazonlinux/amazonlinux:latest",
            "name":"test-sleep"
         }
      ],
      "cpu":"256",
      "executionRoleArn":"arn:aws:iam::012345678910:role/ecsTaskExecutionRole",
      "family":"fargate-task-definition",
      "memory":"512",
      "networkMode":"awsvpc",
      "requiresCompatibilities":[
         "FARGATE"
      ]
   }
   ```

1. 选择**创建**。

**从控制台运行任务**

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 在**集群**页面上，选择您在先决条件中创建的集群。

1. 从**任务**选项卡上，选择**运行新任务**。

1. 对于**应用程序类型**，选择**任务**。

1. 对于**任务定义**，选择 **fargate-task-definition**。

1. 对于 **Desired tasks**（预期任务），请输入要启动的任务数量。

1. 选择**创建**。

# 连接多行或堆栈跟踪 Amazon ECS 日志消息
<a name="firelens-concatanate-multiline"></a>

从 AWS for Fluent Bit 版本 2.22.0 开始，包含多行筛选条件。多行筛选条件有助于连接原属于一个上下文但被分割为多个记录或日志行的日志消息。有关多行筛选条件的更多信息，请参阅 [Fluent Bit 文档](https://docs.fluentbit.io/manual/pipeline/filters/multiline-stacktrace)。

拆分日志消息的常见示例有：
+ 堆栈跟踪。
+ 在多行上打印日志的应用程序。
+ 因为比指定的运行时间最大缓冲区大小长而拆分的日志消息。您可以按照 GitHub 中的以下示例来连接被容器运行时拆分的日志消息：[FireLens Example: Concatenate Partial/Split Container Logs](https://github.com/aws-samples/amazon-ecs-firelens-examples/tree/mainline/examples/fluent-bit/filter-multiline-partial-message-mode)（FireLens 示例：连接部分/拆分的容器日志）。

## 所需的 IAM 权限
<a name="iam-permissions"></a>

您拥有使用容器代理从 Amazon ECR 中提取容器映像以及使用容器将日志路由到 CloudWatch Logs 的所需 IAM 权限。

对于这些权限，您还必须具有以下角色：
+ 任务 IAM 角色。
+ 任务执行 IAM 角色。

您需要以下权限：
+ `logs:CreateLogStream`
+ `logs:CreateLogGroup`
+ `logs:PutLogEvents`

## 确定何时使用多行日志设置
<a name="determine-filter"></a>

以下是您将在使用默认日志设置的 CloudWatch Logs 控制台中看到的示例日志片段。您可以查看以 `log` 开头的行，以确定是否需要多行筛选条件。当上下文相同时，您可以使用多行日志设置，在本例中，上下文为“com.myproject.model.MyProject”。

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "source=": "stdout", "log": ": "     at com.myproject.modele.(MyProject.badMethod.java:22)",
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log": ": "     at com.myproject.model.MyProject.badMethod(MyProject.java:22)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE",
      "ecs_task_definition": "firelense-example-multiline:3"
     }
```

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "stdout", "log": ": "     at com.myproject.modele.(MyProject.oneMoreMethod.java:18)",
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log": ": "     at com.myproject.model.MyProject.oneMoreMethod(MyProject.java:18)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE,
      "ecs_task_definition": "firelense-example-multiline:3"
     }
```

使用多行日志设置后，输出将与以下示例类似。

```
2022-09-20T15:47:56:595-05-00                           {"container_id": "82ba37cada1d44d389b03e78caf74faa-EXAMPLE", "container_name": "example-app", "stdout",...
    {
      "container_id":  "82ba37cada1d44d389b03e78caf74faa-EXAMPLE",
      "container_name: ": "example-app",
      "source": "stdout",
      "log:    "September 20, 2022 06:41:48 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!\n    
    at com.myproject.module.MyProject.badMethod(MyProject.java:22)\n    at   
    at com.myproject.model.MyProject.oneMoreMethod(MyProject.java:18) com.myproject.module.MyProject.main(MyProject.java:6)",
      "ecs_cluster": "default",
      "ecs_task_arn": "arn:aws:region:123456789012:task/default/b23c940d29ed4714971cba72cEXAMPLE",
      "ecs_task_definition": "firelense-example-multiline:2"
     }
```

## 解析和连接选项
<a name="parse-multiline-log"></a>

要解析日志并连接因换行符而被拆分的行，您可以使用这两个选项之一。
+ 使用自己的解析器文件，该文件中包含用于解析和连接属于同一消息的行的规则。
+ 使用 Fluent Bit 内置解析器。有关 Fluent Bit 内置解析器支持的语言的列表，请参阅 [Fluent Bit 文档。](https://docs.fluentbit.io/manual/pipeline/filters/multiline-stacktrace)

以下教程将引导您完成每个使用案例的步骤。这些步骤向您展示了如何连接多行并将日志发送到 Amazon CloudWatch。您可以为日志指定其他目标。

### 示例：使用您创建的解析器
<a name="customer-parser"></a>

在本示例中，您将完成以下步骤：

1. 为 Fluent Bit 容器生成并上传映像。

1. 为运行、失败和生成多行堆栈跟踪的演示多行应用程序生成并上传映像。

1. 创建任务定义并运行任务。

1. 查看日志以验证跨越多行的消息是否连接起来。

**为 Fluent Bit 容器生成并上传映像**

此映像将包括您在其中指定正则表达式的解析器文件和引用解析器文件的配置文件。

1. 使用名称 `FluentBitDockerImage` 创建文件夹。

1. 在文件夹内，创建解析器文件，其中包含用于解析日志和连接属于同一消息的行的规则。

   1. 将以下内容粘贴到解析器文件中：

      ```
      [MULTILINE_PARSER]
          name          multiline-regex-test
          type          regex
          flush_timeout 1000
          #
          # Regex rules for multiline parsing
          # ---------------------------------
          #
          # configuration hints:
          #
          #  - first state always has the name: start_state
          #  - every field in the rule must be inside double quotes
          #
          # rules |   state name  | regex pattern                  | next state
          # ------|---------------|--------------------------------------------
          rule      "start_state"   "/(Dec \d+ \d+\:\d+\:\d+)(.*)/"  "cont"
          rule      "cont"          "/^\s+at.*/"                     "cont"
      ```

      自定义正则表达式模式时，我们建议您使用正则表达式编辑器来测试表达式。

   1. 将该文件保存为 `parsers_multiline.conf`。

1. 在 `FluentBitDockerImage` 文件夹中，创建引用您在上一步中创建的解析器文件的自定义配置文件。

   有关自定义配置文件的更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[指定自定义配置文件](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/firelens-taskdef.html#firelens-taskdef-customconfig) 

   1. 将以下内容粘贴到该文件中：

      ```
      [SERVICE]
          flush                 1
          log_level             info
          parsers_file          /parsers_multiline.conf
          
      [FILTER]
          name                  multiline
          match                 *
          multiline.key_content log
          multiline.parser      multiline-regex-test
      ```
**注意**  
您必须使用解析器的绝对路径。

   1. 将该文件保存为 `extra.conf`。

1. 在 `FluentBitDockerImage` 文件夹中，使用 Fluent Bit 映像以及您创建的解析器和配置文件创建 Dockerfile。

   1. 将以下内容粘贴到该文件中：

      ```
      FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
      
      ADD parsers_multiline.conf /parsers_multiline.conf
      ADD extra.conf /extra.conf
      ```

   1. 将该文件保存为 `Dockerfile`。

1. 使用 Dockerfile，构建一个包含解析器和自定义配置文件的自定义 Fluent Bit 映像。
**注意**  
您可以将解析器文件和配置文件放置在 Docker 映像中的任何位置，但 `/fluent-bit/etc/fluent-bit.conf` 除外，因为 FireLens 使用这个文件路径。

   1. 生成镜像：`docker build -t fluent-bit-multiline-image.`

      其中：`fluent-bit-multiline-image` 是此示例中映像的名称。

   1. 验证是否已正确创建映像：`docker images —filter reference=fluent-bit-multiline-image`

      如果成功，输出将显示该映像和 `latest` 标签。

1. 将自定义 Fluent Bit 映像上传到 Amazon Elastic Container Registry。

   1. 创建用于存储映像的 Amazon ECR 存储库：`aws ecr create-repository --repository-name fluent-bit-multiline-repo --region us-east-1`

      其中：`fluent-bit-multiline-repo` 是存储库的名称，`us-east-1` 是此示例中的区域。

      输出为您提供了新存储库的详细信息。

   1. 使用上一个输出中的 `repositoryUri` 值标记您的映像：`docker tag fluent-bit-multiline-image repositoryUri`

      示例：`docker tag fluent-bit-multiline-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

   1. 运行 Docker 镜像以验证它是否正确运行：`docker images —filter reference=repositoryUri`

      在输出中，存储库名称从 fluent-bit-multiline-repo 更改为 `repositoryUri`。

   1. 通过运行 `aws ecr get-login-password` 命令并指定您要对其进行身份验证的注册表 ID 对 Amazon ECR 进行身份验证：`aws ecr get-login-password | docker login --username AWS --password-stdin registry ID.dkr.ecr.region.amazonaws.com`

      示例：`ecr get-login-password | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com`

      此时会显示成功登录消息。

   1. 将映像推送到 Amazon ECR：`docker push registry ID.dkr.ecr.region.amazonaws.com/repository name`

      示例：`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

**为演示多行应用程序生成并上传映像**

此映像将包括运行应用程序的 Python 脚本文件和示例日志文件。

运行任务时，应用程序会模拟运行，然后失败并创建堆栈跟踪。

1. 创建名为 `multiline-app` 的文件夹：`mkdir multiline-app`

1. 创建 Python 脚本文件。

   1. 在 `multiline-app` 文件夹中，创建一个文件并将其命名为 `main.py`。

   1. 将以下内容粘贴到该文件中：

      ```
      import os
      import time
      file1 = open('/test.log', 'r')
      Lines = file1.readlines()
       
      count = 0
      
      for i in range(10):
          print("app running normally...")
          time.sleep(1)
      
      # Strips the newline character
      for line in Lines:
          count += 1
          print(line.rstrip())
      print(count)
      print("app terminated.")
      ```

   1. 保存 `main.py` 文件。

1. 创建示例日志文件。

   1. 在 `multiline-app` 文件夹中，创建一个文件并将其命名为 `test.log`。

   1. 将以下内容粘贴到该文件中：

      ```
      single line...
      Dec 14 06:41:08 Exception in thread "main" java.lang.RuntimeException: Something has gone wrong, aborting!
          at com.myproject.module.MyProject.badMethod(MyProject.java:22)
          at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)
          at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)
          at com.myproject.module.MyProject.someMethod(MyProject.java:10)
          at com.myproject.module.MyProject.main(MyProject.java:6)
      another line...
      ```

   1. 保存 `test.log` 文件。

1. 在 `multiline-app` 文件夹中，创建 Dockerfile。

   1. 将以下内容粘贴到该文件中：

      ```
      FROM public.ecr.aws/amazonlinux/amazonlinux:latest
      ADD test.log /test.log
      
      RUN yum upgrade -y && yum install -y python3
      
      WORKDIR /usr/local/bin
      
      COPY main.py .
      
      CMD ["python3", "main.py"]
      ```

   1. 保存 `Dockerfile` 文件。

1. 使用 Dockerfile 生成映像。

   1. 生成映像：`docker build -t multiline-app-image `

      其中：`multiline-app-image` 是此示例中映像的名称。

   1. 验证是否已正确创建映像：`docker images —filter reference=multiline-app-image`

      如果成功，输出将显示该映像和 `latest` 标签。

1. 将映像上载到 Amazon Elastic Container 注册表。

   1. 创建用于存储映像的 Amazon ECR 存储库：`aws ecr create-repository --repository-name multiline-app-repo --region us-east-1`

      其中：`multiline-app-repo` 是存储库的名称，`us-east-1` 是此示例中的区域。

      输出为您提供了新存储库的详细信息。记下 `repositoryUri` 值，您将需要在后续步骤中使用该值。

   1. 使用上一个输出中的 `repositoryUri` 值标记您的映像：`docker tag multiline-app-image repositoryUri`

      示例：`docker tag multiline-app-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

   1. 运行 Docker 映像以验证它是否正确运行：`docker images —filter reference=repositoryUri`

      在输出中，存储库名称从 `multiline-app-repo` 更改为 `repositoryUri` 值。

   1. 将映像推送到 Amazon ECR：`docker push aws_account_id.dkr.ecr.region.amazonaws.com/repository name`

      示例：`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

**创建任务定义并运行任务**

1. 使用文件名 `multiline-task-definition.json` 创建任务定义文件。

1. 将以下内容粘贴到 `multiline-task-definition.json` 文件中：

   ```
   {
       "family": "firelens-example-multiline",
       "taskRoleArn": "task role ARN,
       "executionRoleArn": "execution role ARN",
       "containerDefinitions": [
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-image:latest",
               "name": "log_router",
               "firelensConfiguration": {
                   "type": "fluentbit",
                   "options": {
                       "config-file-type": "file",
                       "config-file-value": "/extra.conf"
                   }
               },
               "memoryReservation": 50
           },
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/multiline-app-image:latest",
               "name": "app",
               "logConfiguration": {
                   "logDriver": "awsfirelens",
                   "options": {
                       "Name": "cloudwatch_logs",
                       "region": "us-east-1",
                       "log_group_name": "multiline-test/application",
                       "auto_create_group": "true",
                       "log_stream_prefix": "multiline-"
                   }
               },
               "memoryReservation": 100
           }
       ],
       "requiresCompatibilities": ["FARGATE"],
       "networkMode": "awsvpc",
       "cpu": "256",
       "memory": "512"
   }
   ```

   替换 `multiline-task-definition.json` 任务定义中的以下内容：

   1. `task role ARN`

      要查找任务角色 ARN，请转到 IAM 控制台。选择 **Roles**（角色），然后查找您创建的 `ecs-task-role-for-firelens` 任务角色。选择角色，然后复制 **Summary**（摘要）部分中显示的 **ARN**。

   1. `execution role ARN`

      要查找执行角色 ARN，请转到 IAM 控制台。选择 **Roles**（角色），然后查找 `ecsTaskExecutionRole` 角色。选择角色，然后复制 **Summary**（摘要）部分中显示的 **ARN**。

   1. `aws_account_id`

      要查找您的 `aws_account_id`，登录 AWS 管理控制台。在右上角选择您的用户名，然后复制您的账户 ID。

   1. `us-east-1`

      如有必要，请更换区域。

1. 注册任务定义文件：`aws ecs register-task-definition --cli-input-json file://multiline-task-definition.json --region region`

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 在导航窗格中，选择 **Task Definitions**（任务定义），然后选择 `firelens-example-multiline` 系列，因为我们已在上面任务定义的第一行中将任务定义注册到这个系列。

1. 选择最新版本。

1. 选择**部署**、**运行任务**。

1. 在**运行任务**页面上，对于**集群**，请选择集群，然后在**联网**下的**子网**中，选择任务的可用子网。

1. 选择**创建**。

**验证 Amazon CloudWatch 中的多行日志消息是否已连接**

1. 通过 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 打开 CloudWatch 控制台。

1. 从导航窗格中，展开 **Logs**（日志）并选择 **Log groups**（日志组）。

1. 选择 `multiline-test/applicatio` 日志组。

1. 选择日志。查看消息。与解析器文件中的规则匹配的行将串联起来，并显示为单条消息。

   以下日志片段显示了在单个 Java 堆栈跟踪事件中连接的行：

   ```
   {
       "container_id": "xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "log": "Dec 14 06:41:08 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!\n    at com.myproject.module.MyProject.badMethod(MyProject.java:22)\n    at com.myproject.module.MyProject.oneMoreMethod(MyProject.java:18)\n    at com.myproject.module.MyProject.anotherMethod(MyProject.java:14)\n    at com.myproject.module.MyProject.someMethod(MyProject.java:10)\n    at com.myproject.module.MyProject.main(MyProject.java:6)",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:2"
   }
   ```

   以下日志代码段显示了如果您运行的是未配置为连接多行日志消息的 Amazon ECS 容器，则如何只用一行显示相同的消息。

   ```
   {
       "log": "Dec 14 06:41:08 Exception in thread \"main\" java.lang.RuntimeException: Something has gone wrong, aborting!",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:3"
   }
   ```

### 示例：使用 Fluent Bit 内置解析器
<a name="fluent-bit-parser"></a>

在本示例中，您将完成以下步骤：

1. 为 Fluent Bit 容器生成并上传映像。

1. 为运行、失败和生成多行堆栈跟踪的演示多行应用程序生成并上传映像。

1. 创建任务定义并运行任务。

1. 查看日志以验证跨越多行的消息是否连接起来。

**为 Fluent Bit 容器生成并上传映像**

此映像将包含一个引用 Fluent Bit 解析器的配置文件。

1. 使用名称 `FluentBitDockerImage` 创建文件夹。

1. 在 `FluentBitDockerImage` 文件夹中，创建引用 Fluent Bit 内置解析器文件的自定义配置文件。

   有关自定义配置文件的更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[指定自定义配置文件](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/firelens-taskdef.html#firelens-taskdef-customconfig) 

   1. 将以下内容粘贴到该文件中：

      ```
      [FILTER]
          name                  multiline
          match                 *
          multiline.key_content log
          multiline.parser      go
      ```

   1. 将该文件保存为 `extra.conf`。

1. 在 `FluentBitDockerImage` 文件夹中，使用 Fluent Bit 映像以及您创建的解析器和配置文件创建 Dockerfile。

   1. 将以下内容粘贴到该文件中：

      ```
      FROM public.ecr.aws/aws-observability/aws-for-fluent-bit:latest
      ADD extra.conf /extra.conf
      ```

   1. 将该文件保存为 `Dockerfile`。

1. 使用 Dockerfile，构建一个包含自定义配置文件的自定义 Fluent Bit 映像。
**注意**  
您可以将配置文件放置在 Docker 映像中的任何位置，但 `/fluent-bit/etc/fluent-bit.conf` 除外，因为 FireLens 使用这个文件路径。

   1. 生成映像：`docker build -t fluent-bit-multiline-image.`

      其中：`fluent-bit-multiline-image` 是此示例中映像的名称。

   1. 验证是否已正确创建映像：`docker images —filter reference=fluent-bit-multiline-image`

      如果成功，输出将显示该映像和 `latest` 标签。

1. 将自定义 Fluent Bit 映像上传到 Amazon Elastic Container Registry。

   1. 创建用于存储映像的 Amazon ECR 存储库：`aws ecr create-repository --repository-name fluent-bit-multiline-repo --region us-east-1`

      其中：`fluent-bit-multiline-repo` 是存储库的名称，`us-east-1` 是此示例中的区域。

      输出为您提供了新存储库的详细信息。

   1. 使用上一个输出中的 `repositoryUri` 值标记您的映像：`docker tag fluent-bit-multiline-image repositoryUri`

      示例：`docker tag fluent-bit-multiline-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

   1. 运行 Docker 镜像以验证它是否正确运行：`docker images —filter reference=repositoryUri`

      在输出中，存储库名称从 fluent-bit-multiline-repo 更改为 `repositoryUri`。

   1. 通过运行 `aws ecr get-login-password` 命令并指定您要对其进行身份验证的注册表 ID 对 Amazon ECR 进行身份验证：`aws ecr get-login-password | docker login --username AWS --password-stdin registry ID.dkr.ecr.region.amazonaws.com`

      示例：`ecr get-login-password | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com`

      此时会显示成功登录消息。

   1. 将映像推送到 Amazon ECR：`docker push registry ID.dkr.ecr.region.amazonaws.com/repository name`

      示例：`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-repo`

**为演示多行应用程序生成并上传映像**

此映像将包括运行应用程序的 Python 脚本文件和示例日志文件。

1. 创建名为 `multiline-app` 的文件夹：`mkdir multiline-app`

1. 创建 Python 脚本文件。

   1. 在 `multiline-app` 文件夹中，创建一个文件并将其命名为 `main.py`。

   1. 将以下内容粘贴到该文件中：

      ```
      import os
      import time
      file1 = open('/test.log', 'r')
      Lines = file1.readlines()
       
      count = 0
      
      for i in range(10):
          print("app running normally...")
          time.sleep(1)
      
      # Strips the newline character
      for line in Lines:
          count += 1
          print(line.rstrip())
      print(count)
      print("app terminated.")
      ```

   1. 保存 `main.py` 文件。

1. 创建示例日志文件。

   1. 在 `multiline-app` 文件夹中，创建一个文件并将其命名为 `test.log`。

   1. 将以下内容粘贴到该文件中：

      ```
      panic: my panic
      
      goroutine 4 [running]:
      panic(0x45cb40, 0x47ad70)
        /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c
      main.main.func1(0xc420024120)
        foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1
      created by main.main
        foo.go:5 +0x58
      
      goroutine 1 [chan receive]:
      runtime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c
      runtime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e
      runtime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)
        /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4
      runtime.chanrecv1(0xc420024120, 0x0)
        /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b
      main.main()
        foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef
      runtime.main()
        /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1
      
      goroutine 2 [force gc (idle)]:
      runtime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c
      runtime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e
      runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1
      created by runtime.init.4
        /usr/local/go/src/runtime/proc.go:227 +0x35
      
      goroutine 3 [GC sweep wait]:
      runtime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)
        /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c
      runtime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)
        /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e
      runtime.bgsweep(0xc42001e150)
        /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973
      runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1
      created by runtime.gcenable
        /usr/local/go/src/runtime/mgc.go:216 +0x58
      one more line, no multiline
      ```

   1. 保存 `test.log` 文件。

1. 在 `multiline-app` 文件夹中，创建 Dockerfile。

   1. 将以下内容粘贴到该文件中：

      ```
      FROM public.ecr.aws/amazonlinux/amazonlinux:latest
      ADD test.log /test.log
      
      RUN yum upgrade -y && yum install -y python3
      
      WORKDIR /usr/local/bin
      
      COPY main.py .
      
      CMD ["python3", "main.py"]
      ```

   1. 保存 `Dockerfile` 文件。

1. 使用 Dockerfile 生成映像。

   1. 生成映像：`docker build -t multiline-app-image `

      其中：`multiline-app-image` 是此示例中映像的名称。

   1. 验证是否已正确创建映像：`docker images —filter reference=multiline-app-image`

      如果成功，输出将显示该映像和 `latest` 标签。

1. 将映像上载到 Amazon Elastic Container 注册表。

   1. 创建用于存储映像的 Amazon ECR 存储库：`aws ecr create-repository --repository-name multiline-app-repo --region us-east-1`

      其中：`multiline-app-repo` 是存储库的名称，`us-east-1` 是此示例中的区域。

      输出为您提供了新存储库的详细信息。记下 `repositoryUri` 值，您将需要在后续步骤中使用该值。

   1. 使用上一个输出中的 `repositoryUri` 值标记您的映像：`docker tag multiline-app-image repositoryUri`

      示例：`docker tag multiline-app-image xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

   1. 运行 Docker 映像以验证它是否正确运行：`docker images —filter reference=repositoryUri`

      在输出中，存储库名称从 `multiline-app-repo` 更改为 `repositoryUri` 值。

   1. 将映像推送到 Amazon ECR：`docker push aws_account_id.dkr.ecr.region.amazonaws.com/repository name`

      示例：`docker push xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/multiline-app-repo`

**创建任务定义并运行任务**

1. 使用文件名 `multiline-task-definition.json` 创建任务定义文件。

1. 将以下内容粘贴到 `multiline-task-definition.json` 文件中：

   ```
   {
       "family": "firelens-example-multiline",
       "taskRoleArn": "task role ARN,
       "executionRoleArn": "execution role ARN",
       "containerDefinitions": [
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/fluent-bit-multiline-image:latest",
               "name": "log_router",
               "firelensConfiguration": {
                   "type": "fluentbit",
                   "options": {
                       "config-file-type": "file",
                       "config-file-value": "/extra.conf"
                   }
               },
               "memoryReservation": 50
           },
           {
               "essential": true,
               "image": "aws_account_id.dkr.ecr.us-east-1.amazonaws.com/multiline-app-image:latest",
               "name": "app",
               "logConfiguration": {
                   "logDriver": "awsfirelens",
                   "options": {
                       "Name": "cloudwatch_logs",
                       "region": "us-east-1",
                       "log_group_name": "multiline-test/application",
                       "auto_create_group": "true",
                       "log_stream_prefix": "multiline-"
                   }
               },
               "memoryReservation": 100
           }
       ],
       "requiresCompatibilities": ["FARGATE"],
       "networkMode": "awsvpc",
       "cpu": "256",
       "memory": "512"
   }
   ```

   替换 `multiline-task-definition.json` 任务定义中的以下内容：

   1. `task role ARN`

      要查找任务角色 ARN，请转到 IAM 控制台。选择 **Roles**（角色），然后查找您创建的 `ecs-task-role-for-firelens` 任务角色。选择角色，然后复制 **Summary**（摘要）部分中显示的 **ARN**。

   1. `execution role ARN`

      要查找执行角色 ARN，请转到 IAM 控制台。选择 **Roles**（角色），然后查找 `ecsTaskExecutionRole` 角色。选择角色，然后复制 **Summary**（摘要）部分中显示的 **ARN**。

   1. `aws_account_id`

      要查找您的 `aws_account_id`，登录 AWS 管理控制台。在右上角选择您的用户名，然后复制您的账户 ID。

   1. `us-east-1`

      如有必要，请更换区域。

1. 注册任务定义文件：`aws ecs register-task-definition --cli-input-json file://multiline-task-definition.json --region us-east-1`

1. 在 [https://console.aws.amazon.com/ecs/v2](https://console.aws.amazon.com/ecs/v2) 打开控制台。

1. 在导航窗格中，选择 **Task Definitions**（任务定义），然后选择 `firelens-example-multiline` 系列，因为我们已在上面任务定义的第一行中将任务定义注册到这个系列。

1. 选择最新版本。

1. 选择**部署**、**运行任务**。

1. 在**运行任务**页面上，对于**集群**，请选择集群，然后在**联网**下的**子网**中，选择任务的可用子网。

1. 选择**创建**。

**验证 Amazon CloudWatch 中的多行日志消息是否已连接**

1. 通过 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 打开 CloudWatch 控制台。

1. 从导航窗格中，展开 **Logs**（日志）并选择 **Log groups**（日志组）。

1. 选择 `multiline-test/applicatio` 日志组。

1. 选择日志并查看消息。与解析器文件中的规则匹配的行将串联起来，并显示为单条消息。

   以下日志代码段显示了在单个事件中连接的 Go 堆栈跟踪：

   ```
   {
       "log": "panic: my panic\n\ngoroutine 4 [running]:\npanic(0x45cb40, 0x47ad70)\n  /usr/local/go/src/runtime/panic.go:542 +0x46c fp=0xc42003f7b8 sp=0xc42003f710 pc=0x422f7c\nmain.main.func1(0xc420024120)\n  foo.go:6 +0x39 fp=0xc42003f7d8 sp=0xc42003f7b8 pc=0x451339\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003f7e0 sp=0xc42003f7d8 pc=0x44b4d1\ncreated by main.main\n  foo.go:5 +0x58\n\ngoroutine 1 [chan receive]:\nruntime.gopark(0x4739b8, 0xc420024178, 0x46fcd7, 0xc, 0xc420028e17, 0x3)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc420053e30 sp=0xc420053e00 pc=0x42503c\nruntime.goparkunlock(0xc420024178, 0x46fcd7, 0xc, 0x1000f010040c217, 0x3)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc420053e70 sp=0xc420053e30 pc=0x42512e\nruntime.chanrecv(0xc420024120, 0x0, 0xc420053f01, 0x4512d8)\n  /usr/local/go/src/runtime/chan.go:506 +0x304 fp=0xc420053f20 sp=0xc420053e70 pc=0x4046b4\nruntime.chanrecv1(0xc420024120, 0x0)\n  /usr/local/go/src/runtime/chan.go:388 +0x2b fp=0xc420053f50 sp=0xc420053f20 pc=0x40439b\nmain.main()\n  foo.go:9 +0x6f fp=0xc420053f80 sp=0xc420053f50 pc=0x4512ef\nruntime.main()\n  /usr/local/go/src/runtime/proc.go:185 +0x20d fp=0xc420053fe0 sp=0xc420053f80 pc=0x424bad\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc420053fe8 sp=0xc420053fe0 pc=0x44b4d1\n\ngoroutine 2 [force gc (idle)]:\nruntime.gopark(0x4739b8, 0x4ad720, 0x47001e, 0xf, 0x14, 0x1)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003e768 sp=0xc42003e738 pc=0x42503c\nruntime.goparkunlock(0x4ad720, 0x47001e, 0xf, 0xc420000114, 0x1)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003e7a8 sp=0xc42003e768 pc=0x42512e\nruntime.forcegchelper()\n  /usr/local/go/src/runtime/proc.go:238 +0xcc fp=0xc42003e7e0 sp=0xc42003e7a8 pc=0x424e5c\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003e7e8 sp=0xc42003e7e0 pc=0x44b4d1\ncreated by runtime.init.4\n  /usr/local/go/src/runtime/proc.go:227 +0x35\n\ngoroutine 3 [GC sweep wait]:\nruntime.gopark(0x4739b8, 0x4ad7e0, 0x46fdd2, 0xd, 0x419914, 0x1)\n  /usr/local/go/src/runtime/proc.go:280 +0x12c fp=0xc42003ef60 sp=0xc42003ef30 pc=0x42503c\nruntime.goparkunlock(0x4ad7e0, 0x46fdd2, 0xd, 0x14, 0x1)\n  /usr/local/go/src/runtime/proc.go:286 +0x5e fp=0xc42003efa0 sp=0xc42003ef60 pc=0x42512e\nruntime.bgsweep(0xc42001e150)\n  /usr/local/go/src/runtime/mgcsweep.go:52 +0xa3 fp=0xc42003efd8 sp=0xc42003efa0 pc=0x419973\nruntime.goexit()\n  /usr/local/go/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc42003efe0 sp=0xc42003efd8 pc=0x44b4d1\ncreated by runtime.gcenable\n  /usr/local/go/src/runtime/mgc.go:216 +0x58",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:2"
   }
   ```

   以下日志代码段显示了如果您运行的是未配置为连接多行日志消息的 ECS 容器，如何显示相同的事件。日志字段包含单行。

   ```
   {
       "log": "panic: my panic",
       "container_id": "xxxxxx-xxxxxx",
       "container_name": "app",
       "source": "stdout",
       "ecs_cluster": "default",
       "ecs_task_arn": "arn:aws:ecs:us-east-1:xxxxxxxxxxxx:task/default/xxxxxx",
       "ecs_task_definition": "firelens-example-multiline:3"
   ```

**注意**  
如果您的日志转到日志文件而不是标准输出，我们建议在[结尾输入插件](https://docs.fluentbit.io/manual/pipeline/inputs/tail#multiline-support)中指定 `multiline.parser` 和 `multiline.key_content` 配置参数，而不是筛选条件。

# 在 Amazon ECS Windows 容器上部署 Fluent Bit
<a name="tutorial-deploy-fluentbit-on-windows"></a>

Fluent Bit 是一种快速灵活的日志处理器和路由器，受各种操作系统支持。它可以用来将日志路由到不同的 AWS 目的地，例如 Amazon CloudWatch Logs、Firehose Amazon S3 和 Amazon OpenSearch Service。Fluent Bit 支持常见的合作伙伴解决方案，例如 [Datadog](https://www.datadoghq.com/)、[Splunk](https://www.splunk.com/) 和自定义 HTTP 服务器。有关 Fluent Bit 的更多信息，请参阅 [https://fluentbit.io/](https://fluentbit.io/) 网站。

为了获得高可用性，大多数区域的 Amazon ECR 公开映像浏览馆和 Amazon ECR 存储库上的 Amazon ECR 都提供了 **AWS for Fluent Bit** 映像。有关更多信息，请参阅 GitHub 网站上的 [https://github.com/aws/aws-for-fluent-bit](https://github.com/aws/aws-for-fluent-bit)。

本教程介绍如何在 Amazon ECS 中运行的 Windows 实例上部署 Fluent Bit 容器，以便将 Windows 任务生成的日志流式传输到 Amazon CloudWatch 进行集中记录。

本教程使用以下方法：
+ Fluent Bit 使用进程守护程序计划策略作为服务运行。此策略可确保 Fluent Bit 的单个实例始终在集群中的容器实例上运行。
  + 使用正向输入插件侦听端口 24224。
  + 向主机公开端口 24224，这样 Docker 运行时就可以使用这个公开的端口向 Fluent Bit 发送日志。
  + 具有允许 Fluent Bit 将日志记录发送到指定目标的配置。
+ 使用 fluentd 日志记录驱动程序启动所有其他 Amazon ECS 任务容器。有关更多信息，请参阅 Docker 文档网站上的 [Fluentd 日志记录驱动程序](https://docs.docker.com/engine/logging/drivers/fluentd/)。
  + Docker 连接到主机命名空间内本地主机上的 TCP 套接字 24224。
  + Amazon ECS 代理向容器添加标签，其中包括集群名称、任务定义系列名称、任务定义修订版号、任务 ARN 和容器名称。使用 fluentd Docker 日志记录驱动程序的标签选项将相同的信息添加到日志记录中。有关更多信息，请参阅 Docker 文档网站上的[标签、labels-regex、env 和 env-regex](https://docs.docker.com/config/containers/logging/fluentd/#labels-labels-regex-env-and-env-regex)。
  + 由于 fluentd 日志记录驱动程序的 `async` 选项设置为 `true`，因此当重新启动 Fluent Bit 容器时，Docker 会缓冲日志，直到 Fluent Bit 容器重新启动。您可以通过设置 fluentd-bufffer-limit 选项来增加缓冲区限制。有关更多信息，请参阅 Docker 文档网站上的 [fluentd-buffer-limit](https://docs.docker.com/config/containers/logging/fluentd/#fluentd-buffer-limit)。

 工作流程如下：
+ Fluent Bit 容器启动并侦听向主机公开的端口 24224。
+ Fluent Bit 使用其任务定义中指定的任务 IAM 角色凭证。
+ 在同一实例上启动的其他任务使用 fluentd Docker 日志记录驱动程序连接到端口 24224 上的 Fluent Bit 容器。
+ 当应用程序容器生成日志时，Docker 运行时会标记这些记录，添加标签中指定的其他元数据，然后将它们转发到主机命名空间中的端口 24224。
+ Fluent Bit 在端口 24224 上接收日志记录，因为它向主机命名空间公开。
+ Fluent Bit 执行其内部处理并按照指定路由日志。

本教程使用默认 CloudWatch Fluent Bit 配置，该配置执行以下操作：
+ 为每个集群和任务定义系列创建新的日志组。
+ 每当启动新任务时，都会为上面生成的日志组中的每个任务容器创建新的日志流。每个流都将标有容器所属的任务 ID。
+ 在每个日志条目中添加其他元数据，包括集群名称、任务 ARN、任务容器名称、任务定义系列和任务定义修订版号。

  例如，如果 `task_1` 包含 `container_1` 和 `container_2`，`ask_2` 包含 `container_3`，则以下是 CloudWatch 日志流：
  + `/aws/ecs/windows.ecs_task_1`

    `task-out.TASK_ID.container_1`

    `task-out.TASK_ID.container_2`
  + `/aws/ecs/windows.ecs_task_2`

    `task-out.TASK_ID.container_3`

**注意**  
您可以使用双堆栈服务端点通过 IPv4 和 IPv6 从 AWS CLI、SDK 和 Amazon ECS API 与 Amazon ECS 进行交互。有关更多信息，请参阅 [使用 Amazon ECS 双堆栈端点](dual-stack-endpoint.md)。

**Topics**
+ [

## 先决条件
](#tutorial-deploy-fluentbit-on-windows-prereqs)
+ [

## 步骤 1：创建 IAM 访问角色
](#tutorial-deploy-fluentbit-on-windows-iam-access-role)
+ [

## 步骤 2：创建 Amazon ECS Windows 容器实例
](#tutorial-deploy-fluentbit-on-windows-instance)
+ [

## 步骤 3：配置 Fluent Bit
](#tutorial-deploy-fluentbit-on-windows-configure-fluentbit)
+ [

## 步骤 4：注册 Windows Fluent Bit 任务定义，该定义将日志路由到 CloudWatch
](#tutorial-deploy-fluentbit-on-windows-register-task-definition)
+ [

## 步骤 5：使用进程守护程序计划策略将 `ecs-windows-fluent-bit` 任务定义作为 Amazon ECS 服务运行
](#tutorial-deploy-fluentbit-on-windows-run-task)
+ [

## 步骤 6：注册生成日志的 Windows 任务定义
](#tutorial-deploy-fluentbit-on-windows-register-task-def-logs)
+ [

## 步骤 7：运行 `windows-app-task` 任务定义
](#tutorial-deploy-fluentbit-on-windows-run-task-fluentbit)
+ [

## 步骤 8：验证 CloudWatch 上的日志
](#tutorial-deploy-fluentbit-on-windows-verify)
+ [

## 步骤 9：清除
](#tutorial-deploy-fluentbit-on-windows-cleanup)

## 先决条件
<a name="tutorial-deploy-fluentbit-on-windows-prereqs"></a>

本教程假设以下先决条件已完成：
+ 安装并配置了最新版本的 AWS CLI。有关更多信息，请参阅 [Installing or updating to the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
+ `aws-for-fluent-bit` 容器映像可用于以下 Windows 操作系统：
  + Windows Server 2019 Core
  + Windows Server 2019 Full
  + Windows Server 2022 Core
  + Windows Server 2022 Full
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您有一个集群。在本教程中，集群名称为 **FluentBit-cluster**。
+ 您有一个带有公有子网的 VPC，将在该子网中启动 EC2 实例。您可以使用您的原定设置 VPC。您也可以使用允许 Amazon CloudWatch 端点到达子网的私有子网。有关 Amazon CloudWatch 端点的更多信息，请参阅 *AWS 一般参考* 中的 [Amazon CloudWatch 端点和配额](https://docs.aws.amazon.com/general/latest/gr/cw_region.html)。有关如何使用 Amazon VPC 向导创建 VPC 的信息，请参阅 [创建虚拟私有云](get-set-up-for-amazon-ecs.md#create-a-vpc)。

## 步骤 1：创建 IAM 访问角色
<a name="tutorial-deploy-fluentbit-on-windows-iam-access-role"></a>

创建 Amazon ECS IAM 角色。

1.  创建名为“ecsInstanceRol”的 Amazon ECS 容器实例角色。有关更多信息，请参阅 [Amazon ECS 容器实例 IAM 角色](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html)。

1. 为名为 `fluentTaskRole` 的 Fluent Bit 任务创建一个 IAM 角色。有关更多信息，请参阅 [Amazon ECS 任务 IAM 角色](task-iam-roles.md)。

    在此 IAM 角色中被授予的 IAM 权限由任务容器承担。为了允许 Fluent Bit 向 CloudWatch 发送日志，您需要为任务 IAM 角色附加以下权限。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
       {
           "Effect": "Allow",
           "Action": [
               "logs:CreateLogStream",
               "logs:CreateLogGroup",
               "logs:DescribeLogStreams",
               "logs:PutLogEvents"
           ],
           "Resource": "*"
       }
       ]
   }
   ```

------

1. 将 策略附加到该角色。

   1. 将上述内容保存在名为 `fluent-bit-policy.json` 的文件中。

   1. 运行以下命令将内联策略附加到 `fluentTaskRole` IAM 角色。

      ```
      aws iam put-role-policy --role-name fluentTaskRole --policy-name fluentTaskPolicy --policy-document file://fluent-bit-policy.json
      ```

## 步骤 2：创建 Amazon ECS Windows 容器实例
<a name="tutorial-deploy-fluentbit-on-windows-instance"></a>

创建 Amazon ECS Windows 容器实例。

**创建 Amazon ECS 实例**

1. 使用 `aws ssm get-parameters` 命令检索托管您 VPC 的区域的 AMI ID。有关更多信息，请参阅[检索经 Amazon ECS 优化的 AMI 元数据](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/retrieve-ecs-optimized_windows_AMI.html)。

1. 用来创建启动实例的 Amazon EC2 控制台。

   1. 通过以下网址打开 Amazon EC2 控制台：[https://console.aws.amazon.com/ec2/](https://console.aws.amazon.com/ec2/)。

   1. 从导航栏中，选择要使用的区域。

   1. 从 **EC2 控制面板**中，选择 **Launch Instance**（启动实例）。

   1. 对于**名称**，输入唯一的名称。

   1. 对于**应用程序和操作系统映像（亚马逊机器映像）**，选择您在第一步中检索的 AMI。

   1. 对于**实例类型**，选择 `t3.xlarge`。

   1. 对于 **Key pair (login)**（密钥对 [登录]），选择一个密钥对。

   1. 在 **Network settings**（网络设置）下，对于 **Security group**（安全组），可以选择现有安全组或创建新安全组。

   1. 在 **Network settings**（网络设置）下，对于 **Auto-assign Public IP**（自动分配公有 IP），选择 **Enable**（启用）。

   1. 在 **Advanced details**（高级详细信息）下，对于 **IAM instance profile**（IAM 实例配置文件），选择 **ecsInstanceRole**。

   1. 使用以下用户数据配置您的 Amazon ECS 容器实例。在 **Advanced Details**（高级详细信息）下，将以下脚本粘贴到 **User data**（用户数据）字段中，将 *cluster\$1name* 替换为您的集群的名称。

      ```
      <powershell>
      Import-Module ECSTools
      Initialize-ECSAgent -Cluster cluster-name -EnableTaskENI -EnableTaskIAMRole -LoggingDrivers '["awslogs","fluentd"]'
      </powershell>
      ```

   1. 准备好后，选中确认字段，然后选择 **Launch Instances**。

   1. 确认页面会让您知道自己的实例已启动。选择 **View Instances** 以关闭确认页面并返回控制台。

## 步骤 3：配置 Fluent Bit
<a name="tutorial-deploy-fluentbit-on-windows-configure-fluentbit"></a>

您可以使用 AWS 提供的以下默认配置快速入门：
+ [Amazon CloudWatch](https://github.com/aws/aws-for-fluent-bit/blob/mainline/ecs_windows_forward_daemon/cloudwatch.conf) 是基于**《Fluent Bit 官方手册》上的适用于 [Amazon CloudWatch](https://docs.fluentbit.io/manual/v/1.9-pre/pipeline/outputs/cloudwatch) 的 Fluent Bit 插件。

或者，您可以使用 AWS 提供的其他默认配置。有关更多信息，请参阅 Github 网站上关于 `aws-for-fluent-bit` 的[覆盖 Windows 映像的入口点](https://github.com/aws/aws-for-fluent-bit/tree/mainline/ecs_windows_forward_daemon#overriding-the-entrypoint-for-the-windows-image)。

Amazon CloudWatch Fluent Bit 的默认配置如下所示。

替换以下变量：
+ *region*，用您要将 Amazon CloudWatch Logs 发送到的区域替换。

```
[SERVICE]
    Flush               5
    Log_Level           info
    Daemon              off

[INPUT]
    Name                forward
    Listen              0.0.0.0
    Port                24224
    Buffer_Chunk_Size   1M
    Buffer_Max_Size     6M
    Tag_Prefix          ecs.

# Amazon ECS agent adds the following log keys as labels to the docker container.
# We would use fluentd logging driver to add these to log record while sending it to Fluent Bit.
[FILTER]
    Name                modify
    Match               ecs.*
    Rename              com.amazonaws.ecs.cluster ecs_cluster
    Rename              com.amazonaws.ecs.container-name ecs_container_name
    Rename              com.amazonaws.ecs.task-arn ecs_task_arn
    Rename              com.amazonaws.ecs.task-definition-family ecs_task_definition_family
    Rename              com.amazonaws.ecs.task-definition-version ecs_task_definition_version

[FILTER]
    Name                rewrite_tag
    Match               ecs.*
    Rule                $ecs_task_arn ^([a-z-:0-9]+)/([a-zA-Z0-9-_]+)/([a-z0-9]+)$  out.$3.$ecs_container_name false
    Emitter_Name        re_emitted

[OUTPUT]
    Name                cloudwatch_logs
    Match               out.*
    region              region
    log_group_name      fallback-group
    log_group_template  /aws/ecs/$ecs_cluster.$ecs_task_definition_family
    log_stream_prefix   task-
    auto_create_group   On
```

进入 Fluent Bit 的每个日志都有您指定的标签，或者在您不提供标签时自动生成。标签可用于将不同的日志路由到不同的目标。有关更多信息，请参阅**《Fluent Bit 官方手册》中的[标签](https://docs.fluentbit.io/manual/concepts/key-concepts#tag)。

上面描述的 Fluent Bit 配置具有以下属性：
+ 正向输入插件侦听 TCP 端口 24224 上的传入流量。
+ 在该端口上收到的每个日志条目都有一个标签，正向输入插件会修改该标签，以将 `ecs.` 字符串作为记录的前缀。
+ Fluent Bit 内部管道使用 Match 正则表达式路由日志条目以修改筛选条件。此筛选条件将日志记录 JSON 中的密钥替换为 Fluent Bit 可以使用的格式。
+ 然后，rewrite\$1tag 筛选条件会使用修改后的日志条目。此筛选条件将日志记录的标签更改为格式 out.*TASK\$1ID*.*CONTAINER\$1NAME*。
+ 新标签将路由到输出 cloudwatch\$1logs 插件，该插件使用 CloudWatch 输出插件的 `log_group_template` 和 `log_stream_prefix` 选项创建如前所述的日志组和流。有关更多信息，请参阅**《Fluent Bit 官方手册》中的[配置参数](https://docs.fluentbit.io/manual/v/1.9-pre/pipeline/outputs/cloudwatch#configuration-parameters)。

## 步骤 4：注册 Windows Fluent Bit 任务定义，该定义将日志路由到 CloudWatch
<a name="tutorial-deploy-fluentbit-on-windows-register-task-definition"></a>

注册 Windows Fluent Bit 任务定义，该定义将日志路由到 CloudWatch。

**注意**  
此任务定义将 Fluent Bit 容器端口 24224 向主机端口 24224 公开。确认此端口未在您的 EC2 实例安全组中打开，以防止外部访问。

**注册任务定义**

1. 使用以下内容创建名为 `fluent-bit.json` 的文件。

   替换以下变量：
   + 用您的任务 IAM 角色的 Amazon 资源名称（ARN）替换 *task-iam-role*
   + 用您的任务运行所在的区域替换 *region*

   ```
   {
     "family": "ecs-windows-fluent-bit",
     "taskRoleArn": "task-iam-role",
     "containerDefinitions": [
       {
         "name": "fluent-bit",
         "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:windowsservercore-latest",
         "cpu": 512,
         "portMappings": [
           {
             "hostPort": 24224,
             "containerPort": 24224,
             "protocol": "tcp"
           }
         ],
         "entryPoint": [
           "Powershell",
           "-Command"
         ],
         "command": [
           "C:\\entrypoint.ps1 -ConfigFile C:\\ecs_windows_forward_daemon\\cloudwatch.conf"
         ],
         "environment": [
           {
             "name": "AWS_REGION",
             "value": "region"
           }
         ],
         "memory": 512,
         "essential": true,
         "logConfiguration": {
           "logDriver": "awslogs",
           "options": {
             "awslogs-group": "/ecs/fluent-bit-logs",
             "awslogs-region": "region",
             "awslogs-stream-prefix": "flb",
             "awslogs-create-group": "true"
           }
         }
       }
     ],
     "memory": "512",
     "cpu": "512"
   }
   ```

1. 运行以下命令注册任务定义。

   ```
   aws ecs register-task-definition --cli-input-json file://fluent-bit.json --region region
   ```

   您可以通过运行 `list-task-definitions` 命令列出您的账户的任务定义。输出显示了可以与 `run-task` 或 `start-task` 一起使用的系列和修订版本值。

## 步骤 5：使用进程守护程序计划策略将 `ecs-windows-fluent-bit` 任务定义作为 Amazon ECS 服务运行
<a name="tutorial-deploy-fluentbit-on-windows-run-task"></a>

为您的账户注册任务定义后，您可以在集群中运行任务。在本教程中，您将在 `FluentBit-cluster` 集群中运行 `ecs-windows-fluent-bit:1` 任务定义的一个实例。在使用进程守护程序计划策略的服务中运行任务，该策略可确保 Fluent Bit 的单个实例始终在每个容器实例上运行。

**运行任务**

1. 运行以下命令将 `ecs-windows-fluent-bit:1` 任务定义（在上一步中注册）作为服务启动。
**注意**  
此任务定义使用 `awslogs` 日志记录驱动程序，您的容器实例需要具有必要的权限。

   替换以下变量：
   + 用您的服务运行所在的区域替换 *region*

   ```
   aws ecs create-service \
       --cluster FluentBit-cluster \
       --service-name FluentBitForwardDaemonService \
       --task-definition ecs-windows-fluent-bit:1 \
       --launch-type EC2 \
       --scheduling-strategy DAEMON \
       --region region
   ```

1. 运行以下命令列出您的任务。

   替换以下变量：
   + 用您的服务任务运行所在的区域替换 *region*

   ```
   aws ecs list-tasks --cluster FluentBit-cluster --region region
   ```

## 步骤 6：注册生成日志的 Windows 任务定义
<a name="tutorial-deploy-fluentbit-on-windows-register-task-def-logs"></a>

注册生成日志的任务定义。此任务定义部署 Windows 容器映像，该映像将每秒向 `stdout` 写入一个增量数字。

任务定义使用 fluentd 日志记录驱动程序，该驱动程序连接到 Fluent Bit 插件侦听的端口 24224。Amazon ECS 代理使用标签标记每个 Amazon ECS 容器，包括集群名称、任务 ARN、任务定义系列名称、任务定义修订版号和任务容器名称。这些键值标签将传递给 Fluent Bit。

**注意**  
此任务使用 `default` 网络模式。但是，您还可以使用 `awsvpc` 网络模式运行任务。

**注册任务定义**

1. 使用以下内容创建名为 `windows-app-task.json` 的文件。

   ```
   {
     "family": "windows-app-task",
     "containerDefinitions": [
       {
         "name": "sample-container",
         "image": "mcr.microsoft.com/windows/servercore:ltsc2019",
         "cpu": 512,
         "memory": 512,
         "essential": true,
         "entryPoint": [
           "Powershell",
           "-Command"
         ],
         "command": [
           "$count=1;while(1) { Write-Host $count; sleep 1; $count=$count+1;}"
         ],
         "logConfiguration": {
           "logDriver": "fluentd",
           "options": {
             "fluentd-address": "localhost:24224",
             "tag": "{{ index .ContainerLabels \"com.amazonaws.ecs.task-definition-family\" }}",
             "fluentd-async": "true",
             "labels": "com.amazonaws.ecs.cluster,com.amazonaws.ecs.container-name,com.amazonaws.ecs.task-arn,com.amazonaws.ecs.task-definition-family,com.amazonaws.ecs.task-definition-version"
           }
         }
       }
     ],
     "memory": "512",
     "cpu": "512"
   }
   ```

1. 运行以下命令注册任务定义。

   替换以下变量：
   + 用您的任务运行所在的区域替换 *region*

   ```
   aws ecs register-task-definition --cli-input-json file://windows-app-task.json --region region
   ```

   您可以通过运行 `list-task-definitions` 命令列出您的账户的任务定义。输出显示了可以与 `run-task` 或 `start-task` 一起使用的系列和修订版本值。

## 步骤 7：运行 `windows-app-task` 任务定义
<a name="tutorial-deploy-fluentbit-on-windows-run-task-fluentbit"></a>

注册 `windows-app-task` 任务定义后，在 `FluentBit-cluster` 集群中运行。

**运行任务**

1. 运行您在上一步中注册的 `windows-app-task:1` 任务定义。

   替换以下变量：
   + 用您的任务运行所在的区域替换 *region*

   ```
   aws ecs run-task --cluster FluentBit-cluster --task-definition windows-app-task:1 --count 2 --region region
   ```

1. 运行以下命令列出您的任务。

   ```
   aws ecs list-tasks --cluster FluentBit-cluster
   ```

## 步骤 8：验证 CloudWatch 上的日志
<a name="tutorial-deploy-fluentbit-on-windows-verify"></a>

要验证您的 Fluent Bit 设置，请在 CloudWatch 控制台中检查以下日志组：
+ `/ecs/fluent-bit-logs` — 这是与在容器实例上运行的 Fluent Bit 进程守护程序容器相对应的日志组。
+ `/aws/ecs/FluentBit-cluster.windows-app-task` — 这是与 `FluentBit-cluster` 集群中为 `windows-app-task` 任务定义系列启动的所有任务相对应的日志组。

   `task-out.FIRST_TASK_ID.sample-container` — 此日志流包含示例容器任务容器中任务的第一个实例生成的所有日志。

  `task-out.SECOND_TASK_ID.sample-container` — 此日志流包含示例容器任务容器中任务的第二个实例生成的所有日志。

 `task-out.TASK_ID.sample-container` 日志流具有类似于以下内容的字段：

```
{
    "source": "stdout",
    "ecs_task_arn": "arn:aws:ecs:region:0123456789012:task/FluentBit-cluster/13EXAMPLE",
    "container_name": "/ecs-windows-app-task-1-sample-container-cEXAMPLE",
    "ecs_cluster": "FluentBit-cluster",
    "ecs_container_name": "sample-container",
    "ecs_task_definition_version": "1",
    "container_id": "61f5e6EXAMPLE",
    "log": "10",
    "ecs_task_definition_family": "windows-app-task"
}
```

**验证 Fluent Bit 设置**

1. 通过 [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 打开 CloudWatch 控制台。

1. 在导航窗格中，选择 **Log groups（日志组）**。确保您位于将 Fluent Bit 部署到您的容器的区域中。

   在 AWS 区域 的日志组列表中，您将会看到以下内容：
   + `/ecs/fluent-bit-logs`
   + `/aws/ecs/FluentBit-cluster.windows-app-task`

   如果看到这些日志组，则代表 Fluent Bit 设置已验证。

## 步骤 9：清除
<a name="tutorial-deploy-fluentbit-on-windows-cleanup"></a>

完成本教程后，请清除与本教程关联的资源，以避免对您未使用的资源产生费用。

**清除教程资源**

1. 停止 `windows-simple-task` 任务和 `ecs-fluent-bit` 任务。有关更多信息，请参阅 [停止 Amazon ECS 任务](standalone-task-stop.md)。

1. 运行以下命令删除 `/ecs/fluent-bit-logs` 日志组。有关删除日志组的更多信息，请参阅**《AWS Command Line Interface 参考》中的 [delete-log-group](https://docs.aws.amazon.com/cli/latest/reference/logs/delete-log-group.html)。

   ```
   aws logs delete-log-group --log-group-name /ecs/fluent-bit-logs
   aws logs delete-log-group --log-group-name /aws/ecs/FluentBit-cluster.windows-app-task
   ```

1. 运行以下命令终止实例。

   ```
   aws ec2 terminate-instances --instance-ids instance-id
   ```

1. 运行以下命令删除 IAM 角色。

   ```
   aws iam delete-role --role-name ecsInstanceRole
   aws iam delete-role --role-name fluentTaskRole
   ```

1. 运行以下命令删除 Amazon ECS 集群。

   ```
   aws ecs delete-cluster --cluster FluentBit-cluster
   ```

# 将 gMSA 用于 Amazon ECS 上的 EC2 Linux 容器
<a name="linux-gmsa"></a>

Amazon ECS 通过称为*组托管服务账户*（gMSA）的特殊服务账户支持 EC2 上的 Linux 容器的 Active Directory 身份验证。

基于 Linux 的网络应用程序（例如 .NET Core 应用程序）可以使用 Active Directory 来促进用户和服务之间的身份验证和授权管理。通过设计与 Active Directory 集成并在加入域的服务器上运行的应用程序，您可以使用此功能。但是，由于 Linux 容器无法加入域，因此您需要配置一个 Linux 容器来使用 gMSA 运行。

使用 gMSA 运行的 Linux 容器依赖于在容器主机 Amazon EC2 实例上运行的 `credentials-fetcher` 进程守护程序。也就是说，进程守护程序从 Active Directory 域控制器检索 gMSA 凭证，然后将这些凭证传输到容器实例。有关服务账户的更多信息，请参阅 Microsoft Learn 网站上的[为 Windows 容器创建 gMSAs](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts)。

## 注意事项
<a name="linux-gmsa-considerations"></a>

在将 gMSA 用于 Linux 容器之前，请注意以下各项：
+ 如果您的容器在 EC2 上运行，则可以将 gMSA 用于 Windows 容器和 Linux 容器。有关如何在 Fargate 上使用 gMSA Linux 容器的信息，请参阅 [将 gMSA 用于 Fargate 上的 Linux 容器](fargate-linux-gmsa.md)。
+ 您可能需要一台已加入域的 Windows 计算机才能完成先决条件。例如，您可能需要一台已加入域的 Windows 计算机才能使用 PowerShell 在 Active Directory 中创建 gMSA。RSAT Active Director PowerShell 工具仅适用于 Windows。有关更多信息，请参阅[安装 Active Directory 管理工具](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_install_ad_tools.html)。
+ 您可以在**无域 gMSA** 和**将每个实例加入单个域**之间进行选择。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

  然后，选择数据存储用于 CredSpec，以及无域 gMSA 的 Active Directory 用户凭证（可选）。

  Amazon ECS 使用 Active Directory 凭证规范文件（CredSpec）。该文件包含用于将 gMSA 账户上下文传播到容器的 gMSA 元数据。您可以生成 CredSpec 文件，然后将其存储在下表中的一个 CredSpec 存储选项中，该存储选项特定于容器实例的操作系统。要使用无域方法，CredSpec 文件中的可选部分可以在下表中的其中一个 *domainless user credentials* 存储选项中指定凭证，这些凭证特定于容器实例的操作系统。    
<a name="gmsa-table"></a>[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/linux-gmsa.html)

## 先决条件
<a name="linux-gmsa-prerequisites"></a>

在 Amazon ECS 上为 Linux 容器使用 gMSA 之前，请确保完成以下操作：
+ 您可以使用您希望容器访问的资源设置 Active Directory 域。Amazon ECS 支持以下设置：
  + Directory Service Active Directory。Directory Service 是托管在 Amazon EC2 上的 AWS 托管式 Active Directory。有关更多信息，请参阅 *AWS Directory Service 管理指南*中的 [AWS 托管 Microsoft AD 入门](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html)。
  + 本地 Active Directory。您必须确保 Amazon ECS Linux 容器实例可以加入域。有关更多信息，请参阅 [AWS Direct Connect](https://docs.aws.amazon.com/whitepapers/latest/aws-vpc-connectivity-options/aws-direct-connect.html)。
+ 您在 Active Directory 中有现有的 gMSA 账户。有关更多信息，请参阅 [将 gMSA 用于 Amazon ECS 上的 EC2 Linux 容器](#linux-gmsa)。
+ 您已在 Amazon ECS Linux 容器实例上安装并正在运行 `credentials-fetcher` 进程守护程序。您还向 `credentials-fetcher` 进程守护程序添加了一组初始凭证，以便通过 Active Directory 进行身份验证。
**注意**  
`credentials-fetcher` 进程守护程序仅适用于 Amazon Linux 2023 和 Fedora 37 及更高版本。该进程守护程序不适用于 Amazon Linux 2。有关更多信息，请参阅 GitHub 上的 [aws/credentials-fetcher](https://github.com/aws/credentials-fetcher)。
+ 您可以为 `credentials-fetcher` 进程守护程序设置凭证，以便通过 Active Directory 进行身份验证。凭证必须是有权访问 gMSA 账户的 Active Directory 安全组的成员。[决定是要将实例加入域，还是要使用无域 gMSA。](#linux-gmsa-initial-creds) 里面有多个选项。
+ 您已添加所需的 IAM 权限。所需的权限取决于您为初始凭证和存储凭证规范选择的方法：
  + 如果您使用*无域 gMSA* 作为初始凭证，则任务执行角色需要 AWS Secrets Manager 的 IAM 权限。
  + 如果您将凭证规范存储在 SSM Parameter Store 中，则任务执行角色需要获得 Amazon EC2 Systems Manager Parameter Store 的 IAM 权限。
  + 如果您将凭证规范存储在 Amazon S3 中，则任务执行角色需要 Amazon Simple Storage Service 的 IAM 权限。

## 在 Amazon ECS 上设置支持 gMSA 的 Linux 容器
<a name="linux-gmsa-setup"></a>
<a name="linux-gmsa-setup-infra"></a>
**准备基础设施**  
以下步骤是仅执行一次的注意事项和设置。完成这些步骤后，您可以自动创建容器实例，以重复使用此配置。

决定如何提供初始凭证，并在可重复使用的 EC2 启动模板中配置 EC2 用户数据以安装 `credentials-fetcher` 进程守护程序。

1. <a name="linux-gmsa-initial-creds"></a>

**决定是要将实例加入域，还是要使用无域 gMSA。**
   + <a name="linux-gmsa-initial-join"></a>

**将 EC2 实例加入 Active Directory 域**

     
     + <a name="linux-gmsa-initial-join-userdata"></a>

**按用户数据加入实例**

       在 EC2 启动模板中，将加入 Active Directory 域的步骤添加到 EC2 用户数据中。多个 Amazon EC2 Auto Scaling 组可以使用同一个启动模板。

       您可以使用 Fedora 文档中的这些步骤[加入 Active Directory 或 FreeIPA 域](https://docs.fedoraproject.org/en-US/quick-docs/join-active-directory-freeipa/)。
   + <a name="linux-gmsa-initial-domainless"></a>

**为无域 gMSA 创建 Active Directory 用户**

     `credentials-fetcher` 进程守护程序有一个称为*无域 gMSA* 的功能。此功能需要域，但是无需 EC2 实例加入该域。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。相反，您可以在 CredSpec 文件中的 AWS Secrets Manager 中提供密钥的名称。密钥必须包含用户名、密码和要登录到的域。

     该功能受支持，并且可以与 Linux 和 Windows 容器结合使用。

     此功能与该 *gMSA support for non-domain-joined container hosts* 功能类似。有关 Windows 功能的更多信息，请参阅 Microsoft Learn 网站上的 [gMSA 架构和改进](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#gmsa-architecture-and-improvements)。

     1. 在 Active Directory 域中创建用户。Active Directory 中的用户必须具有访问您在任务中使用的 gMSA 服务账户的权限。

     1. 在 Active Directory 中创建用户后，请在 AWS Secrets Manager 中创建密钥。有关更多信息，请参阅[创建一个 AWS Secrets Manager 密钥](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html)。

     1. 将用户的用户名、密码和域分别输入名为 `username`、`password` 和 `domainName` 的 JSON 键值对。

        ```
        {"username":"username","password":"passw0rd", "domainName":"example.com"}
        ```

     1. 向 CredSpec 文件中添加服务账户的配置。其他 `HostAccountConfig` 中包含 Secrets Manager 密钥的 Amazon 资源名称（ARN）。

        在 Windows 上，`PluginGUID` 必须与以下示例代码段中的 GUID 相匹配。在 Linux 上，`PluginGUID` 会被忽略。将 `MySecret` 示例替换为密钥的 Amazon 资源名称（ARN）。

        ```
            "ActiveDirectoryConfig": {
                "HostAccountConfig": {
                    "PortableCcgVersion": "1",
                    "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
                    "PluginInput": {
                        "CredentialArn": "arn:aws:secretsmanager:aws-region:111122223333:secret:MySecret"
                    }
                }
        ```

     1. *无域 gMSA* 功能需要任务执行角色中的额外权限。执行步骤 [（可选）无域 gMSA 密钥](#linux-gmsa-domainless-secret)。

1. <a name="linux-gmsa-install"></a>

**配置实例并安装 `credentials-fetcher` 进程守护程序**

   您可以在 EC2 启动模板中使用用户数据脚本安装 `credentials-fetcher` 进程守护程序。以下示例演示了两种类型的用户数据，`cloud-config` YAML 或 bash 脚本。这些示例适用于 Amazon Linux 2023（AL2023）。将 `MyCluster` 替换为要让这些实例加入的 Amazon ECS 集群的名称。
   + <a name="linux-gmsa-install-yaml"></a>

**`cloud-config` YAML**

     ```
     Content-Type: text/cloud-config
     package_reboot_if_required: true
     packages:
       # prerequisites
       - dotnet
       - realmd
       - oddjob
       - oddjob-mkhomedir
       - sssd
       - adcli
       - krb5-workstation
       - samba-common-tools
       # https://github.com/aws/credentials-fetcher gMSA credentials management for containers
       - credentials-fetcher
     write_files:
     # configure the ECS Agent to join your cluster.
     # replace MyCluster with the name of your cluster.
     - path: /etc/ecs/ecs.config
       owner: root:root
       permissions: '0644'
       content: |
         ECS_CLUSTER=MyCluster
         ECS_GMSA_SUPPORTED=true
     runcmd:
     # start the credentials-fetcher daemon and if it succeeded, make it start after every reboot
     - "systemctl start credentials-fetcher"
     - "systemctl is-active credentials-fetcher && systemctl enable credentials-fetcher"
     ```
   + <a name="linux-gmsa-install-userdata"></a>

**bash 脚本**

     如果您更熟悉 bash 脚本并且有多个变量要写入 `/etc/ecs/ecs.config`，请使用以下 `heredoc` 格式。此格式会将以 **cat** 和 `EOF` 开头的行之间的所有内容写入到配置文件。

     ```
     #!/usr/bin/env bash
     set -euxo pipefail
     
     # prerequisites
     timeout 30 dnf install -y dotnet realmd oddjob oddjob-mkhomedir sssd adcli krb5-workstation samba-common-tools
     # install https://github.com/aws/credentials-fetcher gMSA credentials management for containers
     timeout 30 dnf install -y credentials-fetcher
     
     # start credentials-fetcher
     systemctl start credentials-fetcher
     systemctl is-active credentials-fetcher && systemctl enable credentials-fetcher
     
     cat <<'EOF' >> /etc/ecs/ecs.config
     ECS_CLUSTER=MyCluster
     ECS_GMSA_SUPPORTED=true
     EOF
     ```

   您可以在 `/etc/ecs/ecs.config` 中设置的 `credentials-fetcher` 进程守护程序有一些可选的配置变量。建议您在 YAML 块中或类似于前面的示例的 `heredoc` 中设置用户数据中的变量。这样，在多次编辑文件时，可以防止可能出现的部分配置问题。有关 ECS 代理配置的更多信息，请参阅 GitHub 上的 [Amazon ECS 容器代理](https://github.com/aws/amazon-ecs-agent/blob/master/README.md#environment-variables)。
   + 或者，如果您更改 `credentials-fetcher` 进程守护程序配置以将套接字移到其他位置，则可以使用变量 `CREDENTIALS_FETCHER_HOST`。

**设置权限和密钥**  
针对每个应用程序和每个任务定义，执行一次以下步骤。建议您使用以下最佳实践：授予最低权限和限制策略中使用的权限。这样，每个任务只能读取它需要的密钥。

1. <a name="linux-gmsa-domainless-secret"></a>

**（可选）无域 gMSA 密钥**

   如果您使用实例未加入域的无域方法，请按照此步骤操作。

   您还必须将以下权限作为内联策略添加到任务执行 IAM 角色。这样做可以让 `credentials-fetcher` 进程守护程序访问 Secrets Manager 密钥。将 `MySecret` 示例替换为 `Resource` 列表中的密钥的 Amazon 资源名称（ARN）。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-secret-AbCdEf"
           }
       ]
   }
   ```

------
**注意**  
如果您使用自己的 KMS 密钥来加密您的密钥，则必须向该角色添加必要的权限并将此角色添加到 AWS KMS 密钥策略中。

1. 

**决定是使用 SSM Parameter Store 还是 S3 来存储 CredSpec**

   Amazon ECS 支持使用以下方法在任务定义的 `credentialSpecs` 字段中引用文件路径。

   如果您将实例加入单个域，请使用字符串中 ARN 开头的前缀 `credentialspec:`。如果您使用无域 gMSA，则使用 `credentialspecdomainless:`。

   有关 CredSpec 的更多信息，请参阅 [凭证规范文件](#linux-gmsa-credentialspec)
   + <a name="linux-gmsa-credspec-s3"></a>

**Amazon S3 存储桶**

     将凭证规范添加到 Amazon S3 存储桶。然后在任务定义的 `credentialSpecs` 字段中引用 Amazon S3 存储桶的 Amazon 资源名称（ARN）。

     ```
     {
         "family": "",
         "executionRoleArn": "",
         "containerDefinitions": [
             {
                 "name": "",
                 ...
                 "credentialSpecs": [
                     "credentialspecdomainless:arn:aws:s3:::${BucketName}/${ObjectName}"
                 ],
                 ...
             }
         ],
         ...
     }
     ```

     为了让您的任务可以访问 S3 存储桶，请将以下权限作为内联策略添加到 Amazon ECS 任务执行 IAM 角色中。

------
#### [ JSON ]

****  

     ```
     {
         "Version":"2012-10-17",		 	 	 
         "Statement": [
             {
                 "Sid": "VisualEditor",
                 "Effect": "Allow",
                 "Action": [
                     "s3:Get*",
                     "s3:List*"
                 ],
                 "Resource": [
                     "arn:aws:s3:::amzn-s3-demo-bucket",
                     "arn:aws:s3:::amzn-s3-demo-bucket/{object}"
                 ]
             }
         ]
     }
     ```

------
   + <a name="linux-gmsa-credspec-ssm"></a>

**SSM Parameter Store 参数**

     将凭证规范添加到 SSM Parameter Store 参数中。然后在任务定义的 `credentialSpecs` 字段中引用 SSM Parameter Store 参数的 Amazon 资源名称（ARN）。

     ```
     {
         "family": "",
         "executionRoleArn": "",
         "containerDefinitions": [
             {
                 "name": "",
                 ...
                 "credentialSpecs": [
                     "credentialspecdomainless:arn:aws:ssm:aws-region:111122223333:parameter/parameter_name"
                 ],
                 ...
             }
         ],
         ...
     }
     ```

     为了让您的任务可以访问 SSM Parameter Store 参数，请将以下权限作为内联策略添加到 Amazon ECS 任务执行 IAM 角色。

------
#### [ JSON ]

****  

     ```
     {
         "Version":"2012-10-17",		 	 	 
         "Statement": [
             {
                 "Effect": "Allow",
                 "Action": [
                     "ssm:GetParameters"
                 ],
                 "Resource": "arn:aws:ssm:us-east-1:123456789012:parameter/my-parameter"
             }
         ]
     }
     ```

------

## 凭证规范文件
<a name="linux-gmsa-credentialspec"></a>

Amazon ECS 使用 Active Directory 凭证规范文件（*CredSpec*）。该文件包含用于将 gMSA 账户上下文传播到 Linux 容器的 gMSA 元数据。您可以生成 CredSpec 并在任务定义的 `credentialSpecs` 字段中引用该文件。CredSpec 文件不包含任何机密。

下面是一个 CredSpec 示例文件。

```
{
    "CmsPlugins": [
        "ActiveDirectory"
    ],
    "DomainJoinConfig": {
        "Sid": "S-1-5-21-2554468230-2647958158-2204241789",
        "MachineAccountName": "WebApp01",
        "Guid": "8665abd4-e947-4dd0-9a51-f8254943c90b",
        "DnsTreeName": "example.com",
        "DnsName": "example.com",
        "NetBiosName": "example"
    },
    "ActiveDirectoryConfig": {
        "GroupManagedServiceAccounts": [
            {
                "Name": "WebApp01",
                "Scope": "example.com"
            }
        ],
        "HostAccountConfig": {
            "PortableCcgVersion": "1",
            "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
            "PluginInput": {
                "CredentialArn": "arn:aws:secretsmanager:aws-region:111122223333:secret:MySecret"
            }
        }
    }
}
```
<a name="linux-gmsa-credentialspec-create"></a>
**创建 CredSpec**  
您可以通过在已加入域的 Windows 计算机上使用 CredSpec PowerShell 模块来创建 CredSpec。按照 Microsoft Learn 网站上的[创建凭证规范](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#create-a-credential-spec)中的步骤进行操作。

# 将 gMSA 用于 Fargate 上的 Linux 容器
<a name="fargate-linux-gmsa"></a>

Amazon ECS 通过称为*组托管服务账户*（gMSA）的特殊服务账户支持 Fargate 上的 Linux 容器的 Active Directory 身份验证。

基于 Linux 的网络应用程序（例如 .NET Core 应用程序）可以使用 Active Directory 来促进用户和服务之间的身份验证和授权管理。通过设计与 Active Directory 集成并在加入域的服务器上运行的应用程序，您可以使用此功能。但是，由于 Linux 容器无法加入域，因此您需要配置一个 Linux 容器来使用 gMSA 运行。

## 注意事项
<a name="fargate-linux-gmsa-considerations"></a>

在将 gMSA 用于 Fargate 上的 Linux 容器之前，请注意以下各项：
+ 您必须运行平台版本 1.4 或更高版本。
+ 您可能需要一台已加入域的 Windows 计算机才能完成先决条件。例如，您可能需要一台已加入域的 Windows 计算机才能使用 PowerShell 在 Active Directory 中创建 gMSA。RSAT Active Director PowerShell 工具仅适用于 Windows。有关更多信息，请参阅[安装 Active Directory 管理工具](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_install_ad_tools.html)。
+ 您必须使用**无域 gMSA**。

  Amazon ECS 使用 Active Directory 凭证规范文件（CredSpec）。该文件包含用于将 gMSA 账户上下文传播到容器的 gMSA 元数据。您将生成 CredSpec 文件，然后将其存储在 Amazon S3 存储桶中。
+ 一个任务只能支持一个 Active Directory。

## 先决条件
<a name="fargate-linux-gmsa-prerequisites"></a>

在 Amazon ECS 上为 Linux 容器使用 gMSA 之前，请确保完成以下操作：
+ 您可以使用您希望容器访问的资源设置 Active Directory 域。Amazon ECS 支持以下设置：
  + Directory Service Active Directory。Directory Service 是托管在 Amazon EC2 上的 AWS 托管式 Active Directory。有关更多信息，请参阅 *AWS Directory Service 管理指南*中的 [AWS 托管 Microsoft AD 入门](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html)。
  + 本地 Active Directory。您必须确保 Amazon ECS Linux 容器实例可以加入域。有关更多信息，请参阅 [AWS Direct Connect](https://docs.aws.amazon.com/whitepapers/latest/aws-vpc-connectivity-options/aws-direct-connect-network-to-amazon.html)。
+ 您在 Active Directory 中有一个现有 gMSA 账户，以及一个有权访问 gMSA 服务账户的用户。有关更多信息，请参阅 [为无域 gMSA 创建 Active Directory 用户](#fargate-linux-gmsa-initial-domainless)。
+ 您拥有 Amazon S3 存储桶。有关更多信息，请参阅《Amazon S3 用户指南》**中的[创建存储桶](https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html)。

## 在 Amazon ECS 上设置支持 gMSA 的 Linux 容器
<a name="fargate-linux-gmsa-setup"></a>
<a name="linux-gmsa-setup-infra"></a>
**准备基础设施**  
以下步骤是仅执行一次的注意事项和设置。
+ <a name="fargate-linux-gmsa-initial-domainless"></a>

**为无域 gMSA 创建 Active Directory 用户**

  当您使用无域 gMSA 时，该容器未加入该域。在容器上运行的其他应用程序无法使用凭证访问该域。使用不同域的任务可以在同一个容器上运行。您可以在 CredSpec 文件中的 AWS Secrets Manager 中提供密钥的名称。密钥必须包含用户名、密码和要登录到的域。

  此功能与该 *gMSA support for non-domain-joined container hosts* 功能类似。有关 Windows 功能的更多信息，请参阅 Microsoft Learn 网站上的 [gMSA 架构和改进](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#gmsa-architecture-and-improvements)。

  1. 在 Active Directory 域中配置用户。Active Directory 中的用户必须具有访问您在任务中使用的 gMSA 服务账户的权限。

  1. 您有一个 VPC 和可以解析 Active Directory 域名的子网。使用指向 Active Directory 服务名称的域名配置具有 DHCP 选项的 VPC。有关如何为 VPC 配置 DHCP 选项的信息，请参阅《Amazon Virtual Private Cloud 用户指南》**中的[使用 DHCP 选项集](https://docs.aws.amazon.com/vpc/latest/userguide/DHCPOptionSet.html)。

  1. 在 AWS Secrets Manager 中创建密钥。

  1. 创建凭证规范文件。

**设置权限和密钥**  
针对每个应用程序和每个任务定义，执行一次以下步骤。建议您使用以下最佳实践：授予最低权限和限制策略中使用的权限。这样，每个任务只能读取它需要的密钥。

1. 在 Active Directory 域中创建用户。Active Directory 中的用户必须具有访问您在任务中使用的 gMSA 服务账户的权限。

1. 创建 Active Directory 用户后，请在 AWS Secrets Manager 中创建密钥。有关更多信息，请参阅[创建一个 AWS Secrets Manager 密钥](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html)。

1. 将用户的用户名、密码和域分别输入名为 `username`、`password` 和 `domainName` 的 JSON 键值对。

   ```
   {"username":"username","password":"passw0rd", "domainName":"example.com"}
   ```

1. <a name="fargate-linux-gmsa-domainless-secret"></a>您还必须将以下权限作为内联策略添加到任务执行 IAM 角色。这样做可以让 `credentials-fetcher` 进程守护程序访问 Secrets Manager 密钥。将 `MySecret` 示例替换为 `Resource` 列表中的密钥的 Amazon 资源名称（ARN）。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": [
               "arn:aws:secretsmanager:us-east-1:111122223333:secret:MySecret"
               ]
           }
       ]
   }
   ```

------
**注意**  
如果您使用自己的 KMS 密钥来加密您的密钥，则必须向该角色添加必要的权限并将此角色添加到 AWS KMS 密钥策略中。

1. <a name="linux-gmsa-credspec-ssm"></a>将凭证规范添加到 Amazon S3 存储桶。然后在任务定义的 `credentialSpecs` 字段中引用 Amazon S3 存储桶的 Amazon 资源名称（ARN）。

   ```
   {
       "family": "",
       "executionRoleArn": "",
       "containerDefinitions": [
           {
               "name": "",
               ...
               "credentialSpecs": [
                   "credentialspecdomainless:arn:aws:s3:::${BucketName}/${ObjectName}"
               ],
               ...
           }
       ],
       ...
   }
   ```

   为了让您的任务可以访问 S3 存储桶，请将以下权限作为内联策略添加到 Amazon ECS 任务执行 IAM 角色中。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Sid": "VisualEditor",
               "Effect": "Allow",
               "Action": [
               "s3:GetObjectVersion",
               "s3:ListBucket"
               ],
               "Resource": [
                   "arn:aws:s3:::{bucket_name}",
                   "arn:aws:s3:::{bucket_name}/{object}"
               ]
           }
       ]
   }
   ```

------

## 凭证规范文件
<a name="fargate-linux-gmsa-credentialspec"></a>

Amazon ECS 使用 Active Directory 凭证规范文件（*CredSpec*）。该文件包含用于将 gMSA 账户上下文传播到 Linux 容器的 gMSA 元数据。您可以生成 CredSpec 并在任务定义的 `credentialSpecs` 字段中引用该文件。CredSpec 文件不包含任何机密。

下面是一个 CredSpec 示例文件。

```
{
    "CmsPlugins": [
        "ActiveDirectory"
    ],
    "DomainJoinConfig": {
        "Sid": "S-1-5-21-2554468230-2647958158-2204241789",
        "MachineAccountName": "WebApp01",
        "Guid": "8665abd4-e947-4dd0-9a51-f8254943c90b",
        "DnsTreeName": "example.com",
        "DnsName": "example.com",
        "NetBiosName": "example"
    },
    "ActiveDirectoryConfig": {
        "GroupManagedServiceAccounts": [
            {
                "Name": "WebApp01",
                "Scope": "example.com"
            }
        ],
        "HostAccountConfig": {
            "PortableCcgVersion": "1",
            "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
            "PluginInput": {
                "CredentialArn": "arn:aws:secretsmanager:aws-region:111122223333:secret:MySecret"
            }
        }
    }
}
```
<a name="fargate-linux-gmsa-credentialspec-create"></a>
**创建 CredSpec 并将其上传到 Amazon S3**  
您可以通过在已加入域的 Windows 计算机上使用 CredSpec PowerShell 模块来创建 CredSpec。按照 Microsoft Learn 网站上的[创建凭证规范](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#create-a-credential-spec)中的步骤进行操作。

创建凭证规范文件后，将其上传到 Amazon S3 存储桶。将 CredSpec 文件复制到您正在其中运行 AWS CLI 命令的计算机或环境中。

运行以下 AWS CLI 命令以将 CredSpec 上载到 Amazon S3。将 `amzn-s3-demo-bucket` 替换为您的 Amazon S3 存储桶的名称。您可以将文件作为对象存储在任何存储桶和位置，但您必须允许访问该存储桶和您附加到任务执行角色的策略中的位置。

对于 PowerShell，使用以下命令：

```
$ Write-S3Object -BucketName "amzn-s3-demo-bucket" -Key "ecs-domainless-gmsa-credspec" -File "gmsa-cred-spec.json"
```

以下 AWS CLI 命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。

```
$ aws s3 cp gmsa-cred-spec.json \
s3://amzn-s3-demo-bucket/ecs-domainless-gmsa-credspec
```

# 通过 AWS CLI 将 Amazon ECS Windows 容器与无域 gMSA 结合使用
<a name="tutorial-gmsa-windows"></a>

以下教程演示如何创建一个 Amazon ECS 任务，该任务运行具有凭证的 Windows 容器，以使用 AWS CLI 访问 Active Directory。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

**Topics**
+ [

## 先决条件
](#tutorial-gmsa-windows-prerequisites)
+ [

## 步骤 1：在 Active Directory 域服务（AD DS）上创建和配置 gMSA 账户
](#tutorial-gmsa-windows-step1)
+ [

## 步骤 2：将凭证上传到 Secrets Manager
](#tutorial-gmsa-windows-step2)
+ [

## 步骤 3：修改您的 CredSpec JSON 以包含无域 gMSA 信息
](#tutorial-gmsa-windows-step3)
+ [

## 步骤 4：将 CredSpec 上传到 Amazon S3
](#tutorial-gmsa-windows-step4)
+ [

## 步骤 5：（可选）创建 Amazon ECS 集群
](#tutorial-gmsa-windows-step5)
+ [

## 步骤 6：为容器实例创建 IAM 角色
](#tutorial-gmsa-windows-step6)
+ [

## 步骤 7：创建自定义任务执行角色
](#tutorial-gmsa-windows-step7)
+ [

## 步骤 8：为 Amazon ECS Exec 创建任务角色
](#tutorial-gmsa-windows-step8)
+ [

## 步骤 9：注册一个使用无域 gMSA 的任务定义
](#tutorial-gmsa-windows-step9)
+ [

## 步骤 10：将 Windows 容器实例注册到集群
](#tutorial-gmsa-windows-step10)
+ [

## 步骤 11：验证容器实例
](#tutorial-gmsa-windows-step11)
+ [

## 步骤 12：运行 Windows 任务
](#tutorial-gmsa-windows-step12)
+ [

## 步骤 13：验证容器是否有 gMSA 凭证
](#tutorial-gmsa-windows-step13)
+ [

## 步骤 14：清除
](#tutorial-gmsa-windows-step14)
+ [

## 调试适用于 Windows 容器的 Amazon ECS 无域 gMSA
](#tutorial-gmsa-windows-debugging)

## 先决条件
<a name="tutorial-gmsa-windows-prerequisites"></a>

本教程假设以下先决条件已完成：
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您的 IAM 用户具有 [AmazonECS\$1FullAccess](security-iam-awsmanpol.md#security-iam-awsmanpol-AmazonECS_FullAccess) IAM 策略示例中指定的必需权限。
+  安装并配置了最新版本的 AWS CLI。有关安装或升级 AWS CLI 的更多信息，请参阅[安装 AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)。
**注意**  
您可以使用双堆栈服务端点通过 IPv4 和 IPv6 从 AWS CLI、SDK 和 Amazon ECS API 与 Amazon ECS 进行交互。有关更多信息，请参阅 [使用 Amazon ECS 双堆栈端点](dual-stack-endpoint.md)。
+ 您可以使用您希望容器访问的资源设置 Active Directory 域。Amazon ECS 支持以下设置：
  + Directory Service Active Directory。Directory Service 是托管在 Amazon EC2 上的 AWS 托管式 Active Directory。有关更多信息，请参阅 *AWS Directory Service 管理指南*中的 [AWS 托管 Microsoft AD 入门](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html)。
  + 本地 Active Directory。您必须确保 Amazon ECS Linux 容器实例可以加入域。有关更多信息，请参阅 [AWS Direct Connect](https://docs.aws.amazon.com/whitepapers/latest/aws-vpc-connectivity-options/aws-direct-connect-network-to-amazon.html)。
+ 您有一个 VPC 和可以解析 Active Directory 域名的子网。
+ 您可以在**无域 gMSA** 和**将每个实例加入单个域**之间进行选择。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

  然后，选择数据存储用于 CredSpec，以及无域 gMSA 的 Active Directory 用户凭证（可选）。

  Amazon ECS 使用 Active Directory 凭证规范文件（CredSpec）。该文件包含用于将 gMSA 账户上下文传播到容器的 gMSA 元数据。您可以生成 CredSpec 文件，然后将其存储在下表中的一个 CredSpec 存储选项中，该存储选项特定于容器实例的操作系统。要使用无域方法，CredSpec 文件中的可选部分可以在下表中的其中一个 *domainless user credentials* 存储选项中指定凭证，这些凭证特定于容器实例的操作系统。    
<a name="gmsa-table"></a>[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/tutorial-gmsa-windows.html)
+ （可选）AWS CloudShell 是一种为客户提供命令行的工具，而无需创建自己的 EC2 实例。有关更多信息，请参阅《AWS CloudShell 用户指南》**中的[什么是 AWS CloudShell？](https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html)。

## 步骤 1：在 Active Directory 域服务（AD DS）上创建和配置 gMSA 账户
<a name="tutorial-gmsa-windows-step1"></a>

在 Active Directory 域上创建和配置 gMSA 账户。

**注意**  
此步骤将创建两个独立账户：一个为容器提供身份的组托管服务账户（gMSA），以及一个用于域身份验证的普通用户账户。这些账户有不同的用途，所以应采用不同的名称。

1. 

**生成密钥分发服务根密钥**
**注意**  
如果您使用 Directory Service，可以跳过此步骤。

   KDS 根密钥和 gMSA 权限是使用您的 AWS 托管的 Microsoft AD 配置的。

   如果您尚未在域中创建 gMSA 服务账户，则需要先生成密钥分发服务（KDS）根密钥。KDS 负责创建、轮换 gMSA 密码并将其发布给授权的主机。当 `ccg.exe` 需要检索 gMSA 凭证时，它会联系 KDS 以检索当前密码。

   要检查是否已创建 KDS 根密钥，请使用 `ActiveDirectory` PowerShell 模块在域控制器上以域管理员权限运行以下 PowerShell cmdlet。有关该模块的更多信息，请参阅 Microsoft Learn 网站上的 [ActiveDirectory 模块](https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2022-ps)。

   ```
   PS C:\> Get-KdsRootKey
   ```

   如果命令返回密钥 ID，您可以跳过此步骤的其余部分。否则，请通过运行以下命令创建 KDS 根密钥：

   ```
   PS C:\> Add-KdsRootKey -EffectiveImmediately
   ```

   尽管该命令的参数 `EffectiveImmediately` 暗示密钥立即生效，但您需要等待 10 小时才能复制 KDS 根密钥并使其可在所有域控制器上使用。

1. 

**创建 gMSA 账户**

   要创建 gMSA 账户并允许 `ccg.exe` 检索 gMSA 密码，请从具有域访问权限的 Windows 服务器或客户端上运行以下 PowerShell 命令。将 `ExampleAccount` 替换为您想要的 gMSA 账户名称，然后将 `example-domain` 替换为您的 Active Directory 域名（例如，如果域名是 `contoso.com`，则使用 `contoso`）。

   1. 

      ```
      PS C:\> Install-WindowsFeature RSAT-AD-PowerShell
      ```

   1. 

      ```
      PS C:\> New-ADGroup -Name "ExampleAccount Authorized Hosts" -SamAccountName "ExampleAccountHosts" -GroupScope DomainLocal
      ```

   1. 

      ```
      PS C:\> New-ADServiceAccount -Name "ExampleAccount" -DnsHostName "example-domain" -ServicePrincipalNames "host/ExampleAccount", "host/example-domain" -PrincipalsAllowedToRetrieveManagedPassword "ExampleAccountHosts"
      ```

   1. 使用永不过期的永久密码创建用户。这些凭证存储在 AWS Secrets Manager 中，每个任务都使用这些凭证来加入域。这是与上面创建的 gMSA 账户不同的用户账户。将 `ExampleServiceUser` 替换为您想要的此服务用户账户的名称。

      ```
      PS C:\> New-ADUser -Name "ExampleServiceUser" -AccountPassword (ConvertTo-SecureString -AsPlainText "Test123" -Force) -Enabled 1 -PasswordNeverExpires 1
      ```

   1. 

      ```
      PS C:\> Add-ADGroupMember -Identity "ExampleAccountHosts" -Members "ExampleServiceUser"
      ```

   1. 安装用于在 Active Directory 中创建 CredSpec 对象的 PowerShell 模块并输出 CredSpec JSON。

      ```
      PS C:\> Install-PackageProvider -Name NuGet -Force
      ```

      ```
      PS C:\> Install-Module CredentialSpec
      ```

   1. 

      ```
      PS C:\> New-CredentialSpec -AccountName ExampleAccount
      ```

1. 将前一个命令的 JSON 输出复制到名为 `gmsa-cred-spec.json` 的文件中。这是 CredSpec 文件。它在步骤 3 [步骤 3：修改您的 CredSpec JSON 以包含无域 gMSA 信息](#tutorial-gmsa-windows-step3) 中使用。

## 步骤 2：将凭证上传到 Secrets Manager
<a name="tutorial-gmsa-windows-step2"></a>

将 Active Directory 凭证复制到安全的凭证存储系统中，以便每个任务都能检索该凭证。这是无域 gMSA 方法。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。
+ 运行以下 AWS CLI 命令并替换用户名、密码和域名以匹配您的环境。使用服务用户账户名称（不是 gMSA 账户名称）作为用户名。保留密钥的 ARN 以便在下一步 [步骤 3：修改您的 CredSpec JSON 以包含无域 gMSA 信息](#tutorial-gmsa-windows-step3) 中使用

  以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

  ```
  $ aws secretsmanager create-secret \
  --name gmsa-plugin-input \
  --description "Amazon ECS - gMSA Portable Identity." \
  --secret-string "{\"username\":\"ExampleServiceUser\",\"password\":\"Test123\",\"domainName\":\"contoso.com\"}"
  ```

## 步骤 3：修改您的 CredSpec JSON 以包含无域 gMSA 信息
<a name="tutorial-gmsa-windows-step3"></a>

在将 CredSpec 上传到其中一个存储选项之前，请使用上一步中的 Secrets Manager 中的密钥 ARN 向 CredSpec 添加信息。有关更多信息，请参阅 Microsoft Learn 网站上的[非加入域的容器主机使用案例的其他凭证规范配置](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#additional-credential-spec-configuration-for-non-domain-joined-container-host-use-case)。

1. 将以下信息添加到 `ActiveDirectoryConfig` 内的 CredSpec 文件。将 ARN 替换为上一步中的 Secrets Manager 中的密钥。

   请注意，`PluginGUID` 值必须与以下示例代码段中的 GUID 相匹配，并且是必填项。

   ```
   "HostAccountConfig": {
         "PortableCcgVersion": "1",
         "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
         "PluginInput": "{\"credentialArn\": \"arn:aws:secretsmanager:aws-region:111122223333:secret:gmsa-plugin-input\"}"
       }
   ```

   您也可以使用以下格式的 ARN 在 SSM Parameter Store 中使用密钥：`\"arn:aws:ssm:aws-region:111122223333:parameter/gmsa-plugin-input\"`。

1. CredSpec 文件修改后，应类似于以下示例：

   ```
   {
     "CmsPlugins": [
       "ActiveDirectory"
     ],
     "DomainJoinConfig": {
       "Sid": "S-1-5-21-4066351383-705263209-1606769140",
       "MachineAccountName": "ExampleAccount",
       "Guid": "ac822f13-583e-49f7-aa7b-284f9a8c97b6",
       "DnsTreeName": "example-domain",
       "DnsName": "example-domain",
       "NetBiosName": "example-domain"
     },
     "ActiveDirectoryConfig": {
       "GroupManagedServiceAccounts": [
         {
           "Name": "ExampleAccount",
           "Scope": "example-domain"
         },
         {
           "Name": "ExampleAccount",
           "Scope": "example-domain"
         }
       ],
       "HostAccountConfig": {
         "PortableCcgVersion": "1",
         "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
         "PluginInput": "{\"credentialArn\": \"arn:aws:secretsmanager:aws-region:111122223333:secret:gmsa-plugin-input\"}"
       }
     }
   }
   ```

## 步骤 4：将 CredSpec 上传到 Amazon S3
<a name="tutorial-gmsa-windows-step4"></a>



此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 将 CredSpec 文件复制到您正在其中运行 AWS CLI 命令的计算机或环境中。

1. 运行以下 AWS CLI 命令以将 CredSpec 上载到 Amazon S3。将 `MyBucket` 替换为您的 Amazon S3 存储桶的名称。您可以将文件作为对象存储在任何存储桶和位置，但您必须允许访问该存储桶和您附加到任务执行角色的策略中的位置。

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws s3 cp gmsa-cred-spec.json \
   s3://MyBucket/ecs-domainless-gmsa-credspec
   ```

## 步骤 5：（可选）创建 Amazon ECS 集群
<a name="tutorial-gmsa-windows-step5"></a>

默认情况下，您的账户有一个名为 `default` 的 Amazon ECS 集群。默认情况下，在 AWS CLI、SDK 和 CloudFormation 中使用此集群。您可以使用其他集群来分组和组织任务和基础设施，并为某些配置分配默认值。

您可以从 AWS 管理控制台、AWS CLI、SDK 或 CloudFormation 中创建集群。集群中的设置和配置不影响 gMSA。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

```
$ aws ecs create-cluster --cluster-name windows-domainless-gmsa-cluster
```

**重要**  
如果您选择创建自己的集群，您必须为您打算用于该集群的每个命令指定 `--cluster clusterName`。

## 步骤 6：为容器实例创建 IAM 角色
<a name="tutorial-gmsa-windows-step6"></a>

*容器实例*是在 ECS 任务中运行容器的主机，例如 Amazon EC2 实例。每个容器实例都注册到 Amazon ECS 集群。在启动 Amazon EC2 实例并将其注册到集群之前，必须为容器实例创建 IAM 角色以供使用。

要创建容器实例角色，请参阅 [Amazon ECS 容器实例 IAM 角色](instance_IAM_role.md)。默认的 `ecsInstanceRole` 有足够的权限完成本教程。

## 步骤 7：创建自定义任务执行角色
<a name="tutorial-gmsa-windows-step7"></a>

Amazon ECS 可以使用不同的 IAM 角色来获得启动每项任务所需的权限，而不是容器实例角色。此角色称为*任务执行角色*。建议创建一个仅具有 ECS 运行任务所需的权限（也称为*最低权限*）的任务执行角色。有关最低权限原则的更多信息，请参阅 *AWS Well-Architected Framework* 中的 [SEC03-BP02 授予最低权限访问](https://docs.aws.amazon.com/wellarchitected/latest/framework/sec_permissions_least_privileges.html)。

1. 要创建任务执行角色，请参阅 [创建任务执行 角色](task_execution_IAM_role.md#create-task-execution-role)。默认权限允许容器实例从您的应用程序的 Amazon Elastic Container Registry and `stdout` 和 `stderr` 中提取容器映像，以将其记录到 Amazon CloudWatch Logs 中。

   由于该角色在本教程中需要自定义权限，因此您可以为该角色指定一个不同于 `ecsTaskExecutionRole` 的名称。本教程在后续步骤中使用 `ecsTaskExecutionRole`。

1. 通过创建自定义策略来添加以下权限，可以是仅存在于此角色中的内联策略，也可以是您可以重复使用的策略。将第一条语句中 `Resource` 的 ARN 替换为 Amazon S3 存储桶和位置，将第二个 `Resource` 替换为 Secrets Manager 中密钥的 ARN。

   如果您使用自定义密钥对 Secrets Manager 中的密钥进行加密，则还必须为该密钥允许 `kms:Decrypt`。

   如果您使用 SSM Parameter Store 而不是 Secrets Manager，则必须为该参数允许 `ssm:GetParameter`，而不是 `secretsmanager:GetSecretValue`。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "s3:GetObject"
               ],
               "Resource": "arn:aws:s3:::MyBucket/ecs-domainless-gmsa-credspec/gmsa-cred-spec.json"
           },
           {
               "Effect": "Allow",
               "Action": [
                   "secretsmanager:GetSecretValue"
               ],
               "Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:gmsa-plugin-AbCdEf"
           }
       ]
   }
   ```

------

## 步骤 8：为 Amazon ECS Exec 创建任务角色
<a name="tutorial-gmsa-windows-step8"></a>

本教程使用 Amazon ECS Exec 通过在正在运行的任务中运行命令来验证功能。要使用 ECS Exec，服务或任务必须开启 ECS Exec，并且任务角色（但不是任务执行角色）必须具有 `ssmmessages` 权限。有关所需的 IAM 策略，请参阅 [ECS Exec 权限](task-iam-roles.md#ecs-exec-required-iam-permissions)。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

要使用 AWS CLI 创建任务角色，请执行以下步骤。

1. 使用以下内容创建名为 `ecs-tasks-trust-policy.json` 的文件：

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Principal": {
                   "Service": "ecs-tasks.amazonaws.com"
               },
               "Action": "sts:AssumeRole"
           }
       ]
   }
   ```

------

1. 创建 IAM 角色。您可以替换名称 `ecs-exec-demo-task-role`，但保留该名称以供后续步骤使用。

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws iam create-role --role-name ecs-exec-demo-task-role \
   --assume-role-policy-document file://ecs-tasks-trust-policy.json
   ```

   您可以删除文件 `ecs-tasks-trust-policy.json`。

1. 使用以下内容创建名为 `ecs-exec-demo-task-role-policy.json` 的文件：

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": [
                   "ssmmessages:CreateControlChannel",
                   "ssmmessages:CreateDataChannel",
                   "ssmmessages:OpenControlChannel",
                   "ssmmessages:OpenDataChannel"
               ],
               "Resource": "*"
           }
       ]
   }
   ```

------

1. 创建 IAM 策略并将其附加到上一步的角色中。

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws iam put-role-policy \
       --role-name ecs-exec-demo-task-role \
       --policy-name ecs-exec-demo-task-role-policy \
       --policy-document file://ecs-exec-demo-task-role-policy.json
   ```

   您可以删除文件 `ecs-exec-demo-task-role-policy.json`。

## 步骤 9：注册一个使用无域 gMSA 的任务定义
<a name="tutorial-gmsa-windows-step9"></a>



此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 使用以下内容创建名为 `windows-gmsa-domainless-task-def.json` 的文件：

   ```
   {
     "family": "windows-gmsa-domainless-task",
     "containerDefinitions": [
       {
         "name": "windows_sample_app",
         "image": "mcr.microsoft.com/windows/servercore/iis",
         "cpu": 1024,
         "memory": 1024,
         "essential": true,
         "credentialSpecs": [
                   "credentialspecdomainless:arn:aws:s3:::ecs-domainless-gmsa-credspec/gmsa-cred-spec.json"
         ],
         "entryPoint": [
           "powershell",
           "-Command"
         ],
         "command": [
           "New-Item -Path C:\\inetpub\\wwwroot\\index.html -ItemType file -Value '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p>' -Force ; C:\\ServiceMonitor.exe w3svc"
         ],
         "portMappings": [
           {
             "protocol": "tcp",
             "containerPort": 80,
             "hostPort": 8080
           }
         ]
       }
     ],
     "taskRoleArn": "arn:aws:iam::111122223333:role/ecs-exec-demo-task-role",
     "executionRoleArn": "arn:aws:iam::111122223333:role/ecsTaskExecutionRole"
   }
   ```

1. 通过运行以下命令注册任务定义：

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws ecs register-task-definition \
   --cli-input-json file://windows-gmsa-domainless-task-def.json
   ```

## 步骤 10：将 Windows 容器实例注册到集群
<a name="tutorial-gmsa-windows-step10"></a>

启动 Amazon EC2 Windows 实例并运行 ECS 容器代理将其注册为集群中的容器实例。ECS 在注册到启动任务的集群的容器实例上运行任务。

1. 要启动在 AWS 管理控制台 中为 Amazon ECS 配置的 Amazon EC2 Windows 实例，请参阅 [启动 Amazon ECS Windows 容器实例](launch_window-container_instance.md)。在步骤中停下来获取*用户数据*。

1. 对于 gMSA，用户数据必须在启动 ECS 容器代理之前设置环境变量 `ECS_GMSA_SUPPORTED`。

   对于 ECS Exec，代理必须以参数 `-EnableTaskIAMRole` 开头。

   要通过阻止任务访问 EC2 IMDS Web 服务来检索角色凭证来保护实例 IAM 角色的安全，请添加参数 `-AwsvpcBlockIMDS`。这仅适用于使用 `awsvpc` 网络模式的任务。

   ```
   <powershell>
   [Environment]::SetEnvironmentVariable("ECS_GMSA_SUPPORTED", $TRUE, "Machine")
   Import-Module ECSTools
   Initialize-ECSAgent -Cluster windows-domainless-gmsa-cluster -EnableTaskIAMRole -AwsvpcBlockIMDS
   </powershell>
   ```

1. 查看 **Summary**（摘要）面板中您的实例配置的摘要，当您准备好后，选择 **Launch instance**（启动实例）。

## 步骤 11：验证容器实例
<a name="tutorial-gmsa-windows-step11"></a>

您可以使用 AWS 管理控制台 验证集群中是否有容器实例。但是，gMSA 需要以*属性*表示的其他功能。这些属性在 AWS 管理控制台 中不可见，因此本教程使用 AWS CLI。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 列出集群中的容器实例。容器实例的 ID 与 EC2 实例的 ID 不同。

   ```
   $ aws ecs list-container-instances
   ```

   输出：

   ```
   {
       "containerInstanceArns": [
           "arn:aws:ecs:aws-region:111122223333:container-instance/default/MyContainerInstanceID"
       ]
   }
   ```

   例如，`526bd5d0ced448a788768334e79010fd` 是有效的容器实例 ID。

1. 使用上一步中的容器实例 ID 获取容器实例的详细信息。将 `MyContainerInstanceID` 替换为 ID。

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws ecs describe-container-instances \
        ----container-instances MyContainerInstanceID
   ```

   请注意，输出很长。

1. 验证 `attributes` 列表中是否有一个键名为 `name` 和值为 `ecs.capability.gmsa-domainless` 的对象。以下是对象的示例。

   输出：

   ```
   {
       "name": "ecs.capability.gmsa-domainless"
   }
   ```

## 步骤 12：运行 Windows 任务
<a name="tutorial-gmsa-windows-step12"></a>

运行 Amazon ECS 任务。如果集群中只有 1 个容器实例，则可以使用 `run-task`。如果有许多不同的容器实例，那么使用 `start-task` 和指定运行任务的容器实例 ID 可能比在任务定义中添加放置约束来控制在哪种类型的容器实例上运行该任务要容易得多。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   aws ecs run-task --task-definition windows-gmsa-domainless-task \
       --enable-execute-command --cluster windows-domainless-gmsa-cluster
   ```

   记下该命令返回的任何 ID。

1. 运行以下命令验证任务是否已启动。此命令会等待，直到任务开始后才返回 shell 提示符。将 `MyTaskID` 替换为上一步中的任务 ID。

   ```
   $ aws ecs wait tasks-running --task MyTaskID
   ```

## 步骤 13：验证容器是否有 gMSA 凭证
<a name="tutorial-gmsa-windows-step13"></a>

验证任务中的容器是否有 Kerberos 令牌。gMSA

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws ecs execute-command \
   --task MyTaskID \
   --container windows_sample_app \
   --interactive \
   --command powershell.exe
   ```

   输出将是 PowerShell 提示符。

1. 在容器内的 PowerShell 终端中运行以下命令。

   ```
   PS C:\> klist get ExampleAccount$
   ```

   在输出中，请注意 `Principal` 是您之前创建的输出。

## 步骤 14：清除
<a name="tutorial-gmsa-windows-step14"></a>

完成本教程后，您应清除相关资源，以避免产生与未使用的资源相关的费用。

此步骤使用 AWS CLI。您可以在默认 shell 的 AWS CloudShell 中运行这些命令，即 `bash`。

1. 停止任务。将 `MyTaskID` 替换为步骤 12 [步骤 12：运行 Windows 任务](#tutorial-gmsa-windows-step12) 中的任务 ID。

   ```
   $ aws ecs stop-task --task MyTaskID
   ```

1. 终止 Amazon EC2 实例。之后，集群中的容器实例将在一小时后自动删除。

   您可以使用 Amazon EC2 控制台查找和终止实例。或者，您可以运行以下命令。要运行该命令，请在步骤 1 [步骤 11：验证容器实例](#tutorial-gmsa-windows-step11) 的 `aws ecs describe-container-instances` 命令输出中找到 EC2 实例 ID。i-10a64379 是 EC2 实例 ID 的示例。

   ```
   $ aws ec2 terminate-instances --instance-ids MyInstanceID
   ```

1. 删除 Amazon S3 中的 CredSpec 文件。将 `MyBucket` 替换为您的 Amazon S3 存储桶的名称。

   ```
   $ aws s3api delete-object --bucket MyBucket --key ecs-domainless-gmsa-credspec/gmsa-cred-spec.json
   ```

1. 在 Secrets Manager 中删除密钥。如果您改用 SSM Parameter Store，请删除该参数。

   以下命令使用由 `sh` 和兼容的 shell 使用的反斜杠连续字符。此命令与 PowerShell 不兼容。您必须修改该命令才能将其与 PowerShell 配合使用。

   ```
   $ aws secretsmanager delete-secret --secret-id gmsa-plugin-input \
        --force-delete-without-recovery
   ```

1. 取消注册并删除任务定义。通过取消注册任务定义，可以将其标记为非活动状态，这样它就无法用于启动新任务。然后，您可以删除任务定义。

   1. 通过指定版本来取消注册任务定义。ECS 会自动生成任务定义的版本，这些版本从 1 开始编号。您可以按照与容器映像上的标签相同的格式来引用版本，例如 `:1`。

      ```
      $ aws ecs deregister-task-definition --task-definition windows-gmsa-domainless-task:1
      ```

   1. 删除任务定义。

      ```
      $ aws ecs delete-task-definitions --task-definition windows-gmsa-domainless-task:1
      ```

1. （可选）如果您创建了集群，请删除 ECS 集群。

   ```
   $ aws ecs delete-cluster --cluster windows-domainless-gmsa-cluster
   ```

## 调试适用于 Windows 容器的 Amazon ECS 无域 gMSA
<a name="tutorial-gmsa-windows-debugging"></a>



Amazon ECS 任务状态  
ECS 只会尝试启动任务一次。任何有问题的任务都将停止，并设置为状态 `STOPPED`。任务问题有两种常见类型。首先，是无法启动的任务。其次，应用程序已在其中一个容器内停止的任务。在 AWS 管理控制台 中，查看任务的**停止原因**字段中，了解任务停止的原因。在 AWS CLI 中，描述任务并查看 `stoppedReason`。有关 AWS 管理控制台 和 AWS CLI 中的步骤，请参阅 [查看 Amazon ECS 已停止任务错误](stopped-task-errors.md)。

Windows 事件  
容器中 gMSA 的 Windows 事件记录在 `Microsoft-Windows-Containers-CCG` 日志文件中，可以在 `Logs\Microsoft\Windows\Containers-CCG\Admin` 的“应用程序和服务”部分中的事件查看器中找到。有关更多调试技巧，请参阅 Microsoft Learn 网站上的[适用于 Windows 容器的 gMSA 的故障排除](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/gmsa-troubleshooting#non-domain-joined-container-hosts-use-event-logs-to-identify-configuration-issues)。

ECS 代理 gMSA 插件  
Windows 容器实例上的 ECS 代理的 gMSA 插件的日志位于以下目录 `C:/ProgramData/Amazon/gmsa-plugin/` 中。查看此日志，以了解无域用户凭证是否是从存储位置（例如 Secrets Manager）下载的，以及凭证格式是否正确读取。

# 了解了解如何将 gMSA 用于适用于 Amazon ECS 的 EC2 Windows 容器
<a name="windows-gmsa"></a>

Amazon ECS 通过*组托管服务账户*（gMSA）的特殊服务账户支持 Windows 容器的 Active Directory 身份验证。

基于 Windows 的网络应用程序（例如 .NET 应用程序）通常使用 Active Directory 来促进用户和服务之间的身份验证和授权管理。开发人员通常将其应用程序设计为与 Active Directory 集成，并为此在加入域的服务器上运行。由于 Windows 容器无法加入域，因此必须将 Windows 容器配置为使用 gMSA 运行。

使用 gMSA 运行的 Windows 容器依赖于其主机 Amazon EC2 实例从 Active Directory 域控制器检索 gMSA 凭证并将其提供给容器实例。有关详细信息，请参阅[为 Windows 容器创建 gMSA](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts)。

**注意**  
Fargate 上的 Windows 容器不支持此功能。

**Topics**
+ [

## 注意事项
](#windows-gmsa-considerations)
+ [

## 先决条件
](#windows-gmsa-prerequisites)
+ [

## 在 Amazon ECS 上为 Windows 容器设置 gMSA
](#windows-gmsa-setup)

## 注意事项
<a name="windows-gmsa-considerations"></a>

为 Windows 容器使用 gMSA 时，应考虑以下因素：
+ 为容器实例使用经 Amazon ECS 优化的 Windows Server 2016 Full AMI 时，容器主机名必须与凭证规范文件中定义的 gMSA 账户名相同。要为容器指定主机名，请使用 `hostname` 容器定义参数。有关更多信息，请参阅 [网络设置](task_definition_parameters.md#container_definition_network)。
+ 您可以在**无域 gMSA** 和**将每个实例加入单个域**之间进行选择。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

  然后，选择数据存储用于 CredSpec，以及无域 gMSA 的 Active Directory 用户凭证（可选）。

  Amazon ECS 使用 Active Directory 凭证规范文件（CredSpec）。该文件包含用于将 gMSA 账户上下文传播到容器的 gMSA 元数据。您可以生成 CredSpec 文件，然后将其存储在下表中的一个 CredSpec 存储选项中，该存储选项特定于容器实例的操作系统。要使用无域方法，CredSpec 文件中的可选部分可以在下表中的其中一个 *domainless user credentials* 存储选项中指定凭证，这些凭证特定于容器实例的操作系统。    
<a name="gmsa-table"></a>[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/windows-gmsa.html)

## 先决条件
<a name="windows-gmsa-prerequisites"></a>

在 Amazon ECS 上为 Windows 容器使用 gMSA 之前，请确保完成以下操作：
+ 您可以使用您希望容器访问的资源设置 Active Directory 域。Amazon ECS 支持以下设置：
  + Directory Service Active Directory。Directory Service 是托管在 Amazon EC2 上的 AWS 托管式 Active Directory。有关更多信息，请参阅 *AWS Directory Service 管理指南*中的 [AWS 托管 Microsoft AD 入门](https://docs.aws.amazon.com/directoryservice/latest/admin-guide/ms_ad_getting_started.html)。
  + 本地 Active Directory。您必须确保 Amazon ECS Linux 容器实例可以加入域。有关更多信息，请参阅 [AWS Direct Connect](https://docs.aws.amazon.com/whitepapers/latest/aws-vpc-connectivity-options/aws-direct-connect-network-to-amazon.html)。
+ 您在 Active Directory 中有现有的 gMSA 账户。有关详细信息，请参阅[为 Windows 容器创建 gMSA](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts)。
+ **您选择使用*无域 gMSA*，或托管 Amazon ECS 任务的 Amazon ECS Windows 容器实例必须是加入到 Active Directory 的*域*，并且是有权访问 gMSA 账户的 Active Directory 安全组的成员。**

  通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。
+ 您已添加所需的 IAM 权限。所需的权限取决于您为初始凭证和存储凭证规范选择的方法：
  + 如果您使用*无域 gMSA* 作为初始凭证，则 Amazon EC2 实例角色需要 AWS Secrets Manager 的 IAM 权限。
  + 如果您将凭证规范存储在 SSM Parameter Store 中，则任务执行角色需要获得 Amazon EC2 Systems Manager Parameter Store 的 IAM 权限。
  + 如果您将凭证规范存储在 Amazon S3 中，则任务执行角色需要 Amazon Simple Storage Service 的 IAM 权限。

## 在 Amazon ECS 上为 Windows 容器设置 gMSA
<a name="windows-gmsa-setup"></a>

要在 Amazon ECS 上为 Windows 容器设置 gMSA，您可以按照包括配置先决条件 [通过 AWS CLI 将 Amazon ECS Windows 容器与无域 gMSA 结合使用](tutorial-gmsa-windows.md) 在内的完整教程进行操作。

以下各节将详细介绍 CredSpec 配置。

**Topics**
+ [

### 示例 CredSpec
](#windows-gmsa-example)
+ [

### 无域 gMSA 设置
](#windows-gmsa-domainless)
+ [

### 在任务定义中引用凭证规范文件
](#windows-gmsa-credentialspec)

### 示例 CredSpec
<a name="windows-gmsa-example"></a>

Amazon ECS 使用一个凭证规范文件，该文件包含用于将 gMSA 账户上下文传播到 Windows 容器的 gMSA 元数据。您可以生成凭证规范文件并在任务定义的 `credentialSpec` 字段中引用该文件。凭证规范文件不包含任何密钥。

下面是一个示例凭证规范文件：

```
{
    "CmsPlugins": [
        "ActiveDirectory"
    ],
    "DomainJoinConfig": {
        "Sid": "S-1-5-21-2554468230-2647958158-2204241789",
        "MachineAccountName": "WebApp01",
        "Guid": "8665abd4-e947-4dd0-9a51-f8254943c90b",
        "DnsTreeName": "contoso.com",
        "DnsName": "contoso.com",
        "NetBiosName": "contoso"
    },
    "ActiveDirectoryConfig": {
        "GroupManagedServiceAccounts": [
            {
                "Name": "WebApp01",
                "Scope": "contoso.com"
            }
        ]
    }
}
```

### 无域 gMSA 设置
<a name="windows-gmsa-domainless"></a>

建议使用无域 gMSA，而不是将容器实例加入到单个域中。通过使用无域 gMSA，容器实例不会加入该域，实例上的其他应用程序无法使用凭证访问该域，并且加入不同域的任务可以在同一个实例上运行。

1. 在将 CredSpec 上传到其中一个存储选项之前，请在 Secrets Manager 或 SSM Parameter Store 中使用密钥的 ARN 向 CredSpec 添加信息。有关更多信息，请参阅 Microsoft Learn 网站上的[非加入域的容器主机使用案例的其他凭证规范配置](https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/manage-serviceaccounts#additional-credential-spec-configuration-for-non-domain-joined-container-host-use-case)。

**无域 gMSA 凭证格式**  
以下是 Active Directory 的无域 gMSA 凭证的 JSON 格式。将凭证存储在 Secrets Manager 或 SSM Parameter Store 中。

   ```
   {
       "username":"WebApp01",
       "password":"Test123!",
       "domainName":"contoso.com"
   }
   ```

1. 将以下信息添加到 `ActiveDirectoryConfig` 内的 CredSpec 文件。将 ARN 替换为 Secrets Manager 或 SSM Parameter Store 中的密钥。

   请注意，`PluginGUID` 值必须与以下示例代码段中的 GUID 相匹配，并且是必填项。

   ```
       "HostAccountConfig": {
           "PortableCcgVersion": "1",
           "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
           "PluginInput": "{\"credentialArn\": \"arn:aws:secretsmanager:aws-region:111122223333:secret:gmsa-plugin-input\"}"
       }
   ```

   您也可以使用以下格式的 ARN 在 SSM Parameter Store 中使用密钥：`\"arn:aws:ssm:aws-region:111122223333:parameter/gmsa-plugin-input\"`。

1. CredSpec 文件修改后，应类似于以下示例：

   ```
   {
     "CmsPlugins": [
       "ActiveDirectory"
     ],
     "DomainJoinConfig": {
       "Sid": "S-1-5-21-4066351383-705263209-1606769140",
       "MachineAccountName": "WebApp01",
       "Guid": "ac822f13-583e-49f7-aa7b-284f9a8c97b6",
       "DnsTreeName": "contoso",
       "DnsName": "contoso",
       "NetBiosName": "contoso"
     },
     "ActiveDirectoryConfig": {
       "GroupManagedServiceAccounts": [
         {
           "Name": "WebApp01",
           "Scope": "contoso"
         },
         {
           "Name": "WebApp01",
           "Scope": "contoso"
         }
       ],
       "HostAccountConfig": {
         "PortableCcgVersion": "1",
         "PluginGUID": "{859E1386-BDB4-49E8-85C7-3070B13920E1}",
         "PluginInput": "{\"credentialArn\": \"arn:aws:secretsmanager:aws-region:111122223333:secret:gmsa-plugin-input\"}"
       }
     }
   }
   ```

### 在任务定义中引用凭证规范文件
<a name="windows-gmsa-credentialspec"></a>

Amazon ECS 支持使用以下方法在任务定义的 `credentialSpecs` 字段中引用文件路径。对于这些选项中的每一个，您可以提供 `credentialspec:` 或 `domainlesscredentialspec:`，具体取决于您是将容器实例加入单个域还是使用无域 gMSA。

#### Amazon S3 存储桶
<a name="gmsa-credspec-s3"></a>

将凭证规范添加到 Amazon S3 存储桶，然后在任务定义的 `credentialSpecs` 字段中引用 Amazon S3 存储桶的 Amazon 资源名称（ARN）。

```
{
    "family": "",
    "executionRoleArn": "",
    "containerDefinitions": [
        {
            "name": "",
            ...
            "credentialSpecs": [
                "credentialspecdomainless:arn:aws:s3:::${BucketName}/${ObjectName}"
            ],
            ...
        }
    ],
    ...
}
```

为了让您的任务可以访问 Amazon S3 存储桶，您还必须将以下权限作为内联策略添加到 Amazon ECS 任务执行 IAM 角色。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "VisualEditor",
            "Effect": "Allow",
            "Action": [
                "s3:Get*",
                "s3:List*"
            ],
            "Resource": [
                "arn:aws:s3:::{bucket_name}",
                "arn:aws:s3:::{bucket_name}/{object}"
            ]
        }
    ]
}
```

------

#### SSM Parameter Store 参数
<a name="gmsa-credspec-ssm"></a>

将凭证规范添加到 SSM Parameter Store 参数，然后在任务定义的 `credentialSpecs` 字段中引用 SSM Parameter Store 参数的 Amazon 资源名称（ARN）。

```
{
    "family": "",
    "executionRoleArn": "",
    "containerDefinitions": [
        {
            "name": "",
            ...
            "credentialSpecs": [
                "credentialspecdomainless:arn:aws:ssm:region:111122223333:parameter/parameter_name"
            ],
            ...
        }
    ],
    ...
}
```

为了让您的任务可以访问 SSM Parameter Store 参数，您还必须将以下权限作为内联策略添加到 Amazon ECS 任务执行 IAM 角色。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": "arn:aws:ssm:*:111122223333:parameter/example*"
        }
    ]
}
```

------

#### 本地文件
<a name="gmsa-credspec-file"></a>

使用本地文件中的凭证规范详细信息，在任务定义的 `credentialSpecs` 字段中引用文件路径。引用的文件路径必须相对于 `C:\ProgramData\Docker\CredentialSpecs` 目录，并使用反斜杠（'\$1'）作为文件路径分隔符。

```
{
    "family": "",
    "executionRoleArn": "",
    "containerDefinitions": [
        {
            "name": "",
            ...
            "credentialSpecs": [
                "credentialspec:file://CredentialSpecDir\CredentialSpecFile.json"
            ],
            ...
        }
    ],
    ...
}
```

# 使用 EC2 Image Builder 构建经过 Amazon ECS 优化的自定义 AMI
<a name="image-builder-tutorial"></a>

AWS 建议使用 Amazon ECS 优化型 AMI，因为已经根据运行容器工作负载的要求和建议进行了预配置。有时您可能需要定制您的 AMI 以添加其他软件。您可以使用 EC2 Image Builder 创建、管理和部署您的服务器映像。您保留在账户中创建的自定义映像的所有权。您可以使用 EC2 Image Builder 管道自动执行映像的更新和系统修补，也可以使用独立命令使用您定义的配置资源创建映像。

您可以为映像创建配方。配方包括父映像和任何其他组件。您还可以创建用于分发自定义 AMI 的管道。

您可以为映像创建配方。Image Builder 映像配方是一个文档，用于定义基础映像和要应用于基础映像以生成输出 AMI 映像所需配置的组件。您还可以创建用于分发自定义 AMI 的管道。有关更多信息，请参阅《EC2 Image Builder 用户指南》**中的 [EC2 Image Builder 工作原理](https://docs.aws.amazon.com/imagebuilder/latest/userguide/how-image-builder-works.html)。

建议您在 EC2 Image Builder 中使用以下经过 Amazon ECS 优化的 AMI 之一作为“父映像”：
+ Linux
  + 经 Amazon ECS 优化的 AL2023 x86
  + 经 Amazon ECS 优化的 Amazon Linux 2023（arm64）AMI
  + 经 Amazon ECS 优化的 Amazon Linux 2 内核 5 AMI
  + 经 Amazon ECS 优化的 Amazon Linux 2 x86 AMI
+ Windows
  + 经 Amazon ECS 优化的 Windows 2022 Full x86
  + 经 Amazon ECS 优化的 Windows 2022 Core x86
  + 经 Amazon ECS 优化的 Windows 2019 Full x86
  + 经 Amazon ECS 优化的 Windows 2019 Core x86
  + 经 Amazon ECS 优化的 Windows 2016 Full x86

还建议您选择“使用最新可用的 OS 版本”。管道将对父映像使用语义版本控制，这有助于检测自动安排的作业中的依赖项更新。有关更多信息，请参阅《EC2 Image Builder 用户指南》**中的[语义版本控制](https://docs.aws.amazon.com/imagebuilder/latest/userguide/ibhow-semantic-versioning.html)。

AWS 使用安全补丁和新的容器代理版本定期更新经 Amazon ECS 优化的 AMI 映像。在映像配方中使用 AMI ID 作为父映像时，您需要定期检查父映像是否有更新。如果有更新，则必须使用更新的 AMI 创建配方的新版本。这样可以确保您的自定义映像包含了最新版本的父映像。有关如何创建工作流以使用新创建的 AMI 自动更新 Amazon ECS 集群中的 EC2 实例的信息，请参阅[如何创建 AMI 固化管道并自动更新 ECS 实例集](https://aws.amazon.com/blogs/security/how-to-create-an-ami-hardening-pipeline-and-automate-updates-to-your-ecs-instance-fleet/)。

您还可以指定通过托管 EC2 Image Builder 管道发布的父映像的 Amazon 资源名称（ARN）。Amazon 会定期通过托管管道发布经 Amazon ECS 优化的 AMI 映像。这些映像可公开访问。您必须具有访问映像的正确权限。当您在 Image Builder 配方中使用映像 ARN 而不是 AMI 时，您的管道在每次运行时都会自动使用父映像的最新版本。通过这种方法，无需为每次更新手动创建新的配方版本。

## 清理经 Amazon ECS 优化的 AMI
<a name="cleanup-ecs-optimized-ami"></a>

当使用经 Amazon ECS 优化的 AMI 作为父映像时，您必须清理该映像以防止暂时性问题。经 Amazon ECS 优化的 AMI 已针对 Amazon ECS 代理预先配置，可自动启动并在 Amazon ECS 中注册为容器实例。如果未进行适当清理就将其用作基础映像，可能会导致您的自定义 AMI 出现问题。

要清理映像以备将来使用，请创建一个组件，该组件会运行下面的命令来停止 ecs-init 包和 Docker 进程：

```
sudo systemctl stop ecs
sudo systemctl stop docker
```

移除当前实例中的所有日志文件，以防止在保存映像时保留这些文件。请使用 [EC2 Image Builder 的安全最佳实践](https://docs.aws.amazon.com/imagebuilder/latest/userguide/security-best-practices.html)中的示例脚本来清理实例中的各种文件。

要清除 Amazon ECS 特有的数据，请运行下面的命令：

```
sudo rm -rf /var/log/ecs/*
sudo rm /var/lib/ecs/data/agent.db
```

有关创建经 Amazon ECS 优化的自定义 AMI 的更多信息，请参阅 AWS 知识中心中的 [How do I create a custom AMI from an Amazon ECS-optimized AMI?](https://forums.aws.amazon.com/knowledge-center/ecs-create-custom-amis/)。

## 将映像 ARN 与基础设施即代码（IaC）结合使用
<a name="infrastructure-as-code-arn"></a>

您可以使用 EC2 Image Builder 控制台、基础设施即代码（例如 CloudFormation）或 AWS 开发工具包来配置配方。在配方中指定父映像时，您可以指定 EC2 AMI ID、Image Builder 映像 ARN、AWS Marketplace 产品 ID 或容器映像。AWS 会公开发布经 Amazon ECS 优化的 AMI 的 AMI ID 和 Image Builder 映像 ARN。映像的 ARN 格式如下所示：

```
arn:${Partition}:imagebuilder:${Region}:${Account}:image/${ImageName}/${ImageVersion}
```

`ImageVersion` 具有以下格式。将*主要*要、*次要*和*补丁*替换为最新值。

```
<major>.<minor>.<patch>
```

您可以将 `major`、`minor` 和 `patch` 替换为特定值，也可以使用映像的无版本 ARN，这样您的管道就可以与最新版本的父映像保持同步。无版本 ARN 使用通配符格式“x.x.x”来表示映像版本。通过这种方法，Image Builder 服务能够自动解析为映像的最新版本。使用无版本 ARN 可确保您的参考始终指向可用的最新映像，从而简化在部署中维护最新映像的过程。当使用控制台创建配方时，EC2 Image Builder 会自动识别您的父映像的 ARN。使用 IaC 创建配方时，您必须识别 ARN 并选择所需的映像版本或使用无版本 ARN，以表示最新的可用映像。建议您创建一个自动脚本，以筛选并仅显示符合您标准的映像。以下 Python 脚本显示如何检索经 Amazon ECS 优化的 AMI 的列表。

该脚本接受两个可选参数：`owner` 和 `platform`，其默认值分别为“Amazon”和“Windows”。所有者参数的有效值为：`Self`、`Shared`、`Amazon` 和 `ThirdParty`。平台参数的有效值为 `Windows` 和 `Linux`。例如，如果您在 `owner` 参数设置为 `Amazon` 且 `platform` 设置为 `Linux` 时运行脚本，则脚本会生成 Amazon 发布的映像列表，包括经 Amazon ECS 优化的映像。

```
import boto3
import argparse

def list_images(owner, platform):
    # Create a Boto3 session
    session = boto3.Session()
    
    # Create an EC2 Image Builder client
    client = session.client('imagebuilder')

    # Define the initial request parameters
    request_params = {
        'owner': owner,
        'filters': [
            {
                'name': 'platform',
                'values': [platform]
            }
        ]
    }

    # Initialize the results list
    all_images = []

    # Get the initial response with the first page of results
    response = client.list_images(**request_params)

    # Extract images from the response
    all_images.extend(response['imageVersionList'])

    # While 'nextToken' is present, continue paginating
    while 'nextToken' in response and response['nextToken']:
        # Update the token for the next request
        request_params['nextToken'] = response['nextToken']

        # Get the next page of results
        response = client.list_images(**request_params)

        # Extract images from the response
        all_images.extend(response['imageVersionList'])

    return all_images

def main():
    # Initialize the parser
    parser = argparse.ArgumentParser(description="List AWS images based on owner and platform")
    
    # Add the parameters/arguments
    parser.add_argument("--owner", default="Amazon", help="The owner of the images. Default is 'Amazon'.")
    parser.add_argument("--platform", default="Windows", help="The platform type of the images. Default is 'Windows'.")

    # Parse the arguments
    args = parser.parse_args()

    # Retrieve all images based on the provided owner and platform
    images = list_images(args.owner, args.platform)

    # Print the details of the images
    for image in images:
        print(f"Name: {image['name']}, Version: {image['version']}, ARN: {image['arn']}")

if __name__ == "__main__":
    main()
```

## 将映像 ARN 与 CloudFormation 结合使用
<a name="arn-with-cloudformation"></a>

Image Builder 映像配方是一种蓝图，它指定了实现输出映像预期配置所需的父映像和组件。您使用 `AWS::ImageBuilder::ImageRecipe` 资源。将 `ParentImage` 值设置为映像 ARN。使用所需映像的无版本 ARN，确保您的管道始终使用最新版本的映像。例如 `arn:aws:imagebuilder:us-east-1:aws:image/amazon-linux-2023-ecs-optimized-x86/x.x.x`。以下 `AWS::ImageBuilder::ImageRecipe` 资源定义使用 Amazon 托管的映像 ARN：

```
ECSRecipe:
    Type: AWS::ImageBuilder::ImageRecipe
    Properties:
      Name: MyRecipe
      Version: '1.0.0'
      Components:
        - ComponentArn: [<The component arns of the image recipe>]
      ParentImage: "arn:aws:imagebuilder:us-east-1:aws:image/amazon-linux-2023-ecs-optimized-x86/x.x.x"
```

有关 [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagerecipe.html) 资源更多信息，请参阅《AWS CloudFormation 用户指南》**。

您可以通过设置 `AWS::ImageBuilder::ImagePipeline` 资源的 `Schedule` 属性在管道中自动创建新映像。计划包括启动条件和 cron 表达式。有关更多信息，请参阅《AWS CloudFormation 用户指南》**中的 [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagepipeline.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-imagebuilder-imagepipeline.html)。

 以下 `AWS::ImageBuilder::ImagePipeline` 示例让管道每天在协调世界时间（UTC）上午 10:00 构建项目。设置以下 `Schedule` 值：
+ 将 `PipelineExecutionStartCondition` 设置为 `EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE`。只有当依赖资源（例如父映像或在语义版本中使用通配符“x”的组件）更新后，才会启动该版本。这样可以确保版本包含这些资源的最新更新。
+ 将 ScheduleExpression 设置为 cron 表达式 `(0 10 * * ? *)`。

```
ECSPipeline:
    Type: AWS::ImageBuilder::ImagePipeline
    Properties:
      Name: my-pipeline
      ImageRecipeArn: <arn of the recipe you created in previous step>
      InfrastructureConfigurationArn: <ARN of the infrastructure configuration associated with this image pipeline>
      Schedule:
        PipelineExecutionStartCondition: EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE
        ScheduleExpression: 'cron(0 10 * * ? *)'
```

## 将映像 ARN 与 Terraform 结合使用
<a name="arn-with-terraform"></a>

在 Terraform 中指定管道的父映像和计划的方法与 AWS CloudFormation 中的方法一致。您使用 `aws_imagebuilder_image_recipe` 资源。将 `parent_image` 值设置为映像 ARN。使用所需映像的无版本 ARN，确保您的管道始终使用最新版本的映像。有关更多信息，请参阅 Terraform 文档中的 [https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_recipe#argument-reference](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_recipe#argument-reference)。

在 `aws_imagebuilder_image_pipeline resource` 的调度配置块中，将 `schedule_expression` 参数值设置为您选择的 cron 表达式，以指定管道运行的频率，并将 `pipeline_execution_start_condition` 设置为 `EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE`。有关更多信息，请参阅 Terraform 文档中的 [https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_pipeline#argument-reference](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/imagebuilder_image_pipeline#argument-reference)。

# 使用 Amazon ECS 上的 AWS Deep Learning Containers
<a name="deep-learning-containers"></a>

AWS Deep Learning Containers 提供了一组 Docker 映像，用于在 Amazon ECS 上培训和服务 TensorFlow 和 Apache MXNet（Incubating）中的模型。Deep Learning Containers 支持使用 TensorFlow、NVIDIA CUDA（用于 GPU 实例）和 Intel MKL（用于 CPU 实例）库优化环境。Amazon ECR 中提供了 Deep Learning Containers 的容器映像，在 Amazon ECS 任务定义中参考。您可以将 Deep Learning Containers 与 Amazon Elastic Inference 结合使用，以降低推理成本。

要开始在 Amazon ECS上使用无 Elastic Inference 的 Deep Learning Containers，请参阅《AWS Deep Learning AMIs 开发人员指南》中的 [Amazon ECS setup](https://docs.aws.amazon.com/deep-learning-containers/latest/devguide/deep-learning-containers-ecs-setup.html)**。

# 教程：使用蓝/绿部署创建服务
<a name="create-blue-green"></a>

Amazon ECS 已将蓝/绿部署集成到 Amazon ECS 控制台的创建服务向导中。有关更多信息，请参阅 [创建 Amazon ECS 滚动更新部署](create-service-console-v2.md)。

以下教程说明如何创建一个 Amazon ECS 服务，该服务包含一个将蓝/绿部署类型与 AWS CLI 结合使用的 Fargate 任务。

**注意**  
为 CloudFormation 增加了对执行蓝/绿部署的支持。有关更多信息，请参阅 *AWS CloudFormation 用户指南*中的[使用 CloudFormation 通过 CodeDeploy 进行Amazon ECS 蓝/绿部署](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/blue-green.html)。

## 先决条件
<a name="create-blue-green-prereqs"></a>

本教程假设您已完成以下先决条件：
+ 安装并配置了最新版本的 AWS CLI。有关安装或升级 AWS CLI 的更多信息，请参阅[安装 AWS Command Line Interface](https://docs.aws.amazon.com/cli/latest/userguide/installing.html)。
+ [设置以使用 Amazon ECS](get-set-up-for-amazon-ecs.md) 中的步骤已完成。
+ 您的 IAM 用户具有 [AmazonECS\$1FullAccess](security-iam-awsmanpol.md#security-iam-awsmanpol-AmazonECS_FullAccess) IAM 策略示例中指定的必需权限。
+ 您已创建要使用的 VPC 和安全组。有关更多信息，请参阅 [创建虚拟私有云](get-set-up-for-amazon-ecs.md#create-a-vpc)。
+ 创建 Amazon ECS CodeDeploy IAM 角色。有关更多信息，请参阅 [Amazon ECS CodeDeploy IAM 角色](codedeploy_IAM_role.md)。

## 步骤 1：创建应用程序负载均衡器
<a name="create-blue-green-loadbalancer"></a>

使用蓝绿部署类型的 Amazon ECS 服务需要使用应用程序负载均衡器或网络负载均衡器。本教程使用应用程序负载均衡器。

**创建应用程序负载均衡器**

1. 使用 [create-load-balancer](https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-load-balancer.html) 命令创建应用程序负载均衡器。指定两个不属于同一可用区的子网以及一个安全组。

   ```
   aws elbv2 create-load-balancer \
        --name bluegreen-alb \
        --subnets subnet-abcd1234 subnet-abcd5678 \
        --security-groups sg-abcd1234 \
        --region us-east-1
   ```

   输出包含负载均衡器的 Amazon 资源名称（ARN），格式如下：

   ```
   arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642
   ```

1. 使用 [create-target-group](https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-target-group.html) 命令创建目标组。此目标组将流量路由到服务中的原始任务集。

   ```
   aws elbv2 create-target-group \
        --name bluegreentarget1 \
        --protocol HTTP \
        --port 80 \
        --target-type ip \
        --vpc-id vpc-abcd1234 \
        --region us-east-1
   ```

   输出包含目标组的 ARN，格式如下：

   ```
   arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4
   ```

1. 使用 [create-listener](https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-listener.html) 命令创建负载均衡器侦听器，该侦听器带有将请求转发到目标组的默认规则。

   ```
   aws elbv2 create-listener \
        --load-balancer-arn arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642 \
        --protocol HTTP \
        --port 80 \
        --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4 \
        --region us-east-1
   ```

   输出包含侦听器的 ARN，格式如下：

   ```
   arn:aws:elasticloadbalancing:region:aws_account_id:listener/app/bluegreen-alb/e5ba62739c16e642/665750bec1b03bd4
   ```

## 步骤 2：创建 Amazon ECS 集群
<a name="create-blue-green-cluster"></a>

使用 [create-cluster](https://docs.aws.amazon.com/cli/latest/reference/ecs/create-cluster.html) 命令创建要使用的名为 `tutorial-bluegreen-cluster` 的集群。

```
aws ecs create-cluster \
     --cluster-name tutorial-bluegreen-cluster \
     --region us-east-1
```

输出包含集群的 ARN，格式如下：

```
arn:aws:ecs:region:aws_account_id:cluster/tutorial-bluegreen-cluster
```

## 第 3 步：注册任务定义
<a name="create-blue-green-taskdef"></a>

使用 [register-task-definition](https://docs.aws.amazon.com/cli/latest/reference/ecs/register-task-definition.html) 命令注册与 Fargate 兼容的任务定义。它需要使用 `awsvpc` 网络模式。以下为用于本教程的示例任务定义。

首先，使用以下内容创建名为 `fargate-task.json` 的文件。确保您使用的是任务执行角色的 ARN。有关更多信息，请参阅 [Amazon ECS 任务执行 IAM 角色](task_execution_IAM_role.md)。

```
{
    "family": "sample-fargate", 
    "networkMode": "awsvpc", 
    "containerDefinitions": [
        {
            "name": "sample-app", 
            "image": "public.ecr.aws/docker/library/httpd:latest", 
            "portMappings": [
                {
                    "containerPort": 80, 
                    "hostPort": 80, 
                    "protocol": "tcp"
                }
            ], 
            "essential": true, 
            "entryPoint": [
                "sh",
		"-c"
            ], 
            "command": [
                "/bin/sh -c \"echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p> </div></body></html>' >  /usr/local/apache2/htdocs/index.html && httpd-foreground\""
            ]
        }
    ], 
    "requiresCompatibilities": [
        "FARGATE"
    ], 
    "cpu": "256", 
    "memory": "512"
}
```

然后，使用您创建的 `fargate-task.json` 文件注册任务定义。

```
aws ecs register-task-definition \
     --cli-input-json file://fargate-task.json \
     --region us-east-1
```

## 步骤 4：创建 Amazon ECS 服务
<a name="create-blue-green-service"></a>

使用 [create-service](https://docs.aws.amazon.com/cli/latest/reference/ecs/create-service.html) 命令创建服务。

首先，使用以下内容创建名为 `service-bluegreen.json` 的文件。

```
{
    "cluster": "tutorial-bluegreen-cluster",
    "serviceName": "service-bluegreen",
    "taskDefinition": "tutorial-task-def",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4",
            "containerName": "sample-app",
            "containerPort": 80
        }
    ],
    "launchType": "FARGATE",
    "schedulingStrategy": "REPLICA",
    "deploymentController": {
        "type": "CODE_DEPLOY"
    },
    "platformVersion": "LATEST",
    "networkConfiguration": {
       "awsvpcConfiguration": {
          "assignPublicIp": "ENABLED",
          "securityGroups": [ "sg-abcd1234" ],
          "subnets": [ "subnet-abcd1234", "subnet-abcd5678" ]
       }
    },
    "desiredCount": 1
}
```

然后，使用您创建的 `service-bluegreen.json` 文件创建服务。

```
aws ecs create-service \
     --cli-input-json file://service-bluegreen.json \
     --region us-east-1
```

输出包含该服务的 ARN，格式如下：

```
arn:aws:ecs:region:aws_account_id:service/service-bluegreen
```

## 步骤 5：创建 AWS CodeDeploy 资源
<a name="create-blue-green-codedeploy"></a>

使用以下步骤创建 CodeDeploy 应用程序、CodeDeploy 部署组的应用程序负载均衡器目标组以及 CodeDeploy 部署组。

**创建 CodeDeploy 资源**

1. 使用 [create-application](https://docs.aws.amazon.com/cli/latest/reference/deploy/create-application.html) 命令创建 CodeDeploy 应用程序。指定 `ECS` 计算平台。

   ```
   aws deploy create-application \
        --application-name tutorial-bluegreen-app \
        --compute-platform ECS \
        --region us-east-1
   ```

   输出包含应用程序 ID，格式如下：

   ```
   {
       "applicationId": "b8e9c1ef-3048-424e-9174-885d7dc9dc11"
   }
   ```

1. 使用 [create-target-group](https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-target-group.html) 命令创建另一个应用程序负载均衡器目标组（在创建 CodeDeploy 部署组时将使用该目标组）。

   ```
   aws elbv2 create-target-group \
        --name bluegreentarget2 \
        --protocol HTTP \
        --port 80 \
        --target-type ip \
        --vpc-id "vpc-0b6dd82c67d8012a1" \
        --region us-east-1
   ```

   输出包含目标组的 ARN，格式如下：

   ```
   arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget2/708d384187a3cfdc
   ```

1. 使用 [create-deployment-group](https://docs.aws.amazon.com/cli/latest/reference/deploy/create-deployment-group.html) 命令创建 CodeDeploy 部署组。

   首先，使用以下内容创建名为 `tutorial-deployment-group.json` 的文件。此示例使用您创建的资源。对于 `serviceRoleArn`，指定您的 Amazon ECS CodeDeploy IAM 角色的 ARN。有关更多信息，请参阅 [Amazon ECS CodeDeploy IAM 角色](codedeploy_IAM_role.md)。

   ```
   {
      "applicationName": "tutorial-bluegreen-app",
      "autoRollbackConfiguration": {
         "enabled": true,
         "events": [ "DEPLOYMENT_FAILURE" ]
      },
      "blueGreenDeploymentConfiguration": {
         "deploymentReadyOption": {
            "actionOnTimeout": "CONTINUE_DEPLOYMENT",
            "waitTimeInMinutes": 0
         },
         "terminateBlueInstancesOnDeploymentSuccess": {
            "action": "TERMINATE",
            "terminationWaitTimeInMinutes": 5
         }
      },
      "deploymentGroupName": "tutorial-bluegreen-dg",
      "deploymentStyle": {
         "deploymentOption": "WITH_TRAFFIC_CONTROL",
         "deploymentType": "BLUE_GREEN"
      },
      "loadBalancerInfo": {
         "targetGroupPairInfoList": [
           {
             "targetGroups": [
                {
                    "name": "bluegreentarget1"
                },
                {
                    "name": "bluegreentarget2"
                }
             ],
             "prodTrafficRoute": {
                 "listenerArns": [
                     "arn:aws:elasticloadbalancing:region:aws_account_id:listener/app/bluegreen-alb/e5ba62739c16e642/665750bec1b03bd4"
                 ]
             }
           }
         ]
      },
      "serviceRoleArn": "arn:aws:iam::aws_account_id:role/ecsCodeDeployRole",
      "ecsServices": [
          {
              "serviceName": "service-bluegreen",
              "clusterName": "tutorial-bluegreen-cluster"
          }
      ]
   }
   ```

   然后创建 CodeDeploy 部署组。

   ```
   aws deploy create-deployment-group \
        --cli-input-json file://tutorial-deployment-group.json \
        --region us-east-1
   ```

   输出包含部署组 ID，格式如下：

   ```
   {
       "deploymentGroupId": "6fd9bdc6-dc51-4af5-ba5a-0a4a72431c88"
   }
   ```

## 步骤 6：创建和监控 CodeDeploy 部署
<a name="create-blue-green-verify"></a>

使用以下步骤创建和上传应用程序规范文件（AppSpec 文件）和 CodeDeploy 部署。

**创建和监控 CodeDeploy 部署**

1. 使用以下步骤创建和上传 AppSpec 文件。

   1. 使用 CodeDeploy 部署组的内容创建名为 `appspec.yaml` 的文件。此示例使用您在本教程前面创建的资源。

      ```
      version: 0.0
      Resources:
        - TargetService:
            Type: AWS::ECS::Service
            Properties:
              TaskDefinition: "arn:aws:ecs:region:aws_account_id:task-definition/first-run-task-definition:7"
              LoadBalancerInfo:
                ContainerName: "sample-app"
                ContainerPort: 80
              PlatformVersion: "LATEST"
      ```

   1. 使用 [s3 mb](https://docs.aws.amazon.com/cli/latest/reference/s3/mb.html) 命令为 AppSpec 文件创建 Amazon S3 存储桶。

      ```
      aws s3 mb s3://tutorial-bluegreen-bucket
      ```

   1. 使用 [s3 cp](https://docs.aws.amazon.com/cli/latest/reference/s3/cp.html) 命令将 AppSpec 文件上传到 Amazon S3 存储桶。

      ```
      aws s3 cp ./appspec.yaml s3://tutorial-bluegreen-bucket/appspec.yaml
      ```

1. 使用以下步骤创建 CodeDeploy 部署。

   1. 使用 CodeDeploy 部署的内容创建名为 `create-deployment.json` 的文件。此示例使用您在本教程前面创建的资源。

      ```
      {
          "applicationName": "tutorial-bluegreen-app",
          "deploymentGroupName": "tutorial-bluegreen-dg",
          "revision": {
              "revisionType": "S3",
              "s3Location": {
                  "bucket": "tutorial-bluegreen-bucket",
                  "key": "appspec.yaml",
                  "bundleType": "YAML"
              }
          }
      }
      ```

   1. 使用 [create-deployment](https://docs.aws.amazon.com/cli/latest/reference/deploy/create-deployment.html) 命令创建部署。

      ```
      aws deploy create-deployment \
           --cli-input-json file://create-deployment.json \
           --region us-east-1
      ```

      输出包含部署 ID，格式如下：

      ```
      {
          "deploymentId": "d-RPCR1U3TW"
      }
      ```

   1. 使用 [get-deployment-target](https://docs.aws.amazon.com/cli/latest/reference/deploy/get-deployment-target.html) 命令获取部署的详细信息，并指定上一输出中的 `deploymentId`。

      ```
      aws deploy get-deployment-target \
           --deployment-id "d-IMJU3A8TW" \
           --target-id tutorial-bluegreen-cluster:service-bluegreen \
           --region us-east-1
      ```

      继续检索部署详细信息，直到状态为 `Succeeded`，如以下输出所示。

      ```
      {
          "deploymentTarget": {
              "deploymentTargetType": "ECSTarget",
              "ecsTarget": {
                  "deploymentId": "d-RPCR1U3TW",
                  "targetId": "tutorial-bluegreen-cluster:service-bluegreen",
                  "targetArn": "arn:aws:ecs:region:aws_account_id:service/service-bluegreen",
                  "lastUpdatedAt": 1543431490.226,
                  "lifecycleEvents": [
                      {
                          "lifecycleEventName": "BeforeInstall",
                          "startTime": 1543431361.022,
                          "endTime": 1543431361.433,
                          "status": "Succeeded"
                      },
                      {
                          "lifecycleEventName": "Install",
                          "startTime": 1543431361.678,
                          "endTime": 1543431485.275,
                          "status": "Succeeded"
                      },
                      {
                          "lifecycleEventName": "AfterInstall",
                          "startTime": 1543431485.52,
                          "endTime": 1543431486.033,
                          "status": "Succeeded"
                      },
                      {
                          "lifecycleEventName": "BeforeAllowTraffic",
                          "startTime": 1543431486.838,
                          "endTime": 1543431487.483,
                          "status": "Succeeded"
                      },
                      {
                          "lifecycleEventName": "AllowTraffic",
                          "startTime": 1543431487.748,
                          "endTime": 1543431488.488,
                          "status": "Succeeded"
                      },
                      {
                          "lifecycleEventName": "AfterAllowTraffic",
                          "startTime": 1543431489.152,
                          "endTime": 1543431489.885,
                          "status": "Succeeded"
                      }
                  ],
                  "status": "Succeeded",
                  "taskSetsInfo": [
                      {
                          "identifer": "ecs-svc/9223370493425779968",
                          "desiredCount": 1,
                          "pendingCount": 0,
                          "runningCount": 1,
                          "status": "ACTIVE",
                          "trafficWeight": 0.0,
                          "targetGroup": {
                              "name": "bluegreentarget1"
                          }
                      },
                      {
                          "identifer": "ecs-svc/9223370493423413672",
                          "desiredCount": 1,
                          "pendingCount": 0,
                          "runningCount": 1,
                          "status": "PRIMARY",
                          "trafficWeight": 100.0,
                          "targetGroup": {
                              "name": "bluegreentarget2"
                          }
                      }
                  ]
              }
          }
      }
      ```

## 步骤 7：清理
<a name="create-blue-green-cleanup"></a>

完成本教程后，请清除与本教程关联的资源，以避免对您未使用的资源产生费用。

**清除教程资源**

1. 使用 [delete-deployment-group](https://docs.aws.amazon.com/cli/latest/reference/deploy/delete-deployment-group.html) 命令删除 CodeDeploy 部署组。

   ```
   aws deploy delete-deployment-group \
        --application-name tutorial-bluegreen-app \
        --deployment-group-name tutorial-bluegreen-dg \
        --region us-east-1
   ```

1. 使用 [delete-application](https://docs.aws.amazon.com/cli/latest/reference/deploy/delete-application.html) 命令删除 CodeDeploy 应用程序。

   ```
   aws deploy delete-application \
        --application-name tutorial-bluegreen-app \
        --region us-east-1
   ```

1. 使用 [delete-service](https://docs.aws.amazon.com/cli/latest/reference/ecs/delete-service.html) 命令删除 Amazon ECS 服务。通过使用 `--force` 标记，您可以删除服务，即使它尚未缩减至 0 个任务。

   ```
   aws ecs delete-service \
        --service arn:aws:ecs:region:aws_account_id:service/service-bluegreen \
        --force \
        --region us-east-1
   ```

1. 使用 [delete-cluster](https://docs.aws.amazon.com/cli/latest/reference/ecs/delete-cluster.html) 命令删除 Amazon ECS 集群。

   ```
   aws ecs delete-cluster \
        --cluster tutorial-bluegreen-cluster \
        --region us-east-1
   ```

1. 使用 [s3 rm](https://docs.aws.amazon.com/cli/latest/reference/s3/rm.html) 命令从 Amazon S3 存储桶中删除 AppSpec 文件。

   ```
   aws s3 rm s3://tutorial-bluegreen-bucket/appspec.yaml
   ```

1. 使用 [s3 rb](https://docs.aws.amazon.com/cli/latest/reference/s3/rb.html) 命令删除 Amazon S3 存储桶。

   ```
   aws s3 rb s3://tutorial-bluegreen-bucket
   ```

1. 使用以下 [delete-load-balancer](https://docs.aws.amazon.com/cli/latest/reference/elbv2/delete-load-balancer.html) 命令删除应用程序负载均衡器。

   ```
   aws elbv2 delete-load-balancer \
        --load-balancer-arn arn:aws:elasticloadbalancing:region:aws_account_id:loadbalancer/app/bluegreen-alb/e5ba62739c16e642 \
        --region us-east-1
   ```

1. 使用 [delete-target-group](https://docs.aws.amazon.com/cli/latest/reference/elbv2/delete-target-group.html) 命令删除两个应用程序负载均衡器目标组。

   ```
   aws elbv2 delete-target-group \
        --target-group-arn arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget1/209a844cd01825a4 \
        --region us-east-1
   ```

   ```
   aws elbv2 delete-target-group \
        --target-group-arn arn:aws:elasticloadbalancing:region:aws_account_id:targetgroup/bluegreentarget2/708d384187a3cfdc \
        --region us-east-1
   ```