公式チュートリアル Exposing an External IP Address to Access an Application in a Cluster をオンプレで実行 

オンプレミスのKubenetesクラスタで、公式ドキュメントの[Exposing an External IP Address to Access an Application in a Cluster](https://kubernetes.io/docs/tutorials/stateless-application/expose-external-ip-address/)を実行した場合、パブリック・クラウドのKubernetesサービスのように、クラウドのロードバランサーと連動できませんから、どのように動作を確認できるのか見ていきます。

ここで、オンプレミス環境として、著書の[15Stepで習得 Dockerから入るKubernetes コンテナ開発からK8s本番運用まで](https://kubernetes.io/docs/tutorials/stateless-application/expose-external-ip-address/)の学習環境2を使用します。この記事は読者サービスみたいですね（笑）


## K8sクラスタ 1.17の環境設定

K8sクラスタを構築する為に、必要なファイル一式をGitHubからクローンします。前提となるソフトウェアの導入については、本書の付録、または[GitHubのREADME.md](https://github.com/takara9/vagrant-kubernetes)を参照ください。

ここでは、Kubernetesの最新バージョンで動作を試してみます。 `-b 1.17`を省略すると本書と同じバージョンがクローンされます。

~~~
tkr@luigi:~$ git clone -b 1.17 https://github.com/takara9/vagrant-kubernetes k8s-1.17
Cloning into 'k8s-1.17'...
remote: Enumerating objects: 370, done.
remote: Total 370 (delta 0), reused 0 (delta 0), pack-reused 370
Receiving objects: 100% (370/370), 54.34 KiB | 335.00 KiB/s, done.
Resolving deltas: 100% (229/229), done.
~~~

今回では、この作業はオプションで、実施する必要はありませんが、より大きなアプケーションを実行する際に、必要となる場合があります。
クラスタ環境の設定ファイルを変更して、メモリとCPUコア数を変更します。

~~~
tkr@luigi:~$ cd k8s-1.17
tkr@luigi:~/k8s-1.17$ vi Vagrantfile 
~~~

変更箇所は、以下のvbox.cpusのコア数, vbox.memoryのメモリ容量(MB)部分です。メモリやCPUの少ないパソコン環境でも動作するように小さく設定してありますから、大きくしておくと、メモリ不足で動作不良を起こしたり、待ち時間を短くできます。

~~~file:Vagrantfile
    14	    machine.vm.provider "virtualbox" do |vbox|
    15	      vbox.gui = false
    16	      vbox.cpus = 2
    17	      vbox.memory = 4096
    18	    end
<中略>
    42	    machine.vm.provider "virtualbox" do |vbox|
    43	      vbox.gui = false
    44	      vbox.cpus = 2
    45	      vbox.memory = 4096
<中略>
    71	    machine.vm.provider "virtualbox" do |vbox|
    72	      vbox.gui = false
    73	      vbox.cpus = 2
    74	      vbox.memory = 2048
    75	    end
~~~


## K8sクラスタの起動

`vagrant up`を実行することで、パソコン上(Windows10,macOS,LinuxいずれもOK)で、仮想サーバーを３台起動して、k8sクラスタを構築します。

~~~console:
tkr@luigi:~/k8s-1.17$ vagrant up
Bringing machine 'node1' up with 'virtualbox' provider...
Bringing machine 'node2' up with 'virtualbox' provider...
Bringing machine 'master' up with 'virtualbox' provider...
==> node1: Importing base box 'ubuntu/xenial64'...

<中略>

PLAY RECAP *********************************************************************
node1                      : ok=7    changed=3    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
node2                      : ok=7    changed=3    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
~~~

ネットワーク環境とパソコンの性能に依存するので、一概に起動時間を言えませんが、10〜20分で起動すると思います。

起動が完了したら、｀vagrant ssh master`で、マスターノードにログインして、クラスタの起動状態を確認します。この時にユーザーとパスワードは必要ありません。

~~~console:
tkr@luigi:~/k8s-1.17$ vagrant ssh master
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-170-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage


10 packages can be updated.
10 updates are security updates.

New release '18.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
~~~

既にkubectlコマンドが利用できる状態になっているので、ノードのステータスを確認します。全てReadyになっていれば、準備完了です。

~~~console:
vagrant@master:~$ kubectl get node
NAME     STATUS   ROLES    AGE     VERSION
master   Ready    master   2m41s   v1.17.0
node1    Ready    <none>   2m5s    v1.17.0
node2    Ready    <none>   2m5s    v1.17.0
~~~

ついでに、クラスタの情報も確認しておきます。

~~~console:
vagrant@master:~$ kubectl cluster-info
Kubernetes master is running at https://172.16.20.11:6443
KubeDNS is running at https://172.16.20.11:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://172.16.20.11:6443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
~~~

## チュートリアル の作業開始

[Exposing an External IP Address to Access an Application in a Cluster](https://kubernetes.io/docs/tutorials/stateless-application/expose-external-ip-address/)の記載通りに、以下を実行します。

~~~console:
vagrant@master:~$ kubectl apply -f https://k8s.io/examples/service/load-balancer-example.yaml
deployment.apps/hello-world created
~~~

これで、ポッド、すなわち、コンテナのデプロイが始まります。  AVAILABLEの列が０なのは、コンテナが起動していないことを意味しています。

~~~console:
vagrant@master:~$ kubectl get deployment
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   0/5     5            0           38s
~~~

詳細表示は、以下のようになりますが、細かくて解りにくいですよね。

~~~console:
vagrant@master:~$ kubectl describe deployment hello-world
Name:                   hello-world
Namespace:              default
CreationTimestamp:      Sat, 11 Jan 2020 01:45:40 +0000
Labels:                 app.kubernetes.io/name=load-balancer-example
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"load-balancer-example"},"name...
Selector:               app.kubernetes.io/name=load-balancer-example
Replicas:               5 desired | 5 updated | 5 total | 0 available | 5 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app.kubernetes.io/name=load-balancer-example
  Containers:
   hello-world:
    Image:        gcr.io/google-samples/node-hello:1.0
    Port:         8080/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      False   MinimumReplicasUnavailable
  Progressing    True    ReplicaSetUpdated
OldReplicaSets:  <none>
NewReplicaSet:   hello-world-f9b447754 (5/5 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  57s   deployment-controller  Scaled up replica set hello-world-f9b447754 to 5
~~~

そこで、チュートリアルには書かれていませんが、ポッドの状態をリストしてみます。STATUSをみると、コンテナが作成中のポッドがある事がわかります。

~~~
vagrant@master:~$ kubectl get pods
NAME                          READY   STATUS              RESTARTS   AGE
hello-world-f9b447754-6pc2x   0/1     ContainerCreating   0          72s
hello-world-f9b447754-f7mmg   1/1     Running             0          72s
hello-world-f9b447754-jnl5d   1/1     Running             0          72s
hello-world-f9b447754-mndx9   0/1     ContainerCreating   0          72s
hello-world-f9b447754-n7h7k   0/1     ContainerCreating   0          72s
~~~

以下のように、STATUSが Runningになれば、アプリケーションの起動完了です。

~~~
vagrant@master:~$ kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
hello-world-f9b447754-6pc2x   1/1     Running   0          2m38s
hello-world-f9b447754-f7mmg   1/1     Running   0          2m38s
hello-world-f9b447754-jnl5d   1/1     Running   0          2m38s
hello-world-f9b447754-mndx9   1/1     Running   0          2m38s
hello-world-f9b447754-n7h7k   1/1     Running   0          2m38s
~~~

これをデプロイメント・コントローラーで確認します。 AVAILABLE=5となって、ポッド数と同じになっています。

~~~
vagrant@master:~$ kubectl get deploy
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
hello-world   5/5     5            5           2m51s
~~~

普段意識することはありませんが、デプロイメント・コントローラーの下で動作するレプリカセットで確認します。

~~~
vagrant@master:~$ kubectl get replicasets
NAME                    DESIRED   CURRENT   READY   AGE
hello-world-f9b447754   5         5         5       3m22s
~~~


## アプリケーションの外部公開

ここからが、パブリック・クラウドと、パソコン上で動かすオンプレミスの違いになってきます。

チュートリアルどおり、以下のコマンドを実行します。パブリック・クラウドであれば、クラウドのロードバランサーや、外部IPアドレスの取得と連動して、インターネットから参照できるようになります。

~~~
vagrant@master:~$ kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
service/my-service exposed
~~~

以下にサービス my-service の状態を表示したものです。 EXTERNAL-IPの列が **pending** となっていて、外部アドレスが取れないことを意味しています。つまり、クラウド環境ではないオンプレミス環境なので、公開用IPアドレスを獲得するなどの機能が無いためです。

でも、ガッカリすることはありません。ちゃんと、環境に応じた確認が実施できます。

~~~
vagrant@master:~$ kubectl get services my-service
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
my-service   LoadBalancer   10.32.0.155   <pending>     8080:30852/TCP   23s
~~~

現在、ここでは、マスターノードからアクセスしているので、CLUSTER-IPを使って、アプリケーションにアクセスできます。アクセス方法は以下の通りです。
curl http://CLUSTER-IP:PORT/です。 ポード番号は、先頭がクラスタ内部のポート、コロンの後は、ワーカノード上で公開しているポート番号です。

まずは、CLUSTER-IPでアクセスした結果です。

~~~
vagrant@master:~$ curl http://10.32.0.155:8080;echo
Hello Kubernetes!
~~~

## ノード外部からのアクセス

今回の環境は、Linux の Vagrant と VirutalBoxを利用したので仮想マシンのホストに、kubectlコマンドをインストールして、このホストからアクセスします。

~~~
tkr@luigi:~/k8s-1.17$ sudo snap install kubectl --classic
kubectl 1.17.0 from Canonical✓ installed
~~~

環境変数に、コンフィグファイルのパスを設定することで、パソコンのkubectlコマンドから、仮想マシンのK8sクラスタを利用できます。

~~~
tkr@luigi:~/k8s-1.17$ export KUBECONFIG=`pwd`/kubeconfig/config
~~~

ノードをリストして、正常動作を確認しておきます。

~~~
tkr@luigi:~/k8s-1.17$ kubectl get node
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   16m   v1.17.0
node1    Ready    <none>   15m   v1.17.0
node2    Ready    <none>   15m   v1.17.0
~~~

次に、ノードのIPアドレスをリストしておきます。

~~~
tkr@luigi:~/k8s-1.17$ kubectl get node -o wide
NAME     STATUS   ROLES    AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
master   Ready    master   16m   v1.17.0   172.16.20.11   <none>        Ubuntu 16.04.6 LTS   4.4.0-170-generic   containerd://1.2.4
node1    Ready    <none>   16m   v1.17.0   172.16.20.12   <none>        Ubuntu 16.04.6 LTS   4.4.0-170-generic   containerd://1.2.4
node2    Ready    <none>   16m   v1.17.0   172.16.20.13   <none>        Ubuntu 16.04.6 LTS   4.4.0-170-generic   containerd://1.2.4
~~~

ノードのIPアドレスに、ノードで開放しているポート番号を付加して、アクセスすることで、ロードバランサーが無い環境でも、クラウス外部からアクセスできます。

~~~
tkr@luigi:~/k8s-1.17$ curl http://172.16.20.12:30852;echo
Hello Kubernetes!
~~~

既存のロードバランサーと連携する場合は、ノードのIPアドレスとポート番号を利用すれば良い事がわかると思います。ノード上に開くポート番号はAPIで指定できますから、ロードバランサーの設定と別々に作業ができると思います。


## クリーンナップ

これはチュートリアルどおりです。

~~~
tkr@luigi:~/k8s-1.17$ kubectl delete services my-service
service "my-service" deleted
tkr@luigi:~/k8s-1.17$ kubectl delete deployment hello-world
deployment.apps "hello-world" deleted
~~~


## まとめ

パブリッククラウドのように、インターネットのIPアドレス取得や外部ロードバランサーと自動的に連携する機能が無いオンプレミス環境でも、ノードのIPアドレスとポート番号を利用して、外部からアクセスできる事がわかりました。オンプレミスの環境では、外部DNSや外部ロードバランサー設定は手作業となりますが、問題なく利用できる事がわかると思います。

OpenShiftやIBM Cloud Paksをオンプレミス環境に導入する場合も、同様に外部ロードバランサーの考慮が必要となりますので、一つの参考になればと思います。