PHPカンファレンス2012に行ってきた

PHPカンファレンス2012に行ってきたのでメモを晒します。

当日のタイムテーブルはこちら
http://phpcon.php.gr.jp/w/2012/timetable/

技評さんの写真付きのレポートはこちら
http://gihyo.jp/news/report/01/phpcon2012/0001

GREE いまだからできるふつうの話

長く続く大規模サービス基盤、の作り方にフォーカスして話す

そもそも大規模とは

ポイント

  • 最初から大きくあることがわかっているテーブルは、あらかじめ分割して別々のサーバに置く => あとから分割するのは難しい
  • 購入履歴 => 日付毎のテーブル
  • 所持カードのリスト => ユーザIDでmodとって100分割

よくやる解決法

  • スレーブ増やす
  • スケールアップ(メモリ増やす、Fusion-io使う)
  • クエリチューニング(covering index, index full scan)

実際のチューニング(1)

  • 月に数回 slow query が発生してレプリケーション遅延が発生していた
  • DBのサイズは10GB強
  • MyISAM -> InnoDB
  • メモリ 16GB -> 24GB
  • slaveを6台 -> 3台に縮小
  • 課題:サービスを止めずに alter しないといけない

オンサービスでのDBきりかえ

  • 現行 master の slave として、新セットの master/slave をつける
  • レプリケーションできる形で alter しておく
  • slave を新セットの slave におきかえる
  • master の切り替えのために、auto_increment をあげる。# id 指定で insert しておく
  • あげておかないと2重 master からのレプリケーションで slave で衝突する

結果

  • 新セットへの切り替えのみですべての問題が解決
  • クエリのチューニング計画は空振り orz

実際のチューニング(2)

  • バッチの処理が遅くなって来ている
  • レプリケーション遅延が頻発
  • 4000/sec のupdate 文が飛んでいた
  • in 句で50件ずつまとめて更新するようにした
  • 簡単な対応だけど効果がすごく大きかった
  • 大昔のコードにこういうのがあって、いきなり火を噴くことがある

<継続>

  • よりよいシステムを作る => 完璧なシステムを作る? => 間違い
  • 完璧=その時点での完璧。環境は変化する。

GREE7年間のアーキテクチャ変遷

2005

  • Ethna 利用
  • Action(Controller), View, Manager(Model)
  • DBI master/slaveを選択

2006

  • serviceを追加
  • DAO: データ変換、分割DB対応、SQL動的生成

2007

  • serviceがservice層に。釣りスタサービス
  • Generic DAO 多様な分割DB対応、SQL動的生成廃止、チューニングが必要だから

