Getting started with Amazon EKS の地雷処理

2 年ぶりに Kubernetes を使います。以前、仕事で AWS とオンプレそれぞれにクラスタを構築して運用していたもの、構築部分は k8s プロフェッショナルなエンジニアが身近にいたせいでおんぶにだっこの状態でした。過去に Kubernetes The Hard Way を AWS で実施する をしたものの、2 年も経つとほとんど忘れてしまっています。今回は EKS を使います。

まずは動作を確認するために AWS が提供している Getting Started with Amazon EKS に則って進めてみました。基本的にはドキュメントの通りでしたが、AWS 側の都合で以下 2 点の修正が必要でしたのでワークアラウンドを残しておきます。

地雷1. NAT Gateway が作成できない AZ が存在する

Region に ap-northeast-1(Tokyo) を指定すると NAT Gateway を作成できずに Cloudformation がエラーになりました(個人的な経験だと法人だと a は使用できますが個人だと b と c しか使用できないので a 側にリソースを作成できないのはアカウントに依存するのかもしれません)

f:id:kyagi:20220206180557p:plain

ワークアラウンドとしては単純に ap-northeast-1 以外のリージョンを選びました。最終的には ap-northeast-1 のどの AZ が問題なのかを明確にしたほうがいいですが、試用目的であればその限りではありません。今回は代わりに us-east-1(N.Virginia) を選びました。

地雷2. 選択した Region によっては Cloudformation で指定している AZ(a と b) でリソースに空きがない場合がある

us-east-1 を選択してクラスタを作ろうとしたのですが、AZ a にリソースの空きがないのが理由で AWS マネージメントコンソールがエラーになりました。

f:id:kyagi:20220206181304p:plain

ワークアラウンドとしては a 以外の AZ を選びました。みんな a を使っているのかもしれないので、なるべく混んでいなそうな(?) e と f を選びます。AWS 側が提供している Cloudformation の YAML ファイル をローカルに DL して以下のように修正します。

$ wget https://amazon-eks.s3.us-west-2.amazonaws.com/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml
$ cp amazon-eks-vpc-private-subnets.yaml amazon-eks-vpc-private-subnets-modified.yaml

$ ruby -e "puts ('a'..'z').to_a.find_index('a')"
0
$ ruby -e "puts ('a'..'z').to_a.find_index('b')"
1
$ ruby -e "puts ('a'..'z').to_a.find_index('e')"
4
$ ruby -e "puts ('a'..'z').to_a.find_index('f')"
5

$ vi amazon-eks-vpc-private-subnets-modified.yaml
$ diff -Nurp amazon-eks-vpc-private-subnets.yaml amazon-eks-vpc-private-subnets-modified.yaml
--- amazon-eks-vpc-private-subnets.yaml 2020-10-30 07:36:35.000000000 +0900
+++ amazon-eks-vpc-private-subnets-modified.yaml    2022-02-06 15:19:24.000000000 +0900
@@ -168,7 +168,7 @@ Resources:
       MapPublicIpOnLaunch: true
       AvailabilityZone:
         Fn::Select:
-        - '0'
+        - '4'
         - Fn::GetAZs:
             Ref: AWS::Region
       CidrBlock:
@@ -189,7 +189,7 @@ Resources:
       MapPublicIpOnLaunch: true
       AvailabilityZone:
         Fn::Select:
-        - '1'
+        - '5'
         - Fn::GetAZs:
             Ref: AWS::Region
       CidrBlock:
@@ -209,7 +209,7 @@ Resources:
     Properties:
       AvailabilityZone:
         Fn::Select:
-        - '0'
+        - '4'
         - Fn::GetAZs:
             Ref: AWS::Region
       CidrBlock:
@@ -229,7 +229,7 @@ Resources:
     Properties:
       AvailabilityZone:
         Fn::Select:
-        - '1'
+        - '5'
         - Fn::GetAZs:
             Ref: AWS::Region
       CidrBlock:

