アプリケーションイメージを ECR に公開する

AWSBeginner
オンラインで実践に進む

はじめに

チームが小さな HTTP アプリケーションを配信しようとしています。提供されたコードをコンテナイメージにまとめ、Amazon Elastic Container Registry(ECR)に保存し、保存した正確なリリースが起動してリクエストに応答することを確認します。別の参照用リポジトリは変更せず、自分で作成したリソースを最後に削除します。

先に AWS Foundations と IAM のアイデンティティポリシーの基礎を学習してください。Docker の概念はこの実験で説明します。アプリのコードを書く必要も、個人の AWS アカウントもありません。独立した VM に設定済みの AWS CLI、Docker、Dockerfile、ソースコードが用意されています。上部の AWS View でリポジトリを観察しアプリをテストし、下部の Terminal でコマンドを実行します。両方が同じリソース状態を参照します。

認定試験との関連

認定試験 試験タスク 実践内容
Developer – Associate (DVA-C02) タスク 3.1、スキル 3.1.1 アプリケーションコードとランタイムを、デプロイ用のコンテナイメージにまとめます。

実験の概要

概念図:提供されたアプリを構築して ECR にイメージを公開し、正確なダイジェストを取得して HTTP に応答する Docker コンテナを実行します。

不変タグのイメージリポジトリを作成する

このステップでは、アプリケーションのリリースを保存するリポジトリを作成します。

イメージはアプリのファイルとランタイムをまとめたものです。コンテナはそのイメージの実行インスタンスです。レジストリはイメージを保存して配布し、ECR のリポジトリは一つのアプリのイメージをまとめます。イメージを保存してもアプリは起動しません。

用意された作業ディレクトリに移動して Dockerfile を確認します。cat はテキストファイルを表示します:

cd /home/labex/project
cat application/Dockerfile

FROM は Python ベースイメージ、COPY はアプリの追加、USER は非特権ユーザー、CMD は起動コマンドを指定します。ベースの sha256 参照はベースイメージの内容を固定するもので、新しく作るアプリのイメージを指すものではありません。

ct-demo を作成します。タグは release-1 のような読みやすい名前です。IMMUTABLE は既存タグを異なる内容で置き換えることを禁止し、リリース名の信頼性を保ちます:

aws ecr create-repository \
  --repository-name ct-demo \
  --image-tag-mutability IMMUTABLE

応答には Docker が使うアドレス repositoryUri と imageTagMutability: IMMUTABLE が含まれます。URI をシェル変数に保存します。$(...) はコマンド出力を取得し、--query はフィールドを選び、--output text は JSON の引用符なしで返します:

REPOSITORY_URI=$(aws ecr describe-repositories \
  --repository-names ct-demo \
  --query 'repositories[0].repositoryUri' \
  --output text)
echo "$REPOSITORY_URI"

変数を保持するため、この Terminal セッションを開いたままにします。上部の AWS View に、イメージ数ゼロの ct-demo と ct-reference が表示されます。参照リポジトリは別のアプリのもので、削除対象ではありません。

リリースを構築して公開する

このステップでは、提供されたアプリを構築してイメージを ECR にプッシュします。

