Unyablog.

のにれんのブログ

systemd-nspawn に関するメモ (--as-pid2 / ネットワーク関係)

--as-pid2

指定されたものを pid 2 で実行する。pid 1 には STUBINIT が入り、pid 1 に課せられた仕事を代わりにやってくれる(シグナル処理等)。何か(実質)シングルプロセスで立ち上げたい時はコレ使ってると良さそう。

root@piyo:~# systemd-nspawn --machine hoge -n --settings=no --as-pid2 bash
Spawning container hoge on /var/lib/machines/hoge.  
Press ^] three times within 1s to kill container
root@hoge:/# ps auxwwf 
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  56984  3044 ?        Ss   06:41   0:00 STUBINIT
root         2  0.0  0.1  18220  3176 ?        Ss   06:41   0:00 bash
root         3  0.0  0.1  36636  2752 ?        R+   06:41   0:00  \_ ps auxwwf

https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#-a

ネットワーク関係

--port --network-veth --network-bridge などを使う場合、親子ともに systemd-networkd が立ち上がっているべきである。

systemd 自体は ve-hoge 等のデバイスを新たに作る。この prefix がついていると、 /lib/systemd/network/80-container-***.network が反映されて諸々の設定が行われる。

コンテナ側も同様で、 host0/lib/systemd/network/80-container-host0.network で設定がなされるので systemd-networkd が立ち上がっていないと上手く動かないことがある。*1

https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#-n

--port

--port (ポートフォワーディング)の場合、 IPMasquerade が使われているので、 systemd-networkd は必須。

このポートフォワーディングには iptables の NAT が使われており、 # iptables -L -t nat でその様子を眺めることが出来る。DebianUbuntu の場合、古い (systemd 231-5 より前) と iptables の lib がない状態でコンパイルされてて IPMasquerade は使うことが出来ない。

$ sudo machinectl start hoge

$ sudo iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT 2 packets, 200 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DNAT       tcp  --  any    any     anywhere             anywhere             tcp dpt:2000 ADDRTYPE match dst-type LOCAL to:10.0.0.2:2000

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 2 packets, 126 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DNAT       tcp  --  any    any     anywhere            !127.0.0.0/8          tcp dpt:2000 ADDRTYPE match dst-type LOCAL to:10.0.0.2:2000

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

ちなみに !127.0.0.0/8 となっていることからも分かる通り、親コンテナからはポートフォワーディングは見えないので注意。外部 IP などから確認する必要がある。

github.com

#787480 - build with iptables support - Debian Bug report logs

*1:DHCP を使っているので networking でも初期状態でまあまあ上手くはいく。

option が disabled の時の jQuery の select multiple に対する val の挙動

<select multiple>
  <option>foo</option>
  <option disabled selected>bar</option>
</select>

上のような select を用いた時に、jQuery で選択中の値を取得しようとすると disabled な option は省かれてしまう。

jquery/val.js at 56136897f241db22560b58c3518578ca1453d5c7 · jquery/jquery · GitHub

bootstrap-select が上手く動かなくて気づいた。

w3c を見ても

The disabled attribute is a boolean attribute. An option element is disabled if its disabled attribute is present or if it is a child of an optgroup element whose disabled attribute is present. An option element that is disabled must prevent any click events that are queued on the user interaction task source from being dispatched on the element.

https://www.w3.org/TR/html5/forms.html#the-option-element

と書いてあって、省くような記述はない。

また、<disabled option element>.selectedtrue を返すし、 <select element>.selectedOptions では disabled のも帰ってくる。

調べてみたら、どうやらそういうものらしい…。

#13097 (using .val() on a disabled html-select-option has different results since 1.8.3) – jQuery - Bug Tracker

javascript - Get Value of Disabled Option in Select Multiple Jquery - Stack Overflow

ISUCON7 の予選に出た (95352点)

今年も isucon に参加した。 id:tyageid:non_117 と一緒に参加した。

