Zenn
🤖

【YOLO】②YOLOのテストアプリをコンテナ化してECS上で動かしてみる

2025/03/23に公開

はじめに

前回の記事で、YOLO12を使用した簡単なwebアプリを作成しテスト実行するところまで記載しました。
今回はそのアプリからDockerイメージをビルドし、AWS ECS上で起動させる方法までを共有させていただきます。
以下リンクの前回記事と内容が重複する箇所の記載は省略させていただくことをご承知おきください。

https://zenn.dev/gj77a/articles/e5cd1056fcbdc4

なんでコンテナ化するのか?

(※Dockerの基礎を理解されている方は読み飛ばしてください・・・)

サーバ上で問題なくアプリが動作することを確認できましたが、その上でわざわざコンテナ化する利点はなんでしょうか?

まず簡単に、Dockerは大きく以下の3要素で構成されます。

  1. Dockerファイル
    Dockerイメージを作成するために必要な設計図です。
    このファイルをビルドしてDockerイメージを作成します。

  2. Dockerイメージ
    アプリの実行環境がパッケージ化されたファイルです。
    前述の通り、Dockerファイルをビルドすることで作成されます。

  3. Dockerコンテナ
    Dockerイメージをもとに実行されるアプリ(実行環境)のことを指します。

その上で冒頭の回答として、代表的に以下利点が挙げられます。

  1. 動作環境の一貫性

    Dockerイメージには、OSやライブラリのなどの動作環境がすべて含まれるため、コンテナを起動するOSに依らずどの環境でも同じ動作が保証されます。
    当然、オンプレのWindowsで開発したアプリ(コンテナ)をクラウドのLinux上で起動させることも可能であり、移植性が高いとも言えます。

  2. 依存関係の管理

    アプリの動作に必要なライブラリやツールをイメージ内に含むことができるということは、アプリごとの依存関係を管理できるということでもあります。
    例として同一環境で複数のアプリを開発する場合、使用したいモジュールが同一でもアプリごとに必要なバージョンが異なるといった状況があり得ます。
    それに対し、それぞれ対応したバージョンを指定したイメージファイルを作成すれば、コンテナごとに独立した環境を作成でき、依存関係を解決できます。

  3. 環境構築が簡単

    一度Dockerイメージの作成が完了すれば、docker run コマンド1つで簡単に実行環境を含めたアプリケーションを起動できます。

他にもCI/CDツールと統合ができたり、セキュリティが高いであったりと様々な利点があります。
個人的にはアプリをDockerイメージ化して退避させておけば、元の環境ごと削除しても別環境で復旧できる点に魅力を感じています。

Dockerインストール

Dockerインストールから始めましょう。
サービス起動と自動起動設定も行います。

Dockerインストール

$ sudo dnf install -y docker

バージョン確認

$ docker --version
Docker version 25.0.8, build 0bab007

Dockerサービス起動

$ sudo systemctl start docker

サービス自動起動設定

$ sudo systemctl enable docker

サービス状態確認
(active (running)であること、enabled; preset: disabledに設定されていることを確認しましょう)

$ systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; preset: disabled)
     Active: active (running) since Tue 2025-03-04 13:48:51 UTC; 2min 31s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 3450 (dockerd)
      Tasks: 10
     Memory: 32.6M
        CPU: 310ms
     CGroup: /system.slice/docker.service
             └─3450 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=32768:65536

requirements.txtの作成

Dockerファイルの作成の前に、「requirements.txt」というファイルを作成しておきます。

requirements.txtは、アプリの実行に必要なPythonのパッケージ(ライブラリ)の一覧を記述したファイルのことであり、このファイルを使用することで必要なライブラリを一括でインストールできます。
Dockerファイル内に requirements.txt を使用して依存関係をインストールするコマンドを記述するため、ここで予め作成しておきます。

  1. pipreqsのインストール

アプリの実行に必要なライブラリを手動で洗い出すのは少し大変です。。。
pip freeze コマンドを実行しても良いですが、それでは余分なライブラリまで含まれてしまいます。
が、「pipreqs」というツールを使用することで、実際にコード内で使用されるライブラリのみを抽出して requirements.txt を簡単に作成できます。

インストールコマンド

$ pip install pipreqs
  1. pipreqsの実行

以下コマンドを実行するだけで、指定したディレクトリ(今回はyolo12_testapp)内の app.py を対象として依存関係のあるライブラリのみを抽出し、同ディレクトリ内に requirements.txt が作成されます。

$ pipreqs yolo12_testapp 

作成された requirements.txt の内容

requirements.txt
Flask==3.1.0
ultralytics==8.3.78
Werkzeug==3.1.3

以上です。
pipreqs に感謝します。

Dockerfileの作成

