kubernetes初心者のための入門ハンズオン

この記事は[リクルートライフスタイル Advent Calendar 2016](http://qiita.com/advent-calendar/2016/recruitlifestyle)の10日目の記事です。

# DEPRECATED! [2020/12/05追記]
この記事内のコマンドは現在のバージョンの挙動と一部異なっていたり、説明に不正確な部分があります。
例えば公式のチュートリアルなど、信頼できる情報を参照ください。
https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/



# 2019/05/30追記
下記内容は若干の不正確を含みますので、軽く読み流して雰囲気を掴んでいただいたあとは
https://qiita.com/Kta-M/items/ce475c0063d3d3f36d5d
などご参照いただくとよいかと思います。

# こんばんは
「sshするときの-p 443ってなんの数字ですか？」ぐらいの素人がインフラ周りを担当し8ヶ月、kubernetes始めました。
そんな初心者に送るkubernetes入門記事です。

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

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

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

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

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

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

k8sを使うには、通常はmasterと呼ばれる親機とnodeと呼ばれる子機サーバーを用意して、k8sをそれぞれインストールし、それぞれ通信設定…めんどくさいです。
しかし、 ~~AWSのECSや~~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には触りません。

```shell: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が３つ、といったように、node全体としてpodsを共有している点です。
>
pod1つの中に、任意の数のdocker containerを詰め込むことが可能です。nginxとweb apllicationとredisとか。pod 1 つに対して内部IPが 1 つ割り当てられ、container間で共有できるvolumeも任意の数持てます。

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

```shell: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を確認してみましょう。

```shell: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`にアクセスしてみましょう。
<div align="center">
<img width="448" alt="スクリーンショット 2016-12-08 16.10.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/100059/6257da73-7c81-bbef-4729-e87df3834994.png">
</div>

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


<div align="center">
<img width="348" alt="スクリーンショット 2016-12-08 16.10.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/100059/09c39ee6-4924-9988-79de-bd7548a69f21.png">
</div>


# 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みたいなので気にしなくていのかな…？

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

```yaml: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`を使うと、`containers`の`image`で引っ張ってきたイメージが変わった場合に、ちょっとずつ古いimageのpodから更新してくれます。これで安全にdeployできそう。

```yaml: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>
```

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

```yaml:service.yml
spec:
  selector:
    name: web-server
  type: LoadBalancer
```

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

<div align="center">
<img width="365" alt="スクリーンショット 2016-12-08 16.22.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/100059/e0193f48-d55f-aef9-bf8f-458f1ec7bb50.png">
</div>


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

<div align="center">
<img width="465" alt="スクリーンショット 2016-12-08 16.22.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/100059/28ad9910-0f9d-0eb3-a442-f90f3f0a0dcb.png">
</div>

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

```shell
kubectl delete <type> <name>
```

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


# まとめ
k8sの基本的な使い方について、ざっくり説明してみました。
今回はhttpで行いましたが、httpsも使えます。他にもIngressなど紹介できていない機能がたくさんあります。
省いた説明も大いにあるので、ぜひ[kubernetes本家のチュートリアル](http://kubernetes.io/docs/tutorials/kubernetes-basics/)や[いろいろな入門コンテンツ](http://qiita.com/techeten/items/73d299611c7ed03f5ca8)をご参考ください。

日本語の記事がまだまだ少ないので、何かの参考になれば幸いです。