読者です 読者をやめる 読者になる 読者になる

Shin x Blog

PHPをメインにWebシステムを開発してます。Webシステム開発チームの技術サポートも行っています。

勉強会やカンファレンスにライブ配信は必要か?

ひと昔前(といっても数年前ですが)の勉強会では、Ustream を使ってセッションをライブ配信するということが一般的でした。私は、関西在住のため、特に東京で開催されているイベントをライブで視聴できるのはとても便利でありがたいものでした。

f:id:shin1x1:20160619225055j:plain

私自身がライブ配信の恩恵を受けているので、自分が開催するイベントでも可能であればライブ配信したいと考えていました*1

ある時、関西の PHP コミュニティで集まる機会があったので、ライブ配信について話していました。その場には、15人ほどいたと思うのですが、ライブ配信が欲しいと考えいたのは私一人だけで、あとは後日動画を公開するならライブ配信は無くて良い、あっても見ないという人ばかりでした。正直、ここまで必要とされていないというのは意外でした。

このエントリでは、ユーザコミュニティ主催の勉強会やカンファレンスにおけるライブ配信について考えてみたいと思います。

ライブ配信が欲しい理由

私がライブ配信があって嬉しいのは、下記のような理由です。

  • 遠隔地や都合で参加できない勉強会のセッションを見ることができる。
  • ライブで参加することで、その雰囲気を(一部でも)味わえる。
  • Twitter でハッシュタグで tweet することでよりライブ感を味わえる。
  • 場合によっては、Twitter でのやりとりが発生して、より雰囲気を感じられる。

実際のところ、「気持ち」の部分を除外すれば、後日の動画配信でも問題ありません。勉強会のセッションを今見ないと明日困るとかほぼ無いですし。ただ、この「参加している」感を味わいたいというのが大きいですね。

これは、スポーツ観戦に似ていると思っていて、プロ野球や MotoGP 観戦が好きな自分にはライブ配信(生中継)というのは大きな価値を感じます。一方、「スポーツニュースで結果だけ見ればいいやん」という人には価値を感じないのかもしれません。*2

運営としてのライブ配信

参加者としてのライブ配信は上記のとおり欲しいのですが、運営としてもライブ配信のメリットはあります。

  • 現地に参加していない人にもイベントをアピールできる
  • スポンサーやスピーカーの露出をより高められる。イベントへの参加メリットを増やす。
  • 以前のUstream であれば、ライブ配信と録画、アーカイブ配信が一気に出来た。*3
  • 参加者として享受しているライブ配信のお返し(これは個人的)

おそらく、ライブ配信自体を否定する場面は少ない*4と思うのですが、実際にやるとなると、考えないといけないことがあります。ぱっと思いつくだけで下記のようなものがあります。

  • 運営負荷が上がる(機材調達、設置、撮影、配信、撤収などなど)
  • "ライブ" なので、特に配信担当者の負荷が高い(心理的にも)
  • 配信回線の帯域確保
  • スピーカーへ配信可否の確認が必要
  • スピーカーは配信されることを前提に話すこと必要がある(後日公開なら後で公開、非公開を選べる)

これまでは、自分のように必要としている人がいると思っていましたが、冒頭の結果を受けて、あらためてその必要性を見直そうと考えました。

Twitterでの反応

ライブ配信が必要かどうか、Twitterで聞いてみました。結果は下記です。

「ライブ配信欲しい派」と「後日公開されれば不要派」が、おおよそ半々という結果でした。この結果を見ると、ライブ配信が欲しい人がまだまだいるのだなと感じました。

関西 PHP 勉強会

ちょうど、関西 PHP 勉強会があったので、その会場でも聞いてみました。ざっくり、15 人くらいの方がいたので、上記 tweet の設問を聞いてみると、ライブ配信が必要な人は、たったの 1 人でした*5。冒頭の数と合わせると、おおよそ 1:30 となり、ライブ配信はほぼ必要とされていない状況でした。

ライブ配信をやるべきか

ライブ配信をやるべきかどうか。運営側としては悩ましいところです。

正直なところ、これまで関わったイベントでは、いわゆる配信職人と呼ばれる人*6におんぶに抱っこの状況でした。アウトソースする手もありますが、それなりに費用もかかります。

運営側としてはメリットは感じつつも、その効果が薄く、負荷とのバランスが取れないのであれば、やらないと判断するのも自然なことです。

勉強会自体が増えて気軽に参加できるようなった(個々のイベントの希少価値は相対的に下がった?)、ブームが落ち着いてただ参加したいという人が減った、ライブ配信を閲覧していたがその価値を感じなくなったなど、ライブ配信が必要とされない理由は考えられますが、勉強会を取り巻く状況が変わってきているのは確かなので、これまで当たり前にやっていたことを見直す時期に来ているのかもしれません。

