TenForward

技術ブログ。はてなダイアリーから移転しました

Plamo Linux で Let's Encrypt

はじめに (読まなくても問題ありません)

自前で Plamo Linux で運用しているサーバはずっと StartSSL を使って証明書を取得していました。

Let's Encrypt が出てきた時は、いろいろ調べてみるとコマンド著名ディストリビューション前提で、Plamo には対応していないようなので、更新時期がきた証明書も引き続き StartSSL で取得しました。

その後、Zenlogicで運用している趣味のブログをZenlogicの機能で Let's Encrypt を使った SSL/TLS 化をしたのをきっかけに少し調べてみると、Plamo Linux でも Let's Encrypt の証明書が取得できそうなので、試してみました。

最近は certbot というコマンドを使うようですね。

まず最初に考えたのが、Plamo 用のパッケージを作ってしまうことです。SlackwareでもSlackbuildsにパッケージがありますし、ArchLinuxのスクリプトを見てもそれほど難しくはなさそうです。

しかし、依存パッケージが多い! いくら python setup.py を実行するだけとはいえ、これだけのためにいくつものパッケージを自作するのは面倒だし、それだけのためにたくさんパッケージを入れるのもなんか無駄な気がします。

というわけで、certbot-auto コマンドを実行すると、virtualenv を使った環境を作って、そこで必要なことはやってくれるようなので、お気楽にやってみようということで試しました。

準備とインストール

まずは必要最低限準備するパッケージをインストールしました。(私の作ったオレオレパッケージを公開しているのでリンクしておきます)

pip, virtualenv は Plamo 標準ではパッケージが準備されていませんので、適当にインストールするなり、私の作ったオレオレビルドスクリプト(python2_pip)、(python2_virtualenv)でパッケージを作成するなりしてインストールします (pip パッケージの作成には他にもパッケージインストールが必要かもしれません)。

以下では standalone プラグインしか使ってないので、他のプラグインを使う場合は他にも準備が必要かもしれません (試してません)。

さて、ここまできたらおもむろに certbot を取得します。

$ git clone https://github.com/certbot/certbot.git

準備は OK です。clone した certbot ディレクトリに移動して、virtualenv 環境をします。

$ cd certbot
$ sh tools/venv.sh

これでいろいろと準備が行われて環境が構築されます。

動作テスト

きちんと動作するか確認してみましょう。activate を実行して virtualenv 環境に入り (?)、

# source certbot/venv/bin/activate
(venv) # certbot --help all
usage: 
  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...

Certbot can obtain and install HTTPS/TLS/SSL certificates.  By default,
it will attempt to use a webserver both for obtaining and installing the
cert. The most common SUBCOMMANDS and flags are:
  :(略)

とりあえず help を表示させてみました。うまくセットアップされていないと、ここでエラーになります。

証明書発行のテスト

これであとは証明書を発行すれば良いのですが、スクリプト化をしたり、テスト実行したりしたいでしょうから、何回も証明書の発行を行うこともあるかもしれません。

Let's Encrypt は一定期間中に発行できる証明書数の上限がありますので、とりあえずテストの証明書を発行してみましょう。そのためにステージング環境が準備されています。これで発行した証明書はブラウザでは警告が出てページがそのままでは表示されませんが、証明書の発行テストにはなります。

ステージング環境で実行するには、本番と同じコマンドに "--staging" とつけるだけなので簡単です。さらにまずは "--dry-run" でオプションに間違いがないかを確認してみましょう。

"--standalone" をつけて standalone プラグインを使う場合は、ローカルで Web サーバが起動しますので、Apache や nginx など、他の Web サーバが起動している場合は停止しましょう。

# apachectl stop
# certbot certonly --dry-run --standalone --domain www.example.com --email myaddress@example.com --non-interactive --staging
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for www.example.com
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem

IMPORTANT NOTES:
 - The dry run was successful.

うまくいきそうなら、"--dry-run" を外して実行しましょう。

発行のテストが済んだら証明書更新のテストもやってみましょう。有効な証明書があると言われるので、強制的に新しい証明書を発行するために "--force-renewal" をつけます。

certbot renew --force-renewal --staging

発行

"--staging" で発行できたら、いよいよ本番です。

手元でステージングで発行した証明書がある場合、本番でも有効な証明書があるから発行できないと怒られるかもしれません (いろいろ試してたのでどうだったか忘れたw)。その場合は先と同様に "--force-renewal" をつけます。

# certbot certonly --standalone --domain www.example.com --email myaddress@example.com --non-interactive --agree-tos
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
  :(snip)

