3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【AWSアカウント不要】ローカル環境でAWS CDKのデプロイ&動作確認をするチュートリアル

Last updated at Posted at 2020-11-24

1. はじめに

1-1. この記事で行うこと

  • LocalStackコンテナをセットアップし、動作確認
  • Node.js環境コンテナ内にAWS CDKプロジェクトを作成
  • AWS CDKプロジェクトをLocalStackへデプロイし、動作確認

1-2. この記事の対象者

  • AWS CDKを本物のAWSを使わずに動かしたい方
  • AWS CDKをすぐに捨てられる環境で色々と試してみたい方

1-3. 動作環境

このチュートリアルは、Bashなどのコマンドが利用でき、DockerとDocker Composeがインストール済みの環境で実行できます。

1-4. 各種のバージョン

2022年2月19日時点で動作確認済のバージョンは以下の通りです。

  • docker
    • Docker version 20.10.0, build 7287ab3
  • docker-compose
    • docker-compose version 1.26.0, build unknown
  • localstack
    • 0.14.0
  • aws-cli
    • aws-cli/1.22.58 Python/3.8.5 Linux/4.4.0-210-generic botocore/1.24.3
  • aws-cdk
    • 1.145.0
  • aws-cdk-local
    • 1.65.8

2. チュートリアル

このチュートリアルではLocalStackというローカルでAWSを擬似的に使用できるツールを用います。詳しい説明は以下の記事をご参考ください。
LocalStack を使って無料で AWS を学ぶ

このLocalStackのDockerコンテナ内に擬似的なAWS環境を立て、そこへAWS CDKをデプロイし、動作確認を行っていきます。

2-1. 最終的なファイル構成

いきなりですが、このチュートリアルが完了した状態のファイル構成です。
以下の「▶︎ 詳細」をクリックしてご確認ください。


$ tree -L 5 -I node_modules
.
├── localstack
│   ├── CHANGELOG.md
│   ├── Dockerfile
│   ├── LICENSE.txt
│   ├── MANIFEST.in
│   ├── Makefile
│   ├── README.md
│   ├── bin
│   │   └── 略
│   ├── doc
│   │   └── 略
│   ├── docker-compose.yml
│   ├── localstack
│   │   └── 略
│   ├── requirements.txt
│   ├── setup.py
│   └── tests
│       └── 略
├── node_workdir
│   ├── Dockerfile
│   ├── app
│   │   └── sample
│   │       ├── README.md
│   │       ├── bin
│   │       │   ├── sample.d.ts
│   │       │   ├── sample.js
│   │       │   └── sample.ts
│   │       ├── cdk.json
│   │       ├── cdk.out
│   │       │   ├── SampleStack.template.json
│   │       │   ├── cdk.out
│   │       │   ├── manifest.json
│   │       │   └── tree.json
│   │       ├── jest.config.js
│   │       ├── lib
│   │       │   ├── sample-stack.d.ts
│   │       │   ├── sample-stack.js
│   │       │   └── sample-stack.ts
│   │       ├── package-lock.json
│   │       ├── package.json
│   │       ├── test
│   │       │   ├── sample.test.d.ts
│   │       │   ├── sample.test.js
│   │       │   └── sample.test.ts
│   │       └── tsconfig.json
│   └── docker-compose.yml
└── s3.txt

2-2. LocalStackのセットアップ

2-2-1. LocalStackをダウンロードする

$ git clone https://github.com/localstack/localstack
Cloning into 'localstack'...
remote: Enumerating objects: 27765, done.
remote: Counting objects: 100% (2217/2217), done.
remote: Compressing objects: 100% (1063/1063), done.
remote: Total 27765 (delta 1355), reused 1793 (delta 1044), pack-reused 25548
Receiving objects: 100% (27765/27765), 12.74 MiB | 21.92 MiB/s, done.
Resolving deltas: 100% (20109/20109), done.

2-2-2. LocalStackコンテナを起動する

$ cd localstack

# デフォルトで存在するDockerネットワーク「bridge」では、他のコンテナからコンテナ名で名前解決できないため、
# 「network_mode: bridge」をコメントアウトし、Dockerネットワーク「localstack_default」が自動生成されるようにする
$ sed -i 's/network_mode: bridge/# network_mode: bridge/' docker-compose.yml

