k8sベーシック

kubernetes?

一般のwebサイトはサーバーの上で動いているわけですが、このサーバーが1台だとこんな問題が起きます。

  • キャンペーンをやったら急にアクセスが集中して負荷に耐え切れず、webサイト全体がダウンしてしまった
  • サーバーのライブラリをアップデートしたら変なエラーが出るようになってしまった

ああ大変だ。こうなっては困るので、サーバーを複数台にする対策があります。冗長化と呼ばれます。
ラウンドロビンの話は飛ばしますが、最近だとAWSやGCPでロードバランサーと呼ばれるアクセス分配器的なものを用意して、いくつかのサーバーにアクセスをバラすのがナウいです。
クラウドサービスを使っていれば、オートスケーリングと呼ばれる機能で、サーバーの台数を自動で増減することができます。
しかしこの冗長化構成にも新たな要求が出てきます。

  • サーバーの台数を少なめにしておくと、急にアクセスが来た時にサーバー台数を増やすのが間に合わず、結局サービスが落ちてしまう
  • かといって多く用意しておくと、アクセスが少ない時にお金がもったいない
  • Immutable Infrastructureとはいうものの、デプロイの度にサーバー入れ替えるの時間かかる

ああ困った。そこにkubernetes(以下k8s)が現れます。 k8sはこれらの問題に対して以下のようなメリットを与えます。

  • サーバー台数と関係なく、複数のapplicationを起動できる(コンピュータ資源の最適化ができる)
  • 負荷に応じてapplicationごとに自動で数を増減できる
  • サーバーを入れ替えることなくapplicationのデプロイができる。軽いものならば数秒程度

k8sを使うには、通常はmasterと呼ばれる親機とnodeと呼ばれる子機サーバーを用意して、k8sをそれぞれインストールし、それぞれ通信設定…めんどくさいです。
しかし、 GCPのGKEのようなk8sのマネージドサービス上で展開すれば、masterとnodeは数分で勝手に準備され、不調になったnodeは自動で入れ替わる、さらにnodeの数も増減できる、などいいことだらけ。

ということで長くなりましたが、ここからGKE上で実際にハンズオンをしていきましょう。

前準備

まずGCPのセットアップです。無料使用期間あるので、入門記事など参考に登録しましょう。gcloudとかkubectlとか含めてセットアップが終わったら次のコマンドを叩きます。

gcloud container clusters create k0 

コーヒーを淹れるくらいの時間で、上述のmasterとnode(defaultだと3台)が立ち上がります。nodeとはGCPでのVM、AWSでのinstanceに相当します。GKE上では基本的にmasterには触りません。result

