Slide 1

Slide 1 text

サーバーとは何かを理解して、 コンテナ1つで実行しよう @sadnessOjisan phperkaigi2024 1

Slide 2

Slide 2 text

● はじめに ● Dockerでデプロイすることの難しさに気づくまで ● 理想のDockerfileを考える ● Webサーバーと並行プログラミング ● PHPと並行プログラミング ● PHPとお手軽Dockerコンテナデプロイ ● 2024年、Nginxは必要か不要か ● まとめ 2

Slide 3

Slide 3 text

いきなりですが、 3

Slide 4

Slide 4 text

aiko のモノマネします 4

Slide 5

Slide 5 text

今日、電車の人〜!? YEAH!!!!!!!! 5

Slide 6

Slide 6 text

Laravelの人〜〜 !? 6

Slide 7

Slide 7 text

Symfonyの人〜〜 !? 7

Slide 8

Slide 8 text

Docker で実行の人 !? 8

Slide 9

Slide 9 text

SSHでソースコードそのまま送り込んで systemd とか deploy.sh で実行の人 !? 9

Slide 10

Slide 10 text

FROM php-apache の人 !? 10

Slide 11

Slide 11 text

FROM php-fpm の人 !? 11

Slide 12

Slide 12 text

ありがとう〜〜〜〜!! とても元気をもらえました! それでは次の曲です。 12

Slide 13

Slide 13 text

自己紹介 name: sadnessOjisan work: TechBowl(メンター、業務委託) tech: JSer, Rustacean, phper(←NEW) PHP歴: 1ヶ月 趣味: ブログの運営、ラーメン 13

Slide 14

Slide 14 text

blog.ojisan.io ● ラーメンからシステムコール、並行プログラミングまで色んなジャンルがある ● 今日はPHPにおける、システムコールと並行プログラミングの話をします ● 最近バズった記事: https://blog.ojisan.io/ramen-log/ 14

Slide 15

Slide 15 text

TechBowl について エンジニアに、プログラミン グの相談ができるサービス。 プログラミングの学習カリ キュラムも用意していて、未 経験者でも学べる仕組み。 15

Slide 16

Slide 16 text

TechBowl と私 ● サービス立ち上げの5年以上前からメンターとして手伝っていて、初期は予約シ ステムの開発を担ったり、今は開発アドバイザーをしている @suguru_ohki 16

Slide 17

Slide 17 text

TechBowl と私 ● サービス立ち上げの5年以上前からメンターとして手伝っていて、初期は予約シ ステムの開発を担ったり、今は開発アドバイザーをしている 5年前の負債返済をこのように解決 した事例を紹介します 5年前の人 口を出す以上はPHPやっておくか 17

Slide 18

Slide 18 text

よ〜し、まずは Laravel で掲示板作って、 Cloud Run にデプロイするぞ〜 18

Slide 19

Slide 19 text

PHPをDockerでデプロイすることの 難しさに気づくまで 19

Slide 20

Slide 20 text

ローカルでサーバー起動できたら、 ファイルを COPY して、起動スクリプトを CMD するだけの簡単なお仕事 20

Slide 21

Slide 21 text

composer create-project laravel/laravel example-app CMD [“php”, “artisan”, “serve”] docker compose up 21

Slide 22

Slide 22 text

22

Slide 23

Slide 23 text

チョロいな〜〜〜〜 😂 23

Slide 24

Slide 24 text

インターネット「ローカルサーバー起動したときのスクリプト、 本番で使っちゃダメだよ」 24

Slide 25

Slide 25 text

https://www.php.net/manual/en/features.commandline.webserver.php 25

Slide 26

Slide 26 text

しゃあない、じゃあ PHP のソースをそのまま実行するか 26

Slide 27

Slide 27 text

… 27

Slide 28

Slide 28 text

そもそも、PHP を実行するってどういうこと? 28

Slide 29

