【YOLO】②YOLOのテストアプリをコンテナ化してECS上で動かしてみる
はじめに
前回の記事で、YOLO12を使用した簡単なwebアプリを作成しテスト実行するところまで記載しました。
今回はそのアプリからDockerイメージをビルドし、AWS ECS上で起動させる方法までを共有させていただきます。
以下リンクの前回記事と内容が重複する箇所の記載は省略させていただくことをご承知おきください。
なんでコンテナ化するのか?
(※Dockerの基礎を理解されている方は読み飛ばしてください・・・)
サーバ上で問題なくアプリが動作することを確認できましたが、その上でわざわざコンテナ化する利点はなんでしょうか?
まず簡単に、Dockerは大きく以下の3要素で構成されます。
-
Dockerファイル
Dockerイメージを作成するために必要な設計図です。
このファイルをビルドしてDockerイメージを作成します。 -
Dockerイメージ
アプリの実行環境がパッケージ化されたファイルです。
前述の通り、Dockerファイルをビルドすることで作成されます。 -
Dockerコンテナ
Dockerイメージをもとに実行されるアプリ(実行環境)のことを指します。
その上で冒頭の回答として、代表的に以下利点が挙げられます。
-
動作環境の一貫性
Dockerイメージには、OSやライブラリのなどの動作環境がすべて含まれるため、コンテナを起動するOSに依らずどの環境でも同じ動作が保証されます。
当然、オンプレのWindowsで開発したアプリ(コンテナ)をクラウドのLinux上で起動させることも可能であり、移植性が高いとも言えます。 -
依存関係の管理
アプリの動作に必要なライブラリやツールをイメージ内に含むことができるということは、アプリごとの依存関係を管理できるということでもあります。
例として同一環境で複数のアプリを開発する場合、使用したいモジュールが同一でもアプリごとに必要なバージョンが異なるといった状況があり得ます。
それに対し、それぞれ対応したバージョンを指定したイメージファイルを作成すれば、コンテナごとに独立した環境を作成でき、依存関係を解決できます。 -
環境構築が簡単
一度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 を使用して依存関係をインストールするコマンドを記述するため、ここで予め作成しておきます。
- pipreqsのインストール
アプリの実行に必要なライブラリを手動で洗い出すのは少し大変です。。。
pip freeze
コマンドを実行しても良いですが、それでは余分なライブラリまで含まれてしまいます。
が、「pipreqs」というツールを使用することで、実際にコード内で使用されるライブラリのみを抽出して requirements.txt を簡単に作成できます。
インストールコマンド
$ pip install pipreqs
- pipreqsの実行
以下コマンドを実行するだけで、指定したディレクトリ(今回はyolo12_testapp
)内の app.py
を対象として依存関係のあるライブラリのみを抽出し、同ディレクトリ内に requirements.txt が作成されます。
$ pipreqs yolo12_testapp
作成された requirements.txt の内容
Flask==3.1.0
ultralytics==8.3.78
Werkzeug==3.1.3
以上です。
pipreqs に感謝します。
Dockerfileの作成
続けて、Dockerイメージの設計図となるDockerファイル(ファイル名:Dcokerfile)の作成に着手しましょう。
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イメージをプッシュするために必要な操作は、マネジメントコンソール上の「プッシュコマンドを表示」を押下することで確認できます。
今回の場合実行するコマンドは以下です。
- アカウントの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です。
- Dockerイメージ名をECRにプッシュできる形式に変更する
sudo docker tag yolo12-testapp:latest [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/yolo12-test-ecr:latest
- DockerイメージをECRにプッシュする
sudo docker push [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/yolo12-test-ecr:latest
- 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」
{
"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