更新は "renew" コマンドを実行するだけですが、cron なんかで自動実行する場合は、起動中の Web サーバの停止・起動が必要ですので、

certbot renew --force-renewal --pre-hook "apachectl stop" --post-hook "apachectl start"

みたいにしておくと、確認の前後で hook を実行してくれます。

certbot 実行の詳しくは以下が参考になります (以上で書いたのは適当です)。

Apache HTTP server の mod_proxy_html メモ

最近、結構音楽ブログを更新しています。昔は音楽レビューといえばひたすら言葉でレビューするだけだったのが、最近は動画の埋め込みを行うようになって表現力のない私でも気楽にレビューができるようになってきました。

動画の埋め込みというと YouTube がまず思い浮かびますね。私の音楽ブログでもよく使っています。他に音楽ブログの性格上、V LIVEの動画を埋め込むこともあります。どちらも iframe を使った埋め込みコードが動画ページから生成できますので、気楽に貼り付けられます。

さて、私の音楽ブログは最近常時 SSL 化の流れで Let's Encrypt を使って SSL 化を行いました (Zenlogicの機能を使っただけ!!)。http でアクセスしても https に飛ばされます。https でアクセスするページに YouTube のコードを埋め込むのは何の問題もないのですが、V LIVE は埋め込む URL が http なので https でアクセスするページに埋め込みコードを貼り付けてもブロックされてしまいます。

となると、自分で運用している VPS でリバースプロキシの設定をやって、それを貼り付けたらいいんじゃないか? という考えが思い浮かぶのでやってみました。まあ例えば

ProxyPass "/vlive" "http://www.vlive.tv"
ProxyPassReverse "/vlive" "http://www.vlive.tv"

ってな感じですぐに設定できました。早速喜んで埋め込みコードとして、自 VPS の URL を使ってみましたが、これだと iframe の src に書かれた先を読み込むのは https 化されましたが、その中でさらに呼ばれる js や css が相変わらず http なので、動画はブロックされたままでした。残念...

そこで確かそういう場合に使うモジュールあったよなあと思ってググってみると、proxy 通した場合のコンテンツ内の URL を変換する場合は

  • mod_substitute
  • mod_proxy_html

なんかを使うようですね。

そこでmod_proxy_htmlを試すかということで、mod_proxy_htmlの公式マニュアルを見ながら設定してみました (mod_proxy_html は以前使ったことあるような記憶があったので)。

ProxyHTMLEnable On
ProxyHTMLExtended On
ProxyHTMLURLMap http://www.vlive.tv/ https://www.example.com/

# 以下は mod_proxy_html と一緒にインストールされる proxy-html.conf 内で書かれている設定もあります
ProxyHTMLLinks meta content
ProxyHTMLLinks script src
ProxyHTMLLinks link href
  : (snip)

とするけど、ブラウザでエラーになってしまいます(Chrome だと "ERR_CONTENT_DECODING_FAILED")。うーむ、としばらくいろいろ設定を試したのですがうまくいかずに諦めようかなという時にふと目に付いたのが公式マニュアルのコメント欄

RequestHeader unset Accept-Encoding

これを加えると見事動作しました。マニュアル部分に注釈としてでも書いておいてほしいなー。

SetOutputFilter proxy-html

を設定しろみたいに書いてあるページもいくつか見つけましたが、コメント欄にあるように UTF-8 の部分が文字化けしてしまいますので不要です。

ハマったのでメモ代わりに残しておこうと思ってブログ書いたけど…、ふと今日検索してみると…

こんなページがひっかかった!! (^_^;) mod_substitute でも同じ問題が起きるようですね。なるほど、ブラウザの送るヘッダをそのまま送って圧縮した応答なんかが帰ってきた場合にうまく処理できないってことですか。

なぜハマってる最中にググった時に気づかなかったんだ!? というわけで書くほどでもなかったネタのようですが、せっかく書いたのでそのまま公開しておきます。

せっかく問題は解決したのに、V LIVE で貼り付けようとしていた動画を早速貼り付けようとしたら、YouTube でも公開されていたので、まだ音楽ブログでは活用できてません。

ip netns コマンドが意外にきめ細やかにコンテナを作ってくれる

(2016-07-26: 誤記修正しました "setns -> nsenter")

お手軽にシェルスクリプトなんかでコンテナを作る場合の強い味方といえば

  • util-linux の unshare/nsenter コマンド
  • iproute2 の ip netns コマンド

