libvirtd を Ubuntu 18.04 にインストールし、部屋の LAN とブリッジする

自宅では長らく VMware ESXi を使用してきて、大変良かったのだが、ホストが Linux だと何かと楽だろうと考えたりしていたり、DS77U5 への移行のタイミングでホスト側に直接 Docker で入れられるものは直接入れたくなったりしたのもあり、新たに libvirtd を検証開始。意外とすんなり入って中の状態も分かりやすい感じにはなったが、(主に libvirtd に直接関係ない)よくわからないところに罠があったのでメモ。

なおこの前に OpenStack を試そうとしたが、 conjure-up が半端に色々ラップしたまま失敗してどうなっているのかさっぱりわからない状態になったのを見て OpenStack は諦めた。

まずは、 Ubuntu 18.04: 仮想化のKVMをインストールする – Narrow Escape にしたがってパッケージを入れた。ただし、 libguestfs-tools は要らなそうだったので、今のところ入れていない。なお、 /var/lib/libvirt に色々保存するようだったので、通常必要ない操作だがインストール前にこのディレクトリを実際に保存したいパーティションにシンボリックリンクとして指定した。

$ sudo apt install -y qemu-kvm libvirt0 libvirt-bin virt-manager

また、

$ sudo gpasswd libvirt -a `whoami`

続いて、ブリッジネットワークを構成する。いつの間にか(多分 LTS だと 18.04 から?) /etc/network/interfaces がもぬけの殻になっていて、 netplan に移行したから /etc/netplan/ を見ろと言っているので、 /etc/netplan/01-netcfg.yaml を編集した。なおこのファイルの名前は、環境によっては 50-cloud-init.yaml などになるようだ。

なお、ブリッジ側の MAC アドレスは起動するたびにランダムに変わるようで、 DHCP で固定の IP アドレスを配るのに問題があるので、固定した。IP アドレスを static でホスト側で指定するなら不要と思われる。また、 VM で検証しているときはホスト側の仮想スイッチでプロミスキャスモードを禁止しているために通信がうまく行えない罠にはまっていた。

あと、 macvcap でホストの LAN に直接刺せそうな選択肢があるが、これはブリッジではなく NIC をその VM で占有する時に使うもののようなので、 VM とホスト両方、あるいは複数 VM で使うときはブリッジ構成は必要なようだ。

# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    enp1s0:
      dhcp4: no
  bridges:
    br0:
      macaddress: XX:XX:XX:XX:XX:XX
      dhcp4: yes
      interfaces: [enp1s0]

これで

$ sudo netplan generate
$ sudo netplan apply

などとするとブリッジが構成できたが、古いものが消えなかったりして微妙にこのファイルの定義とずれていくようなので、素直に

$ sudo reboot

するのがよさそう。

これでブリッジが構成されるので、

$ sudo brctl show
bridge name     bridge id               STP enabled     interfaces
br0             XXXX.XXXXXXXXXXXX       no              enp1s0
                                                        vnet0
docker0         XXXX.XXXXXXXXXXXX       no              vethXXXXXXX
                                                        vethXXXXXXX
virbr0          XXXX.XXXXXXXXXXXX       yes             virbr0-nic

みたいな感じで brctl でブリッジが見え、 ifconfig でも DHCP から降ってきた IP アドレスが見れるようになった。

なお、 vnet0 というのが、 libvirtd でブリッジに接続したときに VM と紐づけて作る tap インターフェイスのようだ。なので、この状態で enp1s0, br0, vnet0 は同じ L2 として自由に疎通できそうだが、実際には br0 をまたいだパケットがやりとりされない現象に悩んだ。tcpdump を見ても、 enp1s0 から流れてきたパケットは br0 には流れているが vnet0 には流れず、 vnet0 から流しているはずの DHCP discover も br0 には流れているが enp1s0 には流れなかった。

結局、 networking – why linux bridge doesn’t work – Super User を見て、 iptables が L2 に干渉してくるのか疑問に思いながらも試してみたところ、echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables によって実際に解決した。よくわかっていないが、どうやら自分のホストに来てしまった時点で L2 な転送でも iptables を通ってしまうようだ。

なのであとは /etc/sysctl.conf 相当のファイルに

net.bridge.bridge-nf-call-iptables = 0

という内容を書くことになるが、ここにも罠があった。

まず、 Ubuntu 18.04 だと /etc/sysctl.d といういかにもディレクトリがあり、 /etc/sysctl.d/README にはご丁寧に

After making any changes, please run “service procps start” (or, from
a Debian package maintainer script “invoke-rc.d procps start”).

とまで書いてあったのだが、なぜか動かなかったので、結局昔ながらのやり方で /etc/sysctl.conf に書き込むことにした。

