C言語でモダンなWebアプリを構築する:現代における挑戦
A Modern Web App in C
A Modern Web App in C
C言語を使用してモダンなWebアプリケーションを開発することは、現代のプログラミング環境において非常に興味深く、野心的な試みです。本来、高レベルな言語が担うWeb開発の領域を、C言語のパフォーマンスと制御性を活かしてどのように実現するかというテーマでまとめました。
CGIって素晴らしいよね。悲しいかな、nginxはlighttpdとは違ってネイティブサポートしてないけど。nginxにCGIを追加するこのプロジェクトは気に入ってるよ: https://github.com/pjincz/nginx-cgi (https://github.com/pjincz/nginx-cgi) あと、passenger[1]を使えばnginxから直接Rackアプリケーションを起動する方法もある。RackはRubyのWebアプリ向けのCGIみたいなインターフェースで: https://github.com/rack/rack (https://github.com/rack/rack) これを使えば、数行のRubyコードでアプリやミドルウェアを簡単に書ける。
専用のデータベースサーバーはない。SQLiteのC APIを通じてデータベースファイルを開くんだ。
リクエストのたびに新しいプロセスが生成される。
それだと、同時に複数のリクエストが発生したときはどうなるの?SQLiteは複数のプロセスからの同時書き込みアクセスを許可してるってこと?
変わったWebアプリに興味がある人向けに共有すると、俺はC++でゲームプロジェクトを作ってるんだ。クライアントはEmscriptenでWebAssemblyにコンパイルして、アプリケーションサーバーもC++で実装してる(nginxの背後でね)。これもAI時代より前の話。Civ(Civilization)みたいなゲームで、3D関係からWebの処理まで、すべてゼロから自作したカスタムエンジンだよ。よかったら見てみて: https://statebuilder.com/ (https://statebuilder.com/)
個人的に好きなトリビアなんだけど、busyboxにはcgi-binサポートが組み込まれてるんだ(rclone serve webdavについても触れておくべきだね)。https://elixir.bootlin.com/busybox/1.37.0/source/networking/... (https://elixir.bootlin.com/busybox/1.37.0/source/networking/httpd.c#L2401)
投稿で言及されてることは100%正しいし、特権を落とすためのpledge/chroot/etc.の使い方も含めて、かなり筋が通ってる。
デプロイには対照的な2つの考え方があるよね。
仮想マシンのDockerコンテナの上で、さらにk8sクラスターを動かして、その中で単純なecho "hello world"を動かすために、何層も泥沼のスタックを積み重ねる…
単にbash -c 'echo "hello world"'を実行して、CGIでフロントに出す(ステートレスで、シングルバイナリで、等々…)
https://github.com/Domain/bash.cgi (https://github.com/Domain/bash.cgi) を見てみて。まぁ「busybox上のbashをCGIで動かすのが正解のアーキテクチャ」と言いたいわけじゃないんだ。ただ、本当の意味で「フレームワークなし」の状態に近づくために必要なコードなんてごくわずかで、十分に理解可能なものだということを言いたいんだ。
仕事で内部ツールを作る時も、こんな方向に傾いてるよ。
some-cmd [--json|--markdown] ...yet-more-args...
プロセスやシェル環境そのものをREPLとして使うようになると、[--cgi-bin]も「ただの出力タイプの一つ」に過ぎないと思えてくる。コマンドを独立して再現可能に、そして入力引数だけに依存するようにしておけば、過酷な環境(Firecracker VMやLambdaみたいな)から快適な環境(シェル、デバッガー、テスト環境)までどこでも実行できるようになるんだ。
最初の一文には同意できないな。1990年代に俺が知っていたWebページを作ってた人たちは、CプログラムじゃなくてPerlスクリプトをcgi-binに置いていたよ。
CGO=0でビルドして、テンプレートやドキュメントをすべて埋め込んだGoのバイナリなら、ツール構成やデプロイがもっと楽になるよ。scratchコンテナに入れれば完全に隔離もできる。
Golangも、Webサイトやサービスをシングルバイナリで提供できるね。純粋なGo(CGO_ENABLED=0)にこだわればlibcへのリンクも避けられるし、nginxやapache、httpdといった従来のWebサーバーを置き換えるのに必要なhttpやtlsのライブラリも標準で揃ってる。
自分はCが好きだし、昔はCでCGIをいくつか書いたけど、Cでの並行プログラミングはあまり好きじゃないな。forkやexecなら.textセグメントの共有読み取り専用mmapを使えるけど、.bssや.dataは初期化とコピーが必要になる。スレッド化されたプロセスなら避けられるような、避けようのない起動オーバーヘッドがあるし、生のpthreadsを扱うのはかなりしんどい。まあ、低トラフィックなサイトを動かすだけならCGIでも十分だけどね。
一番懸念するのはSQLite DBのバックアップ頻度かな。今の時代、C言語で書かれたWebサーバーでRCE(リモートコード実行)を完璧に防ぐなんてまず無理だし。
自分は22年前に、自分で経営していたレストラン用にオンライン注文システムを開発したのがきっかけでWeb制作会社を始めたんだ。投稿で語られているのとほぼ同じことをやっていたけど、自分たちはFastCGIを使っていた。最初はC言語しか知らなかったからCで書いた。それが本業だったしね。非常にうまく動いていて、チェーン店が自分のソフトを買ってくれたんだけど、その後Microsoft系スタックに移行させられて、そこから問題だらけになった。それはまた別の話だけど。
その後も、オンライン注文が必要な劇場やレストラン向けにサイトを作っていたよ。すべてC、FastCGI、FreeBSD、PostgreSQL、h2o-serverでね。誰かの都合やルールに縛られず、ずっと自分たちが望む通りに動いてくれた。高速だし、修正も簡単で、誰かに勝手に変更されることもなかった。かなり上手くやれていたよ。
複雑なWeb技術やフレームワークに深く潜れば潜るほど、シンプルさが最強だってことが明白になってくるね。
そしてまたサイクルが繰り返されるんだな。今のRust移行のコストを考えると、言いたいことはわかるよ…