Slide 29 text

皆様の完成品の Dockerfile でも拝見いたしますか〜 29

Slide 30

Slide 30 text

Laravel Docker デプロイ [検索] 30

Slide 31

Slide 31 text

● Git 入れます ● Vim 入れます ● npm 入れます ● apt-get します ● docker-php-ext-installします ● pecl installします 31

Slide 32

Slide 32 text

● Git 入れます ● Vim 入れます ● npm 入れます ● apt-get します ● docker-php-ext-installします ● pecl installします ฅ(´・ω・`)ฅ (ここに huh-cat の画像) 32

Slide 33

Slide 33 text

いやいやいやいや 入れすぎ入れすぎ入れすぎ入れすぎ 33

Slide 34

Slide 34 text

● Git 入れます ● Vim 入れます ● npm 入れます ● apt-get します ● docker-php-ext-installします ● pecl installします ● Apache 入れます ● Nginx入れます 34

Slide 35

Slide 35 text

ฅ(´・ω・`)ฅ (ここに huh-cat の画像) ฅ(´・ω・`)ฅ (ここに huh-cat の画像) ฅ(´・ω・`)ฅ (ここに huh-cat の画像) ● Git 入れます ● Vim 入れます ● npm 入れます ● apt-get します ● docker-php-ext-installします ● pecl installします ● Apache 入れます ● Nginx入れます 35

Slide 36

Slide 36 text

いやいやいやいや Apache も Nginx も関係ないやん docker compose じゃないんですよ? 36

Slide 37

Slide 37 text

PHP動かすために Nginx や Apache が必要と、 本気で言っているんですか!!!!!!!!!!!! 37

Slide 38

Slide 38 text

え、ググった結果、マジで全部 Nginx や Apache 入っている... 38

Slide 39

Slide 39 text

この世界と僕、どちらが間違っているのだろうか 39

Slide 40

Slide 40 text

ググって出てくる Dockerfile の方が、 絶対に絶対に絶対におかしい!!!!!!!!!!!!! 40

Slide 41

Slide 41 text

理想の Dockerfile を考えよう 41

Slide 42

Slide 42 text

理想の Dockerfile ● 高速なビルド ● 小さいイメージ ● 役割を小さく 42

Slide 43

Slide 43 text

高速なビルド ● Docker のコマンド一つ一つがレイヤーになっ ていて、コンテナはレイヤーの積み重ね ● レイヤーに変更がなければキャッシュが 使える ● COPYの順番、マルチステージドビルドが鍵 src にしか変更が入っていなくても、 COPY のレイヤーに差分が生まれているの で、毎回 yarn install してしまう src にしか変更が入っていないなら、 yarn install まではキャッシュが効く 43

Slide 44

Slide 44 text

小さいイメージ ● OSに含まれる、ビルドに必要な機能 は、サーバーの実行には必要ないの で、マルチステージドビルドする ● ビルド用のコンテナでビルドして、 成果物だけを小さい実行用コンテナ (e.g. distroless)に移せば、サイズを 減らせる ● 余計なものが入っていないのでセ キュリティ的にも嬉しい 44

Slide 45

Slide 45 text

役割を小さく ● サーバーの実行に不要なものは入れない ● Vim も Git も要らない ● Dockerコンテナの中に入って作業することはない 45

Slide 46

Slide 46 text

PHPで実現できる? ● 高速なビルド ○ 😄 composer の lock を COPY すればできそう ● 小さいイメージ ○ 😖そもそも distroless が存在していない ○ 😖PHPのサーバーを単体で実行することが難しいので Webサーバー同梱になりそう ● 役割を小さく ○ 😖設計によっては Win/Mac で同じ様に動かすのが難しく、チーム開発はコンテナを開発環境に 使った方が安定しそう 46

Slide 47

Slide 47 text

お前、まさかコンテナとの相性が...😢 47

Slide 48

Slide 48 text

