コンテンツにスキップ

05. レプリケーションと水平自動スケーリング

1
2
3
4
5
# 手動スケーリング
$ kubectl scale deployment nextjs-example --replicas=3

# オートスケーリング(CPU使用率50%で自動増減)
$ kubectl autoscale deployment nextjs-example --min=1 --max=5 --cpu-percent=50

DeploymentにてPodのレプリカ数を定義する。

k8s/app-deployment.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    io.kompose.service: app
  name: app
spec:
  replicas: 3
  selector:
    matchLabels:
      io.kompose.service: app
  template:
    metadata:
      labels:
        io.kompose.service: app
    spec:
      containers:
        - env:
            - name: DATABASE_URL
              value: postgresql://postgres:postgres@db:5432/todo
            - name: HOSTNAME
              value: "0.0.0.0"
          image: 192.168.11.42:8085/nextjs-todo-example:latest
          name: app
          ports:
            - containerPort: 3000
              protocol: TCP
      restartPolicy: Always

Serviceにてtype: NodePortを設定する。これによりServiceが複数のPodにリクエストを割り振るようになる。

k8s/app-service.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
  labels:
    io.kompose.service: app
  name: app
spec:
  type: NodePort
  ports:
    - name: "3000"
      port: 3000
      targetPort: 3000
  selector:
    io.kompose.service: app

なおKubernetesにおいて、Serviceは3種類のポートを持っている。

フィールド 意味
port 3000 Service自身のポート(クラスター内部用)
targetPort 3000 転送先のPodのポート
nodePort 未指定 ノードで公開するポート
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# リソースを適用する
$ kubectl apply -f ./k8s/

# Podsが3つ動作していることを確認する
$ kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
app-585544ffb9-88rqm   1/1     Running   0          18m
app-585544ffb9-qtmkr   1/1     Running   0          33m
app-585544ffb9-tfl74   1/1     Running   0          32m
db-668bb7bcbb-4m5xc    1/1     Running   0          33m

# ServiceのURLを確認する
$ minikube service app --url
http://***.***.***.***:31035

kubectl port-forward service/app 3000:3000は動作確認用のコマンドで Podのポートとホストのポートを紐づける。Podが複数ある場合、1つが自動で選ばれるため、レプリケーションの動作確認には向かない。

minikube service app --urlはServiceに紐づくURLを取得するため、Serviceのロードバランシングの動作確認に使える。

uml diagram

アプリケーションにアクセスできることを確認したら、Podを1つ削除する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Podを1つ削除する
$ kubectl delete pod app-585544ffb9-88rqm
pod "app-585544ffb9-88rqm" deleted from default namespace

# KubernetesがDeploymentの定義に従うようにPodを起動させていることを確認する
$ kubectl get pods
NAME                   READY   STATUS    RESTARTS   AGE
app-585544ffb9-qr79z   1/1     Running   0          3s
app-585544ffb9-qtmkr   1/1     Running   0          55m
app-585544ffb9-tfl74   1/1     Running   0          54m
db-668bb7bcbb-4m5xc    1/1     Running   0          55m

引き続きアプリケーションにアクセスできることを確認する。

水平自動スケーリング

水平自動スケーリングを行うにはHorizontalPodAutoscaler(以下HPAとする)のリソースを定義する。

HPAは設定値に従ってPodを増減させる。

k8s/app-hpa.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: app
  minReplicas: 1
  maxReplicas: 3
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50 # 全Podの平均CPU使用量が requests の50% を超えたらレプリカ数を増やす
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0 # 0秒間高負荷が続いたらスケールアップする (デフォルトは0秒)
    scaleDown:
      stabilizationWindowSeconds: 60 # 60秒間低負荷が続いたらスケールダウンする (デフォルトは5分)

ただしHPAは使用率を計算するための基準値が必要となる。

\[ \mathrm{CPU使用率} = \frac{\mathrm{実際の使用量}}{\mathrm{requests}} \]

基準値はDeploymentで定義する。

k8s/app-deployment.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    io.kompose.service: app
  name: app
spec:
  selector:
    matchLabels:
      io.kompose.service: app
  template:
    metadata:
      labels:
        io.kompose.service: app
    spec:
      containers:
        - env:
            - name: DATABASE_URL
              value: postgresql://postgres:postgres@db:5432/todo
            - name: HOSTNAME
              value: "0.0.0.0"
          image: 192.168.11.42:8085/nextjs-todo-example:latest
          name: app
          ports:
            - containerPort: 3000
              protocol: TCP
          resources:
            requests:
              cpu: "4m" # Pod1個で0.004コアを確保する
            limits:
              cpu: "10m" # Pod1個で最大0.01コアを確保する
      restartPolicy: Always

requestsの値はkubectl top podsで計測して、適切な値に変更する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ minikube addons enable metrics-server
💡  metrics-server is an addon maintained by Kubernetes. For any concerns contact minikube on GitHub.
You can view the list of minikube maintainers at: https://github.com/kubernetes/minikube/blob/master/OWNERS
     Using image registry.k8s.io/metrics-server/metrics-server:v0.8.0
🌟  The 'metrics-server' addon is enabled

# 結構時間がかかる
$ kubectl top pods
error: Metrics API not available
$ kubectl top pods
error: Metrics API not available
$ kubectl top pods
error: Metrics API not available
$ kubectl top pods
NAME                   CPU(cores)   MEMORY(bytes)
app-585544ffb9-qr79z   1m           33Mi
app-585544ffb9-qtmkr   1m           33Mi
app-585544ffb9-tfl74   1m           33Mi
db-668bb7bcbb-4m5xc    4m           20Mi

# 以下でも良い
$ watch -n 0.5 kubectl top pods
# これでも良い
$ kubectl get pods -w

appについて、以下のことがわかった。

\[ \mathrm{appの定常時の使用量} = 1m \]

HPAのリソース定義より、HPAのスケールアップ閾値は以下で求められる。

\[ \begin{align} \mathrm{HPAのスケールアップ閾値} &= \mathrm{Deploymentのrequests} \times \frac{\mathrm{HPAのaverageUtilization}}{100} \\ &= 4m \times \frac{50}{100} \\ &= 2m \end{align} \]

appの定常時の使用量は1mであるから、limitsは10mとすると上限は定常時の10倍までになる。

1
2
3
$ kubectl apply -f ./k8s/

$ watch -n 0.5 kubectl top pods

Webブラウザでアプリケーションにアクセスし、F5を連打するとPodが増えることがわかる。

放置するとPodが減ることがわかる。