それにしても、今回驚いたのが、Twitter での反応と関西 PHP コミュニティでの反応に大きな乖離があった点です。後者では、自分以外ライブ配信に必要性を感じないという結果だったのですが、これは、「関西」だからなのか、「PHP」だからなのか、「関西 PHP」だからなのか。謎は残りますが、少なくとも「関西 PHP」でやるイベントについては頭に入れておかないといけないことですね。

ちなみに、関西 PHP 勉強会では、セッション中の tweet も少なめなので、そのあたりも関係しているのかもしれません。

さいごに

勉強会を Ust で配信して、オンラインで参加して、Twitter で絡む。一時は当たり前のように繰り広げられていた風景です。それがよくある勉強会だと考えていましたが、そうではなくなってきたのは少し寂しい気もします。

見方を変えると、勉強会がより身近になって「ライブ配信」よりも「ライブ」を重視する人が増えたとも言えます。

人によって色々な意見があると思うので、ぜひあなたの意見も教えて下さい。

Twitter で頂いた意見

Twitter でアンケートを行った際に教えてもらった意見です。どれも一理あって、なるほどと思いますね。

*1:とは言っても、実際は大きなイベントでないと実施できてないのが実情ですが

*2:ライブ配信不要派の人がそういう考えかは分かりませんけど;-p

*3:今もできますが、無料プランでは、アーカイブは一定期間経過すると削除されます

*4:クローズドなイベントやスピーカーが配信を望まない場合は別

*5:私です

*6:@suzuki さんとか、関西だと @spice_o さんとか

PHP + PostgreSQL カーソルを使ったデータ取得の計測

PHP(pdo_pgsql)から PostgreSQL に SELECT 文でデータを取得する際に、カーソルを使った場合と使わなかった場合で、どのようにリソース消費(主にメモリと実行時間)の違いがあるのかを計測してみました。

検証内容

PostgreSQL テーブルに、1,000,000 件のレコードを持つテーブルを用意し、下記のようなパターンにて、PHP から全レコードを取得して、実行時間と消費メモリを計測します。カーソル利用時は、1度のフェッチで取得する件数によって計測しています。ここでは、前方向のカーソルのみ扱っています。

  • 1) カーソル未使用 + 1 件づつフェッチ
  • 2) カーソル利用 + 1 件づつフェッチ
  • 3) カーソル利用 + 100 件づつフェッチ
  • 4) カーソル利用 + 10,000 件づつフェッチ
  • 5) カーソル利用 + 100,000 件づつフェッチ
  • 6) カーソル利用 + 1,00,000 件づつフェッチ

参考として、カーソルを使用せずに全件フェッチ(fetchAll)するパターンも計測しています。

  • 7) カーソル未使用 + 全件フェッチ

検証は、VirtualBox 上の CentOS 7(Mem: 2.0GB) に PHP 7.0.6 + PostgreSQL 9.5 をインストールして行いました。

環境構築

下記の SQL で、テーブル構築とレコードの追加(1,000,000件)を行いました。generate_series関数は、こういうダミーデータ作る時に便利ですね。

CREATE TABLE users(
  id int primary key,
  number int,
  created_at timestamp
);

INSERT INTO users(id, number, created_at) SELECT i,(random() * 1000)::int % 1000, CURRENT_TIMESTAMP - CAST((random() * 1000)::int || ' day' AS interval) FROM generate_series(1, 1000000) s (i);

VACUUM;

レコードが格納されているかを確認しておきます。users テーブルには、1,000,000 件のレコードがあり、テーブルサイズは 42MB であることが分かります。

bench=# SELECT relname, to_char(reltuples, '9,999,999') as reltuples, (relpages * 8 / 1024) as kbytes FROM pg_class WHERE relname='users';
 relname | reltuples  | MB
---------+------------+--------
 users   |  1,000,000 |    42
(1 行)

bench=# SELECT COUNT(*) FROM users;
  count
----------
 1000000
(1 行)

PDO で全件を取得

PDO を使って、レコードを取得します。全体の実行コードは下記です。

https://gist.github.com/shin1x1/27db7768218ab3b4557a3dce4f639faa#file-pdo_cursor-php

Benchmark クラスは、実行時間とメモリ利用量(RSS)を出力するクラスです。ここでは、メモリ消費量を RSS 値と memory_get_usage関数で取得しています。

PHP のベンチマークでは、memory_get_usage 関数が使われますが、これは、PHP(Zend Engine) で割り当てられたメモリのみが計測対象なので、今回のように拡張(pdo_pgsql)や拡張が利用しているライブラリ(libpq)が、独自に確保したメモリは加算されません。そこで、比較のために両方を取得しています。

