[AWS] ECRにDockerイメージをプッシュする

気がつけば猫も杓子もDockerと言いますかコンテナな世界になってしまいましたねw DockerイメージをAWSの各種サービスと連携させるためには「ECR」と呼ばれるレジストリサービスへ登録しておく必要があります。

今回はローカルでビルドしたDockerイメージをECRへ登録するまではまとめておきます。

AWS App Runnerが使いたかったのですが、GitHubとの連携で苦戦したためECRにDockerイメージを登録する方法を採用することにしたというのが経緯だったりします。

事前準備

コマンドが最新版か確認

コマンドラインからawsとdockerの各コマンドが最新版か確認します。もし古い物が入っている場合は何らかのエラーでハマる可能性があるのでこのタイミングで更新を。

$ aws --version
aws-cli/2.4.29 Python/3.9.12 Darwin/19.6.0 source/x86_64 prompt/off

$ docker --version
Docker version 20.10.14, build a224086

IAMを準備

ECRが利用できる権限を持ったIAMを作成しアクセスキーとシークレットを保存します。aws configureコマンドを実行するか、~/.aws/credentialsを直接編集してください。

$ cat ~/.aws/credentials
[default]
aws_access_key_id = AKXXXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

ECRにリポジトリを作成する

手順

AWSマネジメントコンソールなどからDockerイメージを保存するリポジトリを作成します。

「ECS」のページの左メニューに「Amazon ECR」があるのでその下の「リポジトリ」をクリック。

リポジトリを作成」ボタンをクリック。プライベートかパブリックかは次の画面で選択できます。

リポジトリの作成内容を入力します。
ここでは「プライベート」、名前は「test」としました。この下にプッシュ時に自動的にスキャンするか、暗号化するかの設問があるのでご希望の設定を入力し、一番下にある「リポジトリを作成」ボタンをクリック。

以上でリポジトリの作成は完了です。めっちゃ簡単ですね。

プッシュ方法のチートシート

先ほど作成したリポジトリの詳細画面の右上にある「プッシュコマンドの表示」ボタンをクリックすると、このあと実行するコマンドを表示してくれます。

ご覧の通り、アカウントIDやリポジトリ名などが置き換わった状態のコマンドが表示されます。あとはこれをコピペしてTerminalなどで実行するだけ。めっちゃ便利!

Dockerイメージをプッシュ

基本的には前述の「チートシート」で表示されたコマンドをそのまま実行するだけです。

ログイン情報を保存

ECRにイメージをアップするための認証情報(トークン)をAWSから取得し、dockerコマンドに渡すことで認証情報がローカルに保存されます。

$ aws ecr get-login-password --region 【リージョン】 | docker login --username AWS --password-stdin 【アカウントID】.dkr.ecr.ap-northeast-1.amazonaws.com
WARNING! Your password will be stored unencrypted in /Users/katsube/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

なおこの認証情報の有効期限は12時間のため、このコマンドは定期的に実行する必要があります。

  • アカウントIDはAWSの物です。
  • ~/.aws/credentialsに複数のアカウントが登録されており、default以外を利用する場合はaws ecr get-login-passwordコマンドに--profile (プロファイル名)オプションを追加して実行してください。

Dockerイメージをビルド

これは通常通りビルドするだけです。「test」の部分は任意の文字列を指定します。

$ docker build -t test .

Dockerイメージにタグ付け

testイメージにlatestという名前のタグを付けています。タグはバージョン番号のようなもので任意の文字列(v1developなど)が指定できます。

$ docker tag test:latest 【アカウントID】.dkr.ecr.ap-northeast-1.amazonaws.com/【リポジトリ名】:latest

ECRへプッシュする

最後にECRへアップします。冒頭のIAMの設定やログイン情報がうまく保存されていればスルッとあがります。