$ docker-compose up -d
Creating network "localstack_default" with the default driver
Pulling localstack (localstack/localstack:)...
latest: Pulling from localstack/localstack
# ・・・(中略)
Digest: sha256:0ab755bf60c1c1ce03aa5adb51e947b04bdac75607cbaea64460802ca3f5de75
Status: Downloaded newer image for localstack/localstack:latest
Creating localstack_main ... done

$ cd ..

2-2-3. LocalStackのステータスを確認する

$ curl http://localhost:4566
{"status": "running"}

https://github.com/localstack/localstack
2020-09-15: A major (breaking) change has been merged in PR #2905 - starting with releases after v0.11.5, all services are now exposed via the edge service (port 4566) only! Please update your client configurations to use this new endpoint.
(DeepLによる翻訳)
PR #2905 に主要な (壊すような) 変更がマージされました - v0.11.5 以降のリリースから、すべてのサービスがエッジサービス (ポート 4566) のみを介して公開されるようになりました! この新しいエンドポイントを使用するために、クライアントの設定を更新してください。

公式githubにport 4566のみで動いてると書いてあるので、curlのポートに:4566を指定しています。
上手く動いていると{"status": "running"}が返ってきます。

2-2-4. aws-cliをインストールする

$ pip install awscli
Collecting awscli
  Downloading awscli-1.22.58-py3-none-any.whl (3.8 MB)
     |████████████████████████████████| 3.8 MB 8.3 MB/s 
# ・・・(中略)
Installing collected packages: pyasn1, rsa, jmespath, python-dateutil, botocore, s3transfer, docutils, awscli
Successfully installed awscli-1.22.58 botocore-1.24.3 docutils-0.15.2 jmespath-0.10.0 pyasn1-0.4.8 python-dateutil-2.8.2 rsa-4.7.2 s3transfer-0.5.1

LocalStackは基本的にはGUIで操作できないのでaws-cliを使用する必要があります。

2-2-5. LocalStack用のaws-cliのconfigureを設定する

$ aws configure set aws_access_key_id dummy --profile local

$ aws configure set aws_secret_access_key dummy --profile local

$ aws configure set region ap-northeast-1 --profile local

LocalStack用なのでaws_access_key_idaws_secret_access_keyは適当な値 (dummy等) で大丈夫です。

2-3. LocalStackの動作確認

Amazon Simple Storage Service (以降、S3) へバケットを登録し、テキストファイルをアップロードできるかを確認します。

2-3-1. S3にバケットを登録する

$ aws s3 mb s3://test-s3 \
  --endpoint-url http://localhost:4566 \
  --profile local
make_bucket: test-s3

2-3-2. S3のバケット一覧を取得する

$ aws s3 ls \
  --endpoint-url http://localhost:4566 \
  --profile local
2022-02-19 06:50:52 test-s3

2-3-3. S3へファイルをコピーする

$ echo "test text" > s3.txt

$ aws s3 cp ./s3.txt s3://test-s3 \
  --endpoint-url http://localhost:4566 \
  --profile local
upload: ./s3.txt to s3://test-s3/s3.txt 

2-3-4. 指定したS3のバケットのファイル一覧を取得する

$ aws s3 ls test-s3 \
  --endpoint-url http://localhost:4566 \
  --profile local
2022-02-19 06:53:10         10 s3.txt

2-4. Node.js環境のセットアップ

今回はAWS CDKをTypeScriptを使用して作成するので、Node.js環境が必要となります。
特別な理由はないですが、Node.js環境もDockerで準備していきます。

2-4-1. Node.js環境作業ディレクトリを作成する

$ mkdir node_workdir && cd node_workdir

$ mkdir app

2-4-2. Dockerfileを作成する

$ vi Dockerfile

以下のファイルの中身をコピペしてください。

Dockerfile
FROM node:14-alpine
WORKDIR /app
CMD ["sh"]

2-4-3. docker-compose.ymlを作成する

$ vi docker-compose.yml

以下のファイルの中身をコピペしてください。

docker-compose.yml
version: '3'

services:
  app:
    build: .
    image: my-node-image
    networks:
      - localstack_default
    volumes:
      - ./app:/app
    environment:
      - LOCALSTACK_HOSTNAME=localstack_main
    tty: true

networks:
  localstack_default:
    external: true

networksに「2-2-2. LocalStackコンテナを起動する」で生成されたDockerネットワークをlocalstack_default指定しています。
また、environmentLOCALSTACK_HOSTNAMEに「2-2-2. LocalStackコンテナを起動する」で生成されたコンテナ名のlocalstack_mainを指定することで、後ほど出てくるcdklocalコマンド実行時にLocalStackコンテナへ接続可能となります。