UserRepository クラスが、PDO を使って、PostgreSQL にアクセスするクラスです。下記のポイントで Benchmark クラスでログを出力しています。

  • SELECT 文(DECLARE CURSOR 文)実行前
  • SELECT 文(DECLARE CURSOR文)実行後
  • フェッチ中(100,000件毎)

実行結果

上記のコードを実行すると、下記のような出力が得られました。

https://gist.github.com/shin1x1/27db7768218ab3b4557a3dce4f639faa#file-results

実行結果をそれぞれまとめてみました。

メモリ消費量

メモリ消費量についてです。

まず、memory_get_usage 関数については、全てのケースでほとんど違いがありませんでした。これは、1-6 の計測では、libpq と PostgreSQL 間でのやりとりにバリエーションはありますが、PHP と libpq の間は違いが無いため(この間では、1 件づつフェッチしている)ためです。以下は、RSS 値についてです。

カーソル未使用では、SQL 文発行時に結果セットを libpq が、一気に PostgreSQL から受けとるので、利用メモリが一気に上がっています。フェッチ中は、libpq から取得するだけなので変化がありません。一方、カーソルでは、SQL 発行時(DECLARE CURSOR文)は、消費量に変化がありません。あとは、フェッチするレコード数に応じてメモリが消費されています。特に、6 は、全件をフェッチしているので、1 と同程度に消費しています。

方式 SQL 実行前 SQL 実行後 フェッチ完了
1) カーソル未使用 13,088 kb 108,708 kb 108,824 kb
2) カーソル + 1 件フェッチ 13,500 kb 13,500 kb 13,512 kb
3) カーソル + 100 件フェッチ 13,512 kb 13,512 kb 13,512 kb
4) カーソル + 10,000 件フェッチ 13,512 kb 13,512 kb 14,356 kb
5) カーソル + 100,000 件フェッチ 14,356 kb 14,356 kb 22,944 kb
6) カーソル + 1,000,000 件フェッチ 22,944 kb 22,944 kb 108,848 kb

上記表をグラフにしてみました。1 と 6 では、タイミングは異なりますが、全レコード分のメモリを消費しています。一方、2、3については、ほぼ変化はありません。4 は 800k、5 は 8m 増加している程度です。

f:id:shin1x1:20160614182705p:plain

実行時間

実行時間についてです。

カーソル未使用では、SQL実行完了(結果セット受け取るまで)に時間がかかっていますが、フェッチ完了までは速いです。カーソルを使うと、SQL 実行は速いですが、あとはフェッチする件数に依存しており、取得するレコード数が少なければ、その分、フェッチ回数が増えるので遅くなっています。

方式 SQL 実行前 SQL 実行後 フェッチ完了
1) カーソル未使用 - 857 ms 1,236 ms
2) カーソル + 1 件フェッチ - 152 ms 17,680 ms
3) カーソル + 100 件フェッチ - 148 ms 1,426 ms
4) カーソル + 10,000 件フェッチ - 148 ms 1,260 ms
5) カーソル + 100,000 件フェッチ - 152 ms 1,344 ms
6) カーソル + 1,000,000 件フェッチ - 153 ms 1,338 ms

上記表をグラフにしてみました。全体の実行時間を見ると、2 だけ実行時間が大きくかかっています。フェッチがレコード数分実行されるので、当然ながら時間がかかります。ここでは、PHP と PostgreSQL が同ホストでしたが、別ホストであれば通信のオーバヘッドもあるので、より遅くなります。それ以外については、大きな違いはありませんでした。

f:id:shin1x1:20160614182714p:plain

fetchAllの場合

PHP と libpq 間のやりとりについて見るために、カーソルなしで fetchAll した場合も計測してみました。結果を見ると、fetchAll 完了後に一気に RSS 値も memory_get_usage 関数の値も上昇しています。特に、memory_get_usage の上昇が顕著です。これは、libpq が持つ結果セットを PHP の値(1,000,000件の配列)として格納したためです。

方式 SQL 実行前 SQL 実行後 fetchAll完了後 ループ完了
RSS 13,516 kb 108,852 kb 563,512 kb 563,832 kb
memory_get_usage 364 kb 455,016 kb 455,016 kb 455,016 kb
実行時間 0 ms 839 ms 1,179 ms 1,405 ms

[おまけ] PDO::ATTR_CURSOR = PDO::CURSOR_SCROLL

pdo_pgsql のコードを見てみると、上記オプションを指定すると、自動でカーソルが使われるようになります。

https://github.com/php/php-src/blob/PHP-7.0.6/ext/pdo_pgsql/pgsql_statement.c#L153 https://github.com/php/php-src/blob/PHP-7.0.6/ext/pdo_pgsql/pgsql_statement.c#L423

