Kubernetes と OpenShift の違いを確かめてみた

IBM Cloud の Kubernetes サービスに 2019年7月で Red Hat OpenShift が加わった。このOpenShiftは、Red Hat がサブスクリプションでサポートするソフトウェアであり、Kubernetesの本来の機能に加えて、Red Hat の魅力的な機能が、たくさん追加されている。
その一端を知るために、IBM Cloud の[OpenShift チュートリアル](https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_tutorial&locale=ja#openshift_deploy_app)を実行しながら、KubernetesとOpenShiftの違いを確かめたメモである。

本メモは、[OpenShift on IBM Cloud](https://cloud.ibm.com/catalog) をデプロイした後に、`oc login` が成功して、ocコマンドが実行できる状態からの開始を想定している。`oc login` までの手順は、OpenShiftクラスタ管理画面のタブ「アクセス」に従ってセットアップできるので省略する。


## バージョンの表示

最初にOpenShiftのバージョンを確認する。 OpenShiftのバージョンは、v3の最新バージョンである事が読み取れる。

~~~console:
$ oc version
oc v3.11.0+0cbc58b
kubernetes v1.11.0+d4cacc0
features: Basic-Auth

Server https://c100-e.jp-tok.containers.cloud.ibm.com:30617
openshift v3.11.135
kubernetes v1.11.0+d4cacc0
~~~

それから、`oc`コマンドと一緒にkubectlコマンドもインストールされるが、これまでのIKSクラスタを利用してきた関係で、kubectl コマンドもインストールしてありパスが先にある。そこで、kubectlコマンドでもバージョンを確認してみる。kubectlコマンドは、IKSの実行時点のデフォルトバージョン v1.14 であり、OpenShift のマスターノードは、Kubernetes v1.11 である。つまり、kubectlコマンドのバージョンが高い状態である。

~~~console:
$ kubectl version --short
Client Version: v1.14.0
Server Version: v1.11.0+d4cacc0
~~~

## 名前空間のリスト

OpenShiftの特徴として、プロジェクトの名前空間を生成して、その中にアプリケーションをデプロイする。そのため、チュートリアルを先に進める前に先立って名前空間を確認しておく。ここでは、oc コマンドと kubectl コマンドの結果に違いはない。

#### ocコマンドで名前空間をリストした結果

~~~console:
$ oc get ns
NAME                                STATUS    AGE
default                             Active    1d
ibm-cert-store                      Active    1d
ibm-system                          Active    1d
kube-proxy-and-dns                  Active    1d
kube-public                         Active    1d
kube-service-catalog                Active    1d
kube-system                         Active    1d
openshift                           Active    1d
openshift-ansible-service-broker    Active    1d
openshift-console                   Active    1d
openshift-infra                     Active    1d
openshift-monitoring                Active    1d
openshift-node                      Active    1d
openshift-template-service-broker   Active    1d
openshift-web-console               Active    1d
~~~

#### kubectlコマンドで名前空間をリストした結果

~~~console:
$ kubectl get ns
NAME                                STATUS   AGE
default                             Active   1d
ibm-cert-store                      Active   1d
ibm-system                          Active   1d
<以下前述と同じ>
~~~

OpenShift で kubectl コマンドが利用できる理由や、違いについては、OpenShift英語ドキュメントの [Differences Between oc and kubectl](https://docs.openshift.com/container-platform/3.11/cli_reference/differences_oc_kubectl.html) が参考になる。


## プロジェクトの作成

チュートリアルの手順に従って、OpenShiftのプロジェクトを作成する。
[`oc new-project プロジェクト名`](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-project)でプロジェクトを作成すると、プロジェクトの名前空間が作成され、デフォルトのコンテキストがプロジェクトの名前空間に切り替わる。

#### OpenShiftプロジェクトの作成

~~~console:
$ oc new-project hello-world
Now using project "hello-world" on server "https://c100-e.jp-tok.containers.cloud.ibm.com:30617".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app centos/ruby-25-centos7~https://github.com/sclorg/ruby-ex.git

to build a new example application in Ruby.
~~~

#### Kubernetesとしての動きの確認

`oc new-project プロジェクト名`の実行結果をkubectlコマンドで確認すると、プロジェクトの名前空間 hello-worldが作成された事が読み取れる。

~~~console:
$ kubectl get ns
NAME                                STATUS   AGE
default                             Active   1d
hello-world                         Active   1m
<以下省略>
~~~

kubectlコマンドや`oc`コマンドのスコープをプロジェクトの名前空間に移動するために、`kubectl config`によってコンテキストが追加され、コンテキストのカレントが、移動している事が、次のコマンドの実行結果から読み取れる。つまり、これから実行するコマンドに、毎回 `-n 名前空間名`を付与して名前空間を指定しなくても良いことを意味する。 `ocコマンド`は 複数の`kubectlコマンド`を一度に複合して実行する利便性を提供している。

~~~console:
$ kubectl config get-contexts
CURRENT   NAME
          default/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com
*         hello-world/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com
~~~

`oc get projects`で、プロジェクトのリストを表示した。すると、名前空間と同じリストが表示され、OpenShiftではプロジェクトごとに、名前空間を作成する思想が感じられる。

~~~console:
$ oc get projects
NAME                                DISPLAY NAME   STATUS
default                                            Active
hello-world                                        Active
<以下省略>
~~~

これは面白いことに、本来 kubectl v1.14 に`project` のリソースは定義が無いが、`kubectl get project` でも同じ結果を得る事ができた。


## アプリケーションの作成

チュートリアルに従って、[`oc new-app --name プロジェクト名`](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-app)を使ってプロジェクトの名前空間 hello-world にアプリケーションを作成する。

`oc new-app --name hello-world https://github.com/IBM/container-service-getting-started-wt --context-dir="Lab 1"`を実行すると、GitHubのURL `https://github.com/IBM/container-service-getting-started-wt/tree/master/Lab%201`の Dockerfile が読み取られ、リソースが作成される。この中でも、`Dockerfile`が読み取られ、イメージがビルドされる動作が興味深い。

~~~console:
$ oc new-app --name hello-world https://github.com/IBM/container-service-getting-started-wt --context-dir="Lab 1"
--> Found Docker image b5f9499 (19 months old) from Docker Hub for "node:9.4.0-alpine"

    * An image stream tag will be created as "node:9.4.0-alpine" that will track the source image
    * A Docker build using source code from https://github.com/IBM/container-service-getting-started-wt will be created
      * The resulting image will be pushed to image stream tag "hello-world:latest"
      * Every time "node:9.4.0-alpine" changes a new build will be triggered
    * This image will be deployed in deployment config "hello-world"
    * Port 8080/tcp will be load balanced by service "hello-world"
      * Other containers can access this service through the hostname "hello-world"
    * WARNING: Image "node:9.4.0-alpine" runs as the 'root' user which may not be permitted by your cluster administrator

--> Creating resources ...
    imagestream.image.openshift.io "node" created
    imagestream.image.openshift.io "hello-world" created
    buildconfig.build.openshift.io "hello-world" created
    deploymentconfig.apps.openshift.io "hello-world" created
    service "hello-world" created
--> Success
    Build scheduled, use 'oc logs -f bc/hello-world' to track its progress.
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/hello-world' 
    Run 'oc status' to view your app.
~~~

この[GitHubのURL](https://github.com/IBM/container-service-getting-started-wt/tree/master/Lab%201)のREADME.mdには、OpenShiftを使わない場合の、イメージのビルド方法、レジストリの登録方法、アプリケーションのデプロイ方法が解説されているが、OpenShiftでは Dockerfile を手がかりに、ビルド、内部レジストリへの登録、アプリの実行までを実施してくれる。

従来のkubectlコマンドやibmcloudコマンドを組み合わせて、実行することに比べると、`oc new-app`コマンドの生産性は高い。

[チュートリアル](https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_tutorial&locale=ja#openshift_deploy_app)に戻って、`oc status` コマンドで、アプリケーションのステータスを表示すると、デプロイされたアプリケーションのポッド、コントローラー、サービスが表示される。ここで興味深いことに`kubectl status`コマンドは存在しない。 `oc status`は、OpenShift独自に拡張した機能である。

~~~console:
$ oc status
In project hello-world on server https://c100-e.jp-tok.containers.cloud.ibm.com:30617

svc/hello-world - 172.21.202.186:8080
  dc/hello-world deploys istag/hello-world:latest <-
    bc/hello-world docker builds https://github.com/IBM/container-service-getting-started-wt on istag/node:9.4.0-alpine 
    deployment #1 deployed about a minute ago - 1 pod


2 infos identified, use 'oc status --suggest' to see details.
~~~

さらに、`dc/hello-world` の `dc`は、[DeploymentConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-apps.openshift.io/v1.DeploymentConfig.html) の略であり、Kubernetesに存在しない独自のコンローラーである。
次に表示されている `bc/hello-world` の `bc` は、[BuildConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-build.openshift.io/v1.BuildConfig.html) の略であり、同じくKubernetesに存在しない独自のコンローラーである。

しかし、CNCFが配布する v1.14の `kubectl` コマンドでも、次のように表示できる。OpenShiftでは、Kubernetesにリソースやコントローラーなどの機能を追加して、利用者の生産性が高くなるようにKubernetesとしての機能が強化されている事が読み取れる。ここで、ポッドは、Red Hat 独自実装の [DeploymentConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-apps.openshift.io/v1.DeploymentConfig.html) と、Kubernetesでは非推奨の扱いの[ReplicationController](https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/) によって起動された事がわかる。

~~~console:
$ kubectl get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
hello-world   ClusterIP   172.21.202.186   <none>        8080/TCP   4m

$ kubectl get deploymentconfig
NAME          REVISION   DESIRED   CURRENT   TRIGGERED BY
hello-world   1          1         1         config,image(hello-world:latest)

$ kubectl get buildconfig
NAME          TYPE     FROM   LATEST
hello-world   Docker   Git    1

$ kubectl get replicationcontroller
NAME            DESIRED   CURRENT   READY   AGE
hello-world-1   1         1         1       4m

$ kubectl get pods
NAME                  READY   STATUS      RESTARTS   AGE
hello-world-1-5cr9p   1/1     Running     0          7m
hello-world-1-build   0/1     Completed   0          8m
~~~

Kubernetesで推奨されている [`Deployments`](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) と [`ReplicaSet`](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) で起動されていないことを確認するために、次のコマンドで確認した。

~~~console:
$ kubectl get deploy
No resources found.

$ kubectl get rs
No resources found.
~~~

[ReplicationController](https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/)は、[`ReplicaSet`](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/)によって置き換わる事が決まっているが、CNCFは[ReplicationController](https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/)のサポートを打ち切らない事からも、OpenShiftの存在の強さを察する事ができると思う。

## OpenShift のビルドプロセス

OpenShiftの特徴的な動作である[DeploymentConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-apps.openshift.io/v1.DeploymentConfig.html) と[BuildConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-build.openshift.io/v1.BuildConfig.html) の動作について見ていくことにする。

次の結果にように、`oc get all` と `kubectl get all` の両方で、同じ結果を得る事ができる。

#### oc get allの実行結果

~~~console:
$ oc get all
NAME                      READY     STATUS      RESTARTS   AGE
pod/hello-world-1-5cr9p   1/1       Running     0          8m
pod/hello-world-1-build   0/1       Completed   0          9m

NAME                                  DESIRED   CURRENT   READY     AGE
replicationcontroller/hello-world-1   1         1         1         8m

NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/hello-world   ClusterIP   172.21.202.186   <none>        8080/TCP   9m

NAME                                             REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/hello-world   1          1         1         config,image(hello-world:latest)

NAME                                         TYPE      FROM      LATEST
buildconfig.build.openshift.io/hello-world   Docker    Git       1

NAME                                     TYPE      FROM          STATUS     STARTED         DURATION
build.build.openshift.io/hello-world-1   Docker    Git@fb14410   Complete   9 minutes ago   35s

NAME                                         DOCKER REPO                                                TAGS           UPDATED
imagestream.image.openshift.io/hello-world   docker-registry.default.svc:5000/hello-world/hello-world   latest         8 minutes ago
imagestream.image.openshift.io/node          docker-registry.default.svc:5000/hello-world/node          9.4.0-alpine   9 minutes ago
~~~

#### kubectl get allの実行結果

~~~console:
$ kubectl get all
NAME                      READY   STATUS      RESTARTS   AGE
pod/hello-world-1-5cr9p   1/1     Running     0          12m
pod/hello-world-1-build   0/1     Completed   0          13m

NAME                                  DESIRED   CURRENT   READY   AGE
replicationcontroller/hello-world-1   1         1         1       12m

NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/hello-world   ClusterIP   172.21.202.186   <none>        8080/TCP   13m

NAME                                             REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/hello-world   1          1         1         config,image(hello-world:latest)

NAME                                         TYPE     FROM   LATEST
buildconfig.build.openshift.io/hello-world   Docker   Git    1

NAME                                     TYPE     FROM          STATUS     STARTED          DURATION
build.build.openshift.io/hello-world-1   Docker   Git@fb14410   Complete   13 minutes ago   35s

NAME                                         DOCKER REPO                                                TAGS           UPDATED
imagestream.image.openshift.io/hello-world   docker-registry.default.svc:5000/hello-world/hello-world   latest         12 minutes ago
imagestream.image.openshift.io/node          docker-registry.default.svc:5000/hello-world/node          9.4.0-alpine   13 minutes ago
~~~

この両者の結果で、Kubernetesで見る事が無い`pod/hello-world-1-build   0/1     Completed`の理由について、ログから追ってみる。
`oc logs pod/hello-world-1-build`の結果を読んでいくと、コンテナをビルドして、レジストリへ登録するビルドコンテナである事がわかる。これは[oc new-app --name プロジェクト名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-app)によって、起動された[BuildConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-build.openshift.io/v1.BuildConfig.html)のコンテナ・イメージのビルドとレジストリ登録のステップを受け持ったポッドである事がわかる。

~~~console:
$ oc logs pod/hello-world-1-build
Step 1/8 : FROM node@sha256:359a2efa481b9edeff9ca120128f89387ce13dafe30b05f762ec63c7f770e415
 ---> b5f94997f35f
Step 2/8 : COPY app.js .
 ---> 2df08d71e264
Removing intermediate container 2581f82c9f49
Step 3/8 : COPY package.json .
 ---> 31a632dcc681
Removing intermediate container 73ee4b3dd8d1
Step 4/8 : RUN npm install &&    apk update &&    apk upgrade
 ---> Running in f26e410e22c0

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN hello-world-demo@0.0.1 No repository field.
npm WARN hello-world-demo@0.0.1 No license field.
added 50 packages in 1.833s

fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.6/community/x86_64/APKINDEX.tar.gz
v3.6.5-44-gda55e27396 [http://dl-cdn.alpinelinux.org/alpine/v3.6/main]
v3.6.5-34-gf0ba0b43d5 [http://dl-cdn.alpinelinux.org/alpine/v3.6/community]
OK: 8448 distinct packages available
Upgrading critical system libraries and apk-tools:
(1/1) Upgrading apk-tools (2.7.5-r0 -> 2.7.6-r0)
Executing busybox-1.26.2-r9.trigger
Continuing the upgrade transaction with new apk-tools:
(1/7) Upgrading musl (1.1.16-r14 -> 1.1.16-r15)
(2/7) Upgrading busybox (1.26.2-r9 -> 1.26.2-r11)
Executing busybox-1.26.2-r11.post-upgrade
(3/7) Upgrading libressl2.5-libcrypto (2.5.5-r0 -> 2.5.5-r2)
(4/7) Upgrading libressl2.5-libssl (2.5.5-r0 -> 2.5.5-r2)
(5/7) Installing libressl2.5-libtls (2.5.5-r2)
(6/7) Installing ssl_client (1.26.2-r11)
(7/7) Upgrading musl-utils (1.1.16-r14 -> 1.1.16-r15)
Executing busybox-1.26.2-r11.trigger
OK: 5 MiB in 15 packages
 ---> 936e33141d50
Removing intermediate container f26e410e22c0
Step 5/8 : EXPOSE 8080
 ---> Running in b65ba08bc6e9
 ---> 5763ed8d3254
Removing intermediate container b65ba08bc6e9
Step 6/8 : CMD node app.js
 ---> Running in 60c0eff924fb
 ---> 3adc7a7fb8c7
Removing intermediate container 60c0eff924fb
Step 7/8 : ENV "OPENSHIFT_BUILD_NAME" "hello-world-1" "OPENSHIFT_BUILD_NAMESPACE" "hello-world" "OPENSHIFT_BUILD_SOURCE" "https://github.com/IBM/container-service-getting-started-wt" "OPENSHIFT_BUILD_COMMIT" "fb14410f3f05f8b8b1d1ce62b4cd132723a36d33"
 ---> Running in 7d64577fbed0
 ---> c336489ffd70
Removing intermediate container 7d64577fbed0
Step 8/8 : LABEL "io.openshift.build.commit.author" "John Pape \u003cjpapejr@icloud.com\u003e" "io.openshift.build.commit.date" "Mon Aug 19 15:23:21 2019 -0400" "io.openshift.build.commit.id" "fb14410f3f05f8b8b1d1ce62b4cd132723a36d33" "io.openshift.build.commit.message" "Merge pull request #103 from IBM/api-dep" "io.openshift.build.commit.ref" "master" "io.openshift.build.name" "hello-world-1" "io.openshift.build.namespace" "hello-world" "io.openshift.build.source-context-dir" "Lab 1" "io.openshift.build.source-location" "https://github.com/IBM/container-service-getting-started-wt"
 ---> Running in 2a6d51151b0d
 ---> ecdc16510bad
Removing intermediate container 2a6d51151b0d
Successfully built ecdc16510bad

Pushing image docker-registry.default.svc:5000/hello-world/hello-world:latest ...
Pushed 3/6 layers, 55% complete
Pushed 4/6 layers, 100% complete
Pushed 5/6 layers, 100% complete
Pushed 6/6 layers, 100% complete
Push successful
~~~

ここで、ビルドしたコンテナ・イメージは、外部のレジストリサービスではなく、OpenShiftのクラスタ内のレジストリである事が、`oc get imagestream` の実行結果からわかる。

~~~console:
$ oc get imagestreams 
NAME          DOCKER REPO                                                TAGS           UPDATED
hello-world   docker-registry.default.svc:5000/hello-world/hello-world   latest         26 minutes ago
node          docker-registry.default.svc:5000/hello-world/node          9.4.0-alpine   27 minutes ago
~~~


## 外部からアクセス可能なように公開する

[チュートリアル](https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_tutorial&locale=ja#openshift_deploy_app)に戻って、ポッドのアプリケーションをインターネットからアクセスできるように進めていく。

ここまでの操作で、hello-world サービスのタイプは、ClusterIPとなっており、K8sクラスタ外部からアクセスができない状態である。ここから、K8sクラスタ外部からアクセス可能なように設定する。

~~~console:
$ oc get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
hello-world   ClusterIP   172.21.202.186   <none>        8080/TCP   1h

$ kubectl get svc
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
hello-world   ClusterIP   172.21.202.186   <none>        8080/TCP   1h
~~~

CNCF Kubernetes には、routeというリソースは存在しないが、kubectl コマンドでも表示が可能である。

~~~console:
$ oc get route
No resources found.

$ kubectl get route
No resources found.
~~~

`oc create route edge --service=hello-world -n hello-world`コマンドによって、名前空間 hello-world の ClusterIPタイプのサービスへのルートを設定する。

~~~console:
$ oc create route edge --service=hello-world -n hello-world
route.route.openshift.io/hello-world created
~~~

実行結果の確認は、`kubectl get route`でも同じようにできる。公開されたURLが読み取れるので、curlコマンドでアクセスする。

~~~console:
$ oc get route
NAME          HOST/PORT                                                                                                PATH      SERVICES      PORT       TERMINATION   WILDCARD
hello-world   hello-world-hello-world.iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud             hello-world   8080-tcp   edge          None
~~~


curlコマンドのURLには、必ず`https`でなければならない。実行結果から、アプリケーションが応答を返している事がわかる。

~~~console:
$ curl -i https://hello-world-hello-world.iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 79
ETag: W/"4f-FHSLeAA/5K++jT9rAbveOkNLE/Y"
Date: Sat, 31 Aug 2019 01:01:39 GMT
Set-Cookie: b52fcf595fb6234fdbaa488498af05fb=cdf3405b3d976db6c91eb3f729714429; path=/; HttpOnly; Secure
Cache-control: private

Hello world from hello-world-1-5cr9p! Your app is up and running in a cluster!
~~~

## routeがALBであるかの確認

IKS上のアプリケーション公開する場合には、イングレスコントローラーとして、ALB(Application LoadBalancer)または、ただの LBを利用する事が一般的で、HTTPSでのアクセスとなると、ポッドにTLSの設定を入れなければ、ALBが必要となる。そこでこれが、ALBを利用してるかの確認するために、`kubectl get ingress`と`ibmcloud ks cluster-get クラスタ名`実行した結果である。ALBのURLと異なり、イングレス・オブジェクトも存在しない事が読み取れる。

~~~console:
$ kubectl get ingress
No resources found.
~~~

~~~console:
$ bx ks cluster-get iks202
クラスター iks202 を取得中...
OK

名前:                   iks202
ID:                    bljo5g6t0qm7ro9lltk0
状態:                   normal
作成日時:               2019-08-29T07:46:28+0000
ロケーション:            tok05
<中略>
マスター・ロケーション:    Tokyo
マスター状況:            Ready (1 day ago)
マスター状態:            deployed
マスター正常性:           normal
Ingress サブドメイン:    iks202.jp-tok.containers.appdomain.cloud
Ingress の秘密:         iks202
ワーカー:                1
ワーカー・ゾーン:         tok05
バージョン:              3.11.135_1522_openshift
<以下省略>
~~~

## routeの実体確認

routeオブジェクトの実体を確認するために、全名前空間のポッドをリストした。言うまでもない無いが、`kubectl get pod --all-namespaces`でも同じ結果が得られる。ここで、名前空間 default に `reouter-xxxxxxx`から始まるポッドが存在する。この[router](https://docs.openshift.com/container-platform/3.11/install_config/router/index.html)は、OpenShiftの実装であり、連携するロードバランサーに応じたものがある事がわかる。`oc describe pod ルーターのポッド名'とすれば、コンテナ・イメージが読み取れる。

~~~console:
$ oc get pod --all-namespaces
NAMESPACE                           NAME                                                    READY     STATUS
default                             docker-registry-775bc74474-9hnnq                        1/1       Running
default                             docker-registry-775bc74474-nbm45                        1/1       Running
default                             registry-console-7f689b5f9-2mp8c                        1/1       Running
default                             router-76cf9c9df8-jf7n8                                 1/1       Running
default                             router-76cf9c9df8-p6mjl                                 1/1       Running
default                             test-web                                                1/1       Running
hello-world                         hello-world-1-5cr9p                                     1/1       Running
hello-world                         hello-world-1-build                                     0/1       Completed
ibm-system                          ibm-cloud-provider-ip-***-***-**-250-6cbd8bc764-8zw22   0/1       Pending
ibm-system                          ibm-cloud-provider-ip-***-***-**-250-6cbd8bc764-944f8   1/1       Running
ibm-system                          ibm-cloud-provider-ip-***-***-**-251-5697d4d9b7-dbwhm   0/1       Pending
ibm-system                          ibm-cloud-provider-ip-***-***-**-251-5697d4d9b7-ph4gd   1/1       Running
kube-proxy-and-dns                  proxy-and-dns-kgvlk                                     1/1       Running
kube-service-catalog                apiserver-57c8b74849-f8qz9                              1/1       Running
kube-service-catalog                apiserver-57c8b74849-ln7lb                              1/1       Running
kube-service-catalog                controller-manager-559c569db7-pc7dj                     1/1       Running
kube-service-catalog                controller-manager-559c569db7-xzsr9                     1/1       Running
kube-system                         calico-kube-controllers-7c97d855cd-s7vth                1/1       Running
kube-system                         calico-node-6nb52                                       1/1       Running
kube-system                         heapster-664dfd6df6-4r2dr                               2/2       Running
kube-system                         ibm-file-plugin-66d458f9cd-d6vll                        1/1       Running
kube-system                         ibm-keepalived-watcher-qlk5j                            1/1       Running
kube-system                         ibm-kube-fluentd-md5mb                                  1/1       Running
kube-system                         ibm-master-proxy-static-10.193.10.50                    2/2       Running
kube-system                         ibm-storage-watcher-58bc48fd4-89frl                     1/1       Running
kube-system                         ibmcloud-block-storage-driver-mcn67                     1/1       Running
kube-system                         ibmcloud-block-storage-plugin-5548584945-2c94h          1/1       Running
kube-system                         public-crbljo5g6t0qm7ro9lltk0-alb1-7c749bcc9b-8svb4     4/4       Running
kube-system                         public-crbljo5g6t0qm7ro9lltk0-alb1-7c749bcc9b-ftgtw     0/4       Pending
kube-system                         vpn-5684cf4bbd-gx46x                                    1/1       Running
openshift-ansible-service-broker    asb-68d56b9f7b-xpg7g                                    1/1       Running
openshift-console                   console-7d8cfb7b8b-wqx5z                                1/1       Running
openshift-monitoring                alertmanager-main-0                                     3/3       Running
openshift-monitoring                alertmanager-main-1                                     3/3       Running
openshift-monitoring                alertmanager-main-2                                     3/3       Running
openshift-monitoring                cluster-monitoring-operator-57ffbc5599-kbrx7            1/1       Running
openshift-monitoring                grafana-79b7f5d8f8-cz78t                                2/2       Running
openshift-monitoring                kube-state-metrics-68d55f97b7-bthtz                     3/3       Running
openshift-monitoring                node-exporter-tqfjr                                     2/2       Running
openshift-monitoring                prometheus-k8s-0                                        4/4       Running
openshift-monitoring                prometheus-k8s-1                                        4/4       Running
openshift-monitoring                prometheus-operator-68c7f86c8-jpm9h                     1/1       Running
openshift-template-service-broker   apiserver-74f9c4dc7f-2pldw                              1/1       Running
openshift-template-service-broker   apiserver-74f9c4dc7f-87vsv                              1/1       Running
openshift-web-console               webconsole-85897f4875-nrn97                             1/1       Running
~~~

サービスをリストすると、二つのLoadBalancer が立ち上がっている事が読み取れる。そのうちの一方がrouterで、他方がIKSのクラスタが起動する際に確保されるロードバランサーである事が読み取れる。

~~~
$ oc get svc --all-namespaces
NAMESPACE                           NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP
default                             docker-registry                      ClusterIP      172.21.198.116   <none>
default                             kubernetes                           ClusterIP      172.21.0.1       <none>           default                             registry-console                     ClusterIP      172.21.153.110   <none>           default                             router                               LoadBalancer   172.21.210.164   ***.***.**.251   hello-world                         hello-world                          ClusterIP      172.21.202.186   <none>           kube-service-catalog                apiserver                            ClusterIP      172.21.52.167    <none>           kube-service-catalog                controller-manager                   ClusterIP      172.21.31.241    <none>           kube-system                         heapster                             ClusterIP      172.21.10.33     <none>           kube-system                         kube-controllers                     ClusterIP      None             <none>           kube-system                         kubelet                              ClusterIP      None             <none>           kube-system                         public-crbljo5g6t0qm7ro9lltk0-alb1   LoadBalancer   172.21.206.212   ***.***.**.250   openshift-ansible-service-broker    asb                                  ClusterIP      172.21.255.65    <none>           openshift-console                   console                              ClusterIP      172.21.255.119   <none>           openshift-monitoring                alertmanager-main                    ClusterIP      172.21.187.233   <none>           openshift-monitoring                alertmanager-operated                ClusterIP      None             <none>           openshift-monitoring                cluster-monitoring-operator          ClusterIP      None             <none>           openshift-monitoring                grafana                              ClusterIP      172.21.181.164   <none>           openshift-monitoring                kube-state-metrics                   ClusterIP      None             <none>           openshift-monitoring                node-exporter                        ClusterIP      None             <none>           openshift-monitoring                prometheus-k8s                       ClusterIP      172.21.22.58     <none>           openshift-monitoring                prometheus-operated                  ClusterIP      None             <none>           openshift-monitoring                prometheus-operator                  ClusterIP      None             <none>           openshift-template-service-broker   apiserver                            ClusterIP      172.21.247.202   <none>           openshift-web-console               webconsole                           ClusterIP      172.21.133.23    <none>           
~~~

つまり、OpenShift on IBM Cloud　では、デフォルトの公開用アドレス以外に、OpenShift の router に対して、パブリックIPが取得され公開されている事がわかる。

#### IKSでデフォルトの公開用URL

~~~console:
$ nslookup iks202.jp-tok.containers.appdomain.cloud
Server:		192.168.1.1
Address:	192.168.1.1#53

Non-authoritative answer:
Name:	iks202.jp-tok.containers.appdomain.cloud
Address: ***.***.**.250
~~~

#### OpenShift で割り当てられたURL

~~~
$ nslookup hello-world-hello-world.iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud
Server:		192.168.1.1
Address:	192.168.1.1#53

Non-authoritative answer:
hello-world-hello-world.iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud	canonical name = iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud.
Name:	iks202-dd3a245ee37e0f22836f16bc6000a054-0001.jp-tok.containers.appdomain.cloud
Address: ***.***.**.251
~~~

## クリーンナップ

[チュートリアル](https://cloud.ibm.com/docs/openshift?topic=openshift-openshift_tutorial&locale=ja#openshift_deploy_app)に従って、アプリケーションを消去する。

~~~console:
$ oc delete all -l app=hello-world -n hello-world
pod "hello-world-1-5cr9p" deleted
replicationcontroller "hello-world-1" deleted
service "hello-world" deleted
deploymentconfig.apps.openshift.io "hello-world" deleted
buildconfig.build.openshift.io "hello-world" deleted
imagestream.image.openshift.io "hello-world" deleted
imagestream.image.openshift.io "node" deleted
route.route.openshift.io "hello-world" deleted
~~~

アプリケーションは削除されたが、プロジェクトが消えた訳ではいので、プロジェクトまで消したい時は、以下を事項する

~~~console:
$ oc delete project hello-world
project.project.openshift.io "hello-world" deleted
~~~

プロジェクトを消しても、コンテキストは、hello-world のままなので、変更する。

~~~console:
$ kubectl config get-contexts
CURRENT   NAME                                                                             NAMESPACE
          default/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com       default
*         hello-world/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com   hello-world
~~~

コンテキストの変更

~~~console:
$ kubectl config use-context default/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com 
Switched to context "default/c100-e-jp-tok-containers-cloud-ibm-com:30617/IAM#TAKARA@jp.ibm.com".
~~~


# まとめ

Kubernetesの本来の機能に加えて、Red Hat の魅力的なサービスが、沢山追加されている。この検証で解ったことを、以下に列挙する。

* OpenShiftでは、`kubectl`コマンドに代わり`oc`コマンドを利用する。
* CNCFが配布する`kubectl`コマンドを利用しても、OpenShiftの操作ができる。
* `oc`コマンドは、`kubectl`コマンドの複合的な操作を一度に実行できる。[oc new-project プロジェクト名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-project)、[oc new-app --name プロジェクト名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-app)など。
* [oc new-project プロジェクト名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-project)は、プロジェクト用名前空間を作成して、カレント・コンテキストを切り替える。
* [oc new-app --name プロジェクト名](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/3.11/html/cli_reference/cli-reference-basic-cli-operations#new-app)は、指定のDockerfileを読んで、コンテナのビルド、レジストリへの登録、デプロイの一連の作業を実行する。
* `oc create route edge --service=サービス名 -n 名前空間名`は、名前空間 hello-world の ClusterIPタイプのサービスへのルートを設定して、アプリケーションをクラスタ外部からアクセスできるようにする。このコマンドは、OpenShift固有のHA-Proxyを利用して公開されるため、URLが異なる。
* クリーンナップでは、コンテキストスイッチがデフォルトに戻らないため、自分で切り替える必要がある。
* OpenShiftでは、独自実装のコントローラー[DeploymentConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-apps.openshift.io/v1.DeploymentConfig.html) と[BuildConfig](https://docs.openshift.com/container-platform/3.11/rest_api/apis-build.openshift.io/v1.BuildConfig.html) が利用されるが、これらのオブジェクトは`kubectl`でも操作可能である。
* OpenShiftでは[ReplicationController](https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/)を利用して、複数のポッドを起動する。
* もちろん、Kubernetes で推奨される [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) と [ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) の組み合わせでも起動できるが、OpenShift v3では`oc`コマンドからは利用されない。

Kubernetes を知っていれば、OpenShift が如何に、使いやすく強化されている事が理解できる。しかし、最初から OpenShift を利用すると、CNCF のアップストリームの Kubernetes が不便に感じると思う。そもそもコアとなるAPIは同じであるため、OpenShift をより使い熟すためにも、Kubernetes の知識は必須だと思う。