お題
表題の関係について記載。
環境
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
 〜省略〜
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.7", GitCommit:"0c38c362511b20a098d7cd855f1314dad92c2780", GitTreeState:"clean", BuildDate:"2018-08-20T10:09:03Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.7-gke.6", GitCommit:"9b635efce81582e1da13b35a7aa539c0ccb32987", GitTreeState:"clean", BuildDate:"2018-08-16T21:33:47Z", GoVersion:"go1.9.3b4", Compiler:"gc", Platform:"linux/amd64"}
定義とYAMLサンプルのリンク
Pod
ポッドテンプレートは、レプリケーションコントローラ、ジョブ、およびデーモンセットなどの他のオブジェクトに含まれるポッド仕様
https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates
ReplicaSet
ReplicaSetは、指定された数のポッドレプリカがいつでも実行されていることを保証する仕様
https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#example
Deployments
Deployment Controllerは、PodおよびReplicaSetsの宣言的な更新を提供する仕様
https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#creating-a-deployment
実践
Pod
ミニマムに以下のようなYAMLを作成
★Podを作るだけなら「labels」は不要だけど、後に expose する時にラベルが必要だと怒られたので付加。
apiVersion: v1
kind: Pod
metadata:
  name: kind-pod
  labels:
    app: go
spec:
  containers:
    - name: go-webapi-for-gke-study
      image: sky0621dhub/go-webapi-for-gke-study:v0.1
反映
$ kubectl apply -f kind-pod.yaml 
pod "kind-pod" created
$
$ kubectl get pods -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP            NODE
kind-pod   1/1       Running   0          53s       10.60.12.10   gke-my-cluster-1-min-default-pool-ea807978-8d2t
サービス公開
$ kubectl expose pod kind-pod --type "LoadBalancer" --port 80
service "kind-pod" exposed
$
$ kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kind-pod     LoadBalancer   10.63.252.240   xx.xxx.xx.xxx   80:30598/TCP   5m
EXTERNAL-IP記載のアドレスにWebブラウザでアクセス
「kind: Pod」だけでサービス公開までは楽々いける。
じゃあ、これを複数の「Node」に適用すれば、他の「kind」を使うことはないのかと言うと、当然、そうではない。
「kind: Pod」の場合、その Pod で何かしらの障害が起きて停止ないし機能しない状態になった際に、単純にその Pod が死ぬ。
例えば、以下のように。(しばらく待っても、立ち上がってきたりはしない。)
$ kubectl get pods -o wide
NAME       READY     STATUS    RESTARTS   AGE       IP           NODE
kind-pod   1/1       Running   0          26s       10.60.14.4   gke-my-cluster-1-min-default-pool-ea807978-8d2t
$
$ kubectl delete pods kind-pod
pod "kind-pod" deleted
$
$ kubectl get pods -o wide
No resources found.
3つ作っていても、2つになる。そして何もしなければ以後、元の3つに戻ることはない。
もちろん、Pod が死んでいるのに気づけば手動で再度、Pod を適用すればいいけど、そんなことしてられない。
では、どうするかと言うと、「ReplicaSet」を使う。
ReplicaSet
PodのYAMLを内包したような作りでYAMLを作成
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: kind-rs
  labels:
    app: go
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rsgo
  template:
    metadata:
      labels:
        app: rsgo
    spec:
      containers:
        - name: go-webapi-for-gke-study
          image: sky0621dhub/go-webapi-for-gke-study:v0.1
          ports:
            - containerPort: 80
