なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

HTTPでバックアップファイルを軽く配る

最近はバックアップファイルを配る時にHTTPを使っている。

クライアント側では curl を使って手軽に引っ張ってこれてそのまま tar に流して簡単に展開できて便利。

あとは scp だとCPUが貧弱なマシンだとやや使いづらい。

クライアント側からのアップロードをするには WebDAV を使う必要があって少し手間だけどその程度で一回用意すれば使い勝手はそこそこよい。

あとは Subversion と組み合わせるのとかも便利かな。いや、これはどっちでもいいか。

curl

curlでhttpリクエスト飛ばすだけでいいけど、バックアップサーバはHDDのようなストレージを乗せていて、 バックアップを取ってる時に復元しようとするとIO的にしんどいタイミングがある。

そういうタイミングは --limit-rate をつけてちょっとだけ優しくするとよい。

curl --limit-rate 100M http://backup.local/data.tar.gz | tar xzv

100M は何となく。シーケンシャルで取るならHDDでも100Mくらいは軽く出るけど、出ないときもあるのでその時はもっと優しくする。 あとは、 ionice しておくとか。ただサーバ側でつけるのは面倒だしクライアント側だけで完結する --limit-rate 程度が手間がなくていいかな。人力なのがちょっとだけ温かみがある。

rsync と scp

暗号化されてCPUが貧弱だと辛い時があるけど、一般にはよく使われる。 rsync はパスの指定で最後に / を付けるようにしているけどたまに忘れると面倒なことになる。 まあこれはどうでもいい。

あと、暗号化方式を軽くする時に -e "ssh -c arcfour" を入れるというテクニックもあったけど、 openssl のバージョンアップに伴って RC4 は使えなくなりつつある。

まとめ

HTTPサーバは apache とか nginx あたりを適当に使えばいいので作るのは簡単。 Web屋さんなら軽く用意できるかな、って感じです。

PXEBOOT/BOOTP/DHCPあたりのことをまとめておく

この辺りはよく使ってる技術なんだけど一回作ってしまうと細かなことは忘れてしまうし、 AWSのようなクラウドサービスを使っていると触れる機会も減ってしまうので、 覚えているうちにまとめておこうと思う。

PXEBOOT

ネットワークカードに搭載されているPXE(Preboot eXecution Environment)機能を使ってプログラムを起動する方法。

起動するプログラムはDHCPサーバとTFTPなどのサーバを組み合わせて配布する。

ネットワークカードにPXEBOOTのための機能が搭載されているものがあれば、BIOSのBOOTをネットワークブートに設定すれば使える。

DHCPはBOOTPの上位互換プロトコルなので、BOOTP的な設定はDHCPサーバの設定に書くこともできる。

DHCP設定にTFTPサーバがどのアドレスかなどの情報を書いておき、クライアントに配布できるが、特に指定がない場合DHCPサーバとTFTPサーバは同じアドレスとみなされる。

TFTP、DHCPサーバともに軽量なサーバなことが多く、同じ物理サーバに設置することもある。

DHCP

PXE対応ネットワークカードは起動時にDHCPマルチキャストパケットを投げる。

ネットワーク上にDHCPサーバがあれば、パケットに応答しPXEの情報をクライアントに返す。

複数のDHCPサーバが応答する場合、どれが応答するかわからなくなるため同一ネットワークに複数のDHCPサーバと立てないようにしたい。

TFTP

UDPでやりとりするファイル転送用のプロトコルFTPより単純で認証などない。PXEで実行するイメージを転送する際に使うことができる。

暗号化も特にされていないので ngrep などでパケットを眺めればどういうやり取りがされているか簡単にわかる。

MACアドレスDHCPで割り当てされたIPアドレスの一部を使って設定ファイルをダウンロードしようとする。これはPXEの仕様。

例えば、 192.168.0.200DHCPで割り当てた場合、それぞれのアドレスを16進数に変換した、 C0A800C8 というファイルをリクエストする。

ファイルが見つからなかった場合、1文字短くした C0A800、 次には2文字短くした C0A8 といったファイルを順にリクエストしてくる。