2-4-4. Node.js環境コンテナを起動する

$ docker-compose up -d
Building app
Step 1/3 : FROM node:14-alpine
14-alpine: Pulling from library/node
59bf1c3509f3: Pull complete
39aa0d19fbe1: Pull complete
f0b94cdce0f3: Pull complete
664688b94c15: Pull complete
Digest: sha256:9a2aa545388a135b496bd55cef2be920b96c4526c99c140170e05a8de3fce653
Status: Downloaded newer image for node:14-alpine
 ---> 755b96824e40
Step 2/3 : WORKDIR /app
 ---> Running in 4cbba13c1a78
Removing intermediate container 4cbba13c1a78
 ---> 852f95361e20
Step 3/3 : CMD ["sh"]
 ---> Running in ce69f75cc813
Removing intermediate container ce69f75cc813
 ---> 6776a5c22ebe
Successfully built 6776a5c22ebe
Successfully tagged my-node-image:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating node_workdir_app_1 ... done

コンテナの起動に成功するとCreating node_workdir_app_1 ... doneのように生成されたコンテナの名前が出力されます。

2-4-5. Node.js環境コンテナの中に入る

$ docker exec -it node_workdir_app_1 sh
/app # 

先ほど生成されたコンテナの名前を指定してdocker execコマンドを実行します。
オプションに-itを、コマンドにshを指定しているので、コンテナの中に入ることができます。
Dockerコンテナに入る方法については以下の記事をご参考ください。
docker exec -itって実際は何をしてるの?【90日目】

なお、「2-5. AWS CDKプロジェクトの作成〜デプロイ」は、このコンテナの中に入っている状態で進めていきます。

2-5. AWS CDKプロジェクトの作成〜デプロイ

aws-cdkでプロジェクトを作成し、aws-cdk-localでLocalStackへデプロイしていきます。

2-5-1. aws-cdkとaws-cdk-localをグローバルインストールする

/app # npm install -g aws-cdk@1
/usr/local/bin/cdk -> /usr/local/lib/node_modules/aws-cdk/bin/cdk
+ aws-cdk@1.145.0
added 217 packages from 231 contributors in 11.003s

/app # npm install -g aws-cdk-local
/usr/local/bin/cdklocal -> /usr/local/lib/node_modules/aws-cdk-local/bin/cdklocal
+ aws-cdk-local@1.65.8
added 2 packages from 1 contributor in 0.874s

補足ですが、下記のIssueを参考にし、npm install -g aws-cdk@1とすることで、aws-cdkのv2がインストールされないようにしています。

「[Error: The stack named CDKToolkit failed to deploy: CREATE_FAILED (Deployment failed) #57] (https://github.com/localstack/aws-cdk-local/issues/57)」

Localstack Free version doesn't support ECR so you must upgrade to Pro-version if you want to use aws-cdk@2

2-5-2. サンプルcdkプロジェクト用ディレクトリを作成し、移動する

/app # mkdir sample && cd sample

2-5-3. サンプルcdkプロジェクトを作成する

/app/sample # cdk init sample-app --language=typescript
Applying project template sample-app for typescript
# Welcome to your CDK TypeScript project

You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`SampleStack`)
which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

## Tutorial

