meishi キットからはじめる自作キーボード入門

f:id:kyagi:20190603014435p:plain

meishi キットとは

遊舎工房さんで販売している自作キーボードの入門セットです。「名刺」サイズの基盤に4つのキーを載せたミニキーボードの組み立てを通して、自作キーボードの作業の概要を把握できるようになっています。費用は meishi キット(1,000 円ほど)と工房利用代金(2 時間で 500 円程度)をあわせて 2,000 円用意しておけば十分です。良心的! (^_^)

最終的に4つのキーを何に使うかは個人が自由に決められますが、デフォルトだと Ctl+z, Ctl+x, Ctl+c, Ctl+y の 4 つが割り当てられています。これは Windows/Mac それぞれで文字列のカット&ペーストのショートカットになっています。私は今のところ、Home, Ctrl+n, Ctrl+P, Enter キーにしています。これはターミナル上でコマンドの検索履歴を辿って Enter をする利用を意図しています。ネットで他の meishi キット作成者の活用法を探してみると仮想デスクトップの切り替えとして使っている方もいるようです。

yushakobo.jp

作業の流れ

大きく分けてハード作業とソフト作業の 2 つがあります。

1. ハード作業キーボードの組み立て

遊舎工房さんでの作業です。はんだづけ作業がメインになります。個人的には「はんだづけ」や「ダイオード」という用語だけで「なんだか難しそう (´・ω・`)」という印象を持っていましたが、工房内で作業していた方やスタッフのかたがとても親切に教えてくださったおかげで、ど素人の私でも 2 時間程度で作業を完了させることができました。ただし、基本的にはハンズオンのようにチューターが逐一指導してくれるわけではなく、meishi キットに付属している QR コードから辿れる以下の URL を教科書として自分で組み立てていくことを想定しているようです。

biacco42.hatenablog.com

ただ、上記のサイトはわかる人には教科書として機能するものの、私のようなど素人は完成図からそこに至るまでの工程を想像できず、一緒にいった友人や工房内で作業していた方、スタッフの方にいろいろご指導いただき、ようやく「やること」がわかったいうのが実情です。同じようなバックグラウンドの方は経験者と一緒に行くか、スタッフの方が比較的空いていそうな時間帯を狙っていくのがよいかもしれません。(´・ω・`)

2. ソフト作業(Mac 用)

ソフト作業はふたつのソフトウェア、QMK Toolbox と QMK Configurator(Web 上で利用可能) を使用します。QMK Toolbox は meishi キーボードのファームウェアを更新(つまり内部のフラッシュメモリを書き換え)するソフトウェアで、QMK Configurator がキーマップを設計するソフトウェアです。

2-1. QMK Toolbox のライブラリのインストール

Installation -> Dependencies -> macOS を参考にして homebrew で必要なライブラリをインストールします。途中でそれぞれ make が走るので結構時間がかかります(2018年の mac mini で15分ぐらい。2013年の macbook で 40分ぐらい)。

GitHub - qmk/qmk_toolbox: A Toolbox companion for QMK Firmware

2-2. QMK Toolbox のインストール

QMK.Toolbox.app.zip をダウンロードして、zip を展開するとアプリを起動できます。

Releases · qmk/qmk_toolbox · GitHub

2-3. QMK Configurator を利用して .hex ファイルの作成とダウンロード

Web サービスとして提供されている QMK Configurator で meishi キーボードの 4 つのキーにそれぞれ割り当てるキーを自分でドラッグアンドドロップをして設計できます。割り当てるキーが決まったら右上の COMPILE からコンパイルを実行した後、右下の FIRMWARE から .hex ファイルをダウンロードします。この .hex ファイルがファームウェアの設計図で、次の QMK Toolbox で読み込み、ファームウェアを更新します。

QMK Configurator

f:id:kyagi:20190603011402p:plain

私の場合、前述した通り、ターミナルの履歴実行の目的で Home, Ctrl+n, Ctrl+P, Enter キーの 4 つを割り当てています。Ctrl + P を割り当てたい場合、いったん「Quantum」タブで RCtl をドラッグアンドドロップから「ANSI」タブで P をドラッグアンドドロップする必要があります。ちなみにデフォルトの LCtrl は私の環境だとことえりが起動してしまい意図通りに動かなかったので RCtl を選んだほうがいいかもしれません。

f:id:kyagi:20190603011320p:plain

2-4. QMK Toolbox を利用してファームウェアの更新(フラッシュメモリの書き換え)

ダウンロードしたファームウェアの設計図の .hex を開いたあと、meishi キット上のリセットボタンを押した後に QMK Toolbox の Flash ボタンを押してフラッシュメモリに書き込みます。成功した場合「Thank you」で終わるメッセージですが、場合によっては Connecting to programmer: .avrdude: butterfly_recv(): programmer is not responding でうまくいかない場合があります。その場合、何度かリセット + Flash を繰り返すか、QMK Toolbox の Auto-Flash にチェックを入れてリセットボタンを「カチカチカチッ」と2回3回連続で押すことで成功したりします。このへんは遊舎工房で一度実施した通りです。

