

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 建立 Amazon ECS 資源 AWS CDK
<a name="tutorial-ecs-web-server-cdk"></a>

 AWS Cloud Development Kit (AWS CDK) 是Infrastructure-as-Code (IAC) 架構，您可以使用您選擇的程式設計語言來定義 AWS 雲端基礎設施。若要定義您自己的雲端基礎設施，要先編寫包含一個或更多堆疊的應用程式 (使用 CDK 支援的其中一種語言)。然後，您將它合成到 CloudFormation 範本，並將您的 資源部署到 AWS 帳戶。請依照本主題中的步驟，使用 Amazon Elastic Container Service (Amazon ECS) 和 Fargate AWS CDK 上的 部署容器化 Web 伺服器。

隨附於 CDK 的 AWS 建構程式庫提供模組，可讓您用來建立 AWS 服務 所提供資源的模型。針對熱門服務，程式庫會提供具有智能預設和最佳實務的彙整建構。其中的模組，特別是 `[aws-ecs-patterns](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns-readme.html)`，提供了高階抽象概念，讓您可以僅憑幾行程式碼行定義您的容器化服務和所有必要的支援資源。

本主題使用 [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html) 建構。此建構會在 Fargate 上一個 Application Load Balancer 的後方部署 Amazon ECS 服務。`aws-ecs-patterns` 模組還包含採用 Network Load Balancer 並在 Amazon EC2 上執行的建構。

開始此任務之前，請先設定您的 AWS CDK 開發環境，然後執行下列命令 AWS CDK 來安裝 。如需如何設定開發 AWS CDK 環境的指示，請參閱 [AWS CDK - 先決條件入門](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_prerequisites)。

```
npm install -g aws-cdk
```

**注意**  
這些說明假設您使用 AWS CDK v2。