おお、これは便利!と思って、試してみたのですが、1 件づつのフェッチなので、件数が多いと重くなるのと、自分で FETCH 文を発行する場合とことなり、メモリ消費量が異常に上がるので、おそらくメモリリークしているように見えます。

このオプションは、カーソルでポインタを自由に移動するためのものなので、前方向のみに利用するには適さないかもしれません。

さいごに

それぞれの方式による実行時間とメモリ消費量を見てみました。

カーソルを利用することで、PHP プロセスのメモリ消費を抑えてフェッチすることができます。しかし、ただカーソルを使えば良いかと言うとそうではなく、カーソルの特性を理解して、フェッチするレコード数を調整する必要があります。ここでは、実行時間とメモリ消費量のバランスで考えると、4 が妥当でしょう。もちろん、どのパターンが良いかはケースバイケースです。

カーソルを利用しない場合は、全件の結果セットを取得しますが、libpq が保持している状態です。ここで、fetch / fetchAll メソッドを呼ぶことで、PHP の値として利用できるようになります。

fetchAll の例で分かるように、PHP の値に格納するのはメモリ消費が大きいので、大量のデータを読み込む場合は、全レコードを一気に配列に格納するのではなく、fetch メソッドで逐次取得して処理するのが良いですね。

バッチ処理などで大量のデータを扱う際は、カーソルを上手く使ってみると良いでしょう。

参考

「制約でコードに秩序を」を PHP カンファレンス福岡 2016 で発表してきました #phpconfuk

PHP カンファレンス福岡 2016 で、「制約でコードに秩序を」という発表を行ってきました。

発表資料

サンプルコードは、下記です。

github.com

セッションでは、資料は最低限のものにして、PhpStorm によるライブコーディングをしながら解説するというスタイルで進めました。

抽象的な話より、具体的にコードを目にする方が伝わりやすいであろうという事と、私自身もコードを書きながら話す方がやりやすいので、このスタイルにしました。

公開している資料は、コードと解説、さらに省いた内容などを盛り込んだものです。

初心者〜中級者向けセッションということで、PHPコードはとりあえず書けるようになって、これからより本格的な開発に入る方や、こうした制約を活用していない方などに参考にして貰えれば嬉しいです。

http://togetter.com/li/979354

発表雑感

今回は、上記の発表資料の構成(資料はもうちょっと簡素ですが)でいこうと考えていたのですが、かなり駆け足でやっても時間オーバーしそうだったので、バッサリとそぎ落としました。おかげで当日は時間に余裕ができたので、落ち着いて進めることができました。

どうしても、作った本人は、あれもこれも入れたいとなってしまいますが、時間枠は決まっているので、やはり削ぎ落とす工程が必要ですね。

雑感

発表は、朝一セッションということで、寝坊できないプレッシャーはありましたが、会場についてしまえば後は楽でした。

最初のセッションはまだ会場の空気も出来上がってなくて、新雪に足を踏み入れるように自分の間でできるのでやりやすかったです。(そういえば、一昨年の PHP カンファレンス(東京)もその会場では最初のセッションで良かった記憶が。)

何より最初に終わるので、その後が気楽というのがいいですね。セッションを終えた充実感はありつつ、気軽に過ごせるので、イベントを楽しむことができました。昼食の時も一緒に行ったメンバーは自分以外午後から発表がある人ばかりだったので、一人テンション高めでした :)

そのおかげか、今回は多くの方と話すことができました。特に、懇親会会場の入り口付近に座り込んでの 8bit 談義は良かったですね。もう高校生の自分がいたら最高にエキサイティングな場だったと思います。それ以外にも、色々な方とお話できて楽しかったです。欲を言えば、福岡の地元の方ともっとお話できれば良かったですね(もともと交流のある人以外の人とも)。

さいごに

PHP カンファレンス福岡の運営のみなさん、ありがとうございました!今年も楽しいイベントでした。さあ、次は関西だ。

今回のカンファレンスについて素敵なエントリがあったので、こちらでもメモ。

hb.matsumoto-r.jp

ここを気をつけるともっと良くなる勉強会の発表

勉強会や技術系のイベントで色々な発表を聞いていると、せっかく勇気を出して壇上に立ったのに、ちょっとしたことでもったいなあと感じることがあります。これは特に、発表に慣れてない人に多く感じます。

f:id:shin1x1:20160414101541j:plain

本エントリでは、大いなる自戒を込めて、発表慣れていない人へこの辺を気をつけるともっと良くなるよ、というポイントを挙げてみます。

なお、下記のポイントは、発表慣れている人や意図してやっている人はスルーして下さい :)

1. 自己紹介が長い

LT の時に感じるのですが、発表時間が 5 分しかないのに、自己紹介に時間をかけるケースがあります。自分が何者か伝えるのは大切ですが、自己紹介より発表内容が大事です。また、聞いている側からすると数多くの発表者の中の一人なので、よほどインパクトがある自己紹介でないと、あまり覚えていなかったりします。(それより内容の方が記憶に残りやすい。)

