ランサーズ PHP7 バージョンアップへの道

ランサーズ PHP7 バージョンアップへの道

2019/06/28に開催されたPHPカンファレンス福岡 リジェクトコン
https://connehito.connpass.com/event/122217/
で発表した、PHP5.6 → 7.3 バージョンアップに関する資料です。

652359244199b2b5108a5e09c027e0da?s=128

Kanazawa Yuki

June 28, 2019
Tweet

Transcript

  1. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ランサーズ PHP7 バージョンアップへの道 https://www.lancers.jp/ 「テクノロジーで、個をエンパワーメントする」

  2. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ͸͡Ίʹ

  3. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ⾃⼰紹介 3 ⽒名︓⾦澤 裕毅 出⾝︓宮城県仙台市 ランサーズSRE 略歴︓ ⼤学時代はネットワークを専攻

    Windowsパッケージ開発(C++) ASP開発(Java)&インフラ(オンプレ) SNS開発(PHP)&インフラ(オンプレ) 現在はランサーズのSRE
  4. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 会社概要 会社名︓ランサーズ株式会社 設⽴︓2008年4⽉ 従業員︓約170名 事業︓オンラインマッチング事業 https://www.lancers.jp/ 所在地︓ 〒150-0002

    東京都渋⾕区渋⾕ 3-10-13 TOKYU REIT 渋⾕Rビル 9F
  5. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ App S3 ELB App CloudFront Cloud Search Route53

    EC2 instance WebSocket ErastiCache Memcached ErastiCache Redis Aurora Reader Aurora Writer EC2 EC2 instance EC2 •Amazon Linux 2017.03 ◦PHP 5.3提供の最終バージョン •PHP 5.3.29 ◦AWS SDK V1&V2 ◦CakePHP 1.3.6 ◦Composer ◦PECL ◦PEAR •Webサーバー ◦Apache 2.2 •Appサーバー ◦mod_php ELB WebSocket 2017/03のサーバー構成(バージョンアップ決断時)
  6. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ App S3 ELB App CloudFront Cloud Search Route53

    EC2 instance WebSocket ErastiCache Memcached ErastiCache Redis Aurora Reader Aurora Writer Api ELB Api EC2 EC2 instance ErastiCache Redis EC2 EC2 •Amazon Linux 2018.03 •PHP 7.3.6(remi) ◦AWS SDK V3 ◦CakePHP 2.10 ◦Composer •Webサーバー ◦Nginx 1.14.1 •Appサーバー ◦PHP-FPM こっちは Python ELB WebSocket 2019/06現在のサーバー構成
  7. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ Docker for Mac(Windows) docker exec 3306 9000 Docker

    マウント SQLクライアント 開発環境(エディタ) ターミナル •Docker for Mac(Windows)で構築 ◦本番環境とほぼ同じ構成をDockerコンテナで再現 ◦DockerマウントでPCとソースを共有 ▪開発はPC上のエディタで⾏う Amazon ECR 10.10.6.11 App ランサーズ本体 10.100.106.5 WordPress コーポレート、ブログ 10.100.51.11 MySQL 10.100.6.131 WebSocket メッセージサービス データ⼊り ランサーズの開発環境
  8. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ソースコード量 ファイル数 行数 ビュー 約2000 約16万 コントローラー 約260

    約10万 モデル 約280 約9万 バッチ 約130 約5万 UT 約500 約20万 全体 約3170 約60万
  9. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ όʔδϣϯΞοϓͷྺ࢙

  10. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ バージョンアップの歴史 ˠ ˠ ˠYNPSF ˠ ˠ ˠ 2017/6完了

    約2カ⽉ 2019/2/5完了 1年超 PHP5.6に必須 CakePHP2.10に必須 PHP7.3に必須 2019/3/27完了 約2カ⽉ 2019/4/17完了 約1⽇ 2019/5/28完了 約1.5カ⽉ AWS V3化
  11. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1ˠ όʔδϣϯΞοϓ

  12. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 以前PHP5.6を試したとき(CakePHP1.3時代) 2017/06/08 01:38:06 [error] 12029#0: *87 FastCGI sent

    in stderr: "PHP message: PHP Strict Standards: Non-static method Configure::getInstance() should not be called statically in /var/www/lancers/cake/bootstrap.php on line 38 PHP message: PHP Strict Standards: Non-static method CakeLog::handleError() should not be called statically in /var/www/lancers/cake/libs/cake_log.php on line 290 PHP message: PHP Strict Standards: Non-static method CakeLog::handleError() should not be called statically in /var/www/lancers/cake/libs/debugger.php on line 707" while reading response header from upstream, client: 172.17.0.5, server: dev.lancers.jp, request: "GET /user/login?1496853486&ref=header_menu HTTP/1.1", upstream: "fastcgi://unix:/var/run/php-fpm/php-fpm.sock:", host: "dev.lancers.jp", referrer: http://dev.lancers.jp/ PHP message: PHP Warning: curl_setopt() expects parameter 2 to be long, string given in /var/www/lancers/app/vendors/AmazonSDK/lib/requestcore/requestcore.class.php on line 610 [08-Jun-2017 01:44:52] WARNING: [pool www] child 13007 said into stderr: "NOTICE: PHP message: PHP Strict Standards: Non-static method CakeLog::handleError() should not be called statically in /var/www/lancers/cake/libs/cake_log.php on line 290" [08-Jun-2017 01:44:52] WARNING: [pool www] child 13007 said into stderr: "NOTICE: PHP message: PHP Strict Standards: Non-static method CakeLog::handleError() should not be called statically in /var/www/lancers/cake/libs/debugger.php on line 707" [08-Jun-2017 01:44:52] WARNING: [pool www] child 13006 said into stderr: "NOTICE: PHP message: PHP Strict Standards: Non-static method Configure::getInstance() should not be called statically in /var/www/lancers/cake/bootstrap.php on line 38" •表向きは問題なく表⽰ ◦debugレベル0の場合 •エラーログにWarningが多発 ◦CakePHP1.3関連 ◦AWS SDK v1関連 •Nginxのエラーログ •PHP-FPMのエラーログ
  13. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6のインストール •PHP5.3時代 ◦Amazon Linux 2017.03を利⽤ ▪PHP 5.3提供の最終バージョン ◦Amazon

    Linuxのリポジトリからインストール •PHP5.6 ◦Amazon Linux 2018.03最新版を利⽤ ◦remiリポジトリからインストール
  14. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.3 ⇔ PHP5.6の切り替え •開発環境のdocker-compose.yml •docker-compose.override.ymlを以下のように記述 •↑のdocker-compose.override.ymlを配置して起動 version: '2'

    services: app: image: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/lancers_app:latest hostname: app networks: lancers: ipv4_address: 10.100.6.11 extra_hosts: - "dev.lancers.jp:10.100.50.11" … container_name: app-6-11 volumes: - ~/www:/var/www version: '2' services: app: image: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/lancers_app:5.6 $ docker-compose up -d
  15. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6切り替え結果 •Strict Warningが多発 ◦debugレベル2の場合

  16. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1ˠ όʔδϣϯΞοϓʹ൐͏ରԠ

  17. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6バージョンアップに伴う警告対応 •親クラスと⼦クラスの引数が合わない ◦CakePHP1.3→2.8で引数が変更されたパターン ▪PHP5.3では警告されなかった ▪Model.php(親クラス) ▪WorkTask.php(⼦クラス) •以下のソースの⼦クラス全関数の引数をチェック ◦Model.php

    ▪Behavior.php ◦Controller.php ▪Component.php Strict Standards: Declaration of WorkTask::afterSave() should be compatible with Model::afterSave($options = Array) public function afterSave($cretated) public function afterSave($created, $options = array()) CakePHP1.3時代のまま これを親クラスと合わせる
  18. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6バージョンアップに伴う警告対応 •参照を引数に取る関数の対応 ◦Strict (2048): Only variables should be

    passed by reference •参照が戻り値の関数の対応 ◦Strict (2048): Only variables should be assigned by reference $key = array_shift(array_keys($data)); ↓ $keys = array_keys($data); $key = array_shift($keys); $Db =& ConnectionManager::getDataSource($model->useDbConfig); ↓ $Db = ConnectionManager::getDataSource($model->useDbConfig);
  19. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6バージョンアップに伴う警告対応 •初期化せずにオブジェクトとみなして代⼊(PHP5.4からWaring) ◦Warning Error: Creating default object from

    empty value in ... •⼊れ⼦にも初期化が必要 $record->type = 'type1ʼ; ↓ $record = new stdclass(); $record->type = 'type1'; $record = new stdclass(); $record->type = 'type1'; $record->fields->key = 'key1ʼ; ↓ $record = new stdclass(); $record->type = 'type1'; $record->fields = new stdclass(); $record->fields->key = 'key1';
  20. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1ˠ όʔδϣϯΞοϓ݁Ռ

  21. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ サーバーレスポンス •約20ms改善(160ms前後→140ms前後)

  22. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ バッチ実⾏時間 •約7時間かかる集計バッチ ◦PHP5.3 ▪2:00-9:04(実⾏時間︓7:04) ◦PHP5.6 ▪2:00-8:27(実⾏時間︓6:27) 約37分短縮

  23. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1Խޙͷ ϥΠϒϥϦΞοϓσʔτ

  24. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ AWS SDKのバージョンアップ •2019/6/24にAmazon S3のAWS署名バージョン2が廃⽌ ◦AWS SDK V3にバージョンアップが必要 ▪PHP5.5以上が必要

    •Guzzleも同時にバージョンアップが必要 ◦AWS SDK V2はGuzzle V3に依存 ◦AWS SDK V3はGuzzleHttp V6に依存 •Composer 1.7.3以降だとGuzzle V3がインストールできない ◦AWS SDK V2もインストールできない ◦Composer 1.7.2で⽌めていた $ php composer.phar install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: remove guzzle/guzzle v3.9.3 - don't install guzzle/guzzle v3.9.3|remove guzzlehttp/guzzle v3.8.1 - don't install guzzlehttp/guzzle v3.8.1|don't install guzzle/guzzle v3.9.3 - Installation request for guzzle/guzzle v3.9.3 -> satisfiable by guzzle/guzzle[v3.9.3]. - Installation request for guzzlehttp/guzzle v3.8.1 -> satisfiable by guzzlehttp/guzzle[v3.8.1]. … 延期になったらしい
  25. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ Guzzleのバージョンアップ •Guzzle 3.9.3 → GuzzleHttp 6.3.3 ◦Guzzle¥Http¥Client ▪→GuzzleHttp¥Client

    ◦Guzzle¥http¥Url ▪→廃⽌ ◦Guzzle¥Plugin¥Oauth¥OauthPlugin ▪→GuzzleHttp¥Subscriber¥Oauth¥Oauth1 •composer.jsonに追加が必要 “guzzlehttp/oauth-subscriber": "0.3.*",
  26. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ AWS SDKのバージョンアップ •Aws¥Common¥Enum¥Region → 削除 ◦V3では廃⽌されている ◦リテラルで書き直しが必要 •各サービスのV3対応

    ◦例︓S3の場合 ◦↓ ◦※詳細はGitHubで公開 ▪https://bit.ly/2IAbYOx $client = S3Client::factory(array( 'key' => Configure::read('AWS.AccessKey'), 'secret' => Configure::read('AWS.SecretKey'), 'region' => Region::AP_NORTHEAST_1, )); $client = new S3Client([ 'version' => 'latest', 'credentials' => [ 'key' => Configure::read('AWS.AccessKey'), 'secret' => Configure::read('AWS.SecretKey'), ], 'region' => 'ap-northeast-1', ]);
  27. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1ˠ όʔδϣϯΞοϓ४උ

  28. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ CakePHP2.8→2.10バージョンアップ •CakePHP2.9でObjectクラスが⾮推奨 ◦CakeObjectに名前を変更 ▪PHP7ではObjectが予約語になる ▪↓ ◦Mcrypt対応 ▪CakePHP2はSecurityライブラリでまだmcryptを使っている ▪CakePHP2.10からOpenSSLが選択できるようになった

    •以下のOptionで変更可能 Configure::write('Security.useOpenSsl', true); class User extends Object class User extends CakeObject
  29. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 廃⽌、⾮推奨関数対応 •廃⽌される関数の対応 ◦ereg_* ◦eregi ◦mysql_* ◦split •⾮推奨関数対応 ◦__autoload

    ▪PHP7.2で⾮推奨 •spl_autoload_register関数に置き換える •警告対応 ◦microtime •PDOの対応 ◦$stmt->bindParam(':param1', $float, PDO::PARAM_INT); ▪PHP5.6→⼩数のまま更新 ▪PHP7.3→整数に切り捨てて更新 •PHP7.2のタイミングで仕様変更されたらしい ⼩数なら PARAM_STRにすべき
  30. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ mcrypt対応 •PHP7.1で⾮推奨。PHP7.2で削除 ◦OpenSSLに移⾏が必要 ▪※PECLのmcryptを⼊れる回避策はある ▪→今回全てOpenSSLに移⾏した •mcrypt関数のブロック暗号処理 ◦⽂字列を固定ブロック⻑に分解 ◦最後のブロックの余った部分に0x00パディングを⾏う

    ▪これを⼿動で実装 •対応したmcrypt暗号形式 ◦Blowfish CBCモード ◦Blowfish ECBモード ▪PEAR::CryptBlowfishで利⽤されていた
  31. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ mcrypt対応 •Blowfish(CBCモード) ◦暗号化 ↓ $resource = mcrypt_module_open(MCRYPT_BLOWFISH, '',

    MCRYPT_MODE_CBC, ''); mcrypt_generic_init($resource, $key, $iv); $encrypted = mcrypt_generic($resource, $content); mcrypt_generic_deinit($resource); mcrypt_module_close($resource); $l = strlen($key); if ($l < 16) { $key = str_repeat($key, ceil(16 / $l)); } if ($m = strlen($content) % 8) { $content .= str_repeat("¥x00", 8 - $m); } $encrypted = openssl_encrypt($content, 'bf-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv); $encrypted = base64_encode($encrypted); ⼿動で0x00パディング OPENSSL_ZERO_PADDING ではダメ
  32. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ mcrypt対応 •Blowfish(CBCモード) ◦復号処理 ↓ $resource = mcrypt_module_open(MCRYPT_BLOWFISH, '',

    MCRYPT_MODE_CBC, ''); mcrypt_generic_init($resource, $key, $iv); $decrypted = mdecrypt_generic($resource, $content); mcrypt_generic_deinit($resource); mcrypt_module_close($resource); $l = strlen($key); if ($l < 16) { $key = str_repeat($key, ceil(16 / $l)); } $content = base64_decode($content); $decrypted = openssl_decrypt($content, 'bf-cbc', $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv); $decrypted = trim($decrypted);
  33. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ PHP5.6 ⇔ PHP7.3の切り替え •開発環境のdocker-compose.yml •docker-compose.override.ymlを以下のように記述 •↑のdocker-compose.override.ymlを配置して起動 version: '2'

    services: app: image: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/lancers_app:latest hostname: app networks: lancers: ipv4_address: 10.100.6.11 extra_hosts: - "dev.lancers.jp:10.100.50.11" … container_name: app-6-11 volumes: - ~/www:/var/www version: '2' services: app: image: xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/lancers_app:7.3 $ docker-compose up -d
  34. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ 1)1ˠ όʔδϣϯΞοϓ݁Ռ

  35. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ サーバーレスポンス •約60ms改善(150ms前後→90ms前後)

  36. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ CPU使⽤率 •約半分に ◦サーバーがオートスケーリングしなくなった ▪1⽇中2台体制 •→半分にスケールダウン PHP5.6 PHP7.3

  37. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ バッチ実⾏時間 •約7時間かかる集計バッチ ◦PHP5.3 ▪2:00-9:04(実⾏時間︓7:04) ◦PHP5.6 ▪2:00-8:27(実⾏時間︓6:27) 約37分短縮 ◦PHP7.3

    ▪2:00-7:44(〜9:55)(実⾏時間︓5:44〜7:55) 期待したほど伸びず
  38. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ࠓޙͷల๬

  39. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ バージョンアップの歴史 ˠ ˠ ˠYNPSF ˠ ˠ ˠ 2017/6完了

    約2カ⽉ 2019/2/5完了 1年超 PHP5.6に必須 CakePHP2.10に必須 PHP7.3に必須 2019/3/27完了 約2カ⽉ 2019/4/17完了 約1⽇ 2019/5/28完了 約1.5カ⽉ AWS V3化
  40. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ CakeFestに登壇します •サイト ◦https://cakefest.org/ •開催⽇ ◦ワークショップ︓2019/11/7〜2019/11/8 ◦カンファレンス︓2019/11/9〜2019/11/19 •登壇テーマ ◦CakePHP3への滑らかな移⾏を考える

    ▪11/9 11:10-11:45
  41. 1)1ΧϯϑΝϨϯε෱Ԭ ϦδΣΫτίϯ ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