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

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

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

人感センサー付き照明は本当に便利で、ライトに近づくだけでスイッチがオンになり、しばらくするとオフになるので、わざわざスイッチをオンオフする必要がなくなります。超楽。自分は玄関と洗面所に設置してます。
で、人感センサー付き照明はこのような感じで電球一体型がメインです。 (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

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

転職ドラフトを使って転職した

こちらの記事は転職ドラフト体験談投稿キャンペーンに参加しています。 job-draft.jp

注意: この記事の半分はAmazonギフトカード10,000円分がほしいという邪な気持ちで書かれています。

転職ドラフトに登録したきっかけ

  • 様々な理由が重なって、転職を考えるようになった。
  • とりあえず自分の市場的な需要を確認する意味合いで軽い気持ちで登録してみた
  • 自分は実は転職ドラフトを運営しているリブセンスの社員だったのだがリブセンスの中の人でも転職ドラフトは使いたいと思うサービスだった。自社の社員が自社のプロダクトを使いたいと思うようなプロダクトは本物だと思う。

転職ドラフトのサービス自体への感想

  • レジュメ入力画面で、どういうことを書けばいいのかが示されていて書きやすかった。以下のようなアドバイスが書かれている。素晴らしい。
    • f:id:shepherdMaster:20180822182325p:plain
  • 転職ドラフトの中の人が勝手にレジュメのレビュー(ここが内容が薄いからもうちょっと詳しく書いたほうがいいとか)もしてくれるらしいので安心感がある。(自分は指摘は受けなかったですが…)
  • 面談の日程調整機能が便利
  • こちらへの通知を自分のSlackへ流せる機能が便利
  • ドラフトルールとか見てもそうだが、かなり細かいところまでプロダクトが作り込まれていて、熱いサービスだと思うしこういった熱い思いで作ったサービスが世界を変えるんだなと思った。
  • もしかしたら売上をスケールさせるように圧力がかかっているかもしれないけど、いいサービスなのでこのサービスの世界観を維持しつつ着実にサービスを継続してほしい。

転職ドラフトでの指名状況

  • ありがたいことに予想以上に指名が入った。提示年収とか自分の市場価値を考えると「正気か!?」と思うような年収が提示されて、私の人生のピークは今かな?と思った。
  • その中から数社に絞ってカジュアル面談を受けてみることに

面接で感じたこと

  • 自分はレジュメをかなり詳しく書いてあったので、今までの経歴や今までやってきたことはそれほど聞かれず、今後どのようなことをやっていきたいかの指向性のチェックと、その会社ではどのような課題があるかの説明がメインだった。
  • それ以外は普通の面接と同じ。あと転職ドラフトからカジュアル面談(選考なし)と面接(選考あり)が選べるのが便利ですね。

転職ドラフトへの改善要望

  • たくさん指名が入るようなエンジニアになると、良くも悪くも競り状態(欲しいエンジニアがいたら実際そうすると思うし、ドラフトとはそういうものだと思うが)になってしまい、そのエンジニアの技術力以上に年収がつけれらてしまう傾向があるように感じた。なのでより適切さを求めるなら指名期間が終わるまで他の会社の指名額は非表示にしたほうがいいかもしれない。でもエンジニアの年収は技術力だけでは決まらないから難しいですが…
  • レジュメの使用技術のタグは自分で並べ替えられるようにしたい。つまりメインで使っていた技術は先頭にしたい。自動で並べ替えないでほしい。使用技術名が勝手に小文字になってしまうタグがある(SolrとかServerspecとかKubernetesとか)。タグは大文字小文字正しく登録してほしい。
  • レジュメ入力画面はForkwell Scoutのほうがやや使いやすかったかな。レジュメ詳細画面(https://job-draft.jp/user/resume)もForkwell Scoutのほうが見やすいかな。
  • 「水とプログラミングどっちが大事?」という質問はナンセンスでしょ(せめて任意回答にしてくれ)。エンジニアは特にこういった非論理的なことは嫌う傾向があると分かってほしい(参考)。それともなにか、"そんな質問させてごめんな"と言ってほしいのか。
  • 自分は以前転職ドラフトを使って採用活動をする側でもあったのだが、転職ドラフトを使ってオファー出すエンジニアを選定しメッセージを考えるのはとてもコストがかかっていたのでそのコストが軽減できると企業側が嬉しいと思う(どうやったらコストが軽減できるか案が思い浮かばないのですが…)

転職ドラフトを使ったことがない方へのメッセージ

  • 転職だけではなく自分の市場価値をはかるのにもいいサービスだと思います。
  • レジュメはできるだけ具体的に詳しく書くとよいです。これを読むとよいです。

(2271文字)

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リソース

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

LimitRangeリソースを定義すると、そのLimitRangeリソースが属しているnamespace内にpodを作成するときに、pod内でResource RequestsResource Limitsを設定していなくても、 LimitRangeリソースで指定したResource RequestsResource Limitsを設定することができる(つまりデフォルト値を定義することができる)。 またLimitRangeリソースで指定したResource RequestsResource Limitsの最大値、最小値を超えるPodを作成できないようにすることができる。

つまりLimitRangeリソースには主に以下が設定できる。

  • podで指定できるResource RequestsResource Limitsの最大値、最小値
  • podでResource RequestsResource Limitsが設定されなかった場合のデフォルト値

(ちなみにLimitRangeリソースはLimitRanger Admission Control pluginによって動く)

例:

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には影響を与えない。なぜならLimitRangeリソースはpodが作成されるときのみ働く(validateする)からだ。

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

ResourceQuotaリソース

ResourceQuotaリソースを使用することでnamespaceに対して使用可能リソース量を指定することができる。 LimitRangeリソースはpodごとに適用される。それに対して、ResourceQuotaリソースはnamespaceに対して適用される。 制限できるのは、CPU, メモリ、PersistentVolumeClaimのディスク使用量, podの数, (ユーザがそのnamespace内に作成することを許した)他のAPI objects。 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には影響がない。

(ちなみにResourceQuotaリソースはResourceQuota Admission Control pluginによって働く)

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のResource Requests、Resource Limitsのトータル値に対して制限がかかる。

kubectl describe quota で詳細がみれる。

% 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