kitak blog

Kみたいなエンジニアになりたいブログ

最近読んだ漫画

とあるカメラを巡って、一匹狼の登山家と彼を追うジャーナリストが話を動かしていく。
谷口ジローの絵の描き込みがすごい。登山の場面もさることながら、渋谷や新宿の居酒屋が細部まで緻密に描かれていて、酒を飲みたくなる。


蕭何のモデルが明らかに遠藤憲一でウケる。
劉邦のだらしないキャラがギャグっぽい描き方で際立っていて面白い。でも、このペースだと天下を取るまでにどんだけかかるんだろうね...


料理がおいしそうな、のほほん漫画と思いきや、策略・謀略の話が途中から増えてきて、なかなか読み応えのある作品。


地底旅行 1 (ビームコミックス)

地底旅行 1 (ビームコミックス)

気狂いのおじさんに付き従って、地底に探検に行く物語。
19世紀のSF小説が基になっているので「いやいや!」とツッコミを入れたくなるところも多いのだけど、当時わかっている科学的事実でロジックを組み上げている。


1122(1) (モーニング KC)

1122(1) (モーニング KC)

はてなにも匿名で書くからね」というインパクトのあるコマをTwitterで見かけて、買った。
仲がいい夫婦なのだけど、婚外恋愛の上になりたっていて、どうみてもフラジャイルな関係。正直、良い終わり方がまったく想像できない...


七都市物語(2) (ヤングマガジンコミックス)

七都市物語(2) (ヤングマガジンコミックス)

騙そうとするもの、騙されたふりをして騙そうとするもの、都市間の駆け引きが面白い。田中芳樹の物語は、人を喰ったというか、斜に構えた良い味のキャラクターが多いのが良い。

Rails + Hypernova なアプリを Heroku にデプロイする

個人メモ

の続き。

Rails + Hypernova で作った趣味アプリをデプロイすることに。

最近は Firebase を使うことも増えてきたのだけど、Express や Rails で作った API やウェブアプリのデプロイには、Heroku や Now などの Docker に対応している PaaS を使っている。

Docker 対応の PaaS で Rails + Hypernova のアプリをデプロイする際に、 1コンテナ 1サービス のお作法に従うと、Rails と Node でそれぞれ PaaS のアプリケーションを用意して、デプロイしないといけない。 このようにアプリケーションを分割すると、Rails と Node で同じコンポーネントのファイルを使用する事情から、正しく動かすには Rails と Node のデプロイ完了のタイミングを(可能な限り)揃える必要がある。

各ロールでさらに複数のアプリケーションを作っておいて、Blue-Green Deployment のようなことを実現するか、コンポーネントをHypernova に登録する際の名前にバージョンを含めるようにして、過去のバージョンも扱えるようにすればできそうだが、前者はデプロイタスクが煩雑になるし、後者はコードの管理が複雑になるので、趣味アプリでそこまでやる必要性は感じない。 また、Heroku や Now では、アプリケーション同士は内部ネットワークでやりとりできず、外部ネットワークを通す形になるのでレイテンシーも気になる(計測はしていない)。

上記の理由と Wantedly でも Rails のコンテナに Hypernova を同居させてうまくいっているようなので( ref: Rearchitecting Wantedly's Frontend | Wantedly Engineer Blog )、1コンテナで Rails と Hypernova 両方のサービスを動かすことにした。

Rails の対応

Hypernova に限らず Heroku にデプロイするために必要な対応。

Heroku では静的ファイルの配信をアプリケーションサーバーがおこなう必要があるので、config/environments/production.rb に以下を書く。

config.public_file_server.enabled = true

また、Heroku の制約でログを標準出力に書き出す必要があるので、RAILS_LOG_TO_STDOUT 環境変数を設定する。これは設定ファイルにデフォルトで以下のような記述がある。

  if ENV["RAILS_LOG_TO_STDOUT"].present?
    logger           = ActiveSupport::Logger.new(STDOUT)
    logger.formatter = config.log_formatter
    config.logger    = ActiveSupport::TaggedLogging.new(logger)
  end

昔は、rails_12factor gem を入れて上記と同じ対応していたのだけど、最近は、そういった gem を入れる必要がなくなったぽい。

Docker の対応

複数のサービスを動かすために Run multiple services in a container | Docker Documentation を参考に supervisor を使うことにした。

以下のように ruby:2.5.1 のイメージをベースに apt-get で supervisor を入れ、必要なディレクトリを掘り、設定ファイルをコピーして、supervisord を起動する。

FROM ruby2.5.1

#...

RUN apt-get update && apt-get install -y supervisor

# ...

RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# ...

CMD ["/usr/bin/supervisord"]

supervisord の設定は以下のようなかんじ。Rails や Hypernova のプロセスの標準入出力を supervisord にリダイレクトするために Dockerで子プロセスからのstdoutをsupervisordにリダイレクトする方法 – 踊る犬.netブログ (旧) を参考にした。

[supervisord]
nodaemon=true
directory=/usr/src/app

[program:rails]
command=/bin/bash -c "rails server -b 0.0.0.0"
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:hypernova]
command=/bin/bash -c "node hypernova.js"
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