「spec/template」配下に先ほどの Pod のYAMLに書いた内容(つまり Pod の仕様)が入る。
また、常時3つの Pod が起動していることを保証するために「replicas: 3」を指定。
あと重要なのが2箇所にある「app: rsgo」の記述。
「spec/template」にラベル付けするための「spec/template/metadata/labels/app」と、
そうしてラベル付けされた Pod を管理するための指定である「spec/selector/matchLabels/app」。
https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/#pod-selector
そう、
何気にバージョンは(Podの時と違って)「apps/v1」を指定。
反映
$ kubectl apply -f kind-replicaset.yaml 
replicaset.apps "kind-rs" created
$
$ kubectl get pods -o wide
NAME            READY     STATUS    RESTARTS   AGE       IP           NODE
kind-rs-57nxb   1/1       Running   0          44s       10.60.16.8   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-rs-rt2r2   1/1       Running   0          44s       10.60.15.5   gke-my-cluster-1-min-default-pool-ea807978-ss7p
kind-rs-s5knz   1/1       Running   0          44s       10.60.14.5   gke-my-cluster-1-min-default-pool-ea807978-8d2t
$
$ kubectl get rs -o wide
NAME      DESIRED   CURRENT   READY     AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-rs   3         3         3         58s       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo
3つ Pod が作られてる。そして均等に Node に振り分けられている。
さて、おもむろに Pod を1つ削除してみる。
$ kubectl delete pods kind-rs-57nxb
pod "kind-rs-57nxb" deleted
でも、
$ kubectl get pods -o wide
NAME            READY     STATUS    RESTARTS   AGE       IP           NODE
kind-rs-rt2r2   1/1       Running   0          4m        10.60.15.5   gke-my-cluster-1-min-default-pool-ea807978-ss7p
kind-rs-s5knz   1/1       Running   0          4m        10.60.14.5   gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-rs-zsbfq   1/1       Running   0          6s        10.60.16.9   gke-my-cluster-1-min-default-pool-ea807978-hpqc
すぐに別の Pod(この場合は「kind-rs-zsbfq」)が立ち上がる。
これが、「kind: ReplicaSet」で Pod を作ったときの挙動。指定したレプリカ数を維持してくれる。
サービス公開
$ kubectl expose rs kind-rs --type "LoadBalancer" --port 80
service "kind-rs" exposed
$
$ kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
kind-rs      LoadBalancer   10.63.248.75   xx.xxx.xx.xxx   80:30290/TCP   1m
EXTERNAL-IP記載のアドレスにWebブラウザでアクセス
Pod は3つあるのだけど、どれにアクセスされたのか?
$ kubectl get pods
NAME            READY     STATUS    RESTARTS   AGE
kind-rs-rt2r2   1/1       Running   0          17m
kind-rs-s5knz   1/1       Running   0          17m
kind-rs-zsbfq   1/1       Running   0          13m
まず1つ目。 ↓違った。(Webサーバ起動時のログしか出てないので)
$ kubectl logs kind-rs-rt2r2
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.6
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:80
次は2つ目。 ↓これだった。
$ kubectl logs kind-rs-s5knz
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.6
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:80
{"level":"info","ts":1537972593.5397136,"caller":"go-webapi-for-gke-study/main.go:34","msg":"INFO LEVEL with severity","severity":"INFO"}
{"level":"warn","ts":1537972593.539827,"caller":"go-webapi-for-gke-study/main.go:35","msg":"WARN LEVEL with severity","severity":"WARN"}
{"level":"error","ts":1537972593.539841,"caller":"go-webapi-for-gke-study/main.go:36","msg":"ERROR LEVEL with severity","severity":"ERROR","stacktrace":"main.main.func1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/main.go:36\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo.(*Echo).Add.func1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/echo.go:480\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware.RequestIDWithConfig.func1.1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware/request_id.go:57\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware.RecoverWithConfig.func1.1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware/recover.go:78\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo.(*Echo).ServeHTTP\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/echo.go:583\nnet/http.serverHandler.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2619\nnet/http.(*conn).serve\n\t/usr/local/go/src/net/http/server.go:1801"}
{"level":"info","ts":1537972670.3378396,"caller":"go-webapi-for-gke-study/main.go:34","msg":"INFO LEVEL with severity","severity":"INFO"}
{"level":"warn","ts":1537972670.3378987,"caller":"go-webapi-for-gke-study/main.go:35","msg":"WARN LEVEL with severity","severity":"WARN"}
{"level":"error","ts":1537972670.3379054,"caller":"go-webapi-for-gke-study/main.go:36","msg":"ERROR LEVEL with severity","severity":"ERROR","stacktrace":"main.main.func1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/main.go:36\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo.(*Echo).Add.func1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/echo.go:480\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware.RequestIDWithConfig.func1.1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware/request_id.go:57\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware.RecoverWithConfig.func1.1\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/middleware/recover.go:78\ngithub.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo.(*Echo).ServeHTTP\n\t/go/src/github.com/sky0621/go-webapi-for-gke-study/vendor/github.com/labstack/echo/echo.go:583\nnet/http.serverHandler.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2619\nnet/http.(*conn).serve\n\t/usr/local/go/src/net/http/server.go:1801"}
最後に3つ目。 ↓当然、違う。
$ kubectl logs kind-rs-zsbfq
   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.6
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:80
一応、何度かWebアクセスしてみると、他の Pod にもアクセスログが出力されていた。分散されている。
ただ、アクセスには、けっこうな偏りがあったので、ラウンドロビンというわけではなさそう。
決めた分だけ Pod の起動を維持してくれるし、これで事足りる。
じゃあ、ReplicaSetで十分だから「Deployments」は使うことないのかというと、これは新しいバージョンのアプリをデプロイする時に役に立つ。
”ローリングアップデート”というのができる。
もともと3つのPodで動いていた現行Verのアプリに対し、同じく3つのPodで動く新Verのアプリで入れ替えようとした時に、
新Verアプリ用のPodを増やしつつ、現行Verアプリ用のPodを減らしていき、最終的に入れ替える。
そして、これがk8sで推奨されているパターンらしい。
Deployment
ReplicaSetのYAMLとまったく同じ形でYAMLを作成
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kind-deployment
  labels:
    app: go