やはり内容が大事で、そこが聞きたいところなので、自己紹介はさらっと言って、本題に入るようにしましょう。

自己紹介でアイスブレイクをやる場合もあると思いますが、それは慣れてからで。

2. 言い訳や自己卑下を入れる

発表の冒頭に「さっき資料作りました」とか「昨日徹夜で作ってきました」などのエクスキューズを入れる場面があります。

忙しい最中、時間を割いて作ってきたのは分かるのですが、聞く側にとっては関係無い話です。発表の枕としてこうしたエピソードを軽く入れる方法もありますが、発表がダメな場合の予防線を張っているようにも感じられます。

他にも、話す前から「この発表は面白くないですよ」と言ったり、自分が作ったものを紹介する時に「これはつまらないものです」と言うなどもあります。謙遜もあると思うのですが、話し始めで、いきなりネガティブなことを言われてると「別にそんなこと言わなくていいのに」と感じてしまいます。

せっかく話す場に立っているのですから、堂々と自信を持って話しましょう。その場では、みんながあなたの話を聞いているのです。

3. 技術と関係の無いネタを入れる

発表内容とは関係の無いネタ(例えば、アニメやマンガ、ゲームなど)を散りばめたり、そこを押してこられると、元ネタを知らないとさっぱり分かりません。発表内容をより伝えるアクセントとして使うのは良いですが、コンテキストが共有できていないものはなかなか伝わりづらいですね。

おそらく、これは過去にうまくやっている人を見て、取り込んでいる人が多いと思うのですが、こういうのを効果的に使うのは案外難しいですし、入れる必要も無いので、あえて入れなくて良いでしょう。

4. 無理に笑いを取ろうとする

うまいユーモアを交えて場を盛り上げる発表は楽しいものです。発表ネタを練りに練って、話の展開や技術ネタで笑わせられるのは、すごいなあと感心させられます。

ただ、発表を聞いている見ず知らずの人に笑ってもらうのはなかなか難しいです。さらに、3. で言及したように、笑わせようとして内容と関係の無いネタを持ってきても、やはり上手くいきません。(知らないと笑えない)

私の経験だと、笑わせようとネタを仕込んで、全く受けないと発表者自身にダメージが来ます><

技術系の勉強会は、やはり技術やそれに纏わる話が本道です。そこに笑いあればもちろん良いですが、必須なわけではありません。その場で盛り上がったけど、後で Twitter や blog を見ると反応もまばら、方や淡々と進めた発表の方がちゃんと伝わっていて好評だったということもあります。

笑いのことは一切気にせずに、しっかり発表内容にフォーカスしましょう。

5. 何を伝えたいか分かりにくい

発表を聞いても「色々話していたけど、何を伝えたかったんだろ?」と感じてしまう場合があります。

これには発表で聞いている人に伝えたいことを決めておくと良いです。あまり欲張らず、5 分 LT なら、1 つで十分です。

ベタですが、最後のスライドにこのメッセージを書いておいて、それで最後を締めると分かりやすいです。

メッセージをゴールとして、動機、背景、検証、結果、考察などを組み立てていくと発表に流れができて構築しやすくなります。こうした流れがあると発表する際も流れに沿って進めれば良いですし、聞く側も理解しやすくなります。

さいごに

発表を聞いていて、ここを直したら、もっと良いのになあと感じたポイントを挙げてみました。

これらのポイントは、全て私自身もやらかしてきたことばかりです。直そうと思ってやらなくなったものもあれば、未だにやってしまうものもあります。上手い人の発表を見ると、自分もあういう風にしたいと思うでしょう。しかし、表面的に真似ても上手くいかないことが多いですし、うまくやるにはそれなりの準備や経験が必要になります。

第一歩目としては「伝えたいことを明確にして、それをしっかり伝える」ということだけに集中するのが良いですね。

CircleCI + Docker で PHP 7 と PhantomJS 使って CI する

CircleCI で、とあるプロジェクトの CI 環境を作りました。このプロジェクトは、PHP 7 で開発しているのですが、まだ CircleCI 公式では PHP 7 がサポートされていません。

f:id:shin1x1:20160216044318p:plain

そこで、Docker を使って、PHP 7 + PhantomJS 環境を構築しました。

構成

PHPUnit と Codeception のテストを実行する環境を構築します。 コンテナの構成は、下記のようになります。

  • PHP 7 コンテナ
  • PhantomJS コンテナ
  • PostgreSQL コンテナ * 2

これらのコンテナは、docker-compose でまとめて構築、実行します。

PHP 7 コンテナ