同時に aws cloudformation のパラメータも修正します(--template-url https://... ではなく --template-body file://... で修正したファイルに置き換える)

$ diff -Nurp a.txt b.txt
--- a.txt   2022-02-06 15:34:18.000000000 +0900
+++ b.txt   2022-02-06 15:33:28.000000000 +0900
@@ -1 +1 @@
-aws cloudformation create-stack --region ap-northeast-1 --stack-name my-eks-vpc-stack --template-url https://amazon-eks.s3.us-west-2.amazonaws.com/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml
+aws cloudformation create-stack --region us-east-1 --stack-name my-eks-vpc-stack --template-body file://amazon-eks-vpc-private-subnets-modified.yaml

あとはドキュメント通りに進んで、クラスタが作成できました。最終的にはひとつひとつの AWS リソースの構成を自分で設定していく予定ですが、動作確認のためにはこれでよしとします。個人で試用している場合、料金も気になります。立ち上げたいサービスの Docker イメージの準備をしてからクラスタを作成し、動作確認後にクラスタと Cloudformation のスタックを削除する、といった流れがよさそうです。

その他、自分の Mac にインストールしている AWS ツールが若干古かったようで下記の設定をする必要がありました。事前に確認しておくといいかもしれません。

事前準備

参考情報

EKSでの認証認可 〜aws-iam-authenticatorとIRSAのしくみ〜 - もうずっといなかぐらし

Github Sponsors に登録するには

大きな流れ

  • Introduction (5,000 characters) と Short bio (250 characters) を書く(自己紹介と略歴)
  • Sponsor tiers や Goals を決める($1/月で 10 人が目標、スポンサーになってくれた方への特典など)
  • Github と連携する Stripe のアカウントを作成し、銀行口座を入力する(運転免許証やパスポートといった顔画像認識処理あり)
  • DocuSign を使用して W-8BEN フォーム(米国源泉税に対する受益者の非居住証明書) に入力し署名する

参考: W-8BEN https://www.irs.gov/pub/irs-pdf/fw8ben.pdf

提出から審査完了までの時間

私の場合は提出から審査完了まで 5 分でした。むしろ提出する前段階で Introduction などの英文を書いていたり Stripe で本人確認をしたり、DocuSign でフォームを埋めたりしている時間のほうが長く、1 時間ほどかかりました。日本時間の深夜に申請しましたが、時差を考えると西海岸で人間が働いているには少し早すぎる時間です。人間が処理しているようなメッセージが表示されたように記憶していますがあやふやです(Our staff will be checking ...)。どちらかというと審査処理もある程度自動化が進んでいて自動化で判断ができない場合に人間が処理している可能性のほうが高そうです。

f:id:kyagi:20220204224008p:plain

オンラインでの本人確認プロセスが一般化してきている

W-8BEN のフォームの入力は 7 年前に US に銀行口座を作成して以来でしたが、当時は同フォームを紙で送ったのに、今は DocuSign を利用してオンライン上で署名が可能なことに時代を感じました。DocuSign は仕事においても US の取引相手と契約書を締結するのに使用しています(日本ではクラウドサインのほうが有名でしょうか)。本人確認のための顔画像認識処理は Coincheck のアカウントを作った場合とほぼ同様のように見受けられました。オンラインでの本人確認プロセスが一般化してきていることを実感しました。フレームワークにライブラリとして実装される日も遠くなさそうです(もうあるのかな?)。

Shamshir が Github Actions に対応しました

従来のスタンドアローンでの起動に加えて、Github Actions からも起動できるようになりました。よろしければお使いください。

https://github.com/kyagi/shamshir

f:id:kyagi:20220204204204p:plain f:id:kyagi:20220204204219p:plain

        uses: kyagi/shamshir@v1

トリガーのイベントとして「PR のレビューや修正」だと数が多くて Github Actions の実行時間の制限が心配かもしれません。その場合、cron 起動にして「平日の 9-19時(JST=UTC+9)に 毎時 0, 30 分の 2 回ずつ」であれば 1 レポジトリ20 回/日なので大丈夫だと思います。

on:
  pull_request_review:
    types: [submitted, edited, dismissed]
  pull_request:
    types: [edited, labeld, unlabeled]
on:
  schedule:
    - cron: '0,30 0-10 * * 1-5'

以下は振り返りになります。

独自 DSL である Github Actions の理解が難しい

Github API Reference をみてスタンドアローンの js を書くのはスムーズに行きました。一方 Github Actions のドキュメントを読んで YAML を書くのは恐ろしく手間でした。これは背景知識の差によるものです。あらかじめ Rest API とはどういうものかを知っているかからはじめるのと、独自 DSL を スクラッチから学ぶ学習コストの違いです。Github Actions を「使う」側ではなく「作る」側は全くの経験だったのでのドキュメントを読んでも「つまり、どういうことだってばよ?」で エラーメッセージを読んでトライ & エラーを延々と繰り返すしかありませんでした。

トライ&エラーの一例を挙げます。例えば、action.yml で「使いますよ」宣言したログを

outputs:
  log:
    description: 'log'

octkit ライブラリを使用して js 内ではこうやって使用して

core.setOutput('log', files)

Github Actions の Web 画面に出すために workflow の yml でこう書かなければいけない

 - run: echo "${{ steps.id.outputs.log }}"

こういったところでつまづきまくりました。要は Github 側でスポーンさせたコンテナに対してどうやって入出力をつなぐか、の仕組みをつくって YAML で提供してくれているのだと思いますがそれぞれがどう連携して動いているか、がわかりづらかったです。ただ慣れればササッとかけるかもしれません。これは利便性と柔軟性のトレードオフですね。

Github Actions を動かすのが面倒(使うのは楽)

動かすために、ダミーレポジトリをつくってダミーの PR でダミーのコミット(Update README) を延々と繰り返すしかありませんでした。PR を 30 個ほど、README に 1 文字加えるだけのコミットを 100 回ほど繰り替えしたでしょうか...

スタンドアローンと Github Actions の両方をサポートする、という設計が無理めだったかも

振り返るとスタンドアローンと github actions 両方やろうとしたのが間違いだったかもしれません。最初はスタンドアローンで作っていたのを友人が「Github Actions で動かせば制限時間内ならタダなのにサーバ代乙 (^Д^) pgr」と煽ってきたため、カッとなって「え!! おなじ動作を Github Actions で!? ... (両方)出来らあっ!! ヽ(・Д・)ノ」と作り込みをはじめてしまいました。結局、認証の違い(personal access token or ${{secret.GITHUB}} )からくる octokit の差分とパラメータの取得処理の違いをどうやって吸収するかに苦しみました。Github Actions 側のコンテナに独自の環境変数や /etc/system-release 的なファイルがあって、「ここは Github 様のコンテナ内ですぞ」が js 内から判別できれば楽だったのですがそんな美味い話があるわけもなく…

結局コンストラクタ内で分岐させましたが、共通メソッドを用意した親クラスを用意して継承 or コンポジションを使った方がよかったかもしれません(v2 の予定が出てくればリファクタリングしたい)。今後拡張する場合、肥大化しそうなのはスタンドアローン側ではなく Github Actions 側のはず。

DSL でのプログラミング(?) について

個人的に触っている Kubernetes も CloudFormation も Ansible も YAML で用意された箱に納めるデータだけ用意する形なので「リソースの定義だけ準備しろ」が Ops における最近形式なのかもしれません。その場合、各コンポーネントがどう連携しているか理解することが重要になってきます。しかし YAML を書くには、フレームワークが提供するコンポーネントをしっかり理解しないといけないので、初動までの学習コストはどうしても高くなるように思えます。Github API Refereces を見てスタンドアローンの js を自由に書いて好きなサーバで動かすのか、Github Actions の DSL に従って Github というプラットフォームで統合された形で動かすのがいいのか... どちらも一長一短です。

「Github Actions おじさん」は副業 or 新規事業になるか

Github API を利用してスタンドアローンのスクリプトを作るのは楽でしたが、それを Github Actions 上で動かすためにカスタムの Github Action を作るのは独自 DSL の学習コストや実行環境の点で、想像以上に大変でした。

ここまで大変なら「Github Actions おじさん」がビジネスになるのではないか、作るのは面倒なので誰もやりたがらない反面、使うのは便利だからこれでビジネスができないか、という議論を友人としたところ、すでに普及したプラットフォームであり、ネットワーク効果が高いので専門職として成り立つ気はする、との結論になりました。

PR だけではなく、commit, issue, packages など、ほぼすべての github イベントに対してアクションを実行できるので用途は幅広く、拡張性も高いので「ウチの Github レポジトリでこういうことをしたい」会社はそれなりにあるのではないでしょうか。スタートアップに提供する場合、彼らは本業のプロダクト開発を効率化できますし、大企業に提供する場合、彼らは複数レポジトリに一括して恩恵を享受できます。使用者が kyagi/shamshir@ref で使い分けられるのも、開発者(私)にとってはブランチごとにクライアントを管理できるのでよさそうです。

まとめ

オレはようやくのぼりはじめたばかりだからな。このはてしなく遠い Github Actions 坂をよ…(未完)。

次回作(v2)にご期待ください。( •ᴗ•)੭

追記

Github Sponsors

使ってみて気に入りましたら Github Sponsor で支援していただけると嬉しいです ( ᵕᴗᵕ ) 支援していただいた方/組織は機能開発の優先リクエストを開発者(私)に送ることができます。 github.com

営業メッセージ

もしこの Github Actions が気に入った場合は Github Sponsors から支援していただけると嬉しいです。$1/月でスポンサーのバッジを、 $5/月で開発における優先リクエストを受け付けています。

現状、Shamshir は「Approved の数」を条件として「ひとつの」ラベルをつけたり/はがしたりしていますが、より複雑な条件や複数のラベル、ユーザーのロールに応じての動作も開発可能です。

Github Actions はとても便利な反面、そのドキュメントはお世辞にも読みやすいものとは言えず、独自の DSL とワークフローがさらにプログラマの理解を難しくしています。また、ドキュメントは基本的に全て英語で書かれており、非ネイティブの開発者は読み解くのに時間がかかります。

わずかなコスト、わずかな時間であなたの開発チームにあったカスタム Github Actions の開発ができたらどうでしょうか? あなたも私もハッピーです。ご連絡、お待ちしています :-)

