Artifact RegistryのイメージをGKEにデプロイする際に詰まった話

# 概要
2023 年 5 月 15 日より、Container Registry は[非推奨](https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr?hl=ja)となり、Artifact Registry への移行が推奨されています。
こちらの推奨に伴い、弊チームでも GKE にデプロイするイメージを Container Registry から Artifact Registry へ移行することになりました。
この記事では移行の際に詰まったポイントをまとめておきます。

# 結論
以下３つの対応をすればよい。
- `REGISTRY_URL` の記述方法を正しく設定する
- `roles/artifactregistry.writer` の権限を付与する
- リポジトリに対する認証を行う

# Container Registry と Artifact Registry の違い
Container Registry では コンテナイメージのみが保存対象としてサポートされますが、[Artifact Registry](https://cloud.google.com/artifact-registry?hl=ja) ではコンテナイメージだけでなく、言語パッケージ、OS パッケージもサポートされます。
Artifact Registry を利用することで Cloud Run でコンテナを利用せずにソースからサービスを稼働させたりもできるらしいです。（[詳細](https://cloud.google.com/run/docs/deploying-source-code?hl=ja)）

# やりたいこと
以下のようなイメージをビルドして Artifact Registry に登録し、GKE にデプロイするスクリプトを実行します。

```script:deploy.sh
set -e
SCRIPT_DIR=$(cd $(dirname $0) && pwd)

REGISTRY_IMAGE_PROJECT=sample_project
IMAGE_NAME=sample-api
REGISTRY_URL="asia-docker.pkg.dev/${REGISTRY_IMAGE_PROJECT}/${IMAGE_NAME}"

kubectl config use-context my-cluster-context
echo "kube_context=$(kubectl config current-context)"

set -x

# build image
docker build -t ${IMAGE_NAME}:latest .

gcloud auth configure-docker --quiet
docker tag ${IMAGE_NAME} ${REGISTRY_URL}:latest
docker push ${REGISTRY_URL}:latest

# deploy
kubectl set image -n sample-namespace deployment/${IMAGE_NAME} ${IMAGE_NAME}=${REGISTRY_URL}:latest --record
```

# 詰まったポイント１

上記のスクリプトを実行すると、以下のように `REGISTRY_URL` の記述方法がよろしくないと怒られました。
```
name invalid: Missing image name. Pushes should be of the form docker push HOST-NAME/PROJECT-ID/REPOSITORY/IMAGE
```
Container Registry の場合は、`gcr.io/${REGISTRY_IMAGE_PROJECT}/${IMAGE_NAME}` で push できたのですが、Artifact Registry では `REPOSITORY` という階層が増え、`asia-docker.pkg.dev/${REGISTRY_IMAGE_PROJECT}/${REPOSITORY_NAME}/${IMAGE_NAME}`としてあげる必要があるみたいです。
そこで、今回は `REGISTRY_URL` に簡易的に `/sample` という階層を増やしました。

```script:deploy.sh
...
REGISTRY_URL="asia-docker.pkg.dev/${REGISTRY_IMAGE_PROJECT}/${IMAGE_NAME}/sample"
...
```

# 詰まったポイント2

詰まったポイント１で修正したスクリプトを実行すると、次は以下のように権限まわりで怒られました。

```
denied: Permission "artifactregistry.repositories.uploadArtifacts" denied on resource "projects/xxxxxxxxxxxxxxxx" (or it may not exist)
```
使用している Service Account に Artifact Regitstry の書き込み権限が足りなかったためですね。言われた通り、`roles/artifactregistry.writer` の権限を付与します。

# 詰まったポイント3
権限を付与して再度スクリプトを実行したのですが、再度同様に権限まわりで怒られました。
```
denied: Permission "artifactregistry.repositories.uploadArtifacts" denied on resource "projects/xxxxxxxxxxxxxxxx" (or it may not exist)
```
行き詰まったのでドキュメントに目を通してみると、以下の項目を発見。

> リポジトリに対する認証
Docker リポジトリまたは Docker リポジトリで別のサードパーティ クライアントを使用する場合は、リポジトリに対して認証を行う必要があります。このセクションでは、認証に成功するために必要なことの概要を説明します。詳細な手順については、Docker の認証の設定をご覧ください。

> 認証情報ヘルパーの使用
gcloud CLI 認証ヘルパーまたはスタンドアロン認証情報ヘルパーの場合、使用する Artifact Registry ホストが Docker 構成ファイルに含まれている必要があります。
Artifact Registry では、すべてのレジストリ ホストが Docker 構成ファイルに自動的に追加されるわけではありません。構成されたレジストリが多い場合、Docker の応答時間が大幅に遅くなります。構成ファイル内のレジストリの数を最小限に抑えるには、必要なホストをファイルに追加します。


https://cloud.google.com/artifact-registry/docs/docker/pushing-and-pulling?hl=ja#auth

認証情報ヘルパーを利用して、リポジトリに対する認証が必要とのことです。
この観点は自分にはなかったので、ドキュメントを読んでなるほどなと思いました。

ホストを確認してみると、予想通り `asia-docker.pkg.dev` が存在していませんでした。

```
cat ~/.docker/config.json
{
  "credHelpers": {
    "asia.gcr.io": "gcloud",
    "eu.gcr.io": "gcloud",
    "gcr.io": "gcloud",
    "us.gcr.io": "gcloud"
  }
}
```

認証情報ヘルパーを実行してホストを追加します。
```
gcloud auth configure-docker asia-docker.pkg.dev
```

再度スクリプトを実行すると、デプロイできました！！！
お疲れ様でした。