最近買ってとても良かったもの

最近買ってとても良かったもの紹介します。
特に人感センサー付きライトはみんな買って損がないと思います。

人感センサー付き ライトソケット

人感センサー付き照明は本当に便利で、ライトに近づくだけでスイッチがオンになり、しばらくするとオフになるので、わざわざスイッチをオンオフする必要がなくなります。超楽。自分は玄関と洗面所に設置してます。
で、人感センサー付き照明はこのような感じで電球一体型がメインです。 (https://amzn.to/2PRBMar は安いですが、パナソニックのやつとかは3000円近くします)
上記の商品は電球一体型ではないので安いし、既存の電球が使えるし、電球が切れたら電球だけ買い直せばOKです。あと照明がオフになるまでの時間を調節できるのがとても良いです。これで1000円なんだから破格だ。
ただこの商品は真下はセンサーの検知外なので真下も検知してほしい場合は、こっちのような電球一体型を買ったほうがいいです。
また自分の電球はE17型だったので以下のような変換ソケットをかましました。

スマートリモコン Nature Remo mini

家に帰ったら部屋がエアコンが起動していて涼しい/暖かい状態にしたかったのと、寝る時ベッドの上で照明をオフにしたかったので、買いました。最高。
IFTTTの連携も楽で、Androidだと家電やシーンの操作用ボタンをホーム画面に設置出来て便利。とりあえずホーム画面に照明オフボタンを設置。
daikinのエアコンだったので対応しているか不安だったが問題なかった。ただ照明は入居したときにリモコンがついてなかったので、このアプリスマホにインストールして照明のリモコンの赤外線を出せるようにしてNatureRemoに家電認識させた。
NatureRemoは家電の製品名で家電登録できるようにしてほしい。。

モデム収納ボックス

モデム周りって散乱しがちだと思いますが、この収納ボックスはモデムや電源タップを入れることができて、部屋がスッキリします。
自分はwifiルータも入れてます。
またボックスの上にモバイルWi-Fiルータスマホを置いて充電するのにちょうどいいです。

バスサンダル

このバスサンダルはタオルハンガーや棒状のものに引っ掛けられて便利。

折りたたみコンテナ

いくつかこのコンテナを買って部屋に収納しきれないものは全部突っ込んでおいて積み重ねている。折り畳めるし、耐久性もある。

+d ニンジャピン

+d ニンジャピン 15ヶ入り クリアー D-331-CL

+d ニンジャピン 15ヶ入り クリアー D-331-CL

壁にさしてもピン跡が目立たない画びょう。

Kubernetes勉強会第6回 〜Kubernetesの実行と管理、CustomResourceDefinitions(CRD)、Container Runtime Interface(CRI)〜

Kubernetes in Action

Kubernetes in Action

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

Kubernetesの実行と管理をしやすくするには

コンテナのイメージを管理しやすくする

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

イメージに適切にタグをうち、imagePullPolicyを賢く使う

docker imageのtagにはlatestが使えるが、podのyamlファイルにはlatestを指定しないようにしよう。 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が起動するのを妨げてしまう。

複数の情報をLabelに含める

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

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

annotationを通して各リソースを表現する

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

なぜプロセスが終了したのかの情報を提供する

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

アプリケーションログをハンドリングする

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

特定のログファイルにはいていたら、 $ kubectl exec <pod> cat <logfile> でみれる。 または、pod内にコンテナが1つなら $ kubectl cp foo-pod:/var/log/foo.log foo.log でログをローカルへコピーすることができる。 複数のコンテナが実行されていたら -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で出力するのがよい。

カスタムリソースを作成する

独自のカスタムリソースを定義し、Kubernetesを拡張することができる

CustomResourceDefinitions

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

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機能を有効にする必要がある。

custom objectのためのcustom API serverを提供する

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を通して公開すればよい。

Service Catalogを使ってKubernetesを拡張する

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を通して渡す。

Service Catalog API server と Controller Manager とは

Service Catalogは以下の3つのコンポーネントからなる分散システムである。

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

Service Brokers と 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プランを提供したりするだろう。

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が接続情報を得ることができる。

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を必要とせずにデプロイすることができる

複数クラスターをkubectlで扱う

以下のように環境変数KUBECONFIGにコロン区切りでクラスタファイルを指定すると複数のファイルを読み込むことができ、つまり複数クラスタを扱うことができる。

KUBECONFIG=~/.kube/config1:~/.kube/config2

kubeconfigの構成を理解する

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のリスト
  • contextsのリスト
  • current contextの名前
  • usersのリスト

それぞれの詳細

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を指定することができる。

他のcontainer runtimesを使用する

Dockerの代わりにrktを使う

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

他のcontainer runtimesをCRIを通して使う

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

新しいCRI実装として、CRI-Oというものもでてきた。 参考: kubernetes用コンテナランタイムcri-oを試す

CRI-Oの開発の背景には、dockerの開発速度が早い中、それに依存するkubernetesが不安定になっており、より小さく単純な、専用のランタイムを用意することでkubernetesの安定化に繋げたいという意図があったようです。

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

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

Cluster Federation

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

※ただ、Federation(の最初のversion)はあまりいけていないらしく、SIG-Multiclusterで仕切り直し中とのこと。

Kubernetes勉強会第5回 〜node affinity, pod affinity、Init containers、lifecycle hooks〜

Kubernetes in Action

Kubernetes in Action

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

node affinity, pod affinity

Kubernetesのnode affinity, pod affinityについて

Summry

  • もしnodeにtaintsをセットすると、podがそのtaintsを許容できるtolerationsをセットしていない限りそのnodeにそのpodはscheduleされない。
  • taintsは3タイプあり、NoScheduleは完璧にscheduleされない。 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を避けさせることができる

Init containers

Init containersを使うと、Pod内のコンテナを起動する前に特定の順番で特定の処理を行うことができる。

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

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

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

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

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

もしInit Containerの実行がエラーになった場合、KubernetesはInit Containerの実行が成功するまでpodを再起動します。 しかしもしrestartPolicyNeverに設定していたなら再起動しません。

例:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

参考: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/

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として実行される

Post-start hooks

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に書き込むこと。

Pre-stop hooks

Pre-stop hooksはコンテナが終了する前にすぐに呼び出される。つまりコンテナが終了するのをブロッキングする。 なのでコンテナを削除する呼び出しが送られる前にPre-stop hooksは終了しなければならない。 またPre-stop hooksには何のパラメーターも渡されない。 Podの終了時の振る舞いの詳細はTermination of Podsを参照すること。

Pre-stop hooksの実行タイミング等は以下の記事がとても詳しいです。 Kubernetes: 詳解 Pods の終了

以下が定義例。コンテナが終了する前に http://POD_IP:8080/shutdown へリクエストが送信される。

apiVersion: v1
kind: Pod
metadata:
  name: pre-stop-hook-httpget-pod
spec:
  containers:
  - image: luksa/kubia
    name: kubia
    lifecycle:
       preStop:
         httpGet:
            port: 8080
            path: shutdown

また以下のように定義すると、shellコマンドが実行される。

apiVersion: v1
kind: Pod
metadata:
  name: pre-stop-hook-shell-pod
spec:
  containers:
  - image: luksa/kubia
    name: kubia
    lifecycle:
       preStop:
         exec:
           command: ["sh", "-c", "sleep 1; nginx -s quit; sleep 5"]

参考: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/

システムリニューアルで重要視していること

自分は今の会社に入社してから、PHPで書かれたシステムをRubyで書かれたシステムへリニューアルするプロジェクトをやっています。 PHPで書かれたシステムが122万行くらいあって、DBのテーブルも300以上あるのでそう簡単にはリニューアルはできません。 またただ書き直すだけでは意味がないので、アーキテクチャから見直したりDB設計を見直したりしてます。 で、やっと主要機能のリニューアルが終わってきて、だいたい全体の半分くらいリニューアルが終わりました。 リニューアルの仕方は画面or機能ごとにRubyに書き換えて移行をしています。なのでPHPのシステムと並行稼動しているわけです。 あとRubyへリニューアル作業をしているのは初期は数人でしたが、今では大半のエンジニアがPHPよりもRubyで書くようになりました。

ということで自分がPHPからRubyへのシステムリニューアルで重要視していることをまとめてみました。(正確には社内で発表する機会があったのでそれをもとにこの記事を書いている) 書いてあるのは当たり前のことなので主に自分への備忘録目的です。

アーキテクチャから見直す

  • アーキテクチャが適切でなければリニューアルしても不適切なシステムが出来上がるだけ
  • アーキテクチャにはDB設計も含まれる
  • DB設計が間違っていればアプリケーションは正しいものにはなりえない

技術的負債に対して確固たる殺意をもつ

  • 技術的負債はもちろん返却していく
  • その際に絶対に保守的にならない
  • 保守的というのは、「ここを大きく変えると他も直さないといけなくて大変だからそっとしておこう」とか「この仕様がよくわからないけど消したら怖いから残しておこう」とか
  • 保守的になったらこのプロジェクトは終わり
  • 常にアグレッシブでいること

メンテナンス性

メンテナンス性はいくつかの要素があるが以下の2つを大切にしている。

  • 一貫性を大切に
    • 一貫性があるシステムはそれだけで生産性が
高い
    • 「一貫性のあるスタイルは”正しい”スタイルよりも大切」By リーダブルコード
    • 例えばエンジニアがそれぞれ考える”正しさ”を持ち寄ると、バラバラのプログラムが出来上がってしまう
  • シンプルに
    • もし何か判断に迷ったらシンプルな方にたおす

仕様の整理

  • 正しい仕様をもう一度考える
  • 複雑性のわりにユーザへの貢献度が低い機能や仕様を削る
  • 何のためにあるのかよく分からない仕様も思い切って捨てる
  • もちろんディレクターやCSの人と相談したり合意を取ることは必要

継続的な開発

  • リニューアルして終わりではなく継続的な開発ができるようにしていく
  • 継続的にリファクタリングしたり
  • 継続的に不要となった機能を消したり
  • 継続的にバージョンアップしたり
  • 継続的にモダンな技術を取り入れたり

おまけ: レガシーシステムを憎みすぎない

古いプログラムや技術的負債と向き合っていると、 ついつい憎んでしまいます。 しかし憎みすぎると既存の仕様や仕組みをガラッと変えるような判断をしがちです(坊主憎けりゃ袈裟まで憎い)。 レガシーシステムの機能や仕様の中にも不適切な部分と適切な部分があります。その適切な部分は活かすべきです。 慈悲の心を持って、きちんと何が不適切な部分なのか冷静&客観的になって見極めるようにしていきましょう。

Kubernetes勉強会第4回 〜cAdvisor、Heapster、podとnodeのauto scaling、taintsとtolerations〜

Kubernetes in Action

Kubernetes in Action

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

cAdvisor

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

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

heapsterが実行されていれば、 kubectl top node でその集約データ結果からnodeのリソース使用量がみれる。 (minikube addons enable heapsterを実行した直後であれば少し待つ必要がある)

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

# またpodごとのリソース消費量もみれる
$ kubectl top pod
NAME           CPU(cores)    MEMORY(bytes)
requests-pod   21m           19Mi

$ kubectl top pod --all-namespaces
NAMESPACE     NAME                                    CPU(cores)   MEMORY(bytes)
default       requests-pod-2                          21m          19Mi
kube-system   kube-scheduler-minikube                 11m          13Mi
kube-system   kube-controller-manager-minikube        40m          34Mi
kube-system   heapster-bmk4j                          0m           16Mi
kube-system   kubernetes-dashboard-5498ccf677-5gjhz   0m           14Mi
kube-system   kube-addon-manager-minikube             10m          19Mi
kube-system   kube-apiserver-minikube                 33m          232Mi
kube-system   etcd-minikube                           18m          32Mi
kube-system   kube-dns-86f4d74b45-hkn2d               1m           25Mi
kube-system   kube-proxy-xnfjc                        2m           15Mi
kube-system   influxdb-grafana-tnbzw                  1m           24Mi
kube-system   storage-provisioner                     0m           33Mi

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

Heapster, InfluxDB, Grafanaを試したい方は以下をご参考下さい。 KubernetesをHeapster + InfluxDB + Grafanaでモニタリングする

podのリソースを管理するためのまとめ

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

podとnodeのauto scaling

Kubernetesのauto scalingについて

taintsとtolerations

Kubernetesのtaintsとtolerationsについて

Kubernetes勉強会第3回 〜Resource RequestとResource Limit、pod QoSクラス、LimitRangeリソース、ResourceQuotaリソース〜

Kubernetes in Action

Kubernetes in Action

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

podの使用リソースを管理する

Resource RequestとResource Limit

KubernetesのResource RequestとResource Limit

pod QoSクラス

KubernetesのQuality of Service(QoS)クラスについて

LimitRangeリソース

LimitRangeリソースを利用すると、Pod、Container、 PersistentVolumeClaimに対して、CPUやメモリのリソースの最小値/最大値、デフォルト値を設定することが可能。 LimitRangeはNamespaceに対して制限をかけるため、Namespaceごとに設定する必要がある。 LimitRangeは新規でpodを作成する際に利用されるため、既存のpodには影響を与えない。

Resource RequestsResource Limitsを設定していないと、QoSクラスがBestEffortになってしまいkillされやすくなってしまう。それを避けたいならそれぞれのコンテナにResource RequestsResource Limitsを設定する必要がある。

LimitRangeを設定可能なリソースはpod / Container / PersistentVolumeClaim の3種類 Podでは、 max / min / PersistentRequestRatio のみが設定可能。Pod内で利用しているコンテナのリソースの合計での制限でminとmaxを利用する PersistentVolumeClaimでは max /minのみが設定可能。要求可能なストレージのサイズを制限する。 Requestsを設定しないと無限にスケジューリングされてしまうため、GKEではCPUのdefaultRequestが100mに設定されている。

LimitRangeリソースで設定可能な制限項目

設定項目 概要
default デフォルトのResource Limits値
defaultRequest デフォルトのResource Requests値
max 最大のResource Requests, Resource Limits値
min 最小のResource Requests, Resource Limits値
maxLimitRequestRatio Resource Limit / Resource Requests の割合

apiVersion: v1
kind: LimitRange
metadata:
  name: example
spec:
  limits:
  - type: Pod # Pod全体に対する制限
    min: # Pod全体で要求される最小のCPUとメモリ
      cpu: 50m
      memory: 5Mi
    max: cpu: 1 # Pod全体で要求される最大のCPUとメモリ
      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

※ CPU maxLimitRequestRatioが4の場合、CPU LimitsはCPU Requestsの4倍以上設定できないことを指す。つまりCPU Requestsが200millicoresだったら、CPU Limitは801millicores以上は設定できない。メモリも同様。

ちなみに、たとえLimitRangeリソースを設定してpodが使用可能なResource量を制限しても、podを何個も作成したらClusterのリソースを食い尽くしてしまう。それを防ぐにはResourceQuotaリソースを使う必要がある。

ResourceQuotaリソース

ResourceQuotaリソースを使用することで各Namespaceに対して利用可能なリソース量を制限することができる。 LimitRangeリソースはPodごとに適用されるがそれに対してResourceQuotaリソースはNamespaceに対して適用される。 制限可能な対象は大きく分けて「作成可能なリソース数の制限」と「リソース使用量の制限」の2種類がある。

制限できる対象は例えば、CPU, メモリ、PersistentVolumeClaimのディスク使用量, Podの数がある

Podを作成するとき、ResourceQuotaリソースで定義された制限を超えていないかチェックされる。超えていたらPodは作成できない。以下のようなエラーメッセージが表示される。

$ kubectl apply -f resource-request-and-limit.yaml
Error from server (Forbidden): error when creating "resource-request-and-limit.yaml": pods "limited-pod" is forbidden: exceeded quota: cpu-and-mem, requested: limits.cpu=1,requests.cpu=1, used: limits.cpu=0,requests.cpu=0, limited: limits.cpu=600m,requests.cpu=400m

既存のResourceQuotaリソースの設定を変更しても、既存のpodには影響がない。

作成可能なリソース数の制限

apiVersion: v1
kind: ResourceQuota
metadata:
  name: sample-resouce-quota-new
spec:
  hard:
    count/services: 10
    count/configmaps: 10
    count/deployments: 10

リソース使用量の制限

apiVersion: v1
kind: ResourceQuota
metadata:
  name: sample-resource-quota-usable
spec:
  hard:
    # 合計のRequestsの制限
    requests.cpu: 400m
    requests.memory: 200Mi
    # 合計のLimitsの制限
    limits.cpu: 600m
    limits.memory: 500Mi

ResourceQuotaリソースはそのnamespace内のpodのResource Requests、Resource Limitsのトータル値に対して制限がかかる。

kubectl describe resourcequotas で詳細がみれる。

% kubectl describe resourcequotas
Name:            cpu-and-mem
Namespace:       default
Resource         Used  Hard
--------         ----  ----
limits.cpu       200m  600m
limits.memory    20Mi  500Mi
requests.cpu     200m  400m
requests.memory  20Mi  200Mi

Kubernetes勉強会第2回 〜RBAC、セキュリティを高める方法、PodSecurityPolicy、NetworkPolicy〜

Kubernetes in Action

Kubernetes in Action

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

RBAC

KubernetesのRBACについて

13. Securing cluster nodes and the network

nodeのネットワークを使用する方法

  • podが自身のvirtualネットワークではなく、nodeのネットワークを使いたい場合、 hostNetworkプロパティを設定すればOK。
apiVersion: v1
kind: Pod
metadata:
  name: pod-with-host-network
spec:
  hostNetwork: true #これ
  containers:
  - name: main
    image: alpine
    command: ["/bin/sleep", "999999"]
  • hostNetworkプロパティをtrueにすれば、nodeあてのネットワークアクセスはそのpodに届くことになる

  • Kubernetes Control Plane components がpodとしてdeployされるとき、hostNetworkがONでデプロイされる。

    • なのでまるでpodの中で動いていないように振る舞う
  • 同じようにnodeのportを使いたい場合は、hostPortを設定すればOK。
    • しかしその場合、そのpodはnodeに1つしか配置できない。なぜなら同じportを複数のpodで使えないから。
  • 同じように hostPID: true、 hostIPC: true もできる
  • もしGCP上でこれらを試したい場合は、 gcloud compute firewall-rulesコマンドを使って設定する必要がある。参考

コンテナのプロセス実行ユーザを制限する

  • 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 # これ

またrootユーザとしてコンテナのプロセスを実行することを防ぐことができる 例えばhostディレクトリをマウントするコンテナが会った場合、rootユーザであればそのマウントしたディレクトリにフルアクセスできてしまう。しかしrootユーザ以外で動かせばそうではない。

apiVersion: v1
kind: Pod
metadata:
  name: pod-run-as-non-root
spec:
  containers:
  - name: main
    image: alpine
    command: ["/bin/sleep", "999999"]
    securityContext:
      runAsNonRoot: true #これ

セキュリティをあげるための方法

  • podのコンテナのプロセスの実行ユーザを限定する(user idを指定)
  • コンテナのプロセスをrootで動かさない
  • コンテナをprivilegedで動かすと、nodeのカーネルにフルアクセスできてしまうので避ける
  • SELinuxを設定する
  • コンテナがfileシステムに書き込めないようにする(=read only)に設定する。
    • コンテナのreadOnlyRootFilesystemをtrueに
  • readonlyにしつつ、特定のvolumeは書き込めるようにもできる

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

SecurityContextをもっと詳しく知るためには以下を参照

13.3 Restricting the use of security-related features in pods

PodSecurityPolicyリソース

PodSecurityPolicyリソースとは

参考: Pod Security Policies

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

引用元: Kubernetes: Pod Security Policy によるセキュリティの設定 PodSecurityPolicyに関しては上記の記事を読めば十分。(Z Labのエンジニアは本当に強い…)

PodSecurityPolicyはクラスタレベルのリソースです。 pod内でどんな機能が使えるのかもしくは使えないのかを定義することができます。 PodSecurityPolicyは働くためにはPodSecurityPolicy admission control pluginが API serverで動いている必要があります。

もし誰かがPodリソースを作成(API Serverへpost)しようとした場合、PodSecurityPolicy admission control pluginはPodの定義を定義されたPodSecurityPolicyに反していないかバリデートします。 もしクラスタのPolicyに従っていれば受け入れられetcdに保存されます。従っていなければ即時に却下されます。 PodSecurityPolicy admission control pluginはPodリソースをpolicyの設定に沿って変更するかもしれません(例えばコンテナ実行ユーザをPodSecurityPolicyで指定したユーザにoverrideすることができる)。 ちなみにpolicyを新しく作成しても既存のpodには影響を与えません。なぜならPodSecurityPoliciesはpodが作成/更新されるとき適用されるため。

PodSecurityPolicyで制限できること

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

apiVersion: extensions/v1beta1
  kind: PodSecurityPolicy
  metadata:
    name: restricted-psp
  spec:
    hostIPC: false   
    hostPID: false
    hostNetwork: false
    hostPorts:    # 使用できるport rangeを指定
    - min: 10000
      max: 11000
    - min: 13000
      max: 14000
    privileged: false
    readOnlyRootFilesystem: true
    runAsUser:
      rule: RunAsAny
    fsGroup:
      rule: RunAsAny
    supplementalGroups:
      rule: RunAsAny
    seLinux:
      rule: RunAsAny # コンテナが必要とするどんなSELinux groupも使用することができる
    volumes: 
    - '*'            # 全てのvolume typeを使用することができる

PodSecurityPolicyをRoleとして定義でき、RBACによって、ServiceAccountに紐付ける(bindingする)ことができる。

# 先のPodSecurityPolicyリソースを作成
& kubectl create -f restricted-psp.yaml

# PodSecurityPolicyをClusterRoleとして定義
$ kubectl create clusterrole psp-cluster-role --verb=use --resource=podsecuritypolicies --resource-name=restricted-psp

# ServiceAccountを作成
$ kubectl create serviceaccount psp-service-account

# ClusterRoleをServiceAccountに紐付ける(binding)
$ kubectl create clusterrolebinding psp-restricted --clusterrole=psp-cluster-role \
    --serviceaccount=default:psp-service-account

# あとはpodにServiceAccount:psp-service-accountを指定して、例えばprivileged: trueにするとpodは作成できなくなる。

13.4 Isolating the pod network

Network Policies

もしnetworking pluginがpod間のネットワーク通信制限機能をサポートしていれば、NetworkPolicyリソースを作成することによって、 ネットワークの隔離をすることができる。 NetworkPolicyリソースはlabel selectorにマッチしたpodに対して適用される。そしてpodへの外への通信とpodの中への通信を制限することができる。 ingress rule, egress ruleと呼ばれる。Ingressリソースとは関係がない。 ingressはネットワークの流入、egressはネットワークの流出のルールを定義できる。

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

例:

以下のingress/egressルールにマッチするものだけ通信が許可される。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
spec:
  podSelector:
    matchLabels:
      role: db # このlabelを持ったpodがこのNetworkPolicyルールの対象になる
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

13.5 Summary

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

Podのセキュリティを高めるためには以下を参照

Kubernetesのセキュリティを高めるためには以下を参照