が代表的でしょう。"ip netns" は Network Namespace 作ってコマンド実行するだけの単純なコマンドかと思ったら、中では意外に色々細かくやってくれていることがわかったのでちょっと紹介しておきます。

以下は iproute2 4.2.0 で試しています。

/var/run/netns

まずよく知られているのが /var/run/netns 以下に Namespace 名のファイルを作ってくれることですね。これは、Namespace 内で動いているプロセスがなくなったら Namespace が消滅してくれるのを防ぐためです。だって ip netns add が終了したら、Namespace 内にはプロセスいなくなりますから。

まずはこの辺りで /var/run/netns を MS_SHARED にして、自身を bind mount して、ココをマウントポイントにしています。

   636        while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
        :(snip)
   644               /* Upgrade NETNS_RUN_DIR to a mount point */
   645               if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND, NULL)) {
        :(snip)
   651        }
   (ip/ipnetns.cより)

そして

   667        /* Bind the netns last so I can watch for it */
   668        if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) {
   (ip/ipnetns.cより)

ip netns add コマンドのプロセスディレクトリの ns/net ファイルを /var/run/netns 以下に bind mount していますね。このように Namespace を示す特殊なファイルを残したままにしておけば Namespace が消えません。

Namespace と veth 作成

ここで Network Namespace として "netns01" を作成し、ホストとコンテナ間は veth インターフェースを作成します。

# export NETNS="netns01"
# export VETH="veth0"

それでは Network Namespace を作りましょう。

# ip netns add $NETNS
# ip netns list
netns01

簡単ですね。

それでは veth インターフェースを作りましょう。

# ip link add name $VETH-host type veth peer name $VETH-ns
# ip link set $VETH-ns netns $NETNS
# ip addr add 10.10.10.10/24 dev $VETH-host
# ip link set $VETH-host up
# ip netns exec $NETNS ip addr add 10.10.10.11/24 dev $VETH-ns
# ip netns exec $NETNS ip link set $VETH-ns up

ホスト側は "veth-host"、コンテナ側は "veth-ns" という名前のペアを作りました。それぞれアドレスを与えて、インターフェースを up します。

では、この作成した Namespace で bash を実行してコンテナを作成してみましょう。

# ip netns exec $NETNS /bin/bash
# echo $$
11945
# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
11: veth0-ns@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ba:aa:99:70:46:ab brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.10.10.11/24 scope global veth0-ns
       valid_lft forever preferred_lft forever
    inet6 fe80::b8aa:99ff:fe70:46ab/64 scope link 
       valid_lft forever preferred_lft forever

"veth0-ns" インターフェースがあって、ちゃんとアドレスが割りあたってますね。まあ、これはそうなるようにコマンド実行していますので当たり前です。

コンテナ内の sysfs

ここで、Namespace 内で /sys 以下を見てみましょう。

# ls /sys/class/net -l
合計 0
lrwxrwxrwx 1 root root 0  7月 26日  20:01 lo -> ../../devices/virtual/net/lo/
lrwxrwxrwx 1 root root 0  7月 26日  20:01 veth0-ns -> ../../devices/virtual/net/veth0-ns/

ちゃんと Namespace 内のインターフェースのみ見えてますね。これは、この辺りの処理ですね。

    66        if (unshare(CLONE_NEWNS) < 0) {
    67               fprintf(stderr, "unshare failed: %s\n", strerror(errno));
    68               return -1;
    69        }
    70        /* Don't let any mounts propagate back to the parent */
    71        if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL)) {
    72               fprintf(stderr, "\"mount --make-rslave /\" failed: %s\n",
    73                      strerror(errno));
    74               return -1;
    75        }
    76        /* Mount a version of /sys that describes the network namespace */
    77        if (umount2("/sys", MNT_DETACH) < 0) {
    78               fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno));
    79               return -1;
    80        }
    81        if (mount(name, "/sys", "sysfs", 0, NULL) < 0) {
    82               fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
    83               return -1;
    84        }
    (lib/namespace.cより)
  • 66行目で Mount Namespace を作ってます
  • 71行目で他の Mount Namespace に自身の Mount が伝播しないようにしています
  • 77行目で現在の /sys を umount して
  • 81行目で sysfs を改めて mount します

まあ、当たり前といえば当たり前ですが、ちゃんとここまで面倒見ててくれたんですね、ip netns。

/etc/netns

そして、以上の辺りのソースをつらつらと眺めていたら、気になる関数を見つけました。

    86        /* Setup bind mounts for config files in /etc */
    87        bind_etc(name);
    (lib/namespace.cより)