この仕組みがあるので、サブネットごとに違うアドレスレンジをDHCPサーバが返すことでネットワークごとに違う設定ファイルをクライアントに渡すことができる。

設定ファイルの実体は pxelinux.cfg と同じ書式のもので、シンボリックリンクで名前だけ用意するのが簡単。

TFTPサーバはセキュリティの都合上、chroot化で実行されることがままあるため、シンボリックリンクを張る際には / からの絶対パスではなく相対パスで作成しておくのが無難。

DebianPXEサーバとDHCPサーバとTFTPサーバを作る例

TFTPサーバは tftpd-hpaDHCPサーバは isc-dhcp-server をそれぞれ使う。

apt install tftpd-hpa isc-dhcp-server

TFTPサーバの設定

TFTPサーバの設定は /etc/default/tftp-hpa だが、対して設定項目もないのでそのまま使ってもよい。

その場合、 /srv/tftp がTFTPサーバのルートとなり、chrootされた状態でサーバが起動される。

TFTP自体の設定は対してないが、いくつか重要なファイルがある。

/srv/tftp/pxelinux.0

pxelinuxのブートイメージ。後述のDHCPサーバの指定で細かな指定がない場合 filename "pxelinux.0" はこのパスになる。

/srv/tftp/pxelinux.cfg/

MACアドレスIPアドレスで設定ファイルを探しに来る場所。該当するMACアドレスIPアドレスのものがない場合は default のファイルが読みだされる。

いずれのファイルも見つからない場合、ネットワークブートは失敗に終わる。

DHCPサーバの設定

DHCPサーバの設定は、 /etc/dhcp/dhcpd.conf でこちらはネットワークに合わせて少し設定が必要になる。

この辺りは単にDHCPの設定になるし、DHCPは家庭LANでもよく使われているので改めて説明の必要もないかもしれない。

allow bootpallow booting あと、 nameserver の設定、PXEのイメージを配るときの名前 filename あたりの設定と、ネットワークのサブネット周りをちゃんと見ておく。

DebianWikiにも設定例があるけど、細かいところはネットワークによって違うので何とも言い難い。

サーバの再起動

設定ができたら、DHCPとTFTPを再起動しておく。最近はsystemdでやる。

動作確認

多分これだけで動く。適当に動作確認して問題なければこのまま使えばいいと思う。終わり。

老エンジニアだけど dotfiles を更新したい、だけど慣れた設定書き換えるのって怖くない?辛い...って思ってサボってきた更新をなんとかした話

概略

  • 最小のSandbox環境を作る。
    • イマドキ風にやるならdockerとかVMとか。
    • とりあえずDebian系で動けばいいので、debootstrap + chrootで動かす。
  • /home/$USERgit init してコミット
    • どうせ Sandbox だし乱暴する
  • dotfiles をいい感じに書き換えてセットアップ
    • どうせ Sandbox だし乱暴する
  • 期待通りの挙動をしない場合、gitの機能で元に戻し、うまくいったらコミット
    • git clean -df でだいたいうまくいく

以下詳細をまとめます。

debootstrap + chrootサンドボックス環境を用意する

前はvagrantで律儀にsnapshotをとって戻して差分を確認していたが、もうちょっと雑にやる方法はないものかと chroot 環境を用意。

DebootstrapはDebianWikiに細かい説明がある。

stable Debianの最小構成をインストールする

sudo apt-get install debootstrap chroot
mkdir -p /tmp/workdir
cd /tmp
sudo debootstrap stable workdir/
sudo chown -R $USER. workdir

これで /tmp/workdirdebianがインストールされる。

chroot する

chrootするには特権が必要なので少し心が傷むが自分のマシンだからまあいいかと割り切ってsudo chrootをキメる。

sudo chroot workdir/ /bin/bash
cd /root

これで root シェルが取れる。

Debianの基本セットアップをする

最小構成のDebianでは色々とパッケージが足りなくて辛い気持ちになる。 apt update すらできないからね。

あと、 proc とか /dev/pts とかもないね。まあこのあたりはどこまでそれっぽくするかによるし、いらないセットアップな気もする。 /tmp が必要なら mkdir /tmp とでもしておけばいいと思う。 (律儀にtmpfsmountしなくてもいいよね)

