

# AWS CDK を使用した Amazon ECS リソースの作成
<a name="tutorial-ecs-web-server-cdk"></a>

AWS Cloud Development Kit (AWS CDK) は、選択したプログラミング言語を使用して、AWS クラウドインフラストラクチャの定義に使用できる、Infrastructure as Code (IAC) フレームワークです。独自のクラウドインフラストラクチャを定義するには、最初に、(CDK でサポートされている言語のいずれかで) 1 つ以上のスタックが含まれるアプリケーションを記述します。次に、それを CloudFormation テンプレートに合成して、AWS アカウント にリソースをデプロイします。このトピックのステップに従って、Amazon Elastic Container Service (Amazon ECS) と Fargate 上の AWS CDK で、コンテナ化されたウェブサーバーをデプロイしてください。

CDK に含まれる AWS 構成ライブラリは、AWS のサービス が提供するリソースのモデル化に使用できるモジュールを提供します。一般的なサービスでは、ライブラリが、スマートなデフォルトとベストプラクティスを持つ厳選された構成を提供します。これらのモジュールの 1 つである `[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) 構成を使用します。この構成は、Application Load Balancer の背後にある Fargate に Amazon ECS サービスをデプロイします。この `aws-ecs-patterns` モジュールには、Network Load Balancer を使用したり、Amazon EC2 で実行したりする構成も含まれています。

このタスクを開始する前に、AWS CDK 開発環境をセットアップし、次のコマンドを実行して AWS CDK をインストールします。AWS CDK 開発環境のセットアップ方法については、「[Getting Started With the AWS CDK - Prerequisites](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 上のコンテナ化されたウェブサーバーを定義する](#ecs-web-server-cdk-step-2)
+ [ステップ 3: ウェブサーバーをテストする](#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 では、**[File]** (ファイル) > **[Import]** (インポート) > **[Maven]** > **[Existing Maven Projects]** (既存の 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-cdk-lib` と呼ばれる単一パッケージですべての AWS のサービス の安定した構成を含んでいます。このパッケージは、プロジェクト開始時に依存関係としてインストールされます。特定のプログラミング言語で作業する場合、パッケージはプロジェクトを初めて構築するときにインストールされます。このトピックでは、Amazon ECS の使用時に高レベルの抽象化を提供する、Amazon ECS Patterns 構成の使用方法について説明します。このモジュールは、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 上のコンテナ化されたウェブサーバーを定義する
<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 ウェブアプリケーションが含まれています。

作成した 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 Public Gallery から取得したコンテナイメージ: `amazon/amazon-ecs-sample`。
+ ロードバランサーが公開アドレスを持ち、インターネットからアクセスできる事実といった、その他の関連情報。

 AWS CDK は、次のリソースを含む、ウェブサーバーのデプロイに必要なすべてのリソースを作成します。この例では、これらのリソースは省略されています。
+ 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 は複数のリソースを作成します。デプロイからの出力の末尾数行に、ロードバランサーの公開ホスト名と、新しいウェブサーバーの 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: ウェブサーバーをテストする
<a name="ecs-web-server-cdk-step-3"></a>

デプロイ出力から URL をコピーし、ウェブブラウザに貼り付けます。ウェブサーバーからの次のウェルカムメッセージが表示されます。

![\[Amazon ECS サンプルアプリケーションのスクリーンショット。出力は「Amazon ECS」を示しています。\]](http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/images/simple-php-app-congrats.png)


## ステップ 4: クリーンアップする
<a name="ecs-web-server-cdk-step-4"></a>

ウェブサーバーの使用が終了したら、CDK を使用して `cdk destroy` コマンドをアプリケーションのメインディレクトリで実行し、サービスを終了します。これにより、将来的に想定外の請求が発生するのを防げます。

## 次のステップ
<a name="ecs-web-server-cdk-next-steps"></a>

AWS CDK を使用して AWS インフラストラクチャを開発する方法の詳細については、「[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)