あんパン

こしあん派

ISUCON11予選参加して敗退した

敗退した。一番高いスコアが42296で、その後ずるずる落ちて最終的には33586で終えた。

という話をしてメンバー募集したところ、 id:papixid:stefafafan といっちょやりますかということになり、天下n品というチームでISUCON11に参加した。お二人は同僚かつ友人であり、関係値マックスの状態から走れたと思う。早速来年どうやっていきますかね〜という話もしている。

練習とか

ツイートの通りこれまではお祭り的な気分で参加してたけど、ちゃんと動き方とか考えて改善していけるといいだろうなーということを2,3年前から思っていた。なので、今回はチームを組んでからちまちま練習して初動を決めたりツールの確認とかをしていた。

  • 定時後に過去問の環境を作って初動〜作戦考えるくらいまでをやる
  • 土日のどちらかで集まって8時間かけて完走する
  • ドキュメントを整備する・今後の動き方を考える

みたいなのをそれぞれ何度かやった。まだ粗はあるものの、初動はかなりうまく動けていたと思う。ドキュメントはScrapboxに残してて、GitHubのIssueとかは使わなかった。repoは練習ごとに作りたいので、チーム固定するならScrapboxに残す方が一覧性が高いと思う。

過去問の環境整備するのはTerraformの定義用意してくれてるrepoがあり、これを使った。

数分で環境が出来上がるのでたいへんありがたく使わせてもらった。本番もAWSだったので、AWSコンソール触る練習にもなったと思う。まあ業務で毎日のように使ってるのでAWSコンソール使ってて新たな発見があったかというとそうでもなかったけど、、

使ったツールとか

言語的には全員Perlの習熟度が一番高いのだけど、APMを使いたくなったときに他の言語の方がライブラリが充実してて都合がよかろうし、Goでいきますかね〜ということでGoにした。いま考えるとPerlでも良かった気もする。というのも、最終的に両方とも使わなかったため。Goでmake通ってればまあいけるやろという気持ちになれた点は良かった。

最初はNew Relicを使ってみようということで入れる練習をしていたのだけど、Goの習熟度がそこまで高くなくどんなフレームワークがやってくるか分からない(echo以外が来たら太刀打ちできなそう)、Segmentとか切れるのは便利そうだけどあんまり時間がないISUCONでそこまで見られるかというとそうでもなさそうということで、本番3日前くらいに相談してalpとpt-query-digestだけで挑むことにした。なので、あまり凝った解析はしていない。せいぜいログ取りを自動化してホスト名、時刻、ブランチ名、コミットハッシュが分かるファイル名にするくらい。

あとは生成したらS3に置いて、Lambda@EdgeでBASIC認証をかけたCloudFrontを通して確認できるようにした。S3にログを送りつけるとLambdaでSlackに通知が飛ぶ。

f:id:masawada:20210822183516p:plain

作るのはCDKでやっていて、repoは以下に置いた。

デプロイスクリプトもある程度自動化していまいるブランチをデプロイしたり、mainブランチが更新されてたらデプロイ前に落ちるようにしたりくらいはしていた。

他のメンバーがどうかは知らないけど個人的にはtmux-xpanesを便利に使っていて、MariaDBのconfigをsymlinkにしてrepoに置いて動かなくなったのでsymlinkをやめて直接tmux-xpanesで全台書き換えてデプロイとか、全台で sudo journalctl -f とかしていた。

github.com

当日やったこと

  • 10:06 CloudFormationで環境構築完了してSSHの情報(Public IP, Private IP, ssh_config)をScrapboxに転記、ベンチ投げる
    • 初期スコアは1718
  • 10:30 ログ取りまわりの設定
  • 11:00 マニュアル読み終える
    • この時点で理解はしてない、というか今もしてなくて今年のマニュアルめっちゃ丁寧だったけどめっちゃ難しかったと思う
    • それとも日本語読んで理解する力が落ちてるのか…?
  • 11:30 デプロイスクリプト用意してデプロイしたら全台アプリケーションが落ちたので直してベンチ入れた、alpとpt-quer-digestかけた
    • Makefileがなくてmakeできてなかったり、なぜかバイナリが書き変わってなかったりしたのでMakefile作って make clean && make するようにした
  • 12:10 作戦会議してざっとあたりをつける
  • 12:50 1, 2をバックエンドにして3をDBにする
    • スコアは27312
  • 13:00 このあたりからMariaDBにアイコン画像を保存するのやめてファイルに書き出すやつやりはじめる
    • ついでにassetsもNginxから配信
  • 16:00 ここらへんで画像の分離完成
    • アイコンの配信と POST /api/isu だけNginxが載ってる方に寄せて片方のホストだけでファイルを保存するようにして、配信はX-Accel-Redirectを使ってユーザ認証してからNginxから画像を返すようにした
    • 最初ホスト上のRubyで書き出すかと思ってやったところ久々すぎてGemfile.lockがないとbundle installできない?? みたいなエラーが出てよくわからなかったりMariaDBだからなのかlibmysqlclient-devがなくてnative extensionsがビルドできなかったりして大変だったので手元にデータもってきてDocker上で書き出してホストに戻すみたいなことやってた
    • あとjia_isu_uuidが使えない?のか椅子の登録時にInternal Server Errorが返ってきて10分くらい悩んでたけど、マニュアル見たところ開発ではホストの5000番で立ってるJIA API Mockを使えと書いてあったので手で初期化したら登録できるようになった
    • ここでやったのはあんまり伸びなくて、なぜかというとアイコンのGETリクエストがそんなに多くなかったから…
      • ちゃんとalp見ようなという話だし、ちゃんとマニュアル読もうなという話でもある
  • 16:30 このあたりでやっぱりgetConditionなんとかしないとねということになってジョブキューに逃してみる? みたいな話をしてfireworq使い初めた
    • スコア全然上がらないし正直1.5時間くらいでできる気がしてなかったので諦めモードに入りつつ、まあやってみるかということで開始した
    • が、Goの習熟度が低いことが仇となってJSONをMarshalするときにうまくいかずタイムアップしてしまった
      • 普段全然使わない言語でいくとダメなことが分かったので慣れていく必要がありそう

