名刺ケースを新調した

いつ買ったのか覚えていない名刺ケースをずっと使っていたのだけど、マチが無くて使いにくかったので買い換えることにした。minneで、REGENさんのものを購入。レザーでマチがあって、シンプルなものがいいなと思っていたのでぴったり。

名刺は結構入る。

普段持ち歩くわけではないので、かなりじっくり育っていくのだろうなあ。楽しみ。

I wrote my first Kubernetes custom-controller to operate OpenStack SecurityGroup

CRD/カスタムコントローラー周りの知識をつけたいと思ってちょこちょこ試していたのだけど、ようやく動くものを作ることができた。ものとしては takaishi/openstack-sg-controller で、kubebuilderを使って作った。これは何かというと、OpenStack上でKubernetesを動かしている時に使うもので、カスタムリソースを作るとそれに基づいてセキュリティグループを作成、ノードにアタッチしてくれる。

動作例

CRDを定義済み、Podが動いている状態で以下のようなカスタムリソースを作成する。

apiVersion: openstack.repl.info/v1beta1
kind: SecurityGroup
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: securitygroup-sample
spec:
nodeSelector:
role: master
name: sg-test
rules:
- portRangeMax: "8888"
portRangeMin: "8888"
remoteIpPrefix: "127.0.0.1/32"

これをapplyすると、openstack-sg-controllerによってSGの作成とアタッチが行われる(ログのメッセージ部分のみを切り出し)。

 "msg":"Debug: deletion timestamp is zero"}
"msg":"Creating SG","name":"sg-test"}
"msg":"Success creating SG","name":"sg-test","id":"13214109-2807-490a-80d2-1b9909d9d7b3"}
"msg":"Creating SG Rule","cidr":"127.0.0.1/32","port":"8888-8888"}
"msg":"Success to create SG Rule","cidr":"127.0.0.1/32","port":"8888-8888"}
"msg":"Deleting SG Rule","cidr":"","port":"0-0"}
"msg":"Success to delete SG Rule","cidr":"","port":"0-0"}
"msg":"Deleting SG Rule","cidr":"","port":"0-0"}
"msg":"Success to delete SG Rule","cidr":"","port":"0-0"}
"msg":"Info","Attach SG to Server":"0cf05471-90b1-4390-aa5d-7a36b73a0efa"}
"msg":"Info","Attach SG to Server":"eff5ef40-7f27-4402-9f5e-48c714eeb18c"}
"msg":"Info","Attach SG to Server":"02ddd567-b8c7-4f75-abef-8ad84117c9d0"}
"msg":"Debug: deletion timestamp is zero"}

コマンド実行結果は省略するが、OpenStack側を見ると、インスタンスにSGがアタッチされていることがわかる。

さて、コントローラーなのでカスタムリソースの状態が変わったらそれに追随してほしい。現時点ではrulesの変更とnodeSelectorの変更に対応している。試しにnodeSelectorを変更してみる。foo: barという条件を追加した。

apiVersion: openstack.repl.info/v1beta1
kind: SecurityGroup
metadata:
labels:
controller-tools.k8s.io: "1.0"
name: securitygroup-sample
spec:
nodeSelector:
role: master
foo: bar
name: sg-test
rules:
- portRangeMax: "8888"
portRangeMin: "8888"
remoteIpPrefix: "127.0.0.1/32"

この条件にマッチするノードは1台のみなので、SGはそのノードにだけついてほしい。

$ k get node
NAME STATUS ROLES AGE VERSION
nks-dev-master-001 Ready master 13d v1.11.6
nks-dev-master-002 Ready master 13d v1.11.6
nks-dev-master-003 Ready master 13d v1.11.6
$ k get node -l "foo=bar"
NAME STATUS ROLES AGE VERSION
nks-dev-master-001 Ready master 13d v1.11.6

applyすると、nodeSelectorにマッチしないノードからはSGがデタッチされる。

 "msg":"Debug: deletion timestamp is zero"}