**Topics**
+ [步驟 1：設定您的 AWS CDK 專案](#ecs-web-server-cdk-step-1)
+ [步驟 2：使用 AWS CDK 在 Fargate 上定義容器化 Web 伺服器](#ecs-web-server-cdk-step-2)
+ [步驟 3：測試 Web 伺服器](#ecs-web-server-cdk-step-3)
+ [步驟 4：清理](#ecs-web-server-cdk-step-4)
+ [後續步驟](#ecs-web-server-cdk-next-steps)

## 步驟 1：設定您的 AWS CDK 專案
<a name="ecs-web-server-cdk-step-1"></a>

為您的新 AWS CDK 應用程式建立目錄並初始化專案。

------
#### [ TypeScript ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language typescript
```

------
#### [ JavaScript ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language javascript
```

------
#### [ Python ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language python
```

專案啟動後，請啟用專案的虛擬環境，並安裝 AWS CDK的基準相依性。

```
source .venv/bin/activate
python -m pip install -r requirements.txt
```

------
#### [ Java ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language java
```

將此 Maven 專案匯入您的 Java IDE。例如，在 Eclipse 中，使用**檔案** > **匯入** > **Maven** > **現有的 Maven 專案**。

------
#### [ C\$1 ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language csharp
```

------
#### [ Go ]

```
mkdir hello-ecs
cd hello-ecs
cdk init --language go
```

------

**注意**  
 AWS CDK 應用程式範本使用專案目錄的名稱來產生來源檔案和類別的名稱。在此範例中，目錄名為 `hello-ecs`。若使用不同的專案目錄名稱，您的應用程式將與這些說明不相符。

AWS CDK v2 包含名為 的單一套件 AWS 服務 中所有 的穩定建構`aws-cdk-lib`。在初始化專案時，此套件會安裝為相依性套件。使用某些程式設計語言時，套件會在您第一次建置專案時安裝。本主題涵蓋了如何使用 Amazon ECS 模式建構，該建構提供了用於 Amazon ECS 的高階抽象概念。此模組依靠 Amazon ECS 建構和其他建構，來佈建您 Amazon ECS 應用程式所需的來源。

用於將這些程式庫匯入 CDK 應用程式的名稱可能會略有不同，具體取決於您使用的程式設計語言。以下是在每種支援的 CDK 程式設計語言中使用的名稱，供您參考。

------
#### [ TypeScript ]

```
aws-cdk-lib/aws-ecs
aws-cdk-lib/aws-ecs-patterns
```

------
#### [ JavaScript ]

```
aws-cdk-lib/aws-ecs
aws-cdk-lib/aws-ecs-patterns
```

------
#### [ Python ]

```
aws_cdk.aws_ecs
aws_cdk.aws_ecs_patterns
```

------
#### [ Java ]

```
software.amazon.awscdk.services.ecs
software.amazon.awscdk.services.ecs.patterns
```

------
#### [ C\$1 ]

```
Amazon.CDK.AWS.ECS
Amazon.CDK.AWS.ECS.Patterns
```

------
#### [ Go ]

```
github.com/aws/aws-cdk-go/awscdk/v2/awsecs
github.com/aws/aws-cdk-go/awscdk/v2/awsecspatterns
```

------

## 步驟 2：使用 AWS CDK 在 Fargate 上定義容器化 Web 伺服器
<a name="ecs-web-server-cdk-step-2"></a>

使用容器映像 [https://gallery.ecr.aws/ecs-sample-image/amazon-ecs-sample](https://gallery.ecr.aws/ecs-sample-image/amazon-ecs-sample)。此映像包含在 Ngingx 上執行的 PHP Web 應用程式。

在您建立的 AWS CDK 專案中，編輯包含堆疊定義的檔案，以類似下列其中一個範例。

**注意**  
堆疊是部署的單位。所有資源必須在同一堆疊中，而一個堆疊中的所有資源都要部署在一起。若某資源無法部署，任何已部署的其他資源將會復原。 AWS CDK 應用程式可以包含多個堆疊，而一個堆疊中的資源可以參考另一個堆疊中的資源。

------
#### [ TypeScript ]

更新 `lib/hello-ecs-stack.ts` 使其相似於以下內容。

```
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns';

export class HelloEcsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new ecsp.ApplicationLoadBalancedFargateService(this, 'MyWebServer', {
      taskImageOptions: {
        image: ecs.ContainerImage.fromRegistry('public.ecr.aws/ecs-sample-image/amazon-ecs-sample:latest'),
      },
      publicLoadBalancer: true
    });
  }
}
```

------
#### [ JavaScript ]

更新 `lib/hello-ecs-stack.js` 使其相似於以下內容。

```
const cdk = require('aws-cdk-lib');
const { Construct } = require('constructs');
const ecs = require('aws-cdk-lib/aws-ecs');
const ecsp = require('aws-cdk-lib/aws-ecs-patterns');

class HelloEcsStack extends cdk.Stack {
  constructor(scope = Construct, id = string, props = cdk.StackProps) {
    super(scope, id, props);

    new ecsp.ApplicationLoadBalancedFargateService(this, 'MyWebServer', {
      taskImageOptions: {
        image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
      },
      publicLoadBalancer: true
    });
  }
}

module.exports = { HelloEcsStack }
```

------
#### [ Python ]

更新 `hello-ecs/hello_ecs_stack.py` 使其相似於以下內容。

```
import aws_cdk as cdk
from constructs import Construct

import aws_cdk.aws_ecs as ecs
import aws_cdk.aws_ecs_patterns as ecsp

class HelloEcsStack(cdk.Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        ecsp.ApplicationLoadBalancedFargateService(self, "MyWebServer",
            task_image_options=ecsp.ApplicationLoadBalancedTaskImageOptions(
                image=ecs.ContainerImage.from_registry("amazon/amazon-ecs-sample")),
            public_load_balancer=True
        )
```

------
#### [ Java ]

更新 `src/main/java/com.myorg/HelloEcsStack.java` 使其相似於以下內容。

```
package com.myorg;

import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;

import software.amazon.awscdk.services.ecs.ContainerImage;
import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedFargateService;
import software.amazon.awscdk.services.ecs.patterns.ApplicationLoadBalancedTaskImageOptions;

public class HelloEcsStack extends Stack {
    public HelloEcsStack(final Construct scope, final String id) {
        this(scope, id, null);
    }

    public HelloEcsStack(final Construct scope, final String id, final StackProps props) {
        super(scope, id, props);

        ApplicationLoadBalancedFargateService.Builder.create(this, "MyWebServer")
        	.taskImageOptions(ApplicationLoadBalancedTaskImageOptions.builder()
        			.image(ContainerImage.fromRegistry("amazon/amazon-ecs-sample"))
        			.build())
        	.publicLoadBalancer(true)
        	.build();        
    }
}
```

------
#### [ C\$1 ]

更新 `src/HelloEcs/HelloEcsStack.cs` 使其相似於以下內容。

```
using Amazon.CDK;
using Constructs;
using Amazon.CDK.AWS.ECS;
using Amazon.CDK.AWS.ECS.Patterns;
namespace HelloEcs
{
    public class HelloEcsStack : Stack
    {
        internal HelloEcsStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            new ApplicationLoadBalancedFargateService(this, "MyWebServer",
                new ApplicationLoadBalancedFargateServiceProps
                {
                    TaskImageOptions = new ApplicationLoadBalancedTaskImageOptions
                    {
                        Image = ContainerImage.FromRegistry("amazon/amazon-ecs-sample")
                    },
                    PublicLoadBalancer = true
                });
        }
    }
}
```

------
#### [ Go ]

更新 `hello-ecs.go` 使其相似於以下內容。

```
package main

import (
	"github.com/aws/aws-cdk-go/awscdk/v2"
	// "github.com/aws/aws-cdk-go/awscdk/v2/awssqs"
	"github.com/aws/aws-cdk-go/awscdk/v2/awsecs"
	"github.com/aws/aws-cdk-go/awscdk/v2/awsecspatterns"
	"github.com/aws/constructs-go/constructs/v10"
	"github.com/aws/jsii-runtime-go"
)

type HelloEcsStackProps struct {
	awscdk.StackProps
}

func NewHelloEcsStack(scope constructs.Construct, id string, props *HelloEcsStackProps) awscdk.Stack {
	var sprops awscdk.StackProps
	if props != nil {
		sprops = props.StackProps
	}
	stack := awscdk.NewStack(scope, &id, &sprops)

	// The code that defines your stack goes here

	// example resource
	// queue := awssqs.NewQueue(stack, jsii.String("HelloEcsQueue"), &awssqs.QueueProps{
	// 	VisibilityTimeout: awscdk.Duration_Seconds(jsii.Number(300)),
	// })
	res := awsecspatterns.NewApplicationLoadBalancedFargateService(stack, jsii.String("MyWebServer"),
		&awsecspatterns.ApplicationLoadBalancedFargateServiceProps{
			TaskImageOptions: &awsecspatterns.ApplicationLoadBalancedTaskImageOptions{
				Image: awsecs.ContainerImage_FromRegistry(jsii.String("amazon/amazon-ecs-sample"), &awsecs.RepositoryImageProps{}),
			},
		},
	)
	awscdk.NewCfnOutput(stack, jsii.String("LoadBalancerDNS"), &awscdk.CfnOutputProps{Value: res.LoadBalancer().LoadBalancerDnsName()})

	return stack
}

func main() {
	defer jsii.Close()

	app := awscdk.NewApp(nil)

	NewHelloEcsStack(app, "HelloEcsStack", &HelloEcsStackProps{
		awscdk.StackProps{
			Env: env(),
		},
	})

	app.Synth(nil)
}

// env determines the AWS environment (account+region) in which our stack is to
// be deployed. For more information see: https://docs.aws.amazon.com/cdk/latest/guide/environments.html
func env() *awscdk.Environment {
	// If unspecified, this stack will be "environment-agnostic".
	// Account/Region-dependent features and context lookups will not work, but a
	// single synthesized template can be deployed anywhere.
	//---------------------------------------------------------------------------
	return nil

	// Uncomment if you know exactly what account and region you want to deploy
	// the stack to. This is the recommendation for production stacks.
	//---------------------------------------------------------------------------
	// return &awscdk.Environment{
	//  Account: jsii.String("123456789012"),
	//  Region:  jsii.String("us-east-1"),
	// }

	// Uncomment to specialize this stack for the AWS Account and Region that are
	// implied by the current CLI configuration. This is recommended for dev
	// stacks.
	//---------------------------------------------------------------------------
	// return &awscdk.Environment{
	//  Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")),
	//  Region:  jsii.String(os.Getenv("CDK_DEFAULT_REGION")),
	// }
}
```

------

前面的簡短程式碼片段包括以下內容：
+ 服務的邏輯名稱：`MyWebServer`。
+ 從 Amazon ECR 公共映像庫中取得的容器映像：`amazon/amazon-ecs-sample`。
+ 其他相關資訊，例如負載平衡器具有公有地址，而且可以從網際網路存取的事實。

 AWS CDK 將建立部署 Web 伺服器所需的所有資源，包括下列資源。此範例中省略了這些資源。
+ Amazon ECS 叢集 
+ Amazon VPC 和 Amazon EC2 執行個體 
+  Auto Scaling 群組
+  Application Load Balancer 
+  (IAM) 角色和政策 

 部分自動佈建的資源是由堆疊中定義的全部 Amazon ECS 服務進行共享。

保存來源檔案，然後在應用程式的主目錄中執行 `cdk synth` 指令。會 AWS CDK 執行應用程式並從中合成 CloudFormation 範本，然後顯示範本。範本是大約 600 行的 YAML 檔案。檔案的開頭如此處所示。您的範本可能與此範例不同。

```
Resources:
  MyWebServerLB3B5FD3AB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      LoadBalancerAttributes:
        - Key: deletion_protection.enabled
          Value: "false"
      Scheme: internet-facing
      SecurityGroups:
        - Fn::GetAtt:
            - MyWebServerLBSecurityGroup01B285AA
            - GroupId
      Subnets:
        - Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1Subnet3C273B99
        - Ref: EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2Subnet95FF715A
      Type: application
    DependsOn:
      - EcsDefaultClusterMnL3mNNYNVpcPublicSubnet1DefaultRouteFF4E2178
      - EcsDefaultClusterMnL3mNNYNVpcPublicSubnet2DefaultRouteB1375520
    Metadata:
      aws:cdk:path: HelloEcsStack/MyWebServer/LB/Resource
  MyWebServerLBSecurityGroup01B285AA:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Automatically created Security Group for ELB HelloEcsStackMyWebServerLB06757F57
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          Description: Allow from anyone on port 80
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
      VpcId:
        Ref: EcsDefaultClusterMnL3mNNYNVpc7788A521
    Metadata:
      aws:cdk:path: HelloEcsStack/MyWebServer/LB/SecurityGroup/Resource
# and so on for another few hundred lines
```

若要在 中部署服務 AWS 帳戶，請在應用程式的主目錄中執行 `cdk deploy`命令。系統會要求您核准 AWS CDK 產生的 IAM 政策。

部署需要幾分鐘的時間，在此期間 AWS CDK 會建立數個資源。部署輸出的最後幾行包含負載平衡器的公開主機名稱，和您的新 Web 伺服器 URL。如下所示：

```
Outputs:
HelloEcsStack.MyWebServerLoadBalancerDNSXXXXXXX = Hello-MyWeb-ZZZZZZZZZZZZZ-ZZZZZZZZZZ.us-west-2.elb.amazonaws.com
HelloEcsStack.MyWebServerServiceURLYYYYYYYY = http://Hello-MyWeb-ZZZZZZZZZZZZZ-ZZZZZZZZZZ.us-west-2.elb.amazonaws.com
```

## 步驟 3：測試 Web 伺服器
<a name="ecs-web-server-cdk-step-3"></a>

從部署輸出複製 URL，並貼到您的 Web 瀏覽器。此時會顯示下列來自 Web 伺服器的歡迎使用訊息。

![\[Amazon ECS 範例應用程式的螢幕擷取畫面。輸出指示 "Amazon ECS"。\]](http://docs.aws.amazon.com/zh_tw/AmazonECS/latest/developerguide/images/simple-php-app-congrats.png)


## 步驟 4：清理
<a name="ecs-web-server-cdk-step-4"></a>

在您完成使用 Web 伺服器之後，於應用程式的主目錄中執行 `cdk destroy` 指令來用 CDK 結束服務。這樣做可以防止您未來意外產生任何費用。

## 後續步驟
<a name="ecs-web-server-cdk-next-steps"></a>

若要進一步了解如何使用 開發 AWS 基礎設施 AWS CDK，請參閱 [AWS CDK 開發人員指南](https://docs.aws.amazon.com/cdk/v2/guide/)。

如需以您選擇的語言撰寫 AWS CDK 應用程式的資訊，請參閱以下內容：

------
#### [ TypeScript ]

[以 TypeScript 使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-typescript.html)

------
#### [ JavaScript ]

[以 JavaScript 使用 AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-javascript.html)

------
#### [ Python ]

[在 Python AWS CDK 中使用](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-python.html)

------
#### [ Java ]

[在 Java AWS CDK 中使用](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-java.html)

------
#### [ C\$1 ]

[在 C\$1 AWS CDK 中使用](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-csharp.html)

------
#### [ Go ]

[在 Go AWS CDK 中使用](https://docs.aws.amazon.com/cdk/v2/guide/work-with-cdk-go.html)

------

如需本主題中使用的 AWS 建構程式庫模組的詳細資訊，請參閱下列 AWS CDK API 參考概觀。
+ [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs-readme.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs-readme.html)
+  [https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns-readme.html](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns-readme.html)