echo "deb http://ftp.jp.debian.org/debian/ jessie main contrib non-free
deb http://ftp.jp.debian.org/debian/ jessie-updates main contrib
deb-src http://ftp.jp.debian.org/debian/ jessie main contrib non-free
deb-src http://ftp.jp.debian.org/debian/ jessie-updates main contrib" > /etc/apt/sources.list
apt update

apt update したいので、sources.list を雑に用意する。(apt-getじゃなくなって久しい)。

あとは適当に必要なパッケージ、zshとかtmuxとか好きに入れる。 このあとで使うので git も入れておく。 git って以外と依存パッケージが多いねって気持ちになるがまあこんなものかもしれない。

apt install zsh tmux curl git

apt の幾つかで警告が出ることがあるが、心を痛めつつ無視して先に進める。(サンドボックスは乱暴する)

git管理を始める

ロックに $HOMEgit init をキメて全ファイルをコミットする。 dotfiles用のリポジトリが用意してあればそれをcloneしておきそれもコミットする。

cd $HOME
git init
git clone https://github.com/..../dotfiles
git add .
git commit -m 'Commit all home files'

これで $HOME 以下のファイルはすべて追跡できるようになった。

dotfilesに乱暴をする

大抵の変更は git commit の時点まで戻せるので、乱暴に自分の dotfiles を書き換えてchroot環境でセットアップする。

変更が気に入らなかった場合、 git clean を使いすべての変更を捨てる。怖い場合は dry-run もある。

git clean -df
# git clean -dfn (DRY RUN)

変更がうまくいったら、適当にコミットする。このあたりはコミット粒度とかプログラミングスタイルが試される。各自いい感じにしてほしい。

コミットは chroot 環境の外からキメるのが楽。

git commit

終わりに

基本なまけものなのでこういうのは気が向いたときにしかやらない。

終わったらSandboxなので適当に捨てるといいけど、丁寧に proc とかを mount していると rm -rf したときに寒い感じがする。

技術書展2 に出ます

ところでまったく話は変わりますが、2017/4/9に行われる技術書オンリーイベント「技術書典2」に 「mochi mochi laboratory」さんと合同で参加します。 (TechBooster / 達人出版会 さん主催)

「mochi mochi laboratory」と「ゆきいろパラソル」の合同サークル 「もちもちパラソル」 です。

情報は ゆいしろさんの特設ページと、こっちでも少し情報を流していきます。

もちもちパラソル 技術書典2 特設ページ

技術書典2

techbookfest.org

進展がありましたら更新します。

最近のこと

デレステのシンデレラキャラバンの回収は期限付き。忘れないようにしないとこうなります。つらい。

iPhoneのディスプレイ割れてる人は強制交換ペナルティ1万2000円がかかります。やったね。

冬コミ、やっぱりコミケ参加したい欲求が高いということがわかり、技術書典2に出ることにしました。夏も申し込むけど、結局同人ソフトで出ることになる…かな。漫画描けるようになりたい。

Dotenvはproductionで使わないほうがよいのではという話の続き

前の話

t-cyrill.hatenablog.jp

もう二年近く前の話になりますが、昔の書いた記事のアクセスが地味に多いので、 結局自分はどうしているのかという話を少しだけ書きます。

続きを読む

真剣にRedisを使ってみようという気持ちになったのでRedisについて知っていることを書く

年末ですね。更新がおろそかになっていたので、たまにはちゃんとした話をしたいなと思い、前々から書こうと思っていたRedisの話を書いてみました。

検証に使用したRedisは3.2ですので、新しくバージョンが変わると以下の話は変わってくるかもしれませんが今のところそのようなことはしばらくなさそうです。

以下長々とRedisを本当に使えるように設定したり調べたり検証したりした内容を書いていきます。

続きを読む

MySQL InnoDB memcached pluginを使ってみようとしてやめた話

InnoDB memcached pluginはMySQL 5.6から搭載された機能で、Memcacheプロトコルを使ってInnoDBにデータを読み書きできる便利機能です。 簡単なプロトコルなためSQLより高速に動作する点、InnoDBに記録できる点、MySQLレプリケーションが利用できる点など、 うまく使えば便利な仕組みですが、結論を先に書いてしまうと使えなかったという話をします。