f:id:kyagi:20190603011553p:plain

何を割り当てるか

meishi キットの 4 つのキーにそれぞれ何を割り当てるかを考えるのは一番悩ましい部分である反面、一番面白い部分です。特定のアプリケーションのスイッチャー、仮想デスクトップの切り替え、スリープやスクリーンセイバー、プレゼン時の戻る/進む、その他私のようにターミナル限定で使うなど、使い方次第で可能性は広がります。(^_^)

※私の場合、通常のキーボードの左側で縦置きで運用し、上から仮想デスクトップ1、仮装デスクトップ2、アプリケーションウィンドウ、ミッションコントロールの 4 つで落ち着きました。

f:id:kyagi:20190603211238p:plain

次の一手

meishi キットで自作キーボードの作業概要が把握できたので、次は本格的なキットに挑戦してみようと思います。以前から分割キーボードに興味があったので、Ergo42 Towel あたりを候補にしています。 yushakobo.jp

みなさまもよい自作キーボードライフを! (^_^)/

おまけ

ハード作業からソフト作業まで動画で解説してくださっている方がいらっしゃいます。こちらを参考にするとはんだづけ作業の具体的な手順もわかります。

www.youtube.com

Kubernetes The Hard Way を AWS で実施する

f:id:kyagi:20180216023000p:plain

Kubernetes The Hard Way とは

Kubernetes の公式ドキュメントで Setup の項目をたどると kubeadm を使う例が記載されている。いくつかの機能はまだ alpha/beta であるものの公式でも kubeadm をクラスタ構築に使う流れを後押ししたいように見える。確かに kubeadm (や各ベンダの構築ツール)を使ったクラスタ構築は楽なのだけれど、ツール内部でやっていることを理解しない限りただツールに使われるだけになってしまうのと、トラブル発生時に原因把握もできなくなってしまう。ここでは従来よりスクラッチからのクラスタ構築として定評のある Kubernetes The Hard Way を AWS で実施していく。なお本家は GCP での構築例であり、AWS のサポートには消極的な様子。ここでは少し古いものの AWS 版の Hard Way を公開している方がいるのでそちらをベースに進めながら、AWS 依存の構成やバージョンのアップデートなどでハマった点を備忘録がてらに残しておく。

実際に Kubernetes The Hardway on AWS を完走するまでにやったことと注意点

  • VPC, Subnet, RouteTable, Internet Gateway などは awscli ではなく ClouldFormation で実施したほうが便利。何回かやり直すことになると思うので再実行しやすいのと振り返りにも役立つ。 f:id:kyagi:20190603024052p:plain
  • 各手順もそれぞれシェルスクリプトにまとめておくと再実行しやすいし、あとでプロビジョニングツール用に変換もできる。slawekzachcial さんの AWS 版では内部IP取得のために無駄にだらだら awscli を長く利用していたり、user-data に埋め込んでいるところがあるが、そこは固定で埋め込んでしまったほうが混乱がなくてよい。 f:id:kyagi:20190603024504p:plain
  • slawekzachcial さんの例では kubelet v1.9.0 だが kubelet v1.9.0 + ubuntu 18.04 だと couldn't propagate object cache: timed out waiting for the condition になるので controllers, workres ともに v1.12.7 にアップデートする。
  • pods "coredns-595db6f9cb-svfj5" is forbidden: User "system:node:ip-10-240-0-20" cannot patch pods/status in the namespace "kube-system" を解決するために clusterrole も更新する。
  • slawekzachcial さんの例では kubelet + cri-containerd で構築しているが kubelet + containerd に置き換える。cri-containerd はあくまで過渡期の一時用であり、containerd 1.1 から完全体となっているため。
  • 公式ドキュメントでも記載されているが、最低限のマシンスペックとして 2GB RAM, 2 CPUs が必要とされる。最初ケチって t3.nano で構築していたが、これだと workers の構築が完了し kubelet, kube-proxy を動作させると controllers 側が劇的に重くなるので t3.small にした。 https://kubernetes.io/docs/setup/independent/install-kubeadm/
  • EC2 の src/dst チェック外しておく。ENI(Elastic Network Interface) で作成した 10.200.0.0/16 のパケットが ENI に届けられず破棄されてしまう。
  • Route Table の「Main Table」を変更するのを忘れずに。
  • Securigy Group で DNS(udp:53) が塞がれていると、kube-dns service(10.32.0.10) から各 endpoint の coredns pod へ通信できない。いっそ Pod CIDR のネットワーク帯は All traffic を通すのも検証段階ではよいと思う(= All traffic from 10.200.0.0/24, 10.200.0.0/16)。これで node(ec2) からも dig が通るようになる。
  • coreDNS は以下が通れば OK。
