Gマイナー志向

とくに意味はありません

オープンソースになったFC2ブログをDockerで構築してみた

FC2ブログがオープンソースになったと聞いたので、Dockerfileの作成練習も兼ねてDockerで構築してみた。ソースはこちら。

https://github.com/matsuu/docker-fc2blog/

ベースOSとしてCentOS版とUbuntu版を用意した。

工夫したこと

  • Link Containerを使ってMySQLApacheを接続している
  • できるだけ環境変数でパラメータを渡せるようにした
  • できるだけDockerfileファイルにすべて収まるようにした
  • 複数のApache/MySQLが起動してもできるだけ大丈夫なように調整(かぶるのは外向けの80番ポートのみ)
  • ここ数日で身につけたノウハウをすべて注ぎ込んでみた

作ってみてわかったこと

  • FC2ブログのdomain(Cookie)周りがアレな実装なのでドメイン名を渡す必要があってアレ
    • HTTP_HOSTを使うようにした
  • ブラウザで初期設定投入後/admin/install.phpを消す必要があるがdockerで自動化しにくい
  • Dockerfileに全部詰め込むのなんとかなる

改善が必要なところ

  • セキュリティを考慮してmysql_secure_installを実行したい
  • ログ周りをsyslogかfluentdに吐くようにしたい
  • fc2blogはMySQLのMaster-Slaveに対応してるようなので対応する
  • Nginxに対応させたい
  • phpのセッションをmemcachedに食わせ、ロードバランサも用意してスケールアウトしたい
  • クラスタ構成のCoreOSでfleetを使ってウハウハしたい
    • fleet経由で起動できるようにしたがetcdを活用できてない
  • Apache(ブログ部分)だけ更新する手段を用意する
  • テストする仕組みを考える
  • プロセス監視、サービス監視
  • Gentoo版をまだ用意してない
    • 用意した

さくらのクラウドでCoreOSを動かしてみた

最近になってようやくDockerに目覚めまして、本番環境にDockerを使った場合の監視方法などを模索している今日このごろ。

ちょうどオープンソースカンファレンスさくらのクラウドの2万円分無料クーポンをもらった*1ので、さくらのクラウドGentooベース(のChromeOSベース)で有名なCoreOSを載せてDocker環境を構築してみた次第。さくらさんありがとう!ありがとう!ありがとう!

どうすればCoreOSを構築できるか

さくらのクラウドではKVM/QEMUを使用しており、CoreOSはQEMU用イメージを用意しているものの、ホスト側を操作できるわけではないのでこの方法は取れない。

そこでInstalling CoreOS to Diskを参考に構築することにした。

CoreOSを起動するサーバをまず用意する

さくらのクラウドDHCPIPアドレスが取得できず、またCoreOSのcoreユーザにはパスワードが設定されていないため、Installing CoreOS to Diskの手順でそのまま構築するとSSH、コンソールのどちらからもアクセスできなくなってしまう。

そこでまずIPアドレスを決定するためにブランクディスクでサーバを作成する。

作成後、NICタブを押して以下の4つを控えておく。

また、ディスクタブでHDDを取り外す。

Ubuntuサーバを用いてDiskにCoreOSをインストール

つづいてUbuntuでサーバを作成。先ほど取り外したディスクを接続して起動。手順はInstalling CoreOS to Diskのとおりだが、さくらのクラウドに合わせて適宜読み替え。

wget https://raw.github.com/coreos/init/master/bin/coreos-install
chmod +x coreos-install
sudo ./coreos-install -d /dev/vdb
cat > cloud-config.yml <<EOF
#cloud-config.yml

ssh_authorized_keys:
  - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h...(ssh公開鍵を記述)

write_files:
  - path: /etc/systemd/network/10-static.network
    content: |
      [Match]
      Name=ens*

      [Network]
      Address=(IPv4アドレス)/(ネットマスク)
      Gateway=(ゲートウェイ)
      DNS=(推奨ネームサーバ1)
      DNS=(推奨ネームサーバ2)
EOF
sudo mount /dev/vdb6 /mnt
sudo cp cloud-config.yml /mnt/
sudo umount /mnt
CoreOSを起動する

ここまでできたら一旦Ubuntuサーバの電源を落として20GBの追加ディスクを取り外し、CoreOSサーバに接続しなおして起動。