感想

本番でも初動をしっかりできたのはよかった。Goの習熟度が低かったところと、うまくボトルネックを見つけられなかったのは引き続きという感じで、特に後者が一番重要な競技だと思うので来年はいろいろ手札を用意して臨みたい。まずは11の復習をどこかでやりたい。

競技そのものとしては事前準備(CloudFormationと環境確認など)から当日までとても快適だった。ベンチでごたついていた? ぽいけど、幸か不幸か終盤はみんな伸び悩んでゴリゴリ実装していてあんまりベンチ走らせてなかったので煽りをうけることがあまりなかった。オープニングとクロージングがあったり、VTuber企画があったりとコンテンツ的にも楽しめてよかった。開催に関わっているみなさまありがとうございました。

追記: チームメンバーのブログ記事

stefafafan.hatenablog.com

Linuxデスクトップで壁紙を接続する無線LANのSSIDによって変える

ということをやっている。

壁紙は feh で設定していて無線LAN APのSSIDは iw で取得しているのでそれぞれインストールしておく必要がある。以下のスクリプトをどこかに置いておいて、systemdでresume時に読むunitファイルを書いたら都度壁紙が変わる。

#!/bin/bash

# select wallpapers by ssid
if ! type feh > /dev/null 2>&1; then
  exit 1;
fi
if ! type iw > /dev/null 2>&1; then
  exit 1;
fi

SSID=$(iw wlp3s0 info | grep ssid | awk '{ print $2 }')
[[ $SSID = '' ]] && SSID='default';

if [ -e "${HOME}/.wallpapers/${SSID}" ]; then
  WALLPAPER_DIR="${HOME}/.wallpapers/${SSID}"
else
  WALLPAPER_DIR="${HOME}/.wallpapers/default"
fi
WALLPAPER_PATH="${WALLPAPER_DIR}/$(ls ${WALLPAPER_DIR} | shuf -n 1)"
[[ ! -f $WALLPAPER_PATH ]] && exit;