これを見てみると

    35               snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name);
    36               snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name);
    37               if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) {
    (lib/namespace.cより)

"etc_netns_path" は /etc/netns/[namespace名] が入っていますので、その下のファイルを作成した Namespace 内に bind mount してくれるようです。

早速、ホスト上で /etc/netns/netns01/{hosts,resolv.conf} を作ってみました。

# cat /etc/netns/$NETNS/hosts 
10.10.10.10	host01
10.10.10.11	ns01
# cat /etc/netns/$NETNS/resolv.conf 
nameserver 8.8.8.8

以上を準備してから、再度コンテナを作成します。

# ip netns exec $NETNS /bin/bash
# cat /etc/resolv.conf 
nameserver 8.8.8.8
# cat /etc/hosts
10.10.10.10	host01
10.10.10.11	ns01

見事に作成した Namespace で実行されるコンテナでは準備したファイルが見えていますね。こんなとこまで面倒見てくれてたとは。(ちゃんと man ip-netns に書いてあるやん)

ip netns コマンドのソース、シンプルでコメントもちゃんと書かれてあって読みやすいのでコンテナ作成の勉強になる気がします。

cgroup なのか cgroups なのか

割とどーでもいい話(でも気になってた人多いはずw)

以前、第4回のコンテナ勉強会でも質問が出たのですが、cgroup/cgroups という機能の正式な名称は cgroup なのか cgroups なのか、というのはよくわかりませんでした。

私は、英語って単数・複数をきちんと使い分けるし、cgroup は複数のサブシステム・コントローラが存在するので、海外の人は "cgroups" と複数形で使うんだろうなあと思ってました。機能自体を指しているのか、コントローラ群を含めて指しているのかって、曖昧な文脈も多いですし。

しかし、この論争にもついにピリオドが打たれました。カーネル付属文書の cgroup-v2.txt をご覧ください。

"cgroup" stands for "control group" and is never capitalized. The
singular form is used to designate the whole feature and also as a
qualifier as in "cgroup controllers". When explicitly referring to
multiple individual control groups, the plural form "cgroups" is used.

https://www.kernel.org/doc/Documentation/cgroup-v2.txt

超訳

"cgroup" は "control group" を表します。大文字では表記することはありま
せん。単数形は全機能を表したり、"cgroup controllers" のように修飾子と
して全機能を表すために使います。明確に複数の個別の control groups を示
すときに、複数形の "cgroups" を使います。

Tejun Heo 氏は非英語ネイティブと想像しているのできっとその辺りが気になっていたんだ、なのでそこをはっきりさせたんだ、と妄想しています(笑)。

まあ相変わらず cgroup v1 については明確な定義が書かれてるわけではないんですがね (^_^;)

4.5 カーネルで stable となった cgroup の単一階層構造 cgroup v2 の io コントローラ

Control Group v2

以前も少し紹介していましたし、連載でも少し触れましたが、今広く (?) 使われている cgroup は色々問題があって、単一階層構造の cgroup が開発されていました。この辺りは

で紹介しました。

以前は開発中の機能だったため、マウントするときに "__DEVEL__sane_behavior" などというふざけたオプションが必要でした。(^^)

この後も順調に (?) 開発はすすみ、4.5 カーネルのリリースでついにこの機能が stable となったようで、"__DEVEL__" というプレフィックスも不要になりましたし、正式な機能で「まともなふるまい」なんてのはないだろうという話があったのかなかったのか知りませんが、名前も "Control Group v2" という名前になったようです。今までのは "v1" です。

ドキュメントは

にあります。

前に試した時は v1 にあったコントローラ (サブシステム) が全部現れていましたが、正式リリースとなった 4.5 の時点で有効なコントローラは memory, io, pid の 3 つだけのようです。

v1 の問題点のひとつに、コントローラがばらばらに実装されているため、コントローラ間の連携ができないという問題がありました。このため、blkio というブロックデバイスに対する I/O 制限を行うコントローラがあるにもかかわらず、通常のファイル I/O (の書き込み) に対する制限ができませんでした (memory と連携できていないため)。

この辺りは「第4回 コンテナ型仮想化の情報交換会@東京」で @hiro_kamezawa さんにお話頂いたので、詳細をお知りになりたい方はそちらをどうぞ。

v2 では、階層構造が単一となって、この辺りの制限ができるようになりました。昨年の LinuxCon で Heo Tejun 氏が ext2 に対応したという話をされていましたが、4.5 を見てみると ext2, ext4, btrfs に対して制限がかかるようです。

というわけで、この制限が働くのか簡単に試してみました。

kernel は 4.6-rc3、ファイルシステムext2ext4 で試しています。

かなり適当な確認なので、間違いとかあるかもしれませんので、気づいたら優しく教えてください。

v1 のおさらい

v2 を試す前に、まずは v1 の blkio コントローラのおさらいをしておきましょう。direct I/O 以外では制限がかかっていないことも確認してみました。

v1 準備

"test01" cgroup を作成して、/dev/vdb に対する 1MB/sec の読み書きの制限を設定してみました。

  1. cgroup 作成
    # mkdir /sys/fs/cgroup/blkio/test01
  2. プロセスを "test01" へ登録
    # echo $$ > /sys/fs/cgroup/blkio/test01/tasks
  3. /dev/vdbに対する制限を設定
    # ls -l /dev/vdb
    brw-rw---- 1 root disk 254, 16 Apr 13 06:30 /dev/vdb (デバイス番号の確認)
    # echo "254:16 1048576" > /sys/fs/cgroup/blkio/test01/blkio.throttle.read_bps_device (読み込み制限)
    # echo "254:16 1048576" > /sys/fs/cgroup/blkio/test01/blkio.throttle.write_bps_device (書き込み制限)
v1 を使った書き込み制限 (direct I/O)
# dd oflag=direct if=/dev/zero of=/data/testfile bs=4K count=1024
1024+0 records in
1024+0 records out
4194304 bytes (4.2 MB) copied, 4.00347 s, 1.0 MB/s

実行時に iostat を実行しました。出力の一部。

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vdb             258.59         0.00      1034.34          0       1024
vdb1            258.59         0.00      1034.34          0       1024

書き込みが 1MB/sec に制限されていますね。

v1 を使った読み込み制限 (direct I/O)
# dd iflag=direct if=/data/testfile of=/dev/null bs=4K count=1024
1024+0 records in
1024+0 records out
4194304 bytes (4.2 MB) copied, 4.0018 s, 1.0 MB/s

同様に iostat 出力の一部。

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vdb             261.22      1044.90         0.00       1024          0
vdb1            261.22      1044.90         0.00       1024          0

読み込みが 1MB/sec に制限されています。

v1 を使った書き込み制限

"oflag=direct" を指定せずに dd を実行してみると、

# dd if=/dev/zero of=/data/testfile bs=4K count=1048576
^C636264+0 レコード入力
636264+0 レコード出力
2606137344 バイト (2.6 GB) コピーされました、 22.6348 秒、 115 MB/秒
# iostat -p vdb 1 | grep "vdb "
  :(略)
vdb             311.00        12.00    315392.00         12     315392
vdb             339.00        12.00    344064.00         12     344064
vdb             456.57        12.12    434424.24         12     430080
vdb             389.53         4.65    351255.81          4     302080
vdb              11.00         0.00      9216.00          0       9216
vdb              39.00         8.00     33688.00          8      33688
vdb              35.00         0.00     31940.00          0      31940
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
  :(略)
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb              84.21         4.21     73675.79          4      69992
vdb             140.43         0.00    126012.77          0     118452
vdb              11.11         4.04      9002.02          4       8912
vdb             507.22        12.37    443455.67         12     430152
vdb             410.10        12.12    376501.01         12     372736
vdb               1.00         0.00       912.00          0        912

制限はかかっていませんね。書き込まれたらしばらく 0 の時間があり、また書き込みが再開されているのがわかります。

v1 を使った読み込み制限

"iflag=direct" を外して実行します。実行前にキャッシュクリアします。

# echo 3 > /proc/sys/vm/drop_caches (キャッシュクリア)
# dd if=/data/testfile of=/dev/null bs=4K count=1024 
1024+0 records in
1024+0 records out
4194304 bytes (4.2 MB) copied, 4.0036 s, 1.0 MB/s

読み込みは制限がききますね。

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vdb               8.25      1055.67         0.00       1024          0
vdb1              8.25      1055.67         0.00       1024          0

cgroup v2 の io コントローラ

v2 の準備

v2 は "cgroup2" という名前でマウントします。名前以外は以前試した「まともなふるまい」時代とあまり変わりません。

  1. /sys/fs/cgroup にマウント。
    mount -t cgroup2 cgroup /sys/fs/cgroup/
  2. ルート cgroup の確認。ファイルが 3 つだけです
    # ls /sys/fs/cgroup/
    cgroup.controllers cgroup.procs cgroup.subtree_control
  3. 使えるコントローラを確認
    # cat /sys/fs/cgroup/cgroup.controllers 
    io memory pids
  4. "memory" と "io" をサブ cgroup で使えるようにします
    # cat /sys/fs/cgroup/cgroup.subtree_control (サブ cgroup で使えるコントローラ一覧の確認。デフォルトは空)
    # echo "+memory +io" > /sys/fs/cgroup/cgroup.subtree_control (io と memory を追加)
    # cat /sys/fs/cgroup/cgroup.subtree_control (再度確認)
    io memory (登録されている)
  5. "test01" cgroup 作成
    mkdir /sys/fs/cgroup/test01 (作成)
    # ls /sys/fs/cgroup/test01 (test01ディレクトリの確認)
    cgroup.controllers io.max memory.events memory.stat
    cgroup.events io.stat memory.high memory.swap.current
    cgroup.procs io.weight memory.low memory.swap.max
    cgroup.subtree_control memory.current memory.max
    # cat /sys/fs/cgroup/test01/cgroup.controllers (test01で使えるコントローラの確認)
    io memory
    "test01"でioとmemoryが使えるのが確認できました
v2 で direct I/O 制限

まずは v1 でもちゃんと制限された direct I/O を確認しました。

まずは書き込み。

# dd oflag=direct if=/dev/zero of=/data/testfile bs=4K count=1024
1024+0 レコード入力
1024+0 レコード出力
4194304 バイト (4.2 MB) コピーされました、 4.0041 秒、 1.0 MB/秒
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vdb             261.22         0.00      1044.90          0       1024
vdb1            261.22         0.00      1044.90          0       1024

読み込み。

#  dd iflag=direct if=/data/testfile of=/dev/null bs=4K count=1024
1024+0 レコード入力
1024+0 レコード出力
4194304 バイト (4.2 MB) コピーされました、 4.00393 秒、 1.0 MB/秒
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vdb             261.22      1044.90         0.00       1024          0
vdb1            261.22      1044.90         0.00       1024          0

v1 と同じく制限されています。

v2 で読み込み制限

少し大きなファイルで確認しました。

# echo 3 > /proc/sys/vm/drop_caches
# dd if=/data/testfile of=/dev/null bs=4K count=1048576
^C4605+0 レコード入力
4604+0 レコード出力
18857984 バイト (19 MB) コピーされました、 18.1077 秒、 1.0 MB/秒

iostat の出力は

# iostat -p vdb 1 | grep "vdb "
vdb              30.83      2378.02     10808.93    1864321    8473984
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               7.07       630.30         0.00        624          0
vdb               8.08      1034.34         0.00       1024          0
vdb               8.00      1024.00         0.00       1024          0
vdb               8.08      1034.34         0.00       1024          0
vdb               8.00      1024.00         0.00       1024          0
vdb               8.08      1034.34         0.00       1024          0
  :(略)

これは v1 と同じように制限がかかります。

v2 で書き込み制限

さて、いよいよハイライト。

# dd if=/dev/zero of=/data/testfile bs=4K count=1048576
^C51563+0 レコード入力
51563+0 レコード出力
211202048 バイト (211 MB) コピーされました、 35.7076 秒、 5.9 MB/秒
# iostat -p vdb 1 | grep "vdb "
vdb              28.16      2182.44      9794.36    1888229    8473984
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb               0.00         0.00         0.00          0          0
vdb              36.17       144.68         0.00        136          0
vdb               1.05         0.00       842.11          0        800
vdb               1.01         0.00       517.17          0        512
vdb               1.00         0.00       508.00          0        508
vdb               1.01         0.00       783.84          0        776
vdb               3.00         0.00      1160.00          0       1160
vdb               1.00         0.00      1004.00          0       1004
vdb               1.01         0.00      1034.34          0       1024
vdb               1.01         0.00      1034.34          0       1024
vdb               1.01         0.00      1034.34          0       1024
vdb               1.01         0.00      1034.34          0       1024
vdb               0.99         0.00      1013.86          0       1024
vdb               2.02         0.00      1046.46          0       1036
vdb               2.00         0.00      1024.00          0       1024
vdb               1.01         0.00      1034.34          0       1024

落ち着くまで少し時間があるのと、一瞬 1024 以上の値が出てますが、大体きれいに 1024 (KB) で制限されています。

まとめ

とりあえずディスクへの書き込みがちゃんと制限されているっぽいです (たぶん)。