IBM Cloud Private (ICP) 用の各種 CLI を含む Dockerfile を作成しました

# はじめに

IBM Cloud Private (ICP) では `kubectl` や `helm` など各種 CLI のインストーラが付属しますが、バージョンが最新でないため既存の Cloud Foundry や Kubernetes へのアクセス環境と衝突する場合があります。また個々にインストールする必要があるため手間がかかります。

この問題を解決するため ICP Version 3.1.2 に含まれる各種 CLI の導入を含む Dockerfile を作成しました。

- ICP CLI (cloudctl)
- Kubernetes CLI (kubectl)
- Docker (docker)
- Helm CLI (helm)
- Cloud Foundry CLI (cf)
- Istio CLI (istiocli)

本稿では作成した Dockerfile の使い方とトラブルシューティングについて記述します。

なお CLI のセットアップを含む資料にはこちらを利用させていただきました。Docker の導入に始まり、Kubernetes、Helm まで体験する良い自習教材です。
>「ICP入門セルフ・ハンズオン」
> https://github.com/ICpTrial/ICPSelfLab/blob/master/README.md

# Dockerfile の使い方
環境

- ICP Version 3.1.2
- macOS 10.14.4 (Mojave)

クラスター情報は以下を決め打ちしています。異なる場合は適宜、編集してください。

- クラスター名: mycluster.icp
- クラスターアカウント: admin

## ビルド手順
GitHub から Dockerfile を取得します。

```shell:コマンド
git clone https://github.com/naomizenge/icp-clis.git 
cd icp-clis
```
イメージのビルドに必要なファイル、情報を取得します。不要な CLI があれば該当の手順を省略し、Dockerfile からも削除してください。

1) ICP クラスターの IP アドレスを控えます。
2) Docker イメージ管理に必要な証明書を取得します。詳細は[こちら](https://www.ibm.com/support/knowledgecenter/ja/SSBS6K_3.1.2/manage_images/configuring_docker_cli.html)を参照してください。

```shell:コマンド
scp root@<cluster_CA_domain>:/etc/docker/certs.d/<cluster_CA_domain>\:8500/ca.crt ./ca.crt
```
3) Kubernetes CLI 設定に必要なトークンを取得します。
i) ICP ダッシュボードにログインします。
ii) 右上のユーザーアイコンをクリックし、「クライアントの構成」をクリックします。
iii) 中の `--token=` で指定されたトークンをコピーします。

イメージをビルドします。オプションには上で指定した値やファイルへのパスを指定してください。

```shell:コマンド
docker image build \
    --build-arg MYCLUSTER_IP= "192.168.27.100" \
    --build-arg MYCLUSTER_TOKEN="abcabc..." \
    --build-arg CA_CRT_FILE="./ca.crt" \
    -t nzenge/icp-clis:1.0 .
```
リポジトリ内には同じコマンドを発行するビルド実行用のスクリプトファイル `build.sh` もあります。ファイルを編集して必要な変数を指定し、実行してください。

## 実行手順

以下のコマンドを実行すると、各種 CLI を実行可能な状態でシェルが開きます。IP アドレス `192.168.27.100` は上で指定した ICP の IP アドレスで置換してください。

```shell:コマンド
docker container run \
    -v ${PWD}:/workspace \
    -v /var/run/docker.sock:/var/run/docker.sock \
    --add-host mycluster.icp:"192.168.27.100" \
    --add-host api.apps.kube.cf.icp.net:"192.168.27.100" \
    --add-host uaa.apps.kube.cf.icp.net:"192.168.27.100" \
    --add-host doppler.apps.kube.cf.icp.net:"192.168.27.100" \
    --add-host ssh.apps.kube.cf.icp.net:"192.168.27.100" \
    --rm -it --name myicp nzenge/icp-clis:1.0
```
リポジトリ内には同じコマンドを実行するスクリプトファイル `run.sh` があります。ファイルを編集して必要な変数を指定し、実行してください。

# 各種 CLI の設定方法

Dockerfile 内部で実行している各種 CLI の導入方法の詳細については、それぞれのドキュメントを参照してください。

ICP、Kubernetes、Helm、Istio

- ICP ダッシュボード > 左上のハンバーガーメニュー > コマンド・ライン・ツール > Cloud Private CLI

Docker

- https://www.ibm.com/support/knowledgecenter/ja/SSBS6K_3.1.2/manage_images/configuring_docker_cli.html

Cloud Foundry

- https://docs.cloudfoundry.org/cf-cli/install-go-cli.html

# トラブルシューティング
ICP を最初に使い始めて「あれ?」となる項目についてまとめました。

## Q: `kubectl version` でエラー「You must be logged in to the server」が出力される。
```shell-session:エラー
root@46d9ac5bb482:/# kubectl version
Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.4", GitCommit:"f49fa022dbe63faafd0da106ef7e05a29721d3f1", GitTreeState:"clean", BuildDate:"2018-12-14T07:10:00Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
error: You must be logged in to the server (the server has asked for the client to provide credentials)
```

A: ICP サーバーにまずログインする必要があります。`cloudctl` コマンドを使用します。

```shell-session:正常
root@46d9ac5bb482:/# cloudctl login -a https://mycluster.icp:8443 -u <user> -p <password> -n default --skip-ssl-validation
Authenticating...
OK

Targeted account mycluster Account (id-mycluster-account)

Targeted namespace default

Configuring kubectl ...
Property "clusters.mycluster" unset.
Property "users.mycluster-user" unset.
Property "contexts.mycluster-context" unset.
Cluster "mycluster" set.
User "mycluster-user" set.
Context "mycluster-context" created.
Switched to context "mycluster-context".
OK

Configuring helm: /root/.helm
OK
root@46d9ac5bb482:/# kubectl version
Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.4", GitCommit:"f49fa022dbe63faafd0da106ef7e05a29721d3f1", GitTreeState:"clean", BuildDate:"2018-12-14T07:10:00Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.4+icp-ee", GitCommit:"d03f6421b5463042d87aa0211f116ba4848a0d0f", GitTreeState:"clean", BuildDate:"2019-01-17T13:14:09Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
root@46d9ac5bb482:/# 
```

## Q: `helm version` でサーバー情報の取得に失敗する。
```shell-session:エラー
root@46d9ac5bb482:/# helm version
Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Error: cannot connect to Tiller
```
A: サーバーの Tiller にアクセスするには「--tls」オプションを付ける必要があります。

```shell-session:正常
root@46d9ac5bb482:/# helm version --tls
Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1+icp", GitCommit:"8ddf4db6a545dc609539ad8171400f6869c61d8d", GitTreeState:"clean"}
root@46d9ac5bb482:/# 
```

## Q; `docker login` でエラー「Service Unavailable」または「Client.Timeout exceeded while awaiting headers」が出力される。
```shell-session:エラー
root@b4055f512e92:/# docker login mycluster.icp:8500
Username: admin
Password: 
Error response from daemon: Get https://mycluster.icp:8500/v2/: Service Unavailable
```
または

```shell-session:エラー
root@7ec99293c23e:/# docker login mycluster.icp:8500 
Username: admin
Password: 
Error response from daemon: Get https://mycluster.icp:8500/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
root@7ec99293c23e:/# 
```

A: ホスト側の /etc/hosts を確認し、ドメイン名(ここでは「mycluster.icp」)と IP アドレスが含まれることを確認してください。コンテナ内部ではなく、ホスト側(ここでは Mac 側)の /etc/hosts です。

```:/etc/hosts.ini
192.168.27.100  mycluster.icp 
```