「IT」カテゴリーアーカイブ

NuxtJS+socketioで認証フィルタをはさむ(passport)

NuxtJSでサーバ側はexpress、websocketを使うのにsocket.ioを使っていて、ユーザー認証をpassportで行なう場合の覚書き。

今回試すこと

axiosを使ってサーバAPIでログインし、その後socket.ioでwebsocketに接続する。やりたいことは、サーバAPIで開始したセッションをsocket.ioで引き継ぎ利用することである。

これには、socket.ioのミドルウェアを使って開始済みのセッションを読み込めば、socket.ioのリクエストでセッションを扱える。気をつける点は、socket.ioのミドルウェア関数インターフェースがexpressのそれと異なっているので、正しく情報を引き継げるようミドルウェアをつないでやることである。具体的には、socket.ioのドキュメントにもあるとおり以下のようにすればよい。

socket.ioのロード

今回は、NuxtJSのmoduleのlistenフックを使ってsocket.ioサーバをnuxtのhttpサーバ に統合する。serverMiddlewareで、/apiにexpressのappをマウントする。また、Nuxtのlistenフックを使ってsocket.ioサーバを統合するための設定をmodulesに定義する。

サーバ側

今回は動作検証のため、セッションはメモリストアを使っている。socket.ioとセッションストアを共有するため、共通の定義を使うようにしている。

server/index.js

server/session.js

server/io.js

クライアント側

クライアント側は、ログインしてセッション確認するだけのためのインターフェースにする。以下のような感じ。

ログインしていない状態の時。

ログインしてsocket.ioにつながった時。

Viewテンプレートはveutifyを使う。

pages/index.vue

NuxtJSは、Universalモードで動かしているのでSSRを行なう。sessionは、asyncDataフックを使ってコンポーネントのdataに統合する。

検証用プログラムは以下

https://github.com/moritetu/nuxt-with-socket.io-passport

参考リンク

vuex-persistedstateによるstateデータの永続化

NuxtJSのSSRモードで、vuex-persistedstateによるデータ永続化を適用してしまいSSRのfetchライフサイクルでstoreに設定したデータが上書きされてしまった。これは、私がvuex-persistedstateの使用方法を誤っただけである。SSRモードで動かす場合は、クライアントサイドで永続化したいpathにのみ適用すべし(自分への戒め)。

stateが上書きされてしまったケース

具体的には以下のような使い方をしてしまっていた。

  1. vuex-persistedstateをpluginとしてロード
  2. クライアントサイドでstoreにデータ書き込み(この段階でlocalStorageにデータが書かれている)
  3. SSRでサーバサイドでfetch処理でstateにデータを書き込む

期待する動きは、3で初回ページアクセスしてサーバサイドでcommitしたstateがクライアントサイドで復元される、である。

しかし、3番目のステップでSSRで設定したstateデータがクライアントサイドで復元されると期待するものの、vuex-persistedstateプラグインの永続化処理により、以前にlocalStorageに書き込んだ値で上書きされてしまう、という訳である

プラグインロードのタイミング

NuxtJSでは以下のようにpluginとして組み込める。クライアント再度でpluginがロードされるタイミングは、ライフサイクルでは以下となる。

  • Receives the HTML
  • Loading assets (e.g. JavaScript)
  • client-side Nuxt plugin ★ここ
    • in order as defined in nuxt.config.js
  • Vue Hydration
  • Middleware
  • Global middleware
  • Layout middleware
  • Route middleware
  • asyncData (blocking)
  • beforeCreate (Vue lifecycle method)
  • created (Vue lifecycle method)
  • The new fetch (top to bottom, siblings = parallel) (non-blocking)
  • beforeMount (Vue lifecycle method)
  • mounted (Vue lifecycle method)

引用:https://nuxtjs.org/docs/concepts/nuxt-lifecycle/

Stateのrestoreのタイミング

サーバーサイドでcommitされたstateの情報は、ページアクセスのレスポンスデータの中でシリアライズされて送信される。そして、アプリケーション初期化のタイミングでstateに復元される。

store.replaceStateの復元段階では、まだプラグインの初期化はされていない。プラグインの初期化が呼ばれるのは、コメント // Plugin executionの以降になるので、上記で復元されたstateは上書きされてしまう。

参考リンク

Nuxt.jsでsocket.ioを使うー nuxt-socket-ioライブラリ

Nuxt.jsでsocket.ioを使ったアプリケーションを作る続きです。

前回、Nuxt.jsでsocket.ioライブラリを使うやり方について整理してみました。socket.ioライブラリを直接使う方法で、3つのアプローチで試してみました。

