IBM Cloud レジストリ・サービスを利用したカスタム・コンテナのデプロイ手順のメモ


IBM Cloud のレジストリ・サービス利用方法のメモを書きました。 k8sライトプランで、欧州パリDCへ、アプリのカスタム・コンテナをデプロイする手順が解ります。


## コンテナのイメージ・レジストリ・サービスとは

コンテナは、アプリケーション、ミドルウェア、ライブラリ、OSを一つにパッケージングした実行イメージであると表現できると思います。　用途に応じて、OSのカスタムイメージの基礎部分のコンテナ、ミドルウェアまで含めた目的別コンテナ、アプリまでを含めた本番用コンテナなどなど、様々なレベルのコンテナを作成しておいて、再利用することで生産性を高くすることができます。　

Dockerfile でコンテナをビルドする場合、コンテナのパブリックなイメージ・レジストリから、Linuxディストリビューションの公式コンテナを指定することができます。　また、アプリケーションのコンテナのイメージを作成して、k8sにデプロイする場合には、他人がアクセスできないプライベートなイメージ・レジストリが必要になります。

IBM Cloud のコンテナ・イメージ・レジストリは、プライベートなコンテナのイメージ・レジストリを提供してくれるサービスです。


### パブリックなレジストリ・サービスからプルする例

nginxのコンテナの最新バージョンを 自分のMacOSのDocker環境に、プルする場合は次の様にできます。 コンテナを実行する際に、これによってダウンロードされたコンテナ・イメージを利用できます。 この例からも解る様に、ユーザーIDやパスワードといった認証情報を要求せず、誰でも利用できるのが、パブリックなレジストリ・サービスです。

~~~
$ docker pull nginx:latest
latest: Pulling from library/nginx
bc95e04b23c0: Pull complete 
f3186e650f4e: Pull complete 
9ac7d6621708: Pull complete 
Digest: sha256:b81f317384d7388708a498555c28a7cce778a8f291d90021208b3eba3fe74887
Status: Downloaded newer image for nginx:latest
~~~

### プライベートなレジストリからプルする例

プライベート・イメージ・レジストリの動作について確認をしてみます。

#### 認証なしで失敗する例

これに対して、プライベートなレジストリ・サービスでは、HTTPSの暗号通信を利用して、認証をパスしていなければ利用することができません。
次の例では、前述のパブリックなレジストリ・サービスへアクセスするのと同じ方法でアクセスした例です。認証がされていないとして、アクセスが拒否されているのが判ります。

~~~
$ docker pull registry.ng.bluemix.net/takara/web:v3
Error response from daemon: Get https://registry.ng.bluemix.net/v2/takara/web/manifests/v3: unauthorized: The login credentials are not valid, or your Bluemix account is not active.
~~~

#### 認証してプルする例

IBM Cloud では、Bluemix コマンドで、`bx login`した後に、`bx cr login` することで、レジストリ・サービスから、ローカルのDocker環境のイメージ・レジストリにダウンロードすることができます。

~~~
$ bx login -a api.ng.bluemix.net -u <ユーザーID>
<中略>
$ bx cr login
<中略>
$ docker pull registry.ng.bluemix.net/takara/web:v3
v3: Pulling from takara/web
c7fefcc4c54c: Pull complete 
7ff5d10493db: Pull complete 
d2562f1ae1d0: Pull complete 
dbbab0a0e002: Pull complete 
Digest: sha256:659e3b2cf19e66755e0b7928a64a362c23f8e8bfc34a8a03ec13d16a811e4760
Status: Downloaded newer image for registry.ng.bluemix.net/takara/web:v3
~~~

確認は、次のコマンドで出来ます。

~~~
$ docker images |grep registry.ng.bluemix.net/takara/web
registry.ng.bluemix.net/takara/web   v3                  7d5c744a7400        5 days ago          108MB
~~~



#### IBM Cloud レジストリ・サービスのパブリック部分からのイメージのプル

リストを表示するには、`bx login` が必須なのですが、IBMが提供するコンテナのイメージは、認証無しでも、ローカルにプルする事ができます。
これは、ユーザー専用のネームスペース下にあるか、否かで区別されています。 前述のプライベートなケースでは、`registry.ng.bluemix.net/takara/web:v3` の中でも、`takara` というユーザー個別に付与したディレクトリ配下（名前空間またはネームスペース）では、認証が必須となります。