isucon.net

max 113846、最終的なスコアは 95252 だった。言語は ruby 。今年は社会人枠で出たので本戦には10万点ぐらい足りなかった。

まあ学生の中だけで考えると上の方に位置していたので良いんでは? 前回は fail だったし大躍進である。

レポジトリはこちら。 Systemd の設定ファイルなどコミットしていないのも一部あるけど。

github.com

準備

もともと何度か出場していたので今回は特に練習は行わなかった。デプロイ用のスクリプト、負荷試験用スクリプトなどの軽い環境整備はチームの人がやってくれていた。

当日

自分は11時過ぎに起きた。一時間遅れていなかったら遅刻するところだった。

ついたあとは3人で軽く作戦会議。初動の分担を決めたりしていた。

開始

蓋を開けてみたらまさかの複数台構成だった。しかもちゃんと DB と app が分かれていて。

あとは時系列で。

  • 13:00 mackerel やデプロイスクリプトssh 鍵などを整備。
  • 13:30 実装を眺める。nginx のログを仕込む。
  • 14:00 goaccess で計測結果の確認。 今度はスローログを見始める。
  • 14:30 nginx で /js, favicon.ico, /css, /fonts を cache で返すようにする。
  • 15:00 icons のキャッシュが上手く行かなくて調べ始める。 また、スローログから index を貼ったりする。

この時、なかなか 304 が返らなくて調べていた。最終的に tcpdump して正常時とヘッダーを見比べ、一回目のレスポンスに Last-Modified ヘッダがないことが原因とわかった。

icons は一回置かれたら変更されないことがわかっていたので、 nginx 側で add-header Last-Modified "Sat, 04 Jun 2011 08:51:44 GMT"; と固定してしまった。これで、 304 が二回目のリクエストから適用されるようになり点数がだいぶ上がった。

次は一回目の icons 取得、その他一般の api がネックになるようになったので、アプリケーションのコード自体を変更する必要が出てきた。

  • 16:30 icons 以外の遅い部分を改善し始める。 sleep はとりあえず消した。

なぜか2台目の app がうまく動かない。よく見たら python が動き続けていた!! ちゃんと disable する。

  • 17:00 proxy_cache を使って nginx で img をキャッシュすることで一回目のアイコン取得の高速化を試みる。

うまくキャッシュできたが、結局 /register で新しく登録されたアイコンを取ってくるのは遅いままなので、DB をやめてファイルに置くことを検討する。

  • 18:30 リクエストはすべて一台の nginx で受けるようにして、app を分配することにする。

また、 mysql に画像を保存するのをやめてすべて LB のあるサーバーに置くようにする( /profile もこのサーバーに飛ばす )。これで nginx から静的配信ができるようになった。この時 3~5 万ぐらい?

  • 19:00 app をよく見ると N+1 がたくさんあったので改善する。サーバー側では FD 増やしたり mysql のキャッシュ増やしたりしていた。

この頃から DB のコネクションが残り続けることが問題になり始める。結局 statement をすべて close するようにした。

  • 20:00 puma のスレッドを増やしたらスコアが増えていくことに気づく。結局二台とも50スレッドぐらい動かした。11万点ぐらい取れたので満足し始める。

この辺りでやっと CPU が全部 100% 回るようになった。

  • 20:45 諸々サービス切ったりして reboot 、最終スコアを出す。ベンチマークガチャを引いて終了。

自分は主に nginx・キャッシュ、その他サーバー周りを見ていた。