続けて、Dockerイメージの設計図となるDockerファイル(ファイル名:Dcokerfile)の作成に着手しましょう。
Dockerfileはアプリのルートディレクトリ直下に配置します。

アプリ構成について詳細を確認されたい方は、前回記事をご参照いただけると幸いです。

Dockerfile
# Amazon Linux 2023のベースイメージを指定
# ローカルにベースイメージが無い場合は自動的にAWSの公式ECRからイメージをダウンロードする
FROM public.ecr.aws/amazonlinux/amazonlinux:2023

#必要な前提パッケージをインストール
RUN dnf install -y mesa-libGL mesa-libGL-devel pip

# 作業ディレクトリを設定(指定したディレクトリを新規作成し、そこでアプリが実行される)
WORKDIR /yolo12-testapp

#必要なファイルをコンデナにコピー
COPY requirements.txt requirements.txt
COPY app.py app.py
COPY templates/ templates/

# 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションを実行
CMD ["python3", "app.py"]

FROM public.ecr.aws/amazonlinux/amazonlinux:2023 の箇所は FROM amazonlinux:2023 でも問題ありません。
しかし今回はOSイメージがAmazonLinuxのため、公式ECRからプルした方が最新バージョンを確実に利用できる点と、またDockerHUBからプルすると場合によってはレート制限に引っかかる可能性もある点から、FROM public.ecr.aws~~ を採用しています。

Dockerイメージの作成

続けて、前項で作成したDockerファイルを元にDockerイメージを作成します。
以下コマンド1つ実行するだけで完了します。

$ sudo docker build -t yolo12-testapp:latest .

-t オプションで指定する yolo12-testapp はイメージ名(アプリ名)で、:latest はバージョンタグです。イメージ名は何でも問題ないですが、バージョンタグは latest が一般的です。

コマンド実行後数分待機し、以下のような表示となれば完了です。

[+] Building 169.2s (12/12) FINISHED  

作成したDockerイメージは以下コマンドで確認できます。

$ sudo docker images
コマンド実行結果
$ sudo docker images
REPOSITORY       TAG       IMAGE ID       CREATED         SIZE
yolo12-testapp   latest    9c039cbb1b4a   3 minutes ago   6.62GB

作成したイメージ名が表示されれば作成完了です。

※上記「コマンド実行結果」の SIZE を参照いただくと分かりますが、Dockerファイルに記述するOSイメージやパッケージの内容によってはDockerイメージのファイルサイズが巨大になります。
ディスクの空き容量を十分確保した上でコマンドを実行しましょう。

作成したDockerイメージからコンテナを起動

続けて、作成したDockerイメージからコンテナを起動し、正常にアプリケーションが動作することを確認しましょう。

今回の場合は以下コマンドを実行します。

sudo docker run -p 5000:5000 yolo12-testapp:latest

コマンド実行後、以下のような表示を確認の上でブラウザからアプリにアクセスし、正常に動作を確認できればOKです。

コマンド実行結果
100%|██████████| 5.34M/5.34M [00:00<00:00, 282MB/s]
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.17.0.2:5000

作成したDockerイメージをAWS ECR上にプッシュする

ローカル上でコンテナの起動確認ができましたら、作成したDockerイメージをAWS上に保管しましょう。
保管先としてコンテナレジストリサービスの AWS ECR を使用します。

プライベートリポジトリの作成

具体的なDockerイメージの保管先として、ECR内にプライベートリポジトリを作成します。

今回は「yolo12-test-ecr」というリポジトリ名で作成します。
他設定値はデフォルトのままです。

Dockerイメージのプッシュ

以後の操作で AWS ECS のタスク定義を作成する際にECR内のイメージURIを指定する必要がありますので、この時点で予めDockerイメージを格納しておきましょう。

ECRにDockerイメージをプッシュするために必要な操作は、マネジメントコンソール上の「プッシュコマンドを表示」を押下することで確認できます。

今回の場合実行するコマンドは以下です。

  1. アカウントのECRに対しDockerクライアントを認証する
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com

コマンド実行後、Login Succeeded が表示されればOKです。

  1. Dockerイメージ名をECRにプッシュできる形式に変更する