~~~
$ docker pull registry.ng.bluemix.net/ibmnode:v4
v4: Pulling from ibmnode
5fd82f9f675e: Pull complete 
7750e90babbd: Pull complete 
2b4b598d6c57: Pull complete 
71567ba937dc: Pull complete 
f45e29b1a414: Pull complete 
788f5e0a14eb: Pull complete 
66471d4d6c84: Pull complete 
de48e42fac22: Pull complete 
b8eaba5e2056: Pull complete 
ff03427b7ef0: Pull complete 
Digest: sha256:d7b62323ce83c31cef2d05c5fca46eecdb065b7b3e2891b1ec3aa5eab2767502
Status: Downloaded newer image for registry.ng.bluemix.net/ibmnode:v4
~~~~

でも、リストを得るには、ログインが必要です。

~~~
imac:~ maho$ bx cr images --include-ibm
失敗
IBM Cloud にログインしていません。
「bx login」コマンドを実行してログインしてください。
~~~


## レジストリのリージョンの切り替え

レジストリ・サービスが提供されるデータセンターのリストを参照して切り替えるには、次のコマンドを実行します。

~~~
$ bx cr region-set 
領域を選択してください
1. ap-south ('registry.au-syd.bluemix.net')
2. eu-central ('registry.eu-de.bluemix.net')
3. international ('registry.bluemix.net')
4. uk-south ('registry.eu-gb.bluemix.net')
5. us-south ('registry.ng.bluemix.net')
Enter a number ()> 
~~~

現在、セットされているリージョンを確認するには、次のコマンドを使います。

~~~
$ bx cr region
領域「international」をターゲットにしています。領域は「registry.bluemix.net」です。
~~~

リージョンを指定して切り替えるには、`bx cr region-set` の後に、リージョン名を指定します。

~~~
$ bx cr region-set us-south
領域は「us-south」に設定されました。領域は「registry.ng.bluemix.net」です。
~~~


## IBM Cloud k8s ライトプランの場合のレジストリ・サービスの利用

2017年12月現在のk8sのLITEプランでは、パリのデータセンターにクラスタが作成されます。 そこで、レジストリは、eu-central を利用する必要があります。

レジストリに自分のカスタムなコンテナを登録するにあたって、次の２つの方法を選べます。　前者は自分の開発環境やパソコン環境に、Dockerがインストールされている必要があります。 一方、後者は、コンテナのビルドをレジストリ・サービスで実行してくれるので、手順が省けてとても楽に利用できます。

* ローカルのDocker環境でビルドする方法
* IBM Cloud レジストリ・サービスでビルドする方法


### 共通の準備
Dockerfile と アプリケーションのコードの入ったデリレクトりを準備します。　今回は、nginxでウェブページを表示するだけの簡単なサンプル的なコンテナですので、準備するのは、以下の内容です。 

~~~
k8s_nginx_web$ tree
.
├── Dockerfile
└── html
    └── index.html
~~~

Dockerfileは、パブリックのレジストリ・サービスから、最新版の公式nginxコンテナを取得します。 そして、nginxのhtmlディレクトリをローカルのhtmlディレクトリで上書きします。

~~~file:Dockerfile 
FROM nginx:latest
ADD ./html /usr/share/nginx/html
~~~

htmlのディレクトリには、index.htmlだけを入れておきます。内容は簡単に、以下の様にします。

~~~file:index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx! on IBM Cloud Kubernetes</h1>
<p>Thank you for using nginx on IBM Cloud Kubernetes.</p>
</body>
</html>
~~~


### ローカルでビルドして登録する方法

#### ローカルでのコンテナのビルド

コンテナをパソコンの開発環境のDockerでビルドします。 これで、nginxでhtmlディレクトリ以下のコンテンツを応答するコンテナが出来上がります。 コンテナの名前は、-t のパラメータで指定した `nginx.webpage:v1` になります。

~~~
k8s_nginx_web$ docker build -t nginx.webpage:v1 .
Sending build context to Docker daemon  3.584kB
Step 1/2 : FROM nginx:latest
 ---> 9e7424e5dbae
Step 2/2 : ADD ./html /usr/share/nginx/html
 ---> d85fe6ed6d92
Successfully built d85fe6ed6d92
Successfully tagged nginx.webpage:v1
~~~

#### ローカルでのコンテナのテスト

ビルドしたコンテナをバックグランドで実行します。 コンテナから外部に公開するポート番号は、root権限がなくても公開できる8001を `-p` のパラメータで設定します。

~~~
k8s_nginx_web$ docker run -d -p 8001:80 nginx.webpage:v1
64b72a2dfa973ed96df034b7d04267d584ddf7001843f8e8f7c9e66f1f043dff
~~~

curlで、結果を確認します。 下記では index.htmlの内容が返されましたので、テスト成功です。