"msg":"Info","Dettach SG from Server":"eff5ef40-7f27-4402-9f5e-48c714eeb18c"}
"msg":"Info","Dettach SG from Server":"02ddd567-b8c7-4f75-abef-8ad84117c9d0"}
"msg":"Debug: deletion timestamp is zero"}

さらに、何かの拍子でノードからSGが外れた場合には再度アタッチしてほしい。OpenStackのAPIをポーリングする必要があるのだが、 「Kubebuilder で Operator を作ってプライベートクラウドの操作を自動化してみる」を参考にして定期実行するようにした。

最後に、カスタムリソースを削除する。SGがデタッチされた後、削除される。

 "msg":"Info","deleting the external dependencies":"sg-test"}
"msg":"Info","Dettach SG from Server: ":"0cf05471-90b1-4390-aa5d-7a36b73a0efa"}
"msg":"Debug: instance not found","SecurityGroup":""}

処理の流れ

まず、カスタムリソースオブジェクトの作成や更新、削除のイベントが発生するとReconcileメソッドが呼び出される。Reconcileメソッドの先頭ではオブジェクトを取得し、存在しなければ削除されたとして終了する。

次に、DeletionTimestampをみて、削除リクエストを受けたオブジェクトかどうかを確認する。DeletionTimestampが設定されている場合、Kubernetesの外のリソースを削除する。具体的には、ノードからSGをデタッチし、SGを削除している。DeletionTimestampが設定されていない場合はFinalizerを設定しておく。

ここまで通過したら、作成時や更新時の処理が始まる。まずはカスタムリソースで指定した名前のSGが既に存在するかをチェックする。存在しない場合はSGを作成する。SGのルールについても、カスタムリソース側と比較してカスタムリソースに書いたようなルールになるように作成・削除を行う。

SGの作成・更新が終わったらノードへのアタッチを行う。OpenStack上でKubernetesを動かすと、NodeInfo.SystemUUIDがOpenStack上におけるインスタンスのIDとなることがわかったので、このIDを使ってノードのSGをチェックし、SGがアタッチされていない場合はアタッチする。

アタッチ先のノードの絞り込みにはNodeSelectorとして、ラベルで指定できるようにした。条件を厳しくしてカスタムリソースを更新した場合、例えばアタッチ先ノードがnode-01、node-02、node-03からnode-01のみに変わる、ということが考えられる。この時、既にSGがアタッチされているノードをチェックする必要がある。これを都度調べるのは困難そうなので、カスタムリソースのStatusにSGをアタッチしたノードのIDを保存することにした。これにより、以前アタッチしたノードがわかるので、条件にマッチしないノードからデタッチすることが可能となる。

所感

作り込みが大変だが、うまく使えばかなり便利だなあ。テストをどう書くかが悩みどころ。後、今の所SGの名前で有無を判別しており、事故がおこりかねないのでここをIDに変更したい。

CloudNativeMeetupTokyo#7 – Consul Kubernetes Integrationにおける Service Sync to Consulについて

2019年3月29日(金)に開催した CloudNativeMeetupTokyo#7 で、Consul ConnectとConsul Kubernetes Integrationについて紹介しました。参加してくれた方、登壇してくれた方、ありがとうございました。

今回はフォントをめちゃ太くしてみた。

KubernetesサービスのConsulへの同期の際、NodePortの場合にポッドが動いているノードのIPアドレスがConsulサービスとして登録されるのはなぜか?という質問をもらい、その辺り調査不足だったのでもう少し調べてみました。なお、サービス同期のドキュメントは https://www.consul.io/docs/platform/k8s/service-sync.html です。

さて、Kubernetesサービスの同期で使えるタイプはNodePort、LoadBalancer、ClusterIPです。そして、ExternalIPsにも対応しています。実際の挙動を見るためにそれぞれのサービスを用意して、Kubernetesの外からConsul APIで確認してみます。

