ECSを使ってデータのバッチ処理をしてみる

この記事は、以下の記事を参照します。

dockerでデータのバッチ処理をしてみる

ECSとは

AWSが提供するフルマネージド型のコンテナオーケストレーションサービスです。要するにDockerコンテナのフルマネージド版ですね。

今まではEC2を立ててその上にDocker環境を置いて動かしていたのが、ECSの登場によりEC2が不要になり、コストも安価で効率的にDockerコンテナの運用ができるようになりました。

使用用途としては、EC2の上に置くのと同じようにWEBサーバーとして使ってもいいですし、実行環境をFargateにして、データ処理のようなバッチ処理をするというのもありですね。バッチ処理としてDockerコンテナ上で実行すれば、環境依存もなくなり、検証環境、開発環境、本番環境への作業工数も非常に削減されます。

データエンジニアの求人をみていると、ECSをデータ基盤に活用している会社さんが多いような印象を受けます。

それほど需要性の高いツールということですね。

ちなみに、dockerについてよく知らない方は、こちらの記事を見てみてください。

【docker】基本的なコマンドと使い方

ECSの構造について

ECSでは、Cluster、Service、Task定義、Taskの4つの要素が存在します。また、レポジトリとしてECRを活用し、実行環境はEC2とFargateのどちらかを選択します。

Cluster

ServiceまたはTaskを格納しておく場所です。一番大きなグループですね。

Service

Taskを管理する場所です。Task定義して指定した数のTaskを起動します。また、Task起動失敗時などに自動で別のTaskを起動する役割も担います。

Task定義

Cluster上で動作するTaskの定義をします。コンテナイメージの指定や使用するCPU、メモリ、実行環境、ロール、ネットワークモード等を設定します。また、複数のコンテナの設定を記述することもでき、1つのTaskの中で複数のコンテナを登録することも可能です。

Task

Task定義に基づいてコンテナを起動し、処理を実行します。

また、コンテナのイメージはECRに保存します。

ECR

Dockerのコンテナレジストリです。DockerHubと同じ役割を担います。

実行環境

コンテナを実行する環境として、EC2Fargateを選択できます。

WEBサービスとして使うのでなければ、基本Fargateを選択するほうがいいです。

メリットデメリット
EC2・コンテナホストを自由に選択、設定できる
・ネットワークモードを自由に選択できる
・コンテナホストの管理が必要
・EC2とコンテナ両方のスケーリング設定が別途必要
・OS、エージェントの更新設定が必要
Fargate・サーバーの管理不要
・オートスケーリング
・コンテナ管理のみに集中できる
・自由にコンテナホストを選択することができない

以上、構造を図にするとこのような感じです。

ECSでデータのバッチ処理をしてみる

冒頭で記載したとおり、前回ローカルのdocker環境で実行した内容をecsでやってみます。

ソース格納先:https://github.com/takuma11248250/test_ecs

ECRリポジトリを作成し、PUSHする

ECSはデプロイするコンテナイメージをECRから取得します。

まずは作成したimageをECRにpushする必要があります。

awsにログインして、リポジトリを作成しましょう。

リポジトリ名はなんでも大丈夫ですが、私は「test_ecs/python3」にしました。

作成できたら、imageをpushしていきます。コマンドはecs画面の「プッシュコマンドの表示」をクリックするとコマンド一覧がでてきますので、順番どおりに実行していきます。

※docker-composeでbuildする。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
docker compose up -d --build
docker tag test_ecs/python3:latest <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/test_ecs/python3:latest
docker push <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/test_ecs/python3:latest

問題なくlatestタグでpushできたら完了です。

AWSのネットワークを構築する

ecsを実行するためにVPCやサブネットなどのリソース設定を行います。

VPCを作成する

CIDRブロック「10.0.0.0/16」で作成していきます。

パブリックサブネットを作成する

作成したvpc内にプライベートサブネットを作成していきます。CIDRブロックは「10.0.0.0/24」でいきます。

インターネットゲートウェイを作成する

AWSからインターネットに接続できるようにインターネットゲートウェイとルートテーブルを作成し、vpcにアタッチします。

ルートテーブルを作成し、ルートを追加する

サブネットとルートテーブルを紐付ける

パブリックIPアドレスの自動有効

コンテナへのインターネットからのアクセス、コンテナがECRからイメージをpullできるようにするためにパブリックIPアドレスを有効にします。

ECS用のIAMロールを作成する