ssh core@(IPv4アドレス)

で接続すればok。

   ______                ____  _____
  / ____/___  ________  / __ \/ ___/
 / /   / __ \/ ___/ _ \/ / / /\__ \
/ /___/ /_/ / /  /  __/ /_/ /___/ /
\____/\____/_/   \___/\____//____/
core@localhost ~ $ 

ヒャッホーイ!

まとめ

*1:実はもらったクーポンを紛失したので中の人に再発行してもらった

Mac Pro(Late 2013)を火鉢に見立てて暖を取る方法

Appleの初売りに並んでる皆様、寒い中お疲れ様です。寒いですね!ツライですね!

そんな時は最新のMac ProのCPUをぶん回せば暖かくなるかもしれませんね!
え?インターネット接続環境がないからベンチマークソフトをダウンロードできない?大丈夫、Mac OS Xに標準で含まれているソフトウェアでお手軽にCPUをぶん回すことが可能です。

  • Finder→アプリケーション→ユーティリティ→ターミナル
  • CPUが論理12コアの場合
openssl speed -multi 12
  • CPUのコア数がわからない場合
openssl speed -multi `getconf _NPROCESSORS_ONLN`

これでしばらくはCPU負荷がガンガンかかり、Mac Pro上部の排気口がほのかに暖かくなりますよ!良かったですね!良かったですね!
もしかしたらアップルストアの店員さんがMac Proを用意してくれるかもしれませんね!ご期待ください!

ご参考

きっかけはTwitterから。

yes > /dev/nullでもok。ただ論理CPU1つ分しか負荷がかからないので論理CPUの数だけ起動が必要となる。

どんどんプロセスを立ち上げるのでこれはfork bombに近い。やっちゃダメー。

openssl speedはOpenSSLが対応するアルゴリズムの処理速度を計測するベンチマーク。-multiで指定された数だけパラレルに実行されるのでカジュアルにCPUに負荷をかけられる。これは素晴らしい。

RHEL(CentOS)6系でトラフィックをたくさん捌くサーバが死ぬ問題は6.5のkernel-2.6.32-431.el6以降で多分直る

タイトルで言いたいことはすべて言った。

経緯

うちの場合はLVS+keepalivedなロードバランサなんだけど、ちょくちょくkernel panicになる問題が発生してた。
そこでcrashコマンドで解析してみた。crashコマンドの使い方はこちらが参考になる。Linux crash dump 読み方入門

# crash /boot/System.map-2.6.32-279.14.1.el6.x86_64 /usr/lib/debug/lib/modules/2.6.32-279.14.1.el6.x86_64/vmlinux /var/crash/127.0.0.1-2013-09-27-16\:21\:01/vmcore 
(snip)
  SYSTEM MAP: /boot/System.map-2.6.32-279.14.1.el6.x86_64              
DEBUG KERNEL: /usr/lib/debug/lib/modules/2.6.32-279.14.1.el6.x86_64/vmlinux (2.6.32-279.14.1.el6.x86_64)
    DUMPFILE: /var/crash/127.0.0.1-2013-09-27-16:21:01/vmcore  [PARTIAL DUMP]
        CPUS: 8
        DATE: Fri Sep 27 16:19:55 2013
      UPTIME: 3 days, 15:03:51
LOAD AVERAGE: 0.01, 0.04, 0.00
       TASKS: 283
    NODENAME: XXXXXX(masked)
     RELEASE: 2.6.32-279.14.1.el6.x86_64
     VERSION: #1 SMP Tue Nov 6 23:43:09 UTC 2012
     MACHINE: x86_64  (2128 Mhz)
      MEMORY: 8 GB
       PANIC: "kernel BUG at net/ipv4/tcp_ipv4.c:417!"
         PID: 0
     COMMAND: "swapper"
        TASK: ffff88022a568aa0  (1 of 8)  [THREAD_INFO: ffff88022a56c000]
         CPU: 3
       STATE: TASK_RUNNING (PANIC)

ログ

