Amazon ECRがOCI ArtifactをサポートしてHelm chartsも管理できるようになったよ

## はじめに
2020/8/28 に Amazon ECR が OCI Artifact をサポートしました。
これにより Helm charts や OPA Bundles など、コンテナイメージ以外の
コンテンツタイプ (アーティファクト) を ECR で保存、配布できるようになります。

**OCI Artifact Support In Amazon ECR**
https://aws.amazon.com/jp/blogs/containers/oci-artifact-support-in-amazon-ecr/

例えば組織内で安全に Helm charts を共有しようとした場合に
これまでは [ChartMuseum](https://chartmuseum.com/) 等を使用して、プライベートな
Helm chart registry を構築する必要がありましたが、ECR だけで管理できるようになります。

8/17の AWS Container Day at KubeCon で Developer Preview として発表されたばかりだった
認識ですが、2週間もたたずに正式リリース？となったようです。
今回のローンチの対象は SDK と CLI のサポートで、マネジメントコンソール側の変更は
今年後半にリリース予定とのこと。

ほぼドキュメント通りの手順になってしまいますが、やってみます。

## 作業環境
* **Helm:** v3.3.0
* **AWS CLI:** 2.0.43
* **Kubernetes:** 1.16.5 (Docker Desktop)

## Helm のインストール
クライアント端末に Helm 3 がインストールされていない場合はインストールしておきます。
Github のリリースページからバイナリを取得、インストールスクリプトの使用、
パッケージマネージャー経由など複数のインストール方法が準備されています。

**Installing Helm**
https://helm.sh/docs/intro/install/

```shell-session
$ helm version
version.BuildInfo{Version:"v3.3.0", GitCommit:"8a4aeec08d67a7b84472007529e8097ec3742105", GitTreeState:"dirty", GoVersion:"go1.14.7"}
```

Helm 3 における OCI のサポートは 現在 experimental な機能とみなされているため
`HELM_EXPERIMENTAL_OCI` を環境変数に設定する必要があります。

```shell-session
export HELM_EXPERIMENTAL_OCI=1
```

## ECR レポジトリの作成と認証
AWS CLI で作成します。
特に追加のオプション等は不要で、いつもどおり作成できます。

```shell-session
$ aws ecr create-repository --repository-name helm-test
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:123456789012:repository/helm-test",
        "registryId": "123456789012",
        "repositoryName": "helm-test",
        "repositoryUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test",
        "createdAt": "2020-08-29T12:22:02+09:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}
```

以下のコマンドで Helm クライアントを ECR に認証します。

```shell-session
$ aws ecr get-login-password | helm registry login \
> --username AWS --password-stdin 123456789012.dkr.ecr.region.amazonaws.com
Login succeeded
```

`aws ecr get-login` コマンドは非推奨になっており、AWS CLI v2 では削除されているため
`aws ecr get-login-password` を使用します。

**参考:AWS CLIでECRにログインする時はget-loginではなくget-login-passwordを使おう**
https://qiita.com/hayao_k/items/3e4c822425b7b72e7fd0

## Chart の作成
`helm create` コマンドで Chart の雛形を作成することができます。

```shell-session
$ mkdir helm-test 
$ cd helm-test

$ helm create mychart
Creating mychart
```
今回はこの雛形をそのまま利用します。

## ECR へ Push
`helm chart save` コマンドで Chart ディレクトリをローカルキャッシュに保存します。
この際、リモートの ECR の URI を使用して、エイリアスを作成します。
このあたりは、コンテナイメージを push するときと同じ感覚です。

```shell-session
$ helm chart save mychart mychart
ref:     mychart:0.1.0
digest:  c455f9c99430ee099ea66999a40a0978947ccfd76e6922cdb8a911491326b0ed
size:    3.5 KiB
name:    mychart
version: 0.1.0
0.1.0: saved

$ helm chart save mychart 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
mychart: saved

$ helm chart list
REF                                                             NAME    VERSION DIGEST  SIZE    CREATED
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-te...    mychart 0.1.0   4865989 3.5 KiB 28 seconds
mychart:0.1.0                                                   mychart 0.1.0   4865989 3.5 KiB About a minute
```

`helm chart push` コマンドで ECR に push できます。

```shell-session
$ helm chart push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test]
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
mychart: pushed to remote (1 layer, 3.5 KiB total)
```

今回のリリースではマネジメントコンソールに変更が入っていないため、
現在は普通に Docker イメージが格納されているだけのように見えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/66f058a6-3fa0-c44b-5610-f6fa57d89aac.png)

push した Chart を確認すると、適切な artifactMediaType で保存されていることがわかります。