ubuntu@ip-10-240-0-10:~/80-k8s-hardway$ dig +short @10.200.0.25 www.google.com kubernetes.default.svc.cluster.local kube-dns.kube-system.svc.cluster.local
172.217.164.164
10.32.0.1
10.32.0.10
  • 正しく完走できれば最終的には以下のようになるはず。 f:id:kyagi:20190603025052p:plain
  • Kubernetes のネットワークモデルについては以下のドキュメントがとてもわかりやすい。AWS を元にして書かれているのと、内部で amazon-vpc-cni-k8s という CNI プラグインを紹介しているがこれは Hard Way で ENI と Route Table を利用して POD CIDR への経路を用意したのと同じように思える。

sookocheff.com

GCP ではなく AWS で Kubernetes The Hard Way を実施するのは、AWS 依存の設定もあり、文字通り以上にハードだったけれど、そのぶん Controll-pane や worker 、そして CNI の概要が理解できたと思う。ただ完全には把握できず、やはり Kubernetes 難しいと改めて感じたのと、バージョンごとに機能や設定がどんどん変わっていくので追随していかないと構築例もあっというまに時代遅れになってしまうと感じた (_o_)

世界各都市の現地時刻を表示するコマンドを作った

仕事上、東京とロサンゼルスの時間を気にする必要があるので、時差を知りたい時は環境変数 TZ にそれぞれの都市を入れて date していたのを peco を使って拡張してみた。timedatectl list-timezones で抜き出したタイムゾーンのリストを peco でフィルタして TZ 変数に渡して date を叩くだけだが、なかなか便利。(^_^;

GitHub - kyagi/localtime: Select and show localtime in cities you pick

f:id:kyagi:20190308031720p:plain f:id:kyagi:20190308031726p:plain f:id:kyagi:20190308031734p:plain f:id:kyagi:20190308031747p:plain

使いかた

  • localtime と入力すると都市名を選ぶプロンプトが表示され、インクリメンタルサーチが走る(内部で peco を使用)
  • 複数の候補を選びたいときは peco の Ctrl+S を利用すればそのまま表示。
  • タイムゾーンを書いた設定ファイルを -c オプションで指定するとその都市の時刻を表示(ここでは東京とロサンゼルスを指定)

IntelliJ から Docker イメージの開発作業を行う

f:id:kyagi:20190303201557p:plain

IntelliJ の Docker Integration プラグインを使う

Dockerfile の修正、ビルド、イメージの動作確認を行うのに、今まではターミナルから docker build -> run -> attach をしていたけれど、全て IntelliJ のインターフェイスでサイクルを回した効率がよかった。

f:id:kyagi:20190303201311p:plain

f:id:kyagi:20190303201423p:plain

IntelliJ だとイメージやコンテナの一覧が見やすいし、右クリックから docker コマンドの操作は大体可能。

f:id:kyagi:20190303203144p:plain

ローカルの k8s クラスタで動かしている各 pod の内部情報は kubectl や k8s dashboard からも参照できるものの、IntelliJ からのほうが見やすい情報もある。

最近はすっかり IntelliJ (JetBrains IDE) の環境に染まってしまった。そういえば git も IntelliJ 経由でしか使用していない。どのツールもそうだけど、最初は使い方や基本概念を学ぶためにコマンドラインから入り、慣れてきた後はより効率的なインターフェイスで利用するのがベストだと思う。かつてはそれが Emacs だったけれど、数年前から完全に IntelliJ に置き換わってしまった感がある。年々 JetBrains 製プロダクトへの依存度があがっていることは自覚しているが、これほど完成された作業環境をお手軽に、柔軟に、素早く、しかも管理コストを少なく構築できるソフトウェアは他にないのではないだろうか。(^_^;

mac mini 2018 向けのディスプレイケーブルとマイク

年末に mac mini 2018 を購入した際に発生した問題がふたつ。

  • それまでのディスプレイケーブルが使えない。USB Type C to HDMI のものを書い直さないといけない。
  • マイクがない(マイクがないと日課のオンライン英会話が mac mini できない)。

しょうがないので以下のディスプレイケーブルとマイクを購入した。mac mini には USB 3.0 ポートは 2 つしかなく、HHKB とマウスで埋めてしまっていたのでマイク用に USB Type C から USB 3.0 へ変化するアダプタも一緒に購入。きちんと動作するのか若干不安だったが、どちらも問題なく動いたのでよかった。他のデバイスもそうだが USB Type C が普及するまではもう少し時間がかかりそうだ...。

ちょっとひっかかったのが外部マイクを接続した際に「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」からマイクに接続するアクセス権をアプリケーションごとに設定する必要があった。

f:id:kyagi:20190226000044p:plain

以上、mac mini 2018 で同様にケーブル難民、マイク難民となっている方への情報提供になれば幸いです。(^_^;

Docker Desktop for Mac の Kubernetes 機能を使用して Mac 上に Kubernetes クラスタを構築する

f:id:kyagi:20180216023000p:plain

Mac でお手軽 Kubernetes クラスタ構築

難しい、となかなか敷居が高くなりがちな Kubernetes だが Docker Desktop for Mac の Kubernetes 機能を利用すると、お手軽に Mac に Kubernetes クラスタを構築することができる。

Docker Desktop for Mac とは?

Mac の Docker 環境の最新版。少し前までは Docker Toolbox が使用されていたが、現在では Docker Desktop for Mac がスタンダードになっている。18.06 から Kubernetes 機能も搭載された。

f:id:kyagi:20190224163753p:plain

インストール

Docker.dmg をダウンロードしてインストールする。 https://hub.docker.com/editions/community/docker-ce-desktop-mac

Kubernetes 機能の有効化

Preferences から Kubernetes タブに進んで、「Enable Kubernetes」にチェックを入れるとバックグラウンドで Kubernetes が動き出す。

f:id:kyagi:20190224164507p:plain

Kubernetes Dashboard の有効化

以下のドキュメント通りに kubectl createkubectl proxy を打った後、指定の localhost:8001 の URL をブラウザで開く。

https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

Kubernetes Dashboard のアクセス

Dashboard のアクセスには認証が必要になる。ネームスペース kube-system で起動している deployment-controller のトークンを利用してアクセスする。

$ kubectl -n kube-system get secret | grep deployment-controller | awk '{print $1}' | xargs -I{} kubectl describe secret {} -n kube-system | grep ^token | awk '{print $2}'

f:id:kyagi:20190224164556p:plain f:id:kyagi:20190224164612p:plain

go の docker イメージや拙作の rod(統合 REPL 環境)を動かす

$ kubectl run golang --image=golang:1.11.5-stretch --command -- tail -f /dev/null
deployment.apps "golang" created

$ kubectl run rod --image=kyagi/rod --command -- tail -f /dev/null
deployment.apps "rod" created

$ kubectl get deployment
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
golang    1         1         1            1           19s
rod       1         1         1            1           2m
ubuntu    1         1         1            1           5d

f:id:kyagi:20190224170512p:plain

まとめ

Docker Desktop for Mac を開発で利用しているのであれば、せっかく追加された Kubernetes 機能を使わないのはもったいない。難しいと思われがちな(実際、自前で構築するのは難しい) Kubernetes クラスタをお手軽に構築できるので「Kubernetes ってこんな感じなんだ」とまずは感触をつかみたい方にもオススメの方法と言える。いったん構築してしまえば kubectl や Dashboard から「作って壊す」のはもちろん、いろいろな操作ができる。Docker 単体や Docker compose を使って開発環境を構築するよりも効率的なことも多い はず。(^_^)

※個人的には imagePullPolicyAlways から IfNotPresent にすることで docker build したイメージを dockerhub に push する手間がなくなり、修正&確認のサイクルが回しやすくなるのがありがたい。イメージを修正した場合は build した後に、単にその pod を削除して自動再生された pod で確認すればよい。( ・ㅂ・)و ̑̑

Go 1.11 からはじめるプロジェクトでは、パッケージマネージャは dep ではなく go mod(Go Modules) を使おう

go mod(Go Modules) がこれからのスタンダード

Go を使いはじめるにあたってパッケージマネージャを探した時の話。ghq や glide といった過去の遺産に加えて godep, dep (ややこしい) と百花繚乱な様子が窺え、初見では、将来的に何が天下統一してくれるのか、わからずじまいだった。公式で紹介されている dep を使えばいいのかな、と会社の同僚に相談したところ dep すらももう古くて、go mod(Go Modules) がこれからのスタンダードになるという話だった。

確かに dep のページでも "official experiment." と明記されている。

github.com

正式採用された go mod(Go Modules) と過去のツールたち

パッケージマネージャとして Go Module(GO111MODULE) のプロポーザルが正式に受け入れられたことと、過去の実験的な試行(GO15VENDOREXPERIMENT)として百花繚乱のツール群が紹介されている。

PackageManagementTools · golang/go Wiki · GitHub

Go 1.11 から go mod が試験的に組み込まれ、1.13 で完全に組み込まれるとのこと。vgo はあくまで go mod のプロトタイプなので 1.11 からは使う必要がない。

Modules · golang/go Wiki · GitHub

Go 1.11 からはじめるプロジェクトでは dep ではなく go mod(Go Modules) を使おう、という議論

github.com

まだ dep を使っていないプロジェクトで 1.11 から開発をはじめる場合dep を使う意味はほとんどありません。
dep は様々な問題を抱えており将来的には使われなくなるので、そこに学習の時間を割くのは避けた方がいいでしょう。
新しく 1.11 から開発をはじめるのであれば、dep ではなく Go modules を使いましょう。

Kubernetes プロジェクトも dep は完全に飛ばして godep から Go modules への移行を計画しています
(godep -> dep はダメだったので godep -> go modules へ移行予定)。
当初 Kubernetes プロジェクトは godep から dep への移行を検討したものの結局うまくいかなかったのです。

go mod の使い方

https://github.com/golang/go/wiki/Modules#quick-start

go mod init してから go build するとライブラリが自動的にダウンロードされて $GOPATH/pkg に格納される。この点は sbt build と似ている。ソースの中で使用されている外部ライブラリが検出され go.mod と go.sum という二つのファイルが生成されるが、go.mod がバージョン情報を記録するファイルで go.sum はその生情報となっている。外部ライブラリのバージョンを更新したい場合は go.mod を編集した後に go build し直す、という流れになる。私は Ruby ユーザなので go.mod = Gemfile, go.sum = Gemfile.lock というようなイメージで捉えている。 ₍₍(ง˘ω˘)ว⁾⁾

$ cd $GOPATH && mkdir hello && cd hello
$ pwd
/home/kyagi/lab/go/hello 
$ vi hello.go

$ go mod init github.com/kyagi/hello/
go: creating new go.mod: module github.com/kyagi/hello/

$ go build
go: finding rsc.io/quote v1.5.2
go: downloading rsc.io/quote v1.5.2
go: finding rsc.io/sampler v1.3.0
go: finding golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
go: downloading rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c

$ find $GOPATH/pkg/mod | grep quote
/home/kyagi/lab/go/pkg/mod/cache/download/rsc.io/quote
/home/kyagi/lab/go/pkg/mod/cache/download/rsc.io/quote/@v
/home/kyagi/lab/go/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.2.mod
(... snip ...)

$ ls
go.mod  go.sum  hello.go

$ cat go.mod
module github.com/kyagi/hello/

require (
    rsc.io/quote v1.5.2
)

$ cat go.sum
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

$ go run hello.go
Hello, world.

GOPATH のいままでとこれから

GO111MODULE になると、$GOPATH は go build 時に $GOPATH/pkg/mod に必要なライブラリがダウンロードされるだけの役割になる、と個人的に理解している。つまり $GOPATH はほとんどシステム的なライブラリ置き場になる(Ruby でいう .gem や Scala でいう .ivy2 に近い役割のディレクトリ)。これに伴ってプロジェクトのディレクトリも GO111MODULE 以前に推奨されていた $GOPATH/src/ ではなく、どこにおいてもいいという認識でいる。

いままで) $GOPATH/src/sandbox で開発をすることが推奨されていた
これから) どこで開発をしてもよい(/tmp/sandbox でも $HOME/lab/sandbox でも)。go build 時に go.mod で指定しているライブラリは $GOPATH/pkg/mod にダウンロード(キャッシュ)される

プロジェクトのディレクトリをどこにおいてもよく(どこで開発してもよく)、GOPATH は go get 時に $GOPATH/src に、 go build 時に $GOPATH/pkg/mod 配下にライブラリがダウンロードされるキャッシュ置き場としての役割になったので、個人環境の設定は以下のように変更している。

いままで) export GOPATH="$HOME/lab/go"
これから) export GOPATH="$HOME/.go"