crash> log
(snip)
kernel BUG at net/ipv4/tcp_ipv4.c:417!
invalid opcode: 0000 [#1] SMP 
last sysfs file: /sys/devices/system/cpu/cpu7/cache/index2/shared_cpu_map
CPU 3 
Modules linked in: ip_vs_rr ip_vs_wlc ip_vs_wrr ipmi_si mpt2sas scsi_transport_sas raid_class mptctl mptbase ipmi_devintf ipmi_msghandler dell_rbu ip_vs libcrc32c 8021q garp stp llc ipv6 nf_nat_ftp nf_conntrack_ftp nf_connt
rack_netbios_ns nf_conntrack_broadcast xt_limit ipt_REJECT xt_state xt_multiport iptable_filter ipt_LOG ipt_MASQUERADE xt_mark iptable_nat nf_nat nf_conntrack_ipv4 nf_conntrack nf_defrag_ipv4 xt_MARK iptable_mangle iptable_
raw ip_tables power_meter ses enclosure sg bnx2 dcdbas microcode serio_raw iTCO_wdt iTCO_vendor_support i7core_edac edac_core ext4 mbcache jbd2 sr_mod cdrom sd_mod crc_t10dif pata_acpi ata_generic ata_piix megaraid_sas dm_m
irror dm_region_hash dm_log dm_mod [last unloaded: ipmi_si]

Pid: 0, comm: swapper Not tainted 2.6.32-279.14.1.el6.x86_64 #1 Dell Inc. PowerEdge R410/01V648
RIP: 0010:[<ffffffff81494c71>]  [<ffffffff81494c71>] tcp_v4_err+0x5e1/0x5f0
RSP: 0018:ffff88002f863bd0  EFLAGS: 00010246
RAX: ffff8801bde90208 RBX: ffff880215d49c2c RCX: ffff8801bde90208
RDX: 00000000000007d0 RSI: 0000000000000002 RDI: ffff8801bde90188
RBP: ffff88002f863c50 R08: ffff8801bde90150 R09: ffff8801bde90188
R10: 0000000055d6096c R11: 0000000000000001 R12: ffff8801bde90140
R13: ffffffff8200cec0 R14: ffff880215d49c40 R15: 0000000000000071
FS:  0000000000000000(0000) GS:ffff88002f860000(0000) knlGS:0000000000000000
CS:  0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 0000003da9613000 CR3: 0000000217c2f000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process swapper (pid: 0, threadinfo ffff88022a56c000, task ffff88022a568aa0)
Stack:
 ffff880000000001 ffffffffa01b3557 ffff880100000014 000000000000b0c6
<d> ffff8801bde90188 fd02a8c0fd02a8c0 0000c6b000000001 01ffffff00000000
<d> 0000000080000000 000000002f863cd8 0000000080000000 0000000000000006
Call Trace:
 <IRQ> 
 [<ffffffffa01b3557>] ? ipv4_confirm+0x87/0x1d0 [nf_conntrack_ipv4]
 [<ffffffff8149f0d0>] icmp_unreach+0x140/0x2d0
 [<ffffffff8149ee40>] icmp_rcv+0x290/0x330
 [<ffffffff814718d0>] ? ip_local_deliver_finish+0x0/0x2d0
 [<ffffffff814719ad>] ip_local_deliver_finish+0xdd/0x2d0
 [<ffffffff81471c38>] ip_local_deliver+0x98/0xa0
 [<ffffffffa0126813>] ? bnx2_poll_work+0x633/0x1270 [bnx2]
 [<ffffffff814710fd>] ip_rcv_finish+0x12d/0x440
 [<ffffffff81471685>] ip_rcv+0x275/0x350
 [<ffffffff81447390>] ? neigh_timer_handler+0x0/0x340
 [<ffffffff8143adcb>] __netif_receive_skb+0x49b/0x6f0
 [<ffffffff8143b0ba>] process_backlog+0x9a/0x100
 [<ffffffff8143f7a3>] net_rx_action+0x103/0x2f0
 [<ffffffff81073f61>] __do_softirq+0xc1/0x1e0
 [<ffffffff81096d60>] ? hrtimer_interrupt+0x140/0x250
 [<ffffffff8100c24c>] call_softirq+0x1c/0x30
 [<ffffffff8100de85>] do_softirq+0x65/0xa0
 [<ffffffff81073d45>] irq_exit+0x85/0x90
 [<ffffffff81506450>] smp_apic_timer_interrupt+0x70/0x9b
 [<ffffffff8100bc13>] apic_timer_interrupt+0x13/0x20
 <EOI> 
 [<ffffffff81407a38>] ? poll_idle+0x38/0x80
 [<ffffffff81407a13>] ? poll_idle+0x13/0x80
 [<ffffffff81407c27>] cpuidle_idle_call+0xa7/0x140
 [<ffffffff81009e06>] cpu_idle+0xb6/0x110
 [<ffffffff814f754f>] start_secondary+0x22a/0x26d
Code: 4c 8b 4d a0 e9 71 fc ff ff 89 d2 eb 8d be ca 01 00 00 48 c7 c7 6e 46 80 81 e8 8c 6b bd ff 44 8b 55 98 4c 8b 4d a0 e9 1e fd ff ff <0f> 0b eb fe 90 90 90 90 90 90 90 90 90 90 90 55 48 89 e5 48 83 
RIP  [<ffffffff81494c71>] tcp_v4_err+0x5e1/0x5f0
 RSP <ffff88002f863bd0>

バックトレース

crash> bt
PID: 0      TASK: ffff88022a568aa0  CPU: 3   COMMAND: "swapper"
 #0 [ffff88002f863890] machine_kexec at ffffffff8103284b
 #1 [ffff88002f8638f0] crash_kexec at ffffffff810ba982
 #2 [ffff88002f8639c0] oops_end at ffffffff81501b00
 #3 [ffff88002f8639f0] die at ffffffff8100f26b
 #4 [ffff88002f863a20] do_trap at ffffffff815013f4
 #5 [ffff88002f863a80] do_invalid_op at ffffffff8100ce35
 #6 [ffff88002f863b20] invalid_op at ffffffff8100bedb
    [exception RIP: tcp_v4_err+1505]
    RIP: ffffffff81494c71  RSP: ffff88002f863bd0  RFLAGS: 00010246
    RAX: ffff8801bde90208  RBX: ffff880215d49c2c  RCX: ffff8801bde90208
    RDX: 00000000000007d0  RSI: 0000000000000002  RDI: ffff8801bde90188
    RBP: ffff88002f863c50   R8: ffff8801bde90150   R9: ffff8801bde90188
    R10: 0000000055d6096c  R11: 0000000000000001  R12: ffff8801bde90140
    R13: ffffffff8200cec0  R14: ffff880215d49c40  R15: 0000000000000071
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #7 [ffff88002f863c58] icmp_unreach at ffffffff8149f0d0
 #8 [ffff88002f863c98] icmp_rcv at ffffffff8149ee40
 #9 [ffff88002f863ce8] ip_local_deliver_finish at ffffffff814719ad
#10 [ffff88002f863d18] ip_local_deliver at ffffffff81471c38
#11 [ffff88002f863d48] ip_rcv_finish at ffffffff814710fd
#12 [ffff88002f863d88] ip_rcv at ffffffff81471685
#13 [ffff88002f863dc8] __netif_receive_skb at ffffffff8143adcb
#14 [ffff88002f863e28] process_backlog at ffffffff8143b0ba
#15 [ffff88002f863e78] net_rx_action at ffffffff8143f7a3
#16 [ffff88002f863ed8] __do_softirq at ffffffff81073f61
#17 [ffff88002f863f48] call_softirq at ffffffff8100c24c
#18 [ffff88002f863f60] do_softirq at ffffffff8100de85
#19 [ffff88002f863f80] irq_exit at ffffffff81073d45
#20 [ffff88002f863f90] smp_apic_timer_interrupt at ffffffff81506450
#21 [ffff88002f863fb0] apic_timer_interrupt at ffffffff8100bc13
--- <IRQ stack> ---
#22 [ffff88022a56ddf8] apic_timer_interrupt at ffffffff8100bc13
    [exception RIP: poll_idle+56]
    RIP: ffffffff81407a38  RSP: ffff88022a56dea8  RFLAGS: 00000246
    RAX: 0000000400000000  RBX: ffff88022a56ded8  RCX: 000000000000001f
    RDX: ffff88022a56dfd8  RSI: ffff88002f87dcd0  RDI: ffffffff81a8fc40
    RBP: ffffffff8100bc0e   R8: 0000000000000004   R9: 0000000000000010
    R10: 0000000000000000  R11: 0000000000000000  R12: 0000000000000000
    R13: 0000000000000000  R14: 0000000000000002  R15: 0000000000000050
    ORIG_RAX: ffffffffffffff10  CS: 0010  SS: 0018
#23 [ffff88022a56dee0] cpuidle_idle_call at ffffffff81407c27
#24 [ffff88022a56df00] cpu_idle at ffffffff81009e06

得られた情報からググってみたところ、これと全く同じ症状だった。

kernel panic in tcp_v4_err() on icmp unreachable

該当パッチは2011年1月にリリースされた2.6.37で取り込まれているのだが、2.6.32を採用したRHEL6にはバックポートされていなかった。ちなみにDebianには2012年9月にリリースされた2.6.32-46でバックポートされた模様。

Debian Bug report logs - #685087

そこでRedHatのbugzilla(一般非公開)に報告したものの待てど暮らせどと変化なし。
あとでRedHatの中の人に聞いたところによると、

RHELの開発者はどれだけ深刻な問題でもBugzillaから直接報告されたバグ報告は見てない。RHNのライセンスを介して報告しないと本家には伝わらない。

とのこと。あらーそうなのね。
でも運良くRedHatの人に拾ってもらえて、古いバグ報告(こちらも非公開)にmarked as duplicateされた後、RHEL6.5のリリースと共に修正が取り込まれた。

カーネルのリリースノートにはこのバグ修正に関する言及はないのだが、同じ問題で悩まれていた方は是非カーネルアップデートをご検討ください。

追記1

Bug #757658Bug #1014877が非公開になっているのは、セキュリティのリスクを考慮してだと思う。CVEとして報告されているわけでもないので。ちなみにBug #757658が登録されたのは2011年11月28日。約2年越しのバグ修正だった*1

追記2

RedHatの中の人より

実は俺もこの意見には賛成で、RedHatが直接Bugzillaに報告された報告を拾うことはあっても、必ず対応する必要なんてないと思ってる。それがどんなにひどいバグだとしても。

ちなみに昔はご丁寧に拾ってくれていたそうです。いつからそのような方針に変わったかは不明。

*1:ちなみに私が報告したBug #1014877の登録日は2013年10月2日

金に物を言わせてISUCONのプログラムをc3.8xlargeで動かしてみた

先日AWSから新しいEC2インスタンスタイプc3.8xlargeが発表されましたね。
【AWS発表】 新世代の数値計算向けEC2インスタンス

2013年11月時点の性能と価格は以下のとおり。ISUCON予選で使用したm3.xlargeと比較。

インスタンスタイプ vCPU ECU Memory(GiB) Tokyoリージョンの1時間あたりのオンデマンド価格
m3.xlarge 4 13 15 0.684ドル
c3.8xlarge 32 108 60 3.064ドル

CPU性能約8倍、メモリ4倍。価格約4.5倍。c3.8xlargeお得。

さて、オンデマンドの価格は上のとおりですが、開始後すぐのためか需要が少なくスポットインスタンスが激安なんですね。

先ほど確認したところ、まだ0.005ドルだったのを確認。

ちなみにm3.xlargeは0.148ドル。

0.005ドルは安い!これは今試しておかないと損!そこでISUCONのプログラムをm3.xlargeとc3.8xlargeで試してみました。

本選プログラム

公式のAMIと構築手順を元に構築。ベンチマークコマンドは以下の通り。

$ carton exec perl bench.pl -d /home/isucon/image_source http://127.0.0.1/

プログラムには一切手を加えてません。

m3.xlarge
starting benchmark: concurrency: 6, time: 60
done benchmark: score 2139.77336684146, elapsed 60.681 sec = 35.263 / sec

スコアは2139.8。うんまぁそんなもん。

c3.8xlarge
starting benchmark: concurrency: 6, time: 60
done benchmark: score 3703.04509395234, elapsed 62.458 sec = 59.289 / sec

スコアは3703.0。うーむ、もっと爆速になるかと思いきや、そうでもなかった。
おそらくストレージ(EBS)にファイルを生成する部分がボトルネックになっているのではないかと思われます。

予選プログラム

予選プログラムについても同様に試してみました。公式AMIを使い、プログラムには一切手を加えてない。

# isucon3 benchmark
m3.xlarge
Result:   SUCCESS 
RawScore: 1162.8
Fails:    0
Score:    1162.8

1162.8。うん、そんなもんでしたね。

c3.8xlarge
Result:   SUCCESS 
RawScore: 836.6
Fails:    0
Score:    836.6

836.6。えっ。
何度かやってみたがほぼ同じ。成績下がった。なんでやねん。
おそらくCPUの並列性が上がったせいでMySQLで詰まった感じでしょうか。ちゃんと検証してません。

まとめ

対象 m3.xlarge c3.8xlarge
本選プログラム 2139.8 3703.0
予選プログラム 1162.8 836.6
  • 金に物を言わせてスケールアップすれば万事okってわけではない
  • ちゃんとボトルネックを把握するの大事
  • ちょっとした実験ならスポットインスタンスがオススメ

isucon3本選に└('-'└)└)└)<HEARTBEATSとして参加した&予選本選を通して得た知見