feh --image-bg white --randomize --bg-max ${WALLPAPER_DIR}/*

$HOME/.wallpapers 以下にSSIDのディレクトリを掘っておいてSSIDに対応する画像を置いておく。SSIDが既知のものでない場合は default ディレクトリにフォールバックされるのでこちらには富士山の写真を入れておく。富士山でなくても良い。こうしておくと、自宅では萌絵(萌絵って久しく聞かないね)を設定しておいて出先で開かないといけなくなったときは富士山の写真とかにできる。

もっとも、最近は外に出ることがなくなったので活躍の場がない。

SKK-JISYO.jawikiをibus-skkで利用する

Linuxデスクトップをメインの端末にしてからこれまで2年ほどibus-skkを利用しており、辞書はSKK-JISYO.Lとユーザ辞書のみという状態で固有名詞に弱いことを不便に思っていた。都度ユーザ辞書に登録すれば良いが、それも面倒なのでWikipediaからSKK辞書を生成して利用できると便利そう。どうせ同じようなことを考えているひとはいるだろうと思って探したところ、tokuhiromさんが作られていた。しかもGitHub Actionsで定期的にビルドされているのである程度最新版のWikipediaに追従されている。最高です、ありがとうございます。

github.com

ということで、これをibus-skkで利用することにした。最初は $HOME/.config/ibus-skk/SKK-JISYO.jawiki に置いて辞書として登録したのだが、どうも使われていない様子。調べたところUTF-8の辞書を置くだけだとibus-skkが読んでくれていないようだった。

対処方法は以下の2つのいずれか。

  • Coding Cookie (;; -*- coding: utf-8 -*-) をファイルの先頭に挿入する
  • dconfで辞書のencodingを設定する

実際に両方とも試したところ、それぞれ辞書が使われるようになった。

辞書の取得と登録を自動化したいのでCoding Cookieを都度入れるのは面倒そう。生成時に含めるようにPull Requestを送るかどうかでいうと、いずれにせよ辞書の場所を設定したりその他の調整も自動化したいのでシェルスクリプトでdconfを叩いたら良いかという気持ちになりそのようにした。

$ dconf write /desktop/ibus/engine/skk/dictionaries "[..., 'file=/path/to/SKK-JISYO.jawiki,mode=readonly,type=file,encoding=UTF-8', ...]"

といった具合。作業前後に dconf read /desktop/ibus/engine/skk/dictionaries で様子を見ておくと良い。

内田真礼を一発で変換できて助かる。

GitHubにcurlで公開鍵を登録する2021

masawada.hatenablog.jp

ここでcurlで公開鍵を登録する方法を紹介していたのだけど、GitHub全域でこれまでのユーザ名・パスワード・OTPによるBASIC認証が使えなくなったようだった。以下のようなエラーが返る。

{
  "message": "Requires authentication",
  "documentation_url": "https://docs.github.com/rest/reference/users#create-a-public-ssh-key-for-the-authenticated-user"
}

ではどうすれば良いかというと、Personal Access Tokenを利用する。

docs.github.com

まず Personal Access Tokens から Generate new token を選択してPersonal Access Tokenを発行する。この際にpublic keysに関する権限をつけておく。

f:id:masawada:20210310111118p:plain

あとは以下で公開鍵を登録できるようになる。

curl -XPOST \
  -H 'Content-Type: application/json' \
  --basic -u '<user>:<personal_access_token>' \
  -d '{"title": "user@hostname", "key": "<public_key>"}' \
  'https://api.github.com/user/keys'

または以下のようにAuthorizationヘッダをつける形でも登録できるようだった。こちらの方がシンプルだと思う。

curl -XPOST \
  -H "Content-Type: application/json" \
  -H "Authorization: token <personal_access_token>" \
  -d '{"title": "user@hostname", "key": "<public_key>"}' \
  "https://api.github.com/user/keys"

登録した公開鍵はPersonal Access Tokenをrevokeすると削除される。あくまで一時的なものとして扱われるらしい。

一見 X-GitHub-OTP ヘッダをつけた方がセキュアなのではと思ったが、Personal Access Tokenを利用することで権限を絞って認可できるし一般的な認証方法に乗っかることができるから良いという話なんだろうか。個人的にはOSインストールの自動化のために利用しているのでPersonal Access Tokenをrevokeすると鍵が削除される仕様を若干不便に感じるけどその方がセキュアそうなので諦めるしかない。

gitでコミットする前のファイルを選択して編集する

repoで作業していて変更を加えたファイルを開き直してさらに追記したいケースは少なくない。historyから絞り込んで選択するよりは、差分が発生したファイルのリストから絞り込む方が見通しが良いのではないか、ということで普段は差分が発生したファイルから絞り込んでいる。以下のようなファイルを git-edit-changed という名前で $PATH のいずれかのディレクトリの中に置いて実行権限をつけておくと git edit-changed コマンドとして呼び出せる。

#!/bin/bash

filepath=$({\
  git ls-files $(git rev-parse --show-toplevel) --exclude-standard --others --full-name; \
  git diff --name-only HEAD; \
} | sort -u | peco)
if [ ${#filepath} -ne 0 ]; then
  cd $(git rev-parse --show-toplevel) && ${EDITOR} ${filepath}
fi

git ls-files $(git rev-parse --show-top-level) --exclude-standard --others --full-name では、untrackedなファイルをrepo rootからのパスとして出力している。 --others はgit管理されていないファイル群を出力するためのオプション。これを指定するだけでは .gitignore 等でignoreしているファイル群も含まれてしまうため、これらのファイルを無視するのが --exclude-standard--full-name を指定することで自分がどのディレクトリにいてもrepo rootからの相対パスとして出力できる。

git diff --name-only HEAD では、HEADからの差分を出力している。git diff --name-only 単体では、indexと現在の差分が出力されてしまうので、 HEAD をつけることで最新コミットからの差分を出力する。untrackedなファイルはindexに追加すると上記のコマンドでは出力されなくなり、こちらのコマンドで出力されるようになる。

これらの出力を {} でひとつにまとめてパイプで sort -u | peco に渡して絞り込んでから $filepath に代入している。といった具合でHEADとの差分一覧(コミットする前のファイル一覧)を取得できる。

どうぞご利用ください。