PHP 7 + Apache のコンテナです。Docker Hub オフィシャルの php:7.0-apache ベースにして、Laravel 5.1 実行に必要な拡張の追加や設定を行ったイメージ shin1x1/docker-laravel-on-php7-apache を構築しました。

https://github.com/shin1x1/docker-laravel-on-php7-apache

イメージは、Docker Hub で公開しているので、イメージ shin1x1/laravel-on-php7-apache で実行できます。

https://hub.docker.com/r/shin1x1/laravel-on-php7-apache/

$ docker run shin1x1/laravel-on-php7-apache php -v
PHP 7.0.3 (cli) (built: Feb  5 2016 18:24:39) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

PostgreSQL コンテナ

DB に PostgreSQL を利用しているのでこちらもコンテナを用意しました。こちらはオフィシャルの postgres:9.5 をそのまま利用しています。このイメージは環境変数で幾つかのパラメータを指定できるので必要なものを設定します。

  • POSTGRES_USER - PostgreSQLユーザ
  • POSTGRES_PASSWORD - PostgreSQLユーザのパスワード
  • POSTGRES_DB - データベース名

アプリケーション動作用とユニットテスト用で二つのコンテナを構築します。

PhantomJS コンテナ

edvakf/phantomjs-japanese イメージを利用します。

docker-compose.yml

コンテナを構築・実行する docker-composer.yml は、下記です。

web が、PHP を動作させるコンテナです。アプリケーションコードはホストのものをマウントします。カレントディレクトリを /var/www/html にマウントしている箇所がそれです。

phantomjs が、PhantomJS のコンテナです。edvakf/phantomjs-japanese イメージを利用しており、4444/tcp をホスト側にマッピングしています。アクセプタンステストで web コンテナにアクセスするので links で指定しています。

db_appdb_app_test が、データベースのコンテナです。上述の通り、PostgreSQL のイメージを利用します。POSTGRES_USER が、vagrant なのは、開発環境でユーザ名をこれにしているのでそれに合わせています。それぞれのコンテナは、web コンテナから参照できるように links で指定しています。

web:
  image: shin1x1/laravel-on-php7-apache
  ports:
    - "8000:80"
  links:
    - db_app
    - db_app_test
  volumes:
    - .:/var/www/html
  environment:
    DB_HOST: db_app

phantomjs:
    image: edvakf/phantomjs-japanese
    command: --webdriver=0.0.0.0:4444
    ports:
      - "4444:4444"
    links:
      - web

db_app:
  image: postgres:9.5
  environment:
    POSTGRES_USER: vagrant
    POSTGRES_PASSWORD: pass
    POSTGRES_DB: app
    LC_ALL: C

db_app_test:
  image: postgres:9.5
  environment:
    POSTGRES_USER: vagrant
    POSTGRES_PASSWORD: pass
    POSTGRES_DB: app_test
    LC_ALL: C

circle.yml

circle.yml は、下記のようにしています。

dependencies では、docker-compose コマンドでコンテナを実行します。ただ、composer install は、キャッシュを効かすために CircleCI 上(ホスト)で実行しています。

test では、pre でアプリケーションのセットアップを行い、override で PHPUnit と Codeception でテストを実行しています。

machine:
  php:
    version: 5.6.14
  services:
    - docker

dependencies:
  override:
    - composer install --prefer-source --no-interaction --dev
    - docker-compose up -d

test:
  pre:
    - cp .env.example .env
    - docker-compose run web php artisan key:generate
    - docker-compose run web php artisan migrate
    - docker-compose run web php artisan db:seed
    - docker-compose run web php artisan db:seed --class DemoSeeder
    - sudo chmod -R a+w storage

  override:
    - docker-compose run -e DB_HOST=db_app_test web ./vendor/bin/phpunit
    - docker-compose run web ./vendor/bin/codecept run -c codeception/codeception.yml

さいごに

これまで Jenkins や Travis CI を使って CI 環境を構築してきました。

CircleCI も以前試したのですが、PHP の拡張が足りないなどやや不足感があって利用していませんでした。その当時から比べると PHP のサポートも充実してきたと思うのですが、それよりも Docker サポートによってかなりの部分をカバーできるようになりました。

なんといっても CircleCI は、SSH でログインできるのが大きなメリットで、試行錯誤の段階で大いに役立ちました。やはり、直接ログインできるのは安心感ありますね。

サーバ/インフラエンジニア養成読本 DevOps編 [Infrastructure as Code を実践するノウハウが満載! ] (Software Design plus)

サーバ/インフラエンジニア養成読本 DevOps編 [Infrastructure as Code を実践するノウハウが満載! ] (Software Design plus)

「サーバ/インフラエンジニア養成読本 DevOps編」にて Ansible 2 について書きました。

2016/02/26 に出版される「サーバ/インフラエンジニア養成読本 DevOps編」というムック本にて、Ansible 2 について書きました。

