[Kubernetes]IBM Cloud Container Registryを使用してServiceAccountの動作を確認する

#はじめに
Podをデプロイする際にコンテナイメージをPullしていますが、パブリックに公開されているイメージをPullする場合には特に認証は必要ありません。
通常業務に使う場合は、パブリックに公開されているイメージだけでなく自分でBuildしたコンテナイメージも使用すると思います。このような自分でBuildしたイメージはパブリックに公開できない場合もあると思いますので、その場合にはプライベートリポジトリからPullすることになります。その際の認証としてServiceAccountを利用できますので、今回はこの動作を確認したいと思います。

#アーキテクチャ
コンテナレジストリとして一番に思いつくのはDockerHubだと思います。このほかにもAWSやGCPなどのクラウドサービスでもレジストリサービスを提供していますが、今回はIBM Cloudのコンテナレジストリを使用したいと思います。

[IBM Cloud Container Registry](https://cloud.ibm.com/docs/Registry)

アーキテクチャとしては以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/b3e60791-0a46-ad22-d06e-e1facc5c0570.png)



#IBM Cloud Container Registryの作成

##サービスプラン
IBM Cloud Container Registryは以下の範囲で無料で利用できます。有償版にアップグレードするとストレージサイズとプルトラフィックは無制限に利用できるようになりますが、利用量に応じて課金されます。

|項目|無料枠
|:---|:---
|ストレージサイズ|500MB
|プルトラフィック|5GB/月

##作成
IBM Cloudにログインし、カタログから「Container Registry」を探します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/cbec6a64-4824-93b0-4c82-02f8848768db.png)

「作成」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/7e62406f-4a64-7992-56cd-3c88e932f624.png)

ロケーションを「東京」にして、「作成」をクリックします。
そうすると「名前空間の作成」ダイアログが出てきますので、ユニークな名称を設定して作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/db2d6c4e-6972-fe48-c9cb-5b65d56af186.png)

#IBM Cloud CLIのインストール
以下に沿ってIBM Cloud CLIをインストールします。ここではKubernetesクラスタのMasterサーバ（CentOS 7）にインストールします。

