| @技術/プログラミング

偶発的に puma のバージョンを上げたところ Encoding::CompatibilityError: incompatible character encodings: UTF-8 and ISO-8859-1 が多発して厳しい感じになった。

このブログでは puma は v4 系を使っていたが、調べると最近 v6 もリリースされたようで v5 系に上げてみることにした。すると忘れていたのだが puma は v5 系から daemonize する機能が削除され、デーモン化は systemd を使うべしということになっていた。プロセスのデーモン化は puma にやってもらわないと capistrano で deploy するときに面倒なので以前は v5 に上げるのを諦めて v4 を維持していたのだった。

capistrano3-puma が systemd に対応していたのでえいやっと puma を v5 に上げて deploy してみたところ、冒頭の Encoding::CompatibilityError: incompatible character encodings: UTF-8 and ISO-8859-1 が多発してページが全く表示されなくなってしまった。

一方で管理画面やアーカイブページは表示に問題がなかった。どうもファイルの読み込みが発生するページ(このブログではキャッシュを多用していて、ファイルに書き出したキャッシュを読み込んでいる)でエラーが発生しているようだった。

自分で fork した sinatra-cache.gem でファイル読み込みする部分で encoding オプションを指定してみたりしたが問題が直らない。 Haml や Sinatra のバージョンも古いのでこれらも上げてみようかと試みたが、そうするとより盛大にエラーが出てしまう( Haml を v6 にすると html_safe している出力もさらにエスケープされて HTML がぶっ壊れる)。

気になるのはローカル環境( Mac )ではこのエラーが発生しないこと。「これは環境起因では?」と思い至ってガチャガチャやってみたところ修正することができた。

Lokka では Encoding.default_external を参照しつつ String#force_encoding しているところがある。「ひょっとして Encoding.default_external の値がローカルとサーバーで異なるのでは?」試してみたところ、ローカルでは #<Encoding:UTF-8> となる Encoding.default_external の結果が、サーバーでは #<Encoding:ISO-8859-1> となっていた。

以下のブログを参考に、環境変数 RUBYOPT でエンコーディングを指定して puma を動かすことでエラーを回避できた。

systemd 経由で puma を動かすときに環境変数を設定するのは結構難しい。最初は puma が RACK_ENV=production で動かず困ったが、 systemd 用の設定ファイルで EnvironmentFile のパスを指定し、環境変数用のファイルの中で各種環境変数を定義してやる必要があった。こんな感じ。

systemd の設定ファイル

[Unit]
Description=Puma HTTP Server for portalshit (production)
After=network.target

[Service]
Type=simple

WorkingDirectory=/var/www/deploys/portalshit/current
# Support older bundler versions where file descriptors weren't kept
# See https://github.com/rubygems/rubygems/issues/3254
EnvironmentFile=/var/www/app/.config/systemd/user/portalshit_env
ExecStart=/var/www/app/.rbenv/bin/rbenv exec bundle exec --keep-file-descriptors puma -C /var/www/app/portalshit/config/puma.rb
ExecReload=/bin/kill -USR1 $MAINPID
StandardOutput=append:/var/www/deploys/portalshit/shared/log/puma_access.log
StandardError=append:/var/www/deploys/portalshit/shared/log/puma_error.log

Restart=always
RestartSec=1

SyslogIdentifier=puma

[Install]
WantedBy=default.target

環境変数の定義ファイル

RACK_ENV=production
RUBYOPT=-EUTF-8

puma v5 に移行しようとしている方の参考になれば幸いです。

| @技術/プログラミング

松浦福島初崎海岸のドクロのような岩

Mac の Homebrew のライブラリ群を久しぶりにアップデートした。 tmux と fish のバージョンを上げたら tmux が動かなくなってめっちゃ焦った。いろんなものを同時にバージョンアップするとどっちに原因があるのかわからなくて困る。結局、 brew reinstall tmux で事なきを得た。

