Amazon ECRのPull through cacheでECR PublicからDocker公式イメージをpullする

この記事は [**セゾン情報システムズ Advent Calendar 2021**](https://qiita.com/advent-calendar/2021/sisco) 1日目の記事です。


## 記事のタイトルは一体何を言っているんだ
AWS re:Invent 2021 の Day 1 で Amazon ECR に関連する2つのアップデートがありました。

https://aws.amazon.com/jp/blogs/aws/announcing-pull-through-cache-repositories-for-amazon-elastic-container-registry/

https://aws.amazon.com/jp/blogs/news/docker-official-images-now-available-on-amazon-elastic-container-registry-public/

これらを合わせて使用することで Private な ECR Repository のみを使いつつ、Docker Hub のオフィシャルイメージを透過的に pull できるようになります。

## Amazon ECR の Pull through cache って?
対応するパブリックコンテナイメージレジストリ上のイメージを、ECR の Private リポジトリにキャッシュできる機能です。2021/11/30 現在で [Quay.io](https://quay.io/) と ECR Public に登録されているイメージをキャッシュできます。

セキュリティ上の理由でパブリックなコンテナレジストリにはアクセスさせたくないが、特定のベースイメージの利用に限っては許可したいケースなどで特に嬉しい機能です。PriavateLink で ECR を利用している環境でもパブリックなイメージを透過的に pull でき、イメージに更新があった場合は自動で同期されます。

※ 厳密には PrivateLink 環境では初回のみ NAT Gateway を経由したインターネットアクセスが必要になります。

## Docker の公式イメージをキャッシュするには？
前述の通り、Pull through cache は 現時点で Quay.io と ECR Public のみ対応しています。Docker Hub に登録されているイメージはキャッシュできませんが、Pull through cache の発表と同日に Docker の公式イメージが Amazon ECR Public 上で利用可能になったことがアナウンスされました。

https://aws.amazon.com/jp/blogs/news/docker-official-images-now-available-on-amazon-elastic-container-registry-public/

つまり、ECR Public 上に登録されている Docker公式イメージを ECR のプライベートリポジトリにキャッシュすることは可能ということです。

実際にやってみます。Amazon ECR コンソールの Private registry → Pull through cache からプルスルーキャッシュ設定を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/04361704-bad1-041e-3a89-467d075ceb9f.png)

ソースのパブリックレジストリで ECR Public を選択します。送信先の名前空間は任意のものを指定できますが、ここではコンソールでデフォルトで入力されている ecr-public を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/4c7a542e-5a94-db37-0407-eb4526ec7c06.png)

設定はこれだけです。簡単ですね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/7870b843-bf0d-58c9-7150-5b8bb94c6c20.png)

今回は Docker の 公式イメージを pull したいので、[NGINX](https://gallery.ecr.aws/docker/library/nginx) のイメージを例にします。Docker から配信されているイメージであることとその URL を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/5060f586-4b20-62a5-ce20-065018bb18b8.png)

ECR のプライベートレジストリにログイン後、以下の形式で Pull through cache 機能を利用してイメージを pull できます。

`docker pull <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<name_space>/<repository_name>/<image_name>:<tag>`

```shell-session
$ aws ecr get-login-password | docker login --username AWS --password-stdin https://123456789012.dkr.ecr.us-east-1.amazonaws.com
Login Succeeded

$ docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/nginx:1.21.4
1.21.4: Pulling from ecr-public/docker/library/nginx
eff15d958d66: Pull complete 
1e5351450a59: Pull complete 
2df63e6ce2be: Pull complete 
9171c7ae368c: Pull complete 
020f975acd28: Pull complete 
266f639b35ad: Pull complete 
Digest: sha256:097c3a0913d7e3a5b01b6c685a60c03632fc7a2b50bc8e35bcaa3691d788226e
Status: Downloaded newer image for 664573244438.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/nginx:1.21.4
123456789012.dkr.ecr.us-east-1.amazonaws.com/ecr-public/docker/library/nginx:1.21.4
```

以下のようにプライベートリポジトリとしてイメージがキャッシュされていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/439d49cf-02e1-ed20-d9d0-96266ab7429a.png)


## Pull 可能なリポジトリを制限するには?
レジストリのアクセス許可設定で制限可能です。コンソールでは簡単に Pull through cache 用のポリシーを作成できるようになっています。

ポリシータイプで `pull through cache policy` を選択し、任意のステートメント ID を指定します。次に許可を付与する IAM エンティティを追加し、キャッシュルールで指定した名前空間 (ここでは ecr-public) を選択します。Docker 公式イメージの pull のみを許可する場合、リポジトリ名に `docker/*` を指定します。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/765356d9-8a2f-1e8d-cf3f-dd734a91bb0a.png)

実際に作成される json のリソースポリシーは以下のような形式になります。

```json
{
  "Sid": "allow-statement",
  "Effect": "Allow",
  "Principal": {
    "AWS": [
      "<許可する IAM エンティティの ARN>"
    ]
  },
  "Action": [
    "ecr:CreateRepository",
    "ecr:BatchImportUpstreamImage"
  ],
  "Resource": [
    "arn:aws:ecr:us-east-1:123456789012:repository/ecr-public/docker/*"
  ]
}
```

上記のようなレジストリのアクセス許可を設定した状態で、今度は NGINX, Inc. が提供するイメージを pull しようとすると想定通りエラーになりました。ただしエラーメッセージが `does not exist` なので少々わかりづらい気がしますね。

```shell-session
$ docker pull 123456789012.dkr.ecr.us-east-1.amazonaws.com/ecr-public/nginx/nginx:1.21.4
Error response from daemon: repository 123456789012.dkr.ecr.us-east-1.amazonaws.com/ecr-public/nginx/nginx not found: name unknown: The repository with name 'ecr-public/nginx/nginx' does not exist in the registry with id '123456789012'
```


## Pull through cache 機能の注意点など
* 初回のイメージ pull 時に Pull through cache によって透過的に作成されるリポジトリは、Amazon S3 が管理する暗号化キーによって暗号化されます
    * KMS による暗号化を行いたい場合はイメージの pull を行う前に明示的に KMS 暗号化を有効化したリポジトリを作成しておく必要があります
* タグのイミュータビリティもデフォルトで無効になります
    * 有効にすると、Pull through cache によるイメージの更新が正常に行われない可能性があります
* PrivateLink 環境下で ECR を使用している場合、初回のイメージ pull 時のみ NAT Gateway 経由でパブリックレジストリに接続する必要があります
    * 初回の pull 移行、Pull through cache によってイメージの更新が行われる場合は NAT Gateway は不要です
    * ドキュメントに以下のように記述されています。

> When an image is pulled using a pull through cache rule for the first time, if you've configured Amazon ECR to use an interface VPC endpoint using AWS PrivateLink then you need to create a public subnet in the same VPC, with a NAT gateway, and then route all outbound traffic to the internet from their private subnet to the NAT gateway in order for the pull to work. Subsequent image pulls don't require this.



## 公式ドキュメント
https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache.html
　

簡単ですが、以上です。
参考になれば幸いです。