Github で PR の APPROVED 数に応じてラベル(releasable) をつけたり外したりする

f:id:kyagi:20220131212856p:plain
Dismiss stale pull request approvals when new commits are pushed

Github の Pull Request のレビュー後、APPROVED の数に応じてラベルをつける運用を自動化します。APPROVED 後にコミットがあった場合、自動的に APPROVED が取り消される運用(画像) での運用を想定しています。*1

f:id:kyagi:20220131220133p:plain
shamshir

任意の数の APPROVED を取得した PR の集合を A とし、releasable ラベルが付与されている PR の集合を B とした場合、動作は以下になります。

  • 差集合 A - B に対して releasable ラベルを付ける
  • 差集合 B - A に対して releasable ラベルを剥がす

差集合 B -A は主に APPROVED 後にコミットすることで発生します ((Github のブランチ設定で Dismiss stale pull request approvals when new commits are pushed 設定をしている場合)))

レポジトリ

https://github.com/kyagi/shamshir

実行

$ node shamshir.js --owner kyagi --repo awesome-project --label releasable --quorum 2

ログ

$ cat combined.log
{"level":"info","message":"Shamshir started.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:22"}
{"level":"info","message":"Shamshir got pulls: 2602,2598,2596,2575,2573,2557,2553,2551,2540,2539,2481,2478,2295,2281,1981,1951,1685","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:22"}
{"level":"info","message":"Shamshir added releasable label to pull/2598.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:24"}
{"level":"info","message":"Shamshir added releasable label to pull/2596.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:24"}
{"level":"info","message":"Shamshir added releasable label to pull/2575.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:25"}
{"level":"info","message":"Shamshir removed releasable label from pull/2573.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:26"}
{"level":"info","message":"Shamshir added releasable label to pull/2551.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:28"}
{"level":"info","message":"Shamshir added releasable label to pull/2540.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:29"}
{"level":"info","message":"Shamshir added releasable label to pull/2539.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:30"}
{"level":"info","message":"Shamshir added releasable label to pull/2478.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:31"}
{"level":"info","message":"Shamshir added releasable label to pull/2295.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:32"}
{"level":"info","message":"Shamshir added releasable label to pull/1951.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:34"}
{"level":"info","message":"Shamshir finished.","mode":"live","owner":"kyagi","repo":"awesome-project","service":"shamshir","timestamp":"2022-01-31 00:54:34"}

