内部ネットワーク上での日記のデプロイ

Ruby のインストールに苦戦

昨日の夜は Raspberry Pi に Nginx + Jekyll で日記の非公開サーバを立てる準備をしていた。しかし、想像以上に難航した。

まず、Nginx を Apt からふつうにインストールした。リクエストの前に git pull するためには lua-nginx-module が必要で、Nginx 公式が Apt のパッケージを用意してくれているから楽に導入できそうだ、と思っていた。しかし実際にインストールしようとするとパッケージが見つからないというエラーになってしまう。Nginx はこのモジュールの公式での提供をやめてしまった可能性が高い。

ただ、このモジュールがないと本当にリクエストの前にコマンドを実行することができないのかちょっと実験してみたくなったので、いったん Nginx は放置して Ruby をインストールしようとした。しかしこれが思わぬ魔境だった。

asdf はすでに Raspberry Pi に導入済みだったので、Ruby のプラグインを導入して、あとは Ruby をインストールするだけだと思っていたのだが、ライブラリ周りのエラーが発生してインストールできなかった。

エラーの原因となるライブラリは主に OpenSSL と psych だった。

psych はネットで調べると libyaml が必要ということらしく、Ubuntu では Apt で libtoollibyaml-dev をインストールすることによって解決することができた。

さて、本当の問題は OpenSSL のほうだ。コイツが本当に厄介だ。

現在の Raspberry Pi では Ubuntu 22 が動いている。そして Ubuntu 22 では OpenSSL 3.0.2 がデフォルトでインストールされている。

しかし Ruby では OpenSSL 3 系の対応が追いついていないらしく、1.1.1 が必要なようだ。

https://www.openssl.org/source/

そこで OpenSSL 1.1.1 をインストールしようとしたのだが、これが盛大にハマった。

OpenSSL 1.1.1 をインストールするためには公式からソースをダウンロードしてビルドしなければならない。インストール自体は問題なくできるのだが、その後、openssl version を実行すると以下のエラーが表示される。

openssl: symbol lookup error: openssl: undefined symbol: EVP_mdc2, version OPENSSL_1_1_0

調べてみると、sudo ldconfig すれば解決できるというページが山のように表示されるが、残念ながらこれをいくら実行しても問題が解決することはなかった。

その後も、ldd コマンドを使って OpenSSL 1.1.1 のほうのパスを指定したり、OpenSSL 1.1.1 側のパスを優先的に使うように順番を入れ替えたりしたのだが効果ナシ。

以下の記事がよくまとまっているようだったのでこの記事の通りに試してみてもやはり解決せず。

https://learnubuntu.com/install-openssl/

そして、今日、以下の GitHub のディスカッションを見つけた。

https://github.com/rbenv/ruby-build/discussions/1940

Ruby 3.1 より前のバージョンでは今まで調べていたとおり OpenSSL 1.1.1 しかサポートされていないのだが、Ruby 3.1 以降では OpenSSL 3 系もサポートされているっぽい。

でも、Ruby 3.2.0 のインストールを試したときにもビルドエラーで失敗したんだよなあ、と思いつつ、ダメ元でやってみたら、なんとこれですんなりインストールすることができた。

うーん、どういうことだったんだろうか。最初にインストールを試みたのは Ruby 2.7.7 で、このときは上記の情報通り OpenSSL のエラーが出ていた。

しかしその後、psych 周りでもエラーが出ていることを知り、最初は libtool だけをインストールしたんだけどエラーが消えなくて、libyaml-dev をインストールするという記事に出会うまでに少し間があった。

だから、Ruby 3.2.0 のほうでも OpenSSL の問題がまだ存在していると思いこんでずっとそっち側の問題解決に当たっていたからドツボにはまったのかもしれない。

いずれにせよ OpenSSL 1.1.1 を正常にインストールすることには失敗したので、OpenSSL をあまりいじりたくないなあという気持ちがより高まった。今までも何かをインストールする際に OpenSSL 周りでコケることは頻繁に発生していたのでもううんざりって感じ。

何はともあれ今日試してみたら無事にインストールすることができたので、とりあえず先に進もう。