11月9日に開催されたisucon3本選にチーム「└('-'└)└)└)<HEARTBEATS」*1として参加してきました。
結果は惨敗だったのですが、当日やったことと、予選本選を通して得た知見を書き留めておきます。

当日やったこと

画像のサムネイル作成をなんとかするためImager使ったら孔明の罠

ベンチマーク中にtopを眺めているとconvertコマンドがボトルネックになっていたのでここをなんとかしようと試みてみました。
convertを呼び出す代わりにImagerを使ってサムネイルを作成してみたのですが、Imagerだと差分検出でエラーになってしまいました。見事な孔明の罠ですね。やられた。

Image::Magickのインストールに手間取る

Imagerが差分検出で引っかかるので早々にあきらめてImage::Magickを使おうとしたのですが、cpanfileに

requires "Image::Magick";

を追記してcartonを実行してもインストールがエラーになって入らない。
includeするファイルが足りないのどうのと言われるので

yum install ImageMagick-devel

を実行してからインストールしてもダメ。なんだ。あ、もしかしてバージョンの問題か!幸いremiリポジトリが入ってたので、

yum remove ImageMagick ImageMagick-devel
yum install ImageMagick-last ImageMagick-last-devel

とすることでcartonでImage::Magickのインストールはできた…のだが、何故かStarletが起動する際にはImage::Magickがないって怒られる。なんでや。local::lib環境でXSはあかんのん?