次に VPS の Ubuntu のバージョンが古くなっていたのでアップグレードした。ついでにいろいろ気になってたところ(ログローテートがうまく動いていないところとか Nginx の設定ファイルの配置など)を直して回った。

OS のアップグレードに伴って Ruby の再インストールが必要になり、 Ruby 再インストール後にアプリケーションを deploy しようとすると mimemagic gem が yank されていたりでライブラリのアップデートが必要になった( MimeMagic は脆弱性があって Mercel に変更しないといけなかったが、変え忘れていたところがあった)。これによって引きずられるように gem のアップデートが必要になり、うっかり capistrano3-puma を v5 系にしたところ、 puma の起動ができなくて困った。どうも puma の v5 ではデーモン化オプションが削除されているようで、 capistrano で puma を再起動させたりはできないようだった。いろいろ面倒くさそうなので capistrano3-puma も puma も v4 系に固定して凌いだ。

Archives ページの npm パッケージも古くなってたので、 React や React Router 、 Webpack 、 Babel など各種ライブラリのバージョンを上げた。 React Router の v5 系から v6 系へのアップデートは結構大変だった。以下を読みながらやった。

withRouter などは React Router から機能が消えるのでそれをラップする関数を自分で書いてコンポーネントに mixin するような感じだった。以前に比べたらマイルドになっているとはいえ、 JavaScript 界隈はアップデートについて行くのが厳しい。

職業プログラマーじゃなくなったので開発環境の維持管理などがおろそかになりがちだし、 Vim やシェルのショートカットを忘れてしまうことがある。 Vim やシェルの操作は特殊技能のようなものなので忘れるともったいない。たまに触って忘れないようにしておきたい。

そういえば温かくなって庭の雑草が伸びてきたので庭の草むしりもやった。ゴールデンメンテナンスウィークだ

| @技術/プログラミング

以前、 AWS ECS で試験運用したことがあったので Docker 化自体は済んでいた。 ECS などマネージドコンテナサービスを使わずに Docker 運用ができないか試してみた。

動機

関連記事の更新処理、諸々障害があって自動化できておらず、 DB を clone してきて手元で実行してサーバーにエクスポートするという運用が続いていた。これを自動化したかった。

二つ問題があって、以下の通りだった。

  1. 関連記事の更新処理時に日本語の分かち書きをする必要があるが、 VPS インスタンスのメモリ上限があり MeCab の拡張辞書をサーバー上でインストールできない
  2. VPS 上で SQLite の算術計算を行うためには追加拡張が必要で、そのためには SQLite をソースコードからコンパイルする必要がある

1 は Docker イメージにして手元でイメージをビルドすれば解決できた。 2 の問題も Docker のなかでコンパイルを行うことで解決できた。

どうやるか

  • nginx.conf の修正
  • コンテナの create
    キャッシュのためにファイルシステムを利用しているのでホストとコンテナで public ディレクトリを共有する必要があった。
docker create \
  -e DATABASE_URL=db_url \
  -e RACK_ENV=production \
  -v /home/morygonzalez/sites/portalshit/public:/app/public \
  -p 3001:3001 --name portalshit -it morygonzalez/portalshit bundle exec puma -p 3001
  • コンテナの起動
docker start portalshit

結果どうだったか

サイトを Docker で公開することはできたが、 docker create して docker start するまでの間、ダウンタイムが発生する。

ダウンタイムなしで deploy するためには deploy のタイミングで Nginx conf を書き換えて service nginx reload する必要が出てくる。個人のブログレベルでそこまでやりたくない。

コンテナを管理するサービス( AWS ECS や Kubernetes )があるんだったら Nginx conf の書き換えなどしなくてもいい感じに deploy できると思うが、こちらも個人のブログレベルで使うものではないと思った。

結論

  • サイトの deploy はこれまで通り cap で行い、 puma はホスト OS で普通に動かす(コンテナ化しない)
  • 関連記事表示のバッチ処理のみコンテナ化することにした