公式チュートリアルで Kubenetes 入門(前編)

 今まではなんとなくの知識だけで、自分で触ってみたことはなかったので、今更ですが公式チュートリアルで Kubernetes に入門してみました。

Tutorials - Kubernetes

 チュートリアルの中にも色々あるのですが、今回は一番基本的な Kubernetes Basics をやってみます。

kubernetes.io

 チュートリアルはブラウザ上で仮想ターミナルからコマンドを実行できる Katacoda の環境の上で提供されています。

www.katacoda.com

 また、 Katacoda の上で Minikube という簡単にローカルに Kubernetes 環境を構築するツールが使われています。

github.com

 Kubernetes Basics は6つの Modules に分かれているので、一つずつ進めてみます。分量が多くなってしまったので、今回は Module 1 から 3 までを進めます。

Kubernetes Clusters(Module 1)

 まずは Kubernetes Clusters についてです。 Kubernetes は可用性の高いクラスタ環境を管理し、アプリケーションコンテナの管理を自動化します。

 クラスタの中には Master と Node が存在し、 Master がクラスタを管理します。 Node は実際にサービスを提供するVMもしくは物理コンピュータになります。

 また、 Node が Master と連携するために Kubelet というエージェントが存在します。

 さらに Node はコンテナのオペレーションをハンドリングするために、 Docker または rkt を使用します。

 Kubenetes Cluster ではトラフィックを処理するためには最低3つの Node を保持する必要があります。

 では実際にチュートリアルを進めてみます。チュートリアルは Katacoda 環境でコマンドを実行しながら進めることができます。

f:id:akanuma-hiroaki:20181229171152p:plain

 まずは Minikube のバージョンを確認します。

$ minikube version
minikube version: v0.28.2

 そして Minikube を実行します。

$ minikube start
Starting local Kubernetes v1.10.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.

 Minikube によって Kubernetes 環境が構築されました。

 Kubernetes の操作には CLI の kubectl を使用します。まずはバージョンを確認してみます。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.0", GitCommit:"91e7b4fd31fcd3d5f436da26c980becec37ceefe", GitTreeState:"clean", BuildDate:"2018-06-27T20:17:28Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.0", GitCommit:"fc32d2f3698e36b93322a3465f63a14e9f0eaead", GitTreeState:"clean", BuildDate:"2018-04-10T12:46:31Z", GoVersion:"go1.9.4", Compiler:"gc", Platform:"linux/amd64"}

 Client Version は kubectl のバージョンで、 ServerVersion は Master にインストールされている Kubernetes のバージョンになります。

 次にクラスタの情報を確認してみます。

$ kubectl cluster-info
Kubernetes master is running at https://172.17.0.48:8443

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

 チュートリアル中は CLI のみで進めますが、表示された URL にアクセスすると、クラスタの情報が確認できるダッシュボードを表示することができます。

 続いて Node を確認してみます。

$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     <none>    7m        v1.10.0

 現在は Node は一つだけで、 STATUS が Ready になっていて、アプリケーションのデプロイが可能な状態になっています。

Kubernetes Deployments(Module 2)

 Kubernetes Cluster を稼働させていれば、その上にコンテナ化されたアプリケーションをデプロイすることができます。そのためには Kubernetes Deployment の設定を作成します。

 もし Node がダウンしたり削除されたりすると、 Deployment コントローラはそれをリプレイスします。これによってメンテナンス時や障害時の自律回復機構を提供します。

 Deployment の管理は kubectl で行うことができます。 Deployment を作成するときは、アプリケーションのコンテナイメージと、稼働させるレプリカの数を指定します。このチュートリアルでは Docker コンテナにパッケージされた Node.js アプリケーションが使われます。

 チュートリアルを開始すると下記のように自動的にシェルが実行され、クラスタが起動します。

$ sleep 1; launch.sh
Starting Kubernetes...
Kubernetes Started

 Node を確認すると下記のように一つだけ稼働していることがわかります。

$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     <none>    2m        v1.10.0

 新しい Deployment を作成するには、 kubectl run コマンドを使用します。実行時には Deployment名とコンテナイメージのロケーションを指定する必要があります。また、特定のポートでアプリを実行するためには --port オプションを使用します。

$ kubectl run kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1 --port=8080
deployment.apps/kubernetes-bootcamp created

 Deployment を確認してみます。

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1         1         1            1           30m

 Deployment が一つあり、一つのインスタンスで稼働していることがわかります。このインスタンスは Node の Docker コンテナ上で稼働しています。

 Kubernetes 内で稼働している Pod はプライベートで独立したネットワーク内で稼働しています。デフォルトでは同じクラスタ内からのみアクセス可能です。 kubectl 使用時には API エンドポイントを通して連携が行われています。また、 kubectl コマンドでクラスタ内のプライベートネットワーク内でのアクセスを転送するプロキシを作成することができます。作業用のターミナルとは別ターミナルで下記のコマンドを実行します。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

 これでオンラインターミナルが稼働しているホストと Kubernetes Cluster 間でのコネクションが作成されました。プロキシによって API にターミナルからダイレクトにアクセスできるようになっています。

$ curl http://localhost:8001/version
{
  "major": "1",
  "minor": "10",
  "gitVersion": "v1.10.0",
  "gitCommit": "fc32d2f3698e36b93322a3465f63a14e9f0eaead",
  "gitTreeState": "clean",
  "buildDate": "2018-04-10T12:46:31Z",
  "goVersion": "go1.9.4",
  "compiler": "gc",
  "platform": "linux/amd64"
}

 API サーバは自動的に Pod名をベースにしたエンドポイントを作成します。プロキシを通してそのエンドポイントにもアクセスすることができます。そのためには Pod名が必要なので、 Pod名を取得して環境変数に保持します。