See [this useful workshop](https://cdkworkshop.com/20-typescript.html) on working with the AWS CDK for Typescript projects.

## Useful commands

* `npm run build`   compile typescript to js
* `npm run watch`   watch for changes and compile
* `npm run test`    perform the jest unit tests
* `cdk deploy`      deploy this stack to your default AWS account/region
* `cdk diff`        compare deployed stack with current state
* `cdk synth`       emits the synthesized CloudFormation template

Initializing a new git repository...
/bin/sh: git: not found
Unable to initialize git repository for your project.
Executing npm install...
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated uuid@3.3.2: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^2.1.2 (node_modules/jest-haste-map/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN sample@0.1.0 No repository field.
npm WARN sample@0.1.0 No license field.

✅ All done!
*************************************************************************************************************************************************
*** Newer version of CDK is available [2.13.0]                                                                                                ***
*** Information about upgrading from version 1.x to version 2.x is available here: https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html ***
*** Upgrade recommended (npm install -g aws-cdk)                                                                                              ***
*************************************************************************************************************************************************

cdk initコマンドで--language=typescriptを指定してTypeScriptでファイルが生成されるようにしています。
また、sample-appと指定すると、簡単なサンプルを含んだプロジェクトが生成されます。出力の中に簡単なサンプルについての説明があります。

You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`SampleStack`)
which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic.

(DeepLによる翻訳)
このプロジェクトの内容を探ってみてください。スタック (SampleStack) のインスタンスを持つ CDK アプリのデモを行います。
これは、Amazon SNSのトピックにサブスクライブされているAmazon SQSキューを含んでいます。

生成された簡単なサンプルのソースコードを、ちょっとだけ見てみましょう。

/app/sample # vi ./lib/sample-stack.ts
./lib/sample-stack.ts
import * as sns from '@aws-cdk/aws-sns';
import * as subs from '@aws-cdk/aws-sns-subscriptions';
import * as sqs from '@aws-cdk/aws-sqs';
import * as cdk from '@aws-cdk/core';

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

    const queue = new sqs.Queue(this, 'SampleQueue', {
      visibilityTimeout: cdk.Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'SampleTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}

Amazon Simple Queue Service (以降、SQS) のキューとAmazon Simple Notification Service (以降、SNS)のトピックとサブスクリプションが定義されています。SNSトピックからメッセージを発行すると、サブスクリプションとして登録されているキューに登録されるようになっています。

2-5-4. TypeScriptファイルをJavaScriptファイルにトランスパイルする

/app/sample # npm run build

> sample@0.1.0 build /app/sample
> tsc

ブラウザやNode.js環境はJavaScriptしか実行できないため、TypeScriptファイルをJavaScriptファイルへ必ず変換しましょう。

2-5-5. デプロイ先のリージョンを環境変数として設定する

/app/sample # export AWS_DEFAULT_REGION=ap-northeast-1

2-5-6. 初めてデプロイする場合、デプロイ用のS3バケットなどを作成する

/app/sample # cdklocal bootstrap
 ⏳  Bootstrapping environment aws://000000000000/ap-northeast-1...
CDKToolkit: creating CloudFormation changeset...



 ✅  Environment aws://000000000000/ap-northeast-1 bootstrapped.

2-5-7. LocalStackへデプロイする

/app/sample # cdklocal deploy

✨  Synthesis time: 4.55s

This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────────┬────────┬─────────────────┬─────────────────────────┬───────────────────────────┐
│   │ Resource                │ Effect │ Action          │ Principal               │ Condition                 │
├───┼─────────────────────────┼────────┼─────────────────┼─────────────────────────┼───────────────────────────┤
│ + │ ${SampleQueue.Arn}      │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.c │ "ArnEquals": {│   │                         │        │                 │ om                      │   "aws:SourceArn": "${Sam │
│   │                         │        │                 │                         │ pleTopic}"                │
│   │                         │        │                 │                         │ }                         │
└───┴─────────────────────────┴────────┴─────────────────┴─────────────────────────┴───────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
SampleStack: deploying...
SampleStack: creating CloudFormation changeset...




 ✅  SampleStack

✨  Deployment time: 5.26s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:000000000000:stack/SampleStack/8775e216

✨  Total time: 9.81s

途中でDo you wish to deploy these changes (y/n)?と入力を求められるので、問題がなければyを入力しましょう。

2-5-8. Node.js環境コンテナから抜け出す

/app/sample # exit
$ 

2-6. AWS CDKからLocalStackへデプロイされたリソースの動作確認

「リソースが生成されているか」と「SNSトピックからメッセージを発行し、サブスクリプションとして登録されているキューに登録されるか」を確認していきます。

2-6-1. SQSキューの一覧を取得する

$ aws sqs list-queues \
  --endpoint-url http://localhost:4566 \
  --profile local
{
    "QueueUrls": [
        "http://localhost:4566/000000000000/SampleStack-SampleQueue49AAAEFF-c0802178"
    ]
}

SQSキューがちゃんと生成されていますね。
ちなみに、QueueUrlsに表示されている値は各自の環境で異ります。

2-6-2. 指定したSQSキューの詳細を取得する

$ aws sqs get-queue-attributes \
  --queue-url http://localhost:4566/000000000000/SampleStack-SampleQueue49AAAEFF-c0802178 \
  --attribute-names All \
  --endpoint-url http://localhost:4566 \
  --profile local
{
    "Attributes": {
        "ApproximateNumberOfMessages": "0",
        "ApproximateNumberOfMessagesDelayed": "0",
        "ApproximateNumberOfMessagesNotVisible": "0",
        "CreatedTimestamp": "1645254466.340205",
        "DelaySeconds": "0",
        "LastModifiedTimestamp": "1645254467.617937",
        "MaximumMessageSize": "262144",
        "MessageRetentionPeriod": "345600",
        "QueueArn": "arn:aws:sqs:ap-northeast-1:000000000000:SampleStack-SampleQueue49AAAEFF-c0802178",
        "Policy": "{\"Statement\": [{\"Action\": \"sqs:SendMessage\", \"Condition\": {\"ArnEquals\": {\"aws:SourceArn\": \"arn:aws:sns:ap-northeast-1:000000000000:SampleStack-SampleTopic5FE9B5DC-c2e948f9\"}}, \"Effect\": \"Allow\", \"Principal\": {\"Service\": \"sns.amazonaws.com\"}, \"Resource\": \"arn:aws:sqs:ap-northeast-1:000000000000:SampleStack-SampleQueue49AAAEFF-c0802178\"}], \"Version\": \"2012-10-17\"}",
        "ReceiveMessageWaitTimeSeconds": "0",
        "VisibilityTimeout": "300"
    }
}

--queue-urlに指定する値は「2-6-1. SQSキューの一覧を取得する」で取得した各自の値を指定してください。
今回はとりあえず出力結果のうち、次の一項目だけ確認します。ApproximateNumberOfMessages (キューから取得可能なメッセージのおおよその数) が"0"になっていることが確認できましたでしょうか。

2-6-3. SNSトピックの一覧を取得する

$ aws sns list-topics \
  --endpoint-url http://localhost:4566 \
  --profile local
{
    "Topics": [
        {
            "TopicArn": "arn:aws:sns:ap-northeast-1:000000000000:SampleStack-SampleTopic5FE9B5DC-c2e948f9"
        }
    ]
}

SNSトピックもちゃんと生成されていますね。
TopicArnも同様に表示されている値は各自の環境で異ります。

2-6-4. 指定したSNSトピックへメッセージを発行する

$ aws sns publish \
  --message test_message \
  --topic-arn arn:aws:sns:ap-northeast-1:000000000000:SampleStack-SampleTopic5FE9B5DC-c2e948f9 \
  --endpoint-url http://localhost:4566 \
  --profile local
{
    "MessageId": "47899304-fe28-4c3a-b7d0-d35a41c44202"
}

--topic-arnに指定する値は「2-6-3. SNSトピックの一覧を取得する」で取得した各自の値を指定してください。
発行に成功するとMessageIdが入ったJSONが返ってきます。

2-6-5. 指定したSQSキューの詳細を取得し、ApproximateNumberOfMessagesを確認する

$ aws sqs get-queue-attributes \
  --queue-url http://localhost:4566/000000000000/SampleStack-SampleQueue49AAAEFF-c0802178 \
  --attribute-names All \
  --endpoint-url http://localhost:4566 \
  --profile local
{
    "Attributes": {
        "ApproximateNumberOfMessages": "1",
        "ApproximateNumberOfMessagesDelayed": "0",
        "ApproximateNumberOfMessagesNotVisible": "0",
        "CreatedTimestamp": "1645254466.340205",
        "DelaySeconds": "0",
        "LastModifiedTimestamp": "1645254467.617937",
        "MaximumMessageSize": "262144",
        "MessageRetentionPeriod": "345600",
        "QueueArn": "arn:aws:sqs:ap-northeast-1:000000000000:SampleStack-SampleQueue49AAAEFF-c0802178",
        "Policy": "{\"Statement\": [{\"Action\": \"sqs:SendMessage\", \"Condition\": {\"ArnEquals\": {\"aws:SourceArn\": \"arn:aws:sns:ap-northeast-1:000000000000:SampleStack-SampleTopic5FE9B5DC-c2e948f9\"}}, \"Effect\": \"Allow\", \"Principal\": {\"Service\": \"sns.amazonaws.com\"}, \"Resource\": \"arn:aws:sqs:ap-northeast-1:000000000000:SampleStack-SampleQueue49AAAEFF-c0802178\"}], \"Version\": \"2012-10-17\"}",
        "ReceiveMessageWaitTimeSeconds": "0",
        "VisibilityTimeout": "300"
    }
}

「2-6-2. 指定したSQSキューの詳細を取得する」と同じコマンドを実行しましょう。
ApproximateNumberOfMessages (キューから取得可能なメッセージのおおよその数) が"1"になっていたら、SNSトピックから発行されたメッセージがSQSキューに登録される処理がうまくいった証拠です!

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?