Kubernetes勉強会第1回 〜Secrets、StatefulSet、DaemonSet、API server への接続方法〜

Kubernetes in Action

Kubernetes in Action

「Kubernetes in Action」を読んで学んだ結果を社内で共有しました。 その第1回の内容です。

Secrets

  • ConfigMapと同様、Key-Value のリストだが、パスワードや認証情報等の秘密情報を格納するためのリソース。
  • Container から Secret を使うには、"環境変数"、"Volume としてマウント"の二種類がある。この使い方はConfigMapと変わらない。
  • 3つ種類がある。使い分けは以下
    • docker-registry: 認証が必要なDocker registryを使うときに使用する
    • generic: secretをローカルファイル、ディレクトリ、literal valueから生成する場合
    • tls: ingressSSL certificateに使用する場合

環境変数から使う場合

apiVersion: v1
kind: Pod
metadata:
  name: fortune-env
spec:
  containers:
  - image: luksa/fortune:env
    name: html-generator
    env:
    - name: MY_PASSWORD_SECRET
      valueFrom:
        secretKeyRef: # configMapを環境変数に使う場合はここが`configMapKeyRef`になる
          name: my-secret
          key: password

volumeとしてmountする

apiVersion: v1
kind: Pod
metadata:
  name: fortune-https
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: certs
      mountPath: /etc/nginx/certs/
  volumes:
  - name: certs
    secret:
      secretName: fortune-https
$ kc exec git-sync-demo-b9cb448f7-972rh ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token
  • Secretがbase64なのは、バイナリデータも含めることができるように。
  • Secretのサイズ上限は1M
  • private dockerレジストリからimageをpullする時の認証をsecretに登録するためには
$ kubectl create secret docker-registry mydockerhubsecret \
  --docker-username=myusername --docker-password=mypassword \
  --docker-email=my.email@provider.com

のようにしてsecret genericではなくsecret docker-registryを指定する。 podには以下のように imagePullSecrets を指定

apiVersion: v1
kind: Pod
metadata:
  name: private-pod
spec:
  imagePullSecrets:
  - name: mydockerhubsecret
  containers:
  - image: username/private:tag
    name: main

API server への接続方法

まずkubectlは中でKubernetes API ServerへREST APIリクエストを送って、Kubernetesの操作をしています。 そのため、kubectl --v=8 get podsのように--v=8オプションをつけて実行すると、どのようなREST APIが送信されているかみることができます。

