日々体験記

できたりしたことを忘れないようにメモしたり、、、がほとんどです。。。

午前4時に障害対応をやらないために

注意

この記事はタイトルに対して汎用的に使用できるものではありません。

GCPのconfig connectorに関する記事です。

本文に多く「リソース」という言葉が出てきますが、これはGCPのconpute engine, gcs, bigquery, gke, memory store...など様々なサービスを想定して書いています。

config connectorはとても便利ではあるが、、

cloud.google.com

個人的にはそう信じています!

GKE上からyamlを定義してapplyするだけで、今までconsoleから作成していたリソースを簡単に作成することができます。

ということは、gitでソースコード同様に管理することができます。

これは嬉しいですよね?

これによって、GCPで使用しているサービスに依存してやりにくかったテストなど、開発のプロセスで少し難しく感じているプロセスを解消することができそうです。

ただ、注意も必要だということも私は、障害を起こしてしまったことで学びました。

先ほども記載したように「簡単に作成することができる」ということは、一方で簡単に削除もできてしまいます。

この特徴をちゃんと理解していないと本当に危険な目に遭います。

本番環境で運用しているBigQueryなどをデータごと吹っ飛ばしてしまうかもしれません。

何を起こしてしまったか

今の職場でconfig connectorを使用できないかということで、検証していました。

その中でプロジェクトのIAMを吹っ飛ばしてしまいました。

サービスアカウントを作成して、必要な権限を与えるためのyamlを定義してapplyしました。

コンソールを確認した時、目を疑いました。

全てのIAMがなくなっているのです。

目を疑いましたが、夢ではありませんでした。

既に運用しているアプリケーションに割り当てているサービスアカウントの権限だけでなく、

各サービスのデフォルトのサービスアカウントやサービスエージェントのIAMがなくなってしまっていました。

数分後には、使用しているサービスがアラートをあげ始める事態となりました。

原因

この2つを理解していなかったことが原因でした。

  • config connectorの適用スコープと適用時の動作
  • config connector独自の削除操作に関するアノテーション

config connectorの適用スコープと適用時の動作

cloud.google.com

全てのリソースではないですが、一部のリソースではプロジェクト全体で上書きされる形で適用されてしまいます。

私はIAM Policyを使用してIAMを全て吹っ飛ばしてしまいました。

IAMPolicy  |  Config Connector Documentation  |  Google Cloud

このexampleのコメントに以下のように記載されています。

    # **WARNING**: The bindings here represent the full declarative intent for the project.
    # It will fully overwrite the existing policy on the given project.
    #
    # For finer-grained control over the project's IAM policy, it is recommended
    # that the IAMPolicyMember resource be used instead.
    #
    # This sample assumes the following additional APIs are enabled:
    #   - compute.googleapis.com
    #   - container.googleapis.com
    #   - containerregistry.googleapis.com
    #   - redis.googleapis.com
    #
    # Replace ${PROJECT_ID?}, ${PROJECT_NUMBER?}, and ${PROJECT_NAME?} with your desired project ID,
    # that project's project number, and your Google Cloud account email respectively.

これを知らずに権限を与えたいサービスアカウントだけ権限を付与するようなyamlを定義して適用させたため、

上書きされてしまい、今まで存在していたサービスアカウント(各サービスのサービスアカウントやサービスジェントも含む)がすべてなくなってしまいました。

ただこのように動作するのはIAM Policyだけのようです。使用する際はドキュメントに記載されているexampleのyamlまでしっかり読んでから動かしてみることを強くお勧めします。

config connector独自の削除操作に関するアノテーション

これは、障害を起こしてしまった際に直接的に作用したものではないですが、その時ちゃんと理解しておらず、もしかしたら他のリソースを動かした時に

このアノテーションによる障害も起こしてしまっていたかもしれないと思い、記載しました。

cloud.google.com

config connectorは新しくリソースを作成することはもちろんですが、既に作成、運用されているリソースに対してconfig connectorの機能によって

インポートして紐づけることもできます。

既存の Google Cloud リソースのインポート  |  Config Connector のドキュメント

作成や紐付けたリソースを削除した場合、

もちろんk8sクラスタのリソースから削除されることはもちろんですが、GCP上のリソースからも削除されてしまいます。

本当に必要ないのであればGCP上のリソースから削除されてしまっても問題ないのですが、ケースによってはconfig connectorの紐付けだけ外して

リソース自体は残しておきたい場面があるかもしれません。(リソースに消えてはまずいデータが保管されているのであれば尚更です)

ドキュメントにも赤く警告文として記載されていますが

...
metadata:
  annotations:
    cnrm.cloud.google.com/deletion-policy: abandon
...

このアノテーションを付けることで、リソース自体を残すことができます。

障害を起こしてしまった時の対応

これは結構特例ですが、

サービスエージェントが失われてしまい、ほとんどのサービスのAPIを使用することができなくなってしまいました。

この消えてしまったサービスエージェントたちは、手作業で復旧しました。

手作業で同じようなIAMを復活させて本当に障害以前のように安定稼働するのか不安でしたが、問題なく動きました。 (後にサポートの方に聞き、状態など確認してもらいましたが問題なく動いているとの連絡をもらいました)

最後に

使用上の注意を正しく読んでお使いください。

以上夏の思い出でした。

今更RBAC

タイトルの通り、いまさらkubernetesのRBACをいじってみた。

Using RBAC Authorization - Kubernetes

ロールベースのアクセス制御  |  Kubernetes Engine ドキュメント  |  Google Cloud

KubernetesのRBACについて - Qiita

Kubernetes道場 20日目 - Role / RoleBinding / ClusterRole / ClusterRoleBindingについて - Toku's Blog

「RBACとは」的な説明は省略しているので、そのようなことを知りたい方は上記のリンクなどに記載している。

いじる前の準備(ベースの環境作成)

namespace

apiVersion: v1
kind: Namespace
metadata:
  name: wakashiyo

service account

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sample
  namespace: wakashiyo

検証用Pod

pod

apiVersion: v1
kind: Pod
metadata:
  name: sample-kubectl
  namespace: wakashiyo
spec:
  serviceAccountName: sample
  containers:
    - name: kubectl-container
      image: lachlanevenson/k8s-kubectl:latest
      command: ["sleep", "86400"]

1. 検証用のPodからkubectlコマンドを使用してPodを作成してみる

この段階では何もRoleは設定されていない

作成してみる

kubectl exec -it sample-kubectl -n wakashiyo -- kubectl run nginx --image=nginx:latest

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot create resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1

作ることができなかった

Podのリソースを作成することが禁じられていると言われてしまったので、Roleを付与してみる

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create"]

role binding

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: sample-role-binding
  namespace: wakashiyo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: sample-role
subjects:
  - kind: ServiceAccount
    name: sample
    namespace: wakashiyo

apply する

kubectl apply -f role.yml
kubectl apply -f rolebinding.yml

再度作成し直してみる

kubectl exec -it sample-kubectl -n wakashiyo -- kubectl run nginx --image=nginx:latest

pod/nginx created


kubectl get pods -n wakashiyo -o wide

NAME             READY   STATUS    RESTARTS   AGE     IP           NODE                                       NOMINATED NODE   READINESS GATES
nginx            1/1     Running   0          10s     10.48.1.12   gke-wakashiyo-default-pool-29ce2b10-b5gc   <none>           <none>
sample-kubectl   1/1     Running   0          9m55s   10.48.0.10   gke-wakashiyo-default-pool-29ce2b10-gx26   <none>           <none>

作成できた

2. 検証用のPodから get pods したりしてみる

kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods -n wakashiyo

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot list resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1



kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods -n wakashiyo

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot list resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1



kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods nginx -n wakashiyo

Error from server (Forbidden): pods "nginx" is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot get resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1



kubectl exec -it sample-kubectl -n wakashiyo -- kubectl describe pods nginx -n wakashiyo

Error from server (Forbidden): pods "nginx" is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot get resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1

できない。

特定のPodを指定してdescribeしたり、getしたりするためにはgetの操作権限、

pod一覧を出したりするためにはlistの操作権限が必要そう。

roleを変更してみる

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "get", "list"] # get, listを追加

再度getしたりしてみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods -n wakashiyo

NAME             READY   STATUS    RESTARTS   AGE
nginx            1/1     Running   0          8m5s
sample-kubectl   1/1     Running   0          17m



% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods nginx -n wakashiyo

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          8m13s



% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl describe pods nginx -n wakashiyo
Name:         nginx
Namespace:    wakashiyo
Priority:     0
... 以下省略

getできた。

ちなみに今まで設定していたのはClusterRoleではなく、Roleだったので、 --all-namespaces でのget操作はできない。

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods --all-namespaces

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot list resource "pods" in API group "" at the cluster scope
command terminated with exit code 1

cluster roleを追加する

cluster role

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-cluster-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]

cluster role binging

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: sample-cluster-role-binding
  namespace: wakashiyo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: sample-cluster-role