Github API (REST API) について

Github API では Issue と Pull Request でエンドポイントを共有しています("shared" actions for both features)。API Reference の Issue ページにはラベル処理がありますが Pulls ページにはラベル処理がありません。だからといって Pull Request にラベルをつけられないというわけではなく、Issue API のエンドポイントを使い key として pull_request を使用することで(shared) Pull Request に対してもラベル処理が可能です。

Note: GitHub's REST API v3 considers every pull request an issue, but not every issue is a pull request. For this reason, "Issues" endpoints may return both issues and pull requests in the response. You can identify pull requests by the pull_request key. Be aware that the id of a pull request returned from "Issues" endpoints will be an issue id. To find out the pull request id, use the "List pull requests" endpoint.

https://docs.github.com/en/rest/reference/issues

Every pull request is an issue, but not every issue is a pull request. For this reason, "shared" actions for both features, like manipulating assignees, labels and milestones, are provided within the Issues API. https://docs.github.com/en/rest/reference/pulls

トップの画像では集合の図を載せていますが、実装で集合演算は使用していません。JavaScript に組み込みの集合演算がないのと、1 ページ 30 個 程度の Pull Request が処理対象の場合、集合演算よりループで処理した方が簡単だからです。

運用方法について

EC2 や ECS で動かすスタンドアローン的な運用と Github Actions で動かす運用の 2 つを考えています(Github Actions は将来サポート予定)。それぞれの長所/短所を以下に挙げます。

