Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPからgoへの移行で分かったこと

mahiguch
December 01, 2019

 PHPからgoへの移行で分かったこと

php conference 2019 発表資料です。

mahiguch

December 01, 2019
Tweet

More Decks by mahiguch

Other Decks in Technology

Transcript

  1. Copyright © LIMIA, Inc. All Rights Reserved.
    PHPからgoへの移行で分かっ
    たこと
    PHP Conference 2019発表資料

    View Slide

  2. 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

    View Slide

  3. Copyright © LIMIA, Inc. All Rights Reserved.
    グリーグループ公式部活動とし
    て技術書典部を立ち上げ、合同
    誌を作って頒布しました。
    次の合同誌を制作着手してお
    り、技術書典8にもサークル参加
    予定です。
    企業部活動として技術書典7にサークル参加
    3

    View Slide

  4. Copyright © LIMIA, Inc. All Rights Reserved.
    オフィスが新宿に移
    転しました。最大190
    名収容可能な勉強会
    スペースがあります。
    社内外の勉強会を開
    催できたらと思ってい
    ます。
    松屋自販機もありま
    す!
    最高すぎる勉強会スペースと松屋自販機
    4

    View Slide

  5. Copyright © LIMIA, Inc. All Rights Reserved.
    弊社では、PHPシステムの一部をgoで書き換えました。その経験から学んだことについ
    て、以下の構成で発表します。
    • 移行前後のWebシステム構成
    • Pythonからgoに移行した推薦システム
    • PHPエンジニアがgolangを学ぶ時にハマりがちなこと
    • golangに移行して良かった点
    • 改めて分かったPHPの良さ
    アジェンダ
    5

    View Slide

  6. Copyright © LIMIA, Inc. All Rights Reserved.
    移行前後のWebシステム構成

    View Slide

  7. Copyright © LIMIA, Inc. All Rights Reserved.
    LIMIAとは?
    7
    ● メディアサービス
    ● Android, iOS, Web
    ● 記事一覧を表示し、タップすると
    記事詳細を閲覧できる。
    ● 記事一覧はパーソナライズ。
    ● 記事詳細読了後に関連記事を出
    している。
    ● Fuel PHP/EC2, go/ECS
    ● 分析基盤はBigQuery

    View Slide

  8. Copyright © LIMIA, Inc. All Rights Reserved.
    ● EC2の上にfpmを乗せて。
    ● MySQL/Memc/Dynamo
    ● Fuel PHP
    つまり、どこにでもあるシンプルなシステム。
    移行前のシステム
    8

    View Slide

  9. Copyright © LIMIA, Inc. All Rights Reserved.
    ● 最近バージョンアップの話を聞かない
    ● Githubを確認すると、最後のcommitが2018年5月
    ● Laravelに行く? いや、コンテナ考えたらgolangでしょ!
    → PHPをgolangで書き換えてしまおう!
    移行の動機
    Fuel PHPが。。。
    9

    View Slide

  10. Copyright © LIMIA, Inc. All Rights Reserved.
    • PHP vs go: 基本はPHP。goのマイクロサービスを作り少しづづ処理を寄
    せていく。
    • https vs grpc: 型の問題が度々発生していたので、grpcで。
    • EKS vs ECS: インフラ工数が取れないため、まずはお手軽なECSで。
    • Envoy vs NLB: 同じく、まずはお手軽なECSで。
    (比較レイヤーが異なるため、登壇後記載削除)
    技術選定
    10

    View Slide

  11. Copyright © LIMIA, Inc. All Rights Reserved.
    NLB経由でgoにアクセス。
    MySQLは両方から。
    APIを一つづつ移行していく。
    移行後のシステム
    11

    View Slide

  12. Copyright © LIMIA, Inc. All Rights Reserved.
    ● PHPからMemcacheへは、ConsistentHashを使って直接アクセスして
    いた。
    ● goからも同様にMemcacheへアクセスしたところ、ライブラリが異なるた
    め、ConsistentHashが合わない!
    → 仕方がないので、MemcacheへアクセスするだけのECS Service(go)を
    作って、必ずそこを通すようにした。
    システム構成ハマりポイント(1)
    MemcachedのConsistentHashが合わない
    12

    View Slide

  13. Copyright © LIMIA, Inc. All Rights Reserved.
    Memcacheが辛すぎるので、MySQL/Memcachedへ
    のアクセスをgoに閉じ込めたい。
    API部分をgoで実装する。
    WebはLaravelコンテナをECSで動かし、APIを叩いて
    データを取得する。
    システム最終形予想図
    13

    View Slide

  14. Copyright © LIMIA, Inc. All Rights Reserved.
    ● 基本文法は自習。書籍を読んだり、playgroundで試したり。
    ● システム構成などを座学で毎日1時間x2週間。
    ● Build用コンテナやMakefileを整備した。
    これで、書こうと思えば書けるレベルに。
    (だけどPHPで書いた方が早い)
    Golangトレーニング
    PHPerのみでバックエンドを書いていた
    14

    View Slide

  15. Copyright © LIMIA, Inc. All Rights Reserved.
    ● UnitTestだけでなく、APIの結合テストもFuel phpのテストを書いてい
    た。
    ● レスポンスのIFをかなり網羅していたので、リファクタのときには重要。
    ● GitのDevelop branchにmergeされるとJenkinsでテストが走り、成功
    したら結合テスト環境に配布していた。
    —> 実装完了してmergeしたらテストが失敗。何故!?
    システム的なハマりポイント(2)
    テストが失敗する
    15

    View Slide

  16. Copyright © LIMIA, Inc. All Rights Reserved.
    ● Jenkins Slaveは、EC2にphpをインストールしてテストを実行していた。
    ● 当然だがgolangコンテナが無いので、接続失敗でテストがコケる。
    ● Jenkins Slave専用にECS Service建てるのは、もったいないよねー。
    —> 開発環境用に作ったdocker-composeをJenkins Slaveの中に立
    ててしまおう!
    システム的なハマりポイント(2)
    テストが失敗した理由
    16

    View Slide

  17. 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行書く。—> Permission
    Denied...
    あれ? どうやってtoken渡そう。—> .netrcに書くことで解決!
    システム的なハマりポイント(2)
    第1の関門: git tokenの渡し方
    17

    View Slide

  18. Copyright © LIMIA, Inc. All Rights Reserved.
    ● 1つ目のテストは成功したが、別のテストで失敗。
    ● コンテナが立てっぱなしだったので、ポートを取れなかった。
    ● 80/tcp —> 8080/tcp(dockerのNginxが動くport)に透過させていたのが原
    因。
    —> テストの開始時にdocker-compose up、終了時にdocker-compose
    downすることで解決。
    システム的なハマりポイント(2)
    第2の関門: コンテナの建て方
    18

    View Slide

  19. Copyright © LIMIA, Inc. All Rights Reserved.
    ● しばらくうまく動いていたが、突然テストが失敗するように。
    ● Jenkins Slaveでコンテナの更新を行なっていなかった。
    ● テスト開始前にdocker-compose pullしたが上手くいかない。あれ?
    —> ecr loginしてなかったので、docker repos.にアクセスできていな
    かった。loginすることで解決。
    課題も解決し、処理の一部がgoで動き始めました。
    システム的なハマりポイント(2)
    第3の関門: コンテナの更新
    19

    View Slide

  20. Copyright © LIMIA, Inc. All Rights Reserved.
    Pythonからgoに移行した
    推薦システム

    View Slide

  21. Copyright © LIMIA, Inc. All Rights Reserved.
    起動直後の一覧表示。興味関心に合わせたものを掲載
    すれば、使いやすいアプリになるのではないか。
    そう考え、記事の推薦システムを開発中でした。
    → これもgolang化してしまおう!
    記事のベクトル化、ユーザのベクトル化、掲載リストの生
    成の3点について、golang化したシステムの解説を行い
    ます。
    概要
    21

    View Slide

  22. Copyright © LIMIA, Inc. All Rights Reserved.
    ItemとUserの距離を計算し、近い物を出す。
    ただし、全記事との距離をリアルタイムに計算
    すると遅いので、分類して中心点との距離を計
    算することにした。クラスタ内での順位はCTR
    を使った。
    記事の推薦
    22

    View Slide

  23. Copyright © LIMIA, Inc. All Rights Reserved.
    記事が更新されると、
    SQSに通知されます。そ
    れをLambdaで読み込ん
    で、単語に分割し、単語を
    vectorにします。記事に
    含まれる単語の平均を記
    事のvectorとします。ただ
    し頻出単語の影響緩和の
    ため、IDFで補正します。
    アイテムベクトル作成
    23

    View Slide

  24. Copyright © LIMIA, Inc. All Rights Reserved.
    ユーザが記事を閲覧すると、その情報がKinesisに流れます。Lambdaで受け取
    り、直近30件の閲覧履歴をDynamoDBに保存します。その変更をDynamoDB
    Streamに流し、Lambdaで受け取って記事のベクトルの平均をユーザベクトルと
    してDynamoDBに書き込みます。
    これで推薦システムもgolang化することができました。
    ユーザベクトル作成
    24

    View Slide

  25. Copyright © LIMIA, Inc. All Rights Reserved.
    PHPエンジニアがgolangを学
    ぶ時にハマりがちなこと

    View Slide

  26. Copyright © LIMIA, Inc. All Rights Reserved.
    ● リクエスト単位 PHP: static → go: context
    ● 長期: PHP: apc → go: static
    すぐ消えると思ってstaticに入れたらOOMになった。。。orz
    キャッシュの持ち方
    26

    View Slide

  27. Copyright © LIMIA, Inc. All Rights Reserved.
    type Person struct {
    Name string
    Age int
    }
    func (p Person) ToString() string {
    return fmt.Sprintf("%s: %d", p.Name, p.Age)
    }
    class xx {}みたいに書くのではなく、funcの直後に書く。
    関数名の先頭が小文字だとprivate、大文字だとpublic。
    classをどう書けば良いのか
    interfaceに足を生やす
    27

    View Slide

  28. 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.WaitGroup
    concurrent := 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.WaitGroup
    for 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()

    View Slide

  29. Copyright © LIMIA, Inc. All Rights Reserved.
    golangに移行して
    良かった点

    View Slide

  30. Copyright © LIMIA, Inc. All Rights Reserved.
    【コンテナサイズ】
    ● PHP: 1,500MB
    ● go: 150MB
    【インスタンスサイズ】
    ● PHP: 4CPU/8GB
    ● go: 2CPU/8GB
    開発系実験結果。コンテナサイズは小さくなる。CPUは少し軽くなってそう。メモ
    リはキャッシュを載せているので、ほとんど変わらない。
    登壇後追記: 不要なファイルを削ればPHPのコンテナサイズは、200MB程度
    になりそう。
    コンテナサイズ/CPU使用量が小さい
    30

    View Slide

  31. Copyright © LIMIA, Inc. All Rights Reserved.
    goはコンパイルが必要なので、ビルドエラーになってくれる。
    これまでは、123を文字列で返して結合検証で見つかることが多かった。
    テストから書けば問題ないが。。。
    型安全
    31

    View Slide

  32. Copyright © LIMIA, Inc. All Rights Reserved.
    みんな新しいものが好きなので。
    API速度改善という名目でgo lang対応を挟むと気分転換になる。
    最新技術を使っている気がしてモチベーションが上がる
    32

    View Slide

  33. Copyright © LIMIA, Inc. All Rights Reserved.
    改めて分かったPHPの良さ

    View Slide

  34. Copyright © LIMIA, Inc. All Rights Reserved.
    shuffleとかexplodeとか、自分で書かなきゃいけないの???
    → はい。そうです。
    これは書いていて、PHPが懐かしくて仕方なくなった。
    標準クラスの充実
    34

    View Slide

  35. Copyright © LIMIA, Inc. All Rights Reserved.
    goには、それっぽいのがあまりない。
    仕方がないから自作したけど、これで良かったのか。。。
    Laravelにしておけば良かったと。
    フレームワークが充実
    35

    View Slide

  36. Copyright © LIMIA, Inc. All Rights Reserved.
    gopherまじ本当いない。募集しても全く応募がない。感覚的にはPHPの
    1/100程度。
    PHPerはgoに興味を持っている人が多いので、やるならPHPerを採用してgo
    をトレーニングした方が良さそう。
    (登壇後記載削除)
    採用
    36

    View Slide

  37. Copyright © LIMIA, Inc. All Rights Reserved.
    終わりに

    View Slide

  38. Copyright © LIMIA, Inc. All Rights Reserved.
    ● WebはPHPで書いた方が早い。
    ● 推薦システムはPythonで書いた方が早い。
    ● 安定的なシステムを作る場合、golangが早い。
    当たり前だが適材適所に使うのが良い。
    WebはPHP(Laravel)に、推薦システムはPythonに戻す予定。
    お気付きの点がございましたら懇親会で教えてください。
    まとめ
    38

    View Slide

  39. Copyright © LIMIA, Inc. All Rights Reserved.
    グリーグループ及びリミアでは、一緒にメディアを作っていく仲間を募集中で
    す。興味のある方は、以下のサイトをご覧ください。
    http://corp.gree.net/jp/ja/recruit/
    https://www.wantedly.com/companies/limia
    ご静聴、ありがとうございました。
    We are hiring!
    39

    View Slide