subjects:
  - kind: ServiceAccount
    name: sample
    namespace: wakashiyo

applyして再度 --all-namespace でgetしてみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods --all-namespaces

NAMESPACE     NAME                                                        READY   STATUS    RESTARTS   AGE
kube-system   event-exporter-v0.2.5-599d65f456-jn2t4                      2/2     Running   0          4h
kube-system   fluentd-gcp-scaler-bfd6cf8dd-cj6m5                          1/1     Running   0          4h
kube-system   fluentd-gcp-v3.1.1-jml9r                                    2/2     Running   0          3h59m
kube-system   fluentd-gcp-v3.1.1-mvhhf                                    2/2     Running   0          3h59m
kube-system   heapster-gke-ddccfc6d-xtgkv                                 3/3     Running   0          3h59m
kube-system   kube-dns-5995c95f64-dclwz                                   4/4     Running   0          4h
kube-system   kube-dns-5995c95f64-qh26s                                   4/4     Running   0          4h
kube-system   kube-dns-autoscaler-8687c64fc-xsh7x                         1/1     Running   0          4h
kube-system   kube-proxy-gke-wakashiyo-default-pool-29ce2b10-b5gc         1/1     Running   0          4h
kube-system   kube-proxy-gke-wakashiyo-default-pool-29ce2b10-gx26         1/1     Running   0          4h
kube-system   l7-default-backend-8f479dd9-r2xgs                           1/1     Running   0          4h
kube-system   metrics-server-v0.3.1-5c6fbf777-26kst                       2/2     Running   0          3h59m
kube-system   prometheus-to-sd-4bkqh                                      2/2     Running   0          4h
kube-system   prometheus-to-sd-lhb4t                                      2/2     Running   0          4h
kube-system   stackdriver-metadata-agent-cluster-level-6f6775c594-qmcx7   2/2     Running   0          3h59m
wakashiyo     nginx                                                       1/1     Running   0          14m
wakashiyo     sample-kubectl                                              1/1     Running   0          24m

できた。

3. 検証用のPodから作成したnginxのPodを削除してみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl delete pods nginx -n wakashiyo

Error from server (Forbidden): pods "nginx" is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot delete resource "pods" in API group "" in the namespace "wakashiyo"
command terminated with exit code 1

当然deleteの権限がないのでできない。

roleを変更する

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "get", "list", "delete"] # deleteを追加

applyして再度delete podを実行してみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl delete pods nginx -n wakashiyo

pod "nginx" deleted




% kubectl get pods -n wakashiyo

NAME             READY   STATUS    RESTARTS   AGE
sample-kubectl   1/1     Running   0          27m

削除できた

4. podをスケールさせてみる

% kubectl get pods -n wakashiyo

NAME                    READY   STATUS    RESTARTS   AGE
nginx-cd9dcdd6c-jwnvn   1/1     Running   0          78s
sample-kubectl          1/1     Running   0          55m




% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl scale deploy nginx --replicas 2

Error from server (Forbidden): deployments.extensions "nginx" is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot get resource "deployments" in API group "extensions" in the namespace "wakashiyo"
command terminated with exit code 1

できない。

roleに権限を追加する

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "get", "list", "delete"]
  - apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["get"] # getを追加

再度スケールさせてみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl scale rs nginx --replicas 2

Error from server (Forbidden): deployments.extensions "nginx" is forbidden: User "system:serviceaccount:wakashiyo:sample" cannot patch resource "deployments/scale" in API group "extensions" in the namespace "wakashiyo"
command terminated with exit code 1

まだできない

再度roleを変更する

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "get", "list", "delete"]
  - apiGroups: ["apps", "extensions"]
    resources: ["deployments"]
    verbs: ["get"]
  # 以下を追加
  - apiGroups: ["apps", "extensions"]
    resources: ["deployments/scale"]
    verbs: ["patch"]

再度スケールさせてみる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl scale deploy nginx --replicas 2

deployment.extensions/nginx scaled




% kubectl get pods -n wakashiyo
NAME                    READY   STATUS    RESTARTS   AGE
nginx-cd9dcdd6c-jwnvn   1/1     Running   0          4m27s
nginx-cd9dcdd6c-zs7f9   1/1     Running   0          7s
sample-kubectl          1/1     Running   0          58m

やっとできた。

スケールさせるには deploymentsではなく、 deployments/scale というリソースに対する権限が必要だった。

その他

RoleBindingはRoleを1つしか設定することができない。

role binding

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: sample-role-binding
  namespace: wakashiyo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  - kind: Role
    name: sample-role
  - kind: Role # 2つ目のrole
    name: sample-role2
subjects:
  - kind: ServiceAccount
    name: sample
    namespace: wakashiyo
% kubectl apply -f rolebinding.yml
error: error parsing rolebinding.yml: error converting YAML to JSON: yaml: line 7: did not find expected key

※ エディタで書いている段階でエラー出る

RoleBindingで紐付けるservice accountは複数設定できる

role binding

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: sample-role-binding
  namespace: wakashiyo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: sample-role
subjects: # 複数設定している
  - kind: ServiceAccount
    name: sample
    namespace: wakashiyo
  - kind: ServiceAccount
    name: sample2
    namespace: wakashiyo

1つのRoleで同じリソースに対してのルールを複数設定することができる

role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-role
  namespace: wakashiyo
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "delete"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]

上記で設定した操作ができる。

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl run nginx --image=nginx
pod/nginx created



% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods nginx -n wakashiyo
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          19s



% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl delete pods nginx -n wakashiyo
pod "nginx" deleted

cluster roleに限ってはaggregationできる。

cluster role 1

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-cluster-role
  labels:
    app: sample-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]

cluster role 2

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-cluster-role2
  labels:
    app: sample-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["delete"]

cluster role 3

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-cluster-role3
  labels:
    app: sample-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create"]

aggregated cluster role

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sample-aggregated-cluster-role
aggregationRule:
  clusterRoleSelectors:
    - matchLabels:
        app: sample-role

aggregateされている

% kubectl get clusterrole sample-aggregated-cluster-role -o yaml
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      app: sample-role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"aggregationRule":{"clusterRoleSelectors":[{"matchLabels":{"app":"sample-role"}}]},"apiVersion":"rbac.authorization.k8s.io/v1beta1","kind":"ClusterRole","metadata":{"annotations":{},"name":"sample-aggregated-cluster-role"}}
  creationTimestamp: "2020-05-05T12:24:26Z"
  name: sample-aggregated-cluster-role
  resourceVersion: "76217"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/sample-aggregated-cluster-role
  uid: 5c5491eb-8ecb-11ea-a4d9-42010a9200fb
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - delete
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - create

実際にcreate, get, deleteできる

% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl run nginx --image=nginx
pod/nginx created



% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods -n wakashiyo
NAME             READY   STATUS    RESTARTS   AGE
nginx            1/1     Running   0          17s
sample-kubectl   1/1     Running   0          102m




% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl get pods nginx -n wakashiyo
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          26s




% kubectl exec -it sample-kubectl -n wakashiyo -- kubectl delete pods nginx -n wakashiyo
pod "nginx" deleted

おしまい。

GraphQLの認証と認可について

GraphQLの認証と認可についてどうなっているのか知りたくなったので、調べてみた。

基本的にgraphql-rubyをベースに書いてます。

認証(Authentification)について

GraphQL - Overview

ここに記載されていた。

In general, authentication is not addressed in GraphQL at all.

残念ながら、認証の機構については全く用意されていなかった。

そのため認証については個別で実装したり、他のサードパーティのライブラリやSaaSなどを使用して導入するしかないっぽい。

ドキュメントには以下のように記載されていた。

Instead, your controller should get the current user based on the HTTP request (eg, an HTTP header or a cookie) and provide that information to the GraphQL query.

代わりにHTTPリクエストのCookieなり、ヘッダーなりにユーザーの情報を含めてGraphQLのやり取りすることが必要だと言っている。

実際、graphql-rubyをインストールした際に生成されるcontrollerはこのようになっている。