しかし、これでは sysctl -p /etc/sysctl.conf や invoke-rc.d procps start では反映されるものの、再起動するとなぜか 1 に戻ってしまう。該当範囲は不明だが、少なくとも Ubuntu 18.04 では procps のサービス起動(sysctl.conf 読み込みを実施する)をネットワークが有効になる前に実施してしまうので、ネットワーク関係の設定はここに書いても無視されてしまうようだ: Ubuntu 18.04 で ipv6 を無効にする | 雑廉堂の雑記帳

少なくとも net.bridge.bridge-nf-call-iptables は /etc/rc.local でもうまくいかなかった。そのためネットワーク関係のあれこれが起動しているであろう net if の post-up にねじこみたかったが、FAQ | netplan.io によるとそのようなものはまだないようで、代わりに networkd-dispatcher で設定するよう勧められている。幸い、手元の Ubuntu 18.04 では OS インストール時に networkd-dispatcher がインストールされていたようだったので、次のようなファイルを作成することで、再起動しても sysctl.conf が読み込まれて、ブリッジなら iptables を無視してパケットが行き来できるようになった。

$ cat /usr/lib/networkd-dispatcher/routable.d/99sysctlworkaround
#!/bin/sh
/etc/init.d/procps restart
exit 0
$ ls -al /usr/lib/networkd-dispatcher/routable.d/99sysctlworkaround
-rwxr-xr-x 1 root root 46 Jul  6 20:57 /usr/lib/networkd-dispatcher/routable.d/99sysctlworkaround

これでようやく、 libvirtd に br0 を認識させて、直接 enp1s0 に出ていけるようになった。

MAC アドレスは中からも外からもホストの vnet0 としても、ここに表示されたものになるようだ。ESXi ではマシンを一回立ち上げるまで MAC アドレスがわからないが、 libvirtd + virt-mangaer では最初に起動する前から表示されるので、インストール前に DHCP/DNS に登録できるのは便利。

(2019/7/23 追記)UEFI ベースの VM を移植するに、 UEFI/OVMF – Ubuntu Wiki にあるように apt install ovmf して UEFI 用のファームウェアをダウンロードする必要があった。また、他のところで使っていた UEFI ベースの VM イメージを取り込む際、 BIOS メニューに入る前(Tiano core ロゴ)に F2 を連打してメニューから efi ファイルを直接選択して起動し起動後に grub-install して起動すべきファイルを認識させる必要があった。

OPNsense を Protectli Vault FW1 にインストール、 VyOS と簡単に比較

前回までで、 Protectli Vault FW1 に VyOS インストール簡単な速度測定をしたので、 OPNsense に乗せ換えて比べてみた。

OPNsense のウェブページから USB メモリ用の img.bz2 をダウンロードして、bz2 を解凍してディスク(パーティションではない)に丸ごと dd で書き込み、そこから boot した。vga なイメージをダウンロードしたにも関わらず画面は Booting で止まり、シリアルポートからしか見えなかった。シリアルポートでも起動時・終了時以外は特に何かが出るわけではないが、起動時にはそれぞれの端子の IP アドレスなどが出るので便利ではある。ちなみに、 115,200 bps なので Vault の BIOS の初期設定と同じでちょうどよい。

Live イメージになっていてそのままでも利用は可能で、”WAN” で DHCP が利用可能だと “LAN” のポート(ただし初期設定では em0 なので、 Vault FW1 では “WAN” と印刷されている)からそのままインターネットに出ることができた(DHCP も動いている)。LAN 側に繋いで 192.168.1.1 宛に ssh (ユーザ名 installer、パスワード opnsense)でインストールできた。その時立ち上がっている WebUI からは installer ログインできないようだ。

起動後も初期設定は WAN と LAN が逆になっているが、管理画面から割り当てを変えることで管理画面の表示と印刷を合わせることができる。割り当て変更直後は DHCP がうまくうごかないのか、一度電源ボタンを単押ししてシャットダウンしたあと起動した。もっといい方法はあるかもしれない。

なお、大抵のサービスは初期設定で全インターフェイスで LISTEN するようになっているので、探して LAN に変更する方がよさそう。

OPNsense は初期設定でも最低限のファイヤーウォールが設置されているようで、色々設定画面に出てきた。Linux (iptables) と FreeBSD の違いか、かなり設定項目が違ったので、ひとまず初期設定で前回と同じ構成で iperf を通したところ、次のようになった。

Vault の top

39 processes:  1 running, 38 sleeping
CPU:  0.0% user,  0.0% nice, 30.7% system,  0.1% interrupt, 69.2% idle
Mem: 115M Active, 103M Inact, 182M Wired, 31M Buf, 7418M Free
Swap: 8192M Total, 8192M Free