~~~
k8s_nginx_web$ curl http://localhost:8001/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx! on IBM Cloud Kubernetes</h1>
<p>Thank you for using nginx on IBM Cloud Kubernetes.</p>
</body>
</html>
~~~

#### レジストリ・サービスへのアップロード

1. `bx login` で認証
2. `bx cr region-set eu-central` でレジストリを欧州に切り替え
3. `bx cr login` でレジストリ・サービスの初期化
4. `bx cr namespace-add` ネームスペースの作成（未作成の場合)
5. `docker tag` ローカルのコンテナ名とリモートでのコンテナ名を関連づけ
6. `docker push` リモートのコンテナ名を指定してアップロードします。
7. `bx cr images` と `bx cr image-inspect` で登録状態の確認
8. `bx cr va` で脆弱性検査


ユーザーIDとパスワードで認証をパスします。

~~~
$ bx login
~~~
次のコマンドで、ログインするべきレジストリ・サービスを指定します。

~~~
$ bx cr region-set eu-central
~~~
以下のコマンドを実行するだけで、前述の認証がパスできれいれば、レジストリ・サービスへログインした事になります。

~~~
$ bx cr login
~~~
もし、レジストリに、ネームスペースを設定していなければ設定ます。 この例では<your name space>に "takara"を設定しています。ご自身の名前やプロジェクトの名前に置き換えて頂けると良いと思います。

~~~
$ bx cr namespace-add <your name space>
~~~
次のコマンドで、ローカルのレジストリのタグとレジストリ・サービスのタグを関連づけます。

~~~
$ docker tag nginx.webpage:v1 registry.eu-de.bluemix.net/takara/webpage:v1
~~~
次に コンテナをローカルからリモートへプッシュします。　実行結果でdigestが表示されたら完了です。

~~~
docker push registry.eu-de.bluemix.net/takara/webpage:v1
The push refers to a repository [registry.eu-de.bluemix.net/takara/webpage]
b3b828555bcb: Pushed 
63c39cd4a775: Mounted from takara/web 
350d50e58b6c: Mounted from takara/web 
cec7521cdf36: Mounted from takara/web 
v1: digest: sha256:15c5d19b3eb5e5388867aa9a1d059c94bbbf96068cd55e0b3c7668811ea52d3b size: 1155
~~~
登録したコンテナを確認します。エントリの有無を確認するには、次のコマンドが便利です。 

~~~
$ bx cr images
Listing images...

REPOSITORY                                  NAMESPACE   TAG   DIGEST         CREATED        SIZE    VULNERABILITY STATUS   
registry.eu-de.bluemix.net/takara/webpage   takara      v1    15c5d19b3eb5   1 hour ago     44 MB   OK   
~~~
さらに、リポジトリ名を指定して、詳細を表示します。 この時、リポジトリ：TAGまで指定が必要な点が注意点です。

~~~
$ bx cr image-inspect registry.eu-de.bluemix.net/takara/webpage:v1
~~~
脆弱性検査を実行するには、次の様に実行します。

~~~
bx cr va registry.eu-de.bluemix.net/takara/webpage:v1
Checking vulnerabilities for 'registry.eu-de.bluemix.net/takara/webpage:v1'...

Image 'registry.eu-de.bluemix.net/takara/webpage:v1' was last scanned on Sun Dec 10 15:19:53 UTC 2017.
The scan results show the image is SAFE to deploy.
~~~

これで、レジストリ・サービスへの登録が完了しました。　コンテナのビルドと登録に、こんなに手間がかかるなんて、本末転倒の様な印象を受けたのではないかと心配しますが、ツールと連携させるなど、自動化を計画していくのが良いですね。　それから、次の `bx cr build` が便利です。 


### レジストリ・サービスでビルドする方法

`bx cr` コマンドは、内部で docker を利用しているので、Dockerがインストールされていないと動作しません。 しかし、一回の操作で、ビルドとリポジトリ登録が完了するので、手間が省けて楽になると思います。

Dockerfile とアプリのディレクトリが存在するディレクトリで、次のコマンドを実行することで、ビルドとアップロードを実行してくれます。

~~~
$ bx cr build --tag registry.eu-de.bluemix.net/takara/webpage:v1 .
~~~

以下は、実行例です。 とっても便利ですね。

~~~
k8s_nginx_web$ bx cr build --tag registry.eu-de.bluemix.net/takara/webpage:v1 .
Sending build context to Docker daemon  3.584kB
Step 1/2 : FROM nginx:latest
latest: Pulling from library/nginx
bc95e04b23c0: Pull complete 
f3186e650f4e: Pull complete 
9ac7d6621708: Pull complete 
Digest: sha256:b81f317384d7388708a498555c28a7cce778a8f291d90021208b3eba3fe74887
Status: Downloaded newer image for nginx:latest
 ---> 9e7424e5dbae