class GraphqlController < ApplicationController
  # If accessing from outside this domain, nullify the session
  # This allows for outside API access while preventing CSRF attacks,
  # but you'll have to authenticate your user separately
  # protect_from_forgery with: :null_session

  def execute
    variables = ensure_hash(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = {
      # Query context goes here, for example:
      # current_user: current_user <= ここがコメントアウトされている
    }
    result = AppSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
    render json: result
  rescue StandardError => e
    raise e unless Rails.env.development?

    handle_error_in_development e
  end

  private

  # Handle form data, JSON body, or a blank value
  def ensure_hash(ambiguous_param)
    case ambiguous_param
    when String
      if ambiguous_param.present?
        ensure_hash(JSON.parse(ambiguous_param))
      else
        {}
      end
    when Hash, ActionController::Parameters
      ambiguous_param
    when nil
      {}
    else
      raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
    end
  end

  def handle_error_in_development(e)
    logger.error e.message
    logger.error e.backtrace.join("\n")

    render json: { error: { message: e.message, backtrace: e.backtrace }, data: {} }, status: 500
  end
end

contextの部分にcurrent_userというものが用意されている。

実装者がこの部分を何らかの形でユーザー情報を取得し、current_userに含めれば、その後のQueryやMutationでcurrent_userにアクセスして 認可的な処理などを実装することができる。

それで認証は?

先ほどの説明から認証の機構は用意されていないので、自前で実装するしかない。

全てを1からやるのは面倒なので、railsの認証のライブラリで有名なDeviseを使用してサンプル実装をやってみた。

使用したgem

  • devise
  • devise-token_authenticatable

deviseの基本的な部分はをインストールすれば、モデルなど自動的に作成してくれるので特にやることはない。

先ほどの GraphqlController のcontextのcurrent_userのコメントアウトを外せば使用できるようになり、

デバッグしてみてもリクエストを送信したユーザーの情報が含まれていることがわかると思う。

(リクエストにはユーザー作成時に発行されたトークンをリクエストの Authorization: Bearer <発行されたトークン> にセットすることでユーザーが誰であるのかをサーバー側で把握することができる。)

認可について

graphql-rubyには3つの認可フレームワークが用意されている。

  • visibility
  • Accessibility
  • Authorization

このうち Accessibility については、非推奨となっているので今回は扱わない。

visibilityについて

これは、ユーザーの権限によってGraphQLスキーマの一部を隠すものだ。

実際にvisibilityを使用してみる

module Types
  class QueryType < Types::BaseObject

    field :todos, Types::TodoType.connection_type, null: false do
      def visible?(context)
        super && context[:current_user].role == 'admin'
      end
    end

    def todos
      Todo.all
    end
  end
end

上記以外の補足説明としては以下の2つのモデルが存在している

  • User
  • Todo
class User < ApplicationRecord
  has_many :todos, dependent: :destroy
end

class Todo < ApplicationRecord
  belongs_to :user
end

上記のような関係性である。

最初のQueryTypeの説明に戻すと、adminの場合はユーザー関係なく全てのTodoを見れるようにしたいとする。

上記を実現するためにvisibilityをfield: todosに実装した。

これの状態でadmin以外のユーザーでクエリを実行すると以下のように返ってくる。

"message": "Field 'todos' doesn't exist on type 'Query'"

そもそもそんなものないよと言われている。

確かに隠している形になっている。

ちなみにvisibilityはドキュメントに記載されているように4種類のメソッドを提供している

Type classes have a .visible?(context) class method

Fields and arguments have a #visible?(context) instance method

Enum values have #visible?(context) instance method

Mutation classes have a .visible?(context) class method

特定のモデルに紐付くTypeのfieldに使用したり、

mutationでもクラスメソッドして提供されているので、特定のmutationを隠蔽することができる。

module Mutations
  class CreateTodoMutation < BaseMutation
    argument :title, String, required: true
    argument :description, String, required: true

    field :todo_edge, Types::TodoType.edge_type, null: false

    def self.visible?(context)
      super && context[:current_user].role == 'admin'
    end

 # 以下省略
end

Authorizationについて

これは、ユーザーがアクセス中のオブジェクトに対してアクセスできる権限をチェックするものだ。

先ほどのvisibilityの例をauthorizationに変えてみた

module Types
  class QueryType < Types::BaseObject

    field :todos, Types::TodoType.connection_type, null: false do
      def authorized?(obj, args, ctx)
        super && context[:current_user].role == 'admin'
      end
    end

    def todos
      Todo.all
    end
  end
end

visibilityは受け渡される引数はcontextのみだったのに対して、 object, argument, contextが受け渡される。

objectはfieldから返されるアプリケーションオブジェクトをあらわす。

argumentはアクセスするfieldに必要なargumentをあらわす。

クエリを実行すると、結果としてはvisibilityと同じようにアクセスできずエラーが返却されるが、 メッセージ内容が異なることがわかる。

"message": "Cannot return null for non-nullable field Query.todos"

QueryTypeのtodosというfieldに対して許可を持ってないので、非表示にしたという内容の記述が返ってきた。

先ほどのvisibilityはtodosなんてないよと言ってきたのに対して、こちらは存在するが権限を持っていないよと言われている。

Authorizationは3種類のメソッドを提供している

Type classes have .authorized?(object, context) class methods

Fields have #authorized?(object, args, context) instance methods

Arguments have #authorized?(object, arg_value, context) instance methods

先ほどと同じように実装することができる。

Authorizationはデフォルトでアクセスしたオブジェクトに対して、許可しなかった場合、オブジェクトが存在しなかったかのようにnilを返すようになっている。

これは実装者自信でカスタマイズすることができる。

class AppSchema < GraphQL::Schema
  mutation(Types::MutationType)
  query(Types::QueryType)

  # Opt in to the new runtime (default in future graphql-ruby versions)
  use GraphQL::Execution::Interpreter
  use GraphQL::Analysis::AST

  # Add built-in connections for pagination
  use GraphQL::Pagination::Connections

  def self.unauthorized_object(error)
    raise GraphQL::ExecutionError, "An object of type #{error.type.graphql_name} was hidden due to permissions"
  end

  def self.unauthorized_field(error)
    raise GraphQL::ExecutionError, "The field #{error.field.graphql_name} on an object of type #{error.type.graphql_name} was hidden due to permissions"
  end
end

クラスメソッドとして提供されているunauthorized_fieldを実装することで、デフォルトではなく、カスタマイズしたものを返すことができる。

特定のfieldに対してはunauthorized_fieldが提供され、

特定の(クラス)オブジェクトに対してはunauthorized_objectが提供される。

module Types
  class QueryType < Types::BaseObject
    def self.authorized?(obj, ctx)
      super && ctx[:current_user].role == 'admin'
    end

  # 以下省略

  end
end

unauthorized_objectを実装していないと以下のようにnullが返ってくる

{
  "data": null
}

unauthorized_objectを実装すると以下のように変えることができる。

{
  "errors": [
    {
      "message": "An object of type Query was hidden due to permissions"
    }
  ]
}

descheduler再実践記録

前回の記事では、ほぼ和訳と実践してみたものの上手く動かないまま終わってしまった。

今回はちゃんと動いた結果を書いていく

環境

GKE

version : 1.13.11-gke.23

マシンタイプ : n1-standard-1

node数 : 3

リソースが欲しいので、stackdriver loggingを無効にしてfluentdがいない構成にしている

まずLowNodeUtilizationからやってみる

実行前Podの一覧である。

見てお分かりの通り、node3台にもかかわらず、2台のnodeで全てのPodが配置されている。

 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-69bkl   1/1     Running   0          29m   10.48.1.16   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-hpdr7   1/1     Running   0          29m   10.48.1.19   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-khgvv   1/1     Running   0          29m   10.48.1.18   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-khvgc   1/1     Running   0          29m   10.48.1.17   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-vkn9h   1/1     Running   0          29m   10.48.0.15   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-z2xfj   1/1     Running   0          29m   10.48.0.16   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>

実行前にnodeの情報を見てみた。

LowNodeUtilizationはnodeのcpu、memory、podのリソースを設定して実行するものなので、事前に確認しておく。

本来の運用であれば、ある程度基準値を設けておいて設定しておくはずだが、今回はとりあえず動いているところを見たい+流れがわかりやすくなると思うので、事前に確認して設定することにする。

gke-wakashiyo-default-pool-8b0ec5ba-x29p

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-x29p
Non-terminated Pods:         (8 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                heapster-86f6474897-stbl9                              63m (6%)      63m (6%)    215840Ki (7%)    215840Ki (7%)  140m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-x29p    100m (10%)    0 (0%)      0 (0%)           0 (0%)         140m
  kube-system                metrics-server-v0.3.1-57c75779f-xg59s                  48m (5%)      143m (15%)  105Mi (3%)       355Mi (13%)    140m
  kube-system                prometheus-to-sd-wmcsp                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      140m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-69bkl                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     31m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-hpdr7                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     31m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-khgvv                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     31m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-khvgc                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     31m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests        Limits
  --------                   --------        ------
  cpu                        612m (65%)      609m (64%)
  memory                     651040Ki (24%)  1009440Ki (37%)
  ephemeral-storage          0 (0%)          0 (0%)
  attachable-volumes-gce-pd  0               0

gke-wakashiyo-default-pool-8b0ec5ba-2w40

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-2w40
Non-terminated Pods:         (6 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                kube-dns-79868f54c5-7cc5w                              260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)     141m
  kube-system                kube-dns-autoscaler-bb58c6784-8ms5s                    20m (2%)      0 (0%)      10Mi (0%)        0 (0%)         141m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-2w40    100m (10%)    0 (0%)      0 (0%)           0 (0%)         141m
  kube-system                prometheus-to-sd-qxg9g                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      141m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-vkn9h                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     32m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-z2xfj                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     32m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests     Limits
  --------                   --------     ------
  cpu                        581m (61%)   203m (21%)
  memory                     290Mi (10%)  390Mi (14%)
  ephemeral-storage          0 (0%)       0 (0%)
  attachable-volumes-gce-pd  0            0

gke-wakashiyo-default-pool-8b0ec5ba-654k

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-654k
Non-terminated Pods:         (4 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                kube-dns-79868f54c5-jvwg8                              260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)     142m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-654k    100m (10%)    0 (0%)      0 (0%)           0 (0%)         142m
  kube-system                l7-default-backend-fd59995cd-jfvwb                     10m (1%)      10m (1%)    20Mi (0%)        20Mi (0%)      142m
  kube-system                prometheus-to-sd-d6vmv                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      142m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests    Limits
  --------                   --------    ------
  cpu                        371m (39%)  13m (1%)
  memory                     150Mi (5%)  210Mi (7%)
  ephemeral-storage          0 (0%)      0 (0%)
  attachable-volumes-gce-pd  0           0

describe した結果を一覧表にまとめた。

node cpu memory pod数
gke-wakashiyo-default-pool-8b0ec5ba-x29p 612m (65%) 651040Ki (24%) 8
kube-system: 4
wakashiyo: 4
gke-wakashiyo-default-pool-8b0ec5ba-2w40 581m (61%) 290Mi (10%) 6
kube-system: 4
wakashiyo: 2
gke-wakashiyo-default-pool-8b0ec5ba-654k 371m (39%) 150Mi (5%) 4
kube-system: 4
wakashiyo: 0

今回はgke-wakashiyo-default-pool-8b0ec5ba-x29pにPodが偏っているので、このnodeを削除対象のnodeとしてスケジュールしたい。

そして、Podが全然配置されていないgke-wakashiyo-default-pool-8b0ec5ba-654kを再スケジュール対象のnodeとしてスケジュールしたい。

上記を考慮して以下のようなconfigMapの設定にした

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "RemoveDuplicates":
        enabled: true
      "RemovePodsViolatingInterPodAntiAffinity":
        enabled: true
      "LowNodeUtilization":
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              "cpu" : 50
              "memory": 50
              "pods": 5
            targetThresholds:
              "cpu" : 60
              "memory": 30
              "pods": 7

注意しなければならないのは、kube-systemも含めた全リソースのcpu使用率、memory使用率、Pod数を考慮して設定しなければならないことだ。

そして、再スケジュール対象のnodeを決めるpolicy(thresholds)は全ての条件を満たさなければならないのに対し、

削除対象のnodeを決めるpolicy(targetThresholds)は3つの要素のいずれかを満たせばいい。

ということを考慮して上記のconfigMapのように設定した。

このconfigMapを適用して、deschedulerのジョブを実行させた。以下が実行後のPodの一覧である。

kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-69bkl   1/1     Running   0          41m   10.48.1.16   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-dts4h   1/1     Running   0          30s   10.48.2.18   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-f72ch   1/1     Running   0          30s   10.48.2.19   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-jg9vv   1/1     Running   0          30s   10.48.1.20   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-vkn9h   1/1     Running   0          41m   10.48.0.15   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-xzffg   1/1     Running   0          30s   10.48.0.17   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>

うまくいったようだ。2台ずつに配置されたので、いい感じになった。

RemovePodsViolatingNodeAffinity をやってみる

policyの説明は省略する。

nodeのラベルの一覧を確認する。

kubectl get nodes -o json | jq ".items[] | .metadata.labels"
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-2w40"
}
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-654k",
  "sample": "bias"
}
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-x29p"
}