$ k get svc | grep hello-consul
hello-consul-cluster-ip       ClusterIP      10.233.62.185                              8080/TCP         1m
hello-consul-external-ips     ClusterIP      10.233.38.236   80.11.12.10                      8080/TCP         1m
hello-consul-load-balancer    LoadBalancer   10.233.48.223   10.230.1.137                     8080:32702/TCP   1m
hello-consul-node-port        NodePort       10.233.12.208                              8080:32754/TCP   1m

NodePort

まずはNodePortです。KubernetesサービスのPodが動いているNodeのアドレスがConsulサービスとして登録されます。ソースコードは このあたり。 厳密には、ServiceのEndpointがあるNodeを取得するようです。確かに、素朴に考えるとNodePortのIPを使いそうですが、一工夫しています。Consul APIでServiceAddressとServicePortを見るとこんな感じ。

[alpaca@staging-bastion-10-230-0-24 ~]$ curl -sS http://127.0.0.1:8500/v1/catalog/service/hello-consul-node-port | jq -r ".[] | [.ServiceAddress, .ServicePort] | @tsv"
10.230.1.135 32754
10.230.0.103 32754
10.230.0.85 32754

LoadBalancer

次に、LoadBalancerです。LoadBalancerのIngressがConsulサービスとして登録されます。ソースコードは このあたり。Consul APIで見るとこんな感じ。

[alpaca@staging-bastion-10-230-0-24 ~]$ curl -sS http://127.0.0.1:8500/v1/catalog/service/hello-consul-load-balancer | jq -r ".[] | [.ServiceAddress, .ServicePort] | @tsv"
10.230.1.137 8080

ClusterIP

次はClusterIPです。KubernetesサービスのIPが登録されるのかな、と思いきや、各PodのIPアドレスがConsulサービスとして登録されています。ソースコードは このあたり

[alpaca@staging-bastion-10-230-0-24 ~]$ curl -sS http://127.0.0.1:8500/v1/catalog/service/hello-consul-cluster-ip | jq -r ".[] | [.ServiceAddress, .ServicePort] | @tsv"
10.233.70.181 8080
10.233.68.140 8080
10.233.74.34 8080

External IPs

最後にExternalIPsについてです。ExternalIPsが設定されている場合、その設定値がConsulサービスに登録され、Ingressなどは登録されません。ソースコードは このあたり

[alpaca@staging-bastion-10-230-0-24 ~]$ curl -sS http://127.0.0.1:8500/v1/catalog/service/hello-consul-external-ips | jq -r ".[] | [.ServiceAddress, .ServicePort] | @tsv"
80.11.12.10 8080

以上、KubernetesからConsulへのサービス同期についての追加調査でした。

コンタクトレンズを買ってみた

これまでずっと眼鏡を使っていて、コンタクトレンズはなんとなく使わなかったのだけど食わず嫌いというのもよくないだろう、ということで試しに買ってみることにした。あまり知識もないので、キャッチアップするという目的もある。

まずは近くの眼科に行って、コンタクトレンズの検査をした。視力を測った後にレンズが目に合うかどうかフィッティング。目に入れるのがとにかく難しくて、これは無理なのでは…というのが最初の感想。なんとか入れられるようになったので、3日分お試しレンズをもらって帰る。なお、ワンデータイプ。使ったら捨てる、くらいのシンプルなものが良いだろうと考えてこのタイプにした。

お試し用のレンズは1日分落として駄目にしてしまったが、残り2日分は使うことができた。フィッティングも問題なかったが、若干乱視の影響で見えにくいなと感じたので左右で度数を変えてもらった。通販とかで買うと安く済むらしいけど、リスクもあるので購入のたびに眼科で検査するつもり。今回は30日で6000円くらい(左右で度数が違うので3000円のパッケージが2つ)。ランニングコストは結構高いなー。毎日使うつもりはあまりないので実際はそこまででもないと思うけど。

後、今使っている眼鏡の度数がちょい高いのでは?という指摘を貰ったので、少し下げるための処方箋も出してもらった。新しく眼鏡を買って、疲れにくくなるか試すつもり。