使えなかった理由

MySQL memcached pluginを使ったInnoDBへのインターフェイスが使えなかった理由はクラッシュが多発するためです。 高速な動作、レプリケーション機能などはうまく動作しているのですが、 しばらく動作させていると get 操作の際に SIGABRT となりMySQLが動作停止します。

Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffcf2ffd700 (LWP 722)]
0x00007ffff67e2067 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
...

MySQL5.7.14での実行結果ですが、5.6.32, 5.7.15, 8.0 でも同じように停止します。 アサーションに引っかかることによる停止なのですが、InnoDBからデータを取得する際に意図しない形でデータが取得されていて停止するようです。

MySQL 8.0 ではフォーラムで報告されていた memcached plugin 周りのいくつかの修正が取り込まれており、コードもそれなりに変更されているのですが、この問題はまだ修正されていないようです。 ネット検索では同じように停止する報告を見つけられなかったので本格的に使おうと思った人がいないのでは?という不安もあり、あまりにも簡単に停止するので検証を断念しました。

チューニングパラメータ

使えないと書いた上で紹介するのも変な話ですが、いくつかのパラメータを調整するとInnoDB memcached pluginを高速に動作させることができます。

https://dev.mysql.com/doc/refman/5.6/ja/innodb-memcached-tuning.html

単にmemcachedの代替として使用する場合、innodb_doublewrite=0innodb_flush_log_at_trx_commit=2 のように設定すると高速に動作します。

トランザクションオーバーヘッドの削減 パラメータ daemon_memcached_r_batch_sizedaemon_memcached_w_batch_size は共に大きな値を設定すると、 ロックが多発しパフォーマンスが大幅に低下してしまうので、エラーログにロックの記載がある場合はどちらも1に設定したほうがいい性能が出ることもあります。

余談

これだけ簡単に停止させられるのであれば、Amazon RDSのMemcacheプラグインも停止するのでは?と思い同じ条件で試してみましたがRDSは停止しませんでした。 うまく調整されているのか謎です。

ISUCON6予選問題のPHP実装を担当しました

こんにちはの方もはじめましての方もエントリーを読んでいただきありがとうございます。

昨日から開催されている ISUCON6 の予選にてPHP実装のコーディングを担当させていただきました t-cyrill と申します。 日頃はWeb屋さんでインフラのお仕事をやっています。

今回はいつものような適当なエントリーではなく少ししっかりとしたエントリーで担当しましたPHP実装について、いくつか簡単な補足をさせていただきます。

はじめに

ISUCON6予選参加者のみなさま、お疲れさまでした。 たくさんのチームの方にPHP実装を触っていただけたようでありがとうございます。

ISUCONということで多くの人に触れていただくコードになるだろうと思い、どのチームの方にも得意不得意の影響があまりないようになるべくよく知られたライブラリを使ったり、元からPHPがうまく動作するような環境を用意できるように準備したつもりです。

いくつか至らない点もあったと思いますが、もし困るところがあまりなかったようでしたら幸いです。

実装に使用したライブラリなど

composer.lockを見ていただればわかることですが、今回はなるべく初期のPerl実装に近くなるようにライブラリを選びました。

軽量フレームワークSlim3、HTTPクライアントの Guzzle6、テンプレートエンジンの Twig などPHPな方にはよく見るようなライブラリを元にして作っていきました。

Twigを選択したのは元になったPerlテンプレート内でテンプレート継承を使用していたためですが、今思えば特に必要ではなかったような気もします。

独自実装を使ったところなど

Perl実装から移す際にPHPで用意できなかったrandom_stringなど一部の関数は元の実装コードとそれほど違和感がないように独自に実装しました。 これらの関数は glue.php にまとまっています。

本質的なところではないのですが、 random_string の実装はかなり雑で、Perl実装のように柔軟なランダム文字列を生成することはできません。

pcreの制限について

webapp/Isuda/app.php の L49 に怪しげな // NOTE: avoid pcre limitation "regular expression is too large at offset" というコメントを見かけたPHPの方は嫌な気持ちになったかもしれません。