Nuxt.jsでsocket.ioを使うやり方

今回は、Nuxt.jsでsocket.ioライブラリをすぐにアプリ内から使うことができる『nuxt-socket-io』というライブラリを使ってみたいと思います。

外部ページ(nuxt-socket-io):https://nuxt-socket-io.netlify.app

nuxt-socket-io

nuxt-socket-ioライブラリは、socket.ioを使ったサーバ側、クライアント側のプログラムを実装しやすくするためのインターフェースを提供してくれています。具体的には、以下の特徴があるようです(公式Docより)。

  • 複数のio socketのconfiguration
  • socket毎のnamespaceに対応したconfiguration
  • socket.ioのサーバ定義の自動登録
  • socket ioステータス
  • 自動エラーハンドリング
  • デバッグロギング
  • 自動teardown
  • vuexモジュール機能
  • ダイナミックAPIサポート
  • Nuxtライタイムconfigのサポート
  • ミドルウェアの自動登録

nuxt-socket-ioライブラリのサーバ側は内部的にはNuxtのlistenフックを使って実装されているようです。自分でlistenフックを使って実装するのもよいと思いますが、ライブラリを使うとconfiurationや利用が少し楽になるかと思います。

nuxt-socket-ioのインストール

使用方法

最小限の設定は以下のような感じになるかと思います。

nuxt.config.js

  • modulesでnuxt-socket-ioを定義します。これにより、プラグインとして$nuxtSocketが使えるようになるとともに、サーバ側でのsocket.ioライブラリの利用が可能になります。
  • 設定は、modulesのオプション、または、io: {} 、または、RuntimeConfig、$nuxtSocketの引数で渡せます。サーバ側の定義は、modulesのオプションまたはioで定義します。

サーバ側

サーバ側の定義は、デフォルトで以下のパスに定義ファイルを置くことになっています。このパスは、io: { servers: ... } オプションで変えることができます。server/io.jsはデフォルトのネームスペースを指しています。

server/io.js

  • middlewareは、io.useに渡されます。
  • setIOは、ioオブジェクトの生成後に呼ばれるフックです。
  • default functionは、socket.onのハンドラになります。関数名がイベント名になります。返り値は、ackに渡されます。

socketに対して自分でハンドラ定義をしたい場合は、以下のようにすると可能ではありますが、基本はreturnで関数定義が返されることを期待されている?ようです。

server/io.js

クライアント側

page/index.vue

チャネル

異なるネームスペースを使用する場合の例です。

server/io/chat.js

定義はメインネームスペース(/)の場合と同じです。

page/index.vue

参考リンク

  • https://nuxt-socket-io.netlify.app
  • https://www.npmjs.com/package/nuxt-socket-io
  • https://github.com/richardeschloss/nuxt-socket-io

Nuxt.jsでsocket.ioを使うやり方

Nuxt.jsでWebSocket、今回はsocket.ioを使う方法についての備忘録です。他によいアプローチがあるかもしれません。他の方の事例を参考にしながら、自分なりに整理してみました。

以下の3つのアプローチで記述してみます。

  1. moduleでserver.listenの上書き
  2. listenフック
  3. nuxt program

server.listenの上書き

これは、公式サンプルで書かれている内容です。

render:beforeフックの中で、listenメソッドを上書きしています。元々の処理では、protocolに応じたserverオブジェクトが作られlistenが呼ばれます。上記例では、プロトコルはhttpとしてserverオブジェクトを生成してsocket.ioライブラリに渡しています。

examples/with-sockets/io/index.js

引用)https://github.com/nuxt/nuxt.js/blob/dev/examples/with-sockets/io/index.js

listenフック

server.listenの以降で呼ばれるlistenフックを使います。serverは既にlistenが呼ばれています。

参考)https://stackoverflow.com/questions/63172001/where-do-i-add-this-websocket-code-in-the-new-nuxt-js-setup-since-it-does-not-ha

nuxt program

nuxtをプログラムから使用する方法です。これも、公式のサンプルに例があります。以下例では、公式サンプルを少し触っています。

参考)https://github.com/nuxt/nuxt.js/blob/dev/examples/with-sockets/server.js

server/index.js

server/io.js

サンプルプログラムは以下のとおり。

https://github.com/moritetu/nuxt-with-socket.io

参考リンク

NuxtJS – 自作のmoduleを作成する

はじめに

UI/UXの設計やプロトタイプ作成してイメージを具現化し仮説検証していくプロセスを効率的に回せるようになりたいという目的でフロントエンドを触り出してから、NuxtJSを学んでいます。

