Terraformしか使ったことなかったけどCDK使ってみた

この記事は[ぷりぷりあぷりけーしょんず Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ppap)の2日目の記事です。

# 概要
これまでAWSの構成管理は[Terraform](https://www.terraform.io/)しか利用してこなかったのですが、最近`CDK`を初めて利用してLambdaの管理を行いました。

CDKは以前から知っていましたが、TypeScriptでの記事が多くPythonで書く気が起きなかったのですが、使ってみたら本当に簡単だったのでざっくりと紹介したいと思います(何番煎じ)。

# 使い方
## プロジェクトの作成
[大先輩の記事](https://qiita.com/ohanamisan_Ba/items/5e1297ad1413ff2a24fb#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E7%94%A8%E6%84%8F)の3行目の`language`をPythonにします。

## ライブラリの管理
上記の手順でプロジェクトを作成すると`venv`がよしなに作られ`setup.py`に必要なライブラリを追加して`pip install -e .`を行うことになります。

このライブラリの管理ですが、以前から[poetry](https://github.com/python-poetry/poetry)が気になっていたので、今回はpoetryを利用します。

※ poetryを利用する理由はただただ使ってみたかっただけです。

### poetryのインストール
```shell
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3
```
### pyenvのインストール
Lambdaのランタイムと開発機のPythonのバージョンを合わせたいのでpyenvを利用します。

```shell
brew install pyenv
cat << 'EOS' >> ~/.bash_profile
export PYENV_ROOT=/usr/local/var/pyenv
if command -v pyenv 1>/dev/null 2>&1; then
  eval "$(pyenv init -)"
fi
EOS
source ~/.bash_profile
```

Python3.8で開発したいので以下をプロジェクトディレクトリで実行します。

```shell
rm -r .venv requirements.txt setup.py source.bat
pyenv install 3.8.6
pyenv local 3.8.6
```
### poetryのセットアップ
poetryの設定を対話式で設定することになりますが、基本enterキーで問題ないです。

```shell
poetry init
```
### PyCharmと連携
[こちらの記事](https://qiita.com/shigeru0215/items/e1c070f497345a478e69)が非常にわかりやすいです。

私の環境でも`$HOME/Library/Caches...`を指定することで動作を確認できています。

# 実装
## Lambdaで実行するスクリプトの作成
```python
def lambda_handler(event, context):
    print("Hello.")
```
上記のようなLambdaで実行したいスクリプトを作成し、プロジェクトの配下に置きます。[cdkのサンプル集](https://github.com/aws-samples/aws-cdk-examples/tree/master/python/lambda-ddb-mysql-etl-pipeline)が非常に参考になります。

たとえば下記のように`lambda_function/hello/lambda_function.py`とします。

```
├── app.py
├── cdk.json
├── ${プロジェクト名}
│   ├── __init__.py
│   └── ${プロジェクト名}_stack.py
├── lambda_function
│   └── hello
│       └── lambda_function.py
└── pyproject.toml
```

## AWSLambdaリソースの作成
必要なライブラリをインストールします。

```shell
poetry add from aws_cdk.aws_lambda
```

すでに星の数ほど記事がありますが、`${プロジェクト名}_stack.py`に下記を追記することでLambdaリソースを作成できます。

```python
from aws_cdk import aws_lambda as lambda_

_ = lambda_.Function(
    self,
    "hello-function",
    code=AssetCode("lambda_function/hello"),
    handler="lambda_function.lambda_handler",
    function_name="hello-function"
)
```

Terraformを利用していると、[archive_file](https://dev.classmethod.jp/articles/terraform-lambda-deployment/)のような組み込みの関数を利用したり、手元でスクリプトをzipファイル化する作業が必要になりますが、CDKを利用することでこれらの手順が不要になりました(便利)。

## 余談
`pandas`を利用したく先ほどのサンプル集を参考にpandasの`LambdaLayer`([参考](https://qiita.com/thimi0412/items/4c725ec2b26aef59e5bd))を作成したのですが、Mac上でpandasのzipを作成してもnumpyでエラーが生じます。

わざわざEC2やVagrantを利用してAmazonLinuxの環境を作るのは面倒だと考えていたのですが、調べてみるとLambdaLayerを簡単に作成するための`ARN`が公開されていました。(https://github.com/keithrozario/Klayers/blob/master/deployments/python3.8/arns/ap-northeast-1.csv)

これを利用することで下記のように簡単にLambdaLayerを利用することができるようになります(めっちゃ便利）。

```python
from aws_cdk import aws_lambda as lambda_

_ = lambda_.Function(
    self,
    "pandas-function",
    code=AssetCode("lambda_function/pandas"),
    handler="lambda_function.lambda_handler",
    function_name="pandas-function",
    layers=lambda_.LayerVersion.from_layer_version_arn(
        self,
        "pandas",
        "arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-python38-pandas:21"
    )
)
```

lambdaで実行するスクリプト内では`import pandas as pd`などするだけで良いです。

# デプロイ
```shell
cdk bootstrap
cdk deploy
```

裏でCloudFormationのStackが動いているみたいです。ここらへんはeksctlなどと同じみたいですね。

Terraformだとバイナリファイル一つでデプロイができてCloudFormationが裏で動くこともなくシンプルになりますが、Lambdaとの親和性はCDKの方が良いため今後はCDKも積極的に利用したいですね！

# まとめ
Terraformにしか馴染みがなかったのですが、案外CDKを触ってみたら便利だったので記事にしてみました。

Pythonで書けるため、今回紹介したようにpoetryだったりpipenvなど各々の管理したいようにカスタマイズできることもありがたい点だと思いました。

`StepFunctions`の実装もCDKでは簡単行えるためインフラの管理をしないサービスの構築にはうってつけだと思います。