あらかじめ、gke-wakashiyo-default-pool-8b0ec5ba-654kに対して、 sample=bias というラベルを付与した。

そして、そのラベルが付いているnodeにPodをスケジューリングするようにした。

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  namespace: wakashiyo
  name: wakashiyo-deployment
spec:
  replicas: 5
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.12
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: 75Mi
              cpu: 100m
            limits:
              memory: 100Mi
              cpu: 100m
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: sample
                    operator: In
                    values:
                      - bias
 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-54664b6fc8-44qf2   1/1     Running   0          20s   10.48.2.20   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-5wlzm   1/1     Running   0          16s   10.48.2.24   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-nm5rh   1/1     Running   0          20s   10.48.2.22   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-qnc2l   1/1     Running   0          20s   10.48.2.21   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-wljgc   1/1     Running   0          16s   10.48.2.23   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
(base) takahiroyoshikawa@TakahironoMacBook-Pro descheduler %

全てのPodがgke-wakashiyo-default-pool-8b0ec5ba-654kに配置されている。

ここで、 sample=bias というラベルを他のnode に付与する。

(base) takahiroyoshikawa@TakahironoMacBook-Pro descheduler % kubectl label nodes gke-wakashiyo-default-pool-8b0ec5ba-654k sample-
node/gke-wakashiyo-default-pool-8b0ec5ba-654k labeled
(base) takahiroyoshikawa@TakahironoMacBook-Pro descheduler % kubectl label nodes gke-wakashiyo-default-pool-8b0ec5ba-x29p sample=bias
node/gke-wakashiyo-default-pool-8b0ec5ba-x29p labeled
kubectl get nodes -o json | jq ".items[] | .metadata.labels"
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-2w40"
}
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-654k"
}
{
  "beta.kubernetes.io/arch": "amd64",
  "beta.kubernetes.io/fluentd-ds-ready": "true",
  "beta.kubernetes.io/instance-type": "n1-standard-1",
  "beta.kubernetes.io/os": "linux",
  "cloud.google.com/gke-nodepool": "default-pool",
  "cloud.google.com/gke-os-distribution": "cos",
  "failure-domain.beta.kubernetes.io/region": "asia-northeast1",
  "failure-domain.beta.kubernetes.io/zone": "asia-northeast1-a",
  "kubernetes.io/hostname": "gke-wakashiyo-default-pool-8b0ec5ba-x29p",
  "sample": "bias"
}

kube-schedulerはPodの作成時にはルールに基づいたスケジューリングを行うが、それ以外の場合は再スケジュールなどはしない。

そのためラベルを付け変えたが、特に変化はない

kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE     IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-54664b6fc8-44qf2   1/1     Running   0          3m40s   10.48.2.20   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-5wlzm   1/1     Running   0          3m36s   10.48.2.24   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-nm5rh   1/1     Running   0          3m40s   10.48.2.22   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-qnc2l   1/1     Running   0          3m40s   10.48.2.21   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-54664b6fc8-wljgc   1/1     Running   0          3m36s   10.48.2.23   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>

再スケジュールするためにconfigMapを適用し、jobを実行した。

configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "RemoveDuplicates":
        enabled: true
      "RemovePodsViolatingInterPodAntiAffinity":
        enabled: true
      "RemovePodsViolatingNodeAffinity":
        enabled: true
        params:
          nodeAffinityType:
          - "requiredDuringSchedulingIgnoredDuringExecution"

結果

 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-54664b6fc8-5hn99   1/1     Running   0          23s   10.48.1.22   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-54664b6fc8-6xp6k   1/1     Running   0          23s   10.48.1.25   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-54664b6fc8-7gvqt   1/1     Running   0          23s   10.48.1.26   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-54664b6fc8-jtqcd   1/1     Running   0          23s   10.48.1.23   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-54664b6fc8-x8tkg   1/1     Running   0          23s   10.48.1.24   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>

新たにラベルを付け替えたnodeにPodが再配置された。

RemoveDeplicates をやってみる

このポリシーを使用して、各nodeに1台ずつPodが配置されるようにしていく。

実行前のPodの一覧

 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-77cvb   1/1     Running   0          32s   10.48.2.27   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-7sdxt   1/1     Running   0          33s   10.48.0.19   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-8dspm   1/1     Running   0          35s   10.48.0.18   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-qk5hp   1/1     Running   0          35s   10.48.2.26   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-rxqm8   1/1     Running   0          32s   10.48.0.20   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-vgw69   1/1     Running   0          35s   10.48.2.25   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>

gke-wakashiyo-default-pool-8b0ec5ba-x29pには1台も配置されていない。。。

以下のようにconfigMapを設定し、適用した

configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "RemoveDuplicates":
        enabled: true

そして、jobを実行した結果、Podがどのnodeにも1台は配置されるようになった。

kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE     IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-6bs4z   1/1     Running   0          36s     10.48.1.31   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-77cvb   1/1     Running   0          3m15s   10.48.2.27   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-7bth7   1/1     Running   0          36s     10.48.1.30   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-7sdxt   1/1     Running   0          3m16s   10.48.0.19   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-ddt28   1/1     Running   0          36s     10.48.1.28   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-jnpgz   1/1     Running   0          36s     10.48.1.29   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>

ドキュメントを読んだ感じではDeploymentなどに紐づくPodが各nodeに1台だけであるみたいに読み取ったから、Replica数が多い場合はどうなるのかと思ったが、今回動かした感じでは最低1台は配置するみたいな解釈でも良さそうな気がしている。。。

追記

