Mac で minikube を試す (3) ReplicaSet と Job

[入門Kubernetes](https://www.oreilly.co.jp/books/9784873118406/) の [メモ1](https://qiita.com/sunadorinekop/items/a317b4095bfcf2449b92) と [メモ2](https://qiita.com/sunadorinekop/items/0a8f6aee2792fc83022b) の続きです。

## ReplicaSet

### Replica 作成

下記の YAML を準備します。

~~~kuard-rs2.yaml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: kuard
spec:
  replicas: 2 // レプリカを 2 に設定してみる。
  template:
    metadata:
      labels:
        app: kuard
        version: "2"
    spec:
      containers:
        - name: kuard
          image: "gcr.io/kuar-demo/kuard-amd64:2"
~~~

apply して get pods すると Pod が 2 つ立ち上がっています。手軽ですね！

~~~sh
$ kubectl apply -f kuard-rs.yaml
$ kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
kuard-d6qs6   1/1     Running   0          3s
kuard-j9xd6   1/1     Running   0          3s
~~~

### Pod がどの ReplicaSet に管理されているか

metadata.ownerReferences に入っている情報を参照すると良いようです。

~~~sh
$ kubectl get pods kuard-d6qs6 -o yaml | grep -A6 ownerReferences
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: kuard
    uid: 9453bcf2-9abb-11e9-9c88-0800275eb3e3
~~~

### ReplicaSet に対応する Pod の集合の特定

まずは Label の集合を取得。

~~~sh
$ kubectl describe pods | grep -A1 Labels
Labels:             app=kuard
                    version=2
--
Labels:             app=kuard
                    version=2
~~~

次に　Label に一致する Pod を見つける。
`-l` は `--selector` の短縮形です。

~~~sh
$ kubectl get pods -l app=kuard,version=2
NAME          READY   STATUS    RESTARTS   AGE
kuard-d6qs6   1/1     Running   0          6m37s
kuard-j9xd6   1/1     Running   0          6m37s
~~~

### 命令的スケール (kubectl scale)

`kubectl scale replicasets` で CLI からもレプリカを変更できます。(**命令的設定**)

~~~sh
$ kubectl scale replicasets kuard --replicas=4
replicaset.extensions/kuard scaled
$ kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
kuard-d6qs6   1/1     Running   0          11m
kuard-j9xd6   1/1     Running   0          11m
kuard-rw8m7   1/1     Running   0          2s
kuard-w5wjr   1/1     Running   0          2s
~~~

これに対してマニフェストファイルを元に `kubectl apply -f` などで設定を行うことを**宣言的設定**と呼びます。
**kubenetes の思想は宣言的設定です。**
**命令的設定をした場合は、マニフェストファイルへの反映も忘れないように！**

### オートスケール (水平)

水平オートスケールを使用するには heapster という Pod がクラスタ内に存在している必要があるらしいのですが、私の minikube 環境では存在しませんでした。残念。

~~~sh
$ kubectl get pods -n kube-system
NAME                               READY   STATUS    RESTARTS   AGE
coredns-fb8b8dccf-m7m9s            1/1     Running   0          166m
coredns-fb8b8dccf-mjk59            1/1     Running   0          166m
etcd-minikube                      1/1     Running   0          165m
kube-addon-manager-minikube        1/1     Running   0          165m
kube-apiserver-minikube            1/1     Running   0          165m
kube-controller-manager-minikube   1/1     Running   0          165m
kube-proxy-gwtcc                   1/1     Running   0          166m
kube-scheduler-minikube            1/1     Running   0          165m
storage-provisioner                1/1     Running   0          166m
~~~

### オートスケーラーの作成

`kubectl autoscale` でオートスケーラーを作成できます。
下記は CPU 使用率 80 % を閾値にする例です。

~~~sh
$ kubectl autoscale rs kuard --min=2 --max=5 --cpu-percent=80
~~~

### オートスケーラーの情報取得

`kubectl get hpa` もしくは `kubectl get horizontalpodautoscalers` (長い！) 

~~~sh
$ kubectl get hpa
NAME    REFERENCE          TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
kuard   ReplicaSet/kuard   <unknown>/80%   2         5         2          5m38s
~~~

### オートスケーラーの削除

`kubectl delete hpa <hpa-name>`

~~~sh
$ kubectl delete hpa kuard
horizontalpodautoscaler.autoscaling "kuard" deleted
~~~

### ReplicaSet の削除

~~~sh
$ kubectl delete rs kuard
~~~

## DaemonSet

ReplicaSet と違うのは、ノードセレクタを使わない限り、DaemonSet は全ノードに Pod を作る点です。
クラスタの全ノードで動かしいたい Pod (ロギングなど) を作る時などに。

下記の YAML を作成します。

~~~fluentd.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    app: fluentd
spec:
  template:
    metadata:
      labels:
        app: fluentd
    spec:
      containers:
      - name: fluentd
        image: fluent/fluentd:v0.14.10
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
~~~

`$ kubectl apply` して `$ kubectl get ds` もしくは `$ kubectl get daemonset` で確認してみます。

~~~sh
$ kubectl apply -f fluentd.yaml
$ kubectl get ds -n kube-system
NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd      1         1         1       1            1           <none>          7s
kube-proxy   1         1         1       1            1           <none>          6h57m
~~~

本当はここでクラスタ内の全ノードに Pod fluentd がデプロイされるらしいのですが、
私の minikube 環境では Node が 1 つなので Pod も 1 つのみ・・という理解で合っているのかな。

~~~sh
$ kubectl get pods -o wide -n kube-system | grep fluentd
fluentd-t6d4k                      1/1     Running   0          5m42s   172.17.0.4   minikube   <none>           <none>
$ kubectl get pods -o wide
No resources found.
~~~

## Job

### Job 実行

1 回限りの処理を書くパターンです。マイグレーションやバッチ処理など。
下記の YAML を作成します。

~~~job-oneshot.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: oneshot
  labels:
    chapter: jobs
spec:
  template:
    metadata:
      labels:
        chapter: jobs
    spec:
      containers:
      - name: kuard
        image: gcr.io/kuar-demo/kuard-amd64:1
        imagePullPolicy: Always
        args:
        - "--keygen-enable"
        - "--keygen-exit-on-complete"
        - "--keygen-num-to-gen=10"
      restartPolicy: OnFailure
~~~

ちなみに `restartPolicy: Never`に変更すると、エラーになった際に Pod を再起動せず、代わりにその Pod を失敗と宣言してクラスタ内にゴミが溜まっていくので`RestartPolicy: OnFailure`が推奨だそうです。

apply して `kubectl logs <Pod 名>` でログを見ると実行されていることがわかります。

~~~sh
$ kubectl apply -f job-oneshot.yaml
$ kubectl logs oneshot-dr9qw
...
2019/06/30 05:18:39 Could not find certificates to serve TLS
2019/06/30 05:18:39 Serving on HTTP on :8080
2019/06/30 05:18:39 (ID 0) Workload starting
2019/06/30 05:18:41 (ID 0 1/10) Item done: SHA256:KDm4wfT1LpBHkgTATK61Q8K73bebl2YI8hzMdmhJVvY
2019/06/30 05:18:43 (ID 0 2/10) Item done: SHA256:KfYXXJXWdloUcBWoAw2r2AcwR0BkqTFe/69Rz+XdyeA
2019/06/30 05:18:45 (ID 0 3/10) Item done: SHA256:aBxBsT3Lmdehn9Jo+SJY4GJa3FV8tRTtGH+7sHaLAPc
2019/06/30 05:18:46 (ID 0 4/10) Item done: SHA256:BbfcwBa1q0H0A8/XbRFu6SAHnbM8Yu5irgyEcAv50/g
2019/06/30 05:18:49 (ID 0 5/10) Item done: SHA256:JAmbqe4td68us1e6+XL5lJ884kw77pj/R4/+Bly0+8s
2019/06/30 05:18:52 (ID 0 6/10) Item done: SHA256:hKiSMd5ktvMWuedTcRZI2z/SAufeRRJyLjglyZa4MbU
2019/06/30 05:18:55 (ID 0 7/10) Item done: SHA256:9XmnkfBiPc1tb0fy0zA7K54DAV+G7NHBUW3axVJEuBU
2019/06/30 05:18:58 (ID 0 8/10) Item done: SHA256:NzsPnixosEuYHYUqi/ghlvNT+oHQvqwIGFuObE/WoSs
2019/06/30 05:19:01 (ID 0 9/10) Item done: SHA256:k6JwEyIO6mKb6tCNMrEnS1JyI5a4rZBurEPhBaTbR2o
2019/06/30 05:19:02 (ID 0 10/10) Item done: SHA256:fj5YxnU+wYFUMP2bvZq7UZTz4jb7EFPhLCvx1Hnhv6E
2019/06/30 05:19:02 (ID 0) Workload exiting
~~~

### Job 実行 (パラレル)

~~~job-parallel.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: parallel // 変更
  labels:
    chapter: jobs
spec:
  parallelism: 5 // 追加
  completions: 10 // 追加
  template:
    metadata:
      labels:
        chapter: jobs
    spec:
      containers:
      - name: kuard
        image: gcr.io/kuar-demo/kuard-amd64:1
        imagePullPolicy: Always
        args:
        - "--keygen-enable"
        - "--keygen-exit-on-complete"
        - "--keygen-num-to-gen=10"
      restartPolicy: OnFailure
~~~

`get pods` すると複数 Pod が立ち上がっています。

~~~sh
$ kubectl get pods -w
NAME             READY   STATUS              RESTARTS   AGE
parallel-4kmsf   0/1     ContainerCreating   0          4s
parallel-h6cp9   0/1     ContainerCreating   0          4s
parallel-hhfg4   0/1     ContainerCreating   0          4s
parallel-shpgt   1/1     Running             0          4s
parallel-vjhj6   0/1     ContainerCreating   0          4s
parallel-4kmsf   1/1     Running             0          4s
parallel-hhfg4   1/1     Running             0          5s
parallel-vjhj6   1/1     Running             0          8s
parallel-h6cp9   1/1     Running             0          9s
~~~

Job オブジェクトを削除しておきます。

~~~sh
$ kubectl delete -f job-parallel.yaml
~~~

### Job 実行 (キュー)

~~~rs-queue.yaml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  labels:
    app: work-queue
    component: queue
    chapter: jobs
  name: queue
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: work-queue
        component: queue
        chapter: jobs
    spec:
      containers:
      - name: queue
        image: "gcr.io/kuar-demo/kuard-amd64:1"
        imagePullPolicy: Always
~~~

~~~sh
$ kubectl apply -f rs-queue.yaml
replicaset.extensions/queue created
~~~

### service を作成し、ポートを公開

これによってキューのプロデューサとコンシューマが DNS 経由でキューを使えるようになります。

~~~service-queue.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: work-queue
    component: queue
    chapter: jobs
  name: queue
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: work-queue
    component: queue
~~~

~~~sh
$ kubectl apply -f service-queue.yaml
service/queue created
~~~

### キューイングスクリプト

~~~que.sh
#!/bin/bash
for i in work-item-{0..99}; do
  curl -X POST localhost:8080/memq/server/queues/keygen/enqueue -d "$i"
done
~~~

~~~sh
$ chmod 755 que.sh
$ ./que.sh
~~~

### consumer

キューを消費する方です。

~~~job-consumers.yaml
apiVersion: batch/v1
kind: Job
metadata:
  labels:
    app: message-queue
    component: consumer
    chapter: jobs
  name: consumers
spec:
  parallelism: 5
  template:
    metadata:
      labels:
        app: message-queue
        component: consumer
        chapter: jobs
    spec:
      containers:
      - name: worker
        image: "gcr.io/kuar-demo/kuard-amd64:1"
        imagePullPolicy: Always
        args:
        - "--keygen-enable"
        - "--keygen-exit-on-complete"
        - "--keygen-memq-server=http://queue:8080/memq/server"
        - "--keygen-memq-queue=keygen"
      restartPolicy: OnFailure
~~~

apply して get pods してみると consumer が並行で動いていることがわかります。

~~~sh
$ kubectl apply -f job-consumers.yaml
job.batch/consumers created

$ kubectl get pods
NAME              READY   STATUS    RESTARTS   AGE
consumers-2hngf   1/1     Running   0          10s
consumers-drmbp   1/1     Running   0          10s
consumers-m5hdj   1/1     Running   0          10s
consumers-m8v9c   1/1     Running   0          10s
consumers-x7jlq   1/1     Running   0          10s
queue-7rl7h       1/1     Running   0          25m
~~~

## 出典

Kubernetes: Up and Running, by Kelsey Hightower, Brendan Burns,  and Joe Beda (O'Reilly), 978-1-491-93567-5. (日本語版「入門 Kubernetes」 Kelsey Hightower, Brendan Burns, Joe Beda 著、オライリー・ジャパン、ISBN 978-4-87311-840-6)