なんで Apache や Nginx が必要になるのだろう? 48

Slide 49

Slide 49 text

拡張なしで、言語それ自体の機能として、 並行プログラミングが苦手だから 49

Slide 50

Slide 50 text

Web サーバーと並行プログラミング 50

Slide 51

Slide 51 text

Webサーバーの仕組みを知りたければ、 並行プログラミングを知るべし 51

Slide 52

Slide 52 text

サーバーに繋がるとはどういうことか ブラウザとサーバープログラムの通信 52

Slide 53

Slide 53 text

とても簡単に言うと、プロセス間通信 53

Slide 54

Slide 54 text

パソコンの中のプロセスとスレッド ● アクティビティモニタから見れる ● プロセスもスレッドもアプリケーション やプログラムの処理の単位 ● プロセス: アプリケーションやプログラ ムそのもの ● スレッド: プロセスを構成する処理の単 位 ● 注: 具体例から導いただけで正しい説明ではないです。環境依存でも あるので正しい説明をするのはここでは控えます 54

Slide 55

Slide 55 text

プロセス間通信は可能なのか ● プロセス間は分離されているのでできなさそう ● 例: Discord から Notion のデータにアクセスできない ● ただし、Finder の画像をChromeで添付などはできるわけで、プロセスを跨いで データをやりとりする口はありそう ● ファイルディスクリプターを介す 55

Slide 56

Slide 56 text

ファイルディスクリプターを使った通信 ● システムコールでファイルディスクリプタを操作する ● プロセスにはそれぞれファイルディスクリプターを割り当てられる ○ open, socket ● ファイルディスクリプタがあれば、データの読み込み・書き込みができる ○ read, write 56

Slide 57

Slide 57 text

サーバーに繋がるとはどういうことか ブラウザとサーバープログラムの通信 インターネットを経由した、 プロセス間通信 57

Slide 58

Slide 58 text

Socket通信 socket() でファイルディスクリプタ を作って、プロセス間通信 58

Slide 59

Slide 59 text

3way handshakeから始まる TCP Stream https://blog.ojisan.io/server-architecture-2023/ 接続済み socket が手に入る 59

Slide 60

Slide 60 text

TCPからHTTPへ ● TCPの上に作られたプロトコルがHTTP ● フィールド(いわゆるヘッダ)とコンテンツ(いわゆるボディ)といった HTTP Semantics に則ったフォーマットで送受信することを、サーバーとブラウザがその前提で実装する だけでよい 60

Slide 61

Slide 61 text

HTTPからアプリケーションロジックへ 1. ソケット経由で read すれば TCP 上の(streamの)テキストを入手できる 2. HTTPでパースできたらヘッダとボディの情報が手に入る 3. ヘッダからパスがわかればルーティングできるし、ボディにデータが入っていたら保存 できるなど、その値を元にプログラムの分岐や処理をかける 4. 最後に(streamに) write すればレスポンスを返せる 61

Slide 62

Slide 62 text

システムコールのみでサーバーを実装できる! 62

Slide 63

Slide 63 text

同時にアクセスされたらどうしますか? 63

Slide 64

Slide 64 text

反復サーバー ● 順番待ちをする ● 前の人の処理が長いと、次 の人の処理が始まらないの で実用的ではない ● php artisan serve で立ち 上がるサーバーはこのモデ ルです... 64

Slide 65

Slide 65 text

処理を分担させよう! 65

Slide 66

Slide 66 text

マルチプロセスサーバー ● システムコール fork でプ ロセスを複数つくれる ● CPUのコア数まで並行に処 理を行える ● 1 リクエストに紐づく処理 を別プロセスに任せるので 順番待ちにならない ● リクエスト来てからプロセ ス作ると遅い 66

Slide 67

Slide 67 text

プリフォークサーバー ● 先に fork しておく ● いわばスレッドプールのよ うなものをする ● プール管理はめんどくさい かも 67