Step 2/2 : ADD ./html /usr/share/nginx/html
 ---> dc75f437f5d6
Removing intermediate container 01cef70f4e80
Successfully built dc75f437f5d6
Successfully tagged registry.eu-de.bluemix.net/takara/webpage:v1
The push refers to a repository [registry.eu-de.bluemix.net/takara/webpage]
3479ac0c906f: Pushed 
63c39cd4a775: Layer already exists 
350d50e58b6c: Layer already exists 
cec7521cdf36: Layer already exists 
v1: digest: sha256:3adc98058ff2fb53fd9586b617b9c99944da3cda1c2f4bbfcca395f2ba7e08a6 size: 1155

OK
k8s_nginx_web$ bx cr image-list
Listing images...

REPOSITORY                                  NAMESPACE   TAG   DIGEST         CREATED          SIZE    VULNERABILITY STATUS   
registry.eu-de.bluemix.net/takara/webpage   takara      v1    3adc98058ff2   13 seconds ago   44 MB   OK   
~~~



### ライトプランのk8sクラスタへデプロイするには

ここまでで、欧州データセンターのレジストリ・サービスに、カスタムのコンテナが登録されたので、これを利用して、パリDCのk8sクラスタにデプロします。

デプロイするための YAMLファイルは、以下になります。 ライトプランでは、LoadBalancer や ingress サービスが利用できないので、NodePortでインターネットへ公開します。この部分の記述が、18行以降になります。 そして、コンテナのイメージは、14行目の指定になります。

~~~file:nginx-nodeport.yml
     1	apiVersion: extensions/v1beta1
     2	kind: Deployment
     3	metadata:
     4	  name: nginx-deployment
     5	spec:
     6	  replicas: 3
     7	  template:
     8	    metadata:
     9	      labels:
    10	        app: nginx
    11	    spec:
    12	      containers:
    13	      - name: nginx
    14	        image: registry.eu-de.bluemix.net/takara/webpage:v1
    15	        ports:
    16	        - containerPort: 80
    17	---
    18	apiVersion: v1
    19	kind: Service
    20	metadata:
    21	  name: nginx-svc
    22	spec:
    23	  type: NodePort
    24	  selector:
    25	    app: nginx
    26	  ports:
    27	  - protocol: TCP
    28	    port: 80
    29	    nodePort: 31580
~~~


このファイルをkubectlでデプロイするまでの実行手順は以下です。

1. `bx login`
2. `bx cr region-set eu-central` 欧州のレジストリサービスへ設定
3. `bx cr login` レジストリサービスへログイン
4. `bx cs init --host https://eu-central.containers.bluemix.net' 欧州パリDCへ接続先を指定
5. `bx cs clusters`
6. `bx cs cluster-config <mycluster>`
7. `export KUBECONFIG=...` 上記で表示される環境変数をexportする
8. `kubectl create -f nginx-nodeport.yml` 上記のYAMLを適用する
9. `kubectl get svc` で公開ポート番号を確認
9. `bx cs workers` パブリック IPを取得
10. `curl`で接続テスト

下記の例は、上記の項目9番以降を実行した例です。


~~~
$ kubectl get svc
NAME         CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   172.21.0.1       <none>        443/TCP        12d
nginx-svc    172.21.203.163   <nodes>       80:31580/TCP   7m

$ bx cs workers mycluster1
OK
ID                                                 Public IP      Private IP       Machine Type   State    Status   Version   
kube-par01-pa1d12e0ad8afc4e69b27f5adce87162d4-w1   169.51.21.83   10.126.127.203   free           normal   Ready    1.7.4_1503*   

* Run worker-update to update to version 1.7.4_1504. For update steps and Kubernetes version notes, see the docs: https://console.bluemix.net/docs/containers/cs_cluster.html#cs_cluster_worker_update

$ curl http://169.51.21.83:31580/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx! on IBM Cloud Kubernetes</h1>
<p>Thank you for using nginx on IBM Cloud Kubernetes.</p>
</body>
</html>
vagrant@vagrant-ubuntu-trusty-64:~/share/k8s/k8s_webpage$ 
~~~






# 参考資料
[1] [IBM Cloud Container Registry CLI](https://console.bluemix.net/docs/cli/plugins/registry/index.html#containerregcli) 
[2] [Registry Quick Start](https://console.bluemix.net/containers-kubernetes/home/registryGettingStarted)
[3] [プライベートとパブリックのイメージ・レジストリーの使用](https://console.bluemix.net/docs/containers/cs_cluster.html#cs_apps_images)