悪習を繰り返そうとしたときにフフッと笑う

最近、試しているやつ。何かの本か記事で読んだ気がするのだけど、思い出せない。

タイトルの通りなのだけど、自分が悪い習慣をおこなおうとしたときにフフッと笑うようにすると、その行動を抑制できる(ような気がする)。

例えば、自分の場合だと数分おきに自然とスマホに手が伸びてしまうことがあるのだけど、そういうときにフフッと笑う。そうすると、行動をおこそうとしている自分を客観的に見ているもうひとりの自分が「お前ってやつは、しょうがないやつだなぁ」と言っている気がして、伸ばしかけた手を引っ込めることができる。

脳には、身体行動を先におこなうと、それに見合った感情が形成されるというクセがある。例えば、とりあえず口角をあげると幸せな気持ちになる、といったものがよく知られている。このクセを利用して、フフッと笑うという身体行動をとりあえずおこなうことで、悪習を抑制する感情を形成させるというわけである。

とはいえ、傍から見たら、スマホに手を伸ばしかけた人間がフフッと笑っているのは気味が悪いので、さりげなくやるのがポイントだと思う。

Heroku Container Registry で Rails アプリをデプロイするときにハマったこと

個人用メモ。

久しぶりに Heroku を触ったら、Dockerfile のコマンドに一部制限はあるものの手元で作ったイメージを動かすことができるようになっているらしく、おお、となった。buildpack を扱うのを避け続けてきた人生でした。

Hypernova の検証のために、Heroku Container Registry を使って Rails アプリのデプロイをやってみたが、アプリケーションエラーが出る。heroku logs コマンドでログを眺めるがよく分からない。heroku run rails console も試すが、コケる。スタックトレースで、pg が入っていないのが原因だと分かる。

多分、Rails アプリの Docker 化をやったことがある人は知っているか、一度はハマったことがあると思うのだけど、手元で叩いた --without production.bundle/config に記録されたのを COPY でまるごと移してしまっていたのが原因だった。 .dockerignore.bundle を追加して解決した。

あと、本筋と関係のないところで、最初に git push でアプリをデプロイして、諸々のアドオンを導入した後に、Heroku Container Registry プラグインを使うようにしたのだけど、アドオンや環境変数の設定はそのまま引き継がれるようだった。

Hypernova で Vue.js のコンポーネントを Rails でレンダリングする

個人メモ。

Rearchitecting Wantedly's Frontend | Wantedly Engineer Blog の記事を読んで、副業先でも使えるかもと思い、Hypernova を触っていた。

Hypernova を使う個人的なモチベーション

リリースまで至った、それなりの規模の SPA ではないブラウザナビゲーションで遷移する Rails アプリを想定する。リッチな UI の実現と保守性の観点から JavaScript フレームワークの導入をおこなう。導入自体は Webpacker のような仕組みで簡単にできるようになったが、ビューのロジックがあちこちに存在するという課題が生まれる。ロジックとは、erb や haml などのテンプレートに書く(サーバーサイドで一度決定したら変わることはないという意味で)静的なロジックと、JavaScript フレームワークが担うUI操作などのイベントに応じて実行される動的なロジックのふたつである。
これらのファイル、あるいはテンプレートの仕様を行き来し( erb, haml <-> react, vue, angular )、頭を切り替えながら実装するのは個人的にはけっこうキツい。

JavaScript フレームワークに静的なロジックを担わせることもできるが、SEOや初期表示の観点から、どこまでをクライアントサイドでレンダリングするか、という問題が出てくる。 理想を言えば、JavaScriptコンポーネントのコードを Railshaml などのテンプレートエンジンと同様に扱えるとよい。Hypernova を使えば、これを実現することができる。

Node.js プロセスの面倒をみるという運用の手間が増えるが、エラーハンドリングの仕組みがよくできているので、最悪 Node.js のプロセスが落ちても、クライアントサイドのレンダリングにうまくフォールバックしてくれる。

Hypernova を Vue.js で使う

Hypernova、React に限らず SSR ができる JavaScript フレームワークならば何でも扱えそうな造りに見えつつ、Vue.js のコンポーネントを扱う話をあまり見かけない。こういう issueがあるということは多分やろうとしている人はいるんだろう、というのと、hypernova-react のコードを見たらファイルが一枚だけあって、これと同じことをするラッパーを用意すればよさそうだな、と思って書いた。こんなかんじ。