※そもそも SRE 的な立場でしか Go を触っていないので間違っていたらご指摘いただけるとありがたいです。 (´・ω・`)

parallel で k8s の複数 pod にコマンドを「並列」で流して結果を保存する

f:id:kyagi:20180216023000p:plain

k8s の複数 pod を横断してかつ「並列」でコマンドを実行する必要がある場合、どのような手段があるだろうか。例えばパフォーマンス測定のために jstat を複数 pod で実行したい場合、以下のように kubectl に pod を次々と「直列」で渡していっては、各 pod でコマンドの実行時間のズレが生じてしまう。何よりも jstat のように標準出力(stdout)を取られてしまうコマンドを kubectl に渡した場合、ひとつひとつ Ctrl+C していかなければならず、目的を果たすことができない。

複数 pod に「直列」でコマンドを実行する例

  1. pod リストを取得する
    $ kubectl get pods -lapp=myapp -o jsonpath="{.items[*].metadata.name}" | xargs -n 1 echo
    myapp-78ccc84d99-2cr24
    myapp-78ccc84d99-2pmp7
    myapp-78ccc84d99-59f4g
    myapp-78ccc84d99-5bwkc
    myapp-78ccc84d99-5d8wc
    myapp-78ccc84d99-5fzsd
    myapp-78ccc84d99-67qkr
  2. 取得した pod リストを kubectl に渡して date コマンドを実行するものの、最初の pod と最後の pod で実行時間のズレが生じてしまう。ターミナルを pod 数分開いて「せーの」で実施すれば実行時間のズレはある程度是正できるものの、数十から数百 pod ある環境では現実的ではない。
    $ while read p; do kubectl exec -it $p -c myapp -- date; done < <(kubectl get pods -lapp=myapp -o jsonpath="{.items[*].metadata.name}" | xargs -n 1 echo)

複数 pod に「並列」でコマンドを実行する例

  1. pod リストを取得する。parallel に渡すのに適したフォーマットにする。
    $ kubectl get pods -lapp=myapp -o jsonpath="{.items[*].metadata.name}"
    myapp-78ccc84d99-2cr24 myapp-78ccc84d99-2pmp7 myapp-78ccc84d99-59f4g myapp-78ccc84d99-5bwkc myapp-78ccc84d99-5d8wc myapp-78ccc84d99-5fzsd myapp-78ccc84d99-67qkr myapp-78ccc84d99-6glcz myapp-78ccc84d99-6htmf myapp-78ccc84d99-7dchn
  2. parallel を使って並列でコマンドを流す。jstat は標準出力(stdout)に連続して出力しようとするので、ここでは標準出力を out ディレクトリ配下に吐き出すように指定する。
    $ parallel --results out kubectl exec -it {} -c myapp -- /usr/bin/jstat -gcutil -t 1 1000 ::: $(kubectl get pods -lapp=myapp -o jsonpath="{.items[*].metadata.name}")
  3. 適当に時間が経過したところで Ctrl+C で jstat を中止する。out ディレクトリ配下を確認すると pod 名のディレクトリが自動作成され、jstat の標準出力が吐き出されていることが確認できる。
    $ tree out/
    out/
    └── 1
        ├── myapp-78ccc84d99-2cr24
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-2pmp7
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-59f4g
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-5bwkc
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-5d8wc
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-5fzsd
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-67qkr
        │   ├── stderr
        │   └── stdout
        ├── myapp-78ccc84d99-6glcz
        │   ├── stderr
        │   └── stdout
        └── myapp-78ccc84d99-6htmf
            ├── stderr
            └── stdout
    $ cat out/1/myapp-78ccc84d99-*/stdout
    Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
           339412.0  36.71   0.00  33.87  85.15  97.85  96.46 112186 6009.403   232   25.555 6034.958
           339413.1  36.71   0.00  62.38  85.15  97.85  96.46 112186 6009.403   232   25.555 6034.958
           339414.1  36.71   0.00  89.70  85.15  97.85  96.46 112186 6009.403   232   25.555 6034.958
           339415.1   0.00  37.42  24.00  85.20  97.85  96.46 112187 6009.455   232   25.555 6035.011
           339416.1   0.00  37.42  61.76  85.20  97.85  96.46 112187 6009.455   232   25.555 6035.011
           339417.1   0.00  37.42  89.42  85.20  97.85  96.46 112187 6009.455   232   25.555 6035.011
    Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
           339551.1   0.00  39.96  68.38  46.40  97.99  95.55 107987 6136.845   238   49.173 6186.018
           339552.1   0.00  39.96  93.15  46.40  97.99  95.55 107987 6136.845   238   49.173 6186.018
           339553.1  44.37   0.00  32.62  46.43  97.99  95.55 107988 6136.903   238   49.173 6186.075
           339554.1  44.37   0.00  88.96  46.43  97.99  95.55 107988 6136.903   238   49.173 6186.075
           339555.1   0.00  41.82  17.52  46.49  97.99  95.55 107989 6136.963   238   49.173 6186.135
           339556.1   0.00  41.82  64.14  46.49  97.99  95.55 107989 6136.963   238   49.173 6186.135
           339557.1  39.57   0.00   4.16  46.59  97.99  95.55 107990 6137.027   238   49.173 6186.200
    Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
           339522.0   0.00  35.20  92.33  45.48  97.82  96.47 112235 6360.675   236   25.843 6386.519
           339523.0  37.73   0.00  34.98  45.53  97.82  96.47 112236 6360.728   236   25.843 6386.572
           339524.0  37.73   0.00  79.99  45.53  97.82  96.47 112236 6360.728   236   25.843 6386.572
           339525.0   0.00  36.40  20.69  45.58  97.82  96.47 112237 6360.781   236   25.843 6386.624
           339526.0   0.00  36.40  64.99  45.58  97.82  96.47 112237 6360.781   236   25.843 6386.624
           339527.0  35.51   0.00  10.36  45.64  97.82  96.47 112238 6360.835   236   25.843 6386.678
           339528.0  35.51   0.00  55.57  45.64  97.82  96.47 112238 6360.835   236   25.843 6386.678
    Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT
           339454.0  38.18   0.00  38.61  44.10  97.78  95.53 114078 6133.794   236   23.938 6157.731
           339455.1  38.18   0.00  82.87  44.10  97.78  95.53 114078 6133.794   236   23.938 6157.731
           339456.1   0.00  41.97  17.91  44.16  97.78  95.53 114079 6133.854   236   23.938 6157.792
           339457.1   0.00  41.97  51.55  44.16  97.78  95.53 114079 6133.854   236   23.938 6157.792
           339458.1   0.00  41.97  82.24  44.16  97.78  95.53 114079 6133.854   236   23.938 6157.792
    (... snip ...)

kubectl と parallel の組み合わせは相性がよいので組み合わせでいろいろなことができそう。(๑•̀ㅂ•́)و✧

※なお、複数 pod を横断してログを tail するには stern という素晴らしいツールがある。

tail -f /dev/null で調査用コンテナを起動してそのままずっと保持しておく

f:id:kyagi:20180216163134p:plain

Docker コンテナを ENTRYPOINT や CMD の指定なしにちょっとだけ立ち上げたいことがある。例えば Docker イメージで提供されている OS の環境で少しだけ作業をしたい場合など。そんな時は大抵 -d でバックグラウンド指定して sleep コマンドで適当に長い秒数を指定してコンテナを起動した後に docker exec -it /bin/bash してその OS に入っているパッケージやそのバージョンを調査していた。

OS の調査用コンテナを起動して 1 時間後にコンテナを終了させる

$ docker run -d ubuntu sh -c 'sleep 3600' # 1 時間後にコンテナは終了する
5ec839f1cdc7ec657583a4fa992d556946d3b7775e9991175da68653020511ed

$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS               NAMES
5ec839f1cdc7        ubuntu              "sh -c 'sleep 3600'"   7 seconds ago       Up 7 seconds                            eloquent_liskov

$ docker exec -it 5ec839f1cdc7 /bin/bash

root@5ec839f1cdc7:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

root@5ec839f1cdc7:/# exit
exit

これだと毎回コンテナを起動し直すはめになるので、調査に複数日かかったりコンテナ状態を保持しておきたい場合は無限ループを与えていた。

OS の調査用コンテナを起動してそのままずっと保持しておく(無限ループ編)

$ docker run -d ubuntu sh -c 'while :; do sleep 3600; done' # 数字はなんでもよい
69f6b10563b922e53139ed5acb5ceb9526daebc857f4a6cc031833f50e1ca04d

ただ、無限ループを与えるよりも tail -f /dev/null を使った方が簡単だいうことを知った。

OS の調査用コンテナを起動してそのままずっと保持しておく(tail -f /dev/null 編)

$ docker run -d ubuntu sh -c 'tail -f /dev/null'
1a258e96395f01d47ec976082ec2d99c5ee5b70949cef02a3108df2255240ab0


$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
1a258e96395f        ubuntu              "sh -c 'tail -f /d..."   3 minutes ago       Up 3 minutes                            unruffled_wilson
69f6b10563b9        ubuntu              "sh -c 'while :; d..."   4 minutes ago       Up 4 minutes                            gracious_wescoff
5ec839f1cdc7        ubuntu              "sh -c 'sleep 3600'"     5 minutes ago       Up 5 minutes                            eloquent_liskov

$ docker exec -it 1a258e96395f /bin/bash
root@1a258e96395f:/#


root@1a258e96395f:/# apt-get update
(... snip ...)

root@1a258e96395f:/# apt-get install dateutils
(... snip ...)

root@1a258e96395f:/# dateutils.ddiff 2018-04-01 2018-12-31 # 今年が終わるまで後何日?
274

確かにこれは楽だ。(´・ω・`)