ECSで使用するタスク実行用のIAMロールを作成します。

ECSサービスにリンクしたロールは、デフォルトで作成されている「AWSServiceRoleForECS」を使用します。

ECSタスク実行用IAMロール

ロールtest_ecs_task_role
エンティティecs-tasks.amazonaws.com
ポリシー1AmazonECSTaskExecutionRolePolicy
ポリシー2AmazonSSMReadOnlyAccess
ポリシー3AmazonS3FullAccess

セキュリティグループの作成

HTTPアクセスできるようにセキュリティグループ設定をしておきます。

CloudWatchログの設定

ECSのログはCloudWatchに保存されます。対象のロググループを作成します。

ECSで処理を実行してみる

ECSに戻り、クラスターとタスク定義を作成して、実行してみます。

クラスターを作成する

タスク定義を作成する

プロジェクト配下(test_ecs配下)にjsonファイル(test-task-definition.json)でタスク定義を作成します。タスク定義はCPUやメモリ、ストレージなどコンテナの定義を指定します。

タスク定義は、以下のコマンドでひな形を作成し、修正していく方法が早いです。

aws ecs register-task-definition --generate-cli-skeleton
{
    "family": "test-ecs",①
    "taskRoleArn": "arn:aws:iam::<アカウントID>:role/test_ecs_task_role",②
    "executionRoleArn": "arn:aws:iam::<アカウントID>:role/test_ecs_task_role",③
    "networkMode": "awsvpc",
    "containerDefinitions": [④
        {
            "name": "python3",
            "image": "<アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/test_ecs/python3:latest",
            "essential": true,
            "readonlyRootFilesystem": false,
            "logConfiguration": {⑤
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "test_ecs",
                    "awslogs-region": "ap-northeast-1",
                    "awslogs-datetime-format": "%Y-%m-%d %H:%M:%S",
                    "awslogs-stream-prefix": "test-ecs/python3"
            }
        }
    }
    ],
    "requiresCompatibilities": [⑥
        "FARGATE"
    ],
    "cpu": "256",⑦
    "memory": "512"⑧
}

必要なもののみに絞り、修正します。

①ECS定義名を指定

②③作成したECS用のIAMロールを指定

④ECRにpushしたimageを指定

⑤CloudWatchで作成したロググループを指定

⑥実行環境をFARGATEに指定

⑦⑧cpuとmemoryを指定

その他も自分の実行したい環境に合わせて修正していただければと思います。

Dockerfileの修正

今まで実行ファイルをマウントしていたところを、COPYに変えてbuildし直します。

FROM python:3
USER root

RUN apt-get update && apt-get install -y --no-install-recommends \
	curl \
	unzip

RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN ./aws/install

RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

COPY ./requirements.txt ./opt/column_re.py ./opt/header_transform_rule.json ./
RUN pip install --no-cache-dir -r requirements.txt

ENTRYPOINT ["python", "column_re.py"]
docker compose up -d --build
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com
docker compose up -d --build
docker tag test_ecs/python3:latest <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/test_ecs/python3:latest
docker push <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/test_ecs/python3:latest

ecs run-taskで実行してみる

オプションでクラスター、タスク定義、タスク実行の引数指定、実行環境、ネットワークを指定して、runしてみます。

aws ecs run-task \
--cluster test-ecs \
--task-definition test-ecs \
--overrides '{"containerOverrides": [{"name":"python3","command": ["./header_transform_rule.json", "<バケット名>", "<inputファイルのパス>", "<バケット名>", "<outputファイルのパス>"]}]}' \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[<作成したサブネットID>],securityGroups=[<作成したセキュリティグループID>],assignPublicIp=ENABLED}"

Logsに「successed」と表示されたら問題なく実行されてます。

データが処理されているかs3のoutputファイルの中身を確認します。

まとめ

ローカルのdocker環境で実行していたことをAWSのECSで実行することができました。AWS上で実行することで、dockerコンテナ自体の運用を自動化することができたり、普段のバッチ処理自動化等ができるようになりました。

Fargateの登場によりコストを抑えながら必要なときだけ実行させるようなこともできるようになったので、いろんな場面で応用できそうです。

この記事のソースファイル格納先

https://github.com/takuma11248250/test_ecs

ABOUTこの記事をかいた人

株式会社メンバーズ データアドベンチャー所属 データエンジニアとしてデータ基盤の構築から運用を行っております。 得意なことは、データ活用推進です。 日々の学びをアウトプットしていきます。