iperf

TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.101 port 57712 connected with 192.168.100.37 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-20.0 sec  2.18 GBytes   935 Mbits/sec

PC Junkie rev2.4 – 【NW】pfsense VS Vyatta でも Vyatta は pfSense と比べて同じスループットでも CPU 使用率がはるかに低かったと報告されているが、今回の比較(VyOS と OPNsense)でも概ね同じ傾向のようだ(前回は VyOS は初期設定で 1% 未満、ファイヤーウォールを構成していくつか設定すると7%くらいだった)。

また、電源ボタンを押すと OPNsense が正常にシャットダウンした。

とりあえず、最初にインストールして気付いた差は次の通り。単純な設定でのスループット性能としては VyOS の方が優れていそうではあるが、どちらも Gigabit な環境でルータにするのに不足ということはなさそう。OPNsense は WebUI で色々いじれたりする点などもあり、ひとまずは OPNsense を使ってみることにした。

項目 VyOS OPNsense
NAT 930Mbps での CPU 使用率 初期設定1%未満
いくつか FW を設定すると 7% (条件は前回)
初期設定約30%
初期インストールでの SSD 消費量 約 500MB
(約 1GB を squashfs 圧縮)
約 1GB
WebUI なし あり

VyOS 負荷測定 on Protectli Vault FW1

Protectli Vault FW1 に VyOS を入れて、一応はファイヤーウォールも形だけ設定した状態での速度を計った。1Gbps リンク程度なら Celeron J1900 でも余裕な模様。

なお、自宅配線の都合上別のルータが挟まっていたりしてこれも正確な測定ではない。あくまで Vault FW1 + VyOS の負荷状況を確認するもの。

ルール

(show config の結果のうち NAT に関係ないものは省略、よくわからないけど公開するほどでもない LAN 内のパラメタは **** にした)

firewall {
    all-ping enable
    broadcast-ping disable
    config-trap disable
    ipv6-receive-redirects disable
    ipv6-src-route disable
    ip-src-route disable
    log-martians enable
    name WAN_TO_LAN {
        default-action drop
        rule 100 {
            action accept
            state {
                established enable
                related enable
            }
        }
        rule 110 {
            action accept
            destination {
                port 22
            }
            protocol tcp
        }
        rule 120 {
            action accept
            destination {
                port 5001
            }
            protocol tcp
        }
    }
    receive-redirects disable
    send-redirects enable
    source-validation disable
    syn-cookies enable
    twa-hazards-protection disable
}
interfaces {
    ethernet eth0 {
        address dhcp
        duplex auto
        firewall {
            in {
                name WAN_TO_LAN
            }
        }
        hw-id ****
        smp-affinity auto
        speed auto
    }
    ethernet eth1 {
        address ****
        duplex auto
        hw-id ****
        smp-affinity auto
        speed auto
    }
    ethernet eth2 {
        duplex auto
        hw-id ****
        smp-affinity auto
        speed auto
    }
    ethernet eth3 {
        duplex auto
        hw-id ****
        smp-affinity auto
        speed auto
    }
    loopback lo {
    }
}
nat {
    destination {
        rule 1 {
            destination {
                port 23
            }
            inbound-interface eth0
            protocol tcp
            translation {
                address ****
                port 22
            }
        }
        rule 2 {
            destination {
                port 5001
            }
            inbound-interface eth0
            protocol tcp
            translation {
                address ****
                port 5001
            }
        }
    }
    source {
        rule 99 {
            outbound-interface eth0
            source {
                address ****
            }
            translation {
                address masquerade
            }
        }
    }
}

WAN -> LAN (LAN 側が iperf における -c)
転送速度