2011 激動

  • Aoi カスケード(社内フレームワーク)
  • Service と Manager 併用という状況の打破。
  • Cascade(Generic DAOの拡張) より厳密な型変換。
  • KVS ドライバ(同じインターフェースで mamcache, flare にアクセス。flare とは gree内製の KVS
  • Service -> Processor (1つの機能) -> Module (Managerに相当)
  • => DataAccessor (DAO) => Cascade
  • => Legacy Module => (Manager, Service) Adapterパターンでレガシー関数をModuleインターフェースに変えている

Ethna

  • 2005年から変わらずに存在する
  • 機能自体には目新しいものはない。認証。ルーティング。バリデーション。
  • すごく良くはないけど、とくに困らない。

DOA -> SOA

  • 2005年 DOA Data Oriented Architecture
  • 2007年 SOA Serivce Oriented Architecture
  • サービスの種類が増え、データの扱い方という単位でサービスを表現できなくなった
  • manager -> service -> Aoi/cascade に段階的に移行したから、できた。
  • 段階的(少しずつ)なので、失敗したら戻せる

安定運用

  • 監視と障害対応
  • 監視。定期的にGangliaのグラフを確認。深夜のアラートは担当者の電話が鳴る
  • pull request のレビューを重要度に合わせてエスカレーション
  • デプロイのサイクルを短く。個別は毎日

お知らせ

安全なPHPアプリ開発の鉄則 徳丸ひろし

鉄則1:php の脆弱性

  • 脆弱性報告を読もう
  • is_a メソッドに文字列を渡すと何故か autoload が呼ばれるという脆弱性

鉄則2:ajaxの脆弱性

  • ブラウザ側でエスケープ vs phpコードでエスケープ。どっちでもいいけどやろうね
  • JSON解釈をevalでやる、と怪しい。jQueryに任せるのが楽
  • X-Content-Type-Options: nosniff を入れないとIE9だと json.php/a.html がHTMLと解釈される脆弱性。IE7だとダメ。
  • IE6, 7 用には <> をユニコードエンコードして、エスケープしてあげること

JSONハイジャック

  • JSONを罠サイトからscript要素で読み出す
  • 利用者のブラウザからは、正規のcookieが送信されるので、認証済みの状態でJSONが取得できる
  • Firefox11.0だと問題なし。Android標準ブラウザでは動いてしまう。Android4.0.3では動かない。

鉄則3:競合条件(排他制御)の脆弱性対処をしよう

  • ドリランド。カード増殖
  • トレードの時に、A さんが B, C さんにトレード。select して insert (相手) して delete (自分)。これで2個に insert できていた。
  • 鳥ランドwwwでデモ

排他制御が必要

  • transaction + 行ロック (SELECT FOR UPDATE)

まとめ

徳丸本読んで勉強してね

xdebug @anatoo アシアル

xdebugの使い方ではなくて、裏側の仕組みを説明。Monacaとか作ってました。 
xdebugを調べてみたら、phpの裏側の仕組みと連携していて、へー、と思ったので紹介

xdebugとは?

  • デバッグや開発を支援してくれる
  • コードカバレッジ取得
  • 関数とレース
  • リモートでバッグ
  • プロファイル

コードカバレッジ機能

  • 実際に実行した行を調べる。テストの網羅率を調べるのに使う。
  • xdebug_start_code_coverage(); をファイル頭で呼ぶ。#めんどいね
  • PHPはどのようにコードを解釈して実行される?
  1. 字句解析
  2. 構文解析 (bison)
  3. コンパイル(Zend Engine)
  4. 実行

コンパイル zend_op_array 構造体を作る

  • zend_op 構造体
  • ZendEngine への命令を表現
    • ZEND_NOP_ZEND_ADD
    • ZEND_SUB
    • などなど

実行 (zend_execute関数)

  • zend_op 構造体を解釈し実行
  • xdebug.c 588行目あたりの初期化コードでなにかオーバーライドしている
  • ZendEngine の振る舞いを拡張ライブラリから変更できるAPI zend_set_user_opcode_handler
  • 実行時の行を保持するようにオーバーライドしている

関数トレース機能

  • zend_execute 関数をすげ変えてフックを追加
  • zend_execute は関数ポインタ。拡張ライブラリも書き換え可能。
  • xdebug_old_execute = zend_execute;
  • zend_execute = xdebug_execute;

リモートデバッグ機能

  • ブレークポイント、ステップ実行。MacGDB と PHP で通信。
  • DBGPプロトコルで通信
  • DBGPデバッガ(MacGDB)がソケットをlisten. デバッガ(php + xdebug) が接続
  • xdebugが止まっている間は命令待ち。DBGPデバッガがデバッガに命令
  • statement_handler. すべての構文の実行ごとに呼び出されるフック。xdebugはここでブレーク
  • xdebug_dbgp_breakpoint ブレークポイントの実装

まとめ

  • PHPの中にはフックできるところがたくさん
  • 「へー」と思ってもらえたらうれしい

感想

  • 「へー」と思いました!面白かった

Git x Pull Request ~ チーム開発最終奥義 @sotarok

Crocos という会社をたちあげました。http://blog.livedoor.jp/kensuu/archives/54119909.html

Git 移行について考える

  • git に移行するkとは、svn に対応するコマンドを覚えることではない
  • git を最大活用できる開発フロー、業務フローに変えていくこと

git の活用とは

  • ローカルコミット
  • 気軽にブランチを切る
  • これを生かしたブランチ戦略!

git のブランチ戦略

  • A Successful Git Branching Model ベストプラクティスのひとつ
  • Crocos ではこれをカスタマイズしたものを使っている

A Successful Git Branching Model の説明

  • master 常に、最新の安定したソースコードが手に入るブランチ。リリースブランチ
  • develop masterから分岐した日々の開発を行うブランチ
  • release リリース前の確認を行う必要があれば修正する。本番リリース前のテスト、確認作業。developはdevelopで使えるようにdevelopからブランチ切っておく。日々のプロセスと、リリースプロセスを平行にできるようにするため。
  • feature 機能追加はdevelopブランチから分岐して開発。その機能はまだリリースに追加しない、となった場合はdevelopにマージしない
  • hotfix developの完成を待たずに緊急修正。masterからhotfixブランチを切る。必要になったときに考えれば思いつくのでいいや。

ツール

  • git-flow Successful Branch に準拠したものを簡単にできます。
  • git-daily Crocosの運用ルールに則って作ったもの

git-daily

  • pearでインストールできます
  • いちいちタグつけない
  • いちいちバージョン番号つけない。Webでバージョンなんていちいちみないよね、という理論。

pull request の話

他の人の責任範囲に対して、変更が必要な場合

  1. 依頼タイプ。コミュニケーション不足で違う物が作られたり
  2. コピーして独自改造タイプ
  3. 勝手に変更タイプ(勝手にcommit)
  4. !pull requestタイプ

pull request タイプ

  • パッチを書いて、取り込んでもらうことを依頼

ブランチ戦略に pull request を組み込む

  • feature ブランチを develop に入れる際に、pull request を送る
  • pull request 毎にレビューする
  • 変更の context が明確なため、レビューが超効果的

pull request のよいところ

  • パッチのやりとりが可視化され、オープンで記録が残る
  • リファクタの機械が与えられる

システム

  • GitHub 落ちてるとリリースできなくなっちゃうよね
  • GitHub Enterprise
  • GitLab クローンだけど、バグある。。。

クロコスでの事例

  • feture ブランチから、pull request
  • 多段 feature ブランチ
  • 大きな feature ブランチの場合は、レビューのしやすい粒度で、さらに切ってレビュー

効果

  • 自分の担当以外の部分の実装・仕様の把握ができるようになる
  • お互いに、だれがどういうコードを書くのかをわかるようになる

うまくいくコツ

  • できるだけ細かくリリース
  • 小さめのチームに分割する # どういう意味かわからなかった。小さくしたら担当(チーム)以外の実装がわからなくなるとおもうけど

感想

  • 自分たちのチームのやり方とあまり変わらなかった
  • ので、違う所として git-daily にもう少しふみこんでほしかったなぁ

PHP 5.5 新機能 Generator 初心者入門

目的:generator ってなんだかすごそうだ、と思ってもらう

generatorとは

  • 関数の一時中断、再開ができる = セーブ機能
  • generator 関数
  • generator オブジェクト = セーブデータ
  • yield 分 = セーブポイント

yield を使った関数を generator 関数と呼ぶ

function gfunc() {
  $i = 0;
  yield $i; // 1回目呼び出しでの return
  $i++;
  yeild $i; // 2回目呼び出しでの return
  $i++;
  yeild $i; // 3回目呼び出しでの return
}

generator関数は使い方が違う。

$g = gfunc(); // generatorオブジェクトが作られる。
foreach ($g as $x) { イテレータとして使ってはじめて実行される
}

generator でなにがうれしいの?

  • 汎用性の高いループ処理を generator 関数にして、ex) each_line
  • 汎用性の低いループ内処理だけ、別ループで書く foreach (each_line($filename) as $line)

