dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

東京大学の講義「AWSによるクラウド入門」をTypeScriptで写経した

AWSによるクラウド入門

少し前に話題になっていた東京大学の講義資料をやってみたので、内容、感想などメモ。

講義で使用するソースコードはすべて Python で書かれていますが、自分が実際に使うとしたら TypeScript で書くだろうなと思ったので TypeScript で写経しました。
が、CDK のコードはすべて TypeScript で書けましたが、Lambda 関数や動作確認用のスクリプトなどを全て置き換えるところまでは至らず、Python のままです。

写経したリポジトリhttps://github.com/zaki-yama-labs/intro-aws に。

学べること

本講義資料には全部で5つのハンズオンがあります。
各ハンズオンで利用する AWS のサービスについては以下の通り。

その他、 7. Docker を用いた大規模機械学習システムの構築 で Docker についての説明があります。
(次の 8. Hands-on #3 で ECS に使用するコンテナイメージとして Docker イメージを使用する)

学べないこと

講義資料の進め方として、基本的にソースコードは GitLab に公開されているものをそのまま使い、手元では CDK のデプロイやスクリプトを実行するぐらいなので、自分でコードを書く場面はないです(CLI を実行してみよう、などを除き)。
そのため、資料をそのままなぞるだけでは上に挙げた AWS の各種サービスの基本的な使い方を学ぶことができる(たとえば、Lambda 関数の書き方がわかる)、というわけではなさそうです。

感想

私の AWS に対する知識が、

3年ぐらい前にこの本を読んだ程度のもので、あとは「Lambda や DynamoDB がどんなものかは知っているが触ったことない、CDK に至ってはどんなものかも知らない...」という感じでした。
特に、↑の本を読んだときも「サーバー構成とかをコードで管理する方法を知りたいなあ」と思った記憶があり、今回 CDK の部分を TypeScript で書き直すことで「AWS のリソースをコードで管理する比較的新しめの方法」を手を動かして学べたのは良かったです。

トラブルシューティング

ここから下は、実際にハンズオンをやっていてハマったところと解決策を残しておきます。

AWS CLI を実行すると "Unknown output type: JSON" エラーで怒られる

たとえば、以下のようなコマンド

$ aws ec2 describe-images --owners amazon

Unknown output type: JSON

💡解決策💡
「14.2. AWS CLI のインストール」で、aws configure 実行時に聞かれるDefault output formatJSON でなく json (小文字)を指定するのが正しい。

4. Hands-on #1 で cdk deploy -c key_name="XXX" するとエラー

💡解決策💡
-c は context と呼ばれる。
https://docs.aws.amazon.com/cdk/latest/guide/context.html

context はコード中では

const keyName = this.node.tryGetContext('key_name');

という書き方で取り出せるので、これを渡してあげれば OK。
資料の class MyFirstEc2 という Python のコードにはコンストラクタ引数で key_name を受け取っているが、これは外から渡している。
https://gitlab.com/tomomano/intro-aws/-/blob/master/handson/01-ec2/app.py

6. Hands-on #2 で cdk deploy するとエラー

2つ問題があった。
1つは

$ cdk deploy -c key_name="XXX"
Unable to determine AMI from AMI map since stack is region-agnostic
Subprocess exited with error 1

というエラーで、これは region を渡す必要があった。

Python のコードでは
https://gitlab.com/tomomano/intro-aws/-/blob/master/handson/02-ec2-dnn/app.py
でスタッククラスを定義しているファイル内で渡しているが、TypeScript のコードだと lib/xxx-stack.ts ではなく bin/xxx.ts に書く必要があった。

const app = new cdk.App();
new XXXStack(app, 'XXXStack', {
  env: {
    region: process.env.CDK_DEFAULT_REGION,
    account: process.env.CDK_DEFAULT_ACCOUNT,
  }
});

ref. https://github.com/zaki-yama-labs/intro-aws/blob/master/ch06/bin/ch06.ts

2つめのエラーは

You have requested more vCPU capacity than your current vCPU limit of 0 allows for the instance bucket that the specified instance type belongs to. Please visit http://aws.ama zon.com/contact-us/ec2-request to request an adjustment to this limit. (Service: AmazonEC2; Status Code: 400; Error Code: VcpuLimitExceeded; Request ID: *****; Proxy: null)

というもので、どうやら現在は vCPU と呼ばれるものの上限を引き上げてもらうよう AWS への申請が必要らしい。(たぶん。回避策あるのかもしれない)

ref. vCPUベースのオンデマンドインスタンス起動数の制限を試してみた。 | Developers.IO

f:id:dackdive:20201003164503p:plainf:id:dackdive:20201003164508p:plain

8. Hands-on #3 で Argument of type 'this' is ... というエラー

このへんで怒られる。

const table = new dynamodb.Table(this, 'EcsClusterQaBot-Table', ...
                                 ^^^^
Argument of type 'this' is not assignable to parameter of type 'Construct'.
  Type 'Ch08Stack' is not assignable to type 'Construct'.
    Types of property 'node' are incompatible.
      Type 'import("/Users/yamazaki/repos/github.com/zaki-yama-labs/intro-aws/ch08/node_modules/@aws-cdk/core/lib/construct-compat").ConstructNode' is not assignable to type 'import("/Users/yamazaki/repos/github.com/zaki-yama-labs/intro-aws/ch08/node_modules/@aws-cdk/aws-iam/node_modules/@aws-cdk/core/lib/construct-compat").ConstructNode'.
        Types have separate declarations of a private property 'host'.

💡解決策💡
@aws-cdk/core とそれ以外のパッケージのマイナーバージョンがずれてた (coreが 1.63.0、それ以外が ^1.64.1)。
バージョンを揃えたら解決。

8. Hands-on #3 で run_task.py を実行したらエラー
$ python run_task.py ask "A giant peach was flowing in the river. She picked it up and brought it home. Later, a healthy baby was born from the peach. She named the baby Momotaro." "What is the name of the baby?" 
Traceback (most recent call last):
  File "run_task.py", line 173, in <module>
    ask(args.context, args.question)
  File "run_task.py", line 22, in ask
    P = Params()
  File "run_task.py", line 12, in __init__
    self.ECS_CLUSTER_NAME = ssm_client.get_parameter(Name="ECS_CLUSTER_NAME")["Parameter"]["Value"]
  File "/Users/yamazaki/repos/github.com/zaki-yama-labs/intro-aws/ch08/.venv/lib/python3.8/site-packages/botocore/client.py", line 337, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/Users/yamazaki/repos/github.com/zaki-yama-labs/intro-aws/ch08/.venv/lib/python3.8/site-packages/botocore/client.py", line 656, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ParameterNotFound: An error occurred (ParameterNotFound) when calling the GetParameter operation:

💡解決策💡
ECS_CLUSTER_NAME などは SSM に保存していた。
https://gitlab.com/tomomano/intro-aws/-/blob/master/handson/03-qa-bot/app.py#L76-101

同様にSSMに保存するようにして解決。