反省とか

  • キャッシュ周りの解決に2~3時間かかっており、ここを N+1 の残りとかに回せればもう少し点取れてたかもしれない。さっさとファイルに保存するようにしておけばよかった。
  • unix domain socket にしようとして失敗したのだけど、できていればファイル投稿周りは早くなっていたと思う。
  • もうちょっと早くなってくると、雑な nginx LB 一台だと帯域が〜〜ってなってきそう。
  • 結局今年は redis 一切使わなかった。 varnish とか使うと icons 周りでもうちょっと捌けたかもしれない?

  • log をみてちゃんとキャッシュができているか(304 が返っているか)丁寧に確認したのは良かった。ブラウザと挙動が微妙に違ったので。

  • mackerel-agent を入れていたのも良かった。はじめにサーバー情報を取得できるし、どこで詰まってるかもわかりやすい。

感想

バランスが取れていて良い問題だったと思います。いろいろ改善できる場所があり、楽しくて良かった。

来年も絶対に参加したい。社会人枠でも本戦に出れるぐらいにならないとな〜。

運営の方々、今年もありがとうございました。本戦の問題も楽しみです。

systemd-nspawn 235 のコンテナで mlock を行う

systemd-nspwan で Elasticsearch の運用をしている。親のホストは ArchLinux なので新しい systemd が入ってくる。

ある日、 pacman -Syu して systemd を 234 から 235 に上げたら Elasticsearch がエラーで落ちるようになった。

[2017-10-19T07:10:13,955][WARN ][o.e.b.JNANatives         ] Unable to lock JVM Memory: error=1, reason=Operation not permitted
[2017-10-19T07:10:13,957][WARN ][o.e.b.JNANatives         ] This can result in part of the JVM being swapped out.
...
ERROR: [1] bootstrap checks failed
[1]: memory locking requested for elasticsearch process but memory is not locked

ソースコードを読むと、mlockall できてなくて落ちていた。

調べた結果、systemd 235 では systemd-nspawn の SystemCallFilter がホワイトリストになり、 mlockall ができなくなった のが原因だった。

解決法

コンテナに CAP_IPC_LOCK を与えるか、@memlock グループを許可する。

例えば、 foo というコンテナであれば、 /etc/systemd/nspawn/foo.nspawn

[Exec]
SystemCallFilter=@memlock # システムコールのみを許可する場合
Capability=CAP_IPC_LOCK # CAP_IPC_LOCK ごと許可する場合

を追加すれば良い。CAP_IPC_LOCK を与えた場合、 @memlock は自動で許可される。

詳細

systemd には SystemCallFilter というのがあって、呼び出せる system call を制限できる。

https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=

systemd-nspawn では、デフォルトでこの制限がかかっており、もともとはブラックリストで制限されていた。

systemd 235 からこのリストのデフォルトがホワイトリスト形式になり、CAP_IPC_LOCK がない場合は @memlock が許可されないようになった。

    * systemd-nspawn gained support for a new --system-call-filter= command
      line option for adding and removing entries in the default system
      call filter it applies. Moreover systemd-nspawn has been changed to
      implement a system call whitelist instead of a blacklist.

https://github.com/systemd/systemd/blob/c1719d8bc924ed59448616bd748671c5c7a66d93/NEWS

当該PRは以下*1

https://github.com/systemd/systemd/pull/6818/files#diff-524fbe88b6a5bf2879b598646b22ed25R70

Elasticsearch では bootstrap.memory_lock: true とした場合に mlockall を内部で実行するのだが、これが権限不足で失敗していた。

これを解決するには、capability を足すか @memlock を直接許可すれば良い。sytemd.nspawn 内でどちらも設定できる(「解決法」を参照)。

CAP_IPC_LOCK を付加すると、自動的に @memlock が付加される*2

Note that the applied system call filter is also altered implicitly if additional capabilities are passed using the --capabilities=.

https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#--system-call-filter=

@memlock のみを許可した場合、コンテナに CAP_IPC_LOCK は与えられないので、 RLIMIT_MEMLOCK の範囲内で mlock できるようになる。

In Linux 2.6.8 and earlier, a process must be privileged (CAP_IPC_LOCK) in order to lock memory and the RLIMIT_MEMLOCK soft resource limit defines a limit on how much memory the process may lock.
Since Linux 2.6.9, no limits are placed on the amount of memory that a privileged process can lock and the RLIMIT_MEMLOCK soft resource limit instead defines a limit on how much memory an unprivileged process may lock.