よくわからないままApp::cpanminusでImage::Magickをインストールしてみたら

cpanm Image::Magick

あれ、なんかいけたっぽい。

Image::Magickで再実装するもエラー解消できず

PerlMagickのドキュメントを見ながらImage::Magickを実装するも、何故かうまく実装できず。試行錯誤するも時間がもったいないので途中で断念しました。

413 Request Entity Too Largeが出てて時間を若干とられた

あるタイミングでベンチマーク実行時のエラーで413 Request Entity Too Largeが出ててなかなか解決せず。nginxからapacheに戻すと解消したことからclient_max_body_sizeを指定してなかったことに気づいたわけですが、上記Image::Magickでの再実装とタイミングが重なっていたので原因究明に時間がかかってしまいました。

/tmpをtmpfsでマウントしたが性能下がった

Image::Magickによる実装がうまくいかないのでconvertコマンドのまま実装を進めていたわけですが、convertコマンドはtempfile()を吐き出し先として使っていたため、/tmpをtmpfsでマウントすればちょっとは性能上がるんじゃね?と思いマウントしてみたのですが、逆に性能が下がってしまったので断念。キャッシュに割くメモリの領域を食ってしまったからですかね?よくわからず。

サムネイル作成時にファイル保存

サムネイルを作成するタイミングでしておき、ファイルが存在する場合はファイルを直接返すことで性能が改善しました。サムネイルを作成するタイミングを変更するといった処理変更は実施してません。

