記録。

めも。

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さんのお酒に例えた話しがわかりやすく、「直接の原因であり、コントロール可能な数値を監視すべき」という言葉でとても腑に落ちました。

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

フロントエンドも重要

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

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

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

さいごに

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

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

realmの様々な初期化を知らなかった。。。

realmはちょこちょこと使ったことはあるんですが、
あれ?テストとかどうすんだ??となったので、ドキュメントを読み直したりしました。
realmがどういう仕組みなのか知っていなかったというかちゃんと読むべきところを読んでいなかったと気づいたので反省です。。。

realmはどこに作られるのか?

これです。.realmファイルが作成されてそこにデータの読み書きをしにいくということまでは知っていました。
しかし、

  • どのディレクトリに作成されるのか?
  • default.realmしか作成できないんじゃないか?

ということを思っていました。

ドキュメントを読み直したら書いてありました。
公式ドキュメントのTesting and Debuggingにしっかり書いてありました。(個人的に知りたいことが全て書いてあったので嬉しい)

どのディレクトリに作成されるのか?

let realm = try! Realm()

上記のようなよく見る形で初期化すると、
Documentsディレクトリ以下にdefault.realmというファイルが作成されます。

default.realmしか作成できないんじゃないか?

違いました。パスを指定することで、default.realmとは別の.realmファイルを作成することができます。

let documentDirPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = documentDirPath[0] + "/test.realm"
let url = URL(fileURLWithPath: path)
let realm = try! Realm(fileURL: url)

上記の場合は、Documentsディレクトリ以下にtest.realmファイルを作成しています。
これで、default.realmとは別のファイルを作成することができ、データベースを使い分けることができます。(テストできる!)

その他(インメモリDBとしての使用)

公式ドキュメントのTesting and Debuggingには、
もう一つ、テストをする方法としてインメモリDBとして使用する方法が書いてあります。

var config = Realm.Configuration.init()
config.inMemoryIdentifier = "inMemory"
let realm = try! Realm(configuration: config)

上記のように初期化することで使用できます。これでもテストできます!
※当然ですがインメモリのため、データの永続化はされません。

初期化する方法がわかったので、うまく使い分けて行きたいと思います。

参考

realm.io

qiita.com

qiita.com

www.nowsprinting.com

標準的なUIを作る 〜Card〜

サブタイトル付きなので、シリーズ的にやっていきたいと思っています。

CardのUIは以前作りたいなと思ったのですが、調べてもなかなか見つからなく、調べることに時間を割いてしまった思い出があるので、ブログに書くことにしました。

作るもの

f:id:jksdaba:20190323165924g:plain
cardui

最近のフロントエンドのライブラリなんかには、標準のコンポーネントとして提供されています。

material-ui.com

vuetifyjs.com

ということで、こんな感じのものをswiftで実装していきたいと思います。

環境

Xcode 10.1
Swift 4.2

主な構成

  • UIViewController
  • UICollectionView
  • UICollectionViewCell

後述しますが、UIViewControllerは今回はxibで作りました。 理由としては、個人的な感想ベースですが、

  • 個人的に使い回ししやすい
  • インスタンス生成が楽
  • カスタムのイニシャライザを定義することができる

ということ感じで、storyboardより使いやすいと感じているため、最近よくxibでViewControllerを作っています。

Cardを作成する上で今回は、UICollectionView, UICollectionViewCellを使いました。 試していないですが、同じ機構であるUITableView, UITableViewCellでも作れると思います。

つくっていく

xibでViewControllerを作る

f:id:jksdaba:20190323171437p:plain

UICollectionViewを貼り付けるだけです。

レイアウトは、
top, leading, trailing, bottom 全てsafeareaに合わせています。

cellの作成

今回は、画像を表示させるだけのcardをつくっています

f:id:jksdaba:20190323171741p:plain

UIImageViewを貼り付けるだけです。
レイアウトは、
top, leading, trailingを大元のviewに合わせて、アスペクト比を4:3にしています。

cellの設定をする

cardっぽい立体的な見た目にしていきます。

masktoboundsとshdowOffset, shadowRadius, shadowOpacityを使うことで立体的で浮いているような見た目になります。

class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var imageView: UIImageView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    func setUp(str: String) {
        imageView.image = UIImage(named: str)
        
        // UICollectionViewのcontentViewプロパティには罫線と角丸に関する設定を行う
        self.contentView.layer.masksToBounds = true
        self.contentView.layer.cornerRadius = 10.0
        
        // UICollectionViewのおおもとの部分にはドロップシャドウに関する設定を行う
        self.layer.masksToBounds = false
        self.layer.shadowOffset = CGSize(width: 2, height: 4)
        self.layer.shadowRadius = 10.0
        self.layer.shadowOpacity = 0.5
    }

}

以上。

ソースはgithubにあげてあります。

StandardUI/CardUI at master · wakashiyo/StandardUI · GitHub