sudo docker tag yolo12-testapp:latest [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/yolo12-test-ecr:latest
  1. DockerイメージをECRにプッシュする
sudo docker push [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/yolo12-test-ecr:latest
  1. ECR上にDockerイメージが保存されたことを確認する
aws ecr describe-images --repository-name yolo12-test-ecr

AWS ECSの作成

続けて、Dockerコンテナのデプロイ先として AWS ECS を作成していきます。
ひとことにECSといっても、その実作成すべき要素がいくつかあるため、項目ごとに目次を分割いたします。

クラスターの作成

まずは一番大枠となる クラスター の作成から行いましょう。

設定値(他はデフォルト)

項目名 設定値
クラスター名 yolo12-test-cluster
インフラストラクチャ AWS Fargate のみ

(作成後)

詳細設定については後述しますが、ECSの起動タイプはFargateのみにします。
Fargate Spotの使用を検討していましたが、タイミングの問題か Capacity is unavailable at this time が頻発したため普通のFargateにしています...

タスク定義の作成

次にECSの タスク定義 を作成します。
ここではECS上に起動させるFargateのリソースサイズやロール、コンテナイメージを設定します。

設定値(他はデフォルト)

項目名 設定値
タスク定義ファミリー名 yolo12-test-taskfamily
起動タイプ AWS Fargate のみ
OS Linux/X86_64
タスクロール なし
タスク実行ロール 新しいロールの作成
コンテナ名 yolo12-test-container
イメージURI 先に作成したECRのプライベートリポジトリ名

少し分かり辛い項目として、タスクロールタスク実行ロール が存在します。
タスクロール はタスク内で起動されるコンテナに権限を付与するロールで、コンテナがAWSの他リソース(S3など)を操作したい場合に使用します。
今回は必要としないため「なし」に設定しています。

タスク実行ロール はFargateエージェント(コンテナエージェント)に権限を付与するロールで、Fargateエージェントが他リソースを操作する場合に必要になります。
今回はECRからコンテナイメージをプルする要件があるため、ロールを新規作成します。(自動作成してもらいます)

タスク実行ロール を自動作成にした場合、以下内容のロール「ecsTaskExecutionRole」が作成されます。

  • 許可ポリシー「AmazonECSTaskExecutionRolePolicy」
AmazonECSTaskExecutionRolePolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
  • 信頼ポリシー
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs-tasks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

また起動OSにつきまして、より安価な ARM64 ではなく X86_64 としている理由は、
DockerイメージをビルドしたOSのアーキテクチャと異なるアーキテクチャに設定した場合、タスク起動時に deployment failed: tasks failed to start. となる可能性があるためです。

今回イメージをビルドしたOS(AmazonLinux2023)のアーキテクチャ(AMI)が「al2023-ami-2023.6.20250211.0-kernel-6.1-x86_64」のため、起動OSも X86_64 としました。

サービスの作成

続けて、先述の②で作成したクラスター内に サービス を作成していきます。
作成したクラスター「yolo12-test-cluster」を開き、「サービス」タブ内の「作成」を押下して作成画面に遷移してください。

設定値(他はデフォルト)

項目名 設定値
コンピューティングオプション キャパシティープロバイダー戦略
キャパシティープロバイダー FARGATE_SPOT
ベース 0
ウェイト 1
アプリケーションタイプ サービス
リビジョンの手動指定 無効(チェックしない)
タスク定義ファミリー yolo12-test-taskfamily
サービス名 yolo12-test-service
必要なタスク 1
アベイラビリティゾーンのリバランス 無効(チェックを外す)
VPC 作成済みのVPCを指定
サブネット 作成済みのパブリックサブネットを指定
セキュリティグループ 作成済みのセキュリティグループを指定
パブリックIP オンになっています

細かい補足ですが リビジョンの手動指定 を有効にし参照するタスク定義のバージョンを固定化すると、タスク定義の更新時に更新前のタスク定義を参照してしまうため注意が必要です。
後述のデプロイ時に上記が原因で The AWS ECS container "タスク定義名" does not exist エラーが発生する可能性があります。(1敗)

設定を完了し「作成」ボタンを押下した時点で、自動でタスクが起動します。
「タスク」タブを開き、「前回のステータス」が 保留中 から 実行中 に遷移することを確認してください。(数分待機)

ステータスが実行中になったことを確認できましたら、起動したタスクの詳細画面に移動し、「ネットワーキング」タブを開きます。

サブネット、セキュリティグループがサービス作成時に指定した値になっており、パブリックIPも付与されていることを確認してください。

内容に問題なければ、表示されたパブリックIP:5000 にアクセスし、アプリが正常に動作することを確認しましょう。
アプリに接続できない場合は、セキュリティグループの穴開けが正しく設定されているか確認してください。

おわりに

ここまでお付き合いいただきありがとうございました。
以上で、ローカルに作成したテストアプリのDockerコンテナ化およびECS上での起動確認は完了です。

設定項目やコマンドが多く、初めて触った際は頭がこんがらがりそうになりますが、何度か実践するうちに大枠は掴めてくるようになるのではないかと思います。
(それと同時にかなり深い沼であることに気づき恐々としています...)

次回は、今回作成したDcokerイメージ・ECR・ECSをそのまま流用し、「AWS CodePipeline」を使用したCI/CD環境の構築方法について記載いたします。
よろしければまたご覧いただけますと幸いです。

Discussion

ログインするとコメントできます