Nuxt.jsを「正しく」終了する
はじめに
この記事はNuxt.js Advent Calendar2021の12日目の記事です。
11日目は@Skmt3PさんのNuxtのコンポーネントをWeb Componentとして利用するでした。(web component触ってきてないからへぇって気持ちで読まさせていただきました)
概要
hooks自体を調べていたときにclose
という項目がありました。そして、説明には
Nuxt インスタンスが正しく終了したとき
というのがありました。
「正しく」とは一体…となって原文を見てみると
Nuxt instance is gracefully closing.
ということで正しく停止=gracefully closingという形であることが判明し、自分が今まで正しく停止していなさそうだと言うことを認識しました。(自虐
今回はNuxt.jsを「正しく」停止するためには、というかこのcloseはどうやってやれば呼ばれるのか、何をすればいいのかを調べてみました。
今回、Nuxt.jsのアプリケーションはTypeScriptで実装しています。
そのため、npm run start
に関してはnuxt start
が動くはずです。
そもそもアプリケーションを停止するとは
こちらの記事がとてもわかり易かったです。
要するにSIGTERMの命令で終了するように実装をしましょうということのようです。(dockerや、k8s環境で実行しているだろうという強い圧力)そもそも正しくないのか?
とりあえず以下のように実装してみます(nuxtをanyにしているのはめんどいからですすいません
import { close } from './hooks'
export default {
hooks: {
close
}
}
export const close = (nuxt: any) => {
console.log('close')
return new Promise((resolve) => {
// 動作確認のために3秒待つ
setTimeout(() => resolve(1), 3000)
})
}
上記の実装をした状態で、npm run start
してみます。
そして以下のコマンドのようにSIGTERMを送ってみます。
pgrep -f npm | xargs kill
そうすると
[1] xxxxxxx terminated npm run start
といって停止していることがわかります。SIGTERMをうけとれず、どうやら「正しく」停止していないようです。
「正しく」停止させてみる
SIGTERMのイベントを監視するには、hooksのready eventに仕込む のが楽そうです(引数としてnuxtのインスタンスが取れるので)
これを実装するには、例えば以下のようになります。
- import { close } from './hooks'
+ import { ready, close } from './hooks'
export default {
hooks: {
+ ready,
close
}
}
+ export const ready = (nuxt: any) => {
+ console.log('ready')
+ process.on("SIGTERM", () => {
+ console.info("Shutdown Server for sigterm...");
+ nuxt.close();
+ })
+ }
export const close = (nuxt: any) => {
console.log('close')
return new Promise((resolve) => {
// 動作確認のために3秒待つ
setTimeout(() => resolve(1), 3000)
})
}
上記の実装をした状態で、npm run start
してみます。
その後先程の通りSIGTERMを送ると、期待通り停止される(stopといってから、3秒待って停止される)ことがわかるかと思います。
正しく停止されたっぽいです。
Docker上で「正しく」停止させる
ではdocker上で問題なく行けるか、かんたんに試してみます。以下のようなDockerfileを用意し、コンテナを動かし停止させます。(docker stopは内部のプロセスに対してSIGTERMを送ります)
FROM node:14.18.2
WORKDIR /app
COPY . .
RUN npm i && npm run build
CMD ["npm", "run", "start"]
docker build -t john:test .
docker run -it --name john-test john:test
// 別ターミナルで
docker stop john-test
すると、stopがでないし、3秒待つこともなく終了しました。
いろいろ調べたところdockerのstopではpid 1
に対して送っているということらしいです
コンテナの中を確認したところ、以下のようになっていたので、npmのプロセスがstopされたということになりそうですね。
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.6 1.9 682384 38820 pts/0 Ssl+ 13:39 0:00 npm
root 19 0.0 0.0 1868 416 pts/0 S+ 13:39 0:00 sh -c nuxt start
root 20 1.0 3.0 707152 61552 pts/0 Sl+ 13:39 0:00 node /app/node_modules/.bin/nuxt
root 31 0.0 0.1 3744 2900 pts/1 Ss 13:39 0:00 /bin/bash
root 39 0.0 0.1 5860 2384 pts/1 R+ 13:39 0:00 ps aux
ローカルの時はnpm run start
のプロセスに対して送られていたようなので停止できたっぽいです。
ではどうするかですが、いろいろ試したところ以下のようにするのが一番良さそうでした。
FROM node:14.18.2
WORKDIR /app
COPY . .
RUN npm i && npm run build
- CMD ["npm", "run", "start"]
+ CMD ["npx", "nuxt", "start"]
Docker内部のプロセスは以下のようになっており、docker stop
を実施したところ、期待通りに正しく終了させることができました。
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.8 2.9 706864 60868 pts/0 Ssl+ 15:21 0:00 node /usr/local/bin/npx nuxt start
root 19 0.0 0.1 3744 2860 pts/1 Ss 15:21 0:00 /bin/bash
root 28 0.0 0.1 5860 2380 pts/1 R+ 15:21 0:00 ps aux
まとめ
Nuxt.jsを使っていながら、勝手に終了させていたので、今考えてみればGracefullではなかったかと思います(そもそもそんなに重たい処理をするものを作っていない+ユーザがあまりいなかった)
ただ、Nuxt.js上でBFFなどを実装していくに当たり、このあたり、気をつけて実装をしていく必要があるかと感じています。
と書きながら、現状のNuxt.jsが本当にGracefully shutdownしていないのかは別途確かめたほうがいいと思います。今回のはあくまでcloseが呼ばれるためにはという感じだったので。
Discussion