多分これが一番早いと思います
VSCode 上で ESLint を使った TypeScript のフォーマット
- https://github.com/microsoft/vscode-eslint を入れる
- VSCode のデフォルトフォーマッタを切る
グローバルか、ワークスペース上の .vscode/settings.json などに書くと良い
{ "typescript.format.enable": false }
- TypeScript で ESLint が働くようにする
"eslint.validate": [ "javascript", "javascriptreact", { "language": "typescript", "autoFix": true }, { "language": "typescriptreact", "autoFix": true } ],
ISUCON9 予選に参加した(failed)
最近は O-Ku-Ri-Mo-No Sunday! にはまっている。めっちゃ良くない?
ということで先日 ISUCON9 に参加した。
チームは :innocent: で id:wass80, id:utgwkk と参加した。
全体的な話は utgwkk の記事を参照。
自分のやったことは大体以下の issue に書いてある。レポジトリも以下参照 *1 。
トラブル集
今回もいろんなことが起きたので列挙してみる。
サーバー構成を1台と勘違いする
全員が [重要] と書いてある場所を読み飛ばしてスコアの計算方法ばっかり気にしていた結果、複数台構成ができないと思い込んでいた!
コンテスト中は「これ複数台勝手に立てたらどうなるかな〜」とか言ってました。1台だったのでインフラ担当の自分は途中暇だった。
たとえ構成は1台限定だったとしても、3台立ててそれぞれ作業用にしたら良かったなあ、という反省も。
VSCode Remote をやめることで得点が10倍になる
なんか 410 点ぐらいしか出なくておかしいな〜とか言いながら htop を見ていたら VSCode Remote がめちゃくちゃ CPU 食ってた。やめると 4100 点ぐらいになって最大のブレイクスルーだった。
Gunicorn のオプションを間違えてプロセスが大量発生する
Gunicorn の --worker-connections
を 10000 にしようとしたときに、間違えて -w 10000
として ワーカー数が 10000 になった。当然プロセスが大量に生まれるので、アクセスするとめちゃくちゃ重くて Load average が 170 になった。
sudo
や w
すらも謎のエラーで使えなくなったので、 root で入って systemctl kill
した。 systemctl は kill できるので最高!*2
uWSGI の restart が効かないので reboot を繰り返す
いろいろあって Gunicorn を uWSGI に載せ換えたのだけど、なぜか restart がハングするようになってしまった。面倒なのでサーバーごと reboot することで restart の代わりにしていた。後述の Redis と同じ理由かも?
途中から systemctl kill --signal=9
すると restart=true
で立ち上がることに気付いたのでそれを使うようにした。 systemctl は kill できるので最高!*3
Redis が立ち上がらないので memcached にする
Redis を apt で素朴に入れたけど apt install
や systemctl restart
がハングしたので諦めて memcached 入れた。おそらく↓の話っぽい。
bind のハマり、MySQL より redis-server を apt で入れると /etc/redis/redis.conf に bind 127.0.0.1 ::1 と書いてあって v6 は disable されているので systemctl redis-server start が刺さって apt install が失敗したように見えるやつの方で 10 分ぐらい捨てたやつがあった...
— れい (Yoshikawa Ryota) (@rrreeeyyy) September 8, 2019
Redis は kill して purge した。 systemctl は kill できるので最高!*4
並列 http アクセスライブラリを入れようとして未実装であることに気づく
transactions で外部 API を呼んでいるところが直列で遅かったので、並列にしよう!ということで検索して出てきた httpx というライブラリを使うことにした。
このドキュメントに即して実装を始めたのだけど、20分程度して実装が終わりつつあったときにドキュメントにある警告文の存在に気付いた。
Warning
This page documents some proposed functionality that is not yet released. See pull request #52 for the first-pass of an implementation.
悲しいね〜。文章読めるようになる必要がある。
Gunicorn や uWSGI を使うと点数が伸びなくなる
途中、デバッグのために Gunicorn じゃなくて Flask の app.run()
を直接使っていた。それでうまく伸びていたのだけど、Gunicorn にした瞬間に点数が1/10になった。uWSGI でも同様で、これについては今も謎。コンテスト中盤からずっとネックになっていて、終盤ドタバタした遠因になったと思う。
今見たら app.run()
の方は threaded=True
になってるので、10 workers の Gunicorn とはいろいろ違ったんだろうか。でも初めは上手くいってたので謎。
並列処理たちとのたたかい
gthread
Gunicorn は gthread を使うことができるのでなんとなく設定して使ったら動いているように見えた。ただ、大量に受け取りすぎて 499 ばかり返すようになってしまったので、もう少しチューニングが必要そうな雰囲気だった。
nodejs でも同じような悩みがあるのだろうか。それとも真にイベントループな言語は関係ないのだろうか...。
transaction
今回は transaction での外部 API が直列でめちゃくちゃ遅いのが課題になっていた。Python の並列処理にいい思い出が無いので、そこだけ nodejs に流すことも考えたものの、cookie やらを考えて結局 Python で行うことにした。
ただ Python の async await ほとんど分からないので難航。出たの最近だし機能もどんどん増えてるので、ぱっと調べるには向いていなかった *5。
結局最後の最後*6には aiohttp 使って勘でなんとかなった気もするけど、他のエラーでてたので分からなかった。
次回は go か node でやってもいいなあ、でも今回で async への理解深まったので Python で使える気もする。自分が一番好きで詳しい言語である Python によく分からないメジャー機能があるのがとても悔しい…。
良かった点
反省点ばっかりなのもアレなので良かった点も。
- 遠隔で行ったが、専用ディスプレイにずっと遠隔者のディスプレイを写していた
- スムーズな情報共有ができたと思う。全員同じ場所にいても有効だと思った。
- 次は VSCode Liveshare とか使ってみたいな。
- バグったときのために、オリジナル版を別ポートで動かしていた
- バグったときにレスポンスをオリジナル版からも取得して diff する、といった活動を行っていて低コストながら高速にバグを発見できた
- 計測やログイン環境のセットアップは順調にできた
- pt-query-digest や kataribe などでボトルネックの計算をちゃんと行っていた
- 定期的に git commit していたので容易に rollback できた
- スコア計算方法の部分はちゃんと読み込んでいた
- 手が空いたときに弁当を買いに行くという判断をした
- 昼飯をちゃんと食べた
- 楽しかった
その他
@チーム
今年もありがとうございました。 wass の迅速な方針決めや実装は頼りになったし、utgw が N+1 や index をバシバシ解決していく姿は最高だった。来年も頑張りたい!!
@運営
今回も楽しかったです!
バランスの良い問題になってて良かったです。isucari や還元キャンペーンなど、いかにもメルカリっぽい話題でニヤッとしました。あと得点がレスポンス数ではなく取引価格の合計なのも、実際のサービス目標感があって良かったです。
運営・インフラ提供・問題作成者の皆さん、ありがとうございました!!
感想
今年はあんまりやる気なくて練習もせず適当に始めたのだけど、実際やったらめっちゃ楽しいし、負けたらめっちゃ悔しい。 ISUCON4 からずっと参加しているけど、最高のコンテストだと思う。
次回もあれば絶対に参加したい!!!!!本戦行くぞ!!!
各 OS の 802.1X 認証における RADIUS サーバーの検証方法
最近 802.1X 認証(WPA2 Enterprise)を構築していた。野良 RADIUS サーバーに Credentail を渡さないためには、サーバー証明書などで RADIUS サーバーの検証を行うことが必要である。 各 OS でどう検証できるかを調べたのでメモ。
今回はクライアントにサーバーの証明書をインストールしない。また、EAP 方式は EAP-TTLS-PAP / EAP-TTLS-GTC。
Android
Android 7.0 より前は、自分でサーバーのCA証明書をインストールするしか検証方法がなかった。何も検証せずに使ってる人が多いのではないか…。
Android 7.0 以降は CA 証明書に「システム証明書」が使えるようになり、RADIUS サーバーの証明書をルート証明書で検証できるようになった。
システム証明書で検証する場合はドメイン名の指定が必須になる。
サーバーの証明書が指定したドメイン名であり、かつルート証明書で検証できれば接続される。
iOS
iOS 12 で検証。
802.1x 認証の場合、ルート証明書に関わらず サーバーの証明書を信頼するかどうかのダイアログが出る。
この時、サーバー証明書がルート証明書で検証できるかどうかが分からない *1 ので、SHA256 fingerprint などを頼りにユーザーが判断することになる。
管理者は SHA256 fingerprint とドメイン名(CN)を提供すれば良いと思う。
Windows 10
記事執筆時点での最新バージョンで検証。
Windows 10 では、ルート証明書での検証はしてくれるが、接続時に証明書の SHA256 fingerprint しか確認できない。
ドメイン名を指定するには、古い設定画面からドメイン名を入力し、信頼するルート証明書を選択する必要がある。この画面に行くのはなかなか難しい *2 ので、通常は SHA256 fingerprint を見てもらうことになりそう。
macOS
macOS 10.14 で検証。
macOS は接続時に証明書の確認画面が出る。そこには
などが見れる。
なので、ユーザーにはドメイン名と、証明書が信頼されているかの確認をして接続してもらうと良い *3 。
まとめ
OS | ルート証明書での検証(ドメイン名指定) | SHA256 fingerprint 表示 |
---|---|---|
Android(7.0 以降) | o | x |
iOS | x | o |
Windows 10 | △ | o |
macOS | o | o |
となるので、管理者は
- ルート証明書で検証できる証明書を LE 等で作る
- ドメイン名 (または CN)と SHA256 Fingerprint を提供して、ユーザーに検証するよう呼びかける
- Android 7.0 未満は諦めるかサーバーのCA証明書を配布する
と良さそうだった。
*1:困ってる人は割といそう。 https://seclists.org/educause/2017/q4/54 を読んでやや納得した。
*2:アダプタ設定から辿れる
Ansible で大量のホストに対して実行する場合のメモリ対策
Ansible で大量のホストを対象として Playbook を実行すると、大量の変数を保持する結果、メモリが足りなくなることがある。
以下はその対策。
gather_subset
を用いて収集する fact を制限する
Ansible は gather_facts
(または setup
モジュール)で大量の fact を取得するが、これを必要最低限にすることでメモリ使用量を抑えることができる。
gather_subset
という setup
モジュールのオプションを使う。
gather_facts: no tasks: - name: Gather facts setup: gather_subset: min
serial
を用いて数回に分けて PlayBook を実行する
serial: 150
とすることで 、150ホストごとに PlayBook が走る。
使い終わった巨大な変数は初期化しておく
serial
にしていても PlayBook の実行ごとにメモリ使用量は増えることがある。
このような場合は、PlayBook の最後で巨大な変数を初期化する(最終手段っぽい)。
tasks: - name: Save large output command: command_outputs_large_text register: large_output ... - name: Init values set_fact: large_output: !!null
systemd 240 と Java の OOM
ArchLinux 上で Debian stretch を systemd-nspawn で立ち上げているのだが、最近 pacman -Syu したら Debian のコンテナで Java が立ち上がらなくなった。
原因
だいたいここに書いてある。
- systemd 240 で RLIMIT_NOFILE が大きくなった
- Debian の PAM では、RLIMIT_NOFILE (hard nofile)を PID 1 からコピーしている
- なので、ログインセッションなどでも RLIMIT_NOFILE が大きくなった
- ところで、Java は起動時に RLIMIT_NOFILE 分の配列を作成する
- このような環境だと RLIMIT_NOFILE が大きすぎるので OOM で落ちる
というもの。
Debian sid も systemd 241 なのに落ちなくてなぜだろうなと思ったら、そもそも hard nofile limit がそんなに大きくなかった。
大きくならないように、ビルド時にフラグをつけるようにしたらしい。
Arch で ulimit -H -s するとめちゃくちゃ大きかったので、特に触ってないのでしょう*1。
どうするか
/etc/security/limits.conf.d に hard nofile をそれなりの値に戻すような config を置く。
今回は /etc/init.d/hogehoge なスクリプトを立ち上げるだけなので大丈夫だが、 ssh でのセッションとかでも対処するなら UsePrivilegeSeparation あたりを変えないといけないらしい(未検証)。
おまけ
この事象は systemd が立ち上げるプロセスには関係せず、pam_limits.so を経由するものに関係がある。
今回、 Elasticsearch では動いたのに Jenkins で動かなかったのは何故だろうと調べていたら、Jenkins は今も /etc/init.d/jenkins なので su 経由で起動し、PAM の影響を受けるということだった。
こういうところで違いが出るんだなあ。
レポジトリから直接ダウンロードした deb ファイルを自分で検証する
apt ではなく dpkg を直接インストールするとき、 http://security-cdn.debian.org/ 等から直接 dpkg をダウンロードする。
HTTPで取得した dpkg が改ざんされていないか気になったので自前で検証してみた。
具体的な構造は以下が詳しい。
GPG Key をインポート・信頼
$ sudo apt-key list # 今 apt で使っている key を見る。ここにあるものは信頼できるだろう。(一応 fingerprint を見て確認しても良いかも) $ gpg --import /etc/apt/trusted.gpg.d/~.gpg $ gpg --edit-key <key id> # import したものを ultimately trust する
Release ファイルの検証
いくつかの key で署名されていたので、複数 import して確認した。
$ gpg --verify Release.gpg Release
Packages のハッシュ確認
Release の中に Packages ファイルのハッシュが記載されているので、あってるか確認する。
deb のハッシュ確認
Packages の中に deb ファイルのハッシュが記載されているので、あってるか確認する。
ここまででエラーがなければ deb ファイルが検証された、と思う(多分!!