[IBM Cloud CLI の概説](https://cloud.ibm.com/docs/cli?topic=cli-getting-started&locale=ja)

```
$  curl -sL https://ibm.biz/idt-installer | bash
[main] --==[ IBM Cloud Developer Tools for Linux/MacOS - Installer, v1.2.3 ]==--
・・・
プラグイン名                           バージョン   状況
cloud-functions/wsk/functions/fn       1.0.41
cloud-object-storage                   1.1.3
container-registry                     0.1.471
container-service/kubernetes-service   1.0.99
・・・
[install_plugins] Finished installing/updating plugins
[env_setup] WARN: Please restart your shell to enable 'ic' alias for ibmcloud!
[install] Install finished.
[main] --==[ Total time: 976 seconds ]==--
```

インストールできたら、ログインします。--ssoを付与すると、ブラウザにワンタイムパスワードが表示されますので、そのパスワードでログインします。

```
$ ibmcloud login --sso
API エンドポイント: https://cloud.ibm.com
```

インストールされたプラグインリストを確認します。

```
$ ibmcloud plugin list
インストール済みプラグインをリストしています...

プラグイン名                           バージョン   状況
container-registry                     0.1.471
container-service/kubernetes-service   1.0.99
cloud-functions/wsk/functions/fn       1.0.41
cloud-object-storage                   1.1.3
```
作成したNamespaceが表示されることを確認します。

```
$ ibmcloud cr namespace-list
レジストリー「jp.icr.io」のアカウント「Kosuke Machida's Account」用の名前空間をリストしています...

名前空間
my-praivate-repo

OK
```

#レジストリへのイメージのPush
作成したレジストリへイメージをPushします。

##IBM Cloud Container Registryへのログイン

```
$ ibmcloud cr login
「jp.icr.io」にログインしています...
「jp.icr.io」にログインしました。
```

##コンテナイメージの作成
DockerHubからnginxのイメージをPullして、commit/tag付けして、IBM CloudにPushします。

###イメージのpull
```
$ docker pull nginx:latest
latest: Pulling from library/nginx
8559a31e96f4: Pull complete
8d69e59170f7: Pull complete
3f9f1ec1d262: Pull complete
d1f5ff4f210d: Pull complete
1e22bfa8652e: Pull complete
Digest: sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
```

###commit
nginxを起動して、index.htmlを更新し、commitします。

```
$ docker run -it nginx /bin/bash
root@62d65397eba0:/# echo "test server" > /usr/share/nginx/html/index.html
root@62d65397eba0:/# exit
$ docker ps -a | grep nginx
62d65397eba0        nginx                                              "/docker-entrypoint.…"   About a minute ago   Exited (0) About a minute ago                       brave_mendeleev
59cbd948358f        nginx                                              "/docker-entrypoint.…"   17 minutes ago       Exited (0) 14 minutes ago                           frosty_herschel
$ docker commit 62d65397eba0 nginx-custom
sha256:fbd9397264a98e3119ea6a5fb83cc40f120d6227d651438e9f2cfe49ac1102d1
$ docker images | grep nginx
nginx-custom                                  latest              fbd9397264a9        15 seconds ago      132MB
nginx                                         latest              2622e6cca7eb        11 days ago         132MB
```

###tag付け

```
$ docker tag fbd9397264a9 jp.icr.io/my-praivate-repo/nginx-custom:latest
$ docker images |grep nginx
jp.icr.io/my-praivate-repo/nginx-custom       latest              fbd9397264a9        5 minutes ago       132MB
nginx-custom                                  latest              fbd9397264a9        5 minutes ago       132MB
nginx                                         latest              2622e6cca7eb        11 days ago         132MB
```

###push
イメージをIBM CloudにPushして確認します。

```
$ docker push jp.icr.io/my-praivate-repo/nginx-custom:latest
The push refers to repository [jp.icr.io/my-praivate-repo/nginx-custom]
d43db004e4c6: Pushed
f978b9ed3f26: Pushed
9040af41bb66: Pushed
7c7d7f446182: Pushed
d4cf327d8ef5: Pushed
13cb14c2acd3: Pushed
latest: digest: sha256:cf199a373ba30d3d8470f4fc9cd904292dedeb0c53f4a5435576121c2dc4ba33 size: 1569
$ ibmcloud cr image-list
イメージをリストしています...

リポジトリー                               タグ     ダイジェスト   名前空間           作成日          サイズ   セキュリティー状況
jp.icr.io/my-praivate-repo/nginx-custom    latest   cf199a373ba3   my-praivate-repo   7 minutes ago   53 MB    7 件の問題

OK
```

####コンソールからの確認
コンソールからも確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/990d6505-8f4b-f137-9b1b-319f00f3d162.png)

何やらセキュリティ状況に「7件の問題」とありますので、確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/492530/c443d5f0-d88d-6940-4772-4d52466c7848.png)