もっと generator

  • generator 関数の引数を generator オブジェクトにして、generator を重ねる、なんてこともできる.


途中退室><

フレームワークアップデート

フレームワークの有識者による最新情報のショートプレゼンセッションです。

  • CakePHP2
  • Symfony
  • FuelPHP
  • Yii Framework
  • BEAR.Sunday

8分でわかる最近のCakePHP @shin1x1

今のCakePHPは?

いまどきのコントローラ

  • ファイル名とコントローラクラス名が同じに

CakePHP2.2の新機能

  • ビューブロック、継承
  • Event System (Dispatcher Filters) オブサーバパターン
  • Hash
  • ModelValidator

次のCakePHP

  • 2.2.3 が10月予定
  • CakePHP3 も発表済み

CakePHP3

  • PHP5.4以上
  • Modelが連想配列ではなく、オブジェクトを返す

まとめ

  • これから使うから CakePHP2.2.2
  • オフィシャルのオンラインドキュメント cakephp cookbook が大変便利です
  • CakePHP2実践入門 2012/09/29発売!

Symfony 株式界社エイチーム

Symfony リリース状況

  • 2.1 が最新

2.0 から 2.1 への変更

FuelPHP

発音

  • まさかの2大派閥
  • フューエル
  • ふえる(日本)