$ export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-5c69669756-288s7

 この Pod名を使ってエンドポイントにアクセスしてみます。 Pod の API の URL は下記のようになります。

$ curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-288s7 | v=1

Kubernetes Pods And Nodes(Module 3)

 Pod は一つまたはそれ以上のアプリケーションコンテナ(Docker または rkt)を表していて、いくつかのリソースをコンテナ間で共有します。リソースには下記のようなものが含まれます。

  • 共有ストレージ

  • ネットワーク(ユニークなクラスタIP)

  • コンテナイメージのバージョンや使用するポートなどの情報

 Pod 内のコンテナは IP アドレスとポートの空間を共有します。

 Pod は Kubernetes プラットフォームでの最小ユニットになります。

 また、 Pod は常に Node 上で稼働します。

 全ての Node では少なくとも Kubelet(Pod とコンテナを管理し、Master と Node の連携を行う)と、コンテナのランタイム(Docker や rkt など)が稼働します。

 現在の Pod を確認するには kubectl の get コマンドを使用します。

$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
kubernetes-bootcamp-5c69669756-sv4wc   1/1       Running   0          1m

 Pod 内のコンテナや、そのコンテナで使われているイメージを確認するには、 kubectl describe pods コマンドを使用します。このコマンドによって Pod の IP アドレスやポート、ライフサイクルイベントなども確認することができます。

$ kubectl describe pods
Name:           kubernetes-bootcamp-5c69669756-sv4wc
Namespace:      default
Node:           minikube/172.17.0.4
Start Time:     Sat, 29 Dec 2018 10:40:18 +0000
Labels:         pod-template-hash=1725225312
                run=kubernetes-bootcamp
Annotations:    <none>
Status:         Running
IP:             172.18.0.2
Controlled By:  ReplicaSet/kubernetes-bootcamp-5c69669756
Containers:
  kubernetes-bootcamp:
    Container ID:   docker://051074e6dc2c7b6fb75c92ace46fad77838d4392b7270a7b2fa9dad372a9086d
    Image:          gcr.io/google-samples/kubernetes-bootcamp:v1
    Image ID:       docker-pullable://gcr.io/google-samples/kubernetes-bootcamp@sha256:0d6b8ee63bb57c5f5b6156f446b3bc3b3c143d233037f3a2f00e279c8fcc64af
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 29 Dec 2018 10:40:18 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-l6t4t (ro)
Conditions:
  Type           Status
  Initialized    True
  Ready          True
  PodScheduled   True
Volumes:
  default-token-l6t4t:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-l6t4t
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason                 Age              From               Message
  ----     ------                 ----             ----               -------
  Warning  FailedScheduling       4m (x4 over 4m)  default-scheduler  0/1 nodes are available: 1 node(s) were not ready.
  Normal   Scheduled              4m               default-scheduler  Successfully assigned kubernetes-bootcamp-5c69669756-sv4wc to minikube
  Normal   SuccessfulMountVolume  4m               kubelet, minikube  MountVolume.SetUpsucceeded for volume "default-token-l6t4t"
  Normal   Pulled                 4m               kubelet, minikube  Container image "gcr.io/google-samples/kubernetes-bootcamp:v1" already present on machine
  Normal   Created                4m               kubelet, minikube  Created container
  Normal   Started                4m               kubelet, minikube  Started container

 プロキシ経由で Pod 関連の操作を行うために再度別ターミナルでプロキシを実行します。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

 Pod 名を環境変数に格納します。

$ export POD_NAME=$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')
$ echo Name of the Pod: $POD_NAME
Name of the Pod: kubernetes-bootcamp-5c69669756-sv4wc

 curl コマンドでアプリケーションのアウトプットを確認します。

$ curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-sv4wc | v=1

 アプリケーションが標準出力に送ったものは Pod 内のコンテナのログになります。そのログは kubectl logs コマンドで確認することができます。 Pod 内にコンテナが一つの場合はコンテナ名を指定する必要はありません。

$ kubectl logs $POD_NAME
Kubernetes Bootcamp App Started At: 2018-12-29T10:40:18.825Z | Running On:  kubernetes-bootcamp-5c69669756-sv4wc

Running On: kubernetes-bootcamp-5c69669756-sv4wc | Total Requests: 1 | App Uptime: 725.334 seconds | Log Time: 2018-12-29T10:52:24.159Z

 Pod が稼働していれば、コンテナ上で直接コマンドを実行することができます。そのためには exec コマンドと Pod 名を使用します。環境変数のリストを表示する例は下記の通りです。

$ kubectl exec $POD_NAME env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubernetes-bootcamp-5c69669756-sv4wc
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT=tcp://10.96.0.1:443
NPM_CONFIG_LOGLEVEL=info
NODE_VERSION=6.3.1
HOME=/root

 bash のセッションを開始することもできます。

$ kubectl exec -ti $POD_NAME bash
root@kubernetes-bootcamp-5c69669756-sv4wc:/#

 セッション開始後はそのままコマンドが実行できます。

root@kubernetes-bootcamp-5c69669756-sv4wc:/# ls
bin   core  etc   lib    media  opt   root  sbin       srv  tmp  var
boot  dev   home  lib64  mnt    proc  run   server.js  sys  usr

 NodeJS コンテナ内からの実行になるので、 curl も localhost 指定で実行可能です。

root@kubernetes-bootcamp-5c69669756-sv4wc:/# curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5c69669756-sv4wc | v=1

まとめ

 仮想環境が提供されているので、すぐにチュートリアルが始められるのはハードルが低くて良いですね。今回は前半まででしたが、クラスタ環境の構成まではイメージすることができました。後編ではスケーリングやローリングアップデートも扱われているので、サクッとやってみたいと思います。