timeline表示のSLEEPを2秒から1秒に

timelineで値が取得できなかった場合に2秒間待機する処理が入っていましたが、これを1秒に変更、またtimelineで都度ユーザ情報を取得してた部分をPerl上でキャッシュ、UPDATEがかかったときはPerlのキャッシュを削除*2するなどいたところ、6100程度までスコアが伸びていました。が、このあたりが自分の限界でした。

スケールアウトの方法をきちんと考えておらず1台構成のままフィニッシュ

終盤に複数台を活用する方法を考えるも良い案が思いつかず、とりあえず

  • 5台ともフロントにする
  • 画像投稿の時だけ1台目にproxyして1台目で処理
  • 投稿された画像はlsyncdで他4台に同期
  • 投稿以外は5台それぞれで処理

なんてザックリ考えていて、その準備が終わらずフィニッシュ。ついでに試行錯誤してるうちにスコアもだだ下がりしてて散々な結果となりました。チーン。

予選本選を通して得た知見

Varnishはできる子

予選の復習をしてる際に思ったのが、Varnishはとてもできる子なんじゃないか説。
httpdStarmanの間にvarnishを挟むだけで良しなにキャッシュしてくれてスコアが1100→2000と改善しました。
さらに予選のようにそのままではキャッシュしにくいパターンでもESIを駆使することで比較的簡単に部分キャッシュが可能なのでかなり使えるのではないかと感じました。
本選のようなパターンだと素直にキャッシュできないので簡単には使えませんけどね…。でもモジュールが豊富で