import Vue from 'vue'
import hypernova, { serialize, load } from 'hypernova';

export const renderVue = (name, component) => {
    const Component = Vue.extend(component);
    return hypernova({
        server() {
            return (props) => {
                const { createRenderer } = __non_webpack_require__('vue-server-renderer');
                const renderer = createRenderer();
                const vm = new Component({ propsData: props });
                return new Promise((resolve, reject) => {
                    renderer.renderToString(vm, (err, contents) => {
                        if (err) {
                            console.error(err);
                            reject(err);
                            return;
                        }
                        resolve(serialize(name, contents, props));
                    });
                });
            };
        },
        client() {
            const payloads = load(name);
            if (payloads) {
                payloads.forEach((payload) => {
                    const { node, data } = payload;
                    const vm = new Component({ propsData: data });
                    vm.$mount(node);
                });
            }
            return component;
        }
    });
};

サーバーとクライアント両方で、Webpack でビルドする前提。Webpack 依存のコード(__non_webpack_require__)が入っているのがイケてない。Webpack の設定でクライアントのビルドの場合は vue-server-renderer を除外してもよさそう。ここらへんの問題が解決できたら、hypernova-vueとして npm に公開したい。

使う時は、Vue コンポーネントのオプションオブジェクトを先のファイルで export している renderVue 関数でラップする。後は、Hypernova の設定で、name に応じて、ラップしたコンポーネントを返すようにする。

import {renderVue} from './hypernova-vue';
import Like from './Like.vue';

export default renderVue('like', Like);

Rails のテンプレートからコンポーネントを呼び出す場合は、以下のようになる。ヘルパー名が react 決め打ちなのがアレだけれども alias か自前で render_vue_component ヘルパーを定義すればいいと思う。

<%= render_react_component('like', liked: false) %>

nuxt-community/typescript-template から生成したプロジェクトに express の API server を組み込む

個人メモ。

GitHub - nuxt-community/typescript-template: Typescript starter with Nuxt.js から生成したプロジェクトで色々開発をしていて、途中で JSON を返す API Server を組み込みたくなった(別リポジトリにしたり、実行環境を分けるのも面倒だった)。

API: Nuxt(options) - Nuxt.js にあるように、Nuxt は nuxt コマンドを通さずに、プログラムからサーバーを起動することができる。また、インスタンスの render プロパティを express の middleware として登録することができる。
問題は API Server の TypeScript のコードのコンパイルだよなーと思って、色々探していたら、自分が求めていることを全部やっているテンプレートを見つけた。

github.com

backpack という Node.js 用の諸々入りのビルドツールを使っている。いったん、このテンプレートの backpack 周辺と server ディレクトリのコードを参考にして目的を達成した。

多分、TypeScript → JavaScriptコンパイルだけできればよいので、開発時は ts-node でサーバーを起動して、本番は tscコンパイルしたコードを動かすだけでよいので、backpack 使わなくてもいいんじゃないかな、という気もする。

認知的不協和を活用して、気が散るのを防ぐ

ここ数週間、職場で試してみて、良かったやつ。

自分はコーディングや文章を書いていると次々と関係のない考えが頭に浮かんでくる。要は気が散りやすい。気がついたら、本来集中しなければいけないことと別のことに本気を出していたりする。

最近はA4サイズのホワイトボードを活用して、これを防いでいる。

まず、ホワイトボードをディスプレイの近くに置く。ディスプレイから目を逸らすとホワイトボードの文字が目に入るくらいの距離感がよい。
ホワイトボードには、「集中したいタスク」と「それをいつまでやるか」を書く。自分の場合は、例えば「~11:30 ○○の△△の実装」と書いて、その後にタスクに取り組む。

気が散って、別のことをしそうになったら(あるいはし始めたら)ホワイトボードをじっと見る。すると、ホワイトボードに書かれた内容と実際の自分の行動が矛盾していることに気づいて、なんとも言えない違和感を感じる。脳は、この違和感を解消しようとするわけだけれども、ホワイトボードに書かれた内容のほうが具体性が高いので、気が散る原因になっている考えを脳は取り下げる(面白いアイディアだったらどこかにメモをしておいて後で見返せばいいかもしれない)。

このように認知的不協和を活用することでひとつのタスクに集中して取り組むことができる。
併せて、ホワイトボードに具体的なタスクを書くことで、近くの席の人に「あの人、ホワイトボードに書いてるのと違うことしてる...」と思われるのは避けたい、といった意識も働いているかもしれない。

この他に、そもそも関係のない考えが浮かんでこないように、こういった考えを出してくる脳のリソースを別のなにかで占有するという手もある。具体的には 論理的思考の放棄の具体的方法 - 登 大遊@筑波大学大学院コンピュータサイエンス専攻の SoftEther VPN 日記 に書かれているような磁石と鉄球のイメージが参考になる。