今回は、NuxtJSでの自作したライブラリをモジュールとして使う方法について整理してみました。コンポーネント共通で使用するアプリ用のモジュールはどうすべきか?というところから来ています。NuxtJSには、communityから提供されるmoduleと同じように自前のモジュールをロードしてアプリケーション内で使用可能になるようなので、その仕組みを使って自作モジュールを作成していきます。

理解の誤りがあるかもしれませんので記述内容については個人の責任のもと判断ください。

NuxtJSのビルドの流れ

以下のソースを参考に、ssrモードでnuxt run devを実行した時の大まかな流れを掴んでおきます。

https://github.com/nuxt/nuxt.js/blob/2.x/packages/cli/src/commands/dev.js

  • Nuxtインスタンスの作成と初期処理(ready)
    • moduleのreadyはここで実行される。
      moduleのhandlerが実行される(実行コンテキストはModuleContainerインスタンスでnuxt.config.jsで指定したmoduleのオプションが引数に渡される)
  • Server インスタンスの作成と初期処理(ready)
    • VueRendererインスタンスの作成と初期処理(ready)
      • manifestファイルなどを読む(dev時はmemoryfsから)
  • Serverのlisten
  • バナーの表示
  • ビルド
    • .nuxtディレクトリにアプリケーションリソースをgenerate
    • webpackビルド

モジュールでできること

プラグインやビルド、serverMiddleWare、フックなどの機能を提供することができます。

NuxtJSでモジュールを作成する

単純な出力だけを行なうモジュールを作成し、モジュール作成のイメージをつかみます。

モジュールのファイル構成

作成するモジュールの構成は以下のとおり。

moduleによって構成は異なると思いますが、ここでは以下の構成としてみます。

  • index.jsは、module機能を使えるようにする定義を書く
  • plugin.jsは、Componentなどでモジュール内の機能を使用可能にする定義を書く
  • mymodule.jsは、プラグイン機能の本体を書く

モジュールを利用可能にする

まず、mymoduleをNuxtJSでロードし利用可能な状態にします。nuxt.config.jsに以下のような定義を書くことで、moduleがロードされるようになります。

オプションがある場合は以下のようにして渡すことができます。

なお、他にも以下のような書き方もできます。

モジュールを作成する

index.js

index.jsでは、オプションのマージ、hookでビルドされるテンプレートファイルをダンプ、プラグインファイル及びテンプレートファイルを追加しています。プラグインで登録しているファイルはnuxtの実行時にimportされます。

plugin.js

ここでは、inject関数を呼び、コンポーネントなどからthis.$mymoduleのように呼び出せるようにします。

mymodule.js

hello関数が登録されたオブジェクトを定義します。moduleはテンプレートファイルとしてコンパイルされるので、オプションの値を元に生成するソースを可変にもできます。

componentからmymoduleを呼び出す

npm run devで起動してブラウザ画面を開くと、以下のような画面が表示されることを確認できます。

まとめ

  • モジュール機構で自作のライブラリをNuxtアプリケーションに組み込み、コンポーネントなどから利用できる。
  • モジュールを使用するには、nuxt.config.jsのmodulesで定義する。
  • addTemplateやaddPluginで追加したリソースは、テンプレートとしてコンパイルされる。

参考リンク

 

NuxtJS – storeのnuxtServerInitアクションの実行

最近はフロントエンド開発の技術に触れています。

NuxtJSのSSRモードで、storeのnuxtServerInitアクションの実行について勘違いしてしまっていた。事象は、ログインセッションがあるのにログインされていないと判断されログインページに飛んでしまうというもの。nuxtServerInitアクションが実行されていない?となり改めてドキュメントを見ると、nuxtServerInitアクションはプライマリモジュールのみが受けとると書いてあった(ちゃんと読めですね。。)

Only the primary module (in store/index.js) will receive this action. You’ll need to chain your module actions from there.

– https://nuxtjs.org/docs/2.x/directory-structure/store

複数のstoreモジュールでのnuxtServerInitの実行

間違いの書き方

以下の例では、サブモジュールでnuxtServerInitを実行しようとしています。しかし、dispatchされないので動きません。

正しい書き方

プライマリモジュールからuser namespaceのmutationsを呼びます。グルーバルのnuxtServerInitが呼ばれるので期待どおり動作します。

ソースの確認

nuxtのソースを見ると以下のようになっており、グローバルのnuxtServerInitが呼ばれています。

まとめ

  • nuxtServerInitアクションは、プライマリモジュールで指定する。
  • チェーンする場合は、プライマリモジュールから呼び出す。

