php conference 2019 発表資料です。
Copyright © LIMIA, Inc. All Rights Reserved.PHPからgoへの移行で分かったことPHP Conference 2019発表資料
View Slide
Copyright © LIMIA, Inc. All Rights Reserved.● グリーグループのリミア株式会社で、LIMIA という住まい領域のメディアを作っています。ゲーム会社ですが、最近はメディアに力を入れています。● 機械学習(RecSys)のエンジニアですが、iOS, Android,JSなどもやっている何でも屋です。5歳の娘のパパ。twitter: @mahiguch1● GCPのイベントでAWSの話をしたら、Googleの担当営業の方から呼び出されました。w● https://limia.jp/● https://arine.jp/● https://aumo.jp/● https://www.mine-3m.com/mine/Masahiro Higuchi/樋口雅拓2
Copyright © LIMIA, Inc. All Rights Reserved.グリーグループ公式部活動として技術書典部を立ち上げ、合同誌を作って頒布しました。次の合同誌を制作着手しており、技術書典8にもサークル参加予定です。企業部活動として技術書典7にサークル参加3
Copyright © LIMIA, Inc. All Rights Reserved.オフィスが新宿に移転しました。最大190名収容可能な勉強会スペースがあります。社内外の勉強会を開催できたらと思っています。松屋自販機もあります!最高すぎる勉強会スペースと松屋自販機4
Copyright © LIMIA, Inc. All Rights Reserved.弊社では、PHPシステムの一部をgoで書き換えました。その経験から学んだことについて、以下の構成で発表します。• 移行前後のWebシステム構成• Pythonからgoに移行した推薦システム• PHPエンジニアがgolangを学ぶ時にハマりがちなこと• golangに移行して良かった点• 改めて分かったPHPの良さアジェンダ5
Copyright © LIMIA, Inc. All Rights Reserved.移行前後のWebシステム構成
Copyright © LIMIA, Inc. All Rights Reserved.LIMIAとは?7● メディアサービス● Android, iOS, Web● 記事一覧を表示し、タップすると記事詳細を閲覧できる。● 記事一覧はパーソナライズ。● 記事詳細読了後に関連記事を出している。● Fuel PHP/EC2, go/ECS● 分析基盤はBigQuery
Copyright © LIMIA, Inc. All Rights Reserved.● EC2の上にfpmを乗せて。● MySQL/Memc/Dynamo● Fuel PHPつまり、どこにでもあるシンプルなシステム。移行前のシステム8
Copyright © LIMIA, Inc. All Rights Reserved.● 最近バージョンアップの話を聞かない● Githubを確認すると、最後のcommitが2018年5月● Laravelに行く? いや、コンテナ考えたらgolangでしょ!→ PHPをgolangで書き換えてしまおう!移行の動機Fuel PHPが。。。9
Copyright © LIMIA, Inc. All Rights Reserved.• PHP vs go: 基本はPHP。goのマイクロサービスを作り少しづづ処理を寄せていく。• https vs grpc: 型の問題が度々発生していたので、grpcで。• EKS vs ECS: インフラ工数が取れないため、まずはお手軽なECSで。• Envoy vs NLB: 同じく、まずはお手軽なECSで。(比較レイヤーが異なるため、登壇後記載削除)技術選定10
Copyright © LIMIA, Inc. All Rights Reserved.NLB経由でgoにアクセス。MySQLは両方から。APIを一つづつ移行していく。移行後のシステム11
Copyright © LIMIA, Inc. All Rights Reserved.● PHPからMemcacheへは、ConsistentHashを使って直接アクセスしていた。● goからも同様にMemcacheへアクセスしたところ、ライブラリが異なるため、ConsistentHashが合わない!→ 仕方がないので、MemcacheへアクセスするだけのECS Service(go)を作って、必ずそこを通すようにした。システム構成ハマりポイント(1)MemcachedのConsistentHashが合わない12
Copyright © LIMIA, Inc. All Rights Reserved.Memcacheが辛すぎるので、MySQL/Memcachedへのアクセスをgoに閉じ込めたい。API部分をgoで実装する。WebはLaravelコンテナをECSで動かし、APIを叩いてデータを取得する。システム最終形予想図13
Copyright © LIMIA, Inc. All Rights Reserved.● 基本文法は自習。書籍を読んだり、playgroundで試したり。● システム構成などを座学で毎日1時間x2週間。● Build用コンテナやMakefileを整備した。これで、書こうと思えば書けるレベルに。(だけどPHPで書いた方が早い)GolangトレーニングPHPerのみでバックエンドを書いていた14
Copyright © LIMIA, Inc. All Rights Reserved.● UnitTestだけでなく、APIの結合テストもFuel phpのテストを書いていた。● レスポンスのIFをかなり網羅していたので、リファクタのときには重要。● GitのDevelop branchにmergeされるとJenkinsでテストが走り、成功したら結合テスト環境に配布していた。—> 実装完了してmergeしたらテストが失敗。何故!?システム的なハマりポイント(2)テストが失敗する15
Copyright © LIMIA, Inc. All Rights Reserved.● Jenkins Slaveは、EC2にphpをインストールしてテストを実行していた。● 当然だがgolangコンテナが無いので、接続失敗でテストがコケる。● Jenkins Slave専用にECS Service建てるのは、もったいないよねー。—> 開発環境用に作ったdocker-composeをJenkins Slaveの中に立ててしまおう!システム的なハマりポイント(2)テストが失敗した理由16
Copyright © LIMIA, Inc. All Rights Reserved.Repository構成● docker: docker-compose.ymlなど● app: PHPで書かれた本体● api: golangで書かれたマイクロサービスこれまでは、Jenkinsにgithubのtokenとrepository pathを登録しておくと、手元に展開されていた。Repositoryが3つだとScriptの所でgit cloneを3行書く。—> PermissionDenied...あれ? どうやってtoken渡そう。—> .netrcに書くことで解決!システム的なハマりポイント(2)第1の関門: git tokenの渡し方17
Copyright © LIMIA, Inc. All Rights Reserved.● 1つ目のテストは成功したが、別のテストで失敗。● コンテナが立てっぱなしだったので、ポートを取れなかった。● 80/tcp —> 8080/tcp(dockerのNginxが動くport)に透過させていたのが原因。—> テストの開始時にdocker-compose up、終了時にdocker-composedownすることで解決。システム的なハマりポイント(2)第2の関門: コンテナの建て方18
Copyright © LIMIA, Inc. All Rights Reserved.● しばらくうまく動いていたが、突然テストが失敗するように。● Jenkins Slaveでコンテナの更新を行なっていなかった。● テスト開始前にdocker-compose pullしたが上手くいかない。あれ?—> ecr loginしてなかったので、docker repos.にアクセスできていなかった。loginすることで解決。課題も解決し、処理の一部がgoで動き始めました。システム的なハマりポイント(2)第3の関門: コンテナの更新19
Copyright © LIMIA, Inc. All Rights Reserved.Pythonからgoに移行した推薦システム
Copyright © LIMIA, Inc. All Rights Reserved.起動直後の一覧表示。興味関心に合わせたものを掲載すれば、使いやすいアプリになるのではないか。そう考え、記事の推薦システムを開発中でした。→ これもgolang化してしまおう!記事のベクトル化、ユーザのベクトル化、掲載リストの生成の3点について、golang化したシステムの解説を行います。概要21
Copyright © LIMIA, Inc. All Rights Reserved.ItemとUserの距離を計算し、近い物を出す。ただし、全記事との距離をリアルタイムに計算すると遅いので、分類して中心点との距離を計算することにした。クラスタ内での順位はCTRを使った。記事の推薦22
Copyright © LIMIA, Inc. All Rights Reserved.記事が更新されると、SQSに通知されます。それをLambdaで読み込んで、単語に分割し、単語をvectorにします。記事に含まれる単語の平均を記事のvectorとします。ただし頻出単語の影響緩和のため、IDFで補正します。アイテムベクトル作成23
Copyright © LIMIA, Inc. All Rights Reserved.ユーザが記事を閲覧すると、その情報がKinesisに流れます。Lambdaで受け取り、直近30件の閲覧履歴をDynamoDBに保存します。その変更をDynamoDBStreamに流し、Lambdaで受け取って記事のベクトルの平均をユーザベクトルとしてDynamoDBに書き込みます。これで推薦システムもgolang化することができました。ユーザベクトル作成24
Copyright © LIMIA, Inc. All Rights Reserved.PHPエンジニアがgolangを学ぶ時にハマりがちなこと
Copyright © LIMIA, Inc. All Rights Reserved.● リクエスト単位 PHP: static → go: context● 長期: PHP: apc → go: staticすぐ消えると思ってstaticに入れたらOOMになった。。。orzキャッシュの持ち方26
Copyright © LIMIA, Inc. All Rights Reserved.type Person struct {Name stringAge int}func (p Person) ToString() string {return fmt.Sprintf("%s: %d", p.Name, p.Age)}class xx {}みたいに書くのではなく、funcの直後に書く。関数名の先頭が小文字だとprivate、大文字だとpublic。classをどう書けば良いのかinterfaceに足を生やす27
Copyright © LIMIA, Inc. All Rights Reserved.// 単純に投げると、待たずに終わってしまう。concurrent := make(chan struct{}, 5)for i := start; i < end; i += step {concurrent <- struct{}{}go func(startAt uint64, endAt uint64) {service.CreateByRange(startAt, endAt)}(i, i+step)}並列数制限こういうケースだと別ファイルにしてsystem()で起動していた。28// 当たり前だが、並列数制限して待つ必要がある。var wg sync.WaitGroupconcurrent := make(chan struct{}, 5)for i := start; i < end; i += step {wg.Add(1)concurrent <- struct{}{}go func(startAt uint64, endAt uint64) {defer func() {wg.Done()<-concurrent}()service.CreateByRange(startAt, endAt)}(i, i+step)}wg.Wait()// 待つだけだと、限界まで起動しちゃう。var wg sync.WaitGroupfor i := start; i < end; i += step {wg.Add(1)go func(startAt uint64, endAt uint64) {defer func() {wg.Done()}()service.CreateByRange(startAt, endAt)}(i, i+step)}wg.Wait()
Copyright © LIMIA, Inc. All Rights Reserved.golangに移行して良かった点
Copyright © LIMIA, Inc. All Rights Reserved.【コンテナサイズ】● PHP: 1,500MB● go: 150MB【インスタンスサイズ】● PHP: 4CPU/8GB● go: 2CPU/8GB開発系実験結果。コンテナサイズは小さくなる。CPUは少し軽くなってそう。メモリはキャッシュを載せているので、ほとんど変わらない。登壇後追記: 不要なファイルを削ればPHPのコンテナサイズは、200MB程度になりそう。コンテナサイズ/CPU使用量が小さい30
Copyright © LIMIA, Inc. All Rights Reserved.goはコンパイルが必要なので、ビルドエラーになってくれる。これまでは、123を文字列で返して結合検証で見つかることが多かった。テストから書けば問題ないが。。。型安全31
Copyright © LIMIA, Inc. All Rights Reserved.みんな新しいものが好きなので。API速度改善という名目でgo lang対応を挟むと気分転換になる。最新技術を使っている気がしてモチベーションが上がる32
Copyright © LIMIA, Inc. All Rights Reserved.改めて分かったPHPの良さ
Copyright © LIMIA, Inc. All Rights Reserved.shuffleとかexplodeとか、自分で書かなきゃいけないの???→ はい。そうです。これは書いていて、PHPが懐かしくて仕方なくなった。標準クラスの充実34
Copyright © LIMIA, Inc. All Rights Reserved.goには、それっぽいのがあまりない。仕方がないから自作したけど、これで良かったのか。。。Laravelにしておけば良かったと。フレームワークが充実35
Copyright © LIMIA, Inc. All Rights Reserved.gopherまじ本当いない。募集しても全く応募がない。感覚的にはPHPの1/100程度。PHPerはgoに興味を持っている人が多いので、やるならPHPerを採用してgoをトレーニングした方が良さそう。(登壇後記載削除)採用36
Copyright © LIMIA, Inc. All Rights Reserved.終わりに
Copyright © LIMIA, Inc. All Rights Reserved.● WebはPHPで書いた方が早い。● 推薦システムはPythonで書いた方が早い。● 安定的なシステムを作る場合、golangが早い。当たり前だが適材適所に使うのが良い。WebはPHP(Laravel)に、推薦システムはPythonに戻す予定。お気付きの点がございましたら懇親会で教えてください。まとめ38
Copyright © LIMIA, Inc. All Rights Reserved.グリーグループ及びリミアでは、一緒にメディアを作っていく仲間を募集中です。興味のある方は、以下のサイトをご覧ください。http://corp.gree.net/jp/ja/recruit/https://www.wantedly.com/companies/limiaご静聴、ありがとうございました。We are hiring!39