追記:

docker run -itd ubuntu /bin/bash でバックグラウンドで起動してから、アタッチ(docker attach)とデタッチ(Ctrl+P, Ctrl+Q) するほうがもっと楽だった。(´・ω・`)

fluentd プラグイン sampling-filter と numeric-monitor を組み合わせて nginx のアクセスログから標本を抽出してパフォーマンス測定を実施する

ログの一部を抽出して計算するには sampling-filter + numeric-monitor の組み合わせがぴったり!

大規模データ収集のログコレクタとして fluentd を使用している場合、全部のログの中から一部を抽出してデータ分析を行いたい場合がないだろうか。例えば、サービスのレスポンスタイムが遅くなったり(早くなったり)していないかを測定することができれば、異常検知に役立つ。つまり、ログ(母集団)をデータベースまで運ぶ間にちょっとつまみ喰い(標本)するといった具合だ。平均や特定のパーセンタイル値を出しておけばパフォーマンス監視としては十分だと思う。こういった要求を満たすのに便利な fluentd プラグインの組み合わせが fluent-plugin-sampling-filterfluent-plugin-numeric-monitor だ。

nginx のアクセスログの一部を抽出してパフォーマンス測定を行う場合

ここでは具体例として nginx のアクセスログを考えてみる。例えば以下のアクセスログから vhost ごとの reqtime をサンプリングしてパフォーマンスの指標として vhost ごとの平均と 99 パーセンタイルを出してみる。

access.log

time:15/Mar/2018:01:00:00 +0900  host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.001   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:00 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.001   vhost:www.ayakumo.net
time:15/Mar/2018:01:00:00 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.002   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:00 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.002   vhost:www.ayakumo.net
time:15/Mar/2018:01:00:00 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.003   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:02 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.004   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:03 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.005   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:05 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.006   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:06 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.007   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:08 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.008   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:08 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.009   vhost:blog.ayakumo.net
time:15/Mar/2018:01:00:08 +0900 host:10.0.0.123 forwardedfor:12.123.123.123 req:GET /index.html HTTP/1.1    status:200  size:123    referer:-   ua:-    reqtime:0.010   vhost:blog.ayakumo.net

fluent プラグインの組み合わせによる実装

以下 4 つのプラグインを組み合わせることによりパフォーマンス測定を実装する。rewrite-tag-filter は昔の rewriteruleN 記法ではなく新しい <rule> ... </rule> 記法を使用することでマッチした部分をタグとして利用できるのが素晴らしい。

sample_td_agent.conf

<source>
  @type forward
  port 88888
</source>

<source>
  @type prometheus
</source>

<match nginx.access>
  @type rewrite_tag_filter
  <rule>
    key vhost
    pattern ^(.*)\.ayakumo\.net$
    tag nginx.access.$1
  </rule>
</match>

<match nginx.access.*>
  @type sampling_filter
  interval 100 # 1/100 sampling
  sample_unit all
  add_prefix sampled
</match>

<match sampled.nginx.access.*>
  @type forest
  subtype numeric_monitor
  <template>
    tag reqtime.${tag_parts[3]}
    unit minute
    aggregate all
    monitor_key reqtime
    percentiles 50,99
  </template>
</match>

<match reqtime.*>
  @type forest
  subtype copy
  <template>
    <store>
      @type prometheus
      <metric>
        name reqtime_percentile_50_${tag_parts[1]}
        type gauge
        desc The 50 percentile of reqtime per minute.
        key  percentile_50
      </metric>
      <metric>
        name reqtime_percentile_99_${tag_parts[1]}
        type gauge
        desc The 99 percentile of reqtime per minute.
        key  percentile_99
      </metric>
    </store>
    <store>
      @type stdout
    </store>
  </template>
</match>

<match **>
  @type null
</match>

nginx のアクセスログを擬似的に発行するために fluent-post を以下のように使用する。reqtime のところは $RANDOM を使う代わりに shuf -i 1-100 -n 1 でもよいかもしれない。

fluent-post.sh

for sld in www blog; do
  for i in `seq 1 100`; do
    r=$(($RANDOM % 100))
    /opt/td-agent/embedded/bin/fluent-post -p 88888 -t nginx.access -v time=time:15/Mar/2018:01:00:01 -v host=10.0.0.123 -v forwardedfor=12.123.123.123 -v req="GET /    index.html HTTP/1.1" -v status=200 -v referer="-" -v ua="-" -v reqtime=0.${r} -v vhost=${sld}.ayakumo.net
  done
done

こうすると @type stdout で td-agent.log に出力している numeric-monitor の基本機能により avg や num は自動的に計算されていることがわかる。www と blog の 2 つの sld(Second Level Domain) に対して 100 回(seq 1 100) 投げているが、サンプリング粒度を 1/100 にしているので(interval 100) 標本のサイズはそれぞれ 10 個づつ("num": 10) となる。

td-agent.log

2018-03-15 01:02:32 +0900 [info]: out_forest plants new output: numeric_monitor for tag 'sampled.nginx.access.www'
2018-03-15 01:02:32 +0900 reqtime.blog: {"num":10,"min":0.1,"max":0.98,"avg":0.587,"sum":5.87,"percentile_50":0.6,"percentile_99":0.94}
2018-03-15 01:02:32 +0900 [info]: out_forest plants new output: copy for tag 'reqtime.www'
2018-03-15 01:02:32 +0900 reqtime.www: {"num":10,"min":0.14,"max":0.79,"avg":0.433,"sum":4.33,"percentile_50":0.36,"percentile_99":0.7}

prometheus のメトリクス名としているパーセンタイル 50, 99 も取得できる。

$ curl -s http://localhost:24231/metrics/ | grep -v '^#'
reqtime_percentile_50_blog 0.6
reqtime_percentile_99_blog 0.94
reqtime_percentile_50_www 0.36
reqtime_percentile_99_www 0.7

fluentd のプラグインは便利なものがたくさんそろっているので、組み合わせ次第でいろいろなことができて本当に助かっている。(^_^)