ちなみに OpenSSL 1.1.1 はもう用済みなので sudo make uninstall で消しておいた。いろいろ試していたのでいろんなパスに OpenSSL 1.1.1 をインストールしてしまったが、一応覚えている限りのパスに対してアンインストールを実行したのでたぶん大丈夫だと思う。openssl version を実行したときにちゃんと 3.0.2 って表示されたので。

Ruby 3.1.3 にダウングレード

Ruby 3.2.0 をインストールできてホッと一安心、と思いきや、bundle install して bundle exec jekyll serve してみたところ、Jekyll 内部で使用されている Liquid のエラーで起動できなかった。

調べてみると、どうやら Ruby 3.2 & Liquid 4.0.3 だと動作しないとのこと。

https://github.com/jekyll/jekyll/issues/9233

なので問題を解決するためには

のいずれかの対応をする必要がある。

ダウングレードよりもアップグレードのほうが良いだろうとはじめは思っていたのだが、Gemfile を見てみると Jekyll や Liquid ではなく github-pages をインストールしていることを思い出した。GitHub Pages ではこの github-pages という Gem に基づいたライブラリとそのバージョンを使用しているので、ローカルと GitHub Pages で差異をなくすためにこのライブラリを使用しているんだった。

そして、現行の github-pages に含まれている Liquid のバージョンはなんと 4.0.3……。おいおいカンベンしてくれ……。

https://pages.github.com/versions/

なので、github-pages を使っている限りは Liquid のバージョンを上げられないということになる。(Liquid 4.0.4 を個別で指定しようとすると依存関係でエラーになる)

そもそも Raspberry Pi で稼働させようとしている背景からも、今のところ GitHub Pages での公開は考えてはいないが、github-pages のほうが Gemfile がスッキリして見えるし今後のことを考えるとやはりなるべくライブラリのバージョンによる差異は避けたいものだ。

ということで仕方なく Ruby 3.1.3 をインストールすることにした。

先は長い……。

とりあえずデプロイ完了

Ruby 3.1.3 をインストールしてからはそれなりに順調に作業が進んだ。

内部ネットワーク用のウェブサイトを立てたことがないのでドメイン周りの Nginx の設定にちょっと手間取ったが、それでも解決方法は割とすぐに見つかった。

Jekyll サーバを立てる際に、サイトのベース URL の中にどうしてもポート番号が含まれてしまうので、仕方なく _config.ymlurl を編集してプロダクション環境で動作させることにした。もし今後 GitHub Pages でインターネット上に公開することになったときはまたここを編集する必要がある。_config.yml の設定を内部ネットワーク用のドメインにしてしまうのはちょっと抵抗があったが、まあ仕方ない。一時的なものだと思うことにしよう。早く嫉妬やら公開されると思うと躊躇うような複雑な心境から開放されたい。

ドメインは diary.test とした。

http://diary.test

IP アドレスのままだと、他のページをデプロイしたくなったときに困ってしまうので今後のことも考えてドメインで分けることにした。ポート番号で分ける方法もあったが、毎日ブラウザでアクセスする常用サイトの URL にポート番号が含まれているのはなんとなく気持ち悪いし、サーバを立てるたびにそのサーバで使用するポート番号を UFW でオープンしなければいけない。Nginx のリバースプロキシを使ったほうがきれいにリソースを管理できると思ったのでドメインを使うことにした。

このドメインは外では使えない。.test はインターネット上で使えるものではなく DNS サーバのテスト用ドメインとして予約されているものらしい。

https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains

トップレベルドメインを何にしようか迷っていて最終的に .test にしたのだが、他の候補と不採用になった理由としてはこんな感じ。

最初は .pi みたいなオリジナルトップレベルドメインで管理することも考えたが、.local と同じくブラウザがドメインという認識をしないのが使いづらそうだなと思ったのでやめた。

あとは日記を更新して git push したあと、Raspberry Pi で git pull する仕組みを構築しないと。とりあえず今思いついているのは、昨日も書いたとおり Lua のモジュールを使ってリクエストの前にコマンドを実行することなんだけど、現状、ビルドが完了するのに 30 秒くらいかかる。

1 ファイルの一部を更新しただけでも毎回これくらいの時間がかかるので、もっと良いやり方はないかなあと考えている。このビルド中は Jekyll サーバの CPU 使用率が Raspberry Pi 上で 100% に張り付くので cron とかで定期実行するのはあまり得策ではなさそうだ。