Docker はプッシュ前にレジストリへのログインが必要です。レジストリアドレスは URI の最初のスラッシュより前の部分です。${REPOSITORY_URI%%/*} はそのスラッシュ以降を削除します:

REGISTRY=${REPOSITORY_URI%%/*}
echo "$REGISTRY"

AWS CLI で短期の ECR ログインパスワードを取得し、直接 Docker に渡します。パイプ | は出力を次のコマンドに送り、--password-stdin はパスワードが引数や Terminal 出力に現れるのを防ぎます。自分で表示したり保存したりしないでください:

aws ecr get-login-password | docker login \
  --username AWS \
  --password-stdin "$REGISTRY"

Docker は Login Succeeded を表示します。現在のアイデンティティに許可された操作のみが可能になり、すべてのリポジトリにアクセスできるわけではありません。

application から構築します。-t は結果に URI とリリースタグを付けます。最後の引数はビルドコンテキストで、COPY が使えるファイルを含むディレクトリです:

docker build -t "${REPOSITORY_URI}:release-1" application

イメージをプッシュします。Docker はファイルシステムのレイヤーと、内容を記述する manifest をアップロードします:

docker push "${REPOSITORY_URI}:release-1"

最後に sha256:... ダイジェストが表示されます。ダイジェストは正確な内容を識別し、タグは名前を識別します。タグ変更が許可されたリポジトリでも、ダイジェストは元の内容に結び付いたままです。

ECR に問い合わせて保存したリリースを確認します:

aws ecr describe-images \
  --repository-name ct-demo \
  --image-ids imageTag=release-1 \
  --query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}'

ダイジェストを変数にも保存します:

IMAGE_DIGEST=$(aws ecr describe-images \
  --repository-name ct-demo \
  --image-ids imageTag=release-1 \
  --query 'imageDetails[0].imageDigest' \
  --output text)

ECR のダイジェストは docker push の出力と一致するはずです。上部の AWS View で、ct-demo に release-1 とダイジェストが表示されます。アプリはまだ実行されていません。次のステップで起動します。

公開した正確なイメージを実行する

このステップでは、ダイジェストでイメージを取得し、実際のアプリコンテナをテストします。

最初にローカルで構築したイメージを削除します。docker image rm はローカル参照を削除するだけで、ECR の保存イメージは削除しません。次の取得で公開済みリリースを読み込みます:

docker image rm "${REPOSITORY_URI}:release-1"

URI とダイジェストを @ で組み合わせ、正確な内容を選択します:

docker pull "${REPOSITORY_URI}@${IMAGE_DIGEST}"

Docker は同じダイジェストとダウンロード成功を表示します。それを ct01-app として起動します。--detach はバックグラウンド実行、--publish 127.0.0.1:8081:8000 はホストの 8081 番ポートとアプリの 8000 番ポートを接続します。ループバックアドレスで直接アクセスを VM 内に限定します。イメージは非特権ユーザーを選択済みです。--read-only はルートファイルシステムを読み取り専用にし、--cap-drop ALL は Linux capabilities を削除し、no-new-privileges は追加権限の取得を防ぎます:

docker run --detach \
  --name ct01-app \
  --read-only \
  --cap-drop ALL \
  --security-opt no-new-privileges \
  --memory 256m \
  --publish 127.0.0.1:8081:8000 \
  "${REPOSITORY_URI}@${IMAGE_DIGEST}"

コンテナ ID は Docker が起動要求を受け付けたことを示しますが、正常動作の証拠ではありません。HTTP を確認します。curl --fail は HTTP エラーで失敗し、--retry-all-errors は短い接続リセットを含む起動時の接続エラーを再試行します:

curl --fail --silent --show-error \
  --retry 5 --retry-all-errors --retry-delay 1 \
  http://127.0.0.1:8081/

"release": "release-1" とコンテナのホスト名を含む JSON が返るはずです。実際のリクエストをログで確認します:

docker logs ct01-app

served application request が表示されます。ホスト名や時刻は変わります。上部の AWS View で Request application を選択し、こちらも release-1 を返すことを確認します。このボタンは実際のコンテナに HTTP リクエストを送信します。

実際の AWS View チェックポイント:ECR に release-1 とダイジェストがあり、Docker アプリが同じリリースを返しています。ダイジェストとホスト名はこの実行の例です。

自分のアプリリソースを削除する

このステップでは、参照リポジトリを保持したまま、アプリを停止してローカルイメージと ECR リポジトリを削除します。

最初にコンテナを停止します:

docker stop ct01-app

次にコンテナを削除します。コンテナの削除でイメージは削除されません:

docker rm ct01-app

使用中の実行コンテナがなくなったため、ローカルのダイジェスト参照を削除します:

docker image rm "${REPOSITORY_URI}@${IMAGE_DIGEST}"

ダイジェストで ECR のイメージを削除します。imageDigest はタグに依存せず正確な内容を選びます:

aws ecr batch-delete-image \
  --repository-name ct-demo \
  --image-ids imageDigest="$IMAGE_DIGEST"

応答の imageIds に削除したイメージが表示され、failures は空になるはずです。空になったアプリのリポジトリを削除します:

aws ecr delete-repository --repository-name ct-demo

参照リポジトリが残っていることを確認します:

aws ecr describe-repositories \
  --query 'repositories[].repositoryName'

ct-reference だけが表示されるはずです。最後に Docker が保存したレジストリログイン情報を削除します:

docker logout "$REGISTRY"

上部の AWS View では ct-demo とアプリコンテナが消え、参照リポジトリは残ります。HTTP エラーだけでは削除を証明できないため、リソース一覧を確認し、実際のコンテナとイメージも削除しました。

まとめ

不変タグの ECR リポジトリを作成し、イメージを構築して公開し、ダイジェストを確認して正確なリリースを実行しました。実際の HTTP 応答とログを確認し、別のアプリのリポジトリを保持したまま、自分のコンテナ、ローカルイメージ、ECR リポジトリを削除しました。次の実験では ECS タスク定義でイメージを選択して実行します。

詳しくは、AWS 公式ドキュメントを参照してください:レジストリ認証とイメージのプッシュ; イメージタグの上書きを防ぐ。