スタンドアローン Github Actions
設計/実装 制約がなく自由度が高い Github Actions の方法に則る必要があり workflow/action 内でできることに制約がある
コスト 走らせるためのリソース(EC2 or ECS) にコストがかかる 条件内の実行時間とストレージ容量であればコストは無料
認証/認可 Personal Access Token が必要 入力として GITHUB_TOKEN が使える

参考情報

*1 Dismiss stale pull request approvals when new commits are pushed 設定をしている場合

Web の QMK Configurator でキーマップを作成してからローカルの QMK Firmware でビルド/書き込みを行う

Corne Cherry で自分専用のキーマップを作成してファームウェアをビルド、書き込みする手順です。ビルドガイドの補足情報としてお役に立てば幸いです。

f:id:kyagi:20190717172535j:plain

Corne Cherry のファームウェア書き込みについて

ビルドガイドにもある通り、QMK Firmware を使用してファームウェアを書き込む必要がありますが、ビルドガイドではキーマップの編集手順が掲載されていないため、自分が行った手順を補足情報として載せておきます。

ここではキーマップの作成と編集を Web の QMK Configurator で行ってからローカルの QMK Firmware でビルド/書き込みを行っています。自分で keymap.c を書き換えるよりもコピー&ペーストで実施できるため、簡単です。*1

1. 自分専用のビルド環境を構築

qmk_firmware の crkbd/keymaps 配下で default をコピーして自分専用のビルド環境を構築します。

$ cd ~/git/qmk_firmware
$ pushd keyboards/crkbd/keymaps
$ cp -rp default kyagi

2. QMK Configurator でキーマップを作成してダウンロード

QMK Firmware/Building Your First Firmware の説明通り、キーマップの変更は keymap.c の keymaps 配列を編集することで行います。エディタで編集してもいいのですが、QMK Configurator を使用して自分好みのキーマップを作成し、COMPILE を実施した後、KEYMAP ONLY のリンクから keymap.c をダウンロードして keymaps 配列を入れ替えるとお手軽です。

$ vi kyagi/keymap.c # 自分で書き換えてもいいが...
$ cp ~/Downloads/crkbd_rev1_kyagi/keymap.c kyagi/keymap.c # QMK Configurator でコンパイル済みの keymap.c を KEYMAP ONLY からダウンロードしたほうが簡単

上記ではコピーしていますが、実際は keymaps 配列だけ書き換えればいいはずです。差分は以下のようになります。