とかあるし結構いけるんじゃないかと思います。

apachetopとwtopは惜しい子

アクセスログからどのクエリが多いか、時間がかかっているかを調べるのにapachetopwtopが便利でした。
単純にログを見るだけなので、Apacheと同じ形式でログ出力すればnginxと組み合わせても使えます。

wtopはクラスを定義すればログの種類をまとめることができるのでとても良いのですが、HTTPメソッド毎の分類ができないのがちょっと残念です。改善してpull requestを送りたい。そのうち。

今回もプロファイラのDevel::NYTProfを使いこなせなかった

isucon予選でDevel::NYTProfをうまく使いこなせなくて、そのことをツイートしたらmiyagawaさんから

とヒントを頂いたのですが、それでもうまく使いこなせず。Devel::NYTProfは今後の課題です。

追記:検証してみたところ、最新版のPlack::Middleware::Profiler::NYTProfを使うと Kossy::__ANON__が上位にくることもなく綺麗にプロファイリングできていい感じでした。

最後に

└('-'└)└('-'└)└('-'└)└('-'└)└('-'└)
 "ISUCON 準備お疲れさまでした!!"
    ありがとうございました!
 来年もよろしくお願いいたします!
└('-'└)└('-'└)└('-'└)└('-'└)└('-'└)

*1:予選のチーム名は└('-'└)でした

*2:今思えば複数台構成にするとこれダメね

2chの書き込み規制のしくみを調べてみた

規制されてないのに2chに書き込めない原因判明

スレを読み進めていくと、どうやら
206.223.144.0/20
207.29.224.0/19
からのTCPポート
443
995
1723
へのアクセスをフィルタすれば書き込めるらしい。

書き込むたびにTCPポートスキャンをしているの?と興味が湧いたので調べてみた。
ちなみに元記事には情報源スレッドのURLが記載されているが、読み方がわからなかったので読んでない。

Twitterに全部書いたので再掲

tcpdumpで確認したのだが、上記の443、995、1723以外は確認できなかった。2,3回しか試してないので実際には他のパターンもあるかもしれない。
クッキーを食わせた状態でもう一度書き込んだ場合はポートスキャンは来なかった。

書き込みテストは「2ch 書き込み テスト」でググって出てきたここに。
書き込みテスト 専用スレッド637
書き込みの作法を知らないのでよくわからなかったんだけども。

ポートの待ち受けはPerlでIO::Socket::INETを使ってテキトーに書いた。

#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;

my $s = IO::Socket::INET->new(Listen => 5, LocalPort => $ARGV[0], Proto => 'tcp', ReuseAddr => 1) or die $!;

$s->listen;

while(my $c = $s->accept) {
  while(my $q = $c->getline) {
    print $q;
    $c->print($q);
  }
  $c->close;
}
$s->close;

3ハンドシェイク後にFIN-ACKをすぐ投げてるので何も出力されなかった。

まとめ

TCPポートスキャンは不正アクセスにあたるのかについては詳しくないのだけれども、3ハンドシェイクとFIN-ACKだけならまぁ許容範囲じゃないですかね。
これだけだと簡単にすり抜けられるけど、かといってより良い方法は今すぐ思いつかない。お手軽にチェックする実装としてはまぁアリかなと思いました。