あまりこのケースに当てはまることがないので意外と知られていないかもしれませんが、PHPpreg系関数が内部で使っている正規表現ライブラリpcreにはデフォルトで渡せる正規表現パターン文字列の長さに制限があります。この制限を変更するには pcre ビルド時にオプションを指定する必要があります。

今回初期実装の通りに実装すると正規表現文字数が pcre の制限にかかります。Perl実装から移すときにここはかなり悩みました。

というのも、正規表現を使わない場合PHP実装がやや有利になってしまいますし、pcreを使うためには独自にpcreを用意する必要があり、初期ビルド以外のPHPを用意したチームが本質的ではないところでトラブルに遭遇するのは避けたかったのです。

結局、間を取ってハッシュの生成には preg_replace_callback を分割した正規表現で複数回呼び出して生成し、本来の置換処理はループ後の strtr にてまとめて行うように実装しました。

余談ですが、Perl実装の動作は正規表現のevalでPHPでは廃止された機能です。

Slim Containerの拡張

細かなところですが各エンドポイントで $this->dbh$this->htmlify のような呼び出しを実現するため、$app コンテナを継承した独自のSlim Containerを用意しています。

PHP7で入った無名クラスの機能をこのような形で使えたのは少し面白かったです。

stash

Perlではテンプレートへの変数渡しにstashを使っており、これを模倣するためコンテナのPimpleを直接突っ込んでおきました。

Slimの依存に含まれていたのでいい感じの入れ物として使いましたがこういう使い方は普段しないのであまりよくないような気もしています。

実装ミスについて

途中で気付かれたチームから報告がありましたが、初期のPHP実装にはベンチマーク結果に影響のない2つのわかりやすい実装ミスがあります。

一つは nginx.php.conf での mime.types ロード忘れでブラウザで閲覧した際に多くのブラウザでスタイルが適用されないなど見た目の問題が発生します。

+    include       mime.types;
+    default_type  application/octet-stream;

もう一つは POST keyword/keyword を呼び出すと必ず500エラーが発生するというもので、ミドルウェアの指定個所に間違いがあります。 また、同エンドポイントはkeywordのパース方法も間違っています。

- $keyword = $req->getParsedBody()['keyword'];
+ $keyword = $req->getAttribute('keyword');

- })->add('authenticate')->add($mw['set_name']);
+ })->add($mw['authenticate'])->add($mw['set_name']);

しかしながら、ベンチマーカーはPOST keyword/keywordが成功するのを試さないので、ベンチマークの結果に影響はありませんでした。

ベンチマーカーがPOST keyword/keywordを試していないことが公開になると他言語実装でも有利になってしまいますし、(特に1日目2日目で情報が違うことになる)

ベンチマークの結果自体には影響のないものでしたので、ルールにのっとり「ベンチマーカーの結果が全てです」という回答としました。 GoやPython実装のバグとは違いこのバグに対してパッチを提供しなかったのはこのような理由です。

2点ともすぐに気づけるようなミスなだけにPHP実装を試していただいた方には申しわけなかったです。

初期実装のサーバについて

初期実装のサーバは予選時には nginx + phpfpm の構成としました。

もともとはビルトインサーバを使用して簡単に呼び出す予定でしたが、並列処理性に問題があるためベンチの初期チェックを通過できなかったため、Ruby実装がunicornを用意したタイミングでphp-fpmを使うようにsystemd unitを置き換えました。

pmも"static"に設定されたのでPerlほどではありませんがPHPの初期スコアはそれなりに出るようになっていたと思います。

実装の最適化について

いくつかのチームから解説が出るでしょうし、PHP実装もそちらを参考にしていただければ同じように速くなりますが、初期実装でわざと遅くなりそうにした個所のみ補足します。

PHP-FPMのtcp呼び出し

初期実装で php-fpm90009001 ポートでlistenしており、nginxからプロキシする形で用意されています。 php-fpm にはunix domain socketでlistenする機能がありますので置き換えることでパフォーマンスがわずかに改善します。

最後に

本選でPHP実装を担当することはおそらくありませんが、本選でもPHP実装が上位チームに入ってくれることを期待しています。 それでは本選でお会いしましょう。