[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes   932 Mbits/sec

LAN -> WAN (LAN 側が iperf における -s)

[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.09 GBytes   933 Mbits/sec

WAN -> LAN の際の Vault

%Cpu(s):  0.0 us,  0.2 sy,  0.0 ni, 93.3 id,  0.0 wa,  0.0 hi,  6.5 si,  0.0 st

このページで出せる考察

ジャンボフレームなどは無効なまま iperf でこの速度が出ているので、ギガビット(TCP/IP ならペイロードは理論値で 950Mbps 未満なはず?)は使い切って CPU が余っているといえそう。

ルータの自由度を上げるために Protectli Vault FW1 を買ってきた

現在家のルータは RouterBoard RB3011-UiAS-RM を使っているが、Protectli Vault FW1 の存在を知り、より自由度が高い遊べる環境を用意しようと思って購入した。

買い物

今回は Amazon.com で買ってきた。Amazon.com でのページは Firewall Micro Appliance With 4x Gigabit Intel LAN Ports, Barebone だが、今は全く同じものは売り切れているようだ(同じシリーズの他のものはある)。買ったときは $199.00 で、それに送料 1,272 円と Import Fees Deposit 1,915 円(多分ドル建てで決まっていたはずだが、 Invoice では JPY になっている)がかかった(勤務先にはこういう時に使える会社の費用補助があるので自己負担はもっと少ない)。

今回購入したのはベアボーンなので、 RAM と SSD を買う必要があった。 RAM は DDR3L で最大 8GB、 SSD は mSATA で、それぞれ次のものを買った。

なお、どちらも滅びかかっている規格のようで、 DDR3L RAM はいくつかの店にあったが、 mSATA SSD は秋葉原ではそもそもほとんどの店に存在せず、あっても 8000 円以上するモデルしか見当たらなかったため Amazon で購入した。ちなみに、 RAM は刺さないと画面にも何も映らないので、動作確認には最低でも RAM が必要。

届いた商品

マザーボードはこのような感じになっている。マザーボードに YANLING のロゴがあるので委託先かもしれない。YANLING のページを見ると、YANLING の N10Plus がほぼ同じ製品であるように見える。筐体裏面には Made in China と書いてあり、時計も中国の標準時に設定されて出荷されてきた。

ファンレスなので音もせずとても静かで、大きさもさすがにポート数の同じスイッチングハブよりは大きいものの、それと比べたくなる程度には小さい(普通の L3 扱えるルータと比べるとむしろ小さい気がする)。

また、 BIOS には「電源オン状態で電力供給が途絶えた場合、電力が復活したら自動でオンに戻る」設定があり、標準で有効になっているようだ。BIOS 画面で AC アダプタを引っこ抜いて落ちた後アダプタを戻したら、自動で OS が起動した。ルータは常時起動していることを想定するので、この機能はありがたい(hp ML115 でも見た気がするので、デスクトップパソコンをあまり使わないから知らないだけでよくある機能なんだろうか)。

なお、 AC アダプタは Protectli 40W Power Supply – Protectli の商品画像と同じ、 CHANNEL WELL TECHNOLOGY なる会社のものがきた。商品紹介ページでは 110-240V と書いてあったり FAQ では 100-240V となっていたりするが、商品画像と同じく 100-240V と書かれたものがきた。アダプタ本体とケーブルの間は PC やモニタの電源でよく見る端子なのでその辺で買えそうなケーブルだった。日本でそのまま使える type-A だがアース付きなので、タップにアースがない場合は3ピン→2ピンをかますか、ケーブルを買い替えることになると思われる。

Ubuntu MATE で動作確認

まず最初に、 RAM だけ買って SSD なしの状態で Ubuntu MATE のインストーラの USB メモリを刺したところすんなり GUI が立ち上がった。

電源ボタンも Ubuntu MATE 側できちんと認識して、押されたらダイアログを出すなりなんなり OS で設定した動作をするようだ。

VyOS 入れてのテスト

SSD が到着したら、とりあえず VyOS を入れてみた。FW1 FW2 FW4A Series Hardware Overview – Protectli の説明通り、 WAN が eth0、LAN が eth1 に割り当たっていた。

また、添付のケーブルを使うことでシリアルポートも利用できた。BIOS 画面をシリアルポートに転送する機能がついていて、 OS が起動した後は OS から見えるようになっているので VyOS もシリアルポート越しに利用できた。VyOS では GRUB で Serial console を選ぶと起動画面もシリアルで流れてくる( GRUB は VGA とシリアルポート両方に表示された)

ただし、 BIOS 画面が出ている状態でのシリアルポートは How to use the Vault’s COM port – Protectli の説明通り 115,200 bps なのに対し、 VyOS の初期設定は 9,600 bps なので、切り替えないと途中で画面が表示されなくなる。BIOS も VyOS も設定項目はあるようなので、使うならどちらかに統一しておいた方がよさそう。

VGA あるから家ならそちらを使えばいい感はあるが、 GPD microPC とか持っていたらシリアルポートが利用できるのが便利なのかもしれない。

インストールしたら、次の二つを参考に VyOS で NAT 構成してみた。まだファイヤーウォールなどは設定していない。インストールも含め、特に問題らしい問題は少なくともデバイス由来のものは起こらなかった。

簡単な速度測定

この状態で、ざっくり NAT 越しに速度を測定してみた。

Protectli で NAT を構成し、 ThinkPad を LAN 側に、部屋の LAN WAN 側にして、部屋の LAN においた ML115 (の上に増設された何かの NIC)と、 ThinkPad の間で NAT 越しの iperf を試してみた。

iperf

[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-20.0 sec  1.65 GBytes   709 Mbits/sec

通信中の Protectli Vault の top

%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.3 id,  0.0 wa,  0.0 hi,  0.6 si,  0.0 st
KiB Mem:   8051500 total,   286864 used,  7764636 free,    31692 buffers
KiB Swap:        0 total,        0 used,        0 free.   138536 cached Mem

(追記:この後 ThinkPad を測定用とせずデスクトップ/サーバ構成のマシン同士で測定したところ、簡易測定でもスループットが 930 Mbps を超えたので、Protectli Vault FW1 + VyOS のせいで 800Mbps を下回っていたわけではなさそう:VyOS スループット測定 on Protectli Vault FW1 | にろきのメモ帳
(追記:↑しかし ThinkPad も状況により 900Mbps を超えたので原因はよくわからない)

php-fpm on Ubuntu での謎エラーの解決

Ubuntu 18.04 + php-fpm 7.2 + NextCloud で、

Zend OPcache can't be temporary enabled (it may be only disabled till the end of request) at Unknown#0

のようなエラーがログに大量に貯まるので調査。色々ぐぐったところ、 opcache.enable を複数回設定しようとするとよくわからないエラーとして登場するようだ。とりあえず、 /etc/php/7.2/fpm/pool.d/***.conf から opcache.enable と opcache.enable_cli をコメントアウトし、 /etc/php/7.2/fpm/php.ini では有効化されているのを確認することでエラーメッセージは消えたが、想定通りになったかどうかまでは確認できていない。

Windows 版 OpenVPN をクライアントにする時、ローカルで鍵ペアを生成して CSR だけ送る

OpenVPN でサーバを構築する方法は2x HOW TO | OpenVPNなどのページに掲載されていたりするが、 Windows 版で新規クライアントに対して easy-rsa を用いつつ秘密鍵を端末から出さないで証明書を得る方法について。何分目新しいものはないが、こういうのは既にサーバが立っていると手順通り以外したくなくなる病になりがち(特に easy-rsa で微妙にラップされているので余計になりがち)なのでメモ。基本的にはEasy_Windows_Guide – OpenVPN Communityの簡略版。

クライアント側

  • Community Downloads | OpenVPNよりクライアントをダウンロード
  • インストール。この時、インストールするもの一覧で、 EasyRSA をインストールするオプションのチェックボックスを忘れずにつける。
  • cmd.exe を管理者権限で開く

init-config は vars をリセットするのでインストール時のみ実行。notepad では “set KEY_” で始まる行の環境変数を適宜設定。client-name は(ca の中で)一意なものを設定。

> cd "C:\Program Files\OpenVPN\easy-rsa"
> init-config
> notepad vars.bat
> vars
> clean-all
> build-key client-name

client-name.key と client-name.csr ができるので、 csr は VPN 設計的には秘密にする必要はないので OpenVPN サーバを管理できる端末に送る。key はクライアント端末から出さない。

サーバ側

# cd easy-rsa # ここを読んでいる時点でeasy-rsa のディレクトリが既にあるはずなので移動
# vim ./keys/client-name.csr # コピー方法はなんでもよいが、ここに csr を配置
# . ./vars
# ./sign-req client-name

これで、 ./keys/client-name.crt に証明書が発行される。これも秘密にする必要はないのでクライアント端末にコピーする。

ISUCON 8 予選に参加した話

ISUCON 8 の予選にチーム「ワイハリマ」として yuta1024, yabuuuuu とともに参加していた。ソースコードは GitHub で公開: yuta1024/isucon8 yuta1024/isucon8-infra

一瞬だけなぜか 47k というスコアが出るも、最終的に、最後の方は 33k-36k 程度をさまよいつつ最後の提出が 35,379 点。予選通過ラインは 36,471 点だった模様。競技中は予選通過はもっと遠いものと思っていたら、もう一工夫どころか下手したらベンチマークガチャの引き程度で超えていたかもしれない程度まで近づいていたのか…… 思うところは色々あるが来年がんばりたい。

ということで思い出せる範囲で、自分が実施したことをメモ。

競技前準備

当日の集合のことと、 GitHub のプライベートリポジトリを使用することを決定。その後、個人的な用事により旅立ってしまい前日に戻ってきたが、その間に yuta1024 と yabuuuuu はその間にアプリケーションの配布ツールや想定されたミドルウェアの設定、 SSH 公開鍵配布などを準備してくれていたようだ。ISUCON の日程も自分の不在もずいぶん前からわかっていたし、数日前だと全員いたとしても時間が潤沢にとれたわけでもないので、もう少し早めに(例えばディストリビューションがわかった時点で)動くべきだった。

競技開始

当日集合し作業開始。作業時間は当初のアナウンス通り、 10:00-18:00。

まずは問題を把握。題材はイベントの予約サイトで、管理者はイベントの追加や売り上げの集計が行え、ユーザは席の予約(ランクだけ選んであとはランダム)とそのキャンセル、自分の予約状況の確認が行える。テーブル構成は先ほどのリポジトリの isucon8/db/schema.sql にもとのものが上がっているが、概ねの構成は以下の通り。全てのイベントを一つの会場で実施していることになかなか気付かず、理解するのに少し時間がかかった。

  • ユーザのテーブル。ユーザごとに一行で、普通。
  • イベントのテーブル。公開されているかや価格(一番安い C 席の価格。上位席は席のグレードごとに決まった金額(後述)が加算)などが格納されている。普通。
  • 席のテーブル。S席(番号1-50で価格は(C席と比べた差額が)5000円)みたいなのが S, A, B, C の 4 種類、 1000 席あり、これで 1000 行を構成している。それぞれに id とは別に種別ごとの ID もついている(例えば A 席なら id=51 が 1 番、 id=60 が 10 番……)。なお、会場は一つしかないようで、この席とイベントの相関はない。中身は不変。まあなんというか、要するに明らかに無駄が多い。
  • 予約テーブル。予約ごとに1行だが、キャンセルした予約も参照することがあるため論理削除しか行われない。しかも、キャンセルフラグはわかれておらず canceled_at の時刻が NULL かどうかで判定している。席の空き状況もここで管理しているようだ(ただ、 UNIQUE KEY で二重予約が防止されている)。初期データで19万行程度あるが、そのうちキャンセルされていないのは 15k 程度しかないようだ。もちろん構成としては席テーブルよりはわかるがここも色々と問題になりやすそう。
  • 管理者の一覧。あんまり見ていなかったが、結果的にこれをいじるようなリクエストはあまり問題にならなかった。

また、今回は PHP を選択した。その場合、初期実装は Slim フレームワーク利用だった。時間はかかるが脱却すると速くはなる気はしていたのだが、初期ベンチで明らかに SQL が重かったこともあり、最後まで Slim に載せたままだった。初期状態でスコアは 1k 程度。

与えられた VM が 3 台あったが、初期実装は一台で動作するものだった。

初動

アプリケーションのロジックを主担当とすることになり、ミドルウェアの設定は残りの二人に任せてとりあえず VM のうち 1 台を使って調査開始。最初にどうにかすべきと判断したのはとりあえず席の一覧(1000件)取得後にその一件一件に対して予約状況を確認する SQL 文を発行していたこと(修正途中、別方面で分析を進めていた二人からもここがやばいと報告あり)。席テーブルの内容をハードコードし、ざっくり書き換えたのと、他の二人のミドルウェア改善と合わせてこの時点で 10k 突破(ただ、ベンチごとの揺らぎが激しく、同じコードで 5k を下回ることもあった)

これでも mysql の CPU 使用率がほとんどなので、これはデータベースチューニング回になると判断。

中盤

とりあえず見るまでもなく修正する点をつぶしている間にミドルウェアの整備が進んで kataribe や MySQL のスローログが使えるようになった。/admin/ や / が重いとわかるが、純粋にデータ量が多いため他と同じ処理時間になるわけもなく、他のボトルネックの検討も含めて相談。予約済みのもののみに興味があるクエリでは明らかに無駄で、特にキャンセル済み判定にインデックスが使えていないという yabuuuuu の指摘もあり、キャンセル判定や最終更新時刻など、テーブルにカラムをいくつか追加。変更点は ALTER やサブクエリを使えば SQL 文で完結しそうだったので /initialize の init-db.sh の修正を行い、保存された sqldump には手を付けなかった(その方が後々の変更も楽だし)。追加で必要となったインデックスも追加。

なお、空席選択のコードは初期実装では「空席の中からランダムに選ぶ」になっていたが、ランダムで通るならルール上はどのような規則で選んでも構わないと判断して、手を加える必要があった際にデバッグのしやすい「条件を満たす空席のうち id が最も若いものを選ぶ」コードを書いたところ、「ランダムではない」ことがシステムにバレて失敗扱いになったのはおもしろかった。検出のロジックが少しだけ気になった。

またついでに、潰せそうな SQL 文(SHA256 のためだけにクエリ発行しているクエリなどもあった)も潰してみたが、こちらは効果がほとんどなかった。

この間に他の二人が、ミドルウェア周りの整備および関連コードの修正を進めて DB×1, FE×2 構成に変更になった。MySQL が入っているマシンは MySQL が本気を出せるようになり、この段階で 30k 突破。一回だけなぜか 47,473 点をマークしたが、ベンチを走らせた本人曰く「修正で想定以上に改善したと思ったら、修正が反映されていなかったのに一回だけスコアが上がった」とのことで、理由は謎に包まれている。

また、この時点で必然的に環境が共有になったため、誰かがデプロイをしたくなっても他の人の作業を待つ必要があり、複数台にデプロイする手間も相まってデプロイ頻度が低下してきた。

終盤

時間があれば実施したいことは山ほどあったが、残り二時間を下回ってはそうも言っていられず、改善できそうなところから修正。複数クエリにまたがるトランザクションの削減などを試みるも、効果は限定的。相変わらずベンチごとにスコアが揺らぐ状況は続いていた。

最後の30分はベンチマークガチャに充てよう、などと話しつつ、終了30分前になって実施した再起動試験で、なんと 502 Bad Gateway になるという事件が発生した。再起動後も正常に稼働できるようになっていないと、レギュレーション上当然ながら失格となってしまうため全員で慌てて対処し、かろうじて残った約5分でベンチを回し始めたが、最後のベンチが0点でなすすべなく☀となることは避けたかったので、 17:59:10 に走行し終えたベンチを最後に終了。その時は予選突破はもっと遠い点数であり最後のベンチの点数こそが自分たちの成績になると思っていたが、結果を見た今ではもう一回回していれば結果は変わったかもしれないという大変微妙なところにいたと言える。

反省会

競技終了後、準備不足であったことを感じながら、移動および夕食のため池袋に向かった。サンシャイン60通り商店街周辺から探索を始めたが、三連休の中日の夜でどの店も長い列ができており、徐々に南下しながら探索を行い、最終的には東通り(JR 池袋駅南口の東南東)に到達した。この間に早くも ISUCON 運営の方々による再起動等の試験・集計が完了して公表され、予選突破ラインとの差がベンチマークガチャ程度の差であったことを知った上で、反省会が始まった。

感想

ウェブアプリケーションとして素直な構成でありながら、最初に一台構成で組まれたコードを複数台に分散する要素が活用されており、奇をてらったり知っているかどうかを競うだけのゲームになったりもしなかった。そのため、チューニングらしくて良いと思った去年にも増して楽しめる競技だった。

そして、そのような素晴らしい競技で今回は惜しいところで本選出場を逃したこと、事前準備を早くにしていれば当日時間短縮できそうなところが多くあったところなど、悔しい点もあった。前回の ISUCON 予選は複数台サーバの時計のずれなど、重要な点に気付く必要があり、それには時間があっても気付けなかっただろうと感じたが、今回は時間があればできたと思うことは多くあった。もちろん、 ISUCON の競技としては事前準備が効きすぎてしまうのはよくないだろうと思うし、その想定で情報を掲出している点もあるとは思うが、それでも8時間という短い時間ですべてを出し切るには特にデプロイ周り、時間の使い方や締切、(ISUCON 7 予選で複数 VM となる前例があった今)複数 VM を複数人で活用する方法など、事前準備が避けられない点は準備しておくべきだった。

NextCloud + Collabora で、 Collabora を更新すると Collabora が灰色一色になった

謎現象メモ。

表題の通り、 NextCloud + Collabora で、

  • NextCloud をアップデートして(この時点では無事なので関係ない?)
  • ついでに Collabora も apt で新しいのを入れて再起動すると
  • Collabora が開かれるべき画面で灰色一色になることがある。
  • 理由は謎だが、その後、 NextCloud の管理者用設定画面から、 Collabora の URL を設定するところで適用ボタンを押すと直った。

BlackBerry KEYone で使うためのランチャーを書いて、 Google Play にリリースした

去年、 Nexus 5X がブートループに陥り、買い換えざるを得なくなったので、BlackBerry KEYone を購入していた。キーボードがあるので快適に使っていたが、 Alfred のようなアプリがあるとより便利なのではないかと思った。幸いにも、 KEYone には(それまで使っていなかったが)「便利キー」という名前の、アプリを最大 3 つまで紐づけられて、特に一つしか紐づけなければどの画面でもボタン一つでアプリを起動できるボタンがあったので、そこから呼び出すことにしてアプリを作成、リリースまでした。

作成したアプリは「Blue Line Console」で、 Google Play にリリースした。ソースコードは GitHub のリポジトリに置いてある。

(3/27 追記:画像を一枚も貼っていなかったので、スクリーンショットを追加)

リリースするまでの流れを思い出せる範囲で書いておく。

    • アプリを作ろうと思った
    • とりあえず、 Android Studio をダウンロードしてパソコンに入れ、プロジェクトを作成した。
    • Android Studio は Git との連携が最初からついていて、 .gitignore もちゃんと書いてあったので、 Git でコードを管理できるようにした。
    • とりあえず欲しい機能が実現できるか書き始めてみた。アプリの検索周りが Android 特有の事情でどれくらい影響を受けるかわからなかったので、そこから書き始めた。
    • 動いたが遅かった。アプリ一覧そのものはすぐ取得できるが、アプリの名前の取得が遅いらしいことに気付いた。ぐぐってみると Android: Getting list of device applications – Very Slow – Stack Overflow というページがひっかかり、どうにもならなそうだったので、アプリの名前を SQLite にキャッシュすることにした。
    • レイアウトの画面で、 left / right より start / end のほうが RTL な端末にフレンドリーだよ、という警告がでていたので、 RTL 周りを調べて修正した。端末の言語設定をアラビア語に変えてどのように動作するか調べた(開発者オプションで日本語のまま RTL レイアウトにすることもできたが、どうせなので実際にアラビア語だとどうなるか調べた)。
    • 必要な機能を実装したり、レイアウトを調整したりしていた。名前がまだ決まっていなかったので、パッケージ名は仮置きのまま、アプリの名前やアイコンはデフォルトのままになっていた。
    • 途中で、 Jenkins でビルドできるようにした。gradlew ファイルを実行可能にして、Jenkins に Android エミュレータプラグインを入れて、 Gradle で “build connectedCheck” の二つのジョブを実行するようにした。GitLab からフックするようにして、コミットしたら入れられる apk ができるようにした。
    • 名前が決まったので、クラス名などを決定し、アイコンを作成した。 デフォルトで roundIcon (Android 7.1 (API レベル25)から)と anydpi なアイコン(Android 8.0 (API レベル26)から)が設定されていたが、一旦は無視することにして削除した。なお、後で Google Play にアップロードする操作の途中でアプリのアイコンの高解像度版やヘッダなどが必要になるので、アイコンができたからといってアイコン用の png だけ残して元ファイルを削除してしまうと悲しい思いをする(さすがに消さなかったが)。
    • Google Play にリリースする準備をした。
      • Google Play コンソールから、デベロッパー登録をして $25 を支払った。
      • Google Play App Signing (Google Play アプリ署名)を利用することにしたが、 Google に確認してもらうための電子署名はしないといけないので、 Jenkins の Android Signing Plugin (と Credentials Plugin)を設定した。PKCS#12 な証明書(秘密鍵あり)を keytool コマンドで作成して Jenkins の Credentials プラグインに読み込ませた。方法は後述。
      • フォームから、デフォルトの言語を選んで、アプリのタイトルや説明、スクリーンショットをアップロードした。レーティング(何歳以上じゃないと使ってはいけないとか)の判別のための設問なども必要だった。基本的には必要な作業は画面を見ればわかるようになっていた。非公開な状態で apk のアップロードと説明の記述まで実施した。
      • 完了したらアップロードボタンを押すと割とすぐにアプリのタイトルで検索してインストールできるようになった。

PKCS#12 な証明書の作成

アプリケーションへの署名 | Android Developersに従って署名を作った後、Credentials Plugin に読み込ませるために PKCS#12 に変換した。よくわかっていなかったのでこうしたが、もっといい方法がありそう。

$ keytool -genkey -v -keystore android-build-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias
$ keytool -importkeystore -srckeystore android-build-key.jks -destkeystore android-build-key.p12 -deststoretype PKCS12 -srcalias my-alias

Jenkins で Android テスト

優秀なプラグインが公開されているおかげでなんてこともない……はずなのだが、なんか迷走した挙句にすごく簡単にできることがわかったのでメモ。キーワードは「いらんことすんな」。

手順

  • Jenkins スレーブとして構成する。つまり、とりあえず最初に Jenkins スレーブにする。ホームディレクトリは必要だが、sudo できる必要はない。
  • gradle を入れる。
  • Jenkins に “Android Emulator Plugin” を入れる。つまり、 Android SDK はまだ入れない。
  • なんでもいいから Android アプリのテストを試みる。つまり、 Android SDK を自分で入れようなどと考えずにテストを試みる。プラグインが入れてくれるが、失敗した。
  • ここで初めて Android SDK を触る。結局のところ Android SDK を入れようとして完全には headless で入らなかったという状況になっているので、 Jenkins と同じ権限で (workspace)/tools/android-sdk/tools/bin/sdkmanager –list を実行する。
  • 再度テストを試みる。通る。

ここで余計なことをすると、

Warning: File /home/xxxx/.android/repositories.cfg could not be loaded.

とか

['(workspace)/tools/android-sdk']: Does not look like an Android SDK directory

とか、とにかく「なんで???」としか言いようがないエラーメッセージを睨めっこして、ぐぐっても必要な情報が出なくて苦労することになる。