イメージ

特徴

  • 規約より設定
  • ゆるい制約
  • コアを改造することも容易
  • オレオレ化もオK

課題

業務での実績

  • 小規模開発only

転職市場

  • Symphony: 7件
  • CakePHP: 6件
  • FuelPHP: 0件

Yii @crifff

  • CodeIgniter から yii にいこうか fuel にいこうかという時代もあったが、
  • fuel にダブルスコアでまけている
  • 中国、ロシアで流行っている気がする
  • 他のフレームワークから良い所どりした洗練されたフレームワーク

yii 2.0

  • PHP5.3.8
  • composer によるパッケージ管理
  • リリースは未定

BEAR.Sunday

特徴

  • API Driven Framework
  • Web API を内部でも使う

Hypertext-Driven API

  • Hypertext じゃないと REST とはなのっていけない
  • Hypertext-Driven? = 情報がつながっている
  • OPTIONS メソッドで、どのメソッドとどのパラメータが使えるのかが分かる
  • HAL http://stateless.co/hal_specification.html

はじめてのchefレシピ

chef http://wiki.opscode.com/display/chef/Home を使ってみました。試しに openldap のインストールレシピを書いてみます。

[:contents]

はじめてのChefレシピ

はじめてのレシピ

レシピ雛形作成
 cd /opt/chef-repo
 knife cookbook create -o cookbooks myopenldap
 cd cookbooks/myopenldap
ファイル作成

metadata.rb

 recipe "myopenldap::client", "installs and configures openldap-clients"
 recipe "myopenldap::server", "installs and configures openldap-servers"

recipe/client.rb

 package "openldap-clients" do
   action :install
 end

recipe/server.rb

 package "openldap-servers" do
   action :install
 end
 
 template "/etc/openldap/slapd.conf" do
   source "slapd.conf.erb"
   action :create
 end
 
 service "slapd" do
   service_name "ldap"
   action [:enable, :start]
 end

template/default/slapd.conf.erb

 /etc/openldap/slapd.conf のコピー。

テンプレート補足

変数展開したい場合は、attributes/default.rb に

 default["first_name"] = "Mickey"

のように書いておき、template erb ファイルで

 <%= node["first_name"] %>

のように利用する。

レシピの登録
 knife cookbook upload -o .. myopenldap

knife cookbook

 knife cookbook delete tomcat
 knife cookbook upload -o path/to/cookbooks tomcat

どうやら一度消さないと更新にならないらしい。

 knife cookbook delete -a
 knife cookbook upload -o path/to/cookbooks -a

Chef

Chef-Serverのインストール

これで一発。むしろこれ以外の方法は、何をどうしても上手くいかなかった。

http://blog.frameos.org/2011/04/29/installing-chef-server-0-10-in-centos-5-rhel-5/

Chef-Clientのインストール

http://wiki.opscode.com/display/chef/Installation+with+RubyGems

