AWS ECRにDockerイメージをpushする一連の流れを復習する

こんにちは。FLINTERSでデータエンジニアをしている栗原です。今回の投稿はFLINTERSブログ祭りの記事です。テーマは #技術 です。

# AWS ECRとは？？
`AWS Elastic Container Registry`の略で、柔軟性の高いDockerコンテナイメージの格納場所を提供します。簡単に言えば、AWSの提供するDockerイメージ用のリモートリポジトリです。

基本的にはDocker Hubのリポジトリと似たような機能を持っています。

| 項目 | AWS ECR | Docker Hub |
| ---- | ---- | ---- |
| 開発者 | Amazon | Docker社 |
| 用途 | Dockerイメージのリモートリポジトリ | Dockerイメージのリモートリポジトリ |

このように、どちらもリモートでDockerイメージを管理するためのサービスですが、それぞれの提供者によって開発されています。AWS ECRはAmazonのエコシステムと統合されており、特にAWSを利用するプロジェクトでは便利です。

# リモートリポジトリへログインする

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/registry_auth.html

上記ドキュメントにECRでの認証方法が記述されています。自分は以下のコマンドをよく使用しています。

```sh 
aws ecr get-login-password | docker login --username AWS --password-stdin <account_id>.dkr.ecr.ap-northeast-1.amazonaws.com
```

Publicリポジトリを対象にpushする場合は以下のコマンド。

```sh
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
```

## ログインコマンド詳細

### aws ecr get-login-password

このコマンドで、ECRリポジトリの認証用パスワードを取得しています。
この時、環境変数`AWS_ACCESS_SECRET_KEY`と`AWS_SECRET_ACCESS_KEY_ID`と`AWS_DEFAULT_REGION`が認証のために必要になります。

```sh
export AWS_ACCESS_KEY_ID = <your_access_key_id>
export AWS_SECRET_ACCESS_KEY = <your_secret_accesss_key>
export AWS_DEFAULT_REGION = <your_region>
```

:::note info
#### aws-vault
`aws-vault`を使用している人は以下のようにしても実行できます。

```sh
aws-vault exec <profile> -- aws ecr get-login-password
```

#### 1Password CLI
1Passwordを使用している人は、awsプラグインをインストールし認証情報を1Password内に保存することで、環境変数セットせず指紋認証での`aws ecr get-login-password`の実行が可能です。

https://developer.1password.com/docs/cli/shell-plugins/aws/
:::

### docker login --username AWS --password-stdin <repository-url>

先ほど取得した認証用パスワードを標準入力から受け取り、docker-cliを使用して認証を実行するコマンドです。認証に成功すると、ECRのプライベートリポジトリに対して`push`や`pull`ができるようになります。

`repository-url = <account_id>.dkr.ecr.<region>.amazonaws.com`

:::note info
## Finchとdocker-cli
上記の説明ではdocker-cliの`docker login`コマンドを使用してログインしています。docker-cliはオープンソースであり、多くの開発者に広く使われているためです。

しかし、最近はDocker Desktopの有償化に伴い、他のCLIツールからDockerを使用する動きが増えています。特に注目されているのが、AWS製のFinchです。ECRはAWSのサービスであるため、AWS製ツールであるFinchを使用することで、よりシームレスな統合が可能となります。

### Finchについて
最近、AWSはDocker Desktopの代替ツールとしてFinchを発表しました。2023年11月頃にMac版がGA、2024年2月頃にWindows版がGAされたようです。
Finchは軽量で効率的なコンテナ管理ツールであり、特にDocker Desktopの有償化に伴い注目を集めています。

- Finchの特徴
    - 軽量で高性能
    - AWSとシームレスに統合
    - オープンソース