IBM Cloud Container Registryには脆弱性アドバイザー機能があって、自動でコンテナイメージを検査してくれます。便利ですね。
今回は無視して先に進みたいと思います。詳細はこちらに記載されています。
[脆弱性アドバイザーを使用したイメージ・セキュリティーの管理](https://cloud.ibm.com/docs/Registry?topic=va-va_index)

#認証とイメージのPull
Container Registryへのアクセスを許可する認証にはIAM（IBM Cloud Identity and Access Management）を利用します。トークンも利用できるのですが、2020年8月12日以降使用できなくなるようで、非推奨となっています。

[Container Registry のアクセス管理](https://cloud.ibm.com/docs/Registry?topic=Registry-iam)

##IAMの設定
マニュアルを探してみても設定方法がまとまって書かれていませんでした。行きついた先はOpenShift on IBM Cloudのマニュアルです。
Container Registry単独で使うことはあまり想定していないのかも知れませんね。

[イメージ・レジストリーのセットアップ](https://cloud.ibm.com/docs/openshift?topic=openshift-registry&locale=ja#other_registry_accounts)

これに沿ってIAMを設定します。

### IBM Cloud IAM サービスID 
 IBM Cloud IAM サービスID を作成します。

```
$ ibmcloud iam service-id-create test-cluster-id --description "Service ID for IBM Cloud Container Registry"
dingtianhongjie@XXXX.jp として 現行アカウント にバインドされるサービス ID test-cluster-id を作成しています...
OK
サービス ID test-cluster-id が正常に作成されました

ID           ServiceId-533a112f-c0c0-4150-XXXX-XXXXXXXXXXX
名前         test-cluster-id
説明         Service ID for IBM Cloud Container Registry
CRN          crn:v1:bluemix:public:iam-identity::a/3XXXXXXXXXXXXXXXXXXXX::serviceid:ServiceId-533a112f-c0c0-XXXX-XXXX-XXXXXXXXXXXXXX
バージョン   1-4b6d6e29dbXXXXXXXXXXXXXXX
ロック中     false
```

###カスタム IBM Cloud IAM ポリシー
IBM Cloud Container Registry へのアクセス権を与えるカスタム IBM Cloud IAM ポリシーを作成したサービスIDに設定します。

```
$ ibmcloud iam service-policy-create test-cluster-id --roles Manager --service-name container-registry
dingtianhongjie@XXXX.jp としてサービス ID test-cluster-id の 現行アカウント にポリシーを作成しています...
失敗
役割 Manager が見つかりませんでした。 有効な役割は 管理者, ライター, リーダー, ビューアー, 管理者, オペレーター, エディター です。
$ ibmcloud iam service-policy-create test-cluster-id --roles 管理者 --service-name container-registry
dingtianhongjie@XXXX.jp としてサービス ID test-cluster-id の 現行アカウント にポリシーを作成しています...
OK
サービス・ポリシーは正常に作成されます


ポリシー ID:   7f0c250f-68b2-XXXX-XXXX-XXXXXXXXXX
バージョン:    1-558268f56XXXXXXXXXXXXXXXXXXXX
役割:          管理者
リソース:
               サービス名   container-registry

```

マニュアルの通りにrolesに「Manager」を指定したら失敗。メッセージの通りに「管理者」としたら通りました。全角文字で指定することがあるんですね。

###APIキー
サービス ID の API キーを作成します。

```
$ ibmcloud iam service-api-key-create test-cluster-key test-cluster-id --description "Service ID for IBM Cloud Container Registry"
dingtianhongjie@XXXX.jp としてアカウント 3fec0071XXXXXXXXXXXXXXX にサービス ID test-cluster-id の API キー test-cluster-key を作成しています...
OK
サービス ID API キー test-cluster-key が作成されます

API キーを保存してください。 作成後に取得することはできません。

ID         ApiKey-5282e4cd-61f2-XXXX-XXXX-XXXXXXXXXXXX
名前       test-cluster-key
説明       Service ID for IBM Cloud Container Registry
作成日時   2020-06-21T12:35+0000
API キー   i6KasN38UK--XXXXXXXXXXXXXXXXXX_XXXXXX
ロック中   false
```

##SecretとServiceAccountの作成
作成したAPIキーを元にSecretとServiceAccountを作成します。

###Secret
以下のコマンドで作成します。

|パラメータ|設定値
|:---|:---
|docker-server|Container Resistryのリージョンに応じて設定
|docker-username|IBM Cloudの場合「iamapikey」固定
|docker-password|APIキー
|docker-email|メールアドレス（架空のアドレスでも可）

```
$ kubectl create secret docker-registry ibmcr --docker-server=jp.icr.io --docker-username=iamapikey --docker-password=i6KasN38UK--XXXXXXXXXXXXXXXXX_XXXXXX --docker-email=dingtianhongjie@XXXXXXX.jp
$ kubectl describe secrets ibmcr
Name:         ibmcr
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/dockerconfigjson

Data
====
.dockerconfigjson:  229 bytes
```

###ServiceAccount
以下のマニフェストで作成します。

```serviceAccount-imagePull.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: service-account-image-pull
imagePullSecrets:
  - name: ibmcr #作成したSecretを指定
```

```
$ kubectl apply -f serviceAccount-imagePull.yaml
serviceaccount/service-account-image-pull created
$ kubectl describe serviceaccounts service-account-image-pull
Name:                service-account-image-pull
Namespace:           default
Labels:              <none>
Annotations:         kubectl.kubernetes.io/last-applied-configuration:
                       {"apiVersion":"v1","imagePullSecrets":[{"name":"ibmcr"}],"kind":"ServiceAccount","metadata":{"annotations":{},"name":"service-account-imag...
Image pull secrets:  ibmcr
Mountable secrets:   service-account-image-pull-token-mfn8r
Tokens:              service-account-image-pull-token-mfn8r
Events:              <none>
```

##イメージのPull（Podのデプロイ）

以下のマニフェストでPodをデプロイします。
なお、ServiceAccountを作成しない場合、PodにimagePullSecretsを指定することでも可能です。

```pod02.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-custom
spec:
  containers:
  - name: nginx
    image: jp.icr.io/my-praivate-repo/nginx-custom:latest
    command:
    - sh
    - -c
    args:
    - tail -f /dev/null
  serviceAccountName: service-account-image-pull
```

```
$ kubectl apply -f pod02.yaml
pod/nginx-custom created
$ kubectl get pod
NAME           READY   STATUS    RESTARTS   AGE
nginx-custom   1/1     Running   0          4s
```

念のためコンテナにログインしてindex.htmlが編集したものになっているか確認します。

```
$ kubectl exec -it nginx-custom cat /usr/share/nginx/html/index.html
test server
```

また、PodのマニフェストでServiceAccountを指定しない、かつimagePullSecretsを指定しない場合、以下のように認証ができずPullに失敗します。

```
$ kubectl apply -f pod03.yaml
pod/nginx-custom-temp created
$ kubectl describe pod nginx-custom-temp
Name:         nginx-custom-temp

Events:
  Type     Reason     Age               From                   Message
  ----     ------     ----              ----                   -------
  Normal   Scheduled  12s               default-scheduler      Successfully assigned default/nginx-custom-temp to k8s-worker02
  Normal   Pulling    11s               kubelet, k8s-worker02  Pulling image "jp.icr.io/my-praivate-repo/nginx-custom:latest"
  Warning  Failed     11s               kubelet, k8s-worker02  Failed to pull image "jp.icr.io/my-praivate-repo/nginx-custom:latest": rpc error: code = Unknown desc = Error response from daemon: Get https://jp.icr.io/v2/my-praivate-repo/nginx-custom/manifests/latest: unauthorized: authentication required
  Warning  Failed     11s               kubelet, k8s-worker02  Error: ErrImagePull
  Normal   BackOff    9s (x2 over 10s)  kubelet, k8s-worker02  Back-off pulling image "jp.icr.io/my-praivate-repo/nginx-custom:latest"
  Warning  Failed     9s (x2 over 10s)  kubelet, k8s-worker02  Error: ImagePullBackOff
```


#まとめ
今回はServiceAccountというよりも、IBM Cloudの設定に苦戦しました。
まあ、クラウドの設定からSeviceAccountでの動作確認までできましたので、どちらも理解が深まりました。今回はContainer Registry単体で利用しましたので、CICDにも応用していきたいと思います。