```shell-session
$ aws ecr batch-get-image --repository-name helm-test --image-ids imageTag=mychart --query 'images[].imageManifest'
[
    "{\"schemaVersion\":2,\"config\":{\"mediaType\":\"application/vnd.cncf.helm.config.v1+json\",\"digest\":\"sha256:65a07b841ece031e6d0ec5eb948eacb17aa6d7294cdeb01d5348e86242951487\",\"size\":141},\"layers\":[{\"mediaType\":\"application/tar+gzip\",\"digest\":\"sha256:acd033c1b722f7dcbfb38a909c422540c584feef52ad4aedb8289eb074638f27\",\"size\":3562}]}"
]
```

## ECR から Chart を pull する
pull を確認するため、`helm chart remove` コマンドで ローカルキャッシュ上の Chart を削除します。

```shell-session
$ helm chart remove 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
mychart: removed

$ helm chart remove mychart:0.1.0
0.1.0: removed
```

`helm chart pull` コマンドで pull します。

```shell-session
$ helm chart pull 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
mychart: Pulling from 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test
ref:     123456789012.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
Status: Downloaded newer chart for 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart

$ helm chart list
REF                                                             NAME    VERSION DIGEST  SIZE    CREATED
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-te...    mychart 0.1.0   4865989 3.5 KiB 9 hours
```

## Chart のデプロイ
pull した Chart を使用して Kubernetes にデプロイするには
`helm chart export` コマンドを使用して一度ローカルに export する必要があります。

```shell-session
helm chart export 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart --destination ./chart-install
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
Exported chart to chart-install\mychart/
```

`helm install` コマンドで Namespce: helm-test に Chart をインストールします。

```shell-session
$ cd chart-install
$ kubectl create ns helm-test
namespace/helm-test created

$ helm install mychart ./mychart -n helm-test
NAME: mychart
LAST DEPLOYED: Sun Aug 30 11:28:24 2020
NAMESPACE: helm-test
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace helm-test -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=mychart" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace helm-test port-forward $POD_NAME 8080:80
```

問題なくデプロイできました。

```shell-session
$ kubectl get all -n helm-test
NAME                           READY   STATUS    RESTARTS   AGE
pod/mychart-5965fbff94-xxnd2   1/1     Running   0          32s

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/mychart   ClusterIP   10.99.155.110   <none>        80/TCP    32s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mychart   1/1     1            1           32s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/mychart-5965fbff94   1         1         1       32s

$ helm list -n helm-test
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
mychart helm-test       1               2020-08-30 11:28:24.6691172 +0900 JST   deployed        mychart-0.1.0   1.16.0
```

削除は `helm uninstall` コマンドを使用します。

```shell-session
$ helm uninstall mychart -n helm-test
release "mychart" uninstalled

$ kubectl delete ns helm-test
namespace "helm-test" deleted
```

## Chart を ECR から削除する
ECR 上の Chart を削除する手順は、Docker イメージの時と変わりません。

```shell-session
$ aws ecr batch-delete-image --repository-name helm-test --image-ids imageTag=mychart
{
    "imageIds": [
        {
            "imageDigest": "sha256:4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3",
            "imageTag": "mychart"
        }
    ],
    "failures": []
}
```

コンソールから削除を行うことも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/1826b718-973e-5427-db08-7026659488cc.png)

## ECR Public に push する
2020/12/1 にリリースされた ECR Public に helm chart を push して公開することも可能です。
基本的には手順は変わらないのですが、AWS CLI で操作する場合は以下の点に注意します。

* ecr-public という専用のサブコマンド使用する
* リージョンは us-east-1 を指定する

リポジトリを作成、

```shell-session
$ aws ecr-public create-repository --repository-name helm-test --region us-east-1
{
    "repository": {
        "repositoryArn": "arn:aws:ecr-public::123456789012:repository/helm-test",
        "registryId": "123456789012",
        "repositoryName": "helm-test",
        "repositoryUri": "public.ecr.aws/xxxxxxxx/helm-test",
        "createdAt": 1607177292.959
    },
    "catalogData": {}
}
```

認証し、

```shell-session
$ aws ecr-public get-login-password --region us-east-1 | helm registry login \
> --username AWS --password-stdin public.ecr.aws/xxxxxxxx
Login succeeded
````

push すると

```shell-session
$ helm chart save mychart public.ecr.aws/xxxxxxxx/helm-test:mychart
$ helm chart push public.ecr.aws/xxxxxxxx/helm-test:mychart
```

ECR Public Gallery に公開されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/48b7a213-4a0b-2b06-d9d2-59afa221fdd8.png)


## 参考
**Pushing a Helm chart**
https://docs.aws.amazon.com/AmazonECR/latest/userguide/push-oci-artifact.html

以上です。
参考になれば幸いです。