spec:
  replicas: 3
  selector:
    matchLabels:
      app: rsgo
  template:
    metadata:
      labels:
        app: rsgo
    spec:
      containers:
        - name: go-webapi-for-gke-study
          image: sky0621dhub/go-webapi-for-gke-study:v0.1
          ports:
            - containerPort: 80
反映
$ kubectl apply -f kind-deployment.yaml 
deployment.apps "kind-deployment" created
$
$ kubectl get pods -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
kind-deployment-6658cdbcd-db7lg   1/1       Running   0          28s       10.60.16.10   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-lkdfw   1/1       Running   0          28s       10.60.14.6    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-z4prb   1/1       Running   0          28s       10.60.15.6    gke-my-cluster-1-min-default-pool-ea807978-ss7p
$ 
$ kubectl get rs -o wide
NAME                        DESIRED   CURRENT   READY     AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment-6658cdbcd   3         3         3         46s       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo,pod-template-hash=221478678
$
$ kubectl get deployments -o wide
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment   3         3         3            3           8m        go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo
Podを1つ削除
$ kubectl delete pods kind-deployment-6658cdbcd-db7lg
pod "kind-deployment-6658cdbcd-db7lg" deleted
$ kubectl get pods -o wide
NAME                              READY     STATUS        RESTARTS   AGE       IP            NODE
kind-deployment-6658cdbcd-db7lg   0/1       Terminating   0          2m        10.60.16.10   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-lkdfw   1/1       Running       0          2m        10.60.14.6    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-mjzvb   1/1       Running       0          3s        10.60.16.11   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-z4prb   1/1       Running       0          2m        10.60.15.6    gke-my-cluster-1-min-default-pool-ea807978-ss7p
素早く1つ新しいPod(kind-deployment-6658cdbcd-mjzvb)を立ち上げ、
削除対象のPod(kind-deployment-6658cdbcd-db7lg)は削除中(Terminating)のステータスになった。
$ kubectl get pods -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
kind-deployment-6658cdbcd-lkdfw   1/1       Running   0          4m        10.60.14.6    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-mjzvb   1/1       Running   0          2m        10.60.16.11   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-z4prb   1/1       Running   0          4m        10.60.15.6    gke-my-cluster-1-min-default-pool-ea807978-ss7p
入れ替え完了。
Ver違いのイメージをPod数4でデプロイするようYAMLを書き換え
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kind-deployment
  labels:
    app: go