Slide 68

Slide 68 text

プロセスはたくさん立てても問題がないのか 68

Slide 69

Slide 69 text

マルチプロセスのデメリット ● プロセス間でデータを共有するときはファイルディスクリプタの作成、読み書き といった処理が必要 ● ファイルディスクリプタには上限数があるのでそれ以上は作れない ● CPUコア数以上のプロセスを作った際の、切り替えコストが比較的高い 69

Slide 70

Slide 70 text

代わりにスレッドを使うと節約できる 70

Slide 71

Slide 71 text

マルチスレッドサーバー ● システムコール clone ● 各プログラミング言語にも spawn 的な名前 のメソッドが生えているはず ● メンタルモデル的にはプロセスと同じ ● プロセスと違って、スレッド間はデータを共 有できる(レースコンディションの危険) 71

Slide 72

Slide 72 text

スレッドはたくさん立てても問題がないのか 72

Slide 73

Slide 73 text

C10K 問題 http://www.kegel.com/c10k.html 73

Slide 74

Slide 74 text

C10K問題 1req ごとに 1 native thread を割り当てていた ら、クライアントの数が増えれば増えるほど負荷 が高まる ハードウェアの性能的に余裕があっても性能が劣 化することがあり、それを C10K 問題と呼ぶ C10K 問題は fd, pid の枯渇、スレッドを固定長 サイズで確保することによるメモリの無駄遣い、 コンテキストスイッチコストを含む これを解決する方法が 1req ごとに 1 native thread を割り当てない技術で、シングルスレッ ド+イベントループ+IO 多重化 https://blog.ojisan.io/c10k-wakaran/ 74

Slide 75

Slide 75 text

C10K 問題はシングルスレッドで解消できる 75

Slide 76

Slide 76 text

それはPHPビルトインサーバーと同じ構成で、 順番待ちになるのでは... 76

Slide 77

Slide 77 text

シングルスレッド + 非同期処理 77

Slide 78

Slide 78 text

シングルスレッド・非同期処理のコンセプト ● イベントループによる Reactor Pattern ● IO が来たらイベントを発行し、キューに 積む ● メインループでキューの中身を取り出し て、イベントを実行 ● イベントが非同期処理を呼び出したのであ れば、完了後にイベントを発行するように して、待ち時間に別のイベントを実行 https://www.oreilly.co.jp/book s/9784873118734/ 78

Slide 79

Slide 79 text

ファイルディスクリプターを監視する ● IOが来たときに発火 ● IOが来たことはファイルディスクリプタの 監視を通して行う ● select, poll, epoll ● epoll がゲームチェンジャー。select, poll は自分からファイルディスクリプタを見に 行く必要があって、数が増えるとオーバー ヘッドになっていたが、epoll は IO 発生の 通知をもらえる設計。大量の IO を捌けるよ うになった。 https://blog.ojisan.io/how-to-epoll/ 79

Slide 80

Slide 80 text

人「CPUのコアを全部使い切りたくない?」 80

Slide 81

Slide 81 text

欲張りな人「マルチスレッドの上で非同期サーバー作ればいいじゃん」 81

Slide 82

Slide 82 text

もっと欲張りな人「自分達でスレッドを実装すれば、 もっと限界突破できるくね?」 82

Slide 83

Slide 83 text

グリーンスレッド ● OSが用意しているスレッドはカーネルスレッドやネイティブスレッド ● コンテキストスイッチ(スレッド切り替えのコスト)、リソース割り当てのスケ ジューリングに最適化の余地がある ● スレッドはコンピュータの処理の単位なので、メモリとスタックを持つ。コン ピュータに元々あるメモリから、一部分切り出してそこを自由に使えば、擬似的 にスレッドを作り出せる 83

Slide 84

Slide 84 text