$ docker push 【アカウントID】.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
The push refers to repository [111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test]
ce669cbeacc7: Pushed 
14435cf07f8c: Pushed 
adc5e96f5fe1: Pushed 
b0fc75156d6d: Pushed 
7f30cde3f699: Pushed 
fe810f5902cc: Pushed 
dfd8c046c602: Pushed 
4fc242d58285: Pushed 
latest: digest: sha256:7977c86c130882643910db1ae3a55865bf5a7cdfaaa8ba11d28b3a1caf86084a size: 1997

マネジメントコンソールなどで確認

ECRのリポジトリを確認し、先ほどプッシュした内容が反映されていれば成功です!

コマンドラインからも確認できます。imageDigestのSHA256のハッシュ値がプッシュ完了後に表示されたものと同一であれば無事にアップされていますね。

$ aws ecr list-images --region ap-northeast-1 --repository-name test
{
    "imageIds": [
        {
            "imageDigest": "sha256:7977c86c130882643910db1ae3a55865bf5a7cdfaaa8ba11d28b3a1caf86084a",
            "imageTag": "latest"
        }
    ]
}
  • ~/.aws/credentialsに複数のアカウントが登録されており、default以外を利用する場合は--profile (プロファイル名)オプションを追加して実行してください。

既存のDockerイメージを更新する

先ほどECRへプッシュしたDockerイメージを更新するには、再度タグをつけてプッシュするだけです。

ソースコードや設定などを変更したらこれもいつも通りビルドしてタグ付け、プッシュします。

$ docker build -t test .
$ docker tag test:latest 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
$ docker push 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest

実際にマネジメントコンソールなどでリポジトリの「プッシュされた日時」の時間が更新されていれば成功です。ちなみにタグをつけ忘れると永遠に更新されません。ハマって一晩ほど頭を抱えていましたw

トラブルシューティング

Error saving credentials: error storing credentials

ログイン時にクレデンシャルエラーとなった場合はDockerの設定を変更します。

Error saving credentials: error storing credentials - err: exit status 1, out: `Post "http://ipc/registry/credstore-updated"

~/.docker/config.json内にある"credsStore":"desktop",の部分を削除し保存しなおします。

$ cp ~/.docker/config.json ~/.docker/config.json.back
$ vi ~/.docker/config.json
{"auths":{"https://index.docker.io/v1/":{}},"credsStore":"desktop","experimental":"disabled","stackOrchestrator":"swarm"}
 ↓
{"auths":{"https://index.docker.io/v1/":{}},"experimental":"disabled","stackOrchestrator":"swarm"}

認証情報の保存先を指定しているようなのですが、これが原因でエラーとなってしまうようです。

no basic auth credentials

ECRへDockerイメージをプッシュする際にno basic auth credentialsと表示された場合は、AWSの認証情報が正しく利用されているか確認してください。

$ docker push 【アカウントID】.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
The push refers to repository [111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test]
ce669cbeacc7: Preparing 
14435cf07f8c: Preparing 
adc5e96f5fe1: Preparing 
b0fc75156d6d: Preparing 
7f30cde3f699: Preparing 
fe810f5902cc: Waiting 
dfd8c046c602: Waiting 
4fc242d58285: Waiting 
no basic auth credentials

私の場合は~/.aws/credentialsに複数のアカウントが登録されており、default以外を利用したかったのにその指定をしていなかったため発生しました。そのため--profileオプションを指定することで解決しました。

$ aws ecr get-login-password --region 【リージョン】--profile (プロファイル名) | \
    docker login --username AWS --password-stdin 【アカウントID】.dkr.ecr.ap-northeast-1.amazonaws.com

denied: Your authorization token has expired

ECRへDockerイメージをプッシュする際にexpiredと言われた場合、AWSから取得した認証情報の有効期限が切れています。

$ docker push 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
The push refers to repository [111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test]
ce669cbeacc7: Preparing 
(中略)
denied: Your authorization token has expired. Reauthenticate and try again.

もう一度ログインしたのち再度プッシュすれば解決します。

$ aws ecr get-login-password --region ap-northeast-1 | \
    docker login --username AWS --password-stdin 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com
$ docker push 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest

参考ページ