spec:
  replicas: 4
  selector:
    matchLabels:
      app: rsgo
  template:
    metadata:
      labels:
        app: rsgo
    spec:
      containers:
        - name: go-webapi-for-gke-study
          image: sky0621dhub/go-webapi-for-gke-study:v0.2
          ports:
            - containerPort: 80
反映
◆反映前のPodの状態◆
$ kubectl get pods -o wide
NAME                              READY     STATUS    RESTARTS   AGE       IP            NODE
kind-deployment-6658cdbcd-lkdfw   1/1       Running   0          9m        10.60.14.6    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-mjzvb   1/1       Running   0          7m        10.60.16.11   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-z4prb   1/1       Running   0          9m        10.60.15.6    gke-my-cluster-1-min-default-pool-ea807978-ss7p
$
$ kubectl get rs -o wide
NAME                        DESIRED   CURRENT   READY     AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment-6658cdbcd   3         3         3         9m        go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo,pod-template-hash=221478678
$
$ kubectl get deployments -o wide
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment   3         3         3            3           9m        go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo
◆では、反映◆
$ kubectl apply -f kind-deployment.yaml 
deployment.apps "kind-deployment" configured
◆入れ替えが始まる◆
$ kubectl get pods -o wide
NAME                               READY     STATUS              RESTARTS   AGE       IP            NODE
kind-deployment-6658cdbcd-dd5lq    0/1       Terminating         0          4s        <none>        gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-lkdfw    1/1       Running             0          10m       10.60.14.6    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-6658cdbcd-mjzvb    1/1       Running             0          8m        10.60.16.11   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-6658cdbcd-z4prb    1/1       Running             0          10m       10.60.15.6    gke-my-cluster-1-min-default-pool-ea807978-ss7p
kind-deployment-8646897b59-fzlng   0/1       ContainerCreating   0          4s        <none>        gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-8646897b59-q9qd4   0/1       ContainerCreating   0          4s        <none>        gke-my-cluster-1-min-default-pool-ea807978-ss7p
$
$ kubectl get rs -o wide
NAME                         DESIRED   CURRENT   READY     AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment-6658cdbcd    1         1         1         10m       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo,pod-template-hash=221478678
kind-deployment-8646897b59   4         4         2         9s        go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.2   app=rsgo,pod-template-hash=4202453615
$
$ kubectl get deployments -o wide
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment   4         4         4            4           10m       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.2   app=rsgo
◆入れ替えが終わる◆
$ kubectl get pods -o wide
NAME                               READY     STATUS    RESTARTS   AGE       IP            NODE
kind-deployment-8646897b59-8whlg   1/1       Running   0          26s       10.60.15.8    gke-my-cluster-1-min-default-pool-ea807978-ss7p
kind-deployment-8646897b59-dt6hz   1/1       Running   0          28s       10.60.14.7    gke-my-cluster-1-min-default-pool-ea807978-8d2t
kind-deployment-8646897b59-fzlng   1/1       Running   0          34s       10.60.16.12   gke-my-cluster-1-min-default-pool-ea807978-hpqc
kind-deployment-8646897b59-q9qd4   1/1       Running   0          34s       10.60.15.7    gke-my-cluster-1-min-default-pool-ea807978-ss7p
$
$ kubectl get rs -o wide
NAME                         DESIRED   CURRENT   READY     AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment-6658cdbcd    0         0         0         10m       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.1   app=rsgo,pod-template-hash=221478678
kind-deployment-8646897b59   4         4         4         47s       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.2   app=rsgo,pod-template-hash=4202453615
$
$ kubectl get deployments -o wide
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS                IMAGES                                     SELECTOR
kind-deployment   4         4         4            4           17m       go-webapi-for-gke-study   sky0621dhub/go-webapi-for-gke-study:v0.2   app=rsgo
入れ替えが終わった後、既存のReplicaSetの定義自体は消えないみたい。ただし、Pod数は0