M:Nの割り当て ● ユーザーが任意の数をスレッドが作れるようになると、有限のコンピュータリ ソースに対して(並行処理可能なネイティブスレッド数 = CPUのコア数上 限)、大量のスレッドを割り当てる M:N の比率で考える ● 「どのタイミングで?」「どのネイティブスレッドにどのグリーンスレッドを割 り当てる?」を考える必要がある。 84

Slide 85

Slide 85 text

コンテキストスイッチ ● CPUのスタックとレジスタをヒープに退避 ● ヒープにある情報をCPUに再現する ● CPUのスタックとレジスタそのものにアクセ スするのでアセンブラを書く必要があり、プ ログラムとマッピングさせるためにFFIなどが 必要となる https://www.oreilly.co.jp//books/9784873119595/ 85

Slide 86

Slide 86 text

コンテキストスイッチ 86

Slide 87

Slide 87 text

Work Stealing ● 特定のコアに処理が集中している場 合に、タスクをネイティブスレッド 間で融通させる仕組み ● タスクの数、タスクの実行時間に よって割り当て直すなど、いろんな 戦略が提案されている https://tokio.rs/blog/2019-10-scheduler 87

Slide 88

Slide 88 text

ガードページの削除 ● スレッドの中にはヒープ領域とスタック領域がある ● 処理が多すぎて、スタックにデータが乗りすぎるとリ ソース全体を逼迫するので、スタックには上限値が設 定されている ● この上限値を超えることをスタックオーバーフローと 言い、ガードページを使って設定されている。 ● だが使い切ることは稀なので、グリーンスレッド実装 ではこの値を削ったり動的に設定したりして、効率を 上げる。 スタック ヒープ ガードページ 0x00000000 0xFFFFFFFF 88

Slide 89

Slide 89 text

人類の叡智の結晶感があるよね...!!! 89

Slide 90

Slide 90 text

PHP と並行プログラミング 90

Slide 91

Slide 91 text

PHP で実現されているもの ● 反復サーバー: ビルトインサーバー ● マルチプロセス: Apache, CGI 91

Slide 92

Slide 92 text

PHPの実行方法 ● Server API(SAPI) を決めて実行する ● SAPI ○ CLI: PHPコマンド ○ CGI: WebServerがHTTPを受けて、CGIプロトコルでPHPプログラムを起動する ○ FastCGI: 同上、FastCGIプロトコルでPHPプロセスにアクセス ○ Apache: Webサーバー(Apache)に組み込まれているPHPモジュールを実行 92 (ゝω・)vキャピ

Slide 93

Slide 93 text

Apache で PHP を実行 ● PHPは元々 CGI 集でしかなかったが、PHP2で NCSAサーバー(後のApache)に統合され、その 上で実行できるような仕組みになる ● なので、PHPの実行の源流とも言える ● Apache と同じプロセスで PHP を実行(モジュー ル版)できるし、もちろん fork もできる https://www.oreilly.co.jp/books/9784814400270/ 93

Slide 94

Slide 94 text

CGI で PHP を実行 ● 処理を実行するために別プロセスを立ち上げて実行されるプログラム。Web サーバーから呼び出されることが多い。 ● 元々は使い捨てだが、FastCGIは一度立ち上げたプロセスを使いまわすことで効 率化を図る ● Nginxでリクエストを受けて FastCGI 経由で PHP を実行し、その結果を Nginx が返すという設計がよくされる。このときにFPM(FastCGI Process Manager) が必要になる。 94

Slide 95

Slide 95 text

ビルトインサーバー で PHP を実行 ● PHP自体にHTTPを喋らせたかったらビルトインサーバー ● シングルスレッドかつ同期的なので、2人同時にアクセスすると前の人のレスポンスが 終わらないと処理が始まらない ● 本番で使ってはいけない 95

Slide 96

Slide 96 text

マルチプロセスで並行プログラミングをするために、 リクエストを受けるサーバーと、 forkされたアプリケーション実装という設計になり、 Apache や Nginx と組み合わせて使っていた! 96