$ kubectl --v=8 get node
省略...
I0321 19:31:59.047070    9111 round_trippers.go:414] GET https://192.168.99.100:8443/api/v1/nodes
I0321 19:31:59.047079    9111 round_trippers.go:421] Request Headers:
I0321 19:31:59.047086    9111 round_trippers.go:424]     Accept: application/json
I0321 19:31:59.047093    9111 round_trippers.go:424]     User-Agent: kubectl/v1.8.4 (darwin/amd64) kubernetes/9befc2b
I0321 19:31:59.060898    9111 round_trippers.go:439] Response Status: 200 OK in 13 milliseconds
I0321 19:31:59.060920    9111 round_trippers.go:442] Response Headers:
I0321 19:31:59.060926    9111 round_trippers.go:445]     Content-Type: application/json
I0321 19:31:59.060932    9111 round_trippers.go:445]     Content-Length: 3171
I0321 19:31:59.060937    9111 round_trippers.go:445]     Date: Wed, 21 Mar 2018 10:31:58 GMT
I0321 19:31:59.061025    9111 request.go:836] Response Body: {"kind":"NodeList","apiVersion":"v1","metadata": 長いので省略

Kubernetesの各pod内のコンテナからもAPI Serverを利用することができます。 参考: https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#accessing-the-api-from-a-pod

kubectl run curl --image=tutum/curl -- tail -f /dev/null kc exec -it pod名 /bin/bash でpodにログインし、

# 以下でkubernetes APIのIPとPORTがわかる。
$ env | grep KUBERNETES_SERVICE
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443

# SSL証明書を指定
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# token(認証情報)を設定
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

curl -H "Authorization: Bearer $TOKEN" https://10.96.0.1:443/api/v1/namespaces/default/pods

# kubernetesというドメインでもアクセスできる
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/services/git-sync-demo

token(認証情報)はpod外から以下でも取得できる

$ kubectl describe secret $(kubectl get secrets | grep default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t'

認証方式はいくつかある kubernetesの認証とアクセス制御を動かしてみる

認可に関してはRBAC(Roles-Based Access Control)という機能が担う。

Rubyのkubernetes API クライアントは https://github.com/abonas/kubeclient がある。

StatefulSets

https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

ステートフルなアプリケーション(例えばDB等)をサポートするためのリソース。

  • 再作成されてもpod nameやhostnameは同じ名前で生成される。
    • pod名は {name}-NというformatでNは0から始まる。つまり{name}-0, {name}-1, {name}-2 ...というように生成される。
  • 作成されるときは、次の使われていないindexを使って作成される。
  • podが起動するときは小さいindexから順に起動される。
  • またpodが削除されるときは大きいindexから順に削除される。
  • 同じPersistentVolumeClaimsを使おうとする。
  • 削除されるときは使っていたPersistentVolumeClaimsは削除されない。

例:

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: kubia
spec:
  serviceName: kubia
  replicas: 2
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia-pet
        ports:
        - name: http
          containerPort: 8080
        volumeMounts:
        - name: data
          mountPath: /var/data
  volumeClaimTemplates: # このテンプレートを使ってPersistentVolumeClaimsが作成される
  - metadata:
      name: data
    spec:
      resources:
        requests:
          storage: 1Mi
      accessModes:
      - ReadWriteOnce

参考: https://scrapbox.io/blackawa/StatefulSet%E3%81%A3%E3%81%A6%E3%81%84%E3%81%A4%E4%BD%BF%E3%81%86%E3%81%AE%EF%BC%9FPersistentVolumes%E3%81%A7%E3%81%84%E3%81%84%E3%82%93%E3%81%98%E3%82%83%E3%81%AA%E3%81%84%E3%81%AE%EF%BC%9F

DaemonSet

https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/

全てのnode、もしくは特定のnodeに必ず1つのpodを動かしたい場合にはDaemonSetを使うとよい。 たとえば、以下のケースに使える。

  • Cluster storage daemon(例えばglusterdやceph)を動かす場合
  • nodeのメトリックス収集用のpodを動かしたい場合(例えば Prometheus Node Exporter, collectd, Datadog agent, New Relic agent, or Ganglia gmond.)
  • nodeのログを収集するpodを動かしたい場合(fluentd や logstash)
  • nodeのネットワークを操作するためのpodを動かしたい場合

例:

apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
  name: ssd-monitor
spec:
  selector:
    matchLabels:
      app: ssd-monitor
  template:
    metadata:
      labels:
        app: ssd-monitor
    spec:
      nodeSelector:
        disk: ssd
      containers:
      - name: main
        image: luksa/ssd-monitor

11. Understanding Kubernetes internals

  • Kubernetes SystemコンポーネントだけがKubernetes API Serverと通信をする
  • Kubernetes API serverのみがetcdと通信をする。他のコンポーネントは直接etcdと通信しない。 クラスタの状態もAPI Serverと通して行われる
  • ectdには楽観ロックで操作するので一貫性がとれる。

11.4.3 Introducing the Container Network Interface

  • コンテナ間で簡単にネットワーク通信できるようにContainer Network Interface (CNI)というプロジェクトがスタートした。 KubernetesはいくつかのCNI pluginを使うことができる。
    • Calico
    • Flannel
    • Romana
    • Weave Net
    • And others

CNIはDaemonSetとして各Nodeにデプロイされる。

http://tech.uzabase.com/entry/2017/09/12/164756

Kubernetes in Actionの個人的なまとめ

Kubernetes in Action

Kubernetes in Action


追記 2018/07/11

以下にまとめ直したのでこちらをご参照下さい。以下のリンク先のほうが詳しいです。


最近Kubernetesを触っていて、英語の勉強がてらこの「Kubernetes in Action」を読んだ。 (600ページもあって1日1時間読むようにしてたけど2ヶ月半くらいかかった。。) Kubernetesを知るためには非常に良い本で、Kubernetesの基礎からKubernetesの内部、セキュリティ周りまで丁寧に書かれていた。とてもおすすめです。

日本語に翻訳された本が出てほしいですね。

https://www.manning.com/books/kubernetes-in-action から1章と3章が無料でダウンロードできるので興味がある人は読んでみて下さい。

ということで、忘れないように重要な点を書き残しておく。

個人的な記録なので、日本語訳や内容が間違っている可能性があるので、ご注意下さい。

1〜6

基本的なことだったので省略

7. ConfigMaps and Secrets: configuring applications

7.5 Using Secrets to pass sensitive data to containers

  • 環境変数として渡せるし、ファイルとしてmountすることもできる。
  • Secretはメモリ内に読み込まれる、ファイルには書き出されない。(volumeとしてmountするときはtmpfsに書かれる)
  • ただしectdに保存されるときは平文で保存される(https://kubernetes.io/docs/concepts/configuration/secret/)
  • By default, the default-token Secret is mounted into every container,
$ kc exec git-sync-demo-b9cb448f7-972rh ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt
namespace
token
  • Secretがbase64なのは、バイナリデータも含めることができるように。
  • Secretのサイズ上限は1M
  • private dockerレジストリからimageをpullする時の認証をsecretに登録するためには
$ kubectl create secret docker-registry mydockerhubsecret \
  --docker-username=myusername --docker-password=mypassword \
  --docker-email=my.email@provider.com

のようにしてsecret genericではなくsecret docker-registryを指定する。 podには以下のように imagePullSecrets を指定

apiVersion: v1
kind: Pod
metadata:
  name: private-pod
spec:
  imagePullSecrets:
  - name: mydockerhubsecret
  containers:
  - image: username/private:tag
    name: main

8. Accessing pod metadata and other resources from applications

8.2.2 Talking to the API server from within a pod

kc exec -it curl /bin/bash でpodにログインし、

env | grep KUBERNETES_SERVICE
# でkubernetes APIのIPとPORTがわかる。

export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/pods
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/default/services/git-sync-demo

ちなみに、 /var/run/secrets/kubernetes.io/serviceaccount/namespace にnamespaceが書いてある。

Rubykubernetes API クライアントは https://github.com/abonas/kubeclient がある。

10. StatefulSets: deploying replicated stateful application

  • 再作成されてもpod nameやhostnameは同じ名前で生成される。
  • また同じPersistentVolumeClaimsを使おうとする。
  • 作成されるときは、次の使われていないindexを使って作成される。
  • 現在が2で3が使われている時、4が使われる。(3が使われているときってどんな時…)
  • またpodが削除されるときは大きいindexから削除される。
  • 削除されるときは使っていたPersistentVolumeClaimsは削除されない。

11. Understanding Kubernetes internals

11.4.3 Introducing the Container Network Interface

コンテナが簡単にネットワークに接続できるようにContainer Network Interface (CNI)というプロジェクトがスタートした。 KubernetesはいくつかのCNI pluginを使うことができる。

  • Calico
  • Flannel
  • Romana
  • Weave Net
  • And others

DaemonSetとして各Nodeにデプロイされる。

11.5.1 How services are implemente

serviceはiptables rulesを追加する。実態はiptables rule。(340ページの図 参照)

11.5.2 How kube-proxy uses iptables

Endpoint objectは後ろにserviceがある全てのpodのIP/portのペアを保持している。

12. Securing the Kubernetes API serve

12.1.3 Creating ServiceAccounts

  • ServiceAccountsで使われているtokeは、JWT token

12.2.2 Introducing RBAC resources

  • RoleClusterRoleは、どのリソースにどんな操作を許可するかを指定するもの
  • RoleBindingClusterRoleBindingはどのRole/ClusterRoleをどのユーザ/グループ/ServiceAcctountに紐付けるかを定義するもの

RoleとClusterRoleの違い、RoleBindingとClusterRoleBindingの違いは、Role/RoleBindingは特定のnamespaceに属するが、ClusterRole/ClusterRoleBindingはnamespaceに属さない(cluster-lebel resource)

  • 操作とは
    • get, create, update, delete, list, watch, deletecollection がある。
  • リソースとは
    • これはkubernetesのリソース。pod, depoyment, service, secret etc..

例: service-reader.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: foo
  name: service-reader
rules:
- apiGroups: [""]
  verbs: ["get", "list"]
  resources: ["services"]
  • ServiceAccountについて
    • podはデフォルトではdefault ServiceAccountに紐づく。default ServiceAccountはnamespace作成時に作成される。
    • 追加のServiceAccountを手動で作成することができ、podに紐付けることができる

(roleのbind先のUserやGroupって何を指すんだろう。。)

  • adminという名前のclusterRoleは そのnamespece内のどんなリソースの操作もできる。ただしResourceQuotasとそのnamespaceリソースを除いて。
  • clusterRolesのedit と admin の違いは、そのnamespaceの Roles and RoleBindingsを参照/変更できるかどうか。adminはできる。

もしminikube上でRBACを有効にしたい場合は、minikube起動時に、--extra-config=apiserver.Authorization.Mode=RBACを指定する必要がある。

13. Securing cluster nodes and the network

  • podが自身のvirtualネットワークではなく、nodeのネットワークを使いたい場合、 hostNetworkプロパティを設定すればOK。
  • Kubernetes Control Plane components がpodとしてdeployされるとき、hostNetworkがONでデプロイされる。
  • 同じようにnodeのportを使いたい場合は、hostPortを設定すればOK。
    • しかしその場合、そのpodはnodeに1つしか配置できない。なぜなら同じportを複数のpodで使えないから。
  • 同じように hostPID: true、 hostIPC: true もできる
  • podのコンテナのプロセスの実行ユーザを限定する方法として、以下のようにユーザidを指定できる
apiVersion: v1
kind: Pod
metadata:
  name: pod-as-user-guest
spec:
  containers:
  - name: main
    image: alpine
    command: ["/bin/sleep", "999999"]
    securityContext:
      runAsUser: 405
  • セキュリティをあげるための方法として以下がある
    • podのコンテナのプロセスの実行ユーザを限定する(user idを指定)
    • コンテナのプロセスをrootで動かさない
    • コンテナをprivilegedで動かすと、nodeのカーネルにフルアクセスできてしまうので避ける
    • SELinuxを設定する
    • コンテナがfileシステムに書き込めないようにする=read onlyに設定する
    • readonlyにしつつ、特定のvolumeは書き込めるようにもできる

またlinuxのcapabilitiesをコンテナに指定することができる

PodSecurityPolicyリソース

参考: https://docs.bitnami.com/kubernetes/how-to/secure-kubernetes-cluster-psp/ https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

PodSecurityPolicy とはクラスタ全体のセキュリティ上のポリシーを定義する機能です。ホストに影響を与える可能性がある 特権 (privileged) や HostIPC などの機能を制限し Pod に脆弱性があった場合にクラスタを守ることができます。

参考: Kubernetes: Pod Security Policy によるセキュリティの設定 https://qiita.com/tkusumi/items/6692af743ae03dc0fdcc

またRBACによって、ServiceAccountに紐付けることができる。

できること

  • どのpodがホストのIPC, PID or Network namespaceを使用できるか
  • どのホストポートをpodが使えるか
  • どのユーザでコンテナのプロセスを動かすか
  • privileged containeを持ったpodを作れるかどうか
  • どの kernel capabilitieを許可するか、またデフォルトでどのkernel capabilitieを追加or削除するか
  • どんな SELinuxLabelをコンテナが使うか
  • コンテナがrootファイルシステムを書き込み許可するかどうか
  • コンテナがどのfile system groupを実行できるか
  • podがどのvolumeタイプを使用できるか

13.4 Isolating the pod network

もしnetworking pluginがサポートしていれば、NetworkPolicyリソースを作成することによって、 ネットワークの隔離をすることができる。 要はpod間の通信の制限ができる。 ingress and egress rulesと呼ばれる。Ingressリソースとは関係がない。 ingressはネットワークの流入、egressはネットワークの流出のルールを定義できる。

このpodはこのpodへしかリクエストを送れないor受け取れないとかこのportを使ってしかあのpodへリクエストを送れないor受け取れないとかが設定できる。 13.4.2 参照。 また特定のlabelがついたnamespace内のpodからリクエストを受け取らないということができる。 もしくはcidrを使ってリクエストを制限することもできる

13.5 Summary

  • podは自分のではなくnodeの Linux namespacesを使うことができる
  • コンテナはコンテナイメージで指定されたユーザとは別のユーザで実行するように設定することができる
  • コンテナはprivileged modeで動かすことができる
  • コンテナはプロセスがファイル書き込みをしないようにread onlyで動くように設定することができる(また特定のvolumeのみ書き込みが可能に設定できる)
  • Cluster-levelリソースであるPodSecurityPolicyは、nodeの安全を脅かすpodの生成を防ぐことができる。
  • PodSecurityPolicyはRBACのClusterRoleとClusterRoleBindingを使って特定のuserに紐付けることができる。
  • NetworkPolicyリソースはpodのinbound/outbound trafficを制限することができる。

14. Managing pods’ computational resources

resource requests

podをデプロイする時に必要とするリソース(CPU/メモリ)を指定することができる。 もちろんそれ以上使うこともできる。(リソース使用量の制限はlimitを使う) またその指定したリソースがそのpod用に確保されるわけではない。 あくまでpodをデプロイ時にそのリソース量が空いているかをチェックするためのもの。 そのリソース量が空いていないnodeにはデプロイされない。

 apiVersion: v1
        kind: Pod
        metadata:
          name: requests-pod
        spec:
          containers:
          - image: busybox
            command: ["dd", "if=/dev/zero", "of=/dev/null"]
name: main
resources:
  requests:
    cpu: 200m
    memory: 10Mi

cpu 200m は200 millicoresの略で1000milli coreで1cpu。200 mill coreで 1/5 CPU使うということ。 10Miは 10 mebibytes 。

$ kubectl describe nodes
Name:       minikube
...
Capacity: # nodeの使用可能リソース量
  cpu: 2
  memory: 2048484Ki
pods: 110
Allocatable: #ここがresource requestsで指定した割当量
  cpu:           2
  memory:        1946084Ki
  pods:          110

ポイントとしては、podをデプロイするときに、nodeのリソース使用量は見ないで、resource requestsをみてデプロイが行われる。 なので、nodeのリソースが100%でも、resource requestsに空きがあればデプロイされる。

resource limits

apiVersion: v1
kind: Pod
metadata:
  name: limited-pod
spec:
  containers:
  - image: busybox
    command: ["dd", "if=/dev/zero", "of=/dev/null"]
    name: main
resources:
  limits: # ここで制限を指定
    cpu: 1
    memory: 20Mi

上記のようにresource requestsを指定していなかったら、limit値と同様の値がresource requestsに設定される。 resource requestと違って、resource limitはnodeの使用可能リソース量を超えて指定ができる。 たとえばメモリが1Gのnodeがあって、pod Aのメモリrequestが500MBでlimitが800MB、pod Bのメモリrequestが500MBでlimitが500MBでもOK。 CPUも同様。

ちなみにそのnode上で使用可能なCPUリソース以上をpodたちが使用しようとした場合、 CPU速度が遅くなるだけで、問題が起きるわけではない。 しかしメモリの場合は違う。resource limitで指定した以上のメモリをpodたちが使用しようとした場合、 コンテナはOOM killされる。 もしpodのrestart policyがAlways or OnFailureが設定されていた場合、podのコンテナは自動的に再起動される。なのでkillされたことに気づかないかもしれない。 もし再度メモリオーバーになったら再度再起動されるが、再起動時間はexponential backoffアルゴリズムにしたがって段々遅くなる。 その場合、podのstatusはCrashLoopBackOffになる。 CrashLoopBackOffはkubeletが諦めたことをささない。少しずつ再起動期間は伸びるが最終的に5分間隔で再起動が繰り返されるようになる。 再起動回数はpodのRESTARTSに表示される。

14.3 pod QoSクラス

Kubernetesは3つのQuality of Service (QoS)クラスを提供する。Qosはpodに付与される。

  • BestEffort (the lowest priority)
  • Burstable
  • Guaranteed (the highest)

QoS classはresource requestsとresource limitsの2つから決定される。

BestEffortが設定される条件

  • pod内のどのコンテナにもresource requestsとlimitsが設定されていない

Guaranteed (the highest)が設定される条件

  • CPUとメモリの両方に、resouce requestsとlimitsがセットされていること
  • 上記がpod内のそれぞれのコンテナにセットされていること。
  • resouce requestsとlimitsの値がそれぞれ同じであること。

Burstableが設定される条件

  • BestEffortもGuaranteedも設定されていない場合

pod内に複数のコンテナが存在する場合、どうやってpodのQoSが決まるか。 まず各コンテナに上記のルールに従ってQoSを割り当てる。 全てのコンテナがBestEffortならpodのQoSはBestEffortになる。 全てのコンテナがGuaranteedならpodのQoSはGuaranteedになる。 それ以外の場合はpodのQoSはBestEffortになる。

podのQoSはkubectl describe pod -o yaml|json したときに、status.qosClass fieldに表示される。

nodeのメモリ上限に達した場合、どういった判断でプロセスがkillされるか

QoSに従ってどのコンテナがkillされるか決まる。 最初にkillされるのはBestEffort。つぎに、Burstable。最後にGuaranteed(ただしsystem processがメモリを必要とした場合のみkillされる)。

同じQoSクラスだった場合、どうやってどのコンテナがkillされるかを決めているか

どの実行中プロセスもOutOfMemory (OOM) scoreを持っている。 システムはOutOfMemory (OOM) scoreのよってどのプロセスをkillするかを比較して決めている。

14.4 namespaceごとに、pod作成時にpodにデフォルトのresource requests/limitsを設定する

LimitRangeリソース

LimitRangeリソースを定義すると、そのLimitRangeリソースが属しているnamespace内にpodを作成するときに、 LimitRangeリソースで指定したresource requestsとlimitsを設定することができる。

apiVersion: v1
kind: LimitRange
metadata:
  name: example
spec:
  limits:
  - type: Pod
    min:
      cpu: 50m
      memory: 5Mi
    max: cpu: 1
      memory: 1Gi
  - type: Container
    defaultRequest: # resource requestsを設定していないコンテナはこのデフォルト値が設定される
      cpu: 100m
      memory: 10Mi
    default: # resource limitsが設定されていないコンテナはこのデフォルト値が設定される。
      cpu: 200m
      memory: 100Mi
    min: # コンテナが設定できる最小のrequests/limits値
      cpu: 50m
      memory: 5Mi
    max: # コンテナが設定できる最大のrequests/limits値
      cpu: 1
      memory: 1Gi
    maxLimitRequestRatio: # Maximum ratio between the limit and request for each resource
      cpu: 4
      memory: 10
  - type: PersistentVolumeClaim # PVCに対しても制限がかけられる
    min:
      storage: 1Gi
    max:
      storage: 10Gi

14.5 namespaceに対して使用可能リソース量を指定する

ResourceQuotaリソース

LimitRangeリソースはpodごとに適用される。 ResourceQuotaリソースはnamespaceに対して適用される。 制限できるのは、 CPU, メモリ、PersistentVolumeClaimのディスク使用量, podの数, 他のAPI objects users(?)が作成すること

apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: cpu-and-mem
  spec:
    hard:
     requests.cpu: 400m
      requests.memory: 200Mi
      limits.cpu: 600m
      limits.memory: 500Mi

ResourceQuotaリソースはそのnamespace内のpodのrequests/limitsのトータル値に対して制限がかかる。

kubectl describe quota で詳細がみれる。

14.6 podリソースの使用量のモニタリング

kubeletには内部にcAdvisorというものを持っている。 cAdvisorはnode自体のリソース使用量及びnode上で実行されているコンテナのリソース消費量のデータを収集する役割。 それらのデータをクラスタ全体で集約するために、Heapsterとよばれるコンポーネントがある。 Heapsterはnodeのうち1つのnode上でpodとして動作する。 Heapsterはserviceを通して公開される。

minikubeの場合は以下のコマンドでheapsterを有効にできる。 minikube addons enable heapster

heapsterが実行されていれば、 kubectl top node でその集約データ結果からnodeのリソース使用量がみれる。

$ kubectl top node
NAME       CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%
minikube   170m         8%        556Mi           27%

グラフィカルにリソースをモニタリングしたい場合、 GKEならGoogle Cloud Monitoringが使える。しかしminikubeやオンプレの場合は、 データの保存にInfluxDB, ビジュアリングにGrafanaが使える。

http://github.com/kubernetes/heapster/tree/master/deploy/kube-config/influxdb に従ってKubernetesクラスタにデプロイできる。 minikubeならHeapster add-onを有効にした時点でInfluxDBもGrafanaも有効になっている。 以下のように。

$ kubectl cluster-info
...
monitoring-grafana is running at https://192.168.99.100:8443/api/v1/proxy/namespaces/kube- system/services/monitoring-grafana

14.7 Summary

  • resource requestを指定することはpodをscheduleするときに役に立つ
  • resource limitsを指定することはpodが他のpodのリソースを使い過ぎることを防ぐ
  • 使われていないCPU時間はコンテナのrequestsに基づいて割り当てられる
  • コンテナはCPUを使い過ぎても決してkillされないが、メモリを使い過ぎようとしたらkillされる。
  • QoSと実際のメモリ使用量に基づいて、より重要なpodのために他のpodが消えるされることもある。
  • LimitRangeを定義することによって、それぞれのpodに対して、resource requestsとlimitsの最小値,最大値, デフォルト値を設定することができる。
  • ResourceQuotaを定義することによって、namespace内のpodのresourceの総量に対して最小, 最大を設定することができる。
  • どのくらい高いpod resource requests/limitsを指定しているか知るために、長い間モニタリングをする必要があります。

15 podとnodeのauto scaling

podのスケールアウト(Horizontal pod autoscaling)とはpodの数を増やすこと HorizontalPodAutoscalerリソースを定義することで可能になる。

まずDeployment, ReplicaSet, ReplicationController, or StatefulSetはscaled resource objectと呼ばれる。 これらはauto scalingが可能なリソース。

autoscalingプロセスは以下の3つで行われる。

  • scaled resource objectによって管理されている全てのpodのメトリックを取得する
  • メトリックを定義されたターゲット値に近づけるためにはどのくらいのpodが必要か計算する
  • scaled resourceのreplicas fieldを更新する。

メトリックはHorizontalPodAutoscalerリソースによって取得されるが、HorizontalPodAutoscalerリソースはHeapsterからメトリックを取得する。HeapsterはcAdvisorからメトリックを取得する。 そのためauto scalingにはHeapsterが起動していないといけない。

scaleoutのための計算方法は、 440のFigure 15.2 の図を参照 もしくは、 https://github.com/kubernetes/community/blob/master/contributors/design-proposals/autoscaling/horizontal-pod-autoscaler.md#autoscaling-algorithm を参照。

※ポイントとしては、auto scalingはtarget valueに近づくようにpod数が調整されるということ。 target valueが80なら 全体(全podの合計)のCPU使用率が80%になるように調整される。たとえば全体のCPU使用率が90%ならpod数は増えるし、50%ならpod数は減らされる。

HorizontalPodAutoscalerリソースには、autoscale条件と、最小/最大pod数の指定ができる。

重要な点は、定期的にメトリックの取得をしているため、すぐにはscalingが行われないということ。少し時間がかかる。 scale up(pod数を増やす)イベントは3分おきに発生する。 scale down(pod数を減らす)イベントは5分おきに発生する。

Memory-based autoscaling was introduced in Kubernetes version 1.8, and is configured exactly like CPU-based autoscaling. とあるが、ほんとか?

15.1.4 Scaling based on other and custom metrics

custom metricsを用いてauto scalingを行うことができる。 HPAには以下の3種類を指定することができる。

  • Resource
    • resource requests/limitsのようなresource metrics
  • Pods
    • custom metricsを含むpodに関係するmetrics。 例えばQueries Per Second (QPS) や message broker’s queueの数。
  • Object
    • podとは直接関係ないmetrics。たとえばIngressのlatency。

15.1.5 どんなmetricsがautoscalingに適しているか判断する

podのメモリ消費量はautoscaling metricsに向かない。 podが増えるほど全体のメモリ消費量は増え、使えるメモリ量が逆に減るからだ。 metricsの一つとして、Queries per Second (QPS)がある。 autoscaling metricsとしてcustom metricsを使う前に、podが増減したらそのmetricsがどのように振る舞うかよく考えること。

15.2 Vertical pod autoscaling

スケールアウトはサーバーの台数を増やすこと、スケールアップはサーバーのスペックを上げることだが、 podをスケールアップはまだサポートされていない。が、今後サポートされるであろう。 podのスケールアップというのは、podのresource requests or limitsを変更するということ。

15.3

Kubernetesは、cloud providerが提供していれば、nodeのauto scalingもでき、Cluster Autoscalerという機能が担っている。 Cluster Autoscalerは、リソースが枯渇して既存のnodeにpodがschedulingできない場合に、nodeを追加する。 またリソース負荷が低いならnodeの削除も行う。 Cluster Autoscalerはnodeを追加する前にそのnodeにpodがscheduleできるかを確認する。じゃないとnodeを追加してもpodがscheduleできない事態になってしまう。

nodeを削除するとき(scale downするとき)system podsが実行されているかチェックしされているならnodeの削除を行わない。ただしsystem podsがDaemonSetの場合を除く。DaemonSetは各nodeで実行されているためnodeからpodを削除しても問題ないため。 また unmanaged pod(replicasetやdeploymentによって管理されていないpod)やlocal strageを使っているpodが存在する場合もnodeを削除しない。 nodeを削除するさいには、nodeにunschedulableを設定する。これによりpodがscheduleされなくなる。 そしてnode上のpodが追い出される。 追い出されたpodはreplicasetやdeploymentの管理下なので他のnodeへ再scheduleされる。

tips

kubectl cordon <node>でnodeにunschedulableを設定できる。 kubectl drain <node>でnodeにunschedulableを設定し、node上のpodを追い出す。 kubectl uncordon <node>を実行すればnodeのunschedulableを解除できる。

15.3.3 cluster scale donw時にサービスの中断を抑える

nodeが突然死んだ場合はどうしようもないが、手動もしくはCluster Autoscalerでnodeの削除が行わえる場合は、サービスを中断させないことができる。 DisruptionBudgetリソースを作成すればよい。 参考: https://qiita.com/tkusumi/items/946b0f31931d21a78058 「PodDisruptionBudget とは Node を計画的に停止したい場合に、Pod の状況を見ながら退去 (evict) させる機能です」

15.4 Summary

  • podの水平スケーリング(scale out)はHorizontalPodAutoscalerリソースを作成し、それをDeployment, ReplicaSetに指定し、対象CPU使用量を指定することで簡単にできる。
  • podのCPU使用量の他に、アプリケーションが提供するcustom metricsやclusterにdeployされている他のリソースのmetricsをもとにauto scalingすることができる。
  • 垂直スケーリング(scale up)はまだ提供されていない
  • (cloud providerから提供されていれば)cluster nodeもautoscalingできる。

16 Advanced scheduling

最初、特定のnodeに特定のpodをscheduleする方法はnode selectorを使う方法があった。 しかし後からもっと柔軟にscheduleするためのメカニズムが導入された。 それがtaintsとtolerations。 これによって特定のtaintsをもったnodeに、特定のtolerationsをもったpodのみをscheduleすることが可能になる。 taints/tolerationsとnode selector / node affinityにはいくらか違いがある。 node selector / node affinityは、podに対して情報を付与することによってpodがどのnodeにscheduleされるかを決めるための仕組み。 taints/tolerationsはそれに加えて、既存のpodに情報を付与せずnodeに情報を付与することによってpodを特定のnodeにscheduleされないようにすることができる仕組み。

例えば、

$ kubectl describe node master.k8s
Name:         master.k8s
Role:
Labels:       beta.kubernetes.io/arch=amd64
              beta.kubernetes.io/os=linux
              kubernetes.io/hostname=master.k8s
              node-role.kubernetes.io/master=
Annotations:  node.alpha.kubernetes.io/ttl=0
              volumes.kubernetes.io/controller-managed-attach-detach=true
Taints:       node-role.kubernetes.io/master:NoSchedule

この場合、node-role.kubernetes.io/master:NoScheduleがTaints。 taintsは=:で構成されていて、 node-role.kubernetes.io/master がkey、 valueはnull、 effectがNoSchedule。 このnodeのtaintsを受け入れられるtolerationをもったpodでないとこのnodeへscheduleすることはできない。

たとえば

$ kubectl describe po kube-proxy-80wqm -n kube-system
...
Tolerations:    node-role.kubernetes.io/master=:NoSchedule
                node.alpha.kubernetes.io/notReady=:Exists:NoExecute
                node.alpha.kubernetes.io/unreachable=:Exists:NoExecute
...

node-role.kubernetes.io/master=:NoScheduleというtolerationをもっているので上記のnodeへscheduleできる。

node.alpha.kubernetes.io/notReady=:Exists:NoExecute のExistsは、値がnullではないことを指す。

effectは以下の4種類ある。

  • NoSchedule
    • taintが許容できなければnodeへscheduleさせない
  • PreferNoSchedule
    • taintが許容できるnodeを探し、なければ許容できないnodeであってもscheduleする
  • NoExecute
    • scheduling時に影響があるNoScheduleやPreferNoScheduleと違って、NoExecuteはnode上で実行中のpodへも影響がある。もしNoExecuteエフェクトをもったtaintをnodeへ追加した場合でかつpodがそのtaintを許容できない場合、そのpodはnode上から追い出される。

このtaints/tolerationsの分かりやすい使用例としては、production nodeに non-productionなpodをscheduleしたいくない場合に使える。

16.1.3 Adding tolerations to pods

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: prod
spec:
  replicas: 5
  template:
spec: ...
      tolerations:
      - key: node-type
        Operator: Equal
        value: production
        effect: NoSchedule

podのtolerationsの設定は上記の用に設定できる。

16.2 Using node affinity to attract pods to certain nodes

taintsは特定のnodeからpodを避けるための仕組み。 node affinityという仕組みは、podを特定のnode集合へscheduleするための仕組み。 node affinityの前身はnode selectorだった。node affinityはnode selectorよりももっとパワフル。 node selectorはいずれ非推奨になる。

node selectorと同様に、各podに自身のaffinity ruleを設定する。

まずnodeのLabelにaffinityを設定する。

$ kubectl describe node gke-kubia-default-pool-db274c5a-mjnf
Name:     gke-kubia-default-pool-db274c5a-mjnf
Role:
Labels: beta.kubernetes.io/arch=amd64
        beta.kubernetes.io/fluentd-ds-ready=true
        beta.kubernetes.io/instance-type=f1-micro
        beta.kubernetes.io/os=linux
        cloud.google.com/gke-nodepool=default-pool
        failure-domain.beta.kubernetes.io/region=europe-west1
        failure-domain.beta.kubernetes.io/zone=europe-west1-d
        kubernetes.io/hostname=gke-kubia-default-pool-db274c5a-mjnf

上記だと下3つがaffinityで、

  • failure-domain.beta.kubernetes.io/region
    • はnodeが位置するregionを指す
  • failure-domain.beta.kubernetes.io/zone
    • はnodeが属するzoneを指す
  • kubernetes.io/hostname
    • はhostnameを指す

podには以下のようにnodeAffinityを設定する。

apiVersion: v1
kind: Pod
metadata:
  name: kubia-gpu
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu
            operator: In
            values:
            - "true"

matchExpressionsを使って柔軟に条件を設定することができる。 requiredDuringSchedulingIgnoredDuringExecution は2つに分解できる。

requiredDuringScheduling はnodeをscheduleするさいこのaffinity ruleに合致するnodeにしかscheduleされないことを意味する。 IgnoredDuringExecution はこのaffinity ruleはnodeで実行中のpodには影響を与えないことを意味する。

preferredDuringSchedulingIgnoredDuringExecution というのもある。 preferredDuringSchedulingはnodeをscheduleするさいこのaffinity ruleに合致するnodeに優先的にscheduleするが合致するnodeがなければ他のnodeへscheduleすることを意味する。 またIgnoredDuringExecutionとは対象的なRequiredDuringExecutionもある。これはnod上で実行中のpodに影響を与えることを意味するが、まだKubernetesで実装されていなく、今後実装される予定。

またnode affinityの設定は複数定義することができ、それぞれにweightを設定することができる。

16.3 pod affinity と pod anti-affinityを用いて一緒に配置する

node selectorと node affinityはpodとnodeの影響を与えるものだった。 pod affinityはpod間に影響を与える仕組み。 たとえばfrontend podとbackend podがあった場合それらのpodが近いほうがlatencyが減らせるので、それぞれ近いほうがいい。 pod affinityを使えば同じnode(もしくはラック、もしくはデータセンター)にscheduleすることができる。

以下のような定義。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 5
  template:
... 
    spec:
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/rack
            labelSelector:
              matchLabels:
                app: backend

topologyKey: kubernetes.io/rackだが、こうすると同じrackにあるnodeにpodが配置される。 どういう仕組かというと、nodeにrack=rack_a, rack=rack_bのようにLabelをつける。 つまりrack_aに属するnodeにはrack=rack_aというLabelをつける。 すると、app=backendというLabelが先にrack_aのnodeに配置されている場合は、このpod affinity ruleをもつpodはrack_aのnodeのいずれかに配置される。

16.3.4 Scheduling pods away from each other with pod anti-affinity

pod anti-affinityを使うと、pod affinityと反対のことができる。

16.4 Summry

  • もしnodeにtaintsをセットすると、podがそのtaintsを許容できるtolerationをセットしていない限りそのnodeにそのpodはscheduleされない。
  • taintsは3タイプあり、NoScheduleは完璧にschedulingされない。 PreferNoScheduleはそこまで厳密ではに。NoExecuteはnode上の実行中のpodであっても追い出す。
  • NoExecute taintは、nodeがunreachableやunready状態になった場合podがどのくらいの時間schedulingされるのを待つか指定できる。
  • Node affinityはどのnodeにpodがscheduleされるべきか指定できる。
  • Pod affinityは同じaffinity ruleをもったpodが実行されているnodeにpodをscheduleすることができる
  • Pod anti-affinity はお互いのpodを避けさせることができる

17.2.3 Starting pods in a specific order

https://qiita.com/tkusumi/items/f64fff37a724b86d9819#init-containers-alpha

Pod内で初期化処理をするコンテナを指定をできる機能です。Podの定義にinitContainersという項目で、従来のcontainersで指定したコンテナの前に起動するコンテナを指定できるようになるようです。複数のコンテナを指定することができますが、containersがパラレルに起動されるのに対してinitContainersは指定した順番に1つずつ起動していくようです。

使用用途としては例えば以下のようなものが挙げられます。

  • データを動的にダウンロードしてくる
  • データベーススキーマの反映
  • アプリケーションが何らかの事前条件を満たすまでウェイトする
BEST PRACTICES FOR HANDLING INTER-POD DEPENDENCIES

init containersを使えば事前条件が満たされるまでpodのmainコンテナが起動するのを遅らせることができる。 readiness probesを設定するのを忘れてはいけません。 アプリケーションがまだ準備段階ではない場合にserviceにpodが追加されるのを防ぐことができますし、 Deployment controllerが、bad versionをrolloutするのを防ぐためにも使用されます。

17.2.4 Adding lifecycle hooks

podのlifecycleに以下の2つのhookを定義することができる。これらをlifecycle hooksと呼ぶ。

  • Post-start hooks
  • Pre-stop hooks

pod全体に設定するinit containersと違って、lifecycle hooksはpodのコンテナごとに設定できる。 lifecycle hooksはcontainerがstartするときとstopするときに実行される。

lifecycle hooksは、以下の点でlivenes、 readiness probesと似ている。

  • containerの内部で実行されるコマンド
  • URLに対してHTTP GET Requestとして実行される

USING A POST-START CONTAINER LIFECYCLE HOOK

Post-start hookはコンテナのmain processが実行された後すぐに実行される。 もしコンテナのアプリケーションとは別のアプリケーションを動かしたい場合、あなたがそのアプリケーションの開発者ならアプリケーション内で別のアプリケーションを動かすことができるが、アプリケーションの開発者ではない場合、post-start hookは役に立つ。 Post-start hookはmain processと平行して実行されるが、以下の点でcontainerに影響を与える まずPost-start hookの実行が完了するまで、containerのstateWaitingのままでありreasonContainerCreatingのまま。そのためpodのstatusはRunningではなくPendingのまま。 もしPost-start hookが実行に失敗したりnon-zero exit statusを返した場合、main containerはkillされる。

以下が定義例。

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-poststart-hook
spec:
  containers:
  - image: luksa/kubia
    name: kubia
    lifecycle:
      postStart:
        exec
          command:
          - sh
          - -c
          - "echo 'hook will fail with exit code 15'; sleep 5; exit 15"

hookによって開始されたprocessが標準出力に出力してもそれをみるすべはない。そのため永続的volumeに書き込むこと。

USING A PRE-STOP CONTAINER LIFECYCLE HOOK

https://qiita.com/superbrothers/items/3ac78daba3560ea406b2

17.3 Ensuring all client requests are handled properly

17.3.1 Preventing broken client connections when a pod is starting up

readinessを設定すること

17.3.2 Preventing broken connections during pod shut-down

まず、podが削除されるときの、Kubernetesの動きは以下になる。

1, client(kubectlとか)からpodの削除リクエストがくる 2, A1とB1が並行して同時に実行される A1, kubeletにpodの削除通知が送られる A2, kubeletからpodにコンテナの停止リクエストが送られる A3, コンテナのpre-stop hookが実行され、, SIGTERMが送られ、, 一定時間待った後、それでもまだコンテナのプロセスが動いていたら強制的にkillする B1, EndpointsControllerへpodの削除通知が送られる B2, EndpointsControllerはAPI Serverへendpointからpodを削除するようにリクエストする B3, API Serverはkube-proxyへEndpoints変更通知を送る B4, kube-proxyはnodeのiptablesからpodを削除する

コンテナが停止プロセスを実行しているときに処理中のリクエストはどうなってしまうか。 リクエストの処理に時間がかかる場合であれば、“Connection Refused”が起こるのは避けられない。 なので考えられる選択肢としては以下。 * 数分待ち、新しいリクエストを受け取るのを止める * 全てのkeep-aliveコネクションを閉じる * 全ての処理中のリクエストが処理完了するまで待つ * shut downを完了させる

pre-stop hookに数分待つように仕込めば実現できる。 しかしそれを入れるとpodの削除まで時間がかかってしまうので諸刃の剣。

17.4 Making your apps easy to run and manage in Kubernetes

17.4.1 Making manageable container images

imageのサイズは小さくし、不要なファイルやツールは含めないように。

17.4.2 Properly tagging your images and using imagePullPolicy wisely

docker imageのtagにはlatestが使えるが、podのyamlファイルにはlatestを指定しないようにしよう。 もしimageを更新しても、新しくscheduleするpodにはその新しいimageが使用されるが、既存のpodのコンテナのimageは古いバージョンのまま。 またlatestタグを使うと1つ前のバージョンへrollbackができない。

もしあなたがimageのタグにmutable tag(同じタグを使い続けること)を使うならpodのspecのimagePullPolicyはAlwaysにしなければならない。 しかしそれはproductionでは大きな落とし穴になる。 もしAlwaysを設定したら、新しいpodがscheduleするたびにcontainer runtimeは毎回container registryに問い合わせることになる。 これはpodが起動するのが少し遅くなる。なぜならnodeは毎回imageが更新されているかチェックする必要がでてくるため。またcontainer registryが応答がない場合、podが起動するのを妨げてしまう。

17.4.3 Using multi-dimensional instead of single-dimensional labels

podだけではなく他のリソースにもlabelをつけることを忘れないようにしよう。 各リソースに複数のlabelをつけるようにすれば、リソースのselectが簡単になる。 labelには例えば以下のものがよいだろう。

  • リソースが属しているアプリケーション(もしくはマイクロサービス)の名前
  • Enviroment(development, QA, staging, production 等)
  • バージョン
  • リリースのタイプ(stable, canary, green or blue for green/blue deployments等)
  • テナント(あなたがもしpodをnamespaceを使用する代わりに各テナントで動かしている場合)
  • シャードシステムならそのシャード

17.4.4 Describing each resource through annotations

他の追加情報はannotationに含めましょう。 たとえばそのアプリケーションの責任を持っている人の情報とか。 もしマイクロサービスであれば、そのpodが使用している他のサービスの名前とか。 バージョン情報とか、ツールやGUIツールが使用するmetadataとか。

17.4.5 Providing information on why the process terminated

Kubernetesにはなぜコンテナが終了したのかの情報を含めることができます。 コンテナの /dev/termination-log に終了した理由を書き込んでおけば、 kubectl describe podしたときにContainersのLast StateのMessageに表示される。

17.4.6 Handling application logs

アプリケーションログは標準に出すべき。 そうすればkubectl logsで見れる。 一つ前のpodのlogはkubectl logs --previousでみれる。

特定のログファイルにはいていたら、 $ kubectl exec <pod> cat <logfile> でみれる。 または、pod内にコンテナが1つなら $ kubectl cp foo-pod:/var/log/foo.log foo.log でログをローカルへcpすることができる。 複数のコンテナが実行されていたら -c containerName オプションでコンテナを指定する。

Kubernetesはログ集約システムは提供していない。 しかしそれをログ集約システムをデプロイするのは簡単。 Google Kubernetes EngineならEnable Stackdriver Logging checkbox にチェックを入れればいい。

EFK(ElacsticSeach, Fluentd, kibana)のようなスタックをデプロイすれば使うこともできる。 fluentdのようなログ収集ミドルウェアは複数行ログ(たとえばJavaスタックトレースのような)は1行1行別のログになってしまうので、jsonとして出力するのがよい。 ただjsonログは人間には見にくいので(kubectl logsした場合)、 標準出力にはplain textにして、特定のログファイルにはjsonで出力するのがよい。

17.6 Summary

  • podのような頻繁にrelocateするappと、ほとんど移動しないappの違いについて
  • マイクロサービスのような複数のコンポーネントアプリは特定の順序で起動にすることに頼るべきではないことを理解する
  • init containersの紹介。init containersとはpodの起動に使用されたり、事前条件を満たすまでpodのmain containerの起動を遅らせることができる
  • コンテナのlifecycle hooks の紹介
  • Kubernetesコンポーネントの分散性質の結果と結果整合性モデルの深い理解
  • リクエストを壊すことないアプリケーションの適切なshut downの仕方について
  • imageサイズを小さくすることにや、annotationsや複数のlabelを各リソースに追加することによって管理がどのように楽になるか
  • どのようにKubernetes上での動くアプリケーションの開発をするか、また、multi node clusterにデプロイする前にminikubeやローカルでどのように開発するか

18 Extending Kubernetes

18.1 Defining custom API objects

自分のAPI objectを定義し、それらのobjectのためのcontrollersを実装することができる。

18.2 Introducing CustomResourceDefinitions

新しいリソースを作成するためには、CustomResourceDefinition objectを定義する必要がある。 CustomResourceDefinitionを定義すれば、他のリソースが同様, yamljsonを通してそのinstanceを作成することができるようになる。 以下が定義例。

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: websites.extensions.example.com
spec:
  scope: Namespaced
group: extensions.example.com
version: v1
names:
  kind: Website
  singular: website
  plural: websites

このリソースのinstanceを作成する場合は以下。

apiVersion: extensions.example.com/v1
kind: Website
metadata:
  name: kubia
spec:
  gitRepo: https://github.com/luksa/kubia-website-example.git

そして新しく定義したこのリソースに対するcontrollerを実装する必要がある。

https://github.com/luksa/k8s-website-controller

controllerもpodとして動かすことができる。

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: website-controller
spec:
  replicas: 1
  template:
metadata:
  name: website-controller
  labels:
    app: website-controller
spec:
  serviceAccountName: website-controller
  containers:
  - name: main
    image: luksa/website-controller
  - name: proxy
    image: luksa/kubectl-proxy:1.6.2

custom objectのバリデートはKubernetes1.8でアルファ機能として導入された。 API serverで バリデートするためにはCustomResourceValidation機能を有効にする必要がある。

18.1.4 Providing a custom API server for your custom objects

Kubernetesでcustom objectのサポートを提供する良い方法は、自分のAPI serverを実装し、clientがそのAPI serverとやり取りさせること。 Kubernetes 1.7から、KubernetesのデフォルトのmainのAPI Serverと自分で作成したAPI serverを統合することができるようになった。 複数のAPI Serverがあっても1つのAPIServerとして扱うことができる。

custom api serverを登録するには、custom api serverをpodとしてdeployしserverを通して公開すればよい。

18.2 Extending Kubernetes with the Kubernetes Service Catalog

最初の追加API Serverとして、Service Catalog API serverがある。 Service Catalogとは名前の通りserviceのカタログで、Service Catalog機能を使えば、カタログの中からサービス(アプリケーション)を選んで、pod,service,configmap等を直接扱うこと無く簡単にデプロイすることができるようになる。

Service Catalogは以下のリソースを提供している。

  • ClusterServiceBroker。これはサービスを提供する(外部)システムについて定義したもの
  • ClusterServiceClass。これは提供されるサービスについて定義したもの
  • ServiceInstance。これは提供されたサービスのinstance
  • ServiceBinding。これはServiceInstanceとclients(pods)のセットを結びつけるもの

Kubernetesは、提供されるサービスのリストをbrokerへ問い合わせ、ClusterServiceClassリソースを作成する ユーザが提供されるサービスが必要な場合、KubernetesはServiceInstanceを作成し、ServiceInstanceをpodsに結びつける(bindingする) それらのpodsに必要な認証情報や接続に必要な情報をsecretを通して渡す。

18.2.2 Introducing the Service Catalog API server and Controller Manager

Kubernetesと似ていて、Service Catalog以下の3つのコンポーネントからなる分散システムである。

  • Service Catalog API Server
  • etcd as the storage
  • Controller Manager, where all the controllers run

18.2.3 Introducing Service Brokers and the OpenServiceBroker API

クラスタの管理者は1つ以上の外部ServiceBrokerをService Catalogに登録することができる。 各brokerはOpenServiceBroker APIを実装する必要がある。

Service Catalogがサービスの一覧を取得したあと、Service Catalog は各ClusterServiceClassリソースを生成する。各ClusterServiceClassリソースは、サービスの1つのタイプが定義されている(例えばあるClusterServiceClassは“PostgreSQL database”)。 各ClusterServiceClassは、1つ以上のservice planを持っている。ユーザは必要とするそのサービスレベルを選択することができる。たとえばdatabase ClusterServiceClassはHDDのfreeプランや、SSDのPremiumプランを提供したりするだろう。

18.2.4 Provisioning and using a service

実際にserviceが提供されるためには、service instanceを作成する必要がある。 例:

apiVersion: servicecatalog.k8s.io/v1alpha1
kind: ServiceInstance
metadata:
  name: my-postgres-db
spec:
  clusterServiceClassName: postgres-database
  clusterServicePlanName: free
  parameters:
    init-db-args: --data-checksums

重要なのは、このservice(これだとpostgres-database)は、Kubernetesクラスタ外に作成されてもいいということ。なので例えばクラウド上のDBでもよい。 serviceへの接続情報はServiceBindingリソースを使ってsecretを通して与えられる。

例:

apiVersion: servicecatalog.k8s.io/v1alpha1
kind: ServiceBinding
metadata:
  name: my-postgres-db-binding
spec:
  instanceRef:
    name: my-postgres-db
  secretName: postgres-secret #ここに好きなsecret名を指定

将来的にはPodPresetsというKubernetesの機能が導入される予定。

Service CatalogはServiceBindingで指定した名前で新しいSecretを生成し、必要な情報をsecret内に保存する。 あとはpodからそのsecretをmountして使えば、podが接続情報を得ることができる。

18.4 Summary

  • CustomResourceDefinition objectを作成することで、Custom resourcesをAPI Serverに登録することができる
  • API Serverのコードを変更すること無く、CustomObjectのインスタンスを保存、取得、更新、削除することができる
  • custom objectsを実際にKubernetes上で動くようにCustom Controllerを実装することができる
  • API aggregation機能を通し、Custom API Serverを使うことでKubernetesを拡張することができる
  • Kubernetes上に構築されたPasSはコンテナを使ったアプリケーションを構築するのが楽になる
  • パッケージマネージャーのhelmは既存のアプリケーションをresource manifestsを必要とせずにデプロイすることができる

appendix A: Using kubectl with multiple clusters

A.2.2 Understanding the contents of the kubeconfig file

apiVersion: v1
clusters:
- cluster:
    certificate-authority: /home/luksa/.minikube/ca.crt
    server: https://192.168.99.100:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
    namespace: default
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /home/luksa/.minikube/apiserver.crt
    client-key: /home/luksa/.minikube/apiserver.key

上記のようにKubeconfig fileは以下で構成されている。

  • clustersのリスト
  • usersのリスト
  • contextsのリスト
  • current contextの名前

それぞれの詳細

cluster

  • Kubernetes clusterを表している。API ServerのURL、certificate authority(CA)ファイル、他には必須ではないがAPI serverと通信するためのいくつかのオプション。 CA certificateは分割して保存できそれをkubeconfigファイル内から指定できる。また直接certificate-authority-dataフィールドに含めることも出来る。

user

  • どのuserもAPI Serverと通信する時に使うcredentialsを定義する。credentialsには以下が使える
    • usernameとpassowrd
    • authentication token
    • client key and certificate
  • client key and certificateはkubeconfig fileに含めることができる。もしくはファイルを分割してconfig file内で指定することができる。以下のように
users:
- name: minikube
  user:
    client-certificate: /home/luksa/.minikube/apiserver.crt
    client-key: /home/luksa/.minikube/apiserver.key

context

  • contextはclusterとuserを紐付けるもの。またkubectlで使われるデフォルトのnamespaceを指定できる。複数のcontextで、同じuserや同じclusterを指定することができる。

appendix B: Setting up a multi-node cluster with kubeadm

kubeadmを使えば複数nodeクラスタが簡単に作成できる。

appendix C: Using other container runtimes

C.1 Replacing Docker with rkt

container runtimeとしてdockerのほかにrktがある。 rktはデフォルトでPod(複数の関連するコンテナを実行する)の概念をサポートしている。 rktはDocker-formatted container imageをそのまま実行することができるのでリパッケージは必要がない。 Kubernetesでrktを使うためにはkubeletに--container-runtime=rktオプションを渡してあげればよい。 なおkubeletはContainer Runtimeとやりとりする唯一のコンポーネント

C.2 Using other container runtimes through the CRI

Kubernetes 1.5 から Container Runtime Interface (CRI) が導入された。 CRIはplugin APIで他のcontainer runtimeを統一的に操作することができる。 kubeletはDockerやrktをCRIを通して操作する。

新しいCRI実装として、CRI-Oというものもでてきた。

Kubernetesをコンテナの代わりにVM上でアプリケーションを動かせる新しいCRI実装が開発中。 そのうちの一つはFrakti(The hypervisor-based container runtime for Kubernetes.)というもの。 https://github.com/kubernetes/frakti

ほかには Mirantis VirtletというCRI実装があり、これは、docker imageの代わりに実際にVM imagesを動かすというもの。

appendix D: Cluster Federation

Kubernetesは複数のKubernetesを1つのKubernetesとして扱うための Cluster Federationという機能がある。 これにより、Kubernetes Clusterを複数のdata center、複数のアベイラビリティゾーン、もしくは複数のクラウド冗長化することができる。

一年間本気で英語勉強をした話

こちらはポエムです。
実際の英語勉強方法については、11ヶ月間でTOEICスコアを300点から835点に上げた英語学習法 をご覧ください。

 

学年ビリのアホが1年半でTOEICスコアを300点から840点に上げた英語勉強法の話の記事を見たのをきっかけにして今年の初めから英語勉強を始めた。

目標は1年間でTOEIC800点以上。あわよくば900点以上取れるといいなと思ったが途中で無理そうというのが分かった。
結果としては11月に受けたTOEIC試験で835点を取ることができ、めでたく目標達成できた。

勉強始める前はTOEIC300点以下だったと思う。勉強時間はだいたい1日3時間ちょっと勉強して11ヶ月間で1100時間以上勉強した。
まあ自分でもよく勉強したなあと思う。以前の自分だったらこんなに勉強できなかったし、やろうと思わなかった。
なにがきっかけで、何のタイミングでこういうことが起こるかわからないものだ。

自分の人生を振り返ると周りに英語が出来る人が多かったのは影響していると思う。
あとは自分自身、学生時代に勉強をちゃんとしてこなかったことが、後悔、後ろめたさ、コンプレックスとなり、今年の英語学習への原動力になったんだと思っている。
今まで自分は何か大きな努力をして大きな事を成すということをしてこなかった。
振り返ってみればこれは自分の人生への戦いでもあったのだ。

さて、800点以上取れて何が変わったかというとそんなに変わっていない。確かに英語のドキュメントは以前よりは読めるようになったが、スラスラは読めないし一部読解できないこともある。
多分世間一般からすると800点以上というと英語がかなりできる人に思えるかもしれないが、実はそんなことがない(もちろん個人差はある)。
900点でもこのような感じなのだ。
要はやっとスタートラインに立った気がする。当初の本当の目的である英語のドキュメントをスラスラ読めることを達成するにはまだまだ努力が必要だ。
ただ英語の基礎は出来たのであとはわりと楽だと思う。(今年の前半は本当に英語の基礎勉強が大変だった。。)

あと英語力が上がったことよりも、毎日3時間勉強しても苦にならない体質になったのが一番良かった。
今後は技術勉強に時間をあてたいと思う。(今年一年は技術勉強がろくに出来なかったので)

 

 いやーしかし努力をして何かを成すのはとても気持ちがいい。努力をしたことで見えてくる景色というのは本当にあるもんだ。TOEICの点数が下がったときの悔しさや800点超えた時の嬉しさ(部屋で歓喜した)は努力なしでは味わえないものだと思う。

今後も高い目標ドリブンで頑張っていきたい所存です。よろしくお願いします。 

TOEICに有効な勉強法、テクニック

勉強時

スピーカーをあえて質の悪いものを使ってリスニング力を鍛える

TOEIC試験ではリスニングがある意味関門です。

まず試験会場の教室が広いと音が反響して聞こえづらいです。もし後ろの席になってしまった場合、音量チェック時に聞き取りづらいかどうか聞かれるので思い切って手を上げて前の席に移動したほうがいいです。

次に試験に使われるスピーカーが大抵ショボいです*1。大きめのラジカセが使われますw このため音はクリアに聴こえません。そのため普段からリスニングのトレーニングには、まずイヤホンを使わないほうがいいでしょう。次にスピーカーも高価なものではなく安価なものにし、スピーカーの場所をあえて遠くに配置すると実際の試験と同じような環境になり、かなり効果的です (参考)。とはいえこのようなトレーニング方法はある程度リスニングに慣れてきてからのほうがいいと思います。

リーディングの問題にどのくらい時間がかかっているか測る

自分はマークシート for TOEICを使いました。

これはマークシート用アプリなんですがこのアプリを使えばそれぞれの問題、それぞれのPartにどのくらい時間がかかったのか、また正解数(正解率)が分かります。結果を書き出すことが出来るので終わった後見返すことができ、非常に便利です。TOEICのリーディングは本当に時間との勝負なのでこれでどこにどのくらいかかっているのか、またどのくらいのペースならいいのかを分析することができます。

試験当日

マークシート用シャープペンシルを使う。

芯が太いのでマークシートを速く塗れます。マジでおすすめ。鉛筆より速く塗れる。 参考

試験前に甘いもの(チョコレート)を食べる

TOEIC試験は極度の集中力を要求されます。リーディングの最後のほうでは頭が働かないこともあります。そのため試験が始まる前にチョコレートを食べておくと効果的です。結構違いがでます。

リスニングは目を閉じて聴く

視覚をシャットダウンすると聴力を集中させることができます。とはいえPart3,4の問題の設問と選択肢が読めないので諸刃の剣。でもリスニングしながら設問と選択肢読めるのって相当スキルがないと出来ない気がする。。自分は目を開けて聴くとどうしても集中力が落ちたので目をつむってます。

*1:試験会場が青山学院大学のときはスピーカーがなぜかBOSEで聞きやすかった

短時間勉強会のススメ

最近社内で短時間勉強会を行っている。 特徴としては、

  • 1時間〜1時間半くらいで終わる
  • 参加者、主催者が特に事前準備をしない

ことがあげられる。

たとえば"他事業部のサービスのソースコードを読む会"をやったときは、

  • 30分間他事業部のサービスのソースコードを各自読み、Wikiページ(ちなみにうちの会社ではConfluenceを使っている)に感想や参考になりそうな点を書き込む。
  • その後30分〜60分間、各自感想や参考になりそうな点を発表して、意見を交換する

といった流れだ。 こういう形式なら参加者も主催者も準備が特に必要なく気軽に開催できる。

ここ最近やった会は以下だ。

StyleGuideを読む会

  • Ruby, RailsのStyleGuideを読む。読んだ結果の感想や気付き、疑問点等々をWikiページに書き込み、各自みんなで話しあう。
  • StyleGuideを読んで理解することはコードを書くうえでMUSTなのだが、まだRubyRailsのコードをあまり書いたことがないメンバーがいたり、また以前StyleGuideを読んだ人も意外と忘れていたりするので、開催してみた。
  • StyleGuideを読んだことがないメンバーにはいいきっかけになったし、またお互い良いスタイルがなにかの認識合わせができてよかったと思う。

他事業部のサービスのRailsソースコードを読む会

  • 大規模サービスのRailsソースコードを読む機会ってあまりなく、大きなサービスをRailsで作っていると若干不安になることがある。そのため他事業部のサービスのRailsソースコードを読むことで、引き出しを増やしたり、良い設計や実装を自分のサービスに取り込もうという企画。
  • 自分が知らなかった便利なGemを使っていたり、参考にできそうな設計がされていたりして、有益な会になった。

NewRelicをみんなで触ってみる会

  • NewRelicというパフォーマンス監視ツールがあり、それを導入しようと話がすすんでいた。
  • ただ、一方的に導入しても他のエンジニアが使わなければ宝の持ち腐れになってしまう。
  • そのため、導入前にみんなでNewRelicを触ってみる会を設けた。
  • これも例によって30分間各自NewRelicを触ってみて、その結果の感想だったり、気づいた便利な機能だったり、こういうことが解決できそうといった感想をWikiページに書き込み、そのあとの30分間で各自発表しあう形式にした。
  • これによって自分も知らなかった便利な機能を知ることができたり、各自がNewRelicを使うことでどんなことが解決できそうか理解することができた。

開発環境共有会

  • 便利なツール1つとっても知っているだけで生産性が上がることってありますよね。そういったお得な情報をみんなで共有して各自の開発速度を上げようという趣旨の共有会
  • 例えば、普段使っている便利なツール、便利な設定、便利なコマンド、使っているキーバインド、使っているシェル、使っているエディタ、使っているキーボード、マウス、取り入れている開発スタイル、タスク管理方法などをWikiページに書きまくって、各自発表する。
  • エンジニアはツールや環境に凝る人が多いので、会は結構盛り上がる。
  • 知らなかった便利情報が集まって非常に有益な会になった。Wikiページに書いてあるので参加しなかったエンジニアもみることができるのがまたいい。

一部、勉強会というより共有会に近いですね。

あとこのスタイルは読書会でも適用できて、30分間各自指定した本を読みその感想をWikiページに書いて、残り30分間感想を発表しあうみたいな形式が可能です。 (このスタイルは以前同僚がやっていて、今回の一連の勉強会もそのスタイルを拝借した)

短時間で終わって参加者主催者が特に準備がないスタイルは気楽にできるので、ぜひ皆さんもやってみてはいかがでしょうか。

ネストしたHashからOpenStructを作る

HashからOpenStructを作成する場合、

pry(main)> OpenStruct.new({hoge: 1})
=> #<OpenStruct hoge=1>
pry(main)> OpenStruct.new({hoge: 1}).hoge
=> 1

のようにすればよい。 しかしネストしたHashだと以下のようになる。

pry(main)> OpenStruct.new({hoge: {fuga: 2}})
=> #<OpenStruct hoge={:fuga=>2}>
pry(main)> OpenStruct.new({hoge: {fuga: 2}}).hoge
=> {:fuga=>2}
pry(main)> OpenStruct.new({hoge: {fuga: 2}}).hoge.fuga
NoMethodError: undefined method `fuga' for {:fuga=>2}:Hash
from (pry):29:in `<main>'

やりたいのは、 再帰的にOpenStructを適用して、 xxx.hoge.fugaでアクセスできるようにすること。 https://github.com/aetherknight/recursive-open-struct のようなgemを使えばできるが、 以下のようにいったんJSONに変換すると簡単に再帰的にOpenStructに変換してくれる。

pry(main)> h = {hoge: {fuga: 2}}
=> {:hoge=>{:fuga=>2}}
pry(main)> JSON.parse(h.to_json, object_class: OpenStruct)
=> #<OpenStruct hoge=#<OpenStruct fuga=2>>
pry(main)> o = JSON.parse(h.to_json, object_class: OpenStruct)
=> #<OpenStruct hoge=#<OpenStruct fuga=2>>
pry(main)> o.hoge.fuga
=> 2

FactoryGirlで Hashを定義

FactoryGirlでARではなくhashのモックデータを返してほしいときがある。

そういう場合は以下のようにすればよい。

FactoryGirl.define do
  factory :dog, class: Hash  do
    id 1
    name 'john'
    color 'black'

    initialize_with { attributes }
  end
end

initialize_with は初期化処理を定義できるもので、attributesはFactoryGirlのメソッドだけどattributesの戻り値がHashなのでそのまま使っている感じ。

利用するときはいつものようにbuildが使える。

dog = FactoryGirl.build(:dog, name: 'taro')

参考: http://stackoverflow.com/questions/10032760/how-to-define-an-array-hash-in-factory-girl