f:id:shin1x1:20160209023314j:plain

本書は、昨今では普及してきた DevOps や Infrastructure as Code といったキーワードに関心がある方向けの本です。はじめに概念の定義や現状をおさえた後に、Ansible、Circle CI、Docker、Kubernetesなどツールを活用して実践する構成となっています。

Ansible 2 によるサーバ環境の構築

特集 1 である「Ansible 2 によるサーバ環境の構築」を担当しました。ここでは、Ansible の基礎をベースに、Vagrantを利用したサーバ環境の構築を解説しています。

Ansible には多くのユーザがおり、すでに多くの Playbook が公開されています。そこで、Playbook のシンタックスや基本的な構造などの基本部分に重点を起き、こうした公開された情報を活用出来る足がかりになるようにと原稿を書きました。

本稿では、Ansible で LAPP(Linux + Apache + PHP + PostgreSQL)構成のサーバを構築するのですが、実は PHP 7 で構築している(執筆時はまだ RC でした)のもポイントです。

Ansible 2 対応本

タイトルに「最速攻略」と銘打ってあるように Ansible 2 が対象になった紙ベースの書籍としては、おそらく世界初だと思います :)

書籍でも触れていますが、企画自体は昨年後半から進行していたので、当初は 1.9 をターゲットに書いていました。もう原稿を書き終えて校正を進める段階に入った頃に Ansible 2 がリリースされました。

時間的にはギリギリだったので、このまま 1.9 で行くか、2.0 に合わせるかで迷ったのですが、サンプルコードを 2.0 で検証したところ、コード自体はほぼそのままで良く、本文の影響範囲も限られていたので、思い切って 2.0 をターゲットにすることにしました。

すでに大きく手を入れる段階ではなかったのに関わらず、対応いただいた編集さんに感謝です。ありがとうございました。

目次

目次は、下記のとおりです。 DevOps や Infrastructure as Code の概念はもちろんのこと、その後の実践パートで手を動かして学べる内容になっているので、きっと現場で役立つ本になっていると思います。

巻頭企画 DevOpsとInfrastructure as Codeの概要
吉羽 龍太郎
第1章 DevOpsとは?
第2章 Infrastructure as Codeとは?

特集1 最速攻略!Ansible 2によるサーバ構築
新原 雅司
第1章 Ansibleの基本
第2章 Playbookの基本
第3章 Ansible[実践]
第4章 Playbookベストプラクティス

特集2 CircleCIによる継続的インテグレーション入門
前田 章
第1章 インフラのテスト入門
第2章 ローカル上でのテスト駆動インフラ
第3章 インフラCIの準備
第4章 CircleCIを利用した継続的インテグレーション
第5章 継続的インテグレーションから継続的デリバリーへ

特集3 Dockerによる仮想環境構築とKubernetesによるDockerクラスタ管理
馬場俊彰
第1章 Dockerの基本
第2章 Dockerを使ってみよう[基礎編]
第3章 Dockerを使ってみよう[応用編]
第4章 クラスタ管理ツールとは
第5章 Kubernetesの基本
第6章 Kubernetesクイックスタート
第7章 Kubernetesを詳しく見てみよう

さいごに

いつものながら、今回もレビュアの方々にレビューして頂きました。ご協力頂いた @hnakamur2 さん、@k1LoW さん、@syu97 さん、ありがとうございました!

Amazon ではすでに予約が始まっていますので、よろしくお願いしますm(_ _)m

最後に原稿に書いた文で締めたいと思います。

Ansibleは、Dev にも Ops にも優しい便利なツールです。
Infrastructure as Code を実現する 1 つのツールとして役立ていただけると嬉しいです。

サーバ/インフラエンジニア養成読本 DevOps編 [Infrastructure as Code を実践するノウハウが満載! ] (Software Design plus)

サーバ/インフラエンジニア養成読本 DevOps編 [Infrastructure as Code を実践するノウハウが満載! ] (Software Design plus)

Vagrant + Ansible で開発環境を作るなら ansible_local プロビジョナがいい!

Vagrant 1.8 で、ansible_local という新しいプロビジョナが追加されました。

f:id:shin1x1:20160120181043p:plain

これは、Ansible をゲスト(VM)側にインストールして、ローカルコネクションで VM 内で実行するものです。これは、まさに待ち望んでいた機能ので紹介します。

Vagrant + Ansible で気を付けること

f:id:shin1x1:20160120150104p:plain:w500

以前から、Vagrant + Ansible の組み合わせでローカルの開発環境を作るなら、ホスト側に Ansible を入れるのではなく、ゲスト(VM)側に Ansible を入れる方が良いと考えていました。勉強会などでも良く話していたのでお聞きになった方もいるかと思います :)