https://linux.die.net/man/2/mlockall

*1:そもそも @memlock が作られたのはこの pr である

*2:capsh --print で持っている capability が見える

sssd: sssd では ldapi は使えない

ドキュメントを読めば

ldap[s]://<host>[:port]

と書いてあるので当然である…。気をつけましょう。

linux.die.net

特に使える予定もなさそう。

Bug 627763 – sssd: connection problems with ldapi

ldap: "additional info: objectClass: value #0 invalid per syntax"

slapd のセットアップで、ドキュメントに基づいて

# cat ~/admin.ldif
dn: dc=example,dc=com
objectClass: organization
objectClass: dcObject
dc: example
o: example

dn: cn=admin,dc=example,dc=com
objectClass: organizationalRole
cn: admin

ldapadd しようとしたら

# ldapadd -Y EXTERNAL -H ldapi:// -f ~/admin.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "dc=example,dc=com"
ldap_add: Invalid syntax (21)
        additional info: objectClass: value #0 invalid per syntax

というエラーが。

どうやらこれは objectClass が定義されていない時に出るエラーらしいが、 organizationdcObject も core.schema にあるし普通に定義されている。

おかしいな〜と思ってググってると同じような症状の人が。

https://www.openldap.org/lists/openldap-software/200110/msg00255.html

なるほど最後のスペース…と思って自分のものを見なおしてみたら含まれていた!!!!

どうやら openldap のドキュメントからそのままコピーしたらスペースが含まれてしまうらしい。気をつけましょう…。

↑の人はこれで2日潰れたらしい。2日は潰しませんでしたが2時間は潰しました…。

OCaml 入門途中の感想

最近京大の五十嵐先生が公開しているテキストを使って OCaml に入門していた。

このテキストは object まで行かないので入門したとは言えそうにないが、一区切り付いたので初心を記録する意味で雑に感想を書いておく。

主に Haskell を考えながら書いています、が Haskell もそんなに知らないです。

  • 型推論強くて良い
  • ref は便利ですね
    • 実用的!
  • type 、簡単に型推論してくれるのは良い
    • 変数名被ったらダメなのはつらい
  • IO 周りが楽なので良い
  • 対話環境は ghci の方が便利
  • セミコロン難しい
    • セミコロンの数で意味が変わる
    • リストよくカンマと間違える
    • 全体的に他の言語と違う作法の使い方をしているので引っかかりやすい
      • 考えられた結果なのか対抗心なのかは知らない
  • 括弧が面倒
    • Haskell における $ がほしい
    • 最近?入ったらしいけどコミュニティでは使われているのかな
    • OCaml でこんなことを言っているし LISP だとどうなることやら
  • 全体的な結合順に違和感がある
    • とりあえず括弧つければ直るが
    • 慣れの問題ではありそう
  • 遅延評価じゃないのは分かりやすい
    • ただ無限リストなどを作るのは大変になる

Haskell のほうが記号が多くて便利だし、楽しさはあると思う 。ラムダ式などはシュッと書きたい。

でもシンプルな構文により初見で分かりやすいのも長所である。どっちもどっちだ(まだ途中なので本当にシンプルなのか分からないけど)。

構文はあまり好きではない(嫌いでもない)けど、諸概念は良いし納得できるものも多いので難しいところだなあという感じ。

とりあえず Haskell の入門を再開したくなった。

予防線張りすぎでは?

まだ途中だからね!

その他

最近とにかく強い静的型付け言語を使いたい気持ちが高まっていて、python のような軽い文法で無いかなあと探していたら nim があった。また使いたい。

他の現代的な言語として Kotlin や Swift は最高だと思っているけど、エディタでサクッと書くには向いてない気がするんですよね。。