rvmを使うように修正した。

 # NOTE: EXECUTE AS A ROOT USER
 #wget http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
 #rpm -Uvh epel-release-5-4.noarch.rpm
 wget -O /etc/yum.repos.d/aegis.repo http://rpm.aegisco.com/aegisco/el5/aegisco.repo
 yum -y install git gcc gcc-c++ automake autoconf make
 yum -y install zlib zlib-devel readline readline-devel openssl openssl-devel
 # Install rvm
 wget https://rvm.beginrescueend.com/install/rvm -O /tmp/rvm-install.sh
 bash /tmp/rvm-install.sh
 echo 'export PATH=/usr/local/rvm/bin:$PATH' >> /etc/bashrc
 source ~/.bash_profile
 # Install ruby1.8.7 using rvm
 rvm install 1.8.7
 rvm use 1.8.7
 rvm use 1.8.7 --default
 # Install gem
 cd /tmp
 wget http://production.cf.rubygems.org/rubygems/rubygems-1.7.2.tgz
 tar zxf rubygems-1.7.2.tgz
 cd rubygems-1.7.2
 ruby setup.rb --no-format-executable
 cd -
 # Install Chef gem and Ohai
 gem install chef
 gem install ohai

Chef-Serverの設定(初回)

サンプルレシピを登録(初回)

http://higelog.brassworks.jp/?p=643

knifeを使って、クライアントとして自分自身(サーバ)に接続して、レシピを登録する。

 knife configure -i
 cd /opt
 git clone git://github.com/opscode/chef-repo.git
 cd /opt/chef-repo
 rm -rf cookbooks
 git clone git://github.com/opscode/cookbooks

cookbook登録

 knife cookbook upload -a -o /opt/chef-repo/cookbooks
Example Role を登録(お試し)

/opt/chef-repo/roles/examples.rbの内容を以下のように編集。

 name "example"
 description "Example role for the chef repository"
 run_list("recipe[zsh]", "recipe[screen]", "recipe[git]")

RoleをChef-Serverに登録

 cd /opt/chef-repo
 rake roles

ユースケース1~ノード登録

Chef-Server

ノード作って、そのノードに Role を追加。

 CHEF_NODE=ノード名 # 普通は、ホスト名。repo など。
 CHEF_ROLE=ロール名 # "role[example]" など
 knife client create ${CHEF_NODE} -n -a -f ~/.chef/${CHEF_NODE}.pem
 knife node create ${CHEF_NODE} -n
 knife node run_list add ${CHEF_NODE} "${CHEF_ROLE}"
Chef-Client
 CHEF_NODE=`hostname -s` # 普通は、ホスト名。repo など。
 mkdir -p ~/.chef
 mkdir -p /etc/chef
 rm -f ~/.chef/knife.rb
 echo "~/.chef/knife.rb
 http://10.60.107.187:4000
 ${CHEF_NODE}
 chef-validator
 /etc/chef/validation.pem " | knife configure
 # プロキシ設定を追加
 echo 'http_proxy "http://proxy.ricoh.co.jp:8080"
 https_proxy "http://proxy.ricoh.co.jp:8080"
 http_proxy_user "p0xx000308"
 http_proxy_pass "v8h8ry29"
 no_proxy ".ricoh.co.jp, .ricoh.com, .rintra.net, localhost, 133.139.*, 165.96.*, 151.114.*, 210.173.218.*, 10.*, 172.16.*, 192.168.*, 127.0.0.1, 10.60.107.*"' >> ~/.chef/knife.rb
 # 鍵を持ってくる
 scp 10.60.107.187:/root/.chef/${CHEF_NODE}.pem ~/.chef/${CHEF_NODE}.pem
 scp 10.60.107.187:/etc/chef/validation.pem /etc/chef/validation.pem

お試し実行

 chef-client -c ~/.chef/knife.rb

ユースケース2~ロールファイルの変更(追加)

Chef-Server

ロールファイルを修正

 vi /opt/chef-repo/roles/ロール名.rb 

ロールファイルを上書き登録

 cd /opt/chef-repo
 rake roles

ノードのRoleを追加or変更

 knife node run_list add ノード名 ロール名
Chef-Client

実行

 chef-client -c ~/.chef/knife.rb

トラブルシュート

chef-clientデバグモード

chef-client -c ~/.chef/knife.rb -l debug

[BUG] Segmentation fault

たまに出るので、もう一度 chef-client を実行。