ホスト側に Ansible を入れない理由は、3 つあります。

まず、ホストに Ansible をインストールする手間が増える点です。VagrantVirtualbox のインストール(あとコードのチェックアウト)以外は、vagrant upのみで環境構築は完結したいところです。専用のシェルスクリプトを用意して、Ansible のインストールを含めて自動化する方法もありますが、専用のスクリプトを覚えるより、vagrant upというどのプロジェクトでも同じ手順で構築出来る方が楽でしょう。

もう一つの方が、今後は重要になります。それは、それぞれのホストで Ansible のバージョンが異なるために問題が起こる可能性がある点です。こうした環境による違いを同じ構成の VM を使うことで吸収できるのが Vagrant の大きな利点なのに、 Ansible がホストにあると、こちらで動くがあちらで動かないというケースが出てきます。Ansible 2 がリリースされたことで、今後はこの問題がより起こりやすいと思います。

最後は、Windows 環境です。Ansible は、Windows をプロビジョニングはできるのですが、Ansible 自体は実行できません。つまり開発機が Windows の場合、Ansible は VM に入れるしかありません(別の Linux 機から Windows 機をプロビジョニングすることは可能です)。

こうした問題を防ぐために、VM 側に Ansible をインストールしてプロビジョニングを行っていました。

shell プロビジョナによる Ansible プロビジョニング

これまで、VM に Ansible をインストールして ansible-playbook を実行するのは、shell プロビジョナを利用していました。

例えば、下記のように、Ansible のインストールとローカルコネクションによる Playbook の実行をスクリプトとして記述していました。

  config.vm.provision "shell", inline: <<-SHELL
    yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    yum -y install ansible
    cd /vagrant/provision; ansible-playbook -i localhost vagrant.yml
  SHELL

この動きを Vagrant の機能として実装したのが、ansible_local です。

ansible_local プロビジョナ

ansible_local プロビジョナは、Ansible のインストール(インストールされていなければ)、ansible-playbook コマンドによる Playbook の実行を行います。

上記の shell プロビジョナの例を ansible_local に書き直すと記のようになります。Playbook のパスを指定するだけなのでシンプルですね。

  config.vm.provision "ansible_local" do |ansible|
    ansible.playbook = "provision/vagrant.yml"
  end

Ansible のインストール方法は、VM の OS によって異なります。例えば、RedHat系なら yum(EPEL) で、Debian 系なら apt-get でインストールされます。

現時点では、Ansible のバージョンを指定してインストールすることはできない(RedHat系ならyum install ansible --eablerepo=epelでインストールされるバージョンのみ)ので、もしその必要があれば従来どおり shell プロビジョナを使って、pip などでインストールすることになります。

このプロビジョナには、いくつかオプションが指定できます。必須は、ansible.playbook のみです。

オプション 内容
ansible.playbook string 必須。VM 上での Playbook のパスを指定。下記、provisioning_pathからの相対パスでも、絶対パスでも可。
ansible.install boolean Ansible を自動インストールするかどうか。デフォルトは、true。
ansible.provisioning_path string ansible-playbookコマンドを実行するパス。デフォルトは、/vagrant
ansible.tmp_path string プロビジョナが利用する一時ファイルの置き場。デフォルトは、/tmp/vagrant-ansible。|ansible.version|string|想定している Ansible バージョン。デフォルトは、バージョンチェックしない。
ansible.inventory string インベントリパス。
ansible.verbose string or false ansible-playbook 実行時に詳細出力するかどうか。vの数が多ければ、より詳細出力になる。(ansible-playbook -v相当)
ansible.limit string ansible-playbook を実行するホストやグループ指定(ansible-playbook --limit相当)

ansibleプロビジョナと共通のオプションも多くあるので、詳細は公式ドキュメントもしくはコードを見て下さい。

shell プロビジョナと ansible_local プロビジョナの違い

2つのプロビジョナを実行してみて分かる大きな違いが、実行中の画面への出力です。

shell プロビジョナでは、ansible-playbook コマンドからの出力はバッファリングされて、コマンド完了後に一度に出力されていました。また、実行結果の色表示などもありません。

f:id:shin1x1:20160120144108g:plain

ansible_local プロビジョナでは、通常 ansible-playbook コマンドを実行したのと同等にタスク実行時に逐次出力が行われます。また、実行結果も色付けされています。

f:id:shin1x1:20160120144118g:plain

これだけでも、ansible_local を使うメリットがありますね。

さいごに

ansible_local は、 Vagrant + Ansible で開発環境を構築するなら決定版ともいえるプロビジョナです。

しばらく Ansible 1.x と 2.x が混在することになるので、ansible_local を使って同一バージョンの Ansible でプロビジョニングするようにしましょう。

Vagrant入門ガイド

Vagrant入門ガイド