Step Functions + Fargate + Embulkを使ってDBエクスポートのバッチ処理を作成する

# 概要
データ分析用に複数のテーブルのデータをエクスポートした時の検証作業を備忘録としてまとめる。
元々はEC2インスタンスにEmbulkをインストールしてcronで定期実行すればいいかなと思っていたが、

- エクスポート処理を並列実行したり、実行順序を制御したい
- 処理が失敗した時に通知したい

という要件があり、Step FunctionsとECS(Fargate)に変更してみたら良い感じになったので、構成を変更した。
（AWS Batchも検討したが、うまく動かせなかったので挫折した。。。）

# 手順
## データ出力用S3バケットの作成
エクスポートしたDBデータを保管するS3バケットを作成する。

```
aws s3api create-bucket --bucket okada-dbexport-20211112 \
--create-bucket-configuration LocationConstraint=ap-northeast-1
aws s3api put-public-access-block --bucket okada-dbexport-20211112 \
--public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
```

## コンテナレジストリの作成
今回、ECS(Fargate)を使用するので、コンテナイメージを登録するレジストリを作成する。

```
aws ecr create-repository --repository-name okada-repo
```

## コンテナイメージの作成
DBデータをエクスポートするコンテナイメージを作成する。

```
mkdir work
cd work
vim Dockerfile
vim config.yml.liquid
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t okada-repo .
docker tag okada-repo:latest <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/okada-repo:latest
docker push <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/okada-repo:latest
```
※設定ファイルは[ここ](https://github.com/okadarhi/exportdb-batch/tree/main/docker-image)参照。

## タスク定義の作成
ECS(Fargate)のタスク定義を作成する。
１テーブル１タスク定義。

```
aws ecs register-task-definition --cli-input-json file://db01_table01.json
aws ecs register-task-definition --cli-input-json file://db01_table02.json
```
※設定ファイルは[ここ](https://github.com/okadarhi/exportdb-batch/tree/main/ecs)参照。

### ロググループの作成
タスク定義に関連するロググループを作成する。

```
aws logs create-log-group --log-group-name /ecs/okada-exportdb-taskdef-table01
aws logs create-log-group --log-group-name /ecs/okada-exportdb-taskdef-table02
```

### SSMパラメータストアの作成
タスク定義に関連するSSMパラメータストアを作成する。

```
aws ssm put-parameter --name "/okada-exportdb/table01/S3_BUCKET" --value "okada-dbexport-20211112" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/DB_HOST" --value "okada-db-mysql.c9wjmpp1ssqh.ap-northeast-1.rds.amazonaws.com" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/DB_ID" --value "okada-db-mysql" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/DB_NAME" --value "db01" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/DB_USER" --value "admin" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/DB_PASS" --value "1Oaw1XAhyJ7MZUvLq2zT" --type "SecureString"
aws ssm put-parameter --name "/okada-exportdb/table01/TBL_NAME" --value "table01" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table01/COLUMN_NAME" --value "\"*\"" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/S3_BUCKET" --value "okada-dbexport-20211112" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/DB_HOST" --value "okada-db-mysql.c9wjmpp1ssqh.ap-northeast-1.rds.amazonaws.com" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/DB_ID" --value "okada-db-mysql" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/DB_NAME" --value "db01" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/DB_USER" --value "admin" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/DB_PASS" --value "1Oaw1XAhyJ7MZUvLq2zT" --type "SecureString"
aws ssm put-parameter --name "/okada-exportdb/table02/TBL_NAME" --value "table02" --type "String"
aws ssm put-parameter --name "/okada-exportdb/table02/COLUMN_NAME" --value "\"*\"" --type "String"
```

## ECSクラスターの作成
タスク定義を実行するECSクラスターを作成する。

```
aws ecs create-cluster --cluster-name okada-exportdb-cluster \
--capacity-providers FARGATE_SPOT FARGATE
--region ap-northeast-1
```

## Step Functionsの作成
### IAMロールの作成
Step Functionsを実行するためのIAMロールを作成する。
とりあえず検証なのでインラインポリシーで権限は定義している。

```
aws iam create-role --role-name okada-exportdb-role \
--assume-role-policy-document file://okada-exportdb-role.json
aws iam put-role-policy --role-name okada-exportdb-role \
--policy-name ecs-task-management-scopedaccess-policy --policy-document file://ecs-task-management-scopedaccess-policy.json
aws iam put-role-policy --role-name okada-exportdb-role \
--policy-name xray-access-policy --policy-document file://xray-access-policy.json
aws iam put-role-policy --role-name okada-exportdb-role \
--policy-name cloudwatchlogs-delivery-fullaccess-policy --policy-document file://cloudwatchlogs-delivery-fullaccess-policy.json
aws iam put-role-policy --role-name okada-exportdb-role \
--policy-name passrole-for-ecs-task-executionp-policy --policy-document file://passrole-for-ecs-task-executionp-policy.json
```
※設定ファイルは[ここ](https://github.com/okadarhi/exportdb-batch/tree/main/iam)参照。

### ステートマシンの作成
ECS(Fargate)でDBデータをエクスポートする一連の処理をまとめたステートマシンを作成する。

```
aws stepfunctions create-state-machine --name okada-exportdb \
--definition file://exportdb-state.json \
--role-arn "arn:aws:iam::<アカウントID>:role/okada-exportdb-role"
```
※設定ファイルは[ここ](https://github.com/okadarhi/exportdb-batch/tree/main/stepfunctions)参照。

![stepfunctions_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1698899/bdd9f830-180f-da6f-2620-de3b5d2456a6.png)


## 動作確認
作成したStep Functionsを実行する。

```
aws stepfunctions start-execution --state-machine-arn "arn:aws:states:ap-northeast-1:<アカウントID>:stateMachine:okada-exportdb"
// コマンド結果
{
    "executionArn": "arn:aws:states:ap-northeast-1:<アカウントID>:execution:okada-exportdb:f5b6d7bb-27ae-4e2a-811a-df7fc730cc14",
    "startDate": "2021-11-12T16:16:45.440000+00:00"
}
```

```
aws stepfunctions describe-execution \
--execution-arn "arn:aws:states:ap-northeast-1:<アカウントID>:execution:okada-exportdb:f5b6d7bb-27ae-4e2a-811a-df7fc730cc14"
// コマンド結果
{
    "executionArn": "arn:aws:states:ap-northeast-1:<アカウントID>:execution:okada-exportdb:f5b6d7bb-27ae-4e2a-811a-df7fc730cc14",
    "stateMachineArn": "arn:aws:states:ap-northeast-1:<アカウントID>:stateMachine:okada-exportdb",
    "name": "f5b6d7bb-27ae-4e2a-811a-df7fc730cc14",
    "status": "SUCCEEDED",
    "startDate": "2021-11-12T16:16:45.440000+00:00",
    "stopDate": "2021-11-12T16:30:00.812000+00:00",
    "input": "{}",
    "inputDetails": {
        "included": true
    },
    "output": "[<長いので省略>]",
    "outputDetails": {
        "included": true
    }
} 
```

→ステータスがSUCCEEDEDになったことが確認できる。

```
aws s3 ls okada-dbexport-20211112 --recursive --human-readable --summarize
// コマンド結果
2021-11-12 16:29:31   37.1 MiB table01/2021/11/13/01/okada-db-mysql.000.00.json.gz
2021-11-12 16:20:03    2.3 MiB table02/2021/11/13/01/okada-db-mysql.000.00.json.gz

Total Objects: 2
   Total Size: 39.4 MiB
```

→S3バケットにもDBデータが格納されていることが確認できる。

#　所感
今回、初めてStep FunctionsとECS(Fargate)を使ったが、思ってたよりすんなり使えたので驚いた。
特にStep FunctionsのWorkflow Studioがめっちゃ使いやすかった。
直感的に操作できるのってGUIで一番大事だと思う。
ECS(Fargate)に関しても、とりあえず動かすまでは容易だった（デフォルト設定でOKな部分が多い）からよかった。
エクスポートする対象のテーブルが増えた場合でも、タスク定義+αを作成してからステートマシンにRunTaskを追加するだけなので、自分なりに良い感じの構成にできたと思う。