$ diff -Nurp keymaps/default/keymap.c keymaps/kyagi/keymap.c
--- keymaps/default/keymap.c    2019-07-15 19:36:36.000000000 +0900
+++ keymaps/kyagi/keymap.c  2019-07-17 20:12:01.000000000 +0900
@@ -58,30 +58,8 @@ enum macro_keycodes {
 #define KC_ALTKN ALT_T(KC_LANG1)

 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
-  [_QWERTY] = LAYOUT_kc( \
-  //,-----------------------------------------.                ,-----------------------------------------.
-        ESC,     Q,     W,     E,     R,     T,                      Y,     U,     I,     O,     P,  BSPC,\
-  //|------+------+------+------+------+------|                |------+------+------+------+------+------|
-      CTLTB,     A,     S,     D,     F,     G,                      H,     J,     K,     L,  SCLN,  QUOT,\
-  //|------+------+------+------+------+------|                |------+------+------+------+------+------|
-       LSFT,     Z,     X,     C,     V,     B,                      N,     M,  COMM,   DOT,  SLSH,  RSFT,\
-  //|------+------+------+------+------+------+------|  |------+------+------+------+------+------+------|
-                                  GUIEI, LOWER,   SPC,      ENT, RAISE, ALTKN \
-                              //`--------------------'  `--------------------'
-  ),
-
-  [_LOWER] = LAYOUT_kc( \
-  //,-----------------------------------------.                ,-----------------------------------------.
-        ESC,     1,     2,     3,     4,     5,                      6,     7,     8,     9,     0,  BSPC,\
-  //|------+------+------+------+------+------|                |------+------+------+------+------+------|
-      CTLTB,    F1,    F2,    F3,    F4,    F5,                     F6,    F7,    F8,    F9,   F10, XXXXX,\
-  //|------+------+------+------+------+------|                |------+------+------+------+------+------|
-       LSFT,   F11,   F12,   F13,   F14,   F15,                    F16,   F17,   F18,   F19,   F20, XXXXX,\
-  //|------+------+------+------+------+------+------|  |------+------+------+------+------+------+------|
-                                  GUIEI, LOWER,   SPC,      ENT, RAISE, ALTKN \
-                              //`--------------------'  `--------------------'
-  ),
-
+  [_QWERTY] = LAYOUT(KC_ESC, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC, KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_MINS, KC_ENT, KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_GRV, MO(1), KC_LALT, KC_SPC, KC_ENT, KC_RCTL, KC_RGUI),
+  [_LOWER] = LAYOUT(KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, RSFT(KC_BSLS), KC_LCTL, LSFT(KC_1), LSFT(KC_2), LSFT(KC_3), KC_F11, LSFT(KC_4), KC_QUOT, RCTL(KC_1), RSFT_T(KC_9), RSFT_T(KC_0), KC_SCLN, KC_F4, KC_LSFT, LSFT(KC_5), LSFT(KC_6), LSFT(KC_7), KC_F12, LSFT(KC_8), KC_BSLS, RCTL(KC_2), KC_LBRC, KC_RBRC, RSFT(KC_SCLN), KC_EQL, KC_NO, KC_LALT, KC_SPC, KC_ENT, KC_RCTL, KC_RGUI),
   [_RAISE] = LAYOUT_kc( \
   //,-----------------------------------------.                ,-----------------------------------------.
         ESC,  EXLM,    AT,  HASH,   DLR,  PERC,                   CIRC,  AMPR,  ASTR,  LPRN,  RPRN,  BSPC,\

3. ローカルでのビルドと書き込み

USB ケーブルで Corne Cherry と Mac を接続し、ビルドと書き込みを行います。

$ popd
$ pwd
/Users/kyagi/git/qmk_firmware
$ make crkbd:kyagi
$ make crkbd:kyagi:avrdude

QMK Configurator でコンパイルしてダウンロードした .hex ファイルのみを差し替えてファームウェアに書き込む(OLED不使用)

コンパイルまでは QMK Configurator で可能なので FIRMWARE からダウンロードした .hex ファイルのみを差し替えて書き込みも可能です。ただし QMK Configurator で作成する keymap.c はレイアウトのみの簡易的なもので OLED 機能がコードに入っていないため、OLED は光りません。ただ、OLED 機能を使うと一部のキーに遅延が起こっているように感じるのと、パスワードなどの表示問題があるためオフのほうがいいかもしれません。機能的には問題ないようです。

$ pwd
/Users/kyagi/git/qmk_firmware
$ cp  ~/Downloads/crkbd_rev1_kyagi.hex .build/
$ ls -l .build
total 3168
-rwxr-xr-x   1 kyagi  staff  414300 Jul 15 19:41 crkbd_rev1_default.elf
-rw-r--r--@  1 kyagi  staff   53429 Jul 16 20:25 crkbd_rev1_default.hex
-rw-r--r--   1 kyagi  staff  325408 Jul 15 19:41 crkbd_rev1_default.map
-rwxr-xr-x   1 kyagi  staff  414292 Jul 17 20:12 crkbd_rev1_kyagi.elf
-rw-r--r--   1 kyagi  staff   70799 Jul 17 20:12 crkbd_rev1_kyagi.hex
-rw-r--r--   1 kyagi  staff  320940 Jul 17 20:12 crkbd_rev1_kyagi.map
drwxr-xr-x   2 kyagi  staff      64 Jul 15 20:41 obj_crkbd_rev1
drwxr-xr-x  33 kyagi  staff    1056 Jul 15 20:41 obj_crkbd_rev1_default
drwxr-xr-x  33 kyagi  staff    1056 Jul 17 20:59 obj_crkbd_rev1_kyagi
$ make crkbd:kyagi:avrdude

QMK Toolbox は使用不可

QMK Toolbox は使えないようです。書き込みしようとすると間違ったポート(Bluetooth)を見てしまうため失敗します。

*** Caterina device connected
    Found port: /dev/cu.Bluetooth-Incoming-Port
*** Attempting to flash, please don't remove device
>>> dfu-programmer atmega32u2 erase --force
    dfu-programmer: no device present.
>>> dfu-programmer atmega32u2 flash /Users/kyagi/Downloads/crkbd_rev1_kyagi.hex
    dfu-programmer: no device present.
>>> dfu-programmer atmega32u2 reset
    dfu-programmer: no device present.
>>> avrdude -p atmega32u2 -c avr109 -U flash:w:/Users/kyagi/Downloads/crkbd_rev1_kyagi.hex:i -P /dev/cu.Bluetooth-Incoming-Port -C avrdude.conf
    avrdude: warning at avrdude.conf:14976: part atmega32u4 overwrites previous definition avrdude.conf:11487.
    
    Connecting to programmer: .avrdude: butterfly_recv(): programmer is not responding
*** Caterina device disconnected

おまけ

私が作成したキーマップです。

  • もともと右親指で Ctrl、左親指で Alt、左小指で英数/かな切り替えの人生を送ってきました。英数/かな切り替えは LeftCtrl を Karabiner Elments でトグル設定にして対応しています。
  • タブキーがないですが、タブを利用するのは iTerm2 か JetBrains 製 IDE のみのためソフトウェアの設定で対応できます。iTerm2 は特定のキー(Ctl+t)からアスキーコード 0x09 を送るように設定しています(Preferences > Profiles > Keys > Send Hex Codes: 0x09)

f:id:kyagi:20190717194023p:plain f:id:kyagi:20190718024141p:plain

割れたキーボードを使うのははじめてなので長期使用するかどうかはまだ不明です... 一定期間試運転した後、決心がつけば、本格的に乗り換えるかもしれません。(^_^;

*1:はじめは quantum/quantum_keycodes.h を見ながら自前で keymap.c を編集していましたが、一部マクロの使い方が間違っている部分でコンパイルエラーになったので、だったら既にコンパイル済みの keymap.c からレイアウト部分だけ抜き出せばいいのでは、と思いつきました。実際は LAYOUT_kc マクロを使うか LAYOUT マクロを使うかの違いだったようです。

HAProxy 2.0.1 の Docker イメージは prometheus 機能がデフォルトでビルドされる

f:id:kyagi:20190616100100p:plain

Dockerhub の haproxy:2.0.1 では prometheus へ統計情報を export する機能がデフォルトで組み込まれる

HAProxy 2.0.1 の Docker イメージから拡張機能である prometheus.o がデフォルトのビルドオプションに含まれるようになった。これで前回の記事で紹介したPrometheus機能を使うためにDockefileの1行書き換えて独自ビルドする必要もなくなった。Dockerhub にある haproxy/2.0.1 のイメージをそのまま使うだけになるので、prometheus ユーザーとしてはありがたい限り。

github.com

prometheus.o をデフォルトでビルドに組み込むかどうかは Github で議論があり、prometheus の普及とパフォーマンスへの影響がないことから組み込むことが決定したようだ。

github.com

また 2.0.1 では 2.0.0 でバグレポートとしてあがっていた「数時間稼働させた後にクラッシュする」バグが修正されている。私も 2.0.0 を 2-3日使用した際に同様の問題が発生したため、一時2.0.0の使用を取りやめていたが、これで安心して 2.0 系を使用することができそうだ。(^_^)

BUG/MAJOR: mux-h1: Don't crush trash chunk area when outgoing message… · haproxy/haproxy@c2518a5 · GitHub

HAProxy 2.0 (-dev7) の prometheus サポート拡張機能を利用してマルチスレッドの統計情報を exporter なしで正しく取得する

f:id:kyagi:20190616100100p:plain

HAProxy 1.7 から 2.0 へ移行

前回 試した通り、HAProxy 1.7 + haproxy-exporter の組み合わせではマルチプロセス時に統計情報が正しく取得できない。代替案として prometheus サポートが提供される HAProxy 2.0 (dev7) を試してみた結果、こちらでうまくいきそうなので置き換えることにした。2.0 は 2019 年夏リリースの予定の LTS(Long Term Support) 版となっており、この記事を書いている時点でも開発は終盤に差し掛かっているようだ。

バージョン 並行処理 統計情報 prometheus対応 その他
1.7 マルチプロセス それぞれのプロセスで統計情報を持つ 別途 exporter が必要 マルチプロセス時、特定のプロセスに統計情報を固定する手段はない。haproxy-exporter は counter を gauge として扱ってしまう
2.0 マルチスレッド それぞれのスレッドの統計情報は統合 内部機能で prometheus サポートを提供 追加コンポーネントとして EXTRA_OBJS に prometheus.o を指定して独自ビルドする必要がある

www.haproxy.com

kubernetes 環境で 1.7 を動かしていた場合 2.0 に移行する場合の変更点

  • 1.7 から 2.0 へのアップグレード
    • haproxy 1.7 から 2.0-dev7(2019/06/11 release) にアップグレードした。
    • 起動オプションとして -Ds (Start in systemd daemon mode, keeping a process in foreground.) がなくなっていたので、YAML から削除した。また global parameter の daemon を削除して foreground で起動させるようにした。
  • prometheus サポートに伴う変更
  • マルチスレッドモデルへの切り替え
    • マルチプロセスからマルチスレッドになるため nbthread=4 を追加した。

haproxy1.7.yaml (-Ds オプションを使用して foreground で起動)

        command:
          - /usr/local/sbin/haproxy           
          - -f
          - /conf/haproxy.cfg
          - -f
          - /conf/frontend.cfg
          - -f
          - /conf/backend.cfg
          - -f
          - -Ds   

haproxy2.0.yaml (-Ds オプションはすでに廃止されていたため、グローバルパラメータの daemon を外して foreground で起動。stats が frontend の設定として独立)

        command:
          - /usr/local/sbin/haproxy           
          - -f
          - /conf/haproxy.cfg
          - -f
          - /conf/frontend.cfg
          - -f
          - /conf/backend.cfg
          - -f
          - /conf/stats.cfg

-Ds オプションについては man ページから削除してもらう PR を出したところ 1 時間も経たずにマージされた。(^_^)/

github.com

$ rg -H -- '-D' {haproxy-1.7.0,haproxy-2.0-dev7}/src/haproxy.c
haproxy-2.0-dev7/src/haproxy.c
493:        "        -D goes daemon ; -C changes to <dir> before loading files.\n"

haproxy-1.7.0/src/haproxy.c
460:        "        -D goes daemon ; -C changes to <dir> before loading files.\n"
823:                if (flag[1] == 's')  /* -Ds */
2038:                /* it's OK because "-Ds -f x" is the shortest form going here */

HAProxy 1.7 ではマルチプロセス時の統計情報が正しく取得できない

f:id:kyagi:20190616100100p:plain

HAProxy 1.7 をマルチプロセスで動かした場合、統計情報がおかしくなる

Kubernetes 上で haproxy を HTTP/HTTPS のロードバランサとして使用したところ、動作的には問題ないが prometheus/grafana で出力する統計情報(stats)が正しくないことに気がついた。正確に言うとシングルプロセス(nbproc 1)の場合は正しいものの、マルチプロセス(nbproc 4)になるとおかしくなる。マルチプロセスでそれぞれのメモリに統計情報を保持しているため、それらをまとめて出力する手段を持ち合わせていないことに由来するようだ。

1.7 のマニュアルを参照すると stats bind-process 1 を指定することで最も PID が若いプロセスに限定して統計情報を取得する説明があったものの、実際に試してみたところ、期待していた動作とは違っていた。

  • マニュアル
By default the stats socket is bound to all processes, causing a warning to be emitted when nbproc is greater than 1 because there is no way to select the target process when connecting. However, by using this setting, it becomes possible to pin
the stats socket to a specific set of processes, typically the first one. 

https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#stats

  • 実際の動作

f:id:kyagi:20190616101815p:plain

上記のグラフは 1.7 を利用して 1. シングルプロセス、2. マルチプロセス(nbproc=4), 3. マルチプロセス(nbproc=4, stats bind-process 1) それぞれを設定して、http リクエストを一定時間流した結果である。負荷が一定であれば単純な Counter 値である haproxy_frontend_http_requests_total は rate() した場合、右肩上がりになってから水平になるのが正しいはず。この例では 1. シングルプロセス時は正しい動きになっているものの、2.マルチプロセス(nbproc=4) および 3.マルチプロセス(nbproc=4, stats-bid-process 1) の時は値がおかしくなっている。本来であればマルチプロセス時に統計情報を取得するプロセスを固定するのが stats bind-process 1 のはずだが、こちらを指定しても機能はせず取得するプロセスはランダムになるようだ(stats ページにブラウザでアクセスしリロードを繰り返すと都度 PID が移り変わることも確認)

Each process has its own memory area, which means:
  (... snip ...)
  Information is stored locally in each process memory area and can't be shared:
    - stick table + traced counters
    - statistics
  (... snip ...)

www.slideshare.net

また、haproxy の統計情報を prometheus が取得(pull)するためのエンドポイントを提供するために haproxy-exporter を利用したが、こちらもメトリクスタイプを Counter ではなく Gauge としてしまうバグが 2015 年から放置されているようだ(こちらも fluentd のエンドポイントを curl して出力されるメトリクスタイプのコメントが gauge になっていることを確認)

github.com

このままでは正しく統計情報を取得できないままになってしまうため、解決策として2019年夏にリリースが予定されている 2.0(-dev7) を利用して、再度動作を確認してみることにする(続く) (o)

www.haproxy.com

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