Creating cluster k0...done.
Created [https://container.googleapis.com/v1/projects/user/zones/asia-east1-a/clusters/k0].
kubeconfig entry generated for k0.
NAME  ZONE          MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
k0    asia-east1-a  1.4.6           104.199.249.239  n1-standard-1  1.4.6         3          RUNNING

1.イントロダクション

細かいことは飛ばして、とりあえず動かしましょう。

kubectl run nginx --image=nginx:1.11.3

何が起きたかというと、さっき立ち上げたnode内で、podが動き始めました。

podとは?

nodeの中で動いているdocker containerの集まり、みたいなイメージです。k8sでは、nodeの集合(nodes)のパワー全体をリソースとして、podがいくつも立ち上がります。
面白いのは、あるnodeにはpodAとpodBが1つずつ、あるnodeにはpodBが3つ、といったように、node全体としてpodsを共有している点です。

pod1つの中に、任意の数のdocker containerを詰め込むことが可能です。nginxとweb apllicationとredisとか。pod 1 つに対して内部IPが 1 つ割り当てられ、container間で共有できるvolumeも任意の数持てます。

引数で指定したのは、Docker Hub 公式のnginx containerです。つまり上のコマンドは「nginx imageを使ってpodを作れ」という意味になります。
ではnginxのpodがどうなってるか確認しましょう。result

[user:~]$ kubectl get pods
NAME                    READY     STATUS    RESTARTS   AGE
nginx-527997929-1m6nj   1/1       Running   0          22s

kubectl get <name>nameの概要を見れます。無事作成できました。

でもこれだと、nodesの中でcontainerが動いてるだけなので外部からアクセスできません。外部アクセスさせるために次のコマンドを打ちます。

kubectl expose deployment nginx --port 80 --type LoadBalancer

これで、serviceとロードバランサーが作られました。

serviceとは?

pods(podの集合)に対して外部と通信を行うための通り道みたいなものです。serviceひとつにpodsがぶらさがって、serviceに来たアクセスをどれかのpodにいい感じで分配します(内部に賢いroutingがいます)。

今回はオプションに LoadBalancer を指定したので、GCP上に外部IPを持ったロードバランサーが作成され、その外部IPに対してのアクセスがserviceに来るようになります。

これで、さっき作成したnginx入りのpodが外部IPと紐付きました。serviceを確認してみましょう。result

[user:~]$ kubectl get services
NAME         CLUSTER-IP      EXTERNAL-IP       PORT(S)   AGE
kubernetes   10.23.240.1     <none>            443/TCP   6m
nginx        10.23.248.197   104.198.112.131   80/TCP    1m

今回はnginxという名前でserviceが作成されました。一緒にでてくるkubernetesはmasterからnodeに通信する用のserviceです。
作成されたロードバランサーの外部IP 104.198.112.131にアクセスしてみましょう。

スクリーンショット 2016-12-08 16.10.10.png

表示されたらOKです。ちなみに今まで作成していたものの全体像はこんな感じです。

スクリーンショット 2016-12-08 16.10.10.png

2.カスタマイズしたpodとserviceを作る

さっきはdefaultのnginx imageを使いましたが、今度はカスタマイズして、nginx+webアプリの構成でpodを作ってみましょう。
サンプルを用意したので、利用するコードをgit cloneしてください。

git clone https://github.com/mihirat/advent.git
cd /path/to/advent/kubernetes
cd k8s

ここにあるdeployment.ymlがpodの設定、service.ymlがserviceの設定になります。
ちなみにpath/to/advent/kubernetes/docker というディレクトリに今回のdocker imageの設定があります、ご参考まで。本ハンズオンではDockerHubにあるものを使ってください。

deploymentとは?

podを管理するコントローラーであるReplication Controllerとpodの両方を一括で管理できるものです。k8sはアップデートが早いので、1年前の記事とかだとReplication Controllerとpodを別で作成していることが多いのですが、絶対にdeploymentを使った方が楽です。
お気づきかもしれませんが、イントロダクションの段階からずっとdeploymentを使っていました。
実はbetaですが、もう1年近くbetaみたいなので気にしなくていのかな…?

設定の中身を見ていきましょう。deployment.yml

etc...
  strategy:
    type: RollingUpdate
...
      containers:
      - name: go-server
        image: mihirat/go-server
...
        readinessProbe:
          httpGet:
            # Path to probe; should be cheap, but representative of typical behavior
            path: /readiness.html
etc...

↑部分のRollingUpdateを使うと、containersimageで引っ張ってきたイメージが変わった場合に、ちょっとずつ古いimageのpodから更新してくれます。これで安全にdeployできそう。service.yml

spec:
  selector:
    name: web-server

ここで対象podのlabelを指定しており、podのlabelの名前を対応させることで、serviceとpodが対応付けられます。

readinessProbeとは?死活監視の仕組みをざっと説明します。

死活監視の仕組み

podが生きてるかどうかを、いわゆるロードバランサーの仕組みと同じように内部で監視しています。
各nodeでkubeletという監視等するプロセスが走っており、「調子はどうだい?」とpodに定期的に呼びかけます。死んでた場合にはdeployment(replication controller)により入れ替え・増減されます。

サンプルでは、nginxの設定内部で /readiness.html にアクセスが来たら200を返すようにしてあるので、nginx自体が通常通り稼働していれば、状態がreadyとしてkubeletに認知されます。
これと別にlivenessProbeもありますが今は略。

3.実際に動かしてみる

これらを以下のコマンドで作成しましょう。

kubectl apply -f k8s/deployment.yml
kubectl apply -f k8s/service.yml

これで、deploymentと同時にpodが生成され、serviceが生成されます。作成したもろもろを細かく確認するコマンドとして

kubectl describe {pods|services|deployments} <name>

describegetより細かく確認するコマンドです。うまく作成できてるかserviceにアクセスし…おや…どこにアクセスすればいいのか?service.yml

spec:
  selector:
    name: web-server
  type: LoadBalancer

イントロダクションではserviceをexposeしてロードバランサーと紐付けましたが、今回はserviceを作成したときにロードバランサーを同時に作っていたのでした。
さっきのようにserviceの外部IPを確認して、アクセスしてみましょう。

スクリーンショット 2016-12-08 16.22.11.png

無事にnginx経由でwebサーバーにアクセスできました!最終的に作成されたものはこんなイメージです。

スクリーンショット 2016-12-08 16.22.11.png

作成したものを消したいときには

kubectl delete <type> <name>

です。例えばkubectl delete pods --allとかやるとpodが全て消えます。deploymentが存在する限り、そのdeploymentが管理するpodは無限に蘇るので、二度とpodが立ち上がらないようにするときはkubectl delete deployments web-serverなどと打ちます。