- [公式GitHubページ](https://github.com/runfinch/finch)

今後はAWSとの連携機能等の向上も期待でき、ECRを利用する場合には導入を考えてみても良さそうです。
:::

# ECRからイメージをpullする
```sh
docker pull <your-aws-account-id>.dkr.ecr.<your-region>.amazonaws.com/<your-repository-name>:<tag>
```

ログインした後であれば、認証が通ったプライベートリポジトリからイメージをpullしてくることが可能になります。

:::note info
### Tips: Amazon ECR Publicからコンテナイメージをpullしてくる
ECRでは、Publicでもいくつかのコンテナイメージを公開しています。例えば、AmazonlinuxOSのコンテナイメージは以下の手順でPublic ECRからpullできます。

```sh: Login Command
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
```

```sh: Pull Command
docker pull public.ecr.aws/amazonlinux/amazonlinux:latest
```

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/amazon_linux_container_image.html

:::

# ECRからpullしたイメージを拡張する
例として、ECR Publicで公開されているamazonlinux2に、python3.9をインストールしてみます。

```Dockerfile
# Dockerfile
FROM public.ecr.aws/amazonlinux/amazonlinux:2

# Install necessary packages
RUN yum update -y \
    && yum install -y gcc openssl-devel bzip2-devel libffi-devel wget tar gzip make \
    && yum clean all

# Set the Python version we want to install
ENV PYTHON_VERSION=3.9.13

# Download, compile, and install Python
# 参考: https://qiita.com/hiren/items/17984191da2ab8955174
RUN cd /opt \
    && wget https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz \
    && tar xzf Python-$PYTHON_VERSION.tgz \
    && cd Python-$PYTHON_VERSION \
    && ./configure --enable-optimizations \
    && make altinstall \
    && rm -f /opt/Python-$PYTHON_VERSION.tgz

RUN echo 'alias python=python3.9' >> ~/.bashrc
RUN source ~/.bashrc

# pipenv install
RUN pip3.9 install --upgrade pip \
    && pip3.9 install pipenv

# package install by pipenv
RUN pipenv install pytest==7.4.0
```

上記のDockerfileを作成後、`docker build`コマンドでイメージを作成します。
この時、ベースとなるDockerイメージ`public.ecr.aws/amazonlinux/amazonlinux:2`がローカルに無い場合、自動で`docker pull`が動作しますが、認証を通していないとエラーになるため注意しましょう。

```sh
docker build -t <image_name>:<tag_name> .
```

タグ名を付け間違えた場合に、tagを付け替える際は以下のコマンド。（元のタグ名のイメージも残ります）

```sh
docker tag MISS_IMAGE[:TAG] TARGET_IMAGE[:TAG]
```

:::note info
### Tips: Dockerコンテナ内に入りカスタマイズしたコンテナからイメージを作成する
Dockerコンテナ内に入り、カスタマイズした後でそのコンテナから新しいイメージを作成する方法もあります。以下の手順で実行できます。

1: コンテナを起動し、シェルに入る。
```sh
docker run -it --name my_custom_container public.ecr.aws/amazonlinux/amazonlinux:2 /bin/bash
```
2: 必要な変更を行う。例えば、パッケージのインストールなどを行う。
```sh
yum install -y python3.9
```

3: 変更が完了したら、別のターミナルを開き、カスタマイズしたコンテナから新しいイメージを作成する。

```sh
docker commit my_custom_container my_custom_image:latest
```

4: 必要に応じて、イメージにタグを付ける。

```sh
docker tag my_custom_image:latest my_custom_image:v1.0
```

※ ただし、この方法では何を変更したのかを追跡するのが難しいので、通常はDockerfileを使って新しいイメージを作成することが推奨されます。また、この方法では作成したイメージに含まれる変更点が明確に記録されないため、再現性や移植性に問題が生じる可能性があります。

:::

# 作成したイメージの動作確認をする
以下コマンドでカレントディレクトリの`<sample_file>.py`を実行し、コンテナが正しく動作するか確認します。

```
docker run -v $(pwd):/app --rm <image_name>:<tag_name> python /app/<sample_file>.py
```

# 作成したイメージをECRにpushする

※ あらかじめ、コンソールまたはCLIからECRリポジトリを作成する必要があります。

ECRにpushするためには、以下のようにイメージ名に`account_id`などの情報も含めたイメージ名に変更しておく必要があります。

```sh 
$ docker tag [target_image]:[target_tag] [account_id].dkr.ecr.[region].amazonaws.com/[upload_image]:[upload_tag]
```

```sh:example
docker tag pipeline-upd-newtag:3.9.0 183780520150.dkr.ecr.ap-northeast-1.amazonaws.com/hello-repository:new.tag.1.0
```

その後、`docker push`します。

```sh
$ docker push [account_id].dkr.ecr.[region].amazonaws.com/[upload_image]:[upload_tag]
```