参考リンク

  • https://nuxtjs.org/docs/2.x/directory-structure/store
  • https://qiita.com/erukiti/items/75792f5fd4d993de3f2f

Amazon Linuxのamazon-linux-extrasのdockerでのulimit指定

Amazon Linux2でdockerを動かす際、open files制限に引っかかってしまった。systemdのdocker.serviveやlimits.confでの設定はしていたが、dockerコンテナを起動してもlimitsが1024:4096となってしまう。

どうも、docker.serviceの環境定義ファイルのオプションで指定されているようだった。

/etc/sysconfig/docker

--default-ulimit ulimitの値を変更してあげればよい。それ以外では、オプションでなく/etc/docker/daemon.jsonを利用してもよいかもしれない。

 amazon-linux-extrasのdocker

パッケージの中身を確認する。

参考リンク

docker-jitsi-meetのENTRYPOINT

docker-jitsi-meetのbaseコンテナイメージ指定されている/initスクリプトは何だろうと思い調べた内容である。結論から言うと、s6というスーパバイザプログラムでプロセスを実行している。

entrypoint /init

s6 overlay

s6 overlayは、s6というunix系のスーパバイザツールをベースとしたDockerイメージ作成のためのツールセットのことらしい。s6は、daemontoolsやrunitのようなスーパバイザツールである。
Dockerfileのサンプルは以下のようになる。

ENTRYPOINTは/initというスクリプトとなる。s6 overlayツールは、プロセス実行のライフサイクルをステージという単位で扱いプロセス実行している。docker-jitsi-meetは上記をbaseイメージとして使用し、各種サービスのコンテナイメージをbaseイメージから派生させ作成している。

s6 overlayのステージ

以下の初期ステージ、実行ステージ、終了ステージ、の3つのステージで構成される。

stage 1

イメージの準備ステージ。具体的には環境変数の設定などである。

stage 2

ユーザ提供のファイルが実行されるステージ。
  • /etc/fix-attrs.dで所有者やパーミッションなどの設定
  • /etc/cont-init.dのスクリプト実行
  • /etc/services.dのサービスを実行しs6が監視を開始

stage 3

終了処理のステージ、プロセスのclean upと停止処理が行われる。
  • /etc/cont-finish.dでfinalize処理
  • SIGTERMでgracefulシャットダウン、SIGKILLで強制停止
基本的には、stage2と3のxxx.dで実行されるフックを使用し目的のプログラムを実行する感じになる。
試しに実行してみよう。

コンソールログから、ステージのライフサイクルを確認できる。また、実行したshは、s6-svscanというプロセスの管理下に置かれていることが分かる。s6-svscanに関する内容は以下にある。
参考)s6-svscan

s6 overlayを使ったイメージ作成と実行

単純な文字列を出力するだけのアプリを動かすイメージを作成する。

ファイル構成

  • etc下にs6で必要となるリソースを配置する。すべてのステージのファイルは必要ではないが、サンプルでは用意する。
  • app下にアプリケーションを置く。今回は、単なるsleepするシェルスクリプトである。

Dockerfileとdocker-compose.yaml

Dockerfile
s6 overlayのインストールとユーザ作成、/initを実行するだけのDockerfileである。

docker-compose.yaml
ホスト側のフックのためのスクリプトを配置したディレクトリをマウントする。

s6 overlayのリソース
etc/fix-attrs.d/01-myattr

これは、以下の形式で記述している。/appディレクトリに対する所有者及びパーミッションを設定している。

参考
etc/cont-init.d/01-config

shebangで指定しているwith-contentというプログラムでは、コンテナの環境変数を構築しexecコマンドで引数で指定されるプログラムを実行している。
etc/services.d/myapp1/run

etc/services.d/myapp1/finish

etc/services.d/myapp2/run

etc/services.d/myapp2/finish

etc/cont-finish.d/

app
単なるsleepするだけのシェルスクリプト。
app/myapp1

app/myapp2

イメージの実行

作成したイメージとアプリを実行してみる。

このプログラムを実行すると、myapp1とmyapp2が終了しても繰り返しプロセスが実行されることが分かる(通常のサーバープログラムは、上記例のようにすぐにプロセスが終了することはない)。s6-svscanによるスーパバイザが機能していることが上記例から分かる。

終了させる場合は、以下のようにできる。

コンテナのログには以下のように表示される。

その他

initやinit-stageXのスクリプトは、execlineというスクリプト言語で記述されている。シェルのようだがシェルとは異なる。コマンド実行に有用な機能が備わっている。
execline

