AWS の外の Kubernetes から Private ECR のイメージを取得する

どこのご家庭でもあるあるだと思いますが、ご自宅の Kubernetes を運用していて、証明書とか取るの大変だしイメージレジストリを自分で建てるのは億劫…イメージだけは AWS Private ECR に置いておきたい、そんな思いは日常茶飯事だと思います。
ここでは Kubernetes v1.26 で stable になった [kubelet image credential provider](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/) を利用して AWS Private ECR からイメージを取得する方法を説明します。
たぶん10分くらいの作業時間で設定できると思います。

# 忙しい人向けのスクリプト

microk8s を利用している前提のスクリプトです。
細かい事は後で説明しているので、参考にして修正して使って下さいね。

```sh
# credential-provider-config.yaml の設置
cat << EOF > credential-provider-config.yaml
apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
providers:
  - name: ecr-credential-provider
    matchImages:
      - "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com"
    defaultCacheDuration: "12h"
    apiVersion: credentialprovider.kubelet.k8s.io/v1
    env:
      - name: AWS_ACCESS_KEY_ID
        value: "AKIAxxxxxxxxxxxxxxxx"
      - name: AWS_SECRET_ACCESS_KEY
        value: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      - name: AWS_REGION
        value: "ap-northeast-1"
EOF
sudo chown root:root credential-provider-config.yaml
sudo mv credential-provider-config.yaml /var/snap/microk8s/common/

# kubelet の設定
sudo echo '--image-credential-provider-config=\${SNAP_COMMON}/credential-provider-config.yaml' >> /var/snap/microk8s/current/args/kubelet
sudo echo '--image-credential-provider-bin-dir=/usr/local/bin/' >> /var/snap/microk8s/current/args/kubelet

# ecr-credential-provider の設置
curl -SsL -o ./ecr-credential-provider 'https://artifacts.k8s.io/binaries/cloud-provider-aws/v1.29.0/linux/amd64/ecr-credential-provider-linux-amd64'
sudo chown root:root ecr-credential-provider
sudo chmod +x ecr-credential-provider
sudo mv ecr-credential-provider /usr/local/bin/

# microk8s の再起動
# sudo microk8s leave # (必要に応じて)
sudo microk8s stop
sudo microk8s start
```

# 設定手順

[kubelet image credential provider の公式ページ](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-credential-provider/) には `ecr-credential-provider` も文字もあり、ちょいと yaml 書いて kubectl apply すれば即対応できそうですが、EKS の設定済みノードと違って自宅クラスタのノードの場合にはひと手間必要です。

## CredentialProviderConfig を書く

まずは公式ページにもあるような yaml を書きます。

```yaml
apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
providers:
  - name: ecr-credential-provider
    matchImages:
      - "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com"
    defaultCacheDuration: "12h"
    apiVersion: credentialprovider.kubelet.k8s.io/v1
    env:
      - name: AWS_ACCESS_KEY_ID
        value: "AKIAxxxxxxxxxxxxxxxx"
      - name: AWS_SECRET_ACCESS_KEY
        value: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      - name: AWS_REGION
        value: "ap-northeast-1"
```

### 説明

#### /providers/-/name

```yaml
  - name: ecr-credential-provider
```

ECR を使う場合は、この name は固定です。
後述する image credential provider の実行可能ファイル名になるので、変更してはいけません。
ちなみに私はわかりやすい名前に適当に変えてしまって人生を無駄にする方の人間でした。

#### /providers/-/matchImages

```yaml
      - "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com"
```

ECR で適当なイメージの URI をコピーして、最初のスラッシュまでの部分をコピペします。
アカウントID、リージョンがお使いのものに変わると思います。

#### /providers/-/defaultCacheDuration

```yaml
    defaultCacheDuration: "12h"
```

ECR の認証トークンは12時間有効なので、それに合わせて `12h` を設定します。
たぶん変更しない方が良いでしょう。

#### /providers/-/env

```yaml
    env:
      - name: AWS_ACCESS_KEY_ID
        value: "AKIAxxxxxxxxxxxxxxxx"
      - name: AWS_SECRET_ACCESS_KEY
        value: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      - name: AWS_REGION
        value: "ap-northeast-1"
```

ECR にアクセス出来る IAM ユーザーのアクセスキーの情報を入力します。
リージョンは ECR のものに合わせて修正してください。

## CredentialProviderConfig を置く

以下の様なコマンドで然るべき場所にファイルを設置します。
microk8s の場合は以下の様な場所が適切とされていますが、どこに置いても問題ない様です。
これを全てのノードで行います。

```sh
sudo chown root:root credential-provider-config.yaml
sudo mv credential-provider-config.yaml /var/snap/microk8s/common/
```

## image credential provider を設置する

以下の様なコマンドを実行して ecr-credential-provider をノードに設置します。
ここでは `/usr/local/bin/` に置きましたが、後述の kubelet の `-image-credential-provider-bin-dir` オプションを変更する事で変更できます。
これを全てのノードで行います。

```sh
curl -SsL -o ./ecr-credential-provider 'https://artifacts.k8s.io/binaries/cloud-provider-aws/v1.29.0/linux/amd64/ecr-credential-provider-linux-amd64'
sudo chown root:root ecr-credential-provider
sudo chmod +x ecr-credential-provider
sudo mv ecr-credential-provider /usr/local/bin/
```

ここでは v1.29.0 を選択しましたが、kubernetes のバージョンに合わせたバイナリーを選択してください。
バイナリの各種バージョンは以下の URL から選ぶことができます。 (この URL がマジで見つからなくて苦労した)
https://console.cloud.google.com/storage/browser/k8s-artifacts-prod/binaries/cloud-provider-aws


## kubelet の起動オプションを変更する。

microk8s の場合は以下のコマンドを実行する事でオプションを変更できます。
これを全てのノードで行います。

```sh
sudo echo '--image-credential-provider-config=\${SNAP_COMMON}/credential-provider-config.yaml' >> /var/snap/microk8s/current/args/kubelet
sudo echo '--image-credential-provider-bin-dir=/usr/local/bin/' >> /var/snap/microk8s/current/args/kubelet
```

## microk8s の再起動

kubelet の設定を反映するために microk8s を再起動します。
これを全てのノードで行います。

```sh
sudo microk8s stop
sudo microk8s start
```

クラスタを組んでいる場合、1ノードでも CredentialProviderConfig の設定が済んで無い場合にノードが Unschedulable になったり、join 後に microk8s が再起動を繰り返したりする事があります。
とっても怖いので、HAを組んでいる場合は `microk8s leave` してから再起動するのをおすすめします。

# 参考

- [Image pull from ECR private registry is not working #4371](https://github.com/canonical/microk8s/issues/4371)