Slide 97

Slide 97 text

これだけじゃありません!!!!!! 97

Slide 98

Slide 98 text

PHP とモダンな並行プログラミング ● Swoole ○ 非同期ランタイムを実装。Node.jsと同じモデル ● FrankenPHP ○ PHPを動かすことを想定したWebサーバー。Go実装。 少なくとも Webサーバー自体は M:N グリーンスレッ ドモデル(net/http と Goroutine への言及があるた め)。SAPI の先の処理系が扱うスレッドモデルは不 明(調べ切れていないです、詳しい人教えて欲しいで す><) 98

Slide 99

Slide 99 text

Apache, Nginx 以外の実行環境がある、 Dockerデプロイ簡単なのでは... 99

Slide 100

Slide 100 text

PHP とお手軽 Docker Deploy 100

Slide 101

Slide 101 text

FrankenPHP https://speakerdeck.com/dunglas/frankenphp-a-modern-app-server-for-php-written-in-go?slide=4 101

Slide 102

Slide 102 text

もしかして、ApacheもNginxも入れずに、 1ファイルのDockerfileで全てが完結する世界来ますか? 102

Slide 103

Slide 103 text

FrankenPHP を導入する ● Laravel Octane が対応してインストールが簡単に ● Laravel Octane: アプリケーションサーバー実装を提供することで、パフォーマンス向 上を目指した拡張セット。FrankenPHP, Swoole, RoadRunner を選べる。 103

Slide 104

Slide 104 text

FrankenPHP 104

Slide 105

Slide 105 text

FrankenPHP 105

Slide 106

Slide 106 text

✨ 106

Slide 107

Slide 107 text

2024年、Nginx は必要か不要か 107

Slide 108

Slide 108 text

僕「FrankenPHPあるし Nginx 不要では」 108

Slide 109

Slide 109 text

Laravel Octane「いるよ」 109

Slide 110

Slide 110 text

https://laravel.com/docs/10.x/octane#serving-your-application-via-nginx 110

Slide 111

Slide 111 text

2024年現在、必要なのか? ● Docker使っている以上はクラウドネイティブな設計のはず。LBやCDNが前段に いるはず。 ● 証明書は?: 前段を TLS 終端にすれば良い ● 静的アセットは?: オブジェクトストレージから配信すればよい、CDNへの beresp で Cache-Control をつければ良い 111

Slide 112

Slide 112 text

僕「とはいえ公式DOCに歯向かうわけだし、本当に良いのか?」 112

Slide 113

Slide 113 text

Swooleのプロ氏〜 前段にWebサーバーなしでやっていいと 思います? Webサーバー置かずにやってたけど、特 に問題なかったよ (著者近影) 113

Slide 114

Slide 114 text

ヨシ!!!!!!!!!!!!!! 114

Slide 115

Slide 115 text

よくある議論らしいです https://qiita.com/emacs_hhkb/items/818f54b75a14832d9d16 115

Slide 116

Slide 116 text

まとめ 116

Slide 117

Slide 117 text

Webサーバーの実装で重要なのは並行プログラミングの実現だ。 PHPで並行プログラミングをするデフォルトの選択肢はマルチプロセスで、 PHPの実行を子プロセスとして呼び出してもらうために Apache や Nginx(FastCGI) が必要となっていた。 一方で Swoole や FrankenPHP といった新たな非同期ランタイムのパラダ イムがPHPにも入ってきており、Laravel Octane から使える。 117

Slide 118

Slide 118 text

FrankenPHP / Laravel Octane があれば、 Docker 1ファイルで全部固められて、 しかもモダンなWebサーバーアーキテクチャを取り入れられる。 まだ枯れていない技術だとは思いますが、これからの PHP の Developer Experience の未来がとても明るくて良いですね。 2024年もがんばっていきまっしょい! 118