参考リンク

nginxプロキシでアップストリームのヘッダを消す

nginxプロキシでアップストリームサーバが返す一部のレスポンスヘッダを消したい。

アップストリーム側で出力しているヘッダがあり、nginxプロキシ側でも同様にadd_headerでヘッダを出力している場合にヘッダが重複してしまう。例えばmattermostのdocker版では、一部のページでX-Frame-Optionsが重複して出力されてしまう?ようだが、どのページかは言及されていない。ただパッと見た限りではloginページくらいか?ブラウザはエラーにはならないが、できれば取り除きたい。

https://github.com/mattermost/mattermost-docker/issues/468

ソースを調査するかぎりでは、ソース内に直書きされているので管理画面で削除するといったことはできなさそうだった。

https://github.com/mattermost/mattermost-server/blob/3099128bbd4faa1cdcf716182e2cc43c294badfd/web/handlers.go#L171

mattermostのdocker版は、nginxコンテナでsecurity.confというファイルが読み込まれるようになっている。その中で以下の3つのヘッダがレスポンスヘッダに追加される。X-Frame-Options SAMEORIGIN;が追加されていることが分かる。

proxy_hide_header

このディレクティブは、ngx_http_proxy_moduleというモジュールで使えるものだが、デフォルトで組み込まれているモジュールのため使用できる。以下のように指定する。

以下をconfに設定して反映してみよう。

nginxコンテナのconfをコピーしてきて、

以下のように編集する。オリジナルのconfとの差は以下のとおり。

編集したconfを反映する。

ページにアクセスしてみると重複は解消されている。

他のアプローチとして、headers-more-nginx-moduleというモジュールがあるが、これは別途組み込みが必要なようだ。

https://www.nginx.com/resources/wiki/modules/headers_more/

参考リンク

PostgreSQL WALの中身を表示する

この記事は【2019年10月6日】と作成から2年以上経っているため、記事の内容が古い可能性があります。最新の情報を合わせてご確認されることを推奨いたします。

PostgreSQLのWAL周りについて調べる機会がありメモ。

pg_waldump

PostgreSQLでは、WALレコードをダンプするpg_waldumpというツールが用意されています。pg_waldumpはコマンドラインツールで、引数にWALセグメントファイルを指定することで、対象のセグメントのレコードを画面にダーっと出力してくれます。WALの内部を知りたい場合、pg_waldumpは大変便利です。

例えば、こんな感じで使えます。

pg_waldumpが出力する内容は以下の通りです。

rmgr リソースマネージャの種別です。PostgreSQLでは、レコード種別ごとにレコードを処理する関数が用意されており、レコード種別で呼び分けられるようになっています。https://doxygen.postgresql.org/rmgrlist_8h_source.html
len (rec/tot) Full-page image (FPI)を除いた長さとレコード全体長さ
tx トランザクションID
lsn 現在読み取ったレコードのLSN
prev 1つ前のレコードのLSN
desc リソースマネージャが出力するWALレコードに関する情報

他にもトランザクションIDを指定したり、WALの開始や終了位置を指定してダンプすることも可能です。

出力はテキスト形式ですので、コマンドラインで加工したりすることもできます。pg_waldumpを触っている中、ふとSQLでも操作できたら良いなと思い、walの読み取り方法や拡張機能の作り方の学習もかねて、walをSQLで出力できるツールwalreaderを作ってみました。この拡張機能は、あくまで学習用です。あまり慣れていないのでバグがあるかもしれません(きっと)。。

walreader

使い方は簡単です。以下で拡張機能をインストールします。

続いて、拡張機能を有効化して、SELECT文を実行します。

pg_waldumpと少し出力が異なります。出力内容は以下です。

timeline タイムラインID
walseg walファイル名
seg_off walファイル内での先頭からのオフセット
page ページNo
page_off ページ内の先頭からのオフセット
rmgr リソースマネージャ種別
rec_len Full-page image (FPI)を除いたレコード長
tot_len トータルレコード長
tot_rlen アライメントされたトータルレコード長
tx トランザクションID
lsn レコードの開始LSN
end_lsn レコードの終了LSN
prev_lsn 1つ前のレコードの開始LSN
identify リソースマネージャでの操作識別子
rmgr_desc リソースマネージャが出力するWALレコードに関する情報

https://github.com/moritetu/walreader

出力したいwalの範囲指定も想定していますが、タイムラインなどの切り替えは考慮されていないです。

WALの構造

walの中身は以下のような感じになっているようです。

wal structure

参考リンク