Linux デスクトップ環境 2016
5年半くらい前に http://d.hatena.ne.jp/eagletmt/20100905/1283686004 というのを書いたけど、そこから今どう変わっているのか。
こう列挙してみると2016年になっても Linux デスクトップは… みたいな気持ちも無いわけじゃないけど、色んなコンポーネントを好きなように設定できたり入れ替えることができたり、場合によってはパッチをあてることもできて、そのへんが好きで使っている。 もちろん、仕事では Linux で動作するようなコードばかり書いたり読んだりしているので、そのへんの知識を手元でも使えたり手元とサーバの違いではまったりしにくいから、というのもあるけど。
ディストリビューション
相変わらずずっと Arch Linux を使っている。 パッケージの更新が早かったり、最小限のデフォルトしか設定されていないのでディストリビューションが勝手に設定しているデフォルトにはまることがなかったり、PKGBUILD がシンプルで独自パッケージを作りやすかったり、ArchWiki が非常に充実していたり、全然不満は無い。
ウィンドウマネージャ
awesome は Lua を書くのがつらくなって XMonad を使うようになっている。 最近はほぼ Haskell を書くことが無いので XMonad のために GHC を入れるのは若干だるいけど、XMonad 自体は快適なので使い続けている。
ディスプレイマネージャの類は今も昔も使っていない。普通に getty でログインして startx 叩いている。
ターミナル
mlterm はよくできてるんだけど動作が遅かったりチラつきが多かったりして rxvt-unicode を使ってたんだけど、最近また mlterm を使うようになっている。 でもやっぱり mlterm は遅いので、また rxvt-unicode を使うようになるかもしれない…… rxvt-unicode は異常に速い。
rxvt-unicode だといわゆる East Asian Ambiguous Width の問題があって「▽」とかを半角として認識してレンダリングされて困るんだけど、そのへんは ambiwidth.rb で UTF-8-CJK という charmap を作って回避している。 いまググったら同じ方法で回避している人がいた https://github.com/hamano/locale-eaw 。
ちなみに tmux も同じ問題をかかえていて、しかし tmux は glibc のロケールの定義は完全に無視して独自のテーブルを持っているので、wcwidth を使うようにしたパッチをあてて使っている https://github.com/eagletmt/arch.wanko.cc/tree/master/aur-eagletmt/PKGBUILDs/tmux-cjkwidth 。
と思ったら tmux 本体もついに wcwidth を使うようになったっぽいので、次のリリースからはパッチ不要かも https://github.com/tmux/tmux/commit/26945d7956bf1f160fba72677082e1a9c6968e0c
日本語入力
相変わらず uim の uim-skk を使っている。 昔は skk-jisyo.L を直接使っていたけど、今は yaskkserv を使っている。 SKK server completion に対応しているし、あと変換候補が L 辞書に見付からなかったときに Google Japanese Input から探してくれて便利。とくにアニメキャラの名前等の固有名詞。
各種ビューア
画像には今も feh を使っている…… 薄い本をスキャンして電子化していたりするんだけど、そういうのを読むときは mcomix を使っている。
音声や動画には、昔は MPlayer を使っていたけど、その fork の MPlayer2 を経由して、最近はさらに別の fork の mpv を使っている。 MPlayer 系は余計な GUI 要素がなくて、キーボードで完結できるのがよい。
PDF は Evince がよくできているので使っている。これ入れると GNOME 系の一部が依存で入ってくるけど仕方ない… 別途 poppler-data というパッケージも入れないと日本語を表示できない点に注意。 パスワードが設定されている PDF も Evince で普通に扱えるんだけど、まぁだるいので適当にパスワードを外したりしている… https://github.com/eagletmt/misc/tree/master/cxx/pdf-unlock
スクリーンショット、スクリーンキャプチャ
ImageMagick に import というコマンドが含まれているので、それでスクリーンショットは簡単にとれる。
スクリーンキャプチャが欲しいときは FFmpeg を使って ffmpeg -f x11grab
で。
Bluetooth
これは他に代替無いだろってかんじだけど BlueZ 5 を使っている。 BlueZ 4 -> 5 と PulseAudio 4 -> 5 の過渡期があってその時期はつらみがあったけど、まぁ今は BlueZ 5 + PulseAudio 8 で問題無く使えている。 そのへんの使い方とか設定はだいたい ArchWiki を見ればわかる。 https://wiki.archlinux.org/index.php/Bluetooth https://wiki.archlinux.org/index.php/Bluetooth_headset
フォント
最近は Web ページで色んなフォントが設定されていたり Unicode の絵文字が普通に使われるようになって、fontconfig でそのへんをちょっと調整しないとつらい時代になった。 基本的にはプロポーショナルフォントには Migu、等幅フォントには Ricty を使っている。 これでカバーできていない絵文字やハングル等は Noto Font を使っている。 Linux でレンダリングすると汚ないようなフォントが一部サイトでは font-family に指定されているので、fontconfig で sans-serif とか monospace に書き換えていたりする。 https://github.com/eagletmt/dotfiles/blob/master/dot.config/fontconfig/fonts.conf
こういうのを設定していると「この文字のグリフを含むフォントは何だろ?」というのを知りたくなると思うんだけど、標準の fc- 系のコマンドでそれを達成する方法が分からなかったので、自分で書いたりした https://github.com/eagletmt/misc/tree/master/cxx/fc-find
fontconfig の設定については、例によって ArchWiki が参考になる https://wiki.archlinux.org/index.php/Font_configuration
Docker コンテナを systemd-nspawn で動かす (作業メモ)
ちょっと前までは machinectl pull-dkr
というコマンドがあったんだけど、Docker の考えるコンテナと systemd-nspawn の考えるコンテナの差が大きいこともあって消されている。
とはいえ、Docker コンテナも systemd-nspawn (machinectl) で扱うコンテナも本質的に違うものではないので、Docker で作ったコンテナを systemd-nspawn で動かせないこともない。
以下、mysql:5.6 を例に使った作業メモ。
イメージのままだと export できないので、適当に起動してコンテナを作ってから export して、適当なディレクトリに展開しておく。
% docker run --detach mysql:5.6 false 9872d546b6d1f245f25b895ef4c05725d3fdba30d604c11801b000b78ac79d23 % docker export -o mysql56.tar 9872d546b6d1f245f25b895ef4c05725d3fdba30d604c11801b000b78ac79d23 % mkdir mysql56 % tar -C mysql56 -xf mysql56.tar
export すると Docker の ENV
とか ENTRYPOINT
とか CMD
等の情報が落ちてしまうため、適当に残しておく。
% docker inspect mysql:5.6 | jq --raw-output '.[].Config.Env | map("export \(.)") | join(";")' > mysql56/env.sh % cat mysql56/env.sh export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin;export MYSQL_MAJOR=5.6;export MYSQL_VERSION=5.6.29-1debian8 % docker inspect mysql:5.6 | jq --raw-output '.[].Config.Entrypoint | join(" ")' /entrypoint.sh % docker inspect mysql:5.6 | jq --raw-output '.[].Config.Cmd | join(" ")' mysqld
systemd-nspawn でコンテナを起動する。
% sudo systemd-nspawn --ephemeral --directory mysql56 --setenv=MYSQL_ROOT_PASSWORD=notasecret --network-veth Spawning container mysql56-f4c55f3750654603 on /home/eagletmt/work/.#machine.mysql56f1652daa7d449b0c. Press ^] three times within 1s to kill container. root@mysql56-f4c55f3750654603:~#
ここで mysqld を起動しようとすると、systemd によって /run に tmpfs がマウントされてしまっていて /run/mysqld が存在せずエラーになってしまうので、適当に回避する。
root@mysql56-f4c55f3750654603:~# mkdir /run/mysqld root@mysql56-f4c55f3750654603:~# chown mysql:mysql /run/mysqld
ホスト側から接続できるように設定。
root@mysql56-f4c55f3750654603:~# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: host0@if28: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 9e:83:dd:e9:23:c2 brd ff:ff:ff:ff:ff:ff root@mysql56-f4c55f3750654603:~# ip addr add 10.0.0.10/24 dev host0 root@mysql56-f4c55f3750654603:~# ip link set dev host0 up root@mysql56-f4c55f3750654603:~# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: host0@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 9e:83:dd:e9:23:c2 brd ff:ff:ff:ff:ff:ff inet 10.0.0.10/24 scope global host0 valid_lft forever preferred_lft forever inet6 fe80::9c83:ddff:fee9:23c2/64 scope link valid_lft forever preferred_lft forever
ようやく mysqld を起動。
root@mysql56-f4c55f3750654603:~# source /env.sh root@mysql56-f4c55f3750654603:~# /entrypoint.sh mysqld Initializing database (snip)
ホスト側から接続確認。
% mysql -uroot -h 10.0.0.10 -pnotasecret -e 'SELECT version()' +-----------+ | version() | +-----------+ | 5.6.29 | +-----------+
PID から Docker の container id を知る方法
ホスト側から見てなんか挙動が怪しいプロセスがいてその PID が分かったときに、どの Docker コンテナで動いているプロセスなのか知りたいことがある。
docker ps -q | xargs -n1 docker top
で全ての container id と PID の対応をリストアップして探すことでも達成できるけど、Docker はコンテナを起動するときに cgroup を作ってそのパスに container id を使っているので、/proc/$PID/cgroup
を見れば一発で container id がわかることに気付いた。
% cat /proc/29823/cgroup 9:freezer:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 8:cpu,cpuacct:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 7:net_cls:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 6:cpuset:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 5:blkio:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 4:pids:/system.slice/docker.service 3:devices:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 2:memory:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e 1:name=systemd:/docker/e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e
こんなかんじになっていれば、PID 29823 が動いている container id は e9e7fa08af0c5478ac379ca587ad2850ffd2a0b72b97b05201d45f3337f4750e だと分かる。
alias_method_chain と prepend を同時に同じメソッドに適用できない問題
Rails 5.0 のリリースが近づいてきてますが、Rails 5.0 から alias_method_chain
を使っていると deprecation warning が出るようになりました https://github.com/rails/rails/pull/19434 。
単純に alias_method_chain
を prepend
に書き換えればいいかと思いきや、かなりのレアケースではあるけれども、以前実際に失敗した事例の紹介です。
#!/usr/bin/env ruby require 'active_support/all' module M def foo p 'M#foo' super end end class C def foo p 'C#foo' end end class C def foo_with_modified p 'C#foo modified' foo_without_modified end alias_method_chain :foo, :modified prepend M end C.new.foo
これを実行すると
"M#foo" "C#foo modified" "C#foo"
という出力結果が得られる。予想通り。
#!/usr/bin/env ruby require 'active_support/all' module M def foo p 'M#foo' super end end class C def foo p 'C#foo' end end class C def foo_with_modified p 'C#foo modified' foo_without_modified end prepend M alias_method_chain :foo, :modified end C.new.foo
これを実行すると SystemStackError。変わったのは alias_method_chain
と prepend
の順序だけ。
言われてみればたしかにそうなるのはわかるけれども、たとえば activerecord をモンキーパッチで拡張するような gem が複数あった場合、たまたま同じメソッドに対する alias_method_chain
と prepend
が混在していると、初期化順でエラーになったりならなかったりする。
2015年の思い出
しゃかいじんにねんめ
去年は http://eagletmt.hateblo.jp/entry/2014/12/31/032313
仕事
去年末から今年の前半にかけて簡単な外向きの API サーバや社内アプリを書いていて、仕事で初めて最初から自分で書いた Web アプリになった。 まぁ最初からといっても既にある機能の置き換えみたいなやつなので、ちょっと設計考えて実装したくらい。
今年の中盤くらいからは開発環境向けの MySQL サーバをさわる機会を得た。個人ではずっと PostgreSQL で MySQL は全然わかってなかったのでいい経験だった。 去年と比べてサーバの面倒を見る時間が圧倒的に増えた。 MySQL もそうだけど、CI サーバを CentOS から Ubuntu にしたり、RRRSpec の面倒を見たり。 Web アプリ書くのも全然嫌いじゃないけど今はこっちのほうが楽しい。
発表
なんか隣の人に唆されて RubyKaigi 2015 で発表してた http://k0kubun.hatenablog.com/entry/2015/12/12/000037 。
hamlx の噂を聞いてから全然動きないなーと思って、haml も slim も文法同じなんだから slim と同じパフォーマンスはいけるでしょと 書き始めて *1 、完全に趣味で書いてたけど色々あって 本番で動くレベルになって 、最終的に RubyKaigi での発表になって一年を通して haml だった。
僕は勉強会とかカンファレンスはあんまり行かないけど、まぁこのまま1年に1回くらいのペースでできたらいいな。
そういえば RubyConf 2015 に行った。海外は ICPC で行ったことがあったけど、アメリカというか英語圏は初めて。 英語の案内とかは結構読めるんだけど、人が何言ってるかなかなか聞き取れなくて大変だった。人と話せないのは英語関係なく日本語でもそうなのである意味いつも通りだった…
インフラ
仕事で少しさわってるけどよくわからんなーというのは、積極的に個人の環境でも使うようにしてみている。 去年は puppet とか zabbix だったけど、今年は itamae とか Jenkins だった。あとはつい最近 OpenLDAP も立てた。 仕事で使ってるツールが OSS だと個人でも使えていい。 来年はネットワークの知識をなんとかしたいと思っている。ルータ自作してみたり自宅と VPS の間に拠点間 VPN 張ってみたりすると経験値貯まるのかなー。
あと ISUCON 5 に参加した。去年とは違って普通に予選を (ギリギリ) 勝ちぬいて、本選でもそこそこの順位をとれてよかった。 http://eagletmt.hateblo.jp/entry/2015/11/03/013045
来年は Ubuntu 16.04 LTS がリリースされてついに systemd を使えるようになるので楽しみ。
アニメ
去年末からの続きだけど SHIROBAKO と クロスアンジュがとてもよかった。全然違う作品だけどどちらも今後しばらく忘れないと思う。 あとあんまり話題にならなかったけどデス・パレードが本当に大好きだった。ぜひ最初の2話を順番に見てほしい。
新規に見たものの中でとくに好きだったのはユーフォニアム、終わりのセラフ、SHOW BY ROCK、グリザイアの楽園あたり。グリザイアはまぁ去年からの続編だけど。 DOG DAYS''、ジョジョ、デレマス、黒バス、Fate UBW、銀魂、プリズマイリヤ、ゆるゆり、うたわれるものあたりはもう鉄板というか当然見て当然面白かった。 あともうずっと見続けてるけどアイカツも。未だにソレイユ大好きだけどダンシングディーヴァもいいですね。
BD 買ったのは SHIROBAKO、アイカツ (映画含む)、プリズマイリヤ、SHOW BY ROCK、ストライクウィッチーズOVA、ゆるゆりなちゅやちゅみ、くらいか。来年はもっと増やしていいかも。
漫画・ラノベ
bookwalker で183冊買ってた。セールのときに一気に買ってる割合もそれなりにあるけど… 新規に買ったものだと終わりのセラフ、明日、今日の君に逢えなくても、やがてきみになる、小百合さんの妹は天使、あたりかなぁ。 終わりのセラフは小説版も買おうか迷ってる。
音楽
ついに日本でも始まった Google Play Music を使うようになった。 といっても手元にある mp3 をアップロードして便利に同期するツールとしてで、曲は相変わらず Amazon から買ってダウンロードしている。Google Play Music 品揃え悪い… 本当は全部 mp3 で買いたいんだけど、一部 CD でしか出てないので仕方なく CD も買っている。アイカツとか…
ゲーム
ムジュラの仮面3D、FE if、Splatoon、そして最近シャープ FE。 普段買わない系統で Splatoon を買って長く楽しめたのは大きい。今も定期的にやってる。 Splatoon のおかげで久しぶりに結構ゲームやってたと思う。
systemd-run によるリソース制御
systemd というと unit ファイルを書いてデーモンを起動して、というイメージが強いかもしれないけど、systemd-run を使うと単発のコマンドを systemd の管理下で実行できる。 こうすることで、CPUQuota=50% とか MemoryLimit=10M とか BlockIOWeight=10 のようにリソースを制限でき、しかも実行中に変更することもできる。
たとえば http://hb.matsumoto-r.jp/entry/2015/12/02/133448 にあるような CPU 使用率を制限しながら yes を実行する例だと、
% sudo systemd-run --scope --uid=eagletmt -p CPUQuota=10% yes > /dev/null Running scope as unit run-rcab5dc0a5f8e4620a996d95d40f7c95a.scope.
のようになる。ここから
% sudo systemctl set-property --runtime run-rcab5dc0a5f8e4620a996d95d40f7c95a.scope CPUQuota=50%
というように動的にリソース制限を変更できる。 systemd のすべてのディレクティブを systemd-run に指定できるわけではないが、リソース制御関連のディレクティブは対応している。
この例では --scope
を使ったので出力はそのままになっているけど、--scope
をつけなければ service unit として実行されるので、コマンドの出力は通常の service unit と同様にデフォルトでは journald に送られる。
参考
Unicorn 5.0.0 の sd_listen_fds(3) emulation とは
Unicorn 5.0.0 がリリースされていて、most boring major release と自称している中、新機能として sd_listen_fds(3) emulation がある http://bogomips.org/unicorn-public/20151101-unicorn-5.0.0-rele@sed/t/ 。
sd_listen_fds(3) は systemd の socket activation という機能に対応するための単純な関数で、socket activation は簡単に言うと systemd が listen してその fd を service unit のプロセスに渡す機能。 この機能の詳細やモチベーションを説明するのは面倒なので作者のブログ記事などを読んでほしい http://0pointer.de/blog/projects/socket-activation.html 。
今回 Unicorn はそれに対応したため、以下のような unicorn.service と unicorn.socket を用意することで、socket activation が可能になる。
[Unit] Description=Unicorn [Service] User=non-root WorkingDirectory=/path/to/current ExecStart=/usr/bin/bundle exec --keep-file-descriptors unicorn -c config/unicorn.rb -E production ExecReload=/bin/kill -HUP $MAINPID ExecStop=/bin/kill -QUIT $MAINPID PIDFile=/path/to/current/tmp/pids/unicorn.pid [Install] WantedBy=multi-user.target
[Socket] ListenStream=/run/unicorn.sock [Install] WantedBy=sockets.target
設定ファイルの unicorn.rb 側には変更は不要。socket activation を利用すると listen の設定が無視されるくらい。
bundle exec に --keep-file-descriptors を渡す必要がある点に気付くまでしばらくはまっていた。 ちゃんと LISTEN_PIDS と LISTEN_FDS は設定されてるのに、なぜか fd=3 が socket ではない別の何かになっていて謎だった…… Ruby 2.0.0 から 0, 1, 2 以外の fd は Kernel#exec するときにデフォルトでは閉じられてしまうそうで、それを防ぐためのオプションが bundle exec にある。 https://github.com/bundler/bundler/pull/2629 https://bugs.ruby-lang.org/issues/5041
また Unicorn のアナウンスでは「You may now stop using PID files and other process monitoring software when using systemd.」とあるけど、Unicorn の SIGUSR2 による graceful restart を利用している場合、 master の pid (systemd 的に言うと main pid) が変わるので、PIDFile は相変わらず必要になるはず。 勘違いだったら教えてください。
実際 Unicorn が socket activation に対応して嬉しいかというと、まぁそんなにメリットは無いと思う。 ほとんどアクセスが無いような個人のサービスで、実際にアクセスがくるまで Unicorn の起動を遅延できる、とかはあるかも…?
socket activation することで、Unicorn 自身が graceful restart しなくても、graceful shutdown ができればクライアントからのコネクションを途中で切ることなくアプリケーションを再起動できるメリットはある。 ただ、新しい worker を用意しつつ準備ができたら切り替える、というような細かい制御は当然 systemd 側からはできず、一旦完全に Unicorn を落としてから再度 socket activation によって起動する流れになるので、接続に失敗することはないけど Unicorn の起動にかかる時間だけブロックする形になる。 どうしても Unicorn を SIGUSR2 によるリロードではなく真っ新な状態から再起動したいときの手段が用意されているのは便利かもしれない。