最初にLowNodeUtilizationを確認した際のconfigMapは、RemoveDeplicatesとLowNodeUtilizationがpolicyに含まれていたため、正直どっちが動いていい感じにスケジューリングされたのかわからなかったと後から気づいた。

そのため、先ほどのRemoveDeplicates実行後の環境を使用してLowNodeUtilizationが動いていることをもう一度確認してみたい。

再度実行前のnodeの状況を確認してみた。

gke-wakashiyo-default-pool-8b0ec5ba-654k

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-654k
Non-terminated Pods:         (5 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                kube-dns-79868f54c5-jvwg8                              260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)     169m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-654k    100m (10%)    0 (0%)      0 (0%)           0 (0%)         169m
  kube-system                l7-default-backend-fd59995cd-jfvwb                     10m (1%)      10m (1%)    20Mi (0%)        20Mi (0%)      169m
  kube-system                prometheus-to-sd-d6vmv                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      169m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-77cvb                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m52s
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests    Limits
  --------                   --------    ------
  cpu                        471m (50%)  113m (12%)
  memory                     225Mi (8%)  310Mi (11%)
  ephemeral-storage          0 (0%)      0 (0%)
  attachable-volumes-gce-pd  0           0

gke-wakashiyo-default-pool-8b0ec5ba-2w40

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-2w40
Non-terminated Pods:         (5 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                kube-dns-79868f54c5-7cc5w                              260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)     170m
  kube-system                kube-dns-autoscaler-bb58c6784-8ms5s                    20m (2%)      0 (0%)      10Mi (0%)        0 (0%)         170m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-2w40    100m (10%)    0 (0%)      0 (0%)           0 (0%)         170m
  kube-system                prometheus-to-sd-qxg9g                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      170m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-7sdxt                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     7m11s
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests    Limits
  --------                   --------    ------
  cpu                        481m (51%)  103m (10%)
  memory                     215Mi (8%)  290Mi (10%)
  ephemeral-storage          0 (0%)      0 (0%)
  attachable-volumes-gce-pd  0           0

gke-wakashiyo-default-pool-8b0ec5ba-x29p

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-x29p
Non-terminated Pods:         (8 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                heapster-86f6474897-stbl9                              63m (6%)      63m (6%)    215840Ki (7%)    215840Ki (7%)  171m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-x29p    100m (10%)    0 (0%)      0 (0%)           0 (0%)         171m
  kube-system                metrics-server-v0.3.1-57c75779f-xg59s                  48m (5%)      143m (15%)  105Mi (3%)       355Mi (13%)    171m
  kube-system                prometheus-to-sd-wmcsp                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      171m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-6bs4z                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m36s
  wakashiyo                  wakashiyo-deployment-75b89bd54b-7bth7                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m36s
  wakashiyo                  wakashiyo-deployment-75b89bd54b-ddt28                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m36s
  wakashiyo                  wakashiyo-deployment-75b89bd54b-jnpgz                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m36s
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests        Limits
  --------                   --------        ------
  cpu                        612m (65%)      609m (64%)
  memory                     651040Ki (24%)  1009440Ki (37%)
  ephemeral-storage          0 (0%)          0 (0%)
  attachable-volumes-gce-pd  0               0
node cpu memory pod数
gke-wakashiyo-default-pool-8b0ec5ba-x29p 612m (65%) 651040Ki (24%) 8
kube-system: 4
wakashiyo: 4
gke-wakashiyo-default-pool-8b0ec5ba-2w40 481m (51%) 215Mi (8%) 5
kube-system: 4
wakashiyo: 1
gke-wakashiyo-default-pool-8b0ec5ba-654k 471m (50%) 225Mi (8%) 5
kube-system: 4
wakashiyo: 1

gke-wakashiyo-default-pool-8b0ec5ba-x29pに偏っているPodをgke-wakashiyo-default-pool-8b0ec5ba-2w40とgke-wakashiyo-default-pool-8b0ec5ba-654kにスケジューリングしたいので、以下のようなconfigMapを適用した

configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "LowNodeUtilization":
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              "cpu" : 60
              "memory": 20
              "pods": 6
            targetThresholds:
              "cpu" : 60
              "memory": 30
              "pods": 7

jobを実行した結果以下のようになった。

 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-77cvb   1/1     Running   0          14m   10.48.2.27   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-7bth7   1/1     Running   0          11m   10.48.1.30   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-7sdxt   1/1     Running   0          14m   10.48.0.19   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-ddt28   1/1     Running   0          11m   10.48.1.28   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-jnpgz   1/1     Running   0          11m   10.48.1.29   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-zhxj9   1/1     Running   0          30s   10.48.2.28   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>

理想的には2台ずつ配置されて欲しかったが、うまくいかなかった。

なぜ上手くいかなかったのか、gke-wakashiyo-default-pool-8b0ec5ba-x29pの情報を確認してみた。

gke-wakashiyo-default-pool-8b0ec5ba-x29p

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-x29p
Non-terminated Pods:         (7 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                heapster-86f6474897-stbl9                              63m (6%)      63m (6%)    215840Ki (7%)    215840Ki (7%)  179m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-x29p    100m (10%)    0 (0%)      0 (0%)           0 (0%)         3h
  kube-system                metrics-server-v0.3.1-57c75779f-xg59s                  48m (5%)      143m (15%)  105Mi (3%)       355Mi (13%)    179m
  kube-system                prometheus-to-sd-wmcsp                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      3h
  wakashiyo                  wakashiyo-deployment-75b89bd54b-7bth7                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     14m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-ddt28                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     14m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-jnpgz                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     14m
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests        Limits
  --------                   --------        ------
  cpu                        512m (54%)      509m (54%)
  memory                     574240Ki (21%)  907040Ki (33%)
  ephemeral-storage          0 (0%)          0 (0%)
  attachable-volumes-gce-pd  0               0

gke-wakashiyo-default-pool-8b0ec5ba-654k

ProviderID:                  gce://wakashiyo-playground/asia-northeast1-a/gke-wakashiyo-default-pool-8b0ec5ba-654k
Non-terminated Pods:         (6 in total)
  Namespace                  Name                                                   CPU Requests  CPU Limits  Memory Requests  Memory Limits  AGE
  ---------                  ----                                                   ------------  ----------  ---------------  -------------  ---
  kube-system                kube-dns-79868f54c5-jvwg8                              260m (27%)    0 (0%)      110Mi (4%)       170Mi (6%)     3h2m
  kube-system                kube-proxy-gke-wakashiyo-default-pool-8b0ec5ba-654k    100m (10%)    0 (0%)      0 (0%)           0 (0%)         3h2m
  kube-system                l7-default-backend-fd59995cd-jfvwb                     10m (1%)      10m (1%)    20Mi (0%)        20Mi (0%)      3h2m
  kube-system                prometheus-to-sd-d6vmv                                 1m (0%)       3m (0%)     20Mi (0%)        20Mi (0%)      3h2m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-77cvb                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     19m
  wakashiyo                  wakashiyo-deployment-75b89bd54b-zhxj9                  100m (10%)    100m (10%)  75Mi (2%)        100Mi (3%)     5m10s
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource                   Requests     Limits
  --------                   --------     ------
  cpu                        571m (60%)   213m (22%)
  memory                     300Mi (11%)  410Mi (15%)
  ephemeral-storage          0 (0%)       0 (0%)
  attachable-volumes-gce-pd  0            0

1台Podが削除された時点で、thresholdsとtargetThresholdsのそれぞれに対象となるnodeがなくなってしまったのかもしれない。。。

(gke-wakashiyo-default-pool-8b0ec5ba-2w40の情報を貼り忘れてしまったので、なんとも言えない感じになってしまいました。ご了承ください。。)

改めてconfigMapを設定した。

configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "LowNodeUtilization":
        enabled: true
        params:
          nodeResourceUtilizationThresholds:
            thresholds:
              "cpu" : 60
              "memory": 20
              "pods": 5
            targetThresholds:
              "cpu" : 70
              "memory": 20
              "pods": 7

jobを回した結果、均等に配置することができた。

 kubectl get pods -n wakashiyo -o wide
NAME                                    READY   STATUS    RESTARTS   AGE   IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-deployment-75b89bd54b-5cph9   1/1     Running   0          29s   10.48.0.26   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-7sdxt   1/1     Running   0          27m   10.48.0.19   gke-wakashiyo-default-pool-8b0ec5ba-2w40   <none>           <none>
wakashiyo-deployment-75b89bd54b-gjmn9   1/1     Running   0          8m    10.48.2.29   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>
wakashiyo-deployment-75b89bd54b-jnpgz   1/1     Running   0          25m   10.48.1.29   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-sb5dn   1/1     Running   0          8m    10.48.1.32   gke-wakashiyo-default-pool-8b0ec5ba-x29p   <none>           <none>
wakashiyo-deployment-75b89bd54b-zhxj9   1/1     Running   0          13m   10.48.2.28   gke-wakashiyo-default-pool-8b0ec5ba-654k   <none>           <none>

kubernetesの個人的メモ(オートスケールとdescheduler)

色々といじってたら、繋がったので、書いてみる。

オートスケールの前に

リソースの制限

kubernetesにはコンテナ単位にリソースの制限を行うことができる。

コンテナ、ノードが適切にパフォーマンスを発揮するためにも必要なことである。

基本的にはCPUとメモリに制限をかけることができる。

CPUについては、1vCPUを1000millicores単位で指定する。 メモリについては、1Gi を 1024Mi単位で指定する。

構成ファイル例

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  namespace: wakashiyo
  name: wakashiyo-dev-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.12
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: 1024Mi
              cpu: 500m
            limits:
              memory: 2048Mi
              cpu: 1000m

resources.requestsとresources.limitsの2種類が設定されている。

requestsは、リソース使用量の下限で、

limitsは、上限を指定している。

Podのスケジューリングは、ノードにrequestsで指定したリソースが残っているとスケジューリングされ、 残っていないとスケジューリングされない。

例えば、ノードAの割り当て可能なCPUが940mだった場合、

kube-systemの合計のrequestsが300mになっている。

これからデプロイしたいPodのrequestsのCPUリソースを100mにしているとする。(レプリカ数は、8にする)

この場合、6台までは割り当てられることができるが、残り割り当て可能なCPUリソースは40m しか残っていないため、

7台目以降はスケジューリングされない。

requestsの値を基準にリソースが残っているか判断しスケジューリングされるため、limitsの方で指定したリソースが残っていなくてもスケジューリングされる。

コンテナのリソース使用量がlimitsを超えた場合どうなってしまうのか?

limitに設定したメモリを超える構成ファイルを用意して、デプロイしてみた

apiVersion: v1
kind: Pod
metadata:
  name: memory-over-pod
  namespace: wakashiyo
spec:
  containers:
    - name: memory-over-pod
      image: polinux/stress
      resources:
        requests:
          memory: "50Mi"
        limits:
          memory: "100Mi"
      command: ["stress"]
      args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

limitsのメモリを100Miにして250のメモリを確保する用コマンドを実行している。

memory-over-pod   0/1     OOMKilled   1          10s
memory-over-pod   0/1     CrashLoopBackOff   1          11s

結果、上記のように強制終了し、再作成の繰り返しとなった。

limitsの値を実際に超えてしまうと、強制的に終了され、再作成のスケジューリングが実行されることがわかった。

CPUの場合、どうなるのかも試してみた。

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  namespace: wakashiyo
  name: cpu-over-pods
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.12
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: 50Mi
              cpu: 5m
            limits:
              memory: 100Mi
              cpu: 10m

デプロイ後に、apach bench で負荷をかけてみた。(ingress , nodeportもデプロイしている)

NAME                             READY   STATUS    RESTARTS   AGE
cpu-over-pods-5579fd4fdd-v97zr   1/1     Running   0          3m16s

CPUのときも強制終了するかもしれないと期待したが、何も変化がなかった。。。(もしかしたらやり方が悪かった?)

あらかじめ、ノードより大きいリソースを割り当てられる設定を行うとスケジューリングされないとわかったが、

実際に使用している間にlimitsを超えたりするとどうなるのかがわかった。

オートスケールしてみる(hpaを使ってみる)

前述のリソースの制限にはLimitRangeやCluster Auto Scaler、Resource Quotaなどもあるが今回は割愛する。

hpa(Horizontal Pod AutoScaler)はCPU負荷に応じて、Deployment, ReplicaSet, Replication Controllerのレプリカ数を自動的にスケールしてくれるとても便利なリソースだ。

内部的には30秒に1回オートスケールするべきかのチェックが行われるらしい。

その際に実際に使用しているCPU使用率と

どれくらのCPU使用率に抑えたいか(targetAverageUtilization)

この2つの値を用いて必要なレプリカ数を算出し、スケールアウトしたり、スケールインしてくれる。

算出する計算式は、ドキュメントに記載されている。

Horizontal Pod Autoscaler - Kubernetes

実際にデプロイしてみた

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  namespace: wakashiyo
  name: wakashiyo-dev-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: wakashiyo-dev-deployment
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        targetAverageUtilization: 30

hpaを適用するdeployment

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  namespace: wakashiyo
  name: wakashiyo-dev-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.12
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: 50Mi
              cpu: 5m
            limits:
              memory: 75Mi
              cpu: 10m

今回はスケールするのを簡単にみたいため、cpuのresourcesを小さくした。

targetAverageUtilization: 30ということで目標のCPU使用率平均の値を30%にした

これをapach benchで負荷をかけてみた

pods(負荷前)

% kubectl get pods -n wakashiyo
NAME                                        READY   STATUS    RESTARTS   AGE
wakashiyo-dev-deployment-6bdb498876-89dk2   1/1     Running   0          17m

pods(負荷後)

% kubectl get pods -n wakashiyo
NAME                                        READY   STATUS    RESTARTS   AGE
wakashiyo-dev-deployment-6bdb498876-5zpxf   1/1     Running   0          2m4s
wakashiyo-dev-deployment-6bdb498876-774dv   1/1     Running   0          3m34s
wakashiyo-dev-deployment-6bdb498876-89dk2   1/1     Running   0          17m
wakashiyo-dev-deployment-6bdb498876-c9ps5   1/1     Running   0          3m4s
wakashiyo-dev-deployment-6bdb498876-f7rl2   1/1     Running   0          3m34s
wakashiyo-dev-deployment-6bdb498876-g95xg   1/1     Running   0          3m4s
wakashiyo-dev-deployment-6bdb498876-p64wc   1/1     Running   0          2m4s
wakashiyo-dev-deployment-6bdb498876-pp9sf   1/1     Running   0          3m4s
wakashiyo-dev-deployment-6bdb498876-qqdms   1/1     Running   0          3m34s
wakashiyo-dev-deployment-6bdb498876-tjxh9   1/1     Running   0          2m4s

hpa

 % kubectl get horizontalpodautoscalers.autoscaling -n wakashiyo --watch
NAME                REFERENCE                             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   20%/30%   1         10        1          10m
NAME                REFERENCE                             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   0%/30%    1         10        1          10m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   100%/30%   1         10        1          11m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   100%/30%   1         10        4          11m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   200%/30%   1         10        4          11m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   200%/30%   1         10        7          12m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   26%/30%    1         10        7          12m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   46%/30%    1         10        7          12m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   46%/30%    1         10        10         13m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   48%/30%    1         10        10         13m
wakashiyo-dev-hpa   Deployment/wakashiyo-dev-deployment   20%/30%    1         10        10         13m

上記のように、CPU使用率の目標値を上回ってから、 podがオートスケールされていき、

目標値に近づいてきた。実際に正確な目標値におさまることは確認できなかったが、CPU使用率に応じて自動でスケールされることがわかった。

一つ気になること

hpaを使用することでオートスケールするのが確認できたが、実際ノードに割り振られているpod数を確認すると以下のようになっていた

% kubectl get pods -o wide -n wakashiyo
NAME                                        READY   STATUS    RESTARTS   AGE     IP           NODE                                       NOMINATED NODE   READINESS GATES
wakashiyo-dev-deployment-6bdb498876-5zpxf   1/1     Running   0          5m35s   10.48.2.11   gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-774dv   1/1     Running   0          7m5s    10.48.2.6    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-89dk2   1/1     Running   0          20m     10.48.2.4    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-c9ps5   1/1     Running   0          6m35s   10.48.2.7    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-f7rl2   1/1     Running   0          7m5s    10.48.1.4    gke-wakashiyo-default-pool-8b127224-4pbq   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-g95xg   1/1     Running   0          6m35s   10.48.2.9    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-p64wc   1/1     Running   0          5m35s   10.48.2.10   gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-pp9sf   1/1     Running   0          6m35s   10.48.2.8    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-qqdms   1/1     Running   0          7m5s    10.48.2.5    gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
wakashiyo-dev-deployment-6bdb498876-tjxh9   1/1     Running   0          5m35s   10.48.2.12   gke-wakashiyo-default-pool-8b127224-zx8q   <none>           <none>
ノード Pod数
A 9
B 1
C 0

1つのノードでは全くPodが割り当てられていないことがわかった。

これで9台割り当てられているリソースで枯渇が起きたら、効率的にクラスタを利用できていないし、パフォーマンスも発揮できない。

できるのならCのノードに何台かPodを割り当てたい。

deschedulerを使用する

deschedulerとは?

GitHub - kubernetes-sigs/descheduler: Descheduler for Kubernetes

一部のノードが使用されていなかったり、使用されすぎたりしてしまっているとき、

ノードが新たに追加されたとき、

ラベルがノードに追加されたり、削除されたりして、今まで適用されていたNode Affinityの条件を満たさなくなってしまった

ノードに障害が起こり、他のノードに移動した

など、クラスタの状態が動的に変化するにもかかわらず、kube-schedulerは配置後の見直しは行わないので、

このdeschedulerによって意図しないPodのスケジューリングを見つけ、再配置をしてくれる。というこれまた便利なものだ。

実際に、スケジューリング自体はkube-systemが行い、deschedulerが行うのは、移動可能なPodを見つけ排除するところまでを行う。

そのため、スケジューリング自体はデフォルトのkube-systemに依存している。

deschedulerは5つのポリシーと戦略から構成される。(RemovePodsViolatingNodeTaints こちらについては今回省略する。)

これらは全てデフォルトではenabledになっているとのこと。

RemoveDuplicates

これは、Deployment, ReplicaSet, ReplicationController, Jobによって動いているPodが同じノード内で1台だけであることを示す。

Podが重複していたら、クラスター内のPodの展開を改善するために排除される。

例としては、あるノードが障害が落ちてしまい、他のノードに移動したとする。 その後、障害していたノードが復活した際に、復活したノードに再配置されるように、障害時に移動して、重複状態のPodを削除する。

LowNodeUtilization

これは、使用されていないノードを見つけ、使用されすぎているノードからPodを削除する。

その上で、使用されていないノードに削除されたPodがスケジュールされるようにする。

nodeResourceUtilizationThresholds というパラメータを使用して、設定を行う。

nodeResourceUtilizationThresholds.thresholds は、

使用されていないノードの条件を設定する。cpu, memory, Pod数から決める。ここで設定した値を下回ると(3つ全て)使用されていないノードとみなされ、他のノードで削除されたPodがあった際に、スケジュール対象ノードになる。

nodeResourceUtilizationThresholds.targetThresholds は、

使用されすぎているノードの条件を設定する。cpu, memory, Pod数から決める。ここで設定した値を上回ると、そのノードに割り当てられているPodが削除される。

thresholdsとtargetThresholdsの間にあるノードはどちらの対象のノードにならない。

もう一つ numberOfNodes というパラメータも存在する。

これは、使用されていないノード数が numberOfNodes で設定した値を超えるとこの LowNodeUtilizationのポリシーが有効になるというもの。

これはデフォルトでは、0になっていて、

大規模なクラスタで運用していて、何台かのノードが、しばしば使用されなさすぎな状態になるとき、あるいは短期間使用されなさすぎな状態になるということがあると役立つらしい。

RemovePodsViolatingInterPodAntiAffinity

これは、anti affinityのルールに違反しているPodをそのノードから削除してくれるもの。

例えば、Pod A, B , C が同じノードで動いているとする。 後から、PodB, PodC にPodAとは同じノードで動いてはいけないというanti affinityルールを付与したとする。 このとき、PodAが削除される。

RemovePodsViolatingNodeAffinity

これは、node affinityのルールに違反しているPodをそのノードから削除してくれるもの。

例えば、ノードAではPodAがスケジュールされるために必要なnode affinityを満たしていて、PodAがノードAで動いていたとする。 時が経って、ノードAではその条件を満たさなくなり、ノードBで条件を満たす状態になったとする。 この時、PodAが削除される。

使ってみる

使用方法は、

GitHub - kubernetes-sigs/descheduler: Descheduler for Kubernetes

README にも記載している。

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: descheduler-cluster-role
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "watch", "list", "delete"]
  - apiGroups: [""]
    resources: ["pods/eviction"]
    verbs: ["create"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: descheduler-sa
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: descheduler-cluster-role-binding
subjects:
  - kind: ServiceAccount
    name: descheduler-sa
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: descheduler-cluster-role
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
data:
  policy.yaml: |
    apiVersion: "descheduler/v1alpha1"
    kind: "DeschedulerPolicy"
    strategies:
      "RemoveDuplicates":
         enabled: true
      "RemovePodsViolatingInterPodAntiAffinity":
         enabled: true
      "LowNodeUtilization":
        enabled: true
          params:
            nodeResourceUtilizationThresholds:
              thresholds:
                "cpu" : 10
                "memory": 10
                "pods": 0
              targetThresholds:
                "cpu" : 25
                "memory": 25
                "pods": 5
kind: ConfigMap
metadata:
  name: descheduler-policy-configmap
  namespace: kube-system

job

apiVersion: batch/v1
kind: Job
metadata:
  name: descheduler-job
  namespace: kube-system
spec:
  parallelism: 1
  completions: 1
  template:
    metadata:
      name: descheduler-pod
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ""
    spec:
      containers:
        - name: descheduler
          image: gcr.io/wakashiyo-playground/descheduler:0.0.1
          volumeMounts:
            - mountPath: /policy-dir
              name: policy-volume
          command:
            [
              "/bin/descheduler",
              "--policy-config-file",
              "/policy-dir/policy.yaml",
              "-v",
              "1",
            ]
      restartPolicy: "Never"
      serviceAccountName: descheduler-sa
      volumes:
        - name: policy-volume
          configMap:
            name: descheduler-policy-configmap

これを適用したが、Podがうまく割り振られたかというと上手くいかなかった。

nodeのリソースをそれぞれ確認すると、

1台も割り当てられていないPodというのは、既にkube-systemで結構リソースが使用されていた。

だから、 LowNodeUtilization.nodeResourceUtilizationThresholds の設定がよくなかったのかもしれない。

元々、そのリソース使用量だったから、偏ったPodの配置をkube-schedulerは行ったのかもしれない。。。(それだったら、kube-systemは適切な配置を行ったとも言うことができる。。。)

この辺よくわからなくなってきたので、今日はここまでにして、もっと調査したりして、理解を深めていきたいと思う。

kubernetesの個人的メモ(Discovery&LBリソース)

エントリーのリソースの違いがあやふやになってしまったので、整理した。

Discovery&LBリソース

デプロイされているPod(コンテナ)に外部からアクセスできるようにするリソース。

エンドポイントの提供などをしれくれる。

大きく、SerivceとIngress に分かれる。

両者の違いは、SerivceリソースはL4のロードバランシングを提供するのに対して、 IngressリソースはL7のロードバランシングまで提供してくれる。

つまり、Ingressはパスルーティングを実現できたりする。

Serviceリソース

細かくは7つに分かれる。

  • ClusterIP
  • ExternalIP
  • NodePort
  • LoadBalancer
  • Headless(None)
  • ExternalName
  • None-Selector

下から3つについては、今回は省略する。

Cluster IP

クラスタ内部ネットワークのみで疎通できる仮装IPが割り当てられる。 指定した Podに対してロードバランシングを行う。

例えば、クラスタで動くPodAから 3台動いているPodBにアクセスしたいとき、PodAからはCluster IPで割り当てられたIPにアクセスすると、 いい感じにロードバランシングしてくれて、PodBにアクセスすることができる。

構成ファイル例

kind: Service
apiVersion: v1
metadata:
  namespace: wakashiyo-development
  name: wakashiyo-dev-clusterip
spec:
  selector:
    app: nginx
  type: ClusterIP
  ports:
  - name: "http-port"
    port:  8080
    targetPort:  80

selectorにはdeployment等でデプロイしているPodのラベルを設定している。

portでCluster IPが受け付けるポート番号を指定し、

targetPort で転送先のコンテナのポート番号を指定する。

External IP

前述のCluster IPは内部疎通だけだったが、これはクラスタ外からも疎通できる。

指定したクラスタIPアドレスを指定することで、そのNodeへのアクセスを指定したコンテナにロードバランシングする。

ロードバランシングされるのは、指定したIPのNodeにあるコンテナだけでなく、デプロイされているコンテナ全てにロードバランシングされる。

kind: Service
apiVersion: v1
metadata:
  namespace: wakashiyo-development
  name: wakashiyo-dev-clusterip
spec:
  selector:
    app: nginx
  type: ClusterIP
  # kubectl get nodes -o custom-columns="NAME:{metadata.name},IP:{status.addresses[].address}"
  externalIPs:
    - 10.146.0.16
  ports:
  - name: "http-port"
    port:  8080
    targetPort:  80
# It did not work

先ほどのCluster IPと異なるのは、externalIPs という項目が増えている。 これは、GKEであれば、GCEインスタンスの外部IPではなく、OSなどから確認できる内部のIPを使用することができるらしい。

NodePort

こちらは、external IPが外部疎通性のあるノードを指定したのに対して、 こちらは、全てのNodeの外部疎通性を提供する。

(厳密には、0.0.0.0:portnumberでListenし、Node全てのIPにBindする形でロードバランシングされる)

なので、3台のノードが存在した場合、いずれかのIPアドレスにアクセスすると、NodePortからロードバランシングされ、 コンテナにアクセスができる。

構成ファイル例

kind: Service
apiVersion: v1
metadata:
  namespace: wakashiyo-development
  name: wakashiyo-dev-nodeport
spec:
  selector:
    app: nginx
  type: NodePort
  ports:
  - name: http-port
    port: 8080
    targetPort: 80
    nodePort: 30080
# It did not work when use not ingress

ports.nodePort という項目が増えている。

これは、NodePort -> ClusterIP -> Pod(container)

のようにクラスタ内部ではClustert IP のようにトラフィックが流れる。

port はCluster IPで受け付けるポート番号に対して、nodePortはクラスタのnodeで受け付けるポート番号になる。

LoadBalancer

今までは外部疎通性を提供するのに、node自身のIPを外部疎通できるように設定していたのに対して、

これは完全にクラスタ外に作成されるイメージになる。

その名の通り、クラスタ外のロードバランサが指定したコンテナに対してロードバランシングしてくれる。(L4レベルで)

とあるNodeが障害の場合、そのNodeへのトラフィックは流れないようにしてくれる。

GKEだと内部的にはロードバランサのサービスが使用されているらしい。

構成ファイル例

kind: Service
apiVersion: v1
metadata:
  namespace: wakashiyo-development
  name: wakashiyo-dev-lb
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - name: "http-port"
      port: 8080
      targetPort: 80
      nodePort: 30082
  # firewall rule (default: 0.0.0.0/0)
  loadBalancerSourceRanges:
    - 10.0.0.0/8

こちらのnodePortはロードバランサで受け付けるポート番号のことを指す。

NodePortと同様にクラスタ内部ではCluster IPが存在して、Cluster IP経由でコンテナまで疎通することができる。

loadBalancerSourceRangesを使用することで、指定したIPレンジからの送信しか受け付けないようにすることができる。

冷やっとしたのは、GKEの場合、Load Balancerを作成した時点で、GCPのロードバランサが作成され、静的IPが払い出される。

これは、クラスタを削除したりしても消えないので注意した方がいい。

Ingress

LoadBalancerと違って、L7のロードバランシングまで提供してくれる。

こちらも内部的には、GCPのロードバランサが使用されている。

構成ファイル例

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: wakashiyo-development
  name: w-d-ig
spec:
  rules:
    - http:
        paths:
          - path: /path1/*
            backend:
                serviceName: w-d-np1
                servicePort: 8080
          - path: /path2/*
            backend:
                serviceName: w-d-np2
                servicePort: 8080
          - path: /path3/*
            backend:
                serviceName: w-d-np3
                servicePort: 8080

backendには転送したいserviceを指定する。

ここでは、3つのNodePortをあらかじめ用意してからingressをデプロイしている。

というのも、Ingressはserviceをバックエンドとして転送する仕組みになっているため、

Serivceをbackendとして指定している。

ingressを通して公開されるコンテナはロードバランサからのヘルスチェックに応答できるようにしなければならない。

ヘルスチェックで何が行われるかというと、ルートパスへのGETリクエストが来るので、そのリクエストに対して200を返すようにしなければならない。

このことを最初知らずに、いつまで経ってもデプロイが失敗して1時間ほど溶かしてしまった。

Ingress を使用した HTTP(S) 負荷分散  |  Kubernetes Engine のドキュメント  |  Google Cloud

「入門 監視」を読んだ

こちら。

www.oreilly.co.jp

監視に興味を持ち始めていましたので、読みました。

読んでみて

監視の基本の原理原則的なものを幅広く抑えられていました。

私には、監視の「か」の字をわかっているかと言われたら危うい感じだったので、初歩から標準のことを知ることができたと思っています。

なので、ある特定の部分の監視のことで。。。みたいな具体的な悩みを持って、それを解決するためにこの本を読もうとしていると少し検討違いになってしまうと思いました。

また、ある程度仕事の中で監視をやってきて経験を積まれている方からしても、そこまで奥深いことが書いているわけではないので、読んだら「なんだ当たり前なことばかりじゃねーか」みたいになってしまうのかなと思いました。(基本がとても整理されている本だと思うので、復習みたいにはなるのかもしれない)

どんな構成だったのか

目次はいろんなwebページにも書いてありますが、

  • 監視の原則
  • 監視の戦略

と大きく二つに分かれていて

監視の原則では

など、について書かれていました。

こちらは、「今から目の前のシステムの監視の設定、実装をしていくぞ!」というわけではなく、

「監視とはなんなのか?」

「今までの監視の悪いところをあげ、こうした方がいい的な」

など、監視の実装を始める前の基本的な考え方をアンチパターンデザインパターンから説明されていました。

監視の戦略では、

  • ビジネスKPI
  • フロンエンド
  • アプリケーション
  • サーバー
  • ネットワーク
  • セキュリティ

など、各領域ごとに監視のパターンが書かれていました。

色々なwebサービスがある中で、フロントエンド監視も見逃してはいけないものと取り上げられていました。

印象に残っていること(というよりも覚えていること)

読んだのにもう忘れている。。。

結構当たり前なことですが、印象に残っていたり、まだ忘れていないことを羅列していきます。

whatから始めない

アンチパターンで述べられていました。

まず、ツール依存が結構多いらしいということ。

「このツールを使えば、OKでしょ。使おう」的なwhatが最初から来てしまい、結局導入した監視によって何も効率的な恩恵を受けられないというもの。

ツールを検証目的で色々試すのはOKだと思いますが、あの会社もあの会社もいいと言っていたから導入するのではなく、目的やワークフローに合っているかの見極めをちゃんとした上で導入なんだと教えられました。

自動化しましょう

このへんのアンチパターンは前職時代と結構紐付きながら読んでいました。。。

まさに「このコマンドを実行して、次にこのコマンドを。。。」みたいな手順書が溢れていましたので、

そういうのは自動化してプロダクトに集中しましょう。

導入は時間をかけて(かけすぎはよくない)、導入後は自動化してコストかけない

こんな感じにできると良さそうですね。

過去の値も必要

KPIに関することをやっていたので、少し引っかかりました。

今はある時点でのデータしか取って来ていないので、過去の値と時系列に見れるようにしたら、周期性が出て分析できたり、、、

この本でそこまで強く言ってないですが、時系列データ、過去のデータを蓄積してみないと原因もわからないと言ってました。

作るのではなく買う

これは企業の規模やフェーズに依存する話でした。

  • スタートアップみたいに規模の小さいところだったら、監視サービスを簡単に導入してプロダクトに集中すべき
  • 大企業になったら、独自の課題が出てしまい、監視サービスでは解決できない。そうしたら、自作する

企業が大きくなるにつれて監視への課題は変わってくるので、そうなったら、自作した方がいい(するしかない)ですが、そうでもなければ、わざわざコストを払って自作する必要ないので、その辺の塩梅はちゃんと判断しましょうということでした。

ついでに、監視の仕組みは1度作って終わりではなく、絶えず変化していくので、その都度、会話・判断・実装が必要です。

監視は単一の問題ではない

非常に大きな問題の塊であるため、1つのツールで解決できるものではないと述べられていました。

書籍では、それぞれ専門的なツールが増えるのは構わない的な形で書かれていました。(使用するものが多くなって複雑になってしまうことに対して恐れることが多いようです) 増えることがいいことではなく、1つの統合ツールで解決できない問題を解決するのであれば、上記のようにツールが増えても構わないということだそうです。

それぞれのツールが疎結合になっていれば、あるツールだけワークフローに合わなくなったりしたら、それだけ削除して新しいものに置き換えればいいと、組み合わせ可能な監視プラットフォームの構築を推奨していました

組み合わせ可能な監視を構成する要素

  • データ収集
  • データストレージ
  • 可視化
  • 分析とレポート
  • アラート

優先すべきは外側から

どこから監視をしていくのかという問いです。

OSのメトリクスなどももちろん有用ですが、まずはユーザーに近いところから着手すべきだと(ちゃんと動いているのかなど)

本書ではOSのメトリクスがちょいちょいと批判されていました。

それが不必要なものではということを言っているわけではなく、 例えば、CPU使用率が急上昇したとしても、ユーザーが使用するのに影響を与えているわけではないのであれば、アラートを上げて動かなくてもいいということでした。

この本の場合、どこを重視するかという点においてユーザーに関わる部分、つまりアプリケーションが動いているかという部分が重要になっているため、OSのメトリクスは上記の重要な部分とは関連性が薄いということで優先度が低くなるというものでした。

なので、この辺は企業や状況によって変わるような気がします。

ちなみにOSのメトリクスに関する話しは付録Cで @songmuさんのお酒に例えた話しがわかりやすく、「直接の原因であり、コントロール可能な数値を監視すべき」という言葉でとても腑に落ちました。

外側からという考えに従うと、個人的にはユーザーに関わる部分、ビジネスに関わる部分に対しては優先的に動けるようにしたいと思いました。

フロントエンドも重要

昔からの慣習なのか、この辺の監視の意識が薄いようです。

ただ、ページが表示されるまでの時間がかかってしまうとユーザーが離れてしまうという実績は様々な企業からたくさん出ているので、この領域の監視は見逃せないものだと思い知らされました。

個人的にはフロントエンドの開発をしたりはしているのですが、この辺に重きを置いて開発したりしたことはなかったので、今度休日に少し遊んでみようと思いました。

さいごに

個人的に、ネットワーク監視は、ほぼ経験したことがなかったので、少